1/* 2 * linux/arch/arm/mach-vexpress/platsmp.c 3 * 4 * Copyright (C) 2002 ARM Ltd. 5 * All Rights Reserved 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11#include <linux/init.h> 12#include <linux/errno.h> 13#include <linux/delay.h> 14#include <linux/device.h> 15#include <linux/jiffies.h> 16#include <linux/smp.h> 17#include <linux/io.h> 18 19#include <asm/cacheflush.h> 20#include <asm/localtimer.h> 21#include <asm/smp_scu.h> 22#include <asm/unified.h> 23 24#include <mach/ct-ca9x4.h> 25#include <mach/motherboard.h> 26#define V2M_PA_CS7 0x10000000 27 28#include "core.h" 29 30extern void vexpress_secondary_startup(void); 31 32/* 33 * control for which core is the next to come out of the secondary 34 * boot "holding pen" 35 */ 36volatile int __cpuinitdata pen_release = -1; 37 38static void __iomem *scu_base_addr(void) 39{ 40 return MMIO_P2V(A9_MPCORE_SCU); 41} 42 43static DEFINE_SPINLOCK(boot_lock); 44 45void __cpuinit platform_secondary_init(unsigned int cpu) 46{ 47 trace_hardirqs_off(); 48 49 /* 50 * if any interrupts are already enabled for the primary 51 * core (e.g. timer irq), then they will not have been enabled 52 * for us: do so 53 */ 54 gic_cpu_init(0, gic_cpu_base_addr); 55 56 /* 57 * let the primary processor know we're out of the 58 * pen, then head off into the C entry point 59 */ 60 pen_release = -1; 61 smp_wmb(); 62 63 /* 64 * Synchronise with the boot thread. 65 */ 66 spin_lock(&boot_lock); 67 spin_unlock(&boot_lock); 68} 69 70int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) 71{ 72 unsigned long timeout; 73 74 /* 75 * Set synchronisation state between this boot processor 76 * and the secondary one 77 */ 78 spin_lock(&boot_lock); 79 80 /* 81 * This is really belt and braces; we hold unintended secondary 82 * CPUs in the holding pen until we're ready for them. However, 83 * since we haven't sent them a soft interrupt, they shouldn't 84 * be there. 85 */ 86 pen_release = cpu; 87 __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); 88 outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1)); 89 90 /* 91 * Send the secondary CPU a soft interrupt, thereby causing 92 * the boot monitor to read the system wide flags register, 93 * and branch to the address found there. 94 */ 95 smp_cross_call(cpumask_of(cpu)); 96 97 timeout = jiffies + (1 * HZ); 98 while (time_before(jiffies, timeout)) { 99 smp_rmb(); 100 if (pen_release == -1) 101 break; 102 103 udelay(10); 104 } 105 106 /* 107 * now the secondary core is starting up let it run its 108 * calibrations, then wait for it to finish 109 */ 110 spin_unlock(&boot_lock); 111 112 return pen_release != -1 ? -ENOSYS : 0; 113} 114 115/* 116 * Initialise the CPU possible map early - this describes the CPUs 117 * which may be present or become present in the system. 118 */ 119void __init smp_init_cpus(void) 120{ 121 void __iomem *scu_base = scu_base_addr(); 122 unsigned int i, ncores; 123 124 ncores = scu_base ? scu_get_core_count(scu_base) : 1; 125 126 /* sanity check */ 127 if (ncores == 0) { 128 printk(KERN_ERR 129 "vexpress: strange CM count of 0? Default to 1\n"); 130 131 ncores = 1; 132 } 133 134 if (ncores > NR_CPUS) { 135 printk(KERN_WARNING 136 "vexpress: no. of cores (%d) greater than configured " 137 "maximum of %d - clipping\n", 138 ncores, NR_CPUS); 139 ncores = NR_CPUS; 140 } 141 142 for (i = 0; i < ncores; i++) 143 set_cpu_possible(i, true); 144} 145 146void __init smp_prepare_cpus(unsigned int max_cpus) 147{ 148 unsigned int ncores = num_possible_cpus(); 149 unsigned int cpu = smp_processor_id(); 150 int i; 151 152 smp_store_cpu_info(cpu); 153 154 /* 155 * are we trying to boot more cores than exist? 156 */ 157 if (max_cpus > ncores) 158 max_cpus = ncores; 159 160 /* 161 * Initialise the present map, which describes the set of CPUs 162 * actually populated at the present time. 163 */ 164 for (i = 0; i < max_cpus; i++) 165 set_cpu_present(i, true); 166 167 /* 168 * Initialise the SCU if there are more than one CPU and let 169 * them know where to start. 170 */ 171 if (max_cpus > 1) { 172 /* 173 * Enable the local timer or broadcast device for the 174 * boot CPU, but only if we have more than one CPU. 175 */ 176 percpu_timer_setup(); 177 178 scu_enable(scu_base_addr()); 179 180 /* 181 * Write the address of secondary startup into the 182 * system-wide flags register. The boot monitor waits 183 * until it receives a soft interrupt, and then the 184 * secondary CPU branches to this address. 185 */ 186 writel(~0, MMIO_P2V(V2M_SYS_FLAGSCLR)); 187 writel(BSYM(virt_to_phys(vexpress_secondary_startup)), 188 MMIO_P2V(V2M_SYS_FLAGSSET)); 189 } 190} 191