Branch data Line data Source code
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 : 0 : static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
29 : : {
30 : : int ret;
31 : :
32 : 9931233 : ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
33 : : nr_to_call, nr_calls);
34 : :
35 : 0 : 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 : 0 : int cpu_pm_register_notifier(struct notifier_block *nb)
49 : : {
50 : : unsigned long flags;
51 : : int ret;
52 : :
53 : 0 : write_lock_irqsave(&cpu_pm_notifier_lock, flags);
54 : 0 : ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb);
55 : 0 : write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
56 : :
57 : 0 : 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 : 0 : int cpu_pm_unregister_notifier(struct notifier_block *nb)
71 : : {
72 : : unsigned long flags;
73 : : int ret;
74 : :
75 : 0 : write_lock_irqsave(&cpu_pm_notifier_lock, flags);
76 : 0 : ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
77 : 0 : write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
78 : :
79 : 0 : return ret;
80 : : }
81 : : EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
82 : :
83 : : /**
84 : : * cpu_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 its 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 : 0 : int cpu_pm_enter(void)
99 : : {
100 : : int nr_calls;
101 : : int ret = 0;
102 : :
103 : 4967720 : read_lock(&cpu_pm_notifier_lock);
104 : 4967692 : ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
105 [ - + ]: 4962367 : 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 : 0 : cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
111 : : read_unlock(&cpu_pm_notifier_lock);
112 : :
113 : 4967698 : return ret;
114 : : }
115 : : EXPORT_SYMBOL_GPL(cpu_pm_enter);
116 : :
117 : : /**
118 : : * cpu_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 its 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 : 0 : int cpu_pm_exit(void)
130 : : {
131 : : int ret;
132 : :
133 : 4967716 : read_lock(&cpu_pm_notifier_lock);
134 : 4967695 : ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
135 : : read_unlock(&cpu_pm_notifier_lock);
136 : :
137 : 4890368 : return ret;
138 : : }
139 : : EXPORT_SYMBOL_GPL(cpu_pm_exit);
140 : :
141 : : /**
142 : : * cpu_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 its 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 : 0 : int cpu_cluster_pm_enter(void)
158 : : {
159 : : int nr_calls;
160 : : int ret = 0;
161 : :
162 : 0 : read_lock(&cpu_pm_notifier_lock);
163 : 0 : ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
164 [ # # ]: 0 : 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 : 0 : cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
170 : : read_unlock(&cpu_pm_notifier_lock);
171 : :
172 : 0 : return ret;
173 : : }
174 : : EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
175 : :
176 : : /**
177 : : * cpu_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 its 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 : 0 : int cpu_cluster_pm_exit(void)
192 : : {
193 : : int ret;
194 : :
195 : 0 : read_lock(&cpu_pm_notifier_lock);
196 : 0 : ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
197 : : read_unlock(&cpu_pm_notifier_lock);
198 : :
199 : 0 : return ret;
200 : : }
201 : : EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
202 : :
203 : : #ifdef CONFIG_PM
204 : 0 : static int cpu_pm_suspend(void)
205 : : {
206 : : int ret;
207 : :
208 : 0 : ret = cpu_pm_enter();
209 [ # # ]: 0 : if (ret)
210 : : return ret;
211 : :
212 : 0 : ret = cpu_cluster_pm_enter();
213 : 0 : return ret;
214 : : }
215 : :
216 : 0 : static void cpu_pm_resume(void)
217 : : {
218 : 0 : cpu_cluster_pm_exit();
219 : 0 : cpu_pm_exit();
220 : 0 : }
221 : :
222 : : static struct syscore_ops cpu_pm_syscore_ops = {
223 : : .suspend = cpu_pm_suspend,
224 : : .resume = cpu_pm_resume,
225 : : };
226 : :
227 : 0 : static int cpu_pm_init(void)
228 : : {
229 : 0 : register_syscore_ops(&cpu_pm_syscore_ops);
230 : 0 : return 0;
231 : : }
232 : : core_initcall(cpu_pm_init);
233 : : #endif
|