linux/arch/arm/mach-exynos/platsmp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
   3//              http://www.samsung.com
   4//
   5// Cloned from linux/arch/arm/mach-vexpress/platsmp.c
   6//
   7//  Copyright (C) 2002 ARM Ltd.
   8//  All Rights Reserved
   9
  10#include <linux/init.h>
  11#include <linux/errno.h>
  12#include <linux/delay.h>
  13#include <linux/jiffies.h>
  14#include <linux/smp.h>
  15#include <linux/io.h>
  16#include <linux/of_address.h>
  17#include <linux/soc/samsung/exynos-regs-pmu.h>
  18
  19#include <asm/cacheflush.h>
  20#include <asm/cp15.h>
  21#include <asm/smp_plat.h>
  22#include <asm/smp_scu.h>
  23#include <asm/firmware.h>
  24
  25#include <mach/map.h>
  26
  27#include "common.h"
  28
  29extern void exynos4_secondary_startup(void);
  30
  31#ifdef CONFIG_HOTPLUG_CPU
  32static inline void cpu_leave_lowpower(u32 core_id)
  33{
  34        unsigned int v;
  35
  36        asm volatile(
  37        "mrc    p15, 0, %0, c1, c0, 0\n"
  38        "       orr     %0, %0, %1\n"
  39        "       mcr     p15, 0, %0, c1, c0, 0\n"
  40        "       mrc     p15, 0, %0, c1, c0, 1\n"
  41        "       orr     %0, %0, %2\n"
  42        "       mcr     p15, 0, %0, c1, c0, 1\n"
  43          : "=&r" (v)
  44          : "Ir" (CR_C), "Ir" (0x40)
  45          : "cc");
  46}
  47
  48static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
  49{
  50        u32 mpidr = cpu_logical_map(cpu);
  51        u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
  52
  53        for (;;) {
  54
  55                /* Turn the CPU off on next WFI instruction. */
  56                exynos_cpu_power_down(core_id);
  57
  58                wfi();
  59
  60                if (pen_release == core_id) {
  61                        /*
  62                         * OK, proper wakeup, we're done
  63                         */
  64                        break;
  65                }
  66
  67                /*
  68                 * Getting here, means that we have come out of WFI without
  69                 * having been woken up - this shouldn't happen
  70                 *
  71                 * Just note it happening - when we're woken, we can report
  72                 * its occurrence.
  73                 */
  74                (*spurious)++;
  75        }
  76}
  77#endif /* CONFIG_HOTPLUG_CPU */
  78
  79/**
  80 * exynos_core_power_down : power down the specified cpu
  81 * @cpu : the cpu to power down
  82 *
  83 * Power down the specified cpu. The sequence must be finished by a
  84 * call to cpu_do_idle()
  85 *
  86 */
  87void exynos_cpu_power_down(int cpu)
  88{
  89        u32 core_conf;
  90
  91        if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) {
  92                /*
  93                 * Bypass power down for CPU0 during suspend. Check for
  94                 * the SYS_PWR_REG value to decide if we are suspending
  95                 * the system.
  96                 */
  97                int val = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG);
  98
  99                if (!(val & S5P_CORE_LOCAL_PWR_EN))
 100                        return;
 101        }
 102
 103        core_conf = pmu_raw_readl(EXYNOS_ARM_CORE_CONFIGURATION(cpu));
 104        core_conf &= ~S5P_CORE_LOCAL_PWR_EN;
 105        pmu_raw_writel(core_conf, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
 106}
 107
 108/**
 109 * exynos_cpu_power_up : power up the specified cpu
 110 * @cpu : the cpu to power up
 111 *
 112 * Power up the specified cpu
 113 */
 114void exynos_cpu_power_up(int cpu)
 115{
 116        u32 core_conf = S5P_CORE_LOCAL_PWR_EN;
 117
 118        if (soc_is_exynos3250())
 119                core_conf |= S5P_CORE_AUTOWAKEUP_EN;
 120
 121        pmu_raw_writel(core_conf,
 122                        EXYNOS_ARM_CORE_CONFIGURATION(cpu));
 123}
 124
 125/**
 126 * exynos_cpu_power_state : returns the power state of the cpu
 127 * @cpu : the cpu to retrieve the power state from
 128 *
 129 */
 130int exynos_cpu_power_state(int cpu)
 131{
 132        return (pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) &
 133                        S5P_CORE_LOCAL_PWR_EN);
 134}
 135
 136/**
 137 * exynos_cluster_power_down : power down the specified cluster
 138 * @cluster : the cluster to power down
 139 */
 140void exynos_cluster_power_down(int cluster)
 141{
 142        pmu_raw_writel(0, EXYNOS_COMMON_CONFIGURATION(cluster));
 143}
 144
 145/**
 146 * exynos_cluster_power_up : power up the specified cluster
 147 * @cluster : the cluster to power up
 148 */
 149void exynos_cluster_power_up(int cluster)
 150{
 151        pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN,
 152                        EXYNOS_COMMON_CONFIGURATION(cluster));
 153}
 154
 155/**
 156 * exynos_cluster_power_state : returns the power state of the cluster
 157 * @cluster : the cluster to retrieve the power state from
 158 *
 159 */
 160int exynos_cluster_power_state(int cluster)
 161{
 162        return (pmu_raw_readl(EXYNOS_COMMON_STATUS(cluster)) &
 163                S5P_CORE_LOCAL_PWR_EN);
 164}
 165
 166/**
 167 * exynos_scu_enable : enables SCU for Cortex-A9 based system
 168 */
 169void exynos_scu_enable(void)
 170{
 171        struct device_node *np;
 172        static void __iomem *scu_base;
 173
 174        if (!scu_base) {
 175                np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
 176                if (np) {
 177                        scu_base = of_iomap(np, 0);
 178                        of_node_put(np);
 179                } else {
 180                        scu_base = ioremap(scu_a9_get_base(), SZ_4K);
 181                }
 182        }
 183        scu_enable(scu_base);
 184}
 185
 186static void __iomem *cpu_boot_reg_base(void)
 187{
 188        if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
 189                return pmu_base_addr + S5P_INFORM5;
 190        return sysram_base_addr;
 191}
 192
 193static inline void __iomem *cpu_boot_reg(int cpu)
 194{
 195        void __iomem *boot_reg;
 196
 197        boot_reg = cpu_boot_reg_base();
 198        if (!boot_reg)
 199                return IOMEM_ERR_PTR(-ENODEV);
 200        if (soc_is_exynos4412())
 201                boot_reg += 4*cpu;
 202        else if (soc_is_exynos5420() || soc_is_exynos5800())
 203                boot_reg += 4;
 204        return boot_reg;
 205}
 206
 207/*
 208 * Set wake up by local power mode and execute software reset for given core.
 209 *
 210 * Currently this is needed only when booting secondary CPU on Exynos3250.
 211 */
 212void exynos_core_restart(u32 core_id)
 213{
 214        u32 val;
 215
 216        if (!of_machine_is_compatible("samsung,exynos3250"))
 217                return;
 218
 219        while (!pmu_raw_readl(S5P_PMU_SPARE2))
 220                udelay(10);
 221        udelay(10);
 222
 223        val = pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(core_id));
 224        val |= S5P_CORE_WAKEUP_FROM_LOCAL_CFG;
 225        pmu_raw_writel(val, EXYNOS_ARM_CORE_STATUS(core_id));
 226
 227        pmu_raw_writel(EXYNOS_CORE_PO_RESET(core_id), EXYNOS_SWRESET);
 228}
 229
 230/*
 231 * Write pen_release in a way that is guaranteed to be visible to all
 232 * observers, irrespective of whether they're taking part in coherency
 233 * or not.  This is necessary for the hotplug code to work reliably.
 234 */
 235static void write_pen_release(int val)
 236{
 237        pen_release = val;
 238        smp_wmb();
 239        sync_cache_w(&pen_release);
 240}
 241
 242static DEFINE_SPINLOCK(boot_lock);
 243
 244static void exynos_secondary_init(unsigned int cpu)
 245{
 246        /*
 247         * let the primary processor know we're out of the
 248         * pen, then head off into the C entry point
 249         */
 250        write_pen_release(-1);
 251
 252        /*
 253         * Synchronise with the boot thread.
 254         */
 255        spin_lock(&boot_lock);
 256        spin_unlock(&boot_lock);
 257}
 258
 259int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr)
 260{
 261        int ret;
 262
 263        /*
 264         * Try to set boot address using firmware first
 265         * and fall back to boot register if it fails.
 266         */
 267        ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr);
 268        if (ret && ret != -ENOSYS)
 269                goto fail;
 270        if (ret == -ENOSYS) {
 271                void __iomem *boot_reg = cpu_boot_reg(core_id);
 272
 273                if (IS_ERR(boot_reg)) {
 274                        ret = PTR_ERR(boot_reg);
 275                        goto fail;
 276                }
 277                writel_relaxed(boot_addr, boot_reg);
 278                ret = 0;
 279        }
 280fail:
 281        return ret;
 282}
 283
 284int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr)
 285{
 286        int ret;
 287
 288        /*
 289         * Try to get boot address using firmware first
 290         * and fall back to boot register if it fails.
 291         */
 292        ret = call_firmware_op(get_cpu_boot_addr, core_id, boot_addr);
 293        if (ret && ret != -ENOSYS)
 294                goto fail;
 295        if (ret == -ENOSYS) {
 296                void __iomem *boot_reg = cpu_boot_reg(core_id);
 297
 298                if (IS_ERR(boot_reg)) {
 299                        ret = PTR_ERR(boot_reg);
 300                        goto fail;
 301                }
 302                *boot_addr = readl_relaxed(boot_reg);
 303                ret = 0;
 304        }
 305fail:
 306        return ret;
 307}
 308
 309static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
 310{
 311        unsigned long timeout;
 312        u32 mpidr = cpu_logical_map(cpu);
 313        u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
 314        int ret = -ENOSYS;
 315
 316        /*
 317         * Set synchronisation state between this boot processor
 318         * and the secondary one
 319         */
 320        spin_lock(&boot_lock);
 321
 322        /*
 323         * The secondary processor is waiting to be released from
 324         * the holding pen - release it, then wait for it to flag
 325         * that it has been released by resetting pen_release.
 326         *
 327         * Note that "pen_release" is the hardware CPU core ID, whereas
 328         * "cpu" is Linux's internal ID.
 329         */
 330        write_pen_release(core_id);
 331
 332        if (!exynos_cpu_power_state(core_id)) {
 333                exynos_cpu_power_up(core_id);
 334                timeout = 10;
 335
 336                /* wait max 10 ms until cpu1 is on */
 337                while (exynos_cpu_power_state(core_id)
 338                       != S5P_CORE_LOCAL_PWR_EN) {
 339                        if (timeout-- == 0)
 340                                break;
 341
 342                        mdelay(1);
 343                }
 344
 345                if (timeout == 0) {
 346                        printk(KERN_ERR "cpu1 power enable failed");
 347                        spin_unlock(&boot_lock);
 348                        return -ETIMEDOUT;
 349                }
 350        }
 351
 352        exynos_core_restart(core_id);
 353
 354        /*
 355         * Send the secondary CPU a soft interrupt, thereby causing
 356         * the boot monitor to read the system wide flags register,
 357         * and branch to the address found there.
 358         */
 359
 360        timeout = jiffies + (1 * HZ);
 361        while (time_before(jiffies, timeout)) {
 362                unsigned long boot_addr;
 363
 364                smp_rmb();
 365
 366                boot_addr = __pa_symbol(exynos4_secondary_startup);
 367
 368                ret = exynos_set_boot_addr(core_id, boot_addr);
 369                if (ret)
 370                        goto fail;
 371
 372                call_firmware_op(cpu_boot, core_id);
 373
 374                if (soc_is_exynos3250())
 375                        dsb_sev();
 376                else
 377                        arch_send_wakeup_ipi_mask(cpumask_of(cpu));
 378
 379                if (pen_release == -1)
 380                        break;
 381
 382                udelay(10);
 383        }
 384
 385        if (pen_release != -1)
 386                ret = -ETIMEDOUT;
 387
 388        /*
 389         * now the secondary core is starting up let it run its
 390         * calibrations, then wait for it to finish
 391         */
 392fail:
 393        spin_unlock(&boot_lock);
 394
 395        return pen_release != -1 ? ret : 0;
 396}
 397
 398static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
 399{
 400        int i;
 401
 402        exynos_sysram_init();
 403
 404        exynos_set_delayed_reset_assertion(true);
 405
 406        if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
 407                exynos_scu_enable();
 408
 409        /*
 410         * Write the address of secondary startup into the
 411         * system-wide flags register. The boot monitor waits
 412         * until it receives a soft interrupt, and then the
 413         * secondary CPU branches to this address.
 414         *
 415         * Try using firmware operation first and fall back to
 416         * boot register if it fails.
 417         */
 418        for (i = 1; i < max_cpus; ++i) {
 419                unsigned long boot_addr;
 420                u32 mpidr;
 421                u32 core_id;
 422                int ret;
 423
 424                mpidr = cpu_logical_map(i);
 425                core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
 426                boot_addr = __pa_symbol(exynos4_secondary_startup);
 427
 428                ret = exynos_set_boot_addr(core_id, boot_addr);
 429                if (ret)
 430                        break;
 431        }
 432}
 433
 434#ifdef CONFIG_HOTPLUG_CPU
 435/*
 436 * platform-specific code to shutdown a CPU
 437 *
 438 * Called with IRQs disabled
 439 */
 440static void exynos_cpu_die(unsigned int cpu)
 441{
 442        int spurious = 0;
 443        u32 mpidr = cpu_logical_map(cpu);
 444        u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
 445
 446        v7_exit_coherency_flush(louis);
 447
 448        platform_do_lowpower(cpu, &spurious);
 449
 450        /*
 451         * bring this CPU back into the world of cache
 452         * coherency, and then restore interrupts
 453         */
 454        cpu_leave_lowpower(core_id);
 455
 456        if (spurious)
 457                pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
 458}
 459#endif /* CONFIG_HOTPLUG_CPU */
 460
 461const struct smp_operations exynos_smp_ops __initconst = {
 462        .smp_prepare_cpus       = exynos_smp_prepare_cpus,
 463        .smp_secondary_init     = exynos_secondary_init,
 464        .smp_boot_secondary     = exynos_boot_secondary,
 465#ifdef CONFIG_HOTPLUG_CPU
 466        .cpu_die                = exynos_cpu_die,
 467#endif
 468};
 469