Commit | Line | Data |
---|---|---|
6fa3eb70 S |
1 | /* kernel/power/sbsuspend.c |
2 | * | |
3 | * This software is licensed under the terms of the GNU General Public | |
4 | * License version 2, as published by the Free Software Foundation, and | |
5 | * may be copied, distributed, and modified under those terms. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | * | |
12 | */ | |
13 | ||
14 | #include <linux/sbsuspend.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/mutex.h> | |
17 | #include <linux/rtc.h> | |
18 | //#include <linux/leds-mt65xx.h> | |
19 | ||
20 | ||
21 | enum { | |
22 | DEBUG_USER_STATE = 1U << 0, | |
23 | DEBUG_SUSPEND = 1U << 2, | |
24 | DEBUG_VERBOSE = 1U << 3, | |
25 | }; | |
26 | int sbsuspend_debug_mask = DEBUG_USER_STATE | DEBUG_SUSPEND | DEBUG_VERBOSE;; | |
27 | module_param_named(sbsuspend_debug_mask, sbsuspend_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); | |
28 | ||
29 | #define _TAG_PM_M "SB_PM" | |
30 | #define pm_warn(fmt, ...) \ | |
31 | if (sbsuspend_debug_mask) pr_warn("[%s][%s]" fmt, _TAG_PM_M, __func__, ##__VA_ARGS__); | |
32 | ||
33 | #define sb_attr(_name) \ | |
34 | static struct kobj_attribute _name##_attr = { \ | |
35 | .attr = { \ | |
36 | .name = __stringify(_name), \ | |
37 | .mode = 0644, \ | |
38 | }, \ | |
39 | .show = _name##_show, \ | |
40 | .store = _name##_store, \ | |
41 | } | |
42 | ||
43 | ||
44 | ||
45 | //sb_state_t sb_state = SB_STATE_DISABLE; | |
46 | static DEFINE_MUTEX(sb_mutex); | |
47 | static LIST_HEAD(sb_handlers); | |
48 | static int sb_bypass = 0; | |
49 | int sb_handler_count = 0; | |
50 | int sb_handler_forbid_id = 0x0; | |
51 | const char *const sb_states[SB_STATE_MAX] = { | |
52 | [SB_STATE_DISABLE] = "disable", | |
53 | [SB_STATE_ENABLE] = "enable", | |
54 | [SB_STATE_SUSPEND] = "suspend", | |
55 | [SB_STATE_RESUME] = "resume", | |
56 | }; | |
57 | ||
58 | ||
59 | ||
60 | extern struct kobject *power_kobj; | |
61 | ||
62 | ||
63 | ||
64 | void register_sb_handler(struct sb_handler *handler) | |
65 | { | |
66 | struct list_head *pos; | |
67 | ||
68 | mutex_lock(&sb_mutex); | |
69 | ||
70 | list_for_each(pos, &sb_handlers) { | |
71 | struct sb_handler *e; | |
72 | e = list_entry(pos, struct sb_handler, link); | |
73 | if (e->level > handler->level) | |
74 | break; | |
75 | } | |
76 | list_add_tail(&handler->link, pos); | |
77 | sb_handler_count++; | |
78 | ||
79 | //if ((state & SUSPENDED) && handler->suspend) | |
80 | // handler->suspend(handler); | |
81 | ||
82 | mutex_unlock(&sb_mutex); | |
83 | } | |
84 | EXPORT_SYMBOL(register_sb_handler); | |
85 | ||
86 | void unregister_sb_handler(struct sb_handler *handler) | |
87 | { | |
88 | mutex_lock(&sb_mutex); | |
89 | ||
90 | list_del(&handler->link); | |
91 | sb_handler_count--; | |
92 | ||
93 | mutex_unlock(&sb_mutex); | |
94 | } | |
95 | EXPORT_SYMBOL(unregister_sb_handler); | |
96 | ||
97 | void sb_enable(void) | |
98 | { | |
99 | struct sb_handler *pos; | |
100 | int count = 0; | |
101 | ||
102 | pr_warn("@@@@@@@@@@@@@@@@@@@\n@@@__sb_enable__@@@\n@@@@@@@@@@@@@@@@@@@\n"); | |
103 | ||
104 | mutex_lock(&sb_mutex); | |
105 | ||
106 | //pm_warn("turn backlight off\n"); | |
107 | //mt65xx_leds_brightness_set(MT65XX_LED_TYPE_BUTTON, 0); | |
108 | //mt65xx_leds_brightness_set(MT65XX_LED_TYPE_LCD, 0); | |
109 | ||
110 | pm_warn("sb_handler_count = %d, sb_handler_forbid_id = 0x%x\n", sb_handler_count, sb_handler_forbid_id); | |
111 | list_for_each_entry(pos, &sb_handlers, link) { | |
112 | if (pos->enable != NULL) { | |
113 | if (!(sb_handler_forbid_id & (0x1 << count))) { | |
114 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
115 | pm_warn("sb enable handler %d: [%pf], level: %d\n", count, pos->enable, pos->level); | |
116 | pos->enable(pos); | |
117 | } | |
118 | count++; | |
119 | } | |
120 | } | |
121 | ||
122 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
123 | pm_warn("sb enable handler done\n"); | |
124 | ||
125 | mutex_unlock(&sb_mutex); | |
126 | ||
127 | } | |
128 | EXPORT_SYMBOL(sb_enable); | |
129 | ||
130 | void sb_disable(void) | |
131 | { | |
132 | struct sb_handler *pos; | |
133 | int count = 0; | |
134 | ||
135 | pr_warn("@@@@@@@@@@@@@@@@@@@@\n@@@__sb_disable__@@@\n@@@@@@@@@@@@@@@@@@@@\n"); | |
136 | ||
137 | mutex_lock(&sb_mutex); | |
138 | ||
139 | pm_warn("sb_handler_count = %d, sb_handler_forbid_id = 0x%x\n", sb_handler_count, sb_handler_forbid_id); | |
140 | list_for_each_entry_reverse(pos, &sb_handlers, link) { | |
141 | if (pos->disable != NULL) { | |
142 | if (!(sb_handler_forbid_id & (0x1 << (sb_handler_count - 1 - count)))) { | |
143 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
144 | pm_warn("sb disable handler %d: [%pf], level: %d\n", count, pos->disable, pos->level); | |
145 | pos->disable(pos); | |
146 | } | |
147 | count++; | |
148 | } | |
149 | } | |
150 | ||
151 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
152 | pm_warn("sb disable handler done\n"); | |
153 | ||
154 | //pm_warn("turn backlight on\n"); | |
155 | //mt65xx_leds_brightness_set(MT65XX_LED_TYPE_LCD, 255); | |
156 | //we don't need to turn keypad light on when SmartBook plug-out | |
157 | //mt65xx_leds_brightness_set(MT65XX_LED_TYPE_BUTTON, 255); | |
158 | ||
159 | mutex_unlock(&sb_mutex); | |
160 | } | |
161 | EXPORT_SYMBOL(sb_disable); | |
162 | ||
163 | void sb_suspend(void) | |
164 | { | |
165 | struct sb_handler *pos; | |
166 | int count = 0; | |
167 | ||
168 | pr_warn("@@@@@@@@@@@@@@@@@@@\n@@@__sb_suspend__@@@\n@@@@@@@@@@@@@@@@@@@\n"); | |
169 | ||
170 | mutex_lock(&sb_mutex); | |
171 | ||
172 | pm_warn("sb_handler_count = %d, sb_handler_forbid_id = 0x%x\n", sb_handler_count, sb_handler_forbid_id); | |
173 | list_for_each_entry(pos, &sb_handlers, link) { | |
174 | if (pos->suspend != NULL) { | |
175 | if (!(sb_handler_forbid_id & (0x1 << count))) { | |
176 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
177 | pm_warn("sb enable handler %d: [%pf], level: %d\n", count, pos->suspend, pos->level); | |
178 | pos->suspend(pos); | |
179 | } | |
180 | count++; | |
181 | } | |
182 | } | |
183 | ||
184 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
185 | pm_warn("sb enable handler done\n"); | |
186 | ||
187 | mutex_unlock(&sb_mutex); | |
188 | ||
189 | } | |
190 | EXPORT_SYMBOL(sb_suspend); | |
191 | ||
192 | void sb_resume(void) | |
193 | { | |
194 | struct sb_handler *pos; | |
195 | int count = 0; | |
196 | ||
197 | pr_warn("@@@@@@@@@@@@@@@@@@@@\n@@@__sb_resume__@@@\n@@@@@@@@@@@@@@@@@@@@\n"); | |
198 | ||
199 | mutex_lock(&sb_mutex); | |
200 | ||
201 | pm_warn("sb_handler_count = %d, sb_handler_forbid_id = 0x%x\n", sb_handler_count, sb_handler_forbid_id); | |
202 | list_for_each_entry_reverse(pos, &sb_handlers, link) { | |
203 | if (pos->resume != NULL) { | |
204 | if (!(sb_handler_forbid_id & (0x1 << (sb_handler_count - 1 - count)))) { | |
205 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
206 | pm_warn("sb disable handler %d: [%pf], level: %d\n", count, pos->resume, pos->level); | |
207 | pos->resume(pos); | |
208 | } | |
209 | count++; | |
210 | } | |
211 | } | |
212 | ||
213 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
214 | pm_warn("sb disable handler done\n"); | |
215 | ||
216 | mutex_unlock(&sb_mutex); | |
217 | } | |
218 | EXPORT_SYMBOL(sb_resume); | |
219 | ||
220 | void sb_plug_in(void) | |
221 | { | |
222 | struct sb_handler *pos; | |
223 | int count = 0; | |
224 | ||
225 | if (sb_bypass) | |
226 | return; | |
227 | ||
228 | pr_warn("@@@@@@@@@@@@@@@@@@@\n@@@__sb_plug_in__@@@\n@@@@@@@@@@@@@@@@@@@\n"); | |
229 | ||
230 | mutex_lock(&sb_mutex); | |
231 | ||
232 | pm_warn("sb_handler_count = %d, sb_handler_forbid_id = 0x%x\n", sb_handler_count, sb_handler_forbid_id); | |
233 | list_for_each_entry(pos, &sb_handlers, link) { | |
234 | if (pos->plug_in != NULL) { | |
235 | if (!(sb_handler_forbid_id & (0x1 << count))) { | |
236 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
237 | pm_warn("sb plug_in handler %d: [%pf], level: %d\n", count, pos->plug_in, pos->level); | |
238 | pos->plug_in(pos); | |
239 | } | |
240 | count++; | |
241 | } | |
242 | } | |
243 | ||
244 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
245 | pm_warn("sb plug_in handler done\n"); | |
246 | ||
247 | mutex_unlock(&sb_mutex); | |
248 | ||
249 | } | |
250 | EXPORT_SYMBOL(sb_plug_in); | |
251 | ||
252 | void sb_plug_out(void) | |
253 | { | |
254 | struct sb_handler *pos; | |
255 | int count = 0; | |
256 | ||
257 | if (sb_bypass) | |
258 | return; | |
259 | ||
260 | pr_warn("@@@@@@@@@@@@@@@@@@@@\n@@@__sb_plug_out__@@@\n@@@@@@@@@@@@@@@@@@@@\n"); | |
261 | ||
262 | mutex_lock(&sb_mutex); | |
263 | ||
264 | pm_warn("sb_handler_count = %d, sb_handler_forbid_id = 0x%x\n", sb_handler_count, sb_handler_forbid_id); | |
265 | list_for_each_entry_reverse(pos, &sb_handlers, link) { | |
266 | if (pos->plug_out != NULL) { | |
267 | if (!(sb_handler_forbid_id & (0x1 << (sb_handler_count - 1 - count)))) { | |
268 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
269 | pm_warn("sb plug_out handler %d: [%pf], level: %d\n", count, pos->plug_out, pos->level); | |
270 | pos->plug_out(pos); | |
271 | } | |
272 | count++; | |
273 | } | |
274 | } | |
275 | ||
276 | if (sbsuspend_debug_mask & DEBUG_SUSPEND) | |
277 | pm_warn("sb plug_out handler done\n"); | |
278 | ||
279 | mutex_unlock(&sb_mutex); | |
280 | } | |
281 | EXPORT_SYMBOL(sb_plug_out); | |
282 | ||
283 | /* | |
284 | void sb_event(sb_event_t event) | |
285 | { | |
286 | mutex_lock(&sb_mutex); | |
287 | ||
288 | if (sbsuspend_debug_mask & DEBUG_USER_STATE) { | |
289 | struct timespec ts; | |
290 | struct rtc_time tm; | |
291 | getnstimeofday(&ts); | |
292 | rtc_time_to_tm(ts.tv_sec, &tm); | |
293 | pm_warn("%s (%d->%d) at %lld " | |
294 | "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", | |
295 | event != PM_SUSPEND_ON ? "sleep" : "wakeup", | |
296 | sb_state, event, | |
297 | ktime_to_ns(ktime_get()), | |
298 | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, | |
299 | tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); | |
300 | } | |
301 | ||
302 | switch (sb_state) | |
303 | { | |
304 | case SB_STATE_DISABLE: | |
305 | ||
306 | case SB_STATE_ENABLE: | |
307 | ||
308 | if (!old_sleep && new_state != PM_SUSPEND_ON) { | |
309 | state |= SUSPEND_REQUESTED; | |
310 | sb_enable(); | |
311 | } else if (old_sleep && new_state == PM_SUSPEND_ON) { | |
312 | state &= ~SUSPEND_REQUESTED; | |
313 | sb_disable(); | |
314 | } | |
315 | sb_state = new_state; | |
316 | ||
317 | mutex_unlock(&sb_mutex); | |
318 | ||
319 | } | |
320 | */ | |
321 | ||
322 | /** | |
323 | * state - control system power state. | |
324 | * | |
325 | * show() returns what states are supported, which is hard-coded to | |
326 | * 'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and | |
327 | * 'disk' (Suspend-to-Disk). | |
328 | * | |
329 | * store() accepts one of those strings, translates it into the | |
330 | * proper enumerated value, and initiates a suspend transition. | |
331 | */ | |
332 | static ssize_t sb_state_show(struct kobject *kobj, struct kobj_attribute *attr, | |
333 | char *buf) | |
334 | { | |
335 | char *s = buf; | |
336 | int i; | |
337 | ||
338 | for (i = 0; i < SB_STATE_MAX; i++) { | |
339 | if (sb_states[i]) | |
340 | s += sprintf(s,"%s ", sb_states[i]); | |
341 | } | |
342 | if (s != buf) | |
343 | /* convert the last space to a newline */ | |
344 | *(s-1) = '\n'; | |
345 | ||
346 | return (s - buf); | |
347 | } | |
348 | ||
349 | static sb_state_t decode_sb_state(const char *buf, size_t n) | |
350 | { | |
351 | sb_state_t state = SB_STATE_DISABLE; | |
352 | const char * const *s; | |
353 | char *p; | |
354 | int len; | |
355 | ||
356 | p = memchr(buf, '\n', n); | |
357 | len = p ? p - buf : n; | |
358 | ||
359 | for (s = &sb_states[state]; state < SB_STATE_MAX; s++, state++) | |
360 | if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) | |
361 | return state; | |
362 | ||
363 | return SB_STATE_MAX; | |
364 | } | |
365 | ||
366 | static ssize_t sb_state_store(struct kobject *kobj, struct kobj_attribute *attr, | |
367 | const char *buf, size_t n) | |
368 | { | |
369 | sb_state_t state; | |
370 | int error = 0; | |
371 | char cmd[32]; | |
372 | int param; | |
373 | ||
374 | if (sscanf(buf, "%s %d", cmd, ¶m) == 2) | |
375 | { | |
376 | if (!strcmp(cmd, "bypass")) | |
377 | sb_bypass = param; | |
378 | } | |
379 | else | |
380 | { | |
381 | if (!sb_bypass) | |
382 | { | |
383 | state = decode_sb_state(buf, n); | |
384 | ||
385 | switch (state) | |
386 | { | |
387 | case SB_STATE_DISABLE: | |
388 | sb_disable(); | |
389 | break; | |
390 | case SB_STATE_ENABLE: | |
391 | sb_enable(); | |
392 | break; | |
393 | case SB_STATE_SUSPEND: | |
394 | sb_suspend(); | |
395 | break; | |
396 | case SB_STATE_RESUME: | |
397 | sb_resume(); | |
398 | break; | |
399 | default: | |
400 | error = -EINVAL; | |
401 | break; | |
402 | } | |
403 | } | |
404 | } | |
405 | ||
406 | return error ? error : n; | |
407 | } | |
408 | ||
409 | sb_attr(sb_state); | |
410 | ||
411 | ||
412 | ||
413 | ||
414 | static int __init sbsuspend_init(void) | |
415 | { | |
416 | int err = 0; | |
417 | ||
418 | err |= sysfs_create_file(power_kobj, &sb_state_attr.attr); | |
419 | if (err) { | |
420 | printk("[%s]: fail to create sysfs\n", __func__); | |
421 | } | |
422 | return 0; | |
423 | } | |
424 | ||
425 | static void __exit sbsuspend_exit(void) | |
426 | { | |
427 | } | |
428 | ||
429 | core_initcall(sbsuspend_init); | |
430 | module_exit(sbsuspend_exit); |