1 /* linux/arch/arm/mach-exynos4/platsmp.c 2 * 3 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. 4 * http://www.samsung.com 5 * 6 * Cloned from linux/arch/arm/mach-vexpress/platsmp.c 7 * 8 * Copyright (C) 2002 ARM Ltd. 9 * All Rights Reserved 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 as 13 * published by the Free Software Foundation. 14 */ 15 16 #include <linux/init.h> 17 #include <linux/errno.h> 18 #include <linux/delay.h> 19 #include <linux/device.h> 20 #include <linux/jiffies.h> 21 #include <linux/smp.h> 22 #include <linux/io.h> 23 24 #include <asm/cacheflush.h> 25 #include <asm/smp_plat.h> 26 #include <asm/smp_scu.h> 27 #include <asm/firmware.h> 28 29 #include <mach/hardware.h> 30 #include <mach/regs-clock.h> 31 #include <mach/regs-pmu.h> 32 33 #include <plat/cpu.h> 34 35 #include "common.h" 36 37 extern void exynos4_secondary_startup(void); 38 39 static inline void __iomem *cpu_boot_reg_base(void) 40 { 41 if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1) 42 return S5P_INFORM5; 43 return S5P_VA_SYSRAM; 44 } 45 46 static inline void __iomem *cpu_boot_reg(int cpu) 47 { 48 void __iomem *boot_reg; 49 50 boot_reg = cpu_boot_reg_base(); 51 if (soc_is_exynos4412()) 52 boot_reg += 4*cpu; 53 else if (soc_is_exynos5420()) 54 boot_reg += 4; 55 return boot_reg; 56 } 57 58 /* 59 * Write pen_release in a way that is guaranteed to be visible to all 60 * observers, irrespective of whether they're taking part in coherency 61 * or not. This is necessary for the hotplug code to work reliably. 62 */ 63 static void write_pen_release(int val) 64 { 65 pen_release = val; 66 smp_wmb(); 67 __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); 68 outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1)); 69 } 70 71 static void __iomem *scu_base_addr(void) 72 { 73 return (void __iomem *)(S5P_VA_SCU); 74 } 75 76 static DEFINE_SPINLOCK(boot_lock); 77 78 static void exynos_secondary_init(unsigned int cpu) 79 { 80 /* 81 * let the primary processor know we're out of the 82 * pen, then head off into the C entry point 83 */ 84 write_pen_release(-1); 85 86 /* 87 * Synchronise with the boot thread. 88 */ 89 spin_lock(&boot_lock); 90 spin_unlock(&boot_lock); 91 } 92 93 static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) 94 { 95 unsigned long timeout; 96 unsigned long phys_cpu = cpu_logical_map(cpu); 97 98 /* 99 * Set synchronisation state between this boot processor 100 * and the secondary one 101 */ 102 spin_lock(&boot_lock); 103 104 /* 105 * The secondary processor is waiting to be released from 106 * the holding pen - release it, then wait for it to flag 107 * that it has been released by resetting pen_release. 108 * 109 * Note that "pen_release" is the hardware CPU ID, whereas 110 * "cpu" is Linux's internal ID. 111 */ 112 write_pen_release(phys_cpu); 113 114 if (!(__raw_readl(S5P_ARM_CORE1_STATUS) & S5P_CORE_LOCAL_PWR_EN)) { 115 __raw_writel(S5P_CORE_LOCAL_PWR_EN, 116 S5P_ARM_CORE1_CONFIGURATION); 117 118 timeout = 10; 119 120 /* wait max 10 ms until cpu1 is on */ 121 while ((__raw_readl(S5P_ARM_CORE1_STATUS) 122 & S5P_CORE_LOCAL_PWR_EN) != S5P_CORE_LOCAL_PWR_EN) { 123 if (timeout-- == 0) 124 break; 125 126 mdelay(1); 127 } 128 129 if (timeout == 0) { 130 printk(KERN_ERR "cpu1 power enable failed"); 131 spin_unlock(&boot_lock); 132 return -ETIMEDOUT; 133 } 134 } 135 /* 136 * Send the secondary CPU a soft interrupt, thereby causing 137 * the boot monitor to read the system wide flags register, 138 * and branch to the address found there. 139 */ 140 141 timeout = jiffies + (1 * HZ); 142 while (time_before(jiffies, timeout)) { 143 unsigned long boot_addr; 144 145 smp_rmb(); 146 147 boot_addr = virt_to_phys(exynos4_secondary_startup); 148 149 /* 150 * Try to set boot address using firmware first 151 * and fall back to boot register if it fails. 152 */ 153 if (call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr)) 154 __raw_writel(boot_addr, cpu_boot_reg(phys_cpu)); 155 156 call_firmware_op(cpu_boot, phys_cpu); 157 158 arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 159 160 if (pen_release == -1) 161 break; 162 163 udelay(10); 164 } 165 166 /* 167 * now the secondary core is starting up let it run its 168 * calibrations, then wait for it to finish 169 */ 170 spin_unlock(&boot_lock); 171 172 return pen_release != -1 ? -ENOSYS : 0; 173 } 174 175 /* 176 * Initialise the CPU possible map early - this describes the CPUs 177 * which may be present or become present in the system. 178 */ 179 180 static void __init exynos_smp_init_cpus(void) 181 { 182 void __iomem *scu_base = scu_base_addr(); 183 unsigned int i, ncores; 184 185 if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9) 186 ncores = scu_base ? scu_get_core_count(scu_base) : 1; 187 else 188 /* 189 * CPU Nodes are passed thru DT and set_cpu_possible 190 * is set by "arm_dt_init_cpu_maps". 191 */ 192 return; 193 194 /* sanity check */ 195 if (ncores > nr_cpu_ids) { 196 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", 197 ncores, nr_cpu_ids); 198 ncores = nr_cpu_ids; 199 } 200 201 for (i = 0; i < ncores; i++) 202 set_cpu_possible(i, true); 203 } 204 205 static void __init exynos_smp_prepare_cpus(unsigned int max_cpus) 206 { 207 int i; 208 209 if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9) 210 scu_enable(scu_base_addr()); 211 212 /* 213 * Write the address of secondary startup into the 214 * system-wide flags register. The boot monitor waits 215 * until it receives a soft interrupt, and then the 216 * secondary CPU branches to this address. 217 * 218 * Try using firmware operation first and fall back to 219 * boot register if it fails. 220 */ 221 for (i = 1; i < max_cpus; ++i) { 222 unsigned long phys_cpu; 223 unsigned long boot_addr; 224 225 phys_cpu = cpu_logical_map(i); 226 boot_addr = virt_to_phys(exynos4_secondary_startup); 227 228 if (call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr)) 229 __raw_writel(boot_addr, cpu_boot_reg(phys_cpu)); 230 } 231 } 232 233 struct smp_operations exynos_smp_ops __initdata = { 234 .smp_init_cpus = exynos_smp_init_cpus, 235 .smp_prepare_cpus = exynos_smp_prepare_cpus, 236 .smp_secondary_init = exynos_secondary_init, 237 .smp_boot_secondary = exynos_boot_secondary, 238 #ifdef CONFIG_HOTPLUG_CPU 239 .cpu_die = exynos_cpu_die, 240 #endif 241 }; 242