summaryrefslogtreecommitdiff
path: root/arch/arm/mach-sunxi
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-sunxi')
-rw-r--r--arch/arm/mach-sunxi/Kconfig37
-rw-r--r--arch/arm/mach-sunxi/Makefile2
-rw-r--r--arch/arm/mach-sunxi/board.c6
-rw-r--r--arch/arm/mach-sunxi/clock_sun50i_h6.c94
-rw-r--r--arch/arm/mach-sunxi/cpu_info.c2
-rw-r--r--arch/arm/mach-sunxi/dram_sun50i_h6.c755
-rw-r--r--arch/arm/mach-sunxi/rmr_switch.S6
7 files changed, 891 insertions, 11 deletions
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 678e33dd40e..558363b52d2 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -42,6 +42,12 @@ config DRAM_SUN9I
Select this dram controller driver for Sun9i platforms,
like A80.
+config DRAM_SUN50I_H6
+ bool
+ help
+ Select this dram controller driver for some sun50i platforms,
+ like H6.
+
config SUN6I_P2WI
bool "Allwinner sun6i internal P2WI controller"
help
@@ -73,16 +79,16 @@ config SUN8I_RSB
with various RSB based devices, such as AXP223, AXP8XX PMICs,
and AC100/AC200 ICs.
-config SUNXI_HIGH_SRAM
- bool
- default n
+config SUNXI_SRAM_ADDRESS
+ hex
+ default 0x10000 if MACH_SUN9I || MACH_SUN50I || MACH_SUN50I_H5
+ default 0x20000 if MACH_SUN50I_H6
+ default 0x0
---help---
Older Allwinner SoCs have their mask boot ROM mapped just below 4GB,
with the first SRAM region being located at address 0.
Some newer SoCs map the boot ROM at address 0 instead and move the
- SRAM to 64KB, just behind the mask ROM.
- Chips using the latter setup are supposed to select this option to
- adjust the addresses accordingly.
+ SRAM to a different address.
config SUNXI_A64_TIMER_ERRATUM
bool
@@ -257,7 +263,6 @@ config MACH_SUN9I
select CPU_V7A
select DRAM_SUN9I
select SUN6I_PRCM
- select SUNXI_HIGH_SRAM
select SUNXI_GEN_SUN6I
select SUN8I_RSB
select SUPPORT_SPL
@@ -269,7 +274,6 @@ config MACH_SUN50I
select PHY_SUN4I_USB
select SUNXI_DE2
select SUNXI_GEN_SUN6I
- select SUNXI_HIGH_SRAM
select SUPPORT_SPL
select SUNXI_DRAM_DW
select SUNXI_DRAM_DW_32BIT
@@ -281,10 +285,17 @@ config MACH_SUN50I_H5
bool "sun50i (Allwinner H5)"
select ARM64
select MACH_SUNXI_H3_H5
- select SUNXI_HIGH_SRAM
select FIT
select SPL_LOAD_FIT
+config MACH_SUN50I_H6
+ bool "sun50i (Allwinner H6)"
+ select ARM64
+ select SUPPORT_SPL
+ select FIT
+ select SPL_LOAD_FIT
+ select DRAM_SUN50I_H6
+
endchoice
# The sun8i SoCs share a lot, this helps to avoid a lot of "if A23 || A33"
@@ -378,6 +389,7 @@ config DRAM_CLK
default 360 if MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || \
MACH_SUN8I_V3S
default 672 if MACH_SUN50I
+ default 744 if MACH_SUN50I_H6
---help---
Set the dram clock speed, valid range 240 - 480 (prior to sun9i),
must be a multiple of 24. For the sun9i (A80), the tested values
@@ -397,7 +409,7 @@ config DRAM_ZQ
default 123 if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I || MACH_SUN8I
default 127 if MACH_SUN7I
default 14779 if MACH_SUN8I_V3S
- default 3881979 if MACH_SUN8I_R40
+ default 3881979 if MACH_SUN8I_R40 || MACH_SUN50I_H6
default 4145117 if MACH_SUN9I
default 3881915 if MACH_SUN50I
---help---
@@ -409,6 +421,7 @@ config DRAM_ODT_EN
default y if MACH_SUN8I_A23
default y if MACH_SUN8I_R40
default y if MACH_SUN50I
+ default y if MACH_SUN50I_H6
---help---
Select this to enable dram odt (on die termination).
@@ -499,6 +512,7 @@ config SYS_CLK_FREQ
default 816000000 if MACH_SUN50I || MACH_SUN50I_H5
default 1008000000 if MACH_SUN8I
default 1008000000 if MACH_SUN9I
+ default 888000000 if MACH_SUN50I_H6
config SYS_CONFIG_NAME
default "sun4i" if MACH_SUN4I
@@ -508,6 +522,7 @@ config SYS_CONFIG_NAME
default "sun8i" if MACH_SUN8I
default "sun9i" if MACH_SUN9I
default "sun50i" if MACH_SUN50I
+ default "sun50i" if MACH_SUN50I_H6
config SYS_BOARD
default "sunxi"
@@ -713,6 +728,7 @@ config VIDEO_SUNXI
depends on !MACH_SUN8I_V3S
depends on !MACH_SUN9I
depends on !MACH_SUN50I
+ depends on !MACH_SUN50I_H6
select VIDEO
imply VIDEO_DT_SIMPLEFB
default y
@@ -945,6 +961,7 @@ config SPL_STACK_R_ADDR
default 0x4fe00000 if MACH_SUN8I
default 0x2fe00000 if MACH_SUN9I
default 0x4fe00000 if MACH_SUN50I
+ default 0x4fe00000 if MACH_SUN50I_H6
config SPL_SPI_SUNXI
bool "Support for SPI Flash on Allwinner SoCs in SPL"
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index 4c752491a15..43a93e30857 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -26,6 +26,7 @@ else
obj-$(CONFIG_MACH_SUN8I) += clock_sun6i.o
endif
obj-$(CONFIG_MACH_SUN9I) += clock_sun9i.o gtbus_sun9i.o
+obj-$(CONFIG_MACH_SUN50I_H6) += clock_sun50i_h6.o
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_DRAM_SUN4I) += dram_sun4i.o
@@ -37,4 +38,5 @@ obj-$(CONFIG_DRAM_SUN9I) += dram_sun9i.o
obj-$(CONFIG_SPL_SPI_SUNXI) += spl_spi_sunxi.o
obj-$(CONFIG_SUNXI_DRAM_DW) += dram_sunxi_dw.o
obj-$(CONFIG_SUNXI_DRAM_DW) += dram_timings/
+obj-$(CONFIG_DRAM_SUN50I_H6) += dram_sun50i_h6.o
endif
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 58fef05bd73..d22a84ea6ba 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -107,6 +107,10 @@ static int gpio_init(void)
sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN50I_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP);
+#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN50I_H6)
+ sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H6_GPH_UART0);
+ sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H6_GPH_UART0);
+ sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_A83T)
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_A83T_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN8I_A83T_GPB_UART0);
@@ -282,7 +286,7 @@ void reset_cpu(ulong addr)
/* sun5i sometimes gets stuck without this */
writel(WDT_MODE_RESET_EN | WDT_MODE_EN, &wdog->mode);
}
-#elif defined(CONFIG_SUNXI_GEN_SUN6I)
+#elif defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_MACH_SUN50I_H6)
static const struct sunxi_wdog *wdog =
((struct sunxi_timer_reg *)SUNXI_TIMER_BASE)->wdog;
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
new file mode 100644
index 00000000000..ba8a26eb0d3
--- /dev/null
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -0,0 +1,94 @@
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/clock.h>
+
+#ifdef CONFIG_SPL_BUILD
+void clock_init_safe(void)
+{
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ clock_set_pll1(408000000);
+
+ writel(CCM_PLL6_DEFAULT, &ccm->pll6_cfg);
+ while (!(readl(&ccm->pll6_cfg) & CCM_PLL6_LOCK))
+ ;
+
+ clrsetbits_le32(&ccm->cpu_axi_cfg, CCM_CPU_AXI_APB_MASK | CCM_CPU_AXI_AXI_MASK,
+ CCM_CPU_AXI_DEFAULT_FACTORS);
+
+ writel(CCM_PSI_AHB1_AHB2_DEFAULT, &ccm->psi_ahb1_ahb2_cfg);
+ writel(CCM_AHB3_DEFAULT, &ccm->ahb3_cfg);
+ writel(CCM_APB1_DEFAULT, &ccm->apb1_cfg);
+
+ /*
+ * The mux and factor are set, but the clock will be enabled in
+ * DRAM initialization code.
+ */
+ writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), &ccm->mbus_cfg);
+}
+#endif
+
+void clock_init_uart(void)
+{
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+ /* uart clock source is apb2 */
+ writel(APB2_CLK_SRC_OSC24M|
+ APB2_CLK_RATE_N_1|
+ APB2_CLK_RATE_M(1),
+ &ccm->apb2_cfg);
+
+ /* open the clock for uart */
+ setbits_le32(&ccm->uart_gate_reset,
+ 1 << (CONFIG_CONS_INDEX - 1));
+
+ /* deassert uart reset */
+ setbits_le32(&ccm->uart_gate_reset,
+ 1 << (RESET_SHIFT + CONFIG_CONS_INDEX - 1));
+}
+
+#ifdef CONFIG_SPL_BUILD
+void clock_set_pll1(unsigned int clk)
+{
+ struct sunxi_ccm_reg * const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ u32 val;
+
+ /* Do not support clocks < 288MHz as they need factor P */
+ if (clk < 288000000) clk = 288000000;
+
+ /* Switch to 24MHz clock while changing PLL1 */
+ val = readl(&ccm->cpu_axi_cfg);
+ val &= ~CCM_CPU_AXI_MUX_MASK;
+ val |= CCM_CPU_AXI_MUX_OSC24M;
+ writel(val, &ccm->cpu_axi_cfg);
+
+ /* clk = 24*n/p, p is ignored if clock is >288MHz */
+ writel(CCM_PLL1_CTRL_EN | CCM_PLL1_LOCK_EN | CCM_PLL1_CLOCK_TIME_2 |
+ CCM_PLL1_CTRL_N(clk / 24000000), &ccm->pll1_cfg);
+ while (!(readl(&ccm->pll1_cfg) & CCM_PLL1_LOCK)) {}
+
+ /* Switch CPU to PLL1 */
+ val = readl(&ccm->cpu_axi_cfg);
+ val &= ~CCM_CPU_AXI_MUX_MASK;
+ val |= CCM_CPU_AXI_MUX_PLL_CPUX;
+ writel(val, &ccm->cpu_axi_cfg);
+}
+#endif
+
+unsigned int clock_get_pll6(void)
+{
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+ uint32_t rval = readl(&ccm->pll6_cfg);
+ int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT);
+ int div1 = ((rval & CCM_PLL6_CTRL_DIV1_MASK) >>
+ CCM_PLL6_CTRL_DIV1_SHIFT) + 1;
+ int div2 = ((rval & CCM_PLL6_CTRL_DIV2_MASK) >>
+ CCM_PLL6_CTRL_DIV2_SHIFT) + 1;
+ /* The register defines PLL6-4X, not plain PLL6 */
+ return 24000000 / 4 * n / div1 / div2;
+}
diff --git a/arch/arm/mach-sunxi/cpu_info.c b/arch/arm/mach-sunxi/cpu_info.c
index aadf575ef2b..ae4745bfecc 100644
--- a/arch/arm/mach-sunxi/cpu_info.c
+++ b/arch/arm/mach-sunxi/cpu_info.c
@@ -96,6 +96,8 @@ int print_cpuinfo(void)
puts("CPU: Allwinner A64 (SUN50I)\n");
#elif defined CONFIG_MACH_SUN50I_H5
puts("CPU: Allwinner H5 (SUN50I)\n");
+#elif defined CONFIG_MACH_SUN50I_H6
+ puts("CPU: Allwinner H6 (SUN50I)\n");
#else
#warning Please update cpu_info.c with correct CPU information
puts("CPU: SUNXI Family\n");
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c
new file mode 100644
index 00000000000..6b94cf38c5c
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c
@@ -0,0 +1,755 @@
+/*
+ * sun50i H6 platform dram controller init
+ *
+ * (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/dram.h>
+#include <asm/arch/cpu.h>
+#include <linux/bitops.h>
+#include <linux/kconfig.h>
+
+/*
+ * The DRAM controller structure on H6 is similar to the ones on A23/A80:
+ * they all contains 3 parts, COM, CTL and PHY. (As a note on A33/A83T/H3/A64
+ * /H5/R40 CTL and PHY is composed).
+ *
+ * COM is allwinner-specific. On H6, the address mapping function is moved
+ * from COM to CTL (with the standard ADDRMAP registers on DesignWare memory
+ * controller).
+ *
+ * CTL (controller) and PHY is from DesignWare.
+ *
+ * The CTL part is a bit similar to the one on A23/A80 (because they all
+ * originate from DesignWare), but gets more registers added.
+ *
+ * The PHY part is quite new, not seen in any previous Allwinner SoCs, and
+ * not seen on other SoCs in U-Boot. The only SoC that is also known to have
+ * similar PHY is ZynqMP.
+ */
+
+/*
+ * The delay parameters below allow to allegedly specify delay times of some
+ * unknown unit for each individual bit trace in each of the four data bytes
+ * the 32-bit wide access consists of. Also three control signals can be
+ * adjusted individually.
+ */
+#define NR_OF_BYTE_LANES (32 / BITS_PER_BYTE)
+/* The eight data lines (DQn) plus DM, DQS, DQS/DM/DQ Output Enable and DQSN */
+#define WR_LINES_PER_BYTE_LANE (BITS_PER_BYTE + 4)
+/*
+ * The eight data lines (DQn) plus DM, DQS, DQS/DM/DQ Output Enable, DQSN,
+ * Termination and Power down
+ */
+#define RD_LINES_PER_BYTE_LANE (BITS_PER_BYTE + 6)
+struct dram_para {
+ u32 clk;
+ enum sunxi_dram_type type;
+ u8 cols;
+ u8 rows;
+ u8 ranks;
+ const u8 dx_read_delays[NR_OF_BYTE_LANES][RD_LINES_PER_BYTE_LANE];
+ const u8 dx_write_delays[NR_OF_BYTE_LANES][WR_LINES_PER_BYTE_LANE];
+};
+
+static void mctl_sys_init(struct dram_para *para);
+static void mctl_com_init(struct dram_para *para);
+static void mctl_set_timing_lpddr3(struct dram_para *para);
+static void mctl_channel_init(struct dram_para *para);
+
+static void mctl_core_init(struct dram_para *para)
+{
+ mctl_sys_init(para);
+ mctl_com_init(para);
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ mctl_set_timing_lpddr3(para);
+ break;
+ default:
+ panic("Unsupported DRAM type!");
+ };
+ mctl_channel_init(para);
+}
+
+static void mctl_phy_pir_init(u32 val)
+{
+ struct sunxi_mctl_phy_reg * const mctl_phy =
+ (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
+
+ writel(val | BIT(0), &mctl_phy->pir);
+ mctl_await_completion(&mctl_phy->pgsr[0], BIT(0), BIT(0));
+}
+
+enum {
+ MBUS_PORT_CPU = 0,
+ MBUS_PORT_GPU = 1,
+ MBUS_PORT_MAHB = 2,
+ MBUS_PORT_DMA = 3,
+ MBUS_PORT_VE = 4,
+ MBUS_PORT_CE = 5,
+ MBUS_PORT_TSC0 = 6,
+ MBUS_PORT_NDFC0 = 8,
+ MBUS_PORT_CSI0 = 11,
+ MBUS_PORT_DI0 = 14,
+ MBUS_PORT_DI1 = 15,
+ MBUS_PORT_DE300 = 16,
+ MBUS_PORT_IOMMU = 25,
+ MBUS_PORT_VE2 = 26,
+ MBUS_PORT_USB3 = 37,
+ MBUS_PORT_PCIE = 38,
+ MBUS_PORT_VP9 = 39,
+ MBUS_PORT_HDCP2 = 40,
+};
+
+enum {
+ MBUS_QOS_LOWEST = 0,
+ MBUS_QOS_LOW,
+ MBUS_QOS_HIGH,
+ MBUS_QOS_HIGHEST
+};
+inline void mbus_configure_port(u8 port,
+ bool bwlimit,
+ bool priority,
+ u8 qos,
+ u8 waittime,
+ u8 acs,
+ u16 bwl0,
+ u16 bwl1,
+ u16 bwl2)
+{
+ struct sunxi_mctl_com_reg * const mctl_com =
+ (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+ const u32 cfg0 = ( (bwlimit ? (1 << 0) : 0)
+ | (priority ? (1 << 1) : 0)
+ | ((qos & 0x3) << 2)
+ | ((waittime & 0xf) << 4)
+ | ((acs & 0xff) << 8)
+ | (bwl0 << 16) );
+ const u32 cfg1 = ((u32)bwl2 << 16) | (bwl1 & 0xffff);
+
+ debug("MBUS port %d cfg0 %08x cfg1 %08x\n", port, cfg0, cfg1);
+ writel(cfg0, &mctl_com->master[port].cfg0);
+ writel(cfg1, &mctl_com->master[port].cfg1);
+}
+
+#define MBUS_CONF(port, bwlimit, qos, acs, bwl0, bwl1, bwl2) \
+ mbus_configure_port(MBUS_PORT_ ## port, bwlimit, false, \
+ MBUS_QOS_ ## qos, 0, acs, bwl0, bwl1, bwl2)
+
+static void mctl_set_master_priority(void)
+{
+ struct sunxi_mctl_com_reg * const mctl_com =
+ (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+
+ /* enable bandwidth limit windows and set windows size 1us */
+ writel(399, &mctl_com->tmr);
+ writel(BIT(16), &mctl_com->bwcr);
+
+ MBUS_CONF( CPU, true, HIGHEST, 0, 256, 128, 100);
+ MBUS_CONF( GPU, true, HIGH, 0, 1536, 1400, 256);
+ MBUS_CONF( MAHB, true, HIGHEST, 0, 512, 256, 96);
+ MBUS_CONF( DMA, true, HIGH, 0, 256, 100, 80);
+ MBUS_CONF( VE, true, HIGH, 2, 8192, 5500, 5000);
+ MBUS_CONF( CE, true, HIGH, 2, 100, 64, 32);
+ MBUS_CONF( TSC0, true, HIGH, 2, 100, 64, 32);
+ MBUS_CONF(NDFC0, true, HIGH, 0, 256, 128, 64);
+ MBUS_CONF( CSI0, true, HIGH, 0, 256, 128, 100);
+ MBUS_CONF( DI0, true, HIGH, 0, 1024, 256, 64);
+ MBUS_CONF(DE300, true, HIGHEST, 6, 8192, 2800, 2400);
+ MBUS_CONF(IOMMU, true, HIGHEST, 0, 100, 64, 32);
+ MBUS_CONF( VE2, true, HIGH, 2, 8192, 5500, 5000);
+ MBUS_CONF( USB3, true, HIGH, 0, 256, 128, 64);
+ MBUS_CONF( PCIE, true, HIGH, 2, 100, 64, 32);
+ MBUS_CONF( VP9, true, HIGH, 2, 8192, 5500, 5000);
+ MBUS_CONF(HDCP2, true, HIGH, 2, 100, 64, 32);
+}
+
+static u32 mr_lpddr3[12] = {
+ 0x00000000, 0x00000043, 0x0000001a, 0x00000001,
+ 0x00000000, 0x00000000, 0x00000048, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000003,
+};
+
+/* TODO: flexible timing */
+static void mctl_set_timing_lpddr3(struct dram_para *para)
+{
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ struct sunxi_mctl_phy_reg * const mctl_phy =
+ (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
+
+ u8 tccd = 2;
+ u8 tfaw = max(ns_to_t(50), 4);
+ u8 trrd = max(ns_to_t(10), 2);
+ u8 trcd = max(ns_to_t(24), 2);
+ u8 trc = ns_to_t(70);
+ u8 txp = max(ns_to_t(8), 2);
+ u8 twtr = max(ns_to_t(8), 2);
+ u8 trtp = max(ns_to_t(8), 2);
+ u8 twr = max(ns_to_t(15), 2);
+ u8 trp = ns_to_t(18);
+ u8 tras = ns_to_t(42);
+ u8 twtr_sa = ns_to_t(5);
+ u8 tcksrea = ns_to_t(11);
+ u16 trefi = ns_to_t(3900) / 32;
+ u16 trfc = ns_to_t(210);
+ u16 txsr = ns_to_t(220);
+
+ if (CONFIG_DRAM_CLK % 400 == 0) {
+ /* Round up these parameters */
+ twtr_sa++;
+ tcksrea++;
+ }
+
+ u8 tmrw = 5;
+ u8 tmrd = 5;
+ u8 tmod = 12;
+ u8 tcke = 3;
+ u8 tcksrx = 5;
+ u8 tcksre = 5;
+ u8 tckesr = 5;
+ u8 trasmax = CONFIG_DRAM_CLK / 60;
+ u8 txs = 4;
+ u8 txsdll = 4;
+ u8 txsabort = 4;
+ u8 txsfast = 4;
+
+ u8 tcl = 5; /* CL 10 */
+ u8 tcwl = 3; /* CWL 6 */
+ u8 t_rdata_en = twtr_sa + 8;
+
+ u32 tdinit0 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */
+ u32 tdinit1 = (100 * CONFIG_DRAM_CLK) / 1000 + 1; /* 100ns */
+ u32 tdinit2 = (11 * CONFIG_DRAM_CLK) + 1; /* 11us */
+ u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */
+
+ u8 twtp = tcwl + 4 + twr + 1;
+ /*
+ * The code below for twr2rd and trd2wr follows the IP core's
+ * document from ZynqMP and i.MX7. The BSP has both number
+ * substracted by 2.
+ */
+ u8 twr2rd = tcwl + 4 + 1 + twtr;
+ u8 trd2wr = tcl + 4 + (tcksrea >> 1) - tcwl + 1;
+
+ /* set mode register */
+ memcpy(mctl_phy->mr, mr_lpddr3, sizeof(mr_lpddr3));
+
+ /* 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(txsr, &mctl_ctl->dramtmg[14]);
+
+ clrsetbits_le32(&mctl_ctl->init[0], (3 << 30), (1 << 30));
+ writel(0, &mctl_ctl->dfimisc);
+ clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
+
+ /*
+ * Set timing registers of the PHY.
+ * Note: the PHY is clocked 2x from the DRAM frequency.
+ */
+ writel((trrd << 25) | (tras << 17) | (trp << 9) | (trtp << 1),
+ &mctl_phy->dtpr[0]);
+ writel((tfaw << 17) | 0x28000400 | (tmrd << 1), &mctl_phy->dtpr[1]);
+ writel(((txs << 6) - 1) | (tcke << 17), &mctl_phy->dtpr[2]);
+ writel(((txsdll << 22) - (0x1 << 16)) | twtr_sa | (tcksrea << 8),
+ &mctl_phy->dtpr[3]);
+ writel((txp << 1) | (trfc << 17) | 0x800, &mctl_phy->dtpr[4]);
+ writel((trc << 17) | (trcd << 9) | (twtr << 1), &mctl_phy->dtpr[5]);
+ writel(0x0505, &mctl_phy->dtpr[6]);
+
+ /* Configure DFI timing */
+ writel(tcl | 0x2000200 | (t_rdata_en << 16) | 0x808000,
+ &mctl_ctl->dfitmg0);
+ writel(0x040201, &mctl_ctl->dfitmg1);
+
+ /* Configure PHY timing */
+ writel(tdinit0 | (tdinit1 << 20), &mctl_phy->ptr[3]);
+ writel(tdinit2 | (tdinit3 << 18), &mctl_phy->ptr[4]);
+
+ /* set refresh timing */
+ writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
+}
+
+static void mctl_sys_init(struct dram_para *para)
+{
+ struct sunxi_ccm_reg * const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ struct sunxi_mctl_com_reg * const mctl_com =
+ (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ /* Put all DRAM-related blocks to reset state */
+ clrbits_le32(&ccm->mbus_cfg, MBUS_ENABLE | MBUS_RESET);
+ writel(0, &ccm->dram_gate_reset);
+ clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN);
+ clrbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET);
+
+ udelay(5);
+
+ /* Set PLL5 rate to doubled DRAM clock rate */
+ writel(CCM_PLL5_CTRL_EN | CCM_PLL5_LOCK_EN |
+ CCM_PLL5_CTRL_N(para->clk * 2 / 24 - 1), &ccm->pll5_cfg);
+ mctl_await_completion(&ccm->pll5_cfg, CCM_PLL5_LOCK, CCM_PLL5_LOCK);
+
+ /* Configure DRAM mod clock */
+ writel(DRAM_CLK_SRC_PLL5, &ccm->dram_clk_cfg);
+ setbits_le32(&ccm->dram_clk_cfg, DRAM_CLK_UPDATE);
+ writel(BIT(0) | BIT(RESET_SHIFT), &ccm->dram_gate_reset);
+
+ /* Disable all channels */
+ writel(0, &mctl_com->maer0);
+ writel(0, &mctl_com->maer1);
+ writel(0, &mctl_com->maer2);
+
+ /* Configure MBUS and enable DRAM mod reset */
+ setbits_le32(&ccm->mbus_cfg, MBUS_RESET);
+ setbits_le32(&ccm->mbus_cfg, MBUS_ENABLE);
+ setbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET);
+ udelay(5);
+
+ /* Unknown hack from the BSP, which enables access of mctl_ctl regs */
+ writel(0x8000, &mctl_ctl->unk_0x00c);
+}
+
+static void mctl_set_addrmap(struct dram_para *para)
+{
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ u8 cols = para->cols;
+ u8 rows = para->rows;
+ u8 ranks = para->ranks;
+
+ /* Ranks */
+ if (ranks == 2)
+ mctl_ctl->addrmap[0] = rows + cols - 3;
+ else
+ mctl_ctl->addrmap[0] = 0x1F;
+
+ /* Banks, hardcoded to 8 banks now */
+ mctl_ctl->addrmap[1] = (cols - 2) | (cols - 2) << 8 | (cols - 2) << 16;
+
+ /* Columns */
+ mctl_ctl->addrmap[2] = 0;
+ switch (cols) {
+ case 8:
+ mctl_ctl->addrmap[3] = 0x1F1F0000;
+ mctl_ctl->addrmap[4] = 0x1F1F;
+ break;
+ case 9:
+ mctl_ctl->addrmap[3] = 0x1F000000;
+ mctl_ctl->addrmap[4] = 0x1F1F;
+ break;
+ case 10:
+ mctl_ctl->addrmap[3] = 0;
+ mctl_ctl->addrmap[4] = 0x1F1F;
+ break;
+ case 11:
+ mctl_ctl->addrmap[3] = 0;
+ mctl_ctl->addrmap[4] = 0x1F00;
+ break;
+ case 12:
+ mctl_ctl->addrmap[3] = 0;
+ mctl_ctl->addrmap[4] = 0;
+ break;
+ default:
+ panic("Unsupported DRAM configuration: column number invalid\n");
+ }
+
+ /* Rows */
+ mctl_ctl->addrmap[5] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);
+ switch (rows) {
+ case 13:
+ mctl_ctl->addrmap[6] = (cols - 3) | 0x0F0F0F00;
+ mctl_ctl->addrmap[7] = 0x0F0F;
+ break;
+ case 14:
+ mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | 0x0F0F0000;
+ mctl_ctl->addrmap[7] = 0x0F0F;
+ break;
+ case 15:
+ mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | 0x0F000000;
+ mctl_ctl->addrmap[7] = 0x0F0F;
+ break;
+ case 16:
+ mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);
+ mctl_ctl->addrmap[7] = 0x0F0F;
+ break;
+ case 17:
+ mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);
+ mctl_ctl->addrmap[7] = (cols - 3) | 0x0F00;
+ break;
+ case 18:
+ mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24);
+ mctl_ctl->addrmap[7] = (cols - 3) | ((cols - 3) << 8);
+ break;
+ default:
+ panic("Unsupported DRAM configuration: row number invalid\n");
+ }
+
+ /* Bank groups, DDR4 only */
+ mctl_ctl->addrmap[8] = 0x3F3F;
+}
+
+static void mctl_com_init(struct dram_para *para)
+{
+ struct sunxi_mctl_com_reg * const mctl_com =
+ (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ struct sunxi_mctl_phy_reg * const mctl_phy =
+ (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
+ u32 reg_val, tmp;
+
+ mctl_set_addrmap(para);
+
+ setbits_le32(&mctl_com->cr, BIT(31));
+ /*
+ * This address is magic; it's in SID memory area, but there's no
+ * known definition of it.
+ * On my Pine H64 board it has content 7.
+ */
+ if (readl(0x03006100) == 7)
+ clrbits_le32(&mctl_com->cr, BIT(27));
+ else if (readl(0x03006100) == 3)
+ setbits_le32(&mctl_com->cr, BIT(27));
+
+ if (para->clk > 408)
+ reg_val = 0xf00;
+ else if (para->clk > 246)
+ reg_val = 0x1f00;
+ else
+ reg_val = 0x3f00;
+ clrsetbits_le32(&mctl_com->unk_0x008, 0x3f00, reg_val);
+
+ /* TODO: half DQ, non-LPDDR3 types */
+ writel(MSTR_DEVICETYPE_LPDDR3 | MSTR_BUSWIDTH_FULL |
+ MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(para->ranks) |
+ 0x80000000, &mctl_ctl->mstr);
+ writel(DCR_LPDDR3 | DCR_DDR8BANK | 0x400, &mctl_phy->dcr);
+
+ if (para->ranks == 2)
+ writel(0x0303, &mctl_ctl->odtmap);
+ else
+ writel(0x0201, &mctl_ctl->odtmap);
+
+ /* TODO: non-LPDDR3 types */
+ tmp = para->clk * 7 / 2000;
+ reg_val = 0x0400;
+ reg_val |= (tmp + 7) << 24;
+ reg_val |= (((para->clk < 400) ? 3 : 4) - tmp) << 16;
+ writel(reg_val, &mctl_ctl->odtcfg);
+
+ /* TODO: half DQ */
+}
+
+static void mctl_bit_delay_set(struct dram_para *para)
+{
+ struct sunxi_mctl_phy_reg * const mctl_phy =
+ (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
+ int i, j;
+ u32 val;
+
+ for (i = 0; i < 4; i++) {
+ val = readl(&mctl_phy->dx[i].bdlr0);
+ for (j = 0; j < 4; j++)
+ val += para->dx_write_delays[i][j] << (j * 8);
+ writel(val, &mctl_phy->dx[i].bdlr0);
+
+ val = readl(&mctl_phy->dx[i].bdlr1);
+ for (j = 0; j < 4; j++)
+ val += para->dx_write_delays[i][j + 4] << (j * 8);
+ writel(val, &mctl_phy->dx[i].bdlr1);
+
+ val = readl(&mctl_phy->dx[i].bdlr2);
+ for (j = 0; j < 4; j++)
+ val += para->dx_write_delays[i][j + 8] << (j * 8);
+ writel(val, &mctl_phy->dx[i].bdlr2);
+ }
+ clrbits_le32(&mctl_phy->pgcr[0], BIT(26));
+
+ for (i = 0; i < 4; i++) {
+ val = readl(&mctl_phy->dx[i].bdlr3);
+ for (j = 0; j < 4; j++)
+ val += para->dx_read_delays[i][j] << (j * 8);
+ writel(val, &mctl_phy->dx[i].bdlr3);
+
+ val = readl(&mctl_phy->dx[i].bdlr4);
+ for (j = 0; j < 4; j++)
+ val += para->dx_read_delays[i][j + 4] << (j * 8);
+ writel(val, &mctl_phy->dx[i].bdlr4);
+
+ val = readl(&mctl_phy->dx[i].bdlr5);
+ for (j = 0; j < 4; j++)
+ val += para->dx_read_delays[i][j + 8] << (j * 8);
+ writel(val, &mctl_phy->dx[i].bdlr5);
+
+ val = readl(&mctl_phy->dx[i].bdlr6);
+ val += (para->dx_read_delays[i][12] << 8) |
+ (para->dx_read_delays[i][13] << 16);
+ writel(val, &mctl_phy->dx[i].bdlr6);
+ }
+ setbits_le32(&mctl_phy->pgcr[0], BIT(26));
+ udelay(1);
+
+ for (i = 1; i < 14; i++) {
+ val = readl(&mctl_phy->acbdlr[i]);
+ val += 0x0a0a0a0a;
+ writel(val, &mctl_phy->acbdlr[i]);
+ }
+}
+
+static void mctl_channel_init(struct dram_para *para)
+{
+ struct sunxi_mctl_com_reg * const mctl_com =
+ (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ struct sunxi_mctl_phy_reg * const mctl_phy =
+ (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE;
+ int i;
+ u32 val;
+
+ setbits_le32(&mctl_ctl->dfiupd[0], BIT(31) | BIT(30));
+ setbits_le32(&mctl_ctl->zqctl[0], BIT(31) | BIT(30));
+ writel(0x2f05, &mctl_ctl->sched[0]);
+ setbits_le32(&mctl_ctl->rfshctl3, BIT(0));
+ setbits_le32(&mctl_ctl->dfimisc, BIT(0));
+ setbits_le32(&mctl_ctl->unk_0x00c, BIT(8));
+ clrsetbits_le32(&mctl_phy->pgcr[1], 0x180, 0xc0);
+ /* TODO: non-LPDDR3 types */
+ clrsetbits_le32(&mctl_phy->pgcr[2], GENMASK(17, 0), ns_to_t(7800));
+ clrbits_le32(&mctl_phy->pgcr[6], BIT(0));
+ clrsetbits_le32(&mctl_phy->dxccr, 0xee0, 0x220);
+ /* TODO: VT compensation */
+ clrsetbits_le32(&mctl_phy->dsgcr, BIT(0), 0x440060);
+ clrbits_le32(&mctl_phy->vtcr[1], BIT(1));
+
+ for (i = 0; i < 4; i++)
+ clrsetbits_le32(&mctl_phy->dx[i].gcr[0], 0xe00, 0x800);
+ for (i = 0; i < 4; i++)
+ clrsetbits_le32(&mctl_phy->dx[i].gcr[2], 0xffff, 0x5555);
+ for (i = 0; i < 4; i++)
+ clrsetbits_le32(&mctl_phy->dx[i].gcr[3], 0x3030, 0x1010);
+
+ udelay(100);
+
+ if (para->ranks == 2)
+ setbits_le32(&mctl_phy->dtcr[1], 0x30000);
+ else
+ clrsetbits_le32(&mctl_phy->dtcr[1], 0x30000, 0x10000);
+
+ clrbits_le32(&mctl_phy->dtcr[1], BIT(1));
+ if (para->ranks == 2) {
+ writel(0x00010001, &mctl_phy->rankidr);
+ writel(0x20000, &mctl_phy->odtcr);
+ } else {
+ writel(0x0, &mctl_phy->rankidr);
+ writel(0x10000, &mctl_phy->odtcr);
+ }
+
+ /* TODO: non-LPDDR3 types */
+ clrsetbits_le32(&mctl_phy->dtcr[0], 0xF0000000, 0x10000040);
+ if (para->clk <= 792) {
+ if (para->clk <= 672) {
+ if (para->clk <= 600)
+ val = 0x300;
+ else
+ val = 0x400;
+ } else {
+ val = 0x500;
+ }
+ } else {
+ val = 0x600;
+ }
+ /* FIXME: NOT REVIEWED YET */
+ clrsetbits_le32(&mctl_phy->zq[0].zqcr, 0x700, val);
+ clrsetbits_le32(&mctl_phy->zq[0].zqpr[0], 0xff,
+ CONFIG_DRAM_ZQ & 0xff);
+ clrbits_le32(&mctl_phy->zq[0].zqor[0], 0xfffff);
+ setbits_le32(&mctl_phy->zq[0].zqor[0], (CONFIG_DRAM_ZQ >> 8) & 0xff);
+ setbits_le32(&mctl_phy->zq[0].zqor[0], (CONFIG_DRAM_ZQ & 0xf00) - 0x100);
+ setbits_le32(&mctl_phy->zq[0].zqor[0], (CONFIG_DRAM_ZQ & 0xff00) << 4);
+ clrbits_le32(&mctl_phy->zq[1].zqpr[0], 0xfffff);
+ setbits_le32(&mctl_phy->zq[1].zqpr[0], (CONFIG_DRAM_ZQ >> 16) & 0xff);
+ setbits_le32(&mctl_phy->zq[1].zqpr[0], ((CONFIG_DRAM_ZQ >> 8) & 0xf00) - 0x100);
+ setbits_le32(&mctl_phy->zq[1].zqpr[0], (CONFIG_DRAM_ZQ & 0xff0000) >> 4);
+ if (para->type == SUNXI_DRAM_TYPE_LPDDR3) {
+ for (i = 1; i < 14; i++)
+ writel(0x06060606, &mctl_phy->acbdlr[i]);
+ }
+
+ /* TODO: non-LPDDR3 types */
+ mctl_phy_pir_init(PIR_ZCAL | PIR_DCAL | PIR_PHYRST | PIR_DRAMINIT |
+ PIR_QSGATE | PIR_RDDSKW | PIR_WRDSKW | PIR_RDEYE |
+ PIR_WREYE);
+
+ /* TODO: non-LPDDR3 types */
+ for (i = 0; i < 4; i++)
+ writel(0x00000909, &mctl_phy->dx[i].gcr[5]);
+
+ for (i = 0; i < 4; i++) {
+ if (IS_ENABLED(CONFIG_DRAM_ODT_EN))
+ val = 0x0;
+ else
+ val = 0xaaaa;
+ clrsetbits_le32(&mctl_phy->dx[i].gcr[2], 0xffff, val);
+
+ if (IS_ENABLED(CONFIG_DRAM_ODT_EN))
+ val = 0x0;
+ else
+ val = 0x2020;
+ clrsetbits_le32(&mctl_phy->dx[i].gcr[3], 0x3030, val);
+ }
+
+ mctl_bit_delay_set(para);
+ udelay(1);
+
+ setbits_le32(&mctl_phy->pgcr[6], BIT(0));
+ clrbits_le32(&mctl_phy->pgcr[6], 0xfff8);
+ for (i = 0; i < 4; i++)
+ clrbits_le32(&mctl_phy->dx[i].gcr[3], ~0x3ffff);
+ udelay(10);
+
+ if (readl(&mctl_phy->pgsr[0]) & 0x400000)
+ {
+ /*
+ * Detect single rank.
+ * TODO: also detect half DQ.
+ */
+ if ((readl(&mctl_phy->dx[0].rsr[0]) & 0x3) == 2 &&
+ (readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 2 &&
+ (readl(&mctl_phy->dx[2].rsr[0]) & 0x3) == 2 &&
+ (readl(&mctl_phy->dx[3].rsr[0]) & 0x3) == 2) {
+ para->ranks = 1;
+ /* Restart DRAM initialization from scratch. */
+ mctl_core_init(para);
+ return;
+ }
+ else {
+ panic("This DRAM setup is currently not supported.\n");
+ }
+ }
+
+ if (readl(&mctl_phy->pgsr[0]) & 0xff00000) {
+ /* Oops! There's something wrong! */
+ debug("PLL = %x\n", readl(0x3001010));
+ debug("DRAM PHY PGSR0 = %x\n", readl(&mctl_phy->pgsr[0]));
+ for (i = 0; i < 4; i++)
+ debug("DRAM PHY DX%dRSR0 = %x\n", i, readl(&mctl_phy->dx[i].rsr[0]));
+ panic("Error while initializing DRAM PHY!\n");
+ }
+
+ clrsetbits_le32(&mctl_phy->dsgcr, 0xc0, 0x40);
+ clrbits_le32(&mctl_phy->pgcr[1], 0x40);
+ clrbits_le32(&mctl_ctl->dfimisc, BIT(0));
+ writel(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, 1, 1);
+ clrbits_le32(&mctl_ctl->rfshctl3, BIT(0));
+
+ setbits_le32(&mctl_com->unk_0x014, BIT(31));
+ writel(0xffffffff, &mctl_com->maer0);
+ writel(0x7ff, &mctl_com->maer1);
+ writel(0xffff, &mctl_com->maer2);
+}
+
+static void mctl_auto_detect_dram_size(struct dram_para *para)
+{
+ /* TODO: non-LPDDR3, half DQ */
+ /*
+ * Detect rank number by the code in mctl_channel_init. Furtherly
+ * when DQ detection is available it will also be executed there.
+ */
+ mctl_core_init(para);
+
+ /* detect row address bits */
+ para->cols = 8;
+ para->rows = 18;
+ mctl_core_init(para);
+
+ for (para->rows = 13; para->rows < 18; para->rows++) {
+ /* 8 banks, 8 bit per byte and 32 bit width */
+ if (mctl_mem_matches((1 << (para->rows + para->cols + 5))))
+ break;
+ }
+
+ /* detect column address bits */
+ para->cols = 11;
+ mctl_core_init(para);
+
+ for (para->cols = 8; para->cols < 11; para->cols++) {
+ /* 8 bits per byte and 32 bit width */
+ if (mctl_mem_matches(1 << (para->cols + 2)))
+ break;
+ }
+}
+
+unsigned long mctl_calc_size(struct dram_para *para)
+{
+ /* TODO: non-LPDDR3, half DQ */
+
+ /* 8 banks, 32-bit (4 byte) data width */
+ return (1ULL << (para->cols + para->rows + 3)) * 4 * para->ranks;
+}
+
+#define SUN50I_H6_DX_WRITE_DELAYS \
+ {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0 }, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}
+#define SUN50I_H6_DX_READ_DELAYS \
+ {{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \
+ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \
+ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \
+ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }}
+
+unsigned long sunxi_dram_init(void)
+{
+ struct sunxi_mctl_com_reg * const mctl_com =
+ (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+ struct dram_para para = {
+ .clk = CONFIG_DRAM_CLK,
+ .type = SUNXI_DRAM_TYPE_LPDDR3,
+ .ranks = 2,
+ .cols = 11,
+ .rows = 14,
+ .dx_read_delays = SUN50I_H6_DX_READ_DELAYS,
+ .dx_write_delays = SUN50I_H6_DX_WRITE_DELAYS,
+ };
+
+ unsigned long size;
+
+ /* RES_CAL_CTRL_REG in BSP U-boot*/
+ setbits_le32(0x7010310, BIT(8));
+ clrbits_le32(0x7010318, 0x3f);
+
+ mctl_auto_detect_dram_size(&para);
+
+ mctl_core_init(&para);
+
+ size = mctl_calc_size(&para);
+
+ clrsetbits_le32(&mctl_com->cr, 0xf0, (size >> (10 + 10 + 4)) & 0xf0);
+
+ mctl_set_master_priority();
+
+ return size;
+};
diff --git a/arch/arm/mach-sunxi/rmr_switch.S b/arch/arm/mach-sunxi/rmr_switch.S
index cefa93001ba..fafd306f95b 100644
--- a/arch/arm/mach-sunxi/rmr_switch.S
+++ b/arch/arm/mach-sunxi/rmr_switch.S
@@ -26,9 +26,15 @@
@ reference and to be able to regenerate a (probably fixed) version of this
@ code found in encoded form in boot0.h.
+#include <config.h>
+
.text
+#ifndef CONFIG_MACH_SUN50I_H6
ldr r1, =0x017000a0 @ MMIO mapped RVBAR[0] register
+#else
+ ldr r1, =0x09010040 @ MMIO mapped RVBAR[0] register
+#endif
ldr r0, =0x57aA7add @ start address, to be replaced
str r0, [r1]
dsb sy