diff options
author | Kalle Jokiniemi <kalle.jokiniemi@digia.com> | 2009-10-14 18:40:37 -0400 |
---|---|---|
committer | paul <paul@twilight.(none)> | 2009-10-14 18:40:37 -0400 |
commit | a0219fbdacc01e039d1b158d16141349a3309915 (patch) | |
tree | ba64eb7a8775fc47e5f5d9454810b97988147191 /arch/arm/mach-omap2 | |
parent | 161291396e76e0832c08f617eb9bd364d1648148 (diff) |
OMAP: Fix race condition with autodeps
There is a possible race condition in clockdomain
code handling hw supported idle transitions.
When multiple autodeps dependencies are being added
or removed, a transition of still remaining dependent
powerdomain can result in false readings of the
state counter. This is especially fatal for off mode
state counter, as it could result in a driver not
noticing a context loss.
Fixed by disabling hw supported state transitions
when autodeps are being changed.
Signed-off-by: Kalle Jokiniemi <kalle.jokiniemi@digia.com>
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Diffstat (limited to 'arch/arm/mach-omap2')
-rw-r--r-- | arch/arm/mach-omap2/clockdomain.c | 74 |
1 files changed, 44 insertions, 30 deletions
diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c index 4ef7b4f5474e..58aff8485df9 100644 --- a/arch/arm/mach-omap2/clockdomain.c +++ b/arch/arm/mach-omap2/clockdomain.c | |||
@@ -137,6 +137,36 @@ static void _clkdm_del_autodeps(struct clockdomain *clkdm) | |||
137 | } | 137 | } |
138 | } | 138 | } |
139 | 139 | ||
140 | /* | ||
141 | * _omap2_clkdm_set_hwsup - set the hwsup idle transition bit | ||
142 | * @clkdm: struct clockdomain * | ||
143 | * @enable: int 0 to disable, 1 to enable | ||
144 | * | ||
145 | * Internal helper for actually switching the bit that controls hwsup | ||
146 | * idle transitions for clkdm. | ||
147 | */ | ||
148 | static void _omap2_clkdm_set_hwsup(struct clockdomain *clkdm, int enable) | ||
149 | { | ||
150 | u32 v; | ||
151 | |||
152 | if (cpu_is_omap24xx()) { | ||
153 | if (enable) | ||
154 | v = OMAP24XX_CLKSTCTRL_ENABLE_AUTO; | ||
155 | else | ||
156 | v = OMAP24XX_CLKSTCTRL_DISABLE_AUTO; | ||
157 | } else if (cpu_is_omap34xx()) { | ||
158 | if (enable) | ||
159 | v = OMAP34XX_CLKSTCTRL_ENABLE_AUTO; | ||
160 | else | ||
161 | v = OMAP34XX_CLKSTCTRL_DISABLE_AUTO; | ||
162 | } else { | ||
163 | BUG(); | ||
164 | } | ||
165 | |||
166 | cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, | ||
167 | v << __ffs(clkdm->clktrctrl_mask), | ||
168 | clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL); | ||
169 | } | ||
140 | 170 | ||
141 | static struct clockdomain *_clkdm_lookup(const char *name) | 171 | static struct clockdomain *_clkdm_lookup(const char *name) |
142 | { | 172 | { |
@@ -456,8 +486,6 @@ int omap2_clkdm_wakeup(struct clockdomain *clkdm) | |||
456 | */ | 486 | */ |
457 | void omap2_clkdm_allow_idle(struct clockdomain *clkdm) | 487 | void omap2_clkdm_allow_idle(struct clockdomain *clkdm) |
458 | { | 488 | { |
459 | u32 v; | ||
460 | |||
461 | if (!clkdm) | 489 | if (!clkdm) |
462 | return; | 490 | return; |
463 | 491 | ||
@@ -473,18 +501,7 @@ void omap2_clkdm_allow_idle(struct clockdomain *clkdm) | |||
473 | if (atomic_read(&clkdm->usecount) > 0) | 501 | if (atomic_read(&clkdm->usecount) > 0) |
474 | _clkdm_add_autodeps(clkdm); | 502 | _clkdm_add_autodeps(clkdm); |
475 | 503 | ||
476 | if (cpu_is_omap24xx()) | 504 | _omap2_clkdm_set_hwsup(clkdm, 1); |
477 | v = OMAP24XX_CLKSTCTRL_ENABLE_AUTO; | ||
478 | else if (cpu_is_omap34xx()) | ||
479 | v = OMAP34XX_CLKSTCTRL_ENABLE_AUTO; | ||
480 | else | ||
481 | BUG(); | ||
482 | |||
483 | |||
484 | cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, | ||
485 | v << __ffs(clkdm->clktrctrl_mask), | ||
486 | clkdm->pwrdm.ptr->prcm_offs, | ||
487 | CM_CLKSTCTRL); | ||
488 | 505 | ||
489 | pwrdm_clkdm_state_switch(clkdm); | 506 | pwrdm_clkdm_state_switch(clkdm); |
490 | } | 507 | } |
@@ -500,8 +517,6 @@ void omap2_clkdm_allow_idle(struct clockdomain *clkdm) | |||
500 | */ | 517 | */ |
501 | void omap2_clkdm_deny_idle(struct clockdomain *clkdm) | 518 | void omap2_clkdm_deny_idle(struct clockdomain *clkdm) |
502 | { | 519 | { |
503 | u32 v; | ||
504 | |||
505 | if (!clkdm) | 520 | if (!clkdm) |
506 | return; | 521 | return; |
507 | 522 | ||
@@ -514,16 +529,7 @@ void omap2_clkdm_deny_idle(struct clockdomain *clkdm) | |||
514 | pr_debug("clockdomain: disabling automatic idle transitions for %s\n", | 529 | pr_debug("clockdomain: disabling automatic idle transitions for %s\n", |
515 | clkdm->name); | 530 | clkdm->name); |
516 | 531 | ||
517 | if (cpu_is_omap24xx()) | 532 | _omap2_clkdm_set_hwsup(clkdm, 0); |
518 | v = OMAP24XX_CLKSTCTRL_DISABLE_AUTO; | ||
519 | else if (cpu_is_omap34xx()) | ||
520 | v = OMAP34XX_CLKSTCTRL_DISABLE_AUTO; | ||
521 | else | ||
522 | BUG(); | ||
523 | |||
524 | cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, | ||
525 | v << __ffs(clkdm->clktrctrl_mask), | ||
526 | clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL); | ||
527 | 533 | ||
528 | if (atomic_read(&clkdm->usecount) > 0) | 534 | if (atomic_read(&clkdm->usecount) > 0) |
529 | _clkdm_del_autodeps(clkdm); | 535 | _clkdm_del_autodeps(clkdm); |
@@ -569,10 +575,14 @@ int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) | |||
569 | v = omap2_clkdm_clktrctrl_read(clkdm); | 575 | v = omap2_clkdm_clktrctrl_read(clkdm); |
570 | 576 | ||
571 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || | 577 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || |
572 | (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO)) | 578 | (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO)) { |
579 | /* Disable HW transitions when we are changing deps */ | ||
580 | _omap2_clkdm_set_hwsup(clkdm, 0); | ||
573 | _clkdm_add_autodeps(clkdm); | 581 | _clkdm_add_autodeps(clkdm); |
574 | else | 582 | _omap2_clkdm_set_hwsup(clkdm, 1); |
583 | } else { | ||
575 | omap2_clkdm_wakeup(clkdm); | 584 | omap2_clkdm_wakeup(clkdm); |
585 | } | ||
576 | 586 | ||
577 | pwrdm_wait_transition(clkdm->pwrdm.ptr); | 587 | pwrdm_wait_transition(clkdm->pwrdm.ptr); |
578 | pwrdm_clkdm_state_switch(clkdm); | 588 | pwrdm_clkdm_state_switch(clkdm); |
@@ -623,10 +633,14 @@ int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) | |||
623 | v = omap2_clkdm_clktrctrl_read(clkdm); | 633 | v = omap2_clkdm_clktrctrl_read(clkdm); |
624 | 634 | ||
625 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || | 635 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || |
626 | (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO)) | 636 | (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO)) { |
637 | /* Disable HW transitions when we are changing deps */ | ||
638 | _omap2_clkdm_set_hwsup(clkdm, 0); | ||
627 | _clkdm_del_autodeps(clkdm); | 639 | _clkdm_del_autodeps(clkdm); |
628 | else | 640 | _omap2_clkdm_set_hwsup(clkdm, 1); |
641 | } else { | ||
629 | omap2_clkdm_sleep(clkdm); | 642 | omap2_clkdm_sleep(clkdm); |
643 | } | ||
630 | 644 | ||
631 | pwrdm_clkdm_state_switch(clkdm); | 645 | pwrdm_clkdm_state_switch(clkdm); |
632 | 646 | ||