Merge remote-tracking branch 'lsk/v3.10/topic/arm64-dma' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.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 #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         if (drv)
255                 drv->refcnt++;
256
257         spin_unlock(&cpuidle_driver_lock);
258         return drv;
259 }
260
261 void cpuidle_driver_unref(void)
262 {
263         struct cpuidle_driver *drv = cpuidle_get_driver();
264
265         spin_lock(&cpuidle_driver_lock);
266
267         if (drv && !WARN_ON(drv->refcnt <= 0))
268                 drv->refcnt--;
269
270         spin_unlock(&cpuidle_driver_lock);
271 }