cpuidle: simplify multiple driver support
[deliverable/linux.git] / drivers / cpuidle / driver.c
CommitLineData
4f86d3a8
LB
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>
a06df062
DL
14#include <linux/cpumask.h>
15#include <linux/clockchips.h>
4f86d3a8
LB
16
17#include "cpuidle.h"
18
4f86d3a8
LB
19DEFINE_SPINLOCK(cpuidle_driver_lock);
20
82467a5a 21#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
bf4d1b5d 22
82467a5a
DL
23static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers);
24
25static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
13dd52f1 26{
82467a5a 27 return per_cpu(cpuidle_drivers, cpu);
a06df062
DL
28}
29
82467a5a 30static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv)
a06df062 31{
82467a5a 32 int cpu;
a06df062 33
82467a5a 34 for_each_cpu(cpu, drv->cpumask) {
a06df062 35
82467a5a 36 if (drv != __cpuidle_get_cpu_driver(cpu))
a06df062
DL
37 continue;
38
82467a5a 39 per_cpu(cpuidle_drivers, cpu) = NULL;
a06df062 40 }
13dd52f1
DL
41}
42
82467a5a 43static inline int __cpuidle_set_driver(struct cpuidle_driver *drv)
4f86d3a8 44{
82467a5a 45 int cpu;
62027aea 46
82467a5a 47 for_each_cpu(cpu, drv->cpumask) {
ed953472 48
82467a5a
DL
49 if (__cpuidle_get_cpu_driver(cpu)) {
50 __cpuidle_unset_driver(drv);
51 return -EBUSY;
52 }
ed953472 53
82467a5a
DL
54 per_cpu(cpuidle_drivers, cpu) = drv;
55 }
42f67f2a 56
13dd52f1
DL
57 return 0;
58}
59
82467a5a 60#else
13dd52f1 61
82467a5a 62static struct cpuidle_driver *cpuidle_curr_driver;
a06df062 63
82467a5a
DL
64static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
65{
66 return cpuidle_curr_driver;
bf4d1b5d
DL
67}
68
82467a5a
DL
69static inline int __cpuidle_set_driver(struct cpuidle_driver *drv)
70{
71 if (cpuidle_curr_driver)
72 return -EBUSY;
bf4d1b5d 73
82467a5a 74 cpuidle_curr_driver = drv;
bf4d1b5d 75
82467a5a 76 return 0;
bf4d1b5d
DL
77}
78
82467a5a 79static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv)
bf4d1b5d 80{
82467a5a
DL
81 if (drv == cpuidle_curr_driver)
82 cpuidle_curr_driver = NULL;
bf4d1b5d
DL
83}
84
82467a5a
DL
85#endif
86
87static void cpuidle_setup_broadcast_timer(void *arg)
bf4d1b5d 88{
82467a5a
DL
89 int cpu = smp_processor_id();
90 clockevents_notify((long)(arg), &cpu);
bf4d1b5d
DL
91}
92
82467a5a 93static int __cpuidle_driver_init(struct cpuidle_driver *drv)
bf4d1b5d 94{
82467a5a 95 int i;
bf4d1b5d 96
82467a5a 97 drv->refcnt = 0;
bf4d1b5d 98
82467a5a
DL
99 if (!drv->cpumask)
100 drv->cpumask = (struct cpumask *)cpu_possible_mask;
bf4d1b5d 101
82467a5a 102 for (i = drv->state_count - 1; i >= 0 ; i--) {
bf4d1b5d 103
82467a5a
DL
104 if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP))
105 continue;
106
107 drv->bctimer = 1;
108 break;
109 }
110
111 return 0;
bf4d1b5d
DL
112}
113
82467a5a 114static int __cpuidle_register_driver(struct cpuidle_driver *drv)
bf4d1b5d
DL
115{
116 int ret;
117
82467a5a
DL
118 if (!drv || !drv->state_count)
119 return -EINVAL;
bf4d1b5d 120
82467a5a
DL
121 if (cpuidle_disabled())
122 return -ENODEV;
bf4d1b5d 123
82467a5a
DL
124 ret = __cpuidle_driver_init(drv);
125 if (ret)
126 return ret;
ed953472 127
82467a5a
DL
128 ret = __cpuidle_set_driver(drv);
129 if (ret)
130 return ret;
13dd52f1 131
82467a5a
DL
132 if (drv->bctimer)
133 on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer,
134 (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1);
4f86d3a8 135
82467a5a 136 return 0;
4f86d3a8 137}
4f86d3a8 138
752138df 139/**
bf4d1b5d
DL
140 * cpuidle_unregister_driver - unregisters a driver
141 * @drv: the driver
752138df 142 */
82467a5a 143static void __cpuidle_unregister_driver(struct cpuidle_driver *drv)
bf4d1b5d 144{
82467a5a
DL
145 if (WARN_ON(drv->refcnt > 0))
146 return;
bf4d1b5d 147
82467a5a
DL
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 }
bf4d1b5d 153
82467a5a 154 __cpuidle_unset_driver(drv);
752138df 155}
bf4d1b5d
DL
156
157/**
158 * cpuidle_register_driver - registers a driver
159 * @drv: the driver
160 */
161int cpuidle_register_driver(struct cpuidle_driver *drv)
162{
82467a5a 163 int ret;
bf4d1b5d 164
bf4d1b5d 165 spin_lock(&cpuidle_driver_lock);
82467a5a 166 ret = __cpuidle_register_driver(drv);
bf4d1b5d 167 spin_unlock(&cpuidle_driver_lock);
bf4d1b5d
DL
168
169 return ret;
170}
171EXPORT_SYMBOL_GPL(cpuidle_register_driver);
752138df 172
4f86d3a8
LB
173/**
174 * cpuidle_unregister_driver - unregisters a driver
175 * @drv: the driver
176 */
177void cpuidle_unregister_driver(struct cpuidle_driver *drv)
178{
4f86d3a8 179 spin_lock(&cpuidle_driver_lock);
82467a5a 180 __cpuidle_unregister_driver(drv);
4f86d3a8
LB
181 spin_unlock(&cpuidle_driver_lock);
182}
4f86d3a8 183EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
bf4d1b5d
DL
184
185/**
186 * cpuidle_get_driver - return the current driver
187 */
188struct 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}
199EXPORT_SYMBOL_GPL(cpuidle_get_driver);
200
201/**
202 * cpuidle_get_cpu_driver - return the driver tied with a cpu
203 */
204struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev)
205{
bf4d1b5d
DL
206 if (!dev)
207 return NULL;
208
ac34d7c8 209 return __cpuidle_get_cpu_driver(dev->cpu);
bf4d1b5d
DL
210}
211EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
6e797a07
RW
212
213struct cpuidle_driver *cpuidle_driver_ref(void)
214{
215 struct cpuidle_driver *drv;
216
217 spin_lock(&cpuidle_driver_lock);
218
13dd52f1 219 drv = cpuidle_get_driver();
42f67f2a 220 drv->refcnt++;
6e797a07
RW
221
222 spin_unlock(&cpuidle_driver_lock);
223 return drv;
224}
225
226void cpuidle_driver_unref(void)
227{
13dd52f1 228 struct cpuidle_driver *drv = cpuidle_get_driver();
42f67f2a 229
6e797a07
RW
230 spin_lock(&cpuidle_driver_lock);
231
42f67f2a
DL
232 if (drv && !WARN_ON(drv->refcnt <= 0))
233 drv->refcnt--;
6e797a07
RW
234
235 spin_unlock(&cpuidle_driver_lock);
236}
This page took 4.752177 seconds and 5 git commands to generate.