@@ -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
@@ -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, ¶))
return 0;
- if (para.dual_rank)
- writel(0x00000303, &mctl_ctl->odtmap);
- else
- writel(0x00000201, &mctl_ctl->odtmap);
+ mctl_set_odtmap(¶);
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);