linux/arch/arm/mach-exynos/mcpm-exynos.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (c) 2014 Samsung Electronics Co., Ltd.
   3//              http://www.samsung.com
   4//
   5// Based on arch/arm/mach-vexpress/dcscb.c
   6
   7#include <linux/arm-cci.h>
   8#include <linux/delay.h>
   9#include <linux/io.h>
  10#include <linux/of_address.h>
  11#include <linux/syscore_ops.h>
  12#include <linux/soc/samsung/exynos-regs-pmu.h>
  13
  14#include <asm/cputype.h>
  15#include <asm/cp15.h>
  16#include <asm/mcpm.h>
  17#include <asm/smp_plat.h>
  18
  19#include "common.h"
  20
  21#define EXYNOS5420_CPUS_PER_CLUSTER     4
  22#define EXYNOS5420_NR_CLUSTERS          2
  23
  24#define EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN   BIT(9)
  25#define EXYNOS5420_USE_ARM_CORE_DOWN_STATE      BIT(29)
  26#define EXYNOS5420_USE_L2_COMMON_UP_STATE       BIT(30)
  27
  28static void __iomem *ns_sram_base_addr __ro_after_init;
  29
  30/*
  31 * The common v7_exit_coherency_flush API could not be used because of the
  32 * Erratum 799270 workaround. This macro is the same as the common one (in
  33 * arch/arm/include/asm/cacheflush.h) except for the erratum handling.
  34 */
  35#define exynos_v7_exit_coherency_flush(level) \
  36        asm volatile( \
  37        "stmfd  sp!, {fp, ip}\n\t"\
  38        "mrc    p15, 0, r0, c1, c0, 0   @ get SCTLR\n\t" \
  39        "bic    r0, r0, #"__stringify(CR_C)"\n\t" \
  40        "mcr    p15, 0, r0, c1, c0, 0   @ set SCTLR\n\t" \
  41        "isb\n\t"\
  42        "bl     v7_flush_dcache_"__stringify(level)"\n\t" \
  43        "mrc    p15, 0, r0, c1, c0, 1   @ get ACTLR\n\t" \
  44        "bic    r0, r0, #(1 << 6)       @ disable local coherency\n\t" \
  45        /* Dummy Load of a device register to avoid Erratum 799270 */ \
  46        "ldr    r4, [%0]\n\t" \
  47        "and    r4, r4, #0\n\t" \
  48        "orr    r0, r0, r4\n\t" \
  49        "mcr    p15, 0, r0, c1, c0, 1   @ set ACTLR\n\t" \
  50        "isb\n\t" \
  51        "dsb\n\t" \
  52        "ldmfd  sp!, {fp, ip}" \
  53        : \
  54        : "Ir" (pmu_base_addr + S5P_INFORM0) \
  55        : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
  56          "r9", "r10", "lr", "memory")
  57
  58static int exynos_cpu_powerup(unsigned int cpu, unsigned int cluster)
  59{
  60        unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
  61
  62        pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
  63        if (cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
  64                cluster >= EXYNOS5420_NR_CLUSTERS)
  65                return -EINVAL;
  66
  67        if (!exynos_cpu_power_state(cpunr)) {
  68                exynos_cpu_power_up(cpunr);
  69
  70                /*
  71                 * This assumes the cluster number of the big cores(Cortex A15)
  72                 * is 0 and the Little cores(Cortex A7) is 1.
  73                 * When the system was booted from the Little core,
  74                 * they should be reset during power up cpu.
  75                 */
  76                if (cluster &&
  77                    cluster == MPIDR_AFFINITY_LEVEL(cpu_logical_map(0), 1)) {
  78                        /*
  79                         * Before we reset the Little cores, we should wait
  80                         * the SPARE2 register is set to 1 because the init
  81                         * codes of the iROM will set the register after
  82                         * initialization.
  83                         */
  84                        while (!pmu_raw_readl(S5P_PMU_SPARE2))
  85                                udelay(10);
  86
  87                        pmu_raw_writel(EXYNOS5420_KFC_CORE_RESET(cpu),
  88                                        EXYNOS_SWRESET);
  89                }
  90        }
  91
  92        return 0;
  93}
  94
  95static int exynos_cluster_powerup(unsigned int cluster)
  96{
  97        pr_debug("%s: cluster %u\n", __func__, cluster);
  98        if (cluster >= EXYNOS5420_NR_CLUSTERS)
  99                return -EINVAL;
 100
 101        exynos_cluster_power_up(cluster);
 102        return 0;
 103}
 104
 105static void exynos_cpu_powerdown_prepare(unsigned int cpu, unsigned int cluster)
 106{
 107        unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
 108
 109        pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
 110        BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
 111                        cluster >= EXYNOS5420_NR_CLUSTERS);
 112        exynos_cpu_power_down(cpunr);
 113}
 114
 115static void exynos_cluster_powerdown_prepare(unsigned int cluster)
 116{
 117        pr_debug("%s: cluster %u\n", __func__, cluster);
 118        BUG_ON(cluster >= EXYNOS5420_NR_CLUSTERS);
 119        exynos_cluster_power_down(cluster);
 120}
 121
 122static void exynos_cpu_cache_disable(void)
 123{
 124        /* Disable and flush the local CPU cache. */
 125        exynos_v7_exit_coherency_flush(louis);
 126}
 127
 128static void exynos_cluster_cache_disable(void)
 129{
 130        if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A15) {
 131                /*
 132                 * On the Cortex-A15 we need to disable
 133                 * L2 prefetching before flushing the cache.
 134                 */
 135                asm volatile(
 136                "mcr    p15, 1, %0, c15, c0, 3\n\t"
 137                "isb\n\t"
 138                "dsb"
 139                : : "r" (0x400));
 140        }
 141
 142        /* Flush all cache levels for this cluster. */
 143        exynos_v7_exit_coherency_flush(all);
 144
 145        /*
 146         * Disable cluster-level coherency by masking
 147         * incoming snoops and DVM messages:
 148         */
 149        cci_disable_port_by_cpu(read_cpuid_mpidr());
 150}
 151
 152static int exynos_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
 153{
 154        unsigned int tries = 100;
 155        unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
 156
 157        pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
 158        BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
 159                        cluster >= EXYNOS5420_NR_CLUSTERS);
 160
 161        /* Wait for the core state to be OFF */
 162        while (tries--) {
 163                if ((exynos_cpu_power_state(cpunr) == 0))
 164                        return 0; /* success: the CPU is halted */
 165
 166                /* Otherwise, wait and retry: */
 167                msleep(1);
 168        }
 169
 170        return -ETIMEDOUT; /* timeout */
 171}
 172
 173static void exynos_cpu_is_up(unsigned int cpu, unsigned int cluster)
 174{
 175        /* especially when resuming: make sure power control is set */
 176        exynos_cpu_powerup(cpu, cluster);
 177}
 178
 179static const struct mcpm_platform_ops exynos_power_ops = {
 180        .cpu_powerup            = exynos_cpu_powerup,
 181        .cluster_powerup        = exynos_cluster_powerup,
 182        .cpu_powerdown_prepare  = exynos_cpu_powerdown_prepare,
 183        .cluster_powerdown_prepare = exynos_cluster_powerdown_prepare,
 184        .cpu_cache_disable      = exynos_cpu_cache_disable,
 185        .cluster_cache_disable  = exynos_cluster_cache_disable,
 186        .wait_for_powerdown     = exynos_wait_for_powerdown,
 187        .cpu_is_up              = exynos_cpu_is_up,
 188};
 189
 190/*
 191 * Enable cluster-level coherency, in preparation for turning on the MMU.
 192 */
 193static void __naked exynos_pm_power_up_setup(unsigned int affinity_level)
 194{
 195        asm volatile ("\n"
 196        "cmp    r0, #1\n"
 197        "bxne   lr\n"
 198        "b      cci_enable_port_for_self");
 199}
 200
 201static const struct of_device_id exynos_dt_mcpm_match[] = {
 202        { .compatible = "samsung,exynos5420" },
 203        { .compatible = "samsung,exynos5800" },
 204        {},
 205};
 206
 207static void exynos_mcpm_setup_entry_point(void)
 208{
 209        /*
 210         * U-Boot SPL is hardcoded to jump to the start of ns_sram_base_addr
 211         * as part of secondary_cpu_start().  Let's redirect it to the
 212         * mcpm_entry_point(). This is done during both secondary boot-up as
 213         * well as system resume.
 214         */
 215        __raw_writel(0xe59f0000, ns_sram_base_addr);     /* ldr r0, [pc, #0] */
 216        __raw_writel(0xe12fff10, ns_sram_base_addr + 4); /* bx  r0 */
 217        __raw_writel(__pa_symbol(mcpm_entry_point), ns_sram_base_addr + 8);
 218}
 219
 220static struct syscore_ops exynos_mcpm_syscore_ops = {
 221        .resume = exynos_mcpm_setup_entry_point,
 222};
 223
 224static int __init exynos_mcpm_init(void)
 225{
 226        struct device_node *node;
 227        unsigned int value, i;
 228        int ret;
 229
 230        node = of_find_matching_node(NULL, exynos_dt_mcpm_match);
 231        if (!node)
 232                return -ENODEV;
 233        of_node_put(node);
 234
 235        if (!cci_probed())
 236                return -ENODEV;
 237
 238        node = of_find_compatible_node(NULL, NULL,
 239                        "samsung,exynos4210-sysram-ns");
 240        if (!node)
 241                return -ENODEV;
 242
 243        ns_sram_base_addr = of_iomap(node, 0);
 244        of_node_put(node);
 245        if (!ns_sram_base_addr) {
 246                pr_err("failed to map non-secure iRAM base address\n");
 247                return -ENOMEM;
 248        }
 249
 250        /*
 251         * To increase the stability of KFC reset we need to program
 252         * the PMU SPARE3 register
 253         */
 254        pmu_raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3);
 255
 256        ret = mcpm_platform_register(&exynos_power_ops);
 257        if (!ret)
 258                ret = mcpm_sync_init(exynos_pm_power_up_setup);
 259        if (!ret)
 260                ret = mcpm_loopback(exynos_cluster_cache_disable); /* turn on the CCI */
 261        if (ret) {
 262                iounmap(ns_sram_base_addr);
 263                return ret;
 264        }
 265
 266        mcpm_smp_set_ops();
 267
 268        pr_info("Exynos MCPM support installed\n");
 269
 270        /*
 271         * On Exynos5420/5800 for the A15 and A7 clusters:
 272         *
 273         * EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN ensures that all the cores
 274         * in a cluster are turned off before turning off the cluster L2.
 275         *
 276         * EXYNOS5420_USE_ARM_CORE_DOWN_STATE ensures that a cores is powered
 277         * off before waking it up.
 278         *
 279         * EXYNOS5420_USE_L2_COMMON_UP_STATE ensures that cluster L2 will be
 280         * turned on before the first man is powered up.
 281         */
 282        for (i = 0; i < EXYNOS5420_NR_CLUSTERS; i++) {
 283                value = pmu_raw_readl(EXYNOS_COMMON_OPTION(i));
 284                value |= EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN |
 285                         EXYNOS5420_USE_ARM_CORE_DOWN_STATE    |
 286                         EXYNOS5420_USE_L2_COMMON_UP_STATE;
 287                pmu_raw_writel(value, EXYNOS_COMMON_OPTION(i));
 288        }
 289
 290        exynos_mcpm_setup_entry_point();
 291
 292        register_syscore_ops(&exynos_mcpm_syscore_ops);
 293
 294        return ret;
 295}
 296
 297early_initcall(exynos_mcpm_init);
 298