summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/cpu/armv8/fel_utils.S2
-rw-r--r--arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h7
-rw-r--r--arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h4
-rw-r--r--arch/arm/include/asm/arch-sunxi/dram.h2
-rw-r--r--arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h230
-rw-r--r--arch/arm/mach-sunxi/Kconfig114
-rw-r--r--arch/arm/mach-sunxi/Makefile2
-rw-r--r--arch/arm/mach-sunxi/board.c4
-rw-r--r--arch/arm/mach-sunxi/clock_sun50i_h6.c3
-rw-r--r--arch/arm/mach-sunxi/cpu_info.c2
-rw-r--r--arch/arm/mach-sunxi/dram_sun50i_a133.c1204
-rw-r--r--arch/arm/mach-sunxi/dram_timings/Makefile2
-rw-r--r--arch/arm/mach-sunxi/dram_timings/a133_ddr4.c80
-rw-r--r--arch/arm/mach-sunxi/dram_timings/a133_lpddr4.c102
-rw-r--r--board/sunxi/MAINTAINERS10
-rw-r--r--board/sunxi/board.c4
-rw-r--r--common/spl/Kconfig4
-rw-r--r--configs/liontron-h-a133l_defconfig37
-rw-r--r--configs/yuzukihd-chameleon_defconfig30
-rw-r--r--dts/upstream/src/arm64/allwinner/sun50i-a100.dtsi3
-rw-r--r--dts/upstream/src/arm64/allwinner/sun50i-a133-liontron-h-a133l.dts211
-rw-r--r--dts/upstream/src/arm64/allwinner/sun50i-h618-yuzukihd-chameleon.dts222
22 files changed, 2263 insertions, 16 deletions
diff --git a/arch/arm/cpu/armv8/fel_utils.S b/arch/arm/cpu/armv8/fel_utils.S
index 044a7c16cc5..6a7ec9a7ec1 100644
--- a/arch/arm/cpu/armv8/fel_utils.S
+++ b/arch/arm/cpu/armv8/fel_utils.S
@@ -41,7 +41,7 @@ ENTRY(return_to_fel)
str w2, [x1]
ldr w0, =0xfa50392f // CPU hotplug magic
-#ifdef CONFIG_MACH_SUN50I_H616
+#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133)
ldr w2, =(SUNXI_R_CPUCFG_BASE + 0x1c0)
str w0, [x2], #0x4
#elif CONFIG_MACH_SUN50I_H6
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 ccacc99d018..575dff68804 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
@@ -90,6 +90,13 @@
#define CCM_PLL6_DEFAULT 0xe8216300
#define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002
#define CCM_APB1_DEFAULT 0x03000102
+
+#elif CONFIG_MACH_SUN50I_A133 /* A133 */
+
+#define CCM_PLL6_DEFAULT 0xb8003100
+#define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002
+#define CCM_AHB3_DEFAULT 0x03000002
+#define CCM_APB1_DEFAULT 0x03000102
#endif
/* apb2 bit field */
diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
index 8a3f465545a..2a9b086991c 100644
--- a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
@@ -29,6 +29,10 @@
#define SUNXI_DRAM_COM_BASE 0x047FA000
#define SUNXI_DRAM_CTL0_BASE 0x047FB000
#define SUNXI_DRAM_PHY0_BASE 0x04800000
+#elif CONFIG_MACH_SUN50I_A133
+#define SUNXI_DRAM_COM_BASE 0x04810000
+#define SUNXI_DRAM_CTL0_BASE 0x04820000
+#define SUNXI_DRAM_PHY0_BASE 0x04830000
#endif
#define SUNXI_TWI0_BASE 0x05002000
diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h
index 9d21b492418..0708ae3ee3b 100644
--- a/arch/arm/include/asm/arch-sunxi/dram.h
+++ b/arch/arm/include/asm/arch-sunxi/dram.h
@@ -31,6 +31,8 @@
#include <asm/arch/dram_sun50i_h6.h>
#elif defined(CONFIG_MACH_SUN50I_H616)
#include <asm/arch/dram_sun50i_h616.h>
+#elif defined(CONFIG_DRAM_SUN50I_A133)
+#include <asm/arch/dram_sun50i_a133.h>
#elif defined(CONFIG_MACH_SUNIV)
#include <asm/arch/dram_suniv.h>
#else
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h b/arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h
new file mode 100644
index 00000000000..a5fc6ad3656
--- /dev/null
+++ b/arch/arm/include/asm/arch-sunxi/dram_sun50i_a133.h
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * A133 dram controller register and constant defines
+ *
+ * (C) Copyright 2024 MasterR3C0RD <masterr3c0rd@epochal.quest>
+ */
+
+#ifndef _SUNXI_DRAM_SUN50I_A133_H
+#define _SUNXI_DRAM_SUN50I_A133_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
+};
+
+static inline int ns_to_t(int nanoseconds)
+{
+ const unsigned int ctrl_freq = CONFIG_DRAM_CLK / 2;
+
+ return DIV_ROUND_UP(ctrl_freq * nanoseconds, 1000);
+}
+
+/* MBUS part is largely the same as in H6, except for one special register */
+#define MCTL_COM_UNK_008 0x008
+/* NOTE: This register has the same importance as mctl_ctl->clken in H616 */
+#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 unused */
+ u32 pwrtmg; /* 0x034 unused */
+ u32 hwlpctl; /* 0x038 unused */
+ u8 reserved_0x03c[20]; /* 0x03c */
+ u32 rfshctl0; /* 0x050 unused */
+ u32 rfshctl1; /* 0x054 unused */
+ u8 reserved_0x058[8]; /* 0x05c */
+ 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[180]; /* 0x258 */
+ 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_GEARDOWNMODE BIT(0) /* Same as MSTR_DEVICETYPE_DDR3, only used for DDR4 */
+#define MSTR_2TMODE BIT(10)
+#define MSTR_BUSWIDTH_FULL (0 << 12)
+#define MSTR_BUSWIDTH_HALF (1 << 12)
+#define MSTR_ACTIVE_RANKS(x) (((x == 1) ? 3 : 1) << 24)
+#define MSTR_BURST_LENGTH(x) (((x) >> 1) << 16)
+#define MSTR_DEVICECONFIG_X32 (3 << 30)
+
+#define TPR10_CA_BIT_DELAY BIT(16)
+#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)
+
+/* MRCTRL constants */
+#define MRCTRL0_MR_RANKS_ALL (3 << 4)
+#define MRCTRL0_MR_ADDR(x) (x << 12)
+#define MRCTRL0_MR_WR BIT(31)
+
+#define MRCTRL1_MR_ADDR(x) (x << 8)
+#define MRCTRL1_MR_DATA(x) (x)
+
+/* ADDRMAP constants */
+#define ADDRMAP_DISABLED_3F_B(b) (0x3f + b)
+#define ADDRMAP_DISABLED_1F_B(b) (0x1f + b)
+#define ADDRMAP_DISABLED_0F_B(b) (0x0f + b)
+
+#define _ADDRMAP_VALUE(a,x,b) (((a) - b) << (x * 8))
+
+/*
+ * Bx = internal base
+ * The selected HIF address bit for each address bit is determined
+ * by adding the internal base to the value of each field
+ * */
+
+#define ADDRMAP0_CS0_B6(v) _ADDRMAP_VALUE(v, 0, 6)
+
+#define ADDRMAP1_BANK0_B2(v) _ADDRMAP_VALUE(v, 0, 2)
+#define ADDRMAP1_BANK1_B3(v) _ADDRMAP_VALUE(v, 1, 3)
+#define ADDRMAP1_BANK2_B4(v) _ADDRMAP_VALUE(v, 2, 4)
+
+#define ADDRMAP2_COL2_B2(v) _ADDRMAP_VALUE(v, 0, 2)
+#define ADDRMAP2_COL3_B3(v) _ADDRMAP_VALUE(v, 1, 3)
+#define ADDRMAP2_COL4_B4(v) _ADDRMAP_VALUE(v, 2, 4)
+#define ADDRMAP2_COL5_B5(v) _ADDRMAP_VALUE(v, 3, 5)
+
+#define ADDRMAP3_COL6_B6(v) _ADDRMAP_VALUE(v, 0, 6)
+#define ADDRMAP3_COL7_B7(v) _ADDRMAP_VALUE(v, 1, 7)
+#define ADDRMAP3_COL8_B8(v) _ADDRMAP_VALUE(v, 2, 8)
+#define ADDRMAP3_COL9_B9(v) _ADDRMAP_VALUE(v, 3, 9)
+
+#define ADDRMAP4_COL10_B10(v) _ADDRMAP_VALUE(v, 0, 10)
+#define ADDRMAP4_COL11_B11(v) _ADDRMAP_VALUE(v, 1, 11)
+
+#define ADDRMAP5_ROW0_B6(v) _ADDRMAP_VALUE(v, 0, 6)
+#define ADDRMAP5_ROW1_B7(v) _ADDRMAP_VALUE(v, 1, 7)
+#define ADDRMAP5_ROW2_10_B8(v) _ADDRMAP_VALUE(v, 2, 8)
+#define ADDRMAP5_ROW11_B17(v) _ADDRMAP_VALUE(v, 3, 17)
+
+#define ADDRMAP6_ROW12_B18(v) _ADDRMAP_VALUE(v, 0, 18)
+#define ADDRMAP6_ROW13_B19(v) _ADDRMAP_VALUE(v, 1, 19)
+#define ADDRMAP6_ROW14_B20(v) _ADDRMAP_VALUE(v, 2, 20)
+#define ADDRMAP6_ROW15_B21(v) _ADDRMAP_VALUE(v, 3, 21)
+
+#define ADDRMAP7_ROW16_B22(v) _ADDRMAP_VALUE(v, 0, 22)
+#define ADDRMAP7_ROW17_B23(v) _ADDRMAP_VALUE(v, 1, 23)
+
+#define ADDRMAP8_BG0_B2(v) _ADDRMAP_VALUE(v, 0, 2)
+#define ADDRMAP8_BG1_B3(v) _ADDRMAP_VALUE(v, 1, 3)
+
+/* These are only used if ADDRMAP5_ROW_BITS_2_10 = ADDRMAP_DISABLED_0F */
+#define ADDRMAP9_ROW2_B8(v) _ADDRMAP_VALUE(v, 0, 8)
+#define ADDRMAP9_ROW3_B9(v) _ADDRMAP_VALUE(v, 1, 9)
+#define ADDRMAP9_ROW4_B10(v) _ADDRMAP_VALUE(v, 2, 10)
+#define ADDRMAP9_ROW5_B11(v) _ADDRMAP_VALUE(v, 3, 11)
+
+#define ADDRMAP10_ROW6_B12(v) _ADDRMAP_VALUE(v, 0, 12)
+#define ADDRMAP10_ROW7_B13(v) _ADDRMAP_VALUE(v, 1, 13)
+#define ADDRMAP10_ROW8_B14(v) _ADDRMAP_VALUE(v, 2, 14)
+#define ADDRMAP10_ROW9_B15(v) _ADDRMAP_VALUE(v, 3, 15)
+
+#define ADDRMAP11_ROW10_B16(v) _ADDRMAP_VALUE(v, 0, 16)
+
+struct dram_para {
+ uint32_t clk;
+ enum sunxi_dram_type type;
+ uint32_t dx_odt;
+ uint32_t dx_dri;
+ uint32_t ca_dri;
+ uint32_t para0;
+ uint32_t mr11;
+ uint32_t mr12;
+ uint32_t mr13;
+ uint32_t mr14;
+ uint32_t tpr1;
+ uint32_t tpr2;
+ uint32_t tpr3;
+ uint32_t tpr6;
+ uint32_t tpr10;
+ uint32_t tpr11;
+ uint32_t tpr12;
+ uint32_t tpr13;
+ uint32_t tpr14;
+};
+
+void mctl_set_timing_params(const struct dram_para *para);
+
+struct dram_config {
+ u8 cols; /* Column bits */
+ u8 rows; /* Row bits */
+ u8 ranks; /* Rank bits (different from H616!) */
+ u8 banks; /* Bank bits */
+ u8 bankgrps; /* Bank group bits */
+ u8 bus_full_width; /* 1 = x32, 0 = x16 */
+};
+
+#endif /* _SUNXI_DRAM_SUN50I_A133_H */
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 17179593913..0a7c029b15a 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -51,7 +51,13 @@ config DRAM_SUN50I_H616
Select this dram controller driver for some sun50i platforms,
like H616.
-if DRAM_SUN50I_H616
+config DRAM_SUN50I_A133
+ bool
+ help
+ Select this dram controller driver for some sun50i platforms,
+ like A133.
+
+if DRAM_SUN50I_H616 || DRAM_SUN50I_A133
config DRAM_SUNXI_DX_ODT
hex "DRAM DX ODT parameter"
help
@@ -73,18 +79,64 @@ config DRAM_SUNXI_ODT_EN
help
ODT EN value from vendor DRAM settings.
+config DRAM_SUNXI_PARA0
+ hex "DRAM PARA0 parameter"
+ depends on DRAM_SUN50I_A133
+ help
+ PARA0 value from vendor DRAM settings.
+
+config DRAM_SUNXI_MR11
+ hex "DRAM MR11 parameter"
+ depends on DRAM_SUN50I_A133
+ default 0x0
+ help
+ MR11 value from vendor DRAM settings.
+
+config DRAM_SUNXI_MR12
+ hex "DRAM MR12 parameter"
+ depends on DRAM_SUN50I_A133
+ default 0x0
+ help
+ MR12 value from vendor DRAM settings.
+
+config DRAM_SUNXI_MR13
+ hex "DRAM MR13 parameter"
+ depends on DRAM_SUN50I_A133
+ default 0x0
+ help
+ MR13 value from vendor DRAM settings.
+
+config DRAM_SUNXI_MR14
+ hex "DRAM MR14 parameter"
+ depends on DRAM_SUN50I_A133
+ default 0x0
+ help
+ MR14 value from vendor DRAM settings.
+
config DRAM_SUNXI_TPR0
hex "DRAM TPR0 parameter"
default 0x0
help
TPR0 value from vendor DRAM settings.
+config DRAM_SUNXI_TPR1
+ hex "DRAM TPR1 parameter"
+ default 0x0
+ help
+ TPR1 value from vendor DRAM settings.
+
config DRAM_SUNXI_TPR2
hex "DRAM TPR2 parameter"
default 0x0
help
TPR2 value from vendor DRAM settings.
+config DRAM_SUNXI_TPR3
+ hex "DRAM TPR3 parameter"
+ default 0x0
+ help
+ TPR3 value from vendor DRAM settings.
+
config DRAM_SUNXI_TPR6
hex "DRAM TPR6 parameter"
default 0x3300c080
@@ -109,6 +161,20 @@ config DRAM_SUNXI_TPR12
help
TPR12 value from vendor DRAM settings.
+config DRAM_SUNXI_TPR13
+ hex "DRAM TPR13 parameter"
+ depends on DRAM_SUN50I_A133
+ default 0x0
+ help
+ TPR13 value from vendor DRAM settings.
+
+config DRAM_SUNXI_TPR14
+ hex "DRAM TPR14 parameter"
+ depends on DRAM_SUN50I_A133
+ default 0x0
+ help
+ TPR14 value from vendor DRAM settings.
+
choice
prompt "DRAM PHY pin mapping selection"
default DRAM_SUNXI_PHY_ADDR_MAP_0
@@ -116,7 +182,8 @@ choice
config DRAM_SUNXI_PHY_ADDR_MAP_0
bool "DRAM PHY address map 0"
help
- This pin mapping selection should be used by the H313, H616, H618.
+ This pin mapping selection should be used by the H313, H616, H618,
+ and A133, R818 SoCs.
config DRAM_SUNXI_PHY_ADDR_MAP_1
bool "DRAM PHY address map 1"
@@ -153,6 +220,7 @@ config SUNXI_SRAM_ADDRESS
config SUNXI_RVBAR_ADDRESS
hex
depends on ARM64
+ default 0x08100040 if MACH_SUN50I_A133
default 0x09010040 if SUN50I_GEN_H6
default 0x017000a0
---help---
@@ -179,8 +247,8 @@ config SUNXI_RVBAR_ALTERNATIVE
config SUNXI_BL31_BASE
hex
default 0x00044000 if MACH_SUN50I || MACH_SUN50I_H5
- default 0x00104000 if MACH_SUN50I_H6
default 0x40000000 if MACH_SUN50I_H616
+ default 0x00104000 if SUN50I_GEN_H6
default 0x0
help
Address where BL31 (TF-A) is loaded, or zero if BL31 is not used.
@@ -262,7 +330,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
+ default 0x100000000 if MACH_SUN50I_H616 || MACH_SUN50I_A133
default 0xC0000000 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN50I_H6
default 0x80000000
@@ -459,6 +527,10 @@ config MACH_SUN50I_H616
config MACH_SUN50I_A133
bool "sun50i (Allwinner A133)"
+ select ARM64
+ select DRAM_SUN50I_A133
+ select SUN50I_GEN_H6
+ imply OF_UPSTREAM
endchoice
@@ -497,7 +569,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
+if SUNXI_DRAM_DW || DRAM_SUN50I_H6 || DRAM_SUN50I_H616 || DRAM_SUN50I_A133
config SUNXI_DRAM_DDR3
bool
@@ -510,6 +582,9 @@ config SUNXI_DRAM_LPDDR3
config SUNXI_DRAM_LPDDR4
bool
+config SUNXI_DRAM_DDR4
+ bool
+
choice
prompt "DRAM Type and Timing"
default SUNXI_DRAM_DDR3_1333 if !MACH_SUN8I_V3S
@@ -518,6 +593,7 @@ choice
config SUNXI_DRAM_DDR3_1333
bool "DDR3 1333"
select SUNXI_DRAM_DDR3
+ depends on !DRAM_SUN50I_A133
---help---
This option is the original only supported memory type, which suits
many H3/H5/A64 boards available now.
@@ -525,6 +601,7 @@ config SUNXI_DRAM_DDR3_1333
config SUNXI_DRAM_LPDDR3_STOCK
bool "LPDDR3 with Allwinner stock configuration"
select SUNXI_DRAM_LPDDR3
+ depends on !DRAM_SUN50I_A133
---help---
This option is the LPDDR3 timing used by the stock boot0 by
Allwinner.
@@ -548,7 +625,7 @@ config SUNXI_DRAM_H6_DDR3_1333
config SUNXI_DRAM_H616_LPDDR3
bool "LPDDR3 DRAM chips on the H616 DRAM controller"
select SUNXI_DRAM_LPDDR3
- depends on DRAM_SUN50I_H616
+ depends on DRAM_SUN50I_H616 || DRAM_SUN50I_A133
help
This option is the LPDDR3 timing used by the stock boot0 by
Allwinner.
@@ -556,7 +633,7 @@ config SUNXI_DRAM_H616_LPDDR3
config SUNXI_DRAM_H616_LPDDR4
bool "LPDDR4 DRAM chips on the H616 DRAM controller"
select SUNXI_DRAM_LPDDR4
- depends on DRAM_SUN50I_H616
+ depends on DRAM_SUN50I_H616 || DRAM_SUN50I_A133
help
This option is the LPDDR4 timing used by the stock boot0 by
Allwinner.
@@ -564,11 +641,27 @@ config SUNXI_DRAM_H616_LPDDR4
config SUNXI_DRAM_H616_DDR3_1333
bool "DDR3-1333 boot0 timings on the H616 DRAM controller"
select SUNXI_DRAM_DDR3
- depends on DRAM_SUN50I_H616
+ depends on DRAM_SUN50I_H616 || DRAM_SUN50I_A133
help
This option is the DDR3 timing used by the boot0 on H616 TV boxes
which use a DDR3-1333 timing.
+config SUNXI_DRAM_A133_DDR4
+ bool "DDR4 boot0 timings on the A133 DRAM controller"
+ select SUNXI_DRAM_DDR4
+ depends on DRAM_SUN50I_A133
+ help
+ This option is the DDR4 timing used by the boot0 on A133 devices
+ which use a DDR4 timing.
+
+config SUNXI_DRAM_A133_LPDDR4
+ bool "LPDDR4 boot0 timings on the A133 DRAM controller"
+ select SUNXI_DRAM_LPDDR4
+ depends on DRAM_SUN50I_A133
+ help
+ This option is the LPDDR4 timing used by the boot0 on A133 devices
+ which use an LPDDR4 timing.
+
config SUNXI_DRAM_DDR2_V3S
bool "DDR2 found in V3s chip"
select SUNXI_DRAM_DDR2
@@ -596,7 +689,7 @@ config DRAM_CLK
MACH_SUN8I_V3S
default 672 if MACH_SUN50I
default 744 if MACH_SUN50I_H6
- default 720 if MACH_SUN50I_H616
+ default 720 if MACH_SUN50I_H616 || MACH_SUN50I_A133
---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
@@ -613,7 +706,7 @@ endif
config DRAM_ZQ
int "sunxi dram zq value"
- depends on !MACH_SUN50I_H616
+ depends on !MACH_SUN50I_H616 && !MACH_SUN50I_A133
default 123 if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I || \
MACH_SUN8I_A23 || MACH_SUN8I_A33 || MACH_SUN8I_A83T
default 127 if MACH_SUN7I
@@ -733,6 +826,7 @@ config SYS_CONFIG_NAME
default "sun50i" if MACH_SUN50I
default "sun50i" if MACH_SUN50I_H6
default "sun50i" if MACH_SUN50I_H616
+ default "sun50i" if MACH_SUN50I_A133
config SYS_BOARD
default "sunxi"
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index a33cd5b0f07..8eff20b77bf 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -45,4 +45,6 @@ obj-$(CONFIG_DRAM_SUN50I_H6) += dram_sun50i_h6.o dram_dw_helpers.o
obj-$(CONFIG_DRAM_SUN50I_H6) += dram_timings/
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/
endif
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index b1bf51f40c5..08d55b3a0e3 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -137,6 +137,10 @@ static int gpio_init(void)
sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H616_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H616_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP);
+#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN50I_A133)
+ 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_SUN8I_A83T)
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_A83T_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN8I_A83T_GPB_UART0);
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index 4c522f60810..3f375a51965 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -87,7 +87,8 @@ void clock_set_pll1(unsigned int clk)
/* 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))
+ 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;
diff --git a/arch/arm/mach-sunxi/cpu_info.c b/arch/arm/mach-sunxi/cpu_info.c
index 310dca06e57..3f4735d4717 100644
--- a/arch/arm/mach-sunxi/cpu_info.c
+++ b/arch/arm/mach-sunxi/cpu_info.c
@@ -104,6 +104,8 @@ int print_cpuinfo(void)
puts("CPU: Allwinner H6 (SUN50I)\n");
#elif defined CONFIG_MACH_SUN50I_H616
puts("CPU: Allwinner H616 (SUN50I)\n");
+#elif defined CONFIG_MACH_SUN50I_A133
+ puts("CPU: Allwinner A133 (SUN50I)\n");
#else
#warning Please update cpu_info.c with correct CPU information
puts("CPU: SUNXI Family\n");
diff --git a/arch/arm/mach-sunxi/dram_sun50i_a133.c b/arch/arm/mach-sunxi/dram_sun50i_a133.c
new file mode 100644
index 00000000000..a0fca3738f4
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_sun50i_a133.c
@@ -0,0 +1,1204 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * sun50i A133 platform dram controller driver
+ *
+ * Controller and PHY appear to be quite similar to that of the H616;
+ * however certain offsets, timings, and other details are different enough that
+ * the original code does not work as expected. Some device flags and
+ * calibrations are not yet implemented, and configuration aside from DDR4
+ * have not been tested.
+ *
+ * (C) Copyright 2024 MasterR3C0RD <masterr3c0rd@epochal.quest>
+ *
+ * Uses code from H616 driver, which is
+ * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ */
+
+//#define DEBUG
+
+#include <asm/arch/clock.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/dram.h>
+#include <asm/arch/prcm.h>
+#include <asm/io.h>
+#include <init.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <log.h>
+
+#ifdef CONFIG_DRAM_SUNXI_PHY_ADDR_MAP_1
+static const u8 phy_init[] = {
+#ifdef CONFIG_SUNXI_DRAM_DDR3
+ 0x0c, 0x08, 0x19, 0x18, 0x10, 0x06, 0x0a, 0x03, 0x0e,
+ 0x00, 0x0b, 0x05, 0x09, 0x1a, 0x04, 0x13, 0x16, 0x11,
+ 0x01, 0x15, 0x0d, 0x07, 0x12, 0x17, 0x14, 0x02, 0x0f
+#elif CONFIG_SUNXI_DRAM_DDR4
+ 0x19, 0x1a, 0x04, 0x12, 0x09, 0x06, 0x08, 0x0a, 0x16,
+ 0x17, 0x18, 0x0f, 0x0c, 0x13, 0x02, 0x05, 0x01, 0x11,
+ 0x0e, 0x00, 0x0b, 0x07, 0x03, 0x14, 0x15, 0x0d, 0x10
+#elif CONFIG_SUNXI_DRAM_LPDDR3
+ 0x08, 0x03, 0x02, 0x00, 0x18, 0x19, 0x09, 0x01, 0x06,
+ 0x17, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x04, 0x05, 0x07, 0x1a
+#elif CONFIG_SUNXI_DRAM_LPDDR4
+ 0x01, 0x05, 0x02, 0x00, 0x19, 0x03, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x04, 0x1a
+#endif
+};
+#else
+static const u8 phy_init[] = {
+#ifdef CONFIG_SUNXI_DRAM_DDR3
+ 0x03, 0x19, 0x18, 0x02, 0x10, 0x15, 0x16, 0x07, 0x06,
+ 0x0e, 0x05, 0x08, 0x0d, 0x04, 0x17, 0x1a, 0x13, 0x11,
+ 0x12, 0x14, 0x00, 0x01, 0x0c, 0x0a, 0x09, 0x0b, 0x0f
+#elif CONFIG_SUNXI_DRAM_DDR4
+ 0x13, 0x17, 0x0e, 0x01, 0x06, 0x12, 0x14, 0x07, 0x09,
+ 0x02, 0x0f, 0x00, 0x0d, 0x05, 0x16, 0x0c, 0x0a, 0x11,
+ 0x04, 0x03, 0x18, 0x15, 0x08, 0x10, 0x0b, 0x19, 0x1a
+#elif CONFIG_SUNXI_DRAM_LPDDR3
+ 0x05, 0x06, 0x17, 0x02, 0x19, 0x18, 0x04, 0x07, 0x03,
+ 0x01, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x08, 0x09, 0x00, 0x1a
+#elif CONFIG_SUNXI_DRAM_LPDDR4
+ 0x01, 0x03, 0x02, 0x19, 0x17, 0x00, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x04, 0x18, 0x05, 0x1a
+#endif
+};
+#endif
+
+static void mctl_clk_init(u32 clk)
+{
+ void * const ccm = (void *)SUNXI_CCM_BASE;
+
+ /* Place all DRAM blocks into reset */
+ clrbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_ENABLE);
+ 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_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);
+ 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));
+ mctl_await_completion(ccm + CCU_H6_PLL5_CFG,
+ CCM_PLL5_LOCK, CCM_PLL5_LOCK);
+
+ /* Enable DRAM clock and gate*/
+ clrbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, BIT(24) | BIT(25));
+ clrsetbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, 0x1f, BIT(1) | BIT(0));
+ setbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, DRAM_CLK_UPDATE);
+ setbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(RESET_SHIFT));
+ setbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(GATE_SHIFT));
+
+ /* Re-enable MBUS and reset the DRAM module */
+ setbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_RESET);
+ setbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_ENABLE);
+ setbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, DRAM_MOD_RESET);
+ udelay(5);
+}
+
+static void mctl_set_odtmap(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ struct sunxi_mctl_ctl_reg *mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ u32 val, temp1, temp2;
+
+ /* Set ODT/rank mappings*/
+ if (config->bus_full_width)
+ writel_relaxed(0x0201, &mctl_ctl->odtmap);
+ else
+ writel_relaxed(0x0303, &mctl_ctl->odtmap);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ val = 0x06000400;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ /* TODO: What's the purpose of these values? */
+ temp1 = para->clk * 7 / 2000;
+ if (para->clk < 400)
+ temp2 = 0x3;
+ else
+ temp2 = 0x4;
+
+ val = 0x400 | (temp2 - temp1) << 16 | temp1 << 24;
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ /* MR4: CS to CMD / ADDR Latency and write preamble */
+ val = 0x400 | (0x000 << 10 & 0x70000) |
+ (((0x0000 >> 12) & 1) + 6) << 24;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ val = 0x4000400;
+ break;
+ }
+
+ writel_relaxed(val, &mctl_ctl->odtcfg);
+ /* Documented as ODTCFG_SHADOW */
+ writel_relaxed(val, &mctl_ctl->unk_0x2240);
+ /* Offset's interesting; additional undocumented shadows? */
+ writel_relaxed(val, &mctl_ctl->unk_0x3240);
+ writel_relaxed(val, &mctl_ctl->unk_0x4240);
+}
+
+/*
+ * This function produces address mapping parameters, used internally by the
+ * controller to map address lines to HIF addresses. HIF addresses are word
+ * addresses, not byte addresses;
+ * In other words, DDR address 0x400 maps to HIF address 0x100.
+ *
+ * This implementation sets up a reasonable mapping where HIF address
+ * ordering (LSB->MSB) is as such:
+ * - Bank Groups
+ * - Columns
+ * - Banks
+ * - Rows
+ * - Ranks
+ *
+ * TODO: Handle 1.5GB + 3GB configurations. Info about these is stored in
+ * upper bits of TPR13 after autoscan in boot0, and then some extra logic
+ * happens in the address mapping
+ */
+#define INITIAL_HIF_OFFSET 3
+
+static void mctl_set_addrmap(const struct dram_config *config)
+{
+ struct sunxi_mctl_ctl_reg *mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ u8 bankgrp_bits = config->bankgrps;
+ u8 col_bits = config->cols;
+ u8 bank_bits = config->banks;
+ u8 row_bits = config->rows;
+ u8 rank_bits = config->ranks;
+
+ unsigned int i, hif_offset, hif_bits[6];
+
+ /*
+ * When the bus is half width, we need to adjust address mapping,
+ * as COL[0] will be reallocated as part of the byte address,
+ * offsetting the column address mapping values by 1
+ */
+ if (!config->bus_full_width)
+ col_bits--;
+
+ /* Match boot0's DRAM requirements */
+ if (bankgrp_bits > 2)
+ panic("invalid dram configuration (bankgrps_bits = %d)",
+ bankgrp_bits);
+ if (col_bits < 8 || col_bits > 12)
+ panic("invalid dram configuration (col_bits = %d)", col_bits);
+
+ if (bank_bits < 2 || bank_bits > 3)
+ panic("invalid dram configuration (bank_bits = %d)", bank_bits);
+
+ if (row_bits < 14 || row_bits > 18)
+ panic("invalid dram configuration (row_bits = %d)", row_bits);
+
+ if (rank_bits > 1)
+ panic("invalid dram configuration (rank_bits = %d)", rank_bits);
+
+ /*
+ * Col[0:1] + HIF[0:1] (hardwired), Col[2] = HIF[2] (required)
+ * Thus, we start allocating from HIF[3] onwards
+ */
+ hif_offset = INITIAL_HIF_OFFSET;
+
+ /* BG[bankgrp_bits:0] = HIF[3 + bankgrp_bits:3]*/
+ switch (bankgrp_bits) {
+ case 0:
+ writel_relaxed(ADDRMAP8_BG0_B2(ADDRMAP_DISABLED_1F_B(2)) |
+ ADDRMAP8_BG1_B3(ADDRMAP_DISABLED_1F_B(3)),
+ &mctl_ctl->addrmap[8]);
+ break;
+ case 1:
+ writel_relaxed(ADDRMAP8_BG0_B2(hif_offset) |
+ ADDRMAP8_BG1_B3(ADDRMAP_DISABLED_1F_B(3)),
+ &mctl_ctl->addrmap[8]);
+ break;
+ case 2:
+ writel_relaxed(ADDRMAP8_BG0_B2(hif_offset) |
+ ADDRMAP8_BG1_B3(hif_offset + 1),
+ &mctl_ctl->addrmap[8]);
+ break;
+ default:
+ panic("invalid dram configuration (bankgrp_bits = %d)",
+ bankgrp_bits);
+ }
+
+ hif_offset += bankgrp_bits;
+
+ /* Col[2] = HIF[2], Col[5:3] = HIF[offset + 2:offset] */
+ writel_relaxed(ADDRMAP2_COL2_B2(2) | ADDRMAP2_COL3_B3(hif_offset) |
+ ADDRMAP2_COL4_B4(hif_offset + 1) |
+ ADDRMAP2_COL5_B5(hif_offset + 2),
+ &mctl_ctl->addrmap[2]);
+
+ /* Col[col_bits:6] = HIF[col_bits + offset - 3:offset - 3] */
+ for (i = 6; i < 12; i++) {
+ if (i < col_bits)
+ hif_bits[i - 6] = hif_offset + (i - INITIAL_HIF_OFFSET);
+ else
+ hif_bits[i - 6] = ADDRMAP_DISABLED_1F_B(i);
+ }
+
+ writel_relaxed(ADDRMAP3_COL6_B6(hif_bits[0]) |
+ ADDRMAP3_COL7_B7(hif_bits[1]) |
+ ADDRMAP3_COL8_B8(hif_bits[2]) |
+ ADDRMAP3_COL9_B9(hif_bits[3]),
+ &mctl_ctl->addrmap[3]);
+
+ writel_relaxed(ADDRMAP4_COL10_B10(hif_bits[4]) |
+ ADDRMAP4_COL11_B11(hif_bits[5]),
+ &mctl_ctl->addrmap[4]);
+
+ hif_offset = bankgrp_bits + col_bits;
+
+ /* Bank[bank_bits:0] = HIF[bank_bits + offset:offset] */
+ if (bank_bits == 3)
+ writel_relaxed(ADDRMAP1_BANK0_B2(hif_offset) |
+ ADDRMAP1_BANK1_B3(hif_offset + 1) |
+ ADDRMAP1_BANK2_B4(hif_offset + 2),
+ &mctl_ctl->addrmap[1]);
+ else
+ writel_relaxed(ADDRMAP1_BANK0_B2(hif_offset) |
+ ADDRMAP1_BANK1_B3(hif_offset + 1) |
+ ADDRMAP1_BANK2_B4(ADDRMAP_DISABLED_1F_B(4)),
+ &mctl_ctl->addrmap[1]);
+
+ hif_offset += bank_bits;
+
+ /* Row[11:0] = HIF[11 + offset:offset] */
+ writel_relaxed(ADDRMAP5_ROW0_B6(hif_offset) |
+ ADDRMAP5_ROW1_B7(hif_offset + 1) |
+ ADDRMAP5_ROW2_10_B8(hif_offset + 2) |
+ ADDRMAP5_ROW11_B17(hif_offset + 11),
+ &mctl_ctl->addrmap[5]);
+
+ /*
+ * There's some complexity here because of a special case
+ * in boot0 code that appears to work around a hardware bug.
+ * For (col_bits, row_bits, rank_bits) = (10, 16, 1), we have to
+ * place CS[0] in the position we would normally place ROW[14],
+ * and shift ROW[14] and ROW[15] over by one. Using the bit following
+ * ROW[15], as would be standard here, seems to cause nonsensical
+ * aliasing patterns.
+ *
+ * Aside from this case, mapping is simple:
+ * Row[row_bits:12] = HIF[offset + row_bits:offset + 12]
+ */
+ for (i = 12; i < 18; i++) {
+ if (i >= row_bits)
+ hif_bits[i - 12] = ADDRMAP_DISABLED_0F_B(6 + i);
+ else if (rank_bits != 1 || col_bits != 10 || row_bits != 16 ||
+ i < 14)
+ hif_bits[i - 12] = hif_offset + i;
+ else
+ hif_bits[i - 12] = hif_offset + i + 1;
+ }
+
+ writel_relaxed(ADDRMAP6_ROW12_B18(hif_bits[0]) |
+ ADDRMAP6_ROW13_B19(hif_bits[1]) |
+ ADDRMAP6_ROW14_B20(hif_bits[2]) |
+ ADDRMAP6_ROW15_B21(hif_bits[3]),
+ &mctl_ctl->addrmap[6]);
+
+ writel_relaxed(ADDRMAP7_ROW16_B22(hif_bits[4]) |
+ ADDRMAP7_ROW17_B23(hif_bits[5]),
+ &mctl_ctl->addrmap[7]);
+
+ hif_offset += row_bits;
+
+ /*
+ * Ranks
+ * Most cases: CS[0] = HIF[offset]
+ * Special case (see above): CS[0] = HIF[offset - 2]
+ */
+ if (rank_bits == 0)
+ writel_relaxed(ADDRMAP0_CS0_B6(ADDRMAP_DISABLED_1F_B(6)),
+ &mctl_ctl->addrmap[0]);
+ else if (col_bits == 10 && row_bits == 16)
+ writel_relaxed(ADDRMAP0_CS0_B6(hif_offset - 2),
+ &mctl_ctl->addrmap[0]);
+ else
+ writel_relaxed(ADDRMAP0_CS0_B6(hif_offset),
+ &mctl_ctl->addrmap[0]);
+}
+
+static void mctl_com_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 *mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ /* Might control power/reset of DDR-related blocks */
+ clrsetbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24), BIT(25) | BIT(9));
+
+ /* Unlock mctl_ctl registers */
+ setbits_le32(mctl_com + MCTL_COM_MAER0, BIT(15));
+
+ if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
+ setbits_le32(0x03102ea8, BIT(0));
+
+ clrsetbits_le32(&mctl_ctl->sched[0], 0xff << 8, 0x30 << 8);
+ if (!(para->tpr13 & BIT(28)))
+ clrsetbits_le32(&mctl_ctl->sched[0], 0xf, BIT(0));
+
+ writel_relaxed(0, &mctl_ctl->hwlpctl);
+
+ /* Master settings */
+ u32 mstr_value = MSTR_DEVICECONFIG_X32 |
+ MSTR_ACTIVE_RANKS(config->ranks);
+
+ if (config->bus_full_width)
+ mstr_value |= MSTR_BUSWIDTH_FULL;
+ else
+ mstr_value |= MSTR_BUSWIDTH_HALF;
+
+ /*
+ * Geardown and 2T mode are always enabled here, but is controlled by a flag in boot0;
+ * it has not been a problem so far, but may be suspect if a particular board isn't booting.
+ */
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ mstr_value |= MSTR_DEVICETYPE_DDR3 | MSTR_BURST_LENGTH(8) |
+ MSTR_2TMODE;
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ mstr_value |= MSTR_DEVICETYPE_DDR4 | MSTR_BURST_LENGTH(8) |
+ MSTR_GEARDOWNMODE | MSTR_2TMODE;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ mstr_value |= MSTR_DEVICETYPE_LPDDR3 | MSTR_BURST_LENGTH(8);
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ mstr_value |= MSTR_DEVICETYPE_LPDDR4 | MSTR_BURST_LENGTH(16);
+ break;
+ }
+
+ writel_relaxed(mstr_value, &mctl_ctl->mstr);
+
+ mctl_set_odtmap(para, config);
+ mctl_set_addrmap(config);
+ mctl_set_timing_params(para);
+
+ dsb();
+ writel(0, &mctl_ctl->pwrctl);
+
+ /* Disable automatic controller updates + automatic controller update requests */
+ 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));
+
+ /*
+ * Data bus inversion
+ * Controlled by a flag in boot0, enabled by default here.
+ */
+ if (para->type == SUNXI_DRAM_TYPE_DDR4 ||
+ para->type == SUNXI_DRAM_TYPE_LPDDR4)
+ setbits_le32(&mctl_ctl->dbictl, BIT(2));
+}
+
+static void mctl_drive_odt_config(const struct dram_para *para)
+{
+ u32 val;
+ u64 base;
+ u32 i;
+
+ /* DX drive */
+ for (i = 0; i < 4; i++) {
+ base = SUNXI_DRAM_PHY0_BASE + 0x388 + 0x40 * i;
+ val = (para->dx_dri >> (i * 8)) & 0x1f;
+
+ writel_relaxed(val, base);
+ if (para->type == SUNXI_DRAM_TYPE_LPDDR4) {
+ if (para->tpr3 & 0x1f1f1f1f)
+ val = (para->tpr3 >> (i * 8)) & 0x1f;
+ else
+ val = 4;
+ }
+ writel_relaxed(val, base + 4);
+ }
+
+ /* CA drive */
+ for (i = 0; i < 2; i++) {
+ base = SUNXI_DRAM_PHY0_BASE + 0x340 + 0x8 * i;
+ val = (para->ca_dri >> (i * 8)) & 0x1f;
+
+ writel_relaxed(val, base);
+ writel_relaxed(val, base + 4);
+ }
+
+ /* DX ODT */
+ for (i = 0; i < 4; i++) {
+ base = SUNXI_DRAM_PHY0_BASE + 0x380 + 0x40 * i;
+ val = (para->dx_odt >> (i * 8)) & 0x1f;
+
+ if (para->type == SUNXI_DRAM_TYPE_DDR4 ||
+ para->type == SUNXI_DRAM_TYPE_LPDDR3)
+ writel_relaxed(0, base);
+ else
+ writel_relaxed(val, base);
+
+ if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
+ writel_relaxed(0, base + 4);
+ else
+ writel_relaxed(val, base + 4);
+ }
+ dsb();
+}
+
+static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para)
+{
+ u32 val, i;
+ u32 *ptr;
+
+ if (para->tpr10 & BIT(31)) {
+ val = para->tpr2;
+ } else {
+ val = ((para->tpr10 << 1) & 0x1e) |
+ ((para->tpr10 << 5) & 0x1e00) |
+ ((para->tpr10 << 9) & 0x1e0000) |
+ ((para->tpr10 << 13) & 0x1e000000);
+
+ if (para->tpr10 >> 29 != 0)
+ val <<= 1;
+ }
+
+ ptr = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x780);
+ for (i = 0; i < 32; i++)
+ writel_relaxed((val >> 8) & 0x3f, &ptr[i]);
+
+ writel_relaxed(val & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x7dc);
+ writel_relaxed(val & 0x3f, SUNXI_DRAM_PHY0_BASE + 0x7e0);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ writel_relaxed((val >> 16) & 0x3f,
+ SUNXI_DRAM_PHY0_BASE + 0x7b8);
+ writel_relaxed((val >> 24) & 0x3f,
+ SUNXI_DRAM_PHY0_BASE + 0x784);
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ writel_relaxed((val >> 16) & 0x3f,
+ SUNXI_DRAM_PHY0_BASE + 0x784);
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ writel_relaxed((val >> 16) & 0x3f,
+ SUNXI_DRAM_PHY0_BASE + 0x788);
+ writel_relaxed((val >> 24) & 0x3f,
+ SUNXI_DRAM_PHY0_BASE + 0x790);
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ writel_relaxed((val >> 16) & 0x3f,
+ SUNXI_DRAM_PHY0_BASE + 0x790);
+ writel_relaxed((val >> 24) & 0x3f,
+ SUNXI_DRAM_PHY0_BASE + 0x78c);
+ break;
+ }
+
+ dsb();
+}
+
+static void mctl_phy_init(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ struct sunxi_mctl_ctl_reg *mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ void *const prcm = (void *)SUNXI_PRCM_BASE;
+ void *const mctl_com = (void *)SUNXI_DRAM_COM_BASE;
+
+ u32 val, val2, i;
+ u32 *ptr;
+
+ /* Disable auto refresh. */
+ setbits_le32(&mctl_ctl->rfshctl3, BIT(0));
+
+ /* Set "phy_dbi_mode" to mark the DFI as implementing DBI functionality */
+ writel_relaxed(0, &mctl_ctl->pwrctl);
+ clrbits_le32(&mctl_ctl->dfimisc, 1);
+ writel_relaxed(0x20, &mctl_ctl->pwrctl);
+
+ /* PHY cold reset */
+ clrsetbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24), BIT(9));
+ udelay(1);
+ setbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24));
+
+ /* Not sure what this gates the power of. */
+ clrbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, BIT(4));
+
+ if (para->type == SUNXI_DRAM_TYPE_LPDDR4)
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x4, BIT(7));
+
+ /* Note: Similar enumeration of values is used during read training */
+ if (config->bus_full_width)
+ val = 0xf;
+ else
+ val = 0x3;
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x3c, 0xf, val);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ val = 13;
+ val2 = 9;
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ val = 13;
+ val2 = 10;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ val = 14;
+ val2 = 8;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ if (para->tpr13 & BIT(28))
+ val = 22;
+ else
+ val = 20;
+
+ val2 = 10;
+ break;
+ }
+
+ writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x14);
+ writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x35c);
+ writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x368);
+ writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x374);
+ writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x18);
+ writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x360);
+ writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x36c);
+ writel_relaxed(0, SUNXI_DRAM_PHY0_BASE + 0x378);
+ writel_relaxed(val2, SUNXI_DRAM_PHY0_BASE + 0x1c);
+ writel_relaxed(val2, SUNXI_DRAM_PHY0_BASE + 0x364);
+ writel_relaxed(val2, SUNXI_DRAM_PHY0_BASE + 0x370);
+ writel_relaxed(val2, SUNXI_DRAM_PHY0_BASE + 0x37c);
+
+ /* Set up SDQ swizzle */
+ ptr = (u32 *)(SUNXI_DRAM_PHY0_BASE + 0xc0);
+ for (i = 0; i < ARRAY_SIZE(phy_init); i++)
+ writel_relaxed(phy_init[i], &ptr[i]);
+
+ /* Set VREF */
+ val = 0;
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ val = para->tpr6 & 0xff;
+ if (val == 0)
+ val = 0x80;
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ val = (para->tpr6 >> 8) & 0xff;
+ if (val == 0)
+ val = 0x80;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ val = (para->tpr6 >> 16) & 0xff;
+ if (val == 0)
+ val = 0x80;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ val = (para->tpr6 >> 24) & 0xff;
+ if (val == 0)
+ val = 0x33;
+ break;
+ }
+ writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x3dc);
+ writel_relaxed(val, SUNXI_DRAM_PHY0_BASE + 0x45c);
+
+ mctl_drive_odt_config(para);
+
+ if (para->tpr10 & TPR10_CA_BIT_DELAY)
+ mctl_phy_ca_bit_delay_compensation(para);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ val = 2;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ val = 3;
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ val = 4;
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ val = 5;
+ break;
+ }
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x4, 0x7, val | 8);
+
+ if (para->clk <= 672)
+ writel_relaxed(0xf, SUNXI_DRAM_PHY0_BASE + 0x20);
+
+ if (para->clk > 500) {
+ val = 0;
+ val2 = 0;
+ } else {
+ val = 0x80;
+ val2 = 0x20;
+ }
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x144, 0x80, val);
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x14c, 0xe0, val2);
+
+ dsb();
+ clrbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(9));
+ udelay(1);
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x14c, BIT(3));
+
+ mctl_await_completion((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x180), BIT(2),
+ BIT(2));
+
+ /*
+ * This delay is controlled by a tpr13 flag in boot0; doesn't hurt
+ * to always do it though.
+ */
+ udelay(1000);
+ writel(0x37, SUNXI_DRAM_PHY0_BASE + 0x58);
+
+ setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, BIT(4));
+}
+
+/* Helpers for updating mode registers */
+static inline void mctl_mr_write(u32 mrctrl0, u32 mrctrl1)
+{
+ struct sunxi_mctl_ctl_reg *mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ writel(mrctrl1, &mctl_ctl->mrctrl1);
+ writel(mrctrl0 | MRCTRL0_MR_WR | MRCTRL0_MR_RANKS_ALL,
+ &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, MRCTRL0_MR_WR, 0);
+}
+
+static inline void mctl_mr_write_lpddr4(u8 addr, u8 value)
+{
+ mctl_mr_write(0, MRCTRL1_MR_ADDR(addr) | MRCTRL1_MR_DATA(value));
+}
+
+static inline void mctl_mr_write_lpddr3(u8 addr, u8 value)
+{
+ /* Bit [7:6] are set by boot0, but undocumented */
+ mctl_mr_write(BIT(6) | BIT(7),
+ MRCTRL1_MR_ADDR(addr) | MRCTRL1_MR_DATA(value));
+}
+
+static void mctl_dfi_init(const struct dram_para *para)
+{
+ void *const mctl_com = (void *)SUNXI_DRAM_COM_BASE;
+ struct sunxi_mctl_ctl_reg *mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ /* Unlock DFI registers? */
+ setbits_le32(mctl_com + MCTL_COM_MAER0, BIT(8));
+
+ /* Enable dfi_init_complete signal and trigger PHY init start request */
+ writel_relaxed(0, &mctl_ctl->swctl);
+ setbits_le32(&mctl_ctl->dfimisc, BIT(0));
+ setbits_le32(&mctl_ctl->dfimisc, BIT(5));
+ writel_relaxed(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, BIT(0), BIT(0));
+
+ /* Stop sending init request and wait for DFI initialization to complete. */
+ writel_relaxed(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->dfimisc, BIT(5));
+ writel_relaxed(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, BIT(0), BIT(0));
+ mctl_await_completion(&mctl_ctl->dfistat, BIT(0), BIT(0));
+
+ /* Enter Software Exit from Self Refresh */
+ writel_relaxed(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->pwrctl, BIT(5));
+ writel_relaxed(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, BIT(0), BIT(0));
+ mctl_await_completion(&mctl_ctl->statr, 0x3, 1);
+
+ udelay(200);
+
+ /* Disable dfi_init_complete signal */
+ writel_relaxed(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->dfimisc, BIT(0));
+ writel_relaxed(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, BIT(0), BIT(0));
+
+ /* Write mode registers, fixed in the JEDEC spec */
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_DDR3:
+ mctl_mr_write(MRCTRL0_MR_ADDR(0), 0x1c70); /* MR0 */
+ /*
+ * outbuf en, TDQs dis, write leveling dis, out drv 40 Ohms,
+ * DLL en, Rtt_nom 120 Ohms
+ */
+ mctl_mr_write(MRCTRL0_MR_ADDR(1), 0x40); /* MR1 */
+ /*
+ * full array self-ref, CAS: 8 cyc, SRT w/ norm temp range,
+ * dynamic ODT off
+ */
+ mctl_mr_write(MRCTRL0_MR_ADDR(2), 0x18); /* MR2 */
+ /* predef MPR pattern */
+ mctl_mr_write(MRCTRL0_MR_ADDR(3), 0); /* MR3 */
+ break;
+ case SUNXI_DRAM_TYPE_DDR4:
+ mctl_mr_write(MRCTRL0_MR_ADDR(0), 0x840);
+ mctl_mr_write(MRCTRL0_MR_ADDR(1), 0x601);
+ mctl_mr_write(MRCTRL0_MR_ADDR(2), 0x8);
+ mctl_mr_write(MRCTRL0_MR_ADDR(3), 0);
+ mctl_mr_write(MRCTRL0_MR_ADDR(4), 0);
+ mctl_mr_write(MRCTRL0_MR_ADDR(5), 0x400);
+
+ mctl_mr_write(MRCTRL0_MR_ADDR(6), 0x862 | BIT(7));
+ mctl_mr_write(MRCTRL0_MR_ADDR(6), 0x862 | BIT(7));
+ mctl_mr_write(MRCTRL0_MR_ADDR(6), 0x862 & (~BIT(7)));
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR3:
+ mctl_mr_write_lpddr3(1, 0xc3); /* MR1: nWR=8, BL8 */
+ mctl_mr_write_lpddr3(2, 0xa); /* MR2: RL=12, WL=6 */
+ mctl_mr_write_lpddr3(3, 0x2); /* MR3: 40 0hms PD/PU */
+ mctl_mr_write_lpddr3(11, para->mr11);
+ break;
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ mctl_mr_write_lpddr4(0, 0); /* MR0 */
+ mctl_mr_write_lpddr4(1, 0x34); /* MR1 */
+ mctl_mr_write_lpddr4(2, 0x1b); /* MR2 */
+ mctl_mr_write_lpddr4(3, 0x33); /* MR3 */
+ mctl_mr_write_lpddr4(4, 0x3); /* MR4 */
+ mctl_mr_write_lpddr4(11, para->mr11);
+ mctl_mr_write_lpddr4(12, para->mr12);
+ mctl_mr_write_lpddr4(13, para->mr13);
+ mctl_mr_write_lpddr4(14, para->mr14);
+ mctl_mr_write_lpddr4(22, para->tpr1);
+ break;
+ }
+
+ writel(0, SUNXI_DRAM_PHY0_BASE + 0x54);
+
+ /* Re-enable controller refresh */
+ writel(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->rfshctl3, BIT(0));
+ writel(1, &mctl_ctl->swctl);
+}
+
+/* Slightly modified from H616 driver */
+static bool mctl_phy_read_calibration(const struct dram_config *config)
+{
+ bool result = true;
+ u32 val, tmp;
+
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30, 0x20);
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1);
+
+ if (config->bus_full_width)
+ val = 0xf;
+ else
+ val = 3;
+
+ while ((readl_relaxed(SUNXI_DRAM_PHY0_BASE + 0x184) & val) != val) {
+ if (readl_relaxed(SUNXI_DRAM_PHY0_BASE + 0x184) & 0x20) {
+ result = false;
+ break;
+ }
+ }
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1);
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30);
+
+ if (config->ranks == 1) {
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30, 0x10);
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1);
+
+ while ((readl_relaxed(SUNXI_DRAM_PHY0_BASE + 0x184) & val) !=
+ val) {
+ if (readl_relaxed(SUNXI_DRAM_PHY0_BASE + 0x184) &
+ 0x20) {
+ result = false;
+ break;
+ }
+ }
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1);
+ }
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30);
+
+ val = readl_relaxed(SUNXI_DRAM_PHY0_BASE + 0x274) & 7;
+ tmp = readl_relaxed(SUNXI_DRAM_PHY0_BASE + 0x26c) & 7;
+ if (val < tmp)
+ val = tmp;
+ tmp = readl_relaxed(SUNXI_DRAM_PHY0_BASE + 0x32c) & 7;
+ if (val < tmp)
+ val = tmp;
+ tmp = readl_relaxed(SUNXI_DRAM_PHY0_BASE + 0x334) & 7;
+ if (val < tmp)
+ val = tmp;
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x38, 0x7, (val + 2) & 7);
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0x20);
+
+ return result;
+}
+
+static inline void mctl_phy_dx_delay1_inner(u32 *base, u32 val1, u32 val2)
+{
+ u32 *ptr = base;
+
+ for (int i = 0; i < 9; i++) {
+ writel_relaxed(val1, ptr);
+ writel_relaxed(val1, ptr + 0x30);
+ ptr += 2;
+ }
+
+ writel_relaxed(val2, ptr + 1);
+ writel_relaxed(val2, ptr + 49);
+ writel_relaxed(val2, ptr);
+ writel_relaxed(val2, ptr + 48);
+}
+
+static inline void mctl_phy_dx_delay0_inner(u32 *base1, u32 *base2, u32 val1,
+ u32 val2)
+{
+ u32 *ptr = base1;
+
+ for (int i = 0; i < 9; i++) {
+ writel_relaxed(val1, ptr);
+ writel_relaxed(val1, ptr + 0x30);
+ ptr += 2;
+ }
+
+ writel_relaxed(val2, base2);
+ writel_relaxed(val2, base2 + 48);
+ writel_relaxed(val2, ptr);
+ writel_relaxed(val2, base2 + 44);
+}
+
+/*
+ * This might be somewhat transferable to H616; whether or not people like
+ * the design is another question
+ */
+static void mctl_phy_dx_delay_compensation(const struct dram_para *para)
+{
+ if (para->tpr10 & TPR10_DX_BIT_DELAY1) {
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 1);
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, BIT(3));
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, BIT(4));
+
+ if (para->type == SUNXI_DRAM_TYPE_DDR4)
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x4, BIT(7));
+
+ mctl_phy_dx_delay1_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x484),
+ para->tpr11 & 0x3f,
+ para->para0 & 0x3f);
+ mctl_phy_dx_delay1_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x4d8),
+ (para->tpr11 >> 8) & 0x3f,
+ (para->para0 >> 8) & 0x3f);
+ mctl_phy_dx_delay1_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x604),
+ (para->tpr11 >> 16) & 0x3f,
+ (para->para0 >> 16) & 0x3f);
+ mctl_phy_dx_delay1_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x658),
+ (para->tpr11 >> 24) & 0x3f,
+ (para->para0 >> 24) & 0x3f);
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 1);
+ }
+
+ if (para->tpr10 & TPR10_DX_BIT_DELAY0) {
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x54, BIT(7));
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, BIT(2));
+
+ mctl_phy_dx_delay0_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x480),
+ (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x528),
+ para->tpr12 & 0x3f,
+ para->tpr14 & 0x3f);
+
+ mctl_phy_dx_delay0_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x4d4),
+ (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x52c),
+ (para->tpr12 >> 8) & 0x3f,
+ (para->tpr14 >> 8) & 0x3f);
+
+ mctl_phy_dx_delay0_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x600),
+ (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x6a8),
+ (para->tpr12 >> 16) & 0x3f,
+ (para->tpr14 >> 16) & 0x3f);
+
+ mctl_phy_dx_delay0_inner((u32 *)(SUNXI_DRAM_PHY0_BASE + 0x6ac),
+ (u32 *)(SUNXI_DRAM_PHY0_BASE + 0x528),
+ (para->tpr12 >> 24) & 0x3f,
+ (para->tpr14 >> 24) & 0x3f);
+
+ setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x54, BIT(7));
+ }
+}
+
+static bool mctl_calibrate_phy(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ struct sunxi_mctl_ctl_reg *mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ int i;
+
+ /* TODO: Implement write levelling */
+ if (para->tpr10 & TPR10_READ_CALIBRATION) {
+ for (i = 0; i < 5; i++)
+ if (mctl_phy_read_calibration(config))
+ break;
+ if (i == 5) {
+ debug("read calibration failed\n");
+ return false;
+ }
+ }
+
+ /* TODO: Implement read training */
+ /* TODO: Implement write training */
+
+ mctl_phy_dx_delay_compensation(para);
+ /* TODO: Implement DFS */
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, BIT(0));
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x54, 7);
+
+ /* Q: Does self-refresh get disabled by a calibration? */
+ writel_relaxed(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->rfshctl3, BIT(1));
+ writel_relaxed(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, BIT(0), BIT(0));
+
+ return true;
+}
+
+static bool mctl_core_init(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ mctl_clk_init(para->clk);
+ mctl_com_init(para, config);
+ mctl_phy_init(para, config);
+ mctl_dfi_init(para);
+
+ return mctl_calibrate_phy(para, config);
+}
+
+/* Heavily inspired from H616 driver. */
+static void auto_detect_ranks(const struct dram_para *para,
+ struct dram_config *config)
+{
+ int i;
+
+ config->cols = 9;
+ config->rows = 14;
+ config->banks = 2;
+ config->bankgrps = 0;
+
+ /* Test ranks */
+ for (i = 1; i >= 0; i--) {
+ config->ranks = i;
+ config->bus_full_width = true;
+ debug("Testing ranks = %d, 32-bit bus: ", i);
+ if (mctl_core_init(para, config)) {
+ debug("OK\n");
+ break;
+ }
+
+ config->bus_full_width = false;
+ debug("Testing ranks = %d, 16-bit bus: ", i);
+ if (mctl_core_init(para, config)) {
+ debug("OK\n");
+ break;
+ }
+ }
+
+ if (i < 0)
+ debug("rank testing failed\n");
+}
+
+static void mctl_write_pattern(void)
+{
+ unsigned int i;
+ u32 *ptr, val;
+
+ ptr = (u32 *)CFG_SYS_SDRAM_BASE;
+ for (i = 0; i < 16; ptr++, i++) {
+ if (i & 1)
+ val = ~(ulong)ptr;
+ else
+ val = (ulong)ptr;
+ writel(val, ptr);
+ }
+}
+
+static bool mctl_check_pattern(ulong offset)
+{
+ unsigned int i;
+ u32 *ptr, val;
+
+ ptr = (u32 *)CFG_SYS_SDRAM_BASE;
+ for (i = 0; i < 16; ptr++, i++) {
+ if (i & 1)
+ val = ~(ulong)ptr;
+ else
+ val = (ulong)ptr;
+ if (val != *(ptr + offset / 4))
+ return false;
+ }
+
+ return true;
+}
+
+static void mctl_auto_detect_dram_size(const struct dram_para *para,
+ struct dram_config *config)
+{
+ unsigned int shift;
+ u32 buffer[16];
+
+ /* max config for bankgrps on DDR4, minimum for everything else */
+ config->cols = 8;
+ config->banks = 2;
+ config->rows = 14;
+
+ shift = 1 + config->bus_full_width;
+ if (para->type == SUNXI_DRAM_TYPE_DDR4) {
+ config->bankgrps = 2;
+ mctl_core_init(para, config);
+
+ /* store content so it can be restored later. */
+ memcpy(buffer, (u32 *)CFG_SYS_SDRAM_BASE, sizeof(buffer));
+ mctl_write_pattern();
+
+ if (mctl_check_pattern(1ULL << (shift + 4)))
+ config->bankgrps = 1;
+
+ /* restore data */
+ memcpy((u32 *)CFG_SYS_SDRAM_BASE, buffer, sizeof(buffer));
+ } else {
+ /* No bank groups in (LP)DDR3/LPDDR4 */
+ config->bankgrps = 0;
+ }
+
+ /* reconfigure to make sure all active columns are accessible */
+ config->cols = 12;
+ mctl_core_init(para, config);
+
+ /* store data again as it might be moved */
+ memcpy(buffer, (u32 *)CFG_SYS_SDRAM_BASE, sizeof(buffer));
+ mctl_write_pattern();
+
+ /*
+ * Detect column address bits. The last number of columns checked
+ * is 11, if that doesn't match, is must be 12, no more checks needed.
+ */
+ shift = 1 + config->bus_full_width + config->bankgrps;
+ for (config->cols = 8; config->cols < 12; config->cols++) {
+ if (mctl_check_pattern(1ULL << (config->cols + shift)))
+ break;
+ }
+ memcpy((u32 *)CFG_SYS_SDRAM_BASE, buffer, sizeof(buffer));
+
+ /* reconfigure to make sure that all active banks are accessible */
+ config->banks = 3;
+ mctl_core_init(para, config);
+
+ memcpy(buffer, (u32 *)CFG_SYS_SDRAM_BASE, sizeof(buffer));
+ mctl_write_pattern();
+
+ /* detect bank bits */
+ shift += config->cols;
+ for (config->banks = 2; config->banks < 3; config->banks++) {
+ if (mctl_check_pattern(1ULL << (config->banks + shift)))
+ break;
+ }
+ memcpy((u32 *)CFG_SYS_SDRAM_BASE, buffer, sizeof(buffer));
+
+ /* reconfigure to make sure that all active rows are accessible */
+ config->rows = 18;
+ mctl_core_init(para, config);
+
+ memcpy(buffer, (u32 *)CFG_SYS_SDRAM_BASE, sizeof(buffer));
+ mctl_write_pattern();
+
+ /* detect row address bits */
+ shift += config->banks;
+ for (config->rows = 14; config->rows < 18; config->rows++) {
+ if (mctl_check_pattern(1ULL << (config->rows + shift)))
+ break;
+ }
+ memcpy((u32 *)CFG_SYS_SDRAM_BASE, buffer, sizeof(buffer));
+}
+
+/* Modified from H616 driver to add banks and bank groups */
+static unsigned long calculate_dram_size(const struct dram_config *config)
+{
+ /* Bootrom only uses x32 or x16 bus widths */
+ u8 width = config->bus_full_width ? 4 : 2;
+
+ return (1ULL << (config->cols + config->rows + config->banks +
+ config->bankgrps)) *
+ width * (1ULL << config->ranks);
+}
+
+static const struct dram_para para = {
+ .clk = CONFIG_DRAM_CLK,
+#ifdef CONFIG_SUNXI_DRAM_DDR3
+ .type = SUNXI_DRAM_TYPE_DDR3,
+#elif defined(CONFIG_SUNXI_DRAM_DDR4)
+ .type = SUNXI_DRAM_TYPE_DDR4,
+#elif defined(CONFIG_SUNXI_DRAM_LPDDR3)
+ .type = SUNXI_DRAM_TYPE_LPDDR3,
+#elif defined(CONFIG_SUNXI_DRAM_LPDDR4)
+ .type = SUNXI_DRAM_TYPE_LPDDR4,
+#endif
+ /* TODO: Populate from config */
+ .dx_odt = CONFIG_DRAM_SUNXI_DX_ODT,
+ .dx_dri = CONFIG_DRAM_SUNXI_DX_DRI,
+ .ca_dri = CONFIG_DRAM_SUNXI_CA_DRI,
+ .para0 = CONFIG_DRAM_SUNXI_PARA0,
+ .mr11 = CONFIG_DRAM_SUNXI_MR11,
+ .mr12 = CONFIG_DRAM_SUNXI_MR12,
+ .mr13 = CONFIG_DRAM_SUNXI_MR13,
+ .mr14 = CONFIG_DRAM_SUNXI_MR14,
+ .tpr1 = CONFIG_DRAM_SUNXI_TPR1,
+ .tpr2 = CONFIG_DRAM_SUNXI_TPR2,
+ .tpr3 = CONFIG_DRAM_SUNXI_TPR3,
+ .tpr6 = CONFIG_DRAM_SUNXI_TPR6,
+ .tpr10 = CONFIG_DRAM_SUNXI_TPR10,
+ .tpr11 = CONFIG_DRAM_SUNXI_TPR11,
+ .tpr12 = CONFIG_DRAM_SUNXI_TPR12,
+ .tpr13 = CONFIG_DRAM_SUNXI_TPR13,
+ .tpr14 = CONFIG_DRAM_SUNXI_TPR14,
+};
+
+unsigned long sunxi_dram_init(void)
+{
+ struct dram_config config;
+
+ /* Writing to undocumented SYS_CFG area, according to user manual. */
+ setbits_le32(0x03000160, BIT(8));
+ clrbits_le32(0x03000168, 0x3f);
+
+ auto_detect_ranks(&para, &config);
+ mctl_auto_detect_dram_size(&para, &config);
+
+ if (!mctl_core_init(&para, &config))
+ return 0;
+
+ debug("cols = 2^%d, rows = 2^%d, banks = %d, bank groups = %d, ranks = %d, width = %d\n",
+ config.cols, config.rows, 1U << config.banks,
+ 1U << config.bankgrps, 1U << config.ranks,
+ 16U << config.bus_full_width);
+
+ return calculate_dram_size(&config);
+}
diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile
index 5f203419240..4dc1f29fc08 100644
--- a/arch/arm/mach-sunxi/dram_timings/Makefile
+++ b/arch/arm/mach-sunxi/dram_timings/Makefile
@@ -6,3 +6,5 @@ obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333) += h6_ddr3_1333.o
obj-$(CONFIG_SUNXI_DRAM_H616_DDR3_1333) += h616_ddr3_1333.o
obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3) += h616_lpddr3.o
obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4) += h616_lpddr4_2133.o
+obj-$(CONFIG_SUNXI_DRAM_A133_DDR4) += a133_ddr4.o
+obj-$(CONFIG_SUNXI_DRAM_A133_LPDDR4) += a133_lpddr4.o
diff --git a/arch/arm/mach-sunxi/dram_timings/a133_ddr4.c b/arch/arm/mach-sunxi/dram_timings/a133_ddr4.c
new file mode 100644
index 00000000000..dec208e22df
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_timings/a133_ddr4.c
@@ -0,0 +1,80 @@
+#include <asm/arch/cpu.h>
+#include <asm/arch/dram.h>
+
+void mctl_set_timing_params(const struct dram_para *para)
+{
+ struct sunxi_mctl_ctl_reg *const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ u8 txsr = 4;
+ u8 tccd = 3;
+ u8 rd2wr = 5;
+ u8 tmrd = 4;
+ u8 tmrw = 0;
+ u8 wrlat = 5;
+ u8 rdlat = 7;
+ u8 wr2pre = 14;
+ u8 dfi_tphy_wrlat = 6;
+ u8 dfi_trddata_en = 10;
+
+ u8 tfaw = ns_to_t(35);
+ u8 trrd = max(ns_to_t(8), 2);
+ u8 txp = max(ns_to_t(6), 2);
+ u8 tmrd_pda = max(ns_to_t(10), 8);
+ u8 trp = ns_to_t(15);
+ u8 trc = ns_to_t(49);
+ u8 wr2rd_s = max(ns_to_t(3), 1) + 7;
+ u8 tras_min = ns_to_t(34);
+ u16 trefi_x32 = ns_to_t(7800) / 32;
+ u16 trfc_min = ns_to_t(350);
+ u16 txs_x32 = ns_to_t(360) / 32;
+ u16 tmod = max(ns_to_t(15), 12);
+ u8 tcke = max(ns_to_t(5), 2);
+ u8 tcksrx = max(ns_to_t(10), 3);
+ u8 txs_abort_x32 = ns_to_t(170) / 32;
+ u8 tras_max = ns_to_t(70200) / 1024;
+
+ u8 rd2pre = (trp < 5 ? 9 - trp : 4);
+ u8 wr2rd = trrd + 7;
+ u8 tckesr = tcke + 1;
+ u8 trcd = trp;
+ u8 trrd_s = txp;
+ u8 tcksre = tcksrx;
+
+ writel(tras_min | tras_max << 8 | tfaw << 16 | wr2pre << 24,
+ &mctl_ctl->dramtmg[0]);
+ writel(trc | rd2pre << 8 | txp << 16, &mctl_ctl->dramtmg[1]);
+ writel(wr2rd | rd2wr << 8 | rdlat << 16 | wrlat << 24,
+ &mctl_ctl->dramtmg[2]);
+ writel(tmod | tmrd << 12 | tmrw << 20, &mctl_ctl->dramtmg[3]);
+ writel(trp | trrd << 8 | tccd << 16 | trcd << 24,
+ &mctl_ctl->dramtmg[4]);
+ writel(tcke | tckesr << 8 | tcksre << 16 | tcksrx << 24,
+ &mctl_ctl->dramtmg[5]);
+ writel((txp + 2) | 0x20 << 16 | 0x20 << 24,
+ &mctl_ctl->dramtmg[6]);
+ writel(txs_x32 | 0x10 << 8 | txs_abort_x32 << 16 | txs_abort_x32 << 24,
+ &mctl_ctl->dramtmg[8]);
+ writel(wr2rd_s | trrd_s << 8 | 0x2 << 16, &mctl_ctl->dramtmg[9]);
+ writel(0xe0c05, &mctl_ctl->dramtmg[10]);
+ writel(0x440c021c, &mctl_ctl->dramtmg[11]);
+ writel(tmrd_pda, &mctl_ctl->dramtmg[12]);
+ writel(0xa100002, &mctl_ctl->dramtmg[13]);
+ writel(txsr, &mctl_ctl->dramtmg[14]);
+
+ clrsetbits_le32(&mctl_ctl->init[0], 0xc0000fff, 1008);
+ writel(0x1f20000, &mctl_ctl->init[1]);
+ clrsetbits_le32(&mctl_ctl->init[2], 0xff0f, 0xd05);
+ writel(0, &mctl_ctl->dfimisc);
+
+ writel(0x840 << 16 | 0x601, &mctl_ctl->init[3]); /* MR0 / MR1 */
+ writel(0x8 << 16 | 0x0, &mctl_ctl->init[4]); /* MR2 / MR3 */
+ writel(0x0 << 16 | 0x400, &mctl_ctl->init[6]); /* MR4 / MR5 */
+ writel(0x826, &mctl_ctl->init[7]); /* MR6 */
+
+ clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
+ writel((dfi_tphy_wrlat - 1) | 0x2000000 | (dfi_trddata_en - 1) << 16 |
+ 0x808000, &mctl_ctl->dfitmg0);
+ writel(0x100202, &mctl_ctl->dfitmg1);
+ writel(trfc_min | trefi_x32 << 16, &mctl_ctl->rfshtmg);
+}
diff --git a/arch/arm/mach-sunxi/dram_timings/a133_lpddr4.c b/arch/arm/mach-sunxi/dram_timings/a133_lpddr4.c
new file mode 100644
index 00000000000..1e607381023
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_timings/a133_lpddr4.c
@@ -0,0 +1,102 @@
+#include <asm/arch/cpu.h>
+#include <asm/arch/dram.h>
+
+void mctl_set_timing_params(const struct dram_para *para)
+{
+ struct sunxi_mctl_ctl_reg *const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+
+ bool tpr13_flag1 = para->tpr13 & BIT(28);
+ bool tpr13_flag2 = para->tpr13 & BIT(3);
+ bool tpr13_flag3 = para->tpr13 & BIT(5);
+
+ u8 tccd = 4;
+ u8 tfaw = ns_to_t(40);
+ u8 trrd = max(ns_to_t(10), 2);
+ u8 trcd = max(ns_to_t(18), 2);
+ u8 trc = ns_to_t(65);
+ u8 txp = max(ns_to_t(8), 2);
+
+ u8 trp = ns_to_t(21);
+ u8 tras_min = ns_to_t(42);
+ u16 trefi_x32 = ns_to_t(3904) / 32;
+ u16 trfc_min = ns_to_t(180);
+ u16 txsr = ns_to_t(190);
+
+ u8 tmrw = max(ns_to_t(14), 5);
+ u8 tmrd = max(ns_to_t(14), 5);
+ u8 tmod = 12;
+ u8 tcke = max(ns_to_t(15), 2);
+ u8 tcksrx = max(ns_to_t(2), 2);
+ u8 tcksre = max(ns_to_t(5), 2);
+ u8 tckesr = max(ns_to_t(15), 2);
+ u8 tras_max = (trefi_x32 * 9) / 32;
+ u8 txs_x32 = 4;
+ u8 txsabort_x32 = 4;
+
+ u8 wrlat = 5;
+ u8 wr2rd_s = 8;
+ u8 trrd_s = 2;
+ u8 tmrd_pda = 8;
+
+ u8 wr2pre = 24;
+ u8 rd2pre = 4;
+ u8 wr2rd = 14 + max(ns_to_t(tpr13_flag1 ? 10 : 12), 4);
+ u8 rd2wr = 17 + ns_to_t(4) - ns_to_t(1);
+ u8 tphy_wrlat = 5;
+
+ u8 rdlat = 10;
+ u8 trddata_en = 17;
+
+ if (tpr13_flag1) {
+ rdlat = 11;
+ trddata_en = 19;
+ }
+
+ writel(tras_min | tras_max << 8 | tfaw << 16 | wr2pre << 24,
+ &mctl_ctl->dramtmg[0]);
+ writel(trc | rd2pre << 8 | txp << 16, &mctl_ctl->dramtmg[1]);
+ writel(wr2rd | rd2wr << 8 | rdlat << 16 | wrlat << 24,
+ &mctl_ctl->dramtmg[2]);
+ writel(tmod | tmrd << 12 | tmrw << 20, &mctl_ctl->dramtmg[3]);
+ writel(trp | trrd << 8 | tccd << 16 | trcd << 24,
+ &mctl_ctl->dramtmg[4]);
+ writel(tcke | tckesr << 8 | tcksre << 16 | tcksrx << 24,
+ &mctl_ctl->dramtmg[5]);
+ writel((txp + 2) | 0x20 << 16 | 0x20 << 24, &mctl_ctl->dramtmg[6]);
+ writel(txs_x32 | 0x10 << 8 | txsabort_x32 << 16 | txsabort_x32 << 24,
+ &mctl_ctl->dramtmg[8]);
+ writel(wr2rd_s | trrd_s << 8 | 0x2 << 16, &mctl_ctl->dramtmg[9]);
+ writel(0xe0c05, &mctl_ctl->dramtmg[10]);
+ writel(0x440c021c, &mctl_ctl->dramtmg[11]);
+ writel(tmrd_pda, &mctl_ctl->dramtmg[12]);
+ writel(0xa100002, &mctl_ctl->dramtmg[13]);
+ writel(txsr, &mctl_ctl->dramtmg[14]);
+
+ clrsetbits_le32(&mctl_ctl->init[0], 0xc0000fff, 1008);
+
+ if (tpr13_flag2)
+ writel(0x420000, &mctl_ctl->init[1]);
+ else
+ writel(0x1f20000, &mctl_ctl->init[1]);
+
+ clrsetbits_le32(&mctl_ctl->init[2], 0xff0f, 0xd05);
+ writel(0, &mctl_ctl->dfimisc);
+
+ writel(0x34 << 16 | 0x1b, &mctl_ctl->init[3]); /* MR1/MR2 */
+ writel(0x33 << 16, &mctl_ctl->init[4]); /* MR3 */
+ writel(para->mr11 << 16 | para->mr12, &mctl_ctl->init[6]);
+ writel(para->tpr1 << 16 | para->mr14, &mctl_ctl->init[7]);
+
+ clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
+ if (!tpr13_flag3) {
+ tphy_wrlat -= 1;
+ trddata_en -= 1;
+ }
+
+ writel(tphy_wrlat | trddata_en << 16 | 0x808000 | 0x2000000,
+ &mctl_ctl->dfitmg0);
+ writel(0x100202, &mctl_ctl->dfitmg1);
+
+ writel(trfc_min | trefi_x32 << 16, &mctl_ctl->rfshtmg);
+}
diff --git a/board/sunxi/MAINTAINERS b/board/sunxi/MAINTAINERS
index 84799879e85..1b4b7d87163 100644
--- a/board/sunxi/MAINTAINERS
+++ b/board/sunxi/MAINTAINERS
@@ -296,6 +296,11 @@ M: Adam Sampson <ats@offog.org>
S: Maintained
F: configs/Linksprite_pcDuino3_Nano_defconfig
+LIONTRON H-A133L BOARD
+M: Andre Przywara <andre.przywara@arm.com>
+S: Maintained
+F: configs/liontron-h-a133l_defconfig
+
MARSBOARD-A10 BOARD
M: Aleksei Mamlin <mamlinav@gmail.com>
S: Maintained
@@ -596,6 +601,11 @@ M: Peter Korsgaard <peter@korsgaard.com>
S: Maintained
F: configs/Yones_Toptech_BS1078_V2_defconfig
+YUZUKIHD CHAMELEON BOARD
+M: Andre Przywara <andre.przywara@arm.com>
+S: Maintained
+F: configs/yuzukihd-chameleon_defconfig
+
ZEROPI BOARD
M: Yu-Tung Chang <mtwget@gmail.com>
S: Maintained
diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index feec95658a2..943b6221b8a 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -114,7 +114,7 @@ void i2c_init_board(void)
clock_twi_onoff(5, 1);
sunxi_gpio_set_cfgpin(SUNXI_GPL(8), SUN50I_GPL_R_TWI);
sunxi_gpio_set_cfgpin(SUNXI_GPL(9), SUN50I_GPL_R_TWI);
-#elif CONFIG_MACH_SUN50I_H616
+#elif defined(CONFIG_MACH_SUN50I_H616)
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);
@@ -435,7 +435,7 @@ 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)
+#elif defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133)
/* 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 880192043c4..9a17ccb2d3d 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
+ default 0xbfa0 if MACH_SUN50I_H616 || MACH_SUN50I_A133
default 0x7000 if RCAR_GEN3
default 0x5fa0 if SUNXI_SRAM_ADDRESS = 0x0
default 0x7fa0 if ARCH_SUNXI
@@ -423,7 +423,7 @@ config SPL_STACK
default 0x91ffb8 if ARCH_MX6 && !MX6_OCRAM_256KB
default 0x118000 if MACH_SUN50I_H6
default 0x52a00 if MACH_SUN50I_H616
- default 0x40000 if MACH_SUN8I_R528
+ default 0x40000 if MACH_SUN8I_R528 || MACH_SUN50I_A133
default 0x54000 if MACH_SUN50I || MACH_SUN50I_H5
default 0x18000 if MACH_SUN9I
default 0x8000 if ARCH_SUNXI
diff --git a/configs/liontron-h-a133l_defconfig b/configs/liontron-h-a133l_defconfig
new file mode 100644
index 00000000000..4b769768e5f
--- /dev/null
+++ b/configs/liontron-h-a133l_defconfig
@@ -0,0 +1,37 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun50i-a133-liontron-h-a133l"
+CONFIG_SPL=y
+CONFIG_DRAM_SUNXI_DX_ODT=0x7070707
+CONFIG_DRAM_SUNXI_DX_DRI=0xd0d0d0d
+CONFIG_DRAM_SUNXI_CA_DRI=0xe0e
+CONFIG_DRAM_SUNXI_PARA0=0xd0a050c
+CONFIG_DRAM_SUNXI_MR11=0x4
+CONFIG_DRAM_SUNXI_MR12=0x72
+CONFIG_DRAM_SUNXI_MR13=0x0
+CONFIG_DRAM_SUNXI_MR14=0x7
+CONFIG_DRAM_SUNXI_TPR1=0x26
+CONFIG_DRAM_SUNXI_TPR2=0x6060606
+CONFIG_DRAM_SUNXI_TPR3=0x84040404
+CONFIG_DRAM_SUNXI_TPR6=0x48000000
+CONFIG_DRAM_SUNXI_TPR10=0x273333
+CONFIG_DRAM_SUNXI_TPR11=0x231d151c
+CONFIG_DRAM_SUNXI_TPR12=0x1212110e
+CONFIG_DRAM_SUNXI_TPR13=0x7521
+CONFIG_DRAM_SUNXI_TPR14=0x2023211f
+CONFIG_MACH_SUN50I_A133=y
+CONFIG_DRAM_CLK=792
+CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+CONFIG_SUNXI_DRAM_A133_LPDDR4=y
+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_AXP803_POWER=y
+CONFIG_AXP_DCDC5_VOLT=1100
+CONFIG_SUPPORT_EMMC_BOOT=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
diff --git a/configs/yuzukihd-chameleon_defconfig b/configs/yuzukihd-chameleon_defconfig
new file mode 100644
index 00000000000..883be12c4ef
--- /dev/null
+++ b/configs/yuzukihd-chameleon_defconfig
@@ -0,0 +1,30 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun50i-h618-yuzukihd-chameleon"
+CONFIG_SPL=y
+CONFIG_DRAM_SUNXI_DX_ODT=0x03030303
+CONFIG_DRAM_SUNXI_DX_DRI=0x0e0e0e0e
+CONFIG_DRAM_SUNXI_CA_DRI=0x1c12
+CONFIG_DRAM_SUNXI_ODT_EN=0x1
+CONFIG_DRAM_SUNXI_TPR6=0x33808080
+CONFIG_DRAM_SUNXI_TPR10=0x002f0006
+CONFIG_DRAM_SUNXI_TPR11=0xddddcccc
+CONFIG_DRAM_SUNXI_TPR12=0xeddc7564
+CONFIG_MACH_SUN50I_H616=y
+CONFIG_SUNXI_DRAM_H616_DDR3_1333=y
+CONFIG_DRAM_CLK=648
+CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+CONFIG_R_I2C_ENABLE=y
+CONFIG_SPL_SPI_SUNXI=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_AXP313_POWER=y
+CONFIG_AXP_DCDC3_VOLT=1500
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_MUSB_GADGET=y
diff --git a/dts/upstream/src/arm64/allwinner/sun50i-a100.dtsi b/dts/upstream/src/arm64/allwinner/sun50i-a100.dtsi
index f9f6fea03b7..bd366389b23 100644
--- a/dts/upstream/src/arm64/allwinner/sun50i-a100.dtsi
+++ b/dts/upstream/src/arm64/allwinner/sun50i-a100.dtsi
@@ -252,6 +252,7 @@
interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&mmc0_pins>;
+ max-frequency = <150000000>;
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
@@ -267,6 +268,7 @@
interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&mmc1_pins>;
+ max-frequency = <150000000>;
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
@@ -282,6 +284,7 @@
interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&mmc2_pins>;
+ max-frequency = <150000000>;
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/dts/upstream/src/arm64/allwinner/sun50i-a133-liontron-h-a133l.dts b/dts/upstream/src/arm64/allwinner/sun50i-a133-liontron-h-a133l.dts
new file mode 100644
index 00000000000..fe77178d3e3
--- /dev/null
+++ b/dts/upstream/src/arm64/allwinner/sun50i-a133-liontron-h-a133l.dts
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2025 Arm Ltd.
+ */
+
+/dts-v1/;
+
+#include "sun50i-a100.dtsi"
+#include "sun50i-a100-cpu-opp.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/leds/common.h>
+
+/{
+ model = "Liontron H-A133L";
+ compatible = "liontron,h-a133l", "allwinner,sun50i-a100";
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led {
+ function = LED_FUNCTION_POWER;
+ color = <LED_COLOR_ID_BLUE>;
+ gpios = <&pio 7 16 GPIO_ACTIVE_LOW>; /* PH16 */
+ };
+ };
+
+ reg_vcc5v: vcc5v {
+ /* board wide 5V supply from a 12V->5V regulator */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-5v";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ reg_usb1_vbus: regulator-usb1-vbus {
+ compatible = "regulator-fixed";
+ regulator-name = "usb1-vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <&reg_vcc5v>;
+ enable-active-high;
+ gpio = <&r_pio 0 8 GPIO_ACTIVE_HIGH>; /* PL8 */
+ };
+};
+
+&cpu0 {
+ cpu-supply = <&reg_dcdc2>;
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&mmc0 {
+ vmmc-supply = <&reg_dcdc1>;
+ cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */
+ bus-width = <4>;
+ status = "okay";
+};
+
+&mmc2 {
+ vmmc-supply = <&reg_dcdc1>;
+ vqmmc-supply = <&reg_eldo1>;
+ cap-mmc-hw-reset;
+ non-removable;
+ bus-width = <8>;
+ mmc-ddr-1_8v;
+ mmc-hs200-1_8v;
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&pio {
+ vcc-pb-supply = <&reg_dcdc1>;
+ vcc-pc-supply = <&reg_eldo1>;
+ vcc-pf-supply = <&reg_dcdc1>;
+ vcc-ph-supply = <&reg_dcdc1>;
+};
+
+&r_i2c0 {
+ status = "okay";
+
+ axp803: pmic@34 {
+ compatible = "x-powers,axp803";
+ reg = <0x34>;
+ interrupt-parent = <&r_intc>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ };
+};
+
+#include "axp803.dtsi"
+
+&ac_power_supply {
+ status = "okay";
+};
+
+&reg_aldo1 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-codec-avcc";
+};
+
+&reg_aldo2 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-dram-1";
+};
+
+&reg_aldo3 {
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-usb-pl";
+};
+
+&reg_dcdc1 {
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-io-usb-pd-emmc";
+};
+
+&reg_dcdc2 {
+ regulator-always-on;
+ regulator-min-microvolt = <810000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-name = "vdd-cpux";
+};
+
+&reg_dcdc3 {
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ regulator-name = "vdd-usb-cpus";
+};
+
+&reg_dcdc4 {
+ regulator-always-on;
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <950000>;
+ regulator-name = "vdd-sys";
+};
+
+&reg_dcdc5 {
+ regulator-always-on;
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-name = "vcc-dram";
+};
+
+/* DCDC6 unused */
+/* DLDO3 unused */
+/* DLDO4 unused */
+
+&reg_eldo1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-pc-emmc";
+};
+
+/* ELDO2 unused */
+/* ELDO3 unused */
+
+&reg_fldo1 {
+ regulator-always-on;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ regulator-name = "vdd-cpus-usb";
+};
+
+/* reg_drivevbus unused */
+/* dc1sw unused */
+
+&uart0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pb_pins>;
+ status = "okay";
+};
+
+&usb_otg {
+ dr_mode = "host"; /* USB A type receptacle, always powered */
+ status = "okay";
+};
+
+&usbphy {
+ usb1_vbus-supply = <&reg_usb1_vbus>;
+ status = "okay";
+};
diff --git a/dts/upstream/src/arm64/allwinner/sun50i-h618-yuzukihd-chameleon.dts b/dts/upstream/src/arm64/allwinner/sun50i-h618-yuzukihd-chameleon.dts
new file mode 100644
index 00000000000..eae56908b9b
--- /dev/null
+++ b/dts/upstream/src/arm64/allwinner/sun50i-h618-yuzukihd-chameleon.dts
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright (C) 2024 Arm Ltd.
+ */
+
+/dts-v1/;
+
+#include "sun50i-h616.dtsi"
+#include "sun50i-h616-cpu-opp.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+ model = "Yuzuki Chameleon";
+ compatible = "yuzukihd,chameleon", "allwinner,sun50i-h618";
+
+ aliases {
+ ethernet1 = &sdio_wifi;
+ serial0 = &uart0;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ reg_vcc5v: vcc5v {
+ /* board wide 5V supply directly from the USB-C socket */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-5v";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ wifi_pwrseq: pwrseq {
+ compatible = "mmc-pwrseq-simple";
+ clocks = <&rtc CLK_OSC32K_FANOUT>;
+ clock-names = "ext_clock";
+ pinctrl-0 = <&x32clk_fanout_pin>;
+ pinctrl-names = "default";
+ reset-gpios = <&pio 6 11 GPIO_ACTIVE_LOW>; /* PG11 */
+ };
+};
+
+&codec {
+ allwinner,audio-routing = "Line Out", "LINEOUT";
+ status = "okay";
+};
+
+&cpu0 {
+ cpu-supply = <&reg_dcdc2>;
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&ehci2 {
+ status = "okay";
+};
+
+&ehci3 {
+ status = "okay";
+};
+
+&mmc0 {
+ bus-width = <4>;
+ cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */
+ disable-wp;
+ vmmc-supply = <&reg_dldo1>;
+ status = "okay";
+};
+
+&mmc1 {
+ bus-width = <4>;
+ mmc-pwrseq = <&wifi_pwrseq>;
+ non-removable;
+ vmmc-supply = <&reg_dldo1>;
+ vqmmc-supply = <&reg_dldo1>;
+ status = "okay";
+
+ sdio_wifi: wifi@1 {
+ reg = <1>;
+ interrupt-parent = <&pio>;
+ interrupts = <6 12 IRQ_TYPE_LEVEL_LOW>; /* PG12 */
+ interrupt-names = "host-wake";
+ };
+};
+
+&mmc2 {
+ bus-width = <8>;
+ cap-mmc-hw-reset;
+ mmc-ddr-3_3v;
+ non-removable;
+ vmmc-supply = <&reg_dldo1>;
+ vqmmc-supply = <&reg_dldo1>;
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&ohci2 {
+ status = "okay";
+};
+
+&ohci3 {
+ status = "okay";
+};
+
+&pio {
+ vcc-pc-supply = <&reg_dldo1>;
+ vcc-pf-supply = <&reg_dldo1>; /* via VCC_IO */
+ vcc-pg-supply = <&reg_dldo1>;
+ vcc-ph-supply = <&reg_dldo1>; /* via VCC_IO */
+ vcc-pi-supply = <&reg_dldo1>;
+};
+
+&r_i2c {
+ status = "okay";
+
+ axp313: pmic@36 {
+ compatible = "x-powers,axp313a";
+ reg = <0x36>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ interrupt-parent = <&pio>;
+ interrupts = <2 2 IRQ_TYPE_LEVEL_LOW>; /* PC2 */
+
+ vin1-supply = <&reg_vcc5v>;
+ vin2-supply = <&reg_vcc5v>;
+ vin3-supply = <&reg_vcc5v>;
+
+ regulators {
+ /* Supplies VCC-PLL, so needs to be always on. */
+ reg_aldo1: aldo1 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc1v8";
+ };
+
+ /* Supplies VCC-IO, so needs to be always on. */
+ reg_dldo1: dldo1 {
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc3v3";
+ };
+
+ reg_dcdc1: dcdc1 {
+ regulator-always-on;
+ regulator-min-microvolt = <810000>;
+ regulator-max-microvolt = <990000>;
+ regulator-name = "vdd-gpu-sys";
+ };
+
+ reg_dcdc2: dcdc2 {
+ regulator-always-on;
+ regulator-min-microvolt = <810000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-name = "vdd-cpu";
+ };
+
+ reg_dcdc3: dcdc3 {
+ regulator-always-on;
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-name = "vdd-dram";
+ };
+ };
+ };
+};
+
+&uart0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_ph_pins>;
+ status = "okay";
+};
+
+/* Connected to the Bluetooth UART pins of the XR829 Wifi/BT chip. */
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart1_pins>, <&uart1_rts_cts_pins>;
+ uart-has-rtscts;
+ status = "okay";
+};
+
+&usbotg {
+ /*
+ * PHY0 pins are connected to a USB-C socket, but a role switch
+ * is not implemented: both CC pins are pulled to GND.
+ * The VBUS pins power the device, so a fixed peripheral mode
+ * is the best choice.
+ * The board can be powered via GPIOs, in this case port0 *can*
+ * act as a host (with a cable/adapter ignoring CC), as VBUS is
+ * then provided by the GPIOs. Any user of this setup would
+ * need to adjust the DT accordingly: dr_mode set to "host",
+ * enabling OHCI0 and EHCI0.
+ */
+ dr_mode = "peripheral";
+ status = "okay";
+};
+
+&usbphy {
+ usb0_id_det-gpios = <&pio 6 18 GPIO_ACTIVE_HIGH>; /* PG18 */
+ usb0_vbus-supply = <&reg_vcc5v>;
+ usb1_vbus-supply = <&reg_vcc5v>;
+ usb2_vbus-supply = <&reg_vcc5v>;
+ usb3_vbus-supply = <&reg_vcc5v>;
+ status = "okay";
+};