]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
net: phy: genphy_loopback: add link speed configuration
authorOleksij Rempel <o.rempel@pengutronix.de>
Mon, 19 Apr 2021 13:01:02 +0000 (15:01 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 20 Apr 2021 23:08:02 +0000 (16:08 -0700)
In case of loopback, in most cases we need to disable autoneg support
and force some speed configuration. Otherwise, depending on currently
active auto negotiated link speed, the loopback may or may not work.

This patch was tested with following PHYs: TJA1102, KSZ8081, KSZ9031,
AT8035, AR9331.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/phy.c
drivers/net/phy/phy_device.c
include/linux/phy.h

index fc2e7cb5b2e584030ee030311f3e365b02153646..1f0512e39c6516d1d8579ce5836b294fb6a658d7 100644 (file)
@@ -701,7 +701,7 @@ out:
 }
 EXPORT_SYMBOL(phy_start_cable_test_tdr);
 
-static int phy_config_aneg(struct phy_device *phydev)
+int phy_config_aneg(struct phy_device *phydev)
 {
        if (phydev->drv->config_aneg)
                return phydev->drv->config_aneg(phydev);
@@ -714,6 +714,7 @@ static int phy_config_aneg(struct phy_device *phydev)
 
        return genphy_config_aneg(phydev);
 }
+EXPORT_SYMBOL(phy_config_aneg);
 
 /**
  * phy_check_link_status - check link status and set state accordingly
index 320a3e5cd10a81914160064f453094f1439a4e2b..0a2d8bedf73da3160ea14ef6aba78ec372f5e200 100644 (file)
@@ -2565,8 +2565,32 @@ EXPORT_SYMBOL(genphy_resume);
 
 int genphy_loopback(struct phy_device *phydev, bool enable)
 {
-       return phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
-                         enable ? BMCR_LOOPBACK : 0);
+       if (enable) {
+               u16 val, ctl = BMCR_LOOPBACK;
+               int ret;
+
+               if (phydev->speed == SPEED_1000)
+                       ctl |= BMCR_SPEED1000;
+               else if (phydev->speed == SPEED_100)
+                       ctl |= BMCR_SPEED100;
+
+               if (phydev->duplex == DUPLEX_FULL)
+                       ctl |= BMCR_FULLDPLX;
+
+               phy_modify(phydev, MII_BMCR, ~0, ctl);
+
+               ret = phy_read_poll_timeout(phydev, MII_BMSR, val,
+                                           val & BMSR_LSTATUS,
+                                   5000, 500000, true);
+               if (ret)
+                       return ret;
+       } else {
+               phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 0);
+
+               phy_config_aneg(phydev);
+       }
+
+       return 0;
 }
 EXPORT_SYMBOL(genphy_loopback);
 
index e3d4d583463b5777183298af6b989a92f606aaa4..60d2b26026a2dfd142f29ed5f6e9827b1361a8fe 100644 (file)
@@ -1410,6 +1410,7 @@ void phy_disconnect(struct phy_device *phydev);
 void phy_detach(struct phy_device *phydev);
 void phy_start(struct phy_device *phydev);
 void phy_stop(struct phy_device *phydev);
+int phy_config_aneg(struct phy_device *phydev);
 int phy_start_aneg(struct phy_device *phydev);
 int phy_aneg_done(struct phy_device *phydev);
 int phy_speed_down(struct phy_device *phydev, bool sync);