aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpuidle/driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpuidle/driver.c')
-rw-r--r--drivers/cpuidle/driver.c209
1 files changed, 180 insertions, 29 deletions
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index 87db3877fead..3af841fb397a 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -14,9 +14,10 @@
14 14
15#include "cpuidle.h" 15#include "cpuidle.h"
16 16
17static struct cpuidle_driver *cpuidle_curr_driver;
18DEFINE_SPINLOCK(cpuidle_driver_lock); 17DEFINE_SPINLOCK(cpuidle_driver_lock);
19int cpuidle_driver_refcount; 18
19static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
20static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
20 21
21static void set_power_states(struct cpuidle_driver *drv) 22static void set_power_states(struct cpuidle_driver *drv)
22{ 23{
@@ -40,11 +41,15 @@ static void set_power_states(struct cpuidle_driver *drv)
40 drv->states[i].power_usage = -1 - i; 41 drv->states[i].power_usage = -1 - i;
41} 42}
42 43
43/** 44static void __cpuidle_driver_init(struct cpuidle_driver *drv)
44 * cpuidle_register_driver - registers a driver 45{
45 * @drv: the driver 46 drv->refcnt = 0;
46 */ 47
47int cpuidle_register_driver(struct cpuidle_driver *drv) 48 if (!drv->power_specified)
49 set_power_states(drv);
50}
51
52static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
48{ 53{
49 if (!drv || !drv->state_count) 54 if (!drv || !drv->state_count)
50 return -EINVAL; 55 return -EINVAL;
@@ -52,31 +57,145 @@ int cpuidle_register_driver(struct cpuidle_driver *drv)
52 if (cpuidle_disabled()) 57 if (cpuidle_disabled())
53 return -ENODEV; 58 return -ENODEV;
54 59
55 spin_lock(&cpuidle_driver_lock); 60 if (__cpuidle_get_cpu_driver(cpu))
56 if (cpuidle_curr_driver) {
57 spin_unlock(&cpuidle_driver_lock);
58 return -EBUSY; 61 return -EBUSY;
62
63 __cpuidle_driver_init(drv);
64
65 __cpuidle_set_cpu_driver(drv, cpu);
66
67 return 0;
68}
69
70static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu)
71{
72 if (drv != __cpuidle_get_cpu_driver(cpu))
73 return;
74
75 if (!WARN_ON(drv->refcnt > 0))
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;
59 } 109 }
60 110
61 if (!drv->power_specified) 111 if (ret)
62 set_power_states(drv); 112 for_each_present_cpu(i) {
113 if (i == cpu)
114 break;
115 __cpuidle_unregister_driver(drv, i);
116 }
63 117
64 cpuidle_curr_driver = drv;
65 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);
66 spin_unlock(&cpuidle_driver_lock); 128 spin_unlock(&cpuidle_driver_lock);
67 129
68 return 0; 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);
138}
139
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);
149 ret = __cpuidle_register_all_cpu_driver(drv);
150 spin_unlock(&cpuidle_driver_lock);
151
152 return ret;
69} 153}
70EXPORT_SYMBOL_GPL(cpuidle_register_driver); 154EXPORT_SYMBOL_GPL(cpuidle_register_driver);
71 155
72/** 156/**
73 * cpuidle_get_driver - return the current driver 157 * cpuidle_unregister_driver - unregisters a driver
158 * @drv: the driver
74 */ 159 */
75struct cpuidle_driver *cpuidle_get_driver(void) 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)
76{ 178{
77 return cpuidle_curr_driver; 179 return cpuidle_curr_driver;
78} 180}
79EXPORT_SYMBOL_GPL(cpuidle_get_driver); 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);
80 199
81/** 200/**
82 * cpuidle_unregister_driver - unregisters a driver 201 * cpuidle_unregister_driver - unregisters a driver
@@ -84,20 +203,50 @@ EXPORT_SYMBOL_GPL(cpuidle_get_driver);
84 */ 203 */
85void cpuidle_unregister_driver(struct cpuidle_driver *drv) 204void cpuidle_unregister_driver(struct cpuidle_driver *drv)
86{ 205{
87 if (drv != cpuidle_curr_driver) { 206 int cpu;
88 WARN(1, "invalid cpuidle_unregister_driver(%s)\n",
89 drv->name);
90 return;
91 }
92 207
208 cpu = get_cpu();
93 spin_lock(&cpuidle_driver_lock); 209 spin_lock(&cpuidle_driver_lock);
210 __cpuidle_unregister_driver(drv, cpu);
211 spin_unlock(&cpuidle_driver_lock);
212 put_cpu();
213}
214EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
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;
94 224
95 if (!WARN_ON(cpuidle_driver_refcount > 0)) 225 cpu = get_cpu();
96 cpuidle_curr_driver = NULL; 226 drv = __cpuidle_get_cpu_driver(cpu);
227 put_cpu();
97 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);
98 spin_unlock(&cpuidle_driver_lock); 245 spin_unlock(&cpuidle_driver_lock);
246
247 return drv;
99} 248}
100EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); 249EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
101 250
102struct cpuidle_driver *cpuidle_driver_ref(void) 251struct cpuidle_driver *cpuidle_driver_ref(void)
103{ 252{
@@ -105,8 +254,8 @@ struct cpuidle_driver *cpuidle_driver_ref(void)
105 254
106 spin_lock(&cpuidle_driver_lock); 255 spin_lock(&cpuidle_driver_lock);
107 256
108 drv = cpuidle_curr_driver; 257 drv = cpuidle_get_driver();
109 cpuidle_driver_refcount++; 258 drv->refcnt++;
110 259
111 spin_unlock(&cpuidle_driver_lock); 260 spin_unlock(&cpuidle_driver_lock);
112 return drv; 261 return drv;
@@ -114,10 +263,12 @@ struct cpuidle_driver *cpuidle_driver_ref(void)
114 263
115void cpuidle_driver_unref(void) 264void cpuidle_driver_unref(void)
116{ 265{
266 struct cpuidle_driver *drv = cpuidle_get_driver();
267
117 spin_lock(&cpuidle_driver_lock); 268 spin_lock(&cpuidle_driver_lock);
118 269
119 if (!WARN_ON(cpuidle_driver_refcount <= 0)) 270 if (drv && !WARN_ON(drv->refcnt <= 0))
120 cpuidle_driver_refcount--; 271 drv->refcnt--;
121 272
122 spin_unlock(&cpuidle_driver_lock); 273 spin_unlock(&cpuidle_driver_lock);
123} 274}