mt76: mt7615: implement hardware reset support
authorFelix Fietkau <nbd@nbd.name>
Thu, 30 Jan 2020 16:42:55 +0000 (17:42 +0100)
committerFelix Fietkau <nbd@nbd.name>
Fri, 14 Feb 2020 09:06:08 +0000 (10:06 +0100)
When the firmware detects a problem, it needs the host to stop/reset DMA and
resume it again when the hardware state has been reset.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mcu.c
drivers/net/wireless/mediatek/mt76/mt76.h
drivers/net/wireless/mediatek/mt76/mt7615/dma.c
drivers/net/wireless/mediatek/mt76/mt7615/init.c
drivers/net/wireless/mediatek/mt76/mt7615/mac.c
drivers/net/wireless/mediatek/mt76/mt7615/mmio.c
drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
drivers/net/wireless/mediatek/mt76/mt7615/regs.h

index 2a976688804dad98d67010f9b7d0f78d4755f78b..b0fb0830c9e105d61bee3453cdc477633512daff 100644 (file)
@@ -35,7 +35,8 @@ struct sk_buff *mt76_mcu_get_response(struct mt76_dev *dev,
 
        timeout = expires - jiffies;
        wait_event_timeout(dev->mmio.mcu.wait,
-                          !skb_queue_empty(&dev->mmio.mcu.res_q),
+                          (!skb_queue_empty(&dev->mmio.mcu.res_q) ||
+                           test_bit(MT76_MCU_RESET, &dev->phy.state)),
                           timeout);
        return skb_dequeue(&dev->mmio.mcu.res_q);
 }
index 815d084860023aab36764cdb88cbb86546f846ba..81f7df01307393132cdba64d34d6f602e92fba09 100644 (file)
@@ -274,6 +274,7 @@ enum {
        MT76_STATE_MCU_RUNNING,
        MT76_SCANNING,
        MT76_RESET,
+       MT76_MCU_RESET,
        MT76_REMOVED,
        MT76_READING_STATS,
 };
index aeccb35e14f316727c23ec1ed3f7b513a0ef6d8d..1bc71f5081ceefb1b4a7b8df2f29fcba79f6c21d 100644 (file)
@@ -265,7 +265,8 @@ int mt7615_dma_init(struct mt7615_dev *dev)
                 MT_WPDMA_GLO_CFG_RX_DMA_EN);
 
        /* enable interrupts for TX/RX rings */
-       mt7615_irq_enable(dev, MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_ALL);
+       mt7615_irq_enable(dev, MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_ALL |
+                              MT_INT_MCU_CMD);
 
        if (is_mt7622(&dev->mt76))
                mt7622_dma_sched_init(dev);
index 2f6ad2b244642da1217136ff42225466143b083b..e7f251957fca817d3f3774c14e9f9aba68e162c5 100644 (file)
@@ -447,6 +447,8 @@ int mt7615_register_device(struct mt7615_dev *dev)
        INIT_DELAYED_WORK(&dev->mt76.mac_work, mt7615_mac_work);
        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, mt7615_mac_reset_work);
 
        ret = mt7622_wmac_init(dev);
        if (ret)
index b8ee49fc02ed7a56bb38d05885c6bf2313274ab4..b0d41ec68b7742fe6eddf15166d95cc7f49b5495 100644 (file)
@@ -1782,6 +1782,131 @@ void mt7615_mac_work(struct work_struct *work)
                                     MT7615_WATCHDOG_TIME);
 }
 
+static bool
+mt7615_wait_reset_state(struct mt7615_dev *dev, u32 state)
+{
+       bool ret;
+
+       ret = wait_event_timeout(dev->reset_wait,
+                                (READ_ONCE(dev->reset_state) & state),
+                                MT7615_RESET_TIMEOUT);
+       WARN(!ret, "Timeout waiting for MCU reset state %x\n", state);
+       return ret;
+}
+
+static void
+mt7615_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct ieee80211_hw *hw = priv;
+
+       mt7615_mcu_set_bcn(hw, vif, vif->bss_conf.enable_beacon);
+}
+
+static void
+mt7615_update_beacons(struct mt7615_dev *dev)
+{
+       ieee80211_iterate_active_interfaces(dev->mt76.hw,
+               IEEE80211_IFACE_ITER_RESUME_ALL,
+               mt7615_update_vif_beacon, dev->mt76.hw);
+
+       if (!dev->mt76.phy2)
+               return;
+
+       ieee80211_iterate_active_interfaces(dev->mt76.phy2->hw,
+               IEEE80211_IFACE_ITER_RESUME_ALL,
+               mt7615_update_vif_beacon, dev->mt76.phy2->hw);
+}
+
+static void
+mt7615_dma_reset(struct mt7615_dev *dev)
+{
+       int i;
+
+       mt76_clear(dev, MT_WPDMA_GLO_CFG,
+                  MT_WPDMA_GLO_CFG_RX_DMA_EN | MT_WPDMA_GLO_CFG_TX_DMA_EN |
+                  MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE);
+       usleep_range(1000, 2000);
+
+       for (i = 0; i < __MT_TXQ_MAX; i++)
+               mt76_queue_tx_cleanup(dev, i, true);
+
+       for (i = 0; i < ARRAY_SIZE(dev->mt76.q_rx); i++)
+               mt76_queue_rx_reset(dev, i);
+
+       mt76_set(dev, MT_WPDMA_GLO_CFG,
+                MT_WPDMA_GLO_CFG_RX_DMA_EN | MT_WPDMA_GLO_CFG_TX_DMA_EN |
+                MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE);
+}
+
+void mt7615_mac_reset_work(struct work_struct *work)
+{
+       struct mt7615_dev *dev;
+
+       dev = container_of(work, struct mt7615_dev, reset_work);
+
+       if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_PDMA))
+               return;
+
+       ieee80211_stop_queues(mt76_hw(dev));
+       if (dev->mt76.phy2)
+               ieee80211_stop_queues(dev->mt76.phy2->hw);
+
+       set_bit(MT76_RESET, &dev->mphy.state);
+       set_bit(MT76_MCU_RESET, &dev->mphy.state);
+       wake_up(&dev->mt76.mmio.mcu.wait);
+       cancel_delayed_work_sync(&dev->mt76.mac_work);
+
+       /* lock/unlock all queues to ensure that no tx is pending */
+       mt76_txq_schedule_all(&dev->mphy);
+       if (dev->mt76.phy2)
+               mt76_txq_schedule_all(dev->mt76.phy2);
+
+       tasklet_disable(&dev->mt76.tx_tasklet);
+       napi_disable(&dev->mt76.napi[0]);
+       napi_disable(&dev->mt76.napi[1]);
+       napi_disable(&dev->mt76.tx_napi);
+
+       mutex_lock(&dev->mt76.mutex);
+
+       mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_PDMA_STOPPED);
+
+       if (mt7615_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
+               mt7615_dma_reset(dev);
+
+               mt76_wr(dev, MT_WPDMA_MEM_RNG_ERR, 0);
+
+               mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_PDMA_INIT);
+               mt7615_wait_reset_state(dev, MT_MCU_CMD_RECOVERY_DONE);
+       }
+
+       clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+       clear_bit(MT76_RESET, &dev->mphy.state);
+
+       tasklet_enable(&dev->mt76.tx_tasklet);
+       napi_enable(&dev->mt76.tx_napi);
+       napi_schedule(&dev->mt76.tx_napi);
+
+       napi_enable(&dev->mt76.napi[0]);
+       napi_schedule(&dev->mt76.napi[0]);
+
+       napi_enable(&dev->mt76.napi[1]);
+       napi_schedule(&dev->mt76.napi[1]);
+
+       ieee80211_wake_queues(mt76_hw(dev));
+       if (dev->mt76.phy2)
+               ieee80211_wake_queues(dev->mt76.phy2->hw);
+
+       mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE);
+       mt7615_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE);
+
+       mutex_unlock(&dev->mt76.mutex);
+
+       mt7615_update_beacons(dev);
+
+       ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mt76.mac_work,
+                                    MT7615_WATCHDOG_TIME);
+}
+
 static void mt7615_dfs_stop_radar_detector(struct mt7615_phy *phy)
 {
        struct mt7615_dev *dev = phy->dev;
index fcd8a8b4e8160855389520f934fbefae6c418750..0b445471b6e8f15408e48b7e12d4c716f2e4b67d 100644 (file)
@@ -53,6 +53,16 @@ static irqreturn_t mt7615_irq_handler(int irq, void *dev_instance)
                napi_schedule(&dev->mt76.napi[1]);
        }
 
+       if (intr & MT_INT_MCU_CMD) {
+               u32 val = mt76_rr(dev, MT_MCU_CMD);
+
+               if (val & MT_MCU_CMD_ERROR_MASK) {
+                       dev->reset_state = val;
+                       ieee80211_queue_work(mt76_hw(dev), &dev->reset_work);
+                       wake_up(&dev->reset_wait);
+               }
+       }
+
        return IRQ_HANDLED;
 }
 
index b4748cf079b1a01bfe437463814c658adf20b15f..95973e49c4a90fa7dd997fa3f9e82b9bca140146 100644 (file)
@@ -18,6 +18,7 @@
                                         MT7615_MAX_INTERFACES)
 
 #define MT7615_WATCHDOG_TIME           (HZ / 10)
+#define MT7615_RESET_TIMEOUT           (30 * HZ)
 #define MT7615_RATE_RETRY              2
 
 #define MT7615_TX_RING_SIZE            1024
@@ -156,6 +157,10 @@ struct mt7615_dev {
 
        struct work_struct mcu_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;
 
@@ -352,6 +357,7 @@ void mt7615_mac_tx_free(struct mt7615_dev *dev, struct sk_buff *skb);
 int mt7615_mac_wtbl_set_key(struct mt7615_dev *dev, struct mt76_wcid *wcid,
                            struct ieee80211_key_conf *key,
                            enum set_key_cmd cmd);
+void mt7615_mac_reset_work(struct work_struct *work);
 
 int mt7615_mcu_set_dbdc(struct mt7615_dev *dev);
 int mt7615_mcu_set_eeprom(struct mt7615_dev *dev);
index abecb3bfbc4b25912049f108921db2edfa54ad34..fe68f6b2cbf81c59c6857f4c25fe88790672e60a 100644 (file)
 #define MT_CFG_LPCR_HOST_FW_OWN                BIT(0)
 #define MT_CFG_LPCR_HOST_DRV_OWN       BIT(1)
 
+#define MT_MCU_INT_EVENT               MT_HIF(0x1f8)
+#define MT_MCU_INT_EVENT_PDMA_STOPPED  BIT(0)
+#define MT_MCU_INT_EVENT_PDMA_INIT     BIT(1)
+#define MT_MCU_INT_EVENT_SER_TRIGGER   BIT(2)
+#define MT_MCU_INT_EVENT_RESET_DONE    BIT(3)
+
 #define MT_INT_SOURCE_CSR              MT_HIF(0x200)
 #define MT_INT_MASK_CSR                        MT_HIF(0x204)
 #define MT_DELAY_INT_CFG               MT_HIF(0x210)
@@ -43,6 +49,7 @@
 #define MT_INT_RX_DONE_ALL             GENMASK(1, 0)
 #define MT_INT_TX_DONE_ALL             GENMASK(19, 4)
 #define MT_INT_TX_DONE(_n)             BIT((_n) + 4)
+#define MT_INT_MCU_CMD                 BIT(30)
 
 #define MT_WPDMA_GLO_CFG               MT_HIF(0x208)
 #define MT_WPDMA_GLO_CFG_TX_DMA_EN     BIT(0)
 
 #define MT_WPDMA_RST_IDX               MT_HIF(0x20c)
 
+#define MT_WPDMA_MEM_RNG_ERR           MT_HIF(0x224)
+
+#define MT_MCU_CMD                     MT_HIF(0x234)
+#define MT_MCU_CMD_CLEAR_FW_OWN                BIT(0)
+#define MT_MCU_CMD_STOP_PDMA_FW_RELOAD BIT(1)
+#define MT_MCU_CMD_STOP_PDMA           BIT(2)
+#define MT_MCU_CMD_RESET_DONE          BIT(3)
+#define MT_MCU_CMD_RECOVERY_DONE       BIT(4)
+#define MT_MCU_CMD_NORMAL_STATE                BIT(5)
+#define MT_MCU_CMD_LMAC_ERROR          BIT(24)
+#define MT_MCU_CMD_PSE_ERROR           BIT(25)
+#define MT_MCU_CMD_PLE_ERROR           BIT(26)
+#define MT_MCU_CMD_PDMA_ERROR          BIT(27)
+#define MT_MCU_CMD_PCIE_ERROR          BIT(28)
+#define MT_MCU_CMD_ERROR_MASK          (GENMASK(5, 1) | GENMASK(28, 24))
+
 #define MT_TX_RING_BASE                        MT_HIF(0x300)
 #define MT_RX_RING_BASE                        MT_HIF(0x400)