xref: /linux/drivers/cpuidle/driver.c (revision 0d456bad36d42d16022be045c8a53ddbb59ee478)
1 /*
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 
17 DEFINE_SPINLOCK(cpuidle_driver_lock);
18 
19 static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
20 static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
21 
22 static void set_power_states(struct cpuidle_driver *drv)
23 {
24 	int i;
25 
26 	/*
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 	 */
40 	for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++)
41 		drv->states[i].power_usage = -1 - i;
42 }
43 
44 static 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 
52 static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
53 {
54 	if (!drv || !drv->state_count)
55 		return -EINVAL;
56 
57 	if (cpuidle_disabled())
58 		return -ENODEV;
59 
60 	if (__cpuidle_get_cpu_driver(cpu))
61 		return -EBUSY;
62 
63 	__cpuidle_driver_init(drv);
64 
65 	__cpuidle_set_cpu_driver(drv, cpu);
66 
67 	return 0;
68 }
69 
70 static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu)
71 {
72 	if (drv != __cpuidle_get_cpu_driver(cpu))
73 		return;
74 
75 	if (!WARN_ON(drv->refcnt > 0))
76 		__cpuidle_set_cpu_driver(NULL, cpu);
77 }
78 
79 #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
80 
81 static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers);
82 
83 static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
84 {
85 	per_cpu(cpuidle_drivers, cpu) = drv;
86 }
87 
88 static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
89 {
90 	return per_cpu(cpuidle_drivers, cpu);
91 }
92 
93 static 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 
100 static 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 
122 int 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 
133 void 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);
138 }
139 
140 /**
141  * cpuidle_register_driver - registers a driver
142  * @drv: the driver
143  */
144 int cpuidle_register_driver(struct cpuidle_driver *drv)
145 {
146 	int ret;
147 
148 	spin_lock(&cpuidle_driver_lock);
149 	ret = __cpuidle_register_all_cpu_driver(drv);
150 	spin_unlock(&cpuidle_driver_lock);
151 
152 	return ret;
153 }
154 EXPORT_SYMBOL_GPL(cpuidle_register_driver);
155 
156 /**
157  * cpuidle_unregister_driver - unregisters a driver
158  * @drv: the driver
159  */
160 void 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 }
166 EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
167 
168 #else
169 
170 static struct cpuidle_driver *cpuidle_curr_driver;
171 
172 static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
173 {
174 	cpuidle_curr_driver = drv;
175 }
176 
177 static 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  */
186 int 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 }
198 EXPORT_SYMBOL_GPL(cpuidle_register_driver);
199 
200 /**
201  * cpuidle_unregister_driver - unregisters a driver
202  * @drv: the driver
203  */
204 void 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 }
214 EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
215 #endif
216 
217 /**
218  * cpuidle_get_driver - return the current driver
219  */
220 struct 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 }
231 EXPORT_SYMBOL_GPL(cpuidle_get_driver);
232 
233 /**
234  * cpuidle_get_cpu_driver - return the driver tied with a cpu
235  */
236 struct 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 }
249 EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
250 
251 struct cpuidle_driver *cpuidle_driver_ref(void)
252 {
253 	struct cpuidle_driver *drv;
254 
255 	spin_lock(&cpuidle_driver_lock);
256 
257 	drv = cpuidle_get_driver();
258 	drv->refcnt++;
259 
260 	spin_unlock(&cpuidle_driver_lock);
261 	return drv;
262 }
263 
264 void cpuidle_driver_unref(void)
265 {
266 	struct cpuidle_driver *drv = cpuidle_get_driver();
267 
268 	spin_lock(&cpuidle_driver_lock);
269 
270 	if (drv && !WARN_ON(drv->refcnt <= 0))
271 		drv->refcnt--;
272 
273 	spin_unlock(&cpuidle_driver_lock);
274 }
275