162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 2002 ARM Ltd.
462306a36Sopenharmony_ci *  All Rights Reserved
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This code is specific to the hardware found on ARM Realview and
762306a36Sopenharmony_ci * Versatile Express platforms where the CPUs are unable to be individually
862306a36Sopenharmony_ci * woken, and where there is no way to hot-unplug CPUs.  Real platforms
962306a36Sopenharmony_ci * should not copy this code.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/errno.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/jiffies.h>
1662306a36Sopenharmony_ci#include <linux/smp.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <asm/cacheflush.h>
1962306a36Sopenharmony_ci#include <asm/smp_plat.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "platsmp.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * versatile_cpu_release controls the release of CPUs from the holding
2562306a36Sopenharmony_ci * pen in headsmp.S, which exists because we are not always able to
2662306a36Sopenharmony_ci * control the release of individual CPUs from the board firmware.
2762306a36Sopenharmony_ci * Production platforms do not need this.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_civolatile int versatile_cpu_release = -1;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/*
3262306a36Sopenharmony_ci * Write versatile_cpu_release in a way that is guaranteed to be visible to
3362306a36Sopenharmony_ci * all observers, irrespective of whether they're taking part in coherency
3462306a36Sopenharmony_ci * or not.  This is necessary for the hotplug code to work reliably.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_cistatic void versatile_write_cpu_release(int val)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	versatile_cpu_release = val;
3962306a36Sopenharmony_ci	smp_wmb();
4062306a36Sopenharmony_ci	sync_cache_w(&versatile_cpu_release);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*
4462306a36Sopenharmony_ci * versatile_lock exists to avoid running the loops_per_jiffy delay loop
4562306a36Sopenharmony_ci * calibrations on the secondary CPU while the requesting CPU is using
4662306a36Sopenharmony_ci * the limited-bandwidth bus - which affects the calibration value.
4762306a36Sopenharmony_ci * Production platforms do not need this.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(versatile_lock);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_civoid versatile_secondary_init(unsigned int cpu)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	/*
5462306a36Sopenharmony_ci	 * let the primary processor know we're out of the
5562306a36Sopenharmony_ci	 * pen, then head off into the C entry point
5662306a36Sopenharmony_ci	 */
5762306a36Sopenharmony_ci	versatile_write_cpu_release(-1);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/*
6062306a36Sopenharmony_ci	 * Synchronise with the boot thread.
6162306a36Sopenharmony_ci	 */
6262306a36Sopenharmony_ci	raw_spin_lock(&versatile_lock);
6362306a36Sopenharmony_ci	raw_spin_unlock(&versatile_lock);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciint versatile_boot_secondary(unsigned int cpu, struct task_struct *idle)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	unsigned long timeout;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/*
7162306a36Sopenharmony_ci	 * Set synchronisation state between this boot processor
7262306a36Sopenharmony_ci	 * and the secondary one
7362306a36Sopenharmony_ci	 */
7462306a36Sopenharmony_ci	raw_spin_lock(&versatile_lock);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/*
7762306a36Sopenharmony_ci	 * This is really belt and braces; we hold unintended secondary
7862306a36Sopenharmony_ci	 * CPUs in the holding pen until we're ready for them.  However,
7962306a36Sopenharmony_ci	 * since we haven't sent them a soft interrupt, they shouldn't
8062306a36Sopenharmony_ci	 * be there.
8162306a36Sopenharmony_ci	 */
8262306a36Sopenharmony_ci	versatile_write_cpu_release(cpu_logical_map(cpu));
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/*
8562306a36Sopenharmony_ci	 * Send the secondary CPU a soft interrupt, thereby causing
8662306a36Sopenharmony_ci	 * the boot monitor to read the system wide flags register,
8762306a36Sopenharmony_ci	 * and branch to the address found there.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	timeout = jiffies + (1 * HZ);
9262306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
9362306a36Sopenharmony_ci		smp_rmb();
9462306a36Sopenharmony_ci		if (versatile_cpu_release == -1)
9562306a36Sopenharmony_ci			break;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		udelay(10);
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/*
10162306a36Sopenharmony_ci	 * now the secondary core is starting up let it run its
10262306a36Sopenharmony_ci	 * calibrations, then wait for it to finish
10362306a36Sopenharmony_ci	 */
10462306a36Sopenharmony_ci	raw_spin_unlock(&versatile_lock);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return versatile_cpu_release != -1 ? -ENOSYS : 0;
10762306a36Sopenharmony_ci}
108