summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/mediatek/mt76/mt7915/init.c
diff options
context:
space:
mode:
authorRyder Lee <ryder.lee@mediatek.com>2021-04-06 12:46:09 +0800
committerFelix Fietkau <nbd@nbd.name>2021-04-12 00:03:06 +0200
commite07419a7dca97dd9bddfe5d099380857c19535f3 (patch)
tree17ff780ed8ee5007de617866d5d348a9921dde57 /drivers/net/wireless/mediatek/mt76/mt7915/init.c
parentac15f9b6da3957b360c356a3a6bb74f5727e433e (diff)
mt76: mt7915: add wifi subsystem reset
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>
Diffstat (limited to 'drivers/net/wireless/mediatek/mt76/mt7915/init.c')
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/init.c58
1 files changed, 56 insertions, 2 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index 2ffadd22c259..261f5ab23721 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -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)