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