wifi: mt76: mt7915: enable full system reset support
authorBo Jiao <bo.jiao@mediatek.com>
Wed, 9 Nov 2022 20:36:32 +0000 (04:36 +0800)
committerFelix Fietkau <nbd@nbd.name>
Thu, 1 Dec 2022 16:29:13 +0000 (17:29 +0100)
Add mt7915_reset() and refactor mt7915_mac_reset_work() to support
full system recovery.

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

index a042e01542117c35f80d469a5ed137ade9df10cd..51593f380f847c10ee380fd00baad521e5326eaa 100644 (file)
@@ -262,9 +262,8 @@ static void mt7915_led_set_brightness(struct led_classdev *led_cdev,
                mt7915_led_set_config(led_cdev, 0xff, 0);
 }
 
-static void
-mt7915_init_txpower(struct mt7915_dev *dev,
-                   struct ieee80211_supported_band *sband)
+void mt7915_init_txpower(struct mt7915_dev *dev,
+                        struct ieee80211_supported_band *sband)
 {
        int i, n_chains = hweight8(dev->mphy.antenna_mask);
        int nss_delta = mt76_tx_power_nss_delta(n_chains);
@@ -470,7 +469,7 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band)
        mt76_rmw(dev, MT_WTBLOFF_TOP_RSCR(band), mask, set);
 }
 
-static void mt7915_mac_init(struct mt7915_dev *dev)
+void mt7915_mac_init(struct mt7915_dev *dev)
 {
        int i;
        u32 rx_len = is_mt7915(&dev->mt76) ? 0x400 : 0x680;
@@ -500,7 +499,7 @@ static void mt7915_mac_init(struct mt7915_dev *dev)
        }
 }
 
-static int mt7915_txbf_init(struct mt7915_dev *dev)
+int mt7915_txbf_init(struct mt7915_dev *dev)
 {
        int ret;
 
@@ -1141,6 +1140,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
                        goto unreg_thermal;
        }
 
+       dev->recovery.hw_init_done = true;
+
        mt7915_init_debugfs(&dev->phy);
 
        return 0;
index 662671621cf0b91f11741f3e4a42d22c5875deb4..383b260eb0aac4fb4bd4d047c95e8cfc95ebc0db 100644 (file)
@@ -1291,7 +1291,7 @@ mt7915_wait_reset_state(struct mt7915_dev *dev, u32 state)
        bool ret;
 
        ret = wait_event_timeout(dev->reset_wait,
-                                (READ_ONCE(dev->reset_state) & state),
+                                (READ_ONCE(dev->recovery.state) & state),
                                 MT7915_RESET_TIMEOUT);
 
        WARN(!ret, "Timeout waiting for MCU reset state %x\n", state);
@@ -1346,6 +1346,168 @@ void mt7915_tx_token_put(struct mt7915_dev *dev)
        idr_destroy(&dev->mt76.token);
 }
 
+static int
+mt7915_mac_restart(struct mt7915_dev *dev)
+{
+       struct mt7915_phy *phy2;
+       struct mt76_phy *ext_phy;
+       struct mt76_dev *mdev = &dev->mt76;
+       int i, ret;
+
+       ext_phy = dev->mt76.phys[MT_BAND1];
+       phy2 = ext_phy ? ext_phy->priv : NULL;
+
+       if (dev->hif2) {
+               mt76_wr(dev, MT_INT1_MASK_CSR, 0x0);
+               mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0);
+       }
+
+       if (dev_is_pci(mdev->dev)) {
+               mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
+               if (dev->hif2)
+                       mt76_wr(dev, MT_PCIE1_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);
+       if (ext_phy) {
+               set_bit(MT76_RESET, &ext_phy->state);
+               set_bit(MT76_MCU_RESET, &ext_phy->state);
+       }
+
+       /* lock/unlock all queues to ensure that no tx is pending */
+       mt76_txq_schedule_all(&dev->mphy);
+       if (ext_phy)
+               mt76_txq_schedule_all(ext_phy);
+
+       /* disable all tx/rx napi */
+       mt76_worker_disable(&dev->mt76.tx_worker);
+       mt76_for_each_q_rx(mdev, i) {
+               if (mdev->q_rx[i].ndesc)
+                       napi_disable(&dev->mt76.napi[i]);
+       }
+       napi_disable(&dev->mt76.tx_napi);
+
+       /* token reinit */
+       mt7915_tx_token_put(dev);
+       idr_init(&dev->mt76.token);
+
+       mt7915_dma_reset(dev, true);
+
+       local_bh_disable();
+       mt76_for_each_q_rx(mdev, i) {
+               if (mdev->q_rx[i].ndesc) {
+                       napi_enable(&dev->mt76.napi[i]);
+                       napi_schedule(&dev->mt76.napi[i]);
+               }
+       }
+       local_bh_enable();
+       clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+       clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
+
+       mt76_wr(dev, MT_INT_MASK_CSR, dev->mt76.mmio.irqmask);
+       mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
+
+       if (dev->hif2) {
+               mt76_wr(dev, MT_INT1_MASK_CSR, dev->mt76.mmio.irqmask);
+               mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0);
+       }
+       if (dev_is_pci(mdev->dev)) {
+               mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
+               if (dev->hif2)
+                       mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff);
+       }
+
+       /* load firmware */
+       ret = mt7915_mcu_init_firmware(dev);
+       if (ret)
+               goto out;
+
+       /* set the necessary init items */
+       ret = mt7915_mcu_set_eeprom(dev);
+       if (ret)
+               goto out;
+
+       mt7915_mac_init(dev);
+       mt7915_init_txpower(dev, &dev->mphy.sband_2g.sband);
+       mt7915_init_txpower(dev, &dev->mphy.sband_5g.sband);
+       ret = mt7915_txbf_init(dev);
+
+       if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) {
+               ret = mt7915_run(dev->mphy.hw);
+               if (ret)
+                       goto out;
+       }
+
+       if (ext_phy && test_bit(MT76_STATE_RUNNING, &ext_phy->state)) {
+               ret = mt7915_run(ext_phy->hw);
+               if (ret)
+                       goto out;
+       }
+
+out:
+       /* reset done */
+       clear_bit(MT76_RESET, &dev->mphy.state);
+       if (phy2)
+               clear_bit(MT76_RESET, &phy2->mt76->state);
+
+       local_bh_disable();
+       napi_enable(&dev->mt76.tx_napi);
+       napi_schedule(&dev->mt76.tx_napi);
+       local_bh_enable();
+
+       mt76_worker_enable(&dev->mt76.tx_worker);
+
+       return ret;
+}
+
+static void
+mt7915_mac_full_reset(struct mt7915_dev *dev)
+{
+       struct mt76_phy *ext_phy;
+       int i;
+
+       ext_phy = dev->mt76.phys[MT_BAND1];
+
+       dev->recovery.hw_full_reset = true;
+
+       wake_up(&dev->mt76.mcu.wait);
+       ieee80211_stop_queues(mt76_hw(dev));
+       if (ext_phy)
+               ieee80211_stop_queues(ext_phy->hw);
+
+       cancel_delayed_work_sync(&dev->mphy.mac_work);
+       if (ext_phy)
+               cancel_delayed_work_sync(&ext_phy->mac_work);
+
+       mutex_lock(&dev->mt76.mutex);
+       for (i = 0; i < 10; i++) {
+               if (!mt7915_mac_restart(dev))
+                       break;
+       }
+       mutex_unlock(&dev->mt76.mutex);
+
+       if (i == 10)
+               dev_err(dev->mt76.dev, "chip full reset failed\n");
+
+       ieee80211_restart_hw(mt76_hw(dev));
+       if (ext_phy)
+               ieee80211_restart_hw(ext_phy->hw);
+
+       ieee80211_wake_queues(mt76_hw(dev));
+       if (ext_phy)
+               ieee80211_wake_queues(ext_phy->hw);
+
+       dev->recovery.hw_full_reset = false;
+       ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
+                                    MT7915_WATCHDOG_TIME);
+       if (ext_phy)
+               ieee80211_queue_delayed_work(ext_phy->hw,
+                                            &ext_phy->mac_work,
+                                            MT7915_WATCHDOG_TIME);
+}
+
 /* system error recovery */
 void mt7915_mac_reset_work(struct work_struct *work)
 {
@@ -1358,7 +1520,28 @@ void mt7915_mac_reset_work(struct work_struct *work)
        ext_phy = dev->mt76.phys[MT_BAND1];
        phy2 = ext_phy ? ext_phy->priv : NULL;
 
-       if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA))
+       /* chip full reset */
+       if (dev->recovery.restart) {
+               /* disable WA/WM WDT */
+               mt76_clear(dev, MT_WFDMA0_MCU_HOST_INT_ENA,
+                          MT_MCU_CMD_WDT_MASK);
+
+               mt7915_mac_full_reset(dev);
+
+               /* enable mcu irq */
+               mt7915_irq_enable(dev, MT_INT_MCU_CMD);
+               mt7915_irq_disable(dev, 0);
+
+               /* enable WA/WM WDT */
+               mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK);
+
+               dev->recovery.state = MT_MCU_CMD_NORMAL_STATE;
+               dev->recovery.restart = false;
+               return;
+       }
+
+       /* chip partial reset */
+       if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
                return;
 
        ieee80211_stop_queues(mt76_hw(dev));
@@ -1432,6 +1615,30 @@ void mt7915_mac_reset_work(struct work_struct *work)
                                             MT7915_WATCHDOG_TIME);
 }
 
+void mt7915_reset(struct mt7915_dev *dev)
+{
+       if (!dev->recovery.hw_init_done)
+               return;
+
+       if (dev->recovery.hw_full_reset)
+               return;
+
+       /* wm/wa exception: do full recovery */
+       if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK) {
+               dev->recovery.restart = true;
+               dev_info(dev->mt76.dev,
+                        "%s indicated firmware crash, attempting recovery\n",
+                        wiphy_name(dev->mt76.hw->wiphy));
+
+               mt7915_irq_disable(dev, MT_INT_MCU_CMD);
+               queue_work(dev->mt76.wq, &dev->reset_work);
+               return;
+       }
+
+       queue_work(dev->mt76.wq, &dev->reset_work);
+       wake_up(&dev->reset_wait);
+}
+
 void mt7915_mac_update_stats(struct mt7915_phy *phy)
 {
        struct mt7915_dev *dev = phy->dev;
index 820c7132bf3ff1e6638b433a39ccb5106963d124..3933f4f2d71d2e83e71b6b0615dafe5dbf5a0a52 100644 (file)
@@ -20,17 +20,13 @@ static bool mt7915_dev_running(struct mt7915_dev *dev)
        return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
 }
 
-static int mt7915_start(struct ieee80211_hw *hw)
+int mt7915_run(struct ieee80211_hw *hw)
 {
        struct mt7915_dev *dev = mt7915_hw_dev(hw);
        struct mt7915_phy *phy = mt7915_hw_phy(hw);
        bool running;
        int ret;
 
-       flush_work(&dev->init_work);
-
-       mutex_lock(&dev->mt76.mutex);
-
        running = mt7915_dev_running(dev);
 
        if (!running) {
@@ -80,6 +76,18 @@ static int mt7915_start(struct ieee80211_hw *hw)
                mt7915_mac_reset_counters(phy);
 
 out:
+       return ret;
+}
+
+static int mt7915_start(struct ieee80211_hw *hw)
+{
+       struct mt7915_dev *dev = mt7915_hw_dev(hw);
+       int ret;
+
+       flush_work(&dev->init_work);
+
+       mutex_lock(&dev->mt76.mutex);
+       ret = mt7915_run(hw);
        mutex_unlock(&dev->mt76.mutex);
 
        return ret;
index 4000fcd7132a7197f90db6c9749c9c1743860ac9..17e13fd0ede9b53d3446c0b5ddd818dc6977be96 100644 (file)
@@ -2249,18 +2249,10 @@ mt7915_mcu_init_rx_airtime(struct mt7915_dev *dev)
                                 sizeof(req), true);
 }
 
-int mt7915_mcu_init(struct mt7915_dev *dev)
+int mt7915_mcu_init_firmware(struct mt7915_dev *dev)
 {
-       static const struct mt76_mcu_ops mt7915_mcu_ops = {
-               .headroom = sizeof(struct mt76_connac2_mcu_txd),
-               .mcu_skb_send_msg = mt7915_mcu_send_message,
-               .mcu_parse_response = mt7915_mcu_parse_response,
-               .mcu_restart = mt76_connac_mcu_restart,
-       };
        int ret;
 
-       dev->mt76.mcu_ops = &mt7915_mcu_ops;
-
        /* force firmware operation mode into normal state,
         * which should be set before firmware download stage.
         */
@@ -2309,6 +2301,20 @@ int mt7915_mcu_init(struct mt7915_dev *dev)
                                 MCU_WA_PARAM_RED, 0, 0);
 }
 
+int mt7915_mcu_init(struct mt7915_dev *dev)
+{
+       static const struct mt76_mcu_ops mt7915_mcu_ops = {
+               .headroom = sizeof(struct mt76_connac2_mcu_txd),
+               .mcu_skb_send_msg = mt7915_mcu_send_message,
+               .mcu_parse_response = mt7915_mcu_parse_response,
+               .mcu_restart = mt76_connac_mcu_restart,
+       };
+
+       dev->mt76.mcu_ops = &mt7915_mcu_ops;
+
+       return mt7915_mcu_init_firmware(dev);
+}
+
 void mt7915_mcu_exit(struct mt7915_dev *dev)
 {
        __mt76_mcu_restart(&dev->mt76);
index be1b8ea711c7a35a92a918fec896c0659d02eca1..032af46289af9a055b054cdac47324315bd38aa0 100644 (file)
@@ -746,10 +746,9 @@ static void mt7915_irq_tasklet(struct tasklet_struct *t)
                u32 val = mt76_rr(dev, MT_MCU_CMD);
 
                mt76_wr(dev, MT_MCU_CMD, val);
-               if (val & MT_MCU_CMD_ERROR_MASK) {
-                       dev->reset_state = val;
-                       queue_work(dev->mt76.wq, &dev->reset_work);
-                       wake_up(&dev->reset_wait);
+               if (val & (MT_MCU_CMD_ERROR_MASK | MT_MCU_CMD_WDT_MASK)) {
+                       dev->recovery.state = val;
+                       mt7915_reset(dev);
                }
        }
 }
index ecbab762b6edb925bbc98528bc82187692e7fcc8..fce1fde72116a2272140d8bc36b17813b2bf290a 100644 (file)
@@ -307,7 +307,13 @@ struct mt7915_dev {
        struct work_struct rc_work;
        struct work_struct reset_work;
        wait_queue_head_t reset_wait;
-       u32 reset_state;
+
+       struct {
+               u32 state;
+               bool hw_full_reset:1;
+               bool hw_init_done:1;
+               bool restart:1;
+       } recovery;
 
        struct list_head sta_rc_list;
        struct list_head sta_poll_list;
@@ -448,7 +454,13 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2);
 void mt7915_dma_prefetch(struct mt7915_dev *dev);
 void mt7915_dma_cleanup(struct mt7915_dev *dev);
 int mt7915_dma_reset(struct mt7915_dev *dev, bool force);
+int mt7915_txbf_init(struct mt7915_dev *dev);
+void mt7915_init_txpower(struct mt7915_dev *dev,
+                        struct ieee80211_supported_band *sband);
+void mt7915_reset(struct mt7915_dev *dev);
+int mt7915_run(struct ieee80211_hw *hw);
 int mt7915_mcu_init(struct mt7915_dev *dev);
+int mt7915_mcu_init_firmware(struct mt7915_dev *dev);
 int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev,
                               struct mt7915_vif *mvif,
                               struct mt7915_twt_flow *flow,
@@ -548,6 +560,7 @@ static inline void mt7915_irq_disable(struct mt7915_dev *dev, u32 mask)
                mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0);
 }
 
+void mt7915_mac_init(struct mt7915_dev *dev);
 u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid, u8 dw);
 bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask);
 void mt7915_mac_reset_counters(struct mt7915_phy *phy);
index 9924271d8e36eb78af52725d251f9eb3c6dbe88d..16214cee5d93c575b6c68be8711c0e92a1856fa6 100644 (file)
@@ -563,6 +563,8 @@ enum offs_rev {
 #define MT_WFDMA0_BUSY_ENA_TX_FIFO1    BIT(1)
 #define MT_WFDMA0_BUSY_ENA_RX_FIFO     BIT(2)
 
+#define MT_WFDMA0_MCU_HOST_INT_ENA     MT_WFDMA0(0x1f4)
+
 #define MT_WFDMA0_GLO_CFG              MT_WFDMA0(0x208)
 #define MT_WFDMA0_GLO_CFG_TX_DMA_EN    BIT(0)
 #define MT_WFDMA0_GLO_CFG_RX_DMA_EN    BIT(2)
@@ -731,6 +733,8 @@ enum offs_rev {
 #define MT_MCU_CMD_NORMAL_STATE                BIT(5)
 #define MT_MCU_CMD_ERROR_MASK          GENMASK(5, 1)
 
+#define MT_MCU_CMD_WDT_MASK            GENMASK(31, 30)
+
 /* TOP RGU */
 #define MT_TOP_RGU_BASE                        0x18000000
 #define MT_TOP_PWR_CTRL                        (MT_TOP_RGU_BASE + (0x0))