xref: /openbmc/u-boot/arch/arm/cpu/armv7/sunxi/psci.c (revision 4257f5f8f631147803cdc6693b5046deb1a57be6)
1*4257f5f8SChen-Yu Tsai /*
2*4257f5f8SChen-Yu Tsai  * Copyright (C) 2016
3*4257f5f8SChen-Yu Tsai  * Author: Chen-Yu Tsai <wens@csie.org>
4*4257f5f8SChen-Yu Tsai  *
5*4257f5f8SChen-Yu Tsai  * Based on assembly code by Marc Zyngier <marc.zyngier@arm.com>,
6*4257f5f8SChen-Yu Tsai  * which was based on code by Carl van Schaik <carl@ok-labs.com>.
7*4257f5f8SChen-Yu Tsai  *
8*4257f5f8SChen-Yu Tsai  * SPDX-License-Identifier:	GPL-2.0
9*4257f5f8SChen-Yu Tsai  */
10*4257f5f8SChen-Yu Tsai #include <config.h>
11*4257f5f8SChen-Yu Tsai #include <common.h>
12*4257f5f8SChen-Yu Tsai 
13*4257f5f8SChen-Yu Tsai #include <asm/arch/cpu.h>
14*4257f5f8SChen-Yu Tsai #include <asm/arch/cpucfg.h>
15*4257f5f8SChen-Yu Tsai #include <asm/arch/prcm.h>
16*4257f5f8SChen-Yu Tsai #include <asm/armv7.h>
17*4257f5f8SChen-Yu Tsai #include <asm/gic.h>
18*4257f5f8SChen-Yu Tsai #include <asm/io.h>
19*4257f5f8SChen-Yu Tsai #include <asm/psci.h>
20*4257f5f8SChen-Yu Tsai #include <asm/system.h>
21*4257f5f8SChen-Yu Tsai 
22*4257f5f8SChen-Yu Tsai #include <linux/bitops.h>
23*4257f5f8SChen-Yu Tsai 
24*4257f5f8SChen-Yu Tsai #define __secure	__attribute__ ((section ("._secure.text")))
25*4257f5f8SChen-Yu Tsai #define __irq		__attribute__ ((interrupt ("IRQ")))
26*4257f5f8SChen-Yu Tsai 
27*4257f5f8SChen-Yu Tsai #define	GICD_BASE	(SUNXI_GIC400_BASE + GIC_DIST_OFFSET)
28*4257f5f8SChen-Yu Tsai #define	GICC_BASE	(SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15)
29*4257f5f8SChen-Yu Tsai 
30*4257f5f8SChen-Yu Tsai static void __secure cp15_write_cntp_tval(u32 tval)
31*4257f5f8SChen-Yu Tsai {
32*4257f5f8SChen-Yu Tsai 	asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
33*4257f5f8SChen-Yu Tsai }
34*4257f5f8SChen-Yu Tsai 
35*4257f5f8SChen-Yu Tsai static void __secure cp15_write_cntp_ctl(u32 val)
36*4257f5f8SChen-Yu Tsai {
37*4257f5f8SChen-Yu Tsai 	asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
38*4257f5f8SChen-Yu Tsai }
39*4257f5f8SChen-Yu Tsai 
40*4257f5f8SChen-Yu Tsai static u32 __secure cp15_read_cntp_ctl(void)
41*4257f5f8SChen-Yu Tsai {
42*4257f5f8SChen-Yu Tsai 	u32 val;
43*4257f5f8SChen-Yu Tsai 
44*4257f5f8SChen-Yu Tsai 	asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
45*4257f5f8SChen-Yu Tsai 
46*4257f5f8SChen-Yu Tsai 	return val;
47*4257f5f8SChen-Yu Tsai }
48*4257f5f8SChen-Yu Tsai 
49*4257f5f8SChen-Yu Tsai #define ONE_MS (CONFIG_TIMER_CLK_FREQ / 1000)
50*4257f5f8SChen-Yu Tsai 
51*4257f5f8SChen-Yu Tsai static void __secure __mdelay(u32 ms)
52*4257f5f8SChen-Yu Tsai {
53*4257f5f8SChen-Yu Tsai 	u32 reg = ONE_MS * ms;
54*4257f5f8SChen-Yu Tsai 
55*4257f5f8SChen-Yu Tsai 	cp15_write_cntp_tval(reg);
56*4257f5f8SChen-Yu Tsai 	ISB;
57*4257f5f8SChen-Yu Tsai 	cp15_write_cntp_ctl(3);
58*4257f5f8SChen-Yu Tsai 
59*4257f5f8SChen-Yu Tsai 	do {
60*4257f5f8SChen-Yu Tsai 		ISB;
61*4257f5f8SChen-Yu Tsai 		reg = cp15_read_cntp_ctl();
62*4257f5f8SChen-Yu Tsai 	} while (!(reg & BIT(2)));
63*4257f5f8SChen-Yu Tsai 
64*4257f5f8SChen-Yu Tsai 	cp15_write_cntp_ctl(0);
65*4257f5f8SChen-Yu Tsai 	ISB;
66*4257f5f8SChen-Yu Tsai }
67*4257f5f8SChen-Yu Tsai 
68*4257f5f8SChen-Yu Tsai static void __secure clamp_release(u32 __maybe_unused *clamp)
69*4257f5f8SChen-Yu Tsai {
70*4257f5f8SChen-Yu Tsai #if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
71*4257f5f8SChen-Yu Tsai 	defined(CONFIG_MACH_SUN8I_H3)
72*4257f5f8SChen-Yu Tsai 	u32 tmp = 0x1ff;
73*4257f5f8SChen-Yu Tsai 	do {
74*4257f5f8SChen-Yu Tsai 		tmp >>= 1;
75*4257f5f8SChen-Yu Tsai 		writel(tmp, clamp);
76*4257f5f8SChen-Yu Tsai 	} while (tmp);
77*4257f5f8SChen-Yu Tsai 
78*4257f5f8SChen-Yu Tsai 	__mdelay(10);
79*4257f5f8SChen-Yu Tsai #endif
80*4257f5f8SChen-Yu Tsai }
81*4257f5f8SChen-Yu Tsai 
82*4257f5f8SChen-Yu Tsai static void __secure clamp_set(u32 __maybe_unused *clamp)
83*4257f5f8SChen-Yu Tsai {
84*4257f5f8SChen-Yu Tsai #if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
85*4257f5f8SChen-Yu Tsai 	defined(CONFIG_MACH_SUN8I_H3)
86*4257f5f8SChen-Yu Tsai 	writel(0xff, clamp);
87*4257f5f8SChen-Yu Tsai #endif
88*4257f5f8SChen-Yu Tsai }
89*4257f5f8SChen-Yu Tsai 
90*4257f5f8SChen-Yu Tsai static void __secure sunxi_power_switch(u32 *clamp, u32 *pwroff, bool on,
91*4257f5f8SChen-Yu Tsai 					int cpu)
92*4257f5f8SChen-Yu Tsai {
93*4257f5f8SChen-Yu Tsai 	if (on) {
94*4257f5f8SChen-Yu Tsai 		/* Release power clamp */
95*4257f5f8SChen-Yu Tsai 		clamp_release(clamp);
96*4257f5f8SChen-Yu Tsai 
97*4257f5f8SChen-Yu Tsai 		/* Clear power gating */
98*4257f5f8SChen-Yu Tsai 		clrbits_le32(pwroff, BIT(cpu));
99*4257f5f8SChen-Yu Tsai 	} else {
100*4257f5f8SChen-Yu Tsai 		/* Set power gating */
101*4257f5f8SChen-Yu Tsai 		setbits_le32(pwroff, BIT(cpu));
102*4257f5f8SChen-Yu Tsai 
103*4257f5f8SChen-Yu Tsai 		/* Activate power clamp */
104*4257f5f8SChen-Yu Tsai 		clamp_set(clamp);
105*4257f5f8SChen-Yu Tsai 	}
106*4257f5f8SChen-Yu Tsai }
107*4257f5f8SChen-Yu Tsai 
108*4257f5f8SChen-Yu Tsai #ifdef CONFIG_MACH_SUN7I
109*4257f5f8SChen-Yu Tsai /* sun7i (A20) is different from other single cluster SoCs */
110*4257f5f8SChen-Yu Tsai static void __secure sunxi_cpu_set_power(int __always_unused cpu, bool on)
111*4257f5f8SChen-Yu Tsai {
112*4257f5f8SChen-Yu Tsai 	struct sunxi_cpucfg_reg *cpucfg =
113*4257f5f8SChen-Yu Tsai 		(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
114*4257f5f8SChen-Yu Tsai 
115*4257f5f8SChen-Yu Tsai 	sunxi_power_switch(&cpucfg->cpu1_pwr_clamp, &cpucfg->cpu1_pwroff,
116*4257f5f8SChen-Yu Tsai 			   on, 0);
117*4257f5f8SChen-Yu Tsai }
118*4257f5f8SChen-Yu Tsai #else /* ! CONFIG_MACH_SUN7I */
119*4257f5f8SChen-Yu Tsai static void __secure sunxi_cpu_set_power(int cpu, bool on)
120*4257f5f8SChen-Yu Tsai {
121*4257f5f8SChen-Yu Tsai 	struct sunxi_prcm_reg *prcm =
122*4257f5f8SChen-Yu Tsai 		(struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
123*4257f5f8SChen-Yu Tsai 
124*4257f5f8SChen-Yu Tsai 	sunxi_power_switch(&prcm->cpu_pwr_clamp[cpu], &prcm->cpu_pwroff,
125*4257f5f8SChen-Yu Tsai 			   on, cpu);
126*4257f5f8SChen-Yu Tsai }
127*4257f5f8SChen-Yu Tsai #endif /* CONFIG_MACH_SUN7I */
128*4257f5f8SChen-Yu Tsai 
129*4257f5f8SChen-Yu Tsai void __secure sunxi_cpu_power_off(u32 cpuid)
130*4257f5f8SChen-Yu Tsai {
131*4257f5f8SChen-Yu Tsai 	struct sunxi_cpucfg_reg *cpucfg =
132*4257f5f8SChen-Yu Tsai 		(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
133*4257f5f8SChen-Yu Tsai 	u32 cpu = cpuid & 0x3;
134*4257f5f8SChen-Yu Tsai 
135*4257f5f8SChen-Yu Tsai 	/* Wait for the core to enter WFI */
136*4257f5f8SChen-Yu Tsai 	while (1) {
137*4257f5f8SChen-Yu Tsai 		if (readl(&cpucfg->cpu[cpu].status) & BIT(2))
138*4257f5f8SChen-Yu Tsai 			break;
139*4257f5f8SChen-Yu Tsai 		__mdelay(1);
140*4257f5f8SChen-Yu Tsai 	}
141*4257f5f8SChen-Yu Tsai 
142*4257f5f8SChen-Yu Tsai 	/* Assert reset on target CPU */
143*4257f5f8SChen-Yu Tsai 	writel(0, &cpucfg->cpu[cpu].rst);
144*4257f5f8SChen-Yu Tsai 
145*4257f5f8SChen-Yu Tsai 	/* Lock CPU (Disable external debug access) */
146*4257f5f8SChen-Yu Tsai 	clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
147*4257f5f8SChen-Yu Tsai 
148*4257f5f8SChen-Yu Tsai 	/* Power down CPU */
149*4257f5f8SChen-Yu Tsai 	sunxi_cpu_set_power(cpuid, false);
150*4257f5f8SChen-Yu Tsai 
151*4257f5f8SChen-Yu Tsai 	/* Unlock CPU (Disable external debug access) */
152*4257f5f8SChen-Yu Tsai 	setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
153*4257f5f8SChen-Yu Tsai }
154*4257f5f8SChen-Yu Tsai 
155*4257f5f8SChen-Yu Tsai static u32 __secure cp15_read_scr(void)
156*4257f5f8SChen-Yu Tsai {
157*4257f5f8SChen-Yu Tsai 	u32 scr;
158*4257f5f8SChen-Yu Tsai 
159*4257f5f8SChen-Yu Tsai 	asm volatile ("mrc p15, 0, %0, c1, c1, 0" : "=r" (scr));
160*4257f5f8SChen-Yu Tsai 
161*4257f5f8SChen-Yu Tsai 	return scr;
162*4257f5f8SChen-Yu Tsai }
163*4257f5f8SChen-Yu Tsai 
164*4257f5f8SChen-Yu Tsai static void __secure cp15_write_scr(u32 scr)
165*4257f5f8SChen-Yu Tsai {
166*4257f5f8SChen-Yu Tsai 	asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr));
167*4257f5f8SChen-Yu Tsai 	ISB;
168*4257f5f8SChen-Yu Tsai }
169*4257f5f8SChen-Yu Tsai 
170*4257f5f8SChen-Yu Tsai /*
171*4257f5f8SChen-Yu Tsai  * Although this is an FIQ handler, the FIQ is processed in monitor mode,
172*4257f5f8SChen-Yu Tsai  * which means there's no FIQ banked registers. This is the same as IRQ
173*4257f5f8SChen-Yu Tsai  * mode, so use the IRQ attribute to ask the compiler to handler entry
174*4257f5f8SChen-Yu Tsai  * and return.
175*4257f5f8SChen-Yu Tsai  */
176*4257f5f8SChen-Yu Tsai void __secure __irq psci_fiq_enter(void)
177*4257f5f8SChen-Yu Tsai {
178*4257f5f8SChen-Yu Tsai 	u32 scr, reg, cpu;
179*4257f5f8SChen-Yu Tsai 
180*4257f5f8SChen-Yu Tsai 	/* Switch to secure mode */
181*4257f5f8SChen-Yu Tsai 	scr = cp15_read_scr();
182*4257f5f8SChen-Yu Tsai 	cp15_write_scr(scr & ~BIT(0));
183*4257f5f8SChen-Yu Tsai 
184*4257f5f8SChen-Yu Tsai 	/* Validate reason based on IAR and acknowledge */
185*4257f5f8SChen-Yu Tsai 	reg = readl(GICC_BASE + GICC_IAR);
186*4257f5f8SChen-Yu Tsai 
187*4257f5f8SChen-Yu Tsai 	/* Skip spurious interrupts 1022 and 1023 */
188*4257f5f8SChen-Yu Tsai 	if (reg == 1023 || reg == 1022)
189*4257f5f8SChen-Yu Tsai 		goto out;
190*4257f5f8SChen-Yu Tsai 
191*4257f5f8SChen-Yu Tsai 	/* End of interrupt */
192*4257f5f8SChen-Yu Tsai 	writel(reg, GICC_BASE + GICC_EOIR);
193*4257f5f8SChen-Yu Tsai 	DSB;
194*4257f5f8SChen-Yu Tsai 
195*4257f5f8SChen-Yu Tsai 	/* Get CPU number */
196*4257f5f8SChen-Yu Tsai 	cpu = (reg >> 10) & 0x7;
197*4257f5f8SChen-Yu Tsai 
198*4257f5f8SChen-Yu Tsai 	/* Power off the CPU */
199*4257f5f8SChen-Yu Tsai 	sunxi_cpu_power_off(cpu);
200*4257f5f8SChen-Yu Tsai 
201*4257f5f8SChen-Yu Tsai out:
202*4257f5f8SChen-Yu Tsai 	/* Restore security level */
203*4257f5f8SChen-Yu Tsai 	cp15_write_scr(scr);
204*4257f5f8SChen-Yu Tsai }
205*4257f5f8SChen-Yu Tsai 
206*4257f5f8SChen-Yu Tsai int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc)
207*4257f5f8SChen-Yu Tsai {
208*4257f5f8SChen-Yu Tsai 	struct sunxi_cpucfg_reg *cpucfg =
209*4257f5f8SChen-Yu Tsai 		(struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
210*4257f5f8SChen-Yu Tsai 	u32 cpu = (mpidr & 0x3);
211*4257f5f8SChen-Yu Tsai 
212*4257f5f8SChen-Yu Tsai 	/* store target PC at target CPU stack top */
213*4257f5f8SChen-Yu Tsai 	writel(pc, psci_get_cpu_stack_top(cpu));
214*4257f5f8SChen-Yu Tsai 	DSB;
215*4257f5f8SChen-Yu Tsai 
216*4257f5f8SChen-Yu Tsai 	/* Set secondary core power on PC */
217*4257f5f8SChen-Yu Tsai 	writel((u32)&psci_cpu_entry, &cpucfg->priv0);
218*4257f5f8SChen-Yu Tsai 
219*4257f5f8SChen-Yu Tsai 	/* Assert reset on target CPU */
220*4257f5f8SChen-Yu Tsai 	writel(0, &cpucfg->cpu[cpu].rst);
221*4257f5f8SChen-Yu Tsai 
222*4257f5f8SChen-Yu Tsai 	/* Invalidate L1 cache */
223*4257f5f8SChen-Yu Tsai 	clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu));
224*4257f5f8SChen-Yu Tsai 
225*4257f5f8SChen-Yu Tsai 	/* Lock CPU (Disable external debug access) */
226*4257f5f8SChen-Yu Tsai 	clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
227*4257f5f8SChen-Yu Tsai 
228*4257f5f8SChen-Yu Tsai 	/* Power up target CPU */
229*4257f5f8SChen-Yu Tsai 	sunxi_cpu_set_power(cpu, true);
230*4257f5f8SChen-Yu Tsai 
231*4257f5f8SChen-Yu Tsai 	/* De-assert reset on target CPU */
232*4257f5f8SChen-Yu Tsai 	writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst);
233*4257f5f8SChen-Yu Tsai 
234*4257f5f8SChen-Yu Tsai 	/* Unlock CPU (Disable external debug access) */
235*4257f5f8SChen-Yu Tsai 	setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
236*4257f5f8SChen-Yu Tsai 
237*4257f5f8SChen-Yu Tsai 	return ARM_PSCI_RET_SUCCESS;
238*4257f5f8SChen-Yu Tsai }
239*4257f5f8SChen-Yu Tsai 
240*4257f5f8SChen-Yu Tsai void __secure psci_cpu_off(void)
241*4257f5f8SChen-Yu Tsai {
242*4257f5f8SChen-Yu Tsai 	psci_cpu_off_common();
243*4257f5f8SChen-Yu Tsai 
244*4257f5f8SChen-Yu Tsai 	/* Ask CPU0 via SGI15 to pull the rug... */
245*4257f5f8SChen-Yu Tsai 	writel(BIT(16) | 15, GICD_BASE + GICD_SGIR);
246*4257f5f8SChen-Yu Tsai 	DSB;
247*4257f5f8SChen-Yu Tsai 
248*4257f5f8SChen-Yu Tsai 	/* Wait to be turned off */
249*4257f5f8SChen-Yu Tsai 	while (1)
250*4257f5f8SChen-Yu Tsai 		wfi();
251*4257f5f8SChen-Yu Tsai }
252*4257f5f8SChen-Yu Tsai 
253*4257f5f8SChen-Yu Tsai void __secure sunxi_gic_init(void)
254*4257f5f8SChen-Yu Tsai {
255*4257f5f8SChen-Yu Tsai 	u32 reg;
256*4257f5f8SChen-Yu Tsai 
257*4257f5f8SChen-Yu Tsai 	/* SGI15 as Group-0 */
258*4257f5f8SChen-Yu Tsai 	clrbits_le32(GICD_BASE + GICD_IGROUPRn, BIT(15));
259*4257f5f8SChen-Yu Tsai 
260*4257f5f8SChen-Yu Tsai 	/* Set SGI15 priority to 0 */
261*4257f5f8SChen-Yu Tsai 	writeb(0, GICD_BASE + GICD_IPRIORITYRn + 15);
262*4257f5f8SChen-Yu Tsai 
263*4257f5f8SChen-Yu Tsai 	/* Be cool with non-secure */
264*4257f5f8SChen-Yu Tsai 	writel(0xff, GICC_BASE + GICC_PMR);
265*4257f5f8SChen-Yu Tsai 
266*4257f5f8SChen-Yu Tsai 	/* Switch FIQEn on */
267*4257f5f8SChen-Yu Tsai 	setbits_le32(GICC_BASE + GICC_CTLR, BIT(3));
268*4257f5f8SChen-Yu Tsai 
269*4257f5f8SChen-Yu Tsai 	reg = cp15_read_scr();
270*4257f5f8SChen-Yu Tsai 	reg |= BIT(2);  /* Enable FIQ in monitor mode */
271*4257f5f8SChen-Yu Tsai 	reg &= ~BIT(0); /* Secure mode */
272*4257f5f8SChen-Yu Tsai 	cp15_write_scr(reg);
273*4257f5f8SChen-Yu Tsai }
274