aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/clockdomain.c
diff options
context:
space:
mode:
authorPaul Walmsley <paul@pwsan.com>2010-01-26 22:13:01 -0500
committerPaul Walmsley <paul@pwsan.com>2010-01-26 22:13:01 -0500
commit369d5614457384edcf62c5f39b03dd20be6ea1df (patch)
tree4ad04d48f3049ff2d88e8ddca35fb0ad8597d12c /arch/arm/mach-omap2/clockdomain.c
parente909d62a8afda7a224a7e322cf2f387d69ca771f (diff)
OMAP clockdomains: add usecounting for wakeup and sleep dependencies
Add usecounting for wakeup and sleep dependencies. In the current situation, if several functions add dependencies on the same clockdomains, when the first dependency removal function is called, the dependency will be incorrectly removed from the hardware. Add clkdm_clear_all_wkdeps() and clkdm_clear_all_sleepdeps(), which provide a fast and usecounting-consistent way to clear all hardware clockdomain dependencies, since accesses to these registers can be quite slow. pm{2,3}4xx.c has been updated to use these new functions. The original version of this patch did not touch these files, which previously wrote directly to the wkdep registers, and thus confused the usecounting code. This problem was found by Kevin Hilman <khilman@deeprootsystems.com>. N.B.: This patch introduces one significant functional difference over the previous pm34xx.c code: sleepdeps are now cleared during clockdomain initialization, whereas previously they were left untouched. This has been tested by Kevin and confirmed to work. The original version of this patch also did not take into consideration that some clockdomains do not have sleep or wakeup dependency sources, which caused NULL pointer dereferences. This problem was debugged and fixed by Kevin Hilman <khilman@deeprootsystems.com>. Signed-off-by: Paul Walmsley <paul@pwsan.com> Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com> Cc: Jouni Högander <jouni.hogander@nokia.com>
Diffstat (limited to 'arch/arm/mach-omap2/clockdomain.c')
-rw-r--r--arch/arm/mach-omap2/clockdomain.c216
1 files changed, 196 insertions, 20 deletions
diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c
index 2af9996f010b..6eaa9314cd64 100644
--- a/arch/arm/mach-omap2/clockdomain.c
+++ b/arch/arm/mach-omap2/clockdomain.c
@@ -113,7 +113,6 @@ static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm,
113 return ERR_PTR(-EINVAL); 113 return ERR_PTR(-EINVAL);
114 114
115 for (cd = deps; cd->clkdm_name; cd++) { 115 for (cd = deps; cd->clkdm_name; cd++) {
116
117 if (!omap_chip_is(cd->omap_chip)) 116 if (!omap_chip_is(cd->omap_chip))
118 continue; 117 continue;
119 118
@@ -122,7 +121,6 @@ static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm,
122 121
123 if (cd->clkdm == clkdm) 122 if (cd->clkdm == clkdm)
124 break; 123 break;
125
126 } 124 }
127 125
128 if (!cd->clkdm_name) 126 if (!cd->clkdm_name)
@@ -254,6 +252,96 @@ static void _omap2_clkdm_set_hwsup(struct clockdomain *clkdm, int enable)
254 252
255} 253}
256 254
255/**
256 * _init_wkdep_usecount - initialize wkdep usecounts to match hardware
257 * @clkdm: clockdomain to initialize wkdep usecounts
258 *
259 * Initialize the wakeup dependency usecount variables for clockdomain @clkdm.
260 * If a wakeup dependency is present in the hardware, the usecount will be
261 * set to 1; otherwise, it will be set to 0. Software should clear all
262 * software wakeup dependencies prior to calling this function if it wishes
263 * to ensure that all usecounts start at 0. No return value.
264 */
265static void _init_wkdep_usecount(struct clockdomain *clkdm)
266{
267 u32 v;
268 struct clkdm_dep *cd;
269
270 if (!clkdm->wkdep_srcs)
271 return;
272
273 for (cd = clkdm->wkdep_srcs; cd->clkdm_name; cd++) {
274 if (!omap_chip_is(cd->omap_chip))
275 continue;
276
277 if (!cd->clkdm && cd->clkdm_name)
278 cd->clkdm = _clkdm_lookup(cd->clkdm_name);
279
280 if (!cd->clkdm) {
281 WARN(!cd->clkdm, "clockdomain: %s: wkdep clkdm %s not "
282 "found\n", clkdm->name, cd->clkdm_name);
283 continue;
284 }
285
286 v = prm_read_mod_bits_shift(clkdm->pwrdm.ptr->prcm_offs,
287 PM_WKDEP,
288 (1 << cd->clkdm->dep_bit));
289
290 if (v)
291 pr_debug("clockdomain: %s: wakeup dependency already "
292 "set to wake up when %s wakes\n",
293 clkdm->name, cd->clkdm->name);
294
295 atomic_set(&cd->wkdep_usecount, (v) ? 1 : 0);
296 }
297}
298
299/**
300 * _init_sleepdep_usecount - initialize sleepdep usecounts to match hardware
301 * @clkdm: clockdomain to initialize sleepdep usecounts
302 *
303 * Initialize the sleep dependency usecount variables for clockdomain @clkdm.
304 * If a sleep dependency is present in the hardware, the usecount will be
305 * set to 1; otherwise, it will be set to 0. Software should clear all
306 * software sleep dependencies prior to calling this function if it wishes
307 * to ensure that all usecounts start at 0. No return value.
308 */
309static void _init_sleepdep_usecount(struct clockdomain *clkdm)
310{
311 u32 v;
312 struct clkdm_dep *cd;
313
314 if (!cpu_is_omap34xx())
315 return;
316
317 if (!clkdm->sleepdep_srcs)
318 return;
319
320 for (cd = clkdm->sleepdep_srcs; cd->clkdm_name; cd++) {
321 if (!omap_chip_is(cd->omap_chip))
322 continue;
323
324 if (!cd->clkdm && cd->clkdm_name)
325 cd->clkdm = _clkdm_lookup(cd->clkdm_name);
326
327 if (!cd->clkdm) {
328 WARN(!cd->clkdm, "clockdomain: %s: sleepdep clkdm %s "
329 "not found\n", clkdm->name, cd->clkdm_name);
330 continue;
331 }
332
333 v = prm_read_mod_bits_shift(clkdm->pwrdm.ptr->prcm_offs,
334 OMAP3430_CM_SLEEPDEP,
335 (1 << cd->clkdm->dep_bit));
336
337 if (v)
338 pr_debug("clockdomain: %s: sleep dependency already "
339 "set to prevent from idling until %s "
340 "idles\n", clkdm->name, cd->clkdm->name);
341
342 atomic_set(&cd->sleepdep_usecount, (v) ? 1 : 0);
343 }
344};
257 345
258/* Public functions */ 346/* Public functions */
259 347
@@ -272,6 +360,7 @@ void clkdm_init(struct clockdomain **clkdms,
272 struct clkdm_autodep *init_autodeps) 360 struct clkdm_autodep *init_autodeps)
273{ 361{
274 struct clockdomain **c = NULL; 362 struct clockdomain **c = NULL;
363 struct clockdomain *clkdm;
275 struct clkdm_autodep *autodep = NULL; 364 struct clkdm_autodep *autodep = NULL;
276 365
277 if (clkdms) 366 if (clkdms)
@@ -282,6 +371,15 @@ void clkdm_init(struct clockdomain **clkdms,
282 if (autodeps) 371 if (autodeps)
283 for (autodep = autodeps; autodep->clkdm.ptr; autodep++) 372 for (autodep = autodeps; autodep->clkdm.ptr; autodep++)
284 _autodep_lookup(autodep); 373 _autodep_lookup(autodep);
374
375 /*
376 * Ensure that the *dep_usecount registers reflect the current
377 * state of the PRCM.
378 */
379 list_for_each_entry(clkdm, &clkdm_list, node) {
380 _init_wkdep_usecount(clkdm);
381 _init_sleepdep_usecount(clkdm);
382 }
285} 383}
286 384
287/** 385/**
@@ -387,11 +485,13 @@ int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
387 return PTR_ERR(cd); 485 return PTR_ERR(cd);
388 } 486 }
389 487
390 pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n", 488 if (atomic_inc_return(&cd->wkdep_usecount) == 1) {
391 clkdm1->name, clkdm2->name); 489 pr_debug("clockdomain: hardware will wake up %s when %s wakes "
490 "up\n", clkdm1->name, clkdm2->name);
392 491
393 prm_set_mod_reg_bits((1 << clkdm2->dep_bit), 492 prm_set_mod_reg_bits((1 << clkdm2->dep_bit),
394 clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP); 493 clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
494 }
395 495
396 return 0; 496 return 0;
397} 497}
@@ -420,11 +520,13 @@ int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
420 return PTR_ERR(cd); 520 return PTR_ERR(cd);
421 } 521 }
422 522
423 pr_debug("clockdomain: hardware will no longer wake up %s after %s " 523 if (atomic_dec_return(&cd->wkdep_usecount) == 0) {
424 "wakes up\n", clkdm1->name, clkdm2->name); 524 pr_debug("clockdomain: hardware will no longer wake up %s "
525 "after %s wakes up\n", clkdm1->name, clkdm2->name);
425 526
426 prm_clear_mod_reg_bits((1 << clkdm2->dep_bit), 527 prm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
427 clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP); 528 clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
529 }
428 530
429 return 0; 531 return 0;
430} 532}
@@ -457,11 +559,44 @@ int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
457 return PTR_ERR(cd); 559 return PTR_ERR(cd);
458 } 560 }
459 561
562 /* XXX It's faster to return the atomic wkdep_usecount */
460 return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP, 563 return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP,
461 (1 << clkdm2->dep_bit)); 564 (1 << clkdm2->dep_bit));
462} 565}
463 566
464/** 567/**
568 * clkdm_clear_all_wkdeps - remove all wakeup dependencies from target clkdm
569 * @clkdm: struct clockdomain * to remove all wakeup dependencies from
570 *
571 * Remove all inter-clockdomain wakeup dependencies that could cause
572 * @clkdm to wake. Intended to be used during boot to initialize the
573 * PRCM to a known state, after all clockdomains are put into swsup idle
574 * and woken up. Returns -EINVAL if @clkdm pointer is invalid, or
575 * 0 upon success.
576 */
577int clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
578{
579 struct clkdm_dep *cd;
580 u32 mask = 0;
581
582 if (!clkdm)
583 return -EINVAL;
584
585 for (cd = clkdm->wkdep_srcs; cd && cd->clkdm_name; cd++) {
586 if (!omap_chip_is(cd->omap_chip))
587 continue;
588
589 /* PRM accesses are slow, so minimize them */
590 mask |= 1 << cd->clkdm->dep_bit;
591 atomic_set(&cd->wkdep_usecount, 0);
592 }
593
594 prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs, PM_WKDEP);
595
596 return 0;
597}
598
599/**
465 * clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 600 * clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1
466 * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) 601 * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
467 * @clkdm2: when this struct clockdomain * is active (source) 602 * @clkdm2: when this struct clockdomain * is active (source)
@@ -491,12 +626,14 @@ int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
491 return PTR_ERR(cd); 626 return PTR_ERR(cd);
492 } 627 }
493 628
494 pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n", 629 if (atomic_inc_return(&cd->sleepdep_usecount) == 1) {
495 clkdm1->name, clkdm2->name); 630 pr_debug("clockdomain: will prevent %s from sleeping if %s "
631 "is active\n", clkdm1->name, clkdm2->name);
496 632
497 cm_set_mod_reg_bits((1 << clkdm2->dep_bit), 633 cm_set_mod_reg_bits((1 << clkdm2->dep_bit),
498 clkdm1->pwrdm.ptr->prcm_offs, 634 clkdm1->pwrdm.ptr->prcm_offs,
499 OMAP3430_CM_SLEEPDEP); 635 OMAP3430_CM_SLEEPDEP);
636 }
500 637
501 return 0; 638 return 0;
502} 639}
@@ -531,12 +668,15 @@ int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
531 return PTR_ERR(cd); 668 return PTR_ERR(cd);
532 } 669 }
533 670
534 pr_debug("clockdomain: will no longer prevent %s from sleeping if " 671 if (atomic_dec_return(&cd->sleepdep_usecount) == 0) {
535 "%s is active\n", clkdm1->name, clkdm2->name); 672 pr_debug("clockdomain: will no longer prevent %s from "
673 "sleeping if %s is active\n", clkdm1->name,
674 clkdm2->name);
536 675
537 cm_clear_mod_reg_bits((1 << clkdm2->dep_bit), 676 cm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
538 clkdm1->pwrdm.ptr->prcm_offs, 677 clkdm1->pwrdm.ptr->prcm_offs,
539 OMAP3430_CM_SLEEPDEP); 678 OMAP3430_CM_SLEEPDEP);
679 }
540 680
541 return 0; 681 return 0;
542} 682}
@@ -575,11 +715,47 @@ int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
575 return PTR_ERR(cd); 715 return PTR_ERR(cd);
576 } 716 }
577 717
718 /* XXX It's faster to return the atomic sleepdep_usecount */
578 return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, 719 return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs,
579 OMAP3430_CM_SLEEPDEP, 720 OMAP3430_CM_SLEEPDEP,
580 (1 << clkdm2->dep_bit)); 721 (1 << clkdm2->dep_bit));
581} 722}
582 723
724/**
725 * clkdm_clear_all_sleepdeps - remove all sleep dependencies from target clkdm
726 * @clkdm: struct clockdomain * to remove all sleep dependencies from
727 *
728 * Remove all inter-clockdomain sleep dependencies that could prevent
729 * @clkdm from idling. Intended to be used during boot to initialize the
730 * PRCM to a known state, after all clockdomains are put into swsup idle
731 * and woken up. Returns -EINVAL if @clkdm pointer is invalid, or
732 * 0 upon success.
733 */
734int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
735{
736 struct clkdm_dep *cd;
737 u32 mask = 0;
738
739 if (!cpu_is_omap34xx())
740 return -EINVAL;
741
742 if (!clkdm)
743 return -EINVAL;
744
745 for (cd = clkdm->sleepdep_srcs; cd && cd->clkdm_name; cd++) {
746 if (!omap_chip_is(cd->omap_chip))
747 continue;
748
749 /* PRM accesses are slow, so minimize them */
750 mask |= 1 << cd->clkdm->dep_bit;
751 atomic_set(&cd->sleepdep_usecount, 0);
752 }
753
754 prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs,
755 OMAP3430_CM_SLEEPDEP);
756
757 return 0;
758}
583 759
584/** 760/**
585 * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode 761 * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode