diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-06-28 07:01:06 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-06-28 07:01:06 -0400 |
commit | 93bc11c3cc1f6d441df2362f0d3efea75dc0e3f4 (patch) | |
tree | a6adc2d83018b98e45c91dbbc9c80e95d8a671b1 /drivers | |
parent | 3e79a8a67d6f28b2b1d4344253ca612e77cca8e6 (diff) | |
parent | b39b0981b0811943d724915a8a0150d6ac5110e0 (diff) |
Merge branch 'pm-cpuidle'
* pm-cpuidle:
cpuidle: Fix ARCH_NEEDS_CPU_IDLE_COUPLED dependency warning
cpuidle: Comment the driver's framework code
cpuidle: simplify multiple driver support
ARM: zynq: Add cpuidle support
cpuidle: improve governor Kconfig options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/cpuidle/Kconfig | 27 | ||||
-rw-r--r-- | drivers/cpuidle/Makefile | 1 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle-zynq.c | 83 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 4 | ||||
-rw-r--r-- | drivers/cpuidle/driver.c | 324 |
5 files changed, 307 insertions, 132 deletions
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index c4cc27e5c8a5..81de5d96749b 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig | |||
@@ -1,7 +1,9 @@ | |||
1 | 1 | ||
2 | config CPU_IDLE | 2 | menuconfig CPU_IDLE |
3 | bool "CPU idle PM support" | 3 | bool "CPU idle PM support" |
4 | default y if ACPI || PPC_PSERIES | 4 | default y if ACPI || PPC_PSERIES |
5 | select CPU_IDLE_GOV_LADDER if (!NO_HZ && !NO_HZ_IDLE) | ||
6 | select CPU_IDLE_GOV_MENU if (NO_HZ || NO_HZ_IDLE) | ||
5 | help | 7 | help |
6 | CPU idle is a generic framework for supporting software-controlled | 8 | CPU idle is a generic framework for supporting software-controlled |
7 | idle processor power management. It includes modular cross-platform | 9 | idle processor power management. It includes modular cross-platform |
@@ -9,9 +11,10 @@ config CPU_IDLE | |||
9 | 11 | ||
10 | If you're using an ACPI-enabled platform, you should say Y here. | 12 | If you're using an ACPI-enabled platform, you should say Y here. |
11 | 13 | ||
14 | if CPU_IDLE | ||
15 | |||
12 | config CPU_IDLE_MULTIPLE_DRIVERS | 16 | config CPU_IDLE_MULTIPLE_DRIVERS |
13 | bool "Support multiple cpuidle drivers" | 17 | bool "Support multiple cpuidle drivers" |
14 | depends on CPU_IDLE | ||
15 | default n | 18 | default n |
16 | help | 19 | help |
17 | Allows the cpuidle framework to use different drivers for each CPU. | 20 | Allows the cpuidle framework to use different drivers for each CPU. |
@@ -19,24 +22,26 @@ config CPU_IDLE_MULTIPLE_DRIVERS | |||
19 | states. If unsure say N. | 22 | states. If unsure say N. |
20 | 23 | ||
21 | config CPU_IDLE_GOV_LADDER | 24 | config CPU_IDLE_GOV_LADDER |
22 | bool | 25 | bool "Ladder governor (for periodic timer tick)" |
23 | depends on CPU_IDLE | ||
24 | default y | 26 | default y |
25 | 27 | ||
26 | config CPU_IDLE_GOV_MENU | 28 | config CPU_IDLE_GOV_MENU |
27 | bool | 29 | bool "Menu governor (for tickless system)" |
28 | depends on CPU_IDLE && NO_HZ | ||
29 | default y | 30 | default y |
30 | 31 | ||
31 | config ARCH_NEEDS_CPU_IDLE_COUPLED | ||
32 | def_bool n | ||
33 | |||
34 | if CPU_IDLE | ||
35 | |||
36 | config CPU_IDLE_CALXEDA | 32 | config CPU_IDLE_CALXEDA |
37 | bool "CPU Idle Driver for Calxeda processors" | 33 | bool "CPU Idle Driver for Calxeda processors" |
38 | depends on ARCH_HIGHBANK | 34 | depends on ARCH_HIGHBANK |
39 | help | 35 | help |
40 | Select this to enable cpuidle on Calxeda processors. | 36 | Select this to enable cpuidle on Calxeda processors. |
41 | 37 | ||
38 | config CPU_IDLE_ZYNQ | ||
39 | bool "CPU Idle Driver for Xilinx Zynq processors" | ||
40 | depends on ARCH_ZYNQ | ||
41 | help | ||
42 | Select this to enable cpuidle on Xilinx Zynq processors. | ||
43 | |||
42 | endif | 44 | endif |
45 | |||
46 | config ARCH_NEEDS_CPU_IDLE_COUPLED | ||
47 | def_bool n | ||
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 0d8bd55e776f..8767a7b3eb91 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile | |||
@@ -7,3 +7,4 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o | |||
7 | 7 | ||
8 | obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o | 8 | obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o |
9 | obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o | 9 | obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o |
10 | obj-$(CONFIG_CPU_IDLE_ZYNQ) += cpuidle-zynq.o | ||
diff --git a/drivers/cpuidle/cpuidle-zynq.c b/drivers/cpuidle/cpuidle-zynq.c new file mode 100644 index 000000000000..38e03a183591 --- /dev/null +++ b/drivers/cpuidle/cpuidle-zynq.c | |||
@@ -0,0 +1,83 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012-2013 Xilinx | ||
3 | * | ||
4 | * CPU idle support for Xilinx Zynq | ||
5 | * | ||
6 | * based on arch/arm/mach-at91/cpuidle.c | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License along with | ||
18 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | * The cpu idle uses wait-for-interrupt and RAM self refresh in order | ||
21 | * to implement two idle states - | ||
22 | * #1 wait-for-interrupt | ||
23 | * #2 wait-for-interrupt and RAM self refresh | ||
24 | * | ||
25 | * Maintainer: Michal Simek <michal.simek@xilinx.com> | ||
26 | */ | ||
27 | |||
28 | #include <linux/init.h> | ||
29 | #include <linux/cpu_pm.h> | ||
30 | #include <linux/cpuidle.h> | ||
31 | #include <linux/of.h> | ||
32 | #include <asm/proc-fns.h> | ||
33 | #include <asm/cpuidle.h> | ||
34 | |||
35 | #define ZYNQ_MAX_STATES 2 | ||
36 | |||
37 | /* Actual code that puts the SoC in different idle states */ | ||
38 | static int zynq_enter_idle(struct cpuidle_device *dev, | ||
39 | struct cpuidle_driver *drv, int index) | ||
40 | { | ||
41 | /* Devices must be stopped here */ | ||
42 | cpu_pm_enter(); | ||
43 | |||
44 | /* Add code for DDR self refresh start */ | ||
45 | cpu_do_idle(); | ||
46 | |||
47 | /* Add code for DDR self refresh stop */ | ||
48 | cpu_pm_exit(); | ||
49 | |||
50 | return index; | ||
51 | } | ||
52 | |||
53 | static struct cpuidle_driver zynq_idle_driver = { | ||
54 | .name = "zynq_idle", | ||
55 | .owner = THIS_MODULE, | ||
56 | .states = { | ||
57 | ARM_CPUIDLE_WFI_STATE, | ||
58 | { | ||
59 | .enter = zynq_enter_idle, | ||
60 | .exit_latency = 10, | ||
61 | .target_residency = 10000, | ||
62 | .flags = CPUIDLE_FLAG_TIME_VALID | | ||
63 | CPUIDLE_FLAG_TIMER_STOP, | ||
64 | .name = "RAM_SR", | ||
65 | .desc = "WFI and RAM Self Refresh", | ||
66 | }, | ||
67 | }, | ||
68 | .safe_state_index = 0, | ||
69 | .state_count = ZYNQ_MAX_STATES, | ||
70 | }; | ||
71 | |||
72 | /* Initialize CPU idle by registering the idle states */ | ||
73 | static int __init zynq_cpuidle_init(void) | ||
74 | { | ||
75 | if (!of_machine_is_compatible("xlnx,zynq-7000")) | ||
76 | return -ENODEV; | ||
77 | |||
78 | pr_info("Xilinx Zynq CpuIdle Driver started\n"); | ||
79 | |||
80 | return cpuidle_register(&zynq_idle_driver, NULL); | ||
81 | } | ||
82 | |||
83 | device_initcall(zynq_cpuidle_init); | ||
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index c3a93fece819..fdc432f18022 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c | |||
@@ -466,7 +466,7 @@ void cpuidle_unregister(struct cpuidle_driver *drv) | |||
466 | int cpu; | 466 | int cpu; |
467 | struct cpuidle_device *device; | 467 | struct cpuidle_device *device; |
468 | 468 | ||
469 | for_each_possible_cpu(cpu) { | 469 | for_each_cpu(cpu, drv->cpumask) { |
470 | device = &per_cpu(cpuidle_dev, cpu); | 470 | device = &per_cpu(cpuidle_dev, cpu); |
471 | cpuidle_unregister_device(device); | 471 | cpuidle_unregister_device(device); |
472 | } | 472 | } |
@@ -498,7 +498,7 @@ int cpuidle_register(struct cpuidle_driver *drv, | |||
498 | return ret; | 498 | return ret; |
499 | } | 499 | } |
500 | 500 | ||
501 | for_each_possible_cpu(cpu) { | 501 | for_each_cpu(cpu, drv->cpumask) { |
502 | device = &per_cpu(cpuidle_dev, cpu); | 502 | device = &per_cpu(cpuidle_dev, cpu); |
503 | device->cpu = cpu; | 503 | device->cpu = cpu; |
504 | 504 | ||
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 8dfaaae94444..3ac499d5a207 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c | |||
@@ -18,182 +18,249 @@ | |||
18 | 18 | ||
19 | DEFINE_SPINLOCK(cpuidle_driver_lock); | 19 | DEFINE_SPINLOCK(cpuidle_driver_lock); |
20 | 20 | ||
21 | static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); | 21 | #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS |
22 | static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); | ||
23 | 22 | ||
24 | static void cpuidle_setup_broadcast_timer(void *arg) | 23 | static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); |
24 | |||
25 | /** | ||
26 | * __cpuidle_get_cpu_driver - return the cpuidle driver tied to a CPU. | ||
27 | * @cpu: the CPU handled by the driver | ||
28 | * | ||
29 | * Returns a pointer to struct cpuidle_driver or NULL if no driver has been | ||
30 | * registered for @cpu. | ||
31 | */ | ||
32 | static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) | ||
25 | { | 33 | { |
26 | int cpu = smp_processor_id(); | 34 | return per_cpu(cpuidle_drivers, cpu); |
27 | clockevents_notify((long)(arg), &cpu); | ||
28 | } | 35 | } |
29 | 36 | ||
30 | static void __cpuidle_driver_init(struct cpuidle_driver *drv, int cpu) | 37 | /** |
38 | * __cpuidle_unset_driver - unset per CPU driver variables. | ||
39 | * @drv: a valid pointer to a struct cpuidle_driver | ||
40 | * | ||
41 | * For each CPU in the driver's CPU mask, unset the registered driver per CPU | ||
42 | * variable. If @drv is different from the registered driver, the corresponding | ||
43 | * variable is not cleared. | ||
44 | */ | ||
45 | static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) | ||
31 | { | 46 | { |
32 | int i; | 47 | int cpu; |
33 | |||
34 | drv->refcnt = 0; | ||
35 | 48 | ||
36 | for (i = drv->state_count - 1; i >= 0 ; i--) { | 49 | for_each_cpu(cpu, drv->cpumask) { |
37 | 50 | ||
38 | if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) | 51 | if (drv != __cpuidle_get_cpu_driver(cpu)) |
39 | continue; | 52 | continue; |
40 | 53 | ||
41 | drv->bctimer = 1; | 54 | per_cpu(cpuidle_drivers, cpu) = NULL; |
42 | on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, | ||
43 | (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); | ||
44 | break; | ||
45 | } | 55 | } |
46 | } | 56 | } |
47 | 57 | ||
48 | static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) | 58 | /** |
59 | * __cpuidle_set_driver - set per CPU driver variables the the given driver. | ||
60 | * @drv: a valid pointer to a struct cpuidle_driver | ||
61 | * | ||
62 | * For each CPU in the driver's cpumask, unset the registered driver per CPU | ||
63 | * to @drv. | ||
64 | * | ||
65 | * Returns 0 on success, -EBUSY if the CPUs have driver(s) already. | ||
66 | */ | ||
67 | static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) | ||
49 | { | 68 | { |
50 | if (!drv || !drv->state_count) | 69 | int cpu; |
51 | return -EINVAL; | ||
52 | |||
53 | if (cpuidle_disabled()) | ||
54 | return -ENODEV; | ||
55 | 70 | ||
56 | if (__cpuidle_get_cpu_driver(cpu)) | 71 | for_each_cpu(cpu, drv->cpumask) { |
57 | return -EBUSY; | ||
58 | 72 | ||
59 | __cpuidle_driver_init(drv, cpu); | 73 | if (__cpuidle_get_cpu_driver(cpu)) { |
74 | __cpuidle_unset_driver(drv); | ||
75 | return -EBUSY; | ||
76 | } | ||
60 | 77 | ||
61 | __cpuidle_set_cpu_driver(drv, cpu); | 78 | per_cpu(cpuidle_drivers, cpu) = drv; |
79 | } | ||
62 | 80 | ||
63 | return 0; | 81 | return 0; |
64 | } | 82 | } |
65 | 83 | ||
66 | static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu) | 84 | #else |
67 | { | ||
68 | if (drv != __cpuidle_get_cpu_driver(cpu)) | ||
69 | return; | ||
70 | 85 | ||
71 | if (!WARN_ON(drv->refcnt > 0)) | 86 | static struct cpuidle_driver *cpuidle_curr_driver; |
72 | __cpuidle_set_cpu_driver(NULL, cpu); | ||
73 | 87 | ||
74 | if (drv->bctimer) { | 88 | /** |
75 | drv->bctimer = 0; | 89 | * __cpuidle_get_cpu_driver - return the global cpuidle driver pointer. |
76 | on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, | 90 | * @cpu: ignored without the multiple driver support |
77 | (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); | 91 | * |
78 | } | 92 | * Return a pointer to a struct cpuidle_driver object or NULL if no driver was |
93 | * previously registered. | ||
94 | */ | ||
95 | static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) | ||
96 | { | ||
97 | return cpuidle_curr_driver; | ||
79 | } | 98 | } |
80 | 99 | ||
81 | #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS | 100 | /** |
101 | * __cpuidle_set_driver - assign the global cpuidle driver variable. | ||
102 | * @drv: pointer to a struct cpuidle_driver object | ||
103 | * | ||
104 | * Returns 0 on success, -EBUSY if the driver is already registered. | ||
105 | */ | ||
106 | static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) | ||
107 | { | ||
108 | if (cpuidle_curr_driver) | ||
109 | return -EBUSY; | ||
82 | 110 | ||
83 | static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); | 111 | cpuidle_curr_driver = drv; |
84 | 112 | ||
85 | static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) | 113 | return 0; |
86 | { | ||
87 | per_cpu(cpuidle_drivers, cpu) = drv; | ||
88 | } | 114 | } |
89 | 115 | ||
90 | static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) | 116 | /** |
117 | * __cpuidle_unset_driver - unset the global cpuidle driver variable. | ||
118 | * @drv: a pointer to a struct cpuidle_driver | ||
119 | * | ||
120 | * Reset the global cpuidle variable to NULL. If @drv does not match the | ||
121 | * registered driver, do nothing. | ||
122 | */ | ||
123 | static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) | ||
91 | { | 124 | { |
92 | return per_cpu(cpuidle_drivers, cpu); | 125 | if (drv == cpuidle_curr_driver) |
126 | cpuidle_curr_driver = NULL; | ||
93 | } | 127 | } |
94 | 128 | ||
95 | static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv) | 129 | #endif |
130 | |||
131 | /** | ||
132 | * cpuidle_setup_broadcast_timer - enable/disable the broadcast timer | ||
133 | * @arg: a void pointer used to match the SMP cross call API | ||
134 | * | ||
135 | * @arg is used as a value of type 'long' with on of the two values: | ||
136 | * - CLOCK_EVT_NOTIFY_BROADCAST_ON | ||
137 | * - CLOCK_EVT_NOTIFY_BROADCAST_OFF | ||
138 | * | ||
139 | * Set the broadcast timer notification for the current CPU. This function | ||
140 | * is executed per CPU by an SMP cross call. It not supposed to be called | ||
141 | * directly. | ||
142 | */ | ||
143 | static void cpuidle_setup_broadcast_timer(void *arg) | ||
96 | { | 144 | { |
97 | int cpu; | 145 | int cpu = smp_processor_id(); |
98 | for_each_present_cpu(cpu) | 146 | clockevents_notify((long)(arg), &cpu); |
99 | __cpuidle_unregister_driver(drv, cpu); | ||
100 | } | 147 | } |
101 | 148 | ||
102 | static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv) | 149 | /** |
150 | * __cpuidle_driver_init - initialize the driver's internal data | ||
151 | * @drv: a valid pointer to a struct cpuidle_driver | ||
152 | * | ||
153 | * Returns 0 on success, a negative error code otherwise. | ||
154 | */ | ||
155 | static int __cpuidle_driver_init(struct cpuidle_driver *drv) | ||
103 | { | 156 | { |
104 | int ret = 0; | 157 | int i; |
105 | int i, cpu; | ||
106 | 158 | ||
107 | for_each_present_cpu(cpu) { | 159 | drv->refcnt = 0; |
108 | ret = __cpuidle_register_driver(drv, cpu); | ||
109 | if (ret) | ||
110 | break; | ||
111 | } | ||
112 | 160 | ||
113 | if (ret) | 161 | /* |
114 | for_each_present_cpu(i) { | 162 | * Use all possible CPUs as the default, because if the kernel boots |
115 | if (i == cpu) | 163 | * with some CPUs offline and then we online one of them, the CPU |
116 | break; | 164 | * notifier has to know which driver to assign. |
117 | __cpuidle_unregister_driver(drv, i); | 165 | */ |
118 | } | 166 | if (!drv->cpumask) |
167 | drv->cpumask = (struct cpumask *)cpu_possible_mask; | ||
168 | |||
169 | /* | ||
170 | * Look for the timer stop flag in the different states, so that we know | ||
171 | * if the broadcast timer has to be set up. The loop is in the reverse | ||
172 | * order, because usually on of the the deeper states has this flag set. | ||
173 | */ | ||
174 | for (i = drv->state_count - 1; i >= 0 ; i--) { | ||
119 | 175 | ||
176 | if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) | ||
177 | continue; | ||
120 | 178 | ||
121 | return ret; | 179 | drv->bctimer = 1; |
180 | break; | ||
181 | } | ||
182 | |||
183 | return 0; | ||
122 | } | 184 | } |
123 | 185 | ||
124 | int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu) | 186 | /** |
187 | * __cpuidle_register_driver: register the driver | ||
188 | * @drv: a valid pointer to a struct cpuidle_driver | ||
189 | * | ||
190 | * Do some sanity checks, initialize the driver, assign the driver to the | ||
191 | * global cpuidle driver variable(s) and set up the broadcast timer if the | ||
192 | * cpuidle driver has some states that shut down the local timer. | ||
193 | * | ||
194 | * Returns 0 on success, a negative error code otherwise: | ||
195 | * * -EINVAL if the driver pointer is NULL or no idle states are available | ||
196 | * * -ENODEV if the cpuidle framework is disabled | ||
197 | * * -EBUSY if the driver is already assigned to the global variable(s) | ||
198 | */ | ||
199 | static int __cpuidle_register_driver(struct cpuidle_driver *drv) | ||
125 | { | 200 | { |
126 | int ret; | 201 | int ret; |
127 | 202 | ||
128 | spin_lock(&cpuidle_driver_lock); | 203 | if (!drv || !drv->state_count) |
129 | ret = __cpuidle_register_driver(drv, cpu); | 204 | return -EINVAL; |
130 | spin_unlock(&cpuidle_driver_lock); | ||
131 | 205 | ||
132 | return ret; | 206 | if (cpuidle_disabled()) |
133 | } | 207 | return -ENODEV; |
134 | 208 | ||
135 | void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu) | 209 | ret = __cpuidle_driver_init(drv); |
136 | { | 210 | if (ret) |
137 | spin_lock(&cpuidle_driver_lock); | 211 | return ret; |
138 | __cpuidle_unregister_driver(drv, cpu); | ||
139 | spin_unlock(&cpuidle_driver_lock); | ||
140 | } | ||
141 | 212 | ||
142 | /** | 213 | ret = __cpuidle_set_driver(drv); |
143 | * cpuidle_register_driver - registers a driver | 214 | if (ret) |
144 | * @drv: the driver | 215 | return ret; |
145 | */ | ||
146 | int cpuidle_register_driver(struct cpuidle_driver *drv) | ||
147 | { | ||
148 | int ret; | ||
149 | 216 | ||
150 | spin_lock(&cpuidle_driver_lock); | 217 | if (drv->bctimer) |
151 | ret = __cpuidle_register_all_cpu_driver(drv); | 218 | on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, |
152 | spin_unlock(&cpuidle_driver_lock); | 219 | (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); |
153 | 220 | ||
154 | return ret; | 221 | return 0; |
155 | } | 222 | } |
156 | EXPORT_SYMBOL_GPL(cpuidle_register_driver); | ||
157 | 223 | ||
158 | /** | 224 | /** |
159 | * cpuidle_unregister_driver - unregisters a driver | 225 | * __cpuidle_unregister_driver - unregister the driver |
160 | * @drv: the driver | 226 | * @drv: a valid pointer to a struct cpuidle_driver |
227 | * | ||
228 | * Check if the driver is no longer in use, reset the global cpuidle driver | ||
229 | * variable(s) and disable the timer broadcast notification mechanism if it was | ||
230 | * in use. | ||
231 | * | ||
161 | */ | 232 | */ |
162 | void cpuidle_unregister_driver(struct cpuidle_driver *drv) | 233 | static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) |
163 | { | 234 | { |
164 | spin_lock(&cpuidle_driver_lock); | 235 | if (WARN_ON(drv->refcnt > 0)) |
165 | __cpuidle_unregister_all_cpu_driver(drv); | 236 | return; |
166 | spin_unlock(&cpuidle_driver_lock); | ||
167 | } | ||
168 | EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); | ||
169 | |||
170 | #else | ||
171 | |||
172 | static struct cpuidle_driver *cpuidle_curr_driver; | ||
173 | 237 | ||
174 | static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) | 238 | if (drv->bctimer) { |
175 | { | 239 | drv->bctimer = 0; |
176 | cpuidle_curr_driver = drv; | 240 | on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, |
177 | } | 241 | (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); |
242 | } | ||
178 | 243 | ||
179 | static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) | 244 | __cpuidle_unset_driver(drv); |
180 | { | ||
181 | return cpuidle_curr_driver; | ||
182 | } | 245 | } |
183 | 246 | ||
184 | /** | 247 | /** |
185 | * cpuidle_register_driver - registers a driver | 248 | * cpuidle_register_driver - registers a driver |
186 | * @drv: the driver | 249 | * @drv: a pointer to a valid struct cpuidle_driver |
250 | * | ||
251 | * Register the driver under a lock to prevent concurrent attempts to | ||
252 | * [un]register the driver from occuring at the same time. | ||
253 | * | ||
254 | * Returns 0 on success, a negative error code (returned by | ||
255 | * __cpuidle_register_driver()) otherwise. | ||
187 | */ | 256 | */ |
188 | int cpuidle_register_driver(struct cpuidle_driver *drv) | 257 | int cpuidle_register_driver(struct cpuidle_driver *drv) |
189 | { | 258 | { |
190 | int ret, cpu; | 259 | int ret; |
191 | 260 | ||
192 | cpu = get_cpu(); | ||
193 | spin_lock(&cpuidle_driver_lock); | 261 | spin_lock(&cpuidle_driver_lock); |
194 | ret = __cpuidle_register_driver(drv, cpu); | 262 | ret = __cpuidle_register_driver(drv); |
195 | spin_unlock(&cpuidle_driver_lock); | 263 | spin_unlock(&cpuidle_driver_lock); |
196 | put_cpu(); | ||
197 | 264 | ||
198 | return ret; | 265 | return ret; |
199 | } | 266 | } |
@@ -201,23 +268,24 @@ EXPORT_SYMBOL_GPL(cpuidle_register_driver); | |||
201 | 268 | ||
202 | /** | 269 | /** |
203 | * cpuidle_unregister_driver - unregisters a driver | 270 | * cpuidle_unregister_driver - unregisters a driver |
204 | * @drv: the driver | 271 | * @drv: a pointer to a valid struct cpuidle_driver |
272 | * | ||
273 | * Unregisters the cpuidle driver under a lock to prevent concurrent attempts | ||
274 | * to [un]register the driver from occuring at the same time. @drv has to | ||
275 | * match the currently registered driver. | ||
205 | */ | 276 | */ |
206 | void cpuidle_unregister_driver(struct cpuidle_driver *drv) | 277 | void cpuidle_unregister_driver(struct cpuidle_driver *drv) |
207 | { | 278 | { |
208 | int cpu; | ||
209 | |||
210 | cpu = get_cpu(); | ||
211 | spin_lock(&cpuidle_driver_lock); | 279 | spin_lock(&cpuidle_driver_lock); |
212 | __cpuidle_unregister_driver(drv, cpu); | 280 | __cpuidle_unregister_driver(drv); |
213 | spin_unlock(&cpuidle_driver_lock); | 281 | spin_unlock(&cpuidle_driver_lock); |
214 | put_cpu(); | ||
215 | } | 282 | } |
216 | EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); | 283 | EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); |
217 | #endif | ||
218 | 284 | ||
219 | /** | 285 | /** |
220 | * cpuidle_get_driver - return the current driver | 286 | * cpuidle_get_driver - return the driver tied to the current CPU. |
287 | * | ||
288 | * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered. | ||
221 | */ | 289 | */ |
222 | struct cpuidle_driver *cpuidle_get_driver(void) | 290 | struct cpuidle_driver *cpuidle_get_driver(void) |
223 | { | 291 | { |
@@ -233,7 +301,11 @@ struct cpuidle_driver *cpuidle_get_driver(void) | |||
233 | EXPORT_SYMBOL_GPL(cpuidle_get_driver); | 301 | EXPORT_SYMBOL_GPL(cpuidle_get_driver); |
234 | 302 | ||
235 | /** | 303 | /** |
236 | * cpuidle_get_cpu_driver - return the driver tied with a cpu | 304 | * cpuidle_get_cpu_driver - return the driver registered for a CPU. |
305 | * @dev: a valid pointer to a struct cpuidle_device | ||
306 | * | ||
307 | * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered | ||
308 | * for the CPU associated with @dev. | ||
237 | */ | 309 | */ |
238 | struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) | 310 | struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) |
239 | { | 311 | { |
@@ -244,6 +316,14 @@ struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) | |||
244 | } | 316 | } |
245 | EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver); | 317 | EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver); |
246 | 318 | ||
319 | /** | ||
320 | * cpuidle_driver_ref - get a reference to the driver. | ||
321 | * | ||
322 | * Increment the reference counter of the cpuidle driver associated with | ||
323 | * the current CPU. | ||
324 | * | ||
325 | * Returns a pointer to the driver, or NULL if the current CPU has no driver. | ||
326 | */ | ||
247 | struct cpuidle_driver *cpuidle_driver_ref(void) | 327 | struct cpuidle_driver *cpuidle_driver_ref(void) |
248 | { | 328 | { |
249 | struct cpuidle_driver *drv; | 329 | struct cpuidle_driver *drv; |
@@ -257,6 +337,12 @@ struct cpuidle_driver *cpuidle_driver_ref(void) | |||
257 | return drv; | 337 | return drv; |
258 | } | 338 | } |
259 | 339 | ||
340 | /** | ||
341 | * cpuidle_driver_unref - puts down the refcount for the driver | ||
342 | * | ||
343 | * Decrement the reference counter of the cpuidle driver associated with | ||
344 | * the current CPU. | ||
345 | */ | ||
260 | void cpuidle_driver_unref(void) | 346 | void cpuidle_driver_unref(void) |
261 | { | 347 | { |
262 | struct cpuidle_driver *drv = cpuidle_get_driver(); | 348 | struct cpuidle_driver *drv = cpuidle_get_driver(); |