blob: 3af841fb397a0cda095b9ca794c04271c5164332 [file] [log] [blame]
Len Brown4f86d3a2007-10-03 18:58:00 -04001/*
2 * driver.c - driver support
3 *
4 * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
5 * Shaohua Li <shaohua.li@intel.com>
6 * Adam Belay <abelay@novell.com>
7 *
8 * This code is licenced under the GPL.
9 */
10
11#include <linux/mutex.h>
12#include <linux/module.h>
13#include <linux/cpuidle.h>
14
15#include "cpuidle.h"
16
Len Brown4f86d3a2007-10-03 18:58:00 -040017DEFINE_SPINLOCK(cpuidle_driver_lock);
18
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000019static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
20static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
21
Daniel Lezcanoed953472012-09-22 00:38:32 +020022static void set_power_states(struct cpuidle_driver *drv)
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +053023{
24 int i;
Daniel Lezcanoed953472012-09-22 00:38:32 +020025
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +053026 /*
27 * cpuidle driver should set the drv->power_specified bit
28 * before registering if the driver provides
29 * power_usage numbers.
30 *
31 * If power_specified is not set,
32 * we fill in power_usage with decreasing values as the
33 * cpuidle code has an implicit assumption that state Cn
34 * uses less power than C(n-1).
35 *
36 * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned
37 * an power value of -1. So we use -2, -3, etc, for other
38 * c-states.
39 */
Daniel Lezcanoed953472012-09-22 00:38:32 +020040 for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++)
41 drv->states[i].power_usage = -1 - i;
Deepthi Dharwar46bcfad2011-10-28 16:20:42 +053042}
43
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000044static void __cpuidle_driver_init(struct cpuidle_driver *drv)
45{
46 drv->refcnt = 0;
47
48 if (!drv->power_specified)
49 set_power_states(drv);
50}
51
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000052static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
Len Brown4f86d3a2007-10-03 18:58:00 -040053{
Daniel Lezcanofc850f32012-03-26 14:51:26 +020054 if (!drv || !drv->state_count)
Len Brown4f86d3a2007-10-03 18:58:00 -040055 return -EINVAL;
56
Len Brown62027ae2011-04-01 18:13:10 -040057 if (cpuidle_disabled())
58 return -ENODEV;
59
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000060 if (__cpuidle_get_cpu_driver(cpu))
Len Brown4f86d3a2007-10-03 18:58:00 -040061 return -EBUSY;
Daniel Lezcanoed953472012-09-22 00:38:32 +020062
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000063 __cpuidle_driver_init(drv);
Daniel Lezcanoed953472012-09-22 00:38:32 +020064
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000065 __cpuidle_set_cpu_driver(drv, cpu);
Len Brown4f86d3a2007-10-03 18:58:00 -040066
67 return 0;
68}
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000069
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000070static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu)
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000071{
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000072 if (drv != __cpuidle_get_cpu_driver(cpu))
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000073 return;
74
75 if (!WARN_ON(drv->refcnt > 0))
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000076 __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);
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000138}
139
140/**
141 * cpuidle_register_driver - registers a driver
142 * @drv: the driver
143 */
144int cpuidle_register_driver(struct cpuidle_driver *drv)
145{
146 int ret;
147
148 spin_lock(&cpuidle_driver_lock);
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000149 ret = __cpuidle_register_all_cpu_driver(drv);
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000150 spin_unlock(&cpuidle_driver_lock);
151
152 return ret;
153}
Len Brown4f86d3a2007-10-03 18:58:00 -0400154EXPORT_SYMBOL_GPL(cpuidle_register_driver);
155
156/**
157 * cpuidle_unregister_driver - unregisters a driver
158 * @drv: the driver
159 */
160void cpuidle_unregister_driver(struct cpuidle_driver *drv)
161{
Len Brown4f86d3a2007-10-03 18:58:00 -0400162 spin_lock(&cpuidle_driver_lock);
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000163 __cpuidle_unregister_all_cpu_driver(drv);
Len Brown4f86d3a2007-10-03 18:58:00 -0400164 spin_unlock(&cpuidle_driver_lock);
165}
Len Brown4f86d3a2007-10-03 18:58:00 -0400166EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200167
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000168#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)
178{
179 return cpuidle_curr_driver;
180}
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);
199
200/**
201 * cpuidle_unregister_driver - unregisters a driver
202 * @drv: the driver
203 */
204void cpuidle_unregister_driver(struct cpuidle_driver *drv)
205{
206 int cpu;
207
208 cpu = get_cpu();
209 spin_lock(&cpuidle_driver_lock);
210 __cpuidle_unregister_driver(drv, cpu);
211 spin_unlock(&cpuidle_driver_lock);
212 put_cpu();
213}
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);
250
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200251struct cpuidle_driver *cpuidle_driver_ref(void)
252{
253 struct cpuidle_driver *drv;
254
255 spin_lock(&cpuidle_driver_lock);
256
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000257 drv = cpuidle_get_driver();
Daniel Lezcano42f67f22012-10-31 16:44:45 +0000258 drv->refcnt++;
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200259
260 spin_unlock(&cpuidle_driver_lock);
261 return drv;
262}
263
264void cpuidle_driver_unref(void)
265{
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000266 struct cpuidle_driver *drv = cpuidle_get_driver();
Daniel Lezcano42f67f22012-10-31 16:44:45 +0000267
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200268 spin_lock(&cpuidle_driver_lock);
269
Daniel Lezcano42f67f22012-10-31 16:44:45 +0000270 if (drv && !WARN_ON(drv->refcnt <= 0))
271 drv->refcnt--;
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200272
273 spin_unlock(&cpuidle_driver_lock);
274}