18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SGI NMI support routines
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
68c2ecf20Sopenharmony_ci * Copyright (C) 2007-2017 Silicon Graphics, Inc. All rights reserved.
78c2ecf20Sopenharmony_ci * Copyright (c) Mike Travis
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/cpu.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/kdb.h>
138c2ecf20Sopenharmony_ci#include <linux/kexec.h>
148c2ecf20Sopenharmony_ci#include <linux/kgdb.h>
158c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
168c2ecf20Sopenharmony_ci#include <linux/nmi.h>
178c2ecf20Sopenharmony_ci#include <linux/sched.h>
188c2ecf20Sopenharmony_ci#include <linux/sched/debug.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <asm/apic.h>
238c2ecf20Sopenharmony_ci#include <asm/current.h>
248c2ecf20Sopenharmony_ci#include <asm/kdebug.h>
258c2ecf20Sopenharmony_ci#include <asm/local64.h>
268c2ecf20Sopenharmony_ci#include <asm/nmi.h>
278c2ecf20Sopenharmony_ci#include <asm/traps.h>
288c2ecf20Sopenharmony_ci#include <asm/uv/uv.h>
298c2ecf20Sopenharmony_ci#include <asm/uv/uv_hub.h>
308c2ecf20Sopenharmony_ci#include <asm/uv/uv_mmrs.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * UV handler for NMI
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * Handle system-wide NMI events generated by the global 'power nmi' command.
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * Basic operation is to field the NMI interrupt on each CPU and wait
388c2ecf20Sopenharmony_ci * until all CPU's have arrived into the nmi handler.  If some CPU's do not
398c2ecf20Sopenharmony_ci * make it into the handler, try and force them in with the IPI(NMI) signal.
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * We also have to lessen UV Hub MMR accesses as much as possible as this
428c2ecf20Sopenharmony_ci * disrupts the UV Hub's primary mission of directing NumaLink traffic and
438c2ecf20Sopenharmony_ci * can cause system problems to occur.
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * To do this we register our primary NMI notifier on the NMI_UNKNOWN
468c2ecf20Sopenharmony_ci * chain.  This reduces the number of false NMI calls when the perf
478c2ecf20Sopenharmony_ci * tools are running which generate an enormous number of NMIs per
488c2ecf20Sopenharmony_ci * second (~4M/s for 1024 CPU threads).  Our secondary NMI handler is
498c2ecf20Sopenharmony_ci * very short as it only checks that if it has been "pinged" with the
508c2ecf20Sopenharmony_ci * IPI(NMI) signal as mentioned above, and does not read the UV Hub's MMR.
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic struct uv_hub_nmi_s **uv_hub_nmi_list;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ciDEFINE_PER_CPU(struct uv_cpu_nmi_s, uv_cpu_nmi);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* Newer SMM NMI handler, not present in all systems */
598c2ecf20Sopenharmony_cistatic unsigned long uvh_nmi_mmrx;		/* UVH_EVENT_OCCURRED0/1 */
608c2ecf20Sopenharmony_cistatic unsigned long uvh_nmi_mmrx_clear;	/* UVH_EVENT_OCCURRED0/1_ALIAS */
618c2ecf20Sopenharmony_cistatic int uvh_nmi_mmrx_shift;			/* UVH_EVENT_OCCURRED0/1_EXTIO_INT0_SHFT */
628c2ecf20Sopenharmony_cistatic char *uvh_nmi_mmrx_type;			/* "EXTIO_INT0" */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* Non-zero indicates newer SMM NMI handler present */
658c2ecf20Sopenharmony_cistatic unsigned long uvh_nmi_mmrx_supported;	/* UVH_EXTIO_INT0_BROADCAST */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/* Indicates to BIOS that we want to use the newer SMM NMI handler */
688c2ecf20Sopenharmony_cistatic unsigned long uvh_nmi_mmrx_req;		/* UVH_BIOS_KERNEL_MMR_ALIAS_2 */
698c2ecf20Sopenharmony_cistatic int uvh_nmi_mmrx_req_shift;		/* 62 */
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* UV hubless values */
728c2ecf20Sopenharmony_ci#define NMI_CONTROL_PORT	0x70
738c2ecf20Sopenharmony_ci#define NMI_DUMMY_PORT		0x71
748c2ecf20Sopenharmony_ci#define PAD_OWN_GPP_D_0		0x2c
758c2ecf20Sopenharmony_ci#define GPI_NMI_STS_GPP_D_0	0x164
768c2ecf20Sopenharmony_ci#define GPI_NMI_ENA_GPP_D_0	0x174
778c2ecf20Sopenharmony_ci#define STS_GPP_D_0_MASK	0x1
788c2ecf20Sopenharmony_ci#define PAD_CFG_DW0_GPP_D_0	0x4c0
798c2ecf20Sopenharmony_ci#define GPIROUTNMI		(1ul << 17)
808c2ecf20Sopenharmony_ci#define PCH_PCR_GPIO_1_BASE	0xfdae0000ul
818c2ecf20Sopenharmony_ci#define PCH_PCR_GPIO_ADDRESS(offset) (int *)((u64)(pch_base) | (u64)(offset))
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic u64 *pch_base;
848c2ecf20Sopenharmony_cistatic unsigned long nmi_mmr;
858c2ecf20Sopenharmony_cistatic unsigned long nmi_mmr_clear;
868c2ecf20Sopenharmony_cistatic unsigned long nmi_mmr_pending;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic atomic_t	uv_in_nmi;
898c2ecf20Sopenharmony_cistatic atomic_t uv_nmi_cpu = ATOMIC_INIT(-1);
908c2ecf20Sopenharmony_cistatic atomic_t uv_nmi_cpus_in_nmi = ATOMIC_INIT(-1);
918c2ecf20Sopenharmony_cistatic atomic_t uv_nmi_slave_continue;
928c2ecf20Sopenharmony_cistatic cpumask_var_t uv_nmi_cpu_mask;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/* Values for uv_nmi_slave_continue */
958c2ecf20Sopenharmony_ci#define SLAVE_CLEAR	0
968c2ecf20Sopenharmony_ci#define SLAVE_CONTINUE	1
978c2ecf20Sopenharmony_ci#define SLAVE_EXIT	2
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/*
1008c2ecf20Sopenharmony_ci * Default is all stack dumps go to the console and buffer.
1018c2ecf20Sopenharmony_ci * Lower level to send to log buffer only.
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_cistatic int uv_nmi_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
1048c2ecf20Sopenharmony_cimodule_param_named(dump_loglevel, uv_nmi_loglevel, int, 0644);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/*
1078c2ecf20Sopenharmony_ci * The following values show statistics on how perf events are affecting
1088c2ecf20Sopenharmony_ci * this system.
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_cistatic int param_get_local64(char *buffer, const struct kernel_param *kp)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	return sprintf(buffer, "%lu\n", local64_read((local64_t *)kp->arg));
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int param_set_local64(const char *val, const struct kernel_param *kp)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	/* Clear on any write */
1188c2ecf20Sopenharmony_ci	local64_set((local64_t *)kp->arg, 0);
1198c2ecf20Sopenharmony_ci	return 0;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic const struct kernel_param_ops param_ops_local64 = {
1238c2ecf20Sopenharmony_ci	.get = param_get_local64,
1248c2ecf20Sopenharmony_ci	.set = param_set_local64,
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci#define param_check_local64(name, p) __param_check(name, p, local64_t)
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic local64_t uv_nmi_count;
1298c2ecf20Sopenharmony_cimodule_param_named(nmi_count, uv_nmi_count, local64, 0644);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic local64_t uv_nmi_misses;
1328c2ecf20Sopenharmony_cimodule_param_named(nmi_misses, uv_nmi_misses, local64, 0644);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic local64_t uv_nmi_ping_count;
1358c2ecf20Sopenharmony_cimodule_param_named(ping_count, uv_nmi_ping_count, local64, 0644);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic local64_t uv_nmi_ping_misses;
1388c2ecf20Sopenharmony_cimodule_param_named(ping_misses, uv_nmi_ping_misses, local64, 0644);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/*
1418c2ecf20Sopenharmony_ci * Following values allow tuning for large systems under heavy loading
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_cistatic int uv_nmi_initial_delay = 100;
1448c2ecf20Sopenharmony_cimodule_param_named(initial_delay, uv_nmi_initial_delay, int, 0644);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int uv_nmi_slave_delay = 100;
1478c2ecf20Sopenharmony_cimodule_param_named(slave_delay, uv_nmi_slave_delay, int, 0644);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic int uv_nmi_loop_delay = 100;
1508c2ecf20Sopenharmony_cimodule_param_named(loop_delay, uv_nmi_loop_delay, int, 0644);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic int uv_nmi_trigger_delay = 10000;
1538c2ecf20Sopenharmony_cimodule_param_named(trigger_delay, uv_nmi_trigger_delay, int, 0644);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int uv_nmi_wait_count = 100;
1568c2ecf20Sopenharmony_cimodule_param_named(wait_count, uv_nmi_wait_count, int, 0644);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int uv_nmi_retry_count = 500;
1598c2ecf20Sopenharmony_cimodule_param_named(retry_count, uv_nmi_retry_count, int, 0644);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic bool uv_pch_intr_enable = true;
1628c2ecf20Sopenharmony_cistatic bool uv_pch_intr_now_enabled;
1638c2ecf20Sopenharmony_cimodule_param_named(pch_intr_enable, uv_pch_intr_enable, bool, 0644);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic bool uv_pch_init_enable = true;
1668c2ecf20Sopenharmony_cimodule_param_named(pch_init_enable, uv_pch_init_enable, bool, 0644);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic int uv_nmi_debug;
1698c2ecf20Sopenharmony_cimodule_param_named(debug, uv_nmi_debug, int, 0644);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci#define nmi_debug(fmt, ...)				\
1728c2ecf20Sopenharmony_ci	do {						\
1738c2ecf20Sopenharmony_ci		if (uv_nmi_debug)			\
1748c2ecf20Sopenharmony_ci			pr_info(fmt, ##__VA_ARGS__);	\
1758c2ecf20Sopenharmony_ci	} while (0)
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/* Valid NMI Actions */
1788c2ecf20Sopenharmony_ci#define	ACTION_LEN	16
1798c2ecf20Sopenharmony_cistatic struct nmi_action {
1808c2ecf20Sopenharmony_ci	char	*action;
1818c2ecf20Sopenharmony_ci	char	*desc;
1828c2ecf20Sopenharmony_ci} valid_acts[] = {
1838c2ecf20Sopenharmony_ci	{	"kdump",	"do kernel crash dump"			},
1848c2ecf20Sopenharmony_ci	{	"dump",		"dump process stack for each cpu"	},
1858c2ecf20Sopenharmony_ci	{	"ips",		"dump Inst Ptr info for each cpu"	},
1868c2ecf20Sopenharmony_ci	{	"kdb",		"enter KDB (needs kgdboc= assignment)"	},
1878c2ecf20Sopenharmony_ci	{	"kgdb",		"enter KGDB (needs gdb target remote)"	},
1888c2ecf20Sopenharmony_ci	{	"health",	"check if CPUs respond to NMI"		},
1898c2ecf20Sopenharmony_ci};
1908c2ecf20Sopenharmony_citypedef char action_t[ACTION_LEN];
1918c2ecf20Sopenharmony_cistatic action_t uv_nmi_action = { "dump" };
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int param_get_action(char *buffer, const struct kernel_param *kp)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	return sprintf(buffer, "%s\n", uv_nmi_action);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int param_set_action(const char *val, const struct kernel_param *kp)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	int i;
2018c2ecf20Sopenharmony_ci	int n = ARRAY_SIZE(valid_acts);
2028c2ecf20Sopenharmony_ci	char arg[ACTION_LEN], *p;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/* (remove possible '\n') */
2058c2ecf20Sopenharmony_ci	strncpy(arg, val, ACTION_LEN - 1);
2068c2ecf20Sopenharmony_ci	arg[ACTION_LEN - 1] = '\0';
2078c2ecf20Sopenharmony_ci	p = strchr(arg, '\n');
2088c2ecf20Sopenharmony_ci	if (p)
2098c2ecf20Sopenharmony_ci		*p = '\0';
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	for (i = 0; i < n; i++)
2128c2ecf20Sopenharmony_ci		if (!strcmp(arg, valid_acts[i].action))
2138c2ecf20Sopenharmony_ci			break;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (i < n) {
2168c2ecf20Sopenharmony_ci		strcpy(uv_nmi_action, arg);
2178c2ecf20Sopenharmony_ci		pr_info("UV: New NMI action:%s\n", uv_nmi_action);
2188c2ecf20Sopenharmony_ci		return 0;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	pr_err("UV: Invalid NMI action:%s, valid actions are:\n", arg);
2228c2ecf20Sopenharmony_ci	for (i = 0; i < n; i++)
2238c2ecf20Sopenharmony_ci		pr_err("UV: %-8s - %s\n",
2248c2ecf20Sopenharmony_ci			valid_acts[i].action, valid_acts[i].desc);
2258c2ecf20Sopenharmony_ci	return -EINVAL;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic const struct kernel_param_ops param_ops_action = {
2298c2ecf20Sopenharmony_ci	.get = param_get_action,
2308c2ecf20Sopenharmony_ci	.set = param_set_action,
2318c2ecf20Sopenharmony_ci};
2328c2ecf20Sopenharmony_ci#define param_check_action(name, p) __param_check(name, p, action_t)
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cimodule_param_named(action, uv_nmi_action, action, 0644);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic inline bool uv_nmi_action_is(const char *action)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	return (strncmp(uv_nmi_action, action, strlen(action)) == 0);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/* Setup which NMI support is present in system */
2428c2ecf20Sopenharmony_cistatic void uv_nmi_setup_mmrs(void)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	/* First determine arch specific MMRs to handshake with BIOS */
2458c2ecf20Sopenharmony_ci	if (UVH_EVENT_OCCURRED0_EXTIO_INT0_MASK) {
2468c2ecf20Sopenharmony_ci		uvh_nmi_mmrx = UVH_EVENT_OCCURRED0;
2478c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_clear = UVH_EVENT_OCCURRED0_ALIAS;
2488c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_shift = UVH_EVENT_OCCURRED0_EXTIO_INT0_SHFT;
2498c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_type = "OCRD0-EXTIO_INT0";
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_supported = UVH_EXTIO_INT0_BROADCAST;
2528c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_req = UVH_BIOS_KERNEL_MMR_ALIAS_2;
2538c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_req_shift = 62;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	} else if (UVH_EVENT_OCCURRED1_EXTIO_INT0_MASK) {
2568c2ecf20Sopenharmony_ci		uvh_nmi_mmrx = UVH_EVENT_OCCURRED1;
2578c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_clear = UVH_EVENT_OCCURRED1_ALIAS;
2588c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_shift = UVH_EVENT_OCCURRED1_EXTIO_INT0_SHFT;
2598c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_type = "OCRD1-EXTIO_INT0";
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_supported = UVH_EXTIO_INT0_BROADCAST;
2628c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_req = UVH_BIOS_KERNEL_MMR_ALIAS_2;
2638c2ecf20Sopenharmony_ci		uvh_nmi_mmrx_req_shift = 62;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	} else {
2668c2ecf20Sopenharmony_ci		pr_err("UV:%s:cannot find EVENT_OCCURRED*_EXTIO_INT0\n",
2678c2ecf20Sopenharmony_ci			__func__);
2688c2ecf20Sopenharmony_ci		return;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/* Then find out if new NMI is supported */
2728c2ecf20Sopenharmony_ci	if (likely(uv_read_local_mmr(uvh_nmi_mmrx_supported))) {
2738c2ecf20Sopenharmony_ci		uv_write_local_mmr(uvh_nmi_mmrx_req,
2748c2ecf20Sopenharmony_ci					1UL << uvh_nmi_mmrx_req_shift);
2758c2ecf20Sopenharmony_ci		nmi_mmr = uvh_nmi_mmrx;
2768c2ecf20Sopenharmony_ci		nmi_mmr_clear = uvh_nmi_mmrx_clear;
2778c2ecf20Sopenharmony_ci		nmi_mmr_pending = 1UL << uvh_nmi_mmrx_shift;
2788c2ecf20Sopenharmony_ci		pr_info("UV: SMI NMI support: %s\n", uvh_nmi_mmrx_type);
2798c2ecf20Sopenharmony_ci	} else {
2808c2ecf20Sopenharmony_ci		nmi_mmr = UVH_NMI_MMR;
2818c2ecf20Sopenharmony_ci		nmi_mmr_clear = UVH_NMI_MMR_CLEAR;
2828c2ecf20Sopenharmony_ci		nmi_mmr_pending = 1UL << UVH_NMI_MMR_SHIFT;
2838c2ecf20Sopenharmony_ci		pr_info("UV: SMI NMI support: %s\n", UVH_NMI_MMR_TYPE);
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/* Read NMI MMR and check if NMI flag was set by BMC. */
2888c2ecf20Sopenharmony_cistatic inline int uv_nmi_test_mmr(struct uv_hub_nmi_s *hub_nmi)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	hub_nmi->nmi_value = uv_read_local_mmr(nmi_mmr);
2918c2ecf20Sopenharmony_ci	atomic_inc(&hub_nmi->read_mmr_count);
2928c2ecf20Sopenharmony_ci	return !!(hub_nmi->nmi_value & nmi_mmr_pending);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic inline void uv_local_mmr_clear_nmi(void)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	uv_write_local_mmr(nmi_mmr_clear, nmi_mmr_pending);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci/*
3018c2ecf20Sopenharmony_ci * UV hubless NMI handler functions
3028c2ecf20Sopenharmony_ci */
3038c2ecf20Sopenharmony_cistatic inline void uv_reassert_nmi(void)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	/* (from arch/x86/include/asm/mach_traps.h) */
3068c2ecf20Sopenharmony_ci	outb(0x8f, NMI_CONTROL_PORT);
3078c2ecf20Sopenharmony_ci	inb(NMI_DUMMY_PORT);		/* dummy read */
3088c2ecf20Sopenharmony_ci	outb(0x0f, NMI_CONTROL_PORT);
3098c2ecf20Sopenharmony_ci	inb(NMI_DUMMY_PORT);		/* dummy read */
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic void uv_init_hubless_pch_io(int offset, int mask, int data)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	int *addr = PCH_PCR_GPIO_ADDRESS(offset);
3158c2ecf20Sopenharmony_ci	int readd = readl(addr);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (mask) {			/* OR in new data */
3188c2ecf20Sopenharmony_ci		int writed = (readd & ~mask) | data;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		nmi_debug("UV:PCH: %p = %x & %x | %x (%x)\n",
3218c2ecf20Sopenharmony_ci			addr, readd, ~mask, data, writed);
3228c2ecf20Sopenharmony_ci		writel(writed, addr);
3238c2ecf20Sopenharmony_ci	} else if (readd & data) {	/* clear status bit */
3248c2ecf20Sopenharmony_ci		nmi_debug("UV:PCH: %p = %x\n", addr, data);
3258c2ecf20Sopenharmony_ci		writel(data, addr);
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	(void)readl(addr);		/* flush write data */
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic void uv_nmi_setup_hubless_intr(void)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	uv_pch_intr_now_enabled = uv_pch_intr_enable;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	uv_init_hubless_pch_io(
3368c2ecf20Sopenharmony_ci		PAD_CFG_DW0_GPP_D_0, GPIROUTNMI,
3378c2ecf20Sopenharmony_ci		uv_pch_intr_now_enabled ? GPIROUTNMI : 0);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	nmi_debug("UV:NMI: GPP_D_0 interrupt %s\n",
3408c2ecf20Sopenharmony_ci		uv_pch_intr_now_enabled ? "enabled" : "disabled");
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic struct init_nmi {
3448c2ecf20Sopenharmony_ci	unsigned int	offset;
3458c2ecf20Sopenharmony_ci	unsigned int	mask;
3468c2ecf20Sopenharmony_ci	unsigned int	data;
3478c2ecf20Sopenharmony_ci} init_nmi[] = {
3488c2ecf20Sopenharmony_ci	{	/* HOSTSW_OWN_GPP_D_0 */
3498c2ecf20Sopenharmony_ci	.offset = 0x84,
3508c2ecf20Sopenharmony_ci	.mask = 0x1,
3518c2ecf20Sopenharmony_ci	.data = 0x0,	/* ACPI Mode */
3528c2ecf20Sopenharmony_ci	},
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci/* Clear status: */
3558c2ecf20Sopenharmony_ci	{	/* GPI_INT_STS_GPP_D_0 */
3568c2ecf20Sopenharmony_ci	.offset = 0x104,
3578c2ecf20Sopenharmony_ci	.mask = 0x0,
3588c2ecf20Sopenharmony_ci	.data = 0x1,	/* Clear Status */
3598c2ecf20Sopenharmony_ci	},
3608c2ecf20Sopenharmony_ci	{	/* GPI_GPE_STS_GPP_D_0 */
3618c2ecf20Sopenharmony_ci	.offset = 0x124,
3628c2ecf20Sopenharmony_ci	.mask = 0x0,
3638c2ecf20Sopenharmony_ci	.data = 0x1,	/* Clear Status */
3648c2ecf20Sopenharmony_ci	},
3658c2ecf20Sopenharmony_ci	{	/* GPI_SMI_STS_GPP_D_0 */
3668c2ecf20Sopenharmony_ci	.offset = 0x144,
3678c2ecf20Sopenharmony_ci	.mask = 0x0,
3688c2ecf20Sopenharmony_ci	.data = 0x1,	/* Clear Status */
3698c2ecf20Sopenharmony_ci	},
3708c2ecf20Sopenharmony_ci	{	/* GPI_NMI_STS_GPP_D_0 */
3718c2ecf20Sopenharmony_ci	.offset = 0x164,
3728c2ecf20Sopenharmony_ci	.mask = 0x0,
3738c2ecf20Sopenharmony_ci	.data = 0x1,	/* Clear Status */
3748c2ecf20Sopenharmony_ci	},
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci/* Disable interrupts: */
3778c2ecf20Sopenharmony_ci	{	/* GPI_INT_EN_GPP_D_0 */
3788c2ecf20Sopenharmony_ci	.offset = 0x114,
3798c2ecf20Sopenharmony_ci	.mask = 0x1,
3808c2ecf20Sopenharmony_ci	.data = 0x0,	/* Disable interrupt generation */
3818c2ecf20Sopenharmony_ci	},
3828c2ecf20Sopenharmony_ci	{	/* GPI_GPE_EN_GPP_D_0 */
3838c2ecf20Sopenharmony_ci	.offset = 0x134,
3848c2ecf20Sopenharmony_ci	.mask = 0x1,
3858c2ecf20Sopenharmony_ci	.data = 0x0,	/* Disable interrupt generation */
3868c2ecf20Sopenharmony_ci	},
3878c2ecf20Sopenharmony_ci	{	/* GPI_SMI_EN_GPP_D_0 */
3888c2ecf20Sopenharmony_ci	.offset = 0x154,
3898c2ecf20Sopenharmony_ci	.mask = 0x1,
3908c2ecf20Sopenharmony_ci	.data = 0x0,	/* Disable interrupt generation */
3918c2ecf20Sopenharmony_ci	},
3928c2ecf20Sopenharmony_ci	{	/* GPI_NMI_EN_GPP_D_0 */
3938c2ecf20Sopenharmony_ci	.offset = 0x174,
3948c2ecf20Sopenharmony_ci	.mask = 0x1,
3958c2ecf20Sopenharmony_ci	.data = 0x0,	/* Disable interrupt generation */
3968c2ecf20Sopenharmony_ci	},
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci/* Setup GPP_D_0 Pad Config: */
3998c2ecf20Sopenharmony_ci	{	/* PAD_CFG_DW0_GPP_D_0 */
4008c2ecf20Sopenharmony_ci	.offset = 0x4c0,
4018c2ecf20Sopenharmony_ci	.mask = 0xffffffff,
4028c2ecf20Sopenharmony_ci	.data = 0x82020100,
4038c2ecf20Sopenharmony_ci/*
4048c2ecf20Sopenharmony_ci *  31:30 Pad Reset Config (PADRSTCFG): = 2h  # PLTRST# (default)
4058c2ecf20Sopenharmony_ci *
4068c2ecf20Sopenharmony_ci *  29    RX Pad State Select (RXPADSTSEL): = 0 # Raw RX pad state directly
4078c2ecf20Sopenharmony_ci *                                                from RX buffer (default)
4088c2ecf20Sopenharmony_ci *
4098c2ecf20Sopenharmony_ci *  28    RX Raw Override to '1' (RXRAW1): = 0 # No Override
4108c2ecf20Sopenharmony_ci *
4118c2ecf20Sopenharmony_ci *  26:25 RX Level/Edge Configuration (RXEVCFG):
4128c2ecf20Sopenharmony_ci *      = 0h # Level
4138c2ecf20Sopenharmony_ci *      = 1h # Edge
4148c2ecf20Sopenharmony_ci *
4158c2ecf20Sopenharmony_ci *  23    RX Invert (RXINV): = 0 # No Inversion (signal active high)
4168c2ecf20Sopenharmony_ci *
4178c2ecf20Sopenharmony_ci *  20    GPIO Input Route IOxAPIC (GPIROUTIOXAPIC):
4188c2ecf20Sopenharmony_ci * = 0 # Routing does not cause peripheral IRQ...
4198c2ecf20Sopenharmony_ci *     # (we want an NMI not an IRQ)
4208c2ecf20Sopenharmony_ci *
4218c2ecf20Sopenharmony_ci *  19    GPIO Input Route SCI (GPIROUTSCI): = 0 # Routing does not cause SCI.
4228c2ecf20Sopenharmony_ci *  18    GPIO Input Route SMI (GPIROUTSMI): = 0 # Routing does not cause SMI.
4238c2ecf20Sopenharmony_ci *  17    GPIO Input Route NMI (GPIROUTNMI): = 1 # Routing can cause NMI.
4248c2ecf20Sopenharmony_ci *
4258c2ecf20Sopenharmony_ci *  11:10 Pad Mode (PMODE1/0): = 0h = GPIO control the Pad.
4268c2ecf20Sopenharmony_ci *   9    GPIO RX Disable (GPIORXDIS):
4278c2ecf20Sopenharmony_ci * = 0 # Enable the input buffer (active low enable)
4288c2ecf20Sopenharmony_ci *
4298c2ecf20Sopenharmony_ci *   8    GPIO TX Disable (GPIOTXDIS):
4308c2ecf20Sopenharmony_ci * = 1 # Disable the output buffer; i.e. Hi-Z
4318c2ecf20Sopenharmony_ci *
4328c2ecf20Sopenharmony_ci *   1 GPIO RX State (GPIORXSTATE): This is the current internal RX pad state..
4338c2ecf20Sopenharmony_ci *   0 GPIO TX State (GPIOTXSTATE):
4348c2ecf20Sopenharmony_ci * = 0 # (Leave at default)
4358c2ecf20Sopenharmony_ci */
4368c2ecf20Sopenharmony_ci	},
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci/* Pad Config DW1 */
4398c2ecf20Sopenharmony_ci	{	/* PAD_CFG_DW1_GPP_D_0 */
4408c2ecf20Sopenharmony_ci	.offset = 0x4c4,
4418c2ecf20Sopenharmony_ci	.mask = 0x3c00,
4428c2ecf20Sopenharmony_ci	.data = 0,	/* Termination = none (default) */
4438c2ecf20Sopenharmony_ci	},
4448c2ecf20Sopenharmony_ci};
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic void uv_init_hubless_pch_d0(void)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	int i, read;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	read = *PCH_PCR_GPIO_ADDRESS(PAD_OWN_GPP_D_0);
4518c2ecf20Sopenharmony_ci	if (read != 0) {
4528c2ecf20Sopenharmony_ci		pr_info("UV: Hubless NMI already configured\n");
4538c2ecf20Sopenharmony_ci		return;
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	nmi_debug("UV: Initializing UV Hubless NMI on PCH\n");
4578c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(init_nmi); i++) {
4588c2ecf20Sopenharmony_ci		uv_init_hubless_pch_io(init_nmi[i].offset,
4598c2ecf20Sopenharmony_ci					init_nmi[i].mask,
4608c2ecf20Sopenharmony_ci					init_nmi[i].data);
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic int uv_nmi_test_hubless(struct uv_hub_nmi_s *hub_nmi)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	int *pstat = PCH_PCR_GPIO_ADDRESS(GPI_NMI_STS_GPP_D_0);
4678c2ecf20Sopenharmony_ci	int status = *pstat;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	hub_nmi->nmi_value = status;
4708c2ecf20Sopenharmony_ci	atomic_inc(&hub_nmi->read_mmr_count);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (!(status & STS_GPP_D_0_MASK))	/* Not a UV external NMI */
4738c2ecf20Sopenharmony_ci		return 0;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	*pstat = STS_GPP_D_0_MASK;	/* Is a UV NMI: clear GPP_D_0 status */
4768c2ecf20Sopenharmony_ci	(void)*pstat;			/* Flush write */
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	return 1;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic int uv_test_nmi(struct uv_hub_nmi_s *hub_nmi)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	if (hub_nmi->hub_present)
4848c2ecf20Sopenharmony_ci		return uv_nmi_test_mmr(hub_nmi);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (hub_nmi->pch_owner)		/* Only PCH owner can check status */
4878c2ecf20Sopenharmony_ci		return uv_nmi_test_hubless(hub_nmi);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	return -1;
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci/*
4938c2ecf20Sopenharmony_ci * If first CPU in on this hub, set hub_nmi "in_nmi" and "owner" values and
4948c2ecf20Sopenharmony_ci * return true.  If first CPU in on the system, set global "in_nmi" flag.
4958c2ecf20Sopenharmony_ci */
4968c2ecf20Sopenharmony_cistatic int uv_set_in_nmi(int cpu, struct uv_hub_nmi_s *hub_nmi)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	int first = atomic_add_unless(&hub_nmi->in_nmi, 1, 1);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (first) {
5018c2ecf20Sopenharmony_ci		atomic_set(&hub_nmi->cpu_owner, cpu);
5028c2ecf20Sopenharmony_ci		if (atomic_add_unless(&uv_in_nmi, 1, 1))
5038c2ecf20Sopenharmony_ci			atomic_set(&uv_nmi_cpu, cpu);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci		atomic_inc(&hub_nmi->nmi_count);
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci	return first;
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci/* Check if this is a system NMI event */
5118c2ecf20Sopenharmony_cistatic int uv_check_nmi(struct uv_hub_nmi_s *hub_nmi)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
5148c2ecf20Sopenharmony_ci	int nmi = 0;
5158c2ecf20Sopenharmony_ci	int nmi_detected = 0;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	local64_inc(&uv_nmi_count);
5188c2ecf20Sopenharmony_ci	this_cpu_inc(uv_cpu_nmi.queries);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	do {
5218c2ecf20Sopenharmony_ci		nmi = atomic_read(&hub_nmi->in_nmi);
5228c2ecf20Sopenharmony_ci		if (nmi)
5238c2ecf20Sopenharmony_ci			break;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		if (raw_spin_trylock(&hub_nmi->nmi_lock)) {
5268c2ecf20Sopenharmony_ci			nmi_detected = uv_test_nmi(hub_nmi);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci			/* Check flag for UV external NMI */
5298c2ecf20Sopenharmony_ci			if (nmi_detected > 0) {
5308c2ecf20Sopenharmony_ci				uv_set_in_nmi(cpu, hub_nmi);
5318c2ecf20Sopenharmony_ci				nmi = 1;
5328c2ecf20Sopenharmony_ci				break;
5338c2ecf20Sopenharmony_ci			}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci			/* A non-PCH node in a hubless system waits for NMI */
5368c2ecf20Sopenharmony_ci			else if (nmi_detected < 0)
5378c2ecf20Sopenharmony_ci				goto slave_wait;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci			/* MMR/PCH NMI flag is clear */
5408c2ecf20Sopenharmony_ci			raw_spin_unlock(&hub_nmi->nmi_lock);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		} else {
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci			/* Wait a moment for the HUB NMI locker to set flag */
5458c2ecf20Sopenharmony_cislave_wait:		cpu_relax();
5468c2ecf20Sopenharmony_ci			udelay(uv_nmi_slave_delay);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci			/* Re-check hub in_nmi flag */
5498c2ecf20Sopenharmony_ci			nmi = atomic_read(&hub_nmi->in_nmi);
5508c2ecf20Sopenharmony_ci			if (nmi)
5518c2ecf20Sopenharmony_ci				break;
5528c2ecf20Sopenharmony_ci		}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		/*
5558c2ecf20Sopenharmony_ci		 * Check if this BMC missed setting the MMR NMI flag (or)
5568c2ecf20Sopenharmony_ci		 * UV hubless system where only PCH owner can check flag
5578c2ecf20Sopenharmony_ci		 */
5588c2ecf20Sopenharmony_ci		if (!nmi) {
5598c2ecf20Sopenharmony_ci			nmi = atomic_read(&uv_in_nmi);
5608c2ecf20Sopenharmony_ci			if (nmi)
5618c2ecf20Sopenharmony_ci				uv_set_in_nmi(cpu, hub_nmi);
5628c2ecf20Sopenharmony_ci		}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci		/* If we're holding the hub lock, release it now */
5658c2ecf20Sopenharmony_ci		if (nmi_detected < 0)
5668c2ecf20Sopenharmony_ci			raw_spin_unlock(&hub_nmi->nmi_lock);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	} while (0);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (!nmi)
5718c2ecf20Sopenharmony_ci		local64_inc(&uv_nmi_misses);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	return nmi;
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci/* Need to reset the NMI MMR register, but only once per hub. */
5778c2ecf20Sopenharmony_cistatic inline void uv_clear_nmi(int cpu)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	if (cpu == atomic_read(&hub_nmi->cpu_owner)) {
5828c2ecf20Sopenharmony_ci		atomic_set(&hub_nmi->cpu_owner, -1);
5838c2ecf20Sopenharmony_ci		atomic_set(&hub_nmi->in_nmi, 0);
5848c2ecf20Sopenharmony_ci		if (hub_nmi->hub_present)
5858c2ecf20Sopenharmony_ci			uv_local_mmr_clear_nmi();
5868c2ecf20Sopenharmony_ci		else
5878c2ecf20Sopenharmony_ci			uv_reassert_nmi();
5888c2ecf20Sopenharmony_ci		raw_spin_unlock(&hub_nmi->nmi_lock);
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci/* Ping non-responding CPU's attempting to force them into the NMI handler */
5938c2ecf20Sopenharmony_cistatic void uv_nmi_nr_cpus_ping(void)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	int cpu;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	for_each_cpu(cpu, uv_nmi_cpu_mask)
5988c2ecf20Sopenharmony_ci		uv_cpu_nmi_per(cpu).pinging = 1;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	apic->send_IPI_mask(uv_nmi_cpu_mask, APIC_DM_NMI);
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci/* Clean up flags for CPU's that ignored both NMI and ping */
6048c2ecf20Sopenharmony_cistatic void uv_nmi_cleanup_mask(void)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	int cpu;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	for_each_cpu(cpu, uv_nmi_cpu_mask) {
6098c2ecf20Sopenharmony_ci		uv_cpu_nmi_per(cpu).pinging =  0;
6108c2ecf20Sopenharmony_ci		uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_OUT;
6118c2ecf20Sopenharmony_ci		cpumask_clear_cpu(cpu, uv_nmi_cpu_mask);
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci/* Loop waiting as CPU's enter NMI handler */
6168c2ecf20Sopenharmony_cistatic int uv_nmi_wait_cpus(int first)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	int i, j, k, n = num_online_cpus();
6198c2ecf20Sopenharmony_ci	int last_k = 0, waiting = 0;
6208c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (first) {
6238c2ecf20Sopenharmony_ci		cpumask_copy(uv_nmi_cpu_mask, cpu_online_mask);
6248c2ecf20Sopenharmony_ci		k = 0;
6258c2ecf20Sopenharmony_ci	} else {
6268c2ecf20Sopenharmony_ci		k = n - cpumask_weight(uv_nmi_cpu_mask);
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	/* PCH NMI causes only one CPU to respond */
6308c2ecf20Sopenharmony_ci	if (first && uv_pch_intr_now_enabled) {
6318c2ecf20Sopenharmony_ci		cpumask_clear_cpu(cpu, uv_nmi_cpu_mask);
6328c2ecf20Sopenharmony_ci		return n - k - 1;
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	udelay(uv_nmi_initial_delay);
6368c2ecf20Sopenharmony_ci	for (i = 0; i < uv_nmi_retry_count; i++) {
6378c2ecf20Sopenharmony_ci		int loop_delay = uv_nmi_loop_delay;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		for_each_cpu(j, uv_nmi_cpu_mask) {
6408c2ecf20Sopenharmony_ci			if (uv_cpu_nmi_per(j).state) {
6418c2ecf20Sopenharmony_ci				cpumask_clear_cpu(j, uv_nmi_cpu_mask);
6428c2ecf20Sopenharmony_ci				if (++k >= n)
6438c2ecf20Sopenharmony_ci					break;
6448c2ecf20Sopenharmony_ci			}
6458c2ecf20Sopenharmony_ci		}
6468c2ecf20Sopenharmony_ci		if (k >= n) {		/* all in? */
6478c2ecf20Sopenharmony_ci			k = n;
6488c2ecf20Sopenharmony_ci			break;
6498c2ecf20Sopenharmony_ci		}
6508c2ecf20Sopenharmony_ci		if (last_k != k) {	/* abort if no new CPU's coming in */
6518c2ecf20Sopenharmony_ci			last_k = k;
6528c2ecf20Sopenharmony_ci			waiting = 0;
6538c2ecf20Sopenharmony_ci		} else if (++waiting > uv_nmi_wait_count)
6548c2ecf20Sopenharmony_ci			break;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		/* Extend delay if waiting only for CPU 0: */
6578c2ecf20Sopenharmony_ci		if (waiting && (n - k) == 1 &&
6588c2ecf20Sopenharmony_ci		    cpumask_test_cpu(0, uv_nmi_cpu_mask))
6598c2ecf20Sopenharmony_ci			loop_delay *= 100;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci		udelay(loop_delay);
6628c2ecf20Sopenharmony_ci	}
6638c2ecf20Sopenharmony_ci	atomic_set(&uv_nmi_cpus_in_nmi, k);
6648c2ecf20Sopenharmony_ci	return n - k;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci/* Wait until all slave CPU's have entered UV NMI handler */
6688c2ecf20Sopenharmony_cistatic void uv_nmi_wait(int master)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	/* Indicate this CPU is in: */
6718c2ecf20Sopenharmony_ci	this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_IN);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	/* If not the first CPU in (the master), then we are a slave CPU */
6748c2ecf20Sopenharmony_ci	if (!master)
6758c2ecf20Sopenharmony_ci		return;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	do {
6788c2ecf20Sopenharmony_ci		/* Wait for all other CPU's to gather here */
6798c2ecf20Sopenharmony_ci		if (!uv_nmi_wait_cpus(1))
6808c2ecf20Sopenharmony_ci			break;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci		/* If not all made it in, send IPI NMI to them */
6838c2ecf20Sopenharmony_ci		pr_alert("UV: Sending NMI IPI to %d CPUs: %*pbl\n",
6848c2ecf20Sopenharmony_ci			 cpumask_weight(uv_nmi_cpu_mask),
6858c2ecf20Sopenharmony_ci			 cpumask_pr_args(uv_nmi_cpu_mask));
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci		uv_nmi_nr_cpus_ping();
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci		/* If all CPU's are in, then done */
6908c2ecf20Sopenharmony_ci		if (!uv_nmi_wait_cpus(0))
6918c2ecf20Sopenharmony_ci			break;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci		pr_alert("UV: %d CPUs not in NMI loop: %*pbl\n",
6948c2ecf20Sopenharmony_ci			 cpumask_weight(uv_nmi_cpu_mask),
6958c2ecf20Sopenharmony_ci			 cpumask_pr_args(uv_nmi_cpu_mask));
6968c2ecf20Sopenharmony_ci	} while (0);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	pr_alert("UV: %d of %d CPUs in NMI\n",
6998c2ecf20Sopenharmony_ci		atomic_read(&uv_nmi_cpus_in_nmi), num_online_cpus());
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci/* Dump Instruction Pointer header */
7038c2ecf20Sopenharmony_cistatic void uv_nmi_dump_cpu_ip_hdr(void)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	pr_info("\nUV: %4s %6s %-32s %s   (Note: PID 0 not listed)\n",
7068c2ecf20Sopenharmony_ci		"CPU", "PID", "COMMAND", "IP");
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci/* Dump Instruction Pointer info */
7108c2ecf20Sopenharmony_cistatic void uv_nmi_dump_cpu_ip(int cpu, struct pt_regs *regs)
7118c2ecf20Sopenharmony_ci{
7128c2ecf20Sopenharmony_ci	pr_info("UV: %4d %6d %-32.32s %pS",
7138c2ecf20Sopenharmony_ci		cpu, current->pid, current->comm, (void *)regs->ip);
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci/*
7178c2ecf20Sopenharmony_ci * Dump this CPU's state.  If action was set to "kdump" and the crash_kexec
7188c2ecf20Sopenharmony_ci * failed, then we provide "dump" as an alternate action.  Action "dump" now
7198c2ecf20Sopenharmony_ci * also includes the show "ips" (instruction pointers) action whereas the
7208c2ecf20Sopenharmony_ci * action "ips" only displays instruction pointers for the non-idle CPU's.
7218c2ecf20Sopenharmony_ci * This is an abbreviated form of the "ps" command.
7228c2ecf20Sopenharmony_ci */
7238c2ecf20Sopenharmony_cistatic void uv_nmi_dump_state_cpu(int cpu, struct pt_regs *regs)
7248c2ecf20Sopenharmony_ci{
7258c2ecf20Sopenharmony_ci	const char *dots = " ................................. ";
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	if (cpu == 0)
7288c2ecf20Sopenharmony_ci		uv_nmi_dump_cpu_ip_hdr();
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	if (current->pid != 0 || !uv_nmi_action_is("ips"))
7318c2ecf20Sopenharmony_ci		uv_nmi_dump_cpu_ip(cpu, regs);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	if (uv_nmi_action_is("dump")) {
7348c2ecf20Sopenharmony_ci		pr_info("UV:%sNMI process trace for CPU %d\n", dots, cpu);
7358c2ecf20Sopenharmony_ci		show_regs(regs);
7368c2ecf20Sopenharmony_ci	}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_DUMP_DONE);
7398c2ecf20Sopenharmony_ci}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci/* Trigger a slave CPU to dump it's state */
7428c2ecf20Sopenharmony_cistatic void uv_nmi_trigger_dump(int cpu)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	int retry = uv_nmi_trigger_delay;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	if (uv_cpu_nmi_per(cpu).state != UV_NMI_STATE_IN)
7478c2ecf20Sopenharmony_ci		return;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_DUMP;
7508c2ecf20Sopenharmony_ci	do {
7518c2ecf20Sopenharmony_ci		cpu_relax();
7528c2ecf20Sopenharmony_ci		udelay(10);
7538c2ecf20Sopenharmony_ci		if (uv_cpu_nmi_per(cpu).state
7548c2ecf20Sopenharmony_ci				!= UV_NMI_STATE_DUMP)
7558c2ecf20Sopenharmony_ci			return;
7568c2ecf20Sopenharmony_ci	} while (--retry > 0);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	pr_crit("UV: CPU %d stuck in process dump function\n", cpu);
7598c2ecf20Sopenharmony_ci	uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_DUMP_DONE;
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci/* Wait until all CPU's ready to exit */
7638c2ecf20Sopenharmony_cistatic void uv_nmi_sync_exit(int master)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	atomic_dec(&uv_nmi_cpus_in_nmi);
7668c2ecf20Sopenharmony_ci	if (master) {
7678c2ecf20Sopenharmony_ci		while (atomic_read(&uv_nmi_cpus_in_nmi) > 0)
7688c2ecf20Sopenharmony_ci			cpu_relax();
7698c2ecf20Sopenharmony_ci		atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR);
7708c2ecf20Sopenharmony_ci	} else {
7718c2ecf20Sopenharmony_ci		while (atomic_read(&uv_nmi_slave_continue))
7728c2ecf20Sopenharmony_ci			cpu_relax();
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci/* Current "health" check is to check which CPU's are responsive */
7778c2ecf20Sopenharmony_cistatic void uv_nmi_action_health(int cpu, struct pt_regs *regs, int master)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	if (master) {
7808c2ecf20Sopenharmony_ci		int in = atomic_read(&uv_nmi_cpus_in_nmi);
7818c2ecf20Sopenharmony_ci		int out = num_online_cpus() - in;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci		pr_alert("UV: NMI CPU health check (non-responding:%d)\n", out);
7848c2ecf20Sopenharmony_ci		atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT);
7858c2ecf20Sopenharmony_ci	} else {
7868c2ecf20Sopenharmony_ci		while (!atomic_read(&uv_nmi_slave_continue))
7878c2ecf20Sopenharmony_ci			cpu_relax();
7888c2ecf20Sopenharmony_ci	}
7898c2ecf20Sopenharmony_ci	uv_nmi_sync_exit(master);
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci/* Walk through CPU list and dump state of each */
7938c2ecf20Sopenharmony_cistatic void uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master)
7948c2ecf20Sopenharmony_ci{
7958c2ecf20Sopenharmony_ci	if (master) {
7968c2ecf20Sopenharmony_ci		int tcpu;
7978c2ecf20Sopenharmony_ci		int ignored = 0;
7988c2ecf20Sopenharmony_ci		int saved_console_loglevel = console_loglevel;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci		pr_alert("UV: tracing %s for %d CPUs from CPU %d\n",
8018c2ecf20Sopenharmony_ci			uv_nmi_action_is("ips") ? "IPs" : "processes",
8028c2ecf20Sopenharmony_ci			atomic_read(&uv_nmi_cpus_in_nmi), cpu);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci		console_loglevel = uv_nmi_loglevel;
8058c2ecf20Sopenharmony_ci		atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT);
8068c2ecf20Sopenharmony_ci		for_each_online_cpu(tcpu) {
8078c2ecf20Sopenharmony_ci			if (cpumask_test_cpu(tcpu, uv_nmi_cpu_mask))
8088c2ecf20Sopenharmony_ci				ignored++;
8098c2ecf20Sopenharmony_ci			else if (tcpu == cpu)
8108c2ecf20Sopenharmony_ci				uv_nmi_dump_state_cpu(tcpu, regs);
8118c2ecf20Sopenharmony_ci			else
8128c2ecf20Sopenharmony_ci				uv_nmi_trigger_dump(tcpu);
8138c2ecf20Sopenharmony_ci		}
8148c2ecf20Sopenharmony_ci		if (ignored)
8158c2ecf20Sopenharmony_ci			pr_alert("UV: %d CPUs ignored NMI\n", ignored);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci		console_loglevel = saved_console_loglevel;
8188c2ecf20Sopenharmony_ci		pr_alert("UV: process trace complete\n");
8198c2ecf20Sopenharmony_ci	} else {
8208c2ecf20Sopenharmony_ci		while (!atomic_read(&uv_nmi_slave_continue))
8218c2ecf20Sopenharmony_ci			cpu_relax();
8228c2ecf20Sopenharmony_ci		while (this_cpu_read(uv_cpu_nmi.state) != UV_NMI_STATE_DUMP)
8238c2ecf20Sopenharmony_ci			cpu_relax();
8248c2ecf20Sopenharmony_ci		uv_nmi_dump_state_cpu(cpu, regs);
8258c2ecf20Sopenharmony_ci	}
8268c2ecf20Sopenharmony_ci	uv_nmi_sync_exit(master);
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cistatic void uv_nmi_touch_watchdogs(void)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	touch_softlockup_watchdog_sync();
8328c2ecf20Sopenharmony_ci	clocksource_touch_watchdog();
8338c2ecf20Sopenharmony_ci	rcu_cpu_stall_reset();
8348c2ecf20Sopenharmony_ci	touch_nmi_watchdog();
8358c2ecf20Sopenharmony_ci}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_cistatic atomic_t uv_nmi_kexec_failed;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci#if defined(CONFIG_KEXEC_CORE)
8408c2ecf20Sopenharmony_cistatic void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs)
8418c2ecf20Sopenharmony_ci{
8428c2ecf20Sopenharmony_ci	/* Call crash to dump system state */
8438c2ecf20Sopenharmony_ci	if (master) {
8448c2ecf20Sopenharmony_ci		pr_emerg("UV: NMI executing crash_kexec on CPU%d\n", cpu);
8458c2ecf20Sopenharmony_ci		crash_kexec(regs);
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci		pr_emerg("UV: crash_kexec unexpectedly returned, ");
8488c2ecf20Sopenharmony_ci		atomic_set(&uv_nmi_kexec_failed, 1);
8498c2ecf20Sopenharmony_ci		if (!kexec_crash_image) {
8508c2ecf20Sopenharmony_ci			pr_cont("crash kernel not loaded\n");
8518c2ecf20Sopenharmony_ci			return;
8528c2ecf20Sopenharmony_ci		}
8538c2ecf20Sopenharmony_ci		pr_cont("kexec busy, stalling cpus while waiting\n");
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	/* If crash exec fails the slaves should return, otherwise stall */
8578c2ecf20Sopenharmony_ci	while (atomic_read(&uv_nmi_kexec_failed) == 0)
8588c2ecf20Sopenharmony_ci		mdelay(10);
8598c2ecf20Sopenharmony_ci}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci#else /* !CONFIG_KEXEC_CORE */
8628c2ecf20Sopenharmony_cistatic inline void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	if (master)
8658c2ecf20Sopenharmony_ci		pr_err("UV: NMI kdump: KEXEC not supported in this kernel\n");
8668c2ecf20Sopenharmony_ci	atomic_set(&uv_nmi_kexec_failed, 1);
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci#endif /* !CONFIG_KEXEC_CORE */
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci#ifdef CONFIG_KGDB
8718c2ecf20Sopenharmony_ci#ifdef CONFIG_KGDB_KDB
8728c2ecf20Sopenharmony_cistatic inline int uv_nmi_kdb_reason(void)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	return KDB_REASON_SYSTEM_NMI;
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ci#else /* !CONFIG_KGDB_KDB */
8778c2ecf20Sopenharmony_cistatic inline int uv_nmi_kdb_reason(void)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	/* Ensure user is expecting to attach gdb remote */
8808c2ecf20Sopenharmony_ci	if (uv_nmi_action_is("kgdb"))
8818c2ecf20Sopenharmony_ci		return 0;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	pr_err("UV: NMI error: KDB is not enabled in this kernel\n");
8848c2ecf20Sopenharmony_ci	return -1;
8858c2ecf20Sopenharmony_ci}
8868c2ecf20Sopenharmony_ci#endif /* CONFIG_KGDB_KDB */
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci/*
8898c2ecf20Sopenharmony_ci * Call KGDB/KDB from NMI handler
8908c2ecf20Sopenharmony_ci *
8918c2ecf20Sopenharmony_ci * Note that if both KGDB and KDB are configured, then the action of 'kgdb' or
8928c2ecf20Sopenharmony_ci * 'kdb' has no affect on which is used.  See the KGDB documention for further
8938c2ecf20Sopenharmony_ci * information.
8948c2ecf20Sopenharmony_ci */
8958c2ecf20Sopenharmony_cistatic void uv_call_kgdb_kdb(int cpu, struct pt_regs *regs, int master)
8968c2ecf20Sopenharmony_ci{
8978c2ecf20Sopenharmony_ci	if (master) {
8988c2ecf20Sopenharmony_ci		int reason = uv_nmi_kdb_reason();
8998c2ecf20Sopenharmony_ci		int ret;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci		if (reason < 0)
9028c2ecf20Sopenharmony_ci			return;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		/* Call KGDB NMI handler as MASTER */
9058c2ecf20Sopenharmony_ci		ret = kgdb_nmicallin(cpu, X86_TRAP_NMI, regs, reason,
9068c2ecf20Sopenharmony_ci				&uv_nmi_slave_continue);
9078c2ecf20Sopenharmony_ci		if (ret) {
9088c2ecf20Sopenharmony_ci			pr_alert("KGDB returned error, is kgdboc set?\n");
9098c2ecf20Sopenharmony_ci			atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT);
9108c2ecf20Sopenharmony_ci		}
9118c2ecf20Sopenharmony_ci	} else {
9128c2ecf20Sopenharmony_ci		/* Wait for KGDB signal that it's ready for slaves to enter */
9138c2ecf20Sopenharmony_ci		int sig;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		do {
9168c2ecf20Sopenharmony_ci			cpu_relax();
9178c2ecf20Sopenharmony_ci			sig = atomic_read(&uv_nmi_slave_continue);
9188c2ecf20Sopenharmony_ci		} while (!sig);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci		/* Call KGDB as slave */
9218c2ecf20Sopenharmony_ci		if (sig == SLAVE_CONTINUE)
9228c2ecf20Sopenharmony_ci			kgdb_nmicallback(cpu, regs);
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci	uv_nmi_sync_exit(master);
9258c2ecf20Sopenharmony_ci}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci#else /* !CONFIG_KGDB */
9288c2ecf20Sopenharmony_cistatic inline void uv_call_kgdb_kdb(int cpu, struct pt_regs *regs, int master)
9298c2ecf20Sopenharmony_ci{
9308c2ecf20Sopenharmony_ci	pr_err("UV: NMI error: KGDB is not enabled in this kernel\n");
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci#endif /* !CONFIG_KGDB */
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci/*
9358c2ecf20Sopenharmony_ci * UV NMI handler
9368c2ecf20Sopenharmony_ci */
9378c2ecf20Sopenharmony_cistatic int uv_handle_nmi(unsigned int reason, struct pt_regs *regs)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi;
9408c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
9418c2ecf20Sopenharmony_ci	int master = 0;
9428c2ecf20Sopenharmony_ci	unsigned long flags;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	local_irq_save(flags);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	/* If not a UV System NMI, ignore */
9478c2ecf20Sopenharmony_ci	if (!this_cpu_read(uv_cpu_nmi.pinging) && !uv_check_nmi(hub_nmi)) {
9488c2ecf20Sopenharmony_ci		local_irq_restore(flags);
9498c2ecf20Sopenharmony_ci		return NMI_DONE;
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	/* Indicate we are the first CPU into the NMI handler */
9538c2ecf20Sopenharmony_ci	master = (atomic_read(&uv_nmi_cpu) == cpu);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	/* If NMI action is "kdump", then attempt to do it */
9568c2ecf20Sopenharmony_ci	if (uv_nmi_action_is("kdump")) {
9578c2ecf20Sopenharmony_ci		uv_nmi_kdump(cpu, master, regs);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci		/* Unexpected return, revert action to "dump" */
9608c2ecf20Sopenharmony_ci		if (master)
9618c2ecf20Sopenharmony_ci			strncpy(uv_nmi_action, "dump", strlen(uv_nmi_action));
9628c2ecf20Sopenharmony_ci	}
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	/* Pause as all CPU's enter the NMI handler */
9658c2ecf20Sopenharmony_ci	uv_nmi_wait(master);
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	/* Process actions other than "kdump": */
9688c2ecf20Sopenharmony_ci	if (uv_nmi_action_is("health")) {
9698c2ecf20Sopenharmony_ci		uv_nmi_action_health(cpu, regs, master);
9708c2ecf20Sopenharmony_ci	} else if (uv_nmi_action_is("ips") || uv_nmi_action_is("dump")) {
9718c2ecf20Sopenharmony_ci		uv_nmi_dump_state(cpu, regs, master);
9728c2ecf20Sopenharmony_ci	} else if (uv_nmi_action_is("kdb") || uv_nmi_action_is("kgdb")) {
9738c2ecf20Sopenharmony_ci		uv_call_kgdb_kdb(cpu, regs, master);
9748c2ecf20Sopenharmony_ci	} else {
9758c2ecf20Sopenharmony_ci		if (master)
9768c2ecf20Sopenharmony_ci			pr_alert("UV: unknown NMI action: %s\n", uv_nmi_action);
9778c2ecf20Sopenharmony_ci		uv_nmi_sync_exit(master);
9788c2ecf20Sopenharmony_ci	}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	/* Clear per_cpu "in_nmi" flag */
9818c2ecf20Sopenharmony_ci	this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_OUT);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/* Clear MMR NMI flag on each hub */
9848c2ecf20Sopenharmony_ci	uv_clear_nmi(cpu);
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	/* Clear global flags */
9878c2ecf20Sopenharmony_ci	if (master) {
9888c2ecf20Sopenharmony_ci		if (cpumask_weight(uv_nmi_cpu_mask))
9898c2ecf20Sopenharmony_ci			uv_nmi_cleanup_mask();
9908c2ecf20Sopenharmony_ci		atomic_set(&uv_nmi_cpus_in_nmi, -1);
9918c2ecf20Sopenharmony_ci		atomic_set(&uv_nmi_cpu, -1);
9928c2ecf20Sopenharmony_ci		atomic_set(&uv_in_nmi, 0);
9938c2ecf20Sopenharmony_ci		atomic_set(&uv_nmi_kexec_failed, 0);
9948c2ecf20Sopenharmony_ci		atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR);
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	uv_nmi_touch_watchdogs();
9988c2ecf20Sopenharmony_ci	local_irq_restore(flags);
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	return NMI_HANDLED;
10018c2ecf20Sopenharmony_ci}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci/*
10048c2ecf20Sopenharmony_ci * NMI handler for pulling in CPU's when perf events are grabbing our NMI
10058c2ecf20Sopenharmony_ci */
10068c2ecf20Sopenharmony_cistatic int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs)
10078c2ecf20Sopenharmony_ci{
10088c2ecf20Sopenharmony_ci	int ret;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	this_cpu_inc(uv_cpu_nmi.queries);
10118c2ecf20Sopenharmony_ci	if (!this_cpu_read(uv_cpu_nmi.pinging)) {
10128c2ecf20Sopenharmony_ci		local64_inc(&uv_nmi_ping_misses);
10138c2ecf20Sopenharmony_ci		return NMI_DONE;
10148c2ecf20Sopenharmony_ci	}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	this_cpu_inc(uv_cpu_nmi.pings);
10178c2ecf20Sopenharmony_ci	local64_inc(&uv_nmi_ping_count);
10188c2ecf20Sopenharmony_ci	ret = uv_handle_nmi(reason, regs);
10198c2ecf20Sopenharmony_ci	this_cpu_write(uv_cpu_nmi.pinging, 0);
10208c2ecf20Sopenharmony_ci	return ret;
10218c2ecf20Sopenharmony_ci}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_cistatic void uv_register_nmi_notifier(void)
10248c2ecf20Sopenharmony_ci{
10258c2ecf20Sopenharmony_ci	if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv"))
10268c2ecf20Sopenharmony_ci		pr_warn("UV: NMI handler failed to register\n");
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping"))
10298c2ecf20Sopenharmony_ci		pr_warn("UV: PING NMI handler failed to register\n");
10308c2ecf20Sopenharmony_ci}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_civoid uv_nmi_init(void)
10338c2ecf20Sopenharmony_ci{
10348c2ecf20Sopenharmony_ci	unsigned int value;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	/*
10378c2ecf20Sopenharmony_ci	 * Unmask NMI on all CPU's
10388c2ecf20Sopenharmony_ci	 */
10398c2ecf20Sopenharmony_ci	value = apic_read(APIC_LVT1) | APIC_DM_NMI;
10408c2ecf20Sopenharmony_ci	value &= ~APIC_LVT_MASKED;
10418c2ecf20Sopenharmony_ci	apic_write(APIC_LVT1, value);
10428c2ecf20Sopenharmony_ci}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci/* Setup HUB NMI info */
10458c2ecf20Sopenharmony_cistatic void __init uv_nmi_setup_common(bool hubbed)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	int size = sizeof(void *) * (1 << NODES_SHIFT);
10488c2ecf20Sopenharmony_ci	int cpu;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	uv_hub_nmi_list = kzalloc(size, GFP_KERNEL);
10518c2ecf20Sopenharmony_ci	nmi_debug("UV: NMI hub list @ 0x%p (%d)\n", uv_hub_nmi_list, size);
10528c2ecf20Sopenharmony_ci	BUG_ON(!uv_hub_nmi_list);
10538c2ecf20Sopenharmony_ci	size = sizeof(struct uv_hub_nmi_s);
10548c2ecf20Sopenharmony_ci	for_each_present_cpu(cpu) {
10558c2ecf20Sopenharmony_ci		int nid = cpu_to_node(cpu);
10568c2ecf20Sopenharmony_ci		if (uv_hub_nmi_list[nid] == NULL) {
10578c2ecf20Sopenharmony_ci			uv_hub_nmi_list[nid] = kzalloc_node(size,
10588c2ecf20Sopenharmony_ci							    GFP_KERNEL, nid);
10598c2ecf20Sopenharmony_ci			BUG_ON(!uv_hub_nmi_list[nid]);
10608c2ecf20Sopenharmony_ci			raw_spin_lock_init(&(uv_hub_nmi_list[nid]->nmi_lock));
10618c2ecf20Sopenharmony_ci			atomic_set(&uv_hub_nmi_list[nid]->cpu_owner, -1);
10628c2ecf20Sopenharmony_ci			uv_hub_nmi_list[nid]->hub_present = hubbed;
10638c2ecf20Sopenharmony_ci			uv_hub_nmi_list[nid]->pch_owner = (nid == 0);
10648c2ecf20Sopenharmony_ci		}
10658c2ecf20Sopenharmony_ci		uv_hub_nmi_per(cpu) = uv_hub_nmi_list[nid];
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci	BUG_ON(!alloc_cpumask_var(&uv_nmi_cpu_mask, GFP_KERNEL));
10688c2ecf20Sopenharmony_ci}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci/* Setup for UV Hub systems */
10718c2ecf20Sopenharmony_civoid __init uv_nmi_setup(void)
10728c2ecf20Sopenharmony_ci{
10738c2ecf20Sopenharmony_ci	uv_nmi_setup_mmrs();
10748c2ecf20Sopenharmony_ci	uv_nmi_setup_common(true);
10758c2ecf20Sopenharmony_ci	uv_register_nmi_notifier();
10768c2ecf20Sopenharmony_ci	pr_info("UV: Hub NMI enabled\n");
10778c2ecf20Sopenharmony_ci}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci/* Setup for UV Hubless systems */
10808c2ecf20Sopenharmony_civoid __init uv_nmi_setup_hubless(void)
10818c2ecf20Sopenharmony_ci{
10828c2ecf20Sopenharmony_ci	uv_nmi_setup_common(false);
10838c2ecf20Sopenharmony_ci	pch_base = xlate_dev_mem_ptr(PCH_PCR_GPIO_1_BASE);
10848c2ecf20Sopenharmony_ci	nmi_debug("UV: PCH base:%p from 0x%lx, GPP_D_0\n",
10858c2ecf20Sopenharmony_ci		pch_base, PCH_PCR_GPIO_1_BASE);
10868c2ecf20Sopenharmony_ci	if (uv_pch_init_enable)
10878c2ecf20Sopenharmony_ci		uv_init_hubless_pch_d0();
10888c2ecf20Sopenharmony_ci	uv_init_hubless_pch_io(GPI_NMI_ENA_GPP_D_0,
10898c2ecf20Sopenharmony_ci				STS_GPP_D_0_MASK, STS_GPP_D_0_MASK);
10908c2ecf20Sopenharmony_ci	uv_nmi_setup_hubless_intr();
10918c2ecf20Sopenharmony_ci	/* Ensure NMI enabled in Processor Interface Reg: */
10928c2ecf20Sopenharmony_ci	uv_reassert_nmi();
10938c2ecf20Sopenharmony_ci	uv_register_nmi_notifier();
10948c2ecf20Sopenharmony_ci	pr_info("UV: PCH NMI enabled\n");
10958c2ecf20Sopenharmony_ci}
1096