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