diff options
Diffstat (limited to 'drivers/base/power/main.c')
-rw-r--r-- | drivers/base/power/main.c | 66 |
1 files changed, 49 insertions, 17 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index b0b072a88f5f..a3c1404c7933 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -57,20 +57,17 @@ static pm_message_t pm_transition; | |||
57 | static int async_error; | 57 | static int async_error; |
58 | 58 | ||
59 | /** | 59 | /** |
60 | * device_pm_init - Initialize the PM-related part of a device object. | 60 | * device_pm_sleep_init - Initialize system suspend-related device fields. |
61 | * @dev: Device object being initialized. | 61 | * @dev: Device object being initialized. |
62 | */ | 62 | */ |
63 | void device_pm_init(struct device *dev) | 63 | void device_pm_sleep_init(struct device *dev) |
64 | { | 64 | { |
65 | dev->power.is_prepared = false; | 65 | dev->power.is_prepared = false; |
66 | dev->power.is_suspended = false; | 66 | dev->power.is_suspended = false; |
67 | init_completion(&dev->power.completion); | 67 | init_completion(&dev->power.completion); |
68 | complete_all(&dev->power.completion); | 68 | complete_all(&dev->power.completion); |
69 | dev->power.wakeup = NULL; | 69 | dev->power.wakeup = NULL; |
70 | spin_lock_init(&dev->power.lock); | ||
71 | pm_runtime_init(dev); | ||
72 | INIT_LIST_HEAD(&dev->power.entry); | 70 | INIT_LIST_HEAD(&dev->power.entry); |
73 | dev->power.power_state = PMSG_INVALID; | ||
74 | } | 71 | } |
75 | 72 | ||
76 | /** | 73 | /** |
@@ -408,6 +405,9 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) | |||
408 | TRACE_DEVICE(dev); | 405 | TRACE_DEVICE(dev); |
409 | TRACE_RESUME(0); | 406 | TRACE_RESUME(0); |
410 | 407 | ||
408 | if (dev->power.syscore) | ||
409 | goto Out; | ||
410 | |||
411 | if (dev->pm_domain) { | 411 | if (dev->pm_domain) { |
412 | info = "noirq power domain "; | 412 | info = "noirq power domain "; |
413 | callback = pm_noirq_op(&dev->pm_domain->ops, state); | 413 | callback = pm_noirq_op(&dev->pm_domain->ops, state); |
@@ -429,6 +429,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) | |||
429 | 429 | ||
430 | error = dpm_run_callback(callback, dev, state, info); | 430 | error = dpm_run_callback(callback, dev, state, info); |
431 | 431 | ||
432 | Out: | ||
432 | TRACE_RESUME(error); | 433 | TRACE_RESUME(error); |
433 | return error; | 434 | return error; |
434 | } | 435 | } |
@@ -486,6 +487,9 @@ static int device_resume_early(struct device *dev, pm_message_t state) | |||
486 | TRACE_DEVICE(dev); | 487 | TRACE_DEVICE(dev); |
487 | TRACE_RESUME(0); | 488 | TRACE_RESUME(0); |
488 | 489 | ||
490 | if (dev->power.syscore) | ||
491 | goto Out; | ||
492 | |||
489 | if (dev->pm_domain) { | 493 | if (dev->pm_domain) { |
490 | info = "early power domain "; | 494 | info = "early power domain "; |
491 | callback = pm_late_early_op(&dev->pm_domain->ops, state); | 495 | callback = pm_late_early_op(&dev->pm_domain->ops, state); |
@@ -507,6 +511,7 @@ static int device_resume_early(struct device *dev, pm_message_t state) | |||
507 | 511 | ||
508 | error = dpm_run_callback(callback, dev, state, info); | 512 | error = dpm_run_callback(callback, dev, state, info); |
509 | 513 | ||
514 | Out: | ||
510 | TRACE_RESUME(error); | 515 | TRACE_RESUME(error); |
511 | return error; | 516 | return error; |
512 | } | 517 | } |
@@ -565,11 +570,13 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) | |||
565 | pm_callback_t callback = NULL; | 570 | pm_callback_t callback = NULL; |
566 | char *info = NULL; | 571 | char *info = NULL; |
567 | int error = 0; | 572 | int error = 0; |
568 | bool put = false; | ||
569 | 573 | ||
570 | TRACE_DEVICE(dev); | 574 | TRACE_DEVICE(dev); |
571 | TRACE_RESUME(0); | 575 | TRACE_RESUME(0); |
572 | 576 | ||
577 | if (dev->power.syscore) | ||
578 | goto Complete; | ||
579 | |||
573 | dpm_wait(dev->parent, async); | 580 | dpm_wait(dev->parent, async); |
574 | device_lock(dev); | 581 | device_lock(dev); |
575 | 582 | ||
@@ -583,7 +590,6 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) | |||
583 | goto Unlock; | 590 | goto Unlock; |
584 | 591 | ||
585 | pm_runtime_enable(dev); | 592 | pm_runtime_enable(dev); |
586 | put = true; | ||
587 | 593 | ||
588 | if (dev->pm_domain) { | 594 | if (dev->pm_domain) { |
589 | info = "power domain "; | 595 | info = "power domain "; |
@@ -632,13 +638,12 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) | |||
632 | 638 | ||
633 | Unlock: | 639 | Unlock: |
634 | device_unlock(dev); | 640 | device_unlock(dev); |
641 | |||
642 | Complete: | ||
635 | complete_all(&dev->power.completion); | 643 | complete_all(&dev->power.completion); |
636 | 644 | ||
637 | TRACE_RESUME(error); | 645 | TRACE_RESUME(error); |
638 | 646 | ||
639 | if (put) | ||
640 | pm_runtime_put_sync(dev); | ||
641 | |||
642 | return error; | 647 | return error; |
643 | } | 648 | } |
644 | 649 | ||
@@ -722,6 +727,9 @@ static void device_complete(struct device *dev, pm_message_t state) | |||
722 | void (*callback)(struct device *) = NULL; | 727 | void (*callback)(struct device *) = NULL; |
723 | char *info = NULL; | 728 | char *info = NULL; |
724 | 729 | ||
730 | if (dev->power.syscore) | ||
731 | return; | ||
732 | |||
725 | device_lock(dev); | 733 | device_lock(dev); |
726 | 734 | ||
727 | if (dev->pm_domain) { | 735 | if (dev->pm_domain) { |
@@ -749,6 +757,8 @@ static void device_complete(struct device *dev, pm_message_t state) | |||
749 | } | 757 | } |
750 | 758 | ||
751 | device_unlock(dev); | 759 | device_unlock(dev); |
760 | |||
761 | pm_runtime_put_sync(dev); | ||
752 | } | 762 | } |
753 | 763 | ||
754 | /** | 764 | /** |
@@ -834,6 +844,9 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) | |||
834 | pm_callback_t callback = NULL; | 844 | pm_callback_t callback = NULL; |
835 | char *info = NULL; | 845 | char *info = NULL; |
836 | 846 | ||
847 | if (dev->power.syscore) | ||
848 | return 0; | ||
849 | |||
837 | if (dev->pm_domain) { | 850 | if (dev->pm_domain) { |
838 | info = "noirq power domain "; | 851 | info = "noirq power domain "; |
839 | callback = pm_noirq_op(&dev->pm_domain->ops, state); | 852 | callback = pm_noirq_op(&dev->pm_domain->ops, state); |
@@ -917,6 +930,9 @@ static int device_suspend_late(struct device *dev, pm_message_t state) | |||
917 | pm_callback_t callback = NULL; | 930 | pm_callback_t callback = NULL; |
918 | char *info = NULL; | 931 | char *info = NULL; |
919 | 932 | ||
933 | if (dev->power.syscore) | ||
934 | return 0; | ||
935 | |||
920 | if (dev->pm_domain) { | 936 | if (dev->pm_domain) { |
921 | info = "late power domain "; | 937 | info = "late power domain "; |
922 | callback = pm_late_early_op(&dev->pm_domain->ops, state); | 938 | callback = pm_late_early_op(&dev->pm_domain->ops, state); |
@@ -996,7 +1012,7 @@ int dpm_suspend_end(pm_message_t state) | |||
996 | 1012 | ||
997 | error = dpm_suspend_noirq(state); | 1013 | error = dpm_suspend_noirq(state); |
998 | if (error) { | 1014 | if (error) { |
999 | dpm_resume_early(state); | 1015 | dpm_resume_early(resume_event(state)); |
1000 | return error; | 1016 | return error; |
1001 | } | 1017 | } |
1002 | 1018 | ||
@@ -1043,16 +1059,23 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) | |||
1043 | if (async_error) | 1059 | if (async_error) |
1044 | goto Complete; | 1060 | goto Complete; |
1045 | 1061 | ||
1046 | pm_runtime_get_noresume(dev); | 1062 | /* |
1063 | * If a device configured to wake up the system from sleep states | ||
1064 | * has been suspended at run time and there's a resume request pending | ||
1065 | * for it, this is equivalent to the device signaling wakeup, so the | ||
1066 | * system suspend operation should be aborted. | ||
1067 | */ | ||
1047 | if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) | 1068 | if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) |
1048 | pm_wakeup_event(dev, 0); | 1069 | pm_wakeup_event(dev, 0); |
1049 | 1070 | ||
1050 | if (pm_wakeup_pending()) { | 1071 | if (pm_wakeup_pending()) { |
1051 | pm_runtime_put_sync(dev); | ||
1052 | async_error = -EBUSY; | 1072 | async_error = -EBUSY; |
1053 | goto Complete; | 1073 | goto Complete; |
1054 | } | 1074 | } |
1055 | 1075 | ||
1076 | if (dev->power.syscore) | ||
1077 | goto Complete; | ||
1078 | |||
1056 | device_lock(dev); | 1079 | device_lock(dev); |
1057 | 1080 | ||
1058 | if (dev->pm_domain) { | 1081 | if (dev->pm_domain) { |
@@ -1111,12 +1134,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) | |||
1111 | Complete: | 1134 | Complete: |
1112 | complete_all(&dev->power.completion); | 1135 | complete_all(&dev->power.completion); |
1113 | 1136 | ||
1114 | if (error) { | 1137 | if (error) |
1115 | pm_runtime_put_sync(dev); | ||
1116 | async_error = error; | 1138 | async_error = error; |
1117 | } else if (dev->power.is_suspended) { | 1139 | else if (dev->power.is_suspended) |
1118 | __pm_runtime_disable(dev, false); | 1140 | __pm_runtime_disable(dev, false); |
1119 | } | ||
1120 | 1141 | ||
1121 | return error; | 1142 | return error; |
1122 | } | 1143 | } |
@@ -1209,6 +1230,17 @@ static int device_prepare(struct device *dev, pm_message_t state) | |||
1209 | char *info = NULL; | 1230 | char *info = NULL; |
1210 | int error = 0; | 1231 | int error = 0; |
1211 | 1232 | ||
1233 | if (dev->power.syscore) | ||
1234 | return 0; | ||
1235 | |||
1236 | /* | ||
1237 | * If a device's parent goes into runtime suspend at the wrong time, | ||
1238 | * it won't be possible to resume the device. To prevent this we | ||
1239 | * block runtime suspend here, during the prepare phase, and allow | ||
1240 | * it again during the complete phase. | ||
1241 | */ | ||
1242 | pm_runtime_get_noresume(dev); | ||
1243 | |||
1212 | device_lock(dev); | 1244 | device_lock(dev); |
1213 | 1245 | ||
1214 | dev->power.wakeup_path = device_may_wakeup(dev); | 1246 | dev->power.wakeup_path = device_may_wakeup(dev); |