diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2019-09-17 03:35:35 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2019-09-17 03:35:35 -0400 |
commit | 1b531e55c52bbe2927e2c3490d47c82113d303e7 (patch) | |
tree | 9e0f3cc50d552165fc146057e6bea87db31d47c1 | |
parent | 78c0f050847cac4c43dbfc1916a63d1557c74ac0 (diff) | |
parent | b90ff3554aa3e123bb7e6d08789f6fd92d86ddde (diff) |
Merge suspend-to-idle rework material for v5.4.
* pm-s2idle-rework: (21 commits)
ACPI: PM: s2idle: Always set up EC GPE for system wakeup
ACPI: PM: s2idle: Avoid rearming SCI for wakeup unnecessarily
PM: suspend: Fix platform_suspend_prepare_noirq()
intel-hid: Disable button array during suspend-to-idle
intel-hid: intel-vbtn: Avoid leaking wakeup_mode set
ACPI: PM: s2idle: Execute LPS0 _DSM functions with suspended devices
ACPI: EC: PM: Make acpi_ec_dispatch_gpe() print debug message
ACPI: EC: PM: Consolidate some code depending on PM_SLEEP
ACPI: PM: s2idle: Eliminate acpi_sleep_no_ec_events()
ACPI: PM: s2idle: Switch EC over to polling during "noirq" suspend
ACPI: PM: s2idle: Add acpi.sleep_no_lps0 module parameter
ACPI: PM: s2idle: Rearrange lps0_device_attach()
ACPI: PM: Set up EC GPE for system wakeup from drivers that need it
PM: sleep: Drop dpm_noirq_begin() and dpm_noirq_end()
PM: sleep: Integrate suspend-to-idle with generig suspend flow
PM: sleep: Simplify suspend-to-idle control flow
ACPI: PM: Set s2idle_wakeup earlier and clear it later
PM: sleep: Fix possible overflow in pm_system_cancel_wakeup()
ACPI: EC: Return bool from acpi_ec_dispatch_gpe()
ACPICA: Return u32 from acpi_dispatch_gpe()
...
-rw-r--r-- | drivers/acpi/acpica/evxfgpe.c | 6 | ||||
-rw-r--r-- | drivers/acpi/ec.c | 57 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 6 | ||||
-rw-r--r-- | drivers/acpi/sleep.c | 165 | ||||
-rw-r--r-- | drivers/base/power/main.c | 35 | ||||
-rw-r--r-- | drivers/base/power/wakeup.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel-hid.c | 36 | ||||
-rw-r--r-- | drivers/platform/x86/intel-vbtn.c | 20 | ||||
-rw-r--r-- | include/acpi/acpixf.h | 8 | ||||
-rw-r--r-- | include/linux/acpi.h | 4 | ||||
-rw-r--r-- | include/linux/interrupt.h | 1 | ||||
-rw-r--r-- | include/linux/pm.h | 4 | ||||
-rw-r--r-- | include/linux/suspend.h | 4 | ||||
-rw-r--r-- | kernel/irq/pm.c | 20 | ||||
-rw-r--r-- | kernel/power/suspend.c | 65 |
15 files changed, 243 insertions, 190 deletions
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 710488ec59e9..04a40d563dd6 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c | |||
@@ -644,17 +644,17 @@ ACPI_EXPORT_SYMBOL(acpi_get_gpe_status) | |||
644 | * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 | 644 | * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 |
645 | * gpe_number - GPE level within the GPE block | 645 | * gpe_number - GPE level within the GPE block |
646 | * | 646 | * |
647 | * RETURN: None | 647 | * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED |
648 | * | 648 | * |
649 | * DESCRIPTION: Detect and dispatch a General Purpose Event to either a function | 649 | * DESCRIPTION: Detect and dispatch a General Purpose Event to either a function |
650 | * (e.g. EC) or method (e.g. _Lxx/_Exx) handler. | 650 | * (e.g. EC) or method (e.g. _Lxx/_Exx) handler. |
651 | * | 651 | * |
652 | ******************************************************************************/ | 652 | ******************************************************************************/ |
653 | void acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number) | 653 | u32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number) |
654 | { | 654 | { |
655 | ACPI_FUNCTION_TRACE(acpi_dispatch_gpe); | 655 | ACPI_FUNCTION_TRACE(acpi_dispatch_gpe); |
656 | 656 | ||
657 | acpi_ev_detect_gpe(gpe_device, NULL, gpe_number); | 657 | return acpi_ev_detect_gpe(gpe_device, NULL, gpe_number); |
658 | } | 658 | } |
659 | 659 | ||
660 | ACPI_EXPORT_SYMBOL(acpi_dispatch_gpe) | 660 | ACPI_EXPORT_SYMBOL(acpi_dispatch_gpe) |
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index c33756ed3304..da1e5c5ce150 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/list.h> | 25 | #include <linux/list.h> |
26 | #include <linux/spinlock.h> | 26 | #include <linux/spinlock.h> |
27 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
28 | #include <linux/suspend.h> | ||
28 | #include <linux/acpi.h> | 29 | #include <linux/acpi.h> |
29 | #include <linux/dmi.h> | 30 | #include <linux/dmi.h> |
30 | #include <asm/io.h> | 31 | #include <asm/io.h> |
@@ -1048,24 +1049,6 @@ void acpi_ec_unblock_transactions(void) | |||
1048 | acpi_ec_start(first_ec, true); | 1049 | acpi_ec_start(first_ec, true); |
1049 | } | 1050 | } |
1050 | 1051 | ||
1051 | void acpi_ec_mark_gpe_for_wake(void) | ||
1052 | { | ||
1053 | if (first_ec && !ec_no_wakeup) | ||
1054 | acpi_mark_gpe_for_wake(NULL, first_ec->gpe); | ||
1055 | } | ||
1056 | |||
1057 | void acpi_ec_set_gpe_wake_mask(u8 action) | ||
1058 | { | ||
1059 | if (first_ec && !ec_no_wakeup) | ||
1060 | acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action); | ||
1061 | } | ||
1062 | |||
1063 | void acpi_ec_dispatch_gpe(void) | ||
1064 | { | ||
1065 | if (first_ec) | ||
1066 | acpi_dispatch_gpe(NULL, first_ec->gpe); | ||
1067 | } | ||
1068 | |||
1069 | /* -------------------------------------------------------------------------- | 1052 | /* -------------------------------------------------------------------------- |
1070 | Event Management | 1053 | Event Management |
1071 | -------------------------------------------------------------------------- */ | 1054 | -------------------------------------------------------------------------- */ |
@@ -1931,7 +1914,7 @@ static int acpi_ec_suspend(struct device *dev) | |||
1931 | struct acpi_ec *ec = | 1914 | struct acpi_ec *ec = |
1932 | acpi_driver_data(to_acpi_device(dev)); | 1915 | acpi_driver_data(to_acpi_device(dev)); |
1933 | 1916 | ||
1934 | if (acpi_sleep_no_ec_events() && ec_freeze_events) | 1917 | if (!pm_suspend_no_platform() && ec_freeze_events) |
1935 | acpi_ec_disable_event(ec); | 1918 | acpi_ec_disable_event(ec); |
1936 | return 0; | 1919 | return 0; |
1937 | } | 1920 | } |
@@ -1948,8 +1931,7 @@ static int acpi_ec_suspend_noirq(struct device *dev) | |||
1948 | ec->reference_count >= 1) | 1931 | ec->reference_count >= 1) |
1949 | acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); | 1932 | acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); |
1950 | 1933 | ||
1951 | if (acpi_sleep_no_ec_events()) | 1934 | acpi_ec_enter_noirq(ec); |
1952 | acpi_ec_enter_noirq(ec); | ||
1953 | 1935 | ||
1954 | return 0; | 1936 | return 0; |
1955 | } | 1937 | } |
@@ -1958,8 +1940,7 @@ static int acpi_ec_resume_noirq(struct device *dev) | |||
1958 | { | 1940 | { |
1959 | struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); | 1941 | struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); |
1960 | 1942 | ||
1961 | if (acpi_sleep_no_ec_events()) | 1943 | acpi_ec_leave_noirq(ec); |
1962 | acpi_ec_leave_noirq(ec); | ||
1963 | 1944 | ||
1964 | if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) && | 1945 | if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) && |
1965 | ec->reference_count >= 1) | 1946 | ec->reference_count >= 1) |
@@ -1976,7 +1957,35 @@ static int acpi_ec_resume(struct device *dev) | |||
1976 | acpi_ec_enable_event(ec); | 1957 | acpi_ec_enable_event(ec); |
1977 | return 0; | 1958 | return 0; |
1978 | } | 1959 | } |
1979 | #endif | 1960 | |
1961 | void acpi_ec_mark_gpe_for_wake(void) | ||
1962 | { | ||
1963 | if (first_ec && !ec_no_wakeup) | ||
1964 | acpi_mark_gpe_for_wake(NULL, first_ec->gpe); | ||
1965 | } | ||
1966 | EXPORT_SYMBOL_GPL(acpi_ec_mark_gpe_for_wake); | ||
1967 | |||
1968 | void acpi_ec_set_gpe_wake_mask(u8 action) | ||
1969 | { | ||
1970 | if (pm_suspend_no_platform() && first_ec && !ec_no_wakeup) | ||
1971 | acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action); | ||
1972 | } | ||
1973 | |||
1974 | bool acpi_ec_dispatch_gpe(void) | ||
1975 | { | ||
1976 | u32 ret; | ||
1977 | |||
1978 | if (!first_ec) | ||
1979 | return false; | ||
1980 | |||
1981 | ret = acpi_dispatch_gpe(NULL, first_ec->gpe); | ||
1982 | if (ret == ACPI_INTERRUPT_HANDLED) { | ||
1983 | pm_pr_dbg("EC GPE dispatched\n"); | ||
1984 | return true; | ||
1985 | } | ||
1986 | return false; | ||
1987 | } | ||
1988 | #endif /* CONFIG_PM_SLEEP */ | ||
1980 | 1989 | ||
1981 | static const struct dev_pm_ops acpi_ec_pm = { | 1990 | static const struct dev_pm_ops acpi_ec_pm = { |
1982 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq) | 1991 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq) |
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index f4c2fe6be4f2..afe6636f9ad3 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
@@ -194,9 +194,6 @@ void acpi_ec_ecdt_probe(void); | |||
194 | void acpi_ec_dsdt_probe(void); | 194 | void acpi_ec_dsdt_probe(void); |
195 | void acpi_ec_block_transactions(void); | 195 | void acpi_ec_block_transactions(void); |
196 | void acpi_ec_unblock_transactions(void); | 196 | void acpi_ec_unblock_transactions(void); |
197 | void acpi_ec_mark_gpe_for_wake(void); | ||
198 | void acpi_ec_set_gpe_wake_mask(u8 action); | ||
199 | void acpi_ec_dispatch_gpe(void); | ||
200 | int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, | 197 | int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, |
201 | acpi_handle handle, acpi_ec_query_func func, | 198 | acpi_handle handle, acpi_ec_query_func func, |
202 | void *data); | 199 | void *data); |
@@ -204,6 +201,7 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); | |||
204 | 201 | ||
205 | #ifdef CONFIG_PM_SLEEP | 202 | #ifdef CONFIG_PM_SLEEP |
206 | void acpi_ec_flush_work(void); | 203 | void acpi_ec_flush_work(void); |
204 | bool acpi_ec_dispatch_gpe(void); | ||
207 | #endif | 205 | #endif |
208 | 206 | ||
209 | 207 | ||
@@ -212,11 +210,9 @@ void acpi_ec_flush_work(void); | |||
212 | -------------------------------------------------------------------------- */ | 210 | -------------------------------------------------------------------------- */ |
213 | #ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT | 211 | #ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT |
214 | extern bool acpi_s2idle_wakeup(void); | 212 | extern bool acpi_s2idle_wakeup(void); |
215 | extern bool acpi_sleep_no_ec_events(void); | ||
216 | extern int acpi_sleep_init(void); | 213 | extern int acpi_sleep_init(void); |
217 | #else | 214 | #else |
218 | static inline bool acpi_s2idle_wakeup(void) { return false; } | 215 | static inline bool acpi_s2idle_wakeup(void) { return false; } |
219 | static inline bool acpi_sleep_no_ec_events(void) { return true; } | ||
220 | static inline int acpi_sleep_init(void) { return -ENXIO; } | 216 | static inline int acpi_sleep_init(void) { return -ENXIO; } |
221 | #endif | 217 | #endif |
222 | 218 | ||
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index f0fe7c15d657..9fa77d72ef27 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c | |||
@@ -89,6 +89,10 @@ bool acpi_sleep_state_supported(u8 sleep_state) | |||
89 | } | 89 | } |
90 | 90 | ||
91 | #ifdef CONFIG_ACPI_SLEEP | 91 | #ifdef CONFIG_ACPI_SLEEP |
92 | static bool sleep_no_lps0 __read_mostly; | ||
93 | module_param(sleep_no_lps0, bool, 0644); | ||
94 | MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface"); | ||
95 | |||
92 | static u32 acpi_target_sleep_state = ACPI_STATE_S0; | 96 | static u32 acpi_target_sleep_state = ACPI_STATE_S0; |
93 | 97 | ||
94 | u32 acpi_target_system_state(void) | 98 | u32 acpi_target_system_state(void) |
@@ -158,11 +162,11 @@ static int __init init_nvs_nosave(const struct dmi_system_id *d) | |||
158 | return 0; | 162 | return 0; |
159 | } | 163 | } |
160 | 164 | ||
161 | static bool acpi_sleep_no_lps0; | 165 | static bool acpi_sleep_default_s3; |
162 | 166 | ||
163 | static int __init init_no_lps0(const struct dmi_system_id *d) | 167 | static int __init init_default_s3(const struct dmi_system_id *d) |
164 | { | 168 | { |
165 | acpi_sleep_no_lps0 = true; | 169 | acpi_sleep_default_s3 = true; |
166 | return 0; | 170 | return 0; |
167 | } | 171 | } |
168 | 172 | ||
@@ -363,7 +367,7 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = { | |||
363 | * S0 Idle firmware interface. | 367 | * S0 Idle firmware interface. |
364 | */ | 368 | */ |
365 | { | 369 | { |
366 | .callback = init_no_lps0, | 370 | .callback = init_default_s3, |
367 | .ident = "Dell XPS13 9360", | 371 | .ident = "Dell XPS13 9360", |
368 | .matches = { | 372 | .matches = { |
369 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 373 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
@@ -376,7 +380,7 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = { | |||
376 | * https://bugzilla.kernel.org/show_bug.cgi?id=199057). | 380 | * https://bugzilla.kernel.org/show_bug.cgi?id=199057). |
377 | */ | 381 | */ |
378 | { | 382 | { |
379 | .callback = init_no_lps0, | 383 | .callback = init_default_s3, |
380 | .ident = "ThinkPad X1 Tablet(2016)", | 384 | .ident = "ThinkPad X1 Tablet(2016)", |
381 | .matches = { | 385 | .matches = { |
382 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), | 386 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), |
@@ -524,8 +528,9 @@ static void acpi_pm_end(void) | |||
524 | acpi_sleep_tts_switch(acpi_target_sleep_state); | 528 | acpi_sleep_tts_switch(acpi_target_sleep_state); |
525 | } | 529 | } |
526 | #else /* !CONFIG_ACPI_SLEEP */ | 530 | #else /* !CONFIG_ACPI_SLEEP */ |
531 | #define sleep_no_lps0 (1) | ||
527 | #define acpi_target_sleep_state ACPI_STATE_S0 | 532 | #define acpi_target_sleep_state ACPI_STATE_S0 |
528 | #define acpi_sleep_no_lps0 (false) | 533 | #define acpi_sleep_default_s3 (1) |
529 | static inline void acpi_sleep_dmi_check(void) {} | 534 | static inline void acpi_sleep_dmi_check(void) {} |
530 | #endif /* CONFIG_ACPI_SLEEP */ | 535 | #endif /* CONFIG_ACPI_SLEEP */ |
531 | 536 | ||
@@ -691,7 +696,6 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = { | |||
691 | .recover = acpi_pm_finish, | 696 | .recover = acpi_pm_finish, |
692 | }; | 697 | }; |
693 | 698 | ||
694 | static bool s2idle_in_progress; | ||
695 | static bool s2idle_wakeup; | 699 | static bool s2idle_wakeup; |
696 | 700 | ||
697 | /* | 701 | /* |
@@ -904,42 +908,43 @@ static int lps0_device_attach(struct acpi_device *adev, | |||
904 | if (lps0_device_handle) | 908 | if (lps0_device_handle) |
905 | return 0; | 909 | return 0; |
906 | 910 | ||
907 | if (acpi_sleep_no_lps0) { | ||
908 | acpi_handle_info(adev->handle, | ||
909 | "Low Power S0 Idle interface disabled\n"); | ||
910 | return 0; | ||
911 | } | ||
912 | |||
913 | if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) | 911 | if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) |
914 | return 0; | 912 | return 0; |
915 | 913 | ||
916 | guid_parse(ACPI_LPS0_DSM_UUID, &lps0_dsm_guid); | 914 | guid_parse(ACPI_LPS0_DSM_UUID, &lps0_dsm_guid); |
917 | /* Check if the _DSM is present and as expected. */ | 915 | /* Check if the _DSM is present and as expected. */ |
918 | out_obj = acpi_evaluate_dsm(adev->handle, &lps0_dsm_guid, 1, 0, NULL); | 916 | out_obj = acpi_evaluate_dsm(adev->handle, &lps0_dsm_guid, 1, 0, NULL); |
919 | if (out_obj && out_obj->type == ACPI_TYPE_BUFFER) { | 917 | if (!out_obj || out_obj->type != ACPI_TYPE_BUFFER) { |
920 | char bitmask = *(char *)out_obj->buffer.pointer; | ||
921 | |||
922 | lps0_dsm_func_mask = bitmask; | ||
923 | lps0_device_handle = adev->handle; | ||
924 | /* | ||
925 | * Use suspend-to-idle by default if the default | ||
926 | * suspend mode was not set from the command line. | ||
927 | */ | ||
928 | if (mem_sleep_default > PM_SUSPEND_MEM) | ||
929 | mem_sleep_current = PM_SUSPEND_TO_IDLE; | ||
930 | |||
931 | acpi_handle_debug(adev->handle, "_DSM function mask: 0x%x\n", | ||
932 | bitmask); | ||
933 | |||
934 | acpi_ec_mark_gpe_for_wake(); | ||
935 | } else { | ||
936 | acpi_handle_debug(adev->handle, | 918 | acpi_handle_debug(adev->handle, |
937 | "_DSM function 0 evaluation failed\n"); | 919 | "_DSM function 0 evaluation failed\n"); |
920 | return 0; | ||
938 | } | 921 | } |
922 | |||
923 | lps0_dsm_func_mask = *(char *)out_obj->buffer.pointer; | ||
924 | |||
939 | ACPI_FREE(out_obj); | 925 | ACPI_FREE(out_obj); |
940 | 926 | ||
927 | acpi_handle_debug(adev->handle, "_DSM function mask: 0x%x\n", | ||
928 | lps0_dsm_func_mask); | ||
929 | |||
930 | lps0_device_handle = adev->handle; | ||
931 | |||
941 | lpi_device_get_constraints(); | 932 | lpi_device_get_constraints(); |
942 | 933 | ||
934 | /* | ||
935 | * Use suspend-to-idle by default if the default suspend mode was not | ||
936 | * set from the command line. | ||
937 | */ | ||
938 | if (mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) | ||
939 | mem_sleep_current = PM_SUSPEND_TO_IDLE; | ||
940 | |||
941 | /* | ||
942 | * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the | ||
943 | * EC GPE to be enabled while suspended for certain wakeup devices to | ||
944 | * work, so mark it as wakeup-capable. | ||
945 | */ | ||
946 | acpi_ec_mark_gpe_for_wake(); | ||
947 | |||
943 | return 0; | 948 | return 0; |
944 | } | 949 | } |
945 | 950 | ||
@@ -951,98 +956,110 @@ static struct acpi_scan_handler lps0_handler = { | |||
951 | static int acpi_s2idle_begin(void) | 956 | static int acpi_s2idle_begin(void) |
952 | { | 957 | { |
953 | acpi_scan_lock_acquire(); | 958 | acpi_scan_lock_acquire(); |
954 | s2idle_in_progress = true; | ||
955 | return 0; | 959 | return 0; |
956 | } | 960 | } |
957 | 961 | ||
958 | static int acpi_s2idle_prepare(void) | 962 | static int acpi_s2idle_prepare(void) |
959 | { | 963 | { |
960 | if (lps0_device_handle) { | 964 | if (acpi_sci_irq_valid()) { |
961 | acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF); | 965 | enable_irq_wake(acpi_sci_irq); |
962 | acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY); | ||
963 | |||
964 | acpi_ec_set_gpe_wake_mask(ACPI_GPE_ENABLE); | 966 | acpi_ec_set_gpe_wake_mask(ACPI_GPE_ENABLE); |
965 | } | 967 | } |
966 | 968 | ||
967 | if (acpi_sci_irq_valid()) | ||
968 | enable_irq_wake(acpi_sci_irq); | ||
969 | |||
970 | acpi_enable_wakeup_devices(ACPI_STATE_S0); | 969 | acpi_enable_wakeup_devices(ACPI_STATE_S0); |
971 | 970 | ||
972 | /* Change the configuration of GPEs to avoid spurious wakeup. */ | 971 | /* Change the configuration of GPEs to avoid spurious wakeup. */ |
973 | acpi_enable_all_wakeup_gpes(); | 972 | acpi_enable_all_wakeup_gpes(); |
974 | acpi_os_wait_events_complete(); | 973 | acpi_os_wait_events_complete(); |
974 | |||
975 | s2idle_wakeup = true; | ||
975 | return 0; | 976 | return 0; |
976 | } | 977 | } |
977 | 978 | ||
978 | static void acpi_s2idle_wake(void) | 979 | static int acpi_s2idle_prepare_late(void) |
979 | { | 980 | { |
980 | if (!lps0_device_handle) | 981 | if (!lps0_device_handle || sleep_no_lps0) |
981 | return; | 982 | return 0; |
982 | 983 | ||
983 | if (pm_debug_messages_on) | 984 | if (pm_debug_messages_on) |
984 | lpi_check_constraints(); | 985 | lpi_check_constraints(); |
985 | 986 | ||
987 | acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF); | ||
988 | acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY); | ||
989 | |||
990 | return 0; | ||
991 | } | ||
992 | |||
993 | static void acpi_s2idle_wake(void) | ||
994 | { | ||
995 | /* | ||
996 | * If IRQD_WAKEUP_ARMED is set for the SCI at this point, the SCI has | ||
997 | * not triggered while suspended, so bail out. | ||
998 | */ | ||
999 | if (!acpi_sci_irq_valid() || | ||
1000 | irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) | ||
1001 | return; | ||
1002 | |||
986 | /* | 1003 | /* |
987 | * If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means | 1004 | * If there are EC events to process, the wakeup may be a spurious one |
988 | * that the SCI has triggered while suspended, so cancel the wakeup in | 1005 | * coming from the EC. |
989 | * case it has not been a wakeup event (the GPEs will be checked later). | ||
990 | */ | 1006 | */ |
991 | if (acpi_sci_irq_valid() && | 1007 | if (acpi_ec_dispatch_gpe()) { |
992 | !irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) { | 1008 | /* |
1009 | * Cancel the wakeup and process all pending events in case | ||
1010 | * there are any wakeup ones in there. | ||
1011 | * | ||
1012 | * Note that if any non-EC GPEs are active at this point, the | ||
1013 | * SCI will retrigger after the rearming below, so no events | ||
1014 | * should be missed by canceling the wakeup here. | ||
1015 | */ | ||
993 | pm_system_cancel_wakeup(); | 1016 | pm_system_cancel_wakeup(); |
994 | s2idle_wakeup = true; | ||
995 | /* | 1017 | /* |
996 | * On some platforms with the LPS0 _DSM device noirq resume | 1018 | * The EC driver uses the system workqueue and an additional |
997 | * takes too much time for EC wakeup events to survive, so look | 1019 | * special one, so those need to be flushed too. |
998 | * for them now. | ||
999 | */ | 1020 | */ |
1000 | acpi_ec_dispatch_gpe(); | 1021 | acpi_os_wait_events_complete(); /* synchronize EC GPE processing */ |
1022 | acpi_ec_flush_work(); | ||
1023 | acpi_os_wait_events_complete(); /* synchronize Notify handling */ | ||
1024 | |||
1025 | rearm_wake_irq(acpi_sci_irq); | ||
1001 | } | 1026 | } |
1002 | } | 1027 | } |
1003 | 1028 | ||
1004 | static void acpi_s2idle_sync(void) | 1029 | static void acpi_s2idle_restore_early(void) |
1005 | { | 1030 | { |
1006 | /* | 1031 | if (!lps0_device_handle || sleep_no_lps0) |
1007 | * Process all pending events in case there are any wakeup ones. | 1032 | return; |
1008 | * | 1033 | |
1009 | * The EC driver uses the system workqueue and an additional special | 1034 | acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT); |
1010 | * one, so those need to be flushed too. | 1035 | acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON); |
1011 | */ | ||
1012 | acpi_os_wait_events_complete(); /* synchronize SCI IRQ handling */ | ||
1013 | acpi_ec_flush_work(); | ||
1014 | acpi_os_wait_events_complete(); /* synchronize Notify handling */ | ||
1015 | s2idle_wakeup = false; | ||
1016 | } | 1036 | } |
1017 | 1037 | ||
1018 | static void acpi_s2idle_restore(void) | 1038 | static void acpi_s2idle_restore(void) |
1019 | { | 1039 | { |
1040 | s2idle_wakeup = false; | ||
1041 | |||
1020 | acpi_enable_all_runtime_gpes(); | 1042 | acpi_enable_all_runtime_gpes(); |
1021 | 1043 | ||
1022 | acpi_disable_wakeup_devices(ACPI_STATE_S0); | 1044 | acpi_disable_wakeup_devices(ACPI_STATE_S0); |
1023 | 1045 | ||
1024 | if (acpi_sci_irq_valid()) | 1046 | if (acpi_sci_irq_valid()) { |
1025 | disable_irq_wake(acpi_sci_irq); | ||
1026 | |||
1027 | if (lps0_device_handle) { | ||
1028 | acpi_ec_set_gpe_wake_mask(ACPI_GPE_DISABLE); | 1047 | acpi_ec_set_gpe_wake_mask(ACPI_GPE_DISABLE); |
1029 | 1048 | disable_irq_wake(acpi_sci_irq); | |
1030 | acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT); | ||
1031 | acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON); | ||
1032 | } | 1049 | } |
1033 | } | 1050 | } |
1034 | 1051 | ||
1035 | static void acpi_s2idle_end(void) | 1052 | static void acpi_s2idle_end(void) |
1036 | { | 1053 | { |
1037 | s2idle_in_progress = false; | ||
1038 | acpi_scan_lock_release(); | 1054 | acpi_scan_lock_release(); |
1039 | } | 1055 | } |
1040 | 1056 | ||
1041 | static const struct platform_s2idle_ops acpi_s2idle_ops = { | 1057 | static const struct platform_s2idle_ops acpi_s2idle_ops = { |
1042 | .begin = acpi_s2idle_begin, | 1058 | .begin = acpi_s2idle_begin, |
1043 | .prepare = acpi_s2idle_prepare, | 1059 | .prepare = acpi_s2idle_prepare, |
1060 | .prepare_late = acpi_s2idle_prepare_late, | ||
1044 | .wake = acpi_s2idle_wake, | 1061 | .wake = acpi_s2idle_wake, |
1045 | .sync = acpi_s2idle_sync, | 1062 | .restore_early = acpi_s2idle_restore_early, |
1046 | .restore = acpi_s2idle_restore, | 1063 | .restore = acpi_s2idle_restore, |
1047 | .end = acpi_s2idle_end, | 1064 | .end = acpi_s2idle_end, |
1048 | }; | 1065 | }; |
@@ -1063,7 +1080,6 @@ static void acpi_sleep_suspend_setup(void) | |||
1063 | } | 1080 | } |
1064 | 1081 | ||
1065 | #else /* !CONFIG_SUSPEND */ | 1082 | #else /* !CONFIG_SUSPEND */ |
1066 | #define s2idle_in_progress (false) | ||
1067 | #define s2idle_wakeup (false) | 1083 | #define s2idle_wakeup (false) |
1068 | #define lps0_device_handle (NULL) | 1084 | #define lps0_device_handle (NULL) |
1069 | static inline void acpi_sleep_suspend_setup(void) {} | 1085 | static inline void acpi_sleep_suspend_setup(void) {} |
@@ -1074,11 +1090,6 @@ bool acpi_s2idle_wakeup(void) | |||
1074 | return s2idle_wakeup; | 1090 | return s2idle_wakeup; |
1075 | } | 1091 | } |
1076 | 1092 | ||
1077 | bool acpi_sleep_no_ec_events(void) | ||
1078 | { | ||
1079 | return !s2idle_in_progress || !lps0_device_handle; | ||
1080 | } | ||
1081 | |||
1082 | #ifdef CONFIG_PM_SLEEP | 1093 | #ifdef CONFIG_PM_SLEEP |
1083 | static u32 saved_bm_rld; | 1094 | static u32 saved_bm_rld; |
1084 | 1095 | ||
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 7fb2c39bc725..134a8af51511 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -716,7 +716,7 @@ static void async_resume_noirq(void *data, async_cookie_t cookie) | |||
716 | put_device(dev); | 716 | put_device(dev); |
717 | } | 717 | } |
718 | 718 | ||
719 | void dpm_noirq_resume_devices(pm_message_t state) | 719 | static void dpm_noirq_resume_devices(pm_message_t state) |
720 | { | 720 | { |
721 | struct device *dev; | 721 | struct device *dev; |
722 | ktime_t starttime = ktime_get(); | 722 | ktime_t starttime = ktime_get(); |
@@ -760,13 +760,6 @@ void dpm_noirq_resume_devices(pm_message_t state) | |||
760 | trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false); | 760 | trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false); |
761 | } | 761 | } |
762 | 762 | ||
763 | void dpm_noirq_end(void) | ||
764 | { | ||
765 | resume_device_irqs(); | ||
766 | device_wakeup_disarm_wake_irqs(); | ||
767 | cpuidle_resume(); | ||
768 | } | ||
769 | |||
770 | /** | 763 | /** |
771 | * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices. | 764 | * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices. |
772 | * @state: PM transition of the system being carried out. | 765 | * @state: PM transition of the system being carried out. |
@@ -777,7 +770,11 @@ void dpm_noirq_end(void) | |||
777 | void dpm_resume_noirq(pm_message_t state) | 770 | void dpm_resume_noirq(pm_message_t state) |
778 | { | 771 | { |
779 | dpm_noirq_resume_devices(state); | 772 | dpm_noirq_resume_devices(state); |
780 | dpm_noirq_end(); | 773 | |
774 | resume_device_irqs(); | ||
775 | device_wakeup_disarm_wake_irqs(); | ||
776 | |||
777 | cpuidle_resume(); | ||
781 | } | 778 | } |
782 | 779 | ||
783 | static pm_callback_t dpm_subsys_resume_early_cb(struct device *dev, | 780 | static pm_callback_t dpm_subsys_resume_early_cb(struct device *dev, |
@@ -1291,11 +1288,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a | |||
1291 | if (async_error) | 1288 | if (async_error) |
1292 | goto Complete; | 1289 | goto Complete; |
1293 | 1290 | ||
1294 | if (pm_wakeup_pending()) { | ||
1295 | async_error = -EBUSY; | ||
1296 | goto Complete; | ||
1297 | } | ||
1298 | |||
1299 | if (dev->power.syscore || dev->power.direct_complete) | 1291 | if (dev->power.syscore || dev->power.direct_complete) |
1300 | goto Complete; | 1292 | goto Complete; |
1301 | 1293 | ||
@@ -1362,14 +1354,7 @@ static int device_suspend_noirq(struct device *dev) | |||
1362 | return __device_suspend_noirq(dev, pm_transition, false); | 1354 | return __device_suspend_noirq(dev, pm_transition, false); |
1363 | } | 1355 | } |
1364 | 1356 | ||
1365 | void dpm_noirq_begin(void) | 1357 | static int dpm_noirq_suspend_devices(pm_message_t state) |
1366 | { | ||
1367 | cpuidle_pause(); | ||
1368 | device_wakeup_arm_wake_irqs(); | ||
1369 | suspend_device_irqs(); | ||
1370 | } | ||
1371 | |||
1372 | int dpm_noirq_suspend_devices(pm_message_t state) | ||
1373 | { | 1358 | { |
1374 | ktime_t starttime = ktime_get(); | 1359 | ktime_t starttime = ktime_get(); |
1375 | int error = 0; | 1360 | int error = 0; |
@@ -1426,7 +1411,11 @@ int dpm_suspend_noirq(pm_message_t state) | |||
1426 | { | 1411 | { |
1427 | int ret; | 1412 | int ret; |
1428 | 1413 | ||
1429 | dpm_noirq_begin(); | 1414 | cpuidle_pause(); |
1415 | |||
1416 | device_wakeup_arm_wake_irqs(); | ||
1417 | suspend_device_irqs(); | ||
1418 | |||
1430 | ret = dpm_noirq_suspend_devices(state); | 1419 | ret = dpm_noirq_suspend_devices(state); |
1431 | if (ret) | 1420 | if (ret) |
1432 | dpm_resume_noirq(resume_event(state)); | 1421 | dpm_resume_noirq(resume_event(state)); |
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index e58b070985b1..5817b51d2b15 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c | |||
@@ -879,7 +879,7 @@ EXPORT_SYMBOL_GPL(pm_system_wakeup); | |||
879 | 879 | ||
880 | void pm_system_cancel_wakeup(void) | 880 | void pm_system_cancel_wakeup(void) |
881 | { | 881 | { |
882 | atomic_dec(&pm_abort_suspend); | 882 | atomic_dec_if_positive(&pm_abort_suspend); |
883 | } | 883 | } |
884 | 884 | ||
885 | void pm_wakeup_clear(bool reset) | 885 | void pm_wakeup_clear(bool reset) |
diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index bc0d55a59015..ef6d4bd77b1a 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c | |||
@@ -253,35 +253,45 @@ static void intel_button_array_enable(struct device *device, bool enable) | |||
253 | 253 | ||
254 | static int intel_hid_pm_prepare(struct device *device) | 254 | static int intel_hid_pm_prepare(struct device *device) |
255 | { | 255 | { |
256 | struct intel_hid_priv *priv = dev_get_drvdata(device); | 256 | if (device_may_wakeup(device)) { |
257 | struct intel_hid_priv *priv = dev_get_drvdata(device); | ||
257 | 258 | ||
258 | priv->wakeup_mode = true; | 259 | priv->wakeup_mode = true; |
260 | } | ||
259 | return 0; | 261 | return 0; |
260 | } | 262 | } |
261 | 263 | ||
264 | static void intel_hid_pm_complete(struct device *device) | ||
265 | { | ||
266 | struct intel_hid_priv *priv = dev_get_drvdata(device); | ||
267 | |||
268 | priv->wakeup_mode = false; | ||
269 | } | ||
270 | |||
262 | static int intel_hid_pl_suspend_handler(struct device *device) | 271 | static int intel_hid_pl_suspend_handler(struct device *device) |
263 | { | 272 | { |
264 | if (pm_suspend_via_firmware()) { | 273 | intel_button_array_enable(device, false); |
274 | |||
275 | if (!pm_suspend_no_platform()) | ||
265 | intel_hid_set_enable(device, false); | 276 | intel_hid_set_enable(device, false); |
266 | intel_button_array_enable(device, false); | 277 | |
267 | } | ||
268 | return 0; | 278 | return 0; |
269 | } | 279 | } |
270 | 280 | ||
271 | static int intel_hid_pl_resume_handler(struct device *device) | 281 | static int intel_hid_pl_resume_handler(struct device *device) |
272 | { | 282 | { |
273 | struct intel_hid_priv *priv = dev_get_drvdata(device); | 283 | intel_hid_pm_complete(device); |
274 | 284 | ||
275 | priv->wakeup_mode = false; | 285 | if (!pm_suspend_no_platform()) |
276 | if (pm_resume_via_firmware()) { | ||
277 | intel_hid_set_enable(device, true); | 286 | intel_hid_set_enable(device, true); |
278 | intel_button_array_enable(device, true); | 287 | |
279 | } | 288 | intel_button_array_enable(device, true); |
280 | return 0; | 289 | return 0; |
281 | } | 290 | } |
282 | 291 | ||
283 | static const struct dev_pm_ops intel_hid_pl_pm_ops = { | 292 | static const struct dev_pm_ops intel_hid_pl_pm_ops = { |
284 | .prepare = intel_hid_pm_prepare, | 293 | .prepare = intel_hid_pm_prepare, |
294 | .complete = intel_hid_pm_complete, | ||
285 | .freeze = intel_hid_pl_suspend_handler, | 295 | .freeze = intel_hid_pl_suspend_handler, |
286 | .thaw = intel_hid_pl_resume_handler, | 296 | .thaw = intel_hid_pl_resume_handler, |
287 | .restore = intel_hid_pl_resume_handler, | 297 | .restore = intel_hid_pl_resume_handler, |
@@ -491,6 +501,12 @@ static int intel_hid_probe(struct platform_device *device) | |||
491 | } | 501 | } |
492 | 502 | ||
493 | device_init_wakeup(&device->dev, true); | 503 | device_init_wakeup(&device->dev, true); |
504 | /* | ||
505 | * In order for system wakeup to work, the EC GPE has to be marked as | ||
506 | * a wakeup one, so do that here (this setting will persist, but it has | ||
507 | * no effect until the wakeup mask is set for the EC GPE). | ||
508 | */ | ||
509 | acpi_ec_mark_gpe_for_wake(); | ||
494 | return 0; | 510 | return 0; |
495 | 511 | ||
496 | err_remove_notify: | 512 | err_remove_notify: |
diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c index a0d0cecff55f..b74932307d69 100644 --- a/drivers/platform/x86/intel-vbtn.c +++ b/drivers/platform/x86/intel-vbtn.c | |||
@@ -176,6 +176,12 @@ static int intel_vbtn_probe(struct platform_device *device) | |||
176 | return -EBUSY; | 176 | return -EBUSY; |
177 | 177 | ||
178 | device_init_wakeup(&device->dev, true); | 178 | device_init_wakeup(&device->dev, true); |
179 | /* | ||
180 | * In order for system wakeup to work, the EC GPE has to be marked as | ||
181 | * a wakeup one, so do that here (this setting will persist, but it has | ||
182 | * no effect until the wakeup mask is set for the EC GPE). | ||
183 | */ | ||
184 | acpi_ec_mark_gpe_for_wake(); | ||
179 | return 0; | 185 | return 0; |
180 | } | 186 | } |
181 | 187 | ||
@@ -195,22 +201,30 @@ static int intel_vbtn_remove(struct platform_device *device) | |||
195 | 201 | ||
196 | static int intel_vbtn_pm_prepare(struct device *dev) | 202 | static int intel_vbtn_pm_prepare(struct device *dev) |
197 | { | 203 | { |
198 | struct intel_vbtn_priv *priv = dev_get_drvdata(dev); | 204 | if (device_may_wakeup(dev)) { |
205 | struct intel_vbtn_priv *priv = dev_get_drvdata(dev); | ||
199 | 206 | ||
200 | priv->wakeup_mode = true; | 207 | priv->wakeup_mode = true; |
208 | } | ||
201 | return 0; | 209 | return 0; |
202 | } | 210 | } |
203 | 211 | ||
204 | static int intel_vbtn_pm_resume(struct device *dev) | 212 | static void intel_vbtn_pm_complete(struct device *dev) |
205 | { | 213 | { |
206 | struct intel_vbtn_priv *priv = dev_get_drvdata(dev); | 214 | struct intel_vbtn_priv *priv = dev_get_drvdata(dev); |
207 | 215 | ||
208 | priv->wakeup_mode = false; | 216 | priv->wakeup_mode = false; |
217 | } | ||
218 | |||
219 | static int intel_vbtn_pm_resume(struct device *dev) | ||
220 | { | ||
221 | intel_vbtn_pm_complete(dev); | ||
209 | return 0; | 222 | return 0; |
210 | } | 223 | } |
211 | 224 | ||
212 | static const struct dev_pm_ops intel_vbtn_pm_ops = { | 225 | static const struct dev_pm_ops intel_vbtn_pm_ops = { |
213 | .prepare = intel_vbtn_pm_prepare, | 226 | .prepare = intel_vbtn_pm_prepare, |
227 | .complete = intel_vbtn_pm_complete, | ||
214 | .resume = intel_vbtn_pm_resume, | 228 | .resume = intel_vbtn_pm_resume, |
215 | .restore = intel_vbtn_pm_resume, | 229 | .restore = intel_vbtn_pm_resume, |
216 | .thaw = intel_vbtn_pm_resume, | 230 | .thaw = intel_vbtn_pm_resume, |
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 3845c8fcc94e..4ed603a3b448 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h | |||
@@ -297,6 +297,9 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); | |||
297 | #define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \ | 297 | #define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \ |
298 | ACPI_EXTERNAL_RETURN_OK(prototype) | 298 | ACPI_EXTERNAL_RETURN_OK(prototype) |
299 | 299 | ||
300 | #define ACPI_HW_DEPENDENT_RETURN_UINT32(prototype) \ | ||
301 | ACPI_EXTERNAL_RETURN_UINT32(prototype) | ||
302 | |||
300 | #define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \ | 303 | #define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \ |
301 | ACPI_EXTERNAL_RETURN_VOID(prototype) | 304 | ACPI_EXTERNAL_RETURN_VOID(prototype) |
302 | 305 | ||
@@ -307,6 +310,9 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); | |||
307 | #define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \ | 310 | #define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \ |
308 | static ACPI_INLINE prototype {return(AE_OK);} | 311 | static ACPI_INLINE prototype {return(AE_OK);} |
309 | 312 | ||
313 | #define ACPI_HW_DEPENDENT_RETURN_UINT32(prototype) \ | ||
314 | static ACPI_INLINE prototype {return(0);} | ||
315 | |||
310 | #define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \ | 316 | #define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \ |
311 | static ACPI_INLINE prototype {return;} | 317 | static ACPI_INLINE prototype {return;} |
312 | 318 | ||
@@ -738,7 +744,7 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status | |||
738 | u32 gpe_number, | 744 | u32 gpe_number, |
739 | acpi_event_status | 745 | acpi_event_status |
740 | *event_status)) | 746 | *event_status)) |
741 | ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number)) | 747 | ACPI_HW_DEPENDENT_RETURN_UINT32(u32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number)) |
742 | ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void)) | 748 | ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void)) |
743 | ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void)) | 749 | ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void)) |
744 | ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_wakeup_gpes(void)) | 750 | ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_wakeup_gpes(void)) |
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 9426b9aaed86..e65a4c5bbeae 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
@@ -931,6 +931,8 @@ int acpi_subsys_suspend_noirq(struct device *dev); | |||
931 | int acpi_subsys_suspend(struct device *dev); | 931 | int acpi_subsys_suspend(struct device *dev); |
932 | int acpi_subsys_freeze(struct device *dev); | 932 | int acpi_subsys_freeze(struct device *dev); |
933 | int acpi_subsys_poweroff(struct device *dev); | 933 | int acpi_subsys_poweroff(struct device *dev); |
934 | void acpi_ec_mark_gpe_for_wake(void); | ||
935 | void acpi_ec_set_gpe_wake_mask(u8 action); | ||
934 | #else | 936 | #else |
935 | static inline int acpi_subsys_prepare(struct device *dev) { return 0; } | 937 | static inline int acpi_subsys_prepare(struct device *dev) { return 0; } |
936 | static inline void acpi_subsys_complete(struct device *dev) {} | 938 | static inline void acpi_subsys_complete(struct device *dev) {} |
@@ -939,6 +941,8 @@ static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; } | |||
939 | static inline int acpi_subsys_suspend(struct device *dev) { return 0; } | 941 | static inline int acpi_subsys_suspend(struct device *dev) { return 0; } |
940 | static inline int acpi_subsys_freeze(struct device *dev) { return 0; } | 942 | static inline int acpi_subsys_freeze(struct device *dev) { return 0; } |
941 | static inline int acpi_subsys_poweroff(struct device *dev) { return 0; } | 943 | static inline int acpi_subsys_poweroff(struct device *dev) { return 0; } |
944 | static inline void acpi_ec_mark_gpe_for_wake(void) {} | ||
945 | static inline void acpi_ec_set_gpe_wake_mask(u8 action) {} | ||
942 | #endif | 946 | #endif |
943 | 947 | ||
944 | #ifdef CONFIG_ACPI | 948 | #ifdef CONFIG_ACPI |
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 5b8328a99b2a..0e9cdb3efda7 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h | |||
@@ -238,6 +238,7 @@ extern void teardown_percpu_nmi(unsigned int irq); | |||
238 | /* The following three functions are for the core kernel use only. */ | 238 | /* The following three functions are for the core kernel use only. */ |
239 | extern void suspend_device_irqs(void); | 239 | extern void suspend_device_irqs(void); |
240 | extern void resume_device_irqs(void); | 240 | extern void resume_device_irqs(void); |
241 | extern void rearm_wake_irq(unsigned int irq); | ||
241 | 242 | ||
242 | /** | 243 | /** |
243 | * struct irq_affinity_notify - context for notification of IRQ affinity changes | 244 | * struct irq_affinity_notify - context for notification of IRQ affinity changes |
diff --git a/include/linux/pm.h b/include/linux/pm.h index 3619a870eaa4..4c441be03079 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h | |||
@@ -712,8 +712,6 @@ struct dev_pm_domain { | |||
712 | extern void device_pm_lock(void); | 712 | extern void device_pm_lock(void); |
713 | extern void dpm_resume_start(pm_message_t state); | 713 | extern void dpm_resume_start(pm_message_t state); |
714 | extern void dpm_resume_end(pm_message_t state); | 714 | extern void dpm_resume_end(pm_message_t state); |
715 | extern void dpm_noirq_resume_devices(pm_message_t state); | ||
716 | extern void dpm_noirq_end(void); | ||
717 | extern void dpm_resume_noirq(pm_message_t state); | 715 | extern void dpm_resume_noirq(pm_message_t state); |
718 | extern void dpm_resume_early(pm_message_t state); | 716 | extern void dpm_resume_early(pm_message_t state); |
719 | extern void dpm_resume(pm_message_t state); | 717 | extern void dpm_resume(pm_message_t state); |
@@ -722,8 +720,6 @@ extern void dpm_complete(pm_message_t state); | |||
722 | extern void device_pm_unlock(void); | 720 | extern void device_pm_unlock(void); |
723 | extern int dpm_suspend_end(pm_message_t state); | 721 | extern int dpm_suspend_end(pm_message_t state); |
724 | extern int dpm_suspend_start(pm_message_t state); | 722 | extern int dpm_suspend_start(pm_message_t state); |
725 | extern void dpm_noirq_begin(void); | ||
726 | extern int dpm_noirq_suspend_devices(pm_message_t state); | ||
727 | extern int dpm_suspend_noirq(pm_message_t state); | 723 | extern int dpm_suspend_noirq(pm_message_t state); |
728 | extern int dpm_suspend_late(pm_message_t state); | 724 | extern int dpm_suspend_late(pm_message_t state); |
729 | extern int dpm_suspend(pm_message_t state); | 725 | extern int dpm_suspend(pm_message_t state); |
diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 9c0ad1a3a727..6fc8843f1c9e 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h | |||
@@ -190,8 +190,9 @@ struct platform_suspend_ops { | |||
190 | struct platform_s2idle_ops { | 190 | struct platform_s2idle_ops { |
191 | int (*begin)(void); | 191 | int (*begin)(void); |
192 | int (*prepare)(void); | 192 | int (*prepare)(void); |
193 | int (*prepare_late)(void); | ||
193 | void (*wake)(void); | 194 | void (*wake)(void); |
194 | void (*sync)(void); | 195 | void (*restore_early)(void); |
195 | void (*restore)(void); | 196 | void (*restore)(void); |
196 | void (*end)(void); | 197 | void (*end)(void); |
197 | }; | 198 | }; |
@@ -336,6 +337,7 @@ static inline void pm_set_suspend_via_firmware(void) {} | |||
336 | static inline void pm_set_resume_via_firmware(void) {} | 337 | static inline void pm_set_resume_via_firmware(void) {} |
337 | static inline bool pm_suspend_via_firmware(void) { return false; } | 338 | static inline bool pm_suspend_via_firmware(void) { return false; } |
338 | static inline bool pm_resume_via_firmware(void) { return false; } | 339 | static inline bool pm_resume_via_firmware(void) { return false; } |
340 | static inline bool pm_suspend_no_platform(void) { return false; } | ||
339 | static inline bool pm_suspend_default_s2idle(void) { return false; } | 341 | static inline bool pm_suspend_default_s2idle(void) { return false; } |
340 | 342 | ||
341 | static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} | 343 | static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} |
diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index d6961d3c6f9e..8f557fa1f4fe 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c | |||
@@ -177,6 +177,26 @@ static void resume_irqs(bool want_early) | |||
177 | } | 177 | } |
178 | 178 | ||
179 | /** | 179 | /** |
180 | * rearm_wake_irq - rearm a wakeup interrupt line after signaling wakeup | ||
181 | * @irq: Interrupt to rearm | ||
182 | */ | ||
183 | void rearm_wake_irq(unsigned int irq) | ||
184 | { | ||
185 | unsigned long flags; | ||
186 | struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); | ||
187 | |||
188 | if (!desc || !(desc->istate & IRQS_SUSPENDED) || | ||
189 | !irqd_is_wakeup_set(&desc->irq_data)) | ||
190 | return; | ||
191 | |||
192 | desc->istate &= ~IRQS_SUSPENDED; | ||
193 | irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED); | ||
194 | __enable_irq(desc); | ||
195 | |||
196 | irq_put_desc_busunlock(desc, flags); | ||
197 | } | ||
198 | |||
199 | /** | ||
180 | * irq_pm_syscore_ops - enable interrupt lines early | 200 | * irq_pm_syscore_ops - enable interrupt lines early |
181 | * | 201 | * |
182 | * Enable all interrupt lines with %IRQF_EARLY_RESUME set. | 202 | * Enable all interrupt lines with %IRQF_EARLY_RESUME set. |
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index c874a7026e24..f3b7239f1892 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c | |||
@@ -121,43 +121,25 @@ static void s2idle_loop(void) | |||
121 | { | 121 | { |
122 | pm_pr_dbg("suspend-to-idle\n"); | 122 | pm_pr_dbg("suspend-to-idle\n"); |
123 | 123 | ||
124 | /* | ||
125 | * Suspend-to-idle equals: | ||
126 | * frozen processes + suspended devices + idle processors. | ||
127 | * Thus s2idle_enter() should be called right after all devices have | ||
128 | * been suspended. | ||
129 | * | ||
130 | * Wakeups during the noirq suspend of devices may be spurious, so try | ||
131 | * to avoid them upfront. | ||
132 | */ | ||
124 | for (;;) { | 133 | for (;;) { |
125 | int error; | 134 | if (s2idle_ops && s2idle_ops->wake) |
126 | |||
127 | dpm_noirq_begin(); | ||
128 | |||
129 | /* | ||
130 | * Suspend-to-idle equals | ||
131 | * frozen processes + suspended devices + idle processors. | ||
132 | * Thus s2idle_enter() should be called right after | ||
133 | * all devices have been suspended. | ||
134 | * | ||
135 | * Wakeups during the noirq suspend of devices may be spurious, | ||
136 | * so prevent them from terminating the loop right away. | ||
137 | */ | ||
138 | error = dpm_noirq_suspend_devices(PMSG_SUSPEND); | ||
139 | if (!error) | ||
140 | s2idle_enter(); | ||
141 | else if (error == -EBUSY && pm_wakeup_pending()) | ||
142 | error = 0; | ||
143 | |||
144 | if (!error && s2idle_ops && s2idle_ops->wake) | ||
145 | s2idle_ops->wake(); | 135 | s2idle_ops->wake(); |
146 | 136 | ||
147 | dpm_noirq_resume_devices(PMSG_RESUME); | ||
148 | |||
149 | dpm_noirq_end(); | ||
150 | |||
151 | if (error) | ||
152 | break; | ||
153 | |||
154 | if (s2idle_ops && s2idle_ops->sync) | ||
155 | s2idle_ops->sync(); | ||
156 | |||
157 | if (pm_wakeup_pending()) | 137 | if (pm_wakeup_pending()) |
158 | break; | 138 | break; |
159 | 139 | ||
160 | pm_wakeup_clear(false); | 140 | pm_wakeup_clear(false); |
141 | |||
142 | s2idle_enter(); | ||
161 | } | 143 | } |
162 | 144 | ||
163 | pm_pr_dbg("resume from suspend-to-idle\n"); | 145 | pm_pr_dbg("resume from suspend-to-idle\n"); |
@@ -271,14 +253,21 @@ static int platform_suspend_prepare_late(suspend_state_t state) | |||
271 | 253 | ||
272 | static int platform_suspend_prepare_noirq(suspend_state_t state) | 254 | static int platform_suspend_prepare_noirq(suspend_state_t state) |
273 | { | 255 | { |
274 | return state != PM_SUSPEND_TO_IDLE && suspend_ops->prepare_late ? | 256 | if (state == PM_SUSPEND_TO_IDLE) |
275 | suspend_ops->prepare_late() : 0; | 257 | return s2idle_ops && s2idle_ops->prepare_late ? |
258 | s2idle_ops->prepare_late() : 0; | ||
259 | |||
260 | return suspend_ops->prepare_late ? suspend_ops->prepare_late() : 0; | ||
276 | } | 261 | } |
277 | 262 | ||
278 | static void platform_resume_noirq(suspend_state_t state) | 263 | static void platform_resume_noirq(suspend_state_t state) |
279 | { | 264 | { |
280 | if (state != PM_SUSPEND_TO_IDLE && suspend_ops->wake) | 265 | if (state == PM_SUSPEND_TO_IDLE) { |
266 | if (s2idle_ops && s2idle_ops->restore_early) | ||
267 | s2idle_ops->restore_early(); | ||
268 | } else if (suspend_ops->wake) { | ||
281 | suspend_ops->wake(); | 269 | suspend_ops->wake(); |
270 | } | ||
282 | } | 271 | } |
283 | 272 | ||
284 | static void platform_resume_early(suspend_state_t state) | 273 | static void platform_resume_early(suspend_state_t state) |
@@ -415,11 +404,6 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) | |||
415 | if (error) | 404 | if (error) |
416 | goto Devices_early_resume; | 405 | goto Devices_early_resume; |
417 | 406 | ||
418 | if (state == PM_SUSPEND_TO_IDLE && pm_test_level != TEST_PLATFORM) { | ||
419 | s2idle_loop(); | ||
420 | goto Platform_early_resume; | ||
421 | } | ||
422 | |||
423 | error = dpm_suspend_noirq(PMSG_SUSPEND); | 407 | error = dpm_suspend_noirq(PMSG_SUSPEND); |
424 | if (error) { | 408 | if (error) { |
425 | pr_err("noirq suspend of devices failed\n"); | 409 | pr_err("noirq suspend of devices failed\n"); |
@@ -432,6 +416,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) | |||
432 | if (suspend_test(TEST_PLATFORM)) | 416 | if (suspend_test(TEST_PLATFORM)) |
433 | goto Platform_wake; | 417 | goto Platform_wake; |
434 | 418 | ||
419 | if (state == PM_SUSPEND_TO_IDLE) { | ||
420 | s2idle_loop(); | ||
421 | goto Platform_wake; | ||
422 | } | ||
423 | |||
435 | error = suspend_disable_secondary_cpus(); | 424 | error = suspend_disable_secondary_cpus(); |
436 | if (error || suspend_test(TEST_CPUS)) | 425 | if (error || suspend_test(TEST_CPUS)) |
437 | goto Enable_cpus; | 426 | goto Enable_cpus; |