From 088e8806c411f76216002a8b37c7eb8563614822 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 30 Dec 2012 10:15:48 -0700 Subject: [PATCH 1/4] ARM: OMAP2xxx: PM: enter WFI via inline asm if CORE stays active There shouldn't be any need to jump to SRAM code if the OMAP CORE clockdomain (and consequently the SDRAM controller and CORE PLL) stays active during MPU WFI. The SRAM code should only be needed when the RAM enters self-refresh. So in the case where CORE stays active, just call WFI directly from the mach-omap2/pm24xx.c code. This removes some unnecessary SRAM code. This second version replaces the inline WFI with the corresponding coprocessor register call, using tlbflush.h as an example. This is because the assembler doesn't recognize WFI as a valid ARMv6 instruction. Signed-off-by: Paul Walmsley Cc: Richard Woodruff Cc: Kevin Hilman --- arch/arm/mach-omap2/pm24xx.c | 12 ++++++------ arch/arm/mach-omap2/sleep24xx.S | 19 ------------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/arch/arm/mach-omap2/pm24xx.c b/arch/arm/mach-omap2/pm24xx.c index c333fa6dffa8..8914b9e32ee7 100644 --- a/arch/arm/mach-omap2/pm24xx.c +++ b/arch/arm/mach-omap2/pm24xx.c @@ -54,7 +54,6 @@ #include "powerdomain.h" #include "clockdomain.h" -static void (*omap2_sram_idle)(void); static void (*omap2_sram_suspend)(u32 dllctrl, void __iomem *sdrc_dlla_ctrl, void __iomem *sdrc_power); @@ -172,6 +171,8 @@ static int omap2_allow_mpu_retention(void) static void omap2_enter_mpu_retention(void) { + const int zero = 0; + /* Putting MPU into the WFI state while a transfer is active * seems to cause the I2C block to timeout. Why? Good question. */ if (omap2_i2c_active()) @@ -196,7 +197,8 @@ static void omap2_enter_mpu_retention(void) OMAP2_PM_PWSTCTRL); } - omap2_sram_idle(); + /* WFI */ + asm("mcr p15, 0, %0, c7, c0, 4" : : "r" (zero) : "memory", "cc"); } static int omap2_can_sleep(void) @@ -356,11 +358,9 @@ int __init omap2_pm_init(void) /* * We copy the assembler sleep/wakeup routines to SRAM. * These routines need to be in SRAM as that's the only - * memory the MPU can see when it wakes up. + * memory the MPU can see when it wakes up after the entire + * chip enters idle. */ - omap2_sram_idle = omap_sram_push(omap24xx_idle_loop_suspend, - omap24xx_idle_loop_suspend_sz); - omap2_sram_suspend = omap_sram_push(omap24xx_cpu_suspend, omap24xx_cpu_suspend_sz); diff --git a/arch/arm/mach-omap2/sleep24xx.S b/arch/arm/mach-omap2/sleep24xx.S index ce0ccd26efbd..1d3cb25c9629 100644 --- a/arch/arm/mach-omap2/sleep24xx.S +++ b/arch/arm/mach-omap2/sleep24xx.S @@ -36,25 +36,6 @@ .text -/* - * Forces OMAP into idle state - * - * omap24xx_idle_loop_suspend() - This bit of code just executes the WFI - * for normal idles. - * - * Note: This code get's copied to internal SRAM at boot. When the OMAP - * wakes up it continues execution at the point it went to sleep. - */ - .align 3 -ENTRY(omap24xx_idle_loop_suspend) - stmfd sp!, {r0, lr} @ save registers on stack - mov r0, #0 @ clear for mcr setup - mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt - ldmfd sp!, {r0, pc} @ restore regs and return - -ENTRY(omap24xx_idle_loop_suspend_sz) - .word . - omap24xx_idle_loop_suspend - /* * omap24xx_cpu_suspend() - Forces OMAP into deep sleep state by completing * SDRC shutdown then ARM shutdown. Upon wake MPU is back on so just restore From db27c0c0d05cafc0eb0bed4d58daea65ed5e6839 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 30 Dec 2012 10:22:25 -0700 Subject: [PATCH 2/4] ARM: OMAP2+: hwmod: add support for blocking WFI when a device is active Apparently, on some OMAPs, the MPU can't be allowed to enter WFI while certain peripherals are active. It's not clear why, and it's likely that there is simply some other bug in the driver or integration code. But since the likelihood that anyone will have the time to track these problems down in the future seems quite small, we'll provide a flag, HWMOD_BLOCK_WFI, to mark these issues in the hwmod data. Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 8 ++++++++ arch/arm/mach-omap2/omap_hwmod.h | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 4653efb87a27..6804d474a47d 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -139,6 +139,8 @@ #include #include +#include + #include "clock.h" #include "omap_hwmod.h" @@ -2134,6 +2136,8 @@ static int _enable(struct omap_hwmod *oh) _enable_clocks(oh); if (soc_ops.enable_module) soc_ops.enable_module(oh); + if (oh->flags & HWMOD_BLOCK_WFI) + disable_hlt(); if (soc_ops.update_context_lost) soc_ops.update_context_lost(oh); @@ -2195,6 +2199,8 @@ static int _idle(struct omap_hwmod *oh) _idle_sysc(oh); _del_initiator_dep(oh, mpu_oh); + if (oh->flags & HWMOD_BLOCK_WFI) + enable_hlt(); if (soc_ops.disable_module) soc_ops.disable_module(oh); @@ -2303,6 +2309,8 @@ static int _shutdown(struct omap_hwmod *oh) if (oh->_state == _HWMOD_STATE_ENABLED) { _del_initiator_dep(oh, mpu_oh); /* XXX what about the other system initiators here? dma, dsp */ + if (oh->flags & HWMOD_BLOCK_WFI) + enable_hlt(); if (soc_ops.disable_module) soc_ops.disable_module(oh); _disable_clocks(oh); diff --git a/arch/arm/mach-omap2/omap_hwmod.h b/arch/arm/mach-omap2/omap_hwmod.h index 3ae852a522f9..80c00e706d69 100644 --- a/arch/arm/mach-omap2/omap_hwmod.h +++ b/arch/arm/mach-omap2/omap_hwmod.h @@ -451,6 +451,14 @@ struct omap_hwmod_omap4_prcm { * enabled. This prevents the hwmod code from being able to * enable and reset the IP block early. XXX Eventually it should * be possible to query the clock framework for this information. + * HWMOD_BLOCK_WFI: Some OMAP peripherals apparently don't work + * correctly if the MPU is allowed to go idle while the + * peripherals are active. This is apparently true for the I2C on + * OMAP2420, and also the EMAC on AM3517/3505. It's unlikely that + * this is really true -- we're probably not configuring something + * correctly, or this is being abused to deal with some PM latency + * issues -- but we're currently suffering from a shortage of + * folks who are able to track these issues down properly. */ #define HWMOD_SWSUP_SIDLE (1 << 0) #define HWMOD_SWSUP_MSTANDBY (1 << 1) @@ -462,6 +470,7 @@ struct omap_hwmod_omap4_prcm { #define HWMOD_CONTROL_OPT_CLKS_IN_RESET (1 << 7) #define HWMOD_16BIT_REG (1 << 8) #define HWMOD_EXT_OPT_MAIN_CLK (1 << 9) +#define HWMOD_BLOCK_WFI (1 << 10) /* * omap_hwmod._int_flags definitions From 1e3d8fe771881de323396aaa1efd20243fa974cb Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 30 Dec 2012 10:31:52 -0700 Subject: [PATCH 3/4] ARM: OMAP2420: hwmod data/PM: use hwmod to block WFI when I2C active Use the HWMOD_BLOCK_WFI flag in the hwmod data to prevent the MPU from entering WFI when the I2C devices are active. No idea why this is needed; this could certainly bear further investigation if anyone is interested. The objective here is to remove some custom code from the OMAP24xx PM code. Signed-off-by: Paul Walmsley Cc: Kevin Hilman --- arch/arm/mach-omap2/omap_hwmod_2420_data.c | 7 ++++++- arch/arm/mach-omap2/pm24xx.c | 13 ------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c index b5efe58c0be0..6a764af6c6d3 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c @@ -121,7 +121,12 @@ static struct omap_hwmod omap2420_i2c1_hwmod = { }, .class = &i2c_class, .dev_attr = &i2c_dev_attr, - .flags = HWMOD_16BIT_REG, + /* + * From mach-omap2/pm24xx.c: "Putting MPU into the WFI state + * while a transfer is active seems to cause the I2C block to + * timeout. Why? Good question." + */ + .flags = (HWMOD_16BIT_REG | HWMOD_BLOCK_WFI), }; /* I2C2 */ diff --git a/arch/arm/mach-omap2/pm24xx.c b/arch/arm/mach-omap2/pm24xx.c index 8914b9e32ee7..bc44bcd64451 100644 --- a/arch/arm/mach-omap2/pm24xx.c +++ b/arch/arm/mach-omap2/pm24xx.c @@ -139,14 +139,6 @@ no_sleep: return 0; } -static int omap2_i2c_active(void) -{ - u32 l; - - l = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); - return l & (OMAP2420_EN_I2C2_MASK | OMAP2420_EN_I2C1_MASK); -} - static int sti_console_enabled; static int omap2_allow_mpu_retention(void) @@ -173,11 +165,6 @@ static void omap2_enter_mpu_retention(void) { const int zero = 0; - /* Putting MPU into the WFI state while a transfer is active - * seems to cause the I2C block to timeout. Why? Good question. */ - if (omap2_i2c_active()) - return; - /* The peripherals seem not to be able to wake up the MPU when * it is in retention mode. */ if (omap2_allow_mpu_retention()) { From 814a18a5d026464f56b3616704b985f9942b29a6 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Wed, 6 Feb 2013 13:48:56 -0700 Subject: [PATCH 4/4] ARM: OMAP AM3517/05: hwmod data: block WFI when EMAC active According to Mark Greer, on OMAP AM3517/3505 chips, the EMAC is unable to wake the ARM up from WFI: http://www.spinics.net/lists/arm-kernel/msg174734.html Further troubleshooting was unable to narrow the problem down. So we don't have much choice other than to block WFI when the EMAC is active with the HWMOD_BLOCK_WFI flag. Based on Mark's original patch. We're removing the omap_device-based pm_lats code, so a different approach was needed. This third version contains some corrections thanks to Mark's review. Signed-off-by: Paul Walmsley Cc: Mark A. Greer Acked-by: Mark A. Greer --- arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 8bb2628df34e..ac7e03ec952f 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -3493,7 +3493,12 @@ static struct omap_hwmod am35xx_emac_hwmod = { .name = "davinci_emac", .mpu_irqs = am35xx_emac_mpu_irqs, .class = &am35xx_emac_class, - .flags = HWMOD_NO_IDLEST, + /* + * According to Mark Greer, the MPU will not return from WFI + * when the EMAC signals an interrupt. + * http://www.spinics.net/lists/arm-kernel/msg174734.html + */ + .flags = (HWMOD_NO_IDLEST | HWMOD_BLOCK_WFI), }; /* l3_core -> davinci emac interface */