[PATCH net-next v1] net: phy: at803x: add mdix configuration support for AR9331 and AR8035

From: Oleksij Rempel
Date: Sun Jul 19 2020 - 04:05:45 EST


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@xxxxxxxxxxxxxx>
---
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