linux/arch/arm/mach-exynos/firmware.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Copyright (C) 2012 Samsung Electronics.
   4// Kyungmin Park <kyungmin.park@samsung.com>
   5// Tomasz Figa <t.figa@samsung.com>
   6
   7#include <linux/kernel.h>
   8#include <linux/io.h>
   9#include <linux/init.h>
  10#include <linux/of.h>
  11#include <linux/of_address.h>
  12
  13#include <asm/cacheflush.h>
  14#include <asm/cputype.h>
  15#include <asm/firmware.h>
  16#include <asm/hardware/cache-l2x0.h>
  17#include <asm/suspend.h>
  18
  19#include "common.h"
  20#include "smc.h"
  21
  22#define EXYNOS_BOOT_ADDR        0x8
  23#define EXYNOS_BOOT_FLAG        0xc
  24
  25static void exynos_save_cp15(void)
  26{
  27        /* Save Power control and Diagnostic registers */
  28        asm ("mrc p15, 0, %0, c15, c0, 0\n"
  29             "mrc p15, 0, %1, c15, c0, 1\n"
  30             : "=r" (cp15_save_power), "=r" (cp15_save_diag)
  31             : : "cc");
  32}
  33
  34static int exynos_do_idle(unsigned long mode)
  35{
  36        switch (mode) {
  37        case FW_DO_IDLE_AFTR:
  38                if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
  39                        exynos_save_cp15();
  40                writel_relaxed(__pa_symbol(exynos_cpu_resume_ns),
  41                               sysram_ns_base_addr + 0x24);
  42                writel_relaxed(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
  43                if (soc_is_exynos3250()) {
  44                        flush_cache_all();
  45                        exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
  46                                   SMC_POWERSTATE_IDLE, 0);
  47                        exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER,
  48                                   SMC_POWERSTATE_IDLE, 0);
  49                } else
  50                        exynos_smc(SMC_CMD_CPU0AFTR, 0, 0, 0);
  51                break;
  52        case FW_DO_IDLE_SLEEP:
  53                exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
  54        }
  55        return 0;
  56}
  57
  58static int exynos_cpu_boot(int cpu)
  59{
  60        /*
  61         * Exynos3250 doesn't need to send smc command for secondary CPU boot
  62         * because Exynos3250 removes WFE in secure mode.
  63         */
  64        if (soc_is_exynos3250())
  65                return 0;
  66
  67        /*
  68         * The second parameter of SMC_CMD_CPU1BOOT command means CPU id.
  69         */
  70        exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0);
  71        return 0;
  72}
  73
  74static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
  75{
  76        void __iomem *boot_reg;
  77
  78        if (!sysram_ns_base_addr)
  79                return -ENODEV;
  80
  81        boot_reg = sysram_ns_base_addr + 0x1c;
  82
  83        /*
  84         * Almost all Exynos-series of SoCs that run in secure mode don't need
  85         * additional offset for every CPU, with Exynos4412 being the only
  86         * exception.
  87         */
  88        if (soc_is_exynos4412())
  89                boot_reg += 4 * cpu;
  90
  91        writel_relaxed(boot_addr, boot_reg);
  92        return 0;
  93}
  94
  95static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
  96{
  97        void __iomem *boot_reg;
  98
  99        if (!sysram_ns_base_addr)
 100                return -ENODEV;
 101
 102        boot_reg = sysram_ns_base_addr + 0x1c;
 103
 104        if (soc_is_exynos4412())
 105                boot_reg += 4 * cpu;
 106
 107        *boot_addr = readl_relaxed(boot_reg);
 108        return 0;
 109}
 110
 111static int exynos_cpu_suspend(unsigned long arg)
 112{
 113        flush_cache_all();
 114        outer_flush_all();
 115
 116        exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
 117
 118        pr_info("Failed to suspend the system\n");
 119        writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
 120        return 1;
 121}
 122
 123static int exynos_suspend(void)
 124{
 125        if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
 126                exynos_save_cp15();
 127
 128        writel(EXYNOS_SLEEP_MAGIC, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
 129        writel(__pa_symbol(exynos_cpu_resume_ns),
 130                sysram_ns_base_addr + EXYNOS_BOOT_ADDR);
 131
 132        return cpu_suspend(0, exynos_cpu_suspend);
 133}
 134
 135static int exynos_resume(void)
 136{
 137        writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
 138
 139        return 0;
 140}
 141
 142static const struct firmware_ops exynos_firmware_ops = {
 143        .do_idle                = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_do_idle : NULL,
 144        .set_cpu_boot_addr      = exynos_set_cpu_boot_addr,
 145        .get_cpu_boot_addr      = exynos_get_cpu_boot_addr,
 146        .cpu_boot               = exynos_cpu_boot,
 147        .suspend                = IS_ENABLED(CONFIG_PM_SLEEP) ? exynos_suspend : NULL,
 148        .resume                 = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_resume : NULL,
 149};
 150
 151static void exynos_l2_write_sec(unsigned long val, unsigned reg)
 152{
 153        static int l2cache_enabled;
 154
 155        switch (reg) {
 156        case L2X0_CTRL:
 157                if (val & L2X0_CTRL_EN) {
 158                        /*
 159                         * Before the cache can be enabled, due to firmware
 160                         * design, SMC_CMD_L2X0INVALL must be called.
 161                         */
 162                        if (!l2cache_enabled) {
 163                                exynos_smc(SMC_CMD_L2X0INVALL, 0, 0, 0);
 164                                l2cache_enabled = 1;
 165                        }
 166                } else {
 167                        l2cache_enabled = 0;
 168                }
 169                exynos_smc(SMC_CMD_L2X0CTRL, val, 0, 0);
 170                break;
 171
 172        case L2X0_DEBUG_CTRL:
 173                exynos_smc(SMC_CMD_L2X0DEBUG, val, 0, 0);
 174                break;
 175
 176        default:
 177                WARN_ONCE(1, "%s: ignoring write to reg 0x%x\n", __func__, reg);
 178        }
 179}
 180
 181static void exynos_l2_configure(const struct l2x0_regs *regs)
 182{
 183        exynos_smc(SMC_CMD_L2X0SETUP1, regs->tag_latency, regs->data_latency,
 184                   regs->prefetch_ctrl);
 185        exynos_smc(SMC_CMD_L2X0SETUP2, regs->pwr_ctrl, regs->aux_ctrl, 0);
 186}
 187
 188void __init exynos_firmware_init(void)
 189{
 190        struct device_node *nd;
 191        const __be32 *addr;
 192
 193        nd = of_find_compatible_node(NULL, NULL,
 194                                        "samsung,secure-firmware");
 195        if (!nd)
 196                return;
 197
 198        addr = of_get_address(nd, 0, NULL, NULL);
 199        if (!addr) {
 200                pr_err("%s: No address specified.\n", __func__);
 201                return;
 202        }
 203
 204        pr_info("Running under secure firmware.\n");
 205
 206        register_firmware_ops(&exynos_firmware_ops);
 207
 208        /*
 209         * Exynos 4 SoCs (based on Cortex A9 and equipped with L2C-310),
 210         * running under secure firmware, require certain registers of L2
 211         * cache controller to be written in secure mode. Here .write_sec
 212         * callback is provided to perform necessary SMC calls.
 213         */
 214        if (IS_ENABLED(CONFIG_CACHE_L2X0) &&
 215            read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
 216                outer_cache.write_sec = exynos_l2_write_sec;
 217                outer_cache.configure = exynos_l2_configure;
 218        }
 219}
 220
 221#define REG_CPU_STATE_ADDR      (sysram_ns_base_addr + 0x28)
 222#define BOOT_MODE_MASK          0x1f
 223
 224void exynos_set_boot_flag(unsigned int cpu, unsigned int mode)
 225{
 226        unsigned int tmp;
 227
 228        tmp = readl_relaxed(REG_CPU_STATE_ADDR + cpu * 4);
 229
 230        if (mode & BOOT_MODE_MASK)
 231                tmp &= ~BOOT_MODE_MASK;
 232
 233        tmp |= mode;
 234        writel_relaxed(tmp, REG_CPU_STATE_ADDR + cpu * 4);
 235}
 236
 237void exynos_clear_boot_flag(unsigned int cpu, unsigned int mode)
 238{
 239        unsigned int tmp;
 240
 241        tmp = readl_relaxed(REG_CPU_STATE_ADDR + cpu * 4);
 242        tmp &= ~mode;
 243        writel_relaxed(tmp, REG_CPU_STATE_ADDR + cpu * 4);
 244}
 245