Commit 93f41e67 authored by Heiner Kallweit's avatar Heiner Kallweit Committed by David S. Miller

net: phy: fix WoL handling when suspending the PHY

Core of the problem is that phy_suspend() suspends the PHY when it
should not because of WoL. phy_suspend() checks for WoL already, but
this works only if the PHY driver handles WoL (what is rarely the case).
Typically WoL is handled by the MAC driver.

This patch uses new member wol_enabled of struct net_device as
additional criteria in the check when not to suspend the PHY because
of WoL.

Last but not least change phy_detach() to call phy_suspend() before
attached_dev is set to NULL. phy_suspend() accesses attached_dev
when checking whether the MAC driver activated WoL.

Fixes: f1e911d5 ("r8169: add basic phylib support")
Fixes: e8cfd9d6 ("net: phy: call state machine synchronously in phy_stop")
Signed-off-by: default avatarHeiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 61941143
...@@ -93,7 +93,12 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) ...@@ -93,7 +93,12 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
if (!netdev) if (!netdev)
return !phydev->suspended; return !phydev->suspended;
/* Don't suspend PHY if the attached netdev parent may wakeup. if (netdev->wol_enabled)
return false;
/* As long as not all affected network drivers support the
* wol_enabled flag, let's check for hints that WoL is enabled.
* Don't suspend PHY if the attached netdev parent may wake up.
* The parent may point to a PCI device, as in tg3 driver. * The parent may point to a PCI device, as in tg3 driver.
*/ */
if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent)) if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
...@@ -1132,9 +1137,9 @@ void phy_detach(struct phy_device *phydev) ...@@ -1132,9 +1137,9 @@ void phy_detach(struct phy_device *phydev)
sysfs_remove_link(&dev->dev.kobj, "phydev"); sysfs_remove_link(&dev->dev.kobj, "phydev");
sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev"); sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
} }
phy_suspend(phydev);
phydev->attached_dev->phydev = NULL; phydev->attached_dev->phydev = NULL;
phydev->attached_dev = NULL; phydev->attached_dev = NULL;
phy_suspend(phydev);
phydev->phylink = NULL; phydev->phylink = NULL;
phy_led_triggers_unregister(phydev); phy_led_triggers_unregister(phydev);
...@@ -1168,12 +1173,13 @@ EXPORT_SYMBOL(phy_detach); ...@@ -1168,12 +1173,13 @@ EXPORT_SYMBOL(phy_detach);
int phy_suspend(struct phy_device *phydev) int phy_suspend(struct phy_device *phydev)
{ {
struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
struct net_device *netdev = phydev->attached_dev;
struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
int ret = 0; int ret = 0;
/* If the device has WOL enabled, we cannot suspend the PHY */ /* If the device has WOL enabled, we cannot suspend the PHY */
phy_ethtool_get_wol(phydev, &wol); phy_ethtool_get_wol(phydev, &wol);
if (wol.wolopts) if (wol.wolopts || (netdev && netdev->wol_enabled))
return -EBUSY; return -EBUSY;
if (phydev->drv && phydrv->suspend) if (phydev->drv && phydrv->suspend)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment