uboot/arch/arm/cpu/armv7/sunxi/psci.c
<<
>>
Prefs
   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>
  20#include <asm/secure.h>
  21#include <asm/system.h>
  22
  23#include <linux/bitops.h>
  24
  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
  30static void __secure cp15_write_cntp_tval(u32 tval)
  31{
  32        asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
  33}
  34
  35static void __secure cp15_write_cntp_ctl(u32 val)
  36{
  37        asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
  38}
  39
  40static 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
  49#define ONE_MS (CONFIG_TIMER_CLK_FREQ / 1000)
  50
  51static void __secure __mdelay(u32 ms)
  52{
  53        u32 reg = ONE_MS * ms;
  54
  55        cp15_write_cntp_tval(reg);
  56        isb();
  57        cp15_write_cntp_ctl(3);
  58
  59        do {
  60                isb();
  61                reg = cp15_read_cntp_ctl();
  62        } while (!(reg & BIT(2)));
  63
  64        cp15_write_cntp_ctl(0);
  65        isb();
  66}
  67
  68static 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
  82static 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
  90static 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 */
 110static 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 */
 119static 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
 129void __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
 155static 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
 164static void __secure cp15_write_scr(u32 scr)
 165{
 166        asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr));
 167        isb();
 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 */
 176void __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);
 193        dsb();
 194
 195        /* Get CPU number */
 196        cpu = (reg >> 10) & 0x7;
 197
 198        /* Power off the CPU */
 199        sunxi_cpu_power_off(cpu);
 200
 201out:
 202        /* Restore security level */
 203        cp15_write_scr(scr);
 204}
 205
 206int __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
 212        /* store target PC */
 213        psci_save_target_pc(cpu, pc);
 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
 239void __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);
 245        dsb();
 246
 247        /* Wait to be turned off */
 248        while (1)
 249                wfi();
 250}
 251
 252void __secure psci_arch_init(void)
 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}
 273