Merge branch 'pm-domains'
authorRafael J. Wysocki <rjw@sisk.pl>
Fri, 16 Mar 2012 20:50:08 +0000 (21:50 +0100)
committerRafael J. Wysocki <rjw@sisk.pl>
Fri, 16 Mar 2012 20:50:08 +0000 (21:50 +0100)
* pm-domains:
  PM / shmobile: Make MTU2 driver use pm_genpd_dev_always_on()
  PM / shmobile: Make CMT driver use pm_genpd_dev_always_on()
  PM / shmobile: Make TMU driver use pm_genpd_dev_always_on()
  PM / Domains: Introduce "always on" device flag
  PM / Domains: Fix hibernation restore of devices, v2
  PM / Domains: Fix handling of wakeup devices during system resume

arch/arm/mach-shmobile/setup-sh7372.c
drivers/base/power/domain.c
drivers/clocksource/sh_cmt.c
drivers/clocksource/sh_mtu2.c
drivers/clocksource/sh_tmu.c
include/linux/pm_domain.h

index a83cf51fc09906a13018472b4f563baebaf7bcb5..cccf91b8fae196e56cf4770b078f0f08cda9b538 100644 (file)
@@ -1043,6 +1043,8 @@ void __init sh7372_add_standard_devices(void)
        sh7372_add_device_to_domain(&sh7372_a4r, &veu2_device);
        sh7372_add_device_to_domain(&sh7372_a4r, &veu3_device);
        sh7372_add_device_to_domain(&sh7372_a4r, &jpu_device);
+       sh7372_add_device_to_domain(&sh7372_a4r, &tmu00_device);
+       sh7372_add_device_to_domain(&sh7372_a4r, &tmu01_device);
 }
 
 void __init sh7372_add_early_devices(void)
index d2c03239abcf9b1de7f877b2eb7bacac0556c605..b6ff6ecf519d95fd1f18c8e446b1c5f9af1d3991 100644 (file)
@@ -366,7 +366,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
        not_suspended = 0;
        list_for_each_entry(pdd, &genpd->dev_list, list_node)
                if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
-                   || pdd->dev->power.irq_safe))
+                   || pdd->dev->power.irq_safe || to_gpd_data(pdd)->always_on))
                        not_suspended++;
 
        if (not_suspended > genpd->in_progress)
@@ -503,6 +503,9 @@ static int pm_genpd_runtime_suspend(struct device *dev)
 
        might_sleep_if(!genpd->dev_irq_safe);
 
+       if (dev_gpd_data(dev)->always_on)
+               return -EBUSY;
+
        stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
        if (stop_ok && !stop_ok(dev))
                return -EBUSY;
@@ -764,8 +767,10 @@ static int pm_genpd_prepare(struct device *dev)
 
        genpd_acquire_lock(genpd);
 
-       if (genpd->prepared_count++ == 0)
+       if (genpd->prepared_count++ == 0) {
+               genpd->suspended_count = 0;
                genpd->suspend_power_off = genpd->status == GPD_STATE_POWER_OFF;
+       }
 
        genpd_release_lock(genpd);
 
@@ -857,7 +862,7 @@ static int pm_genpd_suspend_noirq(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       if (genpd->suspend_power_off
+       if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on
            || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
                return 0;
 
@@ -890,7 +895,8 @@ static int pm_genpd_resume_noirq(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       if (genpd->suspend_power_off)
+       if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on
+           || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
                return 0;
 
        /*
@@ -1009,7 +1015,8 @@ static int pm_genpd_freeze_noirq(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev);
+       return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ?
+               0 : genpd_stop_dev(genpd, dev);
 }
 
 /**
@@ -1029,7 +1036,8 @@ static int pm_genpd_thaw_noirq(struct device *dev)
        if (IS_ERR(genpd))
                return -EINVAL;
 
-       return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev);
+       return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ?
+               0 : genpd_start_dev(genpd, dev);
 }
 
 /**
@@ -1096,22 +1104,32 @@ static int pm_genpd_restore_noirq(struct device *dev)
         * Since all of the "noirq" callbacks are executed sequentially, it is
         * guaranteed that this function will never run twice in parallel for
         * the same PM domain, so it is not necessary to use locking here.
+        *
+        * At this point suspended_count == 0 means we are being run for the
+        * first time for the given domain in the present cycle.
         */
-       genpd->status = GPD_STATE_POWER_OFF;
-       if (genpd->suspend_power_off) {
+       if (genpd->suspended_count++ == 0) {
                /*
-                * The boot kernel might put the domain into the power on state,
-                * so make sure it really is powered off.
+                * The boot kernel might put the domain into arbitrary state,
+                * so make it appear as powered off to pm_genpd_poweron(), so
+                * that it tries to power it on in case it was really off.
                 */
-               if (genpd->power_off)
-                       genpd->power_off(genpd);
-               return 0;
+               genpd->status = GPD_STATE_POWER_OFF;
+               if (genpd->suspend_power_off) {
+                       /*
+                        * If the domain was off before the hibernation, make
+                        * sure it will be off going forward.
+                        */
+                       if (genpd->power_off)
+                               genpd->power_off(genpd);
+
+                       return 0;
+               }
        }
 
        pm_genpd_poweron(genpd);
-       genpd->suspended_count--;
 
-       return genpd_start_dev(genpd, dev);
+       return dev_gpd_data(dev)->always_on ? 0 : genpd_start_dev(genpd, dev);
 }
 
 /**
@@ -1306,6 +1324,26 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
        return ret;
 }
 
+/**
+ * pm_genpd_dev_always_on - Set/unset the "always on" flag for a given device.
+ * @dev: Device to set/unset the flag for.
+ * @val: The new value of the device's "always on" flag.
+ */
+void pm_genpd_dev_always_on(struct device *dev, bool val)
+{
+       struct pm_subsys_data *psd;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->power.lock, flags);
+
+       psd = dev_to_psd(dev);
+       if (psd && psd->domain_data)
+               to_gpd_data(psd->domain_data)->always_on = val;
+
+       spin_unlock_irqrestore(&dev->power.lock, flags);
+}
+EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on);
+
 /**
  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
  * @genpd: Master PM domain to add the subdomain to.
@@ -1648,7 +1686,6 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
        genpd->poweroff_task = NULL;
        genpd->resume_count = 0;
        genpd->device_count = 0;
-       genpd->suspended_count = 0;
        genpd->max_off_time_ns = -1;
        genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
        genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
index ca09bc421ddbc25345cf3d6d9b0efe237941454c..32fe9ef5cc5c374d3a9dabf5c733aa17bd73342f 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/sh_timer.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/pm_domain.h>
 
 struct sh_cmt_priv {
        void __iomem *mapbase;
@@ -689,6 +690,9 @@ static int __devinit sh_cmt_probe(struct platform_device *pdev)
        struct sh_cmt_priv *p = platform_get_drvdata(pdev);
        int ret;
 
+       if (!is_early_platform_device(pdev))
+               pm_genpd_dev_always_on(&pdev->dev, true);
+
        if (p) {
                dev_info(&pdev->dev, "kept as earlytimer\n");
                return 0;
index db8d5955bad47542f17c24c9186bc3c56be46403..a2172f6904180fd9f30d63cfd3cab2448ab9c856 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/sh_timer.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/pm_domain.h>
 
 struct sh_mtu2_priv {
        void __iomem *mapbase;
@@ -306,6 +307,9 @@ static int __devinit sh_mtu2_probe(struct platform_device *pdev)
        struct sh_mtu2_priv *p = platform_get_drvdata(pdev);
        int ret;
 
+       if (!is_early_platform_device(pdev))
+               pm_genpd_dev_always_on(&pdev->dev, true);
+
        if (p) {
                dev_info(&pdev->dev, "kept as earlytimer\n");
                return 0;
index 079e96ad44e875b34861126a7446004109df1f74..97f54b634be43234d820beb1661b0493bd79637f 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/sh_timer.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/pm_domain.h>
 
 struct sh_tmu_priv {
        void __iomem *mapbase;
@@ -410,6 +411,9 @@ static int __devinit sh_tmu_probe(struct platform_device *pdev)
        struct sh_tmu_priv *p = platform_get_drvdata(pdev);
        int ret;
 
+       if (!is_early_platform_device(pdev))
+               pm_genpd_dev_always_on(&pdev->dev, true);
+
        if (p) {
                dev_info(&pdev->dev, "kept as earlytimer\n");
                return 0;
index 5c2bbc248c11bf6115711a16274c85d8ed2d84ab..1236d262b3e868700e330af2fcb0783aa08702cd 100644 (file)
@@ -99,6 +99,7 @@ struct generic_pm_domain_data {
        struct gpd_dev_ops ops;
        struct gpd_timing_data td;
        bool need_restore;
+       bool always_on;
 };
 
 #ifdef CONFIG_PM_GENERIC_DOMAINS
@@ -137,6 +138,7 @@ static inline int pm_genpd_of_add_device(struct device_node *genpd_node,
 
 extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
                                  struct device *dev);
+extern void pm_genpd_dev_always_on(struct device *dev, bool val);
 extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
                                  struct generic_pm_domain *new_subdomain);
 extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
@@ -179,6 +181,7 @@ static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
 {
        return -ENOSYS;
 }
+static inline void pm_genpd_dev_always_on(struct device *dev, bool val) {}
 static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
                                         struct generic_pm_domain *new_sd)
 {