diff options
Diffstat (limited to 'arch/arm/mach-omap2/clockdomain.c')
-rw-r--r-- | arch/arm/mach-omap2/clockdomain.c | 569 |
1 files changed, 378 insertions, 191 deletions
diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c index 7faf82d4e85c..2da3b5ec010c 100644 --- a/arch/arm/mach-omap2/clockdomain.c +++ b/arch/arm/mach-omap2/clockdomain.c | |||
@@ -92,8 +92,6 @@ static int _clkdm_register(struct clockdomain *clkdm) | |||
92 | 92 | ||
93 | pwrdm_add_clkdm(pwrdm, clkdm); | 93 | pwrdm_add_clkdm(pwrdm, clkdm); |
94 | 94 | ||
95 | spin_lock_init(&clkdm->lock); | ||
96 | |||
97 | pr_debug("clockdomain: registered %s\n", clkdm->name); | 95 | pr_debug("clockdomain: registered %s\n", clkdm->name); |
98 | 96 | ||
99 | return 0; | 97 | return 0; |
@@ -122,7 +120,7 @@ static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm, | |||
122 | return cd; | 120 | return cd; |
123 | } | 121 | } |
124 | 122 | ||
125 | /* | 123 | /** |
126 | * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store | 124 | * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store |
127 | * @autodep: struct clkdm_autodep * to resolve | 125 | * @autodep: struct clkdm_autodep * to resolve |
128 | * | 126 | * |
@@ -154,88 +152,206 @@ static void _autodep_lookup(struct clkdm_autodep *autodep) | |||
154 | autodep->clkdm.ptr = clkdm; | 152 | autodep->clkdm.ptr = clkdm; |
155 | } | 153 | } |
156 | 154 | ||
157 | /* | 155 | /** |
158 | * _clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable | 156 | * _resolve_clkdm_deps() - resolve clkdm_names in @clkdm_deps to clkdms |
159 | * @clkdm: struct clockdomain * | 157 | * @clkdm: clockdomain that we are resolving dependencies for |
158 | * @clkdm_deps: ptr to array of struct clkdm_deps to resolve | ||
160 | * | 159 | * |
161 | * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm' | 160 | * Iterates through @clkdm_deps, looking up the struct clockdomain named by |
162 | * in hardware-supervised mode. Meant to be called from clock framework | 161 | * clkdm_name and storing the clockdomain pointer in the struct clkdm_dep. |
163 | * when a clock inside clockdomain 'clkdm' is enabled. No return value. | 162 | * No return value. |
163 | */ | ||
164 | static void _resolve_clkdm_deps(struct clockdomain *clkdm, | ||
165 | struct clkdm_dep *clkdm_deps) | ||
166 | { | ||
167 | struct clkdm_dep *cd; | ||
168 | |||
169 | for (cd = clkdm_deps; cd && cd->clkdm_name; cd++) { | ||
170 | if (cd->clkdm) | ||
171 | continue; | ||
172 | cd->clkdm = _clkdm_lookup(cd->clkdm_name); | ||
173 | |||
174 | WARN(!cd->clkdm, "clockdomain: %s: could not find clkdm %s while resolving dependencies - should never happen", | ||
175 | clkdm->name, cd->clkdm_name); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * _clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1 (lockless) | ||
181 | * @clkdm1: wake this struct clockdomain * up (dependent) | ||
182 | * @clkdm2: when this struct clockdomain * wakes up (source) | ||
164 | * | 183 | * |
165 | * XXX autodeps are deprecated and should be removed at the earliest | 184 | * When the clockdomain represented by @clkdm2 wakes up, wake up |
166 | * opportunity | 185 | * @clkdm1. Implemented in hardware on the OMAP, this feature is |
186 | * designed to reduce wakeup latency of the dependent clockdomain @clkdm1. | ||
187 | * Returns -EINVAL if presented with invalid clockdomain pointers, | ||
188 | * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon | ||
189 | * success. | ||
167 | */ | 190 | */ |
168 | void _clkdm_add_autodeps(struct clockdomain *clkdm) | 191 | static int _clkdm_add_wkdep(struct clockdomain *clkdm1, |
192 | struct clockdomain *clkdm2) | ||
169 | { | 193 | { |
170 | struct clkdm_autodep *autodep; | 194 | struct clkdm_dep *cd; |
195 | int ret = 0; | ||
171 | 196 | ||
172 | if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS) | 197 | if (!clkdm1 || !clkdm2) |
173 | return; | 198 | return -EINVAL; |
174 | 199 | ||
175 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { | 200 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); |
176 | if (IS_ERR(autodep->clkdm.ptr)) | 201 | if (IS_ERR(cd)) |
177 | continue; | 202 | ret = PTR_ERR(cd); |
178 | 203 | ||
179 | pr_debug("clockdomain: %s: adding %s sleepdep/wkdep\n", | 204 | if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep) |
180 | clkdm->name, autodep->clkdm.ptr->name); | 205 | ret = -EINVAL; |
206 | |||
207 | if (ret) { | ||
208 | pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n", | ||
209 | clkdm1->name, clkdm2->name); | ||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | cd->wkdep_usecount++; | ||
214 | if (cd->wkdep_usecount == 1) { | ||
215 | pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n", | ||
216 | clkdm1->name, clkdm2->name); | ||
181 | 217 | ||
182 | clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr); | 218 | ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2); |
183 | clkdm_add_wkdep(clkdm, autodep->clkdm.ptr); | ||
184 | } | 219 | } |
220 | |||
221 | return ret; | ||
185 | } | 222 | } |
186 | 223 | ||
187 | /* | 224 | /** |
188 | * _clkdm_add_autodeps - remove auto sleepdeps/wkdeps from clkdm | 225 | * _clkdm_del_wkdep - remove a wakeup dep from clkdm2 to clkdm1 (lockless) |
189 | * @clkdm: struct clockdomain * | 226 | * @clkdm1: wake this struct clockdomain * up (dependent) |
227 | * @clkdm2: when this struct clockdomain * wakes up (source) | ||
190 | * | 228 | * |
191 | * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm' | 229 | * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2 |
192 | * in hardware-supervised mode. Meant to be called from clock framework | 230 | * wakes up. Returns -EINVAL if presented with invalid clockdomain |
193 | * when a clock inside clockdomain 'clkdm' is disabled. No return value. | 231 | * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or |
232 | * 0 upon success. | ||
233 | */ | ||
234 | static int _clkdm_del_wkdep(struct clockdomain *clkdm1, | ||
235 | struct clockdomain *clkdm2) | ||
236 | { | ||
237 | struct clkdm_dep *cd; | ||
238 | int ret = 0; | ||
239 | |||
240 | if (!clkdm1 || !clkdm2) | ||
241 | return -EINVAL; | ||
242 | |||
243 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | ||
244 | if (IS_ERR(cd)) | ||
245 | ret = PTR_ERR(cd); | ||
246 | |||
247 | if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep) | ||
248 | ret = -EINVAL; | ||
249 | |||
250 | if (ret) { | ||
251 | pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n", | ||
252 | clkdm1->name, clkdm2->name); | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | cd->wkdep_usecount--; | ||
257 | if (cd->wkdep_usecount == 0) { | ||
258 | pr_debug("clockdomain: hardware will no longer wake up %s after %s wakes up\n", | ||
259 | clkdm1->name, clkdm2->name); | ||
260 | |||
261 | ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2); | ||
262 | } | ||
263 | |||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | /** | ||
268 | * _clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 (lockless) | ||
269 | * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) | ||
270 | * @clkdm2: when this struct clockdomain * is active (source) | ||
194 | * | 271 | * |
195 | * XXX autodeps are deprecated and should be removed at the earliest | 272 | * Prevent @clkdm1 from automatically going inactive (and then to |
196 | * opportunity | 273 | * retention or off) if @clkdm2 is active. Returns -EINVAL if |
274 | * presented with invalid clockdomain pointers or called on a machine | ||
275 | * that does not support software-configurable hardware sleep | ||
276 | * dependencies, -ENOENT if the specified dependency cannot be set in | ||
277 | * hardware, or 0 upon success. | ||
197 | */ | 278 | */ |
198 | void _clkdm_del_autodeps(struct clockdomain *clkdm) | 279 | static int _clkdm_add_sleepdep(struct clockdomain *clkdm1, |
280 | struct clockdomain *clkdm2) | ||
199 | { | 281 | { |
200 | struct clkdm_autodep *autodep; | 282 | struct clkdm_dep *cd; |
283 | int ret = 0; | ||
201 | 284 | ||
202 | if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS) | 285 | if (!clkdm1 || !clkdm2) |
203 | return; | 286 | return -EINVAL; |
204 | 287 | ||
205 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { | 288 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); |
206 | if (IS_ERR(autodep->clkdm.ptr)) | 289 | if (IS_ERR(cd)) |
207 | continue; | 290 | ret = PTR_ERR(cd); |
208 | 291 | ||
209 | pr_debug("clockdomain: %s: removing %s sleepdep/wkdep\n", | 292 | if (!arch_clkdm || !arch_clkdm->clkdm_add_sleepdep) |
210 | clkdm->name, autodep->clkdm.ptr->name); | 293 | ret = -EINVAL; |
211 | 294 | ||
212 | clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr); | 295 | if (ret) { |
213 | clkdm_del_wkdep(clkdm, autodep->clkdm.ptr); | 296 | pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n", |
297 | clkdm1->name, clkdm2->name); | ||
298 | return ret; | ||
299 | } | ||
300 | |||
301 | cd->sleepdep_usecount++; | ||
302 | if (cd->sleepdep_usecount == 1) { | ||
303 | pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n", | ||
304 | clkdm1->name, clkdm2->name); | ||
305 | |||
306 | ret = arch_clkdm->clkdm_add_sleepdep(clkdm1, clkdm2); | ||
214 | } | 307 | } |
308 | |||
309 | return ret; | ||
215 | } | 310 | } |
216 | 311 | ||
217 | /** | 312 | /** |
218 | * _resolve_clkdm_deps() - resolve clkdm_names in @clkdm_deps to clkdms | 313 | * _clkdm_del_sleepdep - remove a sleep dep from clkdm2 to clkdm1 (lockless) |
219 | * @clkdm: clockdomain that we are resolving dependencies for | 314 | * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) |
220 | * @clkdm_deps: ptr to array of struct clkdm_deps to resolve | 315 | * @clkdm2: when this struct clockdomain * is active (source) |
221 | * | 316 | * |
222 | * Iterates through @clkdm_deps, looking up the struct clockdomain named by | 317 | * Allow @clkdm1 to automatically go inactive (and then to retention or |
223 | * clkdm_name and storing the clockdomain pointer in the struct clkdm_dep. | 318 | * off), independent of the activity state of @clkdm2. Returns -EINVAL |
224 | * No return value. | 319 | * if presented with invalid clockdomain pointers or called on a machine |
320 | * that does not support software-configurable hardware sleep dependencies, | ||
321 | * -ENOENT if the specified dependency cannot be cleared in hardware, or | ||
322 | * 0 upon success. | ||
225 | */ | 323 | */ |
226 | static void _resolve_clkdm_deps(struct clockdomain *clkdm, | 324 | static int _clkdm_del_sleepdep(struct clockdomain *clkdm1, |
227 | struct clkdm_dep *clkdm_deps) | 325 | struct clockdomain *clkdm2) |
228 | { | 326 | { |
229 | struct clkdm_dep *cd; | 327 | struct clkdm_dep *cd; |
328 | int ret = 0; | ||
230 | 329 | ||
231 | for (cd = clkdm_deps; cd && cd->clkdm_name; cd++) { | 330 | if (!clkdm1 || !clkdm2) |
232 | if (cd->clkdm) | 331 | return -EINVAL; |
233 | continue; | ||
234 | cd->clkdm = _clkdm_lookup(cd->clkdm_name); | ||
235 | 332 | ||
236 | WARN(!cd->clkdm, "clockdomain: %s: could not find clkdm %s while resolving dependencies - should never happen", | 333 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); |
237 | clkdm->name, cd->clkdm_name); | 334 | if (IS_ERR(cd)) |
335 | ret = PTR_ERR(cd); | ||
336 | |||
337 | if (!arch_clkdm || !arch_clkdm->clkdm_del_sleepdep) | ||
338 | ret = -EINVAL; | ||
339 | |||
340 | if (ret) { | ||
341 | pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n", | ||
342 | clkdm1->name, clkdm2->name); | ||
343 | return ret; | ||
238 | } | 344 | } |
345 | |||
346 | cd->sleepdep_usecount--; | ||
347 | if (cd->sleepdep_usecount == 0) { | ||
348 | pr_debug("clockdomain: will no longer prevent %s from sleeping if %s is active\n", | ||
349 | clkdm1->name, clkdm2->name); | ||
350 | |||
351 | ret = arch_clkdm->clkdm_del_sleepdep(clkdm1, clkdm2); | ||
352 | } | ||
353 | |||
354 | return ret; | ||
239 | } | 355 | } |
240 | 356 | ||
241 | /* Public functions */ | 357 | /* Public functions */ |
@@ -456,30 +572,18 @@ struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm) | |||
456 | int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | 572 | int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) |
457 | { | 573 | { |
458 | struct clkdm_dep *cd; | 574 | struct clkdm_dep *cd; |
459 | int ret = 0; | 575 | int ret; |
460 | 576 | ||
461 | if (!clkdm1 || !clkdm2) | 577 | if (!clkdm1 || !clkdm2) |
462 | return -EINVAL; | 578 | return -EINVAL; |
463 | 579 | ||
464 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | 580 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); |
465 | if (IS_ERR(cd)) | 581 | if (IS_ERR(cd)) |
466 | ret = PTR_ERR(cd); | 582 | return PTR_ERR(cd); |
467 | 583 | ||
468 | if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep) | 584 | pwrdm_lock(cd->clkdm->pwrdm.ptr); |
469 | ret = -EINVAL; | 585 | ret = _clkdm_add_wkdep(clkdm1, clkdm2); |
470 | 586 | pwrdm_unlock(cd->clkdm->pwrdm.ptr); | |
471 | if (ret) { | ||
472 | pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n", | ||
473 | clkdm1->name, clkdm2->name); | ||
474 | return ret; | ||
475 | } | ||
476 | |||
477 | if (atomic_inc_return(&cd->wkdep_usecount) == 1) { | ||
478 | pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n", | ||
479 | clkdm1->name, clkdm2->name); | ||
480 | |||
481 | ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2); | ||
482 | } | ||
483 | 587 | ||
484 | return ret; | 588 | return ret; |
485 | } | 589 | } |
@@ -497,30 +601,18 @@ int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | |||
497 | int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | 601 | int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) |
498 | { | 602 | { |
499 | struct clkdm_dep *cd; | 603 | struct clkdm_dep *cd; |
500 | int ret = 0; | 604 | int ret; |
501 | 605 | ||
502 | if (!clkdm1 || !clkdm2) | 606 | if (!clkdm1 || !clkdm2) |
503 | return -EINVAL; | 607 | return -EINVAL; |
504 | 608 | ||
505 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | 609 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); |
506 | if (IS_ERR(cd)) | 610 | if (IS_ERR(cd)) |
507 | ret = PTR_ERR(cd); | 611 | return PTR_ERR(cd); |
508 | 612 | ||
509 | if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep) | 613 | pwrdm_lock(cd->clkdm->pwrdm.ptr); |
510 | ret = -EINVAL; | 614 | ret = _clkdm_del_wkdep(clkdm1, clkdm2); |
511 | 615 | pwrdm_unlock(cd->clkdm->pwrdm.ptr); | |
512 | if (ret) { | ||
513 | pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n", | ||
514 | clkdm1->name, clkdm2->name); | ||
515 | return ret; | ||
516 | } | ||
517 | |||
518 | if (atomic_dec_return(&cd->wkdep_usecount) == 0) { | ||
519 | pr_debug("clockdomain: hardware will no longer wake up %s after %s wakes up\n", | ||
520 | clkdm1->name, clkdm2->name); | ||
521 | |||
522 | ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2); | ||
523 | } | ||
524 | 616 | ||
525 | return ret; | 617 | return ret; |
526 | } | 618 | } |
@@ -560,7 +652,7 @@ int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | |||
560 | return ret; | 652 | return ret; |
561 | } | 653 | } |
562 | 654 | ||
563 | /* XXX It's faster to return the atomic wkdep_usecount */ | 655 | /* XXX It's faster to return the wkdep_usecount */ |
564 | return arch_clkdm->clkdm_read_wkdep(clkdm1, clkdm2); | 656 | return arch_clkdm->clkdm_read_wkdep(clkdm1, clkdm2); |
565 | } | 657 | } |
566 | 658 | ||
@@ -600,30 +692,18 @@ int clkdm_clear_all_wkdeps(struct clockdomain *clkdm) | |||
600 | int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | 692 | int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) |
601 | { | 693 | { |
602 | struct clkdm_dep *cd; | 694 | struct clkdm_dep *cd; |
603 | int ret = 0; | 695 | int ret; |
604 | 696 | ||
605 | if (!clkdm1 || !clkdm2) | 697 | if (!clkdm1 || !clkdm2) |
606 | return -EINVAL; | 698 | return -EINVAL; |
607 | 699 | ||
608 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); | 700 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); |
609 | if (IS_ERR(cd)) | 701 | if (IS_ERR(cd)) |
610 | ret = PTR_ERR(cd); | 702 | return PTR_ERR(cd); |
611 | 703 | ||
612 | if (!arch_clkdm || !arch_clkdm->clkdm_add_sleepdep) | 704 | pwrdm_lock(cd->clkdm->pwrdm.ptr); |
613 | ret = -EINVAL; | 705 | ret = _clkdm_add_sleepdep(clkdm1, clkdm2); |
614 | 706 | pwrdm_unlock(cd->clkdm->pwrdm.ptr); | |
615 | if (ret) { | ||
616 | pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n", | ||
617 | clkdm1->name, clkdm2->name); | ||
618 | return ret; | ||
619 | } | ||
620 | |||
621 | if (atomic_inc_return(&cd->sleepdep_usecount) == 1) { | ||
622 | pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n", | ||
623 | clkdm1->name, clkdm2->name); | ||
624 | |||
625 | ret = arch_clkdm->clkdm_add_sleepdep(clkdm1, clkdm2); | ||
626 | } | ||
627 | 707 | ||
628 | return ret; | 708 | return ret; |
629 | } | 709 | } |
@@ -643,30 +723,18 @@ int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | |||
643 | int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | 723 | int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) |
644 | { | 724 | { |
645 | struct clkdm_dep *cd; | 725 | struct clkdm_dep *cd; |
646 | int ret = 0; | 726 | int ret; |
647 | 727 | ||
648 | if (!clkdm1 || !clkdm2) | 728 | if (!clkdm1 || !clkdm2) |
649 | return -EINVAL; | 729 | return -EINVAL; |
650 | 730 | ||
651 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); | 731 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); |
652 | if (IS_ERR(cd)) | 732 | if (IS_ERR(cd)) |
653 | ret = PTR_ERR(cd); | 733 | return PTR_ERR(cd); |
654 | 734 | ||
655 | if (!arch_clkdm || !arch_clkdm->clkdm_del_sleepdep) | 735 | pwrdm_lock(cd->clkdm->pwrdm.ptr); |
656 | ret = -EINVAL; | 736 | ret = _clkdm_del_sleepdep(clkdm1, clkdm2); |
657 | 737 | pwrdm_unlock(cd->clkdm->pwrdm.ptr); | |
658 | if (ret) { | ||
659 | pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n", | ||
660 | clkdm1->name, clkdm2->name); | ||
661 | return ret; | ||
662 | } | ||
663 | |||
664 | if (atomic_dec_return(&cd->sleepdep_usecount) == 0) { | ||
665 | pr_debug("clockdomain: will no longer prevent %s from sleeping if %s is active\n", | ||
666 | clkdm1->name, clkdm2->name); | ||
667 | |||
668 | ret = arch_clkdm->clkdm_del_sleepdep(clkdm1, clkdm2); | ||
669 | } | ||
670 | 738 | ||
671 | return ret; | 739 | return ret; |
672 | } | 740 | } |
@@ -708,7 +776,7 @@ int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | |||
708 | return ret; | 776 | return ret; |
709 | } | 777 | } |
710 | 778 | ||
711 | /* XXX It's faster to return the atomic sleepdep_usecount */ | 779 | /* XXX It's faster to return the sleepdep_usecount */ |
712 | return arch_clkdm->clkdm_read_sleepdep(clkdm1, clkdm2); | 780 | return arch_clkdm->clkdm_read_sleepdep(clkdm1, clkdm2); |
713 | } | 781 | } |
714 | 782 | ||
@@ -734,18 +802,17 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm) | |||
734 | } | 802 | } |
735 | 803 | ||
736 | /** | 804 | /** |
737 | * clkdm_sleep - force clockdomain sleep transition | 805 | * clkdm_sleep_nolock - force clockdomain sleep transition (lockless) |
738 | * @clkdm: struct clockdomain * | 806 | * @clkdm: struct clockdomain * |
739 | * | 807 | * |
740 | * Instruct the CM to force a sleep transition on the specified | 808 | * Instruct the CM to force a sleep transition on the specified |
741 | * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if | 809 | * clockdomain @clkdm. Only for use by the powerdomain code. Returns |
742 | * clockdomain does not support software-initiated sleep; 0 upon | 810 | * -EINVAL if @clkdm is NULL or if clockdomain does not support |
743 | * success. | 811 | * software-initiated sleep; 0 upon success. |
744 | */ | 812 | */ |
745 | int clkdm_sleep(struct clockdomain *clkdm) | 813 | int clkdm_sleep_nolock(struct clockdomain *clkdm) |
746 | { | 814 | { |
747 | int ret; | 815 | int ret; |
748 | unsigned long flags; | ||
749 | 816 | ||
750 | if (!clkdm) | 817 | if (!clkdm) |
751 | return -EINVAL; | 818 | return -EINVAL; |
@@ -761,26 +828,45 @@ int clkdm_sleep(struct clockdomain *clkdm) | |||
761 | 828 | ||
762 | pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name); | 829 | pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name); |
763 | 830 | ||
764 | spin_lock_irqsave(&clkdm->lock, flags); | ||
765 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; | 831 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; |
766 | ret = arch_clkdm->clkdm_sleep(clkdm); | 832 | ret = arch_clkdm->clkdm_sleep(clkdm); |
767 | spin_unlock_irqrestore(&clkdm->lock, flags); | 833 | ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); |
834 | |||
768 | return ret; | 835 | return ret; |
769 | } | 836 | } |
770 | 837 | ||
771 | /** | 838 | /** |
772 | * clkdm_wakeup - force clockdomain wakeup transition | 839 | * clkdm_sleep - force clockdomain sleep transition |
773 | * @clkdm: struct clockdomain * | 840 | * @clkdm: struct clockdomain * |
774 | * | 841 | * |
775 | * Instruct the CM to force a wakeup transition on the specified | 842 | * Instruct the CM to force a sleep transition on the specified |
776 | * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if the | 843 | * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if |
777 | * clockdomain does not support software-controlled wakeup; 0 upon | 844 | * clockdomain does not support software-initiated sleep; 0 upon |
778 | * success. | 845 | * success. |
779 | */ | 846 | */ |
780 | int clkdm_wakeup(struct clockdomain *clkdm) | 847 | int clkdm_sleep(struct clockdomain *clkdm) |
848 | { | ||
849 | int ret; | ||
850 | |||
851 | pwrdm_lock(clkdm->pwrdm.ptr); | ||
852 | ret = clkdm_sleep_nolock(clkdm); | ||
853 | pwrdm_unlock(clkdm->pwrdm.ptr); | ||
854 | |||
855 | return ret; | ||
856 | } | ||
857 | |||
858 | /** | ||
859 | * clkdm_wakeup_nolock - force clockdomain wakeup transition (lockless) | ||
860 | * @clkdm: struct clockdomain * | ||
861 | * | ||
862 | * Instruct the CM to force a wakeup transition on the specified | ||
863 | * clockdomain @clkdm. Only for use by the powerdomain code. Returns | ||
864 | * -EINVAL if @clkdm is NULL or if the clockdomain does not support | ||
865 | * software-controlled wakeup; 0 upon success. | ||
866 | */ | ||
867 | int clkdm_wakeup_nolock(struct clockdomain *clkdm) | ||
781 | { | 868 | { |
782 | int ret; | 869 | int ret; |
783 | unsigned long flags; | ||
784 | 870 | ||
785 | if (!clkdm) | 871 | if (!clkdm) |
786 | return -EINVAL; | 872 | return -EINVAL; |
@@ -796,28 +882,46 @@ int clkdm_wakeup(struct clockdomain *clkdm) | |||
796 | 882 | ||
797 | pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name); | 883 | pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name); |
798 | 884 | ||
799 | spin_lock_irqsave(&clkdm->lock, flags); | ||
800 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; | 885 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; |
801 | ret = arch_clkdm->clkdm_wakeup(clkdm); | 886 | ret = arch_clkdm->clkdm_wakeup(clkdm); |
802 | ret |= pwrdm_state_switch(clkdm->pwrdm.ptr); | 887 | ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); |
803 | spin_unlock_irqrestore(&clkdm->lock, flags); | 888 | |
804 | return ret; | 889 | return ret; |
805 | } | 890 | } |
806 | 891 | ||
807 | /** | 892 | /** |
808 | * clkdm_allow_idle - enable hwsup idle transitions for clkdm | 893 | * clkdm_wakeup - force clockdomain wakeup transition |
809 | * @clkdm: struct clockdomain * | 894 | * @clkdm: struct clockdomain * |
810 | * | 895 | * |
811 | * Allow the hardware to automatically switch the clockdomain @clkdm into | 896 | * Instruct the CM to force a wakeup transition on the specified |
812 | * active or idle states, as needed by downstream clocks. If the | 897 | * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if the |
898 | * clockdomain does not support software-controlled wakeup; 0 upon | ||
899 | * success. | ||
900 | */ | ||
901 | int clkdm_wakeup(struct clockdomain *clkdm) | ||
902 | { | ||
903 | int ret; | ||
904 | |||
905 | pwrdm_lock(clkdm->pwrdm.ptr); | ||
906 | ret = clkdm_wakeup_nolock(clkdm); | ||
907 | pwrdm_unlock(clkdm->pwrdm.ptr); | ||
908 | |||
909 | return ret; | ||
910 | } | ||
911 | |||
912 | /** | ||
913 | * clkdm_allow_idle_nolock - enable hwsup idle transitions for clkdm | ||
914 | * @clkdm: struct clockdomain * | ||
915 | * | ||
916 | * Allow the hardware to automatically switch the clockdomain @clkdm | ||
917 | * into active or idle states, as needed by downstream clocks. If the | ||
813 | * clockdomain has any downstream clocks enabled in the clock | 918 | * clockdomain has any downstream clocks enabled in the clock |
814 | * framework, wkdep/sleepdep autodependencies are added; this is so | 919 | * framework, wkdep/sleepdep autodependencies are added; this is so |
815 | * device drivers can read and write to the device. No return value. | 920 | * device drivers can read and write to the device. Only for use by |
921 | * the powerdomain code. No return value. | ||
816 | */ | 922 | */ |
817 | void clkdm_allow_idle(struct clockdomain *clkdm) | 923 | void clkdm_allow_idle_nolock(struct clockdomain *clkdm) |
818 | { | 924 | { |
819 | unsigned long flags; | ||
820 | |||
821 | if (!clkdm) | 925 | if (!clkdm) |
822 | return; | 926 | return; |
823 | 927 | ||
@@ -833,11 +937,26 @@ void clkdm_allow_idle(struct clockdomain *clkdm) | |||
833 | pr_debug("clockdomain: enabling automatic idle transitions for %s\n", | 937 | pr_debug("clockdomain: enabling automatic idle transitions for %s\n", |
834 | clkdm->name); | 938 | clkdm->name); |
835 | 939 | ||
836 | spin_lock_irqsave(&clkdm->lock, flags); | ||
837 | clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED; | 940 | clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED; |
838 | arch_clkdm->clkdm_allow_idle(clkdm); | 941 | arch_clkdm->clkdm_allow_idle(clkdm); |
839 | pwrdm_state_switch(clkdm->pwrdm.ptr); | 942 | pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); |
840 | spin_unlock_irqrestore(&clkdm->lock, flags); | 943 | } |
944 | |||
945 | /** | ||
946 | * clkdm_allow_idle - enable hwsup idle transitions for clkdm | ||
947 | * @clkdm: struct clockdomain * | ||
948 | * | ||
949 | * Allow the hardware to automatically switch the clockdomain @clkdm into | ||
950 | * active or idle states, as needed by downstream clocks. If the | ||
951 | * clockdomain has any downstream clocks enabled in the clock | ||
952 | * framework, wkdep/sleepdep autodependencies are added; this is so | ||
953 | * device drivers can read and write to the device. No return value. | ||
954 | */ | ||
955 | void clkdm_allow_idle(struct clockdomain *clkdm) | ||
956 | { | ||
957 | pwrdm_lock(clkdm->pwrdm.ptr); | ||
958 | clkdm_allow_idle_nolock(clkdm); | ||
959 | pwrdm_unlock(clkdm->pwrdm.ptr); | ||
841 | } | 960 | } |
842 | 961 | ||
843 | /** | 962 | /** |
@@ -847,12 +966,11 @@ void clkdm_allow_idle(struct clockdomain *clkdm) | |||
847 | * Prevent the hardware from automatically switching the clockdomain | 966 | * Prevent the hardware from automatically switching the clockdomain |
848 | * @clkdm into inactive or idle states. If the clockdomain has | 967 | * @clkdm into inactive or idle states. If the clockdomain has |
849 | * downstream clocks enabled in the clock framework, wkdep/sleepdep | 968 | * downstream clocks enabled in the clock framework, wkdep/sleepdep |
850 | * autodependencies are removed. No return value. | 969 | * autodependencies are removed. Only for use by the powerdomain |
970 | * code. No return value. | ||
851 | */ | 971 | */ |
852 | void clkdm_deny_idle(struct clockdomain *clkdm) | 972 | void clkdm_deny_idle_nolock(struct clockdomain *clkdm) |
853 | { | 973 | { |
854 | unsigned long flags; | ||
855 | |||
856 | if (!clkdm) | 974 | if (!clkdm) |
857 | return; | 975 | return; |
858 | 976 | ||
@@ -868,11 +986,25 @@ void clkdm_deny_idle(struct clockdomain *clkdm) | |||
868 | pr_debug("clockdomain: disabling automatic idle transitions for %s\n", | 986 | pr_debug("clockdomain: disabling automatic idle transitions for %s\n", |
869 | clkdm->name); | 987 | clkdm->name); |
870 | 988 | ||
871 | spin_lock_irqsave(&clkdm->lock, flags); | ||
872 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; | 989 | clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; |
873 | arch_clkdm->clkdm_deny_idle(clkdm); | 990 | arch_clkdm->clkdm_deny_idle(clkdm); |
874 | pwrdm_state_switch(clkdm->pwrdm.ptr); | 991 | pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); |
875 | spin_unlock_irqrestore(&clkdm->lock, flags); | 992 | } |
993 | |||
994 | /** | ||
995 | * clkdm_deny_idle - disable hwsup idle transitions for clkdm | ||
996 | * @clkdm: struct clockdomain * | ||
997 | * | ||
998 | * Prevent the hardware from automatically switching the clockdomain | ||
999 | * @clkdm into inactive or idle states. If the clockdomain has | ||
1000 | * downstream clocks enabled in the clock framework, wkdep/sleepdep | ||
1001 | * autodependencies are removed. No return value. | ||
1002 | */ | ||
1003 | void clkdm_deny_idle(struct clockdomain *clkdm) | ||
1004 | { | ||
1005 | pwrdm_lock(clkdm->pwrdm.ptr); | ||
1006 | clkdm_deny_idle_nolock(clkdm); | ||
1007 | pwrdm_unlock(clkdm->pwrdm.ptr); | ||
876 | } | 1008 | } |
877 | 1009 | ||
878 | /** | 1010 | /** |
@@ -889,14 +1021,11 @@ void clkdm_deny_idle(struct clockdomain *clkdm) | |||
889 | bool clkdm_in_hwsup(struct clockdomain *clkdm) | 1021 | bool clkdm_in_hwsup(struct clockdomain *clkdm) |
890 | { | 1022 | { |
891 | bool ret; | 1023 | bool ret; |
892 | unsigned long flags; | ||
893 | 1024 | ||
894 | if (!clkdm) | 1025 | if (!clkdm) |
895 | return false; | 1026 | return false; |
896 | 1027 | ||
897 | spin_lock_irqsave(&clkdm->lock, flags); | ||
898 | ret = (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false; | 1028 | ret = (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false; |
899 | spin_unlock_irqrestore(&clkdm->lock, flags); | ||
900 | 1029 | ||
901 | return ret; | 1030 | return ret; |
902 | } | 1031 | } |
@@ -918,30 +1047,91 @@ bool clkdm_missing_idle_reporting(struct clockdomain *clkdm) | |||
918 | return (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) ? true : false; | 1047 | return (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) ? true : false; |
919 | } | 1048 | } |
920 | 1049 | ||
1050 | /* Public autodep handling functions (deprecated) */ | ||
1051 | |||
1052 | /** | ||
1053 | * clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable | ||
1054 | * @clkdm: struct clockdomain * | ||
1055 | * | ||
1056 | * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm' | ||
1057 | * in hardware-supervised mode. Meant to be called from clock framework | ||
1058 | * when a clock inside clockdomain 'clkdm' is enabled. No return value. | ||
1059 | * | ||
1060 | * XXX autodeps are deprecated and should be removed at the earliest | ||
1061 | * opportunity | ||
1062 | */ | ||
1063 | void clkdm_add_autodeps(struct clockdomain *clkdm) | ||
1064 | { | ||
1065 | struct clkdm_autodep *autodep; | ||
1066 | |||
1067 | if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS) | ||
1068 | return; | ||
1069 | |||
1070 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { | ||
1071 | if (IS_ERR(autodep->clkdm.ptr)) | ||
1072 | continue; | ||
1073 | |||
1074 | pr_debug("clockdomain: %s: adding %s sleepdep/wkdep\n", | ||
1075 | clkdm->name, autodep->clkdm.ptr->name); | ||
1076 | |||
1077 | _clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr); | ||
1078 | _clkdm_add_wkdep(clkdm, autodep->clkdm.ptr); | ||
1079 | } | ||
1080 | } | ||
1081 | |||
1082 | /** | ||
1083 | * clkdm_del_autodeps - remove auto sleepdeps/wkdeps from clkdm | ||
1084 | * @clkdm: struct clockdomain * | ||
1085 | * | ||
1086 | * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm' | ||
1087 | * in hardware-supervised mode. Meant to be called from clock framework | ||
1088 | * when a clock inside clockdomain 'clkdm' is disabled. No return value. | ||
1089 | * | ||
1090 | * XXX autodeps are deprecated and should be removed at the earliest | ||
1091 | * opportunity | ||
1092 | */ | ||
1093 | void clkdm_del_autodeps(struct clockdomain *clkdm) | ||
1094 | { | ||
1095 | struct clkdm_autodep *autodep; | ||
1096 | |||
1097 | if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS) | ||
1098 | return; | ||
1099 | |||
1100 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { | ||
1101 | if (IS_ERR(autodep->clkdm.ptr)) | ||
1102 | continue; | ||
1103 | |||
1104 | pr_debug("clockdomain: %s: removing %s sleepdep/wkdep\n", | ||
1105 | clkdm->name, autodep->clkdm.ptr->name); | ||
1106 | |||
1107 | _clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr); | ||
1108 | _clkdm_del_wkdep(clkdm, autodep->clkdm.ptr); | ||
1109 | } | ||
1110 | } | ||
1111 | |||
921 | /* Clockdomain-to-clock/hwmod framework interface code */ | 1112 | /* Clockdomain-to-clock/hwmod framework interface code */ |
922 | 1113 | ||
923 | static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm) | 1114 | static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm) |
924 | { | 1115 | { |
925 | unsigned long flags; | ||
926 | |||
927 | if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable) | 1116 | if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable) |
928 | return -EINVAL; | 1117 | return -EINVAL; |
929 | 1118 | ||
930 | spin_lock_irqsave(&clkdm->lock, flags); | 1119 | pwrdm_lock(clkdm->pwrdm.ptr); |
931 | 1120 | ||
932 | /* | 1121 | /* |
933 | * For arch's with no autodeps, clkcm_clk_enable | 1122 | * For arch's with no autodeps, clkcm_clk_enable |
934 | * should be called for every clock instance or hwmod that is | 1123 | * should be called for every clock instance or hwmod that is |
935 | * enabled, so the clkdm can be force woken up. | 1124 | * enabled, so the clkdm can be force woken up. |
936 | */ | 1125 | */ |
937 | if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps) { | 1126 | clkdm->usecount++; |
938 | spin_unlock_irqrestore(&clkdm->lock, flags); | 1127 | if (clkdm->usecount > 1 && autodeps) { |
1128 | pwrdm_unlock(clkdm->pwrdm.ptr); | ||
939 | return 0; | 1129 | return 0; |
940 | } | 1130 | } |
941 | 1131 | ||
942 | arch_clkdm->clkdm_clk_enable(clkdm); | 1132 | arch_clkdm->clkdm_clk_enable(clkdm); |
943 | pwrdm_state_switch(clkdm->pwrdm.ptr); | 1133 | pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); |
944 | spin_unlock_irqrestore(&clkdm->lock, flags); | 1134 | pwrdm_unlock(clkdm->pwrdm.ptr); |
945 | 1135 | ||
946 | pr_debug("clockdomain: %s: enabled\n", clkdm->name); | 1136 | pr_debug("clockdomain: %s: enabled\n", clkdm->name); |
947 | 1137 | ||
@@ -990,36 +1180,34 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) | |||
990 | */ | 1180 | */ |
991 | int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) | 1181 | int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) |
992 | { | 1182 | { |
993 | unsigned long flags; | ||
994 | |||
995 | if (!clkdm || !clk || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) | 1183 | if (!clkdm || !clk || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) |
996 | return -EINVAL; | 1184 | return -EINVAL; |
997 | 1185 | ||
998 | spin_lock_irqsave(&clkdm->lock, flags); | 1186 | pwrdm_lock(clkdm->pwrdm.ptr); |
999 | 1187 | ||
1000 | /* corner case: disabling unused clocks */ | 1188 | /* corner case: disabling unused clocks */ |
1001 | if ((__clk_get_enable_count(clk) == 0) && | 1189 | if ((__clk_get_enable_count(clk) == 0) && clkdm->usecount == 0) |
1002 | (atomic_read(&clkdm->usecount) == 0)) | ||
1003 | goto ccd_exit; | 1190 | goto ccd_exit; |
1004 | 1191 | ||
1005 | if (atomic_read(&clkdm->usecount) == 0) { | 1192 | if (clkdm->usecount == 0) { |
1006 | spin_unlock_irqrestore(&clkdm->lock, flags); | 1193 | pwrdm_unlock(clkdm->pwrdm.ptr); |
1007 | WARN_ON(1); /* underflow */ | 1194 | WARN_ON(1); /* underflow */ |
1008 | return -ERANGE; | 1195 | return -ERANGE; |
1009 | } | 1196 | } |
1010 | 1197 | ||
1011 | if (atomic_dec_return(&clkdm->usecount) > 0) { | 1198 | clkdm->usecount--; |
1012 | spin_unlock_irqrestore(&clkdm->lock, flags); | 1199 | if (clkdm->usecount > 0) { |
1200 | pwrdm_unlock(clkdm->pwrdm.ptr); | ||
1013 | return 0; | 1201 | return 0; |
1014 | } | 1202 | } |
1015 | 1203 | ||
1016 | arch_clkdm->clkdm_clk_disable(clkdm); | 1204 | arch_clkdm->clkdm_clk_disable(clkdm); |
1017 | pwrdm_state_switch(clkdm->pwrdm.ptr); | 1205 | pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); |
1018 | 1206 | ||
1019 | pr_debug("clockdomain: %s: disabled\n", clkdm->name); | 1207 | pr_debug("clockdomain: %s: disabled\n", clkdm->name); |
1020 | 1208 | ||
1021 | ccd_exit: | 1209 | ccd_exit: |
1022 | spin_unlock_irqrestore(&clkdm->lock, flags); | 1210 | pwrdm_unlock(clkdm->pwrdm.ptr); |
1023 | 1211 | ||
1024 | return 0; | 1212 | return 0; |
1025 | } | 1213 | } |
@@ -1072,8 +1260,6 @@ int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh) | |||
1072 | */ | 1260 | */ |
1073 | int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh) | 1261 | int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh) |
1074 | { | 1262 | { |
1075 | unsigned long flags; | ||
1076 | |||
1077 | /* The clkdm attribute does not exist yet prior OMAP4 */ | 1263 | /* The clkdm attribute does not exist yet prior OMAP4 */ |
1078 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) | 1264 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) |
1079 | return 0; | 1265 | return 0; |
@@ -1086,22 +1272,23 @@ int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh) | |||
1086 | if (!clkdm || !oh || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) | 1272 | if (!clkdm || !oh || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) |
1087 | return -EINVAL; | 1273 | return -EINVAL; |
1088 | 1274 | ||
1089 | spin_lock_irqsave(&clkdm->lock, flags); | 1275 | pwrdm_lock(clkdm->pwrdm.ptr); |
1090 | 1276 | ||
1091 | if (atomic_read(&clkdm->usecount) == 0) { | 1277 | if (clkdm->usecount == 0) { |
1092 | spin_unlock_irqrestore(&clkdm->lock, flags); | 1278 | pwrdm_unlock(clkdm->pwrdm.ptr); |
1093 | WARN_ON(1); /* underflow */ | 1279 | WARN_ON(1); /* underflow */ |
1094 | return -ERANGE; | 1280 | return -ERANGE; |
1095 | } | 1281 | } |
1096 | 1282 | ||
1097 | if (atomic_dec_return(&clkdm->usecount) > 0) { | 1283 | clkdm->usecount--; |
1098 | spin_unlock_irqrestore(&clkdm->lock, flags); | 1284 | if (clkdm->usecount > 0) { |
1285 | pwrdm_unlock(clkdm->pwrdm.ptr); | ||
1099 | return 0; | 1286 | return 0; |
1100 | } | 1287 | } |
1101 | 1288 | ||
1102 | arch_clkdm->clkdm_clk_disable(clkdm); | 1289 | arch_clkdm->clkdm_clk_disable(clkdm); |
1103 | pwrdm_state_switch(clkdm->pwrdm.ptr); | 1290 | pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); |
1104 | spin_unlock_irqrestore(&clkdm->lock, flags); | 1291 | pwrdm_unlock(clkdm->pwrdm.ptr); |
1105 | 1292 | ||
1106 | pr_debug("clockdomain: %s: disabled\n", clkdm->name); | 1293 | pr_debug("clockdomain: %s: disabled\n", clkdm->name); |
1107 | 1294 | ||