blob: 4b0e26065be09e4d7f8299b258fadb3459cfa4e0 [file] [log] [blame]
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -07001/* kernel/power/wakelock.c
2 *
3 * Copyright (C) 2005-2008 Google, Inc.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <linux/module.h>
17#include <linux/platform_device.h>
18#include <linux/rtc.h>
19#include <linux/suspend.h>
20#include <linux/syscalls.h> /* sys_sync */
21#include <linux/wakelock.h>
22#ifdef CONFIG_WAKELOCK_STAT
23#include <linux/proc_fs.h>
24#endif
25#include "power.h"
26
27enum {
28 DEBUG_EXIT_SUSPEND = 1U << 0,
29 DEBUG_WAKEUP = 1U << 1,
30 DEBUG_SUSPEND = 1U << 2,
31 DEBUG_EXPIRE = 1U << 3,
32 DEBUG_WAKE_LOCK = 1U << 4,
33};
34static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP;
35module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
36
37#define WAKE_LOCK_TYPE_MASK (0x0f)
38#define WAKE_LOCK_INITIALIZED (1U << 8)
39#define WAKE_LOCK_ACTIVE (1U << 9)
40#define WAKE_LOCK_AUTO_EXPIRE (1U << 10)
41#define WAKE_LOCK_PREVENTING_SUSPEND (1U << 11)
42
43static DEFINE_SPINLOCK(list_lock);
44static LIST_HEAD(inactive_locks);
45static struct list_head active_wake_locks[WAKE_LOCK_TYPE_COUNT];
46static int current_event_num;
47struct workqueue_struct *suspend_work_queue;
48struct wake_lock main_wake_lock;
49suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
50static struct wake_lock unknown_wakeup;
51
52#ifdef CONFIG_WAKELOCK_STAT
53static struct wake_lock deleted_wake_locks;
54static ktime_t last_sleep_time_update;
55static int wait_for_wakeup;
56
57int get_expired_time(struct wake_lock *lock, ktime_t *expire_time)
58{
59 struct timespec ts;
60 struct timespec kt;
61 struct timespec tomono;
62 struct timespec delta;
63 unsigned long seq;
64 long timeout;
65
66 if (!(lock->flags & WAKE_LOCK_AUTO_EXPIRE))
67 return 0;
68 do {
69 seq = read_seqbegin(&xtime_lock);
70 timeout = lock->expires - jiffies;
71 if (timeout > 0)
72 return 0;
73 kt = current_kernel_time();
74 tomono = wall_to_monotonic;
75 } while (read_seqretry(&xtime_lock, seq));
76 jiffies_to_timespec(-timeout, &delta);
77 set_normalized_timespec(&ts, kt.tv_sec + tomono.tv_sec - delta.tv_sec,
78 kt.tv_nsec + tomono.tv_nsec - delta.tv_nsec);
79 *expire_time = timespec_to_ktime(ts);
80 return 1;
81}
82
83
Arve Hjønnevåg1b074952009-12-02 18:22:00 -080084static int print_lock_stat(struct seq_file *m, struct wake_lock *lock)
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -070085{
86 int lock_count = lock->stat.count;
87 int expire_count = lock->stat.expire_count;
88 ktime_t active_time = ktime_set(0, 0);
89 ktime_t total_time = lock->stat.total_time;
90 ktime_t max_time = lock->stat.max_time;
Erik Gilling10f01382009-08-25 20:09:12 -070091
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -070092 ktime_t prevent_suspend_time = lock->stat.prevent_suspend_time;
93 if (lock->flags & WAKE_LOCK_ACTIVE) {
94 ktime_t now, add_time;
95 int expired = get_expired_time(lock, &now);
96 if (!expired)
97 now = ktime_get();
98 add_time = ktime_sub(now, lock->stat.last_time);
99 lock_count++;
100 if (!expired)
101 active_time = add_time;
102 else
103 expire_count++;
104 total_time = ktime_add(total_time, add_time);
105 if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND)
106 prevent_suspend_time = ktime_add(prevent_suspend_time,
107 ktime_sub(now, last_sleep_time_update));
108 if (add_time.tv64 > max_time.tv64)
109 max_time = add_time;
110 }
111
Arve Hjønnevåg1b074952009-12-02 18:22:00 -0800112 return seq_printf(m,
Erik Gilling10f01382009-08-25 20:09:12 -0700113 "\"%s\"\t%d\t%d\t%d\t%lld\t%lld\t%lld\t%lld\t%lld\n",
114 lock->name, lock_count, expire_count,
115 lock->stat.wakeup_count, ktime_to_ns(active_time),
116 ktime_to_ns(total_time),
117 ktime_to_ns(prevent_suspend_time), ktime_to_ns(max_time),
118 ktime_to_ns(lock->stat.last_time));
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700119}
120
Arve Hjønnevåg1b074952009-12-02 18:22:00 -0800121static int wakelock_stats_show(struct seq_file *m, void *unused)
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700122{
123 unsigned long irqflags;
124 struct wake_lock *lock;
Arve Hjønnevåg1b074952009-12-02 18:22:00 -0800125 int ret;
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700126 int type;
127
128 spin_lock_irqsave(&list_lock, irqflags);
129
Arve Hjønnevåg1b074952009-12-02 18:22:00 -0800130 ret = seq_puts(m, "name\tcount\texpire_count\twake_count\tactive_since"
Erik Gilling10f01382009-08-25 20:09:12 -0700131 "\ttotal_time\tsleep_time\tmax_time\tlast_change\n");
Arve Hjønnevåg1b074952009-12-02 18:22:00 -0800132 list_for_each_entry(lock, &inactive_locks, link)
133 ret = print_lock_stat(m, lock);
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700134 for (type = 0; type < WAKE_LOCK_TYPE_COUNT; type++) {
135 list_for_each_entry(lock, &active_wake_locks[type], link)
Arve Hjønnevåg1b074952009-12-02 18:22:00 -0800136 ret = print_lock_stat(m, lock);
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700137 }
138 spin_unlock_irqrestore(&list_lock, irqflags);
Arve Hjønnevåg1b074952009-12-02 18:22:00 -0800139 return 0;
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700140}
141
142static void wake_unlock_stat_locked(struct wake_lock *lock, int expired)
143{
144 ktime_t duration;
145 ktime_t now;
146 if (!(lock->flags & WAKE_LOCK_ACTIVE))
147 return;
148 if (get_expired_time(lock, &now))
149 expired = 1;
150 else
151 now = ktime_get();
152 lock->stat.count++;
153 if (expired)
154 lock->stat.expire_count++;
155 duration = ktime_sub(now, lock->stat.last_time);
156 lock->stat.total_time = ktime_add(lock->stat.total_time, duration);
157 if (ktime_to_ns(duration) > ktime_to_ns(lock->stat.max_time))
158 lock->stat.max_time = duration;
159 lock->stat.last_time = ktime_get();
160 if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
161 duration = ktime_sub(now, last_sleep_time_update);
162 lock->stat.prevent_suspend_time = ktime_add(
163 lock->stat.prevent_suspend_time, duration);
164 lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
165 }
166}
167
168static void update_sleep_wait_stats_locked(int done)
169{
170 struct wake_lock *lock;
171 ktime_t now, etime, elapsed, add;
172 int expired;
173
174 now = ktime_get();
175 elapsed = ktime_sub(now, last_sleep_time_update);
176 list_for_each_entry(lock, &active_wake_locks[WAKE_LOCK_SUSPEND], link) {
177 expired = get_expired_time(lock, &etime);
178 if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
179 if (expired)
180 add = ktime_sub(etime, last_sleep_time_update);
181 else
182 add = elapsed;
183 lock->stat.prevent_suspend_time = ktime_add(
184 lock->stat.prevent_suspend_time, add);
185 }
186 if (done || expired)
187 lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
188 else
189 lock->flags |= WAKE_LOCK_PREVENTING_SUSPEND;
190 }
191 last_sleep_time_update = now;
192}
193#endif
194
195
196static void expire_wake_lock(struct wake_lock *lock)
197{
198#ifdef CONFIG_WAKELOCK_STAT
199 wake_unlock_stat_locked(lock, 1);
200#endif
201 lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
202 list_del(&lock->link);
203 list_add(&lock->link, &inactive_locks);
204 if (debug_mask & (DEBUG_WAKE_LOCK | DEBUG_EXPIRE))
205 pr_info("expired wake lock %s\n", lock->name);
206}
207
Mike Chan97a0a742009-08-25 18:10:32 -0700208/* Caller must acquire the list_lock spinlock */
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700209static void print_active_locks(int type)
210{
211 unsigned long irqflags;
212 struct wake_lock *lock;
213
214 BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700215 list_for_each_entry(lock, &active_wake_locks[type], link) {
216 if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
217 long timeout = lock->expires - jiffies;
218 if (timeout <= 0)
219 pr_info("wake lock %s, expired\n", lock->name);
220 else
221 pr_info("active wake lock %s, time left %ld\n",
222 lock->name, timeout);
223 } else
224 pr_info("active wake lock %s\n", lock->name);
225 }
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700226}
227
228static long has_wake_lock_locked(int type)
229{
230 struct wake_lock *lock, *n;
231 long max_timeout = 0;
232
233 BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
234 list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {
235 if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
236 long timeout = lock->expires - jiffies;
237 if (timeout <= 0)
238 expire_wake_lock(lock);
239 else if (timeout > max_timeout)
240 max_timeout = timeout;
241 } else
242 return -1;
243 }
244 return max_timeout;
245}
246
247long has_wake_lock(int type)
248{
249 long ret;
250 unsigned long irqflags;
251 spin_lock_irqsave(&list_lock, irqflags);
252 ret = has_wake_lock_locked(type);
253 spin_unlock_irqrestore(&list_lock, irqflags);
254 return ret;
255}
256
257static void suspend(struct work_struct *work)
258{
259 int ret;
260 int entry_event_num;
261
262 if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
263 if (debug_mask & DEBUG_SUSPEND)
264 pr_info("suspend: abort suspend\n");
265 return;
266 }
267
268 entry_event_num = current_event_num;
269 sys_sync();
270 if (debug_mask & DEBUG_SUSPEND)
271 pr_info("suspend: enter suspend\n");
272 ret = pm_suspend(requested_suspend_state);
273 if (debug_mask & DEBUG_EXIT_SUSPEND) {
274 struct timespec ts;
275 struct rtc_time tm;
276 getnstimeofday(&ts);
277 rtc_time_to_tm(ts.tv_sec, &tm);
278 pr_info("suspend: exit suspend, ret = %d "
279 "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
280 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
281 tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
282 }
283 if (current_event_num == entry_event_num) {
284 if (debug_mask & DEBUG_SUSPEND)
285 pr_info("suspend: pm_suspend returned with no event\n");
286 wake_lock_timeout(&unknown_wakeup, HZ / 2);
287 }
288}
289static DECLARE_WORK(suspend_work, suspend);
290
291static void expire_wake_locks(unsigned long data)
292{
293 long has_lock;
294 unsigned long irqflags;
295 if (debug_mask & DEBUG_EXPIRE)
296 pr_info("expire_wake_locks: start\n");
Mike Chan97a0a742009-08-25 18:10:32 -0700297 spin_lock_irqsave(&list_lock, irqflags);
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700298 if (debug_mask & DEBUG_SUSPEND)
299 print_active_locks(WAKE_LOCK_SUSPEND);
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700300 has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND);
301 if (debug_mask & DEBUG_EXPIRE)
302 pr_info("expire_wake_locks: done, has_lock %ld\n", has_lock);
303 if (has_lock == 0)
304 queue_work(suspend_work_queue, &suspend_work);
305 spin_unlock_irqrestore(&list_lock, irqflags);
306}
307static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
308
309static int power_suspend_late(struct device *dev)
310{
311 int ret = has_wake_lock(WAKE_LOCK_SUSPEND) ? -EAGAIN : 0;
312#ifdef CONFIG_WAKELOCK_STAT
313 wait_for_wakeup = 1;
314#endif
315 if (debug_mask & DEBUG_SUSPEND)
316 pr_info("power_suspend_late return %d\n", ret);
317 return ret;
318}
319
320static struct dev_pm_ops power_driver_pm_ops = {
321 .suspend_noirq = power_suspend_late,
322};
323
324static struct platform_driver power_driver = {
325 .driver.name = "power",
326 .driver.pm = &power_driver_pm_ops,
327};
328static struct platform_device power_device = {
329 .name = "power",
330};
331
332void wake_lock_init(struct wake_lock *lock, int type, const char *name)
333{
334 unsigned long irqflags = 0;
335
336 if (name)
337 lock->name = name;
338 BUG_ON(!lock->name);
339
340 if (debug_mask & DEBUG_WAKE_LOCK)
341 pr_info("wake_lock_init name=%s\n", lock->name);
342#ifdef CONFIG_WAKELOCK_STAT
343 lock->stat.count = 0;
344 lock->stat.expire_count = 0;
345 lock->stat.wakeup_count = 0;
346 lock->stat.total_time = ktime_set(0, 0);
347 lock->stat.prevent_suspend_time = ktime_set(0, 0);
348 lock->stat.max_time = ktime_set(0, 0);
349 lock->stat.last_time = ktime_set(0, 0);
350#endif
351 lock->flags = (type & WAKE_LOCK_TYPE_MASK) | WAKE_LOCK_INITIALIZED;
352
353 INIT_LIST_HEAD(&lock->link);
354 spin_lock_irqsave(&list_lock, irqflags);
355 list_add(&lock->link, &inactive_locks);
356 spin_unlock_irqrestore(&list_lock, irqflags);
357}
358EXPORT_SYMBOL(wake_lock_init);
359
360void wake_lock_destroy(struct wake_lock *lock)
361{
362 unsigned long irqflags;
363 if (debug_mask & DEBUG_WAKE_LOCK)
364 pr_info("wake_lock_destroy name=%s\n", lock->name);
365 spin_lock_irqsave(&list_lock, irqflags);
366 lock->flags &= ~WAKE_LOCK_INITIALIZED;
367#ifdef CONFIG_WAKELOCK_STAT
368 if (lock->stat.count) {
369 deleted_wake_locks.stat.count += lock->stat.count;
370 deleted_wake_locks.stat.expire_count += lock->stat.expire_count;
371 deleted_wake_locks.stat.total_time =
372 ktime_add(deleted_wake_locks.stat.total_time,
373 lock->stat.total_time);
374 deleted_wake_locks.stat.prevent_suspend_time =
375 ktime_add(deleted_wake_locks.stat.prevent_suspend_time,
376 lock->stat.prevent_suspend_time);
377 deleted_wake_locks.stat.max_time =
378 ktime_add(deleted_wake_locks.stat.max_time,
379 lock->stat.max_time);
380 }
381#endif
382 list_del(&lock->link);
383 spin_unlock_irqrestore(&list_lock, irqflags);
384}
385EXPORT_SYMBOL(wake_lock_destroy);
386
387static void wake_lock_internal(
388 struct wake_lock *lock, long timeout, int has_timeout)
389{
390 int type;
391 unsigned long irqflags;
392 long expire_in;
393
394 spin_lock_irqsave(&list_lock, irqflags);
395 type = lock->flags & WAKE_LOCK_TYPE_MASK;
396 BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
397 BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));
398#ifdef CONFIG_WAKELOCK_STAT
399 if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {
400 if (debug_mask & DEBUG_WAKEUP)
401 pr_info("wakeup wake lock: %s\n", lock->name);
402 wait_for_wakeup = 0;
403 lock->stat.wakeup_count++;
404 }
405 if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&
406 (long)(lock->expires - jiffies) <= 0) {
407 wake_unlock_stat_locked(lock, 0);
408 lock->stat.last_time = ktime_get();
409 }
410#endif
411 if (!(lock->flags & WAKE_LOCK_ACTIVE)) {
412 lock->flags |= WAKE_LOCK_ACTIVE;
413#ifdef CONFIG_WAKELOCK_STAT
414 lock->stat.last_time = ktime_get();
415#endif
416 }
417 list_del(&lock->link);
418 if (has_timeout) {
419 if (debug_mask & DEBUG_WAKE_LOCK)
420 pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",
421 lock->name, type, timeout / HZ,
422 (timeout % HZ) * MSEC_PER_SEC / HZ);
423 lock->expires = jiffies + timeout;
424 lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
425 list_add_tail(&lock->link, &active_wake_locks[type]);
426 } else {
427 if (debug_mask & DEBUG_WAKE_LOCK)
428 pr_info("wake_lock: %s, type %d\n", lock->name, type);
429 lock->expires = LONG_MAX;
430 lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;
431 list_add(&lock->link, &active_wake_locks[type]);
432 }
433 if (type == WAKE_LOCK_SUSPEND) {
434 current_event_num++;
435#ifdef CONFIG_WAKELOCK_STAT
436 if (lock == &main_wake_lock)
437 update_sleep_wait_stats_locked(1);
438 else if (!wake_lock_active(&main_wake_lock))
439 update_sleep_wait_stats_locked(0);
440#endif
441 if (has_timeout)
442 expire_in = has_wake_lock_locked(type);
443 else
444 expire_in = -1;
445 if (expire_in > 0) {
446 if (debug_mask & DEBUG_EXPIRE)
447 pr_info("wake_lock: %s, start expire timer, "
448 "%ld\n", lock->name, expire_in);
449 mod_timer(&expire_timer, jiffies + expire_in);
450 } else {
451 if (del_timer(&expire_timer))
452 if (debug_mask & DEBUG_EXPIRE)
453 pr_info("wake_lock: %s, stop expire timer\n",
454 lock->name);
455 if (expire_in == 0)
456 queue_work(suspend_work_queue, &suspend_work);
457 }
458 }
459 spin_unlock_irqrestore(&list_lock, irqflags);
460}
461
462void wake_lock(struct wake_lock *lock)
463{
464 wake_lock_internal(lock, 0, 0);
465}
466EXPORT_SYMBOL(wake_lock);
467
468void wake_lock_timeout(struct wake_lock *lock, long timeout)
469{
470 wake_lock_internal(lock, timeout, 1);
471}
472EXPORT_SYMBOL(wake_lock_timeout);
473
474void wake_unlock(struct wake_lock *lock)
475{
476 int type;
477 unsigned long irqflags;
478 spin_lock_irqsave(&list_lock, irqflags);
479 type = lock->flags & WAKE_LOCK_TYPE_MASK;
480#ifdef CONFIG_WAKELOCK_STAT
481 wake_unlock_stat_locked(lock, 0);
482#endif
483 if (debug_mask & DEBUG_WAKE_LOCK)
484 pr_info("wake_unlock: %s\n", lock->name);
485 lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
486 list_del(&lock->link);
487 list_add(&lock->link, &inactive_locks);
488 if (type == WAKE_LOCK_SUSPEND) {
489 long has_lock = has_wake_lock_locked(type);
490 if (has_lock > 0) {
491 if (debug_mask & DEBUG_EXPIRE)
492 pr_info("wake_unlock: %s, start expire timer, "
493 "%ld\n", lock->name, has_lock);
494 mod_timer(&expire_timer, jiffies + has_lock);
495 } else {
496 if (del_timer(&expire_timer))
497 if (debug_mask & DEBUG_EXPIRE)
498 pr_info("wake_unlock: %s, stop expire "
499 "timer\n", lock->name);
500 if (has_lock == 0)
501 queue_work(suspend_work_queue, &suspend_work);
502 }
503 if (lock == &main_wake_lock) {
504 if (debug_mask & DEBUG_SUSPEND)
505 print_active_locks(WAKE_LOCK_SUSPEND);
506#ifdef CONFIG_WAKELOCK_STAT
507 update_sleep_wait_stats_locked(0);
508#endif
509 }
510 }
511 spin_unlock_irqrestore(&list_lock, irqflags);
512}
513EXPORT_SYMBOL(wake_unlock);
514
515int wake_lock_active(struct wake_lock *lock)
516{
517 return !!(lock->flags & WAKE_LOCK_ACTIVE);
518}
519EXPORT_SYMBOL(wake_lock_active);
520
Arve Hjønnevåg1b074952009-12-02 18:22:00 -0800521static int wakelock_stats_open(struct inode *inode, struct file *file)
522{
523 return single_open(file, wakelock_stats_show, NULL);
524}
525
526static const struct file_operations wakelock_stats_fops = {
527 .owner = THIS_MODULE,
528 .open = wakelock_stats_open,
529 .read = seq_read,
530 .llseek = seq_lseek,
531 .release = single_release,
532};
533
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700534static int __init wakelocks_init(void)
535{
536 int ret;
537 int i;
538
539 for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
540 INIT_LIST_HEAD(&active_wake_locks[i]);
541
542#ifdef CONFIG_WAKELOCK_STAT
543 wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,
544 "deleted_wake_locks");
545#endif
546 wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");
547 wake_lock(&main_wake_lock);
548 wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups");
549
550 ret = platform_device_register(&power_device);
551 if (ret) {
552 pr_err("wakelocks_init: platform_device_register failed\n");
553 goto err_platform_device_register;
554 }
555 ret = platform_driver_register(&power_driver);
556 if (ret) {
557 pr_err("wakelocks_init: platform_driver_register failed\n");
558 goto err_platform_driver_register;
559 }
560
561 suspend_work_queue = create_singlethread_workqueue("suspend");
562 if (suspend_work_queue == NULL) {
563 ret = -ENOMEM;
564 goto err_suspend_work_queue;
565 }
566
567#ifdef CONFIG_WAKELOCK_STAT
Arve Hjønnevåg1b074952009-12-02 18:22:00 -0800568 proc_create("wakelocks", S_IRUGO, NULL, &wakelock_stats_fops);
Arve Hjønnevågfe6cd632008-09-09 22:14:34 -0700569#endif
570
571 return 0;
572
573err_suspend_work_queue:
574 platform_driver_unregister(&power_driver);
575err_platform_driver_register:
576 platform_device_unregister(&power_device);
577err_platform_device_register:
578 wake_lock_destroy(&unknown_wakeup);
579 wake_lock_destroy(&main_wake_lock);
580#ifdef CONFIG_WAKELOCK_STAT
581 wake_lock_destroy(&deleted_wake_locks);
582#endif
583 return ret;
584}
585
586static void __exit wakelocks_exit(void)
587{
588#ifdef CONFIG_WAKELOCK_STAT
589 remove_proc_entry("wakelocks", NULL);
590#endif
591 destroy_workqueue(suspend_work_queue);
592 platform_driver_unregister(&power_driver);
593 platform_device_unregister(&power_device);
594 wake_lock_destroy(&unknown_wakeup);
595 wake_lock_destroy(&main_wake_lock);
596#ifdef CONFIG_WAKELOCK_STAT
597 wake_lock_destroy(&deleted_wake_locks);
598#endif
599}
600
601core_initcall(wakelocks_init);
602module_exit(wakelocks_exit);