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