diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-08-14 16:18:04 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-08-14 16:18:04 -0400 |
commit | ee42f75dba3c66e559a13ac86ad1889d2a396378 (patch) | |
tree | c2e234d229ff467999cfb366f1edef80ea243433 /drivers/cpuidle | |
parent | d4e4ab86bcba5a72779c43dc1459f71fea3d89c8 (diff) | |
parent | 9aadfa8fd9f8d311ba3014bf76e4d8aeee515533 (diff) |
Merge back earlier 'pm-cpuidle' material.
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r-- | drivers/cpuidle/Kconfig | 20 | ||||
-rw-r--r-- | drivers/cpuidle/Kconfig.arm | 29 | ||||
-rw-r--r-- | drivers/cpuidle/Makefile | 9 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle-ux500.c | 131 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 94 | ||||
-rw-r--r-- | drivers/cpuidle/governors/ladder.c | 12 | ||||
-rw-r--r-- | drivers/cpuidle/governors/menu.c | 12 | ||||
-rw-r--r-- | drivers/cpuidle/sysfs.c | 101 |
8 files changed, 298 insertions, 110 deletions
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index 0e2cd5cab4d0..b3fb81d7cf04 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig | |||
@@ -1,5 +1,6 @@ | |||
1 | menu "CPU Idle" | ||
1 | 2 | ||
2 | menuconfig CPU_IDLE | 3 | config CPU_IDLE |
3 | bool "CPU idle PM support" | 4 | bool "CPU idle PM support" |
4 | default y if ACPI || PPC_PSERIES | 5 | default y if ACPI || PPC_PSERIES |
5 | select CPU_IDLE_GOV_LADDER if (!NO_HZ && !NO_HZ_IDLE) | 6 | select CPU_IDLE_GOV_LADDER if (!NO_HZ && !NO_HZ_IDLE) |
@@ -29,20 +30,13 @@ config CPU_IDLE_GOV_MENU | |||
29 | bool "Menu governor (for tickless system)" | 30 | bool "Menu governor (for tickless system)" |
30 | default y | 31 | default y |
31 | 32 | ||
32 | config CPU_IDLE_CALXEDA | 33 | menu "ARM CPU Idle Drivers" |
33 | bool "CPU Idle Driver for Calxeda processors" | 34 | depends on ARM |
34 | depends on ARCH_HIGHBANK | 35 | source "drivers/cpuidle/Kconfig.arm" |
35 | select ARM_CPU_SUSPEND | 36 | endmenu |
36 | help | ||
37 | Select this to enable cpuidle on Calxeda processors. | ||
38 | |||
39 | config CPU_IDLE_ZYNQ | ||
40 | bool "CPU Idle Driver for Xilinx Zynq processors" | ||
41 | depends on ARCH_ZYNQ | ||
42 | help | ||
43 | Select this to enable cpuidle on Xilinx Zynq processors. | ||
44 | 37 | ||
45 | endif | 38 | endif |
46 | 39 | ||
47 | config ARCH_NEEDS_CPU_IDLE_COUPLED | 40 | config ARCH_NEEDS_CPU_IDLE_COUPLED |
48 | def_bool n | 41 | def_bool n |
42 | endmenu | ||
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm new file mode 100644 index 000000000000..b3302193c15a --- /dev/null +++ b/drivers/cpuidle/Kconfig.arm | |||
@@ -0,0 +1,29 @@ | |||
1 | # | ||
2 | # ARM CPU Idle drivers | ||
3 | # | ||
4 | |||
5 | config ARM_HIGHBANK_CPUIDLE | ||
6 | bool "CPU Idle Driver for Calxeda processors" | ||
7 | depends on ARCH_HIGHBANK | ||
8 | select ARM_CPU_SUSPEND | ||
9 | help | ||
10 | Select this to enable cpuidle on Calxeda processors. | ||
11 | |||
12 | config ARM_KIRKWOOD_CPUIDLE | ||
13 | bool "CPU Idle Driver for Marvell Kirkwood SoCs" | ||
14 | depends on ARCH_KIRKWOOD | ||
15 | help | ||
16 | This adds the CPU Idle driver for Marvell Kirkwood SoCs. | ||
17 | |||
18 | config ARM_ZYNQ_CPUIDLE | ||
19 | bool "CPU Idle Driver for Xilinx Zynq processors" | ||
20 | depends on ARCH_ZYNQ | ||
21 | help | ||
22 | Select this to enable cpuidle on Xilinx Zynq processors. | ||
23 | |||
24 | config ARM_U8500_CPUIDLE | ||
25 | bool "Cpu Idle Driver for the ST-E u8500 processors" | ||
26 | depends on ARCH_U8500 | ||
27 | help | ||
28 | Select this to enable cpuidle for ST-E u8500 processors | ||
29 | |||
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 8767a7b3eb91..0b9d200c7e45 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile | |||
@@ -5,6 +5,9 @@ | |||
5 | obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ | 5 | obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ |
6 | obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o | 6 | obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o |
7 | 7 | ||
8 | obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o | 8 | ################################################################################## |
9 | obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o | 9 | # ARM SoC drivers |
10 | obj-$(CONFIG_CPU_IDLE_ZYNQ) += cpuidle-zynq.o | 10 | obj-$(CONFIG_ARM_HIGHBANK_CPUIDLE) += cpuidle-calxeda.o |
11 | obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE) += cpuidle-kirkwood.o | ||
12 | obj-$(CONFIG_ARM_ZYNQ_CPUIDLE) += cpuidle-zynq.o | ||
13 | obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o | ||
diff --git a/drivers/cpuidle/cpuidle-ux500.c b/drivers/cpuidle/cpuidle-ux500.c new file mode 100644 index 000000000000..e0564652af35 --- /dev/null +++ b/drivers/cpuidle/cpuidle-ux500.c | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Linaro : Daniel Lezcano <daniel.lezcano@linaro.org> (IBM) | ||
3 | * | ||
4 | * Based on the work of Rickard Andersson <rickard.andersson@stericsson.com> | ||
5 | * and Jonas Aaberg <jonas.aberg@stericsson.com>. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/cpuidle.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include <linux/atomic.h> | ||
16 | #include <linux/smp.h> | ||
17 | #include <linux/mfd/dbx500-prcmu.h> | ||
18 | #include <linux/platform_data/arm-ux500-pm.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | |||
21 | #include <asm/cpuidle.h> | ||
22 | #include <asm/proc-fns.h> | ||
23 | |||
24 | static atomic_t master = ATOMIC_INIT(0); | ||
25 | static DEFINE_SPINLOCK(master_lock); | ||
26 | |||
27 | static inline int ux500_enter_idle(struct cpuidle_device *dev, | ||
28 | struct cpuidle_driver *drv, int index) | ||
29 | { | ||
30 | int this_cpu = smp_processor_id(); | ||
31 | bool recouple = false; | ||
32 | |||
33 | if (atomic_inc_return(&master) == num_online_cpus()) { | ||
34 | |||
35 | /* With this lock, we prevent the other cpu to exit and enter | ||
36 | * this function again and become the master */ | ||
37 | if (!spin_trylock(&master_lock)) | ||
38 | goto wfi; | ||
39 | |||
40 | /* decouple the gic from the A9 cores */ | ||
41 | if (prcmu_gic_decouple()) { | ||
42 | spin_unlock(&master_lock); | ||
43 | goto out; | ||
44 | } | ||
45 | |||
46 | /* If an error occur, we will have to recouple the gic | ||
47 | * manually */ | ||
48 | recouple = true; | ||
49 | |||
50 | /* At this state, as the gic is decoupled, if the other | ||
51 | * cpu is in WFI, we have the guarantee it won't be wake | ||
52 | * up, so we can safely go to retention */ | ||
53 | if (!prcmu_is_cpu_in_wfi(this_cpu ? 0 : 1)) | ||
54 | goto out; | ||
55 | |||
56 | /* The prcmu will be in charge of watching the interrupts | ||
57 | * and wake up the cpus */ | ||
58 | if (prcmu_copy_gic_settings()) | ||
59 | goto out; | ||
60 | |||
61 | /* Check in the meantime an interrupt did | ||
62 | * not occur on the gic ... */ | ||
63 | if (prcmu_gic_pending_irq()) | ||
64 | goto out; | ||
65 | |||
66 | /* ... and the prcmu */ | ||
67 | if (prcmu_pending_irq()) | ||
68 | goto out; | ||
69 | |||
70 | /* Go to the retention state, the prcmu will wait for the | ||
71 | * cpu to go WFI and this is what happens after exiting this | ||
72 | * 'master' critical section */ | ||
73 | if (prcmu_set_power_state(PRCMU_AP_IDLE, true, true)) | ||
74 | goto out; | ||
75 | |||
76 | /* When we switch to retention, the prcmu is in charge | ||
77 | * of recoupling the gic automatically */ | ||
78 | recouple = false; | ||
79 | |||
80 | spin_unlock(&master_lock); | ||
81 | } | ||
82 | wfi: | ||
83 | cpu_do_idle(); | ||
84 | out: | ||
85 | atomic_dec(&master); | ||
86 | |||
87 | if (recouple) { | ||
88 | prcmu_gic_recouple(); | ||
89 | spin_unlock(&master_lock); | ||
90 | } | ||
91 | |||
92 | return index; | ||
93 | } | ||
94 | |||
95 | static struct cpuidle_driver ux500_idle_driver = { | ||
96 | .name = "ux500_idle", | ||
97 | .owner = THIS_MODULE, | ||
98 | .states = { | ||
99 | ARM_CPUIDLE_WFI_STATE, | ||
100 | { | ||
101 | .enter = ux500_enter_idle, | ||
102 | .exit_latency = 70, | ||
103 | .target_residency = 260, | ||
104 | .flags = CPUIDLE_FLAG_TIME_VALID | | ||
105 | CPUIDLE_FLAG_TIMER_STOP, | ||
106 | .name = "ApIdle", | ||
107 | .desc = "ARM Retention", | ||
108 | }, | ||
109 | }, | ||
110 | .safe_state_index = 0, | ||
111 | .state_count = 2, | ||
112 | }; | ||
113 | |||
114 | static int __init dbx500_cpuidle_probe(struct platform_device *pdev) | ||
115 | { | ||
116 | /* Configure wake up reasons */ | ||
117 | prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) | | ||
118 | PRCMU_WAKEUP(ABB)); | ||
119 | |||
120 | return cpuidle_register(&ux500_idle_driver, NULL); | ||
121 | } | ||
122 | |||
123 | static struct platform_driver dbx500_cpuidle_plat_driver = { | ||
124 | .driver = { | ||
125 | .name = "cpuidle-dbx500", | ||
126 | .owner = THIS_MODULE, | ||
127 | }, | ||
128 | .probe = dbx500_cpuidle_probe, | ||
129 | }; | ||
130 | |||
131 | module_platform_driver(dbx500_cpuidle_plat_driver); | ||
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index fdc432f18022..d75040ddd2b3 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c | |||
@@ -42,8 +42,6 @@ void disable_cpuidle(void) | |||
42 | off = 1; | 42 | off = 1; |
43 | } | 43 | } |
44 | 44 | ||
45 | static int __cpuidle_register_device(struct cpuidle_device *dev); | ||
46 | |||
47 | /** | 45 | /** |
48 | * cpuidle_play_dead - cpu off-lining | 46 | * cpuidle_play_dead - cpu off-lining |
49 | * | 47 | * |
@@ -278,7 +276,7 @@ static void poll_idle_init(struct cpuidle_driver *drv) {} | |||
278 | */ | 276 | */ |
279 | int cpuidle_enable_device(struct cpuidle_device *dev) | 277 | int cpuidle_enable_device(struct cpuidle_device *dev) |
280 | { | 278 | { |
281 | int ret, i; | 279 | int ret; |
282 | struct cpuidle_driver *drv; | 280 | struct cpuidle_driver *drv; |
283 | 281 | ||
284 | if (!dev) | 282 | if (!dev) |
@@ -292,15 +290,12 @@ int cpuidle_enable_device(struct cpuidle_device *dev) | |||
292 | if (!drv || !cpuidle_curr_governor) | 290 | if (!drv || !cpuidle_curr_governor) |
293 | return -EIO; | 291 | return -EIO; |
294 | 292 | ||
293 | if (!dev->registered) | ||
294 | return -EINVAL; | ||
295 | |||
295 | if (!dev->state_count) | 296 | if (!dev->state_count) |
296 | dev->state_count = drv->state_count; | 297 | dev->state_count = drv->state_count; |
297 | 298 | ||
298 | if (dev->registered == 0) { | ||
299 | ret = __cpuidle_register_device(dev); | ||
300 | if (ret) | ||
301 | return ret; | ||
302 | } | ||
303 | |||
304 | poll_idle_init(drv); | 299 | poll_idle_init(drv); |
305 | 300 | ||
306 | ret = cpuidle_add_device_sysfs(dev); | 301 | ret = cpuidle_add_device_sysfs(dev); |
@@ -311,12 +306,6 @@ int cpuidle_enable_device(struct cpuidle_device *dev) | |||
311 | (ret = cpuidle_curr_governor->enable(drv, dev))) | 306 | (ret = cpuidle_curr_governor->enable(drv, dev))) |
312 | goto fail_sysfs; | 307 | goto fail_sysfs; |
313 | 308 | ||
314 | for (i = 0; i < dev->state_count; i++) { | ||
315 | dev->states_usage[i].usage = 0; | ||
316 | dev->states_usage[i].time = 0; | ||
317 | } | ||
318 | dev->last_residency = 0; | ||
319 | |||
320 | smp_wmb(); | 309 | smp_wmb(); |
321 | 310 | ||
322 | dev->enabled = 1; | 311 | dev->enabled = 1; |
@@ -360,6 +349,23 @@ void cpuidle_disable_device(struct cpuidle_device *dev) | |||
360 | 349 | ||
361 | EXPORT_SYMBOL_GPL(cpuidle_disable_device); | 350 | EXPORT_SYMBOL_GPL(cpuidle_disable_device); |
362 | 351 | ||
352 | static void __cpuidle_unregister_device(struct cpuidle_device *dev) | ||
353 | { | ||
354 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); | ||
355 | |||
356 | list_del(&dev->device_list); | ||
357 | per_cpu(cpuidle_devices, dev->cpu) = NULL; | ||
358 | module_put(drv->owner); | ||
359 | } | ||
360 | |||
361 | static int __cpuidle_device_init(struct cpuidle_device *dev) | ||
362 | { | ||
363 | memset(dev->states_usage, 0, sizeof(dev->states_usage)); | ||
364 | dev->last_residency = 0; | ||
365 | |||
366 | return 0; | ||
367 | } | ||
368 | |||
363 | /** | 369 | /** |
364 | * __cpuidle_register_device - internal register function called before register | 370 | * __cpuidle_register_device - internal register function called before register |
365 | * and enable routines | 371 | * and enable routines |
@@ -377,24 +383,15 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) | |||
377 | 383 | ||
378 | per_cpu(cpuidle_devices, dev->cpu) = dev; | 384 | per_cpu(cpuidle_devices, dev->cpu) = dev; |
379 | list_add(&dev->device_list, &cpuidle_detected_devices); | 385 | list_add(&dev->device_list, &cpuidle_detected_devices); |
380 | ret = cpuidle_add_sysfs(dev); | ||
381 | if (ret) | ||
382 | goto err_sysfs; | ||
383 | 386 | ||
384 | ret = cpuidle_coupled_register_device(dev); | 387 | ret = cpuidle_coupled_register_device(dev); |
385 | if (ret) | 388 | if (ret) { |
386 | goto err_coupled; | 389 | __cpuidle_unregister_device(dev); |
390 | return ret; | ||
391 | } | ||
387 | 392 | ||
388 | dev->registered = 1; | 393 | dev->registered = 1; |
389 | return 0; | 394 | return 0; |
390 | |||
391 | err_coupled: | ||
392 | cpuidle_remove_sysfs(dev); | ||
393 | err_sysfs: | ||
394 | list_del(&dev->device_list); | ||
395 | per_cpu(cpuidle_devices, dev->cpu) = NULL; | ||
396 | module_put(drv->owner); | ||
397 | return ret; | ||
398 | } | 395 | } |
399 | 396 | ||
400 | /** | 397 | /** |
@@ -403,25 +400,44 @@ err_sysfs: | |||
403 | */ | 400 | */ |
404 | int cpuidle_register_device(struct cpuidle_device *dev) | 401 | int cpuidle_register_device(struct cpuidle_device *dev) |
405 | { | 402 | { |
406 | int ret; | 403 | int ret = -EBUSY; |
407 | 404 | ||
408 | if (!dev) | 405 | if (!dev) |
409 | return -EINVAL; | 406 | return -EINVAL; |
410 | 407 | ||
411 | mutex_lock(&cpuidle_lock); | 408 | mutex_lock(&cpuidle_lock); |
412 | 409 | ||
413 | if ((ret = __cpuidle_register_device(dev))) { | 410 | if (dev->registered) |
414 | mutex_unlock(&cpuidle_lock); | 411 | goto out_unlock; |
415 | return ret; | 412 | |
416 | } | 413 | ret = __cpuidle_device_init(dev); |
414 | if (ret) | ||
415 | goto out_unlock; | ||
416 | |||
417 | ret = __cpuidle_register_device(dev); | ||
418 | if (ret) | ||
419 | goto out_unlock; | ||
420 | |||
421 | ret = cpuidle_add_sysfs(dev); | ||
422 | if (ret) | ||
423 | goto out_unregister; | ||
424 | |||
425 | ret = cpuidle_enable_device(dev); | ||
426 | if (ret) | ||
427 | goto out_sysfs; | ||
417 | 428 | ||
418 | cpuidle_enable_device(dev); | ||
419 | cpuidle_install_idle_handler(); | 429 | cpuidle_install_idle_handler(); |
420 | 430 | ||
431 | out_unlock: | ||
421 | mutex_unlock(&cpuidle_lock); | 432 | mutex_unlock(&cpuidle_lock); |
422 | 433 | ||
423 | return 0; | 434 | return ret; |
424 | 435 | ||
436 | out_sysfs: | ||
437 | cpuidle_remove_sysfs(dev); | ||
438 | out_unregister: | ||
439 | __cpuidle_unregister_device(dev); | ||
440 | goto out_unlock; | ||
425 | } | 441 | } |
426 | 442 | ||
427 | EXPORT_SYMBOL_GPL(cpuidle_register_device); | 443 | EXPORT_SYMBOL_GPL(cpuidle_register_device); |
@@ -432,8 +448,6 @@ EXPORT_SYMBOL_GPL(cpuidle_register_device); | |||
432 | */ | 448 | */ |
433 | void cpuidle_unregister_device(struct cpuidle_device *dev) | 449 | void cpuidle_unregister_device(struct cpuidle_device *dev) |
434 | { | 450 | { |
435 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); | ||
436 | |||
437 | if (dev->registered == 0) | 451 | if (dev->registered == 0) |
438 | return; | 452 | return; |
439 | 453 | ||
@@ -442,14 +456,12 @@ void cpuidle_unregister_device(struct cpuidle_device *dev) | |||
442 | cpuidle_disable_device(dev); | 456 | cpuidle_disable_device(dev); |
443 | 457 | ||
444 | cpuidle_remove_sysfs(dev); | 458 | cpuidle_remove_sysfs(dev); |
445 | list_del(&dev->device_list); | 459 | |
446 | per_cpu(cpuidle_devices, dev->cpu) = NULL; | 460 | __cpuidle_unregister_device(dev); |
447 | 461 | ||
448 | cpuidle_coupled_unregister_device(dev); | 462 | cpuidle_coupled_unregister_device(dev); |
449 | 463 | ||
450 | cpuidle_resume_and_unlock(); | 464 | cpuidle_resume_and_unlock(); |
451 | |||
452 | module_put(drv->owner); | ||
453 | } | 465 | } |
454 | 466 | ||
455 | EXPORT_SYMBOL_GPL(cpuidle_unregister_device); | 467 | EXPORT_SYMBOL_GPL(cpuidle_unregister_device); |
diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c index 9b784051ec12..9f08e8cce1af 100644 --- a/drivers/cpuidle/governors/ladder.c +++ b/drivers/cpuidle/governors/ladder.c | |||
@@ -192,14 +192,4 @@ static int __init init_ladder(void) | |||
192 | return cpuidle_register_governor(&ladder_governor); | 192 | return cpuidle_register_governor(&ladder_governor); |
193 | } | 193 | } |
194 | 194 | ||
195 | /** | 195 | postcore_initcall(init_ladder); |
196 | * exit_ladder - exits the governor | ||
197 | */ | ||
198 | static void __exit exit_ladder(void) | ||
199 | { | ||
200 | cpuidle_unregister_governor(&ladder_governor); | ||
201 | } | ||
202 | |||
203 | MODULE_LICENSE("GPL"); | ||
204 | module_init(init_ladder); | ||
205 | module_exit(exit_ladder); | ||
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index bc580b67a652..cbbb73b37a6d 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c | |||
@@ -442,14 +442,4 @@ static int __init init_menu(void) | |||
442 | return cpuidle_register_governor(&menu_governor); | 442 | return cpuidle_register_governor(&menu_governor); |
443 | } | 443 | } |
444 | 444 | ||
445 | /** | 445 | postcore_initcall(init_menu); |
446 | * exit_menu - exits the governor | ||
447 | */ | ||
448 | static void __exit exit_menu(void) | ||
449 | { | ||
450 | cpuidle_unregister_governor(&menu_governor); | ||
451 | } | ||
452 | |||
453 | MODULE_LICENSE("GPL"); | ||
454 | module_init(init_menu); | ||
455 | module_exit(exit_menu); | ||
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 428754af6236..8739cc05228c 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c | |||
@@ -11,8 +11,10 @@ | |||
11 | #include <linux/sysfs.h> | 11 | #include <linux/sysfs.h> |
12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
13 | #include <linux/cpu.h> | 13 | #include <linux/cpu.h> |
14 | #include <linux/completion.h> | ||
14 | #include <linux/capability.h> | 15 | #include <linux/capability.h> |
15 | #include <linux/device.h> | 16 | #include <linux/device.h> |
17 | #include <linux/kobject.h> | ||
16 | 18 | ||
17 | #include "cpuidle.h" | 19 | #include "cpuidle.h" |
18 | 20 | ||
@@ -33,7 +35,8 @@ static ssize_t show_available_governors(struct device *dev, | |||
33 | 35 | ||
34 | mutex_lock(&cpuidle_lock); | 36 | mutex_lock(&cpuidle_lock); |
35 | list_for_each_entry(tmp, &cpuidle_governors, governor_list) { | 37 | list_for_each_entry(tmp, &cpuidle_governors, governor_list) { |
36 | if (i >= (ssize_t) ((PAGE_SIZE/sizeof(char)) - CPUIDLE_NAME_LEN - 2)) | 38 | if (i >= (ssize_t) ((PAGE_SIZE/sizeof(char)) - |
39 | CPUIDLE_NAME_LEN - 2)) | ||
37 | goto out; | 40 | goto out; |
38 | i += scnprintf(&buf[i], CPUIDLE_NAME_LEN, "%s ", tmp->name); | 41 | i += scnprintf(&buf[i], CPUIDLE_NAME_LEN, "%s ", tmp->name); |
39 | } | 42 | } |
@@ -166,13 +169,28 @@ struct cpuidle_attr { | |||
166 | #define define_one_rw(_name, show, store) \ | 169 | #define define_one_rw(_name, show, store) \ |
167 | static struct cpuidle_attr attr_##_name = __ATTR(_name, 0644, show, store) | 170 | static struct cpuidle_attr attr_##_name = __ATTR(_name, 0644, show, store) |
168 | 171 | ||
169 | #define kobj_to_cpuidledev(k) container_of(k, struct cpuidle_device, kobj) | ||
170 | #define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr) | 172 | #define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr) |
171 | static ssize_t cpuidle_show(struct kobject * kobj, struct attribute * attr ,char * buf) | 173 | |
174 | struct cpuidle_device_kobj { | ||
175 | struct cpuidle_device *dev; | ||
176 | struct completion kobj_unregister; | ||
177 | struct kobject kobj; | ||
178 | }; | ||
179 | |||
180 | static inline struct cpuidle_device *to_cpuidle_device(struct kobject *kobj) | ||
181 | { | ||
182 | struct cpuidle_device_kobj *kdev = | ||
183 | container_of(kobj, struct cpuidle_device_kobj, kobj); | ||
184 | |||
185 | return kdev->dev; | ||
186 | } | ||
187 | |||
188 | static ssize_t cpuidle_show(struct kobject *kobj, struct attribute *attr, | ||
189 | char *buf) | ||
172 | { | 190 | { |
173 | int ret = -EIO; | 191 | int ret = -EIO; |
174 | struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); | 192 | struct cpuidle_device *dev = to_cpuidle_device(kobj); |
175 | struct cpuidle_attr * cattr = attr_to_cpuidleattr(attr); | 193 | struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr); |
176 | 194 | ||
177 | if (cattr->show) { | 195 | if (cattr->show) { |
178 | mutex_lock(&cpuidle_lock); | 196 | mutex_lock(&cpuidle_lock); |
@@ -182,12 +200,12 @@ static ssize_t cpuidle_show(struct kobject * kobj, struct attribute * attr ,char | |||
182 | return ret; | 200 | return ret; |
183 | } | 201 | } |
184 | 202 | ||
185 | static ssize_t cpuidle_store(struct kobject * kobj, struct attribute * attr, | 203 | static ssize_t cpuidle_store(struct kobject *kobj, struct attribute *attr, |
186 | const char * buf, size_t count) | 204 | const char *buf, size_t count) |
187 | { | 205 | { |
188 | int ret = -EIO; | 206 | int ret = -EIO; |
189 | struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); | 207 | struct cpuidle_device *dev = to_cpuidle_device(kobj); |
190 | struct cpuidle_attr * cattr = attr_to_cpuidleattr(attr); | 208 | struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr); |
191 | 209 | ||
192 | if (cattr->store) { | 210 | if (cattr->store) { |
193 | mutex_lock(&cpuidle_lock); | 211 | mutex_lock(&cpuidle_lock); |
@@ -204,9 +222,10 @@ static const struct sysfs_ops cpuidle_sysfs_ops = { | |||
204 | 222 | ||
205 | static void cpuidle_sysfs_release(struct kobject *kobj) | 223 | static void cpuidle_sysfs_release(struct kobject *kobj) |
206 | { | 224 | { |
207 | struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); | 225 | struct cpuidle_device_kobj *kdev = |
226 | container_of(kobj, struct cpuidle_device_kobj, kobj); | ||
208 | 227 | ||
209 | complete(&dev->kobj_unregister); | 228 | complete(&kdev->kobj_unregister); |
210 | } | 229 | } |
211 | 230 | ||
212 | static struct kobj_type ktype_cpuidle = { | 231 | static struct kobj_type ktype_cpuidle = { |
@@ -237,8 +256,8 @@ static ssize_t show_state_##_name(struct cpuidle_state *state, \ | |||
237 | 256 | ||
238 | #define define_store_state_ull_function(_name) \ | 257 | #define define_store_state_ull_function(_name) \ |
239 | static ssize_t store_state_##_name(struct cpuidle_state *state, \ | 258 | static ssize_t store_state_##_name(struct cpuidle_state *state, \ |
240 | struct cpuidle_state_usage *state_usage, \ | 259 | struct cpuidle_state_usage *state_usage, \ |
241 | const char *buf, size_t size) \ | 260 | const char *buf, size_t size) \ |
242 | { \ | 261 | { \ |
243 | unsigned long long value; \ | 262 | unsigned long long value; \ |
244 | int err; \ | 263 | int err; \ |
@@ -256,14 +275,16 @@ static ssize_t store_state_##_name(struct cpuidle_state *state, \ | |||
256 | 275 | ||
257 | #define define_show_state_ull_function(_name) \ | 276 | #define define_show_state_ull_function(_name) \ |
258 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ | 277 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ |
259 | struct cpuidle_state_usage *state_usage, char *buf) \ | 278 | struct cpuidle_state_usage *state_usage, \ |
279 | char *buf) \ | ||
260 | { \ | 280 | { \ |
261 | return sprintf(buf, "%llu\n", state_usage->_name);\ | 281 | return sprintf(buf, "%llu\n", state_usage->_name);\ |
262 | } | 282 | } |
263 | 283 | ||
264 | #define define_show_state_str_function(_name) \ | 284 | #define define_show_state_str_function(_name) \ |
265 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ | 285 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ |
266 | struct cpuidle_state_usage *state_usage, char *buf) \ | 286 | struct cpuidle_state_usage *state_usage, \ |
287 | char *buf) \ | ||
267 | { \ | 288 | { \ |
268 | if (state->_name[0] == '\0')\ | 289 | if (state->_name[0] == '\0')\ |
269 | return sprintf(buf, "<null>\n");\ | 290 | return sprintf(buf, "<null>\n");\ |
@@ -309,8 +330,9 @@ struct cpuidle_state_kobj { | |||
309 | #define kobj_to_state(k) (kobj_to_state_obj(k)->state) | 330 | #define kobj_to_state(k) (kobj_to_state_obj(k)->state) |
310 | #define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage) | 331 | #define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage) |
311 | #define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr) | 332 | #define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr) |
312 | static ssize_t cpuidle_state_show(struct kobject * kobj, | 333 | |
313 | struct attribute * attr ,char * buf) | 334 | static ssize_t cpuidle_state_show(struct kobject *kobj, struct attribute *attr, |
335 | char * buf) | ||
314 | { | 336 | { |
315 | int ret = -EIO; | 337 | int ret = -EIO; |
316 | struct cpuidle_state *state = kobj_to_state(kobj); | 338 | struct cpuidle_state *state = kobj_to_state(kobj); |
@@ -323,8 +345,8 @@ static ssize_t cpuidle_state_show(struct kobject * kobj, | |||
323 | return ret; | 345 | return ret; |
324 | } | 346 | } |
325 | 347 | ||
326 | static ssize_t cpuidle_state_store(struct kobject *kobj, | 348 | static ssize_t cpuidle_state_store(struct kobject *kobj, struct attribute *attr, |
327 | struct attribute *attr, const char *buf, size_t size) | 349 | const char *buf, size_t size) |
328 | { | 350 | { |
329 | int ret = -EIO; | 351 | int ret = -EIO; |
330 | struct cpuidle_state *state = kobj_to_state(kobj); | 352 | struct cpuidle_state *state = kobj_to_state(kobj); |
@@ -371,6 +393,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device) | |||
371 | { | 393 | { |
372 | int i, ret = -ENOMEM; | 394 | int i, ret = -ENOMEM; |
373 | struct cpuidle_state_kobj *kobj; | 395 | struct cpuidle_state_kobj *kobj; |
396 | struct cpuidle_device_kobj *kdev = device->kobj_dev; | ||
374 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); | 397 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); |
375 | 398 | ||
376 | /* state statistics */ | 399 | /* state statistics */ |
@@ -383,7 +406,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device) | |||
383 | init_completion(&kobj->kobj_unregister); | 406 | init_completion(&kobj->kobj_unregister); |
384 | 407 | ||
385 | ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, | 408 | ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, |
386 | &device->kobj, "state%d", i); | 409 | &kdev->kobj, "state%d", i); |
387 | if (ret) { | 410 | if (ret) { |
388 | kfree(kobj); | 411 | kfree(kobj); |
389 | goto error_state; | 412 | goto error_state; |
@@ -449,8 +472,8 @@ static void cpuidle_driver_sysfs_release(struct kobject *kobj) | |||
449 | complete(&driver_kobj->kobj_unregister); | 472 | complete(&driver_kobj->kobj_unregister); |
450 | } | 473 | } |
451 | 474 | ||
452 | static ssize_t cpuidle_driver_show(struct kobject *kobj, struct attribute * attr, | 475 | static ssize_t cpuidle_driver_show(struct kobject *kobj, struct attribute *attr, |
453 | char * buf) | 476 | char *buf) |
454 | { | 477 | { |
455 | int ret = -EIO; | 478 | int ret = -EIO; |
456 | struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj); | 479 | struct cpuidle_driver_kobj *driver_kobj = kobj_to_driver_kobj(kobj); |
@@ -500,6 +523,7 @@ static struct kobj_type ktype_driver_cpuidle = { | |||
500 | static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) | 523 | static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) |
501 | { | 524 | { |
502 | struct cpuidle_driver_kobj *kdrv; | 525 | struct cpuidle_driver_kobj *kdrv; |
526 | struct cpuidle_device_kobj *kdev = dev->kobj_dev; | ||
503 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); | 527 | struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); |
504 | int ret; | 528 | int ret; |
505 | 529 | ||
@@ -511,7 +535,7 @@ static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) | |||
511 | init_completion(&kdrv->kobj_unregister); | 535 | init_completion(&kdrv->kobj_unregister); |
512 | 536 | ||
513 | ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle, | 537 | ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle, |
514 | &dev->kobj, "driver"); | 538 | &kdev->kobj, "driver"); |
515 | if (ret) { | 539 | if (ret) { |
516 | kfree(kdrv); | 540 | kfree(kdrv); |
517 | return ret; | 541 | return ret; |
@@ -580,16 +604,28 @@ void cpuidle_remove_device_sysfs(struct cpuidle_device *device) | |||
580 | */ | 604 | */ |
581 | int cpuidle_add_sysfs(struct cpuidle_device *dev) | 605 | int cpuidle_add_sysfs(struct cpuidle_device *dev) |
582 | { | 606 | { |
607 | struct cpuidle_device_kobj *kdev; | ||
583 | struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); | 608 | struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); |
584 | int error; | 609 | int error; |
585 | 610 | ||
586 | init_completion(&dev->kobj_unregister); | 611 | kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); |
612 | if (!kdev) | ||
613 | return -ENOMEM; | ||
614 | kdev->dev = dev; | ||
615 | dev->kobj_dev = kdev; | ||
587 | 616 | ||
588 | error = kobject_init_and_add(&dev->kobj, &ktype_cpuidle, &cpu_dev->kobj, | 617 | init_completion(&kdev->kobj_unregister); |
589 | "cpuidle"); | 618 | |
590 | if (!error) | 619 | error = kobject_init_and_add(&kdev->kobj, &ktype_cpuidle, &cpu_dev->kobj, |
591 | kobject_uevent(&dev->kobj, KOBJ_ADD); | 620 | "cpuidle"); |
592 | return error; | 621 | if (error) { |
622 | kfree(kdev); | ||
623 | return error; | ||
624 | } | ||
625 | |||
626 | kobject_uevent(&kdev->kobj, KOBJ_ADD); | ||
627 | |||
628 | return 0; | ||
593 | } | 629 | } |
594 | 630 | ||
595 | /** | 631 | /** |
@@ -598,6 +634,9 @@ int cpuidle_add_sysfs(struct cpuidle_device *dev) | |||
598 | */ | 634 | */ |
599 | void cpuidle_remove_sysfs(struct cpuidle_device *dev) | 635 | void cpuidle_remove_sysfs(struct cpuidle_device *dev) |
600 | { | 636 | { |
601 | kobject_put(&dev->kobj); | 637 | struct cpuidle_device_kobj *kdev = dev->kobj_dev; |
602 | wait_for_completion(&dev->kobj_unregister); | 638 | |
639 | kobject_put(&kdev->kobj); | ||
640 | wait_for_completion(&kdev->kobj_unregister); | ||
641 | kfree(kdev); | ||
603 | } | 642 | } |