From d35ef90bf9e7cab9aa85e9c0724bd1ac6f784601 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 12 Oct 2009 05:50:41 +0000 Subject: [PATCH] of/platform: Implement support for dev_pm_ops Linux power management subsystem supports vast amount of new PM callbacks that are crucial for proper suspend and hibernation support in drivers. This patch implements support for dev_pm_ops, preserving support for legacy callbacks. Signed-off-by: Anton Vorontsov Signed-off-by: Benjamin Herrenschmidt --- drivers/of/platform.c | 323 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 299 insertions(+), 24 deletions(-) diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 298de0f95d70..d58ade170c4b 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -65,28 +65,6 @@ static int of_platform_device_remove(struct device *dev) return 0; } -static int of_platform_device_suspend(struct device *dev, pm_message_t state) -{ - struct of_device *of_dev = to_of_device(dev); - struct of_platform_driver *drv = to_of_platform_driver(dev->driver); - int error = 0; - - if (dev->driver && drv->suspend) - error = drv->suspend(of_dev, state); - return error; -} - -static int of_platform_device_resume(struct device * dev) -{ - struct of_device *of_dev = to_of_device(dev); - struct of_platform_driver *drv = to_of_platform_driver(dev->driver); - int error = 0; - - if (dev->driver && drv->resume) - error = drv->resume(of_dev); - return error; -} - static void of_platform_device_shutdown(struct device *dev) { struct of_device *of_dev = to_of_device(dev); @@ -96,16 +74,313 @@ static void of_platform_device_shutdown(struct device *dev) drv->shutdown(of_dev); } +#ifdef CONFIG_PM_SLEEP + +static int of_platform_legacy_suspend(struct device *dev, pm_message_t mesg) +{ + struct of_device *of_dev = to_of_device(dev); + struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + int ret = 0; + + if (dev->driver && drv->suspend) + ret = drv->suspend(of_dev, mesg); + return ret; +} + +static int of_platform_legacy_resume(struct device *dev) +{ + struct of_device *of_dev = to_of_device(dev); + struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + int ret = 0; + + if (dev->driver && drv->resume) + ret = drv->resume(of_dev); + return ret; +} + +static int of_platform_pm_prepare(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm && drv->pm->prepare) + ret = drv->pm->prepare(dev); + + return ret; +} + +static void of_platform_pm_complete(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->complete) + drv->pm->complete(dev); +} + +#ifdef CONFIG_SUSPEND + +static int of_platform_pm_suspend(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->suspend) + ret = drv->pm->suspend(dev); + } else { + ret = of_platform_legacy_suspend(dev, PMSG_SUSPEND); + } + + return ret; +} + +static int of_platform_pm_suspend_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->suspend_noirq) + ret = drv->pm->suspend_noirq(dev); + } + + return ret; +} + +static int of_platform_pm_resume(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->resume) + ret = drv->pm->resume(dev); + } else { + ret = of_platform_legacy_resume(dev); + } + + return ret; +} + +static int of_platform_pm_resume_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->resume_noirq) + ret = drv->pm->resume_noirq(dev); + } + + return ret; +} + +#else /* !CONFIG_SUSPEND */ + +#define of_platform_pm_suspend NULL +#define of_platform_pm_resume NULL +#define of_platform_pm_suspend_noirq NULL +#define of_platform_pm_resume_noirq NULL + +#endif /* !CONFIG_SUSPEND */ + +#ifdef CONFIG_HIBERNATION + +static int of_platform_pm_freeze(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->freeze) + ret = drv->pm->freeze(dev); + } else { + ret = of_platform_legacy_suspend(dev, PMSG_FREEZE); + } + + return ret; +} + +static int of_platform_pm_freeze_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->freeze_noirq) + ret = drv->pm->freeze_noirq(dev); + } + + return ret; +} + +static int of_platform_pm_thaw(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->thaw) + ret = drv->pm->thaw(dev); + } else { + ret = of_platform_legacy_resume(dev); + } + + return ret; +} + +static int of_platform_pm_thaw_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->thaw_noirq) + ret = drv->pm->thaw_noirq(dev); + } + + return ret; +} + +static int of_platform_pm_poweroff(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->poweroff) + ret = drv->pm->poweroff(dev); + } else { + ret = of_platform_legacy_suspend(dev, PMSG_HIBERNATE); + } + + return ret; +} + +static int of_platform_pm_poweroff_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->poweroff_noirq) + ret = drv->pm->poweroff_noirq(dev); + } + + return ret; +} + +static int of_platform_pm_restore(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->restore) + ret = drv->pm->restore(dev); + } else { + ret = of_platform_legacy_resume(dev); + } + + return ret; +} + +static int of_platform_pm_restore_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->restore_noirq) + ret = drv->pm->restore_noirq(dev); + } + + return ret; +} + +#else /* !CONFIG_HIBERNATION */ + +#define of_platform_pm_freeze NULL +#define of_platform_pm_thaw NULL +#define of_platform_pm_poweroff NULL +#define of_platform_pm_restore NULL +#define of_platform_pm_freeze_noirq NULL +#define of_platform_pm_thaw_noirq NULL +#define of_platform_pm_poweroff_noirq NULL +#define of_platform_pm_restore_noirq NULL + +#endif /* !CONFIG_HIBERNATION */ + +static struct dev_pm_ops of_platform_dev_pm_ops = { + .prepare = of_platform_pm_prepare, + .complete = of_platform_pm_complete, + .suspend = of_platform_pm_suspend, + .resume = of_platform_pm_resume, + .freeze = of_platform_pm_freeze, + .thaw = of_platform_pm_thaw, + .poweroff = of_platform_pm_poweroff, + .restore = of_platform_pm_restore, + .suspend_noirq = of_platform_pm_suspend_noirq, + .resume_noirq = of_platform_pm_resume_noirq, + .freeze_noirq = of_platform_pm_freeze_noirq, + .thaw_noirq = of_platform_pm_thaw_noirq, + .poweroff_noirq = of_platform_pm_poweroff_noirq, + .restore_noirq = of_platform_pm_restore_noirq, +}; + +#define OF_PLATFORM_PM_OPS_PTR (&of_platform_dev_pm_ops) + +#else /* !CONFIG_PM_SLEEP */ + +#define OF_PLATFORM_PM_OPS_PTR NULL + +#endif /* !CONFIG_PM_SLEEP */ + int of_bus_type_init(struct bus_type *bus, const char *name) { bus->name = name; bus->match = of_platform_bus_match; bus->probe = of_platform_device_probe; bus->remove = of_platform_device_remove; - bus->suspend = of_platform_device_suspend; - bus->resume = of_platform_device_resume; bus->shutdown = of_platform_device_shutdown; bus->dev_attrs = of_platform_device_attrs; + bus->pm = OF_PLATFORM_PM_OPS_PTR; return bus_register(bus); }