diff options
Diffstat (limited to 'kernel/power/disk.c')
-rw-r--r-- | kernel/power/disk.c | 155 |
1 files changed, 105 insertions, 50 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 432ee575c9ee..5f21ab2bbcdf 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/console.h> | 22 | #include <linux/console.h> |
23 | #include <linux/cpu.h> | 23 | #include <linux/cpu.h> |
24 | #include <linux/freezer.h> | 24 | #include <linux/freezer.h> |
25 | #include <asm/suspend.h> | ||
25 | 26 | ||
26 | #include "power.h" | 27 | #include "power.h" |
27 | 28 | ||
@@ -214,7 +215,7 @@ static int create_image(int platform_mode) | |||
214 | return error; | 215 | return error; |
215 | 216 | ||
216 | device_pm_lock(); | 217 | device_pm_lock(); |
217 | local_irq_disable(); | 218 | |
218 | /* At this point, device_suspend() has been called, but *not* | 219 | /* At this point, device_suspend() has been called, but *not* |
219 | * device_power_down(). We *must* call device_power_down() now. | 220 | * device_power_down(). We *must* call device_power_down() now. |
220 | * Otherwise, drivers for some devices (e.g. interrupt controllers) | 221 | * Otherwise, drivers for some devices (e.g. interrupt controllers) |
@@ -225,6 +226,24 @@ static int create_image(int platform_mode) | |||
225 | if (error) { | 226 | if (error) { |
226 | printk(KERN_ERR "PM: Some devices failed to power down, " | 227 | printk(KERN_ERR "PM: Some devices failed to power down, " |
227 | "aborting hibernation\n"); | 228 | "aborting hibernation\n"); |
229 | goto Unlock; | ||
230 | } | ||
231 | |||
232 | error = platform_pre_snapshot(platform_mode); | ||
233 | if (error || hibernation_test(TEST_PLATFORM)) | ||
234 | goto Platform_finish; | ||
235 | |||
236 | error = disable_nonboot_cpus(); | ||
237 | if (error || hibernation_test(TEST_CPUS) | ||
238 | || hibernation_testmode(HIBERNATION_TEST)) | ||
239 | goto Enable_cpus; | ||
240 | |||
241 | local_irq_disable(); | ||
242 | |||
243 | sysdev_suspend(PMSG_FREEZE); | ||
244 | if (error) { | ||
245 | printk(KERN_ERR "PM: Some devices failed to power down, " | ||
246 | "aborting hibernation\n"); | ||
228 | goto Enable_irqs; | 247 | goto Enable_irqs; |
229 | } | 248 | } |
230 | 249 | ||
@@ -241,15 +260,28 @@ static int create_image(int platform_mode) | |||
241 | restore_processor_state(); | 260 | restore_processor_state(); |
242 | if (!in_suspend) | 261 | if (!in_suspend) |
243 | platform_leave(platform_mode); | 262 | platform_leave(platform_mode); |
263 | |||
244 | Power_up: | 264 | Power_up: |
265 | sysdev_resume(); | ||
245 | /* NOTE: device_power_up() is just a resume() for devices | 266 | /* NOTE: device_power_up() is just a resume() for devices |
246 | * that suspended with irqs off ... no overall powerup. | 267 | * that suspended with irqs off ... no overall powerup. |
247 | */ | 268 | */ |
248 | device_power_up(in_suspend ? | 269 | |
249 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); | ||
250 | Enable_irqs: | 270 | Enable_irqs: |
251 | local_irq_enable(); | 271 | local_irq_enable(); |
272 | |||
273 | Enable_cpus: | ||
274 | enable_nonboot_cpus(); | ||
275 | |||
276 | Platform_finish: | ||
277 | platform_finish(platform_mode); | ||
278 | |||
279 | device_power_up(in_suspend ? | ||
280 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); | ||
281 | |||
282 | Unlock: | ||
252 | device_pm_unlock(); | 283 | device_pm_unlock(); |
284 | |||
253 | return error; | 285 | return error; |
254 | } | 286 | } |
255 | 287 | ||
@@ -257,7 +289,7 @@ static int create_image(int platform_mode) | |||
257 | * hibernation_snapshot - quiesce devices and create the hibernation | 289 | * hibernation_snapshot - quiesce devices and create the hibernation |
258 | * snapshot image. | 290 | * snapshot image. |
259 | * @platform_mode - if set, use the platform driver, if available, to | 291 | * @platform_mode - if set, use the platform driver, if available, to |
260 | * prepare the platform frimware for the power transition. | 292 | * prepare the platform firmware for the power transition. |
261 | * | 293 | * |
262 | * Must be called with pm_mutex held | 294 | * Must be called with pm_mutex held |
263 | */ | 295 | */ |
@@ -283,25 +315,9 @@ int hibernation_snapshot(int platform_mode) | |||
283 | if (hibernation_test(TEST_DEVICES)) | 315 | if (hibernation_test(TEST_DEVICES)) |
284 | goto Recover_platform; | 316 | goto Recover_platform; |
285 | 317 | ||
286 | error = platform_pre_snapshot(platform_mode); | 318 | error = create_image(platform_mode); |
287 | if (error || hibernation_test(TEST_PLATFORM)) | 319 | /* Control returns here after successful restore */ |
288 | goto Finish; | ||
289 | |||
290 | error = disable_nonboot_cpus(); | ||
291 | if (!error) { | ||
292 | if (hibernation_test(TEST_CPUS)) | ||
293 | goto Enable_cpus; | ||
294 | 320 | ||
295 | if (hibernation_testmode(HIBERNATION_TEST)) | ||
296 | goto Enable_cpus; | ||
297 | |||
298 | error = create_image(platform_mode); | ||
299 | /* Control returns here after successful restore */ | ||
300 | } | ||
301 | Enable_cpus: | ||
302 | enable_nonboot_cpus(); | ||
303 | Finish: | ||
304 | platform_finish(platform_mode); | ||
305 | Resume_devices: | 321 | Resume_devices: |
306 | device_resume(in_suspend ? | 322 | device_resume(in_suspend ? |
307 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); | 323 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); |
@@ -323,18 +339,33 @@ int hibernation_snapshot(int platform_mode) | |||
323 | * kernel. | 339 | * kernel. |
324 | */ | 340 | */ |
325 | 341 | ||
326 | static int resume_target_kernel(void) | 342 | static int resume_target_kernel(bool platform_mode) |
327 | { | 343 | { |
328 | int error; | 344 | int error; |
329 | 345 | ||
330 | device_pm_lock(); | 346 | device_pm_lock(); |
331 | local_irq_disable(); | 347 | |
332 | error = device_power_down(PMSG_QUIESCE); | 348 | error = device_power_down(PMSG_QUIESCE); |
333 | if (error) { | 349 | if (error) { |
334 | printk(KERN_ERR "PM: Some devices failed to power down, " | 350 | printk(KERN_ERR "PM: Some devices failed to power down, " |
335 | "aborting resume\n"); | 351 | "aborting resume\n"); |
336 | goto Enable_irqs; | 352 | goto Unlock; |
337 | } | 353 | } |
354 | |||
355 | error = platform_pre_restore(platform_mode); | ||
356 | if (error) | ||
357 | goto Cleanup; | ||
358 | |||
359 | error = disable_nonboot_cpus(); | ||
360 | if (error) | ||
361 | goto Enable_cpus; | ||
362 | |||
363 | local_irq_disable(); | ||
364 | |||
365 | error = sysdev_suspend(PMSG_QUIESCE); | ||
366 | if (error) | ||
367 | goto Enable_irqs; | ||
368 | |||
338 | /* We'll ignore saved state, but this gets preempt count (etc) right */ | 369 | /* We'll ignore saved state, but this gets preempt count (etc) right */ |
339 | save_processor_state(); | 370 | save_processor_state(); |
340 | error = restore_highmem(); | 371 | error = restore_highmem(); |
@@ -357,10 +388,23 @@ static int resume_target_kernel(void) | |||
357 | swsusp_free(); | 388 | swsusp_free(); |
358 | restore_processor_state(); | 389 | restore_processor_state(); |
359 | touch_softlockup_watchdog(); | 390 | touch_softlockup_watchdog(); |
360 | device_power_up(PMSG_RECOVER); | 391 | |
392 | sysdev_resume(); | ||
393 | |||
361 | Enable_irqs: | 394 | Enable_irqs: |
362 | local_irq_enable(); | 395 | local_irq_enable(); |
396 | |||
397 | Enable_cpus: | ||
398 | enable_nonboot_cpus(); | ||
399 | |||
400 | Cleanup: | ||
401 | platform_restore_cleanup(platform_mode); | ||
402 | |||
403 | device_power_up(PMSG_RECOVER); | ||
404 | |||
405 | Unlock: | ||
363 | device_pm_unlock(); | 406 | device_pm_unlock(); |
407 | |||
364 | return error; | 408 | return error; |
365 | } | 409 | } |
366 | 410 | ||
@@ -368,7 +412,7 @@ static int resume_target_kernel(void) | |||
368 | * hibernation_restore - quiesce devices and restore the hibernation | 412 | * hibernation_restore - quiesce devices and restore the hibernation |
369 | * snapshot image. If successful, control returns in hibernation_snaphot() | 413 | * snapshot image. If successful, control returns in hibernation_snaphot() |
370 | * @platform_mode - if set, use the platform driver, if available, to | 414 | * @platform_mode - if set, use the platform driver, if available, to |
371 | * prepare the platform frimware for the transition. | 415 | * prepare the platform firmware for the transition. |
372 | * | 416 | * |
373 | * Must be called with pm_mutex held | 417 | * Must be called with pm_mutex held |
374 | */ | 418 | */ |
@@ -380,19 +424,10 @@ int hibernation_restore(int platform_mode) | |||
380 | pm_prepare_console(); | 424 | pm_prepare_console(); |
381 | suspend_console(); | 425 | suspend_console(); |
382 | error = device_suspend(PMSG_QUIESCE); | 426 | error = device_suspend(PMSG_QUIESCE); |
383 | if (error) | ||
384 | goto Finish; | ||
385 | |||
386 | error = platform_pre_restore(platform_mode); | ||
387 | if (!error) { | 427 | if (!error) { |
388 | error = disable_nonboot_cpus(); | 428 | error = resume_target_kernel(platform_mode); |
389 | if (!error) | 429 | device_resume(PMSG_RECOVER); |
390 | error = resume_target_kernel(); | ||
391 | enable_nonboot_cpus(); | ||
392 | } | 430 | } |
393 | platform_restore_cleanup(platform_mode); | ||
394 | device_resume(PMSG_RECOVER); | ||
395 | Finish: | ||
396 | resume_console(); | 431 | resume_console(); |
397 | pm_restore_console(); | 432 | pm_restore_console(); |
398 | return error; | 433 | return error; |
@@ -428,37 +463,46 @@ int hibernation_platform_enter(void) | |||
428 | goto Resume_devices; | 463 | goto Resume_devices; |
429 | } | 464 | } |
430 | 465 | ||
466 | device_pm_lock(); | ||
467 | |||
468 | error = device_power_down(PMSG_HIBERNATE); | ||
469 | if (error) | ||
470 | goto Unlock; | ||
471 | |||
431 | error = hibernation_ops->prepare(); | 472 | error = hibernation_ops->prepare(); |
432 | if (error) | 473 | if (error) |
433 | goto Resume_devices; | 474 | goto Platofrm_finish; |
434 | 475 | ||
435 | error = disable_nonboot_cpus(); | 476 | error = disable_nonboot_cpus(); |
436 | if (error) | 477 | if (error) |
437 | goto Finish; | 478 | goto Platofrm_finish; |
438 | 479 | ||
439 | device_pm_lock(); | ||
440 | local_irq_disable(); | 480 | local_irq_disable(); |
441 | error = device_power_down(PMSG_HIBERNATE); | 481 | sysdev_suspend(PMSG_HIBERNATE); |
442 | if (!error) { | 482 | hibernation_ops->enter(); |
443 | hibernation_ops->enter(); | 483 | /* We should never get here */ |
444 | /* We should never get here */ | 484 | while (1); |
445 | while (1); | ||
446 | } | ||
447 | local_irq_enable(); | ||
448 | device_pm_unlock(); | ||
449 | 485 | ||
450 | /* | 486 | /* |
451 | * We don't need to reenable the nonboot CPUs or resume consoles, since | 487 | * We don't need to reenable the nonboot CPUs or resume consoles, since |
452 | * the system is going to be halted anyway. | 488 | * the system is going to be halted anyway. |
453 | */ | 489 | */ |
454 | Finish: | 490 | Platofrm_finish: |
455 | hibernation_ops->finish(); | 491 | hibernation_ops->finish(); |
492 | |||
493 | device_power_up(PMSG_RESTORE); | ||
494 | |||
495 | Unlock: | ||
496 | device_pm_unlock(); | ||
497 | |||
456 | Resume_devices: | 498 | Resume_devices: |
457 | entering_platform_hibernation = false; | 499 | entering_platform_hibernation = false; |
458 | device_resume(PMSG_RESTORE); | 500 | device_resume(PMSG_RESTORE); |
459 | resume_console(); | 501 | resume_console(); |
502 | |||
460 | Close: | 503 | Close: |
461 | hibernation_ops->end(); | 504 | hibernation_ops->end(); |
505 | |||
462 | return error; | 506 | return error; |
463 | } | 507 | } |
464 | 508 | ||
@@ -595,6 +639,12 @@ static int software_resume(void) | |||
595 | unsigned int flags; | 639 | unsigned int flags; |
596 | 640 | ||
597 | /* | 641 | /* |
642 | * If the user said "noresume".. bail out early. | ||
643 | */ | ||
644 | if (noresume) | ||
645 | return 0; | ||
646 | |||
647 | /* | ||
598 | * name_to_dev_t() below takes a sysfs buffer mutex when sysfs | 648 | * name_to_dev_t() below takes a sysfs buffer mutex when sysfs |
599 | * is configured into the kernel. Since the regular hibernate | 649 | * is configured into the kernel. Since the regular hibernate |
600 | * trigger path is via sysfs which takes a buffer mutex before | 650 | * trigger path is via sysfs which takes a buffer mutex before |
@@ -610,6 +660,11 @@ static int software_resume(void) | |||
610 | mutex_unlock(&pm_mutex); | 660 | mutex_unlock(&pm_mutex); |
611 | return -ENOENT; | 661 | return -ENOENT; |
612 | } | 662 | } |
663 | /* | ||
664 | * Some device discovery might still be in progress; we need | ||
665 | * to wait for this to finish. | ||
666 | */ | ||
667 | wait_for_device_probe(); | ||
613 | swsusp_resume_device = name_to_dev_t(resume_file); | 668 | swsusp_resume_device = name_to_dev_t(resume_file); |
614 | pr_debug("PM: Resume from partition %s\n", resume_file); | 669 | pr_debug("PM: Resume from partition %s\n", resume_file); |
615 | } else { | 670 | } else { |