xref: /openbmc/linux/arch/x86/platform/uv/uv_nmi.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21e019421SMike Travis /*
3b5dfcb09SIngo Molnar  * SGI NMI support routines
41e019421SMike Travis  *
57a6d94f0SMike Travis  * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
6ae5f8ce3SMike Travis  * Copyright (C) 2007-2017 Silicon Graphics, Inc. All rights reserved.
71e019421SMike Travis  * Copyright (c) Mike Travis
81e019421SMike Travis  */
91e019421SMike Travis 
101e019421SMike Travis #include <linux/cpu.h>
110d12ef0cSMike Travis #include <linux/delay.h>
12e379ea82SMike Travis #include <linux/kdb.h>
1312ba6c99SMike Travis #include <linux/kexec.h>
14e379ea82SMike Travis #include <linux/kgdb.h>
15cc3ae7b0SPaul Gortmaker #include <linux/moduleparam.h>
161e019421SMike Travis #include <linux/nmi.h>
170d12ef0cSMike Travis #include <linux/sched.h>
18b17b0153SIngo Molnar #include <linux/sched/debug.h>
190d12ef0cSMike Travis #include <linux/slab.h>
20d51953b0SIngo Molnar #include <linux/clocksource.h>
211e019421SMike Travis 
221e019421SMike Travis #include <asm/apic.h>
230d12ef0cSMike Travis #include <asm/current.h>
240d12ef0cSMike Travis #include <asm/kdebug.h>
250d12ef0cSMike Travis #include <asm/local64.h>
261e019421SMike Travis #include <asm/nmi.h>
270b45143bSGeorges Aureau #include <asm/reboot.h>
28e379ea82SMike Travis #include <asm/traps.h>
291e019421SMike Travis #include <asm/uv/uv.h>
301e019421SMike Travis #include <asm/uv/uv_hub.h>
311e019421SMike Travis #include <asm/uv/uv_mmrs.h>
321e019421SMike Travis 
330d12ef0cSMike Travis /*
340d12ef0cSMike Travis  * UV handler for NMI
350d12ef0cSMike Travis  *
360d12ef0cSMike Travis  * Handle system-wide NMI events generated by the global 'power nmi' command.
370d12ef0cSMike Travis  *
381e740163Stravis@sgi.com  * Basic operation is to field the NMI interrupt on each CPU and wait
391e740163Stravis@sgi.com  * until all CPU's have arrived into the nmi handler.  If some CPU's do not
400d12ef0cSMike Travis  * make it into the handler, try and force them in with the IPI(NMI) signal.
410d12ef0cSMike Travis  *
420d12ef0cSMike Travis  * We also have to lessen UV Hub MMR accesses as much as possible as this
430d12ef0cSMike Travis  * disrupts the UV Hub's primary mission of directing NumaLink traffic and
440d12ef0cSMike Travis  * can cause system problems to occur.
450d12ef0cSMike Travis  *
460d12ef0cSMike Travis  * To do this we register our primary NMI notifier on the NMI_UNKNOWN
470d12ef0cSMike Travis  * chain.  This reduces the number of false NMI calls when the perf
480d12ef0cSMike Travis  * tools are running which generate an enormous number of NMIs per
491e740163Stravis@sgi.com  * second (~4M/s for 1024 CPU threads).  Our secondary NMI handler is
500d12ef0cSMike Travis  * very short as it only checks that if it has been "pinged" with the
510d12ef0cSMike Travis  * IPI(NMI) signal as mentioned above, and does not read the UV Hub's MMR.
520d12ef0cSMike Travis  *
530d12ef0cSMike Travis  */
540d12ef0cSMike Travis 
550d12ef0cSMike Travis static struct uv_hub_nmi_s **uv_hub_nmi_list;
560d12ef0cSMike Travis 
57e1632170SChristoph Lameter DEFINE_PER_CPU(struct uv_cpu_nmi_s, uv_cpu_nmi);
580d12ef0cSMike Travis 
59ae5f8ce3SMike Travis /* Newer SMM NMI handler, not present in all systems */
60ae5f8ce3SMike Travis static unsigned long uvh_nmi_mmrx;		/* UVH_EVENT_OCCURRED0/1 */
61ae5f8ce3SMike Travis static unsigned long uvh_nmi_mmrx_clear;	/* UVH_EVENT_OCCURRED0/1_ALIAS */
62ae5f8ce3SMike Travis static int uvh_nmi_mmrx_shift;			/* UVH_EVENT_OCCURRED0/1_EXTIO_INT0_SHFT */
63ae5f8ce3SMike Travis static char *uvh_nmi_mmrx_type;			/* "EXTIO_INT0" */
64ae5f8ce3SMike Travis 
65ae5f8ce3SMike Travis /* Non-zero indicates newer SMM NMI handler present */
66ae5f8ce3SMike Travis static unsigned long uvh_nmi_mmrx_supported;	/* UVH_EXTIO_INT0_BROADCAST */
67ae5f8ce3SMike Travis 
68ae5f8ce3SMike Travis /* Indicates to BIOS that we want to use the newer SMM NMI handler */
69ae5f8ce3SMike Travis static unsigned long uvh_nmi_mmrx_req;		/* UVH_BIOS_KERNEL_MMR_ALIAS_2 */
70ae5f8ce3SMike Travis static int uvh_nmi_mmrx_req_shift;		/* 62 */
71ae5f8ce3SMike Travis 
72abdf1df6Stravis@sgi.com /* UV hubless values */
73abdf1df6Stravis@sgi.com #define NMI_CONTROL_PORT	0x70
74abdf1df6Stravis@sgi.com #define NMI_DUMMY_PORT		0x71
7556e17ca2Stravis@sgi.com #define PAD_OWN_GPP_D_0		0x2c
76abdf1df6Stravis@sgi.com #define GPI_NMI_STS_GPP_D_0	0x164
77abdf1df6Stravis@sgi.com #define GPI_NMI_ENA_GPP_D_0	0x174
78abdf1df6Stravis@sgi.com #define STS_GPP_D_0_MASK	0x1
79abdf1df6Stravis@sgi.com #define PAD_CFG_DW0_GPP_D_0	0x4c0
80abdf1df6Stravis@sgi.com #define GPIROUTNMI		(1ul << 17)
81abdf1df6Stravis@sgi.com #define PCH_PCR_GPIO_1_BASE	0xfdae0000ul
82abdf1df6Stravis@sgi.com #define PCH_PCR_GPIO_ADDRESS(offset) (int *)((u64)(pch_base) | (u64)(offset))
83abdf1df6Stravis@sgi.com 
84abdf1df6Stravis@sgi.com static u64 *pch_base;
850d12ef0cSMike Travis static unsigned long nmi_mmr;
860d12ef0cSMike Travis static unsigned long nmi_mmr_clear;
870d12ef0cSMike Travis static unsigned long nmi_mmr_pending;
880d12ef0cSMike Travis 
890d12ef0cSMike Travis static atomic_t	uv_in_nmi;
900d12ef0cSMike Travis static atomic_t uv_nmi_cpu = ATOMIC_INIT(-1);
910d12ef0cSMike Travis static atomic_t uv_nmi_cpus_in_nmi = ATOMIC_INIT(-1);
920d12ef0cSMike Travis static atomic_t uv_nmi_slave_continue;
930d12ef0cSMike Travis static cpumask_var_t uv_nmi_cpu_mask;
940d12ef0cSMike Travis 
9527743f01SIngo Molnar static atomic_t uv_nmi_kexec_failed;
9627743f01SIngo Molnar 
970d12ef0cSMike Travis /* Values for uv_nmi_slave_continue */
980d12ef0cSMike Travis #define SLAVE_CLEAR	0
990d12ef0cSMike Travis #define SLAVE_CONTINUE	1
1000d12ef0cSMike Travis #define SLAVE_EXIT	2
1011e019421SMike Travis 
1021e019421SMike Travis /*
1030d12ef0cSMike Travis  * Default is all stack dumps go to the console and buffer.
1040d12ef0cSMike Travis  * Lower level to send to log buffer only.
1050d12ef0cSMike Travis  */
106a8fe19ebSBorislav Petkov static int uv_nmi_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
1070d12ef0cSMike Travis module_param_named(dump_loglevel, uv_nmi_loglevel, int, 0644);
1080d12ef0cSMike Travis 
1090d12ef0cSMike Travis /*
1100d12ef0cSMike Travis  * The following values show statistics on how perf events are affecting
1110d12ef0cSMike Travis  * this system.
1120d12ef0cSMike Travis  */
param_get_local64(char * buffer,const struct kernel_param * kp)1130d12ef0cSMike Travis static int param_get_local64(char *buffer, const struct kernel_param *kp)
1140d12ef0cSMike Travis {
1150d12ef0cSMike Travis 	return sprintf(buffer, "%lu\n", local64_read((local64_t *)kp->arg));
1160d12ef0cSMike Travis }
1170d12ef0cSMike Travis 
param_set_local64(const char * val,const struct kernel_param * kp)1180d12ef0cSMike Travis static int param_set_local64(const char *val, const struct kernel_param *kp)
1190d12ef0cSMike Travis {
1201e740163Stravis@sgi.com 	/* Clear on any write */
1210d12ef0cSMike Travis 	local64_set((local64_t *)kp->arg, 0);
1220d12ef0cSMike Travis 	return 0;
1230d12ef0cSMike Travis }
1240d12ef0cSMike Travis 
1259c27847dSLuis R. Rodriguez static const struct kernel_param_ops param_ops_local64 = {
1260d12ef0cSMike Travis 	.get = param_get_local64,
1270d12ef0cSMike Travis 	.set = param_set_local64,
1280d12ef0cSMike Travis };
1290d12ef0cSMike Travis #define param_check_local64(name, p) __param_check(name, p, local64_t)
1300d12ef0cSMike Travis 
1310d12ef0cSMike Travis static local64_t uv_nmi_count;
1320d12ef0cSMike Travis module_param_named(nmi_count, uv_nmi_count, local64, 0644);
1330d12ef0cSMike Travis 
1340d12ef0cSMike Travis static local64_t uv_nmi_misses;
1350d12ef0cSMike Travis module_param_named(nmi_misses, uv_nmi_misses, local64, 0644);
1360d12ef0cSMike Travis 
1370d12ef0cSMike Travis static local64_t uv_nmi_ping_count;
1380d12ef0cSMike Travis module_param_named(ping_count, uv_nmi_ping_count, local64, 0644);
1390d12ef0cSMike Travis 
1400d12ef0cSMike Travis static local64_t uv_nmi_ping_misses;
1410d12ef0cSMike Travis module_param_named(ping_misses, uv_nmi_ping_misses, local64, 0644);
1420d12ef0cSMike Travis 
1430d12ef0cSMike Travis /*
1440d12ef0cSMike Travis  * Following values allow tuning for large systems under heavy loading
1450d12ef0cSMike Travis  */
1460d12ef0cSMike Travis static int uv_nmi_initial_delay = 100;
1470d12ef0cSMike Travis module_param_named(initial_delay, uv_nmi_initial_delay, int, 0644);
1480d12ef0cSMike Travis 
1490d12ef0cSMike Travis static int uv_nmi_slave_delay = 100;
1500d12ef0cSMike Travis module_param_named(slave_delay, uv_nmi_slave_delay, int, 0644);
1510d12ef0cSMike Travis 
1520d12ef0cSMike Travis static int uv_nmi_loop_delay = 100;
1530d12ef0cSMike Travis module_param_named(loop_delay, uv_nmi_loop_delay, int, 0644);
1540d12ef0cSMike Travis 
1550d12ef0cSMike Travis static int uv_nmi_trigger_delay = 10000;
1560d12ef0cSMike Travis module_param_named(trigger_delay, uv_nmi_trigger_delay, int, 0644);
1570d12ef0cSMike Travis 
1580d12ef0cSMike Travis static int uv_nmi_wait_count = 100;
1590d12ef0cSMike Travis module_param_named(wait_count, uv_nmi_wait_count, int, 0644);
1600d12ef0cSMike Travis 
1610d12ef0cSMike Travis static int uv_nmi_retry_count = 500;
1620d12ef0cSMike Travis module_param_named(retry_count, uv_nmi_retry_count, int, 0644);
1630d12ef0cSMike Travis 
164abdf1df6Stravis@sgi.com static bool uv_pch_intr_enable = true;
165abdf1df6Stravis@sgi.com static bool uv_pch_intr_now_enabled;
166abdf1df6Stravis@sgi.com module_param_named(pch_intr_enable, uv_pch_intr_enable, bool, 0644);
167abdf1df6Stravis@sgi.com 
16856e17ca2Stravis@sgi.com static bool uv_pch_init_enable = true;
16956e17ca2Stravis@sgi.com module_param_named(pch_init_enable, uv_pch_init_enable, bool, 0644);
17056e17ca2Stravis@sgi.com 
171abdf1df6Stravis@sgi.com static int uv_nmi_debug;
172abdf1df6Stravis@sgi.com module_param_named(debug, uv_nmi_debug, int, 0644);
173abdf1df6Stravis@sgi.com 
174abdf1df6Stravis@sgi.com #define nmi_debug(fmt, ...)				\
175abdf1df6Stravis@sgi.com 	do {						\
176abdf1df6Stravis@sgi.com 		if (uv_nmi_debug)			\
177abdf1df6Stravis@sgi.com 			pr_info(fmt, ##__VA_ARGS__);	\
178abdf1df6Stravis@sgi.com 	} while (0)
179abdf1df6Stravis@sgi.com 
180f550e469Stravis@sgi.com /* Valid NMI Actions */
181f550e469Stravis@sgi.com #define	ACTION_LEN	16
182f550e469Stravis@sgi.com static struct nmi_action {
183f550e469Stravis@sgi.com 	char	*action;
184f550e469Stravis@sgi.com 	char	*desc;
185f550e469Stravis@sgi.com } valid_acts[] = {
186f550e469Stravis@sgi.com 	{	"kdump",	"do kernel crash dump"			},
187f550e469Stravis@sgi.com 	{	"dump",		"dump process stack for each cpu"	},
188f550e469Stravis@sgi.com 	{	"ips",		"dump Inst Ptr info for each cpu"	},
189f550e469Stravis@sgi.com 	{	"kdb",		"enter KDB (needs kgdboc= assignment)"	},
190f550e469Stravis@sgi.com 	{	"kgdb",		"enter KGDB (needs gdb target remote)"	},
191f550e469Stravis@sgi.com 	{	"health",	"check if CPUs respond to NMI"		},
192f550e469Stravis@sgi.com };
193f550e469Stravis@sgi.com typedef char action_t[ACTION_LEN];
194f550e469Stravis@sgi.com static action_t uv_nmi_action = { "dump" };
195f550e469Stravis@sgi.com 
param_get_action(char * buffer,const struct kernel_param * kp)196f550e469Stravis@sgi.com static int param_get_action(char *buffer, const struct kernel_param *kp)
197f550e469Stravis@sgi.com {
198f550e469Stravis@sgi.com 	return sprintf(buffer, "%s\n", uv_nmi_action);
199f550e469Stravis@sgi.com }
200f550e469Stravis@sgi.com 
param_set_action(const char * val,const struct kernel_param * kp)201f550e469Stravis@sgi.com static int param_set_action(const char *val, const struct kernel_param *kp)
202f550e469Stravis@sgi.com {
203f550e469Stravis@sgi.com 	int i;
204f550e469Stravis@sgi.com 	int n = ARRAY_SIZE(valid_acts);
205*1e6f01f7SJustin Stitt 	char arg[ACTION_LEN];
206f550e469Stravis@sgi.com 
207f550e469Stravis@sgi.com 	/* (remove possible '\n') */
208*1e6f01f7SJustin Stitt 	strscpy(arg, val, strnchrnul(val, sizeof(arg)-1, '\n') - val + 1);
209f550e469Stravis@sgi.com 
210f550e469Stravis@sgi.com 	for (i = 0; i < n; i++)
211f550e469Stravis@sgi.com 		if (!strcmp(arg, valid_acts[i].action))
212f550e469Stravis@sgi.com 			break;
213f550e469Stravis@sgi.com 
214f550e469Stravis@sgi.com 	if (i < n) {
215*1e6f01f7SJustin Stitt 		strscpy(uv_nmi_action, arg, sizeof(uv_nmi_action));
216f550e469Stravis@sgi.com 		pr_info("UV: New NMI action:%s\n", uv_nmi_action);
217f550e469Stravis@sgi.com 		return 0;
218f550e469Stravis@sgi.com 	}
219f550e469Stravis@sgi.com 
220f550e469Stravis@sgi.com 	pr_err("UV: Invalid NMI action:%s, valid actions are:\n", arg);
221f550e469Stravis@sgi.com 	for (i = 0; i < n; i++)
222f550e469Stravis@sgi.com 		pr_err("UV: %-8s - %s\n",
223f550e469Stravis@sgi.com 			valid_acts[i].action, valid_acts[i].desc);
224f550e469Stravis@sgi.com 	return -EINVAL;
225f550e469Stravis@sgi.com }
226f550e469Stravis@sgi.com 
227f550e469Stravis@sgi.com static const struct kernel_param_ops param_ops_action = {
228f550e469Stravis@sgi.com 	.get = param_get_action,
229f550e469Stravis@sgi.com 	.set = param_set_action,
230f550e469Stravis@sgi.com };
231f550e469Stravis@sgi.com #define param_check_action(name, p) __param_check(name, p, action_t)
232f550e469Stravis@sgi.com 
233f550e469Stravis@sgi.com module_param_named(action, uv_nmi_action, action, 0644);
2343c121d9aSMike Travis 
uv_nmi_action_is(const char * action)2353c121d9aSMike Travis static inline bool uv_nmi_action_is(const char *action)
2363c121d9aSMike Travis {
2373c121d9aSMike Travis 	return (strncmp(uv_nmi_action, action, strlen(action)) == 0);
2383c121d9aSMike Travis }
2393c121d9aSMike Travis 
2400d12ef0cSMike Travis /* Setup which NMI support is present in system */
uv_nmi_setup_mmrs(void)2410d12ef0cSMike Travis static void uv_nmi_setup_mmrs(void)
2420d12ef0cSMike Travis {
243d812f7c4SMike Travis 	bool new_nmi_method_only = false;
244d812f7c4SMike Travis 
245ae5f8ce3SMike Travis 	/* First determine arch specific MMRs to handshake with BIOS */
246d812f7c4SMike Travis 	if (UVH_EVENT_OCCURRED0_EXTIO_INT0_MASK) {	/* UV2,3,4 setup */
247ae5f8ce3SMike Travis 		uvh_nmi_mmrx = UVH_EVENT_OCCURRED0;
248ae5f8ce3SMike Travis 		uvh_nmi_mmrx_clear = UVH_EVENT_OCCURRED0_ALIAS;
249ae5f8ce3SMike Travis 		uvh_nmi_mmrx_shift = UVH_EVENT_OCCURRED0_EXTIO_INT0_SHFT;
250ae5f8ce3SMike Travis 		uvh_nmi_mmrx_type = "OCRD0-EXTIO_INT0";
251ae5f8ce3SMike Travis 
252ae5f8ce3SMike Travis 		uvh_nmi_mmrx_supported = UVH_EXTIO_INT0_BROADCAST;
253ae5f8ce3SMike Travis 		uvh_nmi_mmrx_req = UVH_BIOS_KERNEL_MMR_ALIAS_2;
254ae5f8ce3SMike Travis 		uvh_nmi_mmrx_req_shift = 62;
255ae5f8ce3SMike Travis 
256d812f7c4SMike Travis 	} else if (UVH_EVENT_OCCURRED1_EXTIO_INT0_MASK) { /* UV5+ setup */
257ae5f8ce3SMike Travis 		uvh_nmi_mmrx = UVH_EVENT_OCCURRED1;
258ae5f8ce3SMike Travis 		uvh_nmi_mmrx_clear = UVH_EVENT_OCCURRED1_ALIAS;
259ae5f8ce3SMike Travis 		uvh_nmi_mmrx_shift = UVH_EVENT_OCCURRED1_EXTIO_INT0_SHFT;
260ae5f8ce3SMike Travis 		uvh_nmi_mmrx_type = "OCRD1-EXTIO_INT0";
261ae5f8ce3SMike Travis 
262d812f7c4SMike Travis 		new_nmi_method_only = true;		/* Newer nmi always valid on UV5+ */
263d812f7c4SMike Travis 		uvh_nmi_mmrx_req = 0;			/* no request bit to clear */
264ae5f8ce3SMike Travis 
265ae5f8ce3SMike Travis 	} else {
266d812f7c4SMike Travis 		pr_err("UV:%s:NMI support not available on this system\n", __func__);
267ae5f8ce3SMike Travis 		return;
268ae5f8ce3SMike Travis 	}
269ae5f8ce3SMike Travis 
270ae5f8ce3SMike Travis 	/* Then find out if new NMI is supported */
271d812f7c4SMike Travis 	if (new_nmi_method_only || uv_read_local_mmr(uvh_nmi_mmrx_supported)) {
272d812f7c4SMike Travis 		if (uvh_nmi_mmrx_req)
273ae5f8ce3SMike Travis 			uv_write_local_mmr(uvh_nmi_mmrx_req,
274ae5f8ce3SMike Travis 						1UL << uvh_nmi_mmrx_req_shift);
275ae5f8ce3SMike Travis 		nmi_mmr = uvh_nmi_mmrx;
276ae5f8ce3SMike Travis 		nmi_mmr_clear = uvh_nmi_mmrx_clear;
277ae5f8ce3SMike Travis 		nmi_mmr_pending = 1UL << uvh_nmi_mmrx_shift;
278ae5f8ce3SMike Travis 		pr_info("UV: SMI NMI support: %s\n", uvh_nmi_mmrx_type);
2790d12ef0cSMike Travis 	} else {
2800d12ef0cSMike Travis 		nmi_mmr = UVH_NMI_MMR;
2810d12ef0cSMike Travis 		nmi_mmr_clear = UVH_NMI_MMR_CLEAR;
2820d12ef0cSMike Travis 		nmi_mmr_pending = 1UL << UVH_NMI_MMR_SHIFT;
2830d12ef0cSMike Travis 		pr_info("UV: SMI NMI support: %s\n", UVH_NMI_MMR_TYPE);
2840d12ef0cSMike Travis 	}
2850d12ef0cSMike Travis }
2860d12ef0cSMike Travis 
2870d12ef0cSMike Travis /* Read NMI MMR and check if NMI flag was set by BMC. */
uv_nmi_test_mmr(struct uv_hub_nmi_s * hub_nmi)2880d12ef0cSMike Travis static inline int uv_nmi_test_mmr(struct uv_hub_nmi_s *hub_nmi)
2890d12ef0cSMike Travis {
2900d12ef0cSMike Travis 	hub_nmi->nmi_value = uv_read_local_mmr(nmi_mmr);
2910d12ef0cSMike Travis 	atomic_inc(&hub_nmi->read_mmr_count);
2920d12ef0cSMike Travis 	return !!(hub_nmi->nmi_value & nmi_mmr_pending);
2930d12ef0cSMike Travis }
2940d12ef0cSMike Travis 
uv_local_mmr_clear_nmi(void)2950d12ef0cSMike Travis static inline void uv_local_mmr_clear_nmi(void)
2960d12ef0cSMike Travis {
2970d12ef0cSMike Travis 	uv_write_local_mmr(nmi_mmr_clear, nmi_mmr_pending);
2980d12ef0cSMike Travis }
2990d12ef0cSMike Travis 
3000d12ef0cSMike Travis /*
301abdf1df6Stravis@sgi.com  * UV hubless NMI handler functions
302abdf1df6Stravis@sgi.com  */
uv_reassert_nmi(void)303abdf1df6Stravis@sgi.com static inline void uv_reassert_nmi(void)
304abdf1df6Stravis@sgi.com {
305abdf1df6Stravis@sgi.com 	/* (from arch/x86/include/asm/mach_traps.h) */
306abdf1df6Stravis@sgi.com 	outb(0x8f, NMI_CONTROL_PORT);
307abdf1df6Stravis@sgi.com 	inb(NMI_DUMMY_PORT);		/* dummy read */
308abdf1df6Stravis@sgi.com 	outb(0x0f, NMI_CONTROL_PORT);
309abdf1df6Stravis@sgi.com 	inb(NMI_DUMMY_PORT);		/* dummy read */
310abdf1df6Stravis@sgi.com }
311abdf1df6Stravis@sgi.com 
uv_init_hubless_pch_io(int offset,int mask,int data)312abdf1df6Stravis@sgi.com static void uv_init_hubless_pch_io(int offset, int mask, int data)
313abdf1df6Stravis@sgi.com {
314abdf1df6Stravis@sgi.com 	int *addr = PCH_PCR_GPIO_ADDRESS(offset);
315abdf1df6Stravis@sgi.com 	int readd = readl(addr);
316abdf1df6Stravis@sgi.com 
317abdf1df6Stravis@sgi.com 	if (mask) {			/* OR in new data */
318abdf1df6Stravis@sgi.com 		int writed = (readd & ~mask) | data;
319abdf1df6Stravis@sgi.com 
320abdf1df6Stravis@sgi.com 		nmi_debug("UV:PCH: %p = %x & %x | %x (%x)\n",
321abdf1df6Stravis@sgi.com 			addr, readd, ~mask, data, writed);
322abdf1df6Stravis@sgi.com 		writel(writed, addr);
323abdf1df6Stravis@sgi.com 	} else if (readd & data) {	/* clear status bit */
324abdf1df6Stravis@sgi.com 		nmi_debug("UV:PCH: %p = %x\n", addr, data);
325abdf1df6Stravis@sgi.com 		writel(data, addr);
326abdf1df6Stravis@sgi.com 	}
327abdf1df6Stravis@sgi.com 
328abdf1df6Stravis@sgi.com 	(void)readl(addr);		/* flush write data */
329abdf1df6Stravis@sgi.com }
330abdf1df6Stravis@sgi.com 
uv_nmi_setup_hubless_intr(void)331abdf1df6Stravis@sgi.com static void uv_nmi_setup_hubless_intr(void)
332abdf1df6Stravis@sgi.com {
333abdf1df6Stravis@sgi.com 	uv_pch_intr_now_enabled = uv_pch_intr_enable;
334abdf1df6Stravis@sgi.com 
335abdf1df6Stravis@sgi.com 	uv_init_hubless_pch_io(
336abdf1df6Stravis@sgi.com 		PAD_CFG_DW0_GPP_D_0, GPIROUTNMI,
337abdf1df6Stravis@sgi.com 		uv_pch_intr_now_enabled ? GPIROUTNMI : 0);
338abdf1df6Stravis@sgi.com 
339abdf1df6Stravis@sgi.com 	nmi_debug("UV:NMI: GPP_D_0 interrupt %s\n",
340abdf1df6Stravis@sgi.com 		uv_pch_intr_now_enabled ? "enabled" : "disabled");
341abdf1df6Stravis@sgi.com }
342abdf1df6Stravis@sgi.com 
34356e17ca2Stravis@sgi.com static struct init_nmi {
34456e17ca2Stravis@sgi.com 	unsigned int	offset;
34556e17ca2Stravis@sgi.com 	unsigned int	mask;
34656e17ca2Stravis@sgi.com 	unsigned int	data;
34756e17ca2Stravis@sgi.com } init_nmi[] = {
34856e17ca2Stravis@sgi.com 	{	/* HOSTSW_OWN_GPP_D_0 */
34956e17ca2Stravis@sgi.com 	.offset = 0x84,
35056e17ca2Stravis@sgi.com 	.mask = 0x1,
35156e17ca2Stravis@sgi.com 	.data = 0x0,	/* ACPI Mode */
35256e17ca2Stravis@sgi.com 	},
35356e17ca2Stravis@sgi.com 
3541e740163Stravis@sgi.com /* Clear status: */
35556e17ca2Stravis@sgi.com 	{	/* GPI_INT_STS_GPP_D_0 */
35656e17ca2Stravis@sgi.com 	.offset = 0x104,
35756e17ca2Stravis@sgi.com 	.mask = 0x0,
35856e17ca2Stravis@sgi.com 	.data = 0x1,	/* Clear Status */
35956e17ca2Stravis@sgi.com 	},
36056e17ca2Stravis@sgi.com 	{	/* GPI_GPE_STS_GPP_D_0 */
36156e17ca2Stravis@sgi.com 	.offset = 0x124,
36256e17ca2Stravis@sgi.com 	.mask = 0x0,
36356e17ca2Stravis@sgi.com 	.data = 0x1,	/* Clear Status */
36456e17ca2Stravis@sgi.com 	},
36556e17ca2Stravis@sgi.com 	{	/* GPI_SMI_STS_GPP_D_0 */
36656e17ca2Stravis@sgi.com 	.offset = 0x144,
36756e17ca2Stravis@sgi.com 	.mask = 0x0,
36856e17ca2Stravis@sgi.com 	.data = 0x1,	/* Clear Status */
36956e17ca2Stravis@sgi.com 	},
37056e17ca2Stravis@sgi.com 	{	/* GPI_NMI_STS_GPP_D_0 */
37156e17ca2Stravis@sgi.com 	.offset = 0x164,
37256e17ca2Stravis@sgi.com 	.mask = 0x0,
37356e17ca2Stravis@sgi.com 	.data = 0x1,	/* Clear Status */
37456e17ca2Stravis@sgi.com 	},
37556e17ca2Stravis@sgi.com 
3761e740163Stravis@sgi.com /* Disable interrupts: */
37756e17ca2Stravis@sgi.com 	{	/* GPI_INT_EN_GPP_D_0 */
37856e17ca2Stravis@sgi.com 	.offset = 0x114,
37956e17ca2Stravis@sgi.com 	.mask = 0x1,
3801e740163Stravis@sgi.com 	.data = 0x0,	/* Disable interrupt generation */
38156e17ca2Stravis@sgi.com 	},
38256e17ca2Stravis@sgi.com 	{	/* GPI_GPE_EN_GPP_D_0 */
38356e17ca2Stravis@sgi.com 	.offset = 0x134,
38456e17ca2Stravis@sgi.com 	.mask = 0x1,
3851e740163Stravis@sgi.com 	.data = 0x0,	/* Disable interrupt generation */
38656e17ca2Stravis@sgi.com 	},
38756e17ca2Stravis@sgi.com 	{	/* GPI_SMI_EN_GPP_D_0 */
38856e17ca2Stravis@sgi.com 	.offset = 0x154,
38956e17ca2Stravis@sgi.com 	.mask = 0x1,
3901e740163Stravis@sgi.com 	.data = 0x0,	/* Disable interrupt generation */
39156e17ca2Stravis@sgi.com 	},
39256e17ca2Stravis@sgi.com 	{	/* GPI_NMI_EN_GPP_D_0 */
39356e17ca2Stravis@sgi.com 	.offset = 0x174,
39456e17ca2Stravis@sgi.com 	.mask = 0x1,
3951e740163Stravis@sgi.com 	.data = 0x0,	/* Disable interrupt generation */
39656e17ca2Stravis@sgi.com 	},
39756e17ca2Stravis@sgi.com 
3981e740163Stravis@sgi.com /* Setup GPP_D_0 Pad Config: */
39956e17ca2Stravis@sgi.com 	{	/* PAD_CFG_DW0_GPP_D_0 */
40056e17ca2Stravis@sgi.com 	.offset = 0x4c0,
40156e17ca2Stravis@sgi.com 	.mask = 0xffffffff,
40256e17ca2Stravis@sgi.com 	.data = 0x82020100,
40356e17ca2Stravis@sgi.com /*
40456e17ca2Stravis@sgi.com  *  31:30 Pad Reset Config (PADRSTCFG): = 2h  # PLTRST# (default)
40556e17ca2Stravis@sgi.com  *
40656e17ca2Stravis@sgi.com  *  29    RX Pad State Select (RXPADSTSEL): = 0 # Raw RX pad state directly
40756e17ca2Stravis@sgi.com  *                                                from RX buffer (default)
40856e17ca2Stravis@sgi.com  *
40956e17ca2Stravis@sgi.com  *  28    RX Raw Override to '1' (RXRAW1): = 0 # No Override
41056e17ca2Stravis@sgi.com  *
41156e17ca2Stravis@sgi.com  *  26:25 RX Level/Edge Configuration (RXEVCFG):
41256e17ca2Stravis@sgi.com  *      = 0h # Level
41356e17ca2Stravis@sgi.com  *      = 1h # Edge
41456e17ca2Stravis@sgi.com  *
41556e17ca2Stravis@sgi.com  *  23    RX Invert (RXINV): = 0 # No Inversion (signal active high)
41656e17ca2Stravis@sgi.com  *
41756e17ca2Stravis@sgi.com  *  20    GPIO Input Route IOxAPIC (GPIROUTIOXAPIC):
41856e17ca2Stravis@sgi.com  * = 0 # Routing does not cause peripheral IRQ...
41956e17ca2Stravis@sgi.com  *     # (we want an NMI not an IRQ)
42056e17ca2Stravis@sgi.com  *
42156e17ca2Stravis@sgi.com  *  19    GPIO Input Route SCI (GPIROUTSCI): = 0 # Routing does not cause SCI.
42256e17ca2Stravis@sgi.com  *  18    GPIO Input Route SMI (GPIROUTSMI): = 0 # Routing does not cause SMI.
42356e17ca2Stravis@sgi.com  *  17    GPIO Input Route NMI (GPIROUTNMI): = 1 # Routing can cause NMI.
42456e17ca2Stravis@sgi.com  *
42556e17ca2Stravis@sgi.com  *  11:10 Pad Mode (PMODE1/0): = 0h = GPIO control the Pad.
42656e17ca2Stravis@sgi.com  *   9    GPIO RX Disable (GPIORXDIS):
42756e17ca2Stravis@sgi.com  * = 0 # Enable the input buffer (active low enable)
42856e17ca2Stravis@sgi.com  *
42956e17ca2Stravis@sgi.com  *   8    GPIO TX Disable (GPIOTXDIS):
43056e17ca2Stravis@sgi.com  * = 1 # Disable the output buffer; i.e. Hi-Z
43156e17ca2Stravis@sgi.com  *
43256e17ca2Stravis@sgi.com  *   1 GPIO RX State (GPIORXSTATE): This is the current internal RX pad state..
43356e17ca2Stravis@sgi.com  *   0 GPIO TX State (GPIOTXSTATE):
43456e17ca2Stravis@sgi.com  * = 0 # (Leave at default)
43556e17ca2Stravis@sgi.com  */
43656e17ca2Stravis@sgi.com 	},
43756e17ca2Stravis@sgi.com 
43856e17ca2Stravis@sgi.com /* Pad Config DW1 */
43956e17ca2Stravis@sgi.com 	{	/* PAD_CFG_DW1_GPP_D_0 */
44056e17ca2Stravis@sgi.com 	.offset = 0x4c4,
44156e17ca2Stravis@sgi.com 	.mask = 0x3c00,
44256e17ca2Stravis@sgi.com 	.data = 0,	/* Termination = none (default) */
44356e17ca2Stravis@sgi.com 	},
44456e17ca2Stravis@sgi.com };
44556e17ca2Stravis@sgi.com 
uv_init_hubless_pch_d0(void)44656e17ca2Stravis@sgi.com static void uv_init_hubless_pch_d0(void)
44756e17ca2Stravis@sgi.com {
44856e17ca2Stravis@sgi.com 	int i, read;
44956e17ca2Stravis@sgi.com 
45056e17ca2Stravis@sgi.com 	read = *PCH_PCR_GPIO_ADDRESS(PAD_OWN_GPP_D_0);
45156e17ca2Stravis@sgi.com 	if (read != 0) {
45256e17ca2Stravis@sgi.com 		pr_info("UV: Hubless NMI already configured\n");
45356e17ca2Stravis@sgi.com 		return;
45456e17ca2Stravis@sgi.com 	}
45556e17ca2Stravis@sgi.com 
45656e17ca2Stravis@sgi.com 	nmi_debug("UV: Initializing UV Hubless NMI on PCH\n");
45756e17ca2Stravis@sgi.com 	for (i = 0; i < ARRAY_SIZE(init_nmi); i++) {
45856e17ca2Stravis@sgi.com 		uv_init_hubless_pch_io(init_nmi[i].offset,
45956e17ca2Stravis@sgi.com 					init_nmi[i].mask,
46056e17ca2Stravis@sgi.com 					init_nmi[i].data);
46156e17ca2Stravis@sgi.com 	}
46256e17ca2Stravis@sgi.com }
46356e17ca2Stravis@sgi.com 
uv_nmi_test_hubless(struct uv_hub_nmi_s * hub_nmi)464abdf1df6Stravis@sgi.com static int uv_nmi_test_hubless(struct uv_hub_nmi_s *hub_nmi)
465abdf1df6Stravis@sgi.com {
466abdf1df6Stravis@sgi.com 	int *pstat = PCH_PCR_GPIO_ADDRESS(GPI_NMI_STS_GPP_D_0);
467abdf1df6Stravis@sgi.com 	int status = *pstat;
468abdf1df6Stravis@sgi.com 
469abdf1df6Stravis@sgi.com 	hub_nmi->nmi_value = status;
470abdf1df6Stravis@sgi.com 	atomic_inc(&hub_nmi->read_mmr_count);
471abdf1df6Stravis@sgi.com 
472abdf1df6Stravis@sgi.com 	if (!(status & STS_GPP_D_0_MASK))	/* Not a UV external NMI */
473abdf1df6Stravis@sgi.com 		return 0;
474abdf1df6Stravis@sgi.com 
475abdf1df6Stravis@sgi.com 	*pstat = STS_GPP_D_0_MASK;	/* Is a UV NMI: clear GPP_D_0 status */
4761e740163Stravis@sgi.com 	(void)*pstat;			/* Flush write */
477abdf1df6Stravis@sgi.com 
478abdf1df6Stravis@sgi.com 	return 1;
479abdf1df6Stravis@sgi.com }
480abdf1df6Stravis@sgi.com 
uv_test_nmi(struct uv_hub_nmi_s * hub_nmi)481abdf1df6Stravis@sgi.com static int uv_test_nmi(struct uv_hub_nmi_s *hub_nmi)
482abdf1df6Stravis@sgi.com {
483abdf1df6Stravis@sgi.com 	if (hub_nmi->hub_present)
484abdf1df6Stravis@sgi.com 		return uv_nmi_test_mmr(hub_nmi);
485abdf1df6Stravis@sgi.com 
486abdf1df6Stravis@sgi.com 	if (hub_nmi->pch_owner)		/* Only PCH owner can check status */
487abdf1df6Stravis@sgi.com 		return uv_nmi_test_hubless(hub_nmi);
488abdf1df6Stravis@sgi.com 
489abdf1df6Stravis@sgi.com 	return -1;
490abdf1df6Stravis@sgi.com }
491abdf1df6Stravis@sgi.com 
492abdf1df6Stravis@sgi.com /*
4931e740163Stravis@sgi.com  * If first CPU in on this hub, set hub_nmi "in_nmi" and "owner" values and
4941e740163Stravis@sgi.com  * return true.  If first CPU in on the system, set global "in_nmi" flag.
4950d12ef0cSMike Travis  */
uv_set_in_nmi(int cpu,struct uv_hub_nmi_s * hub_nmi)4960d12ef0cSMike Travis static int uv_set_in_nmi(int cpu, struct uv_hub_nmi_s *hub_nmi)
4970d12ef0cSMike Travis {
4980d12ef0cSMike Travis 	int first = atomic_add_unless(&hub_nmi->in_nmi, 1, 1);
4990d12ef0cSMike Travis 
5000d12ef0cSMike Travis 	if (first) {
5010d12ef0cSMike Travis 		atomic_set(&hub_nmi->cpu_owner, cpu);
5020d12ef0cSMike Travis 		if (atomic_add_unless(&uv_in_nmi, 1, 1))
5030d12ef0cSMike Travis 			atomic_set(&uv_nmi_cpu, cpu);
5040d12ef0cSMike Travis 
5050d12ef0cSMike Travis 		atomic_inc(&hub_nmi->nmi_count);
5060d12ef0cSMike Travis 	}
5070d12ef0cSMike Travis 	return first;
5080d12ef0cSMike Travis }
5090d12ef0cSMike Travis 
5100d12ef0cSMike Travis /* Check if this is a system NMI event */
uv_check_nmi(struct uv_hub_nmi_s * hub_nmi)5110d12ef0cSMike Travis static int uv_check_nmi(struct uv_hub_nmi_s *hub_nmi)
5120d12ef0cSMike Travis {
5130d12ef0cSMike Travis 	int cpu = smp_processor_id();
5140d12ef0cSMike Travis 	int nmi = 0;
515abdf1df6Stravis@sgi.com 	int nmi_detected = 0;
5160d12ef0cSMike Travis 
5170d12ef0cSMike Travis 	local64_inc(&uv_nmi_count);
518e1632170SChristoph Lameter 	this_cpu_inc(uv_cpu_nmi.queries);
5190d12ef0cSMike Travis 
5200d12ef0cSMike Travis 	do {
5210d12ef0cSMike Travis 		nmi = atomic_read(&hub_nmi->in_nmi);
5220d12ef0cSMike Travis 		if (nmi)
5230d12ef0cSMike Travis 			break;
5240d12ef0cSMike Travis 
5250d12ef0cSMike Travis 		if (raw_spin_trylock(&hub_nmi->nmi_lock)) {
526abdf1df6Stravis@sgi.com 			nmi_detected = uv_test_nmi(hub_nmi);
5270d12ef0cSMike Travis 
5281e740163Stravis@sgi.com 			/* Check flag for UV external NMI */
529abdf1df6Stravis@sgi.com 			if (nmi_detected > 0) {
5300d12ef0cSMike Travis 				uv_set_in_nmi(cpu, hub_nmi);
5310d12ef0cSMike Travis 				nmi = 1;
5320d12ef0cSMike Travis 				break;
5330d12ef0cSMike Travis 			}
5340d12ef0cSMike Travis 
535abdf1df6Stravis@sgi.com 			/* A non-PCH node in a hubless system waits for NMI */
536abdf1df6Stravis@sgi.com 			else if (nmi_detected < 0)
537abdf1df6Stravis@sgi.com 				goto slave_wait;
538abdf1df6Stravis@sgi.com 
539abdf1df6Stravis@sgi.com 			/* MMR/PCH NMI flag is clear */
5400d12ef0cSMike Travis 			raw_spin_unlock(&hub_nmi->nmi_lock);
5410d12ef0cSMike Travis 
5420d12ef0cSMike Travis 		} else {
543abdf1df6Stravis@sgi.com 
544abdf1df6Stravis@sgi.com 			/* Wait a moment for the HUB NMI locker to set flag */
545abdf1df6Stravis@sgi.com slave_wait:		cpu_relax();
5460d12ef0cSMike Travis 			udelay(uv_nmi_slave_delay);
5470d12ef0cSMike Travis 
5481e740163Stravis@sgi.com 			/* Re-check hub in_nmi flag */
5490d12ef0cSMike Travis 			nmi = atomic_read(&hub_nmi->in_nmi);
5500d12ef0cSMike Travis 			if (nmi)
5510d12ef0cSMike Travis 				break;
5520d12ef0cSMike Travis 		}
5530d12ef0cSMike Travis 
554abdf1df6Stravis@sgi.com 		/*
555abdf1df6Stravis@sgi.com 		 * Check if this BMC missed setting the MMR NMI flag (or)
556abdf1df6Stravis@sgi.com 		 * UV hubless system where only PCH owner can check flag
557abdf1df6Stravis@sgi.com 		 */
5580d12ef0cSMike Travis 		if (!nmi) {
5590d12ef0cSMike Travis 			nmi = atomic_read(&uv_in_nmi);
5600d12ef0cSMike Travis 			if (nmi)
5610d12ef0cSMike Travis 				uv_set_in_nmi(cpu, hub_nmi);
5620d12ef0cSMike Travis 		}
5630d12ef0cSMike Travis 
564abdf1df6Stravis@sgi.com 		/* If we're holding the hub lock, release it now */
565abdf1df6Stravis@sgi.com 		if (nmi_detected < 0)
566abdf1df6Stravis@sgi.com 			raw_spin_unlock(&hub_nmi->nmi_lock);
567abdf1df6Stravis@sgi.com 
5680d12ef0cSMike Travis 	} while (0);
5690d12ef0cSMike Travis 
5700d12ef0cSMike Travis 	if (!nmi)
5710d12ef0cSMike Travis 		local64_inc(&uv_nmi_misses);
5720d12ef0cSMike Travis 
5730d12ef0cSMike Travis 	return nmi;
5740d12ef0cSMike Travis }
5750d12ef0cSMike Travis 
5760d12ef0cSMike Travis /* Need to reset the NMI MMR register, but only once per hub. */
uv_clear_nmi(int cpu)5770d12ef0cSMike Travis static inline void uv_clear_nmi(int cpu)
5780d12ef0cSMike Travis {
5790d12ef0cSMike Travis 	struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi;
5800d12ef0cSMike Travis 
5810d12ef0cSMike Travis 	if (cpu == atomic_read(&hub_nmi->cpu_owner)) {
5820d12ef0cSMike Travis 		atomic_set(&hub_nmi->cpu_owner, -1);
5830d12ef0cSMike Travis 		atomic_set(&hub_nmi->in_nmi, 0);
584abdf1df6Stravis@sgi.com 		if (hub_nmi->hub_present)
5850d12ef0cSMike Travis 			uv_local_mmr_clear_nmi();
586abdf1df6Stravis@sgi.com 		else
587abdf1df6Stravis@sgi.com 			uv_reassert_nmi();
5880d12ef0cSMike Travis 		raw_spin_unlock(&hub_nmi->nmi_lock);
5890d12ef0cSMike Travis 	}
5900d12ef0cSMike Travis }
5910d12ef0cSMike Travis 
592a97673a1SIngo Molnar /* Ping non-responding CPU's attempting to force them into the NMI handler */
uv_nmi_nr_cpus_ping(void)5930d12ef0cSMike Travis static void uv_nmi_nr_cpus_ping(void)
5940d12ef0cSMike Travis {
5950d12ef0cSMike Travis 	int cpu;
5960d12ef0cSMike Travis 
5970d12ef0cSMike Travis 	for_each_cpu(cpu, uv_nmi_cpu_mask)
598e1632170SChristoph Lameter 		uv_cpu_nmi_per(cpu).pinging = 1;
5990d12ef0cSMike Travis 
60028b82352SDave Hansen 	__apic_send_IPI_mask(uv_nmi_cpu_mask, APIC_DM_NMI);
6010d12ef0cSMike Travis }
6020d12ef0cSMike Travis 
6031e740163Stravis@sgi.com /* Clean up flags for CPU's that ignored both NMI and ping */
uv_nmi_cleanup_mask(void)6040d12ef0cSMike Travis static void uv_nmi_cleanup_mask(void)
6050d12ef0cSMike Travis {
6060d12ef0cSMike Travis 	int cpu;
6070d12ef0cSMike Travis 
6080d12ef0cSMike Travis 	for_each_cpu(cpu, uv_nmi_cpu_mask) {
609e1632170SChristoph Lameter 		uv_cpu_nmi_per(cpu).pinging =  0;
610e1632170SChristoph Lameter 		uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_OUT;
6110d12ef0cSMike Travis 		cpumask_clear_cpu(cpu, uv_nmi_cpu_mask);
6120d12ef0cSMike Travis 	}
6130d12ef0cSMike Travis }
6140d12ef0cSMike Travis 
6151e740163Stravis@sgi.com /* Loop waiting as CPU's enter NMI handler */
uv_nmi_wait_cpus(int first)6160d12ef0cSMike Travis static int uv_nmi_wait_cpus(int first)
6170d12ef0cSMike Travis {
6180d12ef0cSMike Travis 	int i, j, k, n = num_online_cpus();
6190d12ef0cSMike Travis 	int last_k = 0, waiting = 0;
620abdf1df6Stravis@sgi.com 	int cpu = smp_processor_id();
6210d12ef0cSMike Travis 
6220d12ef0cSMike Travis 	if (first) {
6230d12ef0cSMike Travis 		cpumask_copy(uv_nmi_cpu_mask, cpu_online_mask);
6240d12ef0cSMike Travis 		k = 0;
6250d12ef0cSMike Travis 	} else {
6260d12ef0cSMike Travis 		k = n - cpumask_weight(uv_nmi_cpu_mask);
6270d12ef0cSMike Travis 	}
6280d12ef0cSMike Travis 
6291e740163Stravis@sgi.com 	/* PCH NMI causes only one CPU to respond */
630abdf1df6Stravis@sgi.com 	if (first && uv_pch_intr_now_enabled) {
631abdf1df6Stravis@sgi.com 		cpumask_clear_cpu(cpu, uv_nmi_cpu_mask);
632abdf1df6Stravis@sgi.com 		return n - k - 1;
633abdf1df6Stravis@sgi.com 	}
634abdf1df6Stravis@sgi.com 
6350d12ef0cSMike Travis 	udelay(uv_nmi_initial_delay);
6360d12ef0cSMike Travis 	for (i = 0; i < uv_nmi_retry_count; i++) {
6370d12ef0cSMike Travis 		int loop_delay = uv_nmi_loop_delay;
6380d12ef0cSMike Travis 
6390d12ef0cSMike Travis 		for_each_cpu(j, uv_nmi_cpu_mask) {
640e1632170SChristoph Lameter 			if (uv_cpu_nmi_per(j).state) {
6410d12ef0cSMike Travis 				cpumask_clear_cpu(j, uv_nmi_cpu_mask);
6420d12ef0cSMike Travis 				if (++k >= n)
6430d12ef0cSMike Travis 					break;
6440d12ef0cSMike Travis 			}
6450d12ef0cSMike Travis 		}
6460d12ef0cSMike Travis 		if (k >= n) {		/* all in? */
6470d12ef0cSMike Travis 			k = n;
6480d12ef0cSMike Travis 			break;
6490d12ef0cSMike Travis 		}
6501e740163Stravis@sgi.com 		if (last_k != k) {	/* abort if no new CPU's coming in */
6510d12ef0cSMike Travis 			last_k = k;
6520d12ef0cSMike Travis 			waiting = 0;
6530d12ef0cSMike Travis 		} else if (++waiting > uv_nmi_wait_count)
6540d12ef0cSMike Travis 			break;
6550d12ef0cSMike Travis 
6561e740163Stravis@sgi.com 		/* Extend delay if waiting only for CPU 0: */
6570d12ef0cSMike Travis 		if (waiting && (n - k) == 1 &&
6580d12ef0cSMike Travis 		    cpumask_test_cpu(0, uv_nmi_cpu_mask))
6590d12ef0cSMike Travis 			loop_delay *= 100;
6600d12ef0cSMike Travis 
6610d12ef0cSMike Travis 		udelay(loop_delay);
6620d12ef0cSMike Travis 	}
6630d12ef0cSMike Travis 	atomic_set(&uv_nmi_cpus_in_nmi, k);
6640d12ef0cSMike Travis 	return n - k;
6650d12ef0cSMike Travis }
6660d12ef0cSMike Travis 
6671e740163Stravis@sgi.com /* Wait until all slave CPU's have entered UV NMI handler */
uv_nmi_wait(int master)6680d12ef0cSMike Travis static void uv_nmi_wait(int master)
6690d12ef0cSMike Travis {
6701e740163Stravis@sgi.com 	/* Indicate this CPU is in: */
671e1632170SChristoph Lameter 	this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_IN);
6720d12ef0cSMike Travis 
6731e740163Stravis@sgi.com 	/* If not the first CPU in (the master), then we are a slave CPU */
6740d12ef0cSMike Travis 	if (!master)
6750d12ef0cSMike Travis 		return;
6760d12ef0cSMike Travis 
6770d12ef0cSMike Travis 	do {
6781e740163Stravis@sgi.com 		/* Wait for all other CPU's to gather here */
6790d12ef0cSMike Travis 		if (!uv_nmi_wait_cpus(1))
6800d12ef0cSMike Travis 			break;
6810d12ef0cSMike Travis 
6821e740163Stravis@sgi.com 		/* If not all made it in, send IPI NMI to them */
683abdf1df6Stravis@sgi.com 		pr_alert("UV: Sending NMI IPI to %d CPUs: %*pbl\n",
684bf58b487STejun Heo 			 cpumask_weight(uv_nmi_cpu_mask),
685bf58b487STejun Heo 			 cpumask_pr_args(uv_nmi_cpu_mask));
686bf58b487STejun Heo 
6870d12ef0cSMike Travis 		uv_nmi_nr_cpus_ping();
6880d12ef0cSMike Travis 
6891e740163Stravis@sgi.com 		/* If all CPU's are in, then done */
6900d12ef0cSMike Travis 		if (!uv_nmi_wait_cpus(0))
6910d12ef0cSMike Travis 			break;
6920d12ef0cSMike Travis 
693bf58b487STejun Heo 		pr_alert("UV: %d CPUs not in NMI loop: %*pbl\n",
694bf58b487STejun Heo 			 cpumask_weight(uv_nmi_cpu_mask),
695bf58b487STejun Heo 			 cpumask_pr_args(uv_nmi_cpu_mask));
6960d12ef0cSMike Travis 	} while (0);
6970d12ef0cSMike Travis 
6980d12ef0cSMike Travis 	pr_alert("UV: %d of %d CPUs in NMI\n",
6990d12ef0cSMike Travis 		atomic_read(&uv_nmi_cpus_in_nmi), num_online_cpus());
7000d12ef0cSMike Travis }
7010d12ef0cSMike Travis 
702d0a9964eSMike Travis /* Dump Instruction Pointer header */
uv_nmi_dump_cpu_ip_hdr(void)7033c121d9aSMike Travis static void uv_nmi_dump_cpu_ip_hdr(void)
7043c121d9aSMike Travis {
705d0a9964eSMike Travis 	pr_info("\nUV: %4s %6s %-32s %s   (Note: PID 0 not listed)\n",
7063c121d9aSMike Travis 		"CPU", "PID", "COMMAND", "IP");
7073c121d9aSMike Travis }
7083c121d9aSMike Travis 
709d0a9964eSMike Travis /* Dump Instruction Pointer info */
uv_nmi_dump_cpu_ip(int cpu,struct pt_regs * regs)7103c121d9aSMike Travis static void uv_nmi_dump_cpu_ip(int cpu, struct pt_regs *regs)
7113c121d9aSMike Travis {
712bb5e5ce5SJosh Poimboeuf 	pr_info("UV: %4d %6d %-32.32s %pS",
713bb5e5ce5SJosh Poimboeuf 		cpu, current->pid, current->comm, (void *)regs->ip);
7143c121d9aSMike Travis }
7153c121d9aSMike Travis 
716d0a9964eSMike Travis /*
717d0a9964eSMike Travis  * Dump this CPU's state.  If action was set to "kdump" and the crash_kexec
718d0a9964eSMike Travis  * failed, then we provide "dump" as an alternate action.  Action "dump" now
719d0a9964eSMike Travis  * also includes the show "ips" (instruction pointers) action whereas the
720d0a9964eSMike Travis  * action "ips" only displays instruction pointers for the non-idle CPU's.
721d0a9964eSMike Travis  * This is an abbreviated form of the "ps" command.
722d0a9964eSMike Travis  */
uv_nmi_dump_state_cpu(int cpu,struct pt_regs * regs)7230d12ef0cSMike Travis static void uv_nmi_dump_state_cpu(int cpu, struct pt_regs *regs)
7240d12ef0cSMike Travis {
7250d12ef0cSMike Travis 	const char *dots = " ................................. ";
7260d12ef0cSMike Travis 
7273c121d9aSMike Travis 	if (cpu == 0)
7283c121d9aSMike Travis 		uv_nmi_dump_cpu_ip_hdr();
7293c121d9aSMike Travis 
730d0a9964eSMike Travis 	if (current->pid != 0 || !uv_nmi_action_is("ips"))
7313c121d9aSMike Travis 		uv_nmi_dump_cpu_ip(cpu, regs);
7323c121d9aSMike Travis 
733d0a9964eSMike Travis 	if (uv_nmi_action_is("dump")) {
734d0a9964eSMike Travis 		pr_info("UV:%sNMI process trace for CPU %d\n", dots, cpu);
7350d12ef0cSMike Travis 		show_regs(regs);
7363c121d9aSMike Travis 	}
737d0a9964eSMike Travis 
738e1632170SChristoph Lameter 	this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_DUMP_DONE);
7390d12ef0cSMike Travis }
7400d12ef0cSMike Travis 
7411e740163Stravis@sgi.com /* Trigger a slave CPU to dump it's state */
uv_nmi_trigger_dump(int cpu)7420d12ef0cSMike Travis static void uv_nmi_trigger_dump(int cpu)
7430d12ef0cSMike Travis {
7440d12ef0cSMike Travis 	int retry = uv_nmi_trigger_delay;
7450d12ef0cSMike Travis 
746e1632170SChristoph Lameter 	if (uv_cpu_nmi_per(cpu).state != UV_NMI_STATE_IN)
7470d12ef0cSMike Travis 		return;
7480d12ef0cSMike Travis 
749e1632170SChristoph Lameter 	uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_DUMP;
7500d12ef0cSMike Travis 	do {
7510d12ef0cSMike Travis 		cpu_relax();
7520d12ef0cSMike Travis 		udelay(10);
753e1632170SChristoph Lameter 		if (uv_cpu_nmi_per(cpu).state
7540d12ef0cSMike Travis 				!= UV_NMI_STATE_DUMP)
7550d12ef0cSMike Travis 			return;
7560d12ef0cSMike Travis 	} while (--retry > 0);
7570d12ef0cSMike Travis 
7580d12ef0cSMike Travis 	pr_crit("UV: CPU %d stuck in process dump function\n", cpu);
759e1632170SChristoph Lameter 	uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_DUMP_DONE;
7600d12ef0cSMike Travis }
7610d12ef0cSMike Travis 
7621e740163Stravis@sgi.com /* Wait until all CPU's ready to exit */
uv_nmi_sync_exit(int master)7630d12ef0cSMike Travis static void uv_nmi_sync_exit(int master)
7640d12ef0cSMike Travis {
7650d12ef0cSMike Travis 	atomic_dec(&uv_nmi_cpus_in_nmi);
7660d12ef0cSMike Travis 	if (master) {
7670d12ef0cSMike Travis 		while (atomic_read(&uv_nmi_cpus_in_nmi) > 0)
7680d12ef0cSMike Travis 			cpu_relax();
7690d12ef0cSMike Travis 		atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR);
7700d12ef0cSMike Travis 	} else {
7710d12ef0cSMike Travis 		while (atomic_read(&uv_nmi_slave_continue))
7720d12ef0cSMike Travis 			cpu_relax();
7730d12ef0cSMike Travis 	}
7740d12ef0cSMike Travis }
7750d12ef0cSMike Travis 
776278c9b09Stravis@sgi.com /* Current "health" check is to check which CPU's are responsive */
uv_nmi_action_health(int cpu,struct pt_regs * regs,int master)777278c9b09Stravis@sgi.com static void uv_nmi_action_health(int cpu, struct pt_regs *regs, int master)
778278c9b09Stravis@sgi.com {
779278c9b09Stravis@sgi.com 	if (master) {
780278c9b09Stravis@sgi.com 		int in = atomic_read(&uv_nmi_cpus_in_nmi);
781278c9b09Stravis@sgi.com 		int out = num_online_cpus() - in;
782278c9b09Stravis@sgi.com 
783278c9b09Stravis@sgi.com 		pr_alert("UV: NMI CPU health check (non-responding:%d)\n", out);
784278c9b09Stravis@sgi.com 		atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT);
785278c9b09Stravis@sgi.com 	} else {
786278c9b09Stravis@sgi.com 		while (!atomic_read(&uv_nmi_slave_continue))
787278c9b09Stravis@sgi.com 			cpu_relax();
788278c9b09Stravis@sgi.com 	}
789278c9b09Stravis@sgi.com 	uv_nmi_sync_exit(master);
790278c9b09Stravis@sgi.com }
791278c9b09Stravis@sgi.com 
7921e740163Stravis@sgi.com /* Walk through CPU list and dump state of each */
uv_nmi_dump_state(int cpu,struct pt_regs * regs,int master)7930d12ef0cSMike Travis static void uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master)
7940d12ef0cSMike Travis {
7950d12ef0cSMike Travis 	if (master) {
7960d12ef0cSMike Travis 		int tcpu;
7970d12ef0cSMike Travis 		int ignored = 0;
7980d12ef0cSMike Travis 		int saved_console_loglevel = console_loglevel;
7990d12ef0cSMike Travis 
8003c121d9aSMike Travis 		pr_alert("UV: tracing %s for %d CPUs from CPU %d\n",
8013c121d9aSMike Travis 			uv_nmi_action_is("ips") ? "IPs" : "processes",
8020d12ef0cSMike Travis 			atomic_read(&uv_nmi_cpus_in_nmi), cpu);
8030d12ef0cSMike Travis 
8040d12ef0cSMike Travis 		console_loglevel = uv_nmi_loglevel;
8050d12ef0cSMike Travis 		atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT);
8060d12ef0cSMike Travis 		for_each_online_cpu(tcpu) {
8070d12ef0cSMike Travis 			if (cpumask_test_cpu(tcpu, uv_nmi_cpu_mask))
8080d12ef0cSMike Travis 				ignored++;
8090d12ef0cSMike Travis 			else if (tcpu == cpu)
8100d12ef0cSMike Travis 				uv_nmi_dump_state_cpu(tcpu, regs);
8110d12ef0cSMike Travis 			else
8120d12ef0cSMike Travis 				uv_nmi_trigger_dump(tcpu);
8130d12ef0cSMike Travis 		}
8140d12ef0cSMike Travis 		if (ignored)
815d0a9964eSMike Travis 			pr_alert("UV: %d CPUs ignored NMI\n", ignored);
8160d12ef0cSMike Travis 
8170d12ef0cSMike Travis 		console_loglevel = saved_console_loglevel;
8180d12ef0cSMike Travis 		pr_alert("UV: process trace complete\n");
8190d12ef0cSMike Travis 	} else {
8200d12ef0cSMike Travis 		while (!atomic_read(&uv_nmi_slave_continue))
8210d12ef0cSMike Travis 			cpu_relax();
822e1632170SChristoph Lameter 		while (this_cpu_read(uv_cpu_nmi.state) != UV_NMI_STATE_DUMP)
8230d12ef0cSMike Travis 			cpu_relax();
8240d12ef0cSMike Travis 		uv_nmi_dump_state_cpu(cpu, regs);
8250d12ef0cSMike Travis 	}
8260d12ef0cSMike Travis 	uv_nmi_sync_exit(master);
8270d12ef0cSMike Travis }
8280d12ef0cSMike Travis 
uv_nmi_touch_watchdogs(void)8290d12ef0cSMike Travis static void uv_nmi_touch_watchdogs(void)
8300d12ef0cSMike Travis {
8310d12ef0cSMike Travis 	touch_softlockup_watchdog_sync();
8320d12ef0cSMike Travis 	clocksource_touch_watchdog();
8330d12ef0cSMike Travis 	rcu_cpu_stall_reset();
8340d12ef0cSMike Travis 	touch_nmi_watchdog();
8350d12ef0cSMike Travis }
8360d12ef0cSMike Travis 
uv_nmi_kdump(int cpu,int main,struct pt_regs * regs)8370b45143bSGeorges Aureau static void uv_nmi_kdump(int cpu, int main, struct pt_regs *regs)
83812ba6c99SMike Travis {
8390b45143bSGeorges Aureau 	/* Check if kdump kernel loaded for both main and secondary CPUs */
8400b45143bSGeorges Aureau 	if (!kexec_crash_image) {
8410b45143bSGeorges Aureau 		if (main)
8420b45143bSGeorges Aureau 			pr_err("UV: NMI error: kdump kernel not loaded\n");
8430b45143bSGeorges Aureau 		return;
8440b45143bSGeorges Aureau 	}
8450b45143bSGeorges Aureau 
84612ba6c99SMike Travis 	/* Call crash to dump system state */
8470b45143bSGeorges Aureau 	if (main) {
84812ba6c99SMike Travis 		pr_emerg("UV: NMI executing crash_kexec on CPU%d\n", cpu);
84912ba6c99SMike Travis 		crash_kexec(regs);
85012ba6c99SMike Travis 
8510b45143bSGeorges Aureau 		pr_emerg("UV: crash_kexec unexpectedly returned\n");
852d0a9964eSMike Travis 		atomic_set(&uv_nmi_kexec_failed, 1);
85312ba6c99SMike Travis 
8540b45143bSGeorges Aureau 	} else { /* secondary */
8550b45143bSGeorges Aureau 
8560b45143bSGeorges Aureau 		/* If kdump kernel fails, secondaries will exit this loop */
8570b45143bSGeorges Aureau 		while (atomic_read(&uv_nmi_kexec_failed) == 0) {
8580b45143bSGeorges Aureau 
8590b45143bSGeorges Aureau 			/* Once shootdown cpus starts, they do not return */
8600b45143bSGeorges Aureau 			run_crash_ipi_callback(regs);
8610b45143bSGeorges Aureau 
86212ba6c99SMike Travis 			mdelay(10);
86312ba6c99SMike Travis 		}
86412ba6c99SMike Travis 	}
8650b45143bSGeorges Aureau }
86612ba6c99SMike Travis 
86764389998SMike Travis #ifdef CONFIG_KGDB
868e379ea82SMike Travis #ifdef CONFIG_KGDB_KDB
uv_nmi_kdb_reason(void)86964389998SMike Travis static inline int uv_nmi_kdb_reason(void)
870e379ea82SMike Travis {
87164389998SMike Travis 	return KDB_REASON_SYSTEM_NMI;
87264389998SMike Travis }
87364389998SMike Travis #else /* !CONFIG_KGDB_KDB */
uv_nmi_kdb_reason(void)87464389998SMike Travis static inline int uv_nmi_kdb_reason(void)
87564389998SMike Travis {
876abdf1df6Stravis@sgi.com 	/* Ensure user is expecting to attach gdb remote */
87764389998SMike Travis 	if (uv_nmi_action_is("kgdb"))
87864389998SMike Travis 		return 0;
87964389998SMike Travis 
88064389998SMike Travis 	pr_err("UV: NMI error: KDB is not enabled in this kernel\n");
88164389998SMike Travis 	return -1;
88264389998SMike Travis }
88364389998SMike Travis #endif /* CONFIG_KGDB_KDB */
88464389998SMike Travis 
88564389998SMike Travis /*
88664389998SMike Travis  * Call KGDB/KDB from NMI handler
88764389998SMike Travis  *
88864389998SMike Travis  * Note that if both KGDB and KDB are configured, then the action of 'kgdb' or
889d9f6e12fSIngo Molnar  * 'kdb' has no affect on which is used.  See the KGDB documentation for further
89064389998SMike Travis  * information.
89164389998SMike Travis  */
uv_call_kgdb_kdb(int cpu,struct pt_regs * regs,int master)89264389998SMike Travis static void uv_call_kgdb_kdb(int cpu, struct pt_regs *regs, int master)
89364389998SMike Travis {
89464389998SMike Travis 	if (master) {
89564389998SMike Travis 		int reason = uv_nmi_kdb_reason();
896e379ea82SMike Travis 		int ret;
897e379ea82SMike Travis 
89864389998SMike Travis 		if (reason < 0)
89964389998SMike Travis 			return;
90064389998SMike Travis 
9011e740163Stravis@sgi.com 		/* Call KGDB NMI handler as MASTER */
90264389998SMike Travis 		ret = kgdb_nmicallin(cpu, X86_TRAP_NMI, regs, reason,
90364389998SMike Travis 				&uv_nmi_slave_continue);
904e379ea82SMike Travis 		if (ret) {
90564389998SMike Travis 			pr_alert("KGDB returned error, is kgdboc set?\n");
906e379ea82SMike Travis 			atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT);
907e379ea82SMike Travis 		}
908e379ea82SMike Travis 	} else {
9091e740163Stravis@sgi.com 		/* Wait for KGDB signal that it's ready for slaves to enter */
910e379ea82SMike Travis 		int sig;
911e379ea82SMike Travis 
912e379ea82SMike Travis 		do {
913e379ea82SMike Travis 			cpu_relax();
914e379ea82SMike Travis 			sig = atomic_read(&uv_nmi_slave_continue);
915e379ea82SMike Travis 		} while (!sig);
916e379ea82SMike Travis 
9171e740163Stravis@sgi.com 		/* Call KGDB as slave */
918e379ea82SMike Travis 		if (sig == SLAVE_CONTINUE)
919e379ea82SMike Travis 			kgdb_nmicallback(cpu, regs);
920e379ea82SMike Travis 	}
921e379ea82SMike Travis 	uv_nmi_sync_exit(master);
922e379ea82SMike Travis }
923e379ea82SMike Travis 
92464389998SMike Travis #else /* !CONFIG_KGDB */
uv_call_kgdb_kdb(int cpu,struct pt_regs * regs,int master)92564389998SMike Travis static inline void uv_call_kgdb_kdb(int cpu, struct pt_regs *regs, int master)
926e379ea82SMike Travis {
92764389998SMike Travis 	pr_err("UV: NMI error: KGDB is not enabled in this kernel\n");
928e379ea82SMike Travis }
92964389998SMike Travis #endif /* !CONFIG_KGDB */
930e379ea82SMike Travis 
9310d12ef0cSMike Travis /*
9320d12ef0cSMike Travis  * UV NMI handler
9331e019421SMike Travis  */
uv_handle_nmi(unsigned int reason,struct pt_regs * regs)934d553d03fSColin Ian King static int uv_handle_nmi(unsigned int reason, struct pt_regs *regs)
9351e019421SMike Travis {
9360d12ef0cSMike Travis 	struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi;
9370d12ef0cSMike Travis 	int cpu = smp_processor_id();
9380d12ef0cSMike Travis 	int master = 0;
9390d12ef0cSMike Travis 	unsigned long flags;
9401e019421SMike Travis 
9410d12ef0cSMike Travis 	local_irq_save(flags);
9421e019421SMike Travis 
9430d12ef0cSMike Travis 	/* If not a UV System NMI, ignore */
944e1632170SChristoph Lameter 	if (!this_cpu_read(uv_cpu_nmi.pinging) && !uv_check_nmi(hub_nmi)) {
9450d12ef0cSMike Travis 		local_irq_restore(flags);
9461e019421SMike Travis 		return NMI_DONE;
9470d12ef0cSMike Travis 	}
9481e019421SMike Travis 
9490d12ef0cSMike Travis 	/* Indicate we are the first CPU into the NMI handler */
9500d12ef0cSMike Travis 	master = (atomic_read(&uv_nmi_cpu) == cpu);
9511e019421SMike Travis 
95212ba6c99SMike Travis 	/* If NMI action is "kdump", then attempt to do it */
953d0a9964eSMike Travis 	if (uv_nmi_action_is("kdump")) {
95412ba6c99SMike Travis 		uv_nmi_kdump(cpu, master, regs);
95512ba6c99SMike Travis 
956d0a9964eSMike Travis 		/* Unexpected return, revert action to "dump" */
957d0a9964eSMike Travis 		if (master)
958*1e6f01f7SJustin Stitt 			strscpy(uv_nmi_action, "dump", sizeof(uv_nmi_action));
959d0a9964eSMike Travis 	}
960d0a9964eSMike Travis 
9611e740163Stravis@sgi.com 	/* Pause as all CPU's enter the NMI handler */
9620d12ef0cSMike Travis 	uv_nmi_wait(master);
9630d12ef0cSMike Travis 
964abdf1df6Stravis@sgi.com 	/* Process actions other than "kdump": */
965278c9b09Stravis@sgi.com 	if (uv_nmi_action_is("health")) {
966278c9b09Stravis@sgi.com 		uv_nmi_action_health(cpu, regs, master);
967278c9b09Stravis@sgi.com 	} else if (uv_nmi_action_is("ips") || uv_nmi_action_is("dump")) {
9680d12ef0cSMike Travis 		uv_nmi_dump_state(cpu, regs, master);
969abdf1df6Stravis@sgi.com 	} else if (uv_nmi_action_is("kdb") || uv_nmi_action_is("kgdb")) {
97064389998SMike Travis 		uv_call_kgdb_kdb(cpu, regs, master);
971abdf1df6Stravis@sgi.com 	} else {
972abdf1df6Stravis@sgi.com 		if (master)
973abdf1df6Stravis@sgi.com 			pr_alert("UV: unknown NMI action: %s\n", uv_nmi_action);
974abdf1df6Stravis@sgi.com 		uv_nmi_sync_exit(master);
975abdf1df6Stravis@sgi.com 	}
976e379ea82SMike Travis 
977abdf1df6Stravis@sgi.com 	/* Clear per_cpu "in_nmi" flag */
978e1632170SChristoph Lameter 	this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_OUT);
9790d12ef0cSMike Travis 
9800d12ef0cSMike Travis 	/* Clear MMR NMI flag on each hub */
9810d12ef0cSMike Travis 	uv_clear_nmi(cpu);
9820d12ef0cSMike Travis 
9830d12ef0cSMike Travis 	/* Clear global flags */
9840d12ef0cSMike Travis 	if (master) {
9853a5ff1f6SYury Norov 		if (!cpumask_empty(uv_nmi_cpu_mask))
9860d12ef0cSMike Travis 			uv_nmi_cleanup_mask();
9870d12ef0cSMike Travis 		atomic_set(&uv_nmi_cpus_in_nmi, -1);
9880d12ef0cSMike Travis 		atomic_set(&uv_nmi_cpu, -1);
9890d12ef0cSMike Travis 		atomic_set(&uv_in_nmi, 0);
990d0a9964eSMike Travis 		atomic_set(&uv_nmi_kexec_failed, 0);
991abdf1df6Stravis@sgi.com 		atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR);
9920d12ef0cSMike Travis 	}
9930d12ef0cSMike Travis 
9940d12ef0cSMike Travis 	uv_nmi_touch_watchdogs();
9950d12ef0cSMike Travis 	local_irq_restore(flags);
9961e019421SMike Travis 
9971e019421SMike Travis 	return NMI_HANDLED;
9981e019421SMike Travis }
9991e019421SMike Travis 
10000d12ef0cSMike Travis /*
10011e740163Stravis@sgi.com  * NMI handler for pulling in CPU's when perf events are grabbing our NMI
10020d12ef0cSMike Travis  */
uv_handle_nmi_ping(unsigned int reason,struct pt_regs * regs)100374c93f9dSMike Travis static int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs)
10040d12ef0cSMike Travis {
10050d12ef0cSMike Travis 	int ret;
10060d12ef0cSMike Travis 
1007e1632170SChristoph Lameter 	this_cpu_inc(uv_cpu_nmi.queries);
1008e1632170SChristoph Lameter 	if (!this_cpu_read(uv_cpu_nmi.pinging)) {
10090d12ef0cSMike Travis 		local64_inc(&uv_nmi_ping_misses);
10100d12ef0cSMike Travis 		return NMI_DONE;
10110d12ef0cSMike Travis 	}
10120d12ef0cSMike Travis 
1013e1632170SChristoph Lameter 	this_cpu_inc(uv_cpu_nmi.pings);
10140d12ef0cSMike Travis 	local64_inc(&uv_nmi_ping_count);
10150d12ef0cSMike Travis 	ret = uv_handle_nmi(reason, regs);
1016e1632170SChristoph Lameter 	this_cpu_write(uv_cpu_nmi.pinging, 0);
10170d12ef0cSMike Travis 	return ret;
10180d12ef0cSMike Travis }
10190d12ef0cSMike Travis 
uv_register_nmi_notifier(void)102074c93f9dSMike Travis static void uv_register_nmi_notifier(void)
10211e019421SMike Travis {
10221e019421SMike Travis 	if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv"))
10230d12ef0cSMike Travis 		pr_warn("UV: NMI handler failed to register\n");
10240d12ef0cSMike Travis 
10250d12ef0cSMike Travis 	if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping"))
10260d12ef0cSMike Travis 		pr_warn("UV: PING NMI handler failed to register\n");
10271e019421SMike Travis }
10281e019421SMike Travis 
uv_nmi_init(void)10291e019421SMike Travis void uv_nmi_init(void)
10301e019421SMike Travis {
10311e019421SMike Travis 	unsigned int value;
10321e019421SMike Travis 
10331e019421SMike Travis 	/*
10341e740163Stravis@sgi.com 	 * Unmask NMI on all CPU's
10351e019421SMike Travis 	 */
10361e019421SMike Travis 	value = apic_read(APIC_LVT1) | APIC_DM_NMI;
10371e019421SMike Travis 	value &= ~APIC_LVT_MASKED;
10381e019421SMike Travis 	apic_write(APIC_LVT1, value);
10391e019421SMike Travis }
10401e019421SMike Travis 
1041abdf1df6Stravis@sgi.com /* Setup HUB NMI info */
uv_nmi_setup_common(bool hubbed)1042d553d03fSColin Ian King static void __init uv_nmi_setup_common(bool hubbed)
10430d12ef0cSMike Travis {
10440d12ef0cSMike Travis 	int size = sizeof(void *) * (1 << NODES_SHIFT);
1045abdf1df6Stravis@sgi.com 	int cpu;
10460d12ef0cSMike Travis 
10470d12ef0cSMike Travis 	uv_hub_nmi_list = kzalloc(size, GFP_KERNEL);
1048abdf1df6Stravis@sgi.com 	nmi_debug("UV: NMI hub list @ 0x%p (%d)\n", uv_hub_nmi_list, size);
10490d12ef0cSMike Travis 	BUG_ON(!uv_hub_nmi_list);
10500d12ef0cSMike Travis 	size = sizeof(struct uv_hub_nmi_s);
10510d12ef0cSMike Travis 	for_each_present_cpu(cpu) {
1052abdf1df6Stravis@sgi.com 		int nid = cpu_to_node(cpu);
10530d12ef0cSMike Travis 		if (uv_hub_nmi_list[nid] == NULL) {
10540d12ef0cSMike Travis 			uv_hub_nmi_list[nid] = kzalloc_node(size,
10550d12ef0cSMike Travis 							    GFP_KERNEL, nid);
10560d12ef0cSMike Travis 			BUG_ON(!uv_hub_nmi_list[nid]);
10570d12ef0cSMike Travis 			raw_spin_lock_init(&(uv_hub_nmi_list[nid]->nmi_lock));
10580d12ef0cSMike Travis 			atomic_set(&uv_hub_nmi_list[nid]->cpu_owner, -1);
1059abdf1df6Stravis@sgi.com 			uv_hub_nmi_list[nid]->hub_present = hubbed;
1060abdf1df6Stravis@sgi.com 			uv_hub_nmi_list[nid]->pch_owner = (nid == 0);
10610d12ef0cSMike Travis 		}
10620d12ef0cSMike Travis 		uv_hub_nmi_per(cpu) = uv_hub_nmi_list[nid];
10630d12ef0cSMike Travis 	}
10648a1f4653SIngo Molnar 	BUG_ON(!alloc_cpumask_var(&uv_nmi_cpu_mask, GFP_KERNEL));
1065abdf1df6Stravis@sgi.com }
1066abdf1df6Stravis@sgi.com 
1067abdf1df6Stravis@sgi.com /* Setup for UV Hub systems */
uv_nmi_setup(void)1068abdf1df6Stravis@sgi.com void __init uv_nmi_setup(void)
1069abdf1df6Stravis@sgi.com {
1070abdf1df6Stravis@sgi.com 	uv_nmi_setup_mmrs();
1071abdf1df6Stravis@sgi.com 	uv_nmi_setup_common(true);
107274c93f9dSMike Travis 	uv_register_nmi_notifier();
1073abdf1df6Stravis@sgi.com 	pr_info("UV: Hub NMI enabled\n");
1074abdf1df6Stravis@sgi.com }
1075abdf1df6Stravis@sgi.com 
1076abdf1df6Stravis@sgi.com /* Setup for UV Hubless systems */
uv_nmi_setup_hubless(void)1077abdf1df6Stravis@sgi.com void __init uv_nmi_setup_hubless(void)
1078abdf1df6Stravis@sgi.com {
1079abdf1df6Stravis@sgi.com 	uv_nmi_setup_common(false);
1080abdf1df6Stravis@sgi.com 	pch_base = xlate_dev_mem_ptr(PCH_PCR_GPIO_1_BASE);
1081abdf1df6Stravis@sgi.com 	nmi_debug("UV: PCH base:%p from 0x%lx, GPP_D_0\n",
1082abdf1df6Stravis@sgi.com 		pch_base, PCH_PCR_GPIO_1_BASE);
108356e17ca2Stravis@sgi.com 	if (uv_pch_init_enable)
108456e17ca2Stravis@sgi.com 		uv_init_hubless_pch_d0();
1085abdf1df6Stravis@sgi.com 	uv_init_hubless_pch_io(GPI_NMI_ENA_GPP_D_0,
1086abdf1df6Stravis@sgi.com 				STS_GPP_D_0_MASK, STS_GPP_D_0_MASK);
1087abdf1df6Stravis@sgi.com 	uv_nmi_setup_hubless_intr();
1088abdf1df6Stravis@sgi.com 	/* Ensure NMI enabled in Processor Interface Reg: */
1089abdf1df6Stravis@sgi.com 	uv_reassert_nmi();
1090abdf1df6Stravis@sgi.com 	uv_register_nmi_notifier();
1091ae5f8ce3SMike Travis 	pr_info("UV: PCH NMI enabled\n");
10920d12ef0cSMike Travis }
1093