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/Kconfig b/arch/arm/mach-sunxi/Kconfig
index a10e4c0..a4a8d8e 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -85,6 +85,12 @@
 	help
 	  TPR2 value from vendor DRAM settings.
 
+config DRAM_SUN50I_H616_TPR6
+	hex "H616 DRAM TPR6 parameter"
+	default 0x3300c080
+	help
+	  TPR6 value from vendor DRAM settings.
+
 config DRAM_SUN50I_H616_TPR10
 	hex "H616 DRAM TPR10 parameter"
 	help
@@ -462,6 +468,9 @@
 config SUNXI_DRAM_LPDDR3
 	bool
 
+config SUNXI_DRAM_LPDDR4
+	bool
+
 choice
 	prompt "DRAM Type and Timing"
 	default SUNXI_DRAM_DDR3_1333 if !MACH_SUN8I_V3S
@@ -505,6 +514,14 @@
 	  This option is the LPDDR3 timing used by the stock boot0 by
 	  Allwinner.
 
+config SUNXI_DRAM_H616_LPDDR4
+	bool "LPDDR4 DRAM chips on the H616 DRAM controller"
+	select SUNXI_DRAM_LPDDR4
+	depends on DRAM_SUN50I_H616
+	help
+	  This option is the LPDDR4 timing used by the stock boot0 by
+	  Allwinner.
+
 config SUNXI_DRAM_H616_DDR3_1333
 	bool "DDR3-1333 boot0 timings on the H616 DRAM controller"
 	select SUNXI_DRAM_DDR3
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,
diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile
index 8bfd994..5f20341 100644
--- a/arch/arm/mach-sunxi/dram_timings/Makefile
+++ b/arch/arm/mach-sunxi/dram_timings/Makefile
@@ -5,3 +5,4 @@
 obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333)	+= h6_ddr3_1333.o
 obj-$(CONFIG_SUNXI_DRAM_H616_DDR3_1333)	+= h616_ddr3_1333.o
 obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3)	+= h616_lpddr3.o
+obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4)	+= h616_lpddr4_2133.o
diff --git a/arch/arm/mach-sunxi/dram_timings/h616_lpddr4_2133.c b/arch/arm/mach-sunxi/dram_timings/h616_lpddr4_2133.c
new file mode 100644
index 0000000..c11cb86
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_timings/h616_lpddr4_2133.c
@@ -0,0 +1,95 @@
+/*
+ * sun50i H616 LPDDR4-2133 timings, as programmed by Allwinner's boot0
+ * for orangepi zero3 with the H618 and LPDDR4 memory.
+ *
+ * (C) Copyright 2023 Mikhail Kalashnikov <iuncuim@gmail.com>
+ *   Based on H6 DDR3 timings:
+ *   (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/dram.h>
+#include <asm/arch/cpu.h>
+
+void mctl_set_timing_params(const struct dram_para *para)
+{
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+	u8 tccd		= 4;
+	u8 tfaw		= ns_to_t(40);
+	u8 trrd		= max(ns_to_t(10), 2);
+	u8 trcd		= max(ns_to_t(18), 2);
+	u8 trc		= ns_to_t(65);
+	u8 txp		= max(ns_to_t(8), 2);
+	u8 trtp		= max(ns_to_t(8), 4);
+	u8 trp		= ns_to_t(21);
+	u8 tras		= ns_to_t(42);
+	u16 trefi	= ns_to_t(3904) / 32;
+	u16 trfc	= ns_to_t(280);
+	u16 txsr	= ns_to_t(190);
+
+	u8 tmrw		= max(ns_to_t(14), 5);
+	u8 tmrd		= tmrw;
+	u8 tmod		= 12;
+	u8 tcke		= max(ns_to_t(15), 2);
+	u8 tcksrx	= max(ns_to_t(2), 2);
+	u8 tcksre	= max(ns_to_t(5), 2);
+	u8 tckesr	= tcke;
+	u8 trasmax	= (trefi * 9) / 32;
+	u8 txs		= 4;
+	u8 txsdll	= 16;
+	u8 txsabort	= 4;
+	u8 txsfast	= 4;
+	u8 tcl		= 10;
+	u8 tcwl		= 5;
+	u8 t_rdata_en	= 17;
+	u8 tphy_wrlat	= 5;
+
+	u8 twtp		= 24;
+	u8 twr2rd	= max(trrd, (u8)4) + 14;
+	u8 trd2wr	= (ns_to_t(4) + 17) - ns_to_t(1);
+
+	/* set DRAM timing */
+	writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,
+	       &mctl_ctl->dramtmg[0]);
+	writel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]);
+	writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd,
+	       &mctl_ctl->dramtmg[2]);
+	writel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]);
+	writel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp,
+	       &mctl_ctl->dramtmg[4]);
+	writel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke,
+	       &mctl_ctl->dramtmg[5]);
+	/* Value suggested by ZynqMP manual and used by libdram */
+	writel((txp + 2) | 0x02020000, &mctl_ctl->dramtmg[6]);
+	writel((txsfast << 24) | (txsabort << 16) | (txsdll << 8) | txs,
+	       &mctl_ctl->dramtmg[8]);
+	writel(0x00020208, &mctl_ctl->dramtmg[9]);
+	writel(0xE0C05, &mctl_ctl->dramtmg[10]);
+	writel(0x440C021C, &mctl_ctl->dramtmg[11]);
+	writel(8, &mctl_ctl->dramtmg[12]);
+	writel(0xA100002, &mctl_ctl->dramtmg[13]);
+	writel(txsr, &mctl_ctl->dramtmg[14]);
+
+	clrsetbits_le32(&mctl_ctl->init[0], 0xC0000FFF, 0x3f0);
+	writel(0x01f20000, &mctl_ctl->init[1]);
+	writel(0x00000d05, &mctl_ctl->init[2]);
+	writel(0, &mctl_ctl->dfimisc);
+	writel(0x0034001b, &mctl_ctl->init[3]);
+	writel(0x00330000, &mctl_ctl->init[4]);
+	writel(0x00040072, &mctl_ctl->init[6]);
+	writel(0x00240009, &mctl_ctl->init[7]);
+
+	clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
+
+	/* Configure DFI timing */
+	writel(tphy_wrlat | 0x2000000 | (t_rdata_en << 16) | 0x808000,
+	       &mctl_ctl->dfitmg0);
+	writel(0x100202, &mctl_ctl->dfitmg1);
+
+	/* set refresh timing */
+	writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
+}