Linux wireless drivers development
 help / color / mirror / Atom feed
* RE: [PATCH] rtlwifi: rtl8192d: remove dead SMPS rate mask code
From: Ping-Ke Shih @ 2026-07-12  3:58 UTC (permalink / raw)
  To: Chelsy Ratnawat
  Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20260709194315.157030-1-chelsyratnawat2001@gmail.com>

Chelsy Ratnawat <chelsyratnawat2001@gmail.com> wrote:
> mimo_ps is initialized to IEEE80211_SMPS_OFF and never modified in
> rtl92d_update_hal_rate_table(). Therefore, the IEEE80211_SMPS_STATIC
> case is unreachable.
> Remove the unused mimo_ps variable and the dead branch.
> 
> Signed-off-by: Chelsy Ratnawat <chelsyratnawat2001@gmail.com>

Acked-by: Ping-Ke Shih <pkshih@realtek.com>

I'd use "wifi: rtlwifi: ..." as prefix while merging this patch.


^ permalink raw reply

* [PATCH rtw-next 8/8] wifi: rtw89: debug: add diagnosis for RF
From: Ping-Ke Shih @ 2026-07-12  3:45 UTC (permalink / raw)
  To: linux-wireless; +Cc: echuang, kevin_yang
In-Reply-To: <20260712034506.53209-1-pkshih@realtek.com>

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

Add debugfs diag_rf and show RFK (RF calibration) diagnosis things for
now. Record channel related info before triggering RFK, and then record
state of each kind of RFK from C2H event report. Besides, in track work,
monitor TSSI status too. Both support history up to 10, and show records
via debugfs.

The following is an example of output.

RFK (next index: 2)
  PHY-X    =    0     0     0     0     0     0     0     0     0     0
  S0-CH    = 0012a 0012a 00000 00000 00000 00000 00000 00000 00000 00000
  S0-CV    = 0032c 0032c 00000 00000 00000 00000 00000 00000 00000 00000
  S0-C5    = 10000 10000 00000 00000 00000 00000 00000 00000 00000 00000
  S1-CH    = 0012a 0012a 00000 00000 00000 00000 00000 00000 00000 00000
  S1-CV    = 0032d 0032d 00000 00000 00000 00000 00000 00000 00000 00000
  S1-C5    = 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000
  PRE_NTFY =    0     0     0     0     0     0     0     0     0     0
  TSSI     =    1     1     0     0     0     0     0     0     0     0
  IQK      =    1     1     0     0     0     0     0     0     0     0
  DPK      =    1     1     0     0     0     0     0     0     0     0
  TXGAPK   =    1     1     0     0     0     0     0     0     0     0
  DACK     =    0     0     0     0     0     0     0     0     0     0
  RX_DCK   =    1     1     0     0     0     0     0     0     0     0
  TX_IQK   =    1     1     0     0     0     0     0     0     0     0
  CIM3k    =    1     1     0     0     0     0     0     0     0     0
TSSI-track (next index: 6)
  S0       =   00e   00e   00c   00d   00d   00e   00d   00d   00d   00e
  S1       =   00a   00b   009   00a   00a   00a   009   009   009   00a

Debugfs diag_rf can also be used to manually trigger RFK when written by 1.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c  |  71 ++++++++++++-
 drivers/net/wireless/realtek/rtw89/core.h  |  32 ++++++
 drivers/net/wireless/realtek/rtw89/debug.c | 115 +++++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/phy.c   |  62 +++++++++--
 drivers/net/wireless/realtek/rtw89/phy.h   |   3 +
 drivers/net/wireless/realtek/rtw89/reg.h   |   1 +
 6 files changed, 272 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 3950792651c1..5c58ce311c1b 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -486,6 +486,37 @@ void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev)
 	__rtw89_core_set_chip_txpwr(rtwdev, conf.chans[1], RTW89_PHY_1);
 }
 
+static void rtw89_core_rfk_record(struct rtw89_dev *rtwdev,
+				  struct rtw89_vif_link *rtwvif_link,
+				  bool start)
+{
+	struct rtw89_rfk_wait_info *info = &rtwdev->rfk_wait;
+	struct rtw89_rfk_record *record = &info->records[info->record_idx];
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	u8 path_num = min(chip->rf_path_num, RTW89_RFK_RECORD_PATH_NR);
+	int path;
+
+	if (!start)
+		goto end;
+
+	info->record_ptr = record;
+	record->phy_idx = rtwvif_link->phy_idx;
+
+	for (path = 0; path < path_num; path++) {
+		record->ch[path] = rtw89_read_rf(rtwdev, path, RR_CFGCH,
+						 RR_CFGCH_BAND0 | RR_CFGCH_CH);
+		record->cv[path] = rtw89_read_rf(rtwdev, path, RR_VCO,
+						 RR_VCO_SEL | RR_VCO_STS);
+		record->c5[path] = rtw89_read_rf(rtwdev, path, RR_SYNFB, RFREG_MASK);
+	}
+
+	return;
+
+end:
+	info->record_idx = (info->record_idx + 1) % RTW89_RFK_RECORD_HISTORY_NR;
+	info->record_ptr = NULL;
+}
+
 void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev,
 			    struct rtw89_vif_link *rtwvif_link)
 {
@@ -503,9 +534,14 @@ void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev,
 		rtw89_set_channel(rtwdev);
 	}
 
-	if (chip->ops->rfk_channel)
+	if (chip->ops->rfk_channel) {
+		rtw89_core_rfk_record(rtwdev, rtwvif_link, true);
+
 		chip->ops->rfk_channel(rtwdev, rtwvif_link);
 
+		rtw89_core_rfk_record(rtwdev, rtwvif_link, false);
+	}
+
 	if (prehdl_link) {
 		rtw89_entity_force_hw(rtwdev, RTW89_PHY_NUM);
 		rtw89_set_channel(rtwdev);
@@ -5274,6 +5310,37 @@ static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev,
 	}
 }
 
+static void rtw89_core_rfk_tssi_record(struct rtw89_dev *rtwdev)
+{
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	u8 path_num = min(chip->rf_path_num, RTW89_RFK_RECORD_PATH_NR);
+	struct rtw89_rfk_wait_info *info = &rtwdev->rfk_wait;
+	int i = info->record_tssi_idx;
+	u32 base;
+	u32 mask;
+	int path;
+
+	if (chip->chip_gen == RTW89_CHIP_AX)
+		return;
+
+	info->record_tssi_idx = (info->record_tssi_idx + 1) %
+				RTW89_RFK_RECORD_HISTORY_NR;
+
+	if (chip->chip_id == RTL8922A) {
+		base = 0x2ee10;
+		mask = 0x7fc00;
+	} else {
+		base = 0x2f918;
+		mask = 0x000001ff;
+	}
+
+	for (path = 0; path < path_num; path++) {
+		u32 addr = base + 0x100 * path;
+
+		info->tssi_code[i][path] = rtw89_read32_mask(rtwdev, addr, mask);
+	}
+}
+
 static void rtw89_core_rfk_track(struct rtw89_dev *rtwdev)
 {
 	enum rtw89_entity_mode mode;
@@ -5283,6 +5350,8 @@ static void rtw89_core_rfk_track(struct rtw89_dev *rtwdev)
 		return;
 
 	rtw89_chip_rfk_track(rtwdev);
+
+	rtw89_core_rfk_tssi_record(rtwdev);
 }
 
 void rtw89_core_update_p2p_ps(struct rtw89_dev *rtwdev,
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 4ba6452a7123..6fec9ce59f7a 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5728,11 +5728,43 @@ enum rtw89_rfk_report_state {
 	RTW89_RFK_STATE_H2C_CMD_ERR = 0x4,
 };
 
+enum rtw89_rfk_report_types {
+	RTW89_RFK_REPORT_PRE_NTFY,
+	RTW89_RFK_REPORT_TSSI,
+	RTW89_RFK_REPORT_IQK,
+	RTW89_RFK_REPORT_DPK,
+	RTW89_RFK_REPORT_TXGAPK,
+	RTW89_RFK_REPORT_DACK,
+	RTW89_RFK_REPORT_RX_DCK,
+	RTW89_RFK_REPORT_TX_IQK,
+	RTW89_RFK_REPORT_CIM3k,
+
+	NUM_OF_RTW89_RFK_REPORT_TYPES,
+};
+
+#define RTW89_RFK_RECORD_PATH_NR 2
+#define RTW89_RFK_RECORD_HISTORY_NR 10
+
+struct rtw89_rfk_record {
+	u32 ch[RTW89_RFK_RECORD_PATH_NR];
+	u32 cv[RTW89_RFK_RECORD_PATH_NR];
+	u32 c5[RTW89_RFK_RECORD_PATH_NR];
+	enum rtw89_phy_idx phy_idx;
+	enum rtw89_rfk_report_state states[NUM_OF_RTW89_RFK_REPORT_TYPES];
+};
+
 struct rtw89_rfk_wait_info {
 	struct completion completion;
 	ktime_t start_time;
 	enum rtw89_rfk_report_state state;
 	u8 version;
+
+	int record_idx;
+	struct rtw89_rfk_record *record_ptr;
+	struct rtw89_rfk_record records[RTW89_RFK_RECORD_HISTORY_NR];
+
+	int record_tssi_idx;
+	u32 tssi_code[RTW89_RFK_RECORD_HISTORY_NR][RTW89_RFK_RECORD_PATH_NR];
 };
 
 #define RTW89_DACK_PATH_NR 2
diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index e42dc5707576..ccfd69271f90 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -93,6 +93,7 @@ struct rtw89_debugfs {
 	struct rtw89_debugfs_priv beacon_info;
 	struct rtw89_debugfs_priv diag_mac;
 	struct rtw89_debugfs_priv diag_bb;
+	struct rtw89_debugfs_priv diag_rf;
 	struct rtw89_debugfs_priv monitor_opts;
 };
 
@@ -5397,6 +5398,118 @@ rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev,
 	return p - buf;
 }
 
+static ssize_t
+rtw89_debug_priv_diag_rf_get(struct rtw89_dev *rtwdev,
+			     struct rtw89_debugfs_priv *debugfs_priv,
+			     char *buf, size_t bufsz)
+{
+	struct rtw89_rfk_wait_info *info = &rtwdev->rfk_wait;
+	char *p = buf, *end = buf + bufsz;
+	int i, j;
+
+	p += scnprintf(p, end - p, "RFK (next index: %d)\n", info->record_idx);
+
+	p += scnprintf(p, end - p, "  PHY-X    =");
+	for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++) {
+		struct rtw89_rfk_record *record = &info->records[i];
+
+		p += scnprintf(p, end - p, " %4d ", record->phy_idx);
+	}
+	p += scnprintf(p, end - p, "\n");
+
+	for (j = 0; j < RTW89_RFK_RECORD_PATH_NR; j++) {
+		p += scnprintf(p, end - p, "  S%d-CH    =", j);
+		for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++) {
+			struct rtw89_rfk_record *record = &info->records[i];
+
+			p += scnprintf(p, end - p, " %05x", record->ch[j]);
+		}
+		p += scnprintf(p, end - p, "\n");
+
+		p += scnprintf(p, end - p, "  S%d-CV    =", j);
+		for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++) {
+			struct rtw89_rfk_record *record = &info->records[i];
+
+			p += scnprintf(p, end - p, " %05x", record->cv[j]);
+		}
+		p += scnprintf(p, end - p, "\n");
+
+		p += scnprintf(p, end - p, "  S%d-C5    =", j);
+		for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++) {
+			struct rtw89_rfk_record *record = &info->records[i];
+
+			p += scnprintf(p, end - p, " %05x", record->c5[j]);
+		}
+		p += scnprintf(p, end - p, "\n");
+	}
+
+	for (j = 0; j < NUM_OF_RTW89_RFK_REPORT_TYPES; j++) {
+		p += scnprintf(p, end - p, "  %-8s =", rtw89_rfk_report_names[j]);
+		for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++) {
+			struct rtw89_rfk_record *record = &info->records[i];
+
+			p += scnprintf(p, end - p, " %4d ", record->states[j]);
+		}
+		p += scnprintf(p, end - p, "\n");
+	}
+
+	p += scnprintf(p, end - p, "TSSI-track (next index: %d)\n", info->record_tssi_idx);
+
+	for (j = 0; j < RTW89_RFK_RECORD_PATH_NR; j++) {
+		p += scnprintf(p, end - p, "  S%d       =", j);
+		for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++)
+			p += scnprintf(p, end - p, "   %03x", info->tssi_code[i][j]);
+		p += scnprintf(p, end - p, "\n");
+	}
+
+	return p - buf;
+}
+
+static void rtw89_dbg_diag_rf_set_rfk(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt;
+	struct rtw89_vif_link *rtwvif_link;
+	struct rtw89_vif *rtwvif;
+	unsigned int link_id;
+
+	list_for_each_entry(rtwvif, &mgnt->active_list, mgnt_entry)
+		rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id)
+			rtw89_chip_rfk_channel(rtwdev, rtwvif_link);
+}
+
+enum rtw89_dbg_diag_rf_type {
+	RTW89_DBG_DIAG_RF_RSVD = 0,
+	RTW89_DBG_DIAG_RF_RFK = 1,
+};
+
+static ssize_t
+rtw89_debug_priv_diag_rf_set(struct rtw89_dev *rtwdev,
+			     struct rtw89_debugfs_priv *debugfs_priv,
+			     const char *buf, size_t count)
+{
+	u8 type;
+	int ret;
+
+	ret = kstrtou8(buf, 0, &type);
+	if (ret)
+		return -EINVAL;
+
+	if (rtwdev->scanning)
+		rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
+
+	rtw89_leave_ps_mode(rtwdev);
+
+	switch (type) {
+	case RTW89_DBG_DIAG_RF_RFK:
+		rtw89_dbg_diag_rf_set_rfk(rtwdev);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return count;
+}
+
 static ssize_t
 rtw89_debug_priv_monitor_opts_get(struct rtw89_dev *rtwdev,
 				  struct rtw89_debugfs_priv *debugfs_priv,
@@ -5509,6 +5622,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = {
 	.beacon_info = rtw89_debug_priv_get(beacon_info),
 	.diag_mac = rtw89_debug_priv_get(diag_mac, RSIZE_16K, RLOCK),
 	.diag_bb = rtw89_debug_priv_get(diag_bb, RSIZE_8K, RLOCK),
+	.diag_rf = rtw89_debug_priv_set_and_get(diag_rf, RWLOCK),
 	.monitor_opts = rtw89_debug_priv_set_and_get(monitor_opts, RWLOCK),
 };
 
@@ -5566,6 +5680,7 @@ void rtw89_debugfs_add_sec2(struct rtw89_dev *rtwdev, struct dentry *debugfs_top
 	rtw89_debugfs_add_r(beacon_info);
 	rtw89_debugfs_add_r(diag_mac);
 	rtw89_debugfs_add_r(diag_bb);
+	rtw89_debugfs_add_rw(diag_rf);
 	rtw89_debugfs_add_rw(monitor_opts);
 }
 
diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index 30d5741da87e..c78107e92206 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -3968,6 +3968,23 @@ void (* const rtw89_phy_c2h_rfk_log_handler[])(struct rtw89_dev *rtwdev,
 	[RTW89_PHY_C2H_RFK_LOG_FUNC_CIM3K] = rtw89_phy_c2h_rfk_log_cim3k,
 };
 
+#define NAME_RTW89_RFK_REPORT(type) \
+	[RTW89_RFK_REPORT_ ## type] = #type
+
+const char * const rtw89_rfk_report_names[] = {
+	NAME_RTW89_RFK_REPORT(PRE_NTFY),
+	NAME_RTW89_RFK_REPORT(TSSI),
+	NAME_RTW89_RFK_REPORT(IQK),
+	NAME_RTW89_RFK_REPORT(DPK),
+	NAME_RTW89_RFK_REPORT(TXGAPK),
+	NAME_RTW89_RFK_REPORT(DACK),
+	NAME_RTW89_RFK_REPORT(RX_DCK),
+	NAME_RTW89_RFK_REPORT(TX_IQK),
+	NAME_RTW89_RFK_REPORT(CIM3k),
+};
+
+static_assert(ARRAY_SIZE(rtw89_rfk_report_names) == NUM_OF_RTW89_RFK_REPORT_TYPES);
+
 static
 void rtw89_phy_rfk_report_prep(struct rtw89_dev *rtwdev)
 {
@@ -3979,8 +3996,8 @@ void rtw89_phy_rfk_report_prep(struct rtw89_dev *rtwdev)
 }
 
 static
-int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name,
-			      unsigned int ms)
+int ____rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name,
+				  unsigned int ms)
 {
 	struct rtw89_rfk_wait_info *wait = &rtwdev->rfk_wait;
 	unsigned long time_left;
@@ -4017,6 +4034,29 @@ int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name,
 	return 0;
 }
 
+static
+int __rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev,
+				enum rtw89_rfk_report_types rfk_type,
+				enum rtw89_phy_idx phy_idx,
+				const struct rtw89_chan *chan,
+				unsigned int ms)
+{
+	const char *rfk_name = rtw89_rfk_report_names[rfk_type];
+	struct rtw89_rfk_wait_info *wait = &rtwdev->rfk_wait;
+	struct rtw89_rfk_record *record = wait->record_ptr;
+	int ret;
+
+	ret = ____rtw89_phy_rfk_report_wait(rtwdev, rfk_name, ms);
+
+	if (record)
+		record->states[rfk_type] = wait->state;
+
+	return ret;
+}
+
+#define rtw89_phy_rfk_report_wait(rtwdev, type, phy_idx, chan, ms) \
+	__rtw89_phy_rfk_report_wait(rtwdev, RTW89_RFK_REPORT_ ## type, phy_idx, chan, ms)
+
 static void
 rtw89_phy_c2h_rfk_report_state(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
 {
@@ -4122,7 +4162,7 @@ int rtw89_phy_rfk_pre_ntfy_and_wait(struct rtw89_dev *rtwdev,
 	if (RTW89_CHK_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, &rtwdev->fw)) {
 		rtw89_phy_rfk_report_prep(rtwdev);
 		rtw89_fw_h2c_rf_pre_ntfy(rtwdev, phy_idx);
-		ret = rtw89_phy_rfk_report_wait(rtwdev, "PRE_NTFY", ms);
+		ret = rtw89_phy_rfk_report_wait(rtwdev, PRE_NTFY, phy_idx, NULL, ms);
 		if (ret)
 			return ret;
 	}
@@ -4152,7 +4192,7 @@ int rtw89_phy_rfk_tssi_and_wait(struct rtw89_dev *rtwdev,
 	if (ret)
 		return ret;
 
-	return rtw89_phy_rfk_report_wait(rtwdev, "TSSI", ms);
+	return rtw89_phy_rfk_report_wait(rtwdev, TSSI, phy_idx, chan, ms);
 }
 EXPORT_SYMBOL(rtw89_phy_rfk_tssi_and_wait);
 
@@ -4169,7 +4209,7 @@ int rtw89_phy_rfk_iqk_and_wait(struct rtw89_dev *rtwdev,
 	if (ret)
 		return ret;
 
-	return rtw89_phy_rfk_report_wait(rtwdev, "IQK", ms);
+	return rtw89_phy_rfk_report_wait(rtwdev, IQK, phy_idx, chan, ms);
 }
 EXPORT_SYMBOL(rtw89_phy_rfk_iqk_and_wait);
 
@@ -4186,7 +4226,7 @@ int rtw89_phy_rfk_dpk_and_wait(struct rtw89_dev *rtwdev,
 	if (ret)
 		return ret;
 
-	return rtw89_phy_rfk_report_wait(rtwdev, "DPK", ms);
+	return rtw89_phy_rfk_report_wait(rtwdev, DPK, phy_idx, chan, ms);
 }
 EXPORT_SYMBOL(rtw89_phy_rfk_dpk_and_wait);
 
@@ -4203,7 +4243,7 @@ int rtw89_phy_rfk_txgapk_and_wait(struct rtw89_dev *rtwdev,
 	if (ret)
 		return ret;
 
-	return rtw89_phy_rfk_report_wait(rtwdev, "TXGAPK", ms);
+	return rtw89_phy_rfk_report_wait(rtwdev, TXGAPK, phy_idx, chan, ms);
 }
 EXPORT_SYMBOL(rtw89_phy_rfk_txgapk_and_wait);
 
@@ -4220,7 +4260,7 @@ int rtw89_phy_rfk_dack_and_wait(struct rtw89_dev *rtwdev,
 	if (ret)
 		return ret;
 
-	return rtw89_phy_rfk_report_wait(rtwdev, "DACK", ms);
+	return rtw89_phy_rfk_report_wait(rtwdev, DACK, phy_idx, chan, ms);
 }
 EXPORT_SYMBOL(rtw89_phy_rfk_dack_and_wait);
 
@@ -4237,7 +4277,7 @@ int rtw89_phy_rfk_rxdck_and_wait(struct rtw89_dev *rtwdev,
 	if (ret)
 		return ret;
 
-	return rtw89_phy_rfk_report_wait(rtwdev, "RX_DCK", ms);
+	return rtw89_phy_rfk_report_wait(rtwdev, RX_DCK, phy_idx, chan, ms);
 }
 EXPORT_SYMBOL(rtw89_phy_rfk_rxdck_and_wait);
 
@@ -4254,7 +4294,7 @@ int rtw89_phy_rfk_txiqk_and_wait(struct rtw89_dev *rtwdev,
 	if (ret)
 		return ret;
 
-	return rtw89_phy_rfk_report_wait(rtwdev, "TX_IQK", ms);
+	return rtw89_phy_rfk_report_wait(rtwdev, TX_IQK, phy_idx, chan, ms);
 }
 EXPORT_SYMBOL(rtw89_phy_rfk_txiqk_and_wait);
 
@@ -4271,7 +4311,7 @@ int rtw89_phy_rfk_cim3k_and_wait(struct rtw89_dev *rtwdev,
 	if (ret)
 		return ret;
 
-	return rtw89_phy_rfk_report_wait(rtwdev, "CIM3k", ms);
+	return rtw89_phy_rfk_report_wait(rtwdev, CIM3k, phy_idx, chan, ms);
 }
 EXPORT_SYMBOL(rtw89_phy_rfk_cim3k_and_wait);
 
diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h
index b0c82bac72f0..0963cdfb1594 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.h
+++ b/drivers/net/wireless/realtek/rtw89/phy.h
@@ -1150,6 +1150,9 @@ void rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev,
 bool rtw89_phy_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func);
 void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
 			  u32 len, u8 class, u8 func);
+
+extern const char * const rtw89_rfk_report_names[];
+
 int rtw89_phy_rfk_pre_ntfy_and_wait(struct rtw89_dev *rtwdev,
 				    enum rtw89_phy_idx phy_idx,
 				    unsigned int ms);
diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h
index 4d2117de798b..3bf2865b81ab 100644
--- a/drivers/net/wireless/realtek/rtw89/reg.h
+++ b/drivers/net/wireless/realtek/rtw89/reg.h
@@ -8763,6 +8763,7 @@
 #define RR_VCO 0xb2
 #define RR_VCO_VAL GENMASK(18, 14)
 #define RR_VCO_SEL GENMASK(9, 8)
+#define RR_VCO_STS GENMASK(7, 0)
 #define RR_VCI 0xb3
 #define RR_VCI_ON BIT(7)
 #define RR_LPF 0xb7
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 7/8] wifi: rtw89: fw: do bb_preinit before downloading firmware
From: Ping-Ke Shih @ 2026-07-12  3:45 UTC (permalink / raw)
  To: linux-wireless; +Cc: echuang, kevin_yang
In-Reply-To: <20260712034506.53209-1-pkshih@realtek.com>

The firmware access BB registers while initialization, so driver should
do bb_preinit before downloading firmware. Otherwise, it might get BB IO
stuck and throw error.

  rtw89_8922de 0000:04:00.0: loaded firmware rtw89/rtw8922d_fw.bin
  rtw89_8922de 0000:04:00.0: Firmware version 0.35.111.7 (51c56e7b), cmd version 1, type 14
  rtw89_8922de 0000:04:00.0: Firmware version 0.35.111.7 (51c56e7b), cmd version 1, type 15
  rtw89_8922de 0000:04:00.0: fw unexpected status 6
  rtw89_8922de 0000:04:00.0: download firmware fail
  rtw89_8922de 0000:04:00.0: [ERR]fwdl 0x1E0 = 0x8000012
  rtw89_8922de 0000:04:00.0: [ERR]fwdl 0x78F0 = 0x290900
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2
  rtw89_8922de 0000:04:00.0: [ERR]H2C path ready

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 12 +++++++++++-
 drivers/net/wireless/realtek/rtw89/mac.c  |  7 ++++---
 drivers/net/wireless/realtek/rtw89/mac.h  |  3 ++-
 3 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 0343cd1a0ee1..3950792651c1 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -7197,9 +7197,19 @@ int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
 static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev)
 {
 	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	bool bb_preinit = false;
 	int ret;
 
-	ret = rtw89_mac_partial_init(rtwdev, false);
+	/*
+	 * Normally in probe stage, we don't need to do BB pre-initialization.
+	 * However, RTL8922D firmware can access BB registers at firmware
+	 * initial state, so initialize it to avoid BB IO stuck in firmware.
+	 */
+	if (chip->chip_id == RTL8922D)
+		bb_preinit = true;
+
+	ret = rtw89_mac_partial_init(rtwdev, false, bb_preinit);
 	if (ret)
 		return ret;
 
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index a63f34440554..accc5cdfaa3d 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -4308,13 +4308,14 @@ int rtw89_mac_disable_bb_rf(struct rtw89_dev *rtwdev)
 }
 EXPORT_SYMBOL(rtw89_mac_disable_bb_rf);
 
-int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb)
+int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb,
+			   bool bb_preinit)
 {
 	int ret;
 
 	rtw89_mac_ctrl_hci_dma_trx(rtwdev, true);
 
-	if (include_bb) {
+	if (include_bb || bb_preinit) {
 		/* Only call BB preinit including configuration of BB MCU for
 		 * the chips which need to download BB MCU firmware. Otherwise,
 		 * calling preinit later to prevent touching registers affecting
@@ -4367,7 +4368,7 @@ int rtw89_mac_init(struct rtw89_dev *rtwdev)
 	bool include_bb = !!chip->bbmcu_nr;
 	int ret;
 
-	ret = rtw89_mac_partial_init(rtwdev, include_bb);
+	ret = rtw89_mac_partial_init(rtwdev, include_bb, include_bb);
 	if (ret)
 		goto fail;
 
diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h
index c05f5ee0d2fd..493d2b9626a6 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.h
+++ b/drivers/net/wireless/realtek/rtw89/mac.h
@@ -1302,7 +1302,8 @@ rtw89_write32_port_set(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l
 
 int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev);
 void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev);
-int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb);
+int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb,
+			   bool bb_preinit);
 int rtw89_mac_preinit(struct rtw89_dev *rtwdev);
 int rtw89_mac_init(struct rtw89_dev *rtwdev);
 int rtw89_mac_dle_init(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode,
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 6/8] wifi: rtw89: extend tx shape format for regulatory 6 GHz power type
From: Ping-Ke Shih @ 2026-07-12  3:45 UTC (permalink / raw)
  To: linux-wireless; +Cc: echuang, kevin_yang
In-Reply-To: <20260712034506.53209-1-pkshih@realtek.com>

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

Even under the same regulation, TX shape may need different settings for
different 6 GHz power types. So, add one more dimension for that. Because
TX shape parameters are not quite large, the 2/5/6 GHz sections are not
divided into different structures. So, the 2/5 GHz sections will also get
the new dimension. To 2/5 GHz sections, fill the TX shape settings with
RTW89_REG_6GHZ_POWER_DFLT (0) field.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.h | 17 ++++++++++++-----
 drivers/net/wireless/realtek/rtw89/fw.c   | 16 ++++++++++++++--
 drivers/net/wireless/realtek/rtw89/fw.h   |  2 ++
 3 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 94ba7e368880..4ba6452a7123 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -4516,8 +4516,9 @@ struct rtw89_txpwr_rule_6ghz {
 };
 
 struct rtw89_tx_shape {
-	const u8 (*lmt)[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM];
-	const u8 (*lmt_ru)[RTW89_BAND_NUM][RTW89_REGD_NUM];
+	const u8 (*lmt)[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM]
+		       [NUM_OF_RTW89_REG_6GHZ_POWER];
+	const u8 (*lmt_ru)[RTW89_BAND_NUM][RTW89_REGD_NUM][NUM_OF_RTW89_REG_6GHZ_POWER];
 
 	const u8 (*lmt_v0)[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM];
 	const u8 (*lmt_ru_v0)[RTW89_BAND_NUM][RTW89_REGD_NUM];
@@ -4614,12 +4615,13 @@ struct rtw89_txpwr_lmt_ru_6ghz_data {
 
 struct rtw89_tx_shape_lmt_data {
 	struct rtw89_txpwr_conf conf;
-	u8 v[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM];
+	u8 v[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM]
+	    [NUM_OF_RTW89_REG_6GHZ_POWER];
 };
 
 struct rtw89_tx_shape_lmt_ru_data {
 	struct rtw89_txpwr_conf conf;
-	u8 v[RTW89_BAND_NUM][RTW89_REGD_NUM];
+	u8 v[RTW89_BAND_NUM][RTW89_REGD_NUM][NUM_OF_RTW89_REG_6GHZ_POWER];
 };
 
 struct rtw89_rfe_data {
@@ -8048,8 +8050,10 @@ static inline u8 rtw89_regd_get(struct rtw89_dev *rtwdev, u8 band)
 static inline u8 rtw89_get_tx_shape_idx(struct rtw89_dev *rtwdev, u8 band,
 					enum rtw89_rate_section rs)
 {
+	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
 	const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
 	const struct rtw89_tx_shape *tx_shape = &rfe_parms->tx_shape;
+	u8 reg6_pwr = regulatory->reg_6ghz_power;
 	u8 regd = rtw89_regd_get(rtwdev, band);
 
 	if (unlikely(rs >= RTW89_RS_TX_SHAPE_NUM))
@@ -8058,7 +8062,10 @@ static inline u8 rtw89_get_tx_shape_idx(struct rtw89_dev *rtwdev, u8 band,
 	if (!tx_shape->lmt)
 		goto v0;
 
-	return (*tx_shape->lmt)[band][rs][regd];
+	if (band != RTW89_BAND_6G)
+		reg6_pwr = RTW89_REG_6GHZ_POWER_DFLT;
+
+	return (*tx_shape->lmt)[band][rs][regd][reg6_pwr];
 
 v0:
 	return (*tx_shape->lmt_v0)[band][rs][regd];
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 01770a6a1f6f..95acf7caf08e 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -11626,6 +11626,12 @@ fw_tx_shape_lmt_entry_valid(const struct rtw89_fw_tx_shape_lmt_entry *e,
 	if (e->regd >= RTW89_REGD_NUM)
 		return false;
 
+	/* ensure compatibility work with 2/5 GHz */
+	static_assert(RTW89_REG_6GHZ_POWER_DFLT == 0);
+
+	if (e->reg6_pwr >= NUM_OF_RTW89_REG_6GHZ_POWER)
+		return false;
+
 	return true;
 }
 
@@ -11640,7 +11646,7 @@ void rtw89_fw_load_tx_shape_lmt(struct rtw89_tx_shape_lmt_data *data)
 		if (!fw_tx_shape_lmt_entry_valid(&entry, cursor, conf))
 			continue;
 
-		data->v[entry.band][entry.tx_shape_rs][entry.regd] = entry.v;
+		data->v[entry.band][entry.tx_shape_rs][entry.regd][entry.reg6_pwr] = entry.v;
 	}
 }
 
@@ -11657,6 +11663,12 @@ fw_tx_shape_lmt_ru_entry_valid(const struct rtw89_fw_tx_shape_lmt_ru_entry *e,
 	if (e->regd >= RTW89_REGD_NUM)
 		return false;
 
+	/* ensure compatibility work with 2/5 GHz */
+	static_assert(RTW89_REG_6GHZ_POWER_DFLT == 0);
+
+	if (e->reg6_pwr >= NUM_OF_RTW89_REG_6GHZ_POWER)
+		return false;
+
 	return true;
 }
 
@@ -11671,7 +11683,7 @@ void rtw89_fw_load_tx_shape_lmt_ru(struct rtw89_tx_shape_lmt_ru_data *data)
 		if (!fw_tx_shape_lmt_ru_entry_valid(&entry, cursor, conf))
 			continue;
 
-		data->v[entry.band][entry.regd] = entry.v;
+		data->v[entry.band][entry.regd][entry.reg6_pwr] = entry.v;
 	}
 }
 
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index af126d15a1fb..5efa75f8c392 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -5834,6 +5834,7 @@ struct rtw89_fw_tx_shape_lmt_entry {
 	u8 tx_shape_rs;
 	u8 regd;
 	u8 v;
+	u8 reg6_pwr;
 } __packed;
 
 /* must consider compatibility; don't insert new in the mid */
@@ -5841,6 +5842,7 @@ struct rtw89_fw_tx_shape_lmt_ru_entry {
 	u8 band;
 	u8 regd;
 	u8 v;
+	u8 reg6_pwr;
 } __packed;
 
 const struct rtw89_rfe_parms *
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 5/8] wifi: rtw89: add tx shape v0 to keep built-in arrays compatible during transitions
From: Ping-Ke Shih @ 2026-07-12  3:45 UTC (permalink / raw)
  To: linux-wireless; +Cc: echuang, kevin_yang
In-Reply-To: <20260712034506.53209-1-pkshih@realtek.com>

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

TX shape parameters can come from (old way) built-in arrays or (new way)
FW elements. The built-in arrays will no longer be updated, but will be
retained during a certain transition period. However, the format of newer
TX shape parameters are going to be expanded. It will only be applied to
FW elements. To keep built-in arrays compatible during transition period,
add tx shape v0 for old format.

The v0 fields can be removed along with built-in arrays once transition
period ends.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.h           | 9 +++++++++
 drivers/net/wireless/realtek/rtw89/rtw8851b_table.c | 8 ++++----
 drivers/net/wireless/realtek/rtw89/rtw8852b_table.c | 4 ++--
 drivers/net/wireless/realtek/rtw89/rtw8852c_table.c | 4 ++--
 4 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 1e323007421c..94ba7e368880 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -4518,6 +4518,9 @@ struct rtw89_txpwr_rule_6ghz {
 struct rtw89_tx_shape {
 	const u8 (*lmt)[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM];
 	const u8 (*lmt_ru)[RTW89_BAND_NUM][RTW89_REGD_NUM];
+
+	const u8 (*lmt_v0)[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM];
+	const u8 (*lmt_ru_v0)[RTW89_BAND_NUM][RTW89_REGD_NUM];
 };
 
 struct rtw89_rfe_parms {
@@ -8052,7 +8055,13 @@ static inline u8 rtw89_get_tx_shape_idx(struct rtw89_dev *rtwdev, u8 band,
 	if (unlikely(rs >= RTW89_RS_TX_SHAPE_NUM))
 		rs = RTW89_RS_OFDM;
 
+	if (!tx_shape->lmt)
+		goto v0;
+
 	return (*tx_shape->lmt)[band][rs][regd];
+
+v0:
+	return (*tx_shape->lmt_v0)[band][rs][regd];
 }
 
 static inline void rtw89_ctrl_btg_bt_rx(struct rtw89_dev *rtwdev, bool en,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c b/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c
index b8105f6e94f1..f875beaec555 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c
@@ -14887,8 +14887,8 @@ const struct rtw89_rfe_parms rtw89_8851b_dflt_parms = {
 		.lmt_ru = &rtw89_8851b_txpwr_lmt_ru_5g,
 	},
 	.tx_shape = {
-		.lmt = &rtw89_8851b_tx_shape_lmt,
-		.lmt_ru = &rtw89_8851b_tx_shape_lmt_ru,
+		.lmt_v0 = &rtw89_8851b_tx_shape_lmt,
+		.lmt_ru_v0 = &rtw89_8851b_tx_shape_lmt_ru,
 	},
 };
 
@@ -14903,8 +14903,8 @@ static const struct rtw89_rfe_parms rtw89_8851b_rfe_parms_type2 = {
 		.lmt_ru = &rtw89_8851b_txpwr_lmt_ru_5g_type2,
 	},
 	.tx_shape = {
-		.lmt = &rtw89_8851b_tx_shape_lmt,
-		.lmt_ru = &rtw89_8851b_tx_shape_lmt_ru,
+		.lmt_v0 = &rtw89_8851b_tx_shape_lmt,
+		.lmt_ru_v0 = &rtw89_8851b_tx_shape_lmt_ru,
 	},
 };
 
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c
index 96b18e9095b3..1485a8fc4bde 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c
@@ -22922,7 +22922,7 @@ const struct rtw89_rfe_parms rtw89_8852b_dflt_parms = {
 		.lmt_ru = &rtw89_8852b_txpwr_lmt_ru_5g,
 	},
 	.tx_shape = {
-		.lmt = &rtw89_8852b_tx_shape_lmt,
-		.lmt_ru = &rtw89_8852b_tx_shape_lmt_ru,
+		.lmt_v0 = &rtw89_8852b_tx_shape_lmt,
+		.lmt_ru_v0 = &rtw89_8852b_tx_shape_lmt_ru,
 	},
 };
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c
index b4cf497e8524..c5ede63e7e23 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c
@@ -57154,7 +57154,7 @@ const struct rtw89_rfe_parms rtw89_8852c_dflt_parms = {
 		.lmt_ru = &rtw89_8852c_txpwr_lmt_ru_6g,
 	},
 	.tx_shape = {
-		.lmt = &rtw89_8852c_tx_shape_lmt,
-		.lmt_ru = &rtw89_8852c_tx_shape_lmt_ru,
+		.lmt_v0 = &rtw89_8852c_tx_shape_lmt,
+		.lmt_ru_v0 = &rtw89_8852c_tx_shape_lmt_ru,
 	},
 };
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 4/8] wifi: rtw89: introduce helper to get tx shape index
From: Ping-Ke Shih @ 2026-07-12  3:45 UTC (permalink / raw)
  To: linux-wireless; +Cc: echuang, kevin_yang
In-Reply-To: <20260712034506.53209-1-pkshih@realtek.com>

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

TX shape has a set of parameters inside RFE (RF Front End) parameters.
It also depends on regulation and even will depend on regulatory 6 GHz
power type afterwards. Introduce a helper to encapsulate the access to
TX shape index.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.h           | 13 +++++++++++++
 drivers/net/wireless/realtek/rtw89/rtw8851b.c       |  6 ++----
 .../net/wireless/realtek/rtw89/rtw8852b_common.c    |  6 ++----
 drivers/net/wireless/realtek/rtw89/rtw8852c.c       |  6 ++----
 drivers/net/wireless/realtek/rtw89/rtw8922a.c       |  9 ++-------
 drivers/net/wireless/realtek/rtw89/rtw8922d.c       |  9 ++-------
 6 files changed, 23 insertions(+), 26 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index b7536288c084..1e323007421c 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -8042,6 +8042,19 @@ static inline u8 rtw89_regd_get(struct rtw89_dev *rtwdev, u8 band)
 	return txpwr_regd;
 }
 
+static inline u8 rtw89_get_tx_shape_idx(struct rtw89_dev *rtwdev, u8 band,
+					enum rtw89_rate_section rs)
+{
+	const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
+	const struct rtw89_tx_shape *tx_shape = &rfe_parms->tx_shape;
+	u8 regd = rtw89_regd_get(rtwdev, band);
+
+	if (unlikely(rs >= RTW89_RS_TX_SHAPE_NUM))
+		rs = RTW89_RS_OFDM;
+
+	return (*tx_shape->lmt)[band][rs][regd];
+}
+
 static inline void rtw89_ctrl_btg_bt_rx(struct rtw89_dev *rtwdev, bool en,
 					enum rtw89_phy_idx phy_idx)
 {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
index 8dd909fc4754..38592504b4d0 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
@@ -1918,11 +1918,9 @@ static void rtw8851b_set_tx_shape(struct rtw89_dev *rtwdev,
 				  const struct rtw89_chan *chan,
 				  enum rtw89_phy_idx phy_idx)
 {
-	const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
 	u8 band = chan->band_type;
-	u8 regd = rtw89_regd_get(rtwdev, band);
-	u8 tx_shape_cck = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_CCK][regd];
-	u8 tx_shape_ofdm = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_OFDM][regd];
+	u8 tx_shape_cck = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_CCK);
+	u8 tx_shape_ofdm = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_OFDM);
 
 	if (band == RTW89_BAND_2G)
 		rtw8851b_bb_set_tx_shape_dfir(rtwdev, chan, tx_shape_cck, phy_idx);
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c
index 7d409a64869f..7c68260dce70 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c
@@ -1321,11 +1321,9 @@ static void rtw8852bx_set_tx_shape(struct rtw89_dev *rtwdev,
 				   const struct rtw89_chan *chan,
 				   enum rtw89_phy_idx phy_idx)
 {
-	const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
 	u8 band = chan->band_type;
-	u8 regd = rtw89_regd_get(rtwdev, band);
-	u8 tx_shape_cck = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_CCK][regd];
-	u8 tx_shape_ofdm = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_OFDM][regd];
+	u8 tx_shape_cck = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_CCK);
+	u8 tx_shape_ofdm = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_OFDM);
 
 	if (band == RTW89_BAND_2G)
 		rtw8852bx_bb_set_tx_shape_dfir(rtwdev, chan, tx_shape_cck, phy_idx);
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
index 54425d707bc6..c7d64fe9c2bc 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
@@ -2193,11 +2193,9 @@ static void rtw8852c_set_tx_shape(struct rtw89_dev *rtwdev,
 				  const struct rtw89_chan *chan,
 				  enum rtw89_phy_idx phy_idx)
 {
-	const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
 	u8 band = chan->band_type;
-	u8 regd = rtw89_regd_get(rtwdev, band);
-	u8 tx_shape_cck = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_CCK][regd];
-	u8 tx_shape_ofdm = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_OFDM][regd];
+	u8 tx_shape_cck = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_CCK);
+	u8 tx_shape_ofdm = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_OFDM);
 
 	if (band == RTW89_BAND_2G)
 		rtw8852c_bb_set_tx_shape_dfir(rtwdev, chan, tx_shape_cck, phy_idx);
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
index 900aa20cf6ac..86a756f51026 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
@@ -2502,15 +2502,10 @@ static void rtw8922a_set_tx_shape(struct rtw89_dev *rtwdev,
 				  const struct rtw89_chan *chan,
 				  enum rtw89_phy_idx phy_idx)
 {
-	const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
-	const struct rtw89_tx_shape *tx_shape = &rfe_parms->tx_shape;
+	u8 band = chan->band_type;
 	u8 tx_shape_idx;
-	u8 band, regd;
-
-	band = chan->band_type;
-	regd = rtw89_regd_get(rtwdev, band);
-	tx_shape_idx = (*tx_shape->lmt)[band][RTW89_RS_OFDM][regd];
 
+	tx_shape_idx = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_OFDM);
 	if (tx_shape_idx == 0)
 		rtw8922a_bb_tx_triangular(rtwdev, false, phy_idx);
 	else
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index 27e148b4213a..462165bc2485 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -2854,16 +2854,11 @@ static void rtw8922d_set_tx_shape(struct rtw89_dev *rtwdev,
 				  enum rtw89_phy_idx phy_idx)
 {
 	const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data;
-	const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;
-	const struct rtw89_tx_shape *tx_shape = &rfe_parms->tx_shape;
+	u8 band = chan->band_type;
 	u8 tx_shape_idx;
-	u8 band, regd;
 	const u16 *th;
 
-	band = chan->band_type;
-	regd = rtw89_regd_get(rtwdev, band);
-	tx_shape_idx = (*tx_shape->lmt)[band][RTW89_RS_OFDM][regd];
-
+	tx_shape_idx = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_OFDM);
 	if (tx_shape_idx == 0)
 		goto disable;
 
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 3/8] wifi: rtw89: 8922d: add TX time limit for 2GHz band
From: Ping-Ke Shih @ 2026-07-12  3:45 UTC (permalink / raw)
  To: linux-wireless; +Cc: echuang, kevin_yang
In-Reply-To: <20260712034506.53209-1-pkshih@realtek.com>

Fix 2.4GHz specific L-SIG length TX issue, causing interoperability problem
with certain APs. Limit the A-MPDU duration to be workaround.

For 8922DE, the MAC limit is 164 ticks, and BB limit is 4608 us. The
conversion is 32.768us / tick. Since smaller limit should be adopted,
BB limit is filled into newly added field.

The units of register and CCTL table are tick and us/512 respectively.
Convert to target unit when filling values.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.h     |  1 +
 drivers/net/wireless/realtek/rtw89/mac.c      | 20 +++++++++++++++++--
 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 |  9 +++++++++
 9 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 0f6d4ad66fe2..b7536288c084 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5012,6 +5012,7 @@ struct rtw89_chip_info {
 	const struct wiphy_wowlan_support *wowlan_stub;
 	const struct rtw89_xtal_info *xtal_info;
 	unsigned long default_quirks; /* bitmap of rtw89_quirks */
+	u16 txtime_limit_2ghz;
 };
 
 struct rtw89_chip_variant {
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index b58d9439ee6d..a63f34440554 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -7047,11 +7047,23 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_
 {
 #define MAC_AX_DFLT_TX_TIME 5280
 	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
+	const struct rtw89_chip_info *chip = rtwdev->chip;
 	u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx;
 	u32 max_tx_time = tx_time == 0 ? MAC_AX_DFLT_TX_TIME : tx_time;
+	struct rtw89_entity_conf conf;
+	const struct rtw89_chan *chan;
 	u32 reg;
 	int ret = 0;
 
+	if (chip->txtime_limit_2ghz) {
+		rtw89_entity_get_conf(rtwdev, &conf);
+		chan = conf.chans[mac_idx];
+
+		if (chan->band_type == RTW89_BAND_2G)
+			max_tx_time = min_t(u32, max_tx_time,
+					    chip->txtime_limit_2ghz);
+	}
+
 	if (rtwsta_link->cctl_tx_time) {
 		rtwsta_link->ampdu_max_time = (max_tx_time - 512) >> 9;
 		ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link);
@@ -7062,9 +7074,13 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_
 			return ret;
 		}
 
+		if (chip->chip_gen == RTW89_CHIP_AX)
+			max_tx_time >>= 5;
+		else
+			max_tx_time = max_tx_time * 1000 >> 15;
+
 		reg = rtw89_mac_reg_by_idx(rtwdev, mac->agg_limit.addr, mac_idx);
-		rtw89_write32_mask(rtwdev, reg, mac->agg_limit.mask,
-				   max_tx_time >> 5);
+		rtw89_write32_mask(rtwdev, reg, mac->agg_limit.mask, max_tx_time);
 	}
 
 	return ret;
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
index 50480f72c96d..8dd909fc4754 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
@@ -2779,6 +2779,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = {
 #endif
 	.xtal_info		= &rtw8851b_xtal_info,
 	.default_quirks		= 0,
+	.txtime_limit_2ghz	= 0,
 };
 EXPORT_SYMBOL(rtw8851b_chip_info);
 
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
index 0c7794742650..66e8035703a6 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
@@ -2517,6 +2517,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = {
 #endif
 	.xtal_info		= &rtw8852a_xtal_info,
 	.default_quirks		= 0,
+	.txtime_limit_2ghz	= 0,
 };
 EXPORT_SYMBOL(rtw8852a_chip_info);
 
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
index 5904127cc836..19f35b87d42e 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
@@ -1115,6 +1115,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = {
 #endif
 	.xtal_info		= NULL,
 	.default_quirks		= 0,
+	.txtime_limit_2ghz	= 0,
 };
 EXPORT_SYMBOL(rtw8852b_chip_info);
 
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
index e68f73827fa5..3da95beddc1f 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
@@ -951,6 +951,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {
 #endif
 	.xtal_info		= NULL,
 	.default_quirks		= 0,
+	.txtime_limit_2ghz	= 0,
 };
 EXPORT_SYMBOL(rtw8852bt_chip_info);
 
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
index 968666280c89..54425d707bc6 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
@@ -3320,6 +3320,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = {
 #endif
 	.xtal_info		= NULL,
 	.default_quirks		= 0,
+	.txtime_limit_2ghz	= 0,
 };
 EXPORT_SYMBOL(rtw8852c_chip_info);
 
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
index fe87b4929ddc..900aa20cf6ac 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
@@ -3302,6 +3302,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = {
 #endif
 	.xtal_info		= NULL,
 	.default_quirks		= 0,
+	.txtime_limit_2ghz	= 0,
 };
 EXPORT_SYMBOL(rtw8922a_chip_info);
 
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index 768434db14c6..27e148b4213a 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -1173,8 +1173,10 @@ static void rtw8922d_set_channel_mac(struct rtw89_dev *rtwdev,
 	u32 sub_carr = rtw89_mac_reg_by_idx(rtwdev, R_BE_TX_SUB_BAND_VALUE, mac_idx);
 	u32 chk_rate = rtw89_mac_reg_by_idx(rtwdev, R_BE_TXRATE_CHK, mac_idx);
 	u32 rf_mod = rtw89_mac_reg_by_idx(rtwdev, R_BE_WMAC_RFMOD, mac_idx);
+	const struct rtw89_chip_info *chip = rtwdev->chip;
 	u8 txsb20 = 0, txsb40 = 0, txsb80 = 0;
 	u8 rf_mod_val, chk_rate_mask, sifs;
+	u16 tx_time = AMPDU_MAX_TIME_V1;
 	u32 txsb;
 	u32 reg;
 
@@ -1255,6 +1257,12 @@ static void rtw8922d_set_channel_mac(struct rtw89_dev *rtwdev,
 
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_MUEDCA_EN, mac_idx);
 	rtw89_write32_mask(rtwdev, reg, B_BE_SIFS_MACTXEN_TB_T1_DOT05US_MASK, sifs);
+
+	if (chan->band_type == RTW89_BAND_2G && chip->txtime_limit_2ghz)
+		tx_time = min_t(u32, tx_time, chip->txtime_limit_2ghz * 1000 >> 15);
+
+	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_AMPDU_AGG_LIMIT, mac_idx);
+	rtw89_write32_mask(rtwdev, reg, B_BE_AMPDU_MAX_TIME_MASK, tx_time);
 }
 
 static const u32 rtw8922d_sco_barker_threshold[14] = {
@@ -3591,6 +3599,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = {
 #endif
 	.xtal_info		= NULL,
 	.default_quirks		= BIT(RTW89_QUIRK_THERMAL_PROT_120C),
+	.txtime_limit_2ghz	= 4608,
 };
 EXPORT_SYMBOL(rtw8922d_chip_info);
 
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 2/8] wifi: rtw89: mac: abstract register definition of firmware boot debug
From: Ping-Ke Shih @ 2026-07-12  3:45 UTC (permalink / raw)
  To: linux-wireless; +Cc: echuang, kevin_yang
In-Reply-To: <20260712034506.53209-1-pkshih@realtek.com>

The registers of firmware boot debug are different between WiFi 6 and 7
chips. Add field to abstract it accordingly.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/fw.c     | 5 +++--
 drivers/net/wireless/realtek/rtw89/mac.c    | 1 +
 drivers/net/wireless/realtek/rtw89/mac.h    | 1 +
 drivers/net/wireless/realtek/rtw89/mac_be.c | 1 +
 4 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 0e7168605850..01770a6a1f6f 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -1939,13 +1939,14 @@ static void rtw89_fw_prog_cnt_dump(struct rtw89_dev *rtwdev)
 
 static void rtw89_fw_dl_fail_dump(struct rtw89_dev *rtwdev)
 {
+	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
 	u32 val32;
 
 	val32 = rtw89_read32(rtwdev, R_AX_WCPU_FW_CTRL);
 	rtw89_err(rtwdev, "[ERR]fwdl 0x1E0 = 0x%x\n", val32);
 
-	val32 = rtw89_read32(rtwdev, R_AX_BOOT_DBG);
-	rtw89_err(rtwdev, "[ERR]fwdl 0x83F0 = 0x%x\n", val32);
+	val32 = rtw89_read32(rtwdev, mac->boot_dbg);
+	rtw89_err(rtwdev, "[ERR]fwdl 0x%x = 0x%x\n", mac->boot_dbg, val32);
 
 	rtw89_fw_prog_cnt_dump(rtwdev);
 }
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index 6e3da5e4a1b3..b58d9439ee6d 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -7442,6 +7442,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = {
 	.agg_len_ht = R_AX_AGG_LEN_HT_0,
 	.ps_status = R_AX_PPWRBIT_SETTING,
 	.mu_gid = &rtw89_mac_mu_gid_addr_ax,
+	.boot_dbg = R_AX_BOOT_DBG,
 
 	.muedca_ctrl = {
 		.addr = R_AX_MUEDCA_EN,
diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h
index a5f1694af91a..c05f5ee0d2fd 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.h
+++ b/drivers/net/wireless/realtek/rtw89/mac.h
@@ -1067,6 +1067,7 @@ struct rtw89_mac_gen_def {
 	u32 agg_len_ht;
 	u32 ps_status;
 	const struct rtw89_mac_mu_gid_addr *mu_gid;
+	u32 boot_dbg;
 
 	struct rtw89_reg_def muedca_ctrl;
 	struct rtw89_reg_def bfee_ctrl;
diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c
index 4bdf20b7ba6d..46aea7f6a4a1 100644
--- a/drivers/net/wireless/realtek/rtw89/mac_be.c
+++ b/drivers/net/wireless/realtek/rtw89/mac_be.c
@@ -3287,6 +3287,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = {
 	.agg_len_ht = R_BE_AGG_LEN_HT_0,
 	.ps_status = R_BE_WMTX_POWER_BE_BIT_CTL,
 	.mu_gid = &rtw89_mac_mu_gid_addr_be,
+	.boot_dbg = R_BE_BOOT_DBG,
 
 	.muedca_ctrl = {
 		.addr = R_BE_MUEDCA_EN,
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 1/8] wifi: rtw89: pack I/O during bb_sethw to reduce API execution time
From: Ping-Ke Shih @ 2026-07-12  3:44 UTC (permalink / raw)
  To: linux-wireless; +Cc: echuang, kevin_yang
In-Reply-To: <20260712034506.53209-1-pkshih@realtek.com>

From: Eric Huang <echuang@realtek.com>

Wrap rtw89_chip_bb_sethw() with rtw89_io_pack/unpack so all register
writes during baseband hardware initialization are batched into a single
bus transaction. This reduces API execution time from ~11000 us to
~4000 us on affected platforms.

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 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 174349c6bc58..0f6d4ad66fe2 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -7810,8 +7810,12 @@ static inline void rtw89_chip_bb_sethw(struct rtw89_dev *rtwdev)
 {
 	const struct rtw89_chip_info *chip = rtwdev->chip;
 
+	rtw89_io_pack(rtwdev);
+
 	if (chip->ops->bb_sethw)
 		chip->ops->bb_sethw(rtwdev);
+
+	rtw89_io_unpack(rtwdev);
 }
 
 static inline void rtw89_chip_rfk_init(struct rtw89_dev *rtwdev)
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 0/8] wifi: rtw89: add RF diagnosis and update random patches
From: Ping-Ke Shih @ 2026-07-12  3:44 UTC (permalink / raw)
  To: linux-wireless; +Cc: echuang, kevin_yang

First patch is to reduce IO time for USB device, which is addressed by real
time measurement.

The second patch is to abstract a register for WiFi 6 and 7 chips to read
firmware state for boot debugging.

Patch 3/8 is to resolve RTL8922DE interoperability issue operating in 2GHz
channels. Limit the TX time to prevent peer can't receive packets properly.

Patch 4-6/8 are to extend TX shape per 2/5/6 GHz bands to satisfy
regulation for each bands.

Patch 7/8 is to properly co-work with BB registers access in firmware, so
driver should power-on BB circuit to ensure possibly accessing.

The last patch is to add RF diagnosis assisting in debugging.

Eric Huang (1):
  wifi: rtw89: pack I/O during bb_sethw to reduce API execution time

Ping-Ke Shih (3):
  wifi: rtw89: mac: abstract register definition of firmware boot debug
  wifi: rtw89: 8922d: add TX time limit for 2GHz band
  wifi: rtw89: fw: do bb_preinit before downloading firmware

Zong-Zhe Yang (4):
  wifi: rtw89: introduce helper to get tx shape index
  wifi: rtw89: add tx shape v0 to keep built-in arrays compatible during
    transitions
  wifi: rtw89: extend tx shape format for regulatory 6 GHz power type
  wifi: rtw89: debug: add diagnosis for RF

 drivers/net/wireless/realtek/rtw89/core.c     |  83 ++++++++++++-
 drivers/net/wireless/realtek/rtw89/core.h     |  74 ++++++++++-
 drivers/net/wireless/realtek/rtw89/debug.c    | 115 ++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/fw.c       |  21 +++-
 drivers/net/wireless/realtek/rtw89/fw.h       |   2 +
 drivers/net/wireless/realtek/rtw89/mac.c      |  28 ++++-
 drivers/net/wireless/realtek/rtw89/mac.h      |   4 +-
 drivers/net/wireless/realtek/rtw89/mac_be.c   |   1 +
 drivers/net/wireless/realtek/rtw89/phy.c      |  62 ++++++++--
 drivers/net/wireless/realtek/rtw89/phy.h      |   3 +
 drivers/net/wireless/realtek/rtw89/reg.h      |   1 +
 drivers/net/wireless/realtek/rtw89/rtw8851b.c |   7 +-
 .../wireless/realtek/rtw89/rtw8851b_table.c   |   8 +-
 drivers/net/wireless/realtek/rtw89/rtw8852a.c |   1 +
 drivers/net/wireless/realtek/rtw89/rtw8852b.c |   1 +
 .../wireless/realtek/rtw89/rtw8852b_common.c  |   6 +-
 .../wireless/realtek/rtw89/rtw8852b_table.c   |   4 +-
 .../net/wireless/realtek/rtw89/rtw8852bt.c    |   1 +
 drivers/net/wireless/realtek/rtw89/rtw8852c.c |   7 +-
 .../wireless/realtek/rtw89/rtw8852c_table.c   |   4 +-
 drivers/net/wireless/realtek/rtw89/rtw8922a.c |  10 +-
 drivers/net/wireless/realtek/rtw89/rtw8922d.c |  18 +--
 22 files changed, 400 insertions(+), 61 deletions(-)


base-commit: 56d32cdc6040440b08edfd5d7262250a721233f8
-- 
2.25.1


^ permalink raw reply

* [PATCH rtw-next 11/11] wifi: rtw89: coex: Update coexistence version to 9.24.0
From: Ping-Ke Shih @ 2026-07-12  3:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-1-pkshih@realtek.com>

From: Ching-Te Ku <ku920601@realtek.com>

RTL8922D first release, add related feature support.

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

diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index a6b1aa2cb24e..b044b25aeade 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -11,7 +11,7 @@
 #include "ps.h"
 #include "reg.h"
 
-#define RTW89_COEX_VERSION 0x09000113
+#define RTW89_COEX_VERSION 0x09180013
 #define FCXDEF_STEP 50 /* MUST <= FCXMAX_STEP and match with wl fw*/
 #define BTC_E2G_LIMIT_DEF 80
 
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 10/11] wifi: rtw89: coex: update external control length by case
From: Ping-Ke Shih @ 2026-07-12  3:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-1-pkshih@realtek.com>

From: Ching-Te Ku <ku920601@realtek.com>

Update recommend external control slot length to driver. Some of the
Wi-Fi feature has its time slot requirement can not be simply controlled
by coexistence firmware TDMA timer. For example: Wi-Fi scan/MCC etc.
In the same time, coexistence need to tell driver the recommend Bluetooth
slot length to make sure Bluetooth can still has enough time slot to
traffic.

Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/coex.c | 70 +++++++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/core.c | 11 ++--
 drivers/net/wireless/realtek/rtw89/core.h |  4 +-
 3 files changed, 80 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index c2356d6d9811..a6b1aa2cb24e 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -526,6 +526,14 @@ enum btc_cx_poicy_main_type {
 	BTC_CXP_MAIN_MAX
 };
 
+enum btc_bslot_length {
+	BTC_BSLOT_A2DP_HID = 60,
+	BTC_BSLOT_A2DP = 50,
+	BTC_BSLOT_A2DP_2 = 40,
+	BTC_BSLOT_INQ = 30,
+	BTC_BSLOT_IDLE = 20,
+};
+
 enum btc_cx_poicy_type {
 	/* TDMA off + pri: BT > WL */
 	BTC_CXP_OFF_BT = (BTC_CXP_OFF << 8) | 0,
@@ -5989,6 +5997,67 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev)
 					  &data);
 }
 
+static void _set_phl_bt_slot_req(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_btc *btc = &rtwdev->btc;
+	struct rtw89_btc_cx *cx = &btc->cx;
+	struct rtw89_btc_wl_info *wl = &cx->wl;
+	struct rtw89_btc_dm *dm = &btc->dm;
+	u8 len = 0;
+	u8 i;
+
+	/* don't change bt slot req state during RFK for p2p/mcc case */
+	if (dm->run_reason == BTC_RSN_NTFY_WL_RFK ||
+	    wl->status.map.transacting)
+		return;
+
+	/* enable bt-slot req if ext-slot-control */
+	if (dm->tdma_now.type == CXTDMA_OFF &&
+	    dm->tdma_now.ext_ctrl == CXECTL_EXT &&
+	    dm->tdd_bind.bt_sel != 0) {
+		if (btc->cx.wl.status.val & btc_scanning_map.val)
+			btc->bt_req_en = false;
+		else
+			btc->bt_req_en = true;
+	} else {
+		btc->bt_req_en = false;
+	}
+
+	if (dm->slot_req_more) {
+		len = BTC_BSLOT_A2DP_HID;
+	} else {
+		if (dm->tdd_bind.bt_link_weight >= BTC_BSLOT_A2DP_HID)
+			len = BTC_BSLOT_A2DP_HID;
+		else if (dm->tdd_bind.bt_link_weight <= BTC_BSLOT_IDLE)
+			len = BTC_BSLOT_IDLE;
+		else
+			len = dm->tdd_bind.bt_link_weight;
+	}
+
+	if (dm->tdd_bind.rf_band == BIT(RTW89_BAND_5G))
+		len = 100 - len;
+
+	if (!btc->bt_req_en)
+		len = 0;
+
+	for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) {
+		if (!(dm->tdd_bind.wl_hwb_sel & BIT(i)))
+			continue;
+
+		if (len == btc->bt_req_len[i])
+			continue;
+
+		btc->bt_req_len[i] = len;
+
+		rtw89_core_ntfy_btc_event(rtwdev,
+					  RTW89_BTC_HMSG_SET_BT_REQ_SLOT, i);
+
+		rtw89_debug(rtwdev, RTW89_DBG_BTC,
+			    "[BTC], %s(): HWB%d bt_req_len = %d\n",
+			    __func__, i, btc->bt_req_len[i]);
+	}
+}
+
 static void _set_bt_rx_agc(struct rtw89_dev *rtwdev)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
@@ -6148,6 +6217,7 @@ static void _action_common(struct rtw89_dev *rtwdev)
 	_set_btg_ctrl(rtwdev);
 	_set_wl_preagc_ctrl(rtwdev);
 	_set_wl_tx_limit(rtwdev);
+	_set_phl_bt_slot_req(rtwdev);
 	_set_bt_afh_info(rtwdev);
 	_set_bt_rx_agc(rtwdev);
 	_set_rf_trx_para(rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 0343cd1a0ee1..30a8e337498e 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -6539,15 +6539,18 @@ void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond,
 	rtw89_complete_cond_resp(resp, data);
 }
 
-void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event)
+void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev,
+			       enum rtw89_btc_hmsg event,
+			       enum rtw89_phy_idx phy_idx)
 {
-	u16 bt_req_len;
+	u16 bt_slot_req[RTW89_PHY_NUM];
 
 	switch (event) {
 	case RTW89_BTC_HMSG_SET_BT_REQ_SLOT:
-		bt_req_len = rtw89_coex_query_bt_req_len(rtwdev, RTW89_PHY_0);
+		bt_slot_req[phy_idx] = rtw89_coex_query_bt_req_len(rtwdev, phy_idx);
 		rtw89_debug(rtwdev, RTW89_DBG_BTC,
-			    "coex updates BT req len to %d TU\n", bt_req_len);
+			    "coex updates PHY-%d BT req len to %d TU\n",
+			    phy_idx, bt_slot_req[phy_idx]);
 		rtw89_queue_chanctx_change(rtwdev, RTW89_CHANCTX_BT_SLOT_CHANGE);
 		break;
 	default:
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index e7e72f742847..c8b5c9ed55be 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -8866,7 +8866,9 @@ int rtw89_reg_6ghz_recalc(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvi
 void rtw89_core_update_p2p_ps(struct rtw89_dev *rtwdev,
 			      struct rtw89_vif_link *rtwvif_link,
 			      struct ieee80211_bss_conf *bss_conf);
-void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event);
+void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev,
+			       enum rtw89_btc_hmsg event,
+			       enum rtw89_phy_idx phy_idx);
 int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
 			   unsigned int link_id);
 void rtw89_core_dm_disable_cfg(struct rtw89_dev *rtwdev, u32 new);
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 09/11] wifi: rtw89: coex: Add firmware report control report v11
From: Ping-Ke Shih @ 2026-07-12  3:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-1-pkshih@realtek.com>

From: Ching-Te Ku <ku920601@realtek.com>

In the version 11 report control report, firmware will report firmware
build date, version. And Bluetooth to Wi-Fi scoreboard value will be
read at Wi-Fi firmware and update to Wi-Fi driver.

Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/coex.c | 218 +++++++++++++++++++++-
 drivers/net/wireless/realtek/rtw89/core.h |  26 +++
 2 files changed, 243 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 17290b2cb2c8..c2356d6d9811 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -1417,6 +1417,40 @@ static void _chk_btc_err(struct rtw89_dev *rtwdev, u8 type, u32 cnt)
 			dm->error.map.bt_slot_drift = false;
 
 		break;
+	case BTC_DCNT_WL_STA_NTFY:
+		cnt = dm->cnt_notify[BTC_NCNT_WL_STA] -
+		      dm->cnt_notify[BTC_NCNT_WL_STA_LAST];
+
+		dm->cnt_notify[BTC_NCNT_WL_STA_LAST] =
+			dm->cnt_notify[BTC_NCNT_WL_STA];
+
+		if (cnt == 0 &&
+		    (dm->tdd_bind.wl_link_mode != BTC_WLINK_NOLINK ||
+		     dm->fdd_bind.wl_link_mode != BTC_WLINK_NOLINK))
+			dm->cnt_dm[BTC_DCNT_WL_STA_NTFY]++;
+		else
+			dm->cnt_dm[BTC_DCNT_WL_STA_NTFY] = 0;
+
+		if (dm->cnt_dm[BTC_DCNT_WL_STA_NTFY] >= BTC_CHK_HANG_MAX)
+			dm->error.map.wl_no_sta_ntfy = true;
+		else
+			dm->error.map.wl_no_sta_ntfy = false;
+		break;
+	case BTC_DCNT_W2B_SCBD_NOSYNC:
+		if (!(rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA))
+			return; /* return if no-support W2B scbd readback */
+
+		if (wl->scbd_rb[BTC_BT_1ST] != wl->scbd[BTC_BT_1ST] ||
+		    wl->scbd_rb[BTC_BT_2ND] != wl->scbd[BTC_BT_2ND])
+			dm->cnt_dm[BTC_DCNT_W2B_SCBD_NOSYNC]++;
+		else
+			dm->cnt_dm[BTC_DCNT_W2B_SCBD_NOSYNC] = 0;
+
+		if (dm->cnt_dm[BTC_DCNT_W2B_SCBD_NOSYNC] >= BTC_CHK_HANG_MAX)
+			dm->error.map.w2b_scbd_no_sync = true;
+		else
+			dm->error.map.w2b_scbd_no_sync = false;
+		break;
 	}
 }
 
@@ -1564,6 +1598,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
 	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_dm *dm = &btc->dm;
 	struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
+	struct rtw89_btc_cx *cx = &btc->cx;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
 	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
 	union rtw89_btc_fbtc_rpt_ctrl_ver_info *prpt = NULL;
@@ -1574,7 +1609,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
 	u16 wl_slot_set = 0, wl_slot_real = 0, val16;
 	u32 trace_step = 0, rpt_len = 0, diff_t = 0;
 	u32 cnt_leak_slot, bt_slot_real, bt_slot_set, cnt_rx_imr;
-	u8 i, val = 0, val1, val2;
+	u8 i, j, val = 0, val1, val2;
+	u32 *bt_cnt, start_idx;
 
 	rtw89_debug(rtwdev, RTW89_DBG_BTC,
 		    "[BTC], %s(): index:%d\n",
@@ -1626,6 +1662,10 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
 			pfinfo = &pfwinfo->rpt_ctrl.finfo.v7;
 			pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v7);
 			fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v7.fver;
+		} else if (ver->fcxbtcrpt == 11) {
+			pfinfo = &pfwinfo->rpt_ctrl.finfo.v11;
+			pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v11);
+			fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v11.fver;
 		} else {
 			goto err;
 		}
@@ -2031,6 +2071,70 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
 			_chk_btc_err(rtwdev, BTC_DCNT_RPT_HANG, val1);
 			_chk_btc_err(rtwdev, BTC_DCNT_WL_FW_VER_MATCH, 0);
 			_chk_btc_err(rtwdev, BTC_DCNT_BTTX_HANG, 0);
+		} else if (ver->fcxbtcrpt == 11) {
+			prpt->v11 = pfwinfo->rpt_ctrl.finfo.v11;
+			pfwinfo->rpt_en_map = le32_to_cpu(prpt->v11.rpt_info.en);
+			wl->ver_info.fw_coex = le32_to_cpu(prpt->v11.rpt_info.cx_ver);
+			wl->ver_info.fw = le32_to_cpu(prpt->v11.rpt_info.fw_ver);
+
+			memset(wl->ver_info.build_time, 0,
+			       sizeof(wl->ver_info.build_time));
+			memcpy(wl->ver_info.build_time,
+			       prpt->v11.build_time,
+			       sizeof(wl->ver_info.build_time));
+
+			memset(wl->ver_info.build_date, 0,
+			       sizeof(wl->ver_info.build_date));
+			memcpy(wl->ver_info.build_date,
+			       prpt->v11.build_date,
+			       sizeof(wl->ver_info.build_date));
+
+			for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)
+				memcpy(&dm->gnt_set[i], &prpt->v11.gnt_val[i][0],
+				       sizeof(dm->gnt_set[i]));
+
+			for (i = BTC_BT_1ST; i <= BTC_BT_EXT; i++) {
+				if (i == BTC_BT_EXT) {
+					if (cx->bt_ext.func_type == BTC_BTF_NONE)
+						continue;
+					bt_cnt = cx->bt_ext.bcnt;
+				} else if (i == BTC_BT_2ND) {
+					if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT))
+						continue;
+					bt_cnt = cx->bt1.bcnt;
+					wl->scbd_rb[i] = le32_to_cpu(prpt->v11.scbd_w2b[i]);
+					cx->bt1.scbd_rb = le32_to_cpu(prpt->v11.scbd_b2w[i]);
+				} else {
+					bt_cnt = cx->bt0.bcnt;
+					wl->scbd_rb[i] = le32_to_cpu(prpt->v11.scbd_w2b[i]);
+					cx->bt0.scbd_rb = le32_to_cpu(prpt->v11.scbd_b2w[i]);
+				}
+
+				start_idx = BTC_BCNT_HIPRI_TX;
+				for (j = BTC_BCNT_HI_TX_V105; j <= BTC_BCNT_LO_RX_V105 ; j++)
+					bt_cnt[start_idx++] = le16_to_cpu(prpt->v11.bt_cnt[i][j]);
+
+				/* check if polut counter increase */
+				val1 = le16_to_cpu(prpt->v11.bt_cnt[i][BTC_BCNT_POLLUTED_V105]);
+				if (val1 > bt_cnt[BTC_BCNT_POLUT_NOW]) /* cnt increase*/
+					val2 = val1 - bt_cnt[BTC_BCNT_POLUT_NOW];
+				else
+					val2 = val1; /* overflow */
+
+				bt_cnt[BTC_BCNT_POLUT_DIFF] = val2;
+				bt_cnt[BTC_BCNT_POLUT_NOW] = val1;
+
+				if (i <= BTC_BT_2ND)
+					bt_cnt[BTC_BCNT_SCBDREAD]++;
+			}
+			dm->scbd_w2b_update = true;
+			dm->scbd_b2w_update = true;
+
+			_chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0);
+			_chk_btc_err(rtwdev, BTC_DCNT_WL_FW_VER_MATCH, 0);
+			_chk_btc_err(rtwdev, BTC_DCNT_BTTX_HANG, 0);
+			_chk_btc_err(rtwdev, BTC_DCNT_WL_STA_NTFY, 0);
+			_chk_btc_err(rtwdev, BTC_DCNT_W2B_SCBD_NOSYNC, 0);
 		} else {
 			goto err;
 		}
@@ -11953,6 +12057,116 @@ static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 	return p - buf;
 }
 
+static int _show_summary_v11(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
+{
+	struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo;
+	struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
+	struct rtw89_btc_fbtc_rpt_ctrl_v11 *prptctrl = NULL;
+	struct rtw89_btc_cx *cx = &rtwdev->btc.cx;
+	struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
+	struct rtw89_btc_wl_info *wl = &cx->wl;
+	u32 *cnt = rtwdev->btc.dm.cnt_notify;
+	char *p = buf, *end = buf + bufsz;
+	u32 cnt_sum = 0;
+	u8 i;
+
+	if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY))
+		return 0;
+
+	p += scnprintf(p, end - p, "%s",
+		       "\n\r========== [Statistics] ==========");
+
+	pcinfo = &pfwinfo->rpt_ctrl.cinfo;
+	if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF &&
+	    !wl->status.map.rf_off) {
+		prptctrl = &pfwinfo->rpt_ctrl.finfo.v11;
+
+		p += scnprintf(p, end - p,
+			       "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ",
+			       "[summary]", pfwinfo->cnt_h2c,
+			       pfwinfo->cnt_h2c_fail,
+			       le16_to_cpu(prptctrl->rpt_info.cnt_h2c),
+			       pfwinfo->cnt_c2h,
+			       le16_to_cpu(prptctrl->rpt_info.cnt_c2h),
+			       le16_to_cpu(prptctrl->rpt_info.len_c2h),
+			       (prptctrl->rpt_len_max_h << 8) + prptctrl->rpt_len_max_l,
+			       rtwdev->btc.ver->info_buf);
+
+		p += scnprintf(p, end - p,
+			       "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x",
+			       pfwinfo->event[BTF_EVNT_RPT],
+			       le16_to_cpu(prptctrl->rpt_info.cnt),
+			       le32_to_cpu(prptctrl->rpt_info.en));
+
+		if (dm->error.map.wl_fw_hang)
+			p += scnprintf(p, end - p, " (WL FW Hang!!)");
+
+		p += scnprintf(p, end - p,
+			       "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ",
+			       "[mailbox]",
+			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok),
+			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail),
+			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv));
+
+		p += scnprintf(p, end - p,
+			       "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)",
+			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty),
+			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl),
+			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx),
+			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack),
+			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack));
+
+		p += scnprintf(p, end - p,
+			       "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]",
+			       "[RFK/LPS]", wl->wcnt[BTC_WCNT_RFK_REQ],
+			       wl->wcnt[BTC_WCNT_RFK_GO],
+			       wl->wcnt[BTC_WCNT_RFK_REJECT],
+			       wl->wcnt[BTC_WCNT_RFK_TIMEOUT],
+			       wl->rfk_info.proc_time);
+
+		p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]",
+			       le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on),
+			       le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));
+	} else {
+		p += scnprintf(p, end - p,
+			       "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)",
+			       "[summary]",
+			       pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail,
+			       pfwinfo->cnt_c2h,
+			       wl->status.map.lps, wl->status.map.rf_off);
+	}
+
+	for (i = 0; i < BTC_NCNT_NUM; i++)
+		cnt_sum += dm->cnt_notify[i];
+
+	p += scnprintf(p, end - p,
+		       "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
+		       "[notify_cnt]",
+		       cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
+		       cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+
+	p += scnprintf(p, end - p,
+		       "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d",
+		       cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
+		       cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
+		       cnt[BTC_NCNT_WL_STA]);
+
+	p += scnprintf(p, end - p,
+		       "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ",
+		       "[notify_cnt]",
+		       cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH],
+		       cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW],
+		       cnt[BTC_NCNT_SPECIAL_PACKET]);
+
+	p += scnprintf(p, end - p,
+		       "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d",
+		       cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE],
+		       rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW],
+		       cnt[BTC_NCNT_COUNTRYCODE]);
+
+	return p - buf;
+}
+
 ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
@@ -12016,6 +12230,8 @@ ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 		p += _show_summary_v7(rtwdev, p, end - p);
 	else if (ver->fcxbtcrpt == 8)
 		p += _show_summary_v8(rtwdev, p, end - p);
+	else if (ver->fcxbtcrpt == 11)
+		p += _show_summary_v11(rtwdev, p, end - p);
 
 	return p - buf;
 }
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index d876de21482f..e7e72f742847 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -1406,6 +1406,7 @@ enum rtw89_btc_dcnt {
 	BTC_DCNT_WL_FW_VER_MATCH,
 	BTC_DCNT_NULL_TX_FAIL,
 	BTC_DCNT_WL_STA_NTFY,
+	BTC_DCNT_W2B_SCBD_NOSYNC,
 	BTC_DCNT_NUM,
 };
 
@@ -2058,6 +2059,8 @@ struct rtw89_btc_wl_role_info { /* Logic dynamic using */
 };
 
 struct rtw89_btc_wl_ver_info {
+	char build_time[12];
+	char build_date[12];
 	u32 fw_coex; /* match with which coex_ver */
 	u32 fw;
 	u32 mac;
@@ -2245,6 +2248,7 @@ struct rtw89_btc_dm_emap {
 	u32 h2c_buffer_over: 1;
 	u32 bt_tx_hang: 1; /* for SNR too low bug, BT has no Tx req*/
 	u32 wl_no_sta_ntfy: 1;
+	u32 w2b_scbd_no_sync: 1;
 
 	u32 h2c_bmap_mismatch: 1;
 	u32 c2h_bmap_mismatch: 1;
@@ -2784,6 +2788,26 @@ struct rtw89_btc_fbtc_rpt_ctrl_v8 {
 	struct rtw89_btc_fbtc_rpt_ctrl_bt_mailbox bt_mbx_info;
 } __packed;
 
+struct rtw89_btc_fbtc_rpt_ctrl_v11 {
+	u8 fver;
+	u8 rsvd0;
+	u8 rpt_len_max_l; /* BTC_RPT_MAX bit0~7 */
+	u8 rpt_len_max_h; /* BTC_RPT_MAX bit8~15 */
+
+	u8 build_time[12];
+	u8 build_date[12];
+
+	u8 gnt_val[RTW89_PHY_NUM][8]; /* gwl/gbt012 refer to struct btc_gnt_ctrl */
+	__le16 bt_cnt[BTC_ALL_BT_EZL][BTC_BCNT_STA_MAX_V105];
+
+	struct rtw89_btc_fbtc_rpt_ctrl_info_v8 rpt_info;
+	struct rtw89_btc_fbtc_rpt_ctrl_bt_mailbox bt_mbx_info;
+
+	__le32 error_code;
+	__le32 scbd_w2b[2];
+	__le32 scbd_b2w[2];
+} __packed;
+
 union rtw89_btc_fbtc_rpt_ctrl_ver_info {
 	struct rtw89_btc_fbtc_rpt_ctrl_v1 v1;
 	struct rtw89_btc_fbtc_rpt_ctrl_v4 v4;
@@ -2791,6 +2815,7 @@ union rtw89_btc_fbtc_rpt_ctrl_ver_info {
 	struct rtw89_btc_fbtc_rpt_ctrl_v105 v105;
 	struct rtw89_btc_fbtc_rpt_ctrl_v7 v7;
 	struct rtw89_btc_fbtc_rpt_ctrl_v8 v8;
+	struct rtw89_btc_fbtc_rpt_ctrl_v11 v11;
 };
 
 enum rtw89_fbtc_ext_ctrl_type {
@@ -3691,6 +3716,7 @@ struct rtw89_btc_dm {
 	u8 lps_ctrl_change: 1;
 	u8 scbd_write_instant;
 	bool scbd_b2w_update;
+	bool scbd_w2b_update;
 	bool pre_agc_chg;
 };
 
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 08/11] wifi: rtw89: coex: Correct SET_RFE settings
From: Ping-Ke Shih @ 2026-07-12  3:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-1-pkshih@realtek.com>

From: Ching-Te Ku <ku920601@realtek.com>

Because of dual-BT & dual-MAC, RTL8922D has more complex antenna settings.

Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/coex.h     |  31 +++
 drivers/net/wireless/realtek/rtw89/core.h     |  11 +
 drivers/net/wireless/realtek/rtw89/rtw8922d.c | 213 ++++++++++++++++--
 3 files changed, 231 insertions(+), 24 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h
index fdd63e2c0837..e17407696d8a 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.h
+++ b/drivers/net/wireless/realtek/rtw89/coex.h
@@ -143,6 +143,11 @@ enum btc_switch {
 	BTC_SWITCH_EXTERNAL
 };
 
+enum btc_ant_switch_type {
+	BTC_SWITCH_V1_NONE = 0, /* independent antenna */
+	BTC_SWITCH_V1_INTERNAL, /* internal-switch: BTGA structure */
+};
+
 enum btc_pkt_type {
 	PACKET_DHCP,
 	PACKET_ARP,
@@ -262,6 +267,32 @@ enum btc_chip_feature {
 	BTC_FEAT_DUAL_BTGA = BIT(9) /* the future A-Die */
 };
 
+enum btc_efuse_ant_function_map {
+	BTC_EFMAP_NONE = 0,
+	BTC_EFMAP_BT0 = BIT(0),
+	BTC_EFMAP_BT1 = BIT(1),
+	BTC_EFMAP_ZB = BIT(2), /* ZB or thread */
+	BTC_EFMAP_24GP = BIT(3),
+	BTC_EFMAP_ULL = BIT(4),
+};
+
+enum btc_extsoc_interface { /* cx->other.hw_coex */
+	BTC_EXTSOC_INTF_NONE = 0,
+	BTC_EXTSOC_INTF_PTA = BIT(0),
+	BTC_EXTSOC_INTF_MBX = BIT(1),
+	BTC_EXTSOC_INTF_SWIO = BIT(2),
+	BTC_EXTSOC_INTF_MAX,
+};
+
+enum btc_esoc_type {
+	BTC_ESOC_NONE,
+	BTC_ESOC_8761,
+	BTC_ESOC_8771,
+	BTC_ESOC_SILAB_MG21,
+	BTC_ESOC_NORDI_NRF52840,
+	BTC_ESOC_MAX,
+};
+
 enum btc_wl_mode {
 	BTC_WL_MODE_11B = 0,
 	BTC_WL_MODE_11A = 1,
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index f784e8437292..d876de21482f 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -2434,6 +2434,15 @@ struct rtw89_btc_wl_tx_limit_para {
 	u16 tx_retry;
 };
 
+struct rtw89_btc_wl_trx_nss_para {
+	u8 tx_limit;
+	u8 rx_limit;
+	u8 tx_ss;
+	u8 rx_ss;
+	u8 tx_path;
+	u8 rx_path;
+};
+
 enum rtw89_btc_bt_scan_type {
 	BTC_SCAN_INQ	= 0,
 	BTC_SCAN_PAGE,
@@ -3616,6 +3625,7 @@ struct rtw89_btc_dm {
 	struct rtw89_btc_bind_info fdd_bind;
 	struct rtw89_btc_fddt_info fddt_info;
 	struct rtw89_btc_fddr_info fddr_info;
+	struct rtw89_btc_wl_trx_nss_para wl_trx_nss;
 	u32 cnt_dm[BTC_DCNT_NUM];
 	u32 cnt_notify[BTC_NCNT_NUM];
 	u8 ant_xmap[BTC_RF_NUM][BTC_ALL_BT_EZL]; /* WL-BT ANT interact-map */
@@ -3664,6 +3674,7 @@ struct rtw89_btc_dm {
 	u8 wl_tx_pwr_phy_map;
 	u8 vid;
 	u8 client_ps_tdma_on;
+	u8 wl_trx_nss_en;
 
 	u8 wl_pre_agc: 2;
 	u8 wl_lna2: 1;
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index aeade858ce95..c28670e82c3e 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -3106,46 +3106,211 @@ static u32 rtw8922d_chan_to_rf18_val(struct rtw89_dev *rtwdev,
 
 static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev)
 {
+	struct rtw89_btc *btc = &rtwdev->btc;
+	struct rtw89_btc_dm *dm = &btc->dm;
+	struct rtw89_btc_cx *cx = &btc->cx;
 	union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo;
-	struct rtw89_btc_module_v7 *module = &md->md_v7;
+	struct rtw89_btc_module_v10 *module = &md->md_v10;
+	u8 efuse_bt_func, efuse_ant_info, bt_sw_gpio_pos;
+	u8 is_combo, is_bt_share;
 
+	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s !!\n", __func__);
+
+	/* get from final capability of device */
 	module->rfe_type = rtwdev->efuse.rfe_type;
 	module->kt_ver = rtwdev->hal.cv;
-	module->bt_solo = 0;
-	module->switch_type = BTC_SWITCH_INTERNAL;
+	module->kt_ver_adie = rtwdev->hal.acv;
 	module->wa_type = 0;
+	dm->wl_trx_nss_en = 0;
 
-	module->ant.type = BTC_ANT_SHARED;
 	module->ant.num = 2;
-	module->ant.isolation = 10;
-	module->ant.diversity = 0;
-	module->ant.single_pos = RF_PATH_A;
-	module->ant.btg_pos = RF_PATH_B;
+	module->ant.single_pos = BTC_RF_S0; /* WL 1ss+1Ant 0:s0(A)/ 1:s1(B) */
 
-	if (module->kt_ver <= 1)
-		module->wa_type |= BTC_WA_HFP_ZB;
+	/* set default antenna isolation */
+	cx->bt0.ant_iso_to_wl = module->ant.isolation;
+	cx->bt1.ant_iso_to_wl = module->ant.isolation;
 
-	rtwdev->btc.cx.bt_ext.func_type = BTC_3CX_NONE;
+	module->ant.stream_cnt = 2;
+	module->ant.btg_pos = BTC_RF_S1; /* BTG0 at WL-S1 */
+	module->ant.btg1_pos = BTC_RF_S0; /* BTG1 at WL-S0 if Dual-BTGA */
 
-	if (module->rfe_type == 0) {
-		rtwdev->btc.dm.error.map.rfe_type0 = true;
-		return;
+	cx->bt0.band_56G_support = 1;
+	cx->bt1.band_56G_support = 1;
+	cx->bt0.func_type = BTC_BTF_BT;
+	cx->bt1.func_type = BTC_BTF_BT;
+
+	efuse_bt_func = rtwdev->efuse.bt_setting_2;
+
+	switch ((efuse_bt_func & 0xe0) >> 5) { /* 0xcd[7:5] */
+	default:
+	case 0:
+	case 1:
+		bt_sw_gpio_pos = 5;
+		break;
+	case 2:
+		bt_sw_gpio_pos = 11;
+		break;
+	case 3:
+		bt_sw_gpio_pos = 15;
+		break;
+	case 4:
+		bt_sw_gpio_pos = 20;
+		break;
 	}
 
-	module->ant.num = (module->rfe_type % 2) ?  2 : 3;
+	efuse_bt_func &= 0x1f; /* 0xcd[4:0] */
+	efuse_ant_info = rtwdev->efuse.bt_setting_3;
+	module->ant.num = (efuse_ant_info & 0xe0) >> 5; /* 0xCE[7:5] */
+	is_combo = (efuse_ant_info & 0xe) >> 1; /* 0xCE[3:1] */
+	is_bt_share = efuse_ant_info & BIT(0); /* 0xCE[0] */
 
-	if (module->kt_ver == 0)
-		module->ant.num = 2;
+	memset(dm->ant_xmap, 0, sizeof(dm->ant_xmap));
 
-	if (module->ant.num == 3) {
-		module->ant.type = BTC_ANT_DEDICATED;
-		module->bt_pos = BTC_BT_ALONE;
-	} else {
+	/* To-Do: "RFE_TYpe" to "ant.num" translation */
+	switch (module->ant.num) {
+	case 1: /* 1-Ant WL-S0 only & BT0 only */
 		module->ant.type = BTC_ANT_SHARED;
-		module->bt_pos = BTC_BT_BTG;
+		module->bt0_pos = BTC_BT_BTG;
+		module->bt0_sw_type = BTC_SWITCH_INTERNAL;
+		module->ant.btg_pos = BTC_RF_S0; /* BTG0 at WL-S0 */
+		module->ant.stream_cnt = 1;
+		module->ant.func[0] = BTC_EFMAP_BT0;
+		dm->ant_xmap[BTC_RF_S0][BTC_BT_1ST] = 1; /* BT0 shared with S0*/
+		dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 0; /* WL 1T1R no RF-S1 */
+		break;
+	case 2: /* 2-Ant */
+	default:
+		if (is_combo) {
+			if (efuse_bt_func == (BTC_EFMAP_BT0 | BTC_EFMAP_BT1))
+				module->ant.func[0] = BTC_EFMAP_BT1;
+
+			module->ant.func[1] = BTC_EFMAP_BT0;
+		} else {
+			module->ant.func[0] = BTC_EFMAP_NONE;
+			module->ant.func[1] = BTC_EFMAP_NONE;
+		}
+
+		if (is_bt_share) { /* WL-S0 + (WL-S1 & BT0-S1) */
+			module->ant.type = BTC_ANT_SHARED;
+			module->bt0_pos = BTC_BT_BTG;
+			module->bt0_sw_type = BTC_SWITCH_INTERNAL;
+			dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1;
+		} else { /* WL-S0 + BT0-S1 */
+			module->ant.type = BTC_ANT_DEDICATED;
+			module->bt0_pos = BTC_BT_ALONE;
+			module->bt0_sw_type = BTC_SWITCH_V1_NONE;
+		}
+
+		if (module->ant.func[0] == BTC_EFMAP_BT1) { /* if 2nd BT exist */
+			dm->ant_xmap[BTC_RF_S0][BTC_BT_2ND] = 1;
+			if (module->rfe_type == 12) { /* WL-S0 & BT1 by SPDT */
+				module->bt1_pos = BTC_BT_ALONE;
+				/* Todo: set SPDT GPIO-ctrl */
+				module->bt1_sw_type = bt_sw_gpio_pos;
+			} else { /* WL-S0 & BT1-S1 by BTGA */
+				module->bt1_pos = BTC_BT_BTG;
+				module->bt1_sw_type = BTC_SWITCH_INTERNAL;
+			}
+		}
+
+		dm->wl_trx_nss_en = 1; /* 1ss MIMO-PS capability if 1-BT */
+		break;
+	case 3: /* 3-Ant, 3 different BT-configuration */
+		if (is_bt_share) {
+			module->ant.func[0] = BTC_EFMAP_NONE;
+			module->ant.func[1] = BTC_EFMAP_BT0;
+			module->ant.func[2] = efuse_bt_func & (~BTC_EFMAP_BT0);
+			module->ant.type = BTC_ANT_SHARED;
+			module->bt0_pos = BTC_BT_BTG;
+			module->bt0_sw_type = BTC_SWITCH_INTERNAL;
+			dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1;
+			dm->wl_trx_nss_en = 1; /* 1ss MIMO-PS capability */
+		} else {
+			module->ant.func[0] = BTC_EFMAP_NONE;
+			module->ant.func[1] = BTC_EFMAP_NONE;
+			module->ant.func[2] = efuse_bt_func;
+			module->ant.type = BTC_ANT_DEDICATED;
+			module->bt0_pos = BTC_BT_ALONE;
+			module->bt0_sw_type = BTC_SWITCH_V1_NONE;
+		}
+
+		module->bt1_pos = BTC_BT_ALONE; /* BT1 may exist or not */
+		module->bt1_sw_type = BTC_SWITCH_V1_NONE;
+		break;
+	case 4: /* 4-Ant,  WL-S0 + WL-S1 + BT0 + BT1 */
+		module->ant.func[0] = BTC_EFMAP_NONE;
+		module->ant.func[1] = BTC_EFMAP_NONE;
+		module->ant.func[2] = BTC_EFMAP_BT0;
+		module->ant.func[3] = efuse_bt_func & (~BTC_EFMAP_BT0);
+		module->ant.type = BTC_ANT_DEDICATED;
+		module->bt0_pos = BTC_BT_ALONE;
+		module->bt0_sw_type = BTC_SWITCH_V1_NONE;
+		module->bt1_pos = BTC_BT_ALONE;
+		module->bt1_sw_type = BTC_SWITCH_V1_NONE;
+		break;
+	case 5:
+		module->ant.func[0] = BTC_EFMAP_NONE;
+		module->ant.func[1] = BTC_EFMAP_NONE;
+		module->ant.func[2] = BTC_EFMAP_BT0;
+		module->ant.func[3] = BTC_EFMAP_BT1;
+		module->ant.func[4] = BTC_EFMAP_ZB;
+		module->ant.type = BTC_ANT_DEDICATED;
+		module->bt0_pos = BTC_BT_ALONE;
+		module->bt0_sw_type = BTC_SWITCH_V1_NONE;
+		module->bt1_pos = BTC_BT_ALONE;
+		module->bt1_sw_type = BTC_SWITCH_V1_NONE;
+		break;
 	}
-	rtwdev->btc.btg_pos = module->ant.btg_pos;
-	rtwdev->btc.ant_type = module->ant.type;
+
+	/*
+	 * if only BT0 at BTGA: 2-Ant, 3-Ant(BT1 used dedicated-ant)
+	 * can setup dm->wl_trx_nss_en = 1, WL MIMO-PS to 1T1R
+	 * (WL at WL-S0 only, BT0 at WL-S1)
+	 * tx_limit/rx_limit is decided by _set_trx_nss()
+	 */
+	if (dm->wl_trx_nss_en &&
+	    (dm->wl_trx_nss.tx_limit && dm->wl_trx_nss.rx_limit)) {
+		module->ant.type = BTC_ANT_DEDICATED;
+		module->ant.stream_cnt = 1;
+		dm->ant_xmap[BTC_RF_S0][BTC_BT_1ST] = 0; /* wl 1ss-> RF-S0 */
+		dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 0; /* BT0-> RF-S1 */
+		dm->ant_xmap[BTC_RF_S0][BTC_BT_2ND] = 0;
+		dm->ant_xmap[BTC_RF_S1][BTC_BT_2ND] = 0; /* BT1 -> 3rd Ant */
+	}
+
+	/*
+	 * Todo: call HALBB API to set BT0/1 at WL_S0 or WL_S1
+	 * r_sel_gnt_bt_rx_path0[1:0], r_sel_gnt_bt_rx_path1[1:0]
+	 * WL_S0 with BT1 -> r_sel_gnt_bt_rx_path0[1:0]= 2b'10
+	 * WL_S1 with BT0 -> r_sel_gnt_bt_rx_path1[1:0]= 2b'01
+	 */
+
+	/* To Do: setup ext-SOC coex if exist */
+	switch (cx->bt_ext.chip_id) {
+	default:
+	case BTC_ESOC_NONE:
+		memset(&cx->bt_ext, 0, sizeof(struct rtw89_btc_extsoc_info));
+		cx->bt_ext.max_tx_pwr = RTW89_BTC_BT_DEF_LE_TX_PWR;
+		cx->bt_ext.ant_iso_to_wl = RTW89_BTC_DEFAULT_ANISO;
+		break;
+	case BTC_ESOC_8771:
+		cx->bt_ext.func_type = BTC_BTF_THREAD;
+		cx->bt_ext.hw_coex = BTC_EXTSOC_INTF_PTA;
+		cx->bt_ext.rf_band_map = 0x1; /* 2.4GHz only */
+		cx->bt_ext.link_weight[BTC_BT_B2G] = 10;
+		cx->bt_ext.profile_map[BTC_BT_B2G] |= BTC_BT_THREAD;
+		/* use GPIO 12~15 for Ext-4-wire-PTA */
+		cx->bt_ext.hpta_cfg = BIT(12) | BIT(13) | BIT(14) | BIT(15);
+		/* for Ext-SOC locate at Ant-2 */
+		if (module->ant.num >= 5)
+			module->ant.func[4] = BTC_EFMAP_ZB;
+		else
+			module->ant.func[module->ant.num - 1] = BTC_EFMAP_ZB;
+		break;
+	}
+
+	dm->ant_xmap[BTC_RF_S0][BTC_BT_EXT] = 0;
+	dm->ant_xmap[BTC_RF_S1][BTC_BT_EXT] = 0;
 }
 
 static void rtw8922d_btc_init_cfg(struct rtw89_dev *rtwdev)
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 07/11] wifi: rtw89: coex: Refine _reset_btc_var()
From: Ping-Ke Shih @ 2026-07-12  3:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-1-pkshih@realtek.com>

From: Ching-Te Ku <ku920601@realtek.com>

To avoid the default value not match the real using scenario, it should
after assign desired default value after variable reset.

Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/coex.c |  68 ++++++++----
 drivers/net/wireless/realtek/rtw89/coex.h |   8 ++
 drivers/net/wireless/realtek/rtw89/core.h | 127 ++++++++++++++++++++++
 3 files changed, 180 insertions(+), 23 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 07d054f59eb5..17290b2cb2c8 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -1048,44 +1048,52 @@ static int _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func,
 	return ret;
 }
 
-#define BTC_BT_DEF_BR_TX_PWR 4
-#define BTC_BT_DEF_LE_TX_PWR 4
+#define BTC_DEFAULT_SIR_THRES 51 /* dB, BT Pin - This = TDD/FDD swtch thres */
+#define BTC_FDDR_TP_SETUP_TIME 1 /* in second */
+#define BTC_FDDR_TP_HOLD_TIME 3 /* in second */
+#define BTC_FDDR_RX_LOW_RATE_THRES 4  /* low rate for TDD swotch */
 
 static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_cx *cx = &btc->cx;
-	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
-	struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
-	struct rtw89_btc_wl_link_info *wl_linfo;
-	u8 i, j;
+	const struct rtw89_btc_ver *ver = btc->ver;
+	struct rtw89_btc_bt_info *bt0 = &cx->bt0;
+	struct rtw89_btc_bt_info *bt1 = &cx->bt1;
+	u8 i;
 
 	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s\n", __func__);
 
-	if (type & BTC_RESET_CX)
+	if (type & BTC_RESET_CX) {
 		memset(cx, 0, sizeof(*cx));
-
-	if (type & BTC_RESET_BTINFO) /* only for BT enable */
-		memset(bt, 0, sizeof(*bt));
+		cx->bt_ext.max_tx_pwr = RTW89_BTC_BT_DEF_LE_TX_PWR;
+		cx->bt_ext.ant_iso_to_wl = RTW89_BTC_DEFAULT_ANISO;
+	}
+
+	if (type & BTC_RESET_BTINFO) {/* only for BT enable */
+		memset(bt0, 0, sizeof(*bt0));
+		bt0->link_info.bt_txpwr_desc.br_dbm = RTW89_BTC_BT_DEF_BR_TX_PWR;
+		bt0->link_info_56g.bt_txpwr_desc.br_dbm = RTW89_BTC_BT_DEF_BR_TX_PWR;
+		bt0->link_info.bt_txpwr_desc.le_dbm = RTW89_BTC_BT_DEF_LE_TX_PWR;
+		bt0->link_info_56g.bt_txpwr_desc.le_dbm = RTW89_BTC_BT_DEF_LE_TX_PWR;
+		bt0->ant_iso_to_wl = RTW89_BTC_DEFAULT_ANISO;
+	} else if (type & BTC_RESET_BTINFO2) {
+		memset(bt1, 0, sizeof(*bt1));
+		bt1->link_info.bt_txpwr_desc.br_dbm = RTW89_BTC_BT_DEF_BR_TX_PWR;
+		bt1->link_info_56g.bt_txpwr_desc.br_dbm = RTW89_BTC_BT_DEF_BR_TX_PWR;
+		bt1->link_info.bt_txpwr_desc.le_dbm = RTW89_BTC_BT_DEF_LE_TX_PWR;
+		bt1->link_info_56g.bt_txpwr_desc.le_dbm = RTW89_BTC_BT_DEF_LE_TX_PWR;
+		bt1->ant_iso_to_wl = RTW89_BTC_DEFAULT_ANISO;
+	}
 
 	if (type & BTC_RESET_CTRL) {
 		memset(&btc->ctrl, 0, sizeof(btc->ctrl));
-		btc->manual_ctrl = false;
 		btc->ctrl.trace_step = FCXDEF_STEP;
 	}
 
 	/* Init Coex variables that are not zero */
 	if (type & BTC_RESET_DM) {
 		memset(&btc->dm, 0, sizeof(btc->dm));
-		memset(bt_linfo->rssi_state, 0, sizeof(bt_linfo->rssi_state));
-		for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) {
-			for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) {
-				wl_linfo = &wl->rlink_info[i][j];
-				memset(wl_linfo->rssi_state, 0, sizeof(wl_linfo->rssi_state));
-			}
-		}
 
 		/* set the slot_now table to original */
 		btc->dm.tdma_now = t_def[CXTD_OFF];
@@ -1108,18 +1116,32 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type)
 		btc->policy_len = 0;
 		btc->bt_req_len[RTW89_PHY_0] = 0;
 		btc->bt_req_len[RTW89_PHY_1] = 0;
+		btc->hubmsg_cnt = 0;
+
 		btc->dm.coex_info_map = BTC_COEX_INFO_ALL;
 		btc->dm.wl_tx_limit.tx_time = BTC_MAX_TX_TIME_DEF;
 		btc->dm.wl_tx_limit.tx_retry = BTC_MAX_TX_RETRY_DEF;
+		btc->dm.bt_slot_flood = BTC_B1_MAX;
+		btc->dm.sir_thres = BTC_DEFAULT_SIR_THRES;
+		btc->dm.fddr_info.tp_setup_time = BTC_FDDR_TP_SETUP_TIME;
+		btc->dm.fddr_info.tp_hold_time = BTC_FDDR_TP_HOLD_TIME;
+		btc->dm.fddr_info.wl_rx_rate_thres = BTC_FDDR_RX_LOW_RATE_THRES;
+		btc->dm.fddt_info.type = BTC_FDDT_TYPE_AUTO;
+
 		btc->dm.wl_pre_agc_rb = BTC_PREAGC_NOTFOUND;
 		btc->dm.wl_btg_rx_rb = BTC_BTGCTRL_BB_GNT_NOTFOUND;
 	}
 
-	if (type & BTC_RESET_MDINFO)
+	if (type & BTC_RESET_MDINFO) {
 		memset(&btc->mdinfo, 0, sizeof(btc->mdinfo));
 
-	bt->link_info.bt_txpwr_desc.br_dbm = BTC_BT_DEF_BR_TX_PWR;
-	bt->link_info.bt_txpwr_desc.le_dbm = BTC_BT_DEF_LE_TX_PWR;
+		if (ver->fcxinit == 10)
+			btc->mdinfo.md_v10.ant.isolation = RTW89_BTC_DEFAULT_ANISO;
+		else if (ver->fcxinit == 7)
+			btc->mdinfo.md_v7.ant.isolation = RTW89_BTC_DEFAULT_ANISO;
+		else
+			btc->mdinfo.md.ant.isolation = RTW89_BTC_DEFAULT_ANISO;
+	}
 }
 
 static u8 _search_reg_index(struct rtw89_dev *rtwdev, u8 mreg_num, u16 reg_type, u32 target)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h
index c127bd80d31c..fdd63e2c0837 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.h
+++ b/drivers/net/wireless/realtek/rtw89/coex.h
@@ -241,6 +241,14 @@ enum btc_3cx_type {
 	BTC_3CX_MAX,
 };
 
+enum btc_fddt_type {
+	BTC_FDDT_TYPE_STOP,
+	BTC_FDDT_TYPE_AUTO,
+	BTC_FDDT_TYPE_FIX_TDD,
+	BTC_FDDT_TYPE_FIX_FULL_FDD,
+	BTC_FDDT_MAX,
+};
+
 enum btc_chip_feature {
 	BTC_FEAT_PTA_ONOFF_CTRL  = BIT(0), /* on/off ctrl by HW (not 0x73[2]) */
 	BTC_FEAT_NONBTG_GWL_THRU = BIT(1), /* non-BTG GNT_WL!=0 if GNT_BT = 1 */
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index cb3a740e4719..f784e8437292 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -2462,6 +2462,9 @@ enum rtw89_btc_bt_func_type {
 
 #define RTW89_BTC_BTC_SCAN_V1_FLAG_ENABLE BIT(0)
 #define RTW89_BTC_BTC_SCAN_V1_FLAG_INTERLACE BIT(1)
+#define RTW89_BTC_BT_DEF_BR_TX_PWR 4
+#define RTW89_BTC_BT_DEF_LE_TX_PWR 4
+#define RTW89_BTC_DEFAULT_ANISO 10
 
 struct rtw89_btc_bt_scan_info_v1 {
 	__le16 win;
@@ -2521,6 +2524,7 @@ struct rtw89_btc_bt_info {
 	u8 func_type;
 	u8 tx_power_now;
 	u8 tx_power_now_6g;
+	u8 ant_iso_to_wl; /* ant isolation between BTx and WL */
 
 	u8 fw_ver_mismatch: 1;
 	u8 band_56G_support: 1;
@@ -3474,6 +3478,123 @@ union rtw89_btc_fbtc_slot_u {
 	struct rtw89_btc_fbtc_slot_v7 v7[CXST_MAX];
 };
 
+struct rtw89_btc_fddr_cell {
+	u8 en;
+	u8 wl_rx_max;
+	u8 wl_rx_min;
+};
+
+struct rtw89_btc_fddr_result {
+	u8 wl_rx_limit;
+	u8 wl_rx_limit_step[6]; /* record search process */
+	u8 search_cnt; /* the rx-limit serach count */
+	u32 wl_tp;
+	u32 wl_tp_step[6]; /* record search process */
+};
+
+struct rtw89_btc_fddr_train_info {
+	u8 rx_limit_pre;
+	u8 rx_limit_now;
+
+	u32 tp_rec_cnt;
+	u32 tp_avg_cnt;
+
+	u32 wl_tp_pre;
+	u32 wl_tp_now;
+};
+
+struct rtw89_btc_fddr_info {
+	u8 state; /* refer to enum btc_fddr_state */
+	u8 cell_now; /* wl rssi_level after filter LNA != 6 */
+	u8 cell_change;
+	u8 wl_low_rate;
+
+	u8 tp_setup_time; /* calculate TP after this value (in second) */
+	u8 tp_hold_time; /* TP calculation period (in second) */
+	u8 wl_rssi_thres[BTC_WL_RSSI_THMAX]; /* index 0 -> Max RSSI */
+
+	u8 search_mode; /* 0: search, 1:look-up  */
+	u8 search_dir; /* 0: Max->Min, 1:Min->Max */
+
+	struct rtw89_btc_fddr_train_info tctrl; /* train flag */
+	struct rtw89_btc_fddr_result cell_result[BTC_WL_RSSI_THMAX + 1];
+	struct rtw89_btc_fddr_cell cell[BTC_WL_RSSI_THMAX + 1]; /* parameters */
+
+	u16 wl_rx_rate_thres; /* switch to TDD if rx_rate < this threshold */
+
+	u32 nrsn_map; /* the reason map for no-run fdd-traing */
+	u32 wl_rx_rate_now;
+};
+
+struct rtw89_btc_rpt_ctrl_a2dp_empty {
+	u32 cnt_empty; /* a2dp empty count */
+	u32 cnt_flowctrl; /* a2dp empty flow control counter */
+	u32 cnt_tx;
+	u32 cnt_ack;
+	u32 cnt_nack;
+};
+
+struct rtw89_btc_fddt_bt_stat {
+	struct rtw89_btc_rpt_ctrl_a2dp_empty a2dp_last;
+	u32 retry_last;
+};
+
+struct rtw89_btc_fddt_cell {
+	s8 wl_pwr_min;
+	s8 wl_pwr_max;
+	s8 bt_pwr_dec_max;
+	s8 bt_rx_gain;
+};
+
+struct rtw89_btc_fddt_fail_check { /* for cell stay in training */
+	u8 check_map; /* check pass condition if bit-map = 1 */
+	u8 bt_no_empty_cnt; /* 0-fail if no bt-empty >= th in train_cycle */
+	u8 wl_tp_ratio; /* 1-fail if wl tp rise ratio < th */
+	u8 wl_kpibtr_ratio; /* 2-fail if phase_now_tp < phase_last_tp * kpibtr_ratio */
+};
+
+struct rtw89_btc_fddt_break_check { /* for cell stay in training or train-ok */
+	u8 check_map; /* check break condition if bit-map = 1 */
+	u8 bt_no_empty_cnt; /* 0-break if no empty count >= th */
+	u8 wl_tp_ratio; /* 1-break if wl tp ratio < th (%) */
+	u8 wl_tp_low_bound; /* 2-break if wl tp (in Mbps) < th */
+
+	u8 cn; /* 3-break if (cn >= cn_limit) >= th cycle */
+	u8 cell_chg; /* 4-break if non-matched-RSSI >= th cycle */
+	s8 nhm_limit; /* 5-break if nhm >= th --> ill-condition */
+	u8 cn_limit; /* if condition number >= th --> ill-condition */
+};
+
+struct rtw89_btc_fddt_time_ctrl {
+	/* 1 TDD cycle = w1 + b1, FDD 1cycle = w1fdd-slot + b1fdd-slot */
+	u8 m_cycle; /* KPI Moving-Average-Cycle: 1~32 cycles */
+	u8 w_cycle; /* Start to calcul WKPI after this if train-phase change */
+	u8 k_cycle; /* Total kpi-estimate cycles for each training-step */
+	u8 rsvd;
+};
+
+struct rtw89_btc_fddt_train_info {
+	struct rtw89_btc_fddt_time_ctrl t_ctrl;
+	struct rtw89_btc_fddt_break_check b_chk;
+	struct rtw89_btc_fddt_fail_check f_chk;
+	struct rtw89_btc_fddt_cell cell_ul[5][5];
+	struct rtw89_btc_fddt_cell cell_dl[5][5];
+};
+
+struct rtw89_btc_fddt_info {
+	u8 type; /* refer to enum btc_fddt_type */
+	u8 result; /* fw send fdd-training status by c2h */
+	u8 state; /* refer to enum btc_fddt_state */
+
+	u8 wl_iot[6]; /* wl bssid  */
+	u16 bt_iot; /* bt vendor-id */
+
+	u32 nrsn_map; /* the reason map for no-run fdd-traing */
+	struct rtw89_btc_fddt_bt_stat bt_stat; /* bt statistics */
+	struct rtw89_btc_fddt_train_info train;
+	struct rtw89_btc_fddt_train_info train_now;
+};
+
 struct rtw89_btc_dm {
 	struct rtw89_btc_fbtc_outsrc_set_info ost_info_last; /* outsrc API setup info */
 	struct rtw89_btc_fbtc_outsrc_set_info ost_info; /* outsrc API setup info */
@@ -3493,6 +3614,8 @@ struct rtw89_btc_dm {
 	union rtw89_btc_dm_error_map error;
 	struct rtw89_btc_bind_info tdd_bind;
 	struct rtw89_btc_bind_info fdd_bind;
+	struct rtw89_btc_fddt_info fddt_info;
+	struct rtw89_btc_fddr_info fddr_info;
 	u32 cnt_dm[BTC_DCNT_NUM];
 	u32 cnt_notify[BTC_NCNT_NUM];
 	u8 ant_xmap[BTC_RF_NUM][BTC_ALL_BT_EZL]; /* WL-BT ANT interact-map */
@@ -3506,6 +3629,10 @@ struct rtw89_btc_dm {
 	u8 sit_xmap_last[BTC_RF_NUM][BTC_ALL_BT_EZL];
 	u8 fit_xmap_last[RTW89_PHY_NUM][BTC_ALL_BT_EZL];
 
+	u8 tdd_rssi_thres;  /* The FDD/TDD switch RSSI (in %) */
+	u8 sir_thres; /* WL(Signal) to BT(interference Pin) ratio */
+	u8 sir_state[BTC_ALL_BT_EZL]; /* 1: WL RSSI > BTx-interference */
+
 	u32 update_slot_map;
 	u32 set_ant_path;
 	u32 e2g_slot_limit;
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 06/11] wifi: rtw89: coex: Refine send firmware command function
From: Ping-Ke Shih @ 2026-07-12  3:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-1-pkshih@realtek.com>

From: Ching-Te Ku <ku920601@realtek.com>

Because the coexistence offload more register/ hardware setting I/O to
firmware by coexistence itself, and it goes with the same entry with other
control action, so the firmware command entry need to add different
condition to judge should it followed coexistence TLV format or not.

Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/coex.c | 107 +++++++++++++++++++++-
 drivers/net/wireless/realtek/rtw89/core.h |   4 +
 2 files changed, 110 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index ff3345824a2a..07d054f59eb5 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -936,6 +936,14 @@ static void _run_coex(struct rtw89_dev *rtwdev,
 static void _write_scbd(struct rtw89_dev *rtwdev, u8 bid, u32 val, bool state);
 static u8 _sned_h2c_w2bscbd(struct rtw89_dev *rtwdev, bool force_exec, u8 bid);
 static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid);
+static const char *id_to_h2c(u32 id);
+
+static void _reset_h2c_macro(struct rtw89_btc *btc)
+{
+	btc->hbuf_len = 0;
+	btc->hbuf_cnt = 0;
+	memset(btc->hbuf, 0, BTC_H2C_MAXLEN);
+}
 
 static int _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func,
 			void *param, u16 len)
@@ -945,6 +953,8 @@ static int _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func,
 	struct rtw89_btc_cx *cx = &btc->cx;
 	struct rtw89_btc_wl_info *wl = &cx->wl;
 	struct rtw89_btc_dm *dm = &btc->dm;
+	u8 h2c_func_mask, bid = BTC_BT_1ST;
+	u8 *buf = param;
 	int ret;
 
 	if (len > BTC_H2C_MAXLEN || len == 0) {
@@ -966,7 +976,69 @@ static int _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func,
 		return -EINVAL;
 	}
 
-	ret = rtw89_fw_h2c_raw_with_hdr(rtwdev, h2c_class, h2c_func, param, len,
+	h2c_func_mask = h2c_func & (~BT_H2C_FUNC_BT2ND);
+	if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT)
+		bid = !!(h2c_func & BT_H2C_FUNC_BT2ND);
+
+	if (btc->io_oflld_type != BTC_IO_OFLD_BTC_H2C || btc->cli_h2c_cmd ||
+	    h2c_func_mask == SET_H2C_MACRO ||
+	    (h2c_func_mask == SET_DRV_INFO && buf[0] == CXDRVINFO_TRX) ||
+	    (h2c_func_mask == SET_IOFLD_SCBD && dm->scbd_write_instant) ||
+	    h2c_func_mask == SET_BT_LNA_CONSTRAIN) {
+		h2c_class = BTFC_SET;
+
+		ret = rtw89_fw_h2c_raw_with_hdr(rtwdev, h2c_class, h2c_func,
+						buf, len, false, true);
+
+		rtw89_debug(rtwdev, RTW89_DBG_BTC,
+			    "[BTC], %s():class=%d, bt%d-func=%s, len=%d\n",
+			    __func__, h2c_class, bid,
+			    id_to_h2c(h2c_func_mask), len);
+
+		if (h2c_func_mask == SET_H2C_MACRO) {
+			rtw89_debug(rtwdev, RTW89_DBG_BTC,
+				    "[BTC], %s():Send H2C-MACRO cnt=%d, len=%d\n",
+				    __func__, btc->hbuf_cnt, btc->hbuf_len);
+			_reset_h2c_macro(btc); /* clear H2C MACRO buffer */
+		}
+
+		if (ret != 0) { /* Send H2C fail */
+			rtw89_debug(rtwdev, RTW89_DBG_BTC,
+				    "[BTC], %s():return by rtw_hal_mac_send_h2c\n",
+				    __func__);
+			btc->fwinfo.cnt_h2c_fail++;
+			return 0;
+		}
+
+		btc->fwinfo.cnt_h2c++;
+	} else { /* Fill H2C MACRO buffer(TLV format) temporarily */
+		if (btc->hbuf_cnt == 0)
+			_reset_h2c_macro(btc);
+
+		/* Type:1 byte, Length:2 Bytes, Data:len bytes */
+		if (btc->hbuf_len + len + 3 >= BTC_H2C_MAXLEN) {
+			rtw89_debug(rtwdev, RTW89_DBG_BTC,
+				    "[BTC], %s():return by MACRO buf full(%d)\n",
+				    __func__, btc->hbuf_len + len + 3);
+			btc->fwinfo.cnt_h2c_fail++;
+			return 0;
+		}
+
+		btc->hbuf[btc->hbuf_len] = h2c_func;
+		btc->hbuf[btc->hbuf_len + 1] = len & GENMASK(7, 0);
+		btc->hbuf[btc->hbuf_len + 2] = (len & GENMASK(15, 8)) >> 8;
+
+		memcpy(&btc->hbuf[btc->hbuf_len + 3], buf, len);
+		btc->hbuf_len = btc->hbuf_len + len + 3;
+		btc->hbuf_cnt++;
+
+		rtw89_debug(rtwdev, RTW89_DBG_BTC,
+			    "[BTC], %s():Buffer H2C-MACRO cnt=%d/bt%d-func=%s/len=%d\n",
+			    __func__, btc->hbuf_cnt, bid,
+			    id_to_h2c(h2c_func_mask), len);
+	}
+
+	ret = rtw89_fw_h2c_raw_with_hdr(rtwdev, h2c_class, h2c_func, buf, len,
 					false, true);
 	if (ret)
 		pfwinfo->cnt_h2c_fail++;
@@ -9249,6 +9321,7 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 #define CASE_BTC_POLUT_STR(e) case BTC_PLT_## e: return #e
 #define CASE_BTC_REGTYPE_STR(e) case REG_## e: return #e
 #define CASE_BTC_GDBG_STR(e) case BTC_DBG_## e: return #e
+#define CASE_BTC_H2CCMD(e) case SET_##e: return #e
 
 static const char *id_to_polut(u32 id)
 {
@@ -9529,6 +9602,38 @@ static const char *id_to_ant(u32 id)
 	}
 }
 
+static const char *id_to_h2c(u32 id)
+{
+	switch (id) {
+	CASE_BTC_H2CCMD(REPORT_EN);
+	CASE_BTC_H2CCMD(SLOT_TABLE);
+	CASE_BTC_H2CCMD(MREG_TABLE);
+	CASE_BTC_H2CCMD(CX_POLICY);
+	CASE_BTC_H2CCMD(GPIO_DBG);
+	CASE_BTC_H2CCMD(DRV_INFO);
+	CASE_BTC_H2CCMD(DRV_EVENT);
+	CASE_BTC_H2CCMD(BT_WREG_ADDR);
+	CASE_BTC_H2CCMD(BT_WREG_VAL);
+	CASE_BTC_H2CCMD(BT_RREG_ADDR);
+	CASE_BTC_H2CCMD(BT_WL_CH_INFO);
+	CASE_BTC_H2CCMD(BT_INFO_REPORT);
+	CASE_BTC_H2CCMD(BT_IGNORE_WLAN_ACT);
+	CASE_BTC_H2CCMD(BT_TX_PWR);
+	CASE_BTC_H2CCMD(BT_LNA_CONSTRAIN);
+	CASE_BTC_H2CCMD(BT_QUERY_DEV_LIST);
+	CASE_BTC_H2CCMD(BT_QUERY_DEV_INFO);
+	CASE_BTC_H2CCMD(BT_PSD_REPORT);
+	CASE_BTC_H2CCMD(H2C_TEST);
+	CASE_BTC_H2CCMD(IOFLD_RF);
+	CASE_BTC_H2CCMD(IOFLD_BB);
+	CASE_BTC_H2CCMD(IOFLD_MAC);
+	CASE_BTC_H2CCMD(IOFLD_SCBD);
+	CASE_BTC_H2CCMD(H2C_MACRO);
+	default:
+		return "unknown";
+	}
+}
+
 static
 int scnprintf_segment(char *buf, size_t bufsz, const char *prefix, const u16 *data,
 		      u8 len, u8 seg_len, u8 start_idx, u8 ring_len)
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index a928140300b9..cb3a740e4719 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -3820,6 +3820,7 @@ struct rtw89_btc_btf_fwinfo {
 };
 
 #define RTW89_BTC_POLICY_MAXLEN 512
+#define BTC_H2C_MAXLENC 2020
 
 struct rtw89_btc {
 	const struct rtw89_btc_ver *ver;
@@ -3839,11 +3840,14 @@ struct rtw89_btc {
 	u32 bt_req_len[RTW89_PHY_NUM];
 
 	u8 policy[RTW89_BTC_POLICY_MAXLEN];
+	u8 hbuf[BTC_H2C_MAXLENC]; /* H2C Macro buffer */
+	u8 hbuf_cnt; /* H2C cmd count in buffer */
 	u8 ant_type;
 	u8 btg_pos;
 	u8 io_oflld_type;
 	u16 policy_len;
 	u16 policy_type;
+	u16 hbuf_len; /* H2C used length, it sshould be <= BTC_H2C_MAXLEN */
 	u32 hubmsg_cnt;
 	bool bt_req_en;
 	bool update_policy_force;
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 05/11] wifi: rtw89: coex: Rearrange Bluetooth firmware report entry
From: Ping-Ke Shih @ 2026-07-12  3:05 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-1-pkshih@realtek.com>

From: Ching-Te Ku <ku920601@realtek.com>

To enable/disable firmware report once at the end of mechanism round.
This can make the logic more clearly, and make sure every round the
mechanism running can refresh the settings. It can avoid some report
missing after driver status change.

Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/coex.c | 209 +++++++++++-----------
 1 file changed, 104 insertions(+), 105 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 2ca6090e9f62..ff3345824a2a 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -2659,37 +2659,27 @@ static void rtw89_btc_fw_set_slots(struct rtw89_dev *rtwdev)
 }
 
 static void rtw89_btc_fw_en_rpt(struct rtw89_dev *rtwdev,
-				u32 rpt_map, bool rpt_state)
+				bool force_exec, u32 rpt_map)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_wl_smap *wl_smap = &btc->cx.wl.status.map;
 	struct rtw89_btc_btf_fwinfo *fwinfo = &btc->fwinfo;
 	union rtw89_fbtc_rtp_ctrl r;
-	u32 val, bit_map;
 	int ret;
+	u32 i;
 
-	if ((wl_smap->rf_off || wl_smap->lps != BTC_LPS_OFF) && rpt_state != 0)
+	if (!force_exec && rpt_map == fwinfo->rpt_en_map)
 		return;
 
-	bit_map = rtw89_btc_fw_rpt_ver(rtwdev, rpt_map);
-
-	rtw89_debug(rtwdev, RTW89_DBG_BTC,
-		    "[BTC], %s(): rpt_map=%x, rpt_state=%x\n",
-		    __func__, rpt_map, rpt_state);
-
-	if (rpt_state)
-		val = fwinfo->rpt_en_map | bit_map;
-	else
-		val = fwinfo->rpt_en_map & ~bit_map;
-
-	if (val == fwinfo->rpt_en_map)
-		return;
+	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): rpt_map=%x\n",
+		    __func__, fwinfo->rpt_en_map);
 
-	if (btc->ver->fcxbtcrpt == 7 || btc->ver->fcxbtcrpt == 8) {
+	if (btc->ver->fcxbtcrpt == 7 ||
+	    btc->ver->fcxbtcrpt == 8 ||
+	    btc->ver->fcxbtcrpt == 11) {
 		r.v8.type = SET_REPORT_EN;
 		r.v8.fver = btc->ver->fcxbtcrpt;
 		r.v8.len = sizeof(r.v8.map);
-		r.v8.map = cpu_to_le32(val);
+		r.v8.map = cpu_to_le32(rpt_map);
 		ret = _send_fw_cmd(rtwdev, BTFC_SET, SET_REPORT_EN, &r.v8,
 				   sizeof(r.v8));
 	} else {
@@ -2697,14 +2687,21 @@ static void rtw89_btc_fw_en_rpt(struct rtw89_dev *rtwdev,
 			r.v1.fver = 5;
 		else
 			r.v1.fver = btc->ver->fcxbtcrpt;
-		r.v1.enable = cpu_to_le32(val);
-		r.v1.para = cpu_to_le32(rpt_state);
-		ret = _send_fw_cmd(rtwdev, BTFC_SET, SET_REPORT_EN, &r.v1,
-				   sizeof(r.v1));
+
+		for (i = 0; i < RPT_EN_MONITER; i++) {
+			r.v1.enable = cpu_to_le32(i);
+			if (rpt_map & BIT(i))
+				r.v1.para = cpu_to_le32(1);
+			else
+				r.v1.para = 0;
+
+			ret = _send_fw_cmd(rtwdev, BTFC_SET, SET_REPORT_EN,
+					   &r.v1, sizeof(r.v1));
+		}
 	}
 
 	if (!ret)
-		fwinfo->rpt_en_map = val;
+		fwinfo->rpt_en_map = rpt_map;
 }
 
 static void btc_fw_set_monreg(struct rtw89_dev *rtwdev)
@@ -2766,8 +2763,6 @@ static void btc_fw_set_monreg(struct rtw89_dev *rtwdev)
 	rtw89_debug(rtwdev, RTW89_DBG_BTC,
 		    "[BTC], %s(): sz=%d ulen=%d n=%d\n",
 		    __func__, sz, ulen, n);
-
-	rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_MREG, 1);
 }
 
 static void _update_dm_step(struct rtw89_dev *rtwdev,
@@ -5882,15 +5877,72 @@ static void _update_zb_coex_tbl(struct rtw89_dev *rtwdev)
 	rtw89_write32(rtwdev, R_BTC_ZB_COEX_TBL_1, zb_tbl1);
 }
 
+static void _set_fw_report_map(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_btc *btc = &rtwdev->btc;
+	struct rtw89_btc_wl_smap *wl_smap = &btc->cx.wl.status.map;
+	struct rtw89_btc_bt_info *bt0 = &btc->cx.bt0;
+	struct rtw89_btc_bt_info *bt1 = &btc->cx.bt1;
+	u32 rpt_map = btc->fwinfo.rpt_en_map;
+	u32 bt_rom_code_id, bt_fw_ver;
+	bool bt_ver_unknown = false;
+	u32 bitmap;
+
+	if (wl_smap->rf_off == 1 || wl_smap->lps != BTC_LPS_OFF) {
+		rtw89_btc_fw_en_rpt(rtwdev, false, 0);
+		return;
+	}
+
+	rpt_map |= rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_MREG);
+
+	bitmap = rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_SCAN_INFO);
+	if ((bt0->run_patch_code && bt0->enable.now) ||
+	    (bt1->run_patch_code && bt1->enable.now))
+		rpt_map |= bitmap;
+	else
+		rpt_map &= ~bitmap;
+
+	bt_rom_code_id = chip_id_to_bt_rom_code_id(rtwdev->btc.ver->chip_id);
+	bt_fw_ver = bt0->ver_info.fw & 0xffff;
+	if (bt_fw_ver == 0 ||
+	    (bt_fw_ver == bt_rom_code_id && bt0->run_patch_code))
+		bt_ver_unknown = true;
+
+	bitmap = rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_VER_INFO);
+	if (bt0->enable.now && bt_ver_unknown)
+		rpt_map |= bitmap;
+	else
+		rpt_map &= ~bitmap;
+
+	bitmap = rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_AFH_MAP) |
+		 rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_AFH_MAP_LE) |
+		 rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_TX_PWR_LVL);
+	if (bt0->link_info.link_cnt.now ||
+	    bt0->link_info_56g.link_cnt.now ||
+	    bt1->link_info.link_cnt.now ||
+	    bt1->link_info_56g.link_cnt.now)
+		rpt_map |= bitmap;
+	else
+		rpt_map &= ~bitmap;
+
+	bitmap = rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_DEVICE_INFO);
+	if ((bt0->link_info.a2dp_desc.exist &&
+	     bt0->link_info.a2dp_desc.play_latency) ||
+	    (bt1->link_info.a2dp_desc.exist &&
+	     bt1->link_info.a2dp_desc.play_latency))
+		rpt_map |= bitmap;
+	else
+		rpt_map &= ~bitmap;
+
+	rtw89_btc_fw_en_rpt(rtwdev, false, rpt_map);
+}
+
 static void _action_common(struct rtw89_dev *rtwdev)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
 	struct rtw89_btc_wl_role_info *rinfo = &wl->role_info;
-	struct rtw89_btc_wl_smap *wl_smap = &wl->status.map;
-	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
 	struct rtw89_btc_dm *dm = &btc->dm;
-	u32 bt_rom_code_id, bt_fw_ver;
 	u8 i;
 
 	_wl_req_mac(rtwdev, rinfo->pta_req_band);
@@ -5902,27 +5954,13 @@ static void _action_common(struct rtw89_dev *rtwdev)
 	_set_bt_rx_agc(rtwdev);
 	_set_rf_trx_para(rtwdev);
 	_set_bt_rx_scan_pri(rtwdev);
-
-	bt_rom_code_id = chip_id_to_bt_rom_code_id(rtwdev->btc.ver->chip_id);
-	bt_fw_ver = bt->ver_info.fw & 0xffff;
-	if (bt->enable.now &&
-	    (bt_fw_ver == 0 ||
-	     (bt_fw_ver == bt_rom_code_id && bt->run_patch_code && rtwdev->chip->scbd)))
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, 1);
-	else
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, 0);
+	_set_fw_report_map(rtwdev);
 
 	if (dm->run_reason == BTC_RSN_NTFY_INIT ||
 	    dm->run_reason == BTC_RSN_NTFY_RADIO_STATE ||
-	    dm->run_reason == BTC_RSN_NTFY_POWEROFF) {
+	    dm->run_reason == BTC_RSN_NTFY_POWEROFF)
 		_fw_set_drv_info(rtwdev, CXDRVINFO_ROLE);
 
-		if (wl_smap->rf_off == 1 || wl_smap->lps != BTC_LPS_OFF)
-			rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_ALL, 0);
-		else
-			rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_MREG, 1);
-	}
-
 	for (i = BTC_BT_1ST; i <= BTC_BT_2ND; i++)
 		_sned_h2c_w2bscbd(rtwdev, false, i);
 
@@ -7730,8 +7768,6 @@ void rtw89_btc_ntfy_poweroff(struct rtw89_dev *rtwdev)
 	_write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_ALL, false);
 	_run_coex(rtwdev, BTC_RSN_NTFY_POWEROFF);
 
-	rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_ALL, 0);
-
 	btc->cx.wl.status.map.rf_off_pre = btc->cx.wl.status.map.rf_off;
 }
 
@@ -8344,12 +8380,10 @@ void rtw89_btc_ntfy_radio_state(struct rtw89_dev *rtwdev, enum btc_rfctrl rf_sta
 	}
 
 	if (rf_state == BTC_RFCTRL_WL_ON) {
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_MREG, true);
 		val = BTC_WSCB_ACTIVE | BTC_WSCB_ON | BTC_WSCB_BTLOG;
 		_write_scbd(rtwdev, BTC_ALL_BT, val, true);
 		chip->ops->btc_init_cfg(rtwdev);
 	} else {
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_ALL, false);
 		if (rf_state == BTC_RFCTRL_FW_CTRL)
 			_write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_ACTIVE, false);
 		else if (rf_state == BTC_RFCTRL_WL_OFF)
@@ -8857,11 +8891,6 @@ static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 		       (bt->ver_info.fw_coex >= ver->bt_desired ?
 			"Match" : "Mismatch"), ver->bt_desired);
 
-	if (bt->enable.now && bt->ver_info.fw == 0)
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, true);
-	else
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, false);
-
 	ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw);
 	ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw);
 	ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw);
@@ -9056,7 +9085,6 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 	struct rtw89_btc_cx *cx = &btc->cx;
 	struct rtw89_btc_bt_info *bt = &cx->bt0;
 	struct rtw89_btc_wl_info *wl = &cx->wl;
-	u32 ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw_coex);
 	struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
 	union rtw89_btc_module_info *md = &btc->mdinfo;
 	s8 br_dbm = bt->link_info.bt_txpwr_desc.br_dbm;
@@ -9167,39 +9195,28 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 		       bt->bcnt[BTC_BCNT_LOPRI_TX],
 		       bt->bcnt[BTC_BCNT_POLLUTED]);
 
-	if (!bt->scan_info_update) {
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, true);
-		p += scnprintf(p, end - p, "\n");
-	} else {
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, false);
-		if (ver->fcxbtscan == 1) {
-			p += scnprintf(p, end - p,
-				       "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)",
-				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win),
-				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl),
-				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win),
-				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl),
-				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win),
-				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl),
-				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win),
-				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl));
-		} else if (ver->fcxbtscan == 2) {
-			p += scnprintf(p, end - p,
-				       "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)",
-				       le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win),
-				       le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl),
-				       le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win),
-				       le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl),
-				       le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win),
-				       le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl));
-		}
-		p += scnprintf(p, end - p, "\n");
+	if (ver->fcxbtscan == 1) {
+		p += scnprintf(p, end - p,
+			       "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)",
+			       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win),
+			       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl),
+			       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win),
+			       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl),
+			       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win),
+			       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl),
+			       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win),
+			       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl));
+	} else if (ver->fcxbtscan == 2) {
+		p += scnprintf(p, end - p,
+			       "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)",
+			       le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win),
+			       le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl),
+			       le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win),
+			       le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl),
+			       le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win),
+			       le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl));
 	}
-
-	if (ver_main >= 9 && bt_linfo->link_cnt.now)
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_TX_PWR_LVL, true);
-	else
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_TX_PWR_LVL, false);
+	p += scnprintf(p, end - p, "\n");
 
 	if (bt->bcnt[BTC_BCNT_TXPWR_UPDATE]) {
 		p += scnprintf(p, end - p,
@@ -9218,24 +9235,6 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 	}
 	p += scnprintf(p, end - p, "\n");
 
-	if (bt_linfo->link_cnt.now || bt_linfo->status.map.ble_connect)
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP, true);
-	else
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP, false);
-
-	if (ver->fcxbtafh == 2 && bt_linfo->status.map.ble_connect)
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP_LE, true);
-	else
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP_LE, false);
-
-	if (bt_linfo->a2dp_desc.exist &&
-	    (bt_linfo->a2dp_desc.flush_time == 0 ||
-	     bt_linfo->a2dp_desc.vendor_id == 0 ||
-	     bt_linfo->a2dp_desc.play_latency == 1))
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, true);
-	else
-		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, false);
-
 	return p - buf;
 }
 
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 04/11] wifi: rtw89: ceox: Update antenna & grant signal setting
From: Ping-Ke Shih @ 2026-07-12  3:04 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-1-pkshih@realtek.com>

From: Ching-Te Ku <ku920601@realtek.com>

Merge set antenna & grant signal logic. Combine all information to big
structure for runtime logic using, only separate to version format while
it is going to assign value to register or offload to firmware. Add new
format for dual-BT & external BT for RTL8922D.

Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/coex.c   | 501 ++++++++------------
 drivers/net/wireless/realtek/rtw89/core.h   |   4 +-
 drivers/net/wireless/realtek/rtw89/mac.c    |  27 +-
 drivers/net/wireless/realtek/rtw89/mac_be.c |  21 +-
 4 files changed, 216 insertions(+), 337 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 78360e73d959..2ca6090e9f62 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -492,7 +492,7 @@ enum btc_ant_phase {
 	BTC_ANT_WONLY,
 	BTC_ANT_WOFF,
 	BTC_ANT_W2G,
-	BTC_ANT_W5G,
+	BTC_ANT_FDD,
 	BTC_ANT_W25G,
 	BTC_ANT_FREERUN,
 	BTC_ANT_WRFK,
@@ -841,7 +841,9 @@ enum btc_gnt_state {
 	BTC_GNT_HW	= 0,
 	BTC_GNT_SW_LO,
 	BTC_GNT_SW_HI,
-	BTC_GNT_MAX
+	BTC_GNT_MAX,
+
+	BTC_GNT_SET_SKIP = 0xff,
 };
 
 enum btc_ctr_path {
@@ -1787,8 +1789,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
 			dm->wl_fw_cx_offload = !!le32_to_cpu(prpt->v4.wl_fw_info.cx_offload);
 
 			for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)
-				memcpy(&dm->gnt.band[i], &prpt->v4.gnt_val[i],
-				       sizeof(dm->gnt.band[i]));
+				memcpy(&dm->gnt_set[i], &prpt->v4.gnt_val[i],
+				       sizeof(dm->gnt_set[i]));
 
 			bt->bcnt[BTC_BCNT_HIPRI_TX] =
 				le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_HI_TX]);
@@ -1819,8 +1821,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
 			dm->wl_fw_cx_offload = 0;
 
 			for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)
-				memcpy(&dm->gnt.band[i], &prpt->v5.gnt_val[i][0],
-				       sizeof(dm->gnt.band[i]));
+				memcpy(&dm->gnt_set[i], &prpt->v5.gnt_val[i],
+				       sizeof(dm->gnt_set[i]));
 
 			bt->bcnt[BTC_BCNT_HIPRI_TX] =
 				le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_HI_TX]);
@@ -1846,8 +1848,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
 			dm->wl_fw_cx_offload = 0;
 
 			for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)
-				memcpy(&dm->gnt.band[i], &prpt->v105.gnt_val[i][0],
-				       sizeof(dm->gnt.band[i]));
+				memcpy(&dm->gnt_set[i], &prpt->v105.gnt_val[i],
+				       sizeof(dm->gnt_set[i]));
 
 			bt->bcnt[BTC_BCNT_HIPRI_TX] =
 				le16_to_cpu(prpt->v105.bt_cnt[BTC_BCNT_HI_TX_V105]);
@@ -1872,8 +1874,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
 			wl->ver_info.fw = le32_to_cpu(prpt->v7.rpt_info.fw_ver);
 
 			for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)
-				memcpy(&dm->gnt.band[i], &prpt->v7.gnt_val[i][0],
-				       sizeof(dm->gnt.band[i]));
+				memcpy(&dm->gnt_set[i], &prpt->v7.gnt_val[i],
+				       sizeof(dm->gnt_set[i]));
 
 			bt->bcnt[BTC_BCNT_HIPRI_TX] =
 				le16_to_cpu(prpt->v7.bt_cnt[BTC_BCNT_HI_TX_V105]);
@@ -1904,8 +1906,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
 			wl->ver_info.fw = le32_to_cpu(prpt->v8.rpt_info.fw_ver);
 
 			for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)
-				memcpy(&dm->gnt.band[i], &prpt->v8.gnt_val[i][0],
-				       sizeof(dm->gnt.band[i]));
+				memcpy(&dm->gnt_set[i], &prpt->v8.gnt_val[i],
+				       sizeof(dm->gnt_set[i]));
 
 			bt->bcnt[BTC_BCNT_HIPRI_TX] =
 				le16_to_cpu(prpt->v8.bt_cnt[BTC_BCNT_HI_TX_V105]);
@@ -2971,11 +2973,14 @@ void btc_fw_event(struct rtw89_dev *rtwdev, u8 evt_id, void *data, u32 len)
 		btc->dm.scbd_b2w_update = 0;
 }
 
-static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map, u8 wl_state, u8 bt_state)
+static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map,
+		     u8 wl_state, u8 bt_state, u8 wlact_state)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
 	struct rtw89_btc_dm *dm = &btc->dm;
-	struct rtw89_mac_ax_gnt *g = dm->gnt.band;
+	struct rtw89_btc_fbtc_outsrc_set_info *o = &dm->ost_info;
+	struct rtw89_mac_ax_wl_act *wlact_set;
+	struct rtw89_mac_ax_coex_gnt mg;
 	u8 i;
 
 	if (phy_map > BTC_PHY_ALL)
@@ -2987,126 +2992,118 @@ static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map, u8 wl_state, u8 bt_st
 
 		switch (wl_state) {
 		case BTC_GNT_HW:
-			g[i].gnt_wl_sw_en = 0;
-			g[i].gnt_wl = 0;
+			dm->gnt_set[i].gnt_wl_sw_en = 0;
+			dm->gnt_set[i].gnt_wl = 0;
 			break;
 		case BTC_GNT_SW_LO:
-			g[i].gnt_wl_sw_en = 1;
-			g[i].gnt_wl = 0;
+			dm->gnt_set[i].gnt_wl_sw_en = 1;
+			dm->gnt_set[i].gnt_wl = 0;
 			break;
 		case BTC_GNT_SW_HI:
-			g[i].gnt_wl_sw_en = 1;
-			g[i].gnt_wl = 1;
+			dm->gnt_set[i].gnt_wl_sw_en = 1;
+			dm->gnt_set[i].gnt_wl = 1;
+			break;
+		case BTC_GNT_SET_SKIP:
+		default:
 			break;
 		}
 
 		switch (bt_state) {
 		case BTC_GNT_HW:
-			g[i].gnt_bt_sw_en = 0;
-			g[i].gnt_bt = 0;
+			dm->gnt_set[i].gnt_bt0_sw_en = 0;
+			dm->gnt_set[i].gnt_bt0 = 0;
+			dm->gnt_set[i].gnt_bt1_sw_en = 0;
+			dm->gnt_set[i].gnt_bt1 = 0;
+			dm->gnt_set[i].gnt_zb_sw_en = 0;
+			dm->gnt_set[i].gnt_zb = 0;
 			break;
 		case BTC_GNT_SW_LO:
-			g[i].gnt_bt_sw_en = 1;
-			g[i].gnt_bt = 0;
+			dm->gnt_set[i].gnt_bt0_sw_en = 1;
+			dm->gnt_set[i].gnt_bt0 = 0;
+			dm->gnt_set[i].gnt_bt1_sw_en = 1;
+			dm->gnt_set[i].gnt_bt1 = 0;
+			dm->gnt_set[i].gnt_zb_sw_en = 1;
+			dm->gnt_set[i].gnt_zb = 0;
 			break;
 		case BTC_GNT_SW_HI:
-			g[i].gnt_bt_sw_en = 1;
-			g[i].gnt_bt = 1;
+			dm->gnt_set[i].gnt_bt0_sw_en = 1;
+			dm->gnt_set[i].gnt_bt0 = 1;
+			dm->gnt_set[i].gnt_bt1_sw_en = 1;
+			dm->gnt_set[i].gnt_bt1 = 1;
+			dm->gnt_set[i].gnt_zb_sw_en = 1;
+			dm->gnt_set[i].gnt_zb = 1;
+			break;
+		case BTC_GNT_SET_SKIP:
+		default:
 			break;
 		}
 	}
 
-	rtw89_chip_mac_cfg_gnt(rtwdev, &dm->gnt);
-}
-
-static void _set_gnt_v1(struct rtw89_dev *rtwdev, u8 phy_map,
-			u8 wl_state, u8 bt_state, u8 wlact_state)
-{
-	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_dm *dm = &btc->dm;
-	struct rtw89_btc_fbtc_outsrc_set_info *osi = &dm->ost_info;
-	struct rtw89_mac_ax_wl_act *b = dm->gnt.bt;
-	struct rtw89_mac_ax_gnt *g = dm->gnt.band;
-	u8 i, bt_idx = dm->bt_select + 1;
-
-	if (phy_map > BTC_PHY_ALL)
-		return;
-
-	for (i = 0; i < RTW89_PHY_NUM; i++) {
-		if (!(phy_map & BIT(i)))
-			continue;
+	for (i = 0; i < BTC_ALL_BT_EZL; i++) {
+		if (!(rtwdev->chip->para_ver & BTC_FEAT_WLAN_ACT_MUX))
+			break;
 
-		switch (wl_state) {
-		case BTC_GNT_HW:
-			g[i].gnt_wl_sw_en = 0;
-			g[i].gnt_wl = 0;
+		switch (wlact_state) {
+		case BTC_WLACT_HW:
+			dm->wlact_set[i].wlan_act_en = 0;
+			dm->wlact_set[i].wlan_act = 0;
 			break;
-		case BTC_GNT_SW_LO:
-			g[i].gnt_wl_sw_en = 1;
-			g[i].gnt_wl = 0;
+		case BTC_WLACT_SW_LO:
+			dm->wlact_set[i].wlan_act_en = 1;
+			dm->wlact_set[i].wlan_act = 0;
 			break;
-		case BTC_GNT_SW_HI:
-			g[i].gnt_wl_sw_en = 1;
-			g[i].gnt_wl = 1;
+		case BTC_WLACT_SW_HI:
+			dm->wlact_set[i].wlan_act_en = 1;
+			dm->wlact_set[i].wlan_act = 1;
+			break;
+		default:
 			break;
 		}
 
-		switch (bt_state) {
-		case BTC_GNT_HW:
-			g[i].gnt_bt_sw_en = 0;
-			g[i].gnt_bt = 0;
-			break;
-		case BTC_GNT_SW_LO:
-			g[i].gnt_bt_sw_en = 1;
-			g[i].gnt_bt = 0;
-			break;
-		case BTC_GNT_SW_HI:
-			g[i].gnt_bt_sw_en = 1;
-			g[i].gnt_bt = 1;
+		if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT))
 			break;
-		}
 	}
 
-	if (rtwdev->chip->para_ver & BTC_FEAT_WLAN_ACT_MUX) {
-		for (i = 0; i < 2; i++) {
-			if (!(bt_idx & BIT(i)))
-				continue;
+	rtw89_debug(rtwdev, RTW89_DBG_BTC,
+		    "[BTC], %s(): phy_map=0x%x, gnt_wl:%d, gnt_bt:%d, wl_act:%d\n",
+		    __func__, phy_map, wl_state, bt_state, wlact_state);
 
-			switch (wlact_state) {
-			case BTC_WLACT_HW:
-				b[i].wlan_act_en = 0;
-				b[i].wlan_act = 0;
-				break;
-			case BTC_WLACT_SW_LO:
-				b[i].wlan_act_en = 1;
-				b[i].wlan_act = 0;
-				break;
-			case BTC_WLACT_SW_HI:
-				b[i].wlan_act_en = 1;
-				b[i].wlan_act = 1;
-				break;
+	if (rtwdev->chip->para_ver & BTC_FEAT_H2C_MACRO) {
+		if (o->rf_band[BTC_RF_S0] == 1 && o->rf_band[BTC_RF_S1] == 0)
+			o->rf_gbt_source = BTC_RF_S1;
+		else
+			o->rf_gbt_source = BTC_RF_S0;
+
+		memcpy(o->wlact_set, dm->wlact_set, sizeof(o->wlact_set));
+
+		if (btc->ver->fcxosi == 6) {
+			memcpy(o->gnt_set_be, dm->gnt_set, sizeof(o->gnt_set_be));
+			return;
+		} else if (btc->ver->fcxosi == 1) {
+			for (i = 0; i < RTW89_PHY_NUM; i++) { /* gnt[MAC0/MAC1] */
+				mg.band[i].gnt_bt_sw_en = dm->gnt_set[i].gnt_bt0_sw_en;
+				mg.band[i].gnt_bt = dm->gnt_set[i].gnt_bt0;
+				mg.band[i].gnt_wl_sw_en = dm->gnt_set[i].gnt_wl_sw_en;
+				mg.band[i].gnt_wl = dm->gnt_set[i].gnt_wl;
+				memcpy(o->gnt_set, mg.band, sizeof(o->gnt_set));
 			}
+			return;
 		}
 	}
 
-	if (!btc->ver->fcxosi) {
-		rtw89_mac_cfg_gnt_v2(rtwdev, &dm->gnt);
-		return;
+	for (i = 0; i < RTW89_PHY_NUM; i++) { /* gnt[MAC0/MAC1] */
+		mg.band[i].gnt_bt_sw_en = dm->gnt_set[i].gnt_bt0_sw_en;
+		mg.band[i].gnt_bt = dm->gnt_set[i].gnt_bt0;
+		mg.band[i].gnt_wl_sw_en = dm->gnt_set[i].gnt_wl_sw_en;
+		mg.band[i].gnt_wl = dm->gnt_set[i].gnt_wl;
 	}
-
-	memcpy(osi->gnt_set, dm->gnt.band, sizeof(osi->gnt_set));
-
-	for (i = 0; i < BTC_ALL_BT; i++) {
-		osi->wlact_set[i].wlan_act_en = dm->gnt.bt[i].wlan_act_en;
-		osi->wlact_set[i].wlan_act = dm->gnt.bt[i].wlan_act;
+	for (i = 0; i < BTC_ALL_BT; i++) { /* wlact[BT0/BT1] */
+		wlact_set = &dm->wlact_set[i];
+		mg.bt[i].wlan_act_en = wlact_set->wlan_act_en;
+		mg.bt[i].wlan_act = wlact_set->wlan_act;
 	}
 
-	/* GBT source should be GBT_S1 in 1+1 (HWB0:5G + HWB1:2G) case */
-	if (osi->rf_band[BTC_RF_S0] == 1 &&
-	    osi->rf_band[BTC_RF_S1] == 0)
-		osi->rf_gbt_source = BTC_RF_S1;
-	else
-		osi->rf_gbt_source = BTC_RF_S0;
+	rtw89_chip_mac_cfg_gnt(rtwdev, &mg);
 }
 
 #define BTC_TDMA_WLROLE_MAX 3
@@ -4961,157 +4958,22 @@ static void _set_bt_plut(struct rtw89_dev *rtwdev, u8 phy_map,
 	}
 }
 
-static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec,
-			u8 phy_map, u8 type)
+static void _set_ant(struct rtw89_dev *rtwdev, bool force_exec,
+		     u8 phy_map, u8 type)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_dm *dm = &btc->dm;
 	struct rtw89_btc_cx *cx = &btc->cx;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_role_info *r = &btc->cx.wl.role_info;
-	struct rtw89_btc_bt_info *bt = &cx->bt0;
-	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
-	u8 gwl, gwl0, gwl1, gbt, plt_ctrl, i, b2g = 0;
-	u32 ant_path_type;
-
-	ant_path_type = ((phy_map << 8) + type);
-
-	if (btc->dm.run_reason == BTC_RSN_NTFY_POWEROFF ||
-	    btc->dm.run_reason == BTC_RSN_NTFY_RADIO_STATE ||
-	    btc->dm.run_reason == BTC_RSN_CMD_SET_COEX || r->dbcc_chg)
-		force_exec = FC_EXEC;
-
-	if (!force_exec && ant_path_type == dm->set_ant_path) {
-		rtw89_debug(rtwdev, RTW89_DBG_BTC,
-			    "[BTC], %s(): return by no change!!\n",
-			     __func__);
-		return;
-	} else if (bt->rfk_info.map.run) {
-		rtw89_debug(rtwdev, RTW89_DBG_BTC,
-			    "[BTC], %s(): return by bt rfk!!\n", __func__);
-		return;
-	} else if (btc->dm.run_reason != BTC_RSN_NTFY_WL_RFK &&
-		   wl->rfk_info.state != BTC_WRFK_STOP) {
-		rtw89_debug(rtwdev, RTW89_DBG_BTC,
-			    "[BTC], %s(): return by wl rfk!!\n", __func__);
-		return;
-	}
-
-	dm->set_ant_path = ant_path_type;
-
-	rtw89_debug(rtwdev,
-		    RTW89_DBG_BTC,
-		    "[BTC], %s(): path=0x%x, set_type=0x%x\n",
-		    __func__, phy_map, dm->set_ant_path & 0xff);
-
-	switch (type) {
-	case BTC_ANT_WPOWERON:
-		rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_BT);
-		break;
-	case BTC_ANT_WINIT:
-		if (bt->enable.now)
-			_set_gnt(rtwdev, phy_map, BTC_GNT_SW_LO, BTC_GNT_SW_HI);
-		else
-			_set_gnt(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO);
-
-		rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL);
-		_set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_BT, BTC_PLT_BT);
-		break;
-	case BTC_ANT_WONLY:
-		_set_gnt(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO);
-		rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL);
-		_set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE);
-		break;
-	case BTC_ANT_WOFF:
-		rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_BT);
-		_set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE);
-		break;
-	case BTC_ANT_W2G:
-		rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL);
-		if (rtwdev->dbcc_en) {
-			for (i = 0; i < RTW89_PHY_NUM; i++) {
-				b2g = (wl_dinfo->real_band[i] == RTW89_BAND_2G);
-
-				gwl = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI;
-				gbt = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI;
-				/* BT should control by GNT_BT if WL_2G at S0 */
-				if (i == 1 &&
-				    wl_dinfo->real_band[0] == RTW89_BAND_2G &&
-				    wl_dinfo->real_band[1] == RTW89_BAND_5G)
-					gbt = BTC_GNT_HW;
-				_set_gnt(rtwdev, BIT(i), gwl, gbt);
-				plt_ctrl = b2g ? BTC_PLT_BT : BTC_PLT_NONE;
-				_set_bt_plut(rtwdev, BIT(i),
-					     plt_ctrl, plt_ctrl);
-			}
-		} else {
-			_set_gnt(rtwdev, phy_map, BTC_GNT_HW, BTC_GNT_HW);
-			_set_bt_plut(rtwdev, BTC_PHY_ALL,
-				     BTC_PLT_BT, BTC_PLT_BT);
-		}
-		break;
-	case BTC_ANT_W5G:
-		rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL);
-		_set_gnt(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_HW);
-		_set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE);
-		break;
-	case BTC_ANT_W25G:
-		rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL);
-		_set_gnt(rtwdev, phy_map, BTC_GNT_HW, BTC_GNT_HW);
-		_set_bt_plut(rtwdev, BTC_PHY_ALL,
-			     BTC_PLT_GNT_WL, BTC_PLT_GNT_WL);
-		break;
-	case BTC_ANT_FREERUN:
-		rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL);
-		_set_gnt(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_HI);
-		_set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE);
-		break;
-	case BTC_ANT_WRFK:
-	case BTC_ANT_WRFK2:
-		rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL);
-		_set_gnt(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO);
-		_set_bt_plut(rtwdev, phy_map, BTC_PLT_NONE, BTC_PLT_NONE);
-		break;
-	case BTC_ANT_PTA:
-	default:
-		gbt = BTC_GNT_HW;
-		if ((rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA) ||
-		    !r->dbcc_en) {
-			gwl = BTC_GNT_HW;
-			_set_gnt(rtwdev, BTC_PHY_ALL, gwl, gbt);
-		} else {
-			/* for DBCC Only-1-PTA */
-			if (r->dbcc_2g_phy == RTW89_PHY_0) {
-				gwl0 = BTC_GNT_HW;
-				gwl1 = BTC_GNT_SW_HI;
-			} else {
-				gwl0 = BTC_GNT_SW_HI;
-				gwl1 = BTC_GNT_HW;
-			}
-			_set_gnt(rtwdev, BTC_PHY_0, gwl0, gbt);
-			_set_gnt(rtwdev, BTC_PHY_1, gwl1, gbt);
-		}
-		rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL);
-		_set_bt_plut(rtwdev, phy_map, BTC_PLT_NONE, BTC_PLT_NONE);
-		break;
-	}
-}
-
-static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec,
-			u8 phy_map, u8 type)
-{
-	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
 	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
 	u32 ant_path_type = rtw89_get_antpath_type(phy_map, type);
-	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
 	struct rtw89_btc_dm *dm = &btc->dm;
 	u8 gwl = BTC_GNT_HW, gwl0, gwl1, gbt;
+	bool cx_ctrl;
 
-	if (btc->dm.run_reason == BTC_RSN_NTFY_POWEROFF ||
-	    btc->dm.run_reason == BTC_RSN_NTFY_RADIO_STATE ||
-	    btc->dm.run_reason == BTC_RSN_CMD_SET_COEX || wl_rinfo->dbcc_chg)
+	if (btc->cli_h2c_cmd || wl_rinfo->dbcc_chg ||
+	    dm->run_reason == BTC_RSN_NTFY_POWEROFF ||
+	    dm->run_reason == BTC_RSN_NTFY_RADIO_STATE ||
+	    dm->run_reason == BTC_RSN_CMD_SET_COEX)
 		force_exec = FC_EXEC;
 
 	if (wl_rinfo->link_mode != BTC_WLINK_DB_MCC &&
@@ -5123,10 +4985,6 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec,
 			    "[BTC], %s(): return by no change!!\n",
 			     __func__);
 		return;
-	} else if (bt->rfk_info.map.run) {
-		rtw89_debug(rtwdev, RTW89_DBG_BTC,
-			    "[BTC], %s(): return by bt rfk!!\n", __func__);
-		return;
 	} else if (btc->dm.run_reason != BTC_RSN_NTFY_WL_RFK &&
 		   wl->rfk_info.state != BTC_WRFK_STOP) {
 		rtw89_debug(rtwdev, RTW89_DBG_BTC,
@@ -5141,64 +4999,38 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec,
 		    __func__, phy_map, dm->set_ant_path & 0xff);
 
 	switch (type) {
+	case BTC_ANT_WPOWERON:
+		cx_ctrl = BTC_CTRL_BY_BT;
+		break;
 	case BTC_ANT_WINIT:
+		cx_ctrl = BTC_CTRL_BY_WL;
 		/* To avoid BT MP driver case (bt_enable but no mailbox) */
-		if (bt->enable.now && bt->run_patch_code)
-			_set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_LO, BTC_GNT_SW_HI,
-				    BTC_WLACT_SW_LO);
+		if ((cx->bt0.enable.now && cx->bt0.run_patch_code) ||
+		    (cx->bt1.enable.now && cx->bt1.run_patch_code))
+			_set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_LO, BTC_GNT_SW_HI,
+				 BTC_WLACT_SW_LO);
 		else
-			_set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO,
-				    BTC_WLACT_SW_HI);
+			_set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_SW_LO,
+				 BTC_WLACT_SW_HI);
 		break;
 	case BTC_ANT_WONLY:
-		_set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO,
-			    BTC_WLACT_SW_HI);
+		cx_ctrl = BTC_CTRL_BY_WL;
+		_set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_SW_LO,
+			 BTC_WLACT_SW_HI);
 		break;
 	case BTC_ANT_WOFF:
-		_set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_LO, BTC_GNT_SW_HI,
-			    BTC_WLACT_SW_LO);
-		break;
-	case BTC_ANT_W2G:
-	case BTC_ANT_W25G:
-		if (wl_rinfo->dbcc_en) {
-			if (wl_dinfo->real_band[RTW89_PHY_0] == RTW89_BAND_2G)
-				gwl = BTC_GNT_HW;
-			else
-				gwl = BTC_GNT_SW_HI;
-			_set_gnt_v1(rtwdev, BTC_PHY_0, gwl, BTC_GNT_HW, BTC_WLACT_HW);
-
-			if (wl_dinfo->real_band[RTW89_PHY_1] == RTW89_BAND_2G)
-				gwl = BTC_GNT_HW;
-			else
-				gwl = BTC_GNT_SW_HI;
-			_set_gnt_v1(rtwdev, BTC_PHY_1, gwl, BTC_GNT_HW, BTC_WLACT_HW);
-		} else {
-			gwl = BTC_GNT_HW;
-			_set_gnt_v1(rtwdev, phy_map, gwl, BTC_GNT_HW, BTC_WLACT_HW);
-		}
-		break;
-	case BTC_ANT_W5G:
-		_set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_HW, BTC_WLACT_HW);
-		break;
-	case BTC_ANT_FREERUN:
-		_set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_HI,
-			    BTC_WLACT_SW_LO);
-		break;
-	case BTC_ANT_WRFK:
-		_set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO,
-			    BTC_WLACT_HW);
-		break;
-	case BTC_ANT_WRFK2:
-		_set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO,
-			    BTC_WLACT_SW_HI); /* no BT-Tx */
+		cx_ctrl = BTC_CTRL_BY_BT;
+		_set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_LO, BTC_GNT_SW_HI,
+			 BTC_WLACT_SW_LO);
 		break;
 	case BTC_ANT_PTA:
 	default:
+		cx_ctrl = BTC_CTRL_BY_WL;
 		gbt = BTC_GNT_HW;
 		if ((rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA) ||
 		    !wl_rinfo->dbcc_en) {
 			gwl = BTC_GNT_HW;
-			_set_gnt_v1(rtwdev, BTC_PHY_ALL, gwl, gbt, BTC_WLACT_HW);
+			_set_gnt(rtwdev, BTC_PHY_ALL, gwl, gbt, BTC_WLACT_HW);
 		} else {
 			/* for DBCC Only-1-PTA */
 			if (wl_rinfo->dbcc_2g_phy == RTW89_PHY_0) {
@@ -5208,24 +5040,36 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec,
 				gwl0 = BTC_GNT_SW_HI;
 				gwl1 = BTC_GNT_HW;
 			}
-			_set_gnt_v1(rtwdev, BTC_PHY_0, gwl0, gbt, BTC_WLACT_HW);
-			_set_gnt_v1(rtwdev, BTC_PHY_1, gwl1, gbt, BTC_WLACT_HW);
+			_set_gnt(rtwdev, BTC_PHY_0, gwl0, gbt, BTC_WLACT_HW);
+			_set_gnt(rtwdev, BTC_PHY_1, gwl1, gbt, BTC_WLACT_HW);
 		}
 		break;
+	case BTC_ANT_FDD:
+		cx_ctrl = BTC_CTRL_BY_WL;
+		_set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_HW,
+			 BTC_WLACT_HW);
+		break;
+	case BTC_ANT_FREERUN:
+		cx_ctrl = BTC_CTRL_BY_WL;
+		_set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_SW_HI,
+			 BTC_WLACT_SW_LO);
+		break;
+	case BTC_ANT_WRFK:
+		cx_ctrl = BTC_CTRL_BY_WL;
+		_set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_SW_LO,
+			 BTC_WLACT_HW);
+		break;
+	case BTC_ANT_WRFK2:
+		cx_ctrl = BTC_CTRL_BY_WL;
+		_set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_SW_LO,
+			 BTC_WLACT_SW_HI); /* no BT-Tx */
+		break;
 	}
 
+	rtw89_chip_cfg_ctrl_path(rtwdev, cx_ctrl);
 	_set_bt_plut(rtwdev, phy_map, BTC_PLT_GNT_WL, BTC_PLT_GNT_WL);
 }
 
-static void _set_ant(struct rtw89_dev *rtwdev, bool force_exec,
-		     u8 phy_map, u8 type)
-{
-	if (rtwdev->chip->chip_id >= RTL8922A)
-		_set_ant_v1(rtwdev, force_exec, phy_map, type);
-	else
-		_set_ant_v0(rtwdev, force_exec, phy_map, type);
-}
-
 static void _action_wl_only(struct rtw89_dev *rtwdev)
 {
 	_set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_WONLY);
@@ -5619,7 +5463,7 @@ static void _action_bt_a2dp_pan_hid(struct rtw89_dev *rtwdev)
 
 static void _action_wl_5g(struct rtw89_dev *rtwdev)
 {
-	_set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W5G);
+	_set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_FDD);
 	_set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_5G);
 }
 
@@ -9674,7 +9518,7 @@ static const char *id_to_ant(u32 id)
 	CASE_BTC_ANTPATH_STR(WONLY);
 	CASE_BTC_ANTPATH_STR(WOFF);
 	CASE_BTC_ANTPATH_STR(W2G);
-	CASE_BTC_ANTPATH_STR(W5G);
+	CASE_BTC_ANTPATH_STR(FDD);
 	CASE_BTC_ANTPATH_STR(W25G);
 	CASE_BTC_ANTPATH_STR(FREERUN);
 	CASE_BTC_ANTPATH_STR(WRFK);
@@ -10898,6 +10742,7 @@ static int _show_fw_dm_msg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt_cfg)
 {
 	const struct rtw89_chip_info *chip = rtwdev->chip;
+	struct rtw89_mac_ax_wl_act *bt;
 	struct rtw89_mac_ax_gnt *gnt;
 	u32 val, status;
 
@@ -10932,7 +10777,39 @@ static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt
 		gnt->gnt_bt = !!(status & B_AX_GNT_BT_RFC_S1);
 		gnt->gnt_wl_sw_en = !!(val & B_AX_GNT_WL_RFC_S1_SWCTRL);
 		gnt->gnt_wl = !!(status & B_AX_GNT_WL_RFC_S1);
-	} else {
+
+		bt = &gnt_cfg->bt[0];
+		bt->wlan_act_en = !!(val & B_BE_WL_ACT_SWCTRL);
+		bt->wlan_act = !!(status & B_BE_WL_ACT_VAL);
+
+		bt = &gnt_cfg->bt[1];
+		bt->wlan_act_en = !!(val & B_BE_WL_ACT2_SWCTRL);
+		bt->wlan_act = !!(status & B_BE_WL_ACT2_VAL);
+	} else if (chip->chip_id == RTL8922A) {
+		val = rtw89_read32(rtwdev, R_BE_GNT_SW_CTRL);
+		status = rtw89_read32(rtwdev, R_BE_PTA_GNT_VAL);
+
+		gnt = &gnt_cfg->band[0];
+		gnt->gnt_bt_sw_en = !!(val & B_BE_GNT_BT_BB0_SWCTRL);
+		gnt->gnt_bt = !!(status & B_BE_GNT_BT_BB0_VAL);
+		gnt->gnt_wl_sw_en = !!(val & B_BE_GNT_WL_BB0_SWCTRL);
+		gnt->gnt_wl = !!(status & B_BE_GNT_WL_BB0_VAL);
+
+		gnt = &gnt_cfg->band[1];
+		gnt->gnt_bt_sw_en = !!(val & B_BE_GNT_BT_BB1_SWCTRL);
+		gnt->gnt_bt = !!(status & B_BE_GNT_BT_BB1_VAL);
+		gnt->gnt_wl_sw_en = !!(val & B_BE_GNT_WL_BB1_SWCTRL);
+		gnt->gnt_wl = !!(status & B_BE_GNT_WL_BB1_VAL);
+
+		bt = &gnt_cfg->bt[0];
+		bt->wlan_act_en = !!(val & B_BE_WL_ACT_SWCTRL);
+		bt->wlan_act = !!(status & B_BE_WL_ACT_VAL);
+
+		bt = &gnt_cfg->bt[1];
+		bt->wlan_act_en = !!(val & B_BE_WL_ACT2_SWCTRL);
+		bt->wlan_act = !!(status & B_BE_WL_ACT2_VAL);
+	} else if (chip->chip_id == RTL8922D) {
+		/* Get from firmware */
 		return;
 	}
 }
@@ -11181,7 +11058,6 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 	struct rtw89_btc_bt_info *bt0 = &cx->bt0;
 	struct rtw89_btc_bt_info *bt1 = &cx->bt1;
 	struct rtw89_btc_wl_info *wl = &cx->wl;
-	struct rtw89_mac_ax_gnt *gnt = NULL;
 	struct rtw89_btc_dm *dm = &btc->dm;
 	char *p = buf, *end = buf + bufsz;
 	u8 i, type, cnt = 0;
@@ -11219,18 +11095,19 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 		       wl->pta_req_mac,
 		       id_to_polut(wl->bt_polut_type[wl->pta_req_mac]));
 
-	gnt = &dm->gnt.band[RTW89_PHY_0];
-
 	p += scnprintf(p, end - p, ", phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]",
-		       gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl,
-		       gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt);
+		      dm->gnt_set[RTW89_PHY_0].gnt_wl_sw_en ? "SW" : "HW",
+		      dm->gnt_set[RTW89_PHY_0].gnt_wl,
+		      dm->gnt_set[RTW89_PHY_0].gnt_bt0_sw_en ? "SW" : "HW",
+		      dm->gnt_set[RTW89_PHY_0].gnt_bt0);
 
 	if (rtwdev->dbcc_en) {
-		gnt = &dm->gnt.band[RTW89_PHY_1];
 		p += scnprintf(p, end - p,
 			       ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]",
-			       gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl,
-			       gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt);
+			       dm->gnt_set[RTW89_PHY_1].gnt_wl_sw_en ? "SW" : "HW",
+			       dm->gnt_set[RTW89_PHY_1].gnt_wl,
+			       dm->gnt_set[RTW89_PHY_1].gnt_bt0_sw_en ? "SW" : "HW",
+			       dm->gnt_set[RTW89_PHY_1].gnt_bt0);
 	}
 
 	pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo;
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 676d3ba855d7..a928140300b9 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -3481,7 +3481,9 @@ struct rtw89_btc_dm {
 	union rtw89_btc_fbtc_slot_u slot_now;
 	struct rtw89_btc_fbtc_tdma tdma;
 	struct rtw89_btc_fbtc_tdma tdma_now;
-	struct rtw89_mac_ax_coex_gnt gnt;
+	struct rtw89_btc_gnt_ctrl gnt_set[RTW89_MAC_AX_COEX_GNT_NR];
+	struct rtw89_btc_gnt_ctrl gnt_val[RTW89_MAC_AX_COEX_GNT_NR];
+	struct rtw89_mac_ax_wl_act wlact_set[BTC_ALL_BT_EZL];
 	union rtw89_btc_init_info_u init_info; /* pass to wl_fw if offload */
 	struct rtw89_btc_rf_trx_para_v9 rf_trx_para;
 	struct rtw89_btc_wl_tx_limit_para wl_tx_limit;
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index 6e3da5e4a1b3..b9cd7fd8d76b 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -6538,8 +6538,6 @@ int rtw89_mac_cfg_gnt_v1(struct rtw89_dev *rtwdev,
 	if (gnt_cfg->band[0].gnt_bt)
 		val |= B_AX_GNT_BT_RFC_S0_VAL | B_AX_GNT_BT_RX_VAL |
 		       B_AX_GNT_BT_TX_VAL;
-	else
-		val |= B_AX_WL_ACT_VAL;
 
 	if (gnt_cfg->band[0].gnt_bt_sw_en)
 		val |= B_AX_GNT_BT_RFC_S0_SWCTRL | B_AX_GNT_BT_RX_SWCTRL |
@@ -6556,8 +6554,6 @@ int rtw89_mac_cfg_gnt_v1(struct rtw89_dev *rtwdev,
 	if (gnt_cfg->band[1].gnt_bt)
 		val |= B_AX_GNT_BT_RFC_S1_VAL | B_AX_GNT_BT_RX_VAL |
 		       B_AX_GNT_BT_TX_VAL;
-	else
-		val |= B_AX_WL_ACT_VAL;
 
 	if (gnt_cfg->band[1].gnt_bt_sw_en)
 		val |= B_AX_GNT_BT_RFC_S1_SWCTRL | B_AX_GNT_BT_RX_SWCTRL |
@@ -6571,6 +6567,15 @@ int rtw89_mac_cfg_gnt_v1(struct rtw89_dev *rtwdev,
 		val |= B_AX_GNT_WL_RFC_S1_SWCTRL | B_AX_GNT_WL_RX_SWCTRL |
 		       B_AX_GNT_WL_TX_SWCTRL | B_AX_GNT_WL_BB_SWCTRL;
 
+	if (gnt_cfg->bt[0].wlan_act_en)
+		val |= B_AX_WL_ACT_SWCTRL;
+	if (gnt_cfg->bt[0].wlan_act)
+		val |= B_AX_WL_ACT_VAL;
+	if (gnt_cfg->bt[1].wlan_act_en)
+		val |= B_AX_WL_ACT2_SWCTRL;
+	if (gnt_cfg->bt[1].wlan_act)
+		val |= B_AX_WL_ACT2_VAL;
+
 	rtw89_write32(rtwdev, R_AX_GNT_SW_CTRL, val);
 
 	return 0;
@@ -6645,22 +6650,20 @@ EXPORT_SYMBOL(rtw89_mac_cfg_ctrl_path);
 
 int rtw89_mac_cfg_ctrl_path_v1(struct rtw89_dev *rtwdev, bool wl)
 {
-	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_dm *dm = &btc->dm;
-	struct rtw89_mac_ax_gnt *g = dm->gnt.band;
+	struct rtw89_mac_ax_coex_gnt gnt = {};
 	int i;
 
 	if (wl)
 		return 0;
 
 	for (i = 0; i < RTW89_PHY_NUM; i++) {
-		g[i].gnt_bt_sw_en = 1;
-		g[i].gnt_bt = 1;
-		g[i].gnt_wl_sw_en = 1;
-		g[i].gnt_wl = 0;
+		gnt.band[i].gnt_bt_sw_en = 1;
+		gnt.band[i].gnt_bt = 1;
+		gnt.band[i].gnt_wl_sw_en = 1;
+		gnt.band[i].gnt_wl = 0;
 	}
 
-	return rtw89_mac_cfg_gnt_v1(rtwdev, &dm->gnt);
+	return rtw89_mac_cfg_gnt_v1(rtwdev, &gnt);
 }
 EXPORT_SYMBOL(rtw89_mac_cfg_ctrl_path_v1);
 
diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c
index 4bdf20b7ba6d..33513b283d84 100644
--- a/drivers/net/wireless/realtek/rtw89/mac_be.c
+++ b/drivers/net/wireless/realtek/rtw89/mac_be.c
@@ -2466,29 +2466,26 @@ EXPORT_SYMBOL(rtw89_mac_cfg_gnt_v3);
 
 int rtw89_mac_cfg_ctrl_path_v2(struct rtw89_dev *rtwdev, bool wl)
 {
-	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_dm *dm = &btc->dm;
-	struct rtw89_mac_ax_gnt *g = dm->gnt.band;
-	struct rtw89_mac_ax_wl_act *gbt = dm->gnt.bt;
 	const struct rtw89_chip_info *chip = rtwdev->chip;
+	struct rtw89_mac_ax_coex_gnt gnt = {};
 	int i;
 
 	if (wl)
 		return 0;
 
 	for (i = 0; i < RTW89_PHY_NUM; i++) {
-		g[i].gnt_bt_sw_en = 1;
-		g[i].gnt_bt = 1;
-		g[i].gnt_wl_sw_en = 1;
-		g[i].gnt_wl = 0;
-		gbt[i].wlan_act = 1;
-		gbt[i].wlan_act_en = 0;
+		gnt.band[i].gnt_bt_sw_en = 1;
+		gnt.band[i].gnt_bt = 1;
+		gnt.band[i].gnt_wl_sw_en = 1;
+		gnt.band[i].gnt_wl = 0;
+		gnt.bt[i].wlan_act = 1;
+		gnt.bt[i].wlan_act_en = 0;
 	}
 
 	if (chip->chip_id == RTL8922A)
-		return rtw89_mac_cfg_gnt_v2(rtwdev, &dm->gnt);
+		return rtw89_mac_cfg_gnt_v2(rtwdev, &gnt);
 	else
-		return rtw89_mac_cfg_gnt_v3(rtwdev, &dm->gnt);
+		return rtw89_mac_cfg_gnt_v3(rtwdev, &gnt);
 
 }
 EXPORT_SYMBOL(rtw89_mac_cfg_ctrl_path_v2);
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 03/11] wifi: rtw89: coex: Update driver outsource info to firmware version 6
From: Ping-Ke Shih @ 2026-07-12  3:04 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-1-pkshih@realtek.com>

From: Ching-Te Ku <ku920601@realtek.com>

In order to make dual MAC Wi-Fi performance more stable, and take effect
in time, offload more register/ hardware control to firmware.

Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/coex.c |  13 ++-
 drivers/net/wireless/realtek/rtw89/core.h |  89 +++++++++++++++++--
 drivers/net/wireless/realtek/rtw89/fw.c   | 101 +++++++++++++++++++++-
 drivers/net/wireless/realtek/rtw89/fw.h   |   8 +-
 4 files changed, 199 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 72586179ec2c..78360e73d959 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -2923,7 +2923,12 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index)
 		else
 			return;
 
-		rtw89_fw_h2c_cxdrv_osi_info(rtwdev, index);
+		if (ver->fcxosi == 1)
+			rtw89_fw_h2c_cxdrv_osi_info(rtwdev, index);
+		else if (ver->fcxosi == 6)
+			rtw89_fw_h2c_cxdrv_osi_info_v6(rtwdev, index);
+		else
+			return;
 		break;
 	default:
 		break;
@@ -3090,7 +3095,11 @@ static void _set_gnt_v1(struct rtw89_dev *rtwdev, u8 phy_map,
 	}
 
 	memcpy(osi->gnt_set, dm->gnt.band, sizeof(osi->gnt_set));
-	memcpy(osi->wlact_set, dm->gnt.bt, sizeof(osi->wlact_set));
+
+	for (i = 0; i < BTC_ALL_BT; i++) {
+		osi->wlact_set[i].wlan_act_en = dm->gnt.bt[i].wlan_act_en;
+		osi->wlact_set[i].wlan_act = dm->gnt.bt[i].wlan_act;
+	}
 
 	/* GBT source should be GBT_S1 in 1+1 (HWB0:5G + HWB1:2G) case */
 	if (osi->rf_band[BTC_RF_S0] == 1 &&
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 0b20ee7d7a28..676d3ba855d7 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -1322,6 +1322,17 @@ struct rtw89_mac_ax_gnt {
 	u8 gnt_wl;
 } __packed;
 
+struct rtw89_btc_gnt_ctrl {
+	u8 gnt_zb_sw_en;
+	u8 gnt_zb;
+	u8 gnt_bt1_sw_en;
+	u8 gnt_bt1;
+	u8 gnt_bt0_sw_en;
+	u8 gnt_bt0;
+	u8 gnt_wl_sw_en;
+	u8 gnt_wl;
+} __packed;
+
 struct rtw89_mac_ax_wl_act {
 	u8 wlan_act_en;
 	u8 wlan_act;
@@ -3374,27 +3385,89 @@ enum btc_rf_path {
 	BTC_RF_NUM,
 };
 
-struct rtw89_btc_fbtc_outsrc_set_info {
-	u8 rf_band[BTC_RF_NUM]; /* 0:2G, 1:non-2G */
+struct rtw89_btc_fbtc_outsrc_set_info_v1 {
+	u8 rf_band[BTC_RF_NUM];
 	u8 btg_rx[BTC_RF_NUM];
 	u8 nbtg_tx[BTC_RF_NUM];
 
-	struct rtw89_mac_ax_gnt gnt_set[BTC_RF_NUM]; /* refer to btc_gnt_ctrl */
-	struct rtw89_mac_ax_wl_act wlact_set[BTC_RF_NUM]; /* BT0/BT1 */
+	struct rtw89_mac_ax_gnt gnt_set[BTC_RF_NUM];
+	struct rtw89_mac_ax_wl_act wlact_set[BTC_ALL_BT];
 
 	u8 pta_req_hw_band;
 	u8 rf_gbt_source;
 	u8 bt_enable_state;
 	u8 wl_btg_standby_chg;
-	/* bit[15]-> 0:2G/1:5G,6G, bit[14:0]-> WL HWBx ch freq in MHz */
+
+	u8 fbd_group_en[RTW89_MAC_NUM][2];
+	__le16 rf_center_freq[RTW89_MAC_NUM];
+	__le16 fbd_group_bound[RTW89_MAC_NUM][2];
+	__le16 freq_diff_thres[RTW89_MAC_NUM][BTC_ALL_BT];
+} __packed;
+
+struct rtw89_btc_fbtc_outsrc_set_info_v6 {
+	u8 rf_band[BTC_RF_NUM];
+	u8 btg_rx[BTC_RF_NUM];
+	u8 nbtg_tx[BTC_RF_NUM];
+
+	struct rtw89_btc_gnt_ctrl gnt_set[RTW89_MAC_AX_COEX_GNT_NR];
+	struct rtw89_mac_ax_wl_act wlact_set[BTC_ALL_BT_EZL];
+
+	u8 pta_req_hw_band;
+	u8 rf_gbt_source;
+	u8 bt_enable_state;
+	u8 bt_plut_type;
+	u8 wl_tx_limit_en;
+	u8 fc_exec;
+	u8 wl_btg_standby_chg;
+	u8 rsvd;
+	u8 bb_path_sel_bt[BTC_RF_NUM];
+	u8 bb_phy_sel_bt[RTW89_PHY_NUM];
+	u8 fbd_group_en[RTW89_MAC_NUM][2];
+	__le16 rf_center_freq[RTW89_MAC_NUM];
+	__le16 freq_diff_thres[RTW89_MAC_NUM][BTC_ALL_BT_EZL];
+	__le16 fbd_group_bound[RTW89_MAC_NUM][2];
+	__le32 wl_tx_limit_time;
+} __packed;
+
+struct rtw89_btc_fbtc_outsrc_set_info {
+	u8 rf_band[BTC_RF_NUM]; /* 0:2GHz/1:5GHz for MPI_bb_hwsi_ignore_gnt_wl() */
+	u8 btg_rx[BTC_RF_NUM]; /* for MPI_bb_btg_bt_rx() */
+	u8 nbtg_tx[BTC_RF_NUM]; /* for MPI_bb_nbtg_bt_tx pre=AGC control */
+
+	struct rtw89_mac_ax_gnt gnt_set[BTC_RF_NUM]; /* refer to btc_gnt_ctrl */
+	struct rtw89_btc_gnt_ctrl gnt_set_be[RTW89_MAC_AX_COEX_GNT_NR];
+	struct rtw89_mac_ax_wl_act wlact_set[BTC_ALL_BT_EZL];
+
+	u8 pta_req_hw_band; /* Bind PTA to HWB0 or HWB1, only for 8922a 1-PTA */
+	u8 rf_gbt_source; /* gbt from S0 or S1 for RF 0x2[9], only for 8922a */
+
+	/* The followngs are for 8922c/d  new Multi-PTA design */
+	/* 0:BT0/1:BT1/2:ZB on/off for MAC(0xe580[0]/0xe680[0])/ RF 0x4[3:2] */
+	u8 bt_enable_state;
+	u8 bt_plut_type;   /* BT polluted type, refer to enum btc_plt_map */
+
+	u8 wl_tx_limit_en;
+	u8 fc_exec;
+	u8 wl_btg_standby_chg; /* keep RX-IQGen on in standby mode */
+	u8 rsvd;
+
+	u8 bb_path_sel_bt[BTC_RF_NUM]; /* bb s0(1) select GNT_BT0 or BT1 */
+	u8 bb_phy_sel_bt[RTW89_PHY_NUM]; /* bb phy0(1) select GNT_BT0 or BT1 */
+
 	/* forbidden group-> bit[1]:fbd rf-band, bit[0]: fbd enable */
 	u8 fbd_group_en[RTW89_MAC_NUM][2]; /* HWB0/1 +.Group0/1 */
+
+	/* bit[15]-> 0:2G/1:5G,6G, bit[14:0]-> WL HWBx ch freq in MHz */
 	u16 rf_center_freq[RTW89_MAC_NUM]; /* HWB0/1 */
-	/* forbidden group boundary: [15:8]->UP, [7:0]->LO */
-	u16 fbd_group_bound[RTW89_MAC_NUM][2]; /* HWB0/1 +.Group0/1 */
+
 	/* 11-bit in MHz, freq diff threshold */
 	u16 freq_diff_thres[RTW89_MAC_NUM][BTC_ALL_BT_EZL]; /* HWB0/1 vs.BT0/1/2 */
-} __packed;
+
+	/* forbidden group boundary: [15:8]->UP, [7:0]->LO */
+	u16 fbd_group_bound[RTW89_MAC_NUM][2]; /* HWB0/1 +.Group0/1 */
+
+	u32 wl_tx_limit_time;
+};
 
 union rtw89_btc_fbtc_slot_u {
 	struct rtw89_btc_fbtc_slot v1[CXST_MAX];
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 59e51e67d51b..027f8121efd1 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -6404,6 +6404,7 @@ int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type)
 	u32 len = sizeof(*h2c);
 	struct sk_buff *skb;
 	int ret;
+	u8 i, j;
 
 	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
 	if (!skb) {
@@ -6416,7 +6417,105 @@ int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type)
 	h2c->hdr.type = type;
 	h2c->hdr.ver = btc->ver->fcxosi;
 	h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7;
-	h2c->osi = *osi;
+
+	memcpy(h2c->osi.rf_band, osi->rf_band, sizeof(osi->rf_band));
+	memcpy(h2c->osi.btg_rx, osi->btg_rx, sizeof(osi->btg_rx));
+	memcpy(h2c->osi.nbtg_tx, osi->nbtg_tx, sizeof(osi->nbtg_tx));
+
+	for (i = 0; i < BTC_RF_NUM; i++) {
+		h2c->osi.gnt_set[i] = osi->gnt_set[i];
+		h2c->osi.wlact_set[i] = osi->wlact_set[i];
+	}
+
+	h2c->osi.pta_req_hw_band = osi->pta_req_hw_band;
+	h2c->osi.rf_gbt_source = osi->rf_gbt_source;
+	h2c->osi.bt_enable_state = osi->bt_enable_state;
+	h2c->osi.wl_btg_standby_chg = osi->wl_btg_standby_chg;
+
+	memcpy(h2c->osi.fbd_group_en, osi->fbd_group_en, sizeof(osi->fbd_group_en));
+
+	for (i = 0; i < RTW89_MAC_NUM; i++) {
+		h2c->osi.rf_center_freq[i] = cpu_to_le16(osi->rf_center_freq[i]);
+		for (j = 0; j < BTC_RF_NUM; j++)
+			h2c->osi.fbd_group_bound[i][j] = cpu_to_le16(osi->fbd_group_bound[i][j]);
+	}
+
+	for (i = 0; i < RTW89_MAC_NUM; i++) {
+		for (j = 0; j < BTC_ALL_BT; j++)
+			h2c->osi.freq_diff_thres[i][j] = cpu_to_le16(osi->freq_diff_thres[i][j]);
+	}
+
+	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+			      H2C_CAT_OUTSRC, BTFC_SET,
+			      SET_DRV_INFO, 0, 0,
+			      len);
+
+	ret = rtw89_h2c_tx(rtwdev, skb, false);
+	if (ret) {
+		rtw89_err(rtwdev, "failed to send h2c\n");
+		goto fail;
+	}
+
+	return 0;
+fail:
+	dev_kfree_skb_any(skb);
+
+	return ret;
+}
+
+int rtw89_fw_h2c_cxdrv_osi_info_v6(struct rtw89_dev *rtwdev, u8 type)
+{
+	struct rtw89_btc *btc = &rtwdev->btc;
+	struct rtw89_btc_fbtc_outsrc_set_info *osi = &btc->dm.ost_info;
+	struct rtw89_h2c_cxosi_v6 *h2c;
+	u32 len = sizeof(*h2c);
+	struct sk_buff *skb;
+	int ret;
+	u8 i, j;
+
+	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+	if (!skb) {
+		rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_osi_v6\n");
+		return -ENOMEM;
+	}
+	skb_put(skb, len);
+	h2c = (struct rtw89_h2c_cxosi_v6 *)skb->data;
+
+	h2c->hdr.type = type;
+	h2c->hdr.ver = btc->ver->fcxosi;
+	h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7;
+
+	memcpy(h2c->osi.rf_band, osi->rf_band, sizeof(osi->rf_band));
+	memcpy(h2c->osi.btg_rx, osi->btg_rx, sizeof(osi->btg_rx));
+	memcpy(h2c->osi.nbtg_tx, osi->nbtg_tx, sizeof(osi->nbtg_tx));
+	memcpy(h2c->osi.gnt_set, osi->gnt_set, sizeof(osi->gnt_set));
+	memcpy(h2c->osi.wlact_set, osi->wlact_set, sizeof(osi->wlact_set));
+
+	h2c->osi.pta_req_hw_band = osi->pta_req_hw_band;
+	h2c->osi.rf_gbt_source = osi->rf_gbt_source;
+	h2c->osi.bt_enable_state = osi->bt_enable_state;
+	h2c->osi.bt_plut_type = osi->bt_plut_type;
+	h2c->osi.wl_tx_limit_en = osi->wl_tx_limit_en;
+	h2c->osi.fc_exec = osi->fc_exec;
+	h2c->osi.wl_btg_standby_chg = osi->wl_btg_standby_chg;
+	h2c->osi.rsvd = osi->rsvd;
+
+	memcpy(h2c->osi.bb_path_sel_bt, osi->bb_path_sel_bt, sizeof(osi->bb_path_sel_bt));
+	memcpy(h2c->osi.bb_phy_sel_bt, osi->bb_phy_sel_bt, sizeof(osi->bb_phy_sel_bt));
+	memcpy(h2c->osi.fbd_group_en, osi->fbd_group_en, sizeof(osi->fbd_group_en));
+
+	for (i = 0; i < RTW89_MAC_NUM; i++) {
+		h2c->osi.rf_center_freq[i] = cpu_to_le16(osi->rf_center_freq[i]);
+		for (j = 0; j < BTC_RF_NUM; j++)
+			h2c->osi.fbd_group_bound[i][j] = cpu_to_le16(osi->fbd_group_bound[i][j]);
+	}
+
+	for (i = 0; i < RTW89_MAC_NUM; i++) {
+		for (j = 0; j < BTC_ALL_BT_EZL; j++)
+			h2c->osi.freq_diff_thres[i][j] = cpu_to_le16(osi->freq_diff_thres[i][j]);
+	}
+
+	h2c->osi.wl_tx_limit_time = cpu_to_le32(osi->wl_tx_limit_time);
 
 	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
 			      H2C_CAT_OUTSRC, BTFC_SET,
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index 72f56d32e3b0..c6131778f6a2 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -2455,7 +2455,12 @@ struct rtw89_h2c_cxrole_v10 {
 
 struct rtw89_h2c_cxosi {
 	struct rtw89_h2c_cxhdr_v7 hdr;
-	struct rtw89_btc_fbtc_outsrc_set_info osi;
+	struct rtw89_btc_fbtc_outsrc_set_info_v1 osi;
+} __packed;
+
+struct rtw89_h2c_cxosi_v6 {
+	struct rtw89_h2c_cxhdr_v7 hdr;
+	struct rtw89_btc_fbtc_outsrc_set_info_v6 osi;
 } __packed;
 
 struct rtw89_h2c_cxinit {
@@ -5385,6 +5390,7 @@ int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type);
+int rtw89_fw_h2c_cxdrv_osi_info_v6(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_ctrl_v9(struct rtw89_dev *rtwdev, u8 type);
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 02/11] wifi: rtw89: coex: Rearrange coexistence control structure
From: Ping-Ke Shih @ 2026-07-12  3:04 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-1-pkshih@realtek.com>

From: Ching-Te Ku <ku920601@realtek.com>

The control structure will record some Wi-Fi/Bluetooth status, and
packed send to firmware. The new generation chip had offloaded many
mechanism control to firmware, firmware may need update these very
often to make sure run in correct mechanism.

Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/coex.c  | 154 ++++++++++-----------
 drivers/net/wireless/realtek/rtw89/core.h  |  76 ++++++++--
 drivers/net/wireless/realtek/rtw89/debug.c |   6 +-
 drivers/net/wireless/realtek/rtw89/fw.c    |  63 ++++++++-
 drivers/net/wireless/realtek/rtw89/fw.h    |   6 +
 5 files changed, 207 insertions(+), 98 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index c02de0b4f7bf..72586179ec2c 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -295,10 +295,11 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {
 static const union rtw89_btc_wl_state_map btc_scanning_map = {
 	.map = {
 		.scan = 1,
-		.connecting = 1,
+		.dhcp = 1,
 		.roaming = 1,
-		.dbccing = 1,
+		.transacting = 1,
 		._4way = 1,
+		.handshake = 1,
 	},
 };
 
@@ -998,8 +999,7 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type)
 	if (type & BTC_RESET_CTRL) {
 		memset(&btc->ctrl, 0, sizeof(btc->ctrl));
 		btc->manual_ctrl = false;
-		if (ver->fcxctrl != 7)
-			btc->ctrl.ctrl.trace_step = FCXDEF_STEP;
+		btc->ctrl.trace_step = FCXDEF_STEP;
 	}
 
 	/* Init Coex variables that are not zero */
@@ -1601,7 +1601,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
 	case BTC_RPT_TYPE_STEP:
 		pcinfo = &pfwinfo->rpt_fbtc_step.cinfo;
 		if (ver->fcxctrl != 7)
-			trace_step = btc->ctrl.ctrl.trace_step;
+			trace_step = btc->ctrl.trace_step;
 
 		if (ver->fcxstep == 2) {
 			pfinfo = &pfwinfo->rpt_fbtc_step.finfo.v2;
@@ -3542,8 +3542,9 @@ static void _update_btc_state_map(struct rtw89_dev *rtwdev)
 	struct rtw89_btc_bt_info *bt = &cx->bt0;
 	struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
 
-	if (wl->status.map.connecting || wl->status.map._4way ||
-	    wl->status.map.roaming || wl->status.map.dbccing) {
+	if (wl->status.map.dhcp || wl->status.map._4way ||
+	    wl->status.map.roaming || wl->status.map.transacting ||
+	    wl->status.map.handshake) {
 		cx->state_map = BTC_WLINKING;
 	} else if (wl->status.map.scan) { /* wl scan */
 		if (bt_linfo->status.map.inq_pag)
@@ -5883,7 +5884,6 @@ static void rtw89_tx_time_iter(void *data, struct ieee80211_sta *sta)
 static void _set_wl_tx_limit(struct rtw89_dev *rtwdev)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_cx *cx = &btc->cx;
 	struct rtw89_btc_dm *dm = &btc->dm;
 	struct rtw89_btc_wl_info *wl = &cx->wl;
@@ -5894,19 +5894,14 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev)
 	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
 	struct rtw89_txtime_data data = {.rtwdev = rtwdev};
 	bool reenable = false;
-	u8 igno_bt, tx_retry;
+	u8 tx_retry;
 	u32 tx_time;
 	u16 enable;
 
 	if (btc->manual_ctrl)
 		return;
 
-	if (ver->fcxctrl == 7)
-		igno_bt = btc->ctrl.ctrl_v7.igno_bt;
-	else
-		igno_bt = btc->ctrl.ctrl.igno_bt;
-
-	if (btc->dm.freerun || igno_bt || b->link_cnt.now == 0 ||
+	if (btc->dm.freerun || btc->ctrl.igno_bt || b->link_cnt.now == 0 ||
 	    dm->tdd_bind.rf_band == BIT(RTW89_BAND_5G) ||
 	    wl_rinfo->link_mode == BTC_WLINK_NOLINK) {
 		enable = 0;
@@ -6216,7 +6211,7 @@ static void _action_wl_scan(struct rtw89_dev *rtwdev)
 	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
 
 	if (btc->cx.state_map != BTC_WLINKING &&
-	    RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) {
+	    btc->ctrl.wl_ctrl_info.fw_scan) {
 		_action_wl_25g_mcc(rtwdev);
 		rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], Scan offload!\n");
 	} else if (rtwdev->dbcc_en) {
@@ -7124,11 +7119,13 @@ void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work)
 
 	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__);
 	dm->cnt_notify[BTC_NCNT_TIMER]++;
-	if (wl->status.map._4way)
-		wl->status.map._4way = false;
-	if (wl->status.map.connecting)
-		wl->status.map.connecting = false;
-
+	if (wl->status.map.handshake ||
+	    wl->status.map._4way ||
+	    wl->status.map.dhcp) {
+		wl->status.map._4way = 0;
+		wl->status.map.dhcp = 0;
+		wl->status.map.handshake = 0;
+	}
 	_run_coex(rtwdev, BTC_RSN_ACT1_WORK);
 }
 
@@ -7616,32 +7613,53 @@ static void _set_coex_binding(struct rtw89_btc *btc)
 	dm->ost_info.bt_enable_state = dm->bt_only ? _bind_is_btonly : val;
 }
 
+static void _update_run_ctrl_info(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_btc *btc = &rtwdev->btc;
+	struct rtw89_btc_dm *dm = &btc->dm;
+	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+	struct rtw89_btc_ctrl ctrl = btc->ctrl;
+	struct rtw89_fbtc_wl_ctrl_info *wl_ctrl_info = &ctrl.wl_ctrl_info;
+	u8 i;
+
+	ctrl.wl_only = dm->wl_only;
+	ctrl.bt_only = dm->bt_only;
+	ctrl.ntfy_type = dm->run_reason;
+
+	wl_ctrl_info->smap_val = wl->status.val;
+	wl_ctrl_info->rfk_state = wl->rfk_info.state;
+	wl_ctrl_info->rfk_type = wl->rfk_info.type;
+	wl_ctrl_info->client_pstdma_on =  dm->client_ps_tdma_on;
+	wl_ctrl_info->fw_scan = wl->scan_info.fw_scan;
+
+	for (i = RTW89_MAC_0; i <= RTW89_MAC_1; i++) {
+		wl_ctrl_info->rf_band_map[i] = wl->rf_band_map[i];
+		wl_ctrl_info->rf_ch[i] = wl->rf_ch_info[i].center_ch;
+	}
+
+	if (!memcmp(&btc->ctrl, &ctrl, sizeof(struct rtw89_btc_ctrl))) {
+		memcpy(&btc->ctrl, &ctrl, sizeof(struct rtw89_btc_ctrl));
+		if (btc->ver->fcxctrl >= 9)
+			_fw_set_drv_info(rtwdev, CXDRVINFO_CTRL);
+	}
+}
+
 static
 void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
 	struct rtw89_btc_cx *cx = &btc->cx;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
 	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
 	u8 mode = btc->dm.tdd_bind.wl_link_mode;
 	u8 mode_v0 = wl->role_info.link_mode_v0;
-	u8 igno_bt, always_freerun;
 
 	lockdep_assert_wiphy(rtwdev->hw->wiphy);
 
 	dm->run_reason = reason;
 	_update_dm_step(rtwdev, reason);
 
-	if (ver->fcxctrl == 7) {
-		igno_bt = btc->ctrl.ctrl_v7.igno_bt;
-		always_freerun = btc->ctrl.ctrl_v7.always_freerun;
-	} else {
-		igno_bt = btc->ctrl.ctrl.igno_bt;
-		always_freerun = btc->ctrl.ctrl.always_freerun;
-	}
-
 	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): reason=%d, mode=%d\n",
 		    __func__, reason, mode);
 	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): wl_only=%d, bt_only=%d\n",
@@ -7655,15 +7673,6 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
 		return;
 	}
 
-	if (igno_bt &&
-	    (reason == BTC_RSN_UPDATE_BT_INFO ||
-	     reason == BTC_RSN_UPDATE_BT_SCBD)) {
-		rtw89_debug(rtwdev, RTW89_DBG_BTC,
-			    "[BTC], %s(): return for Stop Coex DM!!\n",
-			    __func__);
-		return;
-	}
-
 	if (!wl->status.map.init_ok) {
 		rtw89_debug(rtwdev, RTW89_DBG_BTC,
 			    "[BTC], %s(): return for WL init fail!!\n",
@@ -7690,6 +7699,8 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
 		}
 	}
 
+	_update_run_ctrl_info(rtwdev);
+
 	if (reason == BTC_RSN_NTFY_INIT || reason == BTC_RSN_NTFY_RADIO_STATE)
 		_update_bt_scbd(rtwdev, false);
 
@@ -7697,28 +7708,24 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
 	dm->cnt_dm[BTC_DCNT_RUN]++;
 	dm->fddt_train = BTC_FDDT_DISABLE;
 	bt->scan_rx_low_pri = false;
-	igno_bt = false;
 
 	_set_coex_binding(btc);
 	_update_btc_state_map(rtwdev);
 
 	dm->freerun_chk = _check_freerun(rtwdev); /* check if meet freerun */
 
-	if (always_freerun) {
+	if (btc->ctrl.always_freerun) {
 		_action_freerun(rtwdev);
-		igno_bt = true;
 		goto exit;
 	}
 
 	if (dm->wl_only) {
 		_action_wl_only(rtwdev);
-		igno_bt = true;
 		goto exit;
 	}
 
 	if (wl->status.map.rf_off || dm->bt_only || wl->status.map.lps) {
 		_action_wl_off(rtwdev, mode);
-		igno_bt = true;
 		goto exit;
 	}
 
@@ -7827,10 +7834,6 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
 
 exit:
 	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): exit\n", __func__);
-	if (ver->fcxctrl == 7)
-		btc->ctrl.ctrl_v7.igno_bt = igno_bt;
-	else
-		btc->ctrl.ctrl.igno_bt = igno_bt;
 	_action_common(rtwdev);
 }
 
@@ -7839,7 +7842,6 @@ void rtw89_btc_init(struct rtw89_dev *rtwdev)
 	const struct rtw89_chip_info *chip = rtwdev->chip;
 	struct rtw89_btc *btc = &rtwdev->btc;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	const struct rtw89_btc_ver *ver = btc->ver;
 
 	rtw89_debug(rtwdev, RTW89_DBG_BTC,
 		    "[BTC], %s(): Init %s !!\n", __func__,
@@ -7849,10 +7851,6 @@ void rtw89_btc_init(struct rtw89_dev *rtwdev)
 
 	btc->dm.run_reason = BTC_RSN_NONE;
 	btc->dm.run_action = BTC_ACT_NONE;
-	if (ver->fcxctrl >= 7)
-		btc->ctrl.ctrl_v7.igno_bt = true;
-	else
-		btc->ctrl.ctrl.igno_bt = true;
 	wl->status.map.init_ok = true;
 }
 
@@ -7937,7 +7935,6 @@ void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode)
 	struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
 	const struct rtw89_chip_info *chip = rtwdev->chip;
-	const struct rtw89_btc_ver *ver = btc->ver;
 
 	rtw89_debug(rtwdev, RTW89_DBG_BTC,
 		    "[BTC], %s(): mode=%d\n", __func__, mode);
@@ -7948,10 +7945,7 @@ void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode)
 	dm->bt_only = mode == BTC_MODE_BT ? 1 : 0;
 	wl->status.map.rf_off = mode == BTC_MODE_WLOFF ? 1 : 0;
 	dm->vid = rtwdev->custid;
-	if (ver->fcxctrl >= 7)
-		btc->ctrl.ctrl_v7.always_freerun = mode == BTC_MODE_COTX;
-	else
-		btc->ctrl.ctrl.always_freerun = mode == BTC_MODE_COTX;
+	btc->ctrl.always_freerun = mode == BTC_MODE_COTX;
 
 	if (!wl->status.map.init_ok) {
 		rtw89_debug(rtwdev, RTW89_DBG_BTC,
@@ -8020,6 +8014,9 @@ void rtw89_btc_ntfy_scan_start(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band)
 		_fw_set_drv_info(rtwdev, CXDRVINFO_DBCC);
 	}
 
+	if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw))
+		btc->ctrl.wl_ctrl_info.fw_scan = 1;
+
 	_run_coex(rtwdev, BTC_RSN_NTFY_SCAN_START);
 }
 
@@ -8086,14 +8083,18 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev,
 		cnt = ++wl->wcnt[BTC_WCNT_DHCP];
 		rtw89_debug(rtwdev, RTW89_DBG_BTC,
 			    "[BTC], %s(): DHCP cnt=%d\n", __func__, cnt);
-		wl->status.map.connecting = true;
+		wl->status.map.handshake = 0;
+		wl->status.map._4way = 0;
+		wl->status.map.dhcp = 1;
 		delay_work = true;
 		break;
 	case PACKET_EAPOL:
 		cnt = ++wl->wcnt[BTC_WCNT_EAPOL];
 		rtw89_debug(rtwdev, RTW89_DBG_BTC,
 			    "[BTC], %s(): EAPOL cnt=%d\n", __func__, cnt);
-		wl->status.map._4way = true;
+		wl->status.map.handshake = 0;
+		wl->status.map._4way = 1;
+		wl->status.map.dhcp = 0;
 		delay_work = true;
 		if (hfp->exist || hid->exist)
 			delay /= 2;
@@ -8103,7 +8104,7 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev,
 		rtw89_debug(rtwdev, RTW89_DBG_BTC,
 			    "[BTC], %s(): EAPOL_End cnt=%d\n",
 			    __func__, cnt);
-		wl->status.map._4way = false;
+		wl->status.map._4way = 0;
 		wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work);
 		break;
 	case PACKET_ARP:
@@ -8435,10 +8436,16 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev,
 	    wlinfo->connected == MLME_NO_LINK)
 		btc->dm.leak_ap = 0;
 
-	if (state == BTC_ROLE_MSTS_STA_CONN_START)
-		wl->status.map.connecting = 1;
-	else
-		wl->status.map.connecting = 0;
+	if (state == BTC_ROLE_MSTS_STA_CONN_START) {
+		wl->status.map.transacting = 1;
+		wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work);
+		wiphy_delayed_work_queue(rtwdev->hw->wiphy,
+					 &rtwdev->coex_act1_work,
+					 RTW89_COEX_ACT1_WORK_PERIOD);
+	} else {
+		wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work);
+		wl->status.map.transacting = 0;
+	}
 
 	if (state == BTC_ROLE_MSTS_STA_DIS_CONN ||
 	    state == BTC_ROLE_MSTS_STA_CONN_END)
@@ -9116,8 +9123,8 @@ static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 		       wl->scan_info.band[RTW89_PHY_0], wl->scan_info.phy_map);
 
 	p += scnprintf(p, end - p,
-		       "connecting:%s, roam:%s, 4way:%s, init_ok:%s\n",
-		       wl->status.map.connecting ? "Y" : "N",
+		       "handshake:%s, roam:%s, 4way:%s, init_ok:%s\n",
+		       wl->status.map.handshake ? "Y" : "N",
 		       wl->status.map.roaming ?  "Y" : "N",
 		       wl->status.map._4way ? "Y" : "N",
 		       wl->status.map.init_ok ? "Y" : "N");
@@ -9718,12 +9725,10 @@ static int _show_dm_step(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_dm *dm = &btc->dm;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
 	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
 	char *p = buf, *end = buf + bufsz;
-	u8 igno_bt;
 
 	if (!(dm->coex_info_map & BTC_COEX_INFO_DM))
 		return 0;
@@ -9744,14 +9749,9 @@ static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 
 	p += _show_dm_step(rtwdev, p, end - p);
 
-	if (ver->fcxctrl == 7)
-		igno_bt = btc->ctrl.ctrl_v7.igno_bt;
-	else
-		igno_bt = btc->ctrl.ctrl.igno_bt;
-
 	p += scnprintf(p, end - p,
 		       " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ",
-		       "[dm_flag]", dm->wl_only, dm->bt_only, igno_bt,
+		       "[dm_flag]", dm->wl_only, dm->bt_only, btc->ctrl.igno_bt,
 		       dm->freerun, btc->btc_ctrl_lps, dm->wl_mimo_ps);
 
 	p += scnprintf(p, end - p, "leak_ap:%d, fw_offload:%s%s\n",
@@ -10742,7 +10742,7 @@ static int _show_fbtc_step_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 			if (ver->fcxctrl == 7 || ver->fcxctrl == 1)
 				trace_step = 50;
 			else
-				trace_step = btc->ctrl.ctrl.trace_step;
+				trace_step = btc->ctrl.trace_step;
 
 			n_start = pos_old;
 			if (pos_new >=  pos_old)
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index ded788534651..0b20ee7d7a28 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -1531,19 +1531,20 @@ enum rtw89_tfc_dir {
 struct rtw89_btc_wl_smap {
 	u32 busy: 1;
 	u32 scan: 1;
-	u32 connecting: 1;
+	u32 dhcp: 1;
 	u32 roaming: 1;
-	u32 dbccing: 1;
+	u32 transacting: 1;
 	u32 _4way: 1;
+	u32 handshake: 1;
 	u32 rf_off: 1;
-	u32 lps: 2;
-	u32 ips: 1;
-	u32 init_ok: 1;
-	u32 traffic_dir : 2;
 	u32 rf_off_pre: 1;
+	u32 ips: 1;
+	u32 lps: 2;
 	u32 lps_pre: 2;
 	u32 lps_exiting: 1;
 	u32 emlsr: 1;
+	u32 init_ok: 1;
+	u32 traffic_dir : 2;
 };
 
 enum rtw89_tfc_interval {
@@ -1725,7 +1726,9 @@ struct rtw89_btc_u8_sta_chg {
 struct rtw89_btc_wl_scan_info {
 	u8 band[RTW89_PHY_NUM];
 	u8 phy_map;
-	u8 rsvd;
+	u8 hw_band_map;
+	u8 type;
+	u8 fw_scan;
 };
 
 struct rtw89_btc_wl_dbcc_info {
@@ -3458,6 +3461,7 @@ struct rtw89_btc_dm {
 	u8 run_action;
 	u8 wl_tx_pwr_phy_map;
 	u8 vid;
+	u8 client_ps_tdma_on;
 
 	u8 wl_pre_agc: 2;
 	u8 wl_lna2: 1;
@@ -3477,13 +3481,38 @@ struct rtw89_btc_dm {
 	bool pre_agc_chg;
 };
 
+struct rtw89_fbtc_wl_ctrl_info {
+	u8 rf_band_map[RTW89_MAC_NUM];
+	u8 rf_ch[RTW89_MAC_NUM];
+
+	u8 client_pstdma_on;
+	u8 fw_scan;
+	u8 rfk_state;
+	u8 rfk_type;
+
+	u32 smap_val;
+};
+
 struct rtw89_btc_ctrl {
+	u32 manual;
+	u32 igno_bt: 1;
+	u32 always_freerun: 1;
+	u32 trace_step: 16;
+
+	u8 wl_only;
+	u8 bt_only;
+
+	u8 ntfy_type;
+	struct rtw89_fbtc_wl_ctrl_info wl_ctrl_info;
+};
+
+struct rtw89_btc_ctrl_v0 {
 	u32 manual: 1;
 	u32 igno_bt: 1;
 	u32 always_freerun: 1;
 	u32 trace_step: 16;
 	u32 rsvd: 12;
-};
+} __packed;
 
 struct rtw89_btc_ctrl_v7 {
 	u8 manual;
@@ -3492,10 +3521,31 @@ struct rtw89_btc_ctrl_v7 {
 	u8 rsvd;
 } __packed;
 
-union rtw89_btc_ctrl_list {
-	struct rtw89_btc_ctrl ctrl;
-	struct rtw89_btc_ctrl_v7 ctrl_v7; /* ver 8, 9 is the same */
-};
+struct rtw89_fbtc_wl_ctrl_info_v9 {
+	u8 rf_band_map[RTW89_MAC_NUM];
+	u8 rf_ch[RTW89_MAC_NUM];
+
+	u8 client_pstdma_on;
+	u8 fw_scan;
+	u8 rfk_state;
+	u8 rfk_type;
+
+	__le32 smap_val;
+} __packed;
+
+struct rtw89_btc_ctrl_v9 {
+	u8 manual;
+	u8 always_freerun;
+	u8 wl_only;
+	u8 bt_only;
+
+	u8 ntfy_type;
+	u8 rsvd0;
+	u8 rsvd1;
+	u8 rsvd2;
+
+	struct rtw89_fbtc_wl_ctrl_info_v9 wl_ctrl_info;
+} __packed;
 
 struct rtw89_btc_dbg {
 	/* cmd "rb" */
@@ -3701,7 +3751,7 @@ struct rtw89_btc {
 
 	struct rtw89_btc_cx cx;
 	struct rtw89_btc_dm dm;
-	union rtw89_btc_ctrl_list ctrl;
+	struct rtw89_btc_ctrl ctrl;
 	union rtw89_btc_module_info mdinfo;
 	struct rtw89_btc_btf_fwinfo fwinfo;
 	struct rtw89_btc_dbg dbg;
diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index e42dc5707576..8b41991963e8 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -3900,17 +3900,13 @@ static ssize_t rtw89_debug_priv_btc_manual_set(struct rtw89_dev *rtwdev,
 					       const char *buf, size_t count)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	const struct rtw89_btc_ver *ver = btc->ver;
 	int ret;
 
 	ret = kstrtobool(buf, &btc->manual_ctrl);
 	if (ret)
 		return ret;
 
-	if (ver->fcxctrl == 7)
-		btc->ctrl.ctrl_v7.manual = btc->manual_ctrl;
-	else
-		btc->ctrl.ctrl.manual = btc->manual_ctrl;
+	btc->ctrl.manual = btc->manual_ctrl;
 
 	return count;
 }
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 41c033d2ae7b..59e51e67d51b 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -6441,7 +6441,7 @@ int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
 	const struct rtw89_btc_ver *ver = btc->ver;
-	struct rtw89_btc_ctrl *ctrl = &btc->ctrl.ctrl;
+	struct rtw89_btc_ctrl *ctrl = &btc->ctrl;
 	struct sk_buff *skb;
 	u8 *cmd;
 	int ret;
@@ -6484,7 +6484,7 @@ int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type)
 int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_ctrl_v7 *ctrl = &btc->ctrl.ctrl_v7;
+	struct rtw89_btc_ctrl *ctrl = &btc->ctrl;
 	struct rtw89_h2c_cxctrl_v7 *h2c;
 	u32 len = sizeof(*h2c);
 	struct sk_buff *skb;
@@ -6501,7 +6501,64 @@ int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type)
 	h2c->hdr.type = type;
 	h2c->hdr.ver = btc->ver->fcxctrl;
 	h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7;
-	h2c->ctrl = *ctrl;
+
+	h2c->ctrl.manual = ctrl->manual;
+	h2c->ctrl.igno_bt = ctrl->igno_bt;
+	h2c->ctrl.always_freerun = ctrl->always_freerun;
+
+	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+			      H2C_CAT_OUTSRC, BTFC_SET,
+			      SET_DRV_INFO, 0, 0, len);
+
+	ret = rtw89_h2c_tx(rtwdev, skb, false);
+	if (ret) {
+		rtw89_err(rtwdev, "failed to send h2c\n");
+		goto fail;
+	}
+
+	return 0;
+fail:
+	dev_kfree_skb_any(skb);
+
+	return ret;
+}
+
+int rtw89_fw_h2c_cxdrv_ctrl_v9(struct rtw89_dev *rtwdev, u8 type)
+{
+	struct rtw89_btc *btc = &rtwdev->btc;
+	struct rtw89_btc_ctrl *ctrl = &btc->ctrl;
+	struct rtw89_fbtc_wl_ctrl_info_v9 *cinfo;
+	struct rtw89_h2c_cxctrl_v9 *h2c;
+	u32 len = sizeof(*h2c);
+	struct sk_buff *skb;
+	int ret;
+
+	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+	if (!skb) {
+		rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_ctrl_v7\n");
+		return -ENOMEM;
+	}
+	skb_put(skb, len);
+	h2c = (struct rtw89_h2c_cxctrl_v9 *)skb->data;
+	cinfo = &h2c->ctrl.wl_ctrl_info;
+
+	h2c->hdr.type = type;
+	h2c->hdr.ver = btc->ver->fcxctrl;
+	h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7;
+
+	h2c->ctrl.manual = ctrl->manual;
+	h2c->ctrl.always_freerun = ctrl->always_freerun;
+	h2c->ctrl.wl_only = ctrl->wl_only;
+	h2c->ctrl.bt_only = ctrl->bt_only;
+	h2c->ctrl.ntfy_type = ctrl->ntfy_type;
+	memcpy(cinfo->rf_band_map, ctrl->wl_ctrl_info.rf_band_map,
+	       sizeof(cinfo->rf_band_map));
+	memcpy(cinfo->rf_ch, ctrl->wl_ctrl_info.rf_ch, sizeof(cinfo->rf_ch));
+	cinfo->client_pstdma_on = ctrl->wl_ctrl_info.client_pstdma_on;
+	cinfo->fw_scan = ctrl->wl_ctrl_info.fw_scan;
+	cinfo->rfk_state = ctrl->wl_ctrl_info.rfk_state;
+	cinfo->rfk_type = ctrl->wl_ctrl_info.rfk_type;
+	cinfo->smap_val = cpu_to_le32(ctrl->wl_ctrl_info.smap_val);
 
 	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
 			      H2C_CAT_OUTSRC, BTFC_SET,
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index 1e4267b22b36..72f56d32e3b0 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -2430,6 +2430,11 @@ struct rtw89_h2c_cxctrl_v7 {
 	struct rtw89_btc_ctrl_v7 ctrl;
 } __packed;
 
+struct rtw89_h2c_cxctrl_v9 {
+	struct rtw89_h2c_cxhdr_v7 hdr;
+	struct rtw89_btc_ctrl_v9 ctrl;
+} __packed;
+
 #define H2C_LEN_CXDRVHDR sizeof(struct rtw89_h2c_cxhdr)
 #define H2C_LEN_CXDRVHDR_V7 sizeof(struct rtw89_h2c_cxhdr_v7)
 
@@ -5382,6 +5387,7 @@ int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type);
+int rtw89_fw_h2c_cxdrv_ctrl_v9(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_trx_v7(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_trx_v9(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev, u8 type);
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 01/11] wifi: rtw89: coex: Add Wi-Fi role info version 10
From: Ping-Ke Shih @ 2026-07-12  3:04 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-1-pkshih@realtek.com>

From: Ching-Te Ku <ku920601@realtek.com>

Because the new generation Bluetooth will able to work on 5/6GHz band,
it will suffer 5/6GHz Wi-Fi, the mechanism need to cover more scenario
with different Wi-Fi/Bluetooth combination.

Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/coex.c     | 2445 +++++++----------
 drivers/net/wireless/realtek/rtw89/coex.h     |   34 +
 drivers/net/wireless/realtek/rtw89/core.h     |  153 +-
 drivers/net/wireless/realtek/rtw89/fw.c       |  392 ++-
 drivers/net/wireless/realtek/rtw89/fw.h       |   48 +-
 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, 1391 insertions(+), 1688 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 989344e93ab7..c02de0b4f7bf 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -436,11 +436,6 @@ union rtw89_fbtc_set_mon_reg {
 	struct rtw89_btc_btf_set_mon_reg_v7 v7;
 } __packed;
 
-struct _wl_rinfo_now {
-	u8 link_mode;
-	u32 dbcc_2g_phy: 2;
-};
-
 enum btc_btf_set_cx_policy {
 	CXPOLICY_TDMA = 0x0,
 	CXPOLICY_SLOT = 0x1,
@@ -570,9 +565,21 @@ enum btc_cx_poicy_type {
 	/* TDMA off + pri: WL > BT, Block-BT*/
 	BTC_CXP_OFF_WL2 = (BTC_CXP_OFF << 8) | 12,
 
-	/* TDMA off+Bcn-Protect + pri: WL_Hi-Tx > BT_Hi_Rx, BT_Hi > WL > BT_Lo*/
+	/* TDMA off+Bcn-Protect + pri: BT_Hi > WL > BT_Lo */
 	BTC_CXP_OFFB_BWB0 = (BTC_CXP_OFFB << 8) | 0,
 
+	/* TDMA off+Bcn-Protect + pri: BT_Hi_Tx = WL_Hi-Tx > BT_Hi_Rx, BT_Hi > WL > BT_Lo */
+	BTC_CXP_OFFB_BWB1 = (BTC_CXP_OFFB << 8) | 1,
+
+	/* TDMA off+Bcn-Protect + pri: WL_Hi-Tx > BT, BT_Hi > other-WL > BT_Lo */
+	BTC_CXP_OFFB_BWB2 = (BTC_CXP_OFFB << 8) | 2,
+
+	/* TDMA off+Bcn-Protect + pri: WL_Hi-Tx = BT*/
+	BTC_CXP_OFFB_BWB3 = (BTC_CXP_OFFB << 8) | 3,
+
+	/* TDMA off+Bcn-Protect + pri: BT > WL*/
+	BTC_CXP_OFFB_BWB4 = (BTC_CXP_OFFB << 8) | 4,
+
 	/* TDMA off + Ext-Ctrl + pri: default */
 	BTC_CXP_OFFE_DEF = (BTC_CXP_OFFE << 8) | 0,
 
@@ -752,19 +759,31 @@ enum btc_w2b_scoreboard {
 	BTC_WSCB_ALL = GENMASK(23, 0),
 };
 
-enum btc_wl_link_mode {
+enum btc_wl_link_mode_v0 {
+	BTC_WLINK_V0_NOLINK = 0x0,
+	BTC_WLINK_V0_2G_STA,
+	BTC_WLINK_V0_2G_AP,
+	BTC_WLINK_V0_2G_GO,
+	BTC_WLINK_V0_2G_GC,
+	BTC_WLINK_V0_2G_SCC,
+	BTC_WLINK_V0_2G_MCC,
+	BTC_WLINK_V0_25G_MCC,
+	BTC_WLINK_V0_25G_DBCC,
+	BTC_WLINK_V0_5G,
+	BTC_WLINK_V0_2G_NAN,
+	BTC_WLINK_V0_OTHER,
+	BTC_WLINK_V0_MAX
+};
+
+enum btc_wl_link_mode_v1 {
 	BTC_WLINK_NOLINK = 0x0,
-	BTC_WLINK_2G_STA,
-	BTC_WLINK_2G_AP,
-	BTC_WLINK_2G_GO,
-	BTC_WLINK_2G_GC,
-	BTC_WLINK_2G_SCC,
-	BTC_WLINK_2G_MCC,
-	BTC_WLINK_25G_MCC,
-	BTC_WLINK_25G_DBCC,
-	BTC_WLINK_5G,
-	BTC_WLINK_2G_NAN,
-	BTC_WLINK_OTHER,
+	BTC_WLINK_STA,
+	BTC_WLINK_AP,
+	BTC_WLINK_GO,
+	BTC_WLINK_GC,
+	BTC_WLINK_SCC,
+	BTC_WLINK_SB_MCC,
+	BTC_WLINK_DB_MCC, /* 2+0/0+2 Dual-RF-band MCC */
 	BTC_WLINK_MAX
 };
 
@@ -774,16 +793,13 @@ static const char *id_to_linkmode(u8 id)
 {
 	switch (id) {
 	CASE_BTC_WL_LINK_MODE(NOLINK);
-	CASE_BTC_WL_LINK_MODE(2G_STA);
-	CASE_BTC_WL_LINK_MODE(2G_AP);
-	CASE_BTC_WL_LINK_MODE(2G_GO);
-	CASE_BTC_WL_LINK_MODE(2G_GC);
-	CASE_BTC_WL_LINK_MODE(2G_SCC);
-	CASE_BTC_WL_LINK_MODE(2G_MCC);
-	CASE_BTC_WL_LINK_MODE(25G_MCC);
-	CASE_BTC_WL_LINK_MODE(25G_DBCC);
-	CASE_BTC_WL_LINK_MODE(5G);
-	CASE_BTC_WL_LINK_MODE(OTHER);
+	CASE_BTC_WL_LINK_MODE(STA);
+	CASE_BTC_WL_LINK_MODE(AP);
+	CASE_BTC_WL_LINK_MODE(GO);
+	CASE_BTC_WL_LINK_MODE(GC);
+	CASE_BTC_WL_LINK_MODE(SCC);
+	CASE_BTC_WL_LINK_MODE(SB_MCC);
+	CASE_BTC_WL_LINK_MODE(DB_MCC);
 	default:
 		return "unknown";
 	}
@@ -969,7 +985,7 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type)
 	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
 	struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
 	struct rtw89_btc_wl_link_info *wl_linfo;
-	u8 i;
+	u8 i, j;
 
 	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s\n", __func__);
 
@@ -990,12 +1006,11 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type)
 	if (type & BTC_RESET_DM) {
 		memset(&btc->dm, 0, sizeof(btc->dm));
 		memset(bt_linfo->rssi_state, 0, sizeof(bt_linfo->rssi_state));
-		for (i = 0; i < RTW89_PORT_NUM; i++) {
-			if (btc->ver->fwlrole == 8)
-				wl_linfo = &wl->rlink_info[i][0];
-			else
-				wl_linfo = &wl->link_info[i];
-			memset(wl_linfo->rssi_state, 0, sizeof(wl_linfo->rssi_state));
+		for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) {
+			for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) {
+				wl_linfo = &wl->rlink_info[i][j];
+				memset(wl_linfo->rssi_state, 0, sizeof(wl_linfo->rssi_state));
+			}
 		}
 
 		/* set the slot_now table to original */
@@ -2847,6 +2862,8 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index)
 			rtw89_fw_h2c_cxdrv_role_v7(rtwdev, index);
 		else if (ver->fwlrole == 8)
 			rtw89_fw_h2c_cxdrv_role_v8(rtwdev, index);
+		else if (ver->fwlrole == 10)
+			rtw89_fw_h2c_cxdrv_role_v10(rtwdev, index);
 		break;
 	case CXDRVINFO_CTRL:
 		if (ver->drvinfo_ver != 0)
@@ -3396,29 +3413,17 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev)
 	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_dm *dm = &btc->dm;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+	struct rtw89_btc_wl_role_info *r = &wl->role_info;
 	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
 	struct rtw89_btc_bt_link_info *b = &bt->link_info;
 	struct rtw89_btc_wl_smap *wl_smap = &wl->status.map;
 	struct rtw89_btc_rf_trx_para_v9 para;
-	u8 lv, link_mode = 0, i, dbcc_2g_phy = 0;
-	u8 ul_para_num, dl_para_num;
-	u8 rf_band = RTW89_BAND_2G;
-	u8 bid = BTC_BT_1ST;
-	u32 wl_stb_chg = 0;
-
-	if (ver->fwlrole == 0) {
-		link_mode = wl->role_info.link_mode;
-		for (i = 0; i < RTW89_PHY_NUM; i++) {
-			if (wl->dbcc_info.real_band[i] == RTW89_BAND_2G)
-				dbcc_2g_phy = i;
-		}
-	} else if (ver->fwlrole == 1) {
-		link_mode = wl->role_info_v1.link_mode;
-		dbcc_2g_phy = wl->role_info_v1.dbcc_2g_phy;
-	} else if (ver->fwlrole == 2) {
-		link_mode = wl->role_info_v2.link_mode;
-		dbcc_2g_phy = wl->role_info_v2.dbcc_2g_phy;
-	}
+	u8 bmode = dm->tdd_bind.wl_link_mode;
+	u8 rf_band = dm->tdd_bind.rf_band;
+	u8 i, ul_para_num, dl_para_num;
+	u8 mode = BTC_WLINK_NOLINK;
+	u8 bid = BTC_BT_1ST, lv;
+	u32 wl_stb_chg;
 
 	if (ver->fcxtrx == 9 && chip->rf_para_ulink_v9) {
 		ul_para_num = chip->rf_para_ulink_num_v9;
@@ -3434,6 +3439,19 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev)
 		goto next;
 	}
 
+	if (bmode == BTC_WLINK_NOLINK) {
+		return;
+	} else if (btc->ver->fwlrole == 10) {
+		if (rf_band == RTW89_BAND_5G) {
+			mode = BTC_WLINK_V0_5G;
+		} else if (rf_band == RTW89_BAND_2G &&
+			   bmode == BTC_WLINK_STA) {
+			mode = BTC_WLINK_V0_2G_STA;
+		}
+	} else {
+		mode = r->link_mode_v0;
+	}
+
 	/* decide trx_para_level */
 	if (btc->ant_type == BTC_ANT_SHARED) {
 		/* fix LNA2 + TIA gain not change by GNT_BT */
@@ -3446,7 +3464,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev)
 		dm->trx_para_level = 5;
 		/* modify trx_para if WK 2.4G-STA-DL + bt link */
 		if (b->link_cnt.now != 0 &&
-		    link_mode == BTC_WLINK_2G_STA &&
+		    mode == BTC_WLINK_V0_2G_STA &&
 		    wl->status.map.traffic_dir & BIT(RTW89_TFC_UL)) { /* uplink */
 			if (wl->rssi_level == 4 && bt->rssi_level > 2)
 				dm->trx_para_level = 6;
@@ -3504,10 +3522,8 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev)
 
 next:
 	if (!bt->enable.now || dm->wl_only || wl_smap->rf_off ||
-	    wl_smap->lps == BTC_LPS_RF_OFF ||
-	    link_mode == BTC_WLINK_5G ||
-	    link_mode == BTC_WLINK_NOLINK ||
-	    (rtwdev->dbcc_en && dbcc_2g_phy != RTW89_PHY_1))
+	    wl_smap->lps == BTC_LPS_RF_OFF || mode == BTC_WLINK_V0_5G ||
+	    (rtwdev->dbcc_en && r->dbcc_2g_phy != RTW89_PHY_1))
 		wl_stb_chg = 0;
 	else
 		wl_stb_chg = 1;
@@ -3544,282 +3560,372 @@ static void _update_btc_state_map(struct rtw89_dev *rtwdev)
 	}
 }
 
-static void _set_bt_afh_info_v0(struct rtw89_dev *rtwdev)
+static u8 _get_wl_guard_bw(struct rtw89_dev *rtwdev, u8 index)
 {
-	const struct rtw89_chip_info *chip = rtwdev->chip;
-	struct rtw89_btc *btc = &rtwdev->btc;
-	const struct rtw89_btc_ver *ver = btc->ver;
-	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
-	struct rtw89_btc_bt_link_info *b = &bt->link_info;
-	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
-	struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1;
-	struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2;
-	struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8;
-	struct rtw89_btc_wl_active_role *r;
-	struct rtw89_btc_wl_active_role_v1 *r1;
-	struct rtw89_btc_wl_active_role_v2 *r2;
-	struct rtw89_btc_wl_active_role_v7 *r7;
-	struct rtw89_btc_wl_rlink *rlink;
-	u8 en = 0, i, ch = 0, bw = 0;
-	u8 mode, connect_cnt;
-
-	if (btc->manual_ctrl || wl->status.map.scan)
-		return;
-
-	if (ver->fwlrole == 0) {
-		mode = wl_rinfo->link_mode;
-		connect_cnt = wl_rinfo->connect_cnt;
-	} else if (ver->fwlrole == 1) {
-		mode = wl_rinfo_v1->link_mode;
-		connect_cnt = wl_rinfo_v1->connect_cnt;
-	} else if (ver->fwlrole == 2) {
-		mode = wl_rinfo_v2->link_mode;
-		connect_cnt = wl_rinfo_v2->connect_cnt;
-	} else if (ver->fwlrole == 7) {
-		mode = wl_rinfo_v7->link_mode;
-		connect_cnt = wl_rinfo_v7->connect_cnt;
-	} else if (ver->fwlrole == 8) {
-		mode = wl_rinfo_v8->link_mode;
-		connect_cnt = wl_rinfo_v8->connect_cnt;
-	} else {
-		return;
-	}
-
-	if (wl->status.map.rf_off || bt->whql_test ||
-	    mode == BTC_WLINK_NOLINK || mode == BTC_WLINK_5G ||
-	    connect_cnt > BTC_TDMA_WLROLE_MAX) {
-		en = false;
-	} else if (mode == BTC_WLINK_2G_MCC || mode == BTC_WLINK_2G_SCC) {
-		en = true;
-		/* get p2p channel */
-		for (i = 0; i < RTW89_PORT_NUM; i++) {
-			r = &wl_rinfo->active_role[i];
-			r1 = &wl_rinfo_v1->active_role_v1[i];
-			r2 = &wl_rinfo_v2->active_role_v2[i];
-			r7 = &wl_rinfo_v7->active_role[i];
-			rlink = &wl_rinfo_v8->rlink[i][0];
-
-			if (ver->fwlrole == 0 &&
-			    (r->role == RTW89_WIFI_ROLE_P2P_GO ||
-			     r->role == RTW89_WIFI_ROLE_P2P_CLIENT)) {
-				ch = r->ch;
-				bw = r->bw;
-				break;
-			} else if (ver->fwlrole == 1 &&
-				   (r1->role == RTW89_WIFI_ROLE_P2P_GO ||
-				    r1->role == RTW89_WIFI_ROLE_P2P_CLIENT)) {
-				ch = r1->ch;
-				bw = r1->bw;
-				break;
-			} else if (ver->fwlrole == 2 &&
-				   (r2->role == RTW89_WIFI_ROLE_P2P_GO ||
-				    r2->role == RTW89_WIFI_ROLE_P2P_CLIENT)) {
-				ch = r2->ch;
-				bw = r2->bw;
-				break;
-			} else if (ver->fwlrole == 7 &&
-				   (r7->role == RTW89_WIFI_ROLE_P2P_GO ||
-				    r7->role == RTW89_WIFI_ROLE_P2P_CLIENT)) {
-				ch = r7->ch;
-				bw = r7->bw;
-				break;
-			} else if (ver->fwlrole == 8 &&
-				   (rlink->role == RTW89_WIFI_ROLE_P2P_GO ||
-				    rlink->role == RTW89_WIFI_ROLE_P2P_CLIENT)) {
-				ch = rlink->ch;
-				bw = rlink->bw;
-				break;
-			}
-		}
-	} else {
-		en = true;
-		/* get 2g channel  */
-		for (i = 0; i < RTW89_PORT_NUM; i++) {
-			r = &wl_rinfo->active_role[i];
-			r1 = &wl_rinfo_v1->active_role_v1[i];
-			r2 = &wl_rinfo_v2->active_role_v2[i];
-			r7 = &wl_rinfo_v7->active_role[i];
-			rlink = &wl_rinfo_v8->rlink[i][0];
-
-			if (ver->fwlrole == 0 &&
-			    r->connected && r->band == RTW89_BAND_2G) {
-				ch = r->ch;
-				bw = r->bw;
-				break;
-			} else if (ver->fwlrole == 1 &&
-				   r1->connected && r1->band == RTW89_BAND_2G) {
-				ch = r1->ch;
-				bw = r1->bw;
-				break;
-			} else if (ver->fwlrole == 2 &&
-				   r2->connected && r2->band == RTW89_BAND_2G) {
-				ch = r2->ch;
-				bw = r2->bw;
-				break;
-			} else if (ver->fwlrole == 7 &&
-				   r7->connected && r7->band == RTW89_BAND_2G) {
-				ch = r7->ch;
-				bw = r7->bw;
-				break;
-			} else if (ver->fwlrole == 8 &&
-				   rlink->connected && rlink->rf_band == RTW89_BAND_2G) {
-				ch = rlink->ch;
-				bw = rlink->bw;
-				break;
-			}
-		}
-	}
+	struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
+	u8 guard_ch = rtwdev->chip->afh_guard_ch;
+	u8 bw = 0;
 
-	switch (bw) {
+	/* default AFH channel sapn = center-ch +- 6MHz  */
+	switch (index) {
 	case RTW89_CHANNEL_WIDTH_20:
-		bw = 20 + chip->afh_guard_ch * 2;
+		if (dm->freerun || dm->fddt_train)
+			bw = 48;
+		else
+			bw = 20 + guard_ch * 2;
 		break;
 	case RTW89_CHANNEL_WIDTH_40:
-		bw = 40 + chip->afh_guard_ch * 2;
+		bw = 40 + guard_ch * 2;
+		break;
+	case RTW89_CHANNEL_WIDTH_80:
+		bw = 80 + guard_ch * 2;
+		break;
+	case RTW89_CHANNEL_WIDTH_160:
+	case RTW89_CHANNEL_WIDTH_80_80:
+		bw = 160 + guard_ch * 2;
+		break;
+	case RTW89_CHANNEL_WIDTH_320:
+		bw = 320 + guard_ch * 2;
 		break;
 	case RTW89_CHANNEL_WIDTH_5:
-		bw = 5 + chip->afh_guard_ch * 2;
+		bw = 5 + guard_ch * 2;
 		break;
 	case RTW89_CHANNEL_WIDTH_10:
-		bw = 10 + chip->afh_guard_ch * 2;
+		bw = 10 + guard_ch * 2;
 		break;
 	default:
 		bw = 0;
-		en = false; /* turn off AFH info if BW > 40 */
 		break;
 	}
 
-	if (wl->afh_info.en == en &&
-	    wl->afh_info.ch == ch &&
-	    wl->afh_info.bw == bw &&
-	    b->link_cnt.last == b->link_cnt.now) {
-		rtw89_debug(rtwdev, RTW89_DBG_BTC,
-			    "[BTC], %s(): return because no change!\n",
-			    __func__);
+	return bw;
+}
+
+static void _set_wl_ch_map(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_btc *btc = &rtwdev->btc;
+	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+	struct rtw89_btc_wl_afh_info *afh;
+	u16 ch_start, ch_end, i;
+	u8 b_group, b_pos; /* byte-group, bit-position */
+	u8 hwb;
+
+	if (wl->afh_info[RTW89_MAC_0][RTW89_BAND_2G].en &&
+	    wl->afh_info[RTW89_MAC_1][RTW89_BAND_2G].en) {
+		/* select wilder bw if 1+1 */
+		if (wl->afh_info[RTW89_MAC_1][RTW89_BAND_2G].bw >
+		    wl->afh_info[RTW89_MAC_0][RTW89_BAND_2G].bw)
+			hwb = RTW89_MAC_1;
+		else
+			hwb = RTW89_MAC_0;
+	} else if (wl->afh_info[RTW89_MAC_1][RTW89_BAND_2G].en) {
+		hwb = RTW89_MAC_1;
+	} else {
+		hwb = RTW89_MAC_0;
+	}
+
+	afh = &wl->afh_info[hwb][RTW89_BAND_2G];
+
+	/* focus on WL 2.4GHz link */
+	if (!afh->en || afh->band != RTW89_BAND_2G)
 		return;
+
+	memset(wl->ch_map, 0x0, sizeof(wl->ch_map));
+	memset(wl->ch_map_le, 0x0, sizeof(wl->ch_map_le));
+
+	ch_start = afh->ch * 5 + 2407 - afh->bw / 2;
+	ch_end = ch_start + afh->bw;
+
+	for (i = ch_start; i < ch_end; i++) {
+		if (i < 2402 || i > 2480)
+			continue;
+		b_group = (i - 2402) / 8;
+		b_pos = 7 - ((i - 2402) % 8);
+		wl->ch_map[b_group] |= BIT(b_pos);
+
+		if (i % 2)  /* BLE ch = even-freq (ex:2402,2404) */
+			continue;
+
+		b_group = (i - 2402) / 16;
+		b_pos = 7 - (((i - 2402) / 2) % 8);
+		wl->ch_map_le[b_group] |= BIT(b_pos);
 	}
+}
 
-	wl->afh_info.en = en;
-	wl->afh_info.ch = ch;
-	wl->afh_info.bw = bw;
+static u8 _get_w2b_fbd_ch(struct rtw89_dev *rtwdev,
+			  u8 bt_fb_band, u32 wl_vco_freq,
+			  u32 *ch_lo_bound, u32 *ch_up_bound)
+{
+	u32 ch_lo, ch_pup, ch_min, ch_max;
+	u8 out_of_range = 0;
+	s32 val_lo, val_up;
+	u32 vco_lo_in_KHz; /* VCO pulling lower bound */
+	u32 vco_up_in_KHz; /* VCO pulling Upper bound */
+	u32 freq_start;
 
-	_send_fw_cmd(rtwdev, BTFC_SET, SET_BT_WL_CH_INFO, &wl->afh_info, 3);
+	vco_lo_in_KHz = wl_vco_freq - BTC_VCO_GUARD_W2B * 1000; /* KHz */
+	vco_up_in_KHz = wl_vco_freq + BTC_VCO_GUARD_W2B * 1000;
 
-	rtw89_debug(rtwdev, RTW89_DBG_BTC,
-		    "[BTC], %s(): en=%d, ch=%d, bw=%d\n",
-		    __func__, en, ch, bw);
-	wl->wcnt[BTC_WCNT_CH_UPDATE]++;
+	switch (bt_fb_band) {
+	default:
+	case BTC_BT_B2G: /* BT 2GHz forbiddened Channel */
+		val_lo = vco_lo_in_KHz * BTC_LO_2G_DNM / BTC_LO_2G_NMR;
+		val_up = vco_up_in_KHz * BTC_LO_2G_DNM / BTC_LO_2G_NMR;
+		freq_start = BTC_FREQ_B2G * 1000; /* KHz */
+		ch_min = BTC_CH_B2G_MIN;
+		ch_max = BTC_CH_B2G_MAX;
+		break;
+	case BTC_BT_B5G: /* BT 5/6GHz forbiddened Channel */
+		val_lo = vco_lo_in_KHz * BTC_LO_6G_DNM / BTC_LO_6G_NMR;
+		val_up = vco_up_in_KHz * BTC_LO_6G_DNM / BTC_LO_6G_NMR;
+		freq_start = BTC_FREQ_B6G * 1000; /* KHz */
+		ch_min = BTC_CH_B5G_MIN;
+		ch_max = BTC_CH_B5G_MAX;
+		break;
+	}
+
+	/* Search forbiddened Channel (un-process range)*/
+	ch_lo = (val_lo - freq_start) / 1000;
+	if ((val_lo - freq_start) % 1000 != 0) /* Round-UP */
+		ch_lo++;
+
+	ch_pup = (val_up - freq_start) / 1000; /* Round-down */
+
+	/* Search forbiddened Channel */
+	if (ch_lo > ch_max || ch_pup < ch_min) { /* out-of-range -> NA */
+		out_of_range = 1;
+		*ch_lo_bound = 0;
+		*ch_up_bound = 0;
+	}
+
+	if (ch_lo < ch_min)
+		*ch_lo_bound = ch_min;
+	else if (ch_lo <= ch_max)
+		*ch_lo_bound = ch_lo;
+
+	if (ch_pup > ch_max)
+		*ch_up_bound = ch_max;
+	else if (ch_pup >= ch_min)
+		*ch_up_bound = ch_pup;
+
+	if (bt_fb_band == BTC_BT_B5G) { /* BT ch is 8MHz-spacing for 5/6GHz */
+		*ch_lo_bound = *ch_lo_bound / 8; /* low-bound -> Round-down */
+
+		if (*ch_up_bound % 8 != 0) /* Up-bound -> cover wider range */
+			*ch_up_bound = *ch_up_bound / 8 + 1;
+		else
+			*ch_up_bound = *ch_up_bound / 8;
+	}
+
+	return out_of_range;
 }
 
-static void _set_bt_afh_info_v1(struct rtw89_dev *rtwdev)
+static void _set_wl_ch_info(struct rtw89_dev *rtwdev)
+{
+	static const u8 path_hwb[BTC_RF_NUM] = {RTW89_PHY_0, RTW89_PHY_1};
+	struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
+	struct rtw89_btc_cx *cx = &rtwdev->btc.cx;
+	struct rtw89_btc_wl_info *wl = &cx->wl;
+	u32 vco_freq, ch_lo = 0, ch_up = 0;
+	u16 rf_band, freq, f_diff;
+	u8 i, j, ch;
+
+	if (!(rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA)) /* only new-PTA require setup */
+		return;
+
+	for (i = RTW89_PHY_0; i <= RTW89_PHY_1; i++) {
+		if (wl->mlo_info.wmode[i] == RTW89_MR_WMODE_NONE) { /* no-connect */
+			dm->ost_info.rf_center_freq[i] = 0;
+			dm->ost_info.fbd_group_en[i][0] = 0;
+			dm->ost_info.fbd_group_en[i][1] = 0;
+			dm->ost_info.fbd_group_bound[i][0] = 0;
+			dm->ost_info.fbd_group_bound[i][1] = 0;
+			continue;
+		}
+
+		ch = wl->rf_ch_info[i].center_ch;
+		switch (wl->rf_ch_info[i].band) {
+		case RTW89_BAND_2G:
+		default:
+			rf_band = 0;
+			if (ch == 14)
+				freq = BTC_FREQ_W2G_CH14;
+			else
+				freq = BTC_FREQ_W2G + (ch - 1) * 5;
+			vco_freq = freq * 1000 * BTC_LO_2G_NMR / BTC_LO_2G_DNM;
+			break;
+		case RTW89_BAND_5G:
+			rf_band = BIT(15);
+			freq = BTC_FREQ_W5G + (ch - 1) * 5;
+			vco_freq = freq * 1000 * BTC_LO_5G_NMR / BTC_LO_5G_DNM;
+			break;
+		case RTW89_BAND_6G:
+			rf_band = BIT(15);
+			freq = BTC_FREQ_W6G + (ch - 1) * 5;
+			vco_freq = freq * 1000 * BTC_LO_6G_NMR / BTC_LO_6G_DNM;
+			break;
+		}
+
+		dm->ost_info.rf_center_freq[i] = rf_band + freq;
+
+		for (j = BTC_BT_B2G; j <= BTC_BT_B5G; j++) {
+			/* no WL->BT forbidden channel */
+			if (_get_w2b_fbd_ch(rtwdev, j, vco_freq, &ch_lo, &ch_up)) {
+				dm->ost_info.fbd_group_en[i][j] = j << 1;
+				dm->ost_info.fbd_group_bound[i][j] = 0;
+				continue;
+			}
+
+			dm->ost_info.fbd_group_en[i][j] = (j << 1) + 0x1;
+			dm->ost_info.fbd_group_bound[i][j] = (ch_up << 8) + ch_lo;
+		}
+	}
+
+	for (i = BTC_RF_S0; i <= BTC_RF_S1; i++) {
+		for (j = BTC_BT_1ST; j <= BTC_BT_EXT; j++) {
+			/*
+			 * If WL & BT share same ANT/Path, set freq-diff > 2GHz,
+			 * because TDD will always run in 5/6GHz band.
+			 * Otherwise, the frq-diff thres is decided by the RF
+			 * out-of-channel rejection capability.
+			 */
+			if (dm->ant_xmap[i][j])
+				f_diff = 0x7ff; /* 2047MHz */
+			else
+				f_diff = rtwdev->chip->fdd_iso_freq;
+
+			dm->ost_info.freq_diff_thres[path_hwb[i]][j] = f_diff;
+		}
+	}
+}
+
+static void _set_bt_afh_info(struct rtw89_dev *rtwdev)
 {
-	const struct rtw89_chip_info *chip = rtwdev->chip;
 	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8;
-	struct rtw89_btc_wl_afh_info *wl_afh = &wl->afh_info;
-	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
+	struct rtw89_btc_cx *cx = &btc->cx;
+	struct rtw89_btc_wl_info *wl = &cx->wl;
+	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
 	struct rtw89_btc_wl_rlink *rlink;
-	u8 en = 0, ch = 0, bw = 0, buf[3] = {};
-	u8 i, j, link_mode;
+	u8 h2c_func, hwb, bt_link_chg;
+	u8 i, j, sz, bw;
+	u8 buf[4];
 
-	if (btc->manual_ctrl || wl->status.map.scan)
+	if (btc->manual_ctrl || wl->status.map.rf_off ||
+	    (wl->status.val & btc_scanning_map.val))
 		return;
 
-	link_mode = wl_rinfo->link_mode;
+	memset(wl->afh_info, 0, sizeof(wl->afh_info));
 
-	for (i = 0; i < btc->ver->max_role_num; i++) {
-		for (j = RTW89_MAC_0; j < RTW89_MAC_NUM; j++) {
-			if (wl->status.map.rf_off || bt->whql_test ||
-			    link_mode == BTC_WLINK_NOLINK ||
-			    link_mode == BTC_WLINK_5G)
-				break;
+	for (j = RTW89_MAC_0; j < RTW89_MAC_NUM; j++) {
+		/* skip HW-Band if no fdd/fdd bind */
+		if ((!(btc->dm.tdd_bind.wl_hwb_sel & BIT(j))) &&
+		    (!(btc->dm.fdd_bind.wl_hwb_sel & BIT(j))))
+			continue;
 
+		for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) {
 			rlink = &wl_rinfo->rlink[i][j];
 
-			/* Don't care no-connected/non-2G-band role */
+			/* Skip no-active case */
 			if (!rlink->connected || !rlink->active ||
-			    rlink->rf_band != RTW89_BAND_2G)
+			    rlink->rf_band >= RTW89_BAND_NUM)
 				continue;
 
-			en = 1;
-			ch = rlink->ch;
-			bw = rlink->bw;
+			/* In addition to AP/P2P, skip if same-rf-band*/
+			if (wl->afh_info[j][rlink->rf_band].en &&
+			    (rlink->role != RTW89_WIFI_ROLE_AP &&
+			     rlink->role != RTW89_WIFI_ROLE_P2P_GO &&
+			     rlink->role != RTW89_WIFI_ROLE_P2P_CLIENT))
+				continue;
 
-			if (link_mode == BTC_WLINK_2G_MCC &&
-			    (rlink->role == RTW89_WIFI_ROLE_AP ||
-			     rlink->role == RTW89_WIFI_ROLE_P2P_GO ||
-			     rlink->role == RTW89_WIFI_ROLE_P2P_CLIENT)) {
-				/* for 2.4G MCC, take role = ap/go/gc */
-				break;
-			} else if (link_mode != BTC_WLINK_2G_SCC ||
-				   rlink->bw == RTW89_CHANNEL_WIDTH_40) {
-				/* for 2.4G scc, take bw = 40M */
-				break;
-			}
+			bw = _get_wl_guard_bw(rtwdev, rlink->bw);
+			wl->afh_info[j][rlink->rf_band].en = 1;
+			wl->afh_info[j][rlink->rf_band].ch = rlink->ch;
+			wl->afh_info[j][rlink->rf_band].bw = bw;
+			wl->afh_info[j][rlink->rf_band].band = rlink->rf_band;
 		}
 	}
 
-	/* default AFH channel sapn = center-ch +- 6MHz */
-	switch (bw) {
-	case RTW89_CHANNEL_WIDTH_20:
-		if (btc->dm.freerun || btc->dm.fddt_train)
-			bw = 48;
-		else
-			bw = 20 + chip->afh_guard_ch * 2;
-		break;
-	case RTW89_CHANNEL_WIDTH_40:
-		if (btc->dm.freerun)
-			bw = 40 + chip->afh_guard_ch * 2;
-		else
-			bw = 40;
-		break;
-	case RTW89_CHANNEL_WIDTH_5:
-		bw = 5 + chip->afh_guard_ch * 2;
-		break;
-	case RTW89_CHANNEL_WIDTH_10:
-		bw = 10 + chip->afh_guard_ch * 2;
-		break;
-	default:
-		en = false; /* turn off AFH info if invalid BW */
-		bw = 0;
-		ch = 0;
-		break;
-	}
+	for (j = RTW89_BAND_2G; j < RTW89_BAND_NUM; j++) {
+		/* skip non-2.4GHz if BT is 2.4GHz only*/
+		if (!(rtwdev->chip->para_ver &  BTC_FEAT_BT_6G)) {
+			if (j != RTW89_BAND_2G)
+				continue;
+			sz = 3;
+		} else {
+			sz = 4;
+		}
 
-	if (!en || ch > 14 || ch == 0) {
-		en = false;
-		bw = 0;
-		ch = 0;
-	}
+		if (wl->afh_info[RTW89_MAC_0][j].en &&
+		    wl->afh_info[RTW89_MAC_1][j].en) {
+			/* select wilder bw if 1+1 */
+			if (wl->afh_info[RTW89_MAC_1][j].bw >
+			    wl->afh_info[RTW89_MAC_0][j].bw)
+				hwb = RTW89_MAC_1;
+			else
+				hwb = RTW89_MAC_0;
+		} else if (wl->afh_info[RTW89_MAC_1][j].en) {
+			hwb = RTW89_MAC_1;
+		} else {
+			hwb = RTW89_MAC_0;
+		}
 
-	if (wl_afh->en == en &&
-	    wl_afh->ch == ch &&
-	    wl_afh->bw == bw &&
-	    (!bt->enable.now || bt->enable.last))
-		return;
+		if (j == RTW89_BAND_2G) {
+			if (cx->bt0.link_info.link_cnt.chg ||
+			    cx->bt1.link_info.link_cnt.chg)
+				bt_link_chg = 1;
+			else
+				bt_link_chg = 0;
+		} else {
+			if (cx->bt0.link_info_56g.link_cnt.chg ||
+			    cx->bt1.link_info_56g.link_cnt.chg)
+				bt_link_chg = 1;
+			else
+				bt_link_chg = 0;
+		}
+
+		/* return if no-afh-info-change/no wl-bt link change */
+		if (!memcmp(&wl->afh_info_last[hwb][j],
+			    &wl->afh_info[hwb][j],
+			    sizeof(struct rtw89_btc_wl_afh_info)) &&
+			    !bt_link_chg &&
+			    !wl->link_mode_chg)
+			continue;
+
+		memcpy(&wl->afh_info_last[hwb][j],
+		       &wl->afh_info[hwb][j],
+		       sizeof(struct rtw89_btc_wl_afh_info));
+
+		buf[0] = wl->afh_info[hwb][j].en;
+		buf[1] = wl->afh_info[hwb][j].ch;
+		buf[2] = wl->afh_info[hwb][j].bw;
+		buf[3] = wl->afh_info[hwb][j].band;
+
+		h2c_func = SET_BT_WL_CH_INFO;
+		if (cx->bt0.enable.now)
+			_send_fw_cmd(rtwdev, BTFC_SET, h2c_func, buf, sz);
 
-	wl_afh->en = buf[0];
-	wl_afh->ch = buf[1];
-	wl_afh->bw = buf[2];
+		/* send mailbox to 2nd-BT if exist */
+		if (cx->bt1.enable.now) {
+			h2c_func |= BT_H2C_FUNC_BT2ND;
+			_send_fw_cmd(rtwdev, BTFC_SET, h2c_func, buf, sz);
+		}
 
-	if (_send_fw_cmd(rtwdev, BTFC_SET, SET_BT_WL_CH_INFO, &wl->afh_info, 3)) {
 		rtw89_debug(rtwdev, RTW89_DBG_BTC,
-			    "[BTC], %s(): en=%d, ch=%d, bw=%d\n",
-			    __func__, en, ch, bw);
+			    "[BTC], %s(): en=%d, ch=%d, bw=%d, rf_band=%d\n",
+			    __func__, buf[0], buf[1], buf[2], buf[3]);
 
 		wl->wcnt[BTC_WCNT_CH_UPDATE]++;
+
+		/* ToDo: Remove if BT update new-define to support 6GHz */
+		if (j == RTW89_BAND_2G)
+			break;
 	}
-}
 
-static void _set_bt_afh_info(struct rtw89_dev *rtwdev)
-{
-	if (rtwdev->chip->chip_id == RTL8922A)
-		_set_bt_afh_info_v1(rtwdev);
-	else
-		_set_bt_afh_info_v0(rtwdev);
+	/* for AFH map display in coex log */
+	_set_wl_ch_map(rtwdev);
+
+	/* set HWB center-ch/forbidden group to ost_info for CR setup in FW */
+	_set_wl_ch_info(rtwdev);
 }
 
 static bool _check_freerun(struct rtw89_dev *rtwdev)
@@ -3828,39 +3934,24 @@ static bool _check_freerun(struct rtw89_dev *rtwdev)
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
 	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
 	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
-	struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1;
-	struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2;
-	struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8;
 	struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
 	struct rtw89_btc_bt_hid_desc *hid = &bt_linfo->hid_desc;
 	union rtw89_btc_module_info *md = &btc->mdinfo;
 	const struct rtw89_btc_ver *ver = btc->ver;
-	u8 isolation, connect_cnt = 0;
+	u8 isolation;
 
 	if (ver->fcxinit == 7)
 		isolation = md->md_v7.ant.isolation;
 	else
 		isolation = md->md.ant.isolation;
 
-	if (ver->fwlrole == 0)
-		connect_cnt = wl_rinfo->connect_cnt;
-	else if (ver->fwlrole == 1)
-		connect_cnt = wl_rinfo_v1->connect_cnt;
-	else if (ver->fwlrole == 2)
-		connect_cnt = wl_rinfo_v2->connect_cnt;
-	else if (ver->fwlrole == 7)
-		connect_cnt = wl_rinfo_v7->connect_cnt;
-	else if (ver->fwlrole == 8)
-		connect_cnt = wl_rinfo_v8->connect_cnt;
-
 	if (btc->ant_type == BTC_ANT_SHARED) {
 		btc->dm.trx_para_level = 0;
 		return false;
 	}
 
 	/* The below is dedicated antenna case */
-	if (connect_cnt > BTC_TDMA_WLROLE_MAX) {
+	if (wl_rinfo->connect_cnt > BTC_TDMA_WLROLE_MAX) {
 		btc->dm.trx_para_level = 5;
 		return true;
 	}
@@ -4097,11 +4188,26 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type)
 	case BTC_CXP_OFFB: /* TDMA off + beacon protect */
 		tdma_on = false;
 		*t = t_def[CXTD_OFF_B2];
-		s[CXST_OFF] = s_def[CXST_OFF];
+		_slot_set_le(btc, CXST_OFF, s_def[CXST_OFF].dur,
+			     s_def[CXST_OFF].cxtbl, s_def[CXST_OFF].cxtype);
+		_slot_set(btc, CXST_B2, 10, cxtbl[1], SLOT_ISO);
+
 		switch (policy_type) {
 		case BTC_CXP_OFFB_BWB0:
+			_slot_set_tbl(btc, CXST_OFF, cxtbl[5]);
+			break;
+		case BTC_CXP_OFFB_BWB1:
 			_slot_set_tbl(btc, CXST_OFF, cxtbl[8]);
 			break;
+		case BTC_CXP_OFFB_BWB2:
+			_slot_set_tbl(btc, CXST_OFF, cxtbl[7]);
+			break;
+		case BTC_CXP_OFFB_BWB3:
+			_slot_set_tbl(btc, CXST_OFF, cxtbl[6]);
+			break;
+		case BTC_CXP_OFFB_BWB4:
+			_slot_set_tbl(btc, CXST_OFF, cxtbl[2]);
+			break;
 		}
 		break;
 	case BTC_CXP_OFFE: /* TDMA off + beacon protect + Ext_control */
@@ -4182,7 +4288,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type)
 	case BTC_CXP_PFIX: /* PS-TDMA Fix-Slot */
 		tdma_on = true;
 		*t = t_def[CXTD_PFIX];
-		if (btc->cx.wl.role_info.role_map.role.ap)
+		if (btc->cx.wl.role_info.role_map  & BIT(RTW89_WIFI_ROLE_AP))
 			_tdma_set_flctrl(btc, CXFLC_QOSNULL);
 
 		switch (policy_type) {
@@ -4461,12 +4567,23 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type)
 		*t = t_def[CXTD_OFF_B2];
 		_slot_set_le(btc, CXST_OFF, s_def[CXST_OFF].dur,
 			     s_def[CXST_OFF].cxtbl, s_def[CXST_OFF].cxtype);
+		_slot_set(btc, CXST_B2, 10, cxtbl[1], SLOT_ISO);
 
 		switch (policy_type) {
 		case BTC_CXP_OFFB_BWB0:
+			_slot_set_tbl(btc, CXST_OFF, cxtbl[5]);
+			break;
+		case BTC_CXP_OFFB_BWB1:
 			_slot_set_tbl(btc, CXST_OFF, cxtbl[8]);
 			break;
-		default:
+		case BTC_CXP_OFFB_BWB2:
+			_slot_set_tbl(btc, CXST_OFF, cxtbl[7]);
+			break;
+		case BTC_CXP_OFFB_BWB3:
+			_slot_set_tbl(btc, CXST_OFF, cxtbl[6]);
+			break;
+		case BTC_CXP_OFFB_BWB4:
+			_slot_set_tbl(btc, CXST_OFF, cxtbl[2]);
 			break;
 		}
 		break;
@@ -4841,35 +4958,17 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec,
 	struct rtw89_btc_dm *dm = &btc->dm;
 	struct rtw89_btc_cx *cx = &btc->cx;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+	struct rtw89_btc_wl_role_info *r = &btc->cx.wl.role_info;
 	struct rtw89_btc_bt_info *bt = &cx->bt0;
 	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
-	u8 gwl, gwl0, gwl1, gbt, plt_ctrl, i, dbcc_2g_phy, b2g = 0;
-	bool dbcc_chg = false, dbcc_en = false;
+	u8 gwl, gwl0, gwl1, gbt, plt_ctrl, i, b2g = 0;
 	u32 ant_path_type;
 
 	ant_path_type = ((phy_map << 8) + type);
 
-	if (btc->ver->fwlrole == 1) {
-		dbcc_chg = wl->role_info_v1.dbcc_chg;
-		dbcc_en = wl->role_info_v1.dbcc_en;
-		dbcc_2g_phy = wl->role_info_v1.dbcc_2g_phy;
-	} else if (btc->ver->fwlrole == 2) {
-		dbcc_chg = wl->role_info_v2.dbcc_chg;
-		dbcc_en = wl->role_info_v2.dbcc_en;
-		dbcc_2g_phy = wl->role_info_v2.dbcc_2g_phy;
-	} else if (btc->ver->fwlrole == 7) {
-		dbcc_chg = wl->role_info_v7.dbcc_chg;
-		dbcc_en = wl->role_info_v7.dbcc_en;
-		dbcc_2g_phy = wl->role_info_v7.dbcc_2g_phy;
-	} else if (btc->ver->fwlrole == 8) {
-		dbcc_chg = wl->role_info_v8.dbcc_chg;
-		dbcc_en = wl->role_info_v8.dbcc_en;
-		dbcc_2g_phy = wl->role_info_v8.dbcc_2g_phy;
-	}
-
 	if (btc->dm.run_reason == BTC_RSN_NTFY_POWEROFF ||
 	    btc->dm.run_reason == BTC_RSN_NTFY_RADIO_STATE ||
-	    btc->dm.run_reason == BTC_RSN_CMD_SET_COEX || dbcc_chg)
+	    btc->dm.run_reason == BTC_RSN_CMD_SET_COEX || r->dbcc_chg)
 		force_exec = FC_EXEC;
 
 	if (!force_exec && ant_path_type == dm->set_ant_path) {
@@ -4967,12 +5066,12 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec,
 	default:
 		gbt = BTC_GNT_HW;
 		if ((rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA) ||
-		    !dbcc_en) {
+		    !r->dbcc_en) {
 			gwl = BTC_GNT_HW;
 			_set_gnt(rtwdev, BTC_PHY_ALL, gwl, gbt);
 		} else {
 			/* for DBCC Only-1-PTA */
-			if (dbcc_2g_phy == RTW89_PHY_0) {
+			if (r->dbcc_2g_phy == RTW89_PHY_0) {
 				gwl0 = BTC_GNT_HW;
 				gwl1 = BTC_GNT_SW_HI;
 			} else {
@@ -4994,7 +5093,7 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec,
 	struct rtw89_btc *btc = &rtwdev->btc;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
 	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8;
+	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
 	u32 ant_path_type = rtw89_get_antpath_type(phy_map, type);
 	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
 	struct rtw89_btc_dm *dm = &btc->dm;
@@ -5005,7 +5104,7 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec,
 	    btc->dm.run_reason == BTC_RSN_CMD_SET_COEX || wl_rinfo->dbcc_chg)
 		force_exec = FC_EXEC;
 
-	if (wl_rinfo->link_mode != BTC_WLINK_25G_MCC &&
+	if (wl_rinfo->link_mode != BTC_WLINK_DB_MCC &&
 	    btc->dm.wl_btg_rx == 2)
 		force_exec = FC_EXEC;
 
@@ -5111,7 +5210,7 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec,
 static void _set_ant(struct rtw89_dev *rtwdev, bool force_exec,
 		     u8 phy_map, u8 type)
 {
-	if (rtwdev->chip->chip_id == RTL8922A)
+	if (rtwdev->chip->chip_id >= RTL8922A)
 		_set_ant_v1(rtwdev, force_exec, phy_map, type);
 	else
 		_set_ant_v0(rtwdev, force_exec, phy_map, type);
@@ -5134,29 +5233,24 @@ static void _action_wl_init(struct rtw89_dev *rtwdev)
 static void _action_wl_off(struct rtw89_dev *rtwdev, u8 mode)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+	struct rtw89_btc_dm *dm = &btc->dm;
+	struct rtw89_btc_cx *cx = &btc->cx;
+	struct rtw89_btc_wl_info *wl = &cx->wl;
 
 	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): !!\n", __func__);
 
-	if (wl->status.map.rf_off || btc->dm.bt_only) {
+	if (wl->status.map.rf_off || btc->dm.bt_only)
 		_set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_WOFF);
-	} else if (wl->status.map.lps == BTC_LPS_RF_ON) {
-		if (mode == BTC_WLINK_5G)
-			_set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_W5G);
-		else
-			_set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
-	}
+	else if (wl->status.map.lps == BTC_LPS_RF_ON ||
+		 cx->bt0.whql_test || cx->bt1.whql_test)
+		_set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_PTA);
 
-	if (mode == BTC_WLINK_5G) {
+	if (dm->out_of_band)
 		_set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_OFF);
-	} else if (wl->status.map.lps == BTC_LPS_RF_ON) {
-		if (btc->cx.bt0.link_info.a2dp_desc.active)
-			_set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_WL_OFF);
-		else
-			_set_policy(rtwdev, BTC_CXP_OFF_BWB1, BTC_ACT_WL_OFF);
-	} else {
+	else if (cx->bt0.whql_test || cx->bt1.whql_test)
+		_set_policy(rtwdev, BTC_CXP_OFFB_BWB4, BTC_ACT_WL_OFF);
+	else
 		_set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_WL_OFF);
-	}
 }
 
 static void _action_freerun(struct rtw89_dev *rtwdev)
@@ -5563,48 +5657,29 @@ static void _action_wl_rfk(struct rtw89_dev *rtwdev)
 static void _set_btg_ctrl(struct rtw89_dev *rtwdev)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
+	struct rtw89_btc_dm *dm = &btc->dm;
+	struct rtw89_btc_fbtc_outsrc_set_info *o_info = &dm->ost_info;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1;
-	struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2;
-	struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8;
-	struct rtw89_btc_fbtc_outsrc_set_info *o_info = &btc->dm.ost_info;
-	struct rtw89_btc_wl_role_info *wl_rinfo_v0 = &wl->role_info;
+	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
 	const struct rtw89_chip_info *chip = rtwdev->chip;
-	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
-	struct rtw89_btc_dm *dm = &btc->dm;
-	struct _wl_rinfo_now wl_rinfo;
 	u32 is_btg = BTC_BTGCTRL_DISABLE;
 
 	if (btc->manual_ctrl)
 		return;
 
-	if (ver->fwlrole == 0)
-		wl_rinfo.link_mode = wl_rinfo_v0->link_mode;
-	else if (ver->fwlrole == 1)
-		wl_rinfo.link_mode = wl_rinfo_v1->link_mode;
-	else if (ver->fwlrole == 2)
-		wl_rinfo.link_mode = wl_rinfo_v2->link_mode;
-	else if (ver->fwlrole == 7)
-		wl_rinfo.link_mode = wl_rinfo_v7->link_mode;
-	else if (ver->fwlrole == 8)
-		wl_rinfo.link_mode = wl_rinfo_v8->link_mode;
-	else
-		return;
-
 	/* notify halbb ignore GNT_BT or not for WL BB Rx-AGC control */
 	if (btc->ant_type == BTC_ANT_SHARED) {
 		if (!(bt->run_patch_code && bt->enable.now))
 			is_btg = BTC_BTGCTRL_DISABLE;
-		else if (wl_rinfo.link_mode != BTC_WLINK_5G)
+		else if (wl_rinfo->link_mode_v0 != BTC_WLINK_V0_5G)
 			is_btg = BTC_BTGCTRL_ENABLE;
 		else
 			is_btg = BTC_BTGCTRL_DISABLE;
 
 		/* bb call ctrl_btg() in WL FW by slot */
-		if (!ver->fcxosi &&
-		    wl_rinfo.link_mode == BTC_WLINK_25G_MCC)
+		if (!btc->ver->fcxosi &&
+		    wl_rinfo->link_mode_v0 == BTC_WLINK_V0_25G_MCC)
 			is_btg = BTC_BTGCTRL_BB_GNT_FWCTRL;
 	}
 
@@ -5614,7 +5689,7 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev)
 		dm->wl_btg_rx = is_btg;
 
 	/* skip setup if btg_ctrl set by wl fw */
-	if (!ver->fcxosi && is_btg > BTC_BTGCTRL_ENABLE)
+	if (!btc->ver->fcxosi && is_btg > BTC_BTGCTRL_ENABLE)
 		return;
 
 	/* Below flow is for BTC_FEAT_NEW_BBAPI_FLOW = 1 */
@@ -5633,7 +5708,7 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev)
 		o_info->btg_rx[BTC_RF_S1] = is_btg;
 	}
 
-	if (ver->fcxosi)
+	if (btc->ver->fcxosi)
 		return;
 
 	chip->ops->ctrl_btg_bt_rx(rtwdev, o_info->btg_rx[BTC_RF_S0],
@@ -5651,40 +5726,20 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev)
 	struct rtw89_btc_fbtc_outsrc_set_info *o_info = &btc->dm.ost_info;
 	struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt0.link_info;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_role_info_v2 *rinfo_v2 = &wl->role_info_v2;
-	struct rtw89_btc_wl_role_info_v7 *rinfo_v7 = &wl->role_info_v7;
-	struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &wl->role_info_v8;
+	struct rtw89_btc_wl_role_info *rinfo = &wl->role_info;
 	const struct rtw89_chip_info *chip = rtwdev->chip;
 	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
 	struct rtw89_btc_dm *dm = &btc->dm;
-	u8 is_preagc, val, link_mode, dbcc_2g_phy;
-	u8 role_ver = rtwdev->btc.ver->fwlrole;
-	bool dbcc_en;
+	u8 is_preagc, val;
 
 	if (btc->manual_ctrl)
 		return;
 
-	if (role_ver == 2) {
-		dbcc_en = rinfo_v2->dbcc_en;
-		link_mode = rinfo_v2->link_mode;
-		dbcc_2g_phy = rinfo_v2->dbcc_2g_phy;
-	} else if (role_ver == 7) {
-		dbcc_en = rinfo_v7->dbcc_en;
-		link_mode = rinfo_v7->link_mode;
-		dbcc_2g_phy = rinfo_v7->dbcc_2g_phy;
-	} else if (role_ver == 8) {
-		dbcc_en = rinfo_v8->dbcc_en;
-		link_mode = rinfo_v8->link_mode;
-		dbcc_2g_phy = rinfo_v7->dbcc_2g_phy;
-	} else {
-		return;
-	}
-
 	if (!(bt->run_patch_code && bt->enable.now)) {
 		is_preagc = BTC_PREAGC_DISABLE;
-	} else if (link_mode == BTC_WLINK_5G) {
+	} else if (dm->tdd_bind.rf_band == BIT(RTW89_BAND_5G)) {
 		is_preagc = BTC_PREAGC_DISABLE;
-	} else if (link_mode == BTC_WLINK_NOLINK ||
+	} else if (rinfo->link_mode == BTC_WLINK_NOLINK ||
 		 btc->cx.bt0.link_info.link_cnt.now == 0) {
 		is_preagc = BTC_PREAGC_DISABLE;
 	} else if (dm->tdma_now.type != CXTDMA_OFF &&
@@ -5692,7 +5747,7 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev)
 		 !bt_linfo->hid_desc.exist &&
 		 dm->fddt_train == BTC_FDDT_DISABLE) {
 		is_preagc = BTC_PREAGC_DISABLE;
-	} else if (dbcc_en && (dbcc_2g_phy != RTW89_PHY_1)) {
+	} else if (rinfo->dbcc_en && (rinfo->dbcc_2g_phy != RTW89_PHY_1)) {
 		is_preagc = BTC_PREAGC_DISABLE;
 	} else if (btc->ant_type == BTC_ANT_SHARED) {
 		is_preagc = BTC_PREAGC_DISABLE;
@@ -5700,7 +5755,7 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev)
 		is_preagc = BTC_PREAGC_ENABLE;
 	}
 
-	if (!btc->ver->fcxosi && link_mode == BTC_WLINK_25G_MCC)
+	if (!btc->ver->fcxosi && rinfo->link_mode == BTC_WLINK_DB_MCC)
 		is_preagc = BTC_PREAGC_BB_FWCTRL;
 
 	if (dm->wl_pre_agc_rb != dm->wl_pre_agc &&
@@ -5771,10 +5826,7 @@ static void __rtw89_tx_time_iter(struct rtw89_vif_link *rtwvif_link,
 	u16 enable = iter_data->enable;
 	bool reenable = iter_data->reenable;
 
-	if (btc->ver->fwlrole == 8)
-		plink = &wl->rlink_info[port][0];
-	else
-		plink = &wl->link_info[port];
+	plink = &wl->rlink_info[port][rtwsta_link->link_id];
 
 	rtw89_debug(rtwdev, RTW89_DBG_BTC,
 		    "[BTC], %s(): port = %d\n", __func__, port);
@@ -5840,39 +5892,23 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev)
 	struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc;
 	struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc;
 	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
-	struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1;
-	struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2;
-	struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8;
 	struct rtw89_txtime_data data = {.rtwdev = rtwdev};
-	u8 mode, igno_bt, tx_retry;
+	bool reenable = false;
+	u8 igno_bt, tx_retry;
 	u32 tx_time;
 	u16 enable;
-	bool reenable = false;
 
 	if (btc->manual_ctrl)
 		return;
 
-	if (ver->fwlrole == 0)
-		mode = wl_rinfo->link_mode;
-	else if (ver->fwlrole == 1)
-		mode = wl_rinfo_v1->link_mode;
-	else if (ver->fwlrole == 2)
-		mode = wl_rinfo_v2->link_mode;
-	else if (ver->fwlrole == 7)
-		mode = wl_rinfo_v7->link_mode;
-	else if (ver->fwlrole == 8)
-		mode = wl_rinfo_v8->link_mode;
-	else
-		return;
-
 	if (ver->fcxctrl == 7)
 		igno_bt = btc->ctrl.ctrl_v7.igno_bt;
 	else
 		igno_bt = btc->ctrl.ctrl.igno_bt;
 
 	if (btc->dm.freerun || igno_bt || b->link_cnt.now == 0 ||
-	    mode == BTC_WLINK_5G || mode == BTC_WLINK_NOLINK) {
+	    dm->tdd_bind.rf_band == BIT(RTW89_BAND_5G) ||
+	    wl_rinfo->link_mode == BTC_WLINK_NOLINK) {
 		enable = 0;
 		tx_time = BTC_MAX_TX_TIME_DEF;
 		tx_retry = BTC_MAX_TX_RETRY_DEF;
@@ -5915,31 +5951,12 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev)
 static void _set_bt_rx_agc(struct rtw89_dev *rtwdev)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
 	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
-	struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1;
-	struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2;
-	struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8;
 	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
 	bool bt_hi_lna_rx = false;
-	u8 mode;
 
-	if (ver->fwlrole == 0)
-		mode = wl_rinfo->link_mode;
-	else if (ver->fwlrole == 1)
-		mode = wl_rinfo_v1->link_mode;
-	else if (ver->fwlrole == 2)
-		mode = wl_rinfo_v2->link_mode;
-	else if (ver->fwlrole == 7)
-		mode = wl_rinfo_v7->link_mode;
-	else if (ver->fwlrole == 8)
-		mode = wl_rinfo_v8->link_mode;
-	else
-		return;
-
-	if (mode != BTC_WLINK_NOLINK && btc->dm.wl_btg_rx)
+	if (wl_rinfo->link_mode != BTC_WLINK_NOLINK && btc->dm.wl_btg_rx)
 		bt_hi_lna_rx = true;
 
 	if (bt_hi_lna_rx == bt->hi_lna_rx)
@@ -5985,20 +6002,51 @@ static void _wl_req_mac(struct rtw89_dev *rtwdev, u8 mac)
 		rtw89_write32_set(rtwdev, add, B_AX_WL_SRC);
 }
 
-static void _action_common(struct rtw89_dev *rtwdev)
+static void _update_zb_coex_tbl(struct rtw89_dev *rtwdev)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &wl->role_info_v8;
-	struct rtw89_btc_wl_smap *wl_smap = &wl->status.map;
-	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
-	struct rtw89_btc_dm *dm = &btc->dm;
-	u32 bt_rom_code_id, bt_fw_ver;
-	u8 i;
-
-	if (btc->ver->fwlrole == 8)
-		_wl_req_mac(rtwdev, rinfo_v8->pta_req_band);
+	const struct rtw89_btc_ver *ver = btc->ver;
+	u32 zb_tbl0 = 0xda5a5a5a, zb_tbl1 = 0xda5a5a5a;
+	u8 link_mode_chg = btc->cx.wl.link_mode_chg;
+	u8 mode = btc->cx.wl.role_info.link_mode;
+	u8 wa_type;
+
+	if (btc->dm.run_reason != BTC_RSN_NTFY_INIT && !link_mode_chg)
+		return;
+
+	if (ver->fcxinit == 7)
+		wa_type = btc->mdinfo.md_v7.wa_type;
+	else
+		wa_type = btc->mdinfo.md.wa_type;
 
+	if (!(wa_type & BTC_WA_HFP_ZB))
+		return;
+
+	if (btc->dm.tdd_bind.rf_band == BIT(RTW89_BAND_5G) ||
+	    rtwdev->btc.dm.freerun) {
+		zb_tbl0 = 0xffffffff;
+		zb_tbl1 = 0xffffffff;
+	} else if (mode == BTC_WLINK_DB_MCC) {
+		zb_tbl0 = 0xffffffff; /* for E5G slot */
+		zb_tbl1 = 0xda5a5a5a; /* for E2G slot */
+	}
+	rtw89_write32(rtwdev, R_BTC_ZB_COEX_TBL_0, zb_tbl0);
+	rtw89_write32(rtwdev, R_BTC_ZB_COEX_TBL_1, zb_tbl1);
+}
+
+static void _action_common(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_btc *btc = &rtwdev->btc;
+	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+	struct rtw89_btc_wl_role_info *rinfo = &wl->role_info;
+	struct rtw89_btc_wl_smap *wl_smap = &wl->status.map;
+	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
+	struct rtw89_btc_dm *dm = &btc->dm;
+	u32 bt_rom_code_id, bt_fw_ver;
+	u8 i;
+
+	_wl_req_mac(rtwdev, rinfo->pta_req_band);
+	_update_zb_coex_tbl(rtwdev);
 	_set_btg_ctrl(rtwdev);
 	_set_wl_preagc_ctrl(rtwdev);
 	_set_wl_tx_limit(rtwdev);
@@ -6041,7 +6089,10 @@ static void _action_common(struct rtw89_dev *rtwdev)
 	}
 	dm->tdma_instant_excute = 0;
 	dm->lps_ctrl_change = false;
+	wl->role_info.link_mode_chg = 0;
 	wl->pta_reg_mac_chg = false;
+	dm->pre_agc_chg = false;
+	wl->dbcc_chg = 0;
 }
 
 static void _action_by_bt(struct rtw89_dev *rtwdev)
@@ -6407,473 +6458,29 @@ void _update_dbcc_band(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx)
 		btc->cx.wl.dbcc_info.op_band[phy_idx];
 }
 
-static void _update_wl_info(struct rtw89_dev *rtwdev)
-{
-	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info;
-	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
-	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
-	u8 i, cnt_connect = 0, cnt_connecting = 0, cnt_active = 0;
-	u8 cnt_2g = 0, cnt_5g = 0, phy;
-	u32 wl_2g_ch[2] = {0}, wl_5g_ch[2] = {0};
-	bool b2g = false, b5g = false, client_joined = false;
-
-	memset(wl_rinfo, 0, sizeof(*wl_rinfo));
-
-	for (i = 0; i < RTW89_PORT_NUM; i++) {
-		/* check if role active? */
-		if (!wl_linfo[i].active)
-			continue;
-
-		cnt_active++;
-		wl_rinfo->active_role[cnt_active - 1].role = wl_linfo[i].role;
-		wl_rinfo->active_role[cnt_active - 1].pid = wl_linfo[i].pid;
-		wl_rinfo->active_role[cnt_active - 1].phy = wl_linfo[i].phy;
-		wl_rinfo->active_role[cnt_active - 1].band = wl_linfo[i].band;
-		wl_rinfo->active_role[cnt_active - 1].noa = (u8)wl_linfo[i].noa;
-		wl_rinfo->active_role[cnt_active - 1].connected = 0;
-
-		wl->port_id[wl_linfo[i].role] = wl_linfo[i].pid;
-
-		phy = wl_linfo[i].phy;
-
-		/* check dbcc role */
-		if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) {
-			wl_dinfo->role[phy] = wl_linfo[i].role;
-			wl_dinfo->op_band[phy] = wl_linfo[i].band;
-			_update_dbcc_band(rtwdev, phy);
-			_fw_set_drv_info(rtwdev, CXDRVINFO_DBCC);
-		}
-
-		if (wl_linfo[i].connected == MLME_NO_LINK) {
-			continue;
-		} else if (wl_linfo[i].connected == MLME_LINKING) {
-			cnt_connecting++;
-		} else {
-			cnt_connect++;
-			if ((wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO ||
-			     wl_linfo[i].role == RTW89_WIFI_ROLE_AP) &&
-			     wl_linfo[i].client_cnt > 1)
-				client_joined = true;
-		}
-
-		wl_rinfo->role_map.val |= BIT(wl_linfo[i].role);
-		wl_rinfo->active_role[cnt_active - 1].ch = wl_linfo[i].ch;
-		wl_rinfo->active_role[cnt_active - 1].bw = wl_linfo[i].bw;
-		wl_rinfo->active_role[cnt_active - 1].connected = 1;
-
-		/* only care 2 roles + BT coex */
-		if (wl_linfo[i].band != RTW89_BAND_2G) {
-			if (cnt_5g <= ARRAY_SIZE(wl_5g_ch) - 1)
-				wl_5g_ch[cnt_5g] = wl_linfo[i].ch;
-			cnt_5g++;
-			b5g = true;
-		} else {
-			if (cnt_2g <= ARRAY_SIZE(wl_2g_ch) - 1)
-				wl_2g_ch[cnt_2g] = wl_linfo[i].ch;
-			cnt_2g++;
-			b2g = true;
-		}
-	}
-
-	wl_rinfo->connect_cnt = cnt_connect;
-
-	/* Be careful to change the following sequence!! */
-	if (cnt_connect == 0) {
-		wl_rinfo->link_mode = BTC_WLINK_NOLINK;
-		wl_rinfo->role_map.role.none = 1;
-	} else if (!b2g && b5g) {
-		wl_rinfo->link_mode = BTC_WLINK_5G;
-	} else if (wl_rinfo->role_map.role.nan) {
-		wl_rinfo->link_mode = BTC_WLINK_2G_NAN;
-	} else if (cnt_connect > BTC_TDMA_WLROLE_MAX) {
-		wl_rinfo->link_mode = BTC_WLINK_OTHER;
-	} else  if (b2g && b5g && cnt_connect == 2) {
-		if (rtwdev->dbcc_en) {
-			switch (wl_dinfo->role[RTW89_PHY_0]) {
-			case RTW89_WIFI_ROLE_STATION:
-				wl_rinfo->link_mode = BTC_WLINK_2G_STA;
-				break;
-			case RTW89_WIFI_ROLE_P2P_GO:
-				wl_rinfo->link_mode = BTC_WLINK_2G_GO;
-				break;
-			case RTW89_WIFI_ROLE_P2P_CLIENT:
-				wl_rinfo->link_mode = BTC_WLINK_2G_GC;
-				break;
-			case RTW89_WIFI_ROLE_AP:
-				wl_rinfo->link_mode = BTC_WLINK_2G_AP;
-				break;
-			default:
-				wl_rinfo->link_mode = BTC_WLINK_OTHER;
-				break;
-			}
-		} else {
-			wl_rinfo->link_mode = BTC_WLINK_25G_MCC;
-		}
-	} else if (!b5g && cnt_connect == 2) {
-		if (wl_rinfo->role_map.role.station &&
-		    (wl_rinfo->role_map.role.p2p_go ||
-		    wl_rinfo->role_map.role.p2p_gc ||
-		    wl_rinfo->role_map.role.ap)) {
-			if (wl_2g_ch[0] == wl_2g_ch[1])
-				wl_rinfo->link_mode = BTC_WLINK_2G_SCC;
-			else
-				wl_rinfo->link_mode = BTC_WLINK_2G_MCC;
-		} else {
-			wl_rinfo->link_mode = BTC_WLINK_2G_MCC;
-		}
-	} else if (!b5g && cnt_connect == 1) {
-		if (wl_rinfo->role_map.role.station)
-			wl_rinfo->link_mode = BTC_WLINK_2G_STA;
-		else if (wl_rinfo->role_map.role.ap)
-			wl_rinfo->link_mode = BTC_WLINK_2G_AP;
-		else if (wl_rinfo->role_map.role.p2p_go)
-			wl_rinfo->link_mode = BTC_WLINK_2G_GO;
-		else if (wl_rinfo->role_map.role.p2p_gc)
-			wl_rinfo->link_mode = BTC_WLINK_2G_GC;
-		else
-			wl_rinfo->link_mode = BTC_WLINK_OTHER;
-	}
-
-	/* if no client_joined, don't care P2P-GO/AP role */
-	if (wl_rinfo->role_map.role.p2p_go || wl_rinfo->role_map.role.ap) {
-		if (!client_joined) {
-			if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC ||
-			    wl_rinfo->link_mode == BTC_WLINK_2G_MCC) {
-				wl_rinfo->link_mode = BTC_WLINK_2G_STA;
-				wl_rinfo->connect_cnt = 1;
-			} else if (wl_rinfo->link_mode == BTC_WLINK_2G_GO ||
-				 wl_rinfo->link_mode == BTC_WLINK_2G_AP) {
-				wl_rinfo->link_mode = BTC_WLINK_NOLINK;
-				wl_rinfo->connect_cnt = 0;
-			}
-		}
-	}
-
-	rtw89_debug(rtwdev, RTW89_DBG_BTC,
-		    "[BTC], cnt_connect = %d, connecting = %d, link_mode = %d\n",
-		    cnt_connect, cnt_connecting, wl_rinfo->link_mode);
-
-	_fw_set_drv_info(rtwdev, CXDRVINFO_ROLE);
-}
-
-static void _update_wl_info_v1(struct rtw89_dev *rtwdev)
-{
-	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info;
-	struct rtw89_btc_wl_role_info_v1 *wl_rinfo = &wl->role_info_v1;
-	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
-	u8 cnt_connect = 0, cnt_connecting = 0, cnt_active = 0;
-	u8 cnt_2g = 0, cnt_5g = 0, phy;
-	u32 wl_2g_ch[2] = {}, wl_5g_ch[2] = {};
-	bool b2g = false, b5g = false, client_joined = false;
-	u8 i;
-
-	memset(wl_rinfo, 0, sizeof(*wl_rinfo));
-
-	for (i = 0; i < RTW89_PORT_NUM; i++) {
-		if (!wl_linfo[i].active)
-			continue;
-
-		cnt_active++;
-		wl_rinfo->active_role_v1[cnt_active - 1].role = wl_linfo[i].role;
-		wl_rinfo->active_role_v1[cnt_active - 1].pid = wl_linfo[i].pid;
-		wl_rinfo->active_role_v1[cnt_active - 1].phy = wl_linfo[i].phy;
-		wl_rinfo->active_role_v1[cnt_active - 1].band = wl_linfo[i].band;
-		wl_rinfo->active_role_v1[cnt_active - 1].noa = (u8)wl_linfo[i].noa;
-		wl_rinfo->active_role_v1[cnt_active - 1].connected = 0;
-
-		wl->port_id[wl_linfo[i].role] = wl_linfo[i].pid;
-
-		phy = wl_linfo[i].phy;
-
-		if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) {
-			wl_dinfo->role[phy] = wl_linfo[i].role;
-			wl_dinfo->op_band[phy] = wl_linfo[i].band;
-			_update_dbcc_band(rtwdev, phy);
-			_fw_set_drv_info(rtwdev, CXDRVINFO_DBCC);
-		}
-
-		if (wl_linfo[i].connected == MLME_NO_LINK) {
-			continue;
-		} else if (wl_linfo[i].connected == MLME_LINKING) {
-			cnt_connecting++;
-		} else {
-			cnt_connect++;
-			if ((wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO ||
-			     wl_linfo[i].role == RTW89_WIFI_ROLE_AP) &&
-			     wl_linfo[i].client_cnt > 1)
-				client_joined = true;
-		}
-
-		wl_rinfo->role_map.val |= BIT(wl_linfo[i].role);
-		wl_rinfo->active_role_v1[cnt_active - 1].ch = wl_linfo[i].ch;
-		wl_rinfo->active_role_v1[cnt_active - 1].bw = wl_linfo[i].bw;
-		wl_rinfo->active_role_v1[cnt_active - 1].connected = 1;
-
-		/* only care 2 roles + BT coex */
-		if (wl_linfo[i].band != RTW89_BAND_2G) {
-			if (cnt_5g <= ARRAY_SIZE(wl_5g_ch) - 1)
-				wl_5g_ch[cnt_5g] = wl_linfo[i].ch;
-			cnt_5g++;
-			b5g = true;
-		} else {
-			if (cnt_2g <= ARRAY_SIZE(wl_2g_ch) - 1)
-				wl_2g_ch[cnt_2g] = wl_linfo[i].ch;
-			cnt_2g++;
-			b2g = true;
-		}
-	}
-
-	wl_rinfo->connect_cnt = cnt_connect;
-
-	/* Be careful to change the following sequence!! */
-	if (cnt_connect == 0) {
-		wl_rinfo->link_mode = BTC_WLINK_NOLINK;
-		wl_rinfo->role_map.role.none = 1;
-	} else if (!b2g && b5g) {
-		wl_rinfo->link_mode = BTC_WLINK_5G;
-	} else if (wl_rinfo->role_map.role.nan) {
-		wl_rinfo->link_mode = BTC_WLINK_2G_NAN;
-	} else if (cnt_connect > BTC_TDMA_WLROLE_MAX) {
-		wl_rinfo->link_mode = BTC_WLINK_OTHER;
-	} else  if (b2g && b5g && cnt_connect == 2) {
-		if (rtwdev->dbcc_en) {
-			switch (wl_dinfo->role[RTW89_PHY_0]) {
-			case RTW89_WIFI_ROLE_STATION:
-				wl_rinfo->link_mode = BTC_WLINK_2G_STA;
-				break;
-			case RTW89_WIFI_ROLE_P2P_GO:
-				wl_rinfo->link_mode = BTC_WLINK_2G_GO;
-				break;
-			case RTW89_WIFI_ROLE_P2P_CLIENT:
-				wl_rinfo->link_mode = BTC_WLINK_2G_GC;
-				break;
-			case RTW89_WIFI_ROLE_AP:
-				wl_rinfo->link_mode = BTC_WLINK_2G_AP;
-				break;
-			default:
-				wl_rinfo->link_mode = BTC_WLINK_OTHER;
-				break;
-			}
-		} else {
-			wl_rinfo->link_mode = BTC_WLINK_25G_MCC;
-		}
-	} else if (!b5g && cnt_connect == 2) {
-		if (wl_rinfo->role_map.role.station &&
-		    (wl_rinfo->role_map.role.p2p_go ||
-		    wl_rinfo->role_map.role.p2p_gc ||
-		    wl_rinfo->role_map.role.ap)) {
-			if (wl_2g_ch[0] == wl_2g_ch[1])
-				wl_rinfo->link_mode = BTC_WLINK_2G_SCC;
-			else
-				wl_rinfo->link_mode = BTC_WLINK_2G_MCC;
-		} else {
-			wl_rinfo->link_mode = BTC_WLINK_2G_MCC;
-		}
-	} else if (!b5g && cnt_connect == 1) {
-		if (wl_rinfo->role_map.role.station)
-			wl_rinfo->link_mode = BTC_WLINK_2G_STA;
-		else if (wl_rinfo->role_map.role.ap)
-			wl_rinfo->link_mode = BTC_WLINK_2G_AP;
-		else if (wl_rinfo->role_map.role.p2p_go)
-			wl_rinfo->link_mode = BTC_WLINK_2G_GO;
-		else if (wl_rinfo->role_map.role.p2p_gc)
-			wl_rinfo->link_mode = BTC_WLINK_2G_GC;
-		else
-			wl_rinfo->link_mode = BTC_WLINK_OTHER;
-	}
-
-	/* if no client_joined, don't care P2P-GO/AP role */
-	if (wl_rinfo->role_map.role.p2p_go || wl_rinfo->role_map.role.ap) {
-		if (!client_joined) {
-			if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC ||
-			    wl_rinfo->link_mode == BTC_WLINK_2G_MCC) {
-				wl_rinfo->link_mode = BTC_WLINK_2G_STA;
-				wl_rinfo->connect_cnt = 1;
-			} else if (wl_rinfo->link_mode == BTC_WLINK_2G_GO ||
-				 wl_rinfo->link_mode == BTC_WLINK_2G_AP) {
-				wl_rinfo->link_mode = BTC_WLINK_NOLINK;
-				wl_rinfo->connect_cnt = 0;
-			}
-		}
-	}
-
-	rtw89_debug(rtwdev, RTW89_DBG_BTC,
-		    "[BTC], cnt_connect = %d, connecting = %d, link_mode = %d\n",
-		    cnt_connect, cnt_connecting, wl_rinfo->link_mode);
-
-	_fw_set_drv_info(rtwdev, CXDRVINFO_ROLE);
-}
-
-static void _update_wl_info_v2(struct rtw89_dev *rtwdev)
-{
-	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info;
-	struct rtw89_btc_wl_role_info_v2 *wl_rinfo = &wl->role_info_v2;
-	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
-	u8 cnt_connect = 0, cnt_connecting = 0, cnt_active = 0;
-	u8 cnt_2g = 0, cnt_5g = 0, phy;
-	u32 wl_2g_ch[2] = {}, wl_5g_ch[2] = {};
-	bool b2g = false, b5g = false, client_joined = false;
-	u8 i;
-
-	memset(wl_rinfo, 0, sizeof(*wl_rinfo));
-
-	for (i = 0; i < RTW89_PORT_NUM; i++) {
-		if (!wl_linfo[i].active)
-			continue;
-
-		cnt_active++;
-		wl_rinfo->active_role_v2[cnt_active - 1].role = wl_linfo[i].role;
-		wl_rinfo->active_role_v2[cnt_active - 1].pid = wl_linfo[i].pid;
-		wl_rinfo->active_role_v2[cnt_active - 1].phy = wl_linfo[i].phy;
-		wl_rinfo->active_role_v2[cnt_active - 1].band = wl_linfo[i].band;
-		wl_rinfo->active_role_v2[cnt_active - 1].noa = (u8)wl_linfo[i].noa;
-		wl_rinfo->active_role_v2[cnt_active - 1].connected = 0;
-
-		wl->port_id[wl_linfo[i].role] = wl_linfo[i].pid;
-
-		phy = wl_linfo[i].phy;
-
-		if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) {
-			wl_dinfo->role[phy] = wl_linfo[i].role;
-			wl_dinfo->op_band[phy] = wl_linfo[i].band;
-			_update_dbcc_band(rtwdev, phy);
-			_fw_set_drv_info(rtwdev, CXDRVINFO_DBCC);
-		}
-
-		if (wl_linfo[i].connected == MLME_NO_LINK) {
-			continue;
-		} else if (wl_linfo[i].connected == MLME_LINKING) {
-			cnt_connecting++;
-		} else {
-			cnt_connect++;
-			if ((wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO ||
-			     wl_linfo[i].role == RTW89_WIFI_ROLE_AP) &&
-			     wl_linfo[i].client_cnt > 1)
-				client_joined = true;
-		}
-
-		wl_rinfo->role_map.val |= BIT(wl_linfo[i].role);
-		wl_rinfo->active_role_v2[cnt_active - 1].ch = wl_linfo[i].ch;
-		wl_rinfo->active_role_v2[cnt_active - 1].bw = wl_linfo[i].bw;
-		wl_rinfo->active_role_v2[cnt_active - 1].connected = 1;
-
-		/* only care 2 roles + BT coex */
-		if (wl_linfo[i].band != RTW89_BAND_2G) {
-			if (cnt_5g <= ARRAY_SIZE(wl_5g_ch) - 1)
-				wl_5g_ch[cnt_5g] = wl_linfo[i].ch;
-			cnt_5g++;
-			b5g = true;
-		} else {
-			if (cnt_2g <= ARRAY_SIZE(wl_2g_ch) - 1)
-				wl_2g_ch[cnt_2g] = wl_linfo[i].ch;
-			cnt_2g++;
-			b2g = true;
-		}
-	}
-
-	wl_rinfo->connect_cnt = cnt_connect;
-
-	/* Be careful to change the following sequence!! */
-	if (cnt_connect == 0) {
-		wl_rinfo->link_mode = BTC_WLINK_NOLINK;
-		wl_rinfo->role_map.role.none = 1;
-	} else if (!b2g && b5g) {
-		wl_rinfo->link_mode = BTC_WLINK_5G;
-	} else if (wl_rinfo->role_map.role.nan) {
-		wl_rinfo->link_mode = BTC_WLINK_2G_NAN;
-	} else if (cnt_connect > BTC_TDMA_WLROLE_MAX) {
-		wl_rinfo->link_mode = BTC_WLINK_OTHER;
-	} else  if (b2g && b5g && cnt_connect == 2) {
-		if (rtwdev->dbcc_en) {
-			switch (wl_dinfo->role[RTW89_PHY_0]) {
-			case RTW89_WIFI_ROLE_STATION:
-				wl_rinfo->link_mode = BTC_WLINK_2G_STA;
-				break;
-			case RTW89_WIFI_ROLE_P2P_GO:
-				wl_rinfo->link_mode = BTC_WLINK_2G_GO;
-				break;
-			case RTW89_WIFI_ROLE_P2P_CLIENT:
-				wl_rinfo->link_mode = BTC_WLINK_2G_GC;
-				break;
-			case RTW89_WIFI_ROLE_AP:
-				wl_rinfo->link_mode = BTC_WLINK_2G_AP;
-				break;
-			default:
-				wl_rinfo->link_mode = BTC_WLINK_OTHER;
-				break;
-			}
-		} else {
-			wl_rinfo->link_mode = BTC_WLINK_25G_MCC;
-		}
-	} else if (!b5g && cnt_connect == 2) {
-		if (wl_rinfo->role_map.role.station &&
-		    (wl_rinfo->role_map.role.p2p_go ||
-		    wl_rinfo->role_map.role.p2p_gc ||
-		    wl_rinfo->role_map.role.ap)) {
-			if (wl_2g_ch[0] == wl_2g_ch[1])
-				wl_rinfo->link_mode = BTC_WLINK_2G_SCC;
-			else
-				wl_rinfo->link_mode = BTC_WLINK_2G_MCC;
-		} else {
-			wl_rinfo->link_mode = BTC_WLINK_2G_MCC;
-		}
-	} else if (!b5g && cnt_connect == 1) {
-		if (wl_rinfo->role_map.role.station)
-			wl_rinfo->link_mode = BTC_WLINK_2G_STA;
-		else if (wl_rinfo->role_map.role.ap)
-			wl_rinfo->link_mode = BTC_WLINK_2G_AP;
-		else if (wl_rinfo->role_map.role.p2p_go)
-			wl_rinfo->link_mode = BTC_WLINK_2G_GO;
-		else if (wl_rinfo->role_map.role.p2p_gc)
-			wl_rinfo->link_mode = BTC_WLINK_2G_GC;
-		else
-			wl_rinfo->link_mode = BTC_WLINK_OTHER;
-	}
-
-	/* if no client_joined, don't care P2P-GO/AP role */
-	if (wl_rinfo->role_map.role.p2p_go || wl_rinfo->role_map.role.ap) {
-		if (!client_joined) {
-			if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC ||
-			    wl_rinfo->link_mode == BTC_WLINK_2G_MCC) {
-				wl_rinfo->link_mode = BTC_WLINK_2G_STA;
-				wl_rinfo->connect_cnt = 1;
-			} else if (wl_rinfo->link_mode == BTC_WLINK_2G_GO ||
-				 wl_rinfo->link_mode == BTC_WLINK_2G_AP) {
-				wl_rinfo->link_mode = BTC_WLINK_NOLINK;
-				wl_rinfo->connect_cnt = 0;
-			}
-		}
-	}
-
-	rtw89_debug(rtwdev, RTW89_DBG_BTC,
-		    "[BTC], cnt_connect = %d, connecting = %d, link_mode = %d\n",
-		    cnt_connect, cnt_connecting, wl_rinfo->link_mode);
-
-	_fw_set_drv_info(rtwdev, CXDRVINFO_ROLE);
-}
-
 #define BTC_CHK_HANG_MAX 3
 #define BTC_SCB_INV_VALUE GENMASK(31, 0)
 
-static u8 _get_role_link_mode(u8 role)
+static u8 _get_role_link_mode(struct rtw89_btc_wl_role_info *r, u8 role, bool notv10)
 {
 	switch (role) {
 	case RTW89_WIFI_ROLE_STATION:
-		return BTC_WLINK_2G_STA;
+	default:
+		if (notv10)
+			r->link_mode_v0 = BTC_WLINK_V0_2G_STA;
+		return BTC_WLINK_STA;
 	case RTW89_WIFI_ROLE_P2P_GO:
-		return BTC_WLINK_2G_GO;
+		if (notv10)
+			r->link_mode_v0 = BTC_WLINK_V0_2G_GO;
+		return BTC_WLINK_GO;
 	case RTW89_WIFI_ROLE_P2P_CLIENT:
-		return BTC_WLINK_2G_GC;
+		if (notv10)
+			r->link_mode_v0 = BTC_WLINK_V0_2G_GC;
+		return BTC_WLINK_GC;
 	case RTW89_WIFI_ROLE_AP:
-		return BTC_WLINK_2G_AP;
-	default:
-		return BTC_WLINK_OTHER;
+		if (notv10)
+			r->link_mode_v0 = BTC_WLINK_V0_2G_AP;
+		return BTC_WLINK_AP;
 	}
 }
 
@@ -6891,13 +6498,13 @@ static bool _chk_role_ch_group(const struct rtw89_btc_chdef *r1,
 }
 
 static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch,
-		    u8 *phy, u8 *role, u8 link_cnt)
+		    u8 *phy, u8 *role, u8 link_cnt, bool notv10)
 {
 	struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl;
-	struct rtw89_btc_wl_role_info_v7 *rinfo_v7 = &wl->role_info_v7;
-	struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &wl->role_info_v8;
+	struct rtw89_btc_wl_role_info *rinfo = &wl->role_info;
 	bool is_2g_ch_exist = false, is_multi_role_in_2g_phy = false;
-	u8 j, k, dbcc_2g_cid, dbcc_2g_cid2, dbcc_2g_phy, pta_req_band;
+	u8 j, k, dbcc_2g_cid = 0, dbcc_2g_cid2 = 0;
+	u8 mode;
 
 	/* find out the 2G-PHY by connect-id ->ch  */
 	for (j = 0; j < link_cnt; j++) {
@@ -6908,34 +6515,31 @@ static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch,
 	}
 
 	/* If no any 2G-port exist, it's impossible because 5G-exclude */
-	if (!is_2g_ch_exist)
-		return BTC_WLINK_5G;
+	if (!is_2g_ch_exist) {
+		mode = BTC_WLINK_SB_MCC;
+		if (notv10)
+			rinfo->link_mode_v0 = BTC_WLINK_V0_5G;
+		return mode;
+	}
 
 	dbcc_2g_cid = j;
-	dbcc_2g_phy = phy[dbcc_2g_cid];
+	rinfo->dbcc_2g_phy = phy[dbcc_2g_cid];
 
-	if (dbcc_2g_phy == RTW89_PHY_1)
-		pta_req_band = RTW89_PHY_1;
+	if (rinfo->dbcc_2g_phy == RTW89_PHY_1)
+		rinfo->pta_req_band = RTW89_PHY_1;
 	else
-		pta_req_band = RTW89_PHY_0;
-
-	if (rtwdev->btc.ver->fwlrole == 7) {
-		rinfo_v7->dbcc_2g_phy = dbcc_2g_phy;
-	} else if (rtwdev->btc.ver->fwlrole == 8) {
-		rinfo_v8->dbcc_2g_phy = dbcc_2g_phy;
-		rinfo_v8->pta_req_band = pta_req_band;
-	}
+		rinfo->pta_req_band = RTW89_PHY_0;
 
 	/* connect_cnt <= 2 */
 	if (link_cnt < BTC_TDMA_WLROLE_MAX)
-		return (_get_role_link_mode((role[dbcc_2g_cid])));
+		return _get_role_link_mode(rinfo, role[dbcc_2g_cid], notv10);
 
 	/* find the other-port in the 2G-PHY, ex: PHY-0:6G, PHY1: mcc/scc */
 	for (k = 0; k < link_cnt; k++) {
 		if (k == dbcc_2g_cid)
 			continue;
 
-		if (phy[k] == dbcc_2g_phy) {
+		if (phy[k] == rinfo->dbcc_2g_phy) {
 			is_multi_role_in_2g_phy = true;
 			dbcc_2g_cid2 = k;
 			break;
@@ -6944,319 +6548,151 @@ static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch,
 
 	/* Single-role in 2G-PHY */
 	if (!is_multi_role_in_2g_phy)
-		return (_get_role_link_mode(role[dbcc_2g_cid]));
+		return _get_role_link_mode(rinfo, role[dbcc_2g_cid], notv10);
 
 	/* 2-role in 2G-PHY */
-	if (ch[dbcc_2g_cid2].center_ch > 14)
-		return BTC_WLINK_25G_MCC;
-	else if (_chk_role_ch_group(&ch[dbcc_2g_cid], &ch[dbcc_2g_cid2]))
-		return BTC_WLINK_2G_SCC;
-	else
-		return BTC_WLINK_2G_MCC;
-}
-
-static void _update_role_link_mode(struct rtw89_dev *rtwdev,
-				   bool client_joined, u32 noa)
-{
-	struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &rtwdev->btc.cx.wl.role_info_v8;
-	struct rtw89_btc_wl_role_info_v7 *rinfo_v7 = &rtwdev->btc.cx.wl.role_info_v7;
-	u8 role_ver = rtwdev->btc.ver->fwlrole;
-	u32 type = BTC_WLMROLE_NONE, dur = 0;
-	u8 link_mode, connect_cnt;
-	u32 wl_role;
-
-	if (role_ver == 7) {
-		wl_role = rinfo_v7->role_map;
-		link_mode = rinfo_v7->link_mode;
-		connect_cnt = rinfo_v7->connect_cnt;
-	} else if (role_ver == 8) {
-		wl_role = rinfo_v8->role_map;
-		link_mode = rinfo_v8->link_mode;
-		connect_cnt = rinfo_v8->connect_cnt;
+	if (ch[dbcc_2g_cid2].center_ch > 14) {
+		if (notv10)
+			rinfo->link_mode_v0 = BTC_WLINK_V0_25G_MCC;
+		return BTC_WLINK_DB_MCC;
+	} else if (_chk_role_ch_group(&ch[dbcc_2g_cid], &ch[dbcc_2g_cid2])) {
+		if (notv10)
+			rinfo->link_mode_v0 = BTC_WLINK_V0_2G_SCC;
+		return BTC_WLINK_SCC;
 	} else {
-		return;
-	}
-
-	/* if no client_joined, don't care P2P-GO/AP role */
-	if (((wl_role & BIT(RTW89_WIFI_ROLE_P2P_GO)) ||
-	     (wl_role & BIT(RTW89_WIFI_ROLE_AP))) && !client_joined) {
-		if (link_mode == BTC_WLINK_2G_SCC) {
-			if (role_ver == 7) {
-				rinfo_v7->link_mode = BTC_WLINK_2G_STA;
-				rinfo_v7->connect_cnt--;
-			} else if (role_ver == 8) {
-				rinfo_v8->link_mode = BTC_WLINK_2G_STA;
-				rinfo_v8->connect_cnt--;
-			}
-		} else if (link_mode == BTC_WLINK_2G_GO ||
-			   link_mode == BTC_WLINK_2G_AP) {
-			if (role_ver == 7) {
-				rinfo_v7->link_mode = BTC_WLINK_NOLINK;
-				rinfo_v7->connect_cnt--;
-			} else if (role_ver == 8) {
-				rinfo_v8->link_mode = BTC_WLINK_NOLINK;
-				rinfo_v8->connect_cnt--;
-			}
-		}
-	}
-
-	/* Identify 2-Role type  */
-	if (connect_cnt >= 2 &&
-	    (link_mode == BTC_WLINK_2G_SCC ||
-	     link_mode == BTC_WLINK_2G_MCC ||
-	     link_mode == BTC_WLINK_25G_MCC ||
-	     link_mode == BTC_WLINK_5G)) {
-		if ((wl_role & BIT(RTW89_WIFI_ROLE_P2P_GO)) ||
-		    (wl_role & BIT(RTW89_WIFI_ROLE_AP)))
-			type = noa ? BTC_WLMROLE_STA_GO_NOA : BTC_WLMROLE_STA_GO;
-		else if (wl_role & BIT(RTW89_WIFI_ROLE_P2P_CLIENT))
-			type = noa ? BTC_WLMROLE_STA_GC_NOA : BTC_WLMROLE_STA_GC;
-		else
-			type = BTC_WLMROLE_STA_STA;
-
-		dur = noa;
-	}
-
-	if (role_ver == 7) {
-		rinfo_v7->mrole_type = type;
-		rinfo_v7->mrole_noa_duration = dur;
-	} else if (role_ver == 8) {
-		rinfo_v8->mrole_type = type;
-		rinfo_v8->mrole_noa_duration = dur;
+		if (notv10)
+			rinfo->link_mode_v0 = BTC_WLINK_V0_2G_MCC;
+		return BTC_WLINK_SB_MCC;
 	}
 }
 
-static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid)
+static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type)
 {
-	struct rtw89_btc_chdef cid_ch[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER];
 	struct rtw89_btc *btc = &rtwdev->btc;
+	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_role_info_v7 *wl_rinfo = &wl->role_info_v7;
-	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
-	struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info;
-	struct rtw89_btc_wl_active_role_v7 *act_role = NULL;
-	u8 i, mode, cnt = 0, cnt_2g = 0, cnt_5g = 0, phy_now = RTW89_PHY_NUM, phy_dbcc;
-	bool b2g = false, b5g = false, client_joined = false, client_inc_2g = false;
-	u8 client_cnt_last[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {};
-	u8 cid_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {};
-	u8 cid_phy[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {};
-	u8 mac = RTW89_MAC_0, dbcc_2g_phy = RTW89_PHY_0;
-	u32 noa_duration = 0;
-
-	memset(wl_rinfo, 0, sizeof(*wl_rinfo));
-
-	for (i = 0; i < RTW89_PORT_NUM; i++) {
-		if (!wl_linfo[i].active || wl_linfo[i].phy >= RTW89_PHY_NUM)
-			continue;
-
-		act_role = &wl_rinfo->active_role[i];
-		act_role->role = wl_linfo[i].role;
-
-		/* check if role connect? */
-		if (wl_linfo[i].connected == MLME_NO_LINK) {
-			act_role->connected = 0;
-			continue;
-		} else if (wl_linfo[i].connected == MLME_LINKING) {
-			continue;
-		}
-
-		cnt++;
-		act_role->connected = 1;
-		act_role->pid = wl_linfo[i].pid;
-		act_role->phy = wl_linfo[i].phy;
-		act_role->band = wl_linfo[i].band;
-		act_role->ch = wl_linfo[i].ch;
-		act_role->bw = wl_linfo[i].bw;
-		act_role->noa = wl_linfo[i].noa;
-		act_role->noa_dur = wl_linfo[i].noa_duration;
-		cid_ch[cnt - 1] = wl_linfo[i].chdef;
-		cid_phy[cnt - 1] = wl_linfo[i].phy;
-		cid_role[cnt - 1] = wl_linfo[i].role;
-		wl_rinfo->role_map |= BIT(wl_linfo[i].role);
-
-		if (rid == i)
-			phy_now = act_role->phy;
-
-		if (wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO ||
-		    wl_linfo[i].role == RTW89_WIFI_ROLE_AP) {
-			if (wl_linfo[i].client_cnt > 1)
-				client_joined = true;
-			if (client_cnt_last[i] < wl_linfo[i].client_cnt &&
-			    wl_linfo[i].chdef.band == RTW89_BAND_2G)
-				client_inc_2g = true;
-			act_role->client_cnt = wl_linfo[i].client_cnt;
-		} else {
-			act_role->client_cnt = 0;
-		}
-
-		if (act_role->noa && act_role->noa_dur > 0)
-			noa_duration = act_role->noa_dur;
-
-		if (rtwdev->dbcc_en) {
-			phy_dbcc = wl_linfo[i].phy;
-			wl_dinfo->role[phy_dbcc] |= BIT(wl_linfo[i].role);
-			wl_dinfo->op_band[phy_dbcc] = wl_linfo[i].chdef.band;
-		}
-
-		if (wl_linfo[i].chdef.band != RTW89_BAND_2G) {
-			cnt_5g++;
-			b5g = true;
-		} else {
-			if (((wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO ||
-			      wl_linfo[i].role == RTW89_WIFI_ROLE_AP) &&
-			     client_joined) ||
-			    wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_CLIENT)
-				wl_rinfo->p2p_2g = 1;
-
-			if ((wl_linfo[i].mode & BIT(BTC_WL_MODE_11B)) ||
-			    (wl_linfo[i].mode & BIT(BTC_WL_MODE_11G)))
-				wl->bg_mode = 1;
-			else if (wl_linfo[i].mode & BIT(BTC_WL_MODE_HE))
-				wl->he_mode = true;
-
-			cnt_2g++;
-			b2g = true;
-		}
-
-		if (act_role->band == RTW89_BAND_5G && act_role->ch >= 100)
-			wl->is_5g_hi_channel = 1;
-		else
-			wl->is_5g_hi_channel = 0;
-	}
-
-	wl_rinfo->connect_cnt = cnt;
-	wl->client_cnt_inc_2g = client_inc_2g;
-
-	if (cnt == 0) {
-		mode = BTC_WLINK_NOLINK;
-		wl_rinfo->role_map = BIT(RTW89_WIFI_ROLE_NONE);
-	} else if (!b2g && b5g) {
-		mode = BTC_WLINK_5G;
-	} else if (wl_rinfo->role_map & BIT(RTW89_WIFI_ROLE_NAN)) {
-		mode = BTC_WLINK_2G_NAN;
-	} else if (cnt > BTC_TDMA_WLROLE_MAX) {
-		mode = BTC_WLINK_OTHER;
-	} else if (rtwdev->dbcc_en) {
-		mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, cnt);
-
-		/* correct 2G-located PHY band for gnt ctrl */
-		if (dbcc_2g_phy < RTW89_PHY_NUM)
-			wl_dinfo->op_band[dbcc_2g_phy] = RTW89_BAND_2G;
-	} else if (b2g && b5g && cnt == 2) {
-		mode = BTC_WLINK_25G_MCC;
-	} else if (!b5g && cnt == 2) { /* cnt_connect = 2 */
-		if (_chk_role_ch_group(&cid_ch[0], &cid_ch[cnt - 1]))
-			mode = BTC_WLINK_2G_SCC;
-		else
-			mode = BTC_WLINK_2G_MCC;
-	} else if (!b5g && cnt == 1) { /* cnt_connect = 1 */
-		mode = _get_role_link_mode(cid_role[0]);
-	} else {
-		mode = BTC_WLINK_NOLINK;
-	}
-
-	wl_rinfo->link_mode = mode;
-	_update_role_link_mode(rtwdev, client_joined, noa_duration);
-
-	/* todo DBCC related event */
-	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] wl_info phy_now=%d\n", phy_now);
-	rtw89_debug(rtwdev, RTW89_DBG_BTC,
-		    "[BTC] rlink cnt_2g=%d cnt_5g=%d\n", cnt_2g, cnt_5g);
-
-	if (wl_rinfo->dbcc_en != rtwdev->dbcc_en) {
-		wl_rinfo->dbcc_chg = 1;
-		wl_rinfo->dbcc_en = rtwdev->dbcc_en;
-		wl->wcnt[BTC_WCNT_DBCC_CHG]++;
-	}
-
-	if (rtwdev->dbcc_en) {
-		wl_rinfo->dbcc_2g_phy = dbcc_2g_phy;
-
-		if (dbcc_2g_phy == RTW89_PHY_1)
-			mac = RTW89_MAC_1;
-
-		_update_dbcc_band(rtwdev, RTW89_PHY_0);
-		_update_dbcc_band(rtwdev, RTW89_PHY_1);
-	}
-	_wl_req_mac(rtwdev, mac);
-	_fw_set_drv_info(rtwdev, CXDRVINFO_ROLE);
-}
-
-static u8 _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type)
-{
-	struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl;
 	struct rtw89_btc_wl_mlo_info *mlo_info = &wl->mlo_info;
-	u8 mode = BTC_WLINK_NOLINK;
+	struct rtw89_btc_wl_role_info *r = &wl->role_info;
+	u8 p2p_exist = wl->role_info.p2p_exist;
 
+	if (hw_band == RTW89_PHY_1)
+		p2p_exist = wl->role_info.p2p_exist_hb1;
+
+	/* MLD: nLmR --> 2L1R: 2-Link by 1-RF,  2L2R: 2-link by 2-RF */
 	switch (type) {
 	case RTW89_MR_WTYPE_NONE: /* no-link */
-		mode = BTC_WLINK_NOLINK;
+		r->link_mode = BTC_WLINK_NOLINK;
 		break;
 	case RTW89_MR_WTYPE_NONMLD:  /* Non_MLO 1-role 2+0/0+2 */
 	case RTW89_MR_WTYPE_MLD1L1R: /* MLO only-1 link 2+0/0+2 */
+		if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1AP) {
+			r->link_mode = BTC_WLINK_GO;
+		} else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1CLIENT &&
+			   p2p_exist) {
+			r->link_mode = BTC_WLINK_GC;
+		} else {
+			r->link_mode = BTC_WLINK_STA;
+		}
+
+		if (ver->fwlrole >= 10)
+			break;
+
 		if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) {
-			mode = BTC_WLINK_5G;
+			r->link_mode_v0 = BTC_WLINK_V0_5G;
 		} else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1AP) {
-			mode = BTC_WLINK_2G_GO;
+			r->link_mode_v0 = BTC_WLINK_V0_2G_GO;
 		} else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1CLIENT) {
-			if (wl->role_info_v8.p2p_2g)
-				mode = BTC_WLINK_2G_GC;
+			if (wl->role_info.p2p_2g)
+				r->link_mode_v0 = BTC_WLINK_V0_2G_GC;
 			else
-				mode = BTC_WLINK_2G_STA;
+				r->link_mode_v0 = BTC_WLINK_V0_2G_STA;
 		}
 		break;
 	case RTW89_MR_WTYPE_NONMLD_NONMLD: /* Non_MLO 2-role 2+0/0+2 */
 	case RTW89_MR_WTYPE_MLD1L1R_NONMLD: /* MLO only-1 link + P2P 2+0/0+2 */
+		if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_5GHZ ||
+		    mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_6GHZ) {
+			r->link_mode = BTC_WLINK_DB_MCC;
+		} else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ ||
+			   mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_5GHZ ||
+			   mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_6GHZ ||
+			   mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_5GHZ_6GHZ) {
+			r->link_mode = BTC_WLINK_SB_MCC;
+		} else {
+			r->link_mode = BTC_WLINK_SCC;
+		}
+
+		if (ver->fwlrole >= 10)
+			break;
+
 		if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) {
-			mode = BTC_WLINK_5G;
+			r->link_mode_v0 = BTC_WLINK_V0_5G;
 		} else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_5GHZ ||
 			   mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_6GHZ) {
-			mode = BTC_WLINK_25G_MCC;
+			r->link_mode_v0 = BTC_WLINK_V0_25G_MCC;
 		} else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ) {
-			mode = BTC_WLINK_2G_MCC;
+			r->link_mode_v0 = BTC_WLINK_V0_2G_MCC;
 		} else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX1_2GHZ) {
-			mode = BTC_WLINK_2G_SCC;
+			r->link_mode_v0 = BTC_WLINK_V0_2G_SCC;
 		}
 		break;
 	case RTW89_MR_WTYPE_MLD2L1R: /* MLO_MLSR 2+0/0+2 */
+		if (p2p_exist) /* MLO_MLSR only support STA/GC */
+			r->link_mode = BTC_WLINK_GC;
+		else
+			r->link_mode = BTC_WLINK_STA;
+
+		if (ver->fwlrole >= 10)
+			break;
+
 		if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G)
-			mode = BTC_WLINK_5G;
-		else if (wl->role_info_v8.p2p_2g)
-			mode = BTC_WLINK_2G_GC;
+			r->link_mode_v0 = BTC_WLINK_V0_5G;
+		else if (wl->role_info.p2p_2g)
+			r->link_mode_v0 = BTC_WLINK_V0_2G_GC;
 		else
-			mode = BTC_WLINK_2G_STA;
+			r->link_mode_v0 = BTC_WLINK_V0_2G_STA;
 		break;
 	case RTW89_MR_WTYPE_MLD2L1R_NONMLD: /* MLO_MLSR + P2P 2+0/0+2 */
-	case RTW89_MR_WTYPE_MLD2L2R_NONMLD: /* MLO_MLMR + P2P 1+1/2+2 */
-		/* driver may doze 1-link to
+	case RTW89_MR_WTYPE_MLD2L2R_NONMLD: /* MLO_MLMR + P2P 1+1/2+2*/
+		/* driver may doze 1-link (1+1) or 2+0->0+2->1+1
 		 * 2G+5G   -> TDMA slot switch by E2G/E5G
 		 * 5G only -> TDMA slot switch by E5G
 		 */
-		mode = BTC_WLINK_25G_MCC;
+		r->link_mode = BTC_WLINK_DB_MCC;
+
+		if (ver->fwlrole >= 10)
+			break;
+
+		r->link_mode_v0 = BTC_WLINK_V0_25G_MCC;
 		break;
 	case RTW89_MR_WTYPE_MLD2L2R: /* MLO_MLMR  1+1/2+2 */
+		/* MLMR only support STA now (2024) */
+		r->link_mode = BTC_WLINK_STA;
+
+		if (ver->fwlrole >= 10)
+			break;
+
 		if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) {
-			mode = BTC_WLINK_5G;
+			r->link_mode_v0 = BTC_WLINK_V0_5G;
 		} else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1AP) {
-			mode = BTC_WLINK_2G_GO;
+			r->link_mode_v0 = BTC_WLINK_V0_2G_GO;
 		} else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1CLIENT) {
-			if (wl->role_info_v8.p2p_2g)
-				mode = BTC_WLINK_2G_GC;
+			if (wl->role_info.p2p_2g)
+				r->link_mode_v0 = BTC_WLINK_V0_2G_GC;
 			else
-				mode = BTC_WLINK_2G_STA;
+				r->link_mode_v0 = BTC_WLINK_V0_2G_STA;
 		}
 		break;
 	}
-	return mode;
 }
 
-static void _update_wl_mlo_info(struct rtw89_dev *rtwdev)
+static void _update_wl_mlo_info(struct rtw89_dev *rtwdev, u8 hw_band)
 {
 	struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8;
+	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
 	struct rtw89_btc_wl_mlo_info *mlo_info = &wl->mlo_info;
 	struct rtw89_mr_chanctx_info qinfo;
-	u8 track_band = RTW89_PHY_0;
+	struct rtw89_chanctx *ch;
 	u8 rf_band = RTW89_BAND_2G;
 	u8 i, type;
 
@@ -7265,19 +6701,46 @@ static void _update_wl_mlo_info(struct rtw89_dev *rtwdev)
 		memset(&qinfo, 0, sizeof(qinfo));
 
 		rtw89_query_mr_chanctx_info(rtwdev, i, &qinfo);
+
+		switch (qinfo.ctxtype) {
+		default:
+			mlo_info->hwb_rf_band[i] = 0;
+			break;
+		case RTW89_MR_CTX1_2GHZ:
+		case RTW89_MR_CTX2_2GHZ:
+			mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_2G);
+			break;
+		case RTW89_MR_CTX1_5GHZ:
+		case RTW89_MR_CTX2_5GHZ:
+			mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_5G);
+			break;
+		case RTW89_MR_CTX1_6GHZ:
+		case RTW89_MR_CTX2_6GHZ:
+			mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_6G);
+			break;
+		case RTW89_MR_CTX2_2GHZ_5GHZ:
+			mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_2G) |
+						    BIT(RTW89_BAND_5G);
+			break;
+		case RTW89_MR_CTX2_2GHZ_6GHZ:
+			mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_2G) |
+						    BIT(RTW89_BAND_6G);
+			break;
+		case RTW89_MR_CTX2_5GHZ_6GHZ:
+			mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_5G) |
+						    BIT(RTW89_BAND_6G);
+			break;
+		}
 		mlo_info->wmode[i] = qinfo.wmode;
 		mlo_info->ch_type[i] = qinfo.ctxtype;
 		mlo_info->wtype = qinfo.wtype;
-
-		if (mlo_info->ch_type[i] == RTW89_MR_CTX1_5GHZ ||
-		    mlo_info->ch_type[i] == RTW89_MR_CTX2_5GHZ ||
-		    mlo_info->ch_type[i] == RTW89_MR_CTX2_5GHZ_6GHZ)
-			mlo_info->hwb_rf_band[i] = RTW89_BAND_5G;
-		else if (mlo_info->ch_type[i] == RTW89_MR_CTX1_6GHZ ||
-			 mlo_info->ch_type[i] == RTW89_MR_CTX2_6GHZ)
-			mlo_info->hwb_rf_band[i] = RTW89_BAND_6G;
-		else /* check if "2G-included" or unknown in each HW-band */
-			mlo_info->hwb_rf_band[i] = RTW89_BAND_2G;
+		wl->rf_band_map[i] = mlo_info->hwb_rf_band[i];
+		ch = &rtwdev->hal.chanctx[i];
+		wl->rf_ch_info[i].center_ch = ch->chan.channel;
+		wl->rf_ch_info[i].band = ch->chan.band_type;
+		wl->rf_ch_info[i].chan = ch->chan.channel;
+		wl->rf_ch_info[i].offset = ch->chan.pri_ch_idx;
+		wl->rf_ch_info[i].bw = ch->chan.band_width;
 	}
 
 	mlo_info->link_status = rtwdev->mlo_dbcc_mode;
@@ -7312,71 +6775,64 @@ static void _update_wl_mlo_info(struct rtw89_dev *rtwdev)
 	default:
 	case MLO_2_PLUS_0_1RF: /* 2+0 */
 	case MLO_2_PLUS_0_2RF:
-		mlo_info->rf_combination = BTC_MLO_RF_2_PLUS_0;
-		track_band = RTW89_MAC_0;
-		rf_band = mlo_info->hwb_rf_band[RTW89_MAC_0];
-		mlo_info->path_rf_band[BTC_RF_S0] = rf_band;
-		mlo_info->path_rf_band[BTC_RF_S1] = rf_band;
-
+		_update_wl_link_mode(rtwdev, RTW89_MAC_0, type);
+		wl_rinfo->link_mode_hb1 = wl_rinfo->link_mode;
 		wl_rinfo->pta_req_band = RTW89_MAC_0;
 		wl_rinfo->dbcc_2g_phy = RTW89_PHY_0;
 		wl_rinfo->dbcc_en = 0;
+
+		rf_band = wl->rf_band_map[RTW89_PHY_0];
+		mlo_info->path_rf_band[BTC_RF_S0] = rf_band;
+		mlo_info->path_rf_band[BTC_RF_S1] = rf_band;
 		break;
 	case MLO_0_PLUS_2_1RF: /* 0+2 */
 	case MLO_0_PLUS_2_2RF:
-		mlo_info->rf_combination = BTC_MLO_RF_0_PLUS_2;
-		track_band = RTW89_MAC_1;
-		rf_band = mlo_info->hwb_rf_band[RTW89_MAC_1];
-		mlo_info->path_rf_band[BTC_RF_S0] = rf_band;
-		mlo_info->path_rf_band[BTC_RF_S1] = rf_band;
-
+		_update_wl_link_mode(rtwdev, RTW89_MAC_1, type);
+		wl_rinfo->link_mode_hb1 = wl_rinfo->link_mode;
 		wl_rinfo->pta_req_band = RTW89_MAC_1;
 		wl_rinfo->dbcc_2g_phy = RTW89_PHY_1;
 		wl_rinfo->dbcc_en = 0;
+
+		rf_band = wl->rf_band_map[RTW89_PHY_1];
+		mlo_info->path_rf_band[BTC_RF_S0] = rf_band;
+		mlo_info->path_rf_band[BTC_RF_S1] = rf_band;
 		break;
 	case MLO_1_PLUS_1_1RF: /* 1+1 */
 	case MLO_1_PLUS_1_2RF: /* 1+1 */
 	case MLO_2_PLUS_2_2RF: /* 2+2 */
 	case DBCC_LEGACY: /* DBCC 1+1 */
-		if (mlo_info->link_status == MLO_2_PLUS_2_2RF)
-			mlo_info->rf_combination = BTC_MLO_RF_2_PLUS_2;
-		else
-			mlo_info->rf_combination = BTC_MLO_RF_1_PLUS_1;
-
-		if (mlo_info->hwb_rf_band[RTW89_MAC_0] == RTW89_BAND_2G)
-			track_band = RTW89_MAC_0;
+		/* Check ch count from ch_type and modify type*/
+		if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX1_2GHZ ||
+		    mlo_info->ch_type[hw_band] == RTW89_MR_CTX1_5GHZ ||
+		    mlo_info->ch_type[hw_band] == RTW89_MR_CTX1_6GHZ)
+			type = RTW89_MR_WTYPE_NONMLD; /* only 1-role */
 		else
-			track_band = RTW89_MAC_1;
+			type = RTW89_MR_WTYPE_NONMLD_NONMLD;
 
-		mlo_info->path_rf_band[BTC_RF_S0] =
-					mlo_info->hwb_rf_band[RTW89_MAC_0];
-		mlo_info->path_rf_band[BTC_RF_S1] =
-					mlo_info->hwb_rf_band[RTW89_MAC_1];
+		rf_band = wl->rf_band_map[hw_band];
+		_update_wl_link_mode(rtwdev, hw_band, type);
 
-		/* Check ch count from ch_type @ 2.4G HW-band, and modify type */
-		if (mlo_info->ch_type[track_band] == RTW89_MR_CTX1_2GHZ)
-			type = RTW89_MR_WTYPE_NONMLD; /* only 1-role at 2G */
-		else
-			type = RTW89_MR_WTYPE_NONMLD_NONMLD;
+		if (hw_band == RTW89_MAC_0) {
+			mlo_info->path_rf_band[BTC_RF_S0] = rf_band;
+		} else {
+			wl_rinfo->link_mode_hb1 = wl_rinfo->link_mode;
+			mlo_info->path_rf_band[BTC_RF_S1] = rf_band;
+		}
 
-		if (mlo_info->hwb_rf_band[RTW89_MAC_0] == RTW89_BAND_2G) {
+		/* set pta_req_band for 1-PTA architecture */
+		if (wl->rf_band_map[RTW89_MAC_0] & BIT(RTW89_BAND_2G)) {
 			wl_rinfo->pta_req_band = RTW89_MAC_0;
 			wl_rinfo->dbcc_2g_phy = RTW89_PHY_0;
-		} else {
+		} else if (wl->rf_band_map[RTW89_MAC_1] & BIT(RTW89_BAND_2G)) {
 			wl_rinfo->pta_req_band = RTW89_MAC_1;
 			wl_rinfo->dbcc_2g_phy = RTW89_PHY_1;
+		} else { /* Both HW-BAND are not on 2.4G*/
+			wl_rinfo->pta_req_band = RTW89_MAC_0;
+			wl_rinfo->dbcc_2g_phy = RTW89_PHY_0;
 		}
-
-		if (mlo_info->wmode[RTW89_MAC_0] == RTW89_MR_WMODE_NONE &&
-		    mlo_info->wmode[RTW89_MAC_1] == RTW89_MR_WMODE_NONE)
-			wl_rinfo->dbcc_en = 0;
-		else
-			wl_rinfo->dbcc_en = 1;
 		break;
 	}
 
-	wl_rinfo->link_mode = _update_wl_link_mode(rtwdev, track_band, type);
-
 	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(), mode=%s, pta_band=%d",
 		    __func__, id_to_linkmode(wl_rinfo->link_mode),
 		    wl_rinfo->pta_req_band);
@@ -7386,16 +6842,19 @@ static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev)
 {
 	struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl;
 	struct rtw89_btc_wl_rlink *rlink = NULL;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8;
+	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
 	struct rtw89_btc_chdef cid_ch[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {};
 	u8 cid_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {};
 	u8 cid_phy[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {};
 	bool b2g = false, b5g = false, outloop = false;
+	bool notv10 = rtwdev->btc.ver->fwlrole != 10;
+	u8 mode_v0 = BTC_WLINK_V0_NOLINK;
 	u8 mode = BTC_WLINK_NOLINK;
 	u8 cnt_2g = 0, cnt_5g = 0;
 	u8 i, j, cnt = 0;
 
 	for (j = RTW89_PHY_0; j < RTW89_PHY_NUM; j++) {
+		wl->rf_band_map[j] = 0; /* j=link id, is the link use which band */
 		for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) {
 			rlink = &wl_rinfo->rlink[i][j];
 
@@ -7419,6 +6878,7 @@ static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev)
 				cnt_2g++;
 				b2g = true;
 			}
+			wl->rf_band_map[j] |= BIT(rlink->rf_band);
 		}
 		if (outloop)
 			break;
@@ -7427,56 +6887,73 @@ static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev)
 	rtw89_debug(rtwdev, RTW89_DBG_BTC,
 		    "[BTC], %s(): cnt_2g=%d, cnt_5g=%d\n", __func__, cnt_2g, cnt_5g);
 
+	wl_rinfo->pta_req_band = RTW89_PHY_0; /* no 0+2 for non-MLO */
 	wl_rinfo->dbcc_en = rtwdev->dbcc_en;
+
 	/* Be careful to change the following sequence!! */
 	if (cnt == 0) {
 		mode = BTC_WLINK_NOLINK;
-	} else if (!b2g && b5g) {
-		mode = BTC_WLINK_5G;
+		mode_v0 = BTC_WLINK_V0_NOLINK;
 	} else if (wl_rinfo->dbcc_en) {
-		mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, cnt);
+		/* update pta_req_band to 2.4GHz HW-BAND for DBCC = 1*/
+		mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, cnt, notv10);
+		mode_v0 = wl_rinfo->link_mode_v0;
+	} else if (!b2g && b5g && notv10) {
+		mode_v0 = BTC_WLINK_V0_5G;
 	} else if (b2g && b5g) {
-		mode = BTC_WLINK_25G_MCC;
-	} else if (!b5g && cnt >= 2) {
-		if (_chk_role_ch_group(&cid_ch[0], &cid_ch[1]))
-			mode = BTC_WLINK_2G_SCC;
-		else
-			mode = BTC_WLINK_2G_MCC;
-	} else if (!b5g) { /* cnt_connect = 1 */
-		mode = _get_role_link_mode(cid_role[0]);
+		mode = BTC_WLINK_DB_MCC;
+		mode_v0 = BTC_WLINK_V0_25G_MCC;
+	} else if (cnt >= 2) {
+		if (_chk_role_ch_group(&cid_ch[0], &cid_ch[1])) {
+			mode = BTC_WLINK_SCC;
+			mode_v0 = BTC_WLINK_V0_2G_SCC;
+		} else {
+			mode = BTC_WLINK_SB_MCC;
+			mode_v0 = BTC_WLINK_V0_2G_MCC;
+		}
+	} else {
+		mode = _get_role_link_mode(wl_rinfo, cid_role[0], notv10);
 	}
 
 	wl_rinfo->link_mode = mode;
+	wl_rinfo->link_mode_hb1 = mode;
+	wl_rinfo->link_mode_v0 = mode_v0;
 }
 
-static void _modify_role_link_mode(struct rtw89_dev *rtwdev)
+static void _modify_role_link_mode(struct rtw89_dev *rtwdev, u8 hw_band)
 {
-	struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8;
+	struct rtw89_btc *btc = &rtwdev->btc;
+	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
 	u8 go_cleint_exist = wl->go_client_exist;
-	u8 link_mode = wl_rinfo->link_mode;
+	u8 *link_mode = &wl_rinfo->link_mode;
 	u32 role_map = wl_rinfo->role_map;
 	u8 noa_exist = wl->noa_exist;
 	u32 mrole = BTC_WLMROLE_NONE;
 
+	if (hw_band == RTW89_PHY_1) {
+		*link_mode = wl_rinfo->link_mode_hb1;
+		role_map = wl_rinfo->role_map_hb1;
+		go_cleint_exist = wl->go_client_exist_hb1;
+	}
+
 	/* if no client_joined, don't care P2P-GO/AP role */
 	if (((role_map & BIT(RTW89_WIFI_ROLE_P2P_GO)) ||
-	     (role_map & BIT(RTW89_WIFI_ROLE_AP))) && !go_cleint_exist) {
-		if (link_mode == BTC_WLINK_2G_SCC) {
-			wl_rinfo->link_mode = BTC_WLINK_2G_STA;
-		} else if (link_mode == BTC_WLINK_2G_GO ||
-			   link_mode == BTC_WLINK_2G_AP) {
-			wl_rinfo->link_mode = BTC_WLINK_NOLINK;
+	      (role_map & BIT(RTW89_WIFI_ROLE_AP))) && !go_cleint_exist) {
+		if (*link_mode == BTC_WLINK_SCC) {
+			*link_mode = BTC_WLINK_STA;
+		} else if (*link_mode == BTC_WLINK_GO ||
+			   *link_mode == BTC_WLINK_AP) {
+			*link_mode = BTC_WLINK_NOLINK;
 		}
 	}
 
-	/* Identify 2-Role type */
-	if  (link_mode == BTC_WLINK_2G_SCC ||
-	     link_mode == BTC_WLINK_2G_MCC ||
-	     link_mode == BTC_WLINK_25G_MCC ||
-	     link_mode == BTC_WLINK_5G) {
+	/* Identify 2-Role type  */
+	if  (*link_mode == BTC_WLINK_SCC ||
+	     *link_mode == BTC_WLINK_SB_MCC ||
+	     *link_mode == BTC_WLINK_DB_MCC) {
 		if ((role_map & BIT(RTW89_WIFI_ROLE_P2P_GO)) ||
-		    (role_map & BIT(RTW89_WIFI_ROLE_AP))) {
+		     (role_map & BIT(RTW89_WIFI_ROLE_AP))) {
 			if (noa_exist)
 				mrole = BTC_WLMROLE_STA_GO_NOA;
 			else
@@ -7489,127 +6966,147 @@ static void _modify_role_link_mode(struct rtw89_dev *rtwdev)
 		} else {
 			mrole = BTC_WLMROLE_STA_STA;
 		}
-	}
 
-	wl_rinfo->mrole_type = mrole;
+		if (hw_band == RTW89_PHY_0) {
+			wl_rinfo->mrole_type &= GENMASK(31, 16); /* clear Low-Word */
+			wl_rinfo->mrole_type |= mrole;
+		} else {
+			wl_rinfo->mrole_type &= GENMASK(15, 0); /* clear high-Word */
+			wl_rinfo->mrole_type |= (mrole << 16);
+		}
+	} else {
+		wl_rinfo->mrole_type = BTC_WLMROLE_NONE;
+	}
 
 	rtw89_debug(rtwdev, RTW89_DBG_BTC,
 		    "[BTC], %s(): link_mode=%s, mrole_type=%d\n", __func__,
 		    id_to_linkmode(wl_rinfo->link_mode), wl_rinfo->mrole_type);
 }
 
-static void _update_wl_info_v8(struct rtw89_dev *rtwdev, u8 role_id, u8 rlink_id,
-			       enum btc_role_state state)
+static void _update_wl_info(struct rtw89_dev *rtwdev, struct rtw89_btc_wl_link_info *wl_linfo)
 {
-	struct rtw89_btc_wl_rlink *rlink = NULL;
-	struct rtw89_btc_wl_link_info *wl_linfo;
 	struct rtw89_btc *btc = &rtwdev->btc;
+	struct rtw89_btc_dm *dm = &btc->dm;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8;
-	bool client_joined = false, noa_exist = false, p2p_exist = false;
-	bool is_5g_hi_channel = false, bg_mode = false, dbcc_en_ori;
-	u8 i, j, link_mode_ori;
+	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
+	struct rtw89_btc_wl_rlink *rlink = NULL;
+	u8 dbcc_en_ori, is_5g_hi_ch = 0, bg_mode = 0, p2p_exist = 0;
+	u8 go_client_exist = 0, noa_exist = 0;
+	u8 rlink_id = wl_linfo->phy;
+	u8 role_id = wl_linfo->pid;
+	u8 i, link_mode_ori;
 	u32 role_map = 0;
 
-	if (role_id >= RTW89_BE_BTC_WL_MAX_ROLE_NUMBER || rlink_id >= RTW89_MAC_NUM)
+	if (role_id >= RTW89_BE_BTC_WL_MAX_ROLE_NUMBER || rlink_id >= RTW89_BAND_NUM)
 		return;
 
-	/* Extract wl->link_info[role_id][rlink_id] to wl->role_info
+	/*
+	 * Extract wl->link_info[role_id][rlink_id] to wl->role_info
 	 * role_id: role index
 	 * rlink_id: rlink index (= HW-band index)
 	 * pid: port_index
 	 */
-
-	wl_linfo = &wl->rlink_info[role_id][rlink_id];
 	rlink = &wl_rinfo->rlink[role_id][rlink_id];
 
 	rlink->role = wl_linfo->role;
 	rlink->active = wl_linfo->active; /* Doze or not */
 	rlink->pid = wl_linfo->pid;
+	rlink->mac_id = wl_linfo->mac_id;
 	rlink->phy = wl_linfo->phy;
-	rlink->rf_band = wl_linfo->band;
-	rlink->ch = wl_linfo->ch;
-	rlink->bw = wl_linfo->bw;
+	rlink->rf_band = wl_linfo->chdef.band;
+	rlink->ch = wl_linfo->chdef.center_ch;
+	rlink->bw = wl_linfo->chdef.bw;
 	rlink->noa = wl_linfo->noa;
 	rlink->noa_dur = wl_linfo->noa_duration / 1000;
 	rlink->client_cnt = wl_linfo->client_cnt;
 	rlink->mode = wl_linfo->mode;
 
+	/* check if rlink connect? */
 	switch (wl_linfo->connected) {
 	case MLME_NO_LINK:
 		rlink->connected = 0;
 		break;
 	case MLME_LINKED:
 		rlink->connected = 1;
+
+		/* reset special-AP flag if station mode linked */
+		dm->leak_ap = 0;
 		break;
 	default:
 		return;
 	}
 
-	for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) {
-		for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) {
-			rlink = &wl_rinfo->rlink[i][j];
-
-			if (!rlink->active || !rlink->connected)
-				continue;
+	/* update by HW-Band */
+	for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { /* i = role_id */
+		rlink = &wl_rinfo->rlink[i][rlink_id];
 
-			role_map |= BIT(rlink->role);
+		if (!rlink->active || !rlink->connected)
+			continue;
 
-			/* only one noa-role exist */
-			if (rlink->noa && rlink->noa_dur > 0)
-				noa_exist = true;
+		role_map |= BIT(rlink->role);
 
-			/* for WL 5G-Rx interfered with BT issue */
-			if (rlink->rf_band == RTW89_BAND_5G) {
-				if (rlink->ch >= 100)
-					is_5g_hi_channel = true;
+		/* Identify if P2P-Go (GO/GC/AP) exist at 2GHz band */
+		if (((rlink->role == RTW89_WIFI_ROLE_P2P_GO ||
+		      rlink->role == RTW89_WIFI_ROLE_AP) &&
+		     rlink->client_cnt > 1)) {
+			p2p_exist = 1;
+			go_client_exist = 1;
+		}
 
-				continue;
-			}
+		if (rlink->role == RTW89_WIFI_ROLE_P2P_CLIENT)
+			p2p_exist = 1;
 
-			/* only if client connect for p2p-Go/AP */
-			if ((rlink->role == RTW89_WIFI_ROLE_P2P_GO ||
-			     rlink->role == RTW89_WIFI_ROLE_AP) &&
-			     rlink->client_cnt > 1) {
-				p2p_exist = true;
-				client_joined = true;
-			}
+		/* only one noa-role exist */
+		if (rlink->noa && rlink->noa_dur > 0)
+			noa_exist = 1;
 
-			/* Identify if P2P-Go (GO/GC/AP) exist at 2G band */
-			if (rlink->role == RTW89_WIFI_ROLE_P2P_CLIENT)
-				p2p_exist = true;
+		/* for WL 5G-Rx interfered with BT issue */
+		if (rlink->rf_band == RTW89_BAND_5G && rlink->ch >= 100)
+			is_5g_hi_ch = 1;
 
-			if ((rlink->mode & BIT(BTC_WL_MODE_11B)) ||
-			    (rlink->mode & BIT(BTC_WL_MODE_11G)))
-				bg_mode = true;
-		}
+		if ((rlink->mode & BIT(BTC_WL_MODE_11B)) ||
+		    (rlink->mode & BIT(BTC_WL_MODE_11G)))
+			bg_mode = 1;
 	}
 
-	link_mode_ori = wl_rinfo->link_mode;
-	wl->is_5g_hi_channel = is_5g_hi_channel;
-	wl->bg_mode = bg_mode;
-	wl->go_client_exist = client_joined;
-	wl->noa_exist = noa_exist;
-	wl_rinfo->p2p_2g = p2p_exist;
-	wl_rinfo->role_map = role_map;
+	if (rlink_id == RTW89_PHY_0) {
+		link_mode_ori = wl_rinfo->link_mode;
+		wl->is_5g_hi_ch = is_5g_hi_ch;
+		wl->bg_mode = bg_mode;
+		wl->go_client_exist = go_client_exist;
+		wl->noa_exist = noa_exist;
+		wl_rinfo->p2p_exist = p2p_exist;
+		wl_rinfo->role_map = role_map;
+	} else {
+		link_mode_ori = wl_rinfo->link_mode_hb1;
+		wl->is_5g_hi_ch_hb1 = is_5g_hi_ch;
+		wl->bg_mode_hb1 = bg_mode;
+		wl->go_client_exist_hb1 = go_client_exist;
+		wl->noa_exist_hb1 = noa_exist;
+		wl_rinfo->p2p_exist_hb1 = p2p_exist;
+		wl_rinfo->role_map_hb1 = role_map;
+	}
 
 	dbcc_en_ori = wl_rinfo->dbcc_en;
 
+	/* for MLO-supported, link-mode from driver directly */
 	if (rtwdev->chip->para_ver & BTC_FEAT_MLO_SUPPORT) {
-		/* for MLO-supported, link-mode from driver directly */
-		_update_wl_mlo_info(rtwdev);
-	} else {
-		/* for non-MLO-supported, link-mode by BTC */
+		_update_wl_mlo_info(rtwdev, rlink_id);
+	} else { /* for non-MLO-supported, link-mode by BTC */
 		_update_wl_non_mlo_info(rtwdev);
 	}
 
-	_modify_role_link_mode(rtwdev);
+	_modify_role_link_mode(rtwdev, rlink_id);
 
-	if (link_mode_ori != wl_rinfo->link_mode)
-		wl->link_mode_chg = true;
+	if ((rlink_id == RTW89_PHY_0 && wl_rinfo->link_mode != link_mode_ori) ||
+	    (rlink_id == RTW89_PHY_1 && wl_rinfo->link_mode_hb1 != link_mode_ori)) {
+		wl_rinfo->link_mode_chg = 1;
+		wl->link_mode_chg = 1;
+	}
 
 	if (wl_rinfo->dbcc_en != dbcc_en_ori) {
-		wl->dbcc_chg = true;
+		wl->dbcc_chg = 1;
+		wl->role_info.dbcc_chg = 1;
 		wl->wcnt[BTC_WCNT_DBCC_CHG]++;
 	}
 }
@@ -7685,25 +7182,21 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid)
 	struct rtw89_btc_dm *dm = &btc->dm;
 	struct rtw89_btc_bt_info *bt;
 	u32 val, any_bt_connect, any_bt_6g_connect = 0;
-	u8 id, id_start, id_stop, mode;
+	u8 id, id_start, id_stop, mode = 0;
 	bool bt_link_change = false;
 	bool lps_ctrl = false;
 
 	if (!rtwdev->chip->scbd || bid > BTC_ALL_BT)
 		return;
 
-	if (ver->fwlrole == 0)
-		mode = wl->role_info.link_mode;
-	else if (ver->fwlrole == 1)
-		mode = wl->role_info_v1.link_mode;
-	else if (ver->fwlrole == 2)
-		mode = wl->role_info_v2.link_mode;
-	else if (ver->fwlrole == 7)
-		mode = wl->role_info_v7.link_mode;
-	else if (ver->fwlrole == 8)
-		mode = wl->role_info_v8.link_mode;
-	else
-		return;
+	if (ver->fwlrole == 10) {
+		if (wl->role_info.link_mode == BTC_WLINK_STA &&
+		    (dm->tdd_en && wl->rf_band_map[RTW89_MAC_0] &
+		     BIT(RTW89_BAND_2G)))
+			mode = BTC_WLINK_V0_2G_STA;
+	} else {
+		mode = wl->role_info.link_mode_v0;
+	}
 
 	if (bid == BTC_ALL_BT) {
 		id_start = BTC_BT_1ST;
@@ -7796,7 +7289,7 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid)
 		if (((bt->link_info.a2dp_desc.exist ||
 		      bt->link_info.pan_desc.exist ||
 		      bt->link_info.hfp_desc.exist) &&
-		     mode == BTC_WLINK_2G_STA) ||
+		     mode == BTC_WLINK_V0_2G_STA) ||
 		    bt->whql_test)
 			lps_ctrl = true;
 
@@ -7900,7 +7393,12 @@ static void _set_bind_info(struct rtw89_btc *btc, u8 type)
 	 * HW-Band is decided by wl->mlo_info.mrcx_act_hwb_map
 	 */
 	if (wl->mlo_info.wtype == RTW89_MR_WTYPE_MLD2L1R_NONMLD) {
-		/* TODO: Should patched WiFi mode & WiFi role patch */
+		if (wl->role_info.link_mode != BTC_WLINK_DB_MCC) { /* mode chg */
+			if (wl->mlo_info.mrcx_act_hwb_map == BIT(RTW89_MAC_1))
+				path_hwb[BTC_RF_S0] = RTW89_PHY_1; /* S0/1->HWB1 */
+			else
+				path_hwb[BTC_RF_S1] = RTW89_PHY_0; /* S0/1->HWB0 */
+		}
 	} else {
 		/*
 		 * Dual-RF_band(HWB)TDMA if BT-profile is TDMA-type at both
@@ -7933,7 +7431,22 @@ static void _set_bind_info(struct rtw89_btc *btc, u8 type)
 		}
 	}
 
-	/* TODO: Should patched WiFi mode & WiFi role patch */
+	if (wl->mlo_info.wtype == RTW89_MR_WTYPE_MLD2L2R &&
+	    (bd->wl_hwb_sel == (BIT(RTW89_MAC_1) | BIT(RTW89_MAC_0)))) {
+		if (wl->role_info.link_mode == BTC_WLINK_AP ||
+		    wl->role_info.link_mode_hb1 == BTC_WLINK_AP)
+			bd->wl_link_mode = BTC_WLINK_DB_MCC;
+		else
+			bd->wl_link_mode = BTC_WLINK_STA;
+		bd->wl_bg_mode = wl->bg_mode | wl->bg_mode_hb1;
+	} else if (bd->wl_hwb_sel == (BIT(RTW89_MAC_1))) {
+		bd->wl_link_mode = wl->role_info.link_mode_hb1;
+		bd->wl_bg_mode = wl->bg_mode_hb1;
+	} else {/* HW-BAND-0 or no-hw-band */
+		bd->wl_hwb_sel = BIT(RTW89_MAC_0);
+		bd->wl_link_mode = wl->role_info.link_mode;
+		bd->wl_bg_mode = wl->bg_mode;
+	}
 
 	/* update Bind-BT status map for BT0/BT1 */
 	for (i = BTC_BT_1ST; i < BTC_ALL_BT_EZL; i++) {
@@ -7966,9 +7479,9 @@ static void _set_bind_info(struct rtw89_btc *btc, u8 type)
 			bd->bt_smap.a2dp_sink |= b->a2dp_desc.sink;
 			bd->bt_smap.pan_active |= b->pan_desc.active;
 			bd->bt_smap.connect |= b->status.map.connect;
-			bd->bt_smap.hid_cnt += (u8)b->hid_desc.pair_cnt;
+			bd->bt_smap.hid_cnt += b->hid_desc.pair_cnt;
 			bd->bt_smap.hid_type |= b->hid_desc.type;
-			bd->bt_smap.cis_cnt += (u8)b->leaudio_desc.cis_cnt;
+			bd->bt_smap.cis_cnt += b->leaudio_desc.cis_cnt;
 			bd->bt_smap.link_cnt += b->link_cnt.now;
 			bd->bt_smap.inq_page |= b->status.map.inq_pag;
 			bd->bt_smap.page |= b->pag;
@@ -8028,7 +7541,7 @@ static void _set_coex_binding(struct rtw89_btc *btc)
 	 * ==> 1: interference, 0: no-interference
 	 *
 	 * fdm_map(Frequency-Division-Multiplexing): WL/BT RF_Band overlap-map
-	 * 2bit-map: bit[1]:5GHz/6GHz, bit[0]:2.4GHz
+	 * 2bit-map: bit[1]:5/6GHz, bit[0]:2.4GHz
 	 */
 	for (i = 0; i < RTW89_PHY_NUM; i++) {
 		if (wl->rf_band_map[i] & BIT(RTW89_BAND_2G))
@@ -8050,9 +7563,7 @@ static void _set_coex_binding(struct rtw89_btc *btc)
 	 * In this case, BTC_RF_S0->HWB0, BTC_RF_S1->HWB1
 	 */
 	if (wl->mlo_info.wtype == RTW89_MR_WTYPE_MLD2L1R_NONMLD) {
-		if (wl->role_info.link_mode != BTC_WLINK_2G_MCC &&
-		    wl->role_info.link_mode != BTC_WLINK_25G_MCC &&
-		    wl->role_info.link_mode != BTC_WLINK_25G_DBCC) {/* mode chg */
+		if (wl->role_info.link_mode != BTC_WLINK_DB_MCC) {/* mode chg */
 			if (wl->mlo_info.mrcx_act_hwb_map == BIT(RTW89_PHY_1))
 				path_hwb[BTC_RF_S0] = RTW89_PHY_1;/* S0/1->HWB1 */
 			else
@@ -8114,31 +7625,14 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
 	struct rtw89_btc_cx *cx = &btc->cx;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
 	struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
-	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
-	struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1;
-	struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2;
-	struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8;
-	u8 mode, igno_bt, always_freerun;
+	u8 mode = btc->dm.tdd_bind.wl_link_mode;
+	u8 mode_v0 = wl->role_info.link_mode_v0;
+	u8 igno_bt, always_freerun;
 
 	lockdep_assert_wiphy(rtwdev->hw->wiphy);
 
 	dm->run_reason = reason;
 	_update_dm_step(rtwdev, reason);
-	_update_btc_state_map(rtwdev);
-
-	if (ver->fwlrole == 0)
-		mode = wl_rinfo->link_mode;
-	else if (ver->fwlrole == 1)
-		mode = wl_rinfo_v1->link_mode;
-	else if (ver->fwlrole == 2)
-		mode = wl_rinfo_v2->link_mode;
-	else if (ver->fwlrole == 7)
-		mode = wl_rinfo_v7->link_mode;
-	else if (ver->fwlrole == 8)
-		mode = wl_rinfo_v8->link_mode;
-	else
-		return;
 
 	if (ver->fcxctrl == 7) {
 		igno_bt = btc->ctrl.ctrl_v7.igno_bt;
@@ -8206,6 +7700,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
 	igno_bt = false;
 
 	_set_coex_binding(btc);
+	_update_btc_state_map(rtwdev);
 
 	dm->freerun_chk = _check_freerun(rtwdev); /* check if meet freerun */
 
@@ -8253,43 +7748,76 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
 		goto exit;
 	}
 
-	switch (mode) {
+	if (mode == BTC_WLINK_NOLINK) {
+		mode_v0 = BTC_WLINK_V0_NOLINK;
+	} else if (btc->dm.tdd_bind.rf_band == BIT(RTW89_BAND_5G)) {
+		mode_v0 = BTC_WLINK_V0_5G;
+	} else if (btc->dm.tdd_bind.rf_band & BIT(RTW89_BAND_5G) &&
+		   btc->dm.tdd_bind.rf_band & BIT(RTW89_BAND_2G)) {
+		mode_v0 = BTC_WLINK_V0_25G_MCC;
+	} else if (btc->dm.tdd_bind.rf_band == BIT(RTW89_BAND_2G)) {
+		switch (mode) {
+		case BTC_WLINK_STA:
+			mode_v0 = BTC_WLINK_V0_2G_STA;
+			break;
+		case BTC_WLINK_AP:
+			mode_v0 = BTC_WLINK_V0_2G_AP;
+			break;
+		case BTC_WLINK_GO:
+			mode_v0 = BTC_WLINK_V0_2G_GO;
+			break;
+		case BTC_WLINK_GC:
+			mode_v0 = BTC_WLINK_V0_2G_GC;
+			break;
+		case BTC_WLINK_SCC:
+			mode_v0 = BTC_WLINK_V0_2G_SCC;
+			break;
+		case BTC_WLINK_SB_MCC:
+			mode_v0 = BTC_WLINK_V0_2G_MCC;
+			break;
+		default:
+			mode_v0 = BTC_WLINK_V0_OTHER;
+			break;
+		}
+	}
+
+	switch (mode_v0) {
 	case BTC_WLINK_NOLINK:
 		_action_wl_nc(rtwdev);
 		break;
-	case BTC_WLINK_2G_STA:
+	case BTC_WLINK_V0_2G_STA:
 		if (wl->status.map.traffic_dir & BIT(RTW89_TFC_DL))
 			bt->scan_rx_low_pri = true;
 		_action_wl_2g_sta(rtwdev);
 		break;
-	case BTC_WLINK_2G_AP:
+	case BTC_WLINK_V0_2G_AP:
 		bt->scan_rx_low_pri = true;
 		_action_wl_2g_ap(rtwdev);
 		break;
-	case BTC_WLINK_2G_GO:
+	case BTC_WLINK_V0_2G_GO:
 		bt->scan_rx_low_pri = true;
 		_action_wl_2g_go(rtwdev);
 		break;
-	case BTC_WLINK_2G_GC:
+	case BTC_WLINK_V0_2G_GC:
 		bt->scan_rx_low_pri = true;
 		_action_wl_2g_gc(rtwdev);
 		break;
-	case BTC_WLINK_2G_SCC:
+	case BTC_WLINK_V0_2G_SCC:
 		bt->scan_rx_low_pri = true;
 		_action_wl_2g_scc(rtwdev);
 		break;
-	case BTC_WLINK_2G_MCC:
+	case BTC_WLINK_V0_2G_MCC:
 		bt->scan_rx_low_pri = true;
 		_action_wl_2g_mcc(rtwdev);
 		break;
-	case BTC_WLINK_25G_MCC:
+	case BTC_WLINK_V0_25G_MCC:
 		bt->scan_rx_low_pri = true;
 		_action_wl_25g_mcc(rtwdev);
 		break;
-	case BTC_WLINK_5G:
+	case BTC_WLINK_V0_5G:
 		_action_wl_5g(rtwdev);
 		break;
-	case BTC_WLINK_2G_NAN:
+	case BTC_WLINK_V0_2G_NAN:
 		_action_wl_2g_nan(rtwdev);
 		break;
 	default:
@@ -8678,22 +8206,6 @@ static u8 _update_bt_rssi_level(struct rtw89_dev *rtwdev, u8 rssi)
 	return rssi_level;
 }
 
-static void _update_zb_coex_tbl(struct rtw89_dev *rtwdev)
-{
-	u8 mode = rtwdev->btc.cx.wl.role_info.link_mode;
-	u32 zb_tbl0 = 0xda5a5a5a, zb_tbl1 = 0xda5a5a5a;
-
-	if (mode == BTC_WLINK_5G || rtwdev->btc.dm.freerun) {
-		zb_tbl0 = 0xffffffff;
-		zb_tbl1 = 0xffffffff;
-	} else if (mode == BTC_WLINK_25G_MCC) {
-		zb_tbl0 = 0xffffffff; /* for E5G slot */
-		zb_tbl1 = 0xda5a5a5a; /* for E2G slot */
-	}
-	rtw89_write32(rtwdev, R_BTC_ZB_COEX_TBL_0, zb_tbl0);
-	rtw89_write32(rtwdev, R_BTC_ZB_COEX_TBL_1, zb_tbl1);
-}
-
 #define BT_PROFILE_PROTOCOL_MASK GENMASK(7, 4)
 
 static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len)
@@ -8842,11 +8354,10 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev,
 	struct ieee80211_bss_conf *bss_conf;
 	struct ieee80211_link_sta *link_sta;
 	struct rtw89_btc *btc = &rtwdev->btc;
-	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
 	struct rtw89_btc_wl_link_info r = {0};
 	struct rtw89_btc_wl_link_info *wlinfo = NULL;
-	u8 mode = 0, rlink_id, link_mode_ori, pta_req_mac_ori, wa_type;
+	u8 mode = 0;
 
 	rcu_read_lock();
 
@@ -8894,66 +8405,32 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev,
 	rtw89_debug(rtwdev, RTW89_DBG_BTC,
 		    "[BTC], wifi_role=%d\n", rtwvif_link->wifi_role);
 
-	r.role = rtwvif_link->wifi_role;
-	r.phy = rtwvif_link->phy_idx;
-	r.pid = rtwvif_link->port;
-	r.active = true;
-	r.connected = MLME_LINKED;
-	r.bcn_period = bss_conf->beacon_int;
-	r.dtim_period = bss_conf->dtim_period;
-	r.band = chan->band_type;
-	r.ch = chan->channel;
-	r.bw = chan->band_width;
-	r.chdef.band = chan->band_type;
-	r.chdef.center_ch = chan->channel;
-	r.chdef.bw = chan->band_width;
-	r.chdef.chan = chan->primary_channel;
+	wlinfo = &wl->rlink_info[rtwvif_link->port][rtwvif_link->phy_idx];
+
+	wlinfo->mode = mode;
+	wlinfo->role = rtwvif_link->wifi_role;
+	wlinfo->phy = rtwvif_link->phy_idx;
+	wlinfo->pid = rtwvif_link->port;
+	wlinfo->active = true;
+	wlinfo->connected = MLME_LINKED;
+	wlinfo->bcn_period = bss_conf->beacon_int;
+	wlinfo->dtim_period = bss_conf->dtim_period;
+	wlinfo->band = chan->band_type;
+	wlinfo->ch = chan->channel;
+	wlinfo->bw = chan->band_width;
+	wlinfo->chdef.band = chan->band_type;
+	wlinfo->chdef.center_ch = chan->channel;
+	wlinfo->chdef.bw = chan->band_width;
+	wlinfo->chdef.chan = chan->primary_channel;
 	ether_addr_copy(r.mac_addr, rtwvif_link->mac_addr);
 
 	rcu_read_unlock();
 
 	if (rtwsta_link && vif->type == NL80211_IFTYPE_STATION)
-		r.mac_id = rtwsta_link->mac_id;
+		wlinfo->mac_id = rtwsta_link->mac_id;
 
 	btc->dm.cnt_notify[BTC_NCNT_ROLE_INFO]++;
 
-	wlinfo = &wl->link_info[r.pid];
-
-	if (ver->fwlrole == 0) {
-		*wlinfo = r;
-		_update_wl_info(rtwdev);
-	} else if (ver->fwlrole == 1) {
-		*wlinfo = r;
-		_update_wl_info_v1(rtwdev);
-	} else if (ver->fwlrole == 2) {
-		*wlinfo = r;
-		_update_wl_info_v2(rtwdev);
-	} else if (ver->fwlrole == 7) {
-		*wlinfo = r;
-		_update_wl_info_v7(rtwdev, r.pid);
-	} else if (ver->fwlrole == 8) {
-		rlink_id = rtwvif_link->mac_idx;
-		wlinfo = &wl->rlink_info[r.pid][rlink_id];
-		*wlinfo = r;
-		link_mode_ori = wl->role_info_v8.link_mode;
-		pta_req_mac_ori = wl->pta_req_mac;
-		_update_wl_info_v8(rtwdev, r.pid, rlink_id, state);
-
-		if (wl->role_info_v8.link_mode != link_mode_ori) {
-			wl->role_info_v8.link_mode_chg = 1;
-			if (ver->fcxinit == 7)
-				wa_type = btc->mdinfo.md_v7.wa_type;
-			else
-				wa_type = btc->mdinfo.md.wa_type;
-
-			if (wa_type & BTC_WA_HFP_ZB)
-				_update_zb_coex_tbl(rtwdev);
-		}
-
-		if (wl->pta_req_mac != pta_req_mac_ori)
-			wl->pta_reg_mac_chg = 1;
-	}
-
 	if (wlinfo->role == RTW89_WIFI_ROLE_STATION &&
 	    wlinfo->connected == MLME_NO_LINK)
 		btc->dm.leak_ap = 0;
@@ -8967,6 +8444,8 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev,
 	    state == BTC_ROLE_MSTS_STA_CONN_END)
 		wl->status.map._4way = false;
 
+	_update_wl_info(rtwdev, wlinfo);
+
 	_run_coex(rtwdev, BTC_RSN_NTFY_ROLE_INFO);
 }
 
@@ -9150,15 +8629,14 @@ void __rtw89_btc_ntfy_wl_sta_iter(struct rtw89_vif_link *rtwvif_link,
 	struct rtw89_dev *rtwdev = iter_data->rtwdev;
 	struct rtw89_btc *btc = &rtwdev->btc;
 	struct rtw89_btc_dm *dm = &btc->dm;
-	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
 	struct rtw89_btc_wl_link_info *link_info = NULL;
 	struct rtw89_traffic_stats *link_info_t = NULL;
 	struct rtw89_traffic_stats *stats = &rtwvif->stats;
 	const struct rtw89_chip_info *chip = rtwdev->chip;
 	struct rtw89_btc_wl_role_info *r;
-	struct rtw89_btc_wl_role_info_v1 *r1;
 	u32 last_tx_rate, last_rx_rate;
+	u8 link = rtwvif_link->phy_idx;
 	u16 last_tx_lvl, last_rx_lvl;
 	u8 port = rtwvif_link->port;
 	u8 rssi;
@@ -9171,10 +8649,7 @@ void __rtw89_btc_ntfy_wl_sta_iter(struct rtw89_vif_link *rtwvif_link,
 	rssi = ewma_rssi_read(&rtwsta_link->avg_rssi) >> RSSI_FACTOR;
 	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], rssi=%d\n", rssi);
 
-	if (btc->ver->fwlrole != 8)
-		link_info = &wl->link_info[port];
-	else
-		link_info = &wl->rlink_info[port][rtwvif_link->mac_idx];
+	link_info = &wl->rlink_info[port][link];
 
 	link_info->stat.traffic = *stats;
 	link_info_t = &link_info->stat.traffic;
@@ -9214,7 +8689,6 @@ void __rtw89_btc_ntfy_wl_sta_iter(struct rtw89_vif_link *rtwvif_link,
 	else
 		dir = RTW89_TFC_DL;
 
-	link_info = &wl->link_info[port];
 	if (link_info->busy != busy || link_info->dir != dir) {
 		is_sta_change = true;
 		link_info->busy = busy;
@@ -9244,19 +8718,11 @@ void __rtw89_btc_ntfy_wl_sta_iter(struct rtw89_vif_link *rtwvif_link,
 		dm->trx_info.rx_rate = link_info_t->rx_rate;
 	}
 
-	if (ver->fwlrole == 0) {
-		r = &wl->role_info;
-		r->active_role[port].tx_lvl = stats->tx_tfc_lv;
-		r->active_role[port].rx_lvl = stats->rx_tfc_lv;
-		r->active_role[port].tx_rate = rtwsta_link->ra_report.hw_rate;
-		r->active_role[port].rx_rate = rtwsta_link->rx_hw_rate;
-	} else if (ver->fwlrole == 1) {
-		r1 = &wl->role_info_v1;
-		r1->active_role_v1[port].tx_lvl = stats->tx_tfc_lv;
-		r1->active_role_v1[port].rx_lvl = stats->rx_tfc_lv;
-		r1->active_role_v1[port].tx_rate = rtwsta_link->ra_report.hw_rate;
-		r1->active_role_v1[port].rx_rate = rtwsta_link->rx_hw_rate;
-	}
+	r = &wl->role_info;
+	r->rlink[port][link].tx_lvl = stats->tx_tfc_lv;
+	r->rlink[port][link].rx_lvl = stats->rx_tfc_lv;
+	r->rlink[port][link].tx_rate = rtwsta_link->ra_report.hw_rate;
+	r->rlink[port][link].rx_rate = rtwsta_link->rx_hw_rate;
 
 	dm->trx_info.tx_lvl = stats->tx_tfc_lv;
 	dm->trx_info.rx_lvl = stats->rx_tfc_lv;
@@ -9584,10 +9050,7 @@ static int _show_wl_role_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 
 	for (i = 0; i < btc->ver->max_role_num; i++) {
 		for (j = 0; j < RTW89_MAC_NUM; j++) {
-			if (btc->ver->fwlrole == 8)
-				plink = &btc->cx.wl.rlink_info[i][j];
-			else
-				plink = &btc->cx.wl.link_info[i];
+			plink = &btc->cx.wl.rlink_info[i][j];
 
 			if (!plink->active)
 				continue;
@@ -9630,14 +9093,9 @@ static int _show_wl_role_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_cx *cx = &btc->cx;
 	struct rtw89_btc_wl_info *wl = &cx->wl;
 	struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
-	struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1;
-	struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2;
-	struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7;
-	struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8;
 	char *p = buf, *end = buf + bufsz;
 	u8 mode;
 
@@ -9646,18 +9104,7 @@ static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 
 	p += scnprintf(p, end - p, "========== [WL Status] ==========\n");
 
-	if (ver->fwlrole == 0)
-		mode = wl_rinfo->link_mode;
-	else if (ver->fwlrole == 1)
-		mode = wl_rinfo_v1->link_mode;
-	else if (ver->fwlrole == 2)
-		mode = wl_rinfo_v2->link_mode;
-	else if (ver->fwlrole == 7)
-		mode = wl_rinfo_v7->link_mode;
-	else if (ver->fwlrole == 8)
-		mode = wl_rinfo_v8->link_mode;
-	else
-		goto out;
+	mode = wl_rinfo->link_mode;
 
 	p += scnprintf(p, end - p, " %-15s : link_mode:%s, ", "[status]",
 		       id_to_linkmode(mode));
@@ -9677,7 +9124,6 @@ static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 
 	p += _show_wl_role_info(rtwdev, p, end - p);
 
-out:
 	return p - buf;
 }
 
@@ -9755,6 +9201,8 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 	union rtw89_btc_module_info *md = &btc->mdinfo;
 	s8 br_dbm = bt->link_info.bt_txpwr_desc.br_dbm;
 	s8 le_dbm = bt->link_info.bt_txpwr_desc.le_dbm;
+	u8 hw_band = wl->role_info.pta_req_band;
+	struct rtw89_btc_wl_afh_info *wl_afh;
 	char *p = buf, *end = buf + bufsz;
 	u8 *afh = bt_linfo->afh_map;
 	u8 *afh_le = bt_linfo->afh_map_le;
@@ -9822,8 +9270,9 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
 			       afh_le[0], afh_le[1], afh_le[2],
 			       afh_le[3], afh_le[4]);
 
-	p += scnprintf(p, end - p, "wl_ch_map[en:%d/ch:%d/bw:%d]\n",
-		       wl->afh_info.en, wl->afh_info.ch, wl->afh_info.bw);
+	wl_afh = &wl->afh_info[hw_band][RTW89_BAND_2G];
+	p += scnprintf(p, end - p, "wl_ch_map[hwb:%d/en:%d/ch:%d/bw:%d/rf_band:%d]\n",
+		       hw_band, wl_afh->en, wl_afh->ch, wl_afh->bw, wl_afh->band);
 
 	p += scnprintf(p, end - p,
 		       " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ",
diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h
index 74027ca4eccc..c127bd80d31c 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.h
+++ b/drivers/net/wireless/realtek/rtw89/coex.h
@@ -11,6 +11,40 @@
 #define BTC_TLV_SLOT_ID_LEN_V7 1
 #define BTC_SLOT_REQ_TH 2
 
+#define BTC_FREQ_W2G 2412
+#define BTC_FREQ_W5G 5005
+#define BTC_FREQ_W6G 5955
+
+#define BTC_FREQ_B2G 2402
+#define BTC_FREQ_B6G 5125
+
+#define BTC_FREQ_W2G_CH14 2484
+
+#define BTC_LO_2G_DNM 125 /* LO Coefficient Denominator */
+#define BTC_LO_5G_DNM 125
+#define BTC_LO_6G_DNM 150
+
+#define BTC_LO_2G_NMR 400 /* LO Coefficient Numerator */
+#define BTC_LO_5G_NMR 200
+#define BTC_LO_6G_NMR 200
+
+#define BTC_CH_W2G_MIN 1    /* start from 2412MHz */
+#define BTC_CH_W2G_MAX 14
+#define BTC_CH_W5G_MIN 1    /* start from 5005MHz */
+#define BTC_CH_W5G_MAX 165
+#define BTC_CH_W6G_MIN 1    /* start from 5955MHz */
+#define BTC_CH_W6G_MAX 233
+
+#define BTC_CH_B2G_MIN 0    /* start from 2402MHz */
+#define BTC_CH_B2G_MAX 78
+
+#define BTC_CH_B5G_MIN 600    /* start from 5725MHz */
+#define BTC_CH_B5G_MAX 1300
+
+#define BTC_VCO_GUARD_W2W 10 /* WiFi to WiFi VCO freq forbiddened range (MHZ) */
+#define BTC_VCO_GUARD_W2B 25 /* WiFi to WiFi VCO freq forbiddened range (MHZ) */
+#define BTC_VCO_GUARD_B2B 10 /* WiFi to WiFi VCO freq forbiddened range (MHZ) */
+
 enum btc_mode {
 	BTC_MODE_NORMAL,
 	BTC_MODE_WL,
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 174349c6bc58..ded788534651 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -1854,7 +1854,7 @@ union rtw89_btc_wl_role_info_map {
 	struct rtw89_btc_wl_role_info_bpos role;
 };
 
-struct rtw89_btc_wl_role_info { /* struct size must be n*4 bytes */
+struct rtw89_btc_wl_role_info_v0 { /* struct size must be n*4 bytes */
 	u8 connect_cnt;
 	u8 link_mode;
 	union rtw89_btc_wl_role_info_map role_map;
@@ -1891,7 +1891,7 @@ struct rtw89_btc_wl_role_info_v2 { /* struct size must be n*4 bytes */
 	u32 rsvd: 27;
 };
 
-struct rtw89_btc_wl_rlink { /* H2C info, struct size must be n*4 bytes */
+struct rtw89_btc_wl_rlink_v0 { /* H2C info, struct size must be n*4 bytes */
 	u8 connected;
 	u8 pid;
 	u8 phy;
@@ -1908,6 +1908,28 @@ struct rtw89_btc_wl_rlink { /* H2C info, struct size must be n*4 bytes */
 	u8 mode; /* wifi protocol */
 } __packed;
 
+struct rtw89_btc_wl_rlink_v10 { /* H2C info, struct size must be n*4 bytes */
+	u8 connected;
+	u8 pid;
+	u8 phy;
+	u8 noa;
+
+	u8 rf_band; /* enum band_type RF band: 2.4G/5G/6G */
+	u8 active; /* 0:rlink is under doze */
+	u8 bw; /* enum channel_width */
+	u8 role; /*enum role_type */
+
+	u8 ch;
+	u8 noa_dur; /* ms */
+	u8 client_cnt; /* for Role = P2P-Go/AP */
+	u8 mode; /* wifi protocol */
+
+	u8 mac_id;
+	u8 rsvd0;
+	u8 rsvd1;
+	u8 rsvd2;
+} __packed;
+
 #define RTW89_BE_BTC_WL_MAX_ROLE_NUMBER 6
 struct rtw89_btc_wl_role_info_v7 { /* struct size must be n*4 bytes */
 	u8 connect_cnt;
@@ -1917,12 +1939,12 @@ struct rtw89_btc_wl_role_info_v7 { /* struct size must be n*4 bytes */
 
 	struct rtw89_btc_wl_active_role_v7 active_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER];
 
-	u32 role_map;
-	u32 mrole_type; /* btc_wl_mrole_type */
-	u32 mrole_noa_duration; /* ms */
-	u32 dbcc_en;
-	u32 dbcc_chg;
-	u32 dbcc_2g_phy; /* which phy operate in 2G, HW_PHY_0 or HW_PHY_1 */
+	__le32 role_map;
+	__le32 mrole_type; /* btc_wl_mrole_type */
+	__le32 mrole_noa_duration; /* ms */
+	__le32 dbcc_en;
+	__le32 dbcc_chg;
+	__le32 dbcc_2g_phy; /* which phy operate in 2G, HW_PHY_0 or HW_PHY_1 */
 } __packed;
 
 struct rtw89_btc_wl_role_info_v8 { /* H2C info, struct size must be n*4 bytes */
@@ -1936,12 +1958,90 @@ struct rtw89_btc_wl_role_info_v8 { /* H2C info, struct size must be n*4 bytes */
 	u8 dbcc_chg;
 	u8 dbcc_2g_phy; /* which phy operate in 2G, HW_PHY_0 or HW_PHY_1 */
 
+	struct rtw89_btc_wl_rlink_v0 rlink[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER][RTW89_MAC_NUM];
+
+	__le32 role_map;
+	__le32 mrole_type; /* btc_wl_mrole_type */
+	__le32 mrole_noa_duration; /* ms */
+} __packed;
+
+struct rtw89_btc_wl_role_info_v10 { /* H2C info, struct size must be n*4 bytes */
+	struct rtw89_btc_wl_rlink_v10 rlink[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER][RTW89_MAC_NUM];
+	u8 link_mode;
+	u8 link_mode_hb1;
+	u8 p2p_exist;
+	u8 p2p_exist_hb1;
+
+	u8 pta_req_band;
+	u8 dbcc_en; /* 1+1 and 2.4G-included */
+	u8 dbcc_2g_phy; /* which phy operate in 2G, HW_PHY_0 or HW_PHY_1 */
+	u8 rsvd;
+
+	__le32 role_map;
+	__le32 role_map_hb1;
+	__le32 mrole_type; /* btc_wl_mrole_type: [31:16]:band1, [15:0]:band0 */
+} __packed;
+
+struct rtw89_btc_wl_rlink { /* Logic dynamic using */
+	u8 connected;
+	u8 pid;
+	u8 phy;
+	u8 noa;
+
+	u8 rf_band; /* enum band_type RF band: 2.4G/5G/6G */
+	u8 active; /* 0:rlink is under doze */
+	u8 bw; /* enum channel_width */
+	u8 role; /*enum role_type */
+
+	u8 ch;
+	u8 noa_dur; /* ms */
+	u8 client_cnt; /* for Role = P2P-Go/AP */
+	u8 mode; /* wifi protocol */
+
+	/*v0 v1*/
+	u16 tx_lvl;
+	u16 rx_lvl;
+	u16 tx_rate;
+	u16 rx_rate;
+
+	/* v7 */
+	u8 client_ps; /*v7 v2 v1 v0*/
+
+	/* v10 */
+	u8 mac_id;
+	u8 rsvd0;
+	u8 rsvd1;
+	u8 rsvd2;
+};
+
+struct rtw89_btc_wl_role_info { /* Logic dynamic using */
 	struct rtw89_btc_wl_rlink rlink[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER][RTW89_MAC_NUM];
+	u8 link_mode;
+	u8 link_mode_hb1;
+	u8 p2p_exist;
+	u8 p2p_exist_hb1;
+
+	u8 pta_req_band;
+	u8 dbcc_en; /* 1+1 and 2.4G-included */
+	u8 dbcc_2g_phy; /* which phy operate in 2G, HW_PHY_0 or HW_PHY_1 */
+	u8 rsvd;
 
 	u32 role_map;
-	u32 mrole_type; /* btc_wl_mrole_type */
-	u32 mrole_noa_duration; /* ms */
-} __packed;
+	u32 role_map_hb1;
+	u32 mrole_type; /* btc_wl_mrole_type: [31:16]:band1, [15:0]:band0 */
+
+	/* Before v10 use this linkmode */
+	u8 link_mode_v0;
+
+	/* v7 */
+	u8 connect_cnt;
+	u32 dbcc_chg; /* v7 v2*/
+
+	/* v8 */
+	u8 link_mode_chg; /* v8, v7 v2*/
+	u8 p2p_2g; /* v8, v7 */
+	u32 mrole_noa_duration; /* v8, v7 v2*/
+};
 
 struct rtw89_btc_wl_ver_info {
 	u32 fw_coex; /* match with which coex_ver */
@@ -1955,7 +2055,7 @@ struct rtw89_btc_wl_afh_info {
 	u8 en;
 	u8 ch;
 	u8 bw;
-	u8 rsvd;
+	u8 band;
 } __packed;
 
 struct rtw89_btc_wl_rfk_info {
@@ -2167,16 +2267,13 @@ struct rtw89_btc_wl_nhm {
 };
 
 struct rtw89_btc_wl_info {
-	struct rtw89_btc_wl_link_info link_info[RTW89_PORT_NUM];
 	struct rtw89_btc_wl_link_info rlink_info[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER][RTW89_MAC_NUM];
+	struct rtw89_btc_chdef rf_ch_info[RTW89_PHY_NUM];
 	struct rtw89_btc_wl_rfk_info rfk_info;
-	struct rtw89_btc_wl_ver_info  ver_info;
-	struct rtw89_btc_wl_afh_info afh_info;
+	struct rtw89_btc_wl_ver_info ver_info;
+	struct rtw89_btc_wl_afh_info afh_info[RTW89_MAC_NUM][RTW89_BAND_NUM];
+	struct rtw89_btc_wl_afh_info afh_info_last[RTW89_MAC_NUM][RTW89_BAND_NUM];
 	struct rtw89_btc_wl_role_info role_info;
-	struct rtw89_btc_wl_role_info_v1 role_info_v1;
-	struct rtw89_btc_wl_role_info_v2 role_info_v2;
-	struct rtw89_btc_wl_role_info_v7 role_info_v7;
-	struct rtw89_btc_wl_role_info_v8 role_info_v8;
 	struct rtw89_btc_wl_scan_info scan_info;
 	struct rtw89_btc_wl_dbcc_info dbcc_info;
 	struct rtw89_btc_wl_mlo_info mlo_info;
@@ -2191,12 +2288,18 @@ struct rtw89_btc_wl_info {
 	u8 pta_req_mac;
 	u8 bt_polut_type[RTW89_PHY_NUM]; /* BT polluted WL-Tx type for phy0/1  */
 	u8 rf_band_map[RTW89_PHY_NUM]; /* rf_band bit-map */
+	u8 ch_map[12];
+	u8 ch_map_le[5];
 
-	bool is_5g_hi_channel;
+	bool is_5g_hi_ch;
+	bool is_5g_hi_ch_hb1;
 	bool go_client_exist;
+	bool go_client_exist_hb1;
 	bool noa_exist;
+	bool noa_exist_hb1;
 	bool pta_reg_mac_chg;
 	bool bg_mode;
+	bool bg_mode_hb1;
 	bool he_mode;
 	bool scbd_chg[BTC_ALL_BT];
 	bool fw_ver_mismatch;
@@ -3280,6 +3383,14 @@ struct rtw89_btc_fbtc_outsrc_set_info {
 	u8 rf_gbt_source;
 	u8 bt_enable_state;
 	u8 wl_btg_standby_chg;
+	/* bit[15]-> 0:2G/1:5G,6G, bit[14:0]-> WL HWBx ch freq in MHz */
+	/* forbidden group-> bit[1]:fbd rf-band, bit[0]: fbd enable */
+	u8 fbd_group_en[RTW89_MAC_NUM][2]; /* HWB0/1 +.Group0/1 */
+	u16 rf_center_freq[RTW89_MAC_NUM]; /* HWB0/1 */
+	/* forbidden group boundary: [15:8]->UP, [7:0]->LO */
+	u16 fbd_group_bound[RTW89_MAC_NUM][2]; /* HWB0/1 +.Group0/1 */
+	/* 11-bit in MHz, freq diff threshold */
+	u16 freq_diff_thres[RTW89_MAC_NUM][BTC_ALL_BT_EZL]; /* HWB0/1 vs.BT0/1/2 */
 } __packed;
 
 union rtw89_btc_fbtc_slot_u {
@@ -3363,6 +3474,7 @@ struct rtw89_btc_dm {
 	u8 lps_ctrl_change: 1;
 	u8 scbd_write_instant;
 	bool scbd_b2w_update;
+	bool pre_agc_chg;
 };
 
 struct rtw89_btc_ctrl {
@@ -4961,6 +5073,7 @@ struct rtw89_chip_info {
 	u8 mailbox;
 
 	u8 afh_guard_ch;
+	u16 fdd_iso_freq;
 	const u8 *wl_rssi_thres;
 	const u8 *bt_rssi_thres;
 	u8 rssi_tol;
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 0e7168605850..41c033d2ae7b 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -5922,14 +5922,14 @@ int rtw89_fw_h2c_cxdrv_init_v10(struct rtw89_dev *rtwdev, u8 type)
 int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev, u8 type)
 {
 	struct rtw89_btc *btc = &rtwdev->btc;
-	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_role_info *role_info = &wl->role_info;
-	struct rtw89_btc_wl_role_info_bpos *bpos = &role_info->role_map.role;
-	struct rtw89_btc_wl_active_role *active = role_info->active_role;
+	struct rtw89_btc_wl_role_info *r = &wl->role_info;
+	const struct rtw89_btc_ver *ver = btc->ver;
+	struct rtw89_btc_wl_rlink *rl;
 	struct sk_buff *skb;
-	u32 len;
+	u32 rmap = r->role_map;
 	u8 offset = 0;
+	u32 len;
 	u8 *cmd;
 	int ret;
 	int i;
@@ -5947,36 +5947,37 @@ int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev, u8 type)
 	RTW89_SET_FWCMD_CXHDR_TYPE(cmd, type);
 	RTW89_SET_FWCMD_CXHDR_LEN(cmd, len - H2C_LEN_CXDRVHDR);
 
-	RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, role_info->connect_cnt);
-	RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, role_info->link_mode);
-
-	RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, bpos->none);
-	RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, bpos->station);
-	RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, bpos->ap);
-	RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, bpos->vap);
-	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, bpos->adhoc);
-	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, bpos->adhoc_master);
-	RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, bpos->mesh);
-	RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, bpos->moniter);
-	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, bpos->p2p_device);
-	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, bpos->p2p_gc);
-	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, bpos->p2p_go);
-	RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, bpos->nan);
-
-	for (i = 0; i < RTW89_PORT_NUM; i++, active++) {
-		RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED(cmd, active->connected, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_PID(cmd, active->pid, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_PHY(cmd, active->phy, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_NOA(cmd, active->noa, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_BAND(cmd, active->band, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS(cmd, active->client_ps, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_BW(cmd, active->bw, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_ROLE(cmd, active->role, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_CH(cmd, active->ch, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_TX_LVL(cmd, active->tx_lvl, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_RX_LVL(cmd, active->rx_lvl, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_TX_RATE(cmd, active->tx_rate, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_RX_RATE(cmd, active->rx_rate, i, offset);
+	RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, r->connect_cnt);
+	RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode);
+
+	RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NONE)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_STATION)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP_VLAN)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC_MASTER)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MESH_POINT)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MONITOR)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_DEVICE)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_CLIENT)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_GO)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NAN)));
+
+	for (i = 0; i < RTW89_PORT_NUM; i++) {
+		rl = &r->rlink[i][RTW89_MAC_0];
+		RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED(cmd, rl->connected, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_PID(cmd, rl->pid, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_PHY(cmd, rl->phy, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_NOA(cmd, rl->noa, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_BAND(cmd, rl->rf_band, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS(cmd, rl->client_ps, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_BW(cmd, rl->bw, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_ROLE(cmd, rl->role, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_CH(cmd, rl->ch, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_TX_LVL(cmd, rl->tx_lvl, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_RX_LVL(cmd, rl->rx_lvl, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_TX_RATE(cmd, rl->tx_rate, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_RX_RATE(cmd, rl->rx_rate, i, offset);
 	}
 
 	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
@@ -6005,12 +6006,12 @@ int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type)
 	struct rtw89_btc *btc = &rtwdev->btc;
 	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_role_info_v1 *role_info = &wl->role_info_v1;
-	struct rtw89_btc_wl_role_info_bpos *bpos = &role_info->role_map.role;
-	struct rtw89_btc_wl_active_role_v1 *active = role_info->active_role_v1;
+	struct rtw89_btc_wl_role_info *r = &wl->role_info;
+	struct rtw89_btc_wl_rlink *rl;
 	struct sk_buff *skb;
-	u32 len;
+	u32 rmap = r->role_map;
 	u8 *cmd, offset;
+	u32 len;
 	int ret;
 	int i;
 
@@ -6027,47 +6028,49 @@ int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type)
 	RTW89_SET_FWCMD_CXHDR_TYPE(cmd, type);
 	RTW89_SET_FWCMD_CXHDR_LEN(cmd, len - H2C_LEN_CXDRVHDR);
 
-	RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, role_info->connect_cnt);
-	RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, role_info->link_mode);
-
-	RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, bpos->none);
-	RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, bpos->station);
-	RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, bpos->ap);
-	RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, bpos->vap);
-	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, bpos->adhoc);
-	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, bpos->adhoc_master);
-	RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, bpos->mesh);
-	RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, bpos->moniter);
-	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, bpos->p2p_device);
-	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, bpos->p2p_gc);
-	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, bpos->p2p_go);
-	RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, bpos->nan);
+	RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, r->connect_cnt);
+	RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode);
+
+	RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NONE)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_STATION)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP_VLAN)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC_MASTER)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MESH_POINT)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MONITOR)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_DEVICE)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_CLIENT)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_GO)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NAN)));
 
 	offset = PORT_DATA_OFFSET;
-	for (i = 0; i < RTW89_PORT_NUM; i++, active++) {
-		RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED(cmd, active->connected, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_PID(cmd, active->pid, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_PHY(cmd, active->phy, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_NOA(cmd, active->noa, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_BAND(cmd, active->band, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS(cmd, active->client_ps, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_BW(cmd, active->bw, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_ROLE(cmd, active->role, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_CH(cmd, active->ch, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_TX_LVL(cmd, active->tx_lvl, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_RX_LVL(cmd, active->rx_lvl, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_TX_RATE(cmd, active->tx_rate, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_RX_RATE(cmd, active->rx_rate, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_NOA_DUR(cmd, active->noa_duration, i, offset);
+
+	for (i = 0; i < RTW89_PORT_NUM; i++) {
+		rl = &r->rlink[i][RTW89_MAC_0];
+		RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED(cmd, rl->connected, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_PID(cmd, rl->pid, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_PHY(cmd, rl->phy, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_NOA(cmd, rl->noa, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_BAND(cmd, rl->rf_band, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS(cmd, rl->client_ps, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_BW(cmd, rl->bw, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_ROLE(cmd, rl->role, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_CH(cmd, rl->ch, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_TX_LVL(cmd, rl->tx_lvl, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_RX_LVL(cmd, rl->rx_lvl, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_TX_RATE(cmd, rl->tx_rate, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_RX_RATE(cmd, rl->rx_rate, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_NOA_DUR(cmd, rl->noa_dur, i, offset);
 	}
 
 	offset = len - H2C_LEN_CXDRVINFO_ROLE_DBCC_LEN;
-	RTW89_SET_FWCMD_CXROLE_MROLE_TYPE(cmd, role_info->mrole_type, offset);
-	RTW89_SET_FWCMD_CXROLE_MROLE_NOA(cmd, role_info->mrole_noa_duration, offset);
-	RTW89_SET_FWCMD_CXROLE_DBCC_EN(cmd, role_info->dbcc_en, offset);
-	RTW89_SET_FWCMD_CXROLE_DBCC_CHG(cmd, role_info->dbcc_chg, offset);
-	RTW89_SET_FWCMD_CXROLE_DBCC_2G_PHY(cmd, role_info->dbcc_2g_phy, offset);
-	RTW89_SET_FWCMD_CXROLE_LINK_MODE_CHG(cmd, role_info->link_mode_chg, offset);
+	RTW89_SET_FWCMD_CXROLE_MROLE_TYPE(cmd, r->mrole_type, offset);
+	RTW89_SET_FWCMD_CXROLE_MROLE_NOA(cmd, r->mrole_noa_duration, offset);
+	RTW89_SET_FWCMD_CXROLE_DBCC_EN(cmd, r->dbcc_en, offset);
+	RTW89_SET_FWCMD_CXROLE_DBCC_CHG(cmd, r->dbcc_chg, offset);
+	RTW89_SET_FWCMD_CXROLE_DBCC_2G_PHY(cmd, r->dbcc_2g_phy, offset);
+	RTW89_SET_FWCMD_CXROLE_LINK_MODE_CHG(cmd, r->link_mode_chg, offset);
 
 	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
 			      H2C_CAT_OUTSRC, BTFC_SET,
@@ -6095,10 +6098,10 @@ int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type)
 	struct rtw89_btc *btc = &rtwdev->btc;
 	const struct rtw89_btc_ver *ver = btc->ver;
 	struct rtw89_btc_wl_info *wl = &btc->cx.wl;
-	struct rtw89_btc_wl_role_info_v2 *role_info = &wl->role_info_v2;
-	struct rtw89_btc_wl_role_info_bpos *bpos = &role_info->role_map.role;
-	struct rtw89_btc_wl_active_role_v2 *active = role_info->active_role_v2;
+	struct rtw89_btc_wl_role_info *r = &wl->role_info;
+	struct rtw89_btc_wl_rlink *rl;
 	struct sk_buff *skb;
+	u32 rmap = r->role_map;
 	u32 len;
 	u8 *cmd, offset;
 	int ret;
@@ -6117,43 +6120,45 @@ int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type)
 	RTW89_SET_FWCMD_CXHDR_TYPE(cmd, type);
 	RTW89_SET_FWCMD_CXHDR_LEN(cmd, len - H2C_LEN_CXDRVHDR);
 
-	RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, role_info->connect_cnt);
-	RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, role_info->link_mode);
-
-	RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, bpos->none);
-	RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, bpos->station);
-	RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, bpos->ap);
-	RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, bpos->vap);
-	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, bpos->adhoc);
-	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, bpos->adhoc_master);
-	RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, bpos->mesh);
-	RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, bpos->moniter);
-	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, bpos->p2p_device);
-	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, bpos->p2p_gc);
-	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, bpos->p2p_go);
-	RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, bpos->nan);
+	RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, r->connect_cnt);
+	RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode);
+
+	RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NONE)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_STATION)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP_VLAN)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC_MASTER)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MESH_POINT)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MONITOR)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_DEVICE)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_CLIENT)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_GO)));
+	RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NAN)));
 
 	offset = PORT_DATA_OFFSET;
-	for (i = 0; i < RTW89_PORT_NUM; i++, active++) {
-		RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED_V2(cmd, active->connected, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_PID_V2(cmd, active->pid, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_PHY_V2(cmd, active->phy, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_NOA_V2(cmd, active->noa, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_BAND_V2(cmd, active->band, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS_V2(cmd, active->client_ps, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_BW_V2(cmd, active->bw, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_ROLE_V2(cmd, active->role, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_CH_V2(cmd, active->ch, i, offset);
-		RTW89_SET_FWCMD_CXROLE_ACT_NOA_DUR_V2(cmd, active->noa_duration, i, offset);
+
+	for (i = 0; i < RTW89_PORT_NUM; i++) {
+		rl = &r->rlink[i][RTW89_MAC_0];
+		RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED_V2(cmd, rl->connected, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_PID_V2(cmd, rl->pid, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_PHY_V2(cmd, rl->phy, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_NOA_V2(cmd, rl->noa, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_BAND_V2(cmd, rl->rf_band, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS_V2(cmd, rl->client_ps, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_BW_V2(cmd, rl->bw, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_ROLE_V2(cmd, rl->role, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_CH_V2(cmd, rl->ch, i, offset);
+		RTW89_SET_FWCMD_CXROLE_ACT_NOA_DUR_V2(cmd, rl->noa_dur, i, offset);
 	}
 
 	offset = len - H2C_LEN_CXDRVINFO_ROLE_DBCC_LEN;
-	RTW89_SET_FWCMD_CXROLE_MROLE_TYPE(cmd, role_info->mrole_type, offset);
-	RTW89_SET_FWCMD_CXROLE_MROLE_NOA(cmd, role_info->mrole_noa_duration, offset);
-	RTW89_SET_FWCMD_CXROLE_DBCC_EN(cmd, role_info->dbcc_en, offset);
-	RTW89_SET_FWCMD_CXROLE_DBCC_CHG(cmd, role_info->dbcc_chg, offset);
-	RTW89_SET_FWCMD_CXROLE_DBCC_2G_PHY(cmd, role_info->dbcc_2g_phy, offset);
-	RTW89_SET_FWCMD_CXROLE_LINK_MODE_CHG(cmd, role_info->link_mode_chg, offset);
+	RTW89_SET_FWCMD_CXROLE_MROLE_TYPE(cmd, r->mrole_type, offset);
+	RTW89_SET_FWCMD_CXROLE_MROLE_NOA(cmd, r->mrole_noa_duration, offset);
+	RTW89_SET_FWCMD_CXROLE_DBCC_EN(cmd, r->dbcc_en, offset);
+	RTW89_SET_FWCMD_CXROLE_DBCC_CHG(cmd, r->dbcc_chg, offset);
+	RTW89_SET_FWCMD_CXROLE_DBCC_2G_PHY(cmd, r->dbcc_2g_phy, offset);
+	RTW89_SET_FWCMD_CXROLE_LINK_MODE_CHG(cmd, r->link_mode_chg, offset);
 
 	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
 			      H2C_CAT_OUTSRC, BTFC_SET,
@@ -6175,12 +6180,14 @@ int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type)
 
 int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type)
 {
-	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_wl_role_info_v7 *role = &btc->cx.wl.role_info_v7;
+	struct rtw89_btc_wl_role_info *r = &rtwdev->btc.cx.wl.role_info;
 	struct rtw89_h2c_cxrole_v7 *h2c;
+	struct rtw89_btc_wl_rlink *rl;
 	u32 len = sizeof(*h2c);
 	struct sk_buff *skb;
+	__le32 rm;
 	int ret;
+	u8 i;
 
 	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
 	if (!skb) {
@@ -6190,16 +6197,35 @@ int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type)
 	skb_put(skb, len);
 	h2c = (struct rtw89_h2c_cxrole_v7 *)skb->data;
 
-	h2c->hdr.type = type;
-	h2c->hdr.ver = btc->ver->fwlrole;
-	h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7;
-	memcpy(&h2c->_u8, role, sizeof(h2c->_u8));
-	h2c->_u32.role_map = cpu_to_le32(role->role_map);
-	h2c->_u32.mrole_type = cpu_to_le32(role->mrole_type);
-	h2c->_u32.mrole_noa_duration = cpu_to_le32(role->mrole_noa_duration);
-	h2c->_u32.dbcc_en = cpu_to_le32(role->dbcc_en);
-	h2c->_u32.dbcc_chg = cpu_to_le32(role->dbcc_chg);
-	h2c->_u32.dbcc_2g_phy = cpu_to_le32(role->dbcc_2g_phy);
+	h2c->r.connect_cnt = r->connect_cnt;
+	h2c->r.link_mode = r->link_mode;
+	h2c->r.link_mode_chg = r->link_mode_chg;
+	h2c->r.p2p_2g = r->p2p_2g;
+
+	for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) {
+		rl = &r->rlink[i][RTW89_MAC_0];
+		h2c->r.active_role[i].connected = rl->connected;
+		h2c->r.active_role[i].pid = rl->pid;
+		h2c->r.active_role[i].phy = rl->phy;
+		h2c->r.active_role[i].noa = rl->noa;
+
+		h2c->r.active_role[i].band = rl->rf_band;
+		h2c->r.active_role[i].client_ps = rl->client_ps;
+		h2c->r.active_role[i].bw = rl->bw;
+		h2c->r.active_role[i].role = rl->role;
+
+		h2c->r.active_role[i].ch = rl->ch;
+		h2c->r.active_role[i].noa_dur = rl->noa_dur;
+		h2c->r.active_role[i].client_cnt = rl->client_cnt;
+	}
+
+	rm = cpu_to_le32(r->role_map);
+	memcpy(&h2c->r.role_map, &rm, sizeof(h2c->r.role_map));
+	h2c->r.mrole_type = cpu_to_le32(r->mrole_type);
+	h2c->r.mrole_noa_duration = cpu_to_le32(r->mrole_noa_duration);
+	h2c->r.dbcc_en = cpu_to_le32((u32)r->dbcc_en);
+	h2c->r.dbcc_chg = cpu_to_le32(r->dbcc_chg);
+	h2c->r.dbcc_2g_phy = cpu_to_le32(r->dbcc_2g_phy);
 
 	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
 			      H2C_CAT_OUTSRC, BTFC_SET,
@@ -6221,12 +6247,14 @@ int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type)
 
 int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type)
 {
-	struct rtw89_btc *btc = &rtwdev->btc;
-	struct rtw89_btc_wl_role_info_v8 *role = &btc->cx.wl.role_info_v8;
+	struct rtw89_btc_wl_role_info *r = &rtwdev->btc.cx.wl.role_info;
 	struct rtw89_h2c_cxrole_v8 *h2c;
+	struct rtw89_btc_wl_rlink *rl;
 	u32 len = sizeof(*h2c);
 	struct sk_buff *skb;
+	__le32 rm;
 	int ret;
+	u8 i, j;
 
 	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
 	if (!skb) {
@@ -6237,12 +6265,118 @@ int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type)
 	h2c = (struct rtw89_h2c_cxrole_v8 *)skb->data;
 
 	h2c->hdr.type = type;
-	h2c->hdr.ver = btc->ver->fwlrole;
+	h2c->hdr.ver = rtwdev->btc.ver->fwlrole;
+	h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7;
+	h2c->r.connect_cnt = r->connect_cnt;
+	h2c->r.link_mode = r->link_mode;
+	h2c->r.link_mode_chg =  r->link_mode_chg;
+	h2c->r.p2p_2g = r->p2p_2g;
+	h2c->r.pta_req_band = r->pta_req_band;
+	h2c->r.dbcc_en = r->dbcc_en;
+	h2c->r.dbcc_chg = r->dbcc_chg;
+	h2c->r.dbcc_2g_phy = r->dbcc_2g_phy;
+
+	for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) {
+		for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) {
+			rl = &r->rlink[i][j];
+			h2c->r.rlink[i][j].connected = rl->connected;
+			h2c->r.rlink[i][j].pid = rl->pid;
+			h2c->r.rlink[i][j].phy = rl->phy;
+			h2c->r.rlink[i][j].noa = rl->noa;
+
+			h2c->r.rlink[i][j].rf_band = rl->rf_band;
+			h2c->r.rlink[i][j].active = rl->active;
+			h2c->r.rlink[i][j].bw = rl->bw;
+			h2c->r.rlink[i][j].role = rl->role;
+
+			h2c->r.rlink[i][j].ch = rl->ch;
+			h2c->r.rlink[i][j].noa_dur = rl->noa_dur;
+			h2c->r.rlink[i][j].client_cnt = rl->client_cnt;
+			h2c->r.rlink[i][j].mode = rl->mode;
+		}
+	}
+
+	rm = cpu_to_le32(r->role_map);
+	memcpy(&h2c->r.role_map, &rm, sizeof(h2c->r.role_map));
+	h2c->r.mrole_type = cpu_to_le32(r->mrole_type);
+	h2c->r.mrole_noa_duration = cpu_to_le32(r->mrole_noa_duration);
+
+	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+			      H2C_CAT_OUTSRC, BTFC_SET,
+			      SET_DRV_INFO, 0, 0,
+			      len);
+
+	ret = rtw89_h2c_tx(rtwdev, skb, false);
+	if (ret) {
+		rtw89_err(rtwdev, "failed to send h2c\n");
+		goto fail;
+	}
+
+	return 0;
+fail:
+	dev_kfree_skb_any(skb);
+
+	return ret;
+}
+
+int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type)
+{
+	struct rtw89_btc_wl_role_info *r = &rtwdev->btc.cx.wl.role_info;
+	struct rtw89_h2c_cxrole_v10 *h2c;
+	struct rtw89_btc_wl_rlink *rl;
+	u32 len = sizeof(*h2c);
+	struct sk_buff *skb;
+	__le32 rm;
+	int ret;
+	u8 i, j;
+
+	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+	if (!skb) {
+		rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_role\n");
+		return -ENOMEM;
+	}
+	skb_put(skb, len);
+	h2c = (struct rtw89_h2c_cxrole_v10 *)skb->data;
+
+	h2c->hdr.type = type;
+	h2c->hdr.ver = rtwdev->btc.ver->fwlrole;
 	h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7;
-	memcpy(&h2c->_u8, role, sizeof(h2c->_u8));
-	h2c->_u32.role_map = cpu_to_le32(role->role_map);
-	h2c->_u32.mrole_type = cpu_to_le32(role->mrole_type);
-	h2c->_u32.mrole_noa_duration = cpu_to_le32(role->mrole_noa_duration);
+
+	for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) {
+		for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) {
+			rl = &r->rlink[i][j];
+			h2c->r.rlink[i][j].connected = rl->connected;
+			h2c->r.rlink[i][j].pid = rl->pid;
+			h2c->r.rlink[i][j].phy = rl->phy;
+			h2c->r.rlink[i][j].noa = rl->noa;
+
+			h2c->r.rlink[i][j].rf_band = rl->rf_band;
+			h2c->r.rlink[i][j].active = rl->active;
+			h2c->r.rlink[i][j].bw = rl->bw;
+			h2c->r.rlink[i][j].role = rl->role;
+
+			h2c->r.rlink[i][j].ch = rl->ch;
+			h2c->r.rlink[i][j].noa_dur = rl->noa_dur;
+			h2c->r.rlink[i][j].client_cnt = rl->client_cnt;
+			h2c->r.rlink[i][j].mode = rl->mode;
+			h2c->r.rlink[i][j].mac_id = rl->mac_id;
+		}
+	}
+
+	h2c->r.link_mode = r->link_mode;
+	h2c->r.link_mode_hb1 = r->link_mode_hb1;
+	h2c->r.p2p_exist = r->p2p_exist;
+	h2c->r.p2p_exist_hb1 = r->p2p_exist_hb1;
+
+	h2c->r.pta_req_band = r->pta_req_band;
+	h2c->r.dbcc_en = r->dbcc_en;
+	h2c->r.dbcc_2g_phy = r->dbcc_2g_phy;
+
+	rm = cpu_to_le32(r->role_map);
+	memcpy(&h2c->r.role_map, &rm, sizeof(h2c->r.role_map));
+	rm = cpu_to_le32(r->role_map_hb1);
+	memcpy(&h2c->r.role_map_hb1, &rm, sizeof(h2c->r.role_map_hb1));
+	h2c->r.mrole_type = cpu_to_le32(r->mrole_type);
 
 	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
 			      H2C_CAT_OUTSRC, BTFC_SET,
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index af126d15a1fb..1e4267b22b36 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -2433,54 +2433,19 @@ struct rtw89_h2c_cxctrl_v7 {
 #define H2C_LEN_CXDRVHDR sizeof(struct rtw89_h2c_cxhdr)
 #define H2C_LEN_CXDRVHDR_V7 sizeof(struct rtw89_h2c_cxhdr_v7)
 
-struct rtw89_btc_wl_role_info_v7_u8 {
-	u8 connect_cnt;
-	u8 link_mode;
-	u8 link_mode_chg;
-	u8 p2p_2g;
-
-	struct rtw89_btc_wl_active_role_v7 active_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER];
-} __packed;
-
-struct rtw89_btc_wl_role_info_v7_u32 {
-	__le32 role_map;
-	__le32 mrole_type;
-	__le32 mrole_noa_duration;
-	__le32 dbcc_en;
-	__le32 dbcc_chg;
-	__le32 dbcc_2g_phy;
-} __packed;
-
 struct rtw89_h2c_cxrole_v7 {
 	struct rtw89_h2c_cxhdr_v7 hdr;
-	struct rtw89_btc_wl_role_info_v7_u8 _u8;
-	struct rtw89_btc_wl_role_info_v7_u32 _u32;
-} __packed;
-
-struct rtw89_btc_wl_role_info_v8_u8 {
-	u8 connect_cnt;
-	u8 link_mode;
-	u8 link_mode_chg;
-	u8 p2p_2g;
-
-	u8 pta_req_band;
-	u8 dbcc_en;
-	u8 dbcc_chg;
-	u8 dbcc_2g_phy;
-
-	struct rtw89_btc_wl_rlink rlink[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER][RTW89_MAC_NUM];
+	struct rtw89_btc_wl_role_info_v7 r;
 } __packed;
 
-struct rtw89_btc_wl_role_info_v8_u32 {
-	__le32 role_map;
-	__le32 mrole_type;
-	__le32 mrole_noa_duration;
+struct rtw89_h2c_cxrole_v8 {
+	struct rtw89_h2c_cxhdr_v7 hdr;
+	struct rtw89_btc_wl_role_info_v8 r;
 } __packed;
 
-struct rtw89_h2c_cxrole_v8 {
+struct rtw89_h2c_cxrole_v10 {
 	struct rtw89_h2c_cxhdr_v7 hdr;
-	struct rtw89_btc_wl_role_info_v8_u8 _u8;
-	struct rtw89_btc_wl_role_info_v8_u32 _u32;
+	struct rtw89_btc_wl_role_info_v10 r;
 } __packed;
 
 struct rtw89_h2c_cxosi {
@@ -5413,6 +5378,7 @@ int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type);
+int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type);
 int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type);
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
index 50480f72c96d..91d6cddc713e 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
@@ -2725,6 +2725,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = {
 	.mailbox		= 0x1,
 
 	.afh_guard_ch		= 6,
+	.fdd_iso_freq		= 1000,
 	.wl_rssi_thres		= rtw89_btc_8851b_wl_rssi_thres,
 	.bt_rssi_thres		= rtw89_btc_8851b_bt_rssi_thres,
 	.rssi_tol		= 2,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
index 0c7794742650..ed6dee63694f 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
@@ -2464,6 +2464,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = {
 	.mailbox		= 0x1,
 
 	.afh_guard_ch		= 6,
+	.fdd_iso_freq		= 1000,
 	.wl_rssi_thres		= rtw89_btc_8852a_wl_rssi_thres,
 	.bt_rssi_thres		= rtw89_btc_8852a_bt_rssi_thres,
 	.rssi_tol		= 2,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
index 5904127cc836..4f55a153d169 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
@@ -1060,6 +1060,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = {
 	.mailbox		= 0x1,
 
 	.afh_guard_ch		= 6,
+	.fdd_iso_freq		= 1000,
 	.wl_rssi_thres		= rtw89_btc_8852b_wl_rssi_thres,
 	.bt_rssi_thres		= rtw89_btc_8852b_bt_rssi_thres,
 	.rssi_tol		= 2,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
index e68f73827fa5..9e0c48fde2fa 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
@@ -898,6 +898,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {
 	.mailbox		= 0x1,
 
 	.afh_guard_ch		= 6,
+	.fdd_iso_freq		= 1000,
 	.wl_rssi_thres		= rtw89_btc_8852bt_wl_rssi_thres,
 	.bt_rssi_thres		= rtw89_btc_8852bt_bt_rssi_thres,
 	.rssi_tol		= 2,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
index 968666280c89..f21dd87e3846 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
@@ -3266,6 +3266,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = {
 	.mailbox		= 0x1,
 
 	.afh_guard_ch		= 6,
+	.fdd_iso_freq		= 1000,
 	.wl_rssi_thres		= rtw89_btc_8852c_wl_rssi_thres,
 	.bt_rssi_thres		= rtw89_btc_8852c_bt_rssi_thres,
 	.rssi_tol		= 2,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
index fe87b4929ddc..cdcce091b922 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
@@ -3249,6 +3249,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = {
 	.mailbox		= 0x1,
 
 	.afh_guard_ch		= 6,
+	.fdd_iso_freq		= 1000,
 	.wl_rssi_thres		= rtw89_btc_8922a_wl_rssi_thres,
 	.bt_rssi_thres		= rtw89_btc_8922a_bt_rssi_thres,
 	.rssi_tol		= 2,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index 768434db14c6..aeade858ce95 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -3535,6 +3535,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = {
 	.mailbox		= 0x1,
 
 	.afh_guard_ch		= 6,
+	.fdd_iso_freq		= 1000,
 	.wl_rssi_thres		= rtw89_btc_8922d_wl_rssi_thres,
 	.bt_rssi_thres		= rtw89_btc_8922d_bt_rssi_thres,
 	.rssi_tol		= 2,
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 00/11] wifi: rtw89: coex: implement coex for RTL8922D
From: Ping-Ke Shih @ 2026-07-12  3:04 UTC (permalink / raw)
  To: linux-wireless; +Cc: ku920601

The BT-coexistence introduce many new formats and features for RTL8922D,
including dual-BT support, offload features to firmware and etc. Update
them accordingly and add a tag version for reference.

Ching-Te Ku (11):
  wifi: rtw89: coex: Add Wi-Fi role info version 10
  wifi: rtw89: coex: Rearrange coexistence control structure
  wifi: rtw89: coex: Update driver outsource info to firmware version 6
  wifi: rtw89: ceox: Update antenna & grant signal setting
  wifi: rtw89: coex: Rearrange Bluetooth firmware report entry
  wifi: rtw89: coex: Refine send firmware command function
  wifi: rtw89: coex: Refine _reset_btc_var()
  wifi: rtw89: coex: Correct SET_RFE settings
  wifi: rtw89: coex: Add firmware report control report v11
  wifi: rtw89: coex: update external control length by case
  wifi: rtw89: coex: Update coexistence version to 9.24.0

 drivers/net/wireless/realtek/rtw89/coex.c     | 3781 ++++++++---------
 drivers/net/wireless/realtek/rtw89/coex.h     |   73 +
 drivers/net/wireless/realtek/rtw89/core.c     |   11 +-
 drivers/net/wireless/realtek/rtw89/core.h     |  486 ++-
 drivers/net/wireless/realtek/rtw89/debug.c    |    6 +-
 drivers/net/wireless/realtek/rtw89/fw.c       |  556 ++-
 drivers/net/wireless/realtek/rtw89/fw.h       |   60 +-
 drivers/net/wireless/realtek/rtw89/mac.c      |   27 +-
 drivers/net/wireless/realtek/rtw89/mac_be.c   |   21 +-
 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 |  214 +-
 16 files changed, 2954 insertions(+), 2287 deletions(-)


base-commit: 56d32cdc6040440b08edfd5d7262250a721233f8
-- 
2.25.1


^ permalink raw reply

* Re: [PATCH rtw-next 01/15] wifi: rtw89: 8922d: remove CCK bandwidth compensation
From: Ping-Ke Shih @ 2026-07-12  1:55 UTC (permalink / raw)
  To: Ping-Ke Shih, linux-wireless; +Cc: gary.chang, echuang
In-Reply-To: <20260707091056.42771-2-pkshih@realtek.com>

Ping-Ke Shih <pkshih@realtek.com> wrote:

> From: Eric Huang <echuang@realtek.com>
> 
> Remove the 40MHz bandwidth compensation from CCK efuse gain calculation.
> The design no longer requires the +3dB compensation for 40MHz channels.
> 
> Signed-off-by: Eric Huang <echuang@realtek.com>

15 patch(es) applied to rtw-next branch of rtw.git, thanks.

be8aedb68c96 wifi: rtw89: 8922d: remove CCK bandwidth compensation
daa3fda5aeb9 wifi: rtw89: 8922d: dynamic adjust channel smoothing
8da4883c81ae wifi: rtw89: 8922d: fix EMLSR BB switch sequence for MLO mode transition
852927114f6e wifi: rtw89: phy: fix bandedge primary channel for 2.4GHz 40MHz and 6GHz
390f58e29de3 wifi: rtw89: 8922d: set TX compensation by format v2
d193bdc73396 wifi: rtw89: efuse: no need to export rtw89_efuse_read_ecv_be()
4260edb3b1a6 wifi: rtw89: efuse: read thermal calibration value for RTL8922D
4e199cd66614 wifi: rtw89: 8922d: read default digital voltage calibration values
47ad03f1eca5 wifi: rtw89: add thermal protect by digital voltage reduction
b7911466b6db wifi: rtw89: 8922d: set ANA CLK enter to 500KHz
79afed9426ce wifi: rtw89: 8922d: update scaling factor for RX path
9bf6bd6ed5ac wifi: rtw89: 8852a: fix RSSI report when average beacon RSSI is not ready
235fa79d7561 wifi: rtw89: phy: add NCTL check for WiFi 7 chips
5c925d7722c6 wifi: rtw89: unify access struct of TX power track tables
56d32cdc6040 wifi: rtw89: set needed firmware elements for early chips transition

---
https://github.com/pkshih/rtw.git


^ permalink raw reply

* RE: [PATCH] wifi: rtw89: 8852au: Add support for Mercusys MA72XH
From: Ping-Ke Shih @ 2026-07-12  1:50 UTC (permalink / raw)
  To: Abhishek Tamboli
  Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20260710030055.57438-1-abhishektamboli9@gmail.com>

Abhishek Tamboli <abhishektamboli9@gmail.com> wrote:
> Add the ID 2c4e:0124 to the rtw_8852au_id_table to
> support the Mercusys MA72XH usb adapter.
> 
> Link: https://bugzilla.kernel.org/show_bug.cgi?id=221738

Not sure why I have no permission to access this. Maybe it gets broken.
I'll wait a while to see if people will fix it.




^ permalink raw reply

* [PATCH 01/13 RFC net-next] net: ipv4: introduce CONFIG_IPV4 to decouple the IPv4 stack
From: Fernando Fernandez Mancera @ 2026-07-12  1:38 UTC (permalink / raw)
  To: netdev
  Cc: davem, edumazet, kuba, pabeni, dsahern, horms, idosch,
	Fernando Fernandez Mancera, Jason Gunthorpe, Leon Romanovsky,
	Andrew Lunn, Tony Nguyen, Przemek Kitszel, Elad Nachman,
	Saeed Mahameed, Tariq Toukan, Mark Bloch, Petr Machata,
	Edward Cree, Maxime Coquelin, Alexandre Torgue, Arend van Spriel,
	Miri Korenblit, Keith Busch, Jens Axboe, Christoph Hellwig,
	Sagi Grimberg, Chaitanya Kulkarni, Saurav Kashyap, Javed Hasan,
	GR-QLogic-Storage-Upstream, James E.J. Bottomley,
	Martin K. Petersen, Nilesh Javali, Manish Rangankar,
	Varun Prakash, Alexander Viro, Christian Brauner, Jan Kara,
	David Howells, Marc Dionne, Trond Myklebust, Anna Schumaker,
	Chuck Lever, Jeff Layton, NeilBrown, Olga Kornievskaia, Dai Ngo,
	Tom Talpey, Marek Lindner, Simon Wunderlich, Antonio Quartulli,
	Sven Eckelmann, Nikolay Aleksandrov, Pablo Neira Ayuso,
	Florian Westphal, Phil Sutter, Johannes Berg, Matthieu Baerts,
	Mat Martineau, Geliang Tang, Julian Anastasov, Aaron Conole,
	Eelco Chaudron, Ilya Maximets, Allison Henderson,
	Jamal Hadi Salim, Jiri Pirko, Marcelo Ricardo Leitner, Xin Long,
	D. Wythe, Dust Li, Sidraya Jayagond, Wenjia Zhang,
	Mahanta Jambigi, Tony Lu, Wen Gu, Jon Maloy, Steffen Klassert,
	Herbert Xu, Vikas Gupta, Rajashekar Hudumula, Justin Chen,
	Bhargava Marreddy, Arnd Bergmann, Nicolai Buchwitz,
	Florian Fainelli, Heiner Kallweit, Krzysztof Kozlowski,
	Russell King (Oracle), Yao Zi, Yanteng Si, Maxime Chevallier,
	Julian Braha, Joey Lu, Shangjuan Wei, Chen-Yu Tsai, Inochi Amaoto,
	Lad Prabhakar, Qingfang Deng, Greg Kroah-Hartman, Eric Biggers,
	Ethan Nelson-Moore, Ard Biesheuvel, Dmitry Safonov,
	Kuniyuki Iwashima, Alyssa Ross, linux-rdma, linux-kernel,
	intel-wired-lan, oss-drivers, linux-net-drivers, linux-stm32,
	linux-arm-kernel, linux-wireless, brcm80211,
	brcm80211-dev-list.pdl, linux-nvme, linux-scsi, target-devel,
	linux-fsdevel, linux-afs, linux-nfs, b.a.t.m.a.n, bridge,
	netfilter-devel, coreteam, mptcp, lvs-devel, dev, rds-devel,
	linux-sctp, linux-s390, tipc-discussion
In-Reply-To: <20260712013941.4570-1-fmancera@suse.de>

Historically, the IPv4 protocol has been linked to the core INET
subsystem. Because shared infrastructure like the TCP/UDP engine,
routing or INET hashtables live inside net/ipv4/, it has been impossible
to compile a kernel with only IPv6 support.

This patch introduces the CONFIG_IPV4 Kconfig symbol, which is set to
'def_bool y' for now. This does not allow to completely disable the
IPv4 stack yet but it lays the necessary build-system work for that
goal.

In addition, multiple subsystems Kconfig symbols across networking
features like tunnels, drivers and filesystems have been adjusted to
depend on IPv4. Keep on mind that follow-up work is required to convert
the Kconfig symbol to bool and remove the dependency on IPv4 where it
makes sense.

Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
 drivers/infiniband/Kconfig                    |  2 +-
 drivers/net/Kconfig                           | 24 ++++++++++---------
 drivers/net/ethernet/broadcom/Kconfig         |  2 ++
 drivers/net/ethernet/chelsio/Kconfig          |  2 +-
 drivers/net/ethernet/intel/Kconfig            |  2 ++
 drivers/net/ethernet/marvell/prestera/Kconfig |  1 +
 .../net/ethernet/mellanox/mlx5/core/Kconfig   |  3 ++-
 drivers/net/ethernet/mellanox/mlxsw/Kconfig   |  1 +
 drivers/net/ethernet/netronome/Kconfig        |  1 +
 drivers/net/ethernet/qlogic/Kconfig           |  2 ++
 drivers/net/ethernet/sfc/Kconfig              |  1 +
 drivers/net/ethernet/stmicro/stmmac/Kconfig   |  1 +
 drivers/net/ethernet/via/Kconfig              |  1 +
 drivers/net/ppp/Kconfig                       |  1 +
 .../broadcom/brcm80211/brcmfmac/Kconfig       |  1 +
 drivers/net/wireless/intel/iwlwifi/Kconfig    |  1 +
 drivers/nvme/host/Kconfig                     |  2 +-
 drivers/nvme/target/Kconfig                   |  2 +-
 drivers/scsi/bnx2fc/Kconfig                   |  1 +
 drivers/scsi/bnx2i/Kconfig                    |  1 +
 drivers/scsi/cxgbi/cxgb3i/Kconfig             |  2 +-
 drivers/scsi/cxgbi/cxgb4i/Kconfig             |  2 +-
 drivers/target/iscsi/Kconfig                  |  2 +-
 fs/Kconfig                                    |  1 +
 fs/afs/Kconfig                                |  2 +-
 fs/nfs/Kconfig                                |  2 +-
 fs/nfsd/Kconfig                               |  2 +-
 net/Kconfig                                   |  6 ++---
 net/batman-adv/Kconfig                        |  1 +
 net/bridge/Kconfig                            |  3 ++-
 net/bridge/netfilter/Kconfig                  |  6 ++---
 net/ipv4/Kconfig                              | 21 +++++++++++++---
 net/ipv4/Makefile                             | 21 ++++++++--------
 net/ipv4/netfilter/Kconfig                    |  2 +-
 net/ipv6/Kconfig                              | 17 +++++++++----
 net/ipv6/netfilter/Kconfig                    |  2 +-
 net/l2tp/Kconfig                              |  2 +-
 net/mac80211/Kconfig                          |  2 +-
 net/mpls/Kconfig                              |  1 +
 net/mptcp/Kconfig                             |  2 +-
 net/netfilter/Kconfig                         |  2 +-
 net/netfilter/ipset/Kconfig                   |  2 +-
 net/netfilter/ipvs/Kconfig                    |  2 +-
 net/openvswitch/Kconfig                       |  2 +-
 net/packet/Kconfig                            |  1 +
 net/rds/Kconfig                               |  2 +-
 net/rxrpc/Kconfig                             |  2 +-
 net/sched/Kconfig                             |  2 +-
 net/sctp/Kconfig                              |  2 +-
 net/smc/Kconfig                               |  2 +-
 net/sunrpc/Kconfig                            |  2 +-
 net/tipc/Kconfig                              |  2 +-
 net/xfrm/Kconfig                              | 12 ++++++++--
 53 files changed, 121 insertions(+), 65 deletions(-)

diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index 086195758a8a..e9302002b6ea 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -3,7 +3,7 @@ menuconfig INFINIBAND
 	tristate "InfiniBand support"
 	depends on HAS_IOMEM && HAS_DMA
 	depends on NET
-	depends on INET
+	depends on INET && IPV4
 	depends on !ALPHA
 	select DMA_SHARED_BUFFER
 	select IRQ_POLL
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index ff79c466712d..8e66b3015698 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -40,7 +40,7 @@ if NET_CORE
 
 config BONDING
 	tristate "Bonding driver support"
-	depends on INET
+	depends on INET && IPV4
 	depends on TLS || TLS_DEVICE=n
 	help
 	  Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet
@@ -73,7 +73,7 @@ config DUMMY
 
 config WIREGUARD
 	tristate "WireGuard secure network tunnel"
-	depends on NET && INET
+	depends on NET && INET && IPV4
 	select NET_UDP_TUNNEL
 	select DST_CACHE
 	select CRYPTO_LIB_CURVE25519
@@ -102,7 +102,7 @@ config WIREGUARD_DEBUG
 
 config OVPN
 	tristate "OpenVPN data channel offload"
-	depends on NET && INET
+	depends on NET && INET && IPV4
 	select DST_CACHE
 	select NET_UDP_TUNNEL
 	select CRYPTO
@@ -164,6 +164,7 @@ source "drivers/net/team/Kconfig"
 
 config MACVLAN
 	tristate "MAC-VLAN support"
+	depends on IPV4
 	help
 	  This allows one to create virtual interfaces that map packets to
 	  or from specific MAC addresses to a particular interface.
@@ -198,7 +199,7 @@ config IPVLAN_L3S
 
 config IPVLAN
 	tristate "IP-VLAN support"
-	depends on INET
+	depends on INET && IPV4
 	help
 	  This allows one to create virtual devices off of a main interface
 	  and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
@@ -229,7 +230,7 @@ config IPVTAP
 
 config VXLAN
 	tristate "Virtual eXtensible Local Area Network (VXLAN)"
-	depends on INET
+	depends on INET && IPV4
 	select NET_UDP_TUNNEL
 	select GRO_CELLS
 	help
@@ -244,7 +245,7 @@ config VXLAN
 
 config GENEVE
 	tristate "Generic Network Virtualization Encapsulation"
-	depends on INET
+	depends on INET && IPV4
 	select NET_UDP_TUNNEL
 	select GRO_CELLS
 	help
@@ -259,7 +260,7 @@ config GENEVE
 
 config BAREUDP
 	tristate "Bare UDP Encapsulation"
-	depends on INET
+	depends on INET && IPV4
 	select NET_UDP_TUNNEL
 	select GRO_CELLS
 	help
@@ -271,7 +272,7 @@ config BAREUDP
 
 config GTP
 	tristate "GPRS Tunneling Protocol datapath (GTP-U)"
-	depends on INET
+	depends on INET && IPV4
 	select NET_UDP_TUNNEL
 	help
 	  This allows one to create gtp virtual interfaces that provide
@@ -288,7 +289,7 @@ config GTP
 
 config PFCP
 	tristate "Packet Forwarding Control Protocol (PFCP)"
-	depends on INET
+	depends on INET && IPV4
 	select NET_UDP_TUNNEL
 	help
 	  This allows one to create PFCP virtual interfaces that allows to
@@ -301,7 +302,7 @@ config PFCP
 
 config AMT
 	tristate "Automatic Multicast Tunneling (AMT)"
-	depends on INET && IP_MULTICAST
+	depends on INET && IP_MULTICAST && IPV4
 	select NET_UDP_TUNNEL
 	help
 	  This allows one to create AMT(Automatic Multicast Tunneling)
@@ -471,6 +472,7 @@ config NETKIT
 
 config NET_VRF
 	tristate "Virtual Routing and Forwarding (Lite)"
+	depends on IPV4
 	depends on IP_MULTIPLE_TABLES
 	depends on NET_L3_MASTER_DEV
 	depends on IPV6_MULTIPLE_TABLES || IPV6=n
@@ -604,7 +606,7 @@ source "drivers/net/hyperv/Kconfig"
 config NETDEVSIM
 	tristate "Simulated networking device"
 	depends on DEBUG_FS
-	depends on INET
+	depends on INET && IPV4
 	depends on PSAMPLE || PSAMPLE=n
 	depends on PTP_1588_CLOCK_MOCK || PTP_1588_CLOCK_MOCK=n
 	select NET_DEVLINK
diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig
index f0bac0dd1439..02469b439127 100644
--- a/drivers/net/ethernet/broadcom/Kconfig
+++ b/drivers/net/ethernet/broadcom/Kconfig
@@ -97,6 +97,7 @@ config BNX2
 
 config CNIC
 	tristate "QLogic CNIC support"
+	depends on IPV4
 	depends on PCI
 	select BNX2
 	select UIO
@@ -208,6 +209,7 @@ config BNXT
 	tristate "Broadcom NetXtreme-C/E support"
 	depends on PCI
 	depends on PTP_1588_CLOCK_OPTIONAL
+	depends on IPV4
 	select FW_LOADER
 	select CRC32
 	select NET_DEVLINK
diff --git a/drivers/net/ethernet/chelsio/Kconfig b/drivers/net/ethernet/chelsio/Kconfig
index 96d7779cd2f0..c738820688e0 100644
--- a/drivers/net/ethernet/chelsio/Kconfig
+++ b/drivers/net/ethernet/chelsio/Kconfig
@@ -48,7 +48,7 @@ config CHELSIO_T1_1G
 
 config CHELSIO_T3
 	tristate "Chelsio Communications T3 10Gb Ethernet support"
-	depends on PCI && INET
+	depends on PCI && INET && IPV4
 	select FW_LOADER
 	select MDIO
 	help
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 288fa8ce53af..d975e41a2b68 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -193,6 +193,7 @@ config IXGBE_DCB
 
 config IXGBE_IPSEC
 	bool "IPSec XFRM cryptography-offload acceleration"
+	depends on IPV4
 	depends on IXGBE
 	depends on XFRM_OFFLOAD
 	default y
@@ -219,6 +220,7 @@ config IXGBEVF
 
 config IXGBEVF_IPSEC
 	bool "IPSec XFRM cryptography-offload acceleration"
+	depends on IPV4
 	depends on IXGBEVF
 	depends on XFRM_OFFLOAD
 	default y
diff --git a/drivers/net/ethernet/marvell/prestera/Kconfig b/drivers/net/ethernet/marvell/prestera/Kconfig
index f2f7663c3d10..4c7486c7e706 100644
--- a/drivers/net/ethernet/marvell/prestera/Kconfig
+++ b/drivers/net/ethernet/marvell/prestera/Kconfig
@@ -7,6 +7,7 @@ config PRESTERA
 	tristate "Marvell Prestera Switch ASICs support"
 	depends on NET_SWITCHDEV && VLAN_8021Q
 	depends on BRIDGE || BRIDGE=n
+	depends on IPV4
 	select NET_DEVLINK
 	select PHYLINK
 	help
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
index 925ee25d05b4..86b0884968b6 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
@@ -12,6 +12,7 @@ config MLX5_CORE
 	depends on PTP_1588_CLOCK_OPTIONAL
 	depends on PCI_HYPERV_INTERFACE || !PCI_HYPERV_INTERFACE
 	depends on HWMON || !HWMON
+	depends on IPV4
 	help
 	  Core driver for low level functionality of the ConnectX-4 and
 	  Connect-IB cards by Mellanox Technologies.
@@ -28,7 +29,7 @@ config MLX5_FPGA
 
 config MLX5_CORE_EN
 	bool "Mellanox 5th generation network adapters (ConnectX series) Ethernet support"
-	depends on NETDEVICES && ETHERNET && INET && PCI && MLX5_CORE
+	depends on NETDEVICES && ETHERNET && INET && PCI && MLX5_CORE && IPV4
 	select PAGE_POOL
 	select PAGE_POOL_STATS
 	select DIMLIB
diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig
index 2229a2694aa5..d12c33044a1d 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig
@@ -53,6 +53,7 @@ config MLXSW_I2C
 
 config MLXSW_SPECTRUM
 	tristate "Mellanox Technologies Spectrum family support"
+	depends on IPV4
 	depends on MLXSW_CORE && MLXSW_PCI && NET_SWITCHDEV && VLAN_8021Q
 	depends on PSAMPLE || PSAMPLE=n
 	depends on BRIDGE || BRIDGE=n
diff --git a/drivers/net/ethernet/netronome/Kconfig b/drivers/net/ethernet/netronome/Kconfig
index d115d16d4649..2ac3f89e1f26 100644
--- a/drivers/net/ethernet/netronome/Kconfig
+++ b/drivers/net/ethernet/netronome/Kconfig
@@ -18,6 +18,7 @@ if NET_VENDOR_NETRONOME
 
 config NFP
 	tristate "Netronome(R) NFP4000/NFP6000 NIC driver"
+	depends on IPV4
 	depends on PCI_MSI
 	depends on VXLAN || VXLAN=n
 	depends on TLS && TLS_DEVICE || TLS_DEVICE=n
diff --git a/drivers/net/ethernet/qlogic/Kconfig b/drivers/net/ethernet/qlogic/Kconfig
index 1203353238e5..d198f6add1a8 100644
--- a/drivers/net/ethernet/qlogic/Kconfig
+++ b/drivers/net/ethernet/qlogic/Kconfig
@@ -29,6 +29,7 @@ config QLA3XXX
 config QLCNIC
 	tristate "QLOGIC QLCNIC 1/10Gb Converged Ethernet NIC Support"
 	depends on PCI
+	depends on IPV4
 	select FW_LOADER
 	help
 	  This driver supports QLogic QLE8240 and QLE8242 Converged Ethernet
@@ -68,6 +69,7 @@ config QLCNIC_HWMON
 
 config NETXEN_NIC
 	tristate "NetXen Multi port (1/10) Gigabit Ethernet NIC"
+	depends on IPV4
 	depends on PCI
 	select FW_LOADER
 	help
diff --git a/drivers/net/ethernet/sfc/Kconfig b/drivers/net/ethernet/sfc/Kconfig
index 979f2801e2a8..d2e32e8ffce8 100644
--- a/drivers/net/ethernet/sfc/Kconfig
+++ b/drivers/net/ethernet/sfc/Kconfig
@@ -18,6 +18,7 @@ if NET_VENDOR_SOLARFLARE
 
 config SFC
 	tristate "Solarflare SFC9100/EF100-family support"
+	depends on IPV4
 	depends on PCI
 	depends on PTP_1588_CLOCK_OPTIONAL
 	select MDIO
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index e3dd5adda5ac..4ac59a43d71c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config STMMAC_ETH
 	tristate "STMicroelectronics Multi-Gigabit Ethernet driver"
+	depends on IPV4
 	depends on HAS_IOMEM && HAS_DMA
 	depends on PTP_1588_CLOCK_OPTIONAL
 	depends on ETHTOOL_NETLINK
diff --git a/drivers/net/ethernet/via/Kconfig b/drivers/net/ethernet/via/Kconfig
index 00773f5e4d7e..a62eb98f2e27 100644
--- a/drivers/net/ethernet/via/Kconfig
+++ b/drivers/net/ethernet/via/Kconfig
@@ -45,6 +45,7 @@ config VIA_RHINE_MMIO
 
 config VIA_VELOCITY
 	tristate "VIA Velocity support"
+	depends on IPV4
 	depends on (PCI || (OF_ADDRESS && OF_IRQ))
 	depends on HAS_DMA
 	select CRC32
diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig
index 4047f13b83dc..16826da75d1e 100644
--- a/drivers/net/ppp/Kconfig
+++ b/drivers/net/ppp/Kconfig
@@ -160,6 +160,7 @@ config PPPOE_HASH_BITS
 
 config PPTP
 	tristate "PPP over IPv4 (PPTP)"
+	depends on IPV4
 	depends on NET_IPGRE_DEMUX
 	select PPPOX
 	help
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig
index 32794c1eca23..95d685f165ea 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig
@@ -1,6 +1,7 @@
 config BRCMFMAC
 	tristate "Broadcom FullMAC WLAN driver"
 	depends on CFG80211
+	depends on IPV4
 	select BRCMUTIL
 	help
 	  This module adds support for wireless adapters based on Broadcom
diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig
index 153a8368b412..d2efd96cd513 100644
--- a/drivers/net/wireless/intel/iwlwifi/Kconfig
+++ b/drivers/net/wireless/intel/iwlwifi/Kconfig
@@ -2,6 +2,7 @@
 config IWLWIFI
 	tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) "
 	depends on PCI && HAS_IOMEM && CFG80211
+	depends on IPV4
 	depends on IWLMEI || !IWLMEI
 	select FW_LOADER
 	help
diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig
index 31974c7dd20c..6e119cf475de 100644
--- a/drivers/nvme/host/Kconfig
+++ b/drivers/nvme/host/Kconfig
@@ -82,7 +82,7 @@ config NVME_FC
 
 config NVME_TCP
 	tristate "NVM Express over Fabrics TCP host driver"
-	depends on INET
+	depends on INET && IPV4
 	depends on BLOCK
 	select CRC32
 	select NET_CRC32C
diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig
index 69bde270115e..f731a043f6a9 100644
--- a/drivers/nvme/target/Kconfig
+++ b/drivers/nvme/target/Kconfig
@@ -86,7 +86,7 @@ config NVME_TARGET_FCLOOP
 
 config NVME_TARGET_TCP
 	tristate "NVMe over Fabrics TCP target support"
-	depends on INET
+	depends on INET && IPV4
 	depends on NVME_TARGET
 	help
 	  This enables the NVMe TCP target support, which allows exporting NVMe
diff --git a/drivers/scsi/bnx2fc/Kconfig b/drivers/scsi/bnx2fc/Kconfig
index d12eeb13384a..d5065b8fdea9 100644
--- a/drivers/scsi/bnx2fc/Kconfig
+++ b/drivers/scsi/bnx2fc/Kconfig
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config SCSI_BNX2X_FCOE
 	tristate "QLogic FCoE offload support"
+	depends on IPV4
 	depends on PCI
 	depends on LIBFC
 	depends on LIBFCOE
diff --git a/drivers/scsi/bnx2i/Kconfig b/drivers/scsi/bnx2i/Kconfig
index e649a04fab1d..0458e4811731 100644
--- a/drivers/scsi/bnx2i/Kconfig
+++ b/drivers/scsi/bnx2i/Kconfig
@@ -2,6 +2,7 @@
 config SCSI_BNX2_ISCSI
 	tristate "QLogic NetXtreme II iSCSI support"
 	depends on NET
+	depends on IPV4
 	depends on PCI
 	select SCSI_ISCSI_ATTRS
 	select NETDEVICES
diff --git a/drivers/scsi/cxgbi/cxgb3i/Kconfig b/drivers/scsi/cxgbi/cxgb3i/Kconfig
index 143e881ec77e..abb4a462cb37 100644
--- a/drivers/scsi/cxgbi/cxgb3i/Kconfig
+++ b/drivers/scsi/cxgbi/cxgb3i/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config SCSI_CXGB3_ISCSI
 	tristate "Chelsio T3 iSCSI support"
-	depends on PCI && INET
+	depends on PCI && INET && IPV4
 	select NETDEVICES
 	select ETHERNET
 	select NET_VENDOR_CHELSIO
diff --git a/drivers/scsi/cxgbi/cxgb4i/Kconfig b/drivers/scsi/cxgbi/cxgb4i/Kconfig
index dd1c8ff36b00..04b3e7b053dc 100644
--- a/drivers/scsi/cxgbi/cxgb4i/Kconfig
+++ b/drivers/scsi/cxgbi/cxgb4i/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config SCSI_CXGB4_ISCSI
 	tristate "Chelsio T4 iSCSI support"
-	depends on PCI && INET
+	depends on PCI && INET && IPV4
 	depends on PTP_1588_CLOCK_OPTIONAL
 	depends on THERMAL || !THERMAL
 	depends on ETHERNET
diff --git a/drivers/target/iscsi/Kconfig b/drivers/target/iscsi/Kconfig
index 70d76f3dd693..362edff80c15 100644
--- a/drivers/target/iscsi/Kconfig
+++ b/drivers/target/iscsi/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config ISCSI_TARGET
 	tristate "SCSI Target Mode Stack"
-	depends on INET
+	depends on INET && IPV4
 	select CRC32
 	select CRYPTO
 	select CRYPTO_HASH
diff --git a/fs/Kconfig b/fs/Kconfig
index cf6ae64776e6..20199c1cc93a 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -360,6 +360,7 @@ config GRACE_PERIOD
 
 config LOCKD
 	tristate
+	depends on IPV4
 	depends on FILE_LOCKING
 	select CRC32
 	select GRACE_PERIOD
diff --git a/fs/afs/Kconfig b/fs/afs/Kconfig
index 682bd8ec2c10..e7ed46426f81 100644
--- a/fs/afs/Kconfig
+++ b/fs/afs/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config AFS_FS
 	tristate "Andrew File System support (AFS)"
-	depends on INET
+	depends on INET && IPV4
 	select AF_RXRPC
 	select DNS_RESOLVER
 	select NETFS_SUPPORT
diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
index 6bb30543eff0..52efd3664d21 100644
--- a/fs/nfs/Kconfig
+++ b/fs/nfs/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config NFS_FS
 	tristate "NFS client support"
-	depends on INET && FILE_LOCKING && MULTIUSER
+	depends on INET && FILE_LOCKING && MULTIUSER && IPV4
 	select CRC32
 	select LOCKD
 	select SUNRPC
diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig
index ffb76761d6a8..153a9918214d 100644
--- a/fs/nfsd/Kconfig
+++ b/fs/nfsd/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config NFSD
 	tristate "NFS server support"
-	depends on INET
+	depends on INET && IPV4
 	depends on FILE_LOCKING
 	depends on FSNOTIFY
 	select CRC32
diff --git a/net/Kconfig b/net/Kconfig
index e38477393551..27de9e69b5f7 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -385,7 +385,7 @@ menu "Network testing"
 
 config NET_PKTGEN
 	tristate "Packet Generator (USE WITH CAUTION)"
-	depends on INET && PROC_FS
+	depends on INET && IPV4 && PROC_FS
 	help
 	  This module will inject preconfigured packets, at a configurable
 	  rate, out of a given interface.  It is used for network interface
@@ -452,7 +452,7 @@ config LWTUNNEL
 
 config LWTUNNEL_BPF
 	bool "Execute BPF program as route nexthop action"
-	depends on LWTUNNEL && INET
+	depends on LWTUNNEL && INET && IPV4
 	default y if LWTUNNEL=y
 	help
 	  Allows to run BPF programs as a nexthop action following a route
@@ -474,7 +474,7 @@ config NET_IEEE8021Q_HELPERS
 
 config NET_SELFTESTS
 	def_tristate PHYLIB
-	depends on PHYLIB && INET
+	depends on PHYLIB && INET && IPV4
 
 config NET_SOCK_MSG
 	bool
diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig
index 58c408b7a7d9..60ad4b09be6e 100644
--- a/net/batman-adv/Kconfig
+++ b/net/batman-adv/Kconfig
@@ -9,6 +9,7 @@
 
 config BATMAN_ADV
 	tristate "B.A.T.M.A.N. Advanced Meshing Protocol"
+	depends on IPV4
 	select CRC32
 	help
 	  B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig
index 318715c8fc9b..421892a03129 100644
--- a/net/bridge/Kconfig
+++ b/net/bridge/Kconfig
@@ -7,6 +7,7 @@ config BRIDGE
 	tristate "802.1d Ethernet Bridging"
 	select LLC
 	select STP
+	depends on IPV4
 	help
 	  If you say Y here, then your Linux box will be able to act as an
 	  Ethernet bridge, which means that the different Ethernet segments it
@@ -36,7 +37,7 @@ config BRIDGE
 config BRIDGE_IGMP_SNOOPING
 	bool "IGMP/MLD snooping"
 	depends on BRIDGE
-	depends on INET
+	depends on INET && IPV4
 	default y
 	help
 	  If you say Y here, then the Ethernet bridge will be able selectively
diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig
index 4fd5a6ea26b4..003f055f8cfe 100644
--- a/net/bridge/netfilter/Kconfig
+++ b/net/bridge/netfilter/Kconfig
@@ -4,7 +4,7 @@
 #
 #
 menuconfig NF_TABLES_BRIDGE
-	depends on BRIDGE && NETFILTER && NF_TABLES
+	depends on BRIDGE && NETFILTER && NF_TABLES && IPV4
 	select NETFILTER_FAMILY_BRIDGE
 	tristate "Ethernet Bridge nf_tables support"
 
@@ -43,7 +43,7 @@ config NF_CONNTRACK_BRIDGE
 config BRIDGE_NF_EBTABLES_LEGACY
 	tristate "Legacy EBTABLES support"
 	depends on BRIDGE && NETFILTER_XTABLES_LEGACY
-	depends on NETFILTER_XTABLES
+	depends on NETFILTER_XTABLES && IPV4
 	default	n
 	help
 	 Legacy ebtables packet/frame classifier.
@@ -52,7 +52,7 @@ config BRIDGE_NF_EBTABLES_LEGACY
 
 menuconfig BRIDGE_NF_EBTABLES
 	tristate "Ethernet Bridge tables (ebtables) support"
-	depends on BRIDGE && NETFILTER && NETFILTER_XTABLES
+	depends on BRIDGE && NETFILTER && NETFILTER_XTABLES && IPV4
 	select NETFILTER_FAMILY_BRIDGE
 	help
 	  ebtables is a general, extensible frame/packet identification
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 301b47660305..51bf05d9770c 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -2,6 +2,9 @@
 #
 # IP configuration
 #
+config IPV4
+	def_bool y
+
 config IP_MULTICAST
 	bool "IP: multicasting"
 	help
@@ -62,7 +65,7 @@ config IP_FIB_TRIE_STATS
 
 config IP_MULTIPLE_TABLES
 	bool "IP: policy routing"
-	depends on IP_ADVANCED_ROUTER
+	depends on IPV4 && IP_ADVANCED_ROUTER
 	select FIB_RULES
 	help
 	  Normally, a router decides what to do with a received packet based
@@ -105,6 +108,7 @@ config IP_ROUTE_CLASSID
 
 config IP_PNP
 	bool "IP: kernel level autoconfiguration"
+	depends on IPV4
 	help
 	  This enables automatic configuration of IP addresses of devices and
 	  of the routing table during kernel boot, based on either information
@@ -161,6 +165,7 @@ config IP_PNP_RARP
 
 config NET_IPIP
 	tristate "IP: tunneling"
+	depends on IPV4
 	select INET_TUNNEL
 	select NET_IP_TUNNEL
 	help
@@ -185,12 +190,14 @@ config NET_IPGRE_DEMUX
 
 config NET_IP_TUNNEL
 	tristate
+	depends on IPV4
 	select DST_CACHE
 	select GRO_CELLS
 	default n
 
 config NET_IPGRE
 	tristate "IP: GRE tunnels over IP"
+	depends on IPV4
 	depends on NET_IPGRE_DEMUX
 	select NET_IP_TUNNEL
 	help
@@ -219,7 +226,7 @@ config IP_MROUTE_COMMON
 
 config IP_MROUTE
 	bool "IP: multicast routing"
-	depends on IP_MULTICAST
+	depends on IP_MULTICAST && IPV4
 	select IP_MROUTE_COMMON
 	help
 	  This is used if you want your machine to act as a router for IP
@@ -267,6 +274,7 @@ config IP_PIMSM_V2
 
 config SYN_COOKIES
 	bool "IP: TCP syncookie support"
+	depends on IPV4
 	help
 	  Normal TCP/IP networking is open to an attack known as "SYN
 	  flooding". This denial-of-service attack prevents legitimate remote
@@ -303,6 +311,7 @@ config SYN_COOKIES
 
 config NET_IPVTI
 	tristate "Virtual (secure) IP: tunneling"
+	depends on IPV4
 	select INET_TUNNEL
 	select NET_IP_TUNNEL
 	select XFRM
@@ -315,11 +324,13 @@ config NET_IPVTI
 
 config NET_UDP_TUNNEL
 	tristate
+	depends on IPV4
 	select NET_IP_TUNNEL
 	default n
 
 config NET_FOU
 	tristate "IP: Foo (IP protocols) over UDP"
+	depends on IPV4
 	select NET_UDP_TUNNEL
 	help
 	  Foo over UDP allows any IP protocol to be directly encapsulated
@@ -338,6 +349,7 @@ config NET_FOU_IP_TUNNELS
 
 config INET_AH
 	tristate "IP: AH transformation"
+	depends on IPV4
 	select XFRM_AH
 	help
 	  Support for IPsec AH (Authentication Header).
@@ -353,6 +365,7 @@ config INET_AH
 
 config INET_ESP
 	tristate "IP: ESP transformation"
+	depends on IPV4
 	select XFRM_ESP
 	help
 	  Support for IPsec ESP (Encapsulating Security Payload).
@@ -393,6 +406,7 @@ config INET_ESPINTCP
 
 config INET_IPCOMP
 	tristate "IP: IPComp transformation"
+	depends on IPV4
 	select INET_XFRM_TUNNEL
 	select XFRM_IPCOMP
 	help
@@ -747,7 +761,7 @@ config TCP_AO
 	select CRYPTO_LIB_SHA1
 	select CRYPTO_LIB_SHA256
 	select CRYPTO_LIB_UTILS
-	depends on 64BIT # seq-number extension needs WRITE_ONCE(u64)
+	depends on IPV4 && 64BIT # seq-number extension needs WRITE_ONCE(u64)
 	help
 	  TCP-AO specifies the use of stronger Message Authentication Codes (MACs),
 	  protects against replays for long-lived TCP connections, and
@@ -758,6 +772,7 @@ config TCP_AO
 
 config TCP_MD5SIG
 	bool "TCP: MD5 Signature Option support (RFC2385)"
+	depends on IPV4
 	select CRYPTO_LIB_MD5
 	select CRYPTO_LIB_UTILS
 	help
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 06e21c26b76f..8b148be79fa4 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -3,18 +3,17 @@
 # Makefile for the Linux TCP/IP (INET) layer.
 #
 
-obj-y     := route.o inetpeer.o protocol.o \
-	     ip_input.o ip_fragment.o ip_forward.o ip_options.o \
-	     ip_output.o ip_sockglue.o inet_hashtables.o \
-	     inet_timewait_sock.o inet_connection_sock.o \
-	     tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \
+obj-y     := inetpeer.o protocol.o inet_hashtables.o inet_timewait_sock.o \
+	     inet_connection_sock.o tcp.o tcp_input.o tcp_output.o tcp_timer.o \
 	     tcp_minisocks.o tcp_cong.o tcp_metrics.o tcp_fastopen.o \
-	     tcp_recovery.o tcp_ulp.o \
-	     tcp_offload.o tcp_plb.o datagram.o raw.o udp.o \
-	     udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \
-	     fib_frontend.o fib_semantics.o fib_trie.o fib_notifier.o \
-	     inet_fragment.o ping.o ip_tunnel_core.o gre_offload.o \
-	     metrics.o netlink.o nexthop.o udp_tunnel_stub.o
+	     tcp_recovery.o tcp_ulp.o tcp_offload.o tcp_plb.o raw.o udp.o \
+	     udp_offload.o af_inet.o inet_fragment.o ping.o ip_tunnel_core.o \
+	     gre_offload.o metrics.o netlink.o nexthop.o udp_tunnel_stub.o
+
+obj-$(CONFIG_IPV4) += route.o ip_input.o ip_fragment.o ip_forward.o ip_options.o \
+		      ip_sockglue.o tcp_ipv4.o datagram.o icmp.o arp.o devinet.o \
+		      igmp.o fib_notifier.o ip_output.o fib_frontend.o \
+		      fib_semantics.o fib_trie.o
 
 obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o
 obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index 7dc9772fe2d8..ab4fa6a6d453 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -4,7 +4,7 @@
 #
 
 menu "IP: Netfilter Configuration"
-	depends on INET && NETFILTER
+	depends on INET && NETFILTER && IPV4
 
 config NF_DEFRAG_IPV4
 	tristate
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index c3806c6ac96f..f83efc1505cc 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -47,6 +47,7 @@ config IPV6_OPTIMISTIC_DAD
 config INET6_AH
 	tristate "IPv6: AH transformation"
 	select XFRM_AH
+	depends on IPV4
 	help
 	  Support for IPsec AH (Authentication Header).
 
@@ -61,6 +62,7 @@ config INET6_AH
 
 config INET6_ESP
 	tristate "IPv6: ESP transformation"
+	depends on IPV4
 	select XFRM_ESP
 	help
 	  Support for IPsec ESP (Encapsulating Security Payload).
@@ -101,6 +103,7 @@ config INET6_ESPINTCP
 
 config INET6_IPCOMP
 	tristate "IPv6: IPComp transformation"
+	depends on IPV4
 	select INET6_XFRM_TUNNEL
 	select XFRM_IPCOMP
 	help
@@ -111,6 +114,7 @@ config INET6_IPCOMP
 
 config IPV6_MIP6
 	tristate "IPv6: Mobility"
+	depends on IPV4
 	select XFRM
 	help
 	  Support for IPv6 Mobility described in RFC 3775.
@@ -119,6 +123,7 @@ config IPV6_MIP6
 
 config IPV6_ILA
 	tristate "IPv6: Identifier Locator Addressing (ILA)"
+	depends on IPV4
 	depends on NETFILTER
 	select DST_CACHE
 	select LWTUNNEL
@@ -149,6 +154,7 @@ config INET6_TUNNEL
 
 config IPV6_VTI
 	tristate "Virtual (secure) IPv6: tunneling"
+	depends on IPV4
 	select IPV6_TUNNEL
 	select NET_IP_TUNNEL
 	select XFRM
@@ -161,6 +167,7 @@ config IPV6_VTI
 
 config IPV6_SIT
 	tristate "IPv6: IPv6-in-IPv4 tunnel (SIT driver)"
+	depends on IPV4
 	select INET_TUNNEL
 	select NET_IP_TUNNEL
 	select IPV6_NDISC_NODETYPE
@@ -198,6 +205,7 @@ config IPV6_NDISC_NODETYPE
 
 config IPV6_TUNNEL
 	tristate "IPv6: IP-in-IPv6 tunnel (RFC2473)"
+	depends on IPV4
 	select INET6_TUNNEL
 	select DST_CACHE
 	select GRO_CELLS
@@ -209,6 +217,7 @@ config IPV6_TUNNEL
 
 config IPV6_GRE
 	tristate "IPv6: GRE tunnel"
+	depends on IPV4
 	select IPV6_TUNNEL
 	select NET_IP_TUNNEL
 	depends on NET_IPGRE_DEMUX
@@ -256,7 +265,7 @@ config IPV6_SUBTREES
 
 config IPV6_MROUTE
 	bool "IPv6: multicast routing"
-	depends on IPV6
+	depends on IPV4 && IPV6
 	select IP_MROUTE_COMMON
 	help
 	  Support for IPv6 multicast forwarding.
@@ -285,7 +294,7 @@ config IPV6_PIMSM_V2
 
 config IPV6_SEG6_LWTUNNEL
 	bool "IPv6: Segment Routing Header encapsulation support"
-	depends on IPV6
+	depends on IPV4 && IPV6
 	select LWTUNNEL
 	select DST_CACHE
 	select IPV6_MULTIPLE_TABLES
@@ -316,7 +325,7 @@ config IPV6_SEG6_BPF
 
 config IPV6_RPL_LWTUNNEL
 	bool "IPv6: RPL Source Routing Header support"
-	depends on IPV6
+	depends on IPV4 && IPV6
 	select LWTUNNEL
 	select DST_CACHE
 	help
@@ -327,7 +336,7 @@ config IPV6_RPL_LWTUNNEL
 
 config IPV6_IOAM6_LWTUNNEL
 	bool "IPv6: IOAM Pre-allocated Trace insertion support"
-	depends on IPV6
+	depends on IPV4 && IPV6
 	select LWTUNNEL
 	select DST_CACHE
 	help
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index 81daf82ddc2d..f99a2176bf3d 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -4,7 +4,7 @@
 #
 
 menu "IPv6: Netfilter Configuration"
-	depends on INET && IPV6 && NETFILTER
+	depends on INET && IPV6 && NETFILTER && IPV4
 
 # old sockopt interface and eval loop
 config IP6_NF_IPTABLES_LEGACY
diff --git a/net/l2tp/Kconfig b/net/l2tp/Kconfig
index 0de178d5baba..e92cf6d94135 100644
--- a/net/l2tp/Kconfig
+++ b/net/l2tp/Kconfig
@@ -5,7 +5,7 @@
 
 menuconfig L2TP
 	tristate "Layer Two Tunneling Protocol (L2TP)"
-	depends on INET
+	depends on INET && IPV4
 	select NET_UDP_TUNNEL
 	help
 	  Layer Two Tunneling Protocol
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index d6bc295e23a1..ec485da28373 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config MAC80211
 	tristate "Generic IEEE 802.11 Networking Stack (mac80211)"
-	depends on CFG80211
+	depends on CFG80211 && IPV4
 	select CRYPTO
 	select CRYPTO_LIB_AES_CBC_MACS
 	select CRYPTO_LIB_ARC4
diff --git a/net/mpls/Kconfig b/net/mpls/Kconfig
index d672ab72ab12..541ba192e2a1 100644
--- a/net/mpls/Kconfig
+++ b/net/mpls/Kconfig
@@ -25,6 +25,7 @@ config NET_MPLS_GSO
 
 config MPLS_ROUTING
 	tristate "MPLS: routing support"
+	depends on IPV4
 	depends on NET_IP_TUNNEL || NET_IP_TUNNEL=n
 	depends on PROC_SYSCTL
 	help
diff --git a/net/mptcp/Kconfig b/net/mptcp/Kconfig
index be71fc9b4638..c5b660fdda4c 100644
--- a/net/mptcp/Kconfig
+++ b/net/mptcp/Kconfig
@@ -1,7 +1,7 @@
 
 config MPTCP
 	bool "MPTCP: Multipath TCP"
-	depends on INET
+	depends on INET && IPV4
 	select SKB_EXTENSIONS
 	select CRYPTO_LIB_SHA256
 	select CRYPTO_LIB_UTILS
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 4c04cd8d40a2..3ee9bb726e40 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menu "Core Netfilter Configuration"
-	depends on INET && NETFILTER
+	depends on INET && IPV4 && NETFILTER
 
 config NETFILTER_INGRESS
 	bool "Netfilter ingress support"
diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig
index 6c4d54758106..e6537c842a45 100644
--- a/net/netfilter/ipset/Kconfig
+++ b/net/netfilter/ipset/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menuconfig IP_SET
 	tristate "IP set support"
-	depends on INET && NETFILTER
+	depends on INET && IPV4 && NETFILTER
 	select NETFILTER_NETLINK
 	help
 	  This option adds IP set support to the kernel.
diff --git a/net/netfilter/ipvs/Kconfig b/net/netfilter/ipvs/Kconfig
index 7724cb44e6de..fb09218d84d0 100644
--- a/net/netfilter/ipvs/Kconfig
+++ b/net/netfilter/ipvs/Kconfig
@@ -4,7 +4,7 @@
 #
 menuconfig IP_VS
 	tristate "IP virtual server support"
-	depends on INET && NETFILTER
+	depends on INET && NETFILTER && IPV4
 	depends on (NF_CONNTRACK || NF_CONNTRACK=n)
 	help
 	  IP Virtual Server support will let you build a high-performance
diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig
index e6aaee92dba4..5931c28ef965 100644
--- a/net/openvswitch/Kconfig
+++ b/net/openvswitch/Kconfig
@@ -5,7 +5,7 @@
 
 config OPENVSWITCH
 	tristate "Open vSwitch"
-	depends on INET
+	depends on INET && IPV4
 	depends on !NF_CONNTRACK || \
 		   (NF_CONNTRACK && ((!NF_DEFRAG_IPV6 || NF_DEFRAG_IPV6) && \
 				     (!NF_NAT || NF_NAT) && \
diff --git a/net/packet/Kconfig b/net/packet/Kconfig
index 2997382d597c..00fda9e08cb0 100644
--- a/net/packet/Kconfig
+++ b/net/packet/Kconfig
@@ -5,6 +5,7 @@
 
 config PACKET
 	tristate "Packet socket"
+	depends on IPV4
 	help
 	  The Packet protocol is used by applications which communicate
 	  directly with network devices without an intermediate network
diff --git a/net/rds/Kconfig b/net/rds/Kconfig
index f007730aa2bb..d33cf3d89632 100644
--- a/net/rds/Kconfig
+++ b/net/rds/Kconfig
@@ -2,7 +2,7 @@
 
 config RDS
 	tristate "The Reliable Datagram Sockets Protocol"
-	depends on INET
+	depends on INET && IPV4
 	help
 	  The RDS (Reliable Datagram Sockets) protocol provides reliable,
 	  sequenced delivery of datagrams over Infiniband or TCP.
diff --git a/net/rxrpc/Kconfig b/net/rxrpc/Kconfig
index 2cebb666dc07..2029d2d1065f 100644
--- a/net/rxrpc/Kconfig
+++ b/net/rxrpc/Kconfig
@@ -5,7 +5,7 @@
 
 config AF_RXRPC
 	tristate "RxRPC session sockets"
-	depends on INET
+	depends on INET && IPV4
 	select CRYPTO
 	select CRYPTO_LIB_DES if RXKAD
 	select KEYS
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 6ddff028b81a..bb782822c3fb 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -807,7 +807,7 @@ config NET_ACT_SKBEDIT
 
 config NET_ACT_CSUM
 	tristate "Checksum Updating"
-	depends on NET_CLS_ACT && INET
+	depends on NET_CLS_ACT && INET && IPV4
 	select NET_CRC32C
 	help
 	  Say Y here to update some common checksum after some direct
diff --git a/net/sctp/Kconfig b/net/sctp/Kconfig
index fc989a3791b3..eb3e2bf1ec3a 100644
--- a/net/sctp/Kconfig
+++ b/net/sctp/Kconfig
@@ -5,7 +5,7 @@
 
 menuconfig IP_SCTP
 	tristate "The SCTP Protocol"
-	depends on INET
+	depends on INET && IPV4
 	select CRYPTO_LIB_SHA1
 	select CRYPTO_LIB_SHA256
 	select CRYPTO_LIB_UTILS
diff --git a/net/smc/Kconfig b/net/smc/Kconfig
index 277ef504bc26..52005e4bf77d 100644
--- a/net/smc/Kconfig
+++ b/net/smc/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config SMC
 	tristate "SMC socket protocol family"
-	depends on INET && INFINIBAND && DIBS
+	depends on INET && INFINIBAND && DIBS && IPV4
 	help
 	  SMC-R provides a "sockets over RDMA" solution making use of
 	  RDMA over Converged Ethernet (RoCE) technology to upgrade
diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig
index e7808e5714dc..58e6f6db3100 100644
--- a/net/sunrpc/Kconfig
+++ b/net/sunrpc/Kconfig
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config SUNRPC
 	tristate
-	depends on MULTIUSER
+	depends on MULTIUSER && IPV4
 
 config SUNRPC_GSS
 	tristate
diff --git a/net/tipc/Kconfig b/net/tipc/Kconfig
index 18f62135e47b..869e3fe961f9 100644
--- a/net/tipc/Kconfig
+++ b/net/tipc/Kconfig
@@ -5,7 +5,7 @@
 
 menuconfig TIPC
 	tristate "The TIPC Protocol"
-	depends on INET
+	depends on INET && IPV4
 	help
 	  The Transparent Inter Process Communication (TIPC) protocol is
 	  specially designed for intra cluster communication. This protocol
diff --git a/net/xfrm/Kconfig b/net/xfrm/Kconfig
index 4a62817a88f8..2e4d466c7bd3 100644
--- a/net/xfrm/Kconfig
+++ b/net/xfrm/Kconfig
@@ -4,7 +4,7 @@
 #
 config XFRM
 	bool
-	depends on INET
+	depends on INET && IPV4
 	select GRO_CELLS
 	select SKB_EXTENSIONS
 
@@ -13,6 +13,7 @@ config XFRM_OFFLOAD
 
 config XFRM_ALGO
 	tristate
+	depends on IPV4
 	select XFRM
 	select CRYPTO
 	select CRYPTO_AEAD
@@ -23,6 +24,7 @@ if INET
 config XFRM_USER
 	tristate "Transformation user configuration interface"
 	select XFRM_ALGO
+	depends on IPV4
 	help
 	  Support for Transformation(XFRM) user configuration interface
 	  like IPsec used by native Linux tools.
@@ -42,7 +44,7 @@ config XFRM_USER_COMPAT
 
 config XFRM_INTERFACE
 	tristate "Transformation virtual interface"
-	depends on XFRM && IPV6
+	depends on XFRM && IPV4 && IPV6
 	help
 	  This provides a virtual interface to route IPsec traffic.
 
@@ -83,6 +85,7 @@ config XFRM_STATISTICS
 # RFC 8221 lists as MUST be implemented.
 config XFRM_AH
 	tristate
+	depends on IPV4
 	select XFRM_ALGO
 	select CRYPTO
 	select CRYPTO_HMAC
@@ -92,6 +95,7 @@ config XFRM_AH
 # algorithms that RFC 8221 lists as MUST be implemented.
 config XFRM_ESP
 	tristate
+	depends on IPV4
 	select XFRM_ALGO
 	select CRYPTO
 	select CRYPTO_AES
@@ -105,12 +109,14 @@ config XFRM_ESP
 
 config XFRM_IPCOMP
 	tristate
+	depends on IPV4
 	select XFRM_ALGO
 	select CRYPTO
 	select CRYPTO_DEFLATE
 
 config NET_KEY
 	tristate "PF_KEY sockets (deprecated)"
+	depends on IPV4
 	select XFRM_ALGO
 	help
 	  PF_KEYv2 socket family, compatible to KAME ones.
@@ -140,6 +146,7 @@ config NET_KEY_MIGRATE
 
 config XFRM_IPTFS
 	tristate "IPsec IP-TFS/AGGFRAG (RFC 9347) encapsulation support"
+	depends on IPV4
 	depends on XFRM
 	help
 	  Information on the IP-TFS/AGGFRAG encapsulation can be found
@@ -156,5 +163,6 @@ config XFRM_IPTFS
 
 config XFRM_ESPINTCP
 	bool
+	depends on IPV4
 
 endif # INET
-- 
2.54.0


^ permalink raw reply related


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