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