From: Vishwanath BS <vishwanath.bs@xxxxxx> OMAP3430/3630 has a Silicon bug because of which SDRC is released from IDLE even before Core DPLL has locked. This leads to undefined behaviour of SDRC DLL. Bug Descritpion: The root cause of the issue is that SDRC IDLEREQ is deasserted before DPLL3 has locked. Because of this DLL may/may not lock based on Process Voltage Temperature conditions. The bug can occur when DPLL3 automatic transition is enabled. So DPLL3 automatic transition is disabled by default and it is enabled only when system is entering ret/off state (to facilitate voltage scaling). So when system is entering ret/off state, WA is applied (since DPLL3 autoidle is enabled, we can possibly hit the issue; hence the WA) Errata id: i581 Workaround Descrioption: Description of WA for 3430: Initialization: Disable DPLL3 automatic mode by default. Issue will not be faced as DPLL3 is always locked. Before CORE Voltage Domain (VDD2) Sleep Transition to RETENTION or OFF mode: 1. Reduce DPLL3 M2 Frequency to get L3 running at OPP2 Frequency (by changing M2 Divider value). This is increasing the period duration of one L3 clock cycle. o In case of CORE is at OPP3 (166MHz@xxxxx): " Lower the frequency to 83MHz. o In case of CORE is at OPP2 (83MHz@xxxxx): " Keep the frequency as it is (83MHz). 2. Increase CORE Voltage to 1.2V. This is reducing the timing duration of the critical path signal which will now fit to one L3 clock cycle. 3. Enable DPLL3 Automatic mode. This will ensure proper transition to RETENTION or OFF mode. After CORE Voltage Domain Wakeup Transition from RETENTION or OFF mode: 1. Disable DPLL3 Automatic mode. 2. Restore previous DPLL3 M2 Frequency and CORE Voltage values. Description of WA for 3630: Initialization: Disable DPLL3 automatic mode by default. Issue will not be faced as DPLL3 is always locked. Before CORE Voltage Domain(VDD2) Sleep Transition to RETENTION or OFF mode: 1. Reduce DPLL3 M2 Frequency to get L3 running at OPP50 Frequency (by changing M2 Divider value) and set VDD2 Voltage for OPP100. This is increasing the period duration of one L3 clock cycle and reducing the timing duration of the critical path signal which will now fit to one L3 clock cycle. o In case of CORE is at OPP100 (L3=200MHz, VDD2=1.1375V): " Lower the frequency to 100MHz. " Keep the voltage as it is (1.1375V). o In case of CORE is at OPP50 (L3=100MHz, VDD2=0.93V): " Keep the frequency as it is (100MHz). " Increase the voltage to 1.1375V. 2. Enable DPLL3 Automatic mode. This will ensure proper transition to RETENTION or OFF mode. After CORE Voltage Domain Wakeup Transition from RETENTION or OFF mode: 1. Disable DPLL3 Automatic mode. 2. Restore previous DPLL3 M2 Frequency and CORE Voltage values. Also OSWR should not be attempted if DPLL3 has locked. This should be done as part of OSWR patch series. These patch is based on Thara's Smart Reflex V3 patch series (wip_sr branch) and Tero's OS Idle changes @ https://patchwork.kernel.org/patch/85268/ Patch tested on 3430SDP and 3630 ZOOM3. Changes done in V3: 1. Addressed comments from Kevin 2. Optimized the code based on Peter's patch Cc: Peter 'p2' De Schrijver <peter.de-schrijver@xxxxxxxxx> Signed-off-by: Shweta Gulati <shweta.gulati@xxxxxx> Signed-off-by: Vishwanath BS <vishwanath.bs@xxxxxx> --- arch/arm/mach-omap2/pm34xx.c | 114 ++++++++++++++++++++++++++++++++++++++++- arch/arm/mach-omap2/voltage.c | 1 + 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 9c57081..b0a5d09 100755 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -55,6 +55,7 @@ #include "pm.h" #include "sdrc.h" #include "omap3-opp.h" +#include "clock3xxx.h" #ifdef CONFIG_SUSPEND static suspend_state_t suspend_state = PM_SUSPEND_ON; @@ -97,6 +98,15 @@ static int (*_omap_save_secure_sram)(u32 *addr); static struct powerdomain *mpu_pwrdm, *neon_pwrdm; static struct powerdomain *core_pwrdm, *per_pwrdm; +static struct powerdomain *dss_pwrdm, *usbhost_pwrdm; +static struct powerdomain *cam_pwrdm, *sgx_pwrdm; +static struct clk *dpll3_clk; +static struct omap_opp *vdd2_opp50, *vdd2_opp100; +static unsigned long vdd2_opp50_volt, vdd2_opp100_volt; + +#define DLL_LOCK_ERRATA_581 (1 << 0) +static u16 pm34xx_errata; +#define IS_PM34XX_ERRATA(id) (pm34xx_errata & (id)) static inline void omap3_per_save_context(void) { @@ -367,6 +377,7 @@ void omap_sram_idle(void) int core_next_state = PWRDM_POWER_ON; int core_prev_state, per_prev_state; u32 sdrc_pwr = 0; + int prev_dpll3_div = 0; if (!_omap_sram_idle) return; @@ -417,9 +428,43 @@ void omap_sram_idle(void) */ if (mpu_next_state <= PWRDM_POWER_RET) omap_smartreflex_disable(VDD1, 1); - if (core_next_state <= PWRDM_POWER_RET) + if (core_next_state <= PWRDM_POWER_RET) { omap_smartreflex_disable(VDD2, 1); + /* Apply the errata if Core is entering RET/OFF */ + if ((IS_PM34XX_ERRATA(DLL_LOCK_ERRATA_581)) && + (core_next_state <= PWRDM_POWER_RET)) { + if (pwrdm_can_idle(core_pwrdm) && + pwrdm_can_idle(per_pwrdm) && + pwrdm_can_idle(dss_pwrdm) && + pwrdm_can_idle(usbhost_pwrdm) && + pwrdm_can_idle(cam_pwrdm) && + pwrdm_can_idle(sgx_pwrdm)) { + u32 clksel1_pll; + clksel1_pll = cm_read_mod_reg(PLL_MOD, + OMAP3430_CM_CLKSEL1_PLL); + prev_dpll3_div = clksel1_pll >> + OMAP3430_CORE_DPLL_CLKOUT_DIV_SHIFT; + if (prev_dpll3_div == 1) { + omap3_core_dpll_m2_set_rate(dpll3_clk, + opp_get_freq(vdd2_opp50) * 2); + if (cpu_is_omap343x()) + omap_voltage_scale(VDD2, 1200000, + vdd2_opp100_volt); + } else { + if (cpu_is_omap3630()) + omap_voltage_scale(VDD2, vdd2_opp100_volt, + vdd2_opp50_volt); + else if (cpu_is_omap343x()) + omap_voltage_scale(VDD2, 1200000, + vdd2_opp50_volt); + } + cm_rmw_mod_reg_bits(OMAP3430_AUTO_CORE_DPLL_MASK, + 0x1, PLL_MOD, CM_AUTOIDLE); + } + } + } + /* CORE */ if (core_next_state < PWRDM_POWER_ON) { omap_uart_prepare_idle(0); @@ -484,6 +529,44 @@ void omap_sram_idle(void) if (pwrdm_read_prev_pwrst(mpu_pwrdm) == PWRDM_POWER_OFF) restore_table_entry(); + if (IS_PM34XX_ERRATA(DLL_LOCK_ERRATA_581) && + (core_next_state < PWRDM_POWER_INACTIVE)) { + if (pwrdm_read_prev_pwrst(core_pwrdm) == PWRDM_POWER_OFF) { + u32 clksel1_pll; + + /* ROM code restored the scratchpad settings. So DPLL3 + * autoidle is disabled and L3 clock is back to the + * value before entering this function. This means we + * only have to lower the voltage if L3 runs at OPP50 + */ + + clksel1_pll = cm_read_mod_reg(PLL_MOD, + OMAP3430_CM_CLKSEL1_PLL); + if ((clksel1_pll >> OMAP3430_CORE_DPLL_CLKOUT_DIV_SHIFT) == 2) { + /* restore VDD2 OPP2 voltage */ + if (cpu_is_omap3630()) + omap_voltage_scale(VDD2, vdd2_opp50_volt, vdd2_opp100_volt); + else if (cpu_is_omap343x()) + omap_voltage_scale(VDD2, vdd2_opp50_volt, 1200000); + } + } else { + /* disable DPLL3 autoidle */ + cm_rmw_mod_reg_bits(OMAP3430_AUTO_CORE_DPLL_MASK, + 0x0, PLL_MOD, CM_AUTOIDLE); + if (prev_dpll3_div == 1) { + omap3_core_dpll_m2_set_rate(dpll3_clk, + opp_get_freq(vdd2_opp100) * 2); + if (cpu_is_omap343x()) + omap_voltage_scale(VDD2, vdd2_opp100_volt, 1200000); + } else { + if (cpu_is_omap3630()) + omap_voltage_scale(VDD2, vdd2_opp50_volt, vdd2_opp100_volt); + else if (cpu_is_omap343x()) + omap_voltage_scale(VDD2, vdd2_opp50_volt, 1200000); + } + } + } + /* CORE */ if (core_next_state < PWRDM_POWER_ON) { core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm); @@ -1006,7 +1089,12 @@ static void __init prcm_setup_regs(void) cm_write_mod_reg(1 << OMAP3430_AUTO_MPU_DPLL_SHIFT, MPU_MOD, CM_AUTOIDLE2); - cm_write_mod_reg((1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT) | + if (IS_PM34XX_ERRATA(DLL_LOCK_ERRATA_581)) + cm_write_mod_reg((1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT), + PLL_MOD, + CM_AUTOIDLE); + else + cm_write_mod_reg((1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT) | (1 << OMAP3430_AUTO_CORE_DPLL_SHIFT), PLL_MOD, CM_AUTOIDLE); @@ -1178,17 +1266,27 @@ void omap_push_sram_idle(void) save_secure_ram_context_sz); } +void pm_errata_configure() +{ + /* TODO: add 3630 && omap_rev() <= OMAP3630_REV_ES1_1 */ + if (cpu_is_omap343x() || (cpu_is_omap3630())) + pm34xx_errata |= DLL_LOCK_ERRATA_581; +} + static int __init omap3_pm_init(void) { struct power_state *pwrst, *tmp; struct clockdomain *neon_clkdm, *per_clkdm, *mpu_clkdm, *core_clkdm; int ret; + unsigned long freq = 0; if (!cpu_is_omap34xx()) return -ENODEV; printk(KERN_ERR "Power Management for TI OMAP3.\n"); + pm_errata_configure(); + /* XXX prcm_setup_regs needs to be before enabling hw * supervised mode for powerdomains */ prcm_setup_regs(); @@ -1219,12 +1317,24 @@ static int __init omap3_pm_init(void) neon_pwrdm = pwrdm_lookup("neon_pwrdm"); per_pwrdm = pwrdm_lookup("per_pwrdm"); core_pwrdm = pwrdm_lookup("core_pwrdm"); + usbhost_pwrdm = pwrdm_lookup("usbhost_pwrdm"); + sgx_pwrdm = pwrdm_lookup("sgx_pwrdm"); + dss_pwrdm = pwrdm_lookup("dss_pwrdm"); + cam_pwrdm = pwrdm_lookup("cam_pwrdm"); neon_clkdm = clkdm_lookup("neon_clkdm"); mpu_clkdm = clkdm_lookup("mpu_clkdm"); per_clkdm = clkdm_lookup("per_clkdm"); core_clkdm = clkdm_lookup("core_clkdm"); + dpll3_clk = clk_get(NULL, "dpll3_m2_ck"); + + vdd2_opp50 = opp_find_freq_ceil(OPP_L3, &freq); + freq = ULONG_MAX; + vdd2_opp100 = opp_find_freq_floor(OPP_L3, &freq); + vdd2_opp50_volt = opp_get_voltage(vdd2_opp50); + vdd2_opp100_volt = opp_get_voltage(vdd2_opp100); + omap_push_sram_idle(); #ifdef CONFIG_SUSPEND suspend_set_ops(&omap_pm_ops); diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c index c5e9c42..e84a0ff 100644 --- a/arch/arm/mach-omap2/voltage.c +++ b/arch/arm/mach-omap2/voltage.c @@ -173,6 +173,7 @@ static struct omap_volt_data omap34xx_vdd2_volt_data[] = { {975000, 0, 0xF4, 0x0C}, {1050000, 0, 0xF4, 0x0C}, {1150000, 0, 0xF9, 0x18}, + {1200000, 0, 0xF9, 0x18}, }; static struct omap_volt_data omap36xx_vdd2_volt_data[] = { -- 1.5.4.3 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html