1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Author: Huacai Chen <chenhuacai@loongson.cn> 4 * Copyright (C) 2020 Loongson Technology Corporation Limited 5 */ 6 7#include <linux/acpi.h> 8#include <linux/init.h> 9#include <linux/cpu.h> 10#include <linux/sched.h> 11#include <linux/sched/hotplug.h> 12#include <linux/sched/task_stack.h> 13#include <linux/seq_file.h> 14#include <linux/smp.h> 15#include <linux/syscore_ops.h> 16#include <linux/tracepoint.h> 17#include <asm/processor.h> 18#include <asm/time.h> 19#include <asm/tlbflush.h> 20#include <asm/cacheflush.h> 21#include <loongson.h> 22 23struct secondary_data cpuboot_data; 24 25static DEFINE_PER_CPU(int, cpu_state); 26DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); 27EXPORT_PER_CPU_SYMBOL(irq_stat); 28 29#define MAX_CPUS 64 30 31#define STATUS 0x00 32#define EN 0x04 33#define SET 0x08 34#define CLEAR 0x0c 35#define MBUF 0x20 36 37extern unsigned long long smp_group[MAX_PACKAGES]; 38static u32 core_offsets[4] = {0x000, 0x100, 0x200, 0x300}; 39 40static volatile void *ipi_set_regs[MAX_CPUS]; 41static volatile void *ipi_clear_regs[MAX_CPUS]; 42static volatile void *ipi_status_regs[MAX_CPUS]; 43static volatile void *ipi_en_regs[MAX_CPUS]; 44static volatile void *ipi_mailbox_buf[MAX_CPUS]; 45 46static u32 (*ipi_read_clear)(int cpu); 47static void (*ipi_write_action)(int cpu, u32 action); 48 49enum ipi_msg_type { 50 IPI_RESCHEDULE, 51 IPI_CALL_FUNCTION, 52}; 53 54static const char *ipi_types[NR_IPI] __tracepoint_string = { 55 [IPI_RESCHEDULE] = "Rescheduling interrupts", 56 [IPI_CALL_FUNCTION] = "Function call interrupts", 57}; 58 59void show_ipi_list(struct seq_file *p, int prec) 60{ 61 unsigned int cpu, i; 62 63 for (i = 0; i < NR_IPI; i++) { 64 seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, prec >= 4 ? " " : ""); 65 for_each_online_cpu(cpu) 66 seq_printf(p, "%10u ", per_cpu(irq_stat, cpu).ipi_irqs[i]); 67 seq_printf(p, " LoongArch %d %s\n", i + 1, ipi_types[i]); 68 } 69} 70 71/* Send mail buffer via Mail_Send */ 72static void csr_mail_send(uint64_t data, int cpu, int mailbox) 73{ 74 uint64_t val; 75 76 /* Send high 32 bits */ 77 val = IOCSR_MBUF_SEND_BLOCKING; 78 val |= (IOCSR_MBUF_SEND_BOX_HI(mailbox) << IOCSR_MBUF_SEND_BOX_SHIFT); 79 val |= (cpu << IOCSR_MBUF_SEND_CPU_SHIFT); 80 val |= (data & IOCSR_MBUF_SEND_H32_MASK); 81 iocsr_write64(val, LOONGARCH_IOCSR_MBUF_SEND); 82 83 /* Send low 32 bits */ 84 val = IOCSR_MBUF_SEND_BLOCKING; 85 val |= (IOCSR_MBUF_SEND_BOX_LO(mailbox) << IOCSR_MBUF_SEND_BOX_SHIFT); 86 val |= (cpu << IOCSR_MBUF_SEND_CPU_SHIFT); 87 val |= (data << IOCSR_MBUF_SEND_BUF_SHIFT); 88 iocsr_write64(val, LOONGARCH_IOCSR_MBUF_SEND); 89}; 90 91static u32 csr_ipi_read_clear(int cpu) 92{ 93 u32 action; 94 95 /* Load the ipi register to figure out what we're supposed to do */ 96 action = iocsr_read32(LOONGARCH_IOCSR_IPI_STATUS); 97 /* Clear the ipi register to clear the interrupt */ 98 iocsr_write32(action, LOONGARCH_IOCSR_IPI_CLEAR); 99 100 return action; 101} 102 103static void csr_ipi_write_action(int cpu, u32 action) 104{ 105 unsigned int irq = 0; 106 107 while ((irq = ffs(action))) { 108 uint32_t val = IOCSR_IPI_SEND_BLOCKING; 109 val |= (irq - 1); 110 val |= (cpu << IOCSR_IPI_SEND_CPU_SHIFT); 111 iocsr_write32(val, LOONGARCH_IOCSR_IPI_SEND); 112 action &= ~BIT(irq - 1); 113 } 114} 115 116static u32 legacy_ipi_read_clear(int cpu) 117{ 118 u32 action; 119 120 /* Load the ipi register to figure out what we're supposed to do */ 121 action = xconf_readl(ipi_status_regs[cpu]); 122 /* Clear the ipi register to clear the interrupt */ 123 xconf_writel(action, ipi_clear_regs[cpu]); 124 125 return action; 126} 127 128static void legacy_ipi_write_action(int cpu, u32 action) 129{ 130 xconf_writel((u32)action, ipi_set_regs[cpu]); 131} 132 133static void ipi_method_init(void) 134{ 135 if (cpu_has_csripi) { 136 ipi_read_clear = csr_ipi_read_clear; 137 ipi_write_action = csr_ipi_write_action; 138 } else { 139 ipi_read_clear = legacy_ipi_read_clear; 140 ipi_write_action = legacy_ipi_write_action; 141 } 142} 143 144static void ipi_regaddrs_init(void) 145{ 146 int i, node, core; 147 148 for (i = 0; i< MAX_CPUS; i++) { 149 node = i / 4; 150 core = i % 4; 151 ipi_set_regs[i] = (void *) 152 (smp_group[node] + core_offsets[core] + SET); 153 ipi_clear_regs[i] = (void *) 154 (smp_group[node] + core_offsets[core] + CLEAR); 155 ipi_status_regs[i] = (void *) 156 (smp_group[node] + core_offsets[core] + STATUS); 157 ipi_en_regs[i] = (void *) 158 (smp_group[node] + core_offsets[core] + EN); 159 ipi_mailbox_buf[i] = (void *) 160 (smp_group[node] + core_offsets[core] + MBUF); 161 } 162} 163 164/* 165 * Simple enough, just poke the appropriate ipi register 166 */ 167static void loongson3_send_ipi_single(int cpu, unsigned int action) 168{ 169 ipi_write_action(cpu_logical_map(cpu), (u32)action); 170} 171 172static void 173loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action) 174{ 175 unsigned int i; 176 177 for_each_cpu(i, mask) 178 ipi_write_action(cpu_logical_map(i), (u32)action); 179} 180 181irqreturn_t loongson3_ipi_interrupt(int irq, void *dev) 182{ 183 unsigned int action; 184 unsigned int cpu = smp_processor_id(); 185 186 action = ipi_read_clear(cpu_logical_map(cpu)); 187 188 wbflush(); 189 190 if (action & SMP_RESCHEDULE) { 191 scheduler_ipi(); 192 per_cpu(irq_stat, cpu).ipi_irqs[IPI_RESCHEDULE]++; 193 } 194 195 if (action & SMP_CALL_FUNCTION) { 196 generic_smp_call_function_interrupt(); 197 per_cpu(irq_stat, cpu).ipi_irqs[IPI_CALL_FUNCTION]++; 198 } 199 200 return IRQ_HANDLED; 201} 202 203/* 204 * SMP init and finish on secondary CPUs 205 */ 206static void loongson3_init_secondary(void) 207{ 208 unsigned int cpu = smp_processor_id(); 209 unsigned int imask = ECFGF_IP0 | ECFGF_IP1 | ECFGF_IP2 | 210 ECFGF_IPI | ECFGF_PMC | ECFGF_TIMER; 211 212 change_csr_ecfg(ECFG0_IM, imask); 213 214 if (cpu_has_csripi) 215 iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_EN); 216 else 217 xconf_writel(0xffffffff, ipi_en_regs[cpu_logical_map(cpu)]); 218 219#ifdef CONFIG_NUMA 220 numa_add_cpu(cpu); 221#endif 222 per_cpu(cpu_state, cpu) = CPU_ONLINE; 223 cpu_data[cpu].package = 224 cpu_logical_map(cpu) / loongson_sysconf.cores_per_package; 225 cpu_data[cpu].core = pptt_enabled ? cpu_data[cpu].core : 226 cpu_logical_map(cpu) % loongson_sysconf.cores_per_package; 227} 228 229static void loongson3_smp_finish(void) 230{ 231 int cpu = smp_processor_id(); 232 233 local_irq_enable(); 234 235 if (cpu_has_csripi) 236 iocsr_write64(0, LOONGARCH_IOCSR_MBUF0); 237 else 238 xconf_writeq(0, ipi_mailbox_buf[cpu_logical_map(cpu)]+0x0); 239 240 pr_info("CPU#%d finished\n", smp_processor_id()); 241} 242 243static void __init loongson3_smp_setup(void) 244{ 245 ipi_method_init(); 246 ipi_regaddrs_init(); 247 248 if (cpu_has_csripi) 249 iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_EN); 250 else 251 xconf_writel(0xffffffff, ipi_en_regs[cpu_logical_map(0)]); 252 253 pr_info("Detected %i available CPU(s)\n", loongson_sysconf.nr_cpus); 254 255 cpu_data[0].core = cpu_logical_map(0) % loongson_sysconf.cores_per_package; 256 cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package; 257} 258 259static void __init loongson3_prepare_cpus(unsigned int max_cpus) 260{ 261 int i = 0; 262 263 parse_acpi_topology(); 264 265 for (i = 0; i < loongson_sysconf.nr_cpus; i++) { 266 set_cpu_present(i, true); 267 cpu_data[i].global_id = __cpu_logical_map[i]; 268 269 if (cpu_has_csripi) 270 csr_mail_send(0, __cpu_logical_map[i], 0); 271 else 272 xconf_writeq(0, ipi_mailbox_buf[__cpu_logical_map[i]]+0x0); 273 } 274 275 per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE; 276} 277 278/* 279 * Setup the PC, SP, and TP of a secondary processor and start it runing! 280 */ 281static int loongson3_boot_secondary(int cpu, struct task_struct *idle) 282{ 283 unsigned long entry; 284 285 pr_info("Booting CPU#%d...\n", cpu); 286 287 entry = __pa_symbol((unsigned long)&smpboot_entry); 288 cpuboot_data.stack = (unsigned long)__KSTK_TOS(idle); 289 cpuboot_data.thread_info = (unsigned long)task_thread_info(idle); 290 291 if (cpu_has_csripi) 292 csr_mail_send(entry, cpu_logical_map(cpu), 0); 293 else 294 xconf_writeq(entry, ipi_mailbox_buf[cpu_logical_map(cpu)]+0x0); 295 296 loongson3_send_ipi_single(cpu, SMP_BOOT_CPU); 297 298 return 0; 299} 300 301#ifdef CONFIG_HOTPLUG_CPU 302 303static bool io_master(int cpu) 304{ 305 int i, node, master; 306 307 if (cpu == 0) 308 return true; 309 310 for (i = 1; i < loongson_sysconf.nr_io_pics; i++) { 311 node = eiointc_get_node(i); 312 master = cpu_number_map(node * CORES_PER_EIO_NODE); 313 if (cpu == master) 314 return true; 315 } 316 317 return false; 318} 319 320static int loongson3_cpu_disable(void) 321{ 322 unsigned long flags; 323 unsigned int cpu = smp_processor_id(); 324 325 if (io_master(cpu)) 326 return -EBUSY; 327 328#ifdef CONFIG_NUMA 329 numa_remove_cpu(cpu); 330#endif 331 set_cpu_online(cpu, false); 332 clear_cpu_sibling_map(cpu); 333 calculate_cpu_foreign_map(); 334 local_irq_save(flags); 335 irq_migrate_all_off_this_cpu(); 336 clear_csr_ecfg(ECFG0_IM); 337 local_irq_restore(flags); 338 local_flush_tlb_all(); 339 340 return 0; 341} 342 343 344static void loongson3_cpu_die(unsigned int cpu) 345{ 346 while (per_cpu(cpu_state, cpu) != CPU_DEAD) 347 cpu_relax(); 348 349 mb(); 350} 351 352void __noreturn arch_cpu_idle_dead(void) 353{ 354 register long cpuid, core, node, count; 355 register void *addr, *base; 356 register void (*init_fn)(void); 357 358 idle_task_exit(); 359 local_irq_enable(); 360 change_csr_ecfg(ECFG0_IM, ECFGF_IPI); 361 __this_cpu_write(cpu_state, CPU_DEAD); 362 363 __smp_mb(); 364 __asm__ __volatile__( 365 " idle 0 \n" 366 " csrrd %[cpuid], 0x20 \n" 367 " andi %[cpuid], %[cpuid], 0x1ff \n" 368 " li.d %[base], 0x800000001fe01000 \n" 369 " andi %[core], %[cpuid], 0x3 \n" 370 " slli.w %[core], %[core], 8 \n" /* Get core id */ 371 " or %[base], %[base], %[core] \n" 372 " andi %[node], %[cpuid], 0x3c \n" 373 " slli.d %[node], %[node], 42 \n" /* Get node id */ 374 " or %[base], %[base], %[node] \n" 375 " ld.d %[init_fn], %[base], 0x20 \n" /* Get init PC */ 376 " nop \n" 377 : [core] "=&r" (core), [node] "=&r" (node), 378 [base] "=&r" (base), [cpuid] "=&r" (cpuid), 379 [count] "=&r" (count), [init_fn] "=&r" (addr) 380 : /* No Input */ 381 : "a1"); 382 383 local_irq_disable(); 384 init_fn = __va(addr); 385 386 init_fn(); 387 unreachable(); 388} 389 390#endif 391 392const struct plat_smp_ops loongson3_smp_ops = { 393 .send_ipi_single = loongson3_send_ipi_single, 394 .send_ipi_mask = loongson3_send_ipi_mask, 395 .smp_setup = loongson3_smp_setup, 396 .prepare_cpus = loongson3_prepare_cpus, 397 .boot_secondary = loongson3_boot_secondary, 398 .init_secondary = loongson3_init_secondary, 399 .smp_finish = loongson3_smp_finish, 400#ifdef CONFIG_HOTPLUG_CPU 401 .cpu_disable = loongson3_cpu_disable, 402 .cpu_die = loongson3_cpu_die, 403#endif 404}; 405 406/* 407 * Power management 408 */ 409#ifdef CONFIG_PM 410 411static int loongson3_ipi_suspend(void) 412{ 413 return 0; 414} 415 416static void loongson3_ipi_resume(void) 417{ 418 if (cpu_has_csripi) 419 iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_EN); 420 else 421 xconf_writel(0xffffffff, ipi_en_regs[cpu_logical_map(0)]); 422} 423 424static struct syscore_ops loongson3_ipi_syscore_ops = { 425 .resume = loongson3_ipi_resume, 426 .suspend = loongson3_ipi_suspend, 427}; 428 429/* 430 * Enable boot cpu ipi before enabling nonboot cpus 431 * during syscore_resume. 432 * */ 433static int __init ipi_pm_init(void) 434{ 435 register_syscore_ops(&loongson3_ipi_syscore_ops); 436 return 0; 437} 438 439core_initcall(ipi_pm_init); 440#endif 441