Linux-mediatek Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] wifi: mt76: mt7925: add AXI DMA calibration cache for MT7928
@ 2026-08-10  3:50 Allan Wang
  0 siblings, 0 replies; only message in thread
From: Allan Wang @ 2026-08-10  3:50 UTC (permalink / raw)
  To: nbd, lorenzo
  Cc: litien.chang, jb.tsai, emery.hsin, deren.wu, mingyen.hsieh,
	Sean.Wang, robin.chiu, posh.sun, Quan.Zhou, Ryder.Lee,
	Shayne.Chen, linux-wireless, linux-mediatek, Allan Wang

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 <allan.wang@mediatek.com>
---
 .../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



^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-08-10  3:51 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-10  3:50 [PATCH] wifi: mt76: mt7925: add AXI DMA calibration cache for MT7928 Allan Wang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox