diff options
Diffstat (limited to 'kernel/power/suspend.c')
-rw-r--r-- | kernel/power/suspend.c | 109 |
1 files changed, 76 insertions, 33 deletions
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 155721f7f909..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; |
@@ -68,42 +76,62 @@ void freeze_wake(void) | |||
68 | } | 76 | } |
69 | EXPORT_SYMBOL_GPL(freeze_wake); | 77 | EXPORT_SYMBOL_GPL(freeze_wake); |
70 | 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 | |||
71 | /** | 109 | /** |
72 | * suspend_set_ops - Set the global suspend method table. | 110 | * suspend_set_ops - Set the global suspend method table. |
73 | * @ops: Suspend operations to use. | 111 | * @ops: Suspend operations to use. |
74 | */ | 112 | */ |
75 | void suspend_set_ops(const struct platform_suspend_ops *ops) | 113 | void suspend_set_ops(const struct platform_suspend_ops *ops) |
76 | { | 114 | { |
115 | suspend_state_t i; | ||
116 | int j = PM_SUSPEND_MAX - 1; | ||
117 | |||
77 | lock_system_sleep(); | 118 | lock_system_sleep(); |
119 | |||
78 | 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 | |||
79 | unlock_system_sleep(); | 131 | unlock_system_sleep(); |
80 | } | 132 | } |
81 | EXPORT_SYMBOL_GPL(suspend_set_ops); | 133 | EXPORT_SYMBOL_GPL(suspend_set_ops); |
82 | 134 | ||
83 | bool valid_state(suspend_state_t state) | ||
84 | { | ||
85 | if (state == PM_SUSPEND_FREEZE) { | ||
86 | #ifdef CONFIG_PM_DEBUG | ||
87 | if (pm_test_level != TEST_NONE && | ||
88 | pm_test_level != TEST_FREEZER && | ||
89 | pm_test_level != TEST_DEVICES && | ||
90 | pm_test_level != TEST_PLATFORM) { | ||
91 | printk(KERN_WARNING "Unsupported pm_test mode for " | ||
92 | "freeze state, please choose " | ||
93 | "none/freezer/devices/platform.\n"); | ||
94 | return false; | ||
95 | } | ||
96 | #endif | ||
97 | return true; | ||
98 | } | ||
99 | /* | ||
100 | * PM_SUSPEND_STANDBY and PM_SUSPEND_MEMORY states need lowlevel | ||
101 | * support and need to be valid to the lowlevel | ||
102 | * implementation, no valid callback implies that none are valid. | ||
103 | */ | ||
104 | return suspend_ops && suspend_ops->valid && suspend_ops->valid(state); | ||
105 | } | ||
106 | |||
107 | /** | 135 | /** |
108 | * suspend_valid_only_mem - Generic memory-only valid callback. | 136 | * suspend_valid_only_mem - Generic memory-only valid callback. |
109 | * | 137 | * |
@@ -271,6 +299,10 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
271 | error = suspend_ops->begin(state); | 299 | error = suspend_ops->begin(state); |
272 | if (error) | 300 | if (error) |
273 | 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; | ||
274 | } | 306 | } |
275 | suspend_console(); | 307 | suspend_console(); |
276 | suspend_test_start(); | 308 | suspend_test_start(); |
@@ -296,6 +328,9 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
296 | Close: | 328 | Close: |
297 | if (need_suspend_ops(state) && suspend_ops->end) | 329 | if (need_suspend_ops(state) && suspend_ops->end) |
298 | suspend_ops->end(); | 330 | suspend_ops->end(); |
331 | else if (state == PM_SUSPEND_FREEZE && freeze_ops->end) | ||
332 | freeze_ops->end(); | ||
333 | |||
299 | trace_machine_suspend(PWR_EVENT_EXIT); | 334 | trace_machine_suspend(PWR_EVENT_EXIT); |
300 | return error; | 335 | return error; |
301 | 336 | ||
@@ -330,9 +365,17 @@ static int enter_state(suspend_state_t state) | |||
330 | { | 365 | { |
331 | int error; | 366 | int error; |
332 | 367 | ||
333 | if (!valid_state(state)) | 368 | if (state == PM_SUSPEND_FREEZE) { |
334 | return -ENODEV; | 369 | #ifdef CONFIG_PM_DEBUG |
335 | 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 | } | ||
336 | if (!mutex_trylock(&pm_mutex)) | 379 | if (!mutex_trylock(&pm_mutex)) |
337 | return -EBUSY; | 380 | return -EBUSY; |
338 | 381 | ||
@@ -343,7 +386,7 @@ static int enter_state(suspend_state_t state) | |||
343 | sys_sync(); | 386 | sys_sync(); |
344 | printk("done.\n"); | 387 | printk("done.\n"); |
345 | 388 | ||
346 | 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); |
347 | error = suspend_prepare(state); | 390 | error = suspend_prepare(state); |
348 | if (error) | 391 | if (error) |
349 | goto Unlock; | 392 | goto Unlock; |
@@ -351,7 +394,7 @@ static int enter_state(suspend_state_t state) | |||
351 | if (suspend_test(TEST_FREEZER)) | 394 | if (suspend_test(TEST_FREEZER)) |
352 | goto Finish; | 395 | goto Finish; |
353 | 396 | ||
354 | pr_debug("PM: Entering %s sleep\n", pm_states[state]); | 397 | pr_debug("PM: Entering %s sleep\n", pm_states[state].label); |
355 | pm_restrict_gfp_mask(); | 398 | pm_restrict_gfp_mask(); |
356 | error = suspend_devices_and_enter(state); | 399 | error = suspend_devices_and_enter(state); |
357 | pm_restore_gfp_mask(); | 400 | pm_restore_gfp_mask(); |