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