[PATCH] wifi: mt76: mt7925: add AXI DMA calibration cache for MT7928

Allan Wang <[email protected]>
Newsgroups org.kernel.vger.linux-wireless,org.infradead.lists.linux-mediatek
Message-ID <[email protected]>
MT7928 supports caching calibration (fact-cal) data in a
host-side DMA buffer over AXI DMA, so the firmware can reuse previously
computed calibration results across warm resets instead of recomputing
them on every bring-up.

Add a new cached_cal source file under mt7925/, built into mt7925e
since the cache is reachable only over PCIe AXI DMA, that:
 - allocates a coherent DMA region laid out for the common/group/channel
   fact-cal buffers plus the mapping table,
 - programs the firmware with the region base/offset via the new
   MCU_UNI_CMD_AXIDMA command,
 - builds and streams the fact-cal mapping table to the firmware via the
   new MCU_UNI_CMD_FACT_CAL command.

On MT7928 run-firmware, the cache is enabled only when the chip
advertises MT792x_CHIP_CAP_AXIDMA_EN. A carried-over buffer that still
holds FW-written cal data is reused (reused=true); a freshly allocated
zeroed buffer starts cold. The region is freed on device unregister.

The buf_info and mapping-table layouts are size-asserted to match the
MT7928 firmware ABI.

Signed-off-by: Allan Wang <[email protected]>
---
 .../wireless/mediatek/mt76/mt76_connac_mcu.h  |   2 +
 .../wireless/mediatek/mt76/mt7925/Makefile    |   2 +-
 .../mediatek/mt76/mt7925/cached_cal.c         | 304 ++++++++++++++++++
 .../mediatek/mt76/mt7925/cached_cal.h         | 151 +++++++++
 .../wireless/mediatek/mt76/mt7925/mt7925.h    |   1 +
 .../net/wireless/mediatek/mt76/mt7925/pci.c   |  62 +++-
 .../wireless/mediatek/mt76/mt7925/pci_mac.c   |   2 +
 .../wireless/mediatek/mt76/mt7925/pci_mcu.c   |   2 +
 drivers/net/wireless/mediatek/mt76/mt792x.h   |   8 +
 9 files changed, 532 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.c
 create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.h

diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 0ededa569e9a..e46cb4ae37a9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1357,9 +1357,11 @@ enum {
 	MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
 	MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
 	MCU_UNI_CMD_EXT_EEPROM_CTRL = 0x74,
+	MCU_UNI_CMD_FACT_CAL = 0x7c,
 	MCU_UNI_CMD_RADIO_STATUS = 0x80,
 	MCU_UNI_CMD_MLD = 0x82,
 	MCU_UNI_CMD_SDO = 0x88,
+	MCU_UNI_CMD_AXIDMA = 0xa2,
 };
 
 enum {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
index f9dcc0bba393..33539121a15c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
@@ -6,5 +6,5 @@ obj-$(CONFIG_MT7925U) += mt7925u.o
 
 mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o
 mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
-mt7925e-y := pci.o pci_mac.o pci_mcu.o
+mt7925e-y := pci.o pci_mac.o pci_mcu.o cached_cal.o
 mt7925u-y := usb.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.c b/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.c
new file mode 100644
index 000000000000..03bd682f1224
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#include <linux/dma-mapping.h>
+#include "../mt792x.h"
+#include "cached_cal.h"
+
+static void mt7925_cal_cache_calc_layout(struct mt7925_cal_cache_layout *l)
+{
+	u32 hdr = sizeof(struct mt7925_fact_cal_buf_info);
+
+	l->common_stride  = hdr + MT7925_FACT_CAL_LEN_COM;
+	l->group_stride   = hdr + MT7925_FACT_CAL_LEN_GRP;
+	l->channel_stride = hdr + MT7925_FACT_CAL_LEN_CH;
+
+	l->common_off  = 0;
+	l->group_off   = l->common_off +
+		l->common_stride * MT7925_FACT_CAL_COMMON_BAND_NUM;
+	l->channel_off = l->group_off +
+		l->group_stride * MT7925_FACT_CAL_GROUP_NUM;
+	l->mapping_off = l->channel_off +
+		l->channel_stride * MT7925_FACT_CAL_CH_NUM_ALL;
+	l->total = l->mapping_off +
+		sizeof(struct mt7925_fact_cal_mapping_tbl);
+}
+
+static u32 mt7925_cal_cache_region_size(void)
+{
+	struct mt7925_cal_cache_layout l;
+
+	mt7925_cal_cache_calc_layout(&l);
+
+	return l.total;
+}
+
+int mt7925_axidma_alloc(struct mt792x_dev *dev)
+{
+	u32 total_size;
+	dma_addr_t dma_addr;
+	void *va;
+
+	if (dev->cached_cal.va)
+		return 0;
+
+	total_size = mt7925_cal_cache_region_size();
+
+	va = dma_alloc_coherent(dev->mt76.dev, total_size, &dma_addr,
+				GFP_KERNEL);
+	if (!va)
+		return -ENOMEM;
+
+	dev->cached_cal.va = va;
+	dev->cached_cal.dma_addr = dma_addr;
+	dev->cached_cal.size = total_size;
+
+	return 0;
+}
+
+void mt7925_axidma_free(struct mt792x_dev *dev)
+{
+	if (!dev->cached_cal.va)
+		return;
+
+	dma_free_coherent(dev->mt76.dev, dev->cached_cal.size,
+			  dev->cached_cal.va, dev->cached_cal.dma_addr);
+
+	memset(&dev->cached_cal, 0, sizeof(dev->cached_cal));
+}
+
+int mt7925_mcu_send_axidma_info(struct mt792x_dev *dev)
+{
+	struct mt7925_axidma_info_req *req;
+	u32 remap_base, pa_offset;
+	int ret;
+
+	if (!dev->cached_cal.va)
+		return -EINVAL;
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	remap_base = (u32)(dev->cached_cal.dma_addr >> 16);
+	pa_offset = (u32)(dev->cached_cal.dma_addr & 0xFFFF);
+
+	req->remap.tag = cpu_to_le16(MT7925_AXIDMA_TAG_REMAP);
+	req->remap.len = cpu_to_le16(sizeof(req->remap));
+	req->remap.remap_addr = cpu_to_le32(remap_base);
+
+	req->fact_cal.tag = cpu_to_le16(MT7925_AXIDMA_TAG_FACT_CAL);
+	req->fact_cal.len = cpu_to_le16(sizeof(req->fact_cal));
+	req->fact_cal.host_addr_offset = cpu_to_le32(pa_offset);
+	req->fact_cal.size = cpu_to_le32(dev->cached_cal.size);
+	req->fact_cal.bound = cpu_to_le32(pa_offset + dev->cached_cal.size - 1);
+
+	ret = mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(AXIDMA),
+				req, sizeof(*req), true);
+
+	kfree(req);
+
+	if (ret)
+		dev_err(dev->mt76.dev,
+			"failed to send AXI DMA info: %d\n", ret);
+
+	return ret;
+}
+
+static bool mt7925_cal_cache_read_slot(struct mt792x_dev *dev, u32 region_off,
+				       u32 stride, u32 idx,
+				       u32 *cal_param, u32 *total_buf_num)
+{
+	const struct mt7925_fact_cal_buf_info *bi;
+	u32 off = region_off + stride * idx;
+
+	if (!dev->cached_cal.va || off + sizeof(*bi) > dev->cached_cal.size)
+		return false;
+
+	bi = (const struct mt7925_fact_cal_buf_info *)
+		((const u8 *)dev->cached_cal.va + off);
+	if (cal_param)
+		*cal_param = le32_to_cpu(bi->cal_param);
+	if (total_buf_num)
+		*total_buf_num = le32_to_cpu(bi->total_buf_num);
+	return true;
+}
+
+static void mt7925_fact_cal_build_mapping_tbl(struct mt792x_dev *dev,
+					      struct mt7925_fact_cal_mapping_tbl *tbl)
+{
+	dma_addr_t common_pa, group_pa, channel_pa, mapping_pa;
+	u32 hdr = sizeof(struct mt7925_fact_cal_buf_info);
+	dma_addr_t base = dev->cached_cal.dma_addr;
+	struct mt7925_cal_cache_layout l;
+
+	mt7925_cal_cache_calc_layout(&l);
+
+	common_pa  = base + l.common_off;
+	group_pa   = base + l.group_off;
+	channel_pa = base + l.channel_off;
+	mapping_pa = base + l.mapping_off;
+
+	memset(tbl, 0, sizeof(*tbl));
+
+	tbl->common.phy_addr_h = cpu_to_le32(upper_32_bits(common_pa));
+	tbl->common.phy_addr_l = cpu_to_le32(lower_32_bits(common_pa));
+	tbl->common.offset     = cpu_to_le32(l.common_stride);
+
+	tbl->group.phy_addr_h  = cpu_to_le32(upper_32_bits(group_pa));
+	tbl->group.phy_addr_l  = cpu_to_le32(lower_32_bits(group_pa));
+	tbl->group.offset      = cpu_to_le32(l.group_stride);
+
+	tbl->channel.phy_addr_h = cpu_to_le32(upper_32_bits(channel_pa));
+	tbl->channel.phy_addr_l = cpu_to_le32(lower_32_bits(channel_pa));
+	tbl->channel.offset     = cpu_to_le32(l.channel_stride);
+
+	if (dev->cached_cal.reused) {
+		u32 i, cal_param, total_buf_num;
+
+		for (i = 0; i < MT7925_FACT_CAL_COMMON_BAND_NUM; i++)
+			if (mt7925_cal_cache_read_slot(dev, l.common_off,
+						       l.common_stride, i,
+						       &cal_param, NULL))
+				tbl->common.band[i] = cal_param & 0xff;
+
+		for (i = 0; i < MT7925_FACT_CAL_GROUP_NUM; i++)
+			if (mt7925_cal_cache_read_slot(dev, l.group_off,
+						       l.group_stride, i,
+						       &cal_param, NULL))
+				tbl->group.group[i] = cal_param & 0xff;
+
+		for (i = 0; i < MT7925_FACT_CAL_CH_NUM_ALL; i++)
+			if (mt7925_cal_cache_read_slot(dev, l.channel_off,
+						       l.channel_stride, i,
+						       &cal_param,
+						       &total_buf_num)) {
+				tbl->channel.cal_param[i] =
+					cpu_to_le32(cal_param);
+				tbl->channel.total_buf_num[i] =
+					total_buf_num & 0xff;
+			}
+	}
+
+	tbl->buf_info_size = cpu_to_le32(hdr);
+	tbl->phy_addr_h    = cpu_to_le32(upper_32_bits(mapping_pa));
+	tbl->phy_addr_l    = cpu_to_le32(lower_32_bits(mapping_pa));
+}
+
+static int mt7925_mcu_fact_cal_rapid_set(struct mt792x_dev *dev, u8 cal_type,
+					 u32 seq_num, bool done,
+					 const void *data, u32 data_len)
+{
+	struct mt7925_fact_cal_req *req;
+	int ret;
+
+	if (data_len > MT7925_FACT_CAL_DATA_BUF_LEN)
+		return -EINVAL;
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->set.tag = cpu_to_le16(MT7925_FACT_CAL_TAG_RAPID_SET);
+	req->set.len = cpu_to_le16(sizeof(req->set));
+	req->set.seq_num = cpu_to_le32(seq_num);
+	req->set.buf_data_len = cpu_to_le32(data_len);
+	req->set.cal_type = cal_type;
+	req->set.done = done;
+	if (data && data_len)
+		memcpy(req->set.buf_data, data, data_len);
+
+	/* Wait for the ACK on the final chunk only: the FW applies the whole
+	 * table there (DMAs the cached common / power-on group cal data in and
+	 * programs the PHY), so its ACK is the completion point. Waiting also
+	 * drains the ACKs the earlier chunks left in the MCU response queue,
+	 * since UNI commands always request one.
+	 */
+	ret = mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(FACT_CAL),
+				req, sizeof(*req), done);
+	kfree(req);
+
+	return ret;
+}
+
+int mt7925_mcu_send_fact_cal_mapping_tbl(struct mt792x_dev *dev)
+{
+	struct mt7925_fact_cal_mapping_tbl *tbl;
+	__le32 cfg[MT7925_FACT_CAL_BUF_CFG_LEN / sizeof(__le32)] = {0};
+	u32 seq = 0, off = 0, left;
+	int ret;
+
+	if (!dev->cached_cal.va)
+		return -EINVAL;
+
+	tbl = kzalloc(sizeof(*tbl), GFP_KERNEL);
+	if (!tbl)
+		return -ENOMEM;
+
+	mt7925_fact_cal_build_mapping_tbl(dev, tbl);
+
+	cfg[1] = cpu_to_le32(sizeof(*tbl));
+	cfg[2] = cpu_to_le32(MT7925_FACT_CAL_DATA_BUF_NUM_MAX);
+	ret = mt7925_mcu_fact_cal_rapid_set(dev, MT7925_FACT_CAL_TYPE_MAPPING_TBL,
+					    seq, false, cfg, sizeof(cfg));
+	if (ret) {
+		dev_err(dev->mt76.dev,
+			"fact-cal mapping tbl header send failed (seq=%u): %d\n",
+			seq, ret);
+		goto out;
+	}
+
+	left = sizeof(*tbl);
+	while (left) {
+		u32 chunk = min_t(u32, left, MT7925_FACT_CAL_DATA_BUF_LEN);
+
+		ret = mt7925_mcu_fact_cal_rapid_set(dev,
+						    MT7925_FACT_CAL_TYPE_MAPPING_TBL,
+						    ++seq, false,
+						    (u8 *)tbl + off, chunk);
+		if (ret) {
+			dev_err(dev->mt76.dev,
+				"fact-cal mapping tbl data send failed (seq=%u, off=%u, chunk=%u): %d\n",
+				seq, off, chunk, ret);
+			goto out;
+		}
+
+		off  += chunk;
+		left -= chunk;
+	}
+
+	ret = mt7925_mcu_fact_cal_rapid_set(dev, MT7925_FACT_CAL_TYPE_MAPPING_TBL,
+					    ++seq, true, NULL, 0);
+	if (ret) {
+		dev_err(dev->mt76.dev,
+			"fact-cal mapping tbl done send failed (seq=%u): %d\n",
+			seq, ret);
+		goto out;
+	}
+out:
+	kfree(tbl);
+
+	return ret;
+}
+
+int mt7925_mcu_fact_cal_set_bypass(struct mt792x_dev *dev, bool enable)
+{
+	struct {
+		u8 _rsv[4];
+
+		struct {
+			__le16 tag;
+			__le16 len;
+			__le32 data;
+		} __packed update_flag;
+	} req = {
+		.update_flag = {
+			.tag  = cpu_to_le16(MT7925_FACT_CAL_TAG_UPDATE_FLAG),
+			.len  = cpu_to_le16(sizeof(req.update_flag)),
+			.data = cpu_to_le32(enable ? 1 : 0),
+		},
+	};
+
+	return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(FACT_CAL),
+				 &req, sizeof(req), false);
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.h b/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.h
new file mode 100644
index 000000000000..6572513b7646
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/cached_cal.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#ifndef __CACHED_CAL_H
+#define __CACHED_CAL_H
+
+enum mt7925_axidma_info_tag {
+	MT7925_AXIDMA_TAG_REMAP = 0,
+	MT7925_AXIDMA_TAG_FACT_CAL = 1,
+	MT7925_AXIDMA_TAG_NUM
+};
+
+enum mt7925_fact_cal_tag {
+	MT7925_FACT_CAL_TAG_TRIGGER	= 2,
+	MT7925_FACT_CAL_TAG_UPDATE_FLAG	= 3,
+	MT7925_FACT_CAL_TAG_RAPID_GET	= 4,
+	MT7925_FACT_CAL_TAG_RAPID_SET	= 5,
+};
+
+enum mt7925_fact_cal_type {
+	MT7925_FACT_CAL_TYPE_COMMON		= 0,
+	MT7925_FACT_CAL_TYPE_GROUP		= 1,
+	MT7925_FACT_CAL_TYPE_CHANNEL		= 2,
+	MT7925_FACT_CAL_TYPE_MAPPING_TBL	= 4,
+};
+
+#define MT7925_FACT_CAL_DATA_BUF_LEN		1400
+#define MT7925_FACT_CAL_BUF_CFG_LEN			16
+#define MT7925_FACT_CAL_DATA_BUF_NUM_MAX	4
+#define MT7925_FACT_CAL_BUF_CFG_U32_LEN		4
+#define MT7925_FACT_CAL_DATA_MAX_BUF_LEN \
+	(MT7925_FACT_CAL_DATA_BUF_NUM_MAX * MT7925_FACT_CAL_BUF_CFG_U32_LEN)
+
+#define MT7925_FACT_CAL_COMMON_BAND_NUM		2
+#define MT7925_FACT_CAL_GROUP_NUM		35
+#define MT7925_FACT_CAL_CH_NUM_2G		14
+#define MT7925_FACT_CAL_CH_NUM_5G		68
+#define MT7925_FACT_CAL_CH_NUM_6G		109
+#define MT7925_FACT_CAL_CH_NUM_ALL \
+	(MT7925_FACT_CAL_CH_NUM_2G + MT7925_FACT_CAL_CH_NUM_5G + \
+	 MT7925_FACT_CAL_CH_NUM_6G)
+
+#define MT7925_FACT_CAL_LEN_COM			600
+#define MT7925_FACT_CAL_LEN_GRP			4500
+#define MT7925_FACT_CAL_LEN_CH			2800
+
+struct mt7925_fact_cal_buf_info {
+	__le32 cal_type;
+	__le32 cal_param;
+	__le32 buf_seq_num;
+	__le32 total_buf_num;
+	__le32 buf_data_len;
+	u8 valid;
+	u8 done;
+	u8 rsv[2];
+	__le32 buf_cfg_info[MT7925_FACT_CAL_DATA_MAX_BUF_LEN];
+} __packed;
+
+static_assert(sizeof(struct mt7925_fact_cal_buf_info) == 88,
+	      "buf_info must match the MT7928 FW (88 bytes) or it rejects the mapping table");
+
+struct mt7925_fact_cal_common_map {
+	__le32 phy_addr_h;
+	__le32 phy_addr_l;
+	__le32 offset;
+	u8 band[MT7925_FACT_CAL_COMMON_BAND_NUM];
+	u8 rsv[2];
+} __packed;
+
+struct mt7925_fact_cal_group_map {
+	__le32 phy_addr_h;
+	__le32 phy_addr_l;
+	__le32 offset;
+	u8 group[MT7925_FACT_CAL_GROUP_NUM];
+	u8 rsv;
+} __packed;
+
+struct mt7925_fact_cal_channel_map {
+	__le32 phy_addr_h;
+	__le32 phy_addr_l;
+	__le32 offset;
+	__le32 cal_param[MT7925_FACT_CAL_CH_NUM_ALL];
+	u8 total_buf_num[MT7925_FACT_CAL_CH_NUM_ALL];
+	u8 rsv;
+} __packed;
+
+struct mt7925_fact_cal_mapping_tbl {
+	struct mt7925_fact_cal_common_map common;
+	struct mt7925_fact_cal_group_map group;
+	struct mt7925_fact_cal_channel_map channel;
+	__le32 buf_info_size;
+	__le32 phy_addr_h;
+	__le32 phy_addr_l;
+} __packed;
+
+static_assert(sizeof(struct mt7925_fact_cal_mapping_tbl) == 1044,
+	      "mapping table must match the MT7928 FW (1044 bytes) or the FW parse corrupts");
+
+struct mt7925_cal_cache_layout {
+	u32 common_off, group_off, channel_off, mapping_off;
+	u32 common_stride, group_stride, channel_stride;
+	u32 total;
+};
+
+struct mt7925_axidma_info_req {
+	u8 _rsv[4];
+
+	struct {
+		__le16 tag;
+		__le16 len;
+		__le32 remap_addr;
+		__le32 reserved;
+	} __packed remap;
+
+	struct {
+		__le16 tag;
+		__le16 len;
+		__le32 host_addr_offset;
+		__le32 size;
+		__le32 bound;
+		__le32 reserved;
+	} __packed fact_cal;
+} __packed;
+
+struct mt7925_fact_cal_req {
+	u8 _rsv[4];
+
+	struct {
+		__le16 tag;
+		__le16 len;
+		__le32 data;
+		__le32 seq_num;
+		__le32 buf_data_len;
+		u8 cal_type;
+		u8 done;
+		u8 rsv[2];
+		u8 buf_data[MT7925_FACT_CAL_DATA_BUF_LEN];
+	} __packed set;
+} __packed;
+
+int mt7925_axidma_alloc(struct mt792x_dev *dev);
+
+void mt7925_axidma_free(struct mt792x_dev *dev);
+
+int mt7925_mcu_send_axidma_info(struct mt792x_dev *dev);
+
+int mt7925_mcu_send_fact_cal_mapping_tbl(struct mt792x_dev *dev);
+
+int mt7925_mcu_fact_cal_set_bypass(struct mt792x_dev *dev, bool enable);
+
+#endif /* __CACHED_CAL_H */
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 321e732347f2..4d79c1e66e66 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -353,6 +353,7 @@ int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd,
 
 int mt7925e_mac_reset(struct mt792x_dev *dev);
 int mt7925e_mcu_init(struct mt792x_dev *dev);
+void mt7925e_axidma_cal_cache_init(struct mt792x_dev *dev);
 void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data);
 void mt7928_mac_add_txs_msg(struct mt792x_dev *dev, void *evt);
 void mt7925_set_runtime_pm(struct mt792x_dev *dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index 02ef09dd797d..724abd2be57c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -9,6 +9,7 @@
 #include "mac.h"
 #include "mcu.h"
 #include "regd.h"
+#include "cached_cal.h"
 #include "../dma.h"
 
 static const struct pci_device_id mt7925_pci_device_table[] = {
@@ -38,6 +39,62 @@ static int mt7925e_init_reset(struct mt792x_dev *dev)
 	return mt792x_wpdma_reset(dev, true);
 }
 
+/* Set up the factory-calibration cache in host memory. PCIe only: the FW
+ * reaches the buffer through AXI DMA, so it has no meaning on other buses.
+ * Every failure just leaves the cache off, which is not fatal.
+ */
+void mt7925e_axidma_cal_cache_init(struct mt792x_dev *dev)
+{
+	if (!(dev->phy.chip_cap & MT792x_CHIP_CAP_AXIDMA_EN))
+		return;
+
+	if (dev->cached_cal.va) {
+		/* Carried-over buffer still holds FW-written cal data. */
+		dev->cached_cal.reused = true;
+	} else {
+		if (mt7925_axidma_alloc(dev)) {
+			dev_warn(dev->mt76.dev,
+				 "AXI DMA cal cache disabled: buffer alloc failed\n");
+			return;
+		}
+		/* Freshly allocated buffer is zeroed: no cal data yet. */
+		dev->cached_cal.reused = false;
+	}
+
+	if (mt7925_mcu_send_axidma_info(dev)) {
+		dev_warn(dev->mt76.dev,
+			 "AXI DMA cal cache disabled: info cmd to FW failed\n");
+		return;
+	}
+
+	/* Enable the bypass-cal flow so the FW reuses cached cal data
+	 * (common / power-on group) instead of running a full calibration;
+	 * must be set before the mapping table is sent.
+	 */
+	if (mt7925_mcu_fact_cal_set_bypass(dev, true)) {
+		dev_warn(dev->mt76.dev,
+			 "AXI DMA cal cache disabled: bypass flag set failed\n");
+		return;
+	}
+
+	if (mt7925_mcu_send_fact_cal_mapping_tbl(dev)) {
+		dev_warn(dev->mt76.dev,
+			 "AXI DMA cal cache disabled: mapping tbl send failed\n");
+		return;
+	}
+
+	if (mt7925_mcu_fact_cal_set_bypass(dev, false)) {
+		dev_warn(dev->mt76.dev,
+			 "AXI DMA cal cache disabled: bypass flag set failed\n");
+		return;
+	}
+
+	dev_info(dev->mt76.dev,
+		 "AXI DMA cal cache enabled (DMA=%pad, size=%u, reused=%d)\n",
+		 &dev->cached_cal.dma_addr, dev->cached_cal.size,
+		 dev->cached_cal.reused);
+}
+
 static void mt7925e_unregister_device(struct mt792x_dev *dev)
 {
 	int i;
@@ -58,6 +115,9 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev)
 
 	mt7925_tx_token_put(dev);
 	__mt792x_mcu_drv_pmctrl(dev);
+
+	mt7925_axidma_free(dev);
+
 	mt792x_dma_cleanup(dev);
 	mt792x_wfsys_reset(dev);
 	skb_queue_purge(&dev->mt76.mcu.res_q);
@@ -587,7 +647,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev,
 	is_mt7928_hw = (pdev->device == 0x7928 || pdev->device == 0x7935);
 
 	if (is_mt7928_hw)
-		ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(34));
+		ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34));
 	else
 		ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
index 8477d21abc66..c90f129b3a34 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
@@ -135,6 +135,8 @@ int mt7925e_mac_reset(struct mt792x_dev *dev)
 	if (err)
 		goto out;
 
+	mt7925e_axidma_cal_cache_init(dev);
+
 	err = mt7925_mcu_set_eeprom(dev);
 	if (err)
 		goto out;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
index 72707eddc3db..258f3753db1f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
@@ -46,6 +46,8 @@ int mt7925e_mcu_init(struct mt792x_dev *dev)
 	mt76_rmw_field(dev, dev->pcie_reg->pm, MT_PCIE_MAC_PM_L0S_DIS, 1);
 
 	err = mt7925_run_firmware(dev);
+	if (!err)
+		mt7925e_axidma_cal_cache_init(dev);
 
 	mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false);
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 9efc251cb745..740d517eb38b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -33,6 +33,7 @@
 #define MT792x_CHIP_CAP_REGD_EN BIT(5)
 #define MT792x_CHIP_CAP_MLO_EN BIT(8)
 #define MT792x_CHIP_CAP_MLO_EML_EN BIT(9)
+#define MT792x_CHIP_CAP_AXIDMA_EN BIT(16)
 
 /* NOTE: used to map mt76_rates. idx may change if firmware expands table */
 #define MT792x_BASIC_RATES_TBL	14
@@ -342,6 +343,13 @@ struct mt792x_dev {
 	struct ieee80211_vif *nan_vif;
 	const struct ieee80211_iface_combination *iface_combinations;
 	int n_iface_combinations;
+
+	struct {
+		void *va;
+		dma_addr_t dma_addr;
+		u32 size;
+		bool reused;
+	} cached_cal;
 };
 
 static inline struct mt792x_bss_conf *
-- 
2.45.2
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.