[U-Boot] [PATCH v2 11/11] sunxi: Add PSCI implementation in C

Marc Zyngier marc.zyngier at arm.com
Thu May 26 19:19:58 CEST 2016


On 26/05/16 15:01, Chen-Yu Tsai wrote:
> To make the PSCI backend more maintainable and easier to port to newer
> SoCs, rewrite the current PSCI implementation in C.
> 
> Some inline assembly bits are required to access coprocessor registers.
> PSCI stack setup is the only part left completely in assembly. In theory
> this part could be split out of psci_arch_init into a separate common
> function, and psci_arch_init could be completely in C.
> 
> Signed-off-by: Chen-Yu Tsai <wens at csie.org>
> ---
>  arch/arm/cpu/armv7/sunxi/Makefile     |   7 +-
>  arch/arm/cpu/armv7/sunxi/psci.c       | 269 ++++++++++++++++++++++++++++++++++
>  arch/arm/cpu/armv7/sunxi/psci_head.S  |  66 +++++++++
>  arch/arm/cpu/armv7/sunxi/psci_sun6i.S | 262 ---------------------------------
>  arch/arm/cpu/armv7/sunxi/psci_sun7i.S | 237 ------------------------------
>  5 files changed, 337 insertions(+), 504 deletions(-)
>  create mode 100644 arch/arm/cpu/armv7/sunxi/psci.c
>  create mode 100644 arch/arm/cpu/armv7/sunxi/psci_head.S
>  delete mode 100644 arch/arm/cpu/armv7/sunxi/psci_sun6i.S
>  delete mode 100644 arch/arm/cpu/armv7/sunxi/psci_sun7i.S
> 
> diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile
> index 4d2274a38ed1..c2085101685b 100644
> --- a/arch/arm/cpu/armv7/sunxi/Makefile
> +++ b/arch/arm/cpu/armv7/sunxi/Makefile
> @@ -13,11 +13,8 @@ obj-$(CONFIG_MACH_SUN6I)	+= tzpc.o
>  obj-$(CONFIG_MACH_SUN8I_H3)	+= tzpc.o
>  
>  ifndef CONFIG_SPL_BUILD
> -ifdef CONFIG_ARMV7_PSCI
> -obj-$(CONFIG_MACH_SUN6I)	+= psci_sun6i.o
> -obj-$(CONFIG_MACH_SUN7I)	+= psci_sun7i.o
> -obj-$(CONFIG_MACH_SUN8I)	+= psci_sun6i.o
> -endif
> +obj-$(CONFIG_ARMV7_PSCI)	+= psci.o
> +obj-$(CONFIG_ARMV7_PSCI)	+= psci_head.o
>  endif
>  
>  ifdef CONFIG_SPL_BUILD
> diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c
> new file mode 100644
> index 000000000000..f0c151a349c8
> --- /dev/null
> +++ b/arch/arm/cpu/armv7/sunxi/psci.c
> @@ -0,0 +1,269 @@
> +/*
> + * Copyright (C) 2016
> + * Author: Chen-Yu Tsai <wens at csie.org>
> + *
> + * Based on assembly code by Marc Zyngier <marc.zyngier at arm.com>,
> + * which was based on code by Carl van Schaik <carl at ok-labs.com>.
> + *
> + * SPDX-License-Identifier:	GPL-2.0
> + */
> +#include <config.h>
> +#include <common.h>
> +
> +#include <asm/arch/cpu.h>
> +#include <asm/arch/cpucfg.h>
> +#include <asm/arch/prcm.h>
> +#include <asm/armv7.h>
> +#include <asm/gic.h>
> +#include <asm/io.h>
> +#include <asm/psci.h>
> +#include <asm/system.h>
> +
> +#include <linux/bitops.h>
> +
> +#define __secure	__attribute__ ((section ("._secure.text")))
> +#define __irq		__attribute__ ((interrupt ("IRQ")))
> +
> +#define	GICD_BASE	(SUNXI_GIC400_BASE + GIC_DIST_OFFSET)
> +#define	GICC_BASE	(SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15)
> +
> +static void cp15_write_cntp_tval(u32 tval)
> +{
> +	asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
> +}
> +
> +static void cp15_write_cntp_ctl(u32 val)
> +{
> +	asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
> +}
> +
> +static u32 cp15_read_cntp_ctl(void)
> +{
> +	u32 val;
> +
> +	asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
> +
> +	return val;
> +}
> +
> +static void __secure __mdelay(u32 ms)
> +{
> +	u32 reg = DIV_ROUND_UP(CONFIG_TIMER_CLK_FREQ, ms);
> +
> +	cp15_write_cntp_tval(reg);
> +	ISB;
> +	cp15_write_cntp_ctl(3);
> +
> +	do {
> +		ISB;
> +		reg = cp15_read_cntp_ctl();
> +	} while (!(reg & BIT(2)));
> +
> +	cp15_write_cntp_ctl(0);
> +}
> +
> +#ifdef CONFIG_MACH_SUN7I
> +/* sun7i (A20) is different from other single cluster SoCs */
> +static void sunxi_cpu_set_power(int __always_unused cpu, bool on)

Missing __secure annotation?

> +{
> +	struct sunxi_cpucfg_reg *cpucfg =
> +		(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
> +
> +	if (on) {
> +		/* Release power clamp */
> +		u32 tmp = 0x1ff;
> +		do {
> +			tmp >>= 1;
> +			writel(tmp, &cpucfg->cpu1_pwr_clamp);
> +		} while (tmp);
> +
> +		__mdelay(10);
> +
> +		/* Clear power gating */
> +		clrbits_le32(&cpucfg->cpu1_pwroff, BIT(0));
> +	} else {
> +		/* Set power gating */
> +		setbits_le32(&cpucfg->cpu1_pwroff, BIT(0));
> +
> +		/* Activate power clamp */
> +		writel(0xff, &cpucfg->cpu1_pwr_clamp);
> +	}
> +}
> +#else /* ! CONFIG_MACH_SUN7I */
> +static void sunxi_cpu_set_power(int cpu, bool on)

Same here?

> +{
> +	struct sunxi_prcm_reg *prcm =
> +		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
> +
> +	if (on) {
> +#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN8I_H3)
> +		/* Release power clamp (A31 & H3 only) */
> +		u32 tmp = 0x1ff;
> +		do {
> +			tmp >>= 1;
> +			writel(tmp, &prcm->cpu_pwr_clamp[cpu]);
> +		} while (tmp);
> +#endif

Do you still need these #ifdefs now that you've split the code from the
sun7i case? If you do, you may want to have a set of "power clamp"
operations (which, by the way, would work on sun7i as well).

> +
> +		__mdelay(10);
> +
> +		/* Clear power gating */
> +		clrbits_le32(&prcm->cpu_pwroff, BIT(cpu));
> +	} else {
> +		/* Set power gating */
> +		setbits_le32(&prcm->cpu_pwroff, BIT(cpu));
> +
> +#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN8I_H3)
> +		/* Activate power clamp (A31 & H3 only) */
> +		writel(0xff, &prcm->cpu_pwr_clamp[cpu]);
> +#endif
> +	}
> +}
> +#endif /* CONFIG_MACH_SUN7I */
> +
> +void __secure sunxi_cpu_power_off(u32 cpuid)
> +{
> +	struct sunxi_cpucfg_reg *cpucfg =
> +		(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
> +	u32 cpu = cpuid & 0x3;
> +
> +	/* Wait for the core to enter WFI */
> +	while (1) {
> +		if (readl(&cpucfg->cpu[cpu].status) & BIT(2))
> +			break;
> +		__mdelay(1);
> +	}
> +
> +	/* Assert reset on target CPU */
> +	writel(0, &cpucfg->cpu[cpu].rst);
> +
> +	/* Lock CPU (Disable external debug access) */
> +	clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
> +
> +	/* Power down CPU */
> +	sunxi_cpu_set_power(cpuid, false);
> +
> +	/* Unlock CPU (Disable external debug access) */
> +	setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
> +}
> +
> +static u32 cp15_read_scr(void)
> +{
> +	u32 scr;
> +
> +	asm volatile ("mrc p15, 0, %0, c1, c1, 0" : "=r" (scr));
> +
> +	return scr;
> +}
> +
> +static void cp15_write_scr(u32 scr)
> +{
> +	asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr));
> +}
> +
> +/*
> + * Although this is an FIQ handler, the FIQ is processed in monitor mode,
> + * which means there's no FIQ banked registers. This is the same as IRQ
> + * mode, so use the IRQ attribute to ask the compiler to handler entry
> + * and return.
> + */
> +void __secure __irq psci_fiq_enter(void)
> +{
> +	u32 scr, reg, cpu;
> +
> +	/* Switch to secure mode */
> +	scr = cp15_read_scr();
> +	cp15_write_scr(scr & ~BIT(0));
> +	ISB;
> +
> +	/* Validate reason based on IAR and acknowledge */
> +	reg = readl(GICC_BASE + GICC_IAR);
> +
> +	/* Skip spurious interrupts 1022 and 1023 */
> +	if (reg == 1023 || reg == 1022)
> +		goto out;
> +
> +	/* Acknowledge interrupt */
> +	writel(reg, GICC_BASE + GICC_EOIR);
> +	DSB;
> +
> +	/* Get CPU number */
> +	cpu = (reg >> 10) & 0x7;
> +
> +	/* Power off the CPU */
> +	sunxi_cpu_power_off(cpu);
> +
> +out:
> +	/* Restore security level */
> +	cp15_write_scr(scr);

I'd feel more confident if we had an isb here, or added one to the helper.

Also, I can't see where is the exception return done. Can you shed any
light on it? Have you tried to do a CPU unplug from Linux?

> +}
> +
> +int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc)
> +{
> +	struct sunxi_cpucfg_reg *cpucfg =
> +		(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
> +	u32 cpu = (mpidr & 0x3);
> +
> +	/* store target PC at target CPU stack top */
> +	writel(pc, psci_get_cpu_stack_top(cpu));
> +	DSB;
> +
> +	/* Set secondary core power on PC */
> +	writel((u32)&psci_cpu_entry, &cpucfg->priv0);
> +
> +	/* Assert reset on target CPU */
> +	writel(0, &cpucfg->cpu[cpu].rst);
> +
> +	/* Invalidate L1 cache */
> +	clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu));
> +
> +	/* Lock CPU (Disable external debug access) */
> +	clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
> +
> +	/* 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);
> +
> +	/* Unlock CPU (Disable external debug access) */
> +	setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
> +
> +	return ARM_PSCI_RET_SUCCESS;
> +}
> +
> +void __secure psci_cpu_off(void)
> +{
> +	psci_cpu_off_common();
> +
> +	/* Ask CPU0 via SGI15 to pull the rug... */
> +	writel(BIT(16) | 15, GICD_BASE + GICD_SGIR);
> +	DSB;
> +
> +	/* Wait to be turned off */
> +	while (1)
> +		wfi();
> +}
> +
> +void __secure sunxi_gic_init(void)
> +{
> +	u32 reg;
> +
> +	/* SGI15 as Group-0 */
> +	clrbits_le32(GICD_BASE + GICD_IGROUPRn, BIT(15));
> +
> +	/* Set SGI15 priority to 0 */
> +	writeb(0, GICD_BASE + GICD_IPRIORITYRn + 15);
> +
> +	/* Be cool with non-secure */
> +	writel(0xff, GICC_BASE + GICC_PMR);
> +
> +	/* Switch FIQEn on */
> +	setbits_le32(GICC_BASE + GICC_CTLR, BIT(3));
> +
> +	reg = cp15_read_scr();
> +	reg |= BIT(2);  /* Enable FIQ in monitor mode */
> +	reg &= ~BIT(0); /* Secure mode */
> +	cp15_write_scr(reg);
> +	ISB;

Definitely worth moving that isb inside the helper.

> +}

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...


More information about the U-Boot mailing list