* [PATCH 10/18] wifi: mt76: mt7925: enable MT7927 runtime power management
From: Javier Tia @ 2026-03-07 0:33 UTC (permalink / raw)
To: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Deren Wu, Ming Yen Hsieh
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, Marcin FM,
Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
Thibaut François, 张旭涵, Javier Tia
In-Reply-To: <20260306-mt7927-wifi-support-v1-0-c77e7445511d@jetm.me>
Add MT7927-specific GLO_CFG register programming to mt792x_dma_enable()
so that ADDR_EXT_EN and CSR_LBK_RX_Q_SEL_EN are correctly configured
after every WPDMA reinitialization triggered by CLR_OWN during power
management transitions.
On MT7927, every CLR_OWN causes the ROM to reinitialize WFDMA, which
resets the DMA configuration. The PM wake path already handles ring
reprogramming (mt76_queue_reset restores ring base addresses from
stored desc_dma) and prefetch configuration (mt792x_dma_prefetch has
an is_mt7927 branch), but two GLO_CFG bits were missing from
mt792x_dma_enable:
- BIT(26) ADDR_EXT_EN: extended DMA addressing, required for
MT7927's host memory access
- BIT(20) CSR_LBK_RX_Q_SEL_EN: loopback RX queue select, must
be cleared for normal DMA operation
Also define proper macros for these bits and use them in
mt7927_dma_init() to replace bare BIT() values.
With the DMA recovery path now fully MT7927-aware, remove the
is_mt7927() guard that disabled runtime PM and deep sleep. Let the
standard mt792x power management work for MT7927 hardware.
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
drivers/net/wireless/mediatek/mt76/mt7925/init.c | 5 +----
drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 4 ++--
drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 7 +++++++
drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 2 ++
4 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
index 1e9eadca3988..84af52a0176d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
@@ -242,10 +242,7 @@ int mt7925_register_device(struct mt792x_dev *dev)
dev->pm.idle_timeout = MT792x_PM_TIMEOUT;
dev->pm.stats.last_wake_event = jiffies;
dev->pm.stats.last_doze_event = jiffies;
- /* MT7927: disable power management. Every CLR_OWN triggers the
- * ROM to reinitialize WFDMA, destroying DMA ring configuration.
- * Keep the device awake until the PM wake path handles MT7927. */
- if (!mt76_is_usb(&dev->mt76) && !is_mt7927(&dev->mt76)) {
+ if (!mt76_is_usb(&dev->mt76)) {
dev->pm.enable_user = true;
dev->pm.enable = true;
dev->pm.ds_enable_user = true;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index 0dec25b320f8..0f76d9197230 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -416,8 +416,8 @@ static int mt7927_dma_init(struct mt792x_dev *dev)
napi_enable(&dev->mt76.tx_napi);
/* MT7927-specific GLO_CFG bits before DMA enable */
- mt76_set(dev, MT_WFDMA0_GLO_CFG, BIT(26)); /* ADDR_EXT_EN */
- mt76_clear(dev, MT_WFDMA0_GLO_CFG, BIT(20)); /* CSR_LBK_RX_Q_SEL_EN */
+ mt76_set(dev, MT_WFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_ADDR_EXT_EN);
+ mt76_clear(dev, MT_WFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_CSR_LBK_RX_Q_SEL_EN);
mt76_set(dev, MT_WFDMA0_GLO_CFG_EXT1, BIT(28));
mt76_set(dev, MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_FW_DWLD_BYPASS_DMASHDL);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
index 3177c6cc6eb5..32364f19007d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
@@ -166,6 +166,13 @@ int mt792x_dma_enable(struct mt792x_dev *dev)
mt76_set(dev, MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+ if (is_mt7927(&dev->mt76)) {
+ mt76_set(dev, MT_WFDMA0_GLO_CFG,
+ MT_WFDMA0_GLO_CFG_ADDR_EXT_EN);
+ mt76_clear(dev, MT_WFDMA0_GLO_CFG,
+ MT_WFDMA0_GLO_CFG_CSR_LBK_RX_Q_SEL_EN);
+ }
+
if (is_mt7925(&dev->mt76)) {
mt76_rmw(dev, MT_UWFDMA0_GLO_CFG_EXT1, BIT(28), BIT(28));
mt76_set(dev, MT_WFDMA0_INT_RX_PRI, 0x0F00);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
index 5497cfaab8d7..b364d0038653 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
@@ -299,7 +299,9 @@
#define MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN BIT(12)
#define MT_WFDMA0_GLO_CFG_RX_WB_DDONE BIT(13)
#define MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN BIT(15)
+#define MT_WFDMA0_GLO_CFG_CSR_LBK_RX_Q_SEL_EN BIT(20)
#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21)
+#define MT_WFDMA0_GLO_CFG_ADDR_EXT_EN BIT(26)
#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO BIT(27)
#define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO BIT(28)
#define MT_WFDMA0_GLO_CFG_CLK_GAT_DIS BIT(30)
--
2.53.0
^ permalink raw reply related
* [PATCH 09/18] wifi: mt76: mt7925: populate EHT 320MHz MCS map in sta_rec
From: Javier Tia @ 2026-03-07 0:33 UTC (permalink / raw)
To: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Deren Wu, Ming Yen Hsieh
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, Marcin FM,
Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
Thibaut François, 张旭涵, Javier Tia
In-Reply-To: <20260306-mt7927-wifi-support-v1-0-c77e7445511d@jetm.me>
The sta_rec_eht structure has a mcs_map_bw320 field, and the channel
width mapping includes NL80211_CHAN_WIDTH_320, but the 320MHz MCS/NSS
map was never copied from the station's EHT capabilities to the MCU TLV.
This prevents negotiation of 320MHz channel width even when both the
hardware and firmware advertise support for it.
Add the missing memcpy for the 320MHz MCS map, matching the existing
pattern for BW20, BW80, and BW160.
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index cf0fdea45cf7..8b5ffb240d52 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1683,6 +1683,7 @@ mt7925_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
memcpy(eht->mcs_map_bw20, &mcs_map->only_20mhz, sizeof(eht->mcs_map_bw20));
memcpy(eht->mcs_map_bw80, &mcs_map->bw._80, sizeof(eht->mcs_map_bw80));
memcpy(eht->mcs_map_bw160, &mcs_map->bw._160, sizeof(eht->mcs_map_bw160));
+ memcpy(eht->mcs_map_bw320, &mcs_map->bw._320, sizeof(eht->mcs_map_bw320));
}
static void
--
2.53.0
^ permalink raw reply related
* [PATCH 08/18] wifi: mt76: mt7925: guard mac_reset against MT7927 DMA destruction
From: Javier Tia @ 2026-03-07 0:33 UTC (permalink / raw)
To: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Deren Wu, Ming Yen Hsieh
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, Marcin FM,
Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
Thibaut François, 张旭涵, Javier Tia
In-Reply-To: <20260306-mt7927-wifi-support-v1-0-c77e7445511d@jetm.me>
On MT7927 hardware, the mac_reset path performs CLR_OWN and WPDMA logic
reset, both of which destroy DMA ring configuration. CLR_OWN triggers
the ROM to reinitialize WFDMA, and the logic reset pulse disrupts the
WFDMA state that mt7927_dma_init() carefully configured.
A full MT7927 reset requires re-running the DMA initialization sequence,
which is not yet implemented in the recovery path. Return -EOPNOTSUPP
with a warning so users know to reload the module.
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
index 1626a3684082..cf578a2cdc68 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
@@ -72,6 +72,14 @@ int mt7925e_mac_reset(struct mt792x_dev *dev)
const struct mt792x_irq_map *irq_map = dev->irq_map;
int i, err;
+ /* MT7927: CLR_OWN and WPDMA reset destroy DMA ring configuration.
+ * A full reset requires re-running mt7927_dma_init() which is not
+ * yet implemented in the recovery path. */
+ if (is_mt7927(&dev->mt76)) {
+ dev_warn(dev->mt76.dev, "MT7927 mac_reset not supported, reload module to recover\n");
+ return -EOPNOTSUPP;
+ }
+
mt792xe_mcu_drv_pmctrl(dev);
mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
--
2.53.0
^ permalink raw reply related
* [PATCH 07/18] wifi: mt76: mt7925: use irq_map fields instead of MT_INT_RX_DONE_ALL
From: Javier Tia @ 2026-03-07 0:33 UTC (permalink / raw)
To: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Deren Wu, Ming Yen Hsieh
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, Marcin FM,
Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
Thibaut François, 张旭涵, Javier Tia
In-Reply-To: <20260306-mt7927-wifi-support-v1-0-c77e7445511d@jetm.me>
The mac_reset and resume paths use the hardcoded MT_INT_RX_DONE_ALL
constant (bits 0-2) to re-enable RX interrupts. This is correct for
MT7925 (RX rings 0, 1, 2) but wrong for MT7927 which uses RX rings 4, 6,
7 (bits 12, 14, 15).
Replace with the per-chip irq_map fields so the correct interrupt bits
are enabled regardless of the chip variant.
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 5 ++++-
drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c | 5 ++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index dce9f30b68d7..0dec25b320f8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -766,7 +766,10 @@ static int _mt7925_pci_resume(struct device *device, bool restore)
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
mt76_connac_irq_enable(&dev->mt76,
dev->irq_map->tx.all_complete_mask |
- MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD);
+ dev->irq_map->rx.data_complete_mask |
+ dev->irq_map->rx.wm_complete_mask |
+ dev->irq_map->rx.wm2_complete_mask |
+ MT_INT_MCU_CMD);
mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE);
/* put dma enabled */
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
index 3072850c2752..1626a3684082 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
@@ -118,7 +118,10 @@ int mt7925e_mac_reset(struct mt792x_dev *dev)
mt76_wr(dev, dev->irq_map->host_irq_enable,
dev->irq_map->tx.all_complete_mask |
- MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD);
+ dev->irq_map->rx.data_complete_mask |
+ dev->irq_map->rx.wm_complete_mask |
+ dev->irq_map->rx.wm2_complete_mask |
+ MT_INT_MCU_CMD);
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
err = mt792xe_mcu_fw_pmctrl(dev);
--
2.53.0
^ permalink raw reply related
* [PATCH 06/18] wifi: mt76: mt7925: add MT7927 power management, DBDC, and CNM fixes
From: Javier Tia @ 2026-03-07 0:33 UTC (permalink / raw)
To: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Deren Wu, Ming Yen Hsieh
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, Marcin FM,
Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
Thibaut François, 张旭涵, Javier Tia
In-Reply-To: <20260306-mt7927-wifi-support-v1-0-c77e7445511d@jetm.me>
Three MT7927-specific fixes for WiFi functionality:
1. Disable runtime PM: Every CLR_OWN triggers the ROM to reinitialize
WFDMA, destroying DMA ring configuration. Disable PM until the wake
path properly handles MT7927.
2. Enable DBDC (dual-band): MT7927 firmware defaults to single-band
(2.4GHz only). MT7925 firmware handles DBDC automatically, but
MT7927 requires an explicit MCU_UNI_CMD(SET_DBDC_PARMS) with
mbmc_en=1. Without this, 5GHz scan requests are silently discarded.
3. Force CNM capability: MT7927 firmware lacks the connac2 feature
trailer, causing mt792x_get_mac80211_ops() to replace channel
context operations with emulated stubs. This prevents ROC (remain
on channel) before authentication, causing auth frames to be
silently dropped. Force the CNM flag and restore mt7925_ops.
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
drivers/net/wireless/mediatek/mt76/mt7925/init.c | 17 ++++++++++++++++-
drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 10 ++++++++++
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
index 3ce5d6fcc69d..1e9eadca3988 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
@@ -115,6 +115,18 @@ static int __mt7925_init_hardware(struct mt792x_dev *dev)
if (ret)
goto out;
+ /* MT7927: Enable DBDC (dual-band) mode. Without this, firmware
+ * defaults to 2.4GHz only and ignores 5GHz scan requests.
+ * MT7925 firmware handles DBDC automatically. */
+ if (is_mt7927(&dev->mt76)) {
+ ret = mt7925_mcu_set_dbdc(&dev->mphy, true);
+ if (ret) {
+ dev_warn(dev->mt76.dev,
+ "MT7927 DBDC enable failed: %d\n", ret);
+ ret = 0;
+ }
+ }
+
out:
return ret;
}
@@ -230,7 +242,10 @@ int mt7925_register_device(struct mt792x_dev *dev)
dev->pm.idle_timeout = MT792x_PM_TIMEOUT;
dev->pm.stats.last_wake_event = jiffies;
dev->pm.stats.last_doze_event = jiffies;
- if (!mt76_is_usb(&dev->mt76)) {
+ /* MT7927: disable power management. Every CLR_OWN triggers the
+ * ROM to reinitialize WFDMA, destroying DMA ring configuration.
+ * Keep the device awake until the PM wake path handles MT7927. */
+ if (!mt76_is_usb(&dev->mt76) && !is_mt7927(&dev->mt76)) {
dev->pm.enable_user = true;
dev->pm.enable = true;
dev->pm.ds_enable_user = true;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index ca9ccfe9975c..dce9f30b68d7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -527,6 +527,16 @@ static int mt7925_pci_probe(struct pci_dev *pdev,
goto err_free_pci_vec;
}
+ /* MT7927 firmware lacks the connac2 feature trailer, so
+ * mt792x_get_mac80211_ops() can't detect CNM support and
+ * replaces chanctx/ROC/mgd_prepare_tx ops with stubs.
+ * Force CNM and restore the original mt7925 ops. */
+ if ((pdev->device == 0x6639 || pdev->device == 0x7927) &&
+ !(features & MT792x_FW_CAP_CNM)) {
+ features |= MT792x_FW_CAP_CNM;
+ memcpy(ops, &mt7925_ops, sizeof(*ops));
+ }
+
mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), ops, &drv_ops);
if (!mdev) {
ret = -ENOMEM;
--
2.53.0
^ permalink raw reply related
* [PATCH 05/18] wifi: mt76: mt7925: skip CLR_OWN in mt7925e_mcu_init for MT7927
From: Javier Tia @ 2026-03-07 0:33 UTC (permalink / raw)
To: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Deren Wu, Ming Yen Hsieh
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, Marcin FM,
Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
Thibaut François, 张旭涵, Javier Tia
In-Reply-To: <20260306-mt7927-wifi-support-v1-0-c77e7445511d@jetm.me>
On MT7927 hardware, every CLR_OWN triggers the ROM bootloader to
reinitialize the WFDMA engine, destroying all DMA ring configuration
(base addresses, prefetch settings, descriptor pointers).
The DMA rings are already properly initialized by mt7927_dma_init()
which performs a controlled CLR_OWN before ring setup. Skip the
SET_OWN/CLR_OWN cycle in mt7925e_mcu_init() to preserve that
configuration.
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
index 6cceff88c656..206d525eb550 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
@@ -35,13 +35,20 @@ int mt7925e_mcu_init(struct mt792x_dev *dev)
dev->mt76.mcu_ops = &mt7925_mcu_ops;
- err = mt792xe_mcu_fw_pmctrl(dev);
- if (err)
- return err;
+ if (is_mt7927(&dev->mt76)) {
+ /* MT7927: CLR_OWN was already done in mt7927_dma_init().
+ * The ROM re-initializes WFDMA on every CLR_OWN, wiping
+ * ring and prefetch config. Skip SET_OWN/CLR_OWN here
+ * to preserve DMA state. */
+ } else {
+ err = mt792xe_mcu_fw_pmctrl(dev);
+ if (err)
+ return err;
- err = __mt792xe_mcu_drv_pmctrl(dev);
- if (err)
- return err;
+ err = __mt792xe_mcu_drv_pmctrl(dev);
+ if (err)
+ return err;
+ }
mt76_rmw_field(dev, MT_PCIE_MAC_PM, MT_PCIE_MAC_PM_L0S_DIS, 1);
--
2.53.0
^ permalink raw reply related
* [PATCH 04/18] wifi: mt76: mt7925: add MT7927 DMA ring layout, IRQ map, and prefetch
From: Javier Tia @ 2026-03-07 0:33 UTC (permalink / raw)
To: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Deren Wu, Ming Yen Hsieh
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, Marcin FM,
Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
Thibaut François, 张旭涵, Javier Tia
In-Reply-To: <20260306-mt7927-wifi-support-v1-0-c77e7445511d@jetm.me>
MT7927 uses different DMA ring indices than MT7925:
- RX MCU events: ring 6 (MT7925 uses ring 0)
- RX data: ring 4 (MT7925 uses ring 2)
- RX auxiliary: ring 7 (management frames)
- TX rings are identical
Add mt7927_dma_init() with the correct ring allocation, a dedicated
mt7927_irq_map with matching interrupt enable bits (BIT(12), BIT(14),
BIT(15) for RX rings 4, 6, 7), and MT7927 packed prefetch register
configuration in mt792x_dma_prefetch().
The DMA init performs a controlled SET_OWN/CLR_OWN sequence after CBTOP
and CBInfra initialization, since CLR_OWN triggers the ROM to initialize
WFDMA.
Ring layout and prefetch values derived from Loong0x00's
reverse-engineered MT7927 driver.
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h | 7 ++
drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 131 ++++++++++++++++++++-
drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 18 ++-
drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 10 ++
4 files changed, 163 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 6b9bf1b89032..ba3d2c4de4ce 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -126,6 +126,13 @@ enum mt7925_rxq_id {
MT7925_RXQ_MCU_WM2, /* for tx done */
};
+/* MT7927 uses different RX ring indices than MT7925 */
+enum mt7927_rxq_id {
+ MT7927_RXQ_BAND0 = 4,
+ MT7927_RXQ_MCU_WM = 6,
+ MT7927_RXQ_DATA2 = 7,
+};
+
enum {
MODE_OPEN = 0,
MODE_SHARED = 1,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index c16ec05c5601..ca9ccfe9975c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -324,6 +324,127 @@ static void mt7927_cbtop_remap(struct mt792x_dev *dev)
mt76_rr(dev, MT_CBINFRA_MISC0_REMAP_WF);
}
+static int mt7927_dma_init(struct mt792x_dev *dev)
+{
+ int ret;
+
+ mt76_dma_attach(&dev->mt76);
+
+ /* Do SET_OWN -> CLR_OWN now that CBTOP and CBInfra are ready.
+ * CLR_OWN triggers the ROM to initialize WFDMA properly. */
+ ret = mt792xe_mcu_fw_pmctrl(dev);
+ if (ret)
+ return ret;
+
+ ret = __mt792xe_mcu_drv_pmctrl(dev);
+ if (ret)
+ return ret;
+
+ /* Clear pending interrupts from previous state */
+ mt76_wr(dev, MT_WFDMA0_HOST_INT_STA, ~0);
+
+ /* Disable DMA */
+ mt76_clear(dev, MT_WFDMA0_GLO_CFG,
+ MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_RX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN |
+ MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
+ MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
+ wmb();
+
+ if (!mt76_poll_msec_tick(dev, MT_WFDMA0_GLO_CFG,
+ MT_WFDMA0_GLO_CFG_TX_DMA_BUSY |
+ MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 100, 1))
+ return -ETIMEDOUT;
+
+ /* Reset DMA descriptor pointers */
+ mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0);
+ mt76_wr(dev, MT_WFDMA0_RST_DRX_PTR, ~0);
+ wmb();
+ msleep(10);
+
+ /* init tx queue - ring 0 */
+ ret = mt76_connac_init_tx_queues(dev->phy.mt76, MT7925_TXQ_BAND0,
+ MT7925_TX_RING_SIZE,
+ MT_TX_RING_BASE, NULL, 0);
+ if (ret)
+ return ret;
+
+ mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4);
+
+ /* command to WM - ring 15 */
+ ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM,
+ MT7925_TXQ_MCU_WM,
+ MT7925_TX_MCU_RING_SIZE, MT_TX_RING_BASE);
+ if (ret)
+ return ret;
+
+ /* firmware download - ring 16 */
+ ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL,
+ MT7925_TXQ_FWDL,
+ MT7925_TX_FWDL_RING_SIZE, MT_TX_RING_BASE);
+ if (ret)
+ return ret;
+
+ /* rx MCU events - ring 6 (MT7925 uses ring 0) */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU],
+ MT7927_RXQ_MCU_WM, MT7925_RX_MCU_RING_SIZE,
+ MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE);
+ if (ret)
+ return ret;
+
+ /* rx data - ring 4 (MT7925 uses ring 2) */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
+ MT7927_RXQ_BAND0, MT7925_RX_RING_SIZE,
+ MT_RX_BUF_SIZE, MT_RX_DATA_RING_BASE);
+ if (ret)
+ return ret;
+
+ /* rx auxiliary - ring 7: management frames */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA],
+ MT7927_RXQ_DATA2, MT7925_RX_MCU_RING_SIZE,
+ MT_RX_BUF_SIZE, MT_RX_DATA_RING_BASE);
+ if (ret)
+ return ret;
+
+ ret = mt76_init_queues(dev, mt792x_poll_rx);
+ if (ret < 0)
+ return ret;
+
+ netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
+ mt792x_poll_tx);
+ napi_enable(&dev->mt76.tx_napi);
+
+ /* MT7927-specific GLO_CFG bits before DMA enable */
+ mt76_set(dev, MT_WFDMA0_GLO_CFG, BIT(26)); /* ADDR_EXT_EN */
+ mt76_clear(dev, MT_WFDMA0_GLO_CFG, BIT(20)); /* CSR_LBK_RX_Q_SEL_EN */
+ mt76_set(dev, MT_WFDMA0_GLO_CFG_EXT1, BIT(28));
+ mt76_set(dev, MT_WFDMA0_GLO_CFG,
+ MT_WFDMA0_GLO_CFG_FW_DWLD_BYPASS_DMASHDL);
+
+ ret = mt792x_dma_enable(dev);
+ if (ret)
+ return ret;
+
+ /* Enable interrupts synchronously */
+ mt76_wr(dev, MT_WFDMA0_HOST_INT_ENA, dev->mt76.mmio.irqmask);
+
+ return 0;
+}
+
+static const struct mt792x_irq_map mt7927_irq_map = {
+ .host_irq_enable = MT_WFDMA0_HOST_INT_ENA,
+ .tx = {
+ .all_complete_mask = MT_INT_TX_DONE_ALL,
+ .mcu_complete_mask = MT_INT_TX_DONE_MCU,
+ },
+ .rx = {
+ .data_complete_mask = HOST_RX_DONE_INT_ENA4,
+ .wm_complete_mask = HOST_RX_DONE_INT_ENA6,
+ .wm2_complete_mask = HOST_RX_DONE_INT_ENA7,
+ },
+};
+
static int mt7925_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -417,7 +538,10 @@ static int mt7925_pci_probe(struct pci_dev *pdev,
dev = container_of(mdev, struct mt792x_dev, mt76);
dev->fw_features = features;
dev->hif_ops = &mt7925_pcie_ops;
- dev->irq_map = &irq_map;
+ if (is_mt7927_hw)
+ dev->irq_map = &mt7927_irq_map;
+ else
+ dev->irq_map = &irq_map;
mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]);
tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev);
@@ -489,7 +613,10 @@ static int mt7925_pci_probe(struct pci_dev *pdev,
if (ret)
goto err_free_dev;
- ret = mt7925_dma_init(dev);
+ if (is_mt7927_hw)
+ ret = mt7927_dma_init(dev);
+ else
+ ret = mt7925_dma_init(dev);
if (ret)
goto err_free_irq;
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
index 1ddec7788b66..3177c6cc6eb5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
@@ -90,7 +90,23 @@ EXPORT_SYMBOL_GPL(mt792x_rx_poll_complete);
#define PREFETCH(base, depth) ((base) << 16 | (depth))
static void mt792x_dma_prefetch(struct mt792x_dev *dev)
{
- if (is_mt7925(&dev->mt76)) {
+ if (is_mt7927(&dev->mt76)) {
+ /* Trigger prefetch controller reset before reprogramming */
+ mt76_wr(dev, MT_WFDMA_PREFETCH_CTRL,
+ mt76_rr(dev, MT_WFDMA_PREFETCH_CTRL));
+ /* MT7927 uses packed prefetch registers */
+ mt76_wr(dev, MT_WFDMA_PREFETCH_CFG0, 0x660077);
+ mt76_wr(dev, MT_WFDMA_PREFETCH_CFG1, 0x1100);
+ mt76_wr(dev, MT_WFDMA_PREFETCH_CFG2, 0x30004f);
+ mt76_wr(dev, MT_WFDMA_PREFETCH_CFG3, 0x542200);
+ /* per-ring EXT_CTRL */
+ mt76_wr(dev, MT_WFDMA0_RX_RING4_EXT_CTRL, PREFETCH(0x0000, 0x8));
+ mt76_wr(dev, MT_WFDMA0_RX_RING6_EXT_CTRL, PREFETCH(0x0080, 0x8));
+ mt76_wr(dev, MT_WFDMA0_RX_RING7_EXT_CTRL, PREFETCH(0x0100, 0x4));
+ mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0140, 0x4));
+ mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0180, 0x10));
+ mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0280, 0x4));
+ } else if (is_mt7925(&dev->mt76)) {
/* rx ring */
mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4));
mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4));
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
index 0f75dca0a96c..5497cfaab8d7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
@@ -363,6 +363,16 @@
#define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR(0x44)
#define MT_WFDMA_EXT_CSR_HIF_MISC_BUSY BIT(0)
+/* MT7927 packed prefetch registers */
+#define MT_WFDMA_PREFETCH_CTRL MT_WFDMA_EXT_CSR(0x30)
+#define MT_WFDMA_PREFETCH_CFG0 MT_WFDMA_EXT_CSR(0xf0)
+#define MT_WFDMA_PREFETCH_CFG1 MT_WFDMA_EXT_CSR(0xf4)
+#define MT_WFDMA_PREFETCH_CFG2 MT_WFDMA_EXT_CSR(0xf8)
+#define MT_WFDMA_PREFETCH_CFG3 MT_WFDMA_EXT_CSR(0xfc)
+
+/* MT7927 GLO_CFG extended register */
+#define MT_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4)
+
#define MT_SWDEF_BASE 0x41f200
#define MT_SWDEF(ofs) (MT_SWDEF_BASE + (ofs))
#define MT_SWDEF_MODE MT_SWDEF(0x3c)
--
2.53.0
^ permalink raw reply related
* [PATCH 03/18] wifi: mt76: mt7925: add MT7927 CBTOP remap and chip initialization
From: Javier Tia @ 2026-03-07 0:33 UTC (permalink / raw)
To: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Deren Wu, Ming Yen Hsieh
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, Marcin FM,
Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
Thibaut François, 张旭涵, Javier Tia
In-Reply-To: <20260306-mt7927-wifi-support-v1-0-c77e7445511d@jetm.me>
The MT7927 combo chip has an additional bus fabric called CBInfra
(ConnectaBus Infrastructure) between PCIe and the WiFi subsystem.
Without configuring the CBTOP address remap, all MMIO reads to WiFi
registers return zero.
Add mt7927_cbtop_remap() to configure the PCIe address mapping for WiFi
and Bluetooth subsystems, and mt7927_chip_init() to perform the CBInfra
initialization sequence:
1. EMI sleep protect enable
2. WF subsystem reset via CBInfra RGU
3. MCU ownership acquisition
4. Poll ROMCODE_INDEX for MCU idle (0x1D1E)
5. MCIF remap for host DMA
6. PCIe sleep disable
Skip the early CLR_OWN in probe for MT7927 since CBTOP remap is not yet
configured at that point - the ROM's WFDMA init would fail.
Register values derived from Loong0x00's reverse-engineered MT7927
driver.
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 90 ++++++++++++++++++++++--
drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 18 +++++
2 files changed, 101 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index 54eac5c6e509..c16ec05c5601 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -270,6 +270,60 @@ static int mt7925_dma_init(struct mt792x_dev *dev)
return mt792x_dma_enable(dev);
}
+static int mt7927_chip_init(struct mt792x_dev *dev)
+{
+ struct mt76_dev *mdev = &dev->mt76;
+ u32 val;
+
+ /* EMI sleep protect */
+ mt76_rmw_field(dev, MT_HW_EMI_CTL, MT_HW_EMI_CTL_SLPPROT_EN, 1);
+
+ /* WF subsystem reset via CBInfra RGU */
+ mt76_set(dev, MT_CBINFRA_RGU_WF_RST, MT_CBINFRA_RGU_WF_RST_WF_SUBSYS);
+ msleep(1);
+ mt76_clear(dev, MT_CBINFRA_RGU_WF_RST,
+ MT_CBINFRA_RGU_WF_RST_WF_SUBSYS);
+ msleep(5);
+
+ /* MCU ownership */
+ mt76_wr(dev, MT_CBINFRA_MCU_OWN_SET, BIT(0));
+
+ /* Poll ROMCODE_INDEX for MCU idle */
+ if (!__mt76_poll_msec(mdev, MT_ROMCODE_INDEX,
+ 0xffff, MT_MCU_IDLE_VALUE, 2000)) {
+ val = mt76_rr(dev, MT_ROMCODE_INDEX);
+ dev_err(mdev->dev,
+ "MT7927 MCU idle timeout (ROMCODE_INDEX=0x%04x)\n",
+ val & 0xffff);
+ return -ETIMEDOUT;
+ }
+
+ /* MCIF remap - MCU needs this to DMA to host memory */
+ mt76_wr(dev, MT_MCIF_REMAP_WF_1_BA, MT_MCIF_REMAP_WF_1_BA_VAL);
+
+ /* Disable PCIe sleep */
+ mt76_wr(dev, MT_CBINFRA_SLP_CTRL, 0xffffffff);
+
+ /* Clear CONNINFRA wakeup */
+ mt76_wr(dev, MT_CBINFRA_WAKEPU_TOP, 0x0);
+
+ return 0;
+}
+
+static void mt7927_cbtop_remap(struct mt792x_dev *dev)
+{
+ /* CONNINFRA wakeup - required before CBInfra register access */
+ mt76_wr(dev, MT_CBINFRA_WAKEPU_TOP, 0x1);
+ usleep_range(1000, 2000);
+
+ /* Configure CBTOP PCIe address remap for WF and BT */
+ mt76_wr(dev, MT_CBINFRA_MISC0_REMAP_WF, MT_CBINFRA_REMAP_WF_VAL);
+ mt76_wr(dev, MT_CBINFRA_MISC0_REMAP_BT, MT_CBINFRA_REMAP_BT_VAL);
+
+ /* Readback to push writes */
+ mt76_rr(dev, MT_CBINFRA_MISC0_REMAP_WF);
+}
+
static int mt7925_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -314,6 +368,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev,
struct mt76_bus_ops *bus_ops;
struct mt792x_dev *dev;
struct mt76_dev *mdev;
+ bool is_mt7927_hw;
u8 features;
int ret;
u16 cmd;
@@ -385,24 +440,45 @@ static int mt7925_pci_probe(struct pci_dev *pdev,
if (!mt7925_disable_aspm && mt76_pci_aspm_supported(pdev))
dev->aspm_supported = true;
+ is_mt7927_hw = (pdev->device == 0x6639 || pdev->device == 0x7927);
+
ret = __mt792x_mcu_fw_pmctrl(dev);
if (ret)
goto err_free_dev;
- ret = __mt792xe_mcu_drv_pmctrl(dev);
- if (ret)
- goto err_free_dev;
+ if (!is_mt7927_hw) {
+ ret = __mt792xe_mcu_drv_pmctrl(dev);
+ if (ret)
+ goto err_free_dev;
+ }
+
+ if (is_mt7927_hw)
+ mt7927_cbtop_remap(dev);
mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) |
(mt76_rr(dev, MT_HW_REV) & 0xff);
dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev);
- mt76_rmw_field(dev, MT_HW_EMI_CTL, MT_HW_EMI_CTL_SLPPROT_EN, 1);
+ /* Force chip ID for MT7927 hardware if CHIPID read returns garbage */
+ if (is_mt7927_hw && (mdev->rev >> 16) != 0x7927) {
+ dev_info(mdev->dev,
+ "MT7927 raw CHIPID=0x%04x, forcing chip=0x7927\n",
+ (u16)(mdev->rev >> 16));
+ mdev->rev = (0x7927 << 16) | (mdev->rev & 0xff);
+ }
- ret = mt792x_wfsys_reset(dev);
- if (ret)
- goto err_free_dev;
+ if (is_mt7927_hw) {
+ ret = mt7927_chip_init(dev);
+ if (ret)
+ goto err_free_dev;
+ } else {
+ mt76_rmw_field(dev, MT_HW_EMI_CTL,
+ MT_HW_EMI_CTL_SLPPROT_EN, 1);
+ ret = mt792x_wfsys_reset(dev);
+ if (ret)
+ goto err_free_dev;
+ }
mt76_wr(dev, irq_map.host_irq_enable, 0);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
index acf627aed609..0f75dca0a96c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
@@ -484,4 +484,22 @@
#define WFSYS_SW_RST_B BIT(0)
#define WFSYS_SW_INIT_DONE BIT(4)
+/* CBInfra registers - MT7927 combo chip */
+#define MT_CBINFRA_WAKEPU_TOP 0xe01a0
+#define MT_CBINFRA_MISC0_REMAP_WF 0x1f6554
+#define MT_CBINFRA_MISC0_REMAP_BT 0x1f6558
+#define MT_CBINFRA_RGU_WF_RST 0x1f8600
+#define MT_CBINFRA_RGU_WF_RST_WF_SUBSYS BIT(4)
+#define MT_CBINFRA_MCU_OWN_SET 0x1f5034
+#define MT_CBINFRA_SLP_CTRL 0x1f5018
+#define MT_ROMCODE_INDEX 0xc1604
+#define MT_MCU_IDLE_VALUE 0x1d1e
+#define MT_MCIF_REMAP_WF_1_BA 0xd1034
+
+/* CBInfra CBTOP remap values */
+#define MT_CBINFRA_REMAP_WF_VAL 0x74037001
+#define MT_CBINFRA_REMAP_BT_VAL 0x70007000
+
+#define MT_MCIF_REMAP_WF_1_BA_VAL 0x18051803
+
#endif /* __MT792X_REGS_H */
--
2.53.0
^ permalink raw reply related
* [PATCH 02/18] wifi: mt76: mt7925: add MT7927 firmware paths and PCI device IDs
From: Javier Tia @ 2026-03-07 0:33 UTC (permalink / raw)
To: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Deren Wu, Ming Yen Hsieh
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, Marcin FM,
Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
Thibaut François, 张旭涵, Javier Tia
In-Reply-To: <20260306-mt7927-wifi-support-v1-0-c77e7445511d@jetm.me>
Add PCI device IDs for MT7927 (14c3:7927) and MT7927 (14c3:6639)
hardware variants, along with firmware path definitions for the MT7927
WiFi firmware.
Known hardware using these IDs:
- ASUS ROG Crosshair X870E Hero (14c3:7927)
- Lenovo Legion Pro 7 16ARX9 (14c3:7927)
- Foxconn/Azurewave modules (14c3:6639)
- AMD RZ738 (MediaTek MT7927) (14c3:0738)
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 6 ++++++
drivers/net/wireless/mediatek/mt76/mt792x.h | 6 ++++++
2 files changed, 12 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index c4161754c01d..54eac5c6e509 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -16,6 +16,10 @@ static const struct pci_device_id mt7925_pci_device_table[] = {
.driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0717),
.driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7927),
+ .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x6639),
+ .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM },
{ },
};
@@ -633,6 +637,8 @@ module_pci_driver(mt7925_pci_driver);
MODULE_DEVICE_TABLE(pci, mt7925_pci_device_table);
MODULE_FIRMWARE(MT7925_FIRMWARE_WM);
MODULE_FIRMWARE(MT7925_ROM_PATCH);
+MODULE_FIRMWARE(MT7927_FIRMWARE_WM);
+MODULE_FIRMWARE(MT7927_ROM_PATCH);
MODULE_AUTHOR("Deren Wu <deren.wu@mediatek.com>");
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
MODULE_DESCRIPTION("MediaTek MT7925E (PCIe) wireless driver");
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 8388638ed550..38790ef83e51 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -45,11 +45,13 @@
#define MT7921_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1.bin"
#define MT7922_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7922_1.bin"
#define MT7925_FIRMWARE_WM "mediatek/mt7925/WIFI_RAM_CODE_MT7925_1_1.bin"
+#define MT7927_FIRMWARE_WM "mediatek/mt7927/WIFI_RAM_CODE_MT6639_2_1.bin"
#define MT7920_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1a_2_hdr.bin"
#define MT7921_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1_2_hdr.bin"
#define MT7922_ROM_PATCH "mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin"
#define MT7925_ROM_PATCH "mediatek/mt7925/WIFI_MT7925_PATCH_MCU_1_1_hdr.bin"
+#define MT7927_ROM_PATCH "mediatek/mt7927/WIFI_MT6639_PATCH_MCU_2_1_hdr.bin"
#define MT792x_SDIO_HDR_TX_BYTES GENMASK(15, 0)
#define MT792x_SDIO_HDR_PKT_TYPE GENMASK(17, 16)
@@ -454,6 +456,8 @@ static inline char *mt792x_ram_name(struct mt792x_dev *dev)
return MT7922_FIRMWARE_WM;
case 0x7925:
return MT7925_FIRMWARE_WM;
+ case 0x7927:
+ return MT7927_FIRMWARE_WM;
default:
return MT7921_FIRMWARE_WM;
}
@@ -468,6 +472,8 @@ static inline char *mt792x_patch_name(struct mt792x_dev *dev)
return MT7922_ROM_PATCH;
case 0x7925:
return MT7925_ROM_PATCH;
+ case 0x7927:
+ return MT7927_ROM_PATCH;
default:
return MT7921_ROM_PATCH;
}
--
2.53.0
^ permalink raw reply related
* [PATCH 01/18] wifi: mt76: mt7925: add MT7927 chip ID helpers
From: Javier Tia @ 2026-03-07 0:33 UTC (permalink / raw)
To: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Deren Wu, Ming Yen Hsieh
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, Marcin FM,
Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
Thibaut François, 张旭涵, Javier Tia
In-Reply-To: <20260306-mt7927-wifi-support-v1-0-c77e7445511d@jetm.me>
The MediaTek MT7927 (Filogic 380) combo chip uses MT7927 WiFi silicon
that is architecturally compatible with MT7925. Extend is_mt7925() to
match chip ID 0x7927, and add is_mt7927() for code paths that need
MT7927-specific handling.
Also add 0x7927 to is_mt76_fw_txp() to match MT7925's TXP format.
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
drivers/net/wireless/mediatek/mt76/mt76_connac.h | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
index 813d61bffc2c..bfd5b05c2b20 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
@@ -174,7 +174,12 @@ extern const struct wiphy_wowlan_support mt76_connac_wowlan_support;
static inline bool is_mt7925(struct mt76_dev *dev)
{
- return mt76_chip(dev) == 0x7925;
+ return mt76_chip(dev) == 0x7925 || mt76_chip(dev) == 0x7927;
+}
+
+static inline bool is_mt7927(struct mt76_dev *dev)
+{
+ return mt76_chip(dev) == 0x7927;
}
static inline bool is_mt7920(struct mt76_dev *dev)
@@ -272,6 +277,7 @@ static inline bool is_mt76_fw_txp(struct mt76_dev *dev)
case 0x7920:
case 0x7922:
case 0x7925:
+ case 0x7927:
case 0x7663:
case 0x7622:
return false;
--
2.53.0
^ permalink raw reply related
* [PATCH 00/18] wifi: mt76: mt7925: add MT7927 (Filogic 380) support
From: Javier Tia @ 2026-03-07 0:33 UTC (permalink / raw)
To: linux-wireless, Felix Fietkau, Lorenzo Bianconi, Ryder Lee,
Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Deren Wu, Ming Yen Hsieh
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, Marcin FM,
Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
Thibaut François, 张旭涵, Javier Tia
This series adds support for the MediaTek MT7927 (Filogic 380) combo
WiFi 7 + BT 5.4 module to the mt7925 driver. The MT7927 uses PCI ID
14c3:7927 (some hardware reports 14c3:6639) and shares the mt7925
firmware interface but requires different DMA ring layout, IRQ mapping,
chip initialization, and power management handling.
Tested hardware:
- ASUS ROG Crosshair X870E Hero (BT 0489:e13a, WiFi 14c3:6639)
- ASUS ProArt X870E-Creator WiFi (BT 0489:e13a / 13d3:3588, WiFi 14c3:6639)
- ASUS ROG Strix X870E-E (BT 0489:e13a, WiFi 14c3:7927)
- Gigabyte X870E Aorus Master X3D (BT 0489:e10f, WiFi 14c3:7927)
- Gigabyte Z790 AORUS MASTER X (BT 0489:e10f, WiFi 14c3:7927)
- Lenovo Legion Pro 7 16ARX9 (BT 0489:e0fa, WiFi 14c3:7927)
- TP-Link Archer TBE550E PCIe (BT 0489:e116, WiFi 14c3:7927)
- EDUP EP-MT7927BE M.2 card (WiFi 14c3:7927)
- Foxconn/Azurewave M.2 modules (WiFi 14c3:6639)
- AMD RZ738 reference design (WiFi 14c3:0738)
Tested on Arch Linux, CachyOS, EndeavourOS, Fedora (Bazzite), NixOS,
openSUSE Tumbleweed, and Ubuntu across kernels 6.13-6.19.
What works:
- WiFi 7 with EHT 320MHz on 2.4/5/6 GHz bands
- 320MHz data path verified at 841 Mbps (iperf3 -t30 -P8)
- PCIe initialization with CBTOP remap and MT7927-specific DMA layout
- Runtime power management and suspend/resume (S3)
- DBDC (dual-band concurrent) mode
- Explicit band_idx assignment for stable 5GHz/6GHz operation
- ASPM disabled for MT7927 to prevent throughput collapse
Known limitations (planned as follow-up series):
- MLO (Multi-Link Operation): tested working on 5GHz+2.4GHz STR
(776 Mbps) but requires additional patches for link lifetime
and error handling. Sean Wang's series [1] addresses these;
MLO support will be submitted as a follow-up on top of that.
[1] https://lore.kernel.org/linux-wireless/20260306232238.2039675-1-sean.wang@kernel.org/
- TX retransmissions: elevated retry rate on all bands, firmware-side
rate adaptation issue not addressable in the driver.
Patches 1-11 bring up the basic driver (chip ID, firmware, DMA, init,
power management, 320MHz MCS, mac_reset guard, IRQ map, probe fix).
Patch 12 advertises EHT 320MHz capabilities, guarded by is_mt7927().
Patches 13-14 fix DMASHDL PM wake and disable ASPM.
Patch 15 adds 320MHz bandwidth handling in RXV/TXS status reporting.
Patch 16 fixes stale pointer comparisons in change_vif_links.
Patch 17 assigns explicit band_idx for stable 5GHz/6GHz operation.
Patch 18 adds the missing 320MHz case in bss_rlm_tlv() so firmware
receives the correct bandwidth when associated to a 320MHz BSS.
The WiFi firmware ships as part of the linux-firmware package
(mediatek/mt6639/ directory - the firmware uses the mobile SoC codename).
To: linux-wireless@vger.kernel.org
To: Felix Fietkau <nbd@nbd.name>
To: Lorenzo Bianconi <lorenzo@kernel.org>
To: Ryder Lee <ryder.lee@mediatek.com>
To: Shayne Chen <shayne.chen@mediatek.com>
To: Sean Wang <sean.wang@mediatek.com>
To: Matthias Brugger <matthias.bgg@gmail.com>
To: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
To: Deren Wu <deren.wu@mediatek.com>
To: Ming Yen Hsieh <mingyen.hsieh@mediatek.com>
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-mediatek@lists.infradead.org
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
Javier Tia (18):
wifi: mt76: mt7925: add MT7927 chip ID helpers
wifi: mt76: mt7925: add MT7927 firmware paths and PCI device IDs
wifi: mt76: mt7925: add MT7927 CBTOP remap and chip initialization
wifi: mt76: mt7925: add MT7927 DMA ring layout, IRQ map, and prefetch
wifi: mt76: mt7925: skip CLR_OWN in mt7925e_mcu_init for MT7927
wifi: mt76: mt7925: add MT7927 power management, DBDC, and CNM fixes
wifi: mt76: mt7925: use irq_map fields instead of MT_INT_RX_DONE_ALL
wifi: mt76: mt7925: guard mac_reset against MT7927 DMA destruction
wifi: mt76: mt7925: populate EHT 320MHz MCS map in sta_rec
wifi: mt76: mt7925: enable MT7927 runtime power management
wifi: mt76: mt7925: fix uninitialized is_mt7927_hw in probe
wifi: mt76: mt7925: advertise EHT 320MHz capabilities for 6GHz band
wifi: mt76: mt7925: restore FW_DWLD_BYPASS_DMASHDL in MT7927 PM wake path
wifi: mt76: mt7925: disable ASPM for MT7927 to fix throughput collapse
wifi: mt76: mt7925: handle 320MHz bandwidth in RXV and TXS
wifi: mt76: mt7925: fix stale pointer comparisons in change_vif_links
wifi: mt76: mt7925: fix MT7927 band_idx for stable 5GHz/6GHz operation
wifi: mt76: mt7925: add 320MHz bandwidth to bss_rlm_tlv
drivers/net/wireless/mediatek/mt76/mt76_connac.h | 8 +-
drivers/net/wireless/mediatek/mt76/mt7925/init.c | 12 +
drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 9 +
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 72 +++++-
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 59 ++++-
drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h | 12 +
drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 248 ++++++++++++++++++++-
.../net/wireless/mediatek/mt76/mt7925/pci_mac.c | 13 +-
.../net/wireless/mediatek/mt76/mt7925/pci_mcu.c | 19 +-
drivers/net/wireless/mediatek/mt76/mt792x.h | 6 +
drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 26 ++-
drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 30 +++
12 files changed, 479 insertions(+), 35 deletions(-)
---
base-commit: 97492c019da4b62df83255e968b23b81c0315530
change-id: 20260305-mt7927-wifi-support-02f9738a3962
Best regards,
--
Javier Tia <floss@jetm.me>
^ permalink raw reply
* [RFC 2/2] wifi: iwlwifi: mld: Support force-cleanup op
From: greearb @ 2026-03-07 0:01 UTC (permalink / raw)
To: linux-wireless; +Cc: Ben Greear
In-Reply-To: <20260307000131.2527892-1-greearb@candelatech.com>
From: Ben Greear <greearb@candelatech.com>
This lets mac80211 force the driver to clean up any lingering
configuration, fixing use-after-free in case of unrecoverable
hardware failure.
Signed-off-by: Ben Greear <greearb@candelatech.com>
---
drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 4102ee924757..d9ba77eb20eb 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -500,6 +500,18 @@ iwl_mld_restart_cleanup(struct iwl_mld *mld)
ieee80211_wake_queues(mld->hw);
}
+/* mac80211 thinks our driver/firmware/hardware has crashed
+ * and cannot be recovered. Force clean any existing configuration
+ * (stas, etc), as mac80211 will not attempt further cleanup.
+ */
+static void iwl_mld_mac80211_force_cleanup(struct ieee80211_hw *hw)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ IWL_ERR(mld, "mac80211-force-cleanup called, calling mld_restart_cleanup.\n");
+ iwl_mld_restart_cleanup(mld);
+}
+
static
int iwl_mld_mac80211_start(struct ieee80211_hw *hw)
{
@@ -2963,6 +2975,7 @@ const struct ieee80211_ops iwl_mld_hw_ops = {
.start = iwl_mld_mac80211_start,
.stop = iwl_mld_mac80211_stop,
.config = iwl_mld_mac80211_config,
+ .force_cleanup = iwl_mld_mac80211_force_cleanup,
.add_interface = iwl_mld_mac80211_add_interface,
.remove_interface = iwl_mld_mac80211_remove_interface,
.conf_tx = iwl_mld_mac80211_conf_tx,
--
2.42.0
^ permalink raw reply related
* [RFC 1/2] wifi: mac80211: Add force-cleanup call to driver.
From: greearb @ 2026-03-07 0:01 UTC (permalink / raw)
To: linux-wireless; +Cc: Ben Greear
From: Ben Greear <greearb@candelatech.com>
When hardware is determined by mac80211 to be in non-recoverable
state, then SDATA_IN_DRIVER flag is removed, and mac80211 will no
longer do any 'graceful' teardown of the objects in the driver.
This was causing use-after-free crashes in the iwlwifi driver
since it's logic to do internal cleanup is not quite right for
some reason.
Add an explicit callback to the driver to tell it to clean up
whatever it needs to clean up in case mac80211 considers it
dead.
Signed-off-by: Ben Greear <greearb@candelatech.com>
---
RFC: I'm curious if something like this is wanted upstream?
include/net/mac80211.h | 7 +++++++
net/mac80211/driver-ops.h | 8 ++++++++
net/mac80211/util.c | 5 +++++
3 files changed, 20 insertions(+)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index ae82fb5f2254..faa8c4f52794 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -3942,6 +3942,12 @@ struct ieee80211_prep_tx_info {
* you should ensure to cancel it on this callback.
* Must be implemented and can sleep.
*
+ * @force_cleanup: Called after mac80211 determines the
+ * driver/firmware/hardware has failed and cannot
+ * be restarted. SDATA_IN_DRIVER is false at this point,
+ * so normal cleanup will not happen. This force_cleanup
+ * operation lets the driver do any needed houskeeping.
+ *
* @suspend: Suspend the device; mac80211 itself will quiesce before and
* stop transmitting and doing any other configuration, and then
* ask the device to suspend. This is only invoked when WoWLAN is
@@ -4584,6 +4590,7 @@ struct ieee80211_ops {
struct sk_buff *skb);
int (*start)(struct ieee80211_hw *hw);
void (*stop)(struct ieee80211_hw *hw, bool suspend);
+ void (*force_cleanup)(struct ieee80211_hw *hw);
#ifdef CONFIG_PM
int (*suspend)(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
int (*resume)(struct ieee80211_hw *hw);
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index fb98f6ea30a8..12970bdcf7b9 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -308,6 +308,14 @@ static inline void drv_cancel_hw_scan(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+static inline void
+drv_force_cleanup(struct ieee80211_local *local)
+{
+ lockdep_assert_wiphy(local->hw.wiphy);
+ if (local->ops->force_cleanup)
+ local->ops->force_cleanup(&local->hw);
+}
+
static inline int
drv_sched_scan_start(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 1347e4933888..e2979c411c64 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1740,6 +1740,11 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
*/
list_for_each_entry(ctx, &local->chanctx_list, list)
ctx->driver_present = false;
+
+ /* Tell driver to purge any remaining configuration it may have
+ * lingering around.
+ */
+ drv_force_cleanup(local);
}
static void ieee80211_assign_chanctx(struct ieee80211_local *local,
--
2.42.0
^ permalink raw reply related
* [PATCH 19/19] wifi: mt76: mt7925: host-only unwind published links on add failure
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Release host link resources when mt7925_mac_sta_add_links() fails after
partial success.
msta->link[] and dev->mt76.wcid[] may already be published, so unwind
the host state to avoid leaving stale links behind.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
.../net/wireless/mediatek/mt76/mt7925/main.c | 45 +++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 95c631b57894..73d3722739d0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -989,11 +989,51 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
return ret;
}
+/*
+ * Host-only unwind for sta_add_links() failures.
+ *
+ * If add_links fail due to MCU/firmware timeouts; calling the full remove
+ * path would send more firmware commands and may hang again. So only rollback
+ * host-published state here (msta->link/valid_links, dev->mt76.wcid[idx]) and
+ * free mlink objects (RCU-safe). Firmware state is left for reset/recovery.
+ */
+static void
+mt7925_mac_sta_unwind_links_host(struct mt792x_dev *dev,
+ struct ieee80211_sta *sta,
+ unsigned long links)
+{
+ struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+ unsigned int link_id;
+
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt792x_link_sta *mlink;
+ u16 idx;
+
+ mlink = rcu_replace_pointer(msta->link[link_id], NULL,
+ lockdep_is_held(&dev->mt76.mutex));
+ if (!mlink)
+ continue;
+
+ msta->valid_links &= ~BIT(link_id);
+ if (msta->deflink_id == link_id)
+ msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
+
+ idx = mlink->wcid.idx;
+ rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+ mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
+ mt76_wcid_mask_clear(dev->mt76.wcid_mask, idx);
+
+ if (mlink != &msta->deflink)
+ kfree_rcu(mlink, rcu_head);
+ }
+}
+
static int
mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, unsigned long new_links)
{
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+ unsigned long added_links = 0;
unsigned int link_id;
int err = 0;
@@ -1030,8 +1070,13 @@ mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
rcu_assign_pointer(msta->link[link_id], mlink);
msta->valid_links |= BIT(link_id);
+
+ added_links |= BIT(link_id);
}
+ if (err && added_links)
+ mt7925_mac_sta_unwind_links_host(dev, sta, added_links);
+
return err;
}
--
2.43.0
^ permalink raw reply related
* [PATCH 18/19] wifi: mt76: mt7925: publish msta->link after successful link add
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Move the msta->link[link_id] publication until after
mt7925_mac_link_sta_add() succeeds.
msta->link[] is RCU-visible, so publishing it before setup completes can
expose a link whose add path later fails. Publish it only after success
to avoid partially initialized link state becoming visible.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
.../net/wireless/mediatek/mt76/mt7925/main.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index eb16c4683100..95c631b57894 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1000,10 +1000,11 @@ mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
for_each_set_bit(link_id, &new_links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct ieee80211_link_sta *link_sta;
struct mt792x_link_sta *mlink;
+ bool is_deflink = false;
if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) {
mlink = &msta->deflink;
- msta->deflink_id = link_id;
+ is_deflink = true;
} else {
mlink = kzalloc(sizeof(*mlink), GFP_KERNEL);
if (!mlink) {
@@ -1012,14 +1013,23 @@ mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
}
}
- msta->valid_links |= BIT(link_id);
- rcu_assign_pointer(msta->link[link_id], mlink);
mlink->sta = msta;
mlink->pri_link = &sta->deflink;
mlink->wcid.def_wcid = &msta->deflink.wcid;
link_sta = mt792x_sta_to_link_sta(vif, sta, link_id);
- mt7925_mac_link_sta_add(&dev->mt76, vif, link_sta, mlink);
+ err = mt7925_mac_link_sta_add(&dev->mt76, vif, link_sta, mlink);
+ if (err) {
+ if (!is_deflink)
+ kfree_rcu(mlink, rcu_head);
+ break;
+ }
+
+ if (is_deflink)
+ msta->deflink_id = link_id;
+
+ rcu_assign_pointer(msta->link[link_id], mlink);
+ msta->valid_links |= BIT(link_id);
}
return err;
--
2.43.0
^ permalink raw reply related
* [PATCH 17/19] wifi: mt76: mt7925: switch link STA allocation to RCU lifetime
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Allocate mt792x_link_sta with kzalloc() and free it with kfree_rcu()
instead of devm-managed memory.
msta->link[] is published via RCU, so the link STA must remain valid
until readers have quiesced after teardown. Manage the object lifetime
with kfree_rcu() to match its RCU-visible publication.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 10 +++++++---
drivers/net/wireless/mediatek/mt76/mt792x.h | 1 +
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 9e3f3874d0b3..eb16c4683100 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1005,7 +1005,7 @@ mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
mlink = &msta->deflink;
msta->deflink_id = link_id;
} else {
- mlink = devm_kzalloc(dev->mt76.dev, sizeof(*mlink), GFP_KERNEL);
+ mlink = kzalloc(sizeof(*mlink), GFP_KERNEL);
if (!mlink) {
err = -ENOMEM;
break;
@@ -1197,6 +1197,7 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, unsigned long old_links)
{
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+ struct mt76_dev *mdev = &dev->mt76;
unsigned int link_id;
/* clean up bss before starec */
@@ -1235,17 +1236,20 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
if (!link_sta)
continue;
- mlink = mt792x_sta_to_link(msta, link_id);
+ mlink = rcu_replace_pointer(msta->link[link_id], NULL,
+ lockdep_is_held(&mdev->mutex));
if (!mlink)
continue;
- rcu_assign_pointer(msta->link[link_id], NULL);
msta->valid_links &= ~BIT(link_id);
mlink->sta = NULL;
mlink->pri_link = NULL;
mt7925_mac_link_sta_remove(&dev->mt76, vif, link_sta, mlink);
+ if (mlink != &msta->deflink)
+ kfree_rcu(mlink, rcu_head);
+
if (msta->deflink_id == link_id)
msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 1f381ab356bc..4ff93f2cd624 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -97,6 +97,7 @@ DECLARE_EWMA(avg_signal, 10, 8)
struct mt792x_link_sta {
struct mt76_wcid wcid; /* must be first */
+ struct rcu_head rcu_head;
u32 airtime_ac[8];
--
2.43.0
^ permalink raw reply related
* [PATCH 16/19] wifi: mt76: mt7925: move WCID teardown into link_sta_remove()
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Move WCID teardown into mt7925_mac_link_sta_remove() to mirror the
dev->mt76.wcid[] publish done during link add.
This clears the published WCID before the rest of teardown, so WCID
lookups no longer expose a link that is being removed.
No functional change intended.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index d99dbd707fcd..9e3f3874d0b3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1151,12 +1151,14 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
struct mt792x_link_sta *mlink)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+ struct mt76_wcid *wcid = &mlink->wcid;
struct ieee80211_bss_conf *link_conf;
u8 link_id = link_sta->link_id;
+ u16 idx = wcid->idx;
mt7925_roc_abort_sync(dev);
- mt76_connac_free_pending_tx_skbs(&dev->pm, &mlink->wcid);
+ mt76_connac_free_pending_tx_skbs(&dev->pm, wcid);
mt76_connac_pm_wake(&dev->mphy, &dev->pm);
mt7925_mcu_sta_update(dev, link_sta, vif, mlink, false,
@@ -1183,6 +1185,10 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
list_del_init(&mlink->wcid.poll_list);
spin_unlock_bh(&mdev->sta_poll_lock);
+ rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+ mt76_wcid_cleanup(mdev, wcid);
+ mt76_wcid_mask_clear(mdev->wcid_mask, idx);
+
mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
}
@@ -1191,8 +1197,6 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, unsigned long old_links)
{
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
- struct mt76_dev *mdev = &dev->mt76;
- struct mt76_wcid *wcid;
unsigned int link_id;
/* clean up bss before starec */
@@ -1235,16 +1239,12 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
if (!mlink)
continue;
- mt7925_mac_link_sta_remove(&dev->mt76, vif, link_sta, mlink);
-
- wcid = &mlink->wcid;
rcu_assign_pointer(msta->link[link_id], NULL);
msta->valid_links &= ~BIT(link_id);
mlink->sta = NULL;
mlink->pri_link = NULL;
- mt76_wcid_cleanup(mdev, wcid);
- mt76_wcid_mask_clear(mdev->wcid_mask, wcid->idx);
+ mt7925_mac_link_sta_remove(&dev->mt76, vif, link_sta, mlink);
if (msta->deflink_id == link_id)
msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
--
2.43.0
^ permalink raw reply related
* [PATCH 15/19] wifi: mt76: mt7925: drop WCID reinit after publish
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Remove the redundant mt76_wcid_init() call after publishing the WCID in
mt7925_mac_link_sta_add().
WCID is already initialized before publication, so reinitializing it
afterward is unnecessary and makes the setup ordering less clear.
No functional change intended.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index dbde91727cd0..d99dbd707fcd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -891,7 +891,6 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
ewma_signal_init(&wcid->rssi);
rcu_assign_pointer(dev->mt76.wcid[wcid->idx], wcid);
wcid_published = true;
- mt76_wcid_init(wcid, 0);
ewma_avg_signal_init(&mlink->avg_ack_signal);
memset(mlink->airtime_ac, 0,
sizeof(msta->deflink.airtime_ac));
--
2.43.0
^ permalink raw reply related
* [PATCH 14/19] wifi: mt76: mt7925: unwind WCID setup on link STA add failure
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Undo the published WCID state when mt7925_mac_link_sta_add() fails after
WCID setup.
The add path can fail after dev->mt76.wcid[] is published, so the error
path must clear the partial host-side WCID state to avoid leaving stale
entries behind.
No functional change intended.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
.../net/wireless/mediatek/mt76/mt7925/main.c | 53 +++++++++++++------
1 file changed, 38 insertions(+), 15 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 584d989fb4e8..dbde91727cd0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -862,8 +862,10 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
struct ieee80211_bss_conf *link_conf;
struct mt792x_bss_conf *mconf;
u8 link_id = link_sta->link_id;
+ bool wcid_published = false;
struct mt792x_sta *msta;
struct mt76_wcid *wcid;
+ bool pm_woken = false;
int ret, idx;
msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
@@ -888,6 +890,7 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
wcid = &mlink->wcid;
ewma_signal_init(&wcid->rssi);
rcu_assign_pointer(dev->mt76.wcid[wcid->idx], wcid);
+ wcid_published = true;
mt76_wcid_init(wcid, 0);
ewma_avg_signal_init(&mlink->avg_ack_signal);
memset(mlink->airtime_ac, 0,
@@ -895,7 +898,8 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm);
if (ret)
- return ret;
+ goto out_wcid;
+ pm_woken = true;
mt7925_mac_wtbl_update(dev, idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -909,15 +913,19 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
mlink_bc = mt792x_sta_to_link(&mvif->sta, mconf->link_id);
if (ieee80211_vif_is_mld(vif)) {
- mt7925_mcu_add_bss_info_sta(&dev->phy, mconf->mt76.ctx,
- link_conf, link_sta,
- mlink_bc->wcid.idx, mlink->wcid.idx,
- link_sta != mlink->pri_link);
+ ret = mt7925_mcu_add_bss_info_sta(&dev->phy, mconf->mt76.ctx,
+ link_conf, link_sta,
+ mlink_bc->wcid.idx, mlink->wcid.idx,
+ link_sta != mlink->pri_link);
+ if (ret)
+ goto out_pm;
} else {
- mt7925_mcu_add_bss_info_sta(&dev->phy, mconf->mt76.ctx,
- link_conf, link_sta,
- mlink_bc->wcid.idx, mlink->wcid.idx,
- false);
+ ret = mt7925_mcu_add_bss_info_sta(&dev->phy, mconf->mt76.ctx,
+ link_conf, link_sta,
+ mlink_bc->wcid.idx, mlink->wcid.idx,
+ false);
+ if (ret)
+ goto out_pm;
}
}
@@ -927,7 +935,7 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
mlink, true,
MT76_STA_INFO_STATE_NONE);
if (ret)
- return ret;
+ goto out_pm;
} else if (ieee80211_vif_is_mld(vif) &&
link_sta != mlink->pri_link) {
struct mt792x_link_sta *pri_mlink;
@@ -940,31 +948,46 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
container_of(pri_wcid, struct mt792x_link_sta, wcid) :
NULL;
- if (WARN_ON_ONCE(!pri_mlink))
- return -EINVAL;
+ if (WARN_ON_ONCE(!pri_mlink)) {
+ ret = -EINVAL;
+ goto out_pm;
+ }
ret = mt7925_mcu_sta_update(dev, mlink->pri_link, vif,
pri_mlink, true,
MT76_STA_INFO_STATE_ASSOC);
if (ret)
- return ret;
+ goto out_pm;
ret = mt7925_mcu_sta_update(dev, link_sta, vif,
mlink, true,
MT76_STA_INFO_STATE_ASSOC);
if (ret)
- return ret;
+ goto out_pm;
} else {
ret = mt7925_mcu_sta_update(dev, link_sta, vif,
mlink, true,
MT76_STA_INFO_STATE_NONE);
if (ret)
- return ret;
+ goto out_pm;
}
mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
return 0;
+
+out_pm:
+ if (pm_woken)
+ mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
+out_wcid:
+ if (wcid_published) {
+ u16 idx = wcid->idx;
+
+ rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+ mt76_wcid_cleanup(mdev, wcid);
+ mt76_wcid_mask_clear(mdev->wcid_mask, wcid->idx);
+ }
+ return ret;
}
static int
--
2.43.0
^ permalink raw reply related
* [PATCH 13/19] wifi: mt76: mt7925: make WCID cleanup unconditional in sta_remove_links()
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Drop the dead pri_link check in mt7925_mac_sta_remove_links() and
perform WCID cleanup unconditionally.
mlink->pri_link is already cleared before the test, making the branch
ineffective. This matches the actual teardown behaviour and simplifies
the remove path.
No functional change intended.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 151dc79f7c12..584d989fb4e8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1221,10 +1221,8 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
mlink->sta = NULL;
mlink->pri_link = NULL;
- if (link_sta != mlink->pri_link) {
- mt76_wcid_cleanup(mdev, wcid);
- mt76_wcid_mask_clear(mdev->wcid_mask, wcid->idx);
- }
+ mt76_wcid_cleanup(mdev, wcid);
+ mt76_wcid_mask_clear(mdev->wcid_mask, wcid->idx);
if (msta->deflink_id == link_id)
msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
--
2.43.0
^ permalink raw reply related
* [PATCH 12/19] wifi: mt76: mt7925: pass mconf and mlink to wtbl_update_hdr_trans()
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Drop the mt792x_vif_to_link() lookup in mt7925_mcu_wtbl_update_hdr_trans()
and pass the resolved mconf and mlink from the caller instead. The link
context is already known at the call site, making the lookup redundant.
This keeps the helper lookup-free and makes link ownership explicit.
No functional change intended.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
.../net/wireless/mediatek/mt76/mt7925/main.c | 4 +++-
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 20 ++++---------------
.../wireless/mediatek/mt76/mt7925/mt7925.h | 5 ++---
3 files changed, 9 insertions(+), 20 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 135a803b4382..151dc79f7c12 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1590,8 +1590,10 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,
valid = ieee80211_vif_is_mld(vif) ? mvif->valid_links : BIT(0);
for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt792x_bss_conf *mconf;
struct mt792x_link_sta *mlink;
+ mconf = mt792x_vif_to_link(mvif, i);
mlink = mt792x_sta_to_link(msta, i);
if (enabled)
@@ -1602,7 +1604,7 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,
if (!mlink->wcid.sta)
continue;
- mt7925_mcu_wtbl_update_hdr_trans(dev, vif, sta, mlink, i);
+ mt7925_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
}
mt792x_mutex_release(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 0b096838bca6..2be6160873a5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1066,7 +1066,6 @@ EXPORT_SYMBOL_GPL(mt7925_run_firmware);
static void
mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb,
struct ieee80211_vif *vif,
- struct ieee80211_link_sta *link_sta,
struct mt792x_link_sta *mlink)
{
struct sta_rec_hdr_trans *hdr_trans;
@@ -1096,29 +1095,18 @@ mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb,
int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct mt792x_link_sta *mlink,
- int link_id)
+ struct mt792x_bss_conf *mconf,
+ struct mt792x_link_sta *mlink)
{
- struct ieee80211_link_sta *link_sta = sta ? &sta->deflink : NULL;
- struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
- struct mt792x_bss_conf *mconf;
struct sk_buff *skb;
- link_sta = mt792x_sta_to_link_sta(vif, sta, link_id);
- mconf = mt792x_vif_to_link(mvif, link_id);
-
skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
&mlink->wcid,
MT7925_STA_UPDATE_MAX_SIZE);
if (IS_ERR(skb))
return PTR_ERR(skb);
- /* starec hdr trans */
- if (!link_sta)
- mlink = &mvif->sta.deflink;
-
- mt7925_mcu_sta_hdr_trans_tlv(skb, vif, link_sta, mlink);
+ mt7925_mcu_sta_hdr_trans_tlv(skb, vif, mlink);
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
}
@@ -2022,7 +2010,7 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
if (!info->link_sta)
mlink = &mvif->sta.deflink;
- mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta, mlink);
+ mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, mlink);
}
return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index e28972f0615b..46b480f7d813 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -370,9 +370,8 @@ int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
int mt7925_mcu_set_rts_thresh(struct mt792x_phy *phy, u32 val);
int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct mt792x_link_sta *mlink,
- int link_id);
+ struct mt792x_bss_conf *mconf,
+ struct mt792x_link_sta *mlink);
int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy);
int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
--
2.43.0
^ permalink raw reply related
* [PATCH 11/19] wifi: mt76: mt7925: resolve link after acquiring mt76 mutex
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
mt792x_sta_to_link() uses rcu_dereference_protected() and therefore
expects mt76.mutex to be held. Move the lookup after
mt792x_mutex_acquire() to make the locking explicit and correct.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 68f168a093f2..135a803b4382 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1071,11 +1071,11 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev,
struct mt792x_link_sta *mlink;
struct mt792x_sta *msta;
+ mt792x_mutex_acquire(dev);
+
msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
mlink = mt792x_sta_to_link(msta, link_sta->link_id);
- mt792x_mutex_acquire(dev);
-
if (ieee80211_vif_is_mld(vif)) {
link_conf = mt792x_vif_to_bss_conf(vif, msta->deflink_id);
} else {
--
2.43.0
^ permalink raw reply related
* [PATCH 10/19] wifi: mt76: mt7925: pass mlink to set_link_key()
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Drop the mt792x_sta_to_link() lookup in mt7925_set_link_key() and pass
the resolved mlink from the caller instead. The link context is already
known at the call site, making the lookup redundant.
This keeps the helper lookup-free and makes link ownership explicit.
No functional change intended.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index f5dd91dacca5..68f168a093f2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -594,7 +594,8 @@ static int mt7925_cancel_remain_on_channel(struct ieee80211_hw *hw,
static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key, int link_id)
+ struct ieee80211_key_conf *key, int link_id,
+ struct mt792x_link_sta *mlink)
{
struct mt792x_dev *dev = mt792x_hw_dev(hw);
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
@@ -603,7 +604,6 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_bss_conf *link_conf;
struct ieee80211_link_sta *link_sta;
int idx = key->keyidx, err = 0;
- struct mt792x_link_sta *mlink;
struct mt792x_bss_conf *mconf;
struct mt76_wcid *wcid;
u8 *wcid_keyidx;
@@ -611,7 +611,6 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
link_conf = mt792x_vif_to_bss_conf(vif, link_id);
link_sta = sta ? mt792x_sta_to_link_sta(vif, sta, link_id) : NULL;
mconf = mt792x_vif_to_link(mvif, link_id);
- mlink = mt792x_sta_to_link(msta, link_id);
wcid = &mlink->wcid;
wcid_keyidx = &wcid->hw_key_idx;
@@ -679,6 +678,7 @@ static int mt7925_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
struct mt792x_sta *msta = sta ? (struct mt792x_sta *)sta->drv_priv :
&mvif->sta;
+ struct mt792x_link_sta *mlink;
int err;
/* The hardware does not support per-STA RX GTK, fallback
@@ -700,12 +700,16 @@ static int mt7925_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
add = key->link_id != -1 ? BIT(key->link_id) : msta->valid_links;
for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
- err = mt7925_set_link_key(hw, cmd, vif, sta, key, link_id);
+ mlink = mt792x_sta_to_link(msta, link_id);
+ err = mt7925_set_link_key(hw, cmd, vif, sta, key, link_id,
+ mlink);
if (err < 0)
break;
}
} else {
- err = mt7925_set_link_key(hw, cmd, vif, sta, key, vif->bss_conf.link_id);
+ mlink = mt792x_sta_to_link(msta, vif->bss_conf.link_id);
+ err = mt7925_set_link_key(hw, cmd, vif, sta, key,
+ vif->bss_conf.link_id, mlink);
}
mt792x_mutex_release(dev);
--
2.43.0
^ permalink raw reply related
* [PATCH 09/19] wifi: mt76: mt7925: pass mlink to wtbl_update_hdr_trans()
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Drop the mt792x_sta_to_link() lookup in mt7925_mcu_wtbl_update_hdr_trans()
and pass the resolved mlink from the caller instead. The link context is
already known at the call site, making the lookup redundant.
This keeps the helper lookup-free and makes link ownership explicit.
No functional change intended.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 2 +-
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 8 ++------
drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h | 1 +
3 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index ddff6c5ab876..f5dd91dacca5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1598,7 +1598,7 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,
if (!mlink->wcid.sta)
continue;
- mt7925_mcu_wtbl_update_hdr_trans(dev, vif, sta, i);
+ mt7925_mcu_wtbl_update_hdr_trans(dev, vif, sta, mlink, i);
}
mt792x_mutex_release(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 03515b891643..0b096838bca6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1097,18 +1097,14 @@ mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb,
int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
+ struct mt792x_link_sta *mlink,
int link_id)
{
- struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
struct ieee80211_link_sta *link_sta = sta ? &sta->deflink : NULL;
- struct mt792x_link_sta *mlink;
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
struct mt792x_bss_conf *mconf;
- struct mt792x_sta *msta;
struct sk_buff *skb;
- msta = sta ? (struct mt792x_sta *)sta->drv_priv : &mvif->sta;
-
- mlink = mt792x_sta_to_link(msta, link_id);
link_sta = mt792x_sta_to_link_sta(vif, sta, link_id);
mconf = mt792x_vif_to_link(mvif, link_id);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 95f29dae4d9d..e28972f0615b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -371,6 +371,7 @@ int mt7925_mcu_set_rts_thresh(struct mt792x_phy *phy, u32 val);
int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
+ struct mt792x_link_sta *mlink,
int link_id);
int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy);
--
2.43.0
^ permalink raw reply related
* [PATCH 08/19] wifi: mt76: mt7925: validate mlink in sta_hdr_trans_tlv()
From: Sean Wang @ 2026-03-06 23:22 UTC (permalink / raw)
To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260306232238.2039675-1-sean.wang@kernel.org>
From: Sean Wang <sean.wang@mediatek.com>
Replace the dead wcid NULL check in mt7925_mcu_sta_hdr_trans_tlv() with
a WARN_ON_ONCE() guard on mlink before dereferencing mlink->wcid.
wcid is always derived from mlink, so mlink is the only meaningful
object to validate here.
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index c474dd358650..03515b891643 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1082,11 +1082,11 @@ mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb,
else
hdr_trans->from_ds = true;
- wcid = &mlink->wcid;
-
- if (!wcid)
+ if (WARN_ON_ONCE(!mlink))
return;
+ wcid = &mlink->wcid;
+
hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags);
if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) {
hdr_trans->to_ds = true;
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox