OSDN Git Service

rtw88: add dump fw crash log
authorTzu-En Huang <tehuang@realtek.com>
Fri, 25 Sep 2020 06:12:18 +0000 (14:12 +0800)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 29 Sep 2020 08:22:21 +0000 (11:22 +0300)
This patch adds a function which is able to dump firmware fifo when
firmware crashes. If firmware needs more than one time to dump all logs,
it will set a bit called "more bit" in the header of the first log, and
driver needs to set a register to inform firmware that it is ready for the
next dump.

Signed-off-by: Tzu-En Huang <tehuang@realtek.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200925061219.23754-5-tehuang@realtek.com
drivers/net/wireless/realtek/rtw88/fw.h
drivers/net/wireless/realtek/rtw88/main.c
drivers/net/wireless/realtek/rtw88/main.h
drivers/net/wireless/realtek/rtw88/reg.h

index 9c4863c..0864454 100644 (file)
@@ -507,6 +507,20 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
 #define SET_NLO_LOC_NLO_INFO(h2c_pkt, value)                                   \
        le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16))
 
+#define GET_FW_DUMP_LEN(_header)                                       \
+       le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(15, 0))
+#define GET_FW_DUMP_SEQ(_header)                                       \
+       le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(22, 16))
+#define GET_FW_DUMP_MORE(_header)                                      \
+       le32_get_bits(*((__le32 *)(_header) + 0x00), BIT(23))
+#define GET_FW_DUMP_VERSION(_header)                                   \
+       le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(31, 24))
+#define GET_FW_DUMP_TLV_TYPE(_header)                                  \
+       le32_get_bits(*((__le32 *)(_header) + 0x01), GENMASK(15, 0))
+#define GET_FW_DUMP_TLV_LEN(_header)                                   \
+       le32_get_bits(*((__le32 *)(_header) + 0x01), GENMASK(31, 16))
+#define GET_FW_DUMP_TLV_VAL(_header)                                   \
+       le32_get_bits(*((__le32 *)(_header) + 0x02), GENMASK(31, 0))
 static inline struct rtw_c2h_cmd *get_c2h_from_skb(struct sk_buff *skb)
 {
        u32 pkt_offset;
index 420853e..565efd8 100644 (file)
@@ -317,6 +317,56 @@ 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)
+{
+       u32 size = rtwdev->chip->fw_rxff_size;
+       u32 *buf;
+       u8 seq;
+       bool ret = true;
+
+       buf = vmalloc(size);
+       if (!buf)
+               goto exit;
+
+       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;
+       }
+
+       if (GET_FW_DUMP_LEN(buf) == 0) {
+               rtw_dbg(rtwdev, RTW_DBG_FW, "fw crash dump's length is 0\n");
+               goto free_buf;
+       }
+
+       seq = GET_FW_DUMP_SEQ(buf);
+       if (seq > 0 && seq != (rtwdev->fw.prev_dump_seq + 1)) {
+               rtw_dbg(rtwdev, RTW_DBG_FW,
+                       "fw crash dump's seq is wrong: %d\n", seq);
+               goto free_buf;
+       }
+       if (seq == 0 &&
+           (GET_FW_DUMP_TLV_TYPE(buf) != FW_CD_TYPE ||
+            GET_FW_DUMP_TLV_LEN(buf) != FW_CD_LEN ||
+            GET_FW_DUMP_TLV_VAL(buf) != FW_CD_VAL)) {
+               rtw_dbg(rtwdev, RTW_DBG_FW, "fw crash dump's tlv is wrong\n");
+               goto free_buf;
+       }
+
+       print_hex_dump_bytes("rtw88 fw dump: ", DUMP_PREFIX_OFFSET, buf, size);
+
+       if (GET_FW_DUMP_MORE(buf) == 1) {
+               rtwdev->fw.prev_dump_seq = seq;
+               ret = false;
+       }
+
+free_buf:
+       vfree(buf);
+exit:
+       rtw_write8(rtwdev, REG_MCU_TST_CFG, 0);
+
+       return ret;
+}
+
 void rtw_vif_assoc_changed(struct rtw_vif *rtwvif,
                           struct ieee80211_bss_conf *conf)
 {
@@ -373,6 +423,17 @@ static void rtw_fw_recovery_work(struct work_struct *work)
        struct rtw_dev *rtwdev = container_of(work, struct rtw_dev,
                                              fw_recovery_work);
 
+       /* 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;
+
        WARN(1, "firmware crash, start reset and recover\n");
 
        mutex_lock(&rtwdev->mutex);
index 06bdc68..ffb02e6 100644 (file)
@@ -1621,6 +1621,9 @@ struct rtw_fifo_conf {
        const struct rtw_rqpn *rqpn;
 };
 
+#define FW_CD_TYPE 0xffff
+#define FW_CD_LEN 4
+#define FW_CD_VAL 0xaabbccdd
 struct rtw_fw_state {
        const struct firmware *firmware;
        struct rtw_dev *rtwdev;
@@ -1629,6 +1632,7 @@ struct rtw_fw_state {
        u8 sub_version;
        u8 sub_index;
        u16 h2c_version;
+       u8 prev_dump_seq;
 };
 
 struct rtw_hal {
index 9a696ac..86b94c0 100644 (file)
 #define BIT_ANAPAR_BTPS        BIT(22)
 #define REG_RSTB_SEL   0x1c38
 
+#define REG_HRCV_MSG   0x1cf
+
 #define REG_IGN_GNTBT4 0x4160
 
 #define RF_MODE                0x00