--- /dev/null
+From c001df978e4cb88975147ddd2c829c9e12a55076 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sun, 7 Mar 2021 19:20:50 +0100
+Subject: mt76: dma: export mt76_dma_rx_cleanup routine
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+commit c001df978e4cb88975147ddd2c829c9e12a55076 upstream.
+
+Export mt76_dma_rx_cleanup routine in mt76_queue_ops data structure.
+This is a preliminary patch to introduce mt7921 chip reset support.
+
+Co-developed-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Cc: Deren Wu <Deren.Wu@mediatek.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/mediatek/mt76/dma.c | 1 +
+ drivers/net/wireless/mediatek/mt76/mt76.h | 5 ++++-
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/wireless/mediatek/mt76/dma.c
++++ b/drivers/net/wireless/mediatek/mt76/dma.c
+@@ -653,6 +653,7 @@ static const struct mt76_queue_ops mt76_
+ .tx_queue_skb_raw = mt76_dma_tx_queue_skb_raw,
+ .tx_queue_skb = mt76_dma_tx_queue_skb,
+ .tx_cleanup = mt76_dma_tx_cleanup,
++ .rx_cleanup = mt76_dma_rx_cleanup,
+ .rx_reset = mt76_dma_rx_reset,
+ .kick = mt76_dma_kick_queue,
+ };
+--- a/drivers/net/wireless/mediatek/mt76/mt76.h
++++ b/drivers/net/wireless/mediatek/mt76/mt76.h
+@@ -190,6 +190,8 @@ struct mt76_queue_ops {
+ void (*tx_cleanup)(struct mt76_dev *dev, struct mt76_queue *q,
+ bool flush);
+
++ void (*rx_cleanup)(struct mt76_dev *dev, struct mt76_queue *q);
++
+ void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
+
+ void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q);
+@@ -786,7 +788,8 @@ static inline u16 mt76_rev(struct mt76_d
+ #define mt76_tx_queue_skb_raw(dev, ...) (dev)->mt76.queue_ops->tx_queue_skb_raw(&((dev)->mt76), __VA_ARGS__)
+ #define mt76_tx_queue_skb(dev, ...) (dev)->mt76.queue_ops->tx_queue_skb(&((dev)->mt76), __VA_ARGS__)
+ #define mt76_queue_rx_reset(dev, ...) (dev)->mt76.queue_ops->rx_reset(&((dev)->mt76), __VA_ARGS__)
+-#define mt76_queue_tx_cleanup(dev, ...) (dev)->mt76.queue_ops->tx_cleanup(&((dev)->mt76), __VA_ARGS__)
++#define mt76_queue_tx_cleanup(dev, ...) (dev)->mt76.queue_ops->tx_cleanup(&((dev)->mt76), __VA_ARGS__)
++#define mt76_queue_rx_cleanup(dev, ...) (dev)->mt76.queue_ops->rx_cleanup(&((dev)->mt76), __VA_ARGS__)
+ #define mt76_queue_kick(dev, ...) (dev)->mt76.queue_ops->kick(&((dev)->mt76), __VA_ARGS__)
+ #define mt76_queue_reset(dev, ...) (dev)->mt76.queue_ops->reset_q(&((dev)->mt76), __VA_ARGS__)
+
--- /dev/null
+From 3990465db6829c91e8ebfde51ba2d98885020249 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sun, 7 Mar 2021 19:20:49 +0100
+Subject: mt76: dma: introduce mt76_dma_queue_reset routine
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+commit 3990465db6829c91e8ebfde51ba2d98885020249 upstream.
+
+Introduce mt76_dma_queue_reset utility routine to reset a given hw
+queue. This is a preliminary patch to introduce mt7921 chip reset
+support.
+
+Co-developed-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Cc: Deren Wu <Deren.Wu@mediatek.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/mediatek/mt76/dma.c | 46 ++++++++++++++++++------------
+ drivers/net/wireless/mediatek/mt76/mt76.h | 3 +
+ 2 files changed, 31 insertions(+), 18 deletions(-)
+
+--- a/drivers/net/wireless/mediatek/mt76/dma.c
++++ b/drivers/net/wireless/mediatek/mt76/dma.c
+@@ -79,13 +79,38 @@ mt76_free_pending_txwi(struct mt76_dev *
+ local_bh_enable();
+ }
+
++static void
++mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
++{
++ writel(q->desc_dma, &q->regs->desc_base);
++ writel(q->ndesc, &q->regs->ring_size);
++ q->head = readl(&q->regs->dma_idx);
++ q->tail = q->head;
++}
++
++static void
++mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
++{
++ int i;
++
++ if (!q)
++ return;
++
++ /* clear descriptors */
++ for (i = 0; i < q->ndesc; i++)
++ q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
++
++ writel(0, &q->regs->cpu_idx);
++ writel(0, &q->regs->dma_idx);
++ mt76_dma_sync_idx(dev, q);
++}
++
+ static int
+ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
+ int idx, int n_desc, int bufsize,
+ u32 ring_base)
+ {
+ int size;
+- int i;
+
+ spin_lock_init(&q->lock);
+ spin_lock_init(&q->cleanup_lock);
+@@ -105,14 +130,7 @@ mt76_dma_alloc_queue(struct mt76_dev *de
+ if (!q->entry)
+ return -ENOMEM;
+
+- /* clear descriptors */
+- for (i = 0; i < q->ndesc; i++)
+- q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE);
+-
+- writel(q->desc_dma, &q->regs->desc_base);
+- writel(0, &q->regs->cpu_idx);
+- writel(0, &q->regs->dma_idx);
+- writel(q->ndesc, &q->regs->ring_size);
++ mt76_dma_queue_reset(dev, q);
+
+ return 0;
+ }
+@@ -202,15 +220,6 @@ mt76_dma_tx_cleanup_idx(struct mt76_dev
+ }
+
+ static void
+-mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
+-{
+- writel(q->desc_dma, &q->regs->desc_base);
+- writel(q->ndesc, &q->regs->ring_size);
+- q->head = readl(&q->regs->dma_idx);
+- q->tail = q->head;
+-}
+-
+-static void
+ mt76_dma_kick_queue(struct mt76_dev *dev, struct mt76_queue *q)
+ {
+ wmb();
+@@ -640,6 +649,7 @@ mt76_dma_init(struct mt76_dev *dev)
+ static const struct mt76_queue_ops mt76_dma_ops = {
+ .init = mt76_dma_init,
+ .alloc = mt76_dma_alloc_queue,
++ .reset_q = mt76_dma_queue_reset,
+ .tx_queue_skb_raw = mt76_dma_tx_queue_skb_raw,
+ .tx_queue_skb = mt76_dma_tx_queue_skb,
+ .tx_cleanup = mt76_dma_tx_cleanup,
+--- a/drivers/net/wireless/mediatek/mt76/mt76.h
++++ b/drivers/net/wireless/mediatek/mt76/mt76.h
+@@ -191,6 +191,8 @@ struct mt76_queue_ops {
+ bool flush);
+
+ void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
++
++ void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q);
+ };
+
+ enum mt76_wcid_flags {
+@@ -786,6 +788,7 @@ static inline u16 mt76_rev(struct mt76_d
+ #define mt76_queue_rx_reset(dev, ...) (dev)->mt76.queue_ops->rx_reset(&((dev)->mt76), __VA_ARGS__)
+ #define mt76_queue_tx_cleanup(dev, ...) (dev)->mt76.queue_ops->tx_cleanup(&((dev)->mt76), __VA_ARGS__)
+ #define mt76_queue_kick(dev, ...) (dev)->mt76.queue_ops->kick(&((dev)->mt76), __VA_ARGS__)
++#define mt76_queue_reset(dev, ...) (dev)->mt76.queue_ops->reset_q(&((dev)->mt76), __VA_ARGS__)
+
+ #define mt76_for_each_q_rx(dev, i) \
+ for (i = 0; i < ARRAY_SIZE((dev)->q_rx) && \
--- /dev/null
+From e513ae49088bbb0d00299a9f996f88f08cca7dc6 Mon Sep 17 00:00:00 2001
+From: Sean Wang <sean.wang@mediatek.com>
+Date: Fri, 16 Apr 2021 23:30:36 +0800
+Subject: mt76: mt7921: abort uncompleted scan by wifi reset
+
+From: Sean Wang <sean.wang@mediatek.com>
+
+commit e513ae49088bbb0d00299a9f996f88f08cca7dc6 upstream.
+
+Scan abort should be required for the uncompleted hardware scan
+interrupted by wifi reset. Otherwise, it is possible that the scan
+request after wifi reset gets error code -EBUSY from mac80211 and
+then blocks the reconnectting to the access point.
+
+Fixes: 0c1ce9884607 ("mt76: mt7921: add wifi reset support")
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Cc: Deren Wu <Deren.Wu@mediatek.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+@@ -1386,6 +1386,14 @@ void mt7921_mac_reset_work(struct work_s
+ if (i == 10)
+ dev_err(dev->mt76.dev, "chip reset failed\n");
+
++ if (test_and_clear_bit(MT76_HW_SCANNING, &dev->mphy.state)) {
++ struct cfg80211_scan_info info = {
++ .aborted = true,
++ };
++
++ ieee80211_scan_completed(dev->mphy.hw, &info);
++ }
++
+ ieee80211_wake_queues(hw);
+ ieee80211_iterate_active_interfaces(hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
--- /dev/null
+From 0c1ce988460765ece1ba8eacd00533eefb6e666a Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sun, 7 Mar 2021 19:20:51 +0100
+Subject: mt76: mt7921: add wifi reset support
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+commit 0c1ce988460765ece1ba8eacd00533eefb6e666a upstream.
+
+Introduce wifi chip reset support for mt7921 device to recover mcu
+hangs.
+
+Co-developed-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Cc: Deren Wu <Deren.Wu@mediatek.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/mediatek/mt76/mt7921/init.c | 3
+ drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 203 +++++++++++++++------
+ drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 3
+ drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h | 4
+ drivers/net/wireless/mediatek/mt76/mt7921/regs.h | 4
+ 5 files changed, 156 insertions(+), 61 deletions(-)
+
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
+@@ -142,7 +142,7 @@ mt7921_mac_init_band(struct mt7921_dev *
+ mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN);
+ }
+
+-static void mt7921_mac_init(struct mt7921_dev *dev)
++void mt7921_mac_init(struct mt7921_dev *dev)
+ {
+ int i;
+
+@@ -232,7 +232,6 @@ int mt7921_register_device(struct mt7921
+ INIT_LIST_HEAD(&dev->sta_poll_list);
+ spin_lock_init(&dev->sta_poll_lock);
+
+- init_waitqueue_head(&dev->reset_wait);
+ INIT_WORK(&dev->reset_work, mt7921_mac_reset_work);
+
+ ret = mt7921_init_hardware(dev);
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+@@ -1184,43 +1184,77 @@ void mt7921_update_channel(struct mt76_d
+ mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
+ }
+
+-static bool
+-mt7921_wait_reset_state(struct mt7921_dev *dev, u32 state)
++static int
++mt7921_wfsys_reset(struct mt7921_dev *dev)
+ {
+- bool ret;
++ mt76_set(dev, 0x70002600, BIT(0));
++ msleep(200);
++ mt76_clear(dev, 0x70002600, BIT(0));
+
+- ret = wait_event_timeout(dev->reset_wait,
+- (READ_ONCE(dev->reset_state) & state),
+- MT7921_RESET_TIMEOUT);
+-
+- WARN(!ret, "Timeout waiting for MCU reset state %x\n", state);
+- return ret;
++ return __mt76_poll_msec(&dev->mt76, MT_WFSYS_SW_RST_B,
++ WFSYS_SW_INIT_DONE, WFSYS_SW_INIT_DONE, 500);
+ }
+
+ static void
+-mt7921_dma_reset(struct mt7921_phy *phy)
++mt7921_dma_reset(struct mt7921_dev *dev)
+ {
+- struct mt7921_dev *dev = phy->dev;
+ int i;
+
+- mt76_clear(dev, MT_WFDMA0_GLO_CFG,
+- MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
++ /* reset */
++ mt76_clear(dev, MT_WFDMA0_RST,
++ MT_WFDMA0_RST_DMASHDL_ALL_RST | MT_WFDMA0_RST_LOGIC_RST);
++
++ mt76_set(dev, MT_WFDMA0_RST,
++ MT_WFDMA0_RST_DMASHDL_ALL_RST | MT_WFDMA0_RST_LOGIC_RST);
+
+- usleep_range(1000, 2000);
++ /* disable WFDMA0 */
++ mt76_clear(dev, MT_WFDMA0_GLO_CFG,
++ MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN |
++ MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN |
++ MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
++ MT_WFDMA0_GLO_CFG_OMIT_RX_INFO |
++ MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
++
++ mt76_poll(dev, MT_WFDMA0_GLO_CFG,
++ MT_WFDMA0_GLO_CFG_TX_DMA_BUSY |
++ MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 1000);
+
+- mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WA], true);
++ /* reset hw queues */
+ for (i = 0; i < __MT_TXQ_MAX; i++)
+- mt76_queue_tx_cleanup(dev, phy->mt76->q_tx[i], true);
++ mt76_queue_reset(dev, dev->mphy.q_tx[i]);
+
+- mt76_for_each_q_rx(&dev->mt76, i) {
+- mt76_queue_rx_reset(dev, i);
+- }
++ for (i = 0; i < __MT_MCUQ_MAX; i++)
++ mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
+
+- /* re-init prefetch settings after reset */
++ mt76_for_each_q_rx(&dev->mt76, i)
++ mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
++
++ /* configure perfetch settings */
+ mt7921_dma_prefetch(dev);
+
++ /* reset dma idx */
++ mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0);
++
++ /* configure delay interrupt */
++ mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0, 0);
++
++ mt76_set(dev, MT_WFDMA0_GLO_CFG,
++ MT_WFDMA0_GLO_CFG_TX_WB_DDONE |
++ MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN |
++ MT_WFDMA0_GLO_CFG_CLK_GAT_DIS |
++ MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
++ MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN |
++ MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
++
+ mt76_set(dev, MT_WFDMA0_GLO_CFG,
+ MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
++
++ mt76_set(dev, 0x54000120, BIT(1));
++
++ /* enable interrupts for TX/RX rings */
++ mt7921_irq_enable(dev,
++ MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_ALL |
++ MT_INT_MCU_CMD);
+ }
+
+ void mt7921_tx_token_put(struct mt7921_dev *dev)
+@@ -1244,71 +1278,125 @@ void mt7921_tx_token_put(struct mt7921_d
+ idr_destroy(&dev->token);
+ }
+
+-/* system error recovery */
+-void mt7921_mac_reset_work(struct work_struct *work)
++static void
++mt7921_vif_connect_iter(void *priv, u8 *mac,
++ struct ieee80211_vif *vif)
+ {
+- struct mt7921_dev *dev;
++ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
++ struct mt7921_dev *dev = mvif->phy->dev;
+
+- dev = container_of(work, struct mt7921_dev, reset_work);
++ ieee80211_disconnect(vif, true);
+
+- if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA))
+- return;
++ mt76_connac_mcu_uni_add_dev(&dev->mphy, vif, &mvif->sta.wcid, true);
++ mt7921_mcu_set_tx(dev, vif);
++}
++
++static int
++mt7921_mac_reset(struct mt7921_dev *dev)
++{
++ int i, err;
+
+- ieee80211_stop_queues(mt76_hw(dev));
++ mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
++
++ mt76_wr(dev, MT_WFDMA0_HOST_INT_ENA, 0);
++ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
+
+- set_bit(MT76_RESET, &dev->mphy.state);
+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
+ wake_up(&dev->mt76.mcu.wait);
+- cancel_delayed_work_sync(&dev->mphy.mac_work);
++ skb_queue_purge(&dev->mt76.mcu.res_q);
+
+- /* lock/unlock all queues to ensure that no tx is pending */
+ mt76_txq_schedule_all(&dev->mphy);
+
+ mt76_worker_disable(&dev->mt76.tx_worker);
+- napi_disable(&dev->mt76.napi[0]);
+- napi_disable(&dev->mt76.napi[1]);
+- napi_disable(&dev->mt76.napi[2]);
++ napi_disable(&dev->mt76.napi[MT_RXQ_MAIN]);
++ napi_disable(&dev->mt76.napi[MT_RXQ_MCU]);
++ napi_disable(&dev->mt76.napi[MT_RXQ_MCU_WA]);
+ napi_disable(&dev->mt76.tx_napi);
+
+- mt7921_mutex_acquire(dev);
+-
+- mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
+-
+ mt7921_tx_token_put(dev);
+ idr_init(&dev->token);
+
+- if (mt7921_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
+- mt7921_dma_reset(&dev->phy);
++ /* clean up hw queues */
++ for (i = 0; i < ARRAY_SIZE(dev->mt76.phy.q_tx); i++)
++ mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
+
+- mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_INIT);
+- mt7921_wait_reset_state(dev, MT_MCU_CMD_RECOVERY_DONE);
+- }
++ for (i = 0; i < ARRAY_SIZE(dev->mt76.q_mcu); i++)
++ mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
+
+- clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+- clear_bit(MT76_RESET, &dev->mphy.state);
++ mt76_for_each_q_rx(&dev->mt76, i)
++ mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]);
++
++ mt7921_wfsys_reset(dev);
++ mt7921_dma_reset(dev);
++
++ mt76_for_each_q_rx(&dev->mt76, i) {
++ mt76_queue_rx_reset(dev, i);
++ napi_enable(&dev->mt76.napi[i]);
++ napi_schedule(&dev->mt76.napi[i]);
++ }
+
+- mt76_worker_enable(&dev->mt76.tx_worker);
+ napi_enable(&dev->mt76.tx_napi);
+ napi_schedule(&dev->mt76.tx_napi);
++ mt76_worker_enable(&dev->mt76.tx_worker);
+
+- napi_enable(&dev->mt76.napi[0]);
+- napi_schedule(&dev->mt76.napi[0]);
++ clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+
+- napi_enable(&dev->mt76.napi[1]);
+- napi_schedule(&dev->mt76.napi[1]);
++ mt76_wr(dev, MT_WFDMA0_HOST_INT_ENA, 0);
++ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
++ mt7921_irq_enable(dev,
++ MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_ALL |
++ MT_INT_MCU_CMD);
+
+- napi_enable(&dev->mt76.napi[2]);
+- napi_schedule(&dev->mt76.napi[2]);
++ err = mt7921_run_firmware(dev);
++ if (err)
++ return err;
+
+- ieee80211_wake_queues(mt76_hw(dev));
++ err = mt7921_mcu_set_eeprom(dev);
++ if (err)
++ return err;
+
+- mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE);
+- mt7921_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE);
++ mt7921_mac_init(dev);
++ return __mt7921_start(&dev->phy);
++}
+
+- mt7921_mutex_release(dev);
++/* system error recovery */
++void mt7921_mac_reset_work(struct work_struct *work)
++{
++ struct ieee80211_hw *hw;
++ struct mt7921_dev *dev;
++ int i;
+
+- ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
+- MT7921_WATCHDOG_TIME);
++ dev = container_of(work, struct mt7921_dev, reset_work);
++ hw = mt76_hw(dev);
++
++ dev_err(dev->mt76.dev, "chip reset\n");
++ ieee80211_stop_queues(hw);
++
++ cancel_delayed_work_sync(&dev->mphy.mac_work);
++ cancel_delayed_work_sync(&dev->pm.ps_work);
++ cancel_work_sync(&dev->pm.wake_work);
++
++ mutex_lock(&dev->mt76.mutex);
++ for (i = 0; i < 10; i++) {
++ if (!mt7921_mac_reset(dev))
++ break;
++ }
++ mutex_unlock(&dev->mt76.mutex);
++
++ if (i == 10)
++ dev_err(dev->mt76.dev, "chip reset failed\n");
++
++ ieee80211_wake_queues(hw);
++ ieee80211_iterate_active_interfaces(hw,
++ IEEE80211_IFACE_ITER_RESUME_ALL,
++ mt7921_vif_connect_iter, 0);
++}
++
++void mt7921_reset(struct mt76_dev *mdev)
++{
++ struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76);
++
++ queue_work(dev->mt76.wq, &dev->reset_work);
+ }
+
+ static void
+@@ -1505,4 +1593,5 @@ void mt7921_coredump_work(struct work_st
+ }
+ dev_coredumpv(dev->mt76.dev, dump, MT76_CONNAC_COREDUMP_SZ,
+ GFP_KERNEL);
++ mt7921_reset(&dev->mt76);
+ }
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+@@ -952,6 +952,7 @@ int mt7921_mcu_init(struct mt7921_dev *d
+ .mcu_skb_send_msg = mt7921_mcu_send_message,
+ .mcu_parse_response = mt7921_mcu_parse_response,
+ .mcu_restart = mt7921_mcu_restart,
++ .mcu_reset = mt7921_reset,
+ };
+
+ dev->mt76.mcu_ops = &mt7921_mcu_ops;
+@@ -1269,6 +1270,7 @@ int mt7921_mcu_drv_pmctrl(struct mt7921_
+
+ if (i == MT7921_DRV_OWN_RETRY_COUNT) {
+ dev_err(dev->mt76.dev, "driver own failed\n");
++ mt7921_reset(&dev->mt76);
+ return -EIO;
+ }
+
+@@ -1295,6 +1297,7 @@ int mt7921_mcu_fw_pmctrl(struct mt7921_d
+
+ if (i == MT7921_DRV_OWN_RETRY_COUNT) {
+ dev_err(dev->mt76.dev, "firmware own failed\n");
++ mt7921_reset(&dev->mt76);
+ return -EIO;
+ }
+
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
+@@ -151,8 +151,6 @@ struct mt7921_dev {
+
+ struct work_struct init_work;
+ struct work_struct reset_work;
+- wait_queue_head_t reset_wait;
+- u32 reset_state;
+
+ struct list_head sta_poll_list;
+ spinlock_t sta_poll_lock;
+@@ -283,6 +281,7 @@ mt7921_l1_rmw(struct mt7921_dev *dev, u3
+ #define mt7921_l1_set(dev, addr, val) mt7921_l1_rmw(dev, addr, 0, val)
+ #define mt7921_l1_clear(dev, addr, val) mt7921_l1_rmw(dev, addr, val, 0)
+
++void mt7921_mac_init(struct mt7921_dev *dev);
+ bool mt7921_mac_wtbl_update(struct mt7921_dev *dev, int idx, u32 mask);
+ void mt7921_mac_reset_counters(struct mt7921_phy *phy);
+ void mt7921_mac_write_txwi(struct mt7921_dev *dev, __le32 *txwi,
+@@ -298,6 +297,7 @@ void mt7921_mac_sta_remove(struct mt76_d
+ struct ieee80211_sta *sta);
+ void mt7921_mac_work(struct work_struct *work);
+ void mt7921_mac_reset_work(struct work_struct *work);
++void mt7921_reset(struct mt76_dev *mdev);
+ int mt7921_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
+ enum mt76_txq_id qid, struct mt76_wcid *wcid,
+ struct ieee80211_sta *sta,
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/regs.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/regs.h
+@@ -418,6 +418,10 @@
+ #define PCIE_LPCR_HOST_CLR_OWN BIT(1)
+ #define PCIE_LPCR_HOST_SET_OWN BIT(0)
+
++#define MT_WFSYS_SW_RST_B 0x18000140
++#define WFSYS_SW_RST_B BIT(0)
++#define WFSYS_SW_INIT_DONE BIT(4)
++
+ #define MT_CONN_ON_MISC 0x7c0600f0
+ #define MT_TOP_MISC2_FW_N9_RDY GENMASK(1, 0)
+
--- /dev/null
+From f92f81d35ac26f8a519866f1b561743fe70e33a5 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 26 Feb 2021 12:17:23 +0100
+Subject: mt76: mt7921: check mcu returned values in mt7921_start
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+commit f92f81d35ac26f8a519866f1b561743fe70e33a5 upstream.
+
+Properly check returned values from mcu utility routines in
+mt7921_start.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Cc: Deren Wu <Deren.Wu@mediatek.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/mediatek/mt76/mt7921/main.c | 19 ++++++++++++++-----
+ 1 file changed, 14 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+@@ -172,22 +172,31 @@ static int mt7921_start(struct ieee80211
+ {
+ struct mt7921_dev *dev = mt7921_hw_dev(hw);
+ struct mt7921_phy *phy = mt7921_hw_phy(hw);
++ int err;
+
+ mt7921_mutex_acquire(dev);
+
+- mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, true, false);
+- mt76_connac_mcu_set_channel_domain(phy->mt76);
++ err = mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, true, false);
++ if (err)
++ goto out;
++
++ err = mt76_connac_mcu_set_channel_domain(phy->mt76);
++ if (err)
++ goto out;
++
++ err = mt7921_mcu_set_chan_info(phy, MCU_EXT_CMD_SET_RX_PATH);
++ if (err)
++ goto out;
+
+- mt7921_mcu_set_chan_info(phy, MCU_EXT_CMD_SET_RX_PATH);
+ mt7921_mac_reset_counters(phy);
+ set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+
+ ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+ MT7921_WATCHDOG_TIME);
+-
++out:
+ mt7921_mutex_release(dev);
+
+- return 0;
++ return err;
+ }
+
+ static void mt7921_stop(struct ieee80211_hw *hw)
--- /dev/null
+From d43b3257621dfe57c71d875afd3f624b9a042fc5 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Wed, 21 Apr 2021 12:28:33 +0200
+Subject: mt76: mt7921: get rid of mcu_reset function pointer
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+commit d43b3257621dfe57c71d875afd3f624b9a042fc5 upstream.
+
+since mcu_reset it used only by mt7921, move the reset callback to
+mt7921_mcu_parse_response routine and get rid of the function pointer.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Link: https://lore.kernel.org/linux-wireless/364293ec8609dd254067d8173c1599526ffd662c.1619000828.git.lorenzo@kernel.org/
+Signed-off-by: Deren Wu <deren.wu@mediatek.com>
+Cc: <stable@vger.kernel.org> # 5.12: f92f81d35ac2 mt76: mt7921: check mcu returned values in mt7921_start
+Cc: <stable@vger.kernel.org> # 5.12: d32464e68ffc mt76: mt7921: introduce mt7921_run_firmware utility routine.
+Cc: <stable@vger.kernel.org> # 5.12: 1f7396acfef4 mt76: mt7921: introduce __mt7921_start utility routine
+Cc: <stable@vger.kernel.org> # 5.12: 3990465db682 mt76: dma: introduce mt76_dma_queue_reset routine
+Cc: <stable@vger.kernel.org> # 5.12: c001df978e4c mt76: dma: export mt76_dma_rx_cleanup routine
+Cc: <stable@vger.kernel.org> # 5.12: 0c1ce9884607 mt76: mt7921: add wifi reset support
+Cc: <stable@vger.kernel.org> # 5.12: e513ae49088b mt76: mt7921: abort uncompleted scan by wifi reset
+Cc: <stable@vger.kernel.org> # 5.12
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+@@ -161,6 +161,8 @@ mt7921_mcu_parse_response(struct mt76_de
+ if (!skb) {
+ dev_err(mdev->dev, "Message %d (seq %d) timeout\n",
+ cmd, seq);
++ mt7921_reset(mdev);
++
+ return -ETIMEDOUT;
+ }
+
+@@ -952,7 +954,6 @@ int mt7921_mcu_init(struct mt7921_dev *d
+ .mcu_skb_send_msg = mt7921_mcu_send_message,
+ .mcu_parse_response = mt7921_mcu_parse_response,
+ .mcu_restart = mt7921_mcu_restart,
+- .mcu_reset = mt7921_reset,
+ };
+
+ dev->mt76.mcu_ops = &mt7921_mcu_ops;
--- /dev/null
+From 1f7396acfef4691b8cf4a3e631fd3f59d779c0f2 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sun, 7 Mar 2021 19:20:48 +0100
+Subject: mt76: mt7921: introduce __mt7921_start utility routine
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+commit 1f7396acfef4691b8cf4a3e631fd3f59d779c0f2 upstream.
+
+This is a preliminary patch to introduce mt7921 chip reset support.
+
+Co-developed-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Cc: Deren Wu <Deren.Wu@mediatek.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/mediatek/mt76/mt7921/main.c | 35 ++++++++++++---------
+ drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h | 1
+ 2 files changed, 22 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+@@ -168,33 +168,40 @@ void mt7921_set_stream_he_caps(struct mt
+ }
+ }
+
+-static int mt7921_start(struct ieee80211_hw *hw)
++int __mt7921_start(struct mt7921_phy *phy)
+ {
+- struct mt7921_dev *dev = mt7921_hw_dev(hw);
+- struct mt7921_phy *phy = mt7921_hw_phy(hw);
++ struct mt76_phy *mphy = phy->mt76;
+ int err;
+
+- mt7921_mutex_acquire(dev);
+-
+- err = mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, true, false);
++ err = mt76_connac_mcu_set_mac_enable(mphy->dev, 0, true, false);
+ if (err)
+- goto out;
++ return err;
+
+- err = mt76_connac_mcu_set_channel_domain(phy->mt76);
++ err = mt76_connac_mcu_set_channel_domain(mphy);
+ if (err)
+- goto out;
++ return err;
+
+ err = mt7921_mcu_set_chan_info(phy, MCU_EXT_CMD_SET_RX_PATH);
+ if (err)
+- goto out;
++ return err;
+
+ mt7921_mac_reset_counters(phy);
+- set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
++ set_bit(MT76_STATE_RUNNING, &mphy->state);
+
+- ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
++ ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
+ MT7921_WATCHDOG_TIME);
+-out:
+- mt7921_mutex_release(dev);
++
++ return 0;
++}
++
++static int mt7921_start(struct ieee80211_hw *hw)
++{
++ struct mt7921_phy *phy = mt7921_hw_phy(hw);
++ int err;
++
++ mt7921_mutex_acquire(phy->dev);
++ err = __mt7921_start(phy);
++ mt7921_mutex_release(phy->dev);
+
+ return err;
+ }
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
+@@ -209,6 +209,7 @@ extern struct pci_driver mt7921_pci_driv
+
+ u32 mt7921_reg_map(struct mt7921_dev *dev, u32 addr);
+
++int __mt7921_start(struct mt7921_phy *phy);
+ int mt7921_register_device(struct mt7921_dev *dev);
+ void mt7921_unregister_device(struct mt7921_dev *dev);
+ int mt7921_eeprom_init(struct mt7921_dev *dev);
--- /dev/null
+From d32464e68ffc9cbec4960cd06f05bf48b3b3703f Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sun, 7 Mar 2021 19:20:47 +0100
+Subject: mt76: mt7921: introduce mt7921_run_firmware utility routine.
+
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+
+commit d32464e68ffc9cbec4960cd06f05bf48b3b3703f upstream.
+
+This is a preliminary patch to introduce chip reset for mt7921 devices.
+
+Co-developed-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Cc: Deren Wu <Deren.Wu@mediatek.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 32 ++++++++++++---------
+ drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h | 1
+ 2 files changed, 20 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+@@ -927,6 +927,24 @@ int mt7921_mcu_fw_log_2_host(struct mt79
+ sizeof(data), false);
+ }
+
++int mt7921_run_firmware(struct mt7921_dev *dev)
++{
++ int err;
++
++ err = mt7921_driver_own(dev);
++ if (err)
++ return err;
++
++ err = mt7921_load_firmware(dev);
++ if (err)
++ return err;
++
++ set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
++ mt7921_mcu_fw_log_2_host(dev, 1);
++
++ return 0;
++}
++
+ int mt7921_mcu_init(struct mt7921_dev *dev)
+ {
+ static const struct mt76_mcu_ops mt7921_mcu_ops = {
+@@ -935,22 +953,10 @@ int mt7921_mcu_init(struct mt7921_dev *d
+ .mcu_parse_response = mt7921_mcu_parse_response,
+ .mcu_restart = mt7921_mcu_restart,
+ };
+- int ret;
+
+ dev->mt76.mcu_ops = &mt7921_mcu_ops;
+
+- ret = mt7921_driver_own(dev);
+- if (ret)
+- return ret;
+-
+- ret = mt7921_load_firmware(dev);
+- if (ret)
+- return ret;
+-
+- set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
+- mt7921_mcu_fw_log_2_host(dev, 1);
+-
+- return 0;
++ return mt7921_run_firmware(dev);
+ }
+
+ void mt7921_mcu_exit(struct mt7921_dev *dev)
+--- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
++++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
+@@ -220,6 +220,7 @@ void mt7921_eeprom_init_sku(struct mt792
+ int mt7921_dma_init(struct mt7921_dev *dev);
+ void mt7921_dma_prefetch(struct mt7921_dev *dev);
+ void mt7921_dma_cleanup(struct mt7921_dev *dev);
++int mt7921_run_firmware(struct mt7921_dev *dev);
+ int mt7921_mcu_init(struct mt7921_dev *dev);
+ int mt7921_mcu_add_bss_info(struct mt7921_phy *phy,
+ struct ieee80211_vif *vif, int enable);
hexagon-fix-build-errors.patch
hexagon-add-target-builtins-to-kernel.patch
hexagon-change-jumps-to-must-extend-in-futex_atomic_.patch
+mt76-mt7921-check-mcu-returned-values-in-mt7921_start.patch
+mt76-mt7921-introduce-mt7921_run_firmware-utility-routine.patch
+mt76-mt7921-introduce-__mt7921_start-utility-routine.patch
+mt76-dma-introduce-mt76_dma_queue_reset-routine.patch
+mt76-dma-export-mt76_dma_rx_cleanup-routine.patch
+mt76-mt7921-add-wifi-reset-support.patch
+mt76-mt7921-abort-uncompleted-scan-by-wifi-reset.patch
+mt76-mt7921-get-rid-of-mcu_reset-function-pointer.patch