diff options
-rw-r--r-- | drivers/cpuidle/Kconfig | 9 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 36 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle.h | 4 | ||||
-rw-r--r-- | drivers/cpuidle/driver.c | 166 | ||||
-rw-r--r-- | drivers/cpuidle/sysfs.c | 174 | ||||
-rw-r--r-- | include/linux/cpuidle.h | 7 |
6 files changed, 356 insertions, 40 deletions
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index a76b689e553b..234ae651b38f 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig | |||
@@ -9,6 +9,15 @@ config CPU_IDLE | |||
9 | 9 | ||
10 | If you're using an ACPI-enabled platform, you should say Y here. | 10 | If you're using an ACPI-enabled platform, you should say Y here. |
11 | 11 | ||
12 | config CPU_IDLE_MULTIPLE_DRIVERS | ||
13 | bool "Support multiple cpuidle drivers" | ||
14 | depends on CPU_IDLE | ||
15 | default n | ||
16 | help | ||
17 | Allows the cpuidle framework to use different drivers for each CPU. | ||
18 | This is useful if you have a system with different CPU latencies and | ||
19 | states. If unsure say N. | ||
20 | |||
12 | config CPU_IDLE_GOV_LADDER | 21 | config CPU_IDLE_GOV_LADDER |
13 | bool | 22 | bool |
14 | depends on CPU_IDLE | 23 | depends on CPU_IDLE |
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index ce4cac706dd1..711dd83fd3ba 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c | |||
@@ -68,7 +68,7 @@ static cpuidle_enter_t cpuidle_enter_ops; | |||
68 | int cpuidle_play_dead(void) | 68 | int cpuidle_play_dead(void) |
69 | { | 69 | { |
70 | struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); | 70 | struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); |
71 | struct cpuidle_driver *drv = cpuidle_get_driver(); | 71 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); |
72 | int i, dead_state = -1; | 72 | int i, dead_state = -1; |
73 | int power_usage = -1; | 73 | int power_usage = -1; |
74 | 74 | ||
@@ -128,7 +128,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, | |||
128 | int cpuidle_idle_call(void) | 128 | int cpuidle_idle_call(void) |
129 | { | 129 | { |
130 | struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); | 130 | struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); |
131 | struct cpuidle_driver *drv = cpuidle_get_driver(); | 131 | struct cpuidle_driver *drv; |
132 | int next_state, entered_state; | 132 | int next_state, entered_state; |
133 | 133 | ||
134 | if (off) | 134 | if (off) |
@@ -141,6 +141,8 @@ int cpuidle_idle_call(void) | |||
141 | if (!dev || !dev->enabled) | 141 | if (!dev || !dev->enabled) |
142 | return -EBUSY; | 142 | return -EBUSY; |
143 | 143 | ||
144 | drv = cpuidle_get_cpu_driver(dev); | ||
145 | |||
144 | /* ask the governor for the next state */ | 146 | /* ask the governor for the next state */ |
145 | next_state = cpuidle_curr_governor->select(drv, dev); | 147 | next_state = cpuidle_curr_governor->select(drv, dev); |
146 | if (need_resched()) { | 148 | if (need_resched()) { |
@@ -312,15 +314,19 @@ static void poll_idle_init(struct cpuidle_driver *drv) {} | |||
312 | int cpuidle_enable_device(struct cpuidle_device *dev) | 314 | int cpuidle_enable_device(struct cpuidle_device *dev) |
313 | { | 315 | { |
314 | int ret, i; | 316 | int ret, i; |
315 | struct cpuidle_driver *drv = cpuidle_get_driver(); | 317 | struct cpuidle_driver *drv; |
316 | 318 | ||
317 | if (!dev) | 319 | if (!dev) |
318 | return -EINVAL; | 320 | return -EINVAL; |
319 | 321 | ||
320 | if (dev->enabled) | 322 | if (dev->enabled) |
321 | return 0; | 323 | return 0; |
324 | |||
325 | drv = cpuidle_get_cpu_driver(dev); | ||
326 | |||
322 | if (!drv || !cpuidle_curr_governor) | 327 | if (!drv || !cpuidle_curr_governor) |
323 | return -EIO; | 328 | return -EIO; |
329 | |||
324 | if (!dev->state_count) | 330 | if (!dev->state_count) |
325 | dev->state_count = drv->state_count; | 331 | dev->state_count = drv->state_count; |
326 | 332 | ||
@@ -335,7 +341,8 @@ int cpuidle_enable_device(struct cpuidle_device *dev) | |||
335 | 341 | ||
336 | poll_idle_init(drv); | 342 | poll_idle_init(drv); |
337 | 343 | ||
338 | if ((ret = cpuidle_add_state_sysfs(dev))) | 344 | ret = cpuidle_add_device_sysfs(dev); |
345 | if (ret) | ||
339 | return ret; | 346 | return ret; |
340 | 347 | ||
341 | if (cpuidle_curr_governor->enable && | 348 | if (cpuidle_curr_governor->enable && |
@@ -356,7 +363,7 @@ int cpuidle_enable_device(struct cpuidle_device *dev) | |||
356 | return 0; | 363 | return 0; |
357 | 364 | ||
358 | fail_sysfs: | 365 | fail_sysfs: |
359 | cpuidle_remove_state_sysfs(dev); | 366 | cpuidle_remove_device_sysfs(dev); |
360 | 367 | ||
361 | return ret; | 368 | return ret; |
362 | } | 369 | } |
@@ -372,17 +379,20 @@ EXPORT_SYMBOL_GPL(cpuidle_enable_device); | |||
372 | */ | 379 | */ |
373 | void cpuidle_disable_device(struct cpuidle_device *dev) | 380 | void cpuidle_disable_device(struct cpuidle_device *dev) |
374 | { | 381 | { |
382 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); | ||
383 | |||
375 | if (!dev || !dev->enabled) | 384 | if (!dev || !dev->enabled) |
376 | return; | 385 | return; |
377 | if (!cpuidle_get_driver() || !cpuidle_curr_governor) | 386 | |
387 | if (!drv || !cpuidle_curr_governor) | ||
378 | return; | 388 | return; |
379 | 389 | ||
380 | dev->enabled = 0; | 390 | dev->enabled = 0; |
381 | 391 | ||
382 | if (cpuidle_curr_governor->disable) | 392 | if (cpuidle_curr_governor->disable) |
383 | cpuidle_curr_governor->disable(cpuidle_get_driver(), dev); | 393 | cpuidle_curr_governor->disable(drv, dev); |
384 | 394 | ||
385 | cpuidle_remove_state_sysfs(dev); | 395 | cpuidle_remove_device_sysfs(dev); |
386 | enabled_devices--; | 396 | enabled_devices--; |
387 | } | 397 | } |
388 | 398 | ||
@@ -398,9 +408,9 @@ EXPORT_SYMBOL_GPL(cpuidle_disable_device); | |||
398 | static int __cpuidle_register_device(struct cpuidle_device *dev) | 408 | static int __cpuidle_register_device(struct cpuidle_device *dev) |
399 | { | 409 | { |
400 | int ret; | 410 | int ret; |
401 | struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver(); | 411 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); |
402 | 412 | ||
403 | if (!try_module_get(cpuidle_driver->owner)) | 413 | if (!try_module_get(drv->owner)) |
404 | return -EINVAL; | 414 | return -EINVAL; |
405 | 415 | ||
406 | per_cpu(cpuidle_devices, dev->cpu) = dev; | 416 | per_cpu(cpuidle_devices, dev->cpu) = dev; |
@@ -421,7 +431,7 @@ err_coupled: | |||
421 | err_sysfs: | 431 | err_sysfs: |
422 | list_del(&dev->device_list); | 432 | list_del(&dev->device_list); |
423 | per_cpu(cpuidle_devices, dev->cpu) = NULL; | 433 | per_cpu(cpuidle_devices, dev->cpu) = NULL; |
424 | module_put(cpuidle_driver->owner); | 434 | module_put(drv->owner); |
425 | return ret; | 435 | return ret; |
426 | } | 436 | } |
427 | 437 | ||
@@ -460,7 +470,7 @@ EXPORT_SYMBOL_GPL(cpuidle_register_device); | |||
460 | */ | 470 | */ |
461 | void cpuidle_unregister_device(struct cpuidle_device *dev) | 471 | void cpuidle_unregister_device(struct cpuidle_device *dev) |
462 | { | 472 | { |
463 | struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver(); | 473 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); |
464 | 474 | ||
465 | if (dev->registered == 0) | 475 | if (dev->registered == 0) |
466 | return; | 476 | return; |
@@ -477,7 +487,7 @@ void cpuidle_unregister_device(struct cpuidle_device *dev) | |||
477 | 487 | ||
478 | cpuidle_resume_and_unlock(); | 488 | cpuidle_resume_and_unlock(); |
479 | 489 | ||
480 | module_put(cpuidle_driver->owner); | 490 | module_put(drv->owner); |
481 | } | 491 | } |
482 | 492 | ||
483 | EXPORT_SYMBOL_GPL(cpuidle_unregister_device); | 493 | EXPORT_SYMBOL_GPL(cpuidle_unregister_device); |
diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h index f6b0923c2253..ee97e9672ecf 100644 --- a/drivers/cpuidle/cpuidle.h +++ b/drivers/cpuidle/cpuidle.h | |||
@@ -28,8 +28,8 @@ struct device; | |||
28 | 28 | ||
29 | extern int cpuidle_add_interface(struct device *dev); | 29 | extern int cpuidle_add_interface(struct device *dev); |
30 | extern void cpuidle_remove_interface(struct device *dev); | 30 | extern void cpuidle_remove_interface(struct device *dev); |
31 | extern int cpuidle_add_state_sysfs(struct cpuidle_device *device); | 31 | extern int cpuidle_add_device_sysfs(struct cpuidle_device *device); |
32 | extern void cpuidle_remove_state_sysfs(struct cpuidle_device *device); | 32 | extern void cpuidle_remove_device_sysfs(struct cpuidle_device *device); |
33 | extern int cpuidle_add_sysfs(struct cpuidle_device *dev); | 33 | extern int cpuidle_add_sysfs(struct cpuidle_device *dev); |
34 | extern void cpuidle_remove_sysfs(struct cpuidle_device *dev); | 34 | extern void cpuidle_remove_sysfs(struct cpuidle_device *dev); |
35 | 35 | ||
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 8246662f594a..3af841fb397a 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c | |||
@@ -14,9 +14,11 @@ | |||
14 | 14 | ||
15 | #include "cpuidle.h" | 15 | #include "cpuidle.h" |
16 | 16 | ||
17 | static struct cpuidle_driver *cpuidle_curr_driver; | ||
18 | DEFINE_SPINLOCK(cpuidle_driver_lock); | 17 | DEFINE_SPINLOCK(cpuidle_driver_lock); |
19 | 18 | ||
19 | static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); | ||
20 | static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); | ||
21 | |||
20 | static void set_power_states(struct cpuidle_driver *drv) | 22 | static void set_power_states(struct cpuidle_driver *drv) |
21 | { | 23 | { |
22 | int i; | 24 | int i; |
@@ -47,12 +49,7 @@ static void __cpuidle_driver_init(struct cpuidle_driver *drv) | |||
47 | set_power_states(drv); | 49 | set_power_states(drv); |
48 | } | 50 | } |
49 | 51 | ||
50 | static void cpuidle_set_driver(struct cpuidle_driver *drv) | 52 | static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) |
51 | { | ||
52 | cpuidle_curr_driver = drv; | ||
53 | } | ||
54 | |||
55 | static int __cpuidle_register_driver(struct cpuidle_driver *drv) | ||
56 | { | 53 | { |
57 | if (!drv || !drv->state_count) | 54 | if (!drv || !drv->state_count) |
58 | return -EINVAL; | 55 | return -EINVAL; |
@@ -60,23 +57,84 @@ static int __cpuidle_register_driver(struct cpuidle_driver *drv) | |||
60 | if (cpuidle_disabled()) | 57 | if (cpuidle_disabled()) |
61 | return -ENODEV; | 58 | return -ENODEV; |
62 | 59 | ||
63 | if (cpuidle_get_driver()) | 60 | if (__cpuidle_get_cpu_driver(cpu)) |
64 | return -EBUSY; | 61 | return -EBUSY; |
65 | 62 | ||
66 | __cpuidle_driver_init(drv); | 63 | __cpuidle_driver_init(drv); |
67 | 64 | ||
68 | cpuidle_set_driver(drv); | 65 | __cpuidle_set_cpu_driver(drv, cpu); |
69 | 66 | ||
70 | return 0; | 67 | return 0; |
71 | } | 68 | } |
72 | 69 | ||
73 | static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) | 70 | static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu) |
74 | { | 71 | { |
75 | if (drv != cpuidle_get_driver()) | 72 | if (drv != __cpuidle_get_cpu_driver(cpu)) |
76 | return; | 73 | return; |
77 | 74 | ||
78 | if (!WARN_ON(drv->refcnt > 0)) | 75 | if (!WARN_ON(drv->refcnt > 0)) |
79 | cpuidle_set_driver(NULL); | 76 | __cpuidle_set_cpu_driver(NULL, cpu); |
77 | } | ||
78 | |||
79 | #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS | ||
80 | |||
81 | static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); | ||
82 | |||
83 | static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) | ||
84 | { | ||
85 | per_cpu(cpuidle_drivers, cpu) = drv; | ||
86 | } | ||
87 | |||
88 | static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) | ||
89 | { | ||
90 | return per_cpu(cpuidle_drivers, cpu); | ||
91 | } | ||
92 | |||
93 | static 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 | |||
100 | static 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 | |||
122 | int 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 | |||
133 | void 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); | ||
80 | } | 138 | } |
81 | 139 | ||
82 | /** | 140 | /** |
@@ -88,7 +146,7 @@ int cpuidle_register_driver(struct cpuidle_driver *drv) | |||
88 | int ret; | 146 | int ret; |
89 | 147 | ||
90 | spin_lock(&cpuidle_driver_lock); | 148 | spin_lock(&cpuidle_driver_lock); |
91 | ret = __cpuidle_register_driver(drv); | 149 | ret = __cpuidle_register_all_cpu_driver(drv); |
92 | spin_unlock(&cpuidle_driver_lock); | 150 | spin_unlock(&cpuidle_driver_lock); |
93 | 151 | ||
94 | return ret; | 152 | return ret; |
@@ -96,13 +154,48 @@ int cpuidle_register_driver(struct cpuidle_driver *drv) | |||
96 | EXPORT_SYMBOL_GPL(cpuidle_register_driver); | 154 | EXPORT_SYMBOL_GPL(cpuidle_register_driver); |
97 | 155 | ||
98 | /** | 156 | /** |
99 | * cpuidle_get_driver - return the current driver | 157 | * cpuidle_unregister_driver - unregisters a driver |
158 | * @drv: the driver | ||
100 | */ | 159 | */ |
101 | struct cpuidle_driver *cpuidle_get_driver(void) | 160 | void 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 | } | ||
166 | EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); | ||
167 | |||
168 | #else | ||
169 | |||
170 | static struct cpuidle_driver *cpuidle_curr_driver; | ||
171 | |||
172 | static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) | ||
173 | { | ||
174 | cpuidle_curr_driver = drv; | ||
175 | } | ||
176 | |||
177 | static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) | ||
102 | { | 178 | { |
103 | return cpuidle_curr_driver; | 179 | return cpuidle_curr_driver; |
104 | } | 180 | } |
105 | EXPORT_SYMBOL_GPL(cpuidle_get_driver); | 181 | |
182 | /** | ||
183 | * cpuidle_register_driver - registers a driver | ||
184 | * @drv: the driver | ||
185 | */ | ||
186 | int 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 | } | ||
198 | EXPORT_SYMBOL_GPL(cpuidle_register_driver); | ||
106 | 199 | ||
107 | /** | 200 | /** |
108 | * cpuidle_unregister_driver - unregisters a driver | 201 | * cpuidle_unregister_driver - unregisters a driver |
@@ -110,11 +203,50 @@ EXPORT_SYMBOL_GPL(cpuidle_get_driver); | |||
110 | */ | 203 | */ |
111 | void cpuidle_unregister_driver(struct cpuidle_driver *drv) | 204 | void cpuidle_unregister_driver(struct cpuidle_driver *drv) |
112 | { | 205 | { |
206 | int cpu; | ||
207 | |||
208 | cpu = get_cpu(); | ||
113 | spin_lock(&cpuidle_driver_lock); | 209 | spin_lock(&cpuidle_driver_lock); |
114 | __cpuidle_unregister_driver(drv); | 210 | __cpuidle_unregister_driver(drv, cpu); |
115 | spin_unlock(&cpuidle_driver_lock); | 211 | spin_unlock(&cpuidle_driver_lock); |
212 | put_cpu(); | ||
116 | } | 213 | } |
117 | EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); | 214 | EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); |
215 | #endif | ||
216 | |||
217 | /** | ||
218 | * cpuidle_get_driver - return the current driver | ||
219 | */ | ||
220 | struct 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 | } | ||
231 | EXPORT_SYMBOL_GPL(cpuidle_get_driver); | ||
232 | |||
233 | /** | ||
234 | * cpuidle_get_cpu_driver - return the driver tied with a cpu | ||
235 | */ | ||
236 | struct 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 | } | ||
249 | EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver); | ||
118 | 250 | ||
119 | struct cpuidle_driver *cpuidle_driver_ref(void) | 251 | struct cpuidle_driver *cpuidle_driver_ref(void) |
120 | { | 252 | { |
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 49b1f4bcc1b3..340942946106 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c | |||
@@ -364,17 +364,17 @@ static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i) | |||
364 | } | 364 | } |
365 | 365 | ||
366 | /** | 366 | /** |
367 | * cpuidle_add_driver_sysfs - adds driver-specific sysfs attributes | 367 | * cpuidle_add_state_sysfs - adds cpuidle states sysfs attributes |
368 | * @device: the target device | 368 | * @device: the target device |
369 | */ | 369 | */ |
370 | int cpuidle_add_state_sysfs(struct cpuidle_device *device) | 370 | static int cpuidle_add_state_sysfs(struct cpuidle_device *device) |
371 | { | 371 | { |
372 | int i, ret = -ENOMEM; | 372 | int i, ret = -ENOMEM; |
373 | struct cpuidle_state_kobj *kobj; | 373 | struct cpuidle_state_kobj *kobj; |
374 | struct cpuidle_driver *drv = cpuidle_get_driver(); | 374 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); |
375 | 375 | ||
376 | /* state statistics */ | 376 | /* state statistics */ |
377 | for (i = 0; i < device->state_count; i++) { | 377 | for (i = 0; i < drv->state_count; i++) { |
378 | kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); | 378 | kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); |
379 | if (!kobj) | 379 | if (!kobj) |
380 | goto error_state; | 380 | goto error_state; |
@@ -401,10 +401,10 @@ error_state: | |||
401 | } | 401 | } |
402 | 402 | ||
403 | /** | 403 | /** |
404 | * cpuidle_remove_driver_sysfs - removes driver-specific sysfs attributes | 404 | * cpuidle_remove_driver_sysfs - removes the cpuidle states sysfs attributes |
405 | * @device: the target device | 405 | * @device: the target device |
406 | */ | 406 | */ |
407 | void cpuidle_remove_state_sysfs(struct cpuidle_device *device) | 407 | static void cpuidle_remove_state_sysfs(struct cpuidle_device *device) |
408 | { | 408 | { |
409 | int i; | 409 | int i; |
410 | 410 | ||
@@ -412,6 +412,168 @@ void cpuidle_remove_state_sysfs(struct cpuidle_device *device) | |||
412 | cpuidle_free_state_kobj(device, i); | 412 | cpuidle_free_state_kobj(device, i); |
413 | } | 413 | } |
414 | 414 | ||
415 | #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS | ||
416 | #define kobj_to_driver_kobj(k) container_of(k, struct cpuidle_driver_kobj, kobj) | ||
417 | #define attr_to_driver_attr(a) container_of(a, struct cpuidle_driver_attr, attr) | ||
418 | |||
419 | #define define_one_driver_ro(_name, show) \ | ||
420 | static struct cpuidle_driver_attr attr_driver_##_name = \ | ||
421 | __ATTR(_name, 0644, show, NULL) | ||
422 | |||
423 | struct cpuidle_driver_kobj { | ||
424 | struct cpuidle_driver *drv; | ||
425 | struct completion kobj_unregister; | ||
426 | struct kobject kobj; | ||
427 | }; | ||
428 | |||
429 | struct cpuidle_driver_attr { | ||
430 | struct attribute attr; | ||
431 | ssize_t (*show)(struct cpuidle_driver *, char *); | ||
432 | ssize_t (*store)(struct cpuidle_driver *, const char *, size_t); | ||
433 | }; | ||
434 | |||
435 | static ssize_t show_driver_name(struct cpuidle_driver *drv, char *buf) | ||
436 | { | ||
437 | ssize_t ret; | ||
438 | |||
439 | spin_lock(&cpuidle_driver_lock); | ||
440 | ret = sprintf(buf, "%s\n", drv ? drv->name : "none"); | ||
441 | spin_unlock(&cpuidle_driver_lock); | ||
442 | |||
443 | return ret; | ||
444 | } | ||
445 | |||
446 | static void cpuidle_driver_sysfs_release(struct kobject *kobj) | ||
447 | { | ||
448 | struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj); | ||
449 | complete(&driver_kobj->kobj_unregister); | ||
450 | } | ||
451 | |||
452 | static ssize_t cpuidle_driver_show(struct kobject *kobj, struct attribute * attr, | ||
453 | char * buf) | ||
454 | { | ||
455 | int ret = -EIO; | ||
456 | struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj); | ||
457 | struct cpuidle_driver_attr *dattr = attr_to_driver_attr(attr); | ||
458 | |||
459 | if (dattr->show) | ||
460 | ret = dattr->show(driver_kobj->drv, buf); | ||
461 | |||
462 | return ret; | ||
463 | } | ||
464 | |||
465 | static ssize_t cpuidle_driver_store(struct kobject *kobj, struct attribute *attr, | ||
466 | const char *buf, size_t size) | ||
467 | { | ||
468 | int ret = -EIO; | ||
469 | struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj); | ||
470 | struct cpuidle_driver_attr *dattr = attr_to_driver_attr(attr); | ||
471 | |||
472 | if (dattr->store) | ||
473 | ret = dattr->store(driver_kobj->drv, buf, size); | ||
474 | |||
475 | return ret; | ||
476 | } | ||
477 | |||
478 | define_one_driver_ro(name, show_driver_name); | ||
479 | |||
480 | static const struct sysfs_ops cpuidle_driver_sysfs_ops = { | ||
481 | .show = cpuidle_driver_show, | ||
482 | .store = cpuidle_driver_store, | ||
483 | }; | ||
484 | |||
485 | static struct attribute *cpuidle_driver_default_attrs[] = { | ||
486 | &attr_driver_name.attr, | ||
487 | NULL | ||
488 | }; | ||
489 | |||
490 | static struct kobj_type ktype_driver_cpuidle = { | ||
491 | .sysfs_ops = &cpuidle_driver_sysfs_ops, | ||
492 | .default_attrs = cpuidle_driver_default_attrs, | ||
493 | .release = cpuidle_driver_sysfs_release, | ||
494 | }; | ||
495 | |||
496 | /** | ||
497 | * cpuidle_add_driver_sysfs - adds the driver name sysfs attribute | ||
498 | * @device: the target device | ||
499 | */ | ||
500 | static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) | ||
501 | { | ||
502 | struct cpuidle_driver_kobj *kdrv; | ||
503 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); | ||
504 | int ret; | ||
505 | |||
506 | kdrv = kzalloc(sizeof(*kdrv), GFP_KERNEL); | ||
507 | if (!kdrv) | ||
508 | return -ENOMEM; | ||
509 | |||
510 | kdrv->drv = drv; | ||
511 | init_completion(&kdrv->kobj_unregister); | ||
512 | |||
513 | ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle, | ||
514 | &dev->kobj, "driver"); | ||
515 | if (ret) { | ||
516 | kfree(kdrv); | ||
517 | return ret; | ||
518 | } | ||
519 | |||
520 | kobject_uevent(&kdrv->kobj, KOBJ_ADD); | ||
521 | dev->kobj_driver = kdrv; | ||
522 | |||
523 | return ret; | ||
524 | } | ||
525 | |||
526 | /** | ||
527 | * cpuidle_remove_driver_sysfs - removes the driver name sysfs attribute | ||
528 | * @device: the target device | ||
529 | */ | ||
530 | static void cpuidle_remove_driver_sysfs(struct cpuidle_device *dev) | ||
531 | { | ||
532 | struct cpuidle_driver_kobj *kdrv = dev->kobj_driver; | ||
533 | kobject_put(&kdrv->kobj); | ||
534 | wait_for_completion(&kdrv->kobj_unregister); | ||
535 | kfree(kdrv); | ||
536 | } | ||
537 | #else | ||
538 | static inline int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) | ||
539 | { | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | static inline void cpuidle_remove_driver_sysfs(struct cpuidle_device *dev) | ||
544 | { | ||
545 | ; | ||
546 | } | ||
547 | #endif | ||
548 | |||
549 | /** | ||
550 | * cpuidle_add_device_sysfs - adds device specific sysfs attributes | ||
551 | * @device: the target device | ||
552 | */ | ||
553 | int cpuidle_add_device_sysfs(struct cpuidle_device *device) | ||
554 | { | ||
555 | int ret; | ||
556 | |||
557 | ret = cpuidle_add_state_sysfs(device); | ||
558 | if (ret) | ||
559 | return ret; | ||
560 | |||
561 | ret = cpuidle_add_driver_sysfs(device); | ||
562 | if (ret) | ||
563 | cpuidle_remove_state_sysfs(device); | ||
564 | return ret; | ||
565 | } | ||
566 | |||
567 | /** | ||
568 | * cpuidle_remove_device_sysfs : removes device specific sysfs attributes | ||
569 | * @device : the target device | ||
570 | */ | ||
571 | void cpuidle_remove_device_sysfs(struct cpuidle_device *device) | ||
572 | { | ||
573 | cpuidle_remove_driver_sysfs(device); | ||
574 | cpuidle_remove_state_sysfs(device); | ||
575 | } | ||
576 | |||
415 | /** | 577 | /** |
416 | * cpuidle_add_sysfs - creates a sysfs instance for the target device | 578 | * cpuidle_add_sysfs - creates a sysfs instance for the target device |
417 | * @dev: the target device | 579 | * @dev: the target device |
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index d08e1afa4919..3711b34dc4f9 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h | |||
@@ -91,7 +91,7 @@ struct cpuidle_device { | |||
91 | int state_count; | 91 | int state_count; |
92 | struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX]; | 92 | struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX]; |
93 | struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX]; | 93 | struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX]; |
94 | 94 | struct cpuidle_driver_kobj *kobj_driver; | |
95 | struct list_head device_list; | 95 | struct list_head device_list; |
96 | struct kobject kobj; | 96 | struct kobject kobj; |
97 | struct completion kobj_unregister; | 97 | struct completion kobj_unregister; |
@@ -157,6 +157,10 @@ extern int cpuidle_wrap_enter(struct cpuidle_device *dev, | |||
157 | struct cpuidle_driver *drv, int index)); | 157 | struct cpuidle_driver *drv, int index)); |
158 | extern int cpuidle_play_dead(void); | 158 | extern int cpuidle_play_dead(void); |
159 | 159 | ||
160 | extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); | ||
161 | extern int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu); | ||
162 | extern void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu); | ||
163 | |||
160 | #else | 164 | #else |
161 | static inline void disable_cpuidle(void) { } | 165 | static inline void disable_cpuidle(void) { } |
162 | static inline int cpuidle_idle_call(void) { return -ENODEV; } | 166 | static inline int cpuidle_idle_call(void) { return -ENODEV; } |
@@ -183,7 +187,6 @@ static inline int cpuidle_wrap_enter(struct cpuidle_device *dev, | |||
183 | struct cpuidle_driver *drv, int index)) | 187 | struct cpuidle_driver *drv, int index)) |
184 | { return -ENODEV; } | 188 | { return -ENODEV; } |
185 | static inline int cpuidle_play_dead(void) {return -ENODEV; } | 189 | static inline int cpuidle_play_dead(void) {return -ENODEV; } |
186 | |||
187 | #endif | 190 | #endif |
188 | 191 | ||
189 | #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED | 192 | #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED |