summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/realtek/rtw88/main.c
diff options
context:
space:
mode:
authorZong-Zhe Yang <kevin_yang@realtek.com>2021-05-28 06:29:00 +0300
committerKalle Valo <kvalo@codeaurora.org>2021-06-22 18:21:31 +0300
commit7b80f3e48c4b0ff85ff91945a2537d0bbc3e3cc4 (patch)
tree9f850c171b881aeeeaaf7f5a02bc7b41e42d6d5a /drivers/net/wireless/realtek/rtw88/main.c
parenta853d234e179086040912a8bbb3341829c079495 (diff)
downloadlinux-7b80f3e48c4b0ff85ff91945a2537d0bbc3e3cc4.tar.xz
rtw88: dump FW crash via devcoredump
Use device coredump framework instead of print_hex_dump to support FW crash dump. Pass data to the framework if preparing and dumping are successful. The framework will take the ownership of the data. The data will be freed after the framework determines its lifetime is over. A new coredump will not work if the previous one still exists. Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org> Link: https://lore.kernel.org/r/20210528032901.12927-2-pkshih@realtek.com
Diffstat (limited to 'drivers/net/wireless/realtek/rtw88/main.c')
-rw-r--r--drivers/net/wireless/realtek/rtw88/main.c170
1 files changed, 119 insertions, 51 deletions
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index 47f4838d0c58..4a9a8544e8ca 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -2,6 +2,8 @@
/* Copyright(c) 2018-2019 Realtek Corporation
*/
+#include <linux/devcoredump.h>
+
#include "main.h"
#include "regd.h"
#include "fw.h"
@@ -320,59 +322,131 @@ void rtw_sta_remove(struct rtw_dev *rtwdev, struct ieee80211_sta *sta,
sta->addr, si->mac_id);
}
-static bool rtw_fw_dump_crash_log(struct rtw_dev *rtwdev)
+struct rtw_fwcd_hdr {
+ u32 item;
+ u32 size;
+ u32 padding1;
+ u32 padding2;
+} __packed;
+
+static int rtw_fwcd_prep(struct rtw_dev *rtwdev)
+{
+ struct rtw_chip_info *chip = rtwdev->chip;
+ struct rtw_fwcd_desc *desc = &rtwdev->fw.fwcd_desc;
+ const struct rtw_fwcd_segs *segs = chip->fwcd_segs;
+ u32 prep_size = chip->fw_rxff_size + sizeof(struct rtw_fwcd_hdr);
+ u8 i;
+
+ if (segs) {
+ prep_size += segs->num * sizeof(struct rtw_fwcd_hdr);
+
+ for (i = 0; i < segs->num; i++)
+ prep_size += segs->segs[i];
+ }
+
+ desc->data = vmalloc(prep_size);
+ if (!desc->data)
+ return -ENOMEM;
+
+ desc->size = prep_size;
+ desc->next = desc->data;
+
+ return 0;
+}
+
+static u8 *rtw_fwcd_next(struct rtw_dev *rtwdev, u32 item, u32 size)
+{
+ struct rtw_fwcd_desc *desc = &rtwdev->fw.fwcd_desc;
+ struct rtw_fwcd_hdr *hdr;
+ u8 *next;
+
+ if (!desc->data) {
+ rtw_dbg(rtwdev, RTW_DBG_FW, "fwcd isn't prepared successfully\n");
+ return NULL;
+ }
+
+ next = desc->next + sizeof(struct rtw_fwcd_hdr);
+ if (next - desc->data + size > desc->size) {
+ rtw_dbg(rtwdev, RTW_DBG_FW, "fwcd isn't prepared enough\n");
+ return NULL;
+ }
+
+ hdr = (struct rtw_fwcd_hdr *)(desc->next);
+ hdr->item = item;
+ hdr->size = size;
+ hdr->padding1 = 0x01234567;
+ hdr->padding2 = 0x89abcdef;
+ desc->next = next + size;
+
+ return next;
+}
+
+static void rtw_fwcd_dump(struct rtw_dev *rtwdev)
+{
+ struct rtw_fwcd_desc *desc = &rtwdev->fw.fwcd_desc;
+
+ rtw_dbg(rtwdev, RTW_DBG_FW, "dump fwcd\n");
+
+ /* Data will be freed after lifetime of device coredump. After calling
+ * dev_coredump, data is supposed to be handled by the device coredump
+ * framework. Note that a new dump will be discarded if a previous one
+ * hasn't been released yet.
+ */
+ dev_coredumpv(rtwdev->dev, desc->data, desc->size, GFP_KERNEL);
+}
+
+static void rtw_fwcd_free(struct rtw_dev *rtwdev, bool free_self)
+{
+ struct rtw_fwcd_desc *desc = &rtwdev->fw.fwcd_desc;
+
+ if (free_self) {
+ rtw_dbg(rtwdev, RTW_DBG_FW, "free fwcd by self\n");
+ vfree(desc->data);
+ }
+
+ desc->data = NULL;
+ desc->next = NULL;
+}
+
+static int rtw_fw_dump_crash_log(struct rtw_dev *rtwdev)
{
u32 size = rtwdev->chip->fw_rxff_size;
u32 *buf;
u8 seq;
- bool ret = true;
- buf = vmalloc(size);
+ buf = (u32 *)rtw_fwcd_next(rtwdev, RTW_FWCD_TLV, size);
if (!buf)
- goto exit;
+ return -ENOMEM;
if (rtw_fw_dump_fifo(rtwdev, RTW_FW_FIFO_SEL_RXBUF_FW, 0, size, buf)) {
rtw_dbg(rtwdev, RTW_DBG_FW, "dump fw fifo fail\n");
- goto free_buf;
+ return -EINVAL;
}
if (GET_FW_DUMP_LEN(buf) == 0) {
rtw_dbg(rtwdev, RTW_DBG_FW, "fw crash dump's length is 0\n");
- goto free_buf;
+ return -EINVAL;
}
seq = GET_FW_DUMP_SEQ(buf);
- if (seq > 0 && seq != (rtwdev->fw.prev_dump_seq + 1)) {
+ if (seq > 0) {
rtw_dbg(rtwdev, RTW_DBG_FW,
"fw crash dump's seq is wrong: %d\n", seq);
- goto free_buf;
- }
-
- print_hex_dump(KERN_ERR, "rtw88 fw dump: ", DUMP_PREFIX_OFFSET, 16, 1,
- buf, size, true);
-
- if (GET_FW_DUMP_MORE(buf) == 1) {
- rtwdev->fw.prev_dump_seq = seq;
- ret = false;
+ return -EINVAL;
}
-free_buf:
- vfree(buf);
-exit:
- rtw_write8(rtwdev, REG_MCU_TST_CFG, 0);
-
- return ret;
+ return 0;
}
int rtw_dump_fw(struct rtw_dev *rtwdev, const u32 ocp_src, u32 size,
- const char *prefix_str)
+ u32 fwcd_item)
{
u32 rxff = rtwdev->chip->fw_rxff_size;
u32 dump_size, done_size = 0;
u8 *buf;
int ret;
- buf = vzalloc(size);
+ buf = rtw_fwcd_next(rtwdev, fwcd_item, size);
if (!buf)
return -ENOMEM;
@@ -385,7 +459,7 @@ int rtw_dump_fw(struct rtw_dev *rtwdev, const u32 ocp_src, u32 size,
rtw_err(rtwdev,
"ddma fw 0x%x [+0x%x] to fw fifo fail\n",
ocp_src, done_size);
- goto exit;
+ return ret;
}
ret = rtw_fw_dump_fifo(rtwdev, RTW_FW_FIFO_SEL_RXBUF_FW, 0,
@@ -394,24 +468,18 @@ int rtw_dump_fw(struct rtw_dev *rtwdev, const u32 ocp_src, u32 size,
rtw_err(rtwdev,
"dump fw 0x%x [+0x%x] from fw fifo fail\n",
ocp_src, done_size);
- goto exit;
+ return ret;
}
size -= dump_size;
done_size += dump_size;
}
- print_hex_dump(KERN_ERR, prefix_str, DUMP_PREFIX_OFFSET, 16, 1,
- buf, done_size, true);
-
-exit:
- vfree(buf);
- return ret;
+ return 0;
}
EXPORT_SYMBOL(rtw_dump_fw);
-int rtw_dump_reg(struct rtw_dev *rtwdev, const u32 addr, const u32 size,
- const char *prefix_str)
+int rtw_dump_reg(struct rtw_dev *rtwdev, const u32 addr, const u32 size)
{
u8 *buf;
u32 i;
@@ -421,17 +489,13 @@ int rtw_dump_reg(struct rtw_dev *rtwdev, const u32 addr, const u32 size,
return -EINVAL;
}
- buf = vzalloc(size);
+ buf = rtw_fwcd_next(rtwdev, RTW_FWCD_REG, size);
if (!buf)
return -ENOMEM;
for (i = 0; i < size; i += 4)
*(u32 *)(buf + i) = rtw_read32(rtwdev, addr + i);
- print_hex_dump(KERN_ERR, prefix_str, DUMP_PREFIX_OFFSET, 16, 4, buf,
- size, true);
-
- vfree(buf);
return 0;
}
EXPORT_SYMBOL(rtw_dump_reg);
@@ -489,20 +553,24 @@ void rtw_fw_recovery(struct rtw_dev *rtwdev)
static void __fw_recovery_work(struct rtw_dev *rtwdev)
{
-
- /* rtw_fw_dump_crash_log() returns false indicates that there are
- * still more log to dump. Driver set 0x1cf[7:0] = 0x1 to tell firmware
- * to dump the remaining part of the log, and firmware will trigger an
- * IMR_C2HCMD interrupt to inform driver the log is ready.
- */
- if (!rtw_fw_dump_crash_log(rtwdev)) {
- rtw_write8(rtwdev, REG_HRCV_MSG, 1);
- return;
- }
- rtwdev->fw.prev_dump_seq = 0;
+ int ret = 0;
set_bit(RTW_FLAG_RESTARTING, rtwdev->flags);
- rtw_chip_dump_fw_crash(rtwdev);
+
+ ret = rtw_fwcd_prep(rtwdev);
+ if (ret)
+ goto free;
+ ret = rtw_fw_dump_crash_log(rtwdev);
+ if (ret)
+ goto free;
+ ret = rtw_chip_dump_fw_crash(rtwdev);
+ if (ret)
+ goto free;
+
+ rtw_fwcd_dump(rtwdev);
+free:
+ rtw_fwcd_free(rtwdev, !!ret);
+ rtw_write8(rtwdev, REG_MCU_TST_CFG, 0);
WARN(1, "firmware crash, start reset and recover\n");