19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2aa7eb2bbSMichal Simek /* 3aa7eb2bbSMichal Simek * This file contains Xilinx specific SMP code, used to start up 4aa7eb2bbSMichal Simek * the second processor. 5aa7eb2bbSMichal Simek * 6aa7eb2bbSMichal Simek * Copyright (C) 2011-2013 Xilinx 7aa7eb2bbSMichal Simek * 8aa7eb2bbSMichal Simek * based on linux/arch/arm/mach-realview/platsmp.c 9aa7eb2bbSMichal Simek * 10aa7eb2bbSMichal Simek * Copyright (C) 2002 ARM Ltd. 11aa7eb2bbSMichal Simek */ 12aa7eb2bbSMichal Simek 13aa7eb2bbSMichal Simek #include <linux/export.h> 14aa7eb2bbSMichal Simek #include <linux/jiffies.h> 15aa7eb2bbSMichal Simek #include <linux/init.h> 16aa7eb2bbSMichal Simek #include <linux/io.h> 17aa7eb2bbSMichal Simek #include <asm/cacheflush.h> 18*6c6b3f1fSQuanyang Wang #include <asm/smp_plat.h> 19aa7eb2bbSMichal Simek #include <asm/smp_scu.h> 20aa7eb2bbSMichal Simek #include <linux/irqchip/arm-gic.h> 21aa7eb2bbSMichal Simek #include "common.h" 22aa7eb2bbSMichal Simek 23aa7eb2bbSMichal Simek /* 24aa7eb2bbSMichal Simek * Store number of cores in the system 25aa7eb2bbSMichal Simek * Because of scu_get_core_count() must be in __init section and can't 268bd26e3aSPaul Gortmaker * be called from zynq_cpun_start() because it is not in __init section. 27aa7eb2bbSMichal Simek */ 28aa7eb2bbSMichal Simek static int ncores; 29aa7eb2bbSMichal Simek 308bd26e3aSPaul Gortmaker int zynq_cpun_start(u32 address, int cpu) 31aa7eb2bbSMichal Simek { 32aa7eb2bbSMichal Simek u32 trampoline_code_size = &zynq_secondary_trampoline_end - 33aa7eb2bbSMichal Simek &zynq_secondary_trampoline; 34*6c6b3f1fSQuanyang Wang u32 phy_cpuid = cpu_logical_map(cpu); 35aa7eb2bbSMichal Simek 36aa7eb2bbSMichal Simek /* MS: Expectation that SLCR are directly map and accessible */ 37aa7eb2bbSMichal Simek /* Not possible to jump to non aligned address */ 38aa7eb2bbSMichal Simek if (!(address & 3) && (!address || (address >= trampoline_code_size))) { 39aa7eb2bbSMichal Simek /* Store pointer to ioremap area which points to address 0x0 */ 40aa7eb2bbSMichal Simek static u8 __iomem *zero; 41aa7eb2bbSMichal Simek u32 trampoline_size = &zynq_secondary_trampoline_jump - 42aa7eb2bbSMichal Simek &zynq_secondary_trampoline; 43aa7eb2bbSMichal Simek 44*6c6b3f1fSQuanyang Wang zynq_slcr_cpu_stop(phy_cpuid); 4588cd4e88SMichal Simek if (address) { 46aa7eb2bbSMichal Simek if (__pa(PAGE_OFFSET)) { 47aa7eb2bbSMichal Simek zero = ioremap(0, trampoline_code_size); 48aa7eb2bbSMichal Simek if (!zero) { 49aa7eb2bbSMichal Simek pr_warn("BOOTUP jump vectors not accessible\n"); 50aa7eb2bbSMichal Simek return -1; 51aa7eb2bbSMichal Simek } 52aa7eb2bbSMichal Simek } else { 53aa7eb2bbSMichal Simek zero = (__force u8 __iomem *)PAGE_OFFSET; 54aa7eb2bbSMichal Simek } 55aa7eb2bbSMichal Simek 56aa7eb2bbSMichal Simek /* 57aa7eb2bbSMichal Simek * This is elegant way how to jump to any address 58aa7eb2bbSMichal Simek * 0x0: Load address at 0x8 to r0 59aa7eb2bbSMichal Simek * 0x4: Jump by mov instruction 60aa7eb2bbSMichal Simek * 0x8: Jumping address 61aa7eb2bbSMichal Simek */ 62b7005d4eSLuis Araneda memcpy_toio(zero, &zynq_secondary_trampoline, 63aa7eb2bbSMichal Simek trampoline_size); 64aa7eb2bbSMichal Simek writel(address, zero + trampoline_size); 65aa7eb2bbSMichal Simek 66aa7eb2bbSMichal Simek flush_cache_all(); 67aa7eb2bbSMichal Simek outer_flush_range(0, trampoline_code_size); 68aa7eb2bbSMichal Simek smp_wmb(); 69aa7eb2bbSMichal Simek 70aa7eb2bbSMichal Simek if (__pa(PAGE_OFFSET)) 71aa7eb2bbSMichal Simek iounmap(zero); 7288cd4e88SMichal Simek } 73*6c6b3f1fSQuanyang Wang zynq_slcr_cpu_start(phy_cpuid); 74aa7eb2bbSMichal Simek 75aa7eb2bbSMichal Simek return 0; 76aa7eb2bbSMichal Simek } 77aa7eb2bbSMichal Simek 78aa7eb2bbSMichal Simek pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address); 79aa7eb2bbSMichal Simek 80aa7eb2bbSMichal Simek return -1; 81aa7eb2bbSMichal Simek } 82aa7eb2bbSMichal Simek EXPORT_SYMBOL(zynq_cpun_start); 83aa7eb2bbSMichal Simek 8402b4e275SRussell King static int zynq_boot_secondary(unsigned int cpu, struct task_struct *idle) 85aa7eb2bbSMichal Simek { 865f595063SLuis Araneda return zynq_cpun_start(__pa_symbol(secondary_startup_arm), cpu); 87aa7eb2bbSMichal Simek } 88aa7eb2bbSMichal Simek 89aa7eb2bbSMichal Simek /* 90aa7eb2bbSMichal Simek * Initialise the CPU possible map early - this describes the CPUs 91aa7eb2bbSMichal Simek * which may be present or become present in the system. 92aa7eb2bbSMichal Simek */ 93aa7eb2bbSMichal Simek static void __init zynq_smp_init_cpus(void) 94aa7eb2bbSMichal Simek { 95aa7eb2bbSMichal Simek int i; 96aa7eb2bbSMichal Simek 97aa7eb2bbSMichal Simek ncores = scu_get_core_count(zynq_scu_base); 98aa7eb2bbSMichal Simek 99aa7eb2bbSMichal Simek for (i = 0; i < ncores && i < CONFIG_NR_CPUS; i++) 100aa7eb2bbSMichal Simek set_cpu_possible(i, true); 101aa7eb2bbSMichal Simek } 102aa7eb2bbSMichal Simek 103aa7eb2bbSMichal Simek static void __init zynq_smp_prepare_cpus(unsigned int max_cpus) 104aa7eb2bbSMichal Simek { 105aa7eb2bbSMichal Simek scu_enable(zynq_scu_base); 106aa7eb2bbSMichal Simek } 107aa7eb2bbSMichal Simek 108ae88b85eSSoren Brinkmann /** 109ae88b85eSSoren Brinkmann * zynq_secondary_init - Initialize secondary CPU cores 110ae88b85eSSoren Brinkmann * @cpu: CPU that is initialized 111ae88b85eSSoren Brinkmann * 112ae88b85eSSoren Brinkmann * This function is in the hotplug path. Don't move it into the 113ae88b85eSSoren Brinkmann * init section!! 114ae88b85eSSoren Brinkmann */ 115ae88b85eSSoren Brinkmann static void zynq_secondary_init(unsigned int cpu) 116ae88b85eSSoren Brinkmann { 117ae88b85eSSoren Brinkmann zynq_core_pm_init(); 118ae88b85eSSoren Brinkmann } 119ae88b85eSSoren Brinkmann 120f1fd2fa6SMichal Simek #ifdef CONFIG_HOTPLUG_CPU 121f1fd2fa6SMichal Simek static int zynq_cpu_kill(unsigned cpu) 122f1fd2fa6SMichal Simek { 12350c7960aSSoren Brinkmann unsigned long timeout = jiffies + msecs_to_jiffies(50); 12450c7960aSSoren Brinkmann 12550c7960aSSoren Brinkmann while (zynq_slcr_cpu_state_read(cpu)) 12650c7960aSSoren Brinkmann if (time_after(jiffies, timeout)) 12750c7960aSSoren Brinkmann return 0; 12850c7960aSSoren Brinkmann 129f1fd2fa6SMichal Simek zynq_slcr_cpu_stop(cpu); 130f1fd2fa6SMichal Simek return 1; 131f1fd2fa6SMichal Simek } 132caf86a73SSoren Brinkmann 133ed62e330SSoren Brinkmann /** 134ed62e330SSoren Brinkmann * zynq_cpu_die - Let a CPU core die 135ed62e330SSoren Brinkmann * @cpu: Dying CPU 136caf86a73SSoren Brinkmann * 137ed62e330SSoren Brinkmann * Platform-specific code to shutdown a CPU. 138ed62e330SSoren Brinkmann * Called with IRQs disabled on the dying CPU. 139caf86a73SSoren Brinkmann */ 140ed62e330SSoren Brinkmann static void zynq_cpu_die(unsigned int cpu) 141caf86a73SSoren Brinkmann { 142caf86a73SSoren Brinkmann zynq_slcr_cpu_state_write(cpu, true); 143caf86a73SSoren Brinkmann 144caf86a73SSoren Brinkmann /* 145caf86a73SSoren Brinkmann * there is no power-control hardware on this platform, so all 146caf86a73SSoren Brinkmann * we can do is put the core into WFI; this is safe as the calling 147caf86a73SSoren Brinkmann * code will have already disabled interrupts 148caf86a73SSoren Brinkmann */ 149caf86a73SSoren Brinkmann for (;;) 150caf86a73SSoren Brinkmann cpu_do_idle(); 151caf86a73SSoren Brinkmann } 152f1fd2fa6SMichal Simek #endif 153f1fd2fa6SMichal Simek 15475305275SMasahiro Yamada const struct smp_operations zynq_smp_ops __initconst = { 155aa7eb2bbSMichal Simek .smp_init_cpus = zynq_smp_init_cpus, 156aa7eb2bbSMichal Simek .smp_prepare_cpus = zynq_smp_prepare_cpus, 157aa7eb2bbSMichal Simek .smp_boot_secondary = zynq_boot_secondary, 158ae88b85eSSoren Brinkmann .smp_secondary_init = zynq_secondary_init, 159c7c28b0fSMichal Simek #ifdef CONFIG_HOTPLUG_CPU 160ed62e330SSoren Brinkmann .cpu_die = zynq_cpu_die, 161f1fd2fa6SMichal Simek .cpu_kill = zynq_cpu_kill, 162c7c28b0fSMichal Simek #endif 163aa7eb2bbSMichal Simek }; 164