summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/tegra3_emc.c102
-rw-r--r--arch/arm/mach-tegra/tegra3_emc.h19
2 files changed, 104 insertions, 17 deletions
diff --git a/arch/arm/mach-tegra/tegra3_emc.c b/arch/arm/mach-tegra/tegra3_emc.c
index 044db65e28d1..b4bf0755f197 100644
--- a/arch/arm/mach-tegra/tegra3_emc.c
+++ b/arch/arm/mach-tegra/tegra3_emc.c
@@ -277,6 +277,58 @@ static inline void auto_cal_disable(void)
}
}
+static inline bool dqs_preset(const struct tegra_emc_table *next_timing,
+ const struct tegra_emc_table *last_timing)
+{
+ bool ret = false;
+
+#define DQS_SET(reg, bit) \
+ do { \
+ if ((next_timing->burst_regs[EMC_##reg##_INDEX] & \
+ EMC_##reg##_##bit##_ENABLE) && \
+ (!(last_timing->burst_regs[EMC_##reg##_INDEX] & \
+ EMC_##reg##_##bit##_ENABLE))) { \
+ emc_writel(last_timing->burst_regs[EMC_##reg##_INDEX] \
+ | EMC_##reg##_##bit##_ENABLE, EMC_##reg); \
+ ret = true; \
+ } \
+ } while (0)
+
+ DQS_SET(XM2DQSPADCTRL2, VREF);
+ DQS_SET(XM2DQSPADCTRL3, VREF);
+ DQS_SET(XM2QUSEPADCTRL, IVREF);
+
+ return ret;
+}
+
+static inline void overwrite_mrs_wait_cnt(
+ const struct tegra_emc_table *next_timing,
+ bool zcal_long)
+{
+ u32 reg;
+ u32 cnt = 512;
+
+ /* For ddr3 when DLL is re-started: overwrite EMC DFS table settings
+ for MRS_WAIT_LONG with maximum of MRS_WAIT_SHORT settings and
+ expected operation length. Reduce the latter by the overlapping
+ zq-calibration, if any */
+ if (zcal_long)
+ cnt -= dram_dev_num * 256;
+
+ reg = (next_timing->burst_regs[EMC_MRS_WAIT_CNT_INDEX] &
+ EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK) >>
+ EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT;
+ if (cnt < reg)
+ cnt = reg;
+
+ reg = (next_timing->burst_regs[EMC_MRS_WAIT_CNT_INDEX] &
+ (~EMC_MRS_WAIT_CNT_LONG_WAIT_MASK));
+ reg |= (cnt << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT) &
+ EMC_MRS_WAIT_CNT_LONG_WAIT_MASK;
+
+ emc_writel(reg, EMC_MRS_WAIT_CNT);
+}
+
static inline bool need_qrst(const struct tegra_emc_table *next_timing,
const struct tegra_emc_table *last_timing,
u32 emc_dpd_reg)
@@ -385,26 +437,38 @@ static noinline void emc_set_clock(const struct tegra_emc_table *next_timing,
const struct tegra_emc_table *last_timing,
u32 clk_setting)
{
- int i, dll_change;
+ int i, dll_change, pre_wait;
bool dyn_sref_enabled, vref_cal_toggle, qrst_used, zcal_long;
u32 emc_cfg_reg = emc_readl(EMC_CFG);
u32 emc_dbg_reg = emc_readl(EMC_DBG);
+ dyn_sref_enabled = emc_cfg_reg & EMC_CFG_DYN_SREF_ENABLE;
+ dll_change = get_dll_change(next_timing, last_timing);
+ zcal_long = (next_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX] != 0) &&
+ (last_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX] == 0);
+
/* FIXME: remove steps enumeration below? */
/* 1. clear clkchange_complete interrupts */
emc_writel(EMC_INTSTATUS_CLKCHANGE_COMPLETE, EMC_INTSTATUS);
- /* 2. disable dynamic self-refresh and wait for possible self-refresh
- entry/exit - waiting here before the clock change decreases worst
- case clock change stall time */
- dyn_sref_enabled = emc_cfg_reg & EMC_CFG_DYN_SREF_ENABLE;
+ /* 2. disable dynamic self-refresh and preset dqs vref, then wait for
+ possible self-refresh entry/exit and/or dqs vref settled - waiting
+ before the clock change decreases worst case change stall time */
+ pre_wait = 0;
if (dyn_sref_enabled) {
emc_cfg_reg &= ~EMC_CFG_DYN_SREF_ENABLE;
emc_writel(emc_cfg_reg, EMC_CFG);
+ pre_wait = 5; /* 5us+ for self-refresh entry/exit */
+ }
+ if (dqs_preset(next_timing, last_timing)) {
+ if (pre_wait < 3)
+ pre_wait = 3; /* 3us+ for dqs vref settled */
+ }
+ if (pre_wait) {
emc_timing_update();
- udelay(5); /* wait for possible self-refresh entry/exit */
+ udelay(pre_wait);
}
/* 3. disable auto-cal if vref mode is switching */
@@ -415,13 +479,18 @@ static noinline void emc_set_clock(const struct tegra_emc_table *next_timing,
if (vref_cal_toggle)
auto_cal_disable();
- /* 4. program burst shadow registers
- the last read below makes sure writes are completed*/
+ /* 4. program burst shadow registers */
for (i = 0; i < TEGRA_EMC_NUM_REGS; i++)
__raw_writel(next_timing->burst_regs[i], burst_reg_addr[i]);
wmb();
barrier();
+ /* On ddr3 when DLL is re-started predict MRS long wait count and
+ overwrite DFS table setting */
+ if ((dram_type == DRAM_TYPE_DDR3) && (dll_change == DLL_CHANGE_ON))
+ overwrite_mrs_wait_cnt(next_timing, zcal_long);
+
+ /* the last read below makes sure prev writes are completed */
qrst_used = need_qrst(next_timing, last_timing,
emc_readl(EMC_SEL_DPD_CTRL));
@@ -432,8 +501,10 @@ static noinline void emc_set_clock(const struct tegra_emc_table *next_timing,
if (qrst_used)
periodic_qrst_enable(emc_cfg_reg, emc_dbg_reg);
+ /* 6.1 disable auto-refresh to save time after clock change */
+ emc_writel(EMC_REFCTRL_DISABLE_ALL(dram_dev_num), EMC_REFCTRL);
+
/* 7. turn Off dll and enter self-refresh on DDR3 */
- dll_change = get_dll_change(next_timing, last_timing);
if (dram_type == DRAM_TYPE_DDR3) {
if (dll_change == DLL_CHANGE_OFF)
emc_writel(next_timing->emc_mode_1, EMC_EMRS);
@@ -456,8 +527,6 @@ static noinline void emc_set_clock(const struct tegra_emc_table *next_timing,
set_dram_mode(next_timing, last_timing, dll_change);
/* 12. issue zcal command if turning zcal On */
- zcal_long = (next_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX] != 0) &&
- (last_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX] == 0);
if (zcal_long) {
emc_writel(EMC_ZQ_CAL_LONG_CMD_DEV0, EMC_ZQ_CAL);
if (dram_dev_num > 1)
@@ -472,6 +541,9 @@ static noinline void emc_set_clock(const struct tegra_emc_table *next_timing,
wait for clk change completion */
do_clock_change(clk_setting);
+ /* 14.1 re-enable auto-refresh */
+ emc_writel(EMC_REFCTRL_ENABLE_ALL(dram_dev_num), EMC_REFCTRL);
+
/* 15. restore auto-cal */
if (vref_cal_toggle)
emc_writel(next_timing->emc_acal_interval,
@@ -488,12 +560,8 @@ static noinline void emc_set_clock(const struct tegra_emc_table *next_timing,
emc_writel(next_timing->emc_zcal_cnt_long, EMC_ZCAL_WAIT_CNT);
/* 18. update restored timing */
- if (vref_cal_toggle || dyn_sref_enabled || zcal_long) {
- /* let ZQ calibration, other ops complete before updating
- restored timing settings */
- udelay(2);
- emc_timing_update();
- }
+ udelay(2);
+ emc_timing_update();
}
static inline void emc_get_timing(struct tegra_emc_table *timing)
diff --git a/arch/arm/mach-tegra/tegra3_emc.h b/arch/arm/mach-tegra/tegra3_emc.h
index c55214e95ebd..e4d40dd500e6 100644
--- a/arch/arm/mach-tegra/tegra3_emc.h
+++ b/arch/arm/mach-tegra/tegra3_emc.h
@@ -51,6 +51,16 @@ void tegra_init_emc(const struct tegra_emc_table *table, int table_size);
#define EMC_CFG_PERIODIC_QRST (0x1 << 21)
#define EMC_CFG_DYN_SREF_ENABLE (0x1 << 28)
+#define EMC_REFCTRL 0x20
+#define EMC_REFCTRL_DEV_SEL_SHIFT 0
+#define EMC_REFCTRL_DEV_SEL_MASK (0x3 << EMC_REFCTRL_DEV_SEL_SHIFT)
+#define EMC_REFCTRL_ENABLE (0x1 << 31)
+#define EMC_REFCTRL_ENABLE_ALL(num) \
+ ((((num > 1) ? 0 : 2) << EMC_REFCTRL_DEV_SEL_SHIFT) \
+ | EMC_REFCTRL_ENABLE)
+#define EMC_REFCTRL_DISABLE_ALL(num) \
+ (((num > 1) ? 0 : 2) << EMC_REFCTRL_DEV_SEL_SHIFT)
+
#define EMC_TIMING_CONTROL 0x28
#define EMC_RC 0x2c
#define EMC_RFC 0x30
@@ -89,7 +99,14 @@ void tegra_init_emc(const struct tegra_emc_table *table, int table_size);
#define EMC_ODT_READ 0xb4
#define EMC_WEXT 0xb8
#define EMC_CTT 0xbc
+
#define EMC_MRS_WAIT_CNT 0xc8
+#define EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT 0
+#define EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK \
+ (0x3FF << EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT)
+#define EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT 16
+#define EMC_MRS_WAIT_CNT_LONG_WAIT_MASK \
+ (0x3FF << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT)
#define EMC_MRS 0xcc
#define EMC_MODE_SET_DLL_RESET (0x1 << 8)
@@ -111,6 +128,7 @@ enum {
#define EMC_MRW 0xe8
#define EMC_MRR 0xec
#define EMC_XM2DQSPADCTRL3 0xf8
+#define EMC_XM2DQSPADCTRL3_VREF_ENABLE (0x1 << 5)
#define EMC_FBIO_CFG5 0x104
#define EMC_CFG5_TYPE_SHIFT 0x0
@@ -160,6 +178,7 @@ enum {
#define EMC_XM2CMDPADCTRL 0x2f0
#define EMC_XM2DQSPADCTRL2 0x2fc
+#define EMC_XM2DQSPADCTRL2_VREF_ENABLE (0x1 << 5)
#define EMC_XM2DQPADCTRL2 0x304
#define EMC_XM2CLKPADCTRL 0x308
#define EMC_XM2COMPPADCTRL 0x30c