1 /* 2 * arch/arm/mach-spear13xx/platsmp.c 3 * 4 * based upon linux/arch/arm/mach-realview/platsmp.c 5 * 6 * Copyright (C) 2012 ST Microelectronics Ltd. 7 * Shiraz Hashim <shiraz.hashim@st.com> 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/delay.h> 15 #include <linux/jiffies.h> 16 #include <linux/io.h> 17 #include <linux/smp.h> 18 #include <asm/cacheflush.h> 19 #include <asm/smp_scu.h> 20 #include <mach/spear.h> 21 #include "generic.h" 22 23 static DEFINE_SPINLOCK(boot_lock); 24 25 static void __iomem *scu_base = IOMEM(VA_SCU_BASE); 26 27 static void spear13xx_secondary_init(unsigned int cpu) 28 { 29 /* 30 * let the primary processor know we're out of the 31 * pen, then head off into the C entry point 32 */ 33 pen_release = -1; 34 smp_wmb(); 35 36 /* 37 * Synchronise with the boot thread. 38 */ 39 spin_lock(&boot_lock); 40 spin_unlock(&boot_lock); 41 } 42 43 static int spear13xx_boot_secondary(unsigned int cpu, struct task_struct *idle) 44 { 45 unsigned long timeout; 46 47 /* 48 * set synchronisation state between this boot processor 49 * and the secondary one 50 */ 51 spin_lock(&boot_lock); 52 53 /* 54 * The secondary processor is waiting to be released from 55 * the holding pen - release it, then wait for it to flag 56 * that it has been released by resetting pen_release. 57 * 58 * Note that "pen_release" is the hardware CPU ID, whereas 59 * "cpu" is Linux's internal ID. 60 */ 61 pen_release = cpu; 62 flush_cache_all(); 63 outer_flush_all(); 64 65 timeout = jiffies + (1 * HZ); 66 while (time_before(jiffies, timeout)) { 67 smp_rmb(); 68 if (pen_release == -1) 69 break; 70 71 udelay(10); 72 } 73 74 /* 75 * now the secondary core is starting up let it run its 76 * calibrations, then wait for it to finish 77 */ 78 spin_unlock(&boot_lock); 79 80 return pen_release != -1 ? -ENOSYS : 0; 81 } 82 83 /* 84 * Initialise the CPU possible map early - this describes the CPUs 85 * which may be present or become present in the system. 86 */ 87 static void __init spear13xx_smp_init_cpus(void) 88 { 89 unsigned int i, ncores = scu_get_core_count(scu_base); 90 91 if (ncores > nr_cpu_ids) { 92 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", 93 ncores, nr_cpu_ids); 94 ncores = nr_cpu_ids; 95 } 96 97 for (i = 0; i < ncores; i++) 98 set_cpu_possible(i, true); 99 } 100 101 static void __init spear13xx_smp_prepare_cpus(unsigned int max_cpus) 102 { 103 104 scu_enable(scu_base); 105 106 /* 107 * Write the address of secondary startup into the system-wide location 108 * (presently it is in SRAM). The BootMonitor waits until it receives a 109 * soft interrupt, and then the secondary CPU branches to this address. 110 */ 111 __raw_writel(virt_to_phys(spear13xx_secondary_startup), SYS_LOCATION); 112 } 113 114 struct smp_operations spear13xx_smp_ops __initdata = { 115 .smp_init_cpus = spear13xx_smp_init_cpus, 116 .smp_prepare_cpus = spear13xx_smp_prepare_cpus, 117 .smp_secondary_init = spear13xx_secondary_init, 118 .smp_boot_secondary = spear13xx_boot_secondary, 119 #ifdef CONFIG_HOTPLUG_CPU 120 .cpu_die = spear13xx_cpu_die, 121 #endif 122 }; 123