]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/amba/bus.c
Merge branch 'x86-geode-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / drivers / amba / bus.c
index d74926e0939e9f9362bcfa09e8301a105bdfb76b..bd230e8011311c3298492018617853672016b86d 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
        )
 };
@@ -426,9 +460,17 @@ static int amba_get_enable_pclk(struct amba_device *pcdev)
        if (IS_ERR(pclk))
                return PTR_ERR(pclk);
 
+       ret = clk_prepare(pclk);
+       if (ret) {
+               clk_put(pclk);
+               return ret;
+       }
+
        ret = clk_enable(pclk);
-       if (ret)
+       if (ret) {
+               clk_unprepare(pclk);
                clk_put(pclk);
+       }
 
        return ret;
 }
@@ -438,6 +480,7 @@ static void amba_put_disable_pclk(struct amba_device *pcdev)
        struct clk *pclk = pcdev->pclk;
 
        clk_disable(pclk);
+       clk_unprepare(pclk);
        clk_put(pclk);
 }
 
@@ -494,10 +537,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 +560,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);