aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2008-06-12 17:24:06 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2008-06-12 17:25:09 -0400
commitd8f3de0d2412bb91639cfefc5b3c79dbf3812212 (patch)
tree05d0530d06e898b7eeac5c781ee9f00972a7f67a
parent53eb2fbeb9e68e1a9a23945de8450999c46270ce (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.txt6
-rw-r--r--arch/x86/kernel/acpi/sleep.c2
-rw-r--r--drivers/acpi/sleep/main.c276
-rw-r--r--drivers/base/power/main.c2
-rw-r--r--include/linux/acpi.h3
-rw-r--r--include/linux/suspend.h14
-rw-r--r--kernel/power/disk.c28
-rw-r--r--kernel/power/main.c10
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
25u8 sleep_states[ACPI_S_STATE_COUNT]; 25u8 sleep_states[ACPI_S_STATE_COUNT];
26 26
27#ifdef CONFIG_PM_SLEEP
28static u32 acpi_target_sleep_state = ACPI_STATE_S0;
29#endif
30
31static int acpi_sleep_prepare(u32 acpi_state) 27static 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
54static struct platform_suspend_ops acpi_suspend_ops; 50static 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 */
57static bool old_suspend_ordering;
58
59void __init acpi_old_suspend_ordering(void)
60{
61 old_suspend_ordering = true;
62}
63
64/**
65 * acpi_pm_disable_gpes - Disable the GPEs.
66 */
67static 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 */
79static 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 */
92static 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 */
107static 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 */
128static 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
56extern void do_suspend_lowlevel(void); 139extern void do_suspend_lowlevel(void);
57 140
58static u32 acpi_suspend_states[] = { 141static 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
70static int acpi_suspend_begin(suspend_state_t pm_state) 152static 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
92static 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
113static int acpi_suspend_enter(suspend_state_t pm_state) 175static 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
176static 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
193static 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
202static int acpi_suspend_state_valid(suspend_state_t pm_state) 231static 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)
218static struct platform_suspend_ops acpi_suspend_ops = { 247static 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 */
262static 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 */
275static 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 = {
229static int acpi_hibernation_begin(void) 287static 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
236static 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
248static int acpi_hibernation_enter(void) 293static 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
277static void acpi_hibernation_finish(void) 322static 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
288static void acpi_hibernation_end(void) 327static 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
297static 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 */
345static 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
306static 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
311static 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, 358static 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
324int acpi_suspend(u32 acpi_state) 372int 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}
788EXPORT_SYMBOL_GPL(device_suspend); 786EXPORT_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,
234int acpi_check_mem_region(resource_size_t start, resource_size_t n, 234int 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
238void __init acpi_old_suspend_ordering(void);
239#endif /* CONFIG_PM_SLEEP */
237#else /* CONFIG_ACPI */ 240#else /* CONFIG_ACPI */
238 241
239static inline int early_acpi_boot_init(void) 242static 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 */
90struct platform_suspend_ops { 95struct 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 */
193struct platform_hibernation_ops { 204struct 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
187static 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/**