xref: /openbmc/linux/drivers/cpuidle/driver.c (revision f35e839a)
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 #include <linux/cpumask.h>
15 #include <linux/clockchips.h>
16 
17 #include "cpuidle.h"
18 
19 DEFINE_SPINLOCK(cpuidle_driver_lock);
20 
21 static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
22 static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
23 
24 static void cpuidle_setup_broadcast_timer(void *arg)
25 {
26 	int cpu = smp_processor_id();
27 	clockevents_notify((long)(arg), &cpu);
28 }
29 
30 static void __cpuidle_driver_init(struct cpuidle_driver *drv, int cpu)
31 {
32 	int i;
33 
34 	drv->refcnt = 0;
35 
36 	for (i = drv->state_count - 1; i >= 0 ; i--) {
37 
38 		if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP))
39 			continue;
40 
41 		drv->bctimer = 1;
42 		on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer,
43 				 (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1);
44 		break;
45 	}
46 }
47 
48 static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
49 {
50 	if (!drv || !drv->state_count)
51 		return -EINVAL;
52 
53 	if (cpuidle_disabled())
54 		return -ENODEV;
55 
56 	if (__cpuidle_get_cpu_driver(cpu))
57 		return -EBUSY;
58 
59 	__cpuidle_driver_init(drv, cpu);
60 
61 	__cpuidle_set_cpu_driver(drv, cpu);
62 
63 	return 0;
64 }
65 
66 static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu)
67 {
68 	if (drv != __cpuidle_get_cpu_driver(cpu))
69 		return;
70 
71 	if (!WARN_ON(drv->refcnt > 0))
72 		__cpuidle_set_cpu_driver(NULL, cpu);
73 
74 	if (drv->bctimer) {
75 		drv->bctimer = 0;
76 		on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer,
77 				 (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1);
78 	}
79 }
80 
81 #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
82 
83 static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers);
84 
85 static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
86 {
87 	per_cpu(cpuidle_drivers, cpu) = drv;
88 }
89 
90 static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
91 {
92 	return per_cpu(cpuidle_drivers, cpu);
93 }
94 
95 static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv)
96 {
97 	int cpu;
98 	for_each_present_cpu(cpu)
99 		__cpuidle_unregister_driver(drv, cpu);
100 }
101 
102 static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv)
103 {
104 	int ret = 0;
105 	int i, cpu;
106 
107 	for_each_present_cpu(cpu) {
108 		ret = __cpuidle_register_driver(drv, cpu);
109 		if (ret)
110 			break;
111 	}
112 
113 	if (ret)
114 		for_each_present_cpu(i) {
115 			if (i == cpu)
116 				break;
117 			__cpuidle_unregister_driver(drv, i);
118 		}
119 
120 
121 	return ret;
122 }
123 
124 int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu)
125 {
126 	int ret;
127 
128 	spin_lock(&cpuidle_driver_lock);
129 	ret = __cpuidle_register_driver(drv, cpu);
130 	spin_unlock(&cpuidle_driver_lock);
131 
132 	return ret;
133 }
134 
135 void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu)
136 {
137 	spin_lock(&cpuidle_driver_lock);
138 	__cpuidle_unregister_driver(drv, cpu);
139 	spin_unlock(&cpuidle_driver_lock);
140 }
141 
142 /**
143  * cpuidle_register_driver - registers a driver
144  * @drv: the driver
145  */
146 int cpuidle_register_driver(struct cpuidle_driver *drv)
147 {
148 	int ret;
149 
150 	spin_lock(&cpuidle_driver_lock);
151 	ret = __cpuidle_register_all_cpu_driver(drv);
152 	spin_unlock(&cpuidle_driver_lock);
153 
154 	return ret;
155 }
156 EXPORT_SYMBOL_GPL(cpuidle_register_driver);
157 
158 /**
159  * cpuidle_unregister_driver - unregisters a driver
160  * @drv: the driver
161  */
162 void cpuidle_unregister_driver(struct cpuidle_driver *drv)
163 {
164 	spin_lock(&cpuidle_driver_lock);
165 	__cpuidle_unregister_all_cpu_driver(drv);
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 
174 static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
175 {
176 	cpuidle_curr_driver = drv;
177 }
178 
179 static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
180 {
181 	return cpuidle_curr_driver;
182 }
183 
184 /**
185  * cpuidle_register_driver - registers a driver
186  * @drv: the driver
187  */
188 int cpuidle_register_driver(struct cpuidle_driver *drv)
189 {
190 	int ret, cpu;
191 
192 	cpu = get_cpu();
193 	spin_lock(&cpuidle_driver_lock);
194 	ret = __cpuidle_register_driver(drv, cpu);
195 	spin_unlock(&cpuidle_driver_lock);
196 	put_cpu();
197 
198 	return ret;
199 }
200 EXPORT_SYMBOL_GPL(cpuidle_register_driver);
201 
202 /**
203  * cpuidle_unregister_driver - unregisters a driver
204  * @drv: the driver
205  */
206 void cpuidle_unregister_driver(struct cpuidle_driver *drv)
207 {
208 	int cpu;
209 
210 	cpu = get_cpu();
211 	spin_lock(&cpuidle_driver_lock);
212 	__cpuidle_unregister_driver(drv, cpu);
213 	spin_unlock(&cpuidle_driver_lock);
214 	put_cpu();
215 }
216 EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
217 #endif
218 
219 /**
220  * cpuidle_get_driver - return the current driver
221  */
222 struct cpuidle_driver *cpuidle_get_driver(void)
223 {
224 	struct cpuidle_driver *drv;
225 	int cpu;
226 
227 	cpu = get_cpu();
228 	drv = __cpuidle_get_cpu_driver(cpu);
229 	put_cpu();
230 
231 	return drv;
232 }
233 EXPORT_SYMBOL_GPL(cpuidle_get_driver);
234 
235 /**
236  * cpuidle_get_cpu_driver - return the driver tied with a cpu
237  */
238 struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev)
239 {
240 	if (!dev)
241 		return NULL;
242 
243 	return __cpuidle_get_cpu_driver(dev->cpu);
244 }
245 EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
246 
247 struct cpuidle_driver *cpuidle_driver_ref(void)
248 {
249 	struct cpuidle_driver *drv;
250 
251 	spin_lock(&cpuidle_driver_lock);
252 
253 	drv = cpuidle_get_driver();
254 	drv->refcnt++;
255 
256 	spin_unlock(&cpuidle_driver_lock);
257 	return drv;
258 }
259 
260 void cpuidle_driver_unref(void)
261 {
262 	struct cpuidle_driver *drv = cpuidle_get_driver();
263 
264 	spin_lock(&cpuidle_driver_lock);
265 
266 	if (drv && !WARN_ON(drv->refcnt <= 0))
267 		drv->refcnt--;
268 
269 	spin_unlock(&cpuidle_driver_lock);
270 }
271