vserver 1.9.3
[linux-2.6.git] / kernel / power / main.c
1 /*
2  * kernel/power/main.c - PM subsystem core functionality.
3  *
4  * Copyright (c) 2003 Patrick Mochel
5  * Copyright (c) 2003 Open Source Development Lab
6  * 
7  * This file is release under the GPLv2
8  *
9  */
10
11 #include <linux/suspend.h>
12 #include <linux/kobject.h>
13 #include <linux/string.h>
14 #include <linux/delay.h>
15 #include <linux/errno.h>
16 #include <linux/init.h>
17 #include <linux/pm.h>
18
19
20 #include "power.h"
21
22 DECLARE_MUTEX(pm_sem);
23
24 struct pm_ops * pm_ops = NULL;
25 u32 pm_disk_mode = PM_DISK_SHUTDOWN;
26
27 /**
28  *      pm_set_ops - Set the global power method table. 
29  *      @ops:   Pointer to ops structure.
30  */
31
32 void pm_set_ops(struct pm_ops * ops)
33 {
34         down(&pm_sem);
35         pm_ops = ops;
36         up(&pm_sem);
37 }
38
39
40 /**
41  *      suspend_prepare - Do prep work before entering low-power state.
42  *      @state:         State we're entering.
43  *
44  *      This is common code that is called for each state that we're 
45  *      entering. Allocate a console, stop all processes, then make sure
46  *      the platform can enter the requested state.
47  */
48
49 static int suspend_prepare(u32 state)
50 {
51         int error = 0;
52
53         if (!pm_ops || !pm_ops->enter)
54                 return -EPERM;
55
56         pm_prepare_console();
57
58         if (freeze_processes()) {
59                 error = -EAGAIN;
60                 goto Thaw;
61         }
62
63         if (pm_ops->prepare) {
64                 if ((error = pm_ops->prepare(state)))
65                         goto Thaw;
66         }
67
68         if ((error = device_suspend(state)))
69                 goto Finish;
70         return 0;
71  Finish:
72         if (pm_ops->finish)
73                 pm_ops->finish(state);
74  Thaw:
75         thaw_processes();
76         pm_restore_console();
77         return error;
78 }
79
80
81 static int suspend_enter(u32 state)
82 {
83         int error = 0;
84         unsigned long flags;
85
86         local_irq_save(flags);
87         if ((error = device_power_down(state)))
88                 goto Done;
89         error = pm_ops->enter(state);
90         device_power_up();
91  Done:
92         local_irq_restore(flags);
93         return error;
94 }
95
96
97 /**
98  *      suspend_finish - Do final work before exiting suspend sequence.
99  *      @state:         State we're coming out of.
100  *
101  *      Call platform code to clean up, restart processes, and free the 
102  *      console that we've allocated.
103  */
104
105 static void suspend_finish(u32 state)
106 {
107         device_resume();
108         if (pm_ops && pm_ops->finish)
109                 pm_ops->finish(state);
110         thaw_processes();
111         pm_restore_console();
112 }
113
114
115
116
117 char * pm_states[] = {
118         [PM_SUSPEND_STANDBY]    = "standby",
119         [PM_SUSPEND_MEM]        = "mem",
120         [PM_SUSPEND_DISK]       = "disk",
121         NULL,
122 };
123
124
125 /**
126  *      enter_state - Do common work of entering low-power state.
127  *      @state:         pm_state structure for state we're entering.
128  *
129  *      Make sure we're the only ones trying to enter a sleep state. Fail
130  *      if someone has beat us to it, since we don't want anything weird to
131  *      happen when we wake up.
132  *      Then, do the setup for suspend, enter the state, and cleaup (after
133  *      we've woken up).
134  */
135
136 static int enter_state(u32 state)
137 {
138         int error;
139
140         if (down_trylock(&pm_sem))
141                 return -EBUSY;
142
143         /* Suspend is hard to get right on SMP. */
144         if (num_online_cpus() != 1) {
145                 error = -EPERM;
146                 goto Unlock;
147         }
148
149         if (state == PM_SUSPEND_DISK) {
150                 error = pm_suspend_disk();
151                 goto Unlock;
152         }
153
154         pr_debug("PM: Preparing system for suspend\n");
155         if ((error = suspend_prepare(state)))
156                 goto Unlock;
157
158         pr_debug("PM: Entering state.\n");
159         error = suspend_enter(state);
160
161         pr_debug("PM: Finishing up.\n");
162         suspend_finish(state);
163  Unlock:
164         up(&pm_sem);
165         return error;
166 }
167
168 /*
169  * This is main interface to the outside world. It needs to be
170  * called from process context.
171  */
172 int software_suspend(void)
173 {
174         return enter_state(PM_SUSPEND_DISK);
175 }
176
177
178 /**
179  *      pm_suspend - Externally visible function for suspending system.
180  *      @state:         Enumarted value of state to enter.
181  *
182  *      Determine whether or not value is within range, get state 
183  *      structure, and enter (above).
184  */
185
186 int pm_suspend(u32 state)
187 {
188         if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX)
189                 return enter_state(state);
190         return -EINVAL;
191 }
192
193
194
195 decl_subsys(power,NULL,NULL);
196
197
198 /**
199  *      state - control system power state.
200  *
201  *      show() returns what states are supported, which is hard-coded to
202  *      'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and
203  *      'disk' (Suspend-to-Disk).
204  *
205  *      store() accepts one of those strings, translates it into the 
206  *      proper enumerated value, and initiates a suspend transition.
207  */
208
209 static ssize_t state_show(struct subsystem * subsys, char * buf)
210 {
211         int i;
212         char * s = buf;
213
214         for (i = 0; i < PM_SUSPEND_MAX; i++) {
215                 if (pm_states[i])
216                         s += sprintf(s,"%s ",pm_states[i]);
217         }
218         s += sprintf(s,"\n");
219         return (s - buf);
220 }
221
222 static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n)
223 {
224         u32 state = PM_SUSPEND_STANDBY;
225         char ** s;
226         char *p;
227         int error;
228         int len;
229
230         p = memchr(buf, '\n', n);
231         len = p ? p - buf : n;
232
233         for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
234                 if (*s && !strncmp(buf, *s, len))
235                         break;
236         }
237         if (*s)
238                 error = enter_state(state);
239         else
240                 error = -EINVAL;
241         return error ? error : n;
242 }
243
244 power_attr(state);
245
246 static struct attribute * g[] = {
247         &state_attr.attr,
248         NULL,
249 };
250
251 static struct attribute_group attr_group = {
252         .attrs = g,
253 };
254
255
256 static int __init pm_init(void)
257 {
258         int error = subsystem_register(&power_subsys);
259         if (!error)
260                 error = sysfs_create_group(&power_subsys.kset.kobj,&attr_group);
261         return error;
262 }
263
264 core_initcall(pm_init);