1 /* kernel/power/earlysuspend.c
3 * Copyright (C) 2005-2008 Google, Inc.
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.
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.
16 #include <linux/earlysuspend.h>
17 #include <linux/module.h>
18 #include <linux/mutex.h>
19 #include <linux/rtc.h>
20 #include <linux/syscalls.h> /* sys_sync */
21 #include <linux/wakelock.h>
22 #include <linux/workqueue.h>
23 #include <linux/kallsyms.h>
28 DEBUG_USER_STATE = 1U << 0,
29 DEBUG_SUSPEND = 1U << 2,
32 static int debug_mask = DEBUG_USER_STATE | DEBUG_SUSPEND;
34 static int debug_mask = DEBUG_USER_STATE;
36 module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
38 static DEFINE_MUTEX(early_suspend_lock);
39 static LIST_HEAD(early_suspend_handlers);
40 static void early_suspend(struct work_struct *work);
41 static void late_resume(struct work_struct *work);
42 static DECLARE_WORK(early_suspend_work, early_suspend);
43 static DECLARE_WORK(late_resume_work, late_resume);
44 static DEFINE_SPINLOCK(state_lock);
46 SUSPEND_REQUESTED = 0x1,
48 SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED,
52 void register_early_suspend(struct early_suspend *handler)
54 struct list_head *pos;
56 mutex_lock(&early_suspend_lock);
57 list_for_each(pos, &early_suspend_handlers) {
58 struct early_suspend *e;
59 e = list_entry(pos, struct early_suspend, link);
60 if (e->level > handler->level)
63 list_add_tail(&handler->link, pos);
64 if ((state & SUSPENDED) && handler->suspend)
65 handler->suspend(handler);
66 mutex_unlock(&early_suspend_lock);
68 EXPORT_SYMBOL(register_early_suspend);
70 void unregister_early_suspend(struct early_suspend *handler)
72 mutex_lock(&early_suspend_lock);
73 list_del(&handler->link);
74 mutex_unlock(&early_suspend_lock);
76 EXPORT_SYMBOL(unregister_early_suspend);
78 static void early_suspend(struct work_struct *work)
80 struct early_suspend *pos;
81 unsigned long irqflags;
84 mutex_lock(&early_suspend_lock);
85 spin_lock_irqsave(&state_lock, irqflags);
86 if (state == SUSPEND_REQUESTED)
90 spin_unlock_irqrestore(&state_lock, irqflags);
93 if (debug_mask & DEBUG_SUSPEND)
94 pr_info("early_suspend: abort, state %d\n", state);
95 mutex_unlock(&early_suspend_lock);
99 if (debug_mask & DEBUG_SUSPEND)
100 pr_info("early_suspend: call handlers\n");
101 list_for_each_entry(pos, &early_suspend_handlers, link) {
102 if (debug_mask & DEBUG_SUSPEND)
103 print_symbol("early_suspend: call %s\n", (unsigned long)pos->suspend);
104 if (pos->suspend != NULL)
107 mutex_unlock(&early_suspend_lock);
109 if (debug_mask & DEBUG_SUSPEND)
110 pr_info("early_suspend: sync\n");
114 spin_lock_irqsave(&state_lock, irqflags);
115 if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
116 wake_unlock(&main_wake_lock);
117 spin_unlock_irqrestore(&state_lock, irqflags);
120 static void late_resume(struct work_struct *work)
122 struct early_suspend *pos;
123 unsigned long irqflags;
126 mutex_lock(&early_suspend_lock);
127 spin_lock_irqsave(&state_lock, irqflags);
128 if (state == SUSPENDED)
132 spin_unlock_irqrestore(&state_lock, irqflags);
135 if (debug_mask & DEBUG_SUSPEND)
136 pr_info("late_resume: abort, state %d\n", state);
139 if (debug_mask & DEBUG_SUSPEND)
140 pr_info("late_resume: call handlers\n");
141 list_for_each_entry_reverse(pos, &early_suspend_handlers, link) {
142 if (debug_mask & DEBUG_SUSPEND)
143 print_symbol("late_resume: call %s\n", (unsigned long)pos->resume);
144 if (pos->resume != NULL)
147 if (debug_mask & DEBUG_SUSPEND)
148 pr_info("late_resume: done\n");
150 mutex_unlock(&early_suspend_lock);
153 void request_suspend_state(suspend_state_t new_state)
155 unsigned long irqflags;
158 spin_lock_irqsave(&state_lock, irqflags);
159 old_sleep = state & SUSPEND_REQUESTED;
160 if (debug_mask & DEBUG_USER_STATE) {
164 rtc_time_to_tm(ts.tv_sec, &tm);
165 pr_info("request_suspend_state: %s (%d->%d) at %lld "
166 "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
167 new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
168 requested_suspend_state, new_state,
169 ktime_to_ns(ktime_get()),
170 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
171 tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
173 if (!old_sleep && new_state != PM_SUSPEND_ON) {
174 state |= SUSPEND_REQUESTED;
175 queue_work(suspend_work_queue, &early_suspend_work);
176 } else if (old_sleep && new_state == PM_SUSPEND_ON) {
177 state &= ~SUSPEND_REQUESTED;
178 wake_lock(&main_wake_lock);
179 queue_work(suspend_work_queue, &late_resume_work);
181 requested_suspend_state = new_state;
182 spin_unlock_irqrestore(&state_lock, irqflags);
185 suspend_state_t get_suspend_state(void)
187 return requested_suspend_state;