diff mbox

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

Message ID 1405700585-13546-15-git-send-email-siarhei.siamashka@gmail.com
State Changes Requested
Delegated to: Ian Campbell
Headers show

Commit Message

Siarhei Siamashka July 18, 2014, 4:23 p.m. UTC
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@gmail.com>
---
 arch/arm/cpu/armv7/sunxi/dram.c | 52 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 47 insertions(+), 5 deletions(-)

Comments

Ian Campbell July 21, 2014, 7:54 p.m. UTC | #1
On Fri, 2014-07-18 at 19:23 +0300, Siarhei Siamashka wrote:
> 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@gmail.com>

Acked-by: Ian Campbell <ijc@hellion.org.uk>
diff mbox

Patch

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;
+}