diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2008-06-12 17:24:06 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-06-12 17:25:09 -0400 |
commit | d8f3de0d2412bb91639cfefc5b3c79dbf3812212 (patch) | |
tree | 05d0530d06e898b7eeac5c781ee9f00972a7f67a /drivers | |
parent | 53eb2fbeb9e68e1a9a23945de8450999c46270ce (diff) |
Suspend-related patches for 2.6.27
ACPI PM: Add possibility to change suspend sequence
There are some systems out there that don't work correctly with
our current suspend/hibernation code ordering. Provide a workaround
for these systems allowing them to pass 'acpi_sleep=old_ordering' in
the kernel command line so that it will use the pre-ACPI 2.0 ("old")
suspend code ordering.
Unfortunately, this requires us to add a platform hook to the
resuming of devices for recovering the platform in case one of the
device drivers' .suspend() routines returns error code. Namely,
ACPI 1.0 specifies that _PTS should be called before suspending
devices, but _WAK still should be called before resuming them in
order to undo the changes made by _PTS. However, if there is an
error during suspending devices, they are automatically resumed
without returning control to the PM core, so the _WAK has to be
called from within device_resume() in that cases.
The patch also reorders and refactors the ACPI suspend/hibernation
code to avoid duplication as far as reasonably possible.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@suse.cz>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/sleep/main.c | 276 | ||||
-rw-r--r-- | drivers/base/power/main.c | 2 |
2 files changed, 163 insertions, 115 deletions
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 0f2caea2fc83..4addf8ad50ae 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c | |||
@@ -24,10 +24,6 @@ | |||
24 | 24 | ||
25 | u8 sleep_states[ACPI_S_STATE_COUNT]; | 25 | u8 sleep_states[ACPI_S_STATE_COUNT]; |
26 | 26 | ||
27 | #ifdef CONFIG_PM_SLEEP | ||
28 | static u32 acpi_target_sleep_state = ACPI_STATE_S0; | ||
29 | #endif | ||
30 | |||
31 | static int acpi_sleep_prepare(u32 acpi_state) | 27 | static int acpi_sleep_prepare(u32 acpi_state) |
32 | { | 28 | { |
33 | #ifdef CONFIG_ACPI_SLEEP | 29 | #ifdef CONFIG_ACPI_SLEEP |
@@ -50,9 +46,96 @@ static int acpi_sleep_prepare(u32 acpi_state) | |||
50 | return 0; | 46 | return 0; |
51 | } | 47 | } |
52 | 48 | ||
53 | #ifdef CONFIG_SUSPEND | 49 | #ifdef CONFIG_PM_SLEEP |
54 | static struct platform_suspend_ops acpi_suspend_ops; | 50 | static u32 acpi_target_sleep_state = ACPI_STATE_S0; |
51 | |||
52 | /* | ||
53 | * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the | ||
54 | * user to request that behavior by using the 'acpi_old_suspend_ordering' | ||
55 | * kernel command line option that causes the following variable to be set. | ||
56 | */ | ||
57 | static bool old_suspend_ordering; | ||
58 | |||
59 | void __init acpi_old_suspend_ordering(void) | ||
60 | { | ||
61 | old_suspend_ordering = true; | ||
62 | } | ||
63 | |||
64 | /** | ||
65 | * acpi_pm_disable_gpes - Disable the GPEs. | ||
66 | */ | ||
67 | static int acpi_pm_disable_gpes(void) | ||
68 | { | ||
69 | acpi_hw_disable_all_gpes(); | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * __acpi_pm_prepare - Prepare the platform to enter the target state. | ||
75 | * | ||
76 | * If necessary, set the firmware waking vector and do arch-specific | ||
77 | * nastiness to get the wakeup code to the waking vector. | ||
78 | */ | ||
79 | static int __acpi_pm_prepare(void) | ||
80 | { | ||
81 | int error = acpi_sleep_prepare(acpi_target_sleep_state); | ||
82 | |||
83 | if (error) | ||
84 | acpi_target_sleep_state = ACPI_STATE_S0; | ||
85 | return error; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * acpi_pm_prepare - Prepare the platform to enter the target sleep | ||
90 | * state and disable the GPEs. | ||
91 | */ | ||
92 | static int acpi_pm_prepare(void) | ||
93 | { | ||
94 | int error = __acpi_pm_prepare(); | ||
95 | |||
96 | if (!error) | ||
97 | acpi_hw_disable_all_gpes(); | ||
98 | return error; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * acpi_pm_finish - Instruct the platform to leave a sleep state. | ||
103 | * | ||
104 | * This is called after we wake back up (or if entering the sleep state | ||
105 | * failed). | ||
106 | */ | ||
107 | static void acpi_pm_finish(void) | ||
108 | { | ||
109 | u32 acpi_state = acpi_target_sleep_state; | ||
110 | |||
111 | if (acpi_state == ACPI_STATE_S0) | ||
112 | return; | ||
55 | 113 | ||
114 | printk(KERN_INFO PREFIX "Waking up from system sleep state S%d\n", | ||
115 | acpi_state); | ||
116 | acpi_disable_wakeup_device(acpi_state); | ||
117 | acpi_leave_sleep_state(acpi_state); | ||
118 | |||
119 | /* reset firmware waking vector */ | ||
120 | acpi_set_firmware_waking_vector((acpi_physical_address) 0); | ||
121 | |||
122 | acpi_target_sleep_state = ACPI_STATE_S0; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * acpi_pm_end - Finish up suspend sequence. | ||
127 | */ | ||
128 | static void acpi_pm_end(void) | ||
129 | { | ||
130 | /* | ||
131 | * This is necessary in case acpi_pm_finish() is not called during a | ||
132 | * failing transition to a sleep state. | ||
133 | */ | ||
134 | acpi_target_sleep_state = ACPI_STATE_S0; | ||
135 | } | ||
136 | #endif /* CONFIG_PM_SLEEP */ | ||
137 | |||
138 | #ifdef CONFIG_SUSPEND | ||
56 | extern void do_suspend_lowlevel(void); | 139 | extern void do_suspend_lowlevel(void); |
57 | 140 | ||
58 | static u32 acpi_suspend_states[] = { | 141 | static u32 acpi_suspend_states[] = { |
@@ -66,7 +149,6 @@ static u32 acpi_suspend_states[] = { | |||
66 | * acpi_suspend_begin - Set the target system sleep state to the state | 149 | * acpi_suspend_begin - Set the target system sleep state to the state |
67 | * associated with given @pm_state, if supported. | 150 | * associated with given @pm_state, if supported. |
68 | */ | 151 | */ |
69 | |||
70 | static int acpi_suspend_begin(suspend_state_t pm_state) | 152 | static int acpi_suspend_begin(suspend_state_t pm_state) |
71 | { | 153 | { |
72 | u32 acpi_state = acpi_suspend_states[pm_state]; | 154 | u32 acpi_state = acpi_suspend_states[pm_state]; |
@@ -83,25 +165,6 @@ static int acpi_suspend_begin(suspend_state_t pm_state) | |||
83 | } | 165 | } |
84 | 166 | ||
85 | /** | 167 | /** |
86 | * acpi_suspend_prepare - Do preliminary suspend work. | ||
87 | * | ||
88 | * If necessary, set the firmware waking vector and do arch-specific | ||
89 | * nastiness to get the wakeup code to the waking vector. | ||
90 | */ | ||
91 | |||
92 | static int acpi_suspend_prepare(void) | ||
93 | { | ||
94 | int error = acpi_sleep_prepare(acpi_target_sleep_state); | ||
95 | |||
96 | if (error) { | ||
97 | acpi_target_sleep_state = ACPI_STATE_S0; | ||
98 | return error; | ||
99 | } | ||
100 | |||
101 | return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * acpi_suspend_enter - Actually enter a sleep state. | 168 | * acpi_suspend_enter - Actually enter a sleep state. |
106 | * @pm_state: ignored | 169 | * @pm_state: ignored |
107 | * | 170 | * |
@@ -109,7 +172,6 @@ static int acpi_suspend_prepare(void) | |||
109 | * assembly, which in turn call acpi_enter_sleep_state(). | 172 | * assembly, which in turn call acpi_enter_sleep_state(). |
110 | * It's unfortunate, but it works. Please fix if you're feeling frisky. | 173 | * It's unfortunate, but it works. Please fix if you're feeling frisky. |
111 | */ | 174 | */ |
112 | |||
113 | static int acpi_suspend_enter(suspend_state_t pm_state) | 175 | static int acpi_suspend_enter(suspend_state_t pm_state) |
114 | { | 176 | { |
115 | acpi_status status = AE_OK; | 177 | acpi_status status = AE_OK; |
@@ -166,39 +228,6 @@ static int acpi_suspend_enter(suspend_state_t pm_state) | |||
166 | return ACPI_SUCCESS(status) ? 0 : -EFAULT; | 228 | return ACPI_SUCCESS(status) ? 0 : -EFAULT; |
167 | } | 229 | } |
168 | 230 | ||
169 | /** | ||
170 | * acpi_suspend_finish - Instruct the platform to leave a sleep state. | ||
171 | * | ||
172 | * This is called after we wake back up (or if entering the sleep state | ||
173 | * failed). | ||
174 | */ | ||
175 | |||
176 | static void acpi_suspend_finish(void) | ||
177 | { | ||
178 | u32 acpi_state = acpi_target_sleep_state; | ||
179 | |||
180 | acpi_disable_wakeup_device(acpi_state); | ||
181 | acpi_leave_sleep_state(acpi_state); | ||
182 | |||
183 | /* reset firmware waking vector */ | ||
184 | acpi_set_firmware_waking_vector((acpi_physical_address) 0); | ||
185 | |||
186 | acpi_target_sleep_state = ACPI_STATE_S0; | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | * acpi_suspend_end - Finish up suspend sequence. | ||
191 | */ | ||
192 | |||
193 | static void acpi_suspend_end(void) | ||
194 | { | ||
195 | /* | ||
196 | * This is necessary in case acpi_suspend_finish() is not called during a | ||
197 | * failing transition to a sleep state. | ||
198 | */ | ||
199 | acpi_target_sleep_state = ACPI_STATE_S0; | ||
200 | } | ||
201 | |||
202 | static int acpi_suspend_state_valid(suspend_state_t pm_state) | 231 | static int acpi_suspend_state_valid(suspend_state_t pm_state) |
203 | { | 232 | { |
204 | u32 acpi_state; | 233 | u32 acpi_state; |
@@ -218,10 +247,39 @@ static int acpi_suspend_state_valid(suspend_state_t pm_state) | |||
218 | static struct platform_suspend_ops acpi_suspend_ops = { | 247 | static struct platform_suspend_ops acpi_suspend_ops = { |
219 | .valid = acpi_suspend_state_valid, | 248 | .valid = acpi_suspend_state_valid, |
220 | .begin = acpi_suspend_begin, | 249 | .begin = acpi_suspend_begin, |
221 | .prepare = acpi_suspend_prepare, | 250 | .prepare = acpi_pm_prepare, |
251 | .enter = acpi_suspend_enter, | ||
252 | .finish = acpi_pm_finish, | ||
253 | .end = acpi_pm_end, | ||
254 | }; | ||
255 | |||
256 | /** | ||
257 | * acpi_suspend_begin_old - Set the target system sleep state to the | ||
258 | * state associated with given @pm_state, if supported, and | ||
259 | * execute the _PTS control method. This function is used if the | ||
260 | * pre-ACPI 2.0 suspend ordering has been requested. | ||
261 | */ | ||
262 | static int acpi_suspend_begin_old(suspend_state_t pm_state) | ||
263 | { | ||
264 | int error = acpi_suspend_begin(pm_state); | ||
265 | |||
266 | if (!error) | ||
267 | error = __acpi_pm_prepare(); | ||
268 | return error; | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has | ||
273 | * been requested. | ||
274 | */ | ||
275 | static struct platform_suspend_ops acpi_suspend_ops_old = { | ||
276 | .valid = acpi_suspend_state_valid, | ||
277 | .begin = acpi_suspend_begin_old, | ||
278 | .prepare = acpi_pm_disable_gpes, | ||
222 | .enter = acpi_suspend_enter, | 279 | .enter = acpi_suspend_enter, |
223 | .finish = acpi_suspend_finish, | 280 | .finish = acpi_pm_finish, |
224 | .end = acpi_suspend_end, | 281 | .end = acpi_pm_end, |
282 | .recover = acpi_pm_finish, | ||
225 | }; | 283 | }; |
226 | #endif /* CONFIG_SUSPEND */ | 284 | #endif /* CONFIG_SUSPEND */ |
227 | 285 | ||
@@ -229,22 +287,9 @@ static struct platform_suspend_ops acpi_suspend_ops = { | |||
229 | static int acpi_hibernation_begin(void) | 287 | static int acpi_hibernation_begin(void) |
230 | { | 288 | { |
231 | acpi_target_sleep_state = ACPI_STATE_S4; | 289 | acpi_target_sleep_state = ACPI_STATE_S4; |
232 | |||
233 | return 0; | 290 | return 0; |
234 | } | 291 | } |
235 | 292 | ||
236 | static int acpi_hibernation_prepare(void) | ||
237 | { | ||
238 | int error = acpi_sleep_prepare(ACPI_STATE_S4); | ||
239 | |||
240 | if (error) { | ||
241 | acpi_target_sleep_state = ACPI_STATE_S0; | ||
242 | return error; | ||
243 | } | ||
244 | |||
245 | return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; | ||
246 | } | ||
247 | |||
248 | static int acpi_hibernation_enter(void) | 293 | static int acpi_hibernation_enter(void) |
249 | { | 294 | { |
250 | acpi_status status = AE_OK; | 295 | acpi_status status = AE_OK; |
@@ -274,52 +319,55 @@ static void acpi_hibernation_leave(void) | |||
274 | acpi_leave_sleep_state_prep(ACPI_STATE_S4); | 319 | acpi_leave_sleep_state_prep(ACPI_STATE_S4); |
275 | } | 320 | } |
276 | 321 | ||
277 | static void acpi_hibernation_finish(void) | 322 | static void acpi_pm_enable_gpes(void) |
278 | { | 323 | { |
279 | acpi_disable_wakeup_device(ACPI_STATE_S4); | 324 | acpi_hw_enable_all_runtime_gpes(); |
280 | acpi_leave_sleep_state(ACPI_STATE_S4); | ||
281 | |||
282 | /* reset firmware waking vector */ | ||
283 | acpi_set_firmware_waking_vector((acpi_physical_address) 0); | ||
284 | |||
285 | acpi_target_sleep_state = ACPI_STATE_S0; | ||
286 | } | 325 | } |
287 | 326 | ||
288 | static void acpi_hibernation_end(void) | 327 | static struct platform_hibernation_ops acpi_hibernation_ops = { |
289 | { | 328 | .begin = acpi_hibernation_begin, |
290 | /* | 329 | .end = acpi_pm_end, |
291 | * This is necessary in case acpi_hibernation_finish() is not called | 330 | .pre_snapshot = acpi_pm_prepare, |
292 | * during a failing transition to the sleep state. | 331 | .finish = acpi_pm_finish, |
293 | */ | 332 | .prepare = acpi_pm_prepare, |
294 | acpi_target_sleep_state = ACPI_STATE_S0; | 333 | .enter = acpi_hibernation_enter, |
295 | } | 334 | .leave = acpi_hibernation_leave, |
335 | .pre_restore = acpi_pm_disable_gpes, | ||
336 | .restore_cleanup = acpi_pm_enable_gpes, | ||
337 | }; | ||
296 | 338 | ||
297 | static int acpi_hibernation_pre_restore(void) | 339 | /** |
340 | * acpi_hibernation_begin_old - Set the target system sleep state to | ||
341 | * ACPI_STATE_S4 and execute the _PTS control method. This | ||
342 | * function is used if the pre-ACPI 2.0 suspend ordering has been | ||
343 | * requested. | ||
344 | */ | ||
345 | static int acpi_hibernation_begin_old(void) | ||
298 | { | 346 | { |
299 | acpi_status status; | 347 | int error = acpi_sleep_prepare(ACPI_STATE_S4); |
300 | |||
301 | status = acpi_hw_disable_all_gpes(); | ||
302 | |||
303 | return ACPI_SUCCESS(status) ? 0 : -EFAULT; | ||
304 | } | ||
305 | 348 | ||
306 | static void acpi_hibernation_restore_cleanup(void) | 349 | if (!error) |
307 | { | 350 | acpi_target_sleep_state = ACPI_STATE_S4; |
308 | acpi_hw_enable_all_runtime_gpes(); | 351 | return error; |
309 | } | 352 | } |
310 | 353 | ||
311 | static struct platform_hibernation_ops acpi_hibernation_ops = { | 354 | /* |
312 | .begin = acpi_hibernation_begin, | 355 | * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has |
313 | .end = acpi_hibernation_end, | 356 | * been requested. |
314 | .pre_snapshot = acpi_hibernation_prepare, | 357 | */ |
315 | .finish = acpi_hibernation_finish, | 358 | static struct platform_hibernation_ops acpi_hibernation_ops_old = { |
316 | .prepare = acpi_hibernation_prepare, | 359 | .begin = acpi_hibernation_begin_old, |
360 | .end = acpi_pm_end, | ||
361 | .pre_snapshot = acpi_pm_disable_gpes, | ||
362 | .finish = acpi_pm_finish, | ||
363 | .prepare = acpi_pm_disable_gpes, | ||
317 | .enter = acpi_hibernation_enter, | 364 | .enter = acpi_hibernation_enter, |
318 | .leave = acpi_hibernation_leave, | 365 | .leave = acpi_hibernation_leave, |
319 | .pre_restore = acpi_hibernation_pre_restore, | 366 | .pre_restore = acpi_pm_disable_gpes, |
320 | .restore_cleanup = acpi_hibernation_restore_cleanup, | 367 | .restore_cleanup = acpi_pm_enable_gpes, |
368 | .recover = acpi_pm_finish, | ||
321 | }; | 369 | }; |
322 | #endif /* CONFIG_HIBERNATION */ | 370 | #endif /* CONFIG_HIBERNATION */ |
323 | 371 | ||
324 | int acpi_suspend(u32 acpi_state) | 372 | int acpi_suspend(u32 acpi_state) |
325 | { | 373 | { |
@@ -461,13 +509,15 @@ int __init acpi_sleep_init(void) | |||
461 | } | 509 | } |
462 | } | 510 | } |
463 | 511 | ||
464 | suspend_set_ops(&acpi_suspend_ops); | 512 | suspend_set_ops(old_suspend_ordering ? |
513 | &acpi_suspend_ops_old : &acpi_suspend_ops); | ||
465 | #endif | 514 | #endif |
466 | 515 | ||
467 | #ifdef CONFIG_HIBERNATION | 516 | #ifdef CONFIG_HIBERNATION |
468 | status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b); | 517 | status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b); |
469 | if (ACPI_SUCCESS(status)) { | 518 | if (ACPI_SUCCESS(status)) { |
470 | hibernation_set_ops(&acpi_hibernation_ops); | 519 | hibernation_set_ops(old_suspend_ordering ? |
520 | &acpi_hibernation_ops_old : &acpi_hibernation_ops); | ||
471 | sleep_states[ACPI_STATE_S4] = 1; | 521 | sleep_states[ACPI_STATE_S4] = 1; |
472 | printk(" S4"); | 522 | printk(" S4"); |
473 | } | 523 | } |
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index d571204aaff7..3250c5257b74 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -781,8 +781,6 @@ int device_suspend(pm_message_t state) | |||
781 | error = dpm_prepare(state); | 781 | error = dpm_prepare(state); |
782 | if (!error) | 782 | if (!error) |
783 | error = dpm_suspend(state); | 783 | error = dpm_suspend(state); |
784 | if (error) | ||
785 | device_resume(resume_event(state)); | ||
786 | return error; | 784 | return error; |
787 | } | 785 | } |
788 | EXPORT_SYMBOL_GPL(device_suspend); | 786 | EXPORT_SYMBOL_GPL(device_suspend); |