diff options
author | David S. Miller <davem@davemloft.net> | 2015-01-27 00:16:56 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-01-27 00:16:56 -0800 |
commit | 971f49dee2639badd70bea6cf92e4eaa357ffecf (patch) | |
tree | ddf90b1789e8a24547a0bf6786274df77eebd330 | |
parent | d2fa7cc4e3bd759f5c8e093d1e08b718c722f319 (diff) | |
parent | 803dd9c77ac3a08958535f2a1ad5890104e2c235 (diff) | |
download | linux-971f49dee2639badd70bea6cf92e4eaa357ffecf.tar.bz2 |
Merge branch 'phy-next'
Florian Fainelli says:
====================
net: phy: prevent double suspend
This patch series addresses a problem that Fugang and I observed on different
platforms where a given PHY device might end-up being suspended twice.
Once as part of the call from ndo_open() all the way down to phy_detach() and
phy_suspend() and a second time when the generic platform device/driver
suspend/resume callbacks are called in drivers/net/phy/mdio_bus.c.
Thanks to Fugang for giving this a quick try on i.MX6/FEC and reporting
positive test results!
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 14 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 22 | ||||
-rw-r--r-- | include/linux/phy.h | 3 |
3 files changed, 29 insertions, 10 deletions
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 50051f271b10..095ef3fe369a 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -443,9 +443,13 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) if (!drv || !phydrv->suspend) return false; - /* PHY not attached? May suspend. */ + /* PHY not attached? May suspend if the PHY has not already been + * suspended as part of a prior call to phy_disconnect() -> + * phy_detach() -> phy_suspend() because the parent netdev might be the + * MDIO bus driver and clock gated at this point. + */ if (!netdev) - return true; + return !phydev->suspended; /* Don't suspend PHY if the attched netdev parent may wakeup. * The parent may point to a PCI device, as in tg3 driver. @@ -465,7 +469,6 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) static int mdio_bus_suspend(struct device *dev) { - struct phy_driver *phydrv = to_phy_driver(dev->driver); struct phy_device *phydev = to_phy_device(dev); /* We must stop the state machine manually, otherwise it stops out of @@ -479,19 +482,18 @@ static int mdio_bus_suspend(struct device *dev) if (!mdio_bus_phy_may_suspend(phydev)) return 0; - return phydrv->suspend(phydev); + return phy_suspend(phydev); } static int mdio_bus_resume(struct device *dev) { - struct phy_driver *phydrv = to_phy_driver(dev->driver); struct phy_device *phydev = to_phy_device(dev); int ret; if (!mdio_bus_phy_may_suspend(phydev)) goto no_resume; - ret = phydrv->resume(phydev); + ret = phy_resume(phydev); if (ret < 0) return ret; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3fc91e89f5a5..bdfe51fc3a65 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -699,6 +699,7 @@ int phy_suspend(struct phy_device *phydev) { struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; + int ret = 0; /* If the device has WOL enabled, we cannot suspend the PHY */ phy_ethtool_get_wol(phydev, &wol); @@ -706,18 +707,31 @@ int phy_suspend(struct phy_device *phydev) return -EBUSY; if (phydrv->suspend) - return phydrv->suspend(phydev); - return 0; + ret = phydrv->suspend(phydev); + + if (ret) + return ret; + + phydev->suspended = true; + + return ret; } EXPORT_SYMBOL(phy_suspend); int phy_resume(struct phy_device *phydev) { struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); + int ret = 0; if (phydrv->resume) - return phydrv->resume(phydev); - return 0; + ret = phydrv->resume(phydev); + + if (ret) + return ret; + + phydev->suspended = false; + + return ret; } EXPORT_SYMBOL(phy_resume); diff --git a/include/linux/phy.h b/include/linux/phy.h index 9c189a1fa3a2..685809835b5c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -327,6 +327,8 @@ struct phy_c45_device_ids { * c45_ids: 802.3-c45 Device Identifers if is_c45. * is_c45: Set to true if this phy uses clause 45 addressing. * is_internal: Set to true if this phy is internal to a MAC. + * has_fixups: Set to true if this phy has fixups/quirks. + * suspended: Set to true if this phy has been suspended successfully. * state: state of the PHY for management purposes * dev_flags: Device-specific flags used by the PHY driver. * addr: Bus address of PHY @@ -364,6 +366,7 @@ struct phy_device { bool is_c45; bool is_internal; bool has_fixups; + bool suspended; enum phy_state state; |