162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2010, 2011, 2012, Lemote, Inc. 462306a36Sopenharmony_ci * Author: Chen Huacai, chenhc@lemote.com 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <irq.h> 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/cpu.h> 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/sched/hotplug.h> 1262306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 1362306a36Sopenharmony_ci#include <linux/smp.h> 1462306a36Sopenharmony_ci#include <linux/cpufreq.h> 1562306a36Sopenharmony_ci#include <linux/kexec.h> 1662306a36Sopenharmony_ci#include <asm/processor.h> 1762306a36Sopenharmony_ci#include <asm/smp.h> 1862306a36Sopenharmony_ci#include <asm/time.h> 1962306a36Sopenharmony_ci#include <asm/tlbflush.h> 2062306a36Sopenharmony_ci#include <asm/cacheflush.h> 2162306a36Sopenharmony_ci#include <loongson.h> 2262306a36Sopenharmony_ci#include <loongson_regs.h> 2362306a36Sopenharmony_ci#include <workarounds.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "smp.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciDEFINE_PER_CPU(int, cpu_state); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define LS_IPI_IRQ (MIPS_CPU_IRQ_BASE + 6) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void __iomem *ipi_set0_regs[16]; 3262306a36Sopenharmony_cistatic void __iomem *ipi_clear0_regs[16]; 3362306a36Sopenharmony_cistatic void __iomem *ipi_status0_regs[16]; 3462306a36Sopenharmony_cistatic void __iomem *ipi_en0_regs[16]; 3562306a36Sopenharmony_cistatic void __iomem *ipi_mailbox_buf[16]; 3662306a36Sopenharmony_cistatic uint32_t core0_c0count[NR_CPUS]; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic u32 (*ipi_read_clear)(int cpu); 3962306a36Sopenharmony_cistatic void (*ipi_write_action)(int cpu, u32 action); 4062306a36Sopenharmony_cistatic void (*ipi_write_enable)(int cpu); 4162306a36Sopenharmony_cistatic void (*ipi_clear_buf)(int cpu); 4262306a36Sopenharmony_cistatic void (*ipi_write_buf)(int cpu, struct task_struct *idle); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* send mail via Mail_Send register for 3A4000+ CPU */ 4562306a36Sopenharmony_cistatic void csr_mail_send(uint64_t data, int cpu, int mailbox) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci uint64_t val; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* send high 32 bits */ 5062306a36Sopenharmony_ci val = CSR_MAIL_SEND_BLOCK; 5162306a36Sopenharmony_ci val |= (CSR_MAIL_SEND_BOX_HIGH(mailbox) << CSR_MAIL_SEND_BOX_SHIFT); 5262306a36Sopenharmony_ci val |= (cpu << CSR_MAIL_SEND_CPU_SHIFT); 5362306a36Sopenharmony_ci val |= (data & CSR_MAIL_SEND_H32_MASK); 5462306a36Sopenharmony_ci csr_writeq(val, LOONGSON_CSR_MAIL_SEND); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* send low 32 bits */ 5762306a36Sopenharmony_ci val = CSR_MAIL_SEND_BLOCK; 5862306a36Sopenharmony_ci val |= (CSR_MAIL_SEND_BOX_LOW(mailbox) << CSR_MAIL_SEND_BOX_SHIFT); 5962306a36Sopenharmony_ci val |= (cpu << CSR_MAIL_SEND_CPU_SHIFT); 6062306a36Sopenharmony_ci val |= (data << CSR_MAIL_SEND_BUF_SHIFT); 6162306a36Sopenharmony_ci csr_writeq(val, LOONGSON_CSR_MAIL_SEND); 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic u32 csr_ipi_read_clear(int cpu) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci u32 action; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Load the ipi register to figure out what we're supposed to do */ 6962306a36Sopenharmony_ci action = csr_readl(LOONGSON_CSR_IPI_STATUS); 7062306a36Sopenharmony_ci /* Clear the ipi register to clear the interrupt */ 7162306a36Sopenharmony_ci csr_writel(action, LOONGSON_CSR_IPI_CLEAR); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return action; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void csr_ipi_write_action(int cpu, u32 action) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci unsigned int irq = 0; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci while ((irq = ffs(action))) { 8162306a36Sopenharmony_ci uint32_t val = CSR_IPI_SEND_BLOCK; 8262306a36Sopenharmony_ci val |= (irq - 1); 8362306a36Sopenharmony_ci val |= (cpu << CSR_IPI_SEND_CPU_SHIFT); 8462306a36Sopenharmony_ci csr_writel(val, LOONGSON_CSR_IPI_SEND); 8562306a36Sopenharmony_ci action &= ~BIT(irq - 1); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void csr_ipi_write_enable(int cpu) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci csr_writel(0xffffffff, LOONGSON_CSR_IPI_EN); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void csr_ipi_clear_buf(int cpu) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci csr_writeq(0, LOONGSON_CSR_MAIL_BUF0); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void csr_ipi_write_buf(int cpu, struct task_struct *idle) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci unsigned long startargs[4]; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* startargs[] are initial PC, SP and GP for secondary CPU */ 10462306a36Sopenharmony_ci startargs[0] = (unsigned long)&smp_bootstrap; 10562306a36Sopenharmony_ci startargs[1] = (unsigned long)__KSTK_TOS(idle); 10662306a36Sopenharmony_ci startargs[2] = (unsigned long)task_thread_info(idle); 10762306a36Sopenharmony_ci startargs[3] = 0; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci pr_debug("CPU#%d, func_pc=%lx, sp=%lx, gp=%lx\n", 11062306a36Sopenharmony_ci cpu, startargs[0], startargs[1], startargs[2]); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci csr_mail_send(startargs[3], cpu_logical_map(cpu), 3); 11362306a36Sopenharmony_ci csr_mail_send(startargs[2], cpu_logical_map(cpu), 2); 11462306a36Sopenharmony_ci csr_mail_send(startargs[1], cpu_logical_map(cpu), 1); 11562306a36Sopenharmony_ci csr_mail_send(startargs[0], cpu_logical_map(cpu), 0); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic u32 legacy_ipi_read_clear(int cpu) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci u32 action; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Load the ipi register to figure out what we're supposed to do */ 12362306a36Sopenharmony_ci action = readl_relaxed(ipi_status0_regs[cpu_logical_map(cpu)]); 12462306a36Sopenharmony_ci /* Clear the ipi register to clear the interrupt */ 12562306a36Sopenharmony_ci writel_relaxed(action, ipi_clear0_regs[cpu_logical_map(cpu)]); 12662306a36Sopenharmony_ci nudge_writes(); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return action; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void legacy_ipi_write_action(int cpu, u32 action) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci writel_relaxed((u32)action, ipi_set0_regs[cpu]); 13462306a36Sopenharmony_ci nudge_writes(); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void legacy_ipi_write_enable(int cpu) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci writel_relaxed(0xffffffff, ipi_en0_regs[cpu_logical_map(cpu)]); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void legacy_ipi_clear_buf(int cpu) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci writeq_relaxed(0, ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x0); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void legacy_ipi_write_buf(int cpu, struct task_struct *idle) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci unsigned long startargs[4]; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* startargs[] are initial PC, SP and GP for secondary CPU */ 15262306a36Sopenharmony_ci startargs[0] = (unsigned long)&smp_bootstrap; 15362306a36Sopenharmony_ci startargs[1] = (unsigned long)__KSTK_TOS(idle); 15462306a36Sopenharmony_ci startargs[2] = (unsigned long)task_thread_info(idle); 15562306a36Sopenharmony_ci startargs[3] = 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci pr_debug("CPU#%d, func_pc=%lx, sp=%lx, gp=%lx\n", 15862306a36Sopenharmony_ci cpu, startargs[0], startargs[1], startargs[2]); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci writeq_relaxed(startargs[3], 16162306a36Sopenharmony_ci ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x18); 16262306a36Sopenharmony_ci writeq_relaxed(startargs[2], 16362306a36Sopenharmony_ci ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x10); 16462306a36Sopenharmony_ci writeq_relaxed(startargs[1], 16562306a36Sopenharmony_ci ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x8); 16662306a36Sopenharmony_ci writeq_relaxed(startargs[0], 16762306a36Sopenharmony_ci ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x0); 16862306a36Sopenharmony_ci nudge_writes(); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void csr_ipi_probe(void) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci if (cpu_has_csr() && csr_readl(LOONGSON_CSR_FEATURES) & LOONGSON_CSRF_IPI) { 17462306a36Sopenharmony_ci ipi_read_clear = csr_ipi_read_clear; 17562306a36Sopenharmony_ci ipi_write_action = csr_ipi_write_action; 17662306a36Sopenharmony_ci ipi_write_enable = csr_ipi_write_enable; 17762306a36Sopenharmony_ci ipi_clear_buf = csr_ipi_clear_buf; 17862306a36Sopenharmony_ci ipi_write_buf = csr_ipi_write_buf; 17962306a36Sopenharmony_ci } else { 18062306a36Sopenharmony_ci ipi_read_clear = legacy_ipi_read_clear; 18162306a36Sopenharmony_ci ipi_write_action = legacy_ipi_write_action; 18262306a36Sopenharmony_ci ipi_write_enable = legacy_ipi_write_enable; 18362306a36Sopenharmony_ci ipi_clear_buf = legacy_ipi_clear_buf; 18462306a36Sopenharmony_ci ipi_write_buf = legacy_ipi_write_buf; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void ipi_set0_regs_init(void) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci ipi_set0_regs[0] = (void __iomem *) 19162306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + SET0); 19262306a36Sopenharmony_ci ipi_set0_regs[1] = (void __iomem *) 19362306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + SET0); 19462306a36Sopenharmony_ci ipi_set0_regs[2] = (void __iomem *) 19562306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + SET0); 19662306a36Sopenharmony_ci ipi_set0_regs[3] = (void __iomem *) 19762306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + SET0); 19862306a36Sopenharmony_ci ipi_set0_regs[4] = (void __iomem *) 19962306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + SET0); 20062306a36Sopenharmony_ci ipi_set0_regs[5] = (void __iomem *) 20162306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + SET0); 20262306a36Sopenharmony_ci ipi_set0_regs[6] = (void __iomem *) 20362306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + SET0); 20462306a36Sopenharmony_ci ipi_set0_regs[7] = (void __iomem *) 20562306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + SET0); 20662306a36Sopenharmony_ci ipi_set0_regs[8] = (void __iomem *) 20762306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + SET0); 20862306a36Sopenharmony_ci ipi_set0_regs[9] = (void __iomem *) 20962306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + SET0); 21062306a36Sopenharmony_ci ipi_set0_regs[10] = (void __iomem *) 21162306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + SET0); 21262306a36Sopenharmony_ci ipi_set0_regs[11] = (void __iomem *) 21362306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + SET0); 21462306a36Sopenharmony_ci ipi_set0_regs[12] = (void __iomem *) 21562306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + SET0); 21662306a36Sopenharmony_ci ipi_set0_regs[13] = (void __iomem *) 21762306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + SET0); 21862306a36Sopenharmony_ci ipi_set0_regs[14] = (void __iomem *) 21962306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + SET0); 22062306a36Sopenharmony_ci ipi_set0_regs[15] = (void __iomem *) 22162306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + SET0); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void ipi_clear0_regs_init(void) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci ipi_clear0_regs[0] = (void __iomem *) 22762306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + CLEAR0); 22862306a36Sopenharmony_ci ipi_clear0_regs[1] = (void __iomem *) 22962306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + CLEAR0); 23062306a36Sopenharmony_ci ipi_clear0_regs[2] = (void __iomem *) 23162306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + CLEAR0); 23262306a36Sopenharmony_ci ipi_clear0_regs[3] = (void __iomem *) 23362306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + CLEAR0); 23462306a36Sopenharmony_ci ipi_clear0_regs[4] = (void __iomem *) 23562306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + CLEAR0); 23662306a36Sopenharmony_ci ipi_clear0_regs[5] = (void __iomem *) 23762306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + CLEAR0); 23862306a36Sopenharmony_ci ipi_clear0_regs[6] = (void __iomem *) 23962306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + CLEAR0); 24062306a36Sopenharmony_ci ipi_clear0_regs[7] = (void __iomem *) 24162306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + CLEAR0); 24262306a36Sopenharmony_ci ipi_clear0_regs[8] = (void __iomem *) 24362306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + CLEAR0); 24462306a36Sopenharmony_ci ipi_clear0_regs[9] = (void __iomem *) 24562306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + CLEAR0); 24662306a36Sopenharmony_ci ipi_clear0_regs[10] = (void __iomem *) 24762306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + CLEAR0); 24862306a36Sopenharmony_ci ipi_clear0_regs[11] = (void __iomem *) 24962306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + CLEAR0); 25062306a36Sopenharmony_ci ipi_clear0_regs[12] = (void __iomem *) 25162306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + CLEAR0); 25262306a36Sopenharmony_ci ipi_clear0_regs[13] = (void __iomem *) 25362306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + CLEAR0); 25462306a36Sopenharmony_ci ipi_clear0_regs[14] = (void __iomem *) 25562306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + CLEAR0); 25662306a36Sopenharmony_ci ipi_clear0_regs[15] = (void __iomem *) 25762306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + CLEAR0); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void ipi_status0_regs_init(void) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci ipi_status0_regs[0] = (void __iomem *) 26362306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + STATUS0); 26462306a36Sopenharmony_ci ipi_status0_regs[1] = (void __iomem *) 26562306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + STATUS0); 26662306a36Sopenharmony_ci ipi_status0_regs[2] = (void __iomem *) 26762306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + STATUS0); 26862306a36Sopenharmony_ci ipi_status0_regs[3] = (void __iomem *) 26962306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + STATUS0); 27062306a36Sopenharmony_ci ipi_status0_regs[4] = (void __iomem *) 27162306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + STATUS0); 27262306a36Sopenharmony_ci ipi_status0_regs[5] = (void __iomem *) 27362306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + STATUS0); 27462306a36Sopenharmony_ci ipi_status0_regs[6] = (void __iomem *) 27562306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + STATUS0); 27662306a36Sopenharmony_ci ipi_status0_regs[7] = (void __iomem *) 27762306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + STATUS0); 27862306a36Sopenharmony_ci ipi_status0_regs[8] = (void __iomem *) 27962306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + STATUS0); 28062306a36Sopenharmony_ci ipi_status0_regs[9] = (void __iomem *) 28162306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + STATUS0); 28262306a36Sopenharmony_ci ipi_status0_regs[10] = (void __iomem *) 28362306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + STATUS0); 28462306a36Sopenharmony_ci ipi_status0_regs[11] = (void __iomem *) 28562306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + STATUS0); 28662306a36Sopenharmony_ci ipi_status0_regs[12] = (void __iomem *) 28762306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + STATUS0); 28862306a36Sopenharmony_ci ipi_status0_regs[13] = (void __iomem *) 28962306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + STATUS0); 29062306a36Sopenharmony_ci ipi_status0_regs[14] = (void __iomem *) 29162306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + STATUS0); 29262306a36Sopenharmony_ci ipi_status0_regs[15] = (void __iomem *) 29362306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + STATUS0); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic void ipi_en0_regs_init(void) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci ipi_en0_regs[0] = (void __iomem *) 29962306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + EN0); 30062306a36Sopenharmony_ci ipi_en0_regs[1] = (void __iomem *) 30162306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + EN0); 30262306a36Sopenharmony_ci ipi_en0_regs[2] = (void __iomem *) 30362306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + EN0); 30462306a36Sopenharmony_ci ipi_en0_regs[3] = (void __iomem *) 30562306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + EN0); 30662306a36Sopenharmony_ci ipi_en0_regs[4] = (void __iomem *) 30762306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + EN0); 30862306a36Sopenharmony_ci ipi_en0_regs[5] = (void __iomem *) 30962306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + EN0); 31062306a36Sopenharmony_ci ipi_en0_regs[6] = (void __iomem *) 31162306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + EN0); 31262306a36Sopenharmony_ci ipi_en0_regs[7] = (void __iomem *) 31362306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + EN0); 31462306a36Sopenharmony_ci ipi_en0_regs[8] = (void __iomem *) 31562306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + EN0); 31662306a36Sopenharmony_ci ipi_en0_regs[9] = (void __iomem *) 31762306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + EN0); 31862306a36Sopenharmony_ci ipi_en0_regs[10] = (void __iomem *) 31962306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + EN0); 32062306a36Sopenharmony_ci ipi_en0_regs[11] = (void __iomem *) 32162306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + EN0); 32262306a36Sopenharmony_ci ipi_en0_regs[12] = (void __iomem *) 32362306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + EN0); 32462306a36Sopenharmony_ci ipi_en0_regs[13] = (void __iomem *) 32562306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + EN0); 32662306a36Sopenharmony_ci ipi_en0_regs[14] = (void __iomem *) 32762306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + EN0); 32862306a36Sopenharmony_ci ipi_en0_regs[15] = (void __iomem *) 32962306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + EN0); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void ipi_mailbox_buf_init(void) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci ipi_mailbox_buf[0] = (void __iomem *) 33562306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + BUF); 33662306a36Sopenharmony_ci ipi_mailbox_buf[1] = (void __iomem *) 33762306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + BUF); 33862306a36Sopenharmony_ci ipi_mailbox_buf[2] = (void __iomem *) 33962306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + BUF); 34062306a36Sopenharmony_ci ipi_mailbox_buf[3] = (void __iomem *) 34162306a36Sopenharmony_ci (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + BUF); 34262306a36Sopenharmony_ci ipi_mailbox_buf[4] = (void __iomem *) 34362306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + BUF); 34462306a36Sopenharmony_ci ipi_mailbox_buf[5] = (void __iomem *) 34562306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + BUF); 34662306a36Sopenharmony_ci ipi_mailbox_buf[6] = (void __iomem *) 34762306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + BUF); 34862306a36Sopenharmony_ci ipi_mailbox_buf[7] = (void __iomem *) 34962306a36Sopenharmony_ci (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + BUF); 35062306a36Sopenharmony_ci ipi_mailbox_buf[8] = (void __iomem *) 35162306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + BUF); 35262306a36Sopenharmony_ci ipi_mailbox_buf[9] = (void __iomem *) 35362306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + BUF); 35462306a36Sopenharmony_ci ipi_mailbox_buf[10] = (void __iomem *) 35562306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + BUF); 35662306a36Sopenharmony_ci ipi_mailbox_buf[11] = (void __iomem *) 35762306a36Sopenharmony_ci (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + BUF); 35862306a36Sopenharmony_ci ipi_mailbox_buf[12] = (void __iomem *) 35962306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + BUF); 36062306a36Sopenharmony_ci ipi_mailbox_buf[13] = (void __iomem *) 36162306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + BUF); 36262306a36Sopenharmony_ci ipi_mailbox_buf[14] = (void __iomem *) 36362306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + BUF); 36462306a36Sopenharmony_ci ipi_mailbox_buf[15] = (void __iomem *) 36562306a36Sopenharmony_ci (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + BUF); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* 36962306a36Sopenharmony_ci * Simple enough, just poke the appropriate ipi register 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_cistatic void loongson3_send_ipi_single(int cpu, unsigned int action) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci ipi_write_action(cpu_logical_map(cpu), (u32)action); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic void 37762306a36Sopenharmony_ciloongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci unsigned int i; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci for_each_cpu(i, mask) 38262306a36Sopenharmony_ci ipi_write_action(cpu_logical_map(i), (u32)action); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic irqreturn_t loongson3_ipi_interrupt(int irq, void *dev_id) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci int i, cpu = smp_processor_id(); 38962306a36Sopenharmony_ci unsigned int action, c0count; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci action = ipi_read_clear(cpu); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (action & SMP_RESCHEDULE_YOURSELF) 39462306a36Sopenharmony_ci scheduler_ipi(); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (action & SMP_CALL_FUNCTION) { 39762306a36Sopenharmony_ci irq_enter(); 39862306a36Sopenharmony_ci generic_smp_call_function_interrupt(); 39962306a36Sopenharmony_ci irq_exit(); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (action & SMP_ASK_C0COUNT) { 40362306a36Sopenharmony_ci BUG_ON(cpu != 0); 40462306a36Sopenharmony_ci c0count = read_c0_count(); 40562306a36Sopenharmony_ci c0count = c0count ? c0count : 1; 40662306a36Sopenharmony_ci for (i = 1; i < nr_cpu_ids; i++) 40762306a36Sopenharmony_ci core0_c0count[i] = c0count; 40862306a36Sopenharmony_ci nudge_writes(); /* Let others see the result ASAP */ 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return IRQ_HANDLED; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci#define MAX_LOOPS 800 41562306a36Sopenharmony_ci/* 41662306a36Sopenharmony_ci * SMP init and finish on secondary CPUs 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_cistatic void loongson3_init_secondary(void) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci int i; 42162306a36Sopenharmony_ci uint32_t initcount; 42262306a36Sopenharmony_ci unsigned int cpu = smp_processor_id(); 42362306a36Sopenharmony_ci unsigned int imask = STATUSF_IP7 | STATUSF_IP6 | 42462306a36Sopenharmony_ci STATUSF_IP3 | STATUSF_IP2; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Set interrupt mask, but don't enable */ 42762306a36Sopenharmony_ci change_c0_status(ST0_IM, imask); 42862306a36Sopenharmony_ci ipi_write_enable(cpu); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci per_cpu(cpu_state, cpu) = CPU_ONLINE; 43162306a36Sopenharmony_ci cpu_set_core(&cpu_data[cpu], 43262306a36Sopenharmony_ci cpu_logical_map(cpu) % loongson_sysconf.cores_per_package); 43362306a36Sopenharmony_ci cpu_data[cpu].package = 43462306a36Sopenharmony_ci cpu_logical_map(cpu) / loongson_sysconf.cores_per_package; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci i = 0; 43762306a36Sopenharmony_ci core0_c0count[cpu] = 0; 43862306a36Sopenharmony_ci loongson3_send_ipi_single(0, SMP_ASK_C0COUNT); 43962306a36Sopenharmony_ci while (!core0_c0count[cpu]) { 44062306a36Sopenharmony_ci i++; 44162306a36Sopenharmony_ci cpu_relax(); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (i > MAX_LOOPS) 44562306a36Sopenharmony_ci i = MAX_LOOPS; 44662306a36Sopenharmony_ci if (cpu_data[cpu].package) 44762306a36Sopenharmony_ci initcount = core0_c0count[cpu] + i; 44862306a36Sopenharmony_ci else /* Local access is faster for loops */ 44962306a36Sopenharmony_ci initcount = core0_c0count[cpu] + i/2; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci write_c0_count(initcount); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic void loongson3_smp_finish(void) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci int cpu = smp_processor_id(); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); 45962306a36Sopenharmony_ci local_irq_enable(); 46062306a36Sopenharmony_ci ipi_clear_buf(cpu); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci pr_info("CPU#%d finished, CP0_ST=%x\n", 46362306a36Sopenharmony_ci smp_processor_id(), read_c0_status()); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic void __init loongson3_smp_setup(void) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci int i = 0, num = 0; /* i: physical id, num: logical id */ 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci init_cpu_possible(cpu_none_mask); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* For unified kernel, NR_CPUS is the maximum possible value, 47362306a36Sopenharmony_ci * loongson_sysconf.nr_cpus is the really present value 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_ci while (i < loongson_sysconf.nr_cpus) { 47662306a36Sopenharmony_ci if (loongson_sysconf.reserved_cpus_mask & (1<<i)) { 47762306a36Sopenharmony_ci /* Reserved physical CPU cores */ 47862306a36Sopenharmony_ci __cpu_number_map[i] = -1; 47962306a36Sopenharmony_ci } else { 48062306a36Sopenharmony_ci __cpu_number_map[i] = num; 48162306a36Sopenharmony_ci __cpu_logical_map[num] = i; 48262306a36Sopenharmony_ci set_cpu_possible(num, true); 48362306a36Sopenharmony_ci /* Loongson processors are always grouped by 4 */ 48462306a36Sopenharmony_ci cpu_set_cluster(&cpu_data[num], i / 4); 48562306a36Sopenharmony_ci num++; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci i++; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci pr_info("Detected %i available CPU(s)\n", num); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci while (num < loongson_sysconf.nr_cpus) { 49262306a36Sopenharmony_ci __cpu_logical_map[num] = -1; 49362306a36Sopenharmony_ci num++; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci csr_ipi_probe(); 49762306a36Sopenharmony_ci ipi_set0_regs_init(); 49862306a36Sopenharmony_ci ipi_clear0_regs_init(); 49962306a36Sopenharmony_ci ipi_status0_regs_init(); 50062306a36Sopenharmony_ci ipi_en0_regs_init(); 50162306a36Sopenharmony_ci ipi_mailbox_buf_init(); 50262306a36Sopenharmony_ci ipi_write_enable(0); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci cpu_set_core(&cpu_data[0], 50562306a36Sopenharmony_ci cpu_logical_map(0) % loongson_sysconf.cores_per_package); 50662306a36Sopenharmony_ci cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic void __init loongson3_prepare_cpus(unsigned int max_cpus) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci if (request_irq(LS_IPI_IRQ, loongson3_ipi_interrupt, 51262306a36Sopenharmony_ci IRQF_PERCPU | IRQF_NO_SUSPEND, "SMP_IPI", NULL)) 51362306a36Sopenharmony_ci pr_err("Failed to request IPI IRQ\n"); 51462306a36Sopenharmony_ci init_cpu_present(cpu_possible_mask); 51562306a36Sopenharmony_ci per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/* 51962306a36Sopenharmony_ci * Setup the PC, SP, and GP of a secondary processor and start it runing! 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_cistatic int loongson3_boot_secondary(int cpu, struct task_struct *idle) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci pr_info("Booting CPU#%d...\n", cpu); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ipi_write_buf(cpu, idle); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return 0; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int loongson3_cpu_disable(void) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci unsigned long flags; 53562306a36Sopenharmony_ci unsigned int cpu = smp_processor_id(); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci set_cpu_online(cpu, false); 53862306a36Sopenharmony_ci calculate_cpu_foreign_map(); 53962306a36Sopenharmony_ci local_irq_save(flags); 54062306a36Sopenharmony_ci clear_c0_status(ST0_IM); 54162306a36Sopenharmony_ci local_irq_restore(flags); 54262306a36Sopenharmony_ci local_flush_tlb_all(); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic void loongson3_cpu_die(unsigned int cpu) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci while (per_cpu(cpu_state, cpu) != CPU_DEAD) 55162306a36Sopenharmony_ci cpu_relax(); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci mb(); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci/* To shutdown a core in Loongson 3, the target core should go to CKSEG1 and 55762306a36Sopenharmony_ci * flush all L1 entries at first. Then, another core (usually Core 0) can 55862306a36Sopenharmony_ci * safely disable the clock of the target core. loongson3_play_dead() is 55962306a36Sopenharmony_ci * called via CKSEG1 (uncached and unmmaped) 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_cistatic void loongson3_type1_play_dead(int *state_addr) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci register int val; 56462306a36Sopenharmony_ci register long cpuid, core, node, count; 56562306a36Sopenharmony_ci register void *addr, *base, *initfunc; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci __asm__ __volatile__( 56862306a36Sopenharmony_ci " .set push \n" 56962306a36Sopenharmony_ci " .set noreorder \n" 57062306a36Sopenharmony_ci " li %[addr], 0x80000000 \n" /* KSEG0 */ 57162306a36Sopenharmony_ci "1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */ 57262306a36Sopenharmony_ci " cache 0, 1(%[addr]) \n" 57362306a36Sopenharmony_ci " cache 0, 2(%[addr]) \n" 57462306a36Sopenharmony_ci " cache 0, 3(%[addr]) \n" 57562306a36Sopenharmony_ci " cache 1, 0(%[addr]) \n" /* flush L1 DCache */ 57662306a36Sopenharmony_ci " cache 1, 1(%[addr]) \n" 57762306a36Sopenharmony_ci " cache 1, 2(%[addr]) \n" 57862306a36Sopenharmony_ci " cache 1, 3(%[addr]) \n" 57962306a36Sopenharmony_ci " addiu %[sets], %[sets], -1 \n" 58062306a36Sopenharmony_ci " bnez %[sets], 1b \n" 58162306a36Sopenharmony_ci " addiu %[addr], %[addr], 0x20 \n" 58262306a36Sopenharmony_ci " li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */ 58362306a36Sopenharmony_ci " sw %[val], (%[state_addr]) \n" 58462306a36Sopenharmony_ci " sync \n" 58562306a36Sopenharmony_ci " cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */ 58662306a36Sopenharmony_ci " .set pop \n" 58762306a36Sopenharmony_ci : [addr] "=&r" (addr), [val] "=&r" (val) 58862306a36Sopenharmony_ci : [state_addr] "r" (state_addr), 58962306a36Sopenharmony_ci [sets] "r" (cpu_data[smp_processor_id()].dcache.sets)); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci __asm__ __volatile__( 59262306a36Sopenharmony_ci " .set push \n" 59362306a36Sopenharmony_ci " .set noreorder \n" 59462306a36Sopenharmony_ci " .set mips64 \n" 59562306a36Sopenharmony_ci " mfc0 %[cpuid], $15, 1 \n" 59662306a36Sopenharmony_ci " andi %[cpuid], 0x3ff \n" 59762306a36Sopenharmony_ci " dli %[base], 0x900000003ff01000 \n" 59862306a36Sopenharmony_ci " andi %[core], %[cpuid], 0x3 \n" 59962306a36Sopenharmony_ci " sll %[core], 8 \n" /* get core id */ 60062306a36Sopenharmony_ci " or %[base], %[base], %[core] \n" 60162306a36Sopenharmony_ci " andi %[node], %[cpuid], 0xc \n" 60262306a36Sopenharmony_ci " dsll %[node], 42 \n" /* get node id */ 60362306a36Sopenharmony_ci " or %[base], %[base], %[node] \n" 60462306a36Sopenharmony_ci "1: li %[count], 0x100 \n" /* wait for init loop */ 60562306a36Sopenharmony_ci "2: bnez %[count], 2b \n" /* limit mailbox access */ 60662306a36Sopenharmony_ci " addiu %[count], -1 \n" 60762306a36Sopenharmony_ci " ld %[initfunc], 0x20(%[base]) \n" /* get PC via mailbox */ 60862306a36Sopenharmony_ci " beqz %[initfunc], 1b \n" 60962306a36Sopenharmony_ci " nop \n" 61062306a36Sopenharmony_ci " ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */ 61162306a36Sopenharmony_ci " ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */ 61262306a36Sopenharmony_ci " ld $a1, 0x38(%[base]) \n" 61362306a36Sopenharmony_ci " jr %[initfunc] \n" /* jump to initial PC */ 61462306a36Sopenharmony_ci " nop \n" 61562306a36Sopenharmony_ci " .set pop \n" 61662306a36Sopenharmony_ci : [core] "=&r" (core), [node] "=&r" (node), 61762306a36Sopenharmony_ci [base] "=&r" (base), [cpuid] "=&r" (cpuid), 61862306a36Sopenharmony_ci [count] "=&r" (count), [initfunc] "=&r" (initfunc) 61962306a36Sopenharmony_ci : /* No Input */ 62062306a36Sopenharmony_ci : "a1"); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic void loongson3_type2_play_dead(int *state_addr) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci register int val; 62662306a36Sopenharmony_ci register long cpuid, core, node, count; 62762306a36Sopenharmony_ci register void *addr, *base, *initfunc; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci __asm__ __volatile__( 63062306a36Sopenharmony_ci " .set push \n" 63162306a36Sopenharmony_ci " .set noreorder \n" 63262306a36Sopenharmony_ci " li %[addr], 0x80000000 \n" /* KSEG0 */ 63362306a36Sopenharmony_ci "1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */ 63462306a36Sopenharmony_ci " cache 0, 1(%[addr]) \n" 63562306a36Sopenharmony_ci " cache 0, 2(%[addr]) \n" 63662306a36Sopenharmony_ci " cache 0, 3(%[addr]) \n" 63762306a36Sopenharmony_ci " cache 1, 0(%[addr]) \n" /* flush L1 DCache */ 63862306a36Sopenharmony_ci " cache 1, 1(%[addr]) \n" 63962306a36Sopenharmony_ci " cache 1, 2(%[addr]) \n" 64062306a36Sopenharmony_ci " cache 1, 3(%[addr]) \n" 64162306a36Sopenharmony_ci " addiu %[sets], %[sets], -1 \n" 64262306a36Sopenharmony_ci " bnez %[sets], 1b \n" 64362306a36Sopenharmony_ci " addiu %[addr], %[addr], 0x20 \n" 64462306a36Sopenharmony_ci " li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */ 64562306a36Sopenharmony_ci " sw %[val], (%[state_addr]) \n" 64662306a36Sopenharmony_ci " sync \n" 64762306a36Sopenharmony_ci " cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */ 64862306a36Sopenharmony_ci " .set pop \n" 64962306a36Sopenharmony_ci : [addr] "=&r" (addr), [val] "=&r" (val) 65062306a36Sopenharmony_ci : [state_addr] "r" (state_addr), 65162306a36Sopenharmony_ci [sets] "r" (cpu_data[smp_processor_id()].dcache.sets)); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci __asm__ __volatile__( 65462306a36Sopenharmony_ci " .set push \n" 65562306a36Sopenharmony_ci " .set noreorder \n" 65662306a36Sopenharmony_ci " .set mips64 \n" 65762306a36Sopenharmony_ci " mfc0 %[cpuid], $15, 1 \n" 65862306a36Sopenharmony_ci " andi %[cpuid], 0x3ff \n" 65962306a36Sopenharmony_ci " dli %[base], 0x900000003ff01000 \n" 66062306a36Sopenharmony_ci " andi %[core], %[cpuid], 0x3 \n" 66162306a36Sopenharmony_ci " sll %[core], 8 \n" /* get core id */ 66262306a36Sopenharmony_ci " or %[base], %[base], %[core] \n" 66362306a36Sopenharmony_ci " andi %[node], %[cpuid], 0xc \n" 66462306a36Sopenharmony_ci " dsll %[node], 42 \n" /* get node id */ 66562306a36Sopenharmony_ci " or %[base], %[base], %[node] \n" 66662306a36Sopenharmony_ci " dsrl %[node], 30 \n" /* 15:14 */ 66762306a36Sopenharmony_ci " or %[base], %[base], %[node] \n" 66862306a36Sopenharmony_ci "1: li %[count], 0x100 \n" /* wait for init loop */ 66962306a36Sopenharmony_ci "2: bnez %[count], 2b \n" /* limit mailbox access */ 67062306a36Sopenharmony_ci " addiu %[count], -1 \n" 67162306a36Sopenharmony_ci " ld %[initfunc], 0x20(%[base]) \n" /* get PC via mailbox */ 67262306a36Sopenharmony_ci " beqz %[initfunc], 1b \n" 67362306a36Sopenharmony_ci " nop \n" 67462306a36Sopenharmony_ci " ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */ 67562306a36Sopenharmony_ci " ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */ 67662306a36Sopenharmony_ci " ld $a1, 0x38(%[base]) \n" 67762306a36Sopenharmony_ci " jr %[initfunc] \n" /* jump to initial PC */ 67862306a36Sopenharmony_ci " nop \n" 67962306a36Sopenharmony_ci " .set pop \n" 68062306a36Sopenharmony_ci : [core] "=&r" (core), [node] "=&r" (node), 68162306a36Sopenharmony_ci [base] "=&r" (base), [cpuid] "=&r" (cpuid), 68262306a36Sopenharmony_ci [count] "=&r" (count), [initfunc] "=&r" (initfunc) 68362306a36Sopenharmony_ci : /* No Input */ 68462306a36Sopenharmony_ci : "a1"); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic void loongson3_type3_play_dead(int *state_addr) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci register int val; 69062306a36Sopenharmony_ci register long cpuid, core, node, count; 69162306a36Sopenharmony_ci register void *addr, *base, *initfunc; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci __asm__ __volatile__( 69462306a36Sopenharmony_ci " .set push \n" 69562306a36Sopenharmony_ci " .set noreorder \n" 69662306a36Sopenharmony_ci " li %[addr], 0x80000000 \n" /* KSEG0 */ 69762306a36Sopenharmony_ci "1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */ 69862306a36Sopenharmony_ci " cache 0, 1(%[addr]) \n" 69962306a36Sopenharmony_ci " cache 0, 2(%[addr]) \n" 70062306a36Sopenharmony_ci " cache 0, 3(%[addr]) \n" 70162306a36Sopenharmony_ci " cache 1, 0(%[addr]) \n" /* flush L1 DCache */ 70262306a36Sopenharmony_ci " cache 1, 1(%[addr]) \n" 70362306a36Sopenharmony_ci " cache 1, 2(%[addr]) \n" 70462306a36Sopenharmony_ci " cache 1, 3(%[addr]) \n" 70562306a36Sopenharmony_ci " addiu %[sets], %[sets], -1 \n" 70662306a36Sopenharmony_ci " bnez %[sets], 1b \n" 70762306a36Sopenharmony_ci " addiu %[addr], %[addr], 0x40 \n" 70862306a36Sopenharmony_ci " li %[addr], 0x80000000 \n" /* KSEG0 */ 70962306a36Sopenharmony_ci "2: cache 2, 0(%[addr]) \n" /* flush L1 VCache */ 71062306a36Sopenharmony_ci " cache 2, 1(%[addr]) \n" 71162306a36Sopenharmony_ci " cache 2, 2(%[addr]) \n" 71262306a36Sopenharmony_ci " cache 2, 3(%[addr]) \n" 71362306a36Sopenharmony_ci " cache 2, 4(%[addr]) \n" 71462306a36Sopenharmony_ci " cache 2, 5(%[addr]) \n" 71562306a36Sopenharmony_ci " cache 2, 6(%[addr]) \n" 71662306a36Sopenharmony_ci " cache 2, 7(%[addr]) \n" 71762306a36Sopenharmony_ci " cache 2, 8(%[addr]) \n" 71862306a36Sopenharmony_ci " cache 2, 9(%[addr]) \n" 71962306a36Sopenharmony_ci " cache 2, 10(%[addr]) \n" 72062306a36Sopenharmony_ci " cache 2, 11(%[addr]) \n" 72162306a36Sopenharmony_ci " cache 2, 12(%[addr]) \n" 72262306a36Sopenharmony_ci " cache 2, 13(%[addr]) \n" 72362306a36Sopenharmony_ci " cache 2, 14(%[addr]) \n" 72462306a36Sopenharmony_ci " cache 2, 15(%[addr]) \n" 72562306a36Sopenharmony_ci " addiu %[vsets], %[vsets], -1 \n" 72662306a36Sopenharmony_ci " bnez %[vsets], 2b \n" 72762306a36Sopenharmony_ci " addiu %[addr], %[addr], 0x40 \n" 72862306a36Sopenharmony_ci " li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */ 72962306a36Sopenharmony_ci " sw %[val], (%[state_addr]) \n" 73062306a36Sopenharmony_ci " sync \n" 73162306a36Sopenharmony_ci " cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */ 73262306a36Sopenharmony_ci " .set pop \n" 73362306a36Sopenharmony_ci : [addr] "=&r" (addr), [val] "=&r" (val) 73462306a36Sopenharmony_ci : [state_addr] "r" (state_addr), 73562306a36Sopenharmony_ci [sets] "r" (cpu_data[smp_processor_id()].dcache.sets), 73662306a36Sopenharmony_ci [vsets] "r" (cpu_data[smp_processor_id()].vcache.sets)); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci __asm__ __volatile__( 73962306a36Sopenharmony_ci " .set push \n" 74062306a36Sopenharmony_ci " .set noreorder \n" 74162306a36Sopenharmony_ci " .set mips64 \n" 74262306a36Sopenharmony_ci " mfc0 %[cpuid], $15, 1 \n" 74362306a36Sopenharmony_ci " andi %[cpuid], 0x3ff \n" 74462306a36Sopenharmony_ci " dli %[base], 0x900000003ff01000 \n" 74562306a36Sopenharmony_ci " andi %[core], %[cpuid], 0x3 \n" 74662306a36Sopenharmony_ci " sll %[core], 8 \n" /* get core id */ 74762306a36Sopenharmony_ci " or %[base], %[base], %[core] \n" 74862306a36Sopenharmony_ci " andi %[node], %[cpuid], 0xc \n" 74962306a36Sopenharmony_ci " dsll %[node], 42 \n" /* get node id */ 75062306a36Sopenharmony_ci " or %[base], %[base], %[node] \n" 75162306a36Sopenharmony_ci "1: li %[count], 0x100 \n" /* wait for init loop */ 75262306a36Sopenharmony_ci "2: bnez %[count], 2b \n" /* limit mailbox access */ 75362306a36Sopenharmony_ci " addiu %[count], -1 \n" 75462306a36Sopenharmony_ci " lw %[initfunc], 0x20(%[base]) \n" /* check lower 32-bit as jump indicator */ 75562306a36Sopenharmony_ci " beqz %[initfunc], 1b \n" 75662306a36Sopenharmony_ci " nop \n" 75762306a36Sopenharmony_ci " ld %[initfunc], 0x20(%[base]) \n" /* get PC (whole 64-bit) via mailbox */ 75862306a36Sopenharmony_ci " ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */ 75962306a36Sopenharmony_ci " ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */ 76062306a36Sopenharmony_ci " ld $a1, 0x38(%[base]) \n" 76162306a36Sopenharmony_ci " jr %[initfunc] \n" /* jump to initial PC */ 76262306a36Sopenharmony_ci " nop \n" 76362306a36Sopenharmony_ci " .set pop \n" 76462306a36Sopenharmony_ci : [core] "=&r" (core), [node] "=&r" (node), 76562306a36Sopenharmony_ci [base] "=&r" (base), [cpuid] "=&r" (cpuid), 76662306a36Sopenharmony_ci [count] "=&r" (count), [initfunc] "=&r" (initfunc) 76762306a36Sopenharmony_ci : /* No Input */ 76862306a36Sopenharmony_ci : "a1"); 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_civoid play_dead(void) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci int prid_imp, prid_rev, *state_addr; 77462306a36Sopenharmony_ci unsigned int cpu = smp_processor_id(); 77562306a36Sopenharmony_ci void (*play_dead_at_ckseg1)(int *); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci idle_task_exit(); 77862306a36Sopenharmony_ci cpuhp_ap_report_dead(); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci prid_imp = read_c0_prid() & PRID_IMP_MASK; 78162306a36Sopenharmony_ci prid_rev = read_c0_prid() & PRID_REV_MASK; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (prid_imp == PRID_IMP_LOONGSON_64G) { 78462306a36Sopenharmony_ci play_dead_at_ckseg1 = 78562306a36Sopenharmony_ci (void *)CKSEG1ADDR((unsigned long)loongson3_type3_play_dead); 78662306a36Sopenharmony_ci goto out; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci switch (prid_rev) { 79062306a36Sopenharmony_ci case PRID_REV_LOONGSON3A_R1: 79162306a36Sopenharmony_ci default: 79262306a36Sopenharmony_ci play_dead_at_ckseg1 = 79362306a36Sopenharmony_ci (void *)CKSEG1ADDR((unsigned long)loongson3_type1_play_dead); 79462306a36Sopenharmony_ci break; 79562306a36Sopenharmony_ci case PRID_REV_LOONGSON3B_R1: 79662306a36Sopenharmony_ci case PRID_REV_LOONGSON3B_R2: 79762306a36Sopenharmony_ci play_dead_at_ckseg1 = 79862306a36Sopenharmony_ci (void *)CKSEG1ADDR((unsigned long)loongson3_type2_play_dead); 79962306a36Sopenharmony_ci break; 80062306a36Sopenharmony_ci case PRID_REV_LOONGSON3A_R2_0: 80162306a36Sopenharmony_ci case PRID_REV_LOONGSON3A_R2_1: 80262306a36Sopenharmony_ci case PRID_REV_LOONGSON3A_R3_0: 80362306a36Sopenharmony_ci case PRID_REV_LOONGSON3A_R3_1: 80462306a36Sopenharmony_ci play_dead_at_ckseg1 = 80562306a36Sopenharmony_ci (void *)CKSEG1ADDR((unsigned long)loongson3_type3_play_dead); 80662306a36Sopenharmony_ci break; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ciout: 81062306a36Sopenharmony_ci state_addr = &per_cpu(cpu_state, cpu); 81162306a36Sopenharmony_ci mb(); 81262306a36Sopenharmony_ci play_dead_at_ckseg1(state_addr); 81362306a36Sopenharmony_ci BUG(); 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic int loongson3_disable_clock(unsigned int cpu) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci uint64_t core_id = cpu_core(&cpu_data[cpu]); 81962306a36Sopenharmony_ci uint64_t package_id = cpu_data[cpu].package; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1) { 82262306a36Sopenharmony_ci LOONGSON_CHIPCFG(package_id) &= ~(1 << (12 + core_id)); 82362306a36Sopenharmony_ci } else { 82462306a36Sopenharmony_ci if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG)) 82562306a36Sopenharmony_ci LOONGSON_FREQCTRL(package_id) &= ~(1 << (core_id * 4 + 3)); 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic int loongson3_enable_clock(unsigned int cpu) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci uint64_t core_id = cpu_core(&cpu_data[cpu]); 83362306a36Sopenharmony_ci uint64_t package_id = cpu_data[cpu].package; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1) { 83662306a36Sopenharmony_ci LOONGSON_CHIPCFG(package_id) |= 1 << (12 + core_id); 83762306a36Sopenharmony_ci } else { 83862306a36Sopenharmony_ci if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG)) 83962306a36Sopenharmony_ci LOONGSON_FREQCTRL(package_id) |= 1 << (core_id * 4 + 3); 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci return 0; 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cistatic int register_loongson3_notifier(void) 84562306a36Sopenharmony_ci{ 84662306a36Sopenharmony_ci return cpuhp_setup_state_nocalls(CPUHP_MIPS_SOC_PREPARE, 84762306a36Sopenharmony_ci "mips/loongson:prepare", 84862306a36Sopenharmony_ci loongson3_enable_clock, 84962306a36Sopenharmony_ci loongson3_disable_clock); 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ciearly_initcall(register_loongson3_notifier); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci#endif 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ciconst struct plat_smp_ops loongson3_smp_ops = { 85662306a36Sopenharmony_ci .send_ipi_single = loongson3_send_ipi_single, 85762306a36Sopenharmony_ci .send_ipi_mask = loongson3_send_ipi_mask, 85862306a36Sopenharmony_ci .init_secondary = loongson3_init_secondary, 85962306a36Sopenharmony_ci .smp_finish = loongson3_smp_finish, 86062306a36Sopenharmony_ci .boot_secondary = loongson3_boot_secondary, 86162306a36Sopenharmony_ci .smp_setup = loongson3_smp_setup, 86262306a36Sopenharmony_ci .prepare_cpus = loongson3_prepare_cpus, 86362306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 86462306a36Sopenharmony_ci .cpu_disable = loongson3_cpu_disable, 86562306a36Sopenharmony_ci .cpu_die = loongson3_cpu_die, 86662306a36Sopenharmony_ci#endif 86762306a36Sopenharmony_ci#ifdef CONFIG_KEXEC 86862306a36Sopenharmony_ci .kexec_nonboot_cpu = kexec_nonboot_cpu_jump, 86962306a36Sopenharmony_ci#endif 87062306a36Sopenharmony_ci}; 871