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