PM: add runtime PM support to core Primecell driver
[pandora-kernel.git] / drivers / amba / bus.c
index d74926e..84bdaac 100644 (file)
@@ -365,6 +365,40 @@ static int amba_pm_restore_noirq(struct device *dev)
 
 #endif /* !CONFIG_HIBERNATE_CALLBACKS */
 
+#ifdef CONFIG_PM_RUNTIME
+/*
+ * Hooks to provide runtime PM of the pclk (bus clock).  It is safe to
+ * enable/disable the bus clock at runtime PM suspend/resume as this
+ * does not result in loss of context.  However, disabling vcore power
+ * would do, so we leave that to the driver.
+ */
+static int amba_pm_runtime_suspend(struct device *dev)
+{
+       struct amba_device *pcdev = to_amba_device(dev);
+       int ret = pm_generic_runtime_suspend(dev);
+
+       if (ret == 0 && dev->driver)
+               clk_disable(pcdev->pclk);
+
+       return ret;
+}
+
+static int amba_pm_runtime_resume(struct device *dev)
+{
+       struct amba_device *pcdev = to_amba_device(dev);
+       int ret;
+
+       if (dev->driver) {
+               ret = clk_enable(pcdev->pclk);
+               /* Failure is probably fatal to the system, but... */
+               if (ret)
+                       return ret;
+       }
+
+       return pm_generic_runtime_resume(dev);
+}
+#endif
+
 #ifdef CONFIG_PM
 
 static const struct dev_pm_ops amba_pm = {
@@ -383,8 +417,8 @@ static const struct dev_pm_ops amba_pm = {
        .poweroff_noirq = amba_pm_poweroff_noirq,
        .restore_noirq  = amba_pm_restore_noirq,
        SET_RUNTIME_PM_OPS(
-               pm_generic_runtime_suspend,
-               pm_generic_runtime_resume,
+               amba_pm_runtime_suspend,
+               amba_pm_runtime_resume,
                pm_generic_runtime_idle
        )
 };
@@ -494,10 +528,18 @@ static int amba_probe(struct device *dev)
                if (ret)
                        break;
 
+               pm_runtime_get_noresume(dev);
+               pm_runtime_set_active(dev);
+               pm_runtime_enable(dev);
+
                ret = pcdrv->probe(pcdev, id);
                if (ret == 0)
                        break;
 
+               pm_runtime_disable(dev);
+               pm_runtime_set_suspended(dev);
+               pm_runtime_put_noidle(dev);
+
                amba_put_disable_pclk(pcdev);
                amba_put_disable_vcore(pcdev);
        } while (0);
@@ -509,7 +551,16 @@ static int amba_remove(struct device *dev)
 {
        struct amba_device *pcdev = to_amba_device(dev);
        struct amba_driver *drv = to_amba_driver(dev->driver);
-       int ret = drv->remove(pcdev);
+       int ret;
+
+       pm_runtime_get_sync(dev);
+       ret = drv->remove(pcdev);
+       pm_runtime_put_noidle(dev);
+
+       /* Undo the runtime PM settings in amba_probe() */
+       pm_runtime_disable(dev);
+       pm_runtime_set_suspended(dev);
+       pm_runtime_put_noidle(dev);
 
        amba_put_disable_pclk(pcdev);
        amba_put_disable_vcore(pcdev);