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