diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2008-01-07 18:07:39 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-02-01 18:30:58 -0500 |
commit | 60417f5976df029227450b46d7fa6f0e9b1e654c (patch) | |
tree | b0faa81f4517aa41c6113824b59a47b87b4aea2f | |
parent | 3c1d2b6085d75df0691cec6a4a053c0aa55fe4c9 (diff) |
ACPI suspend: Call _PTS before suspending devices
The ACPI 1.0 specification wants us to put devices into low power
states after executing the _PTS global control method, while ACPI
2.0 and later want us to do that in the reverse order. The current
suspend code follows ACPI 2.0 in that respect which causes some
ACPI 1.0x systems to hang during suspend (ref.
http://bugzilla.kernel.org/show_bug.cgi?id=9528).
Make the suspend code execute _PTS before putting devices into low
power states (ie. in accordance with ACPI 1.0x) and provide a command
line option to override the default if need be.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | Documentation/kernel-parameters.txt | 5 | ||||
-rw-r--r-- | drivers/acpi/sleep/main.c | 51 |
2 files changed, 43 insertions, 13 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 92c40d174355..cf3868956f1e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
@@ -168,6 +168,11 @@ and is between 256 and 4096 characters. It is defined in the file | |||
168 | acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA | 168 | acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA |
169 | Format: <irq>,<irq>... | 169 | Format: <irq>,<irq>... |
170 | 170 | ||
171 | acpi_new_pts_ordering [HW,ACPI] | ||
172 | Enforce the ACPI 2.0 ordering of the _PTS control | ||
173 | method wrt putting devices into low power states | ||
174 | default: pre ACPI 2.0 ordering of _PTS | ||
175 | |||
171 | acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT | 176 | acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT |
172 | 177 | ||
173 | acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS | 178 | acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS |
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 198ff8a1529a..c37c4ead95c9 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c | |||
@@ -26,6 +26,21 @@ u8 sleep_states[ACPI_S_STATE_COUNT]; | |||
26 | 26 | ||
27 | #ifdef CONFIG_PM_SLEEP | 27 | #ifdef CONFIG_PM_SLEEP |
28 | static u32 acpi_target_sleep_state = ACPI_STATE_S0; | 28 | static u32 acpi_target_sleep_state = ACPI_STATE_S0; |
29 | static bool acpi_sleep_finish_wake_up; | ||
30 | |||
31 | /* | ||
32 | * ACPI 2.0 and later want us to execute _PTS after suspending devices, so we | ||
33 | * allow the user to request that behavior by using the 'acpi_new_pts_ordering' | ||
34 | * kernel command line option that causes the following variable to be set. | ||
35 | */ | ||
36 | static bool new_pts_ordering; | ||
37 | |||
38 | static int __init acpi_new_pts_ordering(char *str) | ||
39 | { | ||
40 | new_pts_ordering = true; | ||
41 | return 1; | ||
42 | } | ||
43 | __setup("acpi_new_pts_ordering", acpi_new_pts_ordering); | ||
29 | #endif | 44 | #endif |
30 | 45 | ||
31 | int acpi_sleep_prepare(u32 acpi_state) | 46 | int acpi_sleep_prepare(u32 acpi_state) |
@@ -74,6 +89,14 @@ static int acpi_pm_begin(suspend_state_t pm_state) | |||
74 | 89 | ||
75 | if (sleep_states[acpi_state]) { | 90 | if (sleep_states[acpi_state]) { |
76 | acpi_target_sleep_state = acpi_state; | 91 | acpi_target_sleep_state = acpi_state; |
92 | if (new_pts_ordering) | ||
93 | return 0; | ||
94 | |||
95 | error = acpi_sleep_prepare(acpi_state); | ||
96 | if (error) | ||
97 | acpi_target_sleep_state = ACPI_STATE_S0; | ||
98 | else | ||
99 | acpi_sleep_finish_wake_up = true; | ||
77 | } else { | 100 | } else { |
78 | printk(KERN_ERR "ACPI does not support this state: %d\n", | 101 | printk(KERN_ERR "ACPI does not support this state: %d\n", |
79 | pm_state); | 102 | pm_state); |
@@ -91,15 +114,17 @@ static int acpi_pm_begin(suspend_state_t pm_state) | |||
91 | 114 | ||
92 | static int acpi_pm_prepare(void) | 115 | static int acpi_pm_prepare(void) |
93 | { | 116 | { |
94 | int error; | 117 | if (new_pts_ordering) { |
118 | int error = acpi_sleep_prepare(acpi_target_sleep_state); | ||
95 | 119 | ||
96 | error = acpi_sleep_prepare(acpi_target_sleep_state); | 120 | if (error) { |
97 | if (error) | 121 | acpi_target_sleep_state = ACPI_STATE_S0; |
98 | acpi_target_sleep_state = ACPI_STATE_S0; | 122 | return error; |
99 | else if (!ACPI_SUCCESS(acpi_hw_disable_all_gpes())) | 123 | } |
100 | error = -EFAULT; | 124 | acpi_sleep_finish_wake_up = true; |
125 | } | ||
101 | 126 | ||
102 | return error; | 127 | return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; |
103 | } | 128 | } |
104 | 129 | ||
105 | /** | 130 | /** |
@@ -123,10 +148,8 @@ static int acpi_pm_enter(suspend_state_t pm_state) | |||
123 | if (acpi_state == ACPI_STATE_S3) { | 148 | if (acpi_state == ACPI_STATE_S3) { |
124 | int error = acpi_save_state_mem(); | 149 | int error = acpi_save_state_mem(); |
125 | 150 | ||
126 | if (error) { | 151 | if (error) |
127 | acpi_target_sleep_state = ACPI_STATE_S0; | ||
128 | return error; | 152 | return error; |
129 | } | ||
130 | } | 153 | } |
131 | 154 | ||
132 | local_irq_save(flags); | 155 | local_irq_save(flags); |
@@ -187,6 +210,7 @@ static void acpi_pm_finish(void) | |||
187 | acpi_set_firmware_waking_vector((acpi_physical_address) 0); | 210 | acpi_set_firmware_waking_vector((acpi_physical_address) 0); |
188 | 211 | ||
189 | acpi_target_sleep_state = ACPI_STATE_S0; | 212 | acpi_target_sleep_state = ACPI_STATE_S0; |
213 | acpi_sleep_finish_wake_up = false; | ||
190 | 214 | ||
191 | #ifdef CONFIG_X86 | 215 | #ifdef CONFIG_X86 |
192 | if (init_8259A_after_S1) { | 216 | if (init_8259A_after_S1) { |
@@ -203,10 +227,11 @@ static void acpi_pm_finish(void) | |||
203 | static void acpi_pm_end(void) | 227 | static void acpi_pm_end(void) |
204 | { | 228 | { |
205 | /* | 229 | /* |
206 | * This is necessary in case acpi_pm_finish() is not called during a | 230 | * This is necessary in case acpi_pm_finish() is not called directly |
207 | * failing transition to a sleep state. | 231 | * during a failing transition to a sleep state. |
208 | */ | 232 | */ |
209 | acpi_target_sleep_state = ACPI_STATE_S0; | 233 | if (acpi_sleep_finish_wake_up) |
234 | acpi_pm_finish(); | ||
210 | } | 235 | } |
211 | 236 | ||
212 | static int acpi_pm_state_valid(suspend_state_t pm_state) | 237 | static int acpi_pm_state_valid(suspend_state_t pm_state) |