All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pablo Mazzini <pmazzini@gmail.com>
To: Andre Przywara <andre.przywara@arm.com>, u-boot@lists.u-boot-project.org
Cc: Tom Rini <trini@konsulko.com>,
	linux-sunxi@lists.linux.dev,
	Ilias Apalodimas <ilias.apalodimas@linaro.org>
Subject: [PATCH 2/7] sunxi: dram: support the Allwinner B288
Date: Fri, 16 Oct 2026 10:16:03 +0100	[thread overview]
Message-ID: <20261016091608.121842-3-pmazzini@gmail.com> (raw)
In-Reply-To: <20261016091608.121842-1-pmazzini@gmail.com>

The B288 has the DW controller and PHY of the H3, set up as on the H5.
Gate training finds its ranks and bus width, as in the vendor code.

Signed-off-by: Pablo Mazzini <pmazzini@gmail.com>
---
 arch/arm/mach-sunxi/Kconfig         |  1 +
 arch/arm/mach-sunxi/dram_sunxi_dw.c | 97 +++++++++++++++++++++++++----
 2 files changed, 86 insertions(+), 12 deletions(-)

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index ed2402f71f2..0326c7a976b 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -788,6 +788,7 @@ config DRAM_ODT_EN
 	depends on !MACH_SUN50I_H616
 	depends on !MACH_SUN55I_A523
 	default y if MACH_SUN8I_A23
+	default y if MACH_SUN8I_B288
 	default y if MACH_SUNXI_H3_H5
 	default y if MACH_SUN8I_R40
 	default y if MACH_SUN50I
diff --git a/arch/arm/mach-sunxi/dram_sunxi_dw.c b/arch/arm/mach-sunxi/dram_sunxi_dw.c
index 4ed295909ce..ee96186abc6 100644
--- a/arch/arm/mach-sunxi/dram_sunxi_dw.c
+++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c
@@ -21,6 +21,10 @@ static void mctl_phy_init(u32 val)
 	struct sunxi_mctl_ctl_reg * const mctl_ctl =
 			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
 
+	/* the B288 latches the configuration before triggering */
+	if (IS_ENABLED(CONFIG_MACH_SUN8I_B288))
+		writel(val, &mctl_ctl->pir);
+
 	writel(val | PIR_INIT, &mctl_ctl->pir);
 	mctl_await_completion(&mctl_ctl->pgsr[0], PGSR_INIT_DONE, 0x1);
 }
@@ -48,6 +52,11 @@ static void mctl_set_bit_delays(struct dram_para *para)
 	for (i = 0; i < 4; i++)
 		writel(0x6 << 24, &mctl_ctl->dx[i].sdlr);
 #endif
+	if (IS_ENABLED(CONFIG_MACH_SUN8I_B288)) {
+		/* boot0 uses 8 on DX0/DX1 and 6 on DX2/DX3 */
+		for (i = 0; i < 4; i++)
+			writel((i < 2 ? 0x8 : 0x6) << 24, &mctl_ctl->dx[i].sdlr);
+	}
 
 	setbits_le32(&mctl_ctl->pgcr[0], 1 << 26);
 }
@@ -248,9 +257,24 @@ static void mctl_set_master_priority_r40(void)
 	MBUS_CONF(UNKNOWN3, true,    HIGH, 0, 1280,  144,   64);
 }
 
+/*
+ * The B288 boot0 only sets the MBUS window timer: it leaves the per-port
+ * table, bwcr and mapr alone, so no master is bandwidth limited.
+ */
+static void mctl_set_master_priority_b288(void)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+	clrsetbits_le32(&mctl_com->tmr, 0xfff, 399);
+}
+
 static void mctl_set_master_priority(uint16_t socid)
 {
 	switch (socid) {
+	case SOCID_B288:
+		mctl_set_master_priority_b288();
+		return;
 	case SOCID_H3:
 		mctl_set_master_priority_h3();
 		return;
@@ -430,14 +454,15 @@ static void mctl_sys_init(uint16_t socid, struct dram_para *para)
 	clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL);
 	clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL);
 	clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN);
-	if (socid == SOCID_A64 || socid == SOCID_R40)
+	if (socid == SOCID_A64 || socid == SOCID_R40 || socid == SOCID_B288)
 		clrbits_le32(&ccm->pll11_cfg, CCM_PLL11_CTRL_EN);
 	udelay(10);
 
 	clrbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
 	udelay(1000);
 
-	if (socid == SOCID_A64 || socid == SOCID_R40) {
+	if (socid == SOCID_A64 || socid == SOCID_R40 || socid == SOCID_B288) {
+		/* The B288 feeds the controller from PLL_DDR1 (pll11), too */
 		clock_set_pll11(CONFIG_DRAM_CLK * 2 * 1000000, false);
 		clrsetbits_le32(&ccm->dram_clk_cfg,
 				CCM_DRAMCLK_CFG_DIV_MASK |
@@ -464,7 +489,8 @@ static void mctl_sys_init(uint16_t socid, struct dram_para *para)
 	setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST);
 	udelay(10);
 
-	writel(socid == SOCID_H5 ? 0x8000 : 0xc00e, &mctl_ctl->clken);
+	writel((socid == SOCID_H5 || socid == SOCID_B288) ? 0x8000 : 0xc00e,
+	       &mctl_ctl->clken);
 	udelay(500);
 }
 
@@ -473,6 +499,14 @@ static void mctl_sys_init(uint16_t socid, struct dram_para *para)
 #define DX_GCR_ODT_ALWAYS_ON	(0x1 << 4)
 #define DX_GCR_ODT_OFF		(0x2 << 4)
 
+static void mctl_set_odtmap(struct dram_para *para)
+{
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	writel(para->dual_rank ? 0x00000303 : 0x00000201, &mctl_ctl->odtmap);
+}
+
 static int mctl_channel_init(uint16_t socid, struct dram_para *para)
 {
 	struct sunxi_mctl_com_reg * const mctl_com =
@@ -488,7 +522,7 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para)
 
 	/* setting VTC, default disable all VT */
 	clrbits_le32(&mctl_ctl->pgcr[0], (1 << 30) | 0x3f);
-	if (socid == SOCID_H5)
+	if (socid == SOCID_H5 || socid == SOCID_B288)
 		setbits_le32(&mctl_ctl->pgcr[1], (1 << 24) | (1 << 26));
 	else
 		clrsetbits_le32(&mctl_ctl->pgcr[1], 1 << 24, 1 << 26);
@@ -515,8 +549,9 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para)
 	}
 
 	/* AC PDR should always ON */
-	clrsetbits_le32(&mctl_ctl->aciocr, socid == SOCID_H5 ? (0x1 << 11) : 0,
-			0x1 << 1);
+	clrsetbits_le32(&mctl_ctl->aciocr,
+			(socid == SOCID_H5 || socid == SOCID_B288) ?
+			(0x1 << 11) : 0, 0x1 << 1);
 
 	/* set DQS auto gating PD mode */
 	setbits_le32(&mctl_ctl->pgcr[2], 0x3 << 6);
@@ -535,7 +570,8 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para)
 		/* dphy & aphy phase select 270 degree */
 		clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
 				(0x1 << 10) | (0x1 << 8));
-	} else if (socid == SOCID_A64 || socid == SOCID_H5) {
+	} else if (socid == SOCID_A64 || socid == SOCID_H5 ||
+		   socid == SOCID_B288) {
 		/* dphy & aphy phase select ? */
 		clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
 				(0x0 << 10) | (0x3 << 8));
@@ -572,6 +608,19 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para)
 
 		mctl_phy_init(PIR_PLLINIT | PIR_DCAL | PIR_PHYRST |
 			      PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE);
+	} else if (socid == SOCID_B288) {
+		/* A 24-bit zqcr value, as on the A64; the H3 quirk hangs */
+		clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, CONFIG_DRAM_ZQ);
+		mctl_set_odtmap(para);
+
+		/* DQS gating mode 1: gate training finds the ranks and lanes */
+		clrbits_le32(&mctl_ctl->pgcr[2], 0x3 << 6);
+		clrbits_le32(&mctl_ctl->dqsgmr, 0x107);
+		/* the vendor's training configuration */
+		clrsetbits_le32(&mctl_ctl->dtcr, 0xffffff, 0x3087);
+		mctl_phy_init(PIR_ZCAL | PIR_PLLINIT | PIR_PHYRST);
+		mctl_phy_init(PIR_DCAL | PIR_DRAMRST | PIR_DRAMINIT |
+			      PIR_QSGATE);
 	} else if (socid == SOCID_H3) {
 		mctl_h3_zq_calibration_quirk(para);
 
@@ -636,7 +685,7 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para)
 	udelay(10);
 
 	/* set PGCR3, CKE polarity */
-	if (socid == SOCID_H3 || socid == SOCID_V3S)
+	if (socid == SOCID_H3 || socid == SOCID_V3S || socid == SOCID_B288)
 		writel(0x00aa0060, &mctl_ctl->pgcr[3]);
 	else if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40)
 		writel(0xc0aa0060, &mctl_ctl->pgcr[3]);
@@ -822,6 +871,23 @@ static void mctl_auto_detect_dram_size(uint16_t socid, struct dram_para *para)
 	   3,  3,  3,  3,  3,  3,  3,  3,			\
 	   3,  3,  3,  3,  2,  0,  0      }
 
+/* As programmed by the B288 boot0 */
+#define SUN8I_B288_DX_READ_DELAYS				\
+	{{ 16, 17, 18, 19, 18, 19, 19, 19, 16,  0,  0 },	\
+	 { 26, 26, 26, 25, 25, 25, 25, 25, 20,  0,  0 },	\
+	 { 20, 22, 24, 22, 26, 25, 25, 25, 20,  0,  0 },	\
+	 { 21, 21, 21, 20, 20, 19, 19, 19, 18,  0,  0 }}
+#define SUN8I_B288_DX_WRITE_DELAYS				\
+	{{  0,  1,  3,  3,  3,  3,  3,  4,  3, 11,  0 },	\
+	 {  3,  3,  4,  4,  3,  3,  3,  3,  3, 11,  0 },	\
+	 {  0,  3,  3,  1,  6,  6,  5,  6,  3,  9,  0 },	\
+	 {  5,  5,  6,  6,  4,  5,  3,  3,  3,  9,  0 }}
+#define SUN8I_B288_AC_DELAYS					\
+	{  0,  0,  8,  3,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  0,  0,  0,  0,			\
+	   0,  0,  0,  0,  3,  0,  0      }
+
 unsigned long sunxi_dram_init(void)
 {
 	struct sunxi_mctl_com_reg * const mctl_com =
@@ -851,6 +917,10 @@ unsigned long sunxi_dram_init(void)
 		.dx_read_delays  = SUN8I_H3_DX_READ_DELAYS,
 		.dx_write_delays = SUN8I_H3_DX_WRITE_DELAYS,
 		.ac_delays	 = SUN8I_H3_AC_DELAYS,
+#elif defined(CONFIG_MACH_SUN8I_B288)
+		.dx_read_delays  = SUN8I_B288_DX_READ_DELAYS,
+		.dx_write_delays = SUN8I_B288_DX_WRITE_DELAYS,
+		.ac_delays	 = SUN8I_B288_AC_DELAYS,
 #elif defined(CONFIG_MACH_SUN8I_V3S)
 		.dx_read_delays  = SUN8I_V3S_DX_READ_DELAYS,
 		.dx_write_delays = SUN8I_V3S_DX_WRITE_DELAYS,
@@ -878,6 +948,8 @@ unsigned long sunxi_dram_init(void)
 	uint16_t socid = SOCID_H3;
 #elif defined(CONFIG_MACH_SUN8I_R40)
 	uint16_t socid = SOCID_R40;
+#elif defined(CONFIG_MACH_SUN8I_B288)
+	uint16_t socid = SOCID_B288;
 #elif defined(CONFIG_MACH_SUN8I_V3S)
 	uint16_t socid = SOCID_V3S;
 #elif defined(CONFIG_MACH_SUN50I)
@@ -890,12 +962,13 @@ unsigned long sunxi_dram_init(void)
 	if (mctl_channel_init(socid, &para))
 		return 0;
 
-	if (para.dual_rank)
-		writel(0x00000303, &mctl_ctl->odtmap);
-	else
-		writel(0x00000201, &mctl_ctl->odtmap);
+	mctl_set_odtmap(&para);
 	udelay(1);
 
+	/* the B288 trains with static DX clocks; dynamic from now on */
+	if (socid == SOCID_B288)
+		clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12));
+
 	/* odt delay */
 	if (socid == SOCID_H3)
 		writel(0x0c000400, &mctl_ctl->odtcfg);
-- 
2.52.0


  parent reply	other threads:[~2026-10-16  9:17 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-16  9:16 [PATCH 0/7] sunxi: add the Allwinner B288 and the PocketBook Verse Pablo Mazzini
2026-10-16  9:16 ` [PATCH 1/7] sunxi: add support for the Allwinner B288 Pablo Mazzini
2026-10-16  9:16 ` Pablo Mazzini [this message]
2026-10-16  9:16 ` [PATCH 3/7] dt-bindings: clock: add the Allwinner B288 CCU Pablo Mazzini
2026-10-16  9:16 ` [PATCH 4/7] clk: sunxi: add the Allwinner B288 gates and resets Pablo Mazzini
2026-10-16  9:16 ` [PATCH 5/7] pinctrl: sunxi: add the Allwinner B288 PIO Pablo Mazzini
2026-10-16  9:16 ` [PATCH 6/7] mmc: sunxi: B288 mmc0/mmc1 run off PLL_PERIPH0 2x Pablo Mazzini
2026-10-16  9:16 ` [PATCH 7/7] sunxi: add the PocketBook Verse Pablo Mazzini
2026-10-16 17:28 ` [PATCH 0/7] sunxi: add the Allwinner B288 and " Andre Przywara

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261016091608.121842-3-pmazzini@gmail.com \
    --to=pmazzini@gmail.com \
    --cc=andre.przywara@arm.com \
    --cc=ilias.apalodimas@linaro.org \
    --cc=linux-sunxi@lists.linux.dev \
    --cc=trini@konsulko.com \
    --cc=u-boot@lists.u-boot-project.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.