14257f5f8SChen-Yu Tsai /* 24257f5f8SChen-Yu Tsai * Copyright (C) 2016 34257f5f8SChen-Yu Tsai * Author: Chen-Yu Tsai <wens@csie.org> 44257f5f8SChen-Yu Tsai * 54257f5f8SChen-Yu Tsai * Based on assembly code by Marc Zyngier <marc.zyngier@arm.com>, 64257f5f8SChen-Yu Tsai * which was based on code by Carl van Schaik <carl@ok-labs.com>. 74257f5f8SChen-Yu Tsai * 84257f5f8SChen-Yu Tsai * SPDX-License-Identifier: GPL-2.0 94257f5f8SChen-Yu Tsai */ 104257f5f8SChen-Yu Tsai #include <config.h> 114257f5f8SChen-Yu Tsai #include <common.h> 124257f5f8SChen-Yu Tsai 134257f5f8SChen-Yu Tsai #include <asm/arch/cpu.h> 144257f5f8SChen-Yu Tsai #include <asm/arch/cpucfg.h> 154257f5f8SChen-Yu Tsai #include <asm/arch/prcm.h> 164257f5f8SChen-Yu Tsai #include <asm/armv7.h> 174257f5f8SChen-Yu Tsai #include <asm/gic.h> 184257f5f8SChen-Yu Tsai #include <asm/io.h> 194257f5f8SChen-Yu Tsai #include <asm/psci.h> 20afc1f65fSChen-Yu Tsai #include <asm/secure.h> 214257f5f8SChen-Yu Tsai #include <asm/system.h> 224257f5f8SChen-Yu Tsai 234257f5f8SChen-Yu Tsai #include <linux/bitops.h> 244257f5f8SChen-Yu Tsai 254257f5f8SChen-Yu Tsai #define __irq __attribute__ ((interrupt ("IRQ"))) 264257f5f8SChen-Yu Tsai 274257f5f8SChen-Yu Tsai #define GICD_BASE (SUNXI_GIC400_BASE + GIC_DIST_OFFSET) 284257f5f8SChen-Yu Tsai #define GICC_BASE (SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15) 294257f5f8SChen-Yu Tsai 300918648dSChen-Yu Tsai /* 310918648dSChen-Yu Tsai * R40 is different from other single cluster SoCs. 320918648dSChen-Yu Tsai * 330918648dSChen-Yu Tsai * The power clamps are located in the unused space after the per-core 340918648dSChen-Yu Tsai * reset controls for core 3. The secondary core entry address register 350918648dSChen-Yu Tsai * is in the SRAM controller address range. 360918648dSChen-Yu Tsai */ 370918648dSChen-Yu Tsai #define SUN8I_R40_PWROFF (0x110) 380918648dSChen-Yu Tsai #define SUN8I_R40_PWR_CLAMP(cpu) (0x120 + (cpu) * 0x4) 390918648dSChen-Yu Tsai #define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0 (0xbc) 400918648dSChen-Yu Tsai 414257f5f8SChen-Yu Tsai static void __secure cp15_write_cntp_tval(u32 tval) 424257f5f8SChen-Yu Tsai { 434257f5f8SChen-Yu Tsai asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval)); 444257f5f8SChen-Yu Tsai } 454257f5f8SChen-Yu Tsai 464257f5f8SChen-Yu Tsai static void __secure cp15_write_cntp_ctl(u32 val) 474257f5f8SChen-Yu Tsai { 484257f5f8SChen-Yu Tsai asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val)); 494257f5f8SChen-Yu Tsai } 504257f5f8SChen-Yu Tsai 514257f5f8SChen-Yu Tsai static u32 __secure cp15_read_cntp_ctl(void) 524257f5f8SChen-Yu Tsai { 534257f5f8SChen-Yu Tsai u32 val; 544257f5f8SChen-Yu Tsai 554257f5f8SChen-Yu Tsai asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); 564257f5f8SChen-Yu Tsai 574257f5f8SChen-Yu Tsai return val; 584257f5f8SChen-Yu Tsai } 594257f5f8SChen-Yu Tsai 60e4916e85SAndre Przywara #define ONE_MS (COUNTER_FREQUENCY / 1000) 614257f5f8SChen-Yu Tsai 624257f5f8SChen-Yu Tsai static void __secure __mdelay(u32 ms) 634257f5f8SChen-Yu Tsai { 644257f5f8SChen-Yu Tsai u32 reg = ONE_MS * ms; 654257f5f8SChen-Yu Tsai 664257f5f8SChen-Yu Tsai cp15_write_cntp_tval(reg); 67a78cd861STom Rini isb(); 684257f5f8SChen-Yu Tsai cp15_write_cntp_ctl(3); 694257f5f8SChen-Yu Tsai 704257f5f8SChen-Yu Tsai do { 71a78cd861STom Rini isb(); 724257f5f8SChen-Yu Tsai reg = cp15_read_cntp_ctl(); 734257f5f8SChen-Yu Tsai } while (!(reg & BIT(2))); 744257f5f8SChen-Yu Tsai 754257f5f8SChen-Yu Tsai cp15_write_cntp_ctl(0); 76a78cd861STom Rini isb(); 774257f5f8SChen-Yu Tsai } 784257f5f8SChen-Yu Tsai 794257f5f8SChen-Yu Tsai static void __secure clamp_release(u32 __maybe_unused *clamp) 804257f5f8SChen-Yu Tsai { 814257f5f8SChen-Yu Tsai #if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \ 820918648dSChen-Yu Tsai defined(CONFIG_MACH_SUN8I_H3) || \ 830918648dSChen-Yu Tsai defined(CONFIG_MACH_SUN8I_R40) 844257f5f8SChen-Yu Tsai u32 tmp = 0x1ff; 854257f5f8SChen-Yu Tsai do { 864257f5f8SChen-Yu Tsai tmp >>= 1; 874257f5f8SChen-Yu Tsai writel(tmp, clamp); 884257f5f8SChen-Yu Tsai } while (tmp); 894257f5f8SChen-Yu Tsai 904257f5f8SChen-Yu Tsai __mdelay(10); 914257f5f8SChen-Yu Tsai #endif 924257f5f8SChen-Yu Tsai } 934257f5f8SChen-Yu Tsai 944257f5f8SChen-Yu Tsai static void __secure clamp_set(u32 __maybe_unused *clamp) 954257f5f8SChen-Yu Tsai { 964257f5f8SChen-Yu Tsai #if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN7I) || \ 970918648dSChen-Yu Tsai defined(CONFIG_MACH_SUN8I_H3) || \ 980918648dSChen-Yu Tsai defined(CONFIG_MACH_SUN8I_R40) 994257f5f8SChen-Yu Tsai writel(0xff, clamp); 1004257f5f8SChen-Yu Tsai #endif 1014257f5f8SChen-Yu Tsai } 1024257f5f8SChen-Yu Tsai 1034257f5f8SChen-Yu Tsai static void __secure sunxi_power_switch(u32 *clamp, u32 *pwroff, bool on, 1044257f5f8SChen-Yu Tsai int cpu) 1054257f5f8SChen-Yu Tsai { 1064257f5f8SChen-Yu Tsai if (on) { 1074257f5f8SChen-Yu Tsai /* Release power clamp */ 1084257f5f8SChen-Yu Tsai clamp_release(clamp); 1094257f5f8SChen-Yu Tsai 1104257f5f8SChen-Yu Tsai /* Clear power gating */ 1114257f5f8SChen-Yu Tsai clrbits_le32(pwroff, BIT(cpu)); 1124257f5f8SChen-Yu Tsai } else { 1134257f5f8SChen-Yu Tsai /* Set power gating */ 1144257f5f8SChen-Yu Tsai setbits_le32(pwroff, BIT(cpu)); 1154257f5f8SChen-Yu Tsai 1164257f5f8SChen-Yu Tsai /* Activate power clamp */ 1174257f5f8SChen-Yu Tsai clamp_set(clamp); 1184257f5f8SChen-Yu Tsai } 1194257f5f8SChen-Yu Tsai } 1204257f5f8SChen-Yu Tsai 121*34748272SChen-Yu Tsai #ifdef CONFIG_MACH_SUN8I_R40 122*34748272SChen-Yu Tsai /* secondary core entry address is programmed differently on R40 */ 123*34748272SChen-Yu Tsai static void __secure sunxi_set_entry_address(void *entry) 124*34748272SChen-Yu Tsai { 125*34748272SChen-Yu Tsai writel((u32)entry, 126*34748272SChen-Yu Tsai SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0); 127*34748272SChen-Yu Tsai } 128*34748272SChen-Yu Tsai #else 129*34748272SChen-Yu Tsai static void __secure sunxi_set_entry_address(void *entry) 130*34748272SChen-Yu Tsai { 131*34748272SChen-Yu Tsai struct sunxi_cpucfg_reg *cpucfg = 132*34748272SChen-Yu Tsai (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; 133*34748272SChen-Yu Tsai 134*34748272SChen-Yu Tsai writel((u32)entry, &cpucfg->priv0); 135*34748272SChen-Yu Tsai } 136*34748272SChen-Yu Tsai #endif 137*34748272SChen-Yu Tsai 1384257f5f8SChen-Yu Tsai #ifdef CONFIG_MACH_SUN7I 1394257f5f8SChen-Yu Tsai /* sun7i (A20) is different from other single cluster SoCs */ 1404257f5f8SChen-Yu Tsai static void __secure sunxi_cpu_set_power(int __always_unused cpu, bool on) 1414257f5f8SChen-Yu Tsai { 1424257f5f8SChen-Yu Tsai struct sunxi_cpucfg_reg *cpucfg = 1434257f5f8SChen-Yu Tsai (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; 1444257f5f8SChen-Yu Tsai 1454257f5f8SChen-Yu Tsai sunxi_power_switch(&cpucfg->cpu1_pwr_clamp, &cpucfg->cpu1_pwroff, 1464257f5f8SChen-Yu Tsai on, 0); 1474257f5f8SChen-Yu Tsai } 1480918648dSChen-Yu Tsai #elif defined CONFIG_MACH_SUN8I_R40 1490918648dSChen-Yu Tsai static void __secure sunxi_cpu_set_power(int cpu, bool on) 1500918648dSChen-Yu Tsai { 1510918648dSChen-Yu Tsai struct sunxi_cpucfg_reg *cpucfg = 1520918648dSChen-Yu Tsai (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; 1530918648dSChen-Yu Tsai 1540918648dSChen-Yu Tsai sunxi_power_switch((void *)cpucfg + SUN8I_R40_PWR_CLAMP(cpu), 1550918648dSChen-Yu Tsai (void *)cpucfg + SUN8I_R40_PWROFF, 1560918648dSChen-Yu Tsai on, 0); 1570918648dSChen-Yu Tsai } 1580918648dSChen-Yu Tsai #else /* ! CONFIG_MACH_SUN7I && ! CONFIG_MACH_SUN8I_R40 */ 1594257f5f8SChen-Yu Tsai static void __secure sunxi_cpu_set_power(int cpu, bool on) 1604257f5f8SChen-Yu Tsai { 1614257f5f8SChen-Yu Tsai struct sunxi_prcm_reg *prcm = 1624257f5f8SChen-Yu Tsai (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; 1634257f5f8SChen-Yu Tsai 1644257f5f8SChen-Yu Tsai sunxi_power_switch(&prcm->cpu_pwr_clamp[cpu], &prcm->cpu_pwroff, 1654257f5f8SChen-Yu Tsai on, cpu); 1664257f5f8SChen-Yu Tsai } 1674257f5f8SChen-Yu Tsai #endif /* CONFIG_MACH_SUN7I */ 1684257f5f8SChen-Yu Tsai 1694257f5f8SChen-Yu Tsai void __secure sunxi_cpu_power_off(u32 cpuid) 1704257f5f8SChen-Yu Tsai { 1714257f5f8SChen-Yu Tsai struct sunxi_cpucfg_reg *cpucfg = 1724257f5f8SChen-Yu Tsai (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; 1734257f5f8SChen-Yu Tsai u32 cpu = cpuid & 0x3; 1744257f5f8SChen-Yu Tsai 1754257f5f8SChen-Yu Tsai /* Wait for the core to enter WFI */ 1764257f5f8SChen-Yu Tsai while (1) { 1774257f5f8SChen-Yu Tsai if (readl(&cpucfg->cpu[cpu].status) & BIT(2)) 1784257f5f8SChen-Yu Tsai break; 1794257f5f8SChen-Yu Tsai __mdelay(1); 1804257f5f8SChen-Yu Tsai } 1814257f5f8SChen-Yu Tsai 1824257f5f8SChen-Yu Tsai /* Assert reset on target CPU */ 1834257f5f8SChen-Yu Tsai writel(0, &cpucfg->cpu[cpu].rst); 1844257f5f8SChen-Yu Tsai 1854257f5f8SChen-Yu Tsai /* Lock CPU (Disable external debug access) */ 1864257f5f8SChen-Yu Tsai clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); 1874257f5f8SChen-Yu Tsai 1884257f5f8SChen-Yu Tsai /* Power down CPU */ 1894257f5f8SChen-Yu Tsai sunxi_cpu_set_power(cpuid, false); 1904257f5f8SChen-Yu Tsai 1914257f5f8SChen-Yu Tsai /* Unlock CPU (Disable external debug access) */ 1924257f5f8SChen-Yu Tsai setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); 1934257f5f8SChen-Yu Tsai } 1944257f5f8SChen-Yu Tsai 1954257f5f8SChen-Yu Tsai static u32 __secure cp15_read_scr(void) 1964257f5f8SChen-Yu Tsai { 1974257f5f8SChen-Yu Tsai u32 scr; 1984257f5f8SChen-Yu Tsai 1994257f5f8SChen-Yu Tsai asm volatile ("mrc p15, 0, %0, c1, c1, 0" : "=r" (scr)); 2004257f5f8SChen-Yu Tsai 2014257f5f8SChen-Yu Tsai return scr; 2024257f5f8SChen-Yu Tsai } 2034257f5f8SChen-Yu Tsai 2044257f5f8SChen-Yu Tsai static void __secure cp15_write_scr(u32 scr) 2054257f5f8SChen-Yu Tsai { 2064257f5f8SChen-Yu Tsai asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr)); 207a78cd861STom Rini isb(); 2084257f5f8SChen-Yu Tsai } 2094257f5f8SChen-Yu Tsai 2104257f5f8SChen-Yu Tsai /* 2114257f5f8SChen-Yu Tsai * Although this is an FIQ handler, the FIQ is processed in monitor mode, 2124257f5f8SChen-Yu Tsai * which means there's no FIQ banked registers. This is the same as IRQ 2134257f5f8SChen-Yu Tsai * mode, so use the IRQ attribute to ask the compiler to handler entry 2144257f5f8SChen-Yu Tsai * and return. 2154257f5f8SChen-Yu Tsai */ 2164257f5f8SChen-Yu Tsai void __secure __irq psci_fiq_enter(void) 2174257f5f8SChen-Yu Tsai { 2184257f5f8SChen-Yu Tsai u32 scr, reg, cpu; 2194257f5f8SChen-Yu Tsai 2204257f5f8SChen-Yu Tsai /* Switch to secure mode */ 2214257f5f8SChen-Yu Tsai scr = cp15_read_scr(); 2224257f5f8SChen-Yu Tsai cp15_write_scr(scr & ~BIT(0)); 2234257f5f8SChen-Yu Tsai 2244257f5f8SChen-Yu Tsai /* Validate reason based on IAR and acknowledge */ 2254257f5f8SChen-Yu Tsai reg = readl(GICC_BASE + GICC_IAR); 2264257f5f8SChen-Yu Tsai 2274257f5f8SChen-Yu Tsai /* Skip spurious interrupts 1022 and 1023 */ 2284257f5f8SChen-Yu Tsai if (reg == 1023 || reg == 1022) 2294257f5f8SChen-Yu Tsai goto out; 2304257f5f8SChen-Yu Tsai 2314257f5f8SChen-Yu Tsai /* End of interrupt */ 2324257f5f8SChen-Yu Tsai writel(reg, GICC_BASE + GICC_EOIR); 233a78cd861STom Rini dsb(); 2344257f5f8SChen-Yu Tsai 2354257f5f8SChen-Yu Tsai /* Get CPU number */ 2364257f5f8SChen-Yu Tsai cpu = (reg >> 10) & 0x7; 2374257f5f8SChen-Yu Tsai 2384257f5f8SChen-Yu Tsai /* Power off the CPU */ 2394257f5f8SChen-Yu Tsai sunxi_cpu_power_off(cpu); 2404257f5f8SChen-Yu Tsai 2414257f5f8SChen-Yu Tsai out: 2424257f5f8SChen-Yu Tsai /* Restore security level */ 2434257f5f8SChen-Yu Tsai cp15_write_scr(scr); 2444257f5f8SChen-Yu Tsai } 2454257f5f8SChen-Yu Tsai 2464257f5f8SChen-Yu Tsai int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc) 2474257f5f8SChen-Yu Tsai { 2484257f5f8SChen-Yu Tsai struct sunxi_cpucfg_reg *cpucfg = 2494257f5f8SChen-Yu Tsai (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; 2504257f5f8SChen-Yu Tsai u32 cpu = (mpidr & 0x3); 2514257f5f8SChen-Yu Tsai 2526e6622deSChen-Yu Tsai /* store target PC */ 2536e6622deSChen-Yu Tsai psci_save_target_pc(cpu, pc); 2544257f5f8SChen-Yu Tsai 2554257f5f8SChen-Yu Tsai /* Set secondary core power on PC */ 256*34748272SChen-Yu Tsai sunxi_set_entry_address(&psci_cpu_entry); 2574257f5f8SChen-Yu Tsai 2584257f5f8SChen-Yu Tsai /* Assert reset on target CPU */ 2594257f5f8SChen-Yu Tsai writel(0, &cpucfg->cpu[cpu].rst); 2604257f5f8SChen-Yu Tsai 2614257f5f8SChen-Yu Tsai /* Invalidate L1 cache */ 2624257f5f8SChen-Yu Tsai clrbits_le32(&cpucfg->gen_ctrl, BIT(cpu)); 2634257f5f8SChen-Yu Tsai 2644257f5f8SChen-Yu Tsai /* Lock CPU (Disable external debug access) */ 2654257f5f8SChen-Yu Tsai clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); 2664257f5f8SChen-Yu Tsai 2674257f5f8SChen-Yu Tsai /* Power up target CPU */ 2684257f5f8SChen-Yu Tsai sunxi_cpu_set_power(cpu, true); 2694257f5f8SChen-Yu Tsai 2704257f5f8SChen-Yu Tsai /* De-assert reset on target CPU */ 2714257f5f8SChen-Yu Tsai writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst); 2724257f5f8SChen-Yu Tsai 2734257f5f8SChen-Yu Tsai /* Unlock CPU (Disable external debug access) */ 2744257f5f8SChen-Yu Tsai setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); 2754257f5f8SChen-Yu Tsai 2764257f5f8SChen-Yu Tsai return ARM_PSCI_RET_SUCCESS; 2774257f5f8SChen-Yu Tsai } 2784257f5f8SChen-Yu Tsai 2794257f5f8SChen-Yu Tsai void __secure psci_cpu_off(void) 2804257f5f8SChen-Yu Tsai { 2814257f5f8SChen-Yu Tsai psci_cpu_off_common(); 2824257f5f8SChen-Yu Tsai 2834257f5f8SChen-Yu Tsai /* Ask CPU0 via SGI15 to pull the rug... */ 2844257f5f8SChen-Yu Tsai writel(BIT(16) | 15, GICD_BASE + GICD_SGIR); 285a78cd861STom Rini dsb(); 2864257f5f8SChen-Yu Tsai 2874257f5f8SChen-Yu Tsai /* Wait to be turned off */ 2884257f5f8SChen-Yu Tsai while (1) 2894257f5f8SChen-Yu Tsai wfi(); 2904257f5f8SChen-Yu Tsai } 2914257f5f8SChen-Yu Tsai 29294a389b2SChen-Yu Tsai void __secure psci_arch_init(void) 2934257f5f8SChen-Yu Tsai { 2944257f5f8SChen-Yu Tsai u32 reg; 2954257f5f8SChen-Yu Tsai 2964257f5f8SChen-Yu Tsai /* SGI15 as Group-0 */ 2974257f5f8SChen-Yu Tsai clrbits_le32(GICD_BASE + GICD_IGROUPRn, BIT(15)); 2984257f5f8SChen-Yu Tsai 2994257f5f8SChen-Yu Tsai /* Set SGI15 priority to 0 */ 3004257f5f8SChen-Yu Tsai writeb(0, GICD_BASE + GICD_IPRIORITYRn + 15); 3014257f5f8SChen-Yu Tsai 3024257f5f8SChen-Yu Tsai /* Be cool with non-secure */ 3034257f5f8SChen-Yu Tsai writel(0xff, GICC_BASE + GICC_PMR); 3044257f5f8SChen-Yu Tsai 3054257f5f8SChen-Yu Tsai /* Switch FIQEn on */ 3064257f5f8SChen-Yu Tsai setbits_le32(GICC_BASE + GICC_CTLR, BIT(3)); 3074257f5f8SChen-Yu Tsai 3084257f5f8SChen-Yu Tsai reg = cp15_read_scr(); 3094257f5f8SChen-Yu Tsai reg |= BIT(2); /* Enable FIQ in monitor mode */ 3104257f5f8SChen-Yu Tsai reg &= ~BIT(0); /* Secure mode */ 3114257f5f8SChen-Yu Tsai cp15_write_scr(reg); 3124257f5f8SChen-Yu Tsai } 313