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 | |
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>
-rw-r--r-- | Documentation/kernel-parameters.txt | 6 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/sleep.c | 2 | ||||
-rw-r--r-- | drivers/acpi/sleep/main.c | 276 | ||||
-rw-r--r-- | drivers/base/power/main.c | 2 | ||||
-rw-r--r-- | include/linux/acpi.h | 3 | ||||
-rw-r--r-- | include/linux/suspend.h | 14 | ||||
-rw-r--r-- | kernel/power/disk.c | 28 | ||||
-rw-r--r-- | kernel/power/main.c | 10 |
8 files changed, 215 insertions, 126 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 9cf7b34f2db0..18d793ea0dd3 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
@@ -147,10 +147,14 @@ and is between 256 and 4096 characters. It is defined in the file | |||
147 | default: 0 | 147 | default: 0 |
148 | 148 | ||
149 | acpi_sleep= [HW,ACPI] Sleep options | 149 | acpi_sleep= [HW,ACPI] Sleep options |
150 | Format: { s3_bios, s3_mode, s3_beep } | 150 | Format: { s3_bios, s3_mode, s3_beep, old_ordering } |
151 | See Documentation/power/video.txt for s3_bios and s3_mode. | 151 | See Documentation/power/video.txt for s3_bios and s3_mode. |
152 | s3_beep is for debugging; it makes the PC's speaker beep | 152 | s3_beep is for debugging; it makes the PC's speaker beep |
153 | as soon as the kernel's real-mode entry point is called. | 153 | as soon as the kernel's real-mode entry point is called. |
154 | old_ordering causes the ACPI 1.0 ordering of the _PTS | ||
155 | control method, wrt putting devices into low power | ||
156 | states, to be enforced (the ACPI 2.0 ordering of _PTS is | ||
157 | used by default). | ||
154 | 158 | ||
155 | acpi_sci= [HW,ACPI] ACPI System Control Interrupt trigger mode | 159 | acpi_sci= [HW,ACPI] ACPI System Control Interrupt trigger mode |
156 | Format: { level | edge | high | low } | 160 | Format: { level | edge | high | low } |
diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index afc25ee9964b..882e970032d5 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c | |||
@@ -124,6 +124,8 @@ static int __init acpi_sleep_setup(char *str) | |||
124 | acpi_realmode_flags |= 2; | 124 | acpi_realmode_flags |= 2; |
125 | if (strncmp(str, "s3_beep", 7) == 0) | 125 | if (strncmp(str, "s3_beep", 7) == 0) |
126 | acpi_realmode_flags |= 4; | 126 | acpi_realmode_flags |= 4; |
127 | if (strncmp(str, "old_ordering", 12) == 0) | ||
128 | acpi_old_suspend_ordering(); | ||
127 | str = strchr(str, ','); | 129 | str = strchr(str, ','); |
128 | if (str != NULL) | 130 | if (str != NULL) |
129 | str += strspn(str, ", \t"); | 131 | str += strspn(str, ", \t"); |
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); |
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 41f7ce7edd7a..33adcf91ef41 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
@@ -234,6 +234,9 @@ int acpi_check_region(resource_size_t start, resource_size_t n, | |||
234 | int acpi_check_mem_region(resource_size_t start, resource_size_t n, | 234 | int acpi_check_mem_region(resource_size_t start, resource_size_t n, |
235 | const char *name); | 235 | const char *name); |
236 | 236 | ||
237 | #ifdef CONFIG_PM_SLEEP | ||
238 | void __init acpi_old_suspend_ordering(void); | ||
239 | #endif /* CONFIG_PM_SLEEP */ | ||
237 | #else /* CONFIG_ACPI */ | 240 | #else /* CONFIG_ACPI */ |
238 | 241 | ||
239 | static inline int early_acpi_boot_init(void) | 242 | static inline int early_acpi_boot_init(void) |
diff --git a/include/linux/suspend.h b/include/linux/suspend.h index a6977423baf7..e8e69159af71 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h | |||
@@ -86,6 +86,11 @@ typedef int __bitwise suspend_state_t; | |||
86 | * that implement @begin(), but platforms implementing @begin() should | 86 | * that implement @begin(), but platforms implementing @begin() should |
87 | * also provide a @end() which cleans up transitions aborted before | 87 | * also provide a @end() which cleans up transitions aborted before |
88 | * @enter(). | 88 | * @enter(). |
89 | * | ||
90 | * @recover: Recover the platform from a suspend failure. | ||
91 | * Called by the PM core if the suspending of devices fails. | ||
92 | * This callback is optional and should only be implemented by platforms | ||
93 | * which require special recovery actions in that situation. | ||
89 | */ | 94 | */ |
90 | struct platform_suspend_ops { | 95 | struct platform_suspend_ops { |
91 | int (*valid)(suspend_state_t state); | 96 | int (*valid)(suspend_state_t state); |
@@ -94,6 +99,7 @@ struct platform_suspend_ops { | |||
94 | int (*enter)(suspend_state_t state); | 99 | int (*enter)(suspend_state_t state); |
95 | void (*finish)(void); | 100 | void (*finish)(void); |
96 | void (*end)(void); | 101 | void (*end)(void); |
102 | void (*recover)(void); | ||
97 | }; | 103 | }; |
98 | 104 | ||
99 | #ifdef CONFIG_SUSPEND | 105 | #ifdef CONFIG_SUSPEND |
@@ -149,7 +155,7 @@ extern void mark_free_pages(struct zone *zone); | |||
149 | * The methods in this structure allow a platform to carry out special | 155 | * The methods in this structure allow a platform to carry out special |
150 | * operations required by it during a hibernation transition. | 156 | * operations required by it during a hibernation transition. |
151 | * | 157 | * |
152 | * All the methods below must be implemented. | 158 | * All the methods below, except for @recover(), must be implemented. |
153 | * | 159 | * |
154 | * @begin: Tell the platform driver that we're starting hibernation. | 160 | * @begin: Tell the platform driver that we're starting hibernation. |
155 | * Called right after shrinking memory and before freezing devices. | 161 | * Called right after shrinking memory and before freezing devices. |
@@ -189,6 +195,11 @@ extern void mark_free_pages(struct zone *zone); | |||
189 | * @restore_cleanup: Clean up after a failing image restoration. | 195 | * @restore_cleanup: Clean up after a failing image restoration. |
190 | * Called right after the nonboot CPUs have been enabled and before | 196 | * Called right after the nonboot CPUs have been enabled and before |
191 | * thawing devices (runs with IRQs on). | 197 | * thawing devices (runs with IRQs on). |
198 | * | ||
199 | * @recover: Recover the platform from a failure to suspend devices. | ||
200 | * Called by the PM core if the suspending of devices during hibernation | ||
201 | * fails. This callback is optional and should only be implemented by | ||
202 | * platforms which require special recovery actions in that situation. | ||
192 | */ | 203 | */ |
193 | struct platform_hibernation_ops { | 204 | struct platform_hibernation_ops { |
194 | int (*begin)(void); | 205 | int (*begin)(void); |
@@ -200,6 +211,7 @@ struct platform_hibernation_ops { | |||
200 | void (*leave)(void); | 211 | void (*leave)(void); |
201 | int (*pre_restore)(void); | 212 | int (*pre_restore)(void); |
202 | void (*restore_cleanup)(void); | 213 | void (*restore_cleanup)(void); |
214 | void (*recover)(void); | ||
203 | }; | 215 | }; |
204 | 216 | ||
205 | #ifdef CONFIG_HIBERNATION | 217 | #ifdef CONFIG_HIBERNATION |
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index d416be0efa8a..f011e0870b52 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -180,6 +180,17 @@ static void platform_restore_cleanup(int platform_mode) | |||
180 | } | 180 | } |
181 | 181 | ||
182 | /** | 182 | /** |
183 | * platform_recover - recover the platform from a failure to suspend | ||
184 | * devices. | ||
185 | */ | ||
186 | |||
187 | static void platform_recover(int platform_mode) | ||
188 | { | ||
189 | if (platform_mode && hibernation_ops && hibernation_ops->recover) | ||
190 | hibernation_ops->recover(); | ||
191 | } | ||
192 | |||
193 | /** | ||
183 | * create_image - freeze devices that need to be frozen with interrupts | 194 | * create_image - freeze devices that need to be frozen with interrupts |
184 | * off, create the hibernation image and thaw those devices. Control | 195 | * off, create the hibernation image and thaw those devices. Control |
185 | * reappears in this routine after a restore. | 196 | * reappears in this routine after a restore. |
@@ -258,10 +269,10 @@ int hibernation_snapshot(int platform_mode) | |||
258 | suspend_console(); | 269 | suspend_console(); |
259 | error = device_suspend(PMSG_FREEZE); | 270 | error = device_suspend(PMSG_FREEZE); |
260 | if (error) | 271 | if (error) |
261 | goto Resume_console; | 272 | goto Recover_platform; |
262 | 273 | ||
263 | if (hibernation_test(TEST_DEVICES)) | 274 | if (hibernation_test(TEST_DEVICES)) |
264 | goto Resume_devices; | 275 | goto Recover_platform; |
265 | 276 | ||
266 | error = platform_pre_snapshot(platform_mode); | 277 | error = platform_pre_snapshot(platform_mode); |
267 | if (error || hibernation_test(TEST_PLATFORM)) | 278 | if (error || hibernation_test(TEST_PLATFORM)) |
@@ -285,11 +296,14 @@ int hibernation_snapshot(int platform_mode) | |||
285 | Resume_devices: | 296 | Resume_devices: |
286 | device_resume(in_suspend ? | 297 | device_resume(in_suspend ? |
287 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); | 298 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); |
288 | Resume_console: | ||
289 | resume_console(); | 299 | resume_console(); |
290 | Close: | 300 | Close: |
291 | platform_end(platform_mode); | 301 | platform_end(platform_mode); |
292 | return error; | 302 | return error; |
303 | |||
304 | Recover_platform: | ||
305 | platform_recover(platform_mode); | ||
306 | goto Resume_devices; | ||
293 | } | 307 | } |
294 | 308 | ||
295 | /** | 309 | /** |
@@ -398,8 +412,11 @@ int hibernation_platform_enter(void) | |||
398 | 412 | ||
399 | suspend_console(); | 413 | suspend_console(); |
400 | error = device_suspend(PMSG_HIBERNATE); | 414 | error = device_suspend(PMSG_HIBERNATE); |
401 | if (error) | 415 | if (error) { |
402 | goto Resume_console; | 416 | if (hibernation_ops->recover) |
417 | hibernation_ops->recover(); | ||
418 | goto Resume_devices; | ||
419 | } | ||
403 | 420 | ||
404 | error = hibernation_ops->prepare(); | 421 | error = hibernation_ops->prepare(); |
405 | if (error) | 422 | if (error) |
@@ -428,7 +445,6 @@ int hibernation_platform_enter(void) | |||
428 | hibernation_ops->finish(); | 445 | hibernation_ops->finish(); |
429 | Resume_devices: | 446 | Resume_devices: |
430 | device_resume(PMSG_RESTORE); | 447 | device_resume(PMSG_RESTORE); |
431 | Resume_console: | ||
432 | resume_console(); | 448 | resume_console(); |
433 | Close: | 449 | Close: |
434 | hibernation_ops->end(); | 450 | hibernation_ops->end(); |
diff --git a/kernel/power/main.c b/kernel/power/main.c index d023b6b584e5..3398f4651aa1 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c | |||
@@ -269,11 +269,11 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
269 | error = device_suspend(PMSG_SUSPEND); | 269 | error = device_suspend(PMSG_SUSPEND); |
270 | if (error) { | 270 | if (error) { |
271 | printk(KERN_ERR "PM: Some devices failed to suspend\n"); | 271 | printk(KERN_ERR "PM: Some devices failed to suspend\n"); |
272 | goto Resume_console; | 272 | goto Recover_platform; |
273 | } | 273 | } |
274 | 274 | ||
275 | if (suspend_test(TEST_DEVICES)) | 275 | if (suspend_test(TEST_DEVICES)) |
276 | goto Resume_devices; | 276 | goto Recover_platform; |
277 | 277 | ||
278 | if (suspend_ops->prepare) { | 278 | if (suspend_ops->prepare) { |
279 | error = suspend_ops->prepare(); | 279 | error = suspend_ops->prepare(); |
@@ -294,12 +294,16 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
294 | suspend_ops->finish(); | 294 | suspend_ops->finish(); |
295 | Resume_devices: | 295 | Resume_devices: |
296 | device_resume(PMSG_RESUME); | 296 | device_resume(PMSG_RESUME); |
297 | Resume_console: | ||
298 | resume_console(); | 297 | resume_console(); |
299 | Close: | 298 | Close: |
300 | if (suspend_ops->end) | 299 | if (suspend_ops->end) |
301 | suspend_ops->end(); | 300 | suspend_ops->end(); |
302 | return error; | 301 | return error; |
302 | |||
303 | Recover_platform: | ||
304 | if (suspend_ops->recover) | ||
305 | suspend_ops->recover(); | ||
306 | goto Resume_devices; | ||
303 | } | 307 | } |
304 | 308 | ||
305 | /** | 309 | /** |