1
2
3
4
5
6
7
8
9
10
11#include <linux/kobject.h>
12#include <linux/string.h>
13#include <linux/resume-trace.h>
14#include <linux/workqueue.h>
15
16#include "power.h"
17
18DEFINE_MUTEX(pm_mutex);
19
20#ifdef CONFIG_PM_SLEEP
21
22
23
24static BLOCKING_NOTIFIER_HEAD(pm_chain_head);
25
26int register_pm_notifier(struct notifier_block *nb)
27{
28 return blocking_notifier_chain_register(&pm_chain_head, nb);
29}
30EXPORT_SYMBOL_GPL(register_pm_notifier);
31
32int unregister_pm_notifier(struct notifier_block *nb)
33{
34 return blocking_notifier_chain_unregister(&pm_chain_head, nb);
35}
36EXPORT_SYMBOL_GPL(unregister_pm_notifier);
37
38int pm_notifier_call_chain(unsigned long val)
39{
40 return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)
41 == NOTIFY_BAD) ? -EINVAL : 0;
42}
43
44
45int pm_async_enabled = 1;
46
47static ssize_t pm_async_show(struct kobject *kobj, struct kobj_attribute *attr,
48 char *buf)
49{
50 return sprintf(buf, "%d\n", pm_async_enabled);
51}
52
53static ssize_t pm_async_store(struct kobject *kobj, struct kobj_attribute *attr,
54 const char *buf, size_t n)
55{
56 unsigned long val;
57
58 if (strict_strtoul(buf, 10, &val))
59 return -EINVAL;
60
61 if (val > 1)
62 return -EINVAL;
63
64 pm_async_enabled = val;
65 return n;
66}
67
68power_attr(pm_async);
69
70#ifdef CONFIG_PM_DEBUG
71int pm_test_level = TEST_NONE;
72
73static const char * const pm_tests[__TEST_AFTER_LAST] = {
74 [TEST_NONE] = "none",
75 [TEST_CORE] = "core",
76 [TEST_CPUS] = "processors",
77 [TEST_PLATFORM] = "platform",
78 [TEST_DEVICES] = "devices",
79 [TEST_FREEZER] = "freezer",
80};
81
82static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr,
83 char *buf)
84{
85 char *s = buf;
86 int level;
87
88 for (level = TEST_FIRST; level <= TEST_MAX; level++)
89 if (pm_tests[level]) {
90 if (level == pm_test_level)
91 s += sprintf(s, "[%s] ", pm_tests[level]);
92 else
93 s += sprintf(s, "%s ", pm_tests[level]);
94 }
95
96 if (s != buf)
97
98 *(s-1) = '\n';
99
100 return (s - buf);
101}
102
103static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr,
104 const char *buf, size_t n)
105{
106 const char * const *s;
107 int level;
108 char *p;
109 int len;
110 int error = -EINVAL;
111
112 p = memchr(buf, '\n', n);
113 len = p ? p - buf : n;
114
115 mutex_lock(&pm_mutex);
116
117 level = TEST_FIRST;
118 for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++)
119 if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) {
120 pm_test_level = level;
121 error = 0;
122 break;
123 }
124
125 mutex_unlock(&pm_mutex);
126
127 return error ? error : n;
128}
129
130power_attr(pm_test);
131#endif
132
133#endif
134
135struct kobject *power_kobj;
136
137
138
139
140
141
142
143
144
145
146
147static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
148 char *buf)
149{
150 char *s = buf;
151#ifdef CONFIG_SUSPEND
152 int i;
153
154 for (i = 0; i < PM_SUSPEND_MAX; i++) {
155 if (pm_states[i] && valid_state(i))
156 s += sprintf(s,"%s ", pm_states[i]);
157 }
158#endif
159#ifdef CONFIG_HIBERNATION
160 s += sprintf(s, "%s\n", "disk");
161#else
162 if (s != buf)
163
164 *(s-1) = '\n';
165#endif
166 return (s - buf);
167}
168
169static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
170 const char *buf, size_t n)
171{
172#ifdef CONFIG_SUSPEND
173 suspend_state_t state = PM_SUSPEND_STANDBY;
174 const char * const *s;
175#endif
176 char *p;
177 int len;
178 int error = -EINVAL;
179
180 p = memchr(buf, '\n', n);
181 len = p ? p - buf : n;
182
183
184 if (len == 4 && !strncmp(buf, "disk", len)) {
185 error = hibernate();
186 goto Exit;
187 }
188
189#ifdef CONFIG_SUSPEND
190 for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
191 if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
192 break;
193 }
194 if (state < PM_SUSPEND_MAX && *s)
195 error = enter_state(state);
196#endif
197
198 Exit:
199 return error ? error : n;
200}
201
202power_attr(state);
203
204#ifdef CONFIG_PM_SLEEP
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233static ssize_t wakeup_count_show(struct kobject *kobj,
234 struct kobj_attribute *attr,
235 char *buf)
236{
237 unsigned int val;
238
239 return pm_get_wakeup_count(&val) ? sprintf(buf, "%u\n", val) : -EINTR;
240}
241
242static ssize_t wakeup_count_store(struct kobject *kobj,
243 struct kobj_attribute *attr,
244 const char *buf, size_t n)
245{
246 unsigned int val;
247
248 if (sscanf(buf, "%u", &val) == 1) {
249 if (pm_save_wakeup_count(val))
250 return n;
251 }
252 return -EINVAL;
253}
254
255power_attr(wakeup_count);
256#endif
257
258#ifdef CONFIG_PM_TRACE
259int pm_trace_enabled;
260
261static ssize_t pm_trace_show(struct kobject *kobj, struct kobj_attribute *attr,
262 char *buf)
263{
264 return sprintf(buf, "%d\n", pm_trace_enabled);
265}
266
267static ssize_t
268pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr,
269 const char *buf, size_t n)
270{
271 int val;
272
273 if (sscanf(buf, "%d", &val) == 1) {
274 pm_trace_enabled = !!val;
275 return n;
276 }
277 return -EINVAL;
278}
279
280power_attr(pm_trace);
281
282static ssize_t pm_trace_dev_match_show(struct kobject *kobj,
283 struct kobj_attribute *attr,
284 char *buf)
285{
286 return show_trace_dev_match(buf, PAGE_SIZE);
287}
288
289static ssize_t
290pm_trace_dev_match_store(struct kobject *kobj, struct kobj_attribute *attr,
291 const char *buf, size_t n)
292{
293 return -EINVAL;
294}
295
296power_attr(pm_trace_dev_match);
297
298#endif
299
300static struct attribute * g[] = {
301 &state_attr.attr,
302#ifdef CONFIG_PM_TRACE
303 &pm_trace_attr.attr,
304 &pm_trace_dev_match_attr.attr,
305#endif
306#ifdef CONFIG_PM_SLEEP
307 &pm_async_attr.attr,
308 &wakeup_count_attr.attr,
309#ifdef CONFIG_PM_DEBUG
310 &pm_test_attr.attr,
311#endif
312#endif
313 NULL,
314};
315
316static struct attribute_group attr_group = {
317 .attrs = g,
318};
319
320#ifdef CONFIG_PM_RUNTIME
321struct workqueue_struct *pm_wq;
322EXPORT_SYMBOL_GPL(pm_wq);
323
324static int __init pm_start_workqueue(void)
325{
326 pm_wq = alloc_workqueue("pm", WQ_FREEZABLE, 0);
327
328 return pm_wq ? 0 : -ENOMEM;
329}
330#else
331static inline int pm_start_workqueue(void) { return 0; }
332#endif
333
334static int __init pm_init(void)
335{
336 int error = pm_start_workqueue();
337 if (error)
338 return error;
339 hibernate_image_size_init();
340 hibernate_reserved_size_init();
341 power_kobj = kobject_create_and_add("power", NULL);
342 if (!power_kobj)
343 return -ENOMEM;
344 return sysfs_create_group(power_kobj, &attr_group);
345}
346
347core_initcall(pm_init);
348