diff options
Diffstat (limited to 'kernel/power/disk.c')
-rw-r--r-- | kernel/power/disk.c | 143 |
1 files changed, 88 insertions, 55 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 4a4a206b1979..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,13 +226,25 @@ 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"); |
228 | goto Enable_irqs; | 229 | goto Unlock; |
229 | } | 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 | |||
230 | sysdev_suspend(PMSG_FREEZE); | 243 | sysdev_suspend(PMSG_FREEZE); |
231 | if (error) { | 244 | if (error) { |
232 | printk(KERN_ERR "PM: Some devices failed to power down, " | 245 | printk(KERN_ERR "PM: Some devices failed to power down, " |
233 | "aborting hibernation\n"); | 246 | "aborting hibernation\n"); |
234 | goto Power_up_devices; | 247 | goto Enable_irqs; |
235 | } | 248 | } |
236 | 249 | ||
237 | if (hibernation_test(TEST_CORE)) | 250 | if (hibernation_test(TEST_CORE)) |
@@ -247,17 +260,28 @@ static int create_image(int platform_mode) | |||
247 | restore_processor_state(); | 260 | restore_processor_state(); |
248 | if (!in_suspend) | 261 | if (!in_suspend) |
249 | platform_leave(platform_mode); | 262 | platform_leave(platform_mode); |
263 | |||
250 | Power_up: | 264 | Power_up: |
251 | sysdev_resume(); | 265 | sysdev_resume(); |
252 | /* NOTE: device_power_up() is just a resume() for devices | 266 | /* NOTE: device_power_up() is just a resume() for devices |
253 | * that suspended with irqs off ... no overall powerup. | 267 | * that suspended with irqs off ... no overall powerup. |
254 | */ | 268 | */ |
255 | Power_up_devices: | 269 | |
256 | device_power_up(in_suspend ? | ||
257 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); | ||
258 | Enable_irqs: | 270 | Enable_irqs: |
259 | 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: | ||
260 | device_pm_unlock(); | 283 | device_pm_unlock(); |
284 | |||
261 | return error; | 285 | return error; |
262 | } | 286 | } |
263 | 287 | ||
@@ -265,7 +289,7 @@ static int create_image(int platform_mode) | |||
265 | * hibernation_snapshot - quiesce devices and create the hibernation | 289 | * hibernation_snapshot - quiesce devices and create the hibernation |
266 | * snapshot image. | 290 | * snapshot image. |
267 | * @platform_mode - if set, use the platform driver, if available, to | 291 | * @platform_mode - if set, use the platform driver, if available, to |
268 | * prepare the platform frimware for the power transition. | 292 | * prepare the platform firmware for the power transition. |
269 | * | 293 | * |
270 | * Must be called with pm_mutex held | 294 | * Must be called with pm_mutex held |
271 | */ | 295 | */ |
@@ -291,25 +315,9 @@ int hibernation_snapshot(int platform_mode) | |||
291 | if (hibernation_test(TEST_DEVICES)) | 315 | if (hibernation_test(TEST_DEVICES)) |
292 | goto Recover_platform; | 316 | goto Recover_platform; |
293 | 317 | ||
294 | error = platform_pre_snapshot(platform_mode); | 318 | error = create_image(platform_mode); |
295 | if (error || hibernation_test(TEST_PLATFORM)) | 319 | /* Control returns here after successful restore */ |
296 | goto Finish; | ||
297 | |||
298 | error = disable_nonboot_cpus(); | ||
299 | if (!error) { | ||
300 | if (hibernation_test(TEST_CPUS)) | ||
301 | goto Enable_cpus; | ||
302 | |||
303 | if (hibernation_testmode(HIBERNATION_TEST)) | ||
304 | goto Enable_cpus; | ||
305 | 320 | ||
306 | error = create_image(platform_mode); | ||
307 | /* Control returns here after successful restore */ | ||
308 | } | ||
309 | Enable_cpus: | ||
310 | enable_nonboot_cpus(); | ||
311 | Finish: | ||
312 | platform_finish(platform_mode); | ||
313 | Resume_devices: | 321 | Resume_devices: |
314 | device_resume(in_suspend ? | 322 | device_resume(in_suspend ? |
315 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); | 323 | (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); |
@@ -331,19 +339,33 @@ int hibernation_snapshot(int platform_mode) | |||
331 | * kernel. | 339 | * kernel. |
332 | */ | 340 | */ |
333 | 341 | ||
334 | static int resume_target_kernel(void) | 342 | static int resume_target_kernel(bool platform_mode) |
335 | { | 343 | { |
336 | int error; | 344 | int error; |
337 | 345 | ||
338 | device_pm_lock(); | 346 | device_pm_lock(); |
339 | local_irq_disable(); | 347 | |
340 | error = device_power_down(PMSG_QUIESCE); | 348 | error = device_power_down(PMSG_QUIESCE); |
341 | if (error) { | 349 | if (error) { |
342 | printk(KERN_ERR "PM: Some devices failed to power down, " | 350 | printk(KERN_ERR "PM: Some devices failed to power down, " |
343 | "aborting resume\n"); | 351 | "aborting resume\n"); |
344 | goto Enable_irqs; | 352 | goto Unlock; |
345 | } | 353 | } |
346 | sysdev_suspend(PMSG_QUIESCE); | 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 | |||
347 | /* 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 */ |
348 | save_processor_state(); | 370 | save_processor_state(); |
349 | error = restore_highmem(); | 371 | error = restore_highmem(); |
@@ -366,11 +388,23 @@ static int resume_target_kernel(void) | |||
366 | swsusp_free(); | 388 | swsusp_free(); |
367 | restore_processor_state(); | 389 | restore_processor_state(); |
368 | touch_softlockup_watchdog(); | 390 | touch_softlockup_watchdog(); |
391 | |||
369 | sysdev_resume(); | 392 | sysdev_resume(); |
370 | device_power_up(PMSG_RECOVER); | 393 | |
371 | Enable_irqs: | 394 | Enable_irqs: |
372 | 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: | ||
373 | device_pm_unlock(); | 406 | device_pm_unlock(); |
407 | |||
374 | return error; | 408 | return error; |
375 | } | 409 | } |
376 | 410 | ||
@@ -378,7 +412,7 @@ static int resume_target_kernel(void) | |||
378 | * hibernation_restore - quiesce devices and restore the hibernation | 412 | * hibernation_restore - quiesce devices and restore the hibernation |
379 | * snapshot image. If successful, control returns in hibernation_snaphot() | 413 | * snapshot image. If successful, control returns in hibernation_snaphot() |
380 | * @platform_mode - if set, use the platform driver, if available, to | 414 | * @platform_mode - if set, use the platform driver, if available, to |
381 | * prepare the platform frimware for the transition. | 415 | * prepare the platform firmware for the transition. |
382 | * | 416 | * |
383 | * Must be called with pm_mutex held | 417 | * Must be called with pm_mutex held |
384 | */ | 418 | */ |
@@ -390,19 +424,10 @@ int hibernation_restore(int platform_mode) | |||
390 | pm_prepare_console(); | 424 | pm_prepare_console(); |
391 | suspend_console(); | 425 | suspend_console(); |
392 | error = device_suspend(PMSG_QUIESCE); | 426 | error = device_suspend(PMSG_QUIESCE); |
393 | if (error) | ||
394 | goto Finish; | ||
395 | |||
396 | error = platform_pre_restore(platform_mode); | ||
397 | if (!error) { | 427 | if (!error) { |
398 | error = disable_nonboot_cpus(); | 428 | error = resume_target_kernel(platform_mode); |
399 | if (!error) | 429 | device_resume(PMSG_RECOVER); |
400 | error = resume_target_kernel(); | ||
401 | enable_nonboot_cpus(); | ||
402 | } | 430 | } |
403 | platform_restore_cleanup(platform_mode); | ||
404 | device_resume(PMSG_RECOVER); | ||
405 | Finish: | ||
406 | resume_console(); | 431 | resume_console(); |
407 | pm_restore_console(); | 432 | pm_restore_console(); |
408 | return error; | 433 | return error; |
@@ -438,38 +463,46 @@ int hibernation_platform_enter(void) | |||
438 | goto Resume_devices; | 463 | goto Resume_devices; |
439 | } | 464 | } |
440 | 465 | ||
466 | device_pm_lock(); | ||
467 | |||
468 | error = device_power_down(PMSG_HIBERNATE); | ||
469 | if (error) | ||
470 | goto Unlock; | ||
471 | |||
441 | error = hibernation_ops->prepare(); | 472 | error = hibernation_ops->prepare(); |
442 | if (error) | 473 | if (error) |
443 | goto Resume_devices; | 474 | goto Platofrm_finish; |
444 | 475 | ||
445 | error = disable_nonboot_cpus(); | 476 | error = disable_nonboot_cpus(); |
446 | if (error) | 477 | if (error) |
447 | goto Finish; | 478 | goto Platofrm_finish; |
448 | 479 | ||
449 | device_pm_lock(); | ||
450 | local_irq_disable(); | 480 | local_irq_disable(); |
451 | error = device_power_down(PMSG_HIBERNATE); | 481 | sysdev_suspend(PMSG_HIBERNATE); |
452 | if (!error) { | 482 | hibernation_ops->enter(); |
453 | sysdev_suspend(PMSG_HIBERNATE); | 483 | /* We should never get here */ |
454 | hibernation_ops->enter(); | 484 | while (1); |
455 | /* We should never get here */ | ||
456 | while (1); | ||
457 | } | ||
458 | local_irq_enable(); | ||
459 | device_pm_unlock(); | ||
460 | 485 | ||
461 | /* | 486 | /* |
462 | * 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 |
463 | * the system is going to be halted anyway. | 488 | * the system is going to be halted anyway. |
464 | */ | 489 | */ |
465 | Finish: | 490 | Platofrm_finish: |
466 | hibernation_ops->finish(); | 491 | hibernation_ops->finish(); |
492 | |||
493 | device_power_up(PMSG_RESTORE); | ||
494 | |||
495 | Unlock: | ||
496 | device_pm_unlock(); | ||
497 | |||
467 | Resume_devices: | 498 | Resume_devices: |
468 | entering_platform_hibernation = false; | 499 | entering_platform_hibernation = false; |
469 | device_resume(PMSG_RESTORE); | 500 | device_resume(PMSG_RESTORE); |
470 | resume_console(); | 501 | resume_console(); |
502 | |||
471 | Close: | 503 | Close: |
472 | hibernation_ops->end(); | 504 | hibernation_ops->end(); |
505 | |||
473 | return error; | 506 | return error; |
474 | } | 507 | } |
475 | 508 | ||