3 * Author: Chen-Yu Tsai <wens@csie.org>
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>.
8 * SPDX-License-Identifier: GPL-2.0
13 #include <asm/arch/cpu.h>
14 #include <asm/arch/cpucfg.h>
15 #include <asm/arch/prcm.h>
16 #include <asm/armv7.h>
20 #include <asm/secure.h>
21 #include <asm/system.h>
23 #include <linux/bitops.h>
25 #define __irq __attribute__ ((interrupt ("IRQ")))
27 #define GICD_BASE (SUNXI_GIC400_BASE + GIC_DIST_OFFSET)
28 #define GICC_BASE (SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15)
30 static void __secure cp15_write_cntp_tval(u32 tval)
32 asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
35 static void __secure cp15_write_cntp_ctl(u32 val)
37 asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
40 static u32 __secure cp15_read_cntp_ctl(void)
44 asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
49 #define ONE_MS (COUNTER_FREQUENCY / 1000)
51 static void __secure __mdelay(u32 ms)
53 u32 reg = ONE_MS * ms;
55 cp15_write_cntp_tval(reg);
57 cp15_write_cntp_ctl(3);
61 reg = cp15_read_cntp_ctl();
62 } while (!(reg & BIT(2)));
64 cp15_write_cntp_ctl(0);
68 static void __secure clamp_release(u32 __maybe_unused *clamp)
70 #if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
71 defined(CONFIG_MACH_SUN8I_H3)
82 static void __secure clamp_set(u32 __maybe_unused *clamp)
84 #if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \
85 defined(CONFIG_MACH_SUN8I_H3)
90 static void __secure sunxi_power_switch(u32 *clamp, u32 *pwroff, bool on,
94 /* Release power clamp */
97 /* Clear power gating */
98 clrbits_le32(pwroff, BIT(cpu));
100 /* Set power gating */
101 setbits_le32(pwroff, BIT(cpu));
103 /* Activate power clamp */
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)
112 struct sunxi_cpucfg_reg *cpucfg =
113 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
115 sunxi_power_switch(&cpucfg->cpu1_pwr_clamp, &cpucfg->cpu1_pwroff,
118 #else /* ! CONFIG_MACH_SUN7I */
119 static void __secure sunxi_cpu_set_power(int cpu, bool on)
121 struct sunxi_prcm_reg *prcm =
122 (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
124 sunxi_power_switch(&prcm->cpu_pwr_clamp[cpu], &prcm->cpu_pwroff,
127 #endif /* CONFIG_MACH_SUN7I */
129 void __secure sunxi_cpu_power_off(u32 cpuid)
131 struct sunxi_cpucfg_reg *cpucfg =
132 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
133 u32 cpu = cpuid & 0x3;
135 /* Wait for the core to enter WFI */
137 if (readl(&cpucfg->cpu[cpu].status) & BIT(2))
142 /* Assert reset on target CPU */
143 writel(0, &cpucfg->cpu[cpu].rst);
145 /* Lock CPU (Disable external debug access) */
146 clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
149 sunxi_cpu_set_power(cpuid, false);
151 /* Unlock CPU (Disable external debug access) */
152 setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
155 static u32 __secure cp15_read_scr(void)
159 asm volatile ("mrc p15, 0, %0, c1, c1, 0" : "=r" (scr));
164 static void __secure cp15_write_scr(u32 scr)
166 asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr));
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
176 void __secure __irq psci_fiq_enter(void)
180 /* Switch to secure mode */
181 scr = cp15_read_scr();
182 cp15_write_scr(scr & ~BIT(0));
184 /* Validate reason based on IAR and acknowledge */
185 reg = readl(GICC_BASE + GICC_IAR);
187 /* Skip spurious interrupts 1022 and 1023 */
188 if (reg == 1023 || reg == 1022)
191 /* End of interrupt */
192 writel(reg, GICC_BASE + GICC_EOIR);
196 cpu = (reg >> 10) & 0x7;
198 /* Power off the CPU */
199 sunxi_cpu_power_off(cpu);
202 /* Restore security level */
206 int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc)
208 struct sunxi_cpucfg_reg *cpucfg =
209 (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
210 u32 cpu = (mpidr & 0x3);
212 /* store target PC */
213 psci_save_target_pc(cpu, pc);
215 /* Set secondary core power on PC */
216 writel((u32)&psci_cpu_entry, &cpucfg->priv0);
218 /* Assert reset on target CPU */
219 writel(0, &cpucfg->cpu[cpu].rst);
221 /* Invalidate L1 cache */
222 clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu));
224 /* Lock CPU (Disable external debug access) */
225 clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
227 /* Power up target CPU */
228 sunxi_cpu_set_power(cpu, true);
230 /* De-assert reset on target CPU */
231 writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst);
233 /* Unlock CPU (Disable external debug access) */
234 setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu));
236 return ARM_PSCI_RET_SUCCESS;
239 void __secure psci_cpu_off(void)
241 psci_cpu_off_common();
243 /* Ask CPU0 via SGI15 to pull the rug... */
244 writel(BIT(16) | 15, GICD_BASE + GICD_SGIR);
247 /* Wait to be turned off */
252 void __secure psci_arch_init(void)
256 /* SGI15 as Group-0 */
257 clrbits_le32(GICD_BASE + GICD_IGROUPRn, BIT(15));
259 /* Set SGI15 priority to 0 */
260 writeb(0, GICD_BASE + GICD_IPRIORITYRn + 15);
262 /* Be cool with non-secure */
263 writel(0xff, GICC_BASE + GICC_PMR);
265 /* Switch FIQEn on */
266 setbits_le32(GICC_BASE + GICC_CTLR, BIT(3));
268 reg = cp15_read_scr();
269 reg |= BIT(2); /* Enable FIQ in monitor mode */
270 reg &= ~BIT(0); /* Secure mode */