sunxi: H616: add LPDDR4 DRAM support

The H616 SoC family has support for several types of DRAM: DDR3,
LPDDR3, DDR4 and LPDDR4.
At the moment, the driver only supports DDR3 and LPDDR3 memory.
Let's extend the driver to support the LPDDR4 memory. This type
of memory widely used in device with T507(-H) SoC and new orangepi
zero3 with H618.
The compatibility with T507 is not yet complete, because there
is difference in the phy_init array.
The LPDDR4-2133 timings correspond to DRAM Rayson RS1G32LO4D2BDS-53BT
found on the NOR SPI from the Orangepi Zero 3 4GB.

Signed-off-by: Mikhail Kalashnikov <iuncuim@gmail.com>
Tested-by: Piotr Oniszczuk <piotr.oniszczuk@gmail.com>
Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Acked-by: Andre Przywara <andre.przywara@arm.com>
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c
index ba5659d..c5c1331 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h616.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c
@@ -6,8 +6,8 @@
  * unknown. That's why this driver has plenty of magic numbers. Some
  * meaning was nevertheless deduced from strings found in boot0 and
  * known meaning of some dram parameters.
- * This driver only supports DDR3 memory and omits logic for all
- * other supported types supported by hardware.
+ * This driver supports DDR3, LPDDR3 and LPDDR4 memory. There is no
+ * DDR4 support yet.
  *
  * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
  *
@@ -238,6 +238,11 @@
 	0x08, 0x01, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
 	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x07,
 	0x17, 0x19, 0x1a
+#elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR4)
+	0x02, 0x00, 0x17, 0x05, 0x04, 0x19, 0x06, 0x07,
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x01,
+	0x18, 0x03, 0x1a
 #endif
 };
 
@@ -246,8 +251,20 @@
 {
 	uint32_t val_lo, val_hi;
 
+	/*
+	 * This part should be applicable to all memory types, but is
+	 * usually found in LPDDR4 bootloaders. Therefore, we will leave
+	 * only for this type of memory.
+	 */
+	if (para->type == SUNXI_DRAM_TYPE_LPDDR4) {
+		clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x390, BIT(5), BIT(4));
+		clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x3d0, BIT(5), BIT(4));
+		clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x410, BIT(5), BIT(4));
+		clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x450, BIT(5), BIT(4));
+	}
+
 	val_lo = para->dx_dri;
-	val_hi = para->dx_dri;
+	val_hi = (para->type == SUNXI_DRAM_TYPE_LPDDR4) ? 0x04040404 : para->dx_dri;
 	writel_relaxed(MASK_BYTE(val_lo, 0), SUNXI_DRAM_PHY0_BASE + 0x388);
 	writel_relaxed(MASK_BYTE(val_hi, 0), SUNXI_DRAM_PHY0_BASE + 0x38c);
 	writel_relaxed(MASK_BYTE(val_lo, 1), SUNXI_DRAM_PHY0_BASE + 0x3c8);
@@ -265,7 +282,7 @@
 	writel_relaxed(MASK_BYTE(val_hi, 1), SUNXI_DRAM_PHY0_BASE + 0x34c);
 
 	val_lo = (para->type == SUNXI_DRAM_TYPE_LPDDR3) ? 0 : para->dx_odt;
-	val_hi = para->dx_odt;
+	val_hi = (para->type == SUNXI_DRAM_TYPE_LPDDR4) ? 0 : para->dx_odt;
 	writel_relaxed(MASK_BYTE(val_lo, 0), SUNXI_DRAM_PHY0_BASE + 0x380);
 	writel_relaxed(MASK_BYTE(val_hi, 0), SUNXI_DRAM_PHY0_BASE + 0x384);
 	writel_relaxed(MASK_BYTE(val_lo, 1), SUNXI_DRAM_PHY0_BASE + 0x3c0);
@@ -392,12 +409,18 @@
 	return result;
 }
 
-static bool mctl_phy_read_training(const struct dram_config *config)
+static bool mctl_phy_read_training(const struct dram_para *para,
+			   const struct dram_config *config)
 {
 	u32 val1, val2, *ptr1, *ptr2;
 	bool result = true;
 	int i;
 
+	if (para->type == SUNXI_DRAM_TYPE_LPDDR4) {
+		writel(0, SUNXI_DRAM_PHY0_BASE + 0x800);
+		writel(0, SUNXI_DRAM_PHY0_BASE + 0x81c);
+	}
+
 	clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 3, 2);
 	clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x804, 0x3f, 0xf);
 	clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x808, 0x3f, 0xf);
@@ -578,6 +601,8 @@
 		clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 1);
 		setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 8);
 		clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x10);
+		if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
+			clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x4, 0x80);
 
 		if (para->tpr10 & BIT(30))
 			val = para->tpr11 & 0x3f;
@@ -791,8 +816,9 @@
 	writel(val, SUNXI_DRAM_PHY0_BASE + 0x7e0);
 	writel(val, SUNXI_DRAM_PHY0_BASE + 0x7f4);
 
-	if (para->type == SUNXI_DRAM_TYPE_DDR3) {
-		val = (para->tpr10 >> 7) & 0x1e;
+	val = (para->tpr10 >> 7) & 0x1e;
+	switch (para->type) {
+	case SUNXI_DRAM_TYPE_DDR3:
 		if (para->tpr2 & 1) {
 			writel(val, SUNXI_DRAM_PHY0_BASE + 0x794);
 			if (config->ranks == 2) {
@@ -818,8 +844,8 @@
 				writel(val, SUNXI_DRAM_PHY0_BASE + 0x7b8);
 			}
 		}
-	} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
-		val = (para->tpr10 >> 7) & 0x1e;
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR3:
 		if (para->tpr2 & 1) {
 			writel(val, SUNXI_DRAM_PHY0_BASE + 0x7a0);
 			if (config->ranks == 2) {
@@ -833,7 +859,18 @@
 				writel(val, SUNXI_DRAM_PHY0_BASE + 0x7f8);
 			}
 		}
-	}
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR4:
+		if (para->tpr2 & 1) {
+			writel(val, SUNXI_DRAM_PHY0_BASE + 0x788);
+		} else {
+			writel(val, SUNXI_DRAM_PHY0_BASE + 0x794);
+		};
+		break;
+	case SUNXI_DRAM_TYPE_DDR4:
+	default:
+		panic("This DRAM setup is currently not supported.\n");
+	};
 }
 
 static bool mctl_phy_init(const struct dram_para *para,
@@ -846,30 +883,42 @@
 	u32 val, val2, *ptr, mr0, mr2;
 	int i;
 
+	if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
+		clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x4,0x80);
+
 	if (config->bus_full_width)
 		val = 0xf;
 	else
 		val = 3;
 	clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x3c, 0xf, val);
 
-	if (para->tpr2 & 0x100) {
-		if (para->type == SUNXI_DRAM_TYPE_DDR3) {
+	switch (para->type) {
+	case SUNXI_DRAM_TYPE_DDR3:
+		if (para->tpr2 & 0x100) {
 			val = 9;
 			val2 = 7;
-		} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
-			// untested setup: use some values for now
-			val = 14;
-			val2 = 8;
-		}
-	} else {
-		if (para->type == SUNXI_DRAM_TYPE_DDR3) {
+		} else {
 			val = 13;
 			val2 = 9;
-		} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
+		}
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR3:
+		if (para->tpr2 & 0x100) {
+			val = 12;
+			val2 = 6;
+		} else {
 			val = 14;
 			val2 = 8;
 		}
-	}
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR4:
+		val = 20;
+		val2 = 10;
+		break;
+	case SUNXI_DRAM_TYPE_DDR4:
+	default:
+		panic("This DRAM setup is currently not supported.\n");
+	};
 
 	writel(val, SUNXI_DRAM_PHY0_BASE + 0x14);
 	writel(val, SUNXI_DRAM_PHY0_BASE + 0x35c);
@@ -893,19 +942,40 @@
 	if (para->tpr10 & TPR10_CA_BIT_DELAY)
 		mctl_phy_ca_bit_delay_compensation(para, config);
 
+	switch (para->type) {
+	case SUNXI_DRAM_TYPE_DDR3:
+		val = para->tpr6 & 0xff;
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR3:
+		val = para->tpr6 >> 8 & 0xff;
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR4:
+		val = para->tpr6 >> 24 & 0xff;
+		break;
+	case SUNXI_DRAM_TYPE_DDR4:
+	default:
+		panic("This DRAM setup is currently not supported.\n");
+	};
+
-	if (para->type == SUNXI_DRAM_TYPE_DDR3)
-		val = 0x80;
-	else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
-		val = 0xc0;
 	writel(val, SUNXI_DRAM_PHY0_BASE + 0x3dc);
 	writel(val, SUNXI_DRAM_PHY0_BASE + 0x45c);
 
 	mctl_phy_configure_odt(para);
 
-	if (para->type == SUNXI_DRAM_TYPE_DDR3)
+	switch (para->type) {
+	case SUNXI_DRAM_TYPE_DDR3:
 		val = 0x0a;
-	else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR3:
 		val = 0x0b;
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR4:
+		val = 0x0d;
+		break;
+	case SUNXI_DRAM_TYPE_DDR4:
+	default:
+		panic("This DRAM setup is currently not supported.\n");
+	};
 	clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0x7, val);
 
 	if (para->clk <= 672)
@@ -955,8 +1025,8 @@
 		mr0 = 0x1f14;
 		mr2 = 0x20;
 	}
-
-	if (para->type == SUNXI_DRAM_TYPE_DDR3) {
+	switch (para->type) {
+	case SUNXI_DRAM_TYPE_DDR3:
 		writel(mr0, &mctl_ctl->mrctrl1);
 		writel(0x80000030, &mctl_ctl->mrctrl0);
 		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
@@ -972,7 +1042,8 @@
 		writel(0, &mctl_ctl->mrctrl1);
 		writel(0x80003030, &mctl_ctl->mrctrl0);
 		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
-	} else if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR3:
 		writel(mr0, &mctl_ctl->mrctrl1);
 		writel(0x800000f0, &mctl_ctl->mrctrl0);
 		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
@@ -988,7 +1059,48 @@
 		writel(0x301, &mctl_ctl->mrctrl1);
 		writel(0x800000f0, &mctl_ctl->mrctrl0);
 		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
-	}
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR4:
+		writel(0x0, &mctl_ctl->mrctrl1);
+		writel(0x80000030, &mctl_ctl->mrctrl0);
+		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+		writel(0x134, &mctl_ctl->mrctrl1);
+		writel(0x80000030, &mctl_ctl->mrctrl0);
+		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+		writel(0x21b, &mctl_ctl->mrctrl1);
+		writel(0x80000030, &mctl_ctl->mrctrl0);
+		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+		writel(0x333, &mctl_ctl->mrctrl1);
+		writel(0x80000030, &mctl_ctl->mrctrl0);
+		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+		writel(0x403, &mctl_ctl->mrctrl1);
+		writel(0x80000030, &mctl_ctl->mrctrl0);
+		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+		writel(0xb04, &mctl_ctl->mrctrl1);
+		writel(0x80000030, &mctl_ctl->mrctrl0);
+		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+		writel(0xc72, &mctl_ctl->mrctrl1);
+		writel(0x80000030, &mctl_ctl->mrctrl0);
+		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+		writel(0xe09, &mctl_ctl->mrctrl1);
+		writel(0x80000030, &mctl_ctl->mrctrl0);
+		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+		writel(0x1624, &mctl_ctl->mrctrl1);
+		writel(0x80000030, &mctl_ctl->mrctrl0);
+		mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+		break;
+	case SUNXI_DRAM_TYPE_DDR4:
+	default:
+		panic("This DRAM setup is currently not supported.\n");
+	};
 
 	writel(0, SUNXI_DRAM_PHY0_BASE + 0x54);
 
@@ -1018,7 +1130,7 @@
 
 	if (para->tpr10 & TPR10_READ_TRAINING) {
 		for (i = 0; i < 5; i++)
-			if (mctl_phy_read_training(config))
+			if (mctl_phy_read_training(para, config))
 				break;
 		if (i == 5) {
 			debug("read training failed!\n");
@@ -1057,17 +1169,29 @@
 
 	setbits_le32(&mctl_com->unk_0x008, 0xff00);
 
+	if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
+		writel(1, SUNXI_DRAM_COM_BASE + 0x50);
 	clrsetbits_le32(&mctl_ctl->sched[0], 0xff00, 0x3000);
 
 	writel(0, &mctl_ctl->hwlpctl);
 
 	setbits_le32(&mctl_com->unk_0x008, 0xff00);
 
-	reg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(config->ranks);
-	if (para->type == SUNXI_DRAM_TYPE_DDR3)
-		reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE;
-	else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
-		reg_val |= MSTR_DEVICETYPE_LPDDR3;
+	reg_val = MSTR_ACTIVE_RANKS(config->ranks);
+	switch (para->type) {
+	case SUNXI_DRAM_TYPE_DDR3:
+		reg_val |= MSTR_BURST_LENGTH(8) | MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE;
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR3:
+		reg_val |= MSTR_BURST_LENGTH(8) | MSTR_DEVICETYPE_LPDDR3;
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR4:
+		reg_val |= MSTR_BURST_LENGTH(16) | MSTR_DEVICETYPE_LPDDR4;
+		break;
+	case SUNXI_DRAM_TYPE_DDR4:
+	default:
+		panic("This DRAM setup is currently not supported.\n");
+	};
 	if (config->bus_full_width)
 		reg_val |= MSTR_BUSWIDTH_FULL;
 	else
@@ -1079,10 +1203,20 @@
 	else
 		writel(0x0201, &mctl_ctl->odtmap);
 
-	if (para->type == SUNXI_DRAM_TYPE_DDR3)
+	switch (para->type) {
+	case SUNXI_DRAM_TYPE_DDR3:
 		reg_val = 0x06000400;
-	else if (para->type == SUNXI_DRAM_TYPE_LPDDR3)
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR3:
 		reg_val = 0x09020400;
+		break;
+	case SUNXI_DRAM_TYPE_LPDDR4:
+		reg_val = 0x04000400;
+		break;
+	case SUNXI_DRAM_TYPE_DDR4:
+	default:
+		panic("This DRAM setup is currently not supported.\n");
+	};
 	writel(reg_val, &mctl_ctl->odtcfg);
 	writel(reg_val, &mctl_ctl->unk_0x2240);
 	writel(reg_val, &mctl_ctl->unk_0x3240);
@@ -1102,6 +1236,9 @@
 	setbits_le32(&mctl_ctl->unk_0x3180, BIT(31) | BIT(30));
 	setbits_le32(&mctl_ctl->unk_0x4180, BIT(31) | BIT(30));
 
+	if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
+		setbits_le32(&mctl_ctl->dbictl, 0x1);
+
 	setbits_le32(&mctl_ctl->rfshctl3, BIT(0));
 	clrbits_le32(&mctl_ctl->dfimisc, BIT(0));
 
@@ -1224,6 +1361,8 @@
 	.type = SUNXI_DRAM_TYPE_DDR3,
 #elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR3)
 	.type = SUNXI_DRAM_TYPE_LPDDR3,
+#elif defined(CONFIG_SUNXI_DRAM_H616_LPDDR4)
+	.type = SUNXI_DRAM_TYPE_LPDDR4,
 #endif
 	.dx_odt = CONFIG_DRAM_SUN50I_H616_DX_ODT,
 	.dx_dri = CONFIG_DRAM_SUN50I_H616_DX_DRI,
@@ -1231,6 +1370,7 @@
 	.odt_en = CONFIG_DRAM_SUN50I_H616_ODT_EN,
 	.tpr0 = CONFIG_DRAM_SUN50I_H616_TPR0,
 	.tpr2 = CONFIG_DRAM_SUN50I_H616_TPR2,
+	.tpr6 = CONFIG_DRAM_SUN50I_H616_TPR6,
 	.tpr10 = CONFIG_DRAM_SUN50I_H616_TPR10,
 	.tpr11 = CONFIG_DRAM_SUN50I_H616_TPR11,
 	.tpr12 = CONFIG_DRAM_SUN50I_H616_TPR12,