diff options
author | David S. Miller <davem@davemloft.net> | 2015-01-27 03:16:56 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-01-27 03:16:56 -0500 |
commit | 971f49dee2639badd70bea6cf92e4eaa357ffecf (patch) | |
tree | ddf90b1789e8a24547a0bf6786274df77eebd330 | |
parent | d2fa7cc4e3bd759f5c8e093d1e08b718c722f319 (diff) | |
parent | 803dd9c77ac3a08958535f2a1ad5890104e2c235 (diff) |
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) | |||
443 | if (!drv || !phydrv->suspend) | 443 | if (!drv || !phydrv->suspend) |
444 | return false; | 444 | return false; |
445 | 445 | ||
446 | /* PHY not attached? May suspend. */ | 446 | /* PHY not attached? May suspend if the PHY has not already been |
447 | * suspended as part of a prior call to phy_disconnect() -> | ||
448 | * phy_detach() -> phy_suspend() because the parent netdev might be the | ||
449 | * MDIO bus driver and clock gated at this point. | ||
450 | */ | ||
447 | if (!netdev) | 451 | if (!netdev) |
448 | return true; | 452 | return !phydev->suspended; |
449 | 453 | ||
450 | /* Don't suspend PHY if the attched netdev parent may wakeup. | 454 | /* Don't suspend PHY if the attched netdev parent may wakeup. |
451 | * The parent may point to a PCI device, as in tg3 driver. | 455 | * 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 | 469 | ||
466 | static int mdio_bus_suspend(struct device *dev) | 470 | static int mdio_bus_suspend(struct device *dev) |
467 | { | 471 | { |
468 | struct phy_driver *phydrv = to_phy_driver(dev->driver); | ||
469 | struct phy_device *phydev = to_phy_device(dev); | 472 | struct phy_device *phydev = to_phy_device(dev); |
470 | 473 | ||
471 | /* We must stop the state machine manually, otherwise it stops out of | 474 | /* 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 | if (!mdio_bus_phy_may_suspend(phydev)) | 482 | if (!mdio_bus_phy_may_suspend(phydev)) |
480 | return 0; | 483 | return 0; |
481 | 484 | ||
482 | return phydrv->suspend(phydev); | 485 | return phy_suspend(phydev); |
483 | } | 486 | } |
484 | 487 | ||
485 | static int mdio_bus_resume(struct device *dev) | 488 | static int mdio_bus_resume(struct device *dev) |
486 | { | 489 | { |
487 | struct phy_driver *phydrv = to_phy_driver(dev->driver); | ||
488 | struct phy_device *phydev = to_phy_device(dev); | 490 | struct phy_device *phydev = to_phy_device(dev); |
489 | int ret; | 491 | int ret; |
490 | 492 | ||
491 | if (!mdio_bus_phy_may_suspend(phydev)) | 493 | if (!mdio_bus_phy_may_suspend(phydev)) |
492 | goto no_resume; | 494 | goto no_resume; |
493 | 495 | ||
494 | ret = phydrv->resume(phydev); | 496 | ret = phy_resume(phydev); |
495 | if (ret < 0) | 497 | if (ret < 0) |
496 | return ret; | 498 | return ret; |
497 | 499 | ||
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) | |||
699 | { | 699 | { |
700 | struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); | 700 | struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); |
701 | struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; | 701 | struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; |
702 | int ret = 0; | ||
702 | 703 | ||
703 | /* If the device has WOL enabled, we cannot suspend the PHY */ | 704 | /* If the device has WOL enabled, we cannot suspend the PHY */ |
704 | phy_ethtool_get_wol(phydev, &wol); | 705 | phy_ethtool_get_wol(phydev, &wol); |
@@ -706,18 +707,31 @@ int phy_suspend(struct phy_device *phydev) | |||
706 | return -EBUSY; | 707 | return -EBUSY; |
707 | 708 | ||
708 | if (phydrv->suspend) | 709 | if (phydrv->suspend) |
709 | return phydrv->suspend(phydev); | 710 | ret = phydrv->suspend(phydev); |
710 | return 0; | 711 | |
712 | if (ret) | ||
713 | return ret; | ||
714 | |||
715 | phydev->suspended = true; | ||
716 | |||
717 | return ret; | ||
711 | } | 718 | } |
712 | EXPORT_SYMBOL(phy_suspend); | 719 | EXPORT_SYMBOL(phy_suspend); |
713 | 720 | ||
714 | int phy_resume(struct phy_device *phydev) | 721 | int phy_resume(struct phy_device *phydev) |
715 | { | 722 | { |
716 | struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); | 723 | struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); |
724 | int ret = 0; | ||
717 | 725 | ||
718 | if (phydrv->resume) | 726 | if (phydrv->resume) |
719 | return phydrv->resume(phydev); | 727 | ret = phydrv->resume(phydev); |
720 | return 0; | 728 | |
729 | if (ret) | ||
730 | return ret; | ||
731 | |||
732 | phydev->suspended = false; | ||
733 | |||
734 | return ret; | ||
721 | } | 735 | } |
722 | EXPORT_SYMBOL(phy_resume); | 736 | EXPORT_SYMBOL(phy_resume); |
723 | 737 | ||
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 { | |||
327 | * c45_ids: 802.3-c45 Device Identifers if is_c45. | 327 | * c45_ids: 802.3-c45 Device Identifers if is_c45. |
328 | * is_c45: Set to true if this phy uses clause 45 addressing. | 328 | * is_c45: Set to true if this phy uses clause 45 addressing. |
329 | * is_internal: Set to true if this phy is internal to a MAC. | 329 | * is_internal: Set to true if this phy is internal to a MAC. |
330 | * has_fixups: Set to true if this phy has fixups/quirks. | ||
331 | * suspended: Set to true if this phy has been suspended successfully. | ||
330 | * state: state of the PHY for management purposes | 332 | * state: state of the PHY for management purposes |
331 | * dev_flags: Device-specific flags used by the PHY driver. | 333 | * dev_flags: Device-specific flags used by the PHY driver. |
332 | * addr: Bus address of PHY | 334 | * addr: Bus address of PHY |
@@ -364,6 +366,7 @@ struct phy_device { | |||
364 | bool is_c45; | 366 | bool is_c45; |
365 | bool is_internal; | 367 | bool is_internal; |
366 | bool has_fixups; | 368 | bool has_fixups; |
369 | bool suspended; | ||
367 | 370 | ||
368 | enum phy_state state; | 371 | enum phy_state state; |
369 | 372 | ||