162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/kernel.h> 362306a36Sopenharmony_ci#include <linux/mmzone.h> 462306a36Sopenharmony_ci#include <linux/nodemask.h> 562306a36Sopenharmony_ci#include <linux/spinlock.h> 662306a36Sopenharmony_ci#include <linux/smp.h> 762306a36Sopenharmony_ci#include <linux/atomic.h> 862306a36Sopenharmony_ci#include <asm/sn/types.h> 962306a36Sopenharmony_ci#include <asm/sn/addrs.h> 1062306a36Sopenharmony_ci#include <asm/sn/nmi.h> 1162306a36Sopenharmony_ci#include <asm/sn/arch.h> 1262306a36Sopenharmony_ci#include <asm/sn/agent.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#if 0 1562306a36Sopenharmony_ci#define NODE_NUM_CPUS(n) CNODE_NUM_CPUS(n) 1662306a36Sopenharmony_ci#else 1762306a36Sopenharmony_ci#define NODE_NUM_CPUS(n) CPUS_PER_NODE 1862306a36Sopenharmony_ci#endif 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define SEND_NMI(_nasid, _slice) \ 2162306a36Sopenharmony_ci REMOTE_HUB_S((_nasid), (PI_NMI_A + ((_slice) * PI_NMI_OFFSET)), 1) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_citypedef unsigned long machreg_t; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic arch_spinlock_t nmi_lock = __ARCH_SPIN_LOCK_UNLOCKED; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * Let's see what else we need to do here. Set up sp, gp? 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_civoid nmi_dump(void) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci void cont_nmi_dump(void); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci cont_nmi_dump(); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_civoid install_cpu_nmi_handler(int slice) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci nmi_t *nmi_addr; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci nmi_addr = (nmi_t *)NMI_ADDR(get_nasid(), slice); 4262306a36Sopenharmony_ci if (nmi_addr->call_addr) 4362306a36Sopenharmony_ci return; 4462306a36Sopenharmony_ci nmi_addr->magic = NMI_MAGIC; 4562306a36Sopenharmony_ci nmi_addr->call_addr = (void *)nmi_dump; 4662306a36Sopenharmony_ci nmi_addr->call_addr_c = 4762306a36Sopenharmony_ci (void *)(~((unsigned long)(nmi_addr->call_addr))); 4862306a36Sopenharmony_ci nmi_addr->call_parm = 0; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * Copy the cpu registers which have been saved in the IP27prom format 5362306a36Sopenharmony_ci * into the eframe format for the node under consideration. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_civoid nmi_cpu_eframe_save(nasid_t nasid, int slice) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct reg_struct *nr; 5962306a36Sopenharmony_ci int i; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* Get the pointer to the current cpu's register set. */ 6262306a36Sopenharmony_ci nr = (struct reg_struct *) 6362306a36Sopenharmony_ci (TO_UNCAC(TO_NODE(nasid, IP27_NMI_KREGS_OFFSET)) + 6462306a36Sopenharmony_ci slice * IP27_NMI_KREGS_CPU_SIZE); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci pr_emerg("NMI nasid %d: slice %d\n", nasid, slice); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Saved main processor registers 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci for (i = 0; i < 32; ) { 7262306a36Sopenharmony_ci if ((i % 4) == 0) 7362306a36Sopenharmony_ci pr_emerg("$%2d :", i); 7462306a36Sopenharmony_ci pr_cont(" %016lx", nr->gpr[i]); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci i++; 7762306a36Sopenharmony_ci if ((i % 4) == 0) 7862306a36Sopenharmony_ci pr_cont("\n"); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci pr_emerg("Hi : (value lost)\n"); 8262306a36Sopenharmony_ci pr_emerg("Lo : (value lost)\n"); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * Saved cp0 registers 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci pr_emerg("epc : %016lx %pS\n", nr->epc, (void *)nr->epc); 8862306a36Sopenharmony_ci pr_emerg("%s\n", print_tainted()); 8962306a36Sopenharmony_ci pr_emerg("ErrEPC: %016lx %pS\n", nr->error_epc, (void *)nr->error_epc); 9062306a36Sopenharmony_ci pr_emerg("ra : %016lx %pS\n", nr->gpr[31], (void *)nr->gpr[31]); 9162306a36Sopenharmony_ci pr_emerg("Status: %08lx ", nr->sr); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (nr->sr & ST0_KX) 9462306a36Sopenharmony_ci pr_cont("KX "); 9562306a36Sopenharmony_ci if (nr->sr & ST0_SX) 9662306a36Sopenharmony_ci pr_cont("SX "); 9762306a36Sopenharmony_ci if (nr->sr & ST0_UX) 9862306a36Sopenharmony_ci pr_cont("UX "); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci switch (nr->sr & ST0_KSU) { 10162306a36Sopenharmony_ci case KSU_USER: 10262306a36Sopenharmony_ci pr_cont("USER "); 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci case KSU_SUPERVISOR: 10562306a36Sopenharmony_ci pr_cont("SUPERVISOR "); 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case KSU_KERNEL: 10862306a36Sopenharmony_ci pr_cont("KERNEL "); 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci default: 11162306a36Sopenharmony_ci pr_cont("BAD_MODE "); 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (nr->sr & ST0_ERL) 11662306a36Sopenharmony_ci pr_cont("ERL "); 11762306a36Sopenharmony_ci if (nr->sr & ST0_EXL) 11862306a36Sopenharmony_ci pr_cont("EXL "); 11962306a36Sopenharmony_ci if (nr->sr & ST0_IE) 12062306a36Sopenharmony_ci pr_cont("IE "); 12162306a36Sopenharmony_ci pr_cont("\n"); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci pr_emerg("Cause : %08lx\n", nr->cause); 12462306a36Sopenharmony_ci pr_emerg("PrId : %08x\n", read_c0_prid()); 12562306a36Sopenharmony_ci pr_emerg("BadVA : %016lx\n", nr->badva); 12662306a36Sopenharmony_ci pr_emerg("CErr : %016lx\n", nr->cache_err); 12762306a36Sopenharmony_ci pr_emerg("NMI_SR: %016lx\n", nr->nmi_sr); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci pr_emerg("\n"); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_civoid nmi_dump_hub_irq(nasid_t nasid, int slice) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci u64 mask0, mask1, pend0, pend1; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (slice == 0) { /* Slice A */ 13762306a36Sopenharmony_ci mask0 = REMOTE_HUB_L(nasid, PI_INT_MASK0_A); 13862306a36Sopenharmony_ci mask1 = REMOTE_HUB_L(nasid, PI_INT_MASK1_A); 13962306a36Sopenharmony_ci } else { /* Slice B */ 14062306a36Sopenharmony_ci mask0 = REMOTE_HUB_L(nasid, PI_INT_MASK0_B); 14162306a36Sopenharmony_ci mask1 = REMOTE_HUB_L(nasid, PI_INT_MASK1_B); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci pend0 = REMOTE_HUB_L(nasid, PI_INT_PEND0); 14562306a36Sopenharmony_ci pend1 = REMOTE_HUB_L(nasid, PI_INT_PEND1); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci pr_emerg("PI_INT_MASK0: %16llx PI_INT_MASK1: %16llx\n", mask0, mask1); 14862306a36Sopenharmony_ci pr_emerg("PI_INT_PEND0: %16llx PI_INT_PEND1: %16llx\n", pend0, pend1); 14962306a36Sopenharmony_ci pr_emerg("\n\n"); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* 15362306a36Sopenharmony_ci * Copy the cpu registers which have been saved in the IP27prom format 15462306a36Sopenharmony_ci * into the eframe format for the node under consideration. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_civoid nmi_node_eframe_save(nasid_t nasid) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci int slice; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (nasid == INVALID_NASID) 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Save the registers into eframe for each cpu */ 16462306a36Sopenharmony_ci for (slice = 0; slice < NODE_NUM_CPUS(slice); slice++) { 16562306a36Sopenharmony_ci nmi_cpu_eframe_save(nasid, slice); 16662306a36Sopenharmony_ci nmi_dump_hub_irq(nasid, slice); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * Save the nmi cpu registers for all cpus in the system. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_civoid 17462306a36Sopenharmony_cinmi_eframes_save(void) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci nasid_t nasid; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci for_each_online_node(nasid) 17962306a36Sopenharmony_ci nmi_node_eframe_save(nasid); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_civoid 18362306a36Sopenharmony_cicont_nmi_dump(void) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci#ifndef REAL_NMI_SIGNAL 18662306a36Sopenharmony_ci static atomic_t nmied_cpus = ATOMIC_INIT(0); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci atomic_inc(&nmied_cpus); 18962306a36Sopenharmony_ci#endif 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * Only allow 1 cpu to proceed 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci arch_spin_lock(&nmi_lock); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci#ifdef REAL_NMI_SIGNAL 19662306a36Sopenharmony_ci /* 19762306a36Sopenharmony_ci * Wait up to 15 seconds for the other cpus to respond to the NMI. 19862306a36Sopenharmony_ci * If a cpu has not responded after 10 sec, send it 1 additional NMI. 19962306a36Sopenharmony_ci * This is for 2 reasons: 20062306a36Sopenharmony_ci * - sometimes a MMSC fail to NMI all cpus. 20162306a36Sopenharmony_ci * - on 512p SN0 system, the MMSC will only send NMIs to 20262306a36Sopenharmony_ci * half the cpus. Unfortunately, we don't know which cpus may be 20362306a36Sopenharmony_ci * NMIed - it depends on how the site chooses to configure. 20462306a36Sopenharmony_ci * 20562306a36Sopenharmony_ci * Note: it has been measure that it takes the MMSC up to 2.3 secs to 20662306a36Sopenharmony_ci * send NMIs to all cpus on a 256p system. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci for (i=0; i < 1500; i++) { 20962306a36Sopenharmony_ci for_each_online_node(node) 21062306a36Sopenharmony_ci if (NODEPDA(node)->dump_count == 0) 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci if (node == MAX_NUMNODES) 21362306a36Sopenharmony_ci break; 21462306a36Sopenharmony_ci if (i == 1000) { 21562306a36Sopenharmony_ci for_each_online_node(node) 21662306a36Sopenharmony_ci if (NODEPDA(node)->dump_count == 0) { 21762306a36Sopenharmony_ci cpu = cpumask_first(cpumask_of_node(node)); 21862306a36Sopenharmony_ci for (n=0; n < CNODE_NUM_CPUS(node); cpu++, n++) { 21962306a36Sopenharmony_ci CPUMASK_SETB(nmied_cpus, cpu); 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * cputonasid, cputoslice 22262306a36Sopenharmony_ci * needs kernel cpuid 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci SEND_NMI((cputonasid(cpu)), (cputoslice(cpu))); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci udelay(10000); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci#else 23262306a36Sopenharmony_ci while (atomic_read(&nmied_cpus) != num_online_cpus()); 23362306a36Sopenharmony_ci#endif 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* 23662306a36Sopenharmony_ci * Save the nmi cpu registers for all cpu in the eframe format. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci nmi_eframes_save(); 23962306a36Sopenharmony_ci LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET); 24062306a36Sopenharmony_ci} 241