From bb1ed866d35ca16c0066e8fd8a86694586504e87 Mon Sep 17 00:00:00 2001 From: David Kearsley Date: Sun, 8 Mar 2026 21:46:33 +1030 Subject: [PATCH 1/2] wifi: mt76: mt7615: fix MT7663 PCIe embedded EEPROM loading Some MT7663 PCIe boards store the WiFi EEPROM image inside the factory partition at offset 0x8000 instead of exposing it through the usual mediatek,mtd-eeprom binding. Record the PCI probe result early and fall back to reading the full factory/mtd2 partition so the driver can load the embedded 0x7663 EEPROM image with the correct MAC address and power calibration data. Signed-off-by: David Kearsley --- mt7615/eeprom.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ mt7615/mmio.c | 3 +- mt7615/mt7615.h | 3 +- mt7615/pci.c | 2 +- mt7615/soc.c | 3 +- 5 files changed, 126 insertions(+), 4 deletions(-) diff --git a/mt7615/eeprom.c b/mt7615/eeprom.c index d6828e1cd..2867fe0f5 100644 --- a/mt7615/eeprom.c +++ b/mt7615/eeprom.c @@ -6,9 +6,62 @@ */ #include +#include #include "mt7615.h" #include "eeprom.h" +static int mt7615_check_eeprom(struct mt76_dev *dev); + +static int mt7615_read_factory_partition(struct mt7615_dev *dev, u8 **buf, + size_t *len) +{ +#ifdef CONFIG_MTD + struct mtd_info *mtd; + u8 *data; + size_t retlen; + int ret; + + mtd = get_mtd_device_nm("factory"); + if (IS_ERR(mtd)) + mtd = get_mtd_device(NULL, 2); + if (IS_ERR(mtd)) + return PTR_ERR(mtd); + + if (mtd->size < 0x8000 + MT7663_EEPROM_SIZE) { + put_mtd_device(mtd); + return -EINVAL; + } + + data = kmalloc(mtd->size, GFP_KERNEL); + if (!data) { + put_mtd_device(mtd); + return -ENOMEM; + } + + ret = mtd_read(mtd, 0, mtd->size, &retlen, data); + put_mtd_device(mtd); + + if (mtd_is_bitflip(ret)) + ret = 0; + if (ret) { + kfree(data); + return ret; + } + + if (retlen < mtd->size) { + kfree(data); + return -EINVAL; + } + + *buf = data; + *len = mtd->size; + + return 0; +#else + return -ENOENT; +#endif +} + static int mt7615_efuse_read(struct mt7615_dev *dev, u32 base, u16 addr, u8 *data) { @@ -73,6 +126,7 @@ static int mt7615_efuse_init(struct mt7615_dev *dev, u32 base) static int mt7615_eeprom_load(struct mt7615_dev *dev, u32 addr) { + u8 *factory = NULL; int ret; BUILD_BUG_ON(MT7615_EEPROM_FULL_SIZE < MT7663_EEPROM_SIZE); @@ -81,6 +135,71 @@ static int mt7615_eeprom_load(struct mt7615_dev *dev, u32 addr) if (ret < 0) return ret; + if (dev->is_mt7663_pci && mt7615_check_eeprom(&dev->mt76)) { + static const u32 mt7663_offsets[] = { 0x8000 }; + size_t factory_len = 0; + u8 data[16]; + int i; + + for (i = 0; i < ARRAY_SIZE(mt7663_offsets); i++) { + ret = mt76_get_of_data_from_mtd(&dev->mt76, data, + mt7663_offsets[i], sizeof(data)); + if (ret == -ENOENT) { + if (!factory) { + ret = mt7615_read_factory_partition(dev, &factory, + &factory_len); + if (ret) + goto out; + } + + if (factory_len < mt7663_offsets[i] + sizeof(data)) { + ret = -EINVAL; + goto out; + } + + memcpy(data, factory + mt7663_offsets[i], sizeof(data)); + ret = 0; + } + if (ret) + continue; + + if (get_unaligned_le16(data) != 0x7663 || + !is_valid_ether_addr(data + MT_EE_MAC_ADDR)) + continue; + + memset(dev->mt76.eeprom.data, 0, dev->mt76.eeprom.size); + ret = mt76_get_of_data_from_mtd(&dev->mt76, + dev->mt76.eeprom.data, + mt7663_offsets[i], + MT7663_EEPROM_SIZE); + if (ret == -ENOENT) { + if (!factory) { + ret = mt7615_read_factory_partition(dev, &factory, + &factory_len); + if (ret) + goto out; + } + + if (factory_len < mt7663_offsets[i] + MT7663_EEPROM_SIZE) { + ret = -EINVAL; + goto out; + } + + memcpy(dev->mt76.eeprom.data, factory + mt7663_offsets[i], + MT7663_EEPROM_SIZE); + ret = 0; + } + if (ret) + goto out; + break; + } + } + +out: + kfree(factory); + if (ret) + return ret; + return mt7615_efuse_init(dev, addr); } diff --git a/mt7615/mmio.c b/mt7615/mmio.c index da7edd48c..3eee12a52 100644 --- a/mt7615/mmio.c +++ b/mt7615/mmio.c @@ -164,7 +164,7 @@ static u32 mt7615_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) } int mt7615_mmio_probe(struct device *pdev, void __iomem *mem_base, - int irq, const u32 *map) + int irq, const u32 *map, bool is_mt7663_pci) { static const struct mt76_driver_ops drv_ops = { /* txwi_size = txd size + txp size */ @@ -203,6 +203,7 @@ int mt7615_mmio_probe(struct device *pdev, void __iomem *mem_base, tasklet_setup(&mdev->irq_tasklet, mt7615_irq_tasklet); dev->reg_map = map; + dev->is_mt7663_pci = is_mt7663_pci; dev->ops = ops; mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | (mt76_rr(dev, MT_HW_REV) & 0xff); diff --git a/mt7615/mt7615.h b/mt7615/mt7615.h index fac05d1b0..43ad1e06e 100644 --- a/mt7615/mt7615.h +++ b/mt7615/mt7615.h @@ -272,6 +272,7 @@ struct mt7615_dev { bool fw_debug; bool flash_eeprom; bool dbdc_support; + bool is_mt7663_pci; u8 fw_ver; @@ -368,7 +369,7 @@ static inline int mt7622_wmac_init(struct mt7615_dev *dev) int mt7615_thermal_init(struct mt7615_dev *dev); int mt7615_mmio_probe(struct device *pdev, void __iomem *mem_base, - int irq, const u32 *map); + int irq, const u32 *map, bool is_mt7663_pci); u32 mt7615_reg_map(struct mt7615_dev *dev, u32 addr); u32 mt7615_reg_map(struct mt7615_dev *dev, u32 addr); diff --git a/mt7615/pci.c b/mt7615/pci.c index f5018bfa3..80a737ac5 100644 --- a/mt7615/pci.c +++ b/mt7615/pci.c @@ -47,7 +47,7 @@ static int mt7615_pci_probe(struct pci_dev *pdev, map = id->device == 0x7663 ? mt7663e_reg_map : mt7615e_reg_map; ret = mt7615_mmio_probe(&pdev->dev, pcim_iomap_table(pdev)[0], - pdev->irq, map); + pdev->irq, map, id->device == 0x7663); if (ret) goto error; diff --git a/mt7615/soc.c b/mt7615/soc.c index a18400503..2ff1e8252 100644 --- a/mt7615/soc.c +++ b/mt7615/soc.c @@ -42,7 +42,8 @@ static int mt7622_wmac_probe(struct platform_device *pdev) if (IS_ERR(mem_base)) return PTR_ERR(mem_base); - return mt7615_mmio_probe(&pdev->dev, mem_base, irq, mt7615e_reg_map); + return mt7615_mmio_probe(&pdev->dev, mem_base, irq, mt7615e_reg_map, + false); } #if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0) From 4a9bc3d6d7b214598485021fc0514671a1e96a15 Mon Sep 17 00:00:00 2001 From: David Kearsley Date: Wed, 11 Mar 2026 17:10:31 +1030 Subject: [PATCH 2/2] wifi: mt76: mt7615: use EEPROM-derived SKU limits for MT7663 MT7663 currently bypasses the computed per-rate power limits when building the MCU SKU table and fills every entry with a flat txpower value. Drop that special case so the firmware receives the EEPROM-derived OFDM/HT/VHT power limits that are already parsed by the driver. Signed-off-by: David Kearsley --- mt7615/mcu.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/mt7615/mcu.c b/mt7615/mcu.c index ff57ede87..e05a4d0a3 100644 --- a/mt7615/mcu.c +++ b/mt7615/mcu.c @@ -2074,11 +2074,6 @@ static void mt7615_mcu_set_txpower_sku(struct mt7615_phy *phy, u8 *sku) &limits, tx_power); mphy->txpower_cur = tx_power; - if (is_mt7663(mphy->dev)) { - memset(sku, tx_power, MT_SKU_4SS_DELTA + 1); - return; - } - for (i = 0; i < MT_SKU_1SS_DELTA; i++) sku[i] = limits_array[sku_mapping[i]];