* [PATCH v3 1/4] wifi: mt76: mt7915: rework mt7915_dma_reset()
@ 2022-11-09 20:36 Ryder Lee
2022-11-09 20:36 ` [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support Ryder Lee
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Ryder Lee @ 2022-11-09 20:36 UTC (permalink / raw)
To: Felix Fietkau, linux-wireless
Cc: Lorenzo Bianconi, Shayne Chen, Evelyn Tsai, linux-mediatek,
Bo Jiao, Ryder Lee, Bo Jiao
From: Bo Jiao <bo.jiao@mediatek.com>
Reuse mt7915_dma_disable() to reduce duplicated code.
This is a preliminary patch to enable full system reset.
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>
---
change since v3 - fix indentation
---
.../net/wireless/mediatek/mt76/mt7915/dma.c | 110 ++++++++++++++----
.../net/wireless/mediatek/mt76/mt7915/mac.c | 69 +----------
.../wireless/mediatek/mt76/mt7915/mt7915.h | 1 +
3 files changed, 91 insertions(+), 89 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
index e4fa240834d8..38360f940747 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
@@ -50,23 +50,37 @@ static void mt7915_dma_config(struct mt7915_dev *dev)
#define TXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__TXQ(q), (wfdma), (int), (id))
if (is_mt7915(&dev->mt76)) {
- RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, MT7915_RXQ_BAND0);
- RXQ_CONFIG(MT_RXQ_MCU, WFDMA1, MT_INT_RX_DONE_WM, MT7915_RXQ_MCU_WM);
- RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA1, MT_INT_RX_DONE_WA, MT7915_RXQ_MCU_WA);
- RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7915_RXQ_BAND1);
- RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA1, MT_INT_RX_DONE_WA_EXT, MT7915_RXQ_MCU_WA_EXT);
- RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA1, MT_INT_RX_DONE_WA_MAIN, MT7915_RXQ_MCU_WA);
+ RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0,
+ MT7915_RXQ_BAND0);
+ RXQ_CONFIG(MT_RXQ_MCU, WFDMA1, MT_INT_RX_DONE_WM,
+ MT7915_RXQ_MCU_WM);
+ RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA1, MT_INT_RX_DONE_WA,
+ MT7915_RXQ_MCU_WA);
+ RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1,
+ MT7915_RXQ_BAND1);
+ RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA1, MT_INT_RX_DONE_WA_EXT,
+ MT7915_RXQ_MCU_WA_EXT);
+ RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA1, MT_INT_RX_DONE_WA_MAIN,
+ MT7915_RXQ_MCU_WA);
TXQ_CONFIG(0, WFDMA1, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0);
TXQ_CONFIG(1, WFDMA1, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1);
- MCUQ_CONFIG(MT_MCUQ_WM, WFDMA1, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM);
- MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA, MT7915_TXQ_MCU_WA);
- MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL);
+ MCUQ_CONFIG(MT_MCUQ_WM, WFDMA1, MT_INT_TX_DONE_MCU_WM,
+ MT7915_TXQ_MCU_WM);
+ MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA,
+ MT7915_TXQ_MCU_WA);
+ MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL,
+ MT7915_TXQ_FWDL);
} else {
- RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7916_RXQ_MCU_WM);
- RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916, MT7916_RXQ_MCU_WA_EXT);
- MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM);
- MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916, MT7915_TXQ_MCU_WA);
- MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL);
+ RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM,
+ MT7916_RXQ_MCU_WM);
+ RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916,
+ MT7916_RXQ_MCU_WA_EXT);
+ MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM,
+ MT7915_TXQ_MCU_WM);
+ MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916,
+ MT7915_TXQ_MCU_WA);
+ MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL,
+ MT7915_TXQ_FWDL);
if (is_mt7916(&dev->mt76) && mtk_wed_device_active(&dev->mt76.mmio.wed)) {
RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_WED_RX_DONE_BAND0_MT7916,
@@ -77,16 +91,23 @@ static void mt7915_dma_config(struct mt7915_dev *dev)
MT7916_RXQ_BAND1);
RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_WED_RX_DONE_WA_MAIN_MT7916,
MT7916_RXQ_MCU_WA_MAIN);
- TXQ_CONFIG(0, WFDMA0, MT_INT_WED_TX_DONE_BAND0, MT7915_TXQ_BAND0);
- TXQ_CONFIG(1, WFDMA0, MT_INT_WED_TX_DONE_BAND1, MT7915_TXQ_BAND1);
+ TXQ_CONFIG(0, WFDMA0, MT_INT_WED_TX_DONE_BAND0,
+ MT7915_TXQ_BAND0);
+ TXQ_CONFIG(1, WFDMA0, MT_INT_WED_TX_DONE_BAND1,
+ MT7915_TXQ_BAND1);
} else {
- RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, MT7916_RXQ_BAND0);
- RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7916_RXQ_MCU_WA);
- RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, MT7916_RXQ_BAND1);
+ RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916,
+ MT7916_RXQ_BAND0);
+ RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA,
+ MT7916_RXQ_MCU_WA);
+ RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916,
+ MT7916_RXQ_BAND1);
RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN_MT7916,
MT7916_RXQ_MCU_WA_MAIN);
- TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0);
- TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1);
+ TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0,
+ MT7915_TXQ_BAND0);
+ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1,
+ MT7915_TXQ_BAND1);
}
}
}
@@ -514,6 +535,53 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
return 0;
}
+int mt7915_dma_reset(struct mt7915_dev *dev, bool force)
+{
+ struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1];
+ int i;
+
+ /* 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);
+ if (mphy_ext)
+ mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[i], true);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dev->mt76.q_mcu); i++)
+ mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
+
+ mt76_for_each_q_rx(&dev->mt76, i)
+ mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]);
+
+ /* reset wfsys */
+ if (force)
+ mt7915_wfsys_reset(dev);
+
+ mt7915_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 (mphy_ext)
+ mt76_queue_reset(dev, mphy_ext->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);
+
+ mt7915_dma_enable(dev);
+
+ mt76_for_each_q_rx(&dev->mt76, i)
+ mt76_queue_rx_reset(dev, i);
+
+ return 0;
+}
+
void mt7915_dma_cleanup(struct mt7915_dev *dev)
{
mt7915_dma_disable(dev, true);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index 324f0f58572b..3b1259f14de6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -1306,73 +1306,6 @@ mt7915_update_beacons(struct mt7915_dev *dev)
mt7915_update_vif_beacon, mphy_ext->hw);
}
-static void
-mt7915_dma_reset(struct mt7915_dev *dev)
-{
- struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1];
- 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 (is_mt7915(&dev->mt76))
- mt76_clear(dev, MT_WFDMA1_GLO_CFG,
- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
- MT_WFDMA1_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);
-
- if (is_mt7915(&dev->mt76))
- mt76_clear(dev, MT_WFDMA1_GLO_CFG + hif1_ofs,
- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
- MT_WFDMA1_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 (mphy_ext)
- mt76_queue_tx_cleanup(dev, mphy_ext->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_tx_status_check(&dev->mt76, true);
-
- /* re-init prefetch settings after reset */
- mt7915_dma_prefetch(dev);
-
- mt76_set(dev, MT_WFDMA0_GLO_CFG,
- MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
- if (is_mt7915(&dev->mt76))
- mt76_set(dev, MT_WFDMA1_GLO_CFG,
- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
- MT_WFDMA1_GLO_CFG_RX_DMA_EN |
- MT_WFDMA1_GLO_CFG_OMIT_TX_INFO |
- MT_WFDMA1_GLO_CFG_OMIT_RX_INFO);
- 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);
-
- if (is_mt7915(&dev->mt76))
- mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs,
- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
- MT_WFDMA1_GLO_CFG_RX_DMA_EN |
- MT_WFDMA1_GLO_CFG_OMIT_TX_INFO |
- MT_WFDMA1_GLO_CFG_OMIT_RX_INFO);
- }
-}
-
void mt7915_tx_token_put(struct mt7915_dev *dev)
{
struct mt76_txwi_cache *txwi;
@@ -1424,7 +1357,7 @@ void mt7915_mac_reset_work(struct work_struct *work)
mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
if (mt7915_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
- mt7915_dma_reset(dev);
+ mt7915_dma_reset(dev, false);
mt7915_tx_token_put(dev);
idr_init(&dev->mt76.token);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index fe6a6d3b0a32..5af26e60e902 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -443,6 +443,7 @@ s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band);
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_mcu_init(struct mt7915_dev *dev);
int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev,
struct mt7915_vif *mvif,
--
2.36.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support 2022-11-09 20:36 [PATCH v3 1/4] wifi: mt76: mt7915: rework mt7915_dma_reset() Ryder Lee @ 2022-11-09 20:36 ` Ryder Lee 2022-11-12 12:18 ` Lorenzo Bianconi 2022-11-09 20:36 ` [PATCH v3 3/4] wifi: mt76: mt7915: add full system reset into debugfs Ryder Lee 2022-11-09 20:36 ` [PATCH v3 4/4] wifi: mt76: mt7915: enable coredump support Ryder Lee 2 siblings, 1 reply; 8+ messages in thread From: Ryder Lee @ 2022-11-09 20:36 UTC (permalink / raw) To: Felix Fietkau, linux-wireless Cc: Lorenzo Bianconi, Shayne Chen, Evelyn Tsai, linux-mediatek, Bo Jiao, Ryder Lee From: Bo Jiao <bo.jiao@mediatek.com> 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> --- change since v2 - change dev_info to make it easier to see that firmware crashed while looking at dmesg. --- .../net/wireless/mediatek/mt76/mt7915/init.c | 11 +- .../net/wireless/mediatek/mt76/mt7915/mac.c | 214 +++++++++++++++++- .../net/wireless/mediatek/mt76/mt7915/main.c | 18 +- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 24 +- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 7 +- .../wireless/mediatek/mt76/mt7915/mt7915.h | 15 +- .../net/wireless/mediatek/mt76/mt7915/regs.h | 4 + 7 files changed, 267 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 324db5291c85..1f39aa3e3f3d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -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); @@ -446,7 +445,7 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band) mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN); } -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; @@ -476,7 +475,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; @@ -1117,6 +1116,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; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 3b1259f14de6..69ce3b39aa53 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1265,7 +1265,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); @@ -1320,6 +1320,171 @@ 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; + u32 irq_mask; + + 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, irq_mask); + 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 mt7915_phy *phy2; + struct mt76_phy *ext_phy; + int i; + + ext_phy = dev->mt76.phys[MT_BAND1]; + phy2 = ext_phy ? ext_phy->priv : NULL; + + 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) { @@ -1332,7 +1497,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)); @@ -1406,6 +1592,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; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 6de49b93387e..32c362965dc4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -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; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 65ae959859e7..17fe29d8fd71 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -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); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index be1b8ea711c7..032af46289af 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -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); } } } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 5af26e60e902..49caf7b21a20 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -303,7 +303,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; @@ -444,7 +450,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, @@ -544,6 +556,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); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 5180dd931835..9120380d74c6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -541,6 +541,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) @@ -709,6 +711,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)) -- 2.36.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support 2022-11-09 20:36 ` [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support Ryder Lee @ 2022-11-12 12:18 ` Lorenzo Bianconi 2022-11-12 16:13 ` Lorenzo Bianconi 0 siblings, 1 reply; 8+ messages in thread From: Lorenzo Bianconi @ 2022-11-12 12:18 UTC (permalink / raw) To: Ryder Lee Cc: Felix Fietkau, linux-wireless, Lorenzo Bianconi, Shayne Chen, Evelyn Tsai, linux-mediatek, Bo Jiao [-- Attachment #1: Type: text/plain, Size: 16183 bytes --] > From: Bo Jiao <bo.jiao@mediatek.com> > > 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> > --- > change since v2 - change dev_info to make it easier to see that firmware crashed while looking at dmesg. > --- > .../net/wireless/mediatek/mt76/mt7915/init.c | 11 +- > .../net/wireless/mediatek/mt76/mt7915/mac.c | 214 +++++++++++++++++- > .../net/wireless/mediatek/mt76/mt7915/main.c | 18 +- > .../net/wireless/mediatek/mt76/mt7915/mcu.c | 24 +- > .../net/wireless/mediatek/mt76/mt7915/mmio.c | 7 +- > .../wireless/mediatek/mt76/mt7915/mt7915.h | 15 +- > .../net/wireless/mediatek/mt76/mt7915/regs.h | 4 + > 7 files changed, 267 insertions(+), 26 deletions(-) > > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c > index 324db5291c85..1f39aa3e3f3d 100644 > --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c > @@ -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); > @@ -446,7 +445,7 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band) > mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN); > } > > -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; > @@ -476,7 +475,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; > > @@ -1117,6 +1116,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; > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > index 3b1259f14de6..69ce3b39aa53 100644 > --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > @@ -1265,7 +1265,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); > @@ -1320,6 +1320,171 @@ 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; > + u32 irq_mask; > + > + 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, irq_mask); irq_mask is used uninitialized here. Moreover is mt7915_mac_restart() and full-reset compatible with wed support? it seems in mt7915_dual_hif_set_irq_mask() we took a different approach. Regards, Lorenzo > + 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 mt7915_phy *phy2; > + struct mt76_phy *ext_phy; > + int i; > + > + ext_phy = dev->mt76.phys[MT_BAND1]; > + phy2 = ext_phy ? ext_phy->priv : NULL; > + > + 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) > { > @@ -1332,7 +1497,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)); > @@ -1406,6 +1592,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; > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c > index 6de49b93387e..32c362965dc4 100644 > --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c > @@ -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; > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c > index 65ae959859e7..17fe29d8fd71 100644 > --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c > @@ -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); > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c > index be1b8ea711c7..032af46289af 100644 > --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c > @@ -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); > } > } > } > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h > index 5af26e60e902..49caf7b21a20 100644 > --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h > @@ -303,7 +303,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; > @@ -444,7 +450,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, > @@ -544,6 +556,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); > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h > index 5180dd931835..9120380d74c6 100644 > --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h > @@ -541,6 +541,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) > @@ -709,6 +711,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)) > -- > 2.36.1 > [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support 2022-11-12 12:18 ` Lorenzo Bianconi @ 2022-11-12 16:13 ` Lorenzo Bianconi 2022-11-12 21:48 ` Ryder Lee 0 siblings, 1 reply; 8+ messages in thread From: Lorenzo Bianconi @ 2022-11-12 16:13 UTC (permalink / raw) To: Ryder Lee Cc: Felix Fietkau, linux-wireless, Lorenzo Bianconi, Shayne Chen, Evelyn Tsai, linux-mediatek, Bo Jiao [-- Attachment #1: Type: text/plain, Size: 18497 bytes --] > > From: Bo Jiao <bo.jiao@mediatek.com> > > > > 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> > > --- > > change since v2 - change dev_info to make it easier to see that firmware crashed while looking at dmesg. > > --- > > .../net/wireless/mediatek/mt76/mt7915/init.c | 11 +- > > .../net/wireless/mediatek/mt76/mt7915/mac.c | 214 +++++++++++++++++- > > .../net/wireless/mediatek/mt76/mt7915/main.c | 18 +- > > .../net/wireless/mediatek/mt76/mt7915/mcu.c | 24 +- > > .../net/wireless/mediatek/mt76/mt7915/mmio.c | 7 +- > > .../wireless/mediatek/mt76/mt7915/mt7915.h | 15 +- > > .../net/wireless/mediatek/mt76/mt7915/regs.h | 4 + > > 7 files changed, 267 insertions(+), 26 deletions(-) > > > > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c > > index 324db5291c85..1f39aa3e3f3d 100644 > > --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c > > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c > > @@ -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); > > @@ -446,7 +445,7 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band) > > mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN); > > } > > > > -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; > > @@ -476,7 +475,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; > > > > @@ -1117,6 +1116,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; > > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > > index 3b1259f14de6..69ce3b39aa53 100644 > > --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > > @@ -1265,7 +1265,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); > > @@ -1320,6 +1320,171 @@ 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; > > + u32 irq_mask; > > + > > + 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, irq_mask); > > irq_mask is used uninitialized here. > Moreover is mt7915_mac_restart() and full-reset compatible with wed support? > it seems in mt7915_dual_hif_set_irq_mask() we took a different approach. > > Regards, > Lorenzo should we do something similar to the patch below? diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index c76c5cc398e9..9c99c3014a79 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1386,7 +1386,6 @@ mt7915_mac_restart(struct mt7915_dev *dev) struct mt76_phy *ext_phy; struct mt76_dev *mdev = &dev->mt76; int i, ret; - u32 irq_mask; ext_phy = dev->mt76.phys[MT_BAND1]; phy2 = ext_phy ? ext_phy->priv : NULL; @@ -1440,11 +1439,17 @@ mt7915_mac_restart(struct mt7915_dev *dev) 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 (mtk_wed_device_active(&mdev->mmio.wed)) { + mtk_wed_device_irq_set_mask(&mdev->mmio.wed, + mdev->mmio.irqmask); + mtk_wed_device_irq_get(wed, ~0); + } else { + mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + } if (dev->hif2) { - mt76_wr(dev, MT_INT1_MASK_CSR, irq_mask); + 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_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 mt7915_phy *phy2; > > + struct mt76_phy *ext_phy; > > + int i; > > + > > + ext_phy = dev->mt76.phys[MT_BAND1]; > > + phy2 = ext_phy ? ext_phy->priv : NULL; > > + > > + 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) > > { > > @@ -1332,7 +1497,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)); > > @@ -1406,6 +1592,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; > > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c > > index 6de49b93387e..32c362965dc4 100644 > > --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c > > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c > > @@ -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; > > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c > > index 65ae959859e7..17fe29d8fd71 100644 > > --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c > > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c > > @@ -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); > > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c > > index be1b8ea711c7..032af46289af 100644 > > --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c > > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c > > @@ -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); > > } > > } > > } > > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h > > index 5af26e60e902..49caf7b21a20 100644 > > --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h > > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h > > @@ -303,7 +303,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; > > @@ -444,7 +450,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, > > @@ -544,6 +556,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); > > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h > > index 5180dd931835..9120380d74c6 100644 > > --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h > > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h > > @@ -541,6 +541,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) > > @@ -709,6 +711,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)) > > -- > > 2.36.1 > > [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support 2022-11-12 16:13 ` Lorenzo Bianconi @ 2022-11-12 21:48 ` Ryder Lee 0 siblings, 0 replies; 8+ messages in thread From: Ryder Lee @ 2022-11-12 21:48 UTC (permalink / raw) To: lorenzo@kernel.org Cc: linux-wireless@vger.kernel.org, lorenzo.bianconi@redhat.com, nbd@nbd.name, Shayne Chen (陳軒丞), Evelyn Tsai (蔡珊鈺), linux-mediatek@lists.infradead.org, Bo Jiao (焦波) On Sat, 2022-11-12 at 17:13 +0100, Lorenzo Bianconi wrote: > > > From: Bo Jiao <bo.jiao@mediatek.com> > > > > > > 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> > > > --- > > > change since v2 - change dev_info to make it easier to see that > > > firmware crashed while looking at dmesg. > > > --- > > > .../net/wireless/mediatek/mt76/mt7915/init.c | 11 +- > > > .../net/wireless/mediatek/mt76/mt7915/mac.c | 214 > > > +++++++++++++++++- > > > .../net/wireless/mediatek/mt76/mt7915/main.c | 18 +- > > > .../net/wireless/mediatek/mt76/mt7915/mcu.c | 24 +- > > > .../net/wireless/mediatek/mt76/mt7915/mmio.c | 7 +- > > > .../wireless/mediatek/mt76/mt7915/mt7915.h | 15 +- > > > .../net/wireless/mediatek/mt76/mt7915/regs.h | 4 + > > > 7 files changed, 267 insertions(+), 26 deletions(-) > > > > > > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c > > > b/drivers/net/wireless/mediatek/mt76/mt7915/init.c > > > index 324db5291c85..1f39aa3e3f3d 100644 > > > --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c > > > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c > > > @@ -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); > > > @@ -446,7 +445,7 @@ mt7915_mac_init_band(struct mt7915_dev *dev, > > > u8 band) > > > mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN); > > > } > > > > > > -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; > > > @@ -476,7 +475,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; > > > > > > @@ -1117,6 +1116,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; > > > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > > > b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > > > index 3b1259f14de6..69ce3b39aa53 100644 > > > --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > > > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > > > @@ -1265,7 +1265,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); > > > @@ -1320,6 +1320,171 @@ 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; > > > + u32 irq_mask; > > > + > > > + 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, irq_mask); > > > > irq_mask is used uninitialized here. > > Moreover is mt7915_mac_restart() and full-reset compatible with wed > > support? > > it seems in mt7915_dual_hif_set_irq_mask() we took a different > > approach. > > > > Regards, > > Lorenzo > > should we do something similar to the patch below? > > diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > index c76c5cc398e9..9c99c3014a79 100644 > --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c > @@ -1386,7 +1386,6 @@ mt7915_mac_restart(struct mt7915_dev *dev) > struct mt76_phy *ext_phy; > struct mt76_dev *mdev = &dev->mt76; > int i, ret; > - u32 irq_mask; > > ext_phy = dev->mt76.phys[MT_BAND1]; > phy2 = ext_phy ? ext_phy->priv : NULL; > @@ -1440,11 +1439,17 @@ mt7915_mac_restart(struct mt7915_dev *dev) > 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 (mtk_wed_device_active(&mdev->mmio.wed)) { > + mtk_wed_device_irq_set_mask(&mdev->mmio.wed, > + mdev->mmio.irqmask); > + mtk_wed_device_irq_get(wed, ~0); > + } else { > + mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); > + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); > + } > > if (dev->hif2) { > - mt76_wr(dev, MT_INT1_MASK_CSR, irq_mask); > + mt76_wr(dev, MT_INT1_MASK_CSR, dev->mt76.mmio.irqmask); > mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0); Yes we should use dev->mt76.mmio.irqmask...somehow we added irq_mask here. @Felix, could you help to fold this? As for wed_device I think we need another patch to suppor its SER as it needs to take mtk_eth into account, and actually there's a patch in MTK's git01 repo. Ryder ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 3/4] wifi: mt76: mt7915: add full system reset into debugfs 2022-11-09 20:36 [PATCH v3 1/4] wifi: mt76: mt7915: rework mt7915_dma_reset() Ryder Lee 2022-11-09 20:36 ` [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support Ryder Lee @ 2022-11-09 20:36 ` Ryder Lee 2022-11-09 20:36 ` [PATCH v3 4/4] wifi: mt76: mt7915: enable coredump support Ryder Lee 2 siblings, 0 replies; 8+ messages in thread From: Ryder Lee @ 2022-11-09 20:36 UTC (permalink / raw) To: Felix Fietkau, linux-wireless Cc: Lorenzo Bianconi, Shayne Chen, Evelyn Tsai, linux-mediatek, Ryder Lee Trigger firmware crash and enable full system recovery through debugfs. This also renames knob "fw_ser" to a clear-cut name "sys_recovery". Signed-off-by: Ryder Lee <ryder.lee@mediatek.com> --- change since v2 - print out the text for what the various values that can be written to this debugfs file do. --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 81 +++++++++++++++---- .../net/wireless/mediatek/mt76/mt7915/mac.c | 5 ++ .../net/wireless/mediatek/mt76/mt7915/mcu.h | 2 + .../wireless/mediatek/mt76/mt7915/mt7915.h | 2 + .../net/wireless/mediatek/mt76/mt7915/regs.h | 4 + 5 files changed, 80 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index e1d15394a621..10af98ea5ea8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -46,8 +46,8 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7915_implicit_txbf_get, /* test knob of system error recovery */ static ssize_t -mt7915_fw_ser_set(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) +mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) { struct mt7915_phy *phy = file->private_data; struct mt7915_dev *dev = phy->dev; @@ -71,8 +71,18 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf, return -EINVAL; switch (val) { + /* + * 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 full recovery. + * 8: trigger firmware crash. + */ case SER_QUERY: - /* grab firmware SER stats */ ret = mt7915_mcu_set_ser(dev, 0, 0, ext_phy); break; case SER_SET_RECOVER_L1: @@ -87,6 +97,23 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf, ret = mt7915_mcu_set_ser(dev, SER_RECOVER, val, ext_phy); break; + + /* enable full chip reset */ + case SER_SET_RECOVER_FULL: + mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); + ret = mt7915_mcu_set_ser(dev, 1, 3, ext_phy); + if (ret) + return ret; + + dev->recovery.state |= MT_MCU_CMD_WDT_MASK; + mt7915_reset(dev); + break; + + /* WARNING: trigger firmware crash */ + case 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; } @@ -95,20 +122,45 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf, } static ssize_t -mt7915_fw_ser_get(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) +mt7915_sys_recovery_get(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) { struct mt7915_phy *phy = file->private_data; struct mt7915_dev *dev = phy->dev; char *buff; int desc = 0; ssize_t ret; - static const size_t bufsz = 400; + 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 full recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "8: 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)); @@ -139,15 +191,19 @@ mt7915_fw_ser_get(struct file *file, char __user *user_buf, 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, + "\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 mt7915_fw_ser_ops = { - .write = mt7915_fw_ser_set, - .read = mt7915_fw_ser_get, +static const struct file_operations mt7915_sys_recovery_ops = { + .write = mt7915_sys_recovery_set, + .read = mt7915_sys_recovery_get, .open = simple_open, .llseek = default_llseek, }; @@ -598,10 +654,6 @@ mt7915_fw_util_wm_show(struct seq_file *file, void *data) struct mt7915_dev *dev = file->private; seq_printf(file, "Program counter: 0x%x\n", mt76_rr(dev, MT_WM_MCU_PC)); - seq_printf(file, "Exception state: 0x%x\n", - is_mt7915(&dev->mt76) ? - (u32)mt76_get_field(dev, MT_FW_EXCEPTION, GENMASK(15, 8)) : - (u32)mt76_get_field(dev, MT_FW_EXCEPTION, GENMASK(7, 0))); if (dev->fw.debug_wm) { seq_printf(file, "Busy: %u%% Peak busy: %u%%\n", @@ -1010,7 +1062,8 @@ int mt7915_init_debugfs(struct mt7915_phy *phy) debugfs_create_file("xmit-queues", 0400, dir, phy, &mt7915_xmit_queues_fops); debugfs_create_file("tx_stats", 0400, dir, phy, &mt7915_tx_stats_fops); - debugfs_create_file("fw_ser", 0600, dir, phy, &mt7915_fw_ser_ops); + debugfs_create_file("sys_recovery", 0600, dir, phy, + &mt7915_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); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 69ce3b39aa53..c8e69644c552 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1503,6 +1503,11 @@ void mt7915_mac_reset_work(struct work_struct *work) 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++; + mt7915_mac_full_reset(dev); /* enable mcu irq */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 87cd1bfff3e6..5a8c57a1421f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -448,6 +448,8 @@ enum { SER_SET_RECOVER_L3_TX_ABORT, SER_SET_RECOVER_L3_TX_DISABLE, SER_SET_RECOVER_L3_BF, + SER_SET_RECOVER_FULL, + SER_SET_SYSTEM_ASSERT, /* action */ SER_ENABLE = 2, SER_RECOVER diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 49caf7b21a20..96ef06511552 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -306,6 +306,8 @@ struct mt7915_dev { struct { u32 state; + u32 wa_reset_count; + u32 wm_reset_count; bool hw_full_reset:1; bool hw_init_done:1; bool restart:1; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 9120380d74c6..d7756cb0d29b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -711,6 +711,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_WA_WDT BIT(31) +#define MT_MCU_CMD_WM_WDT BIT(30) #define MT_MCU_CMD_WDT_MASK GENMASK(31, 30) /* TOP RGU */ @@ -1129,5 +1131,7 @@ enum offs_rev { #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.36.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v3 4/4] wifi: mt76: mt7915: enable coredump support 2022-11-09 20:36 [PATCH v3 1/4] wifi: mt76: mt7915: rework mt7915_dma_reset() Ryder Lee 2022-11-09 20:36 ` [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support Ryder Lee 2022-11-09 20:36 ` [PATCH v3 3/4] wifi: mt76: mt7915: add full system reset into debugfs Ryder Lee @ 2022-11-09 20:36 ` Ryder Lee 2022-11-17 12:06 ` Kalle Valo 2 siblings, 1 reply; 8+ messages in thread From: Ryder Lee @ 2022-11-09 20:36 UTC (permalink / raw) To: Felix Fietkau, linux-wireless Cc: Lorenzo Bianconi, Shayne Chen, Evelyn Tsai, linux-mediatek, Ryder Lee [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: text/plain; charset="y", Size: 26377 bytes --] Host triggered and catastrophic event triggered firmware core dumping for basic firmware issues triage, including state reporting, task/irq info, function calltrace and MCU memory dump. Signed-off-by: Ryder Lee <ryder.lee@mediatek.com> --- .../net/wireless/mediatek/mt76/mt7915/Kconfig | 1 + .../wireless/mediatek/mt76/mt7915/Makefile | 3 +- .../wireless/mediatek/mt76/mt7915/coredump.c | 409 ++++++++++++++++++ .../wireless/mediatek/mt76/mt7915/coredump.h | 136 ++++++ .../net/wireless/mediatek/mt76/mt7915/init.c | 12 +- .../net/wireless/mediatek/mt76/mt7915/mac.c | 72 ++- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 59 ++- .../wireless/mediatek/mt76/mt7915/mt7915.h | 22 + .../net/wireless/mediatek/mt76/mt7915/regs.h | 34 +- 9 files changed, 740 insertions(+), 8 deletions(-) create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/coredump.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/coredump.h diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig index f21282cea845..d710726d47bf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig @@ -2,6 +2,7 @@ config MT7915E tristate "MediaTek MT7915E (PCIe) support" select MT76_CONNAC_LIB + select WANT_DEV_COREDUMP depends on MAC80211 depends on PCI select RELAY diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile index b794ceb79c37..797ae49805c3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile +++ b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile @@ -6,4 +6,5 @@ mt7915e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \ debugfs.o mmio.o mt7915e-$(CONFIG_NL80211_TESTMODE) += testmode.o -mt7915e-$(CONFIG_MT7986_WMAC) += soc.o \ No newline at end of file +mt7915e-$(CONFIG_MT7986_WMAC) += soc.o +mt7915e-$(CONFIG_DEV_COREDUMP) += coredump.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c new file mode 100644 index 000000000000..bb4b70401af5 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: ISC +/* Copyright (C) 2022 MediaTek Inc. */ + +#include <linux/devcoredump.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/utsname.h> +#include "coredump.h" + +static bool coredump_memdump; +module_param(coredump_memdump, bool, 0644); + +static const struct mt7915_mem_region mt7915_mem_regions[] = { + { + .start = 0xe003b400, + .len = 0x00003bff, + .name = "CRAM", + }, +}; + +static const struct mt7915_mem_region mt7916_mem_regions[] = { + { + .start = 0x00800000, + .len = 0x0005ffff, + .name = "ROM", + }, + { + .start = 0x00900000, + .len = 0x00013fff, + .name = "ULM1", + }, + { + .start = 0x02200000, + .len = 0x0004ffff, + .name = "ULM2", + }, + { + .start = 0x02300000, + .len = 0x0004ffff, + .name = "ULM3", + }, + { + .start = 0x00400000, + .len = 0x00027fff, + .name = "SRAM", + }, + { + .start = 0xe0000000, + .len = 0x00157fff, + .name = "CRAM", + }, +}; + +static const struct mt7915_mem_region mt7986_mem_regions[] = { + { + .start = 0x00800000, + .len = 0x0005ffff, + .name = "ROM", + }, + { + .start = 0x00900000, + .len = 0x0000ffff, + .name = "ULM1", + }, + { + .start = 0x02200000, + .len = 0x0004ffff, + .name = "ULM2", + }, + { + .start = 0x02300000, + .len = 0x0004ffff, + .name = "ULM3", + }, + { + .start = 0x00400000, + .len = 0x00017fff, + .name = "SRAM", + }, + { + .start = 0xe0000000, + .len = 0x00113fff, + .name = "CRAM", + }, +}; + +const struct mt7915_mem_region* +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7915: + *num = ARRAY_SIZE(mt7915_mem_regions); + return &mt7915_mem_regions[0]; + case 0x7986: + *num = ARRAY_SIZE(mt7986_mem_regions); + return &mt7986_mem_regions[0]; + case 0x7916: + *num = ARRAY_SIZE(mt7916_mem_regions); + return &mt7916_mem_regions[0]; + default: + return NULL; + } +} + +static int mt7915_coredump_get_mem_size(struct mt7915_dev *dev) +{ + const struct mt7915_mem_region *mem_region; + size_t size = 0; + u32 num; + int i; + + mem_region = mt7915_coredump_get_mem_layout(dev, &num); + if (!mem_region) + return 0; + + for (i = 0; i < num; i++) { + size += mem_region->len; + mem_region++; + } + + /* reserve space for the headers */ + size += num * sizeof(struct mt7915_mem_hdr); + /* make sure it is aligned 4 bytes for debug message print out */ + size = ALIGN(size, 4); + + return size; +} + +struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data = dev->coredump.crash_data; + + lockdep_assert_held(&dev->dump_mutex); + + guid_gen(&crash_data->guid); + ktime_get_real_ts64(&crash_data->timestamp); + + return crash_data; +} + +static void +mt7915_coredump_fw_state(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool *exception) +{ + u32 state, count, type; + + type = (u32)mt76_get_field(dev, MT_FW_EXCEPT_TYPE, GENMASK(7, 0)); + state = (u32)mt76_get_field(dev, MT_FW_ASSERT_STAT, GENMASK(7, 0)); + count = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(15, 8)) : + (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(7, 0)); + + /* normal mode: driver can manually trigger assert for detail info */ + if (!count) + strscpy(dump->fw_state, "normal", sizeof(dump->fw_state)); + else if (state > 1 && (count == 1) && type == 5) + strscpy(dump->fw_state, "assert", sizeof(dump->fw_state)); + else if ((state > 1 && count == 1) || count > 1) + strscpy(dump->fw_state, "exception", sizeof(dump->fw_state)); + + *exception = !!count; +} + +static void +mt7915_coredump_fw_trace(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool exception) +{ + u32 n, irq, sch, base = MT_FW_EINT_INFO; + + /* trap or run? */ + dump->last_msg_id = mt76_rr(dev, MT_FW_LAST_MSG_ID); + + n = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, base, GENMASK(7, 0)) : + (u32)mt76_get_field(dev, base, GENMASK(15, 8)); + dump->eint_info_idx = n; + + irq = mt76_rr(dev, base + 0x8); + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(7, 0), irq) : FIELD_GET(GENMASK(23, 16), irq); + dump->irq_info_idx = n; + + sch = mt76_rr(dev, MT_FW_SCHED_INFO); + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(7, 0), sch) : FIELD_GET(GENMASK(15, 8), sch); + dump->sched_info_idx = n; + + if (exception) { + u32 i, y; + + /* sched trace */ + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(15, 8), sch) : FIELD_GET(GENMASK(7, 0), sch); + n = n > 60 ? 60 : n; + + strscpy(dump->trace_sched, "(sched_info) id, time", + sizeof(dump->trace_sched)); + + for (y = dump->sched_info_idx, i = 0; i < n; i++, y++) { + mt7915_memcpy_fromio(dev, dump->sched, base + 0xc + y * 12, + sizeof(dump->sched)); + y = y >= n ? 0 : y; + } + + /* irq trace */ + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(15, 8), irq) : FIELD_GET(GENMASK(7, 0), irq); + n = n > 60 ? 60 : n; + + strscpy(dump->trace_irq, "(irq_info) id, time", + sizeof(dump->trace_irq)); + + for (y = dump->irq_info_idx, i = 0; i < n; i++, y++) { + mt7915_memcpy_fromio(dev, dump->irq, base + 0x4 + y * 16, + sizeof(dump->irq)); + y = y >= n ? 0 : y; + } + } +} + +static void +mt7915_coredump_fw_stack(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool exception) +{ + u32 oldest, i, idx; + + /* stop call stack record */ + if (!exception) + mt76_clear(dev, 0x89050200, BIT(0)); + + oldest = (u32)mt76_get_field(dev, 0x89050200, GENMASK(20, 16)) + 2; + for (i = 0; i < 16; i++) { + idx = ((oldest + 2 * i + 1) % 32); + dump->call_stack[i] = mt76_rr(dev, 0x89050204 + idx * 4); + } + + /* start call stack record */ + if (!exception) + mt76_set(dev, 0x89050200, BIT(0)); +} + +static void +mt7915_coredump_fw_task(struct mt7915_dev *dev, struct mt7915_coredump *dump) +{ + u32 offs = is_mt7915(&dev->mt76) ? 0xe0 : 0x170; + + strscpy(dump->task_qid, "(task queue id) read, write", + sizeof(dump->task_qid)); + + dump->taskq[0].read = mt76_rr(dev, MT_FW_TASK_QID1); + dump->taskq[0].write = mt76_rr(dev, MT_FW_TASK_QID1 - 4); + dump->taskq[1].read = mt76_rr(dev, MT_FW_TASK_QID2); + dump->taskq[1].write = mt76_rr(dev, MT_FW_TASK_QID2 - 4); + + strscpy(dump->task_info, "(task stack) start, end, size", + sizeof(dump->task_info)); + + dump->taski[0].start = mt76_rr(dev, MT_FW_TASK_START); + dump->taski[0].end = mt76_rr(dev, MT_FW_TASK_END); + dump->taski[0].size = mt76_rr(dev, MT_FW_TASK_SIZE); + dump->taski[1].start = mt76_rr(dev, MT_FW_TASK_START + offs); + dump->taski[1].end = mt76_rr(dev, MT_FW_TASK_END + offs); + dump->taski[1].size = mt76_rr(dev, MT_FW_TASK_SIZE + offs); +} + +static void +mt7915_coredump_fw_context(struct mt7915_dev *dev, struct mt7915_coredump *dump) +{ + u32 count, idx, id; + + count = mt76_rr(dev, MT_FW_CIRQ_COUNT); + + /* current context */ + if (!count) { + strscpy(dump->fw_context, "(context) interrupt", + sizeof(dump->fw_context)); + + idx = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(31, 16)) : + (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(15, 0)); + dump->context.idx = idx; + dump->context.handler = mt76_rr(dev, MT_FW_CIRQ_LISR); + } else { + idx = mt76_rr(dev, MT_FW_TASK_IDX); + id = mt76_rr(dev, MT_FW_TASK_ID); + + if (!id && idx == 3) { + strscpy(dump->fw_context, "(context) idle", + sizeof(dump->fw_context)); + } else if (id && idx != 3) { + strscpy(dump->fw_context, "(context) task", + sizeof(dump->fw_context)); + + dump->context.idx = idx; + dump->context.handler = id; + } + } +} + +static struct mt7915_coredump *mt7915_coredump_build(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data = dev->coredump.crash_data; + struct mt7915_coredump *dump; + struct mt7915_coredump_mem *dump_mem; + size_t len, sofar = 0, hdr_len = sizeof(*dump); + unsigned char *buf; + bool exception; + + len = hdr_len; + + if (coredump_memdump && crash_data->memdump_buf_len) + len += sizeof(*dump_mem) + crash_data->memdump_buf_len; + + sofar += hdr_len; + + /* this is going to get big when we start dumping memory and such, + * so go ahead and use vmalloc. + */ + buf = vzalloc(len); + if (!buf) + return NULL; + + mutex_lock(&dev->dump_mutex); + + dump = (struct mt7915_coredump *)(buf); + dump->len = len; + + /* plain text */ + strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic)); + strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel)); + strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version, + sizeof(dump->fw_ver)); + + guid_copy(&dump->guid, &crash_data->guid); + dump->tv_sec = crash_data->timestamp.tv_sec; + dump->tv_nsec = crash_data->timestamp.tv_nsec; + dump->device_id = mt76_chip(&dev->mt76); + + mt7915_coredump_fw_state(dev, dump, &exception); + mt7915_coredump_fw_trace(dev, dump, exception); + mt7915_coredump_fw_task(dev, dump); + mt7915_coredump_fw_context(dev, dump); + mt7915_coredump_fw_stack(dev, dump, exception); + + /* gather memory content */ + dump_mem = (struct mt7915_coredump_mem *)(buf + sofar); + dump_mem->len = crash_data->memdump_buf_len; + if (coredump_memdump && crash_data->memdump_buf_len) + memcpy(dump_mem->data, crash_data->memdump_buf, + crash_data->memdump_buf_len); + + mutex_unlock(&dev->dump_mutex); + + return dump; +} + +int mt7915_coredump_submit(struct mt7915_dev *dev) +{ + struct mt7915_coredump *dump; + + dump = mt7915_coredump_build(dev); + if (!dump) { + dev_warn(dev->mt76.dev, "no crash dump data found\n"); + return -ENODATA; + } + + dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL); + + return 0; +} + +int mt7915_coredump_register(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data; + + crash_data = vzalloc(sizeof(*dev->coredump.crash_data)); + if (!crash_data) + return -ENOMEM; + + dev->coredump.crash_data = crash_data; + + if (coredump_memdump) { + crash_data->memdump_buf_len = mt7915_coredump_get_mem_size(dev); + if (!crash_data->memdump_buf_len) + /* no memory content */ + return 0; + + crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len); + if (!crash_data->memdump_buf) { + vfree(crash_data); + return -ENOMEM; + } + } + + return 0; +} + +void mt7915_coredump_unregister(struct mt7915_dev *dev) +{ + if (dev->coredump.crash_data->memdump_buf) { + vfree(dev->coredump.crash_data->memdump_buf); + dev->coredump.crash_data->memdump_buf = NULL; + dev->coredump.crash_data->memdump_buf_len = 0; + } + + vfree(dev->coredump.crash_data); + dev->coredump.crash_data = NULL; +} + diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h new file mode 100644 index 000000000000..1db326d208d0 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: ISC */ +/* Copyright (C) 2022 MediaTek Inc. */ + +#ifndef _COREDUMP_H_ +#define _COREDUMP_H_ + +#include "mt7915.h" + +struct trace { + u32 id; + u32 timestamp; +}; + +struct mt7915_coredump { + char magic[16]; + + u32 len; + + guid_t guid; + + /* time-of-day stamp */ + u64 tv_sec; + /* time-of-day stamp, nano-seconds */ + u64 tv_nsec; + /* kernel version */ + char kernel[64]; + /* firmware version */ + char fw_ver[ETHTOOL_FWVERS_LEN]; + + u32 device_id; + + /* exception state */ + char fw_state[12]; + + u32 last_msg_id; + u32 eint_info_idx; + u32 irq_info_idx; + u32 sched_info_idx; + + /* schedule info */ + char trace_sched[32]; + struct { + struct trace t; + u32 pc; + } sched[60]; + + /* irq info */ + char trace_irq[32]; + struct trace irq[60]; + + /* task queue status */ + char task_qid[32]; + struct { + u32 read; + u32 write; + } taskq[2]; + + /* task stack info */ + char task_info[32]; + struct { + u32 start; + u32 end; + u32 size; + } taski[2]; + + /* firmware context */ + char fw_context[24]; + struct { + u32 idx; + u32 handler; + } context; + + /* link registers calltrace */ + u32 call_stack[16]; + + /* memory content */ + u8 data[]; +} __packed; + +struct mt7915_coredump_mem { + u32 len; + u8 data[]; +} __packed; + +struct mt7915_mem_hdr { + u32 start; + u32 len; + u8 data[]; +}; + +struct mt7915_mem_region { + u32 start; + size_t len; + + const char *name; +}; + +#ifdef CONFIG_DEV_COREDUMP + +const struct mt7915_mem_region * +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num); +struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev); +int mt7915_coredump_submit(struct mt7915_dev *dev); +int mt7915_coredump_register(struct mt7915_dev *dev); +void mt7915_coredump_unregister(struct mt7915_dev *dev); + +#else /* CONFIG_DEV_COREDUMP */ + +static inline const struct mt7915_mem_region* +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev) +{ + return NULL; +} + +static inline int mt7915_coredump_submit(struct mt7915_dev *dev) +{ + return 0; +} + +struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev) +{ + return NULL; +} + +static inline int mt7915_coredump_register(struct mt7915_dev *dev) +{ + return 0; +} + +static inline void mt7915_coredump_unregister(struct mt7915_dev *dev) +{ +} + +#endif /* CONFIG_DEV_COREDUMP */ + +#endif /* _COREDUMP_H_ */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 1f39aa3e3f3d..db581742994c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -8,6 +8,7 @@ #include "mt7915.h" #include "mac.h" #include "mcu.h" +#include "coredump.h" #include "eeprom.h" static const struct ieee80211_iface_limit if_limits[] = { @@ -1076,6 +1077,8 @@ int mt7915_register_device(struct mt7915_dev *dev) init_waitqueue_head(&dev->reset_wait); INIT_WORK(&dev->reset_work, mt7915_mac_reset_work); + INIT_WORK(&dev->dump_work, mt7915_mac_dump_work); + mutex_init(&dev->dump_mutex); dev->dbdc_support = mt7915_band_config(dev); @@ -1118,7 +1121,13 @@ int mt7915_register_device(struct mt7915_dev *dev) dev->recovery.hw_init_done = true; - mt7915_init_debugfs(&dev->phy); + ret = mt7915_init_debugfs(&dev->phy); + if (ret) + goto unreg_thermal; + + ret = mt7915_coredump_register(dev); + if (ret) + goto unreg_thermal; return 0; @@ -1137,6 +1146,7 @@ int mt7915_register_device(struct mt7915_dev *dev) void mt7915_unregister_device(struct mt7915_dev *dev) { mt7915_unregister_ext_phy(dev); + mt7915_coredump_unregister(dev); mt7915_unregister_thermal(&dev->phy); mt76_unregister_device(&dev->mt76); mt7915_stop_hardware(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index c8e69644c552..ccabe492075e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -3,6 +3,7 @@ #include <linux/etherdevice.h> #include <linux/timekeeping.h> +#include "coredump.h" #include "mt7915.h" #include "../dma.h" #include "mac.h" @@ -1597,6 +1598,75 @@ void mt7915_mac_reset_work(struct work_struct *work) MT7915_WATCHDOG_TIME); } +/* firmware coredump */ +void mt7915_mac_dump_work(struct work_struct *work) +{ + const struct mt7915_mem_region *mem_region; + struct mt7915_crash_data *crash_data; + struct mt7915_dev *dev; + struct mt7915_mem_hdr *hdr; + size_t buf_len; + int i; + u32 num; + u8 *buf; + + dev = container_of(work, struct mt7915_dev, dump_work); + + mutex_lock(&dev->dump_mutex); + + crash_data = mt7915_coredump_new(dev); + if (!crash_data) { + mutex_unlock(&dev->dump_mutex); + goto skip_coredump; + } + + mem_region = mt7915_coredump_get_mem_layout(dev, &num); + if (!mem_region || !crash_data->memdump_buf_len) { + mutex_unlock(&dev->dump_mutex); + goto skip_memdump; + } + + buf = crash_data->memdump_buf; + buf_len = crash_data->memdump_buf_len; + + /* dumping memory content... */ + memset(buf, 0, buf_len); + for (i = 0; i < num; i++) { + if (mem_region->len > buf_len) { + dev_warn(dev->mt76.dev, "%s len %lu is too large\n", + mem_region->name, mem_region->len); + break; + } + + /* reserve space for the header */ + hdr = (void *)buf; + buf += sizeof(*hdr); + buf_len -= sizeof(*hdr); + + mt7915_memcpy_fromio(dev, buf, mem_region->start, + mem_region->len); + + hdr->start = mem_region->start; + hdr->len = mem_region->len; + + if (!mem_region->len) + /* note: the header remains, just with zero length */ + break; + + buf += mem_region->len; + buf_len -= mem_region->len; + + mem_region++; + } + + mutex_unlock(&dev->dump_mutex); + +skip_memdump: + mt7915_coredump_submit(dev); +skip_coredump: + queue_work(dev->mt76.wq, &dev->reset_work); +} + void mt7915_reset(struct mt7915_dev *dev) { if (!dev->recovery.hw_init_done) @@ -1613,7 +1683,7 @@ void mt7915_reset(struct mt7915_dev *dev) wiphy_name(dev->mt76.hw->wiphy)); mt7915_irq_disable(dev, MT_INT_MCU_CMD); - queue_work(dev->mt76.wq, &dev->reset_work); + queue_work(dev->mt76.wq, &dev->dump_work); return; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 032af46289af..3c840853a2c9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -25,7 +25,22 @@ static const u32 mt7915_reg[] = { [WFDMA_EXT_CSR_ADDR] = 0xd7000, [CBTOP1_PHY_END] = 0x77ffffff, [INFRA_MCU_ADDR_END] = 0x7c3fffff, - [FW_EXCEPTION_ADDR] = 0x219848, + [FW_ASSERT_STAT_ADDR] = 0x219848, + [FW_EXCEPT_TYPE_ADDR] = 0x21987c, + [FW_EXCEPT_COUNT_ADDR] = 0x219848, + [FW_CIRQ_COUNT_ADDR] = 0x216f94, + [FW_CIRQ_IDX_ADDR] = 0x216ef8, + [FW_CIRQ_LISR_ADDR] = 0x2170ac, + [FW_TASK_ID_ADDR] = 0x216f90, + [FW_TASK_IDX_ADDR] = 0x216f9c, + [FW_TASK_QID1_ADDR] = 0x219680, + [FW_TASK_QID2_ADDR] = 0x219760, + [FW_TASK_START_ADDR] = 0x219558, + [FW_TASK_END_ADDR] = 0x219554, + [FW_TASK_SIZE_ADDR] = 0x219560, + [FW_LAST_MSG_ID_ADDR] = 0x216f70, + [FW_EINT_INFO_ADDR] = 0x219818, + [FW_SCHED_INFO_ADDR] = 0x219828, [SWDEF_BASE_ADDR] = 0x41f200, [TXQ_WED_RING_BASE] = 0xd7300, [RXQ_WED_RING_BASE] = 0xd7410, @@ -43,7 +58,22 @@ static const u32 mt7916_reg[] = { [WFDMA_EXT_CSR_ADDR] = 0xd7000, [CBTOP1_PHY_END] = 0x7fffffff, [INFRA_MCU_ADDR_END] = 0x7c085fff, - [FW_EXCEPTION_ADDR] = 0x022050bc, + [FW_ASSERT_STAT_ADDR] = 0x02204c14, + [FW_EXCEPT_TYPE_ADDR] = 0x022051a4, + [FW_EXCEPT_COUNT_ADDR] = 0x022050bc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204f84, + [FW_CIRQ_LISR_ADDR] = 0x022050d0, + [FW_TASK_ID_ADDR] = 0x0220406c, + [FW_TASK_IDX_ADDR] = 0x0220500c, + [FW_TASK_QID1_ADDR] = 0x022028c8, + [FW_TASK_QID2_ADDR] = 0x02202a38, + [FW_TASK_START_ADDR] = 0x0220286c, + [FW_TASK_END_ADDR] = 0x02202870, + [FW_TASK_SIZE_ADDR] = 0x02202878, + [FW_LAST_MSG_ID_ADDR] = 0x02204fe8, + [FW_EINT_INFO_ADDR] = 0x0220525c, + [FW_SCHED_INFO_ADDR] = 0x0220516c, [SWDEF_BASE_ADDR] = 0x411400, [TXQ_WED_RING_BASE] = 0xd7300, [RXQ_WED_RING_BASE] = 0xd7410, @@ -61,7 +91,22 @@ static const u32 mt7986_reg[] = { [WFDMA_EXT_CSR_ADDR] = 0x27000, [CBTOP1_PHY_END] = 0x7fffffff, [INFRA_MCU_ADDR_END] = 0x7c085fff, - [FW_EXCEPTION_ADDR] = 0x02204ffc, + [FW_ASSERT_STAT_ADDR] = 0x02204b54, + [FW_EXCEPT_TYPE_ADDR] = 0x022050dc, + [FW_EXCEPT_COUNT_ADDR] = 0x02204ffc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204ec4, + [FW_CIRQ_LISR_ADDR] = 0x02205010, + [FW_TASK_ID_ADDR] = 0x02204fac, + [FW_TASK_IDX_ADDR] = 0x02204f4c, + [FW_TASK_QID1_ADDR] = 0x02202814, + [FW_TASK_QID2_ADDR] = 0x02202984, + [FW_TASK_START_ADDR] = 0x022027b8, + [FW_TASK_END_ADDR] = 0x022027bc, + [FW_TASK_SIZE_ADDR] = 0x022027c4, + [FW_LAST_MSG_ID_ADDR] = 0x02204f28, + [FW_EINT_INFO_ADDR] = 0x02205194, + [FW_SCHED_INFO_ADDR] = 0x022051a4, [SWDEF_BASE_ADDR] = 0x411400, [TXQ_WED_RING_BASE] = 0x24420, [RXQ_WED_RING_BASE] = 0x24520, @@ -457,6 +502,14 @@ static u32 __mt7915_reg_addr(struct mt7915_dev *dev, u32 addr) return mt7915_reg_map_l2(dev, addr); } +void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset, + size_t len) +{ + u32 addr = __mt7915_reg_addr(dev, offset); + + memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len); +} + static u32 mt7915_rr(struct mt76_dev *mdev, u32 offset) { struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 96ef06511552..e647ba26e6ef 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -220,6 +220,15 @@ struct mib_stats { u32 tx_amsdu_cnt; }; +/* crash-dump */ +struct mt7915_crash_data { + guid_t guid; + struct timespec64 timestamp; + + u8 *memdump_buf; + size_t memdump_buf_len; +}; + struct mt7915_hif { struct list_head list; @@ -301,6 +310,7 @@ struct mt7915_dev { struct work_struct init_work; struct work_struct rc_work; + struct work_struct dump_work; struct work_struct reset_work; wait_queue_head_t reset_wait; @@ -313,6 +323,14 @@ struct mt7915_dev { bool restart:1; } recovery; + /* protects coredump data */ + struct mutex dump_mutex; +#ifdef CONFIG_DEV_COREDUMP + struct { + struct mt7915_crash_data *crash_data; + } coredump; +#endif + struct list_head sta_rc_list; struct list_head sta_poll_list; struct list_head twt_list; @@ -558,6 +576,9 @@ 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_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset, + size_t len); + 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); @@ -575,6 +596,7 @@ void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void mt7915_mac_work(struct work_struct *work); void mt7915_mac_reset_work(struct work_struct *work); +void mt7915_mac_dump_work(struct work_struct *work); void mt7915_mac_sta_rc_work(struct work_struct *work); void mt7915_mac_update_stats(struct mt7915_phy *phy); void mt7915_mac_twt_teardown_flow(struct mt7915_dev *dev, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index d7756cb0d29b..3509abaf2b82 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -24,7 +24,22 @@ enum reg_rev { WFDMA_EXT_CSR_ADDR, CBTOP1_PHY_END, INFRA_MCU_ADDR_END, - FW_EXCEPTION_ADDR, + FW_ASSERT_STAT_ADDR, + FW_EXCEPT_TYPE_ADDR, + FW_EXCEPT_COUNT_ADDR, + FW_CIRQ_COUNT_ADDR, + FW_CIRQ_IDX_ADDR, + FW_CIRQ_LISR_ADDR, + FW_TASK_ID_ADDR, + FW_TASK_IDX_ADDR, + FW_TASK_QID1_ADDR, + FW_TASK_QID2_ADDR, + FW_TASK_START_ADDR, + FW_TASK_END_ADDR, + FW_TASK_SIZE_ADDR, + FW_LAST_MSG_ID_ADDR, + FW_EINT_INFO_ADDR, + FW_SCHED_INFO_ADDR, SWDEF_BASE_ADDR, TXQ_WED_RING_BASE, RXQ_WED_RING_BASE, @@ -957,7 +972,22 @@ enum offs_rev { #define MT_ADIE_TYPE_MASK BIT(1) /* FW MODE SYNC */ -#define MT_FW_EXCEPTION __REG(FW_EXCEPTION_ADDR) +#define MT_FW_ASSERT_STAT __REG(FW_ASSERT_STAT_ADDR) +#define MT_FW_EXCEPT_TYPE __REG(FW_EXCEPT_TYPE_ADDR) +#define MT_FW_EXCEPT_COUNT __REG(FW_EXCEPT_COUNT_ADDR) +#define MT_FW_CIRQ_COUNT __REG(FW_CIRQ_COUNT_ADDR) +#define MT_FW_CIRQ_IDX __REG(FW_CIRQ_IDX_ADDR) +#define MT_FW_CIRQ_LISR __REG(FW_CIRQ_LISR_ADDR) +#define MT_FW_TASK_ID __REG(FW_TASK_ID_ADDR) +#define MT_FW_TASK_IDX __REG(FW_TASK_IDX_ADDR) +#define MT_FW_TASK_QID1 __REG(FW_TASK_QID1_ADDR) +#define MT_FW_TASK_QID2 __REG(FW_TASK_QID2_ADDR) +#define MT_FW_TASK_START __REG(FW_TASK_START_ADDR) +#define MT_FW_TASK_END __REG(FW_TASK_END_ADDR) +#define MT_FW_TASK_SIZE __REG(FW_TASK_SIZE_ADDR) +#define MT_FW_LAST_MSG_ID __REG(FW_LAST_MSG_ID_ADDR) +#define MT_FW_EINT_INFO __REG(FW_EINT_INFO_ADDR) +#define MT_FW_SCHED_INFO __REG(FW_SCHED_INFO_ADDR) #define MT_SWDEF_BASE __REG(SWDEF_BASE_ADDR) -- 2.36.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 4/4] wifi: mt76: mt7915: enable coredump support 2022-11-09 20:36 ` [PATCH v3 4/4] wifi: mt76: mt7915: enable coredump support Ryder Lee @ 2022-11-17 12:06 ` Kalle Valo 0 siblings, 0 replies; 8+ messages in thread From: Kalle Valo @ 2022-11-17 12:06 UTC (permalink / raw) To: Ryder Lee Cc: Felix Fietkau, linux-wireless, Lorenzo Bianconi, Shayne Chen, Evelyn Tsai, linux-mediatek Ryder Lee <ryder.lee@mediatek.com> writes: > Host triggered and catastrophic event triggered firmware core dumping > for basic firmware issues triage, including state reporting, task/irq > info, function calltrace and MCU memory dump. > > Signed-off-by: Ryder Lee <ryder.lee@mediatek.com> I missed that there was v3, please check my comments from v1. -- https://patchwork.kernel.org/project/linux-wireless/list/ https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2022-11-17 12:06 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2022-11-09 20:36 [PATCH v3 1/4] wifi: mt76: mt7915: rework mt7915_dma_reset() Ryder Lee 2022-11-09 20:36 ` [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support Ryder Lee 2022-11-12 12:18 ` Lorenzo Bianconi 2022-11-12 16:13 ` Lorenzo Bianconi 2022-11-12 21:48 ` Ryder Lee 2022-11-09 20:36 ` [PATCH v3 3/4] wifi: mt76: mt7915: add full system reset into debugfs Ryder Lee 2022-11-09 20:36 ` [PATCH v3 4/4] wifi: mt76: mt7915: enable coredump support Ryder Lee 2022-11-17 12:06 ` Kalle Valo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).