aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-06-09 19:27:12 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2009-06-12 15:32:33 -0400
commita9d7052363a6e06bb623ed1876c56c7ca5b2c6d8 (patch)
treec4ec7fb30dc4510596ffc9ddffe308ef34837af2 /kernel
parent783ea7d4eeefe895f2731fe73ac951e94418927b (diff)
PM: Separate suspend to RAM functionality from core
Move the suspend to RAM and standby code from kernel/power/main.c to two separate files, kernel/power/suspend.c containing the basic functions and kernel/power/suspend_test.c containing the automatic suspend test facility based on the RTC clock alarm. There are no changes in functionality related to these modifications. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Pavel Machek <pavel@ucw.cz>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/power/Makefile2
-rw-r--r--kernel/power/main.c503
-rw-r--r--kernel/power/power.h17
-rw-r--r--kernel/power/suspend.c300
-rw-r--r--kernel/power/suspend_test.c187
5 files changed, 505 insertions, 504 deletions
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 720ea4f781b..c4baf1b633c 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -6,6 +6,8 @@ endif
6obj-$(CONFIG_PM) += main.o 6obj-$(CONFIG_PM) += main.o
7obj-$(CONFIG_PM_SLEEP) += console.o 7obj-$(CONFIG_PM_SLEEP) += console.o
8obj-$(CONFIG_FREEZER) += process.o 8obj-$(CONFIG_FREEZER) += process.o
9obj-$(CONFIG_SUSPEND) += suspend.o
10obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o
9obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o 11obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
10 12
11obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o 13obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 2a19f347bd8..f710e36930c 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -8,20 +8,9 @@
8 * 8 *
9 */ 9 */
10 10
11#include <linux/module.h>
12#include <linux/suspend.h>
13#include <linux/kobject.h> 11#include <linux/kobject.h>
14#include <linux/string.h> 12#include <linux/string.h>
15#include <linux/delay.h>
16#include <linux/errno.h>
17#include <linux/kmod.h>
18#include <linux/init.h>
19#include <linux/console.h>
20#include <linux/cpu.h>
21#include <linux/resume-trace.h> 13#include <linux/resume-trace.h>
22#include <linux/freezer.h>
23#include <linux/vmstat.h>
24#include <linux/syscalls.h>
25 14
26#include "power.h" 15#include "power.h"
27 16
@@ -119,355 +108,6 @@ power_attr(pm_test);
119 108
120#endif /* CONFIG_PM_SLEEP */ 109#endif /* CONFIG_PM_SLEEP */
121 110
122#ifdef CONFIG_SUSPEND
123
124static int suspend_test(int level)
125{
126#ifdef CONFIG_PM_DEBUG
127 if (pm_test_level == level) {
128 printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
129 mdelay(5000);
130 return 1;
131 }
132#endif /* !CONFIG_PM_DEBUG */
133 return 0;
134}
135
136#ifdef CONFIG_PM_TEST_SUSPEND
137
138/*
139 * We test the system suspend code by setting an RTC wakealarm a short
140 * time in the future, then suspending. Suspending the devices won't
141 * normally take long ... some systems only need a few milliseconds.
142 *
143 * The time it takes is system-specific though, so when we test this
144 * during system bootup we allow a LOT of time.
145 */
146#define TEST_SUSPEND_SECONDS 5
147
148static unsigned long suspend_test_start_time;
149
150static void suspend_test_start(void)
151{
152 /* FIXME Use better timebase than "jiffies", ideally a clocksource.
153 * What we want is a hardware counter that will work correctly even
154 * during the irqs-are-off stages of the suspend/resume cycle...
155 */
156 suspend_test_start_time = jiffies;
157}
158
159static void suspend_test_finish(const char *label)
160{
161 long nj = jiffies - suspend_test_start_time;
162 unsigned msec;
163
164 msec = jiffies_to_msecs(abs(nj));
165 pr_info("PM: %s took %d.%03d seconds\n", label,
166 msec / 1000, msec % 1000);
167
168 /* Warning on suspend means the RTC alarm period needs to be
169 * larger -- the system was sooo slooowwww to suspend that the
170 * alarm (should have) fired before the system went to sleep!
171 *
172 * Warning on either suspend or resume also means the system
173 * has some performance issues. The stack dump of a WARN_ON
174 * is more likely to get the right attention than a printk...
175 */
176 WARN(msec > (TEST_SUSPEND_SECONDS * 1000), "Component: %s\n", label);
177}
178
179#else
180
181static void suspend_test_start(void)
182{
183}
184
185static void suspend_test_finish(const char *label)
186{
187}
188
189#endif
190
191static struct platform_suspend_ops *suspend_ops;
192
193/**
194 * suspend_set_ops - Set the global suspend method table.
195 * @ops: Pointer to ops structure.
196 */
197
198void suspend_set_ops(struct platform_suspend_ops *ops)
199{
200 mutex_lock(&pm_mutex);
201 suspend_ops = ops;
202 mutex_unlock(&pm_mutex);
203}
204
205/**
206 * suspend_valid_only_mem - generic memory-only valid callback
207 *
208 * Platform drivers that implement mem suspend only and only need
209 * to check for that in their .valid callback can use this instead
210 * of rolling their own .valid callback.
211 */
212int suspend_valid_only_mem(suspend_state_t state)
213{
214 return state == PM_SUSPEND_MEM;
215}
216
217/**
218 * suspend_prepare - Do prep work before entering low-power state.
219 *
220 * This is common code that is called for each state that we're entering.
221 * Run suspend notifiers, allocate a console and stop all processes.
222 */
223static int suspend_prepare(void)
224{
225 int error;
226
227 if (!suspend_ops || !suspend_ops->enter)
228 return -EPERM;
229
230 pm_prepare_console();
231
232 error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
233 if (error)
234 goto Finish;
235
236 error = usermodehelper_disable();
237 if (error)
238 goto Finish;
239
240 error = suspend_freeze_processes();
241 if (!error)
242 return 0;
243
244 suspend_thaw_processes();
245 usermodehelper_enable();
246 Finish:
247 pm_notifier_call_chain(PM_POST_SUSPEND);
248 pm_restore_console();
249 return error;
250}
251
252/* default implementation */
253void __attribute__ ((weak)) arch_suspend_disable_irqs(void)
254{
255 local_irq_disable();
256}
257
258/* default implementation */
259void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
260{
261 local_irq_enable();
262}
263
264/**
265 * suspend_enter - enter the desired system sleep state.
266 * @state: state to enter
267 *
268 * This function should be called after devices have been suspended.
269 */
270static int suspend_enter(suspend_state_t state)
271{
272 int error;
273
274 if (suspend_ops->prepare) {
275 error = suspend_ops->prepare();
276 if (error)
277 return error;
278 }
279
280 error = dpm_suspend_noirq(PMSG_SUSPEND);
281 if (error) {
282 printk(KERN_ERR "PM: Some devices failed to power down\n");
283 goto Platfrom_finish;
284 }
285
286 if (suspend_ops->prepare_late) {
287 error = suspend_ops->prepare_late();
288 if (error)
289 goto Power_up_devices;
290 }
291
292 if (suspend_test(TEST_PLATFORM))
293 goto Platform_wake;
294
295 error = disable_nonboot_cpus();
296 if (error || suspend_test(TEST_CPUS))
297 goto Enable_cpus;
298
299 arch_suspend_disable_irqs();
300 BUG_ON(!irqs_disabled());
301
302 error = sysdev_suspend(PMSG_SUSPEND);
303 if (!error) {
304 if (!suspend_test(TEST_CORE))
305 error = suspend_ops->enter(state);
306 sysdev_resume();
307 }
308
309 arch_suspend_enable_irqs();
310 BUG_ON(irqs_disabled());
311
312 Enable_cpus:
313 enable_nonboot_cpus();
314
315 Platform_wake:
316 if (suspend_ops->wake)
317 suspend_ops->wake();
318
319 Power_up_devices:
320 dpm_resume_noirq(PMSG_RESUME);
321
322 Platfrom_finish:
323 if (suspend_ops->finish)
324 suspend_ops->finish();
325
326 return error;
327}
328
329/**
330 * suspend_devices_and_enter - suspend devices and enter the desired system
331 * sleep state.
332 * @state: state to enter
333 */
334int suspend_devices_and_enter(suspend_state_t state)
335{
336 int error;
337
338 if (!suspend_ops)
339 return -ENOSYS;
340
341 if (suspend_ops->begin) {
342 error = suspend_ops->begin(state);
343 if (error)
344 goto Close;
345 }
346 suspend_console();
347 suspend_test_start();
348 error = dpm_suspend_start(PMSG_SUSPEND);
349 if (error) {
350 printk(KERN_ERR "PM: Some devices failed to suspend\n");
351 goto Recover_platform;
352 }
353 suspend_test_finish("suspend devices");
354 if (suspend_test(TEST_DEVICES))
355 goto Recover_platform;
356
357 suspend_enter(state);
358
359 Resume_devices:
360 suspend_test_start();
361 dpm_resume_end(PMSG_RESUME);
362 suspend_test_finish("resume devices");
363 resume_console();
364 Close:
365 if (suspend_ops->end)
366 suspend_ops->end();
367 return error;
368
369 Recover_platform:
370 if (suspend_ops->recover)
371 suspend_ops->recover();
372 goto Resume_devices;
373}
374
375/**
376 * suspend_finish - Do final work before exiting suspend sequence.
377 *
378 * Call platform code to clean up, restart processes, and free the
379 * console that we've allocated. This is not called for suspend-to-disk.
380 */
381static void suspend_finish(void)
382{
383 suspend_thaw_processes();
384 usermodehelper_enable();
385 pm_notifier_call_chain(PM_POST_SUSPEND);
386 pm_restore_console();
387}
388
389
390
391
392static const char * const pm_states[PM_SUSPEND_MAX] = {
393 [PM_SUSPEND_STANDBY] = "standby",
394 [PM_SUSPEND_MEM] = "mem",
395};
396
397static inline int valid_state(suspend_state_t state)
398{
399 /* All states need lowlevel support and need to be valid
400 * to the lowlevel implementation, no valid callback
401 * implies that none are valid. */
402 if (!suspend_ops || !suspend_ops->valid || !suspend_ops->valid(state))
403 return 0;
404 return 1;
405}
406
407
408/**
409 * enter_state - Do common work of entering low-power state.
410 * @state: pm_state structure for state we're entering.
411 *
412 * Make sure we're the only ones trying to enter a sleep state. Fail
413 * if someone has beat us to it, since we don't want anything weird to
414 * happen when we wake up.
415 * Then, do the setup for suspend, enter the state, and cleaup (after
416 * we've woken up).
417 */
418static int enter_state(suspend_state_t state)
419{
420 int error;
421
422 if (!valid_state(state))
423 return -ENODEV;
424
425 if (!mutex_trylock(&pm_mutex))
426 return -EBUSY;
427
428 printk(KERN_INFO "PM: Syncing filesystems ... ");
429 sys_sync();
430 printk("done.\n");
431
432 pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
433 error = suspend_prepare();
434 if (error)
435 goto Unlock;
436
437 if (suspend_test(TEST_FREEZER))
438 goto Finish;
439
440 pr_debug("PM: Entering %s sleep\n", pm_states[state]);
441 error = suspend_devices_and_enter(state);
442
443 Finish:
444 pr_debug("PM: Finishing wakeup.\n");
445 suspend_finish();
446 Unlock:
447 mutex_unlock(&pm_mutex);
448 return error;
449}
450
451
452/**
453 * pm_suspend - Externally visible function for suspending system.
454 * @state: Enumerated value of state to enter.
455 *
456 * Determine whether or not value is within range, get state
457 * structure, and enter (above).
458 */
459
460int pm_suspend(suspend_state_t state)
461{
462 if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
463 return enter_state(state);
464 return -EINVAL;
465}
466
467EXPORT_SYMBOL(pm_suspend);
468
469#endif /* CONFIG_SUSPEND */
470
471struct kobject *power_kobj; 111struct kobject *power_kobj;
472 112
473/** 113/**
@@ -480,7 +120,6 @@ struct kobject *power_kobj;
480 * store() accepts one of those strings, translates it into the 120 * store() accepts one of those strings, translates it into the
481 * proper enumerated value, and initiates a suspend transition. 121 * proper enumerated value, and initiates a suspend transition.
482 */ 122 */
483
484static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, 123static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
485 char *buf) 124 char *buf)
486{ 125{
@@ -578,7 +217,6 @@ static struct attribute_group attr_group = {
578 .attrs = g, 217 .attrs = g,
579}; 218};
580 219
581
582static int __init pm_init(void) 220static int __init pm_init(void)
583{ 221{
584 power_kobj = kobject_create_and_add("power", NULL); 222 power_kobj = kobject_create_and_add("power", NULL);
@@ -588,144 +226,3 @@ static int __init pm_init(void)
588} 226}
589 227
590core_initcall(pm_init); 228core_initcall(pm_init);
591
592
593#ifdef CONFIG_PM_TEST_SUSPEND
594
595#include <linux/rtc.h>
596
597/*
598 * To test system suspend, we need a hands-off mechanism to resume the
599 * system. RTCs wake alarms are a common self-contained mechanism.
600 */
601
602static void __init test_wakealarm(struct rtc_device *rtc, suspend_state_t state)
603{
604 static char err_readtime[] __initdata =
605 KERN_ERR "PM: can't read %s time, err %d\n";
606 static char err_wakealarm [] __initdata =
607 KERN_ERR "PM: can't set %s wakealarm, err %d\n";
608 static char err_suspend[] __initdata =
609 KERN_ERR "PM: suspend test failed, error %d\n";
610 static char info_test[] __initdata =
611 KERN_INFO "PM: test RTC wakeup from '%s' suspend\n";
612
613 unsigned long now;
614 struct rtc_wkalrm alm;
615 int status;
616
617 /* this may fail if the RTC hasn't been initialized */
618 status = rtc_read_time(rtc, &alm.time);
619 if (status < 0) {
620 printk(err_readtime, dev_name(&rtc->dev), status);
621 return;
622 }
623 rtc_tm_to_time(&alm.time, &now);
624
625 memset(&alm, 0, sizeof alm);
626 rtc_time_to_tm(now + TEST_SUSPEND_SECONDS, &alm.time);
627 alm.enabled = true;
628
629 status = rtc_set_alarm(rtc, &alm);
630 if (status < 0) {
631 printk(err_wakealarm, dev_name(&rtc->dev), status);
632 return;
633 }
634
635 if (state == PM_SUSPEND_MEM) {
636 printk(info_test, pm_states[state]);
637 status = pm_suspend(state);
638 if (status == -ENODEV)
639 state = PM_SUSPEND_STANDBY;
640 }
641 if (state == PM_SUSPEND_STANDBY) {
642 printk(info_test, pm_states[state]);
643 status = pm_suspend(state);
644 }
645 if (status < 0)
646 printk(err_suspend, status);
647
648 /* Some platforms can't detect that the alarm triggered the
649 * wakeup, or (accordingly) disable it after it afterwards.
650 * It's supposed to give oneshot behavior; cope.
651 */
652 alm.enabled = false;
653 rtc_set_alarm(rtc, &alm);
654}
655
656static int __init has_wakealarm(struct device *dev, void *name_ptr)
657{
658 struct rtc_device *candidate = to_rtc_device(dev);
659
660 if (!candidate->ops->set_alarm)
661 return 0;
662 if (!device_may_wakeup(candidate->dev.parent))
663 return 0;
664
665 *(const char **)name_ptr = dev_name(dev);
666 return 1;
667}
668
669/*
670 * Kernel options like "test_suspend=mem" force suspend/resume sanity tests
671 * at startup time. They're normally disabled, for faster boot and because
672 * we can't know which states really work on this particular system.
673 */
674static suspend_state_t test_state __initdata = PM_SUSPEND_ON;
675
676static char warn_bad_state[] __initdata =
677 KERN_WARNING "PM: can't test '%s' suspend state\n";
678
679static int __init setup_test_suspend(char *value)
680{
681 unsigned i;
682
683 /* "=mem" ==> "mem" */
684 value++;
685 for (i = 0; i < PM_SUSPEND_MAX; i++) {
686 if (!pm_states[i])
687 continue;
688 if (strcmp(pm_states[i], value) != 0)
689 continue;
690 test_state = (__force suspend_state_t) i;
691 return 0;
692 }
693 printk(warn_bad_state, value);
694 return 0;
695}
696__setup("test_suspend", setup_test_suspend);
697
698static int __init test_suspend(void)
699{
700 static char warn_no_rtc[] __initdata =
701 KERN_WARNING "PM: no wakealarm-capable RTC driver is ready\n";
702
703 char *pony = NULL;
704 struct rtc_device *rtc = NULL;
705
706 /* PM is initialized by now; is that state testable? */
707 if (test_state == PM_SUSPEND_ON)
708 goto done;
709 if (!valid_state(test_state)) {
710 printk(warn_bad_state, pm_states[test_state]);
711 goto done;
712 }
713
714 /* RTCs have initialized by now too ... can we use one? */
715 class_find_device(rtc_class, NULL, &pony, has_wakealarm);
716 if (pony)
717 rtc = rtc_class_open(pony);
718 if (!rtc) {
719 printk(warn_no_rtc);
720 goto done;
721 }
722
723 /* go for it */
724 test_wakealarm(rtc, test_state);
725 rtc_class_close(rtc);
726done:
727 return 0;
728}
729late_initcall(test_suspend);
730
731#endif /* CONFIG_PM_TEST_SUSPEND */
diff --git a/kernel/power/power.h b/kernel/power/power.h
index ec4dbdfb07b..2bd98d9fc19 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -160,15 +160,30 @@ extern void swsusp_show_speed(struct timeval *, struct timeval *,
160 unsigned int, char *); 160 unsigned int, char *);
161 161
162#ifdef CONFIG_SUSPEND 162#ifdef CONFIG_SUSPEND
163/* kernel/power/main.c */ 163/* kernel/power/suspend.c */
164extern const char *const pm_states[];
165
166extern bool valid_state(suspend_state_t state);
164extern int suspend_devices_and_enter(suspend_state_t state); 167extern int suspend_devices_and_enter(suspend_state_t state);
168extern int enter_state(suspend_state_t state);
165#else /* !CONFIG_SUSPEND */ 169#else /* !CONFIG_SUSPEND */
166static inline int suspend_devices_and_enter(suspend_state_t state) 170static inline int suspend_devices_and_enter(suspend_state_t state)
167{ 171{
168 return -ENOSYS; 172 return -ENOSYS;
169} 173}
174static inline int enter_state(suspend_state_t state) { return -ENOSYS; }
175static inline bool valid_state(suspend_state_t state) { return false; }
170#endif /* !CONFIG_SUSPEND */ 176#endif /* !CONFIG_SUSPEND */
171 177
178#ifdef CONFIG_PM_TEST_SUSPEND
179/* kernel/power/suspend_test.c */
180extern void suspend_test_start(void);
181extern void suspend_test_finish(const char *label);
182#else /* !CONFIG_PM_TEST_SUSPEND */
183static inline void suspend_test_start(void) {}
184static inline void suspend_test_finish(const char *label) {}
185#endif /* !CONFIG_PM_TEST_SUSPEND */
186
172#ifdef CONFIG_PM_SLEEP 187#ifdef CONFIG_PM_SLEEP
173/* kernel/power/main.c */ 188/* kernel/power/main.c */
174extern int pm_notifier_call_chain(unsigned long val); 189extern int pm_notifier_call_chain(unsigned long val);
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
new file mode 100644
index 00000000000..6f10dfc2d3e
--- /dev/null
+++ b/kernel/power/suspend.c
@@ -0,0 +1,300 @@
1/*
2 * kernel/power/suspend.c - Suspend to RAM and standby functionality.
3 *
4 * Copyright (c) 2003 Patrick Mochel
5 * Copyright (c) 2003 Open Source Development Lab
6 * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
7 *
8 * This file is released under the GPLv2.
9 */
10
11#include <linux/string.h>
12#include <linux/delay.h>
13#include <linux/errno.h>
14#include <linux/init.h>
15#include <linux/console.h>
16#include <linux/cpu.h>
17#include <linux/syscalls.h>
18
19#include "power.h"
20
21const char *const pm_states[PM_SUSPEND_MAX] = {
22 [PM_SUSPEND_STANDBY] = "standby",
23 [PM_SUSPEND_MEM] = "mem",
24};
25
26static struct platform_suspend_ops *suspend_ops;
27
28/**
29 * suspend_set_ops - Set the global suspend method table.
30 * @ops: Pointer to ops structure.
31 */
32void suspend_set_ops(struct platform_suspend_ops *ops)
33{
34 mutex_lock(&pm_mutex);
35 suspend_ops = ops;
36 mutex_unlock(&pm_mutex);
37}
38
39bool valid_state(suspend_state_t state)
40{
41 /*
42 * All states need lowlevel support and need to be valid to the lowlevel
43 * implementation, no valid callback implies that none are valid.
44 */
45 return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
46}
47
48/**
49 * suspend_valid_only_mem - generic memory-only valid callback
50 *
51 * Platform drivers that implement mem suspend only and only need
52 * to check for that in their .valid callback can use this instead
53 * of rolling their own .valid callback.
54 */
55int suspend_valid_only_mem(suspend_state_t state)
56{
57 return state == PM_SUSPEND_MEM;
58}
59
60static int suspend_test(int level)
61{
62#ifdef CONFIG_PM_DEBUG
63 if (pm_test_level == level) {
64 printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
65 mdelay(5000);
66 return 1;
67 }
68#endif /* !CONFIG_PM_DEBUG */
69 return 0;
70}
71
72/**
73 * suspend_prepare - Do prep work before entering low-power state.
74 *
75 * This is common code that is called for each state that we're entering.
76 * Run suspend notifiers, allocate a console and stop all processes.
77 */
78static int suspend_prepare(void)
79{
80 int error;
81
82 if (!suspend_ops || !suspend_ops->enter)
83 return -EPERM;
84
85 pm_prepare_console();
86
87 error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
88 if (error)
89 goto Finish;
90
91 error = usermodehelper_disable();
92 if (error)
93 goto Finish;
94
95 error = suspend_freeze_processes();
96 if (!error)
97 return 0;
98
99 suspend_thaw_processes();
100 usermodehelper_enable();
101 Finish:
102 pm_notifier_call_chain(PM_POST_SUSPEND);
103 pm_restore_console();
104 return error;
105}
106
107/* default implementation */
108void __attribute__ ((weak)) arch_suspend_disable_irqs(void)
109{
110 local_irq_disable();
111}
112
113/* default implementation */
114void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
115{
116 local_irq_enable();
117}
118
119/**
120 * suspend_enter - enter the desired system sleep state.
121 * @state: state to enter
122 *
123 * This function should be called after devices have been suspended.
124 */
125static int suspend_enter(suspend_state_t state)
126{
127 int error;
128
129 if (suspend_ops->prepare) {
130 error = suspend_ops->prepare();
131 if (error)
132 return error;
133 }
134
135 error = dpm_suspend_noirq(PMSG_SUSPEND);
136 if (error) {
137 printk(KERN_ERR "PM: Some devices failed to power down\n");
138 goto Platfrom_finish;
139 }
140
141 if (suspend_ops->prepare_late) {
142 error = suspend_ops->prepare_late();
143 if (error)
144 goto Power_up_devices;
145 }
146
147 if (suspend_test(TEST_PLATFORM))
148 goto Platform_wake;
149
150 error = disable_nonboot_cpus();
151 if (error || suspend_test(TEST_CPUS))
152 goto Enable_cpus;
153
154 arch_suspend_disable_irqs();
155 BUG_ON(!irqs_disabled());
156
157 error = sysdev_suspend(PMSG_SUSPEND);
158 if (!error) {
159 if (!suspend_test(TEST_CORE))
160 error = suspend_ops->enter(state);
161 sysdev_resume();
162 }
163
164 arch_suspend_enable_irqs();
165 BUG_ON(irqs_disabled());
166
167 Enable_cpus:
168 enable_nonboot_cpus();
169
170 Platform_wake:
171 if (suspend_ops->wake)
172 suspend_ops->wake();
173
174 Power_up_devices:
175 dpm_resume_noirq(PMSG_RESUME);
176
177 Platfrom_finish:
178 if (suspend_ops->finish)
179 suspend_ops->finish();
180
181 return error;
182}
183
184/**
185 * suspend_devices_and_enter - suspend devices and enter the desired system
186 * sleep state.
187 * @state: state to enter
188 */
189int suspend_devices_and_enter(suspend_state_t state)
190{
191 int error;
192
193 if (!suspend_ops)
194 return -ENOSYS;
195
196 if (suspend_ops->begin) {
197 error = suspend_ops->begin(state);
198 if (error)
199 goto Close;
200 }
201 suspend_console();
202 suspend_test_start();
203 error = dpm_suspend_start(PMSG_SUSPEND);
204 if (error) {
205 printk(KERN_ERR "PM: Some devices failed to suspend\n");
206 goto Recover_platform;
207 }
208 suspend_test_finish("suspend devices");
209 if (suspend_test(TEST_DEVICES))
210 goto Recover_platform;
211
212 suspend_enter(state);
213
214 Resume_devices:
215 suspend_test_start();
216 dpm_resume_end(PMSG_RESUME);
217 suspend_test_finish("resume devices");
218 resume_console();
219 Close:
220 if (suspend_ops->end)
221 suspend_ops->end();
222 return error;
223
224 Recover_platform:
225 if (suspend_ops->recover)
226 suspend_ops->recover();
227 goto Resume_devices;
228}
229
230/**
231 * suspend_finish - Do final work before exiting suspend sequence.
232 *
233 * Call platform code to clean up, restart processes, and free the
234 * console that we've allocated. This is not called for suspend-to-disk.
235 */
236static void suspend_finish(void)
237{
238 suspend_thaw_processes();
239 usermodehelper_enable();
240 pm_notifier_call_chain(PM_POST_SUSPEND);
241 pm_restore_console();
242}
243
244/**
245 * enter_state - Do common work of entering low-power state.
246 * @state: pm_state structure for state we're entering.
247 *
248 * Make sure we're the only ones trying to enter a sleep state. Fail
249 * if someone has beat us to it, since we don't want anything weird to
250 * happen when we wake up.
251 * Then, do the setup for suspend, enter the state, and cleaup (after
252 * we've woken up).
253 */
254int enter_state(suspend_state_t state)
255{
256 int error;
257
258 if (!valid_state(state))
259 return -ENODEV;
260
261 if (!mutex_trylock(&pm_mutex))
262 return -EBUSY;
263
264 printk(KERN_INFO "PM: Syncing filesystems ... ");
265 sys_sync();
266 printk("done.\n");
267
268 pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
269 error = suspend_prepare();
270 if (error)
271 goto Unlock;
272
273 if (suspend_test(TEST_FREEZER))
274 goto Finish;
275
276 pr_debug("PM: Entering %s sleep\n", pm_states[state]);
277 error = suspend_devices_and_enter(state);
278
279 Finish:
280 pr_debug("PM: Finishing wakeup.\n");
281 suspend_finish();
282 Unlock:
283 mutex_unlock(&pm_mutex);
284 return error;
285}
286
287/**
288 * pm_suspend - Externally visible function for suspending system.
289 * @state: Enumerated value of state to enter.
290 *
291 * Determine whether or not value is within range, get state
292 * structure, and enter (above).
293 */
294int pm_suspend(suspend_state_t state)
295{
296 if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
297 return enter_state(state);
298 return -EINVAL;
299}
300EXPORT_SYMBOL(pm_suspend);
diff --git a/kernel/power/suspend_test.c b/kernel/power/suspend_test.c
new file mode 100644
index 00000000000..17d8bb1acf9
--- /dev/null
+++ b/kernel/power/suspend_test.c
@@ -0,0 +1,187 @@
1/*
2 * kernel/power/suspend_test.c - Suspend to RAM and standby test facility.
3 *
4 * Copyright (c) 2009 Pavel Machek <pavel@ucw.cz>
5 *
6 * This file is released under the GPLv2.
7 */
8
9#include <linux/init.h>
10#include <linux/rtc.h>
11
12#include "power.h"
13
14/*
15 * We test the system suspend code by setting an RTC wakealarm a short
16 * time in the future, then suspending. Suspending the devices won't
17 * normally take long ... some systems only need a few milliseconds.
18 *
19 * The time it takes is system-specific though, so when we test this
20 * during system bootup we allow a LOT of time.
21 */
22#define TEST_SUSPEND_SECONDS 5
23
24static unsigned long suspend_test_start_time;
25
26void suspend_test_start(void)
27{
28 /* FIXME Use better timebase than "jiffies", ideally a clocksource.
29 * What we want is a hardware counter that will work correctly even
30 * during the irqs-are-off stages of the suspend/resume cycle...
31 */
32 suspend_test_start_time = jiffies;
33}
34
35void suspend_test_finish(const char *label)
36{
37 long nj = jiffies - suspend_test_start_time;
38 unsigned msec;
39
40 msec = jiffies_to_msecs(abs(nj));
41 pr_info("PM: %s took %d.%03d seconds\n", label,
42 msec / 1000, msec % 1000);
43
44 /* Warning on suspend means the RTC alarm period needs to be
45 * larger -- the system was sooo slooowwww to suspend that the
46 * alarm (should have) fired before the system went to sleep!
47 *
48 * Warning on either suspend or resume also means the system
49 * has some performance issues. The stack dump of a WARN_ON
50 * is more likely to get the right attention than a printk...
51 */
52 WARN(msec > (TEST_SUSPEND_SECONDS * 1000), "Component: %s\n", label);
53}
54
55/*
56 * To test system suspend, we need a hands-off mechanism to resume the
57 * system. RTCs wake alarms are a common self-contained mechanism.
58 */
59
60static void __init test_wakealarm(struct rtc_device *rtc, suspend_state_t state)
61{
62 static char err_readtime[] __initdata =
63 KERN_ERR "PM: can't read %s time, err %d\n";
64 static char err_wakealarm [] __initdata =
65 KERN_ERR "PM: can't set %s wakealarm, err %d\n";
66 static char err_suspend[] __initdata =
67 KERN_ERR "PM: suspend test failed, error %d\n";
68 static char info_test[] __initdata =
69 KERN_INFO "PM: test RTC wakeup from '%s' suspend\n";
70
71 unsigned long now;
72 struct rtc_wkalrm alm;
73 int status;
74
75 /* this may fail if the RTC hasn't been initialized */
76 status = rtc_read_time(rtc, &alm.time);
77 if (status < 0) {
78 printk(err_readtime, dev_name(&rtc->dev), status);
79 return;
80 }
81 rtc_tm_to_time(&alm.time, &now);
82
83 memset(&alm, 0, sizeof alm);
84 rtc_time_to_tm(now + TEST_SUSPEND_SECONDS, &alm.time);
85 alm.enabled = true;
86
87 status = rtc_set_alarm(rtc, &alm);
88 if (status < 0) {
89 printk(err_wakealarm, dev_name(&rtc->dev), status);
90 return;
91 }
92
93 if (state == PM_SUSPEND_MEM) {
94 printk(info_test, pm_states[state]);
95 status = pm_suspend(state);
96 if (status == -ENODEV)
97 state = PM_SUSPEND_STANDBY;
98 }
99 if (state == PM_SUSPEND_STANDBY) {
100 printk(info_test, pm_states[state]);
101 status = pm_suspend(state);
102 }
103 if (status < 0)
104 printk(err_suspend, status);
105
106 /* Some platforms can't detect that the alarm triggered the
107 * wakeup, or (accordingly) disable it after it afterwards.
108 * It's supposed to give oneshot behavior; cope.
109 */
110 alm.enabled = false;
111 rtc_set_alarm(rtc, &alm);
112}
113
114static int __init has_wakealarm(struct device *dev, void *name_ptr)
115{
116 struct rtc_device *candidate = to_rtc_device(dev);
117
118 if (!candidate->ops->set_alarm)
119 return 0;
120 if (!device_may_wakeup(candidate->dev.parent))
121 return 0;
122
123 *(const char **)name_ptr = dev_name(dev);
124 return 1;
125}
126
127/*
128 * Kernel options like "test_suspend=mem" force suspend/resume sanity tests
129 * at startup time. They're normally disabled, for faster boot and because
130 * we can't know which states really work on this particular system.
131 */
132static suspend_state_t test_state __initdata = PM_SUSPEND_ON;
133
134static char warn_bad_state[] __initdata =
135 KERN_WARNING "PM: can't test '%s' suspend state\n";
136
137static int __init setup_test_suspend(char *value)
138{
139 unsigned i;
140
141 /* "=mem" ==> "mem" */
142 value++;
143 for (i = 0; i < PM_SUSPEND_MAX; i++) {
144 if (!pm_states[i])
145 continue;
146 if (strcmp(pm_states[i], value) != 0)
147 continue;
148 test_state = (__force suspend_state_t) i;
149 return 0;
150 }
151 printk(warn_bad_state, value);
152 return 0;
153}
154__setup("test_suspend", setup_test_suspend);
155
156static int __init test_suspend(void)
157{
158 static char warn_no_rtc[] __initdata =
159 KERN_WARNING "PM: no wakealarm-capable RTC driver is ready\n";
160
161 char *pony = NULL;
162 struct rtc_device *rtc = NULL;
163
164 /* PM is initialized by now; is that state testable? */
165 if (test_state == PM_SUSPEND_ON)
166 goto done;
167 if (!valid_state(test_state)) {
168 printk(warn_bad_state, pm_states[test_state]);
169 goto done;
170 }
171
172 /* RTCs have initialized by now too ... can we use one? */
173 class_find_device(rtc_class, NULL, &pony, has_wakealarm);
174 if (pony)
175 rtc = rtc_class_open(pony);
176 if (!rtc) {
177 printk(warn_no_rtc);
178 goto done;
179 }
180
181 /* go for it */
182 test_wakealarm(rtc, test_state);
183 rtc_class_close(rtc);
184done:
185 return 0;
186}
187late_initcall(test_suspend);