[PATCH mt76 1/3] wifi: mt76: add external EEPROM support for mt799x chipsets
Shayne Chen
shayne.chen at mediatek.com
Thu Feb 12 01:03:08 PST 2026
From: StanleyYP Wang <StanleyYP.Wang at mediatek.com>
For the MT7992 and MT7990 chipsets, efuse mode is not supported because
there is insufficient space in the efuse to store the calibration data.
Therefore, an additional on-chip EEPROM is added to address this
limitation.
Co-developed-by: Elwin Huang <s09289728096 at gmail.com>
Signed-off-by: Elwin Huang <s09289728096 at gmail.com>
Co-developed-by: Shayne Chen <shayne.chen at mediatek.com>
Signed-off-by: Shayne Chen <shayne.chen at mediatek.com>
Signed-off-by: StanleyYP Wang <StanleyYP.Wang at mediatek.com>
---
.../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 +
.../wireless/mediatek/mt76/mt7996/eeprom.c | 59 +++++++------
.../net/wireless/mediatek/mt76/mt7996/init.c | 3 +-
.../net/wireless/mediatek/mt76/mt7996/mcu.c | 83 ++++++++++++-------
.../net/wireless/mediatek/mt76/mt7996/mcu.h | 43 +++++++++-
.../wireless/mediatek/mt76/mt7996/mt7996.h | 20 ++++-
6 files changed, 148 insertions(+), 61 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index f44977f9093d..e91966cd5efe 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1308,6 +1308,7 @@ enum {
MCU_UNI_CMD_PER_STA_INFO = 0x6d,
MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
+ MCU_UNI_CMD_EXT_EEPROM_CTRL = 0x74,
MCU_UNI_CMD_RADIO_STATUS = 0x80,
MCU_UNI_CMD_SDO = 0x88,
};
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
index 8f60772913b4..00c72be8498f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
@@ -153,7 +153,7 @@ mt7996_eeprom_check_or_use_default(struct mt7996_dev *dev, bool use_default)
dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
memcpy(eeprom, fw->data, MT7996_EEPROM_SIZE);
- dev->flash_mode = true;
+ dev->eeprom_mode = EEPROM_MODE_DEFAULT_BIN;
out:
release_firmware(fw);
@@ -163,26 +163,31 @@ mt7996_eeprom_check_or_use_default(struct mt7996_dev *dev, bool use_default)
static int mt7996_eeprom_load(struct mt7996_dev *dev)
{
+ u32 eeprom_blk_size, block_num;
bool use_default = false;
- int ret;
+ int ret, i;
ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
if (ret < 0)
return ret;
if (ret && !mt7996_check_eeprom(dev)) {
- dev->flash_mode = true;
+ dev->eeprom_mode = EEPROM_MODE_FLASH;
goto out;
}
- if (!dev->flash_mode) {
- u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
- u32 block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
+ memset(dev->mt76.eeprom.data, 0, MT7996_EEPROM_SIZE);
+ if (mt7996_has_ext_eeprom(dev)) {
+ /* external eeprom mode */
+ dev->eeprom_mode = EEPROM_MODE_EXT;
+ eeprom_blk_size = MT7996_EXT_EEPROM_BLOCK_SIZE;
+ } else {
u8 free_block_num;
- int i;
- memset(dev->mt76.eeprom.data, 0, MT7996_EEPROM_SIZE);
- ret = mt7996_mcu_get_eeprom_free_block(dev, &free_block_num);
+ /* efuse mode */
+ dev->eeprom_mode = EEPROM_MODE_EFUSE;
+ eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
+ ret = mt7996_mcu_get_efuse_free_block(dev, &free_block_num);
if (ret < 0)
return ret;
@@ -191,27 +196,29 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
use_default = true;
goto out;
}
+ }
+
+ /* check if eeprom data from fw is valid */
+ if (mt7996_mcu_get_eeprom(dev, 0, NULL, eeprom_blk_size,
+ dev->eeprom_mode) ||
+ mt7996_check_eeprom(dev)) {
+ use_default = true;
+ goto out;
+ }
+
+ /* read eeprom data from fw */
+ block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
+ for (i = 1; i < block_num; i++) {
+ u32 len = eeprom_blk_size;
- /* check if eeprom data from fw is valid */
- if (mt7996_mcu_get_eeprom(dev, 0, NULL, 0) ||
- mt7996_check_eeprom(dev)) {
+ if (i == block_num - 1)
+ len = MT7996_EEPROM_SIZE % eeprom_blk_size;
+ ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size,
+ NULL, len, dev->eeprom_mode);
+ if (ret && ret != -EINVAL) {
use_default = true;
goto out;
}
-
- /* read eeprom data from fw */
- for (i = 1; i < block_num; i++) {
- u32 len = eeprom_blk_size;
-
- if (i == block_num - 1)
- len = MT7996_EEPROM_SIZE % eeprom_blk_size;
- ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size,
- NULL, len);
- if (ret && ret != -EINVAL) {
- use_default = true;
- goto out;
- }
- }
}
out:
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
index 2937e89ad0c9..1fab04909831 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
@@ -1207,7 +1207,8 @@ static int mt7996_variant_fem_init(struct mt7996_dev *dev)
if (ret)
return ret;
- ret = mt7996_mcu_get_eeprom(dev, MT7976C_EFUSE_OFFSET, buf, sizeof(buf));
+ ret = mt7996_mcu_get_eeprom(dev, MT7976C_EFUSE_OFFSET, buf, sizeof(buf),
+ EEPROM_MODE_EFUSE);
if (ret && ret != -EINVAL)
return ret;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 9ccf9f97c984..46099486ec09 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -3950,7 +3950,7 @@ static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
#define MAX_PAGE_IDX_MASK GENMASK(7, 5)
#define PAGE_IDX_MASK GENMASK(4, 2)
#define PER_PAGE_SIZE 0x400
- struct mt7996_mcu_eeprom req = {
+ struct mt7996_mcu_eeprom_update req = {
.tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE),
.buffer_mode = EE_MODE_BUFFER
};
@@ -3992,57 +3992,80 @@ static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
{
- struct mt7996_mcu_eeprom req = {
+ struct mt7996_mcu_eeprom_update req = {
.tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE),
.len = cpu_to_le16(sizeof(req) - 4),
.buffer_mode = EE_MODE_EFUSE,
.format = EE_FORMAT_WHOLE
};
- if (dev->flash_mode)
+ if (dev->eeprom_mode != EEPROM_MODE_EFUSE)
return mt7996_mcu_set_eeprom_flash(dev);
return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
&req, sizeof(req), true);
}
-int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len)
+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len,
+ enum mt7996_eeprom_mode mode)
{
- struct {
- u8 _rsv[4];
-
- __le16 tag;
- __le16 len;
- __le32 addr;
- __le32 valid;
- u8 data[16];
- } __packed req = {
- .tag = cpu_to_le16(UNI_EFUSE_ACCESS),
- .len = cpu_to_le16(sizeof(req) - 4),
- .addr = cpu_to_le32(round_down(offset,
- MT7996_EEPROM_BLOCK_SIZE)),
+ struct mt7996_mcu_eeprom_access req = {
+ .info.len = cpu_to_le16(sizeof(req) - 4),
};
+ struct mt7996_mcu_eeprom_access_event *event;
struct sk_buff *skb;
- bool valid;
- int ret;
+ int ret, cmd;
+ u32 addr;
+
+ switch (mode) {
+ case EEPROM_MODE_EFUSE:
+ addr = round_down(offset, MT7996_EEPROM_BLOCK_SIZE);
+ cmd = MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL);
+ req.info.tag = cpu_to_le16(UNI_EFUSE_ACCESS);
+ break;
+ case EEPROM_MODE_EXT:
+ addr = round_down(offset, MT7996_EXT_EEPROM_BLOCK_SIZE);
+ cmd = MCU_WM_UNI_CMD_QUERY(EXT_EEPROM_CTRL);
+ req.info.tag = cpu_to_le16(UNI_EXT_EEPROM_ACCESS);
+ req.eeprom.ext_eeprom.data_len = cpu_to_le32(buf_len);
+ break;
+ default:
+ return -EINVAL;
+ }
- ret = mt76_mcu_send_and_get_msg(&dev->mt76,
- MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL),
- &req, sizeof(req), true, &skb);
+ req.info.addr = cpu_to_le32(addr);
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, cmd, &req, sizeof(req),
+ true, &skb);
if (ret)
return ret;
- valid = le32_to_cpu(*(__le32 *)(skb->data + 16));
- if (valid) {
- u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12));
+ event = (struct mt7996_mcu_eeprom_access_event *)skb->data;
+ if (event->valid) {
+ u32 ret_len = le32_to_cpu(event->eeprom.ext_eeprom.data_len);
+
+ addr = le32_to_cpu(event->addr);
if (!buf)
buf = (u8 *)dev->mt76.eeprom.data + addr;
- if (!buf_len || buf_len > MT7996_EEPROM_BLOCK_SIZE)
- buf_len = MT7996_EEPROM_BLOCK_SIZE;
- skb_pull(skb, 48);
- memcpy(buf, skb->data, buf_len);
+ switch (mode) {
+ case EEPROM_MODE_EFUSE:
+ if (!buf_len || buf_len > MT7996_EEPROM_BLOCK_SIZE)
+ buf_len = MT7996_EEPROM_BLOCK_SIZE;
+
+ memcpy(buf, event->eeprom.efuse, buf_len);
+ break;
+ case EEPROM_MODE_EXT:
+ if (!buf_len || buf_len > MT7996_EXT_EEPROM_BLOCK_SIZE)
+ buf_len = MT7996_EXT_EEPROM_BLOCK_SIZE;
+
+ memcpy(buf, event->eeprom.ext_eeprom.data,
+ ret_len < buf_len ? ret_len : buf_len);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
} else {
ret = -EINVAL;
}
@@ -4052,7 +4075,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_l
return ret;
}
-int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
+int mt7996_mcu_get_efuse_free_block(struct mt7996_dev *dev, u8 *block_num)
{
struct {
u8 _rsv[4];
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
index d70540982983..39df13679779 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
@@ -145,7 +145,7 @@ struct mt7996_mcu_background_chain_ctrl {
u8 rsv[2];
} __packed;
-struct mt7996_mcu_eeprom {
+struct mt7996_mcu_eeprom_update {
u8 _rsv[4];
__le16 tag;
@@ -155,6 +155,43 @@ struct mt7996_mcu_eeprom {
__le16 buf_len;
} __packed;
+union eeprom_data {
+ struct {
+ __le32 data_len;
+ DECLARE_FLEX_ARRAY(u8, data);
+ } ext_eeprom;
+ DECLARE_FLEX_ARRAY(u8, efuse);
+} __packed;
+
+struct mt7996_mcu_eeprom_info {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ __le32 addr;
+ __le32 valid;
+} __packed;
+
+struct mt7996_mcu_eeprom_access {
+ struct mt7996_mcu_eeprom_info info;
+ union eeprom_data eeprom;
+} __packed;
+
+struct mt7996_mcu_eeprom_access_event {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ __le32 version;
+ __le32 addr;
+ __le32 valid;
+ __le32 size;
+ __le32 magic_no;
+ __le32 type;
+ __le32 rsv[4];
+ union eeprom_data eeprom;
+} __packed;
+
struct mt7996_mcu_phy_rx_info {
u8 category;
u8 rate;
@@ -875,6 +912,10 @@ enum {
UNI_EFUSE_BUFFER_RD,
};
+enum {
+ UNI_EXT_EEPROM_ACCESS = 1,
+};
+
enum {
UNI_VOW_DRR_CTRL,
UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
index 3ff730e36fa6..ea1f656a9334 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
@@ -85,6 +85,7 @@
#define MT7996_EEPROM_SIZE 7680
#define MT7996_EEPROM_BLOCK_SIZE 16
+#define MT7996_EXT_EEPROM_BLOCK_SIZE 1024
#define MT7996_TOKEN_SIZE 16384
#define MT7996_HW_TOKEN_SIZE 8192
@@ -169,6 +170,13 @@ enum mt7996_fem_type {
MT7996_FEM_MIX,
};
+enum mt7996_eeprom_mode {
+ EEPROM_MODE_DEFAULT_BIN,
+ EEPROM_MODE_EFUSE,
+ EEPROM_MODE_FLASH,
+ EEPROM_MODE_EXT,
+};
+
enum mt7996_txq_id {
MT7996_TXQ_FWDL = 16,
MT7996_TXQ_MCU_WM,
@@ -441,7 +449,7 @@ struct mt7996_dev {
u32 hw_pattern;
- bool flash_mode:1;
+ u8 eeprom_mode;
bool has_eht:1;
struct {
@@ -717,8 +725,9 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta,
void *data, u8 link_id, u32 field);
int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
-int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len);
-int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len,
+ enum mt7996_eeprom_mode mode);
+int mt7996_mcu_get_efuse_free_block(struct mt7996_dev *dev, u8 *block_num);
int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap);
int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band);
int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action);
@@ -816,6 +825,11 @@ static inline bool mt7996_has_wa(struct mt7996_dev *dev)
return !is_mt7990(&dev->mt76);
}
+static inline bool mt7996_has_ext_eeprom(struct mt7996_dev *dev)
+{
+ return !is_mt7996(&dev->mt76);
+}
+
void mt7996_mac_init(struct mt7996_dev *dev);
u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw);
bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask);
--
2.51.0
More information about the Linux-mediatek
mailing list