[U-Boot] [PATCH 14/14] sunxi: dram: Autodetect DDR3 bus width and density

Siarhei Siamashka siarhei.siamashka at gmail.com
Fri Jul 18 18:23:05 CEST 2014


In the case if the 'dram_para' struct does not specify the exact bus width
or chip density, just use a trial and error method to find a usable
configuration.

Because all the major bugs in the DRAM initialization sequence are now
hopefully fixed, it should be safe to re-initialize the DRAM controller
multiple times until we get it configured right. The original Allwinner's
boot0 bootloader also used a similar autodetection trick.

The DDR3 spec contains the package pinout and addressing table for different
possible chip densities. It appears to be impossible to distinguish between a
single chip with 16 I/O data lines and a pair of chips with 8 I/O data lines
in the case if they provide the same storage capacity. Because a single 16-bit
chip has a higher density than a pair of equivalent 8-bit chips, it has
stricter refresh timings. So in the case of doubt, we assume that 16-bit
chips are used. Additionally, only Allwinner A20 has all A0-A15 address
lines and can support densities up to 8192. The older Allwinner A10 and
Allwinner A13 can only support densities up to 4096.

We deliberately leave out DDR2, dual-rank configurations and the special case
of a 8-bit chip with density 8192. None of these configurations seem to have
been ever used in real devices. And no new devices are likely to use these
exotic configurations (because only up to 2GB of RAM can be populated in any
case).

This DRAM autodetection feature potentially allows to have a single low
performance fail-safe DDR3 initialiazation for a universal single bootloader
binary, which can be compatible with all Allwinner A10/A13/A20 based devices
(if the ifdefs are replaced with a runtime SoC type detection).

Signed-off-by: Siarhei Siamashka <siarhei.siamashka at gmail.com>
---
 arch/arm/cpu/armv7/sunxi/dram.c | 52 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 47 insertions(+), 5 deletions(-)

diff --git a/arch/arm/cpu/armv7/sunxi/dram.c b/arch/arm/cpu/armv7/sunxi/dram.c
index 9f55799..3778cc9 100644
--- a/arch/arm/cpu/armv7/sunxi/dram.c
+++ b/arch/arm/cpu/armv7/sunxi/dram.c
@@ -536,17 +536,13 @@ static void mctl_set_impedance(u32 zq, u32 odt_en)
 	writel(DRAM_IOCR_ODT_EN(odt_en), &dram->iocr);
 }
 
-unsigned long dramc_init(struct dram_para *para)
+static unsigned long dramc_init_helper(struct dram_para *para)
 {
 	struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
 	u32 reg_val;
 	u32 density;
 	int ret_val;
 
-	/* check input dram parameter structure */
-	if (!para)
-		return 0;
-
 	/*
 	 * only single rank DDR3 is supported by this code even though the
 	 * hardware can theoretically support DDR2 and up to two ranks
@@ -671,3 +667,49 @@ unsigned long dramc_init(struct dram_para *para)
 
 	return get_ram_size((long *)PHYS_SDRAM_0, PHYS_SDRAM_0_SIZE);
 }
+
+unsigned long dramc_init(struct dram_para *para)
+{
+	unsigned long dram_size, actual_density;
+
+	/* If the dram configuration is not provided, use a default */
+	if (!para)
+		return 0;
+
+	/* if everything is known, then autodetection is not necessary */
+	if (para->io_width && para->bus_width && para->density)
+		return dramc_init_helper(para);
+
+	/* try to autodetect the DRAM bus width and density */
+	para->io_width  = 16;
+	para->bus_width = 32;
+#if defined(CONFIG_SUN4I) || defined(CONFIG_SUN5I)
+	/* only A0-A14 address lines on A10/A13, limiting max density to 4096 */
+	para->density = 4096;
+#else
+	/* all A0-A15 address lines on A20, which allow density 8192 */
+	para->density = 8192;
+#endif
+
+	dram_size = dramc_init_helper(para);
+	if (!dram_size) {
+		/* if 32-bit bus width failed, try 16-bit bus width instead */
+		para->bus_width = 16;
+		dram_size = dramc_init_helper(para);
+		if (!dram_size) {
+			/* if 16-bit bus width also failed, then bail out */
+			return dram_size;
+		}
+	}
+
+	/* check if we need to adjust the density */
+	actual_density = (dram_size >> 17) * para->io_width / para->bus_width;
+
+	if (actual_density != para->density) {
+		/* update the density and re-initialize DRAM again */
+		para->density = actual_density;
+		dram_size = dramc_init_helper(para);
+	}
+
+	return dram_size;
+}
-- 
1.8.3.2



More information about the U-Boot mailing list