]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/cpuidle/driver.c
Merge remote-tracking branch 'regmap/fix/debugfs' into tmp
[karo-tx-linux.git] / drivers / cpuidle / driver.c
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 }