Message ID | 1251391962-11217-1-git-send-email-thara@ti.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Kevin Hilman |
Headers | show |
* Thara Gopinath <thara@ti.com> [090827 09:54]: > This patch adds OSWR support for MPU/CORE domains in CPUidle. > Two new C states are being added to the existing C states > which makes the new states look like below. Please explain what OSWR means, the patch description should be clear to everybody. I guess you mean Open SWitch Retention? Tony -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
OSWR stands for Open Switch With Retention. This is one of the target power states where logic is lost for all the modules in the power domain except for the ones with Built in retention Flip Flops. This is the main difference between OFF and OSWR mode. Because of this feature, wake up latency for OSWR is lesser than OFF mode but more than CSWR (Closed Switch With Retention - where module logic retained). Vishwa -----Original Message----- From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-owner@vger.kernel.org] On Behalf Of Tony Lindgren Sent: Friday, August 28, 2009 9:37 PM To: Gopinath, Thara Cc: linux-omap@vger.kernel.org Subject: Re: [PATCH][RFC] OMAP3: PM: Adding OSWR support * Thara Gopinath <thara@ti.com> [090827 09:54]: > This patch adds OSWR support for MPU/CORE domains in CPUidle. > Two new C states are being added to the existing C states > which makes the new states look like below. Please explain what OSWR means, the patch description should be clear to everybody. I guess you mean Open SWitch Retention? Tony -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
> -----Original Message----- > From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-owner@vger.kernel.org] On Behalf Of > Sripathy, Vishwanath > Subject: RE: [PATCH][RFC] OMAP3: PM: Adding OSWR support > > OSWR stands for Open Switch With Retention. > This is one of the target power states where logic is lost for all the modules in the power domain > except for the ones with Built in retention Flip Flops. This is the main difference between OFF and So, this's supported for CORE, PER and MPU? -Girish -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Sripathy, Vishwanath wrote:
> OSWR stands for Open Switch With Retention.
Not to split hairs, but the OMAP3430 Technical Reference Manual states
that OSwR means Open Switch Retention. So no "with". This W always
bother me too!
Cheers
Jon
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Girish > >-----Original Message----- >From: Ghongdemath, Girish >Sent: Wednesday, September 02, 2009 5:03 AM >To: Sripathy, Vishwanath; 'Tony Lindgren'; Gopinath, Thara >Cc: linux-omap@vger.kernel.org >Subject: RE: [PATCH][RFC] OMAP3: PM: Adding OSWR support > > > >> -----Original Message----- >> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-owner@vger.kernel.org] On Behalf Of >> Sripathy, Vishwanath >> Subject: RE: [PATCH][RFC] OMAP3: PM: Adding OSWR support >> >> OSWR stands for Open Switch With Retention. >> This is one of the target power states where logic is lost for all the modules in the power domain >> except for the ones with Built in retention Flip Flops. This is the main difference between OFF and > >So, this's supported for CORE, PER and MPU? > >-Girish OSWR is supported for only for Core and MPU domains (in SW). PER will be in OFF state when Core is in OSWR. -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap- > owner@vger.kernel.org] On Behalf Of Hunter, Jon > Not to split hairs, but the OMAP3430 Technical Reference Manual states > that OSwR means Open Switch Retention. So no "with". This W always > bother me too! No bother with W, its part of switch. Underlying spec's have always defined it as following: OSWR stands for Open SWitch Retention CSWR stands for Closed SWitch Retention Regards, Richard W. -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Thara Gopinath <thara@ti.com> writes: > This patch adds OSWR support for MPU/CORE domains in CPUidle. > Two new C states are being added to the existing C states > which makes the new states look like below. > > C1 - MPU WFI + Core active > C2 - MPU WFI + Core inactive > C3 - MPU CSWR + Core inactive > C4 - MPU OFF + Core inactive > C5 - MPU CSWR + Core CSWR > C6 - MPU OFF + Core CSWR > C7 - MPU OSWR + CORE OSWR (New State) > C8 - MPU OFF + CORE OSWR (New State) > C9 - MPU OFF + CORE OFF > > To enable this feature echo 1 > /sys/powwer/enable_oswr_ret > To disable this feature echo 0 > /sys/poweer/enable_oswr_ret > > This patch depends on http://patchwork.kernel.org/patch/44290/ and > is validated on latest PM branch on OMAP3430 SDP. > > Signed-off-by: Thara Gopinath <thara@ti.com> Thara, This is a large and complex feature which should probably be broken up into bits for cleaner review/merge. First, the Changelog should summarize the OSWR feature and how it is different from current retention mode (CSWR) as well as the motivation for the feature. You might consider breaking it up as follows: - CPUidle max state change - UART prepare idle changes - powerdomain API additions: pwrdm_read_next_*_state() - context save/restore API changes to take target state - base OSWR logic in idle path - 'enable_oswr_ret' option - DPLL4 autoidle hack Some general comments: Can you move the /sys/power/enable_oswr_ret to /debug/pm_debug. I'll be moving the /sys/power/enable_off_mode there as well. Also, I think the name 'enable_oswr' is better than 'enable_oswr_ret'. The 'enable_oswr_ret' feature needs some work. The way it is done will work, but the CPUidle state accounting will not be correct as the target C-state will not be the same as the one actually entered. Sanjeev Premi was working on a way to use the C-state .valid flags to do something like this. Then both, enable_off_mode and enable_oswr can simply set/clear the .valid field of the C-state and the CPUidle enter hook can check the C-state valid flags. More comments below... > --- > arch/arm/mach-omap2/control.c | 20 +++ > arch/arm/mach-omap2/cpuidle34xx.c | 154 +++++++++++++++++++++++-- > arch/arm/mach-omap2/pm-debug.c | 3 + > arch/arm/mach-omap2/pm.c | 16 +++- > arch/arm/mach-omap2/pm.h | 1 + > arch/arm/mach-omap2/pm34xx.c | 119 +++++++++++++++----- > arch/arm/mach-omap2/powerdomain.c | 89 ++++++++++++++- > arch/arm/mach-omap2/powerdomains34xx.h | 2 + > arch/arm/mach-omap2/serial.c | 17 +-- > arch/arm/mach-omap2/sleep34xx.S | 7 +- > arch/arm/plat-omap/include/mach/control.h | 1 + > arch/arm/plat-omap/include/mach/powerdomain.h | 4 + > arch/arm/plat-omap/include/mach/serial.h | 2 +- > include/linux/cpuidle.h | 2 +- > 14 files changed, 379 insertions(+), 58 deletions(-) > > diff --git a/arch/arm/mach-omap2/control.c b/arch/arm/mach-omap2/control.c > index c9407c0..dc90207 100644 > --- a/arch/arm/mach-omap2/control.c > +++ b/arch/arm/mach-omap2/control.c > @@ -331,6 +331,26 @@ void omap3_save_scratchpad_contents(void) > sizeof(sdrc_block_contents), &arm_context_addr, 4); > } > > +void omap3_scratchpad_dpll4autoidle(int enable) > +{ > + void * __iomem scratchpad_address; > + struct omap3_scratchpad_prcm_block prcm_block_contents; > + > + scratchpad_address = OMAP2_IO_ADDRESS(OMAP343X_SCRATCHPAD); > + > + memcpy_fromio(&prcm_block_contents, scratchpad_address + 0x2C, > + sizeof(prcm_block_contents)); > + if (enable) > + prcm_block_contents.cm_autoidle_pll |= > + (1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT); > + else > + prcm_block_contents.cm_autoidle_pll &= > + ~OMAP3430_AUTO_PERIPH_DPLL_MASK; > + > + memcpy_toio(scratchpad_address + 0x2C, &prcm_block_contents, > + sizeof(prcm_block_contents)); > +} > + Do you need to read then write the whole block just to change one value? > void omap3_control_save_context(void) > { > control_context.sysconfig = omap_ctrl_readl(OMAP2_CONTROL_SYSCONFIG); > diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c > index 7bbec90..a5b811b 100644 > --- a/arch/arm/mach-omap2/cpuidle34xx.c > +++ b/arch/arm/mach-omap2/cpuidle34xx.c > @@ -36,14 +36,17 @@ > > #ifdef CONFIG_CPU_IDLE > > -#define OMAP3_MAX_STATES 7 > +#define OMAP3_MAX_STATES 9 > #define OMAP3_STATE_C1 0 /* C1 - MPU WFI + Core active */ > #define OMAP3_STATE_C2 1 /* C2 - MPU WFI + Core inactive */ > #define OMAP3_STATE_C3 2 /* C3 - MPU CSWR + Core inactive */ > -#define OMAP3_STATE_C4 3 /* C4 - MPU OFF + Core iactive */ > -#define OMAP3_STATE_C5 4 /* C5 - MPU RET + Core RET */ > -#define OMAP3_STATE_C6 5 /* C6 - MPU OFF + Core RET */ > -#define OMAP3_STATE_C7 6 /* C7 - MPU OFF + Core OFF */ > +#define OMAP3_STATE_C4 3 /* C4 - MPU OFF + Core inactive */ > +#define OMAP3_STATE_C5 4 /* C5 - MPU CSWR + Core CSWR */ > +#define OMAP3_STATE_C6 5 /* C6 - MPU OFF + Core CSWR */ > +#define OMAP3_STATE_C7 6 /* C7 - MPU OSWR + CORE OSWR */ > +#define OMAP3_STATE_C8 7 /* C8 - MPU OFF + CORE OSWR */ > +#define OMAP3_STATE_C9 8 /* C9 - MPU OFF + CORE OFF */ > + > > struct omap3_processor_cx { > u8 valid; > @@ -52,6 +55,11 @@ struct omap3_processor_cx { > u32 wakeup_latency; > u32 mpu_state; > u32 core_state; > + u32 mpu_logicl1_ret_state; > + u32 mpu_l2cache_ret_state; > + u32 core_logic_state; > + u32 core_mem1_ret_state; > + u32 core_mem2_ret_state; > u32 threshold; > u32 flags; > }; > @@ -95,6 +103,11 @@ static int omap3_enter_idle(struct cpuidle_device *dev, > struct omap3_processor_cx *cx = cpuidle_get_statedata(state); > struct timespec ts_preidle, ts_postidle, ts_idle; > u32 mpu_state = cx->mpu_state, core_state = cx->core_state; > + u32 mpu_logicl1_ret_state = cx->mpu_logicl1_ret_state; > + u32 mpu_l2cache_ret_state = cx->mpu_l2cache_ret_state; > + u32 core_logic_state = cx->core_logic_state; > + u32 core_mem1_ret_state = cx->core_mem1_ret_state; > + u32 core_mem2_ret_state = cx->core_mem2_ret_state; > > current_cx_state = *cx; > > @@ -111,6 +124,34 @@ static int omap3_enter_idle(struct cpuidle_device *dev, > core_state = PWRDM_POWER_RET; > } > > + if (!enable_oswr_ret) { > + if (mpu_logicl1_ret_state == PWRDM_POWER_OFF) > + mpu_logicl1_ret_state = PWRDM_POWER_RET; > + if (mpu_l2cache_ret_state == PWRDM_POWER_OFF) > + mpu_l2cache_ret_state = PWRDM_POWER_RET; > + if (core_logic_state == PWRDM_POWER_OFF) > + core_logic_state = PWRDM_POWER_RET; > + if (core_mem1_ret_state == PWRDM_POWER_OFF) > + core_mem1_ret_state = PWRDM_POWER_RET; > + if (core_mem2_ret_state == PWRDM_POWER_OFF) > + core_mem2_ret_state = PWRDM_POWER_RET; > + } > + > + if (mpu_logicl1_ret_state != 0xFF) Is there any reason you don't init these to PWRDM_POWER_ON in omap_init_power_states() and then always set them here? The 0xFF stuff is rather confusing. > + pwrdm_set_logic_retst(mpu_pd, mpu_logicl1_ret_state); > + > + if (mpu_l2cache_ret_state != 0xFF) > + pwrdm_set_mem_retst(mpu_pd, 0, mpu_l2cache_ret_state); Hmm, so for C-states that have the state set to 0xFF, the logic state will remain the value from the previous state. This seems wrong to me. I think this will be much clearer if every C-state has a valid state and these are set always. > + if (core_logic_state != 0xFF) > + pwrdm_set_logic_retst(core_pd, core_logic_state); > + > + if (core_mem1_ret_state != 0xFF) > + pwrdm_set_mem_retst(core_pd, 0, core_mem1_ret_state); > + > + if (core_mem2_ret_state != 0xFF) > + pwrdm_set_mem_retst(core_pd, 1, core_mem2_ret_state); > + > pwrdm_set_next_pwrst(mpu_pd, mpu_state); > pwrdm_set_next_pwrst(core_pd, core_state); > > @@ -174,10 +215,24 @@ DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev); > * C4 . MPU OFF + Core inactive > * C5 . MPU CSWR + Core CSWR > * C6 . MPU OFF + Core CSWR > - * C7 . MPU OFF + Core OFF > + * C7 . MPU OSWR + Core OSWR > + * C8 . MPU OFF + Core OSWR > + * C9 . MPU OFF + Core OFF > */ > void omap_init_power_states(void) > { > + int i; > + struct omap3_processor_cx *cx; > + > + for (i = OMAP3_STATE_C1; i < OMAP3_MAX_STATES; i++) { > + cx = &omap3_power_states[i]; > + cx->mpu_logicl1_ret_state = 0xFF; > + cx->mpu_l2cache_ret_state = 0xFF; > + cx->core_logic_state = 0xFF; > + cx->core_mem1_ret_state = 0xFF; > + cx->core_mem2_ret_state = 0xFF; > + } > > /* C1 . MPU WFI + Core active */ > omap3_power_states[OMAP3_STATE_C1].valid = 1; > omap3_power_states[OMAP3_STATE_C1].type = OMAP3_STATE_C1; > @@ -206,6 +261,10 @@ void omap_init_power_states(void) > omap3_power_states[OMAP3_STATE_C3].threshold = 300; > omap3_power_states[OMAP3_STATE_C3].mpu_state = PWRDM_POWER_RET; > omap3_power_states[OMAP3_STATE_C3].core_state = PWRDM_POWER_ON; > + omap3_power_states[OMAP3_STATE_C3].mpu_logicl1_ret_state = > + PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C3].mpu_l2cache_ret_state = > + PWRDM_POWER_RET; > omap3_power_states[OMAP3_STATE_C3].flags = CPUIDLE_FLAG_TIME_VALID | > CPUIDLE_FLAG_CHECK_BM; > > @@ -217,6 +276,10 @@ void omap_init_power_states(void) > omap3_power_states[OMAP3_STATE_C4].threshold = 4000; > omap3_power_states[OMAP3_STATE_C4].mpu_state = PWRDM_POWER_OFF; > omap3_power_states[OMAP3_STATE_C4].core_state = PWRDM_POWER_ON; > + omap3_power_states[OMAP3_STATE_C4].mpu_logicl1_ret_state = > + PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C4].mpu_l2cache_ret_state = > + PWRDM_POWER_RET; > omap3_power_states[OMAP3_STATE_C4].flags = CPUIDLE_FLAG_TIME_VALID | > CPUIDLE_FLAG_CHECK_BM; > > @@ -228,6 +291,15 @@ void omap_init_power_states(void) > omap3_power_states[OMAP3_STATE_C5].threshold = 12000; > omap3_power_states[OMAP3_STATE_C5].mpu_state = PWRDM_POWER_RET; > omap3_power_states[OMAP3_STATE_C5].core_state = PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C5].mpu_logicl1_ret_state = > + PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C5].mpu_l2cache_ret_state = > + PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C5].core_logic_state = PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C5].core_mem1_ret_state = > + PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C5].core_mem2_ret_state = > + PWRDM_POWER_RET; > omap3_power_states[OMAP3_STATE_C5].flags = CPUIDLE_FLAG_TIME_VALID | > CPUIDLE_FLAG_CHECK_BM; > > @@ -239,19 +311,77 @@ void omap_init_power_states(void) > omap3_power_states[OMAP3_STATE_C6].threshold = 15000; > omap3_power_states[OMAP3_STATE_C6].mpu_state = PWRDM_POWER_OFF; > omap3_power_states[OMAP3_STATE_C6].core_state = PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C6].mpu_logicl1_ret_state = > + PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C6].mpu_l2cache_ret_state = > + PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C6].core_logic_state = PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C6].core_mem1_ret_state = > + PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C6].core_mem2_ret_state = > + PWRDM_POWER_RET; > omap3_power_states[OMAP3_STATE_C6].flags = CPUIDLE_FLAG_TIME_VALID | > CPUIDLE_FLAG_CHECK_BM; > > - /* C7 . MPU OFF + Core OFF */ > + /* C7 . MPU OSWR + Core OSWR */ > omap3_power_states[OMAP3_STATE_C7].valid = 1; > omap3_power_states[OMAP3_STATE_C7].type = OMAP3_STATE_C7; > - omap3_power_states[OMAP3_STATE_C7].sleep_latency = 10000; > - omap3_power_states[OMAP3_STATE_C7].wakeup_latency = 30000; > - omap3_power_states[OMAP3_STATE_C7].threshold = 300000; > - omap3_power_states[OMAP3_STATE_C7].mpu_state = PWRDM_POWER_OFF; > - omap3_power_states[OMAP3_STATE_C7].core_state = PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C7].sleep_latency = 4000; > + omap3_power_states[OMAP3_STATE_C7].wakeup_latency = 9000; > + omap3_power_states[OMAP3_STATE_C7].threshold = 18000; > + omap3_power_states[OMAP3_STATE_C7].mpu_state = PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C7].core_state = PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C7].mpu_logicl1_ret_state = > + PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C7].mpu_l2cache_ret_state = > + PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C7].core_logic_state = PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C7].core_mem1_ret_state = > + PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C7].core_mem2_ret_state = > + PWRDM_POWER_OFF; > omap3_power_states[OMAP3_STATE_C7].flags = CPUIDLE_FLAG_TIME_VALID | > CPUIDLE_FLAG_CHECK_BM; > + > + /* C8 . MPU OFF + Core OSWR */ > + omap3_power_states[OMAP3_STATE_C8].valid = 1; > + omap3_power_states[OMAP3_STATE_C8].type = OMAP3_STATE_C7; > + omap3_power_states[OMAP3_STATE_C8].sleep_latency = 8000; > + omap3_power_states[OMAP3_STATE_C8].wakeup_latency = 25000; > + omap3_power_states[OMAP3_STATE_C8].threshold = 250000; > + omap3_power_states[OMAP3_STATE_C8].mpu_state = PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C8].core_state = PWRDM_POWER_RET; > + omap3_power_states[OMAP3_STATE_C8].mpu_logicl1_ret_state = > + PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C8].mpu_l2cache_ret_state = > + PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C8].core_logic_state = PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C8].core_mem1_ret_state = > + PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C8].core_mem2_ret_state = > + PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C8].flags = CPUIDLE_FLAG_TIME_VALID | > + CPUIDLE_FLAG_CHECK_BM; > + > + /* C9 . MPU OFF + Core OFF */ > + omap3_power_states[OMAP3_STATE_C9].valid = 1; > + omap3_power_states[OMAP3_STATE_C9].type = OMAP3_STATE_C7; > + omap3_power_states[OMAP3_STATE_C9].sleep_latency = 10000; > + omap3_power_states[OMAP3_STATE_C9].wakeup_latency = 30000; > + omap3_power_states[OMAP3_STATE_C9].threshold = 300000; > + omap3_power_states[OMAP3_STATE_C9].mpu_state = PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C9].core_state = PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C9].mpu_logicl1_ret_state = > + PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C9].mpu_l2cache_ret_state = > + PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C9].core_logic_state = PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C9].core_mem1_ret_state = > + PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C9].core_mem2_ret_state = > + PWRDM_POWER_OFF; > + omap3_power_states[OMAP3_STATE_C9].flags = CPUIDLE_FLAG_TIME_VALID | > + CPUIDLE_FLAG_CHECK_BM; > } > > struct cpuidle_driver omap3_idle_driver = { > diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c > index 1b4c160..ed7eb44 100644 > --- a/arch/arm/mach-omap2/pm-debug.c > +++ b/arch/arm/mach-omap2/pm-debug.c > @@ -383,6 +383,9 @@ static int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user) > for (i = 0; i < 4; i++) > seq_printf(s, ",%s:%d", pwrdm_state_names[i], > pwrdm->state_counter[i]); > + seq_printf(s, ",RET-LOGIC-OFF:%d,RET-MEM-OFF:%d", > + pwrdm->ret_logic_off_counter, > + pwrdm->ret_mem_off_counter); > > seq_printf(s, "\n"); > > diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c > index fec7d00..5e73613 100644 > --- a/arch/arm/mach-omap2/pm.c > +++ b/arch/arm/mach-omap2/pm.c > @@ -40,6 +40,7 @@ > unsigned short enable_dyn_sleep; > unsigned short clocks_off_while_idle; > unsigned short enable_off_mode; > +unsigned short enable_oswr_ret; > unsigned short voltage_off_while_idle; > unsigned short wakeup_timer_seconds; > atomic_t sleep_block = ATOMIC_INIT(0); > @@ -57,6 +58,9 @@ static struct kobj_attribute clocks_off_while_idle_attr = > static struct kobj_attribute enable_off_mode_attr = > __ATTR(enable_off_mode, 0644, idle_show, idle_store); > > +static struct kobj_attribute enable_oswr_ret_attr = > + __ATTR(enable_oswr_ret, 0644, idle_show, idle_store); > + > static struct kobj_attribute voltage_off_while_idle_attr = > __ATTR(voltage_off_while_idle, 0644, idle_show, idle_store); > > @@ -88,6 +92,8 @@ static ssize_t idle_show(struct kobject *kobj, struct kobj_attribute *attr, > return sprintf(buf, "%hu\n", clocks_off_while_idle); > else if (attr == &enable_off_mode_attr) > return sprintf(buf, "%hu\n", enable_off_mode); > + else if (attr == &enable_oswr_ret_attr) > + return sprintf(buf, "%hu\n", enable_oswr_ret); > else if (attr == &voltage_off_while_idle_attr) > return sprintf(buf, "%hu\n", voltage_off_while_idle); > else if (attr == &wakeup_timer_seconds_attr) > @@ -113,7 +119,9 @@ static ssize_t idle_store(struct kobject *kobj, struct kobj_attribute *attr, > } else if (attr == &enable_off_mode_attr) { > enable_off_mode = value; > omap3_pm_off_mode_enable(enable_off_mode); > - } else if (attr == &wakeup_timer_seconds_attr) { > + } else if (attr == &enable_oswr_ret_attr) > + enable_oswr_ret = value; > + else if (attr == &wakeup_timer_seconds_attr) { > wakeup_timer_seconds = value; > } else if (attr == &voltage_off_while_idle_attr) { > voltage_off_while_idle = value; > @@ -240,6 +248,12 @@ static int __init omap_pm_init(void) > return error; > } > error = sysfs_create_file(power_kobj, > + &enable_oswr_ret_attr.attr); > + if (error) { > + printk(KERN_ERR "sysfs_create_file failed: %d\n", error); > + return error; > + } > + error = sysfs_create_file(power_kobj, > &wakeup_timer_seconds_attr.attr); > if (error) > printk(KERN_ERR "sysfs_create_file failed: %d\n", error); > diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h > index 052c601..1e5eb74 100644 > --- a/arch/arm/mach-omap2/pm.h > +++ b/arch/arm/mach-omap2/pm.h > @@ -16,6 +16,7 @@ > extern void *omap3_secure_ram_storage; > extern unsigned short enable_dyn_sleep; > extern unsigned short enable_off_mode; > +extern unsigned short enable_oswr_ret; > extern unsigned short voltage_off_while_idle; > > struct prm_setup_vc { > diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c > index 0150f29..99605e7 100644 > --- a/arch/arm/mach-omap2/pm34xx.c > +++ b/arch/arm/mach-omap2/pm34xx.c > @@ -132,31 +132,41 @@ static void omap3_disable_io_chain(void) > prm_clear_mod_reg_bits(OMAP3430_EN_IO_CHAIN, WKUP_MOD, PM_WKEN); > } > > -static void omap3_core_save_context(void) > +static void omap3_core_save_context(int core_state) > { > - u32 control_padconf_off; > - /* Save the padconf registers */ > - control_padconf_off = > - omap_ctrl_readl(OMAP343X_CONTROL_PADCONF_OFF); > - control_padconf_off |= START_PADCONF_SAVE; > - omap_ctrl_writel(control_padconf_off, OMAP343X_CONTROL_PADCONF_OFF); > - /* wait for the save to complete */ > - while (!omap_ctrl_readl(OMAP343X_CONTROL_GENERAL_PURPOSE_STATUS) > - & PADCONF_SAVE_DONE) > - ; > + if (core_state == PWRDM_POWER_OFF) { > + u32 control_padconf_off; add empty line (you inherited this from moved code, but might as well fix it) > + /* Save the padconf registers */ > + control_padconf_off = > + omap_ctrl_readl(OMAP343X_CONTROL_PADCONF_OFF); indentation (ditto) > + control_padconf_off |= START_PADCONF_SAVE; > + omap_ctrl_writel(control_padconf_off, > + OMAP343X_CONTROL_PADCONF_OFF); > + /* wait for the save to complete */ > + while (!omap_ctrl_readl(OMAP343X_CONTROL_GENERAL_PURPOSE_STATUS) > + & PADCONF_SAVE_DONE) > + ; > + /* Save the system control module context, > + * padconf already save above > + */ > + omap3_control_save_context(); > + > + } > /* Save the Interrupt controller context */ > omap3_intc_save_context(); > /* Save the GPMC context */ > omap3_gpmc_save_context(); > - /* Save the system control module context, padconf already save above*/ > - omap3_control_save_context(); You changed the save sequence here by moving the control module save before INTC and GPMC. Are you sure this is safe? > omap_dma_global_context_save(); > } > > -static void omap3_core_restore_context(void) > +static void omap3_core_restore_context(int core_state) > { > - /* Restore the control module context, padconf restored by h/w */ > - omap3_control_restore_context(); > + if (core_state == PWRDM_POWER_OFF) > + /* Restore the control module context, > + * padconf restored by h/w > + */ > + omap3_control_restore_context(); > + > /* Restore the GPMC context */ > omap3_gpmc_restore_context(); > /* Restore the interrupt controller context */ > @@ -343,7 +353,8 @@ void omap_sram_idle(void) > int mpu_next_state = PWRDM_POWER_ON; > int per_next_state = PWRDM_POWER_ON; > int core_next_state = PWRDM_POWER_ON; > - int core_prev_state, per_prev_state; > + int mpu_prev_state, core_prev_state, per_prev_state; > + int mpu_logic_state, mpu_mem_state, core_logic_state, core_mem_state; > u32 sdrc_pwr = 0; > int per_state_modified = 0; > > @@ -356,11 +367,24 @@ void omap_sram_idle(void) > pwrdm_clear_all_prev_pwrst(per_pwrdm); > > mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); > + mpu_logic_state = pwrdm_read_next_logic_pwrst(mpu_pwrdm); > + mpu_mem_state = pwrdm_read_next_mem_pwrst(mpu_pwrdm, 0); > + > switch (mpu_next_state) { > case PWRDM_POWER_ON: > + /* No need to save context */ > + save_state = 0; > + break; > case PWRDM_POWER_RET: > - /* No need to save context */ > - save_state = 0; > + if (!mpu_logic_state && !mpu_mem_state) > + save_state = 3; > + else if (!mpu_mem_state) > + save_state = 2; > + else if (!mpu_logic_state) > + save_state = 1; > + else > + /* No need to save context */ > + save_state = 0; > break; > case PWRDM_POWER_OFF: > save_state = 3; > @@ -380,8 +404,11 @@ void omap_sram_idle(void) > /* PER */ > per_next_state = pwrdm_read_next_pwrst(per_pwrdm); > core_next_state = pwrdm_read_next_pwrst(core_pwrdm); > + core_logic_state = pwrdm_read_next_logic_pwrst(core_pwrdm); > + core_mem_state = pwrdm_read_next_mem_pwrst(core_pwrdm, 0) | > + pwrdm_read_next_mem_pwrst(core_pwrdm, 1); > if (per_next_state < PWRDM_POWER_ON) { > - omap_uart_prepare_idle(2); > + omap_uart_prepare_idle(2, per_next_state); > omap2_gpio_prepare_for_idle(per_next_state); > if (per_next_state == PWRDM_POWER_OFF) { > if (core_next_state == PWRDM_POWER_ON) { > @@ -401,24 +428,45 @@ void omap_sram_idle(void) > /* Disable smartreflex before entering WFI */ > disable_smartreflex(SR1); > disable_smartreflex(SR2); > - omap_uart_prepare_idle(0); > - omap_uart_prepare_idle(1); > + omap_uart_prepare_idle(0, core_next_state & core_logic_state); > + omap_uart_prepare_idle(1, core_next_state & core_logic_state); > if (core_next_state == PWRDM_POWER_OFF) { > prm_set_mod_reg_bits(OMAP3430_AUTO_OFF, > OMAP3430_GR_MOD, > OMAP3_PRM_VOLTCTRL_OFFSET); > - omap3_core_save_context(); > + omap3_core_save_context(PWRDM_POWER_OFF); > + omap3_prcm_save_context(); > + } else if ((core_next_state == PWRDM_POWER_RET) && > + (core_logic_state == PWRDM_POWER_OFF) && > + (core_mem_state == PWRDM_POWER_OFF)) { > + omap3_core_save_context(PWRDM_POWER_RET); > omap3_prcm_save_context(); > + /* > + * This is a hack. Currently OSWR does not > + * work if rom code restores DPLL4 to non > + * auto idle mode. > + * ROM restore takes 20mS longer if PER/DPLL4 > + * idle is enabled before OFF.So it is typically > + * not enabled. Since OSWR hangs if it is not enabled > + * enable it for OSWR alone. Later in the restore path > + * it is disabled again > + */ > + > + omap3_scratchpad_dpll4autoidle(1); > + prm_set_mod_reg_bits(OMAP3430_AUTO_RET, > + OMAP3430_GR_MOD, > + OMAP3_PRM_VOLTCTRL_OFFSET); > + > } else if (core_next_state == PWRDM_POWER_RET) { > prm_set_mod_reg_bits(OMAP3430_AUTO_RET, > OMAP3430_GR_MOD, > OMAP3_PRM_VOLTCTRL_OFFSET); > } > + stray whitespace change > /* Enable IO-PAD and IO-CHAIN wakeups */ > prm_set_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN); > omap3_enable_io_chain(); > } > - ditto > /* > * On EMU/HS devices ROM code restores a SRDC value > * from scratchpad which has automatic self refresh on timeout > @@ -447,18 +495,33 @@ void omap_sram_idle(void) > core_next_state == PWRDM_POWER_OFF) > sdrc_write_reg(sdrc_pwr, SDRC_POWER); > > + mpu_prev_state = pwrdm_read_prev_pwrst(mpu_pwrdm); > + > /* Restore table entry modified during MMU restoration */ > - if (pwrdm_read_prev_pwrst(mpu_pwrdm) == PWRDM_POWER_OFF) > + if (((mpu_prev_state == PWRDM_POWER_RET) && > + (pwrdm_read_prev_logic_pwrst(mpu_pwrdm) == > + PWRDM_POWER_OFF)) || > + (mpu_prev_state == PWRDM_POWER_OFF)) > restore_table_entry(); > - ditto > /* CORE */ > if (core_next_state < PWRDM_POWER_ON) { > core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm); > - if (core_prev_state == PWRDM_POWER_OFF) { > - omap3_core_restore_context(); > + if ((core_prev_state == PWRDM_POWER_OFF) || > + (core_prev_state == PWRDM_POWER_RET && > + pwrdm_read_prev_logic_pwrst(core_pwrdm) == > + PWRDM_POWER_OFF)) { > + omap3_core_restore_context(core_prev_state); > omap3_prcm_restore_context(); > omap3_sram_restore_context(); > omap2_sms_restore_context(); > + /* > + * For OSWR to work we put PER DPLL in auto > + * idle mode in scratchpad. Clear it so that > + * next time if a OFF is attempted the ROM restore > + * does nt take long > + */ > + if (core_prev_state == PWRDM_POWER_RET) > + omap3_scratchpad_dpll4autoidle(0); > } > omap_uart_resume_idle(0); > omap_uart_resume_idle(1); > diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c > index 6c5fee9..ebd8649 100644 > --- a/arch/arm/mach-omap2/powerdomain.c > +++ b/arch/arm/mach-omap2/powerdomain.c > @@ -128,6 +128,16 @@ static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag) > prev = pwrdm_read_prev_pwrst(pwrdm); > if (pwrdm->state != prev) > pwrdm->state_counter[prev]++; > + if (prev == PWRDM_POWER_RET) { > + if ((pwrdm->pwrsts_logic_ret == PWRSTS_OFF_RET) && > + (pwrdm_read_prev_logic_pwrst(pwrdm) == > + PWRDM_POWER_OFF)) > + pwrdm->ret_logic_off_counter++; > + if ((pwrdm->pwrsts_mem_ret[0] == PWRSTS_OFF_RET) && > + (pwrdm_read_prev_mem_pwrst(pwrdm, 0) == > + PWRDM_POWER_OFF)) > + pwrdm->ret_mem_off_counter++; > + } > break; > default: > return -EINVAL; > @@ -162,7 +172,8 @@ static __init void _pwrdm_setup(struct powerdomain *pwrdm) > > for (i = 0; i < 4; i++) > pwrdm->state_counter[i] = 0; > - ditto > + pwrdm->ret_logic_off_counter = 0; > + pwrdm->ret_mem_off_counter = 0; > pwrdm_wait_transition(pwrdm); > pwrdm->state = pwrdm_read_pwrst(pwrdm); > pwrdm->state_counter[pwrdm->state] = 1; > @@ -951,6 +962,30 @@ int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) > } > > /** > + * pwrdm_read_next_logic_pwrst - get next powerdomain logic power state > + * @pwrdm: struct powerdomain * to get next logic power state > + * > + * Return the powerdomain pwrdm's logic power state. Returns -EINVAL > + * if the powerdomain pointer is null or returns the next logic > + * power state upon success. > + */ > +int pwrdm_read_next_logic_pwrst(struct powerdomain *pwrdm) > +{ > + if (!pwrdm) > + return -EINVAL; > + > + /* > + * The register bit names below may not correspond to the > + * actual names of the bits in each powerdomain's register, > + * but the type of value returned is the same for each > + * powerdomain. > + */ > + return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTCTRL, > + OMAP3430_LOGICSTATEST); > +} > + > + > +/** > * pwrdm_read_mem_pwrst - get current memory bank power state > * @pwrdm: struct powerdomain * to get current memory bank power state > * @bank: memory bank number (0-3) > @@ -976,7 +1011,7 @@ int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) > * in PWST . So the hack. Think of a cleaner > * way of doing this > */ > - if (cpu_is_omap34xx) > + if (cpu_is_omap34xx()) These can be dropped as they were fixed in ver 2 of your previous patch. > if (!strcmp("mpu_pwrdm", pwrdm->name)) > bank = 1; > > @@ -1033,7 +1068,7 @@ int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) > * in PREPWST . So the hack. Think of a cleaner > * way of doing this > */ > - if (cpu_is_omap34xx) > + if (cpu_is_omap34xx()) ditto > if (!strcmp("mpu_pwrdm", pwrdm->name)) > bank = 1; > /* > @@ -1065,6 +1100,54 @@ int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) > } > > /** > + * pwrdm_read_next_mem_pwrst - get next memory bank power state > + * @pwrdm: struct powerdomain * to get mext memory bank power state > + * @bank: memory bank number (0-3) > + * > + * Return the powerdomain pwrdm's next memory power state for bank > + * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if > + * the target memory bank does not exist or is not controllable, or > + * returns the next memory power state upon success. > + */ > +int pwrdm_read_next_mem_pwrst(struct powerdomain *pwrdm, u8 bank) > +{ > + u32 m; > + > + if (!pwrdm) > + return -EINVAL; > + > + if (pwrdm->banks < (bank + 1)) > + return -EEXIST; > + > + /* > + * The register bit names below may not correspond to the > + * actual names of the bits in each powerdomain's register, > + * but the type of value returned is the same for each > + * powerdomain. > + */ > + switch (bank) { > + case 0: > + m = OMAP3430_SHAREDL1CACHEFLATRETSTATE; > + break; > + case 1: > + m = OMAP3430_L1FLATMEMRETSTATE; > + break; > + case 2: > + m = OMAP3430_SHAREDL2CACHEFLATRETSTATE; > + break; > + case 3: > + m = OMAP3430_SHAREDL2CACHEFLATRETSTATE; > + break; > + default: > + WARN_ON(1); /* should never happen */ > + return -EEXIST; > + } > + > + return prm_read_mod_bits_shift(pwrdm->prcm_offs, > + PM_PWSTCTRL, m); > +} > + > +/** > * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm > * @pwrdm: struct powerdomain * to clear > * > diff --git a/arch/arm/mach-omap2/powerdomains34xx.h b/arch/arm/mach-omap2/powerdomains34xx.h > index aa557b2..e3d470d 100644 > --- a/arch/arm/mach-omap2/powerdomains34xx.h > +++ b/arch/arm/mach-omap2/powerdomains34xx.h > @@ -207,6 +207,7 @@ static struct powerdomain core_34xx_pre_es3_1_pwrdm = { > CHIP_IS_OMAP3430ES2 | > CHIP_IS_OMAP3430ES3_0), > .pwrsts = PWRSTS_OFF_RET_ON, > + .pwrsts_logic_ret = PWRSTS_OFF_RET, > .dep_bit = OMAP3430_EN_CORE_SHIFT, > .banks = 2, > .pwrsts_mem_ret = { > @@ -225,6 +226,7 @@ static struct powerdomain core_34xx_es3_1_pwrdm = { > .prcm_offs = CORE_MOD, > .omap_chip = OMAP_CHIP_INIT(CHIP_GE_OMAP3430ES3_1), > .pwrsts = PWRSTS_OFF_RET_ON, > + .pwrsts_logic_ret = PWRSTS_OFF_RET, > .dep_bit = OMAP3430_EN_CORE_SHIFT, > .flags = PWRDM_HAS_HDWR_SAR, /* for USBTLL only */ > .banks = 2, > diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c > index 858447f..4b139b7 100644 > --- a/arch/arm/mach-omap2/serial.c > +++ b/arch/arm/mach-omap2/serial.c > @@ -164,9 +164,6 @@ static void omap_uart_save_context(struct omap_uart_state *uart) > u16 lcr = 0; > struct plat_serial8250_port *p = uart->p; > > - if (!enable_off_mode) > - return; > - > lcr = serial_read_reg(p, UART_LCR); > serial_write_reg(p, UART_LCR, 0xBF); > uart->dll = serial_read_reg(p, UART_DLL); > @@ -185,9 +182,6 @@ static void omap_uart_restore_context(struct omap_uart_state *uart) > u16 efr = 0; > struct plat_serial8250_port *p = uart->p; > > - if (!enable_off_mode) > - return; > - > if (!uart->context_valid) > return; > > @@ -231,12 +225,13 @@ static inline void omap_uart_enable_clocks(struct omap_uart_state *uart) > > #ifdef CONFIG_PM > > -static inline void omap_uart_disable_clocks(struct omap_uart_state *uart) > +static inline void omap_uart_disable_clocks(struct omap_uart_state *uart, > + int power_state) > { > if (!uart->clocked) > return; > - > - omap_uart_save_context(uart); > + if (power_state == PWRDM_POWER_OFF) > + omap_uart_save_context(uart); > uart->clocked = 0; > clk_disable(uart->ick); > clk_disable(uart->fck); > @@ -325,13 +320,13 @@ static void omap_uart_idle_timer(unsigned long data) > omap_uart_allow_sleep(uart); > } > > -void omap_uart_prepare_idle(int num) > +void omap_uart_prepare_idle(int num, int power_state) > { > struct omap_uart_state *uart; > > list_for_each_entry(uart, &uart_list, node) { > if (num == uart->num && uart->can_sleep) { > - omap_uart_disable_clocks(uart); > + omap_uart_disable_clocks(uart, power_state); > return; > } > } > diff --git a/arch/arm/mach-omap2/sleep34xx.S b/arch/arm/mach-omap2/sleep34xx.S > index de31919..aabcd1f 100644 > --- a/arch/arm/mach-omap2/sleep34xx.S > +++ b/arch/arm/mach-omap2/sleep34xx.S > @@ -256,8 +256,13 @@ restore: > and r2, r2, #0x3 > cmp r2, #0x0 @ Check if target power state was OFF or RET > moveq r9, #0x3 @ MPU OFF => L1 and L2 lost > + beq restore_from_off > + cmp r2, #0x1 > + moveq r9, #0x3 > movne r9, #0x1 @ Only L1 and L2 lost => avoid L2 invalidation > bne logic_l1_restore > +restore_from_off: > +/* b restore_from_off*/ > ldr r0, control_stat > ldr r1, [r0] > and r1, #0x700 > @@ -418,7 +423,7 @@ usettbr0: > > ldmfd sp!, {r0-r12, pc} @ restore regs and return > save_context_wfi: > - /*b save_context_wfi*/ @ enable to debug save code > + /* b save_context_wfi*/ @ enable to debug save code whitespace-only change > mov r8, r0 /* Store SDRAM address in r8 */ > /* Check what that target sleep state is:stored in r1*/ > /* 1 - Only L1 and logic lost */ > diff --git a/arch/arm/plat-omap/include/mach/control.h b/arch/arm/plat-omap/include/mach/control.h > index debe3f7..0c012a1 100644 > --- a/arch/arm/plat-omap/include/mach/control.h > +++ b/arch/arm/plat-omap/include/mach/control.h > @@ -287,6 +287,7 @@ extern u32 *get_es3_restore_pointer(void); > extern u32 omap3_arm_context[128]; > extern void omap3_control_save_context(void); > extern void omap3_control_restore_context(void); > +extern void omap3_scratchpad_dpll4autoidle(int enable); > > #else > #define omap_ctrl_base_get() 0 > diff --git a/arch/arm/plat-omap/include/mach/powerdomain.h b/arch/arm/plat-omap/include/mach/powerdomain.h > index 6271d85..9960dcc 100644 > --- a/arch/arm/plat-omap/include/mach/powerdomain.h > +++ b/arch/arm/plat-omap/include/mach/powerdomain.h > @@ -119,6 +119,8 @@ struct powerdomain { > > int state; > unsigned state_counter[4]; > + unsigned ret_logic_off_counter; > + unsigned ret_mem_off_counter; > > #ifdef CONFIG_PM_DEBUG > s64 timer; > @@ -163,8 +165,10 @@ int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst); > > int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm); > int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm); > +int pwrdm_read_next_logic_pwrst(struct powerdomain *pwrdm); > int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank); > int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank); > +int pwrdm_read_next_mem_pwrst(struct powerdomain *pwrdm, u8 bank); > > int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm); > int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm); Kevin -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/arch/arm/mach-omap2/control.c b/arch/arm/mach-omap2/control.c index c9407c0..dc90207 100644 --- a/arch/arm/mach-omap2/control.c +++ b/arch/arm/mach-omap2/control.c @@ -331,6 +331,26 @@ void omap3_save_scratchpad_contents(void) sizeof(sdrc_block_contents), &arm_context_addr, 4); } +void omap3_scratchpad_dpll4autoidle(int enable) +{ + void * __iomem scratchpad_address; + struct omap3_scratchpad_prcm_block prcm_block_contents; + + scratchpad_address = OMAP2_IO_ADDRESS(OMAP343X_SCRATCHPAD); + + memcpy_fromio(&prcm_block_contents, scratchpad_address + 0x2C, + sizeof(prcm_block_contents)); + if (enable) + prcm_block_contents.cm_autoidle_pll |= + (1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT); + else + prcm_block_contents.cm_autoidle_pll &= + ~OMAP3430_AUTO_PERIPH_DPLL_MASK; + + memcpy_toio(scratchpad_address + 0x2C, &prcm_block_contents, + sizeof(prcm_block_contents)); +} + void omap3_control_save_context(void) { control_context.sysconfig = omap_ctrl_readl(OMAP2_CONTROL_SYSCONFIG); diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c index 7bbec90..a5b811b 100644 --- a/arch/arm/mach-omap2/cpuidle34xx.c +++ b/arch/arm/mach-omap2/cpuidle34xx.c @@ -36,14 +36,17 @@ #ifdef CONFIG_CPU_IDLE -#define OMAP3_MAX_STATES 7 +#define OMAP3_MAX_STATES 9 #define OMAP3_STATE_C1 0 /* C1 - MPU WFI + Core active */ #define OMAP3_STATE_C2 1 /* C2 - MPU WFI + Core inactive */ #define OMAP3_STATE_C3 2 /* C3 - MPU CSWR + Core inactive */ -#define OMAP3_STATE_C4 3 /* C4 - MPU OFF + Core iactive */ -#define OMAP3_STATE_C5 4 /* C5 - MPU RET + Core RET */ -#define OMAP3_STATE_C6 5 /* C6 - MPU OFF + Core RET */ -#define OMAP3_STATE_C7 6 /* C7 - MPU OFF + Core OFF */ +#define OMAP3_STATE_C4 3 /* C4 - MPU OFF + Core inactive */ +#define OMAP3_STATE_C5 4 /* C5 - MPU CSWR + Core CSWR */ +#define OMAP3_STATE_C6 5 /* C6 - MPU OFF + Core CSWR */ +#define OMAP3_STATE_C7 6 /* C7 - MPU OSWR + CORE OSWR */ +#define OMAP3_STATE_C8 7 /* C8 - MPU OFF + CORE OSWR */ +#define OMAP3_STATE_C9 8 /* C9 - MPU OFF + CORE OFF */ + struct omap3_processor_cx { u8 valid; @@ -52,6 +55,11 @@ struct omap3_processor_cx { u32 wakeup_latency; u32 mpu_state; u32 core_state; + u32 mpu_logicl1_ret_state; + u32 mpu_l2cache_ret_state; + u32 core_logic_state; + u32 core_mem1_ret_state; + u32 core_mem2_ret_state; u32 threshold; u32 flags; }; @@ -95,6 +103,11 @@ static int omap3_enter_idle(struct cpuidle_device *dev, struct omap3_processor_cx *cx = cpuidle_get_statedata(state); struct timespec ts_preidle, ts_postidle, ts_idle; u32 mpu_state = cx->mpu_state, core_state = cx->core_state; + u32 mpu_logicl1_ret_state = cx->mpu_logicl1_ret_state; + u32 mpu_l2cache_ret_state = cx->mpu_l2cache_ret_state; + u32 core_logic_state = cx->core_logic_state; + u32 core_mem1_ret_state = cx->core_mem1_ret_state; + u32 core_mem2_ret_state = cx->core_mem2_ret_state; current_cx_state = *cx; @@ -111,6 +124,34 @@ static int omap3_enter_idle(struct cpuidle_device *dev, core_state = PWRDM_POWER_RET; } + if (!enable_oswr_ret) { + if (mpu_logicl1_ret_state == PWRDM_POWER_OFF) + mpu_logicl1_ret_state = PWRDM_POWER_RET; + if (mpu_l2cache_ret_state == PWRDM_POWER_OFF) + mpu_l2cache_ret_state = PWRDM_POWER_RET; + if (core_logic_state == PWRDM_POWER_OFF) + core_logic_state = PWRDM_POWER_RET; + if (core_mem1_ret_state == PWRDM_POWER_OFF) + core_mem1_ret_state = PWRDM_POWER_RET; + if (core_mem2_ret_state == PWRDM_POWER_OFF) + core_mem2_ret_state = PWRDM_POWER_RET; + } + + if (mpu_logicl1_ret_state != 0xFF) + pwrdm_set_logic_retst(mpu_pd, mpu_logicl1_ret_state); + + if (mpu_l2cache_ret_state != 0xFF) + pwrdm_set_mem_retst(mpu_pd, 0, mpu_l2cache_ret_state); + + if (core_logic_state != 0xFF) + pwrdm_set_logic_retst(core_pd, core_logic_state); + + if (core_mem1_ret_state != 0xFF) + pwrdm_set_mem_retst(core_pd, 0, core_mem1_ret_state); + + if (core_mem2_ret_state != 0xFF) + pwrdm_set_mem_retst(core_pd, 1, core_mem2_ret_state); + pwrdm_set_next_pwrst(mpu_pd, mpu_state); pwrdm_set_next_pwrst(core_pd, core_state); @@ -174,10 +215,24 @@ DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev); * C4 . MPU OFF + Core inactive * C5 . MPU CSWR + Core CSWR * C6 . MPU OFF + Core CSWR - * C7 . MPU OFF + Core OFF + * C7 . MPU OSWR + Core OSWR + * C8 . MPU OFF + Core OSWR + * C9 . MPU OFF + Core OFF */ void omap_init_power_states(void) { + int i; + struct omap3_processor_cx *cx; + + for (i = OMAP3_STATE_C1; i < OMAP3_MAX_STATES; i++) { + cx = &omap3_power_states[i]; + cx->mpu_logicl1_ret_state = 0xFF; + cx->mpu_l2cache_ret_state = 0xFF; + cx->core_logic_state = 0xFF; + cx->core_mem1_ret_state = 0xFF; + cx->core_mem2_ret_state = 0xFF; + } + /* C1 . MPU WFI + Core active */ omap3_power_states[OMAP3_STATE_C1].valid = 1; omap3_power_states[OMAP3_STATE_C1].type = OMAP3_STATE_C1; @@ -206,6 +261,10 @@ void omap_init_power_states(void) omap3_power_states[OMAP3_STATE_C3].threshold = 300; omap3_power_states[OMAP3_STATE_C3].mpu_state = PWRDM_POWER_RET; omap3_power_states[OMAP3_STATE_C3].core_state = PWRDM_POWER_ON; + omap3_power_states[OMAP3_STATE_C3].mpu_logicl1_ret_state = + PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C3].mpu_l2cache_ret_state = + PWRDM_POWER_RET; omap3_power_states[OMAP3_STATE_C3].flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_CHECK_BM; @@ -217,6 +276,10 @@ void omap_init_power_states(void) omap3_power_states[OMAP3_STATE_C4].threshold = 4000; omap3_power_states[OMAP3_STATE_C4].mpu_state = PWRDM_POWER_OFF; omap3_power_states[OMAP3_STATE_C4].core_state = PWRDM_POWER_ON; + omap3_power_states[OMAP3_STATE_C4].mpu_logicl1_ret_state = + PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C4].mpu_l2cache_ret_state = + PWRDM_POWER_RET; omap3_power_states[OMAP3_STATE_C4].flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_CHECK_BM; @@ -228,6 +291,15 @@ void omap_init_power_states(void) omap3_power_states[OMAP3_STATE_C5].threshold = 12000; omap3_power_states[OMAP3_STATE_C5].mpu_state = PWRDM_POWER_RET; omap3_power_states[OMAP3_STATE_C5].core_state = PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C5].mpu_logicl1_ret_state = + PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C5].mpu_l2cache_ret_state = + PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C5].core_logic_state = PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C5].core_mem1_ret_state = + PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C5].core_mem2_ret_state = + PWRDM_POWER_RET; omap3_power_states[OMAP3_STATE_C5].flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_CHECK_BM; @@ -239,19 +311,77 @@ void omap_init_power_states(void) omap3_power_states[OMAP3_STATE_C6].threshold = 15000; omap3_power_states[OMAP3_STATE_C6].mpu_state = PWRDM_POWER_OFF; omap3_power_states[OMAP3_STATE_C6].core_state = PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C6].mpu_logicl1_ret_state = + PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C6].mpu_l2cache_ret_state = + PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C6].core_logic_state = PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C6].core_mem1_ret_state = + PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C6].core_mem2_ret_state = + PWRDM_POWER_RET; omap3_power_states[OMAP3_STATE_C6].flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_CHECK_BM; - /* C7 . MPU OFF + Core OFF */ + /* C7 . MPU OSWR + Core OSWR */ omap3_power_states[OMAP3_STATE_C7].valid = 1; omap3_power_states[OMAP3_STATE_C7].type = OMAP3_STATE_C7; - omap3_power_states[OMAP3_STATE_C7].sleep_latency = 10000; - omap3_power_states[OMAP3_STATE_C7].wakeup_latency = 30000; - omap3_power_states[OMAP3_STATE_C7].threshold = 300000; - omap3_power_states[OMAP3_STATE_C7].mpu_state = PWRDM_POWER_OFF; - omap3_power_states[OMAP3_STATE_C7].core_state = PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C7].sleep_latency = 4000; + omap3_power_states[OMAP3_STATE_C7].wakeup_latency = 9000; + omap3_power_states[OMAP3_STATE_C7].threshold = 18000; + omap3_power_states[OMAP3_STATE_C7].mpu_state = PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C7].core_state = PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C7].mpu_logicl1_ret_state = + PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C7].mpu_l2cache_ret_state = + PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C7].core_logic_state = PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C7].core_mem1_ret_state = + PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C7].core_mem2_ret_state = + PWRDM_POWER_OFF; omap3_power_states[OMAP3_STATE_C7].flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_CHECK_BM; + + /* C8 . MPU OFF + Core OSWR */ + omap3_power_states[OMAP3_STATE_C8].valid = 1; + omap3_power_states[OMAP3_STATE_C8].type = OMAP3_STATE_C7; + omap3_power_states[OMAP3_STATE_C8].sleep_latency = 8000; + omap3_power_states[OMAP3_STATE_C8].wakeup_latency = 25000; + omap3_power_states[OMAP3_STATE_C8].threshold = 250000; + omap3_power_states[OMAP3_STATE_C8].mpu_state = PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C8].core_state = PWRDM_POWER_RET; + omap3_power_states[OMAP3_STATE_C8].mpu_logicl1_ret_state = + PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C8].mpu_l2cache_ret_state = + PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C8].core_logic_state = PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C8].core_mem1_ret_state = + PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C8].core_mem2_ret_state = + PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C8].flags = CPUIDLE_FLAG_TIME_VALID | + CPUIDLE_FLAG_CHECK_BM; + + /* C9 . MPU OFF + Core OFF */ + omap3_power_states[OMAP3_STATE_C9].valid = 1; + omap3_power_states[OMAP3_STATE_C9].type = OMAP3_STATE_C7; + omap3_power_states[OMAP3_STATE_C9].sleep_latency = 10000; + omap3_power_states[OMAP3_STATE_C9].wakeup_latency = 30000; + omap3_power_states[OMAP3_STATE_C9].threshold = 300000; + omap3_power_states[OMAP3_STATE_C9].mpu_state = PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C9].core_state = PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C9].mpu_logicl1_ret_state = + PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C9].mpu_l2cache_ret_state = + PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C9].core_logic_state = PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C9].core_mem1_ret_state = + PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C9].core_mem2_ret_state = + PWRDM_POWER_OFF; + omap3_power_states[OMAP3_STATE_C9].flags = CPUIDLE_FLAG_TIME_VALID | + CPUIDLE_FLAG_CHECK_BM; } struct cpuidle_driver omap3_idle_driver = { diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c index 1b4c160..ed7eb44 100644 --- a/arch/arm/mach-omap2/pm-debug.c +++ b/arch/arm/mach-omap2/pm-debug.c @@ -383,6 +383,9 @@ static int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user) for (i = 0; i < 4; i++) seq_printf(s, ",%s:%d", pwrdm_state_names[i], pwrdm->state_counter[i]); + seq_printf(s, ",RET-LOGIC-OFF:%d,RET-MEM-OFF:%d", + pwrdm->ret_logic_off_counter, + pwrdm->ret_mem_off_counter); seq_printf(s, "\n"); diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index fec7d00..5e73613 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -40,6 +40,7 @@ unsigned short enable_dyn_sleep; unsigned short clocks_off_while_idle; unsigned short enable_off_mode; +unsigned short enable_oswr_ret; unsigned short voltage_off_while_idle; unsigned short wakeup_timer_seconds; atomic_t sleep_block = ATOMIC_INIT(0); @@ -57,6 +58,9 @@ static struct kobj_attribute clocks_off_while_idle_attr = static struct kobj_attribute enable_off_mode_attr = __ATTR(enable_off_mode, 0644, idle_show, idle_store); +static struct kobj_attribute enable_oswr_ret_attr = + __ATTR(enable_oswr_ret, 0644, idle_show, idle_store); + static struct kobj_attribute voltage_off_while_idle_attr = __ATTR(voltage_off_while_idle, 0644, idle_show, idle_store); @@ -88,6 +92,8 @@ static ssize_t idle_show(struct kobject *kobj, struct kobj_attribute *attr, return sprintf(buf, "%hu\n", clocks_off_while_idle); else if (attr == &enable_off_mode_attr) return sprintf(buf, "%hu\n", enable_off_mode); + else if (attr == &enable_oswr_ret_attr) + return sprintf(buf, "%hu\n", enable_oswr_ret); else if (attr == &voltage_off_while_idle_attr) return sprintf(buf, "%hu\n", voltage_off_while_idle); else if (attr == &wakeup_timer_seconds_attr) @@ -113,7 +119,9 @@ static ssize_t idle_store(struct kobject *kobj, struct kobj_attribute *attr, } else if (attr == &enable_off_mode_attr) { enable_off_mode = value; omap3_pm_off_mode_enable(enable_off_mode); - } else if (attr == &wakeup_timer_seconds_attr) { + } else if (attr == &enable_oswr_ret_attr) + enable_oswr_ret = value; + else if (attr == &wakeup_timer_seconds_attr) { wakeup_timer_seconds = value; } else if (attr == &voltage_off_while_idle_attr) { voltage_off_while_idle = value; @@ -240,6 +248,12 @@ static int __init omap_pm_init(void) return error; } error = sysfs_create_file(power_kobj, + &enable_oswr_ret_attr.attr); + if (error) { + printk(KERN_ERR "sysfs_create_file failed: %d\n", error); + return error; + } + error = sysfs_create_file(power_kobj, &wakeup_timer_seconds_attr.attr); if (error) printk(KERN_ERR "sysfs_create_file failed: %d\n", error); diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h index 052c601..1e5eb74 100644 --- a/arch/arm/mach-omap2/pm.h +++ b/arch/arm/mach-omap2/pm.h @@ -16,6 +16,7 @@ extern void *omap3_secure_ram_storage; extern unsigned short enable_dyn_sleep; extern unsigned short enable_off_mode; +extern unsigned short enable_oswr_ret; extern unsigned short voltage_off_while_idle; struct prm_setup_vc { diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 0150f29..99605e7 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -132,31 +132,41 @@ static void omap3_disable_io_chain(void) prm_clear_mod_reg_bits(OMAP3430_EN_IO_CHAIN, WKUP_MOD, PM_WKEN); } -static void omap3_core_save_context(void) +static void omap3_core_save_context(int core_state) { - u32 control_padconf_off; - /* Save the padconf registers */ - control_padconf_off = - omap_ctrl_readl(OMAP343X_CONTROL_PADCONF_OFF); - control_padconf_off |= START_PADCONF_SAVE; - omap_ctrl_writel(control_padconf_off, OMAP343X_CONTROL_PADCONF_OFF); - /* wait for the save to complete */ - while (!omap_ctrl_readl(OMAP343X_CONTROL_GENERAL_PURPOSE_STATUS) - & PADCONF_SAVE_DONE) - ; + if (core_state == PWRDM_POWER_OFF) { + u32 control_padconf_off; + /* Save the padconf registers */ + control_padconf_off = + omap_ctrl_readl(OMAP343X_CONTROL_PADCONF_OFF); + control_padconf_off |= START_PADCONF_SAVE; + omap_ctrl_writel(control_padconf_off, + OMAP343X_CONTROL_PADCONF_OFF); + /* wait for the save to complete */ + while (!omap_ctrl_readl(OMAP343X_CONTROL_GENERAL_PURPOSE_STATUS) + & PADCONF_SAVE_DONE) + ; + /* Save the system control module context, + * padconf already save above + */ + omap3_control_save_context(); + + } /* Save the Interrupt controller context */ omap3_intc_save_context(); /* Save the GPMC context */ omap3_gpmc_save_context(); - /* Save the system control module context, padconf already save above*/ - omap3_control_save_context(); omap_dma_global_context_save(); } -static void omap3_core_restore_context(void) +static void omap3_core_restore_context(int core_state) { - /* Restore the control module context, padconf restored by h/w */ - omap3_control_restore_context(); + if (core_state == PWRDM_POWER_OFF) + /* Restore the control module context, + * padconf restored by h/w + */ + omap3_control_restore_context(); + /* Restore the GPMC context */ omap3_gpmc_restore_context(); /* Restore the interrupt controller context */ @@ -343,7 +353,8 @@ void omap_sram_idle(void) int mpu_next_state = PWRDM_POWER_ON; int per_next_state = PWRDM_POWER_ON; int core_next_state = PWRDM_POWER_ON; - int core_prev_state, per_prev_state; + int mpu_prev_state, core_prev_state, per_prev_state; + int mpu_logic_state, mpu_mem_state, core_logic_state, core_mem_state; u32 sdrc_pwr = 0; int per_state_modified = 0; @@ -356,11 +367,24 @@ void omap_sram_idle(void) pwrdm_clear_all_prev_pwrst(per_pwrdm); mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); + mpu_logic_state = pwrdm_read_next_logic_pwrst(mpu_pwrdm); + mpu_mem_state = pwrdm_read_next_mem_pwrst(mpu_pwrdm, 0); + switch (mpu_next_state) { case PWRDM_POWER_ON: + /* No need to save context */ + save_state = 0; + break; case PWRDM_POWER_RET: - /* No need to save context */ - save_state = 0; + if (!mpu_logic_state && !mpu_mem_state) + save_state = 3; + else if (!mpu_mem_state) + save_state = 2; + else if (!mpu_logic_state) + save_state = 1; + else + /* No need to save context */ + save_state = 0; break; case PWRDM_POWER_OFF: save_state = 3; @@ -380,8 +404,11 @@ void omap_sram_idle(void) /* PER */ per_next_state = pwrdm_read_next_pwrst(per_pwrdm); core_next_state = pwrdm_read_next_pwrst(core_pwrdm); + core_logic_state = pwrdm_read_next_logic_pwrst(core_pwrdm); + core_mem_state = pwrdm_read_next_mem_pwrst(core_pwrdm, 0) | + pwrdm_read_next_mem_pwrst(core_pwrdm, 1); if (per_next_state < PWRDM_POWER_ON) { - omap_uart_prepare_idle(2); + omap_uart_prepare_idle(2, per_next_state); omap2_gpio_prepare_for_idle(per_next_state); if (per_next_state == PWRDM_POWER_OFF) { if (core_next_state == PWRDM_POWER_ON) { @@ -401,24 +428,45 @@ void omap_sram_idle(void) /* Disable smartreflex before entering WFI */ disable_smartreflex(SR1); disable_smartreflex(SR2); - omap_uart_prepare_idle(0); - omap_uart_prepare_idle(1); + omap_uart_prepare_idle(0, core_next_state & core_logic_state); + omap_uart_prepare_idle(1, core_next_state & core_logic_state); if (core_next_state == PWRDM_POWER_OFF) { prm_set_mod_reg_bits(OMAP3430_AUTO_OFF, OMAP3430_GR_MOD, OMAP3_PRM_VOLTCTRL_OFFSET); - omap3_core_save_context(); + omap3_core_save_context(PWRDM_POWER_OFF); + omap3_prcm_save_context(); + } else if ((core_next_state == PWRDM_POWER_RET) && + (core_logic_state == PWRDM_POWER_OFF) && + (core_mem_state == PWRDM_POWER_OFF)) { + omap3_core_save_context(PWRDM_POWER_RET); omap3_prcm_save_context(); + /* + * This is a hack. Currently OSWR does not + * work if rom code restores DPLL4 to non + * auto idle mode. + * ROM restore takes 20mS longer if PER/DPLL4 + * idle is enabled before OFF.So it is typically + * not enabled. Since OSWR hangs if it is not enabled + * enable it for OSWR alone. Later in the restore path + * it is disabled again + */ + + omap3_scratchpad_dpll4autoidle(1); + prm_set_mod_reg_bits(OMAP3430_AUTO_RET, + OMAP3430_GR_MOD, + OMAP3_PRM_VOLTCTRL_OFFSET); + } else if (core_next_state == PWRDM_POWER_RET) { prm_set_mod_reg_bits(OMAP3430_AUTO_RET, OMAP3430_GR_MOD, OMAP3_PRM_VOLTCTRL_OFFSET); } + /* Enable IO-PAD and IO-CHAIN wakeups */ prm_set_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN); omap3_enable_io_chain(); } - /* * On EMU/HS devices ROM code restores a SRDC value * from scratchpad which has automatic self refresh on timeout @@ -447,18 +495,33 @@ void omap_sram_idle(void) core_next_state == PWRDM_POWER_OFF) sdrc_write_reg(sdrc_pwr, SDRC_POWER); + mpu_prev_state = pwrdm_read_prev_pwrst(mpu_pwrdm); + /* Restore table entry modified during MMU restoration */ - if (pwrdm_read_prev_pwrst(mpu_pwrdm) == PWRDM_POWER_OFF) + if (((mpu_prev_state == PWRDM_POWER_RET) && + (pwrdm_read_prev_logic_pwrst(mpu_pwrdm) == + PWRDM_POWER_OFF)) || + (mpu_prev_state == PWRDM_POWER_OFF)) restore_table_entry(); - /* CORE */ if (core_next_state < PWRDM_POWER_ON) { core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm); - if (core_prev_state == PWRDM_POWER_OFF) { - omap3_core_restore_context(); + if ((core_prev_state == PWRDM_POWER_OFF) || + (core_prev_state == PWRDM_POWER_RET && + pwrdm_read_prev_logic_pwrst(core_pwrdm) == + PWRDM_POWER_OFF)) { + omap3_core_restore_context(core_prev_state); omap3_prcm_restore_context(); omap3_sram_restore_context(); omap2_sms_restore_context(); + /* + * For OSWR to work we put PER DPLL in auto + * idle mode in scratchpad. Clear it so that + * next time if a OFF is attempted the ROM restore + * does nt take long + */ + if (core_prev_state == PWRDM_POWER_RET) + omap3_scratchpad_dpll4autoidle(0); } omap_uart_resume_idle(0); omap_uart_resume_idle(1); diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c index 6c5fee9..ebd8649 100644 --- a/arch/arm/mach-omap2/powerdomain.c +++ b/arch/arm/mach-omap2/powerdomain.c @@ -128,6 +128,16 @@ static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag) prev = pwrdm_read_prev_pwrst(pwrdm); if (pwrdm->state != prev) pwrdm->state_counter[prev]++; + if (prev == PWRDM_POWER_RET) { + if ((pwrdm->pwrsts_logic_ret == PWRSTS_OFF_RET) && + (pwrdm_read_prev_logic_pwrst(pwrdm) == + PWRDM_POWER_OFF)) + pwrdm->ret_logic_off_counter++; + if ((pwrdm->pwrsts_mem_ret[0] == PWRSTS_OFF_RET) && + (pwrdm_read_prev_mem_pwrst(pwrdm, 0) == + PWRDM_POWER_OFF)) + pwrdm->ret_mem_off_counter++; + } break; default: return -EINVAL; @@ -162,7 +172,8 @@ static __init void _pwrdm_setup(struct powerdomain *pwrdm) for (i = 0; i < 4; i++) pwrdm->state_counter[i] = 0; - + pwrdm->ret_logic_off_counter = 0; + pwrdm->ret_mem_off_counter = 0; pwrdm_wait_transition(pwrdm); pwrdm->state = pwrdm_read_pwrst(pwrdm); pwrdm->state_counter[pwrdm->state] = 1; @@ -951,6 +962,30 @@ int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) } /** + * pwrdm_read_next_logic_pwrst - get next powerdomain logic power state + * @pwrdm: struct powerdomain * to get next logic power state + * + * Return the powerdomain pwrdm's logic power state. Returns -EINVAL + * if the powerdomain pointer is null or returns the next logic + * power state upon success. + */ +int pwrdm_read_next_logic_pwrst(struct powerdomain *pwrdm) +{ + if (!pwrdm) + return -EINVAL; + + /* + * The register bit names below may not correspond to the + * actual names of the bits in each powerdomain's register, + * but the type of value returned is the same for each + * powerdomain. + */ + return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTCTRL, + OMAP3430_LOGICSTATEST); +} + + +/** * pwrdm_read_mem_pwrst - get current memory bank power state * @pwrdm: struct powerdomain * to get current memory bank power state * @bank: memory bank number (0-3) @@ -976,7 +1011,7 @@ int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) * in PWST . So the hack. Think of a cleaner * way of doing this */ - if (cpu_is_omap34xx) + if (cpu_is_omap34xx()) if (!strcmp("mpu_pwrdm", pwrdm->name)) bank = 1; @@ -1033,7 +1068,7 @@ int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) * in PREPWST . So the hack. Think of a cleaner * way of doing this */ - if (cpu_is_omap34xx) + if (cpu_is_omap34xx()) if (!strcmp("mpu_pwrdm", pwrdm->name)) bank = 1; /* @@ -1065,6 +1100,54 @@ int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) } /** + * pwrdm_read_next_mem_pwrst - get next memory bank power state + * @pwrdm: struct powerdomain * to get mext memory bank power state + * @bank: memory bank number (0-3) + * + * Return the powerdomain pwrdm's next memory power state for bank + * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if + * the target memory bank does not exist or is not controllable, or + * returns the next memory power state upon success. + */ +int pwrdm_read_next_mem_pwrst(struct powerdomain *pwrdm, u8 bank) +{ + u32 m; + + if (!pwrdm) + return -EINVAL; + + if (pwrdm->banks < (bank + 1)) + return -EEXIST; + + /* + * The register bit names below may not correspond to the + * actual names of the bits in each powerdomain's register, + * but the type of value returned is the same for each + * powerdomain. + */ + switch (bank) { + case 0: + m = OMAP3430_SHAREDL1CACHEFLATRETSTATE; + break; + case 1: + m = OMAP3430_L1FLATMEMRETSTATE; + break; + case 2: + m = OMAP3430_SHAREDL2CACHEFLATRETSTATE; + break; + case 3: + m = OMAP3430_SHAREDL2CACHEFLATRETSTATE; + break; + default: + WARN_ON(1); /* should never happen */ + return -EEXIST; + } + + return prm_read_mod_bits_shift(pwrdm->prcm_offs, + PM_PWSTCTRL, m); +} + +/** * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm * @pwrdm: struct powerdomain * to clear * diff --git a/arch/arm/mach-omap2/powerdomains34xx.h b/arch/arm/mach-omap2/powerdomains34xx.h index aa557b2..e3d470d 100644 --- a/arch/arm/mach-omap2/powerdomains34xx.h +++ b/arch/arm/mach-omap2/powerdomains34xx.h @@ -207,6 +207,7 @@ static struct powerdomain core_34xx_pre_es3_1_pwrdm = { CHIP_IS_OMAP3430ES2 | CHIP_IS_OMAP3430ES3_0), .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, .dep_bit = OMAP3430_EN_CORE_SHIFT, .banks = 2, .pwrsts_mem_ret = { @@ -225,6 +226,7 @@ static struct powerdomain core_34xx_es3_1_pwrdm = { .prcm_offs = CORE_MOD, .omap_chip = OMAP_CHIP_INIT(CHIP_GE_OMAP3430ES3_1), .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, .dep_bit = OMAP3430_EN_CORE_SHIFT, .flags = PWRDM_HAS_HDWR_SAR, /* for USBTLL only */ .banks = 2, diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c index 858447f..4b139b7 100644 --- a/arch/arm/mach-omap2/serial.c +++ b/arch/arm/mach-omap2/serial.c @@ -164,9 +164,6 @@ static void omap_uart_save_context(struct omap_uart_state *uart) u16 lcr = 0; struct plat_serial8250_port *p = uart->p; - if (!enable_off_mode) - return; - lcr = serial_read_reg(p, UART_LCR); serial_write_reg(p, UART_LCR, 0xBF); uart->dll = serial_read_reg(p, UART_DLL); @@ -185,9 +182,6 @@ static void omap_uart_restore_context(struct omap_uart_state *uart) u16 efr = 0; struct plat_serial8250_port *p = uart->p; - if (!enable_off_mode) - return; - if (!uart->context_valid) return; @@ -231,12 +225,13 @@ static inline void omap_uart_enable_clocks(struct omap_uart_state *uart) #ifdef CONFIG_PM -static inline void omap_uart_disable_clocks(struct omap_uart_state *uart) +static inline void omap_uart_disable_clocks(struct omap_uart_state *uart, + int power_state) { if (!uart->clocked) return; - - omap_uart_save_context(uart); + if (power_state == PWRDM_POWER_OFF) + omap_uart_save_context(uart); uart->clocked = 0; clk_disable(uart->ick); clk_disable(uart->fck); @@ -325,13 +320,13 @@ static void omap_uart_idle_timer(unsigned long data) omap_uart_allow_sleep(uart); } -void omap_uart_prepare_idle(int num) +void omap_uart_prepare_idle(int num, int power_state) { struct omap_uart_state *uart; list_for_each_entry(uart, &uart_list, node) { if (num == uart->num && uart->can_sleep) { - omap_uart_disable_clocks(uart); + omap_uart_disable_clocks(uart, power_state); return; } } diff --git a/arch/arm/mach-omap2/sleep34xx.S b/arch/arm/mach-omap2/sleep34xx.S index de31919..aabcd1f 100644 --- a/arch/arm/mach-omap2/sleep34xx.S +++ b/arch/arm/mach-omap2/sleep34xx.S @@ -256,8 +256,13 @@ restore: and r2, r2, #0x3 cmp r2, #0x0 @ Check if target power state was OFF or RET moveq r9, #0x3 @ MPU OFF => L1 and L2 lost + beq restore_from_off + cmp r2, #0x1 + moveq r9, #0x3 movne r9, #0x1 @ Only L1 and L2 lost => avoid L2 invalidation bne logic_l1_restore +restore_from_off: +/* b restore_from_off*/ ldr r0, control_stat ldr r1, [r0] and r1, #0x700 @@ -418,7 +423,7 @@ usettbr0: ldmfd sp!, {r0-r12, pc} @ restore regs and return save_context_wfi: - /*b save_context_wfi*/ @ enable to debug save code + /* b save_context_wfi*/ @ enable to debug save code mov r8, r0 /* Store SDRAM address in r8 */ /* Check what that target sleep state is:stored in r1*/ /* 1 - Only L1 and logic lost */ diff --git a/arch/arm/plat-omap/include/mach/control.h b/arch/arm/plat-omap/include/mach/control.h index debe3f7..0c012a1 100644 --- a/arch/arm/plat-omap/include/mach/control.h +++ b/arch/arm/plat-omap/include/mach/control.h @@ -287,6 +287,7 @@ extern u32 *get_es3_restore_pointer(void); extern u32 omap3_arm_context[128]; extern void omap3_control_save_context(void); extern void omap3_control_restore_context(void); +extern void omap3_scratchpad_dpll4autoidle(int enable); #else #define omap_ctrl_base_get() 0 diff --git a/arch/arm/plat-omap/include/mach/powerdomain.h b/arch/arm/plat-omap/include/mach/powerdomain.h index 6271d85..9960dcc 100644 --- a/arch/arm/plat-omap/include/mach/powerdomain.h +++ b/arch/arm/plat-omap/include/mach/powerdomain.h @@ -119,6 +119,8 @@ struct powerdomain { int state; unsigned state_counter[4]; + unsigned ret_logic_off_counter; + unsigned ret_mem_off_counter; #ifdef CONFIG_PM_DEBUG s64 timer; @@ -163,8 +165,10 @@ int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst); int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm); int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm); +int pwrdm_read_next_logic_pwrst(struct powerdomain *pwrdm); int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank); int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank); +int pwrdm_read_next_mem_pwrst(struct powerdomain *pwrdm, u8 bank); int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm); int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm); diff --git a/arch/arm/plat-omap/include/mach/serial.h b/arch/arm/plat-omap/include/mach/serial.h index e249186..92c1199 100644 --- a/arch/arm/plat-omap/include/mach/serial.h +++ b/arch/arm/plat-omap/include/mach/serial.h @@ -60,7 +60,7 @@ extern void omap_serial_init(void); extern int omap_uart_can_sleep(void); extern void omap_uart_check_wakeup(void); extern void omap_uart_prepare_suspend(void); -extern void omap_uart_prepare_idle(int num); +extern void omap_uart_prepare_idle(int num, int power_state); extern void omap_uart_resume_idle(int num); extern void omap_uart_enable_irqs(int enable); #endif diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index dcf77fa..8ec8300 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -17,7 +17,7 @@ #include <linux/kobject.h> #include <linux/completion.h> -#define CPUIDLE_STATE_MAX 8 +#define CPUIDLE_STATE_MAX 16 #define CPUIDLE_NAME_LEN 16 #define CPUIDLE_DESC_LEN 32
This patch adds OSWR support for MPU/CORE domains in CPUidle. Two new C states are being added to the existing C states which makes the new states look like below. C1 - MPU WFI + Core active C2 - MPU WFI + Core inactive C3 - MPU CSWR + Core inactive C4 - MPU OFF + Core inactive C5 - MPU CSWR + Core CSWR C6 - MPU OFF + Core CSWR C7 - MPU OSWR + CORE OSWR (New State) C8 - MPU OFF + CORE OSWR (New State) C9 - MPU OFF + CORE OFF To enable this feature echo 1 > /sys/powwer/enable_oswr_ret To disable this feature echo 0 > /sys/poweer/enable_oswr_ret This patch depends on http://patchwork.kernel.org/patch/44290/ and is validated on latest PM branch on OMAP3430 SDP. Signed-off-by: Thara Gopinath <thara@ti.com> --- arch/arm/mach-omap2/control.c | 20 +++ arch/arm/mach-omap2/cpuidle34xx.c | 154 +++++++++++++++++++++++-- arch/arm/mach-omap2/pm-debug.c | 3 + arch/arm/mach-omap2/pm.c | 16 +++- arch/arm/mach-omap2/pm.h | 1 + arch/arm/mach-omap2/pm34xx.c | 119 +++++++++++++++----- arch/arm/mach-omap2/powerdomain.c | 89 ++++++++++++++- arch/arm/mach-omap2/powerdomains34xx.h | 2 + arch/arm/mach-omap2/serial.c | 17 +-- arch/arm/mach-omap2/sleep34xx.S | 7 +- arch/arm/plat-omap/include/mach/control.h | 1 + arch/arm/plat-omap/include/mach/powerdomain.h | 4 + arch/arm/plat-omap/include/mach/serial.h | 2 +- include/linux/cpuidle.h | 2 +- 14 files changed, 379 insertions(+), 58 deletions(-)