162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * arch/arm/mach-spear13xx/platsmp.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * based upon linux/arch/arm/mach-realview/platsmp.c
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2012 ST Microelectronics Ltd.
862306a36Sopenharmony_ci * Shiraz Hashim <shiraz.linux.kernel@gmail.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/jiffies.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/smp.h>
1562306a36Sopenharmony_ci#include <asm/cacheflush.h>
1662306a36Sopenharmony_ci#include <asm/smp_scu.h>
1762306a36Sopenharmony_ci#include "spear.h"
1862306a36Sopenharmony_ci#include "generic.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* XXX spear_pen_release is cargo culted code - DO NOT COPY XXX */
2162306a36Sopenharmony_civolatile int spear_pen_release = -1;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * XXX CARGO CULTED CODE - DO NOT COPY XXX
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * Write spear_pen_release in a way that is guaranteed to be visible to
2762306a36Sopenharmony_ci * all observers, irrespective of whether they're taking part in coherency
2862306a36Sopenharmony_ci * or not.  This is necessary for the hotplug code to work reliably.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_cistatic void spear_write_pen_release(int val)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	spear_pen_release = val;
3362306a36Sopenharmony_ci	smp_wmb();
3462306a36Sopenharmony_ci	sync_cache_w(&spear_pen_release);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(boot_lock);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void __iomem *scu_base = IOMEM(VA_SCU_BASE);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void spear13xx_secondary_init(unsigned int cpu)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	/*
4462306a36Sopenharmony_ci	 * let the primary processor know we're out of the
4562306a36Sopenharmony_ci	 * pen, then head off into the C entry point
4662306a36Sopenharmony_ci	 */
4762306a36Sopenharmony_ci	spear_write_pen_release(-1);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/*
5062306a36Sopenharmony_ci	 * Synchronise with the boot thread.
5162306a36Sopenharmony_ci	 */
5262306a36Sopenharmony_ci	spin_lock(&boot_lock);
5362306a36Sopenharmony_ci	spin_unlock(&boot_lock);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int spear13xx_boot_secondary(unsigned int cpu, struct task_struct *idle)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	unsigned long timeout;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/*
6162306a36Sopenharmony_ci	 * set synchronisation state between this boot processor
6262306a36Sopenharmony_ci	 * and the secondary one
6362306a36Sopenharmony_ci	 */
6462306a36Sopenharmony_ci	spin_lock(&boot_lock);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/*
6762306a36Sopenharmony_ci	 * The secondary processor is waiting to be released from
6862306a36Sopenharmony_ci	 * the holding pen - release it, then wait for it to flag
6962306a36Sopenharmony_ci	 * that it has been released by resetting spear_pen_release.
7062306a36Sopenharmony_ci	 *
7162306a36Sopenharmony_ci	 * Note that "spear_pen_release" is the hardware CPU ID, whereas
7262306a36Sopenharmony_ci	 * "cpu" is Linux's internal ID.
7362306a36Sopenharmony_ci	 */
7462306a36Sopenharmony_ci	spear_write_pen_release(cpu);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	timeout = jiffies + (1 * HZ);
7762306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
7862306a36Sopenharmony_ci		smp_rmb();
7962306a36Sopenharmony_ci		if (spear_pen_release == -1)
8062306a36Sopenharmony_ci			break;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		udelay(10);
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/*
8662306a36Sopenharmony_ci	 * now the secondary core is starting up let it run its
8762306a36Sopenharmony_ci	 * calibrations, then wait for it to finish
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	spin_unlock(&boot_lock);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return spear_pen_release != -1 ? -ENOSYS : 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * Initialise the CPU possible map early - this describes the CPUs
9662306a36Sopenharmony_ci * which may be present or become present in the system.
9762306a36Sopenharmony_ci */
9862306a36Sopenharmony_cistatic void __init spear13xx_smp_init_cpus(void)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	unsigned int i, ncores = scu_get_core_count(scu_base);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (ncores > nr_cpu_ids) {
10362306a36Sopenharmony_ci		pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
10462306a36Sopenharmony_ci			ncores, nr_cpu_ids);
10562306a36Sopenharmony_ci		ncores = nr_cpu_ids;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	for (i = 0; i < ncores; i++)
10962306a36Sopenharmony_ci		set_cpu_possible(i, true);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void __init spear13xx_smp_prepare_cpus(unsigned int max_cpus)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	scu_enable(scu_base);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/*
11862306a36Sopenharmony_ci	 * Write the address of secondary startup into the system-wide location
11962306a36Sopenharmony_ci	 * (presently it is in SRAM). The BootMonitor waits until it receives a
12062306a36Sopenharmony_ci	 * soft interrupt, and then the secondary CPU branches to this address.
12162306a36Sopenharmony_ci	 */
12262306a36Sopenharmony_ci	__raw_writel(__pa_symbol(spear13xx_secondary_startup), SYS_LOCATION);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ciconst struct smp_operations spear13xx_smp_ops __initconst = {
12662306a36Sopenharmony_ci       .smp_init_cpus		= spear13xx_smp_init_cpus,
12762306a36Sopenharmony_ci       .smp_prepare_cpus	= spear13xx_smp_prepare_cpus,
12862306a36Sopenharmony_ci       .smp_secondary_init	= spear13xx_secondary_init,
12962306a36Sopenharmony_ci       .smp_boot_secondary	= spear13xx_boot_secondary,
13062306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
13162306a36Sopenharmony_ci       .cpu_die			= spear13xx_cpu_die,
13262306a36Sopenharmony_ci#endif
13362306a36Sopenharmony_ci};
134