162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012-2014, 2018-2023 Intel Corporation 462306a36Sopenharmony_ci * Copyright (C) 2013-2014 Intel Mobile Communications GmbH 562306a36Sopenharmony_ci * Copyright (C) 2015-2017 Intel Deutschland GmbH 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/devcoredump.h> 862306a36Sopenharmony_ci#include "iwl-drv.h" 962306a36Sopenharmony_ci#include "runtime.h" 1062306a36Sopenharmony_ci#include "dbg.h" 1162306a36Sopenharmony_ci#include "debugfs.h" 1262306a36Sopenharmony_ci#include "iwl-io.h" 1362306a36Sopenharmony_ci#include "iwl-prph.h" 1462306a36Sopenharmony_ci#include "iwl-csr.h" 1562306a36Sopenharmony_ci#include "pnvm.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define FW_ASSERT_LMAC_FATAL 0x70 1862306a36Sopenharmony_ci#define FW_ASSERT_LMAC2_FATAL 0x72 1962306a36Sopenharmony_ci#define FW_ASSERT_UMAC_FATAL 0x71 2062306a36Sopenharmony_ci#define UMAC_RT_NMI_LMAC2_FATAL 0x72 2162306a36Sopenharmony_ci#define RT_NMI_INTERRUPT_OTHER_LMAC_FATAL 0x73 2262306a36Sopenharmony_ci#define FW_ASSERT_NMI_UNKNOWN 0x84 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Note: This structure is read from the device with IO accesses, 2662306a36Sopenharmony_ci * and the reading already does the endian conversion. As it is 2762306a36Sopenharmony_ci * read with u32-sized accesses, any members with a different size 2862306a36Sopenharmony_ci * need to be ordered correctly though! 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistruct iwl_error_event_table { 3162306a36Sopenharmony_ci u32 valid; /* (nonzero) valid, (0) log is empty */ 3262306a36Sopenharmony_ci u32 error_id; /* type of error */ 3362306a36Sopenharmony_ci u32 trm_hw_status0; /* TRM HW status */ 3462306a36Sopenharmony_ci u32 trm_hw_status1; /* TRM HW status */ 3562306a36Sopenharmony_ci u32 blink2; /* branch link */ 3662306a36Sopenharmony_ci u32 ilink1; /* interrupt link */ 3762306a36Sopenharmony_ci u32 ilink2; /* interrupt link */ 3862306a36Sopenharmony_ci u32 data1; /* error-specific data */ 3962306a36Sopenharmony_ci u32 data2; /* error-specific data */ 4062306a36Sopenharmony_ci u32 data3; /* error-specific data */ 4162306a36Sopenharmony_ci u32 bcon_time; /* beacon timer */ 4262306a36Sopenharmony_ci u32 tsf_low; /* network timestamp function timer */ 4362306a36Sopenharmony_ci u32 tsf_hi; /* network timestamp function timer */ 4462306a36Sopenharmony_ci u32 gp1; /* GP1 timer register */ 4562306a36Sopenharmony_ci u32 gp2; /* GP2 timer register */ 4662306a36Sopenharmony_ci u32 fw_rev_type; /* firmware revision type */ 4762306a36Sopenharmony_ci u32 major; /* uCode version major */ 4862306a36Sopenharmony_ci u32 minor; /* uCode version minor */ 4962306a36Sopenharmony_ci u32 hw_ver; /* HW Silicon version */ 5062306a36Sopenharmony_ci u32 brd_ver; /* HW board version */ 5162306a36Sopenharmony_ci u32 log_pc; /* log program counter */ 5262306a36Sopenharmony_ci u32 frame_ptr; /* frame pointer */ 5362306a36Sopenharmony_ci u32 stack_ptr; /* stack pointer */ 5462306a36Sopenharmony_ci u32 hcmd; /* last host command header */ 5562306a36Sopenharmony_ci u32 isr0; /* isr status register LMPM_NIC_ISR0: 5662306a36Sopenharmony_ci * rxtx_flag */ 5762306a36Sopenharmony_ci u32 isr1; /* isr status register LMPM_NIC_ISR1: 5862306a36Sopenharmony_ci * host_flag */ 5962306a36Sopenharmony_ci u32 isr2; /* isr status register LMPM_NIC_ISR2: 6062306a36Sopenharmony_ci * enc_flag */ 6162306a36Sopenharmony_ci u32 isr3; /* isr status register LMPM_NIC_ISR3: 6262306a36Sopenharmony_ci * time_flag */ 6362306a36Sopenharmony_ci u32 isr4; /* isr status register LMPM_NIC_ISR4: 6462306a36Sopenharmony_ci * wico interrupt */ 6562306a36Sopenharmony_ci u32 last_cmd_id; /* last HCMD id handled by the firmware */ 6662306a36Sopenharmony_ci u32 wait_event; /* wait event() caller address */ 6762306a36Sopenharmony_ci u32 l2p_control; /* L2pControlField */ 6862306a36Sopenharmony_ci u32 l2p_duration; /* L2pDurationField */ 6962306a36Sopenharmony_ci u32 l2p_mhvalid; /* L2pMhValidBits */ 7062306a36Sopenharmony_ci u32 l2p_addr_match; /* L2pAddrMatchStat */ 7162306a36Sopenharmony_ci u32 lmpm_pmg_sel; /* indicate which clocks are turned on 7262306a36Sopenharmony_ci * (LMPM_PMG_SEL) */ 7362306a36Sopenharmony_ci u32 u_timestamp; /* indicate when the date and time of the 7462306a36Sopenharmony_ci * compilation */ 7562306a36Sopenharmony_ci u32 flow_handler; /* FH read/write pointers, RX credit */ 7662306a36Sopenharmony_ci} __packed /* LOG_ERROR_TABLE_API_S_VER_3 */; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * UMAC error struct - relevant starting from family 8000 chip. 8062306a36Sopenharmony_ci * Note: This structure is read from the device with IO accesses, 8162306a36Sopenharmony_ci * and the reading already does the endian conversion. As it is 8262306a36Sopenharmony_ci * read with u32-sized accesses, any members with a different size 8362306a36Sopenharmony_ci * need to be ordered correctly though! 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistruct iwl_umac_error_event_table { 8662306a36Sopenharmony_ci u32 valid; /* (nonzero) valid, (0) log is empty */ 8762306a36Sopenharmony_ci u32 error_id; /* type of error */ 8862306a36Sopenharmony_ci u32 blink1; /* branch link */ 8962306a36Sopenharmony_ci u32 blink2; /* branch link */ 9062306a36Sopenharmony_ci u32 ilink1; /* interrupt link */ 9162306a36Sopenharmony_ci u32 ilink2; /* interrupt link */ 9262306a36Sopenharmony_ci u32 data1; /* error-specific data */ 9362306a36Sopenharmony_ci u32 data2; /* error-specific data */ 9462306a36Sopenharmony_ci u32 data3; /* error-specific data */ 9562306a36Sopenharmony_ci u32 umac_major; 9662306a36Sopenharmony_ci u32 umac_minor; 9762306a36Sopenharmony_ci u32 frame_pointer; /* core register 27*/ 9862306a36Sopenharmony_ci u32 stack_pointer; /* core register 28 */ 9962306a36Sopenharmony_ci u32 cmd_header; /* latest host cmd sent to UMAC */ 10062306a36Sopenharmony_ci u32 nic_isr_pref; /* ISR status register */ 10162306a36Sopenharmony_ci} __packed; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define ERROR_START_OFFSET (1 * sizeof(u32)) 10462306a36Sopenharmony_ci#define ERROR_ELEM_SIZE (7 * sizeof(u32)) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic bool iwl_fwrt_if_errorid_other_cpu(u32 err_id) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci err_id &= 0xFF; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if ((err_id >= FW_ASSERT_LMAC_FATAL && 11162306a36Sopenharmony_ci err_id <= RT_NMI_INTERRUPT_OTHER_LMAC_FATAL) || 11262306a36Sopenharmony_ci err_id == FW_ASSERT_NMI_UNKNOWN) 11362306a36Sopenharmony_ci return true; 11462306a36Sopenharmony_ci return false; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void iwl_fwrt_dump_umac_error_log(struct iwl_fw_runtime *fwrt) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct iwl_trans *trans = fwrt->trans; 12062306a36Sopenharmony_ci struct iwl_umac_error_event_table table = {}; 12162306a36Sopenharmony_ci u32 base = fwrt->trans->dbg.umac_error_event_table; 12262306a36Sopenharmony_ci char pnvm_name[MAX_PNVM_NAME]; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (!base && 12562306a36Sopenharmony_ci !(fwrt->trans->dbg.error_event_table_tlv_status & 12662306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_UMAC)) 12762306a36Sopenharmony_ci return; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (table.valid) 13262306a36Sopenharmony_ci fwrt->dump.umac_err_id = table.error_id; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.umac_err_id) && 13562306a36Sopenharmony_ci !fwrt->trans->dbg.dump_file_name_ext_valid) { 13662306a36Sopenharmony_ci fwrt->trans->dbg.dump_file_name_ext_valid = true; 13762306a36Sopenharmony_ci snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, 13862306a36Sopenharmony_ci "0x%x", fwrt->dump.umac_err_id); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { 14262306a36Sopenharmony_ci IWL_ERR(trans, "Start IWL Error Log Dump:\n"); 14362306a36Sopenharmony_ci IWL_ERR(trans, "Transport status: 0x%08lX, valid: %d\n", 14462306a36Sopenharmony_ci fwrt->trans->status, table.valid); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if ((table.error_id & ~FW_SYSASSERT_CPU_MASK) == 14862306a36Sopenharmony_ci FW_SYSASSERT_PNVM_MISSING) { 14962306a36Sopenharmony_ci iwl_pnvm_get_fs_name(trans, pnvm_name, sizeof(pnvm_name)); 15062306a36Sopenharmony_ci IWL_ERR(fwrt, "PNVM data is missing, please install %s\n", 15162306a36Sopenharmony_ci pnvm_name); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | %s\n", table.error_id, 15562306a36Sopenharmony_ci iwl_fw_lookup_assert_desc(table.error_id)); 15662306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | umac branchlink1\n", table.blink1); 15762306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | umac branchlink2\n", table.blink2); 15862306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | umac interruptlink1\n", table.ilink1); 15962306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | umac interruptlink2\n", table.ilink2); 16062306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | umac data1\n", table.data1); 16162306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | umac data2\n", table.data2); 16262306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | umac data3\n", table.data3); 16362306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | umac major\n", table.umac_major); 16462306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | umac minor\n", table.umac_minor); 16562306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | frame pointer\n", table.frame_pointer); 16662306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | stack pointer\n", table.stack_pointer); 16762306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | last host cmd\n", table.cmd_header); 16862306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | isr status reg\n", table.nic_isr_pref); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_num) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct iwl_trans *trans = fwrt->trans; 17462306a36Sopenharmony_ci struct iwl_error_event_table table = {}; 17562306a36Sopenharmony_ci u32 val, base = fwrt->trans->dbg.lmac_error_event_table[lmac_num]; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (fwrt->cur_fw_img == IWL_UCODE_INIT) { 17862306a36Sopenharmony_ci if (!base) 17962306a36Sopenharmony_ci base = fwrt->fw->init_errlog_ptr; 18062306a36Sopenharmony_ci } else { 18162306a36Sopenharmony_ci if (!base) 18262306a36Sopenharmony_ci base = fwrt->fw->inst_errlog_ptr; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (!base) { 18662306a36Sopenharmony_ci IWL_ERR(fwrt, 18762306a36Sopenharmony_ci "Not valid error log pointer 0x%08X for %s uCode\n", 18862306a36Sopenharmony_ci base, 18962306a36Sopenharmony_ci (fwrt->cur_fw_img == IWL_UCODE_INIT) 19062306a36Sopenharmony_ci ? "Init" : "RT"); 19162306a36Sopenharmony_ci return; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* check if there is a HW error */ 19562306a36Sopenharmony_ci val = iwl_trans_read_mem32(trans, base); 19662306a36Sopenharmony_ci if (iwl_trans_is_hw_error_value(val)) { 19762306a36Sopenharmony_ci int err; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci IWL_ERR(trans, "HW error, resetting before reading\n"); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* reset the device */ 20262306a36Sopenharmony_ci err = iwl_trans_sw_reset(trans, true); 20362306a36Sopenharmony_ci if (err) 20462306a36Sopenharmony_ci return; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci err = iwl_finish_nic_init(trans); 20762306a36Sopenharmony_ci if (err) 20862306a36Sopenharmony_ci return; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (table.valid) 21462306a36Sopenharmony_ci fwrt->dump.lmac_err_id[lmac_num] = table.error_id; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.lmac_err_id[lmac_num]) && 21762306a36Sopenharmony_ci !fwrt->trans->dbg.dump_file_name_ext_valid) { 21862306a36Sopenharmony_ci fwrt->trans->dbg.dump_file_name_ext_valid = true; 21962306a36Sopenharmony_ci snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, 22062306a36Sopenharmony_ci "0x%x", fwrt->dump.lmac_err_id[lmac_num]); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { 22462306a36Sopenharmony_ci IWL_ERR(trans, "Start IWL Error Log Dump:\n"); 22562306a36Sopenharmony_ci IWL_ERR(trans, "Transport status: 0x%08lX, valid: %d\n", 22662306a36Sopenharmony_ci fwrt->trans->status, table.valid); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Do not change this output - scripts rely on it */ 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci IWL_ERR(fwrt, "Loaded firmware version: %s\n", fwrt->fw->fw_version); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | %-28s\n", table.error_id, 23462306a36Sopenharmony_ci iwl_fw_lookup_assert_desc(table.error_id)); 23562306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | trm_hw_status0\n", table.trm_hw_status0); 23662306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | trm_hw_status1\n", table.trm_hw_status1); 23762306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | branchlink2\n", table.blink2); 23862306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | interruptlink1\n", table.ilink1); 23962306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | interruptlink2\n", table.ilink2); 24062306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | data1\n", table.data1); 24162306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | data2\n", table.data2); 24262306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | data3\n", table.data3); 24362306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | beacon time\n", table.bcon_time); 24462306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tsf low\n", table.tsf_low); 24562306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tsf hi\n", table.tsf_hi); 24662306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | time gp1\n", table.gp1); 24762306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | time gp2\n", table.gp2); 24862306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | uCode revision type\n", table.fw_rev_type); 24962306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | uCode version major\n", table.major); 25062306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | uCode version minor\n", table.minor); 25162306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | hw version\n", table.hw_ver); 25262306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | board version\n", table.brd_ver); 25362306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | hcmd\n", table.hcmd); 25462306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | isr0\n", table.isr0); 25562306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | isr1\n", table.isr1); 25662306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | isr2\n", table.isr2); 25762306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | isr3\n", table.isr3); 25862306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | isr4\n", table.isr4); 25962306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | last cmd Id\n", table.last_cmd_id); 26062306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | wait_event\n", table.wait_event); 26162306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | l2p_control\n", table.l2p_control); 26262306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | l2p_duration\n", table.l2p_duration); 26362306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); 26462306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); 26562306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); 26662306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | timestamp\n", table.u_timestamp); 26762306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | flow_handler\n", table.flow_handler); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * TCM error struct. 27262306a36Sopenharmony_ci * Note: This structure is read from the device with IO accesses, 27362306a36Sopenharmony_ci * and the reading already does the endian conversion. As it is 27462306a36Sopenharmony_ci * read with u32-sized accesses, any members with a different size 27562306a36Sopenharmony_ci * need to be ordered correctly though! 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_cistruct iwl_tcm_error_event_table { 27862306a36Sopenharmony_ci u32 valid; 27962306a36Sopenharmony_ci u32 error_id; 28062306a36Sopenharmony_ci u32 blink2; 28162306a36Sopenharmony_ci u32 ilink1; 28262306a36Sopenharmony_ci u32 ilink2; 28362306a36Sopenharmony_ci u32 data1, data2, data3; 28462306a36Sopenharmony_ci u32 logpc; 28562306a36Sopenharmony_ci u32 frame_pointer; 28662306a36Sopenharmony_ci u32 stack_pointer; 28762306a36Sopenharmony_ci u32 msgid; 28862306a36Sopenharmony_ci u32 isr; 28962306a36Sopenharmony_ci u32 hw_status[5]; 29062306a36Sopenharmony_ci u32 sw_status[1]; 29162306a36Sopenharmony_ci u32 reserved[4]; 29262306a36Sopenharmony_ci} __packed; /* TCM_LOG_ERROR_TABLE_API_S_VER_1 */ 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct iwl_trans *trans = fwrt->trans; 29762306a36Sopenharmony_ci struct iwl_tcm_error_event_table table = {}; 29862306a36Sopenharmony_ci u32 base = fwrt->trans->dbg.tcm_error_event_table[idx]; 29962306a36Sopenharmony_ci int i; 30062306a36Sopenharmony_ci u32 flag = idx ? IWL_ERROR_EVENT_TABLE_TCM2 : 30162306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_TCM1; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (!base || !(fwrt->trans->dbg.error_event_table_tlv_status & flag)) 30462306a36Sopenharmony_ci return; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (table.valid) 30962306a36Sopenharmony_ci fwrt->dump.tcm_err_id[idx] = table.error_id; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.tcm_err_id[idx]) && 31262306a36Sopenharmony_ci !fwrt->trans->dbg.dump_file_name_ext_valid) { 31362306a36Sopenharmony_ci fwrt->trans->dbg.dump_file_name_ext_valid = true; 31462306a36Sopenharmony_ci snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, 31562306a36Sopenharmony_ci "0x%x", fwrt->dump.tcm_err_id[idx]); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci IWL_ERR(fwrt, "TCM%d status:\n", idx + 1); 31962306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id); 32062306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm branchlink2\n", table.blink2); 32162306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm interruptlink1\n", table.ilink1); 32262306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm interruptlink2\n", table.ilink2); 32362306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm data1\n", table.data1); 32462306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm data2\n", table.data2); 32562306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm data3\n", table.data3); 32662306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm log PC\n", table.logpc); 32762306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm frame pointer\n", table.frame_pointer); 32862306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm stack pointer\n", table.stack_pointer); 32962306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm msg ID\n", table.msgid); 33062306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm ISR status\n", table.isr); 33162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(table.hw_status); i++) 33262306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm HW status[%d]\n", 33362306a36Sopenharmony_ci table.hw_status[i], i); 33462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(table.sw_status); i++) 33562306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | tcm SW status[%d]\n", 33662306a36Sopenharmony_ci table.sw_status[i], i); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* 34062306a36Sopenharmony_ci * RCM error struct. 34162306a36Sopenharmony_ci * Note: This structure is read from the device with IO accesses, 34262306a36Sopenharmony_ci * and the reading already does the endian conversion. As it is 34362306a36Sopenharmony_ci * read with u32-sized accesses, any members with a different size 34462306a36Sopenharmony_ci * need to be ordered correctly though! 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_cistruct iwl_rcm_error_event_table { 34762306a36Sopenharmony_ci u32 valid; 34862306a36Sopenharmony_ci u32 error_id; 34962306a36Sopenharmony_ci u32 blink2; 35062306a36Sopenharmony_ci u32 ilink1; 35162306a36Sopenharmony_ci u32 ilink2; 35262306a36Sopenharmony_ci u32 data1, data2, data3; 35362306a36Sopenharmony_ci u32 logpc; 35462306a36Sopenharmony_ci u32 frame_pointer; 35562306a36Sopenharmony_ci u32 stack_pointer; 35662306a36Sopenharmony_ci u32 msgid; 35762306a36Sopenharmony_ci u32 isr; 35862306a36Sopenharmony_ci u32 frame_hw_status; 35962306a36Sopenharmony_ci u32 mbx_lmac_to_rcm_req; 36062306a36Sopenharmony_ci u32 mbx_rcm_to_lmac_req; 36162306a36Sopenharmony_ci u32 mh_ctl; 36262306a36Sopenharmony_ci u32 mh_addr1_lo; 36362306a36Sopenharmony_ci u32 mh_info; 36462306a36Sopenharmony_ci u32 mh_err; 36562306a36Sopenharmony_ci u32 reserved[3]; 36662306a36Sopenharmony_ci} __packed; /* RCM_LOG_ERROR_TABLE_API_S_VER_1 */ 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void iwl_fwrt_dump_rcm_error_log(struct iwl_fw_runtime *fwrt, int idx) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct iwl_trans *trans = fwrt->trans; 37162306a36Sopenharmony_ci struct iwl_rcm_error_event_table table = {}; 37262306a36Sopenharmony_ci u32 base = fwrt->trans->dbg.rcm_error_event_table[idx]; 37362306a36Sopenharmony_ci u32 flag = idx ? IWL_ERROR_EVENT_TABLE_RCM2 : 37462306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_RCM1; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (!base || !(fwrt->trans->dbg.error_event_table_tlv_status & flag)) 37762306a36Sopenharmony_ci return; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (table.valid) 38262306a36Sopenharmony_ci fwrt->dump.rcm_err_id[idx] = table.error_id; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.rcm_err_id[idx]) && 38562306a36Sopenharmony_ci !fwrt->trans->dbg.dump_file_name_ext_valid) { 38662306a36Sopenharmony_ci fwrt->trans->dbg.dump_file_name_ext_valid = true; 38762306a36Sopenharmony_ci snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, 38862306a36Sopenharmony_ci "0x%x", fwrt->dump.rcm_err_id[idx]); 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci IWL_ERR(fwrt, "RCM%d status:\n", idx + 1); 39262306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id); 39362306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | rcm branchlink2\n", table.blink2); 39462306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | rcm interruptlink1\n", table.ilink1); 39562306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | rcm interruptlink2\n", table.ilink2); 39662306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | rcm data1\n", table.data1); 39762306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | rcm data2\n", table.data2); 39862306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | rcm data3\n", table.data3); 39962306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | rcm log PC\n", table.logpc); 40062306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | rcm frame pointer\n", table.frame_pointer); 40162306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | rcm stack pointer\n", table.stack_pointer); 40262306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | rcm msg ID\n", table.msgid); 40362306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | rcm ISR status\n", table.isr); 40462306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | frame HW status\n", table.frame_hw_status); 40562306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | LMAC-to-RCM request mbox\n", 40662306a36Sopenharmony_ci table.mbx_lmac_to_rcm_req); 40762306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | RCM-to-LMAC request mbox\n", 40862306a36Sopenharmony_ci table.mbx_rcm_to_lmac_req); 40962306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | MAC header control\n", table.mh_ctl); 41062306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | MAC header addr1 low\n", table.mh_addr1_lo); 41162306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | MAC header info\n", table.mh_info); 41262306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | MAC header error\n", table.mh_err); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void iwl_fwrt_dump_iml_error_log(struct iwl_fw_runtime *fwrt) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct iwl_trans *trans = fwrt->trans; 41862306a36Sopenharmony_ci u32 error, data1; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { 42162306a36Sopenharmony_ci error = UMAG_SB_CPU_2_STATUS; 42262306a36Sopenharmony_ci data1 = UMAG_SB_CPU_1_STATUS; 42362306a36Sopenharmony_ci } else if (fwrt->trans->trans_cfg->device_family >= 42462306a36Sopenharmony_ci IWL_DEVICE_FAMILY_8000) { 42562306a36Sopenharmony_ci error = SB_CPU_2_STATUS; 42662306a36Sopenharmony_ci data1 = SB_CPU_1_STATUS; 42762306a36Sopenharmony_ci } else { 42862306a36Sopenharmony_ci return; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci error = iwl_read_umac_prph(trans, error); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci IWL_ERR(trans, "IML/ROM dump:\n"); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (error & 0xFFFF0000) 43662306a36Sopenharmony_ci IWL_ERR(trans, "0x%04X | IML/ROM SYSASSERT\n", error >> 16); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | IML/ROM error/state\n", error); 43962306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | IML/ROM data1\n", 44062306a36Sopenharmony_ci iwl_read_umac_prph(trans, data1)); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) 44362306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | IML/ROM WFPM_AUTH_KEY_0\n", 44462306a36Sopenharmony_ci iwl_read_umac_prph(trans, SB_MODIFY_CFG_FLAG)); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci#define FSEQ_REG(x) { .addr = (x), .str = #x, } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic void iwl_fwrt_dump_fseq_regs(struct iwl_fw_runtime *fwrt) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct iwl_trans *trans = fwrt->trans; 45262306a36Sopenharmony_ci int i; 45362306a36Sopenharmony_ci struct { 45462306a36Sopenharmony_ci u32 addr; 45562306a36Sopenharmony_ci const char *str; 45662306a36Sopenharmony_ci } fseq_regs[] = { 45762306a36Sopenharmony_ci FSEQ_REG(FSEQ_ERROR_CODE), 45862306a36Sopenharmony_ci FSEQ_REG(FSEQ_TOP_INIT_VERSION), 45962306a36Sopenharmony_ci FSEQ_REG(FSEQ_CNVIO_INIT_VERSION), 46062306a36Sopenharmony_ci FSEQ_REG(FSEQ_OTP_VERSION), 46162306a36Sopenharmony_ci FSEQ_REG(FSEQ_TOP_CONTENT_VERSION), 46262306a36Sopenharmony_ci FSEQ_REG(FSEQ_ALIVE_TOKEN), 46362306a36Sopenharmony_ci FSEQ_REG(FSEQ_CNVI_ID), 46462306a36Sopenharmony_ci FSEQ_REG(FSEQ_CNVR_ID), 46562306a36Sopenharmony_ci FSEQ_REG(CNVI_AUX_MISC_CHIP), 46662306a36Sopenharmony_ci FSEQ_REG(CNVR_AUX_MISC_CHIP), 46762306a36Sopenharmony_ci FSEQ_REG(CNVR_SCU_SD_REGS_SD_REG_DIG_DCDC_VTRIM), 46862306a36Sopenharmony_ci FSEQ_REG(CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR), 46962306a36Sopenharmony_ci FSEQ_REG(FSEQ_PREV_CNVIO_INIT_VERSION), 47062306a36Sopenharmony_ci FSEQ_REG(FSEQ_WIFI_FSEQ_VERSION), 47162306a36Sopenharmony_ci FSEQ_REG(FSEQ_BT_FSEQ_VERSION), 47262306a36Sopenharmony_ci FSEQ_REG(FSEQ_CLASS_TP_VERSION), 47362306a36Sopenharmony_ci }; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (!iwl_trans_grab_nic_access(trans)) 47662306a36Sopenharmony_ci return; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci IWL_ERR(fwrt, "Fseq Registers:\n"); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fseq_regs); i++) 48162306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | %s\n", 48262306a36Sopenharmony_ci iwl_read_prph_no_grab(trans, fseq_regs[i].addr), 48362306a36Sopenharmony_ci fseq_regs[i].str); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci iwl_trans_release_nic_access(trans); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_civoid iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct iwl_pc_data *pc_data; 49162306a36Sopenharmony_ci u8 count; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (!test_bit(STATUS_DEVICE_ENABLED, &fwrt->trans->status)) { 49462306a36Sopenharmony_ci IWL_ERR(fwrt, 49562306a36Sopenharmony_ci "DEVICE_ENABLED bit is not set. Aborting dump.\n"); 49662306a36Sopenharmony_ci return; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci iwl_fwrt_dump_lmac_error_log(fwrt, 0); 50062306a36Sopenharmony_ci if (fwrt->trans->dbg.lmac_error_event_table[1]) 50162306a36Sopenharmony_ci iwl_fwrt_dump_lmac_error_log(fwrt, 1); 50262306a36Sopenharmony_ci iwl_fwrt_dump_umac_error_log(fwrt); 50362306a36Sopenharmony_ci iwl_fwrt_dump_tcm_error_log(fwrt, 0); 50462306a36Sopenharmony_ci iwl_fwrt_dump_rcm_error_log(fwrt, 0); 50562306a36Sopenharmony_ci if (fwrt->trans->dbg.tcm_error_event_table[1]) 50662306a36Sopenharmony_ci iwl_fwrt_dump_tcm_error_log(fwrt, 1); 50762306a36Sopenharmony_ci if (fwrt->trans->dbg.rcm_error_event_table[1]) 50862306a36Sopenharmony_ci iwl_fwrt_dump_rcm_error_log(fwrt, 1); 50962306a36Sopenharmony_ci iwl_fwrt_dump_iml_error_log(fwrt); 51062306a36Sopenharmony_ci iwl_fwrt_dump_fseq_regs(fwrt); 51162306a36Sopenharmony_ci if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { 51262306a36Sopenharmony_ci pc_data = fwrt->trans->dbg.pc_data; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (!iwl_trans_grab_nic_access(fwrt->trans)) 51562306a36Sopenharmony_ci return; 51662306a36Sopenharmony_ci for (count = 0; count < fwrt->trans->dbg.num_pc; 51762306a36Sopenharmony_ci count++, pc_data++) 51862306a36Sopenharmony_ci IWL_ERR(fwrt, "%s: 0x%x\n", 51962306a36Sopenharmony_ci pc_data->pc_name, 52062306a36Sopenharmony_ci iwl_read_prph_no_grab(fwrt->trans, 52162306a36Sopenharmony_ci pc_data->pc_address)); 52262306a36Sopenharmony_ci iwl_trans_release_nic_access(fwrt->trans); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { 52662306a36Sopenharmony_ci u32 scratch = iwl_read32(fwrt->trans, CSR_FUNC_SCRATCH); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci IWL_ERR(fwrt, "Function Scratch status:\n"); 52962306a36Sopenharmony_ci IWL_ERR(fwrt, "0x%08X | Func Scratch\n", scratch); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_fwrt_dump_error_logs); 533