ARM: 6758/1: amba: support pm ops
authorRabin Vincent <rabin.vincent@stericsson.com>
Wed, 23 Feb 2011 03:33:17 +0000 (04:33 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Wed, 23 Feb 2011 16:26:46 +0000 (16:26 +0000)
Support pm_ops in the AMBA bus, required to allow drivers to use runtime pm.
The implementation of AMBA bus pm ops is based on the platform bus
implementation.

Acked-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
drivers/amba/bus.c
include/linux/amba/bus.h

index 4308899..6d2bb25 100644 (file)
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/io.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/amba/bus.h>
 
 #include <asm/irq.h>
 #include <asm/sizes.h>
 
-#define to_amba_device(d)      container_of(d, struct amba_device, dev)
 #define to_amba_driver(d)      container_of(d, struct amba_driver, drv)
 
 static const struct amba_id *
@@ -57,26 +58,6 @@ static int amba_uevent(struct device *dev, struct kobj_uevent_env *env)
 #define amba_uevent NULL
 #endif
 
-static int amba_suspend(struct device *dev, pm_message_t state)
-{
-       struct amba_driver *drv = to_amba_driver(dev->driver);
-       int ret = 0;
-
-       if (dev->driver && drv->suspend)
-               ret = drv->suspend(to_amba_device(dev), state);
-       return ret;
-}
-
-static int amba_resume(struct device *dev)
-{
-       struct amba_driver *drv = to_amba_driver(dev->driver);
-       int ret = 0;
-
-       if (dev->driver && drv->resume)
-               ret = drv->resume(to_amba_device(dev));
-       return ret;
-}
-
 #define amba_attr_func(name,fmt,arg...)                                        \
 static ssize_t name##_show(struct device *_dev,                                \
                           struct device_attribute *attr, char *buf)    \
@@ -102,6 +83,320 @@ static struct device_attribute amba_dev_attrs[] = {
        __ATTR_NULL,
 };
 
+#ifdef CONFIG_PM_SLEEP
+
+static int amba_legacy_suspend(struct device *dev, pm_message_t mesg)
+{
+       struct amba_driver *adrv = to_amba_driver(dev->driver);
+       struct amba_device *adev = to_amba_device(dev);
+       int ret = 0;
+
+       if (dev->driver && adrv->suspend)
+               ret = adrv->suspend(adev, mesg);
+
+       return ret;
+}
+
+static int amba_legacy_resume(struct device *dev)
+{
+       struct amba_driver *adrv = to_amba_driver(dev->driver);
+       struct amba_device *adev = to_amba_device(dev);
+       int ret = 0;
+
+       if (dev->driver && adrv->resume)
+               ret = adrv->resume(adev);
+
+       return ret;
+}
+
+static int amba_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 amba_pm_complete(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+
+       if (drv && drv->pm && drv->pm->complete)
+               drv->pm->complete(dev);
+}
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define amba_pm_prepare                NULL
+#define amba_pm_complete               NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_SUSPEND
+
+static int amba_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 = amba_legacy_suspend(dev, PMSG_SUSPEND);
+       }
+
+       return ret;
+}
+
+static int amba_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 amba_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 = amba_legacy_resume(dev);
+       }
+
+       return ret;
+}
+
+static int amba_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 amba_pm_suspend                NULL
+#define amba_pm_resume         NULL
+#define amba_pm_suspend_noirq  NULL
+#define amba_pm_resume_noirq   NULL
+
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_HIBERNATION
+
+static int amba_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 = amba_legacy_suspend(dev, PMSG_FREEZE);
+       }
+
+       return ret;
+}
+
+static int amba_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 amba_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 = amba_legacy_resume(dev);
+       }
+
+       return ret;
+}
+
+static int amba_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 amba_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 = amba_legacy_suspend(dev, PMSG_HIBERNATE);
+       }
+
+       return ret;
+}
+
+static int amba_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 amba_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 = amba_legacy_resume(dev);
+       }
+
+       return ret;
+}
+
+static int amba_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 amba_pm_freeze         NULL
+#define amba_pm_thaw           NULL
+#define amba_pm_poweroff               NULL
+#define amba_pm_restore                NULL
+#define amba_pm_freeze_noirq   NULL
+#define amba_pm_thaw_noirq             NULL
+#define amba_pm_poweroff_noirq NULL
+#define amba_pm_restore_noirq  NULL
+
+#endif /* !CONFIG_HIBERNATION */
+
+#ifdef CONFIG_PM
+
+static const struct dev_pm_ops amba_pm = {
+       .prepare        = amba_pm_prepare,
+       .complete       = amba_pm_complete,
+       .suspend        = amba_pm_suspend,
+       .resume         = amba_pm_resume,
+       .freeze         = amba_pm_freeze,
+       .thaw           = amba_pm_thaw,
+       .poweroff       = amba_pm_poweroff,
+       .restore        = amba_pm_restore,
+       .suspend_noirq  = amba_pm_suspend_noirq,
+       .resume_noirq   = amba_pm_resume_noirq,
+       .freeze_noirq   = amba_pm_freeze_noirq,
+       .thaw_noirq     = amba_pm_thaw_noirq,
+       .poweroff_noirq = amba_pm_poweroff_noirq,
+       .restore_noirq  = amba_pm_restore_noirq,
+       SET_RUNTIME_PM_OPS(
+               pm_generic_runtime_suspend,
+               pm_generic_runtime_resume,
+               pm_generic_runtime_idle
+       )
+};
+
+#define AMBA_PM (&amba_pm)
+
+#else /* !CONFIG_PM */
+
+#define AMBA_PM        NULL
+
+#endif /* !CONFIG_PM */
+
 /*
  * Primecells are part of the Advanced Microcontroller Bus Architecture,
  * so we call the bus "amba".
@@ -111,8 +406,7 @@ struct bus_type amba_bustype = {
        .dev_attrs      = amba_dev_attrs,
        .match          = amba_match,
        .uevent         = amba_uevent,
-       .suspend        = amba_suspend,
-       .resume         = amba_resume,
+       .pm             = AMBA_PM,
 };
 
 static int __init amba_init(void)
index 227d590..fcbbe71 100644 (file)
@@ -58,6 +58,8 @@ enum amba_vendor {
 
 extern struct bus_type amba_bustype;
 
+#define to_amba_device(d)      container_of(d, struct amba_device, dev)
+
 #define amba_get_drvdata(d)    dev_get_drvdata(&d->dev)
 #define amba_set_drvdata(d,p)  dev_set_drvdata(&d->dev, p)