From 3749c16b10892e6cdfd7370f4c483ecac734f577 Mon Sep 17 00:00:00 2001
From: Peter 'p2' De Schrijver <peter.de-schrijver@nokia.com>
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@nokia.com>
---
arch/arm/mach-omap2/cpuidle34xx.c | 10 +++-
arch/arm/mach-omap2/pm34xx.c | 78 +++++++++++++++++++++++-
2 files changed, 137 insertions(+), 5 deletions(-)
@@ -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++) {
@@ -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