| From 6b8dfe2580a782be754be7bcd44da6ad79dc4231 Mon Sep 17 00:00:00 2001 |
| From: Bo Jiao <Bo.Jiao@mediatek.com> |
| Date: Mon, 13 Feb 2023 18:00:25 +0800 |
| Subject: [PATCH 13/19] wifi: mt76: mt7996: add L0.5 system error recovery |
| support |
| |
| Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com> |
| --- |
| mt7996/debugfs.c | 155 +++++++++++++++++++++++--- |
| mt7996/dma.c | 65 +++++++++++ |
| mt7996/init.c | 9 +- |
| mt7996/mac.c | 285 +++++++++++++++++++++++++++++++++++++++-------- |
| mt7996/main.c | 18 ++- |
| mt7996/mcu.c | 22 ++-- |
| mt7996/mcu.h | 28 +++-- |
| mt7996/mmio.c | 7 +- |
| mt7996/mt7996.h | 17 ++- |
| mt7996/regs.h | 36 +++++- |
| 10 files changed, 542 insertions(+), 100 deletions(-) |
| |
| diff --git a/mt7996/debugfs.c b/mt7996/debugfs.c |
| index 9c5e9ac1..f2c46a50 100644 |
| --- a/mt7996/debugfs.c |
| +++ b/mt7996/debugfs.c |
| @@ -48,12 +48,12 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7996_implicit_txbf_get, |
| |
| /* test knob of system error recovery */ |
| static ssize_t |
| -mt7996_fw_ser_set(struct file *file, const char __user *user_buf, |
| - size_t count, loff_t *ppos) |
| +mt7996_sys_recovery_set(struct file *file, const char __user *user_buf, |
| + size_t count, loff_t *ppos) |
| { |
| struct mt7996_phy *phy = file->private_data; |
| struct mt7996_dev *dev = phy->dev; |
| - u8 band_idx = phy->mt76->band_idx; |
| + bool band = phy->mt76->band_idx; |
| char buf[16]; |
| int ret = 0; |
| u16 val; |
| @@ -73,17 +73,49 @@ mt7996_fw_ser_set(struct file *file, const char __user *user_buf, |
| return -EINVAL; |
| |
| switch (val) { |
| - case SER_SET_RECOVER_L1: |
| - case SER_SET_RECOVER_L2: |
| - case SER_SET_RECOVER_L3_RX_ABORT: |
| - case SER_SET_RECOVER_L3_TX_ABORT: |
| - case SER_SET_RECOVER_L3_TX_DISABLE: |
| - case SER_SET_RECOVER_L3_BF: |
| - ret = mt7996_mcu_set_ser(dev, SER_ENABLE, BIT(val), band_idx); |
| + /* |
| + * 0: grab firmware current SER state. |
| + * 1: trigger & enable system error L1 recovery. |
| + * 2: trigger & enable system error L2 recovery. |
| + * 3: trigger & enable system error L3 rx abort. |
| + * 4: trigger & enable system error L3 tx abort. |
| + * 5: trigger & enable system error L3 tx disable. |
| + * 6: trigger & enable system error L3 bf recovery. |
| + * 7: trigger & enable system error L4 MDP recovery. |
| + * 8: trigger & enable system error full recovery. |
| + * 9: trigger firmware crash. |
| + */ |
| + case UNI_CMD_SER_QUERY: |
| + ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_QUERY, 0, band); |
| + break; |
| + case UNI_CMD_SER_SET_RECOVER_L1: |
| + case UNI_CMD_SER_SET_RECOVER_L2: |
| + case UNI_CMD_SER_SET_RECOVER_L3_RX_ABORT: |
| + case UNI_CMD_SER_SET_RECOVER_L3_TX_ABORT: |
| + case UNI_CMD_SER_SET_RECOVER_L3_TX_DISABLE: |
| + case UNI_CMD_SER_SET_RECOVER_L3_BF: |
| + case UNI_CMD_SER_SET_RECOVER_L4_MDP: |
| + ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_SET, BIT(val), band); |
| + if (ret) |
| + return ret; |
| + |
| + ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, val, band); |
| + break; |
| + |
| + /* enable full chip reset */ |
| + case UNI_CMD_SER_SET_RECOVER_FULL: |
| + mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); |
| if (ret) |
| return ret; |
| |
| - ret = mt7996_mcu_set_ser(dev, SER_RECOVER, val, band_idx); |
| + dev->recovery.state |= MT_MCU_CMD_WDT_MASK; |
| + mt7996_reset(dev); |
| + break; |
| + |
| + /* WARNING: trigger firmware crash */ |
| + case UNI_CMD_SER_SET_SYSTEM_ASSERT: |
| + mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR, BIT(18)); |
| + mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_SOFT_ADDR, BIT(18)); |
| break; |
| default: |
| break; |
| @@ -92,9 +124,101 @@ mt7996_fw_ser_set(struct file *file, const char __user *user_buf, |
| return ret ? ret : count; |
| } |
| |
| -static const struct file_operations mt7996_fw_ser_ops = { |
| - .write = mt7996_fw_ser_set, |
| - /* TODO: ser read */ |
| +static ssize_t |
| +mt7996_sys_recovery_get(struct file *file, char __user *user_buf, |
| + size_t count, loff_t *ppos) |
| +{ |
| + struct mt7996_phy *phy = file->private_data; |
| + struct mt7996_dev *dev = phy->dev; |
| + char *buff; |
| + int desc = 0; |
| + ssize_t ret; |
| + static const size_t bufsz = 1024; |
| + |
| + buff = kmalloc(bufsz, GFP_KERNEL); |
| + if (!buff) |
| + return -ENOMEM; |
| + |
| + /* HELP */ |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "Please echo the correct value ...\n"); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "0: grab firmware transient SER state\n"); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "1: trigger system error L1 recovery\n"); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "2: trigger system error L2 recovery\n"); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "3: trigger system error L3 rx abort\n"); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "4: trigger system error L3 tx abort\n"); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "5: trigger system error L3 tx disable\n"); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "6: trigger system error L3 bf recovery\n"); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "7: trigger system error L4 MDP recovery\n"); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "8: trigger system error full recovery\n"); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "9: trigger firmware crash\n"); |
| + |
| + /* SER statistics */ |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "\nlet's dump firmware SER statistics...\n"); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_STATUS = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_SER_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_PLE_ERR = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_PLE_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_PLE_ERR_1 = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_PLE1_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_PLE_ERR_AMSDU = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_PLE_AMSDU_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_PSE_ERR = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_PSE_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_PSE_ERR_1 = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_PSE1_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_LMAC_WISR6_B0 = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN0_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_LMAC_WISR6_B1 = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN1_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_LMAC_WISR6_B2 = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN2_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_LMAC_WISR7_B0 = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN0_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_LMAC_WISR7_B1 = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN1_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_LMAC_WISR7_B2 = 0x%08x\n", |
| + mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN2_STATS)); |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "::E R , SER_WFDMA_ERR = 0x%08x\n", |
| + mt76_rr(dev, WF_SWDEF_WFDMA_STATUS_ADDR)); |
| + |
| + desc += scnprintf(buff + desc, bufsz - desc, |
| + "\nSYS_RESET_COUNT: WM %d, WA %d\n", |
| + dev->recovery.wm_reset_count, |
| + dev->recovery.wa_reset_count); |
| + |
| + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); |
| + kfree(buff); |
| + return ret; |
| +} |
| + |
| +static const struct file_operations mt7996_sys_recovery_ops = { |
| + .write = mt7996_sys_recovery_set, |
| + .read = mt7996_sys_recovery_get, |
| .open = simple_open, |
| .llseek = default_llseek, |
| }; |
| @@ -674,6 +798,8 @@ int mt7996_init_debugfs(struct mt7996_phy *phy) |
| debugfs_create_file("xmit-queues", 0400, dir, phy, |
| &mt7996_xmit_queues_fops); |
| debugfs_create_file("tx_stats", 0400, dir, phy, &mt7996_tx_stats_fops); |
| + debugfs_create_file("sys_recovery", 0600, dir, phy, |
| + &mt7996_sys_recovery_ops); |
| debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm); |
| debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa); |
| debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin); |
| @@ -684,7 +810,6 @@ int mt7996_init_debugfs(struct mt7996_phy *phy) |
| &fops_implicit_txbf); |
| debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir, |
| mt7996_twt_stats); |
| - debugfs_create_file("fw_ser", 0600, dir, phy, &mt7996_fw_ser_ops); |
| debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval); |
| |
| if (phy->mt76->cap.has_5ghz) { |
| diff --git a/mt7996/dma.c b/mt7996/dma.c |
| index c09fe427..18ea758c 100644 |
| --- a/mt7996/dma.c |
| +++ b/mt7996/dma.c |
| @@ -352,6 +352,71 @@ int mt7996_dma_init(struct mt7996_dev *dev) |
| return 0; |
| } |
| |
| +void mt7996_dma_reset(struct mt7996_dev *dev, bool force) |
| +{ |
| + struct mt76_phy *phy2 = dev->mt76.phys[MT_BAND1]; |
| + struct mt76_phy *phy3 = dev->mt76.phys[MT_BAND2]; |
| + u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); |
| + int i; |
| + |
| + mt76_clear(dev, MT_WFDMA0_GLO_CFG, |
| + MT_WFDMA0_GLO_CFG_TX_DMA_EN | |
| + MT_WFDMA0_GLO_CFG_RX_DMA_EN); |
| + |
| + if (dev->hif2) |
| + mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, |
| + MT_WFDMA0_GLO_CFG_TX_DMA_EN | |
| + MT_WFDMA0_GLO_CFG_RX_DMA_EN); |
| + |
| + usleep_range(1000, 2000); |
| + |
| + for (i = 0; i < __MT_TXQ_MAX; i++) { |
| + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); |
| + if (phy2) |
| + mt76_queue_tx_cleanup(dev, phy2->q_tx[i], true); |
| + if (phy3) |
| + mt76_queue_tx_cleanup(dev, phy3->q_tx[i], true); |
| + } |
| + |
| + for (i = 0; i < __MT_MCUQ_MAX; i++) |
| + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); |
| + |
| + mt76_for_each_q_rx(&dev->mt76, i) |
| + //mt76_queue_rx_reset(dev, i); |
| + mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]); |
| + |
| + mt76_tx_status_check(&dev->mt76, true); |
| + |
| + /* reset wfsys */ |
| + if (force) |
| + mt7996_wfsys_reset(dev); |
| + |
| + mt7996_dma_disable(dev, force); |
| + |
| + /* reset hw queues */ |
| + for (i = 0; i < __MT_TXQ_MAX; i++) { |
| + mt76_queue_reset(dev, dev->mphy.q_tx[i]); |
| + if (phy2) |
| + mt76_queue_reset(dev, phy2->q_tx[i]); |
| + if (phy3) |
| + mt76_queue_reset(dev, phy3->q_tx[i]); |
| + } |
| + |
| + for (i = 0; i < __MT_MCUQ_MAX; i++) |
| + mt76_queue_reset(dev, dev->mt76.q_mcu[i]); |
| + |
| + mt76_for_each_q_rx(&dev->mt76, i) { |
| + mt76_queue_reset(dev, &dev->mt76.q_rx[i]); |
| + } |
| + |
| + mt76_tx_status_check(&dev->mt76, true); |
| + |
| + mt76_for_each_q_rx(&dev->mt76, i) |
| + mt76_queue_rx_reset(dev, i); |
| + |
| + mt7996_dma_enable(dev); |
| +} |
| + |
| void mt7996_dma_cleanup(struct mt7996_dev *dev) |
| { |
| mt7996_dma_disable(dev, true); |
| diff --git a/mt7996/init.c b/mt7996/init.c |
| index 631ada15..ced38ac8 100644 |
| --- a/mt7996/init.c |
| +++ b/mt7996/init.c |
| @@ -278,8 +278,7 @@ static void mt7996_led_set_brightness(struct led_classdev *led_cdev, |
| mt7996_led_set_config(led_cdev, 0xff, 0); |
| } |
| |
| -static void |
| -mt7996_init_txpower(struct mt7996_dev *dev, |
| +void mt7996_init_txpower(struct mt7996_dev *dev, |
| struct ieee80211_supported_band *sband) |
| { |
| int i, nss = hweight8(dev->mphy.antenna_mask); |
| @@ -429,7 +428,7 @@ mt7996_mac_init_band(struct mt7996_dev *dev, u8 band) |
| mt76_rmw(dev, MT_WTBLOFF_RSCR(band), mask, set); |
| } |
| |
| -static void mt7996_mac_init(struct mt7996_dev *dev) |
| +void mt7996_mac_init(struct mt7996_dev *dev) |
| { |
| #define HIF_TXD_V2_1 4 |
| int i; |
| @@ -463,7 +462,7 @@ static void mt7996_mac_init(struct mt7996_dev *dev) |
| mt7996_mac_init_band(dev, i); |
| } |
| |
| -static int mt7996_txbf_init(struct mt7996_dev *dev) |
| +int mt7996_txbf_init(struct mt7996_dev *dev) |
| { |
| int ret; |
| |
| @@ -1075,6 +1074,8 @@ int mt7996_register_device(struct mt7996_dev *dev) |
| if (ret) |
| return ret; |
| |
| + dev->recovery.hw_init_done = true; |
| + |
| return mt7996_init_debugfs(&dev->phy); |
| } |
| |
| diff --git a/mt7996/mac.c b/mt7996/mac.c |
| index 8dc3a621..4c0c8f1e 100644 |
| --- a/mt7996/mac.c |
| +++ b/mt7996/mac.c |
| @@ -1724,7 +1724,7 @@ mt7996_wait_reset_state(struct mt7996_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), |
| MT7996_RESET_TIMEOUT); |
| |
| WARN(!ret, "Timeout waiting for MCU reset state %x\n", state); |
| @@ -1773,68 +1773,208 @@ mt7996_update_beacons(struct mt7996_dev *dev) |
| mt7996_update_vif_beacon, phy3->hw); |
| } |
| |
| -static void |
| -mt7996_dma_reset(struct mt7996_dev *dev) |
| +void mt7996_tx_token_put(struct mt7996_dev *dev) |
| { |
| - struct mt76_phy *phy2 = dev->mt76.phys[MT_BAND1]; |
| - struct mt76_phy *phy3 = dev->mt76.phys[MT_BAND2]; |
| - u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); |
| - int i; |
| + struct mt76_txwi_cache *txwi; |
| + int id; |
| |
| - mt76_clear(dev, MT_WFDMA0_GLO_CFG, |
| - MT_WFDMA0_GLO_CFG_TX_DMA_EN | |
| - MT_WFDMA0_GLO_CFG_RX_DMA_EN); |
| + spin_lock_bh(&dev->mt76.token_lock); |
| + idr_for_each_entry(&dev->mt76.token, txwi, id) { |
| + mt7996_txwi_free(dev, txwi, NULL, NULL); |
| + dev->mt76.token_count--; |
| + } |
| + spin_unlock_bh(&dev->mt76.token_lock); |
| + idr_destroy(&dev->mt76.token); |
| +} |
| |
| - if (dev->hif2) |
| - mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, |
| - MT_WFDMA0_GLO_CFG_TX_DMA_EN | |
| - MT_WFDMA0_GLO_CFG_RX_DMA_EN); |
| |
| - usleep_range(1000, 2000); |
| +static int |
| +mt7996_mac_restart(struct mt7996_dev *dev) |
| +{ |
| + struct mt7996_phy *phy2, *phy3; |
| + struct mt76_dev *mdev = &dev->mt76; |
| + int i, ret; |
| |
| - for (i = 0; i < __MT_TXQ_MAX; i++) { |
| - mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); |
| - if (phy2) |
| - mt76_queue_tx_cleanup(dev, phy2->q_tx[i], true); |
| - if (phy3) |
| - mt76_queue_tx_cleanup(dev, phy3->q_tx[i], true); |
| + phy2 = mt7996_phy2(dev); |
| + phy3 = mt7996_phy3(dev); |
| + |
| + if (dev->hif2) { |
| + mt76_wr(dev, MT_INT1_MASK_CSR, 0x0); |
| + mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0); |
| } |
| |
| - for (i = 0; i < __MT_MCUQ_MAX; i++) |
| - mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); |
| + 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); |
| + } |
| |
| - mt76_for_each_q_rx(&dev->mt76, i) |
| - mt76_queue_rx_reset(dev, i); |
| + set_bit(MT76_RESET, &dev->mphy.state); |
| + set_bit(MT76_MCU_RESET, &dev->mphy.state); |
| + wake_up(&dev->mt76.mcu.wait); |
| + if (phy2) { |
| + set_bit(MT76_RESET, &phy2->mt76->state); |
| + set_bit(MT76_MCU_RESET, &phy2->mt76->state); |
| + } |
| + if (phy3) { |
| + set_bit(MT76_RESET, &phy3->mt76->state); |
| + set_bit(MT76_MCU_RESET, &phy3->mt76->state); |
| + } |
| |
| - mt76_tx_status_check(&dev->mt76, true); |
| + /* lock/unlock all queues to ensure that no tx is pending */ |
| + mt76_txq_schedule_all(&dev->mphy); |
| + if (phy2) |
| + mt76_txq_schedule_all(phy2->mt76); |
| + if (phy3) |
| + mt76_txq_schedule_all(phy3->mt76); |
| + |
| + /* 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); |
| |
| - /* re-init prefetch settings after reset */ |
| - mt7996_dma_prefetch(dev); |
| + /* token reinit */ |
| + mt7996_tx_token_put(dev); |
| + idr_init(&dev->mt76.token); |
| |
| - mt76_set(dev, MT_WFDMA0_GLO_CFG, |
| - MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); |
| + mt7996_dma_reset(dev, true); |
| |
| - if (dev->hif2) |
| - mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, |
| - MT_WFDMA0_GLO_CFG_TX_DMA_EN | |
| - MT_WFDMA0_GLO_CFG_RX_DMA_EN); |
| + 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 = mt7996_mcu_init_firmware(dev); |
| + if (ret) |
| + goto out; |
| + |
| + /* set the necessary init items */ |
| + ret = mt7996_mcu_set_eeprom(dev); |
| + if (ret) |
| + goto out; |
| + |
| + mt7996_mac_init(dev); |
| + mt7996_init_txpower(dev, &dev->mphy.sband_2g.sband); |
| + mt7996_init_txpower(dev, &dev->mphy.sband_5g.sband); |
| + mt7996_init_txpower(dev, &dev->mphy.sband_6g.sband); |
| + ret = mt7996_txbf_init(dev); |
| + |
| + if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) { |
| + ret = mt7996_run(dev->mphy.hw); |
| + if (ret) |
| + goto out; |
| + } |
| + |
| + if (phy2 && test_bit(MT76_STATE_RUNNING, &phy2->mt76->state)) { |
| + ret = mt7996_run(phy2->mt76->hw); |
| + if (ret) |
| + goto out; |
| + } |
| + |
| + if (phy3 && test_bit(MT76_STATE_RUNNING, &phy3->mt76->state)) { |
| + ret = mt7996_run(phy3->mt76->hw); |
| + if (ret) |
| + goto out; |
| + } |
| + |
| +out: |
| + /* reset done */ |
| + clear_bit(MT76_RESET, &dev->mphy.state); |
| + if (phy2) |
| + clear_bit(MT76_RESET, &phy2->mt76->state); |
| + if (phy3) |
| + clear_bit(MT76_RESET, &phy3->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; |
| } |
| |
| -void mt7996_tx_token_put(struct mt7996_dev *dev) |
| +static void |
| +mt7996_mac_full_reset(struct mt7996_dev *dev) |
| { |
| - struct mt76_txwi_cache *txwi; |
| - int id; |
| + struct mt7996_phy *phy2, *phy3; |
| + int i; |
| |
| - spin_lock_bh(&dev->mt76.token_lock); |
| - idr_for_each_entry(&dev->mt76.token, txwi, id) { |
| - mt7996_txwi_free(dev, txwi, NULL, NULL); |
| - dev->mt76.token_count--; |
| + phy2 = mt7996_phy2(dev); |
| + phy3 = mt7996_phy3(dev); |
| + dev->recovery.hw_full_reset = true; |
| + |
| + wake_up(&dev->mt76.mcu.wait); |
| + ieee80211_stop_queues(mt76_hw(dev)); |
| + if (phy2) |
| + ieee80211_stop_queues(phy2->mt76->hw); |
| + if (phy3) |
| + ieee80211_stop_queues(phy3->mt76->hw); |
| + |
| + cancel_delayed_work_sync(&dev->mphy.mac_work); |
| + if (phy2) |
| + cancel_delayed_work_sync(&phy2->mt76->mac_work); |
| + if (phy3) |
| + cancel_delayed_work_sync(&phy3->mt76->mac_work); |
| + |
| + mutex_lock(&dev->mt76.mutex); |
| + for (i = 0; i < 10; i++) { |
| + if (!mt7996_mac_restart(dev)) |
| + break; |
| } |
| - spin_unlock_bh(&dev->mt76.token_lock); |
| - idr_destroy(&dev->mt76.token); |
| + 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 (phy2) |
| + ieee80211_restart_hw(phy2->mt76->hw); |
| + if (phy3) |
| + ieee80211_restart_hw(phy3->mt76->hw); |
| + |
| + ieee80211_wake_queues(mt76_hw(dev)); |
| + if (phy2) |
| + ieee80211_wake_queues(phy2->mt76->hw); |
| + if (phy3) |
| + ieee80211_wake_queues(phy3->mt76->hw); |
| + |
| + dev->recovery.hw_full_reset = false; |
| + ieee80211_queue_delayed_work(mt76_hw(dev), |
| + &dev->mphy.mac_work, |
| + MT7996_WATCHDOG_TIME); |
| + if (phy2) |
| + ieee80211_queue_delayed_work(phy2->mt76->hw, |
| + &phy2->mt76->mac_work, |
| + MT7996_WATCHDOG_TIME); |
| + if (phy3) |
| + ieee80211_queue_delayed_work(phy3->mt76->hw, |
| + &phy3->mt76->mac_work, |
| + MT7996_WATCHDOG_TIME); |
| } |
| |
| -/* system error recovery */ |
| void mt7996_mac_reset_work(struct work_struct *work) |
| { |
| struct mt7996_phy *phy2, *phy3; |
| @@ -1845,9 +1985,36 @@ void mt7996_mac_reset_work(struct work_struct *work) |
| phy2 = mt7996_phy2(dev); |
| phy3 = mt7996_phy3(dev); |
| |
| - 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); |
| + |
| + if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT) |
| + dev->recovery.wa_reset_count++; |
| + else |
| + dev->recovery.wm_reset_count++; |
| + |
| + mt7996_mac_full_reset(dev); |
| + |
| + /* enable mcu irq */ |
| + mt7996_irq_enable(dev, MT_INT_MCU_CMD); |
| + mt7996_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; |
| + } |
| |
| + if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA)) |
| + return; |
| + |
| + dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.", |
| + wiphy_name(dev->mt76.hw->wiphy)); |
| ieee80211_stop_queues(mt76_hw(dev)); |
| if (phy2) |
| ieee80211_stop_queues(phy2->mt76->hw); |
| @@ -1876,7 +2043,7 @@ void mt7996_mac_reset_work(struct work_struct *work) |
| mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED); |
| |
| if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) { |
| - mt7996_dma_reset(dev); |
| + mt7996_dma_reset(dev, false); |
| |
| mt7996_tx_token_put(dev); |
| idr_init(&dev->mt76.token); |
| @@ -1931,6 +2098,32 @@ void mt7996_mac_reset_work(struct work_struct *work) |
| ieee80211_queue_delayed_work(phy3->mt76->hw, |
| &phy3->mt76->mac_work, |
| MT7996_WATCHDOG_TIME); |
| + dev_info(dev->mt76.dev,"\n%s L1 SER recovery completed.", |
| + wiphy_name(dev->mt76.hw->wiphy)); |
| +} |
| + |
| +void mt7996_reset(struct mt7996_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)); |
| + |
| + mt7996_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 mt7996_mac_update_stats(struct mt7996_phy *phy) |
| diff --git a/mt7996/main.c b/mt7996/main.c |
| index d8d578c0..cb0e0d31 100644 |
| --- a/mt7996/main.c |
| +++ b/mt7996/main.c |
| @@ -22,17 +22,13 @@ static bool mt7996_dev_running(struct mt7996_dev *dev) |
| return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state); |
| } |
| |
| -static int mt7996_start(struct ieee80211_hw *hw) |
| +int mt7996_run(struct ieee80211_hw *hw) |
| { |
| struct mt7996_dev *dev = mt7996_hw_dev(hw); |
| struct mt7996_phy *phy = mt7996_hw_phy(hw); |
| bool running; |
| int ret; |
| |
| - flush_work(&dev->init_work); |
| - |
| - mutex_lock(&dev->mt76.mutex); |
| - |
| running = mt7996_dev_running(dev); |
| if (!running) { |
| ret = mt7996_mcu_set_hdr_trans(dev, true); |
| @@ -71,6 +67,18 @@ static int mt7996_start(struct ieee80211_hw *hw) |
| mt7996_mac_reset_counters(phy); |
| |
| out: |
| + return ret; |
| +} |
| + |
| +static int mt7996_start(struct ieee80211_hw *hw) |
| +{ |
| + struct mt7996_dev *dev = mt7996_hw_dev(hw); |
| + int ret; |
| + |
| + flush_work(&dev->init_work); |
| + |
| + mutex_lock(&dev->mt76.mutex); |
| + ret = mt7996_run(hw); |
| mutex_unlock(&dev->mt76.mutex); |
| |
| return ret; |
| diff --git a/mt7996/mcu.c b/mt7996/mcu.c |
| index 0dbe2e01..a949ff76 100644 |
| --- a/mt7996/mcu.c |
| +++ b/mt7996/mcu.c |
| @@ -2629,17 +2629,10 @@ mt7996_mcu_init_rx_airtime(struct mt7996_dev *dev) |
| MCU_WM_UNI_CMD(VOW), true); |
| } |
| |
| -int mt7996_mcu_init(struct mt7996_dev *dev) |
| +int mt7996_mcu_init_firmware(struct mt7996_dev *dev) |
| { |
| - static const struct mt76_mcu_ops mt7996_mcu_ops = { |
| - .headroom = sizeof(struct mt76_connac2_mcu_txd), /* reuse */ |
| - .mcu_skb_send_msg = mt7996_mcu_send_message, |
| - .mcu_parse_response = mt7996_mcu_parse_response, |
| - }; |
| int ret; |
| |
| - dev->mt76.mcu_ops = &mt7996_mcu_ops; |
| - |
| /* force firmware operation mode into normal state, |
| * which should be set before firmware download stage. |
| */ |
| @@ -2680,6 +2673,19 @@ int mt7996_mcu_init(struct mt7996_dev *dev) |
| MCU_WA_PARAM_RED, 0, 0); |
| } |
| |
| +int mt7996_mcu_init(struct mt7996_dev *dev) |
| +{ |
| + static const struct mt76_mcu_ops mt7996_mcu_ops = { |
| + .headroom = sizeof(struct mt76_connac2_mcu_txd), /* reuse */ |
| + .mcu_skb_send_msg = mt7996_mcu_send_message, |
| + .mcu_parse_response = mt7996_mcu_parse_response, |
| + }; |
| + |
| + dev->mt76.mcu_ops = &mt7996_mcu_ops; |
| + |
| + return mt7996_mcu_init_firmware(dev); |
| +} |
| + |
| void mt7996_mcu_exit(struct mt7996_dev *dev) |
| { |
| mt7996_mcu_restart(&dev->mt76); |
| diff --git a/mt7996/mcu.h b/mt7996/mcu.h |
| index ad66a1f8..778deedf 100644 |
| --- a/mt7996/mcu.h |
| +++ b/mt7996/mcu.h |
| @@ -692,23 +692,21 @@ enum { |
| }; |
| |
| enum { |
| - UNI_CMD_SER_QUERY = 0x0, |
| - UNI_CMD_SER_SET = 0x2, |
| - UNI_CMD_SER_TRIGGER = 0x3, |
| -}; |
| - |
| -enum { |
| - SER_QUERY, |
| + UNI_CMD_SER_QUERY, |
| /* recovery */ |
| - SER_SET_RECOVER_L1, |
| - SER_SET_RECOVER_L2, |
| - SER_SET_RECOVER_L3_RX_ABORT, |
| - SER_SET_RECOVER_L3_TX_ABORT, |
| - SER_SET_RECOVER_L3_TX_DISABLE, |
| - SER_SET_RECOVER_L3_BF, |
| + UNI_CMD_SER_SET_RECOVER_L1, |
| + UNI_CMD_SER_SET_RECOVER_L2, |
| + UNI_CMD_SER_SET_RECOVER_L3_RX_ABORT, |
| + UNI_CMD_SER_SET_RECOVER_L3_TX_ABORT, |
| + UNI_CMD_SER_SET_RECOVER_L3_TX_DISABLE, |
| + UNI_CMD_SER_SET_RECOVER_L3_BF, |
| + UNI_CMD_SER_SET_RECOVER_L4_MDP, |
| + UNI_CMD_SER_SET_RECOVER_FULL, |
| + UNI_CMD_SER_SET_SYSTEM_ASSERT, |
| /* action */ |
| - SER_ENABLE = 2, |
| - SER_RECOVER |
| + UNI_CMD_SER_ENABLE = 1, |
| + UNI_CMD_SER_SET, |
| + UNI_CMD_SER_TRIGGER |
| }; |
| |
| enum { |
| diff --git a/mt7996/mmio.c b/mt7996/mmio.c |
| index 6610cc45..0e11f398 100644 |
| --- a/mt7996/mmio.c |
| +++ b/mt7996/mmio.c |
| @@ -289,10 +289,9 @@ static void mt7996_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; |
| - ieee80211_queue_work(mt76_hw(dev), &dev->reset_work); |
| - wake_up(&dev->reset_wait); |
| + if (val & (MT_MCU_CMD_ERROR_MASK | MT_MCU_CMD_WDT_MASK)) { |
| + dev->recovery.state = val; |
| + mt7996_reset(dev); |
| } |
| } |
| } |
| diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h |
| index 25b20fa6..923e6fc9 100644 |
| --- a/mt7996/mt7996.h |
| +++ b/mt7996/mt7996.h |
| @@ -268,7 +268,14 @@ struct mt7996_dev { |
| struct work_struct rc_work; |
| struct work_struct reset_work; |
| wait_queue_head_t reset_wait; |
| - u32 reset_state; |
| + struct { |
| + u32 state; |
| + u32 wa_reset_count; |
| + u32 wm_reset_count; |
| + 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; |
| @@ -401,9 +408,16 @@ int mt7996_eeprom_get_target_power(struct mt7996_dev *dev, |
| struct ieee80211_channel *chan); |
| s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band); |
| int mt7996_dma_init(struct mt7996_dev *dev); |
| +void mt7996_dma_reset(struct mt7996_dev *dev, bool force); |
| void mt7996_dma_prefetch(struct mt7996_dev *dev); |
| void mt7996_dma_cleanup(struct mt7996_dev *dev); |
| +void mt7996_init_txpower(struct mt7996_dev *dev, |
| + struct ieee80211_supported_band *sband); |
| +int mt7996_txbf_init(struct mt7996_dev *dev); |
| +void mt7996_reset(struct mt7996_dev *dev); |
| +int mt7996_run(struct ieee80211_hw *hw); |
| int mt7996_mcu_init(struct mt7996_dev *dev); |
| +int mt7996_mcu_init_firmware(struct mt7996_dev *dev); |
| int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, |
| struct mt7996_vif *mvif, |
| struct mt7996_twt_flow *flow, |
| @@ -496,6 +510,7 @@ static inline void mt7996_irq_disable(struct mt7996_dev *dev, u32 mask) |
| mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); |
| } |
| |
| +void mt7996_mac_init(struct mt7996_dev *dev); |
| u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw); |
| bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask); |
| void mt7996_mac_reset_counters(struct mt7996_phy *phy); |
| diff --git a/mt7996/regs.h b/mt7996/regs.h |
| index 7a28cae3..0775ca58 100644 |
| --- a/mt7996/regs.h |
| +++ b/mt7996/regs.h |
| @@ -317,6 +317,8 @@ enum base_rev { |
| #define MT_WFDMA0_RX_INT_PCIE_SEL MT_WFDMA0(0x154) |
| #define MT_WFDMA0_RX_INT_SEL_RING3 BIT(3) |
| |
| +#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) |
| @@ -444,6 +446,10 @@ enum base_rev { |
| #define MT_MCU_CMD_NORMAL_STATE BIT(5) |
| #define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) |
| |
| +#define MT_MCU_CMD_WA_WDT BIT(31) |
| +#define MT_MCU_CMD_WM_WDT BIT(30) |
| +#define MT_MCU_CMD_WDT_MASK GENMASK(31, 30) |
| + |
| /* l1/l2 remap */ |
| #define MT_HIF_REMAP_L1 0x155024 |
| #define MT_HIF_REMAP_L1_MASK GENMASK(31, 16) |
| @@ -468,8 +474,27 @@ enum base_rev { |
| #define MT_INFRA_MCU_END 0x7c3fffff |
| |
| /* FW MODE SYNC */ |
| -#define MT_SWDEF_MODE 0x9143c |
| +#define MT_SWDEF_BASE 0x00401400 |
| + |
| +#define MT_SWDEF(ofs) (MT_SWDEF_BASE + (ofs)) |
| +#define MT_SWDEF_MODE MT_SWDEF(0x3c) |
| #define MT_SWDEF_NORMAL_MODE 0 |
| +#define MT_SWDEF_ICAP_MODE 1 |
| +#define MT_SWDEF_SPECTRUM_MODE 2 |
| + |
| +#define MT_SWDEF_SER_STATS MT_SWDEF(0x040) |
| +#define MT_SWDEF_PLE_STATS MT_SWDEF(0x044) |
| +#define MT_SWDEF_PLE1_STATS MT_SWDEF(0x048) |
| +#define MT_SWDEF_PLE_AMSDU_STATS MT_SWDEF(0x04C) |
| +#define MT_SWDEF_PSE_STATS MT_SWDEF(0x050) |
| +#define MT_SWDEF_PSE1_STATS MT_SWDEF(0x054) |
| +#define MT_SWDEF_LAMC_WISR6_BN0_STATS MT_SWDEF(0x058) |
| +#define MT_SWDEF_LAMC_WISR6_BN1_STATS MT_SWDEF(0x05C) |
| +#define MT_SWDEF_LAMC_WISR6_BN2_STATS MT_SWDEF(0x060) |
| +#define MT_SWDEF_LAMC_WISR7_BN0_STATS MT_SWDEF(0x064) |
| +#define MT_SWDEF_LAMC_WISR7_BN1_STATS MT_SWDEF(0x068) |
| +#define MT_SWDEF_LAMC_WISR7_BN2_STATS MT_SWDEF(0x06C) |
| +#define WF_SWDEF_WFDMA_STATUS_ADDR MT_SWDEF(0x090) |
| |
| /* LED */ |
| #define MT_LED_TOP_BASE 0x18013000 |
| @@ -506,7 +531,7 @@ enum base_rev { |
| #define MT_TOP_MISC_FW_STATE GENMASK(2, 0) |
| |
| #define MT_HW_REV 0x70010204 |
| -#define MT_WF_SUBSYS_RST 0x70002600 |
| +#define MT_WF_SUBSYS_RST 0x70028600 |
| |
| /* PCIE MAC */ |
| #define MT_PCIE_MAC_BASE 0x74030000 |
| @@ -539,4 +564,11 @@ enum base_rev { |
| #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY BIT(18) |
| #define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR BIT(29) |
| |
| +#define MT_MCU_WM_CIRQ_BASE 0x89010000 |
| +#define MT_MCU_WM_CIRQ(ofs) (MT_MCU_WM_CIRQ_BASE + (ofs)) |
| +#define MT_MCU_WM_CIRQ_IRQ_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x80) |
| +#define MT_MCU_WM_CIRQ_IRQ_SOFT_ADDR MT_MCU_WM_CIRQ(0xc0) |
| +#define MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x108) |
| +#define MT_MCU_WM_CIRQ_EINT_SOFT_ADDR MT_MCU_WM_CIRQ(0x118) |
| + |
| #endif |
| -- |
| 2.39.2 |
| |