blob: 5cb8cfa6cf3fc0035a5a25651f48de8dba2c3986 [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>
Simon Glass274e0b02020-05-10 11:39:56 -060011#include <asm/cache.h>
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080012
13#include <asm/arch/cpu.h>
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080014#include <asm/armv7.h>
15#include <asm/gic.h>
16#include <asm/io.h>
17#include <asm/psci.h>
Chen-Yu Tsai7ca14502016-06-19 12:38:41 +080018#include <asm/secure.h>
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080019#include <asm/system.h>
20
21#include <linux/bitops.h>
22
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080023#define __irq __attribute__ ((interrupt ("IRQ")))
24
25#define GICD_BASE (SUNXI_GIC400_BASE + GIC_DIST_OFFSET)
26#define GICC_BASE (SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15)
27
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +080028/*
Sam Edwards28c68992023-10-11 19:47:55 -060029 * Offsets into the CPUCFG block applicable to most SUNXIs.
30 */
31#define SUNXI_CPU_RST(cpu) (0x40 + (cpu) * 0x40 + 0x0)
32#define SUNXI_CPU_STATUS(cpu) (0x40 + (cpu) * 0x40 + 0x8)
33#define SUNXI_GEN_CTRL (0x184)
34#define SUNXI_PRIV0 (0x1a4)
35#define SUN7I_CPU1_PWR_CLAMP (0x1b0)
36#define SUN7I_CPU1_PWROFF (0x1b4)
37#define SUNXI_DBG_CTRL1 (0x1e4)
38
39/*
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +080040 * R40 is different from other single cluster SoCs.
41 *
42 * The power clamps are located in the unused space after the per-core
43 * reset controls for core 3. The secondary core entry address register
44 * is in the SRAM controller address range.
45 */
46#define SUN8I_R40_PWROFF (0x110)
47#define SUN8I_R40_PWR_CLAMP(cpu) (0x120 + (cpu) * 0x4)
48#define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0 (0xbc)
49
Sam Edwards21e27f02023-10-11 19:47:56 -060050/*
51 * R528 is also different, as it has both cores powered up (but held in reset
52 * state) after the SoC is reset. Like the R40, it uses a "soft" entry point
53 * address register, but unlike the R40, it uses a newer "CPUX" block to manage
54 * CPU state, rather than the older CPUCFG system.
55 */
56#define SUN8I_R528_SOFT_ENTRY (0x1c8)
57#define SUN8I_R528_C0_RST_CTRL (0x0000)
58#define SUN8I_R528_C0_CTRL_REG0 (0x0010)
59#define SUN8I_R528_C0_CPU_STATUS (0x0080)
60
61#define SUN8I_R528_C0_STATUS_STANDBYWFI (16)
62
63/* Only newer cores have this additional IP block. */
64#ifndef SUNXI_R_CPUCFG_BASE
65#define SUNXI_R_CPUCFG_BASE 0
66#endif
67
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080068static void __secure cp15_write_cntp_tval(u32 tval)
69{
70 asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
71}
72
73static void __secure cp15_write_cntp_ctl(u32 val)
74{
75 asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
76}
77
78static u32 __secure cp15_read_cntp_ctl(void)
79{
80 u32 val;
81
82 asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
83
84 return val;
85}
86
Peng Fane7c59392022-04-13 17:47:22 +080087#define ONE_MS (CONFIG_COUNTER_FREQUENCY / 1000)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080088
89static void __secure __mdelay(u32 ms)
90{
91 u32 reg = ONE_MS * ms;
92
93 cp15_write_cntp_tval(reg);
Tom Rini3b787ef2016-08-01 18:54:53 -040094 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080095 cp15_write_cntp_ctl(3);
96
97 do {
Tom Rini3b787ef2016-08-01 18:54:53 -040098 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080099 reg = cp15_read_cntp_ctl();
100 } while (!(reg & BIT(2)));
101
102 cp15_write_cntp_ctl(0);
Tom Rini3b787ef2016-08-01 18:54:53 -0400103 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800104}
105
Sam Edwardscbb505c2023-10-11 19:47:53 -0600106static void __secure clamp_release(u32 *clamp)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800107{
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800108 u32 tmp = 0x1ff;
109 do {
110 tmp >>= 1;
111 writel(tmp, clamp);
112 } while (tmp);
113
114 __mdelay(10);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800115}
116
Sam Edwardscbb505c2023-10-11 19:47:53 -0600117static void __secure clamp_set(u32 *clamp)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800118{
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800119 writel(0xff, clamp);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800120}
121
Sam Edwards1f684b52023-10-11 19:47:54 -0600122static void __secure sunxi_cpu_set_entry(int __always_unused cpu, void *entry)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800123{
Sam Edwardscbb505c2023-10-11 19:47:53 -0600124 if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) {
125 writel((u32)entry,
126 SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0);
Sam Edwards21e27f02023-10-11 19:47:56 -0600127 } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
128 writel((u32)entry,
129 SUNXI_R_CPUCFG_BASE + SUN8I_R528_SOFT_ENTRY);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800130 } else {
Sam Edwards28c68992023-10-11 19:47:55 -0600131 writel((u32)entry, SUNXI_CPUCFG_BASE + SUNXI_PRIV0);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800132 }
133}
134
Sam Edwardscbb505c2023-10-11 19:47:53 -0600135static void __secure sunxi_cpu_set_power(int cpu, bool on)
Chen-Yu Tsaic4a87602017-06-07 15:11:49 +0800136{
Sam Edwardscbb505c2023-10-11 19:47:53 -0600137 u32 *clamp = NULL;
138 u32 *pwroff;
Chen-Yu Tsaic4a87602017-06-07 15:11:49 +0800139
Sam Edwardscbb505c2023-10-11 19:47:53 -0600140 /* sun7i (A20) is different from other single cluster SoCs */
141 if (IS_ENABLED(CONFIG_MACH_SUN7I)) {
Sam Edwards28c68992023-10-11 19:47:55 -0600142 clamp = (void *)SUNXI_CPUCFG_BASE + SUN7I_CPU1_PWR_CLAMP;
143 pwroff = (void *)SUNXI_CPUCFG_BASE + SUN7I_CPU1_PWROFF;
Sam Edwardscbb505c2023-10-11 19:47:53 -0600144 cpu = 0;
145 } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) {
Sam Edwards28c68992023-10-11 19:47:55 -0600146 clamp = (void *)SUNXI_CPUCFG_BASE + SUN8I_R40_PWR_CLAMP(cpu);
147 pwroff = (void *)SUNXI_CPUCFG_BASE + SUN8I_R40_PWROFF;
Sam Edwards21e27f02023-10-11 19:47:56 -0600148 } else if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
149 /* R528 leaves both cores powered up, manages them via reset */
150 return;
Sam Edwardscbb505c2023-10-11 19:47:53 -0600151 } else {
Sam Edwardscbb505c2023-10-11 19:47:53 -0600152 if (IS_ENABLED(CONFIG_MACH_SUN6I) ||
153 IS_ENABLED(CONFIG_MACH_SUN8I_H3))
Sam Edwards28c68992023-10-11 19:47:55 -0600154 clamp = (void *)SUNXI_PRCM_BASE + 0x140 + cpu * 0x4;
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800155
Sam Edwards28c68992023-10-11 19:47:55 -0600156 pwroff = (void *)SUNXI_PRCM_BASE + 0x100;
Sam Edwardscbb505c2023-10-11 19:47:53 -0600157 }
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +0800158
Sam Edwardscbb505c2023-10-11 19:47:53 -0600159 if (on) {
160 /* Release power clamp */
161 if (clamp)
162 clamp_release(clamp);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800163
Sam Edwardscbb505c2023-10-11 19:47:53 -0600164 /* Clear power gating */
165 clrbits_le32(pwroff, BIT(cpu));
166 } else {
167 /* Set power gating */
168 setbits_le32(pwroff, BIT(cpu));
169
170 /* Activate power clamp */
171 if (clamp)
172 clamp_set(clamp);
173 }
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800174}
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800175
Sam Edwards1f684b52023-10-11 19:47:54 -0600176static void __secure sunxi_cpu_set_reset(int cpu, bool reset)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800177{
Sam Edwards21e27f02023-10-11 19:47:56 -0600178 if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
179 if (reset)
180 clrbits_le32(SUNXI_CPUCFG_BASE + SUN8I_R528_C0_RST_CTRL,
181 BIT(cpu));
182 else
183 setbits_le32(SUNXI_CPUCFG_BASE + SUN8I_R528_C0_RST_CTRL,
184 BIT(cpu));
185
186 return;
187 }
188
Sam Edwards28c68992023-10-11 19:47:55 -0600189 writel(reset ? 0b00 : 0b11, SUNXI_CPUCFG_BASE + SUNXI_CPU_RST(cpu));
Sam Edwards1f684b52023-10-11 19:47:54 -0600190}
191
192static void __secure sunxi_cpu_set_locking(int cpu, bool lock)
193{
Sam Edwards21e27f02023-10-11 19:47:56 -0600194 if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
195 /* Not required on R528 */
196 return;
197 }
198
Sam Edwards1f684b52023-10-11 19:47:54 -0600199 if (lock)
Sam Edwards28c68992023-10-11 19:47:55 -0600200 clrbits_le32(SUNXI_CPUCFG_BASE + SUNXI_DBG_CTRL1, BIT(cpu));
Sam Edwards1f684b52023-10-11 19:47:54 -0600201 else
Sam Edwards28c68992023-10-11 19:47:55 -0600202 setbits_le32(SUNXI_CPUCFG_BASE + SUNXI_DBG_CTRL1, BIT(cpu));
Sam Edwards1f684b52023-10-11 19:47:54 -0600203}
204
205static bool __secure sunxi_cpu_poll_wfi(int cpu)
206{
Sam Edwards21e27f02023-10-11 19:47:56 -0600207 if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
208 return !!(readl(SUNXI_CPUCFG_BASE + SUN8I_R528_C0_CPU_STATUS) &
209 BIT(SUN8I_R528_C0_STATUS_STANDBYWFI + cpu));
210 }
211
Sam Edwards28c68992023-10-11 19:47:55 -0600212 return !!(readl(SUNXI_CPUCFG_BASE + SUNXI_CPU_STATUS(cpu)) & BIT(2));
Sam Edwards1f684b52023-10-11 19:47:54 -0600213}
214
215static void __secure sunxi_cpu_invalidate_cache(int cpu)
216{
Sam Edwards21e27f02023-10-11 19:47:56 -0600217 if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) {
218 clrbits_le32(SUNXI_CPUCFG_BASE + SUN8I_R528_C0_CTRL_REG0,
219 BIT(cpu));
220 return;
221 }
222
Sam Edwards28c68992023-10-11 19:47:55 -0600223 clrbits_le32(SUNXI_CPUCFG_BASE + SUNXI_GEN_CTRL, BIT(cpu));
Sam Edwards1f684b52023-10-11 19:47:54 -0600224}
225
226static void __secure sunxi_cpu_power_off(u32 cpuid)
227{
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800228 u32 cpu = cpuid & 0x3;
229
230 /* Wait for the core to enter WFI */
Sam Edwards1f684b52023-10-11 19:47:54 -0600231 while (!sunxi_cpu_poll_wfi(cpu))
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800232 __mdelay(1);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800233
234 /* Assert reset on target CPU */
Sam Edwards1f684b52023-10-11 19:47:54 -0600235 sunxi_cpu_set_reset(cpu, true);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800236
237 /* Lock CPU (Disable external debug access) */
Sam Edwards1f684b52023-10-11 19:47:54 -0600238 sunxi_cpu_set_locking(cpu, true);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800239
240 /* Power down CPU */
241 sunxi_cpu_set_power(cpuid, false);
242
Sam Edwards1f684b52023-10-11 19:47:54 -0600243 /* Unlock CPU (Reenable external debug access) */
244 sunxi_cpu_set_locking(cpu, false);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800245}
246
247static u32 __secure cp15_read_scr(void)
248{
249 u32 scr;
250
251 asm volatile ("mrc p15, 0, %0, c1, c1, 0" : "=r" (scr));
252
253 return scr;
254}
255
256static void __secure cp15_write_scr(u32 scr)
257{
258 asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr));
Tom Rini3b787ef2016-08-01 18:54:53 -0400259 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800260}
261
262/*
263 * Although this is an FIQ handler, the FIQ is processed in monitor mode,
264 * which means there's no FIQ banked registers. This is the same as IRQ
265 * mode, so use the IRQ attribute to ask the compiler to handler entry
266 * and return.
267 */
268void __secure __irq psci_fiq_enter(void)
269{
270 u32 scr, reg, cpu;
271
272 /* Switch to secure mode */
273 scr = cp15_read_scr();
274 cp15_write_scr(scr & ~BIT(0));
275
276 /* Validate reason based on IAR and acknowledge */
277 reg = readl(GICC_BASE + GICC_IAR);
278
279 /* Skip spurious interrupts 1022 and 1023 */
280 if (reg == 1023 || reg == 1022)
281 goto out;
282
283 /* End of interrupt */
284 writel(reg, GICC_BASE + GICC_EOIR);
Tom Rini3b787ef2016-08-01 18:54:53 -0400285 dsb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800286
287 /* Get CPU number */
288 cpu = (reg >> 10) & 0x7;
289
290 /* Power off the CPU */
291 sunxi_cpu_power_off(cpu);
292
293out:
294 /* Restore security level */
295 cp15_write_scr(scr);
296}
297
Patrick Delaunay93b114c2018-04-16 10:15:11 +0200298int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc,
299 u32 context_id)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800300{
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800301 u32 cpu = (mpidr & 0x3);
302
Patrick Delaunay93b114c2018-04-16 10:15:11 +0200303 /* store target PC and context id */
304 psci_save(cpu, pc, context_id);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800305
306 /* Set secondary core power on PC */
Sam Edwards1f684b52023-10-11 19:47:54 -0600307 sunxi_cpu_set_entry(cpu, &psci_cpu_entry);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800308
309 /* Assert reset on target CPU */
Sam Edwards1f684b52023-10-11 19:47:54 -0600310 sunxi_cpu_set_reset(cpu, true);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800311
312 /* Invalidate L1 cache */
Sam Edwards1f684b52023-10-11 19:47:54 -0600313 sunxi_cpu_invalidate_cache(cpu);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800314
315 /* Lock CPU (Disable external debug access) */
Sam Edwards1f684b52023-10-11 19:47:54 -0600316 sunxi_cpu_set_locking(cpu, true);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800317
318 /* Power up target CPU */
319 sunxi_cpu_set_power(cpu, true);
320
321 /* De-assert reset on target CPU */
Sam Edwards1f684b52023-10-11 19:47:54 -0600322 sunxi_cpu_set_reset(cpu, false);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800323
Sam Edwards1f684b52023-10-11 19:47:54 -0600324 /* Unlock CPU (Reenable external debug access) */
325 sunxi_cpu_set_locking(cpu, false);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800326
327 return ARM_PSCI_RET_SUCCESS;
328}
329
Patrick Delaunay9c59d862019-07-22 14:19:20 +0200330s32 __secure psci_cpu_off(void)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800331{
332 psci_cpu_off_common();
333
334 /* Ask CPU0 via SGI15 to pull the rug... */
335 writel(BIT(16) | 15, GICD_BASE + GICD_SGIR);
Tom Rini3b787ef2016-08-01 18:54:53 -0400336 dsb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800337
338 /* Wait to be turned off */
339 while (1)
340 wfi();
341}
342
Chen-Yu Tsai766c22e2016-06-19 12:38:32 +0800343void __secure psci_arch_init(void)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800344{
345 u32 reg;
346
347 /* SGI15 as Group-0 */
348 clrbits_le32(GICD_BASE + GICD_IGROUPRn, BIT(15));
349
350 /* Set SGI15 priority to 0 */
351 writeb(0, GICD_BASE + GICD_IPRIORITYRn + 15);
352
353 /* Be cool with non-secure */
354 writel(0xff, GICC_BASE + GICC_PMR);
355
356 /* Switch FIQEn on */
357 setbits_le32(GICC_BASE + GICC_CTLR, BIT(3));
358
359 reg = cp15_read_scr();
360 reg |= BIT(2); /* Enable FIQ in monitor mode */
361 reg &= ~BIT(0); /* Secure mode */
362 cp15_write_scr(reg);
363}