]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
net: phy: at803x: add mdix configuration support for AR9331 and AR8035
authorOleksij Rempel <o.rempel@pengutronix.de>
Sun, 19 Jul 2020 08:05:30 +0000 (10:05 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 20 Jul 2020 01:07:16 +0000 (18:07 -0700)
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>
drivers/net/phy/at803x.c

index 96c61aa75bd72e775904ed4294e9df7fbf2115de..101651b2de5416ac9c4ad3e2e96d407ba1b6988e 100644 (file)
 #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);