Moved the impedance setup code part into a separate function. Added explicit
wait for ZQ calibration completion before proceeding to the next initialization
steps. Removed the CONFIG_SUN7I ifdef guard around the code, which has identical
behaviour on sun4i/sun5i/sun7i. And if 'odt_en' is set in the 'dram_para' 
struct,
then ODT now actually gets enabled in the DRAM_IOCR register (which the older
code failed to do and was always running without ODT no matter the settings).

Signed-off-by: Siarhei Siamashka <siarhei.siamas...@gmail.com>
---
 arch/arm/cpu/armv7/sunxi/dram.c        | 71 ++++++++++++++++++++++++++++------
 arch/arm/include/asm/arch-sunxi/dram.h |  4 ++
 2 files changed, 63 insertions(+), 12 deletions(-)

diff --git a/arch/arm/cpu/armv7/sunxi/dram.c b/arch/arm/cpu/armv7/sunxi/dram.c
index def4247..afeb2df 100644
--- a/arch/arm/cpu/armv7/sunxi/dram.c
+++ b/arch/arm/cpu/armv7/sunxi/dram.c
@@ -49,6 +49,19 @@ static void await_completion(u32 *reg, u32 mask)
 }
 
 /*
+ * Wait up to 1s for mask to be set in given reg.
+ */
+static void await_bits_set(u32 *reg, u32 mask)
+{
+       unsigned long tmo = timer_get_us() + 1000000;
+
+       while ((readl(reg) & mask) != mask) {
+               if (timer_get_us() > tmo)
+                       panic("Timeout initialising DRAM\n");
+       }
+}
+
+/*
  * This performs the external DRAM reset by driving the RESET pin low and
  * then high again. According to the DDR3 spec, the RESET pin needs to be
  * kept low for at least 200 us.
@@ -353,6 +366,51 @@ static void mctl_ddr3_initialize(void)
        await_completion(&dram->ccr, DRAM_CCR_INIT);
 }
 
+/*
+ * Perform impedance calibration on the DRAM controller side of the wire.
+ */
+static void mctl_set_impedance(u32 zq, u32 odt_en)
+{
+       struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
+       u32 reg_val;
+       u32 zprog = zq & 0xFF, zdata = (zq >> 8) & 0xFFFFF;
+
+#ifndef CONFIG_SUN7I
+       /* wait for the default impedance configuration to settle */
+       await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE);
+#endif
+
+       if (!odt_en)
+               return;
+
+#ifdef CONFIG_SUN7I
+       /* some weird magic, but sun7i fails to boot without it */
+       writel((1 << 24) | (1 << 1), &dram->zqcr1);
+#endif
+
+       /* needed at least for sun5i, because it does not self clear there */
+       clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL);
+
+       if (zdata) {
+               /* set the user supplied impedance data */
+               reg_val = DRAM_ZQCR0_ZDEN | zdata;
+               writel(reg_val, &dram->zqcr0);
+               /* no need to wait, this takes effect immediately */
+       } else {
+               /* do the calibration using the external resistor */
+               reg_val = DRAM_ZQCR0_ZCAL | DRAM_ZQCR0_IMP_DIV(zprog);
+               writel(reg_val, &dram->zqcr0);
+               /* wait for the new impedance configuration to settle */
+               await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE);
+       }
+
+       /* needed at least for sun5i, because it does not self clear there */
+       clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL);
+
+       /* set I/O configure register */
+       writel(DRAM_IOCR_ODT_EN(odt_en), &dram->iocr);
+}
+
 unsigned long dramc_init(struct dram_para *para)
 {
        struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
@@ -407,20 +465,9 @@ unsigned long dramc_init(struct dram_para *para)
        reg_val |= DRAM_DCR_MODE(DRAM_DCR_MODE_INTERLEAVE);
        writel(reg_val, &dram->dcr);
 
-#ifdef CONFIG_SUN7I
-       setbits_le32(&dram->zqcr1, (0x1 << 24) | (0x1 << 1));
-       if (para->tpr4 & 0x2)
-               clrsetbits_le32(&dram->zqcr1, (0x1 << 24), (0x1 << 1));
        dramc_clock_output_en(1);
-#endif
 
-#if (defined(CONFIG_SUN5I) || defined(CONFIG_SUN7I))
-       /* set odt impendance divide ratio */
-       reg_val = ((para->zq) >> 8) & 0xfffff;
-       reg_val |= ((para->zq) & 0xff) << 20;
-       reg_val |= (para->zq) & 0xf0000000;
-       writel(reg_val, &dram->zqcr0);
-#endif
+       mctl_set_impedance(para->zq, para->odt_en);
 
        mctl_set_cke_delay();
 
diff --git a/arch/arm/include/asm/arch-sunxi/dram.h 
b/arch/arm/include/asm/arch-sunxi/dram.h
index 67fbfad..4433eeb 100644
--- a/arch/arm/include/asm/arch-sunxi/dram.h
+++ b/arch/arm/include/asm/arch-sunxi/dram.h
@@ -159,6 +159,10 @@ struct dram_para {
 
 #define DRAM_ZQCR0_IMP_DIV(n) (((n) & 0xff) << 20)
 #define DRAM_ZQCR0_IMP_DIV_MASK DRAM_ZQCR0_IMP_DIV(0xff)
+#define DRAM_ZQCR0_ZCAL (1 << 31) /* Starts ZQ calibration when set to 1 */
+#define DRAM_ZQCR0_ZDEN (1 << 28) /* Uses ZDATA instead of doing calibration */
+
+#define DRAM_ZQSR_ZDONE (1 << 31) /* ZQ calibration completion flag */
 
 #define DRAM_IOCR_ODT_EN(n) ((((n) & 0x3) << 30) | ((n) & 0x3) << 0)
 #define DRAM_IOCR_ODT_EN_MASK DRAM_IOCR_ODT_EN(0x3)
-- 
1.8.3.2

-- 
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to linux-sunxi+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to