All of lore.kernel.org
 help / color / mirror / Atom feed
From: Vishwanath BS <vishwanath.bs@ti.com>
To: linux-omap@vger.kernel.org
Cc: Vishwanath BS <vishwanath.bs@ti.com>,
	Peter 'p2' De Schrijver <peter.de-schrijver@nokia.com>,
	Shweta Gulati <shweta.gulati@ti.com>
Subject: [PATCH V3] OMAP3: PM: Workaround for DLL Lock issue
Date: Thu, 27 May 2010 18:20:26 +0530	[thread overview]
Message-ID: <1274964626-5202-1-git-send-email-vishwanath.bs@ti.com> (raw)
In-Reply-To: <>

From: Vishwanath BS <vishwanath.bs@ti.com>

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@1.15V):
	"	Lower the frequency to 83MHz.

	o	In case of CORE is at OPP2 (83MHz@1.05V):
	"	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@nokia.com>

Signed-off-by: Shweta Gulati <shweta.gulati@ti.com> 
Signed-off-by: Vishwanath BS <vishwanath.bs@ti.com>
---
 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


             reply	other threads:[~2010-05-27 12:49 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-05-27 12:50 Vishwanath BS [this message]
2010-06-21  4:44 ` [PATCH V3] OMAP3: PM: Workaround for DLL Lock issue Sripathy, Vishwanath
  -- strict thread matches above, loose matches on Subject: below --
2010-05-27 12:45 Vishwanath BS

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1274964626-5202-1-git-send-email-vishwanath.bs@ti.com \
    --to=vishwanath.bs@ti.com \
    --cc=linux-omap@vger.kernel.org \
    --cc=peter.de-schrijver@nokia.com \
    --cc=shweta.gulati@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.