diff mbox

[V3] OMAP3: PM: Workaround for DLL Lock issue

Message ID 1274964626-5202-1-git-send-email-vishwanath.bs@ti.com (mailing list archive)
State New, archived
Delegated to: Kevin Hilman
Headers show

Commit Message

Sripathy, Vishwanath May 27, 2010, 12:50 p.m. UTC
None
diff mbox

Patch

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[] = {