162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/* Copyright (C) 2020 MediaTek Inc. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include "mt7915.h"
562306a36Sopenharmony_ci#include "mac.h"
662306a36Sopenharmony_ci#include "mcu.h"
762306a36Sopenharmony_ci#include "testmode.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_cienum {
1062306a36Sopenharmony_ci	TM_CHANGED_TXPOWER,
1162306a36Sopenharmony_ci	TM_CHANGED_FREQ_OFFSET,
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci	/* must be last */
1462306a36Sopenharmony_ci	NUM_TM_CHANGED
1562306a36Sopenharmony_ci};
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic const u8 tm_change_map[] = {
1862306a36Sopenharmony_ci	[TM_CHANGED_TXPOWER] = MT76_TM_ATTR_TX_POWER,
1962306a36Sopenharmony_ci	[TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET,
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct reg_band {
2362306a36Sopenharmony_ci	u32 band[2];
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define REG_BAND(_list, _reg) \
2762306a36Sopenharmony_ci		{ _list.band[0] = MT_##_reg(0);	\
2862306a36Sopenharmony_ci		  _list.band[1] = MT_##_reg(1); }
2962306a36Sopenharmony_ci#define REG_BAND_IDX(_list, _reg, _idx) \
3062306a36Sopenharmony_ci		{ _list.band[0] = MT_##_reg(0, _idx);	\
3162306a36Sopenharmony_ci		  _list.band[1] = MT_##_reg(1, _idx); }
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define TM_REG_MAX_ID	17
3462306a36Sopenharmony_cistatic struct reg_band reg_backup_list[TM_REG_MAX_ID];
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int
3862306a36Sopenharmony_cimt7915_tm_set_tx_power(struct mt7915_phy *phy)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
4162306a36Sopenharmony_ci	struct mt76_phy *mphy = phy->mt76;
4262306a36Sopenharmony_ci	struct cfg80211_chan_def *chandef = &mphy->chandef;
4362306a36Sopenharmony_ci	int freq = chandef->center_freq1;
4462306a36Sopenharmony_ci	int ret;
4562306a36Sopenharmony_ci	struct {
4662306a36Sopenharmony_ci		u8 format_id;
4762306a36Sopenharmony_ci		u8 band_idx;
4862306a36Sopenharmony_ci		s8 tx_power;
4962306a36Sopenharmony_ci		u8 ant_idx;	/* Only 0 is valid */
5062306a36Sopenharmony_ci		u8 center_chan;
5162306a36Sopenharmony_ci		u8 rsv[3];
5262306a36Sopenharmony_ci	} __packed req = {
5362306a36Sopenharmony_ci		.format_id = 0xf,
5462306a36Sopenharmony_ci		.band_idx = phy->mt76->band_idx,
5562306a36Sopenharmony_ci		.center_chan = ieee80211_frequency_to_channel(freq),
5662306a36Sopenharmony_ci	};
5762306a36Sopenharmony_ci	u8 *tx_power = NULL;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (phy->mt76->test.state != MT76_TM_STATE_OFF)
6062306a36Sopenharmony_ci		tx_power = phy->mt76->test.tx_power;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* Tx power of the other antennas are the same as antenna 0 */
6362306a36Sopenharmony_ci	if (tx_power && tx_power[0])
6462306a36Sopenharmony_ci		req.tx_power = tx_power[0];
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	ret = mt76_mcu_send_msg(&dev->mt76,
6762306a36Sopenharmony_ci				MCU_EXT_CMD(TX_POWER_FEATURE_CTRL),
6862306a36Sopenharmony_ci				&req, sizeof(req), false);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return ret;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int
7462306a36Sopenharmony_cimt7915_tm_set_freq_offset(struct mt7915_phy *phy, bool en, u32 val)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
7762306a36Sopenharmony_ci	struct mt7915_tm_cmd req = {
7862306a36Sopenharmony_ci		.testmode_en = en,
7962306a36Sopenharmony_ci		.param_idx = MCU_ATE_SET_FREQ_OFFSET,
8062306a36Sopenharmony_ci		.param.freq.band = phy->mt76->band_idx,
8162306a36Sopenharmony_ci		.param.freq.freq_offset = cpu_to_le32(val),
8262306a36Sopenharmony_ci	};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
8562306a36Sopenharmony_ci				 sizeof(req), false);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int
8962306a36Sopenharmony_cimt7915_tm_mode_ctrl(struct mt7915_dev *dev, bool enable)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct {
9262306a36Sopenharmony_ci		u8 format_id;
9362306a36Sopenharmony_ci		bool enable;
9462306a36Sopenharmony_ci		u8 rsv[2];
9562306a36Sopenharmony_ci	} __packed req = {
9662306a36Sopenharmony_ci		.format_id = 0x6,
9762306a36Sopenharmony_ci		.enable = enable,
9862306a36Sopenharmony_ci	};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return mt76_mcu_send_msg(&dev->mt76,
10162306a36Sopenharmony_ci				 MCU_EXT_CMD(TX_POWER_FEATURE_CTRL),
10262306a36Sopenharmony_ci				 &req, sizeof(req), false);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int
10662306a36Sopenharmony_cimt7915_tm_set_trx(struct mt7915_phy *phy, int type, bool en)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
10962306a36Sopenharmony_ci	struct mt7915_tm_cmd req = {
11062306a36Sopenharmony_ci		.testmode_en = 1,
11162306a36Sopenharmony_ci		.param_idx = MCU_ATE_SET_TRX,
11262306a36Sopenharmony_ci		.param.trx.type = type,
11362306a36Sopenharmony_ci		.param.trx.enable = en,
11462306a36Sopenharmony_ci		.param.trx.band = phy->mt76->band_idx,
11562306a36Sopenharmony_ci	};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
11862306a36Sopenharmony_ci				 sizeof(req), false);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int
12262306a36Sopenharmony_cimt7915_tm_clean_hwq(struct mt7915_phy *phy, u8 wcid)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
12562306a36Sopenharmony_ci	struct mt7915_tm_cmd req = {
12662306a36Sopenharmony_ci		.testmode_en = 1,
12762306a36Sopenharmony_ci		.param_idx = MCU_ATE_CLEAN_TXQUEUE,
12862306a36Sopenharmony_ci		.param.clean.wcid = wcid,
12962306a36Sopenharmony_ci		.param.clean.band = phy->mt76->band_idx,
13062306a36Sopenharmony_ci	};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
13362306a36Sopenharmony_ci				 sizeof(req), false);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic int
13762306a36Sopenharmony_cimt7915_tm_set_slot_time(struct mt7915_phy *phy, u8 slot_time, u8 sifs)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
14062306a36Sopenharmony_ci	struct mt7915_tm_cmd req = {
14162306a36Sopenharmony_ci		.testmode_en = !(phy->mt76->test.state == MT76_TM_STATE_OFF),
14262306a36Sopenharmony_ci		.param_idx = MCU_ATE_SET_SLOT_TIME,
14362306a36Sopenharmony_ci		.param.slot.slot_time = slot_time,
14462306a36Sopenharmony_ci		.param.slot.sifs = sifs,
14562306a36Sopenharmony_ci		.param.slot.rifs = 2,
14662306a36Sopenharmony_ci		.param.slot.eifs = cpu_to_le16(60),
14762306a36Sopenharmony_ci		.param.slot.band = phy->mt76->band_idx,
14862306a36Sopenharmony_ci	};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
15162306a36Sopenharmony_ci				 sizeof(req), false);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic int
15562306a36Sopenharmony_cimt7915_tm_set_tam_arb(struct mt7915_phy *phy, bool enable, bool mu)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
15862306a36Sopenharmony_ci	u32 op_mode;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (!enable)
16162306a36Sopenharmony_ci		op_mode = TAM_ARB_OP_MODE_NORMAL;
16262306a36Sopenharmony_ci	else if (mu)
16362306a36Sopenharmony_ci		op_mode = TAM_ARB_OP_MODE_TEST;
16462306a36Sopenharmony_ci	else
16562306a36Sopenharmony_ci		op_mode = TAM_ARB_OP_MODE_FORCE_SU;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return mt7915_mcu_set_muru_ctrl(dev, MURU_SET_ARB_OP_MODE, op_mode);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int
17162306a36Sopenharmony_cimt7915_tm_set_wmm_qid(struct mt7915_phy *phy, u8 qid, u8 aifs, u8 cw_min,
17262306a36Sopenharmony_ci		      u16 cw_max, u16 txop)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct mt7915_vif *mvif = (struct mt7915_vif *)phy->monitor_vif->drv_priv;
17562306a36Sopenharmony_ci	struct mt7915_mcu_tx req = { .total = 1 };
17662306a36Sopenharmony_ci	struct edca *e = &req.edca[0];
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	e->queue = qid + mvif->mt76.wmm_idx * MT76_CONNAC_MAX_WMM_SETS;
17962306a36Sopenharmony_ci	e->set = WMM_PARAM_SET;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	e->aifs = aifs;
18262306a36Sopenharmony_ci	e->cw_min = cw_min;
18362306a36Sopenharmony_ci	e->cw_max = cpu_to_le16(cw_max);
18462306a36Sopenharmony_ci	e->txop = cpu_to_le16(txop);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return mt7915_mcu_update_edca(phy->dev, &req);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int
19062306a36Sopenharmony_cimt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci#define TM_DEFAULT_SIFS	10
19362306a36Sopenharmony_ci#define TM_MAX_SIFS	127
19462306a36Sopenharmony_ci#define TM_MAX_AIFSN	0xf
19562306a36Sopenharmony_ci#define TM_MIN_AIFSN	0x1
19662306a36Sopenharmony_ci#define BBP_PROC_TIME	1500
19762306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
19862306a36Sopenharmony_ci	u8 sig_ext = (mode == MT76_TM_TX_MODE_CCK) ? 0 : 6;
19962306a36Sopenharmony_ci	u8 slot_time = 9, sifs = TM_DEFAULT_SIFS;
20062306a36Sopenharmony_ci	u8 aifsn = TM_MIN_AIFSN;
20162306a36Sopenharmony_ci	u8 band = phy->mt76->band_idx;
20262306a36Sopenharmony_ci	u32 i2t_time, tr2t_time, txv_time;
20362306a36Sopenharmony_ci	u16 cw = 0;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (ipg < sig_ext + slot_time + sifs)
20662306a36Sopenharmony_ci		ipg = 0;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!ipg)
20962306a36Sopenharmony_ci		goto done;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ipg -= sig_ext;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (ipg <= (TM_MAX_SIFS + slot_time)) {
21462306a36Sopenharmony_ci		sifs = ipg - slot_time;
21562306a36Sopenharmony_ci	} else {
21662306a36Sopenharmony_ci		u32 val = (ipg + slot_time) / slot_time;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		while (val >>= 1)
21962306a36Sopenharmony_ci			cw++;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		if (cw > 16)
22262306a36Sopenharmony_ci			cw = 16;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		ipg -= ((1 << cw) - 1) * slot_time;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		aifsn = ipg / slot_time;
22762306a36Sopenharmony_ci		if (aifsn > TM_MAX_AIFSN)
22862306a36Sopenharmony_ci			aifsn = TM_MAX_AIFSN;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		ipg -= aifsn * slot_time;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		if (ipg > TM_DEFAULT_SIFS)
23362306a36Sopenharmony_ci			sifs = min_t(u32, ipg, TM_MAX_SIFS);
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_cidone:
23662306a36Sopenharmony_ci	txv_time = mt76_get_field(dev, MT_TMAC_ATCR(band),
23762306a36Sopenharmony_ci				  MT_TMAC_ATCR_TXV_TOUT);
23862306a36Sopenharmony_ci	txv_time *= 50;	/* normal clock time */
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	i2t_time = (slot_time * 1000 - txv_time - BBP_PROC_TIME) / 50;
24162306a36Sopenharmony_ci	tr2t_time = (sifs * 1000 - txv_time - BBP_PROC_TIME) / 50;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	mt76_set(dev, MT_TMAC_TRCR0(band),
24462306a36Sopenharmony_ci		 FIELD_PREP(MT_TMAC_TRCR0_TR2T_CHK, tr2t_time) |
24562306a36Sopenharmony_ci		 FIELD_PREP(MT_TMAC_TRCR0_I2T_CHK, i2t_time));
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	mt7915_tm_set_slot_time(phy, slot_time, sifs);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return mt7915_tm_set_wmm_qid(phy,
25062306a36Sopenharmony_ci				     mt76_connac_lmac_mapping(IEEE80211_AC_BE),
25162306a36Sopenharmony_ci				     aifsn, cw, cw, 0);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int
25562306a36Sopenharmony_cimt7915_tm_set_tx_len(struct mt7915_phy *phy, u32 tx_time)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct mt76_phy *mphy = phy->mt76;
25862306a36Sopenharmony_ci	struct mt76_testmode_data *td = &mphy->test;
25962306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
26062306a36Sopenharmony_ci	struct rate_info rate = {};
26162306a36Sopenharmony_ci	u16 flags = 0, tx_len;
26262306a36Sopenharmony_ci	u32 bitrate;
26362306a36Sopenharmony_ci	int ret;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (!tx_time)
26662306a36Sopenharmony_ci		return 0;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	rate.mcs = td->tx_rate_idx;
26962306a36Sopenharmony_ci	rate.nss = td->tx_rate_nss;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	switch (td->tx_rate_mode) {
27262306a36Sopenharmony_ci	case MT76_TM_TX_MODE_CCK:
27362306a36Sopenharmony_ci	case MT76_TM_TX_MODE_OFDM:
27462306a36Sopenharmony_ci		if (mphy->chandef.chan->band == NL80211_BAND_5GHZ)
27562306a36Sopenharmony_ci			sband = &mphy->sband_5g.sband;
27662306a36Sopenharmony_ci		else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ)
27762306a36Sopenharmony_ci			sband = &mphy->sband_6g.sband;
27862306a36Sopenharmony_ci		else
27962306a36Sopenharmony_ci			sband = &mphy->sband_2g.sband;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		rate.legacy = sband->bitrates[rate.mcs].bitrate;
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	case MT76_TM_TX_MODE_HT:
28462306a36Sopenharmony_ci		rate.mcs += rate.nss * 8;
28562306a36Sopenharmony_ci		flags |= RATE_INFO_FLAGS_MCS;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci		if (td->tx_rate_sgi)
28862306a36Sopenharmony_ci			flags |= RATE_INFO_FLAGS_SHORT_GI;
28962306a36Sopenharmony_ci		break;
29062306a36Sopenharmony_ci	case MT76_TM_TX_MODE_VHT:
29162306a36Sopenharmony_ci		flags |= RATE_INFO_FLAGS_VHT_MCS;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		if (td->tx_rate_sgi)
29462306a36Sopenharmony_ci			flags |= RATE_INFO_FLAGS_SHORT_GI;
29562306a36Sopenharmony_ci		break;
29662306a36Sopenharmony_ci	case MT76_TM_TX_MODE_HE_SU:
29762306a36Sopenharmony_ci	case MT76_TM_TX_MODE_HE_EXT_SU:
29862306a36Sopenharmony_ci	case MT76_TM_TX_MODE_HE_TB:
29962306a36Sopenharmony_ci	case MT76_TM_TX_MODE_HE_MU:
30062306a36Sopenharmony_ci		rate.he_gi = td->tx_rate_sgi;
30162306a36Sopenharmony_ci		flags |= RATE_INFO_FLAGS_HE_MCS;
30262306a36Sopenharmony_ci		break;
30362306a36Sopenharmony_ci	default:
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci	rate.flags = flags;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	switch (mphy->chandef.width) {
30962306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_160:
31062306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_80P80:
31162306a36Sopenharmony_ci		rate.bw = RATE_INFO_BW_160;
31262306a36Sopenharmony_ci		break;
31362306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_80:
31462306a36Sopenharmony_ci		rate.bw = RATE_INFO_BW_80;
31562306a36Sopenharmony_ci		break;
31662306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_40:
31762306a36Sopenharmony_ci		rate.bw = RATE_INFO_BW_40;
31862306a36Sopenharmony_ci		break;
31962306a36Sopenharmony_ci	default:
32062306a36Sopenharmony_ci		rate.bw = RATE_INFO_BW_20;
32162306a36Sopenharmony_ci		break;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	bitrate = cfg80211_calculate_bitrate(&rate);
32562306a36Sopenharmony_ci	tx_len = bitrate * tx_time / 10 / 8;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	ret = mt76_testmode_alloc_skb(phy->mt76, tx_len);
32862306a36Sopenharmony_ci	if (ret)
32962306a36Sopenharmony_ci		return ret;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return 0;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void
33562306a36Sopenharmony_cimt7915_tm_reg_backup_restore(struct mt7915_phy *phy)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	int n_regs = ARRAY_SIZE(reg_backup_list);
33862306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
33962306a36Sopenharmony_ci	u32 *b = phy->test.reg_backup;
34062306a36Sopenharmony_ci	u8 band = phy->mt76->band_idx;
34162306a36Sopenharmony_ci	int i;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	REG_BAND_IDX(reg_backup_list[0], AGG_PCR0, 0);
34462306a36Sopenharmony_ci	REG_BAND_IDX(reg_backup_list[1], AGG_PCR0, 1);
34562306a36Sopenharmony_ci	REG_BAND_IDX(reg_backup_list[2], AGG_AWSCR0, 0);
34662306a36Sopenharmony_ci	REG_BAND_IDX(reg_backup_list[3], AGG_AWSCR0, 1);
34762306a36Sopenharmony_ci	REG_BAND_IDX(reg_backup_list[4], AGG_AWSCR0, 2);
34862306a36Sopenharmony_ci	REG_BAND_IDX(reg_backup_list[5], AGG_AWSCR0, 3);
34962306a36Sopenharmony_ci	REG_BAND(reg_backup_list[6], AGG_MRCR);
35062306a36Sopenharmony_ci	REG_BAND(reg_backup_list[7], TMAC_TFCR0);
35162306a36Sopenharmony_ci	REG_BAND(reg_backup_list[8], TMAC_TCR0);
35262306a36Sopenharmony_ci	REG_BAND(reg_backup_list[9], AGG_ATCR1);
35362306a36Sopenharmony_ci	REG_BAND(reg_backup_list[10], AGG_ATCR3);
35462306a36Sopenharmony_ci	REG_BAND(reg_backup_list[11], TMAC_TRCR0);
35562306a36Sopenharmony_ci	REG_BAND(reg_backup_list[12], TMAC_ICR0);
35662306a36Sopenharmony_ci	REG_BAND_IDX(reg_backup_list[13], ARB_DRNGR0, 0);
35762306a36Sopenharmony_ci	REG_BAND_IDX(reg_backup_list[14], ARB_DRNGR0, 1);
35862306a36Sopenharmony_ci	REG_BAND(reg_backup_list[15], WF_RFCR);
35962306a36Sopenharmony_ci	REG_BAND(reg_backup_list[16], WF_RFCR1);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (phy->mt76->test.state == MT76_TM_STATE_OFF) {
36262306a36Sopenharmony_ci		for (i = 0; i < n_regs; i++)
36362306a36Sopenharmony_ci			mt76_wr(dev, reg_backup_list[i].band[band], b[i]);
36462306a36Sopenharmony_ci		return;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (!b) {
36862306a36Sopenharmony_ci		b = devm_kzalloc(dev->mt76.dev, 4 * n_regs, GFP_KERNEL);
36962306a36Sopenharmony_ci		if (!b)
37062306a36Sopenharmony_ci			return;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		phy->test.reg_backup = b;
37362306a36Sopenharmony_ci		for (i = 0; i < n_regs; i++)
37462306a36Sopenharmony_ci			b[i] = mt76_rr(dev, reg_backup_list[i].band[band]);
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	mt76_clear(dev, MT_AGG_PCR0(band, 0), MT_AGG_PCR0_MM_PROT |
37862306a36Sopenharmony_ci		   MT_AGG_PCR0_GF_PROT | MT_AGG_PCR0_ERP_PROT |
37962306a36Sopenharmony_ci		   MT_AGG_PCR0_VHT_PROT | MT_AGG_PCR0_BW20_PROT |
38062306a36Sopenharmony_ci		   MT_AGG_PCR0_BW40_PROT | MT_AGG_PCR0_BW80_PROT);
38162306a36Sopenharmony_ci	mt76_set(dev, MT_AGG_PCR0(band, 0), MT_AGG_PCR0_PTA_WIN_DIS);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	mt76_wr(dev, MT_AGG_PCR0(band, 1), MT_AGG_PCR1_RTS0_NUM_THRES |
38462306a36Sopenharmony_ci		MT_AGG_PCR1_RTS0_LEN_THRES);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	mt76_clear(dev, MT_AGG_MRCR(band), MT_AGG_MRCR_BAR_CNT_LIMIT |
38762306a36Sopenharmony_ci		   MT_AGG_MRCR_LAST_RTS_CTS_RN | MT_AGG_MRCR_RTS_FAIL_LIMIT |
38862306a36Sopenharmony_ci		   MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	mt76_rmw(dev, MT_AGG_MRCR(band), MT_AGG_MRCR_RTS_FAIL_LIMIT |
39162306a36Sopenharmony_ci		 MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT,
39262306a36Sopenharmony_ci		 FIELD_PREP(MT_AGG_MRCR_RTS_FAIL_LIMIT, 1) |
39362306a36Sopenharmony_ci		 FIELD_PREP(MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT, 1));
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	mt76_wr(dev, MT_TMAC_TFCR0(band), 0);
39662306a36Sopenharmony_ci	mt76_clear(dev, MT_TMAC_TCR0(band), MT_TMAC_TCR0_TBTT_STOP_CTRL);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* config rx filter for testmode rx */
39962306a36Sopenharmony_ci	mt76_wr(dev, MT_WF_RFCR(band), 0xcf70a);
40062306a36Sopenharmony_ci	mt76_wr(dev, MT_WF_RFCR1(band), 0);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic void
40462306a36Sopenharmony_cimt7915_tm_init(struct mt7915_phy *phy, bool en)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
40962306a36Sopenharmony_ci		return;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	mt7915_mcu_set_sku_en(phy, !en);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	mt7915_tm_mode_ctrl(dev, en);
41462306a36Sopenharmony_ci	mt7915_tm_reg_backup_restore(phy);
41562306a36Sopenharmony_ci	mt7915_tm_set_trx(phy, TM_MAC_TXRX, !en);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	mt7915_mcu_add_bss_info(phy, phy->monitor_vif, en);
41862306a36Sopenharmony_ci	mt7915_mcu_add_sta(dev, phy->monitor_vif, NULL, en);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (!en)
42162306a36Sopenharmony_ci		mt7915_tm_set_tam_arb(phy, en, 0);
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic void
42562306a36Sopenharmony_cimt7915_tm_update_channel(struct mt7915_phy *phy)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	mutex_unlock(&phy->dev->mt76.mutex);
42862306a36Sopenharmony_ci	mt7915_set_channel(phy);
42962306a36Sopenharmony_ci	mutex_lock(&phy->dev->mt76.mutex);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	mt7915_mcu_set_chan_info(phy, MCU_EXT_CMD(SET_RX_PATH));
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic void
43562306a36Sopenharmony_cimt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct mt76_testmode_data *td = &phy->mt76->test;
43862306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
43962306a36Sopenharmony_ci	struct ieee80211_tx_info *info;
44062306a36Sopenharmony_ci	u8 duty_cycle = td->tx_duty_cycle;
44162306a36Sopenharmony_ci	u32 tx_time = td->tx_time;
44262306a36Sopenharmony_ci	u32 ipg = td->tx_ipg;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, false);
44562306a36Sopenharmony_ci	mt7915_tm_clean_hwq(phy, dev->mt76.global_wcid.idx);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (en) {
44862306a36Sopenharmony_ci		mt7915_tm_update_channel(phy);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		if (td->tx_spe_idx)
45162306a36Sopenharmony_ci			phy->test.spe_idx = td->tx_spe_idx;
45262306a36Sopenharmony_ci		else
45362306a36Sopenharmony_ci			phy->test.spe_idx = mt76_connac_spe_idx(td->tx_antenna_mask);
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	mt7915_tm_set_tam_arb(phy, en,
45762306a36Sopenharmony_ci			      td->tx_rate_mode == MT76_TM_TX_MODE_HE_MU);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* if all three params are set, duty_cycle will be ignored */
46062306a36Sopenharmony_ci	if (duty_cycle && tx_time && !ipg) {
46162306a36Sopenharmony_ci		ipg = tx_time * 100 / duty_cycle - tx_time;
46262306a36Sopenharmony_ci	} else if (duty_cycle && !tx_time && ipg) {
46362306a36Sopenharmony_ci		if (duty_cycle < 100)
46462306a36Sopenharmony_ci			tx_time = duty_cycle * ipg / (100 - duty_cycle);
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	mt7915_tm_set_ipg_params(phy, ipg, td->tx_rate_mode);
46862306a36Sopenharmony_ci	mt7915_tm_set_tx_len(phy, tx_time);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (ipg)
47162306a36Sopenharmony_ci		td->tx_queued_limit = MT76_TM_TIMEOUT * 1000000 / ipg / 2;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (!en || !td->tx_skb)
47462306a36Sopenharmony_ci		return;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	info = IEEE80211_SKB_CB(td->tx_skb);
47762306a36Sopenharmony_ci	info->control.vif = phy->monitor_vif;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	mt7915_tm_set_trx(phy, TM_MAC_TX, en);
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic void
48362306a36Sopenharmony_cimt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, false);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (en) {
48862306a36Sopenharmony_ci		struct mt7915_dev *dev = phy->dev;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		mt7915_tm_update_channel(phy);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		/* read-clear */
49362306a36Sopenharmony_ci		mt76_rr(dev, MT_MIB_SDR3(phy->mt76->band_idx));
49462306a36Sopenharmony_ci		mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, en);
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic int
49962306a36Sopenharmony_cimt7915_tm_rf_switch_mode(struct mt7915_dev *dev, u32 oper)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct mt7915_tm_rf_test req = {
50262306a36Sopenharmony_ci		.op.op_mode = cpu_to_le32(oper),
50362306a36Sopenharmony_ci	};
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RF_TEST), &req,
50662306a36Sopenharmony_ci				 sizeof(req), true);
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int
51062306a36Sopenharmony_cimt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci#define TX_CONT_START	0x05
51362306a36Sopenharmony_ci#define TX_CONT_STOP	0x06
51462306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
51562306a36Sopenharmony_ci	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
51662306a36Sopenharmony_ci	int freq1 = ieee80211_frequency_to_channel(chandef->center_freq1);
51762306a36Sopenharmony_ci	struct mt76_testmode_data *td = &phy->mt76->test;
51862306a36Sopenharmony_ci	u32 func_idx = en ? TX_CONT_START : TX_CONT_STOP;
51962306a36Sopenharmony_ci	u8 rate_idx = td->tx_rate_idx, mode;
52062306a36Sopenharmony_ci	u8 band = phy->mt76->band_idx;
52162306a36Sopenharmony_ci	u16 rateval;
52262306a36Sopenharmony_ci	struct mt7915_tm_rf_test req = {
52362306a36Sopenharmony_ci		.action = 1,
52462306a36Sopenharmony_ci		.icap_len = 120,
52562306a36Sopenharmony_ci		.op.rf.func_idx = cpu_to_le32(func_idx),
52662306a36Sopenharmony_ci	};
52762306a36Sopenharmony_ci	struct tm_tx_cont *tx_cont = &req.op.rf.param.tx_cont;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	tx_cont->control_ch = chandef->chan->hw_value;
53062306a36Sopenharmony_ci	tx_cont->center_ch = freq1;
53162306a36Sopenharmony_ci	tx_cont->tx_ant = td->tx_antenna_mask;
53262306a36Sopenharmony_ci	tx_cont->band = band;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	switch (chandef->width) {
53562306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_40:
53662306a36Sopenharmony_ci		tx_cont->bw = CMD_CBW_40MHZ;
53762306a36Sopenharmony_ci		break;
53862306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_80:
53962306a36Sopenharmony_ci		tx_cont->bw = CMD_CBW_80MHZ;
54062306a36Sopenharmony_ci		break;
54162306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_80P80:
54262306a36Sopenharmony_ci		tx_cont->bw = CMD_CBW_8080MHZ;
54362306a36Sopenharmony_ci		break;
54462306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_160:
54562306a36Sopenharmony_ci		tx_cont->bw = CMD_CBW_160MHZ;
54662306a36Sopenharmony_ci		break;
54762306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_5:
54862306a36Sopenharmony_ci		tx_cont->bw = CMD_CBW_5MHZ;
54962306a36Sopenharmony_ci		break;
55062306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_10:
55162306a36Sopenharmony_ci		tx_cont->bw = CMD_CBW_10MHZ;
55262306a36Sopenharmony_ci		break;
55362306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_20:
55462306a36Sopenharmony_ci		tx_cont->bw = CMD_CBW_20MHZ;
55562306a36Sopenharmony_ci		break;
55662306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_20_NOHT:
55762306a36Sopenharmony_ci		tx_cont->bw = CMD_CBW_20MHZ;
55862306a36Sopenharmony_ci		break;
55962306a36Sopenharmony_ci	default:
56062306a36Sopenharmony_ci		return -EINVAL;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (!en) {
56462306a36Sopenharmony_ci		req.op.rf.param.func_data = cpu_to_le32(band);
56562306a36Sopenharmony_ci		goto out;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (td->tx_rate_mode <= MT76_TM_TX_MODE_OFDM) {
56962306a36Sopenharmony_ci		struct ieee80211_supported_band *sband;
57062306a36Sopenharmony_ci		u8 idx = rate_idx;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci		if (chandef->chan->band == NL80211_BAND_5GHZ)
57362306a36Sopenharmony_ci			sband = &phy->mt76->sband_5g.sband;
57462306a36Sopenharmony_ci		else if (chandef->chan->band == NL80211_BAND_6GHZ)
57562306a36Sopenharmony_ci			sband = &phy->mt76->sband_6g.sband;
57662306a36Sopenharmony_ci		else
57762306a36Sopenharmony_ci			sband = &phy->mt76->sband_2g.sband;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci		if (td->tx_rate_mode == MT76_TM_TX_MODE_OFDM)
58062306a36Sopenharmony_ci			idx += 4;
58162306a36Sopenharmony_ci		rate_idx = sband->bitrates[idx].hw_value & 0xff;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	switch (td->tx_rate_mode) {
58562306a36Sopenharmony_ci	case MT76_TM_TX_MODE_CCK:
58662306a36Sopenharmony_ci		mode = MT_PHY_TYPE_CCK;
58762306a36Sopenharmony_ci		break;
58862306a36Sopenharmony_ci	case MT76_TM_TX_MODE_OFDM:
58962306a36Sopenharmony_ci		mode = MT_PHY_TYPE_OFDM;
59062306a36Sopenharmony_ci		break;
59162306a36Sopenharmony_ci	case MT76_TM_TX_MODE_HT:
59262306a36Sopenharmony_ci		mode = MT_PHY_TYPE_HT;
59362306a36Sopenharmony_ci		break;
59462306a36Sopenharmony_ci	case MT76_TM_TX_MODE_VHT:
59562306a36Sopenharmony_ci		mode = MT_PHY_TYPE_VHT;
59662306a36Sopenharmony_ci		break;
59762306a36Sopenharmony_ci	case MT76_TM_TX_MODE_HE_SU:
59862306a36Sopenharmony_ci		mode = MT_PHY_TYPE_HE_SU;
59962306a36Sopenharmony_ci		break;
60062306a36Sopenharmony_ci	case MT76_TM_TX_MODE_HE_EXT_SU:
60162306a36Sopenharmony_ci		mode = MT_PHY_TYPE_HE_EXT_SU;
60262306a36Sopenharmony_ci		break;
60362306a36Sopenharmony_ci	case MT76_TM_TX_MODE_HE_TB:
60462306a36Sopenharmony_ci		mode = MT_PHY_TYPE_HE_TB;
60562306a36Sopenharmony_ci		break;
60662306a36Sopenharmony_ci	case MT76_TM_TX_MODE_HE_MU:
60762306a36Sopenharmony_ci		mode = MT_PHY_TYPE_HE_MU;
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci	default:
61062306a36Sopenharmony_ci		return -EINVAL;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	rateval =  mode << 6 | rate_idx;
61462306a36Sopenharmony_ci	tx_cont->rateval = cpu_to_le16(rateval);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ciout:
61762306a36Sopenharmony_ci	if (!en) {
61862306a36Sopenharmony_ci		int ret;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RF_TEST), &req,
62162306a36Sopenharmony_ci					sizeof(req), true);
62262306a36Sopenharmony_ci		if (ret)
62362306a36Sopenharmony_ci			return ret;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		return mt7915_tm_rf_switch_mode(dev, RF_OPER_NORMAL);
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	mt7915_tm_rf_switch_mode(dev, RF_OPER_RF_TEST);
62962306a36Sopenharmony_ci	mt7915_tm_update_channel(phy);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RF_TEST), &req,
63262306a36Sopenharmony_ci				 sizeof(req), true);
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic void
63662306a36Sopenharmony_cimt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	struct mt76_testmode_data *td = &phy->mt76->test;
63962306a36Sopenharmony_ci	bool en = phy->mt76->test.state != MT76_TM_STATE_OFF;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (changed & BIT(TM_CHANGED_FREQ_OFFSET))
64262306a36Sopenharmony_ci		mt7915_tm_set_freq_offset(phy, en, en ? td->freq_offset : 0);
64362306a36Sopenharmony_ci	if (changed & BIT(TM_CHANGED_TXPOWER))
64462306a36Sopenharmony_ci		mt7915_tm_set_tx_power(phy);
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic int
64862306a36Sopenharmony_cimt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	struct mt76_testmode_data *td = &mphy->test;
65162306a36Sopenharmony_ci	struct mt7915_phy *phy = mphy->priv;
65262306a36Sopenharmony_ci	enum mt76_testmode_state prev_state = td->state;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	mphy->test.state = state;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (prev_state == MT76_TM_STATE_TX_FRAMES ||
65762306a36Sopenharmony_ci	    state == MT76_TM_STATE_TX_FRAMES)
65862306a36Sopenharmony_ci		mt7915_tm_set_tx_frames(phy, state == MT76_TM_STATE_TX_FRAMES);
65962306a36Sopenharmony_ci	else if (prev_state == MT76_TM_STATE_RX_FRAMES ||
66062306a36Sopenharmony_ci		 state == MT76_TM_STATE_RX_FRAMES)
66162306a36Sopenharmony_ci		mt7915_tm_set_rx_frames(phy, state == MT76_TM_STATE_RX_FRAMES);
66262306a36Sopenharmony_ci	else if (prev_state == MT76_TM_STATE_TX_CONT ||
66362306a36Sopenharmony_ci		 state == MT76_TM_STATE_TX_CONT)
66462306a36Sopenharmony_ci		mt7915_tm_set_tx_cont(phy, state == MT76_TM_STATE_TX_CONT);
66562306a36Sopenharmony_ci	else if (prev_state == MT76_TM_STATE_OFF ||
66662306a36Sopenharmony_ci		 state == MT76_TM_STATE_OFF)
66762306a36Sopenharmony_ci		mt7915_tm_init(phy, !(state == MT76_TM_STATE_OFF));
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if ((state == MT76_TM_STATE_IDLE &&
67062306a36Sopenharmony_ci	     prev_state == MT76_TM_STATE_OFF) ||
67162306a36Sopenharmony_ci	    (state == MT76_TM_STATE_OFF &&
67262306a36Sopenharmony_ci	     prev_state == MT76_TM_STATE_IDLE)) {
67362306a36Sopenharmony_ci		u32 changed = 0;
67462306a36Sopenharmony_ci		int i;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
67762306a36Sopenharmony_ci			u16 cur = tm_change_map[i];
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci			if (td->param_set[cur / 32] & BIT(cur % 32))
68062306a36Sopenharmony_ci				changed |= BIT(i);
68162306a36Sopenharmony_ci		}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		mt7915_tm_update_params(phy, changed);
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return 0;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic int
69062306a36Sopenharmony_cimt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
69162306a36Sopenharmony_ci		     enum mt76_testmode_state new_state)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct mt76_testmode_data *td = &mphy->test;
69462306a36Sopenharmony_ci	struct mt7915_phy *phy = mphy->priv;
69562306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
69662306a36Sopenharmony_ci	u32 chainmask = mphy->chainmask, changed = 0;
69762306a36Sopenharmony_ci	bool ext_phy = phy != &dev->phy;
69862306a36Sopenharmony_ci	int i;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	if (new_state == MT76_TM_STATE_OFF ||
70362306a36Sopenharmony_ci	    td->state == MT76_TM_STATE_OFF)
70462306a36Sopenharmony_ci		return 0;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	chainmask = ext_phy ? chainmask >> dev->chainshift : chainmask;
70762306a36Sopenharmony_ci	if (td->tx_antenna_mask > chainmask)
70862306a36Sopenharmony_ci		return -EINVAL;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
71162306a36Sopenharmony_ci		if (tb[tm_change_map[i]])
71262306a36Sopenharmony_ci			changed |= BIT(i);
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	mt7915_tm_update_params(phy, changed);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	return 0;
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic int
72162306a36Sopenharmony_cimt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	struct mt7915_phy *phy = mphy->priv;
72462306a36Sopenharmony_ci	struct mt7915_dev *dev = phy->dev;
72562306a36Sopenharmony_ci	enum mt76_rxq_id q;
72662306a36Sopenharmony_ci	void *rx, *rssi;
72762306a36Sopenharmony_ci	u16 fcs_err;
72862306a36Sopenharmony_ci	int i;
72962306a36Sopenharmony_ci	u32 cnt;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX);
73262306a36Sopenharmony_ci	if (!rx)
73362306a36Sopenharmony_ci		return -ENOMEM;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, phy->test.last_freq_offset))
73662306a36Sopenharmony_ci		return -ENOMEM;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RCPI);
73962306a36Sopenharmony_ci	if (!rssi)
74062306a36Sopenharmony_ci		return -ENOMEM;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++)
74362306a36Sopenharmony_ci		if (nla_put_u8(msg, i, phy->test.last_rcpi[i]))
74462306a36Sopenharmony_ci			return -ENOMEM;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	nla_nest_end(msg, rssi);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_IB_RSSI);
74962306a36Sopenharmony_ci	if (!rssi)
75062306a36Sopenharmony_ci		return -ENOMEM;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(phy->test.last_ib_rssi); i++)
75362306a36Sopenharmony_ci		if (nla_put_s8(msg, i, phy->test.last_ib_rssi[i]))
75462306a36Sopenharmony_ci			return -ENOMEM;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	nla_nest_end(msg, rssi);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_WB_RSSI);
75962306a36Sopenharmony_ci	if (!rssi)
76062306a36Sopenharmony_ci		return -ENOMEM;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(phy->test.last_wb_rssi); i++)
76362306a36Sopenharmony_ci		if (nla_put_s8(msg, i, phy->test.last_wb_rssi[i]))
76462306a36Sopenharmony_ci			return -ENOMEM;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	nla_nest_end(msg, rssi);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (nla_put_u8(msg, MT76_TM_RX_ATTR_SNR, phy->test.last_snr))
76962306a36Sopenharmony_ci		return -ENOMEM;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	nla_nest_end(msg, rx);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	cnt = mt76_rr(dev, MT_MIB_SDR3(phy->mt76->band_idx));
77462306a36Sopenharmony_ci	fcs_err = is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) :
77562306a36Sopenharmony_ci		FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	q = phy->mt76->band_idx ? MT_RXQ_BAND1 : MT_RXQ_MAIN;
77862306a36Sopenharmony_ci	mphy->test.rx_stats.packets[q] += fcs_err;
77962306a36Sopenharmony_ci	mphy->test.rx_stats.fcs_error[q] += fcs_err;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	return 0;
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ciconst struct mt76_testmode_ops mt7915_testmode_ops = {
78562306a36Sopenharmony_ci	.set_state = mt7915_tm_set_state,
78662306a36Sopenharmony_ci	.set_params = mt7915_tm_set_params,
78762306a36Sopenharmony_ci	.dump_stats = mt7915_tm_dump_stats,
78862306a36Sopenharmony_ci};
789