2022-11-09 20:37:37

by Ryder Lee

[permalink] [raw]
Subject: [PATCH v3 1/4] wifi: mt76: mt7915: rework mt7915_dma_reset()

From: Bo Jiao <[email protected]>

Reuse mt7915_dma_disable() to reduce duplicated code.
This is a preliminary patch to enable full system reset.

Co-developed-by: Ryder Lee <[email protected]>
Signed-off-by: Ryder Lee <[email protected]>
Signed-off-by: Bo Jiao <[email protected]>
---
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



2022-11-09 20:37:55

by Ryder Lee

[permalink] [raw]
Subject: [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support

From: Bo Jiao <[email protected]>

Add mt7915_reset() and refactor mt7915_mac_reset_work() to support
full system recovery.

Co-developed-by: Ryder Lee <[email protected]>
Signed-off-by: Ryder Lee <[email protected]>
Signed-off-by: Bo Jiao <[email protected]>
---
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


2022-11-09 20:39:48

by Ryder Lee

[permalink] [raw]
Subject: [PATCH v3 4/4] wifi: mt76: mt7915: enable coredump support

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 <[email protected]>
---
.../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


2022-11-12 12:42:03

by Lorenzo Bianconi

[permalink] [raw]
Subject: Re: [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support

> From: Bo Jiao <[email protected]>
>
> Add mt7915_reset() and refactor mt7915_mac_reset_work() to support
> full system recovery.
>
> Co-developed-by: Ryder Lee <[email protected]>
> Signed-off-by: Ryder Lee <[email protected]>
> Signed-off-by: Bo Jiao <[email protected]>
> ---
> 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
>


Attachments:
(No filename) (15.80 kB)
signature.asc (235.00 B)
Download all attachments

2022-11-12 16:27:14

by Lorenzo Bianconi

[permalink] [raw]
Subject: Re: [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support

> > From: Bo Jiao <[email protected]>
> >
> > Add mt7915_reset() and refactor mt7915_mac_reset_work() to support
> > full system recovery.
> >
> > Co-developed-by: Ryder Lee <[email protected]>
> > Signed-off-by: Ryder Lee <[email protected]>
> > Signed-off-by: Bo Jiao <[email protected]>
> > ---
> > 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
> >



Attachments:
(No filename) (18.06 kB)
signature.asc (235.00 B)
Download all attachments

2022-11-12 21:53:54

by Ryder Lee

[permalink] [raw]
Subject: Re: [PATCH v3 2/4] wifi: mt76: mt7915: enable full system reset support

On Sat, 2022-11-12 at 17:13 +0100, Lorenzo Bianconi wrote:
> > > From: Bo Jiao <[email protected]>
> > >
> > > Add mt7915_reset() and refactor mt7915_mac_reset_work() to
> > > support
> > > full system recovery.
> > >
> > > Co-developed-by: Ryder Lee <[email protected]>
> > > Signed-off-by: Ryder Lee <[email protected]>
> > > Signed-off-by: Bo Jiao <[email protected]>
> > > ---
> > > 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

2022-11-17 12:07:23

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH v3 4/4] wifi: mt76: mt7915: enable coredump support

Ryder Lee <[email protected]> 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 <[email protected]>

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