diff options
Diffstat (limited to 'kernel/power/main.c')
-rw-r--r-- | kernel/power/main.c | 171 |
1 files changed, 145 insertions, 26 deletions
diff --git a/kernel/power/main.c b/kernel/power/main.c index efc08360e627..6a6d5eb3524e 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c | |||
@@ -24,13 +24,112 @@ | |||
24 | 24 | ||
25 | #include "power.h" | 25 | #include "power.h" |
26 | 26 | ||
27 | BLOCKING_NOTIFIER_HEAD(pm_chain_head); | ||
28 | |||
29 | DEFINE_MUTEX(pm_mutex); | 27 | DEFINE_MUTEX(pm_mutex); |
30 | 28 | ||
31 | unsigned int pm_flags; | 29 | unsigned int pm_flags; |
32 | EXPORT_SYMBOL(pm_flags); | 30 | EXPORT_SYMBOL(pm_flags); |
33 | 31 | ||
32 | #ifdef CONFIG_PM_SLEEP | ||
33 | |||
34 | /* Routines for PM-transition notifications */ | ||
35 | |||
36 | static BLOCKING_NOTIFIER_HEAD(pm_chain_head); | ||
37 | |||
38 | int register_pm_notifier(struct notifier_block *nb) | ||
39 | { | ||
40 | return blocking_notifier_chain_register(&pm_chain_head, nb); | ||
41 | } | ||
42 | EXPORT_SYMBOL_GPL(register_pm_notifier); | ||
43 | |||
44 | int unregister_pm_notifier(struct notifier_block *nb) | ||
45 | { | ||
46 | return blocking_notifier_chain_unregister(&pm_chain_head, nb); | ||
47 | } | ||
48 | EXPORT_SYMBOL_GPL(unregister_pm_notifier); | ||
49 | |||
50 | int pm_notifier_call_chain(unsigned long val) | ||
51 | { | ||
52 | return (blocking_notifier_call_chain(&pm_chain_head, val, NULL) | ||
53 | == NOTIFY_BAD) ? -EINVAL : 0; | ||
54 | } | ||
55 | |||
56 | #ifdef CONFIG_PM_DEBUG | ||
57 | int pm_test_level = TEST_NONE; | ||
58 | |||
59 | static int suspend_test(int level) | ||
60 | { | ||
61 | if (pm_test_level == level) { | ||
62 | printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n"); | ||
63 | mdelay(5000); | ||
64 | return 1; | ||
65 | } | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static const char * const pm_tests[__TEST_AFTER_LAST] = { | ||
70 | [TEST_NONE] = "none", | ||
71 | [TEST_CORE] = "core", | ||
72 | [TEST_CPUS] = "processors", | ||
73 | [TEST_PLATFORM] = "platform", | ||
74 | [TEST_DEVICES] = "devices", | ||
75 | [TEST_FREEZER] = "freezer", | ||
76 | }; | ||
77 | |||
78 | static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr, | ||
79 | char *buf) | ||
80 | { | ||
81 | char *s = buf; | ||
82 | int level; | ||
83 | |||
84 | for (level = TEST_FIRST; level <= TEST_MAX; level++) | ||
85 | if (pm_tests[level]) { | ||
86 | if (level == pm_test_level) | ||
87 | s += sprintf(s, "[%s] ", pm_tests[level]); | ||
88 | else | ||
89 | s += sprintf(s, "%s ", pm_tests[level]); | ||
90 | } | ||
91 | |||
92 | if (s != buf) | ||
93 | /* convert the last space to a newline */ | ||
94 | *(s-1) = '\n'; | ||
95 | |||
96 | return (s - buf); | ||
97 | } | ||
98 | |||
99 | static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
100 | const char *buf, size_t n) | ||
101 | { | ||
102 | const char * const *s; | ||
103 | int level; | ||
104 | char *p; | ||
105 | int len; | ||
106 | int error = -EINVAL; | ||
107 | |||
108 | p = memchr(buf, '\n', n); | ||
109 | len = p ? p - buf : n; | ||
110 | |||
111 | mutex_lock(&pm_mutex); | ||
112 | |||
113 | level = TEST_FIRST; | ||
114 | for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++) | ||
115 | if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) { | ||
116 | pm_test_level = level; | ||
117 | error = 0; | ||
118 | break; | ||
119 | } | ||
120 | |||
121 | mutex_unlock(&pm_mutex); | ||
122 | |||
123 | return error ? error : n; | ||
124 | } | ||
125 | |||
126 | power_attr(pm_test); | ||
127 | #else /* !CONFIG_PM_DEBUG */ | ||
128 | static inline int suspend_test(int level) { return 0; } | ||
129 | #endif /* !CONFIG_PM_DEBUG */ | ||
130 | |||
131 | #endif /* CONFIG_PM_SLEEP */ | ||
132 | |||
34 | #ifdef CONFIG_SUSPEND | 133 | #ifdef CONFIG_SUSPEND |
35 | 134 | ||
36 | /* This is just an arbitrary number */ | 135 | /* This is just an arbitrary number */ |
@@ -76,13 +175,13 @@ static int suspend_prepare(void) | |||
76 | if (!suspend_ops || !suspend_ops->enter) | 175 | if (!suspend_ops || !suspend_ops->enter) |
77 | return -EPERM; | 176 | return -EPERM; |
78 | 177 | ||
178 | pm_prepare_console(); | ||
179 | |||
79 | error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); | 180 | error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); |
80 | if (error) | 181 | if (error) |
81 | goto Finish; | 182 | goto Finish; |
82 | 183 | ||
83 | pm_prepare_console(); | 184 | if (suspend_freeze_processes()) { |
84 | |||
85 | if (freeze_processes()) { | ||
86 | error = -EAGAIN; | 185 | error = -EAGAIN; |
87 | goto Thaw; | 186 | goto Thaw; |
88 | } | 187 | } |
@@ -100,10 +199,10 @@ static int suspend_prepare(void) | |||
100 | return 0; | 199 | return 0; |
101 | 200 | ||
102 | Thaw: | 201 | Thaw: |
103 | thaw_processes(); | 202 | suspend_thaw_processes(); |
104 | pm_restore_console(); | ||
105 | Finish: | 203 | Finish: |
106 | pm_notifier_call_chain(PM_POST_SUSPEND); | 204 | pm_notifier_call_chain(PM_POST_SUSPEND); |
205 | pm_restore_console(); | ||
107 | return error; | 206 | return error; |
108 | } | 207 | } |
109 | 208 | ||
@@ -133,10 +232,13 @@ static int suspend_enter(suspend_state_t state) | |||
133 | BUG_ON(!irqs_disabled()); | 232 | BUG_ON(!irqs_disabled()); |
134 | 233 | ||
135 | if ((error = device_power_down(PMSG_SUSPEND))) { | 234 | if ((error = device_power_down(PMSG_SUSPEND))) { |
136 | printk(KERN_ERR "Some devices failed to power down\n"); | 235 | printk(KERN_ERR "PM: Some devices failed to power down\n"); |
137 | goto Done; | 236 | goto Done; |
138 | } | 237 | } |
139 | error = suspend_ops->enter(state); | 238 | |
239 | if (!suspend_test(TEST_CORE)) | ||
240 | error = suspend_ops->enter(state); | ||
241 | |||
140 | device_power_up(); | 242 | device_power_up(); |
141 | Done: | 243 | Done: |
142 | arch_suspend_enable_irqs(); | 244 | arch_suspend_enable_irqs(); |
@@ -145,8 +247,8 @@ static int suspend_enter(suspend_state_t state) | |||
145 | } | 247 | } |
146 | 248 | ||
147 | /** | 249 | /** |
148 | * suspend_devices_and_enter - suspend devices and enter the desired system sleep | 250 | * suspend_devices_and_enter - suspend devices and enter the desired system |
149 | * state. | 251 | * sleep state. |
150 | * @state: state to enter | 252 | * @state: state to enter |
151 | */ | 253 | */ |
152 | int suspend_devices_and_enter(suspend_state_t state) | 254 | int suspend_devices_and_enter(suspend_state_t state) |
@@ -156,33 +258,45 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
156 | if (!suspend_ops) | 258 | if (!suspend_ops) |
157 | return -ENOSYS; | 259 | return -ENOSYS; |
158 | 260 | ||
159 | if (suspend_ops->set_target) { | 261 | if (suspend_ops->begin) { |
160 | error = suspend_ops->set_target(state); | 262 | error = suspend_ops->begin(state); |
161 | if (error) | 263 | if (error) |
162 | return error; | 264 | goto Close; |
163 | } | 265 | } |
164 | suspend_console(); | 266 | suspend_console(); |
165 | error = device_suspend(PMSG_SUSPEND); | 267 | error = device_suspend(PMSG_SUSPEND); |
166 | if (error) { | 268 | if (error) { |
167 | printk(KERN_ERR "Some devices failed to suspend\n"); | 269 | printk(KERN_ERR "PM: Some devices failed to suspend\n"); |
168 | goto Resume_console; | 270 | goto Resume_console; |
169 | } | 271 | } |
272 | |||
273 | if (suspend_test(TEST_DEVICES)) | ||
274 | goto Resume_devices; | ||
275 | |||
170 | if (suspend_ops->prepare) { | 276 | if (suspend_ops->prepare) { |
171 | error = suspend_ops->prepare(); | 277 | error = suspend_ops->prepare(); |
172 | if (error) | 278 | if (error) |
173 | goto Resume_devices; | 279 | goto Resume_devices; |
174 | } | 280 | } |
281 | |||
282 | if (suspend_test(TEST_PLATFORM)) | ||
283 | goto Finish; | ||
284 | |||
175 | error = disable_nonboot_cpus(); | 285 | error = disable_nonboot_cpus(); |
176 | if (!error) | 286 | if (!error && !suspend_test(TEST_CPUS)) |
177 | suspend_enter(state); | 287 | suspend_enter(state); |
178 | 288 | ||
179 | enable_nonboot_cpus(); | 289 | enable_nonboot_cpus(); |
290 | Finish: | ||
180 | if (suspend_ops->finish) | 291 | if (suspend_ops->finish) |
181 | suspend_ops->finish(); | 292 | suspend_ops->finish(); |
182 | Resume_devices: | 293 | Resume_devices: |
183 | device_resume(); | 294 | device_resume(); |
184 | Resume_console: | 295 | Resume_console: |
185 | resume_console(); | 296 | resume_console(); |
297 | Close: | ||
298 | if (suspend_ops->end) | ||
299 | suspend_ops->end(); | ||
186 | return error; | 300 | return error; |
187 | } | 301 | } |
188 | 302 | ||
@@ -194,9 +308,9 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
194 | */ | 308 | */ |
195 | static void suspend_finish(void) | 309 | static void suspend_finish(void) |
196 | { | 310 | { |
197 | thaw_processes(); | 311 | suspend_thaw_processes(); |
198 | pm_restore_console(); | ||
199 | pm_notifier_call_chain(PM_POST_SUSPEND); | 312 | pm_notifier_call_chain(PM_POST_SUSPEND); |
313 | pm_restore_console(); | ||
200 | } | 314 | } |
201 | 315 | ||
202 | 316 | ||
@@ -238,17 +352,22 @@ static int enter_state(suspend_state_t state) | |||
238 | if (!mutex_trylock(&pm_mutex)) | 352 | if (!mutex_trylock(&pm_mutex)) |
239 | return -EBUSY; | 353 | return -EBUSY; |
240 | 354 | ||
241 | printk("Syncing filesystems ... "); | 355 | printk(KERN_INFO "PM: Syncing filesystems ... "); |
242 | sys_sync(); | 356 | sys_sync(); |
243 | printk("done.\n"); | 357 | printk("done.\n"); |
244 | 358 | ||
245 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); | 359 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); |
246 | if ((error = suspend_prepare())) | 360 | error = suspend_prepare(); |
361 | if (error) | ||
247 | goto Unlock; | 362 | goto Unlock; |
248 | 363 | ||
364 | if (suspend_test(TEST_FREEZER)) | ||
365 | goto Finish; | ||
366 | |||
249 | pr_debug("PM: Entering %s sleep\n", pm_states[state]); | 367 | pr_debug("PM: Entering %s sleep\n", pm_states[state]); |
250 | error = suspend_devices_and_enter(state); | 368 | error = suspend_devices_and_enter(state); |
251 | 369 | ||
370 | Finish: | ||
252 | pr_debug("PM: Finishing wakeup.\n"); | 371 | pr_debug("PM: Finishing wakeup.\n"); |
253 | suspend_finish(); | 372 | suspend_finish(); |
254 | Unlock: | 373 | Unlock: |
@@ -369,18 +488,18 @@ pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr, | |||
369 | } | 488 | } |
370 | 489 | ||
371 | power_attr(pm_trace); | 490 | power_attr(pm_trace); |
491 | #endif /* CONFIG_PM_TRACE */ | ||
372 | 492 | ||
373 | static struct attribute * g[] = { | 493 | static struct attribute * g[] = { |
374 | &state_attr.attr, | 494 | &state_attr.attr, |
495 | #ifdef CONFIG_PM_TRACE | ||
375 | &pm_trace_attr.attr, | 496 | &pm_trace_attr.attr, |
497 | #endif | ||
498 | #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG) | ||
499 | &pm_test_attr.attr, | ||
500 | #endif | ||
376 | NULL, | 501 | NULL, |
377 | }; | 502 | }; |
378 | #else | ||
379 | static struct attribute * g[] = { | ||
380 | &state_attr.attr, | ||
381 | NULL, | ||
382 | }; | ||
383 | #endif /* CONFIG_PM_TRACE */ | ||
384 | 503 | ||
385 | static struct attribute_group attr_group = { | 504 | static struct attribute_group attr_group = { |
386 | .attrs = g, | 505 | .attrs = g, |