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