#include <linux/mmc/sdio_func.h>
 #include "mt7921.h"
 #include "mac.h"
+#include "../sdio.h"
+
+static void mt7921s_enable_irq(struct mt76_dev *dev)
+{
+       struct mt76_sdio *sdio = &dev->sdio;
+
+       sdio_claim_host(sdio->func);
+       sdio_writel(sdio->func, WHLPCR_INT_EN_SET, MCR_WHLPCR, NULL);
+       sdio_release_host(sdio->func);
+}
+
+static void mt7921s_disable_irq(struct mt76_dev *dev)
+{
+       struct mt76_sdio *sdio = &dev->sdio;
+
+       sdio_claim_host(sdio->func);
+       sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL);
+       sdio_release_host(sdio->func);
+}
+
+static u32 mt7921s_read_whcr(struct mt76_dev *dev)
+{
+       return sdio_readl(dev->sdio.func, MCR_WHCR, NULL);
+}
+
+int mt7921s_wfsys_reset(struct mt7921_dev *dev)
+{
+       struct mt76_sdio *sdio = &dev->mt76.sdio;
+       u32 val, status;
+
+       mt7921s_mcu_drv_pmctrl(dev);
+
+       sdio_claim_host(sdio->func);
+
+       val = sdio_readl(sdio->func, MCR_WHCR, NULL);
+       val &= ~WF_WHOLE_PATH_RSTB;
+       sdio_writel(sdio->func, val, MCR_WHCR, NULL);
+
+       msleep(50);
+
+       val = sdio_readl(sdio->func, MCR_WHCR, NULL);
+       val &= ~WF_SDIO_WF_PATH_RSTB;
+       sdio_writel(sdio->func, val, MCR_WHCR, NULL);
+
+       usleep_range(1000, 2000);
+
+       val = sdio_readl(sdio->func, MCR_WHCR, NULL);
+       val |= WF_WHOLE_PATH_RSTB;
+       sdio_writel(sdio->func, val, MCR_WHCR, NULL);
+
+       readx_poll_timeout(mt7921s_read_whcr, &dev->mt76, status,
+                          status & WF_RST_DONE, 50000, 2000000);
+
+       sdio_release_host(sdio->func);
+
+       /* activate mt7921s again */
+       mt7921s_mcu_fw_pmctrl(dev);
+       mt7921s_mcu_drv_pmctrl(dev);
+
+       return 0;
+}
+
+int mt7921s_init_reset(struct mt7921_dev *dev)
+{
+       set_bit(MT76_MCU_RESET, &dev->mphy.state);
+
+       wake_up(&dev->mt76.mcu.wait);
+       skb_queue_purge(&dev->mt76.mcu.res_q);
+       wait_event_timeout(dev->mt76.sdio.wait,
+                          mt76s_txqs_empty(&dev->mt76), 5 * HZ);
+       mt76_worker_disable(&dev->mt76.sdio.txrx_worker);
+
+       mt7921s_disable_irq(&dev->mt76);
+       mt7921s_wfsys_reset(dev);
+
+       mt76_worker_enable(&dev->mt76.sdio.txrx_worker);
+       clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
+       clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+       mt7921s_enable_irq(&dev->mt76);
+
+       return 0;
+}
+
+int mt7921s_mac_reset(struct mt7921_dev *dev)
+{
+       int err;
+
+       mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
+       mt76_txq_schedule_all(&dev->mphy);
+       mt76_worker_disable(&dev->mt76.tx_worker);
+       set_bit(MT76_RESET, &dev->mphy.state);
+       set_bit(MT76_MCU_RESET, &dev->mphy.state);
+       wake_up(&dev->mt76.mcu.wait);
+       skb_queue_purge(&dev->mt76.mcu.res_q);
+       wait_event_timeout(dev->mt76.sdio.wait,
+                          mt76s_txqs_empty(&dev->mt76), 5 * HZ);
+       mt76_worker_disable(&dev->mt76.sdio.txrx_worker);
+       mt76_worker_disable(&dev->mt76.sdio.status_worker);
+       mt76_worker_disable(&dev->mt76.sdio.net_worker);
+       cancel_work_sync(&dev->mt76.sdio.stat_work);
+
+       mt7921s_disable_irq(&dev->mt76);
+       mt7921s_wfsys_reset(dev);
+
+       mt76_worker_enable(&dev->mt76.sdio.txrx_worker);
+       mt76_worker_enable(&dev->mt76.sdio.status_worker);
+       mt76_worker_enable(&dev->mt76.sdio.net_worker);
+
+       dev->fw_assert = false;
+       clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
+       clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+       mt7921s_enable_irq(&dev->mt76);
+
+       err = mt7921_run_firmware(dev);
+       if (err)
+               goto out;
+
+       err = mt7921_mcu_set_eeprom(dev);
+       if (err)
+               goto out;
+
+       err = mt7921_mac_init(dev);
+       if (err)
+               goto out;
+
+       err = __mt7921_start(&dev->phy);
+out:
+       clear_bit(MT76_RESET, &dev->mphy.state);
+
+       mt76_worker_enable(&dev->mt76.tx_worker);
+
+       return err;
+}
 
 static void
 mt7921s_write_txwi(struct mt7921_dev *dev, struct mt76_wcid *wcid,
 
 
                smp_rmb();
 
+               if (test_bit(MT76_MCU_RESET, &dev->phy.state))
+                       goto next;
+
                if (!test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) {
                        __skb_put_zero(e->skb, 4);
                        err = __mt76s_xmit_queue(dev, e->skb->data,
                ret = mt76s_rx_handler(dev);
                if (ret > 0)
                        nframes += ret;
+
+               if (test_bit(MT76_MCU_RESET, &dev->phy.state)) {
+                       if (!mt76s_txqs_empty(dev))
+                               continue;
+                       else
+                               wake_up(&sdio->wait);
+               }
        } while (nframes > 0);
 
        /* enable interrupt */
        struct mt76_dev *dev = sdio_get_drvdata(func);
        struct mt76_sdio *sdio = &dev->sdio;
 
-       if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state))
+       if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state) ||
+           test_bit(MT76_MCU_RESET, &dev->phy.state))
                return;
 
        mt76_worker_schedule(&sdio->txrx_worker);
 }
 EXPORT_SYMBOL_GPL(mt76s_sdio_irq);
+
+bool mt76s_txqs_empty(struct mt76_dev *dev)
+{
+       struct mt76_queue *q;
+       int i;
+
+       for (i = 0; i <= MT_TXQ_PSD + 1; i++) {
+               if (i <= MT_TXQ_PSD)
+                       q = dev->phy.q_tx[i];
+               else
+                       q = dev->q_mcu[MT_MCUQ_WM];
+
+               if (q->first != q->head)
+                       return false;
+       }
+
+       return true;
+}
+EXPORT_SYMBOL_GPL(mt76s_txqs_empty);