diff options
Diffstat (limited to 'drivers/cpuidle/driver.c')
-rw-r--r-- | drivers/cpuidle/driver.c | 209 |
1 files changed, 180 insertions, 29 deletions
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 87db3877fead..3af841fb397a 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c | |||
@@ -14,9 +14,10 @@ | |||
14 | 14 | ||
15 | #include "cpuidle.h" | 15 | #include "cpuidle.h" |
16 | 16 | ||
17 | static struct cpuidle_driver *cpuidle_curr_driver; | ||
18 | DEFINE_SPINLOCK(cpuidle_driver_lock); | 17 | DEFINE_SPINLOCK(cpuidle_driver_lock); |
19 | int cpuidle_driver_refcount; | 18 | |
19 | static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); | ||
20 | static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); | ||
20 | 21 | ||
21 | static void set_power_states(struct cpuidle_driver *drv) | 22 | static void set_power_states(struct cpuidle_driver *drv) |
22 | { | 23 | { |
@@ -40,11 +41,15 @@ static void set_power_states(struct cpuidle_driver *drv) | |||
40 | drv->states[i].power_usage = -1 - i; | 41 | drv->states[i].power_usage = -1 - i; |
41 | } | 42 | } |
42 | 43 | ||
43 | /** | 44 | static void __cpuidle_driver_init(struct cpuidle_driver *drv) |
44 | * cpuidle_register_driver - registers a driver | 45 | { |
45 | * @drv: the driver | 46 | drv->refcnt = 0; |
46 | */ | 47 | |
47 | int cpuidle_register_driver(struct cpuidle_driver *drv) | 48 | if (!drv->power_specified) |
49 | set_power_states(drv); | ||
50 | } | ||
51 | |||
52 | static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) | ||
48 | { | 53 | { |
49 | if (!drv || !drv->state_count) | 54 | if (!drv || !drv->state_count) |
50 | return -EINVAL; | 55 | return -EINVAL; |
@@ -52,31 +57,145 @@ int cpuidle_register_driver(struct cpuidle_driver *drv) | |||
52 | if (cpuidle_disabled()) | 57 | if (cpuidle_disabled()) |
53 | return -ENODEV; | 58 | return -ENODEV; |
54 | 59 | ||
55 | spin_lock(&cpuidle_driver_lock); | 60 | if (__cpuidle_get_cpu_driver(cpu)) |
56 | if (cpuidle_curr_driver) { | ||
57 | spin_unlock(&cpuidle_driver_lock); | ||
58 | return -EBUSY; | 61 | return -EBUSY; |
62 | |||
63 | __cpuidle_driver_init(drv); | ||
64 | |||
65 | __cpuidle_set_cpu_driver(drv, cpu); | ||
66 | |||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu) | ||
71 | { | ||
72 | if (drv != __cpuidle_get_cpu_driver(cpu)) | ||
73 | return; | ||
74 | |||
75 | if (!WARN_ON(drv->refcnt > 0)) | ||
76 | __cpuidle_set_cpu_driver(NULL, cpu); | ||
77 | } | ||
78 | |||
79 | #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS | ||
80 | |||
81 | static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); | ||
82 | |||
83 | static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) | ||
84 | { | ||
85 | per_cpu(cpuidle_drivers, cpu) = drv; | ||
86 | } | ||
87 | |||
88 | static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) | ||
89 | { | ||
90 | return per_cpu(cpuidle_drivers, cpu); | ||
91 | } | ||
92 | |||
93 | static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv) | ||
94 | { | ||
95 | int cpu; | ||
96 | for_each_present_cpu(cpu) | ||
97 | __cpuidle_unregister_driver(drv, cpu); | ||
98 | } | ||
99 | |||
100 | static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv) | ||
101 | { | ||
102 | int ret = 0; | ||
103 | int i, cpu; | ||
104 | |||
105 | for_each_present_cpu(cpu) { | ||
106 | ret = __cpuidle_register_driver(drv, cpu); | ||
107 | if (ret) | ||
108 | break; | ||
59 | } | 109 | } |
60 | 110 | ||
61 | if (!drv->power_specified) | 111 | if (ret) |
62 | set_power_states(drv); | 112 | for_each_present_cpu(i) { |
113 | if (i == cpu) | ||
114 | break; | ||
115 | __cpuidle_unregister_driver(drv, i); | ||
116 | } | ||
63 | 117 | ||
64 | cpuidle_curr_driver = drv; | ||
65 | 118 | ||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu) | ||
123 | { | ||
124 | int ret; | ||
125 | |||
126 | spin_lock(&cpuidle_driver_lock); | ||
127 | ret = __cpuidle_register_driver(drv, cpu); | ||
66 | spin_unlock(&cpuidle_driver_lock); | 128 | spin_unlock(&cpuidle_driver_lock); |
67 | 129 | ||
68 | return 0; | 130 | return ret; |
131 | } | ||
132 | |||
133 | void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu) | ||
134 | { | ||
135 | spin_lock(&cpuidle_driver_lock); | ||
136 | __cpuidle_unregister_driver(drv, cpu); | ||
137 | spin_unlock(&cpuidle_driver_lock); | ||
138 | } | ||
139 | |||
140 | /** | ||
141 | * cpuidle_register_driver - registers a driver | ||
142 | * @drv: the driver | ||
143 | */ | ||
144 | int cpuidle_register_driver(struct cpuidle_driver *drv) | ||
145 | { | ||
146 | int ret; | ||
147 | |||
148 | spin_lock(&cpuidle_driver_lock); | ||
149 | ret = __cpuidle_register_all_cpu_driver(drv); | ||
150 | spin_unlock(&cpuidle_driver_lock); | ||
151 | |||
152 | return ret; | ||
69 | } | 153 | } |
70 | EXPORT_SYMBOL_GPL(cpuidle_register_driver); | 154 | EXPORT_SYMBOL_GPL(cpuidle_register_driver); |
71 | 155 | ||
72 | /** | 156 | /** |
73 | * cpuidle_get_driver - return the current driver | 157 | * cpuidle_unregister_driver - unregisters a driver |
158 | * @drv: the driver | ||
74 | */ | 159 | */ |
75 | struct cpuidle_driver *cpuidle_get_driver(void) | 160 | void cpuidle_unregister_driver(struct cpuidle_driver *drv) |
161 | { | ||
162 | spin_lock(&cpuidle_driver_lock); | ||
163 | __cpuidle_unregister_all_cpu_driver(drv); | ||
164 | spin_unlock(&cpuidle_driver_lock); | ||
165 | } | ||
166 | EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); | ||
167 | |||
168 | #else | ||
169 | |||
170 | static struct cpuidle_driver *cpuidle_curr_driver; | ||
171 | |||
172 | static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) | ||
173 | { | ||
174 | cpuidle_curr_driver = drv; | ||
175 | } | ||
176 | |||
177 | static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) | ||
76 | { | 178 | { |
77 | return cpuidle_curr_driver; | 179 | return cpuidle_curr_driver; |
78 | } | 180 | } |
79 | EXPORT_SYMBOL_GPL(cpuidle_get_driver); | 181 | |
182 | /** | ||
183 | * cpuidle_register_driver - registers a driver | ||
184 | * @drv: the driver | ||
185 | */ | ||
186 | int cpuidle_register_driver(struct cpuidle_driver *drv) | ||
187 | { | ||
188 | int ret, cpu; | ||
189 | |||
190 | cpu = get_cpu(); | ||
191 | spin_lock(&cpuidle_driver_lock); | ||
192 | ret = __cpuidle_register_driver(drv, cpu); | ||
193 | spin_unlock(&cpuidle_driver_lock); | ||
194 | put_cpu(); | ||
195 | |||
196 | return ret; | ||
197 | } | ||
198 | EXPORT_SYMBOL_GPL(cpuidle_register_driver); | ||
80 | 199 | ||
81 | /** | 200 | /** |
82 | * cpuidle_unregister_driver - unregisters a driver | 201 | * cpuidle_unregister_driver - unregisters a driver |
@@ -84,20 +203,50 @@ EXPORT_SYMBOL_GPL(cpuidle_get_driver); | |||
84 | */ | 203 | */ |
85 | void cpuidle_unregister_driver(struct cpuidle_driver *drv) | 204 | void cpuidle_unregister_driver(struct cpuidle_driver *drv) |
86 | { | 205 | { |
87 | if (drv != cpuidle_curr_driver) { | 206 | int cpu; |
88 | WARN(1, "invalid cpuidle_unregister_driver(%s)\n", | ||
89 | drv->name); | ||
90 | return; | ||
91 | } | ||
92 | 207 | ||
208 | cpu = get_cpu(); | ||
93 | spin_lock(&cpuidle_driver_lock); | 209 | spin_lock(&cpuidle_driver_lock); |
210 | __cpuidle_unregister_driver(drv, cpu); | ||
211 | spin_unlock(&cpuidle_driver_lock); | ||
212 | put_cpu(); | ||
213 | } | ||
214 | EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); | ||
215 | #endif | ||
216 | |||
217 | /** | ||
218 | * cpuidle_get_driver - return the current driver | ||
219 | */ | ||
220 | struct cpuidle_driver *cpuidle_get_driver(void) | ||
221 | { | ||
222 | struct cpuidle_driver *drv; | ||
223 | int cpu; | ||
94 | 224 | ||
95 | if (!WARN_ON(cpuidle_driver_refcount > 0)) | 225 | cpu = get_cpu(); |
96 | cpuidle_curr_driver = NULL; | 226 | drv = __cpuidle_get_cpu_driver(cpu); |
227 | put_cpu(); | ||
97 | 228 | ||
229 | return drv; | ||
230 | } | ||
231 | EXPORT_SYMBOL_GPL(cpuidle_get_driver); | ||
232 | |||
233 | /** | ||
234 | * cpuidle_get_cpu_driver - return the driver tied with a cpu | ||
235 | */ | ||
236 | struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) | ||
237 | { | ||
238 | struct cpuidle_driver *drv; | ||
239 | |||
240 | if (!dev) | ||
241 | return NULL; | ||
242 | |||
243 | spin_lock(&cpuidle_driver_lock); | ||
244 | drv = __cpuidle_get_cpu_driver(dev->cpu); | ||
98 | spin_unlock(&cpuidle_driver_lock); | 245 | spin_unlock(&cpuidle_driver_lock); |
246 | |||
247 | return drv; | ||
99 | } | 248 | } |
100 | EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); | 249 | EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver); |
101 | 250 | ||
102 | struct cpuidle_driver *cpuidle_driver_ref(void) | 251 | struct cpuidle_driver *cpuidle_driver_ref(void) |
103 | { | 252 | { |
@@ -105,8 +254,8 @@ struct cpuidle_driver *cpuidle_driver_ref(void) | |||
105 | 254 | ||
106 | spin_lock(&cpuidle_driver_lock); | 255 | spin_lock(&cpuidle_driver_lock); |
107 | 256 | ||
108 | drv = cpuidle_curr_driver; | 257 | drv = cpuidle_get_driver(); |
109 | cpuidle_driver_refcount++; | 258 | drv->refcnt++; |
110 | 259 | ||
111 | spin_unlock(&cpuidle_driver_lock); | 260 | spin_unlock(&cpuidle_driver_lock); |
112 | return drv; | 261 | return drv; |
@@ -114,10 +263,12 @@ struct cpuidle_driver *cpuidle_driver_ref(void) | |||
114 | 263 | ||
115 | void cpuidle_driver_unref(void) | 264 | void cpuidle_driver_unref(void) |
116 | { | 265 | { |
266 | struct cpuidle_driver *drv = cpuidle_get_driver(); | ||
267 | |||
117 | spin_lock(&cpuidle_driver_lock); | 268 | spin_lock(&cpuidle_driver_lock); |
118 | 269 | ||
119 | if (!WARN_ON(cpuidle_driver_refcount <= 0)) | 270 | if (drv && !WARN_ON(drv->refcnt <= 0)) |
120 | cpuidle_driver_refcount--; | 271 | drv->refcnt--; |
121 | 272 | ||
122 | spin_unlock(&cpuidle_driver_lock); | 273 | spin_unlock(&cpuidle_driver_lock); |
123 | } | 274 | } |