From: Felix Fietkau Date: Mon, 20 Oct 2025 14:10:08 +0000 (+0200) Subject: mt76: update to Git HEAD (2025-10-20) X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=13c30c2e58b02427f6071a3674dbaf0e03d9c792;p=openwrt%2Fstaging%2Fnbd.git mt76: update to Git HEAD (2025-10-20) 9050597c4cd6 wifi: mt76: fix license/copyright of util.h 33f29d2345cc wifi: mt76: relicense to BSD-3-Clause-Clear fc25ba82192e add LICENSE file c1349a686cd1 wifi: mt76: mt7921: fix a potential clc buffer length underflow 4e7077f77400 wifi: mt76: mt7996: fix FCS error flag check in RX descriptor 754e3357045d wifi: mt76: mt7996: Remove unnecessary link_id checks in mt7996_tx 2e63c495f59b wifi: mt76: connac: Replace memcpy + hard-coded size with strscpy 95c4df3233ed wifi: mt76: Fix DTS power-limits on little endian systems 6f27d647c0ce wifi: mt76: mt7915: add bf backoff limit table support 1e91eadc9d46 wifi: mt76: wed: use proper wed reference in mt76 wed driver callabacks 2fe5bdbe7e13 wifi: mt76: use GFP_DMA32 for page_pool buffer allocation c63db0fcadb8 wifi: mt76: mt7915: add WDS support when WED is enabled Signed-off-by: Felix Fietkau --- diff --git a/package/kernel/mt76/Makefile b/package/kernel/mt76/Makefile index 98a768b3a4..82be30c474 100644 --- a/package/kernel/mt76/Makefile +++ b/package/kernel/mt76/Makefile @@ -3,14 +3,14 @@ include $(TOPDIR)/rules.mk PKG_NAME:=mt76 PKG_RELEASE=1 -PKG_LICENSE:=GPLv2 +PKG_LICENSE:=BSD-3-Clause-Clear PKG_LICENSE_FILES:= PKG_SOURCE_URL:=https://github.com/openwrt/mt76 PKG_SOURCE_PROTO:=git -PKG_SOURCE_DATE:=2025-10-03 -PKG_SOURCE_VERSION:=ec3f05480e5b02a39bac93b76253d56b2a19bdb6 -PKG_MIRROR_HASH:=c6469e91204793d0a859b78ab3baa1eb33a97458228a2849451baabb31e6499b +PKG_SOURCE_DATE:=2025-10-20 +PKG_SOURCE_VERSION:=c63db0fcadb88680b35bec202b5142cfd016c10f +PKG_MIRROR_HASH:=38c3c84f5c58b6967283acf524412f6e13628d50add6f09d539f1239fb02b486 PKG_MAINTAINER:=Felix Fietkau PKG_USE_NINJA:=0 diff --git a/package/kernel/mt76/patches/0001-wifi-mt76-Fix-DTS-power-limits-on-little-endian-syst.patch b/package/kernel/mt76/patches/0001-wifi-mt76-Fix-DTS-power-limits-on-little-endian-syst.patch deleted file mode 100644 index bd21a23e33..0000000000 --- a/package/kernel/mt76/patches/0001-wifi-mt76-Fix-DTS-power-limits-on-little-endian-syst.patch +++ /dev/null @@ -1,125 +0,0 @@ -From: Sven Eckelmann (Plasma Cloud) -Date: Mon, 30 Jun 2025 16:18:21 +0200 -Subject: wifi: mt76: Fix DTS power-limits on little endian systems - -The power-limits for ru and mcs and stored in the devicetree as bytewise -array (often with sizes which are not a multiple of 4). These arrays have a -prefix which defines for how many modes a line is applied. This prefix is -also only a byte - but the code still tried to fix the endianness of this -byte with a be32 operation. As result, loading was mostly failing or was -sending completely unexpected values to the firmware. - -Since the other rates are also stored in the devicetree as bytewise arrays, -just drop the u32 access + be32_to_cpu conversion and directly access them -as bytes arrays. - -Cc: stable@vger.kernel.org -Fixes: 22b980badc0f ("mt76: add functions for parsing rate power limits from DT") -Fixes: a9627d992b5e ("mt76: extend DT rate power limits to support 11ax devices") -Signed-off-by: Sven Eckelmann (Plasma Cloud) -Forwarded: https://lore.kernel.org/r/20250926-fix-power-limits-v2-1-c2bc7881eb6d@simonwunderlich.de - ---- a/eeprom.c -+++ b/eeprom.c -@@ -273,6 +273,19 @@ mt76_get_of_array(struct device_node *np - return prop->value; - } - -+static const s8 * -+mt76_get_of_array_s8(struct device_node *np, char *name, size_t *len, int min) -+{ -+ struct property *prop = of_find_property(np, name, NULL); -+ -+ if (!prop || !prop->value || prop->length < min) -+ return NULL; -+ -+ *len = prop->length; -+ -+ return prop->value; -+} -+ - struct device_node * - mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan) - { -@@ -314,7 +327,7 @@ mt76_get_txs_delta(struct device_node *n - } - - static void --mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data, -+mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const s8 *data, - s8 target_power, s8 nss_delta, s8 *max_power) - { - int i; -@@ -323,15 +336,14 @@ mt76_apply_array_limit(s8 *pwr, size_t p - return; - - for (i = 0; i < pwr_len; i++) { -- pwr[i] = min_t(s8, target_power, -- be32_to_cpu(data[i]) + nss_delta); -+ pwr[i] = min_t(s8, target_power, data[i] + nss_delta); - *max_power = max(*max_power, pwr[i]); - } - } - - static void - mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num, -- const __be32 *data, size_t len, s8 target_power, -+ const s8 *data, size_t len, s8 target_power, - s8 nss_delta, s8 *max_power) - { - int i, cur; -@@ -339,8 +351,7 @@ mt76_apply_multi_array_limit(s8 *pwr, si - if (!data) - return; - -- len /= 4; -- cur = be32_to_cpu(data[0]); -+ cur = data[0]; - for (i = 0; i < pwr_num; i++) { - if (len < pwr_len + 1) - break; -@@ -355,7 +366,7 @@ mt76_apply_multi_array_limit(s8 *pwr, si - if (!len) - break; - -- cur = be32_to_cpu(data[0]); -+ cur = data[0]; - } - } - -@@ -366,7 +377,7 @@ s8 mt76_get_rate_power_limits(struct mt7 - { - struct mt76_dev *dev = phy->dev; - struct device_node *np; -- const __be32 *val; -+ const s8 *val; - char name[16]; - u32 mcs_rates = dev->drv->mcs_rates; - u32 ru_rates = ARRAY_SIZE(dest->ru[0]); -@@ -412,21 +423,21 @@ s8 mt76_get_rate_power_limits(struct mt7 - - txs_delta = mt76_get_txs_delta(np, hweight16(phy->chainmask)); - -- val = mt76_get_of_array(np, "rates-cck", &len, ARRAY_SIZE(dest->cck)); -+ val = mt76_get_of_array_s8(np, "rates-cck", &len, ARRAY_SIZE(dest->cck)); - mt76_apply_array_limit(dest->cck, ARRAY_SIZE(dest->cck), val, - target_power, txs_delta, &max_power); - -- val = mt76_get_of_array(np, "rates-ofdm", -- &len, ARRAY_SIZE(dest->ofdm)); -+ val = mt76_get_of_array_s8(np, "rates-ofdm", -+ &len, ARRAY_SIZE(dest->ofdm)); - mt76_apply_array_limit(dest->ofdm, ARRAY_SIZE(dest->ofdm), val, - target_power, txs_delta, &max_power); - -- val = mt76_get_of_array(np, "rates-mcs", &len, mcs_rates + 1); -+ val = mt76_get_of_array_s8(np, "rates-mcs", &len, mcs_rates + 1); - mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]), - ARRAY_SIZE(dest->mcs), val, len, - target_power, txs_delta, &max_power); - -- val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1); -+ val = mt76_get_of_array_s8(np, "rates-ru", &len, ru_rates + 1); - mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]), - ARRAY_SIZE(dest->ru), val, len, - target_power, txs_delta, &max_power); diff --git a/package/kernel/mt76/patches/0002-wifi-mt76-mt7915-add-bf-backoff-limit-table-support.patch b/package/kernel/mt76/patches/0002-wifi-mt76-mt7915-add-bf-backoff-limit-table-support.patch deleted file mode 100644 index 029936cbf7..0000000000 --- a/package/kernel/mt76/patches/0002-wifi-mt76-mt7915-add-bf-backoff-limit-table-support.patch +++ /dev/null @@ -1,576 +0,0 @@ -From: Shayne Chen -Date: Mon, 5 Dec 2022 18:21:51 +0800 -Subject: wifi: mt76: mt7915: add bf backoff limit table support - -The commit 22b980badc0f ("mt76: add functions for parsing rate power limits -from DT") introduced generic support for rates limits in the devicetree. -But the mt7915 supports beamforming and has another table for configuring -the backoff limits. These can be configured in the DT with the paths-* -properties. The path-*-bf are the ones relevant for beamforming and the -ones without -bf suffix for "traditional" path backoff. - -Signed-off-by: Shayne Chen -Signed-off-by: Sven Eckelmann (Plasma Cloud) -Forwarded: https://lore.kernel.org/r/20250924-backoff-table-support-v1-3-20e50fbc59de@simonwunderlich.de - ---- a/debugfs.c -+++ b/debugfs.c -@@ -93,9 +93,9 @@ void mt76_seq_puts_array(struct seq_file - { - int i; - -- seq_printf(file, "%10s:", str); -+ seq_printf(file, "%16s:", str); - for (i = 0; i < len; i++) -- seq_printf(file, " %2d", val[i]); -+ seq_printf(file, " %4d", val[i]); - seq_puts(file, "\n"); - } - EXPORT_SYMBOL_GPL(mt76_seq_puts_array); ---- a/eeprom.c -+++ b/eeprom.c -@@ -344,9 +344,10 @@ mt76_apply_array_limit(s8 *pwr, size_t p - static void - mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num, - const s8 *data, size_t len, s8 target_power, -- s8 nss_delta, s8 *max_power) -+ s8 nss_delta) - { - int i, cur; -+ s8 max_power = -128; - - if (!data) - return; -@@ -357,7 +358,7 @@ mt76_apply_multi_array_limit(s8 *pwr, si - break; - - mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1, -- target_power, nss_delta, max_power); -+ target_power, nss_delta, &max_power); - if (--cur > 0) - continue; - -@@ -384,12 +385,16 @@ s8 mt76_get_rate_power_limits(struct mt7 - char band; - size_t len; - s8 max_power = 0; -+ s8 max_power_backoff = -127; - s8 txs_delta; -+ int n_chains = hweight16(phy->chainmask); -+ s8 target_power_combine = target_power + mt76_tx_power_path_delta(n_chains); - - if (!mcs_rates) - mcs_rates = 10; - -- memset(dest, target_power, sizeof(*dest)); -+ memset(dest, target_power, sizeof(*dest) - sizeof(dest->path)); -+ memset(&dest->path, 0, sizeof(dest->path)); - - if (!IS_ENABLED(CONFIG_OF)) - return target_power; -@@ -435,12 +440,35 @@ s8 mt76_get_rate_power_limits(struct mt7 - val = mt76_get_of_array_s8(np, "rates-mcs", &len, mcs_rates + 1); - mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]), - ARRAY_SIZE(dest->mcs), val, len, -- target_power, txs_delta, &max_power); -+ target_power, txs_delta); - - val = mt76_get_of_array_s8(np, "rates-ru", &len, ru_rates + 1); - mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]), - ARRAY_SIZE(dest->ru), val, len, -- target_power, txs_delta, &max_power); -+ target_power, txs_delta); -+ -+ max_power_backoff = max_power; -+ val = mt76_get_of_array_s8(np, "paths-cck", &len, ARRAY_SIZE(dest->path.cck)); -+ mt76_apply_array_limit(dest->path.cck, ARRAY_SIZE(dest->path.cck), val, -+ target_power_combine, txs_delta, &max_power_backoff); -+ -+ val = mt76_get_of_array_s8(np, "paths-ofdm", &len, ARRAY_SIZE(dest->path.ofdm)); -+ mt76_apply_array_limit(dest->path.ofdm, ARRAY_SIZE(dest->path.ofdm), val, -+ target_power_combine, txs_delta, &max_power_backoff); -+ -+ val = mt76_get_of_array_s8(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest->path.ofdm_bf)); -+ mt76_apply_array_limit(dest->path.ofdm_bf, ARRAY_SIZE(dest->path.ofdm_bf), val, -+ target_power_combine, txs_delta, &max_power_backoff); -+ -+ val = mt76_get_of_array_s8(np, "paths-ru", &len, ARRAY_SIZE(dest->path.ru[0]) + 1); -+ mt76_apply_multi_array_limit(dest->path.ru[0], ARRAY_SIZE(dest->path.ru[0]), -+ ARRAY_SIZE(dest->path.ru), val, len, -+ target_power_combine, txs_delta); -+ -+ val = mt76_get_of_array_s8(np, "paths-ru-bf", &len, ARRAY_SIZE(dest->path.ru_bf[0]) + 1); -+ mt76_apply_multi_array_limit(dest->path.ru_bf[0], ARRAY_SIZE(dest->path.ru_bf[0]), -+ ARRAY_SIZE(dest->path.ru_bf), val, len, -+ target_power_combine, txs_delta); - - return max_power; - } ---- a/mt76.h -+++ b/mt76.h -@@ -1118,6 +1118,14 @@ struct mt76_power_limits { - s8 mcs[4][10]; - s8 ru[7][12]; - s8 eht[16][16]; -+ -+ struct { -+ s8 cck[4]; -+ s8 ofdm[4]; -+ s8 ofdm_bf[4]; -+ s8 ru[7][10]; -+ s8 ru_bf[7][10]; -+ } path; - }; - - struct mt76_ethtool_worker_info { ---- a/mt7915/debugfs.c -+++ b/mt7915/debugfs.c -@@ -1008,7 +1008,7 @@ mt7915_rate_txpower_get(struct file *fil - if (!buf) - return -ENOMEM; - -- ret = mt7915_mcu_get_txpower_sku(phy, txpwr, sizeof(txpwr)); -+ ret = mt7915_mcu_get_txpower_sku(phy, txpwr, sizeof(txpwr), TX_POWER_INFO_RATE); - if (ret) - goto out; - -@@ -1118,7 +1118,7 @@ mt7915_rate_txpower_set(struct file *fil - - mutex_lock(&dev->mt76.mutex); - ret = mt7915_mcu_get_txpower_sku(phy, req.txpower_sku, -- sizeof(req.txpower_sku)); -+ sizeof(req.txpower_sku), TX_POWER_INFO_RATE); - if (ret) - goto out; - -@@ -1160,7 +1160,7 @@ out: - return ret ? ret : count; - } - --static const struct file_operations mt7915_rate_txpower_fops = { -+static const struct file_operations mt7915_txpower_fops = { - .write = mt7915_rate_txpower_set, - .read = mt7915_rate_txpower_get, - .open = simple_open, -@@ -1169,6 +1169,70 @@ static const struct file_operations mt79 - }; - - static int -+mt7915_path_txpower_show(struct seq_file *file) -+{ -+ struct mt7915_phy *phy = file->private; -+ s8 txpower[MT7915_SKU_PATH_NUM], *buf = txpower; -+ int ret; -+ -+#define PATH_POWER_SHOW(_name, _len, _skip) do { \ -+ size_t __len = (_len); \ -+ if (_skip) { \ -+ buf -= 1; \ -+ *buf = 0; \ -+ } \ -+ mt76_seq_puts_array(file, _name, buf, __len); \ -+ buf += __len; \ -+ } while (0) -+ -+ seq_printf(file, "\n%*c", 18, ' '); -+ seq_puts(file, "1T1S/2T1S/3T1S/4T1S/2T2S/3T2S/4T2S/3T3S/4T3S/4T4S\n"); -+ ret = mt7915_mcu_get_txpower_sku(phy, txpower, sizeof(txpower), -+ TX_POWER_INFO_PATH); -+ if (ret) -+ return ret; -+ -+ PATH_POWER_SHOW("CCK", 4, 0); -+ PATH_POWER_SHOW("OFDM", 4, 0); -+ PATH_POWER_SHOW("BF-OFDM", 4, 1); -+ -+ PATH_POWER_SHOW("HT/VHT20", 10, 0); -+ PATH_POWER_SHOW("BF-HT/VHT20", 10, 1); -+ PATH_POWER_SHOW("HT/VHT40", 10, 0); -+ PATH_POWER_SHOW("BF-HT/VHT40", 10, 1); -+ -+ PATH_POWER_SHOW("BW20/RU242", 10, 0); -+ PATH_POWER_SHOW("BF-BW20/RU242", 10, 1); -+ PATH_POWER_SHOW("BW40/RU484", 10, 0); -+ PATH_POWER_SHOW("BF-BW40/RU484", 10, 1); -+ PATH_POWER_SHOW("BW80/RU996", 10, 0); -+ PATH_POWER_SHOW("BF-BW80/RU996", 10, 1); -+ PATH_POWER_SHOW("BW160/RU2x996", 10, 0); -+ PATH_POWER_SHOW("BF-BW160/RU2x996", 10, 1); -+ PATH_POWER_SHOW("RU26", 10, 0); -+ PATH_POWER_SHOW("BF-RU26", 10, 0); -+ PATH_POWER_SHOW("RU52", 10, 0); -+ PATH_POWER_SHOW("BF-RU52", 10, 0); -+ PATH_POWER_SHOW("RU106", 10, 0); -+ PATH_POWER_SHOW("BF-RU106", 10, 0); -+#undef PATH_POWER_SHOW -+ -+ return 0; -+} -+ -+static int -+mt7915_txpower_path_show(struct seq_file *file, void *data) -+{ -+ struct mt7915_phy *phy = file->private; -+ -+ seq_printf(file, "\nBand %d\n", phy != &phy->dev->phy); -+ -+ return mt7915_path_txpower_show(file); -+} -+ -+DEFINE_SHOW_ATTRIBUTE(mt7915_txpower_path); -+ -+static int - mt7915_twt_stats(struct seq_file *s, void *data) - { - struct mt7915_dev *dev = dev_get_drvdata(s->private); -@@ -1254,7 +1318,9 @@ int mt7915_init_debugfs(struct mt7915_ph - debugfs_create_file("implicit_txbf", 0600, dir, dev, - &fops_implicit_txbf); - debugfs_create_file("txpower_sku", 0400, dir, phy, -- &mt7915_rate_txpower_fops); -+ &mt7915_txpower_fops); -+ debugfs_create_file("txpower_path", 0400, dir, phy, -+ &mt7915_txpower_path_fops); - debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir, - mt7915_twt_stats); - debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval); ---- a/mt7915/init.c -+++ b/mt7915/init.c -@@ -289,6 +289,8 @@ static void __mt7915_init_txpower(struct - int pwr_delta = mt7915_eeprom_get_power_delta(dev, sband->band); - struct mt76_power_limits limits; - -+ phy->sku_limit_en = true; -+ phy->sku_path_en = true; - for (i = 0; i < sband->n_channels; i++) { - struct ieee80211_channel *chan = &sband->channels[i]; - u32 target_power = 0; -@@ -305,6 +307,11 @@ static void __mt7915_init_txpower(struct - target_power = mt76_get_rate_power_limits(phy->mt76, chan, - &limits, - target_power); -+ -+ /* MT7915N can not enable Backoff table without setting value in dts */ -+ if (!limits.path.ofdm[0]) -+ phy->sku_path_en = false; -+ - target_power += path_delta; - target_power = DIV_ROUND_UP(target_power, 2); - chan->max_power = min_t(int, chan->max_reg_power, ---- a/mt7915/main.c -+++ b/mt7915/main.c -@@ -73,7 +73,7 @@ int mt7915_run(struct ieee80211_hw *hw) - if (ret) - goto out; - -- ret = mt7915_mcu_set_sku_en(phy, true); -+ ret = mt7915_mcu_set_sku_en(phy); - if (ret) - goto out; - ---- a/mt7915/mcu.c -+++ b/mt7915/mcu.c -@@ -3336,7 +3336,8 @@ int mt7915_mcu_set_txpower_frame(struct - int ret; - s8 txpower_sku[MT7915_SKU_RATE_NUM]; - -- ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku)); -+ ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku), -+ TX_POWER_INFO_RATE); - if (ret) - return ret; - -@@ -3376,51 +3377,136 @@ int mt7915_mcu_set_txpower_frame(struct - sizeof(req), true); - } - -+static void -+mt7915_update_txpower(struct mt7915_phy *phy, int tx_power) -+{ -+ struct mt76_phy *mphy = phy->mt76; -+ struct ieee80211_channel *chan = mphy->main_chandef.chan; -+ int chain_idx, val, e2p_power_limit = 0; -+ -+ if (!chan) { -+ mphy->txpower_cur = tx_power; -+ return; -+ } -+ -+ for (chain_idx = 0; chain_idx < hweight16(mphy->chainmask); chain_idx++) { -+ val = mt7915_eeprom_get_target_power(phy->dev, chan, chain_idx); -+ val += mt7915_eeprom_get_power_delta(phy->dev, chan->band); -+ -+ e2p_power_limit = max_t(int, e2p_power_limit, val); -+ } -+ -+ if (phy->sku_limit_en) -+ mphy->txpower_cur = min_t(int, e2p_power_limit, tx_power); -+ else -+ mphy->txpower_cur = e2p_power_limit; -+} -+ - int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) - { -+#define TX_POWER_LIMIT_TABLE_RATE 0 -+#define TX_POWER_LIMIT_TABLE_PATH 1 - struct mt7915_dev *dev = phy->dev; - struct mt76_phy *mphy = phy->mt76; - struct ieee80211_hw *hw = mphy->hw; -- struct mt7915_mcu_txpower_sku req = { -+ struct mt7915_sku_val { -+ u8 format_id; -+ u8 limit_type; -+ u8 band_idx; -+ } __packed hdr = { - .format_id = TX_POWER_LIMIT_TABLE, -+ .limit_type = TX_POWER_LIMIT_TABLE_RATE, - .band_idx = phy->mt76->band_idx, - }; -- struct mt76_power_limits limits_array; -- s8 *la = (s8 *)&limits_array; -- int i, idx; -- int tx_power; -+ int i, ret, tx_power; -+ const u8 *len = mt7915_sku_group_len; -+ struct mt76_power_limits la = {}; -+ struct sk_buff *skb; - - tx_power = mt76_get_power_bound(mphy, hw->conf.power_level); -- tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, -- &limits_array, tx_power); -- mphy->txpower_cur = tx_power; -- -- for (i = 0, idx = 0; i < ARRAY_SIZE(mt7915_sku_group_len); i++) { -- u8 mcs_num, len = mt7915_sku_group_len[i]; -- int j; -+ if (phy->sku_limit_en) { -+ tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, -+ &la, tx_power); -+ mt7915_update_txpower(phy, tx_power); -+ } else { -+ mt7915_update_txpower(phy, tx_power); -+ return 0; -+ } - -- if (i >= SKU_HT_BW20 && i <= SKU_VHT_BW160) { -- mcs_num = 10; -+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, -+ sizeof(hdr) + MT7915_SKU_RATE_NUM); -+ if (!skb) -+ return -ENOMEM; -+ -+ skb_put_data(skb, &hdr, sizeof(hdr)); -+ skb_put_data(skb, &la.cck, len[SKU_CCK] + len[SKU_OFDM]); -+ skb_put_data(skb, &la.mcs[0], len[SKU_HT_BW20]); -+ skb_put_data(skb, &la.mcs[1], len[SKU_HT_BW40]); -+ -+ /* vht */ -+ for (i = 0; i < 4; i++) { -+ skb_put_data(skb, &la.mcs[i], sizeof(la.mcs[i])); -+ skb_put_zero(skb, 2); /* padding */ -+ } - -- if (i == SKU_HT_BW20 || i == SKU_VHT_BW20) -- la = (s8 *)&limits_array + 12; -- } else { -- mcs_num = len; -- } -+ /* he */ -+ skb_put_data(skb, &la.ru[0], sizeof(la.ru)); -+ ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, -+ MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), true); -+ if (ret) -+ return ret; -+ -+ /* only set per-path power table when it's configured */ -+ if (!phy->sku_path_en) -+ return 0; -+ -+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, -+ sizeof(hdr) + MT7915_SKU_PATH_NUM); -+ if (!skb) -+ return -ENOMEM; -+ -+ hdr.limit_type = TX_POWER_LIMIT_TABLE_PATH; -+ skb_put_data(skb, &hdr, sizeof(hdr)); -+ skb_put_data(skb, &la.path.cck, sizeof(la.path.cck)); -+ skb_put_data(skb, &la.path.ofdm, sizeof(la.path.ofdm)); -+ skb_put_data(skb, &la.path.ofdm_bf[1], sizeof(la.path.ofdm_bf) - 1); -+ -+ /* HT20 and HT40 */ -+ skb_put_data(skb, &la.path.ru[3], sizeof(la.path.ru[3])); -+ skb_put_data(skb, &la.path.ru_bf[3][1], sizeof(la.path.ru_bf[3]) - 1); -+ skb_put_data(skb, &la.path.ru[4], sizeof(la.path.ru[4])); -+ skb_put_data(skb, &la.path.ru_bf[4][1], sizeof(la.path.ru_bf[4]) - 1); -+ -+ /* start from non-bf and bf fields of -+ * BW20/RU242, BW40/RU484, BW80/RU996, BW160/RU2x996, -+ * RU26, RU52, and RU106 -+ */ -+ -+ for (i = 0; i < 8; i++) { -+ bool bf = i % 2; -+ u8 idx = (i + 6) / 2; -+ s8 *buf = bf ? la.path.ru_bf[idx] : la.path.ru[idx]; -+ /* The non-bf fields of RU26 to RU106 are special cases */ -+ if (bf) -+ skb_put_data(skb, buf + 1, 9); -+ else -+ skb_put_data(skb, buf, 10); -+ } - -- for (j = 0; j < min_t(u8, mcs_num, len); j++) -- req.txpower_sku[idx + j] = la[j]; -+ for (i = 0; i < 6; i++) { -+ bool bf = i % 2; -+ u8 idx = i / 2; -+ s8 *buf = bf ? la.path.ru_bf[idx] : la.path.ru[idx]; - -- la += mcs_num; -- idx += len; -+ skb_put_data(skb, buf, 10); - } - -- return mt76_mcu_send_msg(&dev->mt76, -- MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, -- sizeof(req), true); -+ return mt76_mcu_skb_send_msg(&dev->mt76, skb, -+ MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), true); - } - --int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len) -+int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len, -+ u8 category) - { - #define RATE_POWER_INFO 2 - struct mt7915_dev *dev = phy->dev; -@@ -3431,10 +3517,9 @@ int mt7915_mcu_get_txpower_sku(struct mt - u8 _rsv; - } __packed req = { - .format_id = TX_POWER_LIMIT_INFO, -- .category = RATE_POWER_INFO, -+ .category = category, - .band_idx = phy->mt76->band_idx, - }; -- s8 txpower_sku[MT7915_SKU_RATE_NUM][2]; - struct sk_buff *skb; - int ret, i; - -@@ -3444,9 +3529,15 @@ int mt7915_mcu_get_txpower_sku(struct mt - if (ret) - return ret; - -- memcpy(txpower_sku, skb->data + 4, sizeof(txpower_sku)); -- for (i = 0; i < len; i++) -- txpower[i] = txpower_sku[i][req.band_idx]; -+ if (category == TX_POWER_INFO_RATE) { -+ s8 res[MT7915_SKU_RATE_NUM][2]; -+ -+ memcpy(res, skb->data + 4, sizeof(res)); -+ for (i = 0; i < len; i++) -+ txpower[i] = res[i][req.band_idx]; -+ } else if (category == TX_POWER_INFO_PATH) { -+ memcpy(txpower, skb->data + 4, len); -+ } - - dev_kfree_skb(skb); - -@@ -3475,7 +3566,7 @@ int mt7915_mcu_set_test_param(struct mt7 - sizeof(req), false); - } - --int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable) -+int mt7915_mcu_set_sku_en(struct mt7915_phy *phy) - { - struct mt7915_dev *dev = phy->dev; - struct mt7915_sku { -@@ -3484,10 +3575,21 @@ int mt7915_mcu_set_sku_en(struct mt7915_ - u8 band_idx; - u8 rsv; - } __packed req = { -- .format_id = TX_POWER_LIMIT_ENABLE, - .band_idx = phy->mt76->band_idx, -- .sku_enable = enable, - }; -+ int ret; -+ -+ req.sku_enable = phy->sku_limit_en; -+ req.format_id = TX_POWER_LIMIT_ENABLE; -+ -+ ret = mt76_mcu_send_msg(&dev->mt76, -+ MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, -+ sizeof(req), true); -+ if (ret) -+ return ret; -+ -+ req.sku_enable = phy->sku_path_en; -+ req.format_id = TX_POWER_LIMIT_PATH_ENABLE; - - return mt76_mcu_send_msg(&dev->mt76, - MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, ---- a/mt7915/mcu.h -+++ b/mt7915/mcu.h -@@ -429,6 +429,7 @@ enum { - - enum { - TX_POWER_LIMIT_ENABLE, -+ TX_POWER_LIMIT_PATH_ENABLE = 0x3, - TX_POWER_LIMIT_TABLE = 0x4, - TX_POWER_LIMIT_INFO = 0x7, - TX_POWER_LIMIT_FRAME = 0x11, -@@ -436,6 +437,11 @@ enum { - }; - - enum { -+ TX_POWER_INFO_PATH = 1, -+ TX_POWER_INFO_RATE, -+}; -+ -+enum { - SPR_ENABLE = 0x1, - SPR_ENABLE_SD = 0x3, - SPR_ENABLE_MODE = 0x5, ---- a/mt7915/mt7915.h -+++ b/mt7915/mt7915.h -@@ -70,6 +70,7 @@ - #define MT7915_CDEV_THROTTLE_MAX 99 - - #define MT7915_SKU_RATE_NUM 161 -+#define MT7915_SKU_PATH_NUM 185 - - #define MT7915_MAX_TWT_AGRT 16 - #define MT7915_MAX_STA_TWT_AGRT 8 -@@ -223,6 +224,9 @@ struct mt7915_phy { - struct mt76_mib_stats mib; - struct mt76_channel_state state_ts; - -+ bool sku_limit_en:1; -+ bool sku_path_en:1; -+ - #ifdef CONFIG_NL80211_TESTMODE - struct { - u32 *reg_backup; -@@ -491,9 +495,10 @@ int mt7915_mcu_set_mac(struct mt7915_dev - int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode, - u8 en); - int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band); --int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable); -+int mt7915_mcu_set_sku_en(struct mt7915_phy *phy); - int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy); --int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len); -+int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len, -+ u8 category); - int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower); - int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, - struct ieee80211_vif *vif, ---- a/mt7915/testmode.c -+++ b/mt7915/testmode.c -@@ -409,7 +409,7 @@ mt7915_tm_init(struct mt7915_phy *phy, b - if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) - return; - -- mt7915_mcu_set_sku_en(phy, !en); -+ mt7915_mcu_set_sku_en(phy); - - mt7915_tm_mode_ctrl(dev, en); - mt7915_tm_reg_backup_restore(phy);