From f88ff8e490c912f95c3531b544a563982c8616c3 Mon Sep 17 00:00:00 2001 From: Josep Orga Date: Thu, 28 Oct 2021 18:06:59 +0200 Subject: [PATCH] net: fec: Add phy-reset-in-suspend functionality. Signed-off-by: Josep Orga --- drivers/net/ethernet/freescale/fec.h | 5 + drivers/net/ethernet/freescale/fec_main.c | 145 +++++++++++++++------- 2 files changed, 105 insertions(+), 45 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 5b18cc712476..262844aae341 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -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; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index b3eac8a14f5d..0c5a3e2f8168 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -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); } -- 2.17.1