Half DQ width memory configuration is detected in the same way as rank:
by trying to access memory in full-width configuration and checking for
timeout error code.
The code is modeled after time_out_detect() routine from a leaked
vendor lib-dram u-boot source code. It's very fragile and accessing the
wrong memory location in a wrong order will leave the DRAM controller
stuck.
Tested on Allwinner A40i-based Wiren Board 7 automation controller,
for the following memory configurations:
* 1x 16-bit 4Gbit DDR3, 512 MiB total RAM
* 2x 16-bit 4Gbit DDR3, 1024 MiB total RAM
* 2x 16-bit 8Gbit DDR3, 2048 MiB total RAM
Signed-off-by: Evgeny Boger
---
.../include/asm/arch-sunxi/dram_sunxi_dw.h| 1 +
arch/arm/mach-sunxi/dram_sunxi_dw.c | 115 --
2 files changed, 79 insertions(+), 37 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h
b/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h
index e843c14202..90fec96c5c 100644
--- a/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h
+++ b/arch/arm/include/asm/arch-sunxi/dram_sunxi_dw.h
@@ -193,6 +193,7 @@ struct sunxi_mctl_ctl_reg {
#define PIR_INIT (0x1 << 0) /* PHY initialization trigger */
#define PGSR_INIT_DONE (0x1 << 0) /* PHY init done */
+#define PGSR_TIMEOUT (0x1 << 13) /* Timeout error status */
#define ZQCR_PWRDOWN (1U << 31) /* ZQ power down */
diff --git a/arch/arm/mach-sunxi/dram_sunxi_dw.c
b/arch/arm/mach-sunxi/dram_sunxi_dw.c
index 9107b114df..574c60a3f6 100644
--- a/arch/arm/mach-sunxi/dram_sunxi_dw.c
+++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c
@@ -417,9 +417,38 @@ static void mctl_set_cr(uint16_t socid, struct dram_para
*para)
/* Mux pin to A15 address line for single rank memory. */
if (!para->dual_rank)
setbits_le32(_com->cr_r1, MCTL_CR_R1_MUX_A15);
+ else
+ clrbits_le32(_com->cr_r1, MCTL_CR_R1_MUX_A15);
}
}
+static void mctl_set_bus_width(struct dram_para *para)
+{
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ /* set half DQ */
+ if (!para->bus_full_width) {
+#if defined CONFIG_SUNXI_DRAM_DW_32BIT
+ writel(0x0, _ctl->dx[2].gcr);
+ writel(0x0, _ctl->dx[3].gcr);
+#elif defined CONFIG_SUNXI_DRAM_DW_16BIT
+ writel(0x0, _ctl->dx[1].gcr);
+#else
+#error Unsupported DRAM bus width!
+#endif
+ }
+}
+
+static void mctl_set_training_cfg(struct dram_para *para)
+{
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ clrsetbits_le32(_ctl->dtcr, 0xf << 24,
+ (para->dual_rank ? 0x03 : 0x01) << 24);
+}
+
static void mctl_sys_init(uint16_t socid, struct dram_para *para)
{
struct sunxi_ccm_reg * const ccm =
@@ -550,22 +579,8 @@ static int mctl_channel_init(uint16_t socid, struct
dram_para *para)
(0x0 << 10) | (0x3 << 8));
}
- /* set half DQ */
- if (!para->bus_full_width) {
-#if defined CONFIG_SUNXI_DRAM_DW_32BIT
- writel(0x0, _ctl->dx[2].gcr);
- writel(0x0, _ctl->dx[3].gcr);
-#elif defined CONFIG_SUNXI_DRAM_DW_16BIT
- writel(0x0, _ctl->dx[1].gcr);
-#else
-#error Unsupported DRAM bus width!
-#endif
- }
-
- /* data training configuration */
- clrsetbits_le32(_ctl->dtcr, 0xf << 24,
- (para->dual_rank ? 0x3 : 0x1) << 24);
-
+ mctl_set_bus_width(para);
+ mctl_set_training_cfg(para);
mctl_set_bit_delays(para);
udelay(50);
@@ -600,25 +615,23 @@ static int mctl_channel_init(uint16_t socid, struct
dram_para *para)
|| ((readl(_ctl->dx[1].gsr[0]) >> 24) & 0x2)
#endif
) {
- clrsetbits_le32(_ctl->dtcr, 0xf << 24, 0x1 << 24);
para->dual_rank = 0;
+ mctl_set_training_cfg(para);
}
/* only half DQ width */
#if defined CONFIG_SUNXI_DRAM_DW_32BIT
if (((readl(_ctl->dx[2].gsr[0]) >> 24) & 0x1) ||
((readl(_ctl->dx[3].gsr[0]) >> 24) & 0x1)) {
- writel(0x0, _ctl->dx[2].gcr);
- writel(0x0, _ctl->dx[3].gcr);
para->bus_full_width = 0;
}
#elif defined CONFIG_SUNXI_DRAM_DW_16BIT
if ((readl(_ctl->dx[1].gsr[0]) >> 24) & 0x1) {
- writel(0x0, _ctl->dx[1].gcr);
para->bus_full_width = 0;
}
#endif
+ mctl_set_bus_width(para);
mctl_set