aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPaul Walmsley <paul@pwsan.com>2012-07-04 07:22:53 -0400
committerKevin Hilman <khilman@ti.com>2012-07-05 20:25:38 -0400
commit006c7f18449a06027b0165e938c67b3a029813c9 (patch)
treee8c9f9ebc1d3dc555b857c135b22c4883c567429 /arch
parent6887a4131da3adaab011613776d865f4bcfb5678 (diff)
ARM: OMAP2+: hwmod code/clockdomain data: fix 32K sync timer
Kevin discovered that commit c8d82ff68fb6873691536cf33021977efbf5593c ("ARM: OMAP2/3: hwmod data: Add 32k-sync timer data to hwmod database") broke CORE idle on OMAP3. This prevents device low power states. The root cause is that the 32K sync timer IP block does not support smart-idle mode[1], and so the hwmod code keeps the IP block in no-idle mode while it is active. This in turn prevents the WKUP clockdomain from transitioning to idle. There is a hardcoded sleep dependency that prevents the CORE_L3 and CORE_CM clockdomains from transitioning to idle when the WKUP clockdomain is active[2], so the chip cannot enter any device low power states. It turns out that there is no need to take the 32k sync timer out of idle. The IP block itself probably does not have any native idle handling at all, due to its simplicity. Furthermore, the PRCM will never request target idle for this IP block while the kernel is running, due to the sleep dependency that prevents the WKUP clockdomain from idling while the CORE_L3 clockdomain is active. So we can safely leave the 32k sync timer in target-force-idle mode, even while we continue to access it. This workaround is implemented by defining a new clockdomain flag, CLKDM_ACTIVE_WITH_MPU, that indicates that the clockdomain is guaranteed to be active whenever the MPU is inactive. If an IP block's main functional clock exists inside this clockdomain, and the IP block does not support smart-idle modes, then the hwmod code will place the IP block into target force-idle mode even when enabled. The WKUP clockdomains on OMAP3/4 are marked with this flag. (On OMAP2xxx, no OCP header existed on the 32k sync timer.) Other clockdomains also should be marked with this flag, but those changes are deferred until a later merge window, to create a minimal fix. Another theoretically clean fix for this problem would be to implement PM runtime-based control for 32k sync timer accesses. These PM runtime calls would need to located in a custom clocksource, since the 32k sync timer is currently used as an MMIO clocksource. But in practice, there would be little benefit to doing so; and there would be some cost, due to the addition of unnecessary lines of code and the additional CPU overhead of the PM runtime and hwmod code - unnecessary in this case. Another possible fix would have been to modify the pm34xx.c code to force the IP block idle before entering WFI. But this would not have been an acceptable approach: we are trying to remove this type of centralized IP block idle control from the PM code. This patch is a collaboration between Kevin Hilman <khilman@ti.com> and Paul Walmsley <paul@pwsan.com>. Thanks to Vaibhav Hiremath <hvaibhav@ti.com> for providing comments on an earlier version of this patch. Thanks to Tero Kristo <t-kristo@ti.com> for identifying a bug in an earlier version of this patch. Thanks to Benoît Cousson <b-cousson@ti.com> for identifying some bugs in several versions of this patch and for implementation comments. References: 1. Table 16-96 "REG_32KSYNCNT_SYSCONFIG" of the OMAP34xx TRM Rev. ZU (SWPU223U), available from: http://www.ti.com/pdfs/wtbu/OMAP34x_ES3.1.x_PUBLIC_TRM_vzU.zip 2. Table 4-72 "Sleep Dependencies" of the OMAP34xx TRM Rev. ZU (SWPU223U) 3. ibid. Cc: Tony Lindgren <tony@atomide.com> Cc: Vaibhav Hiremath <hvaibhav@ti.com> Cc: Benoît Cousson <b-cousson@ti.com> Cc: Tero Kristo <t-kristo@ti.com> Tested-by: Kevin Hilman <khilman@ti.com> Signed-off-by: Paul Walmsley <paul@pwsan.com> Signed-off-by: Kevin Hilman <khilman@ti.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-omap2/clockdomain.h4
-rw-r--r--arch/arm/mach-omap2/clockdomains2xxx_3xxx_data.c1
-rw-r--r--arch/arm/mach-omap2/clockdomains44xx_data.c2
-rw-r--r--arch/arm/mach-omap2/omap_hwmod.c32
4 files changed, 30 insertions, 9 deletions
diff --git a/arch/arm/mach-omap2/clockdomain.h b/arch/arm/mach-omap2/clockdomain.h
index f7b58609bad8..6227e9505c2d 100644
--- a/arch/arm/mach-omap2/clockdomain.h
+++ b/arch/arm/mach-omap2/clockdomain.h
@@ -31,12 +31,16 @@
31 * 31 *
32 * CLKDM_NO_AUTODEPS: Prevent "autodeps" from being added/removed from this 32 * CLKDM_NO_AUTODEPS: Prevent "autodeps" from being added/removed from this
33 * clockdomain. (Currently, this applies to OMAP3 clockdomains only.) 33 * clockdomain. (Currently, this applies to OMAP3 clockdomains only.)
34 * CLKDM_ACTIVE_WITH_MPU: The PRCM guarantees that this clockdomain is
35 * active whenever the MPU is active. True for interconnects and
36 * the WKUP clockdomains.
34 */ 37 */
35#define CLKDM_CAN_FORCE_SLEEP (1 << 0) 38#define CLKDM_CAN_FORCE_SLEEP (1 << 0)
36#define CLKDM_CAN_FORCE_WAKEUP (1 << 1) 39#define CLKDM_CAN_FORCE_WAKEUP (1 << 1)
37#define CLKDM_CAN_ENABLE_AUTO (1 << 2) 40#define CLKDM_CAN_ENABLE_AUTO (1 << 2)
38#define CLKDM_CAN_DISABLE_AUTO (1 << 3) 41#define CLKDM_CAN_DISABLE_AUTO (1 << 3)
39#define CLKDM_NO_AUTODEPS (1 << 4) 42#define CLKDM_NO_AUTODEPS (1 << 4)
43#define CLKDM_ACTIVE_WITH_MPU (1 << 5)
40 44
41#define CLKDM_CAN_HWSUP (CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_DISABLE_AUTO) 45#define CLKDM_CAN_HWSUP (CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_DISABLE_AUTO)
42#define CLKDM_CAN_SWSUP (CLKDM_CAN_FORCE_SLEEP | CLKDM_CAN_FORCE_WAKEUP) 46#define CLKDM_CAN_SWSUP (CLKDM_CAN_FORCE_SLEEP | CLKDM_CAN_FORCE_WAKEUP)
diff --git a/arch/arm/mach-omap2/clockdomains2xxx_3xxx_data.c b/arch/arm/mach-omap2/clockdomains2xxx_3xxx_data.c
index 839145e1cfbe..4972219653ce 100644
--- a/arch/arm/mach-omap2/clockdomains2xxx_3xxx_data.c
+++ b/arch/arm/mach-omap2/clockdomains2xxx_3xxx_data.c
@@ -88,4 +88,5 @@ struct clockdomain wkup_common_clkdm = {
88 .name = "wkup_clkdm", 88 .name = "wkup_clkdm",
89 .pwrdm = { .name = "wkup_pwrdm" }, 89 .pwrdm = { .name = "wkup_pwrdm" },
90 .dep_bit = OMAP_EN_WKUP_SHIFT, 90 .dep_bit = OMAP_EN_WKUP_SHIFT,
91 .flags = CLKDM_ACTIVE_WITH_MPU,
91}; 92};
diff --git a/arch/arm/mach-omap2/clockdomains44xx_data.c b/arch/arm/mach-omap2/clockdomains44xx_data.c
index c53425847493..7f2133abe7d3 100644
--- a/arch/arm/mach-omap2/clockdomains44xx_data.c
+++ b/arch/arm/mach-omap2/clockdomains44xx_data.c
@@ -381,7 +381,7 @@ static struct clockdomain l4_wkup_44xx_clkdm = {
381 .cm_inst = OMAP4430_PRM_WKUP_CM_INST, 381 .cm_inst = OMAP4430_PRM_WKUP_CM_INST,
382 .clkdm_offs = OMAP4430_PRM_WKUP_CM_WKUP_CDOFFS, 382 .clkdm_offs = OMAP4430_PRM_WKUP_CM_WKUP_CDOFFS,
383 .dep_bit = OMAP4430_L4WKUP_STATDEP_SHIFT, 383 .dep_bit = OMAP4430_L4WKUP_STATDEP_SHIFT,
384 .flags = CLKDM_CAN_HWSUP, 384 .flags = CLKDM_CAN_HWSUP | CLKDM_ACTIVE_WITH_MPU,
385}; 385};
386 386
387static struct clockdomain emu_sys_44xx_clkdm = { 387static struct clockdomain emu_sys_44xx_clkdm = {
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 773193670ea2..2d710f50fca2 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -1124,15 +1124,18 @@ static struct omap_hwmod_addr_space * __init _find_mpu_rt_addr_space(struct omap
1124 * _enable_sysc - try to bring a module out of idle via OCP_SYSCONFIG 1124 * _enable_sysc - try to bring a module out of idle via OCP_SYSCONFIG
1125 * @oh: struct omap_hwmod * 1125 * @oh: struct omap_hwmod *
1126 * 1126 *
1127 * If module is marked as SWSUP_SIDLE, force the module out of slave 1127 * Ensure that the OCP_SYSCONFIG register for the IP block represented
1128 * idle; otherwise, configure it for smart-idle. If module is marked 1128 * by @oh is set to indicate to the PRCM that the IP block is active.
1129 * as SWSUP_MSUSPEND, force the module out of master standby; 1129 * Usually this means placing the module into smart-idle mode and
1130 * otherwise, configure it for smart-standby. No return value. 1130 * smart-standby, but if there is a bug in the automatic idle handling
1131 * for the IP block, it may need to be placed into the force-idle or
1132 * no-idle variants of these modes. No return value.
1131 */ 1133 */
1132static void _enable_sysc(struct omap_hwmod *oh) 1134static void _enable_sysc(struct omap_hwmod *oh)
1133{ 1135{
1134 u8 idlemode, sf; 1136 u8 idlemode, sf;
1135 u32 v; 1137 u32 v;
1138 bool clkdm_act;
1136 1139
1137 if (!oh->class->sysc) 1140 if (!oh->class->sysc)
1138 return; 1141 return;
@@ -1141,8 +1144,16 @@ static void _enable_sysc(struct omap_hwmod *oh)
1141 sf = oh->class->sysc->sysc_flags; 1144 sf = oh->class->sysc->sysc_flags;
1142 1145
1143 if (sf & SYSC_HAS_SIDLEMODE) { 1146 if (sf & SYSC_HAS_SIDLEMODE) {
1144 idlemode = (oh->flags & HWMOD_SWSUP_SIDLE) ? 1147 clkdm_act = ((oh->clkdm &&
1145 HWMOD_IDLEMODE_NO : HWMOD_IDLEMODE_SMART; 1148 oh->clkdm->flags & CLKDM_ACTIVE_WITH_MPU) ||
1149 (oh->_clk && oh->_clk->clkdm &&
1150 oh->_clk->clkdm->flags & CLKDM_ACTIVE_WITH_MPU));
1151 if (clkdm_act && !(oh->class->sysc->idlemodes &
1152 (SIDLE_SMART | SIDLE_SMART_WKUP)))
1153 idlemode = HWMOD_IDLEMODE_FORCE;
1154 else
1155 idlemode = (oh->flags & HWMOD_SWSUP_SIDLE) ?
1156 HWMOD_IDLEMODE_NO : HWMOD_IDLEMODE_SMART;
1146 _set_slave_idlemode(oh, idlemode, &v); 1157 _set_slave_idlemode(oh, idlemode, &v);
1147 } 1158 }
1148 1159
@@ -1208,8 +1219,13 @@ static void _idle_sysc(struct omap_hwmod *oh)
1208 sf = oh->class->sysc->sysc_flags; 1219 sf = oh->class->sysc->sysc_flags;
1209 1220
1210 if (sf & SYSC_HAS_SIDLEMODE) { 1221 if (sf & SYSC_HAS_SIDLEMODE) {
1211 idlemode = (oh->flags & HWMOD_SWSUP_SIDLE) ? 1222 /* XXX What about HWMOD_IDLEMODE_SMART_WKUP? */
1212 HWMOD_IDLEMODE_FORCE : HWMOD_IDLEMODE_SMART; 1223 if (oh->flags & HWMOD_SWSUP_SIDLE ||
1224 !(oh->class->sysc->idlemodes &
1225 (SIDLE_SMART | SIDLE_SMART_WKUP)))
1226 idlemode = HWMOD_IDLEMODE_FORCE;
1227 else
1228 idlemode = HWMOD_IDLEMODE_SMART;
1213 _set_slave_idlemode(oh, idlemode, &v); 1229 _set_slave_idlemode(oh, idlemode, &v);
1214 } 1230 }
1215 1231