summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSiarhei Siamashka <siarhei.siamashka@gmail.com>2014-08-03 05:32:54 +0300
committerHans de Goede <hdegoede@redhat.com>2014-08-12 08:42:34 +0200
commitbf4ca384ad991c09cad7dd7838c1da6756c101b9 (patch)
treef1c7102049204dee8c647f93147a86a8a697b0a3
parent935758b1d5e58ebd24d8570487455ba286ba4656 (diff)
sunxi: dram: Autodetect DDR3 bus width and density
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> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-rw-r--r--arch/arm/cpu/armv7/sunxi/dram.c52
1 files changed, 47 insertions, 5 deletions
diff --git a/arch/arm/cpu/armv7/sunxi/dram.c b/arch/arm/cpu/armv7/sunxi/dram.c
index 2ad685b2f8..584f7420d7 100644
--- a/arch/arm/cpu/armv7/sunxi/dram.c
+++ b/arch/arm/cpu/armv7/sunxi/dram.c
@@ -572,17 +572,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
@@ -706,3 +702,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;
+}