summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDaniel Lezcano <daniel.lezcano@linaro.org>2012-10-31 12:44:48 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2012-11-14 18:34:23 -0500
commitbf4d1b5ddb78f86078ac6ae0415802d5f0c68f92 (patch)
tree36ec1f061372f6a8cfe7b3326036f06aa3a5067c /drivers
parent13dd52f11a04e616900f565d6a1e5138e58d579f (diff)
cpuidle: support multiple drivers
With the tegra3 and the big.LITTLE [1] new architectures, several cpus with different characteristics (latencies and states) can co-exists on the system. The cpuidle framework has the limitation of handling only identical cpus. This patch removes this limitation by introducing the multiple driver support for cpuidle. This option is configurable at compile time and should be enabled for the architectures mentioned above. So there is no impact for the other platforms if the option is disabled. The option defaults to 'n'. Note the multiple drivers support is also compatible with the existing drivers, even if just one driver is needed, all the cpu will be tied to this driver using an extra small chunk of processor memory. The multiple driver support use a per-cpu driver pointer instead of a global variable and the accessor to this variable are done from a cpu context. In order to keep the compatibility with the existing drivers, the function 'cpuidle_register_driver' and 'cpuidle_unregister_driver' will register the specified driver for all the cpus. The semantic for the output of /sys/devices/system/cpu/cpuidle/current_driver remains the same except the driver name will be related to the current cpu. The /sys/devices/system/cpu/cpu[0-9]/cpuidle/driver/name files are added allowing to read the per cpu driver name. [1] http://lwn.net/Articles/481055/ Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/cpuidle/Kconfig9
-rw-r--r--drivers/cpuidle/cpuidle.c36
-rw-r--r--drivers/cpuidle/cpuidle.h4
-rw-r--r--drivers/cpuidle/driver.c166
-rw-r--r--drivers/cpuidle/sysfs.c174
5 files changed, 351 insertions, 38 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
12config 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
12config CPU_IDLE_GOV_LADDER 21config 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;
68int cpuidle_play_dead(void) 68int 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,
128int cpuidle_idle_call(void) 128int 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) {}
312int cpuidle_enable_device(struct cpuidle_device *dev) 314int 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
358fail_sysfs: 365fail_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 */
373void cpuidle_disable_device(struct cpuidle_device *dev) 380void 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);
398static int __cpuidle_register_device(struct cpuidle_device *dev) 408static 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:
421err_sysfs: 431err_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 */
461void cpuidle_unregister_device(struct cpuidle_device *dev) 471void 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
483EXPORT_SYMBOL_GPL(cpuidle_unregister_device); 493EXPORT_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
29extern int cpuidle_add_interface(struct device *dev); 29extern int cpuidle_add_interface(struct device *dev);
30extern void cpuidle_remove_interface(struct device *dev); 30extern void cpuidle_remove_interface(struct device *dev);
31extern int cpuidle_add_state_sysfs(struct cpuidle_device *device); 31extern int cpuidle_add_device_sysfs(struct cpuidle_device *device);
32extern void cpuidle_remove_state_sysfs(struct cpuidle_device *device); 32extern void cpuidle_remove_device_sysfs(struct cpuidle_device *device);
33extern int cpuidle_add_sysfs(struct cpuidle_device *dev); 33extern int cpuidle_add_sysfs(struct cpuidle_device *dev);
34extern void cpuidle_remove_sysfs(struct cpuidle_device *dev); 34extern 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
17static struct cpuidle_driver *cpuidle_curr_driver;
18DEFINE_SPINLOCK(cpuidle_driver_lock); 17DEFINE_SPINLOCK(cpuidle_driver_lock);
19 18
19static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
20static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
21
20static void set_power_states(struct cpuidle_driver *drv) 22static 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
50static void cpuidle_set_driver(struct cpuidle_driver *drv) 52static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
51{
52 cpuidle_curr_driver = drv;
53}
54
55static 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
73static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) 70static 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
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);
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)
96EXPORT_SYMBOL_GPL(cpuidle_register_driver); 154EXPORT_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 */
101struct 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)
102{ 178{
103 return cpuidle_curr_driver; 179 return cpuidle_curr_driver;
104} 180}
105EXPORT_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);
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 */
111void cpuidle_unregister_driver(struct cpuidle_driver *drv) 204void 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}
117EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); 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;
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);
118 250
119struct cpuidle_driver *cpuidle_driver_ref(void) 251struct 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 */
370int cpuidle_add_state_sysfs(struct cpuidle_device *device) 370static 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 */
407void cpuidle_remove_state_sysfs(struct cpuidle_device *device) 407static 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
423struct cpuidle_driver_kobj {
424 struct cpuidle_driver *drv;
425 struct completion kobj_unregister;
426 struct kobject kobj;
427};
428
429struct 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
435static 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
446static 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
452static 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
465static 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
478define_one_driver_ro(name, show_driver_name);
479
480static const struct sysfs_ops cpuidle_driver_sysfs_ops = {
481 .show = cpuidle_driver_show,
482 .store = cpuidle_driver_store,
483};
484
485static struct attribute *cpuidle_driver_default_attrs[] = {
486 &attr_driver_name.attr,
487 NULL
488};
489
490static 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 */
500static 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 */
530static 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
538static inline int cpuidle_add_driver_sysfs(struct cpuidle_device *dev)
539{
540 return 0;
541}
542
543static 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 */
553int 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 */
571void 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