net: phy: mxl-gpy: enhance delay time required by loopback disable function
authorXu Liang <lxu@maxlinear.com>
Tue, 14 Mar 2023 16:30:23 +0000 (00:30 +0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 16 Mar 2023 17:30:45 +0000 (17:30 +0000)
GPY2xx devices need 3 seconds to fully switch out of loopback mode
before it can safely re-enter loopback mode. Implement timeout mechanism
to guarantee 3 seconds waited before re-enter loopback mode.

Signed-off-by: Xu Liang <lxu@maxlinear.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/mxl-gpy.c

index e5972b4..8e6bb97 100644 (file)
@@ -107,6 +107,13 @@ struct gpy_priv {
 
        u8 fw_major;
        u8 fw_minor;
+
+       /* It takes 3 seconds to fully switch out of loopback mode before
+        * it can safely re-enter loopback mode. Record the time when
+        * loopback is disabled. Check and wait if necessary before loopback
+        * is enabled.
+        */
+       u64 lb_dis_to;
 };
 
 static const struct {
@@ -769,18 +776,34 @@ static void gpy_get_wol(struct phy_device *phydev,
 
 static int gpy_loopback(struct phy_device *phydev, bool enable)
 {
+       struct gpy_priv *priv = phydev->priv;
+       u16 set = 0;
        int ret;
 
-       ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
-                        enable ? BMCR_LOOPBACK : 0);
-       if (!ret) {
-               /* It takes some time for PHY device to switch
-                * into/out-of loopback mode.
+       if (enable) {
+               u64 now = get_jiffies_64();
+
+               /* wait until 3 seconds from last disable */
+               if (time_before64(now, priv->lb_dis_to))
+                       msleep(jiffies64_to_msecs(priv->lb_dis_to - now));
+
+               set = BMCR_LOOPBACK;
+       }
+
+       ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, set);
+       if (ret <= 0)
+               return ret;
+
+       if (enable) {
+               /* It takes some time for PHY device to switch into
+                * loopback mode.
                 */
                msleep(100);
+       } else {
+               priv->lb_dis_to = get_jiffies_64() + HZ * 3;
        }
 
-       return ret;
+       return 0;
 }
 
 static int gpy115_loopback(struct phy_device *phydev, bool enable)