[PATCH v2 10/13] wifi: mt76: mt7925: add MT7927 hardware initialization
Javier Tia
floss at jetm.me
Thu Mar 19 15:24:26 PDT 2026
Add MT7927-specific hardware initialization for the Filogic 380 combo
chip, which has an additional CBInfra (ConnectaBus Infrastructure) bus
fabric between PCIe and the WiFi subsystem.
CBTOP remap: configure PCIe address mapping so MMIO reads to WiFi
registers return valid data instead of zero.
Chip initialization sequence:
1. WF subsystem reset via CBInfra RGU
2. MCU ownership acquisition
3. Poll ROMCODE_INDEX for MCU idle (0x1D1E)
4. MCIF remap for host DMA
5. PCIe sleep disable
Probe flow changes for MT7927:
- Skip early CLR_OWN (CBTOP not yet configured)
- Force chip ID if CHIPID register returns stale value
- Replace wfsys_reset with mt7927_chip_init
CLR_OWN skip in mt7925e_mcu_init(): every CLR_OWN triggers the ROM to
reinitialize WFDMA, destroying ring configuration set up by DMA init.
The controlled SET_OWN/CLR_OWN is already handled in the DMA pre-ring
setup hook.
DBDC enable: MT7927 firmware defaults to single-band (2.4GHz only).
Send explicit SET_DBDC_PARMS to enable dual-band operation.
CNM force: MT7927 firmware lacks the connac2 feature trailer, causing
channel context ops to be replaced with stubs. Force the CNM flag at
probe to preserve ROC support needed for authentication.
mac_reset guard: return -EOPNOTSUPP for MT7927 since the reset path
performs CLR_OWN and logic reset that destroy DMA configuration. Full
reset recovery is follow-up work.
Register values derived from Loong0x00's reverse-engineered MT7927
driver.
Reported-by: 张旭涵 <Loong.0x00 at gmail.com>
Closes: https://github.com/openwrt/mt76/issues/927
Tested-by: Marcin FM <marcin at lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris at gmail.com>
Tested-by: George Salukvadze <giosal90 at gmail.com>
Tested-by: Evgeny Kapusta <3193631 at gmail.com>
Tested-by: Samu Toljamo <samu.toljamo at gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750 at gmail.com>
Tested-by: Chapuis Dario <chapuisdario4 at gmail.com>
Tested-by: Thibaut François <tibo at humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00 at gmail.com>
Signed-off-by: Javier Tia <floss at jetm.me>
---
drivers/net/wireless/mediatek/mt76/mt7925/init.c | 13 +++
drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 103 +++++++++++++++++++--
.../net/wireless/mediatek/mt76/mt7925/pci_mac.c | 9 ++
.../net/wireless/mediatek/mt76/mt7925/pci_mcu.c | 20 ++--
drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 18 ++++
5 files changed, 149 insertions(+), 14 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
index 3ce5d6fcc69d..c4c99380f5b5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
@@ -115,6 +115,19 @@ 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;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index 415194a440f8..393d9f408b84 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -400,6 +400,64 @@ static const struct mt792x_irq_map mt7927_irq_map = {
.wm2_complete_mask = MT7927_RX_DONE_INT_ENA7,
},
};
+
+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, MT7927_CBINFRA_RGU_WF_RST,
+ MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS);
+ msleep(1);
+ mt76_clear(dev, MT7927_CBINFRA_RGU_WF_RST,
+ MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS);
+ msleep(5);
+
+ /* MCU ownership */
+ mt76_wr(dev, MT7927_CBINFRA_MCU_OWN_SET, BIT(0));
+
+ /* Poll ROMCODE_INDEX for MCU idle */
+ if (!__mt76_poll_msec(mdev, MT7927_ROMCODE_INDEX,
+ 0xffff, MT7927_MCU_IDLE_VALUE, 2000)) {
+ val = mt76_rr(dev, MT7927_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, MT7927_MCIF_REMAP_WF_1_BA,
+ MT7927_MCIF_REMAP_WF_1_BA_VAL);
+
+ /* Disable PCIe sleep */
+ mt76_wr(dev, MT7927_CBINFRA_SLP_CTRL, 0xffffffff);
+
+ /* Clear CONNINFRA wakeup */
+ mt76_wr(dev, MT7927_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, MT7927_CBINFRA_WAKEPU_TOP, 0x1);
+ usleep_range(1000, 2000);
+
+ /* Configure CBTOP PCIe address remap for WF and BT */
+ mt76_wr(dev, MT7927_CBINFRA_MISC0_REMAP_WF,
+ MT7927_CBINFRA_REMAP_WF_VAL);
+ mt76_wr(dev, MT7927_CBINFRA_MISC0_REMAP_BT,
+ MT7927_CBINFRA_REMAP_BT_VAL);
+
+ /* Readback to push writes */
+ mt76_rr(dev, MT7927_CBINFRA_MISC0_REMAP_WF);
+}
static int mt7925_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -482,6 +540,17 @@ 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;
@@ -517,25 +586,43 @@ static int mt7925_pci_probe(struct pci_dev *pdev,
if (!mt7925_disable_aspm && mt76_pci_aspm_supported(pdev))
dev->aspm_supported = true;
-
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/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
index 1626a3684082..9b3eeb1328f6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
@@ -72,6 +72,15 @@ 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);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
index 6cceff88c656..1f50d1ef6fb6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
@@ -35,13 +35,21 @@ 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);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
index 1b9b51d43f79..9c4bbbe898f3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
@@ -499,4 +499,22 @@
#define WFSYS_SW_RST_B BIT(0)
#define WFSYS_SW_INIT_DONE BIT(4)
+/* CBInfra registers - MT7927 combo chip */
+#define MT7927_CBINFRA_WAKEPU_TOP 0xe01a0
+#define MT7927_CBINFRA_MISC0_REMAP_WF 0x1f6554
+#define MT7927_CBINFRA_MISC0_REMAP_BT 0x1f6558
+#define MT7927_CBINFRA_RGU_WF_RST 0x1f8600
+#define MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS BIT(4)
+#define MT7927_CBINFRA_MCU_OWN_SET 0x1f5034
+#define MT7927_CBINFRA_SLP_CTRL 0x1f5018
+#define MT7927_ROMCODE_INDEX 0xc1604
+#define MT7927_MCU_IDLE_VALUE 0x1d1e
+#define MT7927_MCIF_REMAP_WF_1_BA 0xd1034
+
+/* CBInfra CBTOP remap values */
+#define MT7927_CBINFRA_REMAP_WF_VAL 0x74037001
+#define MT7927_CBINFRA_REMAP_BT_VAL 0x70007000
+
+#define MT7927_MCIF_REMAP_WF_1_BA_VAL 0x18051803
+
#endif /* __MT792X_REGS_H */
--
2.53.0
More information about the Linux-mediatek
mailing list