Merge branch 'develop' of 10.10.10.29:/home/rockchip/kernel into develop
[firefly-linux-kernel-4.4.55.git] / kernel / power / earlysuspend.c
1 /* kernel/power/earlysuspend.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/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>
24
25 #include "power.h"
26
27 enum {
28         DEBUG_USER_STATE = 1U << 0,
29         DEBUG_SUSPEND = 1U << 2,
30 };
31 #ifdef DEBUG
32 static int debug_mask = DEBUG_USER_STATE | DEBUG_SUSPEND;
33 #else
34 static int debug_mask = DEBUG_USER_STATE;
35 #endif
36 module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
37
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);
45 enum {
46         SUSPEND_REQUESTED = 0x1,
47         SUSPENDED = 0x2,
48         SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED,
49 };
50 static int state;
51
52 void register_early_suspend(struct early_suspend *handler)
53 {
54         struct list_head *pos;
55
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)
61                         break;
62         }
63         list_add_tail(&handler->link, pos);
64         if ((state & SUSPENDED) && handler->suspend)
65                 handler->suspend(handler);
66         mutex_unlock(&early_suspend_lock);
67 }
68 EXPORT_SYMBOL(register_early_suspend);
69
70 void unregister_early_suspend(struct early_suspend *handler)
71 {
72         mutex_lock(&early_suspend_lock);
73         list_del(&handler->link);
74         mutex_unlock(&early_suspend_lock);
75 }
76 EXPORT_SYMBOL(unregister_early_suspend);
77
78 static void early_suspend(struct work_struct *work)
79 {
80         struct early_suspend *pos;
81         unsigned long irqflags;
82         int abort = 0;
83
84         mutex_lock(&early_suspend_lock);
85         spin_lock_irqsave(&state_lock, irqflags);
86         if (state == SUSPEND_REQUESTED)
87                 state |= SUSPENDED;
88         else
89                 abort = 1;
90         spin_unlock_irqrestore(&state_lock, irqflags);
91
92         if (abort) {
93                 if (debug_mask & DEBUG_SUSPEND)
94                         pr_info("early_suspend: abort, state %d\n", state);
95                 mutex_unlock(&early_suspend_lock);
96                 goto abort;
97         }
98
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)
105                         pos->suspend(pos);
106         }
107         mutex_unlock(&early_suspend_lock);
108
109         if (debug_mask & DEBUG_SUSPEND)
110                 pr_info("early_suspend: sync\n");
111
112         sys_sync();
113 abort:
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);
118 }
119
120 static void late_resume(struct work_struct *work)
121 {
122         struct early_suspend *pos;
123         unsigned long irqflags;
124         int abort = 0;
125
126         mutex_lock(&early_suspend_lock);
127         spin_lock_irqsave(&state_lock, irqflags);
128         if (state == SUSPENDED)
129                 state &= ~SUSPENDED;
130         else
131                 abort = 1;
132         spin_unlock_irqrestore(&state_lock, irqflags);
133
134         if (abort) {
135                 if (debug_mask & DEBUG_SUSPEND)
136                         pr_info("late_resume: abort, state %d\n", state);
137                 goto abort;
138         }
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)
145                         pos->resume(pos);
146         }
147         if (debug_mask & DEBUG_SUSPEND)
148                 pr_info("late_resume: done\n");
149 abort:
150         mutex_unlock(&early_suspend_lock);
151 }
152
153 void request_suspend_state(suspend_state_t new_state)
154 {
155         unsigned long irqflags;
156         int old_sleep;
157
158         spin_lock_irqsave(&state_lock, irqflags);
159         old_sleep = state & SUSPEND_REQUESTED;
160         if (debug_mask & DEBUG_USER_STATE) {
161                 struct timespec ts;
162                 struct rtc_time tm;
163                 getnstimeofday(&ts);
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);
172         }
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);
180         }
181         requested_suspend_state = new_state;
182         spin_unlock_irqrestore(&state_lock, irqflags);
183 }
184
185 suspend_state_t get_suspend_state(void)
186 {
187         return requested_suspend_state;
188 }