blob: b3a34de1aafe5dd13be2f727d3ac15a65b1676d5 [file] [log] [blame]
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +08001/*
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>
Chen-Yu Tsai7ca14502016-06-19 12:38:41 +080020#include <asm/secure.h>
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080021#include <asm/system.h>
22
23#include <linux/bitops.h>
24
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080025#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
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +080030/*
31 * R40 is different from other single cluster SoCs.
32 *
33 * The power clamps are located in the unused space after the per-core
34 * reset controls for core 3. The secondary core entry address register
35 * is in the SRAM controller address range.
36 */
37#define SUN8I_R40_PWROFF (0x110)
38#define SUN8I_R40_PWR_CLAMP(cpu) (0x120 + (cpu) * 0x4)
39#define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0 (0xbc)
40
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080041static void __secure cp15_write_cntp_tval(u32 tval)
42{
43 asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
44}
45
46static void __secure cp15_write_cntp_ctl(u32 val)
47{
48 asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
49}
50
51static u32 __secure cp15_read_cntp_ctl(void)
52{
53 u32 val;
54
55 asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
56
57 return val;
58}
59
Andre Przywara70c78932017-02-16 01:20:19 +000060#define ONE_MS (COUNTER_FREQUENCY / 1000)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080061
62static void __secure __mdelay(u32 ms)
63{
64 u32 reg = ONE_MS * ms;
65
66 cp15_write_cntp_tval(reg);
Tom Rini3b787ef2016-08-01 18:54:53 -040067 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080068 cp15_write_cntp_ctl(3);
69
70 do {
Tom Rini3b787ef2016-08-01 18:54:53 -040071 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080072 reg = cp15_read_cntp_ctl();
73 } while (!(reg & BIT(2)));
74
75 cp15_write_cntp_ctl(0);
Tom Rini3b787ef2016-08-01 18:54:53 -040076 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080077}
78
79static void __secure clamp_release(u32 __maybe_unused *clamp)
80{
81#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +080082 defined(CONFIG_MACH_SUN8I_H3) || \
83 defined(CONFIG_MACH_SUN8I_R40)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080084 u32 tmp = 0x1ff;
85 do {
86 tmp >>= 1;
87 writel(tmp, clamp);
88 } while (tmp);
89
90 __mdelay(10);
91#endif
92}
93
94static void __secure clamp_set(u32 __maybe_unused *clamp)
95{
96#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +080097 defined(CONFIG_MACH_SUN8I_H3) || \
98 defined(CONFIG_MACH_SUN8I_R40)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +080099 writel(0xff, clamp);
100#endif
101}
102
103static void __secure sunxi_power_switch(u32 *clamp, u32 *pwroff, bool on,
104 int cpu)
105{
106 if (on) {
107 /* Release power clamp */
108 clamp_release(clamp);
109
110 /* Clear power gating */
111 clrbits_le32(pwroff, BIT(cpu));
112 } else {
113 /* Set power gating */
114 setbits_le32(pwroff, BIT(cpu));
115
116 /* Activate power clamp */
117 clamp_set(clamp);
118 }
119}
120
121#ifdef CONFIG_MACH_SUN7I
122/* sun7i (A20) is different from other single cluster SoCs */
123static void __secure sunxi_cpu_set_power(int __always_unused cpu, bool on)
124{
125 struct sunxi_cpucfg_reg *cpucfg =
126 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
127
128 sunxi_power_switch(&cpucfg->cpu1_pwr_clamp, &cpucfg->cpu1_pwroff,
129 on, 0);
130}
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +0800131#elif defined CONFIG_MACH_SUN8I_R40
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800132static void __secure sunxi_cpu_set_power(int cpu, bool on)
133{
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +0800134 struct sunxi_cpucfg_reg *cpucfg =
135 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
136
137 sunxi_power_switch((void *)cpucfg + SUN8I_R40_PWR_CLAMP(cpu),
138 (void *)cpucfg + SUN8I_R40_PWROFF,
139 on, 0);
140}
141#else /* ! CONFIG_MACH_SUN7I && ! CONFIG_MACH_SUN8I_R40 */
142static void __secure sunxi_cpu_set_power(int cpu, bool on)
143{
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800144 struct sunxi_prcm_reg *prcm =
145 (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
146
147 sunxi_power_switch(&prcm->cpu_pwr_clamp[cpu], &prcm->cpu_pwroff,
148 on, cpu);
149}
150#endif /* CONFIG_MACH_SUN7I */
151
152void __secure sunxi_cpu_power_off(u32 cpuid)
153{
154 struct sunxi_cpucfg_reg *cpucfg =
155 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
156 u32 cpu = cpuid & 0x3;
157
158 /* Wait for the core to enter WFI */
159 while (1) {
160 if (readl(&cpucfg->cpu[cpu].status) & BIT(2))
161 break;
162 __mdelay(1);
163 }
164
165 /* Assert reset on target CPU */
166 writel(0, &cpucfg->cpu[cpu].rst);
167
168 /* Lock CPU (Disable external debug access) */
169 clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
170
171 /* Power down CPU */
172 sunxi_cpu_set_power(cpuid, false);
173
174 /* Unlock CPU (Disable external debug access) */
175 setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
176}
177
178static u32 __secure cp15_read_scr(void)
179{
180 u32 scr;
181
182 asm volatile ("mrc p15, 0, %0, c1, c1, 0" : "=r" (scr));
183
184 return scr;
185}
186
187static void __secure cp15_write_scr(u32 scr)
188{
189 asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr));
Tom Rini3b787ef2016-08-01 18:54:53 -0400190 isb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800191}
192
193/*
194 * Although this is an FIQ handler, the FIQ is processed in monitor mode,
195 * which means there's no FIQ banked registers. This is the same as IRQ
196 * mode, so use the IRQ attribute to ask the compiler to handler entry
197 * and return.
198 */
199void __secure __irq psci_fiq_enter(void)
200{
201 u32 scr, reg, cpu;
202
203 /* Switch to secure mode */
204 scr = cp15_read_scr();
205 cp15_write_scr(scr & ~BIT(0));
206
207 /* Validate reason based on IAR and acknowledge */
208 reg = readl(GICC_BASE + GICC_IAR);
209
210 /* Skip spurious interrupts 1022 and 1023 */
211 if (reg == 1023 || reg == 1022)
212 goto out;
213
214 /* End of interrupt */
215 writel(reg, GICC_BASE + GICC_EOIR);
Tom Rini3b787ef2016-08-01 18:54:53 -0400216 dsb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800217
218 /* Get CPU number */
219 cpu = (reg >> 10) & 0x7;
220
221 /* Power off the CPU */
222 sunxi_cpu_power_off(cpu);
223
224out:
225 /* Restore security level */
226 cp15_write_scr(scr);
227}
228
229int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc)
230{
231 struct sunxi_cpucfg_reg *cpucfg =
232 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
233 u32 cpu = (mpidr & 0x3);
234
Chen-Yu Tsai6e147952016-06-19 12:38:44 +0800235 /* store target PC */
236 psci_save_target_pc(cpu, pc);
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800237
238 /* Set secondary core power on PC */
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +0800239#ifdef CONFIG_MACH_SUN8I_R40
240 /* secondary core entry address is programmed differently */
241 writel((u32)&psci_cpu_entry,
242 SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0);
243#else
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800244 writel((u32)&psci_cpu_entry, &cpucfg->priv0);
Chen-Yu Tsaib1a1fda2017-03-01 11:03:15 +0800245#endif
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800246
247 /* Assert reset on target CPU */
248 writel(0, &cpucfg->cpu[cpu].rst);
249
250 /* Invalidate L1 cache */
251 clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu));
252
253 /* Lock CPU (Disable external debug access) */
254 clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
255
256 /* Power up target CPU */
257 sunxi_cpu_set_power(cpu, true);
258
259 /* De-assert reset on target CPU */
260 writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst);
261
262 /* Unlock CPU (Disable external debug access) */
263 setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
264
265 return ARM_PSCI_RET_SUCCESS;
266}
267
268void __secure psci_cpu_off(void)
269{
270 psci_cpu_off_common();
271
272 /* Ask CPU0 via SGI15 to pull the rug... */
273 writel(BIT(16) | 15, GICD_BASE + GICD_SGIR);
Tom Rini3b787ef2016-08-01 18:54:53 -0400274 dsb();
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800275
276 /* Wait to be turned off */
277 while (1)
278 wfi();
279}
280
Chen-Yu Tsai766c22e2016-06-19 12:38:32 +0800281void __secure psci_arch_init(void)
Chen-Yu Tsai60e0b182016-06-07 10:54:34 +0800282{
283 u32 reg;
284
285 /* SGI15 as Group-0 */
286 clrbits_le32(GICD_BASE + GICD_IGROUPRn, BIT(15));
287
288 /* Set SGI15 priority to 0 */
289 writeb(0, GICD_BASE + GICD_IPRIORITYRn + 15);
290
291 /* Be cool with non-secure */
292 writel(0xff, GICC_BASE + GICC_PMR);
293
294 /* Switch FIQEn on */
295 setbits_le32(GICC_BASE + GICC_CTLR, BIT(3));
296
297 reg = cp15_read_scr();
298 reg |= BIT(2); /* Enable FIQ in monitor mode */
299 reg &= ~BIT(0); /* Secure mode */
300 cp15_write_scr(reg);
301}