[PATCH 1/1] sunxi: support half DQ width memory on R40

2022-07-31 Thread Evgeny Boger
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

[PATCH 0/1] sunxi: support half DQ width memory on R40

2022-07-31 Thread Evgeny Boger
On Allwinner boards memory configuration is detected by bootloader.
However, R40 (A40i/T3) is somewhat unique because the detection by
the DRAM controller via PIR_QSGATE is broken (either in U-Boot or
in the controller). So the memory configuration must be detected
by other means.

Dual rank detection has been already implemented by Icenowy earlier.
This patch extends the automation detection so it can detect half-width
DQ configuration (i.e. single x16 DDR3 IC).

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

Unfortunately it has NOT been tested with dual-rank configuration yet,
as I don't have any dual-rank boards at hand.
I will really appreciate if someone could help me with that.

Evgeny Boger (1):
  sunxi: support half DQ width memory on R40

 .../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(-)

-- 
2.25.1