18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * arch/arm/mach-spear13xx/platsmp.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * based upon linux/arch/arm/mach-realview/platsmp.c
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2012 ST Microelectronics Ltd.
88c2ecf20Sopenharmony_ci * Shiraz Hashim <shiraz.linux.kernel@gmail.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/smp.h>
158c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
168c2ecf20Sopenharmony_ci#include <asm/smp_scu.h>
178c2ecf20Sopenharmony_ci#include <mach/spear.h>
188c2ecf20Sopenharmony_ci#include "generic.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* XXX spear_pen_release is cargo culted code - DO NOT COPY XXX */
218c2ecf20Sopenharmony_civolatile int spear_pen_release = -1;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * XXX CARGO CULTED CODE - DO NOT COPY XXX
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * Write spear_pen_release in a way that is guaranteed to be visible to
278c2ecf20Sopenharmony_ci * all observers, irrespective of whether they're taking part in coherency
288c2ecf20Sopenharmony_ci * or not.  This is necessary for the hotplug code to work reliably.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_cistatic void spear_write_pen_release(int val)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	spear_pen_release = val;
338c2ecf20Sopenharmony_ci	smp_wmb();
348c2ecf20Sopenharmony_ci	sync_cache_w(&spear_pen_release);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(boot_lock);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void __iomem *scu_base = IOMEM(VA_SCU_BASE);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void spear13xx_secondary_init(unsigned int cpu)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	/*
448c2ecf20Sopenharmony_ci	 * let the primary processor know we're out of the
458c2ecf20Sopenharmony_ci	 * pen, then head off into the C entry point
468c2ecf20Sopenharmony_ci	 */
478c2ecf20Sopenharmony_ci	spear_write_pen_release(-1);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/*
508c2ecf20Sopenharmony_ci	 * Synchronise with the boot thread.
518c2ecf20Sopenharmony_ci	 */
528c2ecf20Sopenharmony_ci	spin_lock(&boot_lock);
538c2ecf20Sopenharmony_ci	spin_unlock(&boot_lock);
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int spear13xx_boot_secondary(unsigned int cpu, struct task_struct *idle)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	unsigned long timeout;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	/*
618c2ecf20Sopenharmony_ci	 * set synchronisation state between this boot processor
628c2ecf20Sopenharmony_ci	 * and the secondary one
638c2ecf20Sopenharmony_ci	 */
648c2ecf20Sopenharmony_ci	spin_lock(&boot_lock);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/*
678c2ecf20Sopenharmony_ci	 * The secondary processor is waiting to be released from
688c2ecf20Sopenharmony_ci	 * the holding pen - release it, then wait for it to flag
698c2ecf20Sopenharmony_ci	 * that it has been released by resetting spear_pen_release.
708c2ecf20Sopenharmony_ci	 *
718c2ecf20Sopenharmony_ci	 * Note that "spear_pen_release" is the hardware CPU ID, whereas
728c2ecf20Sopenharmony_ci	 * "cpu" is Linux's internal ID.
738c2ecf20Sopenharmony_ci	 */
748c2ecf20Sopenharmony_ci	spear_write_pen_release(cpu);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	timeout = jiffies + (1 * HZ);
778c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
788c2ecf20Sopenharmony_ci		smp_rmb();
798c2ecf20Sopenharmony_ci		if (spear_pen_release == -1)
808c2ecf20Sopenharmony_ci			break;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		udelay(10);
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/*
868c2ecf20Sopenharmony_ci	 * now the secondary core is starting up let it run its
878c2ecf20Sopenharmony_ci	 * calibrations, then wait for it to finish
888c2ecf20Sopenharmony_ci	 */
898c2ecf20Sopenharmony_ci	spin_unlock(&boot_lock);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return spear_pen_release != -1 ? -ENOSYS : 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/*
958c2ecf20Sopenharmony_ci * Initialise the CPU possible map early - this describes the CPUs
968c2ecf20Sopenharmony_ci * which may be present or become present in the system.
978c2ecf20Sopenharmony_ci */
988c2ecf20Sopenharmony_cistatic void __init spear13xx_smp_init_cpus(void)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	unsigned int i, ncores = scu_get_core_count(scu_base);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (ncores > nr_cpu_ids) {
1038c2ecf20Sopenharmony_ci		pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
1048c2ecf20Sopenharmony_ci			ncores, nr_cpu_ids);
1058c2ecf20Sopenharmony_ci		ncores = nr_cpu_ids;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	for (i = 0; i < ncores; i++)
1098c2ecf20Sopenharmony_ci		set_cpu_possible(i, true);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void __init spear13xx_smp_prepare_cpus(unsigned int max_cpus)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	scu_enable(scu_base);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/*
1188c2ecf20Sopenharmony_ci	 * Write the address of secondary startup into the system-wide location
1198c2ecf20Sopenharmony_ci	 * (presently it is in SRAM). The BootMonitor waits until it receives a
1208c2ecf20Sopenharmony_ci	 * soft interrupt, and then the secondary CPU branches to this address.
1218c2ecf20Sopenharmony_ci	 */
1228c2ecf20Sopenharmony_ci	__raw_writel(__pa_symbol(spear13xx_secondary_startup), SYS_LOCATION);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ciconst struct smp_operations spear13xx_smp_ops __initconst = {
1268c2ecf20Sopenharmony_ci       .smp_init_cpus		= spear13xx_smp_init_cpus,
1278c2ecf20Sopenharmony_ci       .smp_prepare_cpus	= spear13xx_smp_prepare_cpus,
1288c2ecf20Sopenharmony_ci       .smp_secondary_init	= spear13xx_secondary_init,
1298c2ecf20Sopenharmony_ci       .smp_boot_secondary	= spear13xx_boot_secondary,
1308c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
1318c2ecf20Sopenharmony_ci       .cpu_die			= spear13xx_cpu_die,
1328c2ecf20Sopenharmony_ci#endif
1338c2ecf20Sopenharmony_ci};
134