summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/cpu/armv8/fel_utils.S5
-rw-r--r--arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h49
-rw-r--r--arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h20
-rw-r--r--arch/arm/include/asm/arch-sunxi/dram.h2
-rw-r--r--arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h153
-rw-r--r--arch/arm/include/asm/arch-sunxi/watchdog.h2
-rw-r--r--arch/arm/mach-sunxi/Kconfig54
-rw-r--r--arch/arm/mach-sunxi/Makefile2
-rw-r--r--arch/arm/mach-sunxi/board.c11
-rw-r--r--arch/arm/mach-sunxi/clock_sun50i_h6.c153
-rw-r--r--arch/arm/mach-sunxi/cpu_info.c2
-rw-r--r--arch/arm/mach-sunxi/dram_sun50i_a133.c10
-rw-r--r--arch/arm/mach-sunxi/dram_sun50i_h6.c6
-rw-r--r--arch/arm/mach-sunxi/dram_sun50i_h616.c6
-rw-r--r--arch/arm/mach-sunxi/dram_sun55i_a523.c1590
-rw-r--r--arch/arm/mach-sunxi/dram_timings/Makefile2
-rw-r--r--arch/arm/mach-sunxi/dram_timings/a523_ddr3.c113
-rw-r--r--arch/arm/mach-sunxi/dram_timings/a523_lpddr4.c119
-rw-r--r--board/sunxi/MAINTAINERS15
-rw-r--r--board/sunxi/board.c7
-rw-r--r--common/spl/Kconfig4
-rw-r--r--configs/avaota-a1_defconfig31
-rw-r--r--configs/radxa-cubie-a5e_defconfig32
-rw-r--r--configs/x96q_pro_plus_defconfig32
-rw-r--r--drivers/clk/sunxi/Kconfig14
-rw-r--r--drivers/clk/sunxi/Makefile2
-rw-r--r--drivers/clk/sunxi/clk_a523.c85
-rw-r--r--drivers/clk/sunxi/clk_a523_r.c39
-rw-r--r--drivers/clk/sunxi/clk_sunxi.c10
-rw-r--r--drivers/mmc/sunxi_mmc.c20
-rw-r--r--drivers/pinctrl/sunxi/Kconfig10
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.c50
-rw-r--r--drivers/power/pmic/axp.c1
-rw-r--r--drivers/power/regulator/axp_regulator.c1
-rw-r--r--dts/upstream/src/arm64/allwinner/sun55i-a523.dtsi598
-rw-r--r--dts/upstream/src/arm64/allwinner/sun55i-a527-cubie-a5e.dts299
-rw-r--r--dts/upstream/src/arm64/allwinner/sun55i-h728-x96qpro+.dts287
-rw-r--r--dts/upstream/src/arm64/allwinner/sun55i-t527-avaota-a1.dts308
-rw-r--r--include/axp_pmic.h1
-rw-r--r--include/configs/sun55i.h11
40 files changed, 4094 insertions, 62 deletions
diff --git a/arch/arm/cpu/armv8/fel_utils.S b/arch/arm/cpu/armv8/fel_utils.S
index 6a7ec9a7ec1..ccddfaaf04c 100644
--- a/arch/arm/cpu/armv8/fel_utils.S
+++ b/arch/arm/cpu/armv8/fel_utils.S
@@ -41,10 +41,11 @@ ENTRY(return_to_fel)
str w2, [x1]
ldr w0, =0xfa50392f // CPU hotplug magic
-#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133)
+#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133) || \
+ defined(CONFIG_MACH_SUN55I_A523)
ldr w2, =(SUNXI_R_CPUCFG_BASE + 0x1c0)
str w0, [x2], #0x4
-#elif CONFIG_MACH_SUN50I_H6
+#elif defined(CONFIG_MACH_SUN50I_H6)
ldr w2, =(SUNXI_RTC_BASE + 0x1b8) // BOOT_CPU_HP_FLAG_REG
str w0, [x2], #0x4
#else
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
index 575dff68804..45fa4ab6e57 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
@@ -13,6 +13,7 @@
#include <linux/bitops.h>
#endif
+/* Main CCU register offsets */
#define CCU_H6_PLL1_CFG 0x000
#define CCU_H6_PLL5_CFG 0x010
#define CCU_H6_PLL6_CFG 0x020
@@ -31,29 +32,39 @@
#define CCU_H6_UART_GATE_RESET 0x90c
#define CCU_H6_I2C_GATE_RESET 0x91c
-/* pll1 bit field */
-#define CCM_PLL1_CTRL_EN BIT(31)
-#define CCM_PLL1_LDO_EN BIT(30)
-#define CCM_PLL1_LOCK_EN BIT(29)
-#define CCM_PLL1_LOCK BIT(28)
-#define CCM_PLL1_OUT_EN BIT(27)
-#define CCM_PLL1_CLOCK_TIME_2 (2 << 24)
+/* A523 CPU PLL offsets */
+#define CPC_CPUA_PLL_CTRL 0x04
+#define CPC_DSU_PLL_CTRL 0x08
+#define CPC_CPUB_PLL_CTRL 0x0c
+#define CPC_CPUA_CLK_REG 0x60
+#define CPC_CPUB_CLK_REG 0x64
+#define CPC_DSU_CLK_REG 0x6c
+
+/* PLL bit fields */
+#define CCM_PLL_CTRL_EN BIT(31)
+#define CCM_PLL_LDO_EN BIT(30)
+#define CCM_PLL_LOCK_EN BIT(29)
+#define CCM_PLL_LOCK BIT(28)
+#define CCM_PLL_OUT_EN BIT(27)
+#define CCM_PLL1_UPDATE BIT(26)
#define CCM_PLL1_CTRL_P(p) ((p) << 16)
+#define CCM_PLL1_CTRL_N_MASK GENMASK(15, 8)
#define CCM_PLL1_CTRL_N(n) (((n) - 1) << 8)
+/* A523 CPU clock fields */
+#define CPU_CLK_SRC_HOSC (0 << 24)
+#define CPU_CLK_SRC_CPUPLL (3 << 24)
+#define CPU_CLK_CTRL_P(p) ((p) << 16)
+#define CPU_CLK_APB_DIV(n) (((n) - 1) << 8)
+#define CPU_CLK_PERI_DIV(m1) (((m1) - 1) << 2)
+#define CPU_CLK_AXI_DIV(m) (((m) - 1) << 0)
+
/* pll5 bit field */
-#define CCM_PLL5_CTRL_EN BIT(31)
-#define CCM_PLL5_LOCK_EN BIT(29)
-#define CCM_PLL5_LOCK BIT(28)
-#define CCM_PLL5_OUT_EN BIT(27)
#define CCM_PLL5_CTRL_N(n) (((n) - 1) << 8)
#define CCM_PLL5_CTRL_DIV1(div1) ((div1) << 0)
#define CCM_PLL5_CTRL_DIV2(div0) ((div0) << 1)
/* pll6 bit field */
-#define CCM_PLL6_CTRL_EN BIT(31)
-#define CCM_PLL6_LOCK_EN BIT(29)
-#define CCM_PLL6_LOCK BIT(28)
#define CCM_PLL6_CTRL_P0_SHIFT 16
#define CCM_PLL6_CTRL_P0_MASK (0x7 << CCM_PLL6_CTRL_P0_SHIFT)
#define CCM_PLL6_CTRL_N_SHIFT 8
@@ -97,6 +108,13 @@
#define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002
#define CCM_AHB3_DEFAULT 0x03000002
#define CCM_APB1_DEFAULT 0x03000102
+
+#elif CONFIG_MACH_SUN55I_A523 /* A523 */
+
+#define CCM_PLL6_DEFAULT 0xe8216310 /* 1200 MHz */
+#define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002 /* 200 MHz */
+#define CCM_APB1_DEFAULT 0x03000005 /* APB0 really */
+#define CCM_APB2_DEFAULT 0x03000005 /* APB1 really */
#endif
/* apb2 bit field */
@@ -116,6 +134,7 @@
/* MBUS clock bit field */
#define MBUS_ENABLE BIT(31)
#define MBUS_RESET BIT(30)
+#define MBUS_UPDATE BIT(27)
#define MBUS_CLK_SRC_MASK GENMASK(25, 24)
#define MBUS_CLK_SRC_OSCM24 (0 << 24)
#define MBUS_CLK_SRC_PLL6X2 (1 << 24)
@@ -128,10 +147,12 @@
#define GATE_SHIFT (0)
/* DRAM clock bit field */
+#define DRAM_CLK_ENABLE BIT(31)
#define DRAM_MOD_RESET BIT(30)
#define DRAM_CLK_UPDATE BIT(27)
#define DRAM_CLK_SRC_MASK GENMASK(25, 24)
#define DRAM_CLK_SRC_PLL5 (0 << 24)
+#define DRAM_CLK_M_MASK (0x1f)
#define DRAM_CLK_M(m) (((m)-1) << 0)
/* MMC clock bit field */
diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h b/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
index 908a582ae0f..bcfdc0a41c5 100644
--- a/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
+++ b/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
@@ -21,14 +21,34 @@
#define SUNXI_SID_BASE 0x03006200
#define SUNXI_GIC400_BASE 0x03020000
+#ifdef CONFIG_MACH_SUN55I_A523
+#define SUNXI_DRAM_COM_BASE 0x03120000
+#define SUNXI_DRAM_CTL0_BASE 0x03130000
+#define SUNXI_DRAM_PHY0_BASE 0x03140000
+#endif
+
#define SUNXI_MMC0_BASE 0x04020000
#define SUNXI_MMC1_BASE 0x04021000
#define SUNXI_MMC2_BASE 0x04022000
+#ifndef CONFIG_MACH_SUN55I_A523
#define SUNXI_R_CPUCFG_BASE 0x07000400
+#endif
#define SUNXI_PRCM_BASE 0x07010000
+#define SUNXI_R_WDOG_BASE 0x07020400
+#ifdef CONFIG_MACH_SUN55I_A523
+#define SUNXI_R_CPUCFG_BASE 0x07050000
+#endif
+#define SUNXI_R_TWI_BASE 0x07081400
+#define SUNXI_RTC_BASE 0x07090000
+#ifdef CONFIG_MACH_SUN55I_A523
+#define SUNXI_CPUCFG_BASE 0x08815000
+#else
#define SUNXI_CPUCFG_BASE 0x09010000
+#endif
+
+#define SUNXI_CPU_PLL_CFG_BASE 0x08817000
#ifndef __ASSEMBLY__
void sunxi_board_init(void);
diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h
index 0708ae3ee3b..0eccb1e6c28 100644
--- a/arch/arm/include/asm/arch-sunxi/dram.h
+++ b/arch/arm/include/asm/arch-sunxi/dram.h
@@ -35,6 +35,8 @@
#include <asm/arch/dram_sun50i_a133.h>
#elif defined(CONFIG_MACH_SUNIV)
#include <asm/arch/dram_suniv.h>
+#elif defined(CONFIG_MACH_SUN55I_A523)
+#include <asm/arch/dram_sun55i_a523.h>
#else
#include <asm/arch/dram_sun4i.h>
#endif
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h b/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
new file mode 100644
index 00000000000..08bfe462856
--- /dev/null
+++ b/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * t527 dram controller register and constant defines
+ *
+ * (C) Copyright 2024 Jernej Skrabec <jernej.skrabec@gmail.com>
+ */
+
+#ifndef _SUNXI_DRAM_SUN55I_A523_H
+#define _SUNXI_DRAM_SUN55I_A523_H
+
+#include <linux/bitops.h>
+
+enum sunxi_dram_type {
+ SUNXI_DRAM_TYPE_DDR3 = 3,
+ SUNXI_DRAM_TYPE_DDR4,
+ SUNXI_DRAM_TYPE_LPDDR3 = 7,
+ SUNXI_DRAM_TYPE_LPDDR4
+};
+
+#define MCTL_COM_UNK_008 0x008
+#define MCTL_COM_MAER0 0x020
+
+/*
+ * Controller registers seems to be the same or at least very similar
+ * to those in H6.
+ */
+struct sunxi_mctl_ctl_reg {
+ u32 mstr; /* 0x000 */
+ u32 statr; /* 0x004 unused */
+ u32 mstr1; /* 0x008 unused */
+ u32 clken; /* 0x00c */
+ u32 mrctrl0; /* 0x010 unused */
+ u32 mrctrl1; /* 0x014 unused */
+ u32 mrstatr; /* 0x018 unused */
+ u32 mrctrl2; /* 0x01c unused */
+ u32 derateen; /* 0x020 unused */
+ u32 derateint; /* 0x024 unused */
+ u8 reserved_0x028[8]; /* 0x028 */
+ u32 pwrctl; /* 0x030 */
+ u32 pwrtmg; /* 0x034 unused */
+ u32 hwlpctl; /* 0x038 */
+ u8 reserved_0x03c[20]; /* 0x03c */
+ u32 rfshctl0; /* 0x050 unused */
+ u32 rfshctl1; /* 0x054 unused */
+ u8 reserved_0x058[8]; /* 0x058 */
+ u32 rfshctl3; /* 0x060 */
+ u32 rfshtmg; /* 0x064 */
+ u8 reserved_0x068[104]; /* 0x068 */
+ u32 init[8]; /* 0x0d0 */
+ u32 dimmctl; /* 0x0f0 unused */
+ u32 rankctl; /* 0x0f4 */
+ u8 reserved_0x0f8[8]; /* 0x0f8 */
+ u32 dramtmg[17]; /* 0x100 */
+ u8 reserved_0x144[60]; /* 0x144 */
+ u32 zqctl[3]; /* 0x180 */
+ u32 zqstat; /* 0x18c unused */
+ u32 dfitmg0; /* 0x190 */
+ u32 dfitmg1; /* 0x194 */
+ u32 dfilpcfg[2]; /* 0x198 unused */
+ u32 dfiupd[3]; /* 0x1a0 */
+ u32 reserved_0x1ac; /* 0x1ac */
+ u32 dfimisc; /* 0x1b0 */
+ u32 dfitmg2; /* 0x1b4 unused */
+ u32 dfitmg3; /* 0x1b8 unused */
+ u32 dfistat; /* 0x1bc */
+ u32 dbictl; /* 0x1c0 */
+ u8 reserved_0x1c4[60]; /* 0x1c4 */
+ u32 addrmap[12]; /* 0x200 */
+ u8 reserved_0x230[16]; /* 0x230 */
+ u32 odtcfg; /* 0x240 */
+ u32 odtmap; /* 0x244 */
+ u8 reserved_0x248[8]; /* 0x248 */
+ u32 sched[2]; /* 0x250 */
+ u8 reserved_0x258[12]; /* 0x258 */
+ u32 unk_0x264; /* 0x264 */
+ u8 reserved_0x268[8]; /* 0x268 */
+ u32 unk_0x270; /* 0x270 */
+ u8 reserved_0x274[152]; /* 0x274 */
+ u32 dbgcmd; /* 0x30c unused */
+ u32 dbgstat; /* 0x310 unused */
+ u8 reserved_0x314[12]; /* 0x314 */
+ u32 swctl; /* 0x320 */
+ u32 swstat; /* 0x324 */
+ u8 reserved_0x328[7768];/* 0x328 */
+ u32 unk_0x2180; /* 0x2180 */
+ u8 reserved_0x2184[188];/* 0x2184 */
+ u32 unk_0x2240; /* 0x2240 */
+ u8 reserved_0x2244[3900];/* 0x2244 */
+ u32 unk_0x3180; /* 0x3180 */
+ u8 reserved_0x3184[188];/* 0x3184 */
+ u32 unk_0x3240; /* 0x3240 */
+ u8 reserved_0x3244[3900];/* 0x3244 */
+ u32 unk_0x4180; /* 0x4180 */
+ u8 reserved_0x4184[188];/* 0x4184 */
+ u32 unk_0x4240; /* 0x4240 */
+};
+check_member(sunxi_mctl_ctl_reg, swstat, 0x324);
+check_member(sunxi_mctl_ctl_reg, unk_0x4240, 0x4240);
+
+#define MSTR_DEVICETYPE_DDR3 BIT(0)
+#define MSTR_DEVICETYPE_LPDDR2 BIT(2)
+#define MSTR_DEVICETYPE_LPDDR3 BIT(3)
+#define MSTR_DEVICETYPE_DDR4 BIT(4)
+#define MSTR_DEVICETYPE_LPDDR4 BIT(5)
+#define MSTR_DEVICETYPE_MASK GENMASK(5, 0)
+#define MSTR_2TMODE BIT(10)
+#define MSTR_BUSWIDTH_FULL (0 << 12)
+#define MSTR_BUSWIDTH_HALF (1 << 12)
+#define MSTR_ACTIVE_RANKS(x) ((((x) == 2) ? 3 : 1) << 24)
+#define MSTR_BURST_LENGTH(x) (((x) >> 1) << 16)
+
+#define TPR10_CA_BIT_DELAY 0xffff0000
+#define TPR10_DX_BIT_DELAY0 BIT(17)
+#define TPR10_DX_BIT_DELAY1 BIT(18)
+#define TPR10_WRITE_LEVELING BIT(20)
+#define TPR10_READ_CALIBRATION BIT(21)
+#define TPR10_READ_TRAINING BIT(22)
+#define TPR10_WRITE_TRAINING BIT(23)
+
+struct dram_para {
+ enum sunxi_dram_type type;
+ u32 dx_odt;
+ u32 dx_dri;
+ u32 ca_dri;
+ u32 tpr0;
+ u32 tpr1;
+ u32 tpr2;
+ u32 tpr6;
+ u32 tpr10;
+};
+
+struct dram_config {
+ u8 cols;
+ u8 rows;
+ u8 ranks;
+ u8 bus_full_width;
+ u32 clk;
+ u32 odt_en;
+ u32 tpr11;
+ u32 tpr12;
+ u32 tpr14;
+};
+
+static inline int ns_to_t(int nanoseconds, u32 clk)
+{
+ const unsigned int ctrl_freq = clk / 2;
+
+ return DIV_ROUND_UP(ctrl_freq * nanoseconds, 1000);
+}
+
+void mctl_set_timing_params(u32 clk);
+
+#endif /* _SUNXI_DRAM_SUN55I_T527_H */
diff --git a/arch/arm/include/asm/arch-sunxi/watchdog.h b/arch/arm/include/asm/arch-sunxi/watchdog.h
index 38e2ef2aca3..14a6e89ccfa 100644
--- a/arch/arm/include/asm/arch-sunxi/watchdog.h
+++ b/arch/arm/include/asm/arch-sunxi/watchdog.h
@@ -12,6 +12,8 @@
#define WDT_CTRL_RESTART (0x1 << 0)
#define WDT_CTRL_KEY (0x0a57 << 1)
+#define WDT_SRST_REG 0x08
+
#if defined(CONFIG_MACH_SUN4I) || \
defined(CONFIG_MACH_SUN5I) || \
defined(CONFIG_MACH_SUN7I) || \
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 0a7c029b15a..6a511c4fd39 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -57,7 +57,12 @@ config DRAM_SUN50I_A133
Select this dram controller driver for some sun50i platforms,
like A133.
-if DRAM_SUN50I_H616 || DRAM_SUN50I_A133
+config DRAM_SUN55I_A523
+ bool
+ help
+ Select this DRAM controller driver for A523/T527 SoCs.
+
+if DRAM_SUN50I_H616 || DRAM_SUN50I_A133 || DRAM_SUN55I_A523
config DRAM_SUNXI_DX_ODT
hex "DRAM DX ODT parameter"
help
@@ -170,8 +175,8 @@ config DRAM_SUNXI_TPR13
config DRAM_SUNXI_TPR14
hex "DRAM TPR14 parameter"
- depends on DRAM_SUN50I_A133
- default 0x0
+ depends on DRAM_SUN50I_A133 || MACH_SUN55I_A523
+ default 0x48484848
help
TPR14 value from vendor DRAM settings.
@@ -209,6 +214,7 @@ config AXP_PMIC_BUS
config SUNXI_SRAM_ADDRESS
hex
default 0x10000 if MACH_SUN9I || MACH_SUN50I || MACH_SUN50I_H5
+ default 0x44000 if MACH_SUN55I_A523
default 0x20000 if SUN50I_GEN_H6 || SUNXI_GEN_NCAT2
default 0x0
---help---
@@ -221,6 +227,7 @@ config SUNXI_RVBAR_ADDRESS
hex
depends on ARM64
default 0x08100040 if MACH_SUN50I_A133
+ default 0x08000040 if MACH_SUN55I_A523
default 0x09010040 if SUN50I_GEN_H6
default 0x017000a0
---help---
@@ -249,6 +256,7 @@ config SUNXI_BL31_BASE
default 0x00044000 if MACH_SUN50I || MACH_SUN50I_H5
default 0x40000000 if MACH_SUN50I_H616
default 0x00104000 if SUN50I_GEN_H6
+ default 0x00054000 if MACH_SUN55I_A523
default 0x0
help
Address where BL31 (TF-A) is loaded, or zero if BL31 is not used.
@@ -330,7 +338,7 @@ config MACH_SUNXI_H3_H5
# TODO: try out A80's 8GiB DRAM space
config SUNXI_DRAM_MAX_SIZE
hex
- default 0x100000000 if MACH_SUN50I_H616 || MACH_SUN50I_A133
+ default 0x100000000 if MACH_SUN50I_H616 || MACH_SUN50I_A133 || MACH_SUN55I_A523
default 0xC0000000 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN50I_H6
default 0x80000000
@@ -532,6 +540,16 @@ config MACH_SUN50I_A133
select SUN50I_GEN_H6
imply OF_UPSTREAM
+config MACH_SUN55I_A523
+ bool "sun55i (Allwinner A523/A527/T527/H728)"
+ select ARM64
+ select SUNXI_GEN_NCAT2
+ select SUNXI_NEW_PINCTRL
+ select DRAM_SUN55I_A523
+ select FIT
+ select SPL_LOAD_FIT if SPL
+ imply OF_UPSTREAM
+
endchoice
# The sun8i SoCs share a lot, this helps to avoid a lot of "if A23 || A33"
@@ -569,7 +587,7 @@ config ARM_BOOT_HOOK_RMR
This allows both the SPL and the U-Boot proper to be entered in
either mode and switch to AArch64 if needed.
-if SUNXI_DRAM_DW || DRAM_SUN50I_H6 || DRAM_SUN50I_H616 || DRAM_SUN50I_A133
+if SUNXI_DRAM_DW || DRAM_SUN50I_H6 || DRAM_SUN50I_H616 || DRAM_SUN50I_A133 || DRAM_SUN55I_A523
config SUNXI_DRAM_DDR3
bool
@@ -587,6 +605,7 @@ config SUNXI_DRAM_DDR4
choice
prompt "DRAM Type and Timing"
+ default SUNXI_DRAM_A523_LPDDR4 if MACH_SUN55I_A523
default SUNXI_DRAM_DDR3_1333 if !MACH_SUN8I_V3S
default SUNXI_DRAM_DDR2_V3S if MACH_SUN8I_V3S
@@ -670,6 +689,21 @@ config SUNXI_DRAM_DDR2_V3S
This option is only for the DDR2 memory chip which is co-packaged in
Allwinner V3s SoC.
+config SUNXI_DRAM_A523_DDR3
+ bool "DDR3 DRAM chips on the A523/T527 DRAM controller"
+ select SUNXI_DRAM_DDR3
+ depends on DRAM_SUN55I_A523
+ help
+ This option is the DDR3 timing used by the stock boot0 by
+ Allwinner.
+
+config SUNXI_DRAM_A523_LPDDR4
+ bool "LPDDR4 DRAM chips on the A523/T527 DRAM controller"
+ select SUNXI_DRAM_LPDDR4
+ depends on DRAM_SUN55I_A523
+ help
+ This option is the LPDDR4 timing used by the stock boot0 by
+ Allwinner.
endchoice
endif
@@ -690,6 +724,7 @@ config DRAM_CLK
default 672 if MACH_SUN50I
default 744 if MACH_SUN50I_H6
default 720 if MACH_SUN50I_H616 || MACH_SUN50I_A133
+ default 1200 if MACH_SUN55I_A523
---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
@@ -706,7 +741,9 @@ endif
config DRAM_ZQ
int "sunxi dram zq value"
- depends on !MACH_SUN50I_H616 && !MACH_SUN50I_A133
+ depends on !MACH_SUN50I_H616
+ depends on !MACH_SUN50I_A133
+ depends on !MACH_SUN55I_A523
default 123 if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I || \
MACH_SUN8I_A23 || MACH_SUN8I_A33 || MACH_SUN8I_A83T
default 127 if MACH_SUN7I
@@ -720,6 +757,7 @@ config DRAM_ZQ
config DRAM_ODT_EN
bool "sunxi dram odt enable"
depends on !MACH_SUN50I_H616
+ depends on !MACH_SUN55I_A523
default y if MACH_SUN8I_A23
default y if MACH_SUNXI_H3_H5
default y if MACH_SUN8I_R40
@@ -809,6 +847,7 @@ endif
config SYS_CLK_FREQ
default 408000000 if MACH_SUNIV
+ default 792000000 if MACH_SUN55I_A523
default 816000000 if MACH_SUN50I || MACH_SUN50I_H5
default 888000000 if MACH_SUN50I_H6
default 912000000 if MACH_SUN7I
@@ -827,6 +866,7 @@ config SYS_CONFIG_NAME
default "sun50i" if MACH_SUN50I_H6
default "sun50i" if MACH_SUN50I_H616
default "sun50i" if MACH_SUN50I_A133
+ default "sun55i" if MACH_SUN55I_A523
config SYS_BOARD
default "sunxi"
@@ -893,7 +933,7 @@ config I2C1_ENABLE
---help---
See I2C0_ENABLE help text.
-if SUNXI_GEN_SUN6I || SUN50I_GEN_H6
+if SUNXI_GEN_SUN6I || SUN50I_GEN_H6 || SUNXI_GEN_NCAT2
config R_I2C_ENABLE
bool "Enable the PRCM I2C/TWI controller"
# This is used for the pmic on H3
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index 8eff20b77bf..579530f27e3 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -47,4 +47,6 @@ obj-$(CONFIG_DRAM_SUN50I_H616) += dram_sun50i_h616.o dram_dw_helpers.o
obj-$(CONFIG_DRAM_SUN50I_H616) += dram_timings/
obj-$(CONFIG_DRAM_SUN50I_A133) += dram_sun50i_a133.o
obj-$(CONFIG_DRAM_SUN50I_A133) += dram_timings/
+obj-$(CONFIG_MACH_SUN55I_A523) += dram_sun55i_a523.o
+obj-$(CONFIG_DRAM_SUN55I_A523) += dram_timings/
endif
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 08d55b3a0e3..fb4837c2082 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -141,6 +141,10 @@ static int gpio_init(void)
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_H616_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN50I_H616_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP);
+#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN55I_A523)
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(9), 2);
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(10), 2);
+ sunxi_gpio_set_pull(SUNXI_GPB(10), 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);
@@ -197,6 +201,7 @@ static int gpio_init(void)
if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
IS_ENABLED(CONFIG_SUN50I_GEN_NCAT2)) {
val = readl(SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_VAL);
+ /* TODO: A523: keep only the lower two bits? */
writel(val, SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_SEL);
}
if (IS_ENABLED(CONFIG_SUN50I_GEN_H6)) {
@@ -502,6 +507,12 @@ void reset_cpu(void)
/* sun5i sometimes gets stuck without this */
writel(WDT_MODE_RESET_EN | WDT_MODE_EN, &wdog->mode);
}
+#elif defined(CONFIG_MACH_SUN55I_A523)
+ static const void *wdog = (void *)SUNXI_TIMER_BASE;
+
+ writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, wdog + WDT_SRST_REG);
+ while (1)
+ ;
#elif defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
#if defined(CONFIG_MACH_SUN50I_H6)
/* WDOG is broken for some H6 rev. use the R_WDOG instead */
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index 3f375a51965..80004f13a1e 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -1,7 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clock.h>
#include <asm/arch/prcm.h>
+#include <linux/delay.h>
+
+#ifndef SUNXI_CPU_PLL_CFG_BASE
+#define SUNXI_CPU_PLL_CFG_BASE 0
+#endif
#ifdef CONFIG_XPL_BUILD
void clock_init_safe(void)
@@ -9,15 +16,22 @@ void clock_init_safe(void)
void *const ccm = (void *)SUNXI_CCM_BASE;
void *const prcm = (void *)SUNXI_PRCM_BASE;
- if (IS_ENABLED(CONFIG_MACH_SUN50I_H616)) {
- /* this seems to enable PLLs on H616 */
+ if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 0x10);
+ if (IS_ENABLED(CONFIG_MACH_SUN55I_A523))
+ setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 0x200);
+ udelay(1);
+
+ if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
+ IS_ENABLED(CONFIG_MACH_SUN55I_A523))
setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 2);
- }
+ udelay(1);
if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
- IS_ENABLED(CONFIG_MACH_SUN50I_H6)) {
+ IS_ENABLED(CONFIG_MACH_SUN50I_H6) ||
+ IS_ENABLED(CONFIG_MACH_SUN55I_A523)) {
clrbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 1);
+ udelay(1);
setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 1);
}
@@ -31,12 +45,13 @@ void clock_init_safe(void)
clock_set_pll1(408000000);
writel(CCM_PLL6_DEFAULT, ccm + CCU_H6_PLL6_CFG);
- while (!(readl(ccm + CCU_H6_PLL6_CFG) & CCM_PLL6_LOCK))
+ while (!(readl(ccm + CCU_H6_PLL6_CFG) & CCM_PLL_LOCK))
;
- clrsetbits_le32(ccm + CCU_H6_CPU_AXI_CFG,
- CCM_CPU_AXI_APB_MASK | CCM_CPU_AXI_AXI_MASK,
- CCM_CPU_AXI_DEFAULT_FACTORS);
+ if (!IS_ENABLED(CONFIG_MACH_SUN55I_A523))
+ clrsetbits_le32(ccm + CCU_H6_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 + CCU_H6_PSI_AHB1_AHB2_CFG);
#ifdef CCM_AHB3_DEFAULT
@@ -48,7 +63,15 @@ void clock_init_safe(void)
* 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 + CCU_H6_MBUS_CFG);
+ if (IS_ENABLED(CONFIG_MACH_SUN55I_A523)) {
+ writel(MBUS_RESET, ccm + CCU_H6_MBUS_CFG);
+ udelay(1);
+ writel(MBUS_UPDATE | MBUS_CLK_SRC_OSCM24 | MBUS_CLK_M(4),
+ ccm + CCU_H6_MBUS_CFG);
+ } else {
+ writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3),
+ ccm + CCU_H6_MBUS_CFG);
+ }
}
void clock_init_uart(void)
@@ -70,38 +93,118 @@ void clock_init_uart(void)
1 << (RESET_SHIFT + CONFIG_CONS_INDEX - 1));
}
-void clock_set_pll1(unsigned int clk)
+static bool has_pll_output_gate(void)
{
- void *const ccm = (void *)SUNXI_CCM_BASE;
+ return (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2) ||
+ IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
+ IS_ENABLED(CONFIG_MACH_SUN50I_A133));
+}
+
+/* A shared routine to program the CPU PLLs for H6, H616, T113, A523 */
+static void clock_set_pll(u32 *reg, unsigned int n)
+{
+ u32 val = readl(reg);
+
+ /* clear the lock enable bit */
+ val &= ~CCM_PLL_LOCK_EN;
+ writel(val, reg);
+
+ /* gate the output on the newer SoCs */
+ if (has_pll_output_gate()) {
+ val &= ~CCM_PLL_OUT_EN;
+ writel(val, reg);
+ }
+
+ val &= ~(CCM_PLL1_CTRL_N_MASK | GENMASK(3, 0) | GENMASK(21, 16));
+ val |= CCM_PLL1_CTRL_N(n);
+ writel(val, reg); /* program parameter */
+
+ val |= CCM_PLL_CTRL_EN;
+ if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
+ val |= CCM_PLL_LDO_EN;
+ writel(val, reg); /* enable PLL */
+
+ val |= CCM_PLL_LOCK_EN;
+ if (IS_ENABLED(CONFIG_MACH_SUN55I_A523))
+ val |= CCM_PLL1_UPDATE;
+ writel(val, reg); /* start locking process */
+
+ while (!(readl(reg) & CCM_PLL_LOCK)) { /* wait for lock bit */
+ }
+ udelay(20); /* wait as per manual */
+
+ /* un-gate the output on the newer SoCs */
+ if (has_pll_output_gate()) {
+ val |= CCM_PLL_OUT_EN;
+ writel(val, reg);
+ }
+}
+
+/* Program the PLLs for both clusters plus the DSU. */
+static void clock_a523_set_cpu_plls(unsigned int n_factor)
+{
+ void *const cpc = (void *)SUNXI_CPU_PLL_CFG_BASE;
u32 val;
- /* Do not support clocks < 288MHz as they need factor P */
- if (clk < 288000000) clk = 288000000;
+ val = CPU_CLK_SRC_HOSC | CPU_CLK_CTRL_P(0) |
+ CPU_CLK_APB_DIV(4) | CPU_CLK_PERI_DIV(2) |
+ CPU_CLK_AXI_DIV(2);
+
+ /* Switch CPU clock source to 24MHz HOSC while changing the PLL */
+ writel(val, cpc + CPC_CPUA_CLK_REG);
+ writel(val, cpc + CPC_CPUB_CLK_REG);
+ udelay(20);
+ writel(CPU_CLK_SRC_HOSC | CPU_CLK_CTRL_P(0),
+ cpc + CPC_DSU_CLK_REG);
+ udelay(20);
- /* Switch to 24MHz clock while changing PLL1 */
+ clock_set_pll(cpc + CPC_CPUA_PLL_CTRL, n_factor);
+ clock_set_pll(cpc + CPC_CPUB_PLL_CTRL, n_factor);
+ clock_set_pll(cpc + CPC_DSU_PLL_CTRL, n_factor);
+
+ /* Switch CPU clock source to the CPU PLL */
+ clrsetbits_le32(cpc + CPC_CPUA_CLK_REG, CPU_CLK_SRC_HOSC,
+ CPU_CLK_SRC_CPUPLL);
+ clrsetbits_le32(cpc + CPC_CPUB_CLK_REG, CPU_CLK_SRC_HOSC,
+ CPU_CLK_SRC_CPUPLL);
+ clrsetbits_le32(cpc + CPC_DSU_CLK_REG, CPU_CLK_SRC_HOSC,
+ CPU_CLK_SRC_CPUPLL);
+}
+
+static void clock_h6_set_cpu_pll(unsigned int n_factor)
+{
+ void *const ccm = (void *)SUNXI_CCM_BASE;
+ u32 val;
+
+ /* Switch CPU clock source to 24MHz HOSC while changing the PLL */
val = readl(ccm + CCU_H6_CPU_AXI_CFG);
val &= ~CCM_CPU_AXI_MUX_MASK;
val |= CCM_CPU_AXI_MUX_OSC24M;
writel(val, ccm + CCU_H6_CPU_AXI_CFG);
- /* clk = 24*n/p, p is ignored if clock is >288MHz */
- val = CCM_PLL1_CTRL_EN | CCM_PLL1_LOCK_EN | CCM_PLL1_CLOCK_TIME_2;
- val |= CCM_PLL1_CTRL_N(clk / 24000000);
- if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
- IS_ENABLED(CONFIG_MACH_SUN50I_A133))
- val |= CCM_PLL1_OUT_EN;
- if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
- val |= CCM_PLL1_OUT_EN | CCM_PLL1_LDO_EN;
- writel(val, ccm + CCU_H6_PLL1_CFG);
- while (!(readl(ccm + CCU_H6_PLL1_CFG) & CCM_PLL1_LOCK)) {}
+ clock_set_pll(ccm + CCU_H6_PLL1_CFG, n_factor);
- /* Switch CPU to PLL1 */
+ /* Switch CPU clock source to the CPU PLL */
val = readl(ccm + CCU_H6_CPU_AXI_CFG);
val &= ~CCM_CPU_AXI_MUX_MASK;
val |= CCM_CPU_AXI_MUX_PLL_CPUX;
writel(val, ccm + CCU_H6_CPU_AXI_CFG);
}
+void clock_set_pll1(unsigned int clk)
+{
+ /* Do not support clocks < 288MHz as they need factor P */
+ if (clk < 288000000)
+ clk = 288000000;
+
+ clk /= 24000000;
+
+ if (IS_ENABLED(CONFIG_MACH_SUN55I_A523))
+ clock_a523_set_cpu_plls(clk);
+ else
+ clock_h6_set_cpu_pll(clk);
+}
+
int clock_twi_onoff(int port, int state)
{
void *const ccm = (void *)SUNXI_CCM_BASE;
diff --git a/arch/arm/mach-sunxi/cpu_info.c b/arch/arm/mach-sunxi/cpu_info.c
index 3f4735d4717..c3a51d9956e 100644
--- a/arch/arm/mach-sunxi/cpu_info.c
+++ b/arch/arm/mach-sunxi/cpu_info.c
@@ -106,6 +106,8 @@ int print_cpuinfo(void)
puts("CPU: Allwinner H616 (SUN50I)\n");
#elif defined CONFIG_MACH_SUN50I_A133
puts("CPU: Allwinner A133 (SUN50I)\n");
+#elif defined CONFIG_MACH_SUN55I_A523
+ puts("CPU: Allwinner A523 (SUN55I)\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_a133.c b/arch/arm/mach-sunxi/dram_sun50i_a133.c
index a0fca3738f4..3a231141168 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_a133.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_a133.c
@@ -78,19 +78,19 @@ static void mctl_clk_init(u32 clk)
clrbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_RESET);
clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(GATE_SHIFT));
clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(RESET_SHIFT));
- clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL5_CTRL_EN);
+ clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL_CTRL_EN);
clrbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, DRAM_MOD_RESET);
udelay(5);
/* Set up PLL5 clock, used for DRAM */
clrsetbits_le32(ccm + CCU_H6_PLL5_CFG, 0xff03,
- CCM_PLL5_CTRL_N((clk * 2) / 24) | CCM_PLL5_CTRL_EN);
+ CCM_PLL5_CTRL_N((clk * 2) / 24) | CCM_PLL_CTRL_EN);
setbits_le32(ccm + CCU_H6_PLL5_CFG, BIT(24));
clrsetbits_le32(ccm + CCU_H6_PLL5_CFG, 0x3,
- CCM_PLL5_LOCK_EN | CCM_PLL5_CTRL_EN | BIT(30));
- clrbits_le32(ccm + CCU_H6_PLL5_CFG, 0x3 | BIT(30));
+ CCM_PLL_LOCK_EN | CCM_PLL_CTRL_EN | CCM_PLL_LDO_EN);
+ clrbits_le32(ccm + CCU_H6_PLL5_CFG, 0x3 | CCM_PLL_LDO_EN);
mctl_await_completion(ccm + CCU_H6_PLL5_CFG,
- CCM_PLL5_LOCK, CCM_PLL5_LOCK);
+ CCM_PLL_LOCK, CCM_PLL_LOCK);
/* Enable DRAM clock and gate*/
clrbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, BIT(24) | BIT(25));
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c
index 84fd64a2bfc..ea26e6dd327 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c
@@ -167,16 +167,16 @@ static void mctl_sys_init(u32 clk_rate)
clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(0));
udelay(5);
writel(0, ccm + CCU_H6_DRAM_GATE_RESET);
- clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL5_CTRL_EN);
+ clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL_CTRL_EN);
clrbits_le32(ccm + CCU_H6_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 |
+ writel(CCM_PLL_CTRL_EN | CCM_PLL_LOCK_EN |
CCM_PLL5_CTRL_N(clk_rate * 2 / 24), ccm + CCU_H6_PLL5_CFG);
mctl_await_completion(ccm + CCU_H6_PLL5_CFG,
- CCM_PLL5_LOCK, CCM_PLL5_LOCK);
+ CCM_PLL_LOCK, CCM_PLL_LOCK);
/* Configure DRAM mod clock */
writel(DRAM_CLK_SRC_PLL5, ccm + CCU_H6_DRAM_CLK_CFG);
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c
index 5a59f82d1ef..877181016f3 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h616.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c
@@ -106,16 +106,16 @@ static void mctl_sys_init(u32 clk_rate)
clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(GATE_SHIFT));
udelay(5);
clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(RESET_SHIFT));
- clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL5_CTRL_EN);
+ clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL_CTRL_EN);
clrbits_le32(ccm + CCU_H6_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_OUT_EN |
+ writel(CCM_PLL_CTRL_EN | CCM_PLL_LOCK_EN | CCM_PLL_OUT_EN |
CCM_PLL5_CTRL_N(clk_rate * 2 / 24), ccm + CCU_H6_PLL5_CFG);
mctl_await_completion(ccm + CCU_H6_PLL5_CFG,
- CCM_PLL5_LOCK, CCM_PLL5_LOCK);
+ CCM_PLL_LOCK, CCM_PLL_LOCK);
/* Configure DRAM mod clock */
writel(DRAM_CLK_SRC_PLL5, ccm + CCU_H6_DRAM_CLK_CFG);
diff --git a/arch/arm/mach-sunxi/dram_sun55i_a523.c b/arch/arm/mach-sunxi/dram_sun55i_a523.c
new file mode 100644
index 00000000000..30bbeb40d0b
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_sun55i_a523.c
@@ -0,0 +1,1590 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * sun55i A523/A527/T527/H728 platform DRAM controller driver
+ *
+ * This driver supports DDR3 and LPDDR4 memory.
+ *
+ * (C) Copyright 2024 Jernej Skrabec <jernej.skrabec@gmail.com>
+ *
+ */
+#include <init.h>
+#include <log.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/dram.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/prcm.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+static void mctl_sys_init(u32 clk_rate)
+{
+ void * const ccm = (void *)SUNXI_CCM_BASE;
+
+ /* Put all DRAM-related blocks to reset state */
+ clrbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_ENABLE);
+ clrbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_RESET);
+ setbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_UPDATE);
+ clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(GATE_SHIFT));
+ udelay(5);
+ clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(RESET_SHIFT));
+ clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL_CTRL_EN);
+ clrsetbits_le32(ccm + CCU_H6_DRAM_CLK_CFG,
+ DRAM_CLK_ENABLE, DRAM_CLK_UPDATE);
+
+ udelay(5);
+
+ /* Set PLL5 rate to doubled DRAM clock rate */
+ writel(CCM_PLL_CTRL_EN | CCM_PLL_LDO_EN | CCM_PLL_LOCK_EN |
+ CCM_PLL_OUT_EN | CCM_PLL5_CTRL_N(clk_rate * 2 / 24),
+ ccm + CCU_H6_PLL5_CFG);
+ mctl_await_completion(ccm + CCU_H6_PLL5_CFG,
+ CCM_PLL_LOCK, CCM_PLL_LOCK);
+
+ /* Configure DRAM mod clock */
+ writel(DRAM_CLK_SRC_PLL5, ccm + CCU_H6_DRAM_CLK_CFG);
+ writel(BIT(RESET_SHIFT), ccm + CCU_H6_DRAM_GATE_RESET);
+ udelay(5);
+ setbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(GATE_SHIFT));
+
+ /* Configure MBUS and enable DRAM clock */
+ setbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_RESET | MBUS_UPDATE);
+ setbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_ENABLE | MBUS_UPDATE);
+
+ clrsetbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, DRAM_CLK_M_MASK,
+ DRAM_CLK_ENABLE | DRAM_CLK_UPDATE | DRAM_CLK_M(4));
+ udelay(5);
+}
+
+static void mctl_set_addrmap(const struct dram_config *config)
+{
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ u8 cols = config->cols;
+ u8 rows = config->rows;
+ u8 ranks = config->ranks;
+
+ if (!config->bus_full_width)
+ cols -= 1;
+
+ /* Ranks */
+ if (ranks == 2)
+ mctl_ctl->addrmap[0] = 0x1F00 | (rows + cols - 3);
+ else
+ mctl_ctl->addrmap[0] = 0x1F1F;
+
+ /* 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 7:
+ mctl_ctl->addrmap[3] = 0x1F1F1F00;
+ mctl_ctl->addrmap[4] = 0x1F1F;
+ break;
+ 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;
+}
+
+#define MASK_BYTE(reg, nr) (((reg) >> ((nr) * 8)) & 0x1f)
+static void mctl_phy_configure_odt(const struct dram_para *para)
+{
+ u32 val_lo, val_hi;
+
+ val_hi = para->dx_dri;
+ val_lo = (para->type != SUNXI_DRAM_TYPE_LPDDR4) ? para->dx_dri :
+ (para->tpr1 & 0x1f1f1f1f) ? para->tpr1 : 0x04040404;
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x304, 0x1f1f0000,
+ (MASK_BYTE(val_hi, 0) << 24) |
+ (MASK_BYTE(val_lo, 0) << 16));
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x484, 0x1f1f0000,
+ (MASK_BYTE(val_hi, 1) << 24) |
+ (MASK_BYTE(val_lo, 1) << 16));
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x604, 0x1f1f0000,
+ (MASK_BYTE(val_hi, 2) << 24) |
+ (MASK_BYTE(val_lo, 2) << 16));
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x784, 0x1f1f0000,
+ (MASK_BYTE(val_hi, 3) << 24) |
+ (MASK_BYTE(val_lo, 3) << 16));
+
+ val_lo = para->ca_dri;
+ val_hi = para->ca_dri;
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0xf4, 0x1f1f1f1f,
+ (MASK_BYTE(val_hi, 0) << 24) |
+ (MASK_BYTE(val_lo, 0) << 16) |
+ (MASK_BYTE(val_hi, 1) << 8) |
+ (MASK_BYTE(val_lo, 1)));
+
+ val_hi = para->dx_odt;
+ val_lo = (para->type == SUNXI_DRAM_TYPE_LPDDR4) ? 0 : para->dx_odt;
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x304, 0x00001f1f,
+ (MASK_BYTE(val_hi, 0) << 8) | MASK_BYTE(val_lo, 0));
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x484, 0x00001f1f,
+ (MASK_BYTE(val_hi, 1) << 8) | MASK_BYTE(val_lo, 1));
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x604, 0x00001f1f,
+ (MASK_BYTE(val_hi, 2) << 8) | MASK_BYTE(val_lo, 2));
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x784, 0x00001f1f,
+ (MASK_BYTE(val_hi, 3) << 8) | MASK_BYTE(val_lo, 3));
+}
+
+static bool mctl_phy_write_leveling(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ u32 mr2, low, high, val = 0;
+ bool result = true;
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0xf00, 0xe00);
+
+ if (para->type == SUNXI_DRAM_TYPE_LPDDR4) {
+ if (config->clk <= 936)
+ mr2 = 0x1b;
+ else if (config->clk <= 1200)
+ mr2 = 0x2d;
+ else
+ mr2 = 0x36;
+ writeb(mr2, SUNXI_DRAM_PHY0_BASE + 3);
+ }
+
+ low = readw(SUNXI_DRAM_PHY0_BASE + 2) | 4;
+ high = readw(SUNXI_DRAM_PHY0_BASE + 4);
+ writew(low, SUNXI_DRAM_PHY0_BASE + 2);
+ writew(high, SUNXI_DRAM_PHY0_BASE + 4);
+
+ if (config->bus_full_width)
+ val = 0xf;
+ else
+ val = 3;
+
+ mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x62), val, val);
+
+ low = readw(SUNXI_DRAM_PHY0_BASE + 2) & 0xfffb;
+ high = readw(SUNXI_DRAM_PHY0_BASE + 4);
+ writew(low, SUNXI_DRAM_PHY0_BASE + 2);
+ writew(high, SUNXI_DRAM_PHY0_BASE + 4);
+
+ val = readl(SUNXI_DRAM_PHY0_BASE + 0x96);
+ if (val == 0 || val == 0x3f)
+ result = false;
+ val = readl(SUNXI_DRAM_PHY0_BASE + 0x97); //TODO: ???
+ if (val == 0 || val == 0x3f)
+ result = false;
+ val = readl(SUNXI_DRAM_PHY0_BASE + 0xc6);
+ if (val == 0 || val == 0x3f)
+ result = false;
+ val = readl(SUNXI_DRAM_PHY0_BASE + 0xc7); //TODO: ???
+ if (val == 0 || val == 0x3f)
+ result = false;
+
+ low = readw(SUNXI_DRAM_PHY0_BASE + 2) & 0xff3f;
+ high = readw(SUNXI_DRAM_PHY0_BASE + 4);
+ writew(low, SUNXI_DRAM_PHY0_BASE + 2);
+ writew(high, SUNXI_DRAM_PHY0_BASE + 4);
+
+ if (config->ranks == 2) {
+ low = (readw(SUNXI_DRAM_PHY0_BASE + 2) & 0xff3f) | 0x40;
+ high = readw(SUNXI_DRAM_PHY0_BASE + 4);
+ writew(low, SUNXI_DRAM_PHY0_BASE + 2);
+ writew(high, SUNXI_DRAM_PHY0_BASE + 4);
+
+ low = readw(SUNXI_DRAM_PHY0_BASE + 2) | 4;
+ high = readw(SUNXI_DRAM_PHY0_BASE + 4);
+ writew(low, SUNXI_DRAM_PHY0_BASE + 2);
+ writew(high, SUNXI_DRAM_PHY0_BASE + 4);
+
+ if (config->bus_full_width)
+ val = 0xf;
+ else
+ val = 3;
+
+ mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x62), val, val);
+
+ low = readw(SUNXI_DRAM_PHY0_BASE + 2) & 0xfffb;
+ high = readw(SUNXI_DRAM_PHY0_BASE + 4);
+ writew(low, SUNXI_DRAM_PHY0_BASE + 2);
+ writew(high, SUNXI_DRAM_PHY0_BASE + 4);
+ }
+
+ low = readw(SUNXI_DRAM_PHY0_BASE + 2) & 0xff3f;
+ high = readw(SUNXI_DRAM_PHY0_BASE + 4);
+ writew(low, SUNXI_DRAM_PHY0_BASE + 2);
+ writew(high, SUNXI_DRAM_PHY0_BASE + 4);
+
+ return result;
+}
+
+static bool mctl_phy_read_calibration(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ bool result = true;
+ u32 val;
+
+ if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x44, 0x20000000);
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0x3c, 0x38);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 1);
+
+ if (config->bus_full_width)
+ val = 0xf;
+ else
+ val = 3;
+
+ while ((readl(SUNXI_DRAM_PHY0_BASE + 0x20c) & val) != val) {
+ if (readl(SUNXI_DRAM_PHY0_BASE + 0x20c) & 0x20) {
+ result = false;
+ break;
+ }
+ }
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 1);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0x3c);
+
+ if (config->ranks == 2) {
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0x3c, 0x34);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 1);
+
+ while ((readl(SUNXI_DRAM_PHY0_BASE + 0x20c) & val) != val) {
+ if (readl(SUNXI_DRAM_PHY0_BASE + 0x20c) & 0x20) {
+ result = false;
+ break;
+ }
+ }
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 1);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0x3c);
+ }
+
+ return result;
+}
+
+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 + 0x200);
+ writeb(0, SUNXI_DRAM_PHY0_BASE + 0x207);
+ writeb(0, SUNXI_DRAM_PHY0_BASE + 0x208);
+ writeb(0, SUNXI_DRAM_PHY0_BASE + 0x209);
+ writeb(0, SUNXI_DRAM_PHY0_BASE + 0x20a);
+ }
+
+ 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);
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0xa04, 0x3f, 0xf);
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0xa08, 0x3f, 0xf);
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 6);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 1);
+
+ mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x840), 0xc, 0xc);
+ if (readl(SUNXI_DRAM_PHY0_BASE + 0x840) & 3)
+ result = false;
+
+ if (config->bus_full_width) {
+ mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0xa40), 0xc, 0xc);
+ if (readl(SUNXI_DRAM_PHY0_BASE + 0xa40) & 3)
+ result = false;
+ }
+
+ ptr1 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x898);
+ ptr2 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x850);
+ for (i = 0; i < 9; i++) {
+ val1 = readl(&ptr1[i]);
+ val2 = readl(&ptr2[i]);
+ if (val1 - val2 <= 6)
+ result = false;
+ }
+ ptr1 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x8bc);
+ ptr2 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x874);
+ for (i = 0; i < 9; i++) {
+ val1 = readl(&ptr1[i]);
+ val2 = readl(&ptr2[i]);
+ if (val1 - val2 <= 6)
+ result = false;
+ }
+
+ if (config->bus_full_width) {
+ ptr1 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xa98);
+ ptr2 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xa50);
+ for (i = 0; i < 9; i++) {
+ val1 = readl(&ptr1[i]);
+ val2 = readl(&ptr2[i]);
+ if (val1 - val2 <= 6)
+ result = false;
+ }
+
+ ptr1 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xabc);
+ ptr2 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xa74);
+ for (i = 0; i < 9; i++) {
+ val1 = readl(&ptr1[i]);
+ val2 = readl(&ptr2[i]);
+ if (val1 - val2 <= 6)
+ result = false;
+ }
+ }
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 3);
+
+ if (config->ranks == 2) {
+ /* maybe last parameter should be 1? */
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 3, 2);
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 6);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 1);
+
+ mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x840), 0xc, 0xc);
+ if (readl(SUNXI_DRAM_PHY0_BASE + 0x840) & 3)
+ result = false;
+
+ if (config->bus_full_width) {
+ mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0xa40), 0xc, 0xc);
+ if (readl(SUNXI_DRAM_PHY0_BASE + 0xa40) & 3)
+ result = false;
+ }
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 3);
+ }
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 3);
+
+ return result;
+}
+
+static bool mctl_phy_write_training(const struct dram_config *config)
+{
+ u32 val1, val2, *ptr1, *ptr2;
+ bool result = true;
+ int i;
+
+ writel(0, SUNXI_DRAM_PHY0_BASE + 0x134);
+ writel(0, SUNXI_DRAM_PHY0_BASE + 0x138);
+ writel(0, SUNXI_DRAM_PHY0_BASE + 0x19c);
+ writel(0, SUNXI_DRAM_PHY0_BASE + 0x1a0);
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 0xc, 8);
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x10);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x20);
+
+ mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x8e0), 3, 3);
+ if (readl(SUNXI_DRAM_PHY0_BASE + 0x8e0) & 0xc)
+ result = false;
+
+ if (config->bus_full_width) {
+ mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0xae0), 3, 3);
+ if (readl(SUNXI_DRAM_PHY0_BASE + 0xae0) & 0xc)
+ result = false;
+ }
+
+ ptr1 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x938);
+ ptr2 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x8f0);
+ for (i = 0; i < 9; i++) {
+ val1 = readl(&ptr1[i]);
+ val2 = readl(&ptr2[i]);
+ if (val1 - val2 <= 6)
+ result = false;
+ }
+ ptr1 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x95c);
+ ptr2 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x914);
+ for (i = 0; i < 9; i++) {
+ val1 = readl(&ptr1[i]);
+ val2 = readl(&ptr2[i]);
+ if (val1 - val2 <= 6)
+ result = false;
+ }
+
+ if (config->bus_full_width) {
+ ptr1 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xb38);
+ ptr2 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xaf0);
+ for (i = 0; i < 9; i++) {
+ val1 = readl(&ptr1[i]);
+ val2 = readl(&ptr2[i]);
+ if (val1 - val2 <= 6)
+ result = false;
+ }
+ ptr1 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xb5c);
+ ptr2 = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xb14);
+ for (i = 0; i < 9; i++) {
+ val1 = readl(&ptr1[i]);
+ val2 = readl(&ptr2[i]);
+ if (val1 - val2 <= 6)
+ result = false;
+ }
+ }
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x60);
+
+ if (config->ranks == 2) {
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 0xc, 4);
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x10);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x20);
+
+ mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x8e0), 3, 3);
+ if (readl(SUNXI_DRAM_PHY0_BASE + 0x8e0) & 0xc)
+ result = false;
+
+ if (config->bus_full_width) {
+ mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0xae0), 3, 3);
+ if (readl(SUNXI_DRAM_PHY0_BASE + 0xae0) & 0xc)
+ result = false;
+ }
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x60);
+ }
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 0xc);
+
+ return result;
+}
+
+static void mctl_phy_bit_delay_compensation(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ u8 array0[32], array1[32];
+ u32 tmp;
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ array0[i] = (config->tpr11 >> (i & 0xf8)) & 0xff;
+ array1[i] = (config->tpr12 >> (i & 0xf8)) & 0x7f;
+ }
+
+ if (para->tpr10 & TPR10_DX_BIT_DELAY1) {
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x84, 0x40000);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0xa0, 3);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0x80);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x44, BIT(28));
+
+ writel(array0[0], SUNXI_DRAM_PHY0_BASE + 0x320);
+ writel((array0[0] << 24) | (array0[1] << 16) |
+ (array0[2] << 8) |
+ array0[3], SUNXI_DRAM_PHY0_BASE + 0x324);
+ writel((array0[4] << 24) | (array0[5] << 16) |
+ (array0[6] << 8) |
+ array0[7], SUNXI_DRAM_PHY0_BASE + 0x328);
+
+ writel(array0[0], SUNXI_DRAM_PHY0_BASE + 0x340);
+ writel((array0[0] << 24) | (array0[1] << 16) |
+ (array0[2] << 8) |
+ array0[3], SUNXI_DRAM_PHY0_BASE + 0x344);
+ writel((array0[4] << 24) | (array0[5] << 16) |
+ (array0[6] << 8) |
+ array0[7], SUNXI_DRAM_PHY0_BASE + 0x348);
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x40c, 0xff00,
+ array0[0] << 8);
+ writel((array0[0] << 24) | (array0[1] << 16) |
+ (array0[2] << 8) | array0[3],
+ SUNXI_DRAM_PHY0_BASE + 0x400);
+ writel((array0[4] << 24) | (array0[5] << 16) |
+ (array0[6] << 8) | array0[7],
+ SUNXI_DRAM_PHY0_BASE + 0x404);
+
+ writel(array0[0], SUNXI_DRAM_PHY0_BASE + 0x41c);
+ writel((array0[0] << 24) | (array0[1] << 16) |
+ (array0[2] << 8) | array0[3],
+ SUNXI_DRAM_PHY0_BASE + 0x420);
+ writel((array0[4] << 24) | (array0[5] << 16) |
+ (array0[6] << 8) | array0[7],
+ SUNXI_DRAM_PHY0_BASE + 0x424);
+
+ tmp = config->odt_en & 0xff;
+ tmp = (tmp << 24) | (tmp << 8);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x32c);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x34c);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x408);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x428);
+
+ writel(array0[8], SUNXI_DRAM_PHY0_BASE + 0x4a0);
+ writel((array0[8] << 24) | (array0[9] << 16) |
+ (array0[10] << 8) | array0[11],
+ SUNXI_DRAM_PHY0_BASE + 0x4a4);
+ writel((array0[12] << 24) | (array0[13] << 16) |
+ (array0[14] << 8) | array0[15],
+ SUNXI_DRAM_PHY0_BASE + 0x4a8);
+
+ writel(array0[8], SUNXI_DRAM_PHY0_BASE + 0x4c0);
+ writel((array0[8] << 24) | (array0[9] << 16) |
+ (array0[10] << 8) | array0[11],
+ SUNXI_DRAM_PHY0_BASE + 0x4c4);
+ writel((array0[12] << 24) | (array0[13] << 16) |
+ (array0[14] << 8) | array0[15],
+ SUNXI_DRAM_PHY0_BASE + 0x4c8);
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x58c, 0xff00,
+ array0[8] << 8);
+ writel((array0[8] << 24) | (array0[9] << 16) |
+ (array0[10] << 8) | array0[11],
+ SUNXI_DRAM_PHY0_BASE + 0x580);
+ writel((array0[12] << 24) | (array0[13] << 16) |
+ (array0[14] << 8) | array0[15],
+ SUNXI_DRAM_PHY0_BASE + 0x584);
+
+ writel(array0[8], SUNXI_DRAM_PHY0_BASE + 0x59c);
+ writel((array0[8] << 24) | (array0[9] << 16) |
+ (array0[10] << 8) | array0[11],
+ SUNXI_DRAM_PHY0_BASE + 0x5a0);
+ writel((array0[12] << 24) | (array0[13] << 16) |
+ (array0[14] << 8) | array0[15],
+ SUNXI_DRAM_PHY0_BASE + 0x5a4);
+
+ tmp = (config->odt_en >> 8) & 0xff;
+ tmp = (tmp << 24) | (tmp << 8);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x4ac);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x4cc);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x588);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x5a8);
+
+ writel(array0[16], SUNXI_DRAM_PHY0_BASE + 0x620);
+ writel((array0[16] << 24) | (array0[17] << 16) |
+ (array0[18] << 8) | array0[19],
+ SUNXI_DRAM_PHY0_BASE + 0x624);
+ writel((array0[20] << 24) | (array0[21] << 16) |
+ (array0[22] << 8) | array0[23],
+ SUNXI_DRAM_PHY0_BASE + 0x628);
+
+ writel(array0[16], SUNXI_DRAM_PHY0_BASE + 0x640);
+ writel((array0[16] << 24) | (array0[17] << 16) |
+ (array0[18] << 8) | array0[19],
+ SUNXI_DRAM_PHY0_BASE + 0x644);
+ writel((array0[20] << 24) | (array0[21] << 16) |
+ (array0[22] << 8) | array0[23],
+ SUNXI_DRAM_PHY0_BASE + 0x648);
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x70c,
+ 0xff00, array0[16] << 8);
+ writel((array0[16] << 24) | (array0[17] << 16) |
+ (array0[18] << 8) | array0[19],
+ SUNXI_DRAM_PHY0_BASE + 0x700);
+ writel((array0[20] << 24) | (array0[21] << 16) |
+ (array0[22] << 8) | array0[23],
+ SUNXI_DRAM_PHY0_BASE + 0x704);
+
+ writel(array0[16], SUNXI_DRAM_PHY0_BASE + 0x71c);
+ writel((array0[16] << 24) | (array0[17] << 16) |
+ (array0[18] << 8) | array0[19],
+ SUNXI_DRAM_PHY0_BASE + 0x720);
+ writel((array0[20] << 24) | (array0[21] << 16) |
+ (array0[22] << 8) | array0[23], SUNXI_DRAM_PHY0_BASE + 0x724);
+
+ tmp = (config->odt_en >> 16) & 0xff;
+ tmp = (tmp << 24) | (tmp << 8);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x62c);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x64c);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x708);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x728);
+
+ writel(array0[24], SUNXI_DRAM_PHY0_BASE + 0x7a0);
+ writel((array0[24] << 24) | (array0[25] << 16) |
+ (array0[26] << 8) | array0[27],
+ SUNXI_DRAM_PHY0_BASE + 0x7a4);
+ writel((array0[28] << 24) | (array0[29] << 16) |
+ (array0[30] << 8) | array0[31],
+ SUNXI_DRAM_PHY0_BASE + 0x7a8);
+
+ writel(array0[24], SUNXI_DRAM_PHY0_BASE + 0x7c0);
+ writel((array0[24] << 24) | (array0[25] << 16) |
+ (array0[26] << 8) | array0[27],
+ SUNXI_DRAM_PHY0_BASE + 0x7c4);
+ writel((array0[28] << 24) | (array0[29] << 16) |
+ (array0[30] << 8) | array0[31],
+ SUNXI_DRAM_PHY0_BASE + 0x7c8);
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x88c, 0xff00,
+ array0[24] << 8);
+ writel((array0[24] << 24) | (array0[25] << 16) |
+ (array0[26] << 8) | array0[27],
+ SUNXI_DRAM_PHY0_BASE + 0x880);
+ writel((array0[28] << 24) | (array0[29] << 16) |
+ (array0[30] << 8) | array0[31],
+ SUNXI_DRAM_PHY0_BASE + 0x884);
+
+ writel(array0[24], SUNXI_DRAM_PHY0_BASE + 0x89c);
+ writel((array0[24] << 24) | (array0[25] << 16) |
+ (array0[26] << 8) | array0[27],
+ SUNXI_DRAM_PHY0_BASE + 0x8a0);
+ writel((array0[28] << 24) | (array0[29] << 16) |
+ (array0[30] << 8) | array0[31],
+ SUNXI_DRAM_PHY0_BASE + 0x8a4);
+
+ tmp = (config->odt_en >> 24) & 0xff;
+ tmp = (tmp << 24) | (tmp << 8);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x7ac);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x7cc);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x888);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x8a8);
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x44, BIT(28));
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x44, BIT(28));
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x84, 0x40000);
+ }
+
+ if (para->tpr10 & TPR10_DX_BIT_DELAY0) {
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x84, 0x40000);
+
+ writel(array1[0] << 8, SUNXI_DRAM_PHY0_BASE + 0x330);
+ writel((array1[0] << 24) | (array1[1] << 16) |
+ (array1[2] << 8) | array1[3],
+ SUNXI_DRAM_PHY0_BASE + 0x334);
+ writel((array1[4] << 24) | (array1[5] << 16) |
+ (array1[6] << 8) | array1[7],
+ SUNXI_DRAM_PHY0_BASE + 0x338);
+
+ writel(array1[0] << 8, SUNXI_DRAM_PHY0_BASE + 0x350);
+ writel((array1[0] << 24) | (array1[1] << 16) |
+ (array1[2] << 8) | array1[3],
+ SUNXI_DRAM_PHY0_BASE + 0x354);
+ writel((array1[4] << 24) | (array1[5] << 16) |
+ (array1[6] << 8) | array1[7],
+ SUNXI_DRAM_PHY0_BASE + 0x358);
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x40c, 0xff, array1[0]);
+ writel((array1[0] << 24) | (array1[1] << 16) |
+ (array1[2] << 8) | array1[3],
+ SUNXI_DRAM_PHY0_BASE + 0x410);
+ writel((array1[4] << 24) | (array1[5] << 16) |
+ (array1[6] << 8) | array1[7],
+ SUNXI_DRAM_PHY0_BASE + 0x414);
+
+ writel(array1[0] << 8, SUNXI_DRAM_PHY0_BASE + 0x42c);
+ writel((array1[0] << 24) | (array1[1] << 16) |
+ (array1[2] << 8) | array1[3],
+ SUNXI_DRAM_PHY0_BASE + 0x430);
+ writel((array1[4] << 24) | (array1[5] << 16) |
+ (array1[6] << 8) | array1[7],
+ SUNXI_DRAM_PHY0_BASE + 0x434);
+
+ tmp = config->tpr14 & 0xff;
+ tmp = (tmp << 24) | (tmp << 8);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x33c);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x35c);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x418);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x438);
+
+ writel(array1[8] << 8, SUNXI_DRAM_PHY0_BASE + 0x4b0);
+ writel((array1[8] << 24) | (array1[9] << 16) |
+ (array1[10] << 8) | array1[11],
+ SUNXI_DRAM_PHY0_BASE + 0x4b4);
+ writel((array1[12] << 24) | (array1[13] << 16) |
+ (array1[14] << 8) | array1[15],
+ SUNXI_DRAM_PHY0_BASE + 0x4b8);
+
+ writel(array1[8] << 8, SUNXI_DRAM_PHY0_BASE + 0x4d0);
+ writel((array1[8] << 24) | (array1[9] << 16) |
+ (array1[10] << 8) | array1[11],
+ SUNXI_DRAM_PHY0_BASE + 0x4d4);
+ writel((array1[12] << 24) | (array1[13] << 16) |
+ (array1[14] << 8) | array1[15],
+ SUNXI_DRAM_PHY0_BASE + 0x4d8);
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x58c, 0xff, array1[8]);
+ writel((array1[8] << 24) | (array1[9] << 16) |
+ (array1[10] << 8) | array1[11],
+ SUNXI_DRAM_PHY0_BASE + 0x590);
+ writel((array1[12] << 24) | (array1[13] << 16) |
+ (array1[14] << 8) | array1[15],
+ SUNXI_DRAM_PHY0_BASE + 0x594);
+
+ writel(array1[8] << 8, SUNXI_DRAM_PHY0_BASE + 0x5ac);
+ writel((array1[8] << 24) | (array1[9] << 16) |
+ (array1[10] << 8) | array1[11],
+ SUNXI_DRAM_PHY0_BASE + 0x5b0);
+ writel((array1[12] << 24) | (array1[13] << 16) |
+ (array1[14] << 8) | array1[15],
+ SUNXI_DRAM_PHY0_BASE + 0x5b4);
+
+ tmp = (config->tpr14 >> 8) & 0xff;
+ tmp = (tmp << 24) | (tmp << 8);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x4bc);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x4dc);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x598);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x5b8);
+
+ writel(array1[16] << 8, SUNXI_DRAM_PHY0_BASE + 0x630);
+ writel((array1[16] << 24) | (array1[17] << 16) |
+ (array1[18] << 8) | array1[19],
+ SUNXI_DRAM_PHY0_BASE + 0x634);
+ writel((array1[20] << 24) | (array1[21] << 16) |
+ (array1[22] << 8) | array1[23],
+ SUNXI_DRAM_PHY0_BASE + 0x638);
+
+ writel(array1[16] << 8, SUNXI_DRAM_PHY0_BASE + 0x650);
+ writel((array1[16] << 24) | (array1[17] << 16) |
+ (array1[18] << 8) | array1[19],
+ SUNXI_DRAM_PHY0_BASE + 0x654);
+ writel((array1[20] << 24) | (array1[21] << 16) |
+ (array1[22] << 8) | array1[23],
+ SUNXI_DRAM_PHY0_BASE + 0x658);
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x70c, 0xff, array1[16]);
+ writel((array1[16] << 24) | (array1[17] << 16) |
+ (array1[18] << 8) | array1[19],
+ SUNXI_DRAM_PHY0_BASE + 0x710);
+ writel((array1[20] << 24) | (array1[21] << 16) |
+ (array1[22] << 8) | array1[23],
+ SUNXI_DRAM_PHY0_BASE + 0x714);
+
+ writel(array1[16] << 8, SUNXI_DRAM_PHY0_BASE + 0x72c);
+ writel((array1[16] << 24) | (array1[17] << 16) |
+ (array1[18] << 8) | array1[19],
+ SUNXI_DRAM_PHY0_BASE + 0x730);
+ writel((array1[20] << 24) | (array1[21] << 16) |
+ (array1[22] << 8) | array1[23],
+ SUNXI_DRAM_PHY0_BASE + 0x734);
+
+ tmp = (config->tpr14 >> 16) & 0xff;
+ tmp = (tmp << 24) | (tmp << 8);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x63c);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x65c);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x718);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x738);
+
+ writel(array1[24] << 8, SUNXI_DRAM_PHY0_BASE + 0x7b0);
+ writel((array1[24] << 24) | (array1[25] << 16) |
+ (array1[26] << 8) | array1[27],
+ SUNXI_DRAM_PHY0_BASE + 0x7b4);
+ writel((array1[28] << 24) | (array1[29] << 16) |
+ (array1[30] << 8) | array1[31],
+ SUNXI_DRAM_PHY0_BASE + 0x7b8);
+
+ writel(array1[24] << 8, SUNXI_DRAM_PHY0_BASE + 0x7d0);
+ writel((array1[24] << 24) | (array1[25] << 16) |
+ (array1[26] << 8) | array1[27],
+ SUNXI_DRAM_PHY0_BASE + 0x7d4);
+ writel((array1[28] << 24) | (array1[29] << 16) |
+ (array1[30] << 8) | array1[31],
+ SUNXI_DRAM_PHY0_BASE + 0x7d8);
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x88c, 0xff, array1[24]);
+ writel((array1[24] << 24) | (array1[25] << 16) |
+ (array1[26] << 8) | array1[27],
+ SUNXI_DRAM_PHY0_BASE + 0x890);
+ writel((array1[28] << 24) | (array1[29] << 16) |
+ (array1[30] << 8) | array1[31],
+ SUNXI_DRAM_PHY0_BASE + 0x894);
+
+ writel(array1[24] << 8, SUNXI_DRAM_PHY0_BASE + 0x8ac);
+ writel((array1[24] << 24) | (array1[25] << 16) |
+ (array1[26] << 8) | array1[27],
+ SUNXI_DRAM_PHY0_BASE + 0x8b0);
+ writel((array1[28] << 24) | (array1[29] << 16) |
+ (array1[30] << 8) | array1[31],
+ SUNXI_DRAM_PHY0_BASE + 0x8b4);
+
+ tmp = (config->tpr14 >> 24) & 0xff;
+ tmp = (tmp << 24) | (tmp << 8);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x7bc);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x7dc);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x898);
+ writel(tmp, SUNXI_DRAM_PHY0_BASE + 0x8b8);
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x94, 4);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x94, 4);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x84, 0x40000);
+ }
+}
+
+static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ u32 val, low, high;
+
+ if (para->tpr10 & BIT(31)) {
+ val = para->tpr0;
+ } else {
+ val = ((para->tpr10 & 0xf0) << 5) | ((para->tpr10 & 0xf) << 1);
+ if (para->tpr10 >> 29)
+ val <<= 1;
+ }
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x84, 0x40000);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0xac, 0x1000);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x48, 0xc0000000);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ case SUNXI_DRAM_TYPE_DDR4:
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ low = val & 0xff;
+ high = (val >> 8) & 0xff;
+
+ val = (high << 24) | (high << 16) | (high << 8) | high;
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x104);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x108);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x10c);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x114);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x118);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x11c);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x120);
+
+ val = (low << 24) | (low << 16) | (high << 8) | high;
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x11c);
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ low = val & 0xff;
+ high = (val >> 8) & 0xff;
+
+ val = (high << 24) | (high << 16) | (high << 8) | high;
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x104);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x108);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x10c);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x114);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x118);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x11c);
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x120);
+
+ val = (high << 24) | (high << 16) | (low << 8) | low;
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x110);
+
+ val = (low << 24) | (high << 16) | (low << 8) | high;
+ writel(val, SUNXI_DRAM_PHY0_BASE + 0x11c);
+ break;
+ default:
+ panic("This DRAM setup is currently not supported.\n");
+ };
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x38, 1);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x38, 1);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x84, 0x40000);
+}
+
+static bool mctl_phy_init(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ void * const mctl_com = (void *)SUNXI_DRAM_COM_BASE;
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ void *const prcm = (void *)SUNXI_PRCM_BASE;
+ u32 val, val2, mr1, mr2;
+ int i;
+
+ clrbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 1);
+ udelay(1);
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x84, 0x40000);
+
+ if (config->bus_full_width)
+ val = 0xf00;
+ else
+ val = 0x300;
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x00, 0xf00, val);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ val = 9;
+ val2 = 13;
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ if (config->clk <= 936) {
+ val = 10;
+ val2 = 14;
+ } else if (config->clk <= 1200) {
+ val = 12;
+ val2 = 16;
+ } else {
+ val = 14;
+ val2 = 18;
+ }
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ val = 8;
+ val2 = 14;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ if (config->clk <= 936) {
+ val = 10;
+ val2 = 20;
+ } else if (config->clk <= 1200) {
+ val = 14;
+ val2 = 28;
+ } else {
+ val = 16;
+ val2 = 32;
+ }
+ break;
+ default:
+ panic("This DRAM setup is currently not supported.\n");
+ };
+
+ writel((val << 24) | (val << 16) | (val << 8) | val, SUNXI_DRAM_PHY0_BASE + 0x10);
+ writel((val2 << 24) | (val2 << 16) | (val2 << 8) | val2, SUNXI_DRAM_PHY0_BASE + 0x0c);
+ writel(0, SUNXI_DRAM_PHY0_BASE + 0x08);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ writel(0x150a0310, SUNXI_DRAM_PHY0_BASE + 0x54);
+ writel(0x13140816, SUNXI_DRAM_PHY0_BASE + 0x58);
+ writel(0x001c0d1b, SUNXI_DRAM_PHY0_BASE + 0x5c);
+ writel(0x050c1d1a, SUNXI_DRAM_PHY0_BASE + 0x60);
+ writel(0x0411060b, SUNXI_DRAM_PHY0_BASE + 0x64);
+ writel(0x09071217, SUNXI_DRAM_PHY0_BASE + 0x68);
+ writel(0x18190e01, SUNXI_DRAM_PHY0_BASE + 0x6c);
+ writel(0x020f1e00, SUNXI_DRAM_PHY0_BASE + 0x70);
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ writel(0x090c1c14, SUNXI_DRAM_PHY0_BASE + 0x54);
+ writel(0x1300060f, SUNXI_DRAM_PHY0_BASE + 0x58);
+ writel(0x12030807, SUNXI_DRAM_PHY0_BASE + 0x5c);
+ writel(0x0b100a02, SUNXI_DRAM_PHY0_BASE + 0x60);
+ writel(0x1a110e05, SUNXI_DRAM_PHY0_BASE + 0x64);
+ writel(0x0d041617, SUNXI_DRAM_PHY0_BASE + 0x68);
+ writel(0x1819011b, SUNXI_DRAM_PHY0_BASE + 0x6c);
+ writel(0x151d1e00, SUNXI_DRAM_PHY0_BASE + 0x70);
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ writel(0x010a1a0f, SUNXI_DRAM_PHY0_BASE + 0x54);
+ writel(0x10081b07, SUNXI_DRAM_PHY0_BASE + 0x58);
+ writel(0x11061c12, SUNXI_DRAM_PHY0_BASE + 0x5c);
+ writel(0x00131409, SUNXI_DRAM_PHY0_BASE + 0x60);
+ writel(0x15030e16, SUNXI_DRAM_PHY0_BASE + 0x64);
+ writel(0x0b0c0d17, SUNXI_DRAM_PHY0_BASE + 0x68);
+ writel(0x18190204, SUNXI_DRAM_PHY0_BASE + 0x6c);
+ writel(0x051d1e00, SUNXI_DRAM_PHY0_BASE + 0x70);
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ writel(0x00010203, SUNXI_DRAM_PHY0_BASE + 0x54);
+ writel(0x04050607, SUNXI_DRAM_PHY0_BASE + 0x58);
+ writel(0x08090a0b, SUNXI_DRAM_PHY0_BASE + 0x5c);
+ writel(0x0c0d0e0f, SUNXI_DRAM_PHY0_BASE + 0x60);
+ writel(0x10111213, SUNXI_DRAM_PHY0_BASE + 0x64);
+ writel(0x14151617, SUNXI_DRAM_PHY0_BASE + 0x68);
+ writel(0x18191a1b, SUNXI_DRAM_PHY0_BASE + 0x6c);
+ writel(0x1c1d1e00, SUNXI_DRAM_PHY0_BASE + 0x70);
+ break;
+ default:
+ panic("This DRAM setup is currently not supported.\n");
+ };
+
+ mctl_phy_configure_odt(para);
+
+ if (para->tpr10 & TPR10_CA_BIT_DELAY)
+ mctl_phy_ca_bit_delay_compensation(para, config);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ val = 0x2bbd4900;
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ val = 0x3841b800;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ val = 0x19016300;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ val = 0x18fd6300;
+ break;
+ default:
+ panic("This DRAM setup is currently not supported.\n");
+ };
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0xa8, 0xffffff00, val);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x00, 0x70);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ val = 0x20;
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ val = 0x40;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ val = 0x30;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ val = 0x50;
+ break;
+ default:
+ panic("This DRAM setup is currently not supported.\n");
+ };
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x00, val);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x00, 0x80);
+
+ // TODO: fix intervals
+ if (config->clk - 251 < 250) {
+ val = 0x18000000;
+ val2 = 0x18181818;
+ } else if (config->clk - 126 < 125) {
+ val = 0x28000000;
+ val2 = 0x28282828;
+ } else if (config->clk < 126) {
+ val = 0x38000000;
+ val2 = 0x38383838;
+ } else {
+ val = 0x18000000;
+ val2 = 0;
+ }
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0xc0, 0x78000000, val);
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0xd0, 0x78787878, val2);
+
+ clrbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(9));
+ udelay(10);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ val = para->tpr6 & 0xff;
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ val = para->tpr6 >> 8 & 0xff;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ val = para->tpr6 >> 16 & 0xff;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ val = para->tpr6 >> 24;
+ break;
+ default:
+ panic("This DRAM setup is currently not supported.\n");
+ };
+ val <<= 24;
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x300, 0xff800060, val | 0x40);
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x600, 0xff800060, val | 0x40);
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x480, 0xff800060, val | 0x40);
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x780, 0xff800060, val | 0x40);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x84, 0x8000000);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x94, 0x80);
+ udelay(10);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x94, 0x80);
+ udelay(10);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x84, 0x8000000);
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x308, 0x200);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x488, 0x200);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x608, 0x200);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x788, 0x200);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x908, 0x200);
+ if (para->type == SUNXI_DRAM_TYPE_LPDDR4) {
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x308, 0x200);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x488, 0x200);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x608, 0x200);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x788, 0x200);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x908, 0x200);
+ }
+
+ if (config->clk < 936)
+ val = 0x1b000000;
+ else
+ val = 0xc000000;
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x14, 0x1f000000, val);
+
+ setbits_le32(mctl_com + MCTL_COM_MAER0, BIT(8));
+
+ /* start DFI init */
+ writel(0, &mctl_ctl->swctl);
+ setbits_le32(&mctl_ctl->dfimisc, 1);
+ setbits_le32(&mctl_ctl->dfimisc, 0x20);
+ writel(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, 1, 1);
+ mctl_await_completion(&mctl_ctl->dfistat, 1, 1);
+
+ udelay(500);
+ setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 1);
+ udelay(1);
+
+ writel(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->dfimisc, 0x20);
+ writel(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, 1, 1);
+
+ writel(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->pwrctl, 0x20);
+ writel(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, 1, 1);
+ mctl_await_completion(&mctl_ctl->statr, 3, 1);
+
+ udelay(500);
+
+ writel(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->dfimisc, 1);
+ writel(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, 1, 1);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ writel(0x1f14, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(4, &mctl_ctl->mrctrl1);
+ writel(0x800010f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0x20, &mctl_ctl->mrctrl1);
+ writel(0x800020f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0, &mctl_ctl->mrctrl1);
+ writel(0x800030f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ if (config->clk <= 936) {
+ mr1 = 0x34;
+ mr2 = 0x1b;
+ } else if (config->clk <= 1200) {
+ mr1 = 0x54;
+ mr2 = 0x2d;
+ } else {
+ mr1 = 0x64;
+ mr2 = 0x36;
+ }
+
+ writel(0x0, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0x100 | mr1, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0x200 | mr2, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0x333, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0x403, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0xb04, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0xc72, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0xd00, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0xe08, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0x1626, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+ break;
+ default:
+ panic("This DRAM setup is currently not supported.\n");
+ };
+
+ writel(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->rfshctl3, 1);
+ writel(1, &mctl_ctl->swctl);
+
+ if (para->tpr10 & TPR10_WRITE_LEVELING) {
+ for (i = 0; i < 5; i++)
+ if (mctl_phy_write_leveling(para, config))
+ break;
+ if (i == 5) {
+ debug("write leveling failed!\n");
+ return false;
+ }
+ }
+
+ if (para->tpr10 & TPR10_READ_CALIBRATION) {
+ for (i = 0; i < 5; i++)
+ if (mctl_phy_read_calibration(para, config))
+ break;
+ if (i == 5) {
+ debug("read calibration failed!\n");
+ return false;
+ }
+ }
+
+ if (para->tpr10 & TPR10_READ_TRAINING) {
+ for (i = 0; i < 5; i++)
+ if (mctl_phy_read_training(para, config))
+ break;
+ if (i == 5) {
+ debug("read training failed!\n");
+ return false;
+ }
+ }
+
+ if (para->tpr10 & TPR10_WRITE_TRAINING) {
+ for (i = 0; i < 5; i++)
+ if (mctl_phy_write_training(config))
+ break;
+ if (i == 5) {
+ debug("write training failed!\n");
+ return false;
+ }
+ }
+
+ mctl_phy_bit_delay_compensation(para, config);
+
+ return true;
+}
+
+static bool mctl_ctrl_init(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ void * const mctl_com = (void *)SUNXI_DRAM_COM_BASE;
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ u32 reg_val;
+
+ clrsetbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24), BIT(25) | BIT(9));
+ setbits_le32(mctl_com + MCTL_COM_MAER0, BIT(15) | BIT(9));
+
+ if (para->type == SUNXI_DRAM_TYPE_LPDDR4) {
+ setbits_le32(0x02023ea8, 1); // NSI
+ setbits_le32(0x02071008, 1); // NSI_CPU
+ }
+
+ clrsetbits_le32(&mctl_ctl->sched[0], 0xff08, 0x3000);
+ clrsetbits_le32(&mctl_ctl->sched[1], 0x77000000, 0x33000000);
+ clrsetbits_le32(&mctl_ctl->unk_0x270, 0xffff, 0x808);
+ clrsetbits_le32(&mctl_ctl->unk_0x264, 0xff00ffff, 0x1f000030);
+
+ writel(0, &mctl_ctl->hwlpctl);
+
+ 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_LPDDR4:
+ reg_val |= MSTR_BURST_LENGTH(16) | MSTR_DEVICETYPE_LPDDR4;
+ break;
+ default:
+ panic("This DRAM setup is currently not supported.\n");
+ };
+ if (config->bus_full_width)
+ reg_val |= MSTR_BUSWIDTH_FULL;
+ else
+ reg_val |= MSTR_BUSWIDTH_HALF;
+ writel(BIT(31) | BIT(30) | reg_val, &mctl_ctl->mstr);
+
+ if (config->ranks == 2)
+ writel(0x0303, &mctl_ctl->odtmap);
+ else
+ writel(0x0201, &mctl_ctl->odtmap);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ reg_val = 0x06000400;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ reg_val = 0x04000400;
+ break;
+ 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);
+ writel(reg_val, &mctl_ctl->unk_0x4240);
+
+ mctl_set_addrmap(config);
+
+ mctl_set_timing_params(config->clk);
+
+ writel(0, &mctl_ctl->pwrctl);
+
+ setbits_le32(&mctl_ctl->dfiupd[0], BIT(31) | BIT(30));
+ setbits_le32(&mctl_ctl->zqctl[0], BIT(31) | BIT(30));
+ setbits_le32(&mctl_ctl->unk_0x2180, BIT(31) | BIT(30));
+ 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));
+
+ writel(0x20, &mctl_ctl->pwrctl);
+ setbits_le32(&mctl_ctl->clken, BIT(8));
+
+ clrsetbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24), BIT(9));
+ udelay(1);
+ /* this write seems to enable PHY MMIO region */
+ setbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24));
+
+ if (!mctl_phy_init(para, config))
+ return false;
+
+ writel(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->rfshctl3, BIT(0));
+ writel(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, 1, 1);
+
+ return true;
+}
+
+static bool mctl_core_init(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ mctl_sys_init(config->clk);
+
+ return mctl_ctrl_init(para, config);
+}
+
+static void mctl_auto_detect_rank_width(const struct dram_para *para,
+ struct dram_config *config)
+{
+ /* this is minimum size that it's supported */
+ config->cols = 8;
+ config->rows = 13;
+
+ /*
+ * Strategy here is to test most demanding combination first and least
+ * demanding last, otherwise HW might not be fully utilized. For
+ * example, half bus width and rank = 1 combination would also work
+ * on HW with full bus width and rank = 2, but only 1/4 RAM would be
+ * visible.
+ */
+
+ debug("testing 32-bit width, rank = 2\n");
+ config->bus_full_width = 1;
+ config->ranks = 2;
+ if (mctl_core_init(para, config))
+ return;
+
+ debug("testing 32-bit width, rank = 1\n");
+ config->bus_full_width = 1;
+ config->ranks = 1;
+ if (mctl_core_init(para, config))
+ return;
+
+ debug("testing 16-bit width, rank = 2\n");
+ config->bus_full_width = 0;
+ config->ranks = 2;
+ if (mctl_core_init(para, config))
+ return;
+
+ debug("testing 16-bit width, rank = 1\n");
+ config->bus_full_width = 0;
+ config->ranks = 1;
+ if (mctl_core_init(para, config))
+ return;
+
+ panic("This DRAM setup is currently not supported.\n");
+}
+
+static void mctl_auto_detect_dram_size(const struct dram_para *para,
+ struct dram_config *config)
+{
+ /* detect row address bits */
+ config->cols = 8;
+ config->rows = 16;
+ mctl_core_init(para, config);
+
+ for (config->rows = 13; config->rows < 16; config->rows++) {
+ /* 8 banks, 8 bit per byte and 16/32 bit width */
+ if (mctl_mem_matches((1 << (config->rows + config->cols +
+ 4 + config->bus_full_width))))
+ break;
+ }
+
+ /* detect column address bits */
+ config->cols = 11;
+ mctl_core_init(para, config);
+
+ for (config->cols = 8; config->cols < 11; config->cols++) {
+ /* 8 bits per byte and 16/32 bit width */
+ if (mctl_mem_matches(1 << (config->cols + 1 +
+ config->bus_full_width)))
+ break;
+ }
+}
+
+static unsigned long long mctl_calc_size(const struct dram_config *config)
+{
+ u8 width = config->bus_full_width ? 4 : 2;
+
+ /* 8 banks */
+ return (1ULL << (config->cols + config->rows + 3)) * width * config->ranks;
+}
+
+static const struct dram_para para = {
+#ifdef CONFIG_SUNXI_DRAM_A523_DDR3
+ .type = SUNXI_DRAM_TYPE_DDR3,
+#elif defined(CONFIG_SUNXI_DRAM_A523_LPDDR4)
+ .type = SUNXI_DRAM_TYPE_LPDDR4,
+#endif
+ .dx_odt = CONFIG_DRAM_SUNXI_DX_ODT,
+ .dx_dri = CONFIG_DRAM_SUNXI_DX_DRI,
+ .ca_dri = CONFIG_DRAM_SUNXI_CA_DRI,
+ .tpr0 = CONFIG_DRAM_SUNXI_TPR0,
+ .tpr1 = CONFIG_DRAM_SUNXI_TPR1,
+ .tpr2 = CONFIG_DRAM_SUNXI_TPR2,
+ .tpr6 = CONFIG_DRAM_SUNXI_TPR6,
+ .tpr10 = CONFIG_DRAM_SUNXI_TPR10,
+};
+
+static void sunxi_nsi_init(void)
+{
+ /* IOMMU prio 3 */
+ writel(0x1, 0x02021418);
+ writel(0xf, 0x02021414);
+ /* DE prio 2 */
+ writel(0x1, 0x02021a18);
+ writel(0xa, 0x02021a14);
+ /* VE R prio 2 */
+ writel(0x1, 0x02021618);
+ writel(0xa, 0x02021614);
+ /* VE RW prio 2 */
+ writel(0x1, 0x02021818);
+ writel(0xa, 0x02021814);
+ /* ISP prio 2 */
+ writel(0x1, 0x02020c18);
+ writel(0xa, 0x02020c14);
+ /* CSI prio 2 */
+ writel(0x1, 0x02021c18);
+ writel(0xa, 0x02021c14);
+ /* NPU prio 2 */
+ writel(0x1, 0x02020a18);
+ writel(0xa, 0x02020a14);
+
+ /* close ra0 autogating */
+ writel(0x0, 0x02023c00);
+ /* close ta autogating */
+ writel(0x0, 0x02023e00);
+ /* close pcie autogating */
+ writel(0x0, 0x02020600);
+}
+
+static void init_something(void)
+
+{
+ u32 *ptr = (u32 *)0x02000804;
+
+ do {
+ *ptr++ = 0xffffffff;
+ } while (ptr != (u32 *)0x20008e4);
+
+ writel(0, 0x07002400);
+ writel(0, 0x07002404);
+ writel(0, 0x07002408);
+
+ writel(0xffffffff, 0x07002004);
+ writel(0xffffffff, 0x07002014);
+ writel(0xffffffff, 0x07002024);
+ setbits_le32(0x07010290, 7);
+
+ writel(7, 0x02001f00);
+ writel(0xffff, 0x03002020);
+ writel(3, 0x020008e0);
+ writel(7, 0x07102008);
+}
+
+unsigned long sunxi_dram_init(void)
+{
+ struct dram_config config;
+ unsigned long size;
+
+ config.clk = 360;
+ switch (para.type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ config.odt_en = 0x90909090;
+ config.tpr11 = 0x8f919190;
+ config.tpr12 = 0x22222723;
+ config.tpr14 = 0x48484848;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ config.odt_en = 0x84848484;
+ config.tpr11 = 0x9a9a9a9a;
+ config.tpr12 = 0x0e0f070a;
+ config.tpr14 = 0x48484848;
+ break;
+ default:
+ panic("This DRAM setup is currently not supported.\n");
+ };
+
+ setbits_le32(0x03000160, BIT(8));
+ clrbits_le32(0x03000168, 0x3f);
+
+ mctl_auto_detect_rank_width(&para, &config);
+ mctl_auto_detect_dram_size(&para, &config);
+
+ config.clk = CONFIG_DRAM_CLK;
+ config.odt_en = CONFIG_DRAM_SUNXI_ODT_EN;
+ config.tpr11 = CONFIG_DRAM_SUNXI_TPR11;
+ config.tpr12 = CONFIG_DRAM_SUNXI_TPR12;
+ config.tpr14 = CONFIG_DRAM_SUNXI_TPR14;
+
+ mctl_core_init(&para, &config);
+
+ size = mctl_calc_size(&config);
+
+ sunxi_nsi_init();
+ init_something();
+
+ return size;
+};
diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile
index 4dc1f29fc08..5de9fd5aab4 100644
--- a/arch/arm/mach-sunxi/dram_timings/Makefile
+++ b/arch/arm/mach-sunxi/dram_timings/Makefile
@@ -8,3 +8,5 @@ obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3) += h616_lpddr3.o
obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4) += h616_lpddr4_2133.o
obj-$(CONFIG_SUNXI_DRAM_A133_DDR4) += a133_ddr4.o
obj-$(CONFIG_SUNXI_DRAM_A133_LPDDR4) += a133_lpddr4.o
+obj-$(CONFIG_SUNXI_DRAM_A523_DDR3) += a523_ddr3.o
+obj-$(CONFIG_SUNXI_DRAM_A523_LPDDR4) += a523_lpddr4.o
diff --git a/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c b/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
new file mode 100644
index 00000000000..6e140bb1454
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * sun55i A523 DDR3 timings, as programmed by Allwinner's boot0 on
+ * the X96QPro+ TV box. As usual very conservative timings, but probably
+ * the most compatible and reliable.
+ *
+ * (C) Copyright 2024 Mikhail Kalashnikov <iuncuim@gmail.com>
+ * Based on H616 DDR3 timings:
+ * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ */
+
+#include <asm/arch/dram.h>
+#include <asm/arch/cpu.h>
+
+void mctl_set_timing_params(u32 clk)
+{
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ /*
+ * formulas and constraints as of
+ * JEDEC DDR3 specification, for
+ * DDR3-1600, per JESD79-3F
+ */
+ u8 tccd = 2; /* 4nCK */
+ u8 tfaw = ns_to_t(50, clk);
+ u8 trrd = max(ns_to_t(6, clk), 4); /* max(6 ns, 4nCK) */
+ u8 twtr = max(ns_to_t(8, clk), 4); /* max(7.5 ns, 4nCK) */
+ u8 trcd = ns_to_t(15, clk); /* 13.5 ns */
+ u8 trc = ns_to_t(53, clk);
+ u8 txp = max(ns_to_t(8, clk), 2); /* max(6 ns, 3nCK) */
+ u8 trtp = max(ns_to_t(8, clk), 4); /* max(7.5 ns, 4nCK) */
+ u8 trp = ns_to_t(15, clk); /* >= 13.75 ns */
+ u8 tras = ns_to_t(38, clk);
+ u16 trefi = ns_to_t(11350, clk) / 32;
+ u16 trfc = ns_to_t(360, clk); /* 160 ns for 2Gb */
+ u16 txsr = 4;
+
+ u8 tmrw = 0;
+ u8 tmrd = 4; /* 4nCK */
+
+ u8 tmod = max(ns_to_t(15, clk), 12); /* max(15 ns, 12nCK) */
+ u8 tcke = max(ns_to_t(6, clk), 4); /* max(5.625 ns, 3nCK)*/
+ u8 tcksrx = max(ns_to_t(10, clk), 4); /* max(10 ns, 5nCK) */
+ u8 tcksre = max(ns_to_t(10, clk), 4); /* max(10 ns, 5nCK) */
+ u8 trasmax = (clk / 2) / 15; /* tREFI * 9 */
+
+ /*
+ * TODO: support multiple DDR3 speed grades, these values below match
+ * the worst case for DDR3-2133, so should be good for all frequencies,
+ * but use the most conversative timings.
+ * DDR3-1866 (DRAM_CLK=912) should also work, or tcl=6 and tcwl=4 with
+ * DRAM_CLK=792. Maybe even the combination of both, depending on the
+ * particular device.
+ */
+ u8 tcl = 7; /* CAS latency: 14 */
+ u8 tcwl = 5; /* CAS write latency: 10 */
+ u8 t_rdata_en = 9;
+ u8 tphy_wrlat = 5;
+ u8 twr = 7;
+
+ u8 tckesr = tcke + 1; /* tCKE(min) + 1nCK */
+
+ u8 twtp = twr + 2 + tcwl;
+ u8 twr2rd = twtr + 2 + tcwl; /* (WL + BL / 2 + tWTR) / 2 */
+ u8 trd2wr = tcl + 3 - tcwl;
+ u8 txs = ns_to_t(360, clk) / 32; /* max(5nCK,tRFC+10ns)*/
+ u8 txsdll = 512 / 32; /* 512 nCK */
+ u8 txsabort = 4;
+ u8 txsfast = 4;
+
+ /* 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, 0x156);
+ writel(0x01f20000, &mctl_ctl->init[1]);
+ writel(0x00001700, &mctl_ctl->init[2]);
+ writel(0, &mctl_ctl->dfimisc);
+ writel(0x1f140004, &mctl_ctl->init[3]);
+ writel(0x00200000, &mctl_ctl->init[4]);
+ writel(0, &mctl_ctl->init[6]); /* ? */
+ writel(0, &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 */
+ trfc = 0; /* as written so by boot0 */
+ writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
+}
diff --git a/arch/arm/mach-sunxi/dram_timings/a523_lpddr4.c b/arch/arm/mach-sunxi/dram_timings/a523_lpddr4.c
new file mode 100644
index 00000000000..940a4d04d57
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_timings/a523_lpddr4.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * sun55i A523 LPDDR4-2133 timings, as programmed by Allwinner's boot0
+ *
+ * (C) Copyright 2024 Jernej Skrabec <jernej.skrabec@gmail.com>
+ * (C) Copyright 2023 Mikhail Kalashnikov <iuncuim@gmail.com>
+ *
+ */
+
+#include <asm/arch/dram.h>
+#include <asm/arch/cpu.h>
+
+void mctl_set_timing_params(u32 clk)
+{
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ u8 tcl, tcwl, t_rdata_en, trtp, twr, tphy_wrlat;
+ unsigned int mr1, mr2;
+
+ u8 tccd = 4;
+ u8 tfaw = ns_to_t(40, clk);
+ u8 trrd = max(ns_to_t(10, clk), 2);
+ u8 twtr = max(ns_to_t(10, clk), 4);
+ u8 trcd = max(ns_to_t(18, clk), 2);
+ u8 trc = ns_to_t(65, clk);
+ u8 txp = max(ns_to_t(8, clk), 2);
+ u8 trp = ns_to_t(21, clk);
+ u8 tras = ns_to_t(42, clk);
+ u16 trefi = ns_to_t(3904, clk) / 32;
+ u16 trfc = ns_to_t(280, clk);
+ u16 txsr = ns_to_t(290, clk);
+
+ u8 tmrw = max(ns_to_t(14, clk), 5);
+ u8 tmod = 12;
+ u8 tcke = max(ns_to_t(15, clk), 2);
+ u8 tcksrx = max(ns_to_t(2, clk), 2);
+ u8 tcksre = max(ns_to_t(5, clk), 2);
+ u8 trasmax = (trefi * 9) / 32;
+
+ if (clk <= 936) {
+ mr1 = 0x34;
+ mr2 = 0x1b;
+ tcl = 10;
+ tcwl = 5;
+ t_rdata_en = 17;
+ trtp = 4;
+ tphy_wrlat = 5;
+ twr = 10;
+ } else if (clk <= 1200) {
+ mr1 = 0x54;
+ mr2 = 0x2d;
+ tcl = 14;
+ tcwl = 7;
+ t_rdata_en = 25;
+ trtp = 6;
+ tphy_wrlat = 9;
+ twr = 15;
+ } else {
+ mr1 = 0x64;
+ mr2 = 0x36;
+ tcl = 16;
+ tcwl = 8;
+ t_rdata_en = 29;
+ trtp = 7;
+ tphy_wrlat = 11;
+ twr = 17;
+ }
+
+ u8 tmrd = tmrw;
+ u8 tckesr = tcke;
+ u8 twtp = twr + 9 + tcwl;
+ u8 twr2rd = twtr + 9 + tcwl;
+ u8 trd2wr = ns_to_t(4, clk) + 7 - ns_to_t(1, clk) + tcl;
+ u8 txs = 4;
+ u8 txsdll = 16;
+ u8 txsabort = 4;
+ u8 txsfast = 4;
+
+ /* 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, 0x558);
+ writel(0x01f20000, &mctl_ctl->init[1]);
+ writel(0x00001705, &mctl_ctl->init[2]);
+ writel(0, &mctl_ctl->dfimisc);
+ writel((mr1 << 16) | mr2, &mctl_ctl->init[3]);
+ writel(0x00330000, &mctl_ctl->init[4]);
+ writel(0x00040072, &mctl_ctl->init[6]);
+ writel(0x00260008, &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);
+}
diff --git a/board/sunxi/MAINTAINERS b/board/sunxi/MAINTAINERS
index 1b4b7d87163..e00b54f4535 100644
--- a/board/sunxi/MAINTAINERS
+++ b/board/sunxi/MAINTAINERS
@@ -137,6 +137,11 @@ M: Chris Morgan <macromorgan@hotmail.com>
S: Maintained
F: configs/anbernic_rg35xx_h700_defconfig
+AVAOTA A1 BOARD
+M: Andre Przywara <andre.przywara@arm.com>
+S: Maintained
+F: configs/avaota-a1_defconfig
+
BANANAPI M1 PLUS
M: Jagan Teki <jagan@amarulasolutions.com>
S: Maintained
@@ -531,6 +536,11 @@ M: Quentin Schulz <quentin.schulz@free-electrons.com>
S: Maintained
F: configs/parrot_r16_defconfig
+RADXA CUBIE A5E BOARD
+M: Andre Przywara <andre.przywara@arm.com>
+S: Maintained
+F: configs/radxa-cubie-a5e_defconfig
+
SINLINX SINA31s BOARD
M: Chen-Yu Tsai <wens@csie.org>
S: Maintained
@@ -591,6 +601,11 @@ M: Andre Przywara <andre.przywara@arm.com>
S: Maintained
F: configs/x96_mate_defconfig
+X96Q PRO+ TV BOX
+M: Andre Przywara <andre.przywara@arm.com>
+S: Maintained
+F: configs/x96q_pro_plus_defconfig
+
YONES TOPTECH BD1078 BOARD
M: Paul Kocialkowski <contact@paulk.fr>
S: Maintained
diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index 943b6221b8a..2929bc17f08 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -118,6 +118,10 @@ void i2c_init_board(void)
clock_twi_onoff(5, 1);
sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN50I_H616_GPL_R_TWI);
sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN50I_H616_GPL_R_TWI);
+#elif CONFIG_MACH_SUN55I_A523
+ clock_twi_onoff(5, 1);
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN50I_GPL_R_TWI);
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN50I_GPL_R_TWI);
#else
clock_twi_onoff(5, 1);
sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_H3_GPL_R_TWI);
@@ -435,7 +439,8 @@ static void mmc_pinmux_setup(int sdc)
sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
sunxi_gpio_set_drv(pin, 2);
}
-#elif defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133)
+#elif defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133) || \
+ defined(CONFIG_MACH_SUN55I_A523)
/* SDC2: PC0-PC1, PC5-PC6, PC8-PC11, PC13-PC16 */
for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(16); pin++) {
if (pin > SUNXI_GPC(1) && pin < SUNXI_GPC(5))
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index ac25fcea21d..ab05536bd02 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -80,7 +80,7 @@ config SPL_MAX_SIZE
default 0x1b000 if AM33XX && !TI_SECURE_DEVICE
default 0xec00 if OMAP34XX
default 0x10000 if ARCH_MX6 && !MX6_OCRAM_256KB
- default 0xbfa0 if MACH_SUN50I_H616 || MACH_SUN50I_A133
+ default 0xbfa0 if MACH_SUN50I_H616 || MACH_SUN50I_A133 || MACH_SUN55I_A523
default 0x7000 if RCAR_GEN3
default 0x5fa0 if SUNXI_SRAM_ADDRESS = 0x0
default 0x7fa0 if ARCH_SUNXI
@@ -278,6 +278,7 @@ config SPL_TEXT_BASE
default 0x00912000 if ARCH_MX7
default 0x40301350 if OMAP54XX
default 0x10060 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN9I
+ default 0x44060 if MACH_SUN55I_A523
default 0x20060 if SUN50I_GEN_H6 || SUNXI_GEN_NCAT2
default 0x00060 if ARCH_SUNXI
default 0xfffc0000 if ARCH_ZYNQMP
@@ -432,6 +433,7 @@ config SPL_STACK
default 0x118000 if MACH_SUN50I_H6
default 0x52a00 if MACH_SUN50I_H616
default 0x40000 if MACH_SUN8I_R528 || MACH_SUN50I_A133
+ default 0x44000 if MACH_SUN55I_A523
default 0x54000 if MACH_SUN50I || MACH_SUN50I_H5
default 0x18000 if MACH_SUN9I
default 0x8000 if ARCH_SUNXI
diff --git a/configs/avaota-a1_defconfig b/configs/avaota-a1_defconfig
new file mode 100644
index 00000000000..55457edd3b3
--- /dev/null
+++ b/configs/avaota-a1_defconfig
@@ -0,0 +1,31 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun55i-t527-avaota-a1"
+CONFIG_SPL=y
+CONFIG_DRAM_SUNXI_DX_ODT=0x07070707
+CONFIG_DRAM_SUNXI_DX_DRI=0x0d0d0d0d
+CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e
+CONFIG_DRAM_SUNXI_ODT_EN=0x84848484
+CONFIG_DRAM_SUNXI_TPR0=0x80808080
+CONFIG_DRAM_SUNXI_TPR1=0x06060606
+CONFIG_DRAM_SUNXI_TPR6=0x38000000
+CONFIG_DRAM_SUNXI_TPR10=0x802f3333
+CONFIG_DRAM_SUNXI_TPR11=0xc7c5c4c2
+CONFIG_DRAM_SUNXI_TPR12=0x3533302f
+CONFIG_MACH_SUN55I_A523=y
+CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+CONFIG_R_I2C_ENABLE=y
+# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SPL_I2C=y
+CONFIG_SPL_SYS_I2C_LEGACY=y
+CONFIG_SYS_I2C_MVTWSI=y
+CONFIG_SYS_I2C_SLAVE=0x7f
+CONFIG_SYS_I2C_SPEED=400000
+CONFIG_SUPPORT_EMMC_BOOT=y
+CONFIG_REGULATOR_AXP=y
+CONFIG_AXP717_POWER=y
+CONFIG_AXP_I2C_ADDRESS=0x35
+CONFIG_AXP_DCDC2_VOLT=920
+CONFIG_AXP_DCDC3_VOLT=1160
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
diff --git a/configs/radxa-cubie-a5e_defconfig b/configs/radxa-cubie-a5e_defconfig
new file mode 100644
index 00000000000..f5958629226
--- /dev/null
+++ b/configs/radxa-cubie-a5e_defconfig
@@ -0,0 +1,32 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun55i-a527-cubie-a5e"
+CONFIG_SPL=y
+CONFIG_DRAM_SUNXI_DX_ODT=0x07070707
+CONFIG_DRAM_SUNXI_DX_DRI=0x0d0d0d0d
+CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e
+CONFIG_DRAM_SUNXI_ODT_EN=0x84848484
+CONFIG_DRAM_SUNXI_TPR0=0x80808080
+CONFIG_DRAM_SUNXI_TPR1=0x06060606
+CONFIG_DRAM_SUNXI_TPR6=0x38000000
+CONFIG_DRAM_SUNXI_TPR10=0x802f3333
+CONFIG_DRAM_SUNXI_TPR11=0xc7c5c4c2
+CONFIG_DRAM_SUNXI_TPR12=0x3533302f
+# BSP value: CONFIG_DRAM_SUNXI_TPR11=0xc6c4c2c0
+# BSP value: CONFIG_DRAM_SUNXI_TPR12=0x3a373233
+CONFIG_MACH_SUN55I_A523=y
+CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+CONFIG_R_I2C_ENABLE=y
+# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SPL_I2C=y
+CONFIG_SPL_SYS_I2C_LEGACY=y
+CONFIG_SYS_I2C_MVTWSI=y
+CONFIG_SYS_I2C_SLAVE=0x7f
+CONFIG_SYS_I2C_SPEED=400000
+CONFIG_SUPPORT_EMMC_BOOT=y
+CONFIG_REGULATOR_AXP=y
+CONFIG_AXP717_POWER=y
+CONFIG_AXP_DCDC2_VOLT=920
+CONFIG_AXP_DCDC3_VOLT=1100
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
diff --git a/configs/x96q_pro_plus_defconfig b/configs/x96q_pro_plus_defconfig
new file mode 100644
index 00000000000..07d3b078bef
--- /dev/null
+++ b/configs/x96q_pro_plus_defconfig
@@ -0,0 +1,32 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun55i-h728-x96qpro+"
+CONFIG_SPL=y
+CONFIG_DRAM_SUNXI_DX_ODT=0x07070707
+CONFIG_DRAM_SUNXI_DX_DRI=0x0c0c0c0c
+CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e
+CONFIG_DRAM_SUNXI_ODT_EN=0x90909090
+CONFIG_DRAM_SUNXI_TPR0=0x80808080
+CONFIG_DRAM_SUNXI_TPR1=0x06060606
+CONFIG_DRAM_SUNXI_TPR6=0x3380807e
+CONFIG_DRAM_SUNXI_TPR10=0x802f7788
+CONFIG_DRAM_SUNXI_TPR11=0x8f919190
+CONFIG_DRAM_SUNXI_TPR12=0x22222723
+CONFIG_MACH_SUN55I_A523=y
+CONFIG_SUNXI_DRAM_A523_DDR3=y
+CONFIG_DRAM_CLK=792
+CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+CONFIG_R_I2C_ENABLE=y
+# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SPL_I2C=y
+CONFIG_SPL_SYS_I2C_LEGACY=y
+CONFIG_SYS_I2C_MVTWSI=y
+CONFIG_SYS_I2C_SLAVE=0x7f
+CONFIG_SYS_I2C_SPEED=400000
+CONFIG_SUPPORT_EMMC_BOOT=y
+CONFIG_REGULATOR_AXP=y
+CONFIG_AXP717_POWER=y
+CONFIG_AXP_DCDC2_VOLT=920
+CONFIG_AXP_DCDC3_VOLT=1360
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig
index f44db76c182..1c1cc82719c 100644
--- a/drivers/clk/sunxi/Kconfig
+++ b/drivers/clk/sunxi/Kconfig
@@ -129,4 +129,18 @@ config CLK_SUN50I_A100
This enables common clock driver support for platforms based
on Allwinner A100/A133 SoCs.
+config CLK_SUN55I_A523
+ bool "Clock driver for Allwinner A523/T527"
+ default MACH_SUN55I_A523
+ help
+ This enables common clock driver support for platforms based
+ on Allwinner A523/T527 SoC.
+
+config CLK_SUN55I_A523_R
+ bool "Clock driver for Allwinner A523 generation PRCM"
+ default MACH_SUN55I_A523
+ help
+ This enables common clock driver support for the PRCM
+ in Allwinner A523/T527 SoCs.
+
endif # CLK_SUNXI
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 7ff71c756e0..93b542cebcd 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -25,3 +25,5 @@ obj-$(CONFIG_CLK_SUN50I_H6_R) += clk_h6_r.o
obj-$(CONFIG_CLK_SUN50I_H616) += clk_h616.o
obj-$(CONFIG_CLK_SUN50I_A64) += clk_a64.o
obj-$(CONFIG_CLK_SUN50I_A100) += clk_a100.o
+obj-$(CONFIG_CLK_SUN55I_A523) += clk_a523.o
+obj-$(CONFIG_CLK_SUN55I_A523_R) += clk_a523_r.o
diff --git a/drivers/clk/sunxi/clk_a523.c b/drivers/clk/sunxi/clk_a523.c
new file mode 100644
index 00000000000..1de95fbaf2f
--- /dev/null
+++ b/drivers/clk/sunxi/clk_a523.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2024 Arm Ltd.
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <clk/sunxi.h>
+#include <linux/bitops.h>
+
+#include <dt-bindings/clock/sun55i-a523-ccu.h>
+#include <dt-bindings/reset/sun55i-a523-ccu.h>
+
+static struct ccu_clk_gate a523_gates[] = {
+ [CLK_PLL_PERIPH0_200M] = GATE_DUMMY,
+ [CLK_APB1] = GATE_DUMMY,
+
+ [CLK_BUS_MMC0] = GATE(0x84c, BIT(0)),
+ [CLK_BUS_MMC1] = GATE(0x84c, BIT(1)),
+ [CLK_BUS_MMC2] = GATE(0x84c, BIT(2)),
+ [CLK_BUS_UART0] = GATE(0x90c, BIT(0)),
+ [CLK_BUS_UART1] = GATE(0x90c, BIT(1)),
+ [CLK_BUS_UART2] = GATE(0x90c, BIT(2)),
+ [CLK_BUS_UART3] = GATE(0x90c, BIT(3)),
+ [CLK_BUS_UART4] = GATE(0x90c, BIT(4)),
+ [CLK_BUS_UART5] = GATE(0x90c, BIT(5)),
+ [CLK_BUS_I2C0] = GATE(0x91c, BIT(0)),
+ [CLK_BUS_I2C1] = GATE(0x91c, BIT(1)),
+ [CLK_BUS_I2C2] = GATE(0x91c, BIT(2)),
+ [CLK_BUS_I2C3] = GATE(0x91c, BIT(3)),
+ [CLK_SPI0] = GATE(0x940, BIT(31)),
+ [CLK_SPI1] = GATE(0x944, BIT(31)),
+ [CLK_BUS_SPI0] = GATE(0x96c, BIT(0)),
+ [CLK_BUS_SPI1] = GATE(0x96c, BIT(1)),
+
+ [CLK_EMAC0_25M] = GATE(0x970, BIT(30) | BIT(31)),
+ [CLK_EMAC1_25M] = GATE(0x974, BIT(30) | BIT(31)),
+ [CLK_BUS_EMAC0] = GATE(0x97c, BIT(0)),
+ [CLK_BUS_EMAC1] = GATE(0x98c, BIT(0)),
+
+ [CLK_USB_OHCI0] = GATE(0xa70, BIT(31)),
+ [CLK_USB_OHCI1] = GATE(0xa74, BIT(31)),
+ [CLK_BUS_OHCI0] = GATE(0xa8c, BIT(0)),
+ [CLK_BUS_OHCI1] = GATE(0xa8c, BIT(1)),
+ [CLK_BUS_EHCI0] = GATE(0xa8c, BIT(4)),
+ [CLK_BUS_EHCI1] = GATE(0xa8c, BIT(5)),
+ [CLK_BUS_OTG] = GATE(0xa8c, BIT(8)),
+};
+
+static struct ccu_reset a523_resets[] = {
+ [RST_BUS_MMC0] = RESET(0x84c, BIT(16)),
+ [RST_BUS_MMC1] = RESET(0x84c, BIT(17)),
+ [RST_BUS_MMC2] = RESET(0x84c, BIT(18)),
+ [RST_BUS_UART0] = RESET(0x90c, BIT(16)),
+ [RST_BUS_UART1] = RESET(0x90c, BIT(17)),
+ [RST_BUS_UART2] = RESET(0x90c, BIT(18)),
+ [RST_BUS_UART3] = RESET(0x90c, BIT(19)),
+ [RST_BUS_UART4] = RESET(0x90c, BIT(20)),
+ [RST_BUS_UART5] = RESET(0x90c, BIT(21)),
+ [RST_BUS_I2C0] = RESET(0x91c, BIT(16)),
+ [RST_BUS_I2C1] = RESET(0x91c, BIT(17)),
+ [RST_BUS_I2C2] = RESET(0x91c, BIT(18)),
+ [RST_BUS_I2C3] = RESET(0x91c, BIT(19)),
+ [RST_BUS_SPI0] = RESET(0x96c, BIT(16)),
+ [RST_BUS_SPI1] = RESET(0x96c, BIT(17)),
+
+ [RST_BUS_EMAC0] = RESET(0x97c, BIT(16)),
+ [RST_BUS_EMAC1] = RESET(0x98c, BIT(16) | BIT(17)),
+
+ [RST_USB_PHY0] = RESET(0xa70, BIT(30)),
+ [RST_USB_PHY1] = RESET(0xa74, BIT(30)),
+ [RST_BUS_OHCI0] = RESET(0xa8c, BIT(16)),
+ [RST_BUS_OHCI1] = RESET(0xa8c, BIT(17)),
+ [RST_BUS_EHCI0] = RESET(0xa8c, BIT(20)),
+ [RST_BUS_EHCI1] = RESET(0xa8c, BIT(21)),
+ [RST_BUS_OTG] = RESET(0xa8c, BIT(24)),
+};
+
+const struct ccu_desc a523_ccu_desc = {
+ .gates = a523_gates,
+ .resets = a523_resets,
+ .num_gates = ARRAY_SIZE(a523_gates),
+ .num_resets = ARRAY_SIZE(a523_resets),
+};
diff --git a/drivers/clk/sunxi/clk_a523_r.c b/drivers/clk/sunxi/clk_a523_r.c
new file mode 100644
index 00000000000..01e613d20aa
--- /dev/null
+++ b/drivers/clk/sunxi/clk_a523_r.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2024 Arm Ltd.
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <clk/sunxi.h>
+#include <dt-bindings/clock/sun55i-a523-r-ccu.h>
+#include <dt-bindings/reset/sun55i-a523-r-ccu.h>
+#include <linux/bitops.h>
+
+static struct ccu_clk_gate a523_r_gates[] = {
+ [CLK_R_AHB] = GATE_DUMMY,
+ [CLK_R_APB0] = GATE_DUMMY,
+ [CLK_R_APB1] = GATE_DUMMY,
+ [CLK_BUS_R_TWD] = GATE(0x12c, BIT(0)),
+ [CLK_BUS_R_I2C0] = GATE(0x19c, BIT(0)),
+ [CLK_BUS_R_I2C1] = GATE(0x19c, BIT(1)),
+ [CLK_BUS_R_I2C2] = GATE(0x19c, BIT(2)),
+ [CLK_BUS_R_RTC] = GATE(0x20c, BIT(0)),
+};
+
+static struct ccu_reset a523_r_resets[] = {
+ [RST_BUS_R_TWD] = RESET(0x12c, BIT(16)),
+ [RST_BUS_R_UART0] = RESET(0x18c, BIT(16)),
+ [RST_BUS_R_I2C0] = RESET(0x19c, BIT(16)),
+ [RST_BUS_R_I2C1] = RESET(0x19c, BIT(17)),
+ [RST_BUS_R_I2C2] = RESET(0x19c, BIT(18)),
+ [RST_BUS_R_PPU1] = RESET(0x1ac, BIT(17)),
+ [RST_BUS_R_RTC] = RESET(0x20c, BIT(16)),
+};
+
+const struct ccu_desc a523_r_ccu_desc = {
+ .gates = a523_r_gates,
+ .resets = a523_r_resets,
+ .num_gates = ARRAY_SIZE(a523_r_gates),
+ .num_resets = ARRAY_SIZE(a523_r_resets),
+};
diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c
index e0765cbc6dc..842a0541bd6 100644
--- a/drivers/clk/sunxi/clk_sunxi.c
+++ b/drivers/clk/sunxi/clk_sunxi.c
@@ -126,6 +126,8 @@ extern const struct ccu_desc a100_ccu_desc;
extern const struct ccu_desc h6_r_ccu_desc;
extern const struct ccu_desc r40_ccu_desc;
extern const struct ccu_desc v3s_ccu_desc;
+extern const struct ccu_desc a523_ccu_desc;
+extern const struct ccu_desc a523_r_ccu_desc;
static const struct udevice_id sunxi_clk_ids[] = {
#ifdef CONFIG_CLK_SUN4I_A10
@@ -224,6 +226,14 @@ static const struct udevice_id sunxi_clk_ids[] = {
{ .compatible = "allwinner,suniv-f1c100s-ccu",
.data = (ulong)&f1c100s_ccu_desc },
#endif
+#ifdef CONFIG_CLK_SUN55I_A523
+ { .compatible = "allwinner,sun55i-a523-ccu",
+ .data = (ulong)&a523_ccu_desc },
+#endif
+#ifdef CONFIG_CLK_SUN55I_A523_R
+ { .compatible = "allwinner,sun55i-a523-r-ccu",
+ .data = (ulong)&a523_r_ccu_desc },
+#endif
{ }
};
diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
index 06c1e09bf26..e28c81afffe 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -99,6 +99,19 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
*/
if (IS_ENABLED(CONFIG_MACH_SUN8I_R528))
pll_hz /= 2;
+
+ /*
+ * The A523/T527 uses PERIPH0_400M as the MMC0/1 input clock,
+ * and PERIPH0_800M for MMC2. There is also the hidden divider
+ * of 2. The clock code reports 600 MHz for PERIPH0.
+ * Adjust the calculation accordingly: 600 * hidden2 / 3 for
+ * MMC0/1, and 600 * hidden2 / 3 * 2 for MMC2.
+ */
+ if (IS_ENABLED(CONFIG_MACH_SUN55I_A523)) {
+ pll_hz /= 3;
+ if (priv->mmc_no == 2)
+ pll_hz *= 2;
+ }
}
div = pll_hz / hz;
@@ -153,6 +166,10 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
CCM_MMC_CTRL_SCLK_DLY(sclk_dly);
}
+ /* The A523 has a second divider, not a shift. */
+ if (IS_ENABLED(CONFIG_MACH_SUN55I_A523))
+ n = (1U << n) - 1;
+
writel(CCM_MMC_CTRL_ENABLE| pll | CCM_MMC_CTRL_N(n) |
CCM_MMC_CTRL_M(div) | val, priv->mclkreg);
@@ -559,7 +576,8 @@ struct mmc *sunxi_mmc_init(int sdc_no)
cfg->host_caps = MMC_MODE_4BIT;
if ((IS_ENABLED(CONFIG_MACH_SUN50I) || IS_ENABLED(CONFIG_MACH_SUN8I) ||
- IS_ENABLED(CONFIG_SUN50I_GEN_H6)) && (sdc_no == 2))
+ IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_MACH_SUN55I_A523)) &&
+ (sdc_no == 2))
cfg->host_caps = MMC_MODE_8BIT;
cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
index 65e8192a99a..54314992299 100644
--- a/drivers/pinctrl/sunxi/Kconfig
+++ b/drivers/pinctrl/sunxi/Kconfig
@@ -139,4 +139,14 @@ config PINCTRL_SUN20I_D1
default MACH_SUN8I_R528
select PINCTRL_SUNXI
+config PINCTRL_SUN55I_A523
+ bool "Support for the Allwinner A523 PIO"
+ default MACH_SUN55I_A523
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN55I_A523_R
+ bool "Support for the Allwinner A523 R-PIO"
+ default MACH_SUN55I_A523
+ select PINCTRL_SUNXI
+
endif
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index c38edf7d4f5..03cfe23aaf8 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -759,6 +759,29 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h616_pinctrl_desc =
.num_banks = 9,
};
+static const struct sunxi_pinctrl_function sun55i_a523_pinctrl_functions[] = {
+ { "emac0", 5 }, /* PI0-PI16 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC0-PC16 */
+ { "spi0", 4 }, /* PC0-PC7, PC15-PC16 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PH0-PH1 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun55i_a523_pinctrl_desc = {
+ .functions = sun55i_a523_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun55i_a523_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 11,
+};
+
static const struct sunxi_pinctrl_function sun50i_h616_r_pinctrl_functions[] = {
{ "gpio_in", 0 },
{ "gpio_out", 1 },
@@ -809,6 +832,21 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a100_r_pinctrl_desc
.num_banks = 1,
};
+static const struct sunxi_pinctrl_function sun55i_a523_r_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "r_i2c0", 2 }, /* PL0-PL1 */
+ { "r_spi", 6 }, /* PL10-PL13 */
+ { "r_uart", 2 }, /* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun55i_a523_r_pinctrl_desc = {
+ .functions = sun55i_a523_r_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun55i_a523_r_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_L,
+ .num_banks = 2,
+};
+
static const struct udevice_id sunxi_pinctrl_ids[] = {
#ifdef CONFIG_PINCTRL_SUNIV_F1C100S
{
@@ -984,6 +1022,18 @@ static const struct udevice_id sunxi_pinctrl_ids[] = {
.data = (ulong)&sun50i_a100_r_pinctrl_desc,
},
#endif
+#ifdef CONFIG_PINCTRL_SUN55I_A523
+ {
+ .compatible = "allwinner,sun55i-a523-pinctrl",
+ .data = (ulong)&sun55i_a523_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN55I_A523_R
+ {
+ .compatible = "allwinner,sun55i-a523-r-pinctrl",
+ .data = (ulong)&sun55i_a523_r_pinctrl_desc,
+ },
+#endif
{}
};
diff --git a/drivers/power/pmic/axp.c b/drivers/power/pmic/axp.c
index c300fd2bbc2..1204ec00f8d 100644
--- a/drivers/power/pmic/axp.c
+++ b/drivers/power/pmic/axp.c
@@ -89,6 +89,7 @@ static const struct udevice_id axp_pmic_ids[] = {
{ .compatible = "x-powers,axp221", .data = AXP221_ID },
{ .compatible = "x-powers,axp223", .data = AXP223_ID },
{ .compatible = "x-powers,axp313a", .data = AXP313_ID },
+ { .compatible = "x-powers,axp323", .data = AXP323_ID },
{ .compatible = "x-powers,axp717", .data = AXP717_ID },
{ .compatible = "x-powers,axp803", .data = AXP803_ID },
{ .compatible = "x-powers,axp806", .data = AXP806_ID },
diff --git a/drivers/power/regulator/axp_regulator.c b/drivers/power/regulator/axp_regulator.c
index 75cdbca30f6..7794a4f5d92 100644
--- a/drivers/power/regulator/axp_regulator.c
+++ b/drivers/power/regulator/axp_regulator.c
@@ -318,6 +318,7 @@ static const struct axp_regulator_plat *const axp_regulators[] = {
[AXP221_ID] = axp22x_regulators,
[AXP223_ID] = axp22x_regulators,
[AXP313_ID] = axp313_regulators,
+ [AXP323_ID] = axp313_regulators,
[AXP717_ID] = axp717_regulators,
[AXP803_ID] = axp803_regulators,
[AXP806_ID] = axp806_regulators,
diff --git a/dts/upstream/src/arm64/allwinner/sun55i-a523.dtsi b/dts/upstream/src/arm64/allwinner/sun55i-a523.dtsi
new file mode 100644
index 00000000000..ee485899ba0
--- /dev/null
+++ b/dts/upstream/src/arm64/allwinner/sun55i-a523.dtsi
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+// Copyright (C) 2023-2024 Arm Ltd.
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/clock/sun6i-rtc.h>
+#include <dt-bindings/clock/sun55i-a523-ccu.h>
+#include <dt-bindings/clock/sun55i-a523-r-ccu.h>
+#include <dt-bindings/reset/sun55i-a523-ccu.h>
+#include <dt-bindings/reset/sun55i-a523-r-ccu.h>
+
+/ {
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x000>;
+ enable-method = "psci";
+ };
+
+ cpu1: cpu@100 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x100>;
+ enable-method = "psci";
+ };
+
+ cpu2: cpu@200 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x200>;
+ enable-method = "psci";
+ };
+
+ cpu3: cpu@300 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x300>;
+ enable-method = "psci";
+ };
+
+ cpu4: cpu@400 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x400>;
+ enable-method = "psci";
+ };
+
+ cpu5: cpu@500 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x500>;
+ enable-method = "psci";
+ };
+
+ cpu6: cpu@600 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x600>;
+ enable-method = "psci";
+ };
+
+ cpu7: cpu@700 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x700>;
+ enable-method = "psci";
+ };
+ };
+
+ osc24M: osc24M-clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ clock-output-names = "osc24M";
+ };
+
+ pmu {
+ compatible = "arm,cortex-a55-pmu";
+ interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ psci {
+ compatible = "arm,psci-0.2";
+ method = "smc";
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ arm,no-tick-in-suspend;
+ interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_PPI 14 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_PPI 10 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x0 0x0 0x40000000>;
+
+ pio: pinctrl@2000000 {
+ compatible = "allwinner,sun55i-a523-pinctrl";
+ reg = <0x2000000 0x800>;
+ interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_APB1>, <&osc24M>, <&rtc CLK_OSC32K>;
+ clock-names = "apb", "hosc", "losc";
+ gpio-controller;
+ #gpio-cells = <3>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ mmc0_pins: mmc0-pins {
+ pins = "PF0" ,"PF1", "PF2", "PF3", "PF4", "PF5";
+ allwinner,pinmux = <2>;
+ function = "mmc0";
+ drive-strength = <30>;
+ bias-pull-up;
+ };
+
+ /omit-if-no-ref/
+ mmc1_pins: mmc1-pins {
+ pins = "PG0" ,"PG1", "PG2", "PG3", "PG4", "PG5";
+ allwinner,pinmux = <2>;
+ function = "mmc1";
+ drive-strength = <30>;
+ bias-pull-up;
+ };
+
+ mmc2_pins: mmc2-pins {
+ pins = "PC0", "PC1" ,"PC5", "PC6", "PC8",
+ "PC9", "PC10", "PC11", "PC13", "PC14",
+ "PC15", "PC16";
+ allwinner,pinmux = <3>;
+ function = "mmc2";
+ drive-strength = <30>;
+ bias-pull-up;
+ };
+
+ uart0_pb_pins: uart0-pb-pins {
+ pins = "PB9", "PB10";
+ allwinner,pinmux = <2>;
+ function = "uart0";
+ };
+ };
+
+ ccu: clock-controller@2001000 {
+ compatible = "allwinner,sun55i-a523-ccu";
+ reg = <0x02001000 0x1000>;
+ clocks = <&osc24M>, <&rtc CLK_OSC32K>,
+ <&rtc CLK_IOSC>, <&rtc CLK_OSC32K_FANOUT>;
+ clock-names = "hosc", "losc",
+ "iosc", "losc-fanout";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ mmc0: mmc@4020000 {
+ compatible = "allwinner,sun55i-a523-mmc",
+ "allwinner,sun20i-d1-mmc";
+ reg = <0x04020000 0x1000>;
+ clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>;
+ clock-names = "ahb", "mmc";
+ resets = <&ccu RST_BUS_MMC0>;
+ reset-names = "ahb";
+ interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc0_pins>;
+ status = "disabled";
+
+ max-frequency = <150000000>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ cap-sdio-irq;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ mmc1: mmc@4021000 {
+ compatible = "allwinner,sun55i-a523-mmc",
+ "allwinner,sun20i-d1-mmc";
+ reg = <0x04021000 0x1000>;
+ clocks = <&ccu CLK_BUS_MMC1>, <&ccu CLK_MMC1>;
+ clock-names = "ahb", "mmc";
+ resets = <&ccu RST_BUS_MMC1>;
+ reset-names = "ahb";
+ interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
+ status = "disabled";
+
+ max-frequency = <150000000>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ cap-sdio-irq;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ mmc2: mmc@4022000 {
+ compatible = "allwinner,sun55i-a523-mmc",
+ "allwinner,sun20i-d1-mmc";
+ reg = <0x04022000 0x1000>;
+ clocks = <&ccu CLK_BUS_MMC2>, <&ccu CLK_MMC2>;
+ clock-names = "ahb", "mmc";
+ resets = <&ccu RST_BUS_MMC2>;
+ reset-names = "ahb";
+ interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc2_pins>;
+ status = "disabled";
+
+ max-frequency = <150000000>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ cap-sdio-irq;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ wdt: watchdog@2050000 {
+ compatible = "allwinner,sun55i-a523-wdt";
+ reg = <0x2050000 0x20>;
+ interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&osc24M>, <&rtc CLK_OSC32K>;
+ clock-names = "hosc", "losc";
+ status = "okay";
+ };
+
+ uart0: serial@2500000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02500000 0x400>;
+ interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART0>;
+ resets = <&ccu RST_BUS_UART0>;
+ status = "disabled";
+ };
+
+ uart1: serial@2500400 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02500400 0x400>;
+ interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART1>;
+ resets = <&ccu RST_BUS_UART1>;
+ status = "disabled";
+ };
+
+ uart2: serial@2500800 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02500800 0x400>;
+ interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART2>;
+ resets = <&ccu RST_BUS_UART2>;
+ status = "disabled";
+ };
+
+ uart3: serial@2500c00 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02500c00 0x400>;
+ interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART3>;
+ resets = <&ccu RST_BUS_UART3>;
+ status = "disabled";
+ };
+
+ uart4: serial@2501000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02501000 0x400>;
+ interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART4>;
+ resets = <&ccu RST_BUS_UART4>;
+ status = "disabled";
+ };
+
+ uart5: serial@2501400 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02501400 0x400>;
+ interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART5>;
+ resets = <&ccu RST_BUS_UART5>;
+ status = "disabled";
+ };
+
+ uart6: serial@2501800 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02501800 0x400>;
+ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART6>;
+ resets = <&ccu RST_BUS_UART6>;
+ status = "disabled";
+ };
+
+ uart7: serial@2501c00 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02501c00 0x400>;
+ interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART7>;
+ resets = <&ccu RST_BUS_UART7>;
+ status = "disabled";
+ };
+
+ i2c0: i2c@2502000 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2502000 0x400>;
+ interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C0>;
+ resets = <&ccu RST_BUS_I2C0>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c1: i2c@2502400 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2502400 0x400>;
+ interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C1>;
+ resets = <&ccu RST_BUS_I2C1>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c2: i2c@2502800 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2502800 0x400>;
+ interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C2>;
+ resets = <&ccu RST_BUS_I2C2>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c3: i2c@2502c00 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2502c00 0x400>;
+ interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C3>;
+ resets = <&ccu RST_BUS_I2C3>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c4: i2c@2503000 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2503000 0x400>;
+ interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C4>;
+ resets = <&ccu RST_BUS_I2C4>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c5: i2c@2503400 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2503400 0x400>;
+ interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C5>;
+ resets = <&ccu RST_BUS_I2C5>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ gic: interrupt-controller@3400000 {
+ compatible = "arm,gic-v3";
+ #address-cells = <1>;
+ #interrupt-cells = <3>;
+ #size-cells = <1>;
+ ranges;
+ interrupt-controller;
+ reg = <0x3400000 0x10000>,
+ <0x3460000 0x100000>;
+ interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ dma-noncoherent;
+
+ its: msi-controller@3440000 {
+ compatible = "arm,gic-v3-its";
+ reg = <0x3440000 0x20000>;
+ msi-controller;
+ #msi-cells = <1>;
+ dma-noncoherent;
+ };
+ };
+
+ usb_otg: usb@4100000 {
+ compatible = "allwinner,sun55i-a523-musb",
+ "allwinner,sun8i-a33-musb";
+ reg = <0x4100000 0x400>;
+ interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "mc";
+ clocks = <&ccu CLK_BUS_OTG>;
+ resets = <&ccu RST_BUS_OTG>;
+ extcon = <&usbphy 0>;
+ phys = <&usbphy 0>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ usbphy: phy@4100400 {
+ compatible = "allwinner,sun55i-a523-usb-phy",
+ "allwinner,sun20i-d1-usb-phy";
+ reg = <0x4100400 0x100>,
+ <0x4101800 0x100>,
+ <0x4200800 0x100>;
+ reg-names = "phy_ctrl",
+ "pmu0",
+ "pmu1";
+ clocks = <&osc24M>,
+ <&osc24M>;
+ clock-names = "usb0_phy",
+ "usb1_phy";
+ resets = <&ccu RST_USB_PHY0>,
+ <&ccu RST_USB_PHY1>;
+ reset-names = "usb0_reset",
+ "usb1_reset";
+ status = "disabled";
+ #phy-cells = <1>;
+ };
+
+ ehci0: usb@4101000 {
+ compatible = "allwinner,sun55i-a523-ehci",
+ "generic-ehci";
+ reg = <0x4101000 0x100>;
+ interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_OHCI0>,
+ <&ccu CLK_BUS_EHCI0>,
+ <&ccu CLK_USB_OHCI0>;
+ resets = <&ccu RST_BUS_OHCI0>,
+ <&ccu RST_BUS_EHCI0>;
+ phys = <&usbphy 0>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci0: usb@4101400 {
+ compatible = "allwinner,sun55i-a523-ohci",
+ "generic-ohci";
+ reg = <0x4101400 0x100>;
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_OHCI0>,
+ <&ccu CLK_USB_OHCI0>;
+ resets = <&ccu RST_BUS_OHCI0>;
+ phys = <&usbphy 0>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ehci1: usb@4200000 {
+ compatible = "allwinner,sun55i-a523-ehci",
+ "generic-ehci";
+ reg = <0x4200000 0x100>;
+ interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_OHCI1>,
+ <&ccu CLK_BUS_EHCI1>,
+ <&ccu CLK_USB_OHCI1>;
+ resets = <&ccu RST_BUS_OHCI1>,
+ <&ccu RST_BUS_EHCI1>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci1: usb@4200400 {
+ compatible = "allwinner,sun55i-a523-ohci",
+ "generic-ohci";
+ reg = <0x4200400 0x100>;
+ interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_OHCI1>,
+ <&ccu CLK_USB_OHCI1>;
+ resets = <&ccu RST_BUS_OHCI1>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ r_ccu: clock-controller@7010000 {
+ compatible = "allwinner,sun55i-a523-r-ccu";
+ reg = <0x7010000 0x250>;
+ clocks = <&osc24M>,
+ <&rtc CLK_OSC32K>,
+ <&rtc CLK_IOSC>,
+ <&ccu CLK_PLL_PERIPH0_200M>,
+ <&ccu CLK_PLL_AUDIO0_4X>;
+ clock-names = "hosc",
+ "losc",
+ "iosc",
+ "pll-periph",
+ "pll-audio";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ nmi_intc: interrupt-controller@7010320 {
+ compatible = "allwinner,sun55i-a523-nmi";
+ reg = <0x07010320 0xc>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ r_pio: pinctrl@7022000 {
+ compatible = "allwinner,sun55i-a523-r-pinctrl";
+ reg = <0x7022000 0x800>;
+ interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&r_ccu CLK_R_APB0>,
+ <&osc24M>,
+ <&rtc CLK_OSC32K>;
+ clock-names = "apb", "hosc", "losc";
+ gpio-controller;
+ #gpio-cells = <3>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ r_i2c_pins: r-i2c-pins {
+ pins = "PL0" ,"PL1";
+ allwinner,pinmux = <2>;
+ function = "r_i2c0";
+ };
+ };
+
+ r_i2c0: i2c@7081400 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x07081400 0x400>;
+ interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&r_ccu CLK_BUS_R_I2C0>;
+ resets = <&r_ccu RST_BUS_R_I2C0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&r_i2c_pins>;
+ status = "disabled";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ rtc: rtc@7090000 {
+ compatible = "allwinner,sun55i-a523-rtc",
+ "allwinner,sun50i-r329-rtc";
+ reg = <0x7090000 0x400>;
+ interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&r_ccu CLK_BUS_R_RTC>,
+ <&osc24M>,
+ <&r_ccu CLK_R_AHB>;
+ clock-names = "bus", "hosc", "ahb";
+ #clock-cells = <1>;
+ };
+ };
+};
diff --git a/dts/upstream/src/arm64/allwinner/sun55i-a527-cubie-a5e.dts b/dts/upstream/src/arm64/allwinner/sun55i-a527-cubie-a5e.dts
new file mode 100644
index 00000000000..ad9bd6da424
--- /dev/null
+++ b/dts/upstream/src/arm64/allwinner/sun55i-a527-cubie-a5e.dts
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+// Copyright (C) 2025 Arm Ltd.
+
+/dts-v1/;
+
+#include "sun55i-a523.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ model = "Radxa A5E";
+ compatible = "radxa,cubie-a5e", "allwinner,sun55i-a527";
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ ext_osc32k: ext-osc32k-clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ clock-output-names = "ext_osc32k";
+ };
+
+ reg_vcc5v: vcc5v {
+ /* board wide 5V supply from the USB-C connector */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-5v";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ reg_usb_vbus: vbus {
+ compatible = "regulator-fixed";
+ regulator-name = "usb-vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&reg_vcc5v>;
+ gpio = <&r_pio 0 8 GPIO_ACTIVE_HIGH>; /* PL8 */
+ enable-active-high;
+ };
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&mmc0 {
+ vmmc-supply = <&reg_cldo3>;
+ cd-gpios = <&pio 5 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* PF6 */
+ bus-width = <4>;
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&pio {
+ vcc-pb-supply = <&reg_cldo3>; /* via VCC-IO */
+ vcc-pc-supply = <&reg_cldo1>;
+ vcc-pd-supply = <&reg_cldo3>;
+ vcc-pe-supply = <&reg_aldo2>;
+ vcc-pf-supply = <&reg_cldo3>; /* actually switchable */
+ vcc-pg-supply = <&reg_bldo1>;
+ vcc-ph-supply = <&reg_cldo3>; /* via VCC-IO */
+ vcc-pi-supply = <&reg_cldo3>;
+ vcc-pj-supply = <&reg_cldo4>;
+ vcc-pk-supply = <&reg_cldo1>;
+};
+
+&r_i2c0 {
+ status = "okay";
+
+ axp717: pmic@34 {
+ compatible = "x-powers,axp717";
+ reg = <0x34>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+
+ vin1-supply = <&reg_vcc5v>;
+ vin2-supply = <&reg_vcc5v>;
+ vin3-supply = <&reg_vcc5v>;
+ vin4-supply = <&reg_vcc5v>;
+ aldoin-supply = <&reg_vcc5v>;
+ bldoin-supply = <&reg_vcc5v>;
+ cldoin-supply = <&reg_vcc5v>;
+
+ regulators {
+ /* Supplies the "little" cluster (1.4 GHz cores) */
+ reg_dcdc1: dcdc1 {
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1160000>;
+ regulator-name = "vdd-cpul";
+ };
+
+ reg_dcdc2: dcdc2 {
+ regulator-always-on;
+ regulator-min-microvolt = <920000>;
+ regulator-max-microvolt = <920000>;
+ regulator-name = "vdd-gpu-sys";
+ };
+
+ reg_dcdc3: dcdc3 {
+ regulator-always-on;
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-name = "vdd-dram";
+ };
+
+ reg_aldo1: aldo1 {
+ /* not connected */
+ };
+
+ reg_aldo2: aldo2 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-pe";
+ };
+
+ reg_aldo3: aldo3 {
+ /* supplies the I2C pins for this PMIC */
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-pl-usb";
+ };
+
+ reg_aldo4: aldo4 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-pll-dxco-avcc";
+ };
+
+ reg_bldo1: bldo1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-pg-iowifi";
+ };
+
+ reg_bldo2: bldo2 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-pm-lpddr4";
+ };
+
+ reg_bldo3: bldo3 {
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-mipi-cam";
+ };
+
+ reg_bldo4: bldo4 {
+ /* not connected */
+ };
+
+ reg_cldo1: cldo1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-pc-and-their-dog";
+ };
+
+ reg_cldo2: cldo2 {
+ /* not connected */
+ };
+
+ reg_cldo3: cldo3 {
+ /* IO, USB-2, 3V3, card, NAND, sensor, PI */
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-io-mmc-spi-ana";
+ };
+
+ reg_cldo4: cldo4 {
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-pj-phy";
+ };
+
+ reg_cpusldo: cpusldo {
+ /* supplies the management core */
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ regulator-name = "vdd-cpus";
+ };
+ };
+ };
+
+ axp323: pmic@36 {
+ compatible = "x-powers,axp323";
+ reg = <0x36>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ status = "okay";
+
+ vin1-supply = <&reg_vcc5v>;
+ vin2-supply = <&reg_vcc5v>;
+ vin3-supply = <&reg_vcc5v>;
+
+ regulators {
+ aldo1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-mipi-dsi";
+ };
+
+ dldo1 {
+ /* not connected */
+ };
+
+ /* Supplies the "big" cluster (1.8 GHz cores) */
+ reg_dcdc1_323: dcdc1 {
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1160000>;
+ regulator-name = "vdd-cpub";
+ };
+
+ /* DCDC2 is polyphased with DCDC1 */
+
+ /* RISC-V management core supply */
+ reg_dcdc3_323: dcdc3 {
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ regulator-name = "vdd-dnr";
+ };
+ };
+ };
+};
+
+&r_pio {
+/*
+ * Specifying the supply would create a circular dependency.
+ *
+ * vcc-pl-supply = <&reg_aldo3>;
+ */
+ vcc-pm-supply = <&reg_aldo3>;
+};
+
+&uart0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pb_pins>;
+ status = "okay";
+};
+
+&usb_otg {
+ /*
+ * The USB-C port is the primary power supply, so in this configuration
+ * relies on the other end of the USB cable to supply the VBUS power.
+ * So use this port in peripheral mode.
+ * It is possible to supply the board with the 5V pins on the GPIO
+ * header, and since the DCIN_5V line is hardwired to the USB-C VBUS
+ * pins, the port turns into a host port, unconditionally supplying
+ * power. The dr_mode property should be changed to "host" here, if
+ * users choose this setup.
+ */
+ dr_mode = "peripheral";
+ status = "okay";
+};
+
+/*
+ * The schematic describes USB0_ID (PL10), measuring VBUS_5V, which looks to
+ * be always on. Also there is USB-VBUSDET (PL2), which is measuring the same
+ * VBUS_5V. There is also DCIN_DET, which measures DCIN_5V, so the power
+ * input rail.
+ * None of them seem to make any sense in relation to detecting USB devices
+ * or whether there is power provided via any USB pins: they would always
+ * report high, otherwise the system wouldn't be running.
+ * The AXP717C provides proper USB-C CC pin functionality, but the PMIC is
+ * not connected to those pins of the USB-C connector.
+ */
+&usbphy {
+ usb0_vbus-supply = <&reg_vcc5v>;
+ usb1_vbus-supply = <&reg_usb_vbus>;
+ status = "okay";
+};
diff --git a/dts/upstream/src/arm64/allwinner/sun55i-h728-x96qpro+.dts b/dts/upstream/src/arm64/allwinner/sun55i-h728-x96qpro+.dts
new file mode 100644
index 00000000000..59db103546f
--- /dev/null
+++ b/dts/upstream/src/arm64/allwinner/sun55i-h728-x96qpro+.dts
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+// Copyright (C) 2024 Arm Ltd.
+
+/dts-v1/;
+
+#include "sun55i-a523.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ model = "X96Q Pro+";
+ compatible = "amediatech,x96q-pro-plus", "allwinner,sun55i-h728";
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ ext_osc32k: ext-osc32k-clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ clock-output-names = "ext_osc32k";
+ };
+
+ reg_vcc5v: vcc5v {
+ /* board wide 5V supply from the barrel plug */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-5v";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ reg_vcc3v3: vcc3v3 {
+ /* 3.3V dummy supply for the SD card */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ vin-supply = <&reg_vcc5v>;
+ regulator-always-on;
+ };
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&mmc0 {
+ vmmc-supply = <&reg_vcc3v3>;
+ cd-gpios = <&pio 5 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* PF6 */
+ bus-width = <4>;
+ disable-wp;
+ status = "okay";
+};
+
+&mmc2 {
+ vmmc-supply = <&reg_cldo3>;
+ vqmmc-supply = <&reg_cldo1>;
+ bus-width = <8>;
+ non-removable;
+ cap-mmc-hw-reset;
+ mmc-ddr-1_8v;
+ mmc-hs200-1_8v;
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&pio {
+ vcc-pb-supply = <&reg_cldo3>; /* via VCC-IO */
+ vcc-pc-supply = <&reg_cldo1>;
+ vcc-pd-supply = <&reg_dcdc4>;
+ vcc-pe-supply = <&reg_dcdc4>;
+ vcc-pf-supply = <&reg_cldo3>; /* actually switchable */
+ vcc-pg-supply = <&reg_bldo1>;
+ vcc-ph-supply = <&reg_cldo3>; /* via VCC-IO */
+ vcc-pi-supply = <&reg_dcdc4>;
+ vcc-pj-supply = <&reg_dcdc4>;
+ vcc-pk-supply = <&reg_bldo3>;
+};
+
+&r_i2c0 {
+ status = "okay";
+
+ axp717: pmic@34 {
+ compatible = "x-powers,axp717";
+ reg = <0x34>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+
+ vin1-supply = <&reg_vcc5v>;
+ vin2-supply = <&reg_vcc5v>;
+ vin3-supply = <&reg_vcc5v>;
+ vin4-supply = <&reg_vcc5v>;
+ aldoin-supply = <&reg_vcc5v>;
+ bldoin-supply = <&reg_vcc5v>;
+ cldoin-supply = <&reg_vcc5v>;
+
+ regulators {
+ /* Supplies the "little" cluster (1.0(?) GHz cores) */
+ reg_dcdc1: dcdc1 {
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1160000>;
+ regulator-name = "vdd-cpul";
+ };
+
+ reg_dcdc2: dcdc2 {
+ regulator-always-on;
+ regulator-min-microvolt = <920000>;
+ regulator-max-microvolt = <920000>;
+ regulator-name = "vdd-gpu-sys";
+ };
+
+ reg_dcdc3: dcdc3 {
+ regulator-always-on;
+ regulator-min-microvolt = <1360000>;
+ regulator-max-microvolt = <1360000>;
+ regulator-name = "vdd-dram";
+ };
+
+ reg_dcdc4: dcdc4 {
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-name = "vdd-dcdc4";
+ };
+
+ reg_aldo1: aldo1 {
+ /* not connected */
+ };
+
+ reg_aldo2: aldo2 {
+ /* not connected */
+ };
+
+ reg_aldo3: aldo3 {
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-aldo3";
+ };
+
+ reg_aldo4: aldo4 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-pll-dxco-avcc";
+ };
+
+ reg_bldo1: bldo1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-pg-wifi-lvds";
+ };
+
+ reg_bldo2: bldo2 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-dram-1v8";
+ };
+
+ reg_bldo3: bldo3 {
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-name = "vcc-bldo3";
+ };
+
+ reg_bldo4: bldo4 {
+ /* not connected */
+ };
+
+ reg_cldo1: cldo1 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-codec-sd";
+ };
+
+ reg_cldo2: cldo2 {
+ };
+
+ reg_cldo3: cldo3 {
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-codec-eth-sd";
+ };
+
+ reg_cldo4: cldo4 {
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-eth-phy";
+ };
+
+ reg_cpusldo: cpusldo {
+ /* supplies the management core */
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ regulator-name = "vdd-cpus";
+ };
+ };
+ };
+
+ axp323: pmic@36 {
+ compatible = "x-powers,axp323";
+ reg = <0x36>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ status = "okay";
+
+ vin1-supply = <&reg_vcc5v>;
+ vin2-supply = <&reg_vcc5v>;
+ vin3-supply = <&reg_vcc5v>;
+
+ regulators {
+ aldo1 {
+ /* not connected */
+ };
+
+ dldo1 {
+ /* not connected */
+ };
+
+ /* Supplies the "big" cluster (1.8 GHz cores) */
+ reg_dcdc1_323: dcdc1 {
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1160000>;
+ regulator-name = "vdd-cpub";
+ };
+
+ /* DCDC2 is polyphased with DCDC1 */
+
+ reg_dcdc3_323: dcdc3 {
+ regulator-always-on;
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1050000>;
+ regulator-name = "vdd-dcdc3";
+ };
+ };
+ };
+};
+
+&r_pio {
+/*
+ * Specifying the supply would create a circular dependency.
+ *
+ * vcc-pl-supply = <&reg_aldo3>;
+ */
+ vcc-pm-supply = <&reg_aldo3>;
+};
+
+&uart0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pb_pins>;
+ status = "okay";
+};
+
+&usb_otg {
+ /* USB0 is a USB-A receptacle, always powered, so force host mode. */
+ dr_mode = "host";
+ status = "okay";
+};
+
+&usbphy {
+ status = "okay";
+};
diff --git a/dts/upstream/src/arm64/allwinner/sun55i-t527-avaota-a1.dts b/dts/upstream/src/arm64/allwinner/sun55i-t527-avaota-a1.dts
new file mode 100644
index 00000000000..dea2acc1849
--- /dev/null
+++ b/dts/upstream/src/arm64/allwinner/sun55i-t527-avaota-a1.dts
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+// Copyright (C) 2024 Arm Ltd.
+
+/dts-v1/;
+
+#include "sun55i-a523.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ model = "Avaota A1";
+ compatible = "yuzukihd,avaota-a1", "allwinner,sun55i-t527";
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ ext_osc32k: ext-osc32k-clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ clock-output-names = "ext_osc32k";
+ };
+
+ reg_vcc12v: vcc12v {
+ /* DC input jack */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-12v";
+ regulator-min-microvolt = <12000000>;
+ regulator-max-microvolt = <12000000>;
+ regulator-always-on;
+ };
+
+ reg_vcc5v: vcc5v {
+ /* board wide 5V supply from the 12V->5V regulator */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-5v";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&reg_vcc12v>;
+ regulator-always-on;
+ };
+
+ reg_usb_vbus: vbus {
+ compatible = "regulator-fixed";
+ regulator-name = "usb-vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&reg_vcc5v>;
+ gpio = <&pio 8 12 GPIO_ACTIVE_HIGH>; /* PI12 */
+ enable-active-high;
+ };
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&mmc0 {
+ vmmc-supply = <&reg_cldo3>;
+ cd-gpios = <&pio 5 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* PF6 */
+ bus-width = <4>;
+ status = "okay";
+};
+
+&mmc2 {
+ bus-width = <8>;
+ cap-mmc-hw-reset;
+ mmc-ddr-1_8v;
+ mmc-hs200-1_8v;
+ non-removable;
+ vmmc-supply = <&reg_cldo3>;
+ vqmmc-supply = <&reg_cldo1>;
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&pio {
+ vcc-pb-supply = <&reg_cldo3>; /* via VCC-IO */
+ vcc-pc-supply = <&reg_cldo1>;
+ vcc-pd-supply = <&reg_dcdc4>;
+ vcc-pe-supply = <&reg_dcdc4>;
+ vcc-pf-supply = <&reg_cldo3>; /* actually switchable */
+ vcc-pg-supply = <&reg_bldo1>;
+ vcc-ph-supply = <&reg_cldo3>; /* via VCC-IO */
+ vcc-pi-supply = <&reg_dcdc4>;
+ vcc-pj-supply = <&reg_dcdc4>;
+ vcc-pk-supply = <&reg_bldo3>;
+};
+
+&r_i2c0 {
+ status = "okay";
+
+ axp717: pmic@35 {
+ compatible = "x-powers,axp717";
+ reg = <0x35>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+
+ vin1-supply = <&reg_vcc5v>;
+ vin2-supply = <&reg_vcc5v>;
+ vin3-supply = <&reg_vcc5v>;
+ vin4-supply = <&reg_vcc5v>;
+ aldoin-supply = <&reg_vcc5v>;
+ bldoin-supply = <&reg_vcc5v>;
+ cldoin-supply = <&reg_vcc5v>;
+
+ regulators {
+ /* Supplies the "little" cluster (1.4 GHz cores) */
+ reg_dcdc1: dcdc1 {
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1160000>;
+ regulator-name = "vdd-cpul";
+ };
+
+ reg_dcdc2: dcdc2 {
+ regulator-always-on;
+ regulator-min-microvolt = <920000>;
+ regulator-max-microvolt = <920000>;
+ regulator-name = "vdd-gpu-sys";
+ };
+
+ reg_dcdc3: dcdc3 {
+ regulator-always-on;
+ regulator-min-microvolt = <1160000>;
+ regulator-max-microvolt = <1160000>;
+ regulator-name = "vdd-dram";
+ };
+
+ reg_dcdc4: dcdc4 {
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vdd-io";
+ };
+
+ reg_aldo1: aldo1 {
+ /* not connected */
+ };
+
+ reg_aldo2: aldo2 {
+ /* not connected */
+ };
+
+ reg_aldo3: aldo3 {
+ /* supplies the I2C pins for this PMIC */
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-pl-pm";
+ };
+
+ reg_aldo4: aldo4 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-pll-dxco-avcc";
+ };
+
+ reg_bldo1: bldo1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-pg-wifi-lvds";
+ };
+
+ reg_bldo2: bldo2 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-dram-1v8";
+ };
+
+ reg_bldo3: bldo3 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-cvp-pk-vid1v8";
+ };
+
+ reg_bldo4: bldo4 {
+ /* not connected */
+ };
+
+ reg_cldo1: cldo1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-pc";
+ };
+
+ reg_cldo2: cldo2 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-efuse";
+ };
+
+ reg_cldo3: cldo3 {
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-io-mmc-spi-ana";
+ };
+
+ reg_cldo4: cldo4 {
+ /* not connected */
+ };
+
+ reg_cpusldo: cpusldo {
+ /* supplies the management core */
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ regulator-name = "vdd-cpus";
+ };
+ };
+ };
+
+ axp323: pmic@36 {
+ compatible = "x-powers,axp323";
+ reg = <0x36>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ status = "okay";
+
+ vin1-supply = <&reg_vcc5v>;
+ vin2-supply = <&reg_vcc5v>;
+ vin3-supply = <&reg_vcc5v>;
+
+ regulators {
+ aldo1 {
+ /* not connected */
+ };
+
+ dldo1 {
+ /* not connected */
+ };
+
+ /* Supplies the "big" cluster (1.8 GHz cores) */
+ reg_dcdc1_323: dcdc1 {
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1160000>;
+ regulator-name = "vdd-cpub";
+ };
+
+ /* DCDC2 is polyphased with DCDC1 */
+
+ /* Some RISC-V management core related voltage */
+ reg_dcdc3_323: dcdc3 {
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ regulator-name = "vdd-dnr";
+ };
+ };
+ };
+};
+
+&r_pio {
+/*
+ * Specifying the supply would create a circular dependency.
+ *
+ * vcc-pl-supply = <&reg_aldo3>;
+ */
+ vcc-pm-supply = <&reg_aldo3>;
+};
+
+&uart0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pb_pins>;
+ status = "okay";
+};
+
+&usb_otg {
+ /*
+ * The CC pins of the USB-C port have two pull-down resistors
+ * connected to GND, which fixes this port to a peripheral role.
+ * There is a regulator, controlled by a GPIO, to provide VBUS power
+ * to the port, and a VBUSDET GPIO, to detect externally provided
+ * power, but without the CC pins there is no real way to do a
+ * runtime role detection.
+ */
+ dr_mode = "peripheral";
+ status = "okay";
+};
+
+&usbphy {
+ usb0_vbus-supply = <&reg_usb_vbus>;
+ usb0_vbus_det-gpios = <&pio 8 13 GPIO_ACTIVE_HIGH>; /* PI13 */
+ status = "okay";
+};
diff --git a/include/axp_pmic.h b/include/axp_pmic.h
index ae62ef0d76d..1806a7270a0 100644
--- a/include/axp_pmic.h
+++ b/include/axp_pmic.h
@@ -33,6 +33,7 @@ enum {
AXP221_ID,
AXP223_ID,
AXP313_ID,
+ AXP323_ID,
AXP717_ID,
AXP803_ID,
AXP806_ID,
diff --git a/include/configs/sun55i.h b/include/configs/sun55i.h
new file mode 100644
index 00000000000..70649366c1c
--- /dev/null
+++ b/include/configs/sun55i.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Placeholder wrapper to allow addressing Allwinner devices with Cortex-A55
+ * cores separately. Please do not add anything in here.
+ */
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+#include <configs/sunxi-common.h>
+
+#endif /* __CONFIG_H */