[PATCH net-next v1] net: phy: at803x: add mdix configuration support for AR9331 and AR8035
From: Oleksij Rempel <o.rempel@pengutronix.de>
Date: 2020-07-19 08:05:47
Also in:
lkml
Subsystem:
ethernet phy library, networking drivers, the rest · Maintainers:
Andrew Lunn, Heiner Kallweit, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
This patch add MDIX configuration ability for AR9331 and AR8035. Theoretically it should work on other Atheros PHYs, but I was able to test only this two. Since I have no certified reference HW able to detect or configure MDIX, this functionality was confirmed by oscilloscope. Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de> --- drivers/net/phy/at803x.c | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+)
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index 96c61aa75bd7..101651b2de54 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c@@ -21,6 +21,17 @@ #include <linux/regulator/consumer.h> #include <dt-bindings/net/qca-ar803x.h> +#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 +#define AT803X_SFC_ASSERT_CRS BIT(11) +#define AT803X_SFC_FORCE_LINK BIT(10) +#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) +#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 +#define AT803X_SFC_MANUAL_MDIX 0x1 +#define AT803X_SFC_MANUAL_MDI 0x0 +#define AT803X_SFC_SQE_TEST BIT(2) +#define AT803X_SFC_POLARITY_REVERSAL BIT(1) +#define AT803X_SFC_DISABLE_JABBER BIT(0) + #define AT803X_SPECIFIC_STATUS 0x11 #define AT803X_SS_SPEED_MASK (3 << 14) #define AT803X_SS_SPEED_1000 (2 << 14)
@@ -703,6 +714,12 @@ static int at803x_read_status(struct phy_device *phydev) return ss; if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { + int sfc; + + sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); + if (sfc < 0) + return sfc; + switch (ss & AT803X_SS_SPEED_MASK) { case AT803X_SS_SPEED_10: phydev->speed = SPEED_10;
@@ -718,10 +735,23 @@ static int at803x_read_status(struct phy_device *phydev) phydev->duplex = DUPLEX_FULL; else phydev->duplex = DUPLEX_HALF; + if (ss & AT803X_SS_MDIX) phydev->mdix = ETH_TP_MDI_X; else phydev->mdix = ETH_TP_MDI; + + switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { + case AT803X_SFC_MANUAL_MDI: + phydev->mdix_ctrl = ETH_TP_MDI; + break; + case AT803X_SFC_MANUAL_MDIX: + phydev->mdix_ctrl = ETH_TP_MDI_X; + break; + case AT803X_SFC_AUTOMATIC_CROSSOVER: + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + break; + } } if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
@@ -730,6 +760,50 @@ static int at803x_read_status(struct phy_device *phydev) return 0; } +static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) +{ + u16 val; + + switch (ctrl) { + case ETH_TP_MDI: + val = AT803X_SFC_MANUAL_MDI; + break; + case ETH_TP_MDI_X: + val = AT803X_SFC_MANUAL_MDIX; + break; + case ETH_TP_MDI_AUTO: + val = AT803X_SFC_AUTOMATIC_CROSSOVER; + break; + default: + return 0; + } + + return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, + AT803X_SFC_MDI_CROSSOVER_MODE_M, + FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); +} + +static int at803x_config_aneg(struct phy_device *phydev) +{ + int ret; + + ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); + if (ret < 0) + return ret; + + /* Changes of the midx bits are disruptive to the normal operation; + * therefore any changes to these registers must be followed by a + * software reset to take effect. + */ + if (ret == 1) { + ret = genphy_soft_reset(phydev); + if (ret < 0) + return ret; + } + + return genphy_config_aneg(phydev); +} + static int at803x_get_downshift(struct phy_device *phydev, u8 *d) { int val;
@@ -979,6 +1053,7 @@ static struct phy_driver at803x_driver[] = { .flags = PHY_POLL_CABLE_TEST, .probe = at803x_probe, .remove = at803x_remove, + .config_aneg = at803x_config_aneg, .config_init = at803x_config_init, .soft_reset = genphy_soft_reset, .set_wol = at803x_set_wol,
@@ -1061,6 +1136,9 @@ static struct phy_driver at803x_driver[] = { .config_intr = &at803x_config_intr, .cable_test_start = at803x_cable_test_start, .cable_test_get_status = at803x_cable_test_get_status, + .read_status = at803x_read_status, + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, } }; module_phy_driver(at803x_driver);
--
2.27.0