public inbox for linux-wireless@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors
@ 2026-04-20  3:40 Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 01/16] wifi: rtw89: 8922d: fix typo rx_freq_frome_ie Ping-Ke Shih
                   ` (15 more replies)
  0 siblings, 16 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

First patch is to correct typo which was missed because of two patchset
were submitted at the same time; patch 7 is similar. The second one is to
improve user experience on realtime applications, such as remote desktop.

The patches 3-5 are to add switches by debugfs to control the functions for
latency-sensitive scenario.

Patch 6 is to fix data type to prevent overflow for 6GHz RNR scan.

Patches 8-10 are to add USB IO offload, which packs IO into a H2C command
and firmware does all IO in one go. This is to improve overall IO
performance.

Patch 11 is trivial to add a USB device.

Patch 12-14 are to fix issues rarely found by MCC test. Address them and
fix.

Patch 15-16 are to support RTL8922D's new design, which hardware does
backup RF calibration data on two PHY with the same channel to support
runtime switch channels without doing calibration again, for example eMLSR
that should change to 5GHz or 6GHz channel quickly.

Chia-Yuan Li (2):
  wifi: rtw89: add IO offload support via firmware
  wifi: rtw89: offload DMAC and CMAC init IO to firmware

Chih-Kang Chang (3):
  wifi: rtw89: use struct to fill C2H recv ack
  wifi: rtw89: check scan C2H event recv ack instead of C2H event done
    ack
  wifi: rtw89: suspend DIG when remain-on-channel

Eric Huang (2):
  wifi: rtw89: phy: support static PD level setting
  wifi: rtw89: use firmware offload for PHY and RF batch register writes

Johnson Tsai (3):
  wifi: rtw89: debug: disable hw_scan for latency-sensitive scenarios
  wifi: rtw89: debug: disable inactive power save to reduce bus overhead
  wifi: rtw89: 8832cu: Add ID 2c7c:8206 for RTL8832CU

Ping-Ke Shih (1):
  wifi: rtw89: 8922d: fix typo rx_freq_frome_ie

Po-Hao Huang (1):
  wifi: rtw89: 8852a: refine power save to lower latency

Shin-Yi Lin (1):
  wifi: rtw89: Correct data type for scan index to avoid infinite loop

Zong-Zhe Yang (3):
  wifi: rtw89: 8852bt: configure support_noise field explicitly
  wifi: rtw89: chan: introduce new helper to get entity current
    configuration
  wifi: rtw89: 8922d: update RF calibration flow for MLD

 drivers/net/wireless/realtek/rtw89/chan.c     |  90 +++--
 drivers/net/wireless/realtek/rtw89/chan.h     |  21 +-
 drivers/net/wireless/realtek/rtw89/core.c     | 111 +++++-
 drivers/net/wireless/realtek/rtw89/core.h     |  97 ++++-
 drivers/net/wireless/realtek/rtw89/debug.c    |  96 +++++
 drivers/net/wireless/realtek/rtw89/fw.c       | 349 +++++++++++++++++-
 drivers/net/wireless/realtek/rtw89/fw.h       |  75 +++-
 drivers/net/wireless/realtek/rtw89/mac.c      |  89 ++++-
 drivers/net/wireless/realtek/rtw89/mac80211.c |   7 +
 drivers/net/wireless/realtek/rtw89/mac_be.c   |  57 ++-
 drivers/net/wireless/realtek/rtw89/phy.c      |  94 +++--
 drivers/net/wireless/realtek/rtw89/phy.h      |  30 +-
 drivers/net/wireless/realtek/rtw89/phy_be.c   |   8 +-
 drivers/net/wireless/realtek/rtw89/ps.c       |   6 +
 drivers/net/wireless/realtek/rtw89/reg.h      |   4 +
 drivers/net/wireless/realtek/rtw89/rtw8851b.c |   1 +
 drivers/net/wireless/realtek/rtw89/rtw8852a.c |   1 +
 drivers/net/wireless/realtek/rtw89/rtw8852b.c |   1 +
 .../net/wireless/realtek/rtw89/rtw8852b_rfk.c |   6 +-
 .../net/wireless/realtek/rtw89/rtw8852bt.c    |   2 +
 .../wireless/realtek/rtw89/rtw8852bt_rfk.c    |   6 +-
 drivers/net/wireless/realtek/rtw89/rtw8852c.c |   1 +
 .../net/wireless/realtek/rtw89/rtw8852cu.c    |   2 +
 drivers/net/wireless/realtek/rtw89/rtw8922a.c |  18 +-
 .../net/wireless/realtek/rtw89/rtw8922a_rfk.c |  22 +-
 drivers/net/wireless/realtek/rtw89/rtw8922d.c |  40 +-
 .../net/wireless/realtek/rtw89/rtw8922d_rfk.c |  92 +++--
 .../net/wireless/realtek/rtw89/rtw8922d_rfk.h |   1 +
 28 files changed, 1130 insertions(+), 197 deletions(-)


base-commit: fa489a77e3267e05df95db96ba98e141ec07cbd9
-- 
2.25.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 01/16] wifi: rtw89: 8922d: fix typo rx_freq_frome_ie
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 02/16] wifi: rtw89: 8852a: refine power save to lower latency Ping-Ke Shih
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

The commit 127ea8d0b068 ("wifi: rtw89: fix typo "frome" -> "from" in rx_freq_frome_ie")
change the struct field to correct the typo, but miss to fix this for
RTL8922D because it is submitting at the same time.

As RTL8922D is not enabled yet, this build error doesn't appear.

Fix it accordingly.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index e3b77cd23514..a3890e863423 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -2988,7 +2988,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = {
 	.support_noise		= false,
 	.ul_tb_waveform_ctrl	= false,
 	.ul_tb_pwr_diff		= false,
-	.rx_freq_frome_ie	= false,
+	.rx_freq_from_ie	= false,
 	.hw_sec_hdr		= true,
 	.hw_mgmt_tx_encrypt	= true,
 	.hw_tkip_crypto		= true,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 02/16] wifi: rtw89: 8852a: refine power save to lower latency
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 01/16] wifi: rtw89: 8922d: fix typo rx_freq_frome_ie Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 03/16] wifi: rtw89: debug: disable hw_scan for latency-sensitive scenarios Ping-Ke Shih
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Po-Hao Huang <phhuang@realtek.com>

Improve user experience while using interactive applications.
Adjust power saving decisions under different scenarios.
Minimize latency for delay-sensitive connections by exiting
power-save on packet bursts and maintain active until a period
of inactivity is reached. For connections that are not that
sensitive to delays, keep the current aggressive power save logic.

Signed-off-by: Po-Hao Huang <phhuang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 87 +++++++++++++++++++++--
 drivers/net/wireless/realtek/rtw89/core.h |  9 +++
 2 files changed, 89 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 70feab97dccb..c9c4ec1d93af 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -341,10 +341,26 @@ static const struct rtw89_hw_rate_def {
 	},
 };
 
+static void rtw89_core_tx_skb_proto_stats(struct rtw89_traffic_stats *stats,
+					  struct sk_buff *skb)
+{
+	switch (ip_hdr(skb)->protocol) {
+	case IPPROTO_TCP:
+		stats->tcp++;
+		break;
+	case IPPROTO_UDP:
+		stats->udp++;
+		break;
+	default:
+		break;
+	}
+}
+
 static void __rtw89_traffic_stats_accu(struct rtw89_traffic_stats *stats,
 				       struct sk_buff *skb, bool tx)
 {
 	if (tx) {
+		rtw89_core_tx_skb_proto_stats(stats, skb);
 		stats->tx_cnt++;
 		stats->tx_unicast += skb->len;
 	} else {
@@ -2902,6 +2918,23 @@ static void rtw89_core_bcn_track_reset(struct rtw89_dev *rtwdev)
 	memset(&rtwdev->bcn_track, 0, sizeof(rtwdev->bcn_track));
 }
 
+static bool rtw89_core_proto_stats_can_lps(struct rtw89_dev *rtwdev,
+					   struct rtw89_vif *rtwvif,
+					   enum rtw89_tfc_interval interval)
+{
+	if (rtwdev->chip->chip_id != RTL8852A)
+		return true;
+
+	if (rtwvif->burst_active)
+		return false;
+
+	if (interval == RTW89_TFC_INTERVAL_100MS &&
+	    hweight8(rtwvif->stats_ps.active_histogram) < 3)
+		return false;
+
+	return true;
+}
+
 static void rtw89_vif_rx_bcn_stat(struct rtw89_dev *rtwdev, struct sk_buff *skb)
 {
 #define RTW89_APPEND_TSF_2GHZ 384
@@ -4540,9 +4573,42 @@ static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev,
 	return RTW89_TFC_ULTRA_LOW;
 }
 
+static void rtw89_calc_vif_active_histogram(struct rtw89_dev *rtwdev,
+					    struct rtw89_traffic_stats *stats,
+					    enum rtw89_tfc_interval interval)
+{
+	struct rtw89_vif *rtwvif;
+
+	stats->udp_ratio = stats->tx_cnt ?
+			   DIV_ROUND_DOWN_ULL(stats->udp * 100, stats->tx_cnt) : 0;
+	stats->active_histogram <<= 1;
+
+	switch (interval) {
+	case RTW89_TFC_INTERVAL_2SEC:
+		rtwvif = container_of(stats, struct rtw89_vif, stats);
+
+		if (stats->tcp >= RTW89_TCP_TH && stats->tx_cnt >= stats->rx_cnt)
+			stats->active_histogram |= BIT(0);
+
+		if (stats->active_histogram & RTW89_RECENT_ACTIVE_HIST)
+			rtwvif->burst_active = true;
+		else
+			rtwvif->burst_active = false;
+
+		break;
+	case RTW89_TFC_INTERVAL_100MS:
+		if (stats->udp_ratio >= RTW89_UDP_RATIO_TH)
+			stats->active_histogram |= BIT(0);
+		break;
+	}
+
+	stats->tcp = 0;
+	stats->udp = 0;
+}
+
 static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev,
 				     struct rtw89_traffic_stats *stats,
-				     enum rtw89_tfc_interval interval)
+				     enum rtw89_tfc_interval interval, bool by_vif)
 {
 	enum rtw89_tfc_lv tx_tfc_lv = stats->tx_tfc_lv;
 	enum rtw89_tfc_lv rx_tfc_lv = stats->rx_tfc_lv;
@@ -4564,6 +4630,9 @@ static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev,
 	stats->rx_avg_len = stats->rx_cnt ?
 			    DIV_ROUND_DOWN_ULL(stats->rx_unicast, stats->rx_cnt) : 0;
 
+	if (by_vif)
+		rtw89_calc_vif_active_histogram(rtwdev, stats, interval);
+
 	stats->tx_unicast = 0;
 	stats->rx_unicast = 0;
 	stats->tx_cnt = 0;
@@ -4585,11 +4654,11 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev)
 	bool tfc_changed;
 
 	tfc_changed = rtw89_traffic_stats_calc(rtwdev, &rtwdev->stats,
-					       RTW89_TFC_INTERVAL_2SEC);
+					       RTW89_TFC_INTERVAL_2SEC, false);
 
 	rtw89_for_each_rtwvif(rtwdev, rtwvif) {
 		rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats,
-					 RTW89_TFC_INTERVAL_2SEC);
+					 RTW89_TFC_INTERVAL_2SEC, true);
 
 		rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id)
 			rtw89_fw_h2c_tp_offload(rtwdev, rtwvif_link);
@@ -4598,7 +4667,8 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev)
 	return tfc_changed;
 }
 
-static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev)
+static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev,
+				  enum rtw89_tfc_interval interval)
 {
 	struct ieee80211_vif *vif;
 	struct rtw89_vif *rtwvif;
@@ -4622,6 +4692,9 @@ static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev)
 		if (!rtw89_core_bcn_track_can_lps(rtwdev))
 			continue;
 
+		if (!rtw89_core_proto_stats_can_lps(rtwdev, rtwvif, interval))
+			continue;
+
 		rtw89_enter_lps(rtwdev, rtwvif, true);
 	}
 }
@@ -4771,13 +4844,13 @@ static void rtw89_track_ps_work(struct wiphy *wiphy, struct wiphy_work *work)
 
 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
 		rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats_ps,
-					 RTW89_TFC_INTERVAL_100MS);
+					 RTW89_TFC_INTERVAL_100MS, true);
 
 	if (rtwdev->scanning)
 		return;
 
 	if (rtwdev->lps_enabled && !rtwdev->btc.lps)
-		rtw89_enter_lps_track(rtwdev);
+		rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_100MS);
 }
 
 static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work)
@@ -4825,7 +4898,7 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work)
 	rtw89_core_mlo_track(rtwdev);
 
 	if (rtwdev->lps_enabled && !rtwdev->btc.lps)
-		rtw89_enter_lps_track(rtwdev);
+		rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_2SEC);
 }
 
 void rtw89_core_dm_disable_cfg(struct rtw89_dev *rtwdev, u32 new)
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index fd29dbbb120d..bbcac7cc207c 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -1477,6 +1477,10 @@ enum rtw89_tfc_lv {
 
 DECLARE_EWMA(tp, 10, 2);
 
+#define RTW89_TCP_TH 40
+#define RTW89_UDP_RATIO_TH 70
+#define RTW89_RECENT_ACTIVE_HIST GENMASK(4, 0)
+
 struct rtw89_traffic_stats {
 	/* units in bytes */
 	u64 tx_unicast;
@@ -1504,6 +1508,10 @@ struct rtw89_traffic_stats {
 
 	u16 tx_rate;
 	u16 rx_rate;
+
+	/* used by rtwvif only */
+	u64 tcp, udp, udp_ratio;
+	u8 active_histogram;
 };
 
 struct rtw89_btc_chdef {
@@ -6354,6 +6362,7 @@ struct rtw89_vif {
 
 	struct rtw89_roc roc;
 	bool offchan;
+	bool burst_active;
 
 	enum rtw89_mlo_mode mlo_mode;
 	struct rtw89_vif_ml_trans ml_trans;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 03/16] wifi: rtw89: debug: disable hw_scan for latency-sensitive scenarios
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 01/16] wifi: rtw89: 8922d: fix typo rx_freq_frome_ie Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 02/16] wifi: rtw89: 8852a: refine power save to lower latency Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 04/16] wifi: rtw89: debug: disable inactive power save to reduce bus overhead Ping-Ke Shih
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Johnson Tsai <wenjie.tsai@realtek.com>

This helps avoid scan-triggered off-channel activity during
latency-sensitive scenarios.

Add RTW89_DM_HW_SCAN to the disabled_dm bitmap via debugfs and
check it in the hw_scan path. When set, the driver rejects hw_scan
requests and returns -EBUSY.

Signed-off-by: Johnson Tsai <wenjie.tsai@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.h     | 1 +
 drivers/net/wireless/realtek/rtw89/debug.c    | 1 +
 drivers/net/wireless/realtek/rtw89/mac80211.c | 7 +++++++
 3 files changed, 9 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index bbcac7cc207c..4a05a8dbb9ed 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5178,6 +5178,7 @@ enum rtw89_dm_type {
 	RTW89_DM_THERMAL_PROTECT,
 	RTW89_DM_TAS,
 	RTW89_DM_MLO,
+	RTW89_DM_HW_SCAN,
 };
 
 #define RTW89_THERMAL_PROT_LV_MAX 5
diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index 7d8d22311018..02386d1d07c6 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -4347,6 +4347,7 @@ static const struct rtw89_disabled_dm_info {
 	DM_INFO(THERMAL_PROTECT),
 	DM_INFO(TAS),
 	DM_INFO(MLO),
+	DM_INFO(HW_SCAN),
 };
 
 static ssize_t
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 501c3af1da01..9ee2aa225976 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -1241,6 +1241,7 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 	struct rtw89_dev *rtwdev = hw->priv;
 	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
 	struct rtw89_vif_link *rtwvif_link;
+	struct rtw89_hal *hal = &rtwdev->hal;
 	int ret;
 
 	lockdep_assert_wiphy(hw->wiphy);
@@ -1248,6 +1249,12 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 	if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw))
 		return 1;
 
+	if (hal->disabled_dm_bitmap & BIT(RTW89_DM_HW_SCAN)) {
+		rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN,
+			    "reject hw scan due to disabled_dm\n");
+		return -EBUSY;
+	}
+
 	if (rtwdev->scanning || rtwvif->offchan)
 		return -EBUSY;
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 04/16] wifi: rtw89: debug: disable inactive power save to reduce bus overhead
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (2 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 03/16] wifi: rtw89: debug: disable hw_scan for latency-sensitive scenarios Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 05/16] wifi: rtw89: phy: support static PD level setting Ping-Ke Shih
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Johnson Tsai <wenjie.tsai@realtek.com>

This helps avoid excessive bus traffic and I/O overhead on
slower HCIs (e.g., USB, SDIO) caused by frequent power state
transitions.

Add RTW89_DM_INACTIVE_PS to the disabled_dm bitmap via debugfs and
check it in the rtw89_enter_ips path. When set, the driver skips
entering inactive power save and returns immediately.

Signed-off-by: Johnson Tsai <wenjie.tsai@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.h  | 1 +
 drivers/net/wireless/realtek/rtw89/debug.c | 1 +
 drivers/net/wireless/realtek/rtw89/ps.c    | 6 ++++++
 3 files changed, 8 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 4a05a8dbb9ed..4e920472c8aa 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5179,6 +5179,7 @@ enum rtw89_dm_type {
 	RTW89_DM_TAS,
 	RTW89_DM_MLO,
 	RTW89_DM_HW_SCAN,
+	RTW89_DM_INACTIVE_PS,
 };
 
 #define RTW89_THERMAL_PROT_LV_MAX 5
diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index 02386d1d07c6..ebe3b1f9c39d 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -4348,6 +4348,7 @@ static const struct rtw89_disabled_dm_info {
 	DM_INFO(TAS),
 	DM_INFO(MLO),
 	DM_INFO(HW_SCAN),
+	DM_INFO(INACTIVE_PS),
 };
 
 static ssize_t
diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c
index 125cf14fa581..31bb5dcd284a 100644
--- a/drivers/net/wireless/realtek/rtw89/ps.c
+++ b/drivers/net/wireless/realtek/rtw89/ps.c
@@ -234,8 +234,14 @@ void rtw89_enter_ips(struct rtw89_dev *rtwdev)
 {
 	struct rtw89_vif_link *rtwvif_link;
 	struct rtw89_vif *rtwvif;
+	struct rtw89_hal *hal = &rtwdev->hal;
 	unsigned int link_id;
 
+	if (hal->disabled_dm_bitmap & BIT(RTW89_DM_INACTIVE_PS)) {
+		rtw89_debug(rtwdev, RTW89_DBG_PS, "skip enter IPS due to disabled_dm\n");
+		return;
+	}
+
 	set_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags);
 
 	if (!test_bit(RTW89_FLAG_POWERON, rtwdev->flags))
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 05/16] wifi: rtw89: phy: support static PD level setting
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (3 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 04/16] wifi: rtw89: debug: disable inactive power save to reduce bus overhead Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 06/16] wifi: rtw89: Correct data type for scan index to avoid infinite loop Ping-Ke Shih
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Eric Huang <echuang@realtek.com>

PD (Packet Detection) threshold is a key parameter in the DIG
(Dynamic Initial Gain) algorithm that determines the sensitivity
of packet detection. Current implementation only supports dynamic
PD adjustment based on environment. This patch adds support for
static PD threshold via debugfs allowing users to set a fixed PD
value for testing or specific scenarios.

Signed-off-by: Eric Huang <echuang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.h  |  4 +
 drivers/net/wireless/realtek/rtw89/debug.c | 94 ++++++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/phy.c   | 44 +++++++---
 3 files changed, 131 insertions(+), 11 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 4e920472c8aa..8618a7c30ee0 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5180,6 +5180,7 @@ enum rtw89_dm_type {
 	RTW89_DM_MLO,
 	RTW89_DM_HW_SCAN,
 	RTW89_DM_INACTIVE_PS,
+	RTW89_DM_DIG_PD,
 };
 
 #define RTW89_THERMAL_PROT_LV_MAX 5
@@ -5221,6 +5222,9 @@ struct rtw89_hal {
 
 	u8 thermal_prot_th;
 	u8 thermal_prot_lv; /* 0 ~ RTW89_THERMAL_PROT_LV_MAX */
+
+	u8 fixed_dig_pd_th; /* v = (X(dBm) + 102)/2 */
+	s8 fixed_dig_cck_pd_th; /* dBm */
 };
 
 #define RTW89_MAX_MAC_ID_NUM 128
diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index ebe3b1f9c39d..f7672d7e0229 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -87,6 +87,7 @@ struct rtw89_debugfs {
 	struct rtw89_debugfs_priv phy_info;
 	struct rtw89_debugfs_priv stations;
 	struct rtw89_debugfs_priv disable_dm;
+	struct rtw89_debugfs_priv static_pd_th;
 	struct rtw89_debugfs_priv mlo_mode;
 	struct rtw89_debugfs_priv beacon_info;
 	struct rtw89_debugfs_priv diag_mac;
@@ -4349,6 +4350,7 @@ static const struct rtw89_disabled_dm_info {
 	DM_INFO(MLO),
 	DM_INFO(HW_SCAN),
 	DM_INFO(INACTIVE_PS),
+	DM_INFO(DIG_PD),
 };
 
 static ssize_t
@@ -4394,6 +4396,96 @@ rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev,
 	return count;
 }
 
+#define RTW89_DIG_PD_TH_MIN_DBM		-102
+#define RTW89_DIG_PD_TH_MAX_DBM		-40
+#define RTW89_DIG_PD_TH_STEP		2
+
+static inline s8 rtw89_dig_pd_th_to_dbm(u8 reg_val)
+{
+	return RTW89_DIG_PD_TH_MIN_DBM + RTW89_DIG_PD_TH_STEP * reg_val;
+}
+
+static inline u8 rtw89_dig_pd_th_dbm_to_reg(s8 dbm)
+{
+	return (dbm - RTW89_DIG_PD_TH_MIN_DBM) / RTW89_DIG_PD_TH_STEP;
+}
+
+static ssize_t
+rtw89_debug_priv_static_pd_th_get(struct rtw89_dev *rtwdev,
+				  struct rtw89_debugfs_priv *debugfs_priv,
+				  char *buf, size_t bufsz)
+{
+	struct rtw89_hal *hal = &rtwdev->hal;
+	char *p = buf, *end = buf + bufsz;
+	bool disabled;
+	s8 pd_th_dbm;
+	s8 cck_pd_th;
+
+	disabled = hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD);
+
+	if (disabled) {
+		pd_th_dbm = rtw89_dig_pd_th_to_dbm(hal->fixed_dig_pd_th);
+		cck_pd_th = hal->fixed_dig_cck_pd_th;
+
+		p += scnprintf(p, end - p, "DIG: static\n");
+		p += scnprintf(p, end - p, "OFDM PD threshold: %d dBm\n", pd_th_dbm);
+		p += scnprintf(p, end - p, "CCK PD threshold: %d dBm\n", cck_pd_th);
+	} else {
+		p += scnprintf(p, end - p, "DIG: dynamic\n");
+	}
+
+	p += scnprintf(p, end - p, "\nUsage: echo <mode> [pd_th] > static_pd_th\n");
+	p += scnprintf(p, end - p, "  mode: 0 = dynamic, 1 = static\n");
+	p += scnprintf(p, end - p, "  pd_th: PD threshold in dBm (-102 ~ -40)\n");
+
+	return p - buf;
+}
+
+static ssize_t
+rtw89_debug_priv_static_pd_th_set(struct rtw89_dev *rtwdev,
+				  struct rtw89_debugfs_priv *debugfs_priv,
+				  const char *buf, size_t count)
+{
+	struct rtw89_hal *hal = &rtwdev->hal;
+	int ret;
+	u32 mode;
+	s32 pd_th_dbm;
+
+	ret = sscanf(buf, "%u %d", &mode, &pd_th_dbm);
+	if (ret < 1)
+		return -EINVAL;
+
+	if (mode > 1)
+		return -EINVAL;
+
+	if (mode == 0) {
+		rtw89_core_dm_disable_clr(rtwdev, RTW89_DM_DIG_PD);
+		hal->fixed_dig_pd_th = 0;
+		hal->fixed_dig_cck_pd_th = 0;
+
+		rtw89_debug(rtwdev, RTW89_DBG_DIG,
+			    "DIG static mode disabled\n");
+	} else {
+		if (ret < 2 || pd_th_dbm < RTW89_DIG_PD_TH_MIN_DBM ||
+		    pd_th_dbm > RTW89_DIG_PD_TH_MAX_DBM)
+			return -EINVAL;
+
+		rtw89_core_dm_disable_set(rtwdev, RTW89_DM_DIG_PD);
+		hal->fixed_dig_pd_th = clamp(rtw89_dig_pd_th_dbm_to_reg(pd_th_dbm),
+					     0, 0x1f);
+		/* CCK uses dBm directly */
+		hal->fixed_dig_cck_pd_th = pd_th_dbm;
+
+		rtw89_debug(rtwdev, RTW89_DBG_DIG,
+			    "DIG static mode: PD=0x%02x (%d dBm), CCK=%d dBm\n",
+			    hal->fixed_dig_pd_th,
+			    rtw89_dig_pd_th_to_dbm(hal->fixed_dig_pd_th),
+			    hal->fixed_dig_cck_pd_th);
+	}
+
+	return count;
+}
+
 static void rtw89_debug_mlo_mode_set_mlsr(struct rtw89_dev *rtwdev,
 					  unsigned int link_id)
 {
@@ -4882,6 +4974,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = {
 	.phy_info = rtw89_debug_priv_get(phy_info),
 	.stations = rtw89_debug_priv_get(stations, RLOCK),
 	.disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK),
+	.static_pd_th = rtw89_debug_priv_set_and_get(static_pd_th, RWLOCK),
 	.mlo_mode = rtw89_debug_priv_set_and_get(mlo_mode, RWLOCK),
 	.beacon_info = rtw89_debug_priv_get(beacon_info),
 	.diag_mac = rtw89_debug_priv_get(diag_mac, RSIZE_16K, RLOCK),
@@ -4930,6 +5023,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top
 	rtw89_debugfs_add_r(phy_info);
 	rtw89_debugfs_add_r(stations);
 	rtw89_debugfs_add_rw(disable_dm);
+	rtw89_debugfs_add_rw(static_pd_th);
 	rtw89_debugfs_add_rw(mlo_mode);
 	rtw89_debugfs_add_r(beacon_info);
 	rtw89_debugfs_add_r(diag_mac);
diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index e70d0e283987..f26cbde6bdd0 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -7162,11 +7162,19 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev,
 	const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx);
 	const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs;
 	struct rtw89_dig_info *dig = &bb->dig;
+	struct rtw89_hal *hal = &rtwdev->hal;
 	u8 final_rssi, under_region = dig->pd_low_th_ofst;
 	s8 cck_cca_th;
 	u32 pd_val;
 
-	pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi, enable, chan);
+	if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) {
+		pd_val = hal->fixed_dig_pd_th;
+
+		rtw89_debug(rtwdev, RTW89_DBG_DIG,
+			    "Fixed DIG: PD_low=%d\n", pd_val);
+	} else {
+		pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi, enable, chan);
+	}
 	dig->bak_dig = pd_val;
 
 	rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg,
@@ -7174,17 +7182,24 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev,
 	rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg,
 			      dig_regs->pd_spatial_reuse_en, enable, bb->phy_idx);
 
-	if (!rtwdev->hal.support_cckpd)
+	if (!hal->support_cckpd)
 		return;
 
-	final_rssi = min_t(u8, rssi, dig->igi_rssi);
-	under_region = rtw89_phy_dig_cal_under_region(rtwdev, bb, chan);
-	cck_cca_th = max_t(s8, final_rssi - under_region, CCKPD_TH_MIN_RSSI);
-	pd_val = (u32)(cck_cca_th - IGI_RSSI_MAX);
+	if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) {
+		pd_val = hal->fixed_dig_cck_pd_th;
 
-	rtw89_debug(rtwdev, RTW89_DBG_DIG,
-		    "igi=%d, cck_ccaTH=%d, backoff=%d, cck_PD_low=((%d))dB\n",
-		    final_rssi, cck_cca_th, under_region, pd_val);
+		rtw89_debug(rtwdev, RTW89_DBG_DIG,
+			    "Fixed DIG: cck_PD_low=((%d))dB\n", pd_val);
+	} else {
+		final_rssi = min_t(u8, rssi, dig->igi_rssi);
+		under_region = rtw89_phy_dig_cal_under_region(rtwdev, bb, chan);
+		cck_cca_th = max_t(s8, final_rssi - under_region, CCKPD_TH_MIN_RSSI);
+		pd_val = (u32)(cck_cca_th - IGI_RSSI_MAX);
+
+		rtw89_debug(rtwdev, RTW89_DBG_DIG,
+			    "igi=%d, cck_ccaTH=%d, backoff=%d, cck_PD_low=((%d))dB\n",
+			    final_rssi, cck_cca_th, under_region, pd_val);
+	}
 
 	rtw89_phy_write32_idx(rtwdev, dig_regs->bmode_pd_reg,
 			      dig_regs->bmode_cca_rssi_limit_en, enable, bb->phy_idx);
@@ -7315,6 +7330,7 @@ static void rtw89_phy_dig_ctrl(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb
 {
 	const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs;
 	struct rtw89_dig_info *dig = &bb->dig;
+	struct rtw89_hal *hal = &rtwdev->hal;
 	bool en_dig;
 	u32 pd_val;
 
@@ -7323,10 +7339,16 @@ static void rtw89_phy_dig_ctrl(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb
 
 	if (pause_dig) {
 		en_dig = false;
-		pd_val = 0;
+		if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD))
+			pd_val = hal->fixed_dig_pd_th;
+		else
+			pd_val = 0;
 	} else {
 		en_dig = rtwdev->total_sta_assoc > 0;
-		pd_val = restore ? dig->bak_dig : 0;
+		if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD))
+			pd_val = hal->fixed_dig_pd_th;
+		else
+			pd_val = restore ? dig->bak_dig : 0;
 	}
 
 	rtw89_debug(rtwdev, RTW89_DBG_DIG, "%s <%s> PD_low=%d", __func__,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 06/16] wifi: rtw89: Correct data type for scan index to avoid infinite loop
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (4 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 05/16] wifi: rtw89: phy: support static PD level setting Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 07/16] wifi: rtw89: 8852bt: configure support_noise field explicitly Ping-Ke Shih
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Shin-Yi Lin <isaiah@realtek.com>

A kernel soft lockup was observed during Wi-Fi scanning on the 6GHz band.
The CPU becomes stuck in rtw89_hw_scan_add_chan_ax for over 20 seconds,
leading to a system panic.

RIP points to 0f b6 c3 (movzbl %bl, %eax), which zero-extends
the low 8 bits of RBX into RAX.
RBX (the counter i) has reached a huge value: 0x137466a1.

  watchdog: BUG: soft lockup - CPU#2 stuck for 26s! [kworker/u16:4:6124]
  Workqueue: events_unbound cfg80211_wiphy_work [cfg80211]
  RIP: 0010:rtw89_hw_scan_add_chan_ax+0xb3/0x6e0 [rtw89_core]
  Code: a0 48 89 45 a8 44 89 6d 9c 44 89 75 98 eb 29 66 66 2e 0f 1f
  84 00 00 00 00 00 66 66 2e 0f 1f 84 00 00 00 00 00 66 90 83 c3 01
  <0f> b6 c3 41 3b 44 24 74 0f 83 0b 02 00 00 0f b6 c3 48 8d 14 80 49
  RSP: 0018:ffffcb48cbaa39f8 EFLAGS: 00000202
  RAX: 0000000000000005 RBX: 00000000137466a1 RCX: 0000000000000000
  RDX: ffff89ffc9d851a8 RSI: 0000000000004f0d RDI: 0000000096af0130
  RBP: ffffcb48cbaa3a60 R08: 0000000000000000 R09: ffff8a00b7502080
  R10: ffff8a00b75ff600 R11: 0000000000000000 R12: ffff89ffc7553870
  R13: ffff8a00b7ac8f19 R14: ffff8a00b75020d8 R15: ffff89ffc3d54d80
  FS:  0000000000000000(0000) GS:ffff8a014f962000(0000)
  knlGS:0000000000000000
  CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
  CR2: 00007558d7f9f4c4 CR3: 0000000178040001 CR4: 00000000001706f0
  Call Trace:
   <TASK>
   rtw89_hw_scan_prep_chan_list_ax+0x8a/0x400 [rtw89_core]
   rtw89_hw_scan_start+0x546/0x8a0 [rtw89_core]
   ? rtw89_fw_h2c_default_cmac_tbl+0x13c/0x1f0 [rtw89_core]
   rtw89_ops_hw_scan+0xae/0x120 [rtw89_core]
   drv_hw_scan+0xbb/0x180 [mac80211]
   __ieee80211_start_scan+0x2fc/0x750 [mac80211]
   ieee80211_request_scan+0xe/0x20 [mac80211]
   ieee80211_scan+0x123/0x190 [mac80211]
   rdev_scan+0x40/0x110 [cfg80211]
   cfg80211_scan_6ghz+0x5a1/0xa30 [cfg80211]

By objdump with source:

	for (i = 0; i < req->n_6ghz_params; i++) {
   5fbc0:	83 c3 01             	add    $0x1,%ebx --> i++
   5fbc3:	0f b6 c3             	movzbl %bl,%eax  --> get counter
   fbc6:	41 3b 44 24 74       	cmp    0x74(%r12),%eax

   * RBX: 00000000137466a1 -> %bl = a1 -> EAX = 000000a1 (161)

Fixes: c6aa9a9c4725 ("wifi: rtw89: add RNR support for 6 GHz scan")
Signed-off-by: Shin-Yi Lin <isaiah@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/fw.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 17704f054727..089c9071b58f 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -8318,7 +8318,7 @@ static int rtw89_update_6ghz_rnr_chan_ax(struct rtw89_dev *rtwdev,
 	struct sk_buff *skb;
 	bool found;
 	int ret = 0;
-	u8 i;
+	u32 i;
 
 	if (!req->n_6ghz_params)
 		return 0;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 07/16] wifi: rtw89: 8852bt: configure support_noise field explicitly
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (5 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 06/16] wifi: rtw89: Correct data type for scan index to avoid infinite loop Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 08/16] wifi: rtw89: add IO offload support via firmware Ping-Ke Shih
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Zong-Zhe Yang <kevin_yang@realtek.com>

RTL8852BT missed to configure support_noise field in chip_info.
Although RTL8852BT doesn't support that, add the configuration
to make things clear.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/rtw8852bt.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
index ab4263bc8b9f..0d980fcd22d4 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
@@ -811,6 +811,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {
 	.support_ant_gain	= true,
 	.support_tas		= false,
 	.support_sar_by_ant	= true,
+	.support_noise		= false,
 	.ul_tb_waveform_ctrl	= true,
 	.ul_tb_pwr_diff		= false,
 	.rx_freq_from_ie	= true,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 08/16] wifi: rtw89: add IO offload support via firmware
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (6 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 07/16] wifi: rtw89: 8852bt: configure support_noise field explicitly Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 09/16] wifi: rtw89: offload DMAC and CMAC init IO to firmware Ping-Ke Shih
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Chia-Yuan Li <leo.li@realtek.com>

Offload register write operations to the firmware to improve IO efficiency.
Instead of the host performing direct bus access for every register, the
operations are now encapsulated into firmware commands.

This implementation allows the host to aggregate multiple register write
actions into a single command buffer. By batching these operations, we
reduce the number of individual transactions on the bus and minimize
per-transfer overhead, leading to faster overall IO performance.

Add a support_fw_cmd_ofld field to the chip_info structure to control
whether firmware-offloaded batch writes are enabled. Enable it by
default for RTL8832CU.

Co-developed-by: Eric Huang <echuang@realtek.com>
Signed-off-by: Eric Huang <echuang@realtek.com>
Co-developed-by: Johnson Tsai <wenjie.tsai@realtek.com>
Signed-off-by: Johnson Tsai <wenjie.tsai@realtek.com>
Signed-off-by: Chia-Yuan Li <leo.li@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c     |   2 +
 drivers/net/wireless/realtek/rtw89/core.h     |  82 ++++-
 drivers/net/wireless/realtek/rtw89/fw.c       | 347 ++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/fw.h       |  57 +++
 drivers/net/wireless/realtek/rtw89/phy.h      |  30 +-
 drivers/net/wireless/realtek/rtw89/rtw8851b.c |   1 +
 drivers/net/wireless/realtek/rtw89/rtw8852a.c |   1 +
 drivers/net/wireless/realtek/rtw89/rtw8852b.c |   1 +
 .../net/wireless/realtek/rtw89/rtw8852bt.c    |   1 +
 drivers/net/wireless/realtek/rtw89/rtw8852c.c |   1 +
 drivers/net/wireless/realtek/rtw89/rtw8922a.c |   1 +
 drivers/net/wireless/realtek/rtw89/rtw8922d.c |   1 +
 12 files changed, 515 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index c9c4ec1d93af..7fe979e47ad5 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -6302,6 +6302,8 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
 	struct rtw89_btc *btc = &rtwdev->btc;
 	u8 band;
 
+	rtwdev->io = rtw89_fw_cmd_ofld_alloc_and_get_io_ops(rtwdev);
+
 	bitmap_or(rtwdev->quirks, rtwdev->quirks, &rtwdev->chip->default_quirks,
 		  NUM_OF_RTW89_QUIRKS);
 
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 8618a7c30ee0..6a8ad5d91130 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -27,6 +27,7 @@ struct rtw89_phy_calc_efuse_gain;
 struct rtw89_debugfs;
 struct rtw89_regd_data;
 struct rtw89_wow_cam_info;
+struct rtw89_fw_cmd_ofld_info;
 
 extern const struct ieee80211_ops rtw89_ops;
 
@@ -4569,6 +4570,7 @@ struct rtw89_chip_info {
 	bool support_tas;
 	bool support_sar_by_ant;
 	bool support_noise;
+	bool support_fw_cmd_ofld;
 	bool ul_tb_waveform_ctrl;
 	bool ul_tb_pwr_diff;
 	bool rx_freq_from_ie;
@@ -6202,10 +6204,26 @@ struct rtw89_tid_stats {
 	bool started;
 };
 
+struct rtw89_io_ops {
+	int (*pack)(struct rtw89_dev *rtwdev);
+	int (*unpack)(struct rtw89_dev *rtwdev);
+	void (*do_udelay)(struct rtw89_dev *rtwdev, u32 us);
+	void (*do_mdelay)(struct rtw89_dev *rtwdev, u32 ms);
+	void (*write8)(struct rtw89_dev *rtwdev, u32 addr, u8 data);
+	void (*write16)(struct rtw89_dev *rtwdev, u32 addr, u16 data);
+	void (*write32)(struct rtw89_dev *rtwdev, u32 addr, u32 data);
+	void (*phy_write8)(struct rtw89_dev *rtwdev, u32 addr, u8 data);
+	void (*phy_write16)(struct rtw89_dev *rtwdev, u32 addr, u16 data);
+	void (*phy_write32)(struct rtw89_dev *rtwdev, u32 addr, u32 data);
+	void (*write_rf)(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
+			 u32 addr, u32 mask, u32 data);
+};
+
 struct rtw89_dev {
 	struct ieee80211_hw *hw;
 	struct device *dev;
 	const struct ieee80211_ops *ops;
+	const struct rtw89_io_ops *io;
 
 	bool dbcc_en;
 	bool support_mlo;
@@ -6227,6 +6245,8 @@ struct rtw89_dev {
 	struct rtw89_rfe_data *rfe_data;
 	enum rtw89_custid custid;
 
+	struct rtw89_fw_cmd_ofld_info *fw_cmd_ofld_info;
+
 	struct rtw89_sta_link __rcu *assoc_link_on_macid[RTW89_MAX_MAC_ID_NUM];
 	refcount_t refcount_ap_info;
 
@@ -6688,6 +6708,38 @@ struct rtw89_tx_skb_data *RTW89_TX_SKB_CB(struct sk_buff *skb)
 	return (struct rtw89_tx_skb_data *)info->driver_data;
 }
 
+static inline int rtw89_io_pack(struct rtw89_dev *rtwdev)
+{
+	if (rtwdev->io->pack)
+		return rtwdev->io->pack(rtwdev);
+
+	return 0;
+}
+
+static inline int rtw89_io_unpack(struct rtw89_dev *rtwdev)
+{
+	if (rtwdev->io->unpack)
+		return rtwdev->io->unpack(rtwdev);
+
+	return 0;
+}
+
+static inline void rtw89_io_udelay(struct rtw89_dev *rtwdev, u32 us)
+{
+	if (rtwdev->io->do_udelay)
+		rtwdev->io->do_udelay(rtwdev, us);
+	else
+		udelay(us);
+}
+
+static inline void rtw89_io_mdelay(struct rtw89_dev *rtwdev, u32 ms)
+{
+	if (rtwdev->io->do_mdelay)
+		rtwdev->io->do_mdelay(rtwdev, ms);
+	else
+		mdelay(ms);
+}
+
 static inline u8 rtw89_read8(struct rtw89_dev *rtwdev, u32 addr)
 {
 	return rtwdev->hci.ops->read8(rtwdev, addr);
@@ -6703,21 +6755,36 @@ static inline u32 rtw89_read32(struct rtw89_dev *rtwdev, u32 addr)
 	return rtwdev->hci.ops->read32(rtwdev, addr);
 }
 
-static inline void rtw89_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data)
+static inline void rtw89_raw_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data)
 {
 	rtwdev->hci.ops->write8(rtwdev, addr, data);
 }
 
-static inline void rtw89_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data)
+static inline void rtw89_raw_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data)
 {
 	rtwdev->hci.ops->write16(rtwdev, addr, data);
 }
 
-static inline void rtw89_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data)
+static inline void rtw89_raw_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data)
 {
 	rtwdev->hci.ops->write32(rtwdev, addr, data);
 }
 
+static inline void rtw89_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data)
+{
+	rtwdev->io->write8(rtwdev, addr, data);
+}
+
+static inline void rtw89_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data)
+{
+	rtwdev->io->write16(rtwdev, addr, data);
+}
+
+static inline void rtw89_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data)
+{
+	rtwdev->io->write32(rtwdev, addr, data);
+}
+
 static inline void
 rtw89_write8_set(struct rtw89_dev *rtwdev, u32 addr, u8 bit)
 {
@@ -6862,13 +6929,20 @@ rtw89_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
 	return rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask);
 }
 
+static inline void
+rtw89_raw_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
+		   u32 addr, u32 mask, u32 data)
+{
+	rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data);
+}
+
 static inline void
 rtw89_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
 	       u32 addr, u32 mask, u32 data)
 {
 	lockdep_assert_wiphy(rtwdev->hw->wiphy);
 
-	rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data);
+	rtwdev->io->write_rf(rtwdev, rf_path, addr, mask, data);
 }
 
 static inline u32 rtw89_read32_pci_cfg(struct rtw89_dev *rtwdev, u32 addr)
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 089c9071b58f..4c10f069e242 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -11344,3 +11344,350 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,
 
 	return parms;
 }
+
+static int rtw89_fw_cmd_ofld_pack(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info;
+
+	lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+	info->pack_level++;
+
+	return 0;
+}
+
+static void rtw89_fw_cmd_ofld_flush(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info;
+	struct sk_buff *skb;
+	int ret;
+	u32 len;
+
+	len = info->cnt * sizeof(info->cmds[0]);
+	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+	if (!skb) {
+		rtw89_err(rtwdev, "alloc skb fail\n");
+		return;
+	}
+
+	skb_put_data(skb, info->cmds, len);
+
+	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+			      H2C_CAT_MAC,
+			      H2C_CL_MAC_FW_OFLD,
+			      H2C_FUNC_CMD_OFLD_PKT, 0, 0,
+			      len);
+
+	ret = rtw89_h2c_tx(rtwdev, skb, false);
+	if (ret) {
+		rtw89_err(rtwdev, "failed to send cmd ofld\n");
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	if (info->accu_delay)
+		fsleep(info->accu_delay);
+
+	info->cnt = 0;
+	info->accu_delay = 0;
+}
+
+static int rtw89_fw_cmd_ofld_unpack(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info;
+
+	lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+	if (info->pack_level == 0)
+		return -EFAULT;
+
+	if (--info->pack_level != 0)
+		return 0;
+
+	if (info->cnt == 0)
+		return 0;
+
+	rtw89_fw_cmd_ofld_flush(rtwdev);
+
+	return 0;
+}
+
+static int rtw89_fw_cmd_ofld_enqueue(struct rtw89_dev *rtwdev,
+				     const struct rtw89_fw_cmd_ofld_arg *cmd)
+{
+	struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info;
+	struct rtw89_h2c_cmd_ofld *h2c;
+	u32 base_off;
+
+	lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+	if (info->pack_level == 0)
+		return 1;
+
+	if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags))
+		return -EBUSY;
+
+	if (cmd->type != RTW89_FW_CMD_OFLD_DELAY &&
+	    cmd->src != RTW89_FW_CMD_OFLD_SRC_RF &&
+	    cmd->src != RTW89_FW_CMD_OFLD_SRC_RF_DDIE &&
+	    !IS_ALIGNED(cmd->offset, 4))
+		return -EFAULT;
+
+	if (info->cnt >= ARRAY_SIZE(info->cmds))
+		rtw89_fw_cmd_ofld_flush(rtwdev);
+
+	h2c = &info->cmds[info->cnt++];
+
+	h2c->w0 = le32_encode_bits(cmd->src, RTW89_H2C_CMD_OFLD_W0_SRC) |
+		  le32_encode_bits(cmd->type, RTW89_H2C_CMD_OFLD_W0_TYPE) |
+		  le32_encode_bits(cmd->rf_path, RTW89_H2C_CMD_OFLD_W0_PATH) |
+		  le32_encode_bits(info->cnt, RTW89_H2C_CMD_OFLD_W0_CMD_NUM) |
+		  le32_encode_bits(cmd->offset, RTW89_H2C_CMD_OFLD_W0_OFFSET);
+
+	if (cmd->src == RTW89_FW_CMD_OFLD_SRC_RF_DDIE)
+		base_off = 1;
+	else if (cmd->src == RTW89_FW_CMD_OFLD_SRC_RF)
+		base_off = 0;
+	else
+		base_off = cmd->offset >> 16;
+
+	h2c->w1 = le32_encode_bits(cmd->id, RTW89_H2C_CMD_OFLD_W1_ID) |
+		  le32_encode_bits(base_off, RTW89_H2C_CMD_OFLD_W1_BASE_OFFSET);
+	h2c->w2 = le32_encode_bits(cmd->value, RTW89_H2C_CMD_OFLD_W2_VALUE);
+	h2c->w3 = le32_encode_bits(cmd->mask, RTW89_H2C_CMD_OFLD_W3_MASK);
+
+	if (cmd->type == RTW89_FW_CMD_OFLD_DELAY)
+		info->accu_delay += cmd->value;
+
+	return 0;
+}
+
+static const struct rtw89_io_ops rtw89_raw_io = {
+	.pack = NULL,
+	.unpack = NULL,
+	.write8 = rtw89_raw_write8,
+	.write16 = rtw89_raw_write16,
+	.write32 = rtw89_raw_write32,
+	.phy_write8 = rtw89_raw_phy_write8,
+	.phy_write16 = rtw89_raw_phy_write16,
+	.phy_write32 = rtw89_raw_phy_write32,
+	.write_rf = rtw89_raw_write_rf,
+};
+
+static
+void rtw89_fw_cmd_ofld_phy_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data)
+{
+	struct rtw89_fw_cmd_ofld_arg cmd = {
+		.src = RTW89_FW_CMD_OFLD_SRC_BB,
+		.type = RTW89_FW_CMD_OFLD_WRITE,
+		.offset = ALIGN_DOWN(addr, 4),
+		.mask = RTW89_W8_MASK_OF_ALIGNED_ADDR(addr),
+		.value = data,
+	};
+	int ret;
+
+	ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd);
+	if (ret)
+		rtw89_raw_io.phy_write8(rtwdev, addr, data);
+}
+
+static
+void rtw89_fw_cmd_ofld_phy_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data)
+{
+	struct rtw89_fw_cmd_ofld_arg cmd = {
+		.src = RTW89_FW_CMD_OFLD_SRC_BB,
+		.type = RTW89_FW_CMD_OFLD_WRITE,
+		.offset = ALIGN_DOWN(addr, 2),
+		.mask = RTW89_W16_MASK_OF_ALIGNED_ADDR(addr),
+		.value = data,
+	};
+	int ret;
+
+	ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd);
+	if (ret)
+		rtw89_raw_io.phy_write16(rtwdev, addr, data);
+}
+
+static
+void rtw89_fw_cmd_ofld_phy_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data)
+{
+	struct rtw89_fw_cmd_ofld_arg cmd = {
+		.src = RTW89_FW_CMD_OFLD_SRC_BB,
+		.type = RTW89_FW_CMD_OFLD_WRITE,
+		.offset = addr,
+		.mask = MASKDWORD,
+		.value = data,
+	};
+	int ret;
+
+	ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd);
+	if (ret)
+		rtw89_raw_io.phy_write32(rtwdev, addr, data);
+}
+
+static
+void rtw89_fw_cmd_ofld_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data)
+{
+	struct rtw89_fw_cmd_ofld_arg cmd = {
+		.src = RTW89_FW_CMD_OFLD_SRC_MAC,
+		.type = RTW89_FW_CMD_OFLD_WRITE,
+		.offset = ALIGN_DOWN(addr, 4),
+		.mask = RTW89_W8_MASK_OF_ALIGNED_ADDR(addr),
+		.value = data,
+	};
+	int ret;
+
+	ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd);
+	if (ret)
+		rtw89_raw_io.write8(rtwdev, addr, data);
+}
+
+static
+void rtw89_fw_cmd_ofld_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data)
+{
+	struct rtw89_fw_cmd_ofld_arg cmd = {
+		.src = RTW89_FW_CMD_OFLD_SRC_MAC,
+		.type = RTW89_FW_CMD_OFLD_WRITE,
+		.offset = ALIGN_DOWN(addr, 2),
+		.mask = RTW89_W16_MASK_OF_ALIGNED_ADDR(addr),
+		.value = data,
+	};
+	int ret;
+
+	ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd);
+	if (ret)
+		rtw89_raw_io.write16(rtwdev, addr, data);
+}
+
+static
+void rtw89_fw_cmd_ofld_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data)
+{
+	struct rtw89_fw_cmd_ofld_arg cmd = {
+		.src = RTW89_FW_CMD_OFLD_SRC_MAC,
+		.type = RTW89_FW_CMD_OFLD_WRITE,
+		.offset = addr,
+		.mask = MASKDWORD,
+		.value = data,
+	};
+	int ret;
+
+	ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd);
+	if (ret)
+		rtw89_raw_io.write32(rtwdev, addr, data);
+}
+
+static void rtw89_fw_cmd_ofld_write_rf_ddv(struct rtw89_dev *rtwdev,
+					   struct rtw89_fw_cmd_ofld_arg *cmd,
+					   enum rtw89_rf_path rf_path, u32 addr, u32 mask,
+					   u32 data)
+{
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+
+	*cmd = (typeof(*cmd)){
+		.src = RTW89_FW_CMD_OFLD_SRC_BB,
+		.type = RTW89_FW_CMD_OFLD_WRITE,
+		.rf_path = 0, /* Set rf_path to zero in rf_ddv */
+		.offset = chip->rf_base_addr[rf_path] + ((addr & 0xff) << 2),
+		.mask = mask,
+		.value = data,
+	};
+}
+
+static void rtw89_fw_cmd_ofld_write_rf_dav(struct rtw89_dev *rtwdev,
+					   struct rtw89_fw_cmd_ofld_arg *cmd,
+					   enum rtw89_rf_path rf_path, u32 addr, u32 mask,
+					   u32 data)
+{
+	*cmd = (typeof(*cmd)){
+		.src = RTW89_FW_CMD_OFLD_SRC_RF,
+		.type = RTW89_FW_CMD_OFLD_WRITE,
+		.rf_path = rf_path,
+		.offset = addr,
+		.mask = mask,
+		.value = data,
+	};
+}
+
+static void rtw89_fw_cmd_ofld_write_rf(struct rtw89_dev *rtwdev,
+				       enum rtw89_rf_path rf_path, u32 addr, u32 mask,
+				       u32 data)
+{
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	bool ad_sel = u32_get_bits(addr, RTW89_RF_ADDR_ADSEL_MASK);
+	struct rtw89_fw_cmd_ofld_arg cmd;
+	int ret;
+
+	if (unlikely(rf_path >= chip->rf_path_num)) {
+		rtw89_warn(rtwdev, "unsupported rf path (%d) in fw offload\n", rf_path);
+		return;
+	}
+
+	if (ad_sel)
+		rtw89_fw_cmd_ofld_write_rf_ddv(rtwdev, &cmd, rf_path, addr, mask, data);
+	else
+		rtw89_fw_cmd_ofld_write_rf_dav(rtwdev, &cmd, rf_path, addr, mask, data);
+
+	ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd);
+	if (ret)
+		rtw89_raw_io.write_rf(rtwdev, rf_path, addr, mask, data);
+}
+
+static void rtw89_fw_cmd_ofld_udelay(struct rtw89_dev *rtwdev, u32 us)
+{
+	struct rtw89_fw_cmd_ofld_arg cmd = {
+		.src = RTW89_FW_CMD_OFLD_SRC_OTHER,
+		.type = RTW89_FW_CMD_OFLD_DELAY,
+		.value = us,
+	};
+	int ret;
+
+	ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd);
+	if (ret)
+		udelay(us);
+}
+
+static void rtw89_fw_cmd_ofld_mdelay(struct rtw89_dev *rtwdev, u32 ms)
+{
+	struct rtw89_fw_cmd_ofld_arg cmd = {
+		.src = RTW89_FW_CMD_OFLD_SRC_OTHER,
+		.type = RTW89_FW_CMD_OFLD_DELAY,
+		.value = ms * 1000,
+	};
+	int ret;
+
+	ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd);
+	if (ret)
+		mdelay(ms);
+}
+
+static const struct rtw89_io_ops rtw89_fw_cmd_ofld_io = {
+	.pack = rtw89_fw_cmd_ofld_pack,
+	.unpack = rtw89_fw_cmd_ofld_unpack,
+	.do_udelay = rtw89_fw_cmd_ofld_udelay,
+	.do_mdelay = rtw89_fw_cmd_ofld_mdelay,
+	.write8 = rtw89_fw_cmd_ofld_write8,
+	.write16 = rtw89_fw_cmd_ofld_write16,
+	.write32 = rtw89_fw_cmd_ofld_write32,
+	.phy_write8 = rtw89_fw_cmd_ofld_phy_write8,
+	.phy_write16 = rtw89_fw_cmd_ofld_phy_write16,
+	.phy_write32 = rtw89_fw_cmd_ofld_phy_write32,
+	.write_rf = rtw89_fw_cmd_ofld_write_rf,
+};
+
+const struct rtw89_io_ops *
+rtw89_fw_cmd_ofld_alloc_and_get_io_ops(struct rtw89_dev *rtwdev)
+{
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	struct rtw89_fw_cmd_ofld_info *info;
+
+	if (!(rtwdev->hci.type == RTW89_HCI_TYPE_USB && chip->support_fw_cmd_ofld))
+		return &rtw89_raw_io;
+
+	info = devm_kzalloc(rtwdev->dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return &rtw89_raw_io;
+
+	rtwdev->fw_cmd_ofld_info = info;
+
+	return &rtw89_fw_cmd_ofld_io;
+}
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index db252d45e498..4d94a7195708 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -3115,6 +3115,59 @@ struct rtw89_h2c_trx_protect {
 #define RTW89_H2C_TRX_PROTECT_W1_CHINFO_EN BIT(0)
 #define RTW89_H2C_TRX_PROTECT_W1_DFS_EN BIT(1)
 
+enum rtw89_fw_cmd_ofld_arg_src {
+	RTW89_FW_CMD_OFLD_SRC_BB,
+	RTW89_FW_CMD_OFLD_SRC_RF,
+	RTW89_FW_CMD_OFLD_SRC_MAC,
+	RTW89_FW_CMD_OFLD_SRC_RF_DDIE,
+	RTW89_FW_CMD_OFLD_SRC_OTHER,
+};
+
+enum rtw89_fw_cmd_ofld_arg_type {
+	RTW89_FW_CMD_OFLD_WRITE,
+	RTW89_FW_CMD_OFLD_COMPARE,
+	RTW89_FW_CMD_OFLD_DELAY,
+	RTW89_FW_CMD_OFLD_MOVE,
+};
+
+struct rtw89_fw_cmd_ofld_arg {
+	enum rtw89_fw_cmd_ofld_arg_src src;
+	enum rtw89_fw_cmd_ofld_arg_type type;
+	enum rtw89_rf_path rf_path;
+	u32 value;
+	u32 mask;
+	u32 offset;
+	u16 id;
+};
+
+struct rtw89_h2c_cmd_ofld {
+	__le32 w0;
+	__le32 w1;
+	__le32 w2;
+	__le32 w3;
+} __packed;
+
+#define RTW89_H2C_CMD_OFLD_W0_SRC GENMASK(1, 0)
+#define RTW89_H2C_CMD_OFLD_W0_TYPE GENMASK(3, 2)
+#define RTW89_H2C_CMD_OFLD_W0_LC BIT(4)
+#define RTW89_H2C_CMD_OFLD_W0_PATH GENMASK(6, 5)
+#define RTW89_H2C_CMD_OFLD_W0_CMD_NUM GENMASK(14, 8)
+#define RTW89_H2C_CMD_OFLD_W0_OFFSET GENMASK(31, 16)
+#define RTW89_H2C_CMD_OFLD_W1_ID GENMASK(15, 0)
+#define RTW89_H2C_CMD_OFLD_W1_BASE_OFFSET GENMASK(31, 16)
+#define RTW89_H2C_CMD_OFLD_W2_VALUE GENMASK(31, 0)
+#define RTW89_H2C_CMD_OFLD_W3_MASK GENMASK(31, 0)
+#define RTW89_W8_MASK_OF_ALIGNED_ADDR(offset) (0xff << (((offset) & 0x3) << 3))
+#define RTW89_W16_MASK_OF_ALIGNED_ADDR(offset) (0xffff << (((offset) & 0x2) * 8))
+
+#define RTW89_FW_CMD_OFLD_NR 125
+struct rtw89_fw_cmd_ofld_info {
+	unsigned int pack_level;
+	u32 cnt;
+	u32 accu_delay;
+	struct rtw89_h2c_cmd_ofld cmds[RTW89_FW_CMD_OFLD_NR];
+};
+
 struct rtw89_h2c_fwips {
 	__le32 w0;
 } __packed;
@@ -4606,6 +4659,7 @@ enum rtw89_fw_ofld_h2c_func {
 	H2C_FUNC_MAC_MACID_PAUSE	= 0x8,
 	H2C_FUNC_USR_EDCA		= 0xF,
 	H2C_FUNC_TSF32_TOGL		= 0x10,
+	H2C_FUNC_CMD_OFLD_PKT		= 0x13,
 	H2C_FUNC_OFLD_CFG		= 0x14,
 	H2C_FUNC_ADD_SCANOFLD_CH	= 0x16,
 	H2C_FUNC_SCANOFLD		= 0x17,
@@ -5737,4 +5791,7 @@ enum rtw89_wow_wakeup_ver {
 	RTW89_WOW_REASON_NUM,
 };
 
+const struct rtw89_io_ops *
+rtw89_fw_cmd_ofld_alloc_and_get_io_ops(struct rtw89_dev *rtwdev);
+
 #endif
diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h
index bde419edf744..3f9d306ff1ca 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.h
+++ b/drivers/net/wireless/realtek/rtw89/phy.h
@@ -576,30 +576,48 @@ extern const struct rtw89_phy_gen_def rtw89_phy_gen_ax;
 extern const struct rtw89_phy_gen_def rtw89_phy_gen_be;
 extern const struct rtw89_phy_gen_def rtw89_phy_gen_be_v1;
 
-static inline void rtw89_phy_write8(struct rtw89_dev *rtwdev,
-				    u32 addr, u8 data)
+static inline void rtw89_raw_phy_write8(struct rtw89_dev *rtwdev,
+					u32 addr, u8 data)
 {
 	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
 
 	rtw89_write8(rtwdev, addr + phy->cr_base, data);
 }
 
-static inline void rtw89_phy_write16(struct rtw89_dev *rtwdev,
-				     u32 addr, u16 data)
+static inline void rtw89_raw_phy_write16(struct rtw89_dev *rtwdev,
+					 u32 addr, u16 data)
 {
 	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
 
 	rtw89_write16(rtwdev, addr + phy->cr_base, data);
 }
 
-static inline void rtw89_phy_write32(struct rtw89_dev *rtwdev,
-				     u32 addr, u32 data)
+static inline void rtw89_raw_phy_write32(struct rtw89_dev *rtwdev,
+					 u32 addr, u32 data)
 {
 	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
 
 	rtw89_write32(rtwdev, addr + phy->cr_base, data);
 }
 
+static inline void rtw89_phy_write8(struct rtw89_dev *rtwdev,
+				    u32 addr, u8 data)
+{
+	rtwdev->io->phy_write8(rtwdev, addr, data);
+}
+
+static inline void rtw89_phy_write16(struct rtw89_dev *rtwdev,
+				     u32 addr, u16 data)
+{
+	rtwdev->io->phy_write16(rtwdev, addr, data);
+}
+
+static inline void rtw89_phy_write32(struct rtw89_dev *rtwdev,
+				     u32 addr, u32 data)
+{
+	rtwdev->io->phy_write32(rtwdev, addr, data);
+}
+
 static inline void rtw89_phy_write32_set(struct rtw89_dev *rtwdev,
 					 u32 addr, u32 bits)
 {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
index 84bdd39b3ceb..5750038b0ba7 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
@@ -2639,6 +2639,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = {
 	.support_tas		= false,
 	.support_sar_by_ant	= false,
 	.support_noise		= false,
+	.support_fw_cmd_ofld	= false,
 	.ul_tb_waveform_ctrl	= true,
 	.ul_tb_pwr_diff		= false,
 	.rx_freq_from_ie	= true,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
index 1d4f1df524a1..bb63b5efbe82 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
@@ -2376,6 +2376,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = {
 	.support_tas		= false,
 	.support_sar_by_ant	= false,
 	.support_noise		= true,
+	.support_fw_cmd_ofld	= false,
 	.ul_tb_waveform_ctrl	= false,
 	.ul_tb_pwr_diff		= false,
 	.rx_freq_from_ie	= true,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
index 5e8738bb2dc2..a71d01d5be7e 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
@@ -972,6 +972,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = {
 	.support_tas		= false,
 	.support_sar_by_ant	= true,
 	.support_noise		= false,
+	.support_fw_cmd_ofld	= false,
 	.ul_tb_waveform_ctrl	= true,
 	.ul_tb_pwr_diff		= false,
 	.rx_freq_from_ie	= true,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
index 0d980fcd22d4..9d6a13f569f8 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
@@ -812,6 +812,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {
 	.support_tas		= false,
 	.support_sar_by_ant	= true,
 	.support_noise		= false,
+	.support_fw_cmd_ofld	= false,
 	.ul_tb_waveform_ctrl	= true,
 	.ul_tb_pwr_diff		= false,
 	.rx_freq_from_ie	= true,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
index 40db7e3c0d97..b3f3eb365d19 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
@@ -3169,6 +3169,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = {
 	.support_tas		= true,
 	.support_sar_by_ant	= true,
 	.support_noise		= false,
+	.support_fw_cmd_ofld	= true,
 	.ul_tb_waveform_ctrl	= false,
 	.ul_tb_pwr_diff		= true,
 	.rx_freq_from_ie	= false,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
index 8f6cf64271e8..d2ff55b23f95 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
@@ -2973,6 +2973,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = {
 	.support_tas		= true,
 	.support_sar_by_ant	= true,
 	.support_noise		= false,
+	.support_fw_cmd_ofld	= false,
 	.ul_tb_waveform_ctrl	= false,
 	.ul_tb_pwr_diff		= false,
 	.rx_freq_from_ie	= false,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index a3890e863423..564d4baa59e7 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -2986,6 +2986,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = {
 	.support_tas		= false,
 	.support_sar_by_ant	= true,
 	.support_noise		= false,
+	.support_fw_cmd_ofld	= false,
 	.ul_tb_waveform_ctrl	= false,
 	.ul_tb_pwr_diff		= false,
 	.rx_freq_from_ie	= false,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 09/16] wifi: rtw89: offload DMAC and CMAC init IO to firmware
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (7 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 08/16] wifi: rtw89: add IO offload support via firmware Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 10/16] wifi: rtw89: use firmware offload for PHY and RF batch register writes Ping-Ke Shih
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Chia-Yuan Li <leo.li@realtek.com>

During the initialization of DMAC and CMAC, the driver performs a
large number of consecutive register writes. Sending these I/O
requests individually over the host interface introduces significant
latency and delays the device bring-up process.

Optimize the initialization flow by wrapping the register write
sequences in mac.c and mac_be.c with rtw89_io_pack() and
rtw89_io_unpack(). This batches the operations and offloads them
to the firmware via H2C commands, which significantly accelerates
the overall initialization time.
(8852C USB2.0 AMD B450 1379 ms -> 1273 ms)

Signed-off-by: Chia-Yuan Li <leo.li@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/mac.c    | 53 +++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/mac_be.c | 57 ++++++++++++++++++++-
 2 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index 54aad37485d6..aac14d768bc3 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -2300,6 +2300,8 @@ static int preload_init_set_ax(struct rtw89_dev *rtwdev, u8 mac_idx,
 {
 	u32 reg, max_preld_size, min_rsvd_size;
 
+	rtw89_io_pack(rtwdev);
+
 	max_preld_size = (mac_idx == RTW89_MAC_0 ?
 			  PRELD_B0_ENT_NUM : PRELD_B1_ENT_NUM) * PRELD_AMSDU_SIZE;
 	reg = mac_idx == RTW89_MAC_0 ?
@@ -2313,6 +2315,8 @@ static int preload_init_set_ax(struct rtw89_dev *rtwdev, u8 mac_idx,
 	rtw89_write32_mask(rtwdev, reg, B_AX_B0_PRELD_NXT_TXENDWIN_MASK, PRELD_NEXT_WND);
 	rtw89_write32_mask(rtwdev, reg, B_AX_B0_PRELD_NXT_RSVMINSZ_MASK, min_rsvd_size);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -2392,11 +2396,15 @@ static int sta_sch_init_ax(struct rtw89_dev *rtwdev)
 		return ret;
 	}
 
+	rtw89_io_pack(rtwdev);
+
 	rtw89_write32_set(rtwdev, R_AX_SS_CTRL, B_AX_SS_WARM_INIT_FLG);
 	rtw89_write32_clr(rtwdev, R_AX_SS_CTRL, B_AX_SS_NONEMPTY_SS2FINFO_EN);
 
 	_patch_ss2f_path(rtwdev);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -2408,12 +2416,16 @@ static int mpdu_proc_init_ax(struct rtw89_dev *rtwdev)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	rtw89_write32(rtwdev, R_AX_ACTION_FWD0, TRXCFG_MPDU_PROC_ACT_FRWD);
 	rtw89_write32(rtwdev, R_AX_TF_FWD, TRXCFG_MPDU_PROC_TF_FRWD);
 	rtw89_write32_set(rtwdev, R_AX_MPDU_PROC,
 			  B_AX_APPEND_FCS | B_AX_A_ICV_ERR);
 	rtw89_write32(rtwdev, R_AX_CUT_AMSDU_CTRL, TRXCFG_MPDU_PROC_CUT_CTRL);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -2427,6 +2439,8 @@ static int sec_eng_init_ax(struct rtw89_dev *rtwdev)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	val = rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL);
 	/* init clock */
 	val |= (B_AX_CLK_EN_CGCMP | B_AX_CLK_EN_WAPI | B_AX_CLK_EN_WEP_TKIP);
@@ -2452,6 +2466,8 @@ static int sec_eng_init_ax(struct rtw89_dev *rtwdev)
 		rtw89_write32_mask(rtwdev, R_AX_SEC_DEBUG1,
 				   B_AX_TX_TIMEOUT_SEL_MASK, AX_TX_TO_VAL);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -2535,6 +2551,8 @@ static int scheduler_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_PREBKF_CFG_1, mac_idx);
 	if (rtwdev->chip->chip_id == RTL8852C)
 		rtw89_write32_mask(rtwdev, reg, B_AX_SIFS_MACTXEN_T1_MASK,
@@ -2563,6 +2581,8 @@ static int scheduler_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 				   SCH_PREBKF_24US);
 	}
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -2631,6 +2651,8 @@ static int rx_fltr_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	for (i = RTW89_MGNT; i <= RTW89_DATA; i++) {
 		ret = rtw89_mac_typ_fltr_opt_ax(rtwdev, i, RTW89_FWD_TO_HOST,
 						mac_idx);
@@ -2647,6 +2669,8 @@ static int rx_fltr_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	rtw89_write16(rtwdev, rtw89_mac_reg_by_idx(rtwdev, R_AX_PLCP_HDR_FLTR, mac_idx),
 		      plcp_ftlr);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -2693,6 +2717,8 @@ static int cca_ctrl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_CCA_CONTROL, mac_idx);
 	val = rtw89_read32(rtwdev, reg);
 	val |= (B_AX_TB_CHK_BASIC_NAV | B_AX_TB_CHK_BTCCA |
@@ -2713,16 +2739,22 @@ static int cca_ctrl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 
 	_patch_dis_resp_chk(rtwdev, mac_idx);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
 static int nav_ctrl_init_ax(struct rtw89_dev *rtwdev)
 {
+	rtw89_io_pack(rtwdev);
+
 	rtw89_write32_set(rtwdev, R_AX_WMAC_NAV_CTL, B_AX_WMAC_PLCP_UP_NAV_EN |
 						     B_AX_WMAC_TF_UP_NAV_EN |
 						     B_AX_WMAC_NAV_UPPER_EN);
 	rtw89_write32_mask(rtwdev, R_AX_WMAC_NAV_CTL, B_AX_WMAC_NAV_UPPER_MASK, NAV_25MS);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -2734,12 +2766,17 @@ static int spatial_reuse_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
 	if (ret)
 		return ret;
+
+	rtw89_io_pack(rtwdev);
+
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_RX_SR_CTRL, mac_idx);
 	rtw89_write8_clr(rtwdev, reg, B_AX_SR_EN);
 
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_BSSID_SRC_CTRL, mac_idx);
 	rtw89_write8_set(rtwdev, reg, B_AX_PLCP_SRC_EN);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -2752,6 +2789,8 @@ static int tmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_MAC_LOOPBACK, mac_idx);
 	rtw89_write32_clr(rtwdev, reg, B_AX_MACLBK_EN);
 
@@ -2762,6 +2801,8 @@ static int tmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	rtw89_write32_mask(rtwdev, reg, B_AX_TXDFIFO_HIGH_MCS_THRE_MASK, TXDFIFO_HIGH_MCS_THRE);
 	rtw89_write32_mask(rtwdev, reg, B_AX_TXDFIFO_LOW_MCS_THRE_MASK, TXDFIFO_LOW_MCS_THRE);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -2776,6 +2817,8 @@ static int trxptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_TRXPTCL_RESP_0, mac_idx);
 	val = rtw89_read32(rtwdev, reg);
 	val &= ~B_AX_WMAC_SPEC_SIFS_CCK_MASK;
@@ -2806,6 +2849,8 @@ static int trxptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	reg = rtw89_mac_reg_by_idx(rtwdev, rrsr->rsc.addr, mac_idx);
 	rtw89_write32_mask(rtwdev, reg, rrsr->rsc.mask, rrsr->rsc.data);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -2840,6 +2885,8 @@ static int rmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	if (mac_idx == RTW89_MAC_0)
 		rst_bacam(rtwdev);
 
@@ -2881,6 +2928,8 @@ static int rmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_PLCP_HDR_FLTR, mac_idx);
 	rtw89_write8_clr(rtwdev, reg, B_AX_VHT_SU_SIGB_CRC_CHK);
 
+	rtw89_io_unpack(rtwdev);
+
 	return ret;
 }
 
@@ -2932,6 +2981,8 @@ static int ptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) {
 		reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_SIFS_SETTING, mac_idx);
 		val = rtw89_read32(rtwdev, reg);
@@ -2969,6 +3020,8 @@ static int ptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 				   B_AX_AMPDU_MAX_LEN_VHT_MASK, 0x3FF80);
 	}
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c
index 39a28fd27412..a0fa8e059739 100644
--- a/drivers/net/wireless/realtek/rtw89/mac_be.c
+++ b/drivers/net/wireless/realtek/rtw89/mac_be.c
@@ -1000,6 +1000,8 @@ static int mpdu_proc_init_be(struct rtw89_dev *rtwdev)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	rtw89_write32_set(rtwdev, R_BE_MPDU_PROC, B_BE_APPEND_FCS);
 	rtw89_write32(rtwdev, R_BE_CUT_AMSDU_CTRL, TRXCFG_MPDU_PROC_CUT_CTRL |
 						   B_BE_CA_CHK_ADDRCAM_EN);
@@ -1019,6 +1021,8 @@ static int mpdu_proc_init_be(struct rtw89_dev *rtwdev)
 	val32 = u32_replace_bits(val32, 1, B_BE_FWD_WLAN_CPU_TYPE_1_MASK);
 	rtw89_write32(rtwdev, R_BE_DISP_FWD_WLAN_0, val32);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -1031,6 +1035,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	val32 = rtw89_read32(rtwdev, R_BE_SEC_ENG_CTRL);
 	val32 |= B_BE_CLK_EN_CGCMP | B_BE_CLK_EN_WAPI | B_BE_CLK_EN_WEP_TKIP |
 		 B_BE_SEC_TX_ENC | B_BE_SEC_RX_DEC |
@@ -1041,6 +1047,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev)
 
 	rtw89_write32_set(rtwdev, R_BE_SEC_MPDU_PROC, B_BE_APPEND_ICV | B_BE_APPEND_MIC);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -1083,13 +1091,16 @@ static int mlo_init_be(struct rtw89_dev *rtwdev)
 	u32 reg;
 	int ret;
 
-	val32 = rtw89_read32(rtwdev, R_BE_MLO_INIT_CTL);
+	rtw89_io_pack(rtwdev);
 
+	val32 = rtw89_read32(rtwdev, R_BE_MLO_INIT_CTL);
 	val32 |= B_BE_MLO_TABLE_REINIT;
 	rtw89_write32(rtwdev, R_BE_MLO_INIT_CTL, val32);
 	val32 &= ~B_BE_MLO_TABLE_REINIT;
 	rtw89_write32(rtwdev, R_BE_MLO_INIT_CTL, val32);
 
+	rtw89_io_unpack(rtwdev);
+
 	ret = read_poll_timeout_atomic(rtw89_read32, val32,
 				       val32 & B_BE_MLO_TABLE_INIT_DONE,
 				       1, 1000, false, rtwdev, R_BE_MLO_INIT_CTL);
@@ -1101,9 +1112,13 @@ static int mlo_init_be(struct rtw89_dev *rtwdev)
 	else
 		reg = R_BE_SS_CTRL_V1;
 
+	rtw89_io_pack(rtwdev);
+
 	rtw89_write32_set(rtwdev, reg, B_BE_MLO_HW_CHGLINK_EN);
 	rtw89_write32_set(rtwdev, R_BE_CMAC_SHARE_ACQCHK_CFG_0, B_BE_R_MACID_ACQ_CHK_EN);
 
+	rtw89_io_unpack(rtwdev);
+
 	return ret;
 }
 
@@ -1173,6 +1188,8 @@ static int scheduler_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	if (chip->chip_id == RTL8922D) {
 		reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_SCH_EXT_CTRL, mac_idx);
 		rtw89_write32_set(rtwdev, reg, B_BE_CWCNT_PLUS_MODE);
@@ -1216,6 +1233,8 @@ static int scheduler_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 		rtw89_write32_set(rtwdev, reg, B_BE_TX_NAV_RST_EDCA_EN);
 	}
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -1292,6 +1311,8 @@ static int rx_fltr_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 	u32 reg;
 	u32 val;
 
+	rtw89_io_pack(rtwdev);
+
 	rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_MGNT, RTW89_FWD_TO_HOST, mac_idx);
 	rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_CTRL, RTW89_FWD_TO_HOST, mac_idx);
 	rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_DATA, RTW89_FWD_TO_HOST, mac_idx);
@@ -1309,6 +1330,8 @@ static int rx_fltr_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 	      B_BE_LSIG_PARITY_CHK_EN | B_BE_CCK_SIG_CHK | B_BE_CCK_CRC_CHK;
 	rtw89_write16(rtwdev, reg, val);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -1322,6 +1345,8 @@ static int nav_ctrl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 	u32 val32;
 	u32 reg;
 
+	rtw89_io_pack(rtwdev);
+
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_WMAC_NAV_CTL, mac_idx);
 
 	val32 = rtw89_read32(rtwdev, reg);
@@ -1337,6 +1362,8 @@ static int nav_ctrl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_TRXPTCL_RESP_0, mac_idx);
 	rtw89_write32_set(rtwdev, reg, B_BE_WMAC_MBA_DUR_FORCE);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -1349,12 +1376,16 @@ static int spatial_reuse_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RX_SR_CTRL, mac_idx);
 	rtw89_write8_clr(rtwdev, reg, B_BE_SR_EN | B_BE_SR_CTRL_PLCP_EN);
 
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_BSSID_SRC_CTRL, mac_idx);
 	rtw89_write8_set(rtwdev, reg, B_BE_PLCP_SRC_EN);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -1364,6 +1395,8 @@ static int tmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 	struct rtw89_hal *hal = &rtwdev->hal;
 	u32 reg;
 
+	rtw89_io_pack(rtwdev);
+
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_TB_PPDU_CTRL, mac_idx);
 	rtw89_write32_clr(rtwdev, reg, B_BE_QOSNULL_UPD_MUEDCA_EN);
 
@@ -1378,6 +1411,8 @@ static int tmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 		rtw89_write32_clr(rtwdev, reg, CLEAR_DTOP_DIS);
 	}
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -1394,6 +1429,8 @@ static int trxptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_MAC_LOOPBACK, mac_idx);
 	val32 = rtw89_read32(rtwdev, reg);
 	val32 = u32_replace_bits(val32, S_BE_MACLBK_PLCP_DLY_DEF,
@@ -1453,6 +1490,8 @@ static int trxptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 		rtw89_write32_mask(rtwdev, reg, B_BE_RSC_MASK, 1);
 	}
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -1495,6 +1534,8 @@ static int rmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 			return ret;
 	}
 
+	rtw89_io_pack(rtwdev);
+
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_DLK_PROTECT_CTL, mac_idx);
 	val16 = rtw89_read16(rtwdev, reg);
 	val16 = u16_replace_bits(val16, TRXCFG_RMAC_DATA_TO, B_BE_RX_DLK_DATA_TIME_MASK);
@@ -1534,6 +1575,8 @@ static int rmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 		rtw89_write32_set(rtwdev, reg, B_BE_DIS_CHK_MIN_LEN);
 	}
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -1555,10 +1598,14 @@ static int resp_pktctl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 		return ret;
 	}
 
+	rtw89_io_pack(rtwdev);
+
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RESP_CSI_RESERVED_PAGE, mac_idx);
 	rtw89_write32_mask(rtwdev, reg, B_BE_CSI_RESERVED_START_PAGE_MASK, qt_cfg.pktid);
 	rtw89_write32_mask(rtwdev, reg, B_BE_CSI_RESERVED_PAGE_NUM_MASK, qt_cfg.pg_num + 1);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -1602,6 +1649,8 @@ static int ptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	rtw89_io_pack(rtwdev);
+
 	if (is_qta_poh(rtwdev)) {
 		reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_SIFS_SETTING, mac_idx);
 		val32 = rtw89_read32(rtwdev, reg);
@@ -1671,6 +1720,8 @@ static int ptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 					       B_BE_PRELD_HIQ_P0_EN);
 	}
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
@@ -1965,6 +2016,8 @@ static int preload_init_be(struct rtw89_dev *rtwdev, u8 mac_idx,
 	if (!(chip->chip_id == RTL8922A || rtw89_mac_chk_preload_allow(rtwdev)))
 		return 0;
 
+	rtw89_io_pack(rtwdev);
+
 	max_preld_size = mac_idx == RTW89_MAC_0 ?
 			 PRELD_B0_ENT_NUM : PRELD_B1_ENT_NUM;
 	if (chip->chip_id == RTL8922D)
@@ -1997,6 +2050,8 @@ static int preload_init_be(struct rtw89_dev *rtwdev, u8 mac_idx,
 	val32 |= B_BE_B0_PRELD_FEN;
 	rtw89_write32(rtwdev, reg, val32);
 
+	rtw89_io_unpack(rtwdev);
+
 	return 0;
 }
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 10/16] wifi: rtw89: use firmware offload for PHY and RF batch register writes
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (8 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 09/16] wifi: rtw89: offload DMAC and CMAC init IO to firmware Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 11/16] wifi: rtw89: 8832cu: Add ID 2c7c:8206 for RTL8832CU Ping-Ke Shih
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Eric Huang <echuang@realtek.com>

Use firmware offload to perform batch register writes for PHY and RF.
This reduces I/O operations, improves bus transfer efficiency, and
decreases hardware initialization time.

Performance improvement on USB 2.0 HCI:
- rtw89_phy_init_bb_reg: 61 ms -> 5 ms
- rtw89_phy_init_rf_reg: 587 ms -> 106 ms

Use offload-aware delay functions (do_udelay and do_mdelay) for the
delay calls in rtw89_phy_init_reg, rtw89_phy_config_rf_reg, and
rtw89_phy_config_rf_reg_v1 to ensure compatibility with batch register
writes.

Signed-off-by: Eric Huang <echuang@realtek.com>
Signed-off-by: Johnson Tsai <wenjie.tsai@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/phy.c | 38 ++++++++++++++++--------
 1 file changed, 26 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index f26cbde6bdd0..5065326d8c53 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -1352,17 +1352,17 @@ static void rtw89_phy_config_bb_reg(struct rtw89_dev *rtwdev,
 	u32 addr;
 
 	if (reg->addr == 0xfe) {
-		mdelay(50);
+		rtw89_io_mdelay(rtwdev, 50);
 	} else if (reg->addr == 0xfd) {
-		mdelay(5);
+		rtw89_io_mdelay(rtwdev, 5);
 	} else if (reg->addr == 0xfc) {
-		mdelay(1);
+		rtw89_io_mdelay(rtwdev, 1);
 	} else if (reg->addr == 0xfb) {
-		udelay(50);
+		rtw89_io_udelay(rtwdev, 50);
 	} else if (reg->addr == 0xfa) {
-		udelay(5);
+		rtw89_io_udelay(rtwdev, 5);
 	} else if (reg->addr == 0xf9) {
-		udelay(1);
+		rtw89_io_udelay(rtwdev, 1);
 	} else if (reg->data == BYPASS_CR_DATA) {
 		rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "Bypass CR 0x%x\n", reg->addr);
 	} else {
@@ -1692,17 +1692,17 @@ void rtw89_phy_config_rf_reg(struct rtw89_dev *rtwdev,
 			     void *extra_data)
 {
 	if (reg->addr == 0xfe) {
-		mdelay(50);
+		rtw89_io_mdelay(rtwdev, 50);
 	} else if (reg->addr == 0xfd) {
-		mdelay(5);
+		rtw89_io_mdelay(rtwdev, 5);
 	} else if (reg->addr == 0xfc) {
-		mdelay(1);
+		rtw89_io_mdelay(rtwdev, 1);
 	} else if (reg->addr == 0xfb) {
-		udelay(50);
+		rtw89_io_udelay(rtwdev, 50);
 	} else if (reg->addr == 0xfa) {
-		udelay(5);
+		rtw89_io_udelay(rtwdev, 5);
 	} else if (reg->addr == 0xf9) {
-		udelay(1);
+		rtw89_io_udelay(rtwdev, 1);
 	} else {
 		rtw89_write_rf(rtwdev, rf_path, reg->addr, 0xfffff, reg->data);
 		rtw89_phy_cofig_rf_reg_store(rtwdev, reg, rf_path,
@@ -1715,6 +1715,11 @@ void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev,
 				enum rtw89_rf_path rf_path,
 				void *extra_data)
 {
+	if (reg->addr == 0xfe) {
+		rtw89_io_mdelay(rtwdev, 50);
+		return;
+	}
+
 	rtw89_write_rf(rtwdev, rf_path, reg->addr, RFREG_MASK, reg->data);
 
 	if (reg->addr < 0x100)
@@ -1885,11 +1890,16 @@ void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev)
 	const struct rtw89_phy_table *bb_gain_table;
 
 	bb_table = elm_info->bb_tbl ? elm_info->bb_tbl : chip->bb_table;
+
+	rtw89_io_pack(rtwdev);
+
 	rtw89_phy_init_reg(rtwdev, bb_table, false, rtw89_phy_config_bb_reg, NULL);
 	if (rtwdev->dbcc_en)
 		rtw89_phy_init_reg(rtwdev, bb_table, false, rtw89_phy_config_bb_reg,
 				   (void *)RTW89_PHY_1);
 
+	rtw89_io_unpack(rtwdev);
+
 	rtw89_chip_init_txpwr_unit(rtwdev);
 
 	bb_gain_table = elm_info->bb_gain ? elm_info->bb_gain : chip->bb_gain_table;
@@ -2016,7 +2026,11 @@ void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev, bool noio)
 		else
 			config = rf_table->config ? rf_table->config :
 				 rtw89_phy_config_rf_reg;
+
+		rtw89_io_pack(rtwdev);
 		rtw89_phy_init_reg(rtwdev, rf_table, by_acv, config, (void *)rf_reg_info);
+		rtw89_io_unpack(rtwdev);
+
 		if (rtw89_phy_config_rf_reg_fw(rtwdev, rf_reg_info))
 			rtw89_warn(rtwdev, "rf path %d reg h2c config failed\n",
 				   rf_reg_info->rf_path);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 11/16] wifi: rtw89: 8832cu: Add ID 2c7c:8206 for RTL8832CU
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (9 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 10/16] wifi: rtw89: use firmware offload for PHY and RF batch register writes Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 12/16] wifi: rtw89: use struct to fill C2H recv ack Ping-Ke Shih
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Johnson Tsai <wenjie.tsai@realtek.com>

This ID is used by RTL8832CU-based Quectel USB dongle devices.

Signed-off-by: Johnson Tsai <wenjie.tsai@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
index 092d2812a4d5..790fd1dec66d 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
@@ -53,6 +53,8 @@ static const struct usb_device_id rtw_8852cu_id_table[] = {
 	  .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x28de, 0x2432, 0xff, 0xff, 0xff),
 	  .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x8206, 0xff, 0xff, 0xff),
+	  .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x35b2, 0x0502, 0xff, 0xff, 0xff),
 	  .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0101, 0xff, 0xff, 0xff),
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 12/16] wifi: rtw89: use struct to fill C2H recv ack
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (10 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 11/16] wifi: rtw89: 8832cu: Add ID 2c7c:8206 for RTL8832CU Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 13/16] wifi: rtw89: check scan C2H event recv ack instead of C2H event done ack Ping-Ke Shih
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Chih-Kang Chang <gary.chang@realtek.com>

This C2H packet is used to indicate that FW has received the
corresponding H2C command. No logic is changed.

Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/fw.h  | 18 ++++++++++--------
 drivers/net/wireless/realtek/rtw89/mac.c | 13 ++++++++-----
 2 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index 4d94a7195708..6ef53fcd0cce 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -3886,14 +3886,16 @@ struct rtw89_c2h_done_ack {
 #define	RTW89_C2H_SCAN_DONE_ACK_RETURN GENMASK(5, 0)
 #define RTW89_C2H_DONE_ACK_W2_H2C_SEQ GENMASK(31, 24)
 
-#define RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h) \
-	le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(1, 0))
-#define RTW89_GET_MAC_C2H_REV_ACK_CLASS(c2h) \
-	le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(7, 2))
-#define RTW89_GET_MAC_C2H_REV_ACK_FUNC(c2h) \
-	le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(15, 8))
-#define RTW89_GET_MAC_C2H_REV_ACK_H2C_SEQ(c2h) \
-	le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(23, 16))
+struct rtw89_c2h_rev_ack {
+	__le32 w0;
+	__le32 w1;
+	__le32 w2;
+} __packed;
+
+#define RTW89_C2H_REV_ACK_W2_CAT GENMASK(1, 0)
+#define RTW89_C2H_REV_ACK_W2_CLASS GENMASK(7, 2)
+#define RTW89_C2H_REV_ACK_W2_FUNC GENMASK(15, 8)
+#define RTW89_C2H_REV_ACK_W2_H2C_SEQ GENMASK(23, 16)
 
 struct rtw89_fw_c2h_log_fmt {
 	__le16 signature;
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index aac14d768bc3..6ad411149c3e 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -5406,16 +5406,19 @@ rtw89_mac_c2h_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h,
 }
 
 static void
-rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 len)
 {
 	/* N.B. This will run in interrupt context. */
+	const struct rtw89_c2h_rev_ack *c2h = (const void *)skb_c2h->data;
+
+	u8 h2c_cat = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_CAT);
+	u8 h2c_class = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_CLASS);
+	u8 h2c_func = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_FUNC);
+	u8 h2c_seq = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_H2C_SEQ);
 
 	rtw89_debug(rtwdev, RTW89_DBG_FW,
 		    "C2H rev ack recv, cat: %d, class: %d, func: %d, seq : %d\n",
-		    RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h->data),
-		    RTW89_GET_MAC_C2H_REV_ACK_CLASS(c2h->data),
-		    RTW89_GET_MAC_C2H_REV_ACK_FUNC(c2h->data),
-		    RTW89_GET_MAC_C2H_REV_ACK_H2C_SEQ(c2h->data));
+		    h2c_cat, h2c_class, h2c_func, h2c_seq);
 }
 
 static void
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 13/16] wifi: rtw89: check scan C2H event recv ack instead of C2H event done ack
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (11 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 12/16] wifi: rtw89: use struct to fill C2H recv ack Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 14/16] wifi: rtw89: suspend DIG when remain-on-channel Ping-Ke Shih
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Chih-Kang Chang <gary.chang@realtek.com>

The original flow checks the scan offload C2H event done ack to increase
the scan sequence. However, the scan offload response C2H event may be
received earlier than the scan offload C2H event done ack, which causes
these C2H event to be dropped by rtw89_fw_c2h_purge_obsoleted_scan_events()
check because the scan sequence of the scan offload response C2H event
is less than the scan offload C2H event done ack. Therefore, checking
the scan offload C2H event recv ack instead of C2H event done ack to fix
this issue.

Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/mac.c | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index 6ad411149c3e..af2e5b67f4d5 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -5415,10 +5415,30 @@ rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 len
 	u8 h2c_class = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_CLASS);
 	u8 h2c_func = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_FUNC);
 	u8 h2c_seq = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_H2C_SEQ);
+	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
 
 	rtw89_debug(rtwdev, RTW89_DBG_FW,
 		    "C2H rev ack recv, cat: %d, class: %d, func: %d, seq : %d\n",
 		    h2c_cat, h2c_class, h2c_func, h2c_seq);
+
+	if (h2c_cat != H2C_CAT_MAC)
+		return;
+
+	switch (h2c_class) {
+	default:
+		return;
+	case H2C_CL_MAC_FW_OFLD:
+		switch (h2c_func) {
+		default:
+			return;
+		case H2C_FUNC_SCANOFLD:
+		case H2C_FUNC_SCANOFLD_BE:
+			scan_info->seq++;
+			break;
+		}
+
+		return;
+	}
 }
 
 static void
@@ -5426,7 +5446,6 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le
 {
 	/* N.B. This will run in interrupt context. */
 	struct rtw89_wait_info *fw_ofld_wait = &rtwdev->mac.fw_ofld_wait;
-	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
 	struct rtw89_wait_info *ps_wait = &rtwdev->mac.ps_wait;
 	const struct rtw89_c2h_done_ack *c2h =
 		(const struct rtw89_c2h_done_ack *)skb_c2h->data;
@@ -5469,11 +5488,9 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le
 			h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN;
 			break;
 		case H2C_FUNC_SCANOFLD:
-			scan_info->seq++;
 			cond = RTW89_SCANOFLD_WAIT_COND_START;
 			break;
 		case H2C_FUNC_SCANOFLD_BE:
-			scan_info->seq++;
 			cond = RTW89_SCANOFLD_BE_WAIT_COND_START;
 			h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN;
 			break;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 14/16] wifi: rtw89: suspend DIG when remain-on-channel
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (12 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 13/16] wifi: rtw89: check scan C2H event recv ack instead of C2H event done ack Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 15/16] wifi: rtw89: chan: introduce new helper to get entity current configuration Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 16/16] wifi: rtw89: 8922d: update RF calibration flow for MLD Ping-Ke Shih
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Chih-Kang Chang <gary.chang@realtek.com>

The PD (Packet Detection) lower bound is set after the STA interface
connects. When the P2P interface attempts to find peers, it enters
the remain-on-channel flow to listen for probe requests. However, if
the RSSI of the connected AP is too high, the P2P interface fails to
receive it. To resolve this, suspend the DIG during the
remain-on-channel period.

Signed-off-by: Chih-Kang Chang <gary.chang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 7fe979e47ad5..41eefe901ab2 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -4474,6 +4474,7 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
 	reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx);
 	rtw89_write32_clr(rtwdev, reg, B_AX_A_UC_CAM_MATCH | B_AX_A_BC_CAM_MATCH);
 
+	rtw89_phy_dig_suspend(rtwdev);
 	ieee80211_ready_on_channel(hw);
 	wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work);
 	wiphy_delayed_work_queue(hw->wiphy, &rtwvif->roc.roc_work,
@@ -4518,6 +4519,7 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
 
 	rtw89_core_handle_sta_pending_tx(rtwdev, rtwvif_link);
 	queue_work(rtwdev->txq_wq, &rtwdev->txq_work);
+	rtw89_phy_dig_resume(rtwdev, true);
 
 	if (hw->conf.flags & IEEE80211_CONF_IDLE)
 		wiphy_delayed_work_queue(hw->wiphy, &roc->roc_work,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 15/16] wifi: rtw89: chan: introduce new helper to get entity current configuration
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (13 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 14/16] wifi: rtw89: suspend DIG when remain-on-channel Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  2026-04-20  3:40 ` [PATCH rtw-next 16/16] wifi: rtw89: 8922d: update RF calibration flow for MLD Ping-Ke Shih
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Zong-Zhe Yang <kevin_yang@realtek.com>

The original helper can only queries target channel, but cannot determine
current role's status, e.g. is it MLD or not. These things should be a set.
Adding more and more helpers to query them individually without synchronous
doesn't seem right. Introduce a new helper to query current channel set and
role status.

Besides, when single channel, e.g. MLO_2_PLUS_0_1RF and MLO_0_PLUS_2_1RF,
the target channel pointer will be duplicated to fill the returned channel
set. So, some callers can save trivial things for these cases. The returned
channels will be non-NULL, so callers don't need trivial NULL check either.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/chan.c     | 90 +++++++++++--------
 drivers/net/wireless/realtek/rtw89/chan.h     | 21 ++---
 drivers/net/wireless/realtek/rtw89/core.c     | 20 ++---
 drivers/net/wireless/realtek/rtw89/phy.c      | 12 ++-
 drivers/net/wireless/realtek/rtw89/phy_be.c   |  8 +-
 .../net/wireless/realtek/rtw89/rtw8852b_rfk.c |  6 +-
 .../wireless/realtek/rtw89/rtw8852bt_rfk.c    |  6 +-
 drivers/net/wireless/realtek/rtw89/rtw8922a.c | 17 +---
 .../net/wireless/realtek/rtw89/rtw8922a_rfk.c | 22 +----
 drivers/net/wireless/realtek/rtw89/rtw8922d.c | 30 +++++--
 .../net/wireless/realtek/rtw89/rtw8922d_rfk.c | 22 +----
 11 files changed, 129 insertions(+), 125 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c
index ceb399fc2b94..cd846cb81f0c 100644
--- a/drivers/net/wireless/realtek/rtw89/chan.c
+++ b/drivers/net/wireless/realtek/rtw89/chan.c
@@ -398,43 +398,6 @@ static u8 rtw89_entity_role_get_index(struct rtw89_dev *rtwdev)
 	}
 }
 
-const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,
-					       const char *caller_message,
-					       u8 link_index, bool nullchk)
-{
-	struct rtw89_hal *hal = &rtwdev->hal;
-	struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
-	enum rtw89_chanctx_idx chanctx_idx;
-	u8 role_index;
-
-	lockdep_assert_wiphy(rtwdev->hw->wiphy);
-
-	if (unlikely(link_index >= __RTW89_MLD_MAX_LINK_NUM)) {
-		WARN(1, "link index %u is invalid (max link inst num: %d)\n",
-		     link_index, __RTW89_MLD_MAX_LINK_NUM);
-		goto dflt;
-	}
-
-	role_index = rtw89_entity_role_get_index(rtwdev);
-
-	chanctx_idx = mgnt->chanctx_tbl[role_index][link_index];
-	if (chanctx_idx == RTW89_CHANCTX_IDLE)
-		goto dflt;
-
-	return rtw89_chan_get(rtwdev, chanctx_idx);
-
-dflt:
-	if (unlikely(nullchk))
-		return NULL;
-
-	rtw89_debug(rtwdev, RTW89_DBG_CHAN,
-		    "%s (%s): prefetch NULL on link index %u\n",
-		    __func__, caller_message ?: "", link_index);
-
-	return rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
-}
-EXPORT_SYMBOL(__rtw89_mgnt_chan_get);
-
 bool rtw89_entity_check_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx)
 {
 	switch (rtwdev->mlo_dbcc_mode) {
@@ -457,6 +420,59 @@ void rtw89_entity_force_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx)
 		rtw89_debug(rtwdev, RTW89_DBG_CHAN, "%s: (none)\n", __func__);
 }
 
+void rtw89_entity_get_conf(struct rtw89_dev *rtwdev, struct rtw89_entity_conf *conf)
+{
+	struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt;
+	enum rtw89_chanctx_idx idxes[ARRAY_SIZE(conf->chans)];
+	struct rtw89_vif *role;
+	u8 ridx;
+	int i;
+
+	lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+	memset(conf, 0, sizeof(*conf));
+
+	ridx = rtw89_entity_role_get_index(rtwdev);
+	role = mgnt->active_roles[ridx];
+	if (role) {
+		struct ieee80211_vif *vif = rtwvif_to_vif(role);
+
+		conf->is_mld = ieee80211_vif_is_mld(vif);
+		conf->en_emlsr = role->mlo_mode == RTW89_MLO_MODE_EMLSR;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(idxes); i++)
+		idxes[i] = RTW89_CHANCTX_IDLE;
+
+	switch (rtwdev->mlo_dbcc_mode) {
+	default:
+	case MLO_2_PLUS_0_1RF:
+		set_bit(0, conf->hw_bitmap);
+		idxes[0] = mgnt->chanctx_tbl[ridx][0];
+		idxes[1] = idxes[0];
+		break;
+	case MLO_0_PLUS_2_1RF:
+		set_bit(1, conf->hw_bitmap);
+		idxes[1] = mgnt->chanctx_tbl[ridx][1];
+		idxes[0] = idxes[1];
+		break;
+	case MLO_1_PLUS_1_1RF:
+		set_bit(0, conf->hw_bitmap);
+		set_bit(1, conf->hw_bitmap);
+		idxes[0] = mgnt->chanctx_tbl[ridx][0];
+		idxes[1] = mgnt->chanctx_tbl[ridx][1];
+		break;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(idxes); i++) {
+		if (idxes[i] == RTW89_CHANCTX_IDLE)
+			idxes[i] = RTW89_CHANCTX_0;
+
+		conf->chans[i] = rtw89_chan_get(rtwdev, idxes[i]);
+	}
+}
+EXPORT_SYMBOL(rtw89_entity_get_conf);
+
 static enum rtw89_mlo_dbcc_mode
 rtw89_entity_sel_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws)
 {
diff --git a/drivers/net/wireless/realtek/rtw89/chan.h b/drivers/net/wireless/realtek/rtw89/chan.h
index c797cda2e763..a9a5f1b307a2 100644
--- a/drivers/net/wireless/realtek/rtw89/chan.h
+++ b/drivers/net/wireless/realtek/rtw89/chan.h
@@ -116,6 +116,13 @@ struct rtw89_entity_weight {
 	unsigned int active_roles;
 };
 
+struct rtw89_entity_conf {
+	bool is_mld;
+	bool en_emlsr;
+	DECLARE_BITMAP(hw_bitmap, __RTW89_MLD_MAX_LINK_NUM);
+	const struct rtw89_chan *chans[__RTW89_MLD_MAX_LINK_NUM];
+};
+
 static inline bool rtw89_get_entity_state(struct rtw89_dev *rtwdev,
 					  enum rtw89_phy_idx phy_idx)
 {
@@ -168,6 +175,7 @@ void rtw89_entity_init(struct rtw89_dev *rtwdev);
 enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev);
 bool rtw89_entity_check_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx);
 void rtw89_entity_force_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx);
+void rtw89_entity_get_conf(struct rtw89_dev *rtwdev, struct rtw89_entity_conf *conf);
 void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work);
 void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev);
 void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev,
@@ -180,19 +188,6 @@ void rtw89_chanctx_pause(struct rtw89_dev *rtwdev,
 void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev,
 			   const struct rtw89_chanctx_cb_parm *cb_parm);
 
-const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,
-					       const char *caller_message,
-					       u8 link_index, bool nullchk);
-
-#define rtw89_mgnt_chan_get(rtwdev, link_index) \
-	__rtw89_mgnt_chan_get(rtwdev, __func__, link_index, false)
-
-static inline const struct rtw89_chan *
-rtw89_mgnt_chan_get_or_null(struct rtw89_dev *rtwdev, u8 link_index)
-{
-	return __rtw89_mgnt_chan_get(rtwdev, NULL, link_index, true);
-}
-
 struct rtw89_mcc_links_info {
 	struct rtw89_vif_link *links[NUM_OF_RTW89_MCC_ROLES];
 };
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 41eefe901ab2..aeeba52b8c5e 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -474,16 +474,16 @@ static void __rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev,
 
 void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev)
 {
-	const struct rtw89_chan *chan;
+	struct rtw89_entity_conf conf;
+
+	rtw89_entity_get_conf(rtwdev, &conf);
 
-	chan = rtw89_mgnt_chan_get(rtwdev, 0);
-	__rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_0);
+	__rtw89_core_set_chip_txpwr(rtwdev, conf.chans[0], RTW89_PHY_0);
 
 	if (rtwdev->chip->chip_gen == RTW89_CHIP_AX)
 		return;
 
-	chan = rtw89_mgnt_chan_get(rtwdev, 1);
-	__rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_1);
+	__rtw89_core_set_chip_txpwr(rtwdev, conf.chans[1], RTW89_PHY_1);
 }
 
 void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev,
@@ -562,7 +562,7 @@ static void __rtw89_set_channel(struct rtw89_dev *rtwdev,
 
 int rtw89_set_channel(struct rtw89_dev *rtwdev)
 {
-	const struct rtw89_chan *chan;
+	struct rtw89_entity_conf conf;
 	enum rtw89_entity_mode mode;
 
 	mode = rtw89_entity_recalc(rtwdev);
@@ -571,14 +571,14 @@ int rtw89_set_channel(struct rtw89_dev *rtwdev)
 		return -EINVAL;
 	}
 
-	chan = rtw89_mgnt_chan_get(rtwdev, 0);
-	__rtw89_set_channel(rtwdev, chan, RTW89_MAC_0, RTW89_PHY_0);
+	rtw89_entity_get_conf(rtwdev, &conf);
+
+	__rtw89_set_channel(rtwdev, conf.chans[0], RTW89_MAC_0, RTW89_PHY_0);
 
 	if (rtwdev->chip->chip_gen == RTW89_CHIP_AX)
 		return 0;
 
-	chan = rtw89_mgnt_chan_get(rtwdev, 1);
-	__rtw89_set_channel(rtwdev, chan, RTW89_MAC_1, RTW89_PHY_1);
+	__rtw89_set_channel(rtwdev, conf.chans[1], RTW89_MAC_1, RTW89_PHY_1);
 
 	return 0;
 }
diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index 5065326d8c53..45c31ef45198 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -6839,11 +6839,15 @@ static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev,
 static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev,
 				      struct rtw89_bb_ctx *bb)
 {
-	const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx);
 	struct rtw89_dig_info *dig = &bb->dig;
 	bool is_linked = rtwdev->total_sta_assoc > 0;
+	struct rtw89_entity_conf conf;
+	const struct rtw89_chan *chan;
 	const u16 *fa_th_src = NULL;
 
+	rtw89_entity_get_conf(rtwdev, &conf);
+	chan = conf.chans[bb->phy_idx];
+
 	switch (chan->band_type) {
 	case RTW89_BAND_2G:
 		dig->lna_gain = dig->lna_gain_g;
@@ -7173,14 +7177,18 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev,
 				    struct rtw89_bb_ctx *bb,
 				    u8 rssi, bool enable)
 {
-	const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx);
 	const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs;
 	struct rtw89_dig_info *dig = &bb->dig;
 	struct rtw89_hal *hal = &rtwdev->hal;
 	u8 final_rssi, under_region = dig->pd_low_th_ofst;
+	struct rtw89_entity_conf conf;
+	const struct rtw89_chan *chan;
 	s8 cck_cca_th;
 	u32 pd_val;
 
+	rtw89_entity_get_conf(rtwdev, &conf);
+	chan = conf.chans[bb->phy_idx];
+
 	if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) {
 		pd_val = hal->fixed_dig_pd_th;
 
diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c
index 929fac1b10d2..25f1b068daa2 100644
--- a/drivers/net/wireless/realtek/rtw89/phy_be.c
+++ b/drivers/net/wireless/realtek/rtw89/phy_be.c
@@ -865,6 +865,7 @@ static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev,
 {
 	enum rtw89_phy_idx phy_idx = mac_idx != RTW89_MAC_0 ? RTW89_PHY_1 : RTW89_PHY_0;
 	enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id;
+	struct rtw89_entity_conf conf;
 	const struct rtw89_chan *chan;
 
 	if (chip_id != RTL8922D)
@@ -879,9 +880,10 @@ static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev,
 
 	rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, phy_idx);
 
-	chan = rtw89_mgnt_chan_get(rtwdev, phy_idx);
-	if (chan)
-		rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx);
+	rtw89_entity_get_conf(rtwdev, &conf);
+	chan = conf.chans[phy_idx];
+
+	rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx);
 }
 
 static void rtw89_phy_bb_wrap_ul_pwr(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c
index 70b1515c00fa..0d443edfe259 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c
@@ -4167,11 +4167,15 @@ void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev,
 
 void rtw8852b_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx)
 {
-	const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, 0);
 	struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data;
 	struct rtw89_rfk_chan_desc desc[__RTW89_RFK_CHS_NR_V0] = {};
+	struct rtw89_entity_conf conf;
+	const struct rtw89_chan *chan;
 	u8 idx;
 
+	rtw89_entity_get_conf(rtwdev, &conf);
+	chan = conf.chans[0];
+
 	for (idx = 0; idx < ARRAY_SIZE(desc); idx++) {
 		struct rtw89_rfk_chan_desc *p = &desc[idx];
 
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c
index 961b26ba2d3c..433807e712e4 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c
@@ -4234,11 +4234,15 @@ void rtw8852bt_set_channel_rf(struct rtw89_dev *rtwdev,
 
 void rtw8852bt_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx)
 {
-	const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, 0);
 	struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data;
 	struct rtw89_rfk_chan_desc desc[__RTW89_RFK_CHS_NR_V0] = {};
+	struct rtw89_entity_conf conf;
+	const struct rtw89_chan *chan;
 	u8 idx;
 
+	rtw89_entity_get_conf(rtwdev, &conf);
+	chan = conf.chans[0];
+
 	for (idx = 0; idx < ARRAY_SIZE(desc); idx++) {
 		struct rtw89_rfk_chan_desc *p = &desc[idx];
 
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
index d2ff55b23f95..ca6c24c6a153 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
@@ -1860,7 +1860,7 @@ static void rtw8922a_digital_pwr_comp(struct rtw89_dev *rtwdev,
 
 static int rtw8922a_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode)
 {
-	const struct rtw89_chan *chan0, *chan1;
+	struct rtw89_entity_conf conf;
 
 	if (mode == MLO_1_PLUS_1_1RF || mode == DBCC_LEGACY) {
 		rtw89_phy_write32_mask(rtwdev, R_DBCC, B_DBCC_EN, 0x1);
@@ -1873,19 +1873,10 @@ static int rtw8922a_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode
 		return -EOPNOTSUPP;
 	}
 
-	if (mode == MLO_1_PLUS_1_1RF) {
-		chan0 = rtw89_mgnt_chan_get(rtwdev, 0);
-		chan1 = rtw89_mgnt_chan_get(rtwdev, 1);
-	} else if (mode == MLO_0_PLUS_2_1RF) {
-		chan1 = rtw89_mgnt_chan_get(rtwdev, 1);
-		chan0 = chan1;
-	} else {
-		chan0 = rtw89_mgnt_chan_get(rtwdev, 0);
-		chan1 = chan0;
-	}
+	rtw89_entity_get_conf(rtwdev, &conf);
 
-	rtw8922a_ctrl_afe_dac(rtwdev, chan0->band_width, RF_PATH_A);
-	rtw8922a_ctrl_afe_dac(rtwdev, chan1->band_width, RF_PATH_B);
+	rtw8922a_ctrl_afe_dac(rtwdev, conf.chans[0]->band_width, RF_PATH_A);
+	rtw8922a_ctrl_afe_dac(rtwdev, conf.chans[1]->band_width, RF_PATH_B);
 
 	rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0x6180);
 
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c
index 98f14b31cf52..1e2b563f2bca 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c
@@ -278,27 +278,13 @@ static u8 rtw8922a_chlk_reload_sel_tbl(struct rtw89_dev *rtwdev,
 
 static void rtw8922a_chlk_reload(struct rtw89_dev *rtwdev)
 {
-	const struct rtw89_chan *chan0, *chan1;
+	struct rtw89_entity_conf conf;
 	u8 s0_tbl, s1_tbl;
 
-	switch (rtwdev->mlo_dbcc_mode) {
-	default:
-	case MLO_2_PLUS_0_1RF:
-		chan0 = rtw89_mgnt_chan_get(rtwdev, 0);
-		chan1 = chan0;
-		break;
-	case MLO_0_PLUS_2_1RF:
-		chan1 = rtw89_mgnt_chan_get(rtwdev, 1);
-		chan0 = chan1;
-		break;
-	case MLO_1_PLUS_1_1RF:
-		chan0 = rtw89_mgnt_chan_get(rtwdev, 0);
-		chan1 = rtw89_mgnt_chan_get(rtwdev, 1);
-		break;
-	}
+	rtw89_entity_get_conf(rtwdev, &conf);
 
-	s0_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, chan0, 0);
-	s1_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, chan1, 1);
+	s0_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, conf.chans[0], 0);
+	s1_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, conf.chans[1], 1);
 
 	rtw8922a_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl);
 	rtw8922a_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl);
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index 564d4baa59e7..f0c9d8661e57 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -1943,10 +1943,19 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev,
 static void rtw8922d_digital_pwr_comp(struct rtw89_dev *rtwdev,
 				      enum rtw89_phy_idx phy_idx)
 {
-	const struct rtw89_chan *chan0 = rtw89_mgnt_chan_get(rtwdev, 0);
-	const struct rtw89_chan *chan1 = rtw89_mgnt_chan_get(rtwdev, 1);
-
-	if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) {
+	const struct rtw89_chan *chan0, *chan1;
+	struct rtw89_entity_conf conf;
+
+	rtw89_entity_get_conf(rtwdev, &conf);
+	chan0 = conf.chans[0];
+	chan1 = conf.chans[1];
+
+	if (conf.en_emlsr) {
+		rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 1, RF_PATH_A, RTW89_PHY_0);
+		rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 1, RF_PATH_B, RTW89_PHY_0);
+		rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 1, RF_PATH_A, RTW89_PHY_1);
+		rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 1, RF_PATH_B, RTW89_PHY_1);
+	} else if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) {
 		rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 0, RF_PATH_A, RTW89_PHY_0);
 		rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 0, RF_PATH_B, RTW89_PHY_1);
 	} else {
@@ -1958,7 +1967,6 @@ static void rtw8922d_digital_pwr_comp(struct rtw89_dev *rtwdev,
 static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode,
 			     bool pwr_comp)
 {
-	const struct rtw89_chan *chan1;
 	u32 reg0, reg1;
 	u8 cck_phy_idx;
 
@@ -2016,8 +2024,10 @@ static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode
 		rtw89_write32_mask(rtwdev, reg0, B_BBWRAP_ELMSR_EN_BE4, 0);
 		rtw89_write32_mask(rtwdev, reg1, B_BBWRAP_ELMSR_EN_BE4, 0);
 	} else if ((mode == MLO_1_PLUS_1_1RF) || (mode == DBCC_LEGACY)) {
-		chan1 = rtw89_mgnt_chan_get(rtwdev, 1);
-		cck_phy_idx = chan1->band_type == RTW89_BAND_2G ?
+		struct rtw89_entity_conf conf;
+
+		rtw89_entity_get_conf(rtwdev, &conf);
+		cck_phy_idx = conf.chans[1]->band_type == RTW89_BAND_2G ?
 			      RTW89_PHY_1 : RTW89_PHY_0;
 
 		rtw89_phy_write32_mask(rtwdev, R_SYS_DBCC_BE4,
@@ -2475,9 +2485,11 @@ static void rtw8922d_set_txpwr(struct rtw89_dev *rtwdev,
 static void rtw8922d_set_txpwr_ctrl(struct rtw89_dev *rtwdev,
 				    enum rtw89_phy_idx phy_idx)
 {
-	const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, phy_idx);
+	struct rtw89_entity_conf conf;
 
-	rtw8922d_set_txpwr_ref(rtwdev, chan, phy_idx);
+	rtw89_entity_get_conf(rtwdev, &conf);
+
+	rtw8922d_set_txpwr_ref(rtwdev, conf.chans[phy_idx], phy_idx);
 }
 
 static void rtw8922d_ctrl_trx_path(struct rtw89_dev *rtwdev,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c
index 4e6a8e88a71e..7957f7b2d8e5 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c
@@ -187,27 +187,13 @@ static u8 rtw8922d_chlk_reload_sel_tbl(struct rtw89_dev *rtwdev,
 
 static void rtw8922d_chlk_reload(struct rtw89_dev *rtwdev)
 {
-	const struct rtw89_chan *chan0, *chan1;
+	struct rtw89_entity_conf conf;
 	u8 s0_tbl, s1_tbl;
 
-	switch (rtwdev->mlo_dbcc_mode) {
-	default:
-	case MLO_2_PLUS_0_1RF:
-		chan0 = rtw89_mgnt_chan_get(rtwdev, 0);
-		chan1 = chan0;
-		break;
-	case MLO_0_PLUS_2_1RF:
-		chan1 = rtw89_mgnt_chan_get(rtwdev, 1);
-		chan0 = chan1;
-		break;
-	case MLO_1_PLUS_1_1RF:
-		chan0 = rtw89_mgnt_chan_get(rtwdev, 0);
-		chan1 = rtw89_mgnt_chan_get(rtwdev, 1);
-		break;
-	}
+	rtw89_entity_get_conf(rtwdev, &conf);
 
-	s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, chan0, 0);
-	s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, chan1, 1);
+	s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[0], 0);
+	s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[1], 1);
 
 	rtw8922d_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl);
 	rtw8922d_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH rtw-next 16/16] wifi: rtw89: 8922d: update RF calibration flow for MLD
  2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
                   ` (14 preceding siblings ...)
  2026-04-20  3:40 ` [PATCH rtw-next 15/16] wifi: rtw89: chan: introduce new helper to get entity current configuration Ping-Ke Shih
@ 2026-04-20  3:40 ` Ping-Ke Shih
  15 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-04-20  3:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: leo.li, gary.chang, echuang, wenjie.tsai, phhuang, isaiah,
	kevin_yang, mh_chen

From: Zong-Zhe Yang <kevin_yang@realtek.com>

RF calibration (RFK) will record the results in HW RFK tables. SW can
select and configure the target HW RFK table to reuse the RFK result
on the target channel. Though the above can work as before, when MLD
runs eMLSR mode, a faster switching between links than SW is needed.

So, introduce a new working flow for MLD. With the new settings, HW
can easily switch RFK results between links during eMLSR mode.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/reg.h      |  4 ++
 drivers/net/wireless/realtek/rtw89/rtw8922d.c |  7 ++
 .../net/wireless/realtek/rtw89/rtw8922d_rfk.c | 70 +++++++++++++++----
 .../net/wireless/realtek/rtw89/rtw8922d_rfk.h |  1 +
 4 files changed, 69 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h
index 42ffe83931a3..b8b2aabedc8f 100644
--- a/drivers/net/wireless/realtek/rtw89/reg.h
+++ b/drivers/net/wireless/realtek/rtw89/reg.h
@@ -8462,6 +8462,8 @@
 #define RR_MODOPT_V1 0x10001
 #define RR_SW_SEL BIT(19)
 #define RR_TXG_SEL GENMASK(19, 17)
+#define RR_TBL_SEL_IDX1 GENMASK(9, 8)
+#define RR_TBL_SEL_IDX0 GENMASK(7, 6)
 #define RR_MODOPT_M_TXPWR GENMASK(5, 0)
 #define RR_WLSEL 0x02
 #define RR_WLSEL_AG GENMASK(18, 16)
@@ -10687,6 +10689,8 @@
 #define R_KTBL0B_BE4 0x38204
 #define B_KTBL0_IDX0 GENMASK(1, 0)
 #define B_KTBL0_IDX1 GENMASK(9, 8)
+#define B_KTBL0_MLD_IDX0 GENMASK(25, 24)
+#define B_KTBL0_MLD_IDX1 GENMASK(27, 26)
 #define B_KTBL0_RST BIT(31)
 #define R_KTBL1A_BE4 0x38154
 #define R_KTBL1B_BE4 0x38254
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index f0c9d8661e57..47e0e360ed6e 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -2332,12 +2332,16 @@ static void rtw8922d_rfk_tssi(struct rtw89_dev *rtwdev,
 static void rtw8922d_rfk_channel(struct rtw89_dev *rtwdev,
 				 struct rtw89_vif_link *rtwvif_link)
 {
+	struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif_link->rtwvif);
 	enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx;
 	const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, chanctx_idx);
 	enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx;
 	u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, RF_AB, chanctx_idx);
 	u32 tx_en;
 
+	if (ieee80211_vif_is_mld(vif))
+		rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, true);
+
 	rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_START);
 	rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL);
 	_wait_rx_mode(rtwdev, RF_AB);
@@ -2353,6 +2357,9 @@ static void rtw8922d_rfk_channel(struct rtw89_dev *rtwdev,
 
 	rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en);
 	rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_STOP);
+
+	if (ieee80211_vif_is_mld(vif))
+		rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, false);
 }
 
 static void rtw8922d_rfk_band_changed(struct rtw89_dev *rtwdev,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c
index 7957f7b2d8e5..a465e6631a22 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c
@@ -105,26 +105,60 @@ static void rtw8922d_set_syn01(struct rtw89_dev *rtwdev, enum _rf_syn_pow syn)
 	}
 }
 
-static void rtw8922d_chlk_ktbl_sel(struct rtw89_dev *rtwdev, u8 kpath, u8 idx)
+void rtw8922d_chlk_ktbl_ctl_mld(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool manual)
 {
-	bool mlo_linking = false;
+	if (manual) {
+		struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data;
+		u8 path = phy_idx; /* numerical equal for target */
+		u8 idx = rfk_mcc[path].table_idx;
 
-	if (idx > 2) {
-		rtw89_warn(rtwdev, "[DBCC][ERROR]indx is out of limit!! index(%d)", idx);
+		rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_RST, 0x1);
+		rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_RST, 0x1);
+		rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_IDX0, idx);
+		rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_IDX1, idx);
+		rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_IDX0, idx);
+		rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_IDX1, idx);
+		rtw89_phy_write32_mask(rtwdev, R_KTBL1A_BE4, B_KTBL1_TBL0, idx & BIT(0));
+		rtw89_phy_write32_mask(rtwdev, R_KTBL1A_BE4, B_KTBL1_TBL1, (idx & BIT(1)) >> 1);
+		rtw89_phy_write32_mask(rtwdev, R_KTBL1B_BE4, B_KTBL1_TBL0, idx & BIT(0));
+		rtw89_phy_write32_mask(rtwdev, R_KTBL1B_BE4, B_KTBL1_TBL1, (idx & BIT(1)) >> 1);
 		return;
 	}
 
-	if (mlo_linking) {
-		if (kpath & RF_A) {
-			rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_SW_SEL, 0x0);
-			rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_SW_SEL, 0x0);
-		}
+	rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_RST, 0x0);
+	rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_RST, 0x0);
+	rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_SW_SEL, 0x0);
+	rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_SW_SEL, 0x0);
+	rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_SW_SEL, 0x0);
+	rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_SW_SEL, 0x0);
+}
 
-		if (kpath & RF_B) {
-			rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_SW_SEL, 0x0);
-			rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_SW_SEL, 0x0);
-		}
+static void rtw8922d_chlk_ktbl_sel_mld(struct rtw89_dev *rtwdev,
+				       enum rtw89_phy_idx phy_idx, u8 idx)
+{
+	if (phy_idx == RTW89_PHY_0) {
+		rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_MLD_IDX0, idx);
+		rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_MLD_IDX0, idx);
+		rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_TBL_SEL_IDX0, idx);
+		rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_TBL_SEL_IDX0, idx);
+		rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_TBL_SEL_IDX0, idx);
+		rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_TBL_SEL_IDX0, idx);
+	} else if (phy_idx == RTW89_PHY_1) {
+		rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_MLD_IDX1, idx);
+		rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_MLD_IDX1, idx);
+		rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_TBL_SEL_IDX1, idx);
+		rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_TBL_SEL_IDX1, idx);
+		rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_TBL_SEL_IDX1, idx);
+		rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_TBL_SEL_IDX1, idx);
+	}
+
+	rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, false);
+}
 
+static void rtw8922d_chlk_ktbl_sel(struct rtw89_dev *rtwdev, u8 kpath, u8 idx)
+{
+	if (idx > 2) {
+		rtw89_warn(rtwdev, "[DBCC][ERROR]indx is out of limit!! index(%d)", idx);
 		return;
 	}
 
@@ -195,8 +229,18 @@ static void rtw8922d_chlk_reload(struct rtw89_dev *rtwdev)
 	s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[0], 0);
 	s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[1], 1);
 
+	if (conf.is_mld)
+		goto mld;
+
 	rtw8922d_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl);
 	rtw8922d_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl);
+	return;
+
+mld:
+	if (test_bit(RTW89_PHY_0, conf.hw_bitmap))
+		rtw8922d_chlk_ktbl_sel_mld(rtwdev, RTW89_PHY_0, s0_tbl);
+	if (test_bit(RTW89_PHY_1, conf.hw_bitmap))
+		rtw8922d_chlk_ktbl_sel_mld(rtwdev, RTW89_PHY_1, s1_tbl);
 }
 
 static enum _rf_syn_pow rtw8922d_get_syn_pow(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h
index c5bbe0eb972a..11742b4a8f59 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h
@@ -18,5 +18,6 @@ void rtw8922d_rfk_mlo_ctrl(struct rtw89_dev *rtwdev);
 void rtw8922d_pre_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx);
 void rtw8922d_post_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx);
 void rtw8922d_lck_track(struct rtw89_dev *rtwdev);
+void rtw8922d_chlk_ktbl_ctl_mld(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool manual);
 
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2026-04-20  3:42 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-20  3:40 [PATCH rtw-next 00/16] wifi: rtw89: add USB IO offload and some refactors Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 01/16] wifi: rtw89: 8922d: fix typo rx_freq_frome_ie Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 02/16] wifi: rtw89: 8852a: refine power save to lower latency Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 03/16] wifi: rtw89: debug: disable hw_scan for latency-sensitive scenarios Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 04/16] wifi: rtw89: debug: disable inactive power save to reduce bus overhead Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 05/16] wifi: rtw89: phy: support static PD level setting Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 06/16] wifi: rtw89: Correct data type for scan index to avoid infinite loop Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 07/16] wifi: rtw89: 8852bt: configure support_noise field explicitly Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 08/16] wifi: rtw89: add IO offload support via firmware Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 09/16] wifi: rtw89: offload DMAC and CMAC init IO to firmware Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 10/16] wifi: rtw89: use firmware offload for PHY and RF batch register writes Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 11/16] wifi: rtw89: 8832cu: Add ID 2c7c:8206 for RTL8832CU Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 12/16] wifi: rtw89: use struct to fill C2H recv ack Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 13/16] wifi: rtw89: check scan C2H event recv ack instead of C2H event done ack Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 14/16] wifi: rtw89: suspend DIG when remain-on-channel Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 15/16] wifi: rtw89: chan: introduce new helper to get entity current configuration Ping-Ke Shih
2026-04-20  3:40 ` [PATCH rtw-next 16/16] wifi: rtw89: 8922d: update RF calibration flow for MLD Ping-Ke Shih

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox