[PATCH 2/2] sunxi: enable dual rank memory on R40

Icenowy Zheng icenowy at aosc.io
Thu Feb 25 17:13:25 CET 2021


Previously we do not have proper dual rank memory detection on R40
(because we omitted PIR_QSGATE, which does not work on R40 with our
configuration), and dual rank memory is just simply disabled as early
R40 boards available (Banana Pi M2 Ultra and Berry) have single rank
memory.

As a board with dual rank memory (Forlinx OKA40i-C) is now known to us,
we need to have a way to do memory rank detection to support that board.

Add some routine to detect memory rank by trying to access the memory
in rank 1 and check for error status of the memory controller, and then
enable dual rank memory on R40.

Similar routine can be used to detect half DQ width (which is also
detected by PIR_QSGATE on other SoCs), but it's left unimplemented
because there's no known R40 board with half DQ width now.

Signed-off-by: Icenowy Zheng <icenowy at aosc.io>
---
 arch/arm/mach-sunxi/dram_sunxi_dw.c | 55 +++++++++++++++++++++++++----
 1 file changed, 49 insertions(+), 6 deletions(-)

diff --git a/arch/arm/mach-sunxi/dram_sunxi_dw.c b/arch/arm/mach-sunxi/dram_sunxi_dw.c
index 2b9d631d49..b86ae7cdf3 100644
--- a/arch/arm/mach-sunxi/dram_sunxi_dw.c
+++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c
@@ -414,11 +414,9 @@ static void mctl_set_cr(uint16_t socid, struct dram_para *para)
 	}
 
 	if (socid == SOCID_R40) {
-		if (para->dual_rank)
-			panic("Dual rank memory not supported\n");
-
 		/* Mux pin to A15 address line for single rank memory. */
-		setbits_le32(&mctl_com->cr_r1, MCTL_CR_R1_MUX_A15);
+		if (!para->dual_rank)
+			setbits_le32(&mctl_com->cr_r1, MCTL_CR_R1_MUX_A15);
 	}
 }
 
@@ -702,8 +700,55 @@ static unsigned long mctl_calc_rank_size(struct rank_para *rank)
 	return (1UL << (rank->row_bits + rank->bank_bits)) * rank->page_size;
 }
 
+/*
+ * Because we cannot do mctl_phy_init(PIR_QSGATE) on R40 now (which leads
+ * to failure), it's needed to detect the rank count of R40 in another way.
+ *
+ * The code here is modelled after time_out_detect() in BSP, which tries to
+ * access the memory and check for error code.
+ *
+ * TODO: auto detect half DQ width here
+ */
+static void mctl_r40_detect_rank_count(struct dram_para *para)
+{
+	ulong rank1_base = (ulong) CONFIG_SYS_SDRAM_BASE +
+			   mctl_calc_rank_size(&para->ranks[0]);
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	/* Enable read time out */
+	setbits_le32(&mctl_ctl->pgcr[0], 0x1 << 25);
+
+	(void) readl((void *) rank1_base);
+	udelay(10);
+
+	if (readl(&mctl_ctl->pgsr[0]) & (0x1 << 13)) {
+		clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24, 0x1 << 24);
+		para->dual_rank = 0;
+	}
+
+	/* Reset PHY FIFO to clear it */
+	clrbits_le32(&mctl_ctl->pgcr[0], 0x1 << 26);
+	udelay(100);
+	setbits_le32(&mctl_ctl->pgcr[0], 0x1 << 26);
+
+	/* Clear error status */
+	setbits_le32(&mctl_ctl->pgcr[0], 0x1 << 24);
+
+	/* Clear time out flag */
+	clrbits_le32(&mctl_ctl->pgsr[0], 0x1 << 13);
+
+	/* Disable read time out */
+	clrbits_le32(&mctl_ctl->pgcr[0], 0x1 << 25);
+}
+
 static void mctl_auto_detect_dram_size(uint16_t socid, struct dram_para *para)
 {
+	if (socid == SOCID_R40) {
+		mctl_r40_detect_rank_count(para);
+		mctl_set_cr(socid, para);
+	}
+
 	mctl_auto_detect_dram_size_rank(socid, para, (ulong)CONFIG_SYS_SDRAM_BASE, &para->ranks[0]);
 
 	if ((socid == SOCID_A64 || socid == SOCID_R40) && para->dual_rank) {
@@ -854,8 +899,6 @@ unsigned long sunxi_dram_init(void)
 	uint16_t socid = SOCID_H3;
 #elif defined(CONFIG_MACH_SUN8I_R40)
 	uint16_t socid = SOCID_R40;
-	/* Currently we cannot support R40 with dual rank memory */
-	para.dual_rank = 0;
 #elif defined(CONFIG_MACH_SUN8I_V3S)
 	uint16_t socid = SOCID_V3S;
 #elif defined(CONFIG_MACH_SUN50I)
-- 
2.30.0


More information about the U-Boot mailing list