summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleksij Rempel <o.rempel@pengutronix.de>2020-07-19 10:05:30 +0200
committerDavid S. Miller <davem@davemloft.net>2020-07-20 03:07:16 +0200
commit7dce80c2a526a199ae1447e7a58d4135bb18ff8a (patch)
treedc6de6388e008121896ee462fbc686c4628bea26
parentMerge branch 'net-enetc-remove-bootloader-dependency' (diff)
downloadlinux-7dce80c2a526a199ae1447e7a58d4135bb18ff8a.tar.xz
linux-7dce80c2a526a199ae1447e7a58d4135bb18ff8a.zip
net: phy: at803x: add mdix configuration support for AR9331 and AR8035
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> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/phy/at803x.c78
1 files changed, 78 insertions, 0 deletions
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);