Linux wireless drivers development
 help / color / mirror / Atom feed
From: Ping-Ke Shih <pkshih@realtek.com>
To: <linux-wireless@vger.kernel.org>
Cc: <ku920601@realtek.com>
Subject: [PATCH rtw-next 01/11] wifi: rtw89: coex: Add Wi-Fi role info version 10
Date: Sun, 12 Jul 2026 11:04:56 +0800	[thread overview]
Message-ID: <20260712030506.43438-2-pkshih@realtek.com> (raw)
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


  reply	other threads:[~2026-07-12  3:06 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-12  3:04 [PATCH rtw-next 00/11] wifi: rtw89: coex: implement coex for RTL8922D Ping-Ke Shih
2026-07-12  3:04 ` Ping-Ke Shih [this message]
2026-07-12  3:04 ` [PATCH rtw-next 02/11] wifi: rtw89: coex: Rearrange coexistence control structure Ping-Ke Shih
2026-07-12  3:04 ` [PATCH rtw-next 03/11] wifi: rtw89: coex: Update driver outsource info to firmware version 6 Ping-Ke Shih
2026-07-12  3:04 ` [PATCH rtw-next 04/11] wifi: rtw89: ceox: Update antenna & grant signal setting Ping-Ke Shih
2026-07-12  3:05 ` [PATCH rtw-next 05/11] wifi: rtw89: coex: Rearrange Bluetooth firmware report entry Ping-Ke Shih
2026-07-12  3:05 ` [PATCH rtw-next 06/11] wifi: rtw89: coex: Refine send firmware command function Ping-Ke Shih
2026-07-12  3:05 ` [PATCH rtw-next 07/11] wifi: rtw89: coex: Refine _reset_btc_var() Ping-Ke Shih
2026-07-12  3:05 ` [PATCH rtw-next 08/11] wifi: rtw89: coex: Correct SET_RFE settings Ping-Ke Shih
2026-07-12  3:05 ` [PATCH rtw-next 09/11] wifi: rtw89: coex: Add firmware report control report v11 Ping-Ke Shih
2026-07-12  3:05 ` [PATCH rtw-next 10/11] wifi: rtw89: coex: update external control length by case Ping-Ke Shih
2026-07-12  3:05 ` [PATCH rtw-next 11/11] wifi: rtw89: coex: Update coexistence version to 9.24.0 Ping-Ke Shih

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=20260712030506.43438-2-pkshih@realtek.com \
    --to=pkshih@realtek.com \
    --cc=ku920601@realtek.com \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

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

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