* [PATCH mt76 v3 0/3] wifi: mt76: mt7915: Beamforming backoff limit table
@ 2025-10-07 8:11 Sven Eckelmann (Plasma Cloud)
2025-10-07 8:11 ` [PATCH mt76 v3 1/3] dt-bindings: net: wireless: mt76: Document power-limits country property Sven Eckelmann (Plasma Cloud)
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Sven Eckelmann (Plasma Cloud) @ 2025-10-07 8:11 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
Maxime Coquelin, Alexandre Torgue, Johannes Berg, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
linux-stm32, devicetree, Sven Eckelmann (Plasma Cloud)
It was noticed that Mediatek's MT7915 does not only require calibration of
the normal rates (configured via DT) but also some backoff values for
beamforming. The proprietary driver loads this from some file in the
firmware folder. But for the the upstream driver, it is necessary to get
them from the same place as the normal limits - in this case from the
devicetree.
They released the support for this [1,2] in their own SDK but
without any kind of documentation. And it was (to my knowledge) never
forwarded to upstream. But since also (in my case) OpenWrt needs correct
calibration for this WiFi chip, we should get this upstreamed.
I try to my best to provide some sensible information about the patch and
the DT entries. If anyone else has more information about it (to improve
the descriptions), please feel free to comment and provide some
enhanced/corrected version.
The patch from Shayne Chen was only rebased and modified to perform byte
reads from the DT [3]. Besides this, only a commit message was added,
MTK_DEBUG parts were dropped and some checkpatch problems were fixed.
Regards,
Sven
[1] https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/f0d2527deb4f91592b6486a5c98ea3f584f76a61
[2] https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/737340322ab22b138fd200e020d61ffdbe3e36a9/autobuild/autobuild_5.4_mac80211_release/package/kernel/mt76/patches/1022-wifi-mt76-mt7915-add-bf-backoff-limit-table-support.patch
[3] https://lore.kernel.org/r/20250917-fix-power-limits-v1-1-616e859a9881@simonwunderlich.de
Signed-off-by: Sven Eckelmann (Plasma Cloud) <se@simonwunderlich.de>
---
Changes in v3:
- Add pattern for country property, Thanks Rob Herring
- Add Robs' Reviewed-by to "introduce backoff limit properties" dt-schema patch
- Link to v2: https://lore.kernel.org/r/20250926-backoff-table-support-v2-0-16d3726646c4@simonwunderlich.de
Changes in v2:
- add [PATCH mt76] as requested by Zhi-Jun You
- Link to v1: https://lore.kernel.org/r/20250924-backoff-table-support-v1-0-20e50fbc59de@simonwunderlich.de
---
Shayne Chen (1):
wifi: mt76: mt7915: add bf backoff limit table support
Sven Eckelmann (Plasma Cloud) (2):
dt-bindings: net: wireless: mt76: Document power-limits country property
dt-bindings: net: wireless: mt76: introduce backoff limit properties
.../bindings/net/wireless/mediatek,mt76.yaml | 66 ++++++++
drivers/net/wireless/mediatek/mt76/debugfs.c | 4 +-
drivers/net/wireless/mediatek/mt76/eeprom.c | 38 ++++-
drivers/net/wireless/mediatek/mt76/mt76.h | 8 +
.../net/wireless/mediatek/mt76/mt7915/debugfs.c | 74 ++++++++-
drivers/net/wireless/mediatek/mt76/mt7915/init.c | 7 +
drivers/net/wireless/mediatek/mt76/mt7915/main.c | 2 +-
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 182 ++++++++++++++++-----
drivers/net/wireless/mediatek/mt76/mt7915/mcu.h | 6 +
drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h | 9 +-
.../net/wireless/mediatek/mt76/mt7915/testmode.c | 2 +-
11 files changed, 343 insertions(+), 55 deletions(-)
---
base-commit: b36d55610215a976267197ddc914902c494705d7
change-id: 20250917-backoff-table-support-99218823576d
prerequisite-change-id: 20250917-fix-power-limits-5ce07b993681:v2
prerequisite-patch-id: 964626eea847052cefc907ef0f01286a080fdc3c
Best regards,
--
Sven Eckelmann (Plasma Cloud) <se@simonwunderlich.de>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH mt76 v3 1/3] dt-bindings: net: wireless: mt76: Document power-limits country property
2025-10-07 8:11 [PATCH mt76 v3 0/3] wifi: mt76: mt7915: Beamforming backoff limit table Sven Eckelmann (Plasma Cloud)
@ 2025-10-07 8:11 ` Sven Eckelmann (Plasma Cloud)
2025-10-10 14:25 ` Rob Herring (Arm)
2025-10-07 8:11 ` [PATCH mt76 v3 2/3] dt-bindings: net: wireless: mt76: introduce backoff limit properties Sven Eckelmann (Plasma Cloud)
2025-10-07 8:11 ` [PATCH mt76 v3 3/3] wifi: mt76: mt7915: add bf backoff limit table support Sven Eckelmann (Plasma Cloud)
2 siblings, 1 reply; 5+ messages in thread
From: Sven Eckelmann (Plasma Cloud) @ 2025-10-07 8:11 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
Maxime Coquelin, Alexandre Torgue, Johannes Berg, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
linux-stm32, devicetree, Sven Eckelmann (Plasma Cloud)
The commit 22b980badc0f ("mt76: add functions for parsing rate power limits
from DT") added filtering of the power limits based on two properties:
* regdomain
* country
If either the country or the regdomain matches, the power limits are
applied and the search is aborted. If none of the two is defined for the
power limit, it is a global (or "fallback") power limit. The last
"fallback" power limit in the list will be returned when not matching
regdomain or country was found.
The idea is here to allow to specify "overwriting" country limits in front
of the list - just in case a regdomain is shared but a country has
additional limitations.
But this property was forgotten to be defined in commit 2de6ccebe0e7
("dt-bindings:net:wireless:mediatek,mt76: introduce power-limits node").
Signed-off-by: Sven Eckelmann (Plasma Cloud) <se@simonwunderlich.de>
---
Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
index eabceb849537..82b224c2f6f7 100644
--- a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
@@ -151,6 +151,12 @@ properties:
- ETSI
- JP
+ country:
+ $ref: /schemas/types.yaml#/definitions/string
+ pattern: '^[A-Z]{2}$'
+ description:
+ ISO 3166-1 alpha-2 country code for power limits
+
patternProperties:
"^txpower-[256]g$":
type: object
--
2.47.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH mt76 v3 2/3] dt-bindings: net: wireless: mt76: introduce backoff limit properties
2025-10-07 8:11 [PATCH mt76 v3 0/3] wifi: mt76: mt7915: Beamforming backoff limit table Sven Eckelmann (Plasma Cloud)
2025-10-07 8:11 ` [PATCH mt76 v3 1/3] dt-bindings: net: wireless: mt76: Document power-limits country property Sven Eckelmann (Plasma Cloud)
@ 2025-10-07 8:11 ` Sven Eckelmann (Plasma Cloud)
2025-10-07 8:11 ` [PATCH mt76 v3 3/3] wifi: mt76: mt7915: add bf backoff limit table support Sven Eckelmann (Plasma Cloud)
2 siblings, 0 replies; 5+ messages in thread
From: Sven Eckelmann (Plasma Cloud) @ 2025-10-07 8:11 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
Maxime Coquelin, Alexandre Torgue, Johannes Berg, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
linux-stm32, devicetree, Sven Eckelmann (Plasma Cloud)
Introduce path backoff limit properties in mt76 binding in order to specify
beamforming and non-beamforming backoff limits for 802.11n/ac/ax.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Sven Eckelmann (Plasma Cloud) <se@simonwunderlich.de>
---
.../bindings/net/wireless/mediatek,mt76.yaml | 60 ++++++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
index 82b224c2f6f7..ae6b97cdc44b 100644
--- a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
@@ -216,6 +216,66 @@ properties:
minItems: 13
maxItems: 13
+ paths-cck:
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ minItems: 4
+ maxItems: 4
+ description:
+ 4 half-dBm backoff values (1 - 4 antennas, single spacial
+ stream)
+
+ paths-ofdm:
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ minItems: 4
+ maxItems: 4
+ description:
+ 4 half-dBm backoff values (1 - 4 antennas, single spacial
+ stream)
+
+ paths-ofdm-bf:
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ minItems: 4
+ maxItems: 4
+ description:
+ 4 half-dBm backoff values for beamforming
+ (1 - 4 antennas, single spacial stream)
+
+ paths-ru:
+ $ref: /schemas/types.yaml#/definitions/uint8-matrix
+ description:
+ Sets of half-dBm backoff values for 802.11ax rates for
+ 1T1ss (aka 1 transmitting antenna with 1 spacial stream),
+ 2T1ss, 3T1ss, 4T1ss, 2T2ss, 3T2ss, 4T2ss, 3T3ss, 4T3ss
+ and 4T4ss.
+ Each set starts with the number of channel bandwidth or
+ resource unit settings for which the rate set applies,
+ followed by 10 power limit values. The order of the
+ channel resource unit settings is RU26, RU52, RU106,
+ RU242/SU20, RU484/SU40, RU996/SU80 and RU2x996/SU160.
+ minItems: 1
+ maxItems: 7
+ items:
+ minItems: 11
+ maxItems: 11
+
+ paths-ru-bf:
+ $ref: /schemas/types.yaml#/definitions/uint8-matrix
+ description:
+ Sets of half-dBm backoff (beamforming) values for 802.11ax
+ rates for 1T1ss (aka 1 transmitting antenna with 1 spacial
+ stream), 2T1ss, 3T1ss, 4T1ss, 2T2ss, 3T2ss, 4T2ss, 3T3ss,
+ 4T3ss and 4T4ss.
+ Each set starts with the number of channel bandwidth or
+ resource unit settings for which the rate set applies,
+ followed by 10 power limit values. The order of the
+ channel resource unit settings is RU26, RU52, RU106,
+ RU242/SU20, RU484/SU40, RU996/SU80 and RU2x996/SU160.
+ minItems: 1
+ maxItems: 7
+ items:
+ minItems: 11
+ maxItems: 11
+
txs-delta:
$ref: /schemas/types.yaml#/definitions/uint32-array
description:
--
2.47.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH mt76 v3 3/3] wifi: mt76: mt7915: add bf backoff limit table support
2025-10-07 8:11 [PATCH mt76 v3 0/3] wifi: mt76: mt7915: Beamforming backoff limit table Sven Eckelmann (Plasma Cloud)
2025-10-07 8:11 ` [PATCH mt76 v3 1/3] dt-bindings: net: wireless: mt76: Document power-limits country property Sven Eckelmann (Plasma Cloud)
2025-10-07 8:11 ` [PATCH mt76 v3 2/3] dt-bindings: net: wireless: mt76: introduce backoff limit properties Sven Eckelmann (Plasma Cloud)
@ 2025-10-07 8:11 ` Sven Eckelmann (Plasma Cloud)
2 siblings, 0 replies; 5+ messages in thread
From: Sven Eckelmann (Plasma Cloud) @ 2025-10-07 8:11 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
Maxime Coquelin, Alexandre Torgue, Johannes Berg, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
linux-stm32, devicetree, Sven Eckelmann (Plasma Cloud)
From: Shayne Chen <shayne.chen@mediatek.com>
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 <shayne.chen@mediatek.com>
Signed-off-by: Sven Eckelmann (Plasma Cloud) <se@simonwunderlich.de>
---
drivers/net/wireless/mediatek/mt76/debugfs.c | 4 +-
drivers/net/wireless/mediatek/mt76/eeprom.c | 38 ++++-
drivers/net/wireless/mediatek/mt76/mt76.h | 8 +
.../net/wireless/mediatek/mt76/mt7915/debugfs.c | 74 ++++++++-
drivers/net/wireless/mediatek/mt76/mt7915/init.c | 7 +
drivers/net/wireless/mediatek/mt76/mt7915/main.c | 2 +-
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 182 ++++++++++++++++-----
drivers/net/wireless/mediatek/mt76/mt7915/mcu.h | 6 +
drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h | 9 +-
.../net/wireless/mediatek/mt76/mt7915/testmode.c | 2 +-
10 files changed, 277 insertions(+), 55 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/debugfs.c b/drivers/net/wireless/mediatek/mt76/debugfs.c
index b6a2746c187d..bee1177594d3 100644
--- a/drivers/net/wireless/mediatek/mt76/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/debugfs.c
@@ -93,9 +93,9 @@ void mt76_seq_puts_array(struct seq_file *file, const char *str,
{
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);
diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c
index 6ce8e4af18fe..cbda19a86882 100644
--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
@@ -324,9 +324,10 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const s8 *data,
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;
@@ -337,7 +338,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
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;
@@ -364,12 +365,16 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
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;
@@ -415,12 +420,35 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
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;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index e0d50b58cd01..86b812f68c97 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -1113,6 +1113,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 {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
index b287b7d9394e..6ff0d262c28a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
@@ -1008,7 +1008,7 @@ mt7915_rate_txpower_get(struct file *file, char __user *user_buf,
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 *file, const char __user *user_buf,
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 @@ mt7915_rate_txpower_set(struct file *file, const char __user *user_buf,
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,
@@ -1168,6 +1168,70 @@ static const struct file_operations mt7915_rate_txpower_fops = {
.llseek = default_llseek,
};
+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)
{
@@ -1254,7 +1318,9 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
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);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index 5ea8b46e092e..1ac7ee2922b0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -289,6 +289,8 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
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 mt7915_phy *phy,
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,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
index fe0639c14bf9..fa135447a263 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+++ b/drivers/net/wireless/mediatek/mt76/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;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index c1fdd3c4f1ba..673338d508a8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -3336,7 +3336,8 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
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 mt7915_phy *phy,
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 (i >= SKU_HT_BW20 && i <= SKU_VHT_BW160) {
- mcs_num = 10;
-
- if (i == SKU_HT_BW20 || i == SKU_VHT_BW20)
- la = (s8 *)&limits_array + 12;
- } else {
- mcs_num = len;
- }
-
- for (j = 0; j < min_t(u8, mcs_num, len); j++)
- req.txpower_sku[idx + j] = la[j];
-
- la += mcs_num;
- idx += len;
+ 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;
}
- return mt76_mcu_send_msg(&dev->mt76,
- MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
- sizeof(req), true);
+ 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 */
+ }
+
+ /* 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 (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];
+
+ skb_put_data(skb, buf, 10);
+ }
+
+ 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 mt7915_phy *phy, s8 *txpower, int len)
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 mt7915_phy *phy, s8 *txpower, int len)
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 mt7915_dev *dev, u8 param, bool test_mode,
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_phy *phy, bool enable)
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,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
index 086ad89ecd91..b72535efc6d7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
@@ -429,12 +429,18 @@ 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,
TX_POWER_LIMIT_FRAME_MIN = 0x12,
};
+enum {
+ TX_POWER_INFO_PATH = 1,
+ TX_POWER_INFO_RATE,
+};
+
enum {
SPR_ENABLE = 0x1,
SPR_ENABLE_SD = 0x3,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index 2e94347c46d6..b15d31d36a87 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/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 *dev, int band, bool enable,
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,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
index d534fff5c952..5836b9733f27 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
@@ -409,7 +409,7 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
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);
--
2.47.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH mt76 v3 1/3] dt-bindings: net: wireless: mt76: Document power-limits country property
2025-10-07 8:11 ` [PATCH mt76 v3 1/3] dt-bindings: net: wireless: mt76: Document power-limits country property Sven Eckelmann (Plasma Cloud)
@ 2025-10-10 14:25 ` Rob Herring (Arm)
0 siblings, 0 replies; 5+ messages in thread
From: Rob Herring (Arm) @ 2025-10-10 14:25 UTC (permalink / raw)
To: Sven Eckelmann (Plasma Cloud)
Cc: Alexandre Torgue, linux-arm-kernel, Matthias Brugger, Sean Wang,
Felix Fietkau, linux-stm32, Lorenzo Bianconi, Maxime Coquelin,
Shayne Chen, linux-kernel, devicetree, Johannes Berg,
Krzysztof Kozlowski, linux-mediatek, Conor Dooley,
AngeloGioacchino Del Regno, Ryder Lee, linux-wireless
On Tue, 07 Oct 2025 10:11:14 +0200, Sven Eckelmann (Plasma Cloud) wrote:
> The commit 22b980badc0f ("mt76: add functions for parsing rate power limits
> from DT") added filtering of the power limits based on two properties:
>
> * regdomain
> * country
>
> If either the country or the regdomain matches, the power limits are
> applied and the search is aborted. If none of the two is defined for the
> power limit, it is a global (or "fallback") power limit. The last
> "fallback" power limit in the list will be returned when not matching
> regdomain or country was found.
>
> The idea is here to allow to specify "overwriting" country limits in front
> of the list - just in case a regdomain is shared but a country has
> additional limitations.
>
> But this property was forgotten to be defined in commit 2de6ccebe0e7
> ("dt-bindings:net:wireless:mediatek,mt76: introduce power-limits node").
>
> Signed-off-by: Sven Eckelmann (Plasma Cloud) <se@simonwunderlich.de>
> ---
> Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml | 6 ++++++
> 1 file changed, 6 insertions(+)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-10-10 14:25 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-07 8:11 [PATCH mt76 v3 0/3] wifi: mt76: mt7915: Beamforming backoff limit table Sven Eckelmann (Plasma Cloud)
2025-10-07 8:11 ` [PATCH mt76 v3 1/3] dt-bindings: net: wireless: mt76: Document power-limits country property Sven Eckelmann (Plasma Cloud)
2025-10-10 14:25 ` Rob Herring (Arm)
2025-10-07 8:11 ` [PATCH mt76 v3 2/3] dt-bindings: net: wireless: mt76: introduce backoff limit properties Sven Eckelmann (Plasma Cloud)
2025-10-07 8:11 ` [PATCH mt76 v3 3/3] wifi: mt76: mt7915: add bf backoff limit table support Sven Eckelmann (Plasma Cloud)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).