Commit ccf5511797cd4b48d20a85fa1778f5608eac9fd7

Authored by Abhilash Kesavan
Committed by Kukjin Kim
1 parent 5b56642bd8

ARM: EXYNOS: Add MCPM call-back functions

Add machine-dependent MCPM call-backs for Exynos5420. These are used
to power up/down the secondary CPUs during boot, shutdown, s2r and
switching.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Inderpal Singh <inderpal.s@samsung.com>
Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
Reviewed-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>

Showing 4 changed files with 364 additions and 0 deletions Side-by-side Diff

arch/arm/mach-exynos/Kconfig
... ... @@ -110,5 +110,13 @@
110 110  
111 111 endmenu
112 112  
  113 +config EXYNOS5420_MCPM
  114 + bool "Exynos5420 Multi-Cluster PM support"
  115 + depends on MCPM && SOC_EXYNOS5420
  116 + select ARM_CCI
  117 + help
  118 + This is needed to provide CPU and cluster power management
  119 + on Exynos5420 implementing big.LITTLE.
  120 +
113 121 endif
arch/arm/mach-exynos/Makefile
... ... @@ -29,4 +29,6 @@
29 29  
30 30 plus_sec := $(call as-instr,.arch_extension sec,+sec)
31 31 AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec)
  32 +
  33 +obj-$(CONFIG_EXYNOS5420_MCPM) += mcpm-exynos.o
arch/arm/mach-exynos/mcpm-exynos.c
  1 +/*
  2 + * Copyright (c) 2014 Samsung Electronics Co., Ltd.
  3 + * http://www.samsung.com
  4 + *
  5 + * arch/arm/mach-exynos/mcpm-exynos.c
  6 + *
  7 + * Based on arch/arm/mach-vexpress/dcscb.c
  8 + *
  9 + * This program is free software; you can redistribute it and/or modify
  10 + * it under the terms of the GNU General Public License version 2 as
  11 + * published by the Free Software Foundation.
  12 + */
  13 +
  14 +#include <linux/arm-cci.h>
  15 +#include <linux/delay.h>
  16 +#include <linux/io.h>
  17 +#include <linux/of_address.h>
  18 +
  19 +#include <asm/cputype.h>
  20 +#include <asm/cp15.h>
  21 +#include <asm/mcpm.h>
  22 +
  23 +#include "regs-pmu.h"
  24 +#include "common.h"
  25 +
  26 +#define EXYNOS5420_CPUS_PER_CLUSTER 4
  27 +#define EXYNOS5420_NR_CLUSTERS 2
  28 +#define MCPM_BOOT_ADDR_OFFSET 0x1c
  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 + "clrex\n\t"\
  44 + "mrc p15, 0, r0, c1, c0, 1 @ get ACTLR\n\t" \
  45 + "bic r0, r0, #(1 << 6) @ disable local coherency\n\t" \
  46 + /* Dummy Load of a device register to avoid Erratum 799270 */ \
  47 + "ldr r4, [%0]\n\t" \
  48 + "and r4, r4, #0\n\t" \
  49 + "orr r0, r0, r4\n\t" \
  50 + "mcr p15, 0, r0, c1, c0, 1 @ set ACTLR\n\t" \
  51 + "isb\n\t" \
  52 + "dsb\n\t" \
  53 + "ldmfd sp!, {fp, ip}" \
  54 + : \
  55 + : "Ir" (S5P_INFORM0) \
  56 + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
  57 + "r9", "r10", "lr", "memory")
  58 +
  59 +/*
  60 + * We can't use regular spinlocks. In the switcher case, it is possible
  61 + * for an outbound CPU to call power_down() after its inbound counterpart
  62 + * is already live using the same logical CPU number which trips lockdep
  63 + * debugging.
  64 + */
  65 +static arch_spinlock_t exynos_mcpm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
  66 +static int
  67 +cpu_use_count[EXYNOS5420_CPUS_PER_CLUSTER][EXYNOS5420_NR_CLUSTERS];
  68 +
  69 +#define exynos_cluster_usecnt(cluster) \
  70 + (cpu_use_count[0][cluster] + \
  71 + cpu_use_count[1][cluster] + \
  72 + cpu_use_count[2][cluster] + \
  73 + cpu_use_count[3][cluster])
  74 +
  75 +#define exynos_cluster_unused(cluster) !exynos_cluster_usecnt(cluster)
  76 +
  77 +static int exynos_cluster_power_control(unsigned int cluster, int enable)
  78 +{
  79 + unsigned int tries = 100;
  80 + unsigned int val;
  81 +
  82 + if (enable) {
  83 + exynos_cluster_power_up(cluster);
  84 + val = S5P_CORE_LOCAL_PWR_EN;
  85 + } else {
  86 + exynos_cluster_power_down(cluster);
  87 + val = 0;
  88 + }
  89 +
  90 + /* Wait until cluster power control is applied */
  91 + while (tries--) {
  92 + if (exynos_cluster_power_state(cluster) == val)
  93 + return 0;
  94 +
  95 + cpu_relax();
  96 + }
  97 + pr_debug("timed out waiting for cluster %u to power %s\n", cluster,
  98 + enable ? "on" : "off");
  99 +
  100 + return -ETIMEDOUT;
  101 +}
  102 +
  103 +static int exynos_power_up(unsigned int cpu, unsigned int cluster)
  104 +{
  105 + unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
  106 + int err = 0;
  107 +
  108 + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
  109 + if (cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
  110 + cluster >= EXYNOS5420_NR_CLUSTERS)
  111 + return -EINVAL;
  112 +
  113 + /*
  114 + * Since this is called with IRQs enabled, and no arch_spin_lock_irq
  115 + * variant exists, we need to disable IRQs manually here.
  116 + */
  117 + local_irq_disable();
  118 + arch_spin_lock(&exynos_mcpm_lock);
  119 +
  120 + cpu_use_count[cpu][cluster]++;
  121 + if (cpu_use_count[cpu][cluster] == 1) {
  122 + bool was_cluster_down =
  123 + (exynos_cluster_usecnt(cluster) == 1);
  124 +
  125 + /*
  126 + * Turn on the cluster (L2/COMMON) and then power on the
  127 + * cores.
  128 + */
  129 + if (was_cluster_down)
  130 + err = exynos_cluster_power_control(cluster, 1);
  131 +
  132 + if (!err)
  133 + exynos_cpu_power_up(cpunr);
  134 + else
  135 + exynos_cluster_power_control(cluster, 0);
  136 + } else if (cpu_use_count[cpu][cluster] != 2) {
  137 + /*
  138 + * The only possible values are:
  139 + * 0 = CPU down
  140 + * 1 = CPU (still) up
  141 + * 2 = CPU requested to be up before it had a chance
  142 + * to actually make itself down.
  143 + * Any other value is a bug.
  144 + */
  145 + BUG();
  146 + }
  147 +
  148 + arch_spin_unlock(&exynos_mcpm_lock);
  149 + local_irq_enable();
  150 +
  151 + return err;
  152 +}
  153 +
  154 +/*
  155 + * NOTE: This function requires the stack data to be visible through power down
  156 + * and can only be executed on processors like A15 and A7 that hit the cache
  157 + * with the C bit clear in the SCTLR register.
  158 + */
  159 +static void exynos_power_down(void)
  160 +{
  161 + unsigned int mpidr, cpu, cluster;
  162 + bool last_man = false, skip_wfi = false;
  163 + unsigned int cpunr;
  164 +
  165 + mpidr = read_cpuid_mpidr();
  166 + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
  167 + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
  168 + cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
  169 +
  170 + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
  171 + BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
  172 + cluster >= EXYNOS5420_NR_CLUSTERS);
  173 +
  174 + __mcpm_cpu_going_down(cpu, cluster);
  175 +
  176 + arch_spin_lock(&exynos_mcpm_lock);
  177 + BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
  178 + cpu_use_count[cpu][cluster]--;
  179 + if (cpu_use_count[cpu][cluster] == 0) {
  180 + exynos_cpu_power_down(cpunr);
  181 +
  182 + if (exynos_cluster_unused(cluster))
  183 + /* TODO: Turn off the cluster here to save power. */
  184 + last_man = true;
  185 + } else if (cpu_use_count[cpu][cluster] == 1) {
  186 + /*
  187 + * A power_up request went ahead of us.
  188 + * Even if we do not want to shut this CPU down,
  189 + * the caller expects a certain state as if the WFI
  190 + * was aborted. So let's continue with cache cleaning.
  191 + */
  192 + skip_wfi = true;
  193 + } else {
  194 + BUG();
  195 + }
  196 +
  197 + if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
  198 + arch_spin_unlock(&exynos_mcpm_lock);
  199 +
  200 + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) {
  201 + /*
  202 + * On the Cortex-A15 we need to disable
  203 + * L2 prefetching before flushing the cache.
  204 + */
  205 + asm volatile(
  206 + "mcr p15, 1, %0, c15, c0, 3\n\t"
  207 + "isb\n\t"
  208 + "dsb"
  209 + : : "r" (0x400));
  210 + }
  211 +
  212 + /* Flush all cache levels for this cluster. */
  213 + exynos_v7_exit_coherency_flush(all);
  214 +
  215 + /*
  216 + * Disable cluster-level coherency by masking
  217 + * incoming snoops and DVM messages:
  218 + */
  219 + cci_disable_port_by_cpu(mpidr);
  220 +
  221 + __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
  222 + } else {
  223 + arch_spin_unlock(&exynos_mcpm_lock);
  224 +
  225 + /* Disable and flush the local CPU cache. */
  226 + exynos_v7_exit_coherency_flush(louis);
  227 + }
  228 +
  229 + __mcpm_cpu_down(cpu, cluster);
  230 +
  231 + /* Now we are prepared for power-down, do it: */
  232 + if (!skip_wfi)
  233 + wfi();
  234 +
  235 + /* Not dead at this point? Let our caller cope. */
  236 +}
  237 +
  238 +static int exynos_power_down_finish(unsigned int cpu, unsigned int cluster)
  239 +{
  240 + unsigned int tries = 100;
  241 + unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
  242 +
  243 + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
  244 + BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
  245 + cluster >= EXYNOS5420_NR_CLUSTERS);
  246 +
  247 + /* Wait for the core state to be OFF */
  248 + while (tries--) {
  249 + if (ACCESS_ONCE(cpu_use_count[cpu][cluster]) == 0) {
  250 + if ((exynos_cpu_power_state(cpunr) == 0))
  251 + return 0; /* success: the CPU is halted */
  252 + }
  253 +
  254 + /* Otherwise, wait and retry: */
  255 + msleep(1);
  256 + }
  257 +
  258 + return -ETIMEDOUT; /* timeout */
  259 +}
  260 +
  261 +static const struct mcpm_platform_ops exynos_power_ops = {
  262 + .power_up = exynos_power_up,
  263 + .power_down = exynos_power_down,
  264 + .power_down_finish = exynos_power_down_finish,
  265 +};
  266 +
  267 +static void __init exynos_mcpm_usage_count_init(void)
  268 +{
  269 + unsigned int mpidr, cpu, cluster;
  270 +
  271 + mpidr = read_cpuid_mpidr();
  272 + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
  273 + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
  274 +
  275 + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
  276 + BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
  277 + cluster >= EXYNOS5420_NR_CLUSTERS);
  278 +
  279 + cpu_use_count[cpu][cluster] = 1;
  280 +}
  281 +
  282 +/*
  283 + * Enable cluster-level coherency, in preparation for turning on the MMU.
  284 + */
  285 +static void __naked exynos_pm_power_up_setup(unsigned int affinity_level)
  286 +{
  287 + asm volatile ("\n"
  288 + "cmp r0, #1\n"
  289 + "bxne lr\n"
  290 + "b cci_enable_port_for_self");
  291 +}
  292 +
  293 +static int __init exynos_mcpm_init(void)
  294 +{
  295 + struct device_node *node;
  296 + void __iomem *ns_sram_base_addr;
  297 + int ret;
  298 +
  299 + node = of_find_compatible_node(NULL, NULL, "samsung,exynos5420");
  300 + if (!node)
  301 + return -ENODEV;
  302 + of_node_put(node);
  303 +
  304 + if (!cci_probed())
  305 + return -ENODEV;
  306 +
  307 + node = of_find_compatible_node(NULL, NULL,
  308 + "samsung,exynos4210-sysram-ns");
  309 + if (!node)
  310 + return -ENODEV;
  311 +
  312 + ns_sram_base_addr = of_iomap(node, 0);
  313 + of_node_put(node);
  314 + if (!ns_sram_base_addr) {
  315 + pr_err("failed to map non-secure iRAM base address\n");
  316 + return -ENOMEM;
  317 + }
  318 +
  319 + /*
  320 + * To increase the stability of KFC reset we need to program
  321 + * the PMU SPARE3 register
  322 + */
  323 + __raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3);
  324 +
  325 + exynos_mcpm_usage_count_init();
  326 +
  327 + ret = mcpm_platform_register(&exynos_power_ops);
  328 + if (!ret)
  329 + ret = mcpm_sync_init(exynos_pm_power_up_setup);
  330 + if (ret) {
  331 + iounmap(ns_sram_base_addr);
  332 + return ret;
  333 + }
  334 +
  335 + mcpm_smp_set_ops();
  336 +
  337 + pr_info("Exynos MCPM support installed\n");
  338 +
  339 + /*
  340 + * Future entries into the kernel can now go
  341 + * through the cluster entry vectors.
  342 + */
  343 + __raw_writel(virt_to_phys(mcpm_entry_point),
  344 + ns_sram_base_addr + MCPM_BOOT_ADDR_OFFSET);
  345 +
  346 + iounmap(ns_sram_base_addr);
  347 +
  348 + return ret;
  349 +}
  350 +
  351 +early_initcall(exynos_mcpm_init);
arch/arm/mach-exynos/regs-pmu.h
... ... @@ -38,6 +38,7 @@
38 38 #define S5P_INFORM5 S5P_PMUREG(0x0814)
39 39 #define S5P_INFORM6 S5P_PMUREG(0x0818)
40 40 #define S5P_INFORM7 S5P_PMUREG(0x081C)
  41 +#define S5P_PMU_SPARE3 S5P_PMUREG(0x090C)
41 42  
42 43 #define S5P_ARM_CORE0_LOWPWR S5P_PMUREG(0x1000)
43 44 #define S5P_DIS_IRQ_CORE0 S5P_PMUREG(0x1004)
... ... @@ -321,6 +322,8 @@
321 322 #define EXYNOS5_OPTION_USE_STANDBYWFI (1 << 16)
322 323  
323 324 #define EXYNOS5_OPTION_USE_RETENTION (1 << 4)
  325 +
  326 +#define EXYNOS5420_SWRESET_KFC_SEL 0x3
324 327  
325 328 #endif /* __ASM_ARCH_REGS_PMU_H */