From 2f5cb43406d0b29b96248f5328a14a6f6abf8ae6 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Wed, 30 Dec 2009 08:23:30 +0000 Subject: phylib: Properly reinitialize PHYs after hibernation Since hibernation assumes power loss, we should fully reinitialize PHYs (including platform fixups), as if PHYs were just attached. This patch factors phy_init_hw() out of phy_attach_direct(), then converts mdio_bus to dev_pm_ops and adds an appropriate restore() callback. Signed-off-by: Anton Vorontsov Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 50 +++++++++++++++++++++++++++++++++++++------- drivers/net/phy/phy_device.c | 30 +++++++++++++------------- 2 files changed, 58 insertions(+), 22 deletions(-) (limited to 'drivers/net/phy') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 49252d390903..e17b70291bbc 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -264,6 +264,8 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv) (phydev->phy_id & phydrv->phy_id_mask)); } +#ifdef CONFIG_PM + static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) { struct device_driver *drv = phydev->dev.driver; @@ -295,10 +297,7 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) return true; } -/* Suspend and resume. Copied from platform_suspend and - * platform_resume - */ -static int mdio_bus_suspend(struct device * dev, pm_message_t state) +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); @@ -318,7 +317,7 @@ static int mdio_bus_suspend(struct device * dev, pm_message_t state) return phydrv->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); @@ -338,11 +337,48 @@ no_resume: return 0; } +static int mdio_bus_restore(struct device *dev) +{ + struct phy_device *phydev = to_phy_device(dev); + struct net_device *netdev = phydev->attached_dev; + int ret; + + if (!netdev) + return 0; + + ret = phy_init_hw(phydev); + if (ret < 0) + return ret; + + /* The PHY needs to renegotiate. */ + phydev->link = 0; + phydev->state = PHY_UP; + + phy_start_machine(phydev, NULL); + + return 0; +} + +static struct dev_pm_ops mdio_bus_pm_ops = { + .suspend = mdio_bus_suspend, + .resume = mdio_bus_resume, + .freeze = mdio_bus_suspend, + .thaw = mdio_bus_resume, + .restore = mdio_bus_restore, +}; + +#define MDIO_BUS_PM_OPS (&mdio_bus_pm_ops) + +#else + +#define MDIO_BUS_PM_OPS NULL + +#endif /* CONFIG_PM */ + struct bus_type mdio_bus_type = { .name = "mdio_bus", .match = mdio_bus_match, - .suspend = mdio_bus_suspend, - .resume = mdio_bus_resume, + .pm = MDIO_BUS_PM_OPS, }; EXPORT_SYMBOL(mdio_bus_type); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index b10fedd82143..8212b2b93422 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -378,6 +378,20 @@ void phy_disconnect(struct phy_device *phydev) } EXPORT_SYMBOL(phy_disconnect); +int phy_init_hw(struct phy_device *phydev) +{ + int ret; + + if (!phydev->drv || !phydev->drv->config_init) + return 0; + + ret = phy_scan_fixups(phydev); + if (ret < 0) + return ret; + + return phydev->drv->config_init(phydev); +} + /** * phy_attach_direct - attach a network device to a given PHY device pointer * @dev: network device to attach @@ -425,21 +439,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, /* Do initial configuration here, now that * we have certain key parameters * (dev_flags and interface) */ - if (phydev->drv->config_init) { - int err; - - err = phy_scan_fixups(phydev); - - if (err < 0) - return err; - - err = phydev->drv->config_init(phydev); - - if (err < 0) - return err; - } - - return 0; + return phy_init_hw(phydev); } EXPORT_SYMBOL(phy_attach_direct); -- cgit v1.2.3