1*4257f5f8SChen-Yu Tsai /* 2*4257f5f8SChen-Yu Tsai * Copyright (C) 2016 3*4257f5f8SChen-Yu Tsai * Author: Chen-Yu Tsai <wens@csie.org> 4*4257f5f8SChen-Yu Tsai * 5*4257f5f8SChen-Yu Tsai * Based on assembly code by Marc Zyngier <marc.zyngier@arm.com>, 6*4257f5f8SChen-Yu Tsai * which was based on code by Carl van Schaik <carl@ok-labs.com>. 7*4257f5f8SChen-Yu Tsai * 8*4257f5f8SChen-Yu Tsai * SPDX-License-Identifier: GPL-2.0 9*4257f5f8SChen-Yu Tsai */ 10*4257f5f8SChen-Yu Tsai #include <config.h> 11*4257f5f8SChen-Yu Tsai #include <common.h> 12*4257f5f8SChen-Yu Tsai 13*4257f5f8SChen-Yu Tsai #include <asm/arch/cpu.h> 14*4257f5f8SChen-Yu Tsai #include <asm/arch/cpucfg.h> 15*4257f5f8SChen-Yu Tsai #include <asm/arch/prcm.h> 16*4257f5f8SChen-Yu Tsai #include <asm/armv7.h> 17*4257f5f8SChen-Yu Tsai #include <asm/gic.h> 18*4257f5f8SChen-Yu Tsai #include <asm/io.h> 19*4257f5f8SChen-Yu Tsai #include <asm/psci.h> 20*4257f5f8SChen-Yu Tsai #include <asm/system.h> 21*4257f5f8SChen-Yu Tsai 22*4257f5f8SChen-Yu Tsai #include <linux/bitops.h> 23*4257f5f8SChen-Yu Tsai 24*4257f5f8SChen-Yu Tsai #define __secure __attribute__ ((section ("._secure.text"))) 25*4257f5f8SChen-Yu Tsai #define __irq __attribute__ ((interrupt ("IRQ"))) 26*4257f5f8SChen-Yu Tsai 27*4257f5f8SChen-Yu Tsai #define GICD_BASE (SUNXI_GIC400_BASE + GIC_DIST_OFFSET) 28*4257f5f8SChen-Yu Tsai #define GICC_BASE (SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15) 29*4257f5f8SChen-Yu Tsai 30*4257f5f8SChen-Yu Tsai static void __secure cp15_write_cntp_tval(u32 tval) 31*4257f5f8SChen-Yu Tsai { 32*4257f5f8SChen-Yu Tsai asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval)); 33*4257f5f8SChen-Yu Tsai } 34*4257f5f8SChen-Yu Tsai 35*4257f5f8SChen-Yu Tsai static void __secure cp15_write_cntp_ctl(u32 val) 36*4257f5f8SChen-Yu Tsai { 37*4257f5f8SChen-Yu Tsai asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val)); 38*4257f5f8SChen-Yu Tsai } 39*4257f5f8SChen-Yu Tsai 40*4257f5f8SChen-Yu Tsai static u32 __secure cp15_read_cntp_ctl(void) 41*4257f5f8SChen-Yu Tsai { 42*4257f5f8SChen-Yu Tsai u32 val; 43*4257f5f8SChen-Yu Tsai 44*4257f5f8SChen-Yu Tsai asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); 45*4257f5f8SChen-Yu Tsai 46*4257f5f8SChen-Yu Tsai return val; 47*4257f5f8SChen-Yu Tsai } 48*4257f5f8SChen-Yu Tsai 49*4257f5f8SChen-Yu Tsai #define ONE_MS (CONFIG_TIMER_CLK_FREQ / 1000) 50*4257f5f8SChen-Yu Tsai 51*4257f5f8SChen-Yu Tsai static void __secure __mdelay(u32 ms) 52*4257f5f8SChen-Yu Tsai { 53*4257f5f8SChen-Yu Tsai u32 reg = ONE_MS * ms; 54*4257f5f8SChen-Yu Tsai 55*4257f5f8SChen-Yu Tsai cp15_write_cntp_tval(reg); 56*4257f5f8SChen-Yu Tsai ISB; 57*4257f5f8SChen-Yu Tsai cp15_write_cntp_ctl(3); 58*4257f5f8SChen-Yu Tsai 59*4257f5f8SChen-Yu Tsai do { 60*4257f5f8SChen-Yu Tsai ISB; 61*4257f5f8SChen-Yu Tsai reg = cp15_read_cntp_ctl(); 62*4257f5f8SChen-Yu Tsai } while (!(reg & BIT(2))); 63*4257f5f8SChen-Yu Tsai 64*4257f5f8SChen-Yu Tsai cp15_write_cntp_ctl(0); 65*4257f5f8SChen-Yu Tsai ISB; 66*4257f5f8SChen-Yu Tsai } 67*4257f5f8SChen-Yu Tsai 68*4257f5f8SChen-Yu Tsai static void __secure clamp_release(u32 __maybe_unused *clamp) 69*4257f5f8SChen-Yu Tsai { 70*4257f5f8SChen-Yu Tsai #if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \ 71*4257f5f8SChen-Yu Tsai defined(CONFIG_MACH_SUN8I_H3) 72*4257f5f8SChen-Yu Tsai u32 tmp = 0x1ff; 73*4257f5f8SChen-Yu Tsai do { 74*4257f5f8SChen-Yu Tsai tmp >>= 1; 75*4257f5f8SChen-Yu Tsai writel(tmp, clamp); 76*4257f5f8SChen-Yu Tsai } while (tmp); 77*4257f5f8SChen-Yu Tsai 78*4257f5f8SChen-Yu Tsai __mdelay(10); 79*4257f5f8SChen-Yu Tsai #endif 80*4257f5f8SChen-Yu Tsai } 81*4257f5f8SChen-Yu Tsai 82*4257f5f8SChen-Yu Tsai static void __secure clamp_set(u32 __maybe_unused *clamp) 83*4257f5f8SChen-Yu Tsai { 84*4257f5f8SChen-Yu Tsai #if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \ 85*4257f5f8SChen-Yu Tsai defined(CONFIG_MACH_SUN8I_H3) 86*4257f5f8SChen-Yu Tsai writel(0xff, clamp); 87*4257f5f8SChen-Yu Tsai #endif 88*4257f5f8SChen-Yu Tsai } 89*4257f5f8SChen-Yu Tsai 90*4257f5f8SChen-Yu Tsai static void __secure sunxi_power_switch(u32 *clamp, u32 *pwroff, bool on, 91*4257f5f8SChen-Yu Tsai int cpu) 92*4257f5f8SChen-Yu Tsai { 93*4257f5f8SChen-Yu Tsai if (on) { 94*4257f5f8SChen-Yu Tsai /* Release power clamp */ 95*4257f5f8SChen-Yu Tsai clamp_release(clamp); 96*4257f5f8SChen-Yu Tsai 97*4257f5f8SChen-Yu Tsai /* Clear power gating */ 98*4257f5f8SChen-Yu Tsai clrbits_le32(pwroff, BIT(cpu)); 99*4257f5f8SChen-Yu Tsai } else { 100*4257f5f8SChen-Yu Tsai /* Set power gating */ 101*4257f5f8SChen-Yu Tsai setbits_le32(pwroff, BIT(cpu)); 102*4257f5f8SChen-Yu Tsai 103*4257f5f8SChen-Yu Tsai /* Activate power clamp */ 104*4257f5f8SChen-Yu Tsai clamp_set(clamp); 105*4257f5f8SChen-Yu Tsai } 106*4257f5f8SChen-Yu Tsai } 107*4257f5f8SChen-Yu Tsai 108*4257f5f8SChen-Yu Tsai #ifdef CONFIG_MACH_SUN7I 109*4257f5f8SChen-Yu Tsai /* sun7i (A20) is different from other single cluster SoCs */ 110*4257f5f8SChen-Yu Tsai static void __secure sunxi_cpu_set_power(int __always_unused cpu, bool on) 111*4257f5f8SChen-Yu Tsai { 112*4257f5f8SChen-Yu Tsai struct sunxi_cpucfg_reg *cpucfg = 113*4257f5f8SChen-Yu Tsai (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; 114*4257f5f8SChen-Yu Tsai 115*4257f5f8SChen-Yu Tsai sunxi_power_switch(&cpucfg->cpu1_pwr_clamp, &cpucfg->cpu1_pwroff, 116*4257f5f8SChen-Yu Tsai on, 0); 117*4257f5f8SChen-Yu Tsai } 118*4257f5f8SChen-Yu Tsai #else /* ! CONFIG_MACH_SUN7I */ 119*4257f5f8SChen-Yu Tsai static void __secure sunxi_cpu_set_power(int cpu, bool on) 120*4257f5f8SChen-Yu Tsai { 121*4257f5f8SChen-Yu Tsai struct sunxi_prcm_reg *prcm = 122*4257f5f8SChen-Yu Tsai (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; 123*4257f5f8SChen-Yu Tsai 124*4257f5f8SChen-Yu Tsai sunxi_power_switch(&prcm->cpu_pwr_clamp[cpu], &prcm->cpu_pwroff, 125*4257f5f8SChen-Yu Tsai on, cpu); 126*4257f5f8SChen-Yu Tsai } 127*4257f5f8SChen-Yu Tsai #endif /* CONFIG_MACH_SUN7I */ 128*4257f5f8SChen-Yu Tsai 129*4257f5f8SChen-Yu Tsai void __secure sunxi_cpu_power_off(u32 cpuid) 130*4257f5f8SChen-Yu Tsai { 131*4257f5f8SChen-Yu Tsai struct sunxi_cpucfg_reg *cpucfg = 132*4257f5f8SChen-Yu Tsai (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; 133*4257f5f8SChen-Yu Tsai u32 cpu = cpuid & 0x3; 134*4257f5f8SChen-Yu Tsai 135*4257f5f8SChen-Yu Tsai /* Wait for the core to enter WFI */ 136*4257f5f8SChen-Yu Tsai while (1) { 137*4257f5f8SChen-Yu Tsai if (readl(&cpucfg->cpu[cpu].status) & BIT(2)) 138*4257f5f8SChen-Yu Tsai break; 139*4257f5f8SChen-Yu Tsai __mdelay(1); 140*4257f5f8SChen-Yu Tsai } 141*4257f5f8SChen-Yu Tsai 142*4257f5f8SChen-Yu Tsai /* Assert reset on target CPU */ 143*4257f5f8SChen-Yu Tsai writel(0, &cpucfg->cpu[cpu].rst); 144*4257f5f8SChen-Yu Tsai 145*4257f5f8SChen-Yu Tsai /* Lock CPU (Disable external debug access) */ 146*4257f5f8SChen-Yu Tsai clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); 147*4257f5f8SChen-Yu Tsai 148*4257f5f8SChen-Yu Tsai /* Power down CPU */ 149*4257f5f8SChen-Yu Tsai sunxi_cpu_set_power(cpuid, false); 150*4257f5f8SChen-Yu Tsai 151*4257f5f8SChen-Yu Tsai /* Unlock CPU (Disable external debug access) */ 152*4257f5f8SChen-Yu Tsai setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); 153*4257f5f8SChen-Yu Tsai } 154*4257f5f8SChen-Yu Tsai 155*4257f5f8SChen-Yu Tsai static u32 __secure cp15_read_scr(void) 156*4257f5f8SChen-Yu Tsai { 157*4257f5f8SChen-Yu Tsai u32 scr; 158*4257f5f8SChen-Yu Tsai 159*4257f5f8SChen-Yu Tsai asm volatile ("mrc p15, 0, %0, c1, c1, 0" : "=r" (scr)); 160*4257f5f8SChen-Yu Tsai 161*4257f5f8SChen-Yu Tsai return scr; 162*4257f5f8SChen-Yu Tsai } 163*4257f5f8SChen-Yu Tsai 164*4257f5f8SChen-Yu Tsai static void __secure cp15_write_scr(u32 scr) 165*4257f5f8SChen-Yu Tsai { 166*4257f5f8SChen-Yu Tsai asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr)); 167*4257f5f8SChen-Yu Tsai ISB; 168*4257f5f8SChen-Yu Tsai } 169*4257f5f8SChen-Yu Tsai 170*4257f5f8SChen-Yu Tsai /* 171*4257f5f8SChen-Yu Tsai * Although this is an FIQ handler, the FIQ is processed in monitor mode, 172*4257f5f8SChen-Yu Tsai * which means there's no FIQ banked registers. This is the same as IRQ 173*4257f5f8SChen-Yu Tsai * mode, so use the IRQ attribute to ask the compiler to handler entry 174*4257f5f8SChen-Yu Tsai * and return. 175*4257f5f8SChen-Yu Tsai */ 176*4257f5f8SChen-Yu Tsai void __secure __irq psci_fiq_enter(void) 177*4257f5f8SChen-Yu Tsai { 178*4257f5f8SChen-Yu Tsai u32 scr, reg, cpu; 179*4257f5f8SChen-Yu Tsai 180*4257f5f8SChen-Yu Tsai /* Switch to secure mode */ 181*4257f5f8SChen-Yu Tsai scr = cp15_read_scr(); 182*4257f5f8SChen-Yu Tsai cp15_write_scr(scr & ~BIT(0)); 183*4257f5f8SChen-Yu Tsai 184*4257f5f8SChen-Yu Tsai /* Validate reason based on IAR and acknowledge */ 185*4257f5f8SChen-Yu Tsai reg = readl(GICC_BASE + GICC_IAR); 186*4257f5f8SChen-Yu Tsai 187*4257f5f8SChen-Yu Tsai /* Skip spurious interrupts 1022 and 1023 */ 188*4257f5f8SChen-Yu Tsai if (reg == 1023 || reg == 1022) 189*4257f5f8SChen-Yu Tsai goto out; 190*4257f5f8SChen-Yu Tsai 191*4257f5f8SChen-Yu Tsai /* End of interrupt */ 192*4257f5f8SChen-Yu Tsai writel(reg, GICC_BASE + GICC_EOIR); 193*4257f5f8SChen-Yu Tsai DSB; 194*4257f5f8SChen-Yu Tsai 195*4257f5f8SChen-Yu Tsai /* Get CPU number */ 196*4257f5f8SChen-Yu Tsai cpu = (reg >> 10) & 0x7; 197*4257f5f8SChen-Yu Tsai 198*4257f5f8SChen-Yu Tsai /* Power off the CPU */ 199*4257f5f8SChen-Yu Tsai sunxi_cpu_power_off(cpu); 200*4257f5f8SChen-Yu Tsai 201*4257f5f8SChen-Yu Tsai out: 202*4257f5f8SChen-Yu Tsai /* Restore security level */ 203*4257f5f8SChen-Yu Tsai cp15_write_scr(scr); 204*4257f5f8SChen-Yu Tsai } 205*4257f5f8SChen-Yu Tsai 206*4257f5f8SChen-Yu Tsai int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc) 207*4257f5f8SChen-Yu Tsai { 208*4257f5f8SChen-Yu Tsai struct sunxi_cpucfg_reg *cpucfg = 209*4257f5f8SChen-Yu Tsai (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; 210*4257f5f8SChen-Yu Tsai u32 cpu = (mpidr & 0x3); 211*4257f5f8SChen-Yu Tsai 212*4257f5f8SChen-Yu Tsai /* store target PC at target CPU stack top */ 213*4257f5f8SChen-Yu Tsai writel(pc, psci_get_cpu_stack_top(cpu)); 214*4257f5f8SChen-Yu Tsai DSB; 215*4257f5f8SChen-Yu Tsai 216*4257f5f8SChen-Yu Tsai /* Set secondary core power on PC */ 217*4257f5f8SChen-Yu Tsai writel((u32)&psci_cpu_entry, &cpucfg->priv0); 218*4257f5f8SChen-Yu Tsai 219*4257f5f8SChen-Yu Tsai /* Assert reset on target CPU */ 220*4257f5f8SChen-Yu Tsai writel(0, &cpucfg->cpu[cpu].rst); 221*4257f5f8SChen-Yu Tsai 222*4257f5f8SChen-Yu Tsai /* Invalidate L1 cache */ 223*4257f5f8SChen-Yu Tsai clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu)); 224*4257f5f8SChen-Yu Tsai 225*4257f5f8SChen-Yu Tsai /* Lock CPU (Disable external debug access) */ 226*4257f5f8SChen-Yu Tsai clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); 227*4257f5f8SChen-Yu Tsai 228*4257f5f8SChen-Yu Tsai /* Power up target CPU */ 229*4257f5f8SChen-Yu Tsai sunxi_cpu_set_power(cpu, true); 230*4257f5f8SChen-Yu Tsai 231*4257f5f8SChen-Yu Tsai /* De-assert reset on target CPU */ 232*4257f5f8SChen-Yu Tsai writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst); 233*4257f5f8SChen-Yu Tsai 234*4257f5f8SChen-Yu Tsai /* Unlock CPU (Disable external debug access) */ 235*4257f5f8SChen-Yu Tsai setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); 236*4257f5f8SChen-Yu Tsai 237*4257f5f8SChen-Yu Tsai return ARM_PSCI_RET_SUCCESS; 238*4257f5f8SChen-Yu Tsai } 239*4257f5f8SChen-Yu Tsai 240*4257f5f8SChen-Yu Tsai void __secure psci_cpu_off(void) 241*4257f5f8SChen-Yu Tsai { 242*4257f5f8SChen-Yu Tsai psci_cpu_off_common(); 243*4257f5f8SChen-Yu Tsai 244*4257f5f8SChen-Yu Tsai /* Ask CPU0 via SGI15 to pull the rug... */ 245*4257f5f8SChen-Yu Tsai writel(BIT(16) | 15, GICD_BASE + GICD_SGIR); 246*4257f5f8SChen-Yu Tsai DSB; 247*4257f5f8SChen-Yu Tsai 248*4257f5f8SChen-Yu Tsai /* Wait to be turned off */ 249*4257f5f8SChen-Yu Tsai while (1) 250*4257f5f8SChen-Yu Tsai wfi(); 251*4257f5f8SChen-Yu Tsai } 252*4257f5f8SChen-Yu Tsai 253*4257f5f8SChen-Yu Tsai void __secure sunxi_gic_init(void) 254*4257f5f8SChen-Yu Tsai { 255*4257f5f8SChen-Yu Tsai u32 reg; 256*4257f5f8SChen-Yu Tsai 257*4257f5f8SChen-Yu Tsai /* SGI15 as Group-0 */ 258*4257f5f8SChen-Yu Tsai clrbits_le32(GICD_BASE + GICD_IGROUPRn, BIT(15)); 259*4257f5f8SChen-Yu Tsai 260*4257f5f8SChen-Yu Tsai /* Set SGI15 priority to 0 */ 261*4257f5f8SChen-Yu Tsai writeb(0, GICD_BASE + GICD_IPRIORITYRn + 15); 262*4257f5f8SChen-Yu Tsai 263*4257f5f8SChen-Yu Tsai /* Be cool with non-secure */ 264*4257f5f8SChen-Yu Tsai writel(0xff, GICC_BASE + GICC_PMR); 265*4257f5f8SChen-Yu Tsai 266*4257f5f8SChen-Yu Tsai /* Switch FIQEn on */ 267*4257f5f8SChen-Yu Tsai setbits_le32(GICC_BASE + GICC_CTLR, BIT(3)); 268*4257f5f8SChen-Yu Tsai 269*4257f5f8SChen-Yu Tsai reg = cp15_read_scr(); 270*4257f5f8SChen-Yu Tsai reg |= BIT(2); /* Enable FIQ in monitor mode */ 271*4257f5f8SChen-Yu Tsai reg &= ~BIT(0); /* Secure mode */ 272*4257f5f8SChen-Yu Tsai cp15_write_scr(reg); 273*4257f5f8SChen-Yu Tsai } 274