162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file contains Xilinx specific SMP code, used to start up 462306a36Sopenharmony_ci * the second processor. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2011-2013 Xilinx 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * based on linux/arch/arm/mach-realview/platsmp.c 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 2002 ARM Ltd. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <linux/jiffies.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <asm/cacheflush.h> 1862306a36Sopenharmony_ci#include <asm/smp_plat.h> 1962306a36Sopenharmony_ci#include <asm/smp_scu.h> 2062306a36Sopenharmony_ci#include <linux/irqchip/arm-gic.h> 2162306a36Sopenharmony_ci#include "common.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * Store number of cores in the system 2562306a36Sopenharmony_ci * Because of scu_get_core_count() must be in __init section and can't 2662306a36Sopenharmony_ci * be called from zynq_cpun_start() because it is not in __init section. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistatic int ncores; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciint zynq_cpun_start(u32 address, int cpu) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci u32 trampoline_code_size = &zynq_secondary_trampoline_end - 3362306a36Sopenharmony_ci &zynq_secondary_trampoline; 3462306a36Sopenharmony_ci u32 phy_cpuid = cpu_logical_map(cpu); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* MS: Expectation that SLCR are directly map and accessible */ 3762306a36Sopenharmony_ci /* Not possible to jump to non aligned address */ 3862306a36Sopenharmony_ci if (!(address & 3) && (!address || (address >= trampoline_code_size))) { 3962306a36Sopenharmony_ci /* Store pointer to ioremap area which points to address 0x0 */ 4062306a36Sopenharmony_ci static u8 __iomem *zero; 4162306a36Sopenharmony_ci u32 trampoline_size = &zynq_secondary_trampoline_jump - 4262306a36Sopenharmony_ci &zynq_secondary_trampoline; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci zynq_slcr_cpu_stop(phy_cpuid); 4562306a36Sopenharmony_ci if (address) { 4662306a36Sopenharmony_ci if (__pa(PAGE_OFFSET)) { 4762306a36Sopenharmony_ci zero = ioremap(0, trampoline_code_size); 4862306a36Sopenharmony_ci if (!zero) { 4962306a36Sopenharmony_ci pr_warn("BOOTUP jump vectors not accessible\n"); 5062306a36Sopenharmony_ci return -1; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci } else { 5362306a36Sopenharmony_ci zero = (__force u8 __iomem *)PAGE_OFFSET; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* 5762306a36Sopenharmony_ci * This is elegant way how to jump to any address 5862306a36Sopenharmony_ci * 0x0: Load address at 0x8 to r0 5962306a36Sopenharmony_ci * 0x4: Jump by mov instruction 6062306a36Sopenharmony_ci * 0x8: Jumping address 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci memcpy_toio(zero, &zynq_secondary_trampoline, 6362306a36Sopenharmony_ci trampoline_size); 6462306a36Sopenharmony_ci writel(address, zero + trampoline_size); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci flush_cache_all(); 6762306a36Sopenharmony_ci outer_flush_range(0, trampoline_code_size); 6862306a36Sopenharmony_ci smp_wmb(); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (__pa(PAGE_OFFSET)) 7162306a36Sopenharmony_ci iounmap(zero); 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci zynq_slcr_cpu_start(phy_cpuid); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return -1; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ciEXPORT_SYMBOL(zynq_cpun_start); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int zynq_boot_secondary(unsigned int cpu, struct task_struct *idle) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci return zynq_cpun_start(__pa_symbol(secondary_startup_arm), cpu); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * Initialise the CPU possible map early - this describes the CPUs 9162306a36Sopenharmony_ci * which may be present or become present in the system. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_cistatic void __init zynq_smp_init_cpus(void) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci int i; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ncores = scu_get_core_count(zynq_scu_base); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci for (i = 0; i < ncores && i < CONFIG_NR_CPUS; i++) 10062306a36Sopenharmony_ci set_cpu_possible(i, true); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void __init zynq_smp_prepare_cpus(unsigned int max_cpus) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci scu_enable(zynq_scu_base); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/** 10962306a36Sopenharmony_ci * zynq_secondary_init - Initialize secondary CPU cores 11062306a36Sopenharmony_ci * @cpu: CPU that is initialized 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * This function is in the hotplug path. Don't move it into the 11362306a36Sopenharmony_ci * init section!! 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_cistatic void zynq_secondary_init(unsigned int cpu) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci zynq_core_pm_init(); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 12162306a36Sopenharmony_cistatic int zynq_cpu_kill(unsigned cpu) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(50); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci while (zynq_slcr_cpu_state_read(cpu)) 12662306a36Sopenharmony_ci if (time_after(jiffies, timeout)) 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci zynq_slcr_cpu_stop(cpu); 13062306a36Sopenharmony_ci return 1; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/** 13462306a36Sopenharmony_ci * zynq_cpu_die - Let a CPU core die 13562306a36Sopenharmony_ci * @cpu: Dying CPU 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * Platform-specific code to shutdown a CPU. 13862306a36Sopenharmony_ci * Called with IRQs disabled on the dying CPU. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic void zynq_cpu_die(unsigned int cpu) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci zynq_slcr_cpu_state_write(cpu, true); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * there is no power-control hardware on this platform, so all 14662306a36Sopenharmony_ci * we can do is put the core into WFI; this is safe as the calling 14762306a36Sopenharmony_ci * code will have already disabled interrupts 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci for (;;) 15062306a36Sopenharmony_ci cpu_do_idle(); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci#endif 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciconst struct smp_operations zynq_smp_ops __initconst = { 15562306a36Sopenharmony_ci .smp_init_cpus = zynq_smp_init_cpus, 15662306a36Sopenharmony_ci .smp_prepare_cpus = zynq_smp_prepare_cpus, 15762306a36Sopenharmony_ci .smp_boot_secondary = zynq_boot_secondary, 15862306a36Sopenharmony_ci .smp_secondary_init = zynq_secondary_init, 15962306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 16062306a36Sopenharmony_ci .cpu_die = zynq_cpu_die, 16162306a36Sopenharmony_ci .cpu_kill = zynq_cpu_kill, 16262306a36Sopenharmony_ci#endif 16362306a36Sopenharmony_ci}; 164