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