]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/base/power/domain.c
Merge branches 'pm-core', 'pm-qos', 'pm-domains' and 'pm-opp'
[linux.git] / drivers / base / power / domain.c
index 2997026b4dfb00c1daec88f1f18264656a61ffb0..e697dec9d25bf585175a5ee569097f849f9669c6 100644 (file)
@@ -130,7 +130,7 @@ static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev,
 
        ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd);
 
-       /* Warn once for each IRQ safe dev in no sleep domain */
+       /* Warn once if IRQ safe dev in no sleep domain */
        if (ret)
                dev_warn_once(dev, "PM domain %s will not be powered off\n",
                                genpd->name);
@@ -201,7 +201,7 @@ static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
        smp_mb__after_atomic();
 }
 
-static int genpd_power_on(struct generic_pm_domain *genpd, bool timed)
+static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
 {
        unsigned int state_idx = genpd->state_idx;
        ktime_t time_start;
@@ -231,7 +231,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd, bool timed)
        return ret;
 }
 
-static int genpd_power_off(struct generic_pm_domain *genpd, bool timed)
+static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
 {
        unsigned int state_idx = genpd->state_idx;
        ktime_t time_start;
@@ -262,10 +262,10 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed)
 }
 
 /**
- * genpd_queue_power_off_work - Queue up the execution of genpd_poweroff().
+ * genpd_queue_power_off_work - Queue up the execution of genpd_power_off().
  * @genpd: PM domain to power off.
  *
- * Queue up the execution of genpd_poweroff() unless it's already been done
+ * Queue up the execution of genpd_power_off() unless it's already been done
  * before.
  */
 static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
@@ -274,14 +274,101 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
 }
 
 /**
- * genpd_poweron - Restore power to a given PM domain and its masters.
+ * genpd_power_off - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ * @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the
+ * RPM status of the releated device is in an intermediate state, not yet turned
+ * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not
+ * be RPM_SUSPENDED, while it tries to power off the PM domain.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, remove power from @genpd.
+ */
+static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
+                          unsigned int depth)
+{
+       struct pm_domain_data *pdd;
+       struct gpd_link *link;
+       unsigned int not_suspended = 0;
+
+       /*
+        * Do not try to power off the domain in the following situations:
+        * (1) The domain is already in the "power off" state.
+        * (2) System suspend is in progress.
+        */
+       if (genpd->status == GPD_STATE_POWER_OFF
+           || genpd->prepared_count > 0)
+               return 0;
+
+       if (atomic_read(&genpd->sd_count) > 0)
+               return -EBUSY;
+
+       list_for_each_entry(pdd, &genpd->dev_list, list_node) {
+               enum pm_qos_flags_status stat;
+
+               stat = dev_pm_qos_flags(pdd->dev,
+                                       PM_QOS_FLAG_NO_POWER_OFF
+                                               | PM_QOS_FLAG_REMOTE_WAKEUP);
+               if (stat > PM_QOS_FLAGS_NONE)
+                       return -EBUSY;
+
+               /*
+                * Do not allow PM domain to be powered off, when an IRQ safe
+                * device is part of a non-IRQ safe domain.
+                */
+               if (!pm_runtime_suspended(pdd->dev) ||
+                       irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd))
+                       not_suspended++;
+       }
+
+       if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on))
+               return -EBUSY;
+
+       if (genpd->gov && genpd->gov->power_down_ok) {
+               if (!genpd->gov->power_down_ok(&genpd->domain))
+                       return -EAGAIN;
+       }
+
+       if (genpd->power_off) {
+               int ret;
+
+               if (atomic_read(&genpd->sd_count) > 0)
+                       return -EBUSY;
+
+               /*
+                * If sd_count > 0 at this point, one of the subdomains hasn't
+                * managed to call genpd_power_on() for the master yet after
+                * incrementing it.  In that case genpd_power_on() will wait
+                * for us to drop the lock, so we can call .power_off() and let
+                * the genpd_power_on() restore power for us (this shouldn't
+                * happen very often).
+                */
+               ret = _genpd_power_off(genpd, true);
+               if (ret)
+                       return ret;
+       }
+
+       genpd->status = GPD_STATE_POWER_OFF;
+
+       list_for_each_entry(link, &genpd->slave_links, slave_node) {
+               genpd_sd_counter_dec(link->master);
+               genpd_lock_nested(link->master, depth + 1);
+               genpd_power_off(link->master, false, depth + 1);
+               genpd_unlock(link->master);
+       }
+
+       return 0;
+}
+
+/**
+ * genpd_power_on - Restore power to a given PM domain and its masters.
  * @genpd: PM domain to power up.
  * @depth: nesting count for lockdep.
  *
  * Restore power to @genpd and all of its masters so that it is possible to
  * resume a device belonging to it.
  */
-static int genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth)
+static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
 {
        struct gpd_link *link;
        int ret = 0;
@@ -300,7 +387,7 @@ static int genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth)
                genpd_sd_counter_inc(master);
 
                genpd_lock_nested(master, depth + 1);
-               ret = genpd_poweron(master, depth + 1);
+               ret = genpd_power_on(master, depth + 1);
                genpd_unlock(master);
 
                if (ret) {
@@ -309,7 +396,7 @@ static int genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth)
                }
        }
 
-       ret = genpd_power_on(genpd, true);
+       ret = _genpd_power_on(genpd, true);
        if (ret)
                goto err;
 
@@ -321,7 +408,9 @@ static int genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth)
                                        &genpd->slave_links,
                                        slave_node) {
                genpd_sd_counter_dec(link->master);
-               genpd_queue_power_off_work(link->master);
+               genpd_lock_nested(link->master, depth + 1);
+               genpd_power_off(link->master, false, depth + 1);
+               genpd_unlock(link->master);
        }
 
        return ret;
@@ -367,87 +456,6 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
        return NOTIFY_DONE;
 }
 
-/**
- * genpd_poweroff - Remove power from a given PM domain.
- * @genpd: PM domain to power down.
- * @is_async: PM domain is powered down from a scheduled work
- *
- * If all of the @genpd's devices have been suspended and all of its subdomains
- * have been powered down, remove power from @genpd.
- */
-static int genpd_poweroff(struct generic_pm_domain *genpd, bool is_async)
-{
-       struct pm_domain_data *pdd;
-       struct gpd_link *link;
-       unsigned int not_suspended = 0;
-
-       /*
-        * Do not try to power off the domain in the following situations:
-        * (1) The domain is already in the "power off" state.
-        * (2) System suspend is in progress.
-        */
-       if (genpd->status == GPD_STATE_POWER_OFF
-           || genpd->prepared_count > 0)
-               return 0;
-
-       if (atomic_read(&genpd->sd_count) > 0)
-               return -EBUSY;
-
-       list_for_each_entry(pdd, &genpd->dev_list, list_node) {
-               enum pm_qos_flags_status stat;
-
-               stat = dev_pm_qos_flags(pdd->dev,
-                                       PM_QOS_FLAG_NO_POWER_OFF
-                                               | PM_QOS_FLAG_REMOTE_WAKEUP);
-               if (stat > PM_QOS_FLAGS_NONE)
-                       return -EBUSY;
-
-               /*
-                * Do not allow PM domain to be powered off, when an IRQ safe
-                * device is part of a non-IRQ safe domain.
-                */
-               if (!pm_runtime_suspended(pdd->dev) ||
-                       irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd))
-                       not_suspended++;
-       }
-
-       if (not_suspended > 1 || (not_suspended == 1 && is_async))
-               return -EBUSY;
-
-       if (genpd->gov && genpd->gov->power_down_ok) {
-               if (!genpd->gov->power_down_ok(&genpd->domain))
-                       return -EAGAIN;
-       }
-
-       if (genpd->power_off) {
-               int ret;
-
-               if (atomic_read(&genpd->sd_count) > 0)
-                       return -EBUSY;
-
-               /*
-                * If sd_count > 0 at this point, one of the subdomains hasn't
-                * managed to call genpd_poweron() for the master yet after
-                * incrementing it.  In that case genpd_poweron() will wait
-                * for us to drop the lock, so we can call .power_off() and let
-                * the genpd_poweron() restore power for us (this shouldn't
-                * happen very often).
-                */
-               ret = genpd_power_off(genpd, true);
-               if (ret)
-                       return ret;
-       }
-
-       genpd->status = GPD_STATE_POWER_OFF;
-
-       list_for_each_entry(link, &genpd->slave_links, slave_node) {
-               genpd_sd_counter_dec(link->master);
-               genpd_queue_power_off_work(link->master);
-       }
-
-       return 0;
-}
-
 /**
  * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
  * @work: Work structure used for scheduling the execution of this function.
@@ -459,7 +467,7 @@ static void genpd_power_off_work_fn(struct work_struct *work)
        genpd = container_of(work, struct generic_pm_domain, power_off_work);
 
        genpd_lock(genpd);
-       genpd_poweroff(genpd, true);
+       genpd_power_off(genpd, false, 0);
        genpd_unlock(genpd);
 }
 
@@ -578,7 +586,7 @@ static int genpd_runtime_suspend(struct device *dev)
                return 0;
 
        genpd_lock(genpd);
-       genpd_poweroff(genpd, false);
+       genpd_power_off(genpd, true, 0);
        genpd_unlock(genpd);
 
        return 0;
@@ -618,7 +626,7 @@ static int genpd_runtime_resume(struct device *dev)
        }
 
        genpd_lock(genpd);
-       ret = genpd_poweron(genpd, 0);
+       ret = genpd_power_on(genpd, 0);
        genpd_unlock(genpd);
 
        if (ret)
@@ -658,7 +666,7 @@ static int genpd_runtime_resume(struct device *dev)
        if (!pm_runtime_is_irq_safe(dev) ||
                (pm_runtime_is_irq_safe(dev) && genpd_is_irq_safe(genpd))) {
                genpd_lock(genpd);
-               genpd_poweroff(genpd, 0);
+               genpd_power_off(genpd, true, 0);
                genpd_unlock(genpd);
        }
 
@@ -674,9 +682,9 @@ static int __init pd_ignore_unused_setup(char *__unused)
 __setup("pd_ignore_unused", pd_ignore_unused_setup);
 
 /**
- * genpd_poweroff_unused - Power off all PM domains with no devices in use.
+ * genpd_power_off_unused - Power off all PM domains with no devices in use.
  */
-static int __init genpd_poweroff_unused(void)
+static int __init genpd_power_off_unused(void)
 {
        struct generic_pm_domain *genpd;
 
@@ -694,7 +702,7 @@ static int __init genpd_poweroff_unused(void)
 
        return 0;
 }
-late_initcall(genpd_poweroff_unused);
+late_initcall(genpd_power_off_unused);
 
 #if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF)
 
@@ -727,18 +735,20 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
 }
 
 /**
- * genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
+ * genpd_sync_power_off - Synchronously power off a PM domain and its masters.
  * @genpd: PM domain to power off, if possible.
+ * @use_lock: use the lock.
+ * @depth: nesting count for lockdep.
  *
  * Check if the given PM domain can be powered off (during system suspend or
  * hibernation) and do that if so.  Also, in that case propagate to its masters.
  *
  * This function is only called in "noirq" and "syscore" stages of system power
- * transitions, so it need not acquire locks (all of the "noirq" callbacks are
- * executed sequentially, so it is guaranteed that it will never run twice in
- * parallel).
+ * transitions. The "noirq" callbacks may be executed asynchronously, thus in
+ * these cases the lock must be held.
  */
-static void genpd_sync_poweroff(struct generic_pm_domain *genpd)
+static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
+                                unsigned int depth)
 {
        struct gpd_link *link;
 
@@ -751,26 +761,35 @@ static void genpd_sync_poweroff(struct generic_pm_domain *genpd)
 
        /* Choose the deepest state when suspending */
        genpd->state_idx = genpd->state_count - 1;
-       genpd_power_off(genpd, false);
+       _genpd_power_off(genpd, false);
 
        genpd->status = GPD_STATE_POWER_OFF;
 
        list_for_each_entry(link, &genpd->slave_links, slave_node) {
                genpd_sd_counter_dec(link->master);
-               genpd_sync_poweroff(link->master);
+
+               if (use_lock)
+                       genpd_lock_nested(link->master, depth + 1);
+
+               genpd_sync_power_off(link->master, use_lock, depth + 1);
+
+               if (use_lock)
+                       genpd_unlock(link->master);
        }
 }
 
 /**
- * genpd_sync_poweron - Synchronously power on a PM domain and its masters.
+ * genpd_sync_power_on - Synchronously power on a PM domain and its masters.
  * @genpd: PM domain to power on.
+ * @use_lock: use the lock.
+ * @depth: nesting count for lockdep.
  *
  * This function is only called in "noirq" and "syscore" stages of system power
- * transitions, so it need not acquire locks (all of the "noirq" callbacks are
- * executed sequentially, so it is guaranteed that it will never run twice in
- * parallel).
+ * transitions. The "noirq" callbacks may be executed asynchronously, thus in
+ * these cases the lock must be held.
  */
-static void genpd_sync_poweron(struct generic_pm_domain *genpd)
+static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock,
+                               unsigned int depth)
 {
        struct gpd_link *link;
 
@@ -778,11 +797,18 @@ static void genpd_sync_poweron(struct generic_pm_domain *genpd)
                return;
 
        list_for_each_entry(link, &genpd->slave_links, slave_node) {
-               genpd_sync_poweron(link->master);
                genpd_sd_counter_inc(link->master);
+
+               if (use_lock)
+                       genpd_lock_nested(link->master, depth + 1);
+
+               genpd_sync_power_on(link->master, use_lock, depth + 1);
+
+               if (use_lock)
+                       genpd_unlock(link->master);
        }
 
-       genpd_power_on(genpd, false);
+       _genpd_power_on(genpd, false);
 
        genpd->status = GPD_STATE_ACTIVE;
 }
@@ -888,13 +914,10 @@ static int pm_genpd_suspend_noirq(struct device *dev)
                        return ret;
        }
 
-       /*
-        * 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.
-        */
+       genpd_lock(genpd);
        genpd->suspended_count++;
-       genpd_sync_poweroff(genpd);
+       genpd_sync_power_off(genpd, true, 0);
+       genpd_unlock(genpd);
 
        return 0;
 }
@@ -919,13 +942,10 @@ static int pm_genpd_resume_noirq(struct device *dev)
        if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))
                return 0;
 
-       /*
-        * 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.
-        */
-       genpd_sync_poweron(genpd);
+       genpd_lock(genpd);
+       genpd_sync_power_on(genpd, true, 0);
        genpd->suspended_count--;
+       genpd_unlock(genpd);
 
        if (genpd->dev_ops.stop && genpd->dev_ops.start)
                ret = pm_runtime_force_resume(dev);
@@ -1002,22 +1022,20 @@ static int pm_genpd_restore_noirq(struct device *dev)
                return -EINVAL;
 
        /*
-        * 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_lock(genpd);
        if (genpd->suspended_count++ == 0)
                /*
                 * The boot kernel might put the domain into arbitrary state,
-                * so make it appear as powered off to genpd_sync_poweron(),
+                * so make it appear as powered off to genpd_sync_power_on(),
                 * so that it tries to power it on in case it was really off.
                 */
                genpd->status = GPD_STATE_POWER_OFF;
 
-       genpd_sync_poweron(genpd);
+       genpd_sync_power_on(genpd, true, 0);
+       genpd_unlock(genpd);
 
        if (genpd->dev_ops.stop && genpd->dev_ops.start)
                ret = pm_runtime_force_resume(dev);
@@ -1072,9 +1090,9 @@ static void genpd_syscore_switch(struct device *dev, bool suspend)
 
        if (suspend) {
                genpd->suspended_count++;
-               genpd_sync_poweroff(genpd);
+               genpd_sync_power_off(genpd, false, 0);
        } else {
-               genpd_sync_poweron(genpd);
+               genpd_sync_power_on(genpd, false, 0);
                genpd->suspended_count--;
        }
 }
@@ -2043,7 +2061,7 @@ int genpd_dev_pm_attach(struct device *dev)
        dev->pm_domain->sync = genpd_dev_pm_sync;
 
        genpd_lock(pd);
-       ret = genpd_poweron(pd, 0);
+       ret = genpd_power_on(pd, 0);
        genpd_unlock(pd);
 out:
        return ret ? -EPROBE_DEFER : 0;