18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * OPAL hypervisor Maintenance interrupt handling support in PowerNV.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2014 IBM Corporation
68c2ecf20Sopenharmony_ci * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#undef DEBUG
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/mm.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <asm/opal.h>
188c2ecf20Sopenharmony_ci#include <asm/cputable.h>
198c2ecf20Sopenharmony_ci#include <asm/machdep.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "powernv.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic int opal_hmi_handler_nb_init;
248c2ecf20Sopenharmony_cistruct OpalHmiEvtNode {
258c2ecf20Sopenharmony_ci	struct list_head list;
268c2ecf20Sopenharmony_ci	struct OpalHMIEvent hmi_evt;
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct xstop_reason {
308c2ecf20Sopenharmony_ci	uint32_t xstop_reason;
318c2ecf20Sopenharmony_ci	const char *unit_failed;
328c2ecf20Sopenharmony_ci	const char *description;
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic LIST_HEAD(opal_hmi_evt_list);
368c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(opal_hmi_evt_lock);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void print_core_checkstop_reason(const char *level,
398c2ecf20Sopenharmony_ci					struct OpalHMIEvent *hmi_evt)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	int i;
428c2ecf20Sopenharmony_ci	static const struct xstop_reason xstop_reason[] = {
438c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_IFU_REGFILE, "IFU",
448c2ecf20Sopenharmony_ci				"RegFile core check stop" },
458c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_IFU_LOGIC, "IFU", "Logic core check stop" },
468c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_PC_DURING_RECOV, "PC",
478c2ecf20Sopenharmony_ci				"Core checkstop during recovery" },
488c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_ISU_REGFILE, "ISU",
498c2ecf20Sopenharmony_ci				"RegFile core check stop (mapper error)" },
508c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_ISU_LOGIC, "ISU", "Logic core check stop" },
518c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_FXU_LOGIC, "FXU", "Logic core check stop" },
528c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_VSU_LOGIC, "VSU", "Logic core check stop" },
538c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_PC_RECOV_IN_MAINT_MODE, "PC",
548c2ecf20Sopenharmony_ci				"Recovery in maintenance mode" },
558c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_LSU_REGFILE, "LSU",
568c2ecf20Sopenharmony_ci				"RegFile core check stop" },
578c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_PC_FWD_PROGRESS, "PC",
588c2ecf20Sopenharmony_ci				"Forward Progress Error" },
598c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_LSU_LOGIC, "LSU", "Logic core check stop" },
608c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_PC_LOGIC, "PC", "Logic core check stop" },
618c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_PC_HYP_RESOURCE, "PC",
628c2ecf20Sopenharmony_ci				"Hypervisor Resource error - core check stop" },
638c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_PC_HANG_RECOV_FAILED, "PC",
648c2ecf20Sopenharmony_ci				"Hang Recovery Failed (core check stop)" },
658c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_PC_AMBI_HANG_DETECTED, "PC",
668c2ecf20Sopenharmony_ci				"Ambiguous Hang Detected (unknown source)" },
678c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_PC_DEBUG_TRIG_ERR_INJ, "PC",
688c2ecf20Sopenharmony_ci				"Debug Trigger Error inject" },
698c2ecf20Sopenharmony_ci		{ CORE_CHECKSTOP_PC_SPRD_HYP_ERR_INJ, "PC",
708c2ecf20Sopenharmony_ci				"Hypervisor check stop via SPRC/SPRD" },
718c2ecf20Sopenharmony_ci	};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Validity check */
748c2ecf20Sopenharmony_ci	if (!hmi_evt->u.xstop_error.xstop_reason) {
758c2ecf20Sopenharmony_ci		printk("%s	Unknown Core check stop.\n", level);
768c2ecf20Sopenharmony_ci		return;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	printk("%s	CPU PIR: %08x\n", level,
808c2ecf20Sopenharmony_ci			be32_to_cpu(hmi_evt->u.xstop_error.u.pir));
818c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(xstop_reason); i++)
828c2ecf20Sopenharmony_ci		if (be32_to_cpu(hmi_evt->u.xstop_error.xstop_reason) &
838c2ecf20Sopenharmony_ci					xstop_reason[i].xstop_reason)
848c2ecf20Sopenharmony_ci			printk("%s	[Unit: %-3s] %s\n", level,
858c2ecf20Sopenharmony_ci					xstop_reason[i].unit_failed,
868c2ecf20Sopenharmony_ci					xstop_reason[i].description);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void print_nx_checkstop_reason(const char *level,
908c2ecf20Sopenharmony_ci					struct OpalHMIEvent *hmi_evt)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	int i;
938c2ecf20Sopenharmony_ci	static const struct xstop_reason xstop_reason[] = {
948c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_SHM_INVAL_STATE_ERR, "DMA & Engine",
958c2ecf20Sopenharmony_ci					"SHM invalid state error" },
968c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_INVAL_STATE_ERR_1, "DMA & Engine",
978c2ecf20Sopenharmony_ci					"DMA invalid state error bit 15" },
988c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_INVAL_STATE_ERR_2, "DMA & Engine",
998c2ecf20Sopenharmony_ci					"DMA invalid state error bit 16" },
1008c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_CH0_INVAL_STATE_ERR, "DMA & Engine",
1018c2ecf20Sopenharmony_ci					"Channel 0 invalid state error" },
1028c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_CH1_INVAL_STATE_ERR, "DMA & Engine",
1038c2ecf20Sopenharmony_ci					"Channel 1 invalid state error" },
1048c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_CH2_INVAL_STATE_ERR, "DMA & Engine",
1058c2ecf20Sopenharmony_ci					"Channel 2 invalid state error" },
1068c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_CH3_INVAL_STATE_ERR, "DMA & Engine",
1078c2ecf20Sopenharmony_ci					"Channel 3 invalid state error" },
1088c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_CH4_INVAL_STATE_ERR, "DMA & Engine",
1098c2ecf20Sopenharmony_ci					"Channel 4 invalid state error" },
1108c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_CH5_INVAL_STATE_ERR, "DMA & Engine",
1118c2ecf20Sopenharmony_ci					"Channel 5 invalid state error" },
1128c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_CH6_INVAL_STATE_ERR, "DMA & Engine",
1138c2ecf20Sopenharmony_ci					"Channel 6 invalid state error" },
1148c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_CH7_INVAL_STATE_ERR, "DMA & Engine",
1158c2ecf20Sopenharmony_ci					"Channel 7 invalid state error" },
1168c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_CRB_UE, "DMA & Engine",
1178c2ecf20Sopenharmony_ci					"UE error on CRB(CSB address, CCB)" },
1188c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_DMA_CRB_SUE, "DMA & Engine",
1198c2ecf20Sopenharmony_ci					"SUE error on CRB(CSB address, CCB)" },
1208c2ecf20Sopenharmony_ci		{ NX_CHECKSTOP_PBI_ISN_UE, "PowerBus Interface",
1218c2ecf20Sopenharmony_ci		"CRB Kill ISN received while holding ISN with UE error" },
1228c2ecf20Sopenharmony_ci	};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* Validity check */
1258c2ecf20Sopenharmony_ci	if (!hmi_evt->u.xstop_error.xstop_reason) {
1268c2ecf20Sopenharmony_ci		printk("%s	Unknown NX check stop.\n", level);
1278c2ecf20Sopenharmony_ci		return;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	printk("%s	NX checkstop on CHIP ID: %x\n", level,
1318c2ecf20Sopenharmony_ci			be32_to_cpu(hmi_evt->u.xstop_error.u.chip_id));
1328c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(xstop_reason); i++)
1338c2ecf20Sopenharmony_ci		if (be32_to_cpu(hmi_evt->u.xstop_error.xstop_reason) &
1348c2ecf20Sopenharmony_ci					xstop_reason[i].xstop_reason)
1358c2ecf20Sopenharmony_ci			printk("%s	[Unit: %-3s] %s\n", level,
1368c2ecf20Sopenharmony_ci					xstop_reason[i].unit_failed,
1378c2ecf20Sopenharmony_ci					xstop_reason[i].description);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void print_npu_checkstop_reason(const char *level,
1418c2ecf20Sopenharmony_ci					struct OpalHMIEvent *hmi_evt)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	uint8_t reason, reason_count, i;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/*
1468c2ecf20Sopenharmony_ci	 * We may not have a checkstop reason on some combination of
1478c2ecf20Sopenharmony_ci	 * hardware and/or skiboot version
1488c2ecf20Sopenharmony_ci	 */
1498c2ecf20Sopenharmony_ci	if (!hmi_evt->u.xstop_error.xstop_reason) {
1508c2ecf20Sopenharmony_ci		printk("%s	NPU checkstop on chip %x\n", level,
1518c2ecf20Sopenharmony_ci			be32_to_cpu(hmi_evt->u.xstop_error.u.chip_id));
1528c2ecf20Sopenharmony_ci		return;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/*
1568c2ecf20Sopenharmony_ci	 * NPU2 has 3 FIRs. Reason encoded on a byte as:
1578c2ecf20Sopenharmony_ci	 *   2 bits for the FIR number
1588c2ecf20Sopenharmony_ci	 *   6 bits for the bit number
1598c2ecf20Sopenharmony_ci	 * It may be possible to find several reasons.
1608c2ecf20Sopenharmony_ci	 *
1618c2ecf20Sopenharmony_ci	 * We don't display a specific message per FIR bit as there
1628c2ecf20Sopenharmony_ci	 * are too many and most are meaningless without the workbook
1638c2ecf20Sopenharmony_ci	 * and/or hw team help anyway.
1648c2ecf20Sopenharmony_ci	 */
1658c2ecf20Sopenharmony_ci	reason_count = sizeof(hmi_evt->u.xstop_error.xstop_reason) /
1668c2ecf20Sopenharmony_ci		sizeof(reason);
1678c2ecf20Sopenharmony_ci	for (i = 0; i < reason_count; i++) {
1688c2ecf20Sopenharmony_ci		reason = (hmi_evt->u.xstop_error.xstop_reason >> (8 * i)) & 0xFF;
1698c2ecf20Sopenharmony_ci		if (reason)
1708c2ecf20Sopenharmony_ci			printk("%s	NPU checkstop on chip %x: FIR%d bit %d is set\n",
1718c2ecf20Sopenharmony_ci				level,
1728c2ecf20Sopenharmony_ci				be32_to_cpu(hmi_evt->u.xstop_error.u.chip_id),
1738c2ecf20Sopenharmony_ci				reason >> 6, reason & 0x3F);
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic void print_checkstop_reason(const char *level,
1788c2ecf20Sopenharmony_ci					struct OpalHMIEvent *hmi_evt)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	uint8_t type = hmi_evt->u.xstop_error.xstop_type;
1818c2ecf20Sopenharmony_ci	switch (type) {
1828c2ecf20Sopenharmony_ci	case CHECKSTOP_TYPE_CORE:
1838c2ecf20Sopenharmony_ci		print_core_checkstop_reason(level, hmi_evt);
1848c2ecf20Sopenharmony_ci		break;
1858c2ecf20Sopenharmony_ci	case CHECKSTOP_TYPE_NX:
1868c2ecf20Sopenharmony_ci		print_nx_checkstop_reason(level, hmi_evt);
1878c2ecf20Sopenharmony_ci		break;
1888c2ecf20Sopenharmony_ci	case CHECKSTOP_TYPE_NPU:
1898c2ecf20Sopenharmony_ci		print_npu_checkstop_reason(level, hmi_evt);
1908c2ecf20Sopenharmony_ci		break;
1918c2ecf20Sopenharmony_ci	default:
1928c2ecf20Sopenharmony_ci		printk("%s	Unknown Malfunction Alert of type %d\n",
1938c2ecf20Sopenharmony_ci		       level, type);
1948c2ecf20Sopenharmony_ci		break;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void print_hmi_event_info(struct OpalHMIEvent *hmi_evt)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	const char *level, *sevstr, *error_info;
2018c2ecf20Sopenharmony_ci	static const char *hmi_error_types[] = {
2028c2ecf20Sopenharmony_ci		"Malfunction Alert",
2038c2ecf20Sopenharmony_ci		"Processor Recovery done",
2048c2ecf20Sopenharmony_ci		"Processor recovery occurred again",
2058c2ecf20Sopenharmony_ci		"Processor recovery occurred for masked error",
2068c2ecf20Sopenharmony_ci		"Timer facility experienced an error",
2078c2ecf20Sopenharmony_ci		"TFMR SPR is corrupted",
2088c2ecf20Sopenharmony_ci		"UPS (Uninterrupted Power System) Overflow indication",
2098c2ecf20Sopenharmony_ci		"An XSCOM operation failure",
2108c2ecf20Sopenharmony_ci		"An XSCOM operation completed",
2118c2ecf20Sopenharmony_ci		"SCOM has set a reserved FIR bit to cause recovery",
2128c2ecf20Sopenharmony_ci		"Debug trigger has set a reserved FIR bit to cause recovery",
2138c2ecf20Sopenharmony_ci		"A hypervisor resource error occurred",
2148c2ecf20Sopenharmony_ci		"CAPP recovery process is in progress",
2158c2ecf20Sopenharmony_ci	};
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* Print things out */
2188c2ecf20Sopenharmony_ci	if (hmi_evt->version < OpalHMIEvt_V1) {
2198c2ecf20Sopenharmony_ci		pr_err("HMI Interrupt, Unknown event version %d !\n",
2208c2ecf20Sopenharmony_ci			hmi_evt->version);
2218c2ecf20Sopenharmony_ci		return;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci	switch (hmi_evt->severity) {
2248c2ecf20Sopenharmony_ci	case OpalHMI_SEV_NO_ERROR:
2258c2ecf20Sopenharmony_ci		level = KERN_INFO;
2268c2ecf20Sopenharmony_ci		sevstr = "Harmless";
2278c2ecf20Sopenharmony_ci		break;
2288c2ecf20Sopenharmony_ci	case OpalHMI_SEV_WARNING:
2298c2ecf20Sopenharmony_ci		level = KERN_WARNING;
2308c2ecf20Sopenharmony_ci		sevstr = "";
2318c2ecf20Sopenharmony_ci		break;
2328c2ecf20Sopenharmony_ci	case OpalHMI_SEV_ERROR_SYNC:
2338c2ecf20Sopenharmony_ci		level = KERN_ERR;
2348c2ecf20Sopenharmony_ci		sevstr = "Severe";
2358c2ecf20Sopenharmony_ci		break;
2368c2ecf20Sopenharmony_ci	case OpalHMI_SEV_FATAL:
2378c2ecf20Sopenharmony_ci	default:
2388c2ecf20Sopenharmony_ci		level = KERN_ERR;
2398c2ecf20Sopenharmony_ci		sevstr = "Fatal";
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	printk("%s%s Hypervisor Maintenance interrupt [%s]\n",
2448c2ecf20Sopenharmony_ci		level, sevstr,
2458c2ecf20Sopenharmony_ci		hmi_evt->disposition == OpalHMI_DISPOSITION_RECOVERED ?
2468c2ecf20Sopenharmony_ci		"Recovered" : "Not recovered");
2478c2ecf20Sopenharmony_ci	error_info = hmi_evt->type < ARRAY_SIZE(hmi_error_types) ?
2488c2ecf20Sopenharmony_ci			hmi_error_types[hmi_evt->type]
2498c2ecf20Sopenharmony_ci			: "Unknown";
2508c2ecf20Sopenharmony_ci	printk("%s Error detail: %s\n", level, error_info);
2518c2ecf20Sopenharmony_ci	printk("%s	HMER: %016llx\n", level, be64_to_cpu(hmi_evt->hmer));
2528c2ecf20Sopenharmony_ci	if ((hmi_evt->type == OpalHMI_ERROR_TFAC) ||
2538c2ecf20Sopenharmony_ci		(hmi_evt->type == OpalHMI_ERROR_TFMR_PARITY))
2548c2ecf20Sopenharmony_ci		printk("%s	TFMR: %016llx\n", level,
2558c2ecf20Sopenharmony_ci						be64_to_cpu(hmi_evt->tfmr));
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (hmi_evt->version < OpalHMIEvt_V2)
2588c2ecf20Sopenharmony_ci		return;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* OpalHMIEvt_V2 and above provides reason for malfunction alert. */
2618c2ecf20Sopenharmony_ci	if (hmi_evt->type == OpalHMI_ERROR_MALFUNC_ALERT)
2628c2ecf20Sopenharmony_ci		print_checkstop_reason(level, hmi_evt);
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic void hmi_event_handler(struct work_struct *work)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	unsigned long flags;
2688c2ecf20Sopenharmony_ci	struct OpalHMIEvent *hmi_evt;
2698c2ecf20Sopenharmony_ci	struct OpalHmiEvtNode *msg_node;
2708c2ecf20Sopenharmony_ci	uint8_t disposition;
2718c2ecf20Sopenharmony_ci	struct opal_msg msg;
2728c2ecf20Sopenharmony_ci	int unrecoverable = 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&opal_hmi_evt_lock, flags);
2758c2ecf20Sopenharmony_ci	while (!list_empty(&opal_hmi_evt_list)) {
2768c2ecf20Sopenharmony_ci		msg_node = list_entry(opal_hmi_evt_list.next,
2778c2ecf20Sopenharmony_ci					   struct OpalHmiEvtNode, list);
2788c2ecf20Sopenharmony_ci		list_del(&msg_node->list);
2798c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&opal_hmi_evt_lock, flags);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		hmi_evt = (struct OpalHMIEvent *) &msg_node->hmi_evt;
2828c2ecf20Sopenharmony_ci		print_hmi_event_info(hmi_evt);
2838c2ecf20Sopenharmony_ci		disposition = hmi_evt->disposition;
2848c2ecf20Sopenharmony_ci		kfree(msg_node);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		/*
2878c2ecf20Sopenharmony_ci		 * Check if HMI event has been recovered or not. If not
2888c2ecf20Sopenharmony_ci		 * then kernel can't continue, we need to panic.
2898c2ecf20Sopenharmony_ci		 * But before we do that, display all the HMI event
2908c2ecf20Sopenharmony_ci		 * available on the list and set unrecoverable flag to 1.
2918c2ecf20Sopenharmony_ci		 */
2928c2ecf20Sopenharmony_ci		if (disposition != OpalHMI_DISPOSITION_RECOVERED)
2938c2ecf20Sopenharmony_ci			unrecoverable = 1;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		spin_lock_irqsave(&opal_hmi_evt_lock, flags);
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&opal_hmi_evt_lock, flags);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (unrecoverable) {
3008c2ecf20Sopenharmony_ci		/* Pull all HMI events from OPAL before we panic. */
3018c2ecf20Sopenharmony_ci		while (opal_get_msg(__pa(&msg), sizeof(msg)) == OPAL_SUCCESS) {
3028c2ecf20Sopenharmony_ci			u32 type;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci			type = be32_to_cpu(msg.msg_type);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci			/* skip if not HMI event */
3078c2ecf20Sopenharmony_ci			if (type != OPAL_MSG_HMI_EVT)
3088c2ecf20Sopenharmony_ci				continue;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci			/* HMI event info starts from param[0] */
3118c2ecf20Sopenharmony_ci			hmi_evt = (struct OpalHMIEvent *)&msg.params[0];
3128c2ecf20Sopenharmony_ci			print_hmi_event_info(hmi_evt);
3138c2ecf20Sopenharmony_ci		}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		pnv_platform_error_reboot(NULL, "Unrecoverable HMI exception");
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic DECLARE_WORK(hmi_event_work, hmi_event_handler);
3208c2ecf20Sopenharmony_ci/*
3218c2ecf20Sopenharmony_ci * opal_handle_hmi_event - notifier handler that queues up HMI events
3228c2ecf20Sopenharmony_ci * to be preocessed later.
3238c2ecf20Sopenharmony_ci */
3248c2ecf20Sopenharmony_cistatic int opal_handle_hmi_event(struct notifier_block *nb,
3258c2ecf20Sopenharmony_ci			  unsigned long msg_type, void *msg)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	unsigned long flags;
3288c2ecf20Sopenharmony_ci	struct OpalHMIEvent *hmi_evt;
3298c2ecf20Sopenharmony_ci	struct opal_msg *hmi_msg = msg;
3308c2ecf20Sopenharmony_ci	struct OpalHmiEvtNode *msg_node;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* Sanity Checks */
3338c2ecf20Sopenharmony_ci	if (msg_type != OPAL_MSG_HMI_EVT)
3348c2ecf20Sopenharmony_ci		return 0;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* HMI event info starts from param[0] */
3378c2ecf20Sopenharmony_ci	hmi_evt = (struct OpalHMIEvent *)&hmi_msg->params[0];
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/* Delay the logging of HMI events to workqueue. */
3408c2ecf20Sopenharmony_ci	msg_node = kzalloc(sizeof(*msg_node), GFP_ATOMIC);
3418c2ecf20Sopenharmony_ci	if (!msg_node) {
3428c2ecf20Sopenharmony_ci		pr_err("HMI: out of memory, Opal message event not handled\n");
3438c2ecf20Sopenharmony_ci		return -ENOMEM;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci	memcpy(&msg_node->hmi_evt, hmi_evt, sizeof(*hmi_evt));
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	spin_lock_irqsave(&opal_hmi_evt_lock, flags);
3488c2ecf20Sopenharmony_ci	list_add(&msg_node->list, &opal_hmi_evt_list);
3498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&opal_hmi_evt_lock, flags);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	schedule_work(&hmi_event_work);
3528c2ecf20Sopenharmony_ci	return 0;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic struct notifier_block opal_hmi_handler_nb = {
3568c2ecf20Sopenharmony_ci	.notifier_call	= opal_handle_hmi_event,
3578c2ecf20Sopenharmony_ci	.next		= NULL,
3588c2ecf20Sopenharmony_ci	.priority	= 0,
3598c2ecf20Sopenharmony_ci};
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ciint __init opal_hmi_handler_init(void)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	int ret;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (!opal_hmi_handler_nb_init) {
3668c2ecf20Sopenharmony_ci		ret = opal_message_notifier_register(
3678c2ecf20Sopenharmony_ci				OPAL_MSG_HMI_EVT, &opal_hmi_handler_nb);
3688c2ecf20Sopenharmony_ci		if (ret) {
3698c2ecf20Sopenharmony_ci			pr_err("%s: Can't register OPAL event notifier (%d)\n",
3708c2ecf20Sopenharmony_ci			       __func__, ret);
3718c2ecf20Sopenharmony_ci			return ret;
3728c2ecf20Sopenharmony_ci		}
3738c2ecf20Sopenharmony_ci		opal_hmi_handler_nb_init = 1;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci	return 0;
3768c2ecf20Sopenharmony_ci}
377