Hi, As promised, in attachment my work around for the DLL lock issue for 3630. The patch also disables off mode for 3430 as the workaround for this chip is slightly different. This patch is posted for informational purposes only. Thanks, Peter. -- goa is a state of mind
>From 3749c16b10892e6cdfd7370f4c483ecac734f577 Mon Sep 17 00:00:00 2001 From: Peter 'p2' De Schrijver <peter.de-schrijver@xxxxxxxxx> Date: Thu, 25 Mar 2010 15:04:15 +0200 Subject: [PATCH] OMAP3 PM workaround for 3630 dll locking issue Signed-off-by: Peter 'p2' De Schrijver <peter.de-schrijver@xxxxxxxxx> --- arch/arm/mach-omap2/cpuidle34xx.c | 10 +++- arch/arm/mach-omap2/pm34xx.c | 78 +++++++++++++++++++++++- 2 files changed, 137 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c index 1cb64c1..30599ae 100644 --- a/arch/arm/mach-omap2/cpuidle34xx.c +++ b/arch/arm/mach-omap2/cpuidle34xx.c @@ -223,13 +223,17 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev, else per_state = saved_per_state; + + dss_state = pwrdm_read_pwrst(dss_pd); + if (dss_state == PWRDM_POWER_ON) + new_core_state = PWRDM_POWER_INACTIVE; + /* * If we are attempting CORE off, check if any other * powerdomains are at retention or higher. CORE off causes * chipwide reset which would reset these domains also. */ if (new_core_state == PWRDM_POWER_OFF) { - dss_state = pwrdm_read_pwrst(dss_pd); iva2_state = pwrdm_read_pwrst(iva2_pd); sgx_state = pwrdm_read_pwrst(sgx_pd); usb_state = pwrdm_read_pwrst(usb_pd); @@ -472,6 +476,10 @@ int __init omap3_idle_init(void) omap_init_power_states(); cpuidle_register_driver(&omap3_idle_driver); + /* Disable off mode on 3430 */ + if (!cpu_is_omap3630()) + omap3_power_states[OMAP3_STATE_C7].valid = 0; + dev = &per_cpu(omap3_idle_dev, smp_processor_id()); for (i = OMAP3_STATE_C1; i < OMAP3_MAX_STATES; i++) { diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index d75eb54..6b62f10 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -42,6 +42,7 @@ #include <plat/usb.h> #include <plat/resource.h> +#include <plat/opp_twl_tps.h> #include <asm/tlbflush.h> @@ -54,6 +55,8 @@ #include "pm.h" #include "sdrc.h" #include "omap3-opp.h" +#include "smartreflex.h" +#include "clock34xx.h" static int regset_save_on_suspend; @@ -92,6 +95,9 @@ static struct powerdomain *mpu_pwrdm, *neon_pwrdm; static struct powerdomain *core_pwrdm, *per_pwrdm; static int secure_ram_save_status; static int secure_ram_saved; +static struct clk *dpll3_clk; +static struct omap_opp *vdd2_opp50, *vdd2_opp100; +static u8 vdd2_opp50_vsel, vdd2_opp100_vsel; static struct prm_setup_vc prm_setup = { .clksetup = 0xff, @@ -370,6 +376,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; @@ -405,9 +412,10 @@ void omap_sram_idle(void) omap3_save_neon_context(); } - /* PER */ per_next_state = omap3_pwrdm_read_next_pwrst(per_pwrdm); core_next_state = omap3_pwrdm_read_next_pwrst(core_pwrdm); + + /* PER */ if (per_next_state < PWRDM_POWER_ON) { omap2_gpio_prepare_for_idle(per_next_state); if (per_next_state == PWRDM_POWER_OFF) @@ -462,6 +470,27 @@ void omap_sram_idle(void) if (regset_save_on_suspend) pm_dbg_regset_save(1); + if (core_next_state < PWRDM_POWER_INACTIVE && cpu_is_omap3630()) { + u32 clksel1_pll, v; + + clksel1_pll = cm_read_mod_reg(PLL_MOD, OMAP3430_CM_CLKSEL1_PLL); + prev_dpll3_div = clksel1_pll >> 28; + + if (prev_dpll3_div == 1) + omap3_core_dpll_m2_set_rate(dpll3_clk, + opp_get_freq(vdd2_opp50) * 2); + else + sr_voltagescale_vcbypass(ID_VDD(2), ID_VDD(2), + vdd2_opp100_vsel, vdd2_opp50_vsel); + + /* enable DPLL3 autoidle */ + v = cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE); + v |= 1; + cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE); + } + + memcpy(save_sdrc_counters, _sdrc_counters, sizeof(save_sdrc_counters)); + /* * omap3_arm_context is the location where ARM registers * get saved. The restore path then reads from this @@ -483,6 +512,42 @@ void omap_sram_idle(void) if (pwrdm_read_prev_pwrst(mpu_pwrdm) == PWRDM_POWER_OFF) restore_table_entry(); + if (core_next_state < PWRDM_POWER_INACTIVE && cpu_is_omap3630()) { + 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 >> 28) == 2) { + /* restore VDD2 OPP2 voltage */ + sr_voltagescale_vcbypass(ID_VDD(2), ID_VDD(2), + vdd2_opp50_vsel, + vdd2_opp100_vsel); + } + } else { + u32 v; + + /* disable DPLL3 autoidle */ + v = cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE); + v &= ~0x7; + cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE); + + if (prev_dpll3_div == 1) + omap3_core_dpll_m2_set_rate(dpll3_clk, + opp_get_freq(vdd2_opp100) * 2); + else + sr_voltagescale_vcbypass(ID_VDD(2), ID_VDD(2), + vdd2_opp50_vsel, + vdd2_opp100_vsel); + } + } + /* CORE */ if (core_next_state < PWRDM_POWER_ON) { core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm); @@ -512,7 +577,6 @@ void omap_sram_idle(void) } omap3_intc_resume_idle(); - memcpy(save_sdrc_counters, _sdrc_counters, sizeof(save_sdrc_counters)); /* * Enable smartreflex after WFI. Only needed if we entered @@ -1012,8 +1076,7 @@ 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) | - (1 << OMAP3430_AUTO_CORE_DPLL_SHIFT), + cm_write_mod_reg((1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT), PLL_MOD, CM_AUTOIDLE); cm_write_mod_reg(1 << OMAP3430ES2_AUTO_PERIPH2_DPLL_SHIFT, @@ -1242,6 +1305,13 @@ static int __init omap3_pm_init(void) per_pwrdm = pwrdm_lookup("per_pwrdm"); core_pwrdm = pwrdm_lookup("core_pwrdm"); + dpll3_clk = clk_get(NULL, "dpll3_m2_ck"); + + vdd2_opp50 = opp_find_by_opp_id(OPP_L3, 1); + vdd2_opp100 = opp_find_by_opp_id(OPP_L3, 2); + vdd2_opp50_vsel = omap_twl_uv_to_vsel(opp_get_voltage(vdd2_opp50)); + vdd2_opp100_vsel = omap_twl_uv_to_vsel(opp_get_voltage(vdd2_opp100)); + omap_push_sram_idle(); #ifdef CONFIG_SUSPEND suspend_set_ops(&omap_pm_ops); -- 1.6.2.4