Commit 4dbcb912 authored by Ryder Lee's avatar Ryder Lee Committed by Felix Fietkau

wifi: mt76: mt7915: enable coredump support

Host triggered and catastrophic event triggered firmware core dumping
for basic firmware issues triage, including state reporting, task/irq
info, function calltrace and MCU memory dump.
Signed-off-by: default avatarRyder Lee <ryder.lee@mediatek.com>
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
parent b662b71a
......@@ -2,6 +2,7 @@
config MT7915E
tristate "MediaTek MT7915E (PCIe) support"
select MT76_CONNAC_LIB
select WANT_DEV_COREDUMP
depends on MAC80211
depends on PCI
select RELAY
......
......@@ -7,3 +7,4 @@ mt7915e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
mt7915e-$(CONFIG_NL80211_TESTMODE) += testmode.o
mt7915e-$(CONFIG_MT7986_WMAC) += soc.o
mt7915e-$(CONFIG_DEV_COREDUMP) += coredump.o
This diff is collapsed.
/* SPDX-License-Identifier: ISC */
/* Copyright (C) 2022 MediaTek Inc. */
#ifndef _COREDUMP_H_
#define _COREDUMP_H_
#include "mt7915.h"
struct trace {
u32 id;
u32 timestamp;
};
struct mt7915_coredump {
char magic[16];
u32 len;
guid_t guid;
/* time-of-day stamp */
u64 tv_sec;
/* time-of-day stamp, nano-seconds */
u64 tv_nsec;
/* kernel version */
char kernel[64];
/* firmware version */
char fw_ver[ETHTOOL_FWVERS_LEN];
u32 device_id;
/* exception state */
char fw_state[12];
u32 last_msg_id;
u32 eint_info_idx;
u32 irq_info_idx;
u32 sched_info_idx;
/* schedule info */
char trace_sched[32];
struct {
struct trace t;
u32 pc;
} sched[60];
/* irq info */
char trace_irq[32];
struct trace irq[60];
/* task queue status */
char task_qid[32];
struct {
u32 read;
u32 write;
} taskq[2];
/* task stack info */
char task_info[32];
struct {
u32 start;
u32 end;
u32 size;
} taski[2];
/* firmware context */
char fw_context[24];
struct {
u32 idx;
u32 handler;
} context;
/* link registers calltrace */
u32 call_stack[16];
/* memory content */
u8 data[];
} __packed;
struct mt7915_coredump_mem {
u32 len;
u8 data[];
} __packed;
struct mt7915_mem_hdr {
u32 start;
u32 len;
u8 data[];
};
struct mt7915_mem_region {
u32 start;
size_t len;
const char *name;
};
#ifdef CONFIG_DEV_COREDUMP
const struct mt7915_mem_region *
mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num);
struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev);
int mt7915_coredump_submit(struct mt7915_dev *dev);
int mt7915_coredump_register(struct mt7915_dev *dev);
void mt7915_coredump_unregister(struct mt7915_dev *dev);
#else /* CONFIG_DEV_COREDUMP */
static inline const struct mt7915_mem_region *
mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num)
{
return NULL;
}
static inline int mt7915_coredump_submit(struct mt7915_dev *dev)
{
return 0;
}
static inline struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev)
{
return NULL;
}
static inline int mt7915_coredump_register(struct mt7915_dev *dev)
{
return 0;
}
static inline void mt7915_coredump_unregister(struct mt7915_dev *dev)
{
}
#endif /* CONFIG_DEV_COREDUMP */
#endif /* _COREDUMP_H_ */
......@@ -8,6 +8,7 @@
#include "mt7915.h"
#include "mac.h"
#include "mcu.h"
#include "coredump.h"
#include "eeprom.h"
static const struct ieee80211_iface_limit if_limits[] = {
......@@ -1100,6 +1101,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
init_waitqueue_head(&dev->reset_wait);
INIT_WORK(&dev->reset_work, mt7915_mac_reset_work);
INIT_WORK(&dev->dump_work, mt7915_mac_dump_work);
mutex_init(&dev->dump_mutex);
dev->dbdc_support = mt7915_band_config(dev);
......@@ -1142,7 +1145,13 @@ int mt7915_register_device(struct mt7915_dev *dev)
dev->recovery.hw_init_done = true;
mt7915_init_debugfs(&dev->phy);
ret = mt7915_init_debugfs(&dev->phy);
if (ret)
goto unreg_thermal;
ret = mt7915_coredump_register(dev);
if (ret)
goto unreg_thermal;
return 0;
......@@ -1161,6 +1170,7 @@ int mt7915_register_device(struct mt7915_dev *dev)
void mt7915_unregister_device(struct mt7915_dev *dev)
{
mt7915_unregister_ext_phy(dev);
mt7915_coredump_unregister(dev);
mt7915_unregister_thermal(&dev->phy);
mt76_unregister_device(&dev->mt76);
mt7915_stop_hardware(dev);
......
......@@ -3,6 +3,7 @@
#include <linux/etherdevice.h>
#include <linux/timekeeping.h>
#include "coredump.h"
#include "mt7915.h"
#include "../dma.h"
#include "mac.h"
......@@ -1620,6 +1621,76 @@ void mt7915_mac_reset_work(struct work_struct *work)
MT7915_WATCHDOG_TIME);
}
/* firmware coredump */
void mt7915_mac_dump_work(struct work_struct *work)
{
const struct mt7915_mem_region *mem_region;
struct mt7915_crash_data *crash_data;
struct mt7915_dev *dev;
struct mt7915_mem_hdr *hdr;
size_t buf_len;
int i;
u32 num;
u8 *buf;
dev = container_of(work, struct mt7915_dev, dump_work);
mutex_lock(&dev->dump_mutex);
crash_data = mt7915_coredump_new(dev);
if (!crash_data) {
mutex_unlock(&dev->dump_mutex);
goto skip_coredump;
}
mem_region = mt7915_coredump_get_mem_layout(dev, &num);
if (!mem_region || !crash_data->memdump_buf_len) {
mutex_unlock(&dev->dump_mutex);
goto skip_memdump;
}
buf = crash_data->memdump_buf;
buf_len = crash_data->memdump_buf_len;
/* dumping memory content... */
memset(buf, 0, buf_len);
for (i = 0; i < num; i++) {
if (mem_region->len > buf_len) {
dev_warn(dev->mt76.dev, "%s len %lu is too large\n",
mem_region->name,
(unsigned long)mem_region->len);
break;
}
/* reserve space for the header */
hdr = (void *)buf;
buf += sizeof(*hdr);
buf_len -= sizeof(*hdr);
mt7915_memcpy_fromio(dev, buf, mem_region->start,
mem_region->len);
hdr->start = mem_region->start;
hdr->len = mem_region->len;
if (!mem_region->len)
/* note: the header remains, just with zero length */
break;
buf += mem_region->len;
buf_len -= mem_region->len;
mem_region++;
}
mutex_unlock(&dev->dump_mutex);
skip_memdump:
mt7915_coredump_submit(dev);
skip_coredump:
queue_work(dev->mt76.wq, &dev->reset_work);
}
void mt7915_reset(struct mt7915_dev *dev)
{
if (!dev->recovery.hw_init_done)
......@@ -1636,7 +1707,7 @@ void mt7915_reset(struct mt7915_dev *dev)
wiphy_name(dev->mt76.hw->wiphy));
mt7915_irq_disable(dev, MT_INT_MCU_CMD);
queue_work(dev->mt76.wq, &dev->reset_work);
queue_work(dev->mt76.wq, &dev->dump_work);
return;
}
......
......@@ -25,7 +25,22 @@ static const u32 mt7915_reg[] = {
[WFDMA_EXT_CSR_ADDR] = 0xd7000,
[CBTOP1_PHY_END] = 0x77ffffff,
[INFRA_MCU_ADDR_END] = 0x7c3fffff,
[FW_EXCEPTION_ADDR] = 0x219848,
[FW_ASSERT_STAT_ADDR] = 0x219848,
[FW_EXCEPT_TYPE_ADDR] = 0x21987c,
[FW_EXCEPT_COUNT_ADDR] = 0x219848,
[FW_CIRQ_COUNT_ADDR] = 0x216f94,
[FW_CIRQ_IDX_ADDR] = 0x216ef8,
[FW_CIRQ_LISR_ADDR] = 0x2170ac,
[FW_TASK_ID_ADDR] = 0x216f90,
[FW_TASK_IDX_ADDR] = 0x216f9c,
[FW_TASK_QID1_ADDR] = 0x219680,
[FW_TASK_QID2_ADDR] = 0x219760,
[FW_TASK_START_ADDR] = 0x219558,
[FW_TASK_END_ADDR] = 0x219554,
[FW_TASK_SIZE_ADDR] = 0x219560,
[FW_LAST_MSG_ID_ADDR] = 0x216f70,
[FW_EINT_INFO_ADDR] = 0x219818,
[FW_SCHED_INFO_ADDR] = 0x219828,
[SWDEF_BASE_ADDR] = 0x41f200,
[TXQ_WED_RING_BASE] = 0xd7300,
[RXQ_WED_RING_BASE] = 0xd7410,
......@@ -43,7 +58,22 @@ static const u32 mt7916_reg[] = {
[WFDMA_EXT_CSR_ADDR] = 0xd7000,
[CBTOP1_PHY_END] = 0x7fffffff,
[INFRA_MCU_ADDR_END] = 0x7c085fff,
[FW_EXCEPTION_ADDR] = 0x022050bc,
[FW_ASSERT_STAT_ADDR] = 0x02204c14,
[FW_EXCEPT_TYPE_ADDR] = 0x022051a4,
[FW_EXCEPT_COUNT_ADDR] = 0x022050bc,
[FW_CIRQ_COUNT_ADDR] = 0x022001ac,
[FW_CIRQ_IDX_ADDR] = 0x02204f84,
[FW_CIRQ_LISR_ADDR] = 0x022050d0,
[FW_TASK_ID_ADDR] = 0x0220406c,
[FW_TASK_IDX_ADDR] = 0x0220500c,
[FW_TASK_QID1_ADDR] = 0x022028c8,
[FW_TASK_QID2_ADDR] = 0x02202a38,
[FW_TASK_START_ADDR] = 0x0220286c,
[FW_TASK_END_ADDR] = 0x02202870,
[FW_TASK_SIZE_ADDR] = 0x02202878,
[FW_LAST_MSG_ID_ADDR] = 0x02204fe8,
[FW_EINT_INFO_ADDR] = 0x0220525c,
[FW_SCHED_INFO_ADDR] = 0x0220516c,
[SWDEF_BASE_ADDR] = 0x411400,
[TXQ_WED_RING_BASE] = 0xd7300,
[RXQ_WED_RING_BASE] = 0xd7410,
......@@ -61,7 +91,22 @@ static const u32 mt7986_reg[] = {
[WFDMA_EXT_CSR_ADDR] = 0x27000,
[CBTOP1_PHY_END] = 0x7fffffff,
[INFRA_MCU_ADDR_END] = 0x7c085fff,
[FW_EXCEPTION_ADDR] = 0x02204ffc,
[FW_ASSERT_STAT_ADDR] = 0x02204b54,
[FW_EXCEPT_TYPE_ADDR] = 0x022050dc,
[FW_EXCEPT_COUNT_ADDR] = 0x02204ffc,
[FW_CIRQ_COUNT_ADDR] = 0x022001ac,
[FW_CIRQ_IDX_ADDR] = 0x02204ec4,
[FW_CIRQ_LISR_ADDR] = 0x02205010,
[FW_TASK_ID_ADDR] = 0x02204fac,
[FW_TASK_IDX_ADDR] = 0x02204f4c,
[FW_TASK_QID1_ADDR] = 0x02202814,
[FW_TASK_QID2_ADDR] = 0x02202984,
[FW_TASK_START_ADDR] = 0x022027b8,
[FW_TASK_END_ADDR] = 0x022027bc,
[FW_TASK_SIZE_ADDR] = 0x022027c4,
[FW_LAST_MSG_ID_ADDR] = 0x02204f28,
[FW_EINT_INFO_ADDR] = 0x02205194,
[FW_SCHED_INFO_ADDR] = 0x022051a4,
[SWDEF_BASE_ADDR] = 0x411400,
[TXQ_WED_RING_BASE] = 0x24420,
[RXQ_WED_RING_BASE] = 0x24520,
......@@ -457,6 +502,14 @@ static u32 __mt7915_reg_addr(struct mt7915_dev *dev, u32 addr)
return mt7915_reg_map_l2(dev, addr);
}
void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset,
size_t len)
{
u32 addr = __mt7915_reg_addr(dev, offset);
memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len);
}
static u32 mt7915_rr(struct mt76_dev *mdev, u32 offset)
{
struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
......
......@@ -224,6 +224,15 @@ struct mib_stats {
u32 tx_amsdu_cnt;
};
/* crash-dump */
struct mt7915_crash_data {
guid_t guid;
struct timespec64 timestamp;
u8 *memdump_buf;
size_t memdump_buf_len;
};
struct mt7915_hif {
struct list_head list;
......@@ -305,6 +314,7 @@ struct mt7915_dev {
struct work_struct init_work;
struct work_struct rc_work;
struct work_struct dump_work;
struct work_struct reset_work;
wait_queue_head_t reset_wait;
......@@ -317,6 +327,14 @@ struct mt7915_dev {
bool restart:1;
} recovery;
/* protects coredump data */
struct mutex dump_mutex;
#ifdef CONFIG_DEV_COREDUMP
struct {
struct mt7915_crash_data *crash_data;
} coredump;
#endif
struct list_head sta_rc_list;
struct list_head sta_poll_list;
struct list_head twt_list;
......@@ -562,6 +580,9 @@ static inline void mt7915_irq_disable(struct mt7915_dev *dev, u32 mask)
mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0);
}
void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset,
size_t len);
void mt7915_mac_init(struct mt7915_dev *dev);
u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid, u8 dw);
bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask);
......@@ -581,6 +602,7 @@ void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
void mt7915_mac_work(struct work_struct *work);
void mt7915_mac_reset_work(struct work_struct *work);
void mt7915_mac_dump_work(struct work_struct *work);
void mt7915_mac_sta_rc_work(struct work_struct *work);
void mt7915_mac_update_stats(struct mt7915_phy *phy);
void mt7915_mac_twt_teardown_flow(struct mt7915_dev *dev,
......
......@@ -24,7 +24,22 @@ enum reg_rev {
WFDMA_EXT_CSR_ADDR,
CBTOP1_PHY_END,
INFRA_MCU_ADDR_END,
FW_EXCEPTION_ADDR,
FW_ASSERT_STAT_ADDR,
FW_EXCEPT_TYPE_ADDR,
FW_EXCEPT_COUNT_ADDR,
FW_CIRQ_COUNT_ADDR,
FW_CIRQ_IDX_ADDR,
FW_CIRQ_LISR_ADDR,
FW_TASK_ID_ADDR,
FW_TASK_IDX_ADDR,
FW_TASK_QID1_ADDR,
FW_TASK_QID2_ADDR,
FW_TASK_START_ADDR,
FW_TASK_END_ADDR,
FW_TASK_SIZE_ADDR,
FW_LAST_MSG_ID_ADDR,
FW_EINT_INFO_ADDR,
FW_SCHED_INFO_ADDR,
SWDEF_BASE_ADDR,
TXQ_WED_RING_BASE,
RXQ_WED_RING_BASE,
......@@ -979,7 +994,22 @@ enum offs_rev {
#define MT_ADIE_TYPE_MASK BIT(1)
/* FW MODE SYNC */
#define MT_FW_EXCEPTION __REG(FW_EXCEPTION_ADDR)
#define MT_FW_ASSERT_STAT __REG(FW_ASSERT_STAT_ADDR)
#define MT_FW_EXCEPT_TYPE __REG(FW_EXCEPT_TYPE_ADDR)
#define MT_FW_EXCEPT_COUNT __REG(FW_EXCEPT_COUNT_ADDR)
#define MT_FW_CIRQ_COUNT __REG(FW_CIRQ_COUNT_ADDR)
#define MT_FW_CIRQ_IDX __REG(FW_CIRQ_IDX_ADDR)
#define MT_FW_CIRQ_LISR __REG(FW_CIRQ_LISR_ADDR)
#define MT_FW_TASK_ID __REG(FW_TASK_ID_ADDR)
#define MT_FW_TASK_IDX __REG(FW_TASK_IDX_ADDR)
#define MT_FW_TASK_QID1 __REG(FW_TASK_QID1_ADDR)
#define MT_FW_TASK_QID2 __REG(FW_TASK_QID2_ADDR)
#define MT_FW_TASK_START __REG(FW_TASK_START_ADDR)
#define MT_FW_TASK_END __REG(FW_TASK_END_ADDR)
#define MT_FW_TASK_SIZE __REG(FW_TASK_SIZE_ADDR)
#define MT_FW_LAST_MSG_ID __REG(FW_LAST_MSG_ID_ADDR)
#define MT_FW_EINT_INFO __REG(FW_EINT_INFO_ADDR)
#define MT_FW_SCHED_INFO __REG(FW_SCHED_INFO_ADDR)
#define MT_SWDEF_BASE __REG(SWDEF_BASE_ADDR)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment