blob: abeb149138b540c6c834d01ffaec8e647068d2c1 [file] [log] [blame]
developer0443cd32023-09-19 14:11:49 +08001From 1334f63638dc3cfc7bbcd8f14ee72d2b9847dd6d Mon Sep 17 00:00:00 2001
developer887da632022-10-28 09:35:38 +08002From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
3Date: Thu, 27 Oct 2022 17:42:07 +0800
developer0443cd32023-09-19 14:11:49 +08004Subject: [PATCH 1012/1040] wifi: mt76: testmode: add ZWDFS test mode support
developer887da632022-10-28 09:35:38 +08005
developer887da632022-10-28 09:35:38 +08006Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
7---
developera6df1772022-11-08 13:49:58 +08008 mt76.h | 9 ++
developer887da632022-10-28 09:35:38 +08009 mt76_connac_mcu.h | 2 +
developer62b3f572023-06-07 17:39:27 +080010 mt7915/mcu.c | 66 +++++++++++
11 mt7915/mcu.h | 46 ++++++++
developer887da632022-10-28 09:35:38 +080012 mt7915/mt7915.h | 4 +
developera6df1772022-11-08 13:49:58 +080013 mt7915/regs.h | 2 +
developer62b3f572023-06-07 17:39:27 +080014 mt7915/testmode.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++
15 testmode.c | 25 +++-
16 testmode.h | 45 ++++++++
17 tools/fields.c | 22 ++++
18 10 files changed, 508 insertions(+), 1 deletion(-)
developer887da632022-10-28 09:35:38 +080019
20diff --git a/mt76.h b/mt76.h
developer0443cd32023-09-19 14:11:49 +080021index ed1c864..ed69106 100644
developer887da632022-10-28 09:35:38 +080022--- a/mt76.h
23+++ b/mt76.h
developer0443cd32023-09-19 14:11:49 +080024@@ -754,6 +754,15 @@ struct mt76_testmode_data {
developer887da632022-10-28 09:35:38 +080025 u64 fcs_error[__MT_RXQ_MAX];
26 u64 len_mismatch;
27 } rx_stats;
28+
29+ u8 offchan_ch;
30+ u8 offchan_center_ch;
31+ u8 offchan_bw;
32+
33+ u8 ipi_threshold;
34+ u32 ipi_period;
35+ u8 ipi_antenna_idx;
developera6df1772022-11-08 13:49:58 +080036+ u8 ipi_reset;
developer887da632022-10-28 09:35:38 +080037 };
38
39 struct mt76_vif {
40diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
developer0443cd32023-09-19 14:11:49 +080041index a6449d1..2848179 100644
developer887da632022-10-28 09:35:38 +080042--- a/mt76_connac_mcu.h
43+++ b/mt76_connac_mcu.h
developer0443cd32023-09-19 14:11:49 +080044@@ -1206,6 +1206,7 @@ enum {
developer887da632022-10-28 09:35:38 +080045 MCU_EXT_CMD_OFFCH_SCAN_CTRL = 0x9a,
46 MCU_EXT_CMD_SET_RDD_TH = 0x9d,
47 MCU_EXT_CMD_MURU_CTRL = 0x9f,
48+ MCU_EXT_CMD_IPI_HIST_CTRL = 0xa3,
49 MCU_EXT_CMD_RX_STAT = 0xa4,
50 MCU_EXT_CMD_SET_SPR = 0xa8,
51 MCU_EXT_CMD_GROUP_PRE_CAL_INFO = 0xab,
developer0443cd32023-09-19 14:11:49 +080052@@ -1216,6 +1217,7 @@ enum {
developer887da632022-10-28 09:35:38 +080053 MCU_EXT_CMD_RX_STAT_USER_CTRL = 0xb3,
54 MCU_EXT_CMD_CERT_CFG = 0xb7,
55 MCU_EXT_CMD_CSI_CTRL = 0xc2,
56+ MCU_EXT_CMD_IPI_HIST_SCAN = 0xc5,
57 };
58
59 enum {
60diff --git a/mt7915/mcu.c b/mt7915/mcu.c
developer0443cd32023-09-19 14:11:49 +080061index 474bed1..2cd772b 100644
developer887da632022-10-28 09:35:38 +080062--- a/mt7915/mcu.c
63+++ b/mt7915/mcu.c
developer0443cd32023-09-19 14:11:49 +080064@@ -2670,6 +2670,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy,
developer887da632022-10-28 09:35:38 +080065 req.monitor_chan = chandef->chan->hw_value;
66 req.monitor_central_chan =
67 ieee80211_frequency_to_channel(chandef->center_freq1);
68+ req.monitor_bw = mt76_connac_chan_bw(chandef);
developereb6a0182022-12-12 18:53:32 +080069 req.band_idx = phy->mt76->band_idx;
developer887da632022-10-28 09:35:38 +080070 req.scan_mode = 2;
71 break;
developer0443cd32023-09-19 14:11:49 +080072@@ -4708,3 +4709,68 @@ int mt7915_mcu_set_amsdu_algo(struct mt7915_dev *dev, u16 wcid, u8 enable)
developer887da632022-10-28 09:35:38 +080073 return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MEC_CTRL), &req, sizeof(req), true);
74 }
75 #endif
76+
77+int mt7915_mcu_ipi_hist_ctrl(struct mt7915_phy *phy, void *data, u8 cmd, bool wait_resp)
78+{
79+ struct mt7915_dev *dev = phy->dev;
80+ struct sk_buff *skb;
81+ int ret;
82+ struct {
83+ u8 ipi_hist_idx;
84+ u8 band_idx;
85+ u8 set_val;
86+ u8 rsv;
87+ int idle_power_th;
88+ u32 idle_power_max_cnt;
89+ u32 idle_power_duration;
90+ u32 idle_power_cmd_type;
91+ } __packed req = {
92+ .ipi_hist_idx = cmd,
developereb6a0182022-12-12 18:53:32 +080093+ .band_idx = phy->mt76->band_idx,
developer887da632022-10-28 09:35:38 +080094+ };
95+
developera6df1772022-11-08 13:49:58 +080096+ if (!wait_resp)
97+ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(IPI_HIST_CTRL),
98+ &req, sizeof(req), true);
99+
100+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_QUERY(IPI_HIST_CTRL),
developer887da632022-10-28 09:35:38 +0800101+ &req, sizeof(req), wait_resp, &skb);
developera6df1772022-11-08 13:49:58 +0800102+
developer887da632022-10-28 09:35:38 +0800103+ if (ret)
104+ return ret;
105+
developer887da632022-10-28 09:35:38 +0800106+ memcpy(data, skb->data, sizeof(struct mt7915_mcu_rdd_ipi_ctrl));
107+ dev_kfree_skb(skb);
108+
109+ return 0;
110+}
111+
112+int mt7915_mcu_ipi_hist_scan(struct mt7915_phy *phy, void *data, u8 mode, bool wait_resp)
113+{
114+ struct mt7915_dev *dev = phy->dev;
115+ struct sk_buff *skb;
116+ int ret;
117+ struct rdd_ipi_hist_scan {
118+ u8 mode;
119+ u8 pd_setting;
120+ u8 band_idx;
121+ u8 rsv;
122+ } __packed req = {
123+ .mode = mode,
124+ .pd_setting = 1,
developereb6a0182022-12-12 18:53:32 +0800125+ .band_idx = phy->mt76->band_idx,
developer887da632022-10-28 09:35:38 +0800126+ };
127+
128+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(IPI_HIST_SCAN),
129+ &req, sizeof(req), wait_resp, &skb);
130+ if (ret)
131+ return ret;
132+
133+ if (!wait_resp)
134+ return 0;
135+
136+ memcpy(data, skb->data, sizeof(struct mt7915_mcu_rdd_ipi_scan));
137+ dev_kfree_skb(skb);
138+
139+ return 0;
140+}
141diff --git a/mt7915/mcu.h b/mt7915/mcu.h
developer0443cd32023-09-19 14:11:49 +0800142index 9982735..f4c3bf4 100644
developer887da632022-10-28 09:35:38 +0800143--- a/mt7915/mcu.h
144+++ b/mt7915/mcu.h
developerbbd45e12023-05-19 08:22:06 +0800145@@ -686,6 +686,52 @@ struct csi_data {
developer887da632022-10-28 09:35:38 +0800146 };
147 #endif
148
149+enum {
150+ RDD_SET_IPI_CR_INIT, /* CR initialization */
151+ RDD_SET_IPI_HIST_RESET, /* Reset IPI histogram counter */
152+ RDD_SET_IDLE_POWER, /* Idle power info */
153+ RDD_SET_IPI_HIST_NUM
154+};
155+
156+enum {
157+ RDD_IPI_HIST_0, /* IPI count for power <= -92 (dBm) */
158+ RDD_IPI_HIST_1, /* IPI count for -92 < power <= -89 (dBm) */
159+ RDD_IPI_HIST_2, /* IPI count for -89 < power <= -86 (dBm) */
160+ RDD_IPI_HIST_3, /* IPI count for -86 < power <= -83 (dBm) */
161+ RDD_IPI_HIST_4, /* IPI count for -83 < power <= -80 (dBm) */
162+ RDD_IPI_HIST_5, /* IPI count for -80 < power <= -75 (dBm) */
163+ RDD_IPI_HIST_6, /* IPI count for -75 < power <= -70 (dBm) */
164+ RDD_IPI_HIST_7, /* IPI count for -70 < power <= -65 (dBm) */
165+ RDD_IPI_HIST_8, /* IPI count for -65 < power <= -60 (dBm) */
166+ RDD_IPI_HIST_9, /* IPI count for -60 < power <= -55 (dBm) */
167+ RDD_IPI_HIST_10, /* IPI count for -55 < power (dBm) */
168+ RDD_IPI_FREE_RUN_CNT, /* IPI count for counter++ per 8 us */
169+ RDD_IPI_HIST_ALL_CNT, /* Get all IPI */
170+ RDD_IPI_HIST_0_TO_10_CNT, /* Get IPI histogram 0 to 10 */
171+ RDD_IPI_HIST_2_TO_10_CNT, /* Get IPI histogram 2 to 10 */
172+ RDD_TX_ASSERT_TIME, /* Get band 1 TX assert time */
173+ RDD_IPI_HIST_NUM
174+};
175+
176+#define RDM_NF_MAX_WF_IDX 8
177+#define POWER_INDICATE_HIST_MAX RDD_IPI_FREE_RUN_CNT
178+#define IPI_HIST_TYPE_NUM (POWER_INDICATE_HIST_MAX + 1)
179+
180+struct mt7915_mcu_rdd_ipi_ctrl {
181+ u8 ipi_hist_idx;
182+ u8 band_idx;
183+ u8 rsv[2];
184+ u32 ipi_hist_val[IPI_HIST_TYPE_NUM];
185+ u32 tx_assert_time; /* unit: us */
186+} __packed;
187+
188+struct mt7915_mcu_rdd_ipi_scan {
189+ u32 ipi_hist_val[RDM_NF_MAX_WF_IDX][POWER_INDICATE_HIST_MAX];
190+ u8 band_idx;
191+ u8 rsv[2];
192+ u8 tx_assert_time; /* unit: us */
193+} __packed;
194+
195 /* MURU */
196 #define OFDMA_DL BIT(0)
197 #define OFDMA_UL BIT(1)
198diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
developer0443cd32023-09-19 14:11:49 +0800199index 18881c0..33ac2d2 100644
developer887da632022-10-28 09:35:38 +0800200--- a/mt7915/mt7915.h
201+++ b/mt7915/mt7915.h
developer8f0d89b2023-07-28 07:16:44 +0800202@@ -256,6 +256,7 @@ struct mt7915_phy {
developer887da632022-10-28 09:35:38 +0800203
developer2157bf82023-06-26 02:27:49 +0800204 struct mt76_mib_stats mib;
developer887da632022-10-28 09:35:38 +0800205 struct mt76_channel_state state_ts;
206+ struct delayed_work ipi_work;
207
developer5bea7322023-04-13 18:50:55 +0800208 u8 stats_work_count;
209 struct list_head stats_list;
developer0443cd32023-09-19 14:11:49 +0800210@@ -710,6 +711,9 @@ int mt7915_vendor_amnt_sta_remove(struct mt7915_phy *phy,
developer887da632022-10-28 09:35:38 +0800211 struct ieee80211_sta *sta);
212 #endif
213
214+int mt7915_mcu_ipi_hist_ctrl(struct mt7915_phy *phy, void *data, u8 cmd, bool wait_resp);
215+int mt7915_mcu_ipi_hist_scan(struct mt7915_phy *phy, void *data, u8 mode, bool wait_resp);
216+
217 #ifdef MTK_DEBUG
218 int mt7915_mtk_init_debugfs(struct mt7915_phy *phy, struct dentry *dir);
219 int mt7915_dbg_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3, bool wait_resp);
developera6df1772022-11-08 13:49:58 +0800220diff --git a/mt7915/regs.h b/mt7915/regs.h
developer0443cd32023-09-19 14:11:49 +0800221index 6783797..44863e8 100644
developera6df1772022-11-08 13:49:58 +0800222--- a/mt7915/regs.h
223+++ b/mt7915/regs.h
developer0443cd32023-09-19 14:11:49 +0800224@@ -1209,6 +1209,8 @@ enum offs_rev {
developera6df1772022-11-08 13:49:58 +0800225 #define MT_WF_IRPI_NSS(phy, nss) MT_WF_IRPI(0x6000 + ((phy) << 20) + ((nss) << 16))
226 #define MT_WF_IRPI_NSS_MT7916(phy, nss) MT_WF_IRPI(0x1000 + ((phy) << 20) + ((nss) << 16))
227
228+#define MT_WF_IPI_RESET 0x831a3008
229+
230 /* PHY */
231 #define MT_WF_PHY_BASE 0x83080000
232 #define MT_WF_PHY(ofs) (MT_WF_PHY_BASE + (ofs))
developer887da632022-10-28 09:35:38 +0800233diff --git a/mt7915/testmode.c b/mt7915/testmode.c
developer0443cd32023-09-19 14:11:49 +0800234index 86deae6..e321848 100644
developer887da632022-10-28 09:35:38 +0800235--- a/mt7915/testmode.c
236+++ b/mt7915/testmode.c
developera6df1772022-11-08 13:49:58 +0800237@@ -13,6 +13,12 @@ enum {
developer887da632022-10-28 09:35:38 +0800238 TM_CHANGED_AID,
239 TM_CHANGED_CFG,
240 TM_CHANGED_TXBF_ACT,
241+ TM_CHANGED_OFF_CHAN_CH,
242+ TM_CHANGED_OFF_CHAN_CENTER_CH,
243+ TM_CHANGED_OFF_CHAN_BW,
244+ TM_CHANGED_IPI_THRESHOLD,
245+ TM_CHANGED_IPI_PERIOD,
developera6df1772022-11-08 13:49:58 +0800246+ TM_CHANGED_IPI_RESET,
developer887da632022-10-28 09:35:38 +0800247
248 /* must be last */
249 NUM_TM_CHANGED
developera6df1772022-11-08 13:49:58 +0800250@@ -24,6 +30,12 @@ static const u8 tm_change_map[] = {
developer887da632022-10-28 09:35:38 +0800251 [TM_CHANGED_AID] = MT76_TM_ATTR_AID,
252 [TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
253 [TM_CHANGED_TXBF_ACT] = MT76_TM_ATTR_TXBF_ACT,
254+ [TM_CHANGED_OFF_CHAN_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CH,
255+ [TM_CHANGED_OFF_CHAN_CENTER_CH] = MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
256+ [TM_CHANGED_OFF_CHAN_BW] = MT76_TM_ATTR_OFF_CH_SCAN_BW,
257+ [TM_CHANGED_IPI_THRESHOLD] = MT76_TM_ATTR_IPI_THRESHOLD,
258+ [TM_CHANGED_IPI_PERIOD] = MT76_TM_ATTR_IPI_PERIOD,
developera6df1772022-11-08 13:49:58 +0800259+ [TM_CHANGED_IPI_RESET] = MT76_TM_ATTR_IPI_RESET,
developer887da632022-10-28 09:35:38 +0800260 };
261
262 struct reg_band {
developer0443cd32023-09-19 14:11:49 +0800263@@ -982,6 +994,272 @@ mt7915_tm_set_txbf(struct mt7915_phy *phy)
developer887da632022-10-28 09:35:38 +0800264 return 0;
265 }
266
developer62b3f572023-06-07 17:39:27 +0800267+static u8
268+mt7915_tm_get_center_chan(struct mt7915_phy *phy, struct cfg80211_chan_def *chandef,
269+ int width_mhz)
270+{
271+ struct mt76_phy *mphy = phy->mt76;
272+ const struct ieee80211_channel *chan = mphy->sband_5g.sband.channels;
273+ u32 bitmap, i, offset, size = 32;
274+ u16 first_control = 0, control_chan = chandef->chan->hw_value;
275+ static const u32 width_to_bitmap[] = {
276+ [NL80211_CHAN_WIDTH_20_NOHT] = 0x0,
277+ [NL80211_CHAN_WIDTH_20] = 0x0,
278+ [NL80211_CHAN_WIDTH_40] = 0x55554055,
279+ [NL80211_CHAN_WIDTH_80] = 0x44444011,
280+ [NL80211_CHAN_WIDTH_80P80] = 0x0,
281+ [NL80211_CHAN_WIDTH_160] = 0x04004001,
282+ };
283+
284+ bitmap = width_to_bitmap[chandef->width];
285+ if (!bitmap)
286+ return control_chan;
287+
288+ offset = width_mhz / 10 - 2;
289+ for (i = 0; i < size; i++) {
290+ if (!((1 << i) & bitmap))
291+ continue;
292+
293+ if (control_chan >= chan[i].hw_value)
294+ first_control = chan[i].hw_value;
295+ else
296+ break;
297+ }
298+
299+ if (chandef->width == NL80211_CHAN_WIDTH_40 &&
300+ control_chan >= chan[size].hw_value)
301+ return chan[size].hw_value + offset;
302+ else if (first_control == 0)
303+ return control_chan;
304+
305+ return first_control + offset;
306+}
307+
developer887da632022-10-28 09:35:38 +0800308+static int
developer62b3f572023-06-07 17:39:27 +0800309+mt7915_tm_set_offchan(struct mt7915_phy *phy, bool no_center)
developer887da632022-10-28 09:35:38 +0800310+{
311+ struct mt76_phy *mphy = phy->mt76;
312+ struct mt7915_dev *dev = phy->dev;
313+ struct ieee80211_hw *hw = mphy->hw;
314+ struct mt76_testmode_data *td = &phy->mt76->test;
315+ struct cfg80211_chan_def chandef = {};
316+ struct ieee80211_channel *chan;
317+ int ret, freq = ieee80211_channel_to_frequency(td->offchan_ch, NL80211_BAND_5GHZ);
318+ int width_mhz;
319+ const int bw_to_mhz[] = {
320+ [NL80211_CHAN_WIDTH_20_NOHT] = 20,
321+ [NL80211_CHAN_WIDTH_20] = 20,
322+ [NL80211_CHAN_WIDTH_40] = 40,
323+ [NL80211_CHAN_WIDTH_80] = 80,
324+ [NL80211_CHAN_WIDTH_80P80] = 80,
325+ [NL80211_CHAN_WIDTH_160] = 160,
326+ };
327+
developera6df1772022-11-08 13:49:58 +0800328+ if (!mphy->cap.has_5ghz || !freq) {
developer887da632022-10-28 09:35:38 +0800329+ ret = -EINVAL;
330+ dev_info(dev->mt76.dev, "Failed to set offchan (invalid band or channel)!\n");
331+ goto out;
332+ }
333+
334+ chandef.width = td->offchan_bw;
335+ width_mhz = bw_to_mhz[chandef.width];
developer887da632022-10-28 09:35:38 +0800336+ chan = ieee80211_get_channel(hw->wiphy, freq);
developer62b3f572023-06-07 17:39:27 +0800337+ if (!chan) {
338+ ret = -EINVAL;
339+ dev_info(dev->mt76.dev, "Failed to set offchan (invalid control channel)!\n");
340+ goto out;
341+ }
developer887da632022-10-28 09:35:38 +0800342+ chandef.chan = chan;
343+
developer62b3f572023-06-07 17:39:27 +0800344+ if (no_center)
345+ td->offchan_center_ch = mt7915_tm_get_center_chan(phy, &chandef, width_mhz);
346+ chandef.center_freq1 = ieee80211_channel_to_frequency(td->offchan_center_ch,
347+ NL80211_BAND_5GHZ);
348+ if (!cfg80211_chandef_valid(&chandef)) {
349+ ret = -EINVAL;
350+ dev_info(dev->mt76.dev, "Failed to set offchan, chandef is invalid!\n");
351+ goto out;
352+ }
353+
developer887da632022-10-28 09:35:38 +0800354+ memset(&dev->rdd2_chandef, 0, sizeof(struct cfg80211_chan_def));
355+
356+ ret = mt7915_mcu_rdd_background_enable(phy, &chandef);
357+
358+ if (ret)
359+ goto out;
360+
361+ dev->rdd2_phy = phy;
362+ dev->rdd2_chandef = chandef;
363+
364+ return ret;
365+
366+out:
367+ td->offchan_ch = 0;
developer62b3f572023-06-07 17:39:27 +0800368+ td->offchan_center_ch = 0;
developer887da632022-10-28 09:35:38 +0800369+ td->offchan_bw = 0;
370+
371+ return ret;
372+}
373+
374+static void
375+mt7915_tm_dump_ipi(struct mt7915_phy *phy, void *data, u8 antenna_num,
376+ u8 start_antenna_idx, bool is_scan)
377+{
378+#define PRECISION 100
379+ struct mt7915_dev *dev = phy->dev;
380+ struct mt76_testmode_data *td = &phy->mt76->test;
381+ struct mt7915_mcu_rdd_ipi_scan *scan_data;
382+ struct mt7915_mcu_rdd_ipi_ctrl *ctrl_data;
383+ u32 ipi_idx, ipi_free_count, ipi_percentage, ipi_hist_count_th, ipi_hist_total_count;
384+ u32 self_idle_ratio, ipi_idle_ratio, channel_load, tx_assert_time;
385+ u8 i, antenna_idx = start_antenna_idx;
386+ u32 *ipi_hist_data;
387+ const char *power_lower_bound, *power_upper_bound;
388+ static const char * const ipi_idx_to_power_bound[] = {
389+ [RDD_IPI_HIST_0] = "-92",
390+ [RDD_IPI_HIST_1] = "-89",
391+ [RDD_IPI_HIST_2] = "-86",
392+ [RDD_IPI_HIST_3] = "-83",
393+ [RDD_IPI_HIST_4] = "-80",
394+ [RDD_IPI_HIST_5] = "-75",
395+ [RDD_IPI_HIST_6] = "-70",
396+ [RDD_IPI_HIST_7] = "-65",
397+ [RDD_IPI_HIST_8] = "-60",
398+ [RDD_IPI_HIST_9] = "-55",
399+ [RDD_IPI_HIST_10] = "inf",
400+ };
401+
402+ if (is_scan) {
403+ scan_data = (struct mt7915_mcu_rdd_ipi_scan *)data;
404+ tx_assert_time = scan_data->tx_assert_time;
405+ } else {
406+ ctrl_data = (struct mt7915_mcu_rdd_ipi_ctrl *)data;
407+ tx_assert_time = ctrl_data->tx_assert_time;
408+ }
409+
410+ for (i = 0; i < antenna_num; i++) {
411+ ipi_free_count = 0;
412+ ipi_hist_count_th = 0;
413+ ipi_hist_total_count = 0;
414+ ipi_hist_data = is_scan ? scan_data->ipi_hist_val[antenna_idx] :
415+ ctrl_data->ipi_hist_val;
416+
417+ dev_info(dev->mt76.dev, "Antenna index: %d\n", antenna_idx);
418+ for (ipi_idx = 0; ipi_idx < POWER_INDICATE_HIST_MAX; ipi_idx++) {
419+ power_lower_bound = ipi_idx ? ipi_idx_to_power_bound[ipi_idx - 1] :
420+ "-inf";
421+ power_upper_bound = ipi_idx_to_power_bound[ipi_idx];
422+
423+ dev_info(dev->mt76.dev,
424+ "IPI %d (power range: (%s, %s] dBm): ipi count = %d\n",
425+ ipi_idx, power_lower_bound,
426+ power_upper_bound, ipi_hist_data[ipi_idx]);
427+
428+ if (td->ipi_threshold <= ipi_idx && ipi_idx <= RDD_IPI_HIST_10)
429+ ipi_hist_count_th += ipi_hist_data[ipi_idx];
430+
431+ ipi_hist_total_count += ipi_hist_data[ipi_idx];
432+ }
433+ ipi_free_count = is_scan ? ipi_hist_total_count :
434+ ipi_hist_data[RDD_IPI_FREE_RUN_CNT];
435+
436+ dev_info(dev->mt76.dev,
437+ "IPI threshold %d: ipi_hist_count_th = %d, ipi_free_count = %d\n",
438+ td->ipi_threshold, ipi_hist_count_th, ipi_free_count);
439+ dev_info(dev->mt76.dev, "TX assert time = %d [ms]\n",
440+ tx_assert_time / 1000);
441+
442+ // Calculate channel load = (self idle ratio - idle ratio) / self idle ratio
443+ if (ipi_hist_count_th >= UINT_MAX / (100 * PRECISION))
444+ ipi_percentage = 100 * PRECISION *
445+ (ipi_hist_count_th / (100 * PRECISION)) /
446+ (ipi_free_count / (100 * PRECISION));
447+ else
448+ ipi_percentage = PRECISION * 100 * ipi_hist_count_th / ipi_free_count;
449+
450+ ipi_idle_ratio = ((100 * PRECISION) - ipi_percentage) / PRECISION;
451+
452+ self_idle_ratio = PRECISION * 100 *
453+ (td->ipi_period - (tx_assert_time / 1000)) /
454+ td->ipi_period / PRECISION;
455+
456+ if (self_idle_ratio < ipi_idle_ratio)
457+ channel_load = 0;
458+ else
459+ channel_load = self_idle_ratio - ipi_idle_ratio;
460+
461+ if (self_idle_ratio <= td->ipi_threshold) {
462+ dev_info(dev->mt76.dev,
463+ "band[%d]: self idle ratio = %d%%, idle ratio = %d%%\n",
developereb6a0182022-12-12 18:53:32 +0800464+ phy->mt76->band_idx, self_idle_ratio, ipi_idle_ratio);
developer887da632022-10-28 09:35:38 +0800465+ return;
466+ }
467+
468+ channel_load = (100 * channel_load) / self_idle_ratio;
469+ dev_info(dev->mt76.dev,
470+ "band[%d]: chan load = %d%%, self idle ratio = %d%%, idle ratio = %d%%\n",
developereb6a0182022-12-12 18:53:32 +0800471+ phy->mt76->band_idx, channel_load, self_idle_ratio, ipi_idle_ratio);
developer887da632022-10-28 09:35:38 +0800472+ antenna_idx++;
473+ }
474+}
475+
476+static void
477+mt7915_tm_ipi_work(struct work_struct *work)
478+{
479+ struct mt7915_phy *phy = container_of(work, struct mt7915_phy, ipi_work.work);
480+ struct mt7915_dev *dev = phy->dev;
481+ struct mt76_testmode_data *td = &phy->mt76->test;
482+ u8 start_antenna_idx = 0, antenna_num = 1;
483+
484+ if (!is_mt7915(&dev->mt76)) {
485+ struct mt7915_mcu_rdd_ipi_scan data;
486+
developereb6a0182022-12-12 18:53:32 +0800487+ if (phy->mt76->band_idx)
developer887da632022-10-28 09:35:38 +0800488+ start_antenna_idx = 4;
489+
490+ /* Use all antenna */
491+ if (td->ipi_antenna_idx == MT76_TM_IPI_ANTENNA_ALL)
492+ antenna_num = 4;
493+ else
494+ start_antenna_idx += td->ipi_antenna_idx;
495+
496+ mt7915_mcu_ipi_hist_scan(phy, &data, 0, true);
497+ mt7915_tm_dump_ipi(phy, &data, antenna_num, start_antenna_idx, true);
498+ } else {
499+ struct mt7915_mcu_rdd_ipi_ctrl data;
500+
developera6df1772022-11-08 13:49:58 +0800501+ start_antenna_idx = 4;
developer887da632022-10-28 09:35:38 +0800502+ mt7915_mcu_ipi_hist_ctrl(phy, &data, RDD_IPI_HIST_ALL_CNT, true);
503+ mt7915_tm_dump_ipi(phy, &data, antenna_num, start_antenna_idx, false);
504+ }
505+}
506+
developera6df1772022-11-08 13:49:58 +0800507+static inline void
508+mt7915_tm_reset_ipi(struct mt7915_phy *phy)
509+{
510+#define IPI_RESET_BIT BIT(2)
511+ struct mt7915_dev *dev = phy->dev;
512+
513+ if (is_mt7915(&dev->mt76))
514+ mt7915_mcu_ipi_hist_ctrl(phy, NULL, RDD_SET_IPI_HIST_RESET, false);
515+ else
516+ mt76_set(dev, MT_WF_IPI_RESET, IPI_RESET_BIT);
517+}
518+
developer887da632022-10-28 09:35:38 +0800519+static int
520+mt7915_tm_set_ipi(struct mt7915_phy *phy)
521+{
522+ struct mt76_testmode_data *td = &phy->mt76->test;
523+
developera6df1772022-11-08 13:49:58 +0800524+ mt7915_tm_reset_ipi(phy);
525+
developer887da632022-10-28 09:35:38 +0800526+ cancel_delayed_work(&phy->ipi_work);
527+ ieee80211_queue_delayed_work(phy->mt76->hw, &phy->ipi_work,
528+ msecs_to_jiffies(td->ipi_period));
529+
530+ return 0;
531+}
532+
533 static int
534 mt7915_tm_set_wmm_qid(struct mt7915_phy *phy, u8 qid, u8 aifs, u8 cw_min,
535 u16 cw_max, u16 txop, u8 tx_cmd)
developer0443cd32023-09-19 14:11:49 +0800536@@ -1269,6 +1547,8 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
developer887da632022-10-28 09:35:38 +0800537 phy->mt76->test.tx_mpdu_len = 0;
538 phy->test.bf_en = 0;
539 mt7915_tm_set_entry(phy);
540+ } else {
541+ INIT_DELAYED_WORK(&phy->ipi_work, mt7915_tm_ipi_work);
542 }
543 }
544
developer0443cd32023-09-19 14:11:49 +0800545@@ -2028,6 +2308,14 @@ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
developer887da632022-10-28 09:35:38 +0800546 mt7915_tm_set_cfg(phy);
547 if (changed & BIT(TM_CHANGED_TXBF_ACT))
548 mt7915_tm_set_txbf(phy);
549+ if ((changed & BIT(TM_CHANGED_OFF_CHAN_CH)) &&
550+ (changed & BIT(TM_CHANGED_OFF_CHAN_BW)))
developer62b3f572023-06-07 17:39:27 +0800551+ mt7915_tm_set_offchan(phy, !(changed & BIT(TM_CHANGED_OFF_CHAN_CENTER_CH)));
developer887da632022-10-28 09:35:38 +0800552+ if ((changed & BIT(TM_CHANGED_IPI_THRESHOLD)) &&
553+ (changed & BIT(TM_CHANGED_IPI_PERIOD)))
554+ mt7915_tm_set_ipi(phy);
developera6df1772022-11-08 13:49:58 +0800555+ if (changed & BIT(TM_CHANGED_IPI_RESET))
556+ mt7915_tm_reset_ipi(phy);
developer887da632022-10-28 09:35:38 +0800557 }
558
559 static int
560diff --git a/testmode.c b/testmode.c
developer0443cd32023-09-19 14:11:49 +0800561index f415bec..babae8d 100644
developer887da632022-10-28 09:35:38 +0800562--- a/testmode.c
563+++ b/testmode.c
developer849549c2023-08-02 17:26:48 +0800564@@ -25,6 +25,13 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
developer887da632022-10-28 09:35:38 +0800565 [MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
566 [MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
567 [MT76_TM_ATTR_DRV_DATA] = { .type = NLA_NESTED },
568+ [MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
569+ [MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
570+ [MT76_TM_ATTR_OFF_CH_SCAN_PATH] = { .type = NLA_U8 },
571+ [MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
572+ [MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
573+ [MT76_TM_ATTR_IPI_ANTENNA_INDEX] = { .type = NLA_U8 },
developera6df1772022-11-08 13:49:58 +0800574+ [MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
developer887da632022-10-28 09:35:38 +0800575 };
576 EXPORT_SYMBOL_GPL(mt76_tm_policy);
577
developer849549c2023-08-02 17:26:48 +0800578@@ -406,6 +413,7 @@ mt76_testmode_init_defaults(struct mt76_phy *phy)
developer887da632022-10-28 09:35:38 +0800579 td->tx_count = 1;
580 td->tx_rate_mode = MT76_TM_TX_MODE_OFDM;
581 td->tx_rate_nss = 1;
582+ td->ipi_antenna_idx = MT76_TM_IPI_ANTENNA_ALL;
583
584 memcpy(td->addr[0], phy->macaddr, ETH_ALEN);
585 memcpy(td->addr[1], phy->macaddr, ETH_ALEN);
developer849549c2023-08-02 17:26:48 +0800586@@ -611,6 +619,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
developer887da632022-10-28 09:35:38 +0800587 if (tb[MT76_TM_ATTR_TX_RATE_IDX])
588 td->tx_rate_idx = nla_get_u8(tb[MT76_TM_ATTR_TX_RATE_IDX]);
589
590+ if (tb[MT76_TM_ATTR_IPI_PERIOD])
591+ td->ipi_period = nla_get_u32(tb[MT76_TM_ATTR_IPI_PERIOD]);
592+
593 if (mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_MODE], &td->tx_rate_mode,
594 0, MT76_TM_TX_MODE_MAX) ||
595 mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_NSS], &td->tx_rate_nss,
developer849549c2023-08-02 17:26:48 +0800596@@ -627,7 +638,16 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
developer887da632022-10-28 09:35:38 +0800597 &td->tx_power_control, 0, 1) ||
598 mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16) ||
599 mt76_tm_get_u8(tb[MT76_TM_ATTR_RU_ALLOC], &td->ru_alloc, 0, 0xff) ||
600- mt76_tm_get_u8(tb[MT76_TM_ATTR_RU_IDX], &td->ru_idx, 0, 68))
601+ mt76_tm_get_u8(tb[MT76_TM_ATTR_RU_IDX], &td->ru_idx, 0, 68) ||
602+ mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CH], &td->offchan_ch, 36, 196) ||
603+ mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH], &td->offchan_center_ch,
604+ 36, 196) ||
605+ mt76_tm_get_u8(tb[MT76_TM_ATTR_OFF_CH_SCAN_BW],
606+ &td->offchan_bw, NL80211_CHAN_WIDTH_20_NOHT, NL80211_CHAN_WIDTH_160) ||
607+ mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_THRESHOLD], &td->ipi_threshold, 0, 10) ||
608+ mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_ANTENNA_INDEX], &td->ipi_antenna_idx,
developera6df1772022-11-08 13:49:58 +0800609+ MT76_TM_IPI_ANTENNA_0, MT76_TM_IPI_ANTENNA_ALL) ||
610+ mt76_tm_get_u8(tb[MT76_TM_ATTR_IPI_RESET], &td->ipi_reset, 0, 1))
developer887da632022-10-28 09:35:38 +0800611 goto out;
612
613 if (tb[MT76_TM_ATTR_TX_LENGTH]) {
developer849549c2023-08-02 17:26:48 +0800614@@ -861,6 +881,9 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
developer887da632022-10-28 09:35:38 +0800615 nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_MODE, td->tx_rate_mode) ||
616 nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, td->tx_rate_sgi) ||
617 nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
618+ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CH, td->offchan_ch) ||
619+ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH, td->offchan_center_ch) ||
620+ nla_put_u8(msg, MT76_TM_ATTR_OFF_CH_SCAN_BW, td->offchan_bw) ||
621 (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
622 nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
623 (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
624diff --git a/testmode.h b/testmode.h
developer0443cd32023-09-19 14:11:49 +0800625index d2675dd..97e7596 100644
developer887da632022-10-28 09:35:38 +0800626--- a/testmode.h
627+++ b/testmode.h
developera6df1772022-11-08 13:49:58 +0800628@@ -63,6 +63,20 @@
developer62b3f572023-06-07 17:39:27 +0800629 * (nested, u8 attrs)
developer887da632022-10-28 09:35:38 +0800630 *
631 * @MT76_TM_ATTR_CFG: config testmode rf feature (nested, see &mt76_testmode_cfg)
632+ * @MT76_TM_ATTR_TXBF_ACT: txbf setting actions (u8)
633+ * @MT76_TM_ATTR_TXBF_PARAM: txbf parameters (nested)
634+ *
635+ * @MT76_TM_ATTR_OFF_CH_SCAN_CH: config the channel of background chain (ZWDFS) (u8)
636+ * @MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH: config the center channel of background chain (ZWDFS) (u8)
637+ * @MT76_TM_ATTR_OFF_CH_SCAN_BW: config the bandwidth of background chain (ZWDFS) (u8)
638+ * @MT76_TM_ATTR_OFF_CH_SCAN_PATH: config the tx path of background chain (ZWDFS) (u8)
639+ *
640+ * @MT76_TM_ATTR_IPI_THRESHOLD: config the IPI index you want to read (u8)
641+ * @MT76_TM_ATTR_IPI_PERIOD: config the time period for reading
642+ * the histogram of specific IPI index (u8)
643+ * @MT76_TM_ATTR_IPI_ANTENNA_INDEX: config the antenna index for reading
644+ * the histogram of specific IPI index (u8)
developera6df1772022-11-08 13:49:58 +0800645+ * @MT76_TM_ATTR_IPI_RESET: Reset the IPI counter
developer887da632022-10-28 09:35:38 +0800646 *
647 */
648 enum mt76_testmode_attr {
developera6df1772022-11-08 13:49:58 +0800649@@ -116,6 +130,16 @@ enum mt76_testmode_attr {
developer887da632022-10-28 09:35:38 +0800650 MT76_TM_ATTR_TXBF_ACT,
651 MT76_TM_ATTR_TXBF_PARAM,
652
653+ MT76_TM_ATTR_OFF_CH_SCAN_CH,
654+ MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
655+ MT76_TM_ATTR_OFF_CH_SCAN_BW,
656+ MT76_TM_ATTR_OFF_CH_SCAN_PATH,
657+
658+ MT76_TM_ATTR_IPI_THRESHOLD,
659+ MT76_TM_ATTR_IPI_PERIOD,
660+ MT76_TM_ATTR_IPI_ANTENNA_INDEX,
developera6df1772022-11-08 13:49:58 +0800661+ MT76_TM_ATTR_IPI_RESET,
developer887da632022-10-28 09:35:38 +0800662+
663 /* keep last */
664 NUM_MT76_TM_ATTRS,
665 MT76_TM_ATTR_MAX = NUM_MT76_TM_ATTRS - 1,
developera6df1772022-11-08 13:49:58 +0800666@@ -292,4 +316,25 @@ enum mt76_testmode_txbf_act {
developer887da632022-10-28 09:35:38 +0800667 MT76_TM_TXBF_ACT_MAX = NUM_MT76_TM_TXBF_ACT - 1,
668 };
669
670+/**
671+ * enum mt76_testmode_ipi_antenna - specify antenna index for ipi count
672+ *
673+ * @MT76_TM_IPI_ANTENNA_0: use antenna 0
674+ * @MT76_TM_IPI_ANTENNA_1: use antenna 1
675+ * @MT76_TM_IPI_ANTENNA_2: use antenna 2
676+ * @MT76_TM_IPI_ANTENNA_3: use antenna 3
677+ * @MT76_TM_IPI_ANTENNA_ALL: use all antenna
678+ */
679+enum mt76_testmode_ipi_antenna {
680+ MT76_TM_IPI_ANTENNA_0,
681+ MT76_TM_IPI_ANTENNA_1,
682+ MT76_TM_IPI_ANTENNA_2,
683+ MT76_TM_IPI_ANTENNA_3,
684+ MT76_TM_IPI_ANTENNA_ALL,
685+
686+ /* keep last */
687+ NUM_MT76_TM_IPI_ANTENNA,
688+ MT76_TM_IPI_ANTENNA_MAX = NUM_MT76_TM_IPI_ANTENNA - 1,
689+};
690+
691 #endif
692diff --git a/tools/fields.c b/tools/fields.c
developer0443cd32023-09-19 14:11:49 +0800693index 6bf4c04..fc01043 100644
developer887da632022-10-28 09:35:38 +0800694--- a/tools/fields.c
695+++ b/tools/fields.c
696@@ -46,6 +46,14 @@ static const char * const testmode_txbf_act[] = {
697 [MT76_TM_TXBF_ACT_E2P_UPDATE] = "e2p_update",
698 };
699
700+static const char * const testmode_offchan_bw[] = {
701+ [NL80211_CHAN_WIDTH_20_NOHT] = "NOHT",
702+ [NL80211_CHAN_WIDTH_20] = "20",
703+ [NL80211_CHAN_WIDTH_40] = "40",
704+ [NL80211_CHAN_WIDTH_80] = "80",
705+ [NL80211_CHAN_WIDTH_160] = "160",
706+};
707+
708 static void print_enum(const struct tm_field *field, struct nlattr *attr)
709 {
710 unsigned int i = nla_get_u8(attr);
developera6df1772022-11-08 13:49:58 +0800711@@ -411,6 +419,13 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
developer887da632022-10-28 09:35:38 +0800712 FIELD(u8, RU_IDX, "ru_idx"),
713 FIELD_ENUM(TXBF_ACT, "txbf_act", testmode_txbf_act),
714 FIELD_ARRAY(u16_hex, TXBF_PARAM, "txbf_param"),
715+ FIELD(u8, OFF_CH_SCAN_CH, "offchan_ch"),
716+ FIELD(u8, OFF_CH_SCAN_CENTER_CH, "offchan_center_ch"),
717+ FIELD_ENUM(OFF_CH_SCAN_BW, "offchan_bw", testmode_offchan_bw),
718+ FIELD(u8, IPI_THRESHOLD, "ipi_threshold"),
719+ FIELD(u32, IPI_PERIOD, "ipi_period"),
720+ FIELD(u8, IPI_ANTENNA_INDEX, "ipi_antenna_idx"),
developera6df1772022-11-08 13:49:58 +0800721+ FIELD(u8, IPI_RESET, "ipi_reset"),
developer887da632022-10-28 09:35:38 +0800722 FIELD_MAC(MAC_ADDRS, "mac_addrs"),
723 FIELD_NESTED_RO(STATS, stats, "",
724 .print_extra = print_extra_stats),
developera6df1772022-11-08 13:49:58 +0800725@@ -442,6 +457,13 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
developer887da632022-10-28 09:35:38 +0800726 [MT76_TM_ATTR_RU_IDX] = { .type = NLA_U8 },
727 [MT76_TM_ATTR_STATS] = { .type = NLA_NESTED },
728 [MT76_TM_ATTR_TXBF_ACT] = { .type = NLA_U8 },
729+ [MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
730+ [MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
731+ [MT76_TM_ATTR_OFF_CH_SCAN_BW] = { .type = NLA_U8 },
732+ [MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
733+ [MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
734+ [MT76_TM_ATTR_IPI_ANTENNA_INDEX] = { .type = NLA_U8 },
developera6df1772022-11-08 13:49:58 +0800735+ [MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
developer887da632022-10-28 09:35:38 +0800736 };
737
738 const struct tm_field msg_field = {
739--
developer0443cd32023-09-19 14:11:49 +08007402.18.0
developer887da632022-10-28 09:35:38 +0800741