net: fec: Add phy-reset-in-suspend functionality.
authorJosep Orga <jorga@somdevices.com>
Thu, 28 Oct 2021 16:06:59 +0000 (18:06 +0200)
committerJosep Orga <jorga@somdevices.com>
Thu, 28 Oct 2021 16:06:59 +0000 (18:06 +0200)
Signed-off-by: Josep Orga <jorga@somdevices.com>
drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c

index 5b18cc7..262844a 100644 (file)
@@ -577,6 +577,11 @@ struct fec_enet_private {
        int     wol_flag;
        int     wake_irq;
        u32     quirks;
+       bool    phy_reset_in_suspend;
+       int     phy_reset_gpio;
+       u32     phy_reset_duration;
+       bool    phy_reset_active_high;
+       int     phy_post_delay;
 
        struct  napi_struct napi;
        int     csum_flags;
index b3eac8a..0c5a3e2 100644 (file)
@@ -2011,13 +2011,63 @@ out:
        return ret;
 }
 
+#ifdef CONFIG_OF
+static void fec_reset_phy(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       u32 msec = fep->phy_reset_duration;
+
+       if (fep->phy_reset_gpio < 0)
+               return;
+
+       gpio_set_value_cansleep(fep->phy_reset_gpio,
+                               fep->phy_reset_active_high);
+       if (msec > 20)
+               msleep(msec);
+       else
+               usleep_range(msec * 1000, msec * 1000 + 1000);
+
+       gpio_set_value_cansleep(fep->phy_reset_gpio,
+                               !fep->phy_reset_active_high);
+
+       if (!fep->phy_post_delay)
+               return;
+
+       if (fep->phy_post_delay > 20)
+               msleep(fep->phy_post_delay);
+       else
+               usleep_range(fep->phy_post_delay * 1000,
+                            fep->phy_post_delay * 1000 + 1000);
+
+       return;
+}
+
+#else /* CONFIG_OF */
+static int fec_reset_phy(struct platform_device *pdev)
+{
+       /*
+        * In case of platform probe, the reset has been done
+        * by machine code.
+        */
+       return 0;
+}
+#endif /* CONFIG_OF */
+
 static void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
        struct phy_device *phy_dev = ndev->phydev;
 
        if (phy_dev) {
-               phy_reset_after_clk_enable(phy_dev);
+               if (!gpio_is_valid(fep->phy_reset_gpio)) {
+                       phy_reset_after_clk_enable(phy_dev);
+               } else {
+                       if (phy_dev && phy_dev->drv &&
+                           phy_dev->drv->flags & PHY_RST_AFTER_CLK_EN) {
+                               fec_reset_phy(fep->pdev);
+                       }
+               }
        } else if (fep->phy_node) {
                /*
                 * If the PHY still is not bound to the MAC, but there is
@@ -2027,7 +2077,14 @@ static void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev)
                 * the PHY reset.
                 */
                phy_dev = of_phy_find_device(fep->phy_node);
-               phy_reset_after_clk_enable(phy_dev);
+               if (!gpio_is_valid(fep->phy_reset_gpio)) {
+                       phy_reset_after_clk_enable(phy_dev);
+               } else {
+                       if (phy_dev && phy_dev->drv &&
+                           phy_dev->drv->flags & PHY_RST_AFTER_CLK_EN) {
+                               fec_reset_phy(fep->pdev);
+                       }
+               }
                put_device(&phy_dev->mdio.dev);
        }
 }
@@ -3627,69 +3684,52 @@ free_queue_mem:
 }
 
 #ifdef CONFIG_OF
-static int fec_reset_phy(struct platform_device *pdev)
+static int fec_reset_phy_init(struct platform_device *pdev)
 {
-       int err, phy_reset;
-       bool active_high = false;
-       int msec = 1, phy_post_delay = 0;
        struct device_node *np = pdev->dev.of_node;
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       int err;
 
        if (!np)
                return 0;
 
-       err = of_property_read_u32(np, "phy-reset-duration", &msec);
+       err = of_property_read_u32(np, "phy-reset-duration",
+                                  &fep->phy_reset_duration);
        /* A sane reset duration should not be longer than 1s */
-       if (!err && msec > 1000)
-               msec = 1;
+       if (err || fep->phy_reset_duration > 1000)
+               fep->phy_reset_duration = 1;
 
-       phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
-       if (phy_reset == -EPROBE_DEFER)
-               return phy_reset;
-       else if (!gpio_is_valid(phy_reset))
+       fep->phy_reset_gpio = of_get_named_gpio(np, "phy-reset-gpios", 0);
+       if (fep->phy_reset_gpio == -EPROBE_DEFER)
+               return fep->phy_reset_gpio;
+       else if (!gpio_is_valid(fep->phy_reset_gpio))
                return 0;
 
-       err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay);
+       err = of_property_read_u32(np, "phy-reset-post-delay",
+                                  &fep->phy_post_delay);
        /* valid reset duration should be less than 1s */
-       if (!err && phy_post_delay > 1000)
+       if (!err && fep->phy_post_delay > 1000)
                return -EINVAL;
 
-       active_high = of_property_read_bool(np, "phy-reset-active-high");
+       fep->phy_reset_active_high = of_property_read_bool(np,
+                                               "phy-reset-active-high");
 
-       err = devm_gpio_request_one(&pdev->dev, phy_reset,
-                       active_high ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
-                       "phy-reset");
+       err = devm_gpio_request_one(&pdev->dev, fep->phy_reset_gpio,
+                                   fep->phy_reset_active_high ?
+                                       GPIOF_OUT_INIT_LOW :
+                                       GPIOF_OUT_INIT_HIGH,
+                                   "phy-reset");
        if (err) {
                dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
                return err;
        }
-
-       if (msec > 20)
-               msleep(msec);
-       else
-               usleep_range(msec * 1000, msec * 1000 + 1000);
-
-       gpio_set_value_cansleep(phy_reset, !active_high);
-
-       if (!phy_post_delay)
-               return 0;
-
-       if (phy_post_delay > 20)
-               msleep(phy_post_delay);
-       else
-               usleep_range(phy_post_delay * 1000,
-                            phy_post_delay * 1000 + 1000);
+       fep->phy_reset_in_suspend = of_find_property(np,
+                                                    "phy-reset-in-suspend",
+                                                    NULL);
 
        return 0;
 }
-#else /* CONFIG_OF */
-static int fec_reset_phy(struct platform_device *pdev)
-{
-       /*
-        * In case of platform probe, the reset has been done
-        * by machine code.
-        */
-       return 0;
-}
 #endif /* CONFIG_OF */
 
 static void
@@ -3952,11 +3992,14 @@ fec_probe(struct platform_device *pdev)
        pm_runtime_set_active(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
 
-       ret = fec_reset_phy(pdev);
+       ret = fec_reset_phy_init(pdev);
        if (ret)
                goto failed_reset;
 
        irq_cnt = fec_enet_get_irq_cnt(pdev);
+
+       fec_reset_phy(pdev);
+
        if (fep->bufdesc_ex)
                fec_ptp_init(pdev, irq_cnt);
 
@@ -4103,6 +4146,9 @@ static int __maybe_unused fec_suspend(struct device *dev)
                fec_stop(ndev);
                if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) {
                        fec_irqs_disable(ndev);
+                       if (fep->phy_reset_in_suspend)
+                               gpio_set_value_cansleep(fep->phy_reset_gpio,
+                                                       fep->phy_reset_active_high);
                        pinctrl_pm_select_sleep_state(&fep->pdev->dev);
                } else {
                        fec_enet_stop_mode(fep, true);
@@ -4118,6 +4164,9 @@ static int __maybe_unused fec_suspend(struct device *dev)
                                return ret;
                }
        } else if (fep->mii_bus_share && !ndev->phydev) {
+               if (fep->phy_reset_in_suspend)
+                       gpio_set_value_cansleep(fep->phy_reset_gpio,
+                                               fep->phy_reset_active_high);
                pinctrl_pm_select_sleep_state(&fep->pdev->dev);
        }
        rtnl_unlock();
@@ -4167,6 +4216,9 @@ static int __maybe_unused fec_resume(struct device *dev)
                        fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON;
                } else {
                        pinctrl_pm_select_default_state(&fep->pdev->dev);
+                       if (fep->phy_reset_in_suspend)
+                               gpio_set_value_cansleep(fep->phy_reset_gpio,
+                                                       !fep->phy_reset_active_high);
                }
                fec_restart(ndev);
                netif_tx_lock_bh(ndev);
@@ -4176,6 +4228,9 @@ static int __maybe_unused fec_resume(struct device *dev)
                phy_start(ndev->phydev);
        } else if (fep->mii_bus_share && !ndev->phydev) {
                pinctrl_pm_select_default_state(&fep->pdev->dev);
+               if (fep->phy_reset_in_suspend)
+                       gpio_set_value_cansleep(fep->phy_reset_gpio,
+                                               !fep->phy_reset_active_high);
                /* And then recovery mii bus */
                ret = fec_restore_mii_bus(ndev);
        }