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.linux.kernel@gmail.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 /* XXX spear_pen_release is cargo culted code - DO NOT COPY XXX */ 24 volatile int spear_pen_release = -1; 25 26 /* 27 * XXX CARGO CULTED CODE - DO NOT COPY XXX 28 * 29 * Write spear_pen_release in a way that is guaranteed to be visible to 30 * all observers, irrespective of whether they're taking part in coherency 31 * or not. This is necessary for the hotplug code to work reliably. 32 */ 33 static void spear_write_pen_release(int val) 34 { 35 spear_pen_release = val; 36 smp_wmb(); 37 sync_cache_w(&spear_pen_release); 38 } 39 40 static DEFINE_SPINLOCK(boot_lock); 41 42 static void __iomem *scu_base = IOMEM(VA_SCU_BASE); 43 44 static void spear13xx_secondary_init(unsigned int cpu) 45 { 46 /* 47 * let the primary processor know we're out of the 48 * pen, then head off into the C entry point 49 */ 50 spear_write_pen_release(-1); 51 52 /* 53 * Synchronise with the boot thread. 54 */ 55 spin_lock(&boot_lock); 56 spin_unlock(&boot_lock); 57 } 58 59 static int spear13xx_boot_secondary(unsigned int cpu, struct task_struct *idle) 60 { 61 unsigned long timeout; 62 63 /* 64 * set synchronisation state between this boot processor 65 * and the secondary one 66 */ 67 spin_lock(&boot_lock); 68 69 /* 70 * The secondary processor is waiting to be released from 71 * the holding pen - release it, then wait for it to flag 72 * that it has been released by resetting spear_pen_release. 73 * 74 * Note that "spear_pen_release" is the hardware CPU ID, whereas 75 * "cpu" is Linux's internal ID. 76 */ 77 spear_write_pen_release(cpu); 78 79 timeout = jiffies + (1 * HZ); 80 while (time_before(jiffies, timeout)) { 81 smp_rmb(); 82 if (spear_pen_release == -1) 83 break; 84 85 udelay(10); 86 } 87 88 /* 89 * now the secondary core is starting up let it run its 90 * calibrations, then wait for it to finish 91 */ 92 spin_unlock(&boot_lock); 93 94 return spear_pen_release != -1 ? -ENOSYS : 0; 95 } 96 97 /* 98 * Initialise the CPU possible map early - this describes the CPUs 99 * which may be present or become present in the system. 100 */ 101 static void __init spear13xx_smp_init_cpus(void) 102 { 103 unsigned int i, ncores = scu_get_core_count(scu_base); 104 105 if (ncores > nr_cpu_ids) { 106 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", 107 ncores, nr_cpu_ids); 108 ncores = nr_cpu_ids; 109 } 110 111 for (i = 0; i < ncores; i++) 112 set_cpu_possible(i, true); 113 } 114 115 static void __init spear13xx_smp_prepare_cpus(unsigned int max_cpus) 116 { 117 118 scu_enable(scu_base); 119 120 /* 121 * Write the address of secondary startup into the system-wide location 122 * (presently it is in SRAM). The BootMonitor waits until it receives a 123 * soft interrupt, and then the secondary CPU branches to this address. 124 */ 125 __raw_writel(__pa_symbol(spear13xx_secondary_startup), SYS_LOCATION); 126 } 127 128 const struct smp_operations spear13xx_smp_ops __initconst = { 129 .smp_init_cpus = spear13xx_smp_init_cpus, 130 .smp_prepare_cpus = spear13xx_smp_prepare_cpus, 131 .smp_secondary_init = spear13xx_secondary_init, 132 .smp_boot_secondary = spear13xx_boot_secondary, 133 #ifdef CONFIG_HOTPLUG_CPU 134 .cpu_die = spear13xx_cpu_die, 135 #endif 136 }; 137