aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-06-28 07:01:06 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-06-28 07:01:06 -0400
commit93bc11c3cc1f6d441df2362f0d3efea75dc0e3f4 (patch)
treea6adc2d83018b98e45c91dbbc9c80e95d8a671b1 /drivers
parent3e79a8a67d6f28b2b1d4344253ca612e77cca8e6 (diff)
parentb39b0981b0811943d724915a8a0150d6ac5110e0 (diff)
Merge branch 'pm-cpuidle'
* pm-cpuidle: cpuidle: Fix ARCH_NEEDS_CPU_IDLE_COUPLED dependency warning cpuidle: Comment the driver's framework code cpuidle: simplify multiple driver support ARM: zynq: Add cpuidle support cpuidle: improve governor Kconfig options
Diffstat (limited to 'drivers')
-rw-r--r--drivers/cpuidle/Kconfig27
-rw-r--r--drivers/cpuidle/Makefile1
-rw-r--r--drivers/cpuidle/cpuidle-zynq.c83
-rw-r--r--drivers/cpuidle/cpuidle.c4
-rw-r--r--drivers/cpuidle/driver.c324
5 files changed, 307 insertions, 132 deletions
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
index c4cc27e5c8a5..81de5d96749b 100644
--- a/drivers/cpuidle/Kconfig
+++ b/drivers/cpuidle/Kconfig
@@ -1,7 +1,9 @@
1 1
2config CPU_IDLE 2menuconfig CPU_IDLE
3 bool "CPU idle PM support" 3 bool "CPU idle PM support"
4 default y if ACPI || PPC_PSERIES 4 default y if ACPI || PPC_PSERIES
5 select CPU_IDLE_GOV_LADDER if (!NO_HZ && !NO_HZ_IDLE)
6 select CPU_IDLE_GOV_MENU if (NO_HZ || NO_HZ_IDLE)
5 help 7 help
6 CPU idle is a generic framework for supporting software-controlled 8 CPU idle is a generic framework for supporting software-controlled
7 idle processor power management. It includes modular cross-platform 9 idle processor power management. It includes modular cross-platform
@@ -9,9 +11,10 @@ config CPU_IDLE
9 11
10 If you're using an ACPI-enabled platform, you should say Y here. 12 If you're using an ACPI-enabled platform, you should say Y here.
11 13
14if CPU_IDLE
15
12config CPU_IDLE_MULTIPLE_DRIVERS 16config CPU_IDLE_MULTIPLE_DRIVERS
13 bool "Support multiple cpuidle drivers" 17 bool "Support multiple cpuidle drivers"
14 depends on CPU_IDLE
15 default n 18 default n
16 help 19 help
17 Allows the cpuidle framework to use different drivers for each CPU. 20 Allows the cpuidle framework to use different drivers for each CPU.
@@ -19,24 +22,26 @@ config CPU_IDLE_MULTIPLE_DRIVERS
19 states. If unsure say N. 22 states. If unsure say N.
20 23
21config CPU_IDLE_GOV_LADDER 24config CPU_IDLE_GOV_LADDER
22 bool 25 bool "Ladder governor (for periodic timer tick)"
23 depends on CPU_IDLE
24 default y 26 default y
25 27
26config CPU_IDLE_GOV_MENU 28config CPU_IDLE_GOV_MENU
27 bool 29 bool "Menu governor (for tickless system)"
28 depends on CPU_IDLE && NO_HZ
29 default y 30 default y
30 31
31config ARCH_NEEDS_CPU_IDLE_COUPLED
32 def_bool n
33
34if CPU_IDLE
35
36config CPU_IDLE_CALXEDA 32config CPU_IDLE_CALXEDA
37 bool "CPU Idle Driver for Calxeda processors" 33 bool "CPU Idle Driver for Calxeda processors"
38 depends on ARCH_HIGHBANK 34 depends on ARCH_HIGHBANK
39 help 35 help
40 Select this to enable cpuidle on Calxeda processors. 36 Select this to enable cpuidle on Calxeda processors.
41 37
38config CPU_IDLE_ZYNQ
39 bool "CPU Idle Driver for Xilinx Zynq processors"
40 depends on ARCH_ZYNQ
41 help
42 Select this to enable cpuidle on Xilinx Zynq processors.
43
42endif 44endif
45
46config ARCH_NEEDS_CPU_IDLE_COUPLED
47 def_bool n
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 0d8bd55e776f..8767a7b3eb91 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
7 7
8obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o 8obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o
9obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o 9obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o
10obj-$(CONFIG_CPU_IDLE_ZYNQ) += cpuidle-zynq.o
diff --git a/drivers/cpuidle/cpuidle-zynq.c b/drivers/cpuidle/cpuidle-zynq.c
new file mode 100644
index 000000000000..38e03a183591
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-zynq.c
@@ -0,0 +1,83 @@
1/*
2 * Copyright (C) 2012-2013 Xilinx
3 *
4 * CPU idle support for Xilinx Zynq
5 *
6 * based on arch/arm/mach-at91/cpuidle.c
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * The cpu idle uses wait-for-interrupt and RAM self refresh in order
21 * to implement two idle states -
22 * #1 wait-for-interrupt
23 * #2 wait-for-interrupt and RAM self refresh
24 *
25 * Maintainer: Michal Simek <michal.simek@xilinx.com>
26 */
27
28#include <linux/init.h>
29#include <linux/cpu_pm.h>
30#include <linux/cpuidle.h>
31#include <linux/of.h>
32#include <asm/proc-fns.h>
33#include <asm/cpuidle.h>
34
35#define ZYNQ_MAX_STATES 2
36
37/* Actual code that puts the SoC in different idle states */
38static int zynq_enter_idle(struct cpuidle_device *dev,
39 struct cpuidle_driver *drv, int index)
40{
41 /* Devices must be stopped here */
42 cpu_pm_enter();
43
44 /* Add code for DDR self refresh start */
45 cpu_do_idle();
46
47 /* Add code for DDR self refresh stop */
48 cpu_pm_exit();
49
50 return index;
51}
52
53static struct cpuidle_driver zynq_idle_driver = {
54 .name = "zynq_idle",
55 .owner = THIS_MODULE,
56 .states = {
57 ARM_CPUIDLE_WFI_STATE,
58 {
59 .enter = zynq_enter_idle,
60 .exit_latency = 10,
61 .target_residency = 10000,
62 .flags = CPUIDLE_FLAG_TIME_VALID |
63 CPUIDLE_FLAG_TIMER_STOP,
64 .name = "RAM_SR",
65 .desc = "WFI and RAM Self Refresh",
66 },
67 },
68 .safe_state_index = 0,
69 .state_count = ZYNQ_MAX_STATES,
70};
71
72/* Initialize CPU idle by registering the idle states */
73static int __init zynq_cpuidle_init(void)
74{
75 if (!of_machine_is_compatible("xlnx,zynq-7000"))
76 return -ENODEV;
77
78 pr_info("Xilinx Zynq CpuIdle Driver started\n");
79
80 return cpuidle_register(&zynq_idle_driver, NULL);
81}
82
83device_initcall(zynq_cpuidle_init);
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index c3a93fece819..fdc432f18022 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -466,7 +466,7 @@ void cpuidle_unregister(struct cpuidle_driver *drv)
466 int cpu; 466 int cpu;
467 struct cpuidle_device *device; 467 struct cpuidle_device *device;
468 468
469 for_each_possible_cpu(cpu) { 469 for_each_cpu(cpu, drv->cpumask) {
470 device = &per_cpu(cpuidle_dev, cpu); 470 device = &per_cpu(cpuidle_dev, cpu);
471 cpuidle_unregister_device(device); 471 cpuidle_unregister_device(device);
472 } 472 }
@@ -498,7 +498,7 @@ int cpuidle_register(struct cpuidle_driver *drv,
498 return ret; 498 return ret;
499 } 499 }
500 500
501 for_each_possible_cpu(cpu) { 501 for_each_cpu(cpu, drv->cpumask) {
502 device = &per_cpu(cpuidle_dev, cpu); 502 device = &per_cpu(cpuidle_dev, cpu);
503 device->cpu = cpu; 503 device->cpu = cpu;
504 504
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index 8dfaaae94444..3ac499d5a207 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -18,182 +18,249 @@
18 18
19DEFINE_SPINLOCK(cpuidle_driver_lock); 19DEFINE_SPINLOCK(cpuidle_driver_lock);
20 20
21static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); 21#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
22static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
23 22
24static void cpuidle_setup_broadcast_timer(void *arg) 23static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers);
24
25/**
26 * __cpuidle_get_cpu_driver - return the cpuidle driver tied to a CPU.
27 * @cpu: the CPU handled by the driver
28 *
29 * Returns a pointer to struct cpuidle_driver or NULL if no driver has been
30 * registered for @cpu.
31 */
32static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
25{ 33{
26 int cpu = smp_processor_id(); 34 return per_cpu(cpuidle_drivers, cpu);
27 clockevents_notify((long)(arg), &cpu);
28} 35}
29 36
30static void __cpuidle_driver_init(struct cpuidle_driver *drv, int cpu) 37/**
38 * __cpuidle_unset_driver - unset per CPU driver variables.
39 * @drv: a valid pointer to a struct cpuidle_driver
40 *
41 * For each CPU in the driver's CPU mask, unset the registered driver per CPU
42 * variable. If @drv is different from the registered driver, the corresponding
43 * variable is not cleared.
44 */
45static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv)
31{ 46{
32 int i; 47 int cpu;
33
34 drv->refcnt = 0;
35 48
36 for (i = drv->state_count - 1; i >= 0 ; i--) { 49 for_each_cpu(cpu, drv->cpumask) {
37 50
38 if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) 51 if (drv != __cpuidle_get_cpu_driver(cpu))
39 continue; 52 continue;
40 53
41 drv->bctimer = 1; 54 per_cpu(cpuidle_drivers, cpu) = NULL;
42 on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer,
43 (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1);
44 break;
45 } 55 }
46} 56}
47 57
48static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) 58/**
59 * __cpuidle_set_driver - set per CPU driver variables the the given driver.
60 * @drv: a valid pointer to a struct cpuidle_driver
61 *
62 * For each CPU in the driver's cpumask, unset the registered driver per CPU
63 * to @drv.
64 *
65 * Returns 0 on success, -EBUSY if the CPUs have driver(s) already.
66 */
67static inline int __cpuidle_set_driver(struct cpuidle_driver *drv)
49{ 68{
50 if (!drv || !drv->state_count) 69 int cpu;
51 return -EINVAL;
52
53 if (cpuidle_disabled())
54 return -ENODEV;
55 70
56 if (__cpuidle_get_cpu_driver(cpu)) 71 for_each_cpu(cpu, drv->cpumask) {
57 return -EBUSY;
58 72
59 __cpuidle_driver_init(drv, cpu); 73 if (__cpuidle_get_cpu_driver(cpu)) {
74 __cpuidle_unset_driver(drv);
75 return -EBUSY;
76 }
60 77
61 __cpuidle_set_cpu_driver(drv, cpu); 78 per_cpu(cpuidle_drivers, cpu) = drv;
79 }
62 80
63 return 0; 81 return 0;
64} 82}
65 83
66static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu) 84#else
67{
68 if (drv != __cpuidle_get_cpu_driver(cpu))
69 return;
70 85
71 if (!WARN_ON(drv->refcnt > 0)) 86static struct cpuidle_driver *cpuidle_curr_driver;
72 __cpuidle_set_cpu_driver(NULL, cpu);
73 87
74 if (drv->bctimer) { 88/**
75 drv->bctimer = 0; 89 * __cpuidle_get_cpu_driver - return the global cpuidle driver pointer.
76 on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, 90 * @cpu: ignored without the multiple driver support
77 (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); 91 *
78 } 92 * Return a pointer to a struct cpuidle_driver object or NULL if no driver was
93 * previously registered.
94 */
95static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
96{
97 return cpuidle_curr_driver;
79} 98}
80 99
81#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS 100/**
101 * __cpuidle_set_driver - assign the global cpuidle driver variable.
102 * @drv: pointer to a struct cpuidle_driver object
103 *
104 * Returns 0 on success, -EBUSY if the driver is already registered.
105 */
106static inline int __cpuidle_set_driver(struct cpuidle_driver *drv)
107{
108 if (cpuidle_curr_driver)
109 return -EBUSY;
82 110
83static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); 111 cpuidle_curr_driver = drv;
84 112
85static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) 113 return 0;
86{
87 per_cpu(cpuidle_drivers, cpu) = drv;
88} 114}
89 115
90static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) 116/**
117 * __cpuidle_unset_driver - unset the global cpuidle driver variable.
118 * @drv: a pointer to a struct cpuidle_driver
119 *
120 * Reset the global cpuidle variable to NULL. If @drv does not match the
121 * registered driver, do nothing.
122 */
123static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv)
91{ 124{
92 return per_cpu(cpuidle_drivers, cpu); 125 if (drv == cpuidle_curr_driver)
126 cpuidle_curr_driver = NULL;
93} 127}
94 128
95static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv) 129#endif
130
131/**
132 * cpuidle_setup_broadcast_timer - enable/disable the broadcast timer
133 * @arg: a void pointer used to match the SMP cross call API
134 *
135 * @arg is used as a value of type 'long' with on of the two values:
136 * - CLOCK_EVT_NOTIFY_BROADCAST_ON
137 * - CLOCK_EVT_NOTIFY_BROADCAST_OFF
138 *
139 * Set the broadcast timer notification for the current CPU. This function
140 * is executed per CPU by an SMP cross call. It not supposed to be called
141 * directly.
142 */
143static void cpuidle_setup_broadcast_timer(void *arg)
96{ 144{
97 int cpu; 145 int cpu = smp_processor_id();
98 for_each_present_cpu(cpu) 146 clockevents_notify((long)(arg), &cpu);
99 __cpuidle_unregister_driver(drv, cpu);
100} 147}
101 148
102static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv) 149/**
150 * __cpuidle_driver_init - initialize the driver's internal data
151 * @drv: a valid pointer to a struct cpuidle_driver
152 *
153 * Returns 0 on success, a negative error code otherwise.
154 */
155static int __cpuidle_driver_init(struct cpuidle_driver *drv)
103{ 156{
104 int ret = 0; 157 int i;
105 int i, cpu;
106 158
107 for_each_present_cpu(cpu) { 159 drv->refcnt = 0;
108 ret = __cpuidle_register_driver(drv, cpu);
109 if (ret)
110 break;
111 }
112 160
113 if (ret) 161 /*
114 for_each_present_cpu(i) { 162 * Use all possible CPUs as the default, because if the kernel boots
115 if (i == cpu) 163 * with some CPUs offline and then we online one of them, the CPU
116 break; 164 * notifier has to know which driver to assign.
117 __cpuidle_unregister_driver(drv, i); 165 */
118 } 166 if (!drv->cpumask)
167 drv->cpumask = (struct cpumask *)cpu_possible_mask;
168
169 /*
170 * Look for the timer stop flag in the different states, so that we know
171 * if the broadcast timer has to be set up. The loop is in the reverse
172 * order, because usually on of the the deeper states has this flag set.
173 */
174 for (i = drv->state_count - 1; i >= 0 ; i--) {
119 175
176 if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP))
177 continue;
120 178
121 return ret; 179 drv->bctimer = 1;
180 break;
181 }
182
183 return 0;
122} 184}
123 185
124int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu) 186/**
187 * __cpuidle_register_driver: register the driver
188 * @drv: a valid pointer to a struct cpuidle_driver
189 *
190 * Do some sanity checks, initialize the driver, assign the driver to the
191 * global cpuidle driver variable(s) and set up the broadcast timer if the
192 * cpuidle driver has some states that shut down the local timer.
193 *
194 * Returns 0 on success, a negative error code otherwise:
195 * * -EINVAL if the driver pointer is NULL or no idle states are available
196 * * -ENODEV if the cpuidle framework is disabled
197 * * -EBUSY if the driver is already assigned to the global variable(s)
198 */
199static int __cpuidle_register_driver(struct cpuidle_driver *drv)
125{ 200{
126 int ret; 201 int ret;
127 202
128 spin_lock(&cpuidle_driver_lock); 203 if (!drv || !drv->state_count)
129 ret = __cpuidle_register_driver(drv, cpu); 204 return -EINVAL;
130 spin_unlock(&cpuidle_driver_lock);
131 205
132 return ret; 206 if (cpuidle_disabled())
133} 207 return -ENODEV;
134 208
135void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu) 209 ret = __cpuidle_driver_init(drv);
136{ 210 if (ret)
137 spin_lock(&cpuidle_driver_lock); 211 return ret;
138 __cpuidle_unregister_driver(drv, cpu);
139 spin_unlock(&cpuidle_driver_lock);
140}
141 212
142/** 213 ret = __cpuidle_set_driver(drv);
143 * cpuidle_register_driver - registers a driver 214 if (ret)
144 * @drv: the driver 215 return ret;
145 */
146int cpuidle_register_driver(struct cpuidle_driver *drv)
147{
148 int ret;
149 216
150 spin_lock(&cpuidle_driver_lock); 217 if (drv->bctimer)
151 ret = __cpuidle_register_all_cpu_driver(drv); 218 on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer,
152 spin_unlock(&cpuidle_driver_lock); 219 (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1);
153 220
154 return ret; 221 return 0;
155} 222}
156EXPORT_SYMBOL_GPL(cpuidle_register_driver);
157 223
158/** 224/**
159 * cpuidle_unregister_driver - unregisters a driver 225 * __cpuidle_unregister_driver - unregister the driver
160 * @drv: the driver 226 * @drv: a valid pointer to a struct cpuidle_driver
227 *
228 * Check if the driver is no longer in use, reset the global cpuidle driver
229 * variable(s) and disable the timer broadcast notification mechanism if it was
230 * in use.
231 *
161 */ 232 */
162void cpuidle_unregister_driver(struct cpuidle_driver *drv) 233static void __cpuidle_unregister_driver(struct cpuidle_driver *drv)
163{ 234{
164 spin_lock(&cpuidle_driver_lock); 235 if (WARN_ON(drv->refcnt > 0))
165 __cpuidle_unregister_all_cpu_driver(drv); 236 return;
166 spin_unlock(&cpuidle_driver_lock);
167}
168EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
169
170#else
171
172static struct cpuidle_driver *cpuidle_curr_driver;
173 237
174static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) 238 if (drv->bctimer) {
175{ 239 drv->bctimer = 0;
176 cpuidle_curr_driver = drv; 240 on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer,
177} 241 (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1);
242 }
178 243
179static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) 244 __cpuidle_unset_driver(drv);
180{
181 return cpuidle_curr_driver;
182} 245}
183 246
184/** 247/**
185 * cpuidle_register_driver - registers a driver 248 * cpuidle_register_driver - registers a driver
186 * @drv: the driver 249 * @drv: a pointer to a valid struct cpuidle_driver
250 *
251 * Register the driver under a lock to prevent concurrent attempts to
252 * [un]register the driver from occuring at the same time.
253 *
254 * Returns 0 on success, a negative error code (returned by
255 * __cpuidle_register_driver()) otherwise.
187 */ 256 */
188int cpuidle_register_driver(struct cpuidle_driver *drv) 257int cpuidle_register_driver(struct cpuidle_driver *drv)
189{ 258{
190 int ret, cpu; 259 int ret;
191 260
192 cpu = get_cpu();
193 spin_lock(&cpuidle_driver_lock); 261 spin_lock(&cpuidle_driver_lock);
194 ret = __cpuidle_register_driver(drv, cpu); 262 ret = __cpuidle_register_driver(drv);
195 spin_unlock(&cpuidle_driver_lock); 263 spin_unlock(&cpuidle_driver_lock);
196 put_cpu();
197 264
198 return ret; 265 return ret;
199} 266}
@@ -201,23 +268,24 @@ EXPORT_SYMBOL_GPL(cpuidle_register_driver);
201 268
202/** 269/**
203 * cpuidle_unregister_driver - unregisters a driver 270 * cpuidle_unregister_driver - unregisters a driver
204 * @drv: the driver 271 * @drv: a pointer to a valid struct cpuidle_driver
272 *
273 * Unregisters the cpuidle driver under a lock to prevent concurrent attempts
274 * to [un]register the driver from occuring at the same time. @drv has to
275 * match the currently registered driver.
205 */ 276 */
206void cpuidle_unregister_driver(struct cpuidle_driver *drv) 277void cpuidle_unregister_driver(struct cpuidle_driver *drv)
207{ 278{
208 int cpu;
209
210 cpu = get_cpu();
211 spin_lock(&cpuidle_driver_lock); 279 spin_lock(&cpuidle_driver_lock);
212 __cpuidle_unregister_driver(drv, cpu); 280 __cpuidle_unregister_driver(drv);
213 spin_unlock(&cpuidle_driver_lock); 281 spin_unlock(&cpuidle_driver_lock);
214 put_cpu();
215} 282}
216EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); 283EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
217#endif
218 284
219/** 285/**
220 * cpuidle_get_driver - return the current driver 286 * cpuidle_get_driver - return the driver tied to the current CPU.
287 *
288 * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered.
221 */ 289 */
222struct cpuidle_driver *cpuidle_get_driver(void) 290struct cpuidle_driver *cpuidle_get_driver(void)
223{ 291{
@@ -233,7 +301,11 @@ struct cpuidle_driver *cpuidle_get_driver(void)
233EXPORT_SYMBOL_GPL(cpuidle_get_driver); 301EXPORT_SYMBOL_GPL(cpuidle_get_driver);
234 302
235/** 303/**
236 * cpuidle_get_cpu_driver - return the driver tied with a cpu 304 * cpuidle_get_cpu_driver - return the driver registered for a CPU.
305 * @dev: a valid pointer to a struct cpuidle_device
306 *
307 * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered
308 * for the CPU associated with @dev.
237 */ 309 */
238struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) 310struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev)
239{ 311{
@@ -244,6 +316,14 @@ struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev)
244} 316}
245EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver); 317EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
246 318
319/**
320 * cpuidle_driver_ref - get a reference to the driver.
321 *
322 * Increment the reference counter of the cpuidle driver associated with
323 * the current CPU.
324 *
325 * Returns a pointer to the driver, or NULL if the current CPU has no driver.
326 */
247struct cpuidle_driver *cpuidle_driver_ref(void) 327struct cpuidle_driver *cpuidle_driver_ref(void)
248{ 328{
249 struct cpuidle_driver *drv; 329 struct cpuidle_driver *drv;
@@ -257,6 +337,12 @@ struct cpuidle_driver *cpuidle_driver_ref(void)
257 return drv; 337 return drv;
258} 338}
259 339
340/**
341 * cpuidle_driver_unref - puts down the refcount for the driver
342 *
343 * Decrement the reference counter of the cpuidle driver associated with
344 * the current CPU.
345 */
260void cpuidle_driver_unref(void) 346void cpuidle_driver_unref(void)
261{ 347{
262 struct cpuidle_driver *drv = cpuidle_get_driver(); 348 struct cpuidle_driver *drv = cpuidle_get_driver();