]>
Commit | Line | Data |
---|---|---|
4257f5f8 CYT |
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> | |
afc1f65f | 20 | #include <asm/secure.h> |
4257f5f8 CYT |
21 | #include <asm/system.h> |
22 | ||
23 | #include <linux/bitops.h> | |
24 | ||
4257f5f8 CYT |
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 | ||
e4916e85 | 49 | #define ONE_MS (COUNTER_FREQUENCY / 1000) |
4257f5f8 CYT |
50 | |
51 | static void __secure __mdelay(u32 ms) | |
52 | { | |
53 | u32 reg = ONE_MS * ms; | |
54 | ||
55 | cp15_write_cntp_tval(reg); | |
a78cd861 | 56 | isb(); |
4257f5f8 CYT |
57 | cp15_write_cntp_ctl(3); |
58 | ||
59 | do { | |
a78cd861 | 60 | isb(); |
4257f5f8 CYT |
61 | reg = cp15_read_cntp_ctl(); |
62 | } while (!(reg & BIT(2))); | |
63 | ||
64 | cp15_write_cntp_ctl(0); | |
a78cd861 | 65 | isb(); |
4257f5f8 CYT |
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)); | |
a78cd861 | 167 | isb(); |
4257f5f8 CYT |
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); | |
a78cd861 | 193 | dsb(); |
4257f5f8 CYT |
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 | ||
6e6622de CYT |
212 | /* store target PC */ |
213 | psci_save_target_pc(cpu, pc); | |
4257f5f8 CYT |
214 | |
215 | /* Set secondary core power on PC */ | |
216 | writel((u32)&psci_cpu_entry, &cpucfg->priv0); | |
217 | ||
218 | /* Assert reset on target CPU */ | |
219 | writel(0, &cpucfg->cpu[cpu].rst); | |
220 | ||
221 | /* Invalidate L1 cache */ | |
222 | clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu)); | |
223 | ||
224 | /* Lock CPU (Disable external debug access) */ | |
225 | clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); | |
226 | ||
227 | /* Power up target CPU */ | |
228 | sunxi_cpu_set_power(cpu, true); | |
229 | ||
230 | /* De-assert reset on target CPU */ | |
231 | writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst); | |
232 | ||
233 | /* Unlock CPU (Disable external debug access) */ | |
234 | setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); | |
235 | ||
236 | return ARM_PSCI_RET_SUCCESS; | |
237 | } | |
238 | ||
239 | void __secure psci_cpu_off(void) | |
240 | { | |
241 | psci_cpu_off_common(); | |
242 | ||
243 | /* Ask CPU0 via SGI15 to pull the rug... */ | |
244 | writel(BIT(16) | 15, GICD_BASE + GICD_SGIR); | |
a78cd861 | 245 | dsb(); |
4257f5f8 CYT |
246 | |
247 | /* Wait to be turned off */ | |
248 | while (1) | |
249 | wfi(); | |
250 | } | |
251 | ||
94a389b2 | 252 | void __secure psci_arch_init(void) |
4257f5f8 CYT |
253 | { |
254 | u32 reg; | |
255 | ||
256 | /* SGI15 as Group-0 */ | |
257 | clrbits_le32(GICD_BASE + GICD_IGROUPRn, BIT(15)); | |
258 | ||
259 | /* Set SGI15 priority to 0 */ | |
260 | writeb(0, GICD_BASE + GICD_IPRIORITYRn + 15); | |
261 | ||
262 | /* Be cool with non-secure */ | |
263 | writel(0xff, GICC_BASE + GICC_PMR); | |
264 | ||
265 | /* Switch FIQEn on */ | |
266 | setbits_le32(GICC_BASE + GICC_CTLR, BIT(3)); | |
267 | ||
268 | reg = cp15_read_scr(); | |
269 | reg |= BIT(2); /* Enable FIQ in monitor mode */ | |
270 | reg &= ~BIT(0); /* Secure mode */ | |
271 | cp15_write_scr(reg); | |
272 | } |