diff options
-rw-r--r-- | include/linux/cpu_pm.h | 109 | ||||
-rw-r--r-- | kernel/Makefile | 1 | ||||
-rw-r--r-- | kernel/cpu_pm.c | 200 | ||||
-rw-r--r-- | kernel/power/Kconfig | 4 |
4 files changed, 314 insertions, 0 deletions
diff --git a/include/linux/cpu_pm.h b/include/linux/cpu_pm.h new file mode 100644 index 000000000000..455b233dd3b1 --- /dev/null +++ b/include/linux/cpu_pm.h | |||
@@ -0,0 +1,109 @@ | |||
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 | #ifndef _LINUX_CPU_PM_H | ||
19 | #define _LINUX_CPU_PM_H | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/notifier.h> | ||
23 | |||
24 | /* | ||
25 | * When a CPU goes to a low power state that turns off power to the CPU's | ||
26 | * power domain, the contents of some blocks (floating point coprocessors, | ||
27 | * interrupt controllers, caches, timers) in the same power domain can | ||
28 | * be lost. The cpm_pm notifiers provide a method for platform idle, suspend, | ||
29 | * and hotplug implementations to notify the drivers for these blocks that | ||
30 | * they may be reset. | ||
31 | * | ||
32 | * All cpu_pm notifications must be called with interrupts disabled. | ||
33 | * | ||
34 | * The notifications are split into two classes: CPU notifications and CPU | ||
35 | * cluster notifications. | ||
36 | * | ||
37 | * CPU notifications apply to a single CPU and must be called on the affected | ||
38 | * CPU. They are used to save per-cpu context for affected blocks. | ||
39 | * | ||
40 | * CPU cluster notifications apply to all CPUs in a single power domain. They | ||
41 | * are used to save any global context for affected blocks, and must be called | ||
42 | * after all the CPUs in the power domain have been notified of the low power | ||
43 | * state. | ||
44 | */ | ||
45 | |||
46 | /* | ||
47 | * Event codes passed as unsigned long val to notifier calls | ||
48 | */ | ||
49 | enum cpu_pm_event { | ||
50 | /* A single cpu is entering a low power state */ | ||
51 | CPU_PM_ENTER, | ||
52 | |||
53 | /* A single cpu failed to enter a low power state */ | ||
54 | CPU_PM_ENTER_FAILED, | ||
55 | |||
56 | /* A single cpu is exiting a low power state */ | ||
57 | CPU_PM_EXIT, | ||
58 | |||
59 | /* A cpu power domain is entering a low power state */ | ||
60 | CPU_CLUSTER_PM_ENTER, | ||
61 | |||
62 | /* A cpu power domain failed to enter a low power state */ | ||
63 | CPU_CLUSTER_PM_ENTER_FAILED, | ||
64 | |||
65 | /* A cpu power domain is exiting a low power state */ | ||
66 | CPU_CLUSTER_PM_EXIT, | ||
67 | }; | ||
68 | |||
69 | #ifdef CONFIG_CPU_PM | ||
70 | int cpu_pm_register_notifier(struct notifier_block *nb); | ||
71 | int cpu_pm_unregister_notifier(struct notifier_block *nb); | ||
72 | int cpu_pm_enter(void); | ||
73 | int cpu_pm_exit(void); | ||
74 | int cpu_cluster_pm_enter(void); | ||
75 | int cpu_cluster_pm_exit(void); | ||
76 | |||
77 | #else | ||
78 | |||
79 | static inline int cpu_pm_register_notifier(struct notifier_block *nb) | ||
80 | { | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static inline int cpu_pm_unregister_notifier(struct notifier_block *nb) | ||
85 | { | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static inline int cpu_pm_enter(void) | ||
90 | { | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static inline int cpu_pm_exit(void) | ||
95 | { | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static inline int cpu_cluster_pm_enter(void) | ||
100 | { | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static inline int cpu_cluster_pm_exit(void) | ||
105 | { | ||
106 | return 0; | ||
107 | } | ||
108 | #endif | ||
109 | #endif | ||
diff --git a/kernel/Makefile b/kernel/Makefile index eca595e2fd52..988cb3da7031 100644 --- a/kernel/Makefile +++ b/kernel/Makefile | |||
@@ -101,6 +101,7 @@ obj-$(CONFIG_RING_BUFFER) += trace/ | |||
101 | obj-$(CONFIG_TRACEPOINTS) += trace/ | 101 | obj-$(CONFIG_TRACEPOINTS) += trace/ |
102 | obj-$(CONFIG_SMP) += sched_cpupri.o | 102 | obj-$(CONFIG_SMP) += sched_cpupri.o |
103 | obj-$(CONFIG_IRQ_WORK) += irq_work.o | 103 | obj-$(CONFIG_IRQ_WORK) += irq_work.o |
104 | obj-$(CONFIG_CPU_PM) += cpu_pm.o | ||
104 | 105 | ||
105 | obj-$(CONFIG_PERF_EVENTS) += events/ | 106 | obj-$(CONFIG_PERF_EVENTS) += events/ |
106 | 107 | ||
diff --git a/kernel/cpu_pm.c b/kernel/cpu_pm.c new file mode 100644 index 000000000000..4d1ff4acd04b --- /dev/null +++ b/kernel/cpu_pm.c | |||
@@ -0,0 +1,200 @@ | |||
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 | |||
24 | static DEFINE_RWLOCK(cpu_pm_notifier_lock); | ||
25 | static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain); | ||
26 | |||
27 | static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls) | ||
28 | { | ||
29 | int ret; | ||
30 | |||
31 | ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL, | ||
32 | nr_to_call, nr_calls); | ||
33 | |||
34 | return notifier_to_errno(ret); | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | * cpu_pm_register_notifier - register a driver with cpu_pm | ||
39 | * @nb: notifier block to register | ||
40 | * | ||
41 | * Add a driver to a list of drivers that are notified about | ||
42 | * CPU and CPU cluster low power entry and exit. | ||
43 | * | ||
44 | * This function may sleep, and has the same return conditions as | ||
45 | * raw_notifier_chain_register. | ||
46 | */ | ||
47 | int cpu_pm_register_notifier(struct notifier_block *nb) | ||
48 | { | ||
49 | unsigned long flags; | ||
50 | int ret; | ||
51 | |||
52 | write_lock_irqsave(&cpu_pm_notifier_lock, flags); | ||
53 | ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb); | ||
54 | write_unlock_irqrestore(&cpu_pm_notifier_lock, flags); | ||
55 | |||
56 | return ret; | ||
57 | } | ||
58 | EXPORT_SYMBOL_GPL(cpu_pm_register_notifier); | ||
59 | |||
60 | /** | ||
61 | * cpu_pm_unregister_notifier - unregister a driver with cpu_pm | ||
62 | * @nb: notifier block to be unregistered | ||
63 | * | ||
64 | * Remove a driver from the CPU PM notifier list. | ||
65 | * | ||
66 | * This function may sleep, and has the same return conditions as | ||
67 | * raw_notifier_chain_unregister. | ||
68 | */ | ||
69 | int cpu_pm_unregister_notifier(struct notifier_block *nb) | ||
70 | { | ||
71 | unsigned long flags; | ||
72 | int ret; | ||
73 | |||
74 | write_lock_irqsave(&cpu_pm_notifier_lock, flags); | ||
75 | ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb); | ||
76 | write_unlock_irqrestore(&cpu_pm_notifier_lock, flags); | ||
77 | |||
78 | return ret; | ||
79 | } | ||
80 | EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier); | ||
81 | |||
82 | /** | ||
83 | * cpm_pm_enter - CPU low power entry notifier | ||
84 | * | ||
85 | * Notifies listeners that a single CPU is entering a low power state that may | ||
86 | * cause some blocks in the same power domain as the cpu to reset. | ||
87 | * | ||
88 | * Must be called on the affected CPU with interrupts disabled. Platform is | ||
89 | * responsible for ensuring that cpu_pm_enter is not called twice on the same | ||
90 | * CPU before cpu_pm_exit is called. Notified drivers can include VFP | ||
91 | * co-processor, interrupt controller and it's PM extensions, local CPU | ||
92 | * timers context save/restore which shouldn't be interrupted. Hence it | ||
93 | * must be called with interrupts disabled. | ||
94 | * | ||
95 | * Return conditions are same as __raw_notifier_call_chain. | ||
96 | */ | ||
97 | int cpu_pm_enter(void) | ||
98 | { | ||
99 | int nr_calls; | ||
100 | int ret = 0; | ||
101 | |||
102 | read_lock(&cpu_pm_notifier_lock); | ||
103 | ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls); | ||
104 | if (ret) | ||
105 | /* | ||
106 | * Inform listeners (nr_calls - 1) about failure of CPU PM | ||
107 | * PM entry who are notified earlier to prepare for it. | ||
108 | */ | ||
109 | cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL); | ||
110 | read_unlock(&cpu_pm_notifier_lock); | ||
111 | |||
112 | return ret; | ||
113 | } | ||
114 | EXPORT_SYMBOL_GPL(cpu_pm_enter); | ||
115 | |||
116 | /** | ||
117 | * cpm_pm_exit - CPU low power exit notifier | ||
118 | * | ||
119 | * Notifies listeners that a single CPU is exiting a low power state that may | ||
120 | * have caused some blocks in the same power domain as the cpu to reset. | ||
121 | * | ||
122 | * Notified drivers can include VFP co-processor, interrupt controller | ||
123 | * and it's PM extensions, local CPU timers context save/restore which | ||
124 | * shouldn't be interrupted. Hence it must be called with interrupts disabled. | ||
125 | * | ||
126 | * Return conditions are same as __raw_notifier_call_chain. | ||
127 | */ | ||
128 | int cpu_pm_exit(void) | ||
129 | { | ||
130 | int ret; | ||
131 | |||
132 | read_lock(&cpu_pm_notifier_lock); | ||
133 | ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL); | ||
134 | read_unlock(&cpu_pm_notifier_lock); | ||
135 | |||
136 | return ret; | ||
137 | } | ||
138 | EXPORT_SYMBOL_GPL(cpu_pm_exit); | ||
139 | |||
140 | /** | ||
141 | * cpm_cluster_pm_enter - CPU cluster low power entry notifier | ||
142 | * | ||
143 | * Notifies listeners that all cpus in a power domain are entering a low power | ||
144 | * state that may cause some blocks in the same power domain to reset. | ||
145 | * | ||
146 | * Must be called after cpu_pm_enter has been called on all cpus in the power | ||
147 | * domain, and before cpu_pm_exit has been called on any cpu in the power | ||
148 | * domain. Notified drivers can include VFP co-processor, interrupt controller | ||
149 | * and it's PM extensions, local CPU timers context save/restore which | ||
150 | * shouldn't be interrupted. Hence it must be called with interrupts disabled. | ||
151 | * | ||
152 | * Must be called with interrupts disabled. | ||
153 | * | ||
154 | * Return conditions are same as __raw_notifier_call_chain. | ||
155 | */ | ||
156 | int cpu_cluster_pm_enter(void) | ||
157 | { | ||
158 | int nr_calls; | ||
159 | int ret = 0; | ||
160 | |||
161 | read_lock(&cpu_pm_notifier_lock); | ||
162 | ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls); | ||
163 | if (ret) | ||
164 | /* | ||
165 | * Inform listeners (nr_calls - 1) about failure of CPU cluster | ||
166 | * PM entry who are notified earlier to prepare for it. | ||
167 | */ | ||
168 | cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL); | ||
169 | read_unlock(&cpu_pm_notifier_lock); | ||
170 | |||
171 | return ret; | ||
172 | } | ||
173 | EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter); | ||
174 | |||
175 | /** | ||
176 | * cpm_cluster_pm_exit - CPU cluster low power exit notifier | ||
177 | * | ||
178 | * Notifies listeners that all cpus in a power domain are exiting form a | ||
179 | * low power state that may have caused some blocks in the same power domain | ||
180 | * to reset. | ||
181 | * | ||
182 | * Must be called after cpu_pm_exit has been called on all cpus in the power | ||
183 | * domain, and before cpu_pm_exit has been called on any cpu in the power | ||
184 | * domain. Notified drivers can include VFP co-processor, interrupt controller | ||
185 | * and it's PM extensions, local CPU timers context save/restore which | ||
186 | * shouldn't be interrupted. Hence it must be called with interrupts disabled. | ||
187 | * | ||
188 | * Return conditions are same as __raw_notifier_call_chain. | ||
189 | */ | ||
190 | int cpu_cluster_pm_exit(void) | ||
191 | { | ||
192 | int ret; | ||
193 | |||
194 | read_lock(&cpu_pm_notifier_lock); | ||
195 | ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL); | ||
196 | read_unlock(&cpu_pm_notifier_lock); | ||
197 | |||
198 | return ret; | ||
199 | } | ||
200 | EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit); | ||
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 3744c594b19b..80a85971cf64 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig | |||
@@ -235,3 +235,7 @@ config PM_GENERIC_DOMAINS | |||
235 | config PM_GENERIC_DOMAINS_RUNTIME | 235 | config PM_GENERIC_DOMAINS_RUNTIME |
236 | def_bool y | 236 | def_bool y |
237 | depends on PM_RUNTIME && PM_GENERIC_DOMAINS | 237 | depends on PM_RUNTIME && PM_GENERIC_DOMAINS |
238 | |||
239 | config CPU_PM | ||
240 | bool | ||
241 | depends on SUSPEND || CPU_IDLE | ||