diff options
Diffstat (limited to 'kernel/power/suspend.c')
| -rw-r--r-- | kernel/power/suspend.c | 111 | 
1 files changed, 78 insertions, 33 deletions
| diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 8233cd4047d7..963e6d0f050b 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c | |||
| @@ -31,13 +31,14 @@ | |||
| 31 | 31 | ||
| 32 | #include "power.h" | 32 | #include "power.h" | 
| 33 | 33 | ||
| 34 | const char *const pm_states[PM_SUSPEND_MAX] = { | 34 | struct pm_sleep_state pm_states[PM_SUSPEND_MAX] = { | 
| 35 | [PM_SUSPEND_FREEZE] = "freeze", | 35 | [PM_SUSPEND_FREEZE] = { .label = "freeze", .state = PM_SUSPEND_FREEZE }, | 
| 36 | [PM_SUSPEND_STANDBY] = "standby", | 36 | [PM_SUSPEND_STANDBY] = { .label = "standby", }, | 
| 37 | [PM_SUSPEND_MEM] = "mem", | 37 | [PM_SUSPEND_MEM] = { .label = "mem", }, | 
| 38 | }; | 38 | }; | 
| 39 | 39 | ||
| 40 | static const struct platform_suspend_ops *suspend_ops; | 40 | static const struct platform_suspend_ops *suspend_ops; | 
| 41 | static const struct platform_freeze_ops *freeze_ops; | ||
| 41 | 42 | ||
| 42 | static bool need_suspend_ops(suspend_state_t state) | 43 | static bool need_suspend_ops(suspend_state_t state) | 
| 43 | { | 44 | { | 
| @@ -47,6 +48,13 @@ static bool need_suspend_ops(suspend_state_t state) | |||
| 47 | static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); | 48 | static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); | 
| 48 | static bool suspend_freeze_wake; | 49 | static bool suspend_freeze_wake; | 
| 49 | 50 | ||
| 51 | void freeze_set_ops(const struct platform_freeze_ops *ops) | ||
| 52 | { | ||
| 53 | lock_system_sleep(); | ||
| 54 | freeze_ops = ops; | ||
| 55 | unlock_system_sleep(); | ||
| 56 | } | ||
| 57 | |||
| 50 | static void freeze_begin(void) | 58 | static void freeze_begin(void) | 
| 51 | { | 59 | { | 
| 52 | suspend_freeze_wake = false; | 60 | suspend_freeze_wake = false; | 
| @@ -54,9 +62,11 @@ static void freeze_begin(void) | |||
| 54 | 62 | ||
| 55 | static void freeze_enter(void) | 63 | static void freeze_enter(void) | 
| 56 | { | 64 | { | 
| 65 | cpuidle_use_deepest_state(true); | ||
| 57 | cpuidle_resume(); | 66 | cpuidle_resume(); | 
| 58 | wait_event(suspend_freeze_wait_head, suspend_freeze_wake); | 67 | wait_event(suspend_freeze_wait_head, suspend_freeze_wake); | 
| 59 | cpuidle_pause(); | 68 | cpuidle_pause(); | 
| 69 | cpuidle_use_deepest_state(false); | ||
| 60 | } | 70 | } | 
| 61 | 71 | ||
| 62 | void freeze_wake(void) | 72 | void freeze_wake(void) | 
| @@ -66,42 +76,62 @@ void freeze_wake(void) | |||
| 66 | } | 76 | } | 
| 67 | EXPORT_SYMBOL_GPL(freeze_wake); | 77 | EXPORT_SYMBOL_GPL(freeze_wake); | 
| 68 | 78 | ||
| 79 | static bool valid_state(suspend_state_t state) | ||
| 80 | { | ||
| 81 | /* | ||
| 82 | * PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states need low level | ||
| 83 | * support and need to be valid to the low level | ||
| 84 | * implementation, no valid callback implies that none are valid. | ||
| 85 | */ | ||
| 86 | return suspend_ops && suspend_ops->valid && suspend_ops->valid(state); | ||
| 87 | } | ||
| 88 | |||
| 89 | /* | ||
| 90 | * If this is set, the "mem" label always corresponds to the deepest sleep state | ||
| 91 | * available, the "standby" label corresponds to the second deepest sleep state | ||
| 92 | * available (if any), and the "freeze" label corresponds to the remaining | ||
| 93 | * available sleep state (if there is one). | ||
| 94 | */ | ||
| 95 | static bool relative_states; | ||
| 96 | |||
| 97 | static int __init sleep_states_setup(char *str) | ||
| 98 | { | ||
| 99 | relative_states = !strncmp(str, "1", 1); | ||
| 100 | if (relative_states) { | ||
| 101 | pm_states[PM_SUSPEND_MEM].state = PM_SUSPEND_FREEZE; | ||
| 102 | pm_states[PM_SUSPEND_FREEZE].state = 0; | ||
| 103 | } | ||
| 104 | return 1; | ||
| 105 | } | ||
| 106 | |||
| 107 | __setup("relative_sleep_states=", sleep_states_setup); | ||
| 108 | |||
| 69 | /** | 109 | /** | 
| 70 | * suspend_set_ops - Set the global suspend method table. | 110 | * suspend_set_ops - Set the global suspend method table. | 
| 71 | * @ops: Suspend operations to use. | 111 | * @ops: Suspend operations to use. | 
| 72 | */ | 112 | */ | 
| 73 | void suspend_set_ops(const struct platform_suspend_ops *ops) | 113 | void suspend_set_ops(const struct platform_suspend_ops *ops) | 
| 74 | { | 114 | { | 
| 115 | suspend_state_t i; | ||
| 116 | int j = PM_SUSPEND_MAX - 1; | ||
| 117 | |||
| 75 | lock_system_sleep(); | 118 | lock_system_sleep(); | 
| 119 | |||
| 76 | suspend_ops = ops; | 120 | suspend_ops = ops; | 
| 121 | for (i = PM_SUSPEND_MEM; i >= PM_SUSPEND_STANDBY; i--) | ||
| 122 | if (valid_state(i)) | ||
| 123 | pm_states[j--].state = i; | ||
| 124 | else if (!relative_states) | ||
| 125 | pm_states[j--].state = 0; | ||
| 126 | |||
| 127 | pm_states[j--].state = PM_SUSPEND_FREEZE; | ||
| 128 | while (j >= PM_SUSPEND_MIN) | ||
| 129 | pm_states[j--].state = 0; | ||
| 130 | |||
| 77 | unlock_system_sleep(); | 131 | unlock_system_sleep(); | 
| 78 | } | 132 | } | 
| 79 | EXPORT_SYMBOL_GPL(suspend_set_ops); | 133 | EXPORT_SYMBOL_GPL(suspend_set_ops); | 
| 80 | 134 | ||
| 81 | bool valid_state(suspend_state_t state) | ||
| 82 | { | ||
| 83 | if (state == PM_SUSPEND_FREEZE) { | ||
| 84 | #ifdef CONFIG_PM_DEBUG | ||
| 85 | if (pm_test_level != TEST_NONE && | ||
| 86 | pm_test_level != TEST_FREEZER && | ||
| 87 | pm_test_level != TEST_DEVICES && | ||
| 88 | pm_test_level != TEST_PLATFORM) { | ||
| 89 | printk(KERN_WARNING "Unsupported pm_test mode for " | ||
| 90 | "freeze state, please choose " | ||
| 91 | "none/freezer/devices/platform.\n"); | ||
| 92 | return false; | ||
| 93 | } | ||
| 94 | #endif | ||
| 95 | return true; | ||
| 96 | } | ||
| 97 | /* | ||
| 98 | * PM_SUSPEND_STANDBY and PM_SUSPEND_MEMORY states need lowlevel | ||
| 99 | * support and need to be valid to the lowlevel | ||
| 100 | * implementation, no valid callback implies that none are valid. | ||
| 101 | */ | ||
| 102 | return suspend_ops && suspend_ops->valid && suspend_ops->valid(state); | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | 135 | /** | 
| 106 | * suspend_valid_only_mem - Generic memory-only valid callback. | 136 | * suspend_valid_only_mem - Generic memory-only valid callback. | 
| 107 | * | 137 | * | 
| @@ -269,6 +299,10 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
| 269 | error = suspend_ops->begin(state); | 299 | error = suspend_ops->begin(state); | 
| 270 | if (error) | 300 | if (error) | 
| 271 | goto Close; | 301 | goto Close; | 
| 302 | } else if (state == PM_SUSPEND_FREEZE && freeze_ops->begin) { | ||
| 303 | error = freeze_ops->begin(); | ||
| 304 | if (error) | ||
| 305 | goto Close; | ||
| 272 | } | 306 | } | 
| 273 | suspend_console(); | 307 | suspend_console(); | 
| 274 | suspend_test_start(); | 308 | suspend_test_start(); | 
| @@ -294,6 +328,9 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
| 294 | Close: | 328 | Close: | 
| 295 | if (need_suspend_ops(state) && suspend_ops->end) | 329 | if (need_suspend_ops(state) && suspend_ops->end) | 
| 296 | suspend_ops->end(); | 330 | suspend_ops->end(); | 
| 331 | else if (state == PM_SUSPEND_FREEZE && freeze_ops->end) | ||
| 332 | freeze_ops->end(); | ||
| 333 | |||
| 297 | trace_machine_suspend(PWR_EVENT_EXIT); | 334 | trace_machine_suspend(PWR_EVENT_EXIT); | 
| 298 | return error; | 335 | return error; | 
| 299 | 336 | ||
| @@ -328,9 +365,17 @@ static int enter_state(suspend_state_t state) | |||
| 328 | { | 365 | { | 
| 329 | int error; | 366 | int error; | 
| 330 | 367 | ||
| 331 | if (!valid_state(state)) | 368 | if (state == PM_SUSPEND_FREEZE) { | 
| 332 | return -ENODEV; | 369 | #ifdef CONFIG_PM_DEBUG | 
| 333 | 370 | if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) { | |
| 371 | pr_warning("PM: Unsupported test mode for freeze state," | ||
| 372 | "please choose none/freezer/devices/platform.\n"); | ||
| 373 | return -EAGAIN; | ||
| 374 | } | ||
| 375 | #endif | ||
| 376 | } else if (!valid_state(state)) { | ||
| 377 | return -EINVAL; | ||
| 378 | } | ||
| 334 | if (!mutex_trylock(&pm_mutex)) | 379 | if (!mutex_trylock(&pm_mutex)) | 
| 335 | return -EBUSY; | 380 | return -EBUSY; | 
| 336 | 381 | ||
| @@ -341,7 +386,7 @@ static int enter_state(suspend_state_t state) | |||
| 341 | sys_sync(); | 386 | sys_sync(); | 
| 342 | printk("done.\n"); | 387 | printk("done.\n"); | 
| 343 | 388 | ||
| 344 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); | 389 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state].label); | 
| 345 | error = suspend_prepare(state); | 390 | error = suspend_prepare(state); | 
| 346 | if (error) | 391 | if (error) | 
| 347 | goto Unlock; | 392 | goto Unlock; | 
| @@ -349,7 +394,7 @@ static int enter_state(suspend_state_t state) | |||
| 349 | if (suspend_test(TEST_FREEZER)) | 394 | if (suspend_test(TEST_FREEZER)) | 
| 350 | goto Finish; | 395 | goto Finish; | 
| 351 | 396 | ||
| 352 | pr_debug("PM: Entering %s sleep\n", pm_states[state]); | 397 | pr_debug("PM: Entering %s sleep\n", pm_states[state].label); | 
| 353 | pm_restrict_gfp_mask(); | 398 | pm_restrict_gfp_mask(); | 
| 354 | error = suspend_devices_and_enter(state); | 399 | error = suspend_devices_and_enter(state); | 
| 355 | pm_restore_gfp_mask(); | 400 | pm_restore_gfp_mask(); | 
