|
@@ -21,6 +21,7 @@
|
|
|
#include <linux/interrupt.h>
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/delay.h>
|
|
|
+#include <linux/device.h>
|
|
|
#include <linux/netdevice.h>
|
|
|
#include <linux/etherdevice.h>
|
|
|
#include <linux/skbuff.h>
|
|
@@ -286,33 +287,58 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv)
|
|
|
(phydev->phy_id & phydrv->phy_id_mask));
|
|
|
}
|
|
|
|
|
|
+static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
|
|
|
+{
|
|
|
+ struct device_driver *drv = phydev->dev.driver;
|
|
|
+ struct phy_driver *phydrv = to_phy_driver(drv);
|
|
|
+ struct net_device *netdev = phydev->attached_dev;
|
|
|
+
|
|
|
+ if (!drv || !phydrv->suspend)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /* PHY not attached? May suspend. */
|
|
|
+ if (!netdev)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Don't suspend PHY if the attched netdev parent may wakeup.
|
|
|
+ * The parent may point to a PCI device, as in tg3 driver.
|
|
|
+ */
|
|
|
+ if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Also don't suspend PHY if the netdev itself may wakeup. This
|
|
|
+ * is the case for devices w/o underlaying pwr. mgmt. aware bus,
|
|
|
+ * e.g. SoC devices.
|
|
|
+ */
|
|
|
+ if (device_may_wakeup(&netdev->dev))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
/* Suspend and resume. Copied from platform_suspend and
|
|
|
* platform_resume
|
|
|
*/
|
|
|
static int mdio_bus_suspend(struct device * dev, pm_message_t state)
|
|
|
{
|
|
|
- int ret = 0;
|
|
|
- struct device_driver *drv = dev->driver;
|
|
|
- struct phy_driver *phydrv = to_phy_driver(drv);
|
|
|
+ struct phy_driver *phydrv = to_phy_driver(dev->driver);
|
|
|
struct phy_device *phydev = to_phy_device(dev);
|
|
|
|
|
|
- if (drv && phydrv->suspend && !device_may_wakeup(phydev->dev.parent))
|
|
|
- ret = phydrv->suspend(phydev);
|
|
|
-
|
|
|
- return ret;
|
|
|
+ if (!mdio_bus_phy_may_suspend(phydev))
|
|
|
+ return 0;
|
|
|
+ return phydrv->suspend(phydev);
|
|
|
}
|
|
|
|
|
|
static int mdio_bus_resume(struct device * dev)
|
|
|
{
|
|
|
- int ret = 0;
|
|
|
- struct device_driver *drv = dev->driver;
|
|
|
- struct phy_driver *phydrv = to_phy_driver(drv);
|
|
|
+ struct phy_driver *phydrv = to_phy_driver(dev->driver);
|
|
|
struct phy_device *phydev = to_phy_device(dev);
|
|
|
|
|
|
- if (drv && phydrv->resume && !device_may_wakeup(phydev->dev.parent))
|
|
|
- ret = phydrv->resume(phydev);
|
|
|
-
|
|
|
- return ret;
|
|
|
+ if (!mdio_bus_phy_may_suspend(phydev))
|
|
|
+ return 0;
|
|
|
+ return phydrv->resume(phydev);
|
|
|
}
|
|
|
|
|
|
struct bus_type mdio_bus_type = {
|