Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
suspend.c
Go to the documentation of this file.
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 <[email protected]>, 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 #include <linux/gfp.h>
19 #include <linux/io.h>
20 #include <linux/kernel.h>
21 #include <linux/list.h>
22 #include <linux/mm.h>
23 #include <linux/slab.h>
24 #include <linux/export.h>
25 #include <linux/suspend.h>
26 #include <linux/syscore_ops.h>
27 #include <linux/ftrace.h>
28 #include <trace/events/power.h>
29 
30 #include "power.h"
31 
32 const char *const pm_states[PM_SUSPEND_MAX] = {
33  [PM_SUSPEND_STANDBY] = "standby",
34  [PM_SUSPEND_MEM] = "mem",
35 };
36 
37 static const struct platform_suspend_ops *suspend_ops;
38 
43 void suspend_set_ops(const struct platform_suspend_ops *ops)
44 {
45  lock_system_sleep();
46  suspend_ops = ops;
47  unlock_system_sleep();
48 }
50 
52 {
53  /*
54  * All states need lowlevel support and need to be valid to the lowlevel
55  * implementation, no valid callback implies that none are valid.
56  */
57  return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
58 }
59 
68 {
69  return state == PM_SUSPEND_MEM;
70 }
72 
73 static int suspend_test(int level)
74 {
75 #ifdef CONFIG_PM_DEBUG
76  if (pm_test_level == level) {
77  printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
78  mdelay(5000);
79  return 1;
80  }
81 #endif /* !CONFIG_PM_DEBUG */
82  return 0;
83 }
84 
92 static int suspend_prepare(void)
93 {
94  int error;
95 
96  if (!suspend_ops || !suspend_ops->enter)
97  return -EPERM;
98 
100 
101  error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
102  if (error)
103  goto Finish;
104 
105  error = suspend_freeze_processes();
106  if (!error)
107  return 0;
108 
110  dpm_save_failed_step(SUSPEND_FREEZE);
111  Finish:
112  pm_notifier_call_chain(PM_POST_SUSPEND);
114  return error;
115 }
116 
117 /* default implementation */
119 {
121 }
122 
123 /* default implementation */
124 void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
125 {
127 }
128 
136 static int suspend_enter(suspend_state_t state, bool *wakeup)
137 {
138  int error;
139 
140  if (suspend_ops->prepare) {
141  error = suspend_ops->prepare();
142  if (error)
143  goto Platform_finish;
144  }
145 
146  error = dpm_suspend_end(PMSG_SUSPEND);
147  if (error) {
148  printk(KERN_ERR "PM: Some devices failed to power down\n");
149  goto Platform_finish;
150  }
151 
152  if (suspend_ops->prepare_late) {
153  error = suspend_ops->prepare_late();
154  if (error)
155  goto Platform_wake;
156  }
157 
158  if (suspend_test(TEST_PLATFORM))
159  goto Platform_wake;
160 
161  error = disable_nonboot_cpus();
162  if (error || suspend_test(TEST_CPUS))
163  goto Enable_cpus;
164 
166  BUG_ON(!irqs_disabled());
167 
168  error = syscore_suspend();
169  if (!error) {
170  *wakeup = pm_wakeup_pending();
171  if (!(suspend_test(TEST_CORE) || *wakeup)) {
172  error = suspend_ops->enter(state);
173  events_check_enabled = false;
174  }
175  syscore_resume();
176  }
177 
180 
181  Enable_cpus:
182  enable_nonboot_cpus();
183 
184  Platform_wake:
185  if (suspend_ops->wake)
186  suspend_ops->wake();
187 
189 
190  Platform_finish:
191  if (suspend_ops->finish)
192  suspend_ops->finish();
193 
194  return error;
195 }
196 
202 {
203  int error;
204  bool wakeup = false;
205 
206  if (!suspend_ops)
207  return -ENOSYS;
208 
209  trace_machine_suspend(state);
210  if (suspend_ops->begin) {
211  error = suspend_ops->begin(state);
212  if (error)
213  goto Close;
214  }
215  suspend_console();
216  ftrace_stop();
219  if (error) {
220  printk(KERN_ERR "PM: Some devices failed to suspend\n");
221  goto Recover_platform;
222  }
223  suspend_test_finish("suspend devices");
224  if (suspend_test(TEST_DEVICES))
225  goto Recover_platform;
226 
227  do {
228  error = suspend_enter(state, &wakeup);
229  } while (!error && !wakeup
230  && suspend_ops->suspend_again && suspend_ops->suspend_again());
231 
232  Resume_devices:
235  suspend_test_finish("resume devices");
236  ftrace_start();
237  resume_console();
238  Close:
239  if (suspend_ops->end)
240  suspend_ops->end();
241  trace_machine_suspend(PWR_EVENT_EXIT);
242  return error;
243 
244  Recover_platform:
245  if (suspend_ops->recover)
246  suspend_ops->recover();
247  goto Resume_devices;
248 }
249 
256 static void suspend_finish(void)
257 {
258  suspend_thaw_processes();
259  pm_notifier_call_chain(PM_POST_SUSPEND);
261 }
262 
271 static int enter_state(suspend_state_t state)
272 {
273  int error;
274 
275  if (!valid_state(state))
276  return -ENODEV;
277 
278  if (!mutex_trylock(&pm_mutex))
279  return -EBUSY;
280 
281  printk(KERN_INFO "PM: Syncing filesystems ... ");
282  sys_sync();
283  printk("done.\n");
284 
285  pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
286  error = suspend_prepare();
287  if (error)
288  goto Unlock;
289 
290  if (suspend_test(TEST_FREEZER))
291  goto Finish;
292 
293  pr_debug("PM: Entering %s sleep\n", pm_states[state]);
295  error = suspend_devices_and_enter(state);
297 
298  Finish:
299  pr_debug("PM: Finishing wakeup.\n");
300  suspend_finish();
301  Unlock:
302  mutex_unlock(&pm_mutex);
303  return error;
304 }
305 
314 {
315  int error;
316 
318  return -EINVAL;
319 
320  error = enter_state(state);
321  if (error) {
323  dpm_save_failed_errno(error);
324  } else {
326  }
327  return error;
328 }