aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/power/main.c115
-rw-r--r--include/linux/device.h6
-rw-r--r--include/linux/pm.h3
-rw-r--r--include/linux/resume-trace.h7
4 files changed, 125 insertions, 6 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 6ca5cdf63849..3b44c201ddad 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -25,6 +25,7 @@
25#include <linux/resume-trace.h> 25#include <linux/resume-trace.h>
26#include <linux/interrupt.h> 26#include <linux/interrupt.h>
27#include <linux/sched.h> 27#include <linux/sched.h>
28#include <linux/async.h>
28 29
29#include "../base.h" 30#include "../base.h"
30#include "power.h" 31#include "power.h"
@@ -42,6 +43,7 @@
42LIST_HEAD(dpm_list); 43LIST_HEAD(dpm_list);
43 44
44static DEFINE_MUTEX(dpm_list_mtx); 45static DEFINE_MUTEX(dpm_list_mtx);
46static pm_message_t pm_transition;
45 47
46/* 48/*
47 * Set once the preparation of devices for a PM transition has started, reset 49 * Set once the preparation of devices for a PM transition has started, reset
@@ -56,6 +58,7 @@ static bool transition_started;
56void device_pm_init(struct device *dev) 58void device_pm_init(struct device *dev)
57{ 59{
58 dev->power.status = DPM_ON; 60 dev->power.status = DPM_ON;
61 init_completion(&dev->power.completion);
59 pm_runtime_init(dev); 62 pm_runtime_init(dev);
60} 63}
61 64
@@ -111,6 +114,7 @@ void device_pm_remove(struct device *dev)
111 pr_debug("PM: Removing info for %s:%s\n", 114 pr_debug("PM: Removing info for %s:%s\n",
112 dev->bus ? dev->bus->name : "No Bus", 115 dev->bus ? dev->bus->name : "No Bus",
113 kobject_name(&dev->kobj)); 116 kobject_name(&dev->kobj));
117 complete_all(&dev->power.completion);
114 mutex_lock(&dpm_list_mtx); 118 mutex_lock(&dpm_list_mtx);
115 list_del_init(&dev->power.entry); 119 list_del_init(&dev->power.entry);
116 mutex_unlock(&dpm_list_mtx); 120 mutex_unlock(&dpm_list_mtx);
@@ -188,6 +192,31 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime,
188} 192}
189 193
190/** 194/**
195 * dpm_wait - Wait for a PM operation to complete.
196 * @dev: Device to wait for.
197 * @async: If unset, wait only if the device's power.async_suspend flag is set.
198 */
199static void dpm_wait(struct device *dev, bool async)
200{
201 if (!dev)
202 return;
203
204 if (async || dev->power.async_suspend)
205 wait_for_completion(&dev->power.completion);
206}
207
208static int dpm_wait_fn(struct device *dev, void *async_ptr)
209{
210 dpm_wait(dev, *((bool *)async_ptr));
211 return 0;
212}
213
214static void dpm_wait_for_children(struct device *dev, bool async)
215{
216 device_for_each_child(dev, &async, dpm_wait_fn);
217}
218
219/**
191 * pm_op - Execute the PM operation appropriate for given PM event. 220 * pm_op - Execute the PM operation appropriate for given PM event.
192 * @dev: Device to handle. 221 * @dev: Device to handle.
193 * @ops: PM operations to choose from. 222 * @ops: PM operations to choose from.
@@ -466,17 +495,19 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev))
466} 495}
467 496
468/** 497/**
469 * device_resume - Execute "resume" callbacks for given device. 498 * __device_resume - Execute "resume" callbacks for given device.
470 * @dev: Device to handle. 499 * @dev: Device to handle.
471 * @state: PM transition of the system being carried out. 500 * @state: PM transition of the system being carried out.
501 * @async: If true, the device is being resumed asynchronously.
472 */ 502 */
473static int device_resume(struct device *dev, pm_message_t state) 503static int __device_resume(struct device *dev, pm_message_t state, bool async)
474{ 504{
475 int error = 0; 505 int error = 0;
476 506
477 TRACE_DEVICE(dev); 507 TRACE_DEVICE(dev);
478 TRACE_RESUME(0); 508 TRACE_RESUME(0);
479 509
510 dpm_wait(dev->parent, async);
480 down(&dev->sem); 511 down(&dev->sem);
481 512
482 if (dev->bus) { 513 if (dev->bus) {
@@ -511,11 +542,36 @@ static int device_resume(struct device *dev, pm_message_t state)
511 } 542 }
512 End: 543 End:
513 up(&dev->sem); 544 up(&dev->sem);
545 complete_all(&dev->power.completion);
514 546
515 TRACE_RESUME(error); 547 TRACE_RESUME(error);
516 return error; 548 return error;
517} 549}
518 550
551static void async_resume(void *data, async_cookie_t cookie)
552{
553 struct device *dev = (struct device *)data;
554 int error;
555
556 error = __device_resume(dev, pm_transition, true);
557 if (error)
558 pm_dev_err(dev, pm_transition, " async", error);
559 put_device(dev);
560}
561
562static int device_resume(struct device *dev)
563{
564 INIT_COMPLETION(dev->power.completion);
565
566 if (dev->power.async_suspend && !pm_trace_is_enabled()) {
567 get_device(dev);
568 async_schedule(async_resume, dev);
569 return 0;
570 }
571
572 return __device_resume(dev, pm_transition, false);
573}
574
519/** 575/**
520 * dpm_resume - Execute "resume" callbacks for non-sysdev devices. 576 * dpm_resume - Execute "resume" callbacks for non-sysdev devices.
521 * @state: PM transition of the system being carried out. 577 * @state: PM transition of the system being carried out.
@@ -530,6 +586,7 @@ static void dpm_resume(pm_message_t state)
530 586
531 INIT_LIST_HEAD(&list); 587 INIT_LIST_HEAD(&list);
532 mutex_lock(&dpm_list_mtx); 588 mutex_lock(&dpm_list_mtx);
589 pm_transition = state;
533 while (!list_empty(&dpm_list)) { 590 while (!list_empty(&dpm_list)) {
534 struct device *dev = to_device(dpm_list.next); 591 struct device *dev = to_device(dpm_list.next);
535 592
@@ -540,7 +597,7 @@ static void dpm_resume(pm_message_t state)
540 dev->power.status = DPM_RESUMING; 597 dev->power.status = DPM_RESUMING;
541 mutex_unlock(&dpm_list_mtx); 598 mutex_unlock(&dpm_list_mtx);
542 599
543 error = device_resume(dev, state); 600 error = device_resume(dev);
544 601
545 mutex_lock(&dpm_list_mtx); 602 mutex_lock(&dpm_list_mtx);
546 if (error) 603 if (error)
@@ -555,6 +612,7 @@ static void dpm_resume(pm_message_t state)
555 } 612 }
556 list_splice(&list, &dpm_list); 613 list_splice(&list, &dpm_list);
557 mutex_unlock(&dpm_list_mtx); 614 mutex_unlock(&dpm_list_mtx);
615 async_synchronize_full();
558 dpm_show_time(starttime, state, NULL); 616 dpm_show_time(starttime, state, NULL);
559} 617}
560 618
@@ -732,17 +790,24 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
732 return error; 790 return error;
733} 791}
734 792
793static int async_error;
794
735/** 795/**
736 * device_suspend - Execute "suspend" callbacks for given device. 796 * device_suspend - Execute "suspend" callbacks for given device.
737 * @dev: Device to handle. 797 * @dev: Device to handle.
738 * @state: PM transition of the system being carried out. 798 * @state: PM transition of the system being carried out.
799 * @async: If true, the device is being suspended asynchronously.
739 */ 800 */
740static int device_suspend(struct device *dev, pm_message_t state) 801static int __device_suspend(struct device *dev, pm_message_t state, bool async)
741{ 802{
742 int error = 0; 803 int error = 0;
743 804
805 dpm_wait_for_children(dev, async);
744 down(&dev->sem); 806 down(&dev->sem);
745 807
808 if (async_error)
809 goto End;
810
746 if (dev->class) { 811 if (dev->class) {
747 if (dev->class->pm) { 812 if (dev->class->pm) {
748 pm_dev_dbg(dev, state, "class "); 813 pm_dev_dbg(dev, state, "class ");
@@ -773,12 +838,44 @@ static int device_suspend(struct device *dev, pm_message_t state)
773 error = legacy_suspend(dev, state, dev->bus->suspend); 838 error = legacy_suspend(dev, state, dev->bus->suspend);
774 } 839 }
775 } 840 }
841
842 if (!error)
843 dev->power.status = DPM_OFF;
844
776 End: 845 End:
777 up(&dev->sem); 846 up(&dev->sem);
847 complete_all(&dev->power.completion);
778 848
779 return error; 849 return error;
780} 850}
781 851
852static void async_suspend(void *data, async_cookie_t cookie)
853{
854 struct device *dev = (struct device *)data;
855 int error;
856
857 error = __device_suspend(dev, pm_transition, true);
858 if (error) {
859 pm_dev_err(dev, pm_transition, " async", error);
860 async_error = error;
861 }
862
863 put_device(dev);
864}
865
866static int device_suspend(struct device *dev)
867{
868 INIT_COMPLETION(dev->power.completion);
869
870 if (dev->power.async_suspend) {
871 get_device(dev);
872 async_schedule(async_suspend, dev);
873 return 0;
874 }
875
876 return __device_suspend(dev, pm_transition, false);
877}
878
782/** 879/**
783 * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. 880 * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
784 * @state: PM transition of the system being carried out. 881 * @state: PM transition of the system being carried out.
@@ -791,13 +888,15 @@ static int dpm_suspend(pm_message_t state)
791 888
792 INIT_LIST_HEAD(&list); 889 INIT_LIST_HEAD(&list);
793 mutex_lock(&dpm_list_mtx); 890 mutex_lock(&dpm_list_mtx);
891 pm_transition = state;
892 async_error = 0;
794 while (!list_empty(&dpm_list)) { 893 while (!list_empty(&dpm_list)) {
795 struct device *dev = to_device(dpm_list.prev); 894 struct device *dev = to_device(dpm_list.prev);
796 895
797 get_device(dev); 896 get_device(dev);
798 mutex_unlock(&dpm_list_mtx); 897 mutex_unlock(&dpm_list_mtx);
799 898
800 error = device_suspend(dev, state); 899 error = device_suspend(dev);
801 900
802 mutex_lock(&dpm_list_mtx); 901 mutex_lock(&dpm_list_mtx);
803 if (error) { 902 if (error) {
@@ -805,13 +904,17 @@ static int dpm_suspend(pm_message_t state)
805 put_device(dev); 904 put_device(dev);
806 break; 905 break;
807 } 906 }
808 dev->power.status = DPM_OFF;
809 if (!list_empty(&dev->power.entry)) 907 if (!list_empty(&dev->power.entry))
810 list_move(&dev->power.entry, &list); 908 list_move(&dev->power.entry, &list);
811 put_device(dev); 909 put_device(dev);
910 if (async_error)
911 break;
812 } 912 }
813 list_splice(&list, dpm_list.prev); 913 list_splice(&list, dpm_list.prev);
814 mutex_unlock(&dpm_list_mtx); 914 mutex_unlock(&dpm_list_mtx);
915 async_synchronize_full();
916 if (!error)
917 error = async_error;
815 if (!error) 918 if (!error)
816 dpm_show_time(starttime, state, NULL); 919 dpm_show_time(starttime, state, NULL);
817 return error; 920 return error;
diff --git a/include/linux/device.h b/include/linux/device.h
index a62799f2ab00..70adc5f3f50a 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -472,6 +472,12 @@ static inline int device_is_registered(struct device *dev)
472 return dev->kobj.state_in_sysfs; 472 return dev->kobj.state_in_sysfs;
473} 473}
474 474
475static inline void device_enable_async_suspend(struct device *dev)
476{
477 if (dev->power.status == DPM_ON)
478 dev->power.async_suspend = true;
479}
480
475void driver_init(void); 481void driver_init(void);
476 482
477/* 483/*
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 25b1eca8049d..9c16cd20fc96 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -26,6 +26,7 @@
26#include <linux/spinlock.h> 26#include <linux/spinlock.h>
27#include <linux/wait.h> 27#include <linux/wait.h>
28#include <linux/timer.h> 28#include <linux/timer.h>
29#include <linux/completion.h>
29 30
30/* 31/*
31 * Callbacks for platform drivers to implement. 32 * Callbacks for platform drivers to implement.
@@ -412,9 +413,11 @@ struct dev_pm_info {
412 pm_message_t power_state; 413 pm_message_t power_state;
413 unsigned int can_wakeup:1; 414 unsigned int can_wakeup:1;
414 unsigned int should_wakeup:1; 415 unsigned int should_wakeup:1;
416 unsigned async_suspend:1;
415 enum dpm_state status; /* Owned by the PM core */ 417 enum dpm_state status; /* Owned by the PM core */
416#ifdef CONFIG_PM_SLEEP 418#ifdef CONFIG_PM_SLEEP
417 struct list_head entry; 419 struct list_head entry;
420 struct completion completion;
418#endif 421#endif
419#ifdef CONFIG_PM_RUNTIME 422#ifdef CONFIG_PM_RUNTIME
420 struct timer_list suspend_timer; 423 struct timer_list suspend_timer;
diff --git a/include/linux/resume-trace.h b/include/linux/resume-trace.h
index c9ba2fdf807d..bc8c3881c729 100644
--- a/include/linux/resume-trace.h
+++ b/include/linux/resume-trace.h
@@ -6,6 +6,11 @@
6 6
7extern int pm_trace_enabled; 7extern int pm_trace_enabled;
8 8
9static inline int pm_trace_is_enabled(void)
10{
11 return pm_trace_enabled;
12}
13
9struct device; 14struct device;
10extern void set_trace_device(struct device *); 15extern void set_trace_device(struct device *);
11extern void generate_resume_trace(const void *tracedata, unsigned int user); 16extern void generate_resume_trace(const void *tracedata, unsigned int user);
@@ -17,6 +22,8 @@ extern void generate_resume_trace(const void *tracedata, unsigned int user);
17 22
18#else 23#else
19 24
25static inline int pm_trace_is_enabled(void) { return 0; }
26
20#define TRACE_DEVICE(dev) do { } while (0) 27#define TRACE_DEVICE(dev) do { } while (0)
21#define TRACE_RESUME(dev) do { } while (0) 28#define TRACE_RESUME(dev) do { } while (0)
22 29