162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OPAL hypervisor Maintenance interrupt handling support in PowerNV. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2014 IBM Corporation 662306a36Sopenharmony_ci * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#undef DEBUG 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/opal.h> 1862306a36Sopenharmony_ci#include <asm/cputable.h> 1962306a36Sopenharmony_ci#include <asm/machdep.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "powernv.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int opal_hmi_handler_nb_init; 2462306a36Sopenharmony_cistruct OpalHmiEvtNode { 2562306a36Sopenharmony_ci struct list_head list; 2662306a36Sopenharmony_ci struct OpalHMIEvent hmi_evt; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct xstop_reason { 3062306a36Sopenharmony_ci uint32_t xstop_reason; 3162306a36Sopenharmony_ci const char *unit_failed; 3262306a36Sopenharmony_ci const char *description; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic LIST_HEAD(opal_hmi_evt_list); 3662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(opal_hmi_evt_lock); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void print_core_checkstop_reason(const char *level, 3962306a36Sopenharmony_ci struct OpalHMIEvent *hmi_evt) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci int i; 4262306a36Sopenharmony_ci static const struct xstop_reason xstop_reason[] = { 4362306a36Sopenharmony_ci { CORE_CHECKSTOP_IFU_REGFILE, "IFU", 4462306a36Sopenharmony_ci "RegFile core check stop" }, 4562306a36Sopenharmony_ci { CORE_CHECKSTOP_IFU_LOGIC, "IFU", "Logic core check stop" }, 4662306a36Sopenharmony_ci { CORE_CHECKSTOP_PC_DURING_RECOV, "PC", 4762306a36Sopenharmony_ci "Core checkstop during recovery" }, 4862306a36Sopenharmony_ci { CORE_CHECKSTOP_ISU_REGFILE, "ISU", 4962306a36Sopenharmony_ci "RegFile core check stop (mapper error)" }, 5062306a36Sopenharmony_ci { CORE_CHECKSTOP_ISU_LOGIC, "ISU", "Logic core check stop" }, 5162306a36Sopenharmony_ci { CORE_CHECKSTOP_FXU_LOGIC, "FXU", "Logic core check stop" }, 5262306a36Sopenharmony_ci { CORE_CHECKSTOP_VSU_LOGIC, "VSU", "Logic core check stop" }, 5362306a36Sopenharmony_ci { CORE_CHECKSTOP_PC_RECOV_IN_MAINT_MODE, "PC", 5462306a36Sopenharmony_ci "Recovery in maintenance mode" }, 5562306a36Sopenharmony_ci { CORE_CHECKSTOP_LSU_REGFILE, "LSU", 5662306a36Sopenharmony_ci "RegFile core check stop" }, 5762306a36Sopenharmony_ci { CORE_CHECKSTOP_PC_FWD_PROGRESS, "PC", 5862306a36Sopenharmony_ci "Forward Progress Error" }, 5962306a36Sopenharmony_ci { CORE_CHECKSTOP_LSU_LOGIC, "LSU", "Logic core check stop" }, 6062306a36Sopenharmony_ci { CORE_CHECKSTOP_PC_LOGIC, "PC", "Logic core check stop" }, 6162306a36Sopenharmony_ci { CORE_CHECKSTOP_PC_HYP_RESOURCE, "PC", 6262306a36Sopenharmony_ci "Hypervisor Resource error - core check stop" }, 6362306a36Sopenharmony_ci { CORE_CHECKSTOP_PC_HANG_RECOV_FAILED, "PC", 6462306a36Sopenharmony_ci "Hang Recovery Failed (core check stop)" }, 6562306a36Sopenharmony_ci { CORE_CHECKSTOP_PC_AMBI_HANG_DETECTED, "PC", 6662306a36Sopenharmony_ci "Ambiguous Hang Detected (unknown source)" }, 6762306a36Sopenharmony_ci { CORE_CHECKSTOP_PC_DEBUG_TRIG_ERR_INJ, "PC", 6862306a36Sopenharmony_ci "Debug Trigger Error inject" }, 6962306a36Sopenharmony_ci { CORE_CHECKSTOP_PC_SPRD_HYP_ERR_INJ, "PC", 7062306a36Sopenharmony_ci "Hypervisor check stop via SPRC/SPRD" }, 7162306a36Sopenharmony_ci }; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* Validity check */ 7462306a36Sopenharmony_ci if (!hmi_evt->u.xstop_error.xstop_reason) { 7562306a36Sopenharmony_ci printk("%s Unknown Core check stop.\n", level); 7662306a36Sopenharmony_ci return; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci printk("%s CPU PIR: %08x\n", level, 8062306a36Sopenharmony_ci be32_to_cpu(hmi_evt->u.xstop_error.u.pir)); 8162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xstop_reason); i++) 8262306a36Sopenharmony_ci if (be32_to_cpu(hmi_evt->u.xstop_error.xstop_reason) & 8362306a36Sopenharmony_ci xstop_reason[i].xstop_reason) 8462306a36Sopenharmony_ci printk("%s [Unit: %-3s] %s\n", level, 8562306a36Sopenharmony_ci xstop_reason[i].unit_failed, 8662306a36Sopenharmony_ci xstop_reason[i].description); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void print_nx_checkstop_reason(const char *level, 9062306a36Sopenharmony_ci struct OpalHMIEvent *hmi_evt) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci int i; 9362306a36Sopenharmony_ci static const struct xstop_reason xstop_reason[] = { 9462306a36Sopenharmony_ci { NX_CHECKSTOP_SHM_INVAL_STATE_ERR, "DMA & Engine", 9562306a36Sopenharmony_ci "SHM invalid state error" }, 9662306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_INVAL_STATE_ERR_1, "DMA & Engine", 9762306a36Sopenharmony_ci "DMA invalid state error bit 15" }, 9862306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_INVAL_STATE_ERR_2, "DMA & Engine", 9962306a36Sopenharmony_ci "DMA invalid state error bit 16" }, 10062306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_CH0_INVAL_STATE_ERR, "DMA & Engine", 10162306a36Sopenharmony_ci "Channel 0 invalid state error" }, 10262306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_CH1_INVAL_STATE_ERR, "DMA & Engine", 10362306a36Sopenharmony_ci "Channel 1 invalid state error" }, 10462306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_CH2_INVAL_STATE_ERR, "DMA & Engine", 10562306a36Sopenharmony_ci "Channel 2 invalid state error" }, 10662306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_CH3_INVAL_STATE_ERR, "DMA & Engine", 10762306a36Sopenharmony_ci "Channel 3 invalid state error" }, 10862306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_CH4_INVAL_STATE_ERR, "DMA & Engine", 10962306a36Sopenharmony_ci "Channel 4 invalid state error" }, 11062306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_CH5_INVAL_STATE_ERR, "DMA & Engine", 11162306a36Sopenharmony_ci "Channel 5 invalid state error" }, 11262306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_CH6_INVAL_STATE_ERR, "DMA & Engine", 11362306a36Sopenharmony_ci "Channel 6 invalid state error" }, 11462306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_CH7_INVAL_STATE_ERR, "DMA & Engine", 11562306a36Sopenharmony_ci "Channel 7 invalid state error" }, 11662306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_CRB_UE, "DMA & Engine", 11762306a36Sopenharmony_ci "UE error on CRB(CSB address, CCB)" }, 11862306a36Sopenharmony_ci { NX_CHECKSTOP_DMA_CRB_SUE, "DMA & Engine", 11962306a36Sopenharmony_ci "SUE error on CRB(CSB address, CCB)" }, 12062306a36Sopenharmony_ci { NX_CHECKSTOP_PBI_ISN_UE, "PowerBus Interface", 12162306a36Sopenharmony_ci "CRB Kill ISN received while holding ISN with UE error" }, 12262306a36Sopenharmony_ci }; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Validity check */ 12562306a36Sopenharmony_ci if (!hmi_evt->u.xstop_error.xstop_reason) { 12662306a36Sopenharmony_ci printk("%s Unknown NX check stop.\n", level); 12762306a36Sopenharmony_ci return; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci printk("%s NX checkstop on CHIP ID: %x\n", level, 13162306a36Sopenharmony_ci be32_to_cpu(hmi_evt->u.xstop_error.u.chip_id)); 13262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xstop_reason); i++) 13362306a36Sopenharmony_ci if (be32_to_cpu(hmi_evt->u.xstop_error.xstop_reason) & 13462306a36Sopenharmony_ci xstop_reason[i].xstop_reason) 13562306a36Sopenharmony_ci printk("%s [Unit: %-3s] %s\n", level, 13662306a36Sopenharmony_ci xstop_reason[i].unit_failed, 13762306a36Sopenharmony_ci xstop_reason[i].description); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void print_npu_checkstop_reason(const char *level, 14162306a36Sopenharmony_ci struct OpalHMIEvent *hmi_evt) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci uint8_t reason, reason_count, i; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * We may not have a checkstop reason on some combination of 14762306a36Sopenharmony_ci * hardware and/or skiboot version 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci if (!hmi_evt->u.xstop_error.xstop_reason) { 15062306a36Sopenharmony_ci printk("%s NPU checkstop on chip %x\n", level, 15162306a36Sopenharmony_ci be32_to_cpu(hmi_evt->u.xstop_error.u.chip_id)); 15262306a36Sopenharmony_ci return; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * NPU2 has 3 FIRs. Reason encoded on a byte as: 15762306a36Sopenharmony_ci * 2 bits for the FIR number 15862306a36Sopenharmony_ci * 6 bits for the bit number 15962306a36Sopenharmony_ci * It may be possible to find several reasons. 16062306a36Sopenharmony_ci * 16162306a36Sopenharmony_ci * We don't display a specific message per FIR bit as there 16262306a36Sopenharmony_ci * are too many and most are meaningless without the workbook 16362306a36Sopenharmony_ci * and/or hw team help anyway. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci reason_count = sizeof(hmi_evt->u.xstop_error.xstop_reason) / 16662306a36Sopenharmony_ci sizeof(reason); 16762306a36Sopenharmony_ci for (i = 0; i < reason_count; i++) { 16862306a36Sopenharmony_ci reason = (hmi_evt->u.xstop_error.xstop_reason >> (8 * i)) & 0xFF; 16962306a36Sopenharmony_ci if (reason) 17062306a36Sopenharmony_ci printk("%s NPU checkstop on chip %x: FIR%d bit %d is set\n", 17162306a36Sopenharmony_ci level, 17262306a36Sopenharmony_ci be32_to_cpu(hmi_evt->u.xstop_error.u.chip_id), 17362306a36Sopenharmony_ci reason >> 6, reason & 0x3F); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void print_checkstop_reason(const char *level, 17862306a36Sopenharmony_ci struct OpalHMIEvent *hmi_evt) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci uint8_t type = hmi_evt->u.xstop_error.xstop_type; 18162306a36Sopenharmony_ci switch (type) { 18262306a36Sopenharmony_ci case CHECKSTOP_TYPE_CORE: 18362306a36Sopenharmony_ci print_core_checkstop_reason(level, hmi_evt); 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci case CHECKSTOP_TYPE_NX: 18662306a36Sopenharmony_ci print_nx_checkstop_reason(level, hmi_evt); 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci case CHECKSTOP_TYPE_NPU: 18962306a36Sopenharmony_ci print_npu_checkstop_reason(level, hmi_evt); 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci default: 19262306a36Sopenharmony_ci printk("%s Unknown Malfunction Alert of type %d\n", 19362306a36Sopenharmony_ci level, type); 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic void print_hmi_event_info(struct OpalHMIEvent *hmi_evt) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci const char *level, *sevstr, *error_info; 20162306a36Sopenharmony_ci static const char *hmi_error_types[] = { 20262306a36Sopenharmony_ci "Malfunction Alert", 20362306a36Sopenharmony_ci "Processor Recovery done", 20462306a36Sopenharmony_ci "Processor recovery occurred again", 20562306a36Sopenharmony_ci "Processor recovery occurred for masked error", 20662306a36Sopenharmony_ci "Timer facility experienced an error", 20762306a36Sopenharmony_ci "TFMR SPR is corrupted", 20862306a36Sopenharmony_ci "UPS (Uninterrupted Power System) Overflow indication", 20962306a36Sopenharmony_ci "An XSCOM operation failure", 21062306a36Sopenharmony_ci "An XSCOM operation completed", 21162306a36Sopenharmony_ci "SCOM has set a reserved FIR bit to cause recovery", 21262306a36Sopenharmony_ci "Debug trigger has set a reserved FIR bit to cause recovery", 21362306a36Sopenharmony_ci "A hypervisor resource error occurred", 21462306a36Sopenharmony_ci "CAPP recovery process is in progress", 21562306a36Sopenharmony_ci }; 21662306a36Sopenharmony_ci static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, 21762306a36Sopenharmony_ci DEFAULT_RATELIMIT_BURST); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Print things out */ 22062306a36Sopenharmony_ci if (hmi_evt->version < OpalHMIEvt_V1) { 22162306a36Sopenharmony_ci pr_err("HMI Interrupt, Unknown event version %d !\n", 22262306a36Sopenharmony_ci hmi_evt->version); 22362306a36Sopenharmony_ci return; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci switch (hmi_evt->severity) { 22662306a36Sopenharmony_ci case OpalHMI_SEV_NO_ERROR: 22762306a36Sopenharmony_ci level = KERN_INFO; 22862306a36Sopenharmony_ci sevstr = "Harmless"; 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci case OpalHMI_SEV_WARNING: 23162306a36Sopenharmony_ci level = KERN_WARNING; 23262306a36Sopenharmony_ci sevstr = ""; 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci case OpalHMI_SEV_ERROR_SYNC: 23562306a36Sopenharmony_ci level = KERN_ERR; 23662306a36Sopenharmony_ci sevstr = "Severe"; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case OpalHMI_SEV_FATAL: 23962306a36Sopenharmony_ci default: 24062306a36Sopenharmony_ci level = KERN_ERR; 24162306a36Sopenharmony_ci sevstr = "Fatal"; 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (hmi_evt->severity != OpalHMI_SEV_NO_ERROR || __ratelimit(&rs)) { 24662306a36Sopenharmony_ci printk("%s%s Hypervisor Maintenance interrupt [%s]\n", 24762306a36Sopenharmony_ci level, sevstr, 24862306a36Sopenharmony_ci hmi_evt->disposition == OpalHMI_DISPOSITION_RECOVERED ? 24962306a36Sopenharmony_ci "Recovered" : "Not recovered"); 25062306a36Sopenharmony_ci error_info = hmi_evt->type < ARRAY_SIZE(hmi_error_types) ? 25162306a36Sopenharmony_ci hmi_error_types[hmi_evt->type] 25262306a36Sopenharmony_ci : "Unknown"; 25362306a36Sopenharmony_ci printk("%s Error detail: %s\n", level, error_info); 25462306a36Sopenharmony_ci printk("%s HMER: %016llx\n", level, 25562306a36Sopenharmony_ci be64_to_cpu(hmi_evt->hmer)); 25662306a36Sopenharmony_ci if ((hmi_evt->type == OpalHMI_ERROR_TFAC) || 25762306a36Sopenharmony_ci (hmi_evt->type == OpalHMI_ERROR_TFMR_PARITY)) 25862306a36Sopenharmony_ci printk("%s TFMR: %016llx\n", level, 25962306a36Sopenharmony_ci be64_to_cpu(hmi_evt->tfmr)); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (hmi_evt->version < OpalHMIEvt_V2) 26362306a36Sopenharmony_ci return; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* OpalHMIEvt_V2 and above provides reason for malfunction alert. */ 26662306a36Sopenharmony_ci if (hmi_evt->type == OpalHMI_ERROR_MALFUNC_ALERT) 26762306a36Sopenharmony_ci print_checkstop_reason(level, hmi_evt); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void hmi_event_handler(struct work_struct *work) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci unsigned long flags; 27362306a36Sopenharmony_ci struct OpalHMIEvent *hmi_evt; 27462306a36Sopenharmony_ci struct OpalHmiEvtNode *msg_node; 27562306a36Sopenharmony_ci uint8_t disposition; 27662306a36Sopenharmony_ci struct opal_msg msg; 27762306a36Sopenharmony_ci int unrecoverable = 0; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci spin_lock_irqsave(&opal_hmi_evt_lock, flags); 28062306a36Sopenharmony_ci while (!list_empty(&opal_hmi_evt_list)) { 28162306a36Sopenharmony_ci msg_node = list_entry(opal_hmi_evt_list.next, 28262306a36Sopenharmony_ci struct OpalHmiEvtNode, list); 28362306a36Sopenharmony_ci list_del(&msg_node->list); 28462306a36Sopenharmony_ci spin_unlock_irqrestore(&opal_hmi_evt_lock, flags); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci hmi_evt = (struct OpalHMIEvent *) &msg_node->hmi_evt; 28762306a36Sopenharmony_ci print_hmi_event_info(hmi_evt); 28862306a36Sopenharmony_ci disposition = hmi_evt->disposition; 28962306a36Sopenharmony_ci kfree(msg_node); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * Check if HMI event has been recovered or not. If not 29362306a36Sopenharmony_ci * then kernel can't continue, we need to panic. 29462306a36Sopenharmony_ci * But before we do that, display all the HMI event 29562306a36Sopenharmony_ci * available on the list and set unrecoverable flag to 1. 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci if (disposition != OpalHMI_DISPOSITION_RECOVERED) 29862306a36Sopenharmony_ci unrecoverable = 1; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci spin_lock_irqsave(&opal_hmi_evt_lock, flags); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci spin_unlock_irqrestore(&opal_hmi_evt_lock, flags); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (unrecoverable) { 30562306a36Sopenharmony_ci /* Pull all HMI events from OPAL before we panic. */ 30662306a36Sopenharmony_ci while (opal_get_msg(__pa(&msg), sizeof(msg)) == OPAL_SUCCESS) { 30762306a36Sopenharmony_ci u32 type; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci type = be32_to_cpu(msg.msg_type); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* skip if not HMI event */ 31262306a36Sopenharmony_ci if (type != OPAL_MSG_HMI_EVT) 31362306a36Sopenharmony_ci continue; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* HMI event info starts from param[0] */ 31662306a36Sopenharmony_ci hmi_evt = (struct OpalHMIEvent *)&msg.params[0]; 31762306a36Sopenharmony_ci print_hmi_event_info(hmi_evt); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci pnv_platform_error_reboot(NULL, "Unrecoverable HMI exception"); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic DECLARE_WORK(hmi_event_work, hmi_event_handler); 32562306a36Sopenharmony_ci/* 32662306a36Sopenharmony_ci * opal_handle_hmi_event - notifier handler that queues up HMI events 32762306a36Sopenharmony_ci * to be preocessed later. 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_cistatic int opal_handle_hmi_event(struct notifier_block *nb, 33062306a36Sopenharmony_ci unsigned long msg_type, void *msg) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci unsigned long flags; 33362306a36Sopenharmony_ci struct OpalHMIEvent *hmi_evt; 33462306a36Sopenharmony_ci struct opal_msg *hmi_msg = msg; 33562306a36Sopenharmony_ci struct OpalHmiEvtNode *msg_node; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Sanity Checks */ 33862306a36Sopenharmony_ci if (msg_type != OPAL_MSG_HMI_EVT) 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* HMI event info starts from param[0] */ 34262306a36Sopenharmony_ci hmi_evt = (struct OpalHMIEvent *)&hmi_msg->params[0]; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Delay the logging of HMI events to workqueue. */ 34562306a36Sopenharmony_ci msg_node = kzalloc(sizeof(*msg_node), GFP_ATOMIC); 34662306a36Sopenharmony_ci if (!msg_node) { 34762306a36Sopenharmony_ci pr_err("HMI: out of memory, Opal message event not handled\n"); 34862306a36Sopenharmony_ci return -ENOMEM; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci memcpy(&msg_node->hmi_evt, hmi_evt, sizeof(*hmi_evt)); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci spin_lock_irqsave(&opal_hmi_evt_lock, flags); 35362306a36Sopenharmony_ci list_add(&msg_node->list, &opal_hmi_evt_list); 35462306a36Sopenharmony_ci spin_unlock_irqrestore(&opal_hmi_evt_lock, flags); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci schedule_work(&hmi_event_work); 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic struct notifier_block opal_hmi_handler_nb = { 36162306a36Sopenharmony_ci .notifier_call = opal_handle_hmi_event, 36262306a36Sopenharmony_ci .next = NULL, 36362306a36Sopenharmony_ci .priority = 0, 36462306a36Sopenharmony_ci}; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ciint __init opal_hmi_handler_init(void) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci int ret; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (!opal_hmi_handler_nb_init) { 37162306a36Sopenharmony_ci ret = opal_message_notifier_register( 37262306a36Sopenharmony_ci OPAL_MSG_HMI_EVT, &opal_hmi_handler_nb); 37362306a36Sopenharmony_ci if (ret) { 37462306a36Sopenharmony_ci pr_err("%s: Can't register OPAL event notifier (%d)\n", 37562306a36Sopenharmony_ci __func__, ret); 37662306a36Sopenharmony_ci return ret; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci opal_hmi_handler_nb_init = 1; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 382