aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2011-02-10 05:04:45 -0500
committerSantosh Shilimkar <santosh.shilimkar@ti.com>2011-09-23 02:35:29 -0400
commitab10023e0088d5075354afc7cb9e72304757dddd (patch)
tree5e8424c9818056335baeefcddab18b0600417053
parentb6fd41e29dea9c6753b1843a77e50433e6123bcb (diff)
cpu_pm: Add cpu power management notifiers
During some CPU power modes entered during idle, hotplug and suspend, peripherals located in the CPU power domain, such as the GIC, localtimers, and VFP, may be powered down. Add a notifier chain that allows drivers for those peripherals to be notified before and after they may be reset. Notified drivers can include VFP co-processor, interrupt controller and it's PM extensions, local CPU timers context save/restore which shouldn't be interrupted. Hence CPU PM event APIs must be called with interrupts disabled. Signed-off-by: Colin Cross <ccross@android.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-and-Acked-by: Shawn Guo <shawn.guo@linaro.org> Tested-by: Kevin Hilman <khilman@ti.com> Tested-by: Vishwanath BS <vishwanath.bs@ti.com>
-rw-r--r--include/linux/cpu_pm.h109
-rw-r--r--kernel/Makefile1
-rw-r--r--kernel/cpu_pm.c200
-rw-r--r--kernel/power/Kconfig4
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 */
49enum 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
70int cpu_pm_register_notifier(struct notifier_block *nb);
71int cpu_pm_unregister_notifier(struct notifier_block *nb);
72int cpu_pm_enter(void);
73int cpu_pm_exit(void);
74int cpu_cluster_pm_enter(void);
75int cpu_cluster_pm_exit(void);
76
77#else
78
79static inline int cpu_pm_register_notifier(struct notifier_block *nb)
80{
81 return 0;
82}
83
84static inline int cpu_pm_unregister_notifier(struct notifier_block *nb)
85{
86 return 0;
87}
88
89static inline int cpu_pm_enter(void)
90{
91 return 0;
92}
93
94static inline int cpu_pm_exit(void)
95{
96 return 0;
97}
98
99static inline int cpu_cluster_pm_enter(void)
100{
101 return 0;
102}
103
104static 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/
101obj-$(CONFIG_TRACEPOINTS) += trace/ 101obj-$(CONFIG_TRACEPOINTS) += trace/
102obj-$(CONFIG_SMP) += sched_cpupri.o 102obj-$(CONFIG_SMP) += sched_cpupri.o
103obj-$(CONFIG_IRQ_WORK) += irq_work.o 103obj-$(CONFIG_IRQ_WORK) += irq_work.o
104obj-$(CONFIG_CPU_PM) += cpu_pm.o
104 105
105obj-$(CONFIG_PERF_EVENTS) += events/ 106obj-$(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
24static DEFINE_RWLOCK(cpu_pm_notifier_lock);
25static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
26
27static 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 */
47int 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}
58EXPORT_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 */
69int 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}
80EXPORT_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 */
97int 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}
114EXPORT_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 */
128int 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}
138EXPORT_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 */
156int 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}
173EXPORT_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 */
190int 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}
200EXPORT_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
235config PM_GENERIC_DOMAINS_RUNTIME 235config 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
239config CPU_PM
240 bool
241 depends on SUSPEND || CPU_IDLE