[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