]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/base/power/main.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / base / power / main.c
index ead3e79d6fcf10f684f8d8f55a30d7254356ff42..83404973f97aa31fe1065bb5c93351b8e88640f7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * The driver model core calls device_pm_add() when a device is registered.
- * This will intialize the embedded device_pm_info object in the device
+ * This will initialize the embedded device_pm_info object in the device
  * and add it to the list of power-controlled devices. sysfs entries for
  * controlling device power management will also be added.
  *
@@ -26,6 +26,7 @@
 #include <linux/interrupt.h>
 #include <linux/sched.h>
 #include <linux/async.h>
+#include <linux/suspend.h>
 
 #include "../base.h"
 #include "power.h"
  */
 
 LIST_HEAD(dpm_list);
+LIST_HEAD(dpm_prepared_list);
+LIST_HEAD(dpm_suspended_list);
+LIST_HEAD(dpm_noirq_list);
 
 static DEFINE_MUTEX(dpm_list_mtx);
 static pm_message_t pm_transition;
 
-/*
- * Set once the preparation of devices for a PM transition has started, reset
- * before starting to resume devices.  Protected by dpm_list_mtx.
- */
-static bool transition_started;
-
 static int async_error;
 
 /**
@@ -59,7 +57,7 @@ static int async_error;
  */
 void device_pm_init(struct device *dev)
 {
-       dev->power.status = DPM_ON;
+       dev->power.in_suspend = false;
        init_completion(&dev->power.completion);
        complete_all(&dev->power.completion);
        dev->power.wakeup = NULL;
@@ -90,22 +88,11 @@ void device_pm_unlock(void)
 void device_pm_add(struct device *dev)
 {
        pr_debug("PM: Adding info for %s:%s\n",
-                dev->bus ? dev->bus->name : "No Bus",
-                kobject_name(&dev->kobj));
+                dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
        mutex_lock(&dpm_list_mtx);
-       if (dev->parent) {
-               if (dev->parent->power.status >= DPM_SUSPENDING)
-                       dev_warn(dev, "parent %s should not be sleeping\n",
-                                dev_name(dev->parent));
-       } else if (transition_started) {
-               /*
-                * We refuse to register parentless devices while a PM
-                * transition is in progress in order to avoid leaving them
-                * unhandled down the road
-                */
-               dev_WARN(dev, "Parentless device registered during a PM transaction\n");
-       }
-
+       if (dev->parent && dev->parent->power.in_suspend)
+               dev_warn(dev, "parent %s should not be sleeping\n",
+                       dev_name(dev->parent));
        list_add_tail(&dev->power.entry, &dpm_list);
        mutex_unlock(&dpm_list_mtx);
 }
@@ -117,8 +104,7 @@ void device_pm_add(struct device *dev)
 void device_pm_remove(struct device *dev)
 {
        pr_debug("PM: Removing info for %s:%s\n",
-                dev->bus ? dev->bus->name : "No Bus",
-                kobject_name(&dev->kobj));
+                dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
        complete_all(&dev->power.completion);
        mutex_lock(&dpm_list_mtx);
        list_del_init(&dev->power.entry);
@@ -135,10 +121,8 @@ void device_pm_remove(struct device *dev)
 void device_pm_move_before(struct device *deva, struct device *devb)
 {
        pr_debug("PM: Moving %s:%s before %s:%s\n",
-                deva->bus ? deva->bus->name : "No Bus",
-                kobject_name(&deva->kobj),
-                devb->bus ? devb->bus->name : "No Bus",
-                kobject_name(&devb->kobj));
+                deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
+                devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
        /* Delete deva from dpm_list and reinsert before devb. */
        list_move_tail(&deva->power.entry, &devb->power.entry);
 }
@@ -151,10 +135,8 @@ void device_pm_move_before(struct device *deva, struct device *devb)
 void device_pm_move_after(struct device *deva, struct device *devb)
 {
        pr_debug("PM: Moving %s:%s after %s:%s\n",
-                deva->bus ? deva->bus->name : "No Bus",
-                kobject_name(&deva->kobj),
-                devb->bus ? devb->bus->name : "No Bus",
-                kobject_name(&devb->kobj));
+                deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
+                devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
        /* Delete deva from dpm_list and reinsert after devb. */
        list_move(&deva->power.entry, &devb->power.entry);
 }
@@ -166,8 +148,7 @@ void device_pm_move_after(struct device *deva, struct device *devb)
 void device_pm_move_last(struct device *dev)
 {
        pr_debug("PM: Moving %s:%s to end of list\n",
-                dev->bus ? dev->bus->name : "No Bus",
-                kobject_name(&dev->kobj));
+                dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
        list_move_tail(&dev->power.entry, &dpm_list);
 }
 
@@ -303,7 +284,7 @@ static int pm_noirq_op(struct device *dev,
                        pm_message_t state)
 {
        int error = 0;
-       ktime_t calltime, delta, rettime;
+       ktime_t calltime = ktime_set(0, 0), delta, rettime;
 
        if (initcall_debug) {
                pr_info("calling  %s+ @ %i, parent: %s\n",
@@ -405,7 +386,7 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info,
                        int error)
 {
        printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n",
-               kobject_name(&dev->kobj), pm_verb(state.event), info, error);
+               dev_name(dev), pm_verb(state.event), info, error);
 }
 
 static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info)
@@ -475,33 +456,24 @@ End:
  */
 void dpm_resume_noirq(pm_message_t state)
 {
-       struct list_head list;
        ktime_t starttime = ktime_get();
 
-       INIT_LIST_HEAD(&list);
        mutex_lock(&dpm_list_mtx);
-       transition_started = false;
-       while (!list_empty(&dpm_list)) {
-               struct device *dev = to_device(dpm_list.next);
+       while (!list_empty(&dpm_noirq_list)) {
+               struct device *dev = to_device(dpm_noirq_list.next);
+               int error;
 
                get_device(dev);
-               if (dev->power.status > DPM_OFF) {
-                       int error;
-
-                       dev->power.status = DPM_OFF;
-                       mutex_unlock(&dpm_list_mtx);
+               list_move_tail(&dev->power.entry, &dpm_suspended_list);
+               mutex_unlock(&dpm_list_mtx);
 
-                       error = device_resume_noirq(dev, state);
+               error = device_resume_noirq(dev, state);
+               if (error)
+                       pm_dev_err(dev, state, " early", error);
 
-                       mutex_lock(&dpm_list_mtx);
-                       if (error)
-                               pm_dev_err(dev, state, " early", error);
-               }
-               if (!list_empty(&dev->power.entry))
-                       list_move_tail(&dev->power.entry, &list);
+               mutex_lock(&dpm_list_mtx);
                put_device(dev);
        }
-       list_splice(&list, &dpm_list);
        mutex_unlock(&dpm_list_mtx);
        dpm_show_time(starttime, state, "early");
        resume_device_irqs();
@@ -544,7 +516,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
        dpm_wait(dev->parent, async);
        device_lock(dev);
 
-       dev->power.status = DPM_RESUMING;
+       dev->power.in_suspend = false;
 
        if (dev->bus) {
                if (dev->bus->pm) {
@@ -610,19 +582,14 @@ static bool is_async(struct device *dev)
  */
 static void dpm_resume(pm_message_t state)
 {
-       struct list_head list;
        struct device *dev;
        ktime_t starttime = ktime_get();
 
-       INIT_LIST_HEAD(&list);
        mutex_lock(&dpm_list_mtx);
        pm_transition = state;
        async_error = 0;
 
-       list_for_each_entry(dev, &dpm_list, power.entry) {
-               if (dev->power.status < DPM_OFF)
-                       continue;
-
+       list_for_each_entry(dev, &dpm_suspended_list, power.entry) {
                INIT_COMPLETION(dev->power.completion);
                if (is_async(dev)) {
                        get_device(dev);
@@ -630,28 +597,24 @@ static void dpm_resume(pm_message_t state)
                }
        }
 
-       while (!list_empty(&dpm_list)) {
-               dev = to_device(dpm_list.next);
+       while (!list_empty(&dpm_suspended_list)) {
+               dev = to_device(dpm_suspended_list.next);
                get_device(dev);
-               if (dev->power.status >= DPM_OFF && !is_async(dev)) {
+               if (!is_async(dev)) {
                        int error;
 
                        mutex_unlock(&dpm_list_mtx);
 
                        error = device_resume(dev, state, false);
-
-                       mutex_lock(&dpm_list_mtx);
                        if (error)
                                pm_dev_err(dev, state, "", error);
-               } else if (dev->power.status == DPM_SUSPENDING) {
-                       /* Allow new children of the device to be registered */
-                       dev->power.status = DPM_RESUMING;
+
+                       mutex_lock(&dpm_list_mtx);
                }
                if (!list_empty(&dev->power.entry))
-                       list_move_tail(&dev->power.entry, &list);
+                       list_move_tail(&dev->power.entry, &dpm_prepared_list);
                put_device(dev);
        }
-       list_splice(&list, &dpm_list);
        mutex_unlock(&dpm_list_mtx);
        async_synchronize_full();
        dpm_show_time(starttime, state, NULL);
@@ -697,22 +660,18 @@ static void dpm_complete(pm_message_t state)
 
        INIT_LIST_HEAD(&list);
        mutex_lock(&dpm_list_mtx);
-       transition_started = false;
-       while (!list_empty(&dpm_list)) {
-               struct device *dev = to_device(dpm_list.prev);
+       while (!list_empty(&dpm_prepared_list)) {
+               struct device *dev = to_device(dpm_prepared_list.prev);
 
                get_device(dev);
-               if (dev->power.status > DPM_ON) {
-                       dev->power.status = DPM_ON;
-                       mutex_unlock(&dpm_list_mtx);
+               dev->power.in_suspend = false;
+               list_move(&dev->power.entry, &list);
+               mutex_unlock(&dpm_list_mtx);
 
-                       device_complete(dev, state);
-                       pm_runtime_put_sync(dev);
+               device_complete(dev, state);
+               pm_runtime_put_sync(dev);
 
-                       mutex_lock(&dpm_list_mtx);
-               }
-               if (!list_empty(&dev->power.entry))
-                       list_move(&dev->power.entry, &list);
+               mutex_lock(&dpm_list_mtx);
                put_device(dev);
        }
        list_splice(&list, &dpm_list);
@@ -802,15 +761,13 @@ End:
  */
 int dpm_suspend_noirq(pm_message_t state)
 {
-       struct list_head list;
        ktime_t starttime = ktime_get();
        int error = 0;
 
-       INIT_LIST_HEAD(&list);
        suspend_device_irqs();
        mutex_lock(&dpm_list_mtx);
-       while (!list_empty(&dpm_list)) {
-               struct device *dev = to_device(dpm_list.prev);
+       while (!list_empty(&dpm_suspended_list)) {
+               struct device *dev = to_device(dpm_suspended_list.prev);
 
                get_device(dev);
                mutex_unlock(&dpm_list_mtx);
@@ -823,12 +780,10 @@ int dpm_suspend_noirq(pm_message_t state)
                        put_device(dev);
                        break;
                }
-               dev->power.status = DPM_OFF_IRQ;
                if (!list_empty(&dev->power.entry))
-                       list_move(&dev->power.entry, &list);
+                       list_move(&dev->power.entry, &dpm_noirq_list);
                put_device(dev);
        }
-       list_splice_tail(&list, &dpm_list);
        mutex_unlock(&dpm_list_mtx);
        if (error)
                dpm_resume_noirq(resume_event(state));
@@ -876,6 +831,11 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
        if (async_error)
                goto End;
 
+       if (pm_wakeup_pending()) {
+               async_error = -EBUSY;
+               goto End;
+       }
+
        if (dev->class) {
                if (dev->class->pm) {
                        pm_dev_dbg(dev, state, "class ");
@@ -907,9 +867,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
                }
        }
 
-       if (!error)
-               dev->power.status = DPM_OFF;
-
  End:
        device_unlock(dev);
        complete_all(&dev->power.completion);
@@ -951,16 +908,14 @@ static int device_suspend(struct device *dev)
  */
 static int dpm_suspend(pm_message_t state)
 {
-       struct list_head list;
        ktime_t starttime = ktime_get();
        int error = 0;
 
-       INIT_LIST_HEAD(&list);
        mutex_lock(&dpm_list_mtx);
        pm_transition = state;
        async_error = 0;
-       while (!list_empty(&dpm_list)) {
-               struct device *dev = to_device(dpm_list.prev);
+       while (!list_empty(&dpm_prepared_list)) {
+               struct device *dev = to_device(dpm_prepared_list.prev);
 
                get_device(dev);
                mutex_unlock(&dpm_list_mtx);
@@ -974,12 +929,11 @@ static int dpm_suspend(pm_message_t state)
                        break;
                }
                if (!list_empty(&dev->power.entry))
-                       list_move(&dev->power.entry, &list);
+                       list_move(&dev->power.entry, &dpm_suspended_list);
                put_device(dev);
                if (async_error)
                        break;
        }
-       list_splice(&list, dpm_list.prev);
        mutex_unlock(&dpm_list_mtx);
        async_synchronize_full();
        if (!error)
@@ -1038,22 +992,20 @@ static int device_prepare(struct device *dev, pm_message_t state)
  */
 static int dpm_prepare(pm_message_t state)
 {
-       struct list_head list;
        int error = 0;
 
-       INIT_LIST_HEAD(&list);
        mutex_lock(&dpm_list_mtx);
-       transition_started = true;
        while (!list_empty(&dpm_list)) {
                struct device *dev = to_device(dpm_list.next);
 
                get_device(dev);
-               dev->power.status = DPM_PREPARING;
                mutex_unlock(&dpm_list_mtx);
 
                pm_runtime_get_noresume(dev);
-               if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
-                       /* Wake-up requested during system sleep transition. */
+               if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
+                       pm_wakeup_event(dev, 0);
+
+               if (pm_wakeup_pending()) {
                        pm_runtime_put_sync(dev);
                        error = -EBUSY;
                } else {
@@ -1062,24 +1014,22 @@ static int dpm_prepare(pm_message_t state)
 
                mutex_lock(&dpm_list_mtx);
                if (error) {
-                       dev->power.status = DPM_ON;
                        if (error == -EAGAIN) {
                                put_device(dev);
                                error = 0;
                                continue;
                        }
-                       printk(KERN_ERR "PM: Failed to prepare device %s "
-                               "for power transition: error %d\n",
-                               kobject_name(&dev->kobj), error);
+                       printk(KERN_INFO "PM: Device %s not prepared "
+                               "for power transition: code %d\n",
+                               dev_name(dev), error);
                        put_device(dev);
                        break;
                }
-               dev->power.status = DPM_SUSPENDING;
+               dev->power.in_suspend = true;
                if (!list_empty(&dev->power.entry))
-                       list_move_tail(&dev->power.entry, &list);
+                       list_move_tail(&dev->power.entry, &dpm_prepared_list);
                put_device(dev);
        }
-       list_splice(&list, &dpm_list);
        mutex_unlock(&dpm_list_mtx);
        return error;
 }