aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2007-10-18 06:04:56 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-18 17:37:20 -0400
commit9cd9a0058dd35268b24fa16795a92c800f4086d4 (patch)
tree2525a95542eaa16a9aea865e8078cf66f3274e18
parentc7e0831d385d620a58d95b25e4afa9b643f9a411 (diff)
Hibernation: Enter platform hibernation state in a consistent way
Make hibernation_platform_enter() execute the enter-a-sleep-state sequence instead of the mixed shutdown-with-entering-S4 thing. Replace the shutting down of devices done by kernel_shutdown_prepare(), before entering the ACPI S4 sleep state, with suspending them and the shutting down of sysdevs with calling device_power_down(PMSG_SUSPEND) (just like before entering S1 or S3, but the target state is now S4).  Also, disable the nonboot CPUs before entering the sleep state (S4), which generally always is a good idea. This is known to fix the "double disk spin down during hibernation" on some machines, eg. HPC nx6325 (ref. http://lkml.org/lkml/2007/8/7/316 and the following thread).  Moreover, it has been reported to make /sys/class/rtc/rtc0/wakealarm work correctly with hibernation for some users. It also generally causes the hibernation state (ACPI S4) to be entered faster. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Pavel Machek <pavel@ucw.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--kernel/power/disk.c63
1 files changed, 46 insertions, 17 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c
index e50f4da18fd5..8b15f777010a 100644
--- a/kernel/power/disk.c
+++ b/kernel/power/disk.c
@@ -278,21 +278,50 @@ int hibernation_platform_enter(void)
278{ 278{
279 int error; 279 int error;
280 280
281 if (hibernation_ops) { 281 if (!hibernation_ops)
282 kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); 282 return -ENOSYS;
283 /* 283
284 * We have cancelled the power transition by running 284 /*
285 * hibernation_ops->finish() before saving the image, so we 285 * We have cancelled the power transition by running
286 * should let the firmware know that we're going to enter the 286 * hibernation_ops->finish() before saving the image, so we should let
287 * sleep state after all 287 * the firmware know that we're going to enter the sleep state after all
288 */ 288 */
289 error = hibernation_ops->prepare(); 289 error = hibernation_ops->start();
290 sysdev_shutdown(); 290 if (error)
291 if (!error) 291 return error;
292 error = hibernation_ops->enter(); 292
293 } else { 293 suspend_console();
294 error = -ENOSYS; 294 error = device_suspend(PMSG_SUSPEND);
295 if (error)
296 goto Resume_console;
297
298 error = hibernation_ops->prepare();
299 if (error)
300 goto Resume_devices;
301
302 error = disable_nonboot_cpus();
303 if (error)
304 goto Finish;
305
306 local_irq_disable();
307 error = device_power_down(PMSG_SUSPEND);
308 if (!error) {
309 hibernation_ops->enter();
310 /* We should never get here */
311 while (1);
295 } 312 }
313 local_irq_enable();
314
315 /*
316 * We don't need to reenable the nonboot CPUs or resume consoles, since
317 * the system is going to be halted anyway.
318 */
319 Finish:
320 hibernation_ops->finish();
321 Resume_devices:
322 device_resume();
323 Resume_console:
324 resume_console();
296 return error; 325 return error;
297} 326}
298 327
@@ -309,14 +338,14 @@ static void power_down(void)
309 case HIBERNATION_TEST: 338 case HIBERNATION_TEST:
310 case HIBERNATION_TESTPROC: 339 case HIBERNATION_TESTPROC:
311 break; 340 break;
312 case HIBERNATION_SHUTDOWN:
313 kernel_power_off();
314 break;
315 case HIBERNATION_REBOOT: 341 case HIBERNATION_REBOOT:
316 kernel_restart(NULL); 342 kernel_restart(NULL);
317 break; 343 break;
318 case HIBERNATION_PLATFORM: 344 case HIBERNATION_PLATFORM:
319 hibernation_platform_enter(); 345 hibernation_platform_enter();
346 case HIBERNATION_SHUTDOWN:
347 kernel_power_off();
348 break;
320 } 349 }
321 kernel_halt(); 350 kernel_halt();
322 /* 351 /*