Commit 971f49de authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents d2fa7cc4 803dd9c7
...@@ -443,9 +443,13 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) ...@@ -443,9 +443,13 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
if (!drv || !phydrv->suspend) if (!drv || !phydrv->suspend)
return false; 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) if (!netdev)
return true; return !phydev->suspended;
/* Don't suspend PHY if the attched netdev parent may wakeup. /* Don't suspend PHY if the attched netdev parent may wakeup.
* The parent may point to a PCI device, as in tg3 driver. * 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) ...@@ -465,7 +469,6 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
static int mdio_bus_suspend(struct device *dev) 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); struct phy_device *phydev = to_phy_device(dev);
/* We must stop the state machine manually, otherwise it stops out of /* We must stop the state machine manually, otherwise it stops out of
...@@ -479,19 +482,18 @@ static int mdio_bus_suspend(struct device *dev) ...@@ -479,19 +482,18 @@ static int mdio_bus_suspend(struct device *dev)
if (!mdio_bus_phy_may_suspend(phydev)) if (!mdio_bus_phy_may_suspend(phydev))
return 0; return 0;
return phydrv->suspend(phydev); return phy_suspend(phydev);
} }
static int mdio_bus_resume(struct device *dev) 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); struct phy_device *phydev = to_phy_device(dev);
int ret; int ret;
if (!mdio_bus_phy_may_suspend(phydev)) if (!mdio_bus_phy_may_suspend(phydev))
goto no_resume; goto no_resume;
ret = phydrv->resume(phydev); ret = phy_resume(phydev);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
...@@ -699,6 +699,7 @@ int phy_suspend(struct phy_device *phydev) ...@@ -699,6 +699,7 @@ int phy_suspend(struct phy_device *phydev)
{ {
struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
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);
...@@ -706,18 +707,31 @@ int phy_suspend(struct phy_device *phydev) ...@@ -706,18 +707,31 @@ int phy_suspend(struct phy_device *phydev)
return -EBUSY; return -EBUSY;
if (phydrv->suspend) if (phydrv->suspend)
return phydrv->suspend(phydev); ret = phydrv->suspend(phydev);
return 0;
if (ret)
return ret;
phydev->suspended = true;
return ret;
} }
EXPORT_SYMBOL(phy_suspend); EXPORT_SYMBOL(phy_suspend);
int phy_resume(struct phy_device *phydev) int phy_resume(struct phy_device *phydev)
{ {
struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
int ret = 0;
if (phydrv->resume) if (phydrv->resume)
return phydrv->resume(phydev); ret = phydrv->resume(phydev);
return 0;
if (ret)
return ret;
phydev->suspended = false;
return ret;
} }
EXPORT_SYMBOL(phy_resume); EXPORT_SYMBOL(phy_resume);
......
...@@ -327,6 +327,8 @@ struct phy_c45_device_ids { ...@@ -327,6 +327,8 @@ struct phy_c45_device_ids {
* c45_ids: 802.3-c45 Device Identifers if is_c45. * c45_ids: 802.3-c45 Device Identifers if is_c45.
* is_c45: Set to true if this phy uses clause 45 addressing. * 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. * 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 * state: state of the PHY for management purposes
* dev_flags: Device-specific flags used by the PHY driver. * dev_flags: Device-specific flags used by the PHY driver.
* addr: Bus address of PHY * addr: Bus address of PHY
...@@ -364,6 +366,7 @@ struct phy_device { ...@@ -364,6 +366,7 @@ struct phy_device {
bool is_c45; bool is_c45;
bool is_internal; bool is_internal;
bool has_fixups; bool has_fixups;
bool suspended;
enum phy_state state; enum phy_state state;
......
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