Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
0003 //      http://www.samsung.com
0004 //
0005 // Cloned from linux/arch/arm/mach-vexpress/platsmp.c
0006 //
0007 //  Copyright (C) 2002 ARM Ltd.
0008 //  All Rights Reserved
0009 
0010 #include <linux/init.h>
0011 #include <linux/errno.h>
0012 #include <linux/delay.h>
0013 #include <linux/jiffies.h>
0014 #include <linux/smp.h>
0015 #include <linux/io.h>
0016 #include <linux/of_address.h>
0017 #include <linux/soc/samsung/exynos-regs-pmu.h>
0018 
0019 #include <asm/cacheflush.h>
0020 #include <asm/cp15.h>
0021 #include <asm/smp_plat.h>
0022 #include <asm/smp_scu.h>
0023 #include <asm/firmware.h>
0024 
0025 #include "common.h"
0026 
0027 extern void exynos4_secondary_startup(void);
0028 
0029 /* XXX exynos_pen_release is cargo culted code - DO NOT COPY XXX */
0030 volatile int exynos_pen_release = -1;
0031 
0032 #ifdef CONFIG_HOTPLUG_CPU
0033 static inline void cpu_leave_lowpower(u32 core_id)
0034 {
0035     unsigned int v;
0036 
0037     asm volatile(
0038     "mrc    p15, 0, %0, c1, c0, 0\n"
0039     "   orr %0, %0, %1\n"
0040     "   mcr p15, 0, %0, c1, c0, 0\n"
0041     "   mrc p15, 0, %0, c1, c0, 1\n"
0042     "   orr %0, %0, %2\n"
0043     "   mcr p15, 0, %0, c1, c0, 1\n"
0044       : "=&r" (v)
0045       : "Ir" (CR_C), "Ir" (0x40)
0046       : "cc");
0047 }
0048 
0049 static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
0050 {
0051     u32 mpidr = cpu_logical_map(cpu);
0052     u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
0053 
0054     for (;;) {
0055 
0056         /* Turn the CPU off on next WFI instruction. */
0057         exynos_cpu_power_down(core_id);
0058 
0059         wfi();
0060 
0061         if (exynos_pen_release == core_id) {
0062             /*
0063              * OK, proper wakeup, we're done
0064              */
0065             break;
0066         }
0067 
0068         /*
0069          * Getting here, means that we have come out of WFI without
0070          * having been woken up - this shouldn't happen
0071          *
0072          * Just note it happening - when we're woken, we can report
0073          * its occurrence.
0074          */
0075         (*spurious)++;
0076     }
0077 }
0078 #endif /* CONFIG_HOTPLUG_CPU */
0079 
0080 /**
0081  * exynos_cpu_power_down() - power down the specified cpu
0082  * @cpu: the cpu to power down
0083  *
0084  * Power down the specified cpu. The sequence must be finished by a
0085  * call to cpu_do_idle()
0086  */
0087 void exynos_cpu_power_down(int cpu)
0088 {
0089     u32 core_conf;
0090 
0091     if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) {
0092         /*
0093          * Bypass power down for CPU0 during suspend. Check for
0094          * the SYS_PWR_REG value to decide if we are suspending
0095          * the system.
0096          */
0097         int val = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG);
0098 
0099         if (!(val & S5P_CORE_LOCAL_PWR_EN))
0100             return;
0101     }
0102 
0103     core_conf = pmu_raw_readl(EXYNOS_ARM_CORE_CONFIGURATION(cpu));
0104     core_conf &= ~S5P_CORE_LOCAL_PWR_EN;
0105     pmu_raw_writel(core_conf, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
0106 }
0107 
0108 /**
0109  * exynos_cpu_power_up() - power up the specified cpu
0110  * @cpu: the cpu to power up
0111  *
0112  * Power up the specified cpu
0113  */
0114 void exynos_cpu_power_up(int cpu)
0115 {
0116     u32 core_conf = S5P_CORE_LOCAL_PWR_EN;
0117 
0118     if (soc_is_exynos3250())
0119         core_conf |= S5P_CORE_AUTOWAKEUP_EN;
0120 
0121     pmu_raw_writel(core_conf,
0122             EXYNOS_ARM_CORE_CONFIGURATION(cpu));
0123 }
0124 
0125 /**
0126  * exynos_cpu_power_state() - returns the power state of the cpu
0127  * @cpu: the cpu to retrieve the power state from
0128  */
0129 int exynos_cpu_power_state(int cpu)
0130 {
0131     return (pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) &
0132             S5P_CORE_LOCAL_PWR_EN);
0133 }
0134 
0135 /**
0136  * exynos_cluster_power_down() - power down the specified cluster
0137  * @cluster: the cluster to power down
0138  */
0139 void exynos_cluster_power_down(int cluster)
0140 {
0141     pmu_raw_writel(0, EXYNOS_COMMON_CONFIGURATION(cluster));
0142 }
0143 
0144 /**
0145  * exynos_cluster_power_up() - power up the specified cluster
0146  * @cluster: the cluster to power up
0147  */
0148 void exynos_cluster_power_up(int cluster)
0149 {
0150     pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN,
0151             EXYNOS_COMMON_CONFIGURATION(cluster));
0152 }
0153 
0154 /**
0155  * exynos_cluster_power_state() - returns the power state of the cluster
0156  * @cluster: the cluster to retrieve the power state from
0157  *
0158  */
0159 int exynos_cluster_power_state(int cluster)
0160 {
0161     return (pmu_raw_readl(EXYNOS_COMMON_STATUS(cluster)) &
0162         S5P_CORE_LOCAL_PWR_EN);
0163 }
0164 
0165 /**
0166  * exynos_scu_enable() - enables SCU for Cortex-A9 based system
0167  */
0168 void exynos_scu_enable(void)
0169 {
0170     struct device_node *np;
0171     static void __iomem *scu_base;
0172 
0173     if (!scu_base) {
0174         np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
0175         if (np) {
0176             scu_base = of_iomap(np, 0);
0177             of_node_put(np);
0178         } else {
0179             scu_base = ioremap(scu_a9_get_base(), SZ_4K);
0180         }
0181     }
0182     scu_enable(scu_base);
0183 }
0184 
0185 static void __iomem *cpu_boot_reg_base(void)
0186 {
0187     if (soc_is_exynos4210() && exynos_rev() == EXYNOS4210_REV_1_1)
0188         return pmu_base_addr + S5P_INFORM5;
0189     return sysram_base_addr;
0190 }
0191 
0192 static inline void __iomem *cpu_boot_reg(int cpu)
0193 {
0194     void __iomem *boot_reg;
0195 
0196     boot_reg = cpu_boot_reg_base();
0197     if (!boot_reg)
0198         return IOMEM_ERR_PTR(-ENODEV);
0199     if (soc_is_exynos4412())
0200         boot_reg += 4*cpu;
0201     else if (soc_is_exynos5420() || soc_is_exynos5800())
0202         boot_reg += 4;
0203     return boot_reg;
0204 }
0205 
0206 /*
0207  * Set wake up by local power mode and execute software reset for given core.
0208  *
0209  * Currently this is needed only when booting secondary CPU on Exynos3250.
0210  */
0211 void exynos_core_restart(u32 core_id)
0212 {
0213     unsigned int timeout = 16;
0214     u32 val;
0215 
0216     if (!soc_is_exynos3250())
0217         return;
0218 
0219     while (timeout && !pmu_raw_readl(S5P_PMU_SPARE2)) {
0220         timeout--;
0221         udelay(10);
0222     }
0223     if (timeout == 0) {
0224         pr_err("cpu core %u restart failed\n", core_id);
0225         return;
0226     }
0227     udelay(10);
0228 
0229     val = pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(core_id));
0230     val |= S5P_CORE_WAKEUP_FROM_LOCAL_CFG;
0231     pmu_raw_writel(val, EXYNOS_ARM_CORE_STATUS(core_id));
0232 
0233     pmu_raw_writel(EXYNOS_CORE_PO_RESET(core_id), EXYNOS_SWRESET);
0234 }
0235 
0236 /*
0237  * XXX CARGO CULTED CODE - DO NOT COPY XXX
0238  *
0239  * Write exynos_pen_release in a way that is guaranteed to be visible to
0240  * all observers, irrespective of whether they're taking part in coherency
0241  * or not.  This is necessary for the hotplug code to work reliably.
0242  */
0243 static void exynos_write_pen_release(int val)
0244 {
0245     exynos_pen_release = val;
0246     smp_wmb();
0247     sync_cache_w(&exynos_pen_release);
0248 }
0249 
0250 static DEFINE_SPINLOCK(boot_lock);
0251 
0252 static void exynos_secondary_init(unsigned int cpu)
0253 {
0254     /*
0255      * let the primary processor know we're out of the
0256      * pen, then head off into the C entry point
0257      */
0258     exynos_write_pen_release(-1);
0259 
0260     /*
0261      * Synchronise with the boot thread.
0262      */
0263     spin_lock(&boot_lock);
0264     spin_unlock(&boot_lock);
0265 }
0266 
0267 int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr)
0268 {
0269     int ret;
0270 
0271     /*
0272      * Try to set boot address using firmware first
0273      * and fall back to boot register if it fails.
0274      */
0275     ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr);
0276     if (ret && ret != -ENOSYS)
0277         goto fail;
0278     if (ret == -ENOSYS) {
0279         void __iomem *boot_reg = cpu_boot_reg(core_id);
0280 
0281         if (IS_ERR(boot_reg)) {
0282             ret = PTR_ERR(boot_reg);
0283             goto fail;
0284         }
0285         writel_relaxed(boot_addr, boot_reg);
0286         ret = 0;
0287     }
0288 fail:
0289     return ret;
0290 }
0291 
0292 int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr)
0293 {
0294     int ret;
0295 
0296     /*
0297      * Try to get boot address using firmware first
0298      * and fall back to boot register if it fails.
0299      */
0300     ret = call_firmware_op(get_cpu_boot_addr, core_id, boot_addr);
0301     if (ret && ret != -ENOSYS)
0302         goto fail;
0303     if (ret == -ENOSYS) {
0304         void __iomem *boot_reg = cpu_boot_reg(core_id);
0305 
0306         if (IS_ERR(boot_reg)) {
0307             ret = PTR_ERR(boot_reg);
0308             goto fail;
0309         }
0310         *boot_addr = readl_relaxed(boot_reg);
0311         ret = 0;
0312     }
0313 fail:
0314     return ret;
0315 }
0316 
0317 static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
0318 {
0319     unsigned long timeout;
0320     u32 mpidr = cpu_logical_map(cpu);
0321     u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
0322     int ret = -ENOSYS;
0323 
0324     /*
0325      * Set synchronisation state between this boot processor
0326      * and the secondary one
0327      */
0328     spin_lock(&boot_lock);
0329 
0330     /*
0331      * The secondary processor is waiting to be released from
0332      * the holding pen - release it, then wait for it to flag
0333      * that it has been released by resetting exynos_pen_release.
0334      *
0335      * Note that "exynos_pen_release" is the hardware CPU core ID, whereas
0336      * "cpu" is Linux's internal ID.
0337      */
0338     exynos_write_pen_release(core_id);
0339 
0340     if (!exynos_cpu_power_state(core_id)) {
0341         exynos_cpu_power_up(core_id);
0342         timeout = 10;
0343 
0344         /* wait max 10 ms until cpu1 is on */
0345         while (exynos_cpu_power_state(core_id)
0346                != S5P_CORE_LOCAL_PWR_EN) {
0347             if (timeout == 0)
0348                 break;
0349             timeout--;
0350             mdelay(1);
0351         }
0352 
0353         if (timeout == 0) {
0354             printk(KERN_ERR "cpu1 power enable failed");
0355             spin_unlock(&boot_lock);
0356             return -ETIMEDOUT;
0357         }
0358     }
0359 
0360     exynos_core_restart(core_id);
0361 
0362     /*
0363      * Send the secondary CPU a soft interrupt, thereby causing
0364      * the boot monitor to read the system wide flags register,
0365      * and branch to the address found there.
0366      */
0367 
0368     timeout = jiffies + (1 * HZ);
0369     while (time_before(jiffies, timeout)) {
0370         unsigned long boot_addr;
0371 
0372         smp_rmb();
0373 
0374         boot_addr = __pa_symbol(exynos4_secondary_startup);
0375 
0376         ret = exynos_set_boot_addr(core_id, boot_addr);
0377         if (ret)
0378             goto fail;
0379 
0380         call_firmware_op(cpu_boot, core_id);
0381 
0382         if (soc_is_exynos3250())
0383             dsb_sev();
0384         else
0385             arch_send_wakeup_ipi_mask(cpumask_of(cpu));
0386 
0387         if (exynos_pen_release == -1)
0388             break;
0389 
0390         udelay(10);
0391     }
0392 
0393     if (exynos_pen_release != -1)
0394         ret = -ETIMEDOUT;
0395 
0396     /*
0397      * now the secondary core is starting up let it run its
0398      * calibrations, then wait for it to finish
0399      */
0400 fail:
0401     spin_unlock(&boot_lock);
0402 
0403     return exynos_pen_release != -1 ? ret : 0;
0404 }
0405 
0406 static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
0407 {
0408     exynos_sysram_init();
0409 
0410     exynos_set_delayed_reset_assertion(true);
0411 
0412     if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
0413         exynos_scu_enable();
0414 }
0415 
0416 #ifdef CONFIG_HOTPLUG_CPU
0417 /*
0418  * platform-specific code to shutdown a CPU
0419  *
0420  * Called with IRQs disabled
0421  */
0422 static void exynos_cpu_die(unsigned int cpu)
0423 {
0424     int spurious = 0;
0425     u32 mpidr = cpu_logical_map(cpu);
0426     u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
0427 
0428     v7_exit_coherency_flush(louis);
0429 
0430     platform_do_lowpower(cpu, &spurious);
0431 
0432     /*
0433      * bring this CPU back into the world of cache
0434      * coherency, and then restore interrupts
0435      */
0436     cpu_leave_lowpower(core_id);
0437 
0438     if (spurious)
0439         pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
0440 }
0441 #endif /* CONFIG_HOTPLUG_CPU */
0442 
0443 const struct smp_operations exynos_smp_ops __initconst = {
0444     .smp_prepare_cpus   = exynos_smp_prepare_cpus,
0445     .smp_secondary_init = exynos_secondary_init,
0446     .smp_boot_secondary = exynos_boot_secondary,
0447 #ifdef CONFIG_HOTPLUG_CPU
0448     .cpu_die        = exynos_cpu_die,
0449 #endif
0450 };