blob: 5b689004e89c9bafdc71eccb2228740ac53091d4 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +08002/*
3 * Copyright (C) 2016
4 * Author: Chen-Yu Tsai <wens@csie.org>
5 *
6 * Based on assembly code by Marc Zyngier <marc.zyngier@arm.com>,
7 * which was based on code by Carl van Schaik <carl@ok-labs.com>.
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +08008 */
9#include <config.h>
10#include <common.h>
11
12#include <asm/arch/cpu.h>
13#include <asm/arch/cpucfg.h>
14#include <asm/arch/prcm.h>
15#include <asm/armv7.h>
16#include <asm/gic.h>
17#include <asm/io.h>
18#include <asm/psci.h>
Chen-Yu Tsai7ca14502016-06-19 12:38:41 +080019#include <asm/secure.h>
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080020#include <asm/system.h>
21
22#include <linux/bitops.h>
23
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080024#define __irq __attribute__ ((interrupt ("IRQ")))
25
26#define GICD_BASE (SUNXI_GIC400_BASE + GIC_DIST_OFFSET)
27#define GICC_BASE (SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15)
28
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +080029/*
30 * R40 is different from other single cluster SoCs.
31 *
32 * The power clamps are located in the unused space after the per-core
33 * reset controls for core 3. The secondary core entry address register
34 * is in the SRAM controller address range.
35 */
36#define SUN8I_R40_PWROFF (0x110)
37#define SUN8I_R40_PWR_CLAMP(cpu) (0x120 + (cpu) * 0x4)
38#define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0 (0xbc)
39
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080040static void __secure cp15_write_cntp_tval(u32 tval)
41{
42 asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
43}
44
45static void __secure cp15_write_cntp_ctl(u32 val)
46{
47 asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
48}
49
50static u32 __secure cp15_read_cntp_ctl(void)
51{
52 u32 val;
53
54 asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
55
56 return val;
57}
58
Andre Przywara70c78932017-02-16 01:20:19 +000059#define ONE_MS (COUNTER_FREQUENCY / 1000)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080060
61static void __secure __mdelay(u32 ms)
62{
63 u32 reg = ONE_MS * ms;
64
65 cp15_write_cntp_tval(reg);
Tom Rini3b787ef2016-08-01 18:54:53 -040066 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080067 cp15_write_cntp_ctl(3);
68
69 do {
Tom Rini3b787ef2016-08-01 18:54:53 -040070 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080071 reg = cp15_read_cntp_ctl();
72 } while (!(reg & BIT(2)));
73
74 cp15_write_cntp_ctl(0);
Tom Rini3b787ef2016-08-01 18:54:53 -040075 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080076}
77
Heinrich Schuchardt4fe345972019-11-10 04:10:27 +010078static void __secure clamp_release(void __maybe_unused *clamp)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080079{
80#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +080081 defined(CONFIG_MACH_SUN8I_H3) || \
82 defined(CONFIG_MACH_SUN8I_R40)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080083 u32 tmp = 0x1ff;
84 do {
85 tmp >>= 1;
86 writel(tmp, clamp);
87 } while (tmp);
88
89 __mdelay(10);
90#endif
91}
92
Heinrich Schuchardt4fe345972019-11-10 04:10:27 +010093static void __secure clamp_set(void __maybe_unused *clamp)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080094{
95#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +080096 defined(CONFIG_MACH_SUN8I_H3) || \
97 defined(CONFIG_MACH_SUN8I_R40)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080098 writel(0xff, clamp);
99#endif
100}
101
Heinrich Schuchardt4fe345972019-11-10 04:10:27 +0100102static void __secure sunxi_power_switch(void *clamp, void *pwroff_ptr, bool on,
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800103 int cpu)
104{
Heinrich Schuchardt4fe345972019-11-10 04:10:27 +0100105 u32 pwroff;
106
107 memcpy(&pwroff, pwroff_ptr, sizeof(u32));
108
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800109 if (on) {
110 /* Release power clamp */
111 clamp_release(clamp);
112
113 /* Clear power gating */
Heinrich Schuchardt4fe345972019-11-10 04:10:27 +0100114 clrbits_le32(&pwroff, BIT(cpu));
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800115 } else {
116 /* Set power gating */
Heinrich Schuchardt4fe345972019-11-10 04:10:27 +0100117 setbits_le32(&pwroff, BIT(cpu));
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800118
119 /* Activate power clamp */
120 clamp_set(clamp);
121 }
Heinrich Schuchardt4fe345972019-11-10 04:10:27 +0100122
123 memcpy(pwroff_ptr, &pwroff, sizeof(u32));
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800124}
125
Chen-Yu Tsaic4a87602017-06-07 15:11:49 +0800126#ifdef CONFIG_MACH_SUN8I_R40
127/* secondary core entry address is programmed differently on R40 */
128static void __secure sunxi_set_entry_address(void *entry)
129{
130 writel((u32)entry,
131 SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0);
132}
133#else
134static void __secure sunxi_set_entry_address(void *entry)
135{
136 struct sunxi_cpucfg_reg *cpucfg =
137 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
138
139 writel((u32)entry, &cpucfg->priv0);
140}
141#endif
142
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800143#ifdef CONFIG_MACH_SUN7I
144/* sun7i (A20) is different from other single cluster SoCs */
145static void __secure sunxi_cpu_set_power(int __always_unused cpu, bool on)
146{
147 struct sunxi_cpucfg_reg *cpucfg =
148 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
149
150 sunxi_power_switch(&cpucfg->cpu1_pwr_clamp, &cpucfg->cpu1_pwroff,
151 on, 0);
152}
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +0800153#elif defined CONFIG_MACH_SUN8I_R40
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800154static void __secure sunxi_cpu_set_power(int cpu, bool on)
155{
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +0800156 struct sunxi_cpucfg_reg *cpucfg =
157 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
158
159 sunxi_power_switch((void *)cpucfg + SUN8I_R40_PWR_CLAMP(cpu),
160 (void *)cpucfg + SUN8I_R40_PWROFF,
161 on, 0);
162}
163#else /* ! CONFIG_MACH_SUN7I && ! CONFIG_MACH_SUN8I_R40 */
164static void __secure sunxi_cpu_set_power(int cpu, bool on)
165{
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800166 struct sunxi_prcm_reg *prcm =
167 (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
168
169 sunxi_power_switch(&prcm->cpu_pwr_clamp[cpu], &prcm->cpu_pwroff,
170 on, cpu);
171}
172#endif /* CONFIG_MACH_SUN7I */
173
174void __secure sunxi_cpu_power_off(u32 cpuid)
175{
176 struct sunxi_cpucfg_reg *cpucfg =
177 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
178 u32 cpu = cpuid & 0x3;
179
180 /* Wait for the core to enter WFI */
181 while (1) {
182 if (readl(&cpucfg->cpu[cpu].status) & BIT(2))
183 break;
184 __mdelay(1);
185 }
186
187 /* Assert reset on target CPU */
188 writel(0, &cpucfg->cpu[cpu].rst);
189
190 /* Lock CPU (Disable external debug access) */
191 clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
192
193 /* Power down CPU */
194 sunxi_cpu_set_power(cpuid, false);
195
196 /* Unlock CPU (Disable external debug access) */
197 setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
198}
199
200static u32 __secure cp15_read_scr(void)
201{
202 u32 scr;
203
204 asm volatile ("mrc p15, 0, %0, c1, c1, 0" : "=r" (scr));
205
206 return scr;
207}
208
209static void __secure cp15_write_scr(u32 scr)
210{
211 asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr));
Tom Rini3b787ef2016-08-01 18:54:53 -0400212 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800213}
214
215/*
216 * Although this is an FIQ handler, the FIQ is processed in monitor mode,
217 * which means there's no FIQ banked registers. This is the same as IRQ
218 * mode, so use the IRQ attribute to ask the compiler to handler entry
219 * and return.
220 */
221void __secure __irq psci_fiq_enter(void)
222{
223 u32 scr, reg, cpu;
224
225 /* Switch to secure mode */
226 scr = cp15_read_scr();
227 cp15_write_scr(scr & ~BIT(0));
228
229 /* Validate reason based on IAR and acknowledge */
230 reg = readl(GICC_BASE + GICC_IAR);
231
232 /* Skip spurious interrupts 1022 and 1023 */
233 if (reg == 1023 || reg == 1022)
234 goto out;
235
236 /* End of interrupt */
237 writel(reg, GICC_BASE + GICC_EOIR);
Tom Rini3b787ef2016-08-01 18:54:53 -0400238 dsb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800239
240 /* Get CPU number */
241 cpu = (reg >> 10) & 0x7;
242
243 /* Power off the CPU */
244 sunxi_cpu_power_off(cpu);
245
246out:
247 /* Restore security level */
248 cp15_write_scr(scr);
249}
250
Patrick Delaunay93b114c2018-04-16 10:15:11 +0200251int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc,
252 u32 context_id)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800253{
254 struct sunxi_cpucfg_reg *cpucfg =
255 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
256 u32 cpu = (mpidr & 0x3);
257
Patrick Delaunay93b114c2018-04-16 10:15:11 +0200258 /* store target PC and context id */
259 psci_save(cpu, pc, context_id);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800260
261 /* Set secondary core power on PC */
Chen-Yu Tsaic4a87602017-06-07 15:11:49 +0800262 sunxi_set_entry_address(&psci_cpu_entry);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800263
264 /* Assert reset on target CPU */
265 writel(0, &cpucfg->cpu[cpu].rst);
266
267 /* Invalidate L1 cache */
268 clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu));
269
270 /* Lock CPU (Disable external debug access) */
271 clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
272
273 /* Power up target CPU */
274 sunxi_cpu_set_power(cpu, true);
275
276 /* De-assert reset on target CPU */
277 writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst);
278
279 /* Unlock CPU (Disable external debug access) */
280 setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
281
282 return ARM_PSCI_RET_SUCCESS;
283}
284
Patrick Delaunay9c59d862019-07-22 14:19:20 +0200285s32 __secure psci_cpu_off(void)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800286{
287 psci_cpu_off_common();
288
289 /* Ask CPU0 via SGI15 to pull the rug... */
290 writel(BIT(16) | 15, GICD_BASE + GICD_SGIR);
Tom Rini3b787ef2016-08-01 18:54:53 -0400291 dsb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800292
293 /* Wait to be turned off */
294 while (1)
295 wfi();
296}
297
Chen-Yu Tsai766c22e2016-06-19 12:38:32 +0800298void __secure psci_arch_init(void)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800299{
300 u32 reg;
301
302 /* SGI15 as Group-0 */
303 clrbits_le32(GICD_BASE + GICD_IGROUPRn, BIT(15));
304
305 /* Set SGI15 priority to 0 */
306 writeb(0, GICD_BASE + GICD_IPRIORITYRn + 15);
307
308 /* Be cool with non-secure */
309 writel(0xff, GICC_BASE + GICC_PMR);
310
311 /* Switch FIQEn on */
312 setbits_le32(GICC_BASE + GICC_CTLR, BIT(3));
313
314 reg = cp15_read_scr();
315 reg |= BIT(2); /* Enable FIQ in monitor mode */
316 reg &= ~BIT(0); /* Secure mode */
317 cp15_write_scr(reg);
318}