From 1057d685c6eb4ef5dbc03f97ff25da8ca2418a86 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 27 Jul 2020 19:01:48 +0800 Subject: [PATCH 1/5] net: stmmac: Remove WAKE_MAGIC if HW shows no pmt_magic_frame Remove WAKE_MAGIC from supported modes if the HW capability register shows no support for pmt_magic_frame. Signed-off-by: Jisheng Zhang Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index eae11c585025..9e0af626a24a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -603,6 +603,8 @@ static void stmmac_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) mutex_lock(&priv->lock); if (device_can_wakeup(priv->device)) { wol->supported = WAKE_MAGIC | WAKE_UCAST; + if (priv->hw_cap_support && !priv->dma_cap.pmt_magic_frame) + wol->supported &= ~WAKE_MAGIC; wol->wolopts = priv->wolopts; } mutex_unlock(&priv->lock); From 2f45f7a13e99ef2d05ef3afecaee30f314c9b8b2 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 27 Jul 2020 19:02:13 +0800 Subject: [PATCH 2/5] net: stmmac: Move device_can_wakeup() check earlier in set_wol If !device_can_wakeup(), there's no need to futher check. And return -EOPNOTSUPP rather than -EINVAL if !device_can_wakeup(). Signed-off-by: Jisheng Zhang Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 9e0af626a24a..79795bebd3a2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -615,15 +615,15 @@ static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) struct stmmac_priv *priv = netdev_priv(dev); u32 support = WAKE_MAGIC | WAKE_UCAST; + if (!device_can_wakeup(priv->device)) + return -EOPNOTSUPP; + /* By default almost all GMAC devices support the WoL via * magic frame but we can disable it if the HW capability * register shows no support for pmt_magic_frame. */ if ((priv->hw_cap_support) && (!priv->dma_cap.pmt_magic_frame)) wol->wolopts &= ~WAKE_MAGIC; - if (!device_can_wakeup(priv->device)) - return -EINVAL; - if (wol->wolopts & ~support) return -EINVAL; From e8377e7a29efbe80668b638eccd58f8db3c9ecb7 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 27 Jul 2020 19:02:48 +0800 Subject: [PATCH 3/5] net: stmmac: only call pmt() during suspend/resume if HW enables PMT This is to prepare WOL support with phy. Compared with WOL implementation which relies on the MAC's PMT features, in phy supported WOL case, device_may_wakeup() may also be true, but we should not call mac's pmt() function if HW doesn't enable PMT. And during resume, we should call phylink_start() if PMT is disabled. Signed-off-by: Jisheng Zhang Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 73677c3b33b6..358fd3bf9ef5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -5085,7 +5085,7 @@ int stmmac_suspend(struct device *dev) priv->plat->serdes_powerdown(ndev, priv->plat->bsp_priv); /* Enable Power down mode by programming the PMT regs */ - if (device_may_wakeup(priv->device)) { + if (device_may_wakeup(priv->device) && priv->plat->pmt) { stmmac_pmt(priv, priv->hw, priv->wolopts); priv->irq_wake = 1; } else { @@ -5157,7 +5157,7 @@ int stmmac_resume(struct device *dev) * this bit because it can generate problems while resuming * from another devices (e.g. serial console). */ - if (device_may_wakeup(priv->device)) { + if (device_may_wakeup(priv->device) && priv->plat->pmt) { mutex_lock(&priv->lock); stmmac_pmt(priv, priv->hw, 0); mutex_unlock(&priv->lock); @@ -5200,7 +5200,7 @@ int stmmac_resume(struct device *dev) mutex_unlock(&priv->lock); - if (!device_may_wakeup(priv->device)) { + if (!device_may_wakeup(priv->device) || !priv->plat->pmt) { rtnl_lock(); phylink_start(priv->phylink); rtnl_unlock(); From 1d8e5b0f3f2c6d05697f8192aac7255e6be1e715 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 27 Jul 2020 19:03:09 +0800 Subject: [PATCH 4/5] net: stmmac: Support WOL with phy Currently, the stmmac driver WOL implementation relies on MAC's PMT feature. We have a case: the MAC HW doesn't enable PMT, instead, we rely on the phy to support WOL. Implement the support for this case. Signed-off-by: Jisheng Zhang Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 11 +++++++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 79795bebd3a2..05d63963fdb7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -600,6 +600,9 @@ static void stmmac_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct stmmac_priv *priv = netdev_priv(dev); + if (!priv->plat->pmt) + return phylink_ethtool_get_wol(priv->phylink, wol); + mutex_lock(&priv->lock); if (device_can_wakeup(priv->device)) { wol->supported = WAKE_MAGIC | WAKE_UCAST; @@ -618,6 +621,14 @@ static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) if (!device_can_wakeup(priv->device)) return -EOPNOTSUPP; + if (!priv->plat->pmt) { + int ret = phylink_ethtool_set_wol(priv->phylink, wol); + + if (!ret) + device_set_wakeup_enable(&dev->dev, !!wol->wolopts); + return ret; + } + /* By default almost all GMAC devices support the WoL via * magic frame but we can disable it if the HW capability * register shows no support for pmt_magic_frame. */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 358fd3bf9ef5..32c0c9647b87 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1075,6 +1075,7 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv) */ static int stmmac_init_phy(struct net_device *dev) { + struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; struct stmmac_priv *priv = netdev_priv(dev); struct device_node *node; int ret; @@ -1100,6 +1101,9 @@ static int stmmac_init_phy(struct net_device *dev) ret = phylink_connect_phy(priv->phylink, phydev); } + phylink_ethtool_get_wol(priv->phylink, &wol); + device_set_wakeup_capable(priv->device, !!wol.supported); + return ret; } From 77b2898394e3bb843228ee6f90066a23cf67e8a1 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 27 Jul 2020 19:05:20 +0800 Subject: [PATCH 5/5] net: stmmac: Speed down the PHY if WoL to save energy When WoL is enabled and the machine is powered off, the PHY remains waiting for wakeup events at max speed, which is a waste of energy. Slow down the PHY speed before stopping the ethernet if WoL is enabled, Signed-off-by: Jisheng Zhang Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 32c0c9647b87..89b2b3472852 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2823,6 +2823,8 @@ static int stmmac_open(struct net_device *dev) stmmac_init_coalesce(priv); phylink_start(priv->phylink); + /* We may have called phylink_speed_down before */ + phylink_speed_up(priv->phylink); /* Request the IRQ lines */ ret = request_irq(dev->irq, stmmac_interrupt, @@ -2896,6 +2898,8 @@ static int stmmac_release(struct net_device *dev) if (priv->eee_enabled) del_timer_sync(&priv->eee_ctrl_timer); + if (device_may_wakeup(priv->device)) + phylink_speed_down(priv->phylink, false); /* Stop and disconnect the PHY */ phylink_stop(priv->phylink); phylink_disconnect_phy(priv->phylink); @@ -5095,6 +5099,8 @@ int stmmac_suspend(struct device *dev) } else { mutex_unlock(&priv->lock); rtnl_lock(); + if (device_may_wakeup(priv->device)) + phylink_speed_down(priv->phylink, false); phylink_stop(priv->phylink); rtnl_unlock(); mutex_lock(&priv->lock); @@ -5207,6 +5213,8 @@ int stmmac_resume(struct device *dev) if (!device_may_wakeup(priv->device) || !priv->plat->pmt) { rtnl_lock(); phylink_start(priv->phylink); + /* We may have called phylink_speed_down before */ + phylink_speed_up(priv->phylink); rtnl_unlock(); }