Linux-mediatek Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: JB Tsai <jb.tsai@mediatek.com>
To: <nbd@nbd.name>, <lorenzo@kernel.org>
Cc: <linux-wireless@vger.kernel.org>,
	<linux-mediatek@lists.infradead.org>, <Sean.Wang@mediatek.com>,
	<Quan.Zhou@mediatek.com>, <Ryder.Lee@mediatek.com>,
	<litien.chang@mediatek.com>, <Charlie-cy.Wu@mediatek.com>,
	<jb.tsai@mediatek.com>
Subject: [PATCH] wifi: mt76: mt7928: add debugfs to pin the 6GHz power mode
Date: Fri, 7 Aug 2026 15:11:14 +0800	[thread overview]
Message-ID: <20260807071114.3056469-1-jb.tsai@mediatek.com> (raw)

From: Charlie-cy Wu <Charlie-cy.Wu@mediatek.com>

The 6GHz power mode follows the power type advertised by the AP, which
makes it hard to validate one specific power table on the FW side. Add a
debugfs knob that pins the power mode and pushes the CLC power table of
the selected mode down to the FW.

Key changes:
- Add power_type_user to struct mt792x_phy to flag a pinned power mode
- Add 6g_power_mode debugfs for selecting the 6GHz power mode:
  * 0: UNSET
  * 1: DEFAULT
  * 2: LPI
  * 3: SP
  * 4: VLP
  * 0xff: release the override
- Skip mt7925_regd_set_6ghz_power_type() while pinned, so that 6GHz
  (dis)association no longer overrides the selected power mode
- Return -EOPNOTSUPP when CLC is not supported and -EINVAL for values
  other than the ones listed above

Selecting a mode re-applies the regulatory domain with power_type_change
set, hence only the power table of that mode is sent to the FW. Releasing
the override does not send anything to the FW, it only restores the power
mode to its initial state so that the next 6GHz association or regulatory
domain change programs the power tables again.

Usage:
  echo 3 > /sys/kernel/debug/ieee80211/phy0/mt76/6g_power_mode
  cat /sys/kernel/debug/ieee80211/phy0/mt76/txpower_sku
  echo 0xff > /sys/kernel/debug/ieee80211/phy0/mt76/6g_power_mode

Signed-off-by: Charlie-cy Wu <Charlie-cy.Wu@mediatek.com>
---
 .../wireless/mediatek/mt76/mt7925/debugfs.c   | 66 +++++++++++++++++++
 .../net/wireless/mediatek/mt76/mt7925/main.c  |  4 ++
 .../net/wireless/mediatek/mt76/mt7925/mcu.c   |  8 ++-
 drivers/net/wireless/mediatek/mt76/mt792x.h   |  2 +
 4 files changed, 77 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
index 01a3655e8b82..3b34c20f7325 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
@@ -3,6 +3,7 @@
 
 #include "mt7925.h"
 #include "mcu.h"
+#include "regd.h"
 
 static int
 mt7925_reg_set(void *data, u64 val)
@@ -319,6 +320,69 @@ mt7928_txpwr_tbl_get(void *data, u64 *val)
 DEFINE_DEBUGFS_ATTRIBUTE(fops_txpwr_tbl, mt7928_txpwr_tbl_get,
 			 mt7928_txpwr_tbl_set, "%lld\n");
 
+/* Pin the 6GHz power mode instead of following the AP advertised one and push
+ * only the CLC power table of that mode down to the FW:
+ *   0: UNSET, 1: DEFAULT, 2: LPI, 3: SP, 4: VLP
+ *   0xff: release the override and go back to the AP advertised power mode
+ *
+ * UNSET/DEFAULT keep the FW on the LPI table, which is what the driver uses
+ * when no 6GHz power type has been reported by the AP.
+ *
+ * While pinned, 6GHz (dis)association no longer touches the power mode and a
+ * regulatory domain change only refreshes the pinned 6G power table, so the
+ * 2/5G tables of the new country are not sent until the override is released.
+ *
+ * Releasing does not send anything to the FW, it only restores the power mode
+ * to its initial state so that the next 6GHz association or regulatory domain
+ * change programs the power tables again.
+ */
+static int
+mt7925_6g_power_mode_set(void *data, u64 val)
+{
+	struct mt792x_dev *dev = data;
+	struct mt792x_phy *phy = &dev->phy;
+	int ret;
+
+	if (!mt7925_regd_clc_supported(dev))
+		return -EOPNOTSUPP;
+
+	if (val == 0xff) {
+		mt792x_mutex_acquire(dev);
+		phy->power_type_user = false;
+		phy->power_type = MT_AP_UNSET;
+		mt792x_mutex_release(dev);
+
+		return 0;
+	}
+
+	if (val > MT_AP_VLP)
+		return -EINVAL;
+
+	wiphy_lock(mt76_hw(dev)->wiphy);
+	mt792x_mutex_acquire(dev);
+	phy->power_type_user = true;
+	phy->power_type = (enum mt792x_reg_power_type)val;
+	ret = mt7925_mcu_apply_regd(dev, dev->mt76.alpha2,
+				    dev->country_ie_env, true);
+	mt792x_mutex_release(dev);
+	wiphy_unlock(mt76_hw(dev)->wiphy);
+
+	return ret;
+}
+
+static int
+mt7925_6g_power_mode_get(void *data, u64 *val)
+{
+	struct mt792x_phy *phy = &((struct mt792x_dev *)data)->phy;
+
+	*val = phy->power_type_user ? phy->power_type : 0xff;
+
+	return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_6g_power_mode, mt7925_6g_power_mode_get,
+			 mt7925_6g_power_mode_set, "%lld\n");
+
 static int
 mt7925_pm_set(void *data, u64 val)
 {
@@ -556,6 +620,8 @@ int mt7925_init_debugfs(struct mt792x_dev *dev)
 	debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
 	debugfs_create_file("fixedrate", 0600, dir, dev, &fops_fixedrate);
 	debugfs_create_file("autorate", 0600, dir, dev, &fops_autorate);
+	debugfs_create_file("6g_power_mode", 0600, dir, dev,
+			    &fops_6g_power_mode);
 
 	return 0;
 }
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index cbfbf39322de..0dba2de8e5a5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -941,6 +941,10 @@ mt7925_regd_set_6ghz_power_type(struct ieee80211_vif *vif,
 	enum mt792x_reg_power_type old_power_type;
 	bool power_type_changed = false;
 
+	/* the 6GHz power mode is pinned through debugfs */
+	if (phy->power_type_user)
+		return;
+
 	old_power_type = phy->power_type;
 
 	/* Try to get channel from link_conf first, then vif->bss_conf, then phy */
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 6e892860325b..39ca49506150 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -3672,14 +3672,14 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
 		u8 pad1;
 		u8 alpha2[2];
 		u8 type[2];
-		u8 env_6g;
+		u8 power_type_user; /* 0: AP advertised, 1: forced by debugfs */
 		u8 rsvd[63];
 	} __packed req = {
 		.tag = cpu_to_le16(0x3),
 
 		.idx = idx,
 		.env = env_cap,
-		.env_6g = dev->phy.power_type,
+		.power_type_user = dev->phy.power_type_user,
 	};
 	int ret, valid_cnt = 0;
 	u8 *pos, *last_pos;
@@ -3720,8 +3720,10 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
 		 *   - Send 2/5G ('-') and 6G LPI ('0') and 6G VLP ('2')
 		 * power_type_change == true: from mt7925_regd_set_6ghz_power_type()
 		 *   - Send only the specific 6G power table based on power_type
+		 * A 6GHz power mode pinned through debugfs behaves like the
+		 * latter: only the 6G power table of that mode is sent.
 		 */
-		if (power_type_change) {
+		if (power_type_change || dev->phy.power_type_user) {
 			/* Only send the specific 6G power table */
 			if (rule->type[0] != target_6g_type)
 				continue;
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 65e1b9874ead..c226e5a13164 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -222,6 +222,8 @@ struct mt792x_phy {
 	u8 sta_work_count;
 	u8 clc_chan_conf;
 	enum mt792x_reg_power_type power_type;
+	/* 6GHz power mode pinned through debugfs, ignore the AP advertised one */
+	bool power_type_user;
 
 	struct sk_buff_head scan_event_list;
 	struct delayed_work scan_work;
-- 
2.18.0



                 reply	other threads:[~2026-08-07  7:11 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260807071114.3056469-1-jb.tsai@mediatek.com \
    --to=jb.tsai@mediatek.com \
    --cc=Charlie-cy.Wu@mediatek.com \
    --cc=Quan.Zhou@mediatek.com \
    --cc=Ryder.Lee@mediatek.com \
    --cc=Sean.Wang@mediatek.com \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=litien.chang@mediatek.com \
    --cc=lorenzo@kernel.org \
    --cc=nbd@nbd.name \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox