[RFC PATCH 0/4] Allwinner R528/T113s PSCI

Hi Andre (and list),
Per our previous discussion, these are my patches to add PSCI support to your WIP R528/T113s series. I am sending it as RFC because I have CC'd the list and don't want these changes to be treated as patches against upstream.
The first patch in this series provides a few more register definitions for cpu_sunxi_ncat2.h. The next two are refactoring, independent of any specific arch/SoC. The final patch adds PSCI support.
It might or might not be appropriate to split out the refactoring patches for separate review and inclusion; otherwise, the intent of this patchset is to be incorporated into the existing R528 series.
Cheers, Sam
Sam Edwards (4): SQUASH ME sunxi: psci: clean away preprocessor macros sunxi: psci: refactor register access to separate functions sunxi: psci: implement PSCI on R528
arch/arm/cpu/armv7/sunxi/psci.c | 185 ++++++++++++------ .../include/asm/arch-sunxi/cpu_sunxi_ncat2.h | 7 +- arch/arm/mach-sunxi/Kconfig | 2 + include/configs/sunxi-common.h | 8 + 4 files changed, 138 insertions(+), 64 deletions(-)

This patch contains only register defs for cpu_sunxi_ncat2.h, and should be combined (as appropriate) into:
sunxi: introduce NCAT2 generation model --- arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
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 d01508517c..25f71bbccd 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h +++ b/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h @@ -17,6 +17,9 @@ #define SUNXI_SIDC_BASE 0x03006000 #define SUNXI_SID_BASE 0x03006200 #define SUNXI_TIMER_BASE 0x02050000 +#define SUNXI_GIC400_BASE 0x03020000 +#define SUNXI_CPUX_BASE 0x09010000 +#define SUNXI_CPUCFG_BASE 0
#ifdef CONFIG_MACH_SUN50I_H6 #define SUNXI_DRAM_COM_BASE 0x04002000 @@ -34,11 +37,11 @@ #define SUNXI_SPI0_BASE 0x04025000 #define SUNXI_SPI1_BASE 0x04026000
-#define SUNXI_RTC_BASE 0x07000000 #define SUNXI_R_CPUCFG_BASE 0x07000400 #define SUNXI_PRCM_BASE 0x07010000 #define SUNXI_R_WDOG_BASE 0x07020400 -#define SUNXI_R_TWI_BASE 0x07081400 +#define SUNXI_R_TWI_BASE 0x07020800 +#define SUNXI_RTC_BASE 0x07090000
#ifndef __ASSEMBLY__ void sunxi_board_init(void);

This patch restructures psci.c to get away from the "many different function definitions switched by #ifdef" paradigm to the preferred style of having a single function definition with `if (IS_ENABLED(...))` to make the optimizer include only the appropriate function bodies instead.
There are no functional changes here.
Signed-off-by: Sam Edwards CFSworks@gmail.com --- arch/arm/cpu/armv7/sunxi/psci.c | 94 ++++++++++++++------------------- 1 file changed, 41 insertions(+), 53 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c index f866025c37..e6b73ebf2b 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.c +++ b/arch/arm/cpu/armv7/sunxi/psci.c @@ -76,28 +76,24 @@ static void __secure __mdelay(u32 ms) isb(); }
-static void __secure clamp_release(u32 __maybe_unused *clamp) +static void __secure clamp_release(u32 *clamp) { -#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \ - defined(CONFIG_MACH_SUN8I_H3) || \ - defined(CONFIG_MACH_SUN8I_R40) - u32 tmp = 0x1ff; - do { - tmp >>= 1; - writel(tmp, clamp); - } while (tmp); - - __mdelay(10); -#endif + if (clamp) { + u32 tmp = 0x1ff; + do { + tmp >>= 1; + writel(tmp, clamp); + } while (tmp); + + __mdelay(10); + } }
-static void __secure clamp_set(u32 __maybe_unused *clamp) +static void __secure clamp_set(u32 *clamp) { -#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \ - defined(CONFIG_MACH_SUN8I_H3) || \ - defined(CONFIG_MACH_SUN8I_R40) - writel(0xff, clamp); -#endif + if (clamp) { + writel(0xff, clamp); + } }
static void __secure sunxi_power_switch(u32 *clamp, u32 *pwroff, bool on, @@ -118,53 +114,45 @@ static void __secure sunxi_power_switch(u32 *clamp, u32 *pwroff, bool on, } }
-#ifdef CONFIG_MACH_SUN8I_R40 -/* secondary core entry address is programmed differently on R40 */ static void __secure sunxi_set_entry_address(void *entry) { - writel((u32)entry, - SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0); -} -#else -static void __secure sunxi_set_entry_address(void *entry) -{ - struct sunxi_cpucfg_reg *cpucfg = - (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; + /* secondary core entry address is programmed differently on R40 */ + if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) { + writel((u32)entry, + SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0); + } else { + struct sunxi_cpucfg_reg *cpucfg = + (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
- writel((u32)entry, &cpucfg->priv0); + writel((u32)entry, &cpucfg->priv0); + } } -#endif
-#ifdef CONFIG_MACH_SUN7I -/* sun7i (A20) is different from other single cluster SoCs */ -static void __secure sunxi_cpu_set_power(int __always_unused cpu, bool on) -{ - struct sunxi_cpucfg_reg *cpucfg = - (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; - - sunxi_power_switch(&cpucfg->cpu1_pwr_clamp, &cpucfg->cpu1_pwroff, - on, 0); -} -#elif defined CONFIG_MACH_SUN8I_R40 static void __secure sunxi_cpu_set_power(int cpu, bool on) { struct sunxi_cpucfg_reg *cpucfg = (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
- sunxi_power_switch((void *)cpucfg + SUN8I_R40_PWR_CLAMP(cpu), - (void *)cpucfg + SUN8I_R40_PWROFF, - on, cpu); -} -#else /* ! CONFIG_MACH_SUN7I && ! CONFIG_MACH_SUN8I_R40 */ -static void __secure sunxi_cpu_set_power(int cpu, bool on) -{ - struct sunxi_prcm_reg *prcm = - (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; + /* sun7i (A20) is different from other single cluster SoCs */ + if (IS_ENABLED(CONFIG_MACH_SUN7I)) { + sunxi_power_switch(NULL, &cpucfg->cpu1_pwroff, on, 0); + } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) { + sunxi_power_switch(NULL, (void *)cpucfg + SUN8I_R40_PWROFF, + on, cpu); + } else { +#if !defined(CONFIG_SUN50I_GEN_H6) && !defined(CONFIG_SUNXI_GEN_NCAT2) + struct sunxi_prcm_reg *prcm = + (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
- sunxi_power_switch(&prcm->cpu_pwr_clamp[cpu], &prcm->cpu_pwroff, - on, cpu); + u32 *clamp = &prcm->cpu_pwr_clamp[cpu]; + if (IS_ENABLED(CONFIG_MACH_SUN6I) || + IS_ENABLED(CONFIG_MACH_SUN8I_H3)) + clamp = NULL; + + sunxi_power_switch(clamp, &prcm->cpu_pwroff, on, cpu); +#endif + } } -#endif /* CONFIG_MACH_SUN7I */
void __secure sunxi_cpu_power_off(u32 cpuid) {

This is to prepare for R528, which does not have the typical "CPUCFG" block; it has a "CPUX" block which provides these same functions but is organized differently.
Moving the hardware-access bits to their own functions separates the logic from the hardware so we can reuse the same logic.
Signed-off-by: Sam Edwards CFSworks@gmail.com --- arch/arm/cpu/armv7/sunxi/psci.c | 66 +++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 19 deletions(-)
diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c index e6b73ebf2b..d9a9d73881 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.c +++ b/arch/arm/cpu/armv7/sunxi/psci.c @@ -114,7 +114,7 @@ static void __secure sunxi_power_switch(u32 *clamp, u32 *pwroff, bool on, } }
-static void __secure sunxi_set_entry_address(void *entry) +static void __secure sunxi_cpu_set_entry(int __always_unused cpu, void *entry) { /* secondary core entry address is programmed differently on R40 */ if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) { @@ -154,30 +154,60 @@ static void __secure sunxi_cpu_set_power(int cpu, bool on) } }
-void __secure sunxi_cpu_power_off(u32 cpuid) +static void __secure sunxi_cpu_set_reset(int cpu, bool reset) +{ + struct sunxi_cpucfg_reg *cpucfg = + (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; + + writel(reset ? 0b00 : 0b11, &cpucfg->cpu[cpu].rst); +} + +static void __secure sunxi_cpu_set_locking(int cpu, bool lock) { struct sunxi_cpucfg_reg *cpucfg = (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; + + if (lock) + clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); + else + setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); +} + +static bool __secure sunxi_cpu_poll_wfi(int cpu) +{ + struct sunxi_cpucfg_reg *cpucfg = + (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; + + return !!(readl(&cpucfg->cpu[cpu].status) & BIT(2)); +} + +static void __secure sunxi_cpu_invalidate_cache(int cpu) +{ + struct sunxi_cpucfg_reg *cpucfg = + (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; + + clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu)); +} + +void __secure sunxi_cpu_power_off(u32 cpuid) +{ u32 cpu = cpuid & 0x3;
/* Wait for the core to enter WFI */ - while (1) { - if (readl(&cpucfg->cpu[cpu].status) & BIT(2)) - break; + while (!sunxi_cpu_poll_wfi(cpu)) __mdelay(1); - }
/* Assert reset on target CPU */ - writel(0, &cpucfg->cpu[cpu].rst); + sunxi_cpu_set_reset(cpu, true);
/* Lock CPU (Disable external debug access) */ - clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); + sunxi_cpu_set_locking(cpu, true);
/* Power down CPU */ sunxi_cpu_set_power(cpuid, false);
- /* Unlock CPU (Disable external debug access) */ - setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); + /* Unlock CPU (Reenable external debug access) */ + sunxi_cpu_set_locking(cpu, false); }
static u32 __secure cp15_read_scr(void) @@ -234,33 +264,31 @@ out: int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc, u32 context_id) { - struct sunxi_cpucfg_reg *cpucfg = - (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; u32 cpu = (mpidr & 0x3);
/* store target PC and context id */ psci_save(cpu, pc, context_id);
/* Set secondary core power on PC */ - sunxi_set_entry_address(&psci_cpu_entry); + sunxi_cpu_set_entry(cpu, &psci_cpu_entry);
/* Assert reset on target CPU */ - writel(0, &cpucfg->cpu[cpu].rst); + sunxi_cpu_set_reset(cpu, true);
/* Invalidate L1 cache */ - clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu)); + sunxi_cpu_invalidate_cache(cpu);
/* Lock CPU (Disable external debug access) */ - clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); + sunxi_cpu_set_locking(cpu, true);
/* Power up target CPU */ sunxi_cpu_set_power(cpu, true);
/* De-assert reset on target CPU */ - writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst); + sunxi_cpu_set_reset(cpu, false);
- /* Unlock CPU (Disable external debug access) */ - setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); + /* Unlock CPU (Reenable external debug access) */ + sunxi_cpu_set_locking(cpu, false);
return ARM_PSCI_RET_SUCCESS; }

This patch adds the necessary code to make nonsec booting and PSCI secondary core management functional on the R528/T113.
Signed-off-by: Sam Edwards CFSworks@gmail.com --- arch/arm/cpu/armv7/sunxi/psci.c | 47 ++++++++++++++++++++++++++++++++- arch/arm/mach-sunxi/Kconfig | 2 ++ include/configs/sunxi-common.h | 8 ++++++ 3 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c index d9a9d73881..667715f105 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.c +++ b/arch/arm/cpu/armv7/sunxi/psci.c @@ -38,6 +38,19 @@ #define SUN8I_R40_PWR_CLAMP(cpu) (0x120 + (cpu) * 0x4) #define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0 (0xbc)
+/* + * R528 is also different, as it has both cores powered up (but held in reset + * state) after the SoC is reset. Like the R40, it uses a "soft" entry point + * address register, but unlike the R40, it uses a newer "CPUX" block to manage + * CPU state, rather than the older CPUCFG system. + */ +#define SUN8I_R528_SOFT_ENTRY (0x1c8) +#define SUN8I_R528_C0_RST_CTRL (0x0000) +#define SUN8I_R528_C0_CTRL_REG0 (0x0010) +#define SUN8I_R528_C0_CPU_STATUS (0x0080) + +#define SUN8I_R528_C0_STATUS_STANDBYWFI (16) + static void __secure cp15_write_cntp_tval(u32 tval) { asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval)); @@ -116,10 +129,13 @@ static void __secure sunxi_power_switch(u32 *clamp, u32 *pwroff, bool on,
static void __secure sunxi_cpu_set_entry(int __always_unused cpu, void *entry) { - /* secondary core entry address is programmed differently on R40 */ + /* secondary core entry address is programmed differently on R40/528 */ if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) { writel((u32)entry, SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0); + } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) { + writel((u32)entry, + SUNXI_R_CPUCFG_BASE + SUN8I_R528_SOFT_ENTRY); } else { struct sunxi_cpucfg_reg *cpucfg = (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; @@ -139,6 +155,8 @@ static void __secure sunxi_cpu_set_power(int cpu, bool on) } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) { sunxi_power_switch(NULL, (void *)cpucfg + SUN8I_R40_PWROFF, on, cpu); + } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) { + /* R528 leaves both cores powered up, manages them via reset */ } else { #if !defined(CONFIG_SUN50I_GEN_H6) && !defined(CONFIG_SUNXI_GEN_NCAT2) struct sunxi_prcm_reg *prcm = @@ -159,6 +177,17 @@ static void __secure sunxi_cpu_set_reset(int cpu, bool reset) struct sunxi_cpucfg_reg *cpucfg = (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+ if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) { + if (reset) { + clrbits_le32(SUNXI_CPUX_BASE + SUN8I_R528_C0_RST_CTRL, + BIT(cpu)); + } else { + setbits_le32(SUNXI_CPUX_BASE + SUN8I_R528_C0_RST_CTRL, + BIT(cpu)); + } + return; + } + writel(reset ? 0b00 : 0b11, &cpucfg->cpu[cpu].rst); }
@@ -167,6 +196,11 @@ static void __secure sunxi_cpu_set_locking(int cpu, bool lock) struct sunxi_cpucfg_reg *cpucfg = (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+ if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) { + /* Not required on R528 */ + return; + } + if (lock) clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); else @@ -178,6 +212,11 @@ static bool __secure sunxi_cpu_poll_wfi(int cpu) struct sunxi_cpucfg_reg *cpucfg = (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+ if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) { + return !!(readl(SUNXI_CPUX_BASE + SUN8I_R528_C0_CPU_STATUS) & + BIT(SUN8I_R528_C0_STATUS_STANDBYWFI + cpu)); + } + return !!(readl(&cpucfg->cpu[cpu].status) & BIT(2)); }
@@ -186,6 +225,12 @@ static void __secure sunxi_cpu_invalidate_cache(int cpu) struct sunxi_cpucfg_reg *cpucfg = (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+ if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) { + clrbits_le32(SUNXI_CPUX_BASE + SUN8I_R528_C0_CTRL_REG0, + BIT(cpu)); + return; + } + clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu)); }
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index bb9b863d2c..a5d312d377 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -366,6 +366,8 @@ config MACH_SUN8I_R40 config MACH_SUN8I_R528 bool "sun8i (Allwinner R528)" select CPU_V7A + select CPU_V7_HAS_NONSEC + select ARCH_SUPPORT_PSCI select SUNXI_GEN_NCAT2 select SUNXI_NEW_PINCTRL select MMC_SUNXI_HAS_NEW_MODE diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index b8ca77d031..67eb0d25db 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -33,6 +33,14 @@
/* CPU */
+/* + * Newer ARM SoCs have moved the GIC, but have not updated their ARM cores to + * reflect the correct address in CBAR/PERIPHBASE. + */ +#if defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2) +#define CFG_ARM_GIC_BASE_ADDRESS 0x03020000 +#endif + /* * The DRAM Base differs between some models. We cannot use macros for the * CONFIG_FOO defines which contain the DRAM base address since they end

Hi, Sam,
Thank you for this series! I tested it on my T113s board and got the second core worked.
Here is some kernel log:
[ 0.000000] psci: probing for conduit method from DT. [ 0.000000] psci: Using PSCI v0.1 Function IDs from DT [ 0.120000] CPU: Testing write buffer coherency: ok [ 0.120000] /cpus/cpu@0 missing clock-frequency property [ 0.120000] /cpus/cpu@1 missing clock-frequency property [ 0.120000] CPU0: thread -1, cpu 0, socket 0, mpidr 80000000 [ 0.120000] Setting up static identity map for 0x40100000 - 0x40100060 [ 0.120000] rcu: Hierarchical SRCU implementation. [ 0.120000] rcu: Max phase no-delay instances is 1000. [ 0.120000] smp: Bringing up secondary CPUs ... [ 0.170000] CPU1: thread -1, cpu 1, socket 0, mpidr 80000001 [ 0.180000] smp: Brought up 1 node, 2 CPUs [ 0.180000] SMP: Total of 2 processors activated (1149.33 BogoMIPS). [ 0.180000] CPU: All CPU(s) started in SVC mode.
Tested-by: Maksim Kiselev bigunclemax@gmail.com
participants (2)
-
Maksim Kiselev
-
Sam Edwards