xref: /openbmc/u-boot/arch/arm/cpu/armv7/sunxi/psci.c (revision e8f80a5a)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0
24257f5f8SChen-Yu Tsai /*
34257f5f8SChen-Yu Tsai  * Copyright (C) 2016
44257f5f8SChen-Yu Tsai  * Author: Chen-Yu Tsai <wens@csie.org>
54257f5f8SChen-Yu Tsai  *
64257f5f8SChen-Yu Tsai  * Based on assembly code by Marc Zyngier <marc.zyngier@arm.com>,
74257f5f8SChen-Yu Tsai  * which was based on code by Carl van Schaik <carl@ok-labs.com>.
84257f5f8SChen-Yu Tsai  */
94257f5f8SChen-Yu Tsai #include <config.h>
104257f5f8SChen-Yu Tsai #include <common.h>
114257f5f8SChen-Yu Tsai 
124257f5f8SChen-Yu Tsai #include <asm/arch/cpu.h>
134257f5f8SChen-Yu Tsai #include <asm/arch/cpucfg.h>
144257f5f8SChen-Yu Tsai #include <asm/arch/prcm.h>
154257f5f8SChen-Yu Tsai #include <asm/armv7.h>
164257f5f8SChen-Yu Tsai #include <asm/gic.h>
174257f5f8SChen-Yu Tsai #include <asm/io.h>
184257f5f8SChen-Yu Tsai #include <asm/psci.h>
19afc1f65fSChen-Yu Tsai #include <asm/secure.h>
204257f5f8SChen-Yu Tsai #include <asm/system.h>
214257f5f8SChen-Yu Tsai 
224257f5f8SChen-Yu Tsai #include <linux/bitops.h>
234257f5f8SChen-Yu Tsai 
244257f5f8SChen-Yu Tsai #define __irq		__attribute__ ((interrupt ("IRQ")))
254257f5f8SChen-Yu Tsai 
264257f5f8SChen-Yu Tsai #define	GICD_BASE	(SUNXI_GIC400_BASE + GIC_DIST_OFFSET)
274257f5f8SChen-Yu Tsai #define	GICC_BASE	(SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15)
284257f5f8SChen-Yu Tsai 
290918648dSChen-Yu Tsai /*
300918648dSChen-Yu Tsai  * R40 is different from other single cluster SoCs.
310918648dSChen-Yu Tsai  *
320918648dSChen-Yu Tsai  * The power clamps are located in the unused space after the per-core
330918648dSChen-Yu Tsai  * reset controls for core 3. The secondary core entry address register
340918648dSChen-Yu Tsai  * is in the SRAM controller address range.
350918648dSChen-Yu Tsai  */
360918648dSChen-Yu Tsai #define SUN8I_R40_PWROFF			(0x110)
370918648dSChen-Yu Tsai #define SUN8I_R40_PWR_CLAMP(cpu)		(0x120 + (cpu) * 0x4)
380918648dSChen-Yu Tsai #define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0		(0xbc)
390918648dSChen-Yu Tsai 
cp15_write_cntp_tval(u32 tval)404257f5f8SChen-Yu Tsai static void __secure cp15_write_cntp_tval(u32 tval)
414257f5f8SChen-Yu Tsai {
424257f5f8SChen-Yu Tsai 	asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
434257f5f8SChen-Yu Tsai }
444257f5f8SChen-Yu Tsai 
cp15_write_cntp_ctl(u32 val)454257f5f8SChen-Yu Tsai static void __secure cp15_write_cntp_ctl(u32 val)
464257f5f8SChen-Yu Tsai {
474257f5f8SChen-Yu Tsai 	asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
484257f5f8SChen-Yu Tsai }
494257f5f8SChen-Yu Tsai 
cp15_read_cntp_ctl(void)504257f5f8SChen-Yu Tsai static u32 __secure cp15_read_cntp_ctl(void)
514257f5f8SChen-Yu Tsai {
524257f5f8SChen-Yu Tsai 	u32 val;
534257f5f8SChen-Yu Tsai 
544257f5f8SChen-Yu Tsai 	asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
554257f5f8SChen-Yu Tsai 
564257f5f8SChen-Yu Tsai 	return val;
574257f5f8SChen-Yu Tsai }
584257f5f8SChen-Yu Tsai 
59e4916e85SAndre Przywara #define ONE_MS (COUNTER_FREQUENCY / 1000)
604257f5f8SChen-Yu Tsai 
__mdelay(u32 ms)614257f5f8SChen-Yu Tsai static void __secure __mdelay(u32 ms)
624257f5f8SChen-Yu Tsai {
634257f5f8SChen-Yu Tsai 	u32 reg = ONE_MS * ms;
644257f5f8SChen-Yu Tsai 
654257f5f8SChen-Yu Tsai 	cp15_write_cntp_tval(reg);
66a78cd861STom Rini 	isb();
674257f5f8SChen-Yu Tsai 	cp15_write_cntp_ctl(3);
684257f5f8SChen-Yu Tsai 
694257f5f8SChen-Yu Tsai 	do {
70a78cd861STom Rini 		isb();
714257f5f8SChen-Yu Tsai 		reg = cp15_read_cntp_ctl();
724257f5f8SChen-Yu Tsai 	} while (!(reg & BIT(2)));
734257f5f8SChen-Yu Tsai 
744257f5f8SChen-Yu Tsai 	cp15_write_cntp_ctl(0);
75a78cd861STom Rini 	isb();
764257f5f8SChen-Yu Tsai }
774257f5f8SChen-Yu Tsai 
clamp_release(u32 __maybe_unused * clamp)784257f5f8SChen-Yu Tsai static void __secure clamp_release(u32 __maybe_unused *clamp)
794257f5f8SChen-Yu Tsai {
804257f5f8SChen-Yu Tsai #if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
810918648dSChen-Yu Tsai 	defined(CONFIG_MACH_SUN8I_H3) || \
820918648dSChen-Yu Tsai 	defined(CONFIG_MACH_SUN8I_R40)
834257f5f8SChen-Yu Tsai 	u32 tmp = 0x1ff;
844257f5f8SChen-Yu Tsai 	do {
854257f5f8SChen-Yu Tsai 		tmp >>= 1;
864257f5f8SChen-Yu Tsai 		writel(tmp, clamp);
874257f5f8SChen-Yu Tsai 	} while (tmp);
884257f5f8SChen-Yu Tsai 
894257f5f8SChen-Yu Tsai 	__mdelay(10);
904257f5f8SChen-Yu Tsai #endif
914257f5f8SChen-Yu Tsai }
924257f5f8SChen-Yu Tsai 
clamp_set(u32 __maybe_unused * clamp)934257f5f8SChen-Yu Tsai static void __secure clamp_set(u32 __maybe_unused *clamp)
944257f5f8SChen-Yu Tsai {
954257f5f8SChen-Yu Tsai #if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
960918648dSChen-Yu Tsai 	defined(CONFIG_MACH_SUN8I_H3) || \
970918648dSChen-Yu Tsai 	defined(CONFIG_MACH_SUN8I_R40)
984257f5f8SChen-Yu Tsai 	writel(0xff, clamp);
994257f5f8SChen-Yu Tsai #endif
1004257f5f8SChen-Yu Tsai }
1014257f5f8SChen-Yu Tsai 
sunxi_power_switch(u32 * clamp,u32 * pwroff,bool on,int cpu)1024257f5f8SChen-Yu Tsai static void __secure sunxi_power_switch(u32 *clamp, u32 *pwroff, bool on,
1034257f5f8SChen-Yu Tsai 					int cpu)
1044257f5f8SChen-Yu Tsai {
1054257f5f8SChen-Yu Tsai 	if (on) {
1064257f5f8SChen-Yu Tsai 		/* Release power clamp */
1074257f5f8SChen-Yu Tsai 		clamp_release(clamp);
1084257f5f8SChen-Yu Tsai 
1094257f5f8SChen-Yu Tsai 		/* Clear power gating */
1104257f5f8SChen-Yu Tsai 		clrbits_le32(pwroff, BIT(cpu));
1114257f5f8SChen-Yu Tsai 	} else {
1124257f5f8SChen-Yu Tsai 		/* Set power gating */
1134257f5f8SChen-Yu Tsai 		setbits_le32(pwroff, BIT(cpu));
1144257f5f8SChen-Yu Tsai 
1154257f5f8SChen-Yu Tsai 		/* Activate power clamp */
1164257f5f8SChen-Yu Tsai 		clamp_set(clamp);
1174257f5f8SChen-Yu Tsai 	}
1184257f5f8SChen-Yu Tsai }
1194257f5f8SChen-Yu Tsai 
12034748272SChen-Yu Tsai #ifdef CONFIG_MACH_SUN8I_R40
12134748272SChen-Yu Tsai /* secondary core entry address is programmed differently on R40 */
sunxi_set_entry_address(void * entry)12234748272SChen-Yu Tsai static void __secure sunxi_set_entry_address(void *entry)
12334748272SChen-Yu Tsai {
12434748272SChen-Yu Tsai 	writel((u32)entry,
12534748272SChen-Yu Tsai 	       SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0);
12634748272SChen-Yu Tsai }
12734748272SChen-Yu Tsai #else
sunxi_set_entry_address(void * entry)12834748272SChen-Yu Tsai static void __secure sunxi_set_entry_address(void *entry)
12934748272SChen-Yu Tsai {
13034748272SChen-Yu Tsai 	struct sunxi_cpucfg_reg *cpucfg =
13134748272SChen-Yu Tsai 		(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
13234748272SChen-Yu Tsai 
13334748272SChen-Yu Tsai 	writel((u32)entry, &cpucfg->priv0);
13434748272SChen-Yu Tsai }
13534748272SChen-Yu Tsai #endif
13634748272SChen-Yu Tsai 
1374257f5f8SChen-Yu Tsai #ifdef CONFIG_MACH_SUN7I
1384257f5f8SChen-Yu Tsai /* sun7i (A20) is different from other single cluster SoCs */
sunxi_cpu_set_power(int __always_unused cpu,bool on)1394257f5f8SChen-Yu Tsai static void __secure sunxi_cpu_set_power(int __always_unused cpu, bool on)
1404257f5f8SChen-Yu Tsai {
1414257f5f8SChen-Yu Tsai 	struct sunxi_cpucfg_reg *cpucfg =
1424257f5f8SChen-Yu Tsai 		(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
1434257f5f8SChen-Yu Tsai 
1444257f5f8SChen-Yu Tsai 	sunxi_power_switch(&cpucfg->cpu1_pwr_clamp, &cpucfg->cpu1_pwroff,
1454257f5f8SChen-Yu Tsai 			   on, 0);
1464257f5f8SChen-Yu Tsai }
1470918648dSChen-Yu Tsai #elif defined CONFIG_MACH_SUN8I_R40
sunxi_cpu_set_power(int cpu,bool on)1480918648dSChen-Yu Tsai static void __secure sunxi_cpu_set_power(int cpu, bool on)
1490918648dSChen-Yu Tsai {
1500918648dSChen-Yu Tsai 	struct sunxi_cpucfg_reg *cpucfg =
1510918648dSChen-Yu Tsai 		(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
1520918648dSChen-Yu Tsai 
1530918648dSChen-Yu Tsai 	sunxi_power_switch((void *)cpucfg + SUN8I_R40_PWR_CLAMP(cpu),
1540918648dSChen-Yu Tsai 			   (void *)cpucfg + SUN8I_R40_PWROFF,
1550918648dSChen-Yu Tsai 			   on, 0);
1560918648dSChen-Yu Tsai }
1570918648dSChen-Yu Tsai #else /* ! CONFIG_MACH_SUN7I && ! CONFIG_MACH_SUN8I_R40 */
sunxi_cpu_set_power(int cpu,bool on)1584257f5f8SChen-Yu Tsai static void __secure sunxi_cpu_set_power(int cpu, bool on)
1594257f5f8SChen-Yu Tsai {
1604257f5f8SChen-Yu Tsai 	struct sunxi_prcm_reg *prcm =
1614257f5f8SChen-Yu Tsai 		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
1624257f5f8SChen-Yu Tsai 
1634257f5f8SChen-Yu Tsai 	sunxi_power_switch(&prcm->cpu_pwr_clamp[cpu], &prcm->cpu_pwroff,
1644257f5f8SChen-Yu Tsai 			   on, cpu);
1654257f5f8SChen-Yu Tsai }
1664257f5f8SChen-Yu Tsai #endif /* CONFIG_MACH_SUN7I */
1674257f5f8SChen-Yu Tsai 
sunxi_cpu_power_off(u32 cpuid)1684257f5f8SChen-Yu Tsai void __secure sunxi_cpu_power_off(u32 cpuid)
1694257f5f8SChen-Yu Tsai {
1704257f5f8SChen-Yu Tsai 	struct sunxi_cpucfg_reg *cpucfg =
1714257f5f8SChen-Yu Tsai 		(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
1724257f5f8SChen-Yu Tsai 	u32 cpu = cpuid & 0x3;
1734257f5f8SChen-Yu Tsai 
1744257f5f8SChen-Yu Tsai 	/* Wait for the core to enter WFI */
1754257f5f8SChen-Yu Tsai 	while (1) {
1764257f5f8SChen-Yu Tsai 		if (readl(&cpucfg->cpu[cpu].status) & BIT(2))
1774257f5f8SChen-Yu Tsai 			break;
1784257f5f8SChen-Yu Tsai 		__mdelay(1);
1794257f5f8SChen-Yu Tsai 	}
1804257f5f8SChen-Yu Tsai 
1814257f5f8SChen-Yu Tsai 	/* Assert reset on target CPU */
1824257f5f8SChen-Yu Tsai 	writel(0, &cpucfg->cpu[cpu].rst);
1834257f5f8SChen-Yu Tsai 
1844257f5f8SChen-Yu Tsai 	/* Lock CPU (Disable external debug access) */
1854257f5f8SChen-Yu Tsai 	clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
1864257f5f8SChen-Yu Tsai 
1874257f5f8SChen-Yu Tsai 	/* Power down CPU */
1884257f5f8SChen-Yu Tsai 	sunxi_cpu_set_power(cpuid, false);
1894257f5f8SChen-Yu Tsai 
1904257f5f8SChen-Yu Tsai 	/* Unlock CPU (Disable external debug access) */
1914257f5f8SChen-Yu Tsai 	setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
1924257f5f8SChen-Yu Tsai }
1934257f5f8SChen-Yu Tsai 
cp15_read_scr(void)1944257f5f8SChen-Yu Tsai static u32 __secure cp15_read_scr(void)
1954257f5f8SChen-Yu Tsai {
1964257f5f8SChen-Yu Tsai 	u32 scr;
1974257f5f8SChen-Yu Tsai 
1984257f5f8SChen-Yu Tsai 	asm volatile ("mrc p15, 0, %0, c1, c1, 0" : "=r" (scr));
1994257f5f8SChen-Yu Tsai 
2004257f5f8SChen-Yu Tsai 	return scr;
2014257f5f8SChen-Yu Tsai }
2024257f5f8SChen-Yu Tsai 
cp15_write_scr(u32 scr)2034257f5f8SChen-Yu Tsai static void __secure cp15_write_scr(u32 scr)
2044257f5f8SChen-Yu Tsai {
2054257f5f8SChen-Yu Tsai 	asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr));
206a78cd861STom Rini 	isb();
2074257f5f8SChen-Yu Tsai }
2084257f5f8SChen-Yu Tsai 
2094257f5f8SChen-Yu Tsai /*
2104257f5f8SChen-Yu Tsai  * Although this is an FIQ handler, the FIQ is processed in monitor mode,
2114257f5f8SChen-Yu Tsai  * which means there's no FIQ banked registers. This is the same as IRQ
2124257f5f8SChen-Yu Tsai  * mode, so use the IRQ attribute to ask the compiler to handler entry
2134257f5f8SChen-Yu Tsai  * and return.
2144257f5f8SChen-Yu Tsai  */
psci_fiq_enter(void)2154257f5f8SChen-Yu Tsai void __secure __irq psci_fiq_enter(void)
2164257f5f8SChen-Yu Tsai {
2174257f5f8SChen-Yu Tsai 	u32 scr, reg, cpu;
2184257f5f8SChen-Yu Tsai 
2194257f5f8SChen-Yu Tsai 	/* Switch to secure mode */
2204257f5f8SChen-Yu Tsai 	scr = cp15_read_scr();
2214257f5f8SChen-Yu Tsai 	cp15_write_scr(scr & ~BIT(0));
2224257f5f8SChen-Yu Tsai 
2234257f5f8SChen-Yu Tsai 	/* Validate reason based on IAR and acknowledge */
2244257f5f8SChen-Yu Tsai 	reg = readl(GICC_BASE + GICC_IAR);
2254257f5f8SChen-Yu Tsai 
2264257f5f8SChen-Yu Tsai 	/* Skip spurious interrupts 1022 and 1023 */
2274257f5f8SChen-Yu Tsai 	if (reg == 1023 || reg == 1022)
2284257f5f8SChen-Yu Tsai 		goto out;
2294257f5f8SChen-Yu Tsai 
2304257f5f8SChen-Yu Tsai 	/* End of interrupt */
2314257f5f8SChen-Yu Tsai 	writel(reg, GICC_BASE + GICC_EOIR);
232a78cd861STom Rini 	dsb();
2334257f5f8SChen-Yu Tsai 
2344257f5f8SChen-Yu Tsai 	/* Get CPU number */
2354257f5f8SChen-Yu Tsai 	cpu = (reg >> 10) & 0x7;
2364257f5f8SChen-Yu Tsai 
2374257f5f8SChen-Yu Tsai 	/* Power off the CPU */
2384257f5f8SChen-Yu Tsai 	sunxi_cpu_power_off(cpu);
2394257f5f8SChen-Yu Tsai 
2404257f5f8SChen-Yu Tsai out:
2414257f5f8SChen-Yu Tsai 	/* Restore security level */
2424257f5f8SChen-Yu Tsai 	cp15_write_scr(scr);
2434257f5f8SChen-Yu Tsai }
2444257f5f8SChen-Yu Tsai 
psci_cpu_on(u32 __always_unused unused,u32 mpidr,u32 pc,u32 context_id)245*9622c7e6SPatrick Delaunay int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc,
246*9622c7e6SPatrick Delaunay 			 u32 context_id)
2474257f5f8SChen-Yu Tsai {
2484257f5f8SChen-Yu Tsai 	struct sunxi_cpucfg_reg *cpucfg =
2494257f5f8SChen-Yu Tsai 		(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
2504257f5f8SChen-Yu Tsai 	u32 cpu = (mpidr & 0x3);
2514257f5f8SChen-Yu Tsai 
252*9622c7e6SPatrick Delaunay 	/* store target PC and context id */
253*9622c7e6SPatrick Delaunay 	psci_save(cpu, pc, context_id);
2544257f5f8SChen-Yu Tsai 
2554257f5f8SChen-Yu Tsai 	/* Set secondary core power on PC */
25634748272SChen-Yu Tsai 	sunxi_set_entry_address(&psci_cpu_entry);
2574257f5f8SChen-Yu Tsai 
2584257f5f8SChen-Yu Tsai 	/* Assert reset on target CPU */
2594257f5f8SChen-Yu Tsai 	writel(0, &cpucfg->cpu[cpu].rst);
2604257f5f8SChen-Yu Tsai 
2614257f5f8SChen-Yu Tsai 	/* Invalidate L1 cache */
2624257f5f8SChen-Yu Tsai 	clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu));
2634257f5f8SChen-Yu Tsai 
2644257f5f8SChen-Yu Tsai 	/* Lock CPU (Disable external debug access) */
2654257f5f8SChen-Yu Tsai 	clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
2664257f5f8SChen-Yu Tsai 
2674257f5f8SChen-Yu Tsai 	/* Power up target CPU */
2684257f5f8SChen-Yu Tsai 	sunxi_cpu_set_power(cpu, true);
2694257f5f8SChen-Yu Tsai 
2704257f5f8SChen-Yu Tsai 	/* De-assert reset on target CPU */
2714257f5f8SChen-Yu Tsai 	writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst);
2724257f5f8SChen-Yu Tsai 
2734257f5f8SChen-Yu Tsai 	/* Unlock CPU (Disable external debug access) */
2744257f5f8SChen-Yu Tsai 	setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
2754257f5f8SChen-Yu Tsai 
2764257f5f8SChen-Yu Tsai 	return ARM_PSCI_RET_SUCCESS;
2774257f5f8SChen-Yu Tsai }
2784257f5f8SChen-Yu Tsai 
psci_cpu_off(void)2794257f5f8SChen-Yu Tsai void __secure psci_cpu_off(void)
2804257f5f8SChen-Yu Tsai {
2814257f5f8SChen-Yu Tsai 	psci_cpu_off_common();
2824257f5f8SChen-Yu Tsai 
2834257f5f8SChen-Yu Tsai 	/* Ask CPU0 via SGI15 to pull the rug... */
2844257f5f8SChen-Yu Tsai 	writel(BIT(16) | 15, GICD_BASE + GICD_SGIR);
285a78cd861STom Rini 	dsb();
2864257f5f8SChen-Yu Tsai 
2874257f5f8SChen-Yu Tsai 	/* Wait to be turned off */
2884257f5f8SChen-Yu Tsai 	while (1)
2894257f5f8SChen-Yu Tsai 		wfi();
2904257f5f8SChen-Yu Tsai }
2914257f5f8SChen-Yu Tsai 
psci_arch_init(void)29294a389b2SChen-Yu Tsai void __secure psci_arch_init(void)
2934257f5f8SChen-Yu Tsai {
2944257f5f8SChen-Yu Tsai 	u32 reg;
2954257f5f8SChen-Yu Tsai 
2964257f5f8SChen-Yu Tsai 	/* SGI15 as Group-0 */
2974257f5f8SChen-Yu Tsai 	clrbits_le32(GICD_BASE + GICD_IGROUPRn, BIT(15));
2984257f5f8SChen-Yu Tsai 
2994257f5f8SChen-Yu Tsai 	/* Set SGI15 priority to 0 */
3004257f5f8SChen-Yu Tsai 	writeb(0, GICD_BASE + GICD_IPRIORITYRn + 15);
3014257f5f8SChen-Yu Tsai 
3024257f5f8SChen-Yu Tsai 	/* Be cool with non-secure */
3034257f5f8SChen-Yu Tsai 	writel(0xff, GICC_BASE + GICC_PMR);
3044257f5f8SChen-Yu Tsai 
3054257f5f8SChen-Yu Tsai 	/* Switch FIQEn on */
3064257f5f8SChen-Yu Tsai 	setbits_le32(GICC_BASE + GICC_CTLR, BIT(3));
3074257f5f8SChen-Yu Tsai 
3084257f5f8SChen-Yu Tsai 	reg = cp15_read_scr();
3094257f5f8SChen-Yu Tsai 	reg |= BIT(2);  /* Enable FIQ in monitor mode */
3104257f5f8SChen-Yu Tsai 	reg &= ~BIT(0); /* Secure mode */
3114257f5f8SChen-Yu Tsai 	cp15_write_scr(reg);
3124257f5f8SChen-Yu Tsai }
313