146859ac8SHuacai Chen // SPDX-License-Identifier: GPL-2.0-or-later 246859ac8SHuacai Chen /* 346859ac8SHuacai Chen * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 446859ac8SHuacai Chen * 546859ac8SHuacai Chen * Derived from MIPS: 646859ac8SHuacai Chen * Copyright (C) 2000, 2001 Kanoj Sarcar 746859ac8SHuacai Chen * Copyright (C) 2000, 2001 Ralf Baechle 846859ac8SHuacai Chen * Copyright (C) 2000, 2001 Silicon Graphics, Inc. 946859ac8SHuacai Chen * Copyright (C) 2000, 2001, 2003 Broadcom Corporation 1046859ac8SHuacai Chen */ 1146859ac8SHuacai Chen #include <linux/cpu.h> 1246859ac8SHuacai Chen #include <linux/cpumask.h> 1346859ac8SHuacai Chen #include <linux/init.h> 1446859ac8SHuacai Chen #include <linux/interrupt.h> 1546859ac8SHuacai Chen #include <linux/seq_file.h> 1646859ac8SHuacai Chen #include <linux/smp.h> 1746859ac8SHuacai Chen #include <linux/threads.h> 1846859ac8SHuacai Chen #include <linux/export.h> 1946859ac8SHuacai Chen #include <linux/time.h> 2046859ac8SHuacai Chen #include <linux/tracepoint.h> 2146859ac8SHuacai Chen #include <linux/sched/hotplug.h> 2246859ac8SHuacai Chen #include <linux/sched/task_stack.h> 2346859ac8SHuacai Chen 2446859ac8SHuacai Chen #include <asm/cpu.h> 2546859ac8SHuacai Chen #include <asm/idle.h> 2646859ac8SHuacai Chen #include <asm/loongson.h> 2746859ac8SHuacai Chen #include <asm/mmu_context.h> 28*d4b6f156SHuacai Chen #include <asm/numa.h> 2946859ac8SHuacai Chen #include <asm/processor.h> 3046859ac8SHuacai Chen #include <asm/setup.h> 3146859ac8SHuacai Chen #include <asm/time.h> 3246859ac8SHuacai Chen 3346859ac8SHuacai Chen int __cpu_number_map[NR_CPUS]; /* Map physical to logical */ 3446859ac8SHuacai Chen EXPORT_SYMBOL(__cpu_number_map); 3546859ac8SHuacai Chen 3646859ac8SHuacai Chen int __cpu_logical_map[NR_CPUS]; /* Map logical to physical */ 3746859ac8SHuacai Chen EXPORT_SYMBOL(__cpu_logical_map); 3846859ac8SHuacai Chen 3946859ac8SHuacai Chen /* Number of threads (siblings) per CPU core */ 4046859ac8SHuacai Chen int smp_num_siblings = 1; 4146859ac8SHuacai Chen EXPORT_SYMBOL(smp_num_siblings); 4246859ac8SHuacai Chen 4346859ac8SHuacai Chen /* Representing the threads (siblings) of each logical CPU */ 4446859ac8SHuacai Chen cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly; 4546859ac8SHuacai Chen EXPORT_SYMBOL(cpu_sibling_map); 4646859ac8SHuacai Chen 4746859ac8SHuacai Chen /* Representing the core map of multi-core chips of each logical CPU */ 4846859ac8SHuacai Chen cpumask_t cpu_core_map[NR_CPUS] __read_mostly; 4946859ac8SHuacai Chen EXPORT_SYMBOL(cpu_core_map); 5046859ac8SHuacai Chen 5146859ac8SHuacai Chen static DECLARE_COMPLETION(cpu_starting); 5246859ac8SHuacai Chen static DECLARE_COMPLETION(cpu_running); 5346859ac8SHuacai Chen 5446859ac8SHuacai Chen /* 5546859ac8SHuacai Chen * A logcal cpu mask containing only one VPE per core to 5646859ac8SHuacai Chen * reduce the number of IPIs on large MT systems. 5746859ac8SHuacai Chen */ 5846859ac8SHuacai Chen cpumask_t cpu_foreign_map[NR_CPUS] __read_mostly; 5946859ac8SHuacai Chen EXPORT_SYMBOL(cpu_foreign_map); 6046859ac8SHuacai Chen 6146859ac8SHuacai Chen /* representing cpus for which sibling maps can be computed */ 6246859ac8SHuacai Chen static cpumask_t cpu_sibling_setup_map; 6346859ac8SHuacai Chen 6446859ac8SHuacai Chen /* representing cpus for which core maps can be computed */ 6546859ac8SHuacai Chen static cpumask_t cpu_core_setup_map; 6646859ac8SHuacai Chen 6746859ac8SHuacai Chen struct secondary_data cpuboot_data; 6846859ac8SHuacai Chen static DEFINE_PER_CPU(int, cpu_state); 6946859ac8SHuacai Chen DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); 7046859ac8SHuacai Chen EXPORT_PER_CPU_SYMBOL(irq_stat); 7146859ac8SHuacai Chen 7246859ac8SHuacai Chen enum ipi_msg_type { 7346859ac8SHuacai Chen IPI_RESCHEDULE, 7446859ac8SHuacai Chen IPI_CALL_FUNCTION, 7546859ac8SHuacai Chen }; 7646859ac8SHuacai Chen 7746859ac8SHuacai Chen static const char *ipi_types[NR_IPI] __tracepoint_string = { 7846859ac8SHuacai Chen [IPI_RESCHEDULE] = "Rescheduling interrupts", 7946859ac8SHuacai Chen [IPI_CALL_FUNCTION] = "Function call interrupts", 8046859ac8SHuacai Chen }; 8146859ac8SHuacai Chen 8246859ac8SHuacai Chen void show_ipi_list(struct seq_file *p, int prec) 8346859ac8SHuacai Chen { 8446859ac8SHuacai Chen unsigned int cpu, i; 8546859ac8SHuacai Chen 8646859ac8SHuacai Chen for (i = 0; i < NR_IPI; i++) { 8746859ac8SHuacai Chen seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, prec >= 4 ? " " : ""); 8846859ac8SHuacai Chen for_each_online_cpu(cpu) 8946859ac8SHuacai Chen seq_printf(p, "%10u ", per_cpu(irq_stat, cpu).ipi_irqs[i]); 9046859ac8SHuacai Chen seq_printf(p, " LoongArch %d %s\n", i + 1, ipi_types[i]); 9146859ac8SHuacai Chen } 9246859ac8SHuacai Chen } 9346859ac8SHuacai Chen 9446859ac8SHuacai Chen /* Send mailbox buffer via Mail_Send */ 9546859ac8SHuacai Chen static void csr_mail_send(uint64_t data, int cpu, int mailbox) 9646859ac8SHuacai Chen { 9746859ac8SHuacai Chen uint64_t val; 9846859ac8SHuacai Chen 9946859ac8SHuacai Chen /* Send high 32 bits */ 10046859ac8SHuacai Chen val = IOCSR_MBUF_SEND_BLOCKING; 10146859ac8SHuacai Chen val |= (IOCSR_MBUF_SEND_BOX_HI(mailbox) << IOCSR_MBUF_SEND_BOX_SHIFT); 10246859ac8SHuacai Chen val |= (cpu << IOCSR_MBUF_SEND_CPU_SHIFT); 10346859ac8SHuacai Chen val |= (data & IOCSR_MBUF_SEND_H32_MASK); 10446859ac8SHuacai Chen iocsr_write64(val, LOONGARCH_IOCSR_MBUF_SEND); 10546859ac8SHuacai Chen 10646859ac8SHuacai Chen /* Send low 32 bits */ 10746859ac8SHuacai Chen val = IOCSR_MBUF_SEND_BLOCKING; 10846859ac8SHuacai Chen val |= (IOCSR_MBUF_SEND_BOX_LO(mailbox) << IOCSR_MBUF_SEND_BOX_SHIFT); 10946859ac8SHuacai Chen val |= (cpu << IOCSR_MBUF_SEND_CPU_SHIFT); 11046859ac8SHuacai Chen val |= (data << IOCSR_MBUF_SEND_BUF_SHIFT); 11146859ac8SHuacai Chen iocsr_write64(val, LOONGARCH_IOCSR_MBUF_SEND); 11246859ac8SHuacai Chen }; 11346859ac8SHuacai Chen 11446859ac8SHuacai Chen static u32 ipi_read_clear(int cpu) 11546859ac8SHuacai Chen { 11646859ac8SHuacai Chen u32 action; 11746859ac8SHuacai Chen 11846859ac8SHuacai Chen /* Load the ipi register to figure out what we're supposed to do */ 11946859ac8SHuacai Chen action = iocsr_read32(LOONGARCH_IOCSR_IPI_STATUS); 12046859ac8SHuacai Chen /* Clear the ipi register to clear the interrupt */ 12146859ac8SHuacai Chen iocsr_write32(action, LOONGARCH_IOCSR_IPI_CLEAR); 12246859ac8SHuacai Chen smp_mb(); 12346859ac8SHuacai Chen 12446859ac8SHuacai Chen return action; 12546859ac8SHuacai Chen } 12646859ac8SHuacai Chen 12746859ac8SHuacai Chen static void ipi_write_action(int cpu, u32 action) 12846859ac8SHuacai Chen { 12946859ac8SHuacai Chen unsigned int irq = 0; 13046859ac8SHuacai Chen 13146859ac8SHuacai Chen while ((irq = ffs(action))) { 13246859ac8SHuacai Chen uint32_t val = IOCSR_IPI_SEND_BLOCKING; 13346859ac8SHuacai Chen 13446859ac8SHuacai Chen val |= (irq - 1); 13546859ac8SHuacai Chen val |= (cpu << IOCSR_IPI_SEND_CPU_SHIFT); 13646859ac8SHuacai Chen iocsr_write32(val, LOONGARCH_IOCSR_IPI_SEND); 13746859ac8SHuacai Chen action &= ~BIT(irq - 1); 13846859ac8SHuacai Chen } 13946859ac8SHuacai Chen } 14046859ac8SHuacai Chen 14146859ac8SHuacai Chen void loongson3_send_ipi_single(int cpu, unsigned int action) 14246859ac8SHuacai Chen { 14346859ac8SHuacai Chen ipi_write_action(cpu_logical_map(cpu), (u32)action); 14446859ac8SHuacai Chen } 14546859ac8SHuacai Chen 14646859ac8SHuacai Chen void loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action) 14746859ac8SHuacai Chen { 14846859ac8SHuacai Chen unsigned int i; 14946859ac8SHuacai Chen 15046859ac8SHuacai Chen for_each_cpu(i, mask) 15146859ac8SHuacai Chen ipi_write_action(cpu_logical_map(i), (u32)action); 15246859ac8SHuacai Chen } 15346859ac8SHuacai Chen 15446859ac8SHuacai Chen irqreturn_t loongson3_ipi_interrupt(int irq, void *dev) 15546859ac8SHuacai Chen { 15646859ac8SHuacai Chen unsigned int action; 15746859ac8SHuacai Chen unsigned int cpu = smp_processor_id(); 15846859ac8SHuacai Chen 15946859ac8SHuacai Chen action = ipi_read_clear(cpu_logical_map(cpu)); 16046859ac8SHuacai Chen 16146859ac8SHuacai Chen if (action & SMP_RESCHEDULE) { 16246859ac8SHuacai Chen scheduler_ipi(); 16346859ac8SHuacai Chen per_cpu(irq_stat, cpu).ipi_irqs[IPI_RESCHEDULE]++; 16446859ac8SHuacai Chen } 16546859ac8SHuacai Chen 16646859ac8SHuacai Chen if (action & SMP_CALL_FUNCTION) { 16746859ac8SHuacai Chen generic_smp_call_function_interrupt(); 16846859ac8SHuacai Chen per_cpu(irq_stat, cpu).ipi_irqs[IPI_CALL_FUNCTION]++; 16946859ac8SHuacai Chen } 17046859ac8SHuacai Chen 17146859ac8SHuacai Chen return IRQ_HANDLED; 17246859ac8SHuacai Chen } 17346859ac8SHuacai Chen 17446859ac8SHuacai Chen void __init loongson3_smp_setup(void) 17546859ac8SHuacai Chen { 17646859ac8SHuacai Chen cpu_data[0].core = cpu_logical_map(0) % loongson_sysconf.cores_per_package; 17746859ac8SHuacai Chen cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package; 17846859ac8SHuacai Chen 17946859ac8SHuacai Chen iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_EN); 18046859ac8SHuacai Chen pr_info("Detected %i available CPU(s)\n", loongson_sysconf.nr_cpus); 18146859ac8SHuacai Chen } 18246859ac8SHuacai Chen 18346859ac8SHuacai Chen void __init loongson3_prepare_cpus(unsigned int max_cpus) 18446859ac8SHuacai Chen { 18546859ac8SHuacai Chen int i = 0; 18646859ac8SHuacai Chen 18746859ac8SHuacai Chen for (i = 0; i < loongson_sysconf.nr_cpus; i++) { 18846859ac8SHuacai Chen set_cpu_present(i, true); 18946859ac8SHuacai Chen csr_mail_send(0, __cpu_logical_map[i], 0); 19046859ac8SHuacai Chen } 19146859ac8SHuacai Chen 19246859ac8SHuacai Chen per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE; 19346859ac8SHuacai Chen } 19446859ac8SHuacai Chen 19546859ac8SHuacai Chen /* 19646859ac8SHuacai Chen * Setup the PC, SP, and TP of a secondary processor and start it running! 19746859ac8SHuacai Chen */ 19846859ac8SHuacai Chen void loongson3_boot_secondary(int cpu, struct task_struct *idle) 19946859ac8SHuacai Chen { 20046859ac8SHuacai Chen unsigned long entry; 20146859ac8SHuacai Chen 20246859ac8SHuacai Chen pr_info("Booting CPU#%d...\n", cpu); 20346859ac8SHuacai Chen 20446859ac8SHuacai Chen entry = __pa_symbol((unsigned long)&smpboot_entry); 20546859ac8SHuacai Chen cpuboot_data.stack = (unsigned long)__KSTK_TOS(idle); 20646859ac8SHuacai Chen cpuboot_data.thread_info = (unsigned long)task_thread_info(idle); 20746859ac8SHuacai Chen 20846859ac8SHuacai Chen csr_mail_send(entry, cpu_logical_map(cpu), 0); 20946859ac8SHuacai Chen 21046859ac8SHuacai Chen loongson3_send_ipi_single(cpu, SMP_BOOT_CPU); 21146859ac8SHuacai Chen } 21246859ac8SHuacai Chen 21346859ac8SHuacai Chen /* 21446859ac8SHuacai Chen * SMP init and finish on secondary CPUs 21546859ac8SHuacai Chen */ 21646859ac8SHuacai Chen void loongson3_init_secondary(void) 21746859ac8SHuacai Chen { 21846859ac8SHuacai Chen unsigned int cpu = smp_processor_id(); 21946859ac8SHuacai Chen unsigned int imask = ECFGF_IP0 | ECFGF_IP1 | ECFGF_IP2 | 22046859ac8SHuacai Chen ECFGF_IPI | ECFGF_PMC | ECFGF_TIMER; 22146859ac8SHuacai Chen 22246859ac8SHuacai Chen change_csr_ecfg(ECFG0_IM, imask); 22346859ac8SHuacai Chen 22446859ac8SHuacai Chen iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_EN); 22546859ac8SHuacai Chen 226*d4b6f156SHuacai Chen #ifdef CONFIG_NUMA 227*d4b6f156SHuacai Chen numa_add_cpu(cpu); 228*d4b6f156SHuacai Chen #endif 22946859ac8SHuacai Chen per_cpu(cpu_state, cpu) = CPU_ONLINE; 23046859ac8SHuacai Chen cpu_data[cpu].core = 23146859ac8SHuacai Chen cpu_logical_map(cpu) % loongson_sysconf.cores_per_package; 23246859ac8SHuacai Chen cpu_data[cpu].package = 23346859ac8SHuacai Chen cpu_logical_map(cpu) / loongson_sysconf.cores_per_package; 23446859ac8SHuacai Chen } 23546859ac8SHuacai Chen 23646859ac8SHuacai Chen void loongson3_smp_finish(void) 23746859ac8SHuacai Chen { 23846859ac8SHuacai Chen local_irq_enable(); 23946859ac8SHuacai Chen iocsr_write64(0, LOONGARCH_IOCSR_MBUF0); 24046859ac8SHuacai Chen pr_info("CPU#%d finished\n", smp_processor_id()); 24146859ac8SHuacai Chen } 24246859ac8SHuacai Chen 24346859ac8SHuacai Chen #ifdef CONFIG_HOTPLUG_CPU 24446859ac8SHuacai Chen 24546859ac8SHuacai Chen static bool io_master(int cpu) 24646859ac8SHuacai Chen { 24746859ac8SHuacai Chen if (cpu == 0) 24846859ac8SHuacai Chen return true; 24946859ac8SHuacai Chen 25046859ac8SHuacai Chen return false; 25146859ac8SHuacai Chen } 25246859ac8SHuacai Chen 25346859ac8SHuacai Chen int loongson3_cpu_disable(void) 25446859ac8SHuacai Chen { 25546859ac8SHuacai Chen unsigned long flags; 25646859ac8SHuacai Chen unsigned int cpu = smp_processor_id(); 25746859ac8SHuacai Chen 25846859ac8SHuacai Chen if (io_master(cpu)) 25946859ac8SHuacai Chen return -EBUSY; 26046859ac8SHuacai Chen 261*d4b6f156SHuacai Chen #ifdef CONFIG_NUMA 262*d4b6f156SHuacai Chen numa_remove_cpu(cpu); 263*d4b6f156SHuacai Chen #endif 26446859ac8SHuacai Chen set_cpu_online(cpu, false); 26546859ac8SHuacai Chen calculate_cpu_foreign_map(); 26646859ac8SHuacai Chen local_irq_save(flags); 26746859ac8SHuacai Chen irq_migrate_all_off_this_cpu(); 26846859ac8SHuacai Chen clear_csr_ecfg(ECFG0_IM); 26946859ac8SHuacai Chen local_irq_restore(flags); 27046859ac8SHuacai Chen local_flush_tlb_all(); 27146859ac8SHuacai Chen 27246859ac8SHuacai Chen return 0; 27346859ac8SHuacai Chen } 27446859ac8SHuacai Chen 27546859ac8SHuacai Chen void loongson3_cpu_die(unsigned int cpu) 27646859ac8SHuacai Chen { 27746859ac8SHuacai Chen while (per_cpu(cpu_state, cpu) != CPU_DEAD) 27846859ac8SHuacai Chen cpu_relax(); 27946859ac8SHuacai Chen 28046859ac8SHuacai Chen mb(); 28146859ac8SHuacai Chen } 28246859ac8SHuacai Chen 28346859ac8SHuacai Chen /* 28446859ac8SHuacai Chen * The target CPU should go to XKPRANGE (uncached area) and flush 28546859ac8SHuacai Chen * ICache/DCache/VCache before the control CPU can safely disable its clock. 28646859ac8SHuacai Chen */ 28746859ac8SHuacai Chen static void loongson3_play_dead(int *state_addr) 28846859ac8SHuacai Chen { 28946859ac8SHuacai Chen register int val; 29046859ac8SHuacai Chen register void *addr; 29146859ac8SHuacai Chen register void (*init_fn)(void); 29246859ac8SHuacai Chen 29346859ac8SHuacai Chen __asm__ __volatile__( 29446859ac8SHuacai Chen " li.d %[addr], 0x8000000000000000\n" 29546859ac8SHuacai Chen "1: cacop 0x8, %[addr], 0 \n" /* flush ICache */ 29646859ac8SHuacai Chen " cacop 0x8, %[addr], 1 \n" 29746859ac8SHuacai Chen " cacop 0x8, %[addr], 2 \n" 29846859ac8SHuacai Chen " cacop 0x8, %[addr], 3 \n" 29946859ac8SHuacai Chen " cacop 0x9, %[addr], 0 \n" /* flush DCache */ 30046859ac8SHuacai Chen " cacop 0x9, %[addr], 1 \n" 30146859ac8SHuacai Chen " cacop 0x9, %[addr], 2 \n" 30246859ac8SHuacai Chen " cacop 0x9, %[addr], 3 \n" 30346859ac8SHuacai Chen " addi.w %[sets], %[sets], -1 \n" 30446859ac8SHuacai Chen " addi.d %[addr], %[addr], 0x40 \n" 30546859ac8SHuacai Chen " bnez %[sets], 1b \n" 30646859ac8SHuacai Chen " li.d %[addr], 0x8000000000000000\n" 30746859ac8SHuacai Chen "2: cacop 0xa, %[addr], 0 \n" /* flush VCache */ 30846859ac8SHuacai Chen " cacop 0xa, %[addr], 1 \n" 30946859ac8SHuacai Chen " cacop 0xa, %[addr], 2 \n" 31046859ac8SHuacai Chen " cacop 0xa, %[addr], 3 \n" 31146859ac8SHuacai Chen " cacop 0xa, %[addr], 4 \n" 31246859ac8SHuacai Chen " cacop 0xa, %[addr], 5 \n" 31346859ac8SHuacai Chen " cacop 0xa, %[addr], 6 \n" 31446859ac8SHuacai Chen " cacop 0xa, %[addr], 7 \n" 31546859ac8SHuacai Chen " cacop 0xa, %[addr], 8 \n" 31646859ac8SHuacai Chen " cacop 0xa, %[addr], 9 \n" 31746859ac8SHuacai Chen " cacop 0xa, %[addr], 10 \n" 31846859ac8SHuacai Chen " cacop 0xa, %[addr], 11 \n" 31946859ac8SHuacai Chen " cacop 0xa, %[addr], 12 \n" 32046859ac8SHuacai Chen " cacop 0xa, %[addr], 13 \n" 32146859ac8SHuacai Chen " cacop 0xa, %[addr], 14 \n" 32246859ac8SHuacai Chen " cacop 0xa, %[addr], 15 \n" 32346859ac8SHuacai Chen " addi.w %[vsets], %[vsets], -1 \n" 32446859ac8SHuacai Chen " addi.d %[addr], %[addr], 0x40 \n" 32546859ac8SHuacai Chen " bnez %[vsets], 2b \n" 32646859ac8SHuacai Chen " li.w %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */ 32746859ac8SHuacai Chen " st.w %[val], %[state_addr], 0 \n" 32846859ac8SHuacai Chen " dbar 0 \n" 32946859ac8SHuacai Chen " cacop 0x11, %[state_addr], 0 \n" /* flush entry of *state_addr */ 33046859ac8SHuacai Chen : [addr] "=&r" (addr), [val] "=&r" (val) 33146859ac8SHuacai Chen : [state_addr] "r" (state_addr), 33246859ac8SHuacai Chen [sets] "r" (cpu_data[smp_processor_id()].dcache.sets), 33346859ac8SHuacai Chen [vsets] "r" (cpu_data[smp_processor_id()].vcache.sets)); 33446859ac8SHuacai Chen 33546859ac8SHuacai Chen local_irq_enable(); 33646859ac8SHuacai Chen change_csr_ecfg(ECFG0_IM, ECFGF_IPI); 33746859ac8SHuacai Chen 33846859ac8SHuacai Chen __asm__ __volatile__( 33946859ac8SHuacai Chen " idle 0 \n" 34046859ac8SHuacai Chen " li.w $t0, 0x1020 \n" 34146859ac8SHuacai Chen " iocsrrd.d %[init_fn], $t0 \n" /* Get init PC */ 34246859ac8SHuacai Chen : [init_fn] "=&r" (addr) 34346859ac8SHuacai Chen : /* No Input */ 34446859ac8SHuacai Chen : "a0"); 34546859ac8SHuacai Chen init_fn = __va(addr); 34646859ac8SHuacai Chen 34746859ac8SHuacai Chen init_fn(); 34846859ac8SHuacai Chen unreachable(); 34946859ac8SHuacai Chen } 35046859ac8SHuacai Chen 35146859ac8SHuacai Chen void play_dead(void) 35246859ac8SHuacai Chen { 35346859ac8SHuacai Chen int *state_addr; 35446859ac8SHuacai Chen unsigned int cpu = smp_processor_id(); 35546859ac8SHuacai Chen void (*play_dead_uncached)(int *s); 35646859ac8SHuacai Chen 35746859ac8SHuacai Chen idle_task_exit(); 35846859ac8SHuacai Chen play_dead_uncached = (void *)TO_UNCACHE(__pa((unsigned long)loongson3_play_dead)); 35946859ac8SHuacai Chen state_addr = &per_cpu(cpu_state, cpu); 36046859ac8SHuacai Chen mb(); 36146859ac8SHuacai Chen play_dead_uncached(state_addr); 36246859ac8SHuacai Chen } 36346859ac8SHuacai Chen 36446859ac8SHuacai Chen static int loongson3_enable_clock(unsigned int cpu) 36546859ac8SHuacai Chen { 36646859ac8SHuacai Chen uint64_t core_id = cpu_data[cpu].core; 36746859ac8SHuacai Chen uint64_t package_id = cpu_data[cpu].package; 36846859ac8SHuacai Chen 36946859ac8SHuacai Chen LOONGSON_FREQCTRL(package_id) |= 1 << (core_id * 4 + 3); 37046859ac8SHuacai Chen 37146859ac8SHuacai Chen return 0; 37246859ac8SHuacai Chen } 37346859ac8SHuacai Chen 37446859ac8SHuacai Chen static int loongson3_disable_clock(unsigned int cpu) 37546859ac8SHuacai Chen { 37646859ac8SHuacai Chen uint64_t core_id = cpu_data[cpu].core; 37746859ac8SHuacai Chen uint64_t package_id = cpu_data[cpu].package; 37846859ac8SHuacai Chen 37946859ac8SHuacai Chen LOONGSON_FREQCTRL(package_id) &= ~(1 << (core_id * 4 + 3)); 38046859ac8SHuacai Chen 38146859ac8SHuacai Chen return 0; 38246859ac8SHuacai Chen } 38346859ac8SHuacai Chen 38446859ac8SHuacai Chen static int register_loongson3_notifier(void) 38546859ac8SHuacai Chen { 38646859ac8SHuacai Chen return cpuhp_setup_state_nocalls(CPUHP_LOONGARCH_SOC_PREPARE, 38746859ac8SHuacai Chen "loongarch/loongson:prepare", 38846859ac8SHuacai Chen loongson3_enable_clock, 38946859ac8SHuacai Chen loongson3_disable_clock); 39046859ac8SHuacai Chen } 39146859ac8SHuacai Chen early_initcall(register_loongson3_notifier); 39246859ac8SHuacai Chen 39346859ac8SHuacai Chen #endif 39446859ac8SHuacai Chen 39546859ac8SHuacai Chen /* 39646859ac8SHuacai Chen * Power management 39746859ac8SHuacai Chen */ 39846859ac8SHuacai Chen #ifdef CONFIG_PM 39946859ac8SHuacai Chen 40046859ac8SHuacai Chen static int loongson3_ipi_suspend(void) 40146859ac8SHuacai Chen { 40246859ac8SHuacai Chen return 0; 40346859ac8SHuacai Chen } 40446859ac8SHuacai Chen 40546859ac8SHuacai Chen static void loongson3_ipi_resume(void) 40646859ac8SHuacai Chen { 40746859ac8SHuacai Chen iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_EN); 40846859ac8SHuacai Chen } 40946859ac8SHuacai Chen 41046859ac8SHuacai Chen static struct syscore_ops loongson3_ipi_syscore_ops = { 41146859ac8SHuacai Chen .resume = loongson3_ipi_resume, 41246859ac8SHuacai Chen .suspend = loongson3_ipi_suspend, 41346859ac8SHuacai Chen }; 41446859ac8SHuacai Chen 41546859ac8SHuacai Chen /* 41646859ac8SHuacai Chen * Enable boot cpu ipi before enabling nonboot cpus 41746859ac8SHuacai Chen * during syscore_resume. 41846859ac8SHuacai Chen */ 41946859ac8SHuacai Chen static int __init ipi_pm_init(void) 42046859ac8SHuacai Chen { 42146859ac8SHuacai Chen register_syscore_ops(&loongson3_ipi_syscore_ops); 42246859ac8SHuacai Chen return 0; 42346859ac8SHuacai Chen } 42446859ac8SHuacai Chen 42546859ac8SHuacai Chen core_initcall(ipi_pm_init); 42646859ac8SHuacai Chen #endif 42746859ac8SHuacai Chen 42846859ac8SHuacai Chen static inline void set_cpu_sibling_map(int cpu) 42946859ac8SHuacai Chen { 43046859ac8SHuacai Chen int i; 43146859ac8SHuacai Chen 43246859ac8SHuacai Chen cpumask_set_cpu(cpu, &cpu_sibling_setup_map); 43346859ac8SHuacai Chen 43446859ac8SHuacai Chen if (smp_num_siblings <= 1) 43546859ac8SHuacai Chen cpumask_set_cpu(cpu, &cpu_sibling_map[cpu]); 43646859ac8SHuacai Chen else { 43746859ac8SHuacai Chen for_each_cpu(i, &cpu_sibling_setup_map) { 43846859ac8SHuacai Chen if (cpus_are_siblings(cpu, i)) { 43946859ac8SHuacai Chen cpumask_set_cpu(i, &cpu_sibling_map[cpu]); 44046859ac8SHuacai Chen cpumask_set_cpu(cpu, &cpu_sibling_map[i]); 44146859ac8SHuacai Chen } 44246859ac8SHuacai Chen } 44346859ac8SHuacai Chen } 44446859ac8SHuacai Chen } 44546859ac8SHuacai Chen 44646859ac8SHuacai Chen static inline void set_cpu_core_map(int cpu) 44746859ac8SHuacai Chen { 44846859ac8SHuacai Chen int i; 44946859ac8SHuacai Chen 45046859ac8SHuacai Chen cpumask_set_cpu(cpu, &cpu_core_setup_map); 45146859ac8SHuacai Chen 45246859ac8SHuacai Chen for_each_cpu(i, &cpu_core_setup_map) { 45346859ac8SHuacai Chen if (cpu_data[cpu].package == cpu_data[i].package) { 45446859ac8SHuacai Chen cpumask_set_cpu(i, &cpu_core_map[cpu]); 45546859ac8SHuacai Chen cpumask_set_cpu(cpu, &cpu_core_map[i]); 45646859ac8SHuacai Chen } 45746859ac8SHuacai Chen } 45846859ac8SHuacai Chen } 45946859ac8SHuacai Chen 46046859ac8SHuacai Chen /* 46146859ac8SHuacai Chen * Calculate a new cpu_foreign_map mask whenever a 46246859ac8SHuacai Chen * new cpu appears or disappears. 46346859ac8SHuacai Chen */ 46446859ac8SHuacai Chen void calculate_cpu_foreign_map(void) 46546859ac8SHuacai Chen { 46646859ac8SHuacai Chen int i, k, core_present; 46746859ac8SHuacai Chen cpumask_t temp_foreign_map; 46846859ac8SHuacai Chen 46946859ac8SHuacai Chen /* Re-calculate the mask */ 47046859ac8SHuacai Chen cpumask_clear(&temp_foreign_map); 47146859ac8SHuacai Chen for_each_online_cpu(i) { 47246859ac8SHuacai Chen core_present = 0; 47346859ac8SHuacai Chen for_each_cpu(k, &temp_foreign_map) 47446859ac8SHuacai Chen if (cpus_are_siblings(i, k)) 47546859ac8SHuacai Chen core_present = 1; 47646859ac8SHuacai Chen if (!core_present) 47746859ac8SHuacai Chen cpumask_set_cpu(i, &temp_foreign_map); 47846859ac8SHuacai Chen } 47946859ac8SHuacai Chen 48046859ac8SHuacai Chen for_each_online_cpu(i) 48146859ac8SHuacai Chen cpumask_andnot(&cpu_foreign_map[i], 48246859ac8SHuacai Chen &temp_foreign_map, &cpu_sibling_map[i]); 48346859ac8SHuacai Chen } 48446859ac8SHuacai Chen 48546859ac8SHuacai Chen /* Preload SMP state for boot cpu */ 48646859ac8SHuacai Chen void smp_prepare_boot_cpu(void) 48746859ac8SHuacai Chen { 488*d4b6f156SHuacai Chen unsigned int cpu, node, rr_node; 48946859ac8SHuacai Chen 49046859ac8SHuacai Chen set_cpu_possible(0, true); 49146859ac8SHuacai Chen set_cpu_online(0, true); 49246859ac8SHuacai Chen set_my_cpu_offset(per_cpu_offset(0)); 49346859ac8SHuacai Chen 494*d4b6f156SHuacai Chen rr_node = first_node(node_online_map); 495*d4b6f156SHuacai Chen for_each_possible_cpu(cpu) { 496*d4b6f156SHuacai Chen node = early_cpu_to_node(cpu); 497*d4b6f156SHuacai Chen 498*d4b6f156SHuacai Chen /* 499*d4b6f156SHuacai Chen * The mapping between present cpus and nodes has been 500*d4b6f156SHuacai Chen * built during MADT and SRAT parsing. 501*d4b6f156SHuacai Chen * 502*d4b6f156SHuacai Chen * If possible cpus = present cpus here, early_cpu_to_node 503*d4b6f156SHuacai Chen * will return valid node. 504*d4b6f156SHuacai Chen * 505*d4b6f156SHuacai Chen * If possible cpus > present cpus here (e.g. some possible 506*d4b6f156SHuacai Chen * cpus will be added by cpu-hotplug later), for possible but 507*d4b6f156SHuacai Chen * not present cpus, early_cpu_to_node will return NUMA_NO_NODE, 508*d4b6f156SHuacai Chen * and we just map them to online nodes in round-robin way. 509*d4b6f156SHuacai Chen * Once hotplugged, new correct mapping will be built for them. 510*d4b6f156SHuacai Chen */ 511*d4b6f156SHuacai Chen if (node != NUMA_NO_NODE) 512*d4b6f156SHuacai Chen set_cpu_numa_node(cpu, node); 513*d4b6f156SHuacai Chen else { 514*d4b6f156SHuacai Chen set_cpu_numa_node(cpu, rr_node); 515*d4b6f156SHuacai Chen rr_node = next_node_in(rr_node, node_online_map); 516*d4b6f156SHuacai Chen } 517*d4b6f156SHuacai Chen } 51846859ac8SHuacai Chen } 51946859ac8SHuacai Chen 52046859ac8SHuacai Chen /* called from main before smp_init() */ 52146859ac8SHuacai Chen void __init smp_prepare_cpus(unsigned int max_cpus) 52246859ac8SHuacai Chen { 52346859ac8SHuacai Chen init_new_context(current, &init_mm); 52446859ac8SHuacai Chen current_thread_info()->cpu = 0; 52546859ac8SHuacai Chen loongson3_prepare_cpus(max_cpus); 52646859ac8SHuacai Chen set_cpu_sibling_map(0); 52746859ac8SHuacai Chen set_cpu_core_map(0); 52846859ac8SHuacai Chen calculate_cpu_foreign_map(); 52946859ac8SHuacai Chen #ifndef CONFIG_HOTPLUG_CPU 53046859ac8SHuacai Chen init_cpu_present(cpu_possible_mask); 53146859ac8SHuacai Chen #endif 53246859ac8SHuacai Chen } 53346859ac8SHuacai Chen 53446859ac8SHuacai Chen int __cpu_up(unsigned int cpu, struct task_struct *tidle) 53546859ac8SHuacai Chen { 53646859ac8SHuacai Chen loongson3_boot_secondary(cpu, tidle); 53746859ac8SHuacai Chen 53846859ac8SHuacai Chen /* Wait for CPU to start and be ready to sync counters */ 53946859ac8SHuacai Chen if (!wait_for_completion_timeout(&cpu_starting, 54046859ac8SHuacai Chen msecs_to_jiffies(5000))) { 54146859ac8SHuacai Chen pr_crit("CPU%u: failed to start\n", cpu); 54246859ac8SHuacai Chen return -EIO; 54346859ac8SHuacai Chen } 54446859ac8SHuacai Chen 54546859ac8SHuacai Chen /* Wait for CPU to finish startup & mark itself online before return */ 54646859ac8SHuacai Chen wait_for_completion(&cpu_running); 54746859ac8SHuacai Chen 54846859ac8SHuacai Chen return 0; 54946859ac8SHuacai Chen } 55046859ac8SHuacai Chen 55146859ac8SHuacai Chen /* 55246859ac8SHuacai Chen * First C code run on the secondary CPUs after being started up by 55346859ac8SHuacai Chen * the master. 55446859ac8SHuacai Chen */ 55546859ac8SHuacai Chen asmlinkage void start_secondary(void) 55646859ac8SHuacai Chen { 55746859ac8SHuacai Chen unsigned int cpu; 55846859ac8SHuacai Chen 55946859ac8SHuacai Chen sync_counter(); 56046859ac8SHuacai Chen cpu = smp_processor_id(); 56146859ac8SHuacai Chen set_my_cpu_offset(per_cpu_offset(cpu)); 56246859ac8SHuacai Chen 56346859ac8SHuacai Chen cpu_probe(); 56446859ac8SHuacai Chen constant_clockevent_init(); 56546859ac8SHuacai Chen loongson3_init_secondary(); 56646859ac8SHuacai Chen 56746859ac8SHuacai Chen set_cpu_sibling_map(cpu); 56846859ac8SHuacai Chen set_cpu_core_map(cpu); 56946859ac8SHuacai Chen 57046859ac8SHuacai Chen notify_cpu_starting(cpu); 57146859ac8SHuacai Chen 57246859ac8SHuacai Chen /* Notify boot CPU that we're starting */ 57346859ac8SHuacai Chen complete(&cpu_starting); 57446859ac8SHuacai Chen 57546859ac8SHuacai Chen /* The CPU is running, now mark it online */ 57646859ac8SHuacai Chen set_cpu_online(cpu, true); 57746859ac8SHuacai Chen 57846859ac8SHuacai Chen calculate_cpu_foreign_map(); 57946859ac8SHuacai Chen 58046859ac8SHuacai Chen /* 58146859ac8SHuacai Chen * Notify boot CPU that we're up & online and it can safely return 58246859ac8SHuacai Chen * from __cpu_up() 58346859ac8SHuacai Chen */ 58446859ac8SHuacai Chen complete(&cpu_running); 58546859ac8SHuacai Chen 58646859ac8SHuacai Chen /* 58746859ac8SHuacai Chen * irq will be enabled in loongson3_smp_finish(), enabling it too 58846859ac8SHuacai Chen * early is dangerous. 58946859ac8SHuacai Chen */ 59046859ac8SHuacai Chen WARN_ON_ONCE(!irqs_disabled()); 59146859ac8SHuacai Chen loongson3_smp_finish(); 59246859ac8SHuacai Chen 59346859ac8SHuacai Chen cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); 59446859ac8SHuacai Chen } 59546859ac8SHuacai Chen 59646859ac8SHuacai Chen void __init smp_cpus_done(unsigned int max_cpus) 59746859ac8SHuacai Chen { 59846859ac8SHuacai Chen } 59946859ac8SHuacai Chen 60046859ac8SHuacai Chen static void stop_this_cpu(void *dummy) 60146859ac8SHuacai Chen { 60246859ac8SHuacai Chen set_cpu_online(smp_processor_id(), false); 60346859ac8SHuacai Chen calculate_cpu_foreign_map(); 60446859ac8SHuacai Chen local_irq_disable(); 60546859ac8SHuacai Chen while (true); 60646859ac8SHuacai Chen } 60746859ac8SHuacai Chen 60846859ac8SHuacai Chen void smp_send_stop(void) 60946859ac8SHuacai Chen { 61046859ac8SHuacai Chen smp_call_function(stop_this_cpu, NULL, 0); 61146859ac8SHuacai Chen } 61246859ac8SHuacai Chen 61346859ac8SHuacai Chen int setup_profiling_timer(unsigned int multiplier) 61446859ac8SHuacai Chen { 61546859ac8SHuacai Chen return 0; 61646859ac8SHuacai Chen } 61746859ac8SHuacai Chen 61846859ac8SHuacai Chen static void flush_tlb_all_ipi(void *info) 61946859ac8SHuacai Chen { 62046859ac8SHuacai Chen local_flush_tlb_all(); 62146859ac8SHuacai Chen } 62246859ac8SHuacai Chen 62346859ac8SHuacai Chen void flush_tlb_all(void) 62446859ac8SHuacai Chen { 62546859ac8SHuacai Chen on_each_cpu(flush_tlb_all_ipi, NULL, 1); 62646859ac8SHuacai Chen } 62746859ac8SHuacai Chen 62846859ac8SHuacai Chen static void flush_tlb_mm_ipi(void *mm) 62946859ac8SHuacai Chen { 63046859ac8SHuacai Chen local_flush_tlb_mm((struct mm_struct *)mm); 63146859ac8SHuacai Chen } 63246859ac8SHuacai Chen 63346859ac8SHuacai Chen void flush_tlb_mm(struct mm_struct *mm) 63446859ac8SHuacai Chen { 63546859ac8SHuacai Chen if (atomic_read(&mm->mm_users) == 0) 63646859ac8SHuacai Chen return; /* happens as a result of exit_mmap() */ 63746859ac8SHuacai Chen 63846859ac8SHuacai Chen preempt_disable(); 63946859ac8SHuacai Chen 64046859ac8SHuacai Chen if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { 64146859ac8SHuacai Chen on_each_cpu_mask(mm_cpumask(mm), flush_tlb_mm_ipi, mm, 1); 64246859ac8SHuacai Chen } else { 64346859ac8SHuacai Chen unsigned int cpu; 64446859ac8SHuacai Chen 64546859ac8SHuacai Chen for_each_online_cpu(cpu) { 64646859ac8SHuacai Chen if (cpu != smp_processor_id() && cpu_context(cpu, mm)) 64746859ac8SHuacai Chen cpu_context(cpu, mm) = 0; 64846859ac8SHuacai Chen } 64946859ac8SHuacai Chen local_flush_tlb_mm(mm); 65046859ac8SHuacai Chen } 65146859ac8SHuacai Chen 65246859ac8SHuacai Chen preempt_enable(); 65346859ac8SHuacai Chen } 65446859ac8SHuacai Chen 65546859ac8SHuacai Chen struct flush_tlb_data { 65646859ac8SHuacai Chen struct vm_area_struct *vma; 65746859ac8SHuacai Chen unsigned long addr1; 65846859ac8SHuacai Chen unsigned long addr2; 65946859ac8SHuacai Chen }; 66046859ac8SHuacai Chen 66146859ac8SHuacai Chen static void flush_tlb_range_ipi(void *info) 66246859ac8SHuacai Chen { 66346859ac8SHuacai Chen struct flush_tlb_data *fd = info; 66446859ac8SHuacai Chen 66546859ac8SHuacai Chen local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2); 66646859ac8SHuacai Chen } 66746859ac8SHuacai Chen 66846859ac8SHuacai Chen void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) 66946859ac8SHuacai Chen { 67046859ac8SHuacai Chen struct mm_struct *mm = vma->vm_mm; 67146859ac8SHuacai Chen 67246859ac8SHuacai Chen preempt_disable(); 67346859ac8SHuacai Chen if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { 67446859ac8SHuacai Chen struct flush_tlb_data fd = { 67546859ac8SHuacai Chen .vma = vma, 67646859ac8SHuacai Chen .addr1 = start, 67746859ac8SHuacai Chen .addr2 = end, 67846859ac8SHuacai Chen }; 67946859ac8SHuacai Chen 68046859ac8SHuacai Chen on_each_cpu_mask(mm_cpumask(mm), flush_tlb_range_ipi, &fd, 1); 68146859ac8SHuacai Chen } else { 68246859ac8SHuacai Chen unsigned int cpu; 68346859ac8SHuacai Chen 68446859ac8SHuacai Chen for_each_online_cpu(cpu) { 68546859ac8SHuacai Chen if (cpu != smp_processor_id() && cpu_context(cpu, mm)) 686*d4b6f156SHuacai Chen cpu_context(cpu, mm) = 0; 68746859ac8SHuacai Chen } 68846859ac8SHuacai Chen local_flush_tlb_range(vma, start, end); 68946859ac8SHuacai Chen } 69046859ac8SHuacai Chen preempt_enable(); 69146859ac8SHuacai Chen } 69246859ac8SHuacai Chen 69346859ac8SHuacai Chen static void flush_tlb_kernel_range_ipi(void *info) 69446859ac8SHuacai Chen { 69546859ac8SHuacai Chen struct flush_tlb_data *fd = info; 69646859ac8SHuacai Chen 69746859ac8SHuacai Chen local_flush_tlb_kernel_range(fd->addr1, fd->addr2); 69846859ac8SHuacai Chen } 69946859ac8SHuacai Chen 70046859ac8SHuacai Chen void flush_tlb_kernel_range(unsigned long start, unsigned long end) 70146859ac8SHuacai Chen { 70246859ac8SHuacai Chen struct flush_tlb_data fd = { 70346859ac8SHuacai Chen .addr1 = start, 70446859ac8SHuacai Chen .addr2 = end, 70546859ac8SHuacai Chen }; 70646859ac8SHuacai Chen 70746859ac8SHuacai Chen on_each_cpu(flush_tlb_kernel_range_ipi, &fd, 1); 70846859ac8SHuacai Chen } 70946859ac8SHuacai Chen 71046859ac8SHuacai Chen static void flush_tlb_page_ipi(void *info) 71146859ac8SHuacai Chen { 71246859ac8SHuacai Chen struct flush_tlb_data *fd = info; 71346859ac8SHuacai Chen 71446859ac8SHuacai Chen local_flush_tlb_page(fd->vma, fd->addr1); 71546859ac8SHuacai Chen } 71646859ac8SHuacai Chen 71746859ac8SHuacai Chen void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) 71846859ac8SHuacai Chen { 71946859ac8SHuacai Chen preempt_disable(); 72046859ac8SHuacai Chen if ((atomic_read(&vma->vm_mm->mm_users) != 1) || (current->mm != vma->vm_mm)) { 72146859ac8SHuacai Chen struct flush_tlb_data fd = { 72246859ac8SHuacai Chen .vma = vma, 72346859ac8SHuacai Chen .addr1 = page, 72446859ac8SHuacai Chen }; 72546859ac8SHuacai Chen 72646859ac8SHuacai Chen on_each_cpu_mask(mm_cpumask(vma->vm_mm), flush_tlb_page_ipi, &fd, 1); 72746859ac8SHuacai Chen } else { 72846859ac8SHuacai Chen unsigned int cpu; 72946859ac8SHuacai Chen 73046859ac8SHuacai Chen for_each_online_cpu(cpu) { 73146859ac8SHuacai Chen if (cpu != smp_processor_id() && cpu_context(cpu, vma->vm_mm)) 732*d4b6f156SHuacai Chen cpu_context(cpu, vma->vm_mm) = 0; 73346859ac8SHuacai Chen } 73446859ac8SHuacai Chen local_flush_tlb_page(vma, page); 73546859ac8SHuacai Chen } 73646859ac8SHuacai Chen preempt_enable(); 73746859ac8SHuacai Chen } 73846859ac8SHuacai Chen EXPORT_SYMBOL(flush_tlb_page); 73946859ac8SHuacai Chen 74046859ac8SHuacai Chen static void flush_tlb_one_ipi(void *info) 74146859ac8SHuacai Chen { 74246859ac8SHuacai Chen unsigned long vaddr = (unsigned long) info; 74346859ac8SHuacai Chen 74446859ac8SHuacai Chen local_flush_tlb_one(vaddr); 74546859ac8SHuacai Chen } 74646859ac8SHuacai Chen 74746859ac8SHuacai Chen void flush_tlb_one(unsigned long vaddr) 74846859ac8SHuacai Chen { 74946859ac8SHuacai Chen on_each_cpu(flush_tlb_one_ipi, (void *)vaddr, 1); 75046859ac8SHuacai Chen } 75146859ac8SHuacai Chen EXPORT_SYMBOL(flush_tlb_one); 752