blob: 422c7b69ba7c50e728fa3af5af346a85a553af8f [file] [log] [blame]
Len Brown4f86d3a2007-10-03 18:58:00 -04001/*
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
Len Brown4f86d3a2007-10-03 18:58:00 -040017DEFINE_SPINLOCK(cpuidle_driver_lock);
18
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000019static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
20static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
21
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000022static void __cpuidle_driver_init(struct cpuidle_driver *drv)
23{
24 drv->refcnt = 0;
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000025}
26
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000027static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
Len Brown4f86d3a2007-10-03 18:58:00 -040028{
Daniel Lezcanofc850f32012-03-26 14:51:26 +020029 if (!drv || !drv->state_count)
Len Brown4f86d3a2007-10-03 18:58:00 -040030 return -EINVAL;
31
Len Brown62027ae2011-04-01 18:13:10 -040032 if (cpuidle_disabled())
33 return -ENODEV;
34
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000035 if (__cpuidle_get_cpu_driver(cpu))
Len Brown4f86d3a2007-10-03 18:58:00 -040036 return -EBUSY;
Daniel Lezcanoed953472012-09-22 00:38:32 +020037
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000038 __cpuidle_driver_init(drv);
Daniel Lezcanoed953472012-09-22 00:38:32 +020039
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000040 __cpuidle_set_cpu_driver(drv, cpu);
Len Brown4f86d3a2007-10-03 18:58:00 -040041
42 return 0;
43}
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000044
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000045static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu)
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000046{
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000047 if (drv != __cpuidle_get_cpu_driver(cpu))
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000048 return;
49
50 if (!WARN_ON(drv->refcnt > 0))
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000051 __cpuidle_set_cpu_driver(NULL, cpu);
52}
53
54#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
55
56static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers);
57
58static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
59{
60 per_cpu(cpuidle_drivers, cpu) = drv;
61}
62
63static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
64{
65 return per_cpu(cpuidle_drivers, cpu);
66}
67
68static 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
75static 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
97int 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
108void 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);
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000113}
114
115/**
116 * cpuidle_register_driver - registers a driver
117 * @drv: the driver
118 */
119int cpuidle_register_driver(struct cpuidle_driver *drv)
120{
121 int ret;
122
123 spin_lock(&cpuidle_driver_lock);
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000124 ret = __cpuidle_register_all_cpu_driver(drv);
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000125 spin_unlock(&cpuidle_driver_lock);
126
127 return ret;
128}
Len Brown4f86d3a2007-10-03 18:58:00 -0400129EXPORT_SYMBOL_GPL(cpuidle_register_driver);
130
131/**
132 * cpuidle_unregister_driver - unregisters a driver
133 * @drv: the driver
134 */
135void cpuidle_unregister_driver(struct cpuidle_driver *drv)
136{
Len Brown4f86d3a2007-10-03 18:58:00 -0400137 spin_lock(&cpuidle_driver_lock);
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000138 __cpuidle_unregister_all_cpu_driver(drv);
Len Brown4f86d3a2007-10-03 18:58:00 -0400139 spin_unlock(&cpuidle_driver_lock);
140}
Len Brown4f86d3a2007-10-03 18:58:00 -0400141EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200142
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000143#else
144
145static struct cpuidle_driver *cpuidle_curr_driver;
146
147static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
148{
149 cpuidle_curr_driver = drv;
150}
151
152static 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 */
161int 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}
173EXPORT_SYMBOL_GPL(cpuidle_register_driver);
174
175/**
176 * cpuidle_unregister_driver - unregisters a driver
177 * @drv: the driver
178 */
179void 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}
189EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
190#endif
191
192/**
193 * cpuidle_get_driver - return the current driver
194 */
195struct 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}
206EXPORT_SYMBOL_GPL(cpuidle_get_driver);
207
208/**
209 * cpuidle_get_cpu_driver - return the driver tied with a cpu
210 */
211struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev)
212{
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000213 if (!dev)
214 return NULL;
215
Daniel Lezcanoac34d7c2013-01-03 13:03:18 +0100216 return __cpuidle_get_cpu_driver(dev->cpu);
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000217}
218EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
219
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200220struct cpuidle_driver *cpuidle_driver_ref(void)
221{
222 struct cpuidle_driver *drv;
223
224 spin_lock(&cpuidle_driver_lock);
225
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000226 drv = cpuidle_get_driver();
Daniel Lezcano42f67f22012-10-31 16:44:45 +0000227 drv->refcnt++;
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200228
229 spin_unlock(&cpuidle_driver_lock);
230 return drv;
231}
232
233void cpuidle_driver_unref(void)
234{
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000235 struct cpuidle_driver *drv = cpuidle_get_driver();
Daniel Lezcano42f67f22012-10-31 16:44:45 +0000236
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200237 spin_lock(&cpuidle_driver_lock);
238
Daniel Lezcano42f67f22012-10-31 16:44:45 +0000239 if (drv && !WARN_ON(drv->refcnt <= 0))
240 drv->refcnt--;
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200241
242 spin_unlock(&cpuidle_driver_lock);
243}