blob: e75fa5472a919c4e8bb28d81eb9c18b2497ace3f [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>
Daniel Lezcanoa06df062013-03-27 10:22:10 +000014#include <linux/cpumask.h>
15#include <linux/clockchips.h>
Len Brown4f86d3a2007-10-03 18:58:00 -040016
17#include "cpuidle.h"
18
Len Brown4f86d3a2007-10-03 18:58:00 -040019DEFINE_SPINLOCK(cpuidle_driver_lock);
20
Daniel Lezcano82467a52013-06-07 21:53:09 +000021#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
22
23static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers);
24
25static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
26{
27 return per_cpu(cpuidle_drivers, cpu);
28}
29
30static 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
43static 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
62static struct cpuidle_driver *cpuidle_curr_driver;
63
64static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
65{
66 return cpuidle_curr_driver;
67}
68
69static 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
79static 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
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +000086
Daniel Lezcanoa06df062013-03-27 10:22:10 +000087static void cpuidle_setup_broadcast_timer(void *arg)
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000088{
Daniel Lezcanoa06df062013-03-27 10:22:10 +000089 int cpu = smp_processor_id();
90 clockevents_notify((long)(arg), &cpu);
91}
92
Daniel Lezcano82467a52013-06-07 21:53:09 +000093static int __cpuidle_driver_init(struct cpuidle_driver *drv)
Daniel Lezcanoa06df062013-03-27 10:22:10 +000094{
95 int i;
96
Daniel Lezcano13dd52f2012-10-31 16:44:47 +000097 drv->refcnt = 0;
Daniel Lezcanoa06df062013-03-27 10:22:10 +000098
Daniel Lezcano82467a52013-06-07 21:53:09 +000099 if (!drv->cpumask)
100 drv->cpumask = (struct cpumask *)cpu_possible_mask;
101
Daniel Lezcanoa06df062013-03-27 10:22:10 +0000102 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;
Daniel Lezcanoa06df062013-03-27 10:22:10 +0000108 break;
109 }
Daniel Lezcano82467a52013-06-07 21:53:09 +0000110
111 return 0;
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000112}
113
Daniel Lezcano82467a52013-06-07 21:53:09 +0000114static int __cpuidle_register_driver(struct cpuidle_driver *drv)
Len Brown4f86d3a2007-10-03 18:58:00 -0400115{
Daniel Lezcano82467a52013-06-07 21:53:09 +0000116 int ret;
117
Daniel Lezcanofc850f32012-03-26 14:51:26 +0200118 if (!drv || !drv->state_count)
Len Brown4f86d3a2007-10-03 18:58:00 -0400119 return -EINVAL;
120
Len Brown62027ae2011-04-01 18:13:10 -0400121 if (cpuidle_disabled())
122 return -ENODEV;
123
Daniel Lezcano82467a52013-06-07 21:53:09 +0000124 ret = __cpuidle_driver_init(drv);
125 if (ret)
126 return ret;
Daniel Lezcanoed953472012-09-22 00:38:32 +0200127
Daniel Lezcano82467a52013-06-07 21:53:09 +0000128 ret = __cpuidle_set_driver(drv);
129 if (ret)
130 return ret;
Daniel Lezcanoed953472012-09-22 00:38:32 +0200131
Daniel Lezcano82467a52013-06-07 21:53:09 +0000132 if (drv->bctimer)
133 on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer,
134 (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1);
Len Brown4f86d3a2007-10-03 18:58:00 -0400135
136 return 0;
137}
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000138
Daniel Lezcano82467a52013-06-07 21:53:09 +0000139/**
140 * cpuidle_unregister_driver - unregisters a driver
141 * @drv: the driver
142 */
143static void __cpuidle_unregister_driver(struct cpuidle_driver *drv)
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000144{
Daniel Lezcano82467a52013-06-07 21:53:09 +0000145 if (WARN_ON(drv->refcnt > 0))
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000146 return;
147
Daniel Lezcanoa06df062013-03-27 10:22:10 +0000148 if (drv->bctimer) {
149 drv->bctimer = 0;
Daniel Lezcano82467a52013-06-07 21:53:09 +0000150 on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer,
Daniel Lezcanoa06df062013-03-27 10:22:10 +0000151 (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1);
152 }
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000153
Daniel Lezcano82467a52013-06-07 21:53:09 +0000154 __cpuidle_unset_driver(drv);
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000155}
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;
164
165 spin_lock(&cpuidle_driver_lock);
Daniel Lezcano82467a52013-06-07 21:53:09 +0000166 ret = __cpuidle_register_driver(drv);
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000167 spin_unlock(&cpuidle_driver_lock);
168
169 return ret;
170}
Len Brown4f86d3a2007-10-03 18:58:00 -0400171EXPORT_SYMBOL_GPL(cpuidle_register_driver);
172
173/**
174 * cpuidle_unregister_driver - unregisters a driver
175 * @drv: the driver
176 */
177void cpuidle_unregister_driver(struct cpuidle_driver *drv)
178{
Len Brown4f86d3a2007-10-03 18:58:00 -0400179 spin_lock(&cpuidle_driver_lock);
Daniel Lezcano82467a52013-06-07 21:53:09 +0000180 __cpuidle_unregister_driver(drv);
Len Brown4f86d3a2007-10-03 18:58:00 -0400181 spin_unlock(&cpuidle_driver_lock);
182}
Len Brown4f86d3a2007-10-03 18:58:00 -0400183EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200184
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000185/**
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{
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000206 if (!dev)
207 return NULL;
208
Daniel Lezcanoac34d7c2013-01-03 13:03:18 +0100209 return __cpuidle_get_cpu_driver(dev->cpu);
Daniel Lezcanobf4d1b52012-10-31 16:44:48 +0000210}
211EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
212
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200213struct cpuidle_driver *cpuidle_driver_ref(void)
214{
215 struct cpuidle_driver *drv;
216
217 spin_lock(&cpuidle_driver_lock);
218
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000219 drv = cpuidle_get_driver();
Daniel Lezcano42f67f22012-10-31 16:44:45 +0000220 drv->refcnt++;
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200221
222 spin_unlock(&cpuidle_driver_lock);
223 return drv;
224}
225
226void cpuidle_driver_unref(void)
227{
Daniel Lezcano13dd52f2012-10-31 16:44:47 +0000228 struct cpuidle_driver *drv = cpuidle_get_driver();
Daniel Lezcano42f67f22012-10-31 16:44:45 +0000229
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200230 spin_lock(&cpuidle_driver_lock);
231
Daniel Lezcano42f67f22012-10-31 16:44:45 +0000232 if (drv && !WARN_ON(drv->refcnt <= 0))
233 drv->refcnt--;
Rafael J. Wysocki6e797a02012-06-16 15:20:11 +0200234
235 spin_unlock(&cpuidle_driver_lock);
236}