1aa7eb2bbSMichal Simek /* 2aa7eb2bbSMichal Simek * This file contains Xilinx specific SMP code, used to start up 3aa7eb2bbSMichal Simek * the second processor. 4aa7eb2bbSMichal Simek * 5aa7eb2bbSMichal Simek * Copyright (C) 2011-2013 Xilinx 6aa7eb2bbSMichal Simek * 7aa7eb2bbSMichal Simek * based on linux/arch/arm/mach-realview/platsmp.c 8aa7eb2bbSMichal Simek * 9aa7eb2bbSMichal Simek * Copyright (C) 2002 ARM Ltd. 10aa7eb2bbSMichal Simek * 11aa7eb2bbSMichal Simek * This software is licensed under the terms of the GNU General Public 12aa7eb2bbSMichal Simek * License version 2, as published by the Free Software Foundation, and 13aa7eb2bbSMichal Simek * may be copied, distributed, and modified under those terms. 14aa7eb2bbSMichal Simek * 15aa7eb2bbSMichal Simek * This program is distributed in the hope that it will be useful, 16aa7eb2bbSMichal Simek * but WITHOUT ANY WARRANTY; without even the implied warranty of 17aa7eb2bbSMichal Simek * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18aa7eb2bbSMichal Simek * GNU General Public License for more details. 19aa7eb2bbSMichal Simek */ 20aa7eb2bbSMichal Simek 21aa7eb2bbSMichal Simek #include <linux/export.h> 22aa7eb2bbSMichal Simek #include <linux/jiffies.h> 23aa7eb2bbSMichal Simek #include <linux/init.h> 24aa7eb2bbSMichal Simek #include <linux/io.h> 25aa7eb2bbSMichal Simek #include <asm/cacheflush.h> 26aa7eb2bbSMichal Simek #include <asm/smp_scu.h> 27aa7eb2bbSMichal Simek #include <linux/irqchip/arm-gic.h> 28aa7eb2bbSMichal Simek #include "common.h" 29aa7eb2bbSMichal Simek 30aa7eb2bbSMichal Simek /* 31aa7eb2bbSMichal Simek * Store number of cores in the system 32aa7eb2bbSMichal Simek * Because of scu_get_core_count() must be in __init section and can't 338bd26e3aSPaul Gortmaker * be called from zynq_cpun_start() because it is not in __init section. 34aa7eb2bbSMichal Simek */ 35aa7eb2bbSMichal Simek static int ncores; 36aa7eb2bbSMichal Simek 378bd26e3aSPaul Gortmaker int zynq_cpun_start(u32 address, int cpu) 38aa7eb2bbSMichal Simek { 39aa7eb2bbSMichal Simek u32 trampoline_code_size = &zynq_secondary_trampoline_end - 40aa7eb2bbSMichal Simek &zynq_secondary_trampoline; 41aa7eb2bbSMichal Simek 42aa7eb2bbSMichal Simek /* MS: Expectation that SLCR are directly map and accessible */ 43aa7eb2bbSMichal Simek /* Not possible to jump to non aligned address */ 44aa7eb2bbSMichal Simek if (!(address & 3) && (!address || (address >= trampoline_code_size))) { 45aa7eb2bbSMichal Simek /* Store pointer to ioremap area which points to address 0x0 */ 46aa7eb2bbSMichal Simek static u8 __iomem *zero; 47aa7eb2bbSMichal Simek u32 trampoline_size = &zynq_secondary_trampoline_jump - 48aa7eb2bbSMichal Simek &zynq_secondary_trampoline; 49aa7eb2bbSMichal Simek 50aa7eb2bbSMichal Simek zynq_slcr_cpu_stop(cpu); 5188cd4e88SMichal Simek if (address) { 52aa7eb2bbSMichal Simek if (__pa(PAGE_OFFSET)) { 53aa7eb2bbSMichal Simek zero = ioremap(0, trampoline_code_size); 54aa7eb2bbSMichal Simek if (!zero) { 55aa7eb2bbSMichal Simek pr_warn("BOOTUP jump vectors not accessible\n"); 56aa7eb2bbSMichal Simek return -1; 57aa7eb2bbSMichal Simek } 58aa7eb2bbSMichal Simek } else { 59aa7eb2bbSMichal Simek zero = (__force u8 __iomem *)PAGE_OFFSET; 60aa7eb2bbSMichal Simek } 61aa7eb2bbSMichal Simek 62aa7eb2bbSMichal Simek /* 63aa7eb2bbSMichal Simek * This is elegant way how to jump to any address 64aa7eb2bbSMichal Simek * 0x0: Load address at 0x8 to r0 65aa7eb2bbSMichal Simek * 0x4: Jump by mov instruction 66aa7eb2bbSMichal Simek * 0x8: Jumping address 67aa7eb2bbSMichal Simek */ 68aa7eb2bbSMichal Simek memcpy((__force void *)zero, &zynq_secondary_trampoline, 69aa7eb2bbSMichal Simek trampoline_size); 70aa7eb2bbSMichal Simek writel(address, zero + trampoline_size); 71aa7eb2bbSMichal Simek 72aa7eb2bbSMichal Simek flush_cache_all(); 73aa7eb2bbSMichal Simek outer_flush_range(0, trampoline_code_size); 74aa7eb2bbSMichal Simek smp_wmb(); 75aa7eb2bbSMichal Simek 76aa7eb2bbSMichal Simek if (__pa(PAGE_OFFSET)) 77aa7eb2bbSMichal Simek iounmap(zero); 7888cd4e88SMichal Simek } 79aa7eb2bbSMichal Simek zynq_slcr_cpu_start(cpu); 80aa7eb2bbSMichal Simek 81aa7eb2bbSMichal Simek return 0; 82aa7eb2bbSMichal Simek } 83aa7eb2bbSMichal Simek 84aa7eb2bbSMichal Simek pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address); 85aa7eb2bbSMichal Simek 86aa7eb2bbSMichal Simek return -1; 87aa7eb2bbSMichal Simek } 88aa7eb2bbSMichal Simek EXPORT_SYMBOL(zynq_cpun_start); 89aa7eb2bbSMichal Simek 90*02b4e275SRussell King static int zynq_boot_secondary(unsigned int cpu, struct task_struct *idle) 91aa7eb2bbSMichal Simek { 92*02b4e275SRussell King return zynq_cpun_start(virt_to_phys(secondary_startup), cpu); 93aa7eb2bbSMichal Simek } 94aa7eb2bbSMichal Simek 95aa7eb2bbSMichal Simek /* 96aa7eb2bbSMichal Simek * Initialise the CPU possible map early - this describes the CPUs 97aa7eb2bbSMichal Simek * which may be present or become present in the system. 98aa7eb2bbSMichal Simek */ 99aa7eb2bbSMichal Simek static void __init zynq_smp_init_cpus(void) 100aa7eb2bbSMichal Simek { 101aa7eb2bbSMichal Simek int i; 102aa7eb2bbSMichal Simek 103aa7eb2bbSMichal Simek ncores = scu_get_core_count(zynq_scu_base); 104aa7eb2bbSMichal Simek 105aa7eb2bbSMichal Simek for (i = 0; i < ncores && i < CONFIG_NR_CPUS; i++) 106aa7eb2bbSMichal Simek set_cpu_possible(i, true); 107aa7eb2bbSMichal Simek } 108aa7eb2bbSMichal Simek 109aa7eb2bbSMichal Simek static void __init zynq_smp_prepare_cpus(unsigned int max_cpus) 110aa7eb2bbSMichal Simek { 111aa7eb2bbSMichal Simek scu_enable(zynq_scu_base); 112aa7eb2bbSMichal Simek } 113aa7eb2bbSMichal Simek 114ae88b85eSSoren Brinkmann /** 115ae88b85eSSoren Brinkmann * zynq_secondary_init - Initialize secondary CPU cores 116ae88b85eSSoren Brinkmann * @cpu: CPU that is initialized 117ae88b85eSSoren Brinkmann * 118ae88b85eSSoren Brinkmann * This function is in the hotplug path. Don't move it into the 119ae88b85eSSoren Brinkmann * init section!! 120ae88b85eSSoren Brinkmann */ 121ae88b85eSSoren Brinkmann static void zynq_secondary_init(unsigned int cpu) 122ae88b85eSSoren Brinkmann { 123ae88b85eSSoren Brinkmann zynq_core_pm_init(); 124ae88b85eSSoren Brinkmann } 125ae88b85eSSoren Brinkmann 126f1fd2fa6SMichal Simek #ifdef CONFIG_HOTPLUG_CPU 127f1fd2fa6SMichal Simek static int zynq_cpu_kill(unsigned cpu) 128f1fd2fa6SMichal Simek { 12950c7960aSSoren Brinkmann unsigned long timeout = jiffies + msecs_to_jiffies(50); 13050c7960aSSoren Brinkmann 13150c7960aSSoren Brinkmann while (zynq_slcr_cpu_state_read(cpu)) 13250c7960aSSoren Brinkmann if (time_after(jiffies, timeout)) 13350c7960aSSoren Brinkmann return 0; 13450c7960aSSoren Brinkmann 135f1fd2fa6SMichal Simek zynq_slcr_cpu_stop(cpu); 136f1fd2fa6SMichal Simek return 1; 137f1fd2fa6SMichal Simek } 138caf86a73SSoren Brinkmann 139ed62e330SSoren Brinkmann /** 140ed62e330SSoren Brinkmann * zynq_cpu_die - Let a CPU core die 141ed62e330SSoren Brinkmann * @cpu: Dying CPU 142caf86a73SSoren Brinkmann * 143ed62e330SSoren Brinkmann * Platform-specific code to shutdown a CPU. 144ed62e330SSoren Brinkmann * Called with IRQs disabled on the dying CPU. 145caf86a73SSoren Brinkmann */ 146ed62e330SSoren Brinkmann static void zynq_cpu_die(unsigned int cpu) 147caf86a73SSoren Brinkmann { 148caf86a73SSoren Brinkmann zynq_slcr_cpu_state_write(cpu, true); 149caf86a73SSoren Brinkmann 150caf86a73SSoren Brinkmann /* 151caf86a73SSoren Brinkmann * there is no power-control hardware on this platform, so all 152caf86a73SSoren Brinkmann * we can do is put the core into WFI; this is safe as the calling 153caf86a73SSoren Brinkmann * code will have already disabled interrupts 154caf86a73SSoren Brinkmann */ 155caf86a73SSoren Brinkmann for (;;) 156caf86a73SSoren Brinkmann cpu_do_idle(); 157caf86a73SSoren Brinkmann } 158f1fd2fa6SMichal Simek #endif 159f1fd2fa6SMichal Simek 160aa7eb2bbSMichal Simek struct smp_operations zynq_smp_ops __initdata = { 161aa7eb2bbSMichal Simek .smp_init_cpus = zynq_smp_init_cpus, 162aa7eb2bbSMichal Simek .smp_prepare_cpus = zynq_smp_prepare_cpus, 163aa7eb2bbSMichal Simek .smp_boot_secondary = zynq_boot_secondary, 164ae88b85eSSoren Brinkmann .smp_secondary_init = zynq_secondary_init, 165c7c28b0fSMichal Simek #ifdef CONFIG_HOTPLUG_CPU 166ed62e330SSoren Brinkmann .cpu_die = zynq_cpu_die, 167f1fd2fa6SMichal Simek .cpu_kill = zynq_cpu_kill, 168c7c28b0fSMichal Simek #endif 169aa7eb2bbSMichal Simek }; 170