blob: ddc45f4cbf169bb861c5be2c76992080747840a0 [file] [log] [blame]
developerd0c89452024-10-11 16:53:27 +08001From 4f43eb23f373930534372c1b82787f2da37c940d Mon Sep 17 00:00:00 2001
developer05f3b2b2024-08-19 19:17:34 +08002From: Allen Ye <allen.ye@mediatek.com>
3Date: Thu, 1 Aug 2024 11:25:03 +0800
developerd0c89452024-10-11 16:53:27 +08004Subject: [PATCH 186/223] mtk: mt76: mt7996: Add AFC and lpi power support
developer05f3b2b2024-08-19 19:17:34 +08005
6This patch receiving and storing the power table from hostapd vendor cmd.
7The power table would be use to compare the sku table in standard power
8mode every time when setting power and mt76 would set the minimum values
9between afc and sku table to fw.
10The AFC table format is like below:
11col\row bw20 bw40 ... ru26 ... ru3472
12chan 1
13chan 6
14...
15chan 233
16To compare the afc and sku table, mt76 will find the power list of current
17channel in AFC table. And mtk sku table design serveral rates for each
18bandwidth, so a power value of afc table could compare with serveral
19rates in mtk sku table.
20
21 - Add a new vendor attibute lpi mode, due to the lpi is tuntime decided by AFC
22not like wifi6 switch lpi and standard power should reload interface.
23
24Add dump afc table and information.
25Add the bf on value offset logic. The offset is antenna/beamform gain
26offset = 10 * (log(num of ant) - log(num of NSS)).
27
developerd0c89452024-10-11 16:53:27 +080028Change-Id: Ibc6e9adb149bec02d3443d6e0309c9ac122a925b
29Change-Id: Ic24ee5f95eb5c11d1031428ee1d4f3fc9ff39e11
developer05f3b2b2024-08-19 19:17:34 +080030Signed-off-by: Allen Ye <allen.ye@mediatek.com>
31---
32 mt76.h | 2 +
33 mt7996/mac.c | 2 +-
34 mt7996/main.c | 2 +-
35 mt7996/mcu.c | 186 ++++++++++++++++++++++++++++++++++++++++---
36 mt7996/mcu.h | 139 ++++++++++++++++++++++++++++++++
37 mt7996/mt7996.h | 2 +
38 mt7996/mtk_debugfs.c | 37 +++++++++
39 mt7996/vendor.c | 81 +++++++++++++++----
40 mt7996/vendor.h | 3 +
41 9 files changed, 429 insertions(+), 25 deletions(-)
42
43diff --git a/mt76.h b/mt76.h
developerd0c89452024-10-11 16:53:27 +080044index 50d52776..a465785f 100644
developer05f3b2b2024-08-19 19:17:34 +080045--- a/mt76.h
46+++ b/mt76.h
developerd0c89452024-10-11 16:53:27 +080047@@ -1078,6 +1078,8 @@ struct mt76_dev {
developer05f3b2b2024-08-19 19:17:34 +080048 bool lpi_psd;
49 bool lpi_bcn_enhance;
50 bool mgmt_pwr_enhance;
51+ bool lpi_mode;
52+ s8 **afc_power_table;
53
54 #ifdef CONFIG_NL80211_TESTMODE
55 const struct mt76_testmode_ops *test_ops;
56diff --git a/mt7996/mac.c b/mt7996/mac.c
developerd0c89452024-10-11 16:53:27 +080057index b0b664e7..5d40b8f1 100644
developer05f3b2b2024-08-19 19:17:34 +080058--- a/mt7996/mac.c
59+++ b/mt7996/mac.c
developerd0c89452024-10-11 16:53:27 +080060@@ -908,7 +908,7 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
developer05f3b2b2024-08-19 19:17:34 +080061 if (mcast)
62 val |= MT_TXD6_DIS_MAT;
63 if (dev->mt76.phys[band_idx]->cap.has_6ghz &&
64- dev->mt76.lpi_bcn_enhance &&
65+ dev->mt76.lpi_mode && dev->mt76.lpi_bcn_enhance &&
66 ieee80211_is_mgmt(hdr->frame_control))
67 val |= FIELD_PREP(MT_TXD6_BW, FW_CDBW_80MHZ);
68 txwi[6] |= cpu_to_le32(val);
69diff --git a/mt7996/main.c b/mt7996/main.c
developerd0c89452024-10-11 16:53:27 +080070index 290dff6e..812d8269 100644
developer05f3b2b2024-08-19 19:17:34 +080071--- a/mt7996/main.c
72+++ b/mt7996/main.c
73@@ -891,7 +891,7 @@ mt7996_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_bss_conf *conf,
74
75 if (dev->cert_mode && phy->mt76->band_idx == MT_BAND2)
76 rate = 0x0200;
77- else if (mphy->dev->lpi_bcn_enhance)
78+ else if (mphy->dev->lpi_mode && mphy->dev->lpi_bcn_enhance)
79 rate = FR_RATE_IDX_OFDM_6M;
80
81 /* odd index for driver, even index for firmware */
82diff --git a/mt7996/mcu.c b/mt7996/mcu.c
developerd0c89452024-10-11 16:53:27 +080083index 265f39a6..725f2f31 100644
developer05f3b2b2024-08-19 19:17:34 +080084--- a/mt7996/mcu.c
85+++ b/mt7996/mcu.c
developerd0c89452024-10-11 16:53:27 +080086@@ -6384,7 +6384,8 @@ int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx,
developer05f3b2b2024-08-19 19:17:34 +080087
88 if (beacon) {
89 req.spe_idx_sel = SPE_IXD_SELECT_TXD;
90- req.spe_idx = dev->mt76.mgmt_pwr_enhance ? 0 : 24 + band_idx;
91+ req.spe_idx = dev->mt76.lpi_mode && dev->mt76.mgmt_pwr_enhance ?
92+ 0 : 24 + band_idx;
93 phy->beacon_rate = rate_idx;
94 } else {
95 req.spe_idx_sel = SPE_IXD_SELECT_BMC_WTBL;
developerd0c89452024-10-11 16:53:27 +080096@@ -6667,6 +6668,126 @@ mt7996_update_max_txpower_cur(struct mt7996_phy *phy, int tx_power)
developer05f3b2b2024-08-19 19:17:34 +080097 mphy->txpower_cur = e2p_power_limit;
98 }
99
100+static int mt7996_afc_update_power_limit(struct mt7996_dev *dev,
101+ struct ieee80211_channel *chan,
102+ struct mt76_power_limits *la,
103+ struct mt76_power_path_limits *la_path,
104+ int *tx_power,
105+ struct cfg80211_chan_def *chandef)
106+{
107+ s8 *power_list, bw320_offset, target_power;
108+ int table_idx, i, bw, mcs, ru, eht, path;
109+ s8 bf_offset_ofdm[] = {6, 10, 12, 14};
110+ s8 bf_offset[] = {0, 6, 10, 12, 14, 0, 4, 6, 8, 0, 3, 5, 0, 2, 0};
111+
112+ table_idx = chan->hw_value / 4;
113+ if (table_idx < 0 || table_idx > MAX_CHANNEL_NUM_6G ||
114+ !dev->mt76.afc_power_table[table_idx])
115+ return -EINVAL;
116+
117+ power_list = dev->mt76.afc_power_table[table_idx];
118+
119+ switch (chan->center_freq) {
120+ case 31:
121+ case 95:
122+ case 159:
123+ bw320_offset = 1;
124+ break;
125+ case 63:
126+ case 127:
127+ case 191:
128+ bw320_offset = 2;
129+ break;
130+ default:
131+ bw320_offset = 0;
132+ break;
133+ }
134+
135+ if (chandef) {
136+ switch (chandef->width) {
137+ case NL80211_CHAN_WIDTH_20:
138+ target_power = power_list[afc_power_bw20];
139+ break;
140+ case NL80211_CHAN_WIDTH_40:
141+ target_power = power_list[afc_power_bw40];
142+ break;
143+ case NL80211_CHAN_WIDTH_80:
144+ target_power = power_list[afc_power_bw80];
145+ break;
146+ case NL80211_CHAN_WIDTH_160:
147+ target_power = power_list[afc_power_bw160];
148+ break;
149+ case NL80211_CHAN_WIDTH_320:
150+ if (bw320_offset == 1)
151+ target_power = power_list[afc_power_bw320_1];
152+ else
153+ target_power = power_list[afc_power_bw320_2];
154+ break;
155+ default:
156+ break;
157+ }
158+ *tx_power = min_t(int, *tx_power, target_power);
159+ }
160+
161+ target_power = min_t(s8, (s8)*tx_power, power_list[afc_power_bw20]);
162+ for (i = 0; i < sizeof(la->cck); i++)
163+ la->cck[i] = min_t(s8, la->cck[i], power_list[afc_power_bw20]);
164+ for (i = 0; i < sizeof(la->ofdm); i++)
165+ la->ofdm[i] = min_t(s8, la->ofdm[i], target_power);
166+
167+ for (i = 0; i < sizeof(la_path->cck); i++)
168+ la_path->cck[i] = min_t(s8, la_path->cck[i], power_list[afc_power_bw20]);
169+ for (i = 0; i < sizeof(la_path->ofdm); i++)
170+ la_path->ofdm[i] = min_t(s8, la_path->ofdm[i], target_power);
171+ for (i = 0; i < sizeof(la_path->ofdm_bf); i++) {
172+ la_path->ofdm_bf[i] =
173+ min_t(s8, la_path->ofdm_bf[i],
174+ target_power - bf_offset_ofdm[i]);
175+ }
176+
177+ for (bw = afc_power_bw20; bw < afc_power_table_num; bw++) {
178+ if ((bw == afc_power_bw320_1 && bw320_offset == 2) ||
179+ (bw == afc_power_bw320_2 && bw320_offset == 1))
180+ continue;
181+
182+ if (power_list[bw] == AFC_INVALID_POWER)
183+ continue;
184+
185+ /* Negative index means doesn't need to update powers of the type. */
186+ if (mt7996_get_bw_power_table_idx(bw, &mcs, &ru, &eht, &path))
187+ return -EINVAL;
188+
189+ if (mcs >= 0) {
190+ for (i = 0; i < sizeof(la->mcs[0]); i++)
191+ la->mcs[mcs][i] =
192+ min_t(s8, la->mcs[mcs][i], power_list[bw]);
193+ }
194+
195+ if (ru >= 0) {
196+ for (i = 0; i < sizeof(la->ru[0]); i++)
197+ la->ru[ru][i] = min_t(s8, la->ru[ru][i], power_list[bw]);
198+ }
199+
200+ if (eht >= 0) {
201+ for (i = 0; i < sizeof(la->eht[0]); i++)
202+ la->eht[eht][i] =
203+ min_t(s8, la->eht[eht][i], power_list[bw]);
204+ }
205+
206+ if (path >= 0) {
207+ for (i = 0; i < sizeof(la_path->ru[0]); i++) {
208+ la_path->ru[path][i] =
209+ min_t(s8, la_path->ru[path][i], power_list[bw]);
210+ la_path->ru_bf[path][i] =
211+ min_t(s8, la_path->ru_bf[path][i],
212+ power_list[bw] - bf_offset[i]);
213+ }
214+ }
215+ }
216+
217+ return 0;
218+}
219+
220 bool mt7996_is_psd_country(char *country)
221 {
222 char psd_country_list[][3] = {"US", "KR", "BR", "CL", "MY", ""};
developerd0c89452024-10-11 16:53:27 +0800223@@ -6714,15 +6835,22 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
developer05f3b2b2024-08-19 19:17:34 +0800224 txpower_setting = 127;
225 txpower_limit = mt7996_get_power_bound(phy, txpower_setting);
226
227- if (phy->sku_limit_en) {
228- txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
229- &la, &la_path, txpower_limit);
230- mt7996_update_max_txpower_cur(phy, txpower_limit);
231- } else {
232+ if (!phy->sku_limit_en) {
233 mt7996_update_max_txpower_cur(phy, txpower_limit);
234 return 0;
235 }
236
237+ txpower_limit = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
238+ &la, &la_path, txpower_limit);
239+ if(phy->mt76->cap.has_6ghz && dev->mt76.afc_power_table) {
240+ ret = mt7996_afc_update_power_limit(dev, mphy->main_chan, &la, &la_path,
241+ &txpower_limit, &mphy->chandef);
242+ if (ret)
243+ return ret;
244+ }
245+
246+ mt7996_update_max_txpower_cur(phy, txpower_limit);
247+
248 skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
249 sizeof(req) + MT7996_SKU_PATH_NUM);
250 if (!skb)
developerd0c89452024-10-11 16:53:27 +0800251@@ -6735,7 +6863,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
developer05f3b2b2024-08-19 19:17:34 +0800252 /* FW would compensate for PSD countries
253 * driver doesn't need to do it
254 */
255- if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_psd &&
256+ if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_mode && mphy->dev->lpi_psd &&
257 !mt7996_is_psd_country(dev->mt76.alpha2)) {
258 switch (mphy->chandef.width) {
259 case NL80211_CHAN_WIDTH_20:
developerd0c89452024-10-11 16:53:27 +0800260@@ -6801,7 +6929,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
developer05f3b2b2024-08-19 19:17:34 +0800261 /* FW would NOT compensate in the case of BF backoff table
262 * driver needs to compensate for LPI PSD
263 */
264- if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_psd) {
265+ if (phy->mt76->cap.has_6ghz && mphy->dev->lpi_mode && mphy->dev->lpi_psd) {
266 switch (mphy->chandef.width) {
267 case NL80211_CHAN_WIDTH_20:
268 skb_put_data(skb, &la_path.ru[5], sizeof(la_path.ofdm));
developerd0c89452024-10-11 16:53:27 +0800269@@ -6863,13 +6991,53 @@ int mt7996_mcu_set_lpi_psd(struct mt7996_phy *phy, u8 enable)
developer05f3b2b2024-08-19 19:17:34 +0800270 .tag = cpu_to_le16(UNI_BAND_CONFIG_LPI_CTRL),
271 .len = cpu_to_le16(sizeof(req) - 4),
272 .lpi_enable = enable,
273- .psd_limit = enable ? mt7996_is_psd_country(dev->mt76.alpha2) : 0,
274+ .psd_limit = enable && dev->mt76.lpi_mode ?
275+ mt7996_is_psd_country(dev->mt76.alpha2) : 0,
276 };
277
278 return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
279 &req, sizeof(req), false);
280 }
281
282+int mt7996_alloc_afc_table(struct mt7996_dev *dev)
283+{
284+ struct mt76_dev *mdev = &dev->mt76;
285+ int i;
286+
287+ mdev->afc_power_table =
288+ (s8**)devm_kzalloc(dev->mt76.dev,
289+ MAX_CHANNEL_NUM_6G * sizeof(s8*),
290+ GFP_KERNEL);
291+
292+ if (!mdev->afc_power_table)
293+ return -ENOMEM;
294+
295+ for (i = 0; i < MAX_CHANNEL_NUM_6G; i++) {
296+ mdev->afc_power_table[i] =
297+ (s8*)devm_kzalloc(dev->mt76.dev,
298+ afc_power_table_num * sizeof(s8),
299+ GFP_KERNEL);
300+ if (!mdev->afc_power_table[i]) {
301+ mt7996_free_afc_table(dev);
302+ return -ENOMEM;
303+ }
304+ }
305+ return 0;
306+}
307+
308+void mt7996_free_afc_table(struct mt7996_dev *dev)
309+{
310+ struct mt76_dev *mdev = &dev->mt76;
311+ int i;
312+
313+ if (mdev->afc_power_table) {
314+ for (i = 0; i < MAX_CHANNEL_NUM_6G; i++)
315+ devm_kfree(mdev->dev, mdev->afc_power_table[i]);
316+ devm_kfree(mdev->dev, mdev->afc_power_table);
317+ }
318+ mdev->afc_power_table = NULL;
319+}
320+
321 int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode)
322 {
323 __le32 cp_mode;
324diff --git a/mt7996/mcu.h b/mt7996/mcu.h
325index dab4700e..33ba3774 100644
326--- a/mt7996/mcu.h
327+++ b/mt7996/mcu.h
328@@ -1295,6 +1295,145 @@ enum {
329 UNI_TXPOWER_SHOW_INFO = 7,
330 };
331
332+#define MAX_CHANNEL_NUM_6G 59
333+#define AFC_INVALID_POWER 127
334+enum afc_table_info {
335+ afc_power_bw20,
336+ afc_power_bw40,
337+ afc_power_bw80,
338+ afc_power_bw160,
339+ afc_power_bw320_1,
340+ afc_power_bw320_2,
341+ afc_power_ru26,
342+ afc_power_ru52,
343+ afc_power_ru78,
344+ afc_power_ru106,
345+ afc_power_ru132,
346+ afc_power_ru726,
347+ afc_power_ru1480,
348+ afc_power_ru1772,
349+ afc_power_ru2476,
350+ afc_power_ru2988,
351+ afc_power_ru3472,
352+ afc_power_table_num,
353+};
354+
355+static inline int mt7996_get_bw_power_table_idx(int bw, int *mcs, int *ru, int *eht,
356+ int *path)
357+{
358+ switch (bw) {
359+ case afc_power_bw20:
360+ *mcs = 0;
361+ *ru = 3;
362+ *eht = 3;
363+ *path = 5;
364+ break;
365+ case afc_power_bw40:
366+ *mcs = 1;
367+ *ru = 4;
368+ *eht = 4;
369+ *path = 6;
370+ break;
371+ case afc_power_bw80:
372+ *mcs = 2;
373+ *ru = 5;
374+ *eht = 5;
375+ *path = 8;
376+ break;
377+ case afc_power_bw160:
378+ *mcs = 3;
379+ *ru = 6;
380+ *eht = 6;
381+ *path = 11;
382+ break;
383+ case afc_power_bw320_1:
384+ *mcs = -1;
385+ *ru = -1;
386+ *eht = 7;
387+ *path = 15;
388+ break;
389+ case afc_power_bw320_2:
390+ *mcs = -1;
391+ *ru = -1;
392+ *eht = 7;
393+ *path = 15;
394+ break;
395+ case afc_power_ru26:
396+ *mcs = -1;
397+ *ru = 0;
398+ *eht = 0;
399+ *path = 0;
400+ break;
401+ case afc_power_ru52:
402+ *mcs = -1;
403+ *ru = 1;
404+ *eht = 1;
405+ *path = 1;
406+ break;
407+ case afc_power_ru78:
408+ *mcs = -1;
409+ *ru = -1;
410+ *eht = 8;
411+ *path = 2;
412+ break;
413+ case afc_power_ru106:
414+ *mcs = -1;
415+ *ru = 2;
416+ *eht = 2;
417+ *path = 3;
418+ break;
419+ case afc_power_ru132:
420+ *mcs = -1;
421+ *ru = -1;
422+ *eht = 9;
423+ *path = 4;
424+ break;
425+ case afc_power_ru726:
426+ *mcs = -1;
427+ *ru = -1;
428+ *eht = 10;
429+ *path = 7;
430+ break;
431+ case afc_power_ru1480:
432+ *mcs = -1;
433+ *ru = -1;
434+ *eht = 11;
435+ *path = 9;
436+ break;
437+ case afc_power_ru1772:
438+ *mcs = -1;
439+ *ru = -1;
440+ *eht = 12;
441+ *path = 10;
442+ break;
443+ case afc_power_ru2476:
444+ *mcs = -1;
445+ *ru = -1;
446+ *eht = 13;
447+ *path = 12;
448+ break;
449+ case afc_power_ru2988:
450+ *mcs = -1;
451+ *ru = -1;
452+ *eht = 14;
453+ *path = 13;
454+ break;
455+ case afc_power_ru3472:
456+ *mcs = -1;
457+ *ru = -1;
458+ *eht = 15;
459+ *path = 14;
460+ break;
461+ default:
462+ *mcs = -1;
463+ *ru = -1;
464+ *eht = -1;
465+ *path = -1;
466+ return -EINVAL;
467+ }
468+ return 0;
469+}
470+
471 enum {
472 UNI_CMD_ACCESS_REG_BASIC = 0x0,
473 UNI_CMD_ACCESS_RF_REG_BASIC,
474diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
developerd0c89452024-10-11 16:53:27 +0800475index 1e52377a..b841cc2a 100644
developer05f3b2b2024-08-19 19:17:34 +0800476--- a/mt7996/mt7996.h
477+++ b/mt7996/mt7996.h
developerd0c89452024-10-11 16:53:27 +0800478@@ -1174,6 +1174,8 @@ int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
developer05f3b2b2024-08-19 19:17:34 +0800479 int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
480 int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy,
481 int txpower_setting);
482+int mt7996_alloc_afc_table(struct mt7996_dev *dev);
483+void mt7996_free_afc_table(struct mt7996_dev *dev);
484 int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
485 u8 rx_sel, u8 val);
486 int mt7996_mcu_rdd_background_disable_timer(struct mt7996_dev *dev,
487diff --git a/mt7996/mtk_debugfs.c b/mt7996/mtk_debugfs.c
developerd0c89452024-10-11 16:53:27 +0800488index 96a4a514..5338d0bd 100644
developer05f3b2b2024-08-19 19:17:34 +0800489--- a/mt7996/mtk_debugfs.c
490+++ b/mt7996/mtk_debugfs.c
developerd0c89452024-10-11 16:53:27 +0800491@@ -2522,6 +2522,11 @@ mt7996_get_txpower_info(struct file *file, char __user *user_buf,
developer05f3b2b2024-08-19 19:17:34 +0800492 len += scnprintf(buf + len, size - len,
493 " RegDB: %s\n",
494 !np ? "enable" : "disable");
495+ len += scnprintf(buf + len, size - len,
496+ " sku_index: %d\n", phy->mt76->sku_idx);
497+ len += scnprintf(buf + len, size - len,
498+ " lpi: %s\n",
499+ phy->mt76->dev->lpi_mode ? "enable" : "disable");
500 ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
501
502 out:
developerd0c89452024-10-11 16:53:27 +0800503@@ -4389,6 +4394,37 @@ static int mt7996_pp_alg_show(struct seq_file *s, void *data)
developer05f3b2b2024-08-19 19:17:34 +0800504 }
505 DEFINE_SHOW_ATTRIBUTE(mt7996_pp_alg);
506
507+static int mt7996_afc_table_show(struct seq_file *s, void *data)
508+{
509+ struct mt7996_dev *dev = s->private;
510+
511+ char str[200] = {0}, *pos;
512+ char *end = str + sizeof(str);
513+ int i, j;
514+
515+ if (!dev->mt76.afc_power_table || !dev->mt76.afc_power_table[0]) {
516+ seq_printf(s, "afc table doesn't exist.\n");
517+ return 0;
518+ }
519+
520+ seq_printf(s, "bw/ru : 20 40 80 160 320-1 320-2 26 52 78 "
521+ "106 132 726 1480 1772 2476 2988 3472\n");
522+ for(i = 0; i < MAX_CHANNEL_NUM_6G; i ++) {
523+ pos = str;
524+ for (j = 0; j < afc_power_table_num; j ++) {
525+ pos += snprintf(pos, end - pos, "%5d ",
526+ dev->mt76.afc_power_table[i][j]);
527+ }
528+ seq_printf(s, "ch %3d: %s\n", i * 4 + 1, str);
529+ memset(str, 0, sizeof(str));
530+ }
531+ seq_printf(s, "Unit : 0.5 dBm\n");
532+ seq_printf(s, "NOTE : power of the table is translated to single path.\n");
533+
534+ return 0;
535+}
536+DEFINE_SHOW_ATTRIBUTE(mt7996_afc_table);
537+
538 void mt7996_mtk_init_band_debugfs(struct mt7996_phy *phy, struct dentry *dir)
539 {
540 /* agg */
developerd0c89452024-10-11 16:53:27 +0800541@@ -4515,6 +4551,7 @@ void mt7996_mtk_init_dev_debugfs(struct mt7996_dev *dev, struct dentry *dir)
developer05f3b2b2024-08-19 19:17:34 +0800542
543 debugfs_create_file("muru_dbg", 0200, dir, dev, &fops_muru_dbg_info);
544 debugfs_create_bool("mgmt_pwr_enhance", 0600, dir, &dev->mt76.mgmt_pwr_enhance);
545+ debugfs_create_file("afc_table", 0200, dir, dev, &mt7996_afc_table_fops);
546 }
547
548 #endif
549diff --git a/mt7996/vendor.c b/mt7996/vendor.c
550index e13a148a..867c277d 100644
551--- a/mt7996/vendor.c
552+++ b/mt7996/vendor.c
553@@ -162,6 +162,8 @@ txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = {
554 [MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX] = { .type = NLA_U8 },
555 [MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE] = { .type = NLA_U8 },
556 [MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID] = { .type = NLA_U8 },
557+ [MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE] = { .type = NLA_BINARY },
558+ [MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI] = { .type = NLA_U8 },
559 };
560
561 struct mt7996_amnt_data {
562@@ -1454,6 +1456,34 @@ out:
563 return err;
564 }
565
566+static int mt7996_parse_afc_table(struct mt7996_dev *dev, struct nlattr *tb, int delta)
567+{
568+ int ch, bw, err = 0;
569+ struct mt76_dev *mdev = &dev->mt76;
570+ s8 **table;
571+
572+ if (!mdev->afc_power_table)
573+ err = mt7996_alloc_afc_table(dev);
574+
575+ if (err) {
576+ mt7996_free_afc_table(dev);
577+ return err;
578+ }
579+
580+ table = nla_data(tb);
581+
582+ for (ch = 0; ch < MAX_CHANNEL_NUM_6G; ch++) {
583+ memcpy(mdev->afc_power_table[ch], table[ch],
584+ afc_power_table_num * sizeof(s8));
585+ for (bw = 0; bw < afc_power_table_num; bw++)
586+ if (mdev->afc_power_table[ch][bw] != AFC_INVALID_POWER)
587+ mdev->afc_power_table[ch][bw] -= delta;
588+ }
589+
590+ return 0;
591+}
592+
593+
594 static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
595 struct wireless_dev *wdev,
596 const void *data,
597@@ -1463,12 +1493,13 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
598 struct mt7996_dev *dev;
599 struct mt7996_phy *phy;
600 struct mt76_phy *mphy;
601+ struct mt76_dev *mdev;
602 struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
603 struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
604 struct mt7996_bss_conf *mconf;
605- struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL];
606- struct mt76_power_limits la = {};
607- struct mt76_power_path_limits la_path = {};
608+ struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL], *table;
609+ struct mt76_power_limits *la;
610+ struct mt76_power_path_limits *la_path;
611 int err, current_txpower, delta;
612 u8 val, link_id = 0, idx;
613
614@@ -1496,6 +1527,14 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
615 rcu_read_unlock();
616
617 mphy = phy->mt76;
618+ mdev = mphy->dev;
619+ delta = mt76_tx_power_nss_delta(hweight16(mphy->chainmask));
620+
621+ if (mphy->cap.has_6ghz &&
622+ tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI]) {
623+ val = nla_get_u8(tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI]);
624+ mphy->dev->lpi_mode = !!val;
625+ }
626
627 if (mphy->cap.has_6ghz &&
628 tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD]) {
629@@ -1504,7 +1543,7 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
630
631 err = mt7996_mcu_set_lpi_psd(phy, val);
632 if (err)
633- return err;
634+ goto out;
635 }
636
637 if (tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX]) {
638@@ -1515,19 +1554,22 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
639
640 phy->sku_limit_en = true;
641 phy->sku_path_en = true;
642- mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &la, &la_path, 127);
643- if (!la_path.ofdm[0])
644+ la = kzalloc(sizeof(struct mt76_power_limits), GFP_KERNEL);
645+ la_path = kzalloc(sizeof(struct mt76_power_path_limits), GFP_KERNEL);
646+
647+ mt76_get_rate_power_limits(mphy, mphy->chandef.chan, la, la_path, 127);
648+ if (!la_path->ofdm[0])
649 phy->sku_path_en = false;
650
651 dev = phy->dev;
652 err = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_SKU_POWER_LIMIT_CTRL,
653 dev->dbg.sku_disable ? 0 : phy->sku_limit_en);
654 if (err)
655- return err;
656+ goto out;
657 err = mt7996_mcu_set_tx_power_ctrl(phy, UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL,
658 dev->dbg.sku_disable ? 0 : phy->sku_path_en);
659 if (err)
660- return err;
661+ goto out;
662 }
663
664 if (mphy->cap.has_6ghz &&
665@@ -1536,19 +1578,30 @@ static int mt7996_vendor_txpower_ctrl(struct wiphy *wiphy,
666 mphy->dev->lpi_bcn_enhance = val;
667 idx = MT7996_BEACON_RATES_TBL + 2 * phy->mt76->band_idx;
668
669- err = mt7996_mcu_set_fixed_rate_table(phy, idx, FR_RATE_IDX_OFDM_6M, true);
670+ err = mt7996_mcu_set_fixed_rate_table(phy, idx, FR_RATE_IDX_OFDM_6M,
671+ true);
672 if (err)
673- return err;
674+ goto out;
675+ }
676+
677+ if (mphy->cap.has_6ghz) {
678+ table = tb[MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE];
679+ if (table) {
680+ err = mt7996_parse_afc_table(dev, table, delta);
681+ if (err)
682+ goto out;
683+ } else
684+ mt7996_free_afc_table(dev);
685 }
686
687- delta = mt76_tx_power_nss_delta(hweight16(mphy->chainmask));
688 current_txpower = DIV_ROUND_UP(mphy->txpower_cur + delta, 2);
689
690 err = mt7996_mcu_set_txpower_sku(phy, current_txpower);
691- if (err)
692- return err;
693
694- return 0;
695+out:
696+ kfree(la);
697+ kfree(la_path);
698+ return err;
699 }
700
701 static const struct wiphy_vendor_command mt7996_vendor_commands[] = {
702diff --git a/mt7996/vendor.h b/mt7996/vendor.h
703index 71800590..cd84a492 100644
704--- a/mt7996/vendor.h
705+++ b/mt7996/vendor.h
706@@ -338,12 +338,15 @@ enum mtk_vendor_attr_txpower_ctrl {
707 MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX,
708 MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE,
709 MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID,
710+ MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE,
711+ MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI,
712
713 /* keep last */
714 NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL,
715 MTK_VENDOR_ATTR_TXPOWER_CTRL_MAX =
716 NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL - 1
717 };
718+
719 #endif
720
721 #endif
722--
developerd0c89452024-10-11 16:53:27 +08007232.45.2
developer05f3b2b2024-08-19 19:17:34 +0800724