[][MAC80211][mt76][Implement pre-cal support in testmode]

[Description]
Add atenl and mt76-test commands for getting pre-cal data.

[Release-log]
N/A

Change-Id: Ib113465d0ce1c8e9c8d5461e6911ba26c0afde1c
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/6365523
diff --git a/autobuild_mac80211_release/package/kernel/mt76/patches/1112-mt76-testmode-add-pre-cal-support.patch b/autobuild_mac80211_release/package/kernel/mt76/patches/1112-mt76-testmode-add-pre-cal-support.patch
new file mode 100644
index 0000000..1685acc
--- /dev/null
+++ b/autobuild_mac80211_release/package/kernel/mt76/patches/1112-mt76-testmode-add-pre-cal-support.patch
@@ -0,0 +1,846 @@
+From 7b05ee7c55f2ac724af056e8ae16f8ca2835bc2a Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 31 Aug 2022 20:06:52 +0800
+Subject: [PATCH 1112/1121] mt76: testmode: add pre-cal support
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Change-Id: Ibfbbc3443de994eeb4daa5e364b0a90f5d7d3bcd
+---
+ eeprom.c          |   6 +-
+ mt76.h            |   1 +
+ mt76_connac_mcu.h |   1 +
+ mt7915/eeprom.h   |  34 +++-
+ mt7915/mcu.c      |  27 ++-
+ mt7915/mt7915.h   |   5 +
+ mt7915/testmode.c | 425 +++++++++++++++++++++++++++++++++++++++++++++-
+ mt7915/testmode.h |  36 ++++
+ testmode.c        |  15 +-
+ testmode.h        |  17 ++
+ tools/fields.c    |   8 +
+ 11 files changed, 562 insertions(+), 13 deletions(-)
+
+diff --git a/eeprom.c b/eeprom.c
+index 1e41b94..e083964 100644
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -89,8 +89,10 @@ int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int offset, int len)
+ 	}
+ 
+ #ifdef CONFIG_NL80211_TESTMODE
+-	dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
+-	dev->test_mtd.offset = offset;
++	if (len == dev->eeprom.size) {
++		dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
++		dev->test_mtd.offset = offset;
++	}
+ #endif
+ 
+ out_put_node:
+diff --git a/mt76.h b/mt76.h
+index d03f312..0ed4188 100644
+--- a/mt76.h
++++ b/mt76.h
+@@ -611,6 +611,7 @@ struct mt76_testmode_ops {
+ 			  enum mt76_testmode_state new_state);
+ 	int (*dump_stats)(struct mt76_phy *phy, struct sk_buff *msg);
+ 	int (*set_eeprom)(struct mt76_phy *phy, u32 offset, u8 *val, u8 action);
++	int (*dump_precal)(struct mt76_phy *phy, struct sk_buff *msg, int flag, int type);
+ };
+ 
+ struct mt76_testmode_entry_data {
+diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
+index cb1e94a..9789380 100644
+--- a/mt76_connac_mcu.h
++++ b/mt76_connac_mcu.h
+@@ -963,6 +963,7 @@ enum {
+ 
+ /* ext event table */
+ enum {
++	MCU_EXT_EVENT_RF_TEST = 0x4,
+ 	MCU_EXT_EVENT_PS_SYNC = 0x5,
+ 	MCU_EXT_EVENT_FW_LOG_2_HOST = 0x13,
+ 	MCU_EXT_EVENT_THERMAL_PROTECT = 0x22,
+diff --git a/mt7915/eeprom.h b/mt7915/eeprom.h
+index 7578ac6..b980342 100644
+--- a/mt7915/eeprom.h
++++ b/mt7915/eeprom.h
+@@ -39,10 +39,18 @@ enum mt7915_eeprom_field {
+ };
+ 
+ #define MT_EE_WIFI_CAL_GROUP			BIT(0)
+-#define MT_EE_WIFI_CAL_DPD			GENMASK(2, 1)
++#define MT_EE_WIFI_CAL_DPD_2G			BIT(2)
++#define MT_EE_WIFI_CAL_DPD_5G			BIT(1)
++#define MT_EE_WIFI_CAL_DPD_6G			BIT(3)
++#define MT_EE_WIFI_CAL_DPD			GENMASK(3, 1)
+ #define MT_EE_CAL_UNIT				1024
+-#define MT_EE_CAL_GROUP_SIZE			(49 * MT_EE_CAL_UNIT + 16)
+-#define MT_EE_CAL_DPD_SIZE			(54 * MT_EE_CAL_UNIT)
++#define MT_EE_CAL_GROUP_SIZE_7915		(49 * MT_EE_CAL_UNIT + 16)
++#define MT_EE_CAL_GROUP_SIZE_7916		(54 * MT_EE_CAL_UNIT + 16)
++#define MT_EE_CAL_GROUP_SIZE_7975		(54 * MT_EE_CAL_UNIT + 16)
++#define MT_EE_CAL_GROUP_SIZE_7976		(94 * MT_EE_CAL_UNIT + 16)
++#define MT_EE_CAL_GROUP_SIZE_7916_6G		(94 * MT_EE_CAL_UNIT + 16)
++#define MT_EE_CAL_DPD_SIZE_V1			(54 * MT_EE_CAL_UNIT)
++#define MT_EE_CAL_DPD_SIZE_V2			(300 * MT_EE_CAL_UNIT)
+ 
+ #define MT_EE_WIFI_CONF0_TX_PATH		GENMASK(2, 0)
+ #define MT_EE_WIFI_CONF0_BAND_SEL		GENMASK(7, 6)
+@@ -160,6 +168,26 @@ mt7915_tssi_enabled(struct mt7915_dev *dev, enum nl80211_band band)
+ 		return val & MT_EE_WIFI_CONF7_TSSI0_5G;
+ }
+ 
++static inline u32
++mt7915_get_cal_group_size(struct mt7915_dev *dev)
++{
++	u8 *eep = dev->mt76.eeprom.data;
++	u32 val;
++
++	if (is_mt7915(&dev->mt76)) {
++		return MT_EE_CAL_GROUP_SIZE_7915;
++	} else if (is_mt7916(&dev->mt76)) {
++		val = eep[MT_EE_WIFI_CONF + 1];
++		val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
++		return (val == MT_EE_V2_BAND_SEL_6GHZ) ? MT_EE_CAL_GROUP_SIZE_7916_6G :
++							 MT_EE_CAL_GROUP_SIZE_7916;
++	} else if (mt7915_check_adie(dev, false)) {
++		return MT_EE_CAL_GROUP_SIZE_7976;
++	} else {
++		return MT_EE_CAL_GROUP_SIZE_7975;
++	}
++}
++
+ extern const u8 mt7915_sku_group_len[MAX_SKU_RATE_GROUP_NUM];
+ 
+ #endif
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index ecdc4fb..42b1abc 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -438,6 +438,9 @@ mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb)
+ 	case MCU_EXT_EVENT_BF_STATUS_READ:
+ 		mt7915_tm_txbf_status_read(dev, skb);
+ 		break;
++	case MCU_EXT_EVENT_RF_TEST:
++		mt7915_tm_rf_test_event(dev, skb);
++		break;
+ #endif
+ 	default:
+ 		break;
+@@ -2925,7 +2928,7 @@ int mt7915_mcu_apply_group_cal(struct mt7915_dev *dev)
+ 	u8 idx = 0, *cal = dev->cal, *eep = dev->mt76.eeprom.data;
+ 	u32 total = MT_EE_CAL_GROUP_SIZE;
+ 
+-	if (1 || !(eep[MT_EE_DO_PRE_CAL] & MT_EE_WIFI_CAL_GROUP))
++	if (!(eep[offs] & MT_EE_WIFI_CAL_GROUP))
+ 		return 0;
+ 
+ 	/*
+@@ -3005,11 +3008,29 @@ int mt7915_mcu_apply_tx_dpd(struct mt7915_phy *phy)
+ {
+ 	struct mt7915_dev *dev = phy->dev;
+ 	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+-	u16 total = 2, center_freq = chandef->center_freq1;
++	enum nl80211_band band = chandef->chan->band;
++	u32 offs = is_mt7915(&dev->mt76) ? MT_EE_DO_PRE_CAL : MT_EE_DO_PRE_CAL_V2;
++	u16 center_freq = chandef->center_freq1;
+ 	u8 *cal = dev->cal, *eep = dev->mt76.eeprom.data;
++	u8 dpd_mask, cal_num = is_mt7915(&dev->mt76) ? 2 : 3;
+ 	int idx;
+ 
+-	if (1 || !(eep[MT_EE_DO_PRE_CAL] & MT_EE_WIFI_CAL_DPD))
++	switch (band) {
++	case NL80211_BAND_2GHZ:
++		dpd_mask = MT_EE_WIFI_CAL_DPD_2G;
++		break;
++	case NL80211_BAND_5GHZ:
++		dpd_mask = MT_EE_WIFI_CAL_DPD_5G;
++		break;
++	case NL80211_BAND_6GHZ:
++		dpd_mask = MT_EE_WIFI_CAL_DPD_6G;
++		break;
++	default:
++		dpd_mask = 0;
++		break;
++	}
++
++	if (!(eep[offs] & dpd_mask))
+ 		return 0;
+ 
+ 	idx = mt7915_dpd_freq_idx(center_freq, chandef->width);
+diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
+index 99f364c..fc0621e 100644
+--- a/mt7915/mt7915.h
++++ b/mt7915/mt7915.h
+@@ -381,6 +381,10 @@ struct mt7915_dev {
+ 	struct rchan *relay_fwlog;
+ 
+ 	void *cal;
++	u32 cur_prek_offset;
++	u8 dpd_chan_num_2g;
++	u8 dpd_chan_num_5g;
++	u8 dpd_chan_num_6g;
+ 
+ 	struct {
+ 		u8 debug_wm;
+@@ -617,6 +621,7 @@ int mt7915_mcu_fw_dbg_ctrl(struct mt7915_dev *dev, u32 module, u8 level);
+ void mt7915_mcu_rx_event(struct mt7915_dev *dev, struct sk_buff *skb);
+ void mt7915_mcu_exit(struct mt7915_dev *dev);
+ int mt7915_tm_txbf_status_read(struct mt7915_dev *dev, struct sk_buff *skb);
++void mt7915_tm_rf_test_event(struct mt7915_dev *dev, struct sk_buff *skb);
+ 
+ static inline u16 mt7915_wtbl_size(struct mt7915_dev *dev)
+ {
+diff --git a/mt7915/testmode.c b/mt7915/testmode.c
+index de6e63e..0afce0d 100644
+--- a/mt7915/testmode.c
++++ b/mt7915/testmode.c
+@@ -5,6 +5,7 @@
+ #include "mac.h"
+ #include "mcu.h"
+ #include "testmode.h"
++#include "eeprom.h"
+ 
+ enum {
+ 	TM_CHANGED_TXPOWER,
+@@ -1578,17 +1579,15 @@ mt7915_tm_rf_switch_mode(struct mt7915_dev *dev, u32 oper)
+ static int
+ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
+ {
+-#define TX_CONT_START	0x05
+-#define TX_CONT_STOP	0x06
+ 	struct mt7915_dev *dev = phy->dev;
+ 	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+ 	int freq1 = ieee80211_frequency_to_channel(chandef->center_freq1);
+ 	struct mt76_testmode_data *td = &phy->mt76->test;
+-	u32 func_idx = en ? TX_CONT_START : TX_CONT_STOP;
++	u32 func_idx = en ? RF_TEST_TX_CONT_START : RF_TEST_TX_CONT_STOP;
+ 	u8 rate_idx = td->tx_rate_idx, mode;
+ 	u16 rateval;
+ 	struct mt7915_tm_rf_test req = {
+-		.action = 1,
++		.action = RF_ACT_IN_RFTEST,
+ 		.icap_len = 120,
+ 		.op.rf.func_idx = cpu_to_le32(func_idx),
+ 	};
+@@ -1672,6 +1671,316 @@ out:
+ 				 sizeof(req), true);
+ }
+ 
++static int
++mt7915_tm_group_prek(struct mt7915_phy *phy, enum mt76_testmode_state state)
++{
++	u8 *eeprom;
++	u32 i, group_size, dpd_size, size, offs, *pre_cal;
++	int ret = 0;
++	struct mt7915_dev *dev = phy->dev;
++	struct mt76_dev *mdev = &dev->mt76;
++	struct mt7915_tm_rf_test req = {
++		.action = RF_ACT_IN_RFTEST,
++		.icap_len = 8,
++		.op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
++	};
++
++	if (!dev->flash_mode && !dev->bin_file_mode) {
++		dev_err(dev->mt76.dev, "Currently not in FLASH or BIN MODE,return!\n");
++		return 1;
++	}
++
++	eeprom = mdev->eeprom.data;
++	dev->cur_prek_offset = 0;
++	group_size = mt7915_get_cal_group_size(dev);
++	dpd_size = is_mt7915(&dev->mt76) ? MT_EE_CAL_DPD_SIZE_V1 : MT_EE_CAL_DPD_SIZE_V2;
++	size = group_size + dpd_size;
++	offs = is_mt7915(&dev->mt76) ? MT_EE_DO_PRE_CAL : MT_EE_DO_PRE_CAL_V2;
++
++	switch (state) {
++	case MT76_TM_STATE_GROUP_PREK:
++		req.op.rf.param.cal_param.func_data = cpu_to_le32(RF_PRE_CAL);
++
++		if (!dev->cal) {
++			dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
++			if (!dev->cal)
++				return -ENOMEM;
++		}
++
++		ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RF_TEST), &req,
++					sizeof(req), true);
++
++		if (!ret)
++			eeprom[offs] |= MT_EE_WIFI_CAL_GROUP;
++		break;
++	case MT76_TM_STATE_GROUP_PREK_DUMP:
++		pre_cal = (u32 *)dev->cal;
++		if (!pre_cal) {
++			dev_info(dev->mt76.dev, "Not group pre-cal yet!\n");
++			return ret;
++		}
++		dev_info(dev->mt76.dev, "Group Pre-Cal:\n");
++		for (i = 0; i < (group_size / sizeof(u32)); i += 4) {
++			dev_info(dev->mt76.dev, "[0x%08x] 0x%8x 0x%8x 0x%8x 0x%8x\n",
++				 i * sizeof(u32), pre_cal[i], pre_cal[i + 1],
++				 pre_cal[i + 2], pre_cal[i + 3]);
++		}
++		break;
++	case MT76_TM_STATE_GROUP_PREK_CLEAN:
++		pre_cal = (u32 *)dev->cal;
++		if (!pre_cal)
++			return ret;
++		memset(pre_cal, 0, group_size);
++		eeprom[offs] &= ~MT_EE_WIFI_CAL_GROUP;
++		break;
++	default:
++		return -EINVAL;
++	}
++	return ret;
++}
++
++static int
++mt7915_tm_dpd_prek(struct mt7915_phy *phy, enum mt76_testmode_state state)
++{
++#define DPD_2G_CH_BW20_BITMAP_0         0x444
++#define DPD_5G_CH_BW20_BITMAP_0         0xffffc0ff
++#define DPD_5G_CH_BW20_BITMAP_1         0x3
++#define DPD_5G_CH_BW20_BITMAP_7915_0    0x7dffc0ff
++#define DPD_6G_CH_BW20_BITMAP_0         0xffffffff
++#define DPD_6G_CH_BW20_BITMAP_1         0x07ffffff
++	bool is_set = false;
++	u8 band, do_precal, *eeprom;
++	u16 bw20_size, bw160_size;
++	u32 i, j, *bw160_freq, bw160_5g_freq[] = {5250, 5570, 5815};
++	u32 bw160_6g_freq[] = {6025, 6185, 6345, 6505, 6665, 6825, 6985};
++	u32 shift, freq, group_size, dpd_size, size, offs, *pre_cal, dpd_ch_bw20_bitmap[2] = {0};
++	__le32 func_data = 0;
++	int ret = 0;
++	struct mt7915_dev *dev = phy->dev;
++	struct mt76_dev *mdev = &dev->mt76;
++	struct mt76_phy *mphy = phy->mt76;
++	struct cfg80211_chan_def chandef_backup, *chandef = &mphy->chandef;
++	struct ieee80211_channel chan_backup, chan, *bw20_ch;
++	struct mt7915_tm_rf_test req = {
++		.action = RF_ACT_IN_RFTEST,
++		.icap_len = 8,
++		.op.rf.func_idx = cpu_to_le32(RF_TEST_RE_CAL),
++	};
++
++	if (!dev->flash_mode && !dev->bin_file_mode) {
++		dev_err(dev->mt76.dev, "Currently not in FLASH or BIN MODE,return!\n");
++		return -EOPNOTSUPP;
++	}
++
++	eeprom = mdev->eeprom.data;
++	dev->cur_prek_offset = 0;
++	group_size = mt7915_get_cal_group_size(dev);
++	dev->dpd_chan_num_2g = hweight32(DPD_2G_CH_BW20_BITMAP_0);
++	if (is_mt7915(&dev->mt76)) {
++		dev->dpd_chan_num_5g = hweight32(DPD_5G_CH_BW20_BITMAP_7915_0);
++		dev->dpd_chan_num_6g = 0;
++		dpd_size = MT_EE_CAL_DPD_SIZE_V1;
++		offs = MT_EE_DO_PRE_CAL;
++	} else {
++		dev->dpd_chan_num_5g = hweight32(DPD_5G_CH_BW20_BITMAP_0) +
++				       hweight32(DPD_5G_CH_BW20_BITMAP_1) +
++				       ARRAY_SIZE(bw160_5g_freq);
++		dev->dpd_chan_num_6g = hweight32(DPD_6G_CH_BW20_BITMAP_0) +
++				       hweight32(DPD_6G_CH_BW20_BITMAP_1) +
++				       ARRAY_SIZE(bw160_6g_freq);
++		dpd_size = MT_EE_CAL_DPD_SIZE_V2;
++		offs = MT_EE_DO_PRE_CAL_V2;
++	}
++	size = group_size + dpd_size;
++
++	switch (state) {
++	case MT76_TM_STATE_DPD_2G:
++		if (!is_set) {
++			func_data = cpu_to_le32(RF_DPD_FLAT_CAL);
++			dpd_ch_bw20_bitmap[0] = DPD_2G_CH_BW20_BITMAP_0;
++			bw20_ch = mphy->sband_2g.sband.channels;
++			bw160_freq = NULL;
++			bw160_size = 0;
++			band = NL80211_BAND_2GHZ;
++			do_precal = MT_EE_WIFI_CAL_DPD_2G;
++			is_set = true;
++		}
++		fallthrough;
++	case MT76_TM_STATE_DPD_5G:
++		if (!is_set) {
++			if (is_mt7915(&dev->mt76)) {
++				func_data = cpu_to_le32(RF_DPD_FLAT_CAL);
++				dpd_ch_bw20_bitmap[0] = DPD_5G_CH_BW20_BITMAP_7915_0;
++				bw160_size = 0;
++				dev->cur_prek_offset -= dev->dpd_chan_num_5g * MT_EE_CAL_UNIT * 2;
++			} else {
++				func_data = cpu_to_le32(RF_DPD_FLAT_5G_CAL);
++				dpd_ch_bw20_bitmap[0] = DPD_5G_CH_BW20_BITMAP_0;
++				dpd_ch_bw20_bitmap[1] = DPD_5G_CH_BW20_BITMAP_1;
++				bw160_size = ARRAY_SIZE(bw160_5g_freq);
++			}
++			bw20_ch = mphy->sband_5g.sband.channels;
++			bw160_freq = bw160_5g_freq;
++			band = NL80211_BAND_5GHZ;
++			do_precal = MT_EE_WIFI_CAL_DPD_5G;
++			is_set = true;
++		}
++		fallthrough;
++	case MT76_TM_STATE_DPD_6G:
++		if (!is_set) {
++			func_data = cpu_to_le32(RF_DPD_FLAT_6G_CAL);
++			dpd_ch_bw20_bitmap[0] = DPD_6G_CH_BW20_BITMAP_0;
++			dpd_ch_bw20_bitmap[1] = DPD_6G_CH_BW20_BITMAP_1;
++			bw20_ch = mphy->sband_6g.sband.channels;
++			bw160_freq = bw160_6g_freq;
++			bw160_size = ARRAY_SIZE(bw160_6g_freq);
++			band = NL80211_BAND_6GHZ;
++			do_precal = MT_EE_WIFI_CAL_DPD_6G;
++			is_set = true;
++		}
++
++		if (!bw20_ch)
++			return -EOPNOTSUPP;
++		if (!dev->cal) {
++			dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
++			if (!dev->cal)
++				return -ENOMEM;
++		}
++
++		req.op.rf.param.cal_param.func_data = func_data;
++		req.op.rf.param.cal_param.band_idx = phy->band_idx;
++
++		memcpy(&chan_backup, chandef->chan, sizeof(struct ieee80211_channel));
++		memcpy(&chandef_backup, chandef, sizeof(struct cfg80211_chan_def));
++
++		bw20_size = hweight32(dpd_ch_bw20_bitmap[0]) + hweight32(dpd_ch_bw20_bitmap[1]);
++		for (i = 0, j = 0; i < bw20_size + bw160_size; i++) {
++			if (i < bw20_size) {
++				freq = dpd_ch_bw20_bitmap[0] ? 0 : 1;
++				shift = ffs(dpd_ch_bw20_bitmap[freq]);
++				j += shift;
++				memcpy(&chan, &bw20_ch[j - 1], sizeof(struct ieee80211_channel));
++				chandef->width = NL80211_CHAN_WIDTH_20;
++				dpd_ch_bw20_bitmap[0] >>= shift;
++			} else {
++				freq = bw160_freq[i - bw20_size];
++				chan.center_freq = freq;
++				chan.hw_value = ieee80211_frequency_to_channel(freq);
++				chan.band = band;
++				chandef->width = NL80211_CHAN_WIDTH_160;
++			}
++
++			memcpy(chandef->chan, &chan, sizeof(struct ieee80211_channel));
++			if (is_mt7915(&dev->mt76))
++				mphy->hw->conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
++			else
++				mphy->hw->conf.flags |= IEEE80211_CONF_OFFCHANNEL;
++
++			mt7915_mcu_set_chan_info(phy, MCU_EXT_CMD(CHANNEL_SWITCH));
++
++			ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RF_TEST), &req,
++						sizeof(req), true);
++			if (ret) {
++				dev_err(dev->mt76.dev, "DPD Pre-cal: mcu send msg failed!\n");
++				break;
++			}
++		}
++		memcpy(chandef, &chandef_backup, sizeof(struct cfg80211_chan_def));
++		memcpy(chandef->chan, &chan_backup, sizeof(struct ieee80211_channel));
++		mt7915_mcu_set_chan_info(phy, MCU_EXT_CMD(CHANNEL_SWITCH));
++
++		if (!ret)
++			eeprom[offs] |= do_precal;
++
++		break;
++	case MT76_TM_STATE_DPD_DUMP:
++		pre_cal = (u32 *)dev->cal;
++		if (!dev->cal) {
++			dev_info(dev->mt76.dev, "Not DPD pre-cal yet!\n");
++			return ret;
++		}
++		dev_info(dev->mt76.dev, "DPD Pre-Cal:\n");
++		for (i = 0; i < dpd_size / sizeof(u32); i += 4) {
++			j = i + (group_size / sizeof(u32));
++			dev_info(dev->mt76.dev, "[0x%08x] 0x%8x 0x%8x 0x%8x 0x%8x\n",
++				 j * sizeof(u32), pre_cal[j], pre_cal[j + 1],
++				 pre_cal[j + 2], pre_cal[j + 3]);
++		}
++		break;
++	case MT76_TM_STATE_DPD_CLEAN:
++		pre_cal = (u32 *)dev->cal;
++		if (!pre_cal)
++			return ret;
++		memset(pre_cal + (group_size / sizeof(u32)), 0, dpd_size);
++		do_precal = MT_EE_WIFI_CAL_DPD;
++		eeprom[offs] &= ~do_precal;
++		break;
++	default:
++		return -EINVAL;
++	}
++	return ret;
++}
++
++void mt7915_tm_re_cal_event(struct mt7915_dev *dev, struct mt7915_tm_rf_test_result *result,
++			    struct mt7915_tm_rf_test_data *data)
++{
++#define DPD_PER_CHAN_SIZE_7915	2
++#define DPD_PER_CHAN_SIZE_7986	3
++	u32 base, dpd_offest_2g, dpd_offest_5g, cal_idx = 0, cal_type = 0, len = 0;
++	u8 *pre_cal;
++
++	pre_cal = dev->cal;
++	dpd_offest_5g = dev->dpd_chan_num_6g * DPD_PER_CHAN_SIZE_7986 * MT_EE_CAL_UNIT;
++	dpd_offest_2g = dpd_offest_5g + dev->dpd_chan_num_5g * MT_EE_CAL_UNIT *
++			(is_mt7915(&dev->mt76) ? DPD_PER_CHAN_SIZE_7915 : DPD_PER_CHAN_SIZE_7986);
++	cal_idx = le32_to_cpu(data->cal_idx);
++	cal_type = le32_to_cpu(data->cal_type);
++	len = le32_to_cpu(result->payload_len);
++	len = len - sizeof(struct mt7915_tm_rf_test_data);
++
++	switch (cal_type) {
++	case RF_PRE_CAL:
++		base = 0;
++		break;
++	case RF_DPD_FLAT_CAL:
++		base = mt7915_get_cal_group_size(dev) + dpd_offest_2g;
++		break;
++	case RF_DPD_FLAT_5G_CAL:
++		base = mt7915_get_cal_group_size(dev) + dpd_offest_5g;
++		break;
++	case RF_DPD_FLAT_6G_CAL:
++		base = mt7915_get_cal_group_size(dev);
++		break;
++	default:
++		dev_info(dev->mt76.dev, "Unknown calibration type!\n");
++		return;
++	}
++	pre_cal += (base + dev->cur_prek_offset);
++
++	memcpy(pre_cal, data->data, len);
++	dev->cur_prek_offset += len;
++}
++
++void mt7915_tm_rf_test_event(struct mt7915_dev *dev, struct sk_buff *skb)
++{
++	struct mt7915_tm_rf_test_result *result;
++	struct mt7915_tm_rf_test_data *data;
++	static u32 event_type;
++
++	result = (struct mt7915_tm_rf_test_result *)skb->data;
++	data = (struct mt7915_tm_rf_test_data *)result->event;
++
++	event_type = le32_to_cpu(result->func_idx);
++
++	switch (event_type) {
++	case RF_TEST_RE_CAL:
++		mt7915_tm_re_cal_event(dev, result, data);
++		break;
++	default:
++		break;
++	}
++}
++
+ static void
+ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
+ {
+@@ -1711,6 +2020,10 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
+ 	else if (prev_state == MT76_TM_STATE_OFF ||
+ 		 state == MT76_TM_STATE_OFF)
+ 		mt7915_tm_init(phy, !(state == MT76_TM_STATE_OFF));
++	else if (state >= MT76_TM_STATE_GROUP_PREK && state <= MT76_TM_STATE_GROUP_PREK_CLEAN)
++		return mt7915_tm_group_prek(phy, state);
++	else if (state >= MT76_TM_STATE_DPD_2G && state <= MT76_TM_STATE_DPD_CLEAN)
++		return mt7915_tm_dpd_prek(phy, state);
+ 
+ 	if ((state == MT76_TM_STATE_IDLE &&
+ 	     prev_state == MT76_TM_STATE_OFF) ||
+@@ -1866,9 +2179,113 @@ mt7915_tm_set_eeprom(struct mt76_phy *mphy, u32 offset, u8 *val, u8 action)
+ 	return ret;
+ }
+ 
++static int
++mt7915_tm_dump_precal(struct mt76_phy *mphy, struct sk_buff *msg, int flag, int type)
++{
++#define DPD_PER_CHAN_SIZE_MASK		GENMASK(31, 24)
++#define DPD_CHAN_NUM_2G_MASK		GENMASK(23, 16)
++#define DPD_CHAN_NUM_5G_MASK		GENMASK(15, 8)
++#define DPD_CHAN_NUM_6G_MASK		GENMASK(7, 0)
++	struct mt7915_phy *phy = mphy->priv;
++	struct mt7915_dev *dev = phy->dev;
++	u32 i, group_size, dpd_size, total_size, dpd_per_chan_size, dpd_info = 0;
++	u32 base, size, total_chan_num, offs, transmit_size = 1000;
++	u8 *pre_cal, *eeprom;
++	void *precal;
++	enum prek_ops {
++		PREK_GET_INFO,
++		PREK_SYNC_ALL,
++		PREK_SYNC_GROUP,
++		PREK_SYNC_DPD_2G,
++		PREK_SYNC_DPD_5G,
++		PREK_SYNC_DPD_6G,
++		PREK_CLEAN_GROUP,
++		PREK_CLEAN_DPD,
++	};
++
++	if (!dev->cal) {
++		dev_info(dev->mt76.dev, "Not pre-cal yet!\n");
++		return 0;
++	}
++
++	group_size = mt7915_get_cal_group_size(dev);
++	dpd_size = is_mt7915(&dev->mt76) ? MT_EE_CAL_DPD_SIZE_V1 : MT_EE_CAL_DPD_SIZE_V2;
++	dpd_per_chan_size = is_mt7915(&dev->mt76) ? 2 : 3;
++	total_size = group_size + dpd_size;
++	pre_cal = dev->cal;
++	eeprom = dev->mt76.eeprom.data;
++	offs = is_mt7915(&dev->mt76) ? MT_EE_DO_PRE_CAL : MT_EE_DO_PRE_CAL_V2;
++
++	total_chan_num = dev->dpd_chan_num_2g + dev->dpd_chan_num_5g + dev->dpd_chan_num_6g;
++
++	switch (type) {
++	case PREK_SYNC_ALL:
++		base = 0;
++		size = total_size;
++		break;
++	case PREK_SYNC_GROUP:
++		base = 0;
++		size = group_size;
++		break;
++	case PREK_SYNC_DPD_6G:
++		base = group_size;
++		size = dpd_size * dev->dpd_chan_num_6g / total_chan_num;
++		break;
++	case PREK_SYNC_DPD_5G:
++		base = group_size + dev->dpd_chan_num_6g * dpd_per_chan_size * MT_EE_CAL_UNIT;
++		size = dpd_size * dev->dpd_chan_num_5g / total_chan_num;
++		break;
++	case PREK_SYNC_DPD_2G:
++		base = group_size + (dev->dpd_chan_num_6g + dev->dpd_chan_num_5g) *
++			   dpd_per_chan_size * MT_EE_CAL_UNIT;
++		size = dpd_size * dev->dpd_chan_num_2g / total_chan_num;
++		break;
++	case PREK_GET_INFO:
++		break;
++	default:
++		return 0;
++	}
++
++	if (!flag) {
++		if (eeprom[offs] & MT_EE_WIFI_CAL_DPD) {
++			dpd_info |= u32_encode_bits(dpd_per_chan_size, DPD_PER_CHAN_SIZE_MASK) |
++				    u32_encode_bits(dev->dpd_chan_num_2g, DPD_CHAN_NUM_2G_MASK) |
++				    u32_encode_bits(dev->dpd_chan_num_5g, DPD_CHAN_NUM_5G_MASK) |
++				    u32_encode_bits(dev->dpd_chan_num_6g, DPD_CHAN_NUM_6G_MASK);
++		}
++		dev->cur_prek_offset = 0;
++		precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL_INFO);
++		if (!precal)
++			return -ENOMEM;
++		nla_put_u32(msg, 0, group_size);
++		nla_put_u32(msg, 1, dpd_size);
++		nla_put_u32(msg, 2, dpd_info);
++		nla_put_u32(msg, 3, transmit_size);
++		nla_put_u32(msg, 4, eeprom[offs]);
++		nla_nest_end(msg, precal);
++	} else {
++		precal = nla_nest_start(msg, MT76_TM_ATTR_PRECAL);
++		if (!precal)
++			return -ENOMEM;
++
++		transmit_size = (dev->cur_prek_offset + transmit_size < size) ?
++				transmit_size : (size - dev->cur_prek_offset);
++		for (i = 0; i < transmit_size; i++) {
++			if (nla_put_u8(msg, i, pre_cal[base + dev->cur_prek_offset + i]))
++				return -ENOMEM;
++		}
++		dev->cur_prek_offset += transmit_size;
++
++		nla_nest_end(msg, precal);
++	}
++
++	return 0;
++}
++
+ const struct mt76_testmode_ops mt7915_testmode_ops = {
+ 	.set_state = mt7915_tm_set_state,
+ 	.set_params = mt7915_tm_set_params,
+ 	.dump_stats = mt7915_tm_dump_stats,
+ 	.set_eeprom = mt7915_tm_set_eeprom,
++	.dump_precal = mt7915_tm_dump_precal,
+ };
+diff --git a/mt7915/testmode.h b/mt7915/testmode.h
+index 01b08e9..d500987 100644
+--- a/mt7915/testmode.h
++++ b/mt7915/testmode.h
+@@ -81,6 +81,11 @@ struct tm_tx_cont {
+ 	u8 txfd_mode;
+ };
+ 
++struct tm_cal_param {
++	__le32 func_data;
++	u8 band_idx;
++};
++
+ struct mt7915_tm_rf_test {
+ 	u8 action;
+ 	u8 icap_len;
+@@ -96,6 +101,7 @@ struct mt7915_tm_rf_test {
+ 				__le32 cal_dump;
+ 
+ 				struct tm_tx_cont tx_cont;
++				struct tm_cal_param cal_param;
+ 
+ 				u8 _pad[80];
+ 			} param;
+@@ -103,6 +109,20 @@ struct mt7915_tm_rf_test {
+ 	} op;
+ } __packed;
+ 
++struct mt7915_tm_rf_test_result {
++	struct mt76_connac2_mcu_rxd rxd;
++
++	u32 func_idx;
++	u32 payload_len;
++	u8 event[0];
++} __packed;
++
++struct mt7915_tm_rf_test_data {
++	u32 cal_idx;
++	u32 cal_type;
++	u8 data[0];
++} __packed;
++
+ enum {
+ 	RF_OPER_NORMAL,
+ 	RF_OPER_RF_TEST,
+@@ -111,6 +131,22 @@ enum {
+ 	RF_OPER_WIFI_SPECTRUM,
+ };
+ 
++enum {
++	RF_ACT_SWITCH_MODE,
++	RF_ACT_IN_RFTEST,
++};
++
++enum {
++	RF_TEST_RE_CAL = 0x01,
++	RF_TEST_TX_CONT_START = 0x05,
++	RF_TEST_TX_CONT_STOP = 0x06,
++};
++
++#define RF_DPD_FLAT_CAL		BIT(28)
++#define RF_PRE_CAL		BIT(29)
++#define RF_DPD_FLAT_5G_CAL	GENMASK(29, 28)
++#define RF_DPD_FLAT_6G_CAL	(BIT(30) | BIT(28))
++
+ enum {
+ 	TAM_ARB_OP_MODE_NORMAL = 1,
+ 	TAM_ARB_OP_MODE_TEST,
+diff --git a/testmode.c b/testmode.c
+index 2653182..568b478 100644
+--- a/testmode.c
++++ b/testmode.c
+@@ -764,6 +764,18 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 
+ 	mutex_lock(&dev->mutex);
+ 
++	if (tb[MT76_TM_ATTR_PRECAL] || tb[MT76_TM_ATTR_PRECAL_INFO]) {
++		int flag, type;
++
++		err = -EINVAL;
++		flag = tb[MT76_TM_ATTR_PRECAL] ? 1 : 0;
++		type = flag ? nla_get_u8(tb[MT76_TM_ATTR_PRECAL_INFO]) : 0;
++		if (dev->test_ops->dump_precal)
++			err = dev->test_ops->dump_precal(phy, msg, flag, type);
++
++		goto out;
++	}
++
+ 	if (tb[MT76_TM_ATTR_STATS]) {
+ 		err = -EINVAL;
+ 
+@@ -797,7 +809,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
+ 
+ 	if (dev->test_mtd.name &&
+ 	    (nla_put_string(msg, MT76_TM_ATTR_MTD_PART, dev->test_mtd.name) ||
+-	     nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset)))
++	     nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset) ||
++	     nla_put_u8(msg, MT76_TM_ATTR_IS_MAIN_PHY, phy == &dev->phy)))
+ 		goto out;
+ 
+ 	if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) ||
+diff --git a/testmode.h b/testmode.h
+index 57949f2..e2190e7 100644
+--- a/testmode.h
++++ b/testmode.h
+@@ -19,6 +19,7 @@
+  *
+  * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string)
+  * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
++ * @MT76_TM_ATTR_IS_MAIN_PHY: Is current phy index the main phy or the ext phy (u8)
+  *
+  * @MT76_TM_ATTR_TX_COUNT: configured number of frames to send when setting
+  *	state to MT76_TM_STATE_TX_FRAMES (u32)
+@@ -40,6 +41,11 @@
+  *
+  * @MT76_TM_ATTR_STATS: statistics (nested, see &enum mt76_testmode_stats_attr)
+  *
++ * @MT76_TM_ATTR_PRECAL: Pre-cal data (u8)
++ * @MT76_TM_ATTR_PRECAL_INFO: group size, dpd size, dpd_info, transmit size,
++ *                            eeprom cal indicator (u32),
++ *                            dpd_info = [dpd_per_chan_size, chan_num_2g,
++ *                                        chan_num_5g, chan_num_6g]
+  * @MT76_TM_ATTR_TX_SPE_IDX: tx spatial extension index (u8)
+  *
+  * @MT76_TM_ATTR_TX_DUTY_CYCLE: packet tx duty cycle (u8)
+@@ -67,6 +73,7 @@ enum mt76_testmode_attr {
+ 
+ 	MT76_TM_ATTR_MTD_PART,
+ 	MT76_TM_ATTR_MTD_OFFSET,
++	MT76_TM_ATTR_IS_MAIN_PHY,
+ 
+ 	MT76_TM_ATTR_TX_COUNT,
+ 	MT76_TM_ATTR_TX_LENGTH,
+@@ -85,6 +92,8 @@ enum mt76_testmode_attr {
+ 	MT76_TM_ATTR_FREQ_OFFSET,
+ 
+ 	MT76_TM_ATTR_STATS,
++	MT76_TM_ATTR_PRECAL,
++	MT76_TM_ATTR_PRECAL_INFO,
+ 
+ 	MT76_TM_ATTR_TX_SPE_IDX,
+ 
+@@ -184,6 +193,14 @@ enum mt76_testmode_state {
+ 	MT76_TM_STATE_TX_FRAMES,
+ 	MT76_TM_STATE_RX_FRAMES,
+ 	MT76_TM_STATE_TX_CONT,
++	MT76_TM_STATE_GROUP_PREK,
++	MT76_TM_STATE_GROUP_PREK_DUMP,
++	MT76_TM_STATE_GROUP_PREK_CLEAN,
++	MT76_TM_STATE_DPD_2G,
++	MT76_TM_STATE_DPD_5G,
++	MT76_TM_STATE_DPD_6G,
++	MT76_TM_STATE_DPD_DUMP,
++	MT76_TM_STATE_DPD_CLEAN,
+ 	MT76_TM_STATE_ON,
+ 
+ 	/* keep last */
+diff --git a/tools/fields.c b/tools/fields.c
+index 6e36ab2..1be1ffd 100644
+--- a/tools/fields.c
++++ b/tools/fields.c
+@@ -11,6 +11,14 @@ static const char * const testmode_state[] = {
+ 	[MT76_TM_STATE_TX_FRAMES] = "tx_frames",
+ 	[MT76_TM_STATE_RX_FRAMES] = "rx_frames",
+ 	[MT76_TM_STATE_TX_CONT] = "tx_cont",
++	[MT76_TM_STATE_GROUP_PREK] = "group_prek",
++	[MT76_TM_STATE_GROUP_PREK_DUMP] = "group_prek_dump",
++	[MT76_TM_STATE_GROUP_PREK_CLEAN] = "group_prek_clean",
++	[MT76_TM_STATE_DPD_2G] = "dpd_2g",
++	[MT76_TM_STATE_DPD_5G] = "dpd_5g",
++	[MT76_TM_STATE_DPD_6G] = "dpd_6g",
++	[MT76_TM_STATE_DPD_DUMP] = "dpd_dump",
++	[MT76_TM_STATE_DPD_CLEAN] = "dpd_clean",
+ };
+ 
+ static const char * const testmode_tx_mode[] = {
+-- 
+2.18.0
+