diff options
-rw-r--r-- | drivers/acpi/battery.c | 2 | ||||
-rw-r--r-- | drivers/acpi/button.c | 5 | ||||
-rw-r--r-- | drivers/acpi/device_pm.c | 9 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 2 | ||||
-rw-r--r-- | drivers/acpi/sleep.c | 37 | ||||
-rw-r--r-- | drivers/base/power/main.c | 5 | ||||
-rw-r--r-- | drivers/base/power/wakeup.c | 18 | ||||
-rw-r--r-- | include/acpi/acpi_bus.h | 6 | ||||
-rw-r--r-- | include/linux/suspend.h | 7 | ||||
-rw-r--r-- | kernel/power/process.c | 2 | ||||
-rw-r--r-- | kernel/power/suspend.c | 35 |
11 files changed, 103 insertions, 25 deletions
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index d42eeef9d928..1cbb88d938e5 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c | |||
@@ -782,7 +782,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume) | |||
782 | if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) || | 782 | if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) || |
783 | (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && | 783 | (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && |
784 | (battery->capacity_now <= battery->alarm))) | 784 | (battery->capacity_now <= battery->alarm))) |
785 | pm_wakeup_event(&battery->device->dev, 0); | 785 | acpi_pm_wakeup_event(&battery->device->dev); |
786 | 786 | ||
787 | return result; | 787 | return result; |
788 | } | 788 | } |
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index e19f530f1083..91cfdf377df7 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c | |||
@@ -217,7 +217,7 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state) | |||
217 | } | 217 | } |
218 | 218 | ||
219 | if (state) | 219 | if (state) |
220 | pm_wakeup_event(&device->dev, 0); | 220 | acpi_pm_wakeup_event(&device->dev); |
221 | 221 | ||
222 | ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); | 222 | ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); |
223 | if (ret == NOTIFY_DONE) | 223 | if (ret == NOTIFY_DONE) |
@@ -402,7 +402,7 @@ static void acpi_button_notify(struct acpi_device *device, u32 event) | |||
402 | } else { | 402 | } else { |
403 | int keycode; | 403 | int keycode; |
404 | 404 | ||
405 | pm_wakeup_event(&device->dev, 0); | 405 | acpi_pm_wakeup_event(&device->dev); |
406 | if (button->suspended) | 406 | if (button->suspended) |
407 | break; | 407 | break; |
408 | 408 | ||
@@ -534,6 +534,7 @@ static int acpi_button_add(struct acpi_device *device) | |||
534 | lid_device = device; | 534 | lid_device = device; |
535 | } | 535 | } |
536 | 536 | ||
537 | device_init_wakeup(&device->dev, true); | ||
537 | printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device)); | 538 | printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device)); |
538 | return 0; | 539 | return 0; |
539 | 540 | ||
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index f13c62c4b117..ca0210213773 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/pm_qos.h> | 24 | #include <linux/pm_qos.h> |
25 | #include <linux/pm_domain.h> | 25 | #include <linux/pm_domain.h> |
26 | #include <linux/pm_runtime.h> | 26 | #include <linux/pm_runtime.h> |
27 | #include <linux/suspend.h> | ||
27 | 28 | ||
28 | #include "internal.h" | 29 | #include "internal.h" |
29 | 30 | ||
@@ -385,6 +386,12 @@ EXPORT_SYMBOL(acpi_bus_power_manageable); | |||
385 | #ifdef CONFIG_PM | 386 | #ifdef CONFIG_PM |
386 | static DEFINE_MUTEX(acpi_pm_notifier_lock); | 387 | static DEFINE_MUTEX(acpi_pm_notifier_lock); |
387 | 388 | ||
389 | void acpi_pm_wakeup_event(struct device *dev) | ||
390 | { | ||
391 | pm_wakeup_dev_event(dev, 0, acpi_s2idle_wakeup()); | ||
392 | } | ||
393 | EXPORT_SYMBOL_GPL(acpi_pm_wakeup_event); | ||
394 | |||
388 | static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used) | 395 | static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used) |
389 | { | 396 | { |
390 | struct acpi_device *adev; | 397 | struct acpi_device *adev; |
@@ -399,7 +406,7 @@ static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used) | |||
399 | mutex_lock(&acpi_pm_notifier_lock); | 406 | mutex_lock(&acpi_pm_notifier_lock); |
400 | 407 | ||
401 | if (adev->wakeup.flags.notifier_present) { | 408 | if (adev->wakeup.flags.notifier_present) { |
402 | __pm_wakeup_event(adev->wakeup.ws, 0); | 409 | pm_wakeup_ws_event(adev->wakeup.ws, 0, acpi_s2idle_wakeup()); |
403 | if (adev->wakeup.context.func) | 410 | if (adev->wakeup.context.func) |
404 | adev->wakeup.context.func(&adev->wakeup.context); | 411 | adev->wakeup.context.func(&adev->wakeup.context); |
405 | } | 412 | } |
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 66229ffa909b..75924ea69071 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
@@ -198,8 +198,10 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); | |||
198 | Suspend/Resume | 198 | Suspend/Resume |
199 | -------------------------------------------------------------------------- */ | 199 | -------------------------------------------------------------------------- */ |
200 | #ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT | 200 | #ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT |
201 | extern bool acpi_s2idle_wakeup(void); | ||
201 | extern int acpi_sleep_init(void); | 202 | extern int acpi_sleep_init(void); |
202 | #else | 203 | #else |
204 | static inline bool acpi_s2idle_wakeup(void) { return false; } | ||
203 | static inline int acpi_sleep_init(void) { return -ENXIO; } | 205 | static inline int acpi_sleep_init(void) { return -ENXIO; } |
204 | #endif | 206 | #endif |
205 | 207 | ||
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index a4782c75ebdd..555de11a56b6 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c | |||
@@ -650,6 +650,8 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = { | |||
650 | .recover = acpi_pm_finish, | 650 | .recover = acpi_pm_finish, |
651 | }; | 651 | }; |
652 | 652 | ||
653 | static bool s2idle_wakeup; | ||
654 | |||
653 | static int acpi_freeze_begin(void) | 655 | static int acpi_freeze_begin(void) |
654 | { | 656 | { |
655 | acpi_scan_lock_acquire(); | 657 | acpi_scan_lock_acquire(); |
@@ -666,6 +668,33 @@ static int acpi_freeze_prepare(void) | |||
666 | return 0; | 668 | return 0; |
667 | } | 669 | } |
668 | 670 | ||
671 | static void acpi_freeze_wake(void) | ||
672 | { | ||
673 | /* | ||
674 | * If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means | ||
675 | * that the SCI has triggered while suspended, so cancel the wakeup in | ||
676 | * case it has not been a wakeup event (the GPEs will be checked later). | ||
677 | */ | ||
678 | if (acpi_sci_irq_valid() && | ||
679 | !irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) { | ||
680 | pm_system_cancel_wakeup(); | ||
681 | s2idle_wakeup = true; | ||
682 | } | ||
683 | } | ||
684 | |||
685 | static void acpi_freeze_sync(void) | ||
686 | { | ||
687 | /* | ||
688 | * Process all pending events in case there are any wakeup ones. | ||
689 | * | ||
690 | * The EC driver uses the system workqueue, so that one needs to be | ||
691 | * flushed too. | ||
692 | */ | ||
693 | acpi_os_wait_events_complete(); | ||
694 | flush_scheduled_work(); | ||
695 | s2idle_wakeup = false; | ||
696 | } | ||
697 | |||
669 | static void acpi_freeze_restore(void) | 698 | static void acpi_freeze_restore(void) |
670 | { | 699 | { |
671 | if (acpi_sci_irq_valid()) | 700 | if (acpi_sci_irq_valid()) |
@@ -682,6 +711,8 @@ static void acpi_freeze_end(void) | |||
682 | static const struct platform_freeze_ops acpi_freeze_ops = { | 711 | static const struct platform_freeze_ops acpi_freeze_ops = { |
683 | .begin = acpi_freeze_begin, | 712 | .begin = acpi_freeze_begin, |
684 | .prepare = acpi_freeze_prepare, | 713 | .prepare = acpi_freeze_prepare, |
714 | .wake = acpi_freeze_wake, | ||
715 | .sync = acpi_freeze_sync, | ||
685 | .restore = acpi_freeze_restore, | 716 | .restore = acpi_freeze_restore, |
686 | .end = acpi_freeze_end, | 717 | .end = acpi_freeze_end, |
687 | }; | 718 | }; |
@@ -700,9 +731,15 @@ static void acpi_sleep_suspend_setup(void) | |||
700 | } | 731 | } |
701 | 732 | ||
702 | #else /* !CONFIG_SUSPEND */ | 733 | #else /* !CONFIG_SUSPEND */ |
734 | #define s2idle_wakeup (false) | ||
703 | static inline void acpi_sleep_suspend_setup(void) {} | 735 | static inline void acpi_sleep_suspend_setup(void) {} |
704 | #endif /* !CONFIG_SUSPEND */ | 736 | #endif /* !CONFIG_SUSPEND */ |
705 | 737 | ||
738 | bool acpi_s2idle_wakeup(void) | ||
739 | { | ||
740 | return s2idle_wakeup; | ||
741 | } | ||
742 | |||
706 | #ifdef CONFIG_PM_SLEEP | 743 | #ifdef CONFIG_PM_SLEEP |
707 | static u32 saved_bm_rld; | 744 | static u32 saved_bm_rld; |
708 | 745 | ||
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 253f860e8981..ef5b6a6e5045 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -1095,11 +1095,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a | |||
1095 | if (async_error) | 1095 | if (async_error) |
1096 | goto Complete; | 1096 | goto Complete; |
1097 | 1097 | ||
1098 | if (pm_wakeup_pending()) { | ||
1099 | async_error = -EBUSY; | ||
1100 | goto Complete; | ||
1101 | } | ||
1102 | |||
1103 | if (dev->power.syscore || dev->power.direct_complete) | 1098 | if (dev->power.syscore || dev->power.direct_complete) |
1104 | goto Complete; | 1099 | goto Complete; |
1105 | 1100 | ||
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index c313b600d356..9c36b27996fc 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c | |||
@@ -28,8 +28,8 @@ bool events_check_enabled __read_mostly; | |||
28 | /* First wakeup IRQ seen by the kernel in the last cycle. */ | 28 | /* First wakeup IRQ seen by the kernel in the last cycle. */ |
29 | unsigned int pm_wakeup_irq __read_mostly; | 29 | unsigned int pm_wakeup_irq __read_mostly; |
30 | 30 | ||
31 | /* If set and the system is suspending, terminate the suspend. */ | 31 | /* If greater than 0 and the system is suspending, terminate the suspend. */ |
32 | static bool pm_abort_suspend __read_mostly; | 32 | static atomic_t pm_abort_suspend __read_mostly; |
33 | 33 | ||
34 | /* | 34 | /* |
35 | * Combined counters of registered wakeup events and wakeup events in progress. | 35 | * Combined counters of registered wakeup events and wakeup events in progress. |
@@ -855,20 +855,26 @@ bool pm_wakeup_pending(void) | |||
855 | pm_print_active_wakeup_sources(); | 855 | pm_print_active_wakeup_sources(); |
856 | } | 856 | } |
857 | 857 | ||
858 | return ret || pm_abort_suspend; | 858 | return ret || atomic_read(&pm_abort_suspend) > 0; |
859 | } | 859 | } |
860 | 860 | ||
861 | void pm_system_wakeup(void) | 861 | void pm_system_wakeup(void) |
862 | { | 862 | { |
863 | pm_abort_suspend = true; | 863 | atomic_inc(&pm_abort_suspend); |
864 | freeze_wake(); | 864 | freeze_wake(); |
865 | } | 865 | } |
866 | EXPORT_SYMBOL_GPL(pm_system_wakeup); | 866 | EXPORT_SYMBOL_GPL(pm_system_wakeup); |
867 | 867 | ||
868 | void pm_wakeup_clear(void) | 868 | void pm_system_cancel_wakeup(void) |
869 | { | ||
870 | atomic_dec(&pm_abort_suspend); | ||
871 | } | ||
872 | |||
873 | void pm_wakeup_clear(bool reset) | ||
869 | { | 874 | { |
870 | pm_abort_suspend = false; | ||
871 | pm_wakeup_irq = 0; | 875 | pm_wakeup_irq = 0; |
876 | if (reset) | ||
877 | atomic_set(&pm_abort_suspend, 0); | ||
872 | } | 878 | } |
873 | 879 | ||
874 | void pm_system_irq_wakeup(unsigned int irq_number) | 880 | void pm_system_irq_wakeup(unsigned int irq_number) |
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 79c0af419300..63a90a624a0f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h | |||
@@ -598,15 +598,19 @@ static inline bool acpi_device_always_present(struct acpi_device *adev) | |||
598 | #endif | 598 | #endif |
599 | 599 | ||
600 | #ifdef CONFIG_PM | 600 | #ifdef CONFIG_PM |
601 | void acpi_pm_wakeup_event(struct device *dev); | ||
601 | acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, | 602 | acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, |
602 | void (*func)(struct acpi_device_wakeup_context *context)); | 603 | void (*func)(struct acpi_device_wakeup_context *context)); |
603 | acpi_status acpi_remove_pm_notifier(struct acpi_device *adev); | 604 | acpi_status acpi_remove_pm_notifier(struct acpi_device *adev); |
604 | int acpi_pm_device_sleep_state(struct device *, int *, int); | 605 | int acpi_pm_device_sleep_state(struct device *, int *, int); |
605 | int acpi_pm_device_run_wake(struct device *, bool); | 606 | int acpi_pm_device_run_wake(struct device *, bool); |
606 | #else | 607 | #else |
608 | static inline void acpi_pm_wakeup_event(struct device *dev) | ||
609 | { | ||
610 | } | ||
607 | static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev, | 611 | static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev, |
608 | struct device *dev, | 612 | struct device *dev, |
609 | void (*work_func)(struct work_struct *work)) | 613 | void (*func)(struct acpi_device_wakeup_context *context)) |
610 | { | 614 | { |
611 | return AE_SUPPORT; | 615 | return AE_SUPPORT; |
612 | } | 616 | } |
diff --git a/include/linux/suspend.h b/include/linux/suspend.h index d9718378a8be..0b1cf32edfd7 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h | |||
@@ -189,6 +189,8 @@ struct platform_suspend_ops { | |||
189 | struct platform_freeze_ops { | 189 | struct platform_freeze_ops { |
190 | int (*begin)(void); | 190 | int (*begin)(void); |
191 | int (*prepare)(void); | 191 | int (*prepare)(void); |
192 | void (*wake)(void); | ||
193 | void (*sync)(void); | ||
192 | void (*restore)(void); | 194 | void (*restore)(void); |
193 | void (*end)(void); | 195 | void (*end)(void); |
194 | }; | 196 | }; |
@@ -428,7 +430,8 @@ extern unsigned int pm_wakeup_irq; | |||
428 | 430 | ||
429 | extern bool pm_wakeup_pending(void); | 431 | extern bool pm_wakeup_pending(void); |
430 | extern void pm_system_wakeup(void); | 432 | extern void pm_system_wakeup(void); |
431 | extern void pm_wakeup_clear(void); | 433 | extern void pm_system_cancel_wakeup(void); |
434 | extern void pm_wakeup_clear(bool reset); | ||
432 | extern void pm_system_irq_wakeup(unsigned int irq_number); | 435 | extern void pm_system_irq_wakeup(unsigned int irq_number); |
433 | extern bool pm_get_wakeup_count(unsigned int *count, bool block); | 436 | extern bool pm_get_wakeup_count(unsigned int *count, bool block); |
434 | extern bool pm_save_wakeup_count(unsigned int count); | 437 | extern bool pm_save_wakeup_count(unsigned int count); |
@@ -478,7 +481,7 @@ static inline int unregister_pm_notifier(struct notifier_block *nb) | |||
478 | 481 | ||
479 | static inline bool pm_wakeup_pending(void) { return false; } | 482 | static inline bool pm_wakeup_pending(void) { return false; } |
480 | static inline void pm_system_wakeup(void) {} | 483 | static inline void pm_system_wakeup(void) {} |
481 | static inline void pm_wakeup_clear(void) {} | 484 | static inline void pm_wakeup_clear(bool reset) {} |
482 | static inline void pm_system_irq_wakeup(unsigned int irq_number) {} | 485 | static inline void pm_system_irq_wakeup(unsigned int irq_number) {} |
483 | 486 | ||
484 | static inline void lock_system_sleep(void) {} | 487 | static inline void lock_system_sleep(void) {} |
diff --git a/kernel/power/process.c b/kernel/power/process.c index c7209f060eeb..78672d324a6e 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c | |||
@@ -132,7 +132,7 @@ int freeze_processes(void) | |||
132 | if (!pm_freezing) | 132 | if (!pm_freezing) |
133 | atomic_inc(&system_freezing_cnt); | 133 | atomic_inc(&system_freezing_cnt); |
134 | 134 | ||
135 | pm_wakeup_clear(); | 135 | pm_wakeup_clear(true); |
136 | pr_info("Freezing user space processes ... "); | 136 | pr_info("Freezing user space processes ... "); |
137 | pm_freezing = true; | 137 | pm_freezing = true; |
138 | error = try_to_freeze_tasks(true); | 138 | error = try_to_freeze_tasks(true); |
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 15e6baef5c73..3ecf275d7e44 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c | |||
@@ -72,6 +72,8 @@ static void freeze_begin(void) | |||
72 | 72 | ||
73 | static void freeze_enter(void) | 73 | static void freeze_enter(void) |
74 | { | 74 | { |
75 | trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, true); | ||
76 | |||
75 | spin_lock_irq(&suspend_freeze_lock); | 77 | spin_lock_irq(&suspend_freeze_lock); |
76 | if (pm_wakeup_pending()) | 78 | if (pm_wakeup_pending()) |
77 | goto out; | 79 | goto out; |
@@ -84,11 +86,9 @@ static void freeze_enter(void) | |||
84 | 86 | ||
85 | /* Push all the CPUs into the idle loop. */ | 87 | /* Push all the CPUs into the idle loop. */ |
86 | wake_up_all_idle_cpus(); | 88 | wake_up_all_idle_cpus(); |
87 | pr_debug("PM: suspend-to-idle\n"); | ||
88 | /* Make the current CPU wait so it can enter the idle loop too. */ | 89 | /* Make the current CPU wait so it can enter the idle loop too. */ |
89 | wait_event(suspend_freeze_wait_head, | 90 | wait_event(suspend_freeze_wait_head, |
90 | suspend_freeze_state == FREEZE_STATE_WAKE); | 91 | suspend_freeze_state == FREEZE_STATE_WAKE); |
91 | pr_debug("PM: resume from suspend-to-idle\n"); | ||
92 | 92 | ||
93 | cpuidle_pause(); | 93 | cpuidle_pause(); |
94 | put_online_cpus(); | 94 | put_online_cpus(); |
@@ -98,6 +98,31 @@ static void freeze_enter(void) | |||
98 | out: | 98 | out: |
99 | suspend_freeze_state = FREEZE_STATE_NONE; | 99 | suspend_freeze_state = FREEZE_STATE_NONE; |
100 | spin_unlock_irq(&suspend_freeze_lock); | 100 | spin_unlock_irq(&suspend_freeze_lock); |
101 | |||
102 | trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, false); | ||
103 | } | ||
104 | |||
105 | static void s2idle_loop(void) | ||
106 | { | ||
107 | pr_debug("PM: suspend-to-idle\n"); | ||
108 | |||
109 | do { | ||
110 | freeze_enter(); | ||
111 | |||
112 | if (freeze_ops && freeze_ops->wake) | ||
113 | freeze_ops->wake(); | ||
114 | |||
115 | dpm_resume_noirq(PMSG_RESUME); | ||
116 | if (freeze_ops && freeze_ops->sync) | ||
117 | freeze_ops->sync(); | ||
118 | |||
119 | if (pm_wakeup_pending()) | ||
120 | break; | ||
121 | |||
122 | pm_wakeup_clear(false); | ||
123 | } while (!dpm_suspend_noirq(PMSG_SUSPEND)); | ||
124 | |||
125 | pr_debug("PM: resume from suspend-to-idle\n"); | ||
101 | } | 126 | } |
102 | 127 | ||
103 | void freeze_wake(void) | 128 | void freeze_wake(void) |
@@ -371,10 +396,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) | |||
371 | * all the devices are suspended. | 396 | * all the devices are suspended. |
372 | */ | 397 | */ |
373 | if (state == PM_SUSPEND_FREEZE) { | 398 | if (state == PM_SUSPEND_FREEZE) { |
374 | trace_suspend_resume(TPS("machine_suspend"), state, true); | 399 | s2idle_loop(); |
375 | freeze_enter(); | 400 | goto Platform_early_resume; |
376 | trace_suspend_resume(TPS("machine_suspend"), state, false); | ||
377 | goto Platform_wake; | ||
378 | } | 401 | } |
379 | 402 | ||
380 | error = disable_nonboot_cpus(); | 403 | error = disable_nonboot_cpus(); |