aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpuidle/driver.c
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/cpuidle/driver.c
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/cpuidle/driver.c')
-rw-r--r--drivers/cpuidle/driver.c166
1 files changed, 149 insertions, 17 deletions
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{