* [PATCH rtw-next 01/15] wifi: rtw89: coex: fix RF band check in _set_rf_trx_para() and its propagation
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-07 4:19 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 02/15] wifi: rtw89: coex: track per-HW-band WL role fields with arrays Ping-Ke Shih
` (13 subsequent siblings)
14 siblings, 1 reply; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
dm->tdd_bind.rf_band holds a bitmask built from BIT(BTC_BT_B2G) and
BIT(BTC_BT_B5G), but _set_rf_trx_para() compared it against the raw
band enum values RTW89_BAND_5G and RTW89_BAND_2G instead of their
BIT() form. Every other site in this file that reads tdd_bind.rf_band
wraps the enum with BIT() before comparing.
Because RTW89_BAND_5G equals 1 and BIT(BTC_BT_B2G) also equals 1, a
Wi-Fi/Bluetooth coexistence session bound to the 2.4 GHz band was
misclassified as BTC_WLINK_V0_5G. This incorrectly skipped the RF
Tx/Rx parameter adjustment for 2.4 GHz STA traffic and kept clearing
wl_stb_chg to 0, so chip->ops->btc_wl_s1_standby() never re-enabled
the Wi-Fi standby workaround for the Bluetooth Tx-to-Rx GNT switch on
RTL8852C, degrading Wi-Fi performance while coexisting with Bluetooth
on 2.4 GHz.
Compare rf_band against BIT(RTW89_BAND_5G) and BIT(RTW89_BAND_2G) to
match its actual bitmask representation.
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 | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 6b72e2c80d14..271b002a2d40 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -4186,10 +4186,10 @@ static void _set_bt_tx_power(struct rtw89_dev *rtwdev, bool force_exec, u8 bid,
buf[1] = rf_band; /* bit-map: bit1->5GHz/6Ghz, bit0->2.4GHz */
if (!force_exec && !btc->cli_h2c_cmd) {
- if (rf_band == RTW89_BAND_2G &&
+ if (rf_band == BIT(RTW89_BAND_2G) &&
bt->tx_power_now == level)
continue;
- else if (rf_band != RTW89_BAND_2G &&
+ else if (rf_band != BIT(RTW89_BAND_2G) &&
bt->tx_power_now_6g == level)
continue;
}
@@ -4199,7 +4199,7 @@ static void _set_bt_tx_power(struct rtw89_dev *rtwdev, bool force_exec, u8 bid,
if (!_send_fw_cmd(rtwdev, BTFC_SET, h2c_func, buf, len)) {
btc->dm.rf_trx_para.bt_tx_power[i] = level;
- if (rf_band == RTW89_BAND_2G)
+ if (rf_band == BIT(RTW89_BAND_2G))
bt->tx_power_now = level;
else
bt->tx_power_now_6g = level;
@@ -4240,7 +4240,7 @@ static void _set_bt_rx_gain(struct rtw89_dev *rtwdev, bool force_exec, u8 bid,
id_stop = bid;
}
- if (rf_band == RTW89_BAND_2G)
+ if (rf_band == BIT(RTW89_BAND_2G))
scbd_bit |= BTC_WSCB_RXGAIN;
else
scbd_bit |= BTC_WSCB_RXGAIN_56G;
@@ -4256,10 +4256,10 @@ static void _set_bt_rx_gain(struct rtw89_dev *rtwdev, bool force_exec, u8 bid,
/* return if same setup */
if (!force_exec && !btc->cli_h2c_cmd) {
- if (rf_band == RTW89_BAND_2G &&
+ if (rf_band == BIT(RTW89_BAND_2G) &&
bt->lna_constrain == level)
continue;
- else if (rf_band != RTW89_BAND_2G &&
+ else if (rf_band != BIT(RTW89_BAND_2G) &&
bt->lna_constrain_6g == level)
continue;
}
@@ -4400,9 +4400,9 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev)
if (bmode == BTC_WLINK_NOLINK) {
return;
- } else if (rf_band == RTW89_BAND_5G) {
+ } else if (rf_band == BIT(RTW89_BAND_5G)) {
mode = BTC_WLINK_V0_5G;
- } else if (rf_band == RTW89_BAND_2G && bmode == BTC_WLINK_STA) {
+ } else if (rf_band == BIT(RTW89_BAND_2G) && bmode == BTC_WLINK_STA) {
mode = BTC_WLINK_V0_2G_STA;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH rtw-next 01/15] wifi: rtw89: coex: fix RF band check in _set_rf_trx_para() and its propagation
2026-10-04 3:45 ` [PATCH rtw-next 01/15] wifi: rtw89: coex: fix RF band check in _set_rf_trx_para() and its propagation Ping-Ke Shih
@ 2026-10-07 4:19 ` Ping-Ke Shih
0 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-07 4:19 UTC (permalink / raw)
To: Ping-Ke Shih, linux-wireless; +Cc: ku920601
Ping-Ke Shih <pkshih@realtek.com> wrote:
> From: Ching-Te Ku <ku920601@realtek.com>
>
> dm->tdd_bind.rf_band holds a bitmask built from BIT(BTC_BT_B2G) and
> BIT(BTC_BT_B5G), but _set_rf_trx_para() compared it against the raw
> band enum values RTW89_BAND_5G and RTW89_BAND_2G instead of their
> BIT() form. Every other site in this file that reads tdd_bind.rf_band
> wraps the enum with BIT() before comparing.
>
> Because RTW89_BAND_5G equals 1 and BIT(BTC_BT_B2G) also equals 1, a
> Wi-Fi/Bluetooth coexistence session bound to the 2.4 GHz band was
> misclassified as BTC_WLINK_V0_5G. This incorrectly skipped the RF
> Tx/Rx parameter adjustment for 2.4 GHz STA traffic and kept clearing
> wl_stb_chg to 0, so chip->ops->btc_wl_s1_standby() never re-enabled
> the Wi-Fi standby workaround for the Bluetooth Tx-to-Rx GNT switch on
> RTL8852C, degrading Wi-Fi performance while coexisting with Bluetooth
> on 2.4 GHz.
>
> Compare rf_band against BIT(RTW89_BAND_5G) and BIT(RTW89_BAND_2G) to
> match its actual bitmask representation.
>
> Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
15 patch(es) applied to rtw-next branch of rtw.git, thanks.
bf4c84e72fbd wifi: rtw89: coex: fix RF band check in _set_rf_trx_para() and its propagation
fd4d5551eae1 wifi: rtw89: coex: track per-HW-band WL role fields with arrays
fbe6912f1980 wifi: rtw89: coex: show per-HW-band MLO state in WL status dump
806bd61d5139 wifi: rtw89: coex: add outsrc-set-info v7 for BT SPDT state
235a6ff568c3 wifi: rtw89: coex: compute WL/BT cross-talk interference maps
c4b17492a37c wifi: rtw89: coex: add 1SS MIMO-PS decision for dual-BT CIS
a35cf81f6084 wifi: rtw89: coex: add firmware 0.35.119.3 support for RTL8922A/D
8773800337cd wifi: rtw89: coex: raise default BT TX power to 10 dBm
d4a862cf7661 wifi: rtw89: coex: bind the RF band in use while WL is doing RFK
96ad55bfbb65 wifi: rtw89: coex: store BT TX power per device and per RF band
f3207ae57274 wifi: rtw89: coex: set the WL/BT antenna map on shared-antenna chips
236478d51ffc wifi: rtw89: coex: use the right switch-type enum on RTL8922D
c739640e347f wifi: rtw89: phy: Implement API to control BTG to turn on/off BT TX/RX
256725d0ec78 wifi: rtw89: coex: turn BT TX/RX on and off through the BTG control
57dc6b251419 wifi: rtw89: coex: set rf_combination in _update_wl_mlo_info()
---
https://github.com/pkshih/rtw.git
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH rtw-next 02/15] wifi: rtw89: coex: track per-HW-band WL role fields with arrays
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 01/15] wifi: rtw89: coex: fix RF band check in _set_rf_trx_para() and its propagation Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 03/15] wifi: rtw89: coex: show per-HW-band MLO state in WL status dump Ping-Ke Shih
` (12 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
The role info fields that distinguish HW-band 0 from HW-band 1 were
kept as separate scalar members with a "_hb1" suffix for the second
band, which forced every caller to branch on hw_band and duplicate
the same logic for each member. Convert link_mode, p2p_exist and
role_map in the role info, plus is_5g_hi_ch, go_client_exist,
noa_exist and bg_mode in the WL info, into RTW89_MAC_NUM-indexed
arrays so a single hw_band index addresses both bands.
This lets _update_wl_link_mode(), _update_wl_mlo_info(),
_modify_role_link_mode() and _update_wl_info() drop their
hw_band == RTW89_PHY_1 branches in favor of direct array indexing,
and removes the duplicated per-band assignments at every call site.
The firmware-facing H2C formats are unchanged; only the internal
representation moved from named pairs to an array. Add v11 of the
role info H2C structure and its sender so a later firmware version
bump has a destination to send to. And it should not sharing layout
with the existing v10 structure.
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 | 138 ++++++++++------------
drivers/net/wireless/realtek/rtw89/core.h | 38 +++---
drivers/net/wireless/realtek/rtw89/fw.c | 113 +++++++++++++++---
drivers/net/wireless/realtek/rtw89/fw.h | 6 +
4 files changed, 186 insertions(+), 109 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 271b002a2d40..8412c082ddc8 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -3793,6 +3793,8 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index)
rtw89_fw_h2c_cxdrv_role_v8(rtwdev, index);
else if (ver->fwlrole == 10)
rtw89_fw_h2c_cxdrv_role_v10(rtwdev, index);
+ else if (ver->fwlrole == 11)
+ rtw89_fw_h2c_cxdrv_role_v11(rtwdev, index);
break;
case CXDRVINFO_CTRL:
if (ver->drvinfo_ver != 0)
@@ -5244,7 +5246,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 & BIT(RTW89_WIFI_ROLE_AP))
+ if (btc->cx.wl.role_info.role_map[RTW89_MAC_0] & BIT(RTW89_WIFI_ROLE_AP))
_tdma_set_flctrl(btc, CXFLC_QOSNULL);
switch (policy_type) {
@@ -5449,7 +5451,7 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type)
tbl_b1 = cxtbl[17];
tbl_b4 = cxtbl[17];
- if (wl->bg_mode)
+ if (wl->bg_mode[RTW89_MAC_0])
tbl_w1 = cxtbl[8];
else if ((wl->status.map.traffic_dir & BIT(RTW89_TFC_UL)) &&
hid->exist)
@@ -5925,7 +5927,7 @@ static void _set_ant(struct rtw89_dev *rtwdev, bool force_exec,
dm->run_reason == BTC_RSN_CMD_SET_COEX)
force_exec = FC_EXEC;
- if (wl_rinfo->link_mode != BTC_WLINK_DB_MCC &&
+ if (wl_rinfo->link_mode[RTW89_MAC_0] != BTC_WLINK_DB_MCC &&
btc->dm.wl_btg_rx == 2)
force_exec = FC_EXEC;
@@ -6141,7 +6143,7 @@ static void _action_bt_hfp(struct rtw89_dev *rtwdev)
_set_policy(rtwdev, BTC_CXP_OFF_BWB1, BTC_ACT_BT_HFP);
}
} else {
- if (wl->bg_mode)
+ if (wl->bg_mode[RTW89_MAC_0])
_set_policy(rtwdev, BTC_CXP_OFF_BWB1, BTC_ACT_BT_HFP);
else if (wl->status.map.traffic_dir & BIT(RTW89_TFC_UL))
_set_policy(rtwdev, BTC_CXP_OFF_EQ5, BTC_ACT_BT_HFP);
@@ -6179,7 +6181,7 @@ static void _action_bt_hid(struct rtw89_dev *rtwdev)
policy_type = BTC_CXP_OFF_BWB1;
}
} else { /* dedicated-antenna */
- if (wl->bg_mode)
+ if (wl->bg_mode[RTW89_MAC_0])
policy_type = BTC_CXP_OFF_BWB1;
else if (wl->status.map.traffic_dir & BIT(RTW89_TFC_UL))
policy_type = BTC_CXP_OFF_EQ4;
@@ -6520,7 +6522,7 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev)
/* bb call ctrl_btg() in WL FW by slot */
if (!btc->ver->fcxosi &&
- wl_rinfo->link_mode == BTC_WLINK_DB_MCC)
+ wl_rinfo->link_mode[RTW89_MAC_0] == BTC_WLINK_DB_MCC)
is_btg = BTC_BTGCTRL_BB_GNT_FWCTRL;
}
@@ -6580,7 +6582,7 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev)
is_preagc = BTC_PREAGC_DISABLE;
} else if (dm->tdd_bind.rf_band == BIT(RTW89_BAND_5G)) {
is_preagc = BTC_PREAGC_DISABLE;
- } else if (rinfo->link_mode == BTC_WLINK_NOLINK ||
+ } else if (rinfo->link_mode[RTW89_MAC_0] == 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 &&
@@ -6596,7 +6598,7 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev)
is_preagc = BTC_PREAGC_ENABLE;
}
- if (!btc->ver->fcxosi && rinfo->link_mode == BTC_WLINK_DB_MCC)
+ if (!btc->ver->fcxosi && rinfo->link_mode[RTW89_MAC_0] == BTC_WLINK_DB_MCC)
is_preagc = BTC_PREAGC_BB_FWCTRL;
if (dm->wl_pre_agc_rb != dm->wl_pre_agc &&
@@ -6743,7 +6745,7 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev)
if (btc->dm.freerun || btc->ctrl.igno_bt || b->link_cnt.now == 0 ||
dm->tdd_bind.rf_band == BIT(RTW89_BAND_5G) ||
- wl_rinfo->link_mode == BTC_WLINK_NOLINK) {
+ wl_rinfo->link_mode[RTW89_MAC_0] == BTC_WLINK_NOLINK) {
enable = 0;
tx_time = BTC_MAX_TX_TIME_DEF;
tx_retry = BTC_MAX_TX_RETRY_DEF;
@@ -6852,7 +6854,7 @@ static void _set_bt_rx_agc(struct rtw89_dev *rtwdev)
struct rtw89_btc_bt_info *bt = &btc->cx.bt0;
bool bt_hi_lna_rx = false;
- if (wl_rinfo->link_mode != BTC_WLINK_NOLINK && btc->dm.wl_btg_rx)
+ if (wl_rinfo->link_mode[RTW89_MAC_0] != BTC_WLINK_NOLINK && btc->dm.wl_btg_rx)
bt_hi_lna_rx = true;
if (bt_hi_lna_rx == bt->hi_lna_rx)
@@ -6903,7 +6905,7 @@ static void _update_zb_coex_tbl(struct rtw89_dev *rtwdev)
struct rtw89_btc *btc = &rtwdev->btc;
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 mode = btc->cx.wl.role_info.link_mode[RTW89_MAC_0];
if (btc->dm.run_reason != BTC_RSN_NTFY_INIT && !link_mode_chg)
return;
@@ -7501,46 +7503,43 @@ static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type)
struct rtw89_btc_wl_info *wl = &btc->cx.wl;
struct rtw89_btc_wl_mlo_info *mlo_info = &wl->mlo_info;
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;
+ u8 p2p_exist = wl->role_info.p2p_exist[hw_band];
/* MLD: nLmR --> 2L1R: 2-Link by 1-RF, 2L2R: 2-link by 2-RF */
switch (type) {
case RTW89_MR_WTYPE_NONE: /* no-link */
- r->link_mode = BTC_WLINK_NOLINK;
+ r->link_mode[hw_band] = 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;
+ r->link_mode[hw_band] = BTC_WLINK_GO;
} else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1CLIENT &&
p2p_exist) {
- r->link_mode = BTC_WLINK_GC;
+ r->link_mode[hw_band] = BTC_WLINK_GC;
} else {
- r->link_mode = BTC_WLINK_STA;
+ r->link_mode[hw_band] = BTC_WLINK_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;
+ r->link_mode[hw_band] = 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;
+ r->link_mode[hw_band] = BTC_WLINK_SB_MCC;
} else {
- r->link_mode = BTC_WLINK_SCC;
+ r->link_mode[hw_band] = BTC_WLINK_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;
+ r->link_mode[hw_band] = BTC_WLINK_GC;
else
- r->link_mode = BTC_WLINK_STA;
+ r->link_mode[hw_band] = BTC_WLINK_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*/
@@ -7548,11 +7547,11 @@ static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type)
* 2G+5G -> TDMA slot switch by E2G/E5G
* 5G only -> TDMA slot switch by E5G
*/
- r->link_mode = BTC_WLINK_DB_MCC;
+ r->link_mode[hw_band] = BTC_WLINK_DB_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;
+ r->link_mode[hw_band] = BTC_WLINK_STA;
break;
}
}
@@ -7647,7 +7646,7 @@ static void _update_wl_mlo_info(struct rtw89_dev *rtwdev, u8 hw_band)
case MLO_2_PLUS_0_1RF: /* 2+0 */
case MLO_2_PLUS_0_2RF:
_update_wl_link_mode(rtwdev, RTW89_MAC_0, type);
- wl_rinfo->link_mode_hb1 = wl_rinfo->link_mode;
+ wl_rinfo->link_mode[RTW89_MAC_1] = wl_rinfo->link_mode[RTW89_MAC_0];
wl_rinfo->pta_req_band = RTW89_MAC_0;
wl_rinfo->dbcc_2g_phy = RTW89_PHY_0;
wl_rinfo->dbcc_en = 0;
@@ -7659,7 +7658,7 @@ static void _update_wl_mlo_info(struct rtw89_dev *rtwdev, u8 hw_band)
case MLO_0_PLUS_2_1RF: /* 0+2 */
case MLO_0_PLUS_2_2RF:
_update_wl_link_mode(rtwdev, RTW89_MAC_1, type);
- wl_rinfo->link_mode_hb1 = wl_rinfo->link_mode;
+ wl_rinfo->link_mode[RTW89_MAC_0] = wl_rinfo->link_mode[RTW89_MAC_1];
wl_rinfo->pta_req_band = RTW89_MAC_1;
wl_rinfo->dbcc_2g_phy = RTW89_PHY_1;
wl_rinfo->dbcc_en = 0;
@@ -7686,7 +7685,6 @@ static void _update_wl_mlo_info(struct rtw89_dev *rtwdev, u8 hw_band)
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;
}
@@ -7706,8 +7704,10 @@ static void _update_wl_mlo_info(struct rtw89_dev *rtwdev, u8 hw_band)
break;
}
- rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(), mode=%s, pta_band=%d",
- __func__, id_to_linkmode(wl_rinfo->link_mode),
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(), link_mode(HWB0=%s/HWB1=%s), pta_band=%d",
+ __func__,
+ id_to_linkmode(wl_rinfo->link_mode[RTW89_MAC_0]),
+ id_to_linkmode(wl_rinfo->link_mode[RTW89_MAC_1]),
wl_rinfo->pta_req_band);
}
@@ -7780,8 +7780,8 @@ static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev)
mode = _get_role_link_mode(cid_role[0]);
}
- wl_rinfo->link_mode = mode;
- wl_rinfo->link_mode_hb1 = mode;
+ wl_rinfo->link_mode[RTW89_MAC_0] = mode;
+ wl_rinfo->link_mode[RTW89_MAC_1] = mode;
}
static void _sync_wl_link_mode_v0(struct rtw89_dev *rtwdev)
@@ -7796,12 +7796,12 @@ static void _sync_wl_link_mode_v0(struct rtw89_dev *rtwdev)
r->rlink[i][j].rf_band == RTW89_BAND_2G)
has_2g = true;
- if (!has_2g && r->link_mode != BTC_WLINK_NOLINK) {
+ if (!has_2g && r->link_mode[RTW89_MAC_0] != BTC_WLINK_NOLINK) {
r->link_mode_v0 = BTC_WLINK_V0_5G;
return;
}
- switch (r->link_mode) {
+ switch (r->link_mode[RTW89_MAC_0]) {
case BTC_WLINK_NOLINK:
r->link_mode_v0 = BTC_WLINK_V0_NOLINK;
break;
@@ -7837,18 +7837,12 @@ static void _modify_role_link_mode(struct rtw89_dev *rtwdev, u8 hw_band)
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;
- u32 role_map = wl_rinfo->role_map;
- u8 noa_exist = wl->noa_exist;
+ u8 go_cleint_exist = wl->go_client_exist[hw_band];
+ u8 *link_mode = &wl_rinfo->link_mode[hw_band];
+ u32 role_map = wl_rinfo->role_map[hw_band];
+ u8 noa_exist = wl->noa_exist[hw_band];
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) {
@@ -7892,7 +7886,7 @@ static void _modify_role_link_mode(struct rtw89_dev *rtwdev, u8 hw_band)
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);
+ id_to_linkmode(*link_mode), wl_rinfo->mrole_type);
}
static void _update_wl_info(struct rtw89_dev *rtwdev, struct rtw89_btc_wl_link_info *wl_linfo)
@@ -7981,23 +7975,13 @@ static void _update_wl_info(struct rtw89_dev *rtwdev, struct rtw89_btc_wl_link_i
bg_mode = 1;
}
- 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;
- }
+ link_mode_ori = wl_rinfo->link_mode[rlink_id];
+ wl->is_5g_hi_ch[rlink_id] = is_5g_hi_ch;
+ wl->bg_mode[rlink_id] = bg_mode;
+ wl->go_client_exist[rlink_id] = go_client_exist;
+ wl->noa_exist[rlink_id] = noa_exist;
+ wl_rinfo->p2p_exist[rlink_id] = p2p_exist;
+ wl_rinfo->role_map[rlink_id] = role_map;
dbcc_en_ori = wl_rinfo->dbcc_en;
@@ -8011,8 +7995,7 @@ static void _update_wl_info(struct rtw89_dev *rtwdev, struct rtw89_btc_wl_link_i
_modify_role_link_mode(rtwdev, rlink_id);
_sync_wl_link_mode_v0(rtwdev);
- 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)) {
+ if (wl_rinfo->link_mode[rlink_id] != link_mode_ori) {
wl_rinfo->link_mode_chg = 1;
wl->link_mode_chg = 1;
}
@@ -8487,7 +8470,7 @@ 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) {
- if (wl->role_info.link_mode != BTC_WLINK_DB_MCC) { /* mode chg */
+ if (wl->role_info.link_mode[RTW89_MAC_0] != 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
@@ -8527,19 +8510,19 @@ static void _set_bind_info(struct rtw89_btc *btc, u8 type)
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)
+ if (wl->role_info.link_mode[RTW89_MAC_0] == BTC_WLINK_AP ||
+ wl->role_info.link_mode[RTW89_MAC_1] == 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;
+ bd->wl_bg_mode = wl->bg_mode[RTW89_MAC_0] | wl->bg_mode[RTW89_MAC_1];
} 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;
+ bd->wl_link_mode = wl->role_info.link_mode[RTW89_MAC_1];
+ bd->wl_bg_mode = wl->bg_mode[RTW89_MAC_1];
} 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;
+ bd->wl_link_mode = wl->role_info.link_mode[RTW89_MAC_0];
+ bd->wl_bg_mode = wl->bg_mode[RTW89_MAC_0];
}
/* update Bind-BT status map for BT0/BT1 */
@@ -8657,7 +8640,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_DB_MCC) {/* mode chg */
+ if (wl->role_info.link_mode[RTW89_MAC_0] != 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
@@ -10317,10 +10300,11 @@ static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
p += scnprintf(p, end - p, "========== [WL Status] ==========\n");
- mode = wl_rinfo->link_mode;
+ mode = wl_rinfo->link_mode[RTW89_MAC_0];
- p += scnprintf(p, end - p, " %-15s : link_mode:%s, ", "[status]",
- id_to_linkmode(mode));
+ p += scnprintf(p, end - p, " %-15s : link_mode:%s(HWB0)/%s(HWB1), ", "[status]",
+ id_to_linkmode(mode),
+ id_to_linkmode(wl_rinfo->link_mode[RTW89_MAC_1]));
p += scnprintf(p, end - p,
"rf_off:%d, power_save:%d, scan:%s(band:%d/phy_map:0x%x), ",
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 7e74b5c9ed49..d66a1febb85c 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -2055,6 +2055,23 @@ struct rtw89_btc_wl_role_info_v10 { /* H2C info, struct size must be n*4 bytes *
__le32 mrole_type; /* btc_wl_mrole_type: [31:16]:band1, [15:0]:band0 */
} __packed;
+struct rtw89_btc_wl_role_info_v11 { /* 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;
@@ -2089,18 +2106,15 @@ struct rtw89_btc_wl_rlink { /* Logic dynamic using */
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 link_mode[RTW89_MAC_NUM];
+ u8 p2p_exist[RTW89_MAC_NUM];
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 role_map_hb1;
+ u32 role_map[RTW89_MAC_NUM];
u32 mrole_type; /* btc_wl_mrole_type: [31:16]:band1, [15:0]:band0 */
/* Before v10 use this linkmode */
@@ -2397,15 +2411,11 @@ struct rtw89_btc_wl_info {
u8 ch_map[12];
u8 ch_map_le[5];
- 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 is_5g_hi_ch[RTW89_MAC_NUM];
+ bool go_client_exist[RTW89_MAC_NUM];
+ bool noa_exist[RTW89_MAC_NUM];
bool pta_reg_mac_chg;
- bool bg_mode;
- bool bg_mode_hb1;
+ bool bg_mode[RTW89_MAC_NUM];
bool he_mode;
bool scbd_chg[BTC_ALL_BT];
bool fw_ver_mismatch;
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 4ac2fd117b95..886d7c32d2ef 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -6456,7 +6456,7 @@ int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev, u8 type)
const struct rtw89_btc_ver *ver = btc->ver;
struct rtw89_btc_wl_rlink *rl;
struct sk_buff *skb;
- u32 rmap = r->role_map;
+ u32 rmap = r->role_map[RTW89_MAC_0];
u8 offset = 0;
u32 len;
u8 *cmd;
@@ -6477,7 +6477,7 @@ int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev, u8 type)
RTW89_SET_FWCMD_CXHDR_LEN(cmd, len - H2C_LEN_CXDRVHDR);
RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, r->connect_cnt);
- RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode);
+ RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode[RTW89_MAC_0]);
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)));
@@ -6538,7 +6538,7 @@ int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type)
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 rmap = r->role_map[RTW89_MAC_0];
u8 *cmd, offset;
u32 len;
int ret;
@@ -6558,7 +6558,7 @@ int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type)
RTW89_SET_FWCMD_CXHDR_LEN(cmd, len - H2C_LEN_CXDRVHDR);
RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, r->connect_cnt);
- RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode);
+ RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode[RTW89_MAC_0]);
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)));
@@ -6643,8 +6643,8 @@ int rtw89_fw_h2c_cxdrv_role_v101(struct rtw89_dev *rtwdev, u8 type)
h2c->hdr.len = len - H2C_LEN_CXDRVHDR;
h2c->connect_cnt = role_info->connect_cnt;
- h2c->link_mode = role_info->link_mode;
- h2c->role_map = cpu_to_le16(role_info->role_map);
+ h2c->link_mode = role_info->link_mode[RTW89_MAC_0];
+ h2c->role_map = cpu_to_le16(role_info->role_map[RTW89_MAC_0]);
for (i = 0; i < RTW89_PORT_NUM; i++) {
active = &role_info->rlink[i][0];
@@ -6712,7 +6712,7 @@ int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type)
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 rmap = r->role_map[RTW89_MAC_0];
u32 len;
u8 *cmd, offset;
int ret;
@@ -6732,7 +6732,7 @@ int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type)
RTW89_SET_FWCMD_CXHDR_LEN(cmd, len - H2C_LEN_CXDRVHDR);
RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, r->connect_cnt);
- RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode);
+ RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode[RTW89_MAC_0]);
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)));
@@ -6813,7 +6813,7 @@ int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type)
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 = r->link_mode[RTW89_MAC_0];
h2c->r.link_mode_chg = r->link_mode_chg;
h2c->r.p2p_2g = r->p2p_2g;
@@ -6834,7 +6834,7 @@ int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type)
h2c->r.active_role[i].client_cnt = rl->client_cnt;
}
- rm = cpu_to_le32(r->role_map);
+ rm = cpu_to_le32(r->role_map[RTW89_MAC_0]);
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);
@@ -6883,7 +6883,7 @@ int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type)
h2c->hdr.ver = 8;
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 = r->link_mode[RTW89_MAC_0];
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;
@@ -6911,7 +6911,7 @@ int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type)
}
}
- rm = cpu_to_le32(r->role_map);
+ rm = cpu_to_le32(r->role_map[RTW89_MAC_0]);
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);
@@ -6978,18 +6978,95 @@ int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type)
}
}
- 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.link_mode = r->link_mode[RTW89_MAC_0];
+ h2c->r.link_mode_hb1 = r->link_mode[RTW89_MAC_1];
+ h2c->r.p2p_exist = r->p2p_exist[RTW89_MAC_0];
+ h2c->r.p2p_exist_hb1 = r->p2p_exist[RTW89_MAC_1];
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);
+ rm = cpu_to_le32(r->role_map[RTW89_MAC_0]);
memcpy(&h2c->r.role_map, &rm, sizeof(h2c->r.role_map));
- rm = cpu_to_le32(r->role_map_hb1);
+ rm = cpu_to_le32(r->role_map[RTW89_MAC_1]);
+ 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,
+ 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_v11(struct rtw89_dev *rtwdev, u8 type)
+{
+ struct rtw89_btc_wl_role_info *r = &rtwdev->btc.cx.wl.role_info;
+ struct rtw89_h2c_cxrole_v11 *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_v11 *)skb->data;
+
+ h2c->hdr.type = type;
+ h2c->hdr.ver = 11;
+ h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7;
+
+ 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[RTW89_MAC_0];
+ h2c->r.link_mode_hb1 = r->link_mode[RTW89_MAC_1];
+ h2c->r.p2p_exist = r->p2p_exist[RTW89_MAC_0];
+ h2c->r.p2p_exist_hb1 = r->p2p_exist[RTW89_MAC_1];
+
+ 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[RTW89_MAC_0]);
+ memcpy(&h2c->r.role_map, &rm, sizeof(h2c->r.role_map));
+ rm = cpu_to_le32(r->role_map[RTW89_MAC_1]);
memcpy(&h2c->r.role_map_hb1, &rm, sizeof(h2c->r.role_map_hb1));
h2c->r.mrole_type = cpu_to_le32(r->mrole_type);
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index 9b616ec9323b..b0f1d51d67d9 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -2507,6 +2507,11 @@ struct rtw89_h2c_cxrole_v10 {
struct rtw89_btc_wl_role_info_v10 r;
} __packed;
+struct rtw89_h2c_cxrole_v11 {
+ struct rtw89_h2c_cxhdr_v7 hdr;
+ struct rtw89_btc_wl_role_info_v11 r;
+} __packed;
+
struct rtw89_h2c_cxmlo_v2 {
struct rtw89_h2c_cxhdr_v7 hdr;
struct rtw89_btc_wl_mlo_info_v2 mlo;
@@ -5545,6 +5550,7 @@ 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_role_v11(struct rtw89_dev *rtwdev, u8 type);
int rtw89_fw_h2c_cxdrv_init_v11(struct rtw89_dev *rtwdev, u8 type);
int rtw89_fw_h2c_cxdrv_mlo_v2(struct rtw89_dev *rtwdev, u8 type);
int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type);
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 03/15] wifi: rtw89: coex: show per-HW-band MLO state in WL status dump
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 01/15] wifi: rtw89: coex: fix RF band check in _set_rf_trx_para() and its propagation Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 02/15] wifi: rtw89: coex: track per-HW-band WL role fields with arrays Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 04/15] wifi: rtw89: coex: add outsrc-set-info v7 for BT SPDT state Ping-Ke Shih
` (11 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
The WL status debug dump only printed role, channel and link-mode
state for HW-band 0, even on chips where MLO support means both
bands carry an independent connection. Add an [MLO] line that
prints the operating mode, channel type, RF band and P2P-exist
state for each HW-band side by side, gated on the chip actually
supporting MLO so single-band chips keep the shorter dump.
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 | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 8412c082ddc8..556c35e84043 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -10292,6 +10292,7 @@ static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
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_mlo_info *wl_minfo = &wl->mlo_info;
char *p = buf, *end = buf + bufsz;
u8 mode;
@@ -10319,6 +10320,25 @@ static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
wl->status.map._4way ? "Y" : "N",
wl->status.map.init_ok ? "Y" : "N");
+ if (rtwdev->chip->para_ver & BTC_FEAT_MLO_SUPPORT) {
+ p += scnprintf(p, end - p,
+ " %-15s : type:%d/rf_comb:%d/dbcc_en:%d, ",
+ "[MLO]", wl_minfo->wtype, wl_minfo->rf_combination,
+ wl_rinfo->dbcc_en);
+ p += scnprintf(p, end - p,
+ "HWB0(wmode:%d/ch:%d/rf_band:%d/p2p:%d), ",
+ wl_minfo->wmode[RTW89_MAC_0],
+ wl_minfo->ch_type[RTW89_MAC_0],
+ wl_minfo->hwb_rf_band[RTW89_MAC_0],
+ wl_rinfo->p2p_exist[RTW89_MAC_0]);
+ p += scnprintf(p, end - p,
+ "HWB1(wmode:%d/ch:%d/rf_band:%d/p2p:%d)\n",
+ wl_minfo->wmode[RTW89_MAC_1],
+ wl_minfo->ch_type[RTW89_MAC_1],
+ wl_minfo->hwb_rf_band[RTW89_MAC_1],
+ wl_rinfo->p2p_exist[RTW89_MAC_1]);
+ }
+
p += _show_wl_role_info(rtwdev, p, end - p);
return p - buf;
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 04/15] wifi: rtw89: coex: add outsrc-set-info v7 for BT SPDT state
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (2 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 03/15] wifi: rtw89: coex: show per-HW-band MLO state in WL status dump Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 05/15] wifi: rtw89: coex: compute WL/BT cross-talk interference maps Ping-Ke Shih
` (10 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
The outsrc-set-info H2C used its last reserved byte for nothing. A
newer firmware version reuses that byte to carry whether BT shares
an SPDT switch with WL at the BB level, which the firmware needs to
pick the right RF constraint. Add a v7 variant of the struct with
the top bit of that byte named is_spdt_bb and the rest kept
reserved, a matching H2C sender, and dispatch fcxosi == 7 to it.
Also widen the gnt_set_be branch in the GNT H2C sender to cover
fcxosi == 7, since it shares the same GNT layout as v6, and add the
paired W2B/B2W scoreboard bits that let WL request and BT acknowledge
the LNA-backoff table for this fcxosi version.
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 | 6 +-
drivers/net/wireless/realtek/rtw89/core.h | 28 ++++++++-
drivers/net/wireless/realtek/rtw89/fw.c | 76 ++++++++++++++++++++++-
drivers/net/wireless/realtek/rtw89/fw.h | 6 ++
4 files changed, 113 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 556c35e84043..fbd1a6b33cb6 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -526,6 +526,7 @@ enum btc_b2w_scoreboard {
BTC_BSCB_BT_CONNECT_56G = BIT(17),
BTC_BSCB_BT_LNAB0_56G = BIT(18),
BTC_BSCB_BT_LNAB1_56G = BIT(19),
+ BTC_BSCB_BT_BACKOFF_LNA = BIT(21),
BTC_BSCB_HID_ACT = BIT(23),
BTC_BSCB_BT_15DOT4 = BIT(24),
BTC_BSCB_BT_PROTECT = BIT(27),
@@ -829,6 +830,7 @@ enum btc_w2b_scoreboard {
BTC_WSCB_CTCODE = BIT(15),
BTC_WSCB_RXGAIN_56G = BIT(16),
BTC_WSCB_BT_HILNA_56G = BIT(17),
+ BTC_WSCB_BT_BACKOFF_LNA = BIT(18),
BTC_WSCB_ALL = GENMASK(23, 0),
};
@@ -3871,6 +3873,8 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index)
rtw89_fw_h2c_cxdrv_osi_info(rtwdev, index);
else if (ver->fcxosi == 6)
rtw89_fw_h2c_cxdrv_osi_info_v6(rtwdev, index);
+ else if (ver->fcxosi == 7)
+ rtw89_fw_h2c_cxdrv_osi_info_v7(rtwdev, index);
else
return;
break;
@@ -4018,7 +4022,7 @@ static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map,
memcpy(o->wlact_set, dm->wlact_set, sizeof(o->wlact_set));
- if (btc->ver->fcxosi == 6) {
+ if (btc->ver->fcxosi == 6 || btc->ver->fcxosi == 7) {
memcpy(o->gnt_set_be, dm->gnt_set, sizeof(o->gnt_set_be));
return;
} else if (btc->ver->fcxosi == 1) {
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index d66a1febb85c..30c5822d63b8 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -3925,6 +3925,32 @@ struct rtw89_btc_fbtc_outsrc_set_info_v6 {
__le32 wl_tx_limit_time;
} __packed;
+struct rtw89_btc_fbtc_outsrc_set_info_v7 {
+ u8 rf_band[BTC_RF_NUM];
+ u8 btg_rx[BTC_RF_NUM];
+ u8 nbtg_tx[BTC_RF_NUM];
+
+ struct rtw89_btc_gnt_ctrl gnt_set[RTW89_MAC_AX_COEX_GNT_NR];
+ struct rtw89_mac_ax_wl_act wlact_set[BTC_ALL_BT_EZL];
+
+ u8 pta_req_hw_band;
+ u8 rf_gbt_source;
+ u8 bt_enable_state;
+ u8 bt_plut_type;
+ u8 wl_tx_limit_en;
+ u8 fc_exec;
+ u8 wl_btg_standby_chg;
+ u8 option;
+#define OPTION_SPDT_BB BIT(0)
+ u8 bb_path_sel_bt[BTC_RF_NUM];
+ u8 bb_phy_sel_bt[RTW89_PHY_NUM];
+ u8 fbd_group_en[RTW89_MAC_NUM][2];
+ __le16 rf_center_freq[RTW89_MAC_NUM];
+ __le16 freq_diff_thres[RTW89_MAC_NUM][BTC_ALL_BT_EZL];
+ __le16 fbd_group_bound[RTW89_MAC_NUM][2];
+ __le32 wl_tx_limit_time;
+} __packed;
+
struct rtw89_btc_fbtc_outsrc_set_info {
u8 rf_band[BTC_RF_NUM]; /* 0:2GHz/1:5GHz for MPI_bb_hwsi_ignore_gnt_wl() */
u8 btg_rx[BTC_RF_NUM]; /* for MPI_bb_btg_bt_rx() */
@@ -3945,7 +3971,7 @@ struct rtw89_btc_fbtc_outsrc_set_info {
u8 wl_tx_limit_en;
u8 fc_exec;
u8 wl_btg_standby_chg; /* keep RX-IQGen on in standby mode */
- u8 rsvd;
+ u8 is_spdt_bb; /* v7: BT shares an SPDT with WL at BB level */
u8 bb_path_sel_bt[BTC_RF_NUM]; /* bb s0(1) select GNT_BT0 or BT1 */
u8 bb_phy_sel_bt[RTW89_PHY_NUM]; /* bb phy0(1) select GNT_BT0 or BT1 */
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 886d7c32d2ef..e9f6dc25b94c 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -7248,7 +7248,81 @@ int rtw89_fw_h2c_cxdrv_osi_info_v6(struct rtw89_dev *rtwdev, u8 type)
h2c->osi.wl_tx_limit_en = osi->wl_tx_limit_en;
h2c->osi.fc_exec = osi->fc_exec;
h2c->osi.wl_btg_standby_chg = osi->wl_btg_standby_chg;
- h2c->osi.rsvd = osi->rsvd;
+ h2c->osi.rsvd = 0;
+
+ memcpy(h2c->osi.bb_path_sel_bt, osi->bb_path_sel_bt, sizeof(osi->bb_path_sel_bt));
+ memcpy(h2c->osi.bb_phy_sel_bt, osi->bb_phy_sel_bt, sizeof(osi->bb_phy_sel_bt));
+ memcpy(h2c->osi.fbd_group_en, osi->fbd_group_en, sizeof(osi->fbd_group_en));
+
+ for (i = 0; i < RTW89_MAC_NUM; i++) {
+ h2c->osi.rf_center_freq[i] = cpu_to_le16(osi->rf_center_freq[i]);
+ for (j = 0; j < BTC_RF_NUM; j++)
+ h2c->osi.fbd_group_bound[i][j] =
+ cpu_to_le16(osi->fbd_group_bound[i][j]);
+ }
+
+ for (i = 0; i < RTW89_MAC_NUM; i++) {
+ for (j = 0; j < BTC_ALL_BT_EZL; j++)
+ h2c->osi.freq_diff_thres[i][j] =
+ cpu_to_le16(osi->freq_diff_thres[i][j]);
+ }
+
+ h2c->osi.wl_tx_limit_time = cpu_to_le32(osi->wl_tx_limit_time);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_OUTSRC, BTFC_SET,
+ SET_DRV_INFO, 0, 0,
+ len);
+
+ ret = rtw89_h2c_tx(rtwdev, skb, false);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+
+int rtw89_fw_h2c_cxdrv_osi_info_v7(struct rtw89_dev *rtwdev, u8 type)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_fbtc_outsrc_set_info *osi = &btc->dm.ost_info;
+ struct rtw89_h2c_cxosi_v7 *h2c;
+ u32 len = sizeof(*h2c);
+ struct sk_buff *skb;
+ int ret;
+ u8 i, j;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_osi_v7\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, len);
+ h2c = (struct rtw89_h2c_cxosi_v7 *)skb->data;
+
+ h2c->hdr.type = type;
+ h2c->hdr.ver = btc->ver->fcxosi;
+ h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7;
+
+ memcpy(h2c->osi.rf_band, osi->rf_band, sizeof(osi->rf_band));
+ memcpy(h2c->osi.btg_rx, osi->btg_rx, sizeof(osi->btg_rx));
+ memcpy(h2c->osi.nbtg_tx, osi->nbtg_tx, sizeof(osi->nbtg_tx));
+ memcpy(h2c->osi.gnt_set, osi->gnt_set, sizeof(osi->gnt_set));
+ memcpy(h2c->osi.wlact_set, osi->wlact_set, sizeof(osi->wlact_set));
+
+ h2c->osi.pta_req_hw_band = osi->pta_req_hw_band;
+ h2c->osi.rf_gbt_source = osi->rf_gbt_source;
+ h2c->osi.bt_enable_state = osi->bt_enable_state;
+ h2c->osi.bt_plut_type = osi->bt_plut_type;
+ h2c->osi.wl_tx_limit_en = osi->wl_tx_limit_en;
+ h2c->osi.fc_exec = osi->fc_exec;
+ h2c->osi.wl_btg_standby_chg = osi->wl_btg_standby_chg;
+ h2c->osi.option = u8_encode_bits(!!osi->is_spdt_bb, OPTION_SPDT_BB);
memcpy(h2c->osi.bb_path_sel_bt, osi->bb_path_sel_bt, sizeof(osi->bb_path_sel_bt));
memcpy(h2c->osi.bb_phy_sel_bt, osi->bb_phy_sel_bt, sizeof(osi->bb_phy_sel_bt));
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index b0f1d51d67d9..11b6ade24940 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -2527,6 +2527,11 @@ struct rtw89_h2c_cxosi_v6 {
struct rtw89_btc_fbtc_outsrc_set_info_v6 osi;
} __packed;
+struct rtw89_h2c_cxosi_v7 {
+ struct rtw89_h2c_cxhdr_v7 hdr;
+ struct rtw89_btc_fbtc_outsrc_set_info_v7 osi;
+} __packed;
+
struct rtw89_h2c_cxinit {
struct rtw89_h2c_cxhdr hdr;
u8 ant_type;
@@ -5555,6 +5560,7 @@ int rtw89_fw_h2c_cxdrv_init_v11(struct rtw89_dev *rtwdev, u8 type);
int rtw89_fw_h2c_cxdrv_mlo_v2(struct rtw89_dev *rtwdev, u8 type);
int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type);
int rtw89_fw_h2c_cxdrv_osi_info_v6(struct rtw89_dev *rtwdev, u8 type);
+int rtw89_fw_h2c_cxdrv_osi_info_v7(struct rtw89_dev *rtwdev, u8 type);
int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type);
int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type);
int rtw89_fw_h2c_cxdrv_ctrl_v9(struct rtw89_dev *rtwdev, u8 type);
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 05/15] wifi: rtw89: coex: compute WL/BT cross-talk interference maps
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (3 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 04/15] wifi: rtw89: coex: add outsrc-set-info v7 for BT SPDT state Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 06/15] wifi: rtw89: coex: add 1SS MIMO-PS decision for dual-BT CIS Ping-Ke Shih
` (9 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
The TDD/FDD binding logic consumed dm->sit_xmap and dm->xtk_xmap to
decide whether a WL RF-path and a BT device need TDD or FDD
coexistence, but nothing in the driver ever wrote those two maps, so
they stayed all-zero and the binding logic silently fell back to
whatever that implied. Add _update_wl_rssi_status() to track the
minimum WL RSSI across all connected roles and HW-bands and turn it
into a per-BT interference state by comparing it against each BT's
Tx power minus antenna isolation, and _update_sdm_map() to turn that
state plus the existing antenna-sharing map into the cross-talk and
space-interaction maps the binding logic expects.
Call _update_wl_rssi_status() from the per-station notify path where
RSSI is refreshed, and recompute the maps whenever it reports a
change or whenever the BT scoreboard flips the dedicated TRX-active
bit that forces TDD regardless of RSSI. _update_sdm_map() also runs
once during coex init so the maps are populated before the first
coex cycle, and _set_coex_binding() now derives dm->fit_used_path and
a per-path dm->fit_map alongside the existing fit_xmap use, which a
later change will need once it introduces 1SS Rx-path limiting.
Whether the WL path under a 1SS RX limit still co-receives with a BT
device also depends on traffic direction: the limit only confines
RX, so the cross-talk map that feeds the TDD/FDD binding is derived
differently for downlink than for uplink. Also recompute the maps
when the per-station notify path latches a traffic direction flip,
so a downlink-to-uplink switch with a steady RSSI does not leave the
binding running on the previous direction's map until some later
event happens to refresh it.
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 | 289 +++++++++++++++++++++-
drivers/net/wireless/realtek/rtw89/core.h | 4 +
2 files changed, 283 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index fbd1a6b33cb6..1d3ffde71c07 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -527,6 +527,7 @@ enum btc_b2w_scoreboard {
BTC_BSCB_BT_LNAB0_56G = BIT(18),
BTC_BSCB_BT_LNAB1_56G = BIT(19),
BTC_BSCB_BT_BACKOFF_LNA = BIT(21),
+ BTC_BSCB_BT_15DOT4_TRX = BIT(22),
BTC_BSCB_HID_ACT = BIT(23),
BTC_BSCB_BT_15DOT4 = BIT(24),
BTC_BSCB_BT_PROTECT = BIT(27),
@@ -1013,6 +1014,7 @@ static void _run_coex(struct rtw89_dev *rtwdev,
static void _write_scbd(struct rtw89_dev *rtwdev, u8 bid, u32 val, bool state);
static u8 _sned_h2c_w2bscbd(struct rtw89_dev *rtwdev, bool force_exec, u8 bid);
static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid, bool is_c2h);
+static void _update_sdm_map(struct rtw89_dev *rtwdev);
static const char *id_to_h2c(u32 id);
static void _reset_h2c_macro(struct rtw89_btc *btc)
@@ -8202,6 +8204,10 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid, bool is_c2h)
!!(val & BTC_BSCB_BT_LNAB1) * 2;
if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) {
+ if ((val & BTC_BSCB_BT_15DOT4_TRX) !=
+ (bt->scbd & BTC_BSCB_BT_15DOT4_TRX))
+ _update_sdm_map(rtwdev);
+
if (val & BTC_BSCB_BT_15DOT4)
bt->func_type |= (BTC_BTF_THREAD | BTC_BTF_ZB);
else
@@ -8597,9 +8603,142 @@ static void _set_bind_info(struct rtw89_btc *btc, u8 type)
bd->bt_smap.ull_exist = 1;
}
+/*
+ * Get the only WL RF-path that still receives under 1SS MIMO-PS.
+ *
+ * Single source of truth for "is WL Rx confined to one RF-path, and which
+ * one". Both _update_sdm_map() (xtk_xmap) and _set_coex_binding() (fit_map)
+ * must agree, otherwise the two maps describe different WL Rx topologies.
+ *
+ * Conditions:
+ * - rx_limit: the BB/RF was actually programmed to 1SS by _set_trx_nss()
+ * - DL only: rx_limit shrinks the Rx stream, WL Tx still runs 2SS/2TX on both
+ * paths for UL, so every path stays BT-interactive there
+ * - 2+0/0+2 only: both RF-path serve the same link on one HW-band, so there
+ * is an unused path. In 1+1/2+2 each RF-path serves its own link on its own
+ * HW-band and none of them can be dropped
+ *
+ * Return dm->wl_trx_nss.rx_path (RF_PATH_A/B, == BTC_RF_S0/S1) if 1SS
+ * MIMO-PS is active, RF_PATH_AB if WL keeps both Rx paths.
+ */
+static u8 _get_sdm_1ss_path(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc_wl_trx_nss_para *trx_nss = &rtwdev->btc.dm.wl_trx_nss;
+ struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl;
+
+ if (!trx_nss->rx_limit ||
+ !(wl->status.map.traffic_dir & BIT(RTW89_TFC_DL)))
+ return RF_PATH_AB;
+
+ if (wl->mlo_info.rf_combination != BTC_MLO_RF_2_PLUS_0 &&
+ wl->mlo_info.rf_combination != BTC_MLO_RF_0_PLUS_2)
+ return RF_PATH_AB;
+
+ /*
+ * rx_path is what _set_trx_nss() passed to cfg_trx_path(), it must be
+ * a single path to be used as an RF-path index below
+ */
+ if (trx_nss->rx_path != RF_PATH_A && trx_nss->rx_path != RF_PATH_B)
+ return RF_PATH_AB;
+
+ return trx_nss->rx_path;
+}
+
+static void _update_sdm_map(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_module *module = &btc->mdinfo;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_bt_info *bt0 = &cx->bt0;
+ struct rtw89_btc_bt_info *bt1 = &cx->bt1;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ u8 map[BTC_RF_NUM][BTC_ALL_BT_EZL];
+ bool mlo_1_plus_1 = false;
+ u8 used_path;
+ u8 i, j;
+
+ /*
+ * xtk_xmap(Cross-talk map)-> 1: interaction, 0: no-interaction
+ * calculate WL/BT interference by SIR
+ * ==>1:If WL_RSSI < (BT-Pin -SIR), it means BT-Tx interfer with WL-Rx
+ * ==>BT-pin = BT-Tx-Power - Ant-isolation
+ */
+ if (wl->mlo_info.wtype != RTW89_MR_WTYPE_MLD2L1R_NONMLD &&
+ (wl->mlo_info.rf_combination == BTC_MLO_RF_1_PLUS_1 ||
+ wl->mlo_info.rf_combination == BTC_MLO_RF_2_PLUS_2))
+ mlo_1_plus_1 = true;
+
+ /*
+ * WL running 1SS MIMO-PS (CIS LE-audio): WL Rx is confined to a single
+ * path. The used path co-Rx with its co-ant BT (=1) or follows RSSI vs.
+ * the other BT; the unused path no longer co-Rx with any BT (=0).
+ * RF_PATH_AB -> not 1SS, keep the original per-path logic.
+ *
+ * Record it: this function also runs at the _set_trx_nss() exit, after
+ * rx_limit/rx_path changed, so dm->fit_used_path stays the up-to-date
+ * 1SS state for the _action_common() consumers that run later in the
+ * same coex cycle (_set_wl_preagc_ctrl, _set_bt_corx_table).
+ */
+ used_path = _get_sdm_1ss_path(rtwdev);
+ dm->fit_used_path = used_path;
+
+ memset(map, 0, sizeof(map));
+ for (i = 0; i < BTC_RF_NUM; i++) {
+ if (module->ant.num == 1 && i == BTC_RF_S1)
+ break;
+
+ for (j = 0; j < BTC_ALL_BT_EZL; j++) {
+ if ((bt0->scbd_c2h & BTC_BSCB_BT_15DOT4_TRX) ||
+ (bt1->scbd_c2h & BTC_BSCB_BT_15DOT4_TRX)) {
+ /* 15.4 TRX active: force BT0/BT1 to TDD */
+ if (j < BTC_ALL_BT)
+ dm->xtk_xmap[i][j] = 1;
+ else
+ continue; /* j == EXT: not force TDD */
+ } else if (used_path != RF_PATH_AB && j < BTC_ALL_BT) {
+ /* 1SS MIMO-PS: only the used WL path co-Rx */
+ if (i != used_path)
+ dm->xtk_xmap[i][j] = 0;
+ else if (dm->ant_xmap[i][j] == 1)
+ dm->xtk_xmap[i][j] = 1;
+ else
+ dm->xtk_xmap[i][j] =
+ BTC_RSSI_LOW(dm->sir_state[j]) ? 1 : 0;
+ } else if (dm->ant_xmap[i][j] == 1) {
+ /* if WL RF-pathx co-ant with BTx */
+ dm->xtk_xmap[i][j] = 1;
+ } else if (((j == BTC_BT_1ST &&
+ module->bt0_sw_type != BTC_SWITCH_V1_NONE) ||
+ (j == BTC_BT_2ND &&
+ module->bt1_sw_type != BTC_SWITCH_V1_NONE)) &&
+ !mlo_1_plus_1) {
+ /*
+ * if WL RF-pathx not co-ant/path with BTx
+ * but BTx co-ant/path with the other WL-path
+ * and WL is 2+0/0+2
+ * ex: WL-S0 + (WL-S1 & BT0-S1) ->
+ * BT0-S1 vs. WL-S0 = 1
+ */
+ dm->xtk_xmap[i][j] = 1;
+ } else if (BTC_RSSI_LOW(dm->sir_state[j])) {
+ /* if WL_RSSI below BT-Tx interference thres */
+ dm->xtk_xmap[i][j] = 1;
+ } else {
+ dm->xtk_xmap[i][j] = 0;
+ }
+
+ map[i][j] = dm->ant_xmap[i][j] | dm->xtk_xmap[i][j];
+ }
+ }
+
+ memcpy(dm->sit_xmap, map, sizeof(map));
+}
+
#define _bind_is_btonly 0x7
-static void _set_coex_binding(struct rtw89_btc *btc)
+static void _set_coex_binding(struct rtw89_dev *rtwdev)
{
+ struct rtw89_btc *btc = &rtwdev->btc;
struct rtw89_btc_cx *cx = &btc->cx;
struct rtw89_btc_extsoc_info *bt2 = &cx->bt_ext;
struct rtw89_btc_bt_info *bt0 = &cx->bt0;
@@ -8608,7 +8747,7 @@ static void _set_coex_binding(struct rtw89_btc *btc)
struct rtw89_btc_dm *dm = &btc->dm;
u8 path_hwb[BTC_RF_NUM] = {RTW89_PHY_0, RTW89_PHY_1};
u8 wl_rf_band[RTW89_BAND_NUM] = {};
- u8 i, j, val = 0;
+ u8 i, j, val = 0, fit, used_path;
/*
* sit_xmap(Space-Interaction) = ant_xmap | xtk_xmap
@@ -8657,21 +8796,49 @@ static void _set_coex_binding(struct rtw89_btc *btc)
path_hwb[BTC_RF_S0] = RTW89_PHY_1; /* 2+0 RF-S0/1->HWB1 */
}
+ /*
+ * WL 1SS MIMO-PS: WL Rx is confined to a single path, so the unused
+ * path has no WL-Rx frequency interference with any BT.
+ * RF_PATH_AB -> not 1SS, both paths keep their real fit_xmap.
+ * Same helper as _update_sdm_map(), so xtk_xmap and fit_map always
+ * agree on which RF-path is the unused one.
+ * e.g. CIS@BT_2ND -> rx_path=RF_PATH_B, used_path=S1:
+ * RF-S1 keeps its real fit_xmap, RF-S0 is cleared.
+ */
+ used_path = _get_sdm_1ss_path(rtwdev);
+ dm->fit_used_path = used_path; /* record for dbg cmd "xmap" */
+
/*
* tdd_map = sit_xmap * fdm_map, 1: WL-RF-Sx vs. BTx take TDD-Action
* fdd_map =(!sit_xmap) * fdm_map, 1: WL-RF-Sx vs.BTx take FDD-Action
* co-rx map = ant_xmap * fdm_map 1:WL/BT co-rx (for halbb-btg-ctrl)
* sit_xmap,ant_xmap = 0 or 1, so tdd/fdd use multiplication (*)
+ *
+ * fit: WL-BT freq-interact(2bit band-map) of RF-Sx vs. BTx,
+ * = fit_xmap[HWB][BTx], the used_path keeps this real band-map,
+ * the unused path is cleared to 0 if 1SS MIMO-PS.
*/
for (i = 0; i < BTC_RF_NUM; i++)
for (j = 0; j < BTC_ALL_BT_EZL; j++) {
- dm->tdd_map[i][j] = dm->sit_xmap[i][j] *
- dm->fit_xmap[path_hwb[i]][j];
- dm->fdd_map[i][j] = !dm->sit_xmap[i][j] *
- dm->fit_xmap[path_hwb[i]][j];
- dm->corx_map[i][j] = dm->ant_xmap[i][j] *
- dm->fit_xmap[path_hwb[i]][j];
+ fit = dm->fit_xmap[path_hwb[i]][j];
+
+ /*
+ * 1SS MIMO-PS: no WL-Rx on the unused path.
+ * Only the local fit is dropped, dm->fit_xmap keeps
+ * the real per-HW-band map: it is indexed by HW-band,
+ * and both RF-path may map to the same HW-band, so
+ * clearing it there would also drop used_path's entry
+ * (and the other MLO link's entry in DB_MCC).
+ */
+ if (used_path != RF_PATH_AB && i != used_path)
+ fit = 0;
+
+ dm->fit_map[i][j] = fit; /* for dbg cmd "xmap" */
+
+ dm->tdd_map[i][j] = dm->sit_xmap[i][j] * fit;
+ dm->fdd_map[i][j] = !dm->sit_xmap[i][j] * fit;
+ dm->corx_map[i][j] = dm->ant_xmap[i][j] * fit;
}
/* TDD-Binding */
@@ -8794,7 +8961,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
dm->fddt_train = BTC_FDDT_DISABLE;
bt->scan_rx_low_pri = false;
- _set_coex_binding(btc);
+ _set_coex_binding(rtwdev);
_update_btc_state_map(rtwdev);
dm->freerun_chk = _check_freerun(rtwdev); /* check if meet freerun */
@@ -9031,6 +9198,8 @@ static void _set_init_info(struct rtw89_dev *rtwdev)
btc_fw_set_monreg(rtwdev);
_set_ext_interface(rtwdev);
_set_wl_tx_power(rtwdev, RTW89_BTC_WL_DEF_TX_PWR, RTW89_PHY_0);
+
+ _update_sdm_map(rtwdev);
}
void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode)
@@ -9970,6 +10139,96 @@ static void rtw89_btc_ntfy_wl_sta_iter(void *data, struct ieee80211_sta *sta)
#define BTC_NHM_CHK_INTVL 20
+static bool _update_wl_rssi_status(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_bt_link_info *b;
+ u8 i, j, ant_iso, th, rssi, nxt, bt_enable;
+ u8 wl_rf_band = RTW89_BAND_2G, cnt = 0;
+ bool status_change = false;
+ s8 bt_pin;
+ s32 val;
+
+ /* Update WL RSSI (search the min value between all role/HW-band) */
+ rssi = 110; /* unit: % */
+ for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) {
+ for (j = 0; j < RTW89_MAC_NUM; j++) {
+ if (!wl->rlink_info[i][j].active ||
+ wl->rlink_info[i][j].connected == MLME_NO_LINK)
+ continue;
+
+ if (wl->rlink_info[i][j].stat.rssi < rssi) { /* find min */
+ rssi = wl->rlink_info[i][j].stat.rssi;
+ wl_rf_band = wl->rlink_info[i][j].band;
+ cnt++;
+ }
+ }
+ }
+
+ if (cnt == 0) /* no wifi link exist */
+ wl->rssi = 0;
+ else
+ wl->rssi = rssi;
+
+ /* SIR state: compare WL_RSSI with each BT-Tx interference */
+ for (i = BTC_BT_1ST; i <= BTC_BT_EXT; i++) {
+ switch (i) {
+ case BTC_BT_1ST:
+ default:
+ bt_enable = cx->bt0.enable.now;
+ ant_iso = cx->bt0.ant_iso_to_wl;
+ if (wl_rf_band != RTW89_BAND_2G) /* find same rf-band */
+ b = &cx->bt0.link_info_56g;
+ else
+ b = &cx->bt0.link_info;
+ if (b->bt_txpwr_desc.le_dbm > b->bt_txpwr_desc.br_dbm)
+ bt_pin = b->bt_txpwr_desc.le_dbm;
+ else
+ bt_pin = b->bt_txpwr_desc.br_dbm;
+ break;
+ case BTC_BT_2ND:
+ bt_enable = cx->bt1.enable.now;
+ ant_iso = cx->bt1.ant_iso_to_wl;
+ if (wl_rf_band != RTW89_BAND_2G) /* find same rf-band */
+ b = &cx->bt0.link_info_56g;
+ else
+ b = &cx->bt0.link_info;
+ if (b->bt_txpwr_desc.le_dbm > b->bt_txpwr_desc.br_dbm)
+ bt_pin = b->bt_txpwr_desc.le_dbm;
+ else
+ bt_pin = b->bt_txpwr_desc.br_dbm;
+ break;
+ case BTC_BT_EXT:
+ bt_enable = !!cx->bt_ext.func_type;
+ ant_iso = cx->bt_ext.ant_iso_to_wl;
+ bt_pin = cx->bt_ext.max_tx_pwr;
+ break;
+ }
+
+ val = 110 + bt_pin - ant_iso - dm->sir_thres;
+ if (!bt_enable || val <= 0)
+ th = 0;
+ else if (val >= 110)
+ th = 110;
+ else
+ th = (u8)val;
+
+ dm->tdd_rssi_thres = th;
+
+ nxt = _update_rssi_state(rtwdev, dm->sir_state[i], wl->rssi, th);
+
+ if (nxt != dm->sir_state[i]) {
+ dm->sir_state[i] = nxt;
+ status_change = true;
+ }
+ }
+
+ return status_change;
+}
+
void rtw89_btc_ntfy_wl_sta(struct rtw89_dev *rtwdev)
{
struct rtw89_btc *btc = &rtwdev->btc;
@@ -9995,6 +10254,9 @@ void rtw89_btc_ntfy_wl_sta(struct rtw89_dev *rtwdev)
if (dm->trx_info.wl_rssi != wl->rssi_level)
dm->trx_info.wl_rssi = wl->rssi_level;
+ if (_update_wl_rssi_status(rtwdev))
+ _update_sdm_map(rtwdev);
+
rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): busy=%d\n",
__func__, !!wl->status.map.busy);
@@ -10004,7 +10266,14 @@ void rtw89_btc_ntfy_wl_sta(struct rtw89_dev *rtwdev)
_fw_set_drv_info(rtwdev, CXDRVINFO_ROLE);
if (data.is_sta_change) {
wl->status.map.busy = data.busy_all;
- wl->status.map.traffic_dir = data.dir_all;
+ if (wl->status.map.traffic_dir != data.dir_all) {
+ wl->status.map.traffic_dir = data.dir_all;
+ /*
+ * traffic_dir (UL/DL) switch changes the 1SS MIMO-PS
+ * xtk_xmap, recompute sit_xmap in the same coex cycle
+ */
+ _update_sdm_map(rtwdev);
+ }
_run_coex(rtwdev, BTC_RSN_NTFY_WL_STA);
} else if (btc->dm.cnt_notify[BTC_NCNT_WL_STA] >=
btc->dm.cnt_dm[BTC_DCNT_WL_STA_LAST] + BTC_NHM_CHK_INTVL) {
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 30c5822d63b8..340633c42c9f 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -2403,6 +2403,7 @@ struct rtw89_btc_wl_info {
u8 port_id[RTW89_WIFI_ROLE_MLME_MAX];
u8 rssi_level;
+ u8 rssi; /* min WL RSSI among all active role/HW-band, 0~110% */
u8 cn_report;
u8 coex_mode;
u8 pta_req_mac;
@@ -4144,6 +4145,9 @@ struct rtw89_btc_dm {
u8 tdd_map[BTC_RF_NUM][BTC_ALL_BT_EZL]; /* WL-BT tdd-map */
u8 fdd_map[BTC_RF_NUM][BTC_ALL_BT_EZL]; /* WL-BT fdd-map */
u8 corx_map[BTC_RF_NUM][BTC_ALL_BT_EZL]; /* WL-BT Co-Rx */
+ /* RF-Sx view of fit_xmap, 1SS MIMO-PS applied */
+ u8 fit_map[BTC_RF_NUM][BTC_ALL_BT_EZL];
+ u8 fit_used_path; /* 1SS MIMO-PS Rx path, RF_PATH_AB if no limit */
u8 sit_xmap_last[BTC_RF_NUM][BTC_ALL_BT_EZL];
u8 fit_xmap_last[RTW89_PHY_NUM][BTC_ALL_BT_EZL];
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 06/15] wifi: rtw89: coex: add 1SS MIMO-PS decision for dual-BT CIS
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (4 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 05/15] wifi: rtw89: coex: compute WL/BT cross-talk interference maps Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 07/15] wifi: rtw89: coex: add firmware 0.35.119.3 support for RTL8922A/D Ping-Ke Shih
` (8 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
When a BT CIS (LE-audio) link exists on a dual-BT, shared-antenna
chip, limiting WL to 1SS on the RF-path that is isolated from that
link protects WL Rx from the CIS traffic without a full freerun
fallback. Add _set_trx_nss() to compute whether that 1SS limit
should apply, which path it should apply to, and what stream count
and path the WL TRX should fall back to when it does not, gated on
a chip-reported MIMO-PS capability flag and limited to Lenovo
designs or dual-BT chips to match the hardware this has been
validated on. The result also re-arms the limit once after a device
exits low-power state, since LPS can clear the BB/RF setup the
previous decision put in place.
The RTL8922D 3-antenna shared configuration also claimed that
capability, but it has no WL path shared with BT to power down, so
the limit there would cost a stream without buying any isolation.
Measurement on that configuration confirms it, so stop reporting
the capability for it and leave the 2-antenna configuration as the
only one that does.
There is currently no driver API to apply the computed stream/path
decision to the BB and RF, so the function stops right after
printing the decision for debug and before it would otherwise update
dm->wl_trx_nss, recompute the cross-talk maps, or call into set_rfe().
Those steps follow once that HW-apply API exists.
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 | 128 ++++++++++++++++++
drivers/net/wireless/realtek/rtw89/core.h | 1 +
drivers/net/wireless/realtek/rtw89/rtw8922d.c | 1 -
3 files changed, 129 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 1d3ffde71c07..0b9858d1859a 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -6993,6 +6993,133 @@ static void _set_fw_report_map(struct rtw89_dev *rtwdev)
rtw89_btc_fw_en_rpt(rtwdev, false, rpt_map);
}
+static void _set_trx_nss(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_bind_bt_status *btdd = &dm->tdd_bind.bt_smap;
+ struct rtw89_btc_bind_bt_status *bfdd = &dm->fdd_bind.bt_smap;
+ struct rtw89_btc_wl_trx_nss_para *trx_nss = &dm->wl_trx_nss;
+ struct rtw89_btc_module *module = &btc->mdinfo;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+ bool bt0_cis = cx->bt0.link_info.leaudio_desc.cis_exist;
+ bool bt1_cis = cx->bt1.link_info.leaudio_desc.cis_exist;
+ u8 tx_ss, rx_ss, tx_path, rx_path, limit_path;
+ u8 tx_limit = 0, rx_limit = 0;
+
+ /* tx/rx_limit: 1 = 1ss, 0 = HW default */
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): wl_rinfo->link_mode=%d, dm->wl_trx_nss_en=%d\n",
+ __func__, wl->role_info.link_mode[RTW89_MAC_0], dm->wl_trx_nss_en);
+
+ /*
+ * Skip 1SS MIMO-PS if:
+ * - manual mode or DBCC enabled
+ * - !wl_trx_nss_en: no MIMO-PS capability. The RFE configs that share
+ * a WL path with BT are selected in ops->set_rfe() (e.g. 8922D only
+ * sets it for ant.num=2), so this flag is the single gate here, no
+ * extra ant.num/bt1_sw_type check.
+ * - non-LNV vendor and no DUAL_BT. Dual-BT chips run MIMO-PS for every
+ * vendor, single-BT chips only for LNV (LNV: PCIE-12223)
+ */
+ if (btc->manual_ctrl || wl->role_info.dbcc_en || !dm->wl_trx_nss_en ||
+ (dm->vid != RTW89_CUSTID_LENOVO &&
+ !(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT)))
+ return;
+
+ /*
+ * Disable 1SS limit when:
+ * - No WiFi link (WL idle state)
+ * - Out-of-band scenario (no BT coex needed)
+ */
+ if (wl->role_info.link_mode[RTW89_MAC_0] == BTC_WLINK_NOLINK ||
+ wl->role_info.link_mode[RTW89_MAC_1] == BTC_WLINK_NOLINK ||
+ dm->out_of_band) {
+ tx_limit = 0;
+ rx_limit = 0;
+ } else if (bt0_cis || bt1_cis || btdd->cis_exist || bfdd->cis_exist) {
+ /*
+ * Enable 2T1R when BT CIS profile exists (low latency audio).
+ * tx_limit=1 (1T1R full isolation) reserved for future use
+ * when WL needs to fully isolate Tx from BT on shared antenna.
+ */
+ tx_limit = 0;
+ rx_limit = 1;
+ }
+
+ /*
+ * BT1 at S0 has CIS -> WL must use S1 (path B)
+ * BT0 at S1 has CIS -> WL must use S0 (path A)
+ */
+ limit_path = bt1_cis ? RF_PATH_B : RF_PATH_A;
+
+ /*
+ * Re-arm the exit-LPS one-shot once the event is over. lps_exiting is
+ * held for a while by the LPS-exit timer, so it must not be used as
+ * the force-exec condition directly.
+ */
+ if (!wl->status.map.lps_exiting)
+ dm->trx_nss_lps_done = 0;
+
+ if (trx_nss->rx_limit == rx_limit && trx_nss->tx_limit == tx_limit &&
+ (!rx_limit || trx_nss->rx_path == limit_path)) {
+ /*
+ * WL just exited LPS: LPS may have cleared the BB/RF 1SS
+ * setup, so re-issue cfg_1ss/cfg_trx_path/set_rfe once per
+ * exit-LPS event. They are heavy (set_rfe() re-reads efuse and
+ * does several RF I/O), so a one-shot flag instead of lps_exiting.
+ */
+ if (wl->status.map.lps_exiting && !dm->trx_nss_lps_done)
+ goto force_exec;
+
+ return;
+ }
+
+force_exec:
+ /* one-shot consumed, no re-exec until the next exit-LPS event */
+ dm->trx_nss_lps_done = wl->status.map.lps_exiting;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): stream_cnt=0x%02x, path_pos=0x%02x\n",
+ __func__, module->ant.stream_cnt, module->ant.path_pos);
+
+ /*
+ * Reduce TX to 1SS on the isolated path to avoid BT interference,
+ * else restore TX to HW default stream count and path.
+ */
+ if (tx_limit) {
+ tx_ss = 1;
+ tx_path = limit_path;
+ } else {
+ tx_ss = FIELD_GET(BTC_ANT_TX_MASK, module->ant.stream_cnt);
+ tx_path = FIELD_GET(BTC_ANT_TX_MASK, module->ant.path_pos);
+ }
+
+ /*
+ * Reduce RX to 1SS on the isolated path to protect WL Rx stream,
+ * else restore RX to HW default stream count and path.
+ */
+ if (rx_limit) {
+ rx_ss = 1;
+ rx_path = limit_path;
+ } else {
+ rx_ss = FIELD_GET(BTC_ANT_RX_MASK, module->ant.stream_cnt);
+ rx_path = FIELD_GET(BTC_ANT_RX_MASK, module->ant.path_pos);
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): tx_ss=%d, rx_ss=%d, tx_path=%d, rx_path=%d\n",
+ __func__, tx_ss, rx_ss, tx_path, rx_path);
+
+ /*
+ * TODO: no driver API to apply tx_limit/rx_limit/tx_ss/rx_ss/tx_path/
+ * rx_path to the BB/RF 1SS MIMO-PS HW state yet. Stop here so
+ * trx_nss/always_freerun/ant.type/set_rfe()/_update_sdm_map() below
+ * never run from a decision that was never applied to HW.
+ */
+}
+
static void _action_common(struct rtw89_dev *rtwdev)
{
struct rtw89_btc *btc = &rtwdev->btc;
@@ -7002,6 +7129,7 @@ static void _action_common(struct rtw89_dev *rtwdev)
u8 i;
_wl_req_mac(rtwdev, rinfo->pta_req_band);
+ _set_trx_nss(rtwdev);
_update_zb_coex_tbl(rtwdev);
_set_btg_ctrl(rtwdev);
_set_wl_preagc_ctrl(rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 340633c42c9f..624b8d1b4742 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -4188,6 +4188,7 @@ struct rtw89_btc_dm {
u8 vid;
u8 client_ps_tdma_on;
u8 wl_trx_nss_en;
+ u8 trx_nss_lps_done; /* 1SS re-setup done for this exit-LPS */
u8 wl_pre_agc: 2;
u8 wl_lna2: 1;
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index 41e2aa1c8f78..7ac15a5d949a 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -3336,7 +3336,6 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev)
md->bt0_pos = BTC_BT_BTG;
md->bt0_sw_type = BTC_SWITCH_V1_INTERNAL;
dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1;
- dm->wl_trx_nss_en = 1; /* 1ss MIMO-PS capability */
} else {
md->ant.func[0] = BTC_EFMAP_NONE;
md->ant.func[1] = BTC_EFMAP_NONE;
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 07/15] wifi: rtw89: coex: add firmware 0.35.119.3 support for RTL8922A/D
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (5 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 06/15] wifi: rtw89: coex: add 1SS MIMO-PS decision for dual-BT CIS Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 08/15] wifi: rtw89: coex: raise default BT TX power to 10 dBm Ping-Ke Shih
` (7 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
Firmware 0.35.119.3 moves both RTL8922A and RTL8922D to coex H2C
role-info version 11 and outsrc-set-info version 7. Add top
ver_defs entries for both chips with the new fwlrole and fcxosi
values; all other scope versions are unchanged from the previous
firmware.
Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/coex.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 0b9858d1859a..80c278aa96f0 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -133,6 +133,15 @@ static const u32 cxtbl[] = {
static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {
/* firmware version must be in decreasing order for each chip */
+ {RTL8922D, RTW89_FW_VER_CODE(0, 35, 119, 3),
+ .fcxbtcrpt = 11, .fcxtdma = 8, .fcxslots = 7, .fcxcysta = 8,
+ .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 8,
+ .fcxbtver = 8, .fcxbtscan = 8, .fcxbtafh = 8, .fcxbtdevinfo = 8,
+ .fwlrole = 11, .frptmap = 5, .fcxctrl = 9, .fcxinit = 11,
+ .fwevntrptl = 1, .fwc2hfunc = 4, .drvinfo_ver = 3, .info_buf = 1800,
+ .max_role_num = 6, .fcxosi = 7, .fcxmlo = 2, .bt_desired = 8,
+ .fcxtrx = 9, .fcxtxpwr = 1,
+ },
{RTL8922D, RTW89_FW_VER_CODE(0, 35, 111, 0),
.fcxbtcrpt = 11, .fcxtdma = 8, .fcxslots = 7, .fcxcysta = 8,
.fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 8,
@@ -178,6 +187,15 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {
.max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 8,
.fcxtrx = 0, .fcxtxpwr = 0,
},
+ {RTL8922A, RTW89_FW_VER_CODE(0, 35, 119, 3),
+ .fcxbtcrpt = 11, .fcxtdma = 8, .fcxslots = 7, .fcxcysta = 8,
+ .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 8,
+ .fcxbtver = 8, .fcxbtscan = 8, .fcxbtafh = 8, .fcxbtdevinfo = 8,
+ .fwlrole = 11, .frptmap = 5, .fcxctrl = 9, .fcxinit = 11,
+ .fwevntrptl = 1, .fwc2hfunc = 4, .drvinfo_ver = 3, .info_buf = 1800,
+ .max_role_num = 6, .fcxosi = 7, .fcxmlo = 2, .bt_desired = 8,
+ .fcxtrx = 9, .fcxtxpwr = 1,
+ },
{RTL8922A, RTW89_FW_VER_CODE(0, 35, 111, 0),
.fcxbtcrpt = 11, .fcxtdma = 8, .fcxslots = 7, .fcxcysta = 8,
.fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 8,
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 08/15] wifi: rtw89: coex: raise default BT TX power to 10 dBm
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (6 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 07/15] wifi: rtw89: coex: add firmware 0.35.119.3 support for RTL8922A/D Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 09/15] wifi: rtw89: coex: bind the RF band in use while WL is doing RFK Ping-Ke Shih
` (6 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
The TDD/FDD switch threshold is derived as the WL RSSI at which a BT
device's TX power, after antenna isolation, starts to interfere:
110 + bt_pin - ant_iso - sir_thres, in RSSI percent. Before the first
BT TX power report arrives, and on any link whose report never
arrives, bt_pin falls back to the BR/LE defaults, so those defaults
decide where coexistence flips between TDD and FDD.
Both defaults were 4 dBm, which places the threshold about 6 dB below
what the BT side actually transmits at, so the driver stays in FDD
well past the point where BT TX already interferes with WL RX. Raise
both to 10 dBm, which matches the BT TX power these designs report
once their first query completes.
The 8922A ext-SOC path already used a separate 10 dBm define for the
same reason; drop it now that the common default carries that value.
Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/core.h | 5 ++---
drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 +-
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 624b8d1b4742..b1f92c1d0428 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -2656,10 +2656,9 @@ enum rtw89_btc_bt_func_type {
#define RTW89_BTC_BTC_SCAN_V1_FLAG_ENABLE BIT(0)
#define RTW89_BTC_BTC_SCAN_V1_FLAG_INTERLACE BIT(1)
-#define RTW89_BTC_BT_DEF_BR_TX_PWR 4
-#define RTW89_BTC_BT_DEF_LE_TX_PWR 4
+#define RTW89_BTC_BT_DEF_BR_TX_PWR 10 /* in dBm */
+#define RTW89_BTC_BT_DEF_LE_TX_PWR 10
#define RTW89_BTC_DEFAULT_ANISO 10
-#define RTW89_BTC_BT_DEF_LE_TX_PWR_1 10
struct rtw89_btc_bt_scan_info_v1 {
__le16 win;
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
index 6402bd4f06aa..e48300287697 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
@@ -2796,7 +2796,7 @@ static void rtw8922a_btc_set_rfe(struct rtw89_dev *rtwdev)
default:
case BTC_ESOC_NONE:
memset(&btc->cx.bt_ext, 0, sizeof(struct rtw89_btc_extsoc_info));
- btc->cx.bt_ext.max_tx_pwr = RTW89_BTC_BT_DEF_LE_TX_PWR_1;
+ btc->cx.bt_ext.max_tx_pwr = RTW89_BTC_BT_DEF_LE_TX_PWR;
btc->cx.bt_ext.ant_iso_to_wl = RTW89_BTC_DEFAULT_ANISO;
break;
case BTC_ESOC_8771:
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 09/15] wifi: rtw89: coex: bind the RF band in use while WL is doing RFK
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (7 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 08/15] wifi: rtw89: coex: raise default BT TX power to 10 dBm Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 10/15] wifi: rtw89: coex: store BT TX power per device and per RF band Ping-Ke Shih
` (5 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
The RF band that TDD and FDD bind to is picked by scoring each BT
link's weight against the WL/BT band-overlap map. That map is built
from connected WL links, so while WL is running RFK on a band it has
not associated on yet, neither band scores and the binding ends up
empty. Coexistence then treats the chip as having no WL/BT band
overlap at all and leaves RFK unprotected against a BT device already
transmitting on that same band, which can corrupt the calibration
result.
Force the score of the band being calibrated up to the binding
threshold when any BT is on an overlapping band, so the RFK band
binds for as long as the calibration runs.
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 | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 80c278aa96f0..ebc4b1b415c8 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -8610,6 +8610,26 @@ static void _set_bind_info(struct rtw89_btc *btc, u8 type)
b5g_score += link_weight[j][BTC_BT_B5G];
}
+ /*
+ * Consider WL RFK band for coex (avoid missing RFK case)
+ * If WL is doing RFK, check RFK band vs BT band overlap
+ * Only add score when RFK band matches BT band
+ */
+ if (wl->rfk_info.state != BTC_WRFK_STOP) {
+ if (wl->rfk_info.band == RTW89_BAND_2G) {
+ /* RFK on 2.4GHz, check if any BT is also on 2.4GHz */
+ if ((cx->bt0.rf_band_map & BIT(RTW89_BAND_2G)) ||
+ (cx->bt1.rf_band_map & BIT(RTW89_BAND_2G)))
+ b2g_score = thres;
+ } else if (wl->rfk_info.band == RTW89_BAND_5G ||
+ wl->rfk_info.band == RTW89_BAND_6G) {
+ /* RFK on 5/6GHz, check if any BT is also on 5GHz */
+ if ((cx->bt0.rf_band_map & BIT(RTW89_BAND_5G)) ||
+ (cx->bt1.rf_band_map & BIT(RTW89_BAND_5G)))
+ b5g_score = thres;
+ }
+ }
+
/* rf-band bound by comparing link weight */
if (b2g_score == 0 && b5g_score == 0) /* no-rf-band overlap */
bd->rf_band = 0;
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 10/15] wifi: rtw89: coex: store BT TX power per device and per RF band
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (8 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 09/15] wifi: rtw89: coex: bind the RF band in use while WL is doing RFK Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 11/15] wifi: rtw89: coex: set the WL/BT antenna map on shared-antenna chips Ping-Ke Shih
` (4 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
A BT TX power report identifies which BT device came from, in bit 7
of its second byte, whether it describes that device's 2.4 GHz
or 5/6 GHz link. The handler ignored both and always wrote the first
BT device's 2.4 GHz link info.
On a chip with two BT devices or with 5/6 GHz BT support, the reported
power therefore never reached the second device or the 5/6 GHz link.
The TDD/FDD switch threshold reads exactly those fields when the WL
link is not on 2.4 GHz or when it scores the second BT, so on those
paths it kept comparing against the reset default instead of what BT
actually transmits at.
Select the device from the report's own index and the link from its
band bit, the same way the other BT mailbox reports already do.
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 | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index ebc4b1b415c8..c25bc9e619ce 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -8542,14 +8542,23 @@ static void _update_bt_bistdma_info(struct rtw89_dev *rtwdev, u8 bid,
}
#define BTC_BTINFO_PWR_LEN 5
-static void _update_bt_txpwr_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len)
+static void _update_bt_txpwr_info(struct rtw89_dev *rtwdev, u8 bid, u8 *buf,
+ u32 len)
{
- struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt0;
- struct rtw89_btc_bt_link_info *b = &bt->link_info;
+ struct rtw89_btc_cx *cx = &rtwdev->btc.cx;
+ struct rtw89_btc_bt_link_info *b;
+ struct rtw89_btc_bt_info *bt;
if (len != BTC_BTINFO_PWR_LEN)
return;
+ bt = bid == BTC_BT_1ST ? &cx->bt0 : &cx->bt1;
+
+ if ((buf[BTC_BTINFO_L1] & BIT(7)) && bt->band_56G_support)
+ b = &bt->link_info_56g;
+ else
+ b = &bt->link_info;
+
if (!memcmp(bt->txpwr_info, buf, sizeof(bt->txpwr_info))) {
rtw89_debug(rtwdev, RTW89_DBG_BTC,
"[BTC], %s return by info duplicate!\n", __func__);
@@ -10594,7 +10603,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
break;
case BTF_EVNT_BT_QUERY_TXPWR:
bt->bcnt[BTC_BCNT_TXPWR_UPDATE]++;
- _update_bt_txpwr_info(rtwdev, buf, len);
+ _update_bt_txpwr_info(rtwdev, bid, buf, len);
}
}
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 11/15] wifi: rtw89: coex: set the WL/BT antenna map on shared-antenna chips
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (9 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 10/15] wifi: rtw89: coex: store BT TX power per device and per RF band Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 12/15] wifi: rtw89: coex: use the right switch-type enum on RTL8922D Ping-Ke Shih
` (3 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
The WL/BT space-interaction map that decides whether a WL RF-path and a
BT take a TDD or an FDD action is built from the antenna-division map
with the cross-talk map. A WL RF-path that physically shares its
antenna with a BT must be marked in the antenna-division map, otherwise
it is only ever marked interacting when the measured WL RSSI happens to
fall below the BT Tx interference threshold, and a shared-antenna design
with a strong WL signal gets bound to FDD where it has to coexist in
time instead.
Chips with a single BT never filled that map, so fill it from the RFE
type that the antenna configuration is already derived from: mark the
one WL RF-path that carries the BTG circuit as sharing with the only BT
whenever the antenna type is shared, and leave the map clear for the
dedicated-antenna RFE types where the BT has an antenna of its own. The
second-BT and external-coexistence columns stay clear because these
designs have neither.
This is driver-internal state only. The coex init information these
chips send to firmware has no antenna-map field, so what reaches the
firmware is unchanged.
Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/rtw8851b.c | 5 +++++
drivers/net/wireless/realtek/rtw89/rtw8852a.c | 5 +++++
drivers/net/wireless/realtek/rtw89/rtw8852b.c | 5 +++++
drivers/net/wireless/realtek/rtw89/rtw8852bt.c | 6 ++++++
drivers/net/wireless/realtek/rtw89/rtw8852c.c | 5 +++++
5 files changed, 26 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
index db37ad6653bc..272f4f4e1fb9 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
@@ -2119,6 +2119,7 @@ static u8 rtw8851b_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_p
static void rtw8851b_btc_set_rfe(struct rtw89_dev *rtwdev)
{
struct rtw89_btc_module *md = &rtwdev->btc.mdinfo;
+ struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
md->rfe_type = rtwdev->efuse.rfe_type;
md->kt_ver = rtwdev->hal.cv;
@@ -2127,6 +2128,8 @@ static void rtw8851b_btc_set_rfe(struct rtw89_dev *rtwdev)
md->ant.isolation = 10;
md->kt_ver_adie = rtwdev->hal.acv;
+ memset(dm->ant_xmap, 0, sizeof(dm->ant_xmap));
+
if (md->rfe_type == 0)
return;
@@ -2145,6 +2148,8 @@ static void rtw8851b_btc_set_rfe(struct rtw89_dev *rtwdev)
md->bt0_pos = BTC_BT_BTG;
md->wa_type = 1;
md->ant.diversity = 0;
+ /* the only BT shares the BTG antenna with WL path A */
+ dm->ant_xmap[BTC_RF_S0][BTC_BT_1ST] = 1;
} else { /* ant.num == 2 */
md->ant.type = BTC_ANT_DEDICATED;
md->bt0_pos = BTC_BT_ALONE;
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
index 4b364f039464..5207df4f9cd5 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
@@ -1836,6 +1836,7 @@ static u8 rtw8852a_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_p
static void rtw8852a_btc_set_rfe(struct rtw89_dev *rtwdev)
{
struct rtw89_btc_module *md = &rtwdev->btc.mdinfo;
+ struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
md->rfe_type = rtwdev->efuse.rfe_type;
md->kt_ver = rtwdev->hal.cv;
@@ -1850,12 +1851,16 @@ static void rtw8852a_btc_set_rfe(struct rtw89_dev *rtwdev)
md->ant.diversity = 0;
md->ant.isolation = 10;
+ memset(dm->ant_xmap, 0, sizeof(dm->ant_xmap));
+
if (md->ant.num == 3) {
md->ant.type = BTC_ANT_DEDICATED;
md->bt0_pos = BTC_BT_ALONE;
} else {
md->ant.type = BTC_ANT_SHARED;
md->bt0_pos = BTC_BT_BTG;
+ /* the only BT shares the BTG antenna with WL path B */
+ dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1;
}
rtwdev->btc.btg_pos = md->ant.btg_pos;
rtwdev->btc.ant_type = md->ant.type;
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
index c3c53a6b8119..060c99532936 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
@@ -748,6 +748,7 @@ static void rtw8852b_rfk_track(struct rtw89_dev *rtwdev)
static void rtw8852b_btc_set_rfe(struct rtw89_dev *rtwdev)
{
struct rtw89_btc_module *md = &rtwdev->btc.mdinfo;
+ struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
md->rfe_type = rtwdev->efuse.rfe_type;
md->kt_ver = rtwdev->hal.cv;
@@ -762,12 +763,16 @@ static void rtw8852b_btc_set_rfe(struct rtw89_dev *rtwdev)
md->ant.diversity = 0;
md->ant.isolation = 10;
+ memset(dm->ant_xmap, 0, sizeof(dm->ant_xmap));
+
if (md->ant.num == 3) {
md->ant.type = BTC_ANT_DEDICATED;
md->bt0_pos = BTC_BT_ALONE;
} else {
md->ant.type = BTC_ANT_SHARED;
md->bt0_pos = BTC_BT_BTG;
+ /* the only BT shares the BTG antenna with WL path B */
+ dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1;
}
rtwdev->btc.btg_pos = md->ant.btg_pos;
rtwdev->btc.ant_type = md->ant.type;
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
index 3d93cee562c2..23ad02d1fcd4 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
@@ -636,6 +636,7 @@ static void rtw8852bt_rfk_track(struct rtw89_dev *rtwdev)
static void rtw8852bt_btc_set_rfe(struct rtw89_dev *rtwdev)
{
struct rtw89_btc_module *md = &rtwdev->btc.mdinfo;
+ struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
md->rfe_type = rtwdev->efuse.rfe_type;
md->kt_ver = rtwdev->hal.cv;
@@ -653,6 +654,10 @@ static void rtw8852bt_btc_set_rfe(struct rtw89_dev *rtwdev)
md->ant.single_pos = RF_PATH_A;
md->ant.btg_pos = RF_PATH_B;
+ memset(dm->ant_xmap, 0, sizeof(dm->ant_xmap));
+ /* the only BT shares the BTG antenna with WL path B */
+ dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1;
+
if (md->rfe_type == 0) {
rtwdev->btc.dm.error.map.rfe_type0 = true;
return;
@@ -669,6 +674,7 @@ static void rtw8852bt_btc_set_rfe(struct rtw89_dev *rtwdev)
} else {
md->ant.type = BTC_ANT_DEDICATED;
md->bt0_pos = BTC_BT_ALONE;
+ dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 0;
}
}
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
index 624f23b3f2a1..e556ba0c14a7 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
@@ -2609,6 +2609,7 @@ static u8 rtw8852c_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_p
static void rtw8852c_btc_set_rfe(struct rtw89_dev *rtwdev)
{
struct rtw89_btc_module *md = &rtwdev->btc.mdinfo;
+ struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
md->rfe_type = rtwdev->efuse.rfe_type;
md->kt_ver = rtwdev->hal.cv;
@@ -2623,12 +2624,16 @@ static void rtw8852c_btc_set_rfe(struct rtw89_dev *rtwdev)
md->ant.diversity = 0;
md->ant.isolation = 10;
+ memset(dm->ant_xmap, 0, sizeof(dm->ant_xmap));
+
if (md->ant.num == 3) {
md->ant.type = BTC_ANT_DEDICATED;
md->bt0_pos = BTC_BT_ALONE;
} else {
md->ant.type = BTC_ANT_SHARED;
md->bt0_pos = BTC_BT_BTG;
+ /* the only BT shares the BTG antenna with WL path B */
+ dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1;
}
rtwdev->btc.btg_pos = md->ant.btg_pos;
rtwdev->btc.ant_type = md->ant.type;
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 12/15] wifi: rtw89: coex: use the right switch-type enum on RTL8922D
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (10 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 11/15] wifi: rtw89: coex: set the WL/BT antenna map on shared-antenna chips Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 13/15] wifi: rtw89: phy: Implement API to control BTG to turn on/off BT TX/RX Ping-Ke Shih
` (2 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
Two unrelated enumerations describe a BT antenna switch type. The one
the RTL8922D antenna configuration uses numbers the absent switch as
zero and the internal switch as one, while the older one numbers the
internal switch as zero. The shared 2-antenna case picked the internal
value from the older enumeration, so it stored zero and claimed no
switch at all.
A BT whose switch type says there is no switch is treated as not sharing
an antenna with the WL RF-path that the other RF-path shares with it,
which drops the cross-talk entry for that path. Store the value from the
enumeration this field is read with. The 1-antenna and 3-antenna cases
in the same function already do.
Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
index 7ac15a5d949a..271a92123158 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c
@@ -3305,7 +3305,7 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev)
if (is_bt_share) { /* WL-S0 + (WL-S1 & BT0-S1) */
md->ant.type = BTC_ANT_SHARED;
md->bt0_pos = BTC_BT_BTG;
- md->bt0_sw_type = BTC_SWITCH_INTERNAL;
+ md->bt0_sw_type = BTC_SWITCH_V1_INTERNAL;
dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1;
} else { /* WL-S0 + BT0-S1 */
md->ant.type = BTC_ANT_DEDICATED;
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 13/15] wifi: rtw89: phy: Implement API to control BTG to turn on/off BT TX/RX
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (11 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 12/15] wifi: rtw89: coex: use the right switch-type enum on RTL8922D Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 14/15] wifi: rtw89: coex: turn BT TX/RX on and off through the BTG control Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 15/15] wifi: rtw89: coex: set rf_combination in _update_wl_mlo_info() Ping-Ke Shih
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
The WiFi BE serials chips could disable BT TX/RX by BTG control, with this
control logic, WiFi could lower down the interference signal from BT.
Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/core.h | 1 +
drivers/net/wireless/realtek/rtw89/fw.c | 2 ++
drivers/net/wireless/realtek/rtw89/phy.h | 12 ++++++++++++
drivers/net/wireless/realtek/rtw89/phy_be.c | 18 ++++++++++++++++++
drivers/net/wireless/realtek/rtw89/reg.h | 2 ++
5 files changed, 35 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index b1f92c1d0428..d70123602801 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -6120,6 +6120,7 @@ enum rtw89_fw_feature {
RTW89_FW_FEATURE_LPS_ML_INFO_V1_EXTRA,
RTW89_FW_FEATURE_SER_POST_RECOVER_DMAC,
RTW89_FW_FEATURE_TX_HISTORY_V1,
+ RTW89_FW_FEATURE_NO_DRV_BT_TXRX_EN,
NUM_OF_RTW89_FW_FEATURES,
};
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index e9f6dc25b94c..ea7dba077f5d 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -1026,6 +1026,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = {
__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 109, 1, SCAN_OFFLOAD_BE_V1),
__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 113, 2, SCAN_OFFLOAD_BE_V2),
__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 119, 0, CH_INFO_BE_V1),
+ __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 119, 3, NO_DRV_BT_TXRX_EN),
__CFG_FW_FEAT(RTL8922D, ge, 0, 0, 0, 0, MACID_PAUSE_SLEEP),
__CFG_FW_FEAT(RTL8922D, ge, 0, 35, 75, 2, SCAN_OFFLOAD),
__CFG_FW_FEAT(RTL8922D, ge, 0, 35, 75, 2, BEACON_FILTER),
@@ -1044,6 +1045,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = {
__CFG_FW_FEAT(RTL8922D, lt, 0, 35, 113, 2, SCAN_OFFLOAD_BE_V2),
__CFG_FW_FEAT(RTL8922D, ge, 0, 35, 119, 0, LPS_ML_INFO_V1_EXTRA),
__CFG_FW_FEAT(RTL8922D, lt, 0, 35, 119, 0, CH_INFO_BE_V1),
+ __CFG_FW_FEAT(RTL8922D, lt, 0, 35, 119, 3, NO_DRV_BT_TXRX_EN),
};
static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw,
diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h
index 7bbda2ba5b5c..84eb1defaaf2 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.h
+++ b/drivers/net/wireless/realtek/rtw89/phy.h
@@ -686,6 +686,7 @@ struct rtw89_phy_gen_def {
void (*preinit_rf_nctl)(struct rtw89_dev *rtwdev);
void (*bb_wrap_init)(struct rtw89_dev *rtwdev);
void (*ch_info_init)(struct rtw89_dev *rtwdev);
+ int (*btc_bttrx_en)(struct rtw89_dev *rtwdev, u8 bt_en_int, bool enable);
void (*set_txpwr_byrate)(struct rtw89_dev *rtwdev,
const struct rtw89_chan *chan,
@@ -1066,6 +1067,17 @@ static inline void rtw89_phy_ch_info_init(struct rtw89_dev *rtwdev)
phy->ch_info_init(rtwdev);
}
+static inline
+int rtw89_phy_btc_bttrx_en(struct rtw89_dev *rtwdev, u8 bt_en_int, bool enable)
+{
+ const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
+
+ if (!phy->btc_bttrx_en)
+ return 0;
+
+ return phy->btc_bttrx_en(rtwdev, bt_en_int, enable);
+}
+
static inline
void rtw89_phy_set_txpwr_byrate(struct rtw89_dev *rtwdev,
const struct rtw89_chan *chan,
diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c
index b94754d38673..42470e868ef4 100644
--- a/drivers/net/wireless/realtek/rtw89/phy_be.c
+++ b/drivers/net/wireless/realtek/rtw89/phy_be.c
@@ -1293,6 +1293,22 @@ static void rtw89_phy_ch_info_init_be_v1(struct rtw89_dev *rtwdev)
rtw89_phy_set_phy_regs(rtwdev, R_CHINFO_ALG_BE4, B_CHINFO_ALG_BE4, 0);
}
+static int rtw89_phy_btc_bttrx_en_be(struct rtw89_dev *rtwdev, u8 bt_en_int,
+ bool enable)
+{
+ if (RTW89_CHK_FW_FEATURE(NO_DRV_BT_TXRX_EN, &rtwdev->fw))
+ return 0;
+
+ rtw89_io_pack(rtwdev);
+
+ rtw89_write_rf(rtwdev, RF_PATH_A, RR_BTG_CONTROL,
+ RR_BTG_CONTROL_EN << bt_en_int, enable);
+ rtw89_write_rf(rtwdev, RF_PATH_B, RR_BTG_CONTROL,
+ RR_BTG_CONTROL_EN << bt_en_int, enable);
+
+ return rtw89_io_unpack(rtwdev);
+}
+
struct rtw89_byr_spec_ent_be {
struct rtw89_rate_desc init;
u8 num_of_idx;
@@ -2035,6 +2051,7 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be = {
.preinit_rf_nctl = rtw89_phy_preinit_rf_nctl_be,
.bb_wrap_init = rtw89_phy_bb_wrap_init_be,
.ch_info_init = rtw89_phy_ch_info_init_be,
+ .btc_bttrx_en = rtw89_phy_btc_bttrx_en_be,
.set_txpwr_byrate = rtw89_phy_set_txpwr_byrate_be,
.set_txpwr_offset = rtw89_phy_set_txpwr_offset_be,
@@ -2061,6 +2078,7 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be_v1 = {
.preinit_rf_nctl = rtw89_phy_preinit_rf_nctl_be_v1,
.bb_wrap_init = rtw89_phy_bb_wrap_init_be,
.ch_info_init = rtw89_phy_ch_info_init_be_v1,
+ .btc_bttrx_en = rtw89_phy_btc_bttrx_en_be,
.set_txpwr_byrate = rtw89_phy_set_txpwr_byrate_be,
.set_txpwr_offset = rtw89_phy_set_txpwr_offset_be,
diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h
index 11b30be8f880..4829f9fd8eb5 100644
--- a/drivers/net/wireless/realtek/rtw89/reg.h
+++ b/drivers/net/wireless/realtek/rtw89/reg.h
@@ -8582,6 +8582,8 @@
#define RR_MODOPT_M_TXPWR GENMASK(5, 0)
#define RR_WLSEL 0x02
#define RR_WLSEL_AG GENMASK(18, 16)
+#define RR_BTG_CONTROL 0x4
+#define RR_BTG_CONTROL_EN BIT(2)
#define RR_RSV1 0x05
#define RR_RSV1_RST BIT(0)
#define RR_BBDC 0x10005
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 14/15] wifi: rtw89: coex: turn BT TX/RX on and off through the BTG control
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (12 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 13/15] wifi: rtw89: phy: Implement API to control BTG to turn on/off BT TX/RX Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
2026-10-04 3:45 ` [PATCH rtw-next 15/15] wifi: rtw89: coex: set rf_combination in _update_wl_mlo_info() Ping-Ke Shih
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
Nothing drives the BTG control that turns the BT TX/RX on and off, so the
BT keeps transmitting and receiving even where COEX already knows it is
not in use, and WL takes interference it does not have to.
Program it from the BT enable state coex already maintains, which is the
same value reported to the firmware, so a driver-side write and a
firmware-side one always agree. This matters for the BT-only case, where
that state is forced to enable both BT while the per-BT enable may still
be clear.
That report was not actually reaching the firmware. The control
information is sent only when it differs from what was sent last, but
the comparison is inverted, so an unchanged run sends a redundant
command while a real change is dropped. Drop the inversion, so the
state programmed here is the state the firmware is told about.
Each BT keeps its own shadow of what was programmed and of whether that
is trustworthy, rather than writing on a change of the enable state. A
change is a one-shot edge, so an RF write the lower layer refuses would
never be retried, and an offloaded write can be refused for reasons coex
cannot see. The shadow is dropped on init and on a radio-state change
since the RF may have been re-initialized, and compared on every run so
that a redundant write is suppressed without losing the retry. Skip the
whole thing while the RF is off, and on chips with a single BT, which do
not drive this control from the driver side.
Report the state and the shadow in the coex mechanism-status dump.
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 | 81 +++++++++++++++++++++--
drivers/net/wireless/realtek/rtw89/core.h | 2 +
2 files changed, 76 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index c25bc9e619ce..9107a6c8c6fc 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -8910,6 +8910,59 @@ static void _update_sdm_map(struct rtw89_dev *rtwdev)
memcpy(dm->sit_xmap, map, sizeof(map));
}
+static void _set_bttrx_en(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc_wl_smap *wl_smap = &rtwdev->btc.cx.wl.status.map;
+ struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
+ u8 state = dm->ost_info.bt_enable_state;
+ u8 i, want, done;
+
+ /* only dual-BT chips drive the BT TX/RX enable from the driver */
+ if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT))
+ return;
+
+ /*
+ * RF is not accessible, leave the shadow alone. Leaving this state is
+ * a radio-state run, which drops the shadow and re-programs below.
+ */
+ if (wl_smap->rf_off || wl_smap->lps == BTC_LPS_RF_OFF)
+ return;
+
+ /*
+ * RF may have been re-initialized, drop the shadow and re-program.
+ * Power-off is not listed, it is always followed by an init run
+ * before the next RF access.
+ */
+ if (dm->run_reason == BTC_RSN_NTFY_INIT ||
+ dm->run_reason == BTC_RSN_NTFY_RADIO_STATE)
+ dm->bttrx_en_ack = 0;
+
+ for (i = BTC_BT_1ST; i <= BTC_BT_2ND; i++) {
+ want = !!(state & BIT(i));
+ done = !!(dm->bttrx_en_done & BIT(i));
+
+ if (dm->bttrx_en_ack & BIT(i) && want == done)
+ continue;
+
+ if (rtw89_phy_btc_bttrx_en(rtwdev, i, want)) {
+ /* leave the bit un-acked, the next run retries it */
+ dm->bttrx_en_ack &= ~BIT(i);
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): BT%d trx_en=%d write fail\n",
+ __func__, i, want);
+ continue;
+ }
+
+ dm->bttrx_en_ack |= BIT(i);
+
+ if (want)
+ dm->bttrx_en_done |= BIT(i);
+ else
+ dm->bttrx_en_done &= ~BIT(i);
+ }
+}
+
#define _bind_is_btonly 0x7
static void _set_coex_binding(struct rtw89_dev *rtwdev)
{
@@ -8927,7 +8980,7 @@ static void _set_coex_binding(struct rtw89_dev *rtwdev)
/*
* sit_xmap(Space-Interaction) = ant_xmap | xtk_xmap
* 1: WL-BT space-interference, always 1 if BTG/BTA/SPDT = 1
- * if dedicated-ant, it may be 1 if BT-Tx is bigger than WL-Rx(xtk_xmap)
+ * if dedicated-ant, it may be 1 if BT-TX is bigger than WL-Rx(xtk_xmap)
*
* ant_xmap(ANT-Division-Multiplexing)
* ==> dedicated-ant->0, BTG/BTA/SPDT->1
@@ -8972,8 +9025,8 @@ static void _set_coex_binding(struct rtw89_dev *rtwdev)
}
/*
- * WL 1SS MIMO-PS: WL Rx is confined to a single path, so the unused
- * path has no WL-Rx frequency interference with any BT.
+ * WL 1SS MIMO-PS: WL RX is confined to a single path, so the unused
+ * path has no WL-RX frequency interference with any BT.
* RF_PATH_AB -> not 1SS, both paths keep their real fit_xmap.
* Same helper as _update_sdm_map(), so xtk_xmap and fit_map always
* agree on which RF-path is the unused one.
@@ -9028,15 +9081,23 @@ static void _set_coex_binding(struct rtw89_dev *rtwdev)
/* set BT on/off state for GNT_WL Combined-MUX control */
if (bt0->enable.now)
- val |= BIT(0);
+ val |= BIT(BTC_BT_1ST);
if (bt1->enable.now)
- val |= BIT(1);
+ val |= BIT(BTC_BT_2ND);
if (bt2->func_type)
- val |= BIT(2);
+ val |= BIT(BTC_BT_EXT);
dm->ost_info.bt_enable_state = dm->bt_only ? _bind_is_btonly : val;
+
+ /*
+ * Turn the BT TX/RX on/off by the BTG control in WL RF. Driven by
+ * bt_enable_state shadow instead of its change, so a write the RF
+ * layer rejects is retried in the next run. Call it every time, the
+ * shadow compare inside is what suppresses the redundant RF I/O.
+ */
+ _set_bttrx_en(rtwdev);
}
static void _update_run_ctrl_info(struct rtw89_dev *rtwdev)
@@ -9063,7 +9124,7 @@ static void _update_run_ctrl_info(struct rtw89_dev *rtwdev)
wl_ctrl_info->rf_ch[i] = wl->rf_ch_info[i].center_ch;
}
- if (!memcmp(&btc->ctrl, &ctrl, sizeof(struct rtw89_btc_ctrl))) {
+ if (memcmp(&btc->ctrl, &ctrl, sizeof(struct rtw89_btc_ctrl))) {
memcpy(&btc->ctrl, &ctrl, sizeof(struct rtw89_btc_ctrl));
if (btc->ver->fcxctrl >= 9)
_fw_set_drv_info(rtwdev, CXDRVINFO_CTRL);
@@ -11444,6 +11505,12 @@ static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
" %-15s : pre_agc:%d, btg_rx:%d\n",
"[dm_bb_ctrl]", dm->wl_pre_agc, dm->wl_btg_rx);
+ if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT)
+ p += scnprintf(p, end - p,
+ " %-15s : bt_en_state:0x%x, shadow[done:0x%x/ack:0x%x]\n",
+ "[dm_bttrx_en]", dm->ost_info.bt_enable_state,
+ dm->bttrx_en_done, dm->bttrx_en_ack);
+
p += scnprintf(p, end - p,
" %-15s : wl_tx_limit[en:%d/max_t:%dus/max_retry:%d], bt_slot_reg:%d-TU, bt_scan_rx_low_pri:%d\n",
"[dm_ctrl]", dm->wl_tx_limit.enable,
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index d70123602801..a1e899576582 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -4188,6 +4188,8 @@ struct rtw89_btc_dm {
u8 client_ps_tdma_on;
u8 wl_trx_nss_en;
u8 trx_nss_lps_done; /* 1SS re-setup done for this exit-LPS */
+ u8 bttrx_en_done; /* BT TRX enable last programmed to RF */
+ u8 bttrx_en_ack; /* which bttrx_en_done bits are trustworthy */
u8 wl_pre_agc: 2;
u8 wl_lna2: 1;
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH rtw-next 15/15] wifi: rtw89: coex: set rf_combination in _update_wl_mlo_info()
2026-10-04 3:45 [PATCH rtw-next 00/15] wifi: rtw89: coex: update BT-coexistence along changes of newer firmware Ping-Ke Shih
` (13 preceding siblings ...)
2026-10-04 3:45 ` [PATCH rtw-next 14/15] wifi: rtw89: coex: turn BT TX/RX on and off through the BTG control Ping-Ke Shih
@ 2026-10-04 3:45 ` Ping-Ke Shih
14 siblings, 0 replies; 17+ messages in thread
From: Ping-Ke Shih @ 2026-10-04 3:45 UTC (permalink / raw)
To: linux-wireless; +Cc: ku920601
From: Ching-Te Ku <ku920601@realtek.com>
_update_wl_mlo_info() derives path_rf_band, link_mode, dbcc_en and
other per-RF-path fields from the current MLO link_status, but never
wrote mlo_info->rf_combination in any of the three link_status cases.
The field stayed at its zero-initialized value, which aliases
BTC_MLO_RF_2_PLUS_0.
_set_bind_info() and _set_coex_binding() both branch on
rf_combination to decide how to bind WL RF-paths to BT devices for
1+1, 2+2, 2+0 and 0+2 MLO combinations. With the field always zero,
those branches silently treated every MLO combination as 2+0,
producing an incorrect WL/BT binding whenever the real combination
was 1+1, 2+2 or 0+2.
Set rf_combination to the matching BTC_MLO_RF_* value in each of the
2+0, 0+2 and 1+1/2+2 branches so the binding logic sees the actual
RF 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 | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
index 9107a6c8c6fc..e81581e6da5a 100644
--- a/drivers/net/wireless/realtek/rtw89/coex.c
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -7806,6 +7806,7 @@ static void _update_wl_mlo_info(struct rtw89_dev *rtwdev, u8 hw_band)
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;
+ mlo_info->rf_combination = BTC_MLO_RF_2_PLUS_0;
break;
case MLO_0_PLUS_2_1RF: /* 0+2 */
case MLO_0_PLUS_2_2RF:
@@ -7818,6 +7819,7 @@ static void _update_wl_mlo_info(struct rtw89_dev *rtwdev, u8 hw_band)
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;
+ mlo_info->rf_combination = BTC_MLO_RF_0_PLUS_2;
break;
case MLO_1_PLUS_1_1RF: /* 1+1 */
case MLO_1_PLUS_1_2RF: /* 1+1 */
@@ -7853,6 +7855,11 @@ static void _update_wl_mlo_info(struct rtw89_dev *rtwdev, u8 hw_band)
}
wl_rinfo->dbcc_en = 1; /* two HW-band both active */
+
+ 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;
break;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 17+ messages in thread