summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorSiarhei Siamashka <siarhei.siamashka@gmail.com>2014-08-03 05:32:46 +0300
committerHans de Goede <hdegoede@redhat.com>2014-08-12 08:42:33 +0200
commit5c18384dea60a9acbbc6f2f95a5e291089db7e94 (patch)
tree4594f7dd4d9cfee94ad857627e29f977cffbf347 /arch
parent94cd3019881523b7776138f93f9e6b9849adc4d4 (diff)
downloadtalos-obmc-uboot-5c18384dea60a9acbbc6f2f95a5e291089db7e94.tar.gz
talos-obmc-uboot-5c18384dea60a9acbbc6f2f95a5e291089db7e94.zip
sunxi: dram: Re-introduce the impedance calibration ond ODT
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@gmail.com> Acked-by: Ian Campbell <ijc@hellion.org.uk> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/cpu/armv7/sunxi/dram.c56
-rw-r--r--arch/arm/include/asm/arch-sunxi/dram.h4
2 files changed, 60 insertions, 0 deletions
diff --git a/arch/arm/cpu/armv7/sunxi/dram.c b/arch/arm/cpu/armv7/sunxi/dram.c
index 4a72480425..f87cbebae2 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 67fbfad07e..4433eeb29e 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)
OpenPOWER on IntegriCloud