aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-omap2/dpll44xx.c38
-rw-r--r--arch/arm/mach-omap2/emif.c99
-rw-r--r--arch/arm/mach-omap2/include/mach/emif.h2
3 files changed, 139 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/dpll44xx.c b/arch/arm/mach-omap2/dpll44xx.c
index bc6d418..d4cfe2a 100644
--- a/arch/arm/mach-omap2/dpll44xx.c
+++ b/arch/arm/mach-omap2/dpll44xx.c
@@ -79,6 +79,35 @@ int omap4_core_dpll_m2_set_rate(struct clk *clk, unsigned long rate)
clkdm_wakeup(l3_emif_clkdm);
/*
+ * Errata ID: i728
+ *
+ * DESCRIPTION:
+ *
+ * If during a small window the following three events occur:
+ *
+ * 1) The EMIF_PWR_MGMT_CTRL[7:4] REG_SR_TIM SR_TIMING counter expires
+ * 2) Frequency change update is requested CM_SHADOW_FREQ_CONFIG1
+ * FREQ_UPDATE set to 1
+ * 3) OCP access is requested
+ *
+ * There will be clock instability on the DDR interface.
+ *
+ * WORKAROUND:
+ *
+ * Prevent event 1) while event 2) is happening.
+ *
+ * Disable the self-refresh when requesting a frequency change.
+ * Before requesting a frequency change, program
+ * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE to 0x0
+ * (omap_emif_frequency_pre_notify)
+ *
+ * When the frequency change is completed, reprogram
+ * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE to 0x2.
+ * (omap_emif_frequency_post_notify)
+ */
+ omap_emif_frequency_pre_notify();
+
+ /*
* Program EMIF timing parameters in EMIF shadow registers
* for targetted DRR clock.
* DDR Clock = core_dpll_m2 / 2
@@ -107,6 +136,9 @@ int omap4_core_dpll_m2_set_rate(struct clk *clk, unsigned long rate)
& OMAP4430_FREQ_UPDATE_MASK) == 0),
MAX_FREQ_UPDATE_TIMEOUT, i);
+ /* Re-enable DDR self refresh */
+ omap_emif_frequency_post_notify();
+
/* Configures MEMIF domain back to HW_WKUP */
clkdm_allow_idle(l3_emif_clkdm);
@@ -150,6 +182,9 @@ int omap4_prcm_freq_update(void)
/* Configures MEMIF domain in SW_WKUP */
clkdm_wakeup(l3_emif_clkdm);
+ /* Disable DDR self refresh (Errata ID: i728) */
+ omap_emif_frequency_pre_notify();
+
/*
* FREQ_UPDATE sequence:
* - DLL_OVERRIDE=0 (DLL lock & code must not be overridden
@@ -167,6 +202,9 @@ int omap4_prcm_freq_update(void)
& OMAP4430_FREQ_UPDATE_MASK) == 0),
MAX_FREQ_UPDATE_TIMEOUT, i);
+ /* Re-enable DDR self refresh */
+ omap_emif_frequency_post_notify();
+
/* Configures MEMIF domain back to HW_WKUP */
clkdm_allow_idle(l3_emif_clkdm);
diff --git a/arch/arm/mach-omap2/emif.c b/arch/arm/mach-omap2/emif.c
index 50f1128..8f4ec5f 100644
--- a/arch/arm/mach-omap2/emif.c
+++ b/arch/arm/mach-omap2/emif.c
@@ -38,6 +38,7 @@ struct emif_instance {
void __iomem *base;
u16 irq;
struct platform_device *pdev;
+ bool ddr_refresh_disabled;
};
static struct emif_instance emif[EMIF_NUM_INSTANCES];
static struct emif_regs *emif_curr_regs[EMIF_NUM_INSTANCES];
@@ -520,6 +521,47 @@ static u32 get_ddr_phy_ctrl_1(u32 freq, u8 RL)
}
/*
+ * get_lp_mode - Get the LP Mode of a EMIF instance.
+ *
+ * It returns the REG_LP_MODE of EMIF_PWR_MGMT_CTRL[10:8]
+ * for a EMIF.
+ *
+ */
+static u32 get_lp_mode(u32 emif_nr)
+{
+ u32 temp, lpmode;
+ void __iomem *base = emif[emif_nr].base;
+
+ temp = readl(base + OMAP44XX_EMIF_PWR_MGMT_CTRL);
+ lpmode = (temp & OMAP44XX_REG_LP_MODE_MASK) >>
+ OMAP44XX_REG_LP_MODE_SHIFT;
+
+ return lpmode;
+}
+
+/*
+ * set_lp_mode - Set the LP Mode of a EMIF instance.
+ *
+ * It replaces the REG_LP_MODE of EMIF_PWR_MGMT_CTRL[10:8]
+ * with the new value for a EMIF.
+ *
+ */
+static void set_lp_mode(u32 emif_nr, u32 lpmode)
+{
+ u32 temp;
+ void __iomem *base = emif[emif_nr].base;
+
+ /* Extract current lp mode value */
+ temp = readl(base + OMAP44XX_EMIF_PWR_MGMT_CTRL);
+
+ /* Write out the new lp mode value */
+ temp &= ~OMAP44XX_REG_LP_MODE_MASK;
+ temp |= lpmode << OMAP44XX_REG_LP_MODE_SHIFT;
+ writel(temp, base + OMAP44XX_EMIF_PWR_MGMT_CTRL);
+
+}
+
+/*
* Get the temperature level of the EMIF instance:
* Reads the MR4 register of attached SDRAM parts to find out the temperature
* level. If there are two parts attached(one on each CS), then the temperature
@@ -861,6 +903,8 @@ static int __devinit omap_emif_probe(struct platform_device *pdev)
pr_info("EMIF%d is enabled with IRQ%d\n", id, emif[id].irq);
+ emif[id].ddr_refresh_disabled = false;
+
return 0;
}
@@ -1264,6 +1308,61 @@ int omap_emif_setup_registers(u32 freq, u32 volt_state)
return err;
}
+
+/*
+ * omap_emif_frequency_pre_notify - Disable DDR self refresh of both EMIFs
+ *
+ * It disables the LP mode if the LP mode of EMIFs was LP_MODE_SELF_REFRESH.
+ *
+ * It should be called before any PRCM frequency update sequence.
+ * After the frequency update sequence, omap_emif_frequency_post_notify
+ * should be called to restore the original LP MODE setting of the EMIFs.
+ *
+ */
+void omap_emif_frequency_pre_notify(void)
+{
+ int emif_num;
+
+ for (emif_num = EMIF1; emif_num < EMIF_NUM_INSTANCES; emif_num++) {
+
+ /*
+ * Only disable ddr self-refresh
+ * if ddr self-refresh was enabled
+ */
+ if (likely(LP_MODE_SELF_REFRESH == get_lp_mode(emif_num))) {
+
+ set_lp_mode(emif_num, LP_MODE_DISABLE);
+ emif[emif_num].ddr_refresh_disabled = true;
+ }
+
+ }
+}
+
+/*
+ * omap_emif_frequency_post_notify - Enable DDR self refresh of both EMIFs
+ *
+ * It restores the LP mode of the EMIFs back to LP_MODE_SELF_REFRESH if it
+ * was previously disabled by omap_emif_frequency_pre_notify()
+ *
+ */
+void omap_emif_frequency_post_notify(void)
+{
+ int emif_num;
+
+ for (emif_num = EMIF1; emif_num < EMIF_NUM_INSTANCES; emif_num++) {
+
+ /*
+ * Only re-enable ddr self-refresh
+ * if ddr self-refresh was disabled
+ */
+ if (likely(emif[emif_num].ddr_refresh_disabled)) {
+
+ set_lp_mode(emif_num, LP_MODE_SELF_REFRESH);
+ emif[emif_num].ddr_refresh_disabled = false;
+ }
+ }
+}
+
/*
* omap_emif_setup_device_details - save the SDRAM device details passed
* from the board file
diff --git a/arch/arm/mach-omap2/include/mach/emif.h b/arch/arm/mach-omap2/include/mach/emif.h
index 3a65b20..8a3ea34 100644
--- a/arch/arm/mach-omap2/include/mach/emif.h
+++ b/arch/arm/mach-omap2/include/mach/emif.h
@@ -258,6 +258,8 @@ struct emif_regs {
int omap_emif_setup_registers(u32 freq,
u32 volt_state);
+void omap_emif_frequency_pre_notify(void);
+void omap_emif_frequency_post_notify(void);
int omap_emif_setup_device_details(
const struct emif_device_details *emif1_devices,
const struct emif_device_details *emif2_devices);