* [PATCH v2 01/20] sunxi: clock: H6: unify PLL control bit definitions
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 02/20] sunxi: clock: H6: factor out clock_set_pll() Andre Przywara
` (19 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 | 13 +++++++-----
arch/arm/mach-sunxi/dram_sun50i_a133.c | 10 +++++-----
arch/arm/mach-sunxi/dram_sun50i_h6.c | 6 +++---
arch/arm/mach-sunxi/dram_sun50i_h616.c | 6 +++---
5 files changed, 26 insertions(+), 29 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 575dff68804..37a620c9866 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 3f375a51965..2f2fd5df93d 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0+
+
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clock.h>
@@ -31,7 +33,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,15 +87,16 @@ 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) ||
IS_ENABLED(CONFIG_MACH_SUN50I_A133))
- 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_a133.c b/arch/arm/mach-sunxi/dram_sun50i_a133.c
index a0fca3738f4..3a231141168 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_a133.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_a133.c
@@ -78,19 +78,19 @@ static void mctl_clk_init(u32 clk)
clrbits_le32(ccm + CCU_H6_MBUS_CFG, MBUS_RESET);
clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(GATE_SHIFT));
clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(RESET_SHIFT));
- clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL5_CTRL_EN);
+ clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL_CTRL_EN);
clrbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, DRAM_MOD_RESET);
udelay(5);
/* Set up PLL5 clock, used for DRAM */
clrsetbits_le32(ccm + CCU_H6_PLL5_CFG, 0xff03,
- CCM_PLL5_CTRL_N((clk * 2) / 24) | CCM_PLL5_CTRL_EN);
+ CCM_PLL5_CTRL_N((clk * 2) / 24) | CCM_PLL_CTRL_EN);
setbits_le32(ccm + CCU_H6_PLL5_CFG, BIT(24));
clrsetbits_le32(ccm + CCU_H6_PLL5_CFG, 0x3,
- CCM_PLL5_LOCK_EN | CCM_PLL5_CTRL_EN | BIT(30));
- clrbits_le32(ccm + CCU_H6_PLL5_CFG, 0x3 | BIT(30));
+ CCM_PLL_LOCK_EN | CCM_PLL_CTRL_EN | CCM_PLL_LDO_EN);
+ clrbits_le32(ccm + CCU_H6_PLL5_CFG, 0x3 | CCM_PLL_LDO_EN);
mctl_await_completion(ccm + CCU_H6_PLL5_CFG,
- CCM_PLL5_LOCK, CCM_PLL5_LOCK);
+ CCM_PLL_LOCK, CCM_PLL_LOCK);
/* Enable DRAM clock and gate*/
clrbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, BIT(24) | BIT(25));
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c
index 84fd64a2bfc..ea26e6dd327 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c
@@ -167,16 +167,16 @@ static void mctl_sys_init(u32 clk_rate)
clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(0));
udelay(5);
writel(0, ccm + CCU_H6_DRAM_GATE_RESET);
- clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL5_CTRL_EN);
+ clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL_CTRL_EN);
clrbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, DRAM_MOD_RESET);
udelay(5);
/* Set PLL5 rate to doubled DRAM clock rate */
- writel(CCM_PLL5_CTRL_EN | CCM_PLL5_LOCK_EN |
+ writel(CCM_PLL_CTRL_EN | CCM_PLL_LOCK_EN |
CCM_PLL5_CTRL_N(clk_rate * 2 / 24), ccm + CCU_H6_PLL5_CFG);
mctl_await_completion(ccm + CCU_H6_PLL5_CFG,
- CCM_PLL5_LOCK, CCM_PLL5_LOCK);
+ CCM_PLL_LOCK, CCM_PLL_LOCK);
/* Configure DRAM mod clock */
writel(DRAM_CLK_SRC_PLL5, ccm + CCU_H6_DRAM_CLK_CFG);
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c
index 5a59f82d1ef..877181016f3 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h616.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c
@@ -106,16 +106,16 @@ static void mctl_sys_init(u32 clk_rate)
clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(GATE_SHIFT));
udelay(5);
clrbits_le32(ccm + CCU_H6_DRAM_GATE_RESET, BIT(RESET_SHIFT));
- clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL5_CTRL_EN);
+ clrbits_le32(ccm + CCU_H6_PLL5_CFG, CCM_PLL_CTRL_EN);
clrbits_le32(ccm + CCU_H6_DRAM_CLK_CFG, DRAM_MOD_RESET);
udelay(5);
/* Set PLL5 rate to doubled DRAM clock rate */
- writel(CCM_PLL5_CTRL_EN | CCM_PLL5_LOCK_EN | CCM_PLL5_OUT_EN |
+ writel(CCM_PLL_CTRL_EN | CCM_PLL_LOCK_EN | CCM_PLL_OUT_EN |
CCM_PLL5_CTRL_N(clk_rate * 2 / 24), ccm + CCU_H6_PLL5_CFG);
mctl_await_completion(ccm + CCU_H6_PLL5_CFG,
- CCM_PLL5_LOCK, CCM_PLL5_LOCK);
+ CCM_PLL_LOCK, CCM_PLL_LOCK);
/* Configure DRAM mod clock */
writel(DRAM_CLK_SRC_PLL5, ccm + CCU_H6_DRAM_CLK_CFG);
--
2.46.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 02/20] sunxi: clock: H6: factor out clock_set_pll()
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
2025-07-17 23:54 ` [PATCH v2 01/20] sunxi: clock: H6: unify PLL control bit definitions Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 03/20] sunxi: clock: H6: factor out H6/H616 CPU clock setup Andre Przywara
` (18 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 | 60 +++++++++++++++----
2 files changed, 50 insertions(+), 12 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 37a620c9866..5d6519d2367 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 2f2fd5df93d..90436b45b40 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -4,6 +4,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)
@@ -72,6 +73,53 @@ void clock_init_uart(void)
1 << (RESET_SHIFT + CONFIG_CONS_INDEX - 1));
}
+static bool has_pll_output_gate(void)
+{
+ return (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2) ||
+ IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
+ IS_ENABLED(CONFIG_MACH_SUN50I_A133));
+}
+
+/* A shared routine to program the CPU PLLs for H6, H616, T113, A523 */
+static void clock_set_pll(u32 *reg, unsigned int n)
+{
+ u32 val = readl(reg);
+
+ /* clear the lock enable bit */
+ val &= ~CCM_PLL_LOCK_EN;
+ writel(val, reg);
+
+ /* gate the output on the newer SoCs */
+ if (has_pll_output_gate()) {
+ val &= ~CCM_PLL_OUT_EN;
+ writel(val, reg);
+ }
+
+ val &= ~(CCM_PLL1_CTRL_N_MASK | GENMASK(3, 0) | GENMASK(21, 16));
+ val |= CCM_PLL1_CTRL_N(n);
+ writel(val, reg); /* program parameter */
+
+ val |= CCM_PLL_CTRL_EN;
+ if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2))
+ val |= CCM_PLL_LDO_EN;
+ writel(val, reg); /* enable PLL */
+
+ val |= CCM_PLL_LOCK_EN;
+ if (IS_ENABLED(CONFIG_MACH_SUN55I_A523))
+ val |= CCM_PLL1_UPDATE;
+ writel(val, reg); /* start locking process */
+
+ while (!(readl(reg) & CCM_PLL_LOCK)) { /* wait for lock bit */
+ }
+ udelay(20); /* wait as per manual */
+
+ /* un-gate the output on the newer SoCs */
+ if (has_pll_output_gate()) {
+ val |= CCM_PLL_OUT_EN;
+ writel(val, reg);
+ }
+}
+
void clock_set_pll1(unsigned int clk)
{
void *const ccm = (void *)SUNXI_CCM_BASE;
@@ -86,17 +134,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) ||
- IS_ENABLED(CONFIG_MACH_SUN50I_A133))
- 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] 32+ messages in thread* [PATCH v2 03/20] sunxi: clock: H6: factor out H6/H616 CPU clock setup
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
2025-07-17 23:54 ` [PATCH v2 01/20] sunxi: clock: H6: unify PLL control bit definitions Andre Przywara
2025-07-17 23:54 ` [PATCH v2 02/20] sunxi: clock: H6: factor out clock_set_pll() Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 04/20] sunxi: clock: H6: add A523 CPU PLL support Andre Przywara
` (17 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 90436b45b40..84064c4ed86 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -120,29 +120,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] 32+ messages in thread* [PATCH v2 04/20] sunxi: clock: H6: add A523 CPU PLL support
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (2 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 03/20] sunxi: clock: H6: factor out H6/H616 CPU clock setup Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 05/20] sunxi: spl: add support for Allwinner A523 watchdog Andre Przywara
` (16 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 5d6519d2367..5881ab88573 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 84064c4ed86..3a4399a9c6c 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -6,6 +6,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)
{
@@ -120,6 +124,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;
@@ -148,7 +183,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] 32+ messages in thread* [PATCH v2 05/20] sunxi: spl: add support for Allwinner A523 watchdog
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (3 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 04/20] sunxi: clock: H6: add A523 CPU PLL support Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 06/20] clk: sunxi: Add support for the A523 CCU Andre Przywara
` (15 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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. But it also conveniently adds a direct reset
functionality, so we don't need to use a dummy timeout period.
Avoid introducing a new MMIO register frame C struct, but just define
the one needed register offset as a macro. Then just trigger this new
direct reset functionality in the A523 specific reset_cpu()
implementation.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
arch/arm/include/asm/arch-sunxi/watchdog.h | 2 ++
arch/arm/mach-sunxi/board.c | 6 ++++++
2 files changed, 8 insertions(+)
diff --git a/arch/arm/include/asm/arch-sunxi/watchdog.h b/arch/arm/include/asm/arch-sunxi/watchdog.h
index 38e2ef2aca3..14a6e89ccfa 100644
--- a/arch/arm/include/asm/arch-sunxi/watchdog.h
+++ b/arch/arm/include/asm/arch-sunxi/watchdog.h
@@ -12,6 +12,8 @@
#define WDT_CTRL_RESTART (0x1 << 0)
#define WDT_CTRL_KEY (0x0a57 << 1)
+#define WDT_SRST_REG 0x08
+
#if defined(CONFIG_MACH_SUN4I) || \
defined(CONFIG_MACH_SUN5I) || \
defined(CONFIG_MACH_SUN7I) || \
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 08d55b3a0e3..13caefda884 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -502,6 +502,12 @@ void reset_cpu(void)
/* sun5i sometimes gets stuck without this */
writel(WDT_MODE_RESET_EN | WDT_MODE_EN, &wdog->mode);
}
+#elif defined(CONFIG_MACH_SUN55I_A523)
+ static const void *wdog = (void *)SUNXI_TIMER_BASE;
+
+ writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, wdog + WDT_SRST_REG);
+ while (1)
+ ;
#elif defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
#if defined(CONFIG_MACH_SUN50I_H6)
/* WDOG is broken for some H6 rev. use the R_WDOG instead */
--
2.46.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 06/20] clk: sunxi: Add support for the A523 CCU
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (4 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 05/20] sunxi: spl: add support for Allwinner A523 watchdog Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 07/20] clk: sunxi: Add support for the A523 -R CCU Andre Przywara
` (14 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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] 32+ messages in thread* [PATCH v2 07/20] clk: sunxi: Add support for the A523 -R CCU
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (5 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 06/20] clk: sunxi: Add support for the A523 CCU Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 08/20] pinctrl: sunxi: add Allwinner A523 pinctrl description Andre Przywara
` (13 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 | 39 ++++++++++++++++++++++++++++++++++
drivers/clk/sunxi/clk_sunxi.c | 5 +++++
4 files changed, 52 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..01e613d20aa
--- /dev/null
+++ b/drivers/clk/sunxi/clk_a523_r.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2024 Arm Ltd.
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <clk/sunxi.h>
+#include <dt-bindings/clock/sun55i-a523-r-ccu.h>
+#include <dt-bindings/reset/sun55i-a523-r-ccu.h>
+#include <linux/bitops.h>
+
+static struct ccu_clk_gate a523_r_gates[] = {
+ [CLK_R_AHB] = GATE_DUMMY,
+ [CLK_R_APB0] = GATE_DUMMY,
+ [CLK_R_APB1] = GATE_DUMMY,
+ [CLK_BUS_R_TWD] = GATE(0x12c, BIT(0)),
+ [CLK_BUS_R_I2C0] = GATE(0x19c, BIT(0)),
+ [CLK_BUS_R_I2C1] = GATE(0x19c, BIT(1)),
+ [CLK_BUS_R_I2C2] = GATE(0x19c, BIT(2)),
+ [CLK_BUS_R_RTC] = GATE(0x20c, BIT(0)),
+};
+
+static struct ccu_reset a523_r_resets[] = {
+ [RST_BUS_R_TWD] = RESET(0x12c, BIT(16)),
+ [RST_BUS_R_UART0] = RESET(0x18c, BIT(16)),
+ [RST_BUS_R_I2C0] = RESET(0x19c, BIT(16)),
+ [RST_BUS_R_I2C1] = RESET(0x19c, BIT(17)),
+ [RST_BUS_R_I2C2] = RESET(0x19c, BIT(18)),
+ [RST_BUS_R_PPU1] = RESET(0x1ac, BIT(17)),
+ [RST_BUS_R_RTC] = RESET(0x20c, BIT(16)),
+};
+
+const struct ccu_desc a523_r_ccu_desc = {
+ .gates = a523_r_gates,
+ .resets = a523_r_resets,
+ .num_gates = ARRAY_SIZE(a523_r_gates),
+ .num_resets = ARRAY_SIZE(a523_r_resets),
+};
diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c
index 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] 32+ messages in thread* [PATCH v2 08/20] pinctrl: sunxi: add Allwinner A523 pinctrl description
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (6 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 07/20] clk: sunxi: Add support for the A523 -R CCU Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 09/20] sunxi: mmc: add support for Allwinner A523 MMC mod clock Andre Przywara
` (12 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 | 50 +++++++++++++++++++++++++++
2 files changed, 60 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..03cfe23aaf8 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -759,6 +759,29 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h616_pinctrl_desc =
.num_banks = 9,
};
+static const struct sunxi_pinctrl_function sun55i_a523_pinctrl_functions[] = {
+ { "emac0", 5 }, /* PI0-PI16 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC0-PC16 */
+ { "spi0", 4 }, /* PC0-PC7, PC15-PC16 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PH0-PH1 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun55i_a523_pinctrl_desc = {
+ .functions = sun55i_a523_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun55i_a523_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 11,
+};
+
static const struct sunxi_pinctrl_function sun50i_h616_r_pinctrl_functions[] = {
{ "gpio_in", 0 },
{ "gpio_out", 1 },
@@ -809,6 +832,21 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a100_r_pinctrl_desc
.num_banks = 1,
};
+static const struct sunxi_pinctrl_function sun55i_a523_r_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "r_i2c0", 2 }, /* PL0-PL1 */
+ { "r_spi", 6 }, /* PL10-PL13 */
+ { "r_uart", 2 }, /* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun55i_a523_r_pinctrl_desc = {
+ .functions = sun55i_a523_r_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun55i_a523_r_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_L,
+ .num_banks = 2,
+};
+
static const struct udevice_id sunxi_pinctrl_ids[] = {
#ifdef CONFIG_PINCTRL_SUNIV_F1C100S
{
@@ -983,6 +1021,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] 32+ messages in thread* [PATCH v2 09/20] sunxi: mmc: add support for Allwinner A523 MMC mod clock
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (7 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 08/20] pinctrl: sunxi: add Allwinner A523 pinctrl description Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-22 0:32 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 10/20] power: regulator: add AXP323 support Andre Przywara
` (11 subsequent siblings)
20 siblings, 1 reply; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 06c1e09bf26..7c85030be16 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -99,6 +99,15 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
*/
if (IS_ENABLED(CONFIG_MACH_SUN8I_R528))
pll_hz /= 2;
+
+ /*
+ * The A523/T527 uses PERIPH0_400M as the 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;
@@ -153,6 +162,10 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
CCM_MMC_CTRL_SCLK_DLY(sclk_dly);
}
+ /* The A523 has a second divider, not a shift. */
+ if (IS_ENABLED(CONFIG_MACH_SUN55I_A523))
+ n = (1U << n) - 1;
+
writel(CCM_MMC_CTRL_ENABLE| pll | CCM_MMC_CTRL_N(n) |
CCM_MMC_CTRL_M(div) | val, priv->mclkreg);
@@ -559,7 +572,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] 32+ messages in thread* Re: [PATCH v2 09/20] sunxi: mmc: add support for Allwinner A523 MMC mod clock
2025-07-17 23:54 ` [PATCH v2 09/20] sunxi: mmc: add support for Allwinner A523 MMC mod clock Andre Przywara
@ 2025-07-22 0:32 ` Andre Przywara
0 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-22 0:32 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
On Fri, 18 Jul 2025 00:54:44 +0100
Andre Przywara <andre.przywara@arm.com> wrote:
> 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 06c1e09bf26..7c85030be16 100644
> --- a/drivers/mmc/sunxi_mmc.c
> +++ b/drivers/mmc/sunxi_mmc.c
> @@ -99,6 +99,15 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
> */
> if (IS_ENABLED(CONFIG_MACH_SUN8I_R528))
> pll_hz /= 2;
> +
> + /*
> + * The A523/T527 uses PERIPH0_400M as the MMC input clock,
> + * which is the PERIPH0 nominal rate (1200MHz) / 3.
As Yixun figured correctly, a recent patch fixed the PLL PERIPH0 clock
calculation for NCAT2 chips, so the routine reports 600 MHz, to stay
compatible with older SoCs. So the divider here must be 3, really, not
6.
But this is only half the truth, since for MMC2 the base clock is
PLL_PERIPH0_800M, so we must multiply this by 2 again afterwards, to
reach the proper eMMC frequency.
Fixed in my tree.
Cheers,
Andre
> + * 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;
> @@ -153,6 +162,10 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
> CCM_MMC_CTRL_SCLK_DLY(sclk_dly);
> }
>
> + /* The A523 has a second divider, not a shift. */
> + if (IS_ENABLED(CONFIG_MACH_SUN55I_A523))
> + n = (1U << n) - 1;
> +
> writel(CCM_MMC_CTRL_ENABLE| pll | CCM_MMC_CTRL_N(n) |
> CCM_MMC_CTRL_M(div) | val, priv->mclkreg);
>
> @@ -559,7 +572,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;
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v2 10/20] power: regulator: add AXP323 support
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (8 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 09/20] sunxi: mmc: add support for Allwinner A523 MMC mod clock Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 11/20] sunxi: update cpu_sunxi_ncat2.h Andre Przywara
` (10 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 c300fd2bbc2..1204ec00f8d 100644
--- a/drivers/power/pmic/axp.c
+++ b/drivers/power/pmic/axp.c
@@ -89,6 +89,7 @@ static const struct udevice_id axp_pmic_ids[] = {
{ .compatible = "x-powers,axp221", .data = AXP221_ID },
{ .compatible = "x-powers,axp223", .data = AXP223_ID },
{ .compatible = "x-powers,axp313a", .data = AXP313_ID },
+ { .compatible = "x-powers,axp323", .data = AXP323_ID },
{ .compatible = "x-powers,axp717", .data = AXP717_ID },
{ .compatible = "x-powers,axp803", .data = AXP803_ID },
{ .compatible = "x-powers,axp806", .data = AXP806_ID },
diff --git a/drivers/power/regulator/axp_regulator.c b/drivers/power/regulator/axp_regulator.c
index 75cdbca30f6..7794a4f5d92 100644
--- a/drivers/power/regulator/axp_regulator.c
+++ b/drivers/power/regulator/axp_regulator.c
@@ -318,6 +318,7 @@ static const struct axp_regulator_plat *const axp_regulators[] = {
[AXP221_ID] = axp22x_regulators,
[AXP223_ID] = axp22x_regulators,
[AXP313_ID] = axp313_regulators,
+ [AXP323_ID] = axp313_regulators,
[AXP717_ID] = axp717_regulators,
[AXP803_ID] = axp803_regulators,
[AXP806_ID] = axp806_regulators,
diff --git a/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] 32+ messages in thread* [PATCH v2 11/20] sunxi: update cpu_sunxi_ncat2.h
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (9 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 10/20] power: regulator: add AXP323 support Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 12/20] sunxi: sun50i_h6: add A523 SPL clock setup code Andre Przywara
` (9 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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] 32+ messages in thread* [PATCH v2 12/20] sunxi: sun50i_h6: add A523 SPL clock setup code
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (10 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 11/20] sunxi: update cpu_sunxi_ncat2.h Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-22 0:14 ` Andre Przywara
2025-07-27 20:15 ` Jernej Škrabec
2025-07-17 23:54 ` [PATCH v2 13/20] sunxi: A523: add DRAM initialisation routine Andre Przywara
` (8 subsequent siblings)
20 siblings, 2 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 5881ab88573..699d75f37c4 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
@@ -108,6 +108,13 @@
#define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002
#define CCM_AHB3_DEFAULT 0x03000002
#define CCM_APB1_DEFAULT 0x03000102
+
+#elif CONFIG_MACH_SUN55I_A523 /* A523 */
+
+#define CCM_PLL6_DEFAULT 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 */
@@ -127,6 +134,7 @@
/* MBUS clock bit field */
#define MBUS_ENABLE BIT(31)
#define MBUS_RESET BIT(30)
+#define MBUS_UPDATE BIT(27)
#define MBUS_CLK_SRC_MASK GENMASK(25, 24)
#define MBUS_CLK_SRC_OSCM24 (0 << 24)
#define MBUS_CLK_SRC_PLL6X2 (1 << 24)
@@ -139,10 +147,12 @@
#define GATE_SHIFT (0)
/* DRAM clock bit field */
+#define DRAM_CLK_ENABLE BIT(31)
#define DRAM_MOD_RESET BIT(30)
#define DRAM_CLK_UPDATE BIT(27)
#define DRAM_CLK_SRC_MASK GENMASK(25, 24)
#define DRAM_CLK_SRC_PLL5 (0 << 24)
+#define DRAM_CLK_M_MASK (0x1f)
#define DRAM_CLK_M(m) (((m)-1) << 0)
/* MMC clock bit field */
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index 3a4399a9c6c..80004f13a1e 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -16,15 +16,22 @@ void clock_init_safe(void)
void *const ccm = (void *)SUNXI_CCM_BASE;
void *const prcm = (void *)SUNXI_PRCM_BASE;
- if (IS_ENABLED(CONFIG_MACH_SUN50I_H616)) {
- /* this seems to enable PLLs on H616 */
+ if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 0x10);
+ if (IS_ENABLED(CONFIG_MACH_SUN55I_A523))
+ setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 0x200);
+ udelay(1);
+
+ if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
+ IS_ENABLED(CONFIG_MACH_SUN55I_A523))
setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 2);
- }
+ udelay(1);
if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
- IS_ENABLED(CONFIG_MACH_SUN50I_H6)) {
+ IS_ENABLED(CONFIG_MACH_SUN50I_H6) ||
+ IS_ENABLED(CONFIG_MACH_SUN55I_A523)) {
clrbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 1);
+ udelay(1);
setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 1);
}
@@ -41,9 +48,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
@@ -55,7 +63,15 @@ void clock_init_safe(void)
* The mux and factor are set, but the clock will be enabled in
* DRAM initialization code.
*/
- writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), ccm + CCU_H6_MBUS_CFG);
+ if (IS_ENABLED(CONFIG_MACH_SUN55I_A523)) {
+ writel(MBUS_RESET, ccm + CCU_H6_MBUS_CFG);
+ udelay(1);
+ writel(MBUS_UPDATE | MBUS_CLK_SRC_OSCM24 | MBUS_CLK_M(4),
+ ccm + CCU_H6_MBUS_CFG);
+ } else {
+ writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3),
+ ccm + CCU_H6_MBUS_CFG);
+ }
}
void clock_init_uart(void)
--
2.46.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v2 12/20] sunxi: sun50i_h6: add A523 SPL clock setup code
2025-07-17 23:54 ` [PATCH v2 12/20] sunxi: sun50i_h6: add A523 SPL clock setup code Andre Przywara
@ 2025-07-22 0:14 ` Andre Przywara
2025-07-27 20:15 ` Jernej Škrabec
1 sibling, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-22 0:14 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
On Fri, 18 Jul 2025 00:54:47 +0100
Andre Przywara <andre.przywara@arm.com> wrote:
> 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 5881ab88573..699d75f37c4 100644
> --- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
> +++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
> @@ -108,6 +108,13 @@
> #define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002
> #define CCM_AHB3_DEFAULT 0x03000002
> #define CCM_APB1_DEFAULT 0x03000102
> +
> +#elif CONFIG_MACH_SUN55I_A523 /* A523 */
> +
> +#define CCM_PLL6_DEFAULT 0xe8116310 /* 1200 MHz */
This value is wrong, it must be 0xe8216310. Bits [22:20] program the 800
MHz sub-clock, divided down from the nominal 2.4GHz base frequency. So
the divider must be 3, encoded as 0b010. This is also the reset value.
Linux will calculate the actual frequency, but won't change the
setting, so it keeps and sees that clock as 1200 MHz, not 800 MHz. So
things might still work correctly, but I guess it's called
PLL_PERIPH0_800M for a reason, so let's keep it at this value.
Cheers,
Andre
> +#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 */
> @@ -127,6 +134,7 @@
> /* MBUS clock bit field */
> #define MBUS_ENABLE BIT(31)
> #define MBUS_RESET BIT(30)
> +#define MBUS_UPDATE BIT(27)
> #define MBUS_CLK_SRC_MASK GENMASK(25, 24)
> #define MBUS_CLK_SRC_OSCM24 (0 << 24)
> #define MBUS_CLK_SRC_PLL6X2 (1 << 24)
> @@ -139,10 +147,12 @@
> #define GATE_SHIFT (0)
>
> /* DRAM clock bit field */
> +#define DRAM_CLK_ENABLE BIT(31)
> #define DRAM_MOD_RESET BIT(30)
> #define DRAM_CLK_UPDATE BIT(27)
> #define DRAM_CLK_SRC_MASK GENMASK(25, 24)
> #define DRAM_CLK_SRC_PLL5 (0 << 24)
> +#define DRAM_CLK_M_MASK (0x1f)
> #define DRAM_CLK_M(m) (((m)-1) << 0)
>
> /* MMC clock bit field */
> diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
> index 3a4399a9c6c..80004f13a1e 100644
> --- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
> +++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
> @@ -16,15 +16,22 @@ void clock_init_safe(void)
> void *const ccm = (void *)SUNXI_CCM_BASE;
> void *const prcm = (void *)SUNXI_PRCM_BASE;
>
> - if (IS_ENABLED(CONFIG_MACH_SUN50I_H616)) {
> - /* this seems to enable PLLs on H616 */
> + if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
> setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 0x10);
> + if (IS_ENABLED(CONFIG_MACH_SUN55I_A523))
> + setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 0x200);
> + udelay(1);
> +
> + if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
> + IS_ENABLED(CONFIG_MACH_SUN55I_A523))
> setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 2);
> - }
> + udelay(1);
>
> if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
> - IS_ENABLED(CONFIG_MACH_SUN50I_H6)) {
> + IS_ENABLED(CONFIG_MACH_SUN50I_H6) ||
> + IS_ENABLED(CONFIG_MACH_SUN55I_A523)) {
> clrbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 1);
> + udelay(1);
> setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 1);
> }
>
> @@ -41,9 +48,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
> @@ -55,7 +63,15 @@ void clock_init_safe(void)
> * The mux and factor are set, but the clock will be enabled in
> * DRAM initialization code.
> */
> - writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), ccm + CCU_H6_MBUS_CFG);
> + if (IS_ENABLED(CONFIG_MACH_SUN55I_A523)) {
> + writel(MBUS_RESET, ccm + CCU_H6_MBUS_CFG);
> + udelay(1);
> + writel(MBUS_UPDATE | MBUS_CLK_SRC_OSCM24 | MBUS_CLK_M(4),
> + ccm + CCU_H6_MBUS_CFG);
> + } else {
> + writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3),
> + ccm + CCU_H6_MBUS_CFG);
> + }
> }
>
> void clock_init_uart(void)
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v2 12/20] sunxi: sun50i_h6: add A523 SPL clock setup code
2025-07-17 23:54 ` [PATCH v2 12/20] sunxi: sun50i_h6: add A523 SPL clock setup code Andre Przywara
2025-07-22 0:14 ` Andre Przywara
@ 2025-07-27 20:15 ` Jernej Škrabec
1 sibling, 0 replies; 32+ messages in thread
From: Jernej Škrabec @ 2025-07-27 20:15 UTC (permalink / raw)
To: u-boot, Andre Przywara
Cc: Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski, linux-sunxi,
Tom Rini
Dne petek, 18. julij 2025 ob 01:54:47 Srednjeevropski poletni čas je Andre Przywara napisal(a):
> From: Jernej Skrabec <jernej.skrabec@gmail.com>
Signed-off-by: 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 5881ab88573..699d75f37c4 100644
> --- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
> +++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
> @@ -108,6 +108,13 @@
> #define CCM_PSI_AHB1_AHB2_DEFAULT 0x03000002
> #define CCM_AHB3_DEFAULT 0x03000002
> #define CCM_APB1_DEFAULT 0x03000102
> +
> +#elif CONFIG_MACH_SUN55I_A523 /* A523 */
> +
> +#define CCM_PLL6_DEFAULT 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 */
> @@ -127,6 +134,7 @@
> /* MBUS clock bit field */
> #define MBUS_ENABLE BIT(31)
> #define MBUS_RESET BIT(30)
> +#define MBUS_UPDATE BIT(27)
> #define MBUS_CLK_SRC_MASK GENMASK(25, 24)
> #define MBUS_CLK_SRC_OSCM24 (0 << 24)
> #define MBUS_CLK_SRC_PLL6X2 (1 << 24)
> @@ -139,10 +147,12 @@
> #define GATE_SHIFT (0)
>
> /* DRAM clock bit field */
> +#define DRAM_CLK_ENABLE BIT(31)
> #define DRAM_MOD_RESET BIT(30)
> #define DRAM_CLK_UPDATE BIT(27)
> #define DRAM_CLK_SRC_MASK GENMASK(25, 24)
> #define DRAM_CLK_SRC_PLL5 (0 << 24)
> +#define DRAM_CLK_M_MASK (0x1f)
> #define DRAM_CLK_M(m) (((m)-1) << 0)
>
> /* MMC clock bit field */
> diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
> index 3a4399a9c6c..80004f13a1e 100644
> --- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
> +++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
> @@ -16,15 +16,22 @@ void clock_init_safe(void)
> void *const ccm = (void *)SUNXI_CCM_BASE;
> void *const prcm = (void *)SUNXI_PRCM_BASE;
>
> - if (IS_ENABLED(CONFIG_MACH_SUN50I_H616)) {
> - /* this seems to enable PLLs on H616 */
> + if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
> setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 0x10);
> + if (IS_ENABLED(CONFIG_MACH_SUN55I_A523))
> + setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 0x200);
> + udelay(1);
> +
> + if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
> + IS_ENABLED(CONFIG_MACH_SUN55I_A523))
> setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 2);
> - }
> + udelay(1);
>
> if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
> - IS_ENABLED(CONFIG_MACH_SUN50I_H6)) {
> + IS_ENABLED(CONFIG_MACH_SUN50I_H6) ||
> + IS_ENABLED(CONFIG_MACH_SUN55I_A523)) {
> clrbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 1);
> + udelay(1);
> setbits_le32(prcm + CCU_PRCM_RES_CAL_CTRL, 1);
> }
>
> @@ -41,9 +48,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
> @@ -55,7 +63,15 @@ void clock_init_safe(void)
> * The mux and factor are set, but the clock will be enabled in
> * DRAM initialization code.
> */
> - writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), ccm + CCU_H6_MBUS_CFG);
> + if (IS_ENABLED(CONFIG_MACH_SUN55I_A523)) {
> + writel(MBUS_RESET, ccm + CCU_H6_MBUS_CFG);
> + udelay(1);
> + writel(MBUS_UPDATE | MBUS_CLK_SRC_OSCM24 | MBUS_CLK_M(4),
> + ccm + CCU_H6_MBUS_CFG);
> + } else {
> + writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3),
> + ccm + CCU_H6_MBUS_CFG);
> + }
> }
>
> void clock_init_uart(void)
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v2 13/20] sunxi: A523: add DRAM initialisation routine
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (11 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 12/20] sunxi: sun50i_h6: add A523 SPL clock setup code Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-27 20:16 ` Jernej Škrabec
2025-07-17 23:54 ` [PATCH v2 14/20] sunxi: A523: add DDR3 DRAM support Andre Przywara
` (7 subsequent siblings)
20 siblings, 1 reply; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 | 153 ++
arch/arm/mach-sunxi/Kconfig | 22 +-
arch/arm/mach-sunxi/Makefile | 2 +
arch/arm/mach-sunxi/dram_sun55i_a523.c | 1466 +++++++++++++++++
arch/arm/mach-sunxi/dram_timings/Makefile | 1 +
.../arm/mach-sunxi/dram_timings/a523_lpddr4.c | 119 ++
7 files changed, 1761 insertions(+), 4 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 0708ae3ee3b..0eccb1e6c28 100644
--- a/arch/arm/include/asm/arch-sunxi/dram.h
+++ b/arch/arm/include/asm/arch-sunxi/dram.h
@@ -35,6 +35,8 @@
#include <asm/arch/dram_sun50i_a133.h>
#elif defined(CONFIG_MACH_SUNIV)
#include <asm/arch/dram_suniv.h>
+#elif defined(CONFIG_MACH_SUN55I_A523)
+#include <asm/arch/dram_sun55i_a523.h>
#else
#include <asm/arch/dram_sun4i.h>
#endif
diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h b/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
new file mode 100644
index 00000000000..503a431da4a
--- /dev/null
+++ b/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * t527 dram controller register and constant defines
+ *
+ * (C) Copyright 2024 Jernej Skrabec <jernej.skrabec@gmail.com>
+ */
+
+#ifndef _SUNXI_DRAM_SUN55I_A523_H
+#define _SUNXI_DRAM_SUN55I_A523_H
+
+#include <linux/bitops.h>
+
+enum sunxi_dram_type {
+ SUNXI_DRAM_TYPE_DDR3 = 3,
+ SUNXI_DRAM_TYPE_DDR4,
+ SUNXI_DRAM_TYPE_LPDDR3 = 7,
+ SUNXI_DRAM_TYPE_LPDDR4
+};
+
+#define MCTL_COM_UNK_008 0x008
+#define MCTL_COM_MAER0 0x020
+
+/*
+ * Controller registers seems to be the same or at least very similar
+ * to those in H6.
+ */
+struct sunxi_mctl_ctl_reg {
+ u32 mstr; /* 0x000 */
+ u32 statr; /* 0x004 unused */
+ u32 mstr1; /* 0x008 unused */
+ u32 clken; /* 0x00c */
+ u32 mrctrl0; /* 0x010 unused */
+ u32 mrctrl1; /* 0x014 unused */
+ u32 mrstatr; /* 0x018 unused */
+ u32 mrctrl2; /* 0x01c unused */
+ u32 derateen; /* 0x020 unused */
+ u32 derateint; /* 0x024 unused */
+ u8 reserved_0x028[8]; /* 0x028 */
+ u32 pwrctl; /* 0x030 */
+ u32 pwrtmg; /* 0x034 unused */
+ u32 hwlpctl; /* 0x038 */
+ u8 reserved_0x03c[20]; /* 0x03c */
+ u32 rfshctl0; /* 0x050 unused */
+ u32 rfshctl1; /* 0x054 unused */
+ u8 reserved_0x058[8]; /* 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 0a7c029b15a..8aa5f1b46bf 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -57,7 +57,12 @@ config DRAM_SUN50I_A133
Select this dram controller driver for some sun50i platforms,
like A133.
-if DRAM_SUN50I_H616 || DRAM_SUN50I_A133
+config DRAM_SUN55I_A523
+ bool
+ help
+ Select this DRAM controller driver for A523/T527 SoCs.
+
+if DRAM_SUN50I_H616 || DRAM_SUN50I_A133 || DRAM_SUN55I_A523
config DRAM_SUNXI_DX_ODT
hex "DRAM DX ODT parameter"
help
@@ -170,8 +175,8 @@ config DRAM_SUNXI_TPR13
config DRAM_SUNXI_TPR14
hex "DRAM TPR14 parameter"
- depends on DRAM_SUN50I_A133
- default 0x0
+ depends on DRAM_SUN50I_A133 || MACH_SUN55I_A523
+ default 0x48484848
help
TPR14 value from vendor DRAM settings.
@@ -569,7 +574,7 @@ config ARM_BOOT_HOOK_RMR
This allows both the SPL and the U-Boot proper to be entered in
either mode and switch to AArch64 if needed.
-if SUNXI_DRAM_DW || DRAM_SUN50I_H6 || DRAM_SUN50I_H616 || DRAM_SUN50I_A133
+if SUNXI_DRAM_DW || DRAM_SUN50I_H6 || DRAM_SUN50I_H616 || DRAM_SUN50I_A133 || DRAM_SUN55I_A523
config SUNXI_DRAM_DDR3
bool
@@ -587,6 +592,7 @@ config SUNXI_DRAM_DDR4
choice
prompt "DRAM Type and Timing"
+ default SUNXI_DRAM_A523_LPDDR4 if MACH_SUN55I_A523
default SUNXI_DRAM_DDR3_1333 if !MACH_SUN8I_V3S
default SUNXI_DRAM_DDR2_V3S if MACH_SUN8I_V3S
@@ -670,6 +676,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
@@ -690,6 +703,7 @@ config DRAM_CLK
default 672 if MACH_SUN50I
default 744 if MACH_SUN50I_H6
default 720 if MACH_SUN50I_H616 || MACH_SUN50I_A133
+ default 1200 if MACH_SUN55I_A523
---help---
Set the dram clock speed, valid range 240 - 480 (prior to sun9i),
must be a multiple of 24. For the sun9i (A80), the tested values
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index 8eff20b77bf..579530f27e3 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -47,4 +47,6 @@ obj-$(CONFIG_DRAM_SUN50I_H616) += dram_sun50i_h616.o dram_dw_helpers.o
obj-$(CONFIG_DRAM_SUN50I_H616) += dram_timings/
obj-$(CONFIG_DRAM_SUN50I_A133) += dram_sun50i_a133.o
obj-$(CONFIG_DRAM_SUN50I_A133) += dram_timings/
+obj-$(CONFIG_MACH_SUN55I_A523) += dram_sun55i_a523.o
+obj-$(CONFIG_DRAM_SUN55I_A523) += dram_timings/
endif
diff --git a/arch/arm/mach-sunxi/dram_sun55i_a523.c b/arch/arm/mach-sunxi/dram_sun55i_a523.c
new file mode 100644
index 00000000000..a5c4fba7784
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_sun55i_a523.c
@@ -0,0 +1,1466 @@
+// 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)
+{
+ void * const mctl_com = (void *)SUNXI_DRAM_COM_BASE;
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ void *const prcm = (void *)SUNXI_PRCM_BASE;
+ u32 val, val2, mr1, mr2;
+ int i;
+
+ clrbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 1);
+ udelay(1);
+
+ clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x84, 0x40000);
+
+ if (config->bus_full_width)
+ val = 0xf00;
+ else
+ val = 0x300;
+ clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x00, 0xf00, val);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_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 + MCTL_COM_UNK_008, BIT(9));
+ 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 + MCTL_COM_MAER0, BIT(8));
+
+ /* start DFI init */
+ writel(0, &mctl_ctl->swctl);
+ setbits_le32(&mctl_ctl->dfimisc, 1);
+ setbits_le32(&mctl_ctl->dfimisc, 0x20);
+ writel(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, 1, 1);
+ mctl_await_completion(&mctl_ctl->dfistat, 1, 1);
+
+ udelay(500);
+ setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 1);
+ udelay(1);
+
+ writel(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->dfimisc, 0x20);
+ writel(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, 1, 1);
+
+ writel(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->pwrctl, 0x20);
+ writel(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, 1, 1);
+ mctl_await_completion(&mctl_ctl->statr, 3, 1);
+
+ udelay(500);
+
+ writel(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->dfimisc, 1);
+ writel(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, 1, 1);
+
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_LPDDR4:
+ if (config->clk <= 936) {
+ mr1 = 0x34;
+ mr2 = 0x1b;
+ } else if (config->clk <= 1200) {
+ mr1 = 0x54;
+ mr2 = 0x2d;
+ } else {
+ mr1 = 0x64;
+ mr2 = 0x36;
+ }
+
+ writel(0x0, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0x100 | mr1, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0x200 | mr2, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0x333, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0x403, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0xb04, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0xc72, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0xd00, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0xe08, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+
+ writel(0x1626, &mctl_ctl->mrctrl1);
+ writel(0x800000f0, &mctl_ctl->mrctrl0);
+ mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
+ break;
+ default:
+ panic("This DRAM setup is currently not supported.\n");
+ };
+
+ writel(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->rfshctl3, 1);
+ writel(1, &mctl_ctl->swctl);
+
+ if (para->tpr10 & TPR10_WRITE_LEVELING) {
+ for (i = 0; i < 5; i++)
+ if (mctl_phy_write_leveling(para, config))
+ break;
+ if (i == 5) {
+ debug("write leveling failed!\n");
+ return false;
+ }
+ }
+
+ if (para->tpr10 & TPR10_READ_CALIBRATION) {
+ for (i = 0; i < 5; i++)
+ if (mctl_phy_read_calibration(para, config))
+ break;
+ if (i == 5) {
+ debug("read calibration failed!\n");
+ return false;
+ }
+ }
+
+ if (para->tpr10 & TPR10_READ_TRAINING) {
+ for (i = 0; i < 5; i++)
+ if (mctl_phy_read_training(para, config))
+ break;
+ if (i == 5) {
+ debug("read training failed!\n");
+ return false;
+ }
+ }
+
+ if (para->tpr10 & TPR10_WRITE_TRAINING) {
+ for (i = 0; i < 5; i++)
+ if (mctl_phy_write_training(config))
+ break;
+ if (i == 5) {
+ debug("write training failed!\n");
+ return false;
+ }
+ }
+
+ mctl_phy_bit_delay_compensation(para, config);
+
+ return true;
+}
+
+static bool mctl_ctrl_init(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ void * const mctl_com = (void *)SUNXI_DRAM_COM_BASE;
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ u32 reg_val;
+
+ clrsetbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24), BIT(25) | BIT(9));
+ setbits_le32(mctl_com + MCTL_COM_MAER0, BIT(15) | BIT(9));
+
+ if (para->type == SUNXI_DRAM_TYPE_LPDDR4) {
+ setbits_le32(0x02023ea8, 1); // NSI
+ setbits_le32(0x02071008, 1); // NSI_CPU
+ }
+
+ clrsetbits_le32(&mctl_ctl->sched[0], 0xff08, 0x3000);
+ clrsetbits_le32(&mctl_ctl->sched[1], 0x77000000, 0x33000000);
+ clrsetbits_le32(&mctl_ctl->unk_0x270, 0xffff, 0x808);
+ clrsetbits_le32(&mctl_ctl->unk_0x264, 0xff00ffff, 0x1f000030);
+
+ writel(0, &mctl_ctl->hwlpctl);
+
+ reg_val = MSTR_ACTIVE_RANKS(config->ranks);
+ switch (para->type) {
+ case SUNXI_DRAM_TYPE_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 + MCTL_COM_UNK_008, BIT(24), BIT(9));
+ udelay(1);
+ /* this write seems to enable PHY MMIO region */
+ setbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24));
+
+ if (!mctl_phy_init(para, config))
+ return false;
+
+ writel(0, &mctl_ctl->swctl);
+ clrbits_le32(&mctl_ctl->rfshctl3, BIT(0));
+ writel(1, &mctl_ctl->swctl);
+ mctl_await_completion(&mctl_ctl->swstat, 1, 1);
+
+ return true;
+}
+
+static bool mctl_core_init(const struct dram_para *para,
+ const struct dram_config *config)
+{
+ mctl_sys_init(config->clk);
+
+ return mctl_ctrl_init(para, config);
+}
+
+static void mctl_auto_detect_rank_width(const struct dram_para *para,
+ struct dram_config *config)
+{
+ /* this is minimum size that it's supported */
+ config->cols = 8;
+ config->rows = 13;
+
+ /*
+ * Strategy here is to test most demanding combination first and least
+ * demanding last, otherwise HW might not be fully utilized. For
+ * example, half bus width and rank = 1 combination would also work
+ * on HW with full bus width and rank = 2, but only 1/4 RAM would be
+ * visible.
+ */
+
+ debug("testing 32-bit width, rank = 2\n");
+ config->bus_full_width = 1;
+ config->ranks = 2;
+ if (mctl_core_init(para, config))
+ return;
+
+ debug("testing 32-bit width, rank = 1\n");
+ config->bus_full_width = 1;
+ config->ranks = 1;
+ if (mctl_core_init(para, config))
+ return;
+
+ debug("testing 16-bit width, rank = 2\n");
+ config->bus_full_width = 0;
+ config->ranks = 2;
+ if (mctl_core_init(para, config))
+ return;
+
+ debug("testing 16-bit width, rank = 1\n");
+ config->bus_full_width = 0;
+ config->ranks = 1;
+ if (mctl_core_init(para, config))
+ return;
+
+ panic("This DRAM setup is currently not supported.\n");
+}
+
+static void mctl_auto_detect_dram_size(const struct dram_para *para,
+ struct dram_config *config)
+{
+ /* detect row address bits */
+ config->cols = 8;
+ config->rows = 16;
+ mctl_core_init(para, config);
+
+ for (config->rows = 13; config->rows < 16; config->rows++) {
+ /* 8 banks, 8 bit per byte and 16/32 bit width */
+ if (mctl_mem_matches((1 << (config->rows + config->cols +
+ 4 + config->bus_full_width))))
+ break;
+ }
+
+ /* detect column address bits */
+ config->cols = 11;
+ mctl_core_init(para, config);
+
+ for (config->cols = 8; config->cols < 11; config->cols++) {
+ /* 8 bits per byte and 16/32 bit width */
+ if (mctl_mem_matches(1 << (config->cols + 1 +
+ config->bus_full_width)))
+ break;
+ }
+}
+
+static unsigned long long mctl_calc_size(const struct dram_config *config)
+{
+ u8 width = config->bus_full_width ? 4 : 2;
+
+ /* 8 banks */
+ return (1ULL << (config->cols + config->rows + 3)) * width * config->ranks;
+}
+
+static const struct dram_para para = {
+ .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(¶, &config);
+ mctl_auto_detect_dram_size(¶, &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(¶, &config);
+
+ size = mctl_calc_size(&config);
+
+ sunxi_nsi_init();
+ init_something();
+
+ return size;
+};
diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile
index 4dc1f29fc08..41fee509d5d 100644
--- a/arch/arm/mach-sunxi/dram_timings/Makefile
+++ b/arch/arm/mach-sunxi/dram_timings/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3) += h616_lpddr3.o
obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4) += h616_lpddr4_2133.o
obj-$(CONFIG_SUNXI_DRAM_A133_DDR4) += a133_ddr4.o
obj-$(CONFIG_SUNXI_DRAM_A133_LPDDR4) += a133_lpddr4.o
+obj-$(CONFIG_SUNXI_DRAM_A523_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..940a4d04d57
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_timings/a523_lpddr4.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * sun55i A523 LPDDR4-2133 timings, as programmed by Allwinner's boot0
+ *
+ * (C) Copyright 2024 Jernej Skrabec <jernej.skrabec@gmail.com>
+ * (C) Copyright 2023 Mikhail Kalashnikov <iuncuim@gmail.com>
+ *
+ */
+
+#include <asm/arch/dram.h>
+#include <asm/arch/cpu.h>
+
+void mctl_set_timing_params(u32 clk)
+{
+ struct sunxi_mctl_ctl_reg * const mctl_ctl =
+ (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
+ u8 tcl, tcwl, t_rdata_en, trtp, twr, tphy_wrlat;
+ unsigned int mr1, mr2;
+
+ u8 tccd = 4;
+ u8 tfaw = ns_to_t(40, clk);
+ u8 trrd = max(ns_to_t(10, clk), 2);
+ u8 twtr = max(ns_to_t(10, clk), 4);
+ u8 trcd = max(ns_to_t(18, clk), 2);
+ u8 trc = ns_to_t(65, clk);
+ u8 txp = max(ns_to_t(8, clk), 2);
+ u8 trp = ns_to_t(21, clk);
+ u8 tras = ns_to_t(42, clk);
+ u16 trefi = ns_to_t(3904, clk) / 32;
+ u16 trfc = ns_to_t(280, clk);
+ u16 txsr = ns_to_t(290, clk);
+
+ u8 tmrw = max(ns_to_t(14, clk), 5);
+ u8 tmod = 12;
+ u8 tcke = max(ns_to_t(15, clk), 2);
+ u8 tcksrx = max(ns_to_t(2, clk), 2);
+ u8 tcksre = max(ns_to_t(5, clk), 2);
+ u8 trasmax = (trefi * 9) / 32;
+
+ if (clk <= 936) {
+ mr1 = 0x34;
+ mr2 = 0x1b;
+ tcl = 10;
+ tcwl = 5;
+ t_rdata_en = 17;
+ trtp = 4;
+ tphy_wrlat = 5;
+ twr = 10;
+ } else if (clk <= 1200) {
+ mr1 = 0x54;
+ mr2 = 0x2d;
+ tcl = 14;
+ tcwl = 7;
+ t_rdata_en = 25;
+ trtp = 6;
+ tphy_wrlat = 9;
+ twr = 15;
+ } else {
+ mr1 = 0x64;
+ mr2 = 0x36;
+ tcl = 16;
+ tcwl = 8;
+ t_rdata_en = 29;
+ trtp = 7;
+ tphy_wrlat = 11;
+ twr = 17;
+ }
+
+ u8 tmrd = tmrw;
+ u8 tckesr = tcke;
+ u8 twtp = twr + 9 + tcwl;
+ u8 twr2rd = twtr + 9 + tcwl;
+ u8 trd2wr = ns_to_t(4, clk) + 7 - ns_to_t(1, clk) + tcl;
+ u8 txs = 4;
+ u8 txsdll = 16;
+ u8 txsabort = 4;
+ u8 txsfast = 4;
+
+ /* set DRAM timing */
+ writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,
+ &mctl_ctl->dramtmg[0]);
+ writel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]);
+ writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd,
+ &mctl_ctl->dramtmg[2]);
+ writel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]);
+ writel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp,
+ &mctl_ctl->dramtmg[4]);
+ writel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke,
+ &mctl_ctl->dramtmg[5]);
+ /* Value suggested by ZynqMP manual and used by libdram */
+ writel((txp + 2) | 0x02020000, &mctl_ctl->dramtmg[6]);
+ writel((txsfast << 24) | (txsabort << 16) | (txsdll << 8) | txs,
+ &mctl_ctl->dramtmg[8]);
+ writel(0x00020208, &mctl_ctl->dramtmg[9]);
+ writel(0xE0C05, &mctl_ctl->dramtmg[10]);
+ writel(0x440C021C, &mctl_ctl->dramtmg[11]);
+ writel(8, &mctl_ctl->dramtmg[12]);
+ writel(0xA100002, &mctl_ctl->dramtmg[13]);
+ writel(txsr, &mctl_ctl->dramtmg[14]);
+
+ clrsetbits_le32(&mctl_ctl->init[0], 0xC0000FFF, 0x558);
+ writel(0x01f20000, &mctl_ctl->init[1]);
+ writel(0x00001705, &mctl_ctl->init[2]);
+ writel(0, &mctl_ctl->dfimisc);
+ writel((mr1 << 16) | mr2, &mctl_ctl->init[3]);
+ writel(0x00330000, &mctl_ctl->init[4]);
+ writel(0x00040072, &mctl_ctl->init[6]);
+ writel(0x00260008, &mctl_ctl->init[7]);
+
+ clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
+
+ /* Configure DFI timing */
+ writel(tphy_wrlat | 0x2000000 | (t_rdata_en << 16) | 0x808000,
+ &mctl_ctl->dfitmg0);
+ writel(0x100202, &mctl_ctl->dfitmg1);
+
+ /* set refresh timing */
+ writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
+}
--
2.46.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v2 13/20] sunxi: A523: add DRAM initialisation routine
2025-07-17 23:54 ` [PATCH v2 13/20] sunxi: A523: add DRAM initialisation routine Andre Przywara
@ 2025-07-27 20:16 ` Jernej Škrabec
0 siblings, 0 replies; 32+ messages in thread
From: Jernej Škrabec @ 2025-07-27 20:16 UTC (permalink / raw)
To: u-boot, Andre Przywara
Cc: Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski, linux-sunxi,
Tom Rini
Dne petek, 18. julij 2025 ob 01:54:48 Srednjeevropski poletni č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.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
This needs improvements, but it can be done later.
Best regards,
Jernej
> ---
> arch/arm/include/asm/arch-sunxi/dram.h | 2 +
> .../include/asm/arch-sunxi/dram_sun55i_a523.h | 153 ++
> arch/arm/mach-sunxi/Kconfig | 22 +-
> arch/arm/mach-sunxi/Makefile | 2 +
> arch/arm/mach-sunxi/dram_sun55i_a523.c | 1466 +++++++++++++++++
> arch/arm/mach-sunxi/dram_timings/Makefile | 1 +
> .../arm/mach-sunxi/dram_timings/a523_lpddr4.c | 119 ++
> 7 files changed, 1761 insertions(+), 4 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 0708ae3ee3b..0eccb1e6c28 100644
> --- a/arch/arm/include/asm/arch-sunxi/dram.h
> +++ b/arch/arm/include/asm/arch-sunxi/dram.h
> @@ -35,6 +35,8 @@
> #include <asm/arch/dram_sun50i_a133.h>
> #elif defined(CONFIG_MACH_SUNIV)
> #include <asm/arch/dram_suniv.h>
> +#elif defined(CONFIG_MACH_SUN55I_A523)
> +#include <asm/arch/dram_sun55i_a523.h>
> #else
> #include <asm/arch/dram_sun4i.h>
> #endif
> diff --git a/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h b/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
> new file mode 100644
> index 00000000000..503a431da4a
> --- /dev/null
> +++ b/arch/arm/include/asm/arch-sunxi/dram_sun55i_a523.h
> @@ -0,0 +1,153 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * t527 dram controller register and constant defines
> + *
> + * (C) Copyright 2024 Jernej Skrabec <jernej.skrabec@gmail.com>
> + */
> +
> +#ifndef _SUNXI_DRAM_SUN55I_A523_H
> +#define _SUNXI_DRAM_SUN55I_A523_H
> +
> +#include <linux/bitops.h>
> +
> +enum sunxi_dram_type {
> + SUNXI_DRAM_TYPE_DDR3 = 3,
> + SUNXI_DRAM_TYPE_DDR4,
> + SUNXI_DRAM_TYPE_LPDDR3 = 7,
> + SUNXI_DRAM_TYPE_LPDDR4
> +};
> +
> +#define MCTL_COM_UNK_008 0x008
> +#define MCTL_COM_MAER0 0x020
> +
> +/*
> + * Controller registers seems to be the same or at least very similar
> + * to those in H6.
> + */
> +struct sunxi_mctl_ctl_reg {
> + u32 mstr; /* 0x000 */
> + u32 statr; /* 0x004 unused */
> + u32 mstr1; /* 0x008 unused */
> + u32 clken; /* 0x00c */
> + u32 mrctrl0; /* 0x010 unused */
> + u32 mrctrl1; /* 0x014 unused */
> + u32 mrstatr; /* 0x018 unused */
> + u32 mrctrl2; /* 0x01c unused */
> + u32 derateen; /* 0x020 unused */
> + u32 derateint; /* 0x024 unused */
> + u8 reserved_0x028[8]; /* 0x028 */
> + u32 pwrctl; /* 0x030 */
> + u32 pwrtmg; /* 0x034 unused */
> + u32 hwlpctl; /* 0x038 */
> + u8 reserved_0x03c[20]; /* 0x03c */
> + u32 rfshctl0; /* 0x050 unused */
> + u32 rfshctl1; /* 0x054 unused */
> + u8 reserved_0x058[8]; /* 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 0a7c029b15a..8aa5f1b46bf 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -57,7 +57,12 @@ config DRAM_SUN50I_A133
> Select this dram controller driver for some sun50i platforms,
> like A133.
>
> -if DRAM_SUN50I_H616 || DRAM_SUN50I_A133
> +config DRAM_SUN55I_A523
> + bool
> + help
> + Select this DRAM controller driver for A523/T527 SoCs.
> +
> +if DRAM_SUN50I_H616 || DRAM_SUN50I_A133 || DRAM_SUN55I_A523
> config DRAM_SUNXI_DX_ODT
> hex "DRAM DX ODT parameter"
> help
> @@ -170,8 +175,8 @@ config DRAM_SUNXI_TPR13
>
> config DRAM_SUNXI_TPR14
> hex "DRAM TPR14 parameter"
> - depends on DRAM_SUN50I_A133
> - default 0x0
> + depends on DRAM_SUN50I_A133 || MACH_SUN55I_A523
> + default 0x48484848
> help
> TPR14 value from vendor DRAM settings.
>
> @@ -569,7 +574,7 @@ config ARM_BOOT_HOOK_RMR
> This allows both the SPL and the U-Boot proper to be entered in
> either mode and switch to AArch64 if needed.
>
> -if SUNXI_DRAM_DW || DRAM_SUN50I_H6 || DRAM_SUN50I_H616 || DRAM_SUN50I_A133
> +if SUNXI_DRAM_DW || DRAM_SUN50I_H6 || DRAM_SUN50I_H616 || DRAM_SUN50I_A133 || DRAM_SUN55I_A523
> config SUNXI_DRAM_DDR3
> bool
>
> @@ -587,6 +592,7 @@ config SUNXI_DRAM_DDR4
>
> choice
> prompt "DRAM Type and Timing"
> + default SUNXI_DRAM_A523_LPDDR4 if MACH_SUN55I_A523
> default SUNXI_DRAM_DDR3_1333 if !MACH_SUN8I_V3S
> default SUNXI_DRAM_DDR2_V3S if MACH_SUN8I_V3S
>
> @@ -670,6 +676,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
>
> @@ -690,6 +703,7 @@ config DRAM_CLK
> default 672 if MACH_SUN50I
> default 744 if MACH_SUN50I_H6
> default 720 if MACH_SUN50I_H616 || MACH_SUN50I_A133
> + default 1200 if MACH_SUN55I_A523
> ---help---
> Set the dram clock speed, valid range 240 - 480 (prior to sun9i),
> must be a multiple of 24. For the sun9i (A80), the tested values
> diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
> index 8eff20b77bf..579530f27e3 100644
> --- a/arch/arm/mach-sunxi/Makefile
> +++ b/arch/arm/mach-sunxi/Makefile
> @@ -47,4 +47,6 @@ obj-$(CONFIG_DRAM_SUN50I_H616) += dram_sun50i_h616.o dram_dw_helpers.o
> obj-$(CONFIG_DRAM_SUN50I_H616) += dram_timings/
> obj-$(CONFIG_DRAM_SUN50I_A133) += dram_sun50i_a133.o
> obj-$(CONFIG_DRAM_SUN50I_A133) += dram_timings/
> +obj-$(CONFIG_MACH_SUN55I_A523) += dram_sun55i_a523.o
> +obj-$(CONFIG_DRAM_SUN55I_A523) += dram_timings/
> endif
> diff --git a/arch/arm/mach-sunxi/dram_sun55i_a523.c b/arch/arm/mach-sunxi/dram_sun55i_a523.c
> new file mode 100644
> index 00000000000..a5c4fba7784
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/dram_sun55i_a523.c
> @@ -0,0 +1,1466 @@
> +// 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)
> +{
> + void * const mctl_com = (void *)SUNXI_DRAM_COM_BASE;
> + struct sunxi_mctl_ctl_reg * const mctl_ctl =
> + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
> + void *const prcm = (void *)SUNXI_PRCM_BASE;
> + u32 val, val2, mr1, mr2;
> + int i;
> +
> + clrbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 1);
> + udelay(1);
> +
> + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x84, 0x40000);
> +
> + if (config->bus_full_width)
> + val = 0xf00;
> + else
> + val = 0x300;
> + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x00, 0xf00, val);
> +
> + switch (para->type) {
> + case SUNXI_DRAM_TYPE_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 + MCTL_COM_UNK_008, BIT(9));
> + 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 + MCTL_COM_MAER0, BIT(8));
> +
> + /* start DFI init */
> + writel(0, &mctl_ctl->swctl);
> + setbits_le32(&mctl_ctl->dfimisc, 1);
> + setbits_le32(&mctl_ctl->dfimisc, 0x20);
> + writel(1, &mctl_ctl->swctl);
> + mctl_await_completion(&mctl_ctl->swstat, 1, 1);
> + mctl_await_completion(&mctl_ctl->dfistat, 1, 1);
> +
> + udelay(500);
> + setbits_le32(prcm + CCU_PRCM_SYS_PWROFF_GATING, 1);
> + udelay(1);
> +
> + writel(0, &mctl_ctl->swctl);
> + clrbits_le32(&mctl_ctl->dfimisc, 0x20);
> + writel(1, &mctl_ctl->swctl);
> + mctl_await_completion(&mctl_ctl->swstat, 1, 1);
> +
> + writel(0, &mctl_ctl->swctl);
> + clrbits_le32(&mctl_ctl->pwrctl, 0x20);
> + writel(1, &mctl_ctl->swctl);
> + mctl_await_completion(&mctl_ctl->swstat, 1, 1);
> + mctl_await_completion(&mctl_ctl->statr, 3, 1);
> +
> + udelay(500);
> +
> + writel(0, &mctl_ctl->swctl);
> + clrbits_le32(&mctl_ctl->dfimisc, 1);
> + writel(1, &mctl_ctl->swctl);
> + mctl_await_completion(&mctl_ctl->swstat, 1, 1);
> +
> + switch (para->type) {
> + case SUNXI_DRAM_TYPE_LPDDR4:
> + if (config->clk <= 936) {
> + mr1 = 0x34;
> + mr2 = 0x1b;
> + } else if (config->clk <= 1200) {
> + mr1 = 0x54;
> + mr2 = 0x2d;
> + } else {
> + mr1 = 0x64;
> + mr2 = 0x36;
> + }
> +
> + writel(0x0, &mctl_ctl->mrctrl1);
> + writel(0x800000f0, &mctl_ctl->mrctrl0);
> + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
> +
> + writel(0x100 | mr1, &mctl_ctl->mrctrl1);
> + writel(0x800000f0, &mctl_ctl->mrctrl0);
> + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
> +
> + writel(0x200 | mr2, &mctl_ctl->mrctrl1);
> + writel(0x800000f0, &mctl_ctl->mrctrl0);
> + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
> +
> + writel(0x333, &mctl_ctl->mrctrl1);
> + writel(0x800000f0, &mctl_ctl->mrctrl0);
> + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
> +
> + writel(0x403, &mctl_ctl->mrctrl1);
> + writel(0x800000f0, &mctl_ctl->mrctrl0);
> + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
> +
> + writel(0xb04, &mctl_ctl->mrctrl1);
> + writel(0x800000f0, &mctl_ctl->mrctrl0);
> + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
> +
> + writel(0xc72, &mctl_ctl->mrctrl1);
> + writel(0x800000f0, &mctl_ctl->mrctrl0);
> + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
> +
> + writel(0xd00, &mctl_ctl->mrctrl1);
> + writel(0x800000f0, &mctl_ctl->mrctrl0);
> + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
> +
> + writel(0xe08, &mctl_ctl->mrctrl1);
> + writel(0x800000f0, &mctl_ctl->mrctrl0);
> + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
> +
> + writel(0x1626, &mctl_ctl->mrctrl1);
> + writel(0x800000f0, &mctl_ctl->mrctrl0);
> + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0);
> + break;
> + default:
> + panic("This DRAM setup is currently not supported.\n");
> + };
> +
> + writel(0, &mctl_ctl->swctl);
> + clrbits_le32(&mctl_ctl->rfshctl3, 1);
> + writel(1, &mctl_ctl->swctl);
> +
> + if (para->tpr10 & TPR10_WRITE_LEVELING) {
> + for (i = 0; i < 5; i++)
> + if (mctl_phy_write_leveling(para, config))
> + break;
> + if (i == 5) {
> + debug("write leveling failed!\n");
> + return false;
> + }
> + }
> +
> + if (para->tpr10 & TPR10_READ_CALIBRATION) {
> + for (i = 0; i < 5; i++)
> + if (mctl_phy_read_calibration(para, config))
> + break;
> + if (i == 5) {
> + debug("read calibration failed!\n");
> + return false;
> + }
> + }
> +
> + if (para->tpr10 & TPR10_READ_TRAINING) {
> + for (i = 0; i < 5; i++)
> + if (mctl_phy_read_training(para, config))
> + break;
> + if (i == 5) {
> + debug("read training failed!\n");
> + return false;
> + }
> + }
> +
> + if (para->tpr10 & TPR10_WRITE_TRAINING) {
> + for (i = 0; i < 5; i++)
> + if (mctl_phy_write_training(config))
> + break;
> + if (i == 5) {
> + debug("write training failed!\n");
> + return false;
> + }
> + }
> +
> + mctl_phy_bit_delay_compensation(para, config);
> +
> + return true;
> +}
> +
> +static bool mctl_ctrl_init(const struct dram_para *para,
> + const struct dram_config *config)
> +{
> + void * const mctl_com = (void *)SUNXI_DRAM_COM_BASE;
> + struct sunxi_mctl_ctl_reg * const mctl_ctl =
> + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
> + u32 reg_val;
> +
> + clrsetbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24), BIT(25) | BIT(9));
> + setbits_le32(mctl_com + MCTL_COM_MAER0, BIT(15) | BIT(9));
> +
> + if (para->type == SUNXI_DRAM_TYPE_LPDDR4) {
> + setbits_le32(0x02023ea8, 1); // NSI
> + setbits_le32(0x02071008, 1); // NSI_CPU
> + }
> +
> + clrsetbits_le32(&mctl_ctl->sched[0], 0xff08, 0x3000);
> + clrsetbits_le32(&mctl_ctl->sched[1], 0x77000000, 0x33000000);
> + clrsetbits_le32(&mctl_ctl->unk_0x270, 0xffff, 0x808);
> + clrsetbits_le32(&mctl_ctl->unk_0x264, 0xff00ffff, 0x1f000030);
> +
> + writel(0, &mctl_ctl->hwlpctl);
> +
> + reg_val = MSTR_ACTIVE_RANKS(config->ranks);
> + switch (para->type) {
> + case SUNXI_DRAM_TYPE_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 + MCTL_COM_UNK_008, BIT(24), BIT(9));
> + udelay(1);
> + /* this write seems to enable PHY MMIO region */
> + setbits_le32(mctl_com + MCTL_COM_UNK_008, BIT(24));
> +
> + if (!mctl_phy_init(para, config))
> + return false;
> +
> + writel(0, &mctl_ctl->swctl);
> + clrbits_le32(&mctl_ctl->rfshctl3, BIT(0));
> + writel(1, &mctl_ctl->swctl);
> + mctl_await_completion(&mctl_ctl->swstat, 1, 1);
> +
> + return true;
> +}
> +
> +static bool mctl_core_init(const struct dram_para *para,
> + const struct dram_config *config)
> +{
> + mctl_sys_init(config->clk);
> +
> + return mctl_ctrl_init(para, config);
> +}
> +
> +static void mctl_auto_detect_rank_width(const struct dram_para *para,
> + struct dram_config *config)
> +{
> + /* this is minimum size that it's supported */
> + config->cols = 8;
> + config->rows = 13;
> +
> + /*
> + * Strategy here is to test most demanding combination first and least
> + * demanding last, otherwise HW might not be fully utilized. For
> + * example, half bus width and rank = 1 combination would also work
> + * on HW with full bus width and rank = 2, but only 1/4 RAM would be
> + * visible.
> + */
> +
> + debug("testing 32-bit width, rank = 2\n");
> + config->bus_full_width = 1;
> + config->ranks = 2;
> + if (mctl_core_init(para, config))
> + return;
> +
> + debug("testing 32-bit width, rank = 1\n");
> + config->bus_full_width = 1;
> + config->ranks = 1;
> + if (mctl_core_init(para, config))
> + return;
> +
> + debug("testing 16-bit width, rank = 2\n");
> + config->bus_full_width = 0;
> + config->ranks = 2;
> + if (mctl_core_init(para, config))
> + return;
> +
> + debug("testing 16-bit width, rank = 1\n");
> + config->bus_full_width = 0;
> + config->ranks = 1;
> + if (mctl_core_init(para, config))
> + return;
> +
> + panic("This DRAM setup is currently not supported.\n");
> +}
> +
> +static void mctl_auto_detect_dram_size(const struct dram_para *para,
> + struct dram_config *config)
> +{
> + /* detect row address bits */
> + config->cols = 8;
> + config->rows = 16;
> + mctl_core_init(para, config);
> +
> + for (config->rows = 13; config->rows < 16; config->rows++) {
> + /* 8 banks, 8 bit per byte and 16/32 bit width */
> + if (mctl_mem_matches((1 << (config->rows + config->cols +
> + 4 + config->bus_full_width))))
> + break;
> + }
> +
> + /* detect column address bits */
> + config->cols = 11;
> + mctl_core_init(para, config);
> +
> + for (config->cols = 8; config->cols < 11; config->cols++) {
> + /* 8 bits per byte and 16/32 bit width */
> + if (mctl_mem_matches(1 << (config->cols + 1 +
> + config->bus_full_width)))
> + break;
> + }
> +}
> +
> +static unsigned long long mctl_calc_size(const struct dram_config *config)
> +{
> + u8 width = config->bus_full_width ? 4 : 2;
> +
> + /* 8 banks */
> + return (1ULL << (config->cols + config->rows + 3)) * width * config->ranks;
> +}
> +
> +static const struct dram_para para = {
> + .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(¶, &config);
> + mctl_auto_detect_dram_size(¶, &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(¶, &config);
> +
> + size = mctl_calc_size(&config);
> +
> + sunxi_nsi_init();
> + init_something();
> +
> + return size;
> +};
> diff --git a/arch/arm/mach-sunxi/dram_timings/Makefile b/arch/arm/mach-sunxi/dram_timings/Makefile
> index 4dc1f29fc08..41fee509d5d 100644
> --- a/arch/arm/mach-sunxi/dram_timings/Makefile
> +++ b/arch/arm/mach-sunxi/dram_timings/Makefile
> @@ -8,3 +8,4 @@ obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3) += h616_lpddr3.o
> obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4) += h616_lpddr4_2133.o
> obj-$(CONFIG_SUNXI_DRAM_A133_DDR4) += a133_ddr4.o
> obj-$(CONFIG_SUNXI_DRAM_A133_LPDDR4) += a133_lpddr4.o
> +obj-$(CONFIG_SUNXI_DRAM_A523_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..940a4d04d57
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/dram_timings/a523_lpddr4.c
> @@ -0,0 +1,119 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * sun55i A523 LPDDR4-2133 timings, as programmed by Allwinner's boot0
> + *
> + * (C) Copyright 2024 Jernej Skrabec <jernej.skrabec@gmail.com>
> + * (C) Copyright 2023 Mikhail Kalashnikov <iuncuim@gmail.com>
> + *
> + */
> +
> +#include <asm/arch/dram.h>
> +#include <asm/arch/cpu.h>
> +
> +void mctl_set_timing_params(u32 clk)
> +{
> + struct sunxi_mctl_ctl_reg * const mctl_ctl =
> + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE;
> + u8 tcl, tcwl, t_rdata_en, trtp, twr, tphy_wrlat;
> + unsigned int mr1, mr2;
> +
> + u8 tccd = 4;
> + u8 tfaw = ns_to_t(40, clk);
> + u8 trrd = max(ns_to_t(10, clk), 2);
> + u8 twtr = max(ns_to_t(10, clk), 4);
> + u8 trcd = max(ns_to_t(18, clk), 2);
> + u8 trc = ns_to_t(65, clk);
> + u8 txp = max(ns_to_t(8, clk), 2);
> + u8 trp = ns_to_t(21, clk);
> + u8 tras = ns_to_t(42, clk);
> + u16 trefi = ns_to_t(3904, clk) / 32;
> + u16 trfc = ns_to_t(280, clk);
> + u16 txsr = ns_to_t(290, clk);
> +
> + u8 tmrw = max(ns_to_t(14, clk), 5);
> + u8 tmod = 12;
> + u8 tcke = max(ns_to_t(15, clk), 2);
> + u8 tcksrx = max(ns_to_t(2, clk), 2);
> + u8 tcksre = max(ns_to_t(5, clk), 2);
> + u8 trasmax = (trefi * 9) / 32;
> +
> + if (clk <= 936) {
> + mr1 = 0x34;
> + mr2 = 0x1b;
> + tcl = 10;
> + tcwl = 5;
> + t_rdata_en = 17;
> + trtp = 4;
> + tphy_wrlat = 5;
> + twr = 10;
> + } else if (clk <= 1200) {
> + mr1 = 0x54;
> + mr2 = 0x2d;
> + tcl = 14;
> + tcwl = 7;
> + t_rdata_en = 25;
> + trtp = 6;
> + tphy_wrlat = 9;
> + twr = 15;
> + } else {
> + mr1 = 0x64;
> + mr2 = 0x36;
> + tcl = 16;
> + tcwl = 8;
> + t_rdata_en = 29;
> + trtp = 7;
> + tphy_wrlat = 11;
> + twr = 17;
> + }
> +
> + u8 tmrd = tmrw;
> + u8 tckesr = tcke;
> + u8 twtp = twr + 9 + tcwl;
> + u8 twr2rd = twtr + 9 + tcwl;
> + u8 trd2wr = ns_to_t(4, clk) + 7 - ns_to_t(1, clk) + tcl;
> + u8 txs = 4;
> + u8 txsdll = 16;
> + u8 txsabort = 4;
> + u8 txsfast = 4;
> +
> + /* set DRAM timing */
> + writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,
> + &mctl_ctl->dramtmg[0]);
> + writel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]);
> + writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd,
> + &mctl_ctl->dramtmg[2]);
> + writel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]);
> + writel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp,
> + &mctl_ctl->dramtmg[4]);
> + writel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke,
> + &mctl_ctl->dramtmg[5]);
> + /* Value suggested by ZynqMP manual and used by libdram */
> + writel((txp + 2) | 0x02020000, &mctl_ctl->dramtmg[6]);
> + writel((txsfast << 24) | (txsabort << 16) | (txsdll << 8) | txs,
> + &mctl_ctl->dramtmg[8]);
> + writel(0x00020208, &mctl_ctl->dramtmg[9]);
> + writel(0xE0C05, &mctl_ctl->dramtmg[10]);
> + writel(0x440C021C, &mctl_ctl->dramtmg[11]);
> + writel(8, &mctl_ctl->dramtmg[12]);
> + writel(0xA100002, &mctl_ctl->dramtmg[13]);
> + writel(txsr, &mctl_ctl->dramtmg[14]);
> +
> + clrsetbits_le32(&mctl_ctl->init[0], 0xC0000FFF, 0x558);
> + writel(0x01f20000, &mctl_ctl->init[1]);
> + writel(0x00001705, &mctl_ctl->init[2]);
> + writel(0, &mctl_ctl->dfimisc);
> + writel((mr1 << 16) | mr2, &mctl_ctl->init[3]);
> + writel(0x00330000, &mctl_ctl->init[4]);
> + writel(0x00040072, &mctl_ctl->init[6]);
> + writel(0x00260008, &mctl_ctl->init[7]);
> +
> + clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660);
> +
> + /* Configure DFI timing */
> + writel(tphy_wrlat | 0x2000000 | (t_rdata_en << 16) | 0x808000,
> + &mctl_ctl->dfitmg0);
> + writel(0x100202, &mctl_ctl->dfitmg1);
> +
> + /* set refresh timing */
> + writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
> +}
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v2 14/20] sunxi: A523: add DDR3 DRAM support
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (12 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 13/20] sunxi: A523: add DRAM initialisation routine Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-25 4:44 ` Mikhail Kalashnikov
` (2 more replies)
2025-07-17 23:54 ` [PATCH v2 15/20] sunxi: add basic A523 support Andre Przywara
` (6 subsequent siblings)
20 siblings, 3 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 | 134 ++++++++++++++++++
4 files changed, 273 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 8aa5f1b46bf..8a19534c2ec 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -676,6 +676,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 a5c4fba7784..30bbeb40d0b 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;
@@ -920,6 +938,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;
@@ -941,6 +979,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);
@@ -961,6 +1029,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;
@@ -972,6 +1049,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;
@@ -1002,17 +1088,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);
@@ -1077,6 +1168,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;
@@ -1207,6 +1315,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;
@@ -1225,6 +1336,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;
@@ -1359,7 +1473,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,
@@ -1433,6 +1551,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 41fee509d5d..5de9fd5aab4 100644
--- a/arch/arm/mach-sunxi/dram_timings/Makefile
+++ b/arch/arm/mach-sunxi/dram_timings/Makefile
@@ -8,4 +8,5 @@ obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3) += h616_lpddr3.o
obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4) += h616_lpddr4_2133.o
obj-$(CONFIG_SUNXI_DRAM_A133_DDR4) += a133_ddr4.o
obj-$(CONFIG_SUNXI_DRAM_A133_LPDDR4) += a133_lpddr4.o
+obj-$(CONFIG_SUNXI_DRAM_A523_DDR3) += a523_ddr3.o
obj-$(CONFIG_SUNXI_DRAM_A523_LPDDR4) += a523_lpddr4.o
diff --git a/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c b/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
new file mode 100644
index 00000000000..6db0ea30f7c
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 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>
+ */
+
+#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] 32+ messages in thread* Re: [PATCH v2 14/20] sunxi: A523: add DDR3 DRAM support
2025-07-17 23:54 ` [PATCH v2 14/20] sunxi: A523: add DDR3 DRAM support Andre Przywara
@ 2025-07-25 4:44 ` Mikhail Kalashnikov
2025-07-25 23:48 ` Andre Przywara
2025-07-26 0:04 ` [PATCH] FIXUP! a523: DDR3: rework Andre Przywara
2 siblings, 0 replies; 32+ messages in thread
From: Mikhail Kalashnikov @ 2025-07-25 4:44 UTC (permalink / raw)
To: Andre Przywara, u-boot
Cc: Jernej Skrabec, Yixun Lan, Paul Kocialkowski, linux-sunxi,
Tom Rini
Signed-off-by: Mikhail Kalashnikov <iuncuim@gmail.com>
On 7/18/25 7:54 AM, Andre Przywara wrote:
> 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 | 134 ++++++++++++++++++
> 4 files changed, 273 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 8aa5f1b46bf..8a19534c2ec 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -676,6 +676,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 a5c4fba7784..30bbeb40d0b 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;
> @@ -920,6 +938,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;
> @@ -941,6 +979,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);
> @@ -961,6 +1029,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;
> @@ -972,6 +1049,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;
> @@ -1002,17 +1088,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);
> @@ -1077,6 +1168,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;
> @@ -1207,6 +1315,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;
> @@ -1225,6 +1336,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;
> @@ -1359,7 +1473,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,
> @@ -1433,6 +1551,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 41fee509d5d..5de9fd5aab4 100644
> --- a/arch/arm/mach-sunxi/dram_timings/Makefile
> +++ b/arch/arm/mach-sunxi/dram_timings/Makefile
> @@ -8,4 +8,5 @@ obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR3) += h616_lpddr3.o
> obj-$(CONFIG_SUNXI_DRAM_H616_LPDDR4) += h616_lpddr4_2133.o
> obj-$(CONFIG_SUNXI_DRAM_A133_DDR4) += a133_ddr4.o
> obj-$(CONFIG_SUNXI_DRAM_A133_LPDDR4) += a133_lpddr4.o
> +obj-$(CONFIG_SUNXI_DRAM_A523_DDR3) += a523_ddr3.o
> obj-$(CONFIG_SUNXI_DRAM_A523_LPDDR4) += a523_lpddr4.o
> diff --git a/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c b/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
> new file mode 100644
> index 00000000000..6db0ea30f7c
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
> @@ -0,0 +1,134 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * 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>
> + */
> +
> +#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);
> +}
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v2 14/20] sunxi: A523: add DDR3 DRAM support
2025-07-17 23:54 ` [PATCH v2 14/20] sunxi: A523: add DDR3 DRAM support Andre Przywara
2025-07-25 4:44 ` Mikhail Kalashnikov
@ 2025-07-25 23:48 ` Andre Przywara
2025-07-26 0:04 ` [PATCH] FIXUP! a523: DDR3: rework Andre Przywara
2 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-25 23:48 UTC (permalink / raw)
To: Mikhail Kalashnikov
Cc: u-boot, Jernej Skrabec, Yixun Lan, Paul Kocialkowski, linux-sunxi,
Tom Rini
On Fri, 18 Jul 2025 00:54:49 +0100
Andre Przywara <andre.przywara@arm.com> wrote:
Hi Mikhail,
> From: Mikhail Kalashnikov <iuncuim@gmail.com>
many thanks for the Signed-off-by: line in the other mail, I added this
now. And also not sure I thanked you already for providing the code in
the first place, that's much appreciated, since it enables this neat TV
box ;-)
So when this was running in the U-Boot gitlab CI, which every patch has
to pass for getting merged, it complained about the compiler warnings,
which I was tempted to ignore before (because I saw they weren't easy to
fix). Since this isn't an option now (the CI runs with -Werror), I bit
the bullet and looked into the problem, see below ...
> 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 | 134 ++++++++++++++++++
> 4 files changed, 273 insertions(+), 6 deletions(-)
> create mode 100644 arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
[ .... ]
> 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..6db0ea30f7c
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
> @@ -0,0 +1,134 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * 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>
> + */
> +
> +#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) {
This and the even higher frequencies below make me think you copied
Jernej's LPDDR4 timings code, but the timing values, frequencies and
equation are LPDDR4 specific, and don't work for DDR3.
Which explains why ...
> + 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]);
... you ignore the calculated values right here ^^^, since they don't
match at all with what boot0 wrote into those registers. Instead those
values are hardcoded below here.
So I had a look at the H616 and H6 DDR3 code, and found the equations
much more meaningful there, to the point where they almost perfectly
match the values you force here.
So I went through all the registers, and corrected the calculation of
the timing parameters to match the other DDR3 code, so I could use the
commented lines above. As an example, those are the changes for the
first register:
- u8 twtp = twr + 9 + tcwl;
+ u8 twtp = twr + 2 + tcwl;
- u8 tfaw = ns_to_t(40, clk);
+ u8 tfaw = ns_to_t(50, clk);
- u8 trasmax = (trefi * 9) / 32;
+ u8 trasmax = (clk / 2) / 15; /* tREFI * 9 */
- u8 tras = ns_to_t(42, clk);
+ u8 tras = ns_to_t(38, clk);
...
- // writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,
- // &mctl_ctl->dramtmg[0]);
- writel(0x0e141a10, &mctl_ctl->dramtmg[0]);
+ writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,
+ &mctl_ctl->dramtmg[0]);
I verified that the timing values now calculated for the used 792 MHz
were identical to those hardcoded before. Of course the values are
different for the preliminary 360 MHz setup during the probing runs,
but it still works nicely for me.
I even managed to boot with 912 MHz (DDR3-1866), and with slightly
shorter tCL and tCWL latencies (matching those described for
DDR3-1600), but haven't done any benchmarking or stability tests yet.
So I have changed your patch to accommodate the proper DDR3
calculations, which now compiles without warnings, so should pass the CI.
Hope that's fine for you, I will post the diff for your reference in a
separate reply mail.
Cheers,
Andre
> + 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);
> +}
^ permalink raw reply [flat|nested] 32+ messages in thread* [PATCH] FIXUP! a523: DDR3: rework
2025-07-17 23:54 ` [PATCH v2 14/20] sunxi: A523: add DDR3 DRAM support Andre Przywara
2025-07-25 4:44 ` Mikhail Kalashnikov
2025-07-25 23:48 ` Andre Przywara
@ 2025-07-26 0:04 ` Andre Przywara
2 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-26 0:04 UTC (permalink / raw)
To: Mikhail Kalashnikov
Cc: u-boot, Jernej Skrabec, Yixun Lan, Paul Kocialkowski, linux-sunxi,
Tom Rini
The equations used to calculate the timing values for the DDR3 setup on
the Allwinner A523 SoC were copied from the LPDDR4 code, but DDR3 is
significantly different, so the results don't match what boot0 wrote and
they wouldn't work. Consequently the current code ignored the
calculations and used hardcoded values matching those from the BSP, but
that leads to compiler warnings due to the unused code.
Use the equations from the H6 and H616 DDR3 timings code instead, with
some slight adjustments, to generate the exact same values the hardcoded
writes were producing before.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
arch/arm/mach-sunxi/dram_timings/a523_ddr3.c | 163 ++++++++-----------
1 file changed, 71 insertions(+), 92 deletions(-)
diff --git a/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c b/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
index 6db0ea30f7c..67e1af9cd66 100644
--- a/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
+++ b/arch/arm/mach-sunxi/dram_timings/a523_ddr3.c
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * sun55i A523 DDR3 timings, as programmed by Allwinner's boot0
+ * sun55i A523 DDR3 timings, as programmed by Allwinner's boot0 on
+ * the X96QPro+ TV box. As usual very conservative timings, but probably
+ * the most compatible and reliable.
*
* (C) Copyright 2024 Mikhail Kalashnikov <iuncuim@gmail.com>
- * Based on H6 DDR3 timings:
+ * Based on H616 DDR3 timings:
* (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
*/
@@ -14,121 +16,98 @@ 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);
+ /*
+ * formulas and constraints as of
+ * JEDEC DDR3 specification, for
+ * DDR3-1600, per JESD79-3F
+ */
+ u8 tccd = 2; /* 4nCK */
+ u8 tfaw = ns_to_t(50, clk);
+ u8 trrd = max(ns_to_t(6, clk), 4); /* max(6 ns, 4nCK) */
+ u8 twtr = max(ns_to_t(8, clk), 4); /* max(7.5 ns, 4nCK) */
+ u8 trcd = ns_to_t(15, clk); /* 13.5 ns */
+ u8 trc = ns_to_t(53, clk);
+ u8 txp = max(ns_to_t(8, clk), 2); /* max(6 ns, 3nCK) */
+ u8 trtp = max(ns_to_t(8, clk), 4); /* max(7.5 ns, 4nCK) */
+ u8 trp = ns_to_t(15, clk); /* >= 13.75 ns */
+ u8 tras = ns_to_t(38, clk);
+ u16 trefi = ns_to_t(11350, clk) / 32;
+ u16 trfc = ns_to_t(360, clk); /* 160 ns for 2Gb */
+ u16 txsr = 4;
- u8 tmrw = 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;
+ u8 tmrw = 0;
+ u8 tmrd = 4; /* 4nCK */
- 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 tmod = max(ns_to_t(15, clk), 12); /* max(15 ns, 12nCK) */
+ u8 tcke = max(ns_to_t(6, clk), 4); /* max(5.625 ns, 3nCK)*/
+ u8 tcksrx = max(ns_to_t(10, clk), 4); /* max(10 ns, 5nCK) */
+ u8 tcksre = max(ns_to_t(10, clk), 4); /* max(10 ns, 5nCK) */
+ u8 trasmax = (clk / 2) / 15; /* tREFI * 9 */
- 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;
+ /*
+ * TODO: support multiple DDR3 speed grades, these values below match
+ * the worst case for DDR3-2133, so should be good for all frequencies,
+ * but use the most conversative timings.
+ * DDR3-1866 (DRAM_CLK=912) should also work, or tcl=6 and tcwl=4 with
+ * DRAM_CLK=792. Maybe even the combination of both, depending on the
+ * particular device.
+ */
+ u8 tcl = 7; /* CAS latency: 14 */
+ u8 tcwl = 5; /* CAS write latency: 10 */
+ u8 t_rdata_en = 9;
+ u8 tphy_wrlat = 5;
+ u8 twr = 7;
+
+ u8 tckesr = tcke + 1; /* tCKE(min) + 1nCK */
+
+ u8 twtp = twr + 2 + tcwl;
+ u8 twr2rd = twtr + 2 + tcwl; /* (WL + BL / 2 + tWTR) / 2 */
+ u8 trd2wr = tcl + 3 - tcwl;
+ u8 txs = ns_to_t(360, clk) / 32; /* max(5nCK,tRFC+10ns)*/
+ u8 txsdll = 512 / 32; /* 512 nCK */
u8 txsabort = 4;
u8 txsfast = 4;
/* set DRAM timing */
- // writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras,
- // &mctl_ctl->dramtmg[0]);
- // writel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]);
- // writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd,
- // &mctl_ctl->dramtmg[2]);
- // writel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]);
- // writel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp,
- // &mctl_ctl->dramtmg[4]);
- // writel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke,
- // &mctl_ctl->dramtmg[5]);
- 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]);
+ 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(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]);
+ writel(0xa100002, &mctl_ctl->dramtmg[13]);
+ writel(txsr, &mctl_ctl->dramtmg[14]);
- //clrsetbits_le32(&mctl_ctl->init[0], 0xC0000FFF, 0x558);
- clrsetbits_le32(&mctl_ctl->init[0], 0xC0000FFF, 0x156);
+ 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]);
+ 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(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);
- writel(0x008c0000, &mctl_ctl->rfshtmg);
+ trfc = 0; /* as written so by boot0 */
+ writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg);
}
--
2.46.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v2 15/20] sunxi: add basic A523 support
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (13 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 14/20] sunxi: A523: add DDR3 DRAM support Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 16/20] arm64: dts: allwinner: Add Allwinner A523 .dtsi file Andre Przywara
` (5 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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 | 5 +++--
arch/arm/mach-sunxi/Kconfig | 24 +++++++++++++++++++++---
arch/arm/mach-sunxi/board.c | 5 +++++
arch/arm/mach-sunxi/cpu_info.c | 2 ++
board/sunxi/board.c | 7 ++++++-
common/spl/Kconfig | 4 +++-
include/configs/sun55i.h | 11 +++++++++++
7 files changed, 51 insertions(+), 7 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 6a7ec9a7ec1..ccddfaaf04c 100644
--- a/arch/arm/cpu/armv8/fel_utils.S
+++ b/arch/arm/cpu/armv8/fel_utils.S
@@ -41,10 +41,11 @@ ENTRY(return_to_fel)
str w2, [x1]
ldr w0, =0xfa50392f // CPU hotplug magic
-#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133)
+#if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133) || \
+ defined(CONFIG_MACH_SUN55I_A523)
ldr w2, =(SUNXI_R_CPUCFG_BASE + 0x1c0)
str w0, [x2], #0x4
-#elif CONFIG_MACH_SUN50I_H6
+#elif defined(CONFIG_MACH_SUN50I_H6)
ldr w2, =(SUNXI_RTC_BASE + 0x1b8) // BOOT_CPU_HP_FLAG_REG
str w0, [x2], #0x4
#else
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 8a19534c2ec..6a511c4fd39 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -214,6 +214,7 @@ config AXP_PMIC_BUS
config SUNXI_SRAM_ADDRESS
hex
default 0x10000 if MACH_SUN9I || MACH_SUN50I || MACH_SUN50I_H5
+ default 0x44000 if MACH_SUN55I_A523
default 0x20000 if SUN50I_GEN_H6 || SUNXI_GEN_NCAT2
default 0x0
---help---
@@ -226,6 +227,7 @@ config SUNXI_RVBAR_ADDRESS
hex
depends on ARM64
default 0x08100040 if MACH_SUN50I_A133
+ default 0x08000040 if MACH_SUN55I_A523
default 0x09010040 if SUN50I_GEN_H6
default 0x017000a0
---help---
@@ -254,6 +256,7 @@ config SUNXI_BL31_BASE
default 0x00044000 if MACH_SUN50I || MACH_SUN50I_H5
default 0x40000000 if MACH_SUN50I_H616
default 0x00104000 if SUN50I_GEN_H6
+ default 0x00054000 if MACH_SUN55I_A523
default 0x0
help
Address where BL31 (TF-A) is loaded, or zero if BL31 is not used.
@@ -335,7 +338,7 @@ config MACH_SUNXI_H3_H5
# TODO: try out A80's 8GiB DRAM space
config SUNXI_DRAM_MAX_SIZE
hex
- default 0x100000000 if MACH_SUN50I_H616 || MACH_SUN50I_A133
+ default 0x100000000 if MACH_SUN50I_H616 || MACH_SUN50I_A133 || MACH_SUN55I_A523
default 0xC0000000 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN50I_H6
default 0x80000000
@@ -537,6 +540,16 @@ config MACH_SUN50I_A133
select SUN50I_GEN_H6
imply OF_UPSTREAM
+config MACH_SUN55I_A523
+ bool "sun55i (Allwinner A523/A527/T527/H728)"
+ select ARM64
+ select SUNXI_GEN_NCAT2
+ select SUNXI_NEW_PINCTRL
+ select DRAM_SUN55I_A523
+ select FIT
+ select SPL_LOAD_FIT if SPL
+ imply OF_UPSTREAM
+
endchoice
# The sun8i SoCs share a lot, this helps to avoid a lot of "if A23 || A33"
@@ -728,7 +741,9 @@ endif
config DRAM_ZQ
int "sunxi dram zq value"
- depends on !MACH_SUN50I_H616 && !MACH_SUN50I_A133
+ depends on !MACH_SUN50I_H616
+ depends on !MACH_SUN50I_A133
+ depends on !MACH_SUN55I_A523
default 123 if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I || \
MACH_SUN8I_A23 || MACH_SUN8I_A33 || MACH_SUN8I_A83T
default 127 if MACH_SUN7I
@@ -742,6 +757,7 @@ config DRAM_ZQ
config DRAM_ODT_EN
bool "sunxi dram odt enable"
depends on !MACH_SUN50I_H616
+ depends on !MACH_SUN55I_A523
default y if MACH_SUN8I_A23
default y if MACH_SUNXI_H3_H5
default y if MACH_SUN8I_R40
@@ -831,6 +847,7 @@ endif
config SYS_CLK_FREQ
default 408000000 if MACH_SUNIV
+ default 792000000 if MACH_SUN55I_A523
default 816000000 if MACH_SUN50I || MACH_SUN50I_H5
default 888000000 if MACH_SUN50I_H6
default 912000000 if MACH_SUN7I
@@ -849,6 +866,7 @@ config SYS_CONFIG_NAME
default "sun50i" if MACH_SUN50I_H6
default "sun50i" if MACH_SUN50I_H616
default "sun50i" if MACH_SUN50I_A133
+ default "sun55i" if MACH_SUN55I_A523
config SYS_BOARD
default "sunxi"
@@ -915,7 +933,7 @@ config I2C1_ENABLE
---help---
See I2C0_ENABLE help text.
-if SUNXI_GEN_SUN6I || SUN50I_GEN_H6
+if SUNXI_GEN_SUN6I || SUN50I_GEN_H6 || SUNXI_GEN_NCAT2
config R_I2C_ENABLE
bool "Enable the PRCM I2C/TWI controller"
# This is used for the pmic on H3
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 13caefda884..fb4837c2082 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -141,6 +141,10 @@ static int gpio_init(void)
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_H616_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN50I_H616_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP);
+#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN55I_A523)
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(9), 2);
+ sunxi_gpio_set_cfgpin(SUNXI_GPB(10), 2);
+ sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_A83T)
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_A83T_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN8I_A83T_GPB_UART0);
@@ -197,6 +201,7 @@ static int gpio_init(void)
if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
IS_ENABLED(CONFIG_SUN50I_GEN_NCAT2)) {
val = readl(SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_VAL);
+ /* TODO: A523: keep only the lower two bits? */
writel(val, SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_SEL);
}
if (IS_ENABLED(CONFIG_SUN50I_GEN_H6)) {
diff --git a/arch/arm/mach-sunxi/cpu_info.c b/arch/arm/mach-sunxi/cpu_info.c
index 3f4735d4717..c3a51d9956e 100644
--- a/arch/arm/mach-sunxi/cpu_info.c
+++ b/arch/arm/mach-sunxi/cpu_info.c
@@ -106,6 +106,8 @@ int print_cpuinfo(void)
puts("CPU: Allwinner H616 (SUN50I)\n");
#elif defined CONFIG_MACH_SUN50I_A133
puts("CPU: Allwinner A133 (SUN50I)\n");
+#elif defined CONFIG_MACH_SUN55I_A523
+ puts("CPU: Allwinner A523 (SUN55I)\n");
#else
#warning Please update cpu_info.c with correct CPU information
puts("CPU: SUNXI Family\n");
diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index 943b6221b8a..2929bc17f08 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -118,6 +118,10 @@ void i2c_init_board(void)
clock_twi_onoff(5, 1);
sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN50I_H616_GPL_R_TWI);
sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN50I_H616_GPL_R_TWI);
+#elif CONFIG_MACH_SUN55I_A523
+ clock_twi_onoff(5, 1);
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN50I_GPL_R_TWI);
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN50I_GPL_R_TWI);
#else
clock_twi_onoff(5, 1);
sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_H3_GPL_R_TWI);
@@ -435,7 +439,8 @@ static void mmc_pinmux_setup(int sdc)
sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP);
sunxi_gpio_set_drv(pin, 2);
}
-#elif defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133)
+#elif defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_A133) || \
+ defined(CONFIG_MACH_SUN55I_A523)
/* SDC2: PC0-PC1, PC5-PC6, PC8-PC11, PC13-PC16 */
for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(16); pin++) {
if (pin > SUNXI_GPC(1) && pin < SUNXI_GPC(5))
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index f69eff21107..f89f20a598e 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -80,7 +80,7 @@ config SPL_MAX_SIZE
default 0x1b000 if AM33XX && !TI_SECURE_DEVICE
default 0xec00 if OMAP34XX
default 0x10000 if ARCH_MX6 && !MX6_OCRAM_256KB
- default 0xbfa0 if MACH_SUN50I_H616 || MACH_SUN50I_A133
+ default 0xbfa0 if MACH_SUN50I_H616 || MACH_SUN50I_A133 || MACH_SUN55I_A523
default 0x7000 if RCAR_GEN3
default 0x5fa0 if SUNXI_SRAM_ADDRESS = 0x0
default 0x7fa0 if ARCH_SUNXI
@@ -278,6 +278,7 @@ config SPL_TEXT_BASE
default 0x00912000 if ARCH_MX7
default 0x40301350 if OMAP54XX
default 0x10060 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN9I
+ default 0x44060 if MACH_SUN55I_A523
default 0x20060 if SUN50I_GEN_H6 || SUNXI_GEN_NCAT2
default 0x00060 if ARCH_SUNXI
default 0xfffc0000 if ARCH_ZYNQMP
@@ -425,6 +426,7 @@ config SPL_STACK
default 0x118000 if MACH_SUN50I_H6
default 0x52a00 if MACH_SUN50I_H616
default 0x40000 if MACH_SUN8I_R528 || MACH_SUN50I_A133
+ default 0x44000 if MACH_SUN55I_A523
default 0x54000 if MACH_SUN50I || MACH_SUN50I_H5
default 0x18000 if MACH_SUN9I
default 0x8000 if ARCH_SUNXI
diff --git a/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] 32+ messages in thread* [PATCH v2 16/20] arm64: dts: allwinner: Add Allwinner A523 .dtsi file
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (14 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 15/20] sunxi: add basic A523 support Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 17/20] arm64: dts: allwinner: a523: add X96Q-Pro+ support Andre Przywara
` (4 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
The Allwinner A523, and its siblings A527 and T527, which share the same
die, are a new family of SoCs introduced in 2023. They features eight
Arm Cortex-A55 cores, and, among the other usual peripherals, a PCIe and
USB 3.0 controller.
Add the basic SoC devicetree .dtsi for the chip, describing the
fundamental peripherals: the cores, GIC, timer, RTC, CCU and pinctrl.
Also some other peripherals are fully compatible with previous IP, so
add the USB and MMC nodes as well.
The other peripherals will be added in the future, once we understand
their compatibility and DT requirements.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Link: https://patch.msgid.link/20250307005712.16828-9-andre.przywara@arm.com
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
[ upstream commit: 35ac96f796649346c9b0440413dc6c5138249b3e ]
(cherry picked from commit 247a3572abcfd7a0d48e12f8f810f1cbae5ce4f4)
---
.../src/arm64/allwinner/sun55i-a523.dtsi | 598 ++++++++++++++++++
1 file changed, 598 insertions(+)
create mode 100644 dts/upstream/src/arm64/allwinner/sun55i-a523.dtsi
diff --git a/dts/upstream/src/arm64/allwinner/sun55i-a523.dtsi b/dts/upstream/src/arm64/allwinner/sun55i-a523.dtsi
new file mode 100644
index 00000000000..ee485899ba0
--- /dev/null
+++ b/dts/upstream/src/arm64/allwinner/sun55i-a523.dtsi
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+// Copyright (C) 2023-2024 Arm Ltd.
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/clock/sun6i-rtc.h>
+#include <dt-bindings/clock/sun55i-a523-ccu.h>
+#include <dt-bindings/clock/sun55i-a523-r-ccu.h>
+#include <dt-bindings/reset/sun55i-a523-ccu.h>
+#include <dt-bindings/reset/sun55i-a523-r-ccu.h>
+
+/ {
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x000>;
+ enable-method = "psci";
+ };
+
+ cpu1: cpu@100 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x100>;
+ enable-method = "psci";
+ };
+
+ cpu2: cpu@200 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x200>;
+ enable-method = "psci";
+ };
+
+ cpu3: cpu@300 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x300>;
+ enable-method = "psci";
+ };
+
+ cpu4: cpu@400 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x400>;
+ enable-method = "psci";
+ };
+
+ cpu5: cpu@500 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x500>;
+ enable-method = "psci";
+ };
+
+ cpu6: cpu@600 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x600>;
+ enable-method = "psci";
+ };
+
+ cpu7: cpu@700 {
+ compatible = "arm,cortex-a55";
+ device_type = "cpu";
+ reg = <0x700>;
+ enable-method = "psci";
+ };
+ };
+
+ osc24M: osc24M-clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ clock-output-names = "osc24M";
+ };
+
+ pmu {
+ compatible = "arm,cortex-a55-pmu";
+ interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ psci {
+ compatible = "arm,psci-0.2";
+ method = "smc";
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ arm,no-tick-in-suspend;
+ interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_PPI 14 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_PPI 10 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x0 0x0 0x40000000>;
+
+ pio: pinctrl@2000000 {
+ compatible = "allwinner,sun55i-a523-pinctrl";
+ reg = <0x2000000 0x800>;
+ interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_APB1>, <&osc24M>, <&rtc CLK_OSC32K>;
+ clock-names = "apb", "hosc", "losc";
+ gpio-controller;
+ #gpio-cells = <3>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ mmc0_pins: mmc0-pins {
+ pins = "PF0" ,"PF1", "PF2", "PF3", "PF4", "PF5";
+ allwinner,pinmux = <2>;
+ function = "mmc0";
+ drive-strength = <30>;
+ bias-pull-up;
+ };
+
+ /omit-if-no-ref/
+ mmc1_pins: mmc1-pins {
+ pins = "PG0" ,"PG1", "PG2", "PG3", "PG4", "PG5";
+ allwinner,pinmux = <2>;
+ function = "mmc1";
+ drive-strength = <30>;
+ bias-pull-up;
+ };
+
+ mmc2_pins: mmc2-pins {
+ pins = "PC0", "PC1" ,"PC5", "PC6", "PC8",
+ "PC9", "PC10", "PC11", "PC13", "PC14",
+ "PC15", "PC16";
+ allwinner,pinmux = <3>;
+ function = "mmc2";
+ drive-strength = <30>;
+ bias-pull-up;
+ };
+
+ uart0_pb_pins: uart0-pb-pins {
+ pins = "PB9", "PB10";
+ allwinner,pinmux = <2>;
+ function = "uart0";
+ };
+ };
+
+ ccu: clock-controller@2001000 {
+ compatible = "allwinner,sun55i-a523-ccu";
+ reg = <0x02001000 0x1000>;
+ clocks = <&osc24M>, <&rtc CLK_OSC32K>,
+ <&rtc CLK_IOSC>, <&rtc CLK_OSC32K_FANOUT>;
+ clock-names = "hosc", "losc",
+ "iosc", "losc-fanout";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ mmc0: mmc@4020000 {
+ compatible = "allwinner,sun55i-a523-mmc",
+ "allwinner,sun20i-d1-mmc";
+ reg = <0x04020000 0x1000>;
+ clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>;
+ clock-names = "ahb", "mmc";
+ resets = <&ccu RST_BUS_MMC0>;
+ reset-names = "ahb";
+ interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc0_pins>;
+ status = "disabled";
+
+ max-frequency = <150000000>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ cap-sdio-irq;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ mmc1: mmc@4021000 {
+ compatible = "allwinner,sun55i-a523-mmc",
+ "allwinner,sun20i-d1-mmc";
+ reg = <0x04021000 0x1000>;
+ clocks = <&ccu CLK_BUS_MMC1>, <&ccu CLK_MMC1>;
+ clock-names = "ahb", "mmc";
+ resets = <&ccu RST_BUS_MMC1>;
+ reset-names = "ahb";
+ interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
+ status = "disabled";
+
+ max-frequency = <150000000>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ cap-sdio-irq;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ mmc2: mmc@4022000 {
+ compatible = "allwinner,sun55i-a523-mmc",
+ "allwinner,sun20i-d1-mmc";
+ reg = <0x04022000 0x1000>;
+ clocks = <&ccu CLK_BUS_MMC2>, <&ccu CLK_MMC2>;
+ clock-names = "ahb", "mmc";
+ resets = <&ccu RST_BUS_MMC2>;
+ reset-names = "ahb";
+ interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc2_pins>;
+ status = "disabled";
+
+ max-frequency = <150000000>;
+ cap-sd-highspeed;
+ cap-mmc-highspeed;
+ cap-sdio-irq;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ wdt: watchdog@2050000 {
+ compatible = "allwinner,sun55i-a523-wdt";
+ reg = <0x2050000 0x20>;
+ interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&osc24M>, <&rtc CLK_OSC32K>;
+ clock-names = "hosc", "losc";
+ status = "okay";
+ };
+
+ uart0: serial@2500000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02500000 0x400>;
+ interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART0>;
+ resets = <&ccu RST_BUS_UART0>;
+ status = "disabled";
+ };
+
+ uart1: serial@2500400 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02500400 0x400>;
+ interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART1>;
+ resets = <&ccu RST_BUS_UART1>;
+ status = "disabled";
+ };
+
+ uart2: serial@2500800 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02500800 0x400>;
+ interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART2>;
+ resets = <&ccu RST_BUS_UART2>;
+ status = "disabled";
+ };
+
+ uart3: serial@2500c00 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02500c00 0x400>;
+ interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART3>;
+ resets = <&ccu RST_BUS_UART3>;
+ status = "disabled";
+ };
+
+ uart4: serial@2501000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02501000 0x400>;
+ interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART4>;
+ resets = <&ccu RST_BUS_UART4>;
+ status = "disabled";
+ };
+
+ uart5: serial@2501400 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02501400 0x400>;
+ interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART5>;
+ resets = <&ccu RST_BUS_UART5>;
+ status = "disabled";
+ };
+
+ uart6: serial@2501800 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02501800 0x400>;
+ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART6>;
+ resets = <&ccu RST_BUS_UART6>;
+ status = "disabled";
+ };
+
+ uart7: serial@2501c00 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x02501c00 0x400>;
+ interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ clocks = <&ccu CLK_BUS_UART7>;
+ resets = <&ccu RST_BUS_UART7>;
+ status = "disabled";
+ };
+
+ i2c0: i2c@2502000 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2502000 0x400>;
+ interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C0>;
+ resets = <&ccu RST_BUS_I2C0>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c1: i2c@2502400 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2502400 0x400>;
+ interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C1>;
+ resets = <&ccu RST_BUS_I2C1>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c2: i2c@2502800 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2502800 0x400>;
+ interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C2>;
+ resets = <&ccu RST_BUS_I2C2>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c3: i2c@2502c00 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2502c00 0x400>;
+ interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C3>;
+ resets = <&ccu RST_BUS_I2C3>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c4: i2c@2503000 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2503000 0x400>;
+ interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C4>;
+ resets = <&ccu RST_BUS_I2C4>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ i2c5: i2c@2503400 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x2503400 0x400>;
+ interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2C5>;
+ resets = <&ccu RST_BUS_I2C5>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ gic: interrupt-controller@3400000 {
+ compatible = "arm,gic-v3";
+ #address-cells = <1>;
+ #interrupt-cells = <3>;
+ #size-cells = <1>;
+ ranges;
+ interrupt-controller;
+ reg = <0x3400000 0x10000>,
+ <0x3460000 0x100000>;
+ interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ dma-noncoherent;
+
+ its: msi-controller@3440000 {
+ compatible = "arm,gic-v3-its";
+ reg = <0x3440000 0x20000>;
+ msi-controller;
+ #msi-cells = <1>;
+ dma-noncoherent;
+ };
+ };
+
+ usb_otg: usb@4100000 {
+ compatible = "allwinner,sun55i-a523-musb",
+ "allwinner,sun8i-a33-musb";
+ reg = <0x4100000 0x400>;
+ interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "mc";
+ clocks = <&ccu CLK_BUS_OTG>;
+ resets = <&ccu RST_BUS_OTG>;
+ extcon = <&usbphy 0>;
+ phys = <&usbphy 0>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ usbphy: phy@4100400 {
+ compatible = "allwinner,sun55i-a523-usb-phy",
+ "allwinner,sun20i-d1-usb-phy";
+ reg = <0x4100400 0x100>,
+ <0x4101800 0x100>,
+ <0x4200800 0x100>;
+ reg-names = "phy_ctrl",
+ "pmu0",
+ "pmu1";
+ clocks = <&osc24M>,
+ <&osc24M>;
+ clock-names = "usb0_phy",
+ "usb1_phy";
+ resets = <&ccu RST_USB_PHY0>,
+ <&ccu RST_USB_PHY1>;
+ reset-names = "usb0_reset",
+ "usb1_reset";
+ status = "disabled";
+ #phy-cells = <1>;
+ };
+
+ ehci0: usb@4101000 {
+ compatible = "allwinner,sun55i-a523-ehci",
+ "generic-ehci";
+ reg = <0x4101000 0x100>;
+ interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_OHCI0>,
+ <&ccu CLK_BUS_EHCI0>,
+ <&ccu CLK_USB_OHCI0>;
+ resets = <&ccu RST_BUS_OHCI0>,
+ <&ccu RST_BUS_EHCI0>;
+ phys = <&usbphy 0>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci0: usb@4101400 {
+ compatible = "allwinner,sun55i-a523-ohci",
+ "generic-ohci";
+ reg = <0x4101400 0x100>;
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_OHCI0>,
+ <&ccu CLK_USB_OHCI0>;
+ resets = <&ccu RST_BUS_OHCI0>;
+ phys = <&usbphy 0>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ehci1: usb@4200000 {
+ compatible = "allwinner,sun55i-a523-ehci",
+ "generic-ehci";
+ reg = <0x4200000 0x100>;
+ interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_OHCI1>,
+ <&ccu CLK_BUS_EHCI1>,
+ <&ccu CLK_USB_OHCI1>;
+ resets = <&ccu RST_BUS_OHCI1>,
+ <&ccu RST_BUS_EHCI1>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci1: usb@4200400 {
+ compatible = "allwinner,sun55i-a523-ohci",
+ "generic-ohci";
+ reg = <0x4200400 0x100>;
+ interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_OHCI1>,
+ <&ccu CLK_USB_OHCI1>;
+ resets = <&ccu RST_BUS_OHCI1>;
+ phys = <&usbphy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ r_ccu: clock-controller@7010000 {
+ compatible = "allwinner,sun55i-a523-r-ccu";
+ reg = <0x7010000 0x250>;
+ clocks = <&osc24M>,
+ <&rtc CLK_OSC32K>,
+ <&rtc CLK_IOSC>,
+ <&ccu CLK_PLL_PERIPH0_200M>,
+ <&ccu CLK_PLL_AUDIO0_4X>;
+ clock-names = "hosc",
+ "losc",
+ "iosc",
+ "pll-periph",
+ "pll-audio";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ nmi_intc: interrupt-controller@7010320 {
+ compatible = "allwinner,sun55i-a523-nmi";
+ reg = <0x07010320 0xc>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ r_pio: pinctrl@7022000 {
+ compatible = "allwinner,sun55i-a523-r-pinctrl";
+ reg = <0x7022000 0x800>;
+ interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&r_ccu CLK_R_APB0>,
+ <&osc24M>,
+ <&rtc CLK_OSC32K>;
+ clock-names = "apb", "hosc", "losc";
+ gpio-controller;
+ #gpio-cells = <3>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ r_i2c_pins: r-i2c-pins {
+ pins = "PL0" ,"PL1";
+ allwinner,pinmux = <2>;
+ function = "r_i2c0";
+ };
+ };
+
+ r_i2c0: i2c@7081400 {
+ compatible = "allwinner,sun55i-a523-i2c",
+ "allwinner,sun8i-v536-i2c",
+ "allwinner,sun6i-a31-i2c";
+ reg = <0x07081400 0x400>;
+ interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&r_ccu CLK_BUS_R_I2C0>;
+ resets = <&r_ccu RST_BUS_R_I2C0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&r_i2c_pins>;
+ status = "disabled";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ rtc: rtc@7090000 {
+ compatible = "allwinner,sun55i-a523-rtc",
+ "allwinner,sun50i-r329-rtc";
+ reg = <0x7090000 0x400>;
+ interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&r_ccu CLK_BUS_R_RTC>,
+ <&osc24M>,
+ <&r_ccu CLK_R_AHB>;
+ clock-names = "bus", "hosc", "ahb";
+ #clock-cells = <1>;
+ };
+ };
+};
--
2.46.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 17/20] arm64: dts: allwinner: a523: add X96Q-Pro+ support
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (15 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 16/20] arm64: dts: allwinner: Add Allwinner A523 .dtsi file Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 18/20] arm64: dts: allwinner: a523: add Radxa A5E support Andre Przywara
` (3 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
The X96QPro+ is a TV box using the Allwinner H728 SoC. That SoC seems to
be a package variant of the A523 family, at least it uses the same SoC
ID and is compatible as far as we can assess.
It comes with the following specs:
- Allwinner H728 SoC: 8 Arm Cortex-A55 cores, Mali-G57 MC1 GPU
- 2 or 4GiB DDR3L DRAM
- 32, 64, or 128 GiB eMMC flash
- AXP717 + AXP323 PMICs
- Gigabit Ethernet (using MAXIO PHY)
- HDMI port
- 2 * USB 2.0 ports
- 1 * USB 3.0 port
- microSD card slot
- TOSLINK digital audio output
- 3.5mm A/V port
- infrared sensor
- 7-segment display
- 5V barrel plug power supply
- power button
The PCB provides holes for soldering a UART header or cable, this is
connected to the debug UART0. There is another set of UART pins
available. The board also features a FEL button (accessible through the
3.5mm socket) and a reset button (only accessible when case is open).
This .dts just describes the basic peripherals as far as we support them
at the moment. The PMIC rail assignments are reverse engineered as far
as possible, by dumping them from a running Android system, and correlating
them to other boards using the same SoC.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Link: https://patch.msgid.link/20250307005712.16828-13-andre.przywara@arm.com
[wens@csie.org: Squash in SD card detect pull resistor fix]
Link: https://patch.msgid.link/20250425003422.3465-1-andre.przywara@arm.com
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
[ upstream commit: 4ee87d875071390b4e24ce46dbdd792216d61651 ]
(cherry picked from commit 693da0a03149b77a3e2bc11cfd314df8cc2fab40)
---
.../arm64/allwinner/sun55i-h728-x96qpro+.dts | 287 ++++++++++++++++++
1 file changed, 287 insertions(+)
create mode 100644 dts/upstream/src/arm64/allwinner/sun55i-h728-x96qpro+.dts
diff --git a/dts/upstream/src/arm64/allwinner/sun55i-h728-x96qpro+.dts b/dts/upstream/src/arm64/allwinner/sun55i-h728-x96qpro+.dts
new file mode 100644
index 00000000000..59db103546f
--- /dev/null
+++ b/dts/upstream/src/arm64/allwinner/sun55i-h728-x96qpro+.dts
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+// Copyright (C) 2024 Arm Ltd.
+
+/dts-v1/;
+
+#include "sun55i-a523.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ model = "X96Q Pro+";
+ compatible = "amediatech,x96q-pro-plus", "allwinner,sun55i-h728";
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ ext_osc32k: ext-osc32k-clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ clock-output-names = "ext_osc32k";
+ };
+
+ reg_vcc5v: vcc5v {
+ /* board wide 5V supply from the barrel plug */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-5v";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ reg_vcc3v3: vcc3v3 {
+ /* 3.3V dummy supply for the SD card */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ vin-supply = <®_vcc5v>;
+ regulator-always-on;
+ };
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&mmc0 {
+ vmmc-supply = <®_vcc3v3>;
+ cd-gpios = <&pio 5 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* PF6 */
+ bus-width = <4>;
+ disable-wp;
+ status = "okay";
+};
+
+&mmc2 {
+ vmmc-supply = <®_cldo3>;
+ vqmmc-supply = <®_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 = <®_cldo3>; /* via VCC-IO */
+ vcc-pc-supply = <®_cldo1>;
+ vcc-pd-supply = <®_dcdc4>;
+ vcc-pe-supply = <®_dcdc4>;
+ vcc-pf-supply = <®_cldo3>; /* actually switchable */
+ vcc-pg-supply = <®_bldo1>;
+ vcc-ph-supply = <®_cldo3>; /* via VCC-IO */
+ vcc-pi-supply = <®_dcdc4>;
+ vcc-pj-supply = <®_dcdc4>;
+ vcc-pk-supply = <®_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 = <®_vcc5v>;
+ vin2-supply = <®_vcc5v>;
+ vin3-supply = <®_vcc5v>;
+ vin4-supply = <®_vcc5v>;
+ aldoin-supply = <®_vcc5v>;
+ bldoin-supply = <®_vcc5v>;
+ cldoin-supply = <®_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 = <®_vcc5v>;
+ vin2-supply = <®_vcc5v>;
+ vin3-supply = <®_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 = <®_aldo3>;
+ */
+ vcc-pm-supply = <®_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";
+};
--
2.46.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 18/20] arm64: dts: allwinner: a523: add Radxa A5E support
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (16 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 17/20] arm64: dts: allwinner: a523: add X96Q-Pro+ support Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 19/20] arm64: dts: allwinner: a523: add Avaota-A1 router support Andre Przywara
` (2 subsequent siblings)
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
The Radxa A5E is a development board using the Allwinner A527 SoC, which
is using the same die as the A523 SoC, just exposing the pins of more
peripherals (like HDMI or the 2nd MAC). The board features:
- Allwinner A527/T527 SoC: 8 ARM Cortex-A55 cores, Mali-G57 MC1 GPU
- 1GiB/2GiB/4GiB LPDDR4 DRAM
- AXP717 + AXP323 PMICs
- Raspberry-Pi-2 compatible 40pin GPIO header
- 1 USB 2.0 type C port (OTG), also power supply
- 1 USB 3.0 type A host port (multiplexed with M.2 slot)
- 1 M.2 M-key 2230 slot, with 1 PCIe2.1 lane connected (multiplexed
with USB 3.0 port)
- MicroSD slot
- optional eMMC, 8, 16 or 32GB available
- optional on-board 16MiB bootable SPI NOR flash
- two 1Gbps Ethernet ports (via MAXIO MAE0621A PHYs)
- PoE header for optional supply circuit on one Ethernet port
- WiFi 802.11 a/b/g/n/ac/ax (LB-Link BL-M8800DS2 module using AIC8800)
- HDMI port
- camera and LCD connectors
- power supply via USB-C connector (but no PD) or GPIO header pins
This .dts describes the devices as far as we support them at the moment.
The PMIC rails have been assigned as per the schematics.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Link: https://patch.msgid.link/20250307005712.16828-14-andre.przywara@arm.com
[wens@csie.org: Squash in SD card detect pull resistor fix]
Link: https://patch.msgid.link/20250425003422.3465-1-andre.przywara@arm.com
[wens@csie.org: Rename dts file to sun55i-a527-cubie-a5e.dts]
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
[ upstream commit: c2520cd032ae8ca3fdaf77b3f3aa687c8cb7843f ]
(cherry picked from commit 91ad117321c0901094c1d6467df90f5f6757569a)
---
.../arm64/allwinner/sun55i-a527-cubie-a5e.dts | 299 ++++++++++++++++++
1 file changed, 299 insertions(+)
create mode 100644 dts/upstream/src/arm64/allwinner/sun55i-a527-cubie-a5e.dts
diff --git a/dts/upstream/src/arm64/allwinner/sun55i-a527-cubie-a5e.dts b/dts/upstream/src/arm64/allwinner/sun55i-a527-cubie-a5e.dts
new file mode 100644
index 00000000000..ad9bd6da424
--- /dev/null
+++ b/dts/upstream/src/arm64/allwinner/sun55i-a527-cubie-a5e.dts
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+// Copyright (C) 2025 Arm Ltd.
+
+/dts-v1/;
+
+#include "sun55i-a523.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ model = "Radxa A5E";
+ compatible = "radxa,cubie-a5e", "allwinner,sun55i-a527";
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ ext_osc32k: ext-osc32k-clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ clock-output-names = "ext_osc32k";
+ };
+
+ reg_vcc5v: vcc5v {
+ /* board wide 5V supply from the USB-C connector */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-5v";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ reg_usb_vbus: vbus {
+ compatible = "regulator-fixed";
+ regulator-name = "usb-vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <®_vcc5v>;
+ gpio = <&r_pio 0 8 GPIO_ACTIVE_HIGH>; /* PL8 */
+ enable-active-high;
+ };
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&mmc0 {
+ vmmc-supply = <®_cldo3>;
+ cd-gpios = <&pio 5 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* PF6 */
+ bus-width = <4>;
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&pio {
+ vcc-pb-supply = <®_cldo3>; /* via VCC-IO */
+ vcc-pc-supply = <®_cldo1>;
+ vcc-pd-supply = <®_cldo3>;
+ vcc-pe-supply = <®_aldo2>;
+ vcc-pf-supply = <®_cldo3>; /* actually switchable */
+ vcc-pg-supply = <®_bldo1>;
+ vcc-ph-supply = <®_cldo3>; /* via VCC-IO */
+ vcc-pi-supply = <®_cldo3>;
+ vcc-pj-supply = <®_cldo4>;
+ vcc-pk-supply = <®_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 = <®_vcc5v>;
+ vin2-supply = <®_vcc5v>;
+ vin3-supply = <®_vcc5v>;
+ vin4-supply = <®_vcc5v>;
+ aldoin-supply = <®_vcc5v>;
+ bldoin-supply = <®_vcc5v>;
+ cldoin-supply = <®_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 = <®_vcc5v>;
+ vin2-supply = <®_vcc5v>;
+ vin3-supply = <®_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 = <®_aldo3>;
+ */
+ vcc-pm-supply = <®_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 = <®_vcc5v>;
+ usb1_vbus-supply = <®_usb_vbus>;
+ status = "okay";
+};
--
2.46.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 19/20] arm64: dts: allwinner: a523: add Avaota-A1 router support
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (17 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 18/20] arm64: dts: allwinner: a523: add Radxa A5E support Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-17 23:54 ` [PATCH v2 20/20] sunxi: A523: add defconfigs for three boards Andre Przywara
2025-07-19 2:05 ` [PATCH v2 00/20] sunxi: Add Allwinner A523 support Yixun Lan
20 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
The Avaota A1 router board is an Open Source hardware board, designed
by YuzukiHD. Pine64 produces some boards and sells them. It uses the
Allwinner A527 or T527 SoC, and comes with the following features:
- Eight ARM Cortex-A55 cores, Mali-G57 MC1 GPU
- 1GiB/2GiB/4GiB LPDDR4 DRAM
- AXP717 + AXP323 PMIC
- Raspberry-Pi-2 compatible GPIO header
- 1 USB 2.0 type A host port, 1 USB 3.0 type A host post
- 1 USB 2.0 type C port (OTG + serial debug)
- MicroSD slot
- eMMC between 16 and 128 GiB
- on-board 16MiB bootable SPI NOR flash
- two 1Gbps Ethernet ports (via RTL8211F PHYs)
- HDMI port
- DP port
- camera and LCD connectors
- 3.5mm headphone jack
- (yet) unsupported WiFi/BT chip
- 1.3" LC display, connected via SPI
- 12 V barrel plug for power supply
Add the devicetree file describing the currently supported features.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Link: https://patch.msgid.link/20250307005712.16828-12-andre.przywara@arm.com
[wens@csie.org: Squash in SD card detect pull resistor fix]
Link: https://patch.msgid.link/20250425003422.3465-1-andre.przywara@arm.com
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
[ upstream commit: dbe54efa32afe5b82763c015cbe9e64c4d4e117a ]
(cherry picked from commit ebcb8469ef4336c05c6b9f409714a23cfc891fff)
---
.../arm64/allwinner/sun55i-t527-avaota-a1.dts | 308 ++++++++++++++++++
1 file changed, 308 insertions(+)
create mode 100644 dts/upstream/src/arm64/allwinner/sun55i-t527-avaota-a1.dts
diff --git a/dts/upstream/src/arm64/allwinner/sun55i-t527-avaota-a1.dts b/dts/upstream/src/arm64/allwinner/sun55i-t527-avaota-a1.dts
new file mode 100644
index 00000000000..dea2acc1849
--- /dev/null
+++ b/dts/upstream/src/arm64/allwinner/sun55i-t527-avaota-a1.dts
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+// Copyright (C) 2024 Arm Ltd.
+
+/dts-v1/;
+
+#include "sun55i-a523.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ model = "Avaota A1";
+ compatible = "yuzukihd,avaota-a1", "allwinner,sun55i-t527";
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ ext_osc32k: ext-osc32k-clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ clock-output-names = "ext_osc32k";
+ };
+
+ reg_vcc12v: vcc12v {
+ /* DC input jack */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-12v";
+ regulator-min-microvolt = <12000000>;
+ regulator-max-microvolt = <12000000>;
+ regulator-always-on;
+ };
+
+ reg_vcc5v: vcc5v {
+ /* board wide 5V supply from the 12V->5V regulator */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-5v";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <®_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 = <®_vcc5v>;
+ gpio = <&pio 8 12 GPIO_ACTIVE_HIGH>; /* PI12 */
+ enable-active-high;
+ };
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&mmc0 {
+ vmmc-supply = <®_cldo3>;
+ cd-gpios = <&pio 5 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* PF6 */
+ bus-width = <4>;
+ status = "okay";
+};
+
+&mmc2 {
+ bus-width = <8>;
+ cap-mmc-hw-reset;
+ mmc-ddr-1_8v;
+ mmc-hs200-1_8v;
+ non-removable;
+ vmmc-supply = <®_cldo3>;
+ vqmmc-supply = <®_cldo1>;
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&pio {
+ vcc-pb-supply = <®_cldo3>; /* via VCC-IO */
+ vcc-pc-supply = <®_cldo1>;
+ vcc-pd-supply = <®_dcdc4>;
+ vcc-pe-supply = <®_dcdc4>;
+ vcc-pf-supply = <®_cldo3>; /* actually switchable */
+ vcc-pg-supply = <®_bldo1>;
+ vcc-ph-supply = <®_cldo3>; /* via VCC-IO */
+ vcc-pi-supply = <®_dcdc4>;
+ vcc-pj-supply = <®_dcdc4>;
+ vcc-pk-supply = <®_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 = <®_vcc5v>;
+ vin2-supply = <®_vcc5v>;
+ vin3-supply = <®_vcc5v>;
+ vin4-supply = <®_vcc5v>;
+ aldoin-supply = <®_vcc5v>;
+ bldoin-supply = <®_vcc5v>;
+ cldoin-supply = <®_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 = <®_vcc5v>;
+ vin2-supply = <®_vcc5v>;
+ vin3-supply = <®_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 = <®_aldo3>;
+ */
+ vcc-pm-supply = <®_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 = <®_usb_vbus>;
+ usb0_vbus_det-gpios = <&pio 8 13 GPIO_ACTIVE_HIGH>; /* PI13 */
+ status = "okay";
+};
--
2.46.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 20/20] sunxi: A523: add defconfigs for three boards
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (18 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 19/20] arm64: dts: allwinner: a523: add Avaota-A1 router support Andre Przywara
@ 2025-07-17 23:54 ` Andre Przywara
2025-07-22 0:35 ` Andre Przywara
2025-07-19 2:05 ` [PATCH v2 00/20] sunxi: Add Allwinner A523 support Yixun Lan
20 siblings, 1 reply; 32+ messages in thread
From: Andre Przywara @ 2025-07-17 23:54 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
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>
---
board/sunxi/MAINTAINERS | 15 +++++++++++++++
configs/avaota-a1_defconfig | 31 +++++++++++++++++++++++++++++++
configs/radxa-a5e_defconfig | 30 ++++++++++++++++++++++++++++++
configs/x96q_pro_plus_defconfig | 32 ++++++++++++++++++++++++++++++++
4 files changed, 108 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/board/sunxi/MAINTAINERS b/board/sunxi/MAINTAINERS
index 1b4b7d87163..66e6a8ff962 100644
--- a/board/sunxi/MAINTAINERS
+++ b/board/sunxi/MAINTAINERS
@@ -137,6 +137,11 @@ M: Chris Morgan <macromorgan@hotmail.com>
S: Maintained
F: configs/anbernic_rg35xx_h700_defconfig
+AVAOTA A1 BOARD
+M: Andre Przywara <andre.przywara@arm.com>
+S: Maintained
+F: configs/avaota-a1_defconfig
+
BANANAPI M1 PLUS
M: Jagan Teki <jagan@amarulasolutions.com>
S: Maintained
@@ -531,6 +536,11 @@ M: Quentin Schulz <quentin.schulz@free-electrons.com>
S: Maintained
F: configs/parrot_r16_defconfig
+RADXA CUBIE A5E BOARD
+M: Andre Przywara <andre.przywara@arm.com>
+S: Maintained
+F: configs/radxa-a5e_defconfig
+
SINLINX SINA31s BOARD
M: Chen-Yu Tsai <wens@csie.org>
S: Maintained
@@ -591,6 +601,11 @@ M: Andre Przywara <andre.przywara@arm.com>
S: Maintained
F: configs/x96_mate_defconfig
+X96Q PRO+ TV BOX
+M: Andre Przywara <andre.przywara@arm.com>
+S: Maintained
+F: configs/x96q_pro_plus_defconfig
+
YONES TOPTECH BD1078 BOARD
M: Paul Kocialkowski <contact@paulk.fr>
S: Maintained
diff --git a/configs/avaota-a1_defconfig b/configs/avaota-a1_defconfig
new file mode 100644
index 00000000000..55457edd3b3
--- /dev/null
+++ b/configs/avaota-a1_defconfig
@@ -0,0 +1,31 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun55i-t527-avaota-a1"
+CONFIG_SPL=y
+CONFIG_DRAM_SUNXI_DX_ODT=0x07070707
+CONFIG_DRAM_SUNXI_DX_DRI=0x0d0d0d0d
+CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e
+CONFIG_DRAM_SUNXI_ODT_EN=0x84848484
+CONFIG_DRAM_SUNXI_TPR0=0x80808080
+CONFIG_DRAM_SUNXI_TPR1=0x06060606
+CONFIG_DRAM_SUNXI_TPR6=0x38000000
+CONFIG_DRAM_SUNXI_TPR10=0x802f3333
+CONFIG_DRAM_SUNXI_TPR11=0xc7c5c4c2
+CONFIG_DRAM_SUNXI_TPR12=0x3533302f
+CONFIG_MACH_SUN55I_A523=y
+CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+CONFIG_R_I2C_ENABLE=y
+# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SPL_I2C=y
+CONFIG_SPL_SYS_I2C_LEGACY=y
+CONFIG_SYS_I2C_MVTWSI=y
+CONFIG_SYS_I2C_SLAVE=0x7f
+CONFIG_SYS_I2C_SPEED=400000
+CONFIG_SUPPORT_EMMC_BOOT=y
+CONFIG_REGULATOR_AXP=y
+CONFIG_AXP717_POWER=y
+CONFIG_AXP_I2C_ADDRESS=0x35
+CONFIG_AXP_DCDC2_VOLT=920
+CONFIG_AXP_DCDC3_VOLT=1160
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
diff --git a/configs/radxa-a5e_defconfig b/configs/radxa-a5e_defconfig
new file mode 100644
index 00000000000..012e7b4eeaf
--- /dev/null
+++ b/configs/radxa-a5e_defconfig
@@ -0,0 +1,30 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun55i-a527-cubie-a5e"
+CONFIG_SPL=y
+CONFIG_DRAM_SUNXI_DX_ODT=0x07070707
+CONFIG_DRAM_SUNXI_DX_DRI=0x0d0d0d0d
+CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e
+CONFIG_DRAM_SUNXI_ODT_EN=0x84848484
+CONFIG_DRAM_SUNXI_TPR0=0x80808080
+CONFIG_DRAM_SUNXI_TPR1=0x06060606
+CONFIG_DRAM_SUNXI_TPR6=0x38000000
+CONFIG_DRAM_SUNXI_TPR10=0x802f3333
+CONFIG_DRAM_SUNXI_TPR11=0xc7c5c4c2
+CONFIG_DRAM_SUNXI_TPR12=0x3533302f
+# BSP value: CONFIG_DRAM_SUNXI_TPR11=0xc6c4c2c0
+# BSP value: CONFIG_DRAM_SUNXI_TPR12=0x3a373233
+CONFIG_MACH_SUN55I_A523=y
+CONFIG_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..07d3b078bef
--- /dev/null
+++ b/configs/x96q_pro_plus_defconfig
@@ -0,0 +1,32 @@
+CONFIG_ARM=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun55i-h728-x96qpro+"
+CONFIG_SPL=y
+CONFIG_DRAM_SUNXI_DX_ODT=0x07070707
+CONFIG_DRAM_SUNXI_DX_DRI=0x0c0c0c0c
+CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e
+CONFIG_DRAM_SUNXI_ODT_EN=0x90909090
+CONFIG_DRAM_SUNXI_TPR0=0x80808080
+CONFIG_DRAM_SUNXI_TPR1=0x06060606
+CONFIG_DRAM_SUNXI_TPR6=0x3380807e
+CONFIG_DRAM_SUNXI_TPR10=0x802f7788
+CONFIG_DRAM_SUNXI_TPR11=0x8f919190
+CONFIG_DRAM_SUNXI_TPR12=0x22222723
+CONFIG_MACH_SUN55I_A523=y
+CONFIG_SUNXI_DRAM_A523_DDR3=y
+CONFIG_DRAM_CLK=792
+CONFIG_MMC_SUNXI_SLOT_EXTRA=2
+CONFIG_R_I2C_ENABLE=y
+# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SPL_I2C=y
+CONFIG_SPL_SYS_I2C_LEGACY=y
+CONFIG_SYS_I2C_MVTWSI=y
+CONFIG_SYS_I2C_SLAVE=0x7f
+CONFIG_SYS_I2C_SPEED=400000
+CONFIG_SUPPORT_EMMC_BOOT=y
+CONFIG_REGULATOR_AXP=y
+CONFIG_AXP717_POWER=y
+CONFIG_AXP_DCDC2_VOLT=920
+CONFIG_AXP_DCDC3_VOLT=1360
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OHCI_HCD=y
--
2.46.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH v2 20/20] sunxi: A523: add defconfigs for three boards
2025-07-17 23:54 ` [PATCH v2 20/20] sunxi: A523: add defconfigs for three boards Andre Przywara
@ 2025-07-22 0:35 ` Andre Przywara
2025-07-22 2:49 ` Yixun Lan
0 siblings, 1 reply; 32+ messages in thread
From: Andre Przywara @ 2025-07-22 0:35 UTC (permalink / raw)
To: u-boot
Cc: Jernej Skrabec, Mikhail Kalashnikov, Yixun Lan, Paul Kocialkowski,
linux-sunxi, Tom Rini
On Fri, 18 Jul 2025 00:54:55 +0100
Andre Przywara <andre.przywara@arm.com> wrote:
Hi Yixun,
see below for a comment on the missing eMMC support for the Radxa
defconfig. My board doesn't have eMMC, so can you please check this on
your end?
> 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>
> ---
> board/sunxi/MAINTAINERS | 15 +++++++++++++++
> configs/avaota-a1_defconfig | 31 +++++++++++++++++++++++++++++++
> configs/radxa-a5e_defconfig | 30 ++++++++++++++++++++++++++++++
> configs/x96q_pro_plus_defconfig | 32 ++++++++++++++++++++++++++++++++
> 4 files changed, 108 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/board/sunxi/MAINTAINERS b/board/sunxi/MAINTAINERS
> index 1b4b7d87163..66e6a8ff962 100644
> --- a/board/sunxi/MAINTAINERS
> +++ b/board/sunxi/MAINTAINERS
> @@ -137,6 +137,11 @@ M: Chris Morgan <macromorgan@hotmail.com>
> S: Maintained
> F: configs/anbernic_rg35xx_h700_defconfig
>
> +AVAOTA A1 BOARD
> +M: Andre Przywara <andre.przywara@arm.com>
> +S: Maintained
> +F: configs/avaota-a1_defconfig
> +
> BANANAPI M1 PLUS
> M: Jagan Teki <jagan@amarulasolutions.com>
> S: Maintained
> @@ -531,6 +536,11 @@ M: Quentin Schulz <quentin.schulz@free-electrons.com>
> S: Maintained
> F: configs/parrot_r16_defconfig
>
> +RADXA CUBIE A5E BOARD
> +M: Andre Przywara <andre.przywara@arm.com>
> +S: Maintained
> +F: configs/radxa-a5e_defconfig
> +
> SINLINX SINA31s BOARD
> M: Chen-Yu Tsai <wens@csie.org>
> S: Maintained
> @@ -591,6 +601,11 @@ M: Andre Przywara <andre.przywara@arm.com>
> S: Maintained
> F: configs/x96_mate_defconfig
>
> +X96Q PRO+ TV BOX
> +M: Andre Przywara <andre.przywara@arm.com>
> +S: Maintained
> +F: configs/x96q_pro_plus_defconfig
> +
> YONES TOPTECH BD1078 BOARD
> M: Paul Kocialkowski <contact@paulk.fr>
> S: Maintained
> diff --git a/configs/avaota-a1_defconfig b/configs/avaota-a1_defconfig
> new file mode 100644
> index 00000000000..55457edd3b3
> --- /dev/null
> +++ b/configs/avaota-a1_defconfig
> @@ -0,0 +1,31 @@
> +CONFIG_ARM=y
> +CONFIG_ARCH_SUNXI=y
> +CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun55i-t527-avaota-a1"
> +CONFIG_SPL=y
> +CONFIG_DRAM_SUNXI_DX_ODT=0x07070707
> +CONFIG_DRAM_SUNXI_DX_DRI=0x0d0d0d0d
> +CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e
> +CONFIG_DRAM_SUNXI_ODT_EN=0x84848484
> +CONFIG_DRAM_SUNXI_TPR0=0x80808080
> +CONFIG_DRAM_SUNXI_TPR1=0x06060606
> +CONFIG_DRAM_SUNXI_TPR6=0x38000000
> +CONFIG_DRAM_SUNXI_TPR10=0x802f3333
> +CONFIG_DRAM_SUNXI_TPR11=0xc7c5c4c2
> +CONFIG_DRAM_SUNXI_TPR12=0x3533302f
> +CONFIG_MACH_SUN55I_A523=y
> +CONFIG_MMC_SUNXI_SLOT_EXTRA=2
> +CONFIG_R_I2C_ENABLE=y
> +# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
> +CONFIG_SPL_I2C=y
> +CONFIG_SPL_SYS_I2C_LEGACY=y
> +CONFIG_SYS_I2C_MVTWSI=y
> +CONFIG_SYS_I2C_SLAVE=0x7f
> +CONFIG_SYS_I2C_SPEED=400000
> +CONFIG_SUPPORT_EMMC_BOOT=y
> +CONFIG_REGULATOR_AXP=y
> +CONFIG_AXP717_POWER=y
> +CONFIG_AXP_I2C_ADDRESS=0x35
> +CONFIG_AXP_DCDC2_VOLT=920
> +CONFIG_AXP_DCDC3_VOLT=1160
> +CONFIG_USB_EHCI_HCD=y
> +CONFIG_USB_OHCI_HCD=y
> diff --git a/configs/radxa-a5e_defconfig b/configs/radxa-a5e_defconfig
> new file mode 100644
> index 00000000000..012e7b4eeaf
> --- /dev/null
> +++ b/configs/radxa-a5e_defconfig
> @@ -0,0 +1,30 @@
> +CONFIG_ARM=y
> +CONFIG_ARCH_SUNXI=y
> +CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun55i-a527-cubie-a5e"
> +CONFIG_SPL=y
> +CONFIG_DRAM_SUNXI_DX_ODT=0x07070707
> +CONFIG_DRAM_SUNXI_DX_DRI=0x0d0d0d0d
> +CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e
> +CONFIG_DRAM_SUNXI_ODT_EN=0x84848484
> +CONFIG_DRAM_SUNXI_TPR0=0x80808080
> +CONFIG_DRAM_SUNXI_TPR1=0x06060606
> +CONFIG_DRAM_SUNXI_TPR6=0x38000000
> +CONFIG_DRAM_SUNXI_TPR10=0x802f3333
> +CONFIG_DRAM_SUNXI_TPR11=0xc7c5c4c2
> +CONFIG_DRAM_SUNXI_TPR12=0x3533302f
> +# BSP value: CONFIG_DRAM_SUNXI_TPR11=0xc6c4c2c0
> +# BSP value: CONFIG_DRAM_SUNXI_TPR12=0x3a373233
> +CONFIG_MACH_SUN55I_A523=y
> +CONFIG_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
This is missing CONFIG_MMC_SUNXI_SLOT_EXTRA=2 and
CONFIG_SUPPORT_EMMC_BOOT=y, as there are Radxa boards with eMMC.
The DT describes this, so we should follow suit here, to allow eMMC
booting. Should not harm boards without the eMMC chip.
Thanks,
Andre
> diff --git a/configs/x96q_pro_plus_defconfig b/configs/x96q_pro_plus_defconfig
> new file mode 100644
> index 00000000000..07d3b078bef
> --- /dev/null
> +++ b/configs/x96q_pro_plus_defconfig
> @@ -0,0 +1,32 @@
> +CONFIG_ARM=y
> +CONFIG_ARCH_SUNXI=y
> +CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun55i-h728-x96qpro+"
> +CONFIG_SPL=y
> +CONFIG_DRAM_SUNXI_DX_ODT=0x07070707
> +CONFIG_DRAM_SUNXI_DX_DRI=0x0c0c0c0c
> +CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e
> +CONFIG_DRAM_SUNXI_ODT_EN=0x90909090
> +CONFIG_DRAM_SUNXI_TPR0=0x80808080
> +CONFIG_DRAM_SUNXI_TPR1=0x06060606
> +CONFIG_DRAM_SUNXI_TPR6=0x3380807e
> +CONFIG_DRAM_SUNXI_TPR10=0x802f7788
> +CONFIG_DRAM_SUNXI_TPR11=0x8f919190
> +CONFIG_DRAM_SUNXI_TPR12=0x22222723
> +CONFIG_MACH_SUN55I_A523=y
> +CONFIG_SUNXI_DRAM_A523_DDR3=y
> +CONFIG_DRAM_CLK=792
> +CONFIG_MMC_SUNXI_SLOT_EXTRA=2
> +CONFIG_R_I2C_ENABLE=y
> +# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
> +CONFIG_SPL_I2C=y
> +CONFIG_SPL_SYS_I2C_LEGACY=y
> +CONFIG_SYS_I2C_MVTWSI=y
> +CONFIG_SYS_I2C_SLAVE=0x7f
> +CONFIG_SYS_I2C_SPEED=400000
> +CONFIG_SUPPORT_EMMC_BOOT=y
> +CONFIG_REGULATOR_AXP=y
> +CONFIG_AXP717_POWER=y
> +CONFIG_AXP_DCDC2_VOLT=920
> +CONFIG_AXP_DCDC3_VOLT=1360
> +CONFIG_USB_EHCI_HCD=y
> +CONFIG_USB_OHCI_HCD=y
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v2 20/20] sunxi: A523: add defconfigs for three boards
2025-07-22 0:35 ` Andre Przywara
@ 2025-07-22 2:49 ` Yixun Lan
2025-07-22 10:30 ` Andre Przywara
0 siblings, 1 reply; 32+ messages in thread
From: Yixun Lan @ 2025-07-22 2:49 UTC (permalink / raw)
To: Andre Przywara
Cc: u-boot, Jernej Skrabec, Mikhail Kalashnikov, Paul Kocialkowski,
linux-sunxi, Tom Rini
Hi Andre,
On 01:35 Tue 22 Jul , Andre Przywara wrote:
> On Fri, 18 Jul 2025 00:54:55 +0100
> Andre Przywara <andre.przywara@arm.com> wrote:
>
> Hi Yixun,
>
> see below for a comment on the missing eMMC support for the Radxa
> defconfig. My board doesn't have eMMC, so can you please check this on
> your end?
>
sure
> > 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>
> > ---
> > board/sunxi/MAINTAINERS | 15 +++++++++++++++
> > configs/avaota-a1_defconfig | 31 +++++++++++++++++++++++++++++++
> > configs/radxa-a5e_defconfig | 30 ++++++++++++++++++++++++++++++
> > configs/x96q_pro_plus_defconfig | 32 ++++++++++++++++++++++++++++++++
> > 4 files changed, 108 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/radxa-a5e_defconfig b/configs/radxa-a5e_defconfig
> > new file mode 100644
> > index 00000000000..012e7b4eeaf
> > --- /dev/null
> > +++ b/configs/radxa-a5e_defconfig
> > @@ -0,0 +1,30 @@
> > +CONFIG_ARM=y
> > +CONFIG_ARCH_SUNXI=y
> > +CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun55i-a527-cubie-a5e"
> > +CONFIG_SPL=y
> > +CONFIG_DRAM_SUNXI_DX_ODT=0x07070707
> > +CONFIG_DRAM_SUNXI_DX_DRI=0x0d0d0d0d
> > +CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e
> > +CONFIG_DRAM_SUNXI_ODT_EN=0x84848484
> > +CONFIG_DRAM_SUNXI_TPR0=0x80808080
> > +CONFIG_DRAM_SUNXI_TPR1=0x06060606
> > +CONFIG_DRAM_SUNXI_TPR6=0x38000000
> > +CONFIG_DRAM_SUNXI_TPR10=0x802f3333
> > +CONFIG_DRAM_SUNXI_TPR11=0xc7c5c4c2
> > +CONFIG_DRAM_SUNXI_TPR12=0x3533302f
> > +# BSP value: CONFIG_DRAM_SUNXI_TPR11=0xc6c4c2c0
> > +# BSP value: CONFIG_DRAM_SUNXI_TPR12=0x3a373233
> > +CONFIG_MACH_SUN55I_A523=y
> > +CONFIG_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
>
> This is missing CONFIG_MMC_SUNXI_SLOT_EXTRA=2 and
> CONFIG_SUPPORT_EMMC_BOOT=y, as there are Radxa boards with eMMC.
confirmed, the two configs are exactly what I've added to make eMMC work
> The DT describes this, so we should follow suit here, to allow eMMC
> booting. Should not harm boards without the eMMC chip.
>
agree!
I'd assume you would take care of the config? and send out an updated
version next time?
thanks
--
Yixun Lan (dlan)
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v2 20/20] sunxi: A523: add defconfigs for three boards
2025-07-22 2:49 ` Yixun Lan
@ 2025-07-22 10:30 ` Andre Przywara
0 siblings, 0 replies; 32+ messages in thread
From: Andre Przywara @ 2025-07-22 10:30 UTC (permalink / raw)
To: Yixun Lan
Cc: u-boot, Jernej Skrabec, Mikhail Kalashnikov, Paul Kocialkowski,
linux-sunxi, Tom Rini
On Tue, 22 Jul 2025 10:49:32 +0800
Yixun Lan <dlan@gentoo.org> wrote:
Hi!
> On 01:35 Tue 22 Jul , Andre Przywara wrote:
> > On Fri, 18 Jul 2025 00:54:55 +0100
> > Andre Przywara <andre.przywara@arm.com> wrote:
> >
> > Hi Yixun,
> >
> > see below for a comment on the missing eMMC support for the Radxa
> > defconfig. My board doesn't have eMMC, so can you please check this on
> > your end?
> >
> sure
>
> > > 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>
> > > ---
> > > board/sunxi/MAINTAINERS | 15 +++++++++++++++
> > > configs/avaota-a1_defconfig | 31 +++++++++++++++++++++++++++++++
> > > configs/radxa-a5e_defconfig | 30 ++++++++++++++++++++++++++++++
> > > configs/x96q_pro_plus_defconfig | 32 ++++++++++++++++++++++++++++++++
> > > 4 files changed, 108 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/radxa-a5e_defconfig b/configs/radxa-a5e_defconfig
> > > new file mode 100644
> > > index 00000000000..012e7b4eeaf
> > > --- /dev/null
> > > +++ b/configs/radxa-a5e_defconfig
> > > @@ -0,0 +1,30 @@
> > > +CONFIG_ARM=y
> > > +CONFIG_ARCH_SUNXI=y
> > > +CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun55i-a527-cubie-a5e"
> > > +CONFIG_SPL=y
> > > +CONFIG_DRAM_SUNXI_DX_ODT=0x07070707
> > > +CONFIG_DRAM_SUNXI_DX_DRI=0x0d0d0d0d
> > > +CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e
> > > +CONFIG_DRAM_SUNXI_ODT_EN=0x84848484
> > > +CONFIG_DRAM_SUNXI_TPR0=0x80808080
> > > +CONFIG_DRAM_SUNXI_TPR1=0x06060606
> > > +CONFIG_DRAM_SUNXI_TPR6=0x38000000
> > > +CONFIG_DRAM_SUNXI_TPR10=0x802f3333
> > > +CONFIG_DRAM_SUNXI_TPR11=0xc7c5c4c2
> > > +CONFIG_DRAM_SUNXI_TPR12=0x3533302f
> > > +# BSP value: CONFIG_DRAM_SUNXI_TPR11=0xc6c4c2c0
> > > +# BSP value: CONFIG_DRAM_SUNXI_TPR12=0x3a373233
> > > +CONFIG_MACH_SUN55I_A523=y
> > > +CONFIG_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
> >
> > This is missing CONFIG_MMC_SUNXI_SLOT_EXTRA=2 and
> > CONFIG_SUPPORT_EMMC_BOOT=y, as there are Radxa boards with eMMC.
> confirmed, the two configs are exactly what I've added to make eMMC work
Many thanks for testing!
> > The DT describes this, so we should follow suit here, to allow eMMC
> > booting. Should not harm boards without the eMMC chip.
> >
> agree!
>
> I'd assume you would take care of the config? and send out an updated
> version next time?
Yes, I will now include those two lines in the version I will merge.
Cheers,
Andre
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v2 00/20] sunxi: Add Allwinner A523 support
2025-07-17 23:54 [PATCH v2 00/20] sunxi: Add Allwinner A523 support Andre Przywara
` (19 preceding siblings ...)
2025-07-17 23:54 ` [PATCH v2 20/20] sunxi: A523: add defconfigs for three boards Andre Przywara
@ 2025-07-19 2:05 ` Yixun Lan
20 siblings, 0 replies; 32+ messages in thread
From: Yixun Lan @ 2025-07-19 2:05 UTC (permalink / raw)
To: Andre Przywara
Cc: u-boot, Jernej Skrabec, Mikhail Kalashnikov, Paul Kocialkowski,
linux-sunxi, Tom Rini
Hi Andre,
On 00:54 Fri 18 Jul , Andre Przywara wrote:
> Hi,
>
> (Mikhail and Jernej: please reply with S-o-b: lines for your patches!)
>
> this series introduces support for the Allwinner A523 SoC family.
> Compared to v1, a good chunk of those patches have been merged, so we are
> down from 34 to 20 patches now. There are some small fixes to the
> pinctrl driver, and the SPL clock bits got updated to fix the conflicts
> with the now merged A133 support. I rewrote the SPL watchdog code, to
> avoid the MMIO register C struct. The DRAM driver lost one MMIO register
> struct. The DT files have now landed in the DT rebasing repo, so we can
> cherry-pick them from there.
>
> Otherwise this now based on latest U-Boot master, which hopefully makes
> testing easier. If people don't shout, I would like to merge it still
> during this merge window, since the patches have been around for a while,
> and people want to use them, alongside the now supported mainline Linux
> code. So please test and review!
>
> ==================================
> 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.
>
> To be able to share the SPL clock code, the existing H6 code gets
> refactored in patches 01-04. This unifies the (CPU) PLL handling across
> the SoCs supported by this code (H6, H616, A133), and adds support for
> the separate CPU PLLs on the A523.
>
> Patches 05-10 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-ish AXP323 PMIC used on most boards.
>
> Patches 11 updates some SPL bits to be able to cope with the A523.
> Patches 12-15 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.
>
> Patches 16-19 cherry-pick the DT files from the DT rebasing repo. There
> are more fix patches queued, I will update them once they hit the repo.
>
> 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.
>
for the series, I've only got one clk warning, but otherwise works for me..
Tested-by: Yixun Lan <dlan@gentoo.org> # Radxa A5E
--
Yixun Lan (dlan)
^ permalink raw reply [flat|nested] 32+ messages in thread