cpuidle: support multiple drivers
[linux-block.git] / drivers / cpuidle / driver.c
CommitLineData
4f86d3a8
LB
1/*
2 * driver.c - driver support
3 *
4 * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
5 * Shaohua Li <shaohua.li@intel.com>
6 * Adam Belay <abelay@novell.com>
7 *
8 * This code is licenced under the GPL.
9 */
10
11#include <linux/mutex.h>
12#include <linux/module.h>
13#include <linux/cpuidle.h>
14
15#include "cpuidle.h"
16
4f86d3a8
LB
17DEFINE_SPINLOCK(cpuidle_driver_lock);
18
bf4d1b5d
DL
19static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
20static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
21
ed953472 22static void set_power_states(struct cpuidle_driver *drv)
46bcfad7
DD
23{
24 int i;
ed953472 25
46bcfad7
DD
26 /*
27 * cpuidle driver should set the drv->power_specified bit
28 * before registering if the driver provides
29 * power_usage numbers.
30 *
31 * If power_specified is not set,
32 * we fill in power_usage with decreasing values as the
33 * cpuidle code has an implicit assumption that state Cn
34 * uses less power than C(n-1).
35 *
36 * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned
37 * an power value of -1. So we use -2, -3, etc, for other
38 * c-states.
39 */
ed953472
DL
40 for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++)
41 drv->states[i].power_usage = -1 - i;
46bcfad7
DD
42}
43
13dd52f1
DL
44static void __cpuidle_driver_init(struct cpuidle_driver *drv)
45{
46 drv->refcnt = 0;
47
48 if (!drv->power_specified)
49 set_power_states(drv);
50}
51
bf4d1b5d 52static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
4f86d3a8 53{
fc850f39 54 if (!drv || !drv->state_count)
4f86d3a8
LB
55 return -EINVAL;
56
62027aea
LB
57 if (cpuidle_disabled())
58 return -ENODEV;
59
bf4d1b5d 60 if (__cpuidle_get_cpu_driver(cpu))
4f86d3a8 61 return -EBUSY;
ed953472 62
13dd52f1 63 __cpuidle_driver_init(drv);
ed953472 64
bf4d1b5d 65 __cpuidle_set_cpu_driver(drv, cpu);
42f67f2a 66
13dd52f1
DL
67 return 0;
68}
69
bf4d1b5d 70static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu)
13dd52f1 71{
bf4d1b5d 72 if (drv != __cpuidle_get_cpu_driver(cpu))
13dd52f1
DL
73 return;
74
75 if (!WARN_ON(drv->refcnt > 0))
bf4d1b5d
DL
76 __cpuidle_set_cpu_driver(NULL, cpu);
77}
78
79#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
80
81static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers);
82
83static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
84{
85 per_cpu(cpuidle_drivers, cpu) = drv;
86}
87
88static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
89{
90 return per_cpu(cpuidle_drivers, cpu);
91}
92
93static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv)
94{
95 int cpu;
96 for_each_present_cpu(cpu)
97 __cpuidle_unregister_driver(drv, cpu);
98}
99
100static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv)
101{
102 int ret = 0;
103 int i, cpu;
104
105 for_each_present_cpu(cpu) {
106 ret = __cpuidle_register_driver(drv, cpu);
107 if (ret)
108 break;
109 }
110
111 if (ret)
112 for_each_present_cpu(i) {
113 if (i == cpu)
114 break;
115 __cpuidle_unregister_driver(drv, i);
116 }
117
118
119 return ret;
120}
121
122int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu)
123{
124 int ret;
125
126 spin_lock(&cpuidle_driver_lock);
127 ret = __cpuidle_register_driver(drv, cpu);
128 spin_unlock(&cpuidle_driver_lock);
129
130 return ret;
131}
132
133void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu)
134{
135 spin_lock(&cpuidle_driver_lock);
136 __cpuidle_unregister_driver(drv, cpu);
137 spin_unlock(&cpuidle_driver_lock);
13dd52f1 138}
ed953472 139
13dd52f1
DL
140/**
141 * cpuidle_register_driver - registers a driver
142 * @drv: the driver
143 */
144int cpuidle_register_driver(struct cpuidle_driver *drv)
145{
146 int ret;
147
148 spin_lock(&cpuidle_driver_lock);
bf4d1b5d 149 ret = __cpuidle_register_all_cpu_driver(drv);
4f86d3a8
LB
150 spin_unlock(&cpuidle_driver_lock);
151
13dd52f1 152 return ret;
4f86d3a8 153}
4f86d3a8
LB
154EXPORT_SYMBOL_GPL(cpuidle_register_driver);
155
752138df 156/**
bf4d1b5d
DL
157 * cpuidle_unregister_driver - unregisters a driver
158 * @drv: the driver
752138df 159 */
bf4d1b5d
DL
160void cpuidle_unregister_driver(struct cpuidle_driver *drv)
161{
162 spin_lock(&cpuidle_driver_lock);
163 __cpuidle_unregister_all_cpu_driver(drv);
164 spin_unlock(&cpuidle_driver_lock);
165}
166EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
167
168#else
169
170static struct cpuidle_driver *cpuidle_curr_driver;
171
172static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
173{
174 cpuidle_curr_driver = drv;
175}
176
177static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
752138df
LB
178{
179 return cpuidle_curr_driver;
180}
bf4d1b5d
DL
181
182/**
183 * cpuidle_register_driver - registers a driver
184 * @drv: the driver
185 */
186int cpuidle_register_driver(struct cpuidle_driver *drv)
187{
188 int ret, cpu;
189
190 cpu = get_cpu();
191 spin_lock(&cpuidle_driver_lock);
192 ret = __cpuidle_register_driver(drv, cpu);
193 spin_unlock(&cpuidle_driver_lock);
194 put_cpu();
195
196 return ret;
197}
198EXPORT_SYMBOL_GPL(cpuidle_register_driver);
752138df 199
4f86d3a8
LB
200/**
201 * cpuidle_unregister_driver - unregisters a driver
202 * @drv: the driver
203 */
204void cpuidle_unregister_driver(struct cpuidle_driver *drv)
205{
bf4d1b5d
DL
206 int cpu;
207
208 cpu = get_cpu();
4f86d3a8 209 spin_lock(&cpuidle_driver_lock);
bf4d1b5d 210 __cpuidle_unregister_driver(drv, cpu);
4f86d3a8 211 spin_unlock(&cpuidle_driver_lock);
bf4d1b5d 212 put_cpu();
4f86d3a8 213}
4f86d3a8 214EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
bf4d1b5d
DL
215#endif
216
217/**
218 * cpuidle_get_driver - return the current driver
219 */
220struct cpuidle_driver *cpuidle_get_driver(void)
221{
222 struct cpuidle_driver *drv;
223 int cpu;
224
225 cpu = get_cpu();
226 drv = __cpuidle_get_cpu_driver(cpu);
227 put_cpu();
228
229 return drv;
230}
231EXPORT_SYMBOL_GPL(cpuidle_get_driver);
232
233/**
234 * cpuidle_get_cpu_driver - return the driver tied with a cpu
235 */
236struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev)
237{
238 struct cpuidle_driver *drv;
239
240 if (!dev)
241 return NULL;
242
243 spin_lock(&cpuidle_driver_lock);
244 drv = __cpuidle_get_cpu_driver(dev->cpu);
245 spin_unlock(&cpuidle_driver_lock);
246
247 return drv;
248}
249EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
6e797a07
RW
250
251struct cpuidle_driver *cpuidle_driver_ref(void)
252{
253 struct cpuidle_driver *drv;
254
255 spin_lock(&cpuidle_driver_lock);
256
13dd52f1 257 drv = cpuidle_get_driver();
42f67f2a 258 drv->refcnt++;
6e797a07
RW
259
260 spin_unlock(&cpuidle_driver_lock);
261 return drv;
262}
263
264void cpuidle_driver_unref(void)
265{
13dd52f1 266 struct cpuidle_driver *drv = cpuidle_get_driver();
42f67f2a 267
6e797a07
RW
268 spin_lock(&cpuidle_driver_lock);
269
42f67f2a
DL
270 if (drv && !WARN_ON(drv->refcnt <= 0))
271 drv->refcnt--;
6e797a07
RW
272
273 spin_unlock(&cpuidle_driver_lock);
274}