Linux 3.2.80
[pandora-kernel.git] / kernel / cpu_pm.c
1 /*
2  * Copyright (C) 2011 Google, Inc.
3  *
4  * Author:
5  *      Colin Cross <ccross@android.com>
6  *
7  * This software is licensed under the terms of the GNU General Public
8  * License version 2, as published by the Free Software Foundation, and
9  * may be copied, distributed, and modified under those terms.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17
18 #include <linux/kernel.h>
19 #include <linux/cpu_pm.h>
20 #include <linux/module.h>
21 #include <linux/notifier.h>
22 #include <linux/spinlock.h>
23 #include <linux/syscore_ops.h>
24
25 static DEFINE_RWLOCK(cpu_pm_notifier_lock);
26 static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
27
28 static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
29 {
30         int ret;
31
32         ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
33                 nr_to_call, nr_calls);
34
35         return notifier_to_errno(ret);
36 }
37
38 /**
39  * cpu_pm_register_notifier - register a driver with cpu_pm
40  * @nb: notifier block to register
41  *
42  * Add a driver to a list of drivers that are notified about
43  * CPU and CPU cluster low power entry and exit.
44  *
45  * This function may sleep, and has the same return conditions as
46  * raw_notifier_chain_register.
47  */
48 int cpu_pm_register_notifier(struct notifier_block *nb)
49 {
50         unsigned long flags;
51         int ret;
52
53         write_lock_irqsave(&cpu_pm_notifier_lock, flags);
54         ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb);
55         write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
56
57         return ret;
58 }
59 EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
60
61 /**
62  * cpu_pm_unregister_notifier - unregister a driver with cpu_pm
63  * @nb: notifier block to be unregistered
64  *
65  * Remove a driver from the CPU PM notifier list.
66  *
67  * This function may sleep, and has the same return conditions as
68  * raw_notifier_chain_unregister.
69  */
70 int cpu_pm_unregister_notifier(struct notifier_block *nb)
71 {
72         unsigned long flags;
73         int ret;
74
75         write_lock_irqsave(&cpu_pm_notifier_lock, flags);
76         ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
77         write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
78
79         return ret;
80 }
81 EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
82
83 /**
84  * cpm_pm_enter - CPU low power entry notifier
85  *
86  * Notifies listeners that a single CPU is entering a low power state that may
87  * cause some blocks in the same power domain as the cpu to reset.
88  *
89  * Must be called on the affected CPU with interrupts disabled.  Platform is
90  * responsible for ensuring that cpu_pm_enter is not called twice on the same
91  * CPU before cpu_pm_exit is called. Notified drivers can include VFP
92  * co-processor, interrupt controller and it's PM extensions, local CPU
93  * timers context save/restore which shouldn't be interrupted. Hence it
94  * must be called with interrupts disabled.
95  *
96  * Return conditions are same as __raw_notifier_call_chain.
97  */
98 int cpu_pm_enter(void)
99 {
100         int nr_calls;
101         int ret = 0;
102
103         read_lock(&cpu_pm_notifier_lock);
104         ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
105         if (ret)
106                 /*
107                  * Inform listeners (nr_calls - 1) about failure of CPU PM
108                  * PM entry who are notified earlier to prepare for it.
109                  */
110                 cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
111         read_unlock(&cpu_pm_notifier_lock);
112
113         return ret;
114 }
115 EXPORT_SYMBOL_GPL(cpu_pm_enter);
116
117 /**
118  * cpm_pm_exit - CPU low power exit notifier
119  *
120  * Notifies listeners that a single CPU is exiting a low power state that may
121  * have caused some blocks in the same power domain as the cpu to reset.
122  *
123  * Notified drivers can include VFP co-processor, interrupt controller
124  * and it's PM extensions, local CPU timers context save/restore which
125  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
126  *
127  * Return conditions are same as __raw_notifier_call_chain.
128  */
129 int cpu_pm_exit(void)
130 {
131         int ret;
132
133         read_lock(&cpu_pm_notifier_lock);
134         ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
135         read_unlock(&cpu_pm_notifier_lock);
136
137         return ret;
138 }
139 EXPORT_SYMBOL_GPL(cpu_pm_exit);
140
141 /**
142  * cpm_cluster_pm_enter - CPU cluster low power entry notifier
143  *
144  * Notifies listeners that all cpus in a power domain are entering a low power
145  * state that may cause some blocks in the same power domain to reset.
146  *
147  * Must be called after cpu_pm_enter has been called on all cpus in the power
148  * domain, and before cpu_pm_exit has been called on any cpu in the power
149  * domain. Notified drivers can include VFP co-processor, interrupt controller
150  * and it's PM extensions, local CPU timers context save/restore which
151  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
152  *
153  * Must be called with interrupts disabled.
154  *
155  * Return conditions are same as __raw_notifier_call_chain.
156  */
157 int cpu_cluster_pm_enter(void)
158 {
159         int nr_calls;
160         int ret = 0;
161
162         read_lock(&cpu_pm_notifier_lock);
163         ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
164         if (ret)
165                 /*
166                  * Inform listeners (nr_calls - 1) about failure of CPU cluster
167                  * PM entry who are notified earlier to prepare for it.
168                  */
169                 cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
170         read_unlock(&cpu_pm_notifier_lock);
171
172         return ret;
173 }
174 EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
175
176 /**
177  * cpm_cluster_pm_exit - CPU cluster low power exit notifier
178  *
179  * Notifies listeners that all cpus in a power domain are exiting form a
180  * low power state that may have caused some blocks in the same power domain
181  * to reset.
182  *
183  * Must be called after cpu_pm_exit has been called on all cpus in the power
184  * domain, and before cpu_pm_exit has been called on any cpu in the power
185  * domain. Notified drivers can include VFP co-processor, interrupt controller
186  * and it's PM extensions, local CPU timers context save/restore which
187  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
188  *
189  * Return conditions are same as __raw_notifier_call_chain.
190  */
191 int cpu_cluster_pm_exit(void)
192 {
193         int ret;
194
195         read_lock(&cpu_pm_notifier_lock);
196         ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
197         read_unlock(&cpu_pm_notifier_lock);
198
199         return ret;
200 }
201 EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
202
203 #ifdef CONFIG_PM
204 static int cpu_pm_suspend(void)
205 {
206         int ret;
207
208         ret = cpu_pm_enter();
209         if (ret)
210                 return ret;
211
212         ret = cpu_cluster_pm_enter();
213         return ret;
214 }
215
216 static void cpu_pm_resume(void)
217 {
218         cpu_cluster_pm_exit();
219         cpu_pm_exit();
220 }
221
222 static struct syscore_ops cpu_pm_syscore_ops = {
223         .suspend = cpu_pm_suspend,
224         .resume = cpu_pm_resume,
225 };
226
227 static int cpu_pm_init(void)
228 {
229         register_syscore_ops(&cpu_pm_syscore_ops);
230         return 0;
231 }
232 core_initcall(cpu_pm_init);
233 #endif