mt76: mt7915: add wifi subsystem reset
authorRyder Lee <ryder.lee@mediatek.com>
Tue, 6 Apr 2021 04:46:09 +0000 (12:46 +0800)
committerFelix Fietkau <nbd@nbd.name>
Sun, 11 Apr 2021 22:03:06 +0000 (00:03 +0200)
Reset wifi subsystem when MCU is already running.
Fixes firmware download failure after soft reboot on systems where the PCIe
reset could not be performed properly.

Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
Co-developed-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt7915/init.c
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
drivers/net/wireless/mediatek/mt76/mt7915/regs.h

index 2ffadd22c25939146be07d4216b93c8960d249a8..261f5ab237214a8bf2f1a22dda2b72d853cba768 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/etherdevice.h>
 #include "mt7915.h"
 #include "mac.h"
+#include "mcu.h"
 #include "eeprom.h"
 
 #define CCK_RATE(_idx, _rate) {                                                \
@@ -295,9 +296,50 @@ static void mt7915_init_work(struct work_struct *work)
        mt7915_txbf_init(dev);
 }
 
+static void mt7915_wfsys_reset(struct mt7915_dev *dev)
+{
+       u32 val = MT_TOP_PWR_KEY | MT_TOP_PWR_SW_PWR_ON | MT_TOP_PWR_PWR_ON;
+       u32 reg = mt7915_reg_map_l1(dev, MT_TOP_MISC);
+
+#define MT_MCU_DUMMY_RANDOM    GENMASK(15, 0)
+#define MT_MCU_DUMMY_DEFAULT   GENMASK(31, 16)
+
+       mt76_wr(dev, MT_MCU_WFDMA0_DUMMY_CR, MT_MCU_DUMMY_RANDOM);
+
+       /* change to software control */
+       val |= MT_TOP_PWR_SW_RST;
+       mt76_wr(dev, MT_TOP_PWR_CTRL, val);
+
+       /* reset wfsys */
+       val &= ~MT_TOP_PWR_SW_RST;
+       mt76_wr(dev, MT_TOP_PWR_CTRL, val);
+
+       /* release wfsys then mcu re-excutes romcode */
+       val |= MT_TOP_PWR_SW_RST;
+       mt76_wr(dev, MT_TOP_PWR_CTRL, val);
+
+       /* switch to hw control */
+       val &= ~MT_TOP_PWR_SW_RST;
+       val |= MT_TOP_PWR_HW_CTRL;
+       mt76_wr(dev, MT_TOP_PWR_CTRL, val);
+
+       /* check whether mcu resets to default */
+       if (!mt76_poll_msec(dev, MT_MCU_WFDMA0_DUMMY_CR, MT_MCU_DUMMY_DEFAULT,
+                           MT_MCU_DUMMY_DEFAULT, 1000)) {
+               dev_err(dev->mt76.dev, "wifi subsystem reset failure\n");
+               return;
+       }
+
+       /* wfsys reset won't clear host registers */
+       mt76_clear(dev, reg, MT_TOP_MISC_FW_STATE);
+
+       msleep(100);
+}
+
 static int mt7915_init_hardware(struct mt7915_dev *dev)
 {
        int ret, idx;
+       u32 val;
 
        mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
 
@@ -307,6 +349,12 @@ static int mt7915_init_hardware(struct mt7915_dev *dev)
 
        dev->dbdc_support = !!(mt7915_l1_rr(dev, MT_HW_BOUND) & BIT(5));
 
+       val = mt76_rr(dev, mt7915_reg_map_l1(dev, MT_TOP_MISC));
+
+       /* If MCU was already running, it is likely in a bad state */
+       if (FIELD_GET(MT_TOP_MISC_FW_STATE, val) > FW_STATE_FW_DOWNLOAD)
+               mt7915_wfsys_reset(dev);
+
        ret = mt7915_dma_init(dev);
        if (ret)
                return ret;
@@ -320,8 +368,14 @@ static int mt7915_init_hardware(struct mt7915_dev *dev)
        mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE);
 
        ret = mt7915_mcu_init(dev);
-       if (ret)
-               return ret;
+       if (ret) {
+               /* Reset and try again */
+               mt7915_wfsys_reset(dev);
+
+               ret = mt7915_mcu_init(dev);
+               if (ret)
+                       return ret;
+       }
 
        ret = mt7915_eeprom_init(dev);
        if (ret < 0)
index 3507fd135dfe92705e9a0f6f0d4c051fcdadb3b0..639f65d63addc7d68d0a38aceaae2f1a4da900b0 100644 (file)
@@ -2784,21 +2784,8 @@ out:
 
 static int mt7915_load_firmware(struct mt7915_dev *dev)
 {
+       u32 reg = mt7915_reg_map_l1(dev, MT_TOP_MISC);
        int ret;
-       u32 val, reg = mt7915_reg_map_l1(dev, MT_TOP_MISC);
-
-       val = FIELD_PREP(MT_TOP_MISC_FW_STATE, FW_STATE_FW_DOWNLOAD);
-
-       if (!mt76_poll_msec(dev, reg, MT_TOP_MISC_FW_STATE, val, 1000)) {
-               /* restart firmware once */
-               __mt76_mcu_restart(&dev->mt76);
-               if (!mt76_poll_msec(dev, reg, MT_TOP_MISC_FW_STATE,
-                                   val, 1000)) {
-                       dev_err(dev->mt76.dev,
-                               "Firmware is not ready for download\n");
-                       return -EIO;
-               }
-       }
 
        ret = mt7915_load_patch(dev);
        if (ret)
index ed0c9a24bb53dcb0beb4b9c8e9b1ad042e319eeb..dfb8880657bf6031cc71c16ff7f0e4887af8b99b 100644 (file)
@@ -4,6 +4,11 @@
 #ifndef __MT7915_REGS_H
 #define __MT7915_REGS_H
 
+/* MCU WFDMA0 */
+#define MT_MCU_WFDMA0_BASE             0x2000
+#define MT_MCU_WFDMA0(ofs)             (MT_MCU_WFDMA0_BASE + (ofs))
+#define MT_MCU_WFDMA0_DUMMY_CR         MT_MCU_WFDMA0(0x120)
+
 /* MCU WFDMA1 */
 #define MT_MCU_WFDMA1_BASE             0x3000
 #define MT_MCU_WFDMA1(ofs)             (MT_MCU_WFDMA1_BASE + (ofs))
 #define MT_WFDMA1_PCIE1_BUSY_ENA_TX_FIFO1      BIT(1)
 #define MT_WFDMA1_PCIE1_BUSY_ENA_RX_FIFO       BIT(2)
 
+#define MT_TOP_RGU_BASE                                0xf0000
+#define MT_TOP_PWR_CTRL                                (MT_TOP_RGU_BASE + (0x0))
+#define MT_TOP_PWR_KEY                         (0x5746 << 16)
+#define MT_TOP_PWR_SW_RST                      BIT(0)
+#define MT_TOP_PWR_SW_PWR_ON                   GENMASK(3, 2)
+#define MT_TOP_PWR_HW_CTRL                     BIT(4)
+#define MT_TOP_PWR_PWR_ON                      BIT(7)
+
 #define MT_INFRA_CFG_BASE              0xf1000
 #define MT_INFRA(ofs)                  (MT_INFRA_CFG_BASE + (ofs))