U-Boot Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support
@ 2025-03-23 11:35 Andre Przywara
  2025-03-23 11:35 ` [PATCH 01/34] sunxi: clock: H6: drop usage of struct sunxi_ccm_reg Andre Przywara
                   ` (34 more replies)
  0 siblings, 35 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

This series introduces support for the Allwinner A523 SoC family. The
same die is used in different packages: the A523, A527, T527, and H728:
they connect a different set of peripherals to the pins, or enable extra
goodies like an NPU. From a U-Boot perspective those chips do not differ
much, all the differences are described in the board DT files.
These patches are not the most refined at the moment, but I hope that
people start reviewing them, so we can merge the ones that are ready,
to reduce their number.

To be able to share the SPL clock code, the existing H6 code gets
refactored in patches 01-13. This removes the C struct describing the
127 clock registers, and replaces it with macros defining the register
offsets. For more rationale and explanation see the 01/34 commit message.

Patches 14-20 extend the existing Allwinner U-Boot drivers to cope with
some of the changed peripherals, this includes the mandatory clock and
pinctrl drivers, but also some clock tweaks for the MMC controller
driver, and support for the new watchdog and the AXP323 used.

Patches 21-23 update some SPL bits to be able to cope with the A523.
Patches 24-28 extend the FEL handling code: the A523 has a GICv3, which
requires saving some GICv3 system registers, plus the IRQ mode stack
pointer. Patches 25-27 refactor the CPU clock code, and add the new
clock bits required by the A523.

Patch 29-32 add the new SPL bits for the A523, most prominently the DRAM
initialisation code. Many thanks to Jernej and Mikhail for providing
this part, there is a great reverse engineering and testing effort behind
this.

Patch 33 copies the DT files from the proposed Linux patches. They have
not been merged yet, mostly due to one missing DT binding dependency in
the linux-next tree, but have otherwise been agreed upon, with almost
every used binding being already merged. Eventually we will use the copy
from the DT rebasing repo, but for now those files must do.

The final patch adds defconfig files for the three boards that seem to be
the most popular at the moment, they include two development boards and
one TV box. The most interesting bits in there are the DRAM parameters.

Please have a look, review, and test.

Thanks!
Andre

Andre Przywara (30):
  sunxi: clock: H6: drop usage of struct sunxi_ccm_reg
  sunxi: mmc: remove usage of struct sunxi_ccm_reg
  sunxi: H616: dram: remove usage of struct sunxi_ccm_reg
  sunxi: H6: dram: remove usage of struct sunxi_ccm_reg
  sunxi: clock: H6: remove struct sunxi_ccm_reg
  sunxi: clock: H6: drop usage of struct sunxi_prcm_reg
  sunxi: H6/H616: dram: remove usage of struct sunxi_prcm_reg
  sunxi: clock: H6: remove struct sunxi_prcm_reg
  sunxi: clock: H6: unify PLL control bit definitions
  sunxi: clock: H6: factor out clock_set_pll()
  sunxi: clock: H6: factor out H6/H616 CPU clock setup
  sunxi: clock: H6: add A523 CPU PLL support
  dt-bindings: add Allwinner A523 CCU bindings
  clk: sunxi: Add support for the A523 CCU
  clk: sunxi: Add support for the A523 -R CCU
  pinctrl: sunxi: add Allwinner A523 pinctrl description
  sunxi: mmc: add support for Allwinner A523 MMC mod clock
  watchdog: sunxi: add A523 support
  power: regulator: add AXP323 support
  sunxi: update cpu_sunxi_ncat2.h
  sunxi: Kconfig: consolidate SYS_CLK_FREQ selection
  spl: reorder SPL_MAX_SIZE defaults for sunxi
  sunxi: armv8: fel: move fel_stash variable to the front
  sunxi: arm64: boot0.h: move fel_stash_addr variable to the front
  sunxi: update rmr_switch.S source code
  sunxi: armv8: FEL: save and restore GICv3 registers
  sunxi: armv8: FEL: save and restore SP_IRQ
  sunxi: add basic A523 support
  sunxi: A523: add DT files from Linux v3 branch
  sunxi: A523: add defconfigs for three boards

Jernej Skrabec (3):
  sunxi: spl: add support for Allwinner A523 watchdog
  sunxi: sun50i_h6: add A523 SPL clock setup code
  sunxi: A523: add DRAM initialisation routine

Mikhail Kalashnikov (1):
  sunxi: A523: add DDR3 DRAM support

 arch/arm/cpu/armv8/fel_utils.S                |   20 +-
 arch/arm/dts/sun55i-a523.dtsi                 |  598 +++++++
 arch/arm/dts/sun55i-a527-radxa-a5e.dts        |  299 ++++
 arch/arm/dts/sun55i-h728-x96qpro+.dts         |  287 +++
 arch/arm/dts/sun55i-t527-avaota-a1.dts        |  308 ++++
 arch/arm/include/asm/arch-sunxi/boot0.h       |   20 +-
 arch/arm/include/asm/arch-sunxi/clock_sun4i.h |    6 +
 .../include/asm/arch-sunxi/clock_sun50i_h6.h  |  280 +--
 arch/arm/include/asm/arch-sunxi/clock_sun6i.h |    7 +
 .../include/asm/arch-sunxi/clock_sun8i_a83t.h |    7 +
 arch/arm/include/asm/arch-sunxi/clock_sun9i.h |    7 +
 .../include/asm/arch-sunxi/cpu_sunxi_ncat2.h  |   20 +
 arch/arm/include/asm/arch-sunxi/dram.h        |    2 +
 .../include/asm/arch-sunxi/dram_sun55i_a523.h |  183 ++
 arch/arm/include/asm/arch-sunxi/prcm_sun50i.h |   44 +-
 arch/arm/include/asm/arch-sunxi/watchdog.h    |   12 +
 arch/arm/mach-sunxi/Kconfig                   |   69 +-
 arch/arm/mach-sunxi/Makefile                  |    2 +
 arch/arm/mach-sunxi/board.c                   |   14 +
 arch/arm/mach-sunxi/clock_sun50i_h6.c         |  203 ++-
 arch/arm/mach-sunxi/cpu_info.c                |    2 +
 arch/arm/mach-sunxi/dram_sun50i_h6.c          |   41 +-
 arch/arm/mach-sunxi/dram_sun50i_h616.c        |   41 +-
 arch/arm/mach-sunxi/dram_sun55i_a523.c        | 1592 +++++++++++++++++
 arch/arm/mach-sunxi/dram_timings/Makefile     |    2 +
 arch/arm/mach-sunxi/dram_timings/a523_ddr3.c  |  135 ++
 .../arm/mach-sunxi/dram_timings/a523_lpddr4.c |  119 ++
 arch/arm/mach-sunxi/rmr_switch.S              |   52 +-
 board/sunxi/board.c                           |    6 +-
 common/spl/Kconfig                            |    7 +-
 configs/avaota-a1_defconfig                   |   31 +
 configs/radxa-a5e_defconfig                   |   30 +
 configs/x96q_pro_plus_defconfig               |   33 +
 drivers/clk/sunxi/Kconfig                     |   14 +
 drivers/clk/sunxi/Makefile                    |    2 +
 drivers/clk/sunxi/clk_a523.c                  |   85 +
 drivers/clk/sunxi/clk_a523_r.c                |   44 +
 drivers/clk/sunxi/clk_sunxi.c                 |   10 +
 drivers/mmc/sunxi_mmc.c                       |   36 +-
 drivers/pinctrl/sunxi/Kconfig                 |   10 +
 drivers/pinctrl/sunxi/pinctrl-sunxi.c         |   49 +
 drivers/power/pmic/axp.c                      |    1 +
 drivers/power/regulator/axp_regulator.c       |    1 +
 drivers/watchdog/sunxi_wdt.c                  |   11 +
 include/axp_pmic.h                            |    1 +
 include/configs/sun55i.h                      |   11 +
 include/dt-bindings/clock/sun55i-a523-ccu.h   |  189 ++
 include/dt-bindings/clock/sun55i-a523-r-ccu.h |   37 +
 include/dt-bindings/reset/sun55i-a523-ccu.h   |   88 +
 include/dt-bindings/reset/sun55i-a523-r-ccu.h |   25 +
 50 files changed, 4685 insertions(+), 408 deletions(-)
 create mode 100644 arch/arm/dts/sun55i-a523.dtsi
 create mode 100644 arch/arm/dts/sun55i-a527-radxa-a5e.dts
 create mode 100644 arch/arm/dts/sun55i-h728-x96qpro+.dts
 create mode 100644 arch/arm/dts/sun55i-t527-avaota-a1.dts
 create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
 create mode 100644 arch/arm/mach-sunxi/dram_sun55i_a523.c
 create mode 100644 arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
 create mode 100644 arch/arm/mach-sunxi/dram_timings/a523_lpddr4.c
 create mode 100644 configs/avaota-a1_defconfig
 create mode 100644 configs/radxa-a5e_defconfig
 create mode 100644 configs/x96q_pro_plus_defconfig
 create mode 100644 drivers/clk/sunxi/clk_a523.c
 create mode 100644 drivers/clk/sunxi/clk_a523_r.c
 create mode 100644 include/configs/sun55i.h
 create mode 100644 include/dt-bindings/clock/sun55i-a523-ccu.h
 create mode 100644 include/dt-bindings/clock/sun55i-a523-r-ccu.h
 create mode 100644 include/dt-bindings/reset/sun55i-a523-ccu.h
 create mode 100644 include/dt-bindings/reset/sun55i-a523-r-ccu.h

-- 
2.46.3


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH 01/34] sunxi: clock: H6: drop usage of struct sunxi_ccm_reg
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:56   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 02/34] sunxi: mmc: remove " Andre Przywara
                   ` (33 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

U-Boot drivers often revert to using C structures for modelling hardware
register frames. This creates some problems:
- A "struct" is a C language construct to group several variables
  together. The details of the layout of this struct are partly subject
  to the compiler's discretion (padding and alignment).
- The "packed" attribute would force a certain layout, but we are not
  using it.
- The actual source of information from the data sheet is the register
  offset. Here we create an artificial struct, carefully tuning the
  layout (with a lot of reserved members) to match that offset. To help
  with correctness, we put the desired information as a *comment*,
  though this is purely for the human reader, and has no effect on the
  generated layout. This sounds all very backwards.
- Using a struct suggests we can assign a pointer and then access the
  register content via the members. But this is not the case, instead
  every MMIO register access must go through specific accessor functions,
  to meet the ordering and access size guarantees the hardware requires.
- We share those structs in code shared across multiple SoC families,
  though most SoCs define their own version of the struct. Members must
  match in their name, across every SoC, otherwise compilation will fail.
  We work around this with even more #ifdefs in the shared code.
- Some SoCs have an *almost* identical layout, but differ in a few
  registers. This requires hard to maintain #ifdef's in the struct
  definition.
- Some of the register frames are huge: the H6 CCU device defines 127
  registers. We use 15 of them. Still the whole frame would need to be
  described, which is very tedious, but for no reason.
- Adding a new SoC often forces people to decide whether to share an
  existing struct, or to create a new copy. For some cases (say like 80%
  similarity) this works out badly either way.

The Linux kernel heavily frowns upon those register structs, and instead
uses a much simpler solution: #define REG_NAME	<offset>
This easily maps to the actual information from the data sheet, and can
much simpler be shared across multiple SoCs, as it allows to have all
SoC versions visible, so we can use C "if" statements instead of #ifdef's.
Also it requires to just define the registers we need, and we can use
alternative locations for some registers much more easily.

Drop the usage of "struct sunxi_ccm_reg" in the H6 SPL clock code, by
defining the respective register names and their offsets, then adding
them to the base pointer.
We cannot drop the struct definition quite yet, as it's also used in
other drivers, still.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 .../include/asm/arch-sunxi/clock_sun50i_h6.h  | 12 +++++
 arch/arm/mach-sunxi/clock_sun50i_h6.c         | 52 +++++++++----------
 2 files changed, 36 insertions(+), 28 deletions(-)

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 76dd33c9477..a485e00f1f3 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,18 @@
 #include <linux/bitops.h>
 #endif
 
+#define CCU_H6_PLL1_CFG			0x000
+#define CCU_H6_PLL5_CFG			0x010
+#define CCU_H6_PLL6_CFG			0x020
+#define CCU_H6_CPU_AXI_CFG		0x500
+#define CCU_H6_PSI_AHB1_AHB2_CFG	0x510
+#define CCU_H6_AHB3_CFG			0x51c
+#define CCU_H6_APB1_CFG			0x520
+#define CCU_H6_APB2_CFG			0x524
+#define CCU_H6_MBUS_CFG			0x540
+#define CCU_H6_UART_GATE_RESET		0x90c
+#define CCU_H6_I2C_GATE_RESET		0x91c
+
 struct sunxi_ccm_reg {
 	u32 pll1_cfg;		/* 0x000 pll1 (cpux) control */
 	u8 reserved_0x004[12];
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index b424a7893ea..482d9e0c695 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -6,8 +6,7 @@
 #ifdef CONFIG_XPL_BUILD
 void clock_init_safe(void)
 {
-	struct sunxi_ccm_reg *const ccm =
-		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	void *const ccm = (void *)SUNXI_CCM_BASE;
 	struct sunxi_prcm_reg *const prcm =
 		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
 
@@ -32,60 +31,59 @@ void clock_init_safe(void)
 
 	clock_set_pll1(408000000);
 
-	writel(CCM_PLL6_DEFAULT, &ccm->pll6_cfg);
-	while (!(readl(&ccm->pll6_cfg) & CCM_PLL6_LOCK))
+	writel(CCM_PLL6_DEFAULT, ccm + CCU_H6_PLL6_CFG);
+	while (!(readl(ccm + CCU_H6_PLL6_CFG) & CCM_PLL6_LOCK))
 		;
 
-	clrsetbits_le32(&ccm->cpu_axi_cfg, CCM_CPU_AXI_APB_MASK | CCM_CPU_AXI_AXI_MASK,
+	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->psi_ahb1_ahb2_cfg);
+	writel(CCM_PSI_AHB1_AHB2_DEFAULT, ccm + CCU_H6_PSI_AHB1_AHB2_CFG);
 #ifdef CCM_AHB3_DEFAULT
-	writel(CCM_AHB3_DEFAULT, &ccm->ahb3_cfg);
+	writel(CCM_AHB3_DEFAULT, ccm + CCU_H6_AHB3_CFG);
 #endif
-	writel(CCM_APB1_DEFAULT, &ccm->apb1_cfg);
+	writel(CCM_APB1_DEFAULT, ccm + CCU_H6_APB1_CFG);
 
 	/*
 	 * The mux and factor are set, but the clock will be enabled in
 	 * DRAM initialization code.
 	 */
-	writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), &ccm->mbus_cfg);
+	writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), ccm + CCU_H6_MBUS_CFG);
 }
 
 void clock_init_uart(void)
 {
-	struct sunxi_ccm_reg *const ccm =
-		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	void *const ccm = (void *)SUNXI_CCM_BASE;
 
 	/* uart clock source is apb2 */
 	writel(APB2_CLK_SRC_OSC24M|
 	       APB2_CLK_RATE_N_1|
 	       APB2_CLK_RATE_M(1),
-	       &ccm->apb2_cfg);
+	       ccm + CCU_H6_APB2_CFG);
 
 	/* open the clock for uart */
-	setbits_le32(&ccm->uart_gate_reset,
+	setbits_le32(ccm + CCU_H6_UART_GATE_RESET,
 		     1 << (CONFIG_CONS_INDEX - 1));
 
 	/* deassert uart reset */
-	setbits_le32(&ccm->uart_gate_reset,
+	setbits_le32(ccm + CCU_H6_UART_GATE_RESET,
 		     1 << (RESET_SHIFT + CONFIG_CONS_INDEX - 1));
 }
 
 void clock_set_pll1(unsigned int clk)
 {
-	struct sunxi_ccm_reg * const ccm =
-		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	void *const ccm = (void *)SUNXI_CCM_BASE;
 	u32 val;
 
 	/* Do not support clocks < 288MHz as they need factor P */
 	if (clk < 288000000) clk = 288000000;
 
 	/* Switch to 24MHz clock while changing PLL1 */
-	val = readl(&ccm->cpu_axi_cfg);
+	val = readl(ccm + CCU_H6_CPU_AXI_CFG);
 	val &= ~CCM_CPU_AXI_MUX_MASK;
 	val |= CCM_CPU_AXI_MUX_OSC24M;
-	writel(val, &ccm->cpu_axi_cfg);
+	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;
@@ -94,20 +92,19 @@ void clock_set_pll1(unsigned int clk)
 	       val |= CCM_PLL1_OUT_EN;
 	if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
 	       val |= CCM_PLL1_OUT_EN | CCM_PLL1_LDO_EN;
-	writel(val, &ccm->pll1_cfg);
-	while (!(readl(&ccm->pll1_cfg) & CCM_PLL1_LOCK)) {}
+	writel(val, ccm + CCU_H6_PLL1_CFG);
+	while (!(readl(ccm + CCU_H6_PLL1_CFG) & CCM_PLL1_LOCK)) {}
 
 	/* Switch CPU to PLL1 */
-	val = readl(&ccm->cpu_axi_cfg);
+	val = readl(ccm + CCU_H6_CPU_AXI_CFG);
 	val &= ~CCM_CPU_AXI_MUX_MASK;
 	val |= CCM_CPU_AXI_MUX_PLL_CPUX;
-	writel(val, &ccm->cpu_axi_cfg);
+	writel(val, ccm + CCU_H6_CPU_AXI_CFG);
 }
 
 int clock_twi_onoff(int port, int state)
 {
-	struct sunxi_ccm_reg *const ccm =
-		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	void *const ccm = (void *)SUNXI_CCM_BASE;
 	struct sunxi_prcm_reg *const prcm =
 		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
 	u32 value, *ptr;
@@ -120,7 +117,7 @@ int clock_twi_onoff(int port, int state)
 		ptr = &prcm->twi_gate_reset;
 	} else {
 		shift = port;
-		ptr = &ccm->twi_gate_reset;
+		ptr = ccm + CCU_H6_I2C_GATE_RESET;
 	}
 
 	/* set the apb clock gate and reset for twi */
@@ -136,9 +133,8 @@ int clock_twi_onoff(int port, int state)
 /* PLL_PERIPH0 clock, used by the MMC driver */
 unsigned int clock_get_pll6(void)
 {
-	struct sunxi_ccm_reg *const ccm =
-		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
-	uint32_t rval = readl(&ccm->pll6_cfg);
+	void *const ccm = (void *)SUNXI_CCM_BASE;
+	uint32_t rval = readl(ccm + CCU_H6_PLL6_CFG);
 	int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT) + 1;
 	int div2 = ((rval & CCM_PLL6_CTRL_DIV2_MASK) >>
 		    CCM_PLL6_CTRL_DIV2_SHIFT) + 1;
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 02/34] sunxi: mmc: remove usage of struct sunxi_ccm_reg
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
  2025-03-23 11:35 ` [PATCH 01/34] sunxi: clock: H6: drop usage of struct sunxi_ccm_reg Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 12:04   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 03/34] sunxi: H616: dram: " Andre Przywara
                   ` (32 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi, Peng Fan, Jaehoon Chung

The Allwinner MMC code uses a complex C struct, modelling the clock
device's register frame. We rely on sharing the member names across all
Allwinner SoCs, which is fragile.

Drop the usage of the struct in the MMC code, by using #define'd
register names and their offset, and then adding those names to the base
pointer. This requires to define those offsets for all SoCs, but since we
only use between four and six clock registers in the MMC code, this is
easily done.

This removes one common user of the clock register struct.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/include/asm/arch-sunxi/clock_sun4i.h |  6 ++++++
 .../include/asm/arch-sunxi/clock_sun50i_h6.h  |  4 ++++
 arch/arm/include/asm/arch-sunxi/clock_sun6i.h |  7 +++++++
 .../include/asm/arch-sunxi/clock_sun8i_a83t.h |  7 +++++++
 arch/arm/include/asm/arch-sunxi/clock_sun9i.h |  7 +++++++
 drivers/mmc/sunxi_mmc.c                       | 20 +++++++++----------
 6 files changed, 41 insertions(+), 10 deletions(-)

diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
index 2cec91cb20e..00bdd5f938d 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
@@ -10,6 +10,12 @@
 #ifndef _SUNXI_CLOCK_SUN4I_H
 #define _SUNXI_CLOCK_SUN4I_H
 
+#define CCU_AHB_GATE0		0x60
+#define CCU_MMC0_CLK_CFG	0x88
+#define CCU_MMC1_CLK_CFG	0x8c
+#define CCU_MMC2_CLK_CFG	0x90
+#define CCU_MMC3_CLK_CFG	0x94
+
 struct sunxi_ccm_reg {
 	u32 pll1_cfg;		/* 0x00 pll1 control */
 	u32 pll1_tun;		/* 0x04 pll1 tuning */
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 a485e00f1f3..655f562c2af 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
@@ -22,6 +22,10 @@
 #define CCU_H6_APB1_CFG			0x520
 #define CCU_H6_APB2_CFG			0x524
 #define CCU_H6_MBUS_CFG			0x540
+#define CCU_MMC0_CLK_CFG		0x830
+#define CCU_MMC1_CLK_CFG		0x834
+#define CCU_MMC2_CLK_CFG		0x838
+#define CCU_H6_MMC_GATE_RESET		0x84c
 #define CCU_H6_UART_GATE_RESET		0x90c
 #define CCU_H6_I2C_GATE_RESET		0x91c
 
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
index 7fcf340db69..28c3faccbbc 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
@@ -10,6 +10,13 @@
 #ifndef _SUNXI_CLOCK_SUN6I_H
 #define _SUNXI_CLOCK_SUN6I_H
 
+#define CCU_AHB_GATE0		0x060
+#define CCU_MMC0_CLK_CFG	0x088
+#define CCU_MMC1_CLK_CFG	0x08c
+#define CCU_MMC2_CLK_CFG	0x090
+#define CCU_MMC3_CLK_CFG	0x094
+#define CCU_AHB_RESET0_CFG	0x2c0
+
 struct sunxi_ccm_reg {
 	u32 pll1_cfg;		/* 0x00 pll1 control */
 	u32 reserved0;
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h b/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h
index 35ca0491ac9..5ad2163926a 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h
@@ -13,6 +13,13 @@
 #ifndef _SUNXI_CLOCK_SUN8I_A83T_H
 #define _SUNXI_CLOCK_SUN8I_A83T_H
 
+#define CCU_AHB_GATE0		0x060
+#define CCU_MMC0_CLK_CFG	0x088
+#define CCU_MMC1_CLK_CFG	0x08c
+#define CCU_MMC2_CLK_CFG	0x090
+#define CCU_MMC3_CLK_CFG	0x094
+#define CCU_AHB_RESET0_CFG	0x2c0
+
 struct sunxi_ccm_reg {
 	u32 pll1_c0_cfg;	/* 0x00 c1cpu# pll control */
 	u32 pll1_c1_cfg;	/* 0x04 c1cpu# pll control */
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun9i.h b/arch/arm/include/asm/arch-sunxi/clock_sun9i.h
index 006f7761fc6..8d696e533f8 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun9i.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun9i.h
@@ -12,6 +12,13 @@
 #include <linux/bitops.h>
 #endif
 
+#define CCU_MMC0_CLK_CFG	0x410
+#define CCU_MMC1_CLK_CFG	0x414
+#define CCU_MMC2_CLK_CFG	0x418
+#define CCU_MMC3_CLK_CFG	0x41c
+#define CCU_AHB_GATE0		0x580
+#define CCU_AHB_RESET0_CFG	0x5a0
+
 struct sunxi_ccm_reg {
 	u32 pll1_c0_cfg;	/* 0x00 c0cpu# pll configuration */
 	u32 pll2_c1_cfg;	/* 0x04 c1cpu# pll configuration */
diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
index 0b56d1405be..432be66c632 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -451,29 +451,29 @@ struct sunxi_mmc_priv mmc_host[4];
 static int mmc_resource_init(int sdc_no)
 {
 	struct sunxi_mmc_priv *priv = &mmc_host[sdc_no];
-	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	void *ccm = (void *)SUNXI_CCM_BASE;
 
 	debug("init mmc %d resource\n", sdc_no);
 
 	switch (sdc_no) {
 	case 0:
 		priv->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE;
-		priv->mclkreg = &ccm->sd0_clk_cfg;
+		priv->mclkreg = ccm + CCU_MMC0_CLK_CFG;
 		break;
 	case 1:
 		priv->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE;
-		priv->mclkreg = &ccm->sd1_clk_cfg;
+		priv->mclkreg = ccm + CCU_MMC1_CLK_CFG;
 		break;
 #ifdef SUNXI_MMC2_BASE
 	case 2:
 		priv->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE;
-		priv->mclkreg = &ccm->sd2_clk_cfg;
+		priv->mclkreg = ccm + CCU_MMC2_CLK_CFG;
 		break;
 #endif
 #ifdef SUNXI_MMC3_BASE
 	case 3:
 		priv->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE;
-		priv->mclkreg = &ccm->sd3_clk_cfg;
+		priv->mclkreg = ccm + CCU_MMC3_CLK_CFG;
 		break;
 #endif
 	default:
@@ -520,7 +520,7 @@ static const struct mmc_ops sunxi_mmc_ops = {
 
 struct mmc *sunxi_mmc_init(int sdc_no)
 {
-	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	void *ccm = (void *)SUNXI_CCM_BASE;
 	struct sunxi_mmc_priv *priv = &mmc_host[sdc_no];
 	struct mmc_config *cfg = &priv->cfg;
 	int ret;
@@ -549,11 +549,11 @@ struct mmc *sunxi_mmc_init(int sdc_no)
 	/* config ahb clock */
 	debug("init mmc %d clock and io\n", sdc_no);
 #if !defined(CONFIG_SUN50I_GEN_H6) && !defined(CONFIG_SUNXI_GEN_NCAT2)
-	setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
+	setbits_le32(ccm + CCU_AHB_GATE0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
 
 #ifdef CONFIG_SUNXI_GEN_SUN6I
 	/* unassert reset */
-	setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
+	setbits_le32(ccm + CCU_AHB_RESET0_CFG, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
 #endif
 #if defined(CONFIG_MACH_SUN9I)
 	/* sun9i has a mmc-common module, also set the gate and reset there */
@@ -561,9 +561,9 @@ struct mmc *sunxi_mmc_init(int sdc_no)
 	       SUNXI_MMC_COMMON_BASE + 4 * sdc_no);
 #endif
 #else /* CONFIG_SUN50I_GEN_H6 */
-	setbits_le32(&ccm->sd_gate_reset, 1 << sdc_no);
+	setbits_le32(ccm + CCU_H6_MMC_GATE_RESET, 1 << sdc_no);
 	/* unassert reset */
-	setbits_le32(&ccm->sd_gate_reset, 1 << (RESET_SHIFT + sdc_no));
+	setbits_le32(ccm + CCU_H6_MMC_GATE_RESET, 1 << (RESET_SHIFT + sdc_no));
 #endif
 	ret = mmc_set_mod_clk(priv, 24000000);
 	if (ret)
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 03/34] sunxi: H616: dram: remove usage of struct sunxi_ccm_reg
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
  2025-03-23 11:35 ` [PATCH 01/34] sunxi: clock: H6: drop usage of struct sunxi_ccm_reg Andre Przywara
  2025-03-23 11:35 ` [PATCH 02/34] sunxi: mmc: remove " Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 04/34] sunxi: H6: " Andre Przywara
                   ` (31 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

The Allwinner H616 DRAM initialisation code uses a complex C struct,
modelling the clock device's register frame. For this SoC, the struct
contains 127 registers, but the DRAM code only uses four of them.

Since we want to get rid of this struct, drop the usage of the struct in
the H616 DRAM code, by using #define'd register names and their offset,
and then adding those names to the base pointer.

This removes one more user of the clock register struct.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 .../include/asm/arch-sunxi/clock_sun50i_h6.h  |  2 ++
 arch/arm/mach-sunxi/dram_sun50i_h616.c        | 32 +++++++++----------
 2 files changed, 18 insertions(+), 16 deletions(-)

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 655f562c2af..0a5f9342308 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
@@ -22,6 +22,8 @@
 #define CCU_H6_APB1_CFG			0x520
 #define CCU_H6_APB2_CFG			0x524
 #define CCU_H6_MBUS_CFG			0x540
+#define CCU_H6_DRAM_CLK_CFG		0x800
+#define CCU_H6_DRAM_GATE_RESET		0x80c
 #define CCU_MMC0_CLK_CFG		0x830
 #define CCU_MMC1_CLK_CFG		0x834
 #define CCU_MMC2_CLK_CFG		0x838
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c
index b3554cc64bf..b310c6a7632 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h616.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c
@@ -93,34 +93,34 @@ static void mctl_set_master_priority(void)
 
 static void mctl_sys_init(u32 clk_rate)
 {
-	struct sunxi_ccm_reg * const ccm =
-			(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	void * const ccm = (void *)SUNXI_CCM_BASE;
 	struct sunxi_mctl_com_reg * const mctl_com =
 			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
 	struct sunxi_mctl_ctl_reg * const mctl_ctl =
 			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
 
 	/* Put all DRAM-related blocks to reset state */
-	clrbits_le32(&ccm->mbus_cfg, MBUS_ENABLE);
-	clrbits_le32(&ccm->mbus_cfg, MBUS_RESET);
-	clrbits_le32(&ccm->dram_gate_reset, BIT(GATE_SHIFT));
+	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));
 	udelay(5);
-	clrbits_le32(&ccm->dram_gate_reset, BIT(RESET_SHIFT));
-	clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN);
-	clrbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET);
+	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 PLL5 rate to doubled DRAM clock rate */
 	writel(CCM_PLL5_CTRL_EN | CCM_PLL5_LOCK_EN | CCM_PLL5_OUT_EN |
-	       CCM_PLL5_CTRL_N(clk_rate * 2 / 24), &ccm->pll5_cfg);
-	mctl_await_completion(&ccm->pll5_cfg, CCM_PLL5_LOCK, CCM_PLL5_LOCK);
+	       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);
 
 	/* Configure DRAM mod clock */
-	writel(DRAM_CLK_SRC_PLL5, &ccm->dram_clk_cfg);
-	writel(BIT(RESET_SHIFT), &ccm->dram_gate_reset);
+	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->dram_gate_reset, BIT(GATE_SHIFT));
+	setbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(GATE_SHIFT));
 
 	/* Disable all channels */
 	writel(0, &mctl_com->maer0);
@@ -128,12 +128,12 @@ static void mctl_sys_init(u32 clk_rate)
 	writel(0, &mctl_com->maer2);
 
 	/* Configure MBUS and enable DRAM mod reset */
-	setbits_le32(&ccm->mbus_cfg, MBUS_RESET);
-	setbits_le32(&ccm->mbus_cfg, MBUS_ENABLE);
+	setbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_RESET);
+	setbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_ENABLE);
 
 	clrbits_le32(&mctl_com->unk_0x500, BIT(25));
 
-	setbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET);
+	setbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, DRAM_MOD_RESET);
 	udelay(5);
 
 	/* Unknown hack, which enables access of mctl_ctl regs */
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 04/34] sunxi: H6: dram: remove usage of struct sunxi_ccm_reg
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (2 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 03/34] sunxi: H616: dram: " Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 05/34] sunxi: clock: H6: remove " Andre Przywara
                   ` (30 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

The Allwinner H6 DRAM initialisation code uses a complex C struct,
modelling the clock device's register frame. For this SoC, the struct
contains 127 registers, but the DRAM code only uses four of them.

Since we want to get rid of this struct, drop the usage of the struct in
the H6 DRAM code, by using #define'd register names and their offset, and
then adding those names to the base pointer.

This removes one more user of the clock register struct.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/mach-sunxi/dram_sun50i_h6.c | 32 ++++++++++++++--------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c
index e7862bd06ea..35f7a247718 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c
@@ -152,34 +152,34 @@ static void mctl_set_master_priority(void)
 
 static void mctl_sys_init(struct dram_para *para)
 {
-	struct sunxi_ccm_reg * const ccm =
-			(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	void * const ccm = (void *)SUNXI_CCM_BASE;
 	struct sunxi_mctl_com_reg * const mctl_com =
 			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
 	struct sunxi_mctl_ctl_reg * const mctl_ctl =
 			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
 
 	/* Put all DRAM-related blocks to reset state */
-	clrbits_le32(&ccm->mbus_cfg, MBUS_ENABLE | MBUS_RESET);
-	clrbits_le32(&ccm->dram_gate_reset, BIT(0));
+	clrbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_ENABLE | MBUS_RESET);
+	clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(0));
 	udelay(5);
-	writel(0, &ccm->dram_gate_reset);
-	clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN);
-	clrbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET);
+	writel(0, ccm + CCU_H6_DRAM_GATE_RESET);
+	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 PLL5 rate to doubled DRAM clock rate */
 	writel(CCM_PLL5_CTRL_EN | CCM_PLL5_LOCK_EN |
-	       CCM_PLL5_CTRL_N(para->clk * 2 / 24), &ccm->pll5_cfg);
-	mctl_await_completion(&ccm->pll5_cfg, CCM_PLL5_LOCK, CCM_PLL5_LOCK);
+	       CCM_PLL5_CTRL_N(para->clk * 2 / 24), ccm + CCU_H6_PLL5_CFG);
+	mctl_await_completion(ccm + CCU_H6_PLL5_CFG,
+			      CCM_PLL5_LOCK, CCM_PLL5_LOCK);
 
 	/* Configure DRAM mod clock */
-	writel(DRAM_CLK_SRC_PLL5, &ccm->dram_clk_cfg);
-	setbits_le32(&ccm->dram_clk_cfg, DRAM_CLK_UPDATE);
-	writel(BIT(RESET_SHIFT), &ccm->dram_gate_reset);
+	writel(DRAM_CLK_SRC_PLL5, ccm + CCU_H6_DRAM_CLK_CFG);
+	setbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, DRAM_CLK_UPDATE);
+	writel(BIT(RESET_SHIFT), ccm + CCU_H6_DRAM_GATE_RESET);
 	udelay(5);
-	setbits_le32(&ccm->dram_gate_reset, BIT(0));
+	setbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(0));
 
 	/* Disable all channels */
 	writel(0, &mctl_com->maer0);
@@ -187,9 +187,9 @@ static void mctl_sys_init(struct dram_para *para)
 	writel(0, &mctl_com->maer2);
 
 	/* Configure MBUS and enable DRAM mod reset */
-	setbits_le32(&ccm->mbus_cfg, MBUS_RESET);
-	setbits_le32(&ccm->mbus_cfg, MBUS_ENABLE);
-	setbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET);
+	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);
 
 	/* Unknown hack from the BSP, which enables access of mctl_ctl regs */
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 05/34] sunxi: clock: H6: remove struct sunxi_ccm_reg
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (3 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 04/34] sunxi: H6: " Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 06/34] sunxi: clock: H6: drop usage of struct sunxi_prcm_reg Andre Przywara
                   ` (29 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

With the SPL clock code, the MMC driver, and the DRAM init routine we
converted all users of the H6 class "struct sunxi_ccm_reg" over to use
 #define'd register offsets now.

Drop the whole definition of this struct now, since it's not needed
anymore, for all H6 and H616 boards.
This removes the entire fragile and questionable definition, and allows
new SoCs to share the code more easily.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 .../include/asm/arch-sunxi/clock_sun50i_h6.h  | 213 ------------------
 1 file changed, 213 deletions(-)

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 0a5f9342308..ccacc99d018 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
@@ -31,219 +31,6 @@
 #define CCU_H6_UART_GATE_RESET		0x90c
 #define CCU_H6_I2C_GATE_RESET		0x91c
 
-struct sunxi_ccm_reg {
-	u32 pll1_cfg;		/* 0x000 pll1 (cpux) control */
-	u8 reserved_0x004[12];
-	u32 pll5_cfg;		/* 0x010 pll5 (ddr) control */
-	u8 reserved_0x014[12];
-	u32 pll6_cfg;		/* 0x020 pll6 (periph0) control */
-	u8 reserved_0x020[4];
-	u32 pll_periph1_cfg;	/* 0x028 pll periph1 control */
-	u8 reserved_0x028[4];
-	u32 pll7_cfg;		/* 0x030 pll7 (gpu) control */
-	u8 reserved_0x034[12];
-	u32 pll3_cfg;		/* 0x040 pll3 (video0) control */
-	u8 reserved_0x044[4];
-	u32 pll_video1_cfg;	/* 0x048 pll video1 control */
-	u8 reserved_0x04c[12];
-	u32 pll4_cfg;		/* 0x058 pll4 (ve) control */
-	u8 reserved_0x05c[4];
-	u32 pll10_cfg;		/* 0x060 pll10 (de) control */
-	u8 reserved_0x064[12];
-	u32 pll9_cfg;		/* 0x070 pll9 (hsic) control */
-	u8 reserved_0x074[4];
-	u32 pll2_cfg;		/* 0x078 pll2 (audio) control */
-	u8 reserved_0x07c[148];
-	u32 pll5_pat;		/* 0x110 pll5 (ddr) pattern */
-	u8 reserved_0x114[20];
-	u32 pll_periph1_pat0;	/* 0x128 pll periph1 pattern0 */
-	u32 pll_periph1_pat1;	/* 0x12c pll periph1 pattern1 */
-	u32 pll7_pat0;		/* 0x130 pll7 (gpu) pattern0 */
-	u32 pll7_pat1;		/* 0x134 pll7 (gpu) pattern1 */
-	u8 reserved_0x138[8];
-	u32 pll3_pat0;		/* 0x140 pll3 (video0) pattern0 */
-	u32 pll3_pat1;		/* 0x144 pll3 (video0) pattern1 */
-	u32 pll_video1_pat0;	/* 0x148 pll video1 pattern0 */
-	u32 pll_video1_pat1;	/* 0x14c pll video1 pattern1 */
-	u8 reserved_0x150[8];
-	u32 pll4_pat0;		/* 0x158 pll4 (ve) pattern0 */
-	u32 pll4_pat1;		/* 0x15c pll4 (ve) pattern1 */
-	u32 pll10_pat0;		/* 0x160 pll10 (de) pattern0 */
-	u32 pll10_pat1;		/* 0x164 pll10 (de) pattern1 */
-	u8 reserved_0x168[8];
-	u32 pll9_pat0;		/* 0x170 pll9 (hsic) pattern0 */
-	u32 pll9_pat1;		/* 0x174 pll9 (hsic) pattern1 */
-	u32 pll2_pat0;		/* 0x178 pll2 (audio) pattern0 */
-	u32 pll2_pat1;		/* 0x17c pll2 (audio) pattern1 */
-	u8 reserved_0x180[384];
-	u32 pll1_bias;		/* 0x300 pll1 (cpux) bias */
-	u8 reserved_0x304[12];
-	u32 pll5_bias;		/* 0x310 pll5 (ddr) bias */
-	u8 reserved_0x314[12];
-	u32 pll6_bias;		/* 0x320 pll6 (periph0) bias */
-	u8 reserved_0x324[4];
-	u32 pll_periph1_bias;	/* 0x328 pll periph1 bias */
-	u8 reserved_0x32c[4];
-	u32 pll7_bias;		/* 0x330 pll7 (gpu) bias */
-	u8 reserved_0x334[12];
-	u32 pll3_bias;		/* 0x340 pll3 (video0) bias */
-	u8 reserved_0x344[4];
-	u32 pll_video1_bias;	/* 0x348 pll video1 bias */
-	u8 reserved_0x34c[12];
-	u32 pll4_bias;		/* 0x358 pll4 (ve) bias */
-	u8 reserved_0x35c[4];
-	u32 pll10_bias;		/* 0x360 pll10 (de) bias */
-	u8 reserved_0x364[12];
-	u32 pll9_bias;		/* 0x370 pll9 (hsic) bias */
-	u8 reserved_0x374[4];
-	u32 pll2_bias;		/* 0x378 pll2 (audio) bias */
-	u8 reserved_0x37c[132];
-	u32 pll1_tun;		/* 0x400 pll1 (cpux) tunning */
-	u8 reserved_0x404[252];
-	u32 cpu_axi_cfg;	/* 0x500 CPUX/AXI clock control*/
-	u8 reserved_0x504[12];
-	u32 psi_ahb1_ahb2_cfg;	/* 0x510 PSI/AHB1/AHB2 clock control */
-	u8 reserved_0x514[8];
-	u32 ahb3_cfg;		/* 0x51c AHB3 clock control */
-	u32 apb1_cfg;		/* 0x520 APB1 clock control */
-	u32 apb2_cfg;		/* 0x524 APB2 clock control */
-	u8 reserved_0x528[24];
-	u32 mbus_cfg;		/* 0x540 MBUS clock control */
-	u8 reserved_0x544[188];
-	u32 de_clk_cfg;		/* 0x600 DE clock control */
-	u8 reserved_0x604[8];
-	u32 de_gate_reset;	/* 0x60c DE gate/reset control */
-	u8 reserved_0x610[16];
-	u32 di_clk_cfg;		/* 0x620 DI clock control */
-	u8 reserved_0x024[8];
-	u32 di_gate_reset;	/* 0x62c DI gate/reset control */
-	u8 reserved_0x630[64];
-	u32 gpu_clk_cfg;	/* 0x670 GPU clock control */
-	u8 reserved_0x674[8];
-	u32 gpu_gate_reset;	/* 0x67c GPU gate/reset control */
-	u32 ce_clk_cfg;		/* 0x680 CE clock control */
-	u8 reserved_0x684[8];
-	u32 ce_gate_reset;	/* 0x68c CE gate/reset control */
-	u32 ve_clk_cfg;		/* 0x690 VE clock control */
-	u8 reserved_0x694[8];
-	u32 ve_gate_reset;	/* 0x69c VE gate/reset control */
-	u8 reserved_0x6a0[16];
-	u32 emce_clk_cfg;	/* 0x6b0 EMCE clock control */
-	u8 reserved_0x6b4[8];
-	u32 emce_gate_reset;	/* 0x6bc EMCE gate/reset control */
-	u32 vp9_clk_cfg;	/* 0x6c0 VP9 clock control */
-	u8 reserved_0x6c4[8];
-	u32 vp9_gate_reset;	/* 0x6cc VP9 gate/reset control */
-	u8 reserved_0x6d0[60];
-	u32 dma_gate_reset;	/* 0x70c DMA gate/reset control */
-	u8 reserved_0x710[12];
-	u32 msgbox_gate_reset;	/* 0x71c Message Box gate/reset control */
-	u8 reserved_0x720[12];
-	u32 spinlock_gate_reset;/* 0x72c Spinlock gate/reset control */
-	u8 reserved_0x730[12];
-	u32 hstimer_gate_reset;	/* 0x73c HS Timer gate/reset control */
-	u32 avs_gate_reset;	/* 0x740 AVS gate/reset control */
-	u8 reserved_0x744[72];
-	u32 dbgsys_gate_reset;	/* 0x78c Debugging system gate/reset control */
-	u8 reserved_0x790[12];
-	u32 psi_gate_reset;	/* 0x79c PSI gate/reset control */
-	u8 reserved_0x7a0[12];
-	u32 pwm_gate_reset;	/* 0x7ac PWM gate/reset control */
-	u8 reserved_0x7b0[12];
-	u32 iommu_gate_reset;	/* 0x7bc IOMMU gate/reset control */
-	u8 reserved_0x7c0[64];
-	u32 dram_clk_cfg;		/* 0x800 DRAM clock control */
-	u32 mbus_gate;		/* 0x804 MBUS gate control */
-	u8 reserved_0x808[4];
-	u32 dram_gate_reset;	/* 0x80c DRAM gate/reset control */
-	u32 nand0_clk_cfg;	/* 0x810 NAND0 clock control */
-	u32 nand1_clk_cfg;	/* 0x814 NAND1 clock control */
-	u8 reserved_0x818[20];
-	u32 nand_gate_reset;	/* 0x82c NAND gate/reset control */
-	u32 sd0_clk_cfg;	/* 0x830 MMC0 clock control */
-	u32 sd1_clk_cfg;	/* 0x834 MMC1 clock control */
-	u32 sd2_clk_cfg;	/* 0x838 MMC2 clock control */
-	u8 reserved_0x83c[16];
-	u32 sd_gate_reset;	/* 0x84c MMC gate/reset control */
-	u8 reserved_0x850[188];
-	u32 uart_gate_reset;	/* 0x90c UART gate/reset control */
-	u8 reserved_0x910[12];
-	u32 twi_gate_reset;	/* 0x91c I2C gate/reset control */
-	u8 reserved_0x920[28];
-	u32 scr_gate_reset;	/* 0x93c SCR gate/reset control */
-	u32 spi0_clk_cfg;	/* 0x940 SPI0 clock control */
-	u32 spi1_clk_cfg;	/* 0x944 SPI1 clock control */
-	u8 reserved_0x948[36];
-	u32 spi_gate_reset;	/* 0x96c SPI gate/reset control */
-	u8 reserved_0x970[12];
-	u32 emac_gate_reset;	/* 0x97c EMAC gate/reset control */
-	u8 reserved_0x980[48];
-	u32 ts_clk_cfg;		/* 0x9b0 TS clock control */
-	u8 reserved_0x9b4[8];
-	u32 ts_gate_reset;	/* 0x9bc TS gate/reset control */
-	u32 irtx_clk_cfg;	/* 0x9c0 IR TX clock control */
-	u8 reserved_0x9c4[8];
-	u32 irtx_gate_reset;	/* 0x9cc IR TX gate/reset control */
-	u8 reserved_0x9d0[44];
-	u32 ths_gate_reset;	/* 0x9fc THS gate/reset control */
-	u8 reserved_0xa00[12];
-	u32 i2s3_clk_cfg;	/* 0xa0c I2S3 clock control */
-	u32 i2s0_clk_cfg;	/* 0xa10 I2S0 clock control */
-	u32 i2s1_clk_cfg;	/* 0xa14 I2S1 clock control */
-	u32 i2s2_clk_cfg;	/* 0xa18 I2S2 clock control */
-	u32 i2s_gate_reset;	/* 0xa1c I2S gate/reset control */
-	u32 spdif_clk_cfg;	/* 0xa20 SPDIF clock control */
-	u8 reserved_0xa24[8];
-	u32 spdif_gate_reset;	/* 0xa2c SPDIF gate/reset control */
-	u8 reserved_0xa30[16];
-	u32 dmic_clk_cfg;	/* 0xa40 DMIC clock control */
-	u8 reserved_0xa44[8];
-	u32 dmic_gate_reset;	/* 0xa4c DMIC gate/reset control */
-	u8 reserved_0xa50[16];
-	u32 ahub_clk_cfg;	/* 0xa60 Audio HUB clock control */
-	u8 reserved_0xa64[8];
-	u32 ahub_gate_reset;	/* 0xa6c Audio HUB gate/reset control */
-	u32 usb0_clk_cfg;	/* 0xa70 USB0(OTG) clock control */
-	u32 usb1_clk_cfg;	/* 0xa74 USB1(XHCI) clock control */
-	u8 reserved_0xa78[4];
-	u32 usb3_clk_cfg;	/* 0xa78 USB3 clock control */
-	u8 reserved_0xa80[12];
-	u32 usb_gate_reset;	/* 0xa8c USB gate/reset control */
-	u8 reserved_0xa90[32];
-	u32 pcie_ref_clk_cfg;	/* 0xab0 PCIE REF clock control */
-	u32 pcie_axi_clk_cfg;	/* 0xab4 PCIE AXI clock control */
-	u32 pcie_aux_clk_cfg;	/* 0xab8 PCIE AUX clock control */
-	u32 pcie_gate_reset;	/* 0xabc PCIE gate/reset control */
-	u8 reserved_0xac0[64];
-	u32 hdmi_clk_cfg;	/* 0xb00 HDMI clock control */
-	u32 hdmi_slow_clk_cfg;	/* 0xb04 HDMI slow clock control */
-	u8 reserved_0xb08[8];
-	u32 hdmi_cec_clk_cfg;	/* 0xb10 HDMI CEC clock control */
-	u8 reserved_0xb14[8];
-	u32 hdmi_gate_reset;	/* 0xb1c HDMI gate/reset control */
-	u8 reserved_0xb20[60];
-	u32 tcon_top_gate_reset;/* 0xb5c TCON TOP gate/reset control */
-	u32 tcon_lcd0_clk_cfg;	/* 0xb60 TCON LCD0 clock control */
-	u8 reserved_0xb64[24];
-	u32 tcon_lcd_gate_reset;/* 0xb7c TCON LCD gate/reset control */
-	u32 tcon_tv0_clk_cfg;	/* 0xb80 TCON TV0 clock control */
-	u8 reserved_0xb84[24];
-	u32 tcon_tv_gate_reset;	/* 0xb9c TCON TV gate/reset control */
-	u8 reserved_0xba0[96];
-	u32 csi_misc_clk_cfg;	/* 0xc00 CSI MISC clock control */
-	u32 csi_top_clk_cfg;	/* 0xc04 CSI TOP clock control */
-	u32 csi_mclk_cfg;	/* 0xc08 CSI Master clock control */
-	u8 reserved_0xc0c[32];
-	u32 csi_gate_reset;	/* 0xc2c CSI gate/reset control */
-	u8 reserved_0xc30[16];
-	u32 hdcp_clk_cfg;	/* 0xc40 HDCP clock control */
-	u8 reserved_0xc44[8];
-	u32 hdcp_gate_reset;	/* 0xc4c HDCP gate/reset control */
-	u8 reserved_0xc50[688];
-	u32 ccu_sec_switch;	/* 0xf00 CCU security switch */
-	u32 pll_lock_dbg_ctrl;	/* 0xf04 PLL lock debugging control */
-};
-
 /* pll1 bit field */
 #define CCM_PLL1_CTRL_EN		BIT(31)
 #define CCM_PLL1_LDO_EN			BIT(30)
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 06/34] sunxi: clock: H6: drop usage of struct sunxi_prcm_reg
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (4 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 05/34] sunxi: clock: H6: remove " Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 07/34] sunxi: H6/H616: dram: remove " Andre Przywara
                   ` (28 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

U-Boot drivers often revert to using C structures for modelling hardware
register frames. This creates some problems:
- A "struct" is a C language construct to group several variables
  together. The details of the layout of this struct are partly subject
  to the compiler's discretion (padding and alignment).
- The "packed" attribute would force a certain layout, but we are not
  using it.
- The actual source of information from the data sheet is the register
  offset. Here we create an artificial struct, carefully tuning the
  layout (with a lot of reserved members) to match that offset. To help
  with correctness, we put the desired information as a *comment*,
  though this is purely for the human reader, and has no effect on the
  generated layout. This sounds all very backwards.
- Using a struct suggests we can assign a pointer and then access the
  register content via the members. But this is not the case, instead
  every MMIO register access must go through specific accessor functions,
  to meet the ordering and access size guarantees the hardware requires.
- We share those structs in code shared across multiple SoC families,
  though most SoCs define their own version of the struct. Members must
  match in their name, across every SoC, otherwise compilation will fail.
  We work around this with even more #ifdefs in the shared code.
- Some SoCs have an *almost* identical layout, but differ in a few
  registers. This requires hard to maintain #ifdef's in the struct
  definition.
- Some of the register frames are huge: the H6 CCU device defines 127
  registers. We use 15 of them. Still the whole frame would need to be
  described, which is very tedious, but for no reason.
- Adding a new SoC often forces people to decide whether to share an
  existing struct, or to create a new copy. For some cases (say like 80%
  similarity) this works out badly either way.

The Linux kernel heavily frowns upon those register structs, and instead
uses a much simpler solution: #define REG_NAME  <offset>
This easily maps to the actual information from the data sheet, and can
much simpler be shared across multiple SoCs, as it allows to have all
SoC versions visible, so we can use C "if" statements instead of #ifdef's.
Also it requires to just define the registers we need, and we can use
alternative locations for some registers much more easily.

Drop the usage of "struct sunxi_prcm_reg" in the H6 SPL clock code, by
defining the respective register names and their offsets, then adding
them to the base pointer.
We cannot drop the struct definition quite yet, as it's also used in
other drivers, still.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/include/asm/arch-sunxi/prcm_sun50i.h |  5 +++++
 arch/arm/mach-sunxi/clock_sun50i_h6.c         | 20 +++++++++----------
 2 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h b/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
index fd63d3aad83..8ed78dccf10 100644
--- a/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
+++ b/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
@@ -11,6 +11,11 @@
 #ifndef __ASSEMBLY__
 #include <linux/compiler.h>
 
+#define CCU_PRCM_I2C_GATE_RESET		0x19c
+#define CCU_PRCM_PLL_LDO_CFG		0x244
+#define CCU_PRCM_SYS_PWROFF_GATING	0x250
+#define CCU_PRCM_RES_CAL_CTRL		0x310
+
 struct sunxi_prcm_reg {
 	u32 cpus_cfg;		/* 0x000 */
 	u8 res0[0x8];		/* 0x004 */
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index 482d9e0c695..615c13b5da0 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -7,26 +7,25 @@
 void clock_init_safe(void)
 {
 	void *const ccm = (void *)SUNXI_CCM_BASE;
-	struct sunxi_prcm_reg *const prcm =
-		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
+	void *const prcm = (void *)SUNXI_PRCM_BASE;
 
 	if (IS_ENABLED(CONFIG_MACH_SUN50I_H616)) {
 		/* this seems to enable PLLs on H616 */
-		setbits_le32(&prcm->sys_pwroff_gating, 0x10);
-		setbits_le32(&prcm->res_cal_ctrl, 2);
+		setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 0x10);
+		setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 2);
 	}
 
 	if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
 	    IS_ENABLED(CONFIG_MACH_SUN50I_H6)) {
-		clrbits_le32(&prcm->res_cal_ctrl, 1);
-		setbits_le32(&prcm->res_cal_ctrl, 1);
+		clrbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 1);
+		setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 1);
 	}
 
 	if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) {
 		/* set key field for ldo enable */
-		setbits_le32(&prcm->pll_ldo_cfg, 0xA7000000);
+		setbits_le32(prcm + CCU_PRCM_PLL_LDO_CFG, 0xA7000000);
 		/* set PLL VDD LDO output to 1.14 V */
-		setbits_le32(&prcm->pll_ldo_cfg, 0x60000);
+		setbits_le32(prcm + CCU_PRCM_PLL_LDO_CFG, 0x60000);
 	}
 
 	clock_set_pll1(408000000);
@@ -105,8 +104,7 @@ void clock_set_pll1(unsigned int clk)
 int clock_twi_onoff(int port, int state)
 {
 	void *const ccm = (void *)SUNXI_CCM_BASE;
-	struct sunxi_prcm_reg *const prcm =
-		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
+	void *const prcm = (void *)SUNXI_PRCM_BASE;
 	u32 value, *ptr;
 	int shift;
 
@@ -114,7 +112,7 @@ int clock_twi_onoff(int port, int state)
 
 	if (port == 5) {
 		shift = 0;
-		ptr = &prcm->twi_gate_reset;
+		ptr = prcm + CCU_PRCM_I2C_GATE_RESET;
 	} else {
 		shift = port;
 		ptr = ccm + CCU_H6_I2C_GATE_RESET;
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 07/34] sunxi: H6/H616: dram: remove usage of struct sunxi_prcm_reg
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (5 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 06/34] sunxi: clock: H6: drop usage of struct sunxi_prcm_reg Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 08/34] sunxi: clock: H6: remove " Andre Przywara
                   ` (27 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

The Allwinner H6 and H616 DRAM initialisation code uses a complex C
struct, modelling the PRCM clock register frame. For those SoCs, this
struct contains 20 registers, but the DRAM code only uses two of them.

Since we want to get rid of this struct, drop the usage of the struct in
the H6 and H616 DRAM code, by using #define'd register names and their
offset, and then adding those names to the base pointer.

This removes one more user of the PRCM clock register struct.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/include/asm/arch-sunxi/prcm_sun50i.h | 1 +
 arch/arm/mach-sunxi/dram_sun50i_h6.c          | 7 +++----
 arch/arm/mach-sunxi/dram_sun50i_h616.c        | 7 +++----
 3 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h b/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
index 8ed78dccf10..738cd68320c 100644
--- a/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
+++ b/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
@@ -15,6 +15,7 @@
 #define CCU_PRCM_PLL_LDO_CFG		0x244
 #define CCU_PRCM_SYS_PWROFF_GATING	0x250
 #define CCU_PRCM_RES_CAL_CTRL		0x310
+#define CCU_PRCM_OHMS240		0x318
 
 struct sunxi_prcm_reg {
 	u32 cpus_cfg;		/* 0x000 */
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c
index 35f7a247718..5bf932001cb 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c
@@ -665,8 +665,7 @@ unsigned long sunxi_dram_init(void)
 {
 	struct sunxi_mctl_com_reg * const mctl_com =
 			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
-	struct sunxi_prcm_reg *const prcm =
-		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
+	void *const prcm = (void *)SUNXI_PRCM_BASE;
 	struct dram_para para = {
 		.clk = CONFIG_DRAM_CLK,
 #ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3
@@ -682,8 +681,8 @@ unsigned long sunxi_dram_init(void)
 
 	unsigned long size;
 
-	setbits_le32(&prcm->res_cal_ctrl, BIT(8));
-	clrbits_le32(&prcm->ohms240, 0x3f);
+	setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, BIT(8));
+	clrbits_le32(prcm + CCU_PRCM_OHMS240, 0x3f);
 
 	mctl_auto_detect_rank_width(&para);
 	mctl_auto_detect_dram_size(&para);
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c
index b310c6a7632..ab00bd263f4 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h616.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c
@@ -1423,13 +1423,12 @@ static const struct dram_para para = {
 
 unsigned long sunxi_dram_init(void)
 {
-	struct sunxi_prcm_reg *const prcm =
-		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
+	void *const prcm = (void *)SUNXI_PRCM_BASE;
 	struct dram_config config;
 	unsigned long size;
 
-	setbits_le32(&prcm->res_cal_ctrl, BIT(8));
-	clrbits_le32(&prcm->ohms240, 0x3f);
+	setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, BIT(8));
+	clrbits_le32(prcm + CCU_PRCM_OHMS240, 0x3f);
 
 	mctl_auto_detect_rank_width(&para, &config);
 	mctl_auto_detect_dram_size(&para, &config);
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 08/34] sunxi: clock: H6: remove struct sunxi_prcm_reg
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (6 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 07/34] sunxi: H6/H616: dram: remove " Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 09/34] sunxi: clock: H6: unify PLL control bit definitions Andre Przywara
                   ` (26 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

With the SPL clock code and the DRAM init routine we converted all users
of the H6 class "struct sunxi_prcm_reg" over to use #define'd register
offsets now.

Drop the whole definition of this struct now, since it's not needed
anymore, for all H6 and H616 boards.
This removes the entire fragile and questionable definition, and allows
new SoCs to share the code more easily.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/include/asm/arch-sunxi/prcm_sun50i.h | 40 -------------------
 1 file changed, 40 deletions(-)

diff --git a/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h b/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
index 738cd68320c..d6653c3bb06 100644
--- a/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
+++ b/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
@@ -9,7 +9,6 @@
 #define _SUN50I_PRCM_H
 
 #ifndef __ASSEMBLY__
-#include <linux/compiler.h>
 
 #define CCU_PRCM_I2C_GATE_RESET		0x19c
 #define CCU_PRCM_PLL_LDO_CFG		0x244
@@ -17,45 +16,6 @@
 #define CCU_PRCM_RES_CAL_CTRL		0x310
 #define CCU_PRCM_OHMS240		0x318
 
-struct sunxi_prcm_reg {
-	u32 cpus_cfg;		/* 0x000 */
-	u8 res0[0x8];		/* 0x004 */
-	u32 apbs1_cfg;		/* 0x00c */
-	u32 apbs2_cfg;		/* 0x010 */
-	u8 res1[0x108];		/* 0x014 */
-	u32 tmr_gate_reset;	/* 0x11c */
-	u8 res2[0xc];		/* 0x120 */
-	u32 twd_gate_reset;	/* 0x12c */
-	u8 res3[0xc];		/* 0x130 */
-	u32 pwm_gate_reset;	/* 0x13c */
-	u8 res4[0x4c];		/* 0x140 */
-	u32 uart_gate_reset;	/* 0x18c */
-	u8 res5[0xc];		/* 0x190 */
-	u32 twi_gate_reset;	/* 0x19c */
-	u8 res6[0x1c];		/* 0x1a0 */
-	u32 rsb_gate_reset;	/* 0x1bc */
-	u32 cir_cfg;		/* 0x1c0 */
-	u8 res7[0x8];		/* 0x1c4 */
-	u32 cir_gate_reset;	/* 0x1cc */
-	u8 res8[0x10];		/* 0x1d0 */
-	u32 w1_cfg;		/* 0x1e0 */
-	u8 res9[0x8];		/* 0x1e4 */
-	u32 w1_gate_reset;	/* 0x1ec */
-	u8 res10[0x1c];		/* 0x1f0 */
-	u32 rtc_gate_reset;	/* 0x20c */
-	u8 res11[0x34];		/* 0x210 */
-	u32 pll_ldo_cfg;	/* 0x244 */
-	u8 res12[0x8];		/* 0x248 */
-	u32 sys_pwroff_gating;	/* 0x250 */
-	u8 res13[0xbc];		/* 0x254 */
-	u32 res_cal_ctrl;	/* 0x310 */
-	u32 ohms200;		/* 0x314 */
-	u32 ohms240;		/* 0x318 */
-	u32 res_cal_status;	/* 0x31c */
-};
-check_member(sunxi_prcm_reg, rtc_gate_reset, 0x20c);
-check_member(sunxi_prcm_reg, res_cal_status, 0x31c);
-
 #define PRCM_TWI_GATE		(1 << 0)
 #define PRCM_TWI_RESET		(1 << 16)
 
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 09/34] sunxi: clock: H6: unify PLL control bit definitions
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (7 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 08/34] sunxi: clock: H6: remove " Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 10/34] sunxi: clock: H6: factor out clock_set_pll() Andre Przywara
                   ` (25 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

The Allwinner PLLs share most of their control bits, they differ mostly
in the factors and dividers.

Drop the PLL specific definition of those common bits, and use one
shared macro, for all PLLs.

This requires changing the users in the SPL clock and DRAM code.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 .../include/asm/arch-sunxi/clock_sun50i_h6.h  | 20 +++++++------------
 arch/arm/mach-sunxi/clock_sun50i_h6.c         | 10 +++++-----
 arch/arm/mach-sunxi/dram_sun50i_h6.c          |  6 +++---
 arch/arm/mach-sunxi/dram_sun50i_h616.c        |  6 +++---
 4 files changed, 18 insertions(+), 24 deletions(-)

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..bc64c8e2f97 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
@@ -31,29 +31,23 @@
 #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)
+/* 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_CLOCK_TIME_2		(2 << 24)
 #define CCM_PLL1_CTRL_P(p)		((p) << 16)
 #define CCM_PLL1_CTRL_N(n)		(((n) - 1) << 8)
 
 /* 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
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index 615c13b5da0..67d65fccffe 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -31,7 +31,7 @@ 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,
@@ -85,14 +85,14 @@ void clock_set_pll1(unsigned int clk)
 	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_PLL_CTRL_EN | CCM_PLL_LOCK_EN | CCM_PLL1_CLOCK_TIME_2;
 	val |= CCM_PLL1_CTRL_N(clk / 24000000);
 	if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
-	       val |= CCM_PLL1_OUT_EN;
+	       val |= CCM_PLL_OUT_EN;
 	if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
-	       val |= CCM_PLL1_OUT_EN | CCM_PLL1_LDO_EN;
+	       val |= CCM_PLL_OUT_EN | CCM_PLL_LDO_EN;
 	writel(val, ccm + CCU_H6_PLL1_CFG);
-	while (!(readl(ccm + CCU_H6_PLL1_CFG) & CCM_PLL1_LOCK)) {}
+	while (!(readl(ccm + CCU_H6_PLL1_CFG) & CCM_PLL_LOCK)) {}
 
 	/* Switch CPU to PLL1 */
 	val = readl(ccm + CCU_H6_CPU_AXI_CFG);
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c
index 5bf932001cb..374acfe8a2e 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c
@@ -163,16 +163,16 @@ static void mctl_sys_init(struct dram_para *para)
 	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(para->clk * 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 ab00bd263f4..cbf4668b406 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h616.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c
@@ -105,16 +105,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);
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 10/34] sunxi: clock: H6: factor out clock_set_pll()
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (8 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 09/34] sunxi: clock: H6: unify PLL control bit definitions Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 11/34] sunxi: clock: H6: factor out H6/H616 CPU clock setup Andre Przywara
                   ` (24 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

The SPL initial clock setup code for the Allwinner H6 and H616 SoCs uses
a simple CPU PLL setup routine, which programs all register bits at once,
then waits for the LOCK bit to clear.
The manual suggests to follow a certain procedure for bringing up any
PLLs, which involves several register writes, one at a time, and some
delays. Also the H616 and the new A523 require some tiny changes in this
sequence, and the different SoCs also feature some extra bits here and
there, which we should not just clear.

So factor out the PLL setup routine, and make it follow the manual's
suggestion. This will read the PLL register at the beginning, then tweak
the bits we need to manipulate, and writes the register several times on
the way. This allows to cover the specific bits for different SoCs.

Besides improving the reliability of the PLL setup, this helps with the
A523, which requires *three* CPU PLLs to be programmed.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 .../include/asm/arch-sunxi/clock_sun50i_h6.h  |  2 +-
 arch/arm/mach-sunxi/clock_sun50i_h6.c         | 51 +++++++++++++++----
 2 files changed, 43 insertions(+), 10 deletions(-)

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 bc64c8e2f97..6761ce9d8f7 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
@@ -38,8 +38,8 @@
 #define CCM_PLL_LOCK			BIT(28)
 #define CCM_PLL_OUT_EN			BIT(27)
 #define CCM_PLL1_UPDATE			BIT(26)
-#define CCM_PLL1_CLOCK_TIME_2		(2 << 24)
 #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)
 
 /* pll5 bit field */
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index 67d65fccffe..9faf659f834 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -2,6 +2,7 @@
 #include <asm/arch/cpu.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/prcm.h>
+#include <linux/delay.h>
 
 #ifdef CONFIG_XPL_BUILD
 void clock_init_safe(void)
@@ -70,6 +71,46 @@ void clock_init_uart(void)
 		     1 << (RESET_SHIFT + CONFIG_CONS_INDEX - 1));
 }
 
+/* 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 (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) {
+		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 (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) {
+		val |= CCM_PLL_OUT_EN;
+		writel(val, reg);
+	}
+}
+
 void clock_set_pll1(unsigned int clk)
 {
 	void *const ccm = (void *)SUNXI_CCM_BASE;
@@ -84,15 +125,7 @@ void clock_set_pll1(unsigned int clk)
 	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_PLL_CTRL_EN | CCM_PLL_LOCK_EN | CCM_PLL1_CLOCK_TIME_2;
-	val |= CCM_PLL1_CTRL_N(clk / 24000000);
-	if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
-	       val |= CCM_PLL_OUT_EN;
-	if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
-	       val |= CCM_PLL_OUT_EN | CCM_PLL_LDO_EN;
-	writel(val, ccm + CCU_H6_PLL1_CFG);
-	while (!(readl(ccm + CCU_H6_PLL1_CFG) & CCM_PLL_LOCK)) {}
+	clock_set_pll(ccm + CCU_H6_PLL1_CFG, clk / 24000000);
 
 	/* Switch CPU to PLL1 */
 	val = readl(ccm + CCU_H6_CPU_AXI_CFG);
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 11/34] sunxi: clock: H6: factor out H6/H616 CPU clock setup
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (9 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 10/34] sunxi: clock: H6: factor out clock_set_pll() Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 12/34] sunxi: clock: H6: add A523 CPU PLL support Andre Przywara
                   ` (23 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

When we program the CPU PLL, we need to switch the CPU clock source away
from the PLL temporarily, then switch it back, once the PLL has
stabilised.

The CPU CLK register will be different on the A523, so move the current
code into a separate function, to allow using a different version of
that later for the A523.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/mach-sunxi/clock_sun50i_h6.c | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index 9faf659f834..b205b0a0550 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -111,29 +111,37 @@ static void clock_set_pll(u32 *reg, unsigned int n)
 	}
 }
 
-void clock_set_pll1(unsigned int clk)
+static void clock_h6_set_cpu_pll(unsigned int n_factor)
 {
 	void *const ccm = (void *)SUNXI_CCM_BASE;
 	u32 val;
 
-	/* Do not support clocks < 288MHz as they need factor P */
-	if (clk < 288000000) clk = 288000000;
-
-	/* Switch to 24MHz clock while changing PLL1 */
+	/* 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);
 
-	clock_set_pll(ccm + CCU_H6_PLL1_CFG, clk / 24000000);
+	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;
+
+	clock_h6_set_cpu_pll(clk);
+}
+
 int clock_twi_onoff(int port, int state)
 {
 	void *const ccm = (void *)SUNXI_CCM_BASE;
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 12/34] sunxi: clock: H6: add A523 CPU PLL support
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (10 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 11/34] sunxi: clock: H6: factor out H6/H616 CPU clock setup Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 13/34] sunxi: spl: add support for Allwinner A523 watchdog Andre Przywara
                   ` (22 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

The Allwinner A523 features 8 CPU cores, organised in two clusters, both
driven by separate PLLs. Also there is the DSU PLL, which clocks the
hardware that connects the cores to the rest of the system.
And while the PLL registers itself are very similar, they are located in
a separate register frame, outside the main CCU, and also the register
controlling the CPU clock source (mux) is different.

Provide a separate function that reparents the two clusters and the DSU,
while their PLLs are programmed. For the actual PLL programming, we rely
on the existing shared routine.

The selection between the new A523 routine and the existing code is made
with C if statements, but since the choice is effectively made at compile
time already, the compiler optimises away the other code paths, leaving
just the one required function in.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 .../include/asm/arch-sunxi/clock_sun50i_h6.h  | 17 ++++++++
 .../include/asm/arch-sunxi/cpu_sunxi_ncat2.h  |  2 +
 arch/arm/mach-sunxi/clock_sun50i_h6.c         | 40 ++++++++++++++++++-
 3 files changed, 58 insertions(+), 1 deletion(-)

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 6761ce9d8f7..c95f2b39e64 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,6 +32,14 @@
 #define CCU_H6_UART_GATE_RESET		0x90c
 #define CCU_H6_I2C_GATE_RESET		0x91c
 
+/* 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)
@@ -42,6 +51,14 @@
 #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_N(n)		(((n) - 1) << 8)
 #define CCM_PLL5_CTRL_DIV1(div1)	((div1) << 0)
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..c04ddb3f1d4 100644
--- a/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
+++ b/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
@@ -30,6 +30,8 @@
 
 #define SUNXI_CPUCFG_BASE		0x09010000
 
+#define SUNXI_CPU_PLL_CFG_BASE		0x08817000
+
 #ifndef __ASSEMBLY__
 void sunxi_board_init(void);
 void sunxi_reset(void);
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index b205b0a0550..f76d1b83883 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -4,6 +4,10 @@
 #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)
 {
@@ -111,6 +115,37 @@ static void clock_set_pll(u32 *reg, unsigned int n)
 	}
 }
 
+/* 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;
+
+	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);
+
+	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;
@@ -139,7 +174,10 @@ void clock_set_pll1(unsigned int clk)
 
 	clk /= 24000000;
 
-	clock_h6_set_cpu_pll(clk);
+	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)
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 13/34] sunxi: spl: add support for Allwinner A523 watchdog
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (11 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 12/34] sunxi: clock: H6: add A523 CPU PLL support Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 12:15   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 14/34] dt-bindings: add Allwinner A523 CCU bindings Andre Przywara
                   ` (21 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

From: Jernej Skrabec <jernej.skrabec@gmail.com>

The watchdog in the Allwinner A523 SoC differs a bit from the one in the
previous SoCs: it lives in a separate register frame, so no longer
inside some timer device, and it manages to shuffle around some
registers a bit.

Provide a new struct describing the register layout, and adjust the
address calculation in the SPL code accoringly. This is guarded by the
MACH_SUN55I_A523 Kconfig variable.
---
 arch/arm/include/asm/arch-sunxi/watchdog.h | 12 ++++++++++++
 arch/arm/mach-sunxi/board.c                |  6 ++++++
 2 files changed, 18 insertions(+)

diff --git a/arch/arm/include/asm/arch-sunxi/watchdog.h b/arch/arm/include/asm/arch-sunxi/watchdog.h
index 38e2ef2aca3..96d5725141e 100644
--- a/arch/arm/include/asm/arch-sunxi/watchdog.h
+++ b/arch/arm/include/asm/arch-sunxi/watchdog.h
@@ -26,6 +26,18 @@ struct sunxi_wdog {
 	u32 res[2];
 };
 
+#elif defined(CONFIG_MACH_SUN55I_A523)
+
+struct sunxi_wdog {
+	u32 irq_en;		/* 0x00 */
+	u32 irq_sta;		/* 0x04 */
+	u32 srst;		/* 0x08 */
+	u32 ctl;		/* 0x0c */
+	u32 cfg;		/* 0x10 */
+	u32 mode;		/* 0x14 */
+	u32 ocfg;		/* 0x18 */
+};
+
 #else
 
 #define WDT_CFG_RESET		(0x1)
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 701899ee4b2..89aea61e8e8 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -495,6 +495,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 struct sunxi_wdog *wdog =
+		(struct sunxi_wdog *)SUNXI_TIMER_BASE;
+
+	writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, &wdog->srst);
+	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 */
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 14/34] dt-bindings: add Allwinner A523 CCU bindings
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (12 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 13/34] sunxi: spl: add support for Allwinner A523 watchdog Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 15/34] clk: sunxi: Add support for the A523 CCU Andre Przywara
                   ` (20 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

This adds the latest version of the clock DT binding headers for the
Allwinner A523 family of SoCs.
Those headers have already been merged into the maintainer's trees, and
appeared in linux-next, but have not made it just yet into the official
kernel repository.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/dt-bindings/clock/sun55i-a523-ccu.h   | 189 ++++++++++++++++++
 include/dt-bindings/clock/sun55i-a523-r-ccu.h |  37 ++++
 include/dt-bindings/reset/sun55i-a523-ccu.h   |  88 ++++++++
 include/dt-bindings/reset/sun55i-a523-r-ccu.h |  25 +++
 4 files changed, 339 insertions(+)
 create mode 100644 include/dt-bindings/clock/sun55i-a523-ccu.h
 create mode 100644 include/dt-bindings/clock/sun55i-a523-r-ccu.h
 create mode 100644 include/dt-bindings/reset/sun55i-a523-ccu.h
 create mode 100644 include/dt-bindings/reset/sun55i-a523-r-ccu.h

diff --git a/include/dt-bindings/clock/sun55i-a523-ccu.h b/include/dt-bindings/clock/sun55i-a523-ccu.h
new file mode 100644
index 00000000000..c8259ac5ada
--- /dev/null
+++ b/include/dt-bindings/clock/sun55i-a523-ccu.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2024 Arm Ltd.
+ */
+
+#ifndef _DT_BINDINGS_CLK_SUN55I_A523_CCU_H_
+#define _DT_BINDINGS_CLK_SUN55I_A523_CCU_H_
+
+#define CLK_PLL_DDR0		0
+#define CLK_PLL_PERIPH0_4X	1
+#define CLK_PLL_PERIPH0_2X	2
+#define CLK_PLL_PERIPH0_800M	3
+#define CLK_PLL_PERIPH0_480M	4
+#define CLK_PLL_PERIPH0_600M	5
+#define CLK_PLL_PERIPH0_400M	6
+#define CLK_PLL_PERIPH0_300M	7
+#define CLK_PLL_PERIPH0_200M	8
+#define CLK_PLL_PERIPH0_160M	9
+#define CLK_PLL_PERIPH0_150M	10
+#define CLK_PLL_PERIPH1_4X	11
+#define CLK_PLL_PERIPH1_2X	12
+#define CLK_PLL_PERIPH1_800M	13
+#define CLK_PLL_PERIPH1_480M	14
+#define CLK_PLL_PERIPH1_600M	15
+#define CLK_PLL_PERIPH1_400M	16
+#define CLK_PLL_PERIPH1_300M	17
+#define CLK_PLL_PERIPH1_200M	18
+#define CLK_PLL_PERIPH1_160M	19
+#define CLK_PLL_PERIPH1_150M	20
+#define CLK_PLL_GPU		21
+#define CLK_PLL_VIDEO0_8X	22
+#define CLK_PLL_VIDEO0_4X	23
+#define CLK_PLL_VIDEO0_3X	24
+#define CLK_PLL_VIDEO1_8X	25
+#define CLK_PLL_VIDEO1_4X	26
+#define CLK_PLL_VIDEO1_3X	27
+#define CLK_PLL_VIDEO2_8X	28
+#define CLK_PLL_VIDEO2_4X	29
+#define CLK_PLL_VIDEO2_3X	30
+#define CLK_PLL_VIDEO3_8X	31
+#define CLK_PLL_VIDEO3_4X	32
+#define CLK_PLL_VIDEO3_3X	33
+#define CLK_PLL_VE		34
+#define CLK_PLL_AUDIO0_4X	35
+#define CLK_PLL_AUDIO0_2X	36
+#define CLK_PLL_AUDIO0		37
+#define CLK_PLL_NPU_4X		38
+#define CLK_PLL_NPU_2X		39
+#define CLK_PLL_NPU		40
+#define CLK_AHB			41
+#define CLK_APB0		42
+#define CLK_APB1		43
+#define CLK_MBUS		44
+#define CLK_DE			45
+#define CLK_BUS_DE		46
+#define CLK_DI			47
+#define CLK_BUS_DI		48
+#define CLK_G2D			49
+#define CLK_BUS_G2D		50
+#define CLK_GPU			51
+#define CLK_BUS_GPU		52
+#define CLK_CE			53
+#define CLK_BUS_CE		54
+#define CLK_BUS_CE_SYS		55
+#define CLK_VE			56
+#define CLK_BUS_VE		57
+#define CLK_BUS_DMA		58
+#define CLK_BUS_MSGBOX		59
+#define CLK_BUS_SPINLOCK	60
+#define CLK_HSTIMER0		61
+#define CLK_HSTIMER1		62
+#define CLK_HSTIMER2		63
+#define CLK_HSTIMER3		64
+#define CLK_HSTIMER4		65
+#define CLK_HSTIMER5		66
+#define CLK_BUS_HSTIMER		67
+#define CLK_BUS_DBG		68
+#define CLK_BUS_PWM0		69
+#define CLK_BUS_PWM1		70
+#define CLK_IOMMU		71
+#define CLK_BUS_IOMMU		72
+#define CLK_DRAM		73
+#define CLK_MBUS_DMA		74
+#define CLK_MBUS_VE		75
+#define CLK_MBUS_CE		76
+#define CLK_MBUS_CSI		77
+#define CLK_MBUS_ISP		78
+#define CLK_MBUS_EMAC1		79
+#define CLK_BUS_DRAM		80
+#define CLK_NAND0		81
+#define CLK_NAND1		82
+#define CLK_BUS_NAND		83
+#define CLK_MMC0		84
+#define CLK_MMC1		85
+#define CLK_MMC2		86
+#define CLK_BUS_SYSDAP		87
+#define CLK_BUS_MMC0		88
+#define CLK_BUS_MMC1		89
+#define CLK_BUS_MMC2		90
+#define CLK_BUS_UART0		91
+#define CLK_BUS_UART1		92
+#define CLK_BUS_UART2		93
+#define CLK_BUS_UART3		94
+#define CLK_BUS_UART4		95
+#define CLK_BUS_UART5		96
+#define CLK_BUS_UART6		97
+#define CLK_BUS_UART7		98
+#define CLK_BUS_I2C0		99
+#define CLK_BUS_I2C1		100
+#define CLK_BUS_I2C2		101
+#define CLK_BUS_I2C3		102
+#define CLK_BUS_I2C4		103
+#define CLK_BUS_I2C5		104
+#define CLK_BUS_CAN		105
+#define CLK_SPI0		106
+#define CLK_SPI1		107
+#define CLK_SPI2		108
+#define CLK_SPIFC		109
+#define CLK_BUS_SPI0		110
+#define CLK_BUS_SPI1		111
+#define CLK_BUS_SPI2		112
+#define CLK_BUS_SPIFC		113
+#define CLK_EMAC0_25M		114
+#define CLK_EMAC1_25M		115
+#define CLK_BUS_EMAC0		116
+#define CLK_BUS_EMAC1		117
+#define CLK_IR_RX		118
+#define CLK_BUS_IR_RX		119
+#define CLK_IR_TX		120
+#define CLK_BUS_IR_TX		121
+#define CLK_GPADC0		122
+#define CLK_GPADC1		123
+#define CLK_BUS_GPADC0		124
+#define CLK_BUS_GPADC1		125
+#define CLK_BUS_THS		126
+#define CLK_USB_OHCI0		127
+#define CLK_USB_OHCI1		128
+#define CLK_BUS_OHCI0		129
+#define CLK_BUS_OHCI1		130
+#define CLK_BUS_EHCI0		131
+#define CLK_BUS_EHCI1		132
+#define CLK_BUS_OTG		133
+#define CLK_BUS_LRADC		134
+#define CLK_PCIE_AUX		135
+#define CLK_BUS_DISPLAY0_TOP	136
+#define CLK_BUS_DISPLAY1_TOP	137
+#define CLK_HDMI_24M		138
+#define CLK_HDMI_CEC_32K	139
+#define CLK_HDMI_CEC		140
+#define CLK_BUS_HDMI		141
+#define CLK_MIPI_DSI0		142
+#define CLK_MIPI_DSI1		143
+#define CLK_BUS_MIPI_DSI0	144
+#define CLK_BUS_MIPI_DSI1	145
+#define CLK_TCON_LCD0		146
+#define CLK_TCON_LCD1		147
+#define CLK_TCON_LCD2		148
+#define CLK_COMBOPHY_DSI0	149
+#define CLK_COMBOPHY_DSI1	150
+#define CLK_BUS_TCON_LCD0	151
+#define CLK_BUS_TCON_LCD1	152
+#define CLK_BUS_TCON_LCD2	153
+#define CLK_TCON_TV0		154
+#define CLK_TCON_TV1		155
+#define CLK_BUS_TCON_TV0	156
+#define CLK_BUS_TCON_TV1	157
+#define CLK_EDP			158
+#define CLK_BUS_EDP		159
+#define CLK_LEDC		160
+#define CLK_BUS_LEDC		161
+#define CLK_CSI_TOP		162
+#define CLK_CSI_MCLK0		163
+#define CLK_CSI_MCLK1		164
+#define CLK_CSI_MCLK2		165
+#define CLK_CSI_MCLK3		166
+#define CLK_BUS_CSI		167
+#define CLK_ISP			168
+#define CLK_DSP			169
+#define CLK_FANOUT_24M		170
+#define CLK_FANOUT_12M		171
+#define CLK_FANOUT_16M		172
+#define CLK_FANOUT_25M		173
+#define CLK_FANOUT_27M		174
+#define CLK_FANOUT_PCLK		175
+#define CLK_FANOUT0		176
+#define CLK_FANOUT1		177
+#define CLK_FANOUT2		178
+
+#endif /* _DT_BINDINGS_CLK_SUN55I_A523_CCU_H_ */
diff --git a/include/dt-bindings/clock/sun55i-a523-r-ccu.h b/include/dt-bindings/clock/sun55i-a523-r-ccu.h
new file mode 100644
index 00000000000..365647499b9
--- /dev/null
+++ b/include/dt-bindings/clock/sun55i-a523-r-ccu.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2024 Arm Ltd.
+ */
+
+#ifndef _DT_BINDINGS_CLK_SUN55I_A523_R_CCU_H_
+#define _DT_BINDINGS_CLK_SUN55I_A523_R_CCU_H_
+
+#define CLK_R_AHB		0
+#define CLK_R_APB0		1
+#define CLK_R_APB1		2
+#define CLK_R_TIMER0		3
+#define CLK_R_TIMER1		4
+#define CLK_R_TIMER2		5
+#define CLK_BUS_R_TIMER		6
+#define CLK_BUS_R_TWD		7
+#define CLK_R_PWMCTRL		8
+#define CLK_BUS_R_PWMCTRL	9
+#define CLK_R_SPI		10
+#define CLK_BUS_R_SPI		11
+#define CLK_BUS_R_SPINLOCK	12
+#define CLK_BUS_R_MSGBOX	13
+#define CLK_BUS_R_UART0		14
+#define CLK_BUS_R_UART1		15
+#define CLK_BUS_R_I2C0		16
+#define CLK_BUS_R_I2C1		17
+#define CLK_BUS_R_I2C2		18
+#define CLK_BUS_R_PPU0		19
+#define CLK_BUS_R_PPU1		20
+#define CLK_BUS_R_CPU_BIST	21
+#define CLK_R_IR_RX		22
+#define CLK_BUS_R_IR_RX		23
+#define CLK_BUS_R_DMA		24
+#define CLK_BUS_R_RTC		25
+#define CLK_BUS_R_CPUCFG	26
+
+#endif /* _DT_BINDINGS_CLK_SUN55I_A523_R_CCU_H_ */
diff --git a/include/dt-bindings/reset/sun55i-a523-ccu.h b/include/dt-bindings/reset/sun55i-a523-ccu.h
new file mode 100644
index 00000000000..70df503f34f
--- /dev/null
+++ b/include/dt-bindings/reset/sun55i-a523-ccu.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (c) 2024 Arm Ltd.
+ */
+
+#ifndef _DT_BINDINGS_RST_SUN55I_A523_CCU_H_
+#define _DT_BINDINGS_RST_SUN55I_A523_CCU_H_
+
+#define RST_MBUS		0
+#define RST_BUS_NSI		1
+#define RST_BUS_DE		2
+#define RST_BUS_DI		3
+#define RST_BUS_G2D		4
+#define RST_BUS_SYS		5
+#define RST_BUS_GPU		6
+#define RST_BUS_CE		7
+#define RST_BUS_SYS_CE		8
+#define RST_BUS_VE		9
+#define RST_BUS_DMA		10
+#define RST_BUS_MSGBOX		11
+#define RST_BUS_SPINLOCK	12
+#define RST_BUS_CPUXTIMER	13
+#define RST_BUS_DBG		14
+#define RST_BUS_PWM0		15
+#define RST_BUS_PWM1		16
+#define RST_BUS_DRAM		17
+#define RST_BUS_NAND		18
+#define RST_BUS_MMC0		19
+#define RST_BUS_MMC1		20
+#define RST_BUS_MMC2		21
+#define RST_BUS_SYSDAP		22
+#define RST_BUS_UART0		23
+#define RST_BUS_UART1		24
+#define RST_BUS_UART2		25
+#define RST_BUS_UART3		26
+#define RST_BUS_UART4		27
+#define RST_BUS_UART5		28
+#define RST_BUS_UART6		29
+#define RST_BUS_UART7		30
+#define RST_BUS_I2C0		31
+#define RST_BUS_I2C1		32
+#define RST_BUS_I2C2		33
+#define RST_BUS_I2C3		34
+#define RST_BUS_I2C4		35
+#define RST_BUS_I2C5		36
+#define RST_BUS_CAN		37
+#define RST_BUS_SPI0		38
+#define RST_BUS_SPI1		39
+#define RST_BUS_SPI2		40
+#define RST_BUS_SPIFC		41
+#define RST_BUS_EMAC0		42
+#define RST_BUS_EMAC1		43
+#define RST_BUS_IR_RX		44
+#define RST_BUS_IR_TX		45
+#define RST_BUS_GPADC0		46
+#define RST_BUS_GPADC1		47
+#define RST_BUS_THS		48
+#define RST_USB_PHY0		49
+#define RST_USB_PHY1		50
+#define RST_BUS_OHCI0		51
+#define RST_BUS_OHCI1		52
+#define RST_BUS_EHCI0		53
+#define RST_BUS_EHCI1		54
+#define RST_BUS_OTG		55
+#define RST_BUS_3		56
+#define RST_BUS_LRADC		57
+#define RST_BUS_PCIE_USB3	58
+#define RST_BUS_DISPLAY0_TOP	59
+#define RST_BUS_DISPLAY1_TOP	60
+#define RST_BUS_HDMI_MAIN	61
+#define RST_BUS_HDMI_SUB	62
+#define RST_BUS_MIPI_DSI0	63
+#define RST_BUS_MIPI_DSI1	64
+#define RST_BUS_TCON_LCD0	65
+#define RST_BUS_TCON_LCD1	66
+#define RST_BUS_TCON_LCD2	67
+#define RST_BUS_TCON_TV0	68
+#define RST_BUS_TCON_TV1	69
+#define RST_BUS_LVDS0		70
+#define RST_BUS_LVDS1		71
+#define RST_BUS_EDP		72
+#define RST_BUS_VIDEO_OUT0	73
+#define RST_BUS_VIDEO_OUT1	74
+#define RST_BUS_LEDC		75
+#define RST_BUS_CSI		76
+#define RST_BUS_ISP		77
+
+#endif /* _DT_BINDINGS_RST_SUN55I_A523_CCU_H_ */
diff --git a/include/dt-bindings/reset/sun55i-a523-r-ccu.h b/include/dt-bindings/reset/sun55i-a523-r-ccu.h
new file mode 100644
index 00000000000..dd6fbb372e1
--- /dev/null
+++ b/include/dt-bindings/reset/sun55i-a523-r-ccu.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2024 Arm Ltd.
+ */
+
+#ifndef _DT_BINDINGS_RST_SUN55I_A523_R_CCU_H_
+#define _DT_BINDINGS_RST_SUN55I_A523_R_CCU_H_
+
+#define RST_BUS_R_TIMER		0
+#define RST_BUS_R_TWD		1
+#define RST_BUS_R_PWMCTRL	2
+#define RST_BUS_R_SPI		3
+#define RST_BUS_R_SPINLOCK	4
+#define RST_BUS_R_MSGBOX	5
+#define RST_BUS_R_UART0		6
+#define RST_BUS_R_UART1		7
+#define RST_BUS_R_I2C0		8
+#define RST_BUS_R_I2C1		9
+#define RST_BUS_R_I2C2		10
+#define RST_BUS_R_PPU1		11
+#define RST_BUS_R_IR_RX		12
+#define RST_BUS_R_RTC		13
+#define RST_BUS_R_CPUCFG	14
+
+#endif /* _DT_BINDINGS_RST_SUN55I_A523_R_CCU_H_ */
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 15/34] clk: sunxi: Add support for the A523 CCU
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (13 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 14/34] dt-bindings: add Allwinner A523 CCU bindings Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 16/34] clk: sunxi: Add support for the A523 -R CCU Andre Przywara
                   ` (19 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi, Lukasz Majewski, Sean Anderson

Add a clock driver for the main clock controller on the Allwinner A523
family of SoCs.
As usual, this just describes the clock gates and reset lines for the
few device that U-Boot cares about: USB, Ethernet, MMC, I2C, SPI.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 drivers/clk/sunxi/Kconfig     |  7 +++
 drivers/clk/sunxi/Makefile    |  1 +
 drivers/clk/sunxi/clk_a523.c  | 85 +++++++++++++++++++++++++++++++++++
 drivers/clk/sunxi/clk_sunxi.c |  5 +++
 4 files changed, 98 insertions(+)
 create mode 100644 drivers/clk/sunxi/clk_a523.c

diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig
index f44db76c182..74e89b86301 100644
--- a/drivers/clk/sunxi/Kconfig
+++ b/drivers/clk/sunxi/Kconfig
@@ -129,4 +129,11 @@ 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.
+
 endif # CLK_SUNXI
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 7ff71c756e0..dd33eabe2ed 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -25,3 +25,4 @@ 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
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_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c
index e0765cbc6dc..30baabaafcd 100644
--- a/drivers/clk/sunxi/clk_sunxi.c
+++ b/drivers/clk/sunxi/clk_sunxi.c
@@ -126,6 +126,7 @@ 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;
 
 static const struct udevice_id sunxi_clk_ids[] = {
 #ifdef CONFIG_CLK_SUN4I_A10
@@ -223,6 +224,10 @@ static const struct udevice_id sunxi_clk_ids[] = {
 #ifdef CONFIG_CLK_SUNIV_F1C100S
 	{ .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
 	{ }
 };
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 16/34] clk: sunxi: Add support for the A523 -R CCU
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (14 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 15/34] clk: sunxi: Add support for the A523 CCU Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 12:18   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 17/34] pinctrl: sunxi: add Allwinner A523 pinctrl description Andre Przywara
                   ` (18 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi, Lukasz Majewski, Sean Anderson

Add a clock driver for the PRCM clock controller on the Allwinner A523
family of SoCs, often also used with an "r" prefix or suffix.
This just describes the clock gates and reset lines for the few devices
that we would need, most prominently the R_I2C device for the PMIC.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 drivers/clk/sunxi/Kconfig      |  7 ++++++
 drivers/clk/sunxi/Makefile     |  1 +
 drivers/clk/sunxi/clk_a523_r.c | 44 ++++++++++++++++++++++++++++++++++
 drivers/clk/sunxi/clk_sunxi.c  |  5 ++++
 4 files changed, 57 insertions(+)
 create mode 100644 drivers/clk/sunxi/clk_a523_r.c

diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig
index 74e89b86301..1c1cc82719c 100644
--- a/drivers/clk/sunxi/Kconfig
+++ b/drivers/clk/sunxi/Kconfig
@@ -136,4 +136,11 @@ config CLK_SUN55I_A523
 	  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 dd33eabe2ed..93b542cebcd 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -26,3 +26,4 @@ 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_r.c b/drivers/clk/sunxi/clk_a523_r.c
new file mode 100644
index 00000000000..e864ce16199
--- /dev/null
+++ b/drivers/clk/sunxi/clk_a523_r.c
@@ -0,0 +1,44 @@
+// 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_TIMER]       = RESET(0x11c, BIT(16)),
+	[RST_BUS_R_TWD]         = RESET(0x12c, BIT(16)),
+//	[RST_BUS_R_PWMCTRL]     = RESET(0x13c, BIT(16)),
+//	[RST_BUS_R_SPI]         = RESET(0x15c, BIT(16)),
+//	[RST_BUS_R_UART0]       = RESET(0x18c, BIT(16)),
+//	[RST_BUS_R_UART1]       = RESET(0x18c, BIT(17)),
+	[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)),
+//	[RST_BUS_R_CPUCFG]      = RESET(0x22c, 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 30baabaafcd..842a0541bd6 100644
--- a/drivers/clk/sunxi/clk_sunxi.c
+++ b/drivers/clk/sunxi/clk_sunxi.c
@@ -127,6 +127,7 @@ 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
@@ -228,6 +229,10 @@ static const struct udevice_id sunxi_clk_ids[] = {
 #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
 	{ }
 };
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 17/34] pinctrl: sunxi: add Allwinner A523 pinctrl description
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (15 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 16/34] clk: sunxi: Add support for the A523 -R CCU Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 18/34] sunxi: mmc: add support for Allwinner A523 MMC mod clock Andre Przywara
                   ` (17 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

The new DT pinctrl binding would allow us to read the pinmux values from
the DT, but it is actually easier to just continue with hardcoding the
mux values in the driver, and matching them against the "function" name.

Add the values for the primary and secondary pin controller on the A523.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 drivers/pinctrl/sunxi/Kconfig         | 10 ++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.c | 49 +++++++++++++++++++++++++++
 2 files changed, 59 insertions(+)

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..46b266a3a1d 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,20 @@ 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 },
+	{ "s_i2c",	2 },	/* PL0-PL1 */
+	{ "s_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
 	{
@@ -983,6 +1020,18 @@ static const struct udevice_id sunxi_pinctrl_ids[] = {
 		.compatible = "allwinner,sun50i-a100-r-pinctrl",
 		.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
 	{}
 };
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 18/34] sunxi: mmc: add support for Allwinner A523 MMC mod clock
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (16 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 17/34] pinctrl: sunxi: add Allwinner A523 pinctrl description Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 19/34] watchdog: sunxi: add A523 support Andre Przywara
                   ` (16 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi, Peng Fan, Jaehoon Chung

The Allwinner A523 SoC has a slightly changed mod clock, where the P
factor, formerly a shift value, is now a second divider value.
Also the input clock is not PLL_PERIPH0_2X (1200MHz) anymore, but
PLL_PERIPH0_400M, so adjust the input rate calculation accordingly.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 drivers/mmc/sunxi_mmc.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
index 432be66c632..8614b04332d 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -92,6 +92,15 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
 		pll = CCM_MMC_CTRL_PLL6;
 		pll_hz = clock_get_pll6();
 #endif
+
+		/*
+		 * The A523/T527 uses PERIPH0_400M as the MMC input clock,
+		 * which is the PERIPH0 nominal rate (1200MHz) / 3.
+		 * Together with the fixed post-divider of 2 of the MMC mod
+		 * clock, that gives a divider of 6.
+		 */
+		if (IS_ENABLED(CONFIG_MACH_SUN55I_A523))
+			pll_hz /= 6;
 	}
 
 	div = pll_hz / hz;
@@ -146,6 +155,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);
 
@@ -534,7 +547,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;
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 19/34] watchdog: sunxi: add A523 support
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (17 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 18/34] sunxi: mmc: add support for Allwinner A523 MMC mod clock Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-24  8:38   ` Stefan Roese
  2025-03-23 11:35 ` [PATCH 20/34] power: regulator: add AXP323 support Andre Przywara
                   ` (15 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi, Stefan Roese

The Allwinner A523 SoC moved the watchdog into a separate MMIO frame,
and also shifted the registers a bit: the control, config, and mode
register are located four bytes earlier.

Add the new compatible string, and connect it to the new struct
describing the new register layout.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 drivers/watchdog/sunxi_wdt.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c
index 8eeac935760..467db5fe9bf 100644
--- a/drivers/watchdog/sunxi_wdt.c
+++ b/drivers/watchdog/sunxi_wdt.c
@@ -153,10 +153,21 @@ static const struct sunxi_wdt_reg sun20i_wdt_reg = {
 	.wdt_key_val		= 0x16aa0000,
 };
 
+static const struct sunxi_wdt_reg sun55i_wdt_reg = {
+	.wdt_ctrl		= 0x0c,
+	.wdt_cfg		= 0x10,
+	.wdt_mode		= 0x14,
+	.wdt_timeout_shift	= 4,
+	.wdt_reset_mask		= 0x03,
+	.wdt_reset_val		= 0x01,
+	.wdt_key_val		= 0x16aa0000,
+};
+
 static const struct udevice_id sunxi_wdt_ids[] = {
 	{ .compatible = "allwinner,sun4i-a10-wdt", .data = (ulong)&sun4i_wdt_reg },
 	{ .compatible = "allwinner,sun6i-a31-wdt", .data = (ulong)&sun6i_wdt_reg },
 	{ .compatible = "allwinner,sun20i-d1-wdt", .data = (ulong)&sun20i_wdt_reg },
+	{ .compatible = "allwinner,sun55i-a523-wdt", .data = (ulong)&sun55i_wdt_reg },
 	{ /* sentinel */ }
 };
 
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 20/34] power: regulator: add AXP323 support
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (18 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 19/34] watchdog: sunxi: add A523 support Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 21/34] sunxi: update cpu_sunxi_ncat2.h Andre Przywara
                   ` (14 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

The X-Powers AXP323 is very close sibling to the AXP313A, only that it
adds support for dual-phasing the first two DC/DC converters.

We do not really care about this particular feature, so just add the new
compatible string and tie it to the existing AXP313A support code.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 drivers/power/pmic/axp.c                | 1 +
 drivers/power/regulator/axp_regulator.c | 1 +
 include/axp_pmic.h                      | 1 +
 3 files changed, 3 insertions(+)

diff --git a/drivers/power/pmic/axp.c b/drivers/power/pmic/axp.c
index 521a39dd566..fbe9bf239a3 100644
--- a/drivers/power/pmic/axp.c
+++ b/drivers/power/pmic/axp.c
@@ -88,6 +88,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/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,
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 21/34] sunxi: update cpu_sunxi_ncat2.h
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (19 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 20/34] power: regulator: add AXP323 support Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 22/34] sunxi: Kconfig: consolidate SYS_CLK_FREQ selection Andre Przywara
                   ` (13 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

The cpu_sunxi_ncat2.h header file contains addresses of some peripherals
that are needed for the SPL, for chips that belong to the "NCAT2"
generation.
The Allwinner A523 is a member of this group, but a few addresses
differ, and we need a few more addresses, for playing with the core
reset, for instance.

Add the new addresses needed for the A523 and guard existing definitions
that conflict with that new chip.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 .../include/asm/arch-sunxi/cpu_sunxi_ncat2.h   | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

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 c04ddb3f1d4..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,32 @@
 #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
 
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 22/34] sunxi: Kconfig: consolidate SYS_CLK_FREQ selection
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (20 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 21/34] sunxi: update cpu_sunxi_ncat2.h Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 12:21   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 23/34] spl: reorder SPL_MAX_SIZE defaults for sunxi Andre Przywara
                   ` (12 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

Most Allwinner SoCs (used on 107 out of 172 boards) use a default CPU
frequency of 1008 MHz during the initial setup in the SPL.

Make this the fallback default, in case nothing else is selected, to
simplify the Kconfig stanza and make future additions easier.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/mach-sunxi/Kconfig | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 950b9047470..ab432390d3c 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -716,16 +716,10 @@ endif
 
 config SYS_CLK_FREQ
 	default 408000000 if MACH_SUNIV
-	default 1008000000 if MACH_SUN4I
-	default 1008000000 if MACH_SUN5I
-	default 1008000000 if MACH_SUN6I
-	default 912000000 if MACH_SUN7I
 	default 816000000 if MACH_SUN50I || MACH_SUN50I_H5
-	default 1008000000 if MACH_SUN8I
-	default 1008000000 if MACH_SUN9I
 	default 888000000 if MACH_SUN50I_H6
-	default 1008000000 if MACH_SUN50I_H616
-	default 1008000000 if MACH_SUN8I_R528
+	default 912000000 if MACH_SUN7I
+	default 1008000000
 
 config SYS_CONFIG_NAME
 	default "suniv" if MACH_SUNIV
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 23/34] spl: reorder SPL_MAX_SIZE defaults for sunxi
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (21 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 22/34] sunxi: Kconfig: consolidate SYS_CLK_FREQ selection Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 12:22   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 24/34] sunxi: armv8: fel: move fel_stash variable to the front Andre Przywara
                   ` (11 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

Reorder the Kconfig defaults for the maximum SPL size, to make the
Allwinner specific values more readable and extensible: many older SoCs
need to be limited to 32KB, so make this the last ARCH_SUNXI entry, used
as a fallback unless explicitly overridden before.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 common/spl/Kconfig | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 94e118f8465..29d30f5040d 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -80,11 +80,10 @@ config SPL_MAX_SIZE
 	default 0x1b000 if AM33XX && !TI_SECURE_DEVICE
 	default 0xec00 if OMAP34XX
 	default 0x10000 if ARCH_MX6 && !MX6_OCRAM_256KB
-	default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x10000
-	default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x20000 && !MACH_SUN50I_H616
 	default 0xbfa0 if MACH_SUN50I_H616
 	default 0x7000 if RCAR_GEN3
 	default 0x5fa0 if SUNXI_SRAM_ADDRESS = 0x0
+	default 0x7fa0 if ARCH_SUNXI
 	default 0x10000 if ASPEED_AST2600
 	default 0x27000 if IMX8MM && SPL_TEXT_BASE = 0x7E1000
 	default 0x30000 if ARCH_SC5XX && (SC59X_64 || SC59X)
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 24/34] sunxi: armv8: fel: move fel_stash variable to the front
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (22 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 23/34] spl: reorder SPL_MAX_SIZE defaults for sunxi Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 12:23   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 25/34] sunxi: arm64: boot0.h: move fel_stash_addr " Andre Przywara
                   ` (10 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

To return a 64-bit Allwinner chip back to the 32-bit BootROM code, we
have some embedded AArch32 code that restores the CPU state, before
branching back to the BootROM. At the moment the pointer to the buffer
with that state is located *after* the code, which makes the PC relative
code fragile: adding or removing instructions will change the distance
to that pointer variable.
The "new" Allwinner A523 SoC requires more state to be restored (GICv3
system registers), but we must do that *only* on that SoC. Conditional
compilation sounds like the easiest solution, but would mean that the
distance to that pointer would change.

Solve this rather easily by moving the pointer to the *front* of the
code: we load that pointer in the first instruction, so the distance
would always stay the same. Later in the code we won't need PC relative
addressing anymore, so this code can grow or shrink easily, for instance
due to conditional compilation.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/cpu/armv8/fel_utils.S | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/arch/arm/cpu/armv8/fel_utils.S b/arch/arm/cpu/armv8/fel_utils.S
index 939869b9ffa..f7707acdf1a 100644
--- a/arch/arm/cpu/armv8/fel_utils.S
+++ b/arch/arm/cpu/armv8/fel_utils.S
@@ -63,9 +63,12 @@ ENTRY(return_to_fel)
 1:	wfi
 	b	1b
 
+fel_stash_addr:			// must immediately precede back_in_32:
+	.word   0x00000000	// receives fel_stash addr, by AA64 code above
+
 /* AArch32 code to restore the state from fel_stash and return back to FEL. */
 back_in_32:
-	.word	0xe59f0028	// ldr	r0, [pc, #40]	; load fel_stash address
+	.word	0xe51f000c	// ldr	r0, [pc, #-12]  ; load fel_stash address
 	.word	0xe5901008	// ldr	r1, [r0, #8]
 	.word	0xe129f001	// msr	CPSR_fc, r1
 	.word	0xf57ff06f	// isb
@@ -77,6 +80,4 @@ back_in_32:
 	.word	0xee011f10	// mcr	15, 0, r1, cr1, cr0, {0}  ; SCTLR
 	.word	0xf57ff06f	// isb
 	.word	0xe12fff1e	// bx	lr		; return to FEL
-fel_stash_addr:
-	.word   0x00000000	// receives fel_stash addr, by AA64 code above
 ENDPROC(return_to_fel)
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 25/34] sunxi: arm64: boot0.h: move fel_stash_addr variable to the front
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (23 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 24/34] sunxi: armv8: fel: move fel_stash variable to the front Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 12:23   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 26/34] sunxi: update rmr_switch.S source code Andre Przywara
                   ` (9 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

To be able to return to the BootROM when booting via the FEL USB
protocol, we need to save the CPU state very early, which we need to do
in the embedded AArch32 code. At the moment the pointer to the buffer for
that state is located *after* the code, which makes the PC relative
code fragile: adding or removing instructions will change the distance
to that pointer variable.
The "new" Allwinner A523 SoC requires more state to be saved (GICv3
system registers), but we must do that *only* on that SoC. Conditional
compilation sounds like the easiest solution, but would mean that the
distance to that pointer would change.

Solve this rather easily by moving the pointer to the *front* of the
code: we load that pointer in the first instructions, so the distance
would always stay the same. Later in the code we won't need PC relative
addressing anymore, so this code can grow or shrink easily, for instance
due to conditional compilation.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/include/asm/arch-sunxi/boot0.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arm/include/asm/arch-sunxi/boot0.h b/arch/arm/include/asm/arch-sunxi/boot0.h
index 6b2bb5a4586..24c81391d58 100644
--- a/arch/arm/include/asm/arch-sunxi/boot0.h
+++ b/arch/arm/include/asm/arch-sunxi/boot0.h
@@ -16,10 +16,11 @@
  */
 	tst     x0, x0                  // this is "b #0x84" in ARM
 	b       reset
-	.space  0x7c
+	.space  0x78
+	.word	fel_stash - .
 
-	.word	0xe28f0070	// add     r0, pc, #112	 // @(fel_stash - .)
-	.word	0xe59f106c	// ldr     r1, [pc, #108] // fel_stash - .
+	.word	0xe24f000c	// sub     r0, pc, #12  // @(fel_stash - .)
+	.word	0xe51f1010	// ldr     r1, [pc, #-16] // fel_stash - .
 	.word	0xe0800001	// add     r0, r0, r1
 	.word	0xe580d000	// str     sp, [r0]
 	.word	0xe580e004	// str     lr, [r0, #4]
@@ -54,7 +55,6 @@
 #else
 	.word   CONFIG_TEXT_BASE
 #endif
-	.word	fel_stash - .
 #else
 /* normal execution */
 	b	reset
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 26/34] sunxi: update rmr_switch.S source code
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (24 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 25/34] sunxi: arm64: boot0.h: move fel_stash_addr " Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 12:24   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 27/34] sunxi: armv8: FEL: save and restore GICv3 registers Andre Przywara
                   ` (8 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

Because the Allwinner BootROM always runs in AArch32, even on ARMv8 SoCs,
we need to switch to AArch64 first, but also need to save the CPU state,
when we later may need to return to the BootROM, for continuing with the
FEL USB protocol. This is done in 32-bit code, which we include into the
AArch64 boot assembly file as a series of .word directives, containing
the encoded AArch32 instructions. To be able to change and verify that
code, we also kept an assembly file with the respective 32-bit code, but
just for reference.

As this code is never compiled or assembled - it's just for
documentation - it became stale over time: we didn't really update this
along with the changes we made to the boot code. In particular the FEL
save code was completely missing.

Update that 32-bit assembly file, to match the current version used in
boot0.h, including the FEL save routine. Also update the build
instructions in the comments, to give people an actual chance to
assemble this code.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/mach-sunxi/rmr_switch.S | 40 +++++++++++++++++++++++++++-----
 1 file changed, 34 insertions(+), 6 deletions(-)

diff --git a/arch/arm/mach-sunxi/rmr_switch.S b/arch/arm/mach-sunxi/rmr_switch.S
index 33e55d49686..422007c985b 100644
--- a/arch/arm/mach-sunxi/rmr_switch.S
+++ b/arch/arm/mach-sunxi/rmr_switch.S
@@ -16,7 +16,9 @@
 @ the machine code must be inserted as verbatim .word statements into the
 @ beginning of the AArch64 U-Boot code.
 @ To get the encoded bytes, use:
-@ ${CROSS_COMPILE}gcc -c -o rmr_switch.o rmr_switch.S
+@ ${CROSS_COMPILE}gcc -c -Iinclude -Iarch/arm/include	\
+@   -D__ASSEMBLY__ -DCONFIG_ARM64			\
+@   -o rmr_switch.o arch/arm/mach-sunxi/rmr_switch.S
 @ ${CROSS_COMPILE}objdump -d rmr_switch.o
 @
 @ The resulting words should be inserted into the U-Boot file at
@@ -29,14 +31,40 @@
 #include <config.h>
 
 .text
+	b	start32			// this is "tst x0, x0" in AArch64
+	.word	0x14000047		// this is "b   reset"  in AArch64
 
-#ifndef CONFIG_SUN50I_GEN_H6
-	ldr	r1, =0x017000a0		@ MMIO mapped RVBAR[0] register
+	.space  0x78			// gap distance set by the common
+					// encoding of the first instruction
+fel_stash_addr:
+	.word	fel_stash - .		// distance to fel_stash buffer
+
+start32:
+	adr	r0, fel_stash_addr	// absolute location of fel_stash_addr
+	ldr	r1, fel_stash_addr	// distance to actual fel_stash
+	add	r0, r0, r1		// real address of fel_stash
+
+	/* save the current state as needed by the BROM for a later return */
+	str	sp, [r0]
+	str	lr, [r0, #4]
+	mrs	lr, CPSR
+	str	lr, [r0, #8]
+	mrc	p15, 0, lr, cr1, cr0, 0		// SCTLR
+	str	lr, [r0, #12]
+	mrc	p15, 0, lr, cr12, cr0, 0	// VBAR
+	str	lr, [r0, #16]
+
+	ldr	r1, =CONFIG_SUNXI_RVBAR_ADDRESS
+	ldr	r0, =SUNXI_SRAMC_BASE
+	ldr	r0, [r0, #36]			// SRAM_VER_REG
+	ands	r0, r0, #0xff
+	ldrne	r1, =CONFIG_SUNXI_RVBAR_ALTERNATIVE
+#ifdef CONFIG_XPL_BUILD
+	ldr	r0, =CONFIG_SPL_TEXT_BASE
 #else
-	ldr	r1, =0x09010040		@ MMIO mapped RVBAR[0] register
+	ldr	r0, =CONFIG_TEXT_BASE
 #endif
-	ldr	r0, =0x57aA7add		@ start address, to be replaced
-	str	r0, [r1]
+	str	r0, [r1]			// store start address in RVBAR
 	dsb	sy
 	isb	sy
 	mrc	15, 0, r0, cr12, cr0, 2	@ read RMR register
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 27/34] sunxi: armv8: FEL: save and restore GICv3 registers
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (25 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 26/34] sunxi: update rmr_switch.S source code Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 12:25   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 28/34] sunxi: armv8: FEL: save and restore SP_IRQ Andre Przywara
                   ` (7 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

To be able to return to the BootROM FEL USB debug code, we must restore
the core's state as accurately as possible after the SPL has been run.
Since the BootROM runs in AArch32, but the SPL uses AArch64, this requires
a core reset, which clears the core's state.
So far we were saving and restoring the required registers like SCTLR
and VBAR, but could ignore the interrupt controller's state (GICC), since
that lives in MMIO registers, unaffected by a core reset.
Newer Allwinner SoCs now feature a GICv3 interrupt controller, which keeps
some GIC state in architected system registers, and those are cleared
when we switch back to AArch32.

To enable FEL operation on the Allwinner A523 SoC,
Add AArch32 assembly code to save and restore the ICC_PMR and ICC_IGRPEN1
system registers. The other GICv3 sysregs are either not relevant for the
BROM operation, or haven't been changed from their reset defaults by the
BROM anyway.

This enables FEL operation on the Allwinner A523 family of SoCs.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/cpu/armv8/fel_utils.S          |  7 +++++++
 arch/arm/include/asm/arch-sunxi/boot0.h | 10 +++++++++-
 arch/arm/mach-sunxi/board.c             |  2 ++
 arch/arm/mach-sunxi/rmr_switch.S        | 10 ++++++++++
 4 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/arch/arm/cpu/armv8/fel_utils.S b/arch/arm/cpu/armv8/fel_utils.S
index f7707acdf1a..f9d0c9e1d0a 100644
--- a/arch/arm/cpu/armv8/fel_utils.S
+++ b/arch/arm/cpu/armv8/fel_utils.S
@@ -79,5 +79,12 @@ back_in_32:
 	.word	0xe590100c	// ldr	r1, [r0, #12]
 	.word	0xee011f10	// mcr	15, 0, r1, cr1, cr0, {0}  ; SCTLR
 	.word	0xf57ff06f	// isb
+#ifdef CONFIG_MACH_SUN55I_A523
+	.word	0xe5901014	// ldr  r1, [r0, #20]
+	.word	0xee041f16	// mcr  15, 0, r1, cr4, cr6, {0}; ICC_PMR
+	.word	0xe5901018	// ldr  r1, [r0, #24]
+	.word	0xee0c1ffc	// mcr  15, 0, r1, cr12, cr12, {7}; ICC_IGRPEN1
+#endif
+
 	.word	0xe12fff1e	// bx	lr		; return to FEL
 ENDPROC(return_to_fel)
diff --git a/arch/arm/include/asm/arch-sunxi/boot0.h b/arch/arm/include/asm/arch-sunxi/boot0.h
index 24c81391d58..9baedc2e9af 100644
--- a/arch/arm/include/asm/arch-sunxi/boot0.h
+++ b/arch/arm/include/asm/arch-sunxi/boot0.h
@@ -30,7 +30,15 @@
 	.word	0xe580e00c	// str     lr, [r0, #12]
 	.word	0xee1cef10	// mrc     15, 0, lr, cr12, cr0, {0}
 	.word	0xe580e010	// str     lr, [r0, #16]
-
+#ifdef CONFIG_MACH_SUN55I_A523
+	.word	0xee1cefbc	// mrc     15, 0, lr, cr12, cr12, {5}
+	.word	0xe31e0001	// tst     lr, #1
+	.word	0x0a000003	// beq     cc <start32+0x48>
+	.word	0xee14ef16	// mrc     15, 0, lr, cr4, cr6, {0}
+	.word	0xe580e014	// str     lr, [r0, #20]
+	.word	0xee1ceffc	// mrc     15, 0, lr, cr12, cr12, {7}
+	.word	0xe580e018	// str     lr, [r0, #24]
+#endif
 	.word	0xe59f1034	// ldr     r1, [pc, #52] ; RVBAR_ADDRESS
 	.word	0xe59f0034	// ldr     r0, [pc, #52] ; SUNXI_SRAMC_BASE
 	.word	0xe5900024	// ldr     r0, [r0, #36] ; SRAM_VER_REG
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 89aea61e8e8..195c40d00c6 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -35,6 +35,8 @@ struct fel_stash {
 	uint32_t cpsr;
 	uint32_t sctlr;
 	uint32_t vbar;
+	uint32_t icc_pmr;
+	uint32_t icc_igrpen1;
 };
 
 struct fel_stash fel_stash __section(".data");
diff --git a/arch/arm/mach-sunxi/rmr_switch.S b/arch/arm/mach-sunxi/rmr_switch.S
index 422007c985b..de284c16b0b 100644
--- a/arch/arm/mach-sunxi/rmr_switch.S
+++ b/arch/arm/mach-sunxi/rmr_switch.S
@@ -53,6 +53,16 @@ start32:
 	str	lr, [r0, #12]
 	mrc	p15, 0, lr, cr12, cr0, 0	// VBAR
 	str	lr, [r0, #16]
+//#ifdef CONFIG_MACH_SUN55I_A523
+	mrc	p15, 0, lr, cr12, cr12, 5	// ICC_SRE
+	tst	lr, #1
+	beq	1f
+	mrc	p15, 0, lr, c4, c6, 0		// ICC_PMR
+	str	lr, [r0, #20]
+	mrc	p15, 0, lr, c12, c12, 7		// ICC_IGRPEN1
+	str	lr, [r0, #24]
+1:
+//#endif
 
 	ldr	r1, =CONFIG_SUNXI_RVBAR_ADDRESS
 	ldr	r0, =SUNXI_SRAMC_BASE
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 28/34] sunxi: armv8: FEL: save and restore SP_IRQ
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (26 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 27/34] sunxi: armv8: FEL: save and restore GICv3 registers Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 12:26   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 29/34] sunxi: sun50i_h6: add A523 SPL clock setup code Andre Przywara
                   ` (6 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

Thanks for Jernej's JTAG debugging effort, it turns out that the BROM
expects SP_IRQ to be saved and restored, when we want to enter back into
FEL after the SPL's AArch64 stint.
Save and restore SP_IRQ as part of the FEL state handling. The banked
MRS/MSR access to SP_IRQ, without actually being in IRQ mode, was
introduced with the ARMv7 virtualisation extensions. The Arm Cortex-A8
cores used in the A10/A13s or older F1C100s SoCs would not support that,
but this code here is purely in the ARMv8/AArch64 code path, so it's
safe to use unconditionally.

Reported-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/cpu/armv8/fel_utils.S          | 6 ++++--
 arch/arm/include/asm/arch-sunxi/boot0.h | 6 ++++--
 arch/arm/mach-sunxi/board.c             | 1 +
 arch/arm/mach-sunxi/rmr_switch.S        | 6 ++++--
 4 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/arch/arm/cpu/armv8/fel_utils.S b/arch/arm/cpu/armv8/fel_utils.S
index f9d0c9e1d0a..044a7c16cc5 100644
--- a/arch/arm/cpu/armv8/fel_utils.S
+++ b/arch/arm/cpu/armv8/fel_utils.S
@@ -74,15 +74,17 @@ back_in_32:
 	.word	0xf57ff06f	// isb
 	.word	0xe590d000	// ldr	sp, [r0]
 	.word	0xe590e004	// ldr	lr, [r0, #4]
+	.word	0xe5901014	// ldr  r1, [r0, #20]
+	.word	0xe121f301	// msr  SP_irq, r1
 	.word	0xe5901010	// ldr	r1, [r0, #16]
 	.word	0xee0c1f10	// mcr	15, 0, r1, cr12, cr0, {0} ; VBAR
 	.word	0xe590100c	// ldr	r1, [r0, #12]
 	.word	0xee011f10	// mcr	15, 0, r1, cr1, cr0, {0}  ; SCTLR
 	.word	0xf57ff06f	// isb
 #ifdef CONFIG_MACH_SUN55I_A523
-	.word	0xe5901014	// ldr  r1, [r0, #20]
-	.word	0xee041f16	// mcr  15, 0, r1, cr4, cr6, {0}; ICC_PMR
 	.word	0xe5901018	// ldr  r1, [r0, #24]
+	.word	0xee041f16	// mcr  15, 0, r1, cr4, cr6, {0}; ICC_PMR
+	.word	0xe590101c	// ldr  r1, [r0, #28]
 	.word	0xee0c1ffc	// mcr  15, 0, r1, cr12, cr12, {7}; ICC_IGRPEN1
 #endif
 
diff --git a/arch/arm/include/asm/arch-sunxi/boot0.h b/arch/arm/include/asm/arch-sunxi/boot0.h
index 9baedc2e9af..d79aea97a40 100644
--- a/arch/arm/include/asm/arch-sunxi/boot0.h
+++ b/arch/arm/include/asm/arch-sunxi/boot0.h
@@ -26,6 +26,8 @@
 	.word	0xe580e004	// str     lr, [r0, #4]
 	.word	0xe10fe000	// mrs     lr, CPSR
 	.word	0xe580e008	// str     lr, [r0, #8]
+	.word	0xe101e300	// mrs     lr, SP_irq
+	.word	0xe580e014	// str     lr, [r0, #20]
 	.word	0xee11ef10	// mrc     15, 0, lr, cr1, cr0, {0}
 	.word	0xe580e00c	// str     lr, [r0, #12]
 	.word	0xee1cef10	// mrc     15, 0, lr, cr12, cr0, {0}
@@ -35,9 +37,9 @@
 	.word	0xe31e0001	// tst     lr, #1
 	.word	0x0a000003	// beq     cc <start32+0x48>
 	.word	0xee14ef16	// mrc     15, 0, lr, cr4, cr6, {0}
-	.word	0xe580e014	// str     lr, [r0, #20]
-	.word	0xee1ceffc	// mrc     15, 0, lr, cr12, cr12, {7}
 	.word	0xe580e018	// str     lr, [r0, #24]
+	.word	0xee1ceffc	// mrc     15, 0, lr, cr12, cr12, {7}
+	.word	0xe580e01c	// str     lr, [r0, #28]
 #endif
 	.word	0xe59f1034	// ldr     r1, [pc, #52] ; RVBAR_ADDRESS
 	.word	0xe59f0034	// ldr     r0, [pc, #52] ; SUNXI_SRAMC_BASE
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 195c40d00c6..06a4a79146f 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -35,6 +35,7 @@ struct fel_stash {
 	uint32_t cpsr;
 	uint32_t sctlr;
 	uint32_t vbar;
+	uint32_t sp_irq;
 	uint32_t icc_pmr;
 	uint32_t icc_igrpen1;
 };
diff --git a/arch/arm/mach-sunxi/rmr_switch.S b/arch/arm/mach-sunxi/rmr_switch.S
index de284c16b0b..a6d75c32ed9 100644
--- a/arch/arm/mach-sunxi/rmr_switch.S
+++ b/arch/arm/mach-sunxi/rmr_switch.S
@@ -49,6 +49,8 @@ start32:
 	str	lr, [r0, #4]
 	mrs	lr, CPSR
 	str	lr, [r0, #8]
+	mrs	lr, SP_irq
+	str	lr, [r0, #20]
 	mrc	p15, 0, lr, cr1, cr0, 0		// SCTLR
 	str	lr, [r0, #12]
 	mrc	p15, 0, lr, cr12, cr0, 0	// VBAR
@@ -58,9 +60,9 @@ start32:
 	tst	lr, #1
 	beq	1f
 	mrc	p15, 0, lr, c4, c6, 0		// ICC_PMR
-	str	lr, [r0, #20]
-	mrc	p15, 0, lr, c12, c12, 7		// ICC_IGRPEN1
 	str	lr, [r0, #24]
+	mrc	p15, 0, lr, c12, c12, 7		// ICC_IGRPEN1
+	str	lr, [r0, #28]
 1:
 //#endif
 
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 29/34] sunxi: sun50i_h6: add A523 SPL clock setup code
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (27 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 28/34] sunxi: armv8: FEL: save and restore SP_IRQ Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 12:36   ` Jernej Škrabec
  2025-03-23 11:35 ` [PATCH 30/34] sunxi: A523: add DRAM initialisation routine Andre Przywara
                   ` (5 subsequent siblings)
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

From: Jernej Skrabec <jernej.skrabec@gmail.com>

---
 .../include/asm/arch-sunxi/clock_sun50i_h6.h  | 10 ++++++
 arch/arm/mach-sunxi/clock_sun50i_h6.c         | 32 ++++++++++++++-----
 2 files changed, 34 insertions(+), 8 deletions(-)

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 c95f2b39e64..d251ed49798 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
@@ -101,6 +101,13 @@
 #define CCM_PLL6_DEFAULT		0xe8216300
 #define CCM_PSI_AHB1_AHB2_DEFAULT	0x03000002
 #define CCM_APB1_DEFAULT		0x03000102
+
+#elif CONFIG_MACH_SUN55I_A523				/* A523 */
+
+#define CCM_PLL6_DEFAULT		0xe8116310	    /* 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 */
@@ -120,6 +127,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)
@@ -132,10 +140,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/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index f76d1b83883..2ba144a6ac3 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -14,15 +14,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);
 	}
 
@@ -39,9 +46,10 @@ void clock_init_safe(void)
 	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
@@ -53,7 +61,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)
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 30/34] sunxi: A523: add DRAM initialisation routine
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (28 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 29/34] sunxi: sun50i_h6: add A523 SPL clock setup code Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 13:15   ` Jernej Škrabec
  2025-04-05 22:01   ` Yixun Lan
  2025-03-23 11:35 ` [PATCH 31/34] sunxi: A523: add DDR3 DRAM support Andre Przywara
                   ` (4 subsequent siblings)
  34 siblings, 2 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

From: Jernej Skrabec <jernej.skrabec@gmail.com>

DRAM init code, as per reverse engineering and matching against
previous SoCs.
Supports LPDDR4 for now only.
---
 arch/arm/include/asm/arch-sunxi/dram.h        |    2 +
 .../include/asm/arch-sunxi/dram_sun55i_a523.h |  183 ++
 arch/arm/mach-sunxi/Kconfig                   |   31 +-
 arch/arm/mach-sunxi/Makefile                  |    2 +
 arch/arm/mach-sunxi/dram_sun55i_a523.c        | 1468 +++++++++++++++++
 arch/arm/mach-sunxi/dram_timings/Makefile     |    1 +
 .../arm/mach-sunxi/dram_timings/a523_lpddr4.c |  119 ++
 7 files changed, 1804 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
 create mode 100644 arch/arm/mach-sunxi/dram_sun55i_a523.c
 create mode 100644 arch/arm/mach-sunxi/dram_timings/a523_lpddr4.c

diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h
index 9d21b492418..9676c747442 100644
--- a/arch/arm/include/asm/arch-sunxi/dram.h
+++ b/arch/arm/include/asm/arch-sunxi/dram.h
@@ -33,6 +33,8 @@
 #include <asm/arch/dram_sun50i_h616.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..24e487b6880
--- /dev/null
+++ b/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
@@ -0,0 +1,183 @@
+/*
+ * t527 dram controller register and constant defines
+ *
+ * (C) Copyright 2024  Jernej Skrabec <jernej.skrabec@gmail.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _SUNXI_DRAM_SUN55I_A523_H
+#define _SUNXI_DRAM_SUN55I_A523_H
+
+#include <stdbool.h>
+#ifndef __ASSEMBLY__
+#include <linux/bitops.h>
+#endif
+
+enum sunxi_dram_type {
+	SUNXI_DRAM_TYPE_DDR3 = 3,
+	SUNXI_DRAM_TYPE_DDR4,
+	SUNXI_DRAM_TYPE_LPDDR3 = 7,
+	SUNXI_DRAM_TYPE_LPDDR4
+};
+
+/* MBUS part is largely the same as in H6, except for one special register */
+struct sunxi_mctl_com_reg {
+	u32 cr;			/* 0x000 control register */
+	u8 reserved_0x004[4];	/* 0x004 */
+	u32 unk_0x008;		/* 0x008 */
+	u32 tmr;		/* 0x00c timer register */
+	u8 reserved_0x010[4];	/* 0x010 */
+	u32 unk_0x014;		/* 0x014 */
+	u8 reserved_0x018[8];	/* 0x018 */
+	u32 unk_0x020;		/* 0x020 */
+	u32 unk_0x024;		/* 0x024 */
+	u32 unk_0x028;		/* 0x028 */
+	u8 reserved_0x02c[468];	/* 0x02c */
+	u32 bwcr;		/* 0x200 bandwidth control register */
+	u8 reserved_0x204[12];	/* 0x204 */
+	/*
+	 * The last master configured by BSP libdram is at 0x49x, so the
+	 * size of this struct array is set to 41 (0x29) now.
+	 */
+	struct {
+		u32 cfg0;		/* 0x0 */
+		u32 cfg1;		/* 0x4 */
+		u8 reserved_0x8[8];	/* 0x8 */
+	} master[41];		/* 0x210 + index * 0x10 */
+	u8 reserved_0x4a0[96];	/* 0x4a0 */
+	u32 unk_0x500;		/* 0x500 */
+};
+check_member(sunxi_mctl_com_reg, unk_0x500, 0x500);
+
+/*
+ * 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];	/* 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[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/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index ab432390d3c..aeab2b3f6dd 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -51,7 +51,12 @@ config DRAM_SUN50I_H616
 	  Select this dram controller driver for some sun50i platforms,
 	  like H616.
 
-if DRAM_SUN50I_H616
+config DRAM_SUN55I_A523
+	bool
+	help
+	  Select this DRAM controller driver for A523/T527 SoCs.
+
+if DRAM_SUN50I_H616 || DRAM_SUN55I_A523
 config DRAM_SUNXI_DX_ODT
 	hex "DRAM DX ODT parameter"
 	help
@@ -79,6 +84,12 @@ config DRAM_SUNXI_TPR0
 	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
@@ -109,6 +120,13 @@ config DRAM_SUNXI_TPR12
 	help
 	  TPR12 value from vendor DRAM settings.
 
+config DRAM_SUNXI_TPR14
+	hex "DRAM TPR14 parameter"
+	depends on MACH_SUN55I_A523
+	default 0x48484848
+	help
+	  TPR14 value from vendor DRAM settings.
+
 choice
 	prompt "DRAM PHY pin mapping selection"
 	default DRAM_SUNXI_PHY_ADDR_MAP_0
@@ -497,7 +515,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_SUN55I_A523
 config SUNXI_DRAM_DDR3
 	bool
 
@@ -512,6 +530,7 @@ config SUNXI_DRAM_LPDDR4
 
 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
 
@@ -577,6 +596,13 @@ 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_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
 
@@ -597,6 +623,7 @@ config DRAM_CLK
 	default 672 if MACH_SUN50I
 	default 744 if MACH_SUN50I_H6
 	default 720 if MACH_SUN50I_H616
+	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
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index eb6a49119a1..3521335dbbf 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
 obj-$(CONFIG_DRAM_SUN50I_H6)	+= dram_timings/
 obj-$(CONFIG_DRAM_SUN50I_H616)	+= dram_sun50i_h616.o
 obj-$(CONFIG_DRAM_SUN50I_H616)	+= 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/dram_sun55i_a523.c b/arch/arm/mach-sunxi/dram_sun55i_a523.c
new file mode 100644
index 00000000000..fae02062547
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_sun55i_a523.c
@@ -0,0 +1,1468 @@
+// 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_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)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+	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_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_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_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_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->unk_0x008, 0x200);
+	udelay(10);
+
+	switch (para->type) {
+	case SUNXI_DRAM_TYPE_LPDDR4:
+		val = para->tpr6 >> 24 & 0xff;
+		if (val)
+			val <<= 1;
+		else
+			val = 0x33;
+		break;
+	default:
+		panic("This DRAM setup is currently not supported.\n");
+	};
+	val <<= 23;
+
+	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->unk_0x020, 0x100);
+
+	/* 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_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)
+{
+	struct sunxi_mctl_com_reg * const mctl_com =
+			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+	struct sunxi_mctl_ctl_reg * const mctl_ctl =
+			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+	u32 reg_val;
+
+	clrsetbits_le32(&mctl_com->unk_0x008, BIT(24), 0x2000200);
+	setbits_le32(&mctl_com->unk_0x020, 0x8200);
+
+	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_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_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->unk_0x008, BIT(24), 0x200);
+	udelay(1);
+	/* this write seems to enable PHY MMIO region */
+	setbits_le32(&mctl_com->unk_0x008, 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 = {
+	.type = SUNXI_DRAM_TYPE_LPDDR4,
+	.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_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 5f203419240..7bcda180d0a 100644
--- a/arch/arm/mach-sunxi/dram_timings/Makefile
+++ b/arch/arm/mach-sunxi/dram_timings/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333)	+= h6_ddr3_1333.o
 obj-$(CONFIG_SUNXI_DRAM_H616_DDR3_1333)	+= h616_ddr3_1333.o
 obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3)	+= h616_lpddr3.o
 obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4)	+= h616_lpddr4_2133.o
+obj-$(CONFIG_SUNXI_DRAM_A523_LPDDR4)	+= a523_lpddr4.o
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..64a28e14c4e
--- /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);
+}
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 31/34] sunxi: A523: add DDR3 DRAM support
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (29 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 30/34] sunxi: A523: add DRAM initialisation routine Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 32/34] sunxi: add basic A523 support Andre Przywara
                   ` (3 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

From: Mikhail Kalashnikov <iuncuim@gmail.com>

Add reverse engineered code to add support for DDR3 DRAM chips on the
Allwinner A523 DRAM controller.
---
 arch/arm/mach-sunxi/Kconfig                  |   8 ++
 arch/arm/mach-sunxi/dram_sun55i_a523.c       | 136 ++++++++++++++++++-
 arch/arm/mach-sunxi/dram_timings/Makefile    |   1 +
 arch/arm/mach-sunxi/dram_timings/a523_ddr3.c | 135 ++++++++++++++++++
 4 files changed, 274 insertions(+), 6 deletions(-)
 create mode 100644 arch/arm/mach-sunxi/dram_timings/a523_ddr3.c

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index aeab2b3f6dd..345e685fc4b 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -596,6 +596,14 @@ 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
diff --git a/arch/arm/mach-sunxi/dram_sun55i_a523.c b/arch/arm/mach-sunxi/dram_sun55i_a523.c
index fae02062547..d5e3a2fa0eb 100644
--- a/arch/arm/mach-sunxi/dram_sun55i_a523.c
+++ b/arch/arm/mach-sunxi/dram_sun55i_a523.c
@@ -870,6 +870,24 @@ static void mctl_phy_ca_bit_delay_compensation(const struct dram_para *para,
 	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;
@@ -921,6 +939,26 @@ static bool mctl_phy_init(const struct dram_para *para,
 	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;
@@ -942,6 +980,36 @@ static bool mctl_phy_init(const struct dram_para *para,
 	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);
@@ -962,6 +1030,15 @@ static bool mctl_phy_init(const struct dram_para *para,
 		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;
@@ -973,6 +1050,15 @@ static bool mctl_phy_init(const struct dram_para *para,
 	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;
@@ -1003,17 +1089,22 @@ static bool mctl_phy_init(const struct dram_para *para,
 	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 & 0xff;
-		if (val)
-			val <<= 1;
-		else
-			val = 0x33;
+		val = para->tpr6 >> 24;
 		break;
 	default:
 		panic("This DRAM setup is currently not supported.\n");
 	};
-	val <<= 23;
+	val <<= 24;
 
 	clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x300, 0xff800060, val | 0x40);
 	clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x600, 0xff800060, val | 0x40);
@@ -1078,6 +1169,23 @@ static bool mctl_phy_init(const struct dram_para *para,
 	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;
@@ -1209,6 +1317,9 @@ static bool mctl_ctrl_init(const struct dram_para *para,
 
 	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;
@@ -1227,6 +1338,9 @@ static bool mctl_ctrl_init(const struct dram_para *para,
 		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;
@@ -1361,7 +1475,11 @@ static unsigned long long mctl_calc_size(const struct dram_config *config)
 }
 
 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,
@@ -1435,6 +1553,12 @@ unsigned long sunxi_dram_init(void)
 
 	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;
diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile
index 7bcda180d0a..7f1ee96b864 100644
--- a/arch/arm/mach-sunxi/dram_timings/Makefile
+++ b/arch/arm/mach-sunxi/dram_timings/Makefile
@@ -6,4 +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_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..b9fa5a89dd1
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
@@ -0,0 +1,135 @@
+/*
+ * sun55i A523 DDR3 timings, as programmed by Allwinner's boot0
+ *
+ * (C) Copyright 2024 Mikhail Kalashnikov <iuncuim@gmail.com>
+ *   Based on H6 DDR3 timings:
+ *   (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <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]);
+	writel(0x0e141a10, &mctl_ctl->dramtmg[0]);
+	writel(0x00040415, &mctl_ctl->dramtmg[1]);
+	writel(0x0507050b, &mctl_ctl->dramtmg[2]);
+	writel(0x0000400c, &mctl_ctl->dramtmg[3]);
+	writel(0x06020406, &mctl_ctl->dramtmg[4]);
+	writel(0x04040504, &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]);
+	writel(4, &mctl_ctl->dramtmg[14]);
+
+	//clrsetbits_le32(&mctl_ctl->init[0], 0xC0000FFF, 0x558);
+	clrsetbits_le32(&mctl_ctl->init[0], 0xC0000FFF, 0x156);
+	writel(0x01f20000, &mctl_ctl->init[1]);
+	//writel(0x00001705, &mctl_ctl->init[2]);
+	writel(0x00001700, &mctl_ctl->init[2]);
+	writel(0, &mctl_ctl->dfimisc);
+	//writel((mr1 << 16) | mr2, &mctl_ctl->init[3]);
+	writel(0x1f140004, &mctl_ctl->init[3]);
+	//writel(0x00330000, &mctl_ctl->init[4]);
+	//writel(0x00040072, &mctl_ctl->init[6]);
+	//writel(0x00260008, &mctl_ctl->init[7]);
+	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(0x02898005,&mctl_ctl->dfitmg0);
+	writel(0x100202, &mctl_ctl->dfitmg1);
+
+	/* set refresh timing */
+	//writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
+	writel(0x008c0000, &mctl_ctl->rfshtmg);
+}
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 32/34] sunxi: add basic A523 support
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (30 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 31/34] sunxi: A523: add DDR3 DRAM support Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-03-23 11:35 ` [PATCH 33/34] sunxi: A523: add DT files from Linux v3 branch Andre Przywara
                   ` (2 subsequent siblings)
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

Add the basic Kconfig options, addresses and other values for the
existing Kconfig settings for the new Allwinner A523/T527/H728 SoC.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/cpu/armv8/fel_utils.S |  4 ++--
 arch/arm/mach-sunxi/Kconfig    | 20 ++++++++++++++++++--
 arch/arm/mach-sunxi/board.c    |  5 +++++
 arch/arm/mach-sunxi/cpu_info.c |  2 ++
 board/sunxi/board.c            |  6 +++++-
 common/spl/Kconfig             |  4 +++-
 include/configs/sun55i.h       | 11 +++++++++++
 7 files changed, 46 insertions(+), 6 deletions(-)
 create mode 100644 include/configs/sun55i.h

diff --git a/arch/arm/cpu/armv8/fel_utils.S b/arch/arm/cpu/armv8/fel_utils.S
index 044a7c16cc5..290f83953ad 100644
--- a/arch/arm/cpu/armv8/fel_utils.S
+++ b/arch/arm/cpu/armv8/fel_utils.S
@@ -41,10 +41,10 @@ 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_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/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 345e685fc4b..69353a04f14 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -160,6 +160,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---
@@ -171,6 +172,7 @@ config SUNXI_SRAM_ADDRESS
 config SUNXI_RVBAR_ADDRESS
 	hex
 	depends on ARM64
+	default 0x08000040 if MACH_SUN55I_A523
 	default 0x09010040 if SUN50I_GEN_H6
 	default 0x017000a0
 	---help---
@@ -199,6 +201,7 @@ config SUNXI_BL31_BASE
 	default 0x00044000 if MACH_SUN50I || MACH_SUN50I_H5
 	default 0x00104000 if MACH_SUN50I_H6
 	default 0x40000000 if MACH_SUN50I_H616
+	default 0x00054000 if MACH_SUN55I_A523
 	default 0x0
 	help
 	  Address where BL31 (TF-A) is loaded, or zero if BL31 is not used.
@@ -280,7 +283,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_SUN55I_A523
 	default 0xC0000000 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN50I_H6
 	default 0x80000000
 
@@ -478,6 +481,15 @@ config MACH_SUN50I_H616
 config MACH_SUN50I_A133
 	bool "sun50i (Allwinner A133)"
 
+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
+
 endchoice
 
 # The sun8i SoCs share a lot, this helps to avoid a lot of "if A23 || A33"
@@ -649,6 +661,7 @@ endif
 config DRAM_ZQ
 	int "sunxi dram zq value"
 	depends on !MACH_SUN50I_H616
+	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
@@ -662,6 +675,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
@@ -751,6 +765,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
@@ -768,6 +783,7 @@ config SYS_CONFIG_NAME
 	default "sun50i" if MACH_SUN50I
 	default "sun50i" if MACH_SUN50I_H6
 	default "sun50i" if MACH_SUN50I_H616
+	default "sun55i" if MACH_SUN55I_A523
 
 config SYS_BOARD
 	default "sunxi"
@@ -877,7 +893,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/board.c b/arch/arm/mach-sunxi/board.c
index 06a4a79146f..a75159cf2af 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_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);
@@ -193,6 +197,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)) {
diff --git a/arch/arm/mach-sunxi/cpu_info.c b/arch/arm/mach-sunxi/cpu_info.c
index 310dca06e57..a52adf9ce7f 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_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/board/sunxi/board.c b/board/sunxi/board.c
index ac9cefc6eac..bf4f2088714 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,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_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 29d30f5040d..51cb6d85580 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_SUN55I_A523
 	default 0x7000 if RCAR_GEN3
 	default 0x5fa0 if SUNXI_SRAM_ADDRESS = 0x0
 	default 0x7fa0 if ARCH_SUNXI
@@ -274,6 +274,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
@@ -396,6 +397,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 0x44000 if MACH_SUN55I_A523
 	default 0x40000 if MACH_SUN8I_R528
 	default 0x54000 if MACH_SUN50I || MACH_SUN50I_H5
 	default 0x18000 if MACH_SUN9I
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 */
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 33/34] sunxi: A523: add DT files from Linux v3 branch
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (31 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 32/34] sunxi: add basic A523 support Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-04-09 14:28   ` Yixun Lan
  2025-03-23 11:35 ` [PATCH 34/34] sunxi: A523: add defconfigs for three boards Andre Przywara
  2025-04-05  2:44 ` [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Yixun Lan
  34 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

This imports the (preliminary) devicetree files for the boards with the
new Allwinner A523/T527/H728 SoCs, including the basic SoC .dtsi.

Those files have been reviewed and confirmed, but have not been merged
into the official kernel repositories yet.

Pending upstream kernel repository:
https://github.com/apritzel/linux/commits/a523-v3/

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm/dts/sun55i-a523.dtsi          | 598 +++++++++++++++++++++++++
 arch/arm/dts/sun55i-a527-radxa-a5e.dts | 299 +++++++++++++
 arch/arm/dts/sun55i-h728-x96qpro+.dts  | 287 ++++++++++++
 arch/arm/dts/sun55i-t527-avaota-a1.dts | 308 +++++++++++++
 4 files changed, 1492 insertions(+)
 create mode 100644 arch/arm/dts/sun55i-a523.dtsi
 create mode 100644 arch/arm/dts/sun55i-a527-radxa-a5e.dts
 create mode 100644 arch/arm/dts/sun55i-h728-x96qpro+.dts
 create mode 100644 arch/arm/dts/sun55i-t527-avaota-a1.dts

diff --git a/arch/arm/dts/sun55i-a523.dtsi b/arch/arm/dts/sun55i-a523.dtsi
new file mode 100644
index 00000000000..ee485899ba0
--- /dev/null
+++ b/arch/arm/dts/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/arch/arm/dts/sun55i-a527-radxa-a5e.dts b/arch/arm/dts/sun55i-a527-radxa-a5e.dts
new file mode 100644
index 00000000000..912e1bda974
--- /dev/null
+++ b/arch/arm/dts/sun55i-a527-radxa-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_DOWN)>; /* 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/arch/arm/dts/sun55i-h728-x96qpro+.dts b/arch/arm/dts/sun55i-h728-x96qpro+.dts
new file mode 100644
index 00000000000..c0bce3f4fa9
--- /dev/null
+++ b/arch/arm/dts/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_DOWN)>; /* 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/arch/arm/dts/sun55i-t527-avaota-a1.dts b/arch/arm/dts/sun55i-t527-avaota-a1.dts
new file mode 100644
index 00000000000..85a546aecdb
--- /dev/null
+++ b/arch/arm/dts/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_DOWN)>; /* 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";
+};
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [PATCH 34/34] sunxi: A523: add defconfigs for three boards
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (32 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 33/34] sunxi: A523: add DT files from Linux v3 branch Andre Przywara
@ 2025-03-23 11:35 ` Andre Przywara
  2025-04-05  2:44 ` [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Yixun Lan
  34 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 11:35 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

So far developers seem to use three popular boards:
- Avaota A1: dev board with USB 3.0, dual Ethernet, small display
- X96QPro+: TV box with Gigabit Ethernet, USB 3.0, eMMC
- Radxa A5E: small dev board with USB3/M.2 2230 (muxed), dual Ethernet

Add the defconfig files for those boards, containing the DRAM parameters
and the usual Kconfig options.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 configs/avaota-a1_defconfig     | 31 +++++++++++++++++++++++++++++++
 configs/radxa-a5e_defconfig     | 30 ++++++++++++++++++++++++++++++
 configs/x96q_pro_plus_defconfig | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 94 insertions(+)
 create mode 100644 configs/avaota-a1_defconfig
 create mode 100644 configs/radxa-a5e_defconfig
 create mode 100644 configs/x96q_pro_plus_defconfig

diff --git a/configs/avaota-a1_defconfig b/configs/avaota-a1_defconfig
new file mode 100644
index 00000000000..8721f2f5d1d
--- /dev/null
+++ b/configs/avaota-a1_defconfig
@@ -0,0 +1,31 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="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-a5e_defconfig b/configs/radxa-a5e_defconfig
new file mode 100644
index 00000000000..47c02fef7bd
--- /dev/null
+++ b/configs/radxa-a5e_defconfig
@@ -0,0 +1,30 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="sun55i-a527-radxa-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_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_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..b6bb18860f9
--- /dev/null
+++ b/configs/x96q_pro_plus_defconfig
@@ -0,0 +1,33 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="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_USB1_VBUS_PIN="PI12"
+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
-- 
2.46.3


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* Re: [PATCH 01/34] sunxi: clock: H6: drop usage of struct sunxi_ccm_reg
  2025-03-23 11:35 ` [PATCH 01/34] sunxi: clock: H6: drop usage of struct sunxi_ccm_reg Andre Przywara
@ 2025-03-23 11:56   ` Jernej Škrabec
  2025-03-23 23:50     ` Andre Przywara
  0 siblings, 1 reply; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 11:56 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

Hi Andre!

Dne nedelja, 23. marec 2025 ob 12:35:11 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> U-Boot drivers often revert to using C structures for modelling hardware
> register frames. This creates some problems:
> - A "struct" is a C language construct to group several variables
>   together. The details of the layout of this struct are partly subject
>   to the compiler's discretion (padding and alignment).
> - The "packed" attribute would force a certain layout, but we are not
>   using it.
> - The actual source of information from the data sheet is the register
>   offset. Here we create an artificial struct, carefully tuning the
>   layout (with a lot of reserved members) to match that offset. To help
>   with correctness, we put the desired information as a *comment*,
>   though this is purely for the human reader, and has no effect on the
>   generated layout. This sounds all very backwards.
> - Using a struct suggests we can assign a pointer and then access the
>   register content via the members. But this is not the case, instead
>   every MMIO register access must go through specific accessor functions,
>   to meet the ordering and access size guarantees the hardware requires.
> - We share those structs in code shared across multiple SoC families,
>   though most SoCs define their own version of the struct. Members must
>   match in their name, across every SoC, otherwise compilation will fail.
>   We work around this with even more #ifdefs in the shared code.
> - Some SoCs have an *almost* identical layout, but differ in a few
>   registers. This requires hard to maintain #ifdef's in the struct
>   definition.
> - Some of the register frames are huge: the H6 CCU device defines 127
>   registers. We use 15 of them. Still the whole frame would need to be
>   described, which is very tedious, but for no reason.
> - Adding a new SoC often forces people to decide whether to share an
>   existing struct, or to create a new copy. For some cases (say like 80%
>   similarity) this works out badly either way.
> 
> The Linux kernel heavily frowns upon those register structs, and instead
> uses a much simpler solution: #define REG_NAME	<offset>
> This easily maps to the actual information from the data sheet, and can
> much simpler be shared across multiple SoCs, as it allows to have all
> SoC versions visible, so we can use C "if" statements instead of #ifdef's.
> Also it requires to just define the registers we need, and we can use
> alternative locations for some registers much more easily.
> 
> Drop the usage of "struct sunxi_ccm_reg" in the H6 SPL clock code, by
> defining the respective register names and their offsets, then adding
> them to the base pointer.
> We cannot drop the struct definition quite yet, as it's also used in
> other drivers, still.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  .../include/asm/arch-sunxi/clock_sun50i_h6.h  | 12 +++++
>  arch/arm/mach-sunxi/clock_sun50i_h6.c         | 52 +++++++++----------
>  2 files changed, 36 insertions(+), 28 deletions(-)
> 
> 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 76dd33c9477..a485e00f1f3 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,18 @@
>  #include <linux/bitops.h>
>  #endif
>  
> +#define CCU_H6_PLL1_CFG			0x000
> +#define CCU_H6_PLL5_CFG			0x010
> +#define CCU_H6_PLL6_CFG			0x020
> +#define CCU_H6_CPU_AXI_CFG		0x500
> +#define CCU_H6_PSI_AHB1_AHB2_CFG	0x510
> +#define CCU_H6_AHB3_CFG			0x51c
> +#define CCU_H6_APB1_CFG			0x520
> +#define CCU_H6_APB2_CFG			0x524
> +#define CCU_H6_MBUS_CFG			0x540
> +#define CCU_H6_UART_GATE_RESET		0x90c
> +#define CCU_H6_I2C_GATE_RESET		0x91c

I appreciate this work very much and thanks for doing it! However, I
have just one more request - PLL1, PLL5, PLL6 and similar names are
relics of very old Allwinner SoC drivers. IMO it would be better to follow
names from user manual. This would lower confusion what are they to
uninitiated people.

Best regards.
Jernej

> +
>  struct sunxi_ccm_reg {
>  	u32 pll1_cfg;		/* 0x000 pll1 (cpux) control */
>  	u8 reserved_0x004[12];
> diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
> index b424a7893ea..482d9e0c695 100644
> --- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
> +++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
> @@ -6,8 +6,7 @@
>  #ifdef CONFIG_XPL_BUILD
>  void clock_init_safe(void)
>  {
> -	struct sunxi_ccm_reg *const ccm =
> -		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> +	void *const ccm = (void *)SUNXI_CCM_BASE;
>  	struct sunxi_prcm_reg *const prcm =
>  		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
>  
> @@ -32,60 +31,59 @@ void clock_init_safe(void)
>  
>  	clock_set_pll1(408000000);
>  
> -	writel(CCM_PLL6_DEFAULT, &ccm->pll6_cfg);
> -	while (!(readl(&ccm->pll6_cfg) & CCM_PLL6_LOCK))
> +	writel(CCM_PLL6_DEFAULT, ccm + CCU_H6_PLL6_CFG);
> +	while (!(readl(ccm + CCU_H6_PLL6_CFG) & CCM_PLL6_LOCK))
>  		;
>  
> -	clrsetbits_le32(&ccm->cpu_axi_cfg, CCM_CPU_AXI_APB_MASK | CCM_CPU_AXI_AXI_MASK,
> +	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->psi_ahb1_ahb2_cfg);
> +	writel(CCM_PSI_AHB1_AHB2_DEFAULT, ccm + CCU_H6_PSI_AHB1_AHB2_CFG);
>  #ifdef CCM_AHB3_DEFAULT
> -	writel(CCM_AHB3_DEFAULT, &ccm->ahb3_cfg);
> +	writel(CCM_AHB3_DEFAULT, ccm + CCU_H6_AHB3_CFG);
>  #endif
> -	writel(CCM_APB1_DEFAULT, &ccm->apb1_cfg);
> +	writel(CCM_APB1_DEFAULT, ccm + CCU_H6_APB1_CFG);
>  
>  	/*
>  	 * The mux and factor are set, but the clock will be enabled in
>  	 * DRAM initialization code.
>  	 */
> -	writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), &ccm->mbus_cfg);
> +	writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), ccm + CCU_H6_MBUS_CFG);
>  }
>  
>  void clock_init_uart(void)
>  {
> -	struct sunxi_ccm_reg *const ccm =
> -		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> +	void *const ccm = (void *)SUNXI_CCM_BASE;
>  
>  	/* uart clock source is apb2 */
>  	writel(APB2_CLK_SRC_OSC24M|
>  	       APB2_CLK_RATE_N_1|
>  	       APB2_CLK_RATE_M(1),
> -	       &ccm->apb2_cfg);
> +	       ccm + CCU_H6_APB2_CFG);
>  
>  	/* open the clock for uart */
> -	setbits_le32(&ccm->uart_gate_reset,
> +	setbits_le32(ccm + CCU_H6_UART_GATE_RESET,
>  		     1 << (CONFIG_CONS_INDEX - 1));
>  
>  	/* deassert uart reset */
> -	setbits_le32(&ccm->uart_gate_reset,
> +	setbits_le32(ccm + CCU_H6_UART_GATE_RESET,
>  		     1 << (RESET_SHIFT + CONFIG_CONS_INDEX - 1));
>  }
>  
>  void clock_set_pll1(unsigned int clk)
>  {
> -	struct sunxi_ccm_reg * const ccm =
> -		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> +	void *const ccm = (void *)SUNXI_CCM_BASE;
>  	u32 val;
>  
>  	/* Do not support clocks < 288MHz as they need factor P */
>  	if (clk < 288000000) clk = 288000000;
>  
>  	/* Switch to 24MHz clock while changing PLL1 */
> -	val = readl(&ccm->cpu_axi_cfg);
> +	val = readl(ccm + CCU_H6_CPU_AXI_CFG);
>  	val &= ~CCM_CPU_AXI_MUX_MASK;
>  	val |= CCM_CPU_AXI_MUX_OSC24M;
> -	writel(val, &ccm->cpu_axi_cfg);
> +	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;
> @@ -94,20 +92,19 @@ void clock_set_pll1(unsigned int clk)
>  	       val |= CCM_PLL1_OUT_EN;
>  	if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
>  	       val |= CCM_PLL1_OUT_EN | CCM_PLL1_LDO_EN;
> -	writel(val, &ccm->pll1_cfg);
> -	while (!(readl(&ccm->pll1_cfg) & CCM_PLL1_LOCK)) {}
> +	writel(val, ccm + CCU_H6_PLL1_CFG);
> +	while (!(readl(ccm + CCU_H6_PLL1_CFG) & CCM_PLL1_LOCK)) {}
>  
>  	/* Switch CPU to PLL1 */
> -	val = readl(&ccm->cpu_axi_cfg);
> +	val = readl(ccm + CCU_H6_CPU_AXI_CFG);
>  	val &= ~CCM_CPU_AXI_MUX_MASK;
>  	val |= CCM_CPU_AXI_MUX_PLL_CPUX;
> -	writel(val, &ccm->cpu_axi_cfg);
> +	writel(val, ccm + CCU_H6_CPU_AXI_CFG);
>  }
>  
>  int clock_twi_onoff(int port, int state)
>  {
> -	struct sunxi_ccm_reg *const ccm =
> -		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> +	void *const ccm = (void *)SUNXI_CCM_BASE;
>  	struct sunxi_prcm_reg *const prcm =
>  		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
>  	u32 value, *ptr;
> @@ -120,7 +117,7 @@ int clock_twi_onoff(int port, int state)
>  		ptr = &prcm->twi_gate_reset;
>  	} else {
>  		shift = port;
> -		ptr = &ccm->twi_gate_reset;
> +		ptr = ccm + CCU_H6_I2C_GATE_RESET;
>  	}
>  
>  	/* set the apb clock gate and reset for twi */
> @@ -136,9 +133,8 @@ int clock_twi_onoff(int port, int state)
>  /* PLL_PERIPH0 clock, used by the MMC driver */
>  unsigned int clock_get_pll6(void)
>  {
> -	struct sunxi_ccm_reg *const ccm =
> -		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> -	uint32_t rval = readl(&ccm->pll6_cfg);
> +	void *const ccm = (void *)SUNXI_CCM_BASE;
> +	uint32_t rval = readl(ccm + CCU_H6_PLL6_CFG);
>  	int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT) + 1;
>  	int div2 = ((rval & CCM_PLL6_CTRL_DIV2_MASK) >>
>  		    CCM_PLL6_CTRL_DIV2_SHIFT) + 1;
> 





^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 02/34] sunxi: mmc: remove usage of struct sunxi_ccm_reg
  2025-03-23 11:35 ` [PATCH 02/34] sunxi: mmc: remove " Andre Przywara
@ 2025-03-23 12:04   ` Jernej Škrabec
  0 siblings, 0 replies; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 12:04 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi, Peng Fan,
	Jaehoon Chung

Dne nedelja, 23. marec 2025 ob 12:35:12 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> The Allwinner MMC code uses a complex C struct, modelling the clock
> device's register frame. We rely on sharing the member names across all
> Allwinner SoCs, which is fragile.
> 
> Drop the usage of the struct in the MMC code, by using #define'd
> register names and their offset, and then adding those names to the base
> pointer. This requires to define those offsets for all SoCs, but since we
> only use between four and six clock registers in the MMC code, this is
> easily done.
> 
> This removes one common user of the clock register struct.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  arch/arm/include/asm/arch-sunxi/clock_sun4i.h |  6 ++++++
>  .../include/asm/arch-sunxi/clock_sun50i_h6.h  |  4 ++++
>  arch/arm/include/asm/arch-sunxi/clock_sun6i.h |  7 +++++++
>  .../include/asm/arch-sunxi/clock_sun8i_a83t.h |  7 +++++++
>  arch/arm/include/asm/arch-sunxi/clock_sun9i.h |  7 +++++++
>  drivers/mmc/sunxi_mmc.c                       | 20 +++++++++----------
>  6 files changed, 41 insertions(+), 10 deletions(-)
> 
> diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
> index 2cec91cb20e..00bdd5f938d 100644
> --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
> +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
> @@ -10,6 +10,12 @@
>  #ifndef _SUNXI_CLOCK_SUN4I_H
>  #define _SUNXI_CLOCK_SUN4I_H
>  
> +#define CCU_AHB_GATE0		0x60
> +#define CCU_MMC0_CLK_CFG	0x88
> +#define CCU_MMC1_CLK_CFG	0x8c
> +#define CCU_MMC2_CLK_CFG	0x90
> +#define CCU_MMC3_CLK_CFG	0x94
> +
>  struct sunxi_ccm_reg {
>  	u32 pll1_cfg;		/* 0x00 pll1 control */
>  	u32 pll1_tun;		/* 0x04 pll1 tuning */
> 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 a485e00f1f3..655f562c2af 100644
> --- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
> +++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
> @@ -22,6 +22,10 @@
>  #define CCU_H6_APB1_CFG			0x520
>  #define CCU_H6_APB2_CFG			0x524
>  #define CCU_H6_MBUS_CFG			0x540
> +#define CCU_MMC0_CLK_CFG		0x830
> +#define CCU_MMC1_CLK_CFG		0x834
> +#define CCU_MMC2_CLK_CFG		0x838
> +#define CCU_H6_MMC_GATE_RESET		0x84c
>  #define CCU_H6_UART_GATE_RESET		0x90c
>  #define CCU_H6_I2C_GATE_RESET		0x91c
>  
> diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
> index 7fcf340db69..28c3faccbbc 100644
> --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
> +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
> @@ -10,6 +10,13 @@
>  #ifndef _SUNXI_CLOCK_SUN6I_H
>  #define _SUNXI_CLOCK_SUN6I_H
>  
> +#define CCU_AHB_GATE0		0x060
> +#define CCU_MMC0_CLK_CFG	0x088
> +#define CCU_MMC1_CLK_CFG	0x08c
> +#define CCU_MMC2_CLK_CFG	0x090
> +#define CCU_MMC3_CLK_CFG	0x094
> +#define CCU_AHB_RESET0_CFG	0x2c0
> +
>  struct sunxi_ccm_reg {
>  	u32 pll1_cfg;		/* 0x00 pll1 control */
>  	u32 reserved0;
> diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h b/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h
> index 35ca0491ac9..5ad2163926a 100644
> --- a/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h
> +++ b/arch/arm/include/asm/arch-sunxi/clock_sun8i_a83t.h
> @@ -13,6 +13,13 @@
>  #ifndef _SUNXI_CLOCK_SUN8I_A83T_H
>  #define _SUNXI_CLOCK_SUN8I_A83T_H
>  
> +#define CCU_AHB_GATE0		0x060
> +#define CCU_MMC0_CLK_CFG	0x088
> +#define CCU_MMC1_CLK_CFG	0x08c
> +#define CCU_MMC2_CLK_CFG	0x090
> +#define CCU_MMC3_CLK_CFG	0x094
> +#define CCU_AHB_RESET0_CFG	0x2c0
> +
>  struct sunxi_ccm_reg {
>  	u32 pll1_c0_cfg;	/* 0x00 c1cpu# pll control */
>  	u32 pll1_c1_cfg;	/* 0x04 c1cpu# pll control */
> diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun9i.h b/arch/arm/include/asm/arch-sunxi/clock_sun9i.h
> index 006f7761fc6..8d696e533f8 100644
> --- a/arch/arm/include/asm/arch-sunxi/clock_sun9i.h
> +++ b/arch/arm/include/asm/arch-sunxi/clock_sun9i.h
> @@ -12,6 +12,13 @@
>  #include <linux/bitops.h>
>  #endif
>  
> +#define CCU_MMC0_CLK_CFG	0x410
> +#define CCU_MMC1_CLK_CFG	0x414
> +#define CCU_MMC2_CLK_CFG	0x418
> +#define CCU_MMC3_CLK_CFG	0x41c
> +#define CCU_AHB_GATE0		0x580
> +#define CCU_AHB_RESET0_CFG	0x5a0
> +
>  struct sunxi_ccm_reg {
>  	u32 pll1_c0_cfg;	/* 0x00 c0cpu# pll configuration */
>  	u32 pll2_c1_cfg;	/* 0x04 c1cpu# pll configuration */
> diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
> index 0b56d1405be..432be66c632 100644
> --- a/drivers/mmc/sunxi_mmc.c
> +++ b/drivers/mmc/sunxi_mmc.c
> @@ -451,29 +451,29 @@ struct sunxi_mmc_priv mmc_host[4];
>  static int mmc_resource_init(int sdc_no)
>  {
>  	struct sunxi_mmc_priv *priv = &mmc_host[sdc_no];
> -	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> +	void *ccm = (void *)SUNXI_CCM_BASE;
>  
>  	debug("init mmc %d resource\n", sdc_no);
>  
>  	switch (sdc_no) {
>  	case 0:
>  		priv->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE;
> -		priv->mclkreg = &ccm->sd0_clk_cfg;
> +		priv->mclkreg = ccm + CCU_MMC0_CLK_CFG;
>  		break;
>  	case 1:
>  		priv->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE;
> -		priv->mclkreg = &ccm->sd1_clk_cfg;
> +		priv->mclkreg = ccm + CCU_MMC1_CLK_CFG;
>  		break;
>  #ifdef SUNXI_MMC2_BASE
>  	case 2:
>  		priv->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE;
> -		priv->mclkreg = &ccm->sd2_clk_cfg;
> +		priv->mclkreg = ccm + CCU_MMC2_CLK_CFG;
>  		break;
>  #endif
>  #ifdef SUNXI_MMC3_BASE
>  	case 3:
>  		priv->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE;
> -		priv->mclkreg = &ccm->sd3_clk_cfg;
> +		priv->mclkreg = ccm + CCU_MMC3_CLK_CFG;
>  		break;
>  #endif
>  	default:
> @@ -520,7 +520,7 @@ static const struct mmc_ops sunxi_mmc_ops = {
>  
>  struct mmc *sunxi_mmc_init(int sdc_no)
>  {
> -	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> +	void *ccm = (void *)SUNXI_CCM_BASE;
>  	struct sunxi_mmc_priv *priv = &mmc_host[sdc_no];
>  	struct mmc_config *cfg = &priv->cfg;
>  	int ret;
> @@ -549,11 +549,11 @@ struct mmc *sunxi_mmc_init(int sdc_no)
>  	/* config ahb clock */
>  	debug("init mmc %d clock and io\n", sdc_no);
>  #if !defined(CONFIG_SUN50I_GEN_H6) && !defined(CONFIG_SUNXI_GEN_NCAT2)
> -	setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
> +	setbits_le32(ccm + CCU_AHB_GATE0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
>  
>  #ifdef CONFIG_SUNXI_GEN_SUN6I
>  	/* unassert reset */
> -	setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
> +	setbits_le32(ccm + CCU_AHB_RESET0_CFG, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
>  #endif
>  #if defined(CONFIG_MACH_SUN9I)
>  	/* sun9i has a mmc-common module, also set the gate and reset there */
> @@ -561,9 +561,9 @@ struct mmc *sunxi_mmc_init(int sdc_no)
>  	       SUNXI_MMC_COMMON_BASE + 4 * sdc_no);
>  #endif
>  #else /* CONFIG_SUN50I_GEN_H6 */
> -	setbits_le32(&ccm->sd_gate_reset, 1 << sdc_no);
> +	setbits_le32(ccm + CCU_H6_MMC_GATE_RESET, 1 << sdc_no);
>  	/* unassert reset */
> -	setbits_le32(&ccm->sd_gate_reset, 1 << (RESET_SHIFT + sdc_no));
> +	setbits_le32(ccm + CCU_H6_MMC_GATE_RESET, 1 << (RESET_SHIFT + sdc_no));

I guess this is in SPL path, so DT can't be used here? Anyway, can you use
some little more generic macro name? I assume there could be other generations
which can use same concept.

Best regards,
Jernej

>  #endif
>  	ret = mmc_set_mod_clk(priv, 24000000);
>  	if (ret)
> 





^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 13/34] sunxi: spl: add support for Allwinner A523 watchdog
  2025-03-23 11:35 ` [PATCH 13/34] sunxi: spl: add support for Allwinner A523 watchdog Andre Przywara
@ 2025-03-23 12:15   ` Jernej Škrabec
  2025-03-23 23:57     ` Andre Przywara
  0 siblings, 1 reply; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 12:15 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

Dne nedelja, 23. marec 2025 ob 12:35:23 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> From: Jernej Skrabec <jernej.skrabec@gmail.com>
> 
> The watchdog in the Allwinner A523 SoC differs a bit from the one in the
> previous SoCs: it lives in a separate register frame, so no longer
> inside some timer device, and it manages to shuffle around some
> registers a bit.
> 
> Provide a new struct describing the register layout, and adjust the
> address calculation in the SPL code accoringly. This is guarded by the
> MACH_SUN55I_A523 Kconfig variable.

This one is missing SoB, apparently by me :)

> ---
>  arch/arm/include/asm/arch-sunxi/watchdog.h | 12 ++++++++++++
>  arch/arm/mach-sunxi/board.c                |  6 ++++++
>  2 files changed, 18 insertions(+)
> 
> diff --git a/arch/arm/include/asm/arch-sunxi/watchdog.h b/arch/arm/include/asm/arch-sunxi/watchdog.h
> index 38e2ef2aca3..96d5725141e 100644
> --- a/arch/arm/include/asm/arch-sunxi/watchdog.h
> +++ b/arch/arm/include/asm/arch-sunxi/watchdog.h
> @@ -26,6 +26,18 @@ struct sunxi_wdog {
>  	u32 res[2];
>  };
>  
> +#elif defined(CONFIG_MACH_SUN55I_A523)
> +
> +struct sunxi_wdog {
> +	u32 irq_en;		/* 0x00 */
> +	u32 irq_sta;		/* 0x04 */
> +	u32 srst;		/* 0x08 */
> +	u32 ctl;		/* 0x0c */
> +	u32 cfg;		/* 0x10 */
> +	u32 mode;		/* 0x14 */
> +	u32 ocfg;		/* 0x18 */
> +};

Anyway, since you announced battle against C structures for register
layout, it seems contraproductive to add it here. What do you think?

Best regards,
Jernej

> +
>  #else
>  
>  #define WDT_CFG_RESET		(0x1)
> diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
> index 701899ee4b2..89aea61e8e8 100644
> --- a/arch/arm/mach-sunxi/board.c
> +++ b/arch/arm/mach-sunxi/board.c
> @@ -495,6 +495,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 struct sunxi_wdog *wdog =
> +		(struct sunxi_wdog *)SUNXI_TIMER_BASE;
> +
> +	writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, &wdog->srst);
> +	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 */
> 





^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 16/34] clk: sunxi: Add support for the A523 -R CCU
  2025-03-23 11:35 ` [PATCH 16/34] clk: sunxi: Add support for the A523 -R CCU Andre Przywara
@ 2025-03-23 12:18   ` Jernej Škrabec
  2025-03-24  0:37     ` Andre Przywara
  0 siblings, 1 reply; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 12:18 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi,
	Lukasz Majewski, Sean Anderson

Dne nedelja, 23. marec 2025 ob 12:35:26 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> Add a clock driver for the PRCM clock controller on the Allwinner A523
> family of SoCs, often also used with an "r" prefix or suffix.
> This just describes the clock gates and reset lines for the few devices
> that we would need, most prominently the R_I2C device for the PMIC.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  drivers/clk/sunxi/Kconfig      |  7 ++++++
>  drivers/clk/sunxi/Makefile     |  1 +
>  drivers/clk/sunxi/clk_a523_r.c | 44 ++++++++++++++++++++++++++++++++++
>  drivers/clk/sunxi/clk_sunxi.c  |  5 ++++
>  4 files changed, 57 insertions(+)
>  create mode 100644 drivers/clk/sunxi/clk_a523_r.c
> 
> diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig
> index 74e89b86301..1c1cc82719c 100644
> --- a/drivers/clk/sunxi/Kconfig
> +++ b/drivers/clk/sunxi/Kconfig
> @@ -136,4 +136,11 @@ config CLK_SUN55I_A523
>  	  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 dd33eabe2ed..93b542cebcd 100644
> --- a/drivers/clk/sunxi/Makefile
> +++ b/drivers/clk/sunxi/Makefile
> @@ -26,3 +26,4 @@ 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_r.c b/drivers/clk/sunxi/clk_a523_r.c
> new file mode 100644
> index 00000000000..e864ce16199
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk_a523_r.c
> @@ -0,0 +1,44 @@
> +// 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_TIMER]       = RESET(0x11c, BIT(16)),
> +	[RST_BUS_R_TWD]         = RESET(0x12c, BIT(16)),
> +//	[RST_BUS_R_PWMCTRL]     = RESET(0x13c, BIT(16)),
> +//	[RST_BUS_R_SPI]         = RESET(0x15c, BIT(16)),
> +//	[RST_BUS_R_UART0]       = RESET(0x18c, BIT(16)),
> +//	[RST_BUS_R_UART1]       = RESET(0x18c, BIT(17)),
> +	[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)),
> +//	[RST_BUS_R_CPUCFG]      = RESET(0x22c, BIT(16)),

Any specific reason that you commented out some reset lines?

Best regards,
Jernej

> +};
> +
> +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 30baabaafcd..842a0541bd6 100644
> --- a/drivers/clk/sunxi/clk_sunxi.c
> +++ b/drivers/clk/sunxi/clk_sunxi.c
> @@ -127,6 +127,7 @@ 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
> @@ -228,6 +229,10 @@ static const struct udevice_id sunxi_clk_ids[] = {
>  #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
>  	{ }
>  };
> 





^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 22/34] sunxi: Kconfig: consolidate SYS_CLK_FREQ selection
  2025-03-23 11:35 ` [PATCH 22/34] sunxi: Kconfig: consolidate SYS_CLK_FREQ selection Andre Przywara
@ 2025-03-23 12:21   ` Jernej Škrabec
  0 siblings, 0 replies; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 12:21 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

Dne nedelja, 23. marec 2025 ob 12:35:32 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> Most Allwinner SoCs (used on 107 out of 172 boards) use a default CPU
> frequency of 1008 MHz during the initial setup in the SPL.
> 
> Make this the fallback default, in case nothing else is selected, to
> simplify the Kconfig stanza and make future additions easier.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>

Best regards,
Jernej

> ---
>  arch/arm/mach-sunxi/Kconfig | 10 ++--------
>  1 file changed, 2 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> index 950b9047470..ab432390d3c 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -716,16 +716,10 @@ endif
>  
>  config SYS_CLK_FREQ
>  	default 408000000 if MACH_SUNIV
> -	default 1008000000 if MACH_SUN4I
> -	default 1008000000 if MACH_SUN5I
> -	default 1008000000 if MACH_SUN6I
> -	default 912000000 if MACH_SUN7I
>  	default 816000000 if MACH_SUN50I || MACH_SUN50I_H5
> -	default 1008000000 if MACH_SUN8I
> -	default 1008000000 if MACH_SUN9I
>  	default 888000000 if MACH_SUN50I_H6
> -	default 1008000000 if MACH_SUN50I_H616
> -	default 1008000000 if MACH_SUN8I_R528
> +	default 912000000 if MACH_SUN7I
> +	default 1008000000
>  
>  config SYS_CONFIG_NAME
>  	default "suniv" if MACH_SUNIV
> 





^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 23/34] spl: reorder SPL_MAX_SIZE defaults for sunxi
  2025-03-23 11:35 ` [PATCH 23/34] spl: reorder SPL_MAX_SIZE defaults for sunxi Andre Przywara
@ 2025-03-23 12:22   ` Jernej Škrabec
  0 siblings, 0 replies; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 12:22 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

Dne nedelja, 23. marec 2025 ob 12:35:33 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> Reorder the Kconfig defaults for the maximum SPL size, to make the
> Allwinner specific values more readable and extensible: many older SoCs
> need to be limited to 32KB, so make this the last ARCH_SUNXI entry, used
> as a fallback unless explicitly overridden before.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>

Best regards,
Jernej

> ---
>  common/spl/Kconfig | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)
> 
> diff --git a/common/spl/Kconfig b/common/spl/Kconfig
> index 94e118f8465..29d30f5040d 100644
> --- a/common/spl/Kconfig
> +++ b/common/spl/Kconfig
> @@ -80,11 +80,10 @@ config SPL_MAX_SIZE
>  	default 0x1b000 if AM33XX && !TI_SECURE_DEVICE
>  	default 0xec00 if OMAP34XX
>  	default 0x10000 if ARCH_MX6 && !MX6_OCRAM_256KB
> -	default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x10000
> -	default 0x7fa0 if SUNXI_SRAM_ADDRESS = 0x20000 && !MACH_SUN50I_H616
>  	default 0xbfa0 if MACH_SUN50I_H616
>  	default 0x7000 if RCAR_GEN3
>  	default 0x5fa0 if SUNXI_SRAM_ADDRESS = 0x0
> +	default 0x7fa0 if ARCH_SUNXI
>  	default 0x10000 if ASPEED_AST2600
>  	default 0x27000 if IMX8MM && SPL_TEXT_BASE = 0x7E1000
>  	default 0x30000 if ARCH_SC5XX && (SC59X_64 || SC59X)
> 





^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 24/34] sunxi: armv8: fel: move fel_stash variable to the front
  2025-03-23 11:35 ` [PATCH 24/34] sunxi: armv8: fel: move fel_stash variable to the front Andre Przywara
@ 2025-03-23 12:23   ` Jernej Škrabec
  0 siblings, 0 replies; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 12:23 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

Dne nedelja, 23. marec 2025 ob 12:35:34 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> To return a 64-bit Allwinner chip back to the 32-bit BootROM code, we
> have some embedded AArch32 code that restores the CPU state, before
> branching back to the BootROM. At the moment the pointer to the buffer
> with that state is located *after* the code, which makes the PC relative
> code fragile: adding or removing instructions will change the distance
> to that pointer variable.
> The "new" Allwinner A523 SoC requires more state to be restored (GICv3
> system registers), but we must do that *only* on that SoC. Conditional
> compilation sounds like the easiest solution, but would mean that the
> distance to that pointer would change.
> 
> Solve this rather easily by moving the pointer to the *front* of the
> code: we load that pointer in the first instruction, so the distance
> would always stay the same. Later in the code we won't need PC relative
> addressing anymore, so this code can grow or shrink easily, for instance
> due to conditional compilation.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>

Best regards,
Jernej



^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 25/34] sunxi: arm64: boot0.h: move fel_stash_addr variable to the front
  2025-03-23 11:35 ` [PATCH 25/34] sunxi: arm64: boot0.h: move fel_stash_addr " Andre Przywara
@ 2025-03-23 12:23   ` Jernej Škrabec
  0 siblings, 0 replies; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 12:23 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

Dne nedelja, 23. marec 2025 ob 12:35:35 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> To be able to return to the BootROM when booting via the FEL USB
> protocol, we need to save the CPU state very early, which we need to do
> in the embedded AArch32 code. At the moment the pointer to the buffer for
> that state is located *after* the code, which makes the PC relative
> code fragile: adding or removing instructions will change the distance
> to that pointer variable.
> The "new" Allwinner A523 SoC requires more state to be saved (GICv3
> system registers), but we must do that *only* on that SoC. Conditional
> compilation sounds like the easiest solution, but would mean that the
> distance to that pointer would change.
> 
> Solve this rather easily by moving the pointer to the *front* of the
> code: we load that pointer in the first instructions, so the distance
> would always stay the same. Later in the code we won't need PC relative
> addressing anymore, so this code can grow or shrink easily, for instance
> due to conditional compilation.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>

Best regards,
Jernej



^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 26/34] sunxi: update rmr_switch.S source code
  2025-03-23 11:35 ` [PATCH 26/34] sunxi: update rmr_switch.S source code Andre Przywara
@ 2025-03-23 12:24   ` Jernej Škrabec
  0 siblings, 0 replies; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 12:24 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

Dne nedelja, 23. marec 2025 ob 12:35:36 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> Because the Allwinner BootROM always runs in AArch32, even on ARMv8 SoCs,
> we need to switch to AArch64 first, but also need to save the CPU state,
> when we later may need to return to the BootROM, for continuing with the
> FEL USB protocol. This is done in 32-bit code, which we include into the
> AArch64 boot assembly file as a series of .word directives, containing
> the encoded AArch32 instructions. To be able to change and verify that
> code, we also kept an assembly file with the respective 32-bit code, but
> just for reference.
> 
> As this code is never compiled or assembled - it's just for
> documentation - it became stale over time: we didn't really update this
> along with the changes we made to the boot code. In particular the FEL
> save code was completely missing.
> 
> Update that 32-bit assembly file, to match the current version used in
> boot0.h, including the FEL save routine. Also update the build
> instructions in the comments, to give people an actual chance to
> assemble this code.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>

Best regards,
Jernej



^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 27/34] sunxi: armv8: FEL: save and restore GICv3 registers
  2025-03-23 11:35 ` [PATCH 27/34] sunxi: armv8: FEL: save and restore GICv3 registers Andre Przywara
@ 2025-03-23 12:25   ` Jernej Škrabec
  0 siblings, 0 replies; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 12:25 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

Dne nedelja, 23. marec 2025 ob 12:35:37 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> To be able to return to the BootROM FEL USB debug code, we must restore
> the core's state as accurately as possible after the SPL has been run.
> Since the BootROM runs in AArch32, but the SPL uses AArch64, this requires
> a core reset, which clears the core's state.
> So far we were saving and restoring the required registers like SCTLR
> and VBAR, but could ignore the interrupt controller's state (GICC), since
> that lives in MMIO registers, unaffected by a core reset.
> Newer Allwinner SoCs now feature a GICv3 interrupt controller, which keeps
> some GIC state in architected system registers, and those are cleared
> when we switch back to AArch32.
> 
> To enable FEL operation on the Allwinner A523 SoC,
> Add AArch32 assembly code to save and restore the ICC_PMR and ICC_IGRPEN1
> system registers. The other GICv3 sysregs are either not relevant for the
> BROM operation, or haven't been changed from their reset defaults by the
> BROM anyway.
> 
> This enables FEL operation on the Allwinner A523 family of SoCs.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>

Best regards,
Jernej



^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 28/34] sunxi: armv8: FEL: save and restore SP_IRQ
  2025-03-23 11:35 ` [PATCH 28/34] sunxi: armv8: FEL: save and restore SP_IRQ Andre Przywara
@ 2025-03-23 12:26   ` Jernej Škrabec
  2025-03-23 23:52     ` Andre Przywara
  0 siblings, 1 reply; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 12:26 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

Dne nedelja, 23. marec 2025 ob 12:35:38 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> Thanks for Jernej's JTAG debugging effort, it turns out that the BROM
> expects SP_IRQ to be saved and restored, when we want to enter back into
> FEL after the SPL's AArch64 stint.
> Save and restore SP_IRQ as part of the FEL state handling. The banked
> MRS/MSR access to SP_IRQ, without actually being in IRQ mode, was
> introduced with the ARMv7 virtualisation extensions. The Arm Cortex-A8
> cores used in the A10/A13s or older F1C100s SoCs would not support that,
> but this code here is purely in the ARMv8/AArch64 code path, so it's
> safe to use unconditionally.
> 
> Reported-by: Jernej Skrabec <jernej.skrabec@gmail.com>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

I have sneaky suspicion that this is already the issue on H616, but I
haven't yet confirmed.

FWIW:
Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>

Best regards,
Jernej



^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 29/34] sunxi: sun50i_h6: add A523 SPL clock setup code
  2025-03-23 11:35 ` [PATCH 29/34] sunxi: sun50i_h6: add A523 SPL clock setup code Andre Przywara
@ 2025-03-23 12:36   ` Jernej Škrabec
  0 siblings, 0 replies; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 12:36 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

Dne nedelja, 23. marec 2025 ob 12:35:39 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> From: Jernej Skrabec <jernej.skrabec@gmail.com>

I'm sure you can take credit for this as it's just making definitions and
putting them in place. But I can also provide SoB if you want.

> 
> ---
>  .../include/asm/arch-sunxi/clock_sun50i_h6.h  | 10 ++++++
>  arch/arm/mach-sunxi/clock_sun50i_h6.c         | 32 ++++++++++++++-----
>  2 files changed, 34 insertions(+), 8 deletions(-)
> 
> 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 c95f2b39e64..d251ed49798 100644
> --- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
> +++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
> @@ -101,6 +101,13 @@
>  #define CCM_PLL6_DEFAULT		0xe8216300
>  #define CCM_PSI_AHB1_AHB2_DEFAULT	0x03000002
>  #define CCM_APB1_DEFAULT		0x03000102
> +
> +#elif CONFIG_MACH_SUN55I_A523				/* A523 */
> +
> +#define CCM_PLL6_DEFAULT		0xe8116310	    /* 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 */
> @@ -120,6 +127,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)
> @@ -132,10 +140,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/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
> index f76d1b83883..2ba144a6ac3 100644
> --- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
> +++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
> @@ -14,15 +14,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);
>  	}
>  
> @@ -39,9 +46,10 @@ void clock_init_safe(void)
>  	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
> @@ -53,7 +61,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);
> +	}

While this is in BSP boot0, I don't think it's needed at all. Full reset is
done later in DRAM driver anyway.

Best regards,
Jernej

>  }
>  
>  void clock_init_uart(void)
> 





^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 30/34] sunxi: A523: add DRAM initialisation routine
  2025-03-23 11:35 ` [PATCH 30/34] sunxi: A523: add DRAM initialisation routine Andre Przywara
@ 2025-03-23 13:15   ` Jernej Škrabec
  2025-04-05 22:01   ` Yixun Lan
  1 sibling, 0 replies; 59+ messages in thread
From: Jernej Škrabec @ 2025-03-23 13:15 UTC (permalink / raw)
  To: Tom Rini, Andre Przywara
  Cc: Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

Dne nedelja, 23. marec 2025 ob 12:35:40 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> From: Jernej Skrabec <jernej.skrabec@gmail.com>
> 
> DRAM init code, as per reverse engineering and matching against
> previous SoCs.
> Supports LPDDR4 for now only.
> ---
>  arch/arm/include/asm/arch-sunxi/dram.h        |    2 +
>  .../include/asm/arch-sunxi/dram_sun55i_a523.h |  183 ++
>  arch/arm/mach-sunxi/Kconfig                   |   31 +-
>  arch/arm/mach-sunxi/Makefile                  |    2 +
>  arch/arm/mach-sunxi/dram_sun55i_a523.c        | 1468 +++++++++++++++++
>  arch/arm/mach-sunxi/dram_timings/Makefile     |    1 +
>  .../arm/mach-sunxi/dram_timings/a523_lpddr4.c |  119 ++
>  7 files changed, 1804 insertions(+), 2 deletions(-)
>  create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
>  create mode 100644 arch/arm/mach-sunxi/dram_sun55i_a523.c
>  create mode 100644 arch/arm/mach-sunxi/dram_timings/a523_lpddr4.c
> 
> diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h
> index 9d21b492418..9676c747442 100644
> --- a/arch/arm/include/asm/arch-sunxi/dram.h
> +++ b/arch/arm/include/asm/arch-sunxi/dram.h
> @@ -33,6 +33,8 @@
>  #include <asm/arch/dram_sun50i_h616.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..24e487b6880
> --- /dev/null
> +++ b/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
> @@ -0,0 +1,183 @@
> +/*
> + * t527 dram controller register and constant defines
> + *
> + * (C) Copyright 2024  Jernej Skrabec <jernej.skrabec@gmail.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#ifndef _SUNXI_DRAM_SUN55I_A523_H
> +#define _SUNXI_DRAM_SUN55I_A523_H
> +
> +#include <stdbool.h>
> +#ifndef __ASSEMBLY__
> +#include <linux/bitops.h>
> +#endif
> +
> +enum sunxi_dram_type {
> +	SUNXI_DRAM_TYPE_DDR3 = 3,
> +	SUNXI_DRAM_TYPE_DDR4,
> +	SUNXI_DRAM_TYPE_LPDDR3 = 7,
> +	SUNXI_DRAM_TYPE_LPDDR4
> +};
> +
> +/* MBUS part is largely the same as in H6, except for one special register */
> +struct sunxi_mctl_com_reg {
> +	u32 cr;			/* 0x000 control register */
> +	u8 reserved_0x004[4];	/* 0x004 */
> +	u32 unk_0x008;		/* 0x008 */
> +	u32 tmr;		/* 0x00c timer register */
> +	u8 reserved_0x010[4];	/* 0x010 */
> +	u32 unk_0x014;		/* 0x014 */
> +	u8 reserved_0x018[8];	/* 0x018 */
> +	u32 unk_0x020;		/* 0x020 */
> +	u32 unk_0x024;		/* 0x024 */
> +	u32 unk_0x028;		/* 0x028 */
> +	u8 reserved_0x02c[468];	/* 0x02c */
> +	u32 bwcr;		/* 0x200 bandwidth control register */
> +	u8 reserved_0x204[12];	/* 0x204 */
> +	/*
> +	 * The last master configured by BSP libdram is at 0x49x, so the
> +	 * size of this struct array is set to 41 (0x29) now.
> +	 */
> +	struct {
> +		u32 cfg0;		/* 0x0 */
> +		u32 cfg1;		/* 0x4 */
> +		u8 reserved_0x8[8];	/* 0x8 */
> +	} master[41];		/* 0x210 + index * 0x10 */
> +	u8 reserved_0x4a0[96];	/* 0x4a0 */
> +	u32 unk_0x500;		/* 0x500 */
> +};
> +check_member(sunxi_mctl_com_reg, unk_0x500, 0x500);

This struct doesn't make much sense. It's copy & paste from H616, only a few
registers are used and their names are not known. I would vote for it to be
redesigned as macros.

> +
> +/*
> + * 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];	/* 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[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);

While this struct is much more useful and properly named, I would still
change it to macros, which could even be shared with H6 and H616, since
controller didn't change much between generations.

Fun fact: Those unk_0x2180, unk_0x3180, etc. registers are actually same as
the ones on offset 0x180. In short, there are four backs of same registers.
Seemingly, this is needed for DDR4.

> +
> +#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/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> index ab432390d3c..aeab2b3f6dd 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -51,7 +51,12 @@ config DRAM_SUN50I_H616
>  	  Select this dram controller driver for some sun50i platforms,
>  	  like H616.
>  
> -if DRAM_SUN50I_H616
> +config DRAM_SUN55I_A523
> +	bool
> +	help
> +	  Select this DRAM controller driver for A523/T527 SoCs.
> +
> +if DRAM_SUN50I_H616 || DRAM_SUN55I_A523
>  config DRAM_SUNXI_DX_ODT
>  	hex "DRAM DX ODT parameter"
>  	help
> @@ -79,6 +84,12 @@ config DRAM_SUNXI_TPR0
>  	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
> @@ -109,6 +120,13 @@ config DRAM_SUNXI_TPR12
>  	help
>  	  TPR12 value from vendor DRAM settings.
>  
> +config DRAM_SUNXI_TPR14
> +	hex "DRAM TPR14 parameter"
> +	depends on MACH_SUN55I_A523
> +	default 0x48484848
> +	help
> +	  TPR14 value from vendor DRAM settings.
> +
>  choice
>  	prompt "DRAM PHY pin mapping selection"
>  	default DRAM_SUNXI_PHY_ADDR_MAP_0
> @@ -497,7 +515,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_SUN55I_A523
>  config SUNXI_DRAM_DDR3
>  	bool
>  
> @@ -512,6 +530,7 @@ config SUNXI_DRAM_LPDDR4
>  
>  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
>  
> @@ -577,6 +596,13 @@ 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_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
>  
> @@ -597,6 +623,7 @@ config DRAM_CLK
>  	default 672 if MACH_SUN50I
>  	default 744 if MACH_SUN50I_H6
>  	default 720 if MACH_SUN50I_H616
> +	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
> diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
> index eb6a49119a1..3521335dbbf 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
>  obj-$(CONFIG_DRAM_SUN50I_H6)	+= dram_timings/
>  obj-$(CONFIG_DRAM_SUN50I_H616)	+= dram_sun50i_h616.o
>  obj-$(CONFIG_DRAM_SUN50I_H616)	+= 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/dram_sun55i_a523.c b/arch/arm/mach-sunxi/dram_sun55i_a523.c
> new file mode 100644
> index 00000000000..fae02062547
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/dram_sun55i_a523.c
> @@ -0,0 +1,1468 @@
> +// 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;
> +

This one is untested, I guess warning would be nice?

> +	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;

This one works.

> +
> +	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;

This one is untested too.

> +
> +	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;

This one is simply wrong. I guess empty function with panic would be in order.

> +
> +	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_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)
> +{
> +	struct sunxi_mctl_com_reg * const mctl_com =
> +			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
> +	struct sunxi_mctl_ctl_reg * const mctl_ctl =
> +			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
> +	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_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_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_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_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->unk_0x008, 0x200);
> +	udelay(10);
> +
> +	switch (para->type) {
> +	case SUNXI_DRAM_TYPE_LPDDR4:
> +		val = para->tpr6 >> 24 & 0xff;
> +		if (val)
> +			val <<= 1;
> +		else
> +			val = 0x33;
> +		break;
> +	default:
> +		panic("This DRAM setup is currently not supported.\n");
> +	};
> +	val <<= 23;
> +
> +	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->unk_0x020, 0x100);
> +
> +	/* 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_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)
> +{
> +	struct sunxi_mctl_com_reg * const mctl_com =
> +			(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
> +	struct sunxi_mctl_ctl_reg * const mctl_ctl =
> +			(struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
> +	u32 reg_val;
> +
> +	clrsetbits_le32(&mctl_com->unk_0x008, BIT(24), 0x2000200);
> +	setbits_le32(&mctl_com->unk_0x020, 0x8200);
> +
> +	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_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_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->unk_0x008, BIT(24), 0x200);
> +	udelay(1);
> +	/* this write seems to enable PHY MMIO region */
> +	setbits_le32(&mctl_com->unk_0x008, 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;
> +}

As soon as H616 DRAM size fixup is merged, above functions should be replaced.

> +
> +static const struct dram_para para = {
> +	.type = SUNXI_DRAM_TYPE_LPDDR4,
> +	.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)

This replaces old mctl_set_master_priority(), but it needs to be expanded
to include all channels.

> +{
> +	/* 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)

This function should be removed.

Best regards,
Jernej

> +
> +{
> +	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_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 5f203419240..7bcda180d0a 100644
> --- a/arch/arm/mach-sunxi/dram_timings/Makefile
> +++ b/arch/arm/mach-sunxi/dram_timings/Makefile
> @@ -6,3 +6,4 @@ obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333)	+= h6_ddr3_1333.o
>  obj-$(CONFIG_SUNXI_DRAM_H616_DDR3_1333)	+= h616_ddr3_1333.o
>  obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3)	+= h616_lpddr3.o
>  obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4)	+= h616_lpddr4_2133.o
> +obj-$(CONFIG_SUNXI_DRAM_A523_LPDDR4)	+= a523_lpddr4.o
> 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..64a28e14c4e
> --- /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);
> +}
> 





^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 01/34] sunxi: clock: H6: drop usage of struct sunxi_ccm_reg
  2025-03-23 11:56   ` Jernej Škrabec
@ 2025-03-23 23:50     ` Andre Przywara
  0 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 23:50 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Tom Rini, Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

On Sun, 23 Mar 2025 12:56:55 +0100
Jernej Škrabec <jernej.skrabec@gmail.com> wrote:

Hi Jernej,

thanks for having a look!

> Dne nedelja, 23. marec 2025 ob 12:35:11 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> > U-Boot drivers often revert to using C structures for modelling hardware
> > register frames. This creates some problems:
> > - A "struct" is a C language construct to group several variables
> >   together. The details of the layout of this struct are partly subject
> >   to the compiler's discretion (padding and alignment).
> > - The "packed" attribute would force a certain layout, but we are not
> >   using it.
> > - The actual source of information from the data sheet is the register
> >   offset. Here we create an artificial struct, carefully tuning the
> >   layout (with a lot of reserved members) to match that offset. To help
> >   with correctness, we put the desired information as a *comment*,
> >   though this is purely for the human reader, and has no effect on the
> >   generated layout. This sounds all very backwards.
> > - Using a struct suggests we can assign a pointer and then access the
> >   register content via the members. But this is not the case, instead
> >   every MMIO register access must go through specific accessor functions,
> >   to meet the ordering and access size guarantees the hardware requires.
> > - We share those structs in code shared across multiple SoC families,
> >   though most SoCs define their own version of the struct. Members must
> >   match in their name, across every SoC, otherwise compilation will fail.
> >   We work around this with even more #ifdefs in the shared code.
> > - Some SoCs have an *almost* identical layout, but differ in a few
> >   registers. This requires hard to maintain #ifdef's in the struct
> >   definition.
> > - Some of the register frames are huge: the H6 CCU device defines 127
> >   registers. We use 15 of them. Still the whole frame would need to be
> >   described, which is very tedious, but for no reason.
> > - Adding a new SoC often forces people to decide whether to share an
> >   existing struct, or to create a new copy. For some cases (say like 80%
> >   similarity) this works out badly either way.
> > 
> > The Linux kernel heavily frowns upon those register structs, and instead
> > uses a much simpler solution: #define REG_NAME	<offset>
> > This easily maps to the actual information from the data sheet, and can
> > much simpler be shared across multiple SoCs, as it allows to have all
> > SoC versions visible, so we can use C "if" statements instead of #ifdef's.
> > Also it requires to just define the registers we need, and we can use
> > alternative locations for some registers much more easily.
> > 
> > Drop the usage of "struct sunxi_ccm_reg" in the H6 SPL clock code, by
> > defining the respective register names and their offsets, then adding
> > them to the base pointer.
> > We cannot drop the struct definition quite yet, as it's also used in
> > other drivers, still.
> > 
> > Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> > ---
> >  .../include/asm/arch-sunxi/clock_sun50i_h6.h  | 12 +++++
> >  arch/arm/mach-sunxi/clock_sun50i_h6.c         | 52 +++++++++----------
> >  2 files changed, 36 insertions(+), 28 deletions(-)
> > 
> > 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 76dd33c9477..a485e00f1f3 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,18 @@
> >  #include <linux/bitops.h>
> >  #endif
> >  
> > +#define CCU_H6_PLL1_CFG			0x000
> > +#define CCU_H6_PLL5_CFG			0x010
> > +#define CCU_H6_PLL6_CFG			0x020
> > +#define CCU_H6_CPU_AXI_CFG		0x500
> > +#define CCU_H6_PSI_AHB1_AHB2_CFG	0x510
> > +#define CCU_H6_AHB3_CFG			0x51c
> > +#define CCU_H6_APB1_CFG			0x520
> > +#define CCU_H6_APB2_CFG			0x524
> > +#define CCU_H6_MBUS_CFG			0x540
> > +#define CCU_H6_UART_GATE_RESET		0x90c
> > +#define CCU_H6_I2C_GATE_RESET		0x91c  
> 
> I appreciate this work very much and thanks for doing it!

Ah, great, I was hoping for this kind of feedback. I voiced my concern
about this many years ago, but this was met with some reservations back
then.

> However, I
> have just one more request - PLL1, PLL5, PLL6 and similar names are
> relics of very old Allwinner SoC drivers. IMO it would be better to follow
> names from user manual. This would lower confusion what are they to
> uninitiated people.

I agree, and that's literally patch 37/34 of that branch I took this
series from ;-), so stay tuned for that. The reason this is not in here
or before is that it affects all SoCs, and I wanted to keep that
separate and not blocking the A523 series.

Cheers,
Andre

> 
> Best regards.
> Jernej
> 
> > +
> >  struct sunxi_ccm_reg {
> >  	u32 pll1_cfg;		/* 0x000 pll1 (cpux) control */
> >  	u8 reserved_0x004[12];
> > diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
> > index b424a7893ea..482d9e0c695 100644
> > --- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
> > +++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
> > @@ -6,8 +6,7 @@
> >  #ifdef CONFIG_XPL_BUILD
> >  void clock_init_safe(void)
> >  {
> > -	struct sunxi_ccm_reg *const ccm =
> > -		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> > +	void *const ccm = (void *)SUNXI_CCM_BASE;
> >  	struct sunxi_prcm_reg *const prcm =
> >  		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
> >  
> > @@ -32,60 +31,59 @@ void clock_init_safe(void)
> >  
> >  	clock_set_pll1(408000000);
> >  
> > -	writel(CCM_PLL6_DEFAULT, &ccm->pll6_cfg);
> > -	while (!(readl(&ccm->pll6_cfg) & CCM_PLL6_LOCK))
> > +	writel(CCM_PLL6_DEFAULT, ccm + CCU_H6_PLL6_CFG);
> > +	while (!(readl(ccm + CCU_H6_PLL6_CFG) & CCM_PLL6_LOCK))
> >  		;
> >  
> > -	clrsetbits_le32(&ccm->cpu_axi_cfg, CCM_CPU_AXI_APB_MASK | CCM_CPU_AXI_AXI_MASK,
> > +	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->psi_ahb1_ahb2_cfg);
> > +	writel(CCM_PSI_AHB1_AHB2_DEFAULT, ccm + CCU_H6_PSI_AHB1_AHB2_CFG);
> >  #ifdef CCM_AHB3_DEFAULT
> > -	writel(CCM_AHB3_DEFAULT, &ccm->ahb3_cfg);
> > +	writel(CCM_AHB3_DEFAULT, ccm + CCU_H6_AHB3_CFG);
> >  #endif
> > -	writel(CCM_APB1_DEFAULT, &ccm->apb1_cfg);
> > +	writel(CCM_APB1_DEFAULT, ccm + CCU_H6_APB1_CFG);
> >  
> >  	/*
> >  	 * The mux and factor are set, but the clock will be enabled in
> >  	 * DRAM initialization code.
> >  	 */
> > -	writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), &ccm->mbus_cfg);
> > +	writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), ccm + CCU_H6_MBUS_CFG);
> >  }
> >  
> >  void clock_init_uart(void)
> >  {
> > -	struct sunxi_ccm_reg *const ccm =
> > -		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> > +	void *const ccm = (void *)SUNXI_CCM_BASE;
> >  
> >  	/* uart clock source is apb2 */
> >  	writel(APB2_CLK_SRC_OSC24M|
> >  	       APB2_CLK_RATE_N_1|
> >  	       APB2_CLK_RATE_M(1),
> > -	       &ccm->apb2_cfg);
> > +	       ccm + CCU_H6_APB2_CFG);
> >  
> >  	/* open the clock for uart */
> > -	setbits_le32(&ccm->uart_gate_reset,
> > +	setbits_le32(ccm + CCU_H6_UART_GATE_RESET,
> >  		     1 << (CONFIG_CONS_INDEX - 1));
> >  
> >  	/* deassert uart reset */
> > -	setbits_le32(&ccm->uart_gate_reset,
> > +	setbits_le32(ccm + CCU_H6_UART_GATE_RESET,
> >  		     1 << (RESET_SHIFT + CONFIG_CONS_INDEX - 1));
> >  }
> >  
> >  void clock_set_pll1(unsigned int clk)
> >  {
> > -	struct sunxi_ccm_reg * const ccm =
> > -		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> > +	void *const ccm = (void *)SUNXI_CCM_BASE;
> >  	u32 val;
> >  
> >  	/* Do not support clocks < 288MHz as they need factor P */
> >  	if (clk < 288000000) clk = 288000000;
> >  
> >  	/* Switch to 24MHz clock while changing PLL1 */
> > -	val = readl(&ccm->cpu_axi_cfg);
> > +	val = readl(ccm + CCU_H6_CPU_AXI_CFG);
> >  	val &= ~CCM_CPU_AXI_MUX_MASK;
> >  	val |= CCM_CPU_AXI_MUX_OSC24M;
> > -	writel(val, &ccm->cpu_axi_cfg);
> > +	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;
> > @@ -94,20 +92,19 @@ void clock_set_pll1(unsigned int clk)
> >  	       val |= CCM_PLL1_OUT_EN;
> >  	if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
> >  	       val |= CCM_PLL1_OUT_EN | CCM_PLL1_LDO_EN;
> > -	writel(val, &ccm->pll1_cfg);
> > -	while (!(readl(&ccm->pll1_cfg) & CCM_PLL1_LOCK)) {}
> > +	writel(val, ccm + CCU_H6_PLL1_CFG);
> > +	while (!(readl(ccm + CCU_H6_PLL1_CFG) & CCM_PLL1_LOCK)) {}
> >  
> >  	/* Switch CPU to PLL1 */
> > -	val = readl(&ccm->cpu_axi_cfg);
> > +	val = readl(ccm + CCU_H6_CPU_AXI_CFG);
> >  	val &= ~CCM_CPU_AXI_MUX_MASK;
> >  	val |= CCM_CPU_AXI_MUX_PLL_CPUX;
> > -	writel(val, &ccm->cpu_axi_cfg);
> > +	writel(val, ccm + CCU_H6_CPU_AXI_CFG);
> >  }
> >  
> >  int clock_twi_onoff(int port, int state)
> >  {
> > -	struct sunxi_ccm_reg *const ccm =
> > -		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> > +	void *const ccm = (void *)SUNXI_CCM_BASE;
> >  	struct sunxi_prcm_reg *const prcm =
> >  		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
> >  	u32 value, *ptr;
> > @@ -120,7 +117,7 @@ int clock_twi_onoff(int port, int state)
> >  		ptr = &prcm->twi_gate_reset;
> >  	} else {
> >  		shift = port;
> > -		ptr = &ccm->twi_gate_reset;
> > +		ptr = ccm + CCU_H6_I2C_GATE_RESET;
> >  	}
> >  
> >  	/* set the apb clock gate and reset for twi */
> > @@ -136,9 +133,8 @@ int clock_twi_onoff(int port, int state)
> >  /* PLL_PERIPH0 clock, used by the MMC driver */
> >  unsigned int clock_get_pll6(void)
> >  {
> > -	struct sunxi_ccm_reg *const ccm =
> > -		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
> > -	uint32_t rval = readl(&ccm->pll6_cfg);
> > +	void *const ccm = (void *)SUNXI_CCM_BASE;
> > +	uint32_t rval = readl(ccm + CCU_H6_PLL6_CFG);
> >  	int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT) + 1;
> >  	int div2 = ((rval & CCM_PLL6_CTRL_DIV2_MASK) >>
> >  		    CCM_PLL6_CTRL_DIV2_SHIFT) + 1;
> >   
> 
> 
> 
> 
> 


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 28/34] sunxi: armv8: FEL: save and restore SP_IRQ
  2025-03-23 12:26   ` Jernej Škrabec
@ 2025-03-23 23:52     ` Andre Przywara
  0 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 23:52 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Tom Rini, Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

On Sun, 23 Mar 2025 13:26:34 +0100
Jernej Škrabec <jernej.skrabec@gmail.com> wrote:

> Dne nedelja, 23. marec 2025 ob 12:35:38 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> > Thanks for Jernej's JTAG debugging effort, it turns out that the BROM
> > expects SP_IRQ to be saved and restored, when we want to enter back into
> > FEL after the SPL's AArch64 stint.
> > Save and restore SP_IRQ as part of the FEL state handling. The banked
> > MRS/MSR access to SP_IRQ, without actually being in IRQ mode, was
> > introduced with the ARMv7 virtualisation extensions. The Arm Cortex-A8
> > cores used in the A10/A13s or older F1C100s SoCs would not support that,
> > but this code here is purely in the ARMv8/AArch64 code path, so it's
> > safe to use unconditionally.
> > 
> > Reported-by: Jernej Skrabec <jernej.skrabec@gmail.com>
> > Signed-off-by: Andre Przywara <andre.przywara@arm.com>  
> 
> I have sneaky suspicion that this is already the issue on H616, but I
> haven't yet confirmed.

That's actually a very good point: if we reset the core, SP_IRQ should
be reset as well - on all 64-bit SoCs, so also A64, H5, H6 and H616. We
are surely not in an IRQ handler when entering the SPL, so the stack
*content* is not relevant, but it should still point to some writable
memory. So I keep scratching my head how this worked so far. If I find
some time, I will try to dump the SP_IRQ content after reset, on those
SoCs.

> FWIW:
> Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>

Thanks for that!

Cheers,
Andre

> 
> Best regards,
> Jernej
> 
> 
> 


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 13/34] sunxi: spl: add support for Allwinner A523 watchdog
  2025-03-23 12:15   ` Jernej Škrabec
@ 2025-03-23 23:57     ` Andre Przywara
  0 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-23 23:57 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Tom Rini, Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi

On Sun, 23 Mar 2025 13:15:46 +0100
Jernej Škrabec <jernej.skrabec@gmail.com> wrote:

Hi,

> Dne nedelja, 23. marec 2025 ob 12:35:23 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> > From: Jernej Skrabec <jernej.skrabec@gmail.com>
> > 
> > The watchdog in the Allwinner A523 SoC differs a bit from the one in the
> > previous SoCs: it lives in a separate register frame, so no longer
> > inside some timer device, and it manages to shuffle around some
> > registers a bit.
> > 
> > Provide a new struct describing the register layout, and adjust the
> > address calculation in the SPL code accoringly. This is guarded by the
> > MACH_SUN55I_A523 Kconfig variable.  
> 
> This one is missing SoB, apparently by me :)

Yes, I don't think it's appropriate to add your Signed-off-by, so if
you could give it here.

> 
> > ---
> >  arch/arm/include/asm/arch-sunxi/watchdog.h | 12 ++++++++++++
> >  arch/arm/mach-sunxi/board.c                |  6 ++++++
> >  2 files changed, 18 insertions(+)
> > 
> > diff --git a/arch/arm/include/asm/arch-sunxi/watchdog.h b/arch/arm/include/asm/arch-sunxi/watchdog.h
> > index 38e2ef2aca3..96d5725141e 100644
> > --- a/arch/arm/include/asm/arch-sunxi/watchdog.h
> > +++ b/arch/arm/include/asm/arch-sunxi/watchdog.h
> > @@ -26,6 +26,18 @@ struct sunxi_wdog {
> >  	u32 res[2];
> >  };
> >  
> > +#elif defined(CONFIG_MACH_SUN55I_A523)
> > +
> > +struct sunxi_wdog {
> > +	u32 irq_en;		/* 0x00 */
> > +	u32 irq_sta;		/* 0x04 */
> > +	u32 srst;		/* 0x08 */
> > +	u32 ctl;		/* 0x0c */
> > +	u32 cfg;		/* 0x10 */
> > +	u32 mode;		/* 0x14 */
> > +	u32 ocfg;		/* 0x18 */
> > +};  
> 
> Anyway, since you announced battle against C structures for register
> layout, it seems contraproductive to add it here. What do you think?

Well, I am all for it, but I guess that's another refactor patch first,
so please be my guest. I didn't deem this important enough to address
this now (it's not blocking as for the clocks), and this series is
already long enough, so I was just happy to take this working patch.
For development I really like having a working "reset" command ;-)

Cheers,
Andre

> 
> Best regards,
> Jernej
> 
> > +
> >  #else
> >  
> >  #define WDT_CFG_RESET		(0x1)
> > diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
> > index 701899ee4b2..89aea61e8e8 100644
> > --- a/arch/arm/mach-sunxi/board.c
> > +++ b/arch/arm/mach-sunxi/board.c
> > @@ -495,6 +495,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 struct sunxi_wdog *wdog =
> > +		(struct sunxi_wdog *)SUNXI_TIMER_BASE;
> > +
> > +	writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, &wdog->srst);
> > +	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 */
> >   
> 
> 
> 
> 
> 


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 16/34] clk: sunxi: Add support for the A523 -R CCU
  2025-03-23 12:18   ` Jernej Škrabec
@ 2025-03-24  0:37     ` Andre Przywara
  0 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-03-24  0:37 UTC (permalink / raw)
  To: Jernej Škrabec
  Cc: Tom Rini, Simon Glass, Mikhail Kalashnikov, u-boot, linux-sunxi,
	Lukasz Majewski, Sean Anderson

On Sun, 23 Mar 2025 13:18:17 +0100
Jernej Škrabec <jernej.skrabec@gmail.com> wrote:

Hi,

> Dne nedelja, 23. marec 2025 ob 12:35:26 Srednjeevropski standardni čas je Andre Przywara napisal(a):
> > Add a clock driver for the PRCM clock controller on the Allwinner A523
> > family of SoCs, often also used with an "r" prefix or suffix.
> > This just describes the clock gates and reset lines for the few devices
> > that we would need, most prominently the R_I2C device for the PMIC.
> > 
> > Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> > ---
> >  drivers/clk/sunxi/Kconfig      |  7 ++++++
> >  drivers/clk/sunxi/Makefile     |  1 +
> >  drivers/clk/sunxi/clk_a523_r.c | 44 ++++++++++++++++++++++++++++++++++
> >  drivers/clk/sunxi/clk_sunxi.c  |  5 ++++
> >  4 files changed, 57 insertions(+)
> >  create mode 100644 drivers/clk/sunxi/clk_a523_r.c
> > 
> > diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig
> > index 74e89b86301..1c1cc82719c 100644
> > --- a/drivers/clk/sunxi/Kconfig
> > +++ b/drivers/clk/sunxi/Kconfig
> > @@ -136,4 +136,11 @@ config CLK_SUN55I_A523
> >  	  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 dd33eabe2ed..93b542cebcd 100644
> > --- a/drivers/clk/sunxi/Makefile
> > +++ b/drivers/clk/sunxi/Makefile
> > @@ -26,3 +26,4 @@ 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_r.c b/drivers/clk/sunxi/clk_a523_r.c
> > new file mode 100644
> > index 00000000000..e864ce16199
> > --- /dev/null
> > +++ b/drivers/clk/sunxi/clk_a523_r.c
> > @@ -0,0 +1,44 @@
> > +// 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_TIMER]       = RESET(0x11c, BIT(16)),
> > +	[RST_BUS_R_TWD]         = RESET(0x12c, BIT(16)),
> > +//	[RST_BUS_R_PWMCTRL]     = RESET(0x13c, BIT(16)),
> > +//	[RST_BUS_R_SPI]         = RESET(0x15c, BIT(16)),
> > +//	[RST_BUS_R_UART0]       = RESET(0x18c, BIT(16)),
> > +//	[RST_BUS_R_UART1]       = RESET(0x18c, BIT(17)),
> > +	[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)),
> > +//	[RST_BUS_R_CPUCFG]      = RESET(0x22c, BIT(16)),  
> 
> Any specific reason that you commented out some reset lines?

Ah yeah, looks like a leftover from development, more for documentation
purposes. In general we just mention lines that we actually need, and
that's not much from the PRCM CCU. I will just remove those lines,
shall we need, it's easy to bring them back.

Cheers,
Andre

> 
> Best regards,
> Jernej
> 
> > +};
> > +
> > +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 30baabaafcd..842a0541bd6 100644
> > --- a/drivers/clk/sunxi/clk_sunxi.c
> > +++ b/drivers/clk/sunxi/clk_sunxi.c
> > @@ -127,6 +127,7 @@ 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
> > @@ -228,6 +229,10 @@ static const struct udevice_id sunxi_clk_ids[] = {
> >  #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
> >  	{ }
> >  };
> >   
> 
> 
> 
> 


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 19/34] watchdog: sunxi: add A523 support
  2025-03-23 11:35 ` [PATCH 19/34] watchdog: sunxi: add A523 support Andre Przywara
@ 2025-03-24  8:38   ` Stefan Roese
  0 siblings, 0 replies; 59+ messages in thread
From: Stefan Roese @ 2025-03-24  8:38 UTC (permalink / raw)
  To: Andre Przywara, Tom Rini
  Cc: Simon Glass, Jernej Skrabec, Mikhail Kalashnikov, u-boot,
	linux-sunxi

On 23.03.25 12:35, Andre Przywara wrote:
> The Allwinner A523 SoC moved the watchdog into a separate MMIO frame,
> and also shifted the registers a bit: the control, config, and mode
> register are located four bytes earlier.
> 
> Add the new compatible string, and connect it to the new struct
> describing the new register layout.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Reviewed-by: Stefan Roese <sr@denx.de>

Thanks,
Stefan

> ---
>   drivers/watchdog/sunxi_wdt.c | 11 +++++++++++
>   1 file changed, 11 insertions(+)
> 
> diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c
> index 8eeac935760..467db5fe9bf 100644
> --- a/drivers/watchdog/sunxi_wdt.c
> +++ b/drivers/watchdog/sunxi_wdt.c
> @@ -153,10 +153,21 @@ static const struct sunxi_wdt_reg sun20i_wdt_reg = {
>   	.wdt_key_val		= 0x16aa0000,
>   };
>   
> +static const struct sunxi_wdt_reg sun55i_wdt_reg = {
> +	.wdt_ctrl		= 0x0c,
> +	.wdt_cfg		= 0x10,
> +	.wdt_mode		= 0x14,
> +	.wdt_timeout_shift	= 4,
> +	.wdt_reset_mask		= 0x03,
> +	.wdt_reset_val		= 0x01,
> +	.wdt_key_val		= 0x16aa0000,
> +};
> +
>   static const struct udevice_id sunxi_wdt_ids[] = {
>   	{ .compatible = "allwinner,sun4i-a10-wdt", .data = (ulong)&sun4i_wdt_reg },
>   	{ .compatible = "allwinner,sun6i-a31-wdt", .data = (ulong)&sun6i_wdt_reg },
>   	{ .compatible = "allwinner,sun20i-d1-wdt", .data = (ulong)&sun20i_wdt_reg },
> +	{ .compatible = "allwinner,sun55i-a523-wdt", .data = (ulong)&sun55i_wdt_reg },
>   	{ /* sentinel */ }
>   };
>   

Viele Grüße,
Stefan Roese

-- 
DENX Software Engineering GmbH,      Managing Director: Erika Unter
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr@denx.de


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support
  2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
                   ` (33 preceding siblings ...)
  2025-03-23 11:35 ` [PATCH 34/34] sunxi: A523: add defconfigs for three boards Andre Przywara
@ 2025-04-05  2:44 ` Yixun Lan
  2025-04-05 12:32   ` Andre Przywara
  34 siblings, 1 reply; 59+ messages in thread
From: Yixun Lan @ 2025-04-05  2:44 UTC (permalink / raw)
  To: Andre Przywara
  Cc: Tom Rini, Simon Glass, Jernej Skrabec, Mikhail Kalashnikov,
	u-boot, linux-sunxi

hi Andre:

On 11:35 Sun 23 Mar     , Andre Przywara wrote:
> This series introduces support for the Allwinner A523 SoC family. The
> same die is used in different packages: the A523, A527, T527, and H728:
> they connect a different set of peripherals to the pins, or enable extra
> goodies like an NPU. From a U-Boot perspective those chips do not differ
> much, all the differences are described in the board DT files.
> These patches are not the most refined at the moment, but I hope that
> people start reviewing them, so we can merge the ones that are ready,
> to reduce their number.
> 
> To be able to share the SPL clock code, the existing H6 code gets
> refactored in patches 01-13. This removes the C struct describing the
> 127 clock registers, and replaces it with macros defining the register
> offsets. For more rationale and explanation see the 01/34 commit message.
> 
> Patches 14-20 extend the existing Allwinner U-Boot drivers to cope with
> some of the changed peripherals, this includes the mandatory clock and
> pinctrl drivers, but also some clock tweaks for the MMC controller
> driver, and support for the new watchdog and the AXP323 used.
> 
> Patches 21-23 update some SPL bits to be able to cope with the A523.
> Patches 24-28 extend the FEL handling code: the A523 has a GICv3, which
> requires saving some GICv3 system registers, plus the IRQ mode stack
> pointer. Patches 25-27 refactor the CPU clock code, and add the new
> clock bits required by the A523.
> 
> Patch 29-32 add the new SPL bits for the A523, most prominently the DRAM
> initialisation code. Many thanks to Jernej and Mikhail for providing
> this part, there is a great reverse engineering and testing effort behind
> this.
> 
> Patch 33 copies the DT files from the proposed Linux patches. They have
> not been merged yet, mostly due to one missing DT binding dependency in
> the linux-next tree, but have otherwise been agreed upon, with almost
> every used binding being already merged. Eventually we will use the copy
> from the DT rebasing repo, but for now those files must do.
> 
> The final patch adds defconfig files for the three boards that seem to be
> the most popular at the moment, they include two development boards and
> one TV box. The most interesting bits in there are the DRAM parameters.
> 
> Please have a look, review, and test.
> 
> Thanks!
> Andre
> 


Is there any docs about how to compile and run this patch series?
I've got some complaint while apply to v2025.04-rc5 (not found correct base myself)

I'm following doc/board/allwinner/sunxi.rst to build and using sunxi-tools to run,
but it seems stuck at running uboot (should pass SPL stage)

here are steps I tried

1) build trust firmware
$ git clone https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git
$ make CROSS_COMPILE=aarch64-unknown-linux-gnu- PLAT=sun50i_a64
is sun50i_a64 correct for a527 SoC? besides, got a oversize error if DEBUG=1 enabled

2) build SCP

3) build uboot
$ export BL31=/home/work/trusted-firmware-a/build/sun50i_a64/release/bl31.bin  
export SCP..
$ make ARCH=arm CROSS_COMPILE=aarch64-unknown-linux-gnu- -j30 radxa-a5e_defconfig
$ make ARCH=arm CROSS_COMPILE=aarch64-unknown-linux-gnu- -j30

4) run:  sunxi-fel -v uboot u-boot-sunxi-with-spl.bin
 4.a) serial console from a527 board

U-Boot SPL 2025.04-rc5-00048-g432f0f975187 (Apr 05 2025 - 10:21:15 +0800)
DRAM: 4096 MiB
Trying to boot from FEL

 4.b) from host which run sunxi-fel

 $ sunxi-fel -v uboot u-boot-sunxi-with-spl.bin
found DT name in SPL header: sun55i-a527-radxa-a5e
Stack pointers: sp_irq=0x00045400, sp=0x00060300
MMU is not enabled by BROM
=> Executing the SPL... done.
loading image "ARM Trusted Firmware" (37076 bytes) to 0x54000
loading image "U-Boot" (657216 bytes) to 0x4a000000
loading DTB "sun55i-a527-radxa-a5e" (19024 bytes)
Starting U-Boot (0x00054000).
Store entry point 0x00054000 to RVBAR 0x08000040, and request warm reset with RMR mode 3... done.

is ATF loading address correct, 0x54000 here? which seems override sp address?

thanks
-- 
Yixun Lan (dlan)
Gentoo Linux Developer
GPG Key ID AABEFD55

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support
  2025-04-05  2:44 ` [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Yixun Lan
@ 2025-04-05 12:32   ` Andre Przywara
  2025-04-05 13:04     ` Yixun Lan
  0 siblings, 1 reply; 59+ messages in thread
From: Andre Przywara @ 2025-04-05 12:32 UTC (permalink / raw)
  To: Yixun Lan
  Cc: Tom Rini, Simon Glass, Jernej Skrabec, Mikhail Kalashnikov,
	u-boot, linux-sunxi

On Sat, 5 Apr 2025 02:44:33 +0000
Yixun Lan <dlan@gentoo.org> wrote:

Hi,

> On 11:35 Sun 23 Mar     , Andre Przywara wrote:
> > This series introduces support for the Allwinner A523 SoC family. The
> > same die is used in different packages: the A523, A527, T527, and H728:
> > they connect a different set of peripherals to the pins, or enable extra
> > goodies like an NPU. From a U-Boot perspective those chips do not differ
> > much, all the differences are described in the board DT files.
> > These patches are not the most refined at the moment, but I hope that
> > people start reviewing them, so we can merge the ones that are ready,
> > to reduce their number.
> > 
> > To be able to share the SPL clock code, the existing H6 code gets
> > refactored in patches 01-13. This removes the C struct describing the
> > 127 clock registers, and replaces it with macros defining the register
> > offsets. For more rationale and explanation see the 01/34 commit message.
> > 
> > Patches 14-20 extend the existing Allwinner U-Boot drivers to cope with
> > some of the changed peripherals, this includes the mandatory clock and
> > pinctrl drivers, but also some clock tweaks for the MMC controller
> > driver, and support for the new watchdog and the AXP323 used.
> > 
> > Patches 21-23 update some SPL bits to be able to cope with the A523.
> > Patches 24-28 extend the FEL handling code: the A523 has a GICv3, which
> > requires saving some GICv3 system registers, plus the IRQ mode stack
> > pointer. Patches 25-27 refactor the CPU clock code, and add the new
> > clock bits required by the A523.
> > 
> > Patch 29-32 add the new SPL bits for the A523, most prominently the DRAM
> > initialisation code. Many thanks to Jernej and Mikhail for providing
> > this part, there is a great reverse engineering and testing effort behind
> > this.
> > 
> > Patch 33 copies the DT files from the proposed Linux patches. They have
> > not been merged yet, mostly due to one missing DT binding dependency in
> > the linux-next tree, but have otherwise been agreed upon, with almost
> > every used binding being already merged. Eventually we will use the copy
> > from the DT rebasing repo, but for now those files must do.
> > 
> > The final patch adds defconfig files for the three boards that seem to be
> > the most popular at the moment, they include two development boards and
> > one TV box. The most interesting bits in there are the DRAM parameters.
> > 
> > Please have a look, review, and test.
> > 
> > Thanks!
> > Andre
> >   
> 
> 
> Is there any docs about how to compile and run this patch series?
> I've got some complaint while apply to v2025.04-rc5 (not found correct base myself)

Yes, it's pretty much work in progress, I just wanted to get the
patches out on the list, to start the review process.
You should be able to base it on "sunxi-next" or U-Boot's main next
branch.

> 
> I'm following doc/board/allwinner/sunxi.rst to build and using sunxi-tools to run,
> but it seems stuck at running uboot (should pass SPL stage)
> 
> here are steps I tried
> 
> 1) build trust firmware
> $ git clone https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git
> $ make CROSS_COMPILE=aarch64-unknown-linux-gnu- PLAT=sun50i_a64
> is sun50i_a64 correct for a527 SoC?

No, A64 will not work, as you figured ;-)
Jernej has a WIP branch for TF-A here:
https://github.com/jernejsk/arm-trusted-firmware/commits/a523/
Please use this and the "sun55i_a523" build target.

> besides, got a oversize error if DEBUG=1 enabled

Yes, this is a known issue, the available secure SRAM on the A64 is
just too small for all the features. Should not be a problem on other
SoCs including the A523/T527, though.

> 
> 2) build SCP

SCP is highly optional, and there is no port available anyway.
I would not know of someone working on it as well.

> 
> 3) build uboot
> $ export BL31=/home/work/trusted-firmware-a/build/sun50i_a64/release/bl31.bin  
> export SCP..
> $ make ARCH=arm CROSS_COMPILE=aarch64-unknown-linux-gnu- -j30 radxa-a5e_defconfig
> $ make ARCH=arm CROSS_COMPILE=aarch64-unknown-linux-gnu- -j30

Just use .../build/sun55i_a523/debug/bl31.bin, as mentioned above.

> 
> 4) run:  sunxi-fel -v uboot u-boot-sunxi-with-spl.bin
>  4.a) serial console from a527 board
> 
> U-Boot SPL 2025.04-rc5-00048-g432f0f975187 (Apr 05 2025 - 10:21:15 +0800)
> DRAM: 4096 MiB
> Trying to boot from FEL

Yes, this is the effect when TF-A is built for the wrong target,
normally you would see TF-A messages here.
 
>  4.b) from host which run sunxi-fel
> 
>  $ sunxi-fel -v uboot u-boot-sunxi-with-spl.bin
> found DT name in SPL header: sun55i-a527-radxa-a5e
> Stack pointers: sp_irq=0x00045400, sp=0x00060300
> MMU is not enabled by BROM
> => Executing the SPL... done.  
> loading image "ARM Trusted Firmware" (37076 bytes) to 0x54000
> loading image "U-Boot" (657216 bytes) to 0x4a000000
> loading DTB "sun55i-a527-radxa-a5e" (19024 bytes)
> Starting U-Boot (0x00054000).
> Store entry point 0x00054000 to RVBAR 0x08000040, and request warm reset with RMR mode 3... done.
> 
> is ATF loading address correct, 0x54000 here? which seems override sp address?

Yes, that's the correct address. Which SP would it collide with, anyway?

So in summary you did everything right, and the SPL is returning to FEL
with DRAM initialised, which is the biggest hurdle.
Just use the correct A523 TF-A branch and build target.

Cheers,
Andre.

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support
  2025-04-05 12:32   ` Andre Przywara
@ 2025-04-05 13:04     ` Yixun Lan
  0 siblings, 0 replies; 59+ messages in thread
From: Yixun Lan @ 2025-04-05 13:04 UTC (permalink / raw)
  To: Andre Przywara
  Cc: Tom Rini, Simon Glass, Jernej Skrabec, Mikhail Kalashnikov,
	u-boot, linux-sunxi

Hi Andre:

On 13:32 Sat 05 Apr     , Andre Przywara wrote:
> On Sat, 5 Apr 2025 02:44:33 +0000
> Yixun Lan <dlan@gentoo.org> wrote:
> 
> Hi,
> 
> > On 11:35 Sun 23 Mar     , Andre Przywara wrote:
> > > This series introduces support for the Allwinner A523 SoC family. The
> > > same die is used in different packages: the A523, A527, T527, and H728:
> > > they connect a different set of peripherals to the pins, or enable extra
> > > goodies like an NPU. From a U-Boot perspective those chips do not differ
> > > much, all the differences are described in the board DT files.
> > > These patches are not the most refined at the moment, but I hope that
> > > people start reviewing them, so we can merge the ones that are ready,
> > > to reduce their number.
...
> > is ATF loading address correct, 0x54000 here? which seems override sp address?
> 
> Yes, that's the correct address. Which SP would it collide with, anyway?
> 
> So in summary you did everything right, and the SPL is returning to FEL
> with DRAM initialised, which is the biggest hurdle.
> Just use the correct A523 TF-A branch and build target.
>
right, with correct ATF, I'm able to boot into uboot console
also, emmc works on radxa a5e board (same change with avaota-a1)..

thanks
-- 
Yixun Lan (dlan)
Gentoo Linux Developer
GPG Key ID AABEFD55

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 30/34] sunxi: A523: add DRAM initialisation routine
  2025-03-23 11:35 ` [PATCH 30/34] sunxi: A523: add DRAM initialisation routine Andre Przywara
  2025-03-23 13:15   ` Jernej Škrabec
@ 2025-04-05 22:01   ` Yixun Lan
  2025-04-07  9:26     ` Andre Przywara
  1 sibling, 1 reply; 59+ messages in thread
From: Yixun Lan @ 2025-04-05 22:01 UTC (permalink / raw)
  To: Andre Przywara
  Cc: Tom Rini, Simon Glass, Jernej Skrabec, Mikhail Kalashnikov,
	u-boot, linux-sunxi

Hi Andre:

On 11:35 Sun 23 Mar     , Andre Przywara wrote:
> From: Jernej Skrabec <jernej.skrabec@gmail.com>
> 
> DRAM init code, as per reverse engineering and matching against
> previous SoCs.
> Supports LPDDR4 for now only.
> ---
>  arch/arm/include/asm/arch-sunxi/dram.h        |    2 +
>  .../include/asm/arch-sunxi/dram_sun55i_a523.h |  183 ++
>  arch/arm/mach-sunxi/Kconfig                   |   31 +-
>  arch/arm/mach-sunxi/Makefile                  |    2 +
>  arch/arm/mach-sunxi/dram_sun55i_a523.c        | 1468 +++++++++++++++++
>  arch/arm/mach-sunxi/dram_timings/Makefile     |    1 +
>  .../arm/mach-sunxi/dram_timings/a523_lpddr4.c |  119 ++
>  7 files changed, 1804 insertions(+), 2 deletions(-)
>  create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
>  create mode 100644 arch/arm/mach-sunxi/dram_sun55i_a523.c
>  create mode 100644 arch/arm/mach-sunxi/dram_timings/a523_lpddr4.c
> 
> diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h
> index 9d21b492418..9676c747442 100644
> --- a/arch/arm/include/asm/arch-sunxi/dram.h
> +++ b/arch/arm/include/asm/arch-sunxi/dram.h
> @@ -33,6 +33,8 @@
>  #include <asm/arch/dram_sun50i_h616.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..24e487b6880
> --- /dev/null
> +++ b/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
> @@ -0,0 +1,183 @@
> +/*
> + * t527 dram controller register and constant defines
> + *
> + * (C) Copyright 2024  Jernej Skrabec <jernej.skrabec@gmail.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#ifndef _SUNXI_DRAM_SUN55I_A523_H
> +#define _SUNXI_DRAM_SUN55I_A523_H
> +
> +#include <stdbool.h>
> +#ifndef __ASSEMBLY__
> +#include <linux/bitops.h>
> +#endif
> +
> +enum sunxi_dram_type {
> +	SUNXI_DRAM_TYPE_DDR3 = 3,
> +	SUNXI_DRAM_TYPE_DDR4,
> +	SUNXI_DRAM_TYPE_LPDDR3 = 7,
> +	SUNXI_DRAM_TYPE_LPDDR4
> +};
> +
> +/* MBUS part is largely the same as in H6, except for one special register */
> +struct sunxi_mctl_com_reg {
> +	u32 cr;			/* 0x000 control register */
> +	u8 reserved_0x004[4];	/* 0x004 */
> +	u32 unk_0x008;		/* 0x008 */
> +	u32 tmr;		/* 0x00c timer register */
> +	u8 reserved_0x010[4];	/* 0x010 */
> +	u32 unk_0x014;		/* 0x014 */
> +	u8 reserved_0x018[8];	/* 0x018 */
> +	u32 unk_0x020;		/* 0x020 */
> +	u32 unk_0x024;		/* 0x024 */
> +	u32 unk_0x028;		/* 0x028 */
> +	u8 reserved_0x02c[468];	/* 0x02c */
> +	u32 bwcr;		/* 0x200 bandwidth control register */
> +	u8 reserved_0x204[12];	/* 0x204 */
> +	/*
> +	 * The last master configured by BSP libdram is at 0x49x, so the
> +	 * size of this struct array is set to 41 (0x29) now.
> +	 */
> +	struct {
> +		u32 cfg0;		/* 0x0 */
> +		u32 cfg1;		/* 0x4 */
> +		u8 reserved_0x8[8];	/* 0x8 */
> +	} master[41];		/* 0x210 + index * 0x10 */
> +	u8 reserved_0x4a0[96];	/* 0x4a0 */
> +	u32 unk_0x500;		/* 0x500 */
> +};
> +check_member(sunxi_mctl_com_reg, unk_0x500, 0x500);
> +
> +/*
> + * 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];	/* 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[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/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> index ab432390d3c..aeab2b3f6dd 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -51,7 +51,12 @@ config DRAM_SUN50I_H616
>  	  Select this dram controller driver for some sun50i platforms,
>  	  like H616.
>  
> -if DRAM_SUN50I_H616
> +config DRAM_SUN55I_A523
> +	bool
> +	help
> +	  Select this DRAM controller driver for A523/T527 SoCs.
> +
> +if DRAM_SUN50I_H616 || DRAM_SUN55I_A523
>  config DRAM_SUNXI_DX_ODT
>  	hex "DRAM DX ODT parameter"
>  	help
> @@ -79,6 +84,12 @@ config DRAM_SUNXI_TPR0
>  	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
> @@ -109,6 +120,13 @@ config DRAM_SUNXI_TPR12
>  	help
>  	  TPR12 value from vendor DRAM settings.
>  
> +config DRAM_SUNXI_TPR14
> +	hex "DRAM TPR14 parameter"
> +	depends on MACH_SUN55I_A523
> +	default 0x48484848
> +	help
> +	  TPR14 value from vendor DRAM settings.
> +
>  choice
>  	prompt "DRAM PHY pin mapping selection"
>  	default DRAM_SUNXI_PHY_ADDR_MAP_0
> @@ -497,7 +515,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_SUN55I_A523
>  config SUNXI_DRAM_DDR3
>  	bool
>  
> @@ -512,6 +530,7 @@ config SUNXI_DRAM_LPDDR4
>  
>  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
>  
> @@ -577,6 +596,13 @@ 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_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
>  
> @@ -597,6 +623,7 @@ config DRAM_CLK
>  	default 672 if MACH_SUN50I
>  	default 744 if MACH_SUN50I_H6
>  	default 720 if MACH_SUN50I_H616
> +	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
> diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
> index eb6a49119a1..3521335dbbf 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
>  obj-$(CONFIG_DRAM_SUN50I_H6)	+= dram_timings/
>  obj-$(CONFIG_DRAM_SUN50I_H616)	+= dram_sun50i_h616.o
>  obj-$(CONFIG_DRAM_SUN50I_H616)	+= 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/dram_sun55i_a523.c b/arch/arm/mach-sunxi/dram_sun55i_a523.c
> new file mode 100644
> index 00000000000..fae02062547
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/dram_sun55i_a523.c
> @@ -0,0 +1,1468 @@
> +// 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>
> +
...snip
> +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;
> +	}
on radxa a5e, I've got occasionally wrong dram size, roughly 2/10 rate
in the wrong case it got 8192M, while actually should be 4096M..

spent a few time to debug, found it got config->cols = 11 while should be 10
and above for loop has been skipped, thus fail to detect correct cols value..

-- 
Yixun Lan (dlan)
Gentoo Linux Developer
GPG Key ID AABEFD55

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 30/34] sunxi: A523: add DRAM initialisation routine
  2025-04-05 22:01   ` Yixun Lan
@ 2025-04-07  9:26     ` Andre Przywara
  0 siblings, 0 replies; 59+ messages in thread
From: Andre Przywara @ 2025-04-07  9:26 UTC (permalink / raw)
  To: Yixun Lan
  Cc: Tom Rini, Simon Glass, Jernej Skrabec, Mikhail Kalashnikov,
	u-boot, linux-sunxi

On Sat, 5 Apr 2025 22:01:50 +0000
Yixun Lan <dlan@gentoo.org> wrote:

Hi,

> Hi Andre:
> 
> On 11:35 Sun 23 Mar     , Andre Przywara wrote:
> > From: Jernej Skrabec <jernej.skrabec@gmail.com>
> > 
> > DRAM init code, as per reverse engineering and matching against
> > previous SoCs.
> > Supports LPDDR4 for now only.
> > ---
> >  arch/arm/include/asm/arch-sunxi/dram.h        |    2 +
> >  .../include/asm/arch-sunxi/dram_sun55i_a523.h |  183 ++
> >  arch/arm/mach-sunxi/Kconfig                   |   31 +-
> >  arch/arm/mach-sunxi/Makefile                  |    2 +
> >  arch/arm/mach-sunxi/dram_sun55i_a523.c        | 1468 +++++++++++++++++
> >  arch/arm/mach-sunxi/dram_timings/Makefile     |    1 +
> >  .../arm/mach-sunxi/dram_timings/a523_lpddr4.c |  119 ++
> >  7 files changed, 1804 insertions(+), 2 deletions(-)
> >  create mode 100644 arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
> >  create mode 100644 arch/arm/mach-sunxi/dram_sun55i_a523.c
> >  create mode 100644 arch/arm/mach-sunxi/dram_timings/a523_lpddr4.c
> > 
[ ... ]

> > 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..fae02062547
> > --- /dev/null
> > +++ b/arch/arm/mach-sunxi/dram_sun55i_a523.c
> > @@ -0,0 +1,1468 @@
> > +// 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>
> > +  
> ...snip
> > +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;
> > +	}  
> on radxa a5e, I've got occasionally wrong dram size, roughly 2/10 rate
> in the wrong case it got 8192M, while actually should be 4096M..
> 
> spent a few time to debug, found it got config->cols = 11 while should be 10
> and above for loop has been skipped, thus fail to detect correct cols value..

Ah yes, we were already suspecting that. We had those "double detection"
issues on H616 for a while, and Jernej fixed them there recently:
https://lore.kernel.org/u-boot/20250309063143.62859-1-jernej.skrabec@gmail.com/T/#u

So we were already thinking of folding a similar fix into this (and
other SoCs') DRAM code, ideally by sharing some code.

Thanks for the test and the report!

Cheers,
Andre

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH 33/34] sunxi: A523: add DT files from Linux v3 branch
  2025-03-23 11:35 ` [PATCH 33/34] sunxi: A523: add DT files from Linux v3 branch Andre Przywara
@ 2025-04-09 14:28   ` Yixun Lan
  0 siblings, 0 replies; 59+ messages in thread
From: Yixun Lan @ 2025-04-09 14:28 UTC (permalink / raw)
  To: Andre Przywara
  Cc: Tom Rini, Simon Glass, Jernej Skrabec, Mikhail Kalashnikov,
	u-boot, linux-sunxi

Hi Andre,

On 11:35 Sun 23 Mar     , Andre Przywara wrote:
> This imports the (preliminary) devicetree files for the boards with the
> new Allwinner A523/T527/H728 SoCs, including the basic SoC .dtsi.
> 
> Those files have been reviewed and confirmed, but have not been merged
> into the official kernel repositories yet.
> 
> Pending upstream kernel repository:
> https://github.com/apritzel/linux/commits/a523-v3/
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  arch/arm/dts/sun55i-a523.dtsi          | 598 +++++++++++++++++++++++++
>  arch/arm/dts/sun55i-a527-radxa-a5e.dts | 299 +++++++++++++
>  arch/arm/dts/sun55i-h728-x96qpro+.dts  | 287 ++++++++++++
>  arch/arm/dts/sun55i-t527-avaota-a1.dts | 308 +++++++++++++
>  4 files changed, 1492 insertions(+)
>  create mode 100644 arch/arm/dts/sun55i-a523.dtsi
>  create mode 100644 arch/arm/dts/sun55i-a527-radxa-a5e.dts
>  create mode 100644 arch/arm/dts/sun55i-h728-x96qpro+.dts
>  create mode 100644 arch/arm/dts/sun55i-t527-avaota-a1.dts
> 
> diff --git a/arch/arm/dts/sun55i-a523.dtsi b/arch/arm/dts/sun55i-a523.dtsi
> new file mode 100644
> index 00000000000..ee485899ba0
> --- /dev/null
> +++ b/arch/arm/dts/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>
> +
> +
[...]
> +		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>;

U-Boot 2025.04-rc5-00056-g1ef486ead58d (Apr 09 2025 - 22:06:21 +0800) Allwinner Technology

CPU:   Allwinner A523 (SUN55I)
Model: Radxa A5E
DRAM:  4 GiB
sunxi_set_gate: (CLK#35) unhandled
Core:  77 devices, 21 uclasses, devicetree: separate
..


I've got a "CLK unhandled" err, checked and found CLK_PLL_AUDIO0_4X is not
implemtend in clk driver - drivers/clk/sunxi/clk_a523.c

do you have any idea why this clk gate not implemented?
or somehow, I guess we could drop it from uboot if not used
(or no need to keep sync with kernel dts? I did no further check)

-- 
Yixun Lan (dlan)
Gentoo Linux Developer
GPG Key ID AABEFD55

^ permalink raw reply	[flat|nested] 59+ messages in thread

end of thread, other threads:[~2025-04-09 14:29 UTC | newest]

Thread overview: 59+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-23 11:35 [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Andre Przywara
2025-03-23 11:35 ` [PATCH 01/34] sunxi: clock: H6: drop usage of struct sunxi_ccm_reg Andre Przywara
2025-03-23 11:56   ` Jernej Škrabec
2025-03-23 23:50     ` Andre Przywara
2025-03-23 11:35 ` [PATCH 02/34] sunxi: mmc: remove " Andre Przywara
2025-03-23 12:04   ` Jernej Škrabec
2025-03-23 11:35 ` [PATCH 03/34] sunxi: H616: dram: " Andre Przywara
2025-03-23 11:35 ` [PATCH 04/34] sunxi: H6: " Andre Przywara
2025-03-23 11:35 ` [PATCH 05/34] sunxi: clock: H6: remove " Andre Przywara
2025-03-23 11:35 ` [PATCH 06/34] sunxi: clock: H6: drop usage of struct sunxi_prcm_reg Andre Przywara
2025-03-23 11:35 ` [PATCH 07/34] sunxi: H6/H616: dram: remove " Andre Przywara
2025-03-23 11:35 ` [PATCH 08/34] sunxi: clock: H6: remove " Andre Przywara
2025-03-23 11:35 ` [PATCH 09/34] sunxi: clock: H6: unify PLL control bit definitions Andre Przywara
2025-03-23 11:35 ` [PATCH 10/34] sunxi: clock: H6: factor out clock_set_pll() Andre Przywara
2025-03-23 11:35 ` [PATCH 11/34] sunxi: clock: H6: factor out H6/H616 CPU clock setup Andre Przywara
2025-03-23 11:35 ` [PATCH 12/34] sunxi: clock: H6: add A523 CPU PLL support Andre Przywara
2025-03-23 11:35 ` [PATCH 13/34] sunxi: spl: add support for Allwinner A523 watchdog Andre Przywara
2025-03-23 12:15   ` Jernej Škrabec
2025-03-23 23:57     ` Andre Przywara
2025-03-23 11:35 ` [PATCH 14/34] dt-bindings: add Allwinner A523 CCU bindings Andre Przywara
2025-03-23 11:35 ` [PATCH 15/34] clk: sunxi: Add support for the A523 CCU Andre Przywara
2025-03-23 11:35 ` [PATCH 16/34] clk: sunxi: Add support for the A523 -R CCU Andre Przywara
2025-03-23 12:18   ` Jernej Škrabec
2025-03-24  0:37     ` Andre Przywara
2025-03-23 11:35 ` [PATCH 17/34] pinctrl: sunxi: add Allwinner A523 pinctrl description Andre Przywara
2025-03-23 11:35 ` [PATCH 18/34] sunxi: mmc: add support for Allwinner A523 MMC mod clock Andre Przywara
2025-03-23 11:35 ` [PATCH 19/34] watchdog: sunxi: add A523 support Andre Przywara
2025-03-24  8:38   ` Stefan Roese
2025-03-23 11:35 ` [PATCH 20/34] power: regulator: add AXP323 support Andre Przywara
2025-03-23 11:35 ` [PATCH 21/34] sunxi: update cpu_sunxi_ncat2.h Andre Przywara
2025-03-23 11:35 ` [PATCH 22/34] sunxi: Kconfig: consolidate SYS_CLK_FREQ selection Andre Przywara
2025-03-23 12:21   ` Jernej Škrabec
2025-03-23 11:35 ` [PATCH 23/34] spl: reorder SPL_MAX_SIZE defaults for sunxi Andre Przywara
2025-03-23 12:22   ` Jernej Škrabec
2025-03-23 11:35 ` [PATCH 24/34] sunxi: armv8: fel: move fel_stash variable to the front Andre Przywara
2025-03-23 12:23   ` Jernej Škrabec
2025-03-23 11:35 ` [PATCH 25/34] sunxi: arm64: boot0.h: move fel_stash_addr " Andre Przywara
2025-03-23 12:23   ` Jernej Škrabec
2025-03-23 11:35 ` [PATCH 26/34] sunxi: update rmr_switch.S source code Andre Przywara
2025-03-23 12:24   ` Jernej Škrabec
2025-03-23 11:35 ` [PATCH 27/34] sunxi: armv8: FEL: save and restore GICv3 registers Andre Przywara
2025-03-23 12:25   ` Jernej Škrabec
2025-03-23 11:35 ` [PATCH 28/34] sunxi: armv8: FEL: save and restore SP_IRQ Andre Przywara
2025-03-23 12:26   ` Jernej Škrabec
2025-03-23 23:52     ` Andre Przywara
2025-03-23 11:35 ` [PATCH 29/34] sunxi: sun50i_h6: add A523 SPL clock setup code Andre Przywara
2025-03-23 12:36   ` Jernej Škrabec
2025-03-23 11:35 ` [PATCH 30/34] sunxi: A523: add DRAM initialisation routine Andre Przywara
2025-03-23 13:15   ` Jernej Škrabec
2025-04-05 22:01   ` Yixun Lan
2025-04-07  9:26     ` Andre Przywara
2025-03-23 11:35 ` [PATCH 31/34] sunxi: A523: add DDR3 DRAM support Andre Przywara
2025-03-23 11:35 ` [PATCH 32/34] sunxi: add basic A523 support Andre Przywara
2025-03-23 11:35 ` [PATCH 33/34] sunxi: A523: add DT files from Linux v3 branch Andre Przywara
2025-04-09 14:28   ` Yixun Lan
2025-03-23 11:35 ` [PATCH 34/34] sunxi: A523: add defconfigs for three boards Andre Przywara
2025-04-05  2:44 ` [PATCH 00/34] sunxi: clock refactoring and Allwinner A523 support Yixun Lan
2025-04-05 12:32   ` Andre Przywara
2025-04-05 13:04     ` Yixun Lan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox