162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2004-2008, 2009, 2010 Cavium Networks 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/cpu.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/smp.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/kernel_stat.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/sched/hotplug.h> 1562306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/export.h> 1862306a36Sopenharmony_ci#include <linux/kexec.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/mmu_context.h> 2162306a36Sopenharmony_ci#include <asm/time.h> 2262306a36Sopenharmony_ci#include <asm/setup.h> 2362306a36Sopenharmony_ci#include <asm/smp.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <asm/octeon/octeon.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "octeon_boot.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_civolatile unsigned long octeon_processor_boot = 0xff; 3062306a36Sopenharmony_civolatile unsigned long octeon_processor_sp; 3162306a36Sopenharmony_civolatile unsigned long octeon_processor_gp; 3262306a36Sopenharmony_ci#ifdef CONFIG_RELOCATABLE 3362306a36Sopenharmony_civolatile unsigned long octeon_processor_relocated_kernel_entry; 3462306a36Sopenharmony_ci#endif /* CONFIG_RELOCATABLE */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 3762306a36Sopenharmony_ciuint64_t octeon_bootloader_entry_addr; 3862306a36Sopenharmony_ciEXPORT_SYMBOL(octeon_bootloader_entry_addr); 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ciextern void kernel_entry(unsigned long arg1, ...); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void octeon_icache_flush(void) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci asm volatile ("synci 0($0)\n"); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void (*octeon_message_functions[8])(void) = { 4962306a36Sopenharmony_ci scheduler_ipi, 5062306a36Sopenharmony_ci generic_smp_call_function_interrupt, 5162306a36Sopenharmony_ci octeon_icache_flush, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic irqreturn_t mailbox_interrupt(int irq, void *dev_id) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci u64 mbox_clrx = CVMX_CIU_MBOX_CLRX(cvmx_get_core_num()); 5762306a36Sopenharmony_ci u64 action; 5862306a36Sopenharmony_ci int i; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* 6162306a36Sopenharmony_ci * Make sure the function array initialization remains 6262306a36Sopenharmony_ci * correct. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci BUILD_BUG_ON(SMP_RESCHEDULE_YOURSELF != (1 << 0)); 6562306a36Sopenharmony_ci BUILD_BUG_ON(SMP_CALL_FUNCTION != (1 << 1)); 6662306a36Sopenharmony_ci BUILD_BUG_ON(SMP_ICACHE_FLUSH != (1 << 2)); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Load the mailbox register to figure out what we're supposed 7062306a36Sopenharmony_ci * to do. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci action = cvmx_read_csr(mbox_clrx); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN68XX)) 7562306a36Sopenharmony_ci action &= 0xff; 7662306a36Sopenharmony_ci else 7762306a36Sopenharmony_ci action &= 0xffff; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Clear the mailbox to clear the interrupt */ 8062306a36Sopenharmony_ci cvmx_write_csr(mbox_clrx, action); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(octeon_message_functions) && action;) { 8362306a36Sopenharmony_ci if (action & 1) { 8462306a36Sopenharmony_ci void (*fn)(void) = octeon_message_functions[i]; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (fn) 8762306a36Sopenharmony_ci fn(); 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci action >>= 1; 9062306a36Sopenharmony_ci i++; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci return IRQ_HANDLED; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* 9662306a36Sopenharmony_ci * Cause the function described by call_data to be executed on the passed 9762306a36Sopenharmony_ci * cpu. When the function has finished, increment the finished field of 9862306a36Sopenharmony_ci * call_data. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_civoid octeon_send_ipi_single(int cpu, unsigned int action) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci int coreid = cpu_logical_map(cpu); 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci pr_info("SMP: Mailbox send cpu=%d, coreid=%d, action=%u\n", cpu, 10562306a36Sopenharmony_ci coreid, action); 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci cvmx_write_csr(CVMX_CIU_MBOX_SETX(coreid), action); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic inline void octeon_send_ipi_mask(const struct cpumask *mask, 11162306a36Sopenharmony_ci unsigned int action) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci unsigned int i; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci for_each_cpu(i, mask) 11662306a36Sopenharmony_ci octeon_send_ipi_single(i, action); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* 12062306a36Sopenharmony_ci * Detect available CPUs, populate cpu_possible_mask 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistatic void octeon_smp_hotplug_setup(void) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 12562306a36Sopenharmony_ci struct linux_app_boot_info *labi; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (!setup_max_cpus) 12862306a36Sopenharmony_ci return; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci labi = (struct linux_app_boot_info *)PHYS_TO_XKSEG_CACHED(LABI_ADDR_IN_BOOTLOADER); 13162306a36Sopenharmony_ci if (labi->labi_signature != LABI_SIGNATURE) { 13262306a36Sopenharmony_ci pr_info("The bootloader on this board does not support HOTPLUG_CPU."); 13362306a36Sopenharmony_ci return; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci octeon_bootloader_entry_addr = labi->InitTLBStart_addr; 13762306a36Sopenharmony_ci#endif 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void __init octeon_smp_setup(void) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci const int coreid = cvmx_get_core_num(); 14362306a36Sopenharmony_ci int cpus; 14462306a36Sopenharmony_ci int id; 14562306a36Sopenharmony_ci struct cvmx_sysinfo *sysinfo = cvmx_sysinfo_get(); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 14862306a36Sopenharmony_ci int core_mask = octeon_get_boot_coremask(); 14962306a36Sopenharmony_ci unsigned int num_cores = cvmx_octeon_num_cores(); 15062306a36Sopenharmony_ci#endif 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* The present CPUs are initially just the boot cpu (CPU 0). */ 15362306a36Sopenharmony_ci for (id = 0; id < NR_CPUS; id++) { 15462306a36Sopenharmony_ci set_cpu_possible(id, id == 0); 15562306a36Sopenharmony_ci set_cpu_present(id, id == 0); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci __cpu_number_map[coreid] = 0; 15962306a36Sopenharmony_ci __cpu_logical_map[0] = coreid; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* The present CPUs get the lowest CPU numbers. */ 16262306a36Sopenharmony_ci cpus = 1; 16362306a36Sopenharmony_ci for (id = 0; id < NR_CPUS; id++) { 16462306a36Sopenharmony_ci if ((id != coreid) && cvmx_coremask_is_core_set(&sysinfo->core_mask, id)) { 16562306a36Sopenharmony_ci set_cpu_possible(cpus, true); 16662306a36Sopenharmony_ci set_cpu_present(cpus, true); 16762306a36Sopenharmony_ci __cpu_number_map[id] = cpus; 16862306a36Sopenharmony_ci __cpu_logical_map[cpus] = id; 16962306a36Sopenharmony_ci cpus++; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 17462306a36Sopenharmony_ci /* 17562306a36Sopenharmony_ci * The possible CPUs are all those present on the chip. We 17662306a36Sopenharmony_ci * will assign CPU numbers for possible cores as well. Cores 17762306a36Sopenharmony_ci * are always consecutively numberd from 0. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci for (id = 0; setup_max_cpus && octeon_bootloader_entry_addr && 18062306a36Sopenharmony_ci id < num_cores && id < NR_CPUS; id++) { 18162306a36Sopenharmony_ci if (!(core_mask & (1 << id))) { 18262306a36Sopenharmony_ci set_cpu_possible(cpus, true); 18362306a36Sopenharmony_ci __cpu_number_map[id] = cpus; 18462306a36Sopenharmony_ci __cpu_logical_map[cpus] = id; 18562306a36Sopenharmony_ci cpus++; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci#endif 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci octeon_smp_hotplug_setup(); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci#ifdef CONFIG_RELOCATABLE 19562306a36Sopenharmony_ciint plat_post_relocation(long offset) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci unsigned long entry = (unsigned long)kernel_entry; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Send secondaries into relocated kernel */ 20062306a36Sopenharmony_ci octeon_processor_relocated_kernel_entry = entry + offset; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci#endif /* CONFIG_RELOCATABLE */ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* 20762306a36Sopenharmony_ci * Firmware CPU startup hook 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_cistatic int octeon_boot_secondary(int cpu, struct task_struct *idle) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci int count; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci pr_info("SMP: Booting CPU%02d (CoreId %2d)...\n", cpu, 21462306a36Sopenharmony_ci cpu_logical_map(cpu)); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci octeon_processor_sp = __KSTK_TOS(idle); 21762306a36Sopenharmony_ci octeon_processor_gp = (unsigned long)(task_thread_info(idle)); 21862306a36Sopenharmony_ci octeon_processor_boot = cpu_logical_map(cpu); 21962306a36Sopenharmony_ci mb(); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci count = 10000; 22262306a36Sopenharmony_ci while (octeon_processor_sp && count) { 22362306a36Sopenharmony_ci /* Waiting for processor to get the SP and GP */ 22462306a36Sopenharmony_ci udelay(1); 22562306a36Sopenharmony_ci count--; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci if (count == 0) { 22862306a36Sopenharmony_ci pr_err("Secondary boot timeout\n"); 22962306a36Sopenharmony_ci return -ETIMEDOUT; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* 23662306a36Sopenharmony_ci * After we've done initial boot, this function is called to allow the 23762306a36Sopenharmony_ci * board code to clean up state, if needed 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_cistatic void octeon_init_secondary(void) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci unsigned int sr; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci sr = set_c0_status(ST0_BEV); 24462306a36Sopenharmony_ci write_c0_ebase((u32)ebase); 24562306a36Sopenharmony_ci write_c0_status(sr); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci octeon_check_cpu_bist(); 24862306a36Sopenharmony_ci octeon_init_cvmcount(); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci octeon_irq_setup_secondary(); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* 25462306a36Sopenharmony_ci * Callout to firmware before smp_init 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_cistatic void __init octeon_prepare_cpus(unsigned int max_cpus) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * Only the low order mailbox bits are used for IPIs, leave 26062306a36Sopenharmony_ci * the other bits alone. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci cvmx_write_csr(CVMX_CIU_MBOX_CLRX(cvmx_get_core_num()), 0xffff); 26362306a36Sopenharmony_ci if (request_irq(OCTEON_IRQ_MBOX0, mailbox_interrupt, 26462306a36Sopenharmony_ci IRQF_PERCPU | IRQF_NO_THREAD, "SMP-IPI", 26562306a36Sopenharmony_ci mailbox_interrupt)) { 26662306a36Sopenharmony_ci panic("Cannot request_irq(OCTEON_IRQ_MBOX0)"); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * Last chance for the board code to finish SMP initialization before 27262306a36Sopenharmony_ci * the CPU is "online". 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_cistatic void octeon_smp_finish(void) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci octeon_user_io_init(); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* to generate the first CPU timer interrupt */ 27962306a36Sopenharmony_ci write_c0_compare(read_c0_count() + mips_hpt_frequency / HZ); 28062306a36Sopenharmony_ci local_irq_enable(); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* State of each CPU. */ 28662306a36Sopenharmony_cistatic DEFINE_PER_CPU(int, cpu_state); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int octeon_cpu_disable(void) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci unsigned int cpu = smp_processor_id(); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (!octeon_bootloader_entry_addr) 29362306a36Sopenharmony_ci return -ENOTSUPP; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci set_cpu_online(cpu, false); 29662306a36Sopenharmony_ci calculate_cpu_foreign_map(); 29762306a36Sopenharmony_ci octeon_fixup_irqs(); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci __flush_cache_all(); 30062306a36Sopenharmony_ci local_flush_tlb_all(); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic void octeon_cpu_die(unsigned int cpu) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci int coreid = cpu_logical_map(cpu); 30862306a36Sopenharmony_ci uint32_t mask, new_mask; 30962306a36Sopenharmony_ci const struct cvmx_bootmem_named_block_desc *block_desc; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci while (per_cpu(cpu_state, cpu) != CPU_DEAD) 31262306a36Sopenharmony_ci cpu_relax(); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* 31562306a36Sopenharmony_ci * This is a bit complicated strategics of getting/settig available 31662306a36Sopenharmony_ci * cores mask, copied from bootloader 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci mask = 1 << coreid; 32062306a36Sopenharmony_ci /* LINUX_APP_BOOT_BLOCK is initialized in bootoct binary */ 32162306a36Sopenharmony_ci block_desc = cvmx_bootmem_find_named_block(LINUX_APP_BOOT_BLOCK_NAME); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (!block_desc) { 32462306a36Sopenharmony_ci struct linux_app_boot_info *labi; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci labi = (struct linux_app_boot_info *)PHYS_TO_XKSEG_CACHED(LABI_ADDR_IN_BOOTLOADER); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci labi->avail_coremask |= mask; 32962306a36Sopenharmony_ci new_mask = labi->avail_coremask; 33062306a36Sopenharmony_ci } else { /* alternative, already initialized */ 33162306a36Sopenharmony_ci uint32_t *p = (uint32_t *)PHYS_TO_XKSEG_CACHED(block_desc->base_addr + 33262306a36Sopenharmony_ci AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK); 33362306a36Sopenharmony_ci *p |= mask; 33462306a36Sopenharmony_ci new_mask = *p; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci pr_info("Reset core %d. Available Coremask = 0x%x \n", coreid, new_mask); 33862306a36Sopenharmony_ci mb(); 33962306a36Sopenharmony_ci cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid); 34062306a36Sopenharmony_ci cvmx_write_csr(CVMX_CIU_PP_RST, 0); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_civoid play_dead(void) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci int cpu = cpu_number_map(cvmx_get_core_num()); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci idle_task_exit(); 34862306a36Sopenharmony_ci cpuhp_ap_report_dead(); 34962306a36Sopenharmony_ci octeon_processor_boot = 0xff; 35062306a36Sopenharmony_ci per_cpu(cpu_state, cpu) = CPU_DEAD; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci mb(); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci while (1) /* core will be reset here */ 35562306a36Sopenharmony_ci ; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void start_after_reset(void) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci kernel_entry(0, 0, 0); /* set a2 = 0 for secondary core */ 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int octeon_update_boot_vector(unsigned int cpu) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci int coreid = cpu_logical_map(cpu); 36762306a36Sopenharmony_ci uint32_t avail_coremask; 36862306a36Sopenharmony_ci const struct cvmx_bootmem_named_block_desc *block_desc; 36962306a36Sopenharmony_ci struct boot_init_vector *boot_vect = 37062306a36Sopenharmony_ci (struct boot_init_vector *)PHYS_TO_XKSEG_CACHED(BOOTLOADER_BOOT_VECTOR); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci block_desc = cvmx_bootmem_find_named_block(LINUX_APP_BOOT_BLOCK_NAME); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!block_desc) { 37562306a36Sopenharmony_ci struct linux_app_boot_info *labi; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci labi = (struct linux_app_boot_info *)PHYS_TO_XKSEG_CACHED(LABI_ADDR_IN_BOOTLOADER); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci avail_coremask = labi->avail_coremask; 38062306a36Sopenharmony_ci labi->avail_coremask &= ~(1 << coreid); 38162306a36Sopenharmony_ci } else { /* alternative, already initialized */ 38262306a36Sopenharmony_ci avail_coremask = *(uint32_t *)PHYS_TO_XKSEG_CACHED( 38362306a36Sopenharmony_ci block_desc->base_addr + AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (!(avail_coremask & (1 << coreid))) { 38762306a36Sopenharmony_ci /* core not available, assume, that caught by simple-executive */ 38862306a36Sopenharmony_ci cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid); 38962306a36Sopenharmony_ci cvmx_write_csr(CVMX_CIU_PP_RST, 0); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci boot_vect[coreid].app_start_func_addr = 39362306a36Sopenharmony_ci (uint32_t) (unsigned long) start_after_reset; 39462306a36Sopenharmony_ci boot_vect[coreid].code_addr = octeon_bootloader_entry_addr; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci mb(); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci cvmx_write_csr(CVMX_CIU_NMI, (1 << coreid) & avail_coremask); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int register_cavium_notifier(void) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci return cpuhp_setup_state_nocalls(CPUHP_MIPS_SOC_PREPARE, 40662306a36Sopenharmony_ci "mips/cavium:prepare", 40762306a36Sopenharmony_ci octeon_update_boot_vector, NULL); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_cilate_initcall(register_cavium_notifier); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */ 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic const struct plat_smp_ops octeon_smp_ops = { 41462306a36Sopenharmony_ci .send_ipi_single = octeon_send_ipi_single, 41562306a36Sopenharmony_ci .send_ipi_mask = octeon_send_ipi_mask, 41662306a36Sopenharmony_ci .init_secondary = octeon_init_secondary, 41762306a36Sopenharmony_ci .smp_finish = octeon_smp_finish, 41862306a36Sopenharmony_ci .boot_secondary = octeon_boot_secondary, 41962306a36Sopenharmony_ci .smp_setup = octeon_smp_setup, 42062306a36Sopenharmony_ci .prepare_cpus = octeon_prepare_cpus, 42162306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 42262306a36Sopenharmony_ci .cpu_disable = octeon_cpu_disable, 42362306a36Sopenharmony_ci .cpu_die = octeon_cpu_die, 42462306a36Sopenharmony_ci#endif 42562306a36Sopenharmony_ci#ifdef CONFIG_KEXEC 42662306a36Sopenharmony_ci .kexec_nonboot_cpu = kexec_nonboot_cpu_jump, 42762306a36Sopenharmony_ci#endif 42862306a36Sopenharmony_ci}; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic irqreturn_t octeon_78xx_reched_interrupt(int irq, void *dev_id) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci scheduler_ipi(); 43362306a36Sopenharmony_ci return IRQ_HANDLED; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic irqreturn_t octeon_78xx_call_function_interrupt(int irq, void *dev_id) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci generic_smp_call_function_interrupt(); 43962306a36Sopenharmony_ci return IRQ_HANDLED; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic irqreturn_t octeon_78xx_icache_flush_interrupt(int irq, void *dev_id) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci octeon_icache_flush(); 44562306a36Sopenharmony_ci return IRQ_HANDLED; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci/* 44962306a36Sopenharmony_ci * Callout to firmware before smp_init 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_cistatic void octeon_78xx_prepare_cpus(unsigned int max_cpus) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci if (request_irq(OCTEON_IRQ_MBOX0 + 0, 45462306a36Sopenharmony_ci octeon_78xx_reched_interrupt, 45562306a36Sopenharmony_ci IRQF_PERCPU | IRQF_NO_THREAD, "Scheduler", 45662306a36Sopenharmony_ci octeon_78xx_reched_interrupt)) { 45762306a36Sopenharmony_ci panic("Cannot request_irq for SchedulerIPI"); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci if (request_irq(OCTEON_IRQ_MBOX0 + 1, 46062306a36Sopenharmony_ci octeon_78xx_call_function_interrupt, 46162306a36Sopenharmony_ci IRQF_PERCPU | IRQF_NO_THREAD, "SMP-Call", 46262306a36Sopenharmony_ci octeon_78xx_call_function_interrupt)) { 46362306a36Sopenharmony_ci panic("Cannot request_irq for SMP-Call"); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci if (request_irq(OCTEON_IRQ_MBOX0 + 2, 46662306a36Sopenharmony_ci octeon_78xx_icache_flush_interrupt, 46762306a36Sopenharmony_ci IRQF_PERCPU | IRQF_NO_THREAD, "ICache-Flush", 46862306a36Sopenharmony_ci octeon_78xx_icache_flush_interrupt)) { 46962306a36Sopenharmony_ci panic("Cannot request_irq for ICache-Flush"); 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic void octeon_78xx_send_ipi_single(int cpu, unsigned int action) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci int i; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 47862306a36Sopenharmony_ci if (action & 1) 47962306a36Sopenharmony_ci octeon_ciu3_mbox_send(cpu, i); 48062306a36Sopenharmony_ci action >>= 1; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic void octeon_78xx_send_ipi_mask(const struct cpumask *mask, 48562306a36Sopenharmony_ci unsigned int action) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci unsigned int cpu; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci for_each_cpu(cpu, mask) 49062306a36Sopenharmony_ci octeon_78xx_send_ipi_single(cpu, action); 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic const struct plat_smp_ops octeon_78xx_smp_ops = { 49462306a36Sopenharmony_ci .send_ipi_single = octeon_78xx_send_ipi_single, 49562306a36Sopenharmony_ci .send_ipi_mask = octeon_78xx_send_ipi_mask, 49662306a36Sopenharmony_ci .init_secondary = octeon_init_secondary, 49762306a36Sopenharmony_ci .smp_finish = octeon_smp_finish, 49862306a36Sopenharmony_ci .boot_secondary = octeon_boot_secondary, 49962306a36Sopenharmony_ci .smp_setup = octeon_smp_setup, 50062306a36Sopenharmony_ci .prepare_cpus = octeon_78xx_prepare_cpus, 50162306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 50262306a36Sopenharmony_ci .cpu_disable = octeon_cpu_disable, 50362306a36Sopenharmony_ci .cpu_die = octeon_cpu_die, 50462306a36Sopenharmony_ci#endif 50562306a36Sopenharmony_ci#ifdef CONFIG_KEXEC 50662306a36Sopenharmony_ci .kexec_nonboot_cpu = kexec_nonboot_cpu_jump, 50762306a36Sopenharmony_ci#endif 50862306a36Sopenharmony_ci}; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_civoid __init octeon_setup_smp(void) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci const struct plat_smp_ops *ops; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (octeon_has_feature(OCTEON_FEATURE_CIU3)) 51562306a36Sopenharmony_ci ops = &octeon_78xx_smp_ops; 51662306a36Sopenharmony_ci else 51762306a36Sopenharmony_ci ops = &octeon_smp_ops; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci register_smp_ops(ops); 52062306a36Sopenharmony_ci} 521