[U-Boot] [PATCH v2 08/16] sunxi: dram: Re-introduce the impedance calibration ond ODT

Siarhei Siamashka siarhei.siamashka at gmail.com
Sun Aug 3 04:32:46 CEST 2014


The DRAM controller allows to configure impedance either by using the
calibration against an external high precision 240 ohm resistor, or
by skipping the calibration and loading pre-defined data. The DRAM
controller register guide is available here:

    http://linux-sunxi.org/A10_DRAM_Controller_Register_Guide#SDR_ZQCR0

The new code supports both of the impedance configuration modes:
   - If the higher bits of the 'zq' parameter in the 'dram_para' struct
     are zero, then the lowest 8 bits are used as the ZPROG value, where
     two divisors encoded in lower and higher 4 bits. One divisor is
     used for calibrating the termination impedance, and another is used
     for the output impedance.
   - If bits 27:8 in the 'zq' parameters are non-zero, then they are
     used as the pre-defined ZDATA value instead of performing the ZQ
     calibration.

Two lowest bits in the 'odt_en' parameter enable ODT for the DQ and DQS
lines individually. Enabling ODT for both DQ and DQS means that the
'odt_en' parameter needs to be set to 3.

Signed-off-by: Siarhei Siamashka <siarhei.siamashka at gmail.com>
---
Changes for v2:
   - this is the last part half of the old patch [PATCH 05/14]
     "sunxi: dram: Code cleanup for the impedance calibration",
     split as agreed with Ian Campbell.
   - new commit message now explains how the code works
   - more comments are added to the code

 arch/arm/cpu/armv7/sunxi/dram.c        | 56 ++++++++++++++++++++++++++++++++++
 arch/arm/include/asm/arch-sunxi/dram.h |  4 +++
 2 files changed, 60 insertions(+)

diff --git a/arch/arm/cpu/armv7/sunxi/dram.c b/arch/arm/cpu/armv7/sunxi/dram.c
index 4a72480..f87cbeb 100644
--- a/arch/arm/cpu/armv7/sunxi/dram.c
+++ b/arch/arm/cpu/armv7/sunxi/dram.c
@@ -445,6 +445,60 @@ static void mctl_ddr3_initialize(void)
 	await_bits_clear(&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
+	/* Appears that some kind of automatically initiated default
+	 * ZQ calibration is already in progress at this point on sun4i/sun5i
+	 * hardware, but not on sun7i. So it is reasonable to wait for its
+	 * completion before doing anything else. */
+	await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE);
+#endif
+
+	/* ZQ calibration is not really useful unless ODT is enabled */
+	if (!odt_en)
+		return;
+
+#ifdef CONFIG_SUN7I
+	/* Enabling ODT in SDR_IOCR on sun7i hardware results in a deadlock
+	 * unless bit 24 is set in SDR_ZQCR1. Not much is known about the
+	 * SDR_ZQCR1 register, but there are hints indicating that it might
+	 * be related to periodic impedance re-calibration. This particular
+	 * magic value is borrowed from the Allwinner boot0 bootloader, and
+	 * using it helps to avoid troubles */
+	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;
@@ -505,6 +559,8 @@ unsigned long dramc_init(struct dram_para *para)
 
 	dramc_clock_output_en(1);
 
+	mctl_set_impedance(para->zq, para->odt_en);
+
 	mctl_set_cke_delay();
 
 	mctl_ddr3_reset();
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



More information about the U-Boot mailing list