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