From 7123cb442e13fb426bee8132d0420b59071a5153 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 25 May 2026 15:07:34 +0800 Subject: [PATCH 001/403] wifi: rtw89: fw: add first set of firmware features by version for RTL8922D The firmware features including version of command/event format are maintained by this table, which enables features by firmware version. Define the first feature set accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260525070735.27659-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index d6a594b75ab2..41b48e0d73a3 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -933,6 +933,19 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 92, 0, TX_HISTORY_V1), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 100, 0, SER_POST_RECOVER_DMAC), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 108, 0, SIM_SER_L0L1_BY_HALT_H2C), + __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), + __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 76, 0, LPS_DACK_BY_C2H_REG), + __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 84, 0, CRASH_TRIGGER_TYPE_1), + __CFG_FW_FEAT(RTL8922D, lt, 0, 35, 84, 0, ADDR_CAM_V0), + __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 87, 0, BEACON_LOSS_COUNT_V1), + __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 91, 0, RFK_PRE_NOTIFY_MCC_V2), + __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 91, 4, LPS_ML_INFO_V1), + __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 93, 0, NOTIFY_AP_INFO), + __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 100, 0, SER_POST_RECOVER_DMAC), + __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 104, 0, TX_HISTORY_V1), + __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 108, 0, SIM_SER_L0L1_BY_HALT_H2C), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, From 92827aaf07ceeb1db93a847c287f44f2ae7cda41 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 25 May 2026 15:07:35 +0800 Subject: [PATCH 002/403] wifi: rtw89: fw: support scan offload v2 for WiFi 7 chips The format of scan offload v2 is to extend fields to consider channel noise as a factor to adjust dwell time of certain channels. Leave empty for now to ignore this factor. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260525070735.27659-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/fw.c | 9 +++++++-- drivers/net/wireless/realtek/rtw89/fw.h | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 5547888d7e67..4bf6fe9d3880 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4917,6 +4917,7 @@ enum rtw89_fw_feature { RTW89_FW_FEATURE_BEACON_FILTER, RTW89_FW_FEATURE_MACID_PAUSE_SLEEP, RTW89_FW_FEATURE_SCAN_OFFLOAD_BE_V0, + RTW89_FW_FEATURE_SCAN_OFFLOAD_BE_V1, RTW89_FW_FEATURE_WOW_REASON_V1, RTW89_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, RTW89_FW_FEATURE_RFK_PRE_NOTIFY_V0, diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 41b48e0d73a3..3707dc5bf4f8 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -933,6 +933,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 92, 0, TX_HISTORY_V1), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 100, 0, SER_POST_RECOVER_DMAC), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 108, 0, SIM_SER_L0L1_BY_HALT_H2C), + __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 109, 1, SCAN_OFFLOAD_BE_V1), __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), @@ -946,6 +947,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 100, 0, SER_POST_RECOVER_DMAC), __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 104, 0, TX_HISTORY_V1), __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 108, 0, SIM_SER_L0L1_BY_HALT_H2C), + __CFG_FW_FEAT(RTL8922D, lt, 0, 35, 109, 1, SCAN_OFFLOAD_BE_V1), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, @@ -6842,7 +6844,10 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, rtw89_scan_get_6g_disabled_chan(rtwdev, option); - if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) { + if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V1, &rtwdev->fw)) { + cfg_len = offsetofend(typeof(*h2c), w9); + scan_offload_ver = 1; + } else if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) { cfg_len = offsetofend(typeof(*h2c), w8); scan_offload_ver = 0; } @@ -6921,7 +6926,7 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, if (scan_offload_ver == 0) goto flex_member; - h2c->w9 = le32_encode_bits(sizeof(*h2c) / sizeof(h2c->w0), + h2c->w9 = le32_encode_bits(cfg_len / sizeof(h2c->w0), RTW89_H2C_SCANOFLD_BE_W9_SIZE_CFG) | le32_encode_bits(sizeof(*macc_role) / sizeof(macc_role->w0), RTW89_H2C_SCANOFLD_BE_W9_SIZE_MACC) | diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 20721d5209aa..6cb99144e541 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3063,6 +3063,8 @@ struct rtw89_h2c_scanofld_be { __le32 w7; __le32 w8; __le32 w9; /* Added after SCAN_OFFLOAD_BE_V1 */ + __le32 w10; /* Added after SCAN_OFFLOAD_BE_V2 */ + __le32 w11; /* Added after SCAN_OFFLOAD_BE_V2 */ /* struct rtw89_h2c_scanofld_be_macc_role (flexible number) */ /* struct rtw89_h2c_scanofld_be_opch (flexible number) */ } __packed; From 1908534deb53a018580309be84a4f7dcc9cb1af3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 8 Jun 2026 15:34:46 +0300 Subject: [PATCH 003/403] wifi: rtw89: debug: fix off by on in rtw89_ppdu_str() This > comparison should be >= to avoid an out of bounds access. Fixes: 419ed7f4a053 ("wifi: rtw89: debug: extend bb_info with TX status and PER") Signed-off-by: Dan Carpenter Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/aia25i0ds3B6QF6c@stanley.mountain --- drivers/net/wireless/realtek/rtw89/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 8f5af873e09f..5786120602ab 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4348,7 +4348,7 @@ static const char *rtw89_ppdu_str(struct rtw89_dev *rtwdev, u8 type, u8 subtype) const struct rtw89_chip_info *chip = rtwdev->chip; const struct rtw89_ppdu_info *ppdu_info; - if (type > ARRAY_SIZE(rtw89_ppdu_infos)) + if (type >= ARRAY_SIZE(rtw89_ppdu_infos)) return "RSVD"; ppdu_info = &rtw89_ppdu_infos[type]; From 1b4cd55626b6ffa93d18848412afb912a4add585 Mon Sep 17 00:00:00 2001 From: William Hansen-Baird Date: Tue, 9 Jun 2026 11:53:59 +0200 Subject: [PATCH 004/403] wifi: rtlwifi: rtl8723be: Remove unnecessary irq save/restore in hw_init() rtl8723be hw_init() calls local_save_flags(flags) followed by local_irq_enable(). Later, local_irq_restore(flags) is called. This causes warnings from Lockdep on boot and modprobe, as local_irq_restore(flags) should only be called while irqs are disabled. The warning was introduced to detect this class of bug in [1]. With testing I found that all paths which call hw_init() have irqs already enabled for rtl8723be. Furthermore, the calls were originally added for the rtl8192ce in commit f78bccd79ba3 ("rtlwifi: rtl8192ce: Fix too long disable of IRQs") before later being added to most other rtlwifi drivers. Commit d3feae41a347 ("rtlwifi: Update power-save routines for 062814 driver") then replaces the call to spin_lock_irqsave() before hw_init(), and thus the codepath which caused irqs to be disabled in hw_init and prompted the original commit has been removed. The same irq save/restore pattern is also present in the hw_init() of rtl8192ce, rtl8723ae, rtl8188ee, rtl8192se and rtl8192cu, however I don't have the hardware to test them, so I did not include them in my changes. Tested on a Razer Blade 14 2017. Example of output from Lockdep prior to fix: raw_local_irq_restore() called with IRQs enabled ... Call Trace: rtl8723be_hw_init+0x5992/0x7220 [rtl8723be] ? static_obj+0x61/0xa0 rtl_pci_start+0x222/0x5c0 [rtl_pci] rtl_op_start+0x128/0x1a0 [rtlwifi] ? __kasan_check_read+0x11/0x20 drv_start+0x16c/0x550 [mac80211] ... irq event stamp: 887679 hardirqs last enabled at (887689): [] __up_console_sem+0x90/0xa0 hardirqs last disabled at (887698): [] __up_console_sem+0x75/0xa0 softirqs last enabled at (887670): [] __irq_exit_rcu+0x175/0x2f0 softirqs last disabled at (887649): [] __irq_exit_rcu+0x175/0x2f0 ---[ end trace 0000000000000000 ]--- [1] https://lore.kernel.org/all/20210111153707.10071-1-mark.rutland@arm.com/ Signed-off-by: William Hansen-Baird Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260609095359.2964193-1-william.hansen.baird@gmail.com --- drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c index e1f811218894..bf7b5a32adaa 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c @@ -1333,11 +1333,6 @@ int rtl8723be_hw_init(struct ieee80211_hw *hw) bool rtstatus = true; int err; u8 tmp_u1b; - unsigned long flags; - - /* reenable interrupts to not interfere with other devices */ - local_save_flags(flags); - local_irq_enable(); rtlhal->fw_ready = false; rtlpriv->rtlhal.being_init_adapter = true; @@ -1443,7 +1438,6 @@ int rtl8723be_hw_init(struct ieee80211_hw *hw) rtl8723be_dm_init(hw); exit: - local_irq_restore(flags); rtlpriv->rtlhal.being_init_adapter = false; return err; } From a4a2c1a1032f8254e18b44cfdbe7e2be433b758f Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 10 Jun 2026 16:01:35 +0300 Subject: [PATCH 005/403] wifi: rtw88: 8822c: Don't process RF path C in query_phy_status_page{0,1} Replace <= with < in the loops in query_phy_status_page{0,1}(). They were processing data related to RF path C, which this chip doesn't have. The only bad effect seems to be that the phy_info file in debugfs was printing unexpected values for RF path C. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/ee30b95f-bc68-4711-9b15-cf5fd23c3c48@gmail.com --- drivers/net/wireless/realtek/rtw88/rtw8822c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index 244c8026479c..80c9f0c11e5c 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -2584,7 +2584,7 @@ static void query_phy_status_page0(struct rtw_dev *rtwdev, u8 *phy_status, pkt_stat->rx_power[RF_PATH_A] = rx_power[RF_PATH_A]; pkt_stat->rx_power[RF_PATH_B] = rx_power[RF_PATH_B]; - for (path = 0; path <= rtwdev->hal.rf_path_num; path++) { + for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rssi = rtw_phy_rf_power_2_rssi(&pkt_stat->rx_power[path], 1); dm_info->rssi[path] = rssi; } @@ -2644,7 +2644,7 @@ static void query_phy_status_page1(struct rtw_dev *rtwdev, u8 *phy_status, pkt_stat->cfo_tail[RF_PATH_A] = GET_PHY_STAT_P1_CFO_TAIL_A(phy_status); pkt_stat->cfo_tail[RF_PATH_B] = GET_PHY_STAT_P1_CFO_TAIL_B(phy_status); - for (path = 0; path <= rtwdev->hal.rf_path_num; path++) { + for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rssi = rtw_phy_rf_power_2_rssi(&pkt_stat->rx_power[path], 1); dm_info->rssi[path] = rssi; if (path == RF_PATH_A) { From 617b1d97617bd44319e796811cf5cd4a2cf634fc Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 10 Jun 2026 16:02:21 +0300 Subject: [PATCH 006/403] wifi: rtw88: 8822b: Don't process RF path C in query_phy_status_page1 Replace <= with < in the loop in query_phy_status_page1(). It was processing data related to RF path C, which this chip doesn't have. The only bad effect seems to be that the phy_info file in debugfs was printing unexpected values for RF path C. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/9c4beb36-2954-4db0-844a-74ba5eacf21b@gmail.com --- drivers/net/wireless/realtek/rtw88/rtw8822b.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index e9e8a7f3f382..37b7a520fea0 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -896,7 +896,7 @@ static void query_phy_status_page1(struct rtw_dev *rtwdev, u8 *phy_status, pkt_stat->cfo_tail[RF_PATH_A] = GET_PHY_STAT_P1_CFO_TAIL_A(phy_status); pkt_stat->cfo_tail[RF_PATH_B] = GET_PHY_STAT_P1_CFO_TAIL_B(phy_status); - for (path = 0; path <= rtwdev->hal.rf_path_num; path++) { + for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rssi = rtw_phy_rf_power_2_rssi(&pkt_stat->rx_power[path], 1); dm_info->rssi[path] = rssi; dm_info->rx_snr[path] = pkt_stat->rx_snr[path] >> 1; From e13cd023a4cdbbb9e58ab91e857b5e45ea753f19 Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Thu, 11 Jun 2026 16:20:21 +0800 Subject: [PATCH 007/403] wifi: rtw89: fw: correct preload field of w2 in rtw89_fw_h2c_default_cmac_tbl_be() BE_CCTL_INFO_W2_PRELOAD_ENABLE is for h2c->w2, not h2c->w1. These will cause h2c->w1 wrong overlap by w2 and w2 not initialized. Fixes: c73607b3a8ef ("wifi: rtw89: fw: add CMAC H2C command to initialize default value for RTL8922D") Signed-off-by: Wentao Guan Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260611082021.46650-1-guanwentao@uniontech.com --- drivers/net/wireless/realtek/rtw89/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 3707dc5bf4f8..27b52845c1dd 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3744,7 +3744,7 @@ int rtw89_fw_h2c_default_cmac_tbl_be(struct rtw89_dev *rtwdev, le32_encode_bits(4, BE_CCTL_INFO_W1_RTS_RTY_LOWEST_RATE); h2c->m1 = cpu_to_le32(BE_CCTL_INFO_W1_ALL); - h2c->w1 = le32_encode_bits(preld, BE_CCTL_INFO_W2_PRELOAD_ENABLE); + h2c->w2 = le32_encode_bits(preld, BE_CCTL_INFO_W2_PRELOAD_ENABLE); h2c->m2 = cpu_to_le32(BE_CCTL_INFO_W2_ALL); h2c->m3 = cpu_to_le32(BE_CCTL_INFO_W3_ALL); From 2c0810030cf8a6a04d59cecff4a79d506da177c8 Mon Sep 17 00:00:00 2001 From: Panagiotis Petrakopoulos Date: Sat, 13 Jun 2026 01:30:12 +0300 Subject: [PATCH 008/403] wifi: rtw89: use str_enable_disable() helper Replace "enable"/"disable" strings in ternary expressions with the str_enable_disable() helper from . This covers the rfkill state log in rtw89_core_rfkill_poll() and the DPK on/off log in _dpk_onoff(). No functional change intended. Signed-off-by: Panagiotis Petrakopoulos Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260612223012.504886-1-npetrakopoulos2003@gmail.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 68dad6090f87..26b744dfbcf8 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -7266,7 +7266,7 @@ void rtw89_core_rfkill_poll(struct rtw89_dev *rtwdev, bool force) return; rtw89_info(rtwdev, "rfkill hardware state changed to %s\n", - blocked ? "disable" : "enable"); + str_enable_disable(!blocked)); if (blocked) set_bit(RTW89_FLAG_HW_RFKILL_STATE, rtwdev->flags); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c index e574a9950a3b..a6c7f59223ef 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c @@ -1874,7 +1874,7 @@ static void _dpk_onoff(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, 0xf0000000, val); rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] DPK %s !!!\n", path, - kidx, val == 0 ? "disable" : "enable"); + kidx, str_enable_disable(val)); } static void _dpk_init(struct rtw89_dev *rtwdev, enum rtw89_rf_path path) From 000e63e67a78456bed43a8d681ff83e8f36ad1af Mon Sep 17 00:00:00 2001 From: Chen Jung Ku Date: Sun, 14 Jun 2026 01:04:34 +0800 Subject: [PATCH 009/403] wifi: rtw88: 8822c: replace msleep() with fsleep() for DPK delays Replace msleep() with fsleep(), because msleep() may oversleep to as much as 20 ms when used for a 10 ms delay. According to the kernel documentation, fsleep() is more suitable and aligns better with modern kernel style. Signed-off-by: Chen Jung Ku Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260613170434.23645-1-ku.loong@gapp.nthu.edu.tw --- drivers/net/wireless/realtek/rtw88/rtw8822c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index 80c9f0c11e5c..c3220c98f08a 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -3405,7 +3405,7 @@ static u8 rtw8822c_dpk_one_shot(struct rtw_dev *rtwdev, u8 path, u8 action) rtw_write32_mask(rtwdev, REG_DPD_CTL0, BIT(12), 0x1); rtw_write32_mask(rtwdev, REG_DPD_CTL0, BIT(12), 0x0); rtw_write32_mask(rtwdev, REG_RXSRAM_CTL, BIT_RPT_SEL, 0x0); - msleep(10); + fsleep(10000); if (!check_hw_ready(rtwdev, REG_STAT_RPT, BIT(31), 0x1)) { result = 1; rtw_dbg(rtwdev, RTW_DBG_RFK, "[DPK] one-shot over 20ms\n"); @@ -3418,7 +3418,7 @@ static u8 rtw8822c_dpk_one_shot(struct rtw_dev *rtwdev, u8 path, u8 action) dpk_cmd = rtw8822c_dpk_get_cmd(rtwdev, action, path); rtw_write32(rtwdev, REG_NCTL0, dpk_cmd); rtw_write32(rtwdev, REG_NCTL0, dpk_cmd + 1); - msleep(10); + fsleep(10000); if (!check_hw_ready(rtwdev, 0x2d9c, 0xff, 0x55)) { result = 1; rtw_dbg(rtwdev, RTW_DBG_RFK, "[DPK] one-shot over 20ms\n"); From e779df4806cd29cbcca5c9dc0a1073662c76b889 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 17 Jun 2026 09:35:02 +0800 Subject: [PATCH 010/403] wifi: rtw88: pci: fix resource leak on failed NAPI setup rtw_pci_probe() allocates PCI resources through rtw_pci_setup_resource() before it sets up NAPI. If rtw_pci_napi_init() fails, the error path jumps straight to err_pci_declaim and skips rtw_pci_destroy(), leaving the PCI resources allocated by rtw_pci_setup_resource() behind. Add a dedicated cleanup label for the NAPI setup failure path so probe destroys the PCI resources. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing current mainline kernels. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc7. An x86_64 allyesconfig build showed no new warnings. As we do not have a suitable rtw88 PCI board to test with, no runtime testing was able to be performed. Fixes: d0bcb10e7b94 ("wifi: rtw88: Un-embed dummy device") Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260617013502.114057-1-dawei.feng@seu.edu.cn --- drivers/net/wireless/realtek/rtw88/pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index a30467228912..69f2840fed09 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1834,7 +1834,7 @@ int rtw_pci_probe(struct pci_dev *pdev, ret = rtw_pci_napi_init(rtwdev); if (ret) { rtw_err(rtwdev, "failed to setup NAPI\n"); - goto err_pci_declaim; + goto err_destroy_rsrc; } ret = rtw_chip_info_setup(rtwdev); @@ -1866,6 +1866,8 @@ int rtw_pci_probe(struct pci_dev *pdev, err_destroy_pci: rtw_pci_napi_deinit(rtwdev); + +err_destroy_rsrc: rtw_pci_destroy(rtwdev, pdev); err_pci_declaim: From ed4f05d9f2f42fd866f55108db8123eefcc5fb33 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Sat, 20 Jun 2026 10:56:32 +0800 Subject: [PATCH 011/403] wifi: rtlwifi: rtl8192du: check QoS TID before indexing tids rtl92du_tx_fill_desc() uses ieee80211_get_tid() to read the QoS TID from the 802.11 header and then uses it as an index into sta_entry->tids[]. ieee80211_get_tid() returns the low 4-bit QoS TID value, so the result can be in the range 0..15. rtlwifi only allocates MAX_TID_COUNT entries for sta_entry->tids[], and MAX_TID_COUNT is 9. A QoS TID greater than 8 therefore indexes past the aggregation state array. Keep the default RTL_AGG_STOP state for out-of-range TIDs, matching rtl92cu_tx_fill_desc(). This issue was detected by our static analysis tool and confirmed by manual audit. UBSAN validation for the same bug pattern reports an array-index-out-of-bounds access with index 10 for type 'rtl_tid_data [9]'. Fixes: 8321424134a4 ("wifi: rtlwifi: Add rtl8192du/trx.{c,h}") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260620025632.46206-1-runyu.xiao@seu.edu.cn --- drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c index 743ce0cfffe6..c608c51f1b78 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c @@ -106,7 +106,8 @@ void rtl92du_tx_fill_desc(struct ieee80211_hw *hw, if (sta) { sta_entry = (struct rtl_sta_info *)sta->drv_priv; tid = ieee80211_get_tid(hdr); - agg_state = sta_entry->tids[tid].agg.agg_state; + if (tid < MAX_TID_COUNT) + agg_state = sta_entry->tids[tid].agg.agg_state; ampdu_density = sta->deflink.ht_cap.ampdu_density; } From 1349ed8f104a77964a907d52f25d91fadeb9ce3b Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 22 Jun 2026 09:54:39 +0800 Subject: [PATCH 012/403] wifi: rtl8xxxu: 8723bu: remove reference of non-existing firmware rtl8723bu_bt.bin A report from [1] that firmware is missing in linux-firmware repository. However, there is no specific firmware for RTL8723BU for Bluetooth enabled. Remove the unnecessary reference of firmware file. [1] https://github.com/rtlwifi-linux/rtlwifi-next/issues/20 Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260622015439.9621-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtl8xxxu/8723b.c | 11 +---------- drivers/net/wireless/realtek/rtl8xxxu/core.c | 1 - 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c index e314ef991b38..24c6d8ae76ec 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c @@ -483,16 +483,7 @@ static int rtl8723bu_parse_efuse(struct rtl8xxxu_priv *priv) static int rtl8723bu_load_firmware(struct rtl8xxxu_priv *priv) { - const char *fw_name; - int ret; - - if (priv->enable_bluetooth) - fw_name = "rtlwifi/rtl8723bu_bt.bin"; - else - fw_name = "rtlwifi/rtl8723bu_nic.bin"; - - ret = rtl8xxxu_load_firmware(priv, fw_name); - return ret; + return rtl8xxxu_load_firmware(priv, "rtlwifi/rtl8723bu_nic.bin"); } static void rtl8723bu_init_phy_bb(struct rtl8xxxu_priv *priv) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 646fe76b086e..4e8a4769603c 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -37,7 +37,6 @@ MODULE_FIRMWARE("rtlwifi/rtl8192cufw_B.bin"); MODULE_FIRMWARE("rtlwifi/rtl8192cufw_TMSC.bin"); MODULE_FIRMWARE("rtlwifi/rtl8192eu_nic.bin"); MODULE_FIRMWARE("rtlwifi/rtl8723bu_nic.bin"); -MODULE_FIRMWARE("rtlwifi/rtl8723bu_bt.bin"); MODULE_FIRMWARE("rtlwifi/rtl8188fufw.bin"); MODULE_FIRMWARE("rtlwifi/rtl8710bufw_SMIC.bin"); MODULE_FIRMWARE("rtlwifi/rtl8710bufw_UMC.bin"); From ed51a86b787f6e1008aefc58aca3491ee079aef0 Mon Sep 17 00:00:00 2001 From: "Bitterblue Smith (S.E.A. Datentechnik GmbH)" Date: Mon, 22 Jun 2026 20:02:44 +0300 Subject: [PATCH 013/403] wifi: rtw88: Enable receiving control frames in monitor mode By default RTL8723D, RTL8703B, RTL8812A, RTL8821A, and RTL8814A are configured to filter out all control frames except PS-Poll, even in monitor mode. Handle FIF_CONTROL in rtw_ops_configure_filter(). When it's set, configure REG_RXFLTMAP1 to let all control frames through. When it's unset, restore the original value. Because some drivers configure REG_RXFLTMAP1 differently, keep track of its value in a new member of struct rtw_hal. Signed-off-by: Bitterblue Smith (S.E.A. Datentechnik GmbH) Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/2a52d718-9e46-47f2-84a1-d8e7b1ed89a8@gmail.com --- drivers/net/wireless/realtek/rtw88/bf.c | 6 ++++-- drivers/net/wireless/realtek/rtw88/mac80211.c | 8 +++++++- drivers/net/wireless/realtek/rtw88/main.h | 1 + drivers/net/wireless/realtek/rtw88/rtw8723x.c | 3 ++- drivers/net/wireless/realtek/rtw88/rtw8814a.c | 3 ++- drivers/net/wireless/realtek/rtw88/rtw8821c.c | 4 +++- drivers/net/wireless/realtek/rtw88/rtw8821c.h | 3 ++- drivers/net/wireless/realtek/rtw88/rtw8822b.c | 7 +++++-- drivers/net/wireless/realtek/rtw88/rtw8822c.c | 7 +++++-- drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 3 ++- 10 files changed, 33 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/bf.c b/drivers/net/wireless/realtek/rtw88/bf.c index 0d0ccbc7d00c..7313ecc5c82a 100644 --- a/drivers/net/wireless/realtek/rtw88/bf.c +++ b/drivers/net/wireless/realtek/rtw88/bf.c @@ -137,7 +137,8 @@ void rtw_bf_cfg_sounding(struct rtw_dev *rtwdev, struct rtw_vif *vif, rtw_write8_mask(rtwdev, REG_SND_PTCL_CTRL, BIT_MASK_BEAMFORM, RTW_SND_CTRL_SOUNDING); rtw_write8(rtwdev, REG_SND_PTCL_CTRL + 3, 0x26); - rtw_write8_clr(rtwdev, REG_RXFLTMAP1, BIT_RXFLTMAP1_BF_REPORT_POLL); + rtwdev->hal.rxfltmap1 &= ~BIT_RXFLTMAP1_BF_REPORT_POLL; + rtw_write16(rtwdev, REG_RXFLTMAP1, rtwdev->hal.rxfltmap1); rtw_write8_clr(rtwdev, REG_RXFLTMAP4, BIT_RXFLTMAP4_BF_REPORT_POLL); if (vif->net_type == RTW_NET_AP_MODE) @@ -269,7 +270,8 @@ void rtw_bf_enable_bfee_mu(struct rtw_dev *rtwdev, struct rtw_vif *vif, rtw_write16_set(rtwdev, REG_RXFLTMAP0, BIT_RXFLTMAP0_ACTIONNOACK); /* accept NDPA and BF report poll */ - rtw_write16_set(rtwdev, REG_RXFLTMAP1, BIT_RXFLTMAP1_BF); + rtwdev->hal.rxfltmap1 |= BIT_RXFLTMAP1_BF; + rtw_write16(rtwdev, REG_RXFLTMAP1, rtwdev->hal.rxfltmap1); } EXPORT_SYMBOL(rtw_bf_enable_bfee_mu); diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c index 766f22d31079..b01b98d24b0a 100644 --- a/drivers/net/wireless/realtek/rtw88/mac80211.c +++ b/drivers/net/wireless/realtek/rtw88/mac80211.c @@ -281,12 +281,18 @@ static void rtw_ops_configure_filter(struct ieee80211_hw *hw, struct rtw_dev *rtwdev = hw->priv; *new_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_FCSFAIL | - FIF_BCN_PRBRESP_PROMISC; + FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; mutex_lock(&rtwdev->mutex); rtw_leave_lps_deep(rtwdev); + if (changed_flags & FIF_CONTROL) { + if (*new_flags & FIF_CONTROL) + rtw_write16(rtwdev, REG_RXFLTMAP1, 0xffff); + else + rtw_write16(rtwdev, REG_RXFLTMAP1, rtwdev->hal.rxfltmap1); + } if (changed_flags & FIF_ALLMULTI) { if (*new_flags & FIF_ALLMULTI) rtwdev->hal.rcr |= BIT_AM; diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 9c0b746540b0..c6e981ba7986 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -1963,6 +1963,7 @@ struct rtw_sar { struct rtw_hal { u32 rcr; + u16 rxfltmap1; u32 chip_version; u8 cut_version; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723x.c b/drivers/net/wireless/realtek/rtw88/rtw8723x.c index 3f3e9b0c44e8..97e6f6a62c0d 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723x.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8723x.c @@ -356,7 +356,8 @@ static int __rtw8723x_mac_init(struct rtw_dev *rtwdev) rtw_write32(rtwdev, REG_TCR, BIT_TCR_CFG); rtw_write16(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); - rtw_write16(rtwdev, REG_RXFLTMAP1, WLAN_RX_FILTER1); + rtwdev->hal.rxfltmap1 = WLAN_RX_FILTER1; + rtw_write16(rtwdev, REG_RXFLTMAP1, rtwdev->hal.rxfltmap1); rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2); rtw_write32(rtwdev, REG_RCR, WLAN_RCR_CFG); diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c index ca1079e12023..fa02aa299b5e 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8814a.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c @@ -400,7 +400,8 @@ static int rtw8814a_mac_init(struct rtw_dev *rtwdev) rtw_write16(rtwdev, REG_RETRY_LIMIT, 0x3030); rtw_write16(rtwdev, REG_RXFLTMAP0, 0xffff); - rtw_write16(rtwdev, REG_RXFLTMAP1, 0x0400); + rtwdev->hal.rxfltmap1 = 0x0400; + rtw_write16(rtwdev, REG_RXFLTMAP1, rtwdev->hal.rxfltmap1); rtw_write16(rtwdev, REG_RXFLTMAP2, 0xffff); rtw_write8(rtwdev, REG_MAX_AGGR_NUM, 0x36); diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c index 246046da4f13..ee689fa3d87f 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c @@ -248,7 +248,9 @@ static int rtw8821c_mac_init(struct rtw_dev *rtwdev) rtw_write8_clr(rtwdev, REG_TX_PTCL_CTRL + 1, BIT_SIFS_BK_EN >> 8); /* WMAC configuration */ - rtw_write32(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); + rtw_write16(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); + rtwdev->hal.rxfltmap1 = WLAN_RX_FILTER1; + rtw_write16(rtwdev, REG_RXFLTMAP1, rtwdev->hal.rxfltmap1); rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2); rtw_write32(rtwdev, REG_RCR, WLAN_RCR_CFG); rtw_write8(rtwdev, REG_RX_PKT_LIMIT, WLAN_RXPKT_MAX_SZ_512); diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.h b/drivers/net/wireless/realtek/rtw88/rtw8821c.h index 954e93c8020d..ea85b7e73050 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.h +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.h @@ -139,7 +139,8 @@ extern const struct rtw_chip_info rtw8821c_hw_spec; #define WLAN_DRV_EARLY_INT 0x04 #define WLAN_BCN_DMA_TIME 0x02 -#define WLAN_RX_FILTER0 0x0FFFFFFF +#define WLAN_RX_FILTER0 0xFFFF +#define WLAN_RX_FILTER1 0x0FFF #define WLAN_RX_FILTER2 0xFFFF #define WLAN_RCR_CFG 0xE400220E #define WLAN_RXPKT_MAX_SZ 12288 diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index 37b7a520fea0..a56c8befa077 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -202,7 +202,8 @@ static void rtw8822b_phy_set_param(struct rtw_dev *rtwdev) #define WLAN_DRV_EARLY_INT 0x04 #define WLAN_BCN_DMA_TIME 0x02 -#define WLAN_RX_FILTER0 0x0FFFFFFF +#define WLAN_RX_FILTER0 0xFFFF +#define WLAN_RX_FILTER1 0x0FFF #define WLAN_RX_FILTER2 0xFFFF #define WLAN_RCR_CFG 0xE400220E #define WLAN_RXPKT_MAX_SZ 12288 @@ -273,7 +274,9 @@ static int rtw8822b_mac_init(struct rtw_dev *rtwdev) rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME); rtw_write8_clr(rtwdev, REG_TX_PTCL_CTRL + 1, BIT_SIFS_BK_EN >> 8); /* WMAC configuration */ - rtw_write32(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); + rtw_write16(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); + rtwdev->hal.rxfltmap1 = WLAN_RX_FILTER1; + rtw_write16(rtwdev, REG_RXFLTMAP1, rtwdev->hal.rxfltmap1); rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2); rtw_write32(rtwdev, REG_RCR, WLAN_RCR_CFG); rtw_write8(rtwdev, REG_RX_PKT_LIMIT, WLAN_RXPKT_MAX_SZ_512); diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index c3220c98f08a..32d9709f2c24 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -1942,7 +1942,8 @@ static void rtw8822c_phy_set_param(struct rtw_dev *rtwdev) #define WLAN_EDCA_BE_PARAM 0x005EA42B #define WLAN_EDCA_BK_PARAM 0x0000A44F -#define WLAN_RX_FILTER0 0xFFFFFFFF +#define WLAN_RX_FILTER0 0xFFFF +#define WLAN_RX_FILTER1 0xFFFF #define WLAN_RX_FILTER2 0xFFFF #define WLAN_RCR_CFG 0xE400220E #define WLAN_RXPKT_MAX_SZ 12288 @@ -2093,7 +2094,9 @@ static int rtw8822c_mac_init(struct rtw_dev *rtwdev) rtw_write16(rtwdev, REG_EIFS, WLAN_EIFS_DUR_TUNE); rtw_write8(rtwdev, REG_NAV_CTRL + 2, WLAN_NAV_MAX); rtw_write8(rtwdev, REG_WMAC_TRXPTCL_CTL_H + 2, WLAN_BAR_ACK_TYPE); - rtw_write32(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); + rtw_write16(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); + rtwdev->hal.rxfltmap1 = WLAN_RX_FILTER1; + rtw_write16(rtwdev, REG_RXFLTMAP1, rtwdev->hal.rxfltmap1); rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2); rtw_write32(rtwdev, REG_RCR, WLAN_RCR_CFG); rtw_write8(rtwdev, REG_RX_PKT_LIMIT, WLAN_RXPKT_MAX_SZ_512); diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c index 0fa943271fb6..2eaadcfec4cb 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c +++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c @@ -512,7 +512,8 @@ static int rtw88xxau_init_queue_priority(struct rtw_dev *rtwdev) static void rtw88xxa_init_wmac_setting(struct rtw_dev *rtwdev) { rtw_write16(rtwdev, REG_RXFLTMAP0, 0xffff); - rtw_write16(rtwdev, REG_RXFLTMAP1, 0x0400); + rtwdev->hal.rxfltmap1 = 0x0400; + rtw_write16(rtwdev, REG_RXFLTMAP1, rtwdev->hal.rxfltmap1); rtw_write16(rtwdev, REG_RXFLTMAP2, 0xffff); rtw_write32(rtwdev, REG_MAR, 0xffffffff); From 04e91bb237a438eb562c686b5aa85445ee3df7cd Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 24 Jun 2026 11:39:32 +0800 Subject: [PATCH 014/403] wifi: rtw89: coex: force to exit Wi-Fi LPS while Bluetooth profile exist Wi-Fi can not reach LPS leave threshold while Wi-Fi only throughput not good & Bluetooth share bandwidth. Add logic to let force leave Wi-Fi LPS while Bluetooth profile exist. Update COEX version to 9.0.1. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260624033941.45918-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 87 +++++++++++++++++------ drivers/net/wireless/realtek/rtw89/core.h | 4 ++ 2 files changed, 69 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 0f7ae572ef91..73f271a7ae7a 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -11,7 +11,7 @@ #include "ps.h" #include "reg.h" -#define RTW89_COEX_VERSION 0x09000013 +#define RTW89_COEX_VERSION 0x09000113 #define FCXDEF_STEP 50 /* MUST <= FCXMAX_STEP and match with wl fw*/ #define BTC_E2G_LIMIT_DEF 80 @@ -434,6 +434,8 @@ enum btc_b2w_scoreboard { BTC_BSCB_WLRFK = BIT(11), BTC_BSCB_BT_HILNA = BIT(13), BTC_BSCB_BT_CONNECT = BIT(16), + BTC_BSCB_PAN_ACT = BIT(28), + BTC_BSCB_HFP_ACT = BIT(29), BTC_BSCB_PATCH_CODE = BIT(30), BTC_BSCB_ALL = GENMASK(30, 0), }; @@ -2747,7 +2749,9 @@ static void _fw_set_policy(struct rtw89_dev *rtwdev, u16 policy_type, dm->tdma.rxflctrl == CXFLC_QOSNULL) btc->lps = 1; else - btc->lps = 0; + btc->lps = dm->lps_ctrl_scbd; + + dm->lps_ctrl_scbd_last = dm->lps_ctrl_scbd; if (btc->lps == 1) rtw89_set_coex_ctrl_lps(rtwdev, btc->lps); @@ -4434,7 +4438,7 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) if (dm->leak_ap && dm->tdma.leak_n > 1) _tdma_set_lek(btc, 1); - if (dm->tdma_instant_excute) { + if (dm->tdma_instant_excute || dm->lps_ctrl_change) { btc->dm.tdma.option_ctrl |= BIT(0); btc->update_policy_force = true; } @@ -5638,7 +5642,8 @@ static void _action_common(struct rtw89_dev *rtwdev) _fw_set_drv_info(rtwdev, CXDRVINFO_OSI); } } - btc->dm.tdma_instant_excute = 0; + dm->tdma_instant_excute = 0; + dm->lps_ctrl_change = false; wl->pta_reg_mac_chg = false; } @@ -7384,11 +7389,15 @@ void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work) static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) { const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_btc_ver *ver = rtwdev->btc.ver; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_bt_info *bt = &btc->cx.bt; - u32 val; - bool status_change = false; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_dm *dm = &rtwdev->btc.dm; + bool bt_link_change = false, lps_ctrl = false; + u32 val, any_bt_connect; + u8 mode; if (!chip->scbd) return; @@ -7403,13 +7412,26 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) return; } + if (ver->fwlrole == 0) + mode = wl->role_info.link_mode; + else if (ver->fwlrole == 1) + mode = wl->role_info_v1.link_mode; + else if (ver->fwlrole == 2) + mode = wl->role_info_v2.link_mode; + else if (ver->fwlrole == 7) + mode = wl->role_info_v7.link_mode; + else if (ver->fwlrole == 8) + mode = wl->role_info_v8.link_mode; + else + return; + if (!(val & BTC_BSCB_ON)) bt->enable.now = 0; else bt->enable.now = 1; if (bt->enable.now != bt->enable.last) - status_change = true; + bt_link_change = true; /* reset bt info if bt re-enable */ if (bt->enable.now && !bt->enable.last) { @@ -7423,29 +7445,52 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) bt->mbx_avl = !!(val & BTC_BSCB_ACT); if (bt->whql_test != !!(val & BTC_BSCB_WHQL)) - status_change = true; + bt_link_change = true; bt->whql_test = !!(val & BTC_BSCB_WHQL); bt->btg_type = val & BTC_BSCB_BT_S1 ? BTC_BT_BTG : BTC_BT_ALONE; bt->link_info.a2dp_desc.exist = !!(val & BTC_BSCB_A2DP_ACT); + bt->link_info.pan_desc.exist = !!(val & BTC_BSCB_PAN_ACT); + bt->link_info.hfp_desc.exist = !!(val & BTC_BSCB_HFP_ACT); bt->lna_constrain = !!(val & BTC_BSCB_BT_LNAB0) + !!(val & BTC_BSCB_BT_LNAB1) * 2 + 4; /* if rfk run 1->0 */ if (bt->rfk_info.map.run && !(val & BTC_BSCB_RFK_RUN)) - status_change = true; + bt_link_change = true; bt->rfk_info.map.run = !!(val & BTC_BSCB_RFK_RUN); bt->rfk_info.map.req = !!(val & BTC_BSCB_RFK_REQ); bt->hi_lna_rx = !!(val & BTC_BSCB_BT_HILNA); - bt->link_info.status.map.connect = !!(val & BTC_BSCB_BT_CONNECT); - if (bt->run_patch_code != !!(val & BTC_BSCB_PATCH_CODE)) - status_change = true; + any_bt_connect = !!(val & BTC_BSCB_BT_CONNECT); + + /* if connect change */ + if (bt->link_info.status.map.connect != any_bt_connect) + bt_link_change = true; + + /* if specific profile exist */ + if (((bt->link_info.a2dp_desc.exist || bt->link_info.pan_desc.exist || + bt->link_info.hfp_desc.exist) && mode == BTC_WLINK_2G_STA) || + bt->whql_test) + lps_ctrl = true; + + if (dm->lps_ctrl_scbd != lps_ctrl) { + dm->lps_ctrl_scbd = lps_ctrl; + bt_link_change = true; + dm->lps_ctrl_change = true; + } else { + dm->lps_ctrl_change = false; + } + + bt->link_info.status.map.connect = any_bt_connect; bt->run_patch_code = !!(val & BTC_BSCB_PATCH_CODE); - if (!only_update && status_change) - _run_coex(rtwdev, BTC_RSN_UPDATE_BT_SCBD); + if (bt_link_change) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s: bt status change!!\n", __func__); + /* TODO: Need to notify driver to update EXT-CTRL-BT-SLOT */ + } } #define BTC_BTINFO_PWR_LEN 5 @@ -7560,7 +7605,8 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) } if (wl->status.map.rf_off_pre == wl->status.map.rf_off && - wl->status.map.lps_pre == wl->status.map.lps) { + wl->status.map.lps_pre == wl->status.map.lps && + !dm->lps_ctrl_change && !dm->lps_ctrl_scbd) { if (reason == BTC_RSN_NTFY_POWEROFF || reason == BTC_RSN_NTFY_RADIO_STATE) { rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -7577,6 +7623,9 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) } } + if (reason == BTC_RSN_NTFY_INIT || reason == BTC_RSN_NTFY_RADIO_STATE) + _update_bt_scbd(rtwdev, false); + dm->freerun = false; dm->cnt_dm[BTC_DCNT_RUN]++; dm->fddt_train = BTC_FDDT_DISABLE; @@ -7597,7 +7646,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) goto exit; } - if (wl->status.map.rf_off || wl->status.map.lps || dm->bt_only) { + if (wl->status.map.rf_off || dm->bt_only || wl->status.map.lps) { _action_wl_off(rtwdev, mode); igno_bt = true; goto exit; @@ -7781,7 +7830,6 @@ void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode) _write_scbd(rtwdev, BTC_WSCB_ACTIVE | BTC_WSCB_ON | BTC_WSCB_BTLOG, true); - _update_bt_scbd(rtwdev, true); if (rtw89_mac_get_ctrl_path(rtwdev)) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): PTA owner warning!!\n", @@ -8339,7 +8387,6 @@ void rtw89_btc_ntfy_radio_state(struct rtw89_dev *rtwdev, enum btc_rfctrl rf_sta rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_MREG, true); val = BTC_WSCB_ACTIVE | BTC_WSCB_ON | BTC_WSCB_BTLOG; _write_scbd(rtwdev, val, true); - _update_bt_scbd(rtwdev, true); chip->ops->btc_init_cfg(rtwdev); } else { rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_ALL, false); @@ -8349,10 +8396,6 @@ void rtw89_btc_ntfy_radio_state(struct rtw89_dev *rtwdev, enum btc_rfctrl rf_sta _write_scbd(rtwdev, BTC_WSCB_ALL, false); else _write_scbd(rtwdev, BTC_WSCB_ACTIVE, false); - - if (rf_state == BTC_RFCTRL_LPS_WL_ON && - wl->status.map.lps_pre != BTC_LPS_OFF) - _update_bt_scbd(rtwdev, true); } btc->dm.cnt_dm[BTC_DCNT_BTCNT_HANG] = 0; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 4bf6fe9d3880..920c97df4556 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3153,6 +3153,10 @@ struct rtw89_btc_dm { u8 wl_pre_agc_rb: 2; u8 bt_select: 2; /* 0:s0, 1:s1, 2:s0 & s1, refer to enum btc_bt_index */ u8 slot_req_more: 1; + u8 lps_ctrl_scbd: 1; + + u8 lps_ctrl_scbd_last: 1; + u8 lps_ctrl_change: 1; }; struct rtw89_btc_ctrl { From ce9b6ca8f4ba8cfc111ced2c85fd749f49e95df8 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 24 Jun 2026 11:39:33 +0800 Subject: [PATCH 015/403] wifi: rtw89: coex: offset current BT info to BT0 for dual BT configuration In order to compatible with single/dual Bluetooth structure in one branch, offset the currently using BT info structure to BT-0. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260624033941.45918-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 130 +++++++++++----------- drivers/net/wireless/realtek/rtw89/core.h | 3 +- 2 files changed, 67 insertions(+), 66 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 73f271a7ae7a..f857ba247c23 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -924,7 +924,7 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type) const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; struct rtw89_btc_wl_link_info *wl_linfo; u8 i; @@ -1106,7 +1106,7 @@ static void _chk_btc_err(struct rtw89_dev *rtwdev, u8 type, u32 cnt) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_cx *cx = &btc->cx; - struct rtw89_btc_bt_info *bt = &cx->bt; + struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_dm *dm = &btc->dm; @@ -1276,7 +1276,7 @@ static void _update_bt_report(struct rtw89_dev *rtwdev, u8 rpt_type, u8 *pfinfo) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; struct rtw89_btc_bt_a2dp_desc *a2dp = &bt_linfo->a2dp_desc; union rtw89_btc_fbtc_btver *pver = &btc->fwinfo.rpt_fbtc_btver.finfo; @@ -1417,7 +1417,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; union rtw89_btc_fbtc_rpt_ctrl_ver_info *prpt = NULL; union rtw89_btc_fbtc_cysta_info *pcysta = NULL; struct rtw89_btc_prpt *btc_prpt = NULL; @@ -3079,7 +3079,7 @@ static void _set_wl_rx_gain(struct rtw89_dev *rtwdev, u32 level) static void _set_bt_tx_power(struct rtw89_dev *rtwdev, u8 level) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; int ret; u8 buf; @@ -3106,7 +3106,7 @@ static void _set_bt_tx_power(struct rtw89_dev *rtwdev, u8 level) static void _set_bt_rx_gain(struct rtw89_dev *rtwdev, u8 level) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; if (btc->cx.cnt_bt[BTC_BCNT_INFOUPDATE] == 0) return; @@ -3138,7 +3138,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_bt_link_info *b = &bt->link_info; struct rtw89_btc_wl_smap *wl_smap = &wl->status.map; struct rtw89_btc_rf_trx_para para; @@ -3224,7 +3224,7 @@ static void _update_btc_state_map(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_bt_info *bt = &cx->bt; + struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; if (wl->status.map.connecting || wl->status.map._4way || @@ -3251,7 +3251,7 @@ static void _set_bt_afh_info_v0(struct rtw89_dev *rtwdev) struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_bt_link_info *b = &bt->link_info; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; @@ -3421,7 +3421,7 @@ static void _set_bt_afh_info_v1(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; struct rtw89_btc_wl_afh_info *wl_afh = &wl->afh_info; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_wl_rlink *rlink; u8 en = 0, ch = 0, bw = 0, buf[3] = {}; u8 i, j, link_mode; @@ -3527,7 +3527,7 @@ static bool _check_freerun(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; @@ -4000,9 +4000,9 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_fbtc_tdma *t = &dm->tdma; struct rtw89_btc_wl_role_info_v1 *wl_rinfo = &btc->cx.wl.role_info_v1; - struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; - struct rtw89_btc_bt_hid_desc *hid = &btc->cx.bt.link_info.hid_desc; - struct rtw89_btc_bt_hfp_desc *hfp = &btc->cx.bt.link_info.hfp_desc; + struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt0.link_info.a2dp_desc; + struct rtw89_btc_bt_hid_desc *hid = &btc->cx.bt0.link_info.hid_desc; + struct rtw89_btc_bt_hfp_desc *hfp = &btc->cx.bt0.link_info.hfp_desc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; u8 type, null_role; u32 tbl_w1, tbl_b1, tbl_b4; @@ -4483,7 +4483,7 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec, struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &cx->bt; + struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; u8 gnt_wl_ctrl, gnt_bt_ctrl, plt_ctrl, i, b2g = 0; bool dbcc_chg = false; @@ -4611,7 +4611,7 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec, { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; u32 ant_path_type = rtw89_get_antpath_type(phy_map, type); struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; @@ -4750,7 +4750,7 @@ static void _action_wl_off(struct rtw89_dev *rtwdev, u8 mode) if (mode == BTC_WLINK_5G) { _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_OFF); } else if (wl->status.map.lps == BTC_LPS_RF_ON) { - if (btc->cx.bt.link_info.a2dp_desc.active) + if (btc->cx.bt0.link_info.a2dp_desc.active) _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_WL_OFF); else _set_policy(rtwdev, BTC_CXP_OFF_BWB1, BTC_ACT_WL_OFF); @@ -4790,7 +4790,7 @@ static void _action_bt_off(struct rtw89_dev *rtwdev) static void _action_bt_idle(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_link_info *b = &btc->cx.bt.link_info; + struct rtw89_btc_bt_link_info *b = &btc->cx.bt0.link_info; struct rtw89_btc_wl_info *wl = &btc->cx.wl; _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); @@ -4838,7 +4838,7 @@ static void _action_bt_hfp(struct rtw89_dev *rtwdev) if (btc->cx.wl.status.map._4way) { _set_policy(rtwdev, BTC_CXP_OFF_WL, BTC_ACT_BT_HFP); } else if (wl->status.map.traffic_dir & BIT(RTW89_TFC_UL)) { - btc->cx.bt.scan_rx_low_pri = true; + btc->cx.bt0.scan_rx_low_pri = true; _set_policy(rtwdev, BTC_CXP_OFF_BWB2, BTC_ACT_BT_HFP); } else { _set_policy(rtwdev, BTC_CXP_OFF_BWB1, BTC_ACT_BT_HFP); @@ -4858,7 +4858,7 @@ static void _action_bt_hid(struct rtw89_dev *rtwdev) const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_bt_hid_desc *hid = &bt->link_info.hid_desc; u16 policy_type = BTC_CXP_OFF_BT; @@ -4868,7 +4868,7 @@ static void _action_bt_hid(struct rtw89_dev *rtwdev) if (wl->status.map._4way) { policy_type = BTC_CXP_OFF_WL; } else if (wl->status.map.traffic_dir & BIT(RTW89_TFC_UL)) { - btc->cx.bt.scan_rx_low_pri = true; + btc->cx.bt0.scan_rx_low_pri = true; if (hid->type & BTC_HID_BLE) policy_type = BTC_CXP_OFF_BWB0; else @@ -4960,7 +4960,7 @@ static void _action_bt_a2dpsink(struct rtw89_dev *rtwdev) static void _action_bt_pan(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; + struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt0.link_info; struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; struct rtw89_btc_bt_pan_desc pan = bt_linfo->pan_desc; @@ -5172,7 +5172,7 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_role_info *wl_rinfo_v0 = &wl->role_info; const struct rtw89_chip_info *chip = rtwdev->chip; const struct rtw89_btc_ver *ver = btc->ver; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; struct _wl_rinfo_now wl_rinfo; u32 is_btg = BTC_BTGCTRL_DISABLE; @@ -5249,13 +5249,13 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_fbtc_outsrc_set_info *o_info = &btc->dm.ost_info; - struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; + struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt0.link_info; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_role_info_v2 *rinfo_v2 = &wl->role_info_v2; struct rtw89_btc_wl_role_info_v7 *rinfo_v7 = &wl->role_info_v7; struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &wl->role_info_v8; const struct rtw89_chip_info *chip = rtwdev->chip; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; u8 is_preagc, val, link_mode, dbcc_2g_phy; u8 role_ver = rtwdev->btc.ver->fwlrole; @@ -5285,7 +5285,7 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev) } else if (link_mode == BTC_WLINK_5G) { is_preagc = BTC_PREAGC_DISABLE; } else if (link_mode == BTC_WLINK_NOLINK || - btc->cx.bt.link_info.profile_cnt.now == 0) { + btc->cx.bt0.link_info.profile_cnt.now == 0) { is_preagc = BTC_PREAGC_DISABLE; } else if (dm->tdma_now.type != CXTDMA_OFF && !bt_linfo->hfp_desc.exist && @@ -5435,7 +5435,7 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev) struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &cx->wl; - struct rtw89_btc_bt_info *bt = &cx->bt; + struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_btc_bt_link_info *b = &bt->link_info; struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc; struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc; @@ -5522,7 +5522,7 @@ static void _set_bt_rx_agc(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7; struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; bool bt_hi_lna_rx = false; u8 mode; @@ -5551,7 +5551,7 @@ static void _set_bt_rx_agc(struct rtw89_dev *rtwdev) static void _set_bt_rx_scan_pri(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; _write_scbd(rtwdev, BTC_WSCB_RXSCAN_PRI, (bool)(!!bt->scan_rx_low_pri)); } @@ -5590,7 +5590,7 @@ static void _action_common(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &wl->role_info_v8; struct rtw89_btc_wl_smap *wl_smap = &wl->status.map; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; u32 bt_rom_code_id, bt_fw_ver; @@ -5650,7 +5650,7 @@ static void _action_common(struct rtw89_dev *rtwdev) static void _action_by_bt(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc; struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; @@ -5747,7 +5747,7 @@ static void _action_wl_25g_mcc(struct rtw89_dev *rtwdev) policy_type = BTC_CXP_OFFE_WL; else if (btc->cx.wl.status.val & btc_scanning_map.val) policy_type = BTC_CXP_OFFE_2GBWMIXB; - else if (btc->cx.bt.link_info.status.map.connect == 0) + else if (btc->cx.bt0.link_info.status.map.connect == 0) policy_type = BTC_CXP_OFFE_2GISOB; else policy_type = BTC_CXP_OFFE_2GBWISOB; @@ -5791,7 +5791,7 @@ static void _action_wl_2g_mcc(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); if (btc->ant_type == BTC_ANT_SHARED) { /* shared-antenna */ - if (btc->cx.bt.link_info.profile_cnt.now == 0) + if (btc->cx.bt0.link_info.profile_cnt.now == 0) _set_policy(rtwdev, BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_MCC); else @@ -5809,7 +5809,7 @@ static void _action_wl_2g_scc(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); if (btc->ant_type == BTC_ANT_SHARED) { /* shared-antenna */ - if (btc->cx.bt.link_info.profile_cnt.now == 0) + if (btc->cx.bt0.link_info.profile_cnt.now == 0) _set_policy(rtwdev, BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_SCC); else @@ -5824,7 +5824,7 @@ static void _action_wl_2g_scc_v1(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_role_info_v1 *wl_rinfo = &wl->role_info_v1; u16 policy_type = BTC_CXP_OFF_BT; @@ -5886,7 +5886,7 @@ static void _action_wl_2g_scc_v2(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_role_info_v2 *rinfo_v2 = &wl->role_info_v2; struct rtw89_btc_wl_role_info_v7 *rinfo_v7 = &wl->role_info_v7; @@ -5959,7 +5959,7 @@ static void _action_wl_2g_scc_v8(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; u16 policy_type = BTC_CXP_OFF_BT; @@ -5987,7 +5987,7 @@ static void _action_wl_2g_ap(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); if (btc->ant_type == BTC_ANT_SHARED) { - if (btc->cx.bt.link_info.profile_cnt.now == 0) + if (btc->cx.bt0.link_info.profile_cnt.now == 0) _set_policy(rtwdev, BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_AP); else @@ -6004,7 +6004,7 @@ static void _action_wl_2g_go(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); if (btc->ant_type == BTC_ANT_SHARED) { /* shared-antenna */ - if (btc->cx.bt.link_info.profile_cnt.now == 0) + if (btc->cx.bt0.link_info.profile_cnt.now == 0) _set_policy(rtwdev, BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_GO); else @@ -6035,7 +6035,7 @@ static void _action_wl_2g_nan(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); if (btc->ant_type == BTC_ANT_SHARED) { /* shared-antenna */ - if (btc->cx.bt.link_info.profile_cnt.now == 0) + if (btc->cx.bt0.link_info.profile_cnt.now == 0) _set_policy(rtwdev, BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_NAN); else @@ -7352,7 +7352,7 @@ void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work) coex_bt_devinfo_work.work); struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &rtwdev->btc.dm; - struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; + struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt0.link_info.a2dp_desc; lockdep_assert_wiphy(wiphy); @@ -7392,7 +7392,7 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) const struct rtw89_btc_ver *ver = rtwdev->btc.ver; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_cx *cx = &btc->cx; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_dm *dm = &rtwdev->btc.dm; bool bt_link_change = false, lps_ctrl = false; @@ -7496,7 +7496,7 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) #define BTC_BTINFO_PWR_LEN 5 static void _update_bt_txpwr_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) { - struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt; + struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt0; struct rtw89_btc_bt_link_info *b = &bt->link_info; if (len != BTC_BTINFO_PWR_LEN) @@ -7516,7 +7516,7 @@ static bool _chk_wl_rfk_request(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_cx *cx = &btc->cx; - struct rtw89_btc_bt_info *bt = &cx->bt; + struct rtw89_btc_bt_info *bt = &cx->bt0; _update_bt_scbd(rtwdev, true); @@ -7540,7 +7540,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; @@ -7657,12 +7657,12 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) goto exit; } - if (!cx->bt.enable.now && !cx->other.type) { + if (!cx->bt0.enable.now && !cx->other.type) { _action_bt_off(rtwdev); goto exit; } - if (cx->bt.whql_test) { + if (cx->bt0.whql_test) { _action_bt_whql(rtwdev); goto exit; } @@ -7925,7 +7925,7 @@ void rtw89_btc_ntfy_specific_packet(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_bt_link_info *b = &cx->bt.link_info; + struct rtw89_btc_bt_link_info *b = &cx->bt0.link_info; struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc; struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc; u32 cnt; @@ -8030,7 +8030,7 @@ static u8 _update_bt_rssi_level(struct rtw89_dev *rtwdev, u8 rssi) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; u8 *rssi_st, rssi_th, rssi_level = 0; u8 i; @@ -8080,7 +8080,7 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_cx *cx = &btc->cx; - struct rtw89_btc_bt_info *bt = &cx->bt; + struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_btc_bt_link_info *b = &bt->link_info; struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc; struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc; @@ -8851,7 +8851,7 @@ static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_wl_info *wl = &btc->cx.wl; u32 ver_main = 0, ver_sub = 0, ver_hotfix = 0, id_branch = 0; u8 cv, rfe, iso, ant_num, ant_single_pos; @@ -9050,7 +9050,7 @@ enum btc_bt_a2dp_type { static int _show_bt_profile_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; + struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt0.link_info; struct rtw89_btc_bt_hfp_desc hfp = bt_linfo->hfp_desc; struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc; struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; @@ -9108,7 +9108,7 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_cx *cx = &btc->cx; - struct rtw89_btc_bt_info *bt = &cx->bt; + struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_btc_wl_info *wl = &cx->wl; u32 ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw_coex); struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; @@ -9631,7 +9631,7 @@ static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; char *p = buf, *end = buf + bufsz; u8 igno_bt; @@ -9853,7 +9853,7 @@ static int _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; + struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt0.link_info.a2dp_desc; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_fbtc_cysta_v2 *pcysta_le32 = NULL; union rtw89_btc_fbtc_rxflct r; @@ -9984,7 +9984,7 @@ static int _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz static int _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; + struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt0.link_info.a2dp_desc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_fbtc_a2dp_trx_stat *a2dp_trx; @@ -10123,7 +10123,7 @@ static int _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, char *buf, size_t bufsz static int _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; + struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt0.link_info.a2dp_desc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_fbtc_a2dp_trx_stat_v4 *a2dp_trx; @@ -10262,7 +10262,7 @@ static int _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz static int _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; + struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt0.link_info.a2dp_desc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_fbtc_a2dp_trx_stat_v4 *a2dp_trx; @@ -10399,7 +10399,7 @@ static int _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz static int _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { - struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt; + struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt0; struct rtw89_btc_bt_a2dp_desc *a2dp = &bt->link_info.a2dp_desc; struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo; struct rtw89_btc_fbtc_cysta_v7 *pcysta = NULL; @@ -10898,7 +10898,7 @@ static int _show_mreg_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_fbtc_mreg_val_v1 *pmreg = NULL; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_mac_ax_coex_gnt gnt_cfg = {}; struct rtw89_mac_ax_gnt gnt; char *p = buf, *end = buf + bufsz; @@ -10983,7 +10983,7 @@ static int _show_mreg_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_fbtc_mreg_val_v2 *pmreg = NULL; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_mac_ax_coex_gnt gnt_cfg = {}; struct rtw89_mac_ax_gnt gnt; char *p = buf, *end = buf + bufsz; @@ -11068,7 +11068,7 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &cx->wl; - struct rtw89_btc_bt_info *bt = &cx->bt; + struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_mac_ax_gnt *gnt = NULL; struct rtw89_btc_dm *dm = &btc->dm; char *p = buf, *end = buf + bufsz; @@ -11146,7 +11146,7 @@ static int _show_summary_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &cx->wl; - struct rtw89_btc_bt_info *bt = &cx->bt; + struct rtw89_btc_bt_info *bt = &cx->bt0; u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; char *p = buf, *end = buf + bufsz; u8 i; @@ -11255,7 +11255,7 @@ static int _show_summary_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &cx->wl; - struct rtw89_btc_bt_info *bt = &cx->bt; + struct rtw89_btc_bt_info *bt = &cx->bt0; u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; char *p = buf, *end = buf + bufsz; u8 i; @@ -11929,7 +11929,7 @@ void rtw89_coex_recognize_ver(struct rtw89_dev *rtwdev) void rtw89_btc_ntfy_preserve_bt_time(struct rtw89_dev *rtwdev, u32 ms) { - struct rtw89_btc_bt_link_info *bt_linfo = &rtwdev->btc.cx.bt.link_info; + struct rtw89_btc_bt_link_info *bt_linfo = &rtwdev->btc.cx.bt0.link_info; struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; if (test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags)) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 920c97df4556..83f66cdb0435 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2262,7 +2262,8 @@ struct rtw89_btc_rf_trx_para_v9 { struct rtw89_btc_cx { struct rtw89_btc_wl_info wl; - struct rtw89_btc_bt_info bt; + struct rtw89_btc_bt_info bt0; + struct rtw89_btc_bt_info bt1; struct rtw89_btc_3rdcx_info other; u32 state_map; u32 cnt_bt[BTC_BCNT_NUM]; From 6ca62c49a679eeaad08c39f76e2c2db7f917e0aa Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 24 Jun 2026 11:39:34 +0800 Subject: [PATCH 016/403] wifi: rtw89: coex: Move wifi related counters to wifi info Move wifi related counters to wifi main info, it is to facilitate the after modification for dual MAC wifi structure. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260624033941.45918-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 80 +++++++++++------------ drivers/net/wireless/realtek/rtw89/core.h | 2 +- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index f857ba247c23..e4662e7b74e0 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -3411,7 +3411,7 @@ static void _set_bt_afh_info_v0(struct rtw89_dev *rtwdev) rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): en=%d, ch=%d, bw=%d\n", __func__, en, ch, bw); - btc->cx.cnt_wl[BTC_WCNT_CH_UPDATE]++; + wl->wcnt[BTC_WCNT_CH_UPDATE]++; } static void _set_bt_afh_info_v1(struct rtw89_dev *rtwdev) @@ -3511,7 +3511,7 @@ static void _set_bt_afh_info_v1(struct rtw89_dev *rtwdev) "[BTC], %s(): en=%d, ch=%d, bw=%d\n", __func__, en, ch, bw); - btc->cx.cnt_wl[BTC_WCNT_CH_UPDATE]++; + wl->wcnt[BTC_WCNT_CH_UPDATE]++; } } @@ -5630,7 +5630,7 @@ static void _action_common(struct rtw89_dev *rtwdev) rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], write scbd: 0x%08x\n", wl->scbd); wl->scbd_change = false; - btc->cx.cnt_wl[BTC_WCNT_SCBDUPDATE]++; + wl->wcnt[BTC_WCNT_SCBDUPDATE]++; } if (btc->ver->fcxosi) { @@ -6877,7 +6877,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid) if (wl_rinfo->dbcc_en != rtwdev->dbcc_en) { wl_rinfo->dbcc_chg = 1; wl_rinfo->dbcc_en = rtwdev->dbcc_en; - btc->cx.cnt_wl[BTC_WCNT_DBCC_CHG]++; + wl->wcnt[BTC_WCNT_DBCC_CHG]++; } if (rtwdev->dbcc_en) { @@ -7321,7 +7321,7 @@ static void _update_wl_info_v8(struct rtw89_dev *rtwdev, u8 role_id, u8 rlink_id if (wl_rinfo->dbcc_en != dbcc_en_ori) { wl->dbcc_chg = true; - btc->cx.cnt_wl[BTC_WCNT_DBCC_CHG]++; + wl->wcnt[BTC_WCNT_DBCC_CHG]++; } } @@ -7378,7 +7378,7 @@ void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work) if (wl->rfk_info.state != BTC_WRFK_STOP) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): RFK timeout\n", __func__); - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]++; + wl->wcnt[BTC_WCNT_RFK_TIMEOUT]++; dm->error.map.wl_rfk_timeout = true; wl->rfk_info.state = BTC_WRFK_STOP; _write_scbd(rtwdev, BTC_WSCB_WLRFK, false); @@ -7520,13 +7520,13 @@ static bool _chk_wl_rfk_request(struct rtw89_dev *rtwdev) _update_bt_scbd(rtwdev, true); - cx->cnt_wl[BTC_WCNT_RFK_REQ]++; + cx->wl.wcnt[BTC_WCNT_RFK_REQ]++; if ((bt->rfk_info.map.run || bt->rfk_info.map.req) && !bt->rfk_info.map.timeout) { - cx->cnt_wl[BTC_WCNT_RFK_REJECT]++; + cx->wl.wcnt[BTC_WCNT_RFK_REJECT]++; } else { - cx->cnt_wl[BTC_WCNT_RFK_GO]++; + cx->wl.wcnt[BTC_WCNT_RFK_GO]++; return true; } return false; @@ -7934,14 +7934,14 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev, switch (pkt_type) { case PACKET_DHCP: - cnt = ++cx->cnt_wl[BTC_WCNT_DHCP]; + cnt = ++wl->wcnt[BTC_WCNT_DHCP]; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): DHCP cnt=%d\n", __func__, cnt); wl->status.map.connecting = true; delay_work = true; break; case PACKET_EAPOL: - cnt = ++cx->cnt_wl[BTC_WCNT_EAPOL]; + cnt = ++wl->wcnt[BTC_WCNT_EAPOL]; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): EAPOL cnt=%d\n", __func__, cnt); wl->status.map._4way = true; @@ -7950,7 +7950,7 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev, delay /= 2; break; case PACKET_EAPOL_END: - cnt = ++cx->cnt_wl[BTC_WCNT_EAPOL]; + cnt = ++wl->wcnt[BTC_WCNT_EAPOL]; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): EAPOL_End cnt=%d\n", __func__, cnt); @@ -7958,7 +7958,7 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev, wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work); break; case PACKET_ARP: - cnt = ++cx->cnt_wl[BTC_WCNT_ARP]; + cnt = ++wl->wcnt[BTC_WCNT_ARP]; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): ARP cnt=%d\n", __func__, cnt); return; @@ -10913,7 +10913,7 @@ static int _show_mreg_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", "[scoreboard]", wl->scbd, - cx->cnt_wl[BTC_WCNT_SCBDUPDATE], + wl->wcnt[BTC_WCNT_SCBDUPDATE], bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); @@ -10998,7 +10998,7 @@ static int _show_mreg_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", "[scoreboard]", wl->scbd, - cx->cnt_wl[BTC_WCNT_SCBDUPDATE], + wl->wcnt[BTC_WCNT_SCBDUPDATE], bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); @@ -11083,7 +11083,7 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, "\n\r %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)", "[scoreboard]", wl->scbd, - cx->cnt_wl[BTC_WCNT_SCBDUPDATE], + wl->wcnt[BTC_WCNT_SCBDUPDATE], bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); @@ -11189,10 +11189,10 @@ static int _show_summary_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", - "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + "[RFK]", wl->wcnt[BTC_WCNT_RFK_REQ], + wl->wcnt[BTC_WCNT_RFK_GO], + wl->wcnt[BTC_WCNT_RFK_REJECT], + wl->wcnt[BTC_WCNT_RFK_TIMEOUT]); p += scnprintf(p, end - p, ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", @@ -11304,10 +11304,10 @@ static int _show_summary_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", - "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + "[RFK]", wl->wcnt[BTC_WCNT_RFK_REQ], + wl->wcnt[BTC_WCNT_RFK_GO], + wl->wcnt[BTC_WCNT_RFK_REJECT], + wl->wcnt[BTC_WCNT_RFK_TIMEOUT]); p += scnprintf(p, end - p, ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", @@ -11418,10 +11418,10 @@ static int _show_summary_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + "[RFK/LPS]", wl->wcnt[BTC_WCNT_RFK_REQ], + wl->wcnt[BTC_WCNT_RFK_GO], + wl->wcnt[BTC_WCNT_RFK_REJECT], + wl->wcnt[BTC_WCNT_RFK_TIMEOUT]); p += scnprintf(p, end - p, ", bt_rfk[req:%d]", @@ -11539,10 +11539,10 @@ static int _show_summary_v105(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + "[RFK/LPS]", wl->wcnt[BTC_WCNT_RFK_REQ], + wl->wcnt[BTC_WCNT_RFK_GO], + wl->wcnt[BTC_WCNT_RFK_REJECT], + wl->wcnt[BTC_WCNT_RFK_TIMEOUT]); p += scnprintf(p, end - p, ", bt_rfk[req:%d]", @@ -11664,10 +11664,10 @@ static int _show_summary_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], + "[RFK/LPS]", wl->wcnt[BTC_WCNT_RFK_REQ], + wl->wcnt[BTC_WCNT_RFK_GO], + wl->wcnt[BTC_WCNT_RFK_REJECT], + wl->wcnt[BTC_WCNT_RFK_TIMEOUT], wl->rfk_info.proc_time); p += scnprintf(p, end - p, ", bt_rfk[req:%d]", @@ -11777,10 +11777,10 @@ static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], + "[RFK/LPS]", wl->wcnt[BTC_WCNT_RFK_REQ], + wl->wcnt[BTC_WCNT_RFK_GO], + wl->wcnt[BTC_WCNT_RFK_REJECT], + wl->wcnt[BTC_WCNT_RFK_TIMEOUT], wl->rfk_info.proc_time); p += scnprintf(p, end - p, ", bt_rfk[req:%d]", diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 83f66cdb0435..5bff68f53cb5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2078,6 +2078,7 @@ struct rtw89_btc_wl_info { bool link_mode_chg; bool dbcc_chg; u32 scbd; + u32 wcnt[BTC_WCNT_NUM]; }; struct rtw89_btc_module { @@ -2267,7 +2268,6 @@ struct rtw89_btc_cx { struct rtw89_btc_3rdcx_info other; u32 state_map; u32 cnt_bt[BTC_BCNT_NUM]; - u32 cnt_wl[BTC_WCNT_NUM]; }; struct rtw89_btc_fbtc_tdma { From 257cdb2c6e380e30bf61ed4607b46765e60c747c Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 24 Jun 2026 11:39:35 +0800 Subject: [PATCH 017/403] wifi: rtw89: coex: Extend bt_slot_req for dual MAC wifi This variable is for asking driver occupied Bluetooth traffic slot while wifi is running at multi-port mode. Example like station + AP. The time slot is separated by wifi driver under these wifi modes. And to ensure Bluetooth performance, Coex will advice the Bluetooth slot length to driver. And each MAC is able to run multi-port mode, so extend the variable's index. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260624033941.45918-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 24 +++++++++++------------ drivers/net/wireless/realtek/rtw89/coex.h | 2 +- drivers/net/wireless/realtek/rtw89/core.h | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index e4662e7b74e0..659028edccfa 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -975,8 +975,8 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type) } btc->policy_len = 0; - btc->bt_req_len = 0; - + btc->bt_req_len[RTW89_PHY_0] = 0; + btc->bt_req_len[RTW89_PHY_1] = 0; btc->dm.coex_info_map = BTC_COEX_INFO_ALL; btc->dm.wl_tx_limit.tx_time = BTC_MAX_TX_TIME_DEF; btc->dm.wl_tx_limit.tx_retry = BTC_MAX_TX_RETRY_DEF; @@ -1994,10 +1994,10 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, /* Check diff time between real BT slot and EBT/E5G slot */ if (dm->tdma_now.type == CXTDMA_OFF && dm->tdma_now.ext_ctrl == CXECTL_EXT && - btc->bt_req_len != 0) { + btc->bt_req_len[RTW89_PHY_0] != 0) { bt_slot_real = le16_to_cpu(pcysta->v3.cycle_time.tavg[CXT_BT]); - if (btc->bt_req_len > bt_slot_real) { - diff_t = btc->bt_req_len - bt_slot_real; + if (btc->bt_req_len[RTW89_PHY_0] > bt_slot_real) { + diff_t = btc->bt_req_len[RTW89_PHY_0] - bt_slot_real; _chk_btc_err(rtwdev, BTC_DCNT_BT_SLOT_DRIFT, diff_t); } } @@ -2038,11 +2038,11 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, /* Check diff time between real BT slot and EBT/E5G slot */ if (dm->tdma_now.type == CXTDMA_OFF && dm->tdma_now.ext_ctrl == CXECTL_EXT && - btc->bt_req_len != 0) { + btc->bt_req_len[RTW89_PHY_0] != 0) { bt_slot_real = le16_to_cpu(pcysta->v4.cycle_time.tavg[CXT_BT]); - if (btc->bt_req_len > bt_slot_real) { - diff_t = btc->bt_req_len - bt_slot_real; + if (btc->bt_req_len[RTW89_PHY_0] > bt_slot_real) { + diff_t = btc->bt_req_len[RTW89_PHY_0] - bt_slot_real; _chk_btc_err(rtwdev, BTC_DCNT_BT_SLOT_DRIFT, diff_t); } } @@ -2083,7 +2083,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, _chk_btc_err(rtwdev, BTC_DCNT_WL_SLOT_DRIFT, diff_t); /* Check diff time between real BT slot and EBT/E5G slot */ - bt_slot_set = btc->bt_req_len; + bt_slot_set = btc->bt_req_len[RTW89_PHY_0]; bt_slot_real = le16_to_cpu(pcysta->v5.cycle_time.tavg[CXT_BT]); diff_t = 0; if (dm->tdma_now.type == CXTDMA_OFF && @@ -5861,7 +5861,7 @@ static void _action_wl_2g_scc_v1(struct rtw89_dev *rtwdev) dm->wl_scc.ebt_null = 0; policy_type = BTC_CXP_OFFE_2GISOB; } else if (bt->link_info.a2dp_desc.exist && - dur < btc->bt_req_len) { + dur < btc->bt_req_len[RTW89_PHY_0]) { dm->wl_scc.ebt_null = 1; /* tx null at EBT */ policy_type = BTC_CXP_OFFE_2GBWMIXB2; } else if (bt->link_info.a2dp_desc.exist || @@ -5934,7 +5934,7 @@ static void _action_wl_2g_scc_v2(struct rtw89_dev *rtwdev) dm->wl_scc.ebt_null = 0; policy_type = BTC_CXP_OFFE_2GISOB; } else if (bt->link_info.a2dp_desc.exist && - dur < btc->bt_req_len) { + dur < btc->bt_req_len[RTW89_PHY_0]) { dm->wl_scc.ebt_null = 1; /* tx null at EBT */ policy_type = BTC_CXP_OFFE_2GBWMIXB2; } else if (bt->link_info.a2dp_desc.exist || @@ -9694,7 +9694,7 @@ static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) " %-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, dm->wl_tx_limit.tx_time, - dm->wl_tx_limit.tx_retry, btc->bt_req_len, + dm->wl_tx_limit.tx_retry, btc->bt_req_len[RTW89_PHY_0], bt->scan_rx_low_pri); return p - buf; diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index ea2c1e5d70f5..6ac14611607c 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -329,7 +329,7 @@ static inline u16 rtw89_coex_query_bt_req_len(struct rtw89_dev *rtwdev, { struct rtw89_btc *btc = &rtwdev->btc; - return btc->bt_req_len; + return btc->bt_req_len[phy_idx]; } static inline u32 rtw89_get_antpath_type(u8 phy_map, u8 type) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 5bff68f53cb5..0e3110aa867a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3390,7 +3390,7 @@ struct rtw89_btc { struct wiphy_work dhcp_notify_work; struct wiphy_work icmp_notify_work; - u32 bt_req_len; + u32 bt_req_len[RTW89_PHY_NUM]; u8 policy[RTW89_BTC_POLICY_MAXLEN]; u8 ant_type; From 77e219a25501a0c042d9337e538a4c1a7232697c Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 24 Jun 2026 11:39:36 +0800 Subject: [PATCH 018/403] wifi: rtw89: coex: Move Bluetooth related counters to BT info In order to support dual Bluetooth chip, move Bluetooth counters to BT info. Because the two Bluetooth need to collect their own counters. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260624033941.45918-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 163 +++++++++--------- drivers/net/wireless/realtek/rtw89/core.h | 3 +- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 8 +- 3 files changed, 86 insertions(+), 88 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 659028edccfa..8fa51867055b 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1218,7 +1218,7 @@ static void _chk_btc_err(struct rtw89_dev *rtwdev, u8 type, u32 cnt) dm->error.map.slot_no_sync = false; break; case BTC_DCNT_BTTX_HANG: - cnt = cx->cnt_bt[BTC_BCNT_LOPRI_TX]; + cnt = bt->bcnt[BTC_BCNT_LOPRI_TX]; if (cnt == 0 && bt->link_info.slave_role) dm->cnt_dm[BTC_DCNT_BTTX_HANG]++; @@ -1231,10 +1231,10 @@ static void _chk_btc_err(struct rtw89_dev *rtwdev, u8 type, u32 cnt) dm->error.map.bt_tx_hang = false; break; case BTC_DCNT_BTCNT_HANG: - cnt = cx->cnt_bt[BTC_BCNT_HIPRI_RX] + - cx->cnt_bt[BTC_BCNT_HIPRI_TX] + - cx->cnt_bt[BTC_BCNT_LOPRI_RX] + - cx->cnt_bt[BTC_BCNT_LOPRI_TX]; + cnt = bt->bcnt[BTC_BCNT_HIPRI_RX] + + bt->bcnt[BTC_BCNT_HIPRI_TX] + + bt->bcnt[BTC_BCNT_LOPRI_RX] + + bt->bcnt[BTC_BCNT_LOPRI_TX]; if (cnt == 0) dm->cnt_dm[BTC_DCNT_BTCNT_HANG]++; @@ -1723,7 +1723,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, rtwdev->chip->ops->btc_update_bt_cnt(rtwdev); _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); - btc->cx.cnt_bt[BTC_BCNT_POLUT] = + bt->bcnt[BTC_BCNT_POLUT] = rtw89_mac_get_plt_cnt(rtwdev, RTW89_MAC_0); } @@ -1738,15 +1738,15 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, memcpy(&dm->gnt.band[i], &prpt->v4.gnt_val[i], sizeof(dm->gnt.band[i])); - btc->cx.cnt_bt[BTC_BCNT_HIPRI_TX] = + bt->bcnt[BTC_BCNT_HIPRI_TX] = le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_HI_TX]); - btc->cx.cnt_bt[BTC_BCNT_HIPRI_RX] = + bt->bcnt[BTC_BCNT_HIPRI_RX] = le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_HI_RX]); - btc->cx.cnt_bt[BTC_BCNT_LOPRI_TX] = + bt->bcnt[BTC_BCNT_LOPRI_TX] = le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_LO_TX]); - btc->cx.cnt_bt[BTC_BCNT_LOPRI_RX] = + bt->bcnt[BTC_BCNT_LOPRI_RX] = le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_LO_RX]); - btc->cx.cnt_bt[BTC_BCNT_POLUT] = + bt->bcnt[BTC_BCNT_POLUT] = le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_POLLUTED]); _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); @@ -1770,15 +1770,15 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, memcpy(&dm->gnt.band[i], &prpt->v5.gnt_val[i][0], sizeof(dm->gnt.band[i])); - btc->cx.cnt_bt[BTC_BCNT_HIPRI_TX] = + bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_HI_TX]); - btc->cx.cnt_bt[BTC_BCNT_HIPRI_RX] = + bt->bcnt[BTC_BCNT_HIPRI_RX] = le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_HI_RX]); - btc->cx.cnt_bt[BTC_BCNT_LOPRI_TX] = + bt->bcnt[BTC_BCNT_LOPRI_TX] = le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_LO_TX]); - btc->cx.cnt_bt[BTC_BCNT_LOPRI_RX] = + bt->bcnt[BTC_BCNT_LOPRI_RX] = le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_LO_RX]); - btc->cx.cnt_bt[BTC_BCNT_POLUT] = + bt->bcnt[BTC_BCNT_POLUT] = le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_POLLUTED]); _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); @@ -1797,15 +1797,15 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, memcpy(&dm->gnt.band[i], &prpt->v105.gnt_val[i][0], sizeof(dm->gnt.band[i])); - btc->cx.cnt_bt[BTC_BCNT_HIPRI_TX] = + bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v105.bt_cnt[BTC_BCNT_HI_TX_V105]); - btc->cx.cnt_bt[BTC_BCNT_HIPRI_RX] = + bt->bcnt[BTC_BCNT_HIPRI_RX] = le16_to_cpu(prpt->v105.bt_cnt[BTC_BCNT_HI_RX_V105]); - btc->cx.cnt_bt[BTC_BCNT_LOPRI_TX] = + bt->bcnt[BTC_BCNT_LOPRI_TX] = le16_to_cpu(prpt->v105.bt_cnt[BTC_BCNT_LO_TX_V105]); - btc->cx.cnt_bt[BTC_BCNT_LOPRI_RX] = + bt->bcnt[BTC_BCNT_LOPRI_RX] = le16_to_cpu(prpt->v105.bt_cnt[BTC_BCNT_LO_RX_V105]); - btc->cx.cnt_bt[BTC_BCNT_POLUT] = + bt->bcnt[BTC_BCNT_POLUT] = le16_to_cpu(prpt->v105.bt_cnt[BTC_BCNT_POLLUTED_V105]); _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); @@ -1823,21 +1823,21 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, memcpy(&dm->gnt.band[i], &prpt->v7.gnt_val[i][0], sizeof(dm->gnt.band[i])); - btc->cx.cnt_bt[BTC_BCNT_HIPRI_TX] = + bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v7.bt_cnt[BTC_BCNT_HI_TX_V105]); - btc->cx.cnt_bt[BTC_BCNT_HIPRI_RX] = + bt->bcnt[BTC_BCNT_HIPRI_RX] = le16_to_cpu(prpt->v7.bt_cnt[BTC_BCNT_HI_RX_V105]); - btc->cx.cnt_bt[BTC_BCNT_LOPRI_TX] = + bt->bcnt[BTC_BCNT_LOPRI_TX] = le16_to_cpu(prpt->v7.bt_cnt[BTC_BCNT_LO_TX_V105]); - btc->cx.cnt_bt[BTC_BCNT_LOPRI_RX] = + bt->bcnt[BTC_BCNT_LOPRI_RX] = le16_to_cpu(prpt->v7.bt_cnt[BTC_BCNT_LO_RX_V105]); val1 = le16_to_cpu(prpt->v7.bt_cnt[BTC_BCNT_POLLUTED_V105]); - if (val1 > btc->cx.cnt_bt[BTC_BCNT_POLUT_NOW]) - val1 -= btc->cx.cnt_bt[BTC_BCNT_POLUT_NOW]; /* diff */ + if (val1 > bt->bcnt[BTC_BCNT_POLUT_NOW]) + val1 -= bt->bcnt[BTC_BCNT_POLUT_NOW]; /* diff */ - btc->cx.cnt_bt[BTC_BCNT_POLUT_DIFF] = val1; - btc->cx.cnt_bt[BTC_BCNT_POLUT_NOW] = + bt->bcnt[BTC_BCNT_POLUT_DIFF] = val1; + bt->bcnt[BTC_BCNT_POLUT_NOW] = le16_to_cpu(prpt->v7.bt_cnt[BTC_BCNT_POLLUTED_V105]); val1 = pfwinfo->event[BTF_EVNT_RPT]; @@ -1855,21 +1855,21 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, memcpy(&dm->gnt.band[i], &prpt->v8.gnt_val[i][0], sizeof(dm->gnt.band[i])); - btc->cx.cnt_bt[BTC_BCNT_HIPRI_TX] = + bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v8.bt_cnt[BTC_BCNT_HI_TX_V105]); - btc->cx.cnt_bt[BTC_BCNT_HIPRI_RX] = + bt->bcnt[BTC_BCNT_HIPRI_RX] = le16_to_cpu(prpt->v8.bt_cnt[BTC_BCNT_HI_RX_V105]); - btc->cx.cnt_bt[BTC_BCNT_LOPRI_TX] = + bt->bcnt[BTC_BCNT_LOPRI_TX] = le16_to_cpu(prpt->v8.bt_cnt[BTC_BCNT_LO_TX_V105]); - btc->cx.cnt_bt[BTC_BCNT_LOPRI_RX] = + bt->bcnt[BTC_BCNT_LOPRI_RX] = le16_to_cpu(prpt->v8.bt_cnt[BTC_BCNT_LO_RX_V105]); val1 = le16_to_cpu(prpt->v8.bt_cnt[BTC_BCNT_POLLUTED_V105]); - if (val1 > btc->cx.cnt_bt[BTC_BCNT_POLUT_NOW]) - val1 -= btc->cx.cnt_bt[BTC_BCNT_POLUT_NOW]; /* diff */ + if (val1 > bt->bcnt[BTC_BCNT_POLUT_NOW]) + val1 -= bt->bcnt[BTC_BCNT_POLUT_NOW]; /* diff */ - btc->cx.cnt_bt[BTC_BCNT_POLUT_DIFF] = val1; - btc->cx.cnt_bt[BTC_BCNT_POLUT_NOW] = + bt->bcnt[BTC_BCNT_POLUT_DIFF] = val1; + bt->bcnt[BTC_BCNT_POLUT_NOW] = le16_to_cpu(prpt->v8.bt_cnt[BTC_BCNT_POLLUTED_V105]); val1 = pfwinfo->event[BTF_EVNT_RPT]; @@ -3083,7 +3083,7 @@ static void _set_bt_tx_power(struct rtw89_dev *rtwdev, u8 level) int ret; u8 buf; - if (btc->cx.cnt_bt[BTC_BCNT_INFOUPDATE] == 0) + if (bt->bcnt[BTC_BCNT_INFOUPDATE] == 0) return; if (bt->rf_para.tx_pwr_freerun == level) @@ -3108,7 +3108,7 @@ static void _set_bt_rx_gain(struct rtw89_dev *rtwdev, u8 level) struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; - if (btc->cx.cnt_bt[BTC_BCNT_INFOUPDATE] == 0) + if (bt->bcnt[BTC_BCNT_INFOUPDATE] == 0) return; if ((bt->rf_para.rx_gain_freerun == level || @@ -6059,7 +6059,7 @@ static u32 _read_scbd(struct rtw89_dev *rtwdev) rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], read scbd: 0x%08x\n", scbd_val); - btc->cx.cnt_bt[BTC_BCNT_SCBDREAD]++; + btc->cx.bt0.bcnt[BTC_BCNT_SCBDREAD]++; return scbd_val; } @@ -7388,18 +7388,16 @@ void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work) static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) { - const struct rtw89_chip_info *chip = rtwdev->chip; - const struct rtw89_btc_ver *ver = rtwdev->btc.ver; struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; + const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_dm *dm = &rtwdev->btc.dm; bool bt_link_change = false, lps_ctrl = false; u32 val, any_bt_connect; u8 mode; - if (!chip->scbd) + if (rtwdev->chip->scbd) return; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s\n", __func__); @@ -7436,7 +7434,7 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) /* reset bt info if bt re-enable */ if (bt->enable.now && !bt->enable.last) { _reset_btc_var(rtwdev, BTC_RESET_BTINFO); - cx->cnt_bt[BTC_BCNT_REENABLE]++; + bt->bcnt[BTC_BCNT_REENABLE]++; bt->enable.now = 1; } @@ -8095,7 +8093,7 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): return by bt-info duplicate!!\n", __func__); - cx->cnt_bt[BTC_BCNT_INFOSAME]++; + bt->bcnt[BTC_BCNT_INFOSAME]++; return; } @@ -8116,7 +8114,7 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) b->status.map.acl_busy = btinfo.lb2.acl_busy; b->status.map.inq_pag = btinfo.lb2.inq_pag; bt->inq_pag.now = btinfo.lb2.inq_pag; - cx->cnt_bt[BTC_BCNT_INQPAG] += !!(bt->inq_pag.now && !bt->inq_pag.last); + bt->bcnt[BTC_BCNT_INQPAG] += !!(bt->inq_pag.now && !bt->inq_pag.last); hfp->exist = btinfo.lb2.hfp; b->profile_cnt.now += (u8)hfp->exist; @@ -8131,11 +8129,11 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) /* parse raw info low-Byte3 */ btinfo.val = bt->raw_info[BTC_BTINFO_L3]; if (btinfo.lb3.retry != 0) - cx->cnt_bt[BTC_BCNT_RETRY]++; + bt->bcnt[BTC_BCNT_RETRY]++; b->cqddr = btinfo.lb3.cqddr; - cx->cnt_bt[BTC_BCNT_INQ] += !!(btinfo.lb3.inq && !bt->inq); + bt->bcnt[BTC_BCNT_INQ] += !!(btinfo.lb3.inq && !bt->inq); bt->inq = btinfo.lb3.inq; - cx->cnt_bt[BTC_BCNT_PAGE] += !!(btinfo.lb3.pag && !bt->pag); + bt->bcnt[BTC_BCNT_PAGE] += !!(btinfo.lb3.pag && !bt->pag); bt->pag = btinfo.lb3.pag; b->status.map.mesh_busy = btinfo.lb3.mesh_busy; @@ -8158,11 +8156,11 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) hid->type |= BTC_HID_RCU; } - cx->cnt_bt[BTC_BCNT_REINIT] += !!(btinfo.hb1.reinit && !bt->reinit); + bt->bcnt[BTC_BCNT_REINIT] += !!(btinfo.hb1.reinit && !bt->reinit); bt->reinit = btinfo.hb1.reinit; - cx->cnt_bt[BTC_BCNT_RELINK] += !!(btinfo.hb1.relink && !b->relink.now); + bt->bcnt[BTC_BCNT_RELINK] += !!(btinfo.hb1.relink && !b->relink.now); b->relink.now = btinfo.hb1.relink; - cx->cnt_bt[BTC_BCNT_IGNOWL] += !!(btinfo.hb1.igno_wl && !bt->igno_wl); + bt->bcnt[BTC_BCNT_IGNOWL] += !!(btinfo.hb1.igno_wl && !bt->igno_wl); bt->igno_wl = btinfo.hb1.igno_wl; if (bt->igno_wl && !cx->wl.status.map.rf_off) @@ -8170,7 +8168,7 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) bt->ble_scan_en = btinfo.hb1.ble_scan; - cx->cnt_bt[BTC_BCNT_ROLESW] += !!(btinfo.hb1.role_sw && !b->role_sw); + bt->bcnt[BTC_BCNT_ROLESW] += !!(btinfo.hb1.role_sw && !b->role_sw); b->role_sw = btinfo.hb1.role_sw; b->multi_link.now = btinfo.hb1.multi_link; @@ -8179,7 +8177,7 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) btinfo.val = bt->raw_info[BTC_BTINFO_H2]; pan->active = !!btinfo.hb2.pan_active; - cx->cnt_bt[BTC_BCNT_AFH] += !!(btinfo.hb2.afh_update && !b->afh_update); + bt->bcnt[BTC_BCNT_AFH] += !!(btinfo.hb2.afh_update && !b->afh_update); b->afh_update = btinfo.hb2.afh_update; a2dp->active = btinfo.hb2.a2dp_active; b->slave_role = btinfo.hb2.slave; @@ -8193,7 +8191,7 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) a2dp->bitpool = btinfo.hb3.a2dp_bitpool; if (b->tx_3m != (u32)btinfo.hb3.tx_3m) - cx->cnt_bt[BTC_BCNT_RATECHG]++; + bt->bcnt[BTC_BCNT_RATECHG]++; b->tx_3m = (u32)btinfo.hb3.tx_3m; a2dp->sink = btinfo.hb3.a2dp_sink; @@ -8785,6 +8783,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func) { struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt0; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; u8 *buf = &skb->data[RTW89_C2H_HEADER_LEN]; @@ -8812,13 +8811,13 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, case BTF_EVNT_BT_INFO: rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], handle C2H BT INFO with data %8ph\n", buf); - btc->cx.cnt_bt[BTC_BCNT_INFOUPDATE]++; + bt->bcnt[BTC_BCNT_INFOUPDATE]++; _update_bt_info(rtwdev, buf, len); break; case BTF_EVNT_BT_SCBD: rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], handle C2H BT SCBD with data %8ph\n", buf); - btc->cx.cnt_bt[BTC_BCNT_SCBDUPDATE]++; + bt->bcnt[BTC_BCNT_SCBDUPDATE]++; _update_bt_scbd(rtwdev, false); break; case BTF_EVNT_BT_PSD: @@ -8836,7 +8835,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, btc->dm.cnt_dm[BTC_DCNT_CX_RUNINFO]++; break; case BTF_EVNT_BT_QUERY_TXPWR: - btc->cx.cnt_bt[BTC_BCNT_BTTXPWR_UPDATE]++; + bt->bcnt[BTC_BCNT_BTTXPWR_UPDATE]++; _update_bt_txpwr_info(rtwdev, buf, len); } } @@ -9187,17 +9186,17 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ", - "[stat_cnt]", cx->cnt_bt[BTC_BCNT_RETRY], - cx->cnt_bt[BTC_BCNT_RELINK], - cx->cnt_bt[BTC_BCNT_RATECHG], - cx->cnt_bt[BTC_BCNT_REINIT], - cx->cnt_bt[BTC_BCNT_REENABLE]); + "[stat_cnt]", bt->bcnt[BTC_BCNT_RETRY], + bt->bcnt[BTC_BCNT_RELINK], + bt->bcnt[BTC_BCNT_RATECHG], + bt->bcnt[BTC_BCNT_REINIT], + bt->bcnt[BTC_BCNT_REENABLE]); p += scnprintf(p, end - p, "role-switch:%d, afh:%d, inq_page:%d(inq:%d/page:%d), igno_wl:%d\n", - cx->cnt_bt[BTC_BCNT_ROLESW], cx->cnt_bt[BTC_BCNT_AFH], - cx->cnt_bt[BTC_BCNT_INQPAG], cx->cnt_bt[BTC_BCNT_INQ], - cx->cnt_bt[BTC_BCNT_PAGE], cx->cnt_bt[BTC_BCNT_IGNOWL]); + bt->bcnt[BTC_BCNT_ROLESW], bt->bcnt[BTC_BCNT_AFH], + bt->bcnt[BTC_BCNT_INQPAG], bt->bcnt[BTC_BCNT_INQ], + bt->bcnt[BTC_BCNT_PAGE], bt->bcnt[BTC_BCNT_IGNOWL]); p += _show_bt_profile_info(rtwdev, p, end - p); @@ -9207,16 +9206,16 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) bt->raw_info[4], bt->raw_info[5], bt->raw_info[6], bt->raw_info[7], bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", - cx->cnt_bt[BTC_BCNT_INFOUPDATE], - cx->cnt_bt[BTC_BCNT_INFOSAME]); + bt->bcnt[BTC_BCNT_INFOUPDATE], + bt->bcnt[BTC_BCNT_INFOSAME]); p += scnprintf(p, end - p, " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)", - "[trx_req_cnt]", cx->cnt_bt[BTC_BCNT_HIPRI_RX], - cx->cnt_bt[BTC_BCNT_HIPRI_TX], - cx->cnt_bt[BTC_BCNT_LOPRI_RX], - cx->cnt_bt[BTC_BCNT_LOPRI_TX], - cx->cnt_bt[BTC_BCNT_POLUT]); + "[trx_req_cnt]", bt->bcnt[BTC_BCNT_HIPRI_RX], + bt->bcnt[BTC_BCNT_HIPRI_TX], + bt->bcnt[BTC_BCNT_LOPRI_RX], + bt->bcnt[BTC_BCNT_LOPRI_TX], + bt->bcnt[BTC_BCNT_POLUT]); if (!bt->scan_info_update) { rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, true); @@ -9252,7 +9251,7 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) else rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_TX_PWR_LVL, false); - if (cx->cnt_bt[BTC_BCNT_BTTXPWR_UPDATE]) { + if (bt->bcnt[BTC_BCNT_BTTXPWR_UPDATE]) { p += scnprintf(p, end - p, " %-15s : br_index:0x%x, le_index:0x%x", "[bt_txpwr_lvl]", @@ -10896,7 +10895,6 @@ static int _show_mreg_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_fbtc_mreg_val_v1 *pmreg = NULL; - struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_mac_ax_coex_gnt gnt_cfg = {}; @@ -10914,8 +10912,8 @@ static int _show_mreg_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", "[scoreboard]", wl->scbd, wl->wcnt[BTC_WCNT_SCBDUPDATE], - bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], - cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); + bt->scbd, bt->bcnt[BTC_BCNT_SCBDREAD], + bt->bcnt[BTC_BCNT_SCBDUPDATE]); btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev); _get_gnt(rtwdev, &gnt_cfg); @@ -10981,7 +10979,6 @@ static int _show_mreg_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_fbtc_mreg_val_v2 *pmreg = NULL; - struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_mac_ax_coex_gnt gnt_cfg = {}; @@ -10999,8 +10996,8 @@ static int _show_mreg_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", "[scoreboard]", wl->scbd, wl->wcnt[BTC_WCNT_SCBDUPDATE], - bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], - cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); + bt->scbd, bt->bcnt[BTC_BCNT_SCBDREAD], + bt->bcnt[BTC_BCNT_SCBDUPDATE]); btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev); _get_gnt(rtwdev, &gnt_cfg); @@ -11084,8 +11081,8 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) "\n\r %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)", "[scoreboard]", wl->scbd, wl->wcnt[BTC_WCNT_SCBDUPDATE], - bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], - cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); + bt->scbd, bt->bcnt[BTC_BCNT_SCBDREAD], + bt->bcnt[BTC_BCNT_SCBDUPDATE]); /* To avoid I/O if WL LPS or power-off */ dm->pta_owner = rtw89_mac_get_ctrl_path(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0e3110aa867a..e86db8c5c087 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2250,6 +2250,8 @@ struct rtw89_btc_bt_info { u32 scan_info_update: 1; u32 lna_constrain: 3; u32 rsvd: 17; + + u32 bcnt[BTC_BCNT_NUM]; }; struct rtw89_btc_rf_trx_para_v9 { @@ -2267,7 +2269,6 @@ struct rtw89_btc_cx { struct rtw89_btc_bt_info bt1; struct rtw89_btc_3rdcx_info other; u32 state_map; - u32 cnt_bt[BTC_BCNT_NUM]; }; struct rtw89_btc_fbtc_tdma { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 2c1f166e687f..94027e5b8d28 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2105,12 +2105,12 @@ void rtw8852a_btc_update_bt_cnt(struct rtw89_dev *rtwdev) return; val = rtw89_read32(rtwdev, R_AX_BT_STAST_HIGH); - cx->cnt_bt[BTC_BCNT_HIPRI_TX] = FIELD_GET(B_AX_STATIS_BT_HI_TX_MASK, val); - cx->cnt_bt[BTC_BCNT_HIPRI_RX] = FIELD_GET(B_AX_STATIS_BT_HI_RX_MASK, val); + cx->bt0.bcnt[BTC_BCNT_HIPRI_TX] = FIELD_GET(B_AX_STATIS_BT_HI_TX_MASK, val); + cx->bt0.bcnt[BTC_BCNT_HIPRI_RX] = FIELD_GET(B_AX_STATIS_BT_HI_RX_MASK, val); val = rtw89_read32(rtwdev, R_AX_BT_STAST_LOW); - cx->cnt_bt[BTC_BCNT_LOPRI_TX] = FIELD_GET(B_AX_STATIS_BT_LO_TX_1_MASK, val); - cx->cnt_bt[BTC_BCNT_LOPRI_RX] = FIELD_GET(B_AX_STATIS_BT_LO_RX_1_MASK, val); + cx->bt0.bcnt[BTC_BCNT_LOPRI_TX] = FIELD_GET(B_AX_STATIS_BT_LO_TX_1_MASK, val); + cx->bt0.bcnt[BTC_BCNT_LOPRI_RX] = FIELD_GET(B_AX_STATIS_BT_LO_RX_1_MASK, val); /* clock-gate off before reset counter*/ rtw89_write32_set(rtwdev, R_AX_BTC_CFG, B_AX_DIS_BTC_CLK_G); From 195ce7889423f1fc068490cfbaaed4eaa16a093f Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 24 Jun 2026 11:39:37 +0800 Subject: [PATCH 019/403] wifi: rtw89: coex: Refine third party module related coexistence The incoming chip reserved several IO ports to coexist with the other vendor's stand along chips. In order to configured them easier add the structure. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260624033941.45918-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 8 ++-- drivers/net/wireless/realtek/rtw89/core.h | 31 +++++++++++--- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 40 +++++++++++++++++++ 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 8fa51867055b..5159338960f3 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -7655,7 +7655,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) goto exit; } - if (!cx->bt0.enable.now && !cx->other.type) { + if (!cx->bt0.enable.now && !cx->bt_ext.func_type) { _action_bt_off(rtwdev); goto exit; } @@ -7776,7 +7776,7 @@ static void _set_init_info(struct rtw89_dev *rtwdev) dm->init_info.init_v7.wl_only = (u8)dm->wl_only; dm->init_info.init_v7.bt_only = (u8)dm->bt_only; dm->init_info.init_v7.wl_init_ok = (u8)wl->status.map.init_ok; - dm->init_info.init_v7.cx_other = btc->cx.other.type; + dm->init_info.init_v7.cx_other = btc->cx.bt_ext.func_type; dm->init_info.init_v7.wl_guard_ch = chip->afh_guard_ch; dm->init_info.init_v7.module = btc->mdinfo.md_v7; } else { @@ -7784,7 +7784,7 @@ static void _set_init_info(struct rtw89_dev *rtwdev) dm->init_info.init.bt_only = (u8)dm->bt_only; dm->init_info.init.wl_init_ok = (u8)wl->status.map.init_ok; dm->init_info.init.dbcc_en = rtwdev->dbcc_en; - dm->init_info.init.cx_other = btc->cx.other.type; + dm->init_info.init.cx_other = btc->cx.bt_ext.func_type; dm->init_info.init.wl_guard_ch = chip->afh_guard_ch; dm->init_info.init.module = btc->mdinfo.md; } @@ -8927,7 +8927,7 @@ static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, "3rd_coex:%d, dbcc:%d, tx_num:%d, rx_num:%d\n", - btc->cx.other.type, rtwdev->dbcc_en, hal->tx_nss, + btc->cx.bt_ext.func_type, rtwdev->dbcc_en, hal->tx_nss, hal->rx_nss); return p - buf; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index e86db8c5c087..1293e343d45c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1445,6 +1445,12 @@ enum rtw89_btc_bt_state_cnt { BTC_BCNT_NUM, }; +enum rtw89_btc_bt_rf_band { + BTC_BT_B2G = 0x0, /* 2.4GHz */ + BTC_BT_B5G = 0x1, /* 5GHz or 6GHz */ + BTC_BT_BMAX = 0x2 +}; + enum rtw89_btc_bt_profile { BTC_BT_NOPROFILE = 0, BTC_BT_HFP = BIT(0), @@ -1975,10 +1981,25 @@ struct rtw89_btc_bt_link_info { u32 rsvd: 19; }; -struct rtw89_btc_3rdcx_info { - u8 type; /* 0: none, 1:zigbee, 2:LTE */ - u8 hw_coex; - u16 rsvd; +struct rtw89_btc_extsoc_info { + u8 chip_id; + u8 max_tx_pwr; + u8 rf_band_map; + u8 ant_iso_to_wl; + + u8 link_weight[BTC_BT_BMAX]; + + u8 func_type; /* 0: none, 1:zigbee, 2:LTE */ + u8 hw_coex; /* Hard-Wire coex interface support */ + u8 pta_type; /* 0: RTK 4-wire mode, 1: 3-wire mode */ + u8 pta_req_exist; + + u32 hpta_cfg; + u32 hmbx_cfg; + u32 swout_cfg; + u32 swin_cfg; + u32 profile_map[BTC_BT_BMAX]; + u32 bcnt[BTC_BCNT_NUM]; }; struct rtw89_btc_dm_emap { @@ -2267,7 +2288,7 @@ struct rtw89_btc_cx { struct rtw89_btc_wl_info wl; struct rtw89_btc_bt_info bt0; struct rtw89_btc_bt_info bt1; - struct rtw89_btc_3rdcx_info other; + struct rtw89_btc_extsoc_info bt_ext; u32 state_map; }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index ad3618dfd57d..91897aeced28 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2745,7 +2745,7 @@ static void rtw8922a_btc_set_rfe(struct rtw89_dev *rtwdev) if (module->kt_ver <= 1) module->wa_type |= BTC_WA_HFP_ZB; - rtwdev->btc.cx.other.type = BTC_3CX_NONE; + rtwdev->btc.cx.bt_ext.func_type = BTC_3CX_NONE; if (module->rfe_type == 0) { rtwdev->btc.dm.error.map.rfe_type0 = true; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 838c26231897..888973f4ef95 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2997,6 +2997,46 @@ static u32 rtw8922d_chan_to_rf18_val(struct rtw89_dev *rtwdev, static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) { + union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; + struct rtw89_btc_module_v7 *module = &md->md_v7; + + module->rfe_type = rtwdev->efuse.rfe_type; + module->kt_ver = rtwdev->hal.cv; + module->bt_solo = 0; + module->switch_type = BTC_SWITCH_INTERNAL; + module->wa_type = 0; + + module->ant.type = BTC_ANT_SHARED; + module->ant.num = 2; + module->ant.isolation = 10; + module->ant.diversity = 0; + module->ant.single_pos = RF_PATH_A; + module->ant.btg_pos = RF_PATH_B; + + if (module->kt_ver <= 1) + module->wa_type |= BTC_WA_HFP_ZB; + + rtwdev->btc.cx.bt_ext.func_type = BTC_3CX_NONE; + + if (module->rfe_type == 0) { + rtwdev->btc.dm.error.map.rfe_type0 = true; + return; + } + + module->ant.num = (module->rfe_type % 2) ? 2 : 3; + + if (module->kt_ver == 0) + module->ant.num = 2; + + if (module->ant.num == 3) { + module->ant.type = BTC_ANT_DEDICATED; + module->bt_pos = BTC_BT_ALONE; + } else { + module->ant.type = BTC_ANT_SHARED; + module->bt_pos = BTC_BT_BTG; + } + rtwdev->btc.btg_pos = module->ant.btg_pos; + rtwdev->btc.ant_type = module->ant.type; } static void rtw8922d_btc_init_cfg(struct rtw89_dev *rtwdev) From ebb69df34148c3acffb46c18b76865cbddfb40cd Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 24 Jun 2026 11:39:38 +0800 Subject: [PATCH 020/403] wifi: rtw89: coex: Add TX/RX RF parameter format version 9 In order to support external Zigbee/Thread/Bluetooth etc module, the version 8 add the parameter for the case. And also update the related configuration function. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260624033941.45918-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 230 ++++++++++------ drivers/net/wireless/realtek/rtw89/core.h | 40 +-- drivers/net/wireless/realtek/rtw89/fw.c | 250 ++++++++++++++++-- drivers/net/wireless/realtek/rtw89/fw.h | 160 ++++++----- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 12 +- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 12 +- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 12 +- .../net/wireless/realtek/rtw89/rtw8852bt.c | 12 +- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 12 +- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 12 +- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 4 + 11 files changed, 512 insertions(+), 244 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 5159338960f3..dd9d6cbc2943 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -140,6 +140,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 7, .frptmap = 3, .fcxctrl = 7, .fcxinit = 7, .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 8, + .fcxtrx = 0, }, {RTL8852BT, RTW89_FW_VER_CODE(0, 29, 90, 0), .fcxbtcrpt = 7, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, @@ -148,6 +149,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 7, .frptmap = 3, .fcxctrl = 7, .fcxinit = 7, .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 8, + .fcxtrx = 0, }, {RTL8922A, RTW89_FW_VER_CODE(0, 35, 71, 0), .fcxbtcrpt = 8, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, @@ -156,6 +158,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 8, .frptmap = 4, .fcxctrl = 7, .fcxinit = 7, .fwevntrptl = 1, .fwc2hfunc = 3, .drvinfo_type = 2, .info_buf = 1800, .max_role_num = 6, .fcxosi = 1, .fcxmlo = 1, .bt_desired = 9, + .fcxtrx = 7, }, {RTL8922A, RTW89_FW_VER_CODE(0, 35, 63, 0), .fcxbtcrpt = 8, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, @@ -164,6 +167,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 8, .frptmap = 4, .fcxctrl = 7, .fcxinit = 7, .fwevntrptl = 1, .fwc2hfunc = 3, .drvinfo_type = 2, .info_buf = 1800, .max_role_num = 6, .fcxosi = 1, .fcxmlo = 1, .bt_desired = 9, + .fcxtrx = 0, }, {RTL8922A, RTW89_FW_VER_CODE(0, 35, 8, 0), .fcxbtcrpt = 8, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, @@ -172,6 +176,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 8, .frptmap = 3, .fcxctrl = 7, .fcxinit = 7, .fwevntrptl = 1, .fwc2hfunc = 1, .drvinfo_type = 1, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 0, }, {RTL8851B, RTW89_FW_VER_CODE(0, 29, 29, 0), .fcxbtcrpt = 105, .fcxtdma = 3, .fcxslots = 1, .fcxcysta = 5, @@ -180,6 +185,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 2, .frptmap = 3, .fcxctrl = 1, .fcxinit = 0, .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 0, }, {RTL8852C, RTW89_FW_VER_CODE(0, 27, 57, 0), .fcxbtcrpt = 4, .fcxtdma = 3, .fcxslots = 1, .fcxcysta = 3, @@ -188,6 +194,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 1, .frptmap = 3, .fcxctrl = 1, .fcxinit = 0, .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 0, }, {RTL8852C, RTW89_FW_VER_CODE(0, 27, 42, 0), .fcxbtcrpt = 4, .fcxtdma = 3, .fcxslots = 1, .fcxcysta = 3, @@ -196,6 +203,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 1, .frptmap = 2, .fcxctrl = 1, .fcxinit = 0, .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 0, }, {RTL8852C, RTW89_FW_VER_CODE(0, 27, 0, 0), .fcxbtcrpt = 4, .fcxtdma = 3, .fcxslots = 1, .fcxcysta = 3, @@ -204,6 +212,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 1, .frptmap = 2, .fcxctrl = 1, .fcxinit = 0, .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 0, }, {RTL8852B, RTW89_FW_VER_CODE(0, 29, 122, 0), .fcxbtcrpt = 8, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, @@ -212,6 +221,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 7, .frptmap = 3, .fcxctrl = 7, .fcxinit = 7, .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 8, + .fcxtrx = 0, }, {RTL8852B, RTW89_FW_VER_CODE(0, 29, 29, 0), .fcxbtcrpt = 105, .fcxtdma = 3, .fcxslots = 1, .fcxcysta = 5, @@ -220,6 +230,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 2, .frptmap = 3, .fcxctrl = 1, .fcxinit = 0, .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 0, }, {RTL8852B, RTW89_FW_VER_CODE(0, 29, 14, 0), .fcxbtcrpt = 5, .fcxtdma = 3, .fcxslots = 1, .fcxcysta = 4, @@ -228,6 +239,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 1, .frptmap = 3, .fcxctrl = 1, .fcxinit = 0, .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 0, }, {RTL8852B, RTW89_FW_VER_CODE(0, 27, 0, 0), .fcxbtcrpt = 4, .fcxtdma = 3, .fcxslots = 1, .fcxcysta = 3, @@ -236,6 +248,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 1, .frptmap = 1, .fcxctrl = 1, .fcxinit = 0, .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 0, }, {RTL8852A, RTW89_FW_VER_CODE(0, 13, 37, 0), .fcxbtcrpt = 4, .fcxtdma = 3, .fcxslots = 1, .fcxcysta = 3, @@ -244,6 +257,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 1, .frptmap = 3, .fcxctrl = 1, .fcxinit = 0, .fwevntrptl = 0, .fwc2hfunc = 0, .drvinfo_type = 0, .info_buf = 1280, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 0, }, {RTL8852A, RTW89_FW_VER_CODE(0, 13, 0, 0), .fcxbtcrpt = 1, .fcxtdma = 1, .fcxslots = 1, .fcxcysta = 2, @@ -252,6 +266,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 0, .frptmap = 0, .fcxctrl = 0, .fcxinit = 0, .fwevntrptl = 0, .fwc2hfunc = 0, .drvinfo_type = 0, .info_buf = 1024, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 0, }, /* keep it to be the last as default entry */ @@ -262,6 +277,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fwlrole = 0, .frptmap = 0, .fcxctrl = 0, .fcxinit = 0, .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1024, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 0, }, }; @@ -2777,9 +2793,6 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; - struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_rf_trx_para rf_para = dm->rf_trx_para; switch (type) { case CXDRVINFO_INIT: @@ -2813,17 +2826,10 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 type) if (ver->drvinfo_type == 1) type = 3; - dm->trx_info.tx_power = u32_get_bits(rf_para.wl_tx_power, - RTW89_BTC_WL_DEF_TX_PWR); - dm->trx_info.rx_gain = u32_get_bits(rf_para.wl_rx_gain, - RTW89_BTC_WL_DEF_TX_PWR); - dm->trx_info.bt_tx_power = u32_get_bits(rf_para.bt_tx_power, - RTW89_BTC_WL_DEF_TX_PWR); - dm->trx_info.bt_rx_gain = u32_get_bits(rf_para.bt_rx_gain, - RTW89_BTC_WL_DEF_TX_PWR); - dm->trx_info.cn = wl->cn_report; - dm->trx_info.nhm = wl->nhm.pwr; - rtw89_fw_h2c_cxdrv_trx(rtwdev, type); + if (ver->fcxtrx == 7) + rtw89_fw_h2c_cxdrv_trx_v7(rtwdev, type); + else if (ver->fcxtrx == 9) + rtw89_fw_h2c_cxdrv_trx_v9(rtwdev, type); break; case CXDRVINFO_RFK: if (ver->drvinfo_type == 1) @@ -2832,6 +2838,14 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 type) rtw89_fw_h2c_cxdrv_rfk(rtwdev, type); break; case CXDRVINFO_TXPWR: + if (ver->drvinfo_type == 3) + type = 4; + + if (ver->fcxtrx == 7) + rtw89_fw_h2c_cxtxpwr_v7(rtwdev, type); + else if (ver->fcxtrx == 9) + rtw89_fw_h2c_cxtxpwr_v9(rtwdev, type); + break; case CXDRVINFO_FDDT: case CXDRVINFO_MLO: case CXDRVINFO_OSI: @@ -3025,53 +3039,84 @@ static void _set_bt_ignore_wlan_act(struct rtw89_dev *rtwdev, u8 enable) #define B_BTC_WL_TX_POWER_SIGN BIT(7) #define B_TSSI_WL_TX_POWER_SIGN BIT(8) -static void _set_wl_tx_power(struct rtw89_dev *rtwdev, u32 level) +static void _set_wl_tx_power(struct rtw89_dev *rtwdev, u32 level, u8 phy_map) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; + const struct rtw89_btc_ver *ver = btc->ver; + struct rtw89_btc_dm *dm = &btc->dm; + bool level_chg = false; u32 pwr_val; - if (wl->rf_para.tx_pwr_freerun == level) + if (phy_map & BIT(RTW89_PHY_0)) + level_chg = !(dm->rf_trx_para.wl_tx_power[RTW89_PHY_0] == level); + + if (phy_map & BIT(RTW89_PHY_1)) + level_chg |= !(dm->rf_trx_para.wl_tx_power[RTW89_PHY_1] == level); + + if (!level_chg && !btc->cli_h2c_cmd) return; - wl->rf_para.tx_pwr_freerun = level; - btc->dm.rf_trx_para.wl_tx_power = level; + if (phy_map & BIT(RTW89_PHY_0)) + dm->rf_trx_para.wl_tx_power[RTW89_PHY_0] = level; + if (phy_map & BIT(RTW89_PHY_1)) + dm->rf_trx_para.wl_tx_power[RTW89_PHY_1] = level; + + dm->wl_tx_pwr_phy_map = phy_map; rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): level = %d\n", - __func__, level); + "[BTC], %s(): phy_map=0x%x, wl_tx_pwr-> phy0=%d, phy1=%d\n", + __func__, phy_map, + dm->rf_trx_para.wl_tx_power[RTW89_PHY_0], + dm->rf_trx_para.wl_tx_power[RTW89_PHY_1]); - if (level == RTW89_BTC_WL_DEF_TX_PWR) { - pwr_val = WL_TX_POWER_NO_BTC_CTRL; - } else { /* only apply "force tx power" */ - pwr_val = FIELD_PREP(WL_TX_POWER_INT_PART, level); - if (pwr_val > RTW89_BTC_WL_DEF_TX_PWR) - pwr_val = RTW89_BTC_WL_DEF_TX_PWR; + if (ver->fcxtrx == 7 && chip->chip_id == RTL8922A) { + _fw_set_drv_info(rtwdev, CXDRVINFO_TXPWR); + } else if (ver->fcxtrx == 9) { + _fw_set_drv_info(rtwdev, CXDRVINFO_TXPWR); + } else { + if (level == RTW89_BTC_WL_DEF_TX_PWR) { + pwr_val = WL_TX_POWER_NO_BTC_CTRL; + } else { /* only apply "force tx power" */ + pwr_val = FIELD_PREP(WL_TX_POWER_INT_PART, level); + if (pwr_val > RTW89_BTC_WL_DEF_TX_PWR) + pwr_val = RTW89_BTC_WL_DEF_TX_PWR; - if (level & B_BTC_WL_TX_POWER_SIGN) - pwr_val |= B_TSSI_WL_TX_POWER_SIGN; - pwr_val |= WL_TX_POWER_WITH_BT; + if (level & B_BTC_WL_TX_POWER_SIGN) + pwr_val |= B_TSSI_WL_TX_POWER_SIGN; + pwr_val |= WL_TX_POWER_WITH_BT; + } + chip->ops->btc_set_wl_txpwr_ctrl(rtwdev, pwr_val); } - - chip->ops->btc_set_wl_txpwr_ctrl(rtwdev, pwr_val); } -static void _set_wl_rx_gain(struct rtw89_dev *rtwdev, u32 level) +static void _set_wl_rx_gain(struct rtw89_dev *rtwdev, u32 level, u8 phy_map) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_dm *dm = &btc->dm; + bool level_chg = false; - if (wl->rf_para.rx_gain_freerun == level) + if (phy_map & BIT(RTW89_PHY_0)) + level_chg = !(dm->rf_trx_para.wl_rx_gain[RTW89_PHY_0] == level); + + if (phy_map & BIT(RTW89_PHY_1)) + level_chg |= !(dm->rf_trx_para.wl_rx_gain[RTW89_PHY_1] == level); + + if (!level_chg && !btc->cli_h2c_cmd) return; - wl->rf_para.rx_gain_freerun = level; - btc->dm.rf_trx_para.wl_rx_gain = level; + if (phy_map & BIT(RTW89_PHY_0)) + dm->rf_trx_para.wl_rx_gain[RTW89_PHY_0] = level; + + if (phy_map & BIT(RTW89_PHY_1)) + dm->rf_trx_para.wl_rx_gain[RTW89_PHY_1] = level; rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): level = %d\n", - __func__, level); + "[BTC], %s(): phy_map=0x%x, wl_rx_gain-> phy0=%d, phy1=%d\n", + __func__, phy_map, + dm->rf_trx_para.wl_rx_gain[RTW89_PHY_0], + dm->rf_trx_para.wl_rx_gain[RTW89_PHY_1]); chip->ops->btc_set_wl_rx_gain(rtwdev, level); } @@ -3097,7 +3142,7 @@ static void _set_bt_tx_power(struct rtw89_dev *rtwdev, u8 level) ret = _send_fw_cmd(rtwdev, BTFC_SET, SET_BT_TX_PWR, &buf, 1); if (!ret) { bt->rf_para.tx_pwr_freerun = level; - btc->dm.rf_trx_para.bt_tx_power = level; + btc->dm.rf_trx_para.bt_tx_power[BTC_BT_1ST] = level; } } @@ -3117,7 +3162,7 @@ static void _set_bt_rx_gain(struct rtw89_dev *rtwdev, u8 level) return; bt->rf_para.rx_gain_freerun = level; - btc->dm.rf_trx_para.bt_rx_gain = level; + btc->dm.rf_trx_para.bt_rx_gain[BTC_BT_1ST] = level; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): level = %d\n", @@ -3141,9 +3186,10 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_bt_link_info *b = &bt->link_info; struct rtw89_btc_wl_smap *wl_smap = &wl->status.map; - struct rtw89_btc_rf_trx_para para; + struct rtw89_btc_rf_trx_para_v9 para; + u8 lv, link_mode = 0, i, dbcc_2g_phy = 0; + u8 ul_para_num, dl_para_num; u32 wl_stb_chg = 0; - u8 level_id = 0, link_mode = 0, i, dbcc_2g_phy = 0; if (ver->fwlrole == 0) { link_mode = wl->role_info.link_mode; @@ -3159,6 +3205,18 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) dbcc_2g_phy = wl->role_info_v2.dbcc_2g_phy; } + if (ver->fcxtrx == 9 && chip->rf_para_ulink_v9) { + ul_para_num = chip->rf_para_ulink_num_v9; + dl_para_num = chip->rf_para_dlink_num_v9; + } else if (ver->fcxtrx == 0 && chip->rf_para_ulink_v0) { + ul_para_num = chip->rf_para_ulink_num_v0; + dl_para_num = chip->rf_para_dlink_num_v0; + } else { + rtw89_warn(rtwdev, "[BTC]%s(), No rf_para for verseion %d\n", + __func__, ver->fcxtrx); + goto next; + } + /* decide trx_para_level */ if (btc->ant_type == BTC_ANT_SHARED) { /* fix LNA2 + TIA gain not change by GNT_BT */ @@ -3180,30 +3238,54 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) } } - level_id = dm->trx_para_level; - if (level_id >= chip->rf_para_dlink_num || - level_id >= chip->rf_para_ulink_num) { + lv = dm->trx_para_level; + if (lv >= ul_para_num || + lv >= dl_para_num) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): invalid level_id: %d\n", - __func__, level_id); + __func__, lv); return; } - if (wl->status.map.traffic_dir & BIT(RTW89_TFC_UL)) - para = chip->rf_para_ulink[level_id]; - else - para = chip->rf_para_dlink[level_id]; - - if (dm->fddt_train) { - _set_wl_rx_gain(rtwdev, 1); - _write_scbd(rtwdev, BTC_WSCB_RXGAIN, true); + if (wl->status.map.traffic_dir & BIT(RTW89_TFC_UL)) { + if (ver->fcxtrx == 9) { + para = chip->rf_para_ulink_v9[lv]; + } else { + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) { + para.wl_tx_power[i] = chip->rf_para_ulink_v0[lv].wl_tx_power; + para.wl_rx_gain[i] = chip->rf_para_ulink_v0[lv].wl_rx_gain; + } + for (i = BTC_BT_1ST; i < BTC_ALL_BT; i++) { + para.bt_tx_power[i] = chip->rf_para_ulink_v0[lv].bt_tx_power; + para.bt_rx_gain[i] = chip->rf_para_ulink_v0[lv].bt_rx_gain; + } + } } else { - _set_wl_tx_power(rtwdev, para.wl_tx_power); - _set_wl_rx_gain(rtwdev, para.wl_rx_gain); - _set_bt_tx_power(rtwdev, para.bt_tx_power); - _set_bt_rx_gain(rtwdev, para.bt_rx_gain); + if (ver->fcxtrx == 9) { + para = chip->rf_para_dlink_v9[lv]; + } else { + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) { + para.wl_tx_power[i] = chip->rf_para_dlink_v0[lv].wl_tx_power; + para.wl_rx_gain[i] = chip->rf_para_dlink_v0[lv].wl_rx_gain; + } + for (i = BTC_BT_1ST; i < BTC_ALL_BT; i++) { + para.bt_tx_power[i] = chip->rf_para_dlink_v0[lv].bt_tx_power; + para.bt_rx_gain[i] = chip->rf_para_dlink_v0[lv].bt_rx_gain; + } + } } + if (dm->fddt_train) { + _set_wl_rx_gain(rtwdev, 1, RTW89_PHY_0); + _write_scbd(rtwdev, BTC_WSCB_RXGAIN, true); + } else { + _set_wl_tx_power(rtwdev, para.wl_tx_power[RTW89_PHY_0], RTW89_PHY_0); + _set_wl_rx_gain(rtwdev, para.wl_rx_gain[RTW89_PHY_0], RTW89_PHY_0); + _set_bt_tx_power(rtwdev, para.bt_tx_power[BTC_BT_1ST]); + _set_bt_rx_gain(rtwdev, para.bt_rx_gain[BTC_BT_1ST]); + } + +next: if (!bt->enable.now || dm->wl_only || wl_smap->rf_off || wl_smap->lps == BTC_LPS_RF_OFF || link_mode == BTC_WLINK_5G || @@ -7836,7 +7918,7 @@ void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode) } _set_init_info(rtwdev); - _set_wl_tx_power(rtwdev, RTW89_BTC_WL_DEF_TX_PWR); + _set_wl_tx_power(rtwdev, RTW89_BTC_WL_DEF_TX_PWR, RTW89_PHY_0); btc_fw_set_monreg(rtwdev); rtw89_btc_fw_set_slots(rtwdev); _fw_set_drv_info(rtwdev, CXDRVINFO_INIT); @@ -9669,25 +9751,19 @@ static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) (dm->wl_fw_cx_offload == BTC_CX_FW_OFFLOAD ? "" : "(Mismatch!!)")); - if (dm->rf_trx_para.wl_tx_power == 0xff) - p += scnprintf(p, end - p, - " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:orig, ", - "[trx_ctrl]", wl->rssi_level, - dm->trx_para_level); - - else - p += scnprintf(p, end - p, - " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:%d, ", - "[trx_ctrl]", wl->rssi_level, - dm->trx_para_level, - dm->rf_trx_para.wl_tx_power); + p += scnprintf(p, end - p, + " %-15s : wl[rssi_lvl:%d/para:%d/tx_pwr:[%d %d]/rx_lvl:[%d %d]/lna2:%d/stb_chg:%d]\n ", + "[dm_rf_ctrl]", + wl->rssi_level, dm->trx_para_level, + dm->rf_trx_para.wl_tx_power[RTW89_PHY_0], + dm->rf_trx_para.wl_tx_power[RTW89_PHY_1], + dm->rf_trx_para.wl_rx_gain[RTW89_PHY_0], + dm->rf_trx_para.wl_rx_gain[RTW89_PHY_1], + dm->wl_lna2, dm->wl_stb_chg); p += scnprintf(p, end - p, - "wl_rx_lvl:%d, bt_tx_pwr_dec:%d, bt_rx_lna:%d(%s-tbl), wl_btg_rx:%d\n", - dm->rf_trx_para.wl_rx_gain, - dm->rf_trx_para.bt_tx_power, - dm->rf_trx_para.bt_rx_gain, - (bt->hi_lna_rx ? "Hi" : "Ori"), dm->wl_btg_rx); + " %-15s : pre_agc:%d, btg_rx:%d\n ", + "[dm_bb_ctrl]", dm->wl_pre_agc, dm->wl_btg_rx); 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", diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 1293e343d45c..01c7d36d8e12 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2275,6 +2275,14 @@ struct rtw89_btc_bt_info { u32 bcnt[BTC_BCNT_NUM]; }; +#define RTW89_BTC_WL_DEF_TX_PWR GENMASK(7, 0) +struct rtw89_btc_rf_trx_para_v0 { + u32 wl_tx_power; /* absolute Tx power (dBm), 0xff-> no BTC control */ + u32 wl_rx_gain; /* rx gain table index (TBD.) */ + u8 bt_tx_power; /* decrease Tx power (dB) */ + u8 bt_rx_gain; /* LNA constrain level */ +}; + struct rtw89_btc_rf_trx_para_v9 { u32 wl_tx_power[RTW89_PHY_NUM]; /* absolute Tx power (dBm), 1's complement -5->0x85 */ u32 wl_rx_gain[RTW89_PHY_NUM]; /* rx gain table index (TBD.) */ @@ -2289,6 +2297,7 @@ struct rtw89_btc_cx { struct rtw89_btc_bt_info bt0; struct rtw89_btc_bt_info bt1; struct rtw89_btc_extsoc_info bt_ext; + struct rtw89_btc_rf_trx_para_v9 rf_para; u32 state_map; }; @@ -3068,24 +3077,18 @@ struct rtw89_btc_fbtc_btdevinfo { __le32 flush_time; } __packed; -#define RTW89_BTC_WL_DEF_TX_PWR GENMASK(7, 0) -struct rtw89_btc_rf_trx_para { - u32 wl_tx_power; /* absolute Tx power (dBm), 0xff-> no BTC control */ - u32 wl_rx_gain; /* rx gain table index (TBD.) */ - u8 bt_tx_power; /* decrease Tx power (dB) */ - u8 bt_rx_gain; /* LNA constrain level */ -}; - struct rtw89_btc_trx_info { u8 tx_lvl; u8 rx_lvl; u8 wl_rssi; u8 bt_rssi; - s8 tx_power; /* absolute Tx power (dBm), 0xff-> no BTC control */ - s8 rx_gain; /* rx gain table index (TBD.) */ - s8 bt_tx_power; /* decrease Tx power (dB) */ - s8 bt_rx_gain; /* LNA constrain level */ + s8 wl_tx_power[RTW89_PHY_NUM]; /* absolute Tx power (dBm), 0xff-> no BTC control */ + s8 wl_rx_gain[RTW89_PHY_NUM]; /* rx gain table index (TBD.) */ + s8 bt_tx_power[BTC_ALL_BT]; /* decrease Tx power (dB) */ + s8 bt_rx_gain[BTC_ALL_BT]; /* LNA constrain level */ + s8 zb_tx_power[BTC_ALL_BT]; + s8 zb_rx_gain[BTC_ALL_BT]; u8 cn; /* condition_num */ s8 nhm; @@ -3132,7 +3135,7 @@ struct rtw89_btc_dm { struct rtw89_btc_fbtc_tdma tdma_now; struct rtw89_mac_ax_coex_gnt gnt; union rtw89_btc_init_info_u init_info; /* pass to wl_fw if offload */ - struct rtw89_btc_rf_trx_para rf_trx_para; + struct rtw89_btc_rf_trx_para_v9 rf_trx_para; struct rtw89_btc_wl_tx_limit_para wl_tx_limit; struct rtw89_btc_dm_step dm_step; struct rtw89_btc_wl_scc_ctrl wl_scc; @@ -3169,6 +3172,7 @@ struct rtw89_btc_dm { u8 run_reason; u8 run_action; + u8 wl_tx_pwr_phy_map; u8 wl_pre_agc: 2; u8 wl_lna2: 1; @@ -3367,6 +3371,7 @@ struct rtw89_btc_ver { u8 fcxosi; u8 fcxmlo; u8 bt_desired; + u8 fcxtrx; }; struct rtw89_btc_btf_fwinfo { @@ -3424,6 +3429,7 @@ struct rtw89_btc { bool update_policy_force; bool lps; bool manual_ctrl; + bool cli_h2c_cmd; }; enum rtw89_btc_hmsg { @@ -4772,10 +4778,10 @@ struct rtw89_chip_info { u8 mon_reg_num; const struct rtw89_btc_fbtc_mreg *mon_reg; - u8 rf_para_ulink_num; - const struct rtw89_btc_rf_trx_para *rf_para_ulink; - u8 rf_para_dlink_num; - const struct rtw89_btc_rf_trx_para *rf_para_dlink; + const struct rtw89_btc_rf_trx_para_v0 *rf_para_ulink_v0; + const struct rtw89_btc_rf_trx_para_v0 *rf_para_dlink_v0; + u8 rf_para_ulink_num_v0; + u8 rf_para_dlink_num_v0; const struct rtw89_btc_rf_trx_para_v9 *rf_para_ulink_v9; const struct rtw89_btc_rf_trx_para_v9 *rf_para_dlink_v9; u8 rf_para_ulink_num_v9; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 27b52845c1dd..a20bc9aa5b26 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6318,48 +6318,158 @@ int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type) return ret; } -#define H2C_LEN_CXDRVINFO_TRX (28 + H2C_LEN_CXDRVHDR) -int rtw89_fw_h2c_cxdrv_trx(struct rtw89_dev *rtwdev, u8 type) +int rtw89_fw_h2c_cxdrv_trx_v7(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_rf_trx_para_v9 rf_para = btc->dm.rf_trx_para; struct rtw89_btc_trx_info *trx = &btc->dm.trx_info; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_h2c_cxtrx_v7 *h2c; + u32 len = sizeof(*h2c); struct sk_buff *skb; - u8 *cmd; int ret; + u8 i; - skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_LEN_CXDRVINFO_TRX); + for (i = 0; i < RTW89_PHY_NUM; i++) { + trx->wl_tx_power[i] = u32_get_bits(rf_para.wl_tx_power[i], + RTW89_BTC_WL_DEF_TX_PWR); + trx->wl_rx_gain[i] = u32_get_bits(rf_para.wl_rx_gain[i], + RTW89_BTC_WL_DEF_TX_PWR); + } + for (i = 0; i < BTC_ALL_BT; i++) { + trx->bt_tx_power[i] = u32_get_bits(rf_para.bt_tx_power[i], + RTW89_BTC_WL_DEF_TX_PWR); + trx->bt_rx_gain[i] = u32_get_bits(rf_para.bt_rx_gain[i], + RTW89_BTC_WL_DEF_TX_PWR); + trx->zb_tx_power[i] = u32_get_bits(rf_para.zb_tx_power[i], + RTW89_BTC_WL_DEF_TX_PWR); + trx->zb_rx_gain[i] = u32_get_bits(rf_para.zb_rx_gain[i], + RTW89_BTC_WL_DEF_TX_PWR); + } + trx->cn = wl->cn_report; + trx->nhm = wl->nhm.pwr; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { - rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_trx\n"); + rtw89_err(rtwdev, "failed to alloc skb for h2c cxtrx_v9\n"); return -ENOMEM; } - skb_put(skb, H2C_LEN_CXDRVINFO_TRX); - cmd = skb->data; + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxtrx_v7 *)skb->data; - RTW89_SET_FWCMD_CXHDR_TYPE(cmd, type); - RTW89_SET_FWCMD_CXHDR_LEN(cmd, H2C_LEN_CXDRVINFO_TRX - H2C_LEN_CXDRVHDR); + h2c->hdr.type = type; + h2c->hdr.ver = btc->ver->fcxtrx; + h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7; - RTW89_SET_FWCMD_CXTRX_TXLV(cmd, trx->tx_lvl); - RTW89_SET_FWCMD_CXTRX_RXLV(cmd, trx->rx_lvl); - RTW89_SET_FWCMD_CXTRX_WLRSSI(cmd, trx->wl_rssi); - RTW89_SET_FWCMD_CXTRX_BTRSSI(cmd, trx->bt_rssi); - RTW89_SET_FWCMD_CXTRX_TXPWR(cmd, trx->tx_power); - RTW89_SET_FWCMD_CXTRX_RXGAIN(cmd, trx->rx_gain); - RTW89_SET_FWCMD_CXTRX_BTTXPWR(cmd, trx->bt_tx_power); - RTW89_SET_FWCMD_CXTRX_BTRXGAIN(cmd, trx->bt_rx_gain); - RTW89_SET_FWCMD_CXTRX_CN(cmd, trx->cn); - RTW89_SET_FWCMD_CXTRX_NHM(cmd, trx->nhm); - RTW89_SET_FWCMD_CXTRX_BTPROFILE(cmd, trx->bt_profile); - RTW89_SET_FWCMD_CXTRX_RSVD2(cmd, trx->rsvd2); - RTW89_SET_FWCMD_CXTRX_TXRATE(cmd, trx->tx_rate); - RTW89_SET_FWCMD_CXTRX_RXRATE(cmd, trx->rx_rate); - RTW89_SET_FWCMD_CXTRX_TXTP(cmd, trx->tx_tp); - RTW89_SET_FWCMD_CXTRX_RXTP(cmd, trx->rx_tp); - RTW89_SET_FWCMD_CXTRX_RXERRRA(cmd, trx->rx_err_ratio); + h2c->v7_u8.tx_lvl = trx->tx_lvl; + h2c->v7_u8.rx_lvl = trx->rx_lvl; + h2c->v7_u8.wl_rssi = trx->wl_rssi; + h2c->v7_u8.bt_rssi = trx->bt_rssi; + h2c->v7_u8.wl_tx_power = trx->wl_tx_power[RTW89_PHY_0]; + h2c->v7_u8.wl_rx_gain = trx->wl_rx_gain[RTW89_PHY_0]; + h2c->v7_u8.bt_tx_power = trx->bt_tx_power[BTC_BT_1ST]; + h2c->v7_u8.bt_rx_gain = trx->bt_rx_gain[BTC_BT_1ST]; + h2c->v7_u8.zb_tx_power = trx->zb_tx_power[BTC_BT_1ST]; + h2c->v7_u8.zb_rx_gain = trx->zb_rx_gain[BTC_BT_1ST]; + h2c->v7_u8.cn = trx->cn; + h2c->v7_u8.nhm = trx->nhm; + h2c->v7_u8.bt_profile = trx->bt_profile; + h2c->v7_u8.rsvd2 = trx->rsvd2; + h2c->v7_le.tx_rate = cpu_to_le16(trx->tx_rate); + h2c->v7_le.rx_rate = cpu_to_le16(trx->rx_rate); + h2c->v7_le.tx_tp = cpu_to_le32(trx->tx_tp); + h2c->v7_le.rx_tp = cpu_to_le32(trx->rx_tp); + h2c->v7_le.rx_err_ratio = cpu_to_le32(trx->rx_err_ratio); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, BTFC_SET, - SET_DRV_INFO, 0, 0, - H2C_LEN_CXDRVINFO_TRX); + 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_trx_v9(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_rf_trx_para_v9 rf_para = btc->dm.rf_trx_para; + struct rtw89_btc_trx_info *trx = &btc->dm.trx_info; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_h2c_cxtrx_v9 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + u8 i; + + for (i = 0; i < RTW89_PHY_NUM; i++) { + trx->wl_tx_power[i] = u32_get_bits(rf_para.wl_tx_power[i], + RTW89_BTC_WL_DEF_TX_PWR); + trx->wl_rx_gain[i] = u32_get_bits(rf_para.wl_rx_gain[i], + RTW89_BTC_WL_DEF_TX_PWR); + } + for (i = 0; i < BTC_ALL_BT; i++) { + trx->bt_tx_power[i] = u32_get_bits(rf_para.bt_tx_power[i], + RTW89_BTC_WL_DEF_TX_PWR); + trx->bt_rx_gain[i] = u32_get_bits(rf_para.bt_rx_gain[i], + RTW89_BTC_WL_DEF_TX_PWR); + trx->zb_tx_power[i] = u32_get_bits(rf_para.zb_tx_power[i], + RTW89_BTC_WL_DEF_TX_PWR); + trx->zb_rx_gain[i] = u32_get_bits(rf_para.zb_rx_gain[i], + RTW89_BTC_WL_DEF_TX_PWR); + } + trx->cn = wl->cn_report; + trx->nhm = wl->nhm.pwr; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxtrx_v9\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxtrx_v9 *)skb->data; + + h2c->hdr.type = type; + h2c->hdr.ver = btc->ver->fcxtrx; + h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7; + + h2c->v9_u8.tx_lvl = trx->tx_lvl; + h2c->v9_u8.rx_lvl = trx->rx_lvl; + h2c->v9_u8.wl_rssi = trx->wl_rssi; + h2c->v9_u8.bt_rssi = trx->bt_rssi; + + for (i = 0; i < RTW89_PHY_NUM; i++) { + h2c->v9_u8.wl_tx_power[i] = trx->wl_tx_power[i]; + h2c->v9_u8.wl_rx_gain[i] = trx->wl_rx_gain[i]; + } + + for (i = 0; i < BTC_ALL_BT; i++) { + h2c->v9_u8.bt_tx_power[i] = trx->bt_tx_power[i]; + h2c->v9_u8.bt_rx_gain[i] = trx->bt_rx_gain[i]; + h2c->v9_u8.zb_tx_power[i] = trx->zb_tx_power[i]; + h2c->v9_u8.zb_rx_gain[i] = trx->zb_rx_gain[i]; + } + h2c->v9_u8.cn = trx->cn; + h2c->v9_u8.nhm = trx->nhm; + h2c->v9_u8.bt_profile = trx->bt_profile; + h2c->v9_u8.rsvd2 = trx->rsvd2; + h2c->v9_le.tx_rate = cpu_to_le16(trx->tx_rate); + h2c->v9_le.rx_rate = cpu_to_le16(trx->rx_rate); + h2c->v9_le.tx_tp = cpu_to_le32(trx->tx_tp); + h2c->v9_le.rx_tp = cpu_to_le32(trx->rx_tp); + h2c->v9_le.rx_err_ratio = cpu_to_le32(trx->rx_err_ratio); + + 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) { @@ -6419,6 +6529,90 @@ int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev, u8 type) return ret; } +int rtw89_fw_h2c_cxtxpwr_v7(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_rf_trx_para_v9 rp = dm->rf_trx_para; + struct rtw89_h2c_cxtxpwr_v7 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_ctrl\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxtxpwr_v7 *)skb->data; + + h2c->hdr.type = type; + h2c->hdr.ver = btc->ver->fcxtrx; + h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7; + h2c->pwr = rp.wl_tx_power[RTW89_PHY_0] & 0xff; + + 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_cxtxpwr_v9(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_rf_trx_para_v9 rp = dm->rf_trx_para; + struct rtw89_h2c_cxtxpwr_v9 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_ctrl\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxtxpwr_v9 *)skb->data; + + h2c->hdr.type = type; + h2c->hdr.ver = btc->ver->fcxtrx; + h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7; + if (dm->wl_tx_pwr_phy_map == BIT(RTW89_PHY_1)) + h2c->pwr = rp.wl_tx_power[RTW89_PHY_1] & 0xff; + else + h2c->pwr = rp.wl_tx_power[RTW89_PHY_0] & 0xff; + h2c->band = dm->wl_tx_pwr_phy_map; + + 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; +} + #define H2C_LEN_PKT_OFLD 4 int rtw89_fw_h2c_del_pkt_offload(struct rtw89_dev *rtwdev, u8 id) { diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 6cb99144e541..79e9af3a6805 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2499,6 +2499,76 @@ struct rtw89_h2c_cxinit { u8 rsvd1; } __packed; +struct rtw89_btc_trx_info_u8 { + u8 tx_lvl; + u8 rx_lvl; + u8 wl_rssi; + u8 bt_rssi; + + s8 wl_tx_power[RTW89_PHY_NUM]; /* absolute Tx power (dBm), 0xff-> no BTC control */ + s8 wl_rx_gain[RTW89_PHY_NUM]; /* rx gain table index (TBD.) */ + s8 bt_tx_power[BTC_ALL_BT]; /* decrease Tx power (dB) */ + s8 bt_rx_gain[BTC_ALL_BT]; /* LNA constrain level */ + s8 zb_tx_power[BTC_ALL_BT]; + s8 zb_rx_gain[BTC_ALL_BT]; + + u8 cn; /* condition_num */ + s8 nhm; + u8 bt_profile; + u8 rsvd2; +} __packed; + +struct rtw89_btc_trx_info_v7_u8 { + u8 tx_lvl; + u8 rx_lvl; + u8 wl_rssi; + u8 bt_rssi; + + s8 wl_tx_power; + s8 wl_rx_gain; + s8 bt_tx_power; + s8 bt_rx_gain; + s8 zb_tx_power; + s8 zb_rx_gain; + + u8 cn; + s8 nhm; + u8 bt_profile; + u8 rsvd2; +} __packed; + +struct rtw89_btc_trx_info_le { + __le16 tx_rate; + __le16 rx_rate; + + __le32 tx_tp; + __le32 rx_tp; + __le32 rx_err_ratio; +} __packed; + +struct rtw89_h2c_cxtrx_v9 { + struct rtw89_h2c_cxhdr_v7 hdr; + struct rtw89_btc_trx_info_u8 v9_u8; + struct rtw89_btc_trx_info_le v9_le; +} __packed; + +struct rtw89_h2c_cxtrx_v7 { + struct rtw89_h2c_cxhdr_v7 hdr; + struct rtw89_btc_trx_info_v7_u8 v7_u8; + struct rtw89_btc_trx_info_le v7_le; +} __packed; + +struct rtw89_h2c_cxtxpwr_v7 { + struct rtw89_h2c_cxhdr_v7 hdr; + u8 pwr; +} __packed; + +struct rtw89_h2c_cxtxpwr_v9 { + struct rtw89_h2c_cxhdr_v7 hdr; + u8 pwr; + u8 band; +} __packed; + #define RTW89_H2C_CXINIT_ANT_INFO_POS BIT(0) #define RTW89_H2C_CXINIT_ANT_INFO_DIVERSITY BIT(1) #define RTW89_H2C_CXINIT_ANT_INFO_BTG_POS GENMASK(3, 2) @@ -2760,91 +2830,6 @@ static inline void RTW89_SET_FWCMD_CXCTRL_TRACE_STEP(void *cmd, u32 val) le32p_replace_bits((__le32 *)((u8 *)(cmd) + 2), val, GENMASK(18, 3)); } -static inline void RTW89_SET_FWCMD_CXTRX_TXLV(void *cmd, u8 val) -{ - u8p_replace_bits((u8 *)cmd + 2, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_RXLV(void *cmd, u8 val) -{ - u8p_replace_bits((u8 *)cmd + 3, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_WLRSSI(void *cmd, u8 val) -{ - u8p_replace_bits((u8 *)cmd + 4, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_BTRSSI(void *cmd, u8 val) -{ - u8p_replace_bits((u8 *)cmd + 5, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_TXPWR(void *cmd, s8 val) -{ - u8p_replace_bits((u8 *)cmd + 6, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_RXGAIN(void *cmd, s8 val) -{ - u8p_replace_bits((u8 *)cmd + 7, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_BTTXPWR(void *cmd, s8 val) -{ - u8p_replace_bits((u8 *)cmd + 8, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_BTRXGAIN(void *cmd, s8 val) -{ - u8p_replace_bits((u8 *)cmd + 9, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_CN(void *cmd, u8 val) -{ - u8p_replace_bits((u8 *)cmd + 10, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_NHM(void *cmd, s8 val) -{ - u8p_replace_bits((u8 *)cmd + 11, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_BTPROFILE(void *cmd, u8 val) -{ - u8p_replace_bits((u8 *)cmd + 12, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_RSVD2(void *cmd, u8 val) -{ - u8p_replace_bits((u8 *)cmd + 13, val, GENMASK(7, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_TXRATE(void *cmd, u16 val) -{ - le16p_replace_bits((__le16 *)((u8 *)cmd + 14), val, GENMASK(15, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_RXRATE(void *cmd, u16 val) -{ - le16p_replace_bits((__le16 *)((u8 *)cmd + 16), val, GENMASK(15, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_TXTP(void *cmd, u32 val) -{ - le32p_replace_bits((__le32 *)((u8 *)cmd + 18), val, GENMASK(31, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_RXTP(void *cmd, u32 val) -{ - le32p_replace_bits((__le32 *)((u8 *)cmd + 22), val, GENMASK(31, 0)); -} - -static inline void RTW89_SET_FWCMD_CXTRX_RXERRRA(void *cmd, u32 val) -{ - le32p_replace_bits((__le32 *)((u8 *)cmd + 26), val, GENMASK(31, 0)); -} - static inline void RTW89_SET_FWCMD_CXRFK_STATE(void *cmd, u32 val) { le32p_replace_bits((__le32 *)((u8 *)(cmd) + 2), val, GENMASK(1, 0)); @@ -5401,8 +5386,11 @@ int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type); -int rtw89_fw_h2c_cxdrv_trx(struct rtw89_dev *rtwdev, u8 type); +int rtw89_fw_h2c_cxdrv_trx_v7(struct rtw89_dev *rtwdev, u8 type); +int rtw89_fw_h2c_cxdrv_trx_v9(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev, u8 type); +int rtw89_fw_h2c_cxtxpwr_v7(struct rtw89_dev *rtwdev, u8 type); +int rtw89_fw_h2c_cxtxpwr_v9(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_del_pkt_offload(struct rtw89_dev *rtwdev, u8 id); int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, struct sk_buff *skb_ofld); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 60f362593696..4caf231c6287 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -355,7 +355,7 @@ static const struct rtw89_pmac_regs rtw8851b_pmac_regs = { .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, }; -static const struct rtw89_btc_rf_trx_para rtw89_btc_8851b_rf_ul[] = { +static const struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8851b_rf_ul_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -367,7 +367,7 @@ static const struct rtw89_btc_rf_trx_para rtw89_btc_8851b_rf_ul[] = { {13, 1, 0, 7} }; -static const struct rtw89_btc_rf_trx_para rtw89_btc_8851b_rf_dl[] = { +static const struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8851b_rf_dl_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> reserved for shared-antenna */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -2730,10 +2730,10 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .rssi_tol = 2, .mon_reg_num = ARRAY_SIZE(rtw89_btc_8851b_mon_reg), .mon_reg = rtw89_btc_8851b_mon_reg, - .rf_para_ulink_num = ARRAY_SIZE(rtw89_btc_8851b_rf_ul), - .rf_para_ulink = rtw89_btc_8851b_rf_ul, - .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8851b_rf_dl), - .rf_para_dlink = rtw89_btc_8851b_rf_dl, + .rf_para_ulink_v0 = rtw89_btc_8851b_rf_ul_v0, + .rf_para_dlink_v0 = rtw89_btc_8851b_rf_dl_v0, + .rf_para_ulink_num_v0 = ARRAY_SIZE(rtw89_btc_8851b_rf_ul_v0), + .rf_para_dlink_num_v0 = ARRAY_SIZE(rtw89_btc_8851b_rf_dl_v0), .rf_para_ulink_v9 = NULL, .rf_para_dlink_v9 = NULL, .rf_para_ulink_num_v9 = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 94027e5b8d28..78addc0aef69 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2049,7 +2049,7 @@ s8 rtw8852a_btc_get_bt_rssi(struct rtw89_dev *rtwdev, s8 val) return clamp_t(s8, val + 6, -100, 0) + 100; } -static struct rtw89_btc_rf_trx_para rtw89_btc_8852a_rf_ul[] = { +static struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8852a_rf_ul_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -2061,7 +2061,7 @@ static struct rtw89_btc_rf_trx_para rtw89_btc_8852a_rf_ul[] = { {13, 1, 0, 7} }; -static struct rtw89_btc_rf_trx_para rtw89_btc_8852a_rf_dl[] = { +static struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8852a_rf_dl_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> reserved for shared-antenna */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -2468,10 +2468,10 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .rssi_tol = 2, .mon_reg_num = ARRAY_SIZE(rtw89_btc_8852a_mon_reg), .mon_reg = rtw89_btc_8852a_mon_reg, - .rf_para_ulink_num = ARRAY_SIZE(rtw89_btc_8852a_rf_ul), - .rf_para_ulink = rtw89_btc_8852a_rf_ul, - .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8852a_rf_dl), - .rf_para_dlink = rtw89_btc_8852a_rf_dl, + .rf_para_ulink_v0 = rtw89_btc_8852a_rf_ul_v0, + .rf_para_dlink_v0 = rtw89_btc_8852a_rf_dl_v0, + .rf_para_ulink_num_v0 = ARRAY_SIZE(rtw89_btc_8852a_rf_ul_v0), + .rf_para_dlink_num_v0 = ARRAY_SIZE(rtw89_btc_8852a_rf_dl_v0), .rf_para_ulink_v9 = NULL, .rf_para_dlink_v9 = NULL, .rf_para_ulink_num_v9 = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 4e7b068aaa75..debcdb2eacd6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -307,7 +307,7 @@ static const struct rtw89_pmac_regs rtw8852b_pmac_regs = { .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, }; -static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_ul[] = { +static const struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8852b_rf_ul_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -319,7 +319,7 @@ static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_ul[] = { {13, 1, 0, 7} }; -static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_dl[] = { +static const struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8852b_rf_dl_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> reserved for shared-antenna */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -1063,10 +1063,10 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .rssi_tol = 2, .mon_reg_num = ARRAY_SIZE(rtw89_btc_8852b_mon_reg), .mon_reg = rtw89_btc_8852b_mon_reg, - .rf_para_ulink_num = ARRAY_SIZE(rtw89_btc_8852b_rf_ul), - .rf_para_ulink = rtw89_btc_8852b_rf_ul, - .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8852b_rf_dl), - .rf_para_dlink = rtw89_btc_8852b_rf_dl, + .rf_para_ulink_v0 = rtw89_btc_8852b_rf_ul_v0, + .rf_para_dlink_v0 = rtw89_btc_8852b_rf_dl_v0, + .rf_para_ulink_num_v0 = ARRAY_SIZE(rtw89_btc_8852b_rf_ul_v0), + .rf_para_dlink_num_v0 = ARRAY_SIZE(rtw89_btc_8852b_rf_dl_v0), .rf_para_ulink_v9 = NULL, .rf_para_dlink_v9 = NULL, .rf_para_ulink_num_v9 = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 7fcc877f6ea0..fc8a17fb95f4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -250,7 +250,7 @@ static const struct rtw89_pmac_regs rtw8852bt_pmac_regs = { .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, }; -static const struct rtw89_btc_rf_trx_para rtw89_btc_8852bt_rf_ul[] = { +static const struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8852bt_rf_ul_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -262,7 +262,7 @@ static const struct rtw89_btc_rf_trx_para rtw89_btc_8852bt_rf_ul[] = { {13, 1, 0, 7} }; -static const struct rtw89_btc_rf_trx_para rtw89_btc_8852bt_rf_dl[] = { +static const struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8852bt_rf_dl_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> reserved for shared-antenna */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -902,10 +902,10 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .rssi_tol = 2, .mon_reg_num = ARRAY_SIZE(rtw89_btc_8852bt_mon_reg), .mon_reg = rtw89_btc_8852bt_mon_reg, - .rf_para_ulink_num = ARRAY_SIZE(rtw89_btc_8852bt_rf_ul), - .rf_para_ulink = rtw89_btc_8852bt_rf_ul, - .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8852bt_rf_dl), - .rf_para_dlink = rtw89_btc_8852bt_rf_dl, + .rf_para_ulink_v0 = rtw89_btc_8852bt_rf_ul_v0, + .rf_para_dlink_v0 = rtw89_btc_8852bt_rf_dl_v0, + .rf_para_ulink_num_v0 = ARRAY_SIZE(rtw89_btc_8852bt_rf_ul_v0), + .rf_para_dlink_num_v0 = ARRAY_SIZE(rtw89_btc_8852bt_rf_dl_v0), .rf_para_ulink_v9 = NULL, .rf_para_dlink_v9 = NULL, .rf_para_ulink_num_v9 = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 80821885b08e..29a3c90021f3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2863,7 +2863,7 @@ s8 rtw8852c_btc_get_bt_rssi(struct rtw89_dev *rtwdev, s8 val) return clamp_t(s8, val + 6, -100, 0) + 100; } -static const struct rtw89_btc_rf_trx_para rtw89_btc_8852c_rf_ul[] = { +static const struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8852c_rf_ul_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -2875,7 +2875,7 @@ static const struct rtw89_btc_rf_trx_para rtw89_btc_8852c_rf_ul[] = { {13, 1, 0, 7} }; -static const struct rtw89_btc_rf_trx_para rtw89_btc_8852c_rf_dl[] = { +static const struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8852c_rf_dl_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> reserved for shared-antenna */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -3270,10 +3270,10 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .rssi_tol = 2, .mon_reg_num = ARRAY_SIZE(rtw89_btc_8852c_mon_reg), .mon_reg = rtw89_btc_8852c_mon_reg, - .rf_para_ulink_num = ARRAY_SIZE(rtw89_btc_8852c_rf_ul), - .rf_para_ulink = rtw89_btc_8852c_rf_ul, - .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8852c_rf_dl), - .rf_para_dlink = rtw89_btc_8852c_rf_dl, + .rf_para_ulink_v0 = rtw89_btc_8852c_rf_ul_v0, + .rf_para_dlink_v0 = rtw89_btc_8852c_rf_dl_v0, + .rf_para_ulink_num_v0 = ARRAY_SIZE(rtw89_btc_8852c_rf_ul_v0), + .rf_para_dlink_num_v0 = ARRAY_SIZE(rtw89_btc_8852c_rf_dl_v0), .rf_para_ulink_v9 = NULL, .rf_para_dlink_v9 = NULL, .rf_para_ulink_num_v9 = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 91897aeced28..6d4301661b04 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2874,7 +2874,7 @@ s8 rtw8922a_btc_get_bt_rssi(struct rtw89_dev *rtwdev, s8 val) return clamp_t(s8, val, -100, 0) + 100; } -static const struct rtw89_btc_rf_trx_para rtw89_btc_8922a_rf_ul[] = { +static const struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8922a_rf_ul_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -2886,7 +2886,7 @@ static const struct rtw89_btc_rf_trx_para rtw89_btc_8922a_rf_ul[] = { {13, 1, 0, 7} }; -static const struct rtw89_btc_rf_trx_para rtw89_btc_8922a_rf_dl[] = { +static const struct rtw89_btc_rf_trx_para_v0 rtw89_btc_8922a_rf_dl_v0[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> reserved for shared-antenna */ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ @@ -3254,10 +3254,10 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .rssi_tol = 2, .mon_reg_num = ARRAY_SIZE(rtw89_btc_8922a_mon_reg), .mon_reg = rtw89_btc_8922a_mon_reg, - .rf_para_ulink_num = ARRAY_SIZE(rtw89_btc_8922a_rf_ul), - .rf_para_ulink = rtw89_btc_8922a_rf_ul, - .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8922a_rf_dl), - .rf_para_dlink = rtw89_btc_8922a_rf_dl, + .rf_para_ulink_v0 = rtw89_btc_8922a_rf_ul_v0, + .rf_para_dlink_v0 = rtw89_btc_8922a_rf_dl_v0, + .rf_para_ulink_num_v0 = ARRAY_SIZE(rtw89_btc_8922a_rf_ul_v0), + .rf_para_dlink_num_v0 = ARRAY_SIZE(rtw89_btc_8922a_rf_dl_v0), .rf_para_ulink_v9 = NULL, .rf_para_dlink_v9 = NULL, .rf_para_ulink_num_v9 = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 888973f4ef95..f78d6d46e65f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -3430,6 +3430,10 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .rssi_tol = 2, .mon_reg_num = ARRAY_SIZE(rtw89_btc_8922d_mon_reg), .mon_reg = rtw89_btc_8922d_mon_reg, + .rf_para_ulink_v0 = NULL, + .rf_para_dlink_v0 = NULL, + .rf_para_ulink_num_v0 = 0, + .rf_para_dlink_num_v0 = 0, .rf_para_ulink_v9 = rtw89_btc_8922d_rf_ul_v9, .rf_para_dlink_v9 = rtw89_btc_8922d_rf_dl_v9, .rf_para_ulink_num_v9 = ARRAY_SIZE(rtw89_btc_8922d_rf_ul_v9), From 600649fa9c10e517818bdf42c911fe207bbc243f Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 24 Jun 2026 11:39:39 +0800 Subject: [PATCH 021/403] wifi: rtw89: coex: Renaming drvinfo_type to drvinfo_ver It's more closing to the original meaning. It is defined for rearranging driver info index by firmware support version. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260624033941.45918-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 84 +++++++++++------------ drivers/net/wireless/realtek/rtw89/core.h | 2 +- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index dd9d6cbc2943..572eed7939e1 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -138,7 +138,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, .fwlrole = 7, .frptmap = 3, .fcxctrl = 7, .fcxinit = 7, - .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800, + .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_ver = 1, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 8, .fcxtrx = 0, }, @@ -147,7 +147,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, .fwlrole = 7, .frptmap = 3, .fcxctrl = 7, .fcxinit = 7, - .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800, + .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_ver = 1, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 8, .fcxtrx = 0, }, @@ -156,7 +156,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, .fwlrole = 8, .frptmap = 4, .fcxctrl = 7, .fcxinit = 7, - .fwevntrptl = 1, .fwc2hfunc = 3, .drvinfo_type = 2, .info_buf = 1800, + .fwevntrptl = 1, .fwc2hfunc = 3, .drvinfo_ver = 2, .info_buf = 1800, .max_role_num = 6, .fcxosi = 1, .fcxmlo = 1, .bt_desired = 9, .fcxtrx = 7, }, @@ -165,7 +165,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, .fwlrole = 8, .frptmap = 4, .fcxctrl = 7, .fcxinit = 7, - .fwevntrptl = 1, .fwc2hfunc = 3, .drvinfo_type = 2, .info_buf = 1800, + .fwevntrptl = 1, .fwc2hfunc = 3, .drvinfo_ver = 2, .info_buf = 1800, .max_role_num = 6, .fcxosi = 1, .fcxmlo = 1, .bt_desired = 9, .fcxtrx = 0, }, @@ -174,7 +174,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, .fwlrole = 8, .frptmap = 3, .fcxctrl = 7, .fcxinit = 7, - .fwevntrptl = 1, .fwc2hfunc = 1, .drvinfo_type = 1, .info_buf = 1800, + .fwevntrptl = 1, .fwc2hfunc = 1, .drvinfo_ver = 1, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, @@ -183,7 +183,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 3, .fcxnullsta = 2, .fcxmreg = 2, .fcxgpiodbg = 1, .fcxbtver = 1, .fcxbtscan = 2, .fcxbtafh = 2, .fcxbtdevinfo = 1, .fwlrole = 2, .frptmap = 3, .fcxctrl = 1, .fcxinit = 0, - .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1800, + .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_ver = 0, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, @@ -192,7 +192,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 3, .fcxnullsta = 2, .fcxmreg = 1, .fcxgpiodbg = 1, .fcxbtver = 1, .fcxbtscan = 1, .fcxbtafh = 2, .fcxbtdevinfo = 1, .fwlrole = 1, .frptmap = 3, .fcxctrl = 1, .fcxinit = 0, - .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, + .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_ver = 0, .info_buf = 1280, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, @@ -201,7 +201,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 3, .fcxnullsta = 2, .fcxmreg = 1, .fcxgpiodbg = 1, .fcxbtver = 1, .fcxbtscan = 1, .fcxbtafh = 2, .fcxbtdevinfo = 1, .fwlrole = 1, .frptmap = 2, .fcxctrl = 1, .fcxinit = 0, - .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, + .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_ver = 0, .info_buf = 1280, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, @@ -210,7 +210,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 3, .fcxnullsta = 2, .fcxmreg = 1, .fcxgpiodbg = 1, .fcxbtver = 1, .fcxbtscan = 1, .fcxbtafh = 1, .fcxbtdevinfo = 1, .fwlrole = 1, .frptmap = 2, .fcxctrl = 1, .fcxinit = 0, - .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, + .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_ver = 0, .info_buf = 1280, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, @@ -219,7 +219,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, .fwlrole = 7, .frptmap = 3, .fcxctrl = 7, .fcxinit = 7, - .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800, + .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_ver = 1, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 8, .fcxtrx = 0, }, @@ -228,7 +228,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 3, .fcxnullsta = 2, .fcxmreg = 2, .fcxgpiodbg = 1, .fcxbtver = 1, .fcxbtscan = 2, .fcxbtafh = 2, .fcxbtdevinfo = 1, .fwlrole = 2, .frptmap = 3, .fcxctrl = 1, .fcxinit = 0, - .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1800, + .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_ver = 0, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, @@ -237,7 +237,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 3, .fcxnullsta = 2, .fcxmreg = 1, .fcxgpiodbg = 1, .fcxbtver = 1, .fcxbtscan = 1, .fcxbtafh = 2, .fcxbtdevinfo = 1, .fwlrole = 1, .frptmap = 3, .fcxctrl = 1, .fcxinit = 0, - .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1800, + .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_ver = 0, .info_buf = 1800, .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, @@ -246,7 +246,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 3, .fcxnullsta = 2, .fcxmreg = 1, .fcxgpiodbg = 1, .fcxbtver = 1, .fcxbtscan = 1, .fcxbtafh = 1, .fcxbtdevinfo = 1, .fwlrole = 1, .frptmap = 1, .fcxctrl = 1, .fcxinit = 0, - .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, + .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_ver = 0, .info_buf = 1280, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, @@ -255,7 +255,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 3, .fcxnullsta = 2, .fcxmreg = 1, .fcxgpiodbg = 1, .fcxbtver = 1, .fcxbtscan = 1, .fcxbtafh = 2, .fcxbtdevinfo = 1, .fwlrole = 1, .frptmap = 3, .fcxctrl = 1, .fcxinit = 0, - .fwevntrptl = 0, .fwc2hfunc = 0, .drvinfo_type = 0, .info_buf = 1280, + .fwevntrptl = 0, .fwc2hfunc = 0, .drvinfo_ver = 0, .info_buf = 1280, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, @@ -264,7 +264,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 2, .fcxnullsta = 1, .fcxmreg = 1, .fcxgpiodbg = 1, .fcxbtver = 1, .fcxbtscan = 1, .fcxbtafh = 1, .fcxbtdevinfo = 1, .fwlrole = 0, .frptmap = 0, .fcxctrl = 0, .fcxinit = 0, - .fwevntrptl = 0, .fwc2hfunc = 0, .drvinfo_type = 0, .info_buf = 1024, + .fwevntrptl = 0, .fwc2hfunc = 0, .drvinfo_ver = 0, .info_buf = 1024, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, @@ -275,7 +275,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 2, .fcxnullsta = 1, .fcxmreg = 1, .fcxgpiodbg = 1, .fcxbtver = 1, .fcxbtscan = 1, .fcxbtafh = 1, .fcxbtdevinfo = 1, .fwlrole = 0, .frptmap = 0, .fcxctrl = 0, .fcxinit = 0, - .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1024, + .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_ver = 0, .info_buf = 1024, .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, @@ -2789,62 +2789,62 @@ static void _fw_set_policy(struct rtw89_dev *rtwdev, u16 policy_type, rtw89_set_coex_ctrl_lps(rtwdev, btc->lps); } -static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 type) +static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; - switch (type) { + switch (index) { case CXDRVINFO_INIT: if (ver->fcxinit == 7) - rtw89_fw_h2c_cxdrv_init_v7(rtwdev, type); + rtw89_fw_h2c_cxdrv_init_v7(rtwdev, index); else - rtw89_fw_h2c_cxdrv_init(rtwdev, type); + rtw89_fw_h2c_cxdrv_init(rtwdev, index); break; case CXDRVINFO_ROLE: if (ver->fwlrole == 0) - rtw89_fw_h2c_cxdrv_role(rtwdev, type); + rtw89_fw_h2c_cxdrv_role(rtwdev, index); else if (ver->fwlrole == 1) - rtw89_fw_h2c_cxdrv_role_v1(rtwdev, type); + rtw89_fw_h2c_cxdrv_role_v1(rtwdev, index); else if (ver->fwlrole == 2) - rtw89_fw_h2c_cxdrv_role_v2(rtwdev, type); + rtw89_fw_h2c_cxdrv_role_v2(rtwdev, index); else if (ver->fwlrole == 7) - rtw89_fw_h2c_cxdrv_role_v7(rtwdev, type); + rtw89_fw_h2c_cxdrv_role_v7(rtwdev, index); else if (ver->fwlrole == 8) - rtw89_fw_h2c_cxdrv_role_v8(rtwdev, type); + rtw89_fw_h2c_cxdrv_role_v8(rtwdev, index); break; case CXDRVINFO_CTRL: - if (ver->drvinfo_type == 1) - type = 2; + if (ver->drvinfo_ver == 1) + index = 2; if (ver->fcxctrl == 7) - rtw89_fw_h2c_cxdrv_ctrl_v7(rtwdev, type); + rtw89_fw_h2c_cxdrv_ctrl_v7(rtwdev, index); else - rtw89_fw_h2c_cxdrv_ctrl(rtwdev, type); + rtw89_fw_h2c_cxdrv_ctrl(rtwdev, index); break; case CXDRVINFO_TRX: - if (ver->drvinfo_type == 1) - type = 3; + if (ver->drvinfo_ver == 1) + index = 3; if (ver->fcxtrx == 7) - rtw89_fw_h2c_cxdrv_trx_v7(rtwdev, type); + rtw89_fw_h2c_cxdrv_trx_v7(rtwdev, index); else if (ver->fcxtrx == 9) - rtw89_fw_h2c_cxdrv_trx_v9(rtwdev, type); + rtw89_fw_h2c_cxdrv_trx_v9(rtwdev, index); break; case CXDRVINFO_RFK: - if (ver->drvinfo_type == 1) + if (ver->drvinfo_ver == 1) return; - rtw89_fw_h2c_cxdrv_rfk(rtwdev, type); + rtw89_fw_h2c_cxdrv_rfk(rtwdev, index); break; case CXDRVINFO_TXPWR: - if (ver->drvinfo_type == 3) - type = 4; + if (ver->drvinfo_ver == 3) + index = 4; if (ver->fcxtrx == 7) - rtw89_fw_h2c_cxtxpwr_v7(rtwdev, type); + rtw89_fw_h2c_cxtxpwr_v7(rtwdev, index); else if (ver->fcxtrx == 9) - rtw89_fw_h2c_cxtxpwr_v9(rtwdev, type); + rtw89_fw_h2c_cxtxpwr_v9(rtwdev, index); break; case CXDRVINFO_FDDT: case CXDRVINFO_MLO: @@ -2852,12 +2852,12 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 type) if (!ver->fcxosi) return; - if (ver->drvinfo_type == 2) - type = 7; + if (ver->drvinfo_ver == 2) + index = 7; else return; - rtw89_fw_h2c_cxdrv_osi_info(rtwdev, type); + rtw89_fw_h2c_cxdrv_osi_info(rtwdev, index); break; default: break; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 01c7d36d8e12..d3a21e9ea883 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3365,7 +3365,7 @@ struct rtw89_btc_ver { u8 fwevntrptl; u8 fwc2hfunc; - u8 drvinfo_type; + u8 drvinfo_ver; u16 info_buf; u8 max_role_num; u8 fcxosi; From 5c071a06bbba0f36806cd3ac4a8bb3403c20fd16 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 24 Jun 2026 11:39:40 +0800 Subject: [PATCH 022/403] wifi: rtw89: coex: Add Wi-Fi firmware 0.35.94.1 support for RTL8922D The firmware 0.35.94.1 included several new features. Wi-Fi TX power setting offload to firmware. Including dual BT / dual Wi-Fi MAC related configurations. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260624033941.45918-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 62 +++++++++++++++++++---- drivers/net/wireless/realtek/rtw89/core.h | 3 ++ 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 572eed7939e1..6f9bb31b5263 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, 94, 0), + .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 = 10, .frptmap = 5, .fcxctrl = 9, .fcxinit = 10, + .fwevntrptl = 1, .fwc2hfunc = 4, .drvinfo_ver = 3, .info_buf = 1800, + .max_role_num = 6, .fcxosi = 6, .fcxmlo = 2, .bt_desired = 8, + .fcxtrx = 9, + }, {RTL8852BT, RTW89_FW_VER_CODE(0, 29, 122, 0), .fcxbtcrpt = 8, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, @@ -156,7 +165,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, .fwlrole = 8, .frptmap = 4, .fcxctrl = 7, .fcxinit = 7, - .fwevntrptl = 1, .fwc2hfunc = 3, .drvinfo_ver = 2, .info_buf = 1800, + .fwevntrptl = 1, .fwc2hfunc = 3, .drvinfo_ver = 3, .info_buf = 1800, .max_role_num = 6, .fcxosi = 1, .fcxmlo = 1, .bt_desired = 9, .fcxtrx = 7, }, @@ -2423,6 +2432,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) break; case 3: case 4: + case 5: bit_map = BIT(5); break; default: @@ -2438,6 +2448,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) break; case 3: case 4: + case 5: bit_map = BIT(6); break; default: @@ -2451,6 +2462,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) break; case 3: case 4: + case 5: bit_map = BIT(7); break; default: @@ -2465,6 +2477,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) case 3: break; case 4: + case 5: bit_map = BIT(8); break; default: @@ -2481,6 +2494,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) bit_map = BIT(8); break; case 4: + case 5: bit_map = BIT(9); break; default: @@ -2488,7 +2502,10 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) } break; case RPT_EN_TEST: - bit_map = BIT(31); + if (ver->frptmap == 5) + bit_map = BIT(10); + else + bit_map = BIT(31); break; case RPT_EN_WL_ALL: switch (ver->frptmap) { @@ -2501,6 +2518,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) bit_map = GENMASK(2, 0) | BIT(8); break; case 4: + case 5: bit_map = GENMASK(2, 0) | BIT(9); break; default: @@ -2520,6 +2538,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) bit_map = GENMASK(7, 3); break; case 4: + case 5: bit_map = GENMASK(8, 3); break; default: @@ -2539,6 +2558,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) bit_map = GENMASK(8, 0); break; case 4: + case 5: bit_map = GENMASK(9, 0); break; default: @@ -2558,6 +2578,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) bit_map = GENMASK(8, 2); break; case 4: + case 5: bit_map = GENMASK(9, 2); break; default: @@ -2814,7 +2835,7 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) rtw89_fw_h2c_cxdrv_role_v8(rtwdev, index); break; case CXDRVINFO_CTRL: - if (ver->drvinfo_ver == 1) + if (ver->drvinfo_ver != 0) index = 2; if (ver->fcxctrl == 7) @@ -2823,7 +2844,7 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) rtw89_fw_h2c_cxdrv_ctrl(rtwdev, index); break; case CXDRVINFO_TRX: - if (ver->drvinfo_ver == 1) + if (ver->drvinfo_ver > 1) index = 3; if (ver->fcxtrx == 7) @@ -2832,7 +2853,7 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) rtw89_fw_h2c_cxdrv_trx_v9(rtwdev, index); break; case CXDRVINFO_RFK: - if (ver->drvinfo_ver == 1) + if (ver->drvinfo_ver != 0) return; rtw89_fw_h2c_cxdrv_rfk(rtwdev, index); @@ -2847,12 +2868,26 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) rtw89_fw_h2c_cxtxpwr_v9(rtwdev, index); break; case CXDRVINFO_FDDT: + if (ver->drvinfo_ver == 3) + index = 5; + else + return; + + rtw89_debug(rtwdev, RTW89_DBG_BTC, "drv_info FDDT index=%d\n", index); + break; case CXDRVINFO_MLO: + if (ver->drvinfo_ver == 3) + index = 6; + else + return; + + rtw89_debug(rtwdev, RTW89_DBG_BTC, "drv_info MLO index=%d\n", index); + break; case CXDRVINFO_OSI: if (!ver->fcxosi) return; - if (ver->drvinfo_ver == 2) + if (ver->drvinfo_ver > 1) index = 7; else return; @@ -8828,7 +8863,7 @@ static u8 rtw89_btc_c2h_get_index_by_ver(struct rtw89_dev *rtwdev, u8 func) return BTF_EVNT_BUF_OVERFLOW; else if (ver->fwc2hfunc == 2) return func; - else if (ver->fwc2hfunc == 3) + else if (ver->fwc2hfunc == 3 || ver->fwc2hfunc == 4) return BTF_EVNT_BUF_OVERFLOW; else return BTF_EVNT_MAX; @@ -8839,19 +8874,26 @@ static u8 rtw89_btc_c2h_get_index_by_ver(struct rtw89_dev *rtwdev, u8 func) return BTF_EVNT_C2H_LOOPBACK; else if (ver->fwc2hfunc == 2) return func; - else if (ver->fwc2hfunc == 3) + else if (ver->fwc2hfunc == 3 || ver->fwc2hfunc == 4) return BTF_EVNT_C2H_LOOPBACK; else return BTF_EVNT_MAX; case BTF_EVNT_C2H_LOOPBACK: if (ver->fwc2hfunc == 2) return func; - else if (ver->fwc2hfunc == 3) + else if (ver->fwc2hfunc == 3 || ver->fwc2hfunc == 4) return BTF_EVNT_BT_LEAUDIO_INFO; else return BTF_EVNT_MAX; case BTF_EVNT_BT_QUERY_TXPWR: - if (ver->fwc2hfunc == 3) + if (ver->fwc2hfunc == 3 || ver->fwc2hfunc == 4) + return func; + else + return BTF_EVNT_MAX; + case BTF_EVNT_ZB_INFO: + case BTF_EVNT_ZB_CH: + case BTF_EVNT_ZB_QUERY_TXPWR: + if (ver->fwc2hfunc == 4) return func; else return BTF_EVNT_MAX; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d3a21e9ea883..5dde620b1e5e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3224,6 +3224,9 @@ enum rtw89_btc_btf_fw_event { BTF_EVNT_BUF_OVERFLOW, BTF_EVNT_C2H_LOOPBACK, BTF_EVNT_BT_QUERY_TXPWR, /* fwc2hfunc > 3 */ + BTF_EVNT_ZB_INFO = 11, + BTF_EVNT_ZB_CH = 12, + BTF_EVNT_ZB_QUERY_TXPWR = 13, BTF_EVNT_MAX, }; From 9a149cf572e90157769ab3a8a3bc8cf5601dcfaa Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 24 Jun 2026 11:39:41 +0800 Subject: [PATCH 023/403] wifi: rtw89: coex: Add RTL8922D chip string Add string for logic using and show logs. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260624033941.45918-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 6f9bb31b5263..1361d4d54528 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -313,6 +313,7 @@ static u32 chip_id_to_bt_rom_code_id(u32 id) case RTL8851B: return 0x8851; case RTL8922A: + case RTL8922D: return 0x8922; default: return 0; @@ -347,6 +348,8 @@ static char *chip_id_str(u32 id) return "RTL8851B"; case RTL8922A: return "RTL8922A"; + case RTL8922D: + return "RTL8922D"; default: return "UNKNOWN"; } From 0819de0fd2906236dd38fdd89e52dcb53cd853b2 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 25 Jun 2026 14:15:36 +0800 Subject: [PATCH 024/403] wifi: rtw89: mac: finish active TX immediately without waiting for DMAC Currently active TX only finishes after ensuring PCIE and DMAC become idle. However, the waiting time might be long. Since the packet is already transmitted over the air, update the registers to finish active TX immediately, regardless of the PCIE/DMAC status. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260625061545.44808-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac_be.c | 3 +++ drivers/net/wireless/realtek/rtw89/reg.h | 28 +++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index f24c119b99f1..70e6c9d21986 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -1196,6 +1196,9 @@ static int scheduler_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_MISC_1, mac_idx); + rtw89_write32_set(rtwdev, reg, B_BE_EN_TX_FINISH_PRD_RESP); + if (chip->chip_id == RTL8922D) { reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_SCH_EXT_CTRL, mac_idx); rtw89_write32_set(rtwdev, reg, B_BE_CWCNT_PLUS_MODE); diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 086ef77ebb9f..bf1c6cb0ae9c 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -6858,6 +6858,34 @@ #define B_BE_MUEDCA_EN_MASK GENMASK(1, 0) #define B_BE_MUEDCA_EN_0 BIT(0) +#define R_BE_MISC_1 0x1037C +#define R_BE_MISC_1_C1 0x1437C +#define B_BE_PPS_REMAIN_TIME_MODE BIT(31) +#define B_BE_PPS_IDLE_SORT_EN BIT(30) +#define B_BE_SR_TXOP_USE_SR_PERIOD_EN BIT(29) +#define B_BE_SCH_CCA_PIFS_CLK_GATING_DIS BIT(28) +#define B_BE_SR_TXOP_EN BIT(27) +#define B_BE_SCH_ABORT_CNT_SIFS_EN BIT(26) +#define B_BE_SCH_ABORT_CNT_TB_EN BIT(25) +#define B_BE_SCH_ABORT_CNT_CTN_EN BIT(24) +#define B_BE_SR_CCA_PER20_BITMAP_EN BIT(23) +#define B_BE_SR_CCA_S80_EN BIT(22) +#define B_BE_SR_CCA_S40_EN BIT(21) +#define B_BE_SR_CCA_S20_EN BIT(20) +#define B_BE_EN_TX_FINISH_PRD_RESP BIT(18) +#define B_BE_RESP_TX_ABORT_NON_IDLE BIT(17) +#define B_BE_RESP_TX_ABORT_QUICK_EN BIT(16) +#define B_BE_PREBKF_CHK_LINK_BUSY BIT(15) +#define B_BE_SCH_MSD_PRD_RST_EDCA_EN BIT(14) +#define B_BE_LINK_BUSY_RST_EDCA_EN_MASK GENMASK(13, 12) +#define B_BE_RX_TSFT_SYNC_BYPASS_FCS BIT(11) +#define B_BE_RX_TSFT_DIFF_THD_MASK GENMASK(10, 8) +#define B_BE_CAL_TBTT_OV_EN BIT(5) +#define B_BE_SUBBCN_MS_CNT_MODE BIT(3) +#define B_BE_CAL_ALWAYS_EN BIT(2) +#define B_BE_SIFS_TIMER_AUTO_RST_EN BIT(1) +#define B_BE_CHK_HAS_SIFS_TX_ABORT BIT(0) + #define R_BE_CTN_DRV_TXEN 0x10398 #define R_BE_CTN_DRV_TXEN_C1 0x14398 #define B_BE_CTN_TXEN_TWT_3 BIT(17) From 14dfbfeba17b98d5cb3a7e31bbff4d21e74302c9 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 25 Jun 2026 14:15:37 +0800 Subject: [PATCH 025/403] wifi: rtw89: mac: pass chip version to firmware Set chip version to register shared with firmware before downloading firmware, so firmware can run proper flow according to the version. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260625061545.44808-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac_be.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 70e6c9d21986..4cb48cf9415a 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -641,6 +641,7 @@ static void set_cpu_en(struct rtw89_dev *rtwdev, bool include_bb) static int wcpu_on(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw) { const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_hal *hal = &rtwdev->hal; u32 val32; int ret; @@ -683,6 +684,8 @@ static int wcpu_on(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw) if (chip->chip_id != RTL8922A) rtw89_write32_set(rtwdev, R_BE_WCPU_FW_CTRL, B_BE_HOST_EXIST); + rtw89_write32_mask(rtwdev, R_BE_WCPU_FW_CTRL, + B_BE_WCPU_ROM_CUT_VAL_MASK, hal->cv + 1); rtw89_write16_mask(rtwdev, R_BE_BOOT_REASON, B_BE_BOOT_REASON_MASK, boot_reason); rtw89_write32_clr(rtwdev, R_BE_PLATFORM_ENABLE, B_BE_WCPU_EN); rtw89_write32_clr(rtwdev, R_BE_PLATFORM_ENABLE, B_BE_HOLD_AFTER_RESET); From e50c0fb7867e6b2b0714c79b8385ab6db2c5567a Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 25 Jun 2026 14:15:38 +0800 Subject: [PATCH 026/403] wifi: rtw89: fw: lower debug level for UDM1 debug register The UDM1 is user define message to record count of H2C command sent by driver and received by firmware. Normally, this value should be zero. Otherwise, throw a warning. For the new chip RTL8922DE, its default value is not zero, causing a warning at first time probe. Since this is a debug purpose and the value will be set to zero right after this checking, lower the debug level. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260625061545.44808-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac_be.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 4cb48cf9415a..d9c93adb58ee 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -652,8 +652,10 @@ static int wcpu_on(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw) } val32 = rtw89_read32(rtwdev, R_BE_UDM1); if (val32) { - rtw89_warn(rtwdev, "[SER] AON L2 Debug register not empty before Boot.\n"); - rtw89_warn(rtwdev, "[SER] %s: R_BE_UDM1 = 0x%x\n", __func__, val32); + rtw89_debug(rtwdev, RTW89_DBG_UNEXP, + "[SER] AON L2 Debug register not empty before Boot.\n"); + rtw89_debug(rtwdev, RTW89_DBG_UNEXP, + "[SER] %s: R_BE_UDM1 = 0x%x\n", __func__, val32); } val32 = rtw89_read32(rtwdev, R_BE_UDM2); if (val32) { From c99498b4cbd75f7e1b45e354c2108f4646c0944b Mon Sep 17 00:00:00 2001 From: Dian-Syuan Yang Date: Thu, 25 Jun 2026 14:15:39 +0800 Subject: [PATCH 027/403] wifi: rtw89: drop packet offload entry on H2C addition failure to avoid scan issue A special case is when C2H done ack has been completed, but the corresponding packet offload response has not actually been received, which causes the add packet offload to fail. In this state, firmware treats the entry as added, so subsequent add requests for the same id are rejected as duplicates. To recover from this, send a delete packet offload H2C command to roll back the normal state. It has been tested and verified to have no functional side effect. Signed-off-by: Dian-Syuan Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260625061545.44808-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 26 ++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index a20bc9aa5b26..ac8e0e034a59 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6652,8 +6652,8 @@ int rtw89_fw_h2c_del_pkt_offload(struct rtw89_dev *rtwdev, u8 id) return 0; } -int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, - struct sk_buff *skb_ofld) +static int __rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, + struct sk_buff *skb_ofld) { struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait; struct sk_buff *skb; @@ -6695,13 +6695,33 @@ int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, rtw89_debug(rtwdev, RTW89_DBG_FW, "failed to add pkt ofld: id %d, ret %d\n", alloc_id, ret); + /* + * Firmware may consider that it has added this entry + * successfully even though the H2C return timeout. + * Send a delete H2C command to drop it, and thus the + * next add on the same id won't be rejected as duplicate. + */ + rtw89_fw_h2c_del_pkt_offload(rtwdev, alloc_id); rtw89_core_release_bit_map(rtwdev->pkt_offload, alloc_id); - return ret; + + return -EAGAIN; } return 0; } +int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, + struct sk_buff *skb_ofld) +{ + int ret; + + ret = __rtw89_fw_h2c_add_pkt_offload(rtwdev, id, skb_ofld); + if (ret == -EAGAIN) + ret = __rtw89_fw_h2c_add_pkt_offload(rtwdev, id, skb_ofld); + + return ret; +} + static int rtw89_fw_h2c_scan_list_offload_ax(struct rtw89_dev *rtwdev, int ch_num, struct list_head *chan_list) From b993046234fc3110f5ca3eb561492f8072ccc689 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Thu, 25 Jun 2026 14:15:40 +0800 Subject: [PATCH 028/403] wifi: rtw89: disable sniffer mode in RX filter when initialization for Wi-Fi 7 chips Sniffer mode is enabled by default in the RX filter on Wi-Fi 7 chips, which causes all packets to be received regardless of the ADDR_CAM lookup result. This may result in unexpected packets being received. Therefore, disable it by default. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260625061545.44808-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac_be.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index d9c93adb58ee..14f1e30066e9 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -1333,7 +1333,7 @@ static int rx_fltr_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RX_FLTR_OPT, mac_idx); val = B_BE_A_BC_CAM_MATCH | B_BE_A_UC_CAM_MATCH | B_BE_A_MC | - B_BE_A_BC | B_BE_A_A1_MATCH | B_BE_SNIFFER_MODE | + B_BE_A_BC | B_BE_A_A1_MATCH | u32_encode_bits(15, B_BE_UID_FILTER_MASK); rtw89_write32(rtwdev, reg, val); u32p_replace_bits(&rtwdev->hal.rx_fltr, 15, B_BE_UID_FILTER_MASK); From 0ec249ffc060cd91f3f6cefd7e7008c17bcb20b9 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Thu, 25 Jun 2026 14:15:41 +0800 Subject: [PATCH 029/403] wifi: rtw89: pci: disable phy error flag related to refclk On some platforms, refclk is not available up to 15 ms after entering suspend. The delayed clock cause the hardware to detect falsely error and trigger an unexpected hardware reset. Disable the phy error flag related to refclk to fix it. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260625061545.44808-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index c3f2d0df5846..92c30c7f9fb2 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -58,7 +58,7 @@ #define B_AX_DIV GENMASK(15, 14) #define RAC_SET_PPR_V1 0x31 #define RAC_ANA40 0x40 -#define PHY_ERR_IMR_DIS (BIT(9) | BIT(8) | BIT(0)) +#define PHY_ERR_IMR_DIS (BIT(9) | BIT(2) | BIT(1) | BIT(0)) #define RAC_ANA41 0x41 #define PHY_ERR_FLAG_EN BIT(6) From c1eabaaa088ddbb1b937cd339adfa4c18e93c93d Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Thu, 25 Jun 2026 14:15:42 +0800 Subject: [PATCH 030/403] wifi: rtw89: fw: fix link ID filling for LPS MLO common info The link ID field in H2C command of LPS MLO common info is incorrectly filled with the PHY index. Fix it with the target link ID. Fixes: 20380a039ddd ("wifi: rtw89: phy: add H2C command to send detail RX gain and link parameters for PS mode") Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260625061545.44808-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 4 ++-- drivers/net/wireless/realtek/rtw89/fw.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index ac8e0e034a59..09338466be95 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3454,7 +3454,7 @@ int rtw89_fw_h2c_lps_ml_cmn_info_v1(struct rtw89_dev *rtwdev, h2c->rfe_type = efuse->rfe_type; h2c->rssi_main = U8_MAX; - memset(h2c->link_id, 0xfe, RTW89_BB_PS_LINK_BUF_MAX); + memset(h2c->link_id, RTW89_BB_PS_LINK_ID_SKIP, RTW89_BB_PS_LINK_BUF_MAX); rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) { u8 phy_idx = rtwvif_link->phy_idx; @@ -3462,7 +3462,7 @@ int rtw89_fw_h2c_lps_ml_cmn_info_v1(struct rtw89_dev *rtwdev, bb = rtw89_get_bb_ctx(rtwdev, phy_idx); chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); - h2c->link_id[phy_idx] = phy_idx; + h2c->link_id[phy_idx] = link_id; h2c->central_ch[phy_idx] = chan->channel; h2c->pri_ch[phy_idx] = chan->primary_channel; h2c->band[phy_idx] = chan->band_type; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 79e9af3a6805..71e8554a7af7 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2053,6 +2053,8 @@ enum rtw89_bb_link_rx_gain_table_type { RTW89_BB_PS_LINK_RX_GAIN_TAB_MAX, }; +#define RTW89_BB_PS_LINK_ID_SKIP 0xfe + enum rtw89_bb_ps_link_buf_id { RTW89_BB_PS_LINK_BUF_0 = 0x00, RTW89_BB_PS_LINK_BUF_1 = 0x01, From 76edcedda6437647ecd09b4b47593990a003b07a Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Thu, 25 Jun 2026 14:15:43 +0800 Subject: [PATCH 031/403] wifi: rtw89: wow: use MLD address in WoWLAN ARP replies for MLO stations Currently, WoWLAN ARP replies for MLO stations use the link address in the ARP hardware address fields. As a result, peers may learn the link address from the ARP reply and use it as the destination address for subsequent traffic. Some APs may not forward frames addressed to the link address, causing connectivity issues. Use the MLD address instead when generating WoWLAN ARP replies so peers learn the correct address for MLO stations. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260625061545.44808-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 09338466be95..44eb71c38580 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2982,9 +2982,9 @@ static struct sk_buff *rtw89_arp_response_get(struct rtw89_dev *rtwdev, arp_hdr->ar_pln = 4; arp_hdr->ar_op = htons(ARPOP_REPLY); - ether_addr_copy(arp_skb->sender_hw, rtwvif_link->mac_addr); + ether_addr_copy(arp_skb->sender_hw, rtwvif->mac_addr); arp_skb->sender_ip = rtwvif->ip_addr; - ether_addr_copy(arp_skb->target_hw, rtwvif_link->mac_addr); + ether_addr_copy(arp_skb->target_hw, rtwvif->mac_addr); arp_skb->target_ip = rtwvif->ip_addr; return skb; From 03a963f4aeda538acad50806e94ef25d95b78743 Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Thu, 25 Jun 2026 14:15:44 +0800 Subject: [PATCH 032/403] wifi: rtw89: wow: add QoS control field to WoWLAN ARP response for MLO Some MLO APs expect WoWLAN ARP response frames to be transmitted as QoS data frames and may discard frames that do not contain a QoS Control field. Add a QoS Control field and use the QoS Data subtype when generating WoWLAN ARP responses for MLD vifs. Keep the existing frame format unchanged for non-MLO connections. This allows WoWLAN ARP responses to be accepted by MLO APs while preserving compatibility with legacy APs. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260625061545.44808-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 35 ++++++++++++++++++------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 44eb71c38580..9d98805835d6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2943,6 +2943,7 @@ static struct sk_buff *rtw89_sa_query_get(struct rtw89_dev *rtwdev, static struct sk_buff *rtw89_arp_response_get(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif_link->rtwvif); struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; u8 sec_hdr_len = rtw89_wow_get_sec_hdr_len(rtwdev); struct rtw89_wow_param *rtw_wow = &rtwdev->wow; @@ -2950,26 +2951,42 @@ static struct sk_buff *rtw89_arp_response_get(struct rtw89_dev *rtwdev, struct rtw89_arp_rsp *arp_skb; struct arphdr *arp_hdr; struct sk_buff *skb; - __le16 fc; + bool with_qos; + u16 fc; - skb = dev_alloc_skb(sizeof(*hdr) + sec_hdr_len + sizeof(*arp_skb)); + with_qos = ieee80211_vif_is_mld(vif); + + rtw89_debug(rtwdev, RTW89_DBG_WOW, "[arp_reply] with qos field: %s\n", + str_yes_no(with_qos)); + + skb = dev_alloc_skb(sizeof(*hdr) + sec_hdr_len + sizeof(*arp_skb) + + (with_qos ? 2 : 0)); if (!skb) return NULL; hdr = skb_put_zero(skb, sizeof(*hdr)); - if (rtw_wow->ptk_alg) - fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS | - IEEE80211_FCTL_PROTECTED); - else - fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS); + fc = IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS; + + if (rtw_wow->ptk_alg) + fc |= IEEE80211_FCTL_PROTECTED; + + if (with_qos) + fc |= IEEE80211_STYPE_QOS_DATA; + else + fc |= IEEE80211_STYPE_DATA; + + hdr->frame_control = cpu_to_le16(fc); - hdr->frame_control = fc; ether_addr_copy(hdr->addr1, rtwvif_link->bssid); ether_addr_copy(hdr->addr2, rtwvif_link->mac_addr); ether_addr_copy(hdr->addr3, rtwvif_link->bssid); - skb_put_zero(skb, sec_hdr_len); + if (with_qos) + skb_put_zero(skb, sizeof(__le16)); + + if (sec_hdr_len) + skb_put_zero(skb, sec_hdr_len); arp_skb = skb_put_zero(skb, sizeof(*arp_skb)); memcpy(arp_skb->llc_hdr, rfc1042_header, sizeof(rfc1042_header)); From dbff9040587e9bd6d3ee337315971957f9f76612 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Thu, 25 Jun 2026 14:15:45 +0800 Subject: [PATCH 033/403] wifi: rtw89: wow: only WiFi 6 chips initialize RF registers in WoWLAN mode Only the WiFi 6 chips need to initialize RF register when WoWLAN download FW for some power save issue. Applying the same initialization flow to WiFi 7 chips might trigger the error 'RF parameters exceed size. path=1, idx=1500.'. This happens because normal mode uses rtw89_phy_config_rf_reg_v1(), which skips registers with addresses below 0x100. However, WoWLAN mode uses rtw89_phy_config_rf_reg_noio(), and WiFi 7 chips do not satisfy the rtw89_chip_rf_v1() condition. As a result, more RF registers are configured, causing the size overflow error. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260625061545.44808-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/wow.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index 8dadd8df4fc6..a7539f91264d 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -1299,7 +1299,8 @@ static int rtw89_wow_swap_fw(struct rtw89_dev *rtwdev, bool wow) if (disable_intr_for_dlfw) rtw89_hci_enable_intr(rtwdev); - rtw89_phy_init_rf_reg(rtwdev, true); + if (chip->chip_gen == RTW89_CHIP_AX) + rtw89_phy_init_rf_reg(rtwdev, true); ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif_link, rtwsta_link, RTW89_ROLE_FW_RESTORE); From a8cddb62c573f28eef5f887a8f3156e8ee22776a Mon Sep 17 00:00:00 2001 From: Dmitry Morgun Date: Mon, 29 Jun 2026 09:44:52 +0000 Subject: [PATCH 034/403] wifi: rtw89: check return values in rtw89_ops_start_ap() Several functions called in rtw89_ops_start_ap() may fail to allocate skb or fail to send H2C command to firmware, returning -ENOMEM or an error code. Their return values are ignored, so subsequent commands are executed with incorrect state. Check the return values and propagate errors. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: a52e4f2ce0f5 ("rtw89: implement ieee80211_ops::start_ap and stop_ap") Signed-off-by: Dmitry Morgun Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260629094452.8709-1-d.morgun@ispras.ru --- drivers/net/wireless/realtek/rtw89/mac80211.c | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index aade5c5b79e8..e381aacda667 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -826,11 +826,36 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw, ether_addr_copy(rtwvif_link->bssid, link_conf->bssid); rtw89_cam_bssid_changed(rtwdev, rtwvif_link); - rtw89_mac_port_update(rtwdev, rtwvif_link); - rtw89_chip_h2c_assoc_cmac_tbl(rtwdev, rtwvif_link, NULL); - rtw89_fw_h2c_role_maintain(rtwdev, rtwvif_link, NULL, RTW89_ROLE_TYPE_CHANGE); - rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true); - rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_TYPE_CHANGE); + ret = rtw89_mac_port_update(rtwdev, rtwvif_link); + if (ret) { + rtw89_warn(rtwdev, "failed to update mac port\n"); + return ret; + } + + ret = rtw89_chip_h2c_assoc_cmac_tbl(rtwdev, rtwvif_link, NULL); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c cmac table\n"); + return ret; + } + + ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif_link, NULL, RTW89_ROLE_TYPE_CHANGE); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c role info\n"); + return ret; + } + + ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c join info\n"); + return ret; + } + + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_TYPE_CHANGE); + if (ret) { + rtw89_warn(rtwdev, "failed to send h2c cam\n"); + return ret; + } + rtw89_chip_rfk_channel(rtwdev, rtwvif_link); if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw)) { From 2aba608a86e9b099c9af2ea70b620552dee2b628 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 30 Jun 2026 15:28:27 +0800 Subject: [PATCH 035/403] wifi: rtw89: fix HE extended capability length check rtw89_mac_check_he_obss_narrow_bw_ru_iter() reads extended capability byte 10, but rejects only datalen values below 10. Byte 10 requires at least 11 bytes. Require datalen >= 11 before reading data[10]. Fixes: 8d540f9d2916 ("wifi: rtw89: disable 26-tone RU HE TB PPDU transmissions") Signed-off-by: Pengpeng Hou Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/2026063009025530.2-ccfa108-0024-wifi-rtw89-fix-HE-extended--pengpeng@iscas.ac.cn --- drivers/net/wireless/realtek/rtw89/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 8c395517bd2f..99de1b202976 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5167,7 +5167,7 @@ static void rtw89_mac_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy, elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data, ies->len); - if (!elem || elem->datalen < 10 || + if (!elem || elem->datalen < 11 || !(elem->data[10] & WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) *tolerated = false; rcu_read_unlock(); From 04a46c2dbf3b516c96a1d19b72917b56055ad6c1 Mon Sep 17 00:00:00 2001 From: William Hansen-Baird Date: Tue, 30 Jun 2026 16:15:51 +0200 Subject: [PATCH 036/403] wifi: rtlwifi: fix disabling of ASPM for RTL8723BE with AER flooding commit 77a6407c6ab2 ("wifi: rtlwifi: disable ASPM for RTL8723BE with subsystem ID 11ad:1723") adds code which sets ppsc->support_aspm to false in _rtl_pci_update_default_setting() in order to disable ASPM. This does not, however, disable ASPM. Rather, it disables driver control of ASPM, and blocks calls to rtl_pci_enable_aspm() and rtl_pci_disable_aspm(). In some cases, the pci device supplied to the probe function has ASPM enabled. The code would therefore not disable ASPM, as it means to, but rather just leave it enabled. This was discovered through testing on a Razer Blade 14 2017. Implement a new __rtl_pci_disable_aspm(hw) function which does not check ppsc->support_aspm before disabling and call it from rtl_pci_disable_aspm(). Then move the code added in the previous commit to rtl_pci_init_aspm() to allow adding a call to __rtl_pci_disable_aspm(hw). This makes sure ASPM is disabled while still disabling driver control of ASPM to block it from being enabled later. Signed-off-by: William Hansen-Baird Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260630141553.785769-2-william.hansen.baird@gmail.com --- drivers/net/wireless/realtek/rtlwifi/pci.c | 39 ++++++++++++++-------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 73018a0498b4..f706575f92ce 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -156,15 +156,6 @@ static void _rtl_pci_update_default_setting(struct ieee80211_hw *hw) PCI_EXP_LNKCTL_ASPM_L1 | PCI_EXP_LNKCTL_CCC)) ppsc->support_aspm = false; - /* RTL8723BE found on some ASUSTek laptops, such as F441U and - * X555UQ with subsystem ID 11ad:1723 are known to output large - * amounts of PCIe AER errors during and after boot up, causing - * heavy lags, poor network throughput, and occasional lock-ups. - */ - if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8723BE && - (rtlpci->pdev->subsystem_vendor == 0x11ad && - rtlpci->pdev->subsystem_device == 0x1723)) - ppsc->support_aspm = false; } static bool _rtl_pci_platform_switch_device_pci_aspm( @@ -203,7 +194,7 @@ static void _rtl_pci_switch_clk_req(struct ieee80211_hw *hw, u16 value) } /*Disable RTL8192SE ASPM & Disable Pci Bridge ASPM*/ -static void rtl_pci_disable_aspm(struct ieee80211_hw *hw) +static void __rtl_pci_disable_aspm(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); @@ -215,9 +206,6 @@ static void rtl_pci_disable_aspm(struct ieee80211_hw *hw) u16 aspmlevel = 0; u16 tmp_u1b = 0; - if (!ppsc->support_aspm) - return; - if (pcibridge_vendor == PCI_BRIDGE_VENDOR_UNKNOWN) { rtl_dbg(rtlpriv, COMP_POWER, DBG_TRACE, "PCI(Bridge) UNKNOWN\n"); @@ -240,6 +228,16 @@ static void rtl_pci_disable_aspm(struct ieee80211_hw *hw) _rtl_pci_platform_switch_device_pci_aspm(hw, linkctrl_reg); } +static void rtl_pci_disable_aspm(struct ieee80211_hw *hw) +{ + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + + if (!ppsc->support_aspm) + return; + + __rtl_pci_disable_aspm(hw); +} + /*Enable RTL8192SE ASPM & Enable Pci Bridge ASPM for *power saving We should follow the sequence to enable *RTL8192SE first then enable Pci Bridge ASPM @@ -330,10 +328,25 @@ static void rtl_pci_parse_configuration(struct pci_dev *pdev, static void rtl_pci_init_aspm(struct ieee80211_hw *hw) { + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_priv *rtlpriv = rtl_priv(hw); _rtl_pci_update_default_setting(hw); + /* + * RTL8723BE found on some ASUSTek laptops, such as F441U and + * X555UQ with subsystem ID 11ad:1723 are known to output large + * amounts of PCIe AER errors during and after boot up, causing + * heavy lags, poor network throughput, and occasional lock-ups. + */ + if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8723BE && + (rtlpci->pdev->subsystem_vendor == 0x11ad && + rtlpci->pdev->subsystem_device == 0x1723)) { + __rtl_pci_disable_aspm(hw); + ppsc->support_aspm = false; + } + if (ppsc->reg_rfps_level & RT_RF_PS_LEVEL_ALWAYS_ASPM) { /*Always enable ASPM & Clock Req. */ rtl_pci_enable_aspm(hw); From 676e59a3825c6dff56318f0a502769853b72f223 Mon Sep 17 00:00:00 2001 From: William Hansen-Baird Date: Tue, 30 Jun 2026 16:15:52 +0200 Subject: [PATCH 037/403] wifi: rtlwifi: convert pci if-statement to ID table Refactor the ASUSTek quirk logic from an if-statement to a standard rtl_aspm_quirks pci_device_id table. This allows future devices with the same quirk to be added more easily while avoiding a large if-chain. Signed-off-by: William Hansen-Baird Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260630141553.785769-3-william.hansen.baird@gmail.com --- drivers/net/wireless/realtek/rtlwifi/pci.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index f706575f92ce..9a9c895b0bef 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -31,6 +31,12 @@ static const u8 ac_to_hwq[] = { BK_QUEUE }; +static const struct pci_device_id rtl_aspm_quirks[] = { + /* ASUSTek F441U/X555UQ */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_REALTEK, 0xb723, 0x11ad, 0x1723) }, + {} +}; + static u8 _rtl_mac_to_hwqueue(struct ieee80211_hw *hw, struct sk_buff *skb) { struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); @@ -330,19 +336,15 @@ static void rtl_pci_init_aspm(struct ieee80211_hw *hw) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); - struct rtl_priv *rtlpriv = rtl_priv(hw); _rtl_pci_update_default_setting(hw); /* - * RTL8723BE found on some ASUSTek laptops, such as F441U and - * X555UQ with subsystem ID 11ad:1723 are known to output large + * Certain pci devices are known to output large * amounts of PCIe AER errors during and after boot up, causing * heavy lags, poor network throughput, and occasional lock-ups. */ - if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8723BE && - (rtlpci->pdev->subsystem_vendor == 0x11ad && - rtlpci->pdev->subsystem_device == 0x1723)) { + if (pci_match_id(rtl_aspm_quirks, rtlpci->pdev)) { __rtl_pci_disable_aspm(hw); ppsc->support_aspm = false; } From 2b7858891b100587c10c136cf07205335a897be0 Mon Sep 17 00:00:00 2001 From: William Hansen-Baird Date: Tue, 30 Jun 2026 16:15:53 +0200 Subject: [PATCH 038/403] wifi: rtlwifi: disable ASPM for RTL8723BE with subsystem ID 17aa:b736 RTL8723BE outputs a large amount of PCIe AER errors during and after boot, even before probe and when driver is never loaded. This causes significant system slowdown. The errors are the same as reported by commit 77a6407c6ab2 ("wifi: rtlwifi: disable ASPM for RTL8723BE with subsystem ID 11ad:1723") Add the RTL8723BE with subsystem ID 17aa:b736 to the rtl_aspm_quirks table to stop the AER errors. AER errors can still be present prior to pci probe, as the device by default may have ASPM enabled. Testing on a Razer Blade 14 2017 which shipped from the OEM equipped with an RTL8723BE card with this subsystem ID confirms that this patch resolves the AER flood and allows the wireless card to function normally once the driver takes over. Signed-off-by: William Hansen-Baird Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260630141553.785769-4-william.hansen.baird@gmail.com --- drivers/net/wireless/realtek/rtlwifi/pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 9a9c895b0bef..220fb4dc7927 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -34,6 +34,8 @@ static const u8 ac_to_hwq[] = { static const struct pci_device_id rtl_aspm_quirks[] = { /* ASUSTek F441U/X555UQ */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_REALTEK, 0xb723, 0x11ad, 0x1723) }, + /* Razer Blade 14 2017 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_REALTEK, 0xb723, 0x17aa, 0xb736) }, {} }; From 0b79ae7b09a86ff2b925b3ec738dc18ba073d924 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Mon, 6 Jul 2026 10:54:37 +0800 Subject: [PATCH 039/403] wifi: rtw89: coex: Add Init info version 10 The version 10 Init info add I/O offload type & variable Bluetooth function (EX: Zigbee/Thread...etc) into the structure definition. Firmware need to synchronize these information to do corresponding setting. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260706025445.18428-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 58 +++++++++++++++++-- drivers/net/wireless/realtek/rtw89/coex.h | 6 ++ drivers/net/wireless/realtek/rtw89/core.h | 54 ++++++++++++++++- drivers/net/wireless/realtek/rtw89/fw.c | 41 +++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 6 ++ drivers/net/wireless/realtek/rtw89/rtw8851b.c | 2 - drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 - .../wireless/realtek/rtw89/rtw8852b_common.c | 1 - drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 - drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 - 10 files changed, 159 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 1361d4d54528..43de238c18f8 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -2822,6 +2822,8 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) case CXDRVINFO_INIT: if (ver->fcxinit == 7) rtw89_fw_h2c_cxdrv_init_v7(rtwdev, index); + else if (ver->fcxinit == 10) + rtw89_fw_h2c_cxdrv_init_v10(rtwdev, index); else rtw89_fw_h2c_cxdrv_init(rtwdev, index); break; @@ -7884,15 +7886,30 @@ void rtw89_btc_ntfy_poweroff(struct rtw89_dev *rtwdev) btc->cx.wl.status.map.rf_off_pre = btc->cx.wl.status.map.rf_off; } +#define BTC_PLATFORM_LITTLE_ENDIAN 0 static void _set_init_info(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - if (ver->fcxinit == 7) { + if (ver->fcxinit == 10) { + dm->init_info.init_v10.init_mode = wl->coex_mode; + dm->init_info.init_v10.wl_init_ok = wl->status.map.init_ok; + dm->init_info.init_v10.endian_type = BTC_PLATFORM_LITTLE_ENDIAN; + + dm->init_info.init_v10.module = btc->mdinfo.md_v10; + + dm->init_info.init_v10.bt0_function = cx->bt0.func_type; + dm->init_info.init_v10.bt1_function = cx->bt1.func_type; + dm->init_info.init_v10.bt2_function = cx->bt_ext.func_type; + + dm->init_info.init_v10.pta_mode = RTW89_MAC_AX_COEX_RTK_MODE; + dm->init_info.init_v10.pta_direction = RTW89_MAC_AX_COEX_INNER; + } else if (ver->fcxinit == 7) { dm->init_info.init_v7.wl_only = (u8)dm->wl_only; dm->init_info.init_v7.bt_only = (u8)dm->bt_only; dm->init_info.init_v7.wl_init_ok = (u8)wl->status.map.init_ok; @@ -7908,6 +7925,12 @@ static void _set_init_info(struct rtw89_dev *rtwdev) dm->init_info.init.wl_guard_ch = chip->afh_guard_ch; dm->init_info.init.module = btc->mdinfo.md; } + + _fw_set_drv_info(rtwdev, CXDRVINFO_INIT); + _fw_set_drv_info(rtwdev, CXDRVINFO_CTRL); + rtw89_btc_fw_set_slots(rtwdev); + btc_fw_set_monreg(rtwdev); + _set_wl_tx_power(rtwdev, RTW89_BTC_WL_DEF_TX_PWR, RTW89_PHY_0); } void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode) @@ -7918,13 +7941,18 @@ void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode) const struct rtw89_chip_info *chip = rtwdev->chip; const struct rtw89_btc_ver *ver = btc->ver; + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): Init %s !!\n", __func__, + chip_id_str(chip->chip_id)); + _reset_btc_var(rtwdev, BTC_RESET_ALL); btc->dm.run_reason = BTC_RSN_NONE; btc->dm.run_action = BTC_ACT_NONE; - if (ver->fcxctrl == 7) + if (ver->fcxctrl >= 7) btc->ctrl.ctrl_v7.igno_bt = true; else btc->ctrl.ctrl.igno_bt = true; + wl->status.map.init_ok = true; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): mode=%d\n", __func__, mode); @@ -7934,9 +7962,11 @@ void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode) dm->wl_only = mode == BTC_MODE_WL ? 1 : 0; dm->bt_only = mode == BTC_MODE_BT ? 1 : 0; wl->status.map.rf_off = mode == BTC_MODE_WLOFF ? 1 : 0; - - chip->ops->btc_set_rfe(rtwdev); - chip->ops->btc_init_cfg(rtwdev); + dm->vid = rtwdev->custid; + if (ver->fcxctrl >= 7) + btc->ctrl.ctrl_v7.always_freerun = mode == BTC_MODE_COTX; + else + btc->ctrl.ctrl.always_freerun = mode == BTC_MODE_COTX; if (!wl->status.map.init_ok) { rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -7946,8 +7976,26 @@ void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode) return; } + if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) { + btc->cx.bt1.enable.now = 1; + btc->cx.bt1.run_patch_code = 1; + } + + if (rtwdev->chip->para_ver & BTC_FEAT_H2C_MACRO) { + btc->cx.bt0.enable.now = 1; + btc->cx.bt0.run_patch_code = 1; + btc->io_oflld_type = BTC_IO_OFLD_BTC_H2C; + } else { + btc->io_oflld_type = BTC_IO_OFLD_NO_SUPPORT; + _update_bt_scbd(rtwdev, true); + } + + chip->ops->btc_set_rfe(rtwdev); + chip->ops->btc_init_cfg(rtwdev); + _write_scbd(rtwdev, BTC_WSCB_ACTIVE | BTC_WSCB_ON | BTC_WSCB_BTLOG, true); + if (rtw89_mac_get_ctrl_path(rtwdev)) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): PTA owner warning!!\n", diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index 6ac14611607c..259c6e2c0e3c 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -16,6 +16,8 @@ enum btc_mode { BTC_MODE_WL, BTC_MODE_BT, BTC_MODE_WLOFF, + BTC_MODE_COTX, + BTC_MODE_MECHANISM_INIT, BTC_MODE_MAX }; @@ -212,6 +214,10 @@ enum btc_chip_feature { BTC_FEAT_NEW_BBAPI_FLOW = BIT(3), /* new btg_ctrl/pre_agc_ctrl */ BTC_FEAT_MLO_SUPPORT = BIT(4), BTC_FEAT_H2C_MACRO = BIT(5), + BTC_FEAT_DUAL_BT = BIT(6), + BTC_FEAT_BT_6G = BIT(7), + BTC_FEAT_MULTI_PTA = BIT(8), + BTC_FEAT_DUAL_BTGA = BIT(9) /* the future A-Die */ }; enum btc_wl_mode { diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 5dde620b1e5e..1e72c9b9f3b7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1451,6 +1451,12 @@ enum rtw89_btc_bt_rf_band { BTC_BT_BMAX = 0x2 }; +enum rtw89_btc_io_offload_type { + BTC_IO_OFLD_NO_SUPPORT = 0, + BTC_IO_OFLD_MAC_API = 1, + BTC_IO_OFLD_BTC_H2C = 2 +}; + enum rtw89_btc_bt_profile { BTC_BT_NOPROFILE = 0, BTC_BT_HFP = BIT(0), @@ -1483,6 +1489,19 @@ struct rtw89_btc_ant_info_v7 { u8 rsvd; } __packed; +struct rtw89_btc_ant_info_v10 { + u8 type; /* shared, dedicated(non-shared) */ + u8 num; /* antenna count */ + u8 isolation; /* Ant-Iso between WL/BT */ + u8 single_pos; /* wifi 1ss-1ant at 0:S0 or 1:S1 */ + + u8 stream_cnt; /* spatial_stream count: Tx[7:4], Rx[3:0] */ + u8 btg_pos; /* BT0 btg-circuit at 0:WL-S0/1:WL-S1 */ + u8 btg1_pos; /* BT1 btg-circuit at 0:WL-S0/1:WL-S1 */ + u8 func[5]; /* function at 1~5 Ant refer to enum btc_bt_func_type */ + u8 ant_xmap[2][4]; +} __packed; + enum rtw89_tfc_dir { RTW89_TFC_UL, RTW89_TFC_DL, @@ -2129,9 +2148,24 @@ struct rtw89_btc_module_v7 { struct rtw89_btc_ant_info_v7 ant; } __packed; +struct rtw89_btc_module_v10 { + u8 rfe_type; + u8 wa_type; /* Refer to enum btc_wa_type */ + u8 kt_ver; + u8 kt_ver_adie; + + u8 bt0_pos; /* wl-end view: get from efuse, must compare bt.btg_type*/ + u8 bt0_sw_type; /* BT Ant-switch: None(non-share), Int(BTG), Ext(SPDT)*/ + u8 bt1_pos; /* BTC_BT_ALONE or BTC_BT_BTG */ + u8 bt1_sw_type; + + struct rtw89_btc_ant_info_v10 ant; +} __packed; + union rtw89_btc_module_info { struct rtw89_btc_module md; struct rtw89_btc_module_v7 md_v7; + struct rtw89_btc_module_v10 md_v10; }; #define RTW89_BTC_DM_MAXSTEP 30 @@ -2170,9 +2204,24 @@ struct rtw89_btc_init_info_v7 { struct rtw89_btc_module_v7 module; } __packed; +struct rtw89_btc_init_info_v10 { + u8 endian_type; /* 0: little-endian, 1:big-endian */ + u8 init_mode; /* refer to enum BTC_MODE_xxx */ + u8 wl_init_ok; + u8 bt0_function; + + u8 bt1_function; + u8 bt2_function; + u8 pta_mode; + u8 pta_direction; + + struct rtw89_btc_module_v10 module; +}; + union rtw89_btc_init_info_u { struct rtw89_btc_init_info init; struct rtw89_btc_init_info_v7 init_v7; + struct rtw89_btc_init_info_v10 init_v10; }; struct rtw89_btc_wl_tx_limit_para { @@ -2253,6 +2302,7 @@ struct rtw89_btc_bt_info { u8 raw_info[BTC_BTINFO_MAX]; /* raw bt info from mailbox */ u8 txpwr_info[BTC_BTINFO_MAX]; u8 rssi_level; + u8 func_type; u32 scbd; u32 feature; @@ -3173,6 +3223,7 @@ struct rtw89_btc_dm { u8 run_reason; u8 run_action; u8 wl_tx_pwr_phy_map; + u8 vid; u8 wl_pre_agc: 2; u8 wl_lna2: 1; @@ -3203,7 +3254,7 @@ struct rtw89_btc_ctrl_v7 { union rtw89_btc_ctrl_list { struct rtw89_btc_ctrl ctrl; - struct rtw89_btc_ctrl_v7 ctrl_v7; + struct rtw89_btc_ctrl_v7 ctrl_v7; /* ver 8, 9 is the same */ }; struct rtw89_btc_dbg { @@ -3425,6 +3476,7 @@ struct rtw89_btc { u8 policy[RTW89_BTC_POLICY_MAXLEN]; u8 ant_type; u8 btg_pos; + u8 io_oflld_type; u16 policy_len; u16 policy_type; u32 hubmsg_cnt; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 9d98805835d6..b97c6e9c18bc 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -5863,6 +5863,47 @@ int rtw89_fw_h2c_cxdrv_init_v7(struct rtw89_dev *rtwdev, u8 type) return ret; } +int rtw89_fw_h2c_cxdrv_init_v10(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_init_info_v10 *init_info = &dm->init_info.init_v10; + struct rtw89_h2c_cxinit_v10 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_init_v10\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxinit_v10 *)skb->data; + + h2c->hdr.type = type; + h2c->hdr.ver = btc->ver->fcxinit; + h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; + h2c->init = *init_info; + + 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; +} + #define PORT_DATA_OFFSET 4 #define H2C_LEN_CXDRVINFO_ROLE_DBCC_LEN 12 #define H2C_LEN_CXDRVINFO_ROLE_SIZE(max_role_num) \ diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 71e8554a7af7..de8b77de8705 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2592,6 +2592,11 @@ struct rtw89_h2c_cxinit_v7 { struct rtw89_btc_init_info_v7 init; } __packed; +struct rtw89_h2c_cxinit_v10 { + struct rtw89_h2c_cxhdr_v7 hdr; + struct rtw89_btc_init_info_v10 init; +} __packed; + static inline void RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(void *cmd, u8 val) { u8p_replace_bits((u8 *)(cmd) + 2, val, GENMASK(7, 0)); @@ -5380,6 +5385,7 @@ int rtw89_fw_h2c_tx_history(struct rtw89_dev *rtwdev, u16 mac_id); int rtw89_fw_h2c_drv_ctrl_fw(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_init_v7(struct rtw89_dev *rtwdev, u8 type); +int rtw89_fw_h2c_cxdrv_init_v10(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 4caf231c6287..a1a63588cb90 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2277,8 +2277,6 @@ static void rtw8851b_btc_init_cfg(struct rtw89_dev *rtwdev) /* enable BT counter 0xda40[16,2] = 2b'11 */ rtw89_write32_set(rtwdev, R_AX_CSR_MODE, B_AX_BT_CNT_RST | B_AX_STATIS_BT_EN); - - btc->cx.wl.status.map.init_ok = true; } static diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 78addc0aef69..055c67a07cea 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -1945,7 +1945,6 @@ static void rtw8852a_btc_init_cfg(struct rtw89_dev *rtwdev) /* enable BT counter 0xda40[16,2] = 2b'11 */ rtw89_write32_set(rtwdev, R_AX_CSR_MODE, B_AX_BT_CNT_RST | B_AX_STATIS_BT_EN); - btc->cx.wl.status.map.init_ok = true; } static diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c index df5fbae50ff5..7d409a64869f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c @@ -1838,7 +1838,6 @@ static void __rtw8852bx_btc_init_cfg(struct rtw89_dev *rtwdev) /* enable BT counter 0xda40[16,2] = 2b'11 */ rtw89_write32_set(rtwdev, R_AX_CSR_MODE, B_AX_BT_CNT_RST | B_AX_STATIS_BT_EN); - btc->cx.wl.status.map.init_ok = true; } static diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 29a3c90021f3..9e630b897986 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2761,7 +2761,6 @@ static void rtw8852c_btc_init_cfg(struct rtw89_dev *rtwdev) rtw89_write32_set(rtwdev, R_AX_BT_CNT_CFG, B_AX_BT_CNT_EN | B_AX_BT_CNT_RST_V1); - btc->cx.wl.status.map.init_ok = true; } static diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 6d4301661b04..382034eb27d0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2828,7 +2828,6 @@ static void rtw8922a_btc_init_cfg(struct rtw89_dev *rtwdev) rtw89_write32(rtwdev, R_BTC_ZB_COEX_TBL_1, 0xda5a5a5a); rtw89_write32(rtwdev, R_BTC_ZB_BREAK_TBL, 0xf0ffffff); - btc->cx.wl.status.map.init_ok = true; } static void From c595e0a0958c5b700eed0a91ce0bf25290587f0f Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Mon, 6 Jul 2026 10:54:38 +0800 Subject: [PATCH 040/403] wifi: rtw89: coex: add rtw89_btc_init() entry for initialization once Separate these two type of initialize entry. Because Wi-Fi power save leaving will also call the initializing, but don't have to reset all the stored variables. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260706025445.18428-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 35 ++++++++++++++--------- drivers/net/wireless/realtek/rtw89/coex.h | 1 + drivers/net/wireless/realtek/rtw89/core.c | 1 + 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 43de238c18f8..196bec751070 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -7858,6 +7858,28 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) _action_common(rtwdev); } +void rtw89_btc_init(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + const struct rtw89_btc_ver *ver = btc->ver; + + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): Init %s !!\n", __func__, + chip_id_str(chip->chip_id)); + + _reset_btc_var(rtwdev, BTC_RESET_ALL); + + btc->dm.run_reason = BTC_RSN_NONE; + btc->dm.run_action = BTC_ACT_NONE; + if (ver->fcxctrl >= 7) + btc->ctrl.ctrl_v7.igno_bt = true; + else + btc->ctrl.ctrl.igno_bt = true; + wl->status.map.init_ok = true; +} + void rtw89_btc_ntfy_poweron(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; @@ -7941,19 +7963,6 @@ void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode) const struct rtw89_chip_info *chip = rtwdev->chip; const struct rtw89_btc_ver *ver = btc->ver; - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): Init %s !!\n", __func__, - chip_id_str(chip->chip_id)); - - _reset_btc_var(rtwdev, BTC_RESET_ALL); - btc->dm.run_reason = BTC_RSN_NONE; - btc->dm.run_action = BTC_ACT_NONE; - if (ver->fcxctrl >= 7) - btc->ctrl.ctrl_v7.igno_bt = true; - else - btc->ctrl.ctrl.igno_bt = true; - wl->status.map.init_ok = true; - rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): mode=%d\n", __func__, mode); diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index 259c6e2c0e3c..fb151f68eb64 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -272,6 +272,7 @@ enum btc_wl_gpio_debug { BTC_DBG_USER_DEF = 31, }; +void rtw89_btc_init(struct rtw89_dev *rtwdev); void rtw89_btc_ntfy_poweron(struct rtw89_dev *rtwdev); void rtw89_btc_ntfy_poweroff(struct rtw89_dev *rtwdev); void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode); diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 26b744dfbcf8..85aeb9e90812 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -7488,6 +7488,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) } rtw89_rfkill_polling_init(rtwdev); + rtw89_btc_init(rtwdev); return 0; From 2c5af470819ba6c8a996c297d2beb06987215f8f Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Mon, 6 Jul 2026 10:54:39 +0800 Subject: [PATCH 041/403] wifi: rtw89: coex: Update TDMA descriptor for dual MAC The mechanism needs information to know which MAC is coexisting with which Bluetooth and when to enable TDMA with which MAC. So change an variable to describe the binding target. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260706025445.18428-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 15 ++++++++------- drivers/net/wireless/realtek/rtw89/core.h | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 196bec751070..14cc62cf399d 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -2288,9 +2288,9 @@ static void _append_tdma(struct rtw89_dev *rtwdev) } rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): type:%d, rxflctrl=%d, txpause=%d, wtgle_n=%d, leak_n=%d, ext_ctrl=%d\n", + "[BTC], %s(): type:%d, rxflctrl=%d, txflctrl=%d, bind=%d, leak_n=%d, ext_ctrl=%d\n", __func__, dm->tdma.type, dm->tdma.rxflctrl, - dm->tdma.txpause, dm->tdma.wtgle_n, dm->tdma.leak_n, + dm->tdma.txflctrl, dm->tdma.bind, dm->tdma.leak_n, dm->tdma.ext_ctrl); } @@ -3733,7 +3733,8 @@ static bool _check_freerun(struct rtw89_dev *rtwdev) #define _tdma_set_flctrl(btc, flc) ({(btc)->dm.tdma.rxflctrl = flc; }) #define _tdma_set_flctrl_role(btc, role) ({(btc)->dm.tdma.rxflctrl_role = role; }) -#define _tdma_set_tog(btc, wtg) ({(btc)->dm.tdma.wtgle_n = wtg; }) +#define _tdma_set_rxflctrl(btc, rxflc) ({(btc)->dm.tdma.rxflctrl = rxflc; }) +#define _tdma_set_txflctrl(btc, txflc) ({(btc)->dm.tdma.txflctrl = txflc; }) #define _tdma_set_lek(btc, lek) ({(btc)->dm.tdma.leak_n = lek; }) struct btc_btinfo_lb2 { @@ -9967,13 +9968,13 @@ static int _show_fbtc_tdma(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, " %-15s : ", "[tdma_policy]"); p += scnprintf(p, end - p, - "type:%d, rx_flow_ctrl:%d, tx_pause:%d, ", + "type:%d, rx_flow_ctrl:%d, txflctrl:%d, ", (u32)t->type, - t->rxflctrl, t->txpause); + t->rxflctrl, t->txflctrl); p += scnprintf(p, end - p, - "wl_toggle_n:%d, leak_n:%d, ext_ctrl:%d, ", - t->wtgle_n, t->leak_n, t->ext_ctrl); + "bind:%d, leak_n:%d, ext_ctrl:%d, ", + t->bind, t->leak_n, t->ext_ctrl); p += scnprintf(p, end - p, "policy_type:%d", diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 1e72c9b9f3b7..a0f6929873ab 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2354,8 +2354,8 @@ struct rtw89_btc_cx { struct rtw89_btc_fbtc_tdma { u8 type; /* btc_ver::fcxtdma */ u8 rxflctrl; - u8 txpause; - u8 wtgle_n; + u8 txflctrl; + u8 bind; u8 leak_n; u8 ext_ctrl; u8 rxflctrl_role; From 2b497ba92abe3f31a66643e5d05a7b27a1de0c56 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Mon, 6 Jul 2026 10:54:40 +0800 Subject: [PATCH 042/403] wifi: rtw89: coex: Add Bluetooth binding for Bluetooth TX power setting Dual Bluetooth the each of Bluetooth may use different TX power by their condition. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260706025445.18428-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 65 ++++++++++++++++++----- drivers/net/wireless/realtek/rtw89/core.h | 15 ++++-- drivers/net/wireless/realtek/rtw89/fw.h | 3 ++ 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 14cc62cf399d..2080253559a7 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -3078,6 +3078,7 @@ static void _set_bt_ignore_wlan_act(struct rtw89_dev *rtwdev, u8 enable) #define WL_TX_POWER_FRA_PART GENMASK(1, 0) #define B_BTC_WL_TX_POWER_SIGN BIT(7) #define B_TSSI_WL_TX_POWER_SIGN BIT(8) +#define SET_RF_PARA_AX_LEN 1 static void _set_wl_tx_power(struct rtw89_dev *rtwdev, u32 level, u8 phy_map) { @@ -3161,28 +3162,64 @@ static void _set_wl_rx_gain(struct rtw89_dev *rtwdev, u32 level, u8 phy_map) chip->ops->btc_set_wl_rx_gain(rtwdev, level); } -static void _set_bt_tx_power(struct rtw89_dev *rtwdev, u8 level) +static void _set_bt_tx_power(struct rtw89_dev *rtwdev, bool force_exec, u8 bid, + u8 rf_band, u8 level) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; - int ret; - u8 buf; + u8 h2c_func = SET_BT_TX_PWR; + u8 i, id_start, id_stop; + u8 buf[2] = {}; + u8 len = sizeof(*buf); - if (bt->bcnt[BTC_BCNT_INFOUPDATE] == 0) + if (bt->bcnt[BTC_BCNT_INFOUPDATE] == 0 || !rf_band) return; if (bt->rf_para.tx_pwr_freerun == level) return; - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): level = %d\n", - __func__, level); + if (bid == BTC_ALL_BT) { + id_start = BTC_BT_1ST; + id_stop = BTC_BT_2ND; + } else { + id_start = bid; + id_stop = bid; + } - buf = (s8)(-level); - ret = _send_fw_cmd(rtwdev, BTFC_SET, SET_BT_TX_PWR, &buf, 1); - if (!ret) { - bt->rf_para.tx_pwr_freerun = level; - btc->dm.rf_trx_para.bt_tx_power[BTC_BT_1ST] = level; + for (i = id_start; i <= id_stop; i++) { + if (i == BTC_BT_2ND) { + if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT)) + continue; + + bt = &btc->cx.bt1; + h2c_func |= BT_H2C_FUNC_BT2ND; + } + + buf[0] = (s8)(-level); + 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 && + bt->tx_power_now == level) + continue; + else if (rf_band != RTW89_BAND_2G && + bt->tx_power_now_6g == level) + continue; + } + + if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) + len = SET_RF_PARA_AX_LEN; + + 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) + bt->tx_power_now = level; + else + bt->tx_power_now_6g = level; + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): bt%d_tx_power_level = %d\n", + __func__, i, level); + } } } @@ -3229,6 +3266,8 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) struct rtw89_btc_rf_trx_para_v9 para; u8 lv, link_mode = 0, i, dbcc_2g_phy = 0; u8 ul_para_num, dl_para_num; + u8 rf_band = RTW89_BAND_2G; + u8 bid = BTC_BT_1ST; u32 wl_stb_chg = 0; if (ver->fwlrole == 0) { @@ -3321,7 +3360,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) } else { _set_wl_tx_power(rtwdev, para.wl_tx_power[RTW89_PHY_0], RTW89_PHY_0); _set_wl_rx_gain(rtwdev, para.wl_rx_gain[RTW89_PHY_0], RTW89_PHY_0); - _set_bt_tx_power(rtwdev, para.bt_tx_power[BTC_BT_1ST]); + _set_bt_tx_power(rtwdev, true, bid, rf_band, para.bt_tx_power[BTC_BT_1ST]); _set_bt_rx_gain(rtwdev, para.bt_rx_gain[BTC_BT_1ST]); } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index a0f6929873ab..3f77707e2733 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2302,7 +2302,18 @@ struct rtw89_btc_bt_info { u8 raw_info[BTC_BTINFO_MAX]; /* raw bt info from mailbox */ u8 txpwr_info[BTC_BTINFO_MAX]; u8 rssi_level; + u8 rf_band_map; u8 func_type; + u8 tx_power_now; + u8 tx_power_now_6g; + + u8 fw_ver_mismatch: 1; + u8 band_56G_support: 1; + u8 hi_lna_rx: 1; + u8 lna_constrain: 3; + u8 hi_lna_rx_6g: 1; + u8 lna_constrain_6g: 3; + u8 rsvd: 6; u32 scbd; u32 feature; @@ -2316,11 +2327,9 @@ struct rtw89_btc_bt_info { u32 inq: 1; u32 pag: 1; u32 run_patch_code: 1; - u32 hi_lna_rx: 1; u32 scan_rx_low_pri: 1; u32 scan_info_update: 1; - u32 lna_constrain: 3; - u32 rsvd: 17; + u32 rsvd1: 22; u32 bcnt[BTC_BCNT_NUM]; }; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index de8b77de8705..a6f3b28b9e33 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2337,6 +2337,9 @@ struct rtw89_h2c_arp_offload { #define RTW89_H2C_ARP_OFFLOAD_W0_PKT_ID GENMASK(31, 24) #define RTW89_H2C_ARP_OFFLOAD_W1_CONTENT GENMASK(31, 0) +#define BT_H2C_FUNC_BT2ND 0x80 +#define BT_C2H_FUNC_BT2ND 0x80 + enum rtw89_btc_btf_h2c_class { BTFC_SET = 0x10, BTFC_GET = 0x11, From fe8f6ddb9095caa156983f920922d5f8b9b25e1c Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Mon, 6 Jul 2026 10:54:41 +0800 Subject: [PATCH 043/403] wifi: rtw89: coex: Add Bluetooth binding for Bluetooth RX gain setting Dual Bluetooth the each of Bluetooth may use different RX gain by their condition. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260706025445.18428-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 86 ++++++++++++++++++----- 1 file changed, 68 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 2080253559a7..ff3c05101ab3 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -730,6 +730,7 @@ enum btc_w2b_scoreboard { BTC_WSCB_SCAN = BIT(2), BTC_WSCB_UNDERTEST = BIT(3), BTC_WSCB_RXGAIN = BIT(4), + BTC_WSCB_5GHICH = BIT(6), BTC_WSCB_WLBUSY = BIT(7), BTC_WSCB_EXTFEM = BIT(8), BTC_WSCB_TDMA = BIT(9), @@ -738,6 +739,9 @@ enum btc_w2b_scoreboard { BTC_WSCB_RXSCAN_PRI = BIT(12), BTC_WSCB_BT_HILNA = BIT(13), BTC_WSCB_BTLOG = BIT(14), + BTC_WSCB_CTCODE = BIT(15), + BTC_WSCB_RXGAIN_56G = BIT(16), + BTC_WSCB_BT_HILNA_56G = BIT(17), BTC_WSCB_ALL = GENMASK(23, 0), }; @@ -3224,33 +3228,79 @@ static void _set_bt_tx_power(struct rtw89_dev *rtwdev, bool force_exec, u8 bid, } #define BTC_BT_RX_NORMAL_LVL 7 - -static void _set_bt_rx_gain(struct rtw89_dev *rtwdev, u8 level) +static void _set_bt_rx_gain(struct rtw89_dev *rtwdev, bool force_exec, u8 bid, + u8 rf_band, u8 level) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; + struct rtw89_btc_cx *cx = &btc->cx; + struct rtw89_btc_bt_info *bt = &cx->bt0; + u8 h2c_func = SET_BT_LNA_CONSTRAIN; + u8 i, id_start, id_stop; + bool state = false; + u32 scbd_bit = 0; + u8 buf[2] = {}; + u8 len = sizeof(*buf); - if (bt->bcnt[BTC_BCNT_INFOUPDATE] == 0) + if (bt->bcnt[BTC_BCNT_INFOUPDATE] == 0 || !rf_band) return; - if ((bt->rf_para.rx_gain_freerun == level || - level > BTC_BT_RX_NORMAL_LVL) && - (!rtwdev->chip->scbd || bt->lna_constrain == level)) + if (bt->rf_para.rx_gain_freerun == level || + level > BTC_BT_RX_NORMAL_LVL || !rtwdev->chip->scbd || + bid > BTC_ALL_BT) return; - bt->rf_para.rx_gain_freerun = level; - btc->dm.rf_trx_para.bt_rx_gain[BTC_BT_1ST] = level; + if (bid == BTC_ALL_BT) { + id_start = BTC_BT_1ST; + id_stop = BTC_BT_2ND; + } else { + id_start = bid; + id_stop = bid; + } - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): level = %d\n", - __func__, level); - - if (level == BTC_BT_RX_NORMAL_LVL) - _write_scbd(rtwdev, BTC_WSCB_RXGAIN, false); + if (rf_band == RTW89_BAND_2G) + scbd_bit |= BTC_WSCB_RXGAIN; else - _write_scbd(rtwdev, BTC_WSCB_RXGAIN, true); + scbd_bit |= BTC_WSCB_RXGAIN_56G; + + for (i = id_start; i <= id_stop; i++) { + if (i == BTC_BT_2ND) { + if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT)) + continue; + + bt = &cx->bt1; + h2c_func |= BT_H2C_FUNC_BT2ND; + } + + /* return if same setup */ + if (!force_exec && !btc->cli_h2c_cmd) { + if (rf_band == RTW89_BAND_2G && + bt->lna_constrain == level) + continue; + else if (rf_band != RTW89_BAND_2G && + bt->lna_constrain_6g == level) + continue; + } + + buf[0] = level; + buf[1] = rf_band; + + if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) + len = SET_RF_PARA_AX_LEN; + + if (_send_fw_cmd(rtwdev, BTFC_SET, h2c_func, buf, len)) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): bt%d_rx_gain_level = %d\n", + __func__, i, level); + } + + btc->dm.rf_trx_para.bt_rx_gain[i] = level; + + if (buf[0] != BTC_BT_RX_NORMAL_LVL) + state = true; + + _write_scbd(rtwdev, scbd_bit, state); + } - _send_fw_cmd(rtwdev, BTFC_SET, SET_BT_LNA_CONSTRAIN, &level, sizeof(level)); } static void _set_rf_trx_para(struct rtw89_dev *rtwdev) @@ -3361,7 +3411,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) _set_wl_tx_power(rtwdev, para.wl_tx_power[RTW89_PHY_0], RTW89_PHY_0); _set_wl_rx_gain(rtwdev, para.wl_rx_gain[RTW89_PHY_0], RTW89_PHY_0); _set_bt_tx_power(rtwdev, true, bid, rf_band, para.bt_tx_power[BTC_BT_1ST]); - _set_bt_rx_gain(rtwdev, para.bt_rx_gain[BTC_BT_1ST]); + _set_bt_rx_gain(rtwdev, true, bid, rf_band, para.bt_rx_gain[BTC_BT_1ST]); } next: From 564dd7a9504767bbf1ed9c855f334f042ee4ae7d Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Mon, 6 Jul 2026 10:54:42 +0800 Subject: [PATCH 044/403] wifi: rtw89: coex: Add WiFi/Bluetooth adapter binding info To bind Wi-Fi/Bluetooth with which adapter, in which band. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260706025445.18428-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 309 ++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/core.h | 123 +++++++-- 2 files changed, 397 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index ff3c05101ab3..f2dea0e8c8f2 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -3349,7 +3349,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) /* decide trx_para_level */ if (btc->ant_type == BTC_ANT_SHARED) { /* fix LNA2 + TIA gain not change by GNT_BT */ - if ((btc->dm.wl_btg_rx && b->profile_cnt.now != 0) || + if ((btc->dm.wl_btg_rx && b->link_cnt.now != 0) || dm->bt_only == 1) dm->trx_para_level = 1; /* for better BT ACI issue */ else @@ -3357,7 +3357,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) } else { /* non-shared antenna */ dm->trx_para_level = 5; /* modify trx_para if WK 2.4G-STA-DL + bt link */ - if (b->profile_cnt.now != 0 && + if (b->link_cnt.now != 0 && link_mode == BTC_WLINK_2G_STA && wl->status.map.traffic_dir & BIT(RTW89_TFC_UL)) { /* uplink */ if (wl->rssi_level == 4 && bt->rssi_level > 2) @@ -3606,7 +3606,7 @@ static void _set_bt_afh_info_v0(struct rtw89_dev *rtwdev) if (wl->afh_info.en == en && wl->afh_info.ch == ch && wl->afh_info.bw == bw && - b->profile_cnt.last == b->profile_cnt.now) { + b->link_cnt.last == b->link_cnt.now) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): return because no change!\n", __func__); @@ -3777,7 +3777,7 @@ static bool _check_freerun(struct rtw89_dev *rtwdev) return true; } - if (bt_linfo->profile_cnt.now == 0) { + if (bt_linfo->link_cnt.now == 0) { btc->dm.trx_para_level = 5; return true; } @@ -5497,7 +5497,7 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev) } else if (link_mode == BTC_WLINK_5G) { is_preagc = BTC_PREAGC_DISABLE; } else if (link_mode == BTC_WLINK_NOLINK || - btc->cx.bt0.link_info.profile_cnt.now == 0) { + btc->cx.bt0.link_info.link_cnt.now == 0) { is_preagc = BTC_PREAGC_DISABLE; } else if (dm->tdma_now.type != CXTDMA_OFF && !bt_linfo->hfp_desc.exist && @@ -5683,7 +5683,7 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev) else igno_bt = btc->ctrl.ctrl.igno_bt; - if (btc->dm.freerun || igno_bt || b->profile_cnt.now == 0 || + if (btc->dm.freerun || igno_bt || b->link_cnt.now == 0 || mode == BTC_WLINK_5G || mode == BTC_WLINK_NOLINK) { enable = 0; tx_time = BTC_MAX_TX_TIME_DEF; @@ -6003,7 +6003,7 @@ static void _action_wl_2g_mcc(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); if (btc->ant_type == BTC_ANT_SHARED) { /* shared-antenna */ - if (btc->cx.bt0.link_info.profile_cnt.now == 0) + if (btc->cx.bt0.link_info.link_cnt.now == 0) _set_policy(rtwdev, BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_MCC); else @@ -6021,7 +6021,7 @@ static void _action_wl_2g_scc(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); if (btc->ant_type == BTC_ANT_SHARED) { /* shared-antenna */ - if (btc->cx.bt0.link_info.profile_cnt.now == 0) + if (btc->cx.bt0.link_info.link_cnt.now == 0) _set_policy(rtwdev, BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_SCC); else @@ -6199,7 +6199,7 @@ static void _action_wl_2g_ap(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); if (btc->ant_type == BTC_ANT_SHARED) { - if (btc->cx.bt0.link_info.profile_cnt.now == 0) + if (btc->cx.bt0.link_info.link_cnt.now == 0) _set_policy(rtwdev, BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_AP); else @@ -6216,7 +6216,7 @@ static void _action_wl_2g_go(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); if (btc->ant_type == BTC_ANT_SHARED) { /* shared-antenna */ - if (btc->cx.bt0.link_info.profile_cnt.now == 0) + if (btc->cx.bt0.link_info.link_cnt.now == 0) _set_policy(rtwdev, BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_GO); else @@ -6247,7 +6247,7 @@ static void _action_wl_2g_nan(struct rtw89_dev *rtwdev) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); if (btc->ant_type == BTC_ANT_SHARED) { /* shared-antenna */ - if (btc->cx.bt0.link_info.profile_cnt.now == 0) + if (btc->cx.bt0.link_info.link_cnt.now == 0) _set_policy(rtwdev, BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_NAN); else @@ -7742,6 +7742,273 @@ static bool _chk_wl_rfk_request(struct rtw89_dev *rtwdev) return false; } +static void _set_bind_info(struct rtw89_btc *btc, u8 type) +{ + struct rtw89_btc_cx *cx = &btc->cx; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_wl_info *wl = &cx->wl; + struct rtw89_btc_bt_info *bt = &cx->bt0; + struct rtw89_btc_bt_link_info *b; + struct rtw89_btc_bind_info *bd; + u8 path_hwb[BTC_RF_NUM] = {RTW89_PHY_0, RTW89_PHY_1}; + u8 link_weight[BTC_ALL_BT_EZL][BTC_BT_BMAX]; + u8 map[BTC_RF_NUM][BTC_ALL_BT_EZL]; + u8 b2g_score = 0, b5g_score = 0; + u8 i, j, rf_band_map, thres; + u8 weight[BTC_BT_BMAX] = {}; + + if (type == BTC_MECH_TDD) { + bd = &dm->tdd_bind; + memcpy(map, dm->tdd_map, sizeof(map)); + thres = 30; /* BT profile score threshold for TDMA */ + } else { + bd = &dm->fdd_bind; + memcpy(map, dm->fdd_map, sizeof(map)); + thres = 5; + } + + memset(bd, 0, sizeof(*bd)); + /* compare BT 2GHz/5GHz profile by link-weighting */ + for (i = 0; i < BTC_BT_BMAX; i++) { + link_weight[BTC_BT_1ST][i] = cx->bt0.link_weight[i]; + link_weight[BTC_BT_2ND][i] = cx->bt1.link_weight[i]; + link_weight[BTC_BT_EXT][i] = cx->bt_ext.link_weight[i]; + } + + /* + * get 2GHz/5GHz link weight score by "band-overlap" map + * link_weight from _update_bt_link_cnt(), + * if link_weight >30 --> TDMA-required + */ + for (j = 0; j < BTC_ALL_BT_EZL; j++) { + rf_band_map = map[BTC_RF_S0][j] | map[BTC_RF_S1][j]; + if (rf_band_map & BIT(RTW89_BAND_2G)) + b2g_score += link_weight[j][BTC_BT_B2G]; + if (rf_band_map & BIT(RTW89_BAND_5G)) + b5g_score += link_weight[j][BTC_BT_B5G]; + } + + /* rf-band bound by comparing link weight */ + if (b2g_score == 0 && b5g_score == 0) /* no-rf-band overlap */ + bd->rf_band = 0; + else if (b5g_score > b2g_score) + bd->rf_band = BIT(BTC_BT_B5G); + else + bd->rf_band = BIT(BTC_BT_B2G); + + /* + * if MR_WTYPE_MLD2L1R_NONMLD, FW will change 2+0/0+2/1+1 + * Therefore, BTC_MLO_RF_xxx is not real-time state. + * In this case, RF_S0->HWB0, RF_S1->HWB1 + * If link-mode change by _ntfy_generic(BTC_GNTFY_MRCX_INFO) + * HW-Band is decided by wl->mlo_info.mrcx_act_hwb_map + */ + if (wl->mlo_info.wtype == RTW89_MR_WTYPE_MLD2L1R_NONMLD) { + /* TODO: Should patched WiFi mode & WiFi role patch */ + } else { + /* + * Dual-RF_band(HWB)TDMA if BT-profile is TDMA-type at both + * 2GHz and 5GHz. + * ex: 1+1: HWB0 5GHz + BT0 5GHz (BIS, HDT) + * && HWB1 2.4GHz + BT1 2.4GHz(PAN) + * for TDD: the link score must > 30 (from _bt_link_cnt) + * for FDD: the link score must > 5 (bt enable) + */ + if ((wl->mlo_info.rf_combination == BTC_MLO_RF_1_PLUS_1 || + wl->mlo_info.rf_combination == BTC_MLO_RF_2_PLUS_2) && + (b2g_score >= thres && b5g_score >= thres)) { + bd->rf_band = BIT(BTC_BT_B2G) | BIT(BTC_BT_B5G); + } + + if (wl->mlo_info.rf_combination == BTC_MLO_RF_2_PLUS_0) + path_hwb[BTC_RF_S1] = RTW89_PHY_0; /* 2+0 RF-S0/1->HWB0 */ + else if (wl->mlo_info.rf_combination == BTC_MLO_RF_0_PLUS_2) + path_hwb[BTC_RF_S0] = RTW89_PHY_1; /* 2+0 RF-S0/1->HWB1 */ + } + + /* Get HWB-sel and BT-sel by rf-band-binding */ + for (i = 0; i < BTC_RF_NUM; i++) { + for (j = 0; j < BTC_ALL_BT_EZL; j++) { + if (!(map[i][j] & bd->rf_band)) /* no-overlap */ + continue; + + bd->wl_hwb_sel |= BIT(path_hwb[i]); + bd->bt_sel |= BIT(j); + } + } + + /* TODO: Should patched WiFi mode & WiFi role patch */ + + /* update Bind-BT status map for BT0/BT1 */ + for (i = BTC_BT_1ST; i < BTC_ALL_BT_EZL; i++) { + if (!(bd->bt_sel & BIT(i))) + continue; + + for (j = BTC_BT_B2G; j <= BTC_BT_B5G; j++) { + if (!(bd->rf_band & BIT(j))) + continue; + + if (i == BTC_BT_EXT) { + bd->bt_profile |= cx->bt_ext.profile_map[j]; + weight[j] = cx->bt_ext.link_weight[j]; + if (weight[j] > bd->bt_link_weight) /* max */ + bd->bt_link_weight = weight[j]; + continue; + } else if (i == BTC_BT_1ST) { + bt = &cx->bt0; + } else { + bt = &cx->bt1; + } + + if (j == BTC_BT_B2G) + b = &bt->link_info; + else + b = &bt->link_info_56g; + + bd->bt_profile |= b->status.map.profile_map; + bd->bt_smap.a2dp_active |= b->a2dp_desc.active; + bd->bt_smap.a2dp_sink |= b->a2dp_desc.sink; + bd->bt_smap.pan_active |= b->pan_desc.active; + bd->bt_smap.connect |= b->status.map.connect; + bd->bt_smap.hid_cnt += (u8)b->hid_desc.pair_cnt; + bd->bt_smap.hid_type |= b->hid_desc.type; + bd->bt_smap.cis_cnt += (u8)b->leaudio_desc.cis_cnt; + bd->bt_smap.link_cnt += b->link_cnt.now; + bd->bt_smap.inq_page |= b->status.map.inq_pag; + bd->bt_smap.page |= b->pag; + + if (bt->link_weight[j] > bd->bt_link_weight) /* max */ + bd->bt_link_weight = bt->link_weight[j]; + + if (b->slave_role) + bd->bt_smap.slave_role = b->slave_role; + + if (b->a2dp_desc.vendor_id != 0) + bd->bt_smap.a2dp_vendor_id = + b->a2dp_desc.vendor_id; + } + } + + if (bd->bt_profile & BTC_BT_HFP) + bd->bt_smap.hfp_exist = 1; + if (bd->bt_profile & BTC_BT_HID) + bd->bt_smap.hid_exist = 1; + if (bd->bt_profile & BTC_BT_A2DP) + bd->bt_smap.a2dp_exist = 1; + if (bd->bt_profile & BTC_BT_PAN) + bd->bt_smap.pan_exist = 1; + if (bd->bt_profile & BTC_BT_BIS) + bd->bt_smap.bis_exist = 1; + if (bd->bt_profile & BTC_BT_CIS) + bd->bt_smap.cis_exist = 1; + if (bd->bt_profile & BTC_BT_THREAD) + bd->bt_smap.thread_exist = 1; + if (bd->bt_profile & BTC_BT_ULL) + bd->bt_smap.ull_exist = 1; +} + +#define _bind_is_btonly 0x7 +static void _set_coex_binding(struct rtw89_btc *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; + struct rtw89_btc_bt_info *bt1 = &cx->bt1; + struct rtw89_btc_wl_info *wl = &cx->wl; + 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; + + /* + * 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) + * + * ant_xmap(ANT-Division-Multiplexing) + * ==> dedicated-ant->0, BTG/BTA/SPDT->1 + * + * xtk_xmap(Cross-talk map)-> calculate WL/BT interference by SIR + * ==> 1: interference, 0: no-interference + * + * fdm_map(Frequency-Division-Multiplexing): WL/BT RF_Band overlap-map + * 2bit-map: bit[1]:5GHz/6GHz, bit[0]:2.4GHz + */ + for (i = 0; i < RTW89_PHY_NUM; i++) { + if (wl->rf_band_map[i] & BIT(RTW89_BAND_2G)) + wl_rf_band[i] |= BIT(RTW89_BAND_2G); + + if (wl->rf_band_map[i] & (BIT(RTW89_BAND_5G) | BIT(RTW89_BAND_6G))) + wl_rf_band[i] |= BIT(RTW89_BAND_5G); + + dm->fit_xmap[i][BTC_BT_1ST] = wl_rf_band[i] & bt0->rf_band_map; + dm->fit_xmap[i][BTC_BT_2ND] = wl_rf_band[i] & bt1->rf_band_map; + dm->fit_xmap[i][BTC_BT_EXT] = wl_rf_band[i] & bt2->rf_band_map; + } + + /* + * if MR_WTYPE_MLD2L1R_NONMLD, FW will change 2+0/0+2/1+1 + * Therefore, BTC_MLO_RF_xxx is not real-time state. + * if link mode change by _ntfy_generic(BTC_GNTFY_MRCX_INFO) + * the HW-BAND is decided by wl->mlo_info.mrcx_act_hwb_map + * In this case, BTC_RF_S0->HWB0, BTC_RF_S1->HWB1 + */ + if (wl->mlo_info.wtype == RTW89_MR_WTYPE_MLD2L1R_NONMLD) { + if (wl->role_info.link_mode != BTC_WLINK_2G_MCC && + wl->role_info.link_mode != BTC_WLINK_25G_MCC && + wl->role_info.link_mode != BTC_WLINK_25G_DBCC) {/* mode chg */ + if (wl->mlo_info.mrcx_act_hwb_map == BIT(RTW89_PHY_1)) + path_hwb[BTC_RF_S0] = RTW89_PHY_1;/* S0/1->HWB1 */ + else + path_hwb[BTC_RF_S1] = RTW89_PHY_0;/* S0/1->HWB0 */ + } + } else { + if (wl->mlo_info.rf_combination == BTC_MLO_RF_2_PLUS_0) + path_hwb[BTC_RF_S1] = RTW89_PHY_0; /* 2+0 RF-S0/1->HWB0 */ + else if (wl->mlo_info.rf_combination == BTC_MLO_RF_0_PLUS_2) + path_hwb[BTC_RF_S0] = RTW89_PHY_1; /* 2+0 RF-S0/1->HWB1 */ + } + + /* + * 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 (*) + */ + + 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]; + } + + /* TDD-Binding */ + _set_bind_info(btc, BTC_MECH_TDD); + + /* FDD-Binding */ + _set_bind_info(btc, BTC_MECH_FDD); + + dm->out_of_band = !dm->tdd_bind.rf_band && !dm->fdd_bind.rf_band; + dm->fdd_en = !!dm->fdd_bind.rf_band; + dm->tdd_en = !!dm->tdd_bind.rf_band; + + /* set BT on/off state for GNT_WL Combined-MUX control */ + if (bt0->enable.now) + val |= BIT(0); + + if (bt1->enable.now) + val |= BIT(1); + + if (bt2->func_type) + val |= BIT(2); + + dm->ost_info.bt_enable_state = dm->bt_only ? _bind_is_btonly : val; +} + static void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) { @@ -7842,6 +8109,8 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) bt->scan_rx_low_pri = false; igno_bt = false; + _set_coex_binding(btc); + dm->freerun_chk = _check_freerun(rtwdev); /* check if meet freerun */ if (always_freerun) { @@ -8370,8 +8639,8 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) "[BTC], %s(): bt_info[2]=0x%02x\n", __func__, bt->raw_info[2]); - b->profile_cnt.last = b->profile_cnt.now; - b->profile_cnt.now = 0; + b->link_cnt.last = b->link_cnt.now; + b->link_cnt.now = 0; hid->type = 0; /* parse raw info low-Byte2 */ @@ -8384,13 +8653,13 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) bt->bcnt[BTC_BCNT_INQPAG] += !!(bt->inq_pag.now && !bt->inq_pag.last); hfp->exist = btinfo.lb2.hfp; - b->profile_cnt.now += (u8)hfp->exist; + b->link_cnt.now += (u8)hfp->exist; hid->exist = btinfo.lb2.hid; - b->profile_cnt.now += (u8)hid->exist; + b->link_cnt.now += (u8)hid->exist; a2dp->exist = btinfo.lb2.a2dp; - b->profile_cnt.now += (u8)a2dp->exist; + b->link_cnt.now += (u8)a2dp->exist; pan->exist = btinfo.lb2.pan; - b->profile_cnt.now += (u8)pan->exist; + b->link_cnt.now += (u8)pan->exist; btc->dm.trx_info.bt_profile = u32_get_bits(btinfo.val, BT_PROFILE_PROTOCOL_MASK); /* parse raw info low-Byte3 */ @@ -9418,7 +9687,7 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, " %-15s : profile:%s%s%s%s%s ", "[profile]", - (bt_linfo->profile_cnt.now == 0) ? "None," : "", + (bt_linfo->link_cnt.now == 0) ? "None," : "", bt_linfo->hfp_desc.exist ? "HFP," : "", bt_linfo->hid_desc.exist ? "HID," : "", bt_linfo->a2dp_desc.exist ? @@ -9520,7 +9789,7 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, "\n"); } - if (ver_main >= 9 && bt_linfo->profile_cnt.now) + if (ver_main >= 9 && bt_linfo->link_cnt.now) rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_TX_PWR_LVL, true); else rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_TX_PWR_LVL, false); @@ -9542,7 +9811,7 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) } p += scnprintf(p, end - p, "\n"); - if (bt_linfo->profile_cnt.now || bt_linfo->status.map.ble_connect) + if (bt_linfo->link_cnt.now || bt_linfo->status.map.ble_connect) rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP, true); else rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP, false); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 3f77707e2733..21bdf229723c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1445,6 +1445,11 @@ enum rtw89_btc_bt_state_cnt { BTC_BCNT_NUM, }; +enum rtw89_btc_bt_mech_type { + BTC_MECH_TDD = 0, + BTC_MECH_FDD = 1, +}; + enum rtw89_btc_bt_rf_band { BTC_BT_B2G = 0x0, /* 2.4GHz */ BTC_BT_B5G = 0x1, /* 5GHz or 6GHz */ @@ -1463,6 +1468,12 @@ enum rtw89_btc_bt_profile { BTC_BT_HID = BIT(1), BTC_BT_A2DP = BIT(2), BTC_BT_PAN = BIT(3), + BTC_BT_BIS = BIT(4), + BTC_BT_CIS = BIT(5), + BTC_BT_THREAD = BIT(6), + BTC_BT_ULL = BIT(7), + BTC_BT_LEGACY = 0xf, + BTC_BT_FULL = 0x3f, BTC_PROFILE_MAX = 4, }; @@ -1688,16 +1699,16 @@ struct rtw89_btc_bt_ver_info { }; struct rtw89_btc_bool_sta_chg { - u32 now: 1; - u32 last: 1; - u32 remain: 1; - u32 srvd: 29; + u8 now: 1; + u8 last: 1; + u8 remain: 1; + u8 srvd: 5; }; struct rtw89_btc_u8_sta_chg { u8 now; u8 last; - u8 remain; + u8 chg; u8 rsvd; }; @@ -1956,6 +1967,8 @@ struct rtw89_btc_bt_smap { u32 sco_busy: 1; u32 mesh_busy: 1; u32 inq_pag: 1; + u32 profile_map: 8; + u32 rsvd: 18; }; union rtw89_btc_bt_state_map { @@ -1974,14 +1987,30 @@ struct rtw89_btc_bt_txpwr_desc { u8 le_gain_index; }; +struct rtw89_btc_bt_leaudio_desc { + u32 bis_exist: 1; + u32 bis_exist_last: 1; + u32 cis_exist: 1; + u32 cis_exist_last: 1; + u32 bis_cnt: 3; + u32 cis_cnt: 3; + u32 rssi: 8; + u32 bis_cnt_last: 3; + u32 cis_cnt_last: 3; + u32 rsvd: 8; + + u16 diff_t; +}; + struct rtw89_btc_bt_link_info { - struct rtw89_btc_u8_sta_chg profile_cnt; + struct rtw89_btc_u8_sta_chg link_cnt; struct rtw89_btc_bool_sta_chg multi_link; struct rtw89_btc_bool_sta_chg relink; struct rtw89_btc_bt_hfp_desc hfp_desc; struct rtw89_btc_bt_hid_desc hid_desc; struct rtw89_btc_bt_a2dp_desc a2dp_desc; struct rtw89_btc_bt_pan_desc pan_desc; + struct rtw89_btc_bt_leaudio_desc leaudio_desc; union rtw89_btc_bt_state_map status; struct rtw89_btc_bt_txpwr_desc bt_txpwr_desc; @@ -1990,14 +2019,59 @@ struct rtw89_btc_bt_link_info { u8 rssi_state[BTC_BT_RSSI_THMAX]; u8 afh_map[BTC_BT_AFH_GROUP]; u8 afh_map_le[BTC_BT_AFH_LE_GROUP]; + u8 rssi; - u32 role_sw: 1; - u32 slave_role: 1; - u32 afh_update: 1; - u32 cqddr: 1; - u32 rssi: 8; - u32 tx_3m: 1; - u32 rsvd: 19; + u8 role_sw: 1; + u8 slave_role: 1; + u8 afh_update: 1; + u8 cqddr: 1; + u8 tx_3m: 1; + u8 inq: 1; + u8 pag: 1; + u8 igno_wl: 1; + + u8 ble_scan_en: 1; + u8 reinit: 1; + u8 rsvd: 6; +}; + +struct rtw89_btc_bind_bt_status { + u8 a2dp_active: 1; + u8 a2dp_sink: 1; + u8 pan_active: 1; + u8 connect: 1; + u8 inq_page: 1; + u8 multi_link: 1; + u8 slave_role: 1; + u8 page: 1; + + u8 hfp_exist: 1; + u8 hid_exist: 1; + u8 a2dp_exist: 1; + u8 pan_exist: 1; + u8 bis_exist: 1; + u8 cis_exist: 1; + u8 thread_exist: 1; + u8 ull_exist: 1; + + u8 hid_cnt; + u8 hid_type; + u8 cis_cnt; + u8 link_cnt; + + u16 a2dp_vendor_id; +}; + +struct rtw89_btc_bind_info { + u8 wl_hwb_sel; /* map */ + u8 wl_link_mode; + u8 wl_bg_mode; + u8 rf_band; /* map, 0: no any rf-band bind */ + u8 bt_sel; /* map */ + u8 bt_link_weight; /* select the highest weight between bt/rf-band */ + + u32 bt_profile; /* map */ + struct rtw89_btc_bind_bt_status bt_smap; }; struct rtw89_btc_extsoc_info { @@ -2105,6 +2179,7 @@ struct rtw89_btc_wl_info { u8 coex_mode; u8 pta_req_mac; u8 bt_polut_type[RTW89_PHY_NUM]; /* BT polluted WL-Tx type for phy0/1 */ + u8 rf_band_map[RTW89_PHY_NUM]; /* rf_band bit-map */ bool is_5g_hi_channel; bool go_client_exist; @@ -2291,6 +2366,7 @@ union rtw89_btc_fbtc_btscan { struct rtw89_btc_bt_info { struct rtw89_btc_bt_link_info link_info; + struct rtw89_btc_bt_link_info link_info_56g; struct rtw89_btc_bt_scan_info_v1 scan_info_v1[BTC_SCAN_MAX1]; struct rtw89_btc_bt_scan_info_v2 scan_info_v2[CXSCAN_MAX]; struct rtw89_btc_bt_ver_info ver_info; @@ -2301,6 +2377,7 @@ struct rtw89_btc_bt_info { u8 raw_info[BTC_BTINFO_MAX]; /* raw bt info from mailbox */ u8 txpwr_info[BTC_BTINFO_MAX]; + u8 link_weight[BTC_BT_BMAX]; /* Link Weight for RF-band/HWB selection */ u8 rssi_level; u8 rf_band_map; u8 func_type; @@ -3178,6 +3255,7 @@ struct rtw89_btc_fbtc_outsrc_set_info { u8 pta_req_hw_band; u8 rf_gbt_source; + u8 bt_enable_state; } __packed; union rtw89_btc_fbtc_slot_u { @@ -3200,8 +3278,20 @@ struct rtw89_btc_dm { struct rtw89_btc_wl_scc_ctrl wl_scc; struct rtw89_btc_trx_info trx_info; union rtw89_btc_dm_error_map error; + struct rtw89_btc_bind_info tdd_bind; + struct rtw89_btc_bind_info fdd_bind; u32 cnt_dm[BTC_DCNT_NUM]; u32 cnt_notify[BTC_NCNT_NUM]; + u8 ant_xmap[BTC_RF_NUM][BTC_ALL_BT_EZL]; /* WL-BT ANT interact-map */ + u8 xtk_xmap[BTC_RF_NUM][BTC_ALL_BT_EZL]; /* 1: If RSSI<(BT-Pin -SIR) */ + u8 sit_xmap[BTC_RF_NUM][BTC_ALL_BT_EZL]; /* WL-BT space interact-map */ + u8 fit_xmap[RTW89_PHY_NUM][BTC_ALL_BT_EZL]; /* HWB-BT freq interact-map */ + 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 */ + + u8 sit_xmap_last[BTC_RF_NUM][BTC_ALL_BT_EZL]; + u8 fit_xmap_last[RTW89_PHY_NUM][BTC_ALL_BT_EZL]; u32 update_slot_map; u32 set_ant_path; @@ -3239,9 +3329,12 @@ struct rtw89_btc_dm { u8 freerun_chk: 1; u8 wl_pre_agc_rb: 2; u8 bt_select: 2; /* 0:s0, 1:s1, 2:s0 & s1, refer to enum btc_bt_index */ - u8 slot_req_more: 1; - u8 lps_ctrl_scbd: 1; + u8 slot_req_more: 1; + u8 out_of_band: 1; + u8 fdd_en: 1; + u8 tdd_en: 1; + u8 lps_ctrl_scbd: 1; u8 lps_ctrl_scbd_last: 1; u8 lps_ctrl_change: 1; }; From 3244261af91596da1f17adc0812e42cb7fea9983 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Mon, 6 Jul 2026 10:54:43 +0800 Subject: [PATCH 045/403] wifi: rtw89: coex: Add TDMA binding for dual MAC Because the two MAC should have their own individual using, they will need different TDMA mechanism. This patch will bind TDMA with MAC index, and also the corresponding antenna, hardware grant signal setting. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260706025445.18428-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 381 +++++++++------------- drivers/net/wireless/realtek/rtw89/core.h | 11 +- 2 files changed, 158 insertions(+), 234 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index f2dea0e8c8f2..c1c3638be26d 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -494,7 +494,7 @@ enum btc_ant_phase { BTC_ANT_FREERUN, BTC_ANT_WRFK, BTC_ANT_WRFK2, - BTC_ANT_BRFK, + BTC_ANT_PTA, /* for Multi-PTA, each hw-band has its own PTA */ BTC_ANT_MAX }; @@ -3894,6 +3894,41 @@ static void _set_policy(struct rtw89_dev *rtwdev, u16 policy_type, _fw_set_policy(rtwdev, policy_type, action); } +static void _set_tdma_bind(struct rtw89_dev *rtwdev, bool tdma_on) +{ + struct rtw89_btc_dm *dm = &rtwdev->btc.dm; + struct rtw89_btc_bind_info *bind; + u8 null_role = RTW89_WIFI_ROLE_STATION; + + if (dm->tdd_en) + bind = &dm->tdd_bind; /* tdd = 1 && fdd = 0 or 1 */ + else + bind = &dm->fdd_bind; /* tdd = 0 && fdd = 1 */ + + /* notify BT TDMA on/off by scoreboard for ACL/Scan schedule */ + _write_scbd(rtwdev, BTC_WSCB_TDMA, tdma_on); + + /* + * set hwb/bt bind to TDMA policy parameter + * FW-BTC will setup related hwbx vs. BTx coex tables by slot toggle + * ex: HWb0 + BT0 + BT1, HWB0-BT0/HWB0-BT1 coex by WL/BT slot toggle + */ + dm->tdma.bind = ((bind->bt_sel & 0xf) << 4) + (bind->wl_hwb_sel & 0x3); + if (dm->tdd_en) + dm->tdma.bind |= BIT(2); + if (dm->fdd_en) + dm->tdma.bind |= BIT(3); + if (rtwdev->btc.ver->fcxtdma != 8) + dm->tdma.bind |= 0; + + /* set Null-tx role for 2 HW-BAND TDMA (MLMR) */ + if (!dm->eslot_ctrl.en && dm->tdma.rxflctrl && + (bind->wl_hwb_sel == (BIT(RTW89_PHY_1) | BIT(RTW89_PHY_0)))) { + null_role = (null_role << 4) + null_role; + _tdma_set_flctrl_role(&rtwdev->btc, null_role); + } +} + #define BTC_B1_MAX 250 /* unit ms */ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) { @@ -3903,6 +3938,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) struct rtw89_btc_fbtc_slot *s = dm->slot.v1; u8 type; u32 tbl_w1, tbl_b1, tbl_b4; + bool tdma_on = false; if (btc->ant_type == BTC_ANT_SHARED) { if (btc->cx.wl.status.map._4way) @@ -3928,7 +3964,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) btc->update_policy_force = true; break; case BTC_CXP_OFF: /* TDMA off */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, false); + tdma_on = false; *t = t_def[CXTD_OFF]; s[CXST_OFF] = s_def[CXST_OFF]; @@ -3963,7 +3999,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_OFFB: /* TDMA off + beacon protect */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, false); + tdma_on = false; *t = t_def[CXTD_OFF_B2]; s[CXST_OFF] = s_def[CXST_OFF]; switch (policy_type) { @@ -3974,7 +4010,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) break; case BTC_CXP_OFFE: /* TDMA off + beacon protect + Ext_control */ btc->bt_req_en = true; - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_OFF_EXT]; switch (policy_type) { case BTC_CXP_OFFE_DEF: @@ -3992,7 +4028,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_FIX: /* TDMA Fix-Slot */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_FIX]; switch (policy_type) { case BTC_CXP_FIX_TD3030: @@ -4048,7 +4084,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_PFIX: /* PS-TDMA Fix-Slot */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_PFIX]; if (btc->cx.wl.role_info.role_map.role.ap) _tdma_set_flctrl(btc, CXFLC_QOSNULL); @@ -4081,7 +4117,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_AUTO: /* TDMA Auto-Slot */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_AUTO]; switch (policy_type) { case BTC_CXP_AUTO_TD50B1: @@ -4105,7 +4141,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_PAUTO: /* PS-TDMA Auto-Slot */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_PAUTO]; switch (policy_type) { case BTC_CXP_PAUTO_TD50B1: @@ -4129,7 +4165,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_AUTO2: /* TDMA Auto-Slot2 */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_AUTO2]; switch (policy_type) { case BTC_CXP_AUTO2_TD3050: @@ -4166,7 +4202,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_PAUTO2: /* PS-TDMA Auto-Slot2 */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_PAUTO2]; switch (policy_type) { case BTC_CXP_PAUTO2_TD3050: @@ -4203,6 +4239,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) } break; } + _set_tdma_bind(rtwdev, tdma_on); } EXPORT_SYMBOL(rtw89_btc_set_policy); @@ -4211,14 +4248,14 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_fbtc_tdma *t = &dm->tdma; - struct rtw89_btc_wl_role_info_v1 *wl_rinfo = &btc->cx.wl.role_info_v1; struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt0.link_info.a2dp_desc; struct rtw89_btc_bt_hid_desc *hid = &btc->cx.bt0.link_info.hid_desc; struct rtw89_btc_bt_hfp_desc *hfp = &btc->cx.bt0.link_info.hfp_desc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; u8 type, null_role; u32 tbl_w1, tbl_b1, tbl_b4; - u16 dur_2; + bool tdma_on = false; + u16 dur_1 = 0, dur_2; if (wl->status.map.lps) { _slot_set_le(btc, CXST_E2G, s_def[CXST_E2G].dur, @@ -4272,7 +4309,7 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) _slot_set_tbl(btc, CXST_OFF, cxtbl[2]); break; case BTC_CXP_OFF: /* TDMA off */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, false); + tdma_on = false; *t = t_def[CXTD_OFF]; _slot_set_le(btc, CXST_OFF, s_def[CXST_OFF].dur, s_def[CXST_OFF].cxtbl, s_def[CXST_OFF].cxtype); @@ -4324,7 +4361,7 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_OFFB: /* TDMA off + beacon protect */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, false); + tdma_on = false; *t = t_def[CXTD_OFF_B2]; _slot_set_le(btc, CXST_OFF, s_def[CXST_OFF].dur, s_def[CXST_OFF].cxtbl, s_def[CXST_OFF].cxtype); @@ -4338,24 +4375,35 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_OFFE: /* TDMA off + beacon protect + Ext_control */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_OFF_EXT]; /* To avoid wl-s0 tx break by hid/hfp tx */ if (hid->exist || hfp->exist) tbl_w1 = cxtbl[16]; + if (dm->eslot_ctrl.en) { + null_role = u8_encode_bits(dm->eslot_ctrl.nulltx_role1, 0x0f) | + u8_encode_bits(dm->eslot_ctrl.nulltx_role2, 0xf0); + _tdma_set_flctrl_role(btc, null_role); + _tdma_set_rxflctrl(btc, 1); + _tdma_set_txflctrl(btc, 1); + /* Set Null-Tx time-tick by E2G duration */ + dm->eslot_ctrl.nulltx_pre_time = 5; /* TODO: BIS COEX */ + dur_1 = dm->eslot_ctrl.nulltx_pre_time; + } + dur_2 = dm->e2g_slot_limit; switch (policy_type) { case BTC_CXP_OFFE_2GBWISOB: /* for normal-case */ - _slot_set(btc, CXST_E2G, 5, tbl_w1, SLOT_ISO); + _slot_set(btc, CXST_E2G, dur_1, tbl_w1, SLOT_ISO); _slot_set_le(btc, CXST_EBT, s_def[CXST_EBT].dur, s_def[CXST_EBT].cxtbl, s_def[CXST_EBT].cxtype); _slot_set_dur(btc, CXST_EBT, dur_2); break; case BTC_CXP_OFFE_2GISOB: /* for bt no-link */ - _slot_set(btc, CXST_E2G, 5, cxtbl[1], SLOT_ISO); + _slot_set(btc, CXST_E2G, dur_1, cxtbl[1], SLOT_ISO); _slot_set_le(btc, CXST_EBT, s_def[CXST_EBT].dur, s_def[CXST_EBT].cxtbl, s_def[CXST_EBT].cxtype); _slot_set_dur(btc, CXST_EBT, dur_2); @@ -4383,16 +4431,16 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) break; case BTC_CXP_OFFE_2GBWMIXB: if (a2dp->exist) - _slot_set(btc, CXST_E2G, 5, cxtbl[2], SLOT_MIX); + _slot_set(btc, CXST_E2G, dur_1, cxtbl[2], SLOT_MIX); else - _slot_set(btc, CXST_E2G, 5, tbl_w1, SLOT_MIX); + _slot_set(btc, CXST_E2G, dur_1, tbl_w1, SLOT_MIX); _slot_set_le(btc, CXST_EBT, cpu_to_le16(40), s_def[CXST_EBT].cxtbl, s_def[CXST_EBT].cxtype); _slot_set_dur(btc, CXST_EBT, dur_2); break; case BTC_CXP_OFFE_WL: /* for 4-way */ - _slot_set(btc, CXST_E2G, 5, cxtbl[1], SLOT_MIX); - _slot_set(btc, CXST_EBT, 5, cxtbl[1], SLOT_MIX); + _slot_set(btc, CXST_E2G, dur_1, cxtbl[1], SLOT_MIX); + _slot_set(btc, CXST_EBT, dur_1, cxtbl[1], SLOT_MIX); break; default: break; @@ -4403,7 +4451,7 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) s_def[CXST_OFF].cxtbl, s_def[CXST_OFF].cxtype); break; case BTC_CXP_FIX: /* TDMA Fix-Slot */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_FIX]; switch (policy_type) { @@ -4462,7 +4510,7 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_PFIX: /* PS-TDMA Fix-Slot */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_PFIX]; switch (policy_type) { @@ -4501,7 +4549,7 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_AUTO: /* TDMA Auto-Slot */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_AUTO]; switch (policy_type) { @@ -4528,7 +4576,7 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_PAUTO: /* PS-TDMA Auto-Slot */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_PAUTO]; switch (policy_type) { @@ -4555,7 +4603,7 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_AUTO2: /* TDMA Auto-Slot2 */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_AUTO2]; switch (policy_type) { @@ -4597,7 +4645,7 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) } break; case BTC_CXP_PAUTO2: /* PS-TDMA Auto-Slot2 */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, true); + tdma_on = true; *t = t_def[CXTD_PAUTO2]; switch (policy_type) { @@ -4640,20 +4688,22 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) break; } - if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC && dm->tdma.rxflctrl) { - null_role = FIELD_PREP(0x0f, dm->wl_scc.null_role1) | - FIELD_PREP(0xf0, dm->wl_scc.null_role2); - _tdma_set_flctrl_role(btc, null_role); - } - /* enter leak_slot after each null-1 */ if (dm->leak_ap && dm->tdma.leak_n > 1) _tdma_set_lek(btc, 1); - if (dm->tdma_instant_excute || dm->lps_ctrl_change) { + if (dm->tdma_instant_excute || + dm->error.map.tdma_no_sync || + dm->error.map.slot_no_sync || + dm->lps_ctrl_change) { btc->dm.tdma.option_ctrl |= BIT(0); btc->update_policy_force = true; } + + if (btc->cli_h2c_cmd) + btc->update_policy_force = true; + + _set_tdma_bind(rtwdev, tdma_on); } EXPORT_SYMBOL(rtw89_btc_set_policy_v1); @@ -4697,20 +4747,29 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec, struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; - u8 gnt_wl_ctrl, gnt_bt_ctrl, plt_ctrl, i, b2g = 0; - bool dbcc_chg = false; + u8 gwl, gwl0, gwl1, gbt, plt_ctrl, i, dbcc_2g_phy, b2g = 0; + bool dbcc_chg = false, dbcc_en = false; u32 ant_path_type; ant_path_type = ((phy_map << 8) + type); - if (btc->ver->fwlrole == 1) + if (btc->ver->fwlrole == 1) { dbcc_chg = wl->role_info_v1.dbcc_chg; - else if (btc->ver->fwlrole == 2) + dbcc_en = wl->role_info_v1.dbcc_en; + dbcc_2g_phy = wl->role_info_v1.dbcc_2g_phy; + } else if (btc->ver->fwlrole == 2) { dbcc_chg = wl->role_info_v2.dbcc_chg; - else if (btc->ver->fwlrole == 7) + dbcc_en = wl->role_info_v2.dbcc_en; + dbcc_2g_phy = wl->role_info_v2.dbcc_2g_phy; + } else if (btc->ver->fwlrole == 7) { dbcc_chg = wl->role_info_v7.dbcc_chg; - else if (btc->ver->fwlrole == 8) + dbcc_en = wl->role_info_v7.dbcc_en; + dbcc_2g_phy = wl->role_info_v7.dbcc_2g_phy; + } else if (btc->ver->fwlrole == 8) { dbcc_chg = wl->role_info_v8.dbcc_chg; + dbcc_en = wl->role_info_v8.dbcc_en; + dbcc_2g_phy = wl->role_info_v8.dbcc_2g_phy; + } if (btc->dm.run_reason == BTC_RSN_NTFY_POWEROFF || btc->dm.run_reason == BTC_RSN_NTFY_RADIO_STATE || @@ -4768,14 +4827,14 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec, for (i = 0; i < RTW89_PHY_NUM; i++) { b2g = (wl_dinfo->real_band[i] == RTW89_BAND_2G); - gnt_wl_ctrl = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI; - gnt_bt_ctrl = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI; + gwl = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI; + gbt = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI; /* BT should control by GNT_BT if WL_2G at S0 */ if (i == 1 && wl_dinfo->real_band[0] == RTW89_BAND_2G && wl_dinfo->real_band[1] == RTW89_BAND_5G) - gnt_bt_ctrl = BTC_GNT_HW; - _set_gnt(rtwdev, BIT(i), gnt_wl_ctrl, gnt_bt_ctrl); + gbt = BTC_GNT_HW; + _set_gnt(rtwdev, BIT(i), gwl, gbt); plt_ctrl = b2g ? BTC_PLT_BT : BTC_PLT_NONE; _set_bt_plut(rtwdev, BIT(i), plt_ctrl, plt_ctrl); @@ -4808,12 +4867,27 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec, _set_gnt(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO); _set_bt_plut(rtwdev, phy_map, BTC_PLT_NONE, BTC_PLT_NONE); break; - case BTC_ANT_BRFK: - rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_BT); - _set_gnt(rtwdev, phy_map, BTC_GNT_SW_LO, BTC_GNT_SW_HI); - _set_bt_plut(rtwdev, phy_map, BTC_PLT_NONE, BTC_PLT_NONE); - break; + case BTC_ANT_PTA: default: + gbt = BTC_GNT_HW; + if ((rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA) || + !dbcc_en) { + gwl = BTC_GNT_HW; + _set_gnt(rtwdev, BTC_PHY_ALL, gwl, gbt); + } else { + /* for DBCC Only-1-PTA */ + if (dbcc_2g_phy == RTW89_PHY_0) { + gwl0 = BTC_GNT_HW; + gwl1 = BTC_GNT_SW_HI; + } else { + gwl0 = BTC_GNT_SW_HI; + gwl1 = BTC_GNT_HW; + } + _set_gnt(rtwdev, BTC_PHY_0, gwl0, gbt); + _set_gnt(rtwdev, BTC_PHY_1, gwl1, gbt); + } + rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL); + _set_bt_plut(rtwdev, phy_map, BTC_PLT_NONE, BTC_PLT_NONE); break; } } @@ -4828,7 +4902,7 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec, u32 ant_path_type = rtw89_get_antpath_type(phy_map, type); struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; struct rtw89_btc_dm *dm = &btc->dm; - u8 gwl = BTC_GNT_HW; + u8 gwl = BTC_GNT_HW, gwl0, gwl1, gbt; if (btc->dm.run_reason == BTC_RSN_NTFY_POWEROFF || btc->dm.run_reason == BTC_RSN_NTFY_RADIO_STATE || @@ -4913,8 +4987,26 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec, _set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO, BTC_WLACT_SW_HI); /* no BT-Tx */ break; + case BTC_ANT_PTA: default: - return; + gbt = BTC_GNT_HW; + if ((rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA) || + !wl_rinfo->dbcc_en) { + gwl = BTC_GNT_HW; + _set_gnt_v1(rtwdev, BTC_PHY_ALL, gwl, gbt, BTC_WLACT_HW); + } else { + /* for DBCC Only-1-PTA */ + if (wl_rinfo->dbcc_2g_phy == RTW89_PHY_0) { + gwl0 = BTC_GNT_HW; + gwl1 = BTC_GNT_SW_HI; + } else { + gwl0 = BTC_GNT_SW_HI; + gwl1 = BTC_GNT_HW; + } + _set_gnt_v1(rtwdev, BTC_PHY_0, gwl0, gbt, BTC_WLACT_HW); + _set_gnt_v1(rtwdev, BTC_PHY_1, gwl1, gbt, BTC_WLACT_HW); + } + break; } _set_bt_plut(rtwdev, phy_map, BTC_PLT_GNT_WL, BTC_PLT_GNT_WL); @@ -6017,178 +6109,15 @@ static void _action_wl_2g_mcc(struct rtw89_dev *rtwdev) static void _action_wl_2g_scc(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - - _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); - - if (btc->ant_type == BTC_ANT_SHARED) { /* shared-antenna */ - if (btc->cx.bt0.link_info.link_cnt.now == 0) - _set_policy(rtwdev, - BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_SCC); - else - _set_policy(rtwdev, - BTC_CXP_OFFE_DEF, BTC_ACT_WL_2G_SCC); - } else { /* dedicated-antenna */ - _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_2G_SCC); - } -} - -static void _action_wl_2g_scc_v1(struct rtw89_dev *rtwdev) -{ - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_btc_wl_role_info_v1 *wl_rinfo = &wl->role_info_v1; - u16 policy_type = BTC_CXP_OFF_BT; - u32 dur; + u16 policy_type; - if (btc->ant_type == BTC_ANT_DEDICATED) { - policy_type = BTC_CXP_OFF_EQ0; - } else { - /* shared-antenna */ - switch (wl_rinfo->mrole_type) { - case BTC_WLMROLE_STA_GC: - dm->wl_scc.null_role1 = RTW89_WIFI_ROLE_STATION; - dm->wl_scc.null_role2 = RTW89_WIFI_ROLE_P2P_CLIENT; - dm->wl_scc.ebt_null = 0; /* no ext-slot-control */ - _action_by_bt(rtwdev); - return; - case BTC_WLMROLE_STA_STA: - dm->wl_scc.null_role1 = RTW89_WIFI_ROLE_STATION; - dm->wl_scc.null_role2 = RTW89_WIFI_ROLE_STATION; - dm->wl_scc.ebt_null = 0; /* no ext-slot-control */ - _action_by_bt(rtwdev); - return; - case BTC_WLMROLE_STA_GC_NOA: - case BTC_WLMROLE_STA_GO: - case BTC_WLMROLE_STA_GO_NOA: - dm->wl_scc.null_role1 = RTW89_WIFI_ROLE_STATION; - dm->wl_scc.null_role2 = RTW89_WIFI_ROLE_NONE; - dur = wl_rinfo->mrole_noa_duration; + if (!dm->tdd_bind.bt_smap.connect) + policy_type = BTC_CXP_OFFE_2GISOB; + else + policy_type = BTC_CXP_OFFE_2GBWISOB; - if (wl->status.map._4way) { - dm->wl_scc.ebt_null = 0; - policy_type = BTC_CXP_OFFE_WL; - } else if (bt->link_info.status.map.connect == 0) { - dm->wl_scc.ebt_null = 0; - policy_type = BTC_CXP_OFFE_2GISOB; - } else if (bt->link_info.a2dp_desc.exist && - dur < btc->bt_req_len[RTW89_PHY_0]) { - dm->wl_scc.ebt_null = 1; /* tx null at EBT */ - policy_type = BTC_CXP_OFFE_2GBWMIXB2; - } else if (bt->link_info.a2dp_desc.exist || - bt->link_info.pan_desc.exist) { - dm->wl_scc.ebt_null = 1; /* tx null at EBT */ - policy_type = BTC_CXP_OFFE_2GBWISOB; - } else { - dm->wl_scc.ebt_null = 0; - policy_type = BTC_CXP_OFFE_2GBWISOB; - } - break; - default: - break; - } - } - - _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); - _set_policy(rtwdev, policy_type, BTC_ACT_WL_2G_SCC); -} - -static void _action_wl_2g_scc_v2(struct rtw89_dev *rtwdev) -{ - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; - struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_btc_wl_role_info_v2 *rinfo_v2 = &wl->role_info_v2; - struct rtw89_btc_wl_role_info_v7 *rinfo_v7 = &wl->role_info_v7; - u32 dur, mrole_type, mrole_noa_duration; - u16 policy_type = BTC_CXP_OFF_BT; - - if (btc->ver->fwlrole == 2) { - mrole_type = rinfo_v2->mrole_type; - mrole_noa_duration = rinfo_v2->mrole_noa_duration; - } else if (btc->ver->fwlrole == 7) { - mrole_type = rinfo_v7->mrole_type; - mrole_noa_duration = rinfo_v7->mrole_noa_duration; - } else { - return; - } - - if (btc->ant_type == BTC_ANT_DEDICATED) { - policy_type = BTC_CXP_OFF_EQ0; - } else { - /* shared-antenna */ - switch (mrole_type) { - case BTC_WLMROLE_STA_GC: - dm->wl_scc.null_role1 = RTW89_WIFI_ROLE_STATION; - dm->wl_scc.null_role2 = RTW89_WIFI_ROLE_P2P_CLIENT; - dm->wl_scc.ebt_null = 0; /* no ext-slot-control */ - _action_by_bt(rtwdev); - return; - case BTC_WLMROLE_STA_STA: - dm->wl_scc.null_role1 = RTW89_WIFI_ROLE_STATION; - dm->wl_scc.null_role2 = RTW89_WIFI_ROLE_STATION; - dm->wl_scc.ebt_null = 0; /* no ext-slot-control */ - _action_by_bt(rtwdev); - return; - case BTC_WLMROLE_STA_GC_NOA: - case BTC_WLMROLE_STA_GO: - case BTC_WLMROLE_STA_GO_NOA: - dm->wl_scc.null_role1 = RTW89_WIFI_ROLE_STATION; - dm->wl_scc.null_role2 = RTW89_WIFI_ROLE_NONE; - dur = mrole_noa_duration; - - if (wl->status.map._4way) { - dm->wl_scc.ebt_null = 0; - policy_type = BTC_CXP_OFFE_WL; - } else if (bt->link_info.status.map.connect == 0) { - dm->wl_scc.ebt_null = 0; - policy_type = BTC_CXP_OFFE_2GISOB; - } else if (bt->link_info.a2dp_desc.exist && - dur < btc->bt_req_len[RTW89_PHY_0]) { - dm->wl_scc.ebt_null = 1; /* tx null at EBT */ - policy_type = BTC_CXP_OFFE_2GBWMIXB2; - } else if (bt->link_info.a2dp_desc.exist || - bt->link_info.pan_desc.exist) { - dm->wl_scc.ebt_null = 1; /* tx null at EBT */ - policy_type = BTC_CXP_OFFE_2GBWISOB; - } else { - dm->wl_scc.ebt_null = 0; - policy_type = BTC_CXP_OFFE_2GBWISOB; - } - break; - default: - break; - } - } - - _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); - _set_policy(rtwdev, policy_type, BTC_ACT_WL_2G_SCC); -} - -static void _action_wl_2g_scc_v8(struct rtw89_dev *rtwdev) -{ - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; - struct rtw89_btc_dm *dm = &btc->dm; - u16 policy_type = BTC_CXP_OFF_BT; - - if (btc->ant_type == BTC_ANT_SHARED) { - if (wl->status.map._4way) - policy_type = BTC_CXP_OFFE_WL; - else if (bt->link_info.status.map.connect == 0) - policy_type = BTC_CXP_OFFE_2GISOB; - else - policy_type = BTC_CXP_OFFE_2GBWISOB; - } else { - policy_type = BTC_CXP_OFF_EQ0; - } - - dm->e2g_slot_limit = BTC_E2G_LIMIT_DEF; - - _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); + _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_PTA); _set_policy(rtwdev, policy_type, BTC_ACT_WL_2G_SCC); } @@ -8180,14 +8109,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) break; case BTC_WLINK_2G_SCC: bt->scan_rx_low_pri = true; - if (ver->fwlrole == 0) - _action_wl_2g_scc(rtwdev); - else if (ver->fwlrole == 1) - _action_wl_2g_scc_v1(rtwdev); - else if (ver->fwlrole == 2 || ver->fwlrole == 7) - _action_wl_2g_scc_v2(rtwdev); - else if (ver->fwlrole == 8) - _action_wl_2g_scc_v8(rtwdev); + _action_wl_2g_scc(rtwdev); break; case BTC_WLINK_2G_MCC: bt->scan_rx_low_pri = true; @@ -10115,7 +10037,8 @@ static const char *id_to_ant(u32 id) CASE_BTC_ANTPATH_STR(W25G); CASE_BTC_ANTPATH_STR(FREERUN); CASE_BTC_ANTPATH_STR(WRFK); - CASE_BTC_ANTPATH_STR(BRFK); + CASE_BTC_ANTPATH_STR(WRFK2); + CASE_BTC_ANTPATH_STR(PTA); CASE_BTC_ANTPATH_STR(MAX); default: return "unknown"; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 21bdf229723c..eb814425f536 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1832,10 +1832,11 @@ struct rtw89_btc_wl_role_info_bpos { u16 nan: 1; }; -struct rtw89_btc_wl_scc_ctrl { - u8 null_role1; - u8 null_role2; - u8 ebt_null; /* if tx null at EBT slot */ +struct rtw89_btc_eslot_ctrl { + u8 en; /* 1: toggle tx-flow-ctrl (null 0/1), tx-pause by Ext-slot */ + u8 nulltx_role1; + u8 nulltx_role2; + u8 nulltx_pre_time; /* null-tx time prior to EBT-start (from E2G-end) */ }; union rtw89_btc_wl_role_info_map { @@ -3275,7 +3276,7 @@ struct rtw89_btc_dm { struct rtw89_btc_rf_trx_para_v9 rf_trx_para; struct rtw89_btc_wl_tx_limit_para wl_tx_limit; struct rtw89_btc_dm_step dm_step; - struct rtw89_btc_wl_scc_ctrl wl_scc; + struct rtw89_btc_eslot_ctrl eslot_ctrl; struct rtw89_btc_trx_info trx_info; union rtw89_btc_dm_error_map error; struct rtw89_btc_bind_info tdd_bind; From 36f90091ee57deae8eef55e891f07458f08127eb Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Mon, 6 Jul 2026 10:54:44 +0800 Subject: [PATCH 046/403] wifi: rtw89: coex: Update scoreboard related logic for dual Bluetooth Update WiFi status to each Bluetooth & collect status from the two Bluetooth adapter by non-stop power zone register. To correct the meaning of variable, redefine the naming for the variables. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260706025445.18428-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 543 ++++++++++++++-------- drivers/net/wireless/realtek/rtw89/coex.h | 9 + drivers/net/wireless/realtek/rtw89/core.c | 4 +- drivers/net/wireless/realtek/rtw89/core.h | 33 +- 4 files changed, 379 insertions(+), 210 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index c1c3638be26d..9e51906b1ccc 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -461,7 +461,14 @@ enum btc_b2w_scoreboard { BTC_BSCB_BT_LNAB1 = BIT(10), BTC_BSCB_WLRFK = BIT(11), BTC_BSCB_BT_HILNA = BIT(13), + BTC_BSCB_BT_HILNA_56G = BIT(14), + BTC_BSCB_CTCODE = BIT(15), BTC_BSCB_BT_CONNECT = BIT(16), + BTC_BSCB_BT_CONNECT_56G = BIT(17), + BTC_BSCB_BT_LNAB0_56G = BIT(18), + BTC_BSCB_BT_LNAB1_56G = BIT(19), + BTC_BSCB_BT_15DOT4 = BIT(24), + BTC_BSCB_BT_PROTECT = BIT(27), BTC_BSCB_PAN_ACT = BIT(28), BTC_BSCB_HFP_ACT = BIT(29), BTC_BSCB_PATCH_CODE = BIT(30), @@ -808,6 +815,8 @@ enum btc_reset_module { BTC_RESET_CXDM = BIT(0) | BIT(1), BTC_RESET_BTINFO = BIT(3), BTC_RESET_MDINFO = BIT(4), + BTC_RESET_BT_PSD_DM = BIT(5), + BTC_RESET_BTINFO2 = BIT(6), BTC_RESET_ALL = GENMASK(7, 0), }; @@ -905,8 +914,9 @@ enum btc_reason_and_action { static void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason); -static void _write_scbd(struct rtw89_dev *rtwdev, u32 val, bool state); -static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update); +static void _write_scbd(struct rtw89_dev *rtwdev, u8 bid, u32 val, bool state); +static u8 _sned_h2c_w2bscbd(struct rtw89_dev *rtwdev, bool force_exec, u8 bid); +static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid); static int _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, void *param, u16 len) @@ -1272,11 +1282,6 @@ static void _chk_btc_err(struct rtw89_dev *rtwdev, u8 type, u32 cnt) dm->cnt_dm[BTC_DCNT_BTCNT_HANG]++; else dm->cnt_dm[BTC_DCNT_BTCNT_HANG] = 0; - - if ((dm->cnt_dm[BTC_DCNT_BTCNT_HANG] >= BTC_CHK_HANG_MAX && - bt->enable.now) || (!dm->cnt_dm[BTC_DCNT_BTCNT_HANG] && - !bt->enable.now)) - _update_bt_scbd(rtwdev, false); break; case BTC_DCNT_WL_SLOT_DRIFT: if (cnt >= BTC_CHK_WLSLOT_DRIFT_MAX) @@ -1755,7 +1760,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, rtwdev->chip->ops->btc_update_bt_cnt(rtwdev); _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); - bt->bcnt[BTC_BCNT_POLUT] = + bt->bcnt[BTC_BCNT_POLLUTED] = rtw89_mac_get_plt_cnt(rtwdev, RTW89_MAC_0); } @@ -1778,7 +1783,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_LO_TX]); bt->bcnt[BTC_BCNT_LOPRI_RX] = le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_LO_RX]); - bt->bcnt[BTC_BCNT_POLUT] = + bt->bcnt[BTC_BCNT_POLLUTED] = le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_POLLUTED]); _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); @@ -1810,7 +1815,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_LO_TX]); bt->bcnt[BTC_BCNT_LOPRI_RX] = le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_LO_RX]); - bt->bcnt[BTC_BCNT_POLUT] = + bt->bcnt[BTC_BCNT_POLLUTED] = le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_POLLUTED]); _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); @@ -1837,7 +1842,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, le16_to_cpu(prpt->v105.bt_cnt[BTC_BCNT_LO_TX_V105]); bt->bcnt[BTC_BCNT_LOPRI_RX] = le16_to_cpu(prpt->v105.bt_cnt[BTC_BCNT_LO_RX_V105]); - bt->bcnt[BTC_BCNT_POLUT] = + bt->bcnt[BTC_BCNT_POLLUTED] = le16_to_cpu(prpt->v105.bt_cnt[BTC_BCNT_POLLUTED_V105]); _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); @@ -2791,14 +2796,14 @@ static void _fw_set_policy(struct rtw89_dev *rtwdev, u16 policy_type, if (dm->tdma.rxflctrl == CXFLC_NULLP || dm->tdma.rxflctrl == CXFLC_QOSNULL) - btc->lps = 1; + btc->btc_ctrl_lps = 1; else - btc->lps = dm->lps_ctrl_scbd; + btc->btc_ctrl_lps = dm->lps_ctrl_scbd; dm->lps_ctrl_scbd_last = dm->lps_ctrl_scbd; - if (btc->lps == 1) - rtw89_set_coex_ctrl_lps(rtwdev, btc->lps); + if (btc->btc_ctrl_lps == 1) + rtw89_set_coex_ctrl_lps(rtwdev, btc->btc_ctrl_lps); ret = _send_fw_cmd(rtwdev, BTFC_SET, SET_CX_POLICY, btc->policy, btc->policy_len); @@ -2813,8 +2818,8 @@ static void _fw_set_policy(struct rtw89_dev *rtwdev, u16 policy_type, if (btc->update_policy_force) btc->update_policy_force = false; - if (btc->lps == 0) - rtw89_set_coex_ctrl_lps(rtwdev, btc->lps); + if (btc->btc_ctrl_lps == 0) + rtw89_set_coex_ctrl_lps(rtwdev, btc->btc_ctrl_lps); } static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) @@ -2913,21 +2918,35 @@ void btc_fw_event(struct rtw89_dev *rtwdev, u8 evt_id, void *data, u32 len) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; + u8 i; - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): evt_id:%d len:%d\n", - __func__, evt_id, len); + _parse_btc_report(rtwdev, pfwinfo, data, len); - if (!len || !data) + if (!rtwdev->chip->scbd) return; - switch (evt_id) { - case BTF_EVNT_RPT: - _parse_btc_report(rtwdev, pfwinfo, data, len); - break; - default: - break; + if (!btc->dm.scbd_b2w_update) + return; /* skip if init or no-update */ + + for (i = BTC_BT_1ST; i <= BTC_BT_2ND; i++) { + bt = (i == BTC_BT_1ST) ? &btc->cx.bt0 : &btc->cx.bt1; + + if (bt->scbd_c2h != bt->scbd_rb) { + /* if btx b2w scbd non-sync */ + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s() bt%d:c2h->0x%08x, rb->0x%08x\n", + __func__, i, bt->scbd_c2h, bt->scbd_rb); + bt->scbd_c2h = bt->scbd_rb; + _update_bt_scbd(rtwdev, i); + btc->dm.scbd_b2w_update = false; + } } + + if (!btc->dm.scbd_b2w_update) + _run_coex(rtwdev, BTC_RSN_UPDATE_BT_SCBD); + else + btc->dm.scbd_b2w_update = 0; } static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map, u8 wl_state, u8 bt_state) @@ -3298,7 +3317,7 @@ static void _set_bt_rx_gain(struct rtw89_dev *rtwdev, bool force_exec, u8 bid, if (buf[0] != BTC_BT_RX_NORMAL_LVL) state = true; - _write_scbd(rtwdev, scbd_bit, state); + _write_scbd(rtwdev, bid, scbd_bit, state); } } @@ -3406,7 +3425,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) if (dm->fddt_train) { _set_wl_rx_gain(rtwdev, 1, RTW89_PHY_0); - _write_scbd(rtwdev, BTC_WSCB_RXGAIN, true); + _write_scbd(rtwdev, bid, BTC_WSCB_RXGAIN, true); } else { _set_wl_tx_power(rtwdev, para.wl_tx_power[RTW89_PHY_0], RTW89_PHY_0); _set_wl_rx_gain(rtwdev, para.wl_rx_gain[RTW89_PHY_0], RTW89_PHY_0); @@ -3899,14 +3918,22 @@ static void _set_tdma_bind(struct rtw89_dev *rtwdev, bool tdma_on) struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_bind_info *bind; u8 null_role = RTW89_WIFI_ROLE_STATION; + u8 bt_sel; if (dm->tdd_en) bind = &dm->tdd_bind; /* tdd = 1 && fdd = 0 or 1 */ else bind = &dm->fdd_bind; /* tdd = 0 && fdd = 1 */ + if (bind->bt_sel >= (BIT(BTC_BT_1ST) | BIT(BTC_BT_2ND))) + bt_sel = BTC_ALL_BT; + else if (bind->bt_sel == BIT(BTC_BT_2ND)) + bt_sel = BTC_BT_2ND; + else + bt_sel = BTC_BT_1ST; + /* notify BT TDMA on/off by scoreboard for ACL/Scan schedule */ - _write_scbd(rtwdev, BTC_WSCB_TDMA, tdma_on); + _write_scbd(rtwdev, bt_sel, BTC_WSCB_TDMA, tdma_on); /* * set hwb/bt bind to TDMA policy parameter @@ -5849,7 +5876,7 @@ static void _set_bt_rx_agc(struct rtw89_dev *rtwdev) if (bt_hi_lna_rx == bt->hi_lna_rx) return; - _write_scbd(rtwdev, BTC_WSCB_BT_HILNA, bt_hi_lna_rx); + _write_scbd(rtwdev, BTC_BT_1ST, BTC_WSCB_BT_HILNA, bt_hi_lna_rx); } static void _set_bt_rx_scan_pri(struct rtw89_dev *rtwdev) @@ -5857,7 +5884,8 @@ static void _set_bt_rx_scan_pri(struct rtw89_dev *rtwdev) struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; - _write_scbd(rtwdev, BTC_WSCB_RXSCAN_PRI, (bool)(!!bt->scan_rx_low_pri)); + _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_RXSCAN_PRI, + (bool)(!!bt->scan_rx_low_pri)); } static void _wl_req_mac(struct rtw89_dev *rtwdev, u8 mac) @@ -5897,6 +5925,7 @@ static void _action_common(struct rtw89_dev *rtwdev) struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; u32 bt_rom_code_id, bt_fw_ver; + u8 i; if (btc->ver->fwlrole == 8) _wl_req_mac(rtwdev, rinfo_v8->pta_req_band); @@ -5929,13 +5958,8 @@ static void _action_common(struct rtw89_dev *rtwdev) rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_MREG, 1); } - if (wl->scbd_change) { - rtw89_mac_cfg_sb(rtwdev, wl->scbd); - rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], write scbd: 0x%08x\n", - wl->scbd); - wl->scbd_change = false; - wl->wcnt[BTC_WCNT_SCBDUPDATE]++; - } + for (i = BTC_BT_1ST; i <= BTC_BT_2ND; i++) + _sned_h2c_w2bscbd(rtwdev, false, i); if (btc->ver->fcxosi) { if (memcmp(&dm->ost_info_last, &dm->ost_info, @@ -6187,42 +6211,97 @@ static void _action_wl_2g_nan(struct rtw89_dev *rtwdev) } } -static u32 _read_scbd(struct rtw89_dev *rtwdev) +static u8 _sned_h2c_w2bscbd(struct rtw89_dev *rtwdev, bool force_exec, u8 bid) { - const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; - u32 scbd_val = 0; + struct rtw89_btc_cx *cx = &btc->cx; + struct rtw89_btc_wl_info *wl = &cx->wl; + u8 cnt_idx = BTC_WCNT_SCBDUPDATE; + u8 h2c_func = SET_IOFLD_SCBD; + u8 buf[4]; - if (!chip->scbd) - return 0; + if (bid == BTC_BT_2ND) { + if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT)) + return 0; - scbd_val = rtw89_mac_get_sb(rtwdev); - rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], read scbd: 0x%08x\n", - scbd_val); + h2c_func |= BT_H2C_FUNC_BT2ND; + cnt_idx = BTC_WCNT_SCBDUPDATE2; + } - btc->cx.bt0.bcnt[BTC_BCNT_SCBDREAD]++; - return scbd_val; + if (wl->scbd_chg[bid] || force_exec) { + /* Add delay to avoid BT FW loss information */ + + btc_dw2b(buf, 0, (wl->scbd[bid] | BIT(31))); /* trig */ + if (rtwdev->chip->para_ver & BTC_FEAT_H2C_MACRO) { + if (!_send_fw_cmd(rtwdev, BTFC_SET, + h2c_func, buf, sizeof(buf))) + return 0; + } else { + rtw89_mac_cfg_sb(rtwdev, wl->scbd[bid]); + } + wl->scbd_chg[bid] = 0; + wl->wcnt[cnt_idx]++; + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s() write BT%d scbd:0x%08x\n", + __func__, bid, wl->scbd[bid]); + } + return 1; } -static void _write_scbd(struct rtw89_dev *rtwdev, u32 val, bool state) +static void _write_scbd(struct rtw89_dev *rtwdev, u8 bid, u32 val, bool state) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - u32 scbd_val = 0; - u8 force_exec = false; + struct rtw89_btc_dm *dm = &btc->dm; + u8 force_exec, id, id_start, id_stop; + u32 scbd = 0; if (!chip->scbd) return; - scbd_val = state ? wl->scbd | val : wl->scbd & ~val; + if (bid == BTC_ALL_BT) { + id_start = BTC_BT_1ST; + id_stop = BTC_BT_2ND; + } else { + id_start = bid; + id_stop = bid; + } - if (val & BTC_WSCB_ACTIVE || val & BTC_WSCB_ON) - force_exec = true; + for (id = id_start; id <= id_stop; id++) { + force_exec = false; + if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) && + id == BTC_BT_2ND) + continue; - if (scbd_val != wl->scbd || force_exec) { - wl->scbd = scbd_val; - wl->scbd_change = true; + if (state) + scbd = wl->scbd[id] | val; + else + scbd = wl->scbd[id] & (~val); + + if (val & BTC_WSCB_CTCODE || + val & BTC_WSCB_RXGAIN || + val & BTC_WSCB_RXGAIN_56G) { /* instant exec */ + force_exec = true; + wl->scbd[id] = scbd; + dm->scbd_write_instant = 1; + if (rtwdev->chip->para_ver & BTC_FEAT_H2C_MACRO) + _sned_h2c_w2bscbd(rtwdev, force_exec, id); + else + rtw89_mac_cfg_sb(rtwdev, wl->scbd[id]); + dm->scbd_write_instant = 0; + } else if ((val & BTC_WSCB_ACTIVE || + val & BTC_WSCB_ON || + val & BTC_WSCB_WLRFK) || + scbd != wl->scbd[id]) { + /* + * Just update wl->scbd[] and set wl->scbd_chg[], + * moved "Write scoreboard I/O" to _action_common() + * _write_scbd will be executed if run_coex() + */ + wl->scbd[id] = scbd; + wl->scbd_chg[id] = 1; + } } } @@ -7522,35 +7601,28 @@ void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work) wl->wcnt[BTC_WCNT_RFK_TIMEOUT]++; dm->error.map.wl_rfk_timeout = true; wl->rfk_info.state = BTC_WRFK_STOP; - _write_scbd(rtwdev, BTC_WSCB_WLRFK, false); + _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_WLRFK, false); _run_coex(rtwdev, BTC_RSN_RFK_CHK_WORK); } } -static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) +static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid) { + struct rtw89_btc_bt_link_info *bt_2g, *bt_56g; struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; const struct rtw89_btc_ver *ver = btc->ver; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_dm *dm = &rtwdev->btc.dm; - bool bt_link_change = false, lps_ctrl = false; - u32 val, any_bt_connect; - u8 mode; + 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_info *bt; + u32 val, any_bt_connect, any_bt_6g_connect = 0; + u8 id, id_start, id_stop, mode; + bool bt_link_change = false; + bool lps_ctrl = false; - if (rtwdev->chip->scbd) + if (!rtwdev->chip->scbd || bid > BTC_ALL_BT) return; - rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s\n", __func__); - - val = _read_scbd(rtwdev); - if (val == BTC_SCB_INV_VALUE) { - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): return by invalid scbd value\n", - __func__); - return; - } - if (ver->fwlrole == 0) mode = wl->role_info.link_mode; else if (ver->fwlrole == 1) @@ -7564,66 +7636,112 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) else return; - if (!(val & BTC_BSCB_ON)) - bt->enable.now = 0; - else - bt->enable.now = 1; - - if (bt->enable.now != bt->enable.last) - bt_link_change = true; - - /* reset bt info if bt re-enable */ - if (bt->enable.now && !bt->enable.last) { - _reset_btc_var(rtwdev, BTC_RESET_BTINFO); - bt->bcnt[BTC_BCNT_REENABLE]++; - bt->enable.now = 1; - } - - bt->enable.last = bt->enable.now; - bt->scbd = val; - bt->mbx_avl = !!(val & BTC_BSCB_ACT); - - if (bt->whql_test != !!(val & BTC_BSCB_WHQL)) - bt_link_change = true; - - bt->whql_test = !!(val & BTC_BSCB_WHQL); - bt->btg_type = val & BTC_BSCB_BT_S1 ? BTC_BT_BTG : BTC_BT_ALONE; - bt->link_info.a2dp_desc.exist = !!(val & BTC_BSCB_A2DP_ACT); - bt->link_info.pan_desc.exist = !!(val & BTC_BSCB_PAN_ACT); - bt->link_info.hfp_desc.exist = !!(val & BTC_BSCB_HFP_ACT); - - bt->lna_constrain = !!(val & BTC_BSCB_BT_LNAB0) + - !!(val & BTC_BSCB_BT_LNAB1) * 2 + 4; - - /* if rfk run 1->0 */ - if (bt->rfk_info.map.run && !(val & BTC_BSCB_RFK_RUN)) - bt_link_change = true; - - bt->rfk_info.map.run = !!(val & BTC_BSCB_RFK_RUN); - bt->rfk_info.map.req = !!(val & BTC_BSCB_RFK_REQ); - bt->hi_lna_rx = !!(val & BTC_BSCB_BT_HILNA); - any_bt_connect = !!(val & BTC_BSCB_BT_CONNECT); - - /* if connect change */ - if (bt->link_info.status.map.connect != any_bt_connect) - bt_link_change = true; - - /* if specific profile exist */ - if (((bt->link_info.a2dp_desc.exist || bt->link_info.pan_desc.exist || - bt->link_info.hfp_desc.exist) && mode == BTC_WLINK_2G_STA) || - bt->whql_test) - lps_ctrl = true; - - if (dm->lps_ctrl_scbd != lps_ctrl) { - dm->lps_ctrl_scbd = lps_ctrl; - bt_link_change = true; - dm->lps_ctrl_change = true; + if (bid == BTC_ALL_BT) { + id_start = BTC_BT_1ST; + id_stop = BTC_BT_2ND; } else { - dm->lps_ctrl_change = false; + id_start = bid; + id_stop = bid; } - bt->link_info.status.map.connect = any_bt_connect; - bt->run_patch_code = !!(val & BTC_BSCB_PATCH_CODE); + dm->lps_ctrl_change = false; + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s()\n", __func__); + + for (id = id_start; id <= id_stop; id++) { + bt = (id == BTC_BT_1ST) ? &cx->bt0 : &cx->bt1; + bt_2g = &bt->link_info; + bt_56g = &bt->link_info_56g; + + if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) && id == BTC_BT_2ND) + break; + + val = bt->scbd_c2h; + + if (val == 0xffffffff) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s return by invalid scbd value\n", + __func__); + return; + } + + if (!(val & BTC_BSCB_ON)) + bt->enable.now = 0; + else + bt->enable.now = 1; + + if (bt->enable.now != bt->enable.last) + bt_link_change = true; + + /* reset bt info if bt re-enable */ + if (bt->enable.now && !bt->enable.last) { + if (id == BTC_BT_1ST) + _reset_btc_var(rtwdev, BTC_RESET_BTINFO); + else + _reset_btc_var(rtwdev, BTC_RESET_BTINFO2); + + bt->bcnt[BTC_BCNT_REENABLE]++; + bt->enable.now = 1; + bt->rf_band_map = BIT(RTW89_BAND_2G); + bt->link_weight[BTC_BT_B2G] = 5; /* no-profile */ + } + bt->enable.last = bt->enable.now; + + /* if BT will disconnect and adopt protect plan */ + if ((val & BTC_BSCB_BT_PROTECT) && + !(bt->scbd & BTC_BSCB_BT_PROTECT)) + bt->bcnt[BTC_BCNT_PROTECT]++; + + bt->mbx_avl = !!(val & BTC_BSCB_ACT); + if (bt->whql_test != !!(val & BTC_BSCB_WHQL)) + bt_link_change = true; + + bt->whql_test = !!(val & BTC_BSCB_WHQL); + bt->btg_type = (val & BTC_BSCB_BT_S1 ? BTC_BT_BTG : BTC_BT_ALONE); + + bt_2g->a2dp_desc.exist = !!(val & BTC_BSCB_A2DP_ACT); + bt_2g->pan_desc.exist = !!(val & BTC_BSCB_PAN_ACT); + bt_2g->hfp_desc.exist = !!(val & BTC_BSCB_HFP_ACT); + + bt->lna_constrain = 4 + !!(val & BTC_BSCB_BT_LNAB0) + + !!(val & BTC_BSCB_BT_LNAB1) * 2; + + if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) { + if (val & BTC_BSCB_BT_15DOT4) + bt->func_type |= (BTC_BTF_THREAD | BTC_BTF_ZB); + else + bt->func_type &= ~(BTC_BTF_THREAD | BTC_BTF_ZB); + } + + bt->hi_lna_rx = !!(val & BTC_BSCB_BT_HILNA); + any_bt_connect = !!(val & BTC_BSCB_BT_CONNECT); + + /* if connect change */ + if (bt_2g->status.map.connect != any_bt_connect || + bt_56g->status.map.connect != any_bt_6g_connect) { + bt_link_change = true; + bt_2g->status.map.connect = any_bt_connect; + bt_56g->status.map.connect = any_bt_6g_connect; + } + + /* if specific profile exist */ + if (((bt->link_info.a2dp_desc.exist || + bt->link_info.pan_desc.exist || + bt->link_info.hfp_desc.exist) && + mode == BTC_WLINK_2G_STA) || + bt->whql_test) + lps_ctrl = true; + + if (dm->lps_ctrl_scbd != lps_ctrl) { + dm->lps_ctrl_scbd = lps_ctrl; + bt_link_change = true; + dm->lps_ctrl_change = true; + } else { + dm->lps_ctrl_change = false; + } + + bt->run_patch_code = !!(val & BTC_BSCB_PATCH_CODE); + bt->scbd = val; + } if (bt_link_change) { rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -7651,26 +7769,6 @@ static void _update_bt_txpwr_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) memcpy(&b->bt_txpwr_desc, &buf[2], sizeof(b->bt_txpwr_desc)); } -static bool _chk_wl_rfk_request(struct rtw89_dev *rtwdev) -{ - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_cx *cx = &btc->cx; - struct rtw89_btc_bt_info *bt = &cx->bt0; - - _update_bt_scbd(rtwdev, true); - - cx->wl.wcnt[BTC_WCNT_RFK_REQ]++; - - if ((bt->rfk_info.map.run || bt->rfk_info.map.req) && - !bt->rfk_info.map.timeout) { - cx->wl.wcnt[BTC_WCNT_RFK_REJECT]++; - } else { - cx->wl.wcnt[BTC_WCNT_RFK_GO]++; - return true; - } - return false; -} - static void _set_bind_info(struct rtw89_btc *btc, u8 type) { struct rtw89_btc_cx *cx = &btc->cx; @@ -8181,7 +8279,7 @@ void rtw89_btc_ntfy_poweroff(struct rtw89_dev *rtwdev) btc->cx.wl.status.map.busy = 0; wl->status.map.lps = BTC_LPS_OFF; - _write_scbd(rtwdev, BTC_WSCB_ALL, false); + _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_ALL, false); _run_coex(rtwdev, BTC_RSN_NTFY_POWEROFF); rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_ALL, 0); @@ -8266,24 +8364,22 @@ void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode) return; } + btc->cx.bt0.enable.now = 1; + btc->cx.bt0.run_patch_code = 1; if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) { btc->cx.bt1.enable.now = 1; btc->cx.bt1.run_patch_code = 1; } - if (rtwdev->chip->para_ver & BTC_FEAT_H2C_MACRO) { - btc->cx.bt0.enable.now = 1; - btc->cx.bt0.run_patch_code = 1; + if (rtwdev->chip->para_ver & BTC_FEAT_H2C_MACRO) btc->io_oflld_type = BTC_IO_OFLD_BTC_H2C; - } else { + else btc->io_oflld_type = BTC_IO_OFLD_NO_SUPPORT; - _update_bt_scbd(rtwdev, true); - } chip->ops->btc_set_rfe(rtwdev); chip->ops->btc_init_cfg(rtwdev); - _write_scbd(rtwdev, + _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_ACTIVE | BTC_WSCB_ON | BTC_WSCB_BTLOG, true); if (rtw89_mac_get_ctrl_path(rtwdev)) { @@ -8842,16 +8938,16 @@ void rtw89_btc_ntfy_radio_state(struct rtw89_dev *rtwdev, enum btc_rfctrl rf_sta if (rf_state == BTC_RFCTRL_WL_ON) { rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_MREG, true); val = BTC_WSCB_ACTIVE | BTC_WSCB_ON | BTC_WSCB_BTLOG; - _write_scbd(rtwdev, val, true); + _write_scbd(rtwdev, BTC_ALL_BT, val, true); chip->ops->btc_init_cfg(rtwdev); } else { rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_ALL, false); if (rf_state == BTC_RFCTRL_FW_CTRL) - _write_scbd(rtwdev, BTC_WSCB_ACTIVE, false); + _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_ACTIVE, false); else if (rf_state == BTC_RFCTRL_WL_OFF) - _write_scbd(rtwdev, BTC_WSCB_ALL, false); + _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_ALL, false); else - _write_scbd(rtwdev, BTC_WSCB_ACTIVE, false); + _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_ACTIVE, false); } btc->dm.cnt_dm[BTC_DCNT_BTCNT_HANG] = 0; @@ -8883,27 +8979,26 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path, switch (state) { case BTC_WRFK_START: - result = _chk_wl_rfk_request(rtwdev); - wl->rfk_info.state = result ? BTC_WRFK_START : BTC_WRFK_STOP; - - _write_scbd(rtwdev, BTC_WSCB_WLRFK, result); + result = BTC_WRFK_ALLOW; + wl->rfk_info.state = BTC_WRFK_START; + btc->cx.wl.wcnt[BTC_WCNT_RFK_REQ]++; + btc->cx.wl.wcnt[BTC_WCNT_RFK_GO]++; btc->dm.cnt_notify[BTC_NCNT_WL_RFK]++; + + _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_WLRFK, true); break; case BTC_WRFK_ONESHOT_START: case BTC_WRFK_ONESHOT_STOP: - if (wl->rfk_info.state == BTC_WRFK_STOP) { - result = BTC_WRFK_REJECT; - } else { - result = BTC_WRFK_ALLOW; - wl->rfk_info.state = state; - } + wl->rfk_info.state = state; + if (type != BTC_WRFKT_RXDCK) + return BTC_WRFK_ALLOW; break; case BTC_WRFK_STOP: result = BTC_WRFK_ALLOW; wl->rfk_info.state = BTC_WRFK_STOP; - _write_scbd(rtwdev, BTC_WSCB_WLRFK, false); + _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_WLRFK, false); wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_rfk_chk_work); break; default: @@ -8927,7 +9022,7 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path, "[BTC], %s()_finish: rfk_cnt=%d, result=%d\n", __func__, btc->dm.cnt_notify[BTC_NCNT_WL_RFK], result); - return result == BTC_WRFK_ALLOW; + return result; } void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map, @@ -9160,7 +9255,7 @@ void rtw89_btc_ntfy_wl_sta(struct rtw89_dev *rtwdev) rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): busy=%d\n", __func__, !!wl->status.map.busy); - _write_scbd(rtwdev, BTC_WSCB_WLBUSY, (!!wl->status.map.busy)); + _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_WLBUSY, (!!wl->status.map.busy)); if (data.is_traffic_change) _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); @@ -9251,6 +9346,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt0; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; u8 *buf = &skb->data[RTW89_C2H_HEADER_LEN]; + u8 bid = BTC_BT_1ST; len -= RTW89_C2H_HEADER_LEN; @@ -9261,6 +9357,12 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, if (class != BTFC_FW_EVENT) return; + if (func & BT_C2H_FUNC_BT2ND) { + bid = BTC_BT_2ND; + func &= ~BT_C2H_FUNC_BT2ND; + bt = &btc->cx.bt1; + } + func = rtw89_btc_c2h_get_index_by_ver(rtwdev, func); pfwinfo->cnt_c2h++; @@ -9280,10 +9382,15 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, _update_bt_info(rtwdev, buf, len); break; case BTF_EVNT_BT_SCBD: - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], handle C2H BT SCBD with data %8ph\n", buf); bt->bcnt[BTC_BCNT_SCBDUPDATE]++; - _update_bt_scbd(rtwdev, false); + bt->scbd_c2h = ((buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | (buf[0])); + bt->scbd_rb = bt->scbd_c2h; + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], handle C2H BT%d SCBD with data 0x%08x\n", + bid, bt->scbd_c2h); + _update_bt_scbd(rtwdev, bid); + _run_coex(rtwdev, BTC_RSN_UPDATE_BT_SCBD); break; case BTF_EVNT_BT_PSD: break; @@ -9300,7 +9407,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, btc->dm.cnt_dm[BTC_DCNT_CX_RUNINFO]++; break; case BTF_EVNT_BT_QUERY_TXPWR: - bt->bcnt[BTC_BCNT_BTTXPWR_UPDATE]++; + bt->bcnt[BTC_BCNT_TXPWR_UPDATE]++; _update_bt_txpwr_info(rtwdev, buf, len); } } @@ -9680,7 +9787,7 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) bt->bcnt[BTC_BCNT_HIPRI_TX], bt->bcnt[BTC_BCNT_LOPRI_RX], bt->bcnt[BTC_BCNT_LOPRI_TX], - bt->bcnt[BTC_BCNT_POLUT]); + bt->bcnt[BTC_BCNT_POLLUTED]); if (!bt->scan_info_update) { rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, true); @@ -9716,7 +9823,7 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) else rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_TX_PWR_LVL, false); - if (bt->bcnt[BTC_BCNT_BTTXPWR_UPDATE]) { + if (bt->bcnt[BTC_BCNT_TXPWR_UPDATE]) { p += scnprintf(p, end - p, " %-15s : br_index:0x%x, le_index:0x%x", "[bt_txpwr_lvl]", @@ -10127,7 +10234,7 @@ static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ", "[dm_flag]", dm->wl_only, dm->bt_only, igno_bt, - dm->freerun, btc->lps, dm->wl_mimo_ps); + dm->freerun, btc->btc_ctrl_lps, dm->wl_mimo_ps); p += scnprintf(p, end - p, "leak_ap:%d, fw_offload:%s%s\n", dm->leak_ap, @@ -11353,10 +11460,11 @@ static int _show_mreg_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; - struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_fbtc_mreg_val_v1 *pmreg = NULL; + struct rtw89_btc_bt_info *bt0 = &btc->cx.bt0; + struct rtw89_btc_bt_info *bt1 = &btc->cx.bt1; + struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_mac_ax_coex_gnt gnt_cfg = {}; struct rtw89_mac_ax_gnt gnt; char *p = buf, *end = buf + bufsz; @@ -11369,11 +11477,20 @@ static int _show_mreg_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, "========== [HW Status] ==========\n"); p += scnprintf(p, end - p, - " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", - "[scoreboard]", wl->scbd, + " %-15s : WL->BT0:0x%08x(cnt:%d), BT0->WL:0x%08x(total:%d, bt_update:%d)\n", + "[scoreboard]", wl->scbd[BTC_BT_1ST], wl->wcnt[BTC_WCNT_SCBDUPDATE], - bt->scbd, bt->bcnt[BTC_BCNT_SCBDREAD], - bt->bcnt[BTC_BCNT_SCBDUPDATE]); + bt0->scbd, bt0->bcnt[BTC_BCNT_SCBDREAD], + bt0->bcnt[BTC_BCNT_SCBDUPDATE]); + + if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) { + p += scnprintf(p, end - p, + " %-15s : WL->BT1:0x%08x(cnt:%d), BT1->WL:0x%08x(total:%d, bt_update:%d)\n", + "[scoreboard]", wl->scbd[BTC_BT_2ND], + wl->wcnt[BTC_WCNT_SCBDUPDATE2], + bt1->scbd, bt1->bcnt[BTC_BCNT_SCBDREAD], + bt1->bcnt[BTC_BCNT_SCBDUPDATE]); + } btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev); _get_gnt(rtwdev, &gnt_cfg); @@ -11437,10 +11554,11 @@ static int _show_mreg_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; - struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_fbtc_mreg_val_v2 *pmreg = NULL; + struct rtw89_btc_bt_info *bt0 = &btc->cx.bt0; + struct rtw89_btc_bt_info *bt1 = &btc->cx.bt1; + struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_mac_ax_coex_gnt gnt_cfg = {}; struct rtw89_mac_ax_gnt gnt; char *p = buf, *end = buf + bufsz; @@ -11453,11 +11571,20 @@ static int _show_mreg_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, "========== [HW Status] ==========\n"); p += scnprintf(p, end - p, - " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", - "[scoreboard]", wl->scbd, + " %-15s : WL->BT0:0x%08x(cnt:%d), BT0->WL:0x%08x(total:%d, bt_update:%d)\n", + "[scoreboard]", wl->scbd[BTC_BT_1ST], wl->wcnt[BTC_WCNT_SCBDUPDATE], - bt->scbd, bt->bcnt[BTC_BCNT_SCBDREAD], - bt->bcnt[BTC_BCNT_SCBDUPDATE]); + bt0->scbd, bt0->bcnt[BTC_BCNT_SCBDREAD], + bt0->bcnt[BTC_BCNT_SCBDUPDATE]); + + if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) { + p += scnprintf(p, end - p, + " %-15s : WL->BT1:0x%08x(cnt:%d), BT1->WL:0x%08x(total:%d, bt_update:%d)\n", + "[scoreboard]", wl->scbd[BTC_BT_2ND], + wl->wcnt[BTC_WCNT_SCBDUPDATE2], + bt1->scbd, bt1->bcnt[BTC_BCNT_SCBDREAD], + bt1->bcnt[BTC_BCNT_SCBDUPDATE]); + } btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev); _get_gnt(rtwdev, &gnt_cfg); @@ -11524,8 +11651,9 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_fbtc_mreg_val_v7 *pmreg = NULL; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; 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_bt_info *bt = &cx->bt0; struct rtw89_mac_ax_gnt *gnt = NULL; struct rtw89_btc_dm *dm = &btc->dm; char *p = buf, *end = buf + bufsz; @@ -11538,11 +11666,20 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, "\n\r========== [HW Status] =========="); p += scnprintf(p, end - p, - "\n\r %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)", - "[scoreboard]", wl->scbd, + " %-15s : WL->BT0:0x%08x(cnt:%d), BT0->WL:0x%08x(total:%d, bt_update:%d)\n", + "[scoreboard]", wl->scbd[BTC_BT_1ST], wl->wcnt[BTC_WCNT_SCBDUPDATE], - bt->scbd, bt->bcnt[BTC_BCNT_SCBDREAD], - bt->bcnt[BTC_BCNT_SCBDUPDATE]); + bt0->scbd, bt0->bcnt[BTC_BCNT_SCBDREAD], + bt0->bcnt[BTC_BCNT_SCBDUPDATE]); + + if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) { + p += scnprintf(p, end - p, + " %-15s : WL->BT1:0x%08x(cnt:%d), BT1->WL:0x%08x(total:%d, bt_update:%d)\n", + "[scoreboard]", wl->scbd[BTC_BT_2ND], + wl->wcnt[BTC_WCNT_SCBDUPDATE2], + bt1->scbd, bt1->bcnt[BTC_BCNT_SCBDREAD], + bt1->bcnt[BTC_BCNT_SCBDUPDATE]); + } /* To avoid I/O if WL LPS or power-off */ dm->pta_owner = rtw89_mac_get_ctrl_path(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index fb151f68eb64..74027ca4eccc 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -391,4 +391,13 @@ void _slot_set_tbl(struct rtw89_btc *btc, u8 sid, u32 tbl) btc->dm.slot.v7[sid].cxtbl = cpu_to_le32(tbl); } +static inline +void btc_dw2b(u8 *buf, size_t idx, u32 val) +{ + buf[idx] = u32_get_bits(val, MASKBYTE0); + buf[idx + 1] = u32_get_bits(val, MASKBYTE1); + buf[idx + 2] = u32_get_bits(val, MASKBYTE2); + buf[idx + 3] = u32_get_bits(val, MASKBYTE3); +} + #endif diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 85aeb9e90812..b3376fadf593 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -5417,7 +5417,7 @@ static void rtw89_track_ps_work(struct wiphy *wiphy, struct wiphy_work *work) if (rtwdev->scanning) return; - if (rtwdev->lps_enabled && !rtwdev->btc.lps) + if (rtwdev->lps_enabled && !rtwdev->btc.btc_ctrl_lps) rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_100MS); } @@ -5465,7 +5465,7 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_core_rfkill_poll(rtwdev, false); rtw89_core_mlo_track(rtwdev); - if (rtwdev->lps_enabled && !rtwdev->btc.lps) + if (rtwdev->lps_enabled && !rtwdev->btc.btc_ctrl_lps) rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_2SEC); } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index eb814425f536..8646a13bfd79 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1415,6 +1415,7 @@ enum rtw89_btc_wl_state_cnt { BTC_WCNT_RX_ERR_LAST, BTC_WCNT_RX_ERR_LAST2S, BTC_WCNT_RX_LAST, + BTC_WCNT_SCBDUPDATE2, BTC_WCNT_NUM }; @@ -1431,17 +1432,25 @@ enum rtw89_btc_bt_state_cnt { BTC_BCNT_ROLESW, BTC_BCNT_AFH, BTC_BCNT_INFOUPDATE, + BTC_BCNT_LEAUDIO_INFOUPDATE, BTC_BCNT_INFOSAME, + BTC_BCNT_LEAUDIO_INFOSAME, BTC_BCNT_SCBDUPDATE, BTC_BCNT_HIPRI_TX, BTC_BCNT_HIPRI_RX, BTC_BCNT_LOPRI_TX, BTC_BCNT_LOPRI_RX, - BTC_BCNT_POLUT, BTC_BCNT_POLUT_NOW, BTC_BCNT_POLUT_DIFF, BTC_BCNT_RATECHG, - BTC_BCNT_BTTXPWR_UPDATE, + BTC_BCNT_AFH_CONFLICT, + BTC_BCNT_AFH_LE_CONFLICT, + BTC_BCNT_AFH_UPDATE, + BTC_BCNT_AFH_LE_UPDATE, + BTC_BCNT_AFH_CHN, + BTC_BCNT_AFH_LE_CHN, + BTC_BCNT_TXPWR_UPDATE, + BTC_BCNT_PROTECT, BTC_BCNT_NUM, }; @@ -2188,12 +2197,13 @@ struct rtw89_btc_wl_info { bool pta_reg_mac_chg; bool bg_mode; bool he_mode; - bool scbd_change; + bool scbd_chg[BTC_ALL_BT]; bool fw_ver_mismatch; bool client_cnt_inc_2g; bool link_mode_chg; bool dbcc_chg; - u32 scbd; + u32 scbd[BTC_ALL_BT]; + u32 scbd_rb[BTC_ALL_BT]; u32 wcnt[BTC_WCNT_NUM]; }; @@ -2323,6 +2333,15 @@ enum rtw89_btc_ble_scan_type { CXSCAN_MAX }; +enum rtw89_btc_bt_func_type { + BTC_BTF_NONE = 0, + BTC_BTF_BT = BIT(0), + BTC_BTF_ZB = BIT(1), + BTC_BTF_THREAD = BIT(2), + BTC_BTF_24GPRO = BIT(3), /* 2.4GHz Proprietary */ + BTC_BTF_ULL = BIT(4), +}; + #define RTW89_BTC_BTC_SCAN_V1_FLAG_ENABLE BIT(0) #define RTW89_BTC_BTC_SCAN_V1_FLAG_INTERLACE BIT(1) @@ -2394,6 +2413,8 @@ struct rtw89_btc_bt_info { u8 rsvd: 6; u32 scbd; + u32 scbd_rb; + u32 scbd_c2h; u32 feature; u32 mbx_avl: 1; @@ -3338,6 +3359,8 @@ struct rtw89_btc_dm { u8 lps_ctrl_scbd: 1; u8 lps_ctrl_scbd_last: 1; u8 lps_ctrl_change: 1; + u8 scbd_write_instant; + bool scbd_b2w_update; }; struct rtw89_btc_ctrl { @@ -3585,7 +3608,7 @@ struct rtw89_btc { u32 hubmsg_cnt; bool bt_req_en; bool update_policy_force; - bool lps; + bool btc_ctrl_lps; bool manual_ctrl; bool cli_h2c_cmd; }; From d01bcd34dd98d74c38a6c3e8bdbd94c00ee86a8f Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Mon, 6 Jul 2026 10:54:45 +0800 Subject: [PATCH 047/403] wifi: rtw89: coex: Add Co-RX logic Co-RX means Wi-Fi & Bluetooth can be able to RX in the same time. This patch is for judging the Wi-Fi/Bluetooth condition could be Co-RX or not, and how to set the gain and power. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260706025445.18428-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 69 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 1 + 2 files changed, 70 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 9e51906b1ccc..989344e93ab7 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -3319,7 +3319,74 @@ static void _set_bt_rx_gain(struct rtw89_dev *rtwdev, bool force_exec, u8 bid, _write_scbd(rtwdev, bid, scbd_bit, state); } +} +static void _set_bt_corx_table(struct rtw89_dev *rtwdev, bool en) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_cx *cx = &btc->cx; + struct rtw89_btc_bt_info *bt = &cx->bt0; + struct rtw89_btc_dm *dm = &btc->dm; + u8 is_24g, is_56g, i; + u32 scbd_bit; + + /* + * true: bt use Hi-LNA rx gain table (f/e/3/2) in -3x~-9xdBm for co-rx + * false: bt use original rx gain table (f/b/7/3/2) + */ + + if (btc->dm.fdd_bind.bt_sel == BIT(BTC_BT_EXT)) + return; + + for (i = BTC_BT_1ST; i <= BTC_BT_2ND; i++) { + scbd_bit = 0; + if (i == BTC_BT_2ND) { + if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT)) + continue; + bt = &cx->bt1; + } + + is_24g = (dm->corx_map[BTC_RF_S0][i] || + dm->corx_map[BTC_RF_S1][i]) & BIT(RTW89_BAND_2G); + is_56g = (dm->corx_map[BTC_RF_S0][i] || + dm->corx_map[BTC_RF_S1][i]) & BIT(RTW89_BAND_5G); + if (is_24g) + scbd_bit |= BTC_WSCB_BT_HILNA; + if (is_56g) + scbd_bit |= BTC_WSCB_BT_HILNA_56G; + + if ((is_24g && (en != (!!bt->hi_lna_rx))) || + (is_56g && (en != (!!bt->hi_lna_rx_6g)))) + _write_scbd(rtwdev, i, scbd_bit, en); + } +} + +static void _set_rf_trx_para_v9(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_cx *cx = &btc->cx; + struct rtw89_btc_wl_smap *wl_smap = &cx->wl.status.map; + struct rtw89_btc_dm *dm = &btc->dm; + bool bt_2r = false; + u8 wl_stb_chg = 0; + + if (wl_smap->rf_off || wl_smap->lps == BTC_LPS_RF_OFF) + return; + + /* must call after _set_halbb_btg_ctrl() */ + if (dm->tdd_bind.wl_link_mode != BTC_WLINK_NOLINK) { + wl_stb_chg = 1; + if (dm->wl_btg_rx) + bt_2r = true; + } + + _set_bt_corx_table(rtwdev, bt_2r); + + if (wl_stb_chg != dm->wl_stb_chg) { + dm->wl_stb_chg = wl_stb_chg; + dm->ost_info.wl_btg_standby_chg = wl_stb_chg; + rtwdev->chip->ops->btc_wl_s1_standby(rtwdev, dm->wl_stb_chg); + } } static void _set_rf_trx_para(struct rtw89_dev *rtwdev) @@ -3356,6 +3423,8 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) if (ver->fcxtrx == 9 && chip->rf_para_ulink_v9) { ul_para_num = chip->rf_para_ulink_num_v9; dl_para_num = chip->rf_para_dlink_num_v9; + _set_rf_trx_para_v9(rtwdev); + return; } else if (ver->fcxtrx == 0 && chip->rf_para_ulink_v0) { ul_para_num = chip->rf_para_ulink_num_v0; dl_para_num = chip->rf_para_dlink_num_v0; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 8646a13bfd79..524b4974040c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3278,6 +3278,7 @@ struct rtw89_btc_fbtc_outsrc_set_info { u8 pta_req_hw_band; u8 rf_gbt_source; u8 bt_enable_state; + u8 wl_btg_standby_chg; } __packed; union rtw89_btc_fbtc_slot_u { From be8aedb68c9645d5b76f262f7efc7e6a5581bc9a Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Tue, 7 Jul 2026 17:10:42 +0800 Subject: [PATCH 048/403] wifi: rtw89: 8922d: remove CCK bandwidth compensation Remove the 40MHz bandwidth compensation from CCK efuse gain calculation. The design no longer requires the +3dB compensation for 40MHz channels. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index f78d6d46e65f..625f8d675f08 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -1634,10 +1634,6 @@ static void rtw8922d_calc_rx_gain_normal_cck(struct rtw89_dev *rtwdev, s8 rx_gain_offset; rx_gain_offset = -rtw8922d_get_rx_gain_by_chan(rtwdev, chan, path, true); - - if (chan->band_width == RTW89_CHANNEL_WIDTH_40) - rx_gain_offset += (3 << 2); /* compensate RPL loss of 3dB */ - calc->cck_mean_gain_bias = (rx_gain_offset & 0x3) << 1; calc->cck_rpl_ofst = (rx_gain_offset >> 2) + gain->cck_rpl_base[phy_idx]; } From daa3fda5aeb9a8dc8a3f961dcbca8c740f59be07 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Tue, 7 Jul 2026 17:10:43 +0800 Subject: [PATCH 049/403] wifi: rtw89: 8922d: dynamic adjust channel smoothing Add support for path difference based channel smoothing for RTL8922D chip. This feature measures the ratio of NDP frames and uses a moving average filter to decide whether to enable beamforming channel smoothing. Tone index selection is dynamically adjusted based on bandwidth and link mode (HE/EHT vs VHT). The feature is only enabled for RTL8922D_CID7090 variant. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 8 +++ drivers/net/wireless/realtek/rtw89/core.h | 21 ++++++ drivers/net/wireless/realtek/rtw89/phy.c | 5 ++ drivers/net/wireless/realtek/rtw89/reg.h | 24 +++++++ drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 68 +++++++++++++++++++ 11 files changed, 132 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index b3376fadf593..5d35f13a1ea6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -5806,6 +5806,7 @@ int rtw89_core_sta_link_assoc(struct rtw89_dev *rtwdev, rtwsta_link); const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); struct ieee80211_link_sta *link_sta; int ret; @@ -5858,6 +5859,7 @@ int rtw89_core_sta_link_assoc(struct rtw89_dev *rtwdev, if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) { struct ieee80211_bss_conf *bss_conf; + u8 link_mode = 0; rcu_read_lock(); @@ -5865,6 +5867,12 @@ int rtw89_core_sta_link_assoc(struct rtw89_dev *rtwdev, link_sta = rtw89_sta_rcu_dereference_link(rtwsta_link, true); rtwsta_link->er_cap = rtw89_sta_link_can_er(rtwdev, bss_conf, link_sta); + if (link_sta->he_cap.has_he || link_sta->eht_cap.has_eht) + link_mode = 2; + else if (link_sta->vht_cap.vht_supported) + link_mode = 1; + bb->path_diff.link_mode = link_mode; + rcu_read_unlock(); rtw89_btc_ntfy_role_info(rtwdev, rtwvif_link, rtwsta_link, diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 524b4974040c..72aaef321d61 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -14,6 +14,7 @@ #include struct rtw89_dev; +struct rtw89_bb_ctx; struct rtw89_pci_info; struct rtw89_usb_info; struct rtw89_mac_gen_def; @@ -4127,6 +4128,8 @@ struct rtw89_chip_ops { enum rtw89_rf_path path, enum rtw89_phy_idx phy_idx, struct rtw89_phy_calc_efuse_gain *calc); + void (*path_diff_update)(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb); int (*pwr_on_func)(struct rtw89_dev *rtwdev); int (*pwr_off_func)(struct rtw89_dev *rtwdev); void (*query_rxdesc)(struct rtw89_dev *rtwdev, @@ -5683,6 +5686,14 @@ struct rtw89_beacon_stat { }; DECLARE_EWMA(thermal, 4, 4); +DECLARE_EWMA(path_diff, 4, 2); + +struct rtw89_phy_path_diff { + struct ewma_path_diff avg; + u8 raw; + bool bf_smo_en; + u8 link_mode; +}; #define RTW89_TX_RATE_NR 40 struct rtw89_phy_stat { @@ -6741,6 +6752,7 @@ struct rtw89_dev { struct rtw89_pmac_stat_info pmac_stat; struct rtw89_tx_stat_info tx_stat; struct rtw89_diag_bb diag; + struct rtw89_phy_path_diff path_diff; } bbs[RTW89_PHY_NUM]; struct wiphy_delayed_work track_work; @@ -7852,6 +7864,15 @@ static inline void rtw89_chip_power_trim(struct rtw89_dev *rtwdev) chip->ops->power_trim(rtwdev); } +static inline void rtw89_chip_path_diff_update(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + if (chip->ops->path_diff_update) + chip->ops->path_diff_update(rtwdev, bb); +} + static inline void __rtw89_chip_init_txpwr_unit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 759be4dab42b..981c7f02271a 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5842,6 +5842,9 @@ static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) memset(&bb->last_pkt_stat, 0, sizeof(bb->last_pkt_stat)); ewma_rssi_init(&bb->bcn_rssi); + bb->path_diff.raw = 0; + ewma_path_diff_init(&bb->path_diff.avg); + bb->path_diff.bf_smo_en = false; } rtwdev->hal.thermal_prot_lv = 0; @@ -6190,6 +6193,8 @@ void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) rtw89_for_each_active_bb(rtwdev, bb) { bb->last_pkt_stat = bb->cur_pkt_stat; memset(&bb->cur_pkt_stat, 0, sizeof(bb->cur_pkt_stat)); + + rtw89_chip_path_diff_update(rtwdev, bb); } } diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index bf1c6cb0ae9c..1ff788c24eec 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10822,6 +10822,13 @@ #define B_TXINFO_PATH_MB_BE4 BIT(19) #define R_SHAPER_COEFF_BE4 0x20CBC #define B_SHAPER_COEFF_BE4 BIT(19) + +#define R_CL_MODE_CNT_BE4 0x20DE0 +#define B_CL_MODE_NDP_CNT_PHY0_BE4 GENMASK(31, 24) +#define B_CL_MODE_NDP_CNT_PHY1_BE4 GENMASK(15, 8) +#define B_CL_MODE_CL_CNT_PHY0_BE4 GENMASK(23, 16) +#define B_CL_MODE_CL_CNT_PHY1_BE4 GENMASK(7, 0) + #define R_IFS_T1_AVG_BE4 0x20EDC #define B_IFS_T1_AVG_BE4 GENMASK(15, 0) #define B_IFS_T2_AVG_BE4 GENMASK(31, 16) @@ -10940,6 +10947,14 @@ #define R_GAIN_BIAS_BE4 0x260A0 #define B_GAIN_BIAS_BW20_BE4 GENMASK(11, 6) #define B_GAIN_BIAS_BW40_BE4 GENMASK(17, 12) + +#define R_BF_SMO_PDP_LMT_EHT_BE4 0x26568 +#define B_BF_SMO_PDP_LMT_EHT_BE4 BIT(30) +#define R_BF_SMO_PDP_LMT_HE_BE4 0x26568 +#define B_BF_SMO_PDP_LMT_HE_BE4 BIT(31) +#define R_BF_SMO_PDP_LMT_VHT_BE4 0x2656C +#define B_BF_SMO_PDP_LMT_VHT_BE4 BIT(31) + #define R_AWGN_DET_BE4 0x2668C #define B_AWGN_DET_BE4 GENMASK(17, 9) #define R_CSI_WGT_BE4 0x26770 @@ -10981,6 +10996,15 @@ #define R_BSS_CLR_VLD_BE4 0x26920 #define B_BSS_CLR_VLD_BE4 BIT(2) +#define R_CL_MODE_TRIG_BE4 0x26F44 +#define B_CL_MODE_TRIG_BE4 BIT(31) +#define R_SELECTED_TONE_IDX_BE4 0x26F4C +#define B_SELECTED_TONE_IDX_BE4 GENMASK(11, 0) +#define R_OS_TRIG_BY_SW_BE4 0x26F50 +#define B_OS_TRIG_BY_SW_BE4 BIT(30) +#define R_OS_TRIG_SOURCE_BE4 0x26F6C +#define B_OS_TRIG_SOURCE_BE4 BIT(0) + #define R_SW_SI_DATA_BE4 0x2CF4C #define B_SW_SI_READ_DATA_BE4 GENMASK(19, 0) #define B_SW_SI_W_BUSY_BE4 BIT(24) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index a1a63588cb90..95437ba9b675 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2578,6 +2578,7 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = { .set_txpwr_ul_tb_offset = rtw8851b_set_txpwr_ul_tb_offset, .digital_pwr_comp = NULL, .calc_rx_gain_normal = NULL, + .path_diff_update = NULL, .pwr_on_func = rtw8851b_pwr_on_func, .pwr_off_func = rtw8851b_pwr_off_func, .query_rxdesc = rtw89_core_query_rxdesc, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 055c67a07cea..6aa726efb7f6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2325,6 +2325,7 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = { .set_txpwr_ul_tb_offset = rtw8852a_set_txpwr_ul_tb_offset, .digital_pwr_comp = NULL, .calc_rx_gain_normal = NULL, + .path_diff_update = NULL, .pwr_on_func = NULL, .pwr_off_func = NULL, .query_rxdesc = rtw89_core_query_rxdesc, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index debcdb2eacd6..a8638024b54a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -907,6 +907,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .set_txpwr_ul_tb_offset = rtw8852bx_set_txpwr_ul_tb_offset, .digital_pwr_comp = NULL, .calc_rx_gain_normal = NULL, + .path_diff_update = NULL, .pwr_on_func = rtw8852b_pwr_on_func, .pwr_off_func = rtw8852b_pwr_off_func, .query_rxdesc = rtw89_core_query_rxdesc, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index fc8a17fb95f4..e68f73827fa5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -753,6 +753,7 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = { .set_txpwr_ul_tb_offset = rtw8852bx_set_txpwr_ul_tb_offset, .digital_pwr_comp = NULL, .calc_rx_gain_normal = NULL, + .path_diff_update = NULL, .pwr_on_func = rtw8852bt_pwr_on_func, .pwr_off_func = rtw8852bt_pwr_off_func, .query_rxdesc = rtw89_core_query_rxdesc, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 9e630b897986..a82373198356 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3124,6 +3124,7 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = { .set_txpwr_ul_tb_offset = rtw8852c_set_txpwr_ul_tb_offset, .digital_pwr_comp = NULL, .calc_rx_gain_normal = NULL, + .path_diff_update = NULL, .pwr_on_func = rtw8852c_pwr_on_func, .pwr_off_func = rtw8852c_pwr_off_func, .query_rxdesc = rtw89_core_query_rxdesc, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 382034eb27d0..fe87b4929ddc 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -3109,6 +3109,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .set_txpwr_ul_tb_offset = NULL, .digital_pwr_comp = rtw8922a_digital_pwr_comp, .calc_rx_gain_normal = NULL, + .path_diff_update = NULL, .pwr_on_func = rtw8922a_pwr_on_func, .pwr_off_func = rtw8922a_pwr_off_func, .query_rxdesc = rtw89_core_query_rxdesc_v2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 625f8d675f08..805dd96e61e6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -1716,6 +1716,73 @@ static void rtw8922d_calc_rx_gain_normal(struct rtw89_dev *rtwdev, rtw8922d_calc_rx_gain_normal_cck(rtwdev, chan, path, phy_idx, calc); } +static void rtw8922d_path_diff_update(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ +#define BF_SMOOTH_TH 80 + static const u32 path_diff_cnt_mask[] = {0xff0000, 0xff}; + static const u32 ndp_cnt_mask[] = {0xff000000, 0xff00}; + static const u16 he_eht_sel_tone[4] = {27, 54, 56, 56}; + static const u16 vht_sel_tone[4] = {11, 25, 25, 25}; + struct rtw89_hal *hal = &rtwdev->hal; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; + u8 phy_idx = bb->phy_idx; + u32 path_diff_cnt; + u16 sel_tone = 0; + bool bf_smo_lmt; + u32 ndp_cnt; + + if (hal->cid != RTL8922D_CID7090) + return; + + ndp_cnt = rtw89_phy_read32_mask(rtwdev, R_CL_MODE_CNT_BE4, ndp_cnt_mask[phy_idx]); + path_diff_cnt = rtw89_phy_read32_mask(rtwdev, R_CL_MODE_CNT_BE4, + path_diff_cnt_mask[phy_idx]); + + bb->path_diff.raw = clamp(phy_div(path_diff_cnt * 100, ndp_cnt), 0, 255); + + if (ndp_cnt == 0) + ewma_path_diff_init(&bb->path_diff.avg); + else + ewma_path_diff_add(&bb->path_diff.avg, bb->path_diff.raw); + + rtw89_phy_write32_mask(rtwdev, R_CL_MODE_TRIG_BE4, B_CL_MODE_TRIG_BE4, 1); + rtw89_phy_write32_mask(rtwdev, R_CL_MODE_TRIG_BE4, B_CL_MODE_TRIG_BE4, 0); + + bf_smo_lmt = ewma_path_diff_read(&bb->path_diff.avg) < BF_SMOOTH_TH; + bb->path_diff.bf_smo_en = !bf_smo_lmt; + + rtw89_phy_write32_mask(rtwdev, R_BF_SMO_PDP_LMT_EHT_BE4, + B_BF_SMO_PDP_LMT_EHT_BE4, bf_smo_lmt); + rtw89_phy_write32_mask(rtwdev, R_BF_SMO_PDP_LMT_HE_BE4, + B_BF_SMO_PDP_LMT_HE_BE4, bf_smo_lmt); + rtw89_phy_write32_mask(rtwdev, R_BF_SMO_PDP_LMT_VHT_BE4, + B_BF_SMO_PDP_LMT_VHT_BE4, bf_smo_lmt); + + rtw89_phy_write32_mask(rtwdev, R_OS_TRIG_BY_SW_BE4, + B_OS_TRIG_BY_SW_BE4, !bf_smo_lmt); + rtw89_phy_write32_mask(rtwdev, R_OS_TRIG_SOURCE_BE4, + B_OS_TRIG_SOURCE_BE4, !bf_smo_lmt); + + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[phy_idx]; + if (chan->band_width <= RTW89_CHANNEL_WIDTH_160) { + if (bb->path_diff.link_mode >= 2) + sel_tone = he_eht_sel_tone[chan->band_width]; + else if (bb->path_diff.link_mode == 1) + sel_tone = vht_sel_tone[chan->band_width]; + rtw89_phy_write32_mask(rtwdev, R_SELECTED_TONE_IDX_BE4, + B_SELECTED_TONE_IDX_BE4, sel_tone); + } + + rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, + "[PATH_DIFF] raw=%d%%, ma=%ld%%, bf_smo_en=%d, tone=%d\n", + bb->path_diff.raw, + ewma_path_diff_read(&bb->path_diff.avg), + bb->path_diff.bf_smo_en, sel_tone); +} + static void rtw8922d_set_cck_parameters(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) @@ -3288,6 +3355,7 @@ static const struct rtw89_chip_ops rtw8922d_chip_ops = { .set_txpwr_ul_tb_offset = NULL, .digital_pwr_comp = rtw8922d_digital_pwr_comp, .calc_rx_gain_normal = rtw8922d_calc_rx_gain_normal, + .path_diff_update = rtw8922d_path_diff_update, .pwr_on_func = rtw8922d_pwr_on_func, .pwr_off_func = rtw8922d_pwr_off_func, .query_rxdesc = rtw89_core_query_rxdesc_v3, From 8da4883c81ae69e3abc86328b256c828748d035f Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Tue, 7 Jul 2026 17:10:44 +0800 Subject: [PATCH 050/403] wifi: rtw89: 8922d: fix EMLSR BB switch sequence for MLO mode transition Assert BB reset in the intermediate "switch to 1+1" step of the EMLSR switch sequence for all three MLO mode transitions by updating the B_EMLSR_SWITCH_BE4 intermediate value from 0xAFFF to 0x3BAB. Without the BB reset in this step, the baseband can be left in an inconsistent state before settling into the final MLO configuration. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 45 ++++++++----------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 1ff788c24eec..3908f9729736 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10782,6 +10782,7 @@ #define B_SYS_DBCC_24G_BAND_SEL_BE4 BIT(1) #define R_EMLSR_SWITCH_BE4 0x20044 #define B_EMLSR_SWITCH_BE4 GENMASK(27, 12) +#define B_EMLSR_CLR_FORCE_BE4 GENMASK(20, 19) #define B_EMLSR_BB_CLK_BE4 GENMASK(31, 30) #define R_CHINFO_SEG_BE4 0x200B4 #define B_CHINFO_SEG_LEN_BE4 GENMASK(12, 10) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 805dd96e61e6..212917db7154 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2309,17 +2309,16 @@ static void rtw8922d_digital_pwr_comp(struct rtw89_dev *rtwdev, } } -static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode, - bool pwr_comp) +static void rtw8922d_ctrl_mlo_mode_core(struct rtw89_dev *rtwdev, + enum rtw89_mlo_dbcc_mode mode) { - u32 reg0, reg1; - u8 cck_phy_idx; + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_CLR_FORCE_BE4, 0x3); if (mode == MLO_2_PLUS_0_1RF) { rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xBBBB); udelay(1); rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x3); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xAFFF); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0x3BAB); rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEBAD); udelay(1); @@ -2329,7 +2328,7 @@ static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xBBBB); udelay(1); rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x3); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xAFFF); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0x3BAB); udelay(1); rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEFFF); @@ -2339,7 +2338,7 @@ static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xBBBB); udelay(1); rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x3); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xAFFF); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0x3BAB); udelay(1); rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x0); rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0x3AAB); @@ -2351,6 +2350,15 @@ static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x0); rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0x0); } +} + +static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode, + bool pwr_comp) +{ + u32 reg0, reg1; + u8 cck_phy_idx; + + rtw8922d_ctrl_mlo_mode_core(rtwdev, mode); if (pwr_comp) rtw8922d_digital_pwr_comp(rtwdev, RTW89_PHY_0); @@ -2474,25 +2482,10 @@ static void rtw8922d_pre_set_channel_bb(struct rtw89_dev *rtwdev, rtw89_phy_write32_mask(rtwdev, R_SYS_DBCC_BE4, B_SYS_DBCC_BE4, 0x0); - if (phy_idx == RTW89_PHY_0) { - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xBBBB); - fsleep(1); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x3); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xAFFF); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEBAD); - fsleep(1); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x0); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEAAD); - } else { - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xBBBB); - fsleep(1); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x3); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xAFFF); - fsleep(1); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEFFF); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x0); - rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEEFF); - } + if (phy_idx == RTW89_PHY_0) + rtw8922d_ctrl_mlo_mode_core(rtwdev, MLO_2_PLUS_0_1RF); + else + rtw8922d_ctrl_mlo_mode_core(rtwdev, MLO_0_PLUS_2_1RF); fsleep(1); } From 852927114f6ef37530952b3dba063d24e60b38d9 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Tue, 7 Jul 2026 17:10:45 +0800 Subject: [PATCH 051/403] wifi: rtw89: phy: fix bandedge primary channel for 2.4GHz 40MHz and 6GHz Correct 2.4GHz bandwidth 40MHz bandedge check: pri_ch 5/9 instead of 3/11. Remove stale 6GHz bandedge case; no restricted band borders 6GHz range. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy_be.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 99263355e2f1..ac4ce30445b3 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -623,7 +623,7 @@ static u32 rtw89_phy_bb_wrap_be_bandedge_decision(struct rtw89_dev *rtwdev, case RTW89_BAND_2G: if (pri_ch == 1 || pri_ch == 13) val = BIT(1) | BIT(0); - else if (pri_ch == 3 || pri_ch == 11) + else if (pri_ch == 5 || pri_ch == 9) val = BIT(1); break; case RTW89_BAND_5G: @@ -637,8 +637,6 @@ static u32 rtw89_phy_bb_wrap_be_bandedge_decision(struct rtw89_dev *rtwdev, val = BIT(3); break; case RTW89_BAND_6G: - if (pri_ch == 233) - val = BIT(0); break; } From 390f58e29de3a3ddb05dd739a5fb12ed973d9088 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 7 Jul 2026 17:10:46 +0800 Subject: [PATCH 052/403] wifi: rtw89: 8922d: set TX compensation by format v2 The total number of TX compensation registers is 22, including 7 base and 15 ones according to operating channel. The v0 is 22 ones per channel. To reduce array size, v1 treats 7 base as common part across all conditions. However, we can fine tune the base part to yield better performance, so divide base into two sets according to NSS. Summarize dimensions as follows base (7) vals(15) v0 nss * band * path nss * band * path v1 1 nss * band * path v2 nss nss * band * path Currently, v1 and v2 can present in a file, but only one fw element with a specific format will be used for specific RFE type. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 212917db7154..e8267173bd4c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2249,6 +2249,12 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, struct { __le32 vals[DIGITAL_PWR_COMP_VALS_NUM]; } sets[2][RTW89_TX_COMP_BAND_NR][BB_PATH_NUM_8922D]; + } *pwr_comp_v1; + const struct { + __le32 base[2][DIGITAL_PWR_COMP_BASE_NUM]; + struct { + __le32 vals[DIGITAL_PWR_COMP_VALS_NUM]; + } sets[2][RTW89_TX_COMP_BAND_NR][BB_PATH_NUM_8922D]; } *pwr_comp; struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; const struct rtw89_fw_element_hdr *txcomp_elm = elm_info->tx_comp; @@ -2259,8 +2265,12 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, if (sizeof(*pwr_comp) == le32_to_cpu(txcomp_elm->size)) { pwr_comp = (const void *)txcomp_elm->u.common.contents; - comp_base = &pwr_comp->base; + comp_base = &pwr_comp->base[nss]; comp_vals = &pwr_comp->sets[nss][chan->tx_comp_band][path].vals; + } else if (sizeof(*pwr_comp_v1) == le32_to_cpu(txcomp_elm->size)) { + pwr_comp_v1 = (const void *)txcomp_elm->u.common.contents; + comp_base = &pwr_comp_v1->base; + comp_vals = &pwr_comp_v1->sets[nss][chan->tx_comp_band][path].vals; } else if (sizeof(*pwr_comp_v0) == le32_to_cpu(txcomp_elm->size)) { pwr_comp_v0 = (const void *)txcomp_elm->u.common.contents; comp_base = &pwr_comp_v0->sets[nss][chan->tx_comp_band][path].base; From d193bdc73396110a1d803c51d6e99ee2b3e42c15 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 7 Jul 2026 17:10:47 +0800 Subject: [PATCH 053/403] wifi: rtw89: efuse: no need to export rtw89_efuse_read_ecv_be() The consumer of rtw89_efuse_read_ecv_be() is in the same ko. No need to export it. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/efuse_be.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/efuse_be.c b/drivers/net/wireless/realtek/rtw89/efuse_be.c index 70c1b8be662e..a716ad54fce5 100644 --- a/drivers/net/wireless/realtek/rtw89/efuse_be.c +++ b/drivers/net/wireless/realtek/rtw89/efuse_be.c @@ -537,4 +537,3 @@ int rtw89_efuse_read_ecv_be(struct rtw89_dev *rtwdev) return 0; } -EXPORT_SYMBOL(rtw89_efuse_read_ecv_be); From 4260edb3b1a6da71b8cd362f57a9ac4329eb538c Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 7 Jul 2026 17:10:48 +0800 Subject: [PATCH 054/403] wifi: rtw89: efuse: read thermal calibration value for RTL8922D The thermal calibration value programmed in efuse is the offset to adjust thermal value read from hardware, so that output will be accurate. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 2 ++ drivers/net/wireless/realtek/rtw89/efuse.h | 6 ++++ drivers/net/wireless/realtek/rtw89/efuse_be.c | 29 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.c | 2 ++ drivers/net/wireless/realtek/rtw89/mac.h | 11 +++++++ drivers/net/wireless/realtek/rtw89/mac_be.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 ++ 7 files changed, 53 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 72aaef321d61..f467299b6363 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6063,6 +6063,8 @@ struct rtw89_power_trim_info { u8 pa_bias_trim[RF_PATH_MAX]; u8 pad_bias_trim[RF_PATH_MAX]; u8 vco_trim[RF_PATH_MAX]; + + s16 thermal_k; }; enum rtw89_regd_func { diff --git a/drivers/net/wireless/realtek/rtw89/efuse.h b/drivers/net/wireless/realtek/rtw89/efuse.h index a14a9dfed8e8..c6415da749a3 100644 --- a/drivers/net/wireless/realtek/rtw89/efuse.h +++ b/drivers/net/wireless/realtek/rtw89/efuse.h @@ -16,6 +16,11 @@ #define EF_CV_MASK GENMASK(7, 4) #define EF_CV_INV 15 +#define EFUSE_THERMAL_K_OFFSET_BE 0x17CC +#define EFUSE_THERMAL_K_VALID_BE BIT(9) +#define EFUSE_THERMAL_K_SIGN_BE BIT(8) +#define EFUSE_THERMAL_K_VAL_BE GENMASK(7, 0) + struct rtw89_efuse_block_cfg { u32 offset; u32 size; @@ -32,5 +37,6 @@ int rtw89_efuse_recognize_mss_info_v1(struct rtw89_dev *rtwdev, u8 b1, u8 b2); int rtw89_efuse_read_fw_secure_ax(struct rtw89_dev *rtwdev); int rtw89_efuse_read_fw_secure_be(struct rtw89_dev *rtwdev); int rtw89_efuse_read_ecv_be(struct rtw89_dev *rtwdev); +int rtw89_efuse_read_thermal_k_be(struct rtw89_dev *rtwdev); #endif diff --git a/drivers/net/wireless/realtek/rtw89/efuse_be.c b/drivers/net/wireless/realtek/rtw89/efuse_be.c index a716ad54fce5..67036c89883f 100644 --- a/drivers/net/wireless/realtek/rtw89/efuse_be.c +++ b/drivers/net/wireless/realtek/rtw89/efuse_be.c @@ -537,3 +537,32 @@ int rtw89_efuse_read_ecv_be(struct rtw89_dev *rtwdev) return 0; } + +int rtw89_efuse_read_thermal_k_be(struct rtw89_dev *rtwdev) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u32 dump_addr = EFUSE_THERMAL_K_OFFSET_BE; + u8 buff[4]; /* efuse access must be multiple of 4 bytes in size */ + bool no_k; + u16 val16; + int ret; + + ret = rtw89_dump_physical_efuse_map_be(rtwdev, buff, dump_addr, 4, false); + if (ret) + return ret; + + val16 = buff[0] | buff[1] << 8; + + no_k = !!u16_get_bits(val16, EFUSE_THERMAL_K_VALID_BE); + if (no_k) { + info->thermal_k = 0; + return -ENOENT; + } + + info->thermal_k = u16_get_bits(val16, EFUSE_THERMAL_K_VAL_BE); + + if (u16_get_bits(val16, EFUSE_THERMAL_K_SIGN_BE)) + info->thermal_k *= -1; + + return 0; +} diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 99de1b202976..3d3f683046a7 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1558,6 +1558,7 @@ static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on) if (!test_bit(RTW89_FLAG_PROBE_DONE, rtwdev->flags)) { rtw89_mac_efuse_read_ecv(rtwdev); mac->efuse_read_fw_secure(rtwdev); + rtw89_mac_efuse_read_thermal_k(rtwdev); } set_bit(RTW89_FLAG_POWERON, rtwdev->flags); @@ -7500,6 +7501,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .cnv_efuse_state = rtw89_cnv_efuse_state_ax, .efuse_read_fw_secure = rtw89_efuse_read_fw_secure_ax, .efuse_read_ecv = NULL, + .efuse_read_thermal_k = NULL, .cfg_plt = rtw89_mac_cfg_plt_ax, .get_plt_cnt = rtw89_mac_get_plt_cnt_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 539061fc15e8..7256d64ea07f 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1132,6 +1132,7 @@ struct rtw89_mac_gen_def { int (*cnv_efuse_state)(struct rtw89_dev *rtwdev, bool idle); int (*efuse_read_fw_secure)(struct rtw89_dev *rtwdev); int (*efuse_read_ecv)(struct rtw89_dev *rtwdev); + int (*efuse_read_thermal_k)(struct rtw89_dev *rtwdev); int (*cfg_plt)(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_plt *plt); u16 (*get_plt_cnt)(struct rtw89_dev *rtwdev, u8 band); @@ -1735,6 +1736,16 @@ static inline int rtw89_mac_efuse_read_ecv(struct rtw89_dev *rtwdev) return mac->efuse_read_ecv(rtwdev); } +static inline int rtw89_mac_efuse_read_thermal_k(struct rtw89_dev *rtwdev) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + + if (!mac->efuse_read_thermal_k) + return -ENOENT; + + return mac->efuse_read_thermal_k(rtwdev); +} + static inline void rtw89_mac_fwdl_preconfig(struct rtw89_dev *rtwdev) { diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 14f1e30066e9..077ddf4f77c5 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -3312,6 +3312,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .cnv_efuse_state = rtw89_cnv_efuse_state_be, .efuse_read_fw_secure = rtw89_efuse_read_fw_secure_be, .efuse_read_ecv = rtw89_efuse_read_ecv_be, + .efuse_read_thermal_k = rtw89_efuse_read_thermal_k_be, .cfg_plt = rtw89_mac_cfg_plt_be, .get_plt_cnt = rtw89_mac_get_plt_cnt_be, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index e8267173bd4c..8b91c552a309 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -3006,6 +3006,7 @@ static void rtw8922d_bb_cfg_txrx_path(struct rtw89_dev *rtwdev) static u8 rtw8922d_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path) { + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; u8 val; rtw89_phy_write32_mask(rtwdev, R_TC_EN_BE4, B_TC_EN_BE4, 0x1); @@ -3015,6 +3016,7 @@ static u8 rtw8922d_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_p fsleep(100); val = rtw89_phy_read32_mask(rtwdev, R_TC_VAL_BE4, B_TC_VAL_BE4); + val = clamp_t(int, val + info->thermal_k, 0, 255); return val; } From 4e199cd6661457e426e0f10a63c553c6c50b5262 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 7 Jul 2026 17:10:49 +0800 Subject: [PATCH 055/403] wifi: rtw89: 8922d: read default digital voltage calibration values In order to lower temperature when WiFi is running, reduce the digital voltage for the purpose. The calibration values of digital voltage are programmed in efuse. One is for normal use, which driver stores it into hal->thermal_prot_vmax. The other is the minimum voltage, which is stored into hal->thermal_prot_vmin. These two values define a range of supported voltage, which latter patch uses them to adjust voltage depends on thermal value (temperature). Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 7 ++ drivers/net/wireless/realtek/rtw89/efuse.h | 1 + drivers/net/wireless/realtek/rtw89/efuse_be.c | 87 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.c | 2 + drivers/net/wireless/realtek/rtw89/mac.h | 11 +++ drivers/net/wireless/realtek/rtw89/mac_be.c | 1 + drivers/net/wireless/realtek/rtw89/reg.h | 17 ++++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 24 +++++ 8 files changed, 150 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f467299b6363..d457da2a21fe 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3840,6 +3840,8 @@ struct rtw89_sta_link { struct rtw89_efuse { bool valid; bool power_k_valid; + bool vcore_valid; + bool dswr_valid; u8 xtal_cap; u8 addr[ETH_ALEN]; u8 rfe_type; @@ -3849,6 +3851,8 @@ struct rtw89_efuse { u8 bt_setting_3; u8 sn[RTW89_EFUSE_SN_LEN]; u8 uuid[RTW89_EFUSE_UUID_LEN]; + u8 vcore_vmax_reduce; + u8 dswr_vmin; }; struct rtw89_phy_rate_pattern { @@ -5561,6 +5565,9 @@ struct rtw89_hal { u8 thermal_prot_th; u8 thermal_prot_lv; /* 0 ~ RTW89_THERMAL_PROT_LV_MAX */ + u8 thermal_prot_vmax; + u8 thermal_prot_vmin; + u8 fixed_dig_pd_th; /* v = (X(dBm) + 102)/2 */ s8 fixed_dig_cck_pd_th; /* dBm */ }; diff --git a/drivers/net/wireless/realtek/rtw89/efuse.h b/drivers/net/wireless/realtek/rtw89/efuse.h index c6415da749a3..4eb629273ab9 100644 --- a/drivers/net/wireless/realtek/rtw89/efuse.h +++ b/drivers/net/wireless/realtek/rtw89/efuse.h @@ -38,5 +38,6 @@ int rtw89_efuse_read_fw_secure_ax(struct rtw89_dev *rtwdev); int rtw89_efuse_read_fw_secure_be(struct rtw89_dev *rtwdev); int rtw89_efuse_read_ecv_be(struct rtw89_dev *rtwdev); int rtw89_efuse_read_thermal_k_be(struct rtw89_dev *rtwdev); +int rtw89_efuse_read_pwr_data_be(struct rtw89_dev *rtwdev); #endif diff --git a/drivers/net/wireless/realtek/rtw89/efuse_be.c b/drivers/net/wireless/realtek/rtw89/efuse_be.c index 67036c89883f..825274bd7c41 100644 --- a/drivers/net/wireless/realtek/rtw89/efuse_be.c +++ b/drivers/net/wireless/realtek/rtw89/efuse_be.c @@ -16,6 +16,15 @@ #define EFUSE_SEC_BE_START 0x1580 #define EFUSE_SEC_BE_SIZE 4 +#define EFUSE_VCORE_PWR_BE 0x17D6 +#define EFUSE_VCORE_PWR_BE_VALID BIT(12) +#define EFUSE_VCORE_PWR_BE_VAL GENMASK(11, 0) +#define SWR_DIG_MIN 860 +#define EFUSE_DIG_K_STEP 0xC +#define EFUSE_DSWR_V0_86_BE 0x17DA +#define EFUSE_DSWR_V0_86_BE_VALID BIT(5) +#define EFUSE_DSWR_V0_86_BE_VAL GENMASK(4, 0) + static const u32 sb_sel_mgn[SB_SEL_MGN_MAX_SIZE] = { 0x8000100, 0xC000180 }; @@ -392,6 +401,17 @@ int rtw89_parse_efuse_map_be(struct rtw89_dev *rtwdev) goto out_free; } + if (rtwdev->chip->chip_id != RTL8922D) + goto out_free; + + ret = rtw89_parse_logical_efuse_block_be(rtwdev, phy_map, phy_size, + RTW89_EFUSE_BLOCK_SYS); + if (ret) { + rtw89_warn(rtwdev, "failed to parse efuse logic block %d\n", + RTW89_EFUSE_BLOCK_SYS); + goto out_free; + } + out_free: kfree(dav_phy_map); kfree(phy_map); @@ -566,3 +586,70 @@ int rtw89_efuse_read_thermal_k_be(struct rtw89_dev *rtwdev) return 0; } + +static int rtw89_efuse_read_pwr_data_vcore_be(struct rtw89_dev *rtwdev) +{ + struct rtw89_efuse *efuse = &rtwdev->efuse; + u32 dump_addr; + u8 buff[4]; /* efuse access must 4 bytes align */ + u16 val16; + int ret; + + dump_addr = ALIGN_DOWN(EFUSE_VCORE_PWR_BE, 4); + + ret = rtw89_dump_physical_efuse_map_be(rtwdev, buff, dump_addr, 4, false); + if (ret) + return ret; + + val16 = buff[EFUSE_VCORE_PWR_BE - dump_addr] | + buff[EFUSE_VCORE_PWR_BE - dump_addr + 1] << 8; + + if (val16 & EFUSE_VCORE_PWR_BE_VALID) + return 0; + + efuse->vcore_valid = true; + efuse->vcore_vmax_reduce = + (u16_get_bits(val16, EFUSE_VCORE_PWR_BE_VAL) - SWR_DIG_MIN) / + EFUSE_DIG_K_STEP; + + return 0; +} + +static int rtw89_efuse_read_pwr_data_dswr_be(struct rtw89_dev *rtwdev) +{ + struct rtw89_efuse *efuse = &rtwdev->efuse; + u32 dump_addr; + u8 buff[4]; /* efuse access must 4 bytes align */ + u8 val8; + int ret; + + dump_addr = ALIGN_DOWN(EFUSE_DSWR_V0_86_BE, 4); + + ret = rtw89_dump_physical_efuse_map_be(rtwdev, buff, dump_addr, 4, false); + if (ret) + return ret; + + val8 = buff[EFUSE_DSWR_V0_86_BE - dump_addr]; + + if (val8 & EFUSE_DSWR_V0_86_BE_VALID) + return 0; + + efuse->dswr_valid = true; + efuse->dswr_vmin = u8_get_bits(val8, EFUSE_DSWR_V0_86_BE_VAL); + + return 0; +} + +int rtw89_efuse_read_pwr_data_be(struct rtw89_dev *rtwdev) +{ + int ret; + + if (rtwdev->chip->chip_id != RTL8922D) + return 0; + + ret = rtw89_efuse_read_pwr_data_vcore_be(rtwdev); + if (ret) + return ret; + + return rtw89_efuse_read_pwr_data_dswr_be(rtwdev); +} diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 3d3f683046a7..f5e55e6be119 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1559,6 +1559,7 @@ static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on) rtw89_mac_efuse_read_ecv(rtwdev); mac->efuse_read_fw_secure(rtwdev); rtw89_mac_efuse_read_thermal_k(rtwdev); + rtw89_mac_efuse_read_pwr_data(rtwdev); } set_bit(RTW89_FLAG_POWERON, rtwdev->flags); @@ -7502,6 +7503,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .efuse_read_fw_secure = rtw89_efuse_read_fw_secure_ax, .efuse_read_ecv = NULL, .efuse_read_thermal_k = NULL, + .efuse_read_pwr_data = NULL, .cfg_plt = rtw89_mac_cfg_plt_ax, .get_plt_cnt = rtw89_mac_get_plt_cnt_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 7256d64ea07f..8bbe49492d4c 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1133,6 +1133,7 @@ struct rtw89_mac_gen_def { int (*efuse_read_fw_secure)(struct rtw89_dev *rtwdev); int (*efuse_read_ecv)(struct rtw89_dev *rtwdev); int (*efuse_read_thermal_k)(struct rtw89_dev *rtwdev); + int (*efuse_read_pwr_data)(struct rtw89_dev *rtwdev); int (*cfg_plt)(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_plt *plt); u16 (*get_plt_cnt)(struct rtw89_dev *rtwdev, u8 band); @@ -1746,6 +1747,16 @@ static inline int rtw89_mac_efuse_read_thermal_k(struct rtw89_dev *rtwdev) return mac->efuse_read_thermal_k(rtwdev); } +static inline int rtw89_mac_efuse_read_pwr_data(struct rtw89_dev *rtwdev) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + + if (!mac->efuse_read_pwr_data) + return -ENOENT; + + return mac->efuse_read_pwr_data(rtwdev); +} + static inline void rtw89_mac_fwdl_preconfig(struct rtw89_dev *rtwdev) { diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 077ddf4f77c5..0fe317929b1b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -3313,6 +3313,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .efuse_read_fw_secure = rtw89_efuse_read_fw_secure_be, .efuse_read_ecv = rtw89_efuse_read_ecv_be, .efuse_read_thermal_k = rtw89_efuse_read_thermal_k_be, + .efuse_read_pwr_data = rtw89_efuse_read_pwr_data_be, .cfg_plt = rtw89_mac_cfg_plt_be, .get_plt_cnt = rtw89_mac_get_plt_cnt_be, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 3908f9729736..72f7b80fe1fb 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -4573,6 +4573,23 @@ #define R_BE_UDM2 0x01F8 #define B_BE_UDM2_EPC_RA_MASK GENMASK(31, 0) +#define R_BE_SPS_DIG_ON_CTRL0 0x0200 +#define B_BE_PFMCMP_IQ BIT(31) +#define B_BE_FREQ_MASK GENMASK(30, 27) +#define B_BE_OFF_END_SEL BIT(26) +#define B_BE_POW_MINOFF_L BIT(25) +#define B_BE_REG_BYPASS_L BIT(24) +#define B_BE_VREFPFM_L_MASK GENMASK(23, 19) +#define B_BE_REG_ZCDC_H_MASK GENMASK(18, 17) +#define B_BE_FORCE_ZCD_BIAS BIT(16) +#define B_BE_ZCD_SDZ_L_MASK GENMASK(15, 14) +#define B_BE_ISAW_MASK GENMASK(13, 10) +#define B_BE_SS_DIVSEL_MASK GENMASK(9, 8) +#define B_BE_REG_BG_H BIT(7) +#define B_BE_FPWM_L1 BIT(6) +#define B_BE_POW_ZCD_L BIT(5) +#define B_BE_PWMTUNE_MASK GENMASK(4, 0) + #define R_BE_SPS_DIG_ON_CTRL1 0x0204 #define B_BE_SN_N_L_MASK GENMASK(31, 28) #define B_BE_SP_N_L_MASK GENMASK(27, 24) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 8b91c552a309..a6693c8413d5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -881,6 +881,28 @@ static int rtw8922d_read_efuse_rf(struct rtw89_dev *rtwdev, u8 *log_map) return 0; } +static int rtw8922d_read_sys(struct rtw89_dev *rtwdev, u8 *log_map) +{ + struct rtw89_efuse *efuse = &rtwdev->efuse; + struct rtw89_hal *hal = &rtwdev->hal; + u16 digk; + u8 vmin; + + digk = log_map[0x200] | log_map[0x201] << 8; + hal->thermal_prot_vmax = u16_get_bits(digk, B_BE_PWMTUNE_MASK); + + if (efuse->dswr_valid) + vmin = efuse->dswr_vmin; + else if (efuse->vcore_valid) + vmin = hal->thermal_prot_vmax - efuse->vcore_vmax_reduce; + else + vmin = hal->thermal_prot_vmax - 3; + + hal->thermal_prot_vmin = vmin; + + return 0; +} + static int rtw8922d_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, enum rtw89_efuse_block block) { @@ -891,6 +913,8 @@ static int rtw8922d_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, return rtw8922d_read_efuse_usb(rtwdev, log_map); case RTW89_EFUSE_BLOCK_RF: return rtw8922d_read_efuse_rf(rtwdev, log_map); + case RTW89_EFUSE_BLOCK_SYS: + return rtw8922d_read_sys(rtwdev, log_map); default: return 0; } From 47ad03f1eca5ddbc44562b9a4add5003fdca34c7 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 7 Jul 2026 17:10:50 +0800 Subject: [PATCH 056/403] wifi: rtw89: add thermal protect by digital voltage reduction The temperature is rising when WiFi does high throughput, and reduce voltage is a way to ease temperature. The existing method to do thermal protect is to reduce TX duty, which can affect throughput obviously. Before doing reduce TX duty (with -20 thermal value offset), driver does voltage reduction first, which doesn't affect throughput but lower power consumption. The voltage gram of register is 0.01 voltage, and the suggested maximum reduction is 6 grams, which driver defines 6 as maximum voltage level. When thermal value is over threshold, driver will try to adjust level and corresponding register value. It should be adjust grams one by one with additional 50 ms delay to ensure hardware work properly. A note that the voltage must reset to normal value before entering power save mode, otherwise WiFi card might get lost. Since it takes 50 ms delay for each gram to adjust voltage, we stop to enter power save if protect level is not zero. By the way, adjust thermal threshold to 0xB4 as desired. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 9 +++++ drivers/net/wireless/realtek/rtw89/core.h | 4 ++ drivers/net/wireless/realtek/rtw89/debug.c | 1 + drivers/net/wireless/realtek/rtw89/mac.c | 3 ++ drivers/net/wireless/realtek/rtw89/mac.h | 24 ++++++++++++ drivers/net/wireless/realtek/rtw89/mac_be.c | 38 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.c | 27 +++++++++++++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 +- 8 files changed, 107 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 5d35f13a1ea6..0343cd1a0ee1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -5236,9 +5236,18 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev) static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev, enum rtw89_tfc_interval interval) { + struct rtw89_hal *hal = &rtwdev->hal; struct ieee80211_vif *vif; struct rtw89_vif *rtwvif; + /* + * If vcore level is set, temperature is high and voltage is low. As + * entering power save must reset voltage to default, avoid power save + * until vcore decreases to zero resulting from temperature becomes low. + */ + if (hal->thermal_prot_vlv) + return; + rtw89_for_each_rtwvif(rtwdev, rtwvif) { if (rtwvif->tdls_peer) continue; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d457da2a21fe..174349c6bc58 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5523,10 +5523,13 @@ enum rtw89_dm_type { RTW89_DM_HW_SCAN, RTW89_DM_INACTIVE_PS, RTW89_DM_DIG_PD, + RTW89_DM_VCORE, }; #define RTW89_THERMAL_PROT_LV_MAX 5 #define RTW89_THERMAL_PROT_STEP 5 /* -5% for each level */ +#define RTW89_THERMAL_PROT_VLV_MAX 6 +#define RTW89_THERMAL_PROT_VLV_TH_OFFSET 20 struct rtw89_hal { u32 rx_fltr; @@ -5567,6 +5570,7 @@ struct rtw89_hal { u8 thermal_prot_vmax; u8 thermal_prot_vmin; + u8 thermal_prot_vlv; /* 0 ~ RTW89_THERMAL_PROT_VLV_MAX (6) */ u8 fixed_dig_pd_th; /* v = (X(dBm) + 102)/2 */ s8 fixed_dig_cck_pd_th; /* dBm */ diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 5786120602ab..e42dc5707576 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4778,6 +4778,7 @@ static const struct rtw89_disabled_dm_info { DM_INFO(HW_SCAN), DM_INFO(INACTIVE_PS), DM_INFO(DIG_PD), + DM_INFO(VCORE), }; static ssize_t diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index f5e55e6be119..6e3da5e4a1b3 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1602,6 +1602,8 @@ int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev) void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev) { + rtw89_mac_set_vcore_reset(rtwdev); + rtw89_mac_power_switch(rtwdev, false); } @@ -7477,6 +7479,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .cfg_ppdu_status = rtw89_mac_cfg_ppdu_status_ax, .cfg_phy_rpt = NULL, .set_edcca_mode = NULL, + .set_vcore_cfg = NULL, .dle_mix_cfg = dle_mix_cfg_ax, .chk_dle_rdy = chk_dle_rdy_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 8bbe49492d4c..f85c14ca0f35 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1100,6 +1100,7 @@ struct rtw89_mac_gen_def { int (*cfg_ppdu_status)(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable); void (*cfg_phy_rpt)(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable); void (*set_edcca_mode)(struct rtw89_dev *rtwdev, u8 mac_idx, bool normal); + void (*set_vcore_cfg)(struct rtw89_dev *rtwdev, u8 vlv); int (*dle_mix_cfg)(struct rtw89_dev *rtwdev, const struct rtw89_dle_mem *cfg); int (*chk_dle_rdy)(struct rtw89_dev *rtwdev, bool wde_or_ple); @@ -1459,6 +1460,29 @@ void rtw89_mac_set_edcca_mode(struct rtw89_dev *rtwdev, u8 mac_idx, bool normal) mac->set_edcca_mode(rtwdev, mac_idx, normal); } +static inline +void rtw89_mac_set_vcore_cfg(struct rtw89_dev *rtwdev, u8 vlv) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + + if (!mac->set_vcore_cfg) + return; + + mac->set_vcore_cfg(rtwdev, vlv); +} + +static inline +void rtw89_mac_set_vcore_reset(struct rtw89_dev *rtwdev) +{ + struct rtw89_hal *hal = &rtwdev->hal; + + if (hal->thermal_prot_vlv == 0) + return; + + hal->thermal_prot_vlv = 0; + rtw89_mac_set_vcore_cfg(rtwdev, 0); +} + static inline void rtw89_mac_set_edcca_mode_bands(struct rtw89_dev *rtwdev, bool normal) { diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 0fe317929b1b..4bdf20b7ba6d 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -2676,6 +2676,43 @@ void rtw89_mac_set_edcca_mode_be(struct rtw89_dev *rtwdev, u8 mac_idx, bool norm } } +static +void rtw89_mac_set_vcore_cfg_be(struct rtw89_dev *rtwdev, u8 vlv) +{ + struct rtw89_hal *hal = &rtwdev->hal; + u32 val32; + u8 target; + u8 vpwm; + int i; + + if (rtwdev->chip->chip_id != RTL8922D) + return; + + target = clamp(hal->thermal_prot_vmax - vlv, + hal->thermal_prot_vmin, hal->thermal_prot_vmax); + + val32 = rtw89_read32(rtwdev, R_BE_SPS_DIG_ON_CTRL0); + vpwm = u32_get_bits(val32, B_BE_PWMTUNE_MASK); + val32 &= ~B_BE_PWMTUNE_MASK; + + if (vpwm == target) + return; + + for (i = 0; i < RTW89_THERMAL_PROT_VLV_MAX; i++) { + if (vpwm > target) + vpwm--; + else + vpwm++; + + rtw89_write32(rtwdev, R_BE_SPS_DIG_ON_CTRL0, val32 | vpwm); + + if (vpwm == target) + break; + + mdelay(50); + } +} + static int rtw89_mac_cfg_ppdu_status_be(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable) { @@ -3287,6 +3324,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .cfg_ppdu_status = rtw89_mac_cfg_ppdu_status_be, .cfg_phy_rpt = rtw89_mac_cfg_phy_rpt_be, .set_edcca_mode = rtw89_mac_set_edcca_mode_be, + .set_vcore_cfg = rtw89_mac_set_vcore_cfg_be, .dle_mix_cfg = dle_mix_cfg_be, .chk_dle_rdy = chk_dle_rdy_be, diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 981c7f02271a..dcd54be58a33 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5718,6 +5718,32 @@ static void rtw89_phy_antdiv_init(struct rtw89_dev *rtwdev) rtw89_phy_antdiv_reg_init(rtwdev); } +static void rtw89_phy_thermal_protect_vcore(struct rtw89_dev *rtwdev) +{ + struct rtw89_phy_stat *phystat = &rtwdev->phystat; + struct rtw89_hal *hal = &rtwdev->hal; + bool vcore_enabled = !!hal->thermal_prot_vmax; + u8 th_max = phystat->last_thermal_max; + u8 vlv = hal->thermal_prot_vlv; + u8 prot_th; + + if (!vcore_enabled || (hal->disabled_dm_bitmap & BIT(RTW89_DM_VCORE))) + return; + + prot_th = hal->thermal_prot_th - RTW89_THERMAL_PROT_VLV_TH_OFFSET; + if (th_max > prot_th && vlv < RTW89_THERMAL_PROT_VLV_MAX) + vlv++; + else if (th_max < prot_th - 2 && vlv > 0) + vlv--; + else + return; + + rtw89_debug(rtwdev, RTW89_DBG_RFK_TRACK, "thermal protection vlv=%d\n", vlv); + + hal->thermal_prot_vlv = vlv; + rtw89_mac_set_vcore_cfg(rtwdev, vlv); +} + static void rtw89_phy_thermal_protect(struct rtw89_dev *rtwdev) { struct rtw89_phy_stat *phystat = &rtwdev->phystat; @@ -6186,6 +6212,7 @@ void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) struct rtw89_bb_ctx *bb; rtw89_phy_stat_thermal_update(rtwdev); + rtw89_phy_thermal_protect_vcore(rtwdev); rtw89_phy_thermal_protect(rtwdev); rtw89_phy_stat_rssi_update(rtwdev); rtw89_phy_stat_update(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index a6693c8413d5..36b9b529195d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -3451,7 +3451,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .wde_qempty_acq_grpnum = 8, .wde_qempty_mgq_grpsel = 8, .rf_base_addr = {0x3e000, 0x3f000}, - .thermal_th = {0xac, 0xad}, + .thermal_th = {0xac, 0xb4}, .pwr_on_seq = NULL, .pwr_off_seq = NULL, .bb_table = NULL, From b7911466b6dbba6df7c282044b65eaa8700584cb Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 7 Jul 2026 17:10:51 +0800 Subject: [PATCH 057/403] wifi: rtw89: 8922d: set ANA CLK enter to 500KHz To power save, change the clock of ANA hardware from 12MHz to 500KHz. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index f85c14ca0f35..a5f1694af91a 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1680,6 +1680,7 @@ enum rtw89_mac_xtal_si_offset { XTAL_SI_PWR_CUT = 0x10, #define XTAL_SI_SMALL_PWR_CUT BIT(0) #define XTAL_SI_BIG_PWR_CUT BIT(1) + XTAL_SI_AONLDO_CTRL = 0x10, XTAL_SI_XTAL_DRV = 0x15, #define XTAL_SI_DRV_LATCH BIT(4) XTAL_SI_XTAL_PLL = 0x16, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 36b9b529195d..8ca07aeb377a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -535,6 +535,19 @@ static int rtw8922d_pwr_on_func(struct rtw89_dev *rtwdev) rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_EN_WLON); rtw89_write32_set(rtwdev, R_BE_WLRESUME_CTRL, B_BE_LPSROP_CMAC0 | B_BE_LPSROP_CMAC1); + + if (hal->aid == RTL8922D_AID7102) { + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_AONLDO_CTRL, 0, 0x20); + if (ret) + return ret; + + udelay(1); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_AONLDO_CTRL, 0, 0x40); + if (ret) + return ret; + } + rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFN_ONMAC); ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_APFN_ONMAC), From 79afed9426ce4cc1bf6807dfadda081124ca2c4a Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 7 Jul 2026 17:10:52 +0800 Subject: [PATCH 058/403] wifi: rtw89: 8922d: update scaling factor for RX path Update the per-MCS calibration values of RX scaling factors on RX path (1R or 2R) for BCC and LDPC coding schemes. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 5 ++++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 26 +++++++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 72f7b80fe1fb..2caefb929199 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -11002,6 +11002,11 @@ #define R_RX_LDPC02_BE4 0x26834 #define B_RX_LDPC10_BE4 GENMASK(17, 12) #define B_RX_LDPC11_BE4 GENMASK(23, 18) +#define B_RX_LDPC12_BE4 GENMASK(29, 24) +#define R_RX_LDPC03_BE4 0x26838 +#define B_RX_LDPC13_BE4 GENMASK(5, 0) +#define B_RX_LDPC02_BE4 GENMASK(23, 18) +#define B_RX_LDPC03_BE4 GENMASK(29, 24) #define R_RX_LDPC00_BE4 0x2683C #define B_RX_LDPC04_BE4 GENMASK(5, 0) #define B_RX_LDPC05_BE4 GENMASK(11, 6) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 8ca07aeb377a..768434db14c6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2230,38 +2230,42 @@ static int rtw8922d_ctrl_rx_path_tmac(struct rtw89_dev *rtwdev, rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS6_BE4, 3, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS7_BE4, 7, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS8_BE4, 2, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS9_BE4, 2, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_AWGN00_BE4, B_RX_AWGN04_BE4, 4, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_AWGN00_BE4, B_RX_AWGN07_BE4, 2, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_AWGN01_BE4, B_RX_AWGN09_BE4, 0, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_AWGN02_BE4, B_RX_AWGN11_BE4, 1, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC04_BE4, 8, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC05_BE4, 5, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC06_BE4, 3, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC07_BE4, 5, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC08_BE4, 1, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_RX_LDPC01_BE4, B_RX_LDPC09_BE4, 2, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC10_BE4, 4, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC11_BE4, 2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC03_BE4, B_RX_LDPC02_BE4, 0x6, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC03_BE4, B_RX_LDPC03_BE4, 0xf, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC04_BE4, 0x10, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC05_BE4, 0xa, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC06_BE4, 0x8, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC07_BE4, 0x14, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC01_BE4, B_RX_LDPC09_BE4, 0x14, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC10_BE4, 0xc, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC11_BE4, 0xb, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC12_BE4, 0x14, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC03_BE4, B_RX_LDPC13_BE4, 0x2d, phy_idx); } else { rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC0_BE4, B_RXCH_MCS4_BE4, 13, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS5_BE4, 15, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS6_BE4, 6, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS7_BE4, 15, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS8_BE4, 4, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS9_BE4, 15, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_AWGN00_BE4, B_RX_AWGN04_BE4, 9, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_AWGN00_BE4, B_RX_AWGN07_BE4, 3, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_AWGN01_BE4, B_RX_AWGN09_BE4, 1, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_AWGN02_BE4, B_RX_AWGN11_BE4, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC03_BE4, B_RX_LDPC02_BE4, 0x3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC03_BE4, B_RX_LDPC03_BE4, 0x9, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC04_BE4, 9, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC05_BE4, 8, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC06_BE4, 6, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC07_BE4, 16, phy_idx); - rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC08_BE4, 4, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_LDPC01_BE4, B_RX_LDPC09_BE4, 9, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC10_BE4, 9, phy_idx); rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC11_BE4, 7, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC12_BE4, 0x17, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC03_BE4, B_RX_LDPC13_BE4, 0x1a, phy_idx); } return 0; From 9bf6bd6ed5accb57544d04ded911a2ef1642d48f Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Tue, 7 Jul 2026 17:10:53 +0800 Subject: [PATCH 059/403] wifi: rtw89: 8852a: fix RSSI report when average beacon RSSI is not ready 8852A uses the average beacon RSSI to smooth the RSSI. However, before the average beacon RSSI is available, the RSSI should use the PPDU status RSSI of the received packet to avoid reporting the RSSI as -110 dBm. Fixes: f0f3bf4b370c ("wifi: rtw89: 8852a: report average RSSI to avoid unnecessary scanning") Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 6aa726efb7f6..78e07276fb2e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2211,7 +2211,7 @@ static void rtw8852a_query_ppdu(struct rtw89_dev *rtwdev, u8 raw; if (!status->signal) { - if (phy_ppdu->to_self) + if (phy_ppdu->to_self && ewma_rssi_read(&bb->bcn_rssi)) raw = ewma_rssi_read(&bb->bcn_rssi); else raw = max(rx_power[RF_PATH_A], rx_power[RF_PATH_B]); From 235fa79d756106028b92505e78bd281698d9fb44 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 7 Jul 2026 17:10:54 +0800 Subject: [PATCH 060/403] wifi: rtw89: phy: add NCTL check for WiFi 7 chips Initialize NCTL hardware and check the state before downloading NCTL. Otherwise, the following RF calibrations relying on NCTL may fail. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-14-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 1 + drivers/net/wireless/realtek/rtw89/phy.h | 6 +++ drivers/net/wireless/realtek/rtw89/phy_be.c | 41 +++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/reg.h | 6 +++ 4 files changed, 54 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index dcd54be58a33..30d5741da87e 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -9010,6 +9010,7 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_ax = { .physts = &rtw89_physts_regs_ax, .cfo = &rtw89_cfo_regs_ax, .bb_wrap = NULL, + .nctl = NULL, .phy0_phy1_offset = rtw89_phy0_phy1_offset_ax, .config_bb_gain = rtw89_phy_config_bb_gain_ax, .preinit_rf_nctl = rtw89_phy_preinit_rf_nctl_ax, diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 532232892831..e31034377b54 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -502,6 +502,11 @@ struct rtw89_bb_wrap_regs { u32 pwr_macid_path; }; +struct rtw89_nctl_regs { + u32 cfg; + u32 rw; +}; + enum rtw89_bandwidth_section_num_ax { RTW89_BW20_SEC_NUM_AX = 8, RTW89_BW40_SEC_NUM_AX = 4, @@ -679,6 +684,7 @@ struct rtw89_phy_gen_def { const struct rtw89_physts_regs *physts; const struct rtw89_cfo_regs *cfo; const struct rtw89_bb_wrap_regs *bb_wrap; + const struct rtw89_nctl_regs *nctl; u32 (*phy0_phy1_offset)(struct rtw89_dev *rtwdev, u32 addr); void (*config_bb_gain)(struct rtw89_dev *rtwdev, const struct rtw89_reg2_def *reg, diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index ac4ce30445b3..e471409a4b8f 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -225,6 +225,16 @@ static const struct rtw89_bb_wrap_regs rtw89_bb_wrap_regs_be_v1 = { .pwr_macid_path = R_BE_PWR_MACID_PATH_BASE_V1, }; +static const struct rtw89_nctl_regs rtw89_nctl_regs_be = { + .cfg = R_NCTL_CFG, + .rw = R_NCTL_RW, +}; + +static const struct rtw89_nctl_regs rtw89_nctl_regs_be_v1 = { + .cfg = R_NCTL_CFG_BE4, + .rw = R_NCTL_RW_BE4, +}; + static u32 rtw89_phy0_phy1_offset_be(struct rtw89_dev *rtwdev, u32 addr) { u32 phy_page = addr >> 8; @@ -440,6 +450,28 @@ static void rtw89_phy_config_bb_gain_be(struct rtw89_dev *rtwdev, } } +static void rtw89_phy_preinit_rf_nctl_check_be(struct rtw89_dev *rtwdev) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const struct rtw89_nctl_regs *nctl = phy->nctl; + int poll; + u32 val; + + rtw89_phy_write32(rtwdev, nctl->cfg, B_NCTL_CHK_EN); + + for (poll = 0; poll < 10000; poll++) { + rtw89_phy_write32(rtwdev, nctl->rw, B_NCTL_CHECK); + + fsleep(10); + + val = rtw89_phy_read32(rtwdev, nctl->rw); + if (val == B_NCTL_CHECK) + return; + } + + rtw89_warn(rtwdev, "NCTL INIT check 0x%x[2]TIMEOUT!\n", nctl->rw); +} + static void rtw89_phy_preinit_rf_nctl_be(struct rtw89_dev *rtwdev) { rtw89_phy_write32_mask(rtwdev, R_GOTX_IQKDPK_C0, B_GOTX_IQKDPK, 0x3); @@ -456,6 +488,11 @@ static void rtw89_phy_preinit_rf_nctl_be(struct rtw89_dev *rtwdev) rtw89_phy_write32_mask(rtwdev, R_IQK_DPK_RST_C1, B_IQK_DPK_RST, 0x1); rtw89_phy_write32_mask(rtwdev, R_TXRFC_C1, B_TXRFC_RST, 0x1); } + + rtw89_phy_write32_mask(rtwdev, R_TX_COLLISION_T2R_ST, B_RX_CFIR_PATH_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_TX_COLLISION_T2R_ST, B_RX_CFIR_PATH, 0x3); + + rtw89_phy_preinit_rf_nctl_check_be(rtwdev); } static void rtw89_phy_preinit_rf_nctl_be_v1(struct rtw89_dev *rtwdev) @@ -466,6 +503,8 @@ static void rtw89_phy_preinit_rf_nctl_be_v1(struct rtw89_dev *rtwdev) rtw89_phy_write32_mask(rtwdev, R_IQK_DPK_RST_BE4, B_IQK_DPK_RST, 0x1); rtw89_phy_write32_mask(rtwdev, R_IQK_DPK_PRST_BE4, B_IQK_DPK_PRST, 0x1); rtw89_phy_write32_mask(rtwdev, R_IQK_DPK_PRST_C1_BE4, B_IQK_DPK_PRST, 0x1); + + rtw89_phy_preinit_rf_nctl_check_be(rtwdev); } static u32 rtw89_phy_bb_wrap_flush_addr(struct rtw89_dev *rtwdev, u32 addr) @@ -1905,6 +1944,7 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be = { .physts = &rtw89_physts_regs_be, .cfo = &rtw89_cfo_regs_be, .bb_wrap = &rtw89_bb_wrap_regs_be, + .nctl = &rtw89_nctl_regs_be, .phy0_phy1_offset = rtw89_phy0_phy1_offset_be, .config_bb_gain = rtw89_phy_config_bb_gain_be, .preinit_rf_nctl = rtw89_phy_preinit_rf_nctl_be, @@ -1930,6 +1970,7 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be_v1 = { .physts = &rtw89_physts_regs_be_v1, .cfo = &rtw89_cfo_regs_be_v1, .bb_wrap = &rtw89_bb_wrap_regs_be_v1, + .nctl = &rtw89_nctl_regs_be_v1, .phy0_phy1_offset = rtw89_phy0_phy1_offset_be_v1, .config_bb_gain = rtw89_phy_config_bb_gain_be, .preinit_rf_nctl = rtw89_phy_preinit_rf_nctl_be_v1, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 2caefb929199..4d2117de798b 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -9022,6 +9022,8 @@ #define B_IQK_DPK_RST BIT(0) #define R_TX_COLLISION_T2R_ST 0x0C70 #define B_TX_COLLISION_T2R_ST_M GENMASK(25, 20) +#define B_RX_CFIR_PATH_EN BIT(9) +#define B_RX_CFIR_PATH GENMASK(6, 5) #define B_TXRX_FORCE_VAL GENMASK(9, 0) #define R_TXGATING 0x0C74 #define B_TXGATING_EN BIT(4) @@ -10115,6 +10117,8 @@ #define R_S1_DACKQ8 0x7E98 #define B_S1_DACKQ8_K GENMASK(15, 8) #define R_NCTL_CFG 0x8000 +#define R_NCTL_CFG_BE4 0x38000 +#define B_NCTL_CHK_EN BIT(3) #define B_NCTL_CFG_SPAGE GENMASK(2, 1) #define R_NCTL_RPT 0x8008 #define B_NCTL_RPT_FLG BIT(26) @@ -10148,6 +10152,8 @@ #define R_KIP_MOD 0x8078 #define B_KIP_MOD GENMASK(19, 0) #define R_NCTL_RW 0x8080 +#define R_NCTL_RW_BE4 0x38080 +#define B_NCTL_CHECK BIT(2) #define R_KIP_SYSCFG 0x8088 #define R_KIP_CLK 0x808C #define R_DPK_IDL 0x809C From 5c925d7722c674fe4d0819f2bb176d144a3eaae2 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 7 Jul 2026 17:10:55 +0800 Subject: [PATCH 061/403] wifi: rtw89: unify access struct of TX power track tables There are two struct to access TX power track tables. One is to access driver built-in tables, and the other one is to access the tables in fw element. As we are going to remove the built-in tables from driver, unify to use the struct as fw element style. The precedence is to use tables in fw element if present, and then built-in tables. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-15-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 19 ---- .../net/wireless/realtek/rtw89/rtw8851b_rfk.c | 20 ++-- .../wireless/realtek/rtw89/rtw8851b_table.c | 15 +-- .../wireless/realtek/rtw89/rtw8851b_table.h | 2 +- .../net/wireless/realtek/rtw89/rtw8852a_rfk.c | 36 ++++--- .../wireless/realtek/rtw89/rtw8852a_table.c | 27 ++--- .../wireless/realtek/rtw89/rtw8852a_table.h | 2 +- .../net/wireless/realtek/rtw89/rtw8852b_rfk.c | 36 ++++--- .../wireless/realtek/rtw89/rtw8852b_table.c | 27 ++--- .../wireless/realtek/rtw89/rtw8852b_table.h | 2 +- .../net/wireless/realtek/rtw89/rtw8852c_rfk.c | 101 +++++++----------- .../wireless/realtek/rtw89/rtw8852c_table.c | 35 +++--- .../wireless/realtek/rtw89/rtw8852c_table.h | 2 +- 13 files changed, 146 insertions(+), 178 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index e31034377b54..b0c82bac72f0 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -353,25 +353,6 @@ struct rtw89_txpwr_byrate_cfg { u32 data; }; -struct rtw89_txpwr_track_cfg { - const s8 (*delta_swingidx_6gb_n)[DELTA_SWINGIDX_SIZE]; - const s8 (*delta_swingidx_6gb_p)[DELTA_SWINGIDX_SIZE]; - const s8 (*delta_swingidx_6ga_n)[DELTA_SWINGIDX_SIZE]; - const s8 (*delta_swingidx_6ga_p)[DELTA_SWINGIDX_SIZE]; - const s8 (*delta_swingidx_5gb_n)[DELTA_SWINGIDX_SIZE]; - const s8 (*delta_swingidx_5gb_p)[DELTA_SWINGIDX_SIZE]; - const s8 (*delta_swingidx_5ga_n)[DELTA_SWINGIDX_SIZE]; - const s8 (*delta_swingidx_5ga_p)[DELTA_SWINGIDX_SIZE]; - const s8 *delta_swingidx_2gb_n; - const s8 *delta_swingidx_2gb_p; - const s8 *delta_swingidx_2ga_n; - const s8 *delta_swingidx_2ga_p; - const s8 *delta_swingidx_2g_cck_b_n; - const s8 *delta_swingidx_2g_cck_b_p; - const s8 *delta_swingidx_2g_cck_a_n; - const s8 *delta_swingidx_2g_cck_a_p; -}; - struct rtw89_phy_dig_gain_cfg { const struct rtw89_reg_def *table; u8 size; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c index a6c7f59223ef..c63f0e6364da 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c @@ -2823,6 +2823,7 @@ static void _tssi_set_tmeter_tbl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx ph } \ __val; \ }) + const struct rtw89_fw_txpwr_track_cfg *trk = rtwdev->fw.elm_info.txpwr_trk; struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; u8 ch = chan->channel; u8 subband = chan->subband_type; @@ -2833,23 +2834,26 @@ static void _tssi_set_tmeter_tbl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx ph u32 tmp = 0; u8 i, j; + if (!trk) + trk = &rtw89_8851b_trk_cfg; + switch (subband) { default: case RTW89_CH_2G: - thm_up_a = rtw89_8851b_trk_cfg.delta_swingidx_2ga_p; - thm_down_a = rtw89_8851b_trk_cfg.delta_swingidx_2ga_n; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_P][0]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_N][0]; break; case RTW89_CH_5G_BAND_1: - thm_up_a = rtw89_8851b_trk_cfg.delta_swingidx_5ga_p[0]; - thm_down_a = rtw89_8851b_trk_cfg.delta_swingidx_5ga_n[0]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][0]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][0]; break; case RTW89_CH_5G_BAND_3: - thm_up_a = rtw89_8851b_trk_cfg.delta_swingidx_5ga_p[1]; - thm_down_a = rtw89_8851b_trk_cfg.delta_swingidx_5ga_n[1]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][1]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][1]; break; case RTW89_CH_5G_BAND_4: - thm_up_a = rtw89_8851b_trk_cfg.delta_swingidx_5ga_p[2]; - thm_down_a = rtw89_8851b_trk_cfg.delta_swingidx_5ga_n[2]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][2]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][2]; break; } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c b/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c index a9c309c245c3..b8105f6e94f1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c @@ -2,6 +2,7 @@ /* Copyright(c) 2022-2023 Realtek Corporation */ +#include "fw.h" #include "phy.h" #include "reg.h" #include "rtw8851b_table.h" @@ -14866,13 +14867,13 @@ const struct rtw89_txpwr_table rtw89_8851b_byr_table_type2 = { .load = rtw89_phy_load_txpwr_byrate, }; -const struct rtw89_txpwr_track_cfg rtw89_8851b_trk_cfg = { - .delta_swingidx_5ga_n = _txpwr_track_delta_swingidx_5ga_n, - .delta_swingidx_5ga_p = _txpwr_track_delta_swingidx_5ga_p, - .delta_swingidx_2ga_n = _txpwr_track_delta_swingidx_2ga_n, - .delta_swingidx_2ga_p = _txpwr_track_delta_swingidx_2ga_p, - .delta_swingidx_2g_cck_a_n = _txpwr_track_delta_swingidx_2g_cck_a_n, - .delta_swingidx_2g_cck_a_p = _txpwr_track_delta_swingidx_2g_cck_a_p, +const struct rtw89_fw_txpwr_track_cfg rtw89_8851b_trk_cfg = { + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N] = _txpwr_track_delta_swingidx_5ga_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P] = _txpwr_track_delta_swingidx_5ga_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_N] = &_txpwr_track_delta_swingidx_2ga_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_P] = &_txpwr_track_delta_swingidx_2ga_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_N] = &_txpwr_track_delta_swingidx_2g_cck_a_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_P] = &_txpwr_track_delta_swingidx_2g_cck_a_p, }; const struct rtw89_rfe_parms rtw89_8851b_dflt_parms = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b_table.h b/drivers/net/wireless/realtek/rtw89/rtw8851b_table.h index d8cf545d40a0..a73c0a6f03df 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b_table.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b_table.h @@ -11,7 +11,7 @@ extern const struct rtw89_phy_table rtw89_8851b_phy_bb_table; extern const struct rtw89_phy_table rtw89_8851b_phy_bb_gain_table; extern const struct rtw89_phy_table rtw89_8851b_phy_radioa_table; extern const struct rtw89_phy_table rtw89_8851b_phy_nctl_table; -extern const struct rtw89_txpwr_track_cfg rtw89_8851b_trk_cfg; +extern const struct rtw89_fw_txpwr_track_cfg rtw89_8851b_trk_cfg; extern const struct rtw89_rfe_parms rtw89_8851b_dflt_parms; extern const struct rtw89_rfe_parms_conf rtw89_8851b_rfe_parms_conf[]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c index 8679b21fd3fd..609cc300f24e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c @@ -2910,6 +2910,7 @@ static void _tssi_set_tmeter_tbl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx ph } \ __val; \ }) + const struct rtw89_fw_txpwr_track_cfg *trk = rtwdev->fw.elm_info.txpwr_trk; struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; u8 ch = chan->channel; u8 subband = chan->subband_type; @@ -2922,31 +2923,34 @@ static void _tssi_set_tmeter_tbl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx ph u32 tmp = 0; u8 i, j; + if (!trk) + trk = &rtw89_8852a_trk_cfg; + switch (subband) { default: case RTW89_CH_2G: - thm_up_a = rtw89_8852a_trk_cfg.delta_swingidx_2ga_p; - thm_down_a = rtw89_8852a_trk_cfg.delta_swingidx_2ga_n; - thm_up_b = rtw89_8852a_trk_cfg.delta_swingidx_2gb_p; - thm_down_b = rtw89_8852a_trk_cfg.delta_swingidx_2gb_n; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_P][0]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_N][0]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_P][0]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_N][0]; break; case RTW89_CH_5G_BAND_1: - thm_up_a = rtw89_8852a_trk_cfg.delta_swingidx_5ga_p[0]; - thm_down_a = rtw89_8852a_trk_cfg.delta_swingidx_5ga_n[0]; - thm_up_b = rtw89_8852a_trk_cfg.delta_swingidx_5gb_p[0]; - thm_down_b = rtw89_8852a_trk_cfg.delta_swingidx_5gb_n[0]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][0]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][0]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][0]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][0]; break; case RTW89_CH_5G_BAND_3: - thm_up_a = rtw89_8852a_trk_cfg.delta_swingidx_5ga_p[1]; - thm_down_a = rtw89_8852a_trk_cfg.delta_swingidx_5ga_n[1]; - thm_up_b = rtw89_8852a_trk_cfg.delta_swingidx_5gb_p[1]; - thm_down_b = rtw89_8852a_trk_cfg.delta_swingidx_5gb_n[1]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][1]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][1]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][1]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][1]; break; case RTW89_CH_5G_BAND_4: - thm_up_a = rtw89_8852a_trk_cfg.delta_swingidx_5ga_p[2]; - thm_down_a = rtw89_8852a_trk_cfg.delta_swingidx_5ga_n[2]; - thm_up_b = rtw89_8852a_trk_cfg.delta_swingidx_5gb_p[2]; - thm_down_b = rtw89_8852a_trk_cfg.delta_swingidx_5gb_n[2]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][2]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][2]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][2]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][2]; break; } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852a_table.c index ffdeb3801991..f1bcef64a91f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a_table.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a_table.c @@ -2,6 +2,7 @@ /* Copyright(c) 2019-2020 Realtek Corporation */ +#include "fw.h" #include "phy.h" #include "reg.h" #include "rtw8852a_table.h" @@ -50983,19 +50984,19 @@ const struct rtw89_txpwr_table rtw89_8852a_byr_table = { .load = rtw89_phy_load_txpwr_byrate, }; -const struct rtw89_txpwr_track_cfg rtw89_8852a_trk_cfg = { - .delta_swingidx_5gb_n = _txpwr_track_delta_swingidx_5gb_n, - .delta_swingidx_5gb_p = _txpwr_track_delta_swingidx_5gb_p, - .delta_swingidx_5ga_n = _txpwr_track_delta_swingidx_5ga_n, - .delta_swingidx_5ga_p = _txpwr_track_delta_swingidx_5ga_p, - .delta_swingidx_2gb_n = _txpwr_track_delta_swingidx_2gb_n, - .delta_swingidx_2gb_p = _txpwr_track_delta_swingidx_2gb_p, - .delta_swingidx_2ga_n = _txpwr_track_delta_swingidx_2ga_n, - .delta_swingidx_2ga_p = _txpwr_track_delta_swingidx_2ga_p, - .delta_swingidx_2g_cck_b_n = _txpwr_track_delta_swingidx_2g_cck_b_n, - .delta_swingidx_2g_cck_b_p = _txpwr_track_delta_swingidx_2g_cck_b_p, - .delta_swingidx_2g_cck_a_n = _txpwr_track_delta_swingidx_2g_cck_a_n, - .delta_swingidx_2g_cck_a_p = _txpwr_track_delta_swingidx_2g_cck_a_p, +const struct rtw89_fw_txpwr_track_cfg rtw89_8852a_trk_cfg = { + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N] = _txpwr_track_delta_swingidx_5gb_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P] = _txpwr_track_delta_swingidx_5gb_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N] = _txpwr_track_delta_swingidx_5ga_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P] = _txpwr_track_delta_swingidx_5ga_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_N] = &_txpwr_track_delta_swingidx_2gb_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_P] = &_txpwr_track_delta_swingidx_2gb_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_N] = &_txpwr_track_delta_swingidx_2ga_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_P] = &_txpwr_track_delta_swingidx_2ga_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_B_N] = &_txpwr_track_delta_swingidx_2g_cck_b_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_B_P] = &_txpwr_track_delta_swingidx_2g_cck_b_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_N] = &_txpwr_track_delta_swingidx_2g_cck_a_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_P] = &_txpwr_track_delta_swingidx_2g_cck_a_p, }; const struct rtw89_rfe_parms rtw89_8852a_dflt_parms = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a_table.h b/drivers/net/wireless/realtek/rtw89/rtw8852a_table.h index 58fe8575c1c9..501a8248d1ce 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a_table.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a_table.h @@ -11,7 +11,7 @@ extern const struct rtw89_phy_table rtw89_8852a_phy_bb_table; extern const struct rtw89_phy_table rtw89_8852a_phy_radioa_table; extern const struct rtw89_phy_table rtw89_8852a_phy_radiob_table; extern const struct rtw89_phy_table rtw89_8852a_phy_nctl_table; -extern const struct rtw89_txpwr_track_cfg rtw89_8852a_trk_cfg; +extern const struct rtw89_fw_txpwr_track_cfg rtw89_8852a_trk_cfg; extern const struct rtw89_rfe_parms rtw89_8852a_dflt_parms; #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index 5cfacc10e7c8..29a82b1c4457 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -2784,6 +2784,7 @@ static void _tssi_set_tmeter_tbl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx ph } \ __val; \ }) + const struct rtw89_fw_txpwr_track_cfg *trk = rtwdev->fw.elm_info.txpwr_trk; struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; u8 ch = chan->channel; u8 subband = chan->subband_type; @@ -2796,31 +2797,34 @@ static void _tssi_set_tmeter_tbl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx ph u32 tmp = 0; u8 i, j; + if (!trk) + trk = &rtw89_8852b_trk_cfg; + switch (subband) { default: case RTW89_CH_2G: - thm_up_a = rtw89_8852b_trk_cfg.delta_swingidx_2ga_p; - thm_down_a = rtw89_8852b_trk_cfg.delta_swingidx_2ga_n; - thm_up_b = rtw89_8852b_trk_cfg.delta_swingidx_2gb_p; - thm_down_b = rtw89_8852b_trk_cfg.delta_swingidx_2gb_n; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_P][0]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_N][0]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_P][0]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_N][0]; break; case RTW89_CH_5G_BAND_1: - thm_up_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_p[0]; - thm_down_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_n[0]; - thm_up_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_p[0]; - thm_down_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_n[0]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][0]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][0]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][0]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][0]; break; case RTW89_CH_5G_BAND_3: - thm_up_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_p[1]; - thm_down_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_n[1]; - thm_up_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_p[1]; - thm_down_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_n[1]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][1]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][1]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][1]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][1]; break; case RTW89_CH_5G_BAND_4: - thm_up_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_p[2]; - thm_down_a = rtw89_8852b_trk_cfg.delta_swingidx_5ga_n[2]; - thm_up_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_p[2]; - thm_down_b = rtw89_8852b_trk_cfg.delta_swingidx_5gb_n[2]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][2]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][2]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][2]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][2]; break; } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c index 07945d06dc59..96b18e9095b3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c @@ -2,6 +2,7 @@ /* Copyright(c) 2019-2020 Realtek Corporation */ +#include "fw.h" #include "phy.h" #include "reg.h" #include "rtw8852b_table.h" @@ -22895,19 +22896,19 @@ const struct rtw89_txpwr_table rtw89_8852b_byr_table = { .load = rtw89_phy_load_txpwr_byrate, }; -const struct rtw89_txpwr_track_cfg rtw89_8852b_trk_cfg = { - .delta_swingidx_5gb_n = _txpwr_track_delta_swingidx_5gb_n, - .delta_swingidx_5gb_p = _txpwr_track_delta_swingidx_5gb_p, - .delta_swingidx_5ga_n = _txpwr_track_delta_swingidx_5ga_n, - .delta_swingidx_5ga_p = _txpwr_track_delta_swingidx_5ga_p, - .delta_swingidx_2gb_n = _txpwr_track_delta_swingidx_2gb_n, - .delta_swingidx_2gb_p = _txpwr_track_delta_swingidx_2gb_p, - .delta_swingidx_2ga_n = _txpwr_track_delta_swingidx_2ga_n, - .delta_swingidx_2ga_p = _txpwr_track_delta_swingidx_2ga_p, - .delta_swingidx_2g_cck_b_n = _txpwr_track_delta_swingidx_2g_cck_b_n, - .delta_swingidx_2g_cck_b_p = _txpwr_track_delta_swingidx_2g_cck_b_p, - .delta_swingidx_2g_cck_a_n = _txpwr_track_delta_swingidx_2g_cck_a_n, - .delta_swingidx_2g_cck_a_p = _txpwr_track_delta_swingidx_2g_cck_a_p, +const struct rtw89_fw_txpwr_track_cfg rtw89_8852b_trk_cfg = { + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N] = _txpwr_track_delta_swingidx_5gb_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P] = _txpwr_track_delta_swingidx_5gb_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N] = _txpwr_track_delta_swingidx_5ga_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P] = _txpwr_track_delta_swingidx_5ga_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_N] = &_txpwr_track_delta_swingidx_2gb_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_P] = &_txpwr_track_delta_swingidx_2gb_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_N] = &_txpwr_track_delta_swingidx_2ga_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_P] = &_txpwr_track_delta_swingidx_2ga_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_B_N] = &_txpwr_track_delta_swingidx_2g_cck_b_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_B_P] = &_txpwr_track_delta_swingidx_2g_cck_b_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_N] = &_txpwr_track_delta_swingidx_2g_cck_a_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_P] = &_txpwr_track_delta_swingidx_2g_cck_a_p, }; const struct rtw89_rfe_parms rtw89_8852b_dflt_parms = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.h b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.h index da6c90e2ba93..d30a1fb43e96 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.h @@ -12,7 +12,7 @@ extern const struct rtw89_phy_table rtw89_8852b_phy_bb_gain_table; extern const struct rtw89_phy_table rtw89_8852b_phy_radioa_table; extern const struct rtw89_phy_table rtw89_8852b_phy_radiob_table; extern const struct rtw89_phy_table rtw89_8852b_phy_nctl_table; -extern const struct rtw89_txpwr_track_cfg rtw89_8852b_trk_cfg; +extern const struct rtw89_fw_txpwr_track_cfg rtw89_8852b_trk_cfg; extern const struct rtw89_rfe_parms rtw89_8852b_dflt_parms; #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c index cbee484dee30..8d03a4645947 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c @@ -2992,7 +2992,7 @@ static void _tssi_set_tmeter_tbl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx ph } \ __val; \ }) - struct rtw89_fw_txpwr_track_cfg *trk = rtwdev->fw.elm_info.txpwr_trk; + const struct rtw89_fw_txpwr_track_cfg *trk = rtwdev->fw.elm_info.txpwr_trk; struct rtw89_tssi_info *tssi_info = &rtwdev->tssi; u8 ch = chan->channel; u8 subband = chan->subband_type; @@ -3005,91 +3005,62 @@ static void _tssi_set_tmeter_tbl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx ph u32 tmp = 0; u8 i, j; + if (!trk) + trk = &rtw89_8852c_trk_cfg; + switch (subband) { default: case RTW89_CH_2G: - thm_up_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_P][0] : - rtw89_8852c_trk_cfg.delta_swingidx_2ga_p; - thm_down_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_N][0] : - rtw89_8852c_trk_cfg.delta_swingidx_2ga_n; - thm_up_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_P][0] : - rtw89_8852c_trk_cfg.delta_swingidx_2gb_p; - thm_down_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_N][0] : - rtw89_8852c_trk_cfg.delta_swingidx_2gb_n; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_P][0]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_N][0]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_P][0]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_N][0]; break; case RTW89_CH_5G_BAND_1: - thm_up_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][0] : - rtw89_8852c_trk_cfg.delta_swingidx_5ga_p[0]; - thm_down_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][0] : - rtw89_8852c_trk_cfg.delta_swingidx_5ga_n[0]; - thm_up_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][0] : - rtw89_8852c_trk_cfg.delta_swingidx_5gb_p[0]; - thm_down_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][0] : - rtw89_8852c_trk_cfg.delta_swingidx_5gb_n[0]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][0]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][0]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][0]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][0]; break; case RTW89_CH_5G_BAND_3: - thm_up_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][1] : - rtw89_8852c_trk_cfg.delta_swingidx_5ga_p[1]; - thm_down_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][1] : - rtw89_8852c_trk_cfg.delta_swingidx_5ga_n[1]; - thm_up_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][1] : - rtw89_8852c_trk_cfg.delta_swingidx_5gb_p[1]; - thm_down_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][1] : - rtw89_8852c_trk_cfg.delta_swingidx_5gb_n[1]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][1]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][1]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][1]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][1]; break; case RTW89_CH_5G_BAND_4: - thm_up_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][2] : - rtw89_8852c_trk_cfg.delta_swingidx_5ga_p[2]; - thm_down_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][2] : - rtw89_8852c_trk_cfg.delta_swingidx_5ga_n[2]; - thm_up_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][2] : - rtw89_8852c_trk_cfg.delta_swingidx_5gb_p[2]; - thm_down_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][2] : - rtw89_8852c_trk_cfg.delta_swingidx_5gb_n[2]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P][2]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N][2]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P][2]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N][2]; break; case RTW89_CH_6G_BAND_IDX0: case RTW89_CH_6G_BAND_IDX1: - thm_up_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][0] : - rtw89_8852c_trk_cfg.delta_swingidx_6ga_p[0]; - thm_down_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][0] : - rtw89_8852c_trk_cfg.delta_swingidx_6ga_n[0]; - thm_up_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][0] : - rtw89_8852c_trk_cfg.delta_swingidx_6gb_p[0]; - thm_down_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][0] : - rtw89_8852c_trk_cfg.delta_swingidx_6gb_n[0]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][0]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][0]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][0]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][0]; break; case RTW89_CH_6G_BAND_IDX2: case RTW89_CH_6G_BAND_IDX3: - thm_up_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][1] : - rtw89_8852c_trk_cfg.delta_swingidx_6ga_p[1]; - thm_down_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][1] : - rtw89_8852c_trk_cfg.delta_swingidx_6ga_n[1]; - thm_up_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][1] : - rtw89_8852c_trk_cfg.delta_swingidx_6gb_p[1]; - thm_down_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][1] : - rtw89_8852c_trk_cfg.delta_swingidx_6gb_n[1]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][1]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][1]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][1]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][1]; break; case RTW89_CH_6G_BAND_IDX4: case RTW89_CH_6G_BAND_IDX5: - thm_up_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][2] : - rtw89_8852c_trk_cfg.delta_swingidx_6ga_p[2]; - thm_down_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][2] : - rtw89_8852c_trk_cfg.delta_swingidx_6ga_n[2]; - thm_up_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][2] : - rtw89_8852c_trk_cfg.delta_swingidx_6gb_p[2]; - thm_down_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][2] : - rtw89_8852c_trk_cfg.delta_swingidx_6gb_n[2]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][2]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][2]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][2]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][2]; break; case RTW89_CH_6G_BAND_IDX6: case RTW89_CH_6G_BAND_IDX7: - thm_up_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][3] : - rtw89_8852c_trk_cfg.delta_swingidx_6ga_p[3]; - thm_down_a = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][3] : - rtw89_8852c_trk_cfg.delta_swingidx_6ga_n[3]; - thm_up_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][3] : - rtw89_8852c_trk_cfg.delta_swingidx_6gb_p[3]; - thm_down_b = trk ? trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][3] : - rtw89_8852c_trk_cfg.delta_swingidx_6gb_n[3]; + thm_up_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P][3]; + thm_down_a = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N][3]; + thm_up_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P][3]; + thm_down_b = trk->delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N][3]; break; } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c index 24c390b6f3d3..b4cf497e8524 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c @@ -2,6 +2,7 @@ /* Copyright(c) 2019-2022 Realtek Corporation */ +#include "fw.h" #include "phy.h" #include "reg.h" #include "rtw8852c_table.h" @@ -57109,23 +57110,23 @@ const struct rtw89_txpwr_table rtw89_8852c_byr_table = { .load = rtw89_phy_load_txpwr_byrate, }; -const struct rtw89_txpwr_track_cfg rtw89_8852c_trk_cfg = { - .delta_swingidx_6gb_n = _txpwr_track_delta_swingidx_6gb_n, - .delta_swingidx_6gb_p = _txpwr_track_delta_swingidx_6gb_p, - .delta_swingidx_6ga_n = _txpwr_track_delta_swingidx_6ga_n, - .delta_swingidx_6ga_p = _txpwr_track_delta_swingidx_6ga_p, - .delta_swingidx_5gb_n = _txpwr_track_delta_swingidx_5gb_n, - .delta_swingidx_5gb_p = _txpwr_track_delta_swingidx_5gb_p, - .delta_swingidx_5ga_n = _txpwr_track_delta_swingidx_5ga_n, - .delta_swingidx_5ga_p = _txpwr_track_delta_swingidx_5ga_p, - .delta_swingidx_2gb_n = _txpwr_track_delta_swingidx_2gb_n, - .delta_swingidx_2gb_p = _txpwr_track_delta_swingidx_2gb_p, - .delta_swingidx_2ga_n = _txpwr_track_delta_swingidx_2ga_n, - .delta_swingidx_2ga_p = _txpwr_track_delta_swingidx_2ga_p, - .delta_swingidx_2g_cck_b_n = _txpwr_track_delta_swingidx_2g_cck_b_n, - .delta_swingidx_2g_cck_b_p = _txpwr_track_delta_swingidx_2g_cck_b_p, - .delta_swingidx_2g_cck_a_n = _txpwr_track_delta_swingidx_2g_cck_a_n, - .delta_swingidx_2g_cck_a_p = _txpwr_track_delta_swingidx_2g_cck_a_p, +const struct rtw89_fw_txpwr_track_cfg rtw89_8852c_trk_cfg = { + .delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_N] = _txpwr_track_delta_swingidx_6gb_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_6GB_P] = _txpwr_track_delta_swingidx_6gb_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_N] = _txpwr_track_delta_swingidx_6ga_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_6GA_P] = _txpwr_track_delta_swingidx_6ga_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_N] = _txpwr_track_delta_swingidx_5gb_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GB_P] = _txpwr_track_delta_swingidx_5gb_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_N] = _txpwr_track_delta_swingidx_5ga_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_5GA_P] = _txpwr_track_delta_swingidx_5ga_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_N] = &_txpwr_track_delta_swingidx_2gb_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GB_P] = &_txpwr_track_delta_swingidx_2gb_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_N] = &_txpwr_track_delta_swingidx_2ga_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2GA_P] = &_txpwr_track_delta_swingidx_2ga_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_B_N] = &_txpwr_track_delta_swingidx_2g_cck_b_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_B_P] = &_txpwr_track_delta_swingidx_2g_cck_b_p, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_N] = &_txpwr_track_delta_swingidx_2g_cck_a_n, + .delta[RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_P] = &_txpwr_track_delta_swingidx_2g_cck_a_p, }; const struct rtw89_phy_tssi_dbw_table rtw89_8852c_tssi_dbw_table = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_table.h b/drivers/net/wireless/realtek/rtw89/rtw8852c_table.h index 7c9f3ecdc4e7..69c95f35b535 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c_table.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_table.h @@ -13,7 +13,7 @@ extern const struct rtw89_phy_table rtw89_8852c_phy_radioa_table; extern const struct rtw89_phy_table rtw89_8852c_phy_radiob_table; extern const struct rtw89_phy_table rtw89_8852c_phy_nctl_table; extern const struct rtw89_phy_tssi_dbw_table rtw89_8852c_tssi_dbw_table; -extern const struct rtw89_txpwr_track_cfg rtw89_8852c_trk_cfg; +extern const struct rtw89_fw_txpwr_track_cfg rtw89_8852c_trk_cfg; extern const struct rtw89_rfe_parms rtw89_8852c_dflt_parms; #endif From 56d32cdc6040440b08edfd5d7262250a721233f8 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 7 Jul 2026 17:10:56 +0800 Subject: [PATCH 062/403] wifi: rtw89: set needed firmware elements for early chips transition The early chips including RTL8852A, RTL8851B, RTL8852B and RTL8852C have driver built-in tables, which are not preferred. New firmware is prepared with corresponding tables in firmware elements, so we can start to transition. After this patch, old firmware is still usable, but add a prompt text for users to update firmware. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260707091056.42771-16-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 10 +++++++ drivers/net/wireless/realtek/rtw89/fw.h | 27 ++++++++++++++++--- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 3 ++- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 3 ++- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 3 ++- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 3 ++- 6 files changed, 41 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index b97c6e9c18bc..0e7168605850 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1562,6 +1562,7 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev) u32 unrecognized_elements = chip->needed_fw_elms; const struct rtw89_fw_element_handler *handler; const struct rtw89_fw_element_hdr *hdr; + bool transition; u32 elm_size; u32 elem_id; u32 offset; @@ -1569,6 +1570,9 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev) BUILD_BUG_ON(sizeof(chip->needed_fw_elms) * 8 < RTW89_FW_ELEMENT_ID_NUM); + transition = !!((chip->needed_fw_elms & BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION))); + unrecognized_elements &= ~BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION); + offset = rtw89_mfw_get_size(rtwdev); offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN); if (offset == 0) @@ -1608,6 +1612,12 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev) } if (unrecognized_elements) { + if (transition) { + rtw89_info(rtwdev, "NOTE: This firmware is going to be obsolete!\n" + "Please download the latest firmware from https://gitlab.com/kernel-firmware/linux-firmware.git\n"); + return 0; + } + rtw89_err(rtwdev, "Firmware elements 0x%08x are unrecognized\n", unrecognized_elements); return -ENOENT; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index a6f3b28b9e33..af126d15a1fb 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4354,31 +4354,50 @@ enum rtw89_fw_element_id { RTW89_FW_ELEMENT_ID_DIAG_MAC = 28, RTW89_FW_ELEMENT_ID_TX_COMP = 29, + __RTW89_FW_ELEMENT_ID_INTL_TRANSITION, RTW89_FW_ELEMENT_ID_NUM, }; +#define BITS_OF_RTW89_TXPWR_FW_ELEMENTS_TX_SHAPE \ + (BIT(RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT) | \ + BIT(RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT_RU)) + #define BITS_OF_RTW89_TXPWR_FW_ELEMENTS_NO_6GHZ \ (BIT(RTW89_FW_ELEMENT_ID_TXPWR_BYRATE) | \ BIT(RTW89_FW_ELEMENT_ID_TXPWR_LMT_2GHZ) | \ BIT(RTW89_FW_ELEMENT_ID_TXPWR_LMT_5GHZ) | \ BIT(RTW89_FW_ELEMENT_ID_TXPWR_LMT_RU_2GHZ) | \ BIT(RTW89_FW_ELEMENT_ID_TXPWR_LMT_RU_5GHZ) | \ - BIT(RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT) | \ - BIT(RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT_RU)) + BITS_OF_RTW89_TXPWR_FW_ELEMENTS_TX_SHAPE) #define BITS_OF_RTW89_TXPWR_FW_ELEMENTS \ (BITS_OF_RTW89_TXPWR_FW_ELEMENTS_NO_6GHZ | \ BIT(RTW89_FW_ELEMENT_ID_TXPWR_LMT_6GHZ) | \ BIT(RTW89_FW_ELEMENT_ID_TXPWR_LMT_RU_6GHZ)) -#define RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_NO_6GHZ \ +#define RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_BASE \ (BIT(RTW89_FW_ELEMENT_ID_BB_REG) | \ BIT(RTW89_FW_ELEMENT_ID_RADIO_A) | \ BIT(RTW89_FW_ELEMENT_ID_RADIO_B) | \ BIT(RTW89_FW_ELEMENT_ID_RF_NCTL) | \ - BIT(RTW89_FW_ELEMENT_ID_TXPWR_TRK) | \ + BIT(RTW89_FW_ELEMENT_ID_TXPWR_TRK)) + +#define RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_NO_6GHZ \ + (RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_BASE | \ BITS_OF_RTW89_TXPWR_FW_ELEMENTS_NO_6GHZ) +#define RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS \ + (RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_BASE | \ + BITS_OF_RTW89_TXPWR_FW_ELEMENTS) + +#define RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_RTL8852A \ + (RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_NO_6GHZ & \ + ~BITS_OF_RTW89_TXPWR_FW_ELEMENTS_TX_SHAPE) + +#define RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_RTL8851B \ + (RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_NO_6GHZ & \ + ~BIT(RTW89_FW_ELEMENT_ID_RADIO_B)) + #define RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS_BASE \ (BIT(RTW89_FW_ELEMENT_ID_BB_REG) | \ BIT(RTW89_FW_ELEMENT_ID_RADIO_A) | \ diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 95437ba9b675..50480f72c96d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2635,7 +2635,8 @@ const struct rtw89_chip_info rtw8851b_chip_info = { }, .try_ce_fw = true, .bbmcu_nr = 0, - .needed_fw_elms = 0, + .needed_fw_elms = RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_RTL8851B | + BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION), .fw_blacklist = NULL, .fifo_size = 196608, .small_fifo_size = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 78e07276fb2e..0c7794742650 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2373,7 +2373,8 @@ const struct rtw89_chip_info rtw8852a_chip_info = { }, .try_ce_fw = false, .bbmcu_nr = 0, - .needed_fw_elms = 0, + .needed_fw_elms = RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_RTL8852A | + BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION), .fw_blacklist = NULL, .fifo_size = 458752, .small_fifo_size = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index a8638024b54a..5904127cc836 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -968,7 +968,8 @@ const struct rtw89_chip_info rtw8852b_chip_info = { }, .try_ce_fw = true, .bbmcu_nr = 0, - .needed_fw_elms = 0, + .needed_fw_elms = RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_NO_6GHZ | + BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION), .fw_blacklist = &rtw89_fw_blacklist_default, .fifo_size = 196608, .small_fifo_size = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index a82373198356..968666280c89 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3172,7 +3172,8 @@ const struct rtw89_chip_info rtw8852c_chip_info = { }, .try_ce_fw = false, .bbmcu_nr = 0, - .needed_fw_elms = 0, + .needed_fw_elms = RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS | + BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION), .fw_blacklist = &rtw89_fw_blacklist_default, .fifo_size = 458752, .small_fifo_size = false, From 6c080026ecc17eecb103f8927c64ea73a74bb818 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Tue, 30 Jun 2026 03:31:17 +0000 Subject: [PATCH 063/403] wifi: rtl8xxxu: fix use-after-free from rx_urb_wq on stop rtl8xxxu arms rx_urb_wq from the RX completion path: rtl8xxxu_rx_complete() hands the URB to rtl8xxxu_queue_rx_urb(), which queues it on rx_urb_pending_list and, once the list grows past RTL8XXXU_RX_URB_PENDING_WATER, schedules rx_urb_wq. The worker rtl8xxxu_rx_urb_work() drains rx_urb_pending_list, recovers priv through container_of, and resubmits each URB through rtl8xxxu_submit_rx_urb(), which anchors it on rx_anchor and dereferences priv->udev. rtl8xxxu_stop() cancels the sibling work items (c2hcmd_work, ra_watchdog, update_beacon_work) but never cancels rx_urb_wq, so a worker armed during the last burst of RX traffic can run rtl8xxxu_rx_urb_work() after rtl8xxxu_disconnect() has called ieee80211_free_hw(), which frees priv, producing a use-after-free. The window opens under active RX traffic (pending count above the watermark) followed by a disconnect. There are two teardown races to close: * rtl8xxxu_queue_rx_urb() decided whether to enqueue under rx_urb_lock but called schedule_work() after dropping the lock. A completion that observed shutdown == false and released the lock could then call schedule_work() after rtl8xxxu_stop() had set shutdown and cancel_work_sync() had already returned, arming the worker to run after the teardown. Move schedule_work() under the same !shutdown branch so the arming decision is atomic with the shutdown check. * rtl8xxxu_rx_urb_work() anchors every URB it drained back onto rx_anchor through rtl8xxxu_submit_rx_urb(). A worker still running when usb_kill_anchored_urbs(&priv->rx_anchor) returned would submit a URB that escaped the kill. In rtl8xxxu_stop(), call cancel_work_sync(&priv->rx_urb_wq) before the kill so the worker is drained first. After priv->shutdown is set under rx_urb_lock, completions can no longer queue rx_urb_wq. cancel_work_sync() then drains the last queued or running worker, and the following usb_kill_anchored_urbs() kills the URBs it may have submitted. rtl8xxxu_disconnect() is covered because ieee80211_unregister_hw() guarantees .stop() runs for a live interface before ieee80211_free_hw() frees priv. The probe error path needs no cancel: rx_urb_wq is INIT_WORK()'d there but cannot have been scheduled, since no URB is submitted before ieee80211_register_hw() succeeds. This bug was found by static analysis. Fixes: 26f1fad29ad9 ("New driver: rtl8xxxu (mac80211)") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260630033117.3377-1-fanwu01@zju.edu.cn --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 4e8a4769603c..bddbd0990de7 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -5837,14 +5837,19 @@ static void rtl8xxxu_queue_rx_urb(struct rtl8xxxu_priv *priv, { struct sk_buff *skb; unsigned long flags; - int pending = 0; spin_lock_irqsave(&priv->rx_urb_lock, flags); if (!priv->shutdown) { list_add_tail(&rx_urb->list, &priv->rx_urb_pending_list); priv->rx_urb_pending_count++; - pending = priv->rx_urb_pending_count; + /* + * Arm the worker under rx_urb_lock so this is atomic with the + * shutdown check: moving it out of the lock would let a + * completion arm the work after rtl8xxxu_stop() canceled it. + */ + if (priv->rx_urb_pending_count > RTL8XXXU_RX_URB_PENDING_WATER) + schedule_work(&priv->rx_urb_wq); } else { skb = (struct sk_buff *)rx_urb->urb.context; dev_kfree_skb_irq(skb); @@ -5852,9 +5857,6 @@ static void rtl8xxxu_queue_rx_urb(struct rtl8xxxu_priv *priv, } spin_unlock_irqrestore(&priv->rx_urb_lock, flags); - - if (pending > RTL8XXXU_RX_URB_PENDING_WATER) - schedule_work(&priv->rx_urb_wq); } static void rtl8xxxu_rx_urb_work(struct work_struct *work) @@ -7506,6 +7508,13 @@ static void rtl8xxxu_stop(struct ieee80211_hw *hw, bool suspend) priv->shutdown = true; spin_unlock_irqrestore(&priv->rx_urb_lock, flags); + /* + * Cancel before killing rx_anchor: the worker re-anchors every URB + * it drained via rtl8xxxu_submit_rx_urb(), so a worker still running + * after the kill could submit a URB that escapes it. + */ + cancel_work_sync(&priv->rx_urb_wq); + usb_kill_anchored_urbs(&priv->rx_anchor); usb_kill_anchored_urbs(&priv->tx_anchor); if (priv->usb_interrupts) From 0ba1b366185b770440bf4eee3c4a929bc3cfc0e9 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Sat, 14 Feb 2026 11:05:09 -0800 Subject: [PATCH 064/403] wifi: iwlwifi: Clean dangling pointer in tx path If iwl_txq_gen2_build_tfd fails, we return -1, which will cause calling code to dispose of the skb one way or another. Remove any reference to that skb from the txq entries so that nothing will try to access it later. Signed-off-by: Ben Greear Link: https://patch.msgid.link/20260214190509.2098565-1-greearb@candelatech.com Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx-gen2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx-gen2.c index bda9f807321e..27f4973791d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx-gen2.c @@ -773,6 +773,8 @@ int iwl_txq_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, tfd = iwl_txq_gen2_build_tfd(trans, txq, dev_cmd, skb, out_meta); if (!tfd) { + txq->entries[idx].skb = NULL; + txq->entries[idx].cmd = NULL; spin_unlock(&txq->lock); return -1; } From d6061f5f1318a0fc116fa442bea299095d211ad4 Mon Sep 17 00:00:00 2001 From: Praveen Rajendran Date: Fri, 3 Jul 2026 19:37:57 +0530 Subject: [PATCH 065/403] wifi: iwlwifi: fw: Fix spelling typo in error-dump.h Correct a minor grammatical error inside the kernel-doc comments of error-dump.h where "configuration" was misspelled as "configuraiton". Signed-off-by: Praveen Rajendran Link: https://patch.msgid.link/20260703140757.3372-1-praveenrajendran2009@gmail.com Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/error-dump.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index 07f1240df866..692bb17dfc33 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -352,7 +352,7 @@ struct iwl_fw_ini_error_dump_register { * struct iwl_fw_ini_dump_cfg_name - configuration name * @image_type: image type the configuration is related to * @cfg_name_len: length of the configuration name - * @cfg_name: name of the configuraiton + * @cfg_name: name of the configuration */ struct iwl_fw_ini_dump_cfg_name { __le32 image_type; From 67105abd6195a685a84dcb8a5daf54a1f4bfdb60 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 24 Jun 2026 16:44:04 +0800 Subject: [PATCH 066/403] wifi: iwlwifi: dvm: fix memory leak in iwl_op_mode_dvm_start() In iwl_op_mode_dvm_start(), jumping to out_free_eeprom currently bypasses the out_free_eeprom_blob label. Consequently, error paths triggered after successfully parsing the EEPROM free priv->nvm_data but leak priv->eeprom_blob. Fix this memory leak by reordering the error handling labels so that out_free_eeprom falls through to out_free_eeprom_blob. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc6. An x86_64 allyesconfig build showed no new warnings. As we do not have supported Intel DVM wireless hardware and firmware to test with, no runtime testing was able to be performed. Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Link: https://patch.msgid.link/20260624084404.570703-1-dawei.feng@seu.edu.cn Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/dvm/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index ca5a8140908a..6bd5b6d84b2a 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -1511,10 +1511,10 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, priv->workqueue = NULL; out_uninit_drv: iwl_uninit_drv(priv); -out_free_eeprom_blob: - kfree(priv->eeprom_blob); out_free_eeprom: kfree(priv->nvm_data); +out_free_eeprom_blob: + kfree(priv->eeprom_blob); out_leave_trans: iwl_trans_op_mode_leave(priv->trans); out_free_hw: From 94280e6e0c69a5327e377803732f494d8b5a74cd Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 1 Apr 2026 11:05:55 +0800 Subject: [PATCH 067/403] iwlwifi: dvm: add missing cleaup for on error path In iwlagn_tx_agg_start(), call iwlagn_dealloc_agg_txq() to clear bit on error path. Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260401030555.541685-1-lihaoxiang@isrc.iscas.ac.cn Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/dvm/tx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c index a7806776a51e..1a0167f67c7b 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c @@ -604,8 +604,10 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, } ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); - if (ret) + if (ret) { + iwlagn_dealloc_agg_txq(priv, txq_id); return ret; + } spin_lock_bh(&priv->sta_lock); tid_data = &priv->tid_data[sta_id][tid]; From 7410e4548a17bd868dfc60b48f2e78eacde3ad78 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 15 Jul 2026 21:57:50 +0800 Subject: [PATCH 068/403] wifi: iwlwifi: validate PNVM SKU TLV length iwl_pnvm_parse() reads an iwl_sku_id from an IWL_UCODE_TLV_PNVM_SKU payload after only checking that the generic TLV payload is present. A short type-specific payload can therefore make the three data[] reads extend beyond the TLV. Reject SKU TLVs shorter than the structure before accessing it. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260715135916.24417-1-pengpeng@iscas.ac.cn Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index afff8d51ca95..6daa5cc8c20f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -198,6 +198,12 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, IWL_DEBUG_FW(trans, "Got IWL_UCODE_TLV_PNVM_SKU len %d\n", tlv_len); + if (tlv_len < sizeof(*tlv_sku_id)) { + IWL_ERR(trans, "invalid PNVM SKU TLV len: %u\n", + tlv_len); + return -EINVAL; + } + IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n", le32_to_cpu(tlv_sku_id->data[0]), le32_to_cpu(tlv_sku_id->data[1]), From f051995c539582b05276abda4c5874fd4cafd2a5 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 15 Jul 2026 21:57:50 +0800 Subject: [PATCH 069/403] wifi: iwlwifi: validate UEFI reduced-power SKU TLV length iwl_uefi_reduce_power_parse() reads an iwl_sku_id from an IWL_UCODE_TLV_PNVM_SKU payload after only checking that the generic TLV payload is present. A short type-specific payload can therefore make the three data[] reads extend beyond the TLV. Reject SKU TLVs shorter than the structure before accessing it. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260715135916.24417-2-pengpeng@iscas.ac.cn Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 2ef0a7a920ad..4cd36b42262c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -248,6 +248,12 @@ int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, IWL_DEBUG_FW(trans, "Got IWL_UCODE_TLV_PNVM_SKU len %d\n", tlv_len); + if (tlv_len < sizeof(*tlv_sku_id)) { + IWL_ERR(trans, "invalid PNVM SKU TLV len: %u\n", + tlv_len); + return -EINVAL; + } + IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n", le32_to_cpu(tlv_sku_id->data[0]), le32_to_cpu(tlv_sku_id->data[1]), From 478cad8bba59e8ee0e08d49edabf14bf1fbde5e4 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Jul 2026 17:02:04 +0300 Subject: [PATCH 070/403] wifi: iwlwifi: add a compile time check for too long hcmds A host command that is bigger than the allowed payload length should be sent with the NOCOPY flag. If it is sent without, we will get a warning. We do know at compile time what is the maximum size of a hcmd payload that the transport supports, so in order to catch bugs early, add a compile time check to iwl_*_send_cmd_pdu to catch that. Reviewed-by: Johannes Berg Link: https://patch.msgid.link/20260714165826.a549b9499e3e.Id1a95bbbf92b5862862becaf57419bb9fe1385e5@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/hcmd.h | 9 +++++++-- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 20 ++++++++++++++----- .../net/wireless/intel/iwlwifi/mvm/utils.c | 8 ++++---- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h b/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h index 64a8d4248324..19e1c9e509cf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifndef __iwl_mld_hcmd_h__ #define __iwl_mld_hcmd_h__ @@ -42,7 +42,12 @@ __iwl_mld_send_cmd_with_flags_pdu(struct iwl_mld *mld, u32 id, #define _iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len, \ ignored...) \ - __iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len) + ({ \ + BUILD_BUG_ON(__builtin_constant_p(len) && \ + (u16)(len) > IWL_MAX_CMD_PAYLOAD_SIZE); \ + __iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, \ + data, len); \ + }) #define iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len...) \ _iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, ##len, \ sizeof(*(data))) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 683cac56822c..1b56a2695bde 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1675,14 +1675,24 @@ u32 iwl_mvm_get_systime(struct iwl_mvm *mvm); /* Tx / Host Commands */ int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd); -int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, - u32 flags, u16 len, const void *data); +int _iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, + u32 flags, u16 len, const void *data); +#define iwl_mvm_send_cmd_pdu(mvm, id, flags, len, data) ({ \ + BUILD_BUG_ON(__builtin_constant_p(len) && \ + (u16)(len) > IWL_MAX_CMD_PAYLOAD_SIZE); \ + _iwl_mvm_send_cmd_pdu(mvm, id, flags, len, data); \ +}) int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, u32 *status); -int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, - u16 len, const void *data, - u32 *status); +int _iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, + u16 len, const void *data, + u32 *status); +#define iwl_mvm_send_cmd_pdu_status(mvm, id, len, data, status) ({ \ + BUILD_BUG_ON(__builtin_constant_p(len) && \ + (u16)(len) > IWL_MAX_CMD_PAYLOAD_SIZE); \ + _iwl_mvm_send_cmd_pdu_status(mvm, id, len, data, status); \ +}) int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_sta *sta); int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 2e12f93ad32b..849342c96ae7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -73,8 +73,8 @@ int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd) return ret; } -int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, - u32 flags, u16 len, const void *data) +int _iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, + u32 flags, u16 len, const void *data) { struct iwl_host_cmd cmd = { .id = id, @@ -137,8 +137,8 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, /* * We assume that the caller set the status to the sucess value */ -int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len, - const void *data, u32 *status) +int _iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len, + const void *data, u32 *status) { struct iwl_host_cmd cmd = { .id = id, From 1e749bd58e556f69a0251cfc8cd110b986f641f0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 14 Jul 2026 17:02:05 +0300 Subject: [PATCH 071/403] wifi: iwlwifi: mvm: remove iwl_mvm_recalc_tcm() This function is only called in the worker, so it doesn't need to exist at all, simply move the code there. Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20260714165826.dd9f49714128.I65fbe6890d67ec424d333c362aa7041a117aed44@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 - drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 16 +++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 1b56a2695bde..4420005ef69e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2412,7 +2412,6 @@ bool iwl_mvm_is_vif_assoc(struct iwl_mvm *mvm); #define MVM_TCM_PERIOD (HZ * MVM_TCM_PERIOD_MSEC / 1000) #define MVM_LL_PERIOD (10 * HZ) void iwl_mvm_tcm_work(struct work_struct *work); -void iwl_mvm_recalc_tcm(struct iwl_mvm *mvm); void iwl_mvm_pause_tcm(struct iwl_mvm *mvm, bool with_cancel); void iwl_mvm_resume_tcm(struct iwl_mvm *mvm); void iwl_mvm_tcm_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 849342c96ae7..7eae10982869 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -1055,7 +1055,7 @@ static unsigned long iwl_mvm_calc_tcm_stats(struct iwl_mvm *mvm, /* * If the current load isn't low we need to force re-evaluation * in the TCM period, so that we can return to low load if there - * was no traffic at all (and thus iwl_mvm_recalc_tcm didn't get + * was no traffic at all (and thus iwl_mvm_tcm_work() didn't get * triggered by traffic). */ if (load != IWL_MVM_TRAFFIC_LOW) @@ -1081,8 +1081,11 @@ static unsigned long iwl_mvm_calc_tcm_stats(struct iwl_mvm *mvm, return 0; } -void iwl_mvm_recalc_tcm(struct iwl_mvm *mvm) +void iwl_mvm_tcm_work(struct work_struct *work) { + struct delayed_work *delayed_work = to_delayed_work(work); + struct iwl_mvm *mvm = container_of(delayed_work, struct iwl_mvm, + tcm.work); unsigned long ts = jiffies; bool handle_uapsd = time_after(ts, mvm->tcm.uapsd_nonagg_ts + @@ -1119,15 +1122,6 @@ void iwl_mvm_recalc_tcm(struct iwl_mvm *mvm) iwl_mvm_tcm_results(mvm); } -void iwl_mvm_tcm_work(struct work_struct *work) -{ - struct delayed_work *delayed_work = to_delayed_work(work); - struct iwl_mvm *mvm = container_of(delayed_work, struct iwl_mvm, - tcm.work); - - iwl_mvm_recalc_tcm(mvm); -} - void iwl_mvm_pause_tcm(struct iwl_mvm *mvm, bool with_cancel) { spin_lock_bh(&mvm->tcm.lock); From 6800559a1042bfef9985b400f9dd971650d122f4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 14 Jul 2026 17:02:06 +0300 Subject: [PATCH 072/403] wifi: iwlwifi: claim UHR DBE capability for UHR devices When an Intel device supports UHR it also supports DBE (dynamic bandwidth extension) since that's handled in mac80211. Claim support for it for client mode. Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20260714165826.435828046f11.I538b2d90a4c282118ca2e56292cf5615d477a44c@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index d47b4ae2f486..761424812609 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -699,7 +699,8 @@ static const struct ieee80211_sband_iftype_data iwl_iftype_cap[] = { .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP | IEEE80211_UHR_MAC_CAP0_DPS_SUPP, - [1] = IEEE80211_UHR_MAC_CAP1_DUO_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DUO_SUPP | + IEEE80211_UHR_MAC_CAP1_DBE_SUPP, }, }, }, From 3bff0d12c36247073fca976498b58a940fca72dc Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Jul 2026 17:02:07 +0300 Subject: [PATCH 073/403] wifi: iwlwifi: support TTL platform device ID Add support for a new device ID that we will have on TTL (sc2). Reviewed-by: Johannes Berg Link: https://patch.msgid.link/20260714165826.46183446954f.Icc260831e530c1c92c9be615a7077768b1b9ae30@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 7a7b10152e5a..a3e6c9e09a3b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -548,6 +548,7 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0xD340, PCI_ANY_ID, iwl_sc_mac_cfg)}, {IWL_PCI_DEVICE(0x6E70, PCI_ANY_ID, iwl_sc_mac_cfg)}, {IWL_PCI_DEVICE(0xD240, PCI_ANY_ID, iwl_sc_mac_cfg)}, + {IWL_PCI_DEVICE(0x9327, PCI_ANY_ID, iwl_sc_mac_cfg)}, #endif /* CONFIG_IWLMVM || CONFIG_IWLMLD */ {0} From 9119aeeddc210cdfce0537de3ea3bab9316b2589 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 17:02:08 +0300 Subject: [PATCH 074/403] wifi: iwlwifi: mvm: fix the FCS truncation logic in d3 Fix a harmless mistake in the wake packet management code in the d3 wakeup flow. If the FCS is truncated, we want to detect it, but we cleared the icvlen before updating the truncated variable that holds the number of bytes having been truncated. Fix that. Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714165826.a7d094168ed3.I1a4d13f276c7e75514ab2032ae387873337470b8@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 9a74f60c9185..d7ceb385ae0b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1539,8 +1539,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, /* if truncated, FCS/ICV is (partially) gone */ if (truncated >= icvlen) { - icvlen = 0; truncated -= icvlen; + icvlen = 0; } else { icvlen -= truncated; truncated = 0; From 68f7d05494403ce89eba2b5476b2cd9ac03041db Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 17:02:09 +0300 Subject: [PATCH 075/403] wifi: iwlwifi: mld: treat valid BAID without STA as a FW error Somehow, the firmware sometimes seems to have a valid BAID even if the ieee80211_sta was not found. This happens in sniffer mode. Treat those as a firmware error. Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714165826.4902f73de145.I2cec7133f2a2ec8c39dcfb36938aba2ea3d6be24@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/agg.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c index e3627ad0321c..1aa30d2e8133 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -200,11 +200,11 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, struct iwl_mld_baid_data *baid_data; struct iwl_mld_reorder_buffer *buffer; struct iwl_mld_reorder_buf_entry *entries; - struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); struct iwl_mld_link_sta *mld_link_sta; u32 reorder = le32_to_cpu(desc->reorder_data); bool amsdu, last_subframe, is_old_sn, is_dup; u8 tid = ieee80211_get_tid(hdr); + struct iwl_mld_sta *mld_sta; u8 baid; u16 nssn, sn; u32 sta_mask = 0; @@ -223,10 +223,13 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, return IWL_MLD_PASS_SKB; /* no sta yet */ - if (WARN_ONCE(!sta, - "Got valid BAID without a valid station assigned\n")) + if (IWL_FW_CHECK(mld, !sta, + "Got valid BAID without a valid station assigned - %d\n", + baid)) return IWL_MLD_PASS_SKB; + mld_sta = iwl_mld_sta_from_mac80211(sta); + /* not a data packet */ if (!ieee80211_is_data_qos(hdr->frame_control) || is_multicast_ether_addr(hdr->addr1)) From 21b3263b90e8006d488894e8a71c8debf45f6b88 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 17:02:10 +0300 Subject: [PATCH 076/403] wifi: iwlwifi: mvm: validate monitor notif link_id MONITOR_NOTIF link_id is firmware-provided. Validate link_id range with IWL_FW_CHECK before vif lookup. Payload length is already checked by RX_HANDLER. Use iwl_mvm_rcu_dereference_vif_id which does all we need which allows us to drop iwl_mvm_get_vif_by_macid. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714165826.7601d05649a4.I237f58a007af761468057c9c09039953a3bb37da@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 - drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 4 +-- .../net/wireless/intel/iwlwifi/mvm/utils.c | 30 ------------------- 3 files changed, 2 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 4420005ef69e..979b39314981 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2405,7 +2405,6 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, bool sync, const void *data, u32 size); struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); -struct ieee80211_vif *iwl_mvm_get_vif_by_macid(struct iwl_mvm *mvm, u32 macid); bool iwl_mvm_is_vif_assoc(struct iwl_mvm *mvm); #define MVM_TCM_PERIOD_MSEC 500 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 2297392db955..f43895c2f3c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -155,8 +155,8 @@ static void iwl_mvm_rx_monitor_notif(struct iwl_mvm *mvm, if (notif->type != cpu_to_le32(IWL_DP_MON_NOTIF_TYPE_EXT_CCA)) return; - /* FIXME: should fetch the link and not the vif */ - vif = iwl_mvm_get_vif_by_macid(mvm, notif->link_id); + /* mac_id = link_id since we don't support MLO */ + vif = iwl_mvm_rcu_dereference_vif_id(mvm, notif->link_id, false); if (!vif || vif->type != NL80211_IFTYPE_STATION) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 7eae10982869..2fd4a9961145 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -689,36 +689,6 @@ struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm) return bss_iter_data.vif; } -struct iwl_bss_find_iter_data { - struct ieee80211_vif *vif; - u32 macid; -}; - -static void iwl_mvm_bss_find_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_bss_find_iter_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (mvmvif->id == data->macid) - data->vif = vif; -} - -struct ieee80211_vif *iwl_mvm_get_vif_by_macid(struct iwl_mvm *mvm, u32 macid) -{ - struct iwl_bss_find_iter_data data = { - .macid = macid, - }; - - lockdep_assert_held(&mvm->mutex); - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bss_find_iface_iterator, &data); - - return data.vif; -} - struct iwl_sta_iter_data { bool assoc; }; From c99bc4d4e0ba6f4661b2b914c96a597d26e7cc05 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Jul 2026 17:02:11 +0300 Subject: [PATCH 077/403] wifi: iwlwifi: mld: cancel wiphy work before freeing wiphy When we fail to load the fw during op-mode start, we purge the list of the async handlers but we don't cancel the work. Same when we stop the op-mode. Before freeing wiphy, we need to cancel/flush any pending wiphy work, otherwise the work will fire with a freed memory. cfg80211 will do it anyway, but it will warn. Cancel the work in those cases. Reviewed-by: Johannes Berg Link: https://patch.msgid.link/20260714165826.07aa49f755a2.I734a27b1b0eeb5b0e821aee3318fee8dc0a6bc03@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/mld.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 78c78cf891cd..5a6c9ab20fd6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -446,6 +446,8 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, } if (ret) { + /* wiphy memory is about to be freed, we should cancel any pending work */ + wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk); wiphy_unlock(mld->wiphy); rtnl_unlock(); goto err; @@ -511,6 +513,7 @@ iwl_op_mode_mld_stop(struct iwl_op_mode *op_mode) iwl_mld_thermal_exit(mld); wiphy_lock(mld->wiphy); + wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk); iwl_mld_low_latency_stop(mld); iwl_mld_deinit_time_sync(mld); wiphy_unlock(mld->wiphy); From 4e777dcdfe70d9b3ddf38cb49de9e30306889b18 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 17:02:12 +0300 Subject: [PATCH 078/403] wifi: iwlwifi: mld: validate D3_END notif size Check D3_END_NOTIFICATION payload length before reading notif->flags. On short payloads, mark notif handling as failed. Avoid out-of-bounds reads from malformed notifications. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714165826.db2df8b6b6bb.I6163bbdf433379bf1dbf9eb46fb9562892217bd7@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index 458a668ba916..b5fed6090340 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -1449,8 +1449,16 @@ static bool iwl_mld_handle_d3_notif(struct iwl_notif_wait_data *notif_wait, } case WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION): { struct iwl_d3_end_notif *notif = (void *)pkt->data; + u32 len = iwl_rx_packet_payload_len(pkt); + + if (IWL_FW_CHECK(mld, len < sizeof(*notif), + "Invalid D3_END notification (expected=%zu got=%u)\n", + sizeof(*notif), len)) { + resume_data->notif_handling_err = true; + } else { + resume_data->d3_end_flags = le32_to_cpu(notif->flags); + } - resume_data->d3_end_flags = le32_to_cpu(notif->flags); resume_data->notifs_received |= IWL_D3_NOTIF_D3_END_NOTIF; break; } From 5826799a26fd1c7dd954f6386a49e9fad747f21f Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 17:02:13 +0300 Subject: [PATCH 079/403] wifi: iwlwifi: pcie: validate txq_id in txq_enable Avoid indexing txq arrays and queue-used bitmaps with an invalid queue ID by adding an early bounds check in iwl_trans_pcie_txq_enable(). Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714165826.273be072f5ff.I0d6c36a4c06bdbb4655164c7792da32b6143731e@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx.c index fdb3ba4f63c0..dc2fe0c4f68c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/tx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2003-2014, 2018-2021, 2023-2025 Intel Corporation + * Copyright (C) 2003-2014, 2018-2021, 2023-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1149,10 +1149,17 @@ bool iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, unsigned int wdg_timeout) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = trans_pcie->txqs.txq[txq_id]; + struct iwl_txq *txq; int fifo = -1; bool scd_bug = false; + if (WARN_ONCE(txq_id < 0 || + txq_id >= trans->mac_cfg->base->num_of_queues, + "queue %d out of range", txq_id)) + return false; + + txq = trans_pcie->txqs.txq[txq_id]; + if (test_and_set_bit(txq_id, trans_pcie->txqs.queue_used)) WARN_ONCE(1, "queue %d already used - expect issues", txq_id); From 678148622fa8e5a118784fa3fcb9bd1876b8322a Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Tue, 14 Jul 2026 17:02:14 +0300 Subject: [PATCH 080/403] wifi: iwlwifi: ignore raw-DSM TLV for LARI cmd version 13 and above LARI_CONFIG_CHANGE command version 13 and above accepts raw DSM values by default in firmware, so FW_ACCEPTS_RAW_DSM_TABLE should not gate DSM bitmap handling for these versions. Set has_raw_dsm_capa based on command version (version 13 and above is true) with TLV fallback for older command versions. Also update TLV kernel-doc to mark this capability obsolete for LARI command version 13 and above. Signed-off-by: Pagadala Yesu Anjaneyulu Reviewed-by: Avinash Bhatt Link: https://patch.msgid.link/20260714165826.12c8b407e115.I6809041f1eb52b7fafe9172ca3e47323d43cc30a@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/file.h | 5 +++-- .../net/wireless/intel/iwlwifi/mld/regulatory.c | 16 +++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index a26ed82a8106..664ac25754e9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -448,8 +448,9 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_EXT_FSEQ_IMAGE_SUPPORT: external FSEQ image support * @IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT: FW reset handshake is needed * during assert handling even if the dump isn't split - * @IWL_UCODE_TLV_CAPA_FW_ACCEPTS_RAW_DSM_TABLE: Firmware has capability of - * handling raw DSM table data. + * @IWL_UCODE_TLV_CAPA_FW_ACCEPTS_RAW_DSM_TABLE: Firmware can handle raw DSM + * table data. For LARI_CONFIG_CHANGE command version 13 and above, this + * capability is obsolete since raw DSM values are accepted by default. * @IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT: Supports NAN synchronization * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c index 659243ada86c..858635607f5d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -325,8 +325,16 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) struct iwl_lari_config_change_cmd cmd = { .config_bitmap = iwl_mld_get_lari_config_bitmap(fwrt), }; - bool has_raw_dsm_capa = fw_has_capa(&fwrt->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_FW_ACCEPTS_RAW_DSM_TABLE); + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(REGULATORY_AND_NVM_GROUP, + LARI_CONFIG_CHANGE), 12); + /* + * For LARI_CONFIG_CHANGE command version 13 and above, firmware accepts + * raw DSM values by default and this TLV is no longer needed. + */ + bool has_raw_dsm_capa = cmd_ver >= 13 || + fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_FW_ACCEPTS_RAW_DSM_TABLE); int ret; u32 value; @@ -428,9 +436,7 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) "sending LARI_CONFIG_CHANGE, oem_unii9_enable=0x%x\n", le32_to_cpu(cmd.oem_unii9_enable)); - if (iwl_fw_lookup_cmd_ver(mld->fw, - WIDE_ID(REGULATORY_AND_NVM_GROUP, - LARI_CONFIG_CHANGE), 12) == 12) { + if (cmd_ver == 12) { int cmd_size = offsetof(typeof(cmd), oem_11bn_allow_bitmap); ret = iwl_mld_send_cmd_pdu(mld, From 974cbad4c86d6442d9e08710322b924e4ae79023 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Tue, 14 Jul 2026 17:02:15 +0300 Subject: [PATCH 081/403] wifi: iwlwifi: regulatory: add LARI_CONFIG_CHANGE command v14 support Add support for LARI_CONFIG_CHANGE version 14 and populate the newly added BIOS-related fields in the command payload. Extend the version 14 command layout with UHB extension, puncturing and WBEM metadata fields, update command-size handling for version negotiation, and wire the new data into the LARI configuration flow. Track WBEM and puncturing source/revision in fw runtime, set them when loading ACPI or UEFI tables, and pass the headers to firmware. Update send conditions, debug traces, and related documentation/comments to match the new format. Signed-off-by: Pagadala Yesu Anjaneyulu Link: https://patch.msgid.link/20260714165826.c73d2fbebbfe.I93af7c456f04ef10d03646a43aaeb1858ecdc36d@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 4 +- .../wireless/intel/iwlwifi/fw/api/nvm-reg.h | 15 ++++- .../net/wireless/intel/iwlwifi/fw/runtime.h | 13 +++- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 20 ++++--- drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 4 +- drivers/net/wireless/intel/iwlwifi/mld/mcc.c | 2 +- drivers/net/wireless/intel/iwlwifi/mld/mld.c | 2 +- drivers/net/wireless/intel/iwlwifi/mld/mld.h | 2 - .../wireless/intel/iwlwifi/mld/regulatory.c | 60 ++++++++++++++----- 9 files changed, 88 insertions(+), 34 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index bf0f851a9075..9f2f4a6af1ca 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2019-2025 Intel Corporation + * Copyright (C) 2019-2026 Intel Corporation */ #include #include "iwl-drv.h" @@ -1188,6 +1188,8 @@ int iwl_acpi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value) *value = wifi_pkg->package.elements[1].integer.value & IWL_ACPI_WBEM_REV0_MASK; + fwrt->wbem_source = BIOS_SOURCE_ACPI; + fwrt->wbem_revision = tbl_rev; IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from ACPI\n"); ret = 0; out_free: diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index 443a9a416325..d8ec9934a9b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -662,6 +662,12 @@ struct iwl_lari_config_change_cmd_v8 { * get the data from the BIOS. * @oem_unii9_enable: UNII-9 enablement as read from the BIOS * @bios_hdr: bios config header + * @oem_uhb_allow_extension_bitmap: DSM Function 4 data as an extension of UHB + * enabled MCC sets + * @bios_wcpe_hdr: puncturing config header + * @wcpe_bitmap: bitmap of puncturing enablement per MCC + * @bios_wbem_hdr: 320 MHz per-MCC WBEM config header + * @reserved: reserved */ struct iwl_lari_config_change_cmd { __le32 config_bitmap; @@ -679,9 +685,16 @@ struct iwl_lari_config_change_cmd { __le32 oem_unii9_enable; /* since version 13 */ struct iwl_bios_config_hdr bios_hdr; + /* All the below are since version 14 */ + __le32 oem_uhb_allow_extension_bitmap; + struct iwl_bios_config_hdr bios_wcpe_hdr; + __le32 wcpe_bitmap; + struct iwl_bios_config_hdr bios_wbem_hdr; + __le32 reserved[10]; } __packed; /* LARI_CHANGE_CONF_CMD_S_VER_12 * LARI_CHANGE_CONF_CMD_S_VER_13 + * LARI_CHANGE_CONF_CMD_S_VER_14 */ /* Activate UNII-1 (5.2GHz) for World Wide */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index d80ae610e56c..ac01ac0092ee 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #ifndef __iwl_fw_runtime_h__ #define __iwl_fw_runtime_h__ @@ -144,6 +144,11 @@ struct iwl_txf_iter_data { * @tpc_enabled: TPC enabled * @dsm_source: one of &enum bios_source. UEFI, ACPI or NONE * @dsm_revision: the revision of the DSM table + * @wbem_source: one of &enum bios_source for the WBEM table + * @wbem_revision: the revision of the WBEM table + * @puncturing_source: one of &enum bios_source for the puncturing table + * @puncturing_revision: the revision of the puncturing table + * @bios_puncturing: per-country puncturing enablement bitmap from BIOS */ struct iwl_fw_runtime { struct iwl_trans *trans; @@ -226,6 +231,12 @@ struct iwl_fw_runtime { u32 dsm_funcs_valid; u32 dsm_values[DSM_FUNC_NUM_FUNCS]; #endif + + enum bios_source wbem_source; + u8 wbem_revision; + enum bios_source puncturing_source; + u8 puncturing_revision; + u32 bios_puncturing; }; void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 4cd36b42262c..01e495e24ebc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2021-2025 Intel Corporation + * Copyright(c) 2021-2026 Intel Corporation */ #include "iwl-drv.h" @@ -890,6 +890,8 @@ int iwl_uefi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value) goto out; } *value = data->wbem_320mhz_per_mcc & IWL_UEFI_WBEM_REV0_MASK; + fwrt->wbem_source = BIOS_SOURCE_UEFI; + fwrt->wbem_revision = data->revision; IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from UEFI\n"); out: kfree(data); @@ -976,29 +978,29 @@ int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt) { struct uefi_cnv_var_puncturing_data *data; - /* default value is not enabled if there is any issue in reading - * uefi variable or revision is not supported - */ - int puncturing = 0; + int ret = 0; data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PUNCTURING_NAME, "UefiCnvWlanPuncturing", sizeof(*data), NULL); if (IS_ERR(data)) - return puncturing; + return -EINVAL; if (data->revision != IWL_UEFI_PUNCTURING_REVISION) { IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PUNCTURING rev:%d\n", data->revision); + ret = -EINVAL; } else { - puncturing = data->puncturing & IWL_UEFI_PUNCTURING_REV0_MASK; + fwrt->puncturing_source = BIOS_SOURCE_UEFI; + fwrt->puncturing_revision = data->revision; + fwrt->bios_puncturing = data->puncturing; IWL_DEBUG_RADIO(fwrt, "Loaded puncturing bits from UEFI: %d\n", - puncturing); + fwrt->bios_puncturing); } kfree(data); - return puncturing; + return ret; } IWL_EXPORT_SYMBOL(iwl_uefi_get_puncturing); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 474f06db4d43..386ca3744408 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright(c) 2021-2025 Intel Corporation + * Copyright(c) 2021-2026 Intel Corporation */ #ifndef __iwl_fw_uefi__ #define __iwl_fw_uefi__ @@ -275,8 +275,6 @@ enum iwl_uefi_cnv_puncturing_flags { IWL_UEFI_CNV_PUNCTURING_CANADA_EN_MSK = BIT(1), }; -#define IWL_UEFI_PUNCTURING_REV0_MASK (IWL_UEFI_CNV_PUNCTURING_USA_EN_MSK | \ - IWL_UEFI_CNV_PUNCTURING_CANADA_EN_MSK) /** * struct uefi_cnv_var_puncturing_data - controlling channel * puncturing for few countries. diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c index 8502129abe49..7649ae794dfd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c @@ -131,7 +131,7 @@ iwl_mld_get_regdomain(struct iwl_mld *mld, /* FM follows BIOS/MCC policy, WH disallows puncturing only in US/CA. */ if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_FM) { - if (!iwl_puncturing_is_allowed_in_bios(mld->bios_enable_puncturing, + if (!iwl_puncturing_is_allowed_in_bios(mld->fwrt.bios_puncturing, le16_to_cpu(resp->mcc))) ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING); else diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 5a6c9ab20fd6..99506f354bfa 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -419,7 +419,7 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, iwl_mld_get_bios_tables(mld); iwl_uefi_get_sgom_table(trans, &mld->fwrt); - mld->bios_enable_puncturing = iwl_uefi_get_puncturing(&mld->fwrt); + iwl_uefi_get_puncturing(&mld->fwrt); iwl_mld_hw_set_regulatory(mld); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h index 922aa3dbff54..37f41b504b54 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -172,7 +172,6 @@ * @debug_max_sleep: maximum sleep time in D3 (for debug purposes) * @led: the led device * @mcc_src: the source id of the MCC, comes from the firmware - * @bios_enable_puncturing: is puncturing enabled by bios * @fw_id_to_ba: maps a fw (BA) id to a corresponding Block Ack session data. * @num_rx_ba_sessions: tracks the number of active Rx Block Ack (BA) sessions. * the driver ensures that new BA sessions are blocked once the maximum @@ -279,7 +278,6 @@ struct iwl_mld { struct led_classdev led; #endif enum iwl_mcc_source mcc_src; - bool bios_enable_puncturing; struct iwl_mld_baid_data __rcu *fw_id_to_ba[IWL_MAX_BAID]; u8 num_rx_ba_sessions; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c index 858635607f5d..4a93ccbef495 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -13,6 +13,23 @@ #include "mld.h" #include "hcmd.h" +static ssize_t iwl_mld_get_lari_config_cmd_size(u8 cmd_ver) +{ + switch (cmd_ver) { + case 14: + return sizeof(struct iwl_lari_config_change_cmd); + case 13: + return offsetof(struct iwl_lari_config_change_cmd, + oem_uhb_allow_extension_bitmap); + case 12: + return offsetof(struct iwl_lari_config_change_cmd, + oem_11bn_allow_bitmap); + default: + WARN(true, "unsupported version: %d", cmd_ver); + return -EINVAL; + } +} + void iwl_mld_get_bios_tables(struct iwl_mld *mld) { int ret; @@ -335,9 +352,13 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) bool has_raw_dsm_capa = cmd_ver >= 13 || fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_FW_ACCEPTS_RAW_DSM_TABLE); + ssize_t cmd_len = iwl_mld_get_lari_config_cmd_size(cmd_ver); int ret; u32 value; + if (cmd_len < 0) + return; + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_11AX_ENABLEMENT, &value); if (!ret) { if (!has_raw_dsm_capa) @@ -379,8 +400,11 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) } ret = iwl_bios_get_wbem(fwrt, &value); - if (!ret) + if (!ret) { cmd.oem_320mhz_allow_bitmap = cpu_to_le32(value); + cmd.bios_wbem_hdr.table_source = fwrt->wbem_source; + cmd.bios_wbem_hdr.table_revision = fwrt->wbem_revision; + } ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_11BE, &value); if (!ret) @@ -394,8 +418,17 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) if (!ret) cmd.oem_unii9_enable = cpu_to_le32(value); + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_REGULATORY_CONFIG, &value); + if (!ret) + cmd.oem_uhb_allow_extension_bitmap = cpu_to_le32(value); + + cmd.bios_wcpe_hdr.table_source = fwrt->puncturing_source; + cmd.bios_wcpe_hdr.table_revision = fwrt->puncturing_revision; + cmd.wcpe_bitmap = cpu_to_le32(fwrt->bios_puncturing); + if (!cmd.config_bitmap && !cmd.oem_uhb_allow_bitmap && + !cmd.oem_uhb_allow_extension_bitmap && !cmd.oem_11ax_allow_bitmap && !cmd.oem_unii4_allow_bitmap && !cmd.chan_state_active_bitmap && @@ -404,7 +437,8 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) !cmd.oem_320mhz_allow_bitmap && !cmd.oem_11be_allow_bitmap && !cmd.oem_11bn_allow_bitmap && - !cmd.oem_unii9_enable) + !cmd.oem_unii9_enable && + !cmd.wcpe_bitmap) return; cmd.bios_hdr.table_source = fwrt->dsm_source; @@ -422,6 +456,9 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) "sending LARI_CONFIG_CHANGE, oem_uhb_allow_bitmap=0x%x, force_disable_channels_bitmap=0x%x\n", le32_to_cpu(cmd.oem_uhb_allow_bitmap), le32_to_cpu(cmd.force_disable_channels_bitmap)); + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, oem_uhb_allow_extension_bitmap=0x%x\n", + le32_to_cpu(cmd.oem_uhb_allow_extension_bitmap)); IWL_DEBUG_RADIO(mld, "sending LARI_CONFIG_CHANGE, edt_bitmap=0x%x, oem_320mhz_allow_bitmap=0x%x\n", le32_to_cpu(cmd.edt_bitmap), @@ -435,20 +472,13 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) IWL_DEBUG_RADIO(mld, "sending LARI_CONFIG_CHANGE, oem_unii9_enable=0x%x\n", le32_to_cpu(cmd.oem_unii9_enable)); + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, wcpe_bitmap=0x%x\n", + le32_to_cpu(cmd.wcpe_bitmap)); - if (cmd_ver == 12) { - int cmd_size = offsetof(typeof(cmd), oem_11bn_allow_bitmap); - - ret = iwl_mld_send_cmd_pdu(mld, - WIDE_ID(REGULATORY_AND_NVM_GROUP, - LARI_CONFIG_CHANGE), - &cmd, cmd_size); - } else { - ret = iwl_mld_send_cmd_pdu(mld, - WIDE_ID(REGULATORY_AND_NVM_GROUP, - LARI_CONFIG_CHANGE), - &cmd); - } + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(REGULATORY_AND_NVM_GROUP, + LARI_CONFIG_CHANGE), &cmd, cmd_len); if (ret) IWL_DEBUG_RADIO(mld, "Failed to send LARI_CONFIG_CHANGE (%d)\n", From a51cc8131214870af92abbceab64877b27e690c2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 17:02:16 +0300 Subject: [PATCH 082/403] wifi: iwlwifi: mvm: reset the smart fifo state upon FW stop The smart fifo is a feature in the firmware configured by the driver. The driver keeps a state to remember what was the last configuration sent to the firmware. Obviously, if the firmware stops, we need to reconfigure the smart fifo. Since we didn't reset that state upon firmware stop, we thought the firmware is already properly configured and we didn't send the smart fifo configuration command as part of the init sequence. Reset the smart fifo state in iwl_mvm_stop_device() so that we will properly send the command during the init that will come later. Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714165826.7d3c5efe2d1a.I16b23c328a677257257f695fa6f439e41fbcd081@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index f43895c2f3c6..09150feb330d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1535,6 +1535,7 @@ void iwl_mvm_stop_device(struct iwl_mvm *mvm) iwl_fw_cancel_timestamp(&mvm->fwrt); clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status); + mvm->sf_state = SF_UNINIT; iwl_mvm_pause_tcm(mvm, false); From 0a00c3ec2db7674483dece4863f105b18a966202 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 17:02:17 +0300 Subject: [PATCH 083/403] wifi: iwlwifi: mvm: cleanup the driver state after device_powered_off If the device was powered off during suspend, we need to reload the firmware in resume which also means that we need to reconfigure it. It could be tempting to just set IWL_MVM_STATUS_HW_RESTART_REQUESTED but that would leave IWL_MVM_STATUS_IN_HW_RESTART set forever since that recovery is not managed by mac80211. Just call iwl_mvm_restart_cleanup() from device_powered_off(). Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714165826.dc33533ac962.I6d7cca9c4e5643a477eae1d0a4f5fc83a10d0ee7@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 74bd4038fd56..fd2a50563ab5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1129,7 +1129,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, RCU_INIT_POINTER(mvmvif->deflink.probe_resp_data, NULL); } -static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) +void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) { iwl_mvm_stop_device(mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 979b39314981..d6bed2c08606 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1660,6 +1660,7 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, u8 iwl_mvm_rate_idx_to_fw_idx(const struct iwl_fw *fw, int rate_idx); u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac); bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm); static inline void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 09150feb330d..6ae9f87d5221 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -2091,7 +2091,7 @@ static void iwl_op_mode_mvm_device_powered_off(struct iwl_op_mode *op_mode) mutex_lock(&mvm->mutex); clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status); - iwl_mvm_stop_device(mvm); + iwl_mvm_restart_cleanup(mvm); mvm->fast_resume = false; mutex_unlock(&mvm->mutex); } From 71ac392d8b5c23c45c66f30eff1c319580ada4da Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 17:02:18 +0300 Subject: [PATCH 084/403] wifi: iwlwifi: mld: reset the driver state upon firmware recovery Just like we did in iwlmvm, we also need to clear the mld state when the firmware was killed because of the device being powered off during suspend. Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714165826.ff1c9e05e0a9.I8df8d4a0384065fd2a32cf258339be77084c55bd@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 3 +-- drivers/net/wireless/intel/iwlwifi/mld/mac80211.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mld/mld.c | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 17286b3341c0..4065d8e4fa8c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -543,8 +543,7 @@ iwl_mld_mac80211_tx(struct ieee80211_hw *hw, iwl_mld_tx_skb(mld, skb, NULL); } -static void -iwl_mld_restart_cleanup(struct iwl_mld *mld) +void iwl_mld_restart_cleanup(struct iwl_mld *mld) { iwl_cleanup_mld(mld); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h index aad04d7b2617..f3302997b28f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2026 Intel Corporation */ #ifndef __iwl_mld_mac80211_h__ #define __iwl_mld_mac80211_h__ @@ -9,5 +10,6 @@ int iwl_mld_register_hw(struct iwl_mld *mld); void iwl_mld_recalc_multicast_filter(struct iwl_mld *mld); +void iwl_mld_restart_cleanup(struct iwl_mld *mld); #endif /* __iwl_mld_mac80211_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 99506f354bfa..093bdc130704 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -748,6 +748,7 @@ static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode) wiphy_lock(mld->wiphy); iwl_mld_stop_fw(mld); + iwl_mld_restart_cleanup(mld); mld->fw_status.in_d3 = false; wiphy_unlock(mld->wiphy); } From ef704fc32ae1a519801ac4a06aa290fece5f403c Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 15 Jul 2026 22:04:17 +0300 Subject: [PATCH 085/403] wifi: iwlwifi: mld: support aborting an ongoing ftm request Add support for aborting an ongoing FTM request. Signed-off-by: Avraham Stern Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.340040bdc6a3.Ifbbf70021e42bf1d59db6ec45a73f1806a1c2289@changeid --- .../intel/iwlwifi/mld/ftm-initiator.c | 21 +++++++++++++++++++ .../intel/iwlwifi/mld/ftm-initiator.h | 1 + .../net/wireless/intel/iwlwifi/mld/mac80211.c | 10 +++++++++ 3 files changed, 32 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c index 81df3fdfcbf5..a4b8037b5c76 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c @@ -459,3 +459,24 @@ void iwl_mld_ftm_restart_cleanup(struct iwl_mld *mld) mld->ftm_initiator.req, GFP_KERNEL); iwl_mld_ftm_reset(mld); } + +void iwl_mld_ftm_abort(struct iwl_mld *mld, struct cfg80211_pmsr_request *req) +{ + struct iwl_tof_range_abort_cmd cmd = { + .request_id = req->cookie, + }; + + lockdep_assert_wiphy(mld->wiphy); + + if (req != mld->ftm_initiator.req) + return; + + if (iwl_mld_send_cmd_pdu(mld, WIDE_ID(LOCATION_GROUP, + TOF_RANGE_ABORT_CMD), + &cmd)) + IWL_ERR(mld, "failed to abort FTM process\n"); + + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_FTM_REQ, + mld->ftm_initiator.req->cookie); + iwl_mld_ftm_reset(mld); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h index 3fab25a52508..e82237b31467 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h @@ -25,5 +25,6 @@ int iwl_mld_ftm_start(struct iwl_mld *mld, struct ieee80211_vif *vif, void iwl_mld_handle_ftm_resp_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); void iwl_mld_ftm_restart_cleanup(struct iwl_mld *mld); +void iwl_mld_ftm_abort(struct iwl_mld *mld, struct cfg80211_pmsr_request *req); #endif /* __iwl_mld_ftm_initiator_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 4065d8e4fa8c..17922ed3800d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -2852,6 +2852,15 @@ static int iwl_mld_start_pmsr(struct ieee80211_hw *hw, return iwl_mld_ftm_start(mld, vif, request); } +static void iwl_mld_abort_pmsr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_pmsr_request *request) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + iwl_mld_ftm_abort(mld, request); +} + static enum ieee80211_neg_ttlm_res iwl_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_neg_ttlm *neg_ttlm) @@ -2973,6 +2982,7 @@ const struct ieee80211_ops iwl_mld_hw_ops = { .prep_add_interface = iwl_mld_prep_add_interface, .set_hw_timestamp = iwl_mld_set_hw_timestamp, .start_pmsr = iwl_mld_start_pmsr, + .abort_pmsr = iwl_mld_abort_pmsr, .can_neg_ttlm = iwl_mld_can_neg_ttlm, .start_nan = iwl_mld_start_nan, .stop_nan = iwl_mld_stop_nan, From 7e16dad5d47e29338db9effbeb26b4e8bcfbc2c0 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 22:04:18 +0300 Subject: [PATCH 086/403] wifi: iwlwifi: mld: validate WoWLAN notif header Validate fixed wowlan_info_notif header size first. Only then read num_mlo_link_keys from pkt->data. Apply this to v5 and v6 parsing paths. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.9c33c20194ad.I691d019927cc56898f2516fcf5795c8b1fae362c@changeid --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 57 ++++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index b5fed6090340..c9c0e3c729c9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -573,18 +573,42 @@ iwl_mld_convert_wowlan_notif_v5(const struct iwl_wowlan_info_notif_v5 *notif_v5, } } -static bool iwl_mld_validate_wowlan_notif_size(struct iwl_mld *mld, - u32 len, - u32 expected_len, - u8 num_mlo_keys, +static bool iwl_mld_validate_wowlan_notif_size(struct iwl_mld *mld, u32 len, + const void *notif_data, int version) { u32 len_with_mlo_keys; + u32 expected_len; + u8 num_mlo_keys; - if (IWL_FW_CHECK(mld, len < expected_len, - "Invalid wowlan_info_notif v%d (expected=%u got=%u)\n", - version, expected_len, len)) + /* Extract num_mlo_keys from the void pointer based on version */ + if (version == 5) { + const struct iwl_wowlan_info_notif_v5 *notif_v5 = notif_data; + + expected_len = sizeof(*notif_v5); + + if (IWL_FW_CHECK(mld, len < expected_len, + "Invalid wowlan_info_notif v5 (expected=%u got=%u)\n", + expected_len, len)) + return false; + + num_mlo_keys = notif_v5->num_mlo_link_keys; + } else if (version == 6) { + const struct iwl_wowlan_info_notif *notif = notif_data; + + expected_len = sizeof(*notif); + + if (IWL_FW_CHECK(mld, len < expected_len, + "Invalid wowlan_info_notif v6 (expected=%u got=%u)\n", + expected_len, len)) + return false; + + num_mlo_keys = notif->num_mlo_link_keys; + } else { + IWL_WARN(mld, "Unsupported wowlan_info_notif version %d\n", + version); return false; + } len_with_mlo_keys = expected_len + (num_mlo_keys * sizeof(struct iwl_wowlan_mlo_gtk)); @@ -616,16 +640,14 @@ iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld, if (wowlan_info_ver == 5) { /* v5 format - validate before conversion */ - const struct iwl_wowlan_info_notif_v5 *notif_v5 = (void *)pkt->data; + const struct iwl_wowlan_info_notif_v5 *_notif = + (void *)pkt->data; - if (!iwl_mld_validate_wowlan_notif_size(mld, len, - sizeof(*notif_v5), - notif_v5->num_mlo_link_keys, - 5)) + if (!iwl_mld_validate_wowlan_notif_size(mld, len, _notif, 5)) return true; converted_notif = kzalloc_flex(*converted_notif, mlo_gtks, - notif_v5->num_mlo_link_keys, + _notif->num_mlo_link_keys, GFP_ATOMIC); if (!converted_notif) { IWL_ERR(mld, @@ -633,15 +655,12 @@ iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld, return true; } - iwl_mld_convert_wowlan_notif_v5(notif_v5, - converted_notif); + iwl_mld_convert_wowlan_notif_v5(_notif, converted_notif); notif = converted_notif; } else if (wowlan_info_ver == 6) { notif = (void *)pkt->data; - if (!iwl_mld_validate_wowlan_notif_size(mld, len, - sizeof(*notif), - notif->num_mlo_link_keys, - 6)) + + if (!iwl_mld_validate_wowlan_notif_size(mld, len, notif, 6)) return true; } else { /* smaller versions are not supported */ From bbe2d2fa8780a04dac8de0ecaa3895d3f3bc093e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Jul 2026 22:04:19 +0300 Subject: [PATCH 087/403] wifi: iwlwifi: mvm/mld: fix PPE threshold debug print loop The loop should print all bandwidths, the extra * results in calculating the wrong ARRAY_SIZE() here (of the array inside the per-bandwidth, not the per-bandwidth array.) Fix that, and also clarify the array variable assignment. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.5e1d3448bfca.I6919627382d46605b41b0c6a6f34deedfd45523c@changeid --- drivers/net/wireless/intel/iwlwifi/mld/sta.c | 5 ++--- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c index e18d86f021dc..7957ac11b0cd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -355,10 +355,9 @@ static void iwl_mld_fill_pkt_ext(struct iwl_mld *mld, for (int i = 0; i < MAX_HE_SUPP_NSS; i++) { for (int bw = 0; - bw < ARRAY_SIZE(*pkt_ext->pkt_ext_qam_th[i]); + bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); bw++) { - u8 *qam_th = - &pkt_ext->pkt_ext_qam_th[i][bw][0]; + u8 *qam_th = pkt_ext->pkt_ext_qam_th[i][bw]; IWL_DEBUG_HT(mld, "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n", diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index fd2a50563ab5..14aec0bfd173 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2352,10 +2352,9 @@ int iwl_mvm_set_sta_pkt_ext(struct iwl_mvm *mvm, int bw; for (bw = 0; - bw < ARRAY_SIZE(*pkt_ext->pkt_ext_qam_th[i]); + bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); bw++) { - u8 *qam_th = - &pkt_ext->pkt_ext_qam_th[i][bw][0]; + u8 *qam_th = pkt_ext->pkt_ext_qam_th[i][bw]; IWL_DEBUG_HT(mvm, "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n", From 71e67b4b59337b2f9f4fef976a27de2dad7aabf2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 22:04:20 +0300 Subject: [PATCH 088/403] wifi: iwlwifi: fix counter type in iwl_fwrt_dump_error_logs The loop counter 'count' was declared as u8 while num_pc is u32. If firmware advertises more than 255 PC entries the counter wraps back to zero and the loop never terminates potentially causing an infinite loop or reading past the allocated pc_data array. Change the declaration to u32 to match num_pc. Fixes: 2b69d242e29b ("wifi: iwlwifi: fw: print PC register value instead of address") Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.a61c65f34e87.Ie5f1a7ca43e0cc5a0ddc8305b0448ddffc09cd18@changeid --- drivers/net/wireless/intel/iwlwifi/fw/dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c index c2af66899a78..bbbf3669a555 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c @@ -369,7 +369,7 @@ static void iwl_fwrt_dump_fseq_regs(struct iwl_fw_runtime *fwrt) void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt) { struct iwl_pc_data *pc_data; - u8 count; + u32 count; if (!iwl_trans_device_enabled(fwrt->trans)) { IWL_ERR(fwrt, From 405ff50b72db1dfb86d7502c3c84208779809ab2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 22:04:21 +0300 Subject: [PATCH 089/403] wifi: iwlwifi: mld: fix validation fallback in iwl_mld_notif_is_valid When a firmware notification version is not in the handler's size table, iwl_mld_notif_is_valid() falls back to comparing against the last known structure size but the comparison is wrong: 'return size < last_known_size' returns true (accept) for undersized payloads and false (reject) for payloads that are large enough. Instead of trying to accept notifications that are large enough, just refuse the notification. We shouldn't ever get a notification that is longer than what we expect. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.e0b91efe689d.I7d7604be6819da263e9091370892e6b6f4c57913@changeid --- drivers/net/wireless/intel/iwlwifi/mld/notif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c index 7574689e4088..b3a899828db9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c @@ -517,7 +517,7 @@ iwl_mld_notif_is_valid(struct iwl_mld *mld, struct iwl_rx_packet *pkt, handler->cmd_id, notif_ver, handler->sizes[handler->n_sizes - 1].ver); - return size < handler->sizes[handler->n_sizes - 1].size; + return false; } struct iwl_async_handler_entry { From f6a6c01cbc046f68e6916a7e047a1bc881c8c9ab Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 22:04:22 +0300 Subject: [PATCH 090/403] wifi: iwlwifi: mvm: fix off-by-one in TXF key sanitiser iwl_mvm_frob_txf_key_iter() tracks the last matched byte position in loop variable 'i'. When a full key match is found (match == keylen), 'i' points at the last byte of the matched key. The memset start offset should therefore be i + 1 - keylen, not i - keylen; the current code zeroes one byte before the match and leaves the final key byte un-sanitised. Fixes: 12d60c1efc29 ("iwlwifi: mvm: scrub key material in firmware dumps") Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.355998ec4fbe.I40f3427657b897e911bdf4ebf8e494745508d126@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 6ae9f87d5221..da4c67a91113 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -954,7 +954,7 @@ static void iwl_mvm_frob_txf_key_iter(struct ieee80211_hw *hw, } match++; if (match == keylen) { - memset(txf->buf + i - keylen, 0xAA, keylen); + memset(txf->buf + i + 1 - keylen, 0xAA, keylen); match = 0; } } From ad13072308f82a9aa2e68a135e70672454367d92 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Wed, 15 Jul 2026 22:04:23 +0300 Subject: [PATCH 091/403] wifi: iwlwifi: mld: honor FW puncturing capability in MCC response New MCC response versions expose puncturing support directly in the regulatory capability flags. Propagate that information from NVM MCC parsing to MLD MCC handling and fall back to legacy FM/WH MCC-specific policy when puncturing status is unknown. Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.47d1389fa134.I5c7921d6e3c065e3962c5927991498c2d277fd8f@changeid --- .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 30 ++++++++++++++++++- .../wireless/intel/iwlwifi/iwl-nvm-parse.h | 22 ++++++++++++-- drivers/net/wireless/intel/iwlwifi/mld/mcc.c | 24 ++++++++++----- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- 4 files changed, 67 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 761424812609..2f38eea42963 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -230,6 +230,18 @@ enum iwl_reg_capa_flags_v5 { REG_CAPA_V5_11BN_DISABLED = BIT(17), }; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_4, 5 */ +/** + * enum iwl_reg_capa_flags_v6 - global capability flags, + * applicable from MCC response version 10 onwards. + * Response v6 includes all members of iwl_reg_capa_flags_v5; only v6-specific + * additions are listed here. + * @REG_CAPA_V6_EHT_PUNCTURING_ENABLED: EHT puncturing is enabled for this + * regulatory domain. + */ +enum iwl_reg_capa_flags_v6 { + REG_CAPA_V6_EHT_PUNCTURING_ENABLED = BIT(18), +}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_6 */ + /* * API v2 for reg_capa_flags is relevant from version 6 and onwards of the * MCC update command response. @@ -241,6 +253,11 @@ enum iwl_reg_capa_flags_v5 { */ #define REG_CAPA_V4_RESP_VER 8 +/* API v6 for reg_capa_flags is relevant from version 10 and onwards of the + * MCC update command response. + */ +#define REG_CAPA_V6_RESP_VER 10 + static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level, int chan, u32 flags) { @@ -1686,6 +1703,13 @@ static struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver) { struct iwl_reg_capa reg_capa = {}; + if (resp_ver >= REG_CAPA_V6_RESP_VER) { + if (flags & REG_CAPA_V6_EHT_PUNCTURING_ENABLED) + reg_capa.puncturing_status = IWL_PUNCTURING_STATUS_ENABLED; + else + reg_capa.puncturing_status = IWL_PUNCTURING_STATUS_DISABLED; + } + if (resp_ver >= REG_CAPA_V4_RESP_VER) { reg_capa.allow_40mhz = true; reg_capa.allow_80mhz = flags & REG_CAPA_V5_80MHZ_ALLOWED; @@ -1712,7 +1736,8 @@ static struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver) struct ieee80211_regdomain * iwl_parse_nvm_mcc_info(struct iwl_trans *trans, int num_of_ch, __le32 *channels, u16 fw_mcc, - u16 geo_info, u32 cap, u8 resp_ver) + u16 geo_info, u32 cap, u8 resp_ver, + enum iwl_puncturing_status *puncturing_status) { const struct iwl_rf_cfg *cfg = trans->cfg; struct device *dev = trans->dev; @@ -1772,6 +1797,9 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans, /* parse regulatory capability flags */ reg_capa = iwl_get_reg_capa(cap, resp_ver); + if (puncturing_status) + *puncturing_status = reg_capa.puncturing_status; + for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { enum nl80211_band band = iwl_nl80211_band_from_channel_idx(ch_idx); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index e676d7c2d6cc..9ebb72d3726a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2015, 2018-2025 Intel Corporation + * Copyright (C) 2005-2015, 2018-2026 Intel Corporation * Copyright (C) 2016-2017 Intel Deutschland GmbH */ #ifndef __iwl_nvm_parse_h__ @@ -21,6 +21,19 @@ enum iwl_nvm_sbands_flags { IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ = BIT(1), }; +/** + * enum iwl_puncturing_status - EHT puncturing status from MCC capabilities + * @IWL_PUNCTURING_STATUS_UNKNOWN: puncturing status is not provided by FW + * or is not initialized yet + * @IWL_PUNCTURING_STATUS_ENABLED: puncturing is enabled for the current MCC + * @IWL_PUNCTURING_STATUS_DISABLED: puncturing is disabled for the current MCC + */ +enum iwl_puncturing_status { + IWL_PUNCTURING_STATUS_UNKNOWN, + IWL_PUNCTURING_STATUS_ENABLED, + IWL_PUNCTURING_STATUS_DISABLED, +}; + /** * struct iwl_reg_capa - struct for global regulatory capabilities, Used for * handling the different APIs of reg_capa_flags. @@ -36,6 +49,8 @@ enum iwl_nvm_sbands_flags { * @disable_11ax: 11ax is forbidden for this regulatory domain. * @disable_11be: 11be is forbidden for this regulatory domain. * @disable_11bn: UHR/11bn is not allowed for this regulatory domain + * @puncturing_status: EHT puncturing status for the current MCC. + * See &enum iwl_puncturing_status. */ struct iwl_reg_capa { bool allow_40mhz; @@ -45,6 +60,7 @@ struct iwl_reg_capa { bool disable_11ax; bool disable_11be; bool disable_11bn; + enum iwl_puncturing_status puncturing_status; }; /** @@ -131,11 +147,13 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, * @geo_info: geo info value * @cap: capability * @resp_ver: FW response version + * @puncturing_status: FW puncturing status for current MCC, when available */ struct ieee80211_regdomain * iwl_parse_nvm_mcc_info(struct iwl_trans *trans, int num_of_ch, __le32 *channels, u16 fw_mcc, - u16 geo_info, u32 cap, u8 resp_ver); + u16 geo_info, u32 cap, u8 resp_ver, + enum iwl_puncturing_status *puncturing_status); /** * struct iwl_nvm_section - describes an NVM section in memory. diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c index 7649ae794dfd..e3d1fa370616 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c @@ -89,6 +89,8 @@ iwl_mld_get_regdomain(struct iwl_mld *mld, struct iwl_mcc_update_resp_v8 *resp; u8 resp_ver = iwl_fw_lookup_notif_ver(mld->fw, IWL_ALWAYS_LONG_GROUP, MCC_UPDATE_CMD, 0); + enum iwl_puncturing_status puncturing_status; + u16 mcc; IWL_DEBUG_LAR(mld, "Getting regdomain data for %s from FW\n", alpha2); @@ -110,12 +112,13 @@ iwl_mld_get_regdomain(struct iwl_mld *mld, } IWL_DEBUG_LAR(mld, "MCC update response version: %d\n", resp_ver); + mcc = le16_to_cpu(resp->mcc); regd = iwl_parse_nvm_mcc_info(mld->trans, __le32_to_cpu(resp->n_channels), - resp->channels, - __le16_to_cpu(resp->mcc), + resp->channels, mcc, __le16_to_cpu(resp->geo_info), - le32_to_cpu(resp->cap), resp_ver); + le32_to_cpu(resp->cap), resp_ver, + &puncturing_status); if (IS_ERR(regd)) { IWL_DEBUG_LAR(mld, "Could not get parse update from FW %ld\n", @@ -129,18 +132,25 @@ iwl_mld_get_regdomain(struct iwl_mld *mld, mld->mcc_src = resp->source_id; + if (puncturing_status == IWL_PUNCTURING_STATUS_ENABLED) + __clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING, + mld->hw->flags); + else if (puncturing_status == IWL_PUNCTURING_STATUS_DISABLED) + ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING); + + if (resp_ver >= 10) + goto out; + /* FM follows BIOS/MCC policy, WH disallows puncturing only in US/CA. */ if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_FM) { if (!iwl_puncturing_is_allowed_in_bios(mld->fwrt.bios_puncturing, - le16_to_cpu(resp->mcc))) + mcc)) ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING); else __clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING, mld->hw->flags); } else if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) == - IWL_CFG_RF_TYPE_WH) { - u16 mcc = le16_to_cpu(resp->mcc); - + IWL_CFG_RF_TYPE_WH) { if (mcc == IWL_MCC_US || mcc == IWL_MCC_CANADA) ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING); else diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 14aec0bfd173..37e2e2c6b716 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -152,7 +152,7 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, resp->channels, __le16_to_cpu(resp->mcc), __le16_to_cpu(resp->geo_info), - le32_to_cpu(resp->cap), resp_ver); + le32_to_cpu(resp->cap), resp_ver, NULL); /* Store the return source id */ src_id = resp->source_id; if (IS_ERR_OR_NULL(regd)) { From ebc246e1d5a3e4502dba60cf9e9644de0fc5a8b6 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Wed, 15 Jul 2026 22:04:24 +0300 Subject: [PATCH 092/403] wifi: iwlwifi: mvm: add LARI_CONFIG_EXTENSION command There is a new UHB extension bitmap that is part of the LARI configuration, which needs to be sent to the FW - also frozen ones. But in frozen FWs we cannot increase the version of an API, since the driver assumes a specific version, depending on the core number. In case of a (new) FW that expects the new version and a (old) driver that doesn't support that new version, the driver will send a default old version, causing a fw assert about its bad size. To mitigate this, there is a special command which will be supported only on those frozen FWs. Old drivers will simply not support/send it, and new driver will send it if supported by fw. Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.551f40ee2de3.I3e32a5d5c9aa13cbb0e599bef630cdb8e3b031c4@changeid --- .../wireless/intel/iwlwifi/fw/api/nvm-reg.h | 26 ++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 36 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 3 files changed, 63 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index d8ec9934a9b6..360b626a9572 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -46,6 +46,11 @@ enum iwl_regulatory_and_nvm_subcmd_ids { */ MCC_ALLOWED_AP_TYPE_CMD = 0x5, + /** + * @LARI_CONFIG_EXTENSION: &struct iwl_lari_config_extension_cmd + */ + LARI_CONFIG_EXTENSION = 0x8, + /** * @PNVM_INIT_COMPLETE_NTFY: &struct iwl_pnvm_init_complete_ntfy */ @@ -514,6 +519,27 @@ struct iwl_bios_config_hdr { u8 reserved[2]; } __packed; /* BIOS_CONFIG_HDR_API_S_VER_1 */ +/** + * struct iwl_lari_config_extension_cmd - extend LARI configuration + * + * LARI_CONFIG_CHANGE's version must remain stable for frozen firmware. + * Because the driver might not know this version but still load that + * frozen FW and then send some default old version of LARI, causing the FW to + * assert about the bad size of it. To handle this, we do the following: + * 1. For newer firmware: increase the LARI_CONFIG_CHANGE version to support the + * new firmware API with extra UHB bits. + * 2. For frozen firmware: add a special alternative API that doesn't require + * modifying the frozen LARI_CONFIG_CHANGE's version. + * @dsm_table_hdr: BIOS DSM table source and revision + * @oem_uhb_allow_extension_bitmap: extension bitmap for OEM UHB config + * @reserved: reserved + */ +struct iwl_lari_config_extension_cmd { + struct iwl_bios_config_hdr dsm_table_hdr; + __le32 oem_uhb_allow_extension_bitmap; + __le32 reserved[10]; +} __packed; /* LARI_CONFIG_EXTENSION_CMD_API_S_VER_1 */ + /** * struct bios_value_u32 - BIOS configuration. * @hdr: bios config header diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 6e507d6dcdd2..187f39aa6249 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1507,12 +1507,48 @@ static int iwl_mvm_fill_lari_config(struct iwl_fw_runtime *fwrt, return 0; } +static void iwl_mvm_send_lari_cfg_extension(struct iwl_mvm *mvm) +{ + struct iwl_fw_runtime *fwrt = &mvm->fwrt; + struct iwl_lari_config_extension_cmd cmd = {}; + u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, + LARI_CONFIG_EXTENSION); + u32 value; + int ret; + + if (iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 0) < 1) + return; + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_REGULATORY_CONFIG, &value); + if (ret) + return; + + cmd.dsm_table_hdr.table_source = fwrt->dsm_source; + cmd.dsm_table_hdr.table_revision = fwrt->dsm_revision; + cmd.oem_uhb_allow_extension_bitmap = cpu_to_le32(value); + + IWL_DEBUG_RADIO(mvm, + "sending LARI_CONFIG_EXTENSION, oem_uhb_allow_extension_bitmap=0x%x\n", + le32_to_cpu(cmd.oem_uhb_allow_extension_bitmap)); + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); + if (ret < 0) + IWL_DEBUG_RADIO(mvm, + "Failed to send LARI_CONFIG_EXTENSION (%d)\n", + ret); +} + static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) { struct iwl_lari_config_change_cmd cmd; size_t cmd_size; int ret; + /* + * LARI_CONFIG_CHANGE triggers a profile update, so send + * LARI_CONFIG_EXTENSION first to make sure its data is applied + * in the same update. + */ + iwl_mvm_send_lari_cfg_extension(mvm); ret = iwl_mvm_fill_lari_config(&mvm->fwrt, &cmd, &cmd_size); if (!ret) { ret = iwl_mvm_send_cmd_pdu(mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index da4c67a91113..1dd27292af9b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -707,6 +707,7 @@ static const struct iwl_hcmd_names iwl_mvm_regulatory_and_nvm_names[] = { HCMD_NAME(NVM_ACCESS_COMPLETE), HCMD_NAME(NVM_GET_INFO), HCMD_NAME(TAS_CONFIG), + HCMD_NAME(LARI_CONFIG_EXTENSION), }; /* Please keep this array *SORTED* by hex value. From 51c45bb2c884e0e2f56d6770011994d653371702 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 15 Jul 2026 22:04:25 +0300 Subject: [PATCH 093/403] wifi: iwlwifi: mld: add PNVM_INIT_COMPLETE_NTFY to the hcmd names Add it to the array of host command name so it will be printed with iwl_get_cmd_string Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.e8da467f1883.I75ab56a022f31365042d29aff5484e4329b0f6ce@changeid --- drivers/net/wireless/intel/iwlwifi/mld/mld.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 093bdc130704..7c49d0441fdf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -212,6 +212,7 @@ static const struct iwl_hcmd_names iwl_mld_reg_and_nvm_names[] = { HCMD_NAME(TAS_CONFIG), HCMD_NAME(SAR_OFFSET_MAPPING_TABLE_CMD), HCMD_NAME(MCC_ALLOWED_AP_TYPE_CMD), + HCMD_NAME(PNVM_INIT_COMPLETE_NTFY), }; /* Please keep this array *SORTED* by hex value. From c5aeb11489150be84d3a700711abffd98969d2d5 Mon Sep 17 00:00:00 2001 From: Shahar Tzarfati Date: Wed, 15 Jul 2026 22:04:26 +0300 Subject: [PATCH 094/403] wifi: iwlwifi: mld: fix read in wake packet notification handler In iwl_mld_handle_wake_pkt_notif(), expected_size was initialized from notif->wake_packet_length before the IWL_FW_CHECK that validates the payload covers sizeof(*notif). Move the assignment of expected_size to after the size check so that notif->wake_packet_length is only accessed once the payload length has been validated. Signed-off-by: Shahar Tzarfati Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.94c526d2c66e.I065a19a9dcc7f45a7457667c0f625fcd2c7bf6b6@changeid --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index c9c0e3c729c9..3b785c53948f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -705,7 +705,7 @@ iwl_mld_handle_wake_pkt_notif(struct iwl_mld *mld, { const struct iwl_wowlan_wake_pkt_notif *notif = (void *)pkt->data; u32 actual_size, len = iwl_rx_packet_payload_len(pkt); - u32 expected_size = le32_to_cpu(notif->wake_packet_length); + u32 expected_size; if (IWL_FW_CHECK(mld, len < sizeof(*notif), "Invalid WoWLAN wake packet notification (expected size=%zu got=%u)\n", @@ -718,6 +718,7 @@ iwl_mld_handle_wake_pkt_notif(struct iwl_mld *mld, wowlan_status->wakeup_reasons)) return true; + expected_size = le32_to_cpu(notif->wake_packet_length); actual_size = len - offsetof(struct iwl_wowlan_wake_pkt_notif, wake_packet); From 7d8cc301bcba233f31b589a45f4c1c97f2bb90d6 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 15 Jul 2026 22:04:27 +0300 Subject: [PATCH 095/403] wifi: iwlwifi: mei: check SAP message length before reading it Verify the SAP message size is not larger than the local buffer before reading the message to avoid buffer overflow. Fixes: bcd68b3dbe78 ("wifi: iwlwifi: mei: fix tx DHCP packet for devices with new Tx API") Signed-off-by: Avraham Stern Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.f0026ce26218.I00a856d3aacae1caac605c708f7362689b734234@changeid --- drivers/net/wireless/intel/iwlwifi/mei/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index c5ff1b1b720f..c01435859349 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2021-2024 Intel Corporation + * Copyright (C) 2026 Intel Corporation */ #include @@ -1147,6 +1148,11 @@ static void iwl_mei_handle_sap_rx_cmd(struct mei_cl_device *cldev, iwl_mei_read_from_q(q_head, q_sz, &rd, wr, hdr, sizeof(*hdr)); valid_rx_sz -= sizeof(*hdr); len = le16_to_cpu(hdr->len); + if (len + sizeof(*hdr) > PAGE_SIZE) { + dev_err(&cldev->dev, + "SAP message is too big: %u\n", len); + break; + } if (valid_rx_sz < len) break; From c00a5d65b7ada536eb488280fae53fcf1724a7c3 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 15 Jul 2026 22:04:28 +0300 Subject: [PATCH 096/403] wifi: iwlwifi: mei: skip data read if length is too short When calculating the SAP data length, the code subtracts sizeof(*ethhdr) from len. If the SAP data header indicates a length that is shorter than ethernet header length, this will result in an unsigned underflow which will lead to a kernel panic when trying to put the data into the SKB. Fix it by skipping a message if the indicated length is too short. In addition, if the message type is not SAP_MSG_DATA_PACKET or skb allocation fails, the loop skips to the next message but without reading the message payload. This may result in reading the payload as the next message header, which will lead to errors in parsing the next messages. Fix it by skipping the message payload as well. Signed-off-by: Avraham Stern Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.f66b10736047.I4a1dde517c36561d41358dd82a5cec8b6c886c14@changeid --- drivers/net/wireless/intel/iwlwifi/mei/main.c | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index c01435859349..c462c3b22ec1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -1041,11 +1041,14 @@ static void iwl_mei_read_from_q(const u8 *q_head, u32 q_sz, u32 rd = *_rd; if (rd + len <= q_sz) { - memcpy(buf, q_head + rd, len); + if (buf) + memcpy(buf, q_head + rd, len); rd += len; } else { - memcpy(buf, q_head + rd, q_sz - rd); - memcpy(buf + q_sz - rd, q_head, len - (q_sz - rd)); + if (buf) { + memcpy(buf, q_head + rd, q_sz - rd); + memcpy(buf + q_sz - rd, q_head, len - (q_sz - rd)); + } rd = len - (q_sz - rd); } @@ -1086,24 +1089,29 @@ static void iwl_mei_handle_sap_data(struct mei_cl_device *cldev, break; } + valid_rx_sz -= len; + if (len < sizeof(*ethhdr)) { dev_err(&cldev->dev, "Data len is smaller than an ethernet header? len = %d\n", len); + iwl_mei_read_from_q(q_head, q_sz, &rd, wr, NULL, len); + continue; } - valid_rx_sz -= len; - if (le16_to_cpu(hdr.type) != SAP_MSG_DATA_PACKET) { dev_err(&cldev->dev, "Unsupported Rx data: type %d, len %d\n", le16_to_cpu(hdr.type), len); + iwl_mei_read_from_q(q_head, q_sz, &rd, wr, NULL, len); continue; } /* We need enough room for the WiFi header + SNAP + IV */ skb = netdev_alloc_skb(netdev, len + QOS_HDR_IV_SNAP_LEN); - if (!skb) + if (!skb) { + iwl_mei_read_from_q(q_head, q_sz, &rd, wr, NULL, len); continue; + } skb_reserve(skb, QOS_HDR_IV_SNAP_LEN); ethhdr = skb_push(skb, sizeof(*ethhdr)); From 9318bc0c41b24705690cf80d1596cf6b711e7027 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 22:04:29 +0300 Subject: [PATCH 097/403] wifi: iwlwifi: guard against division by zero in iwl_dbg_tlv_alloc_fragments Make sure we don't end-up with a num_frags = 0 situation. For that, check that the required size is not 0 and put a checker on num_frags as well. Fixes: 14124b25780d ("iwlwifi: dbg_ini: implement monitor allocation flow") Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.60121deecf2c.Iebc891c95a7bd1b2a093b0bb88532db446a758ee@changeid --- drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index d021b24d04d6..8b0f091ff4c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include #include "iwl-drv.h" @@ -602,6 +602,9 @@ static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt, cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH)) return 0; + if (!fw_mon_cfg->req_size) + return -EIO; + num_frags = le32_to_cpu(fw_mon_cfg->max_frags_num); if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1) @@ -612,6 +615,9 @@ static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt, return -EIO; } + if (!num_frags) + return -EIO; + remain_pages = DIV_ROUND_UP(le32_to_cpu(fw_mon_cfg->req_size), PAGE_SIZE); num_frags = min_t(u32, num_frags, BUF_ALLOC_MAX_NUM_FRAGS); From 1c031ac5a39ebcc3269eace8b606043adc398bfa Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Wed, 15 Jul 2026 22:04:30 +0300 Subject: [PATCH 098/403] wifi: iwlwifi: mld: Do not cleanup FW state when the device is dead When a channel context is unassigned, there is a path to cleanup the FW state in case of NON MLO connection: remove the link and add it again. However, when the transport is dead, e.g., during device removal etc., this flow will fail and as a result the mld_vif->link[0] would be set to NULL. Later, when the interface is removed, iwl_mld_remove_link() would warn as the link is NULL. Fix this by not doing the cleanup when the device is dead. Signed-off-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.89a0c44a72a3.I45cca8b84a25943d5771199af6b0155dbac77b58@changeid --- drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 17922ed3800d..92985e500459 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1280,11 +1280,13 @@ void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, iwl_mld_tlc_update_phy(mld, vif, link); /* in the non-MLO case, remove/re-add the link to clean up FW state. - * In MLO, it'll be done in drv_change_vif_link + * In MLO, it'll be done in drv_change_vif_link. + * Do not do so during restart or in case the device is dead. */ if (!ieee80211_vif_is_mld(vif) && !mld_vif->ap_sta && !WARN_ON_ONCE(vif->cfg.assoc) && - vif->type != NL80211_IFTYPE_AP && !mld->fw_status.in_hw_restart) { + vif->type != NL80211_IFTYPE_AP && !mld->fw_status.in_hw_restart && + !iwl_trans_is_dead(mld->trans)) { iwl_mld_remove_link(mld, link); iwl_mld_add_link(mld, link); } From 905f57aefde4f4092a411c8a55856182fb1c7598 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 15 Jul 2026 22:04:31 +0300 Subject: [PATCH 099/403] wifi: iwlwifi: mei: pass correct argument to function The first argument to iwl_mei_write_cyclic_buf() should be the cldev but the q_head pointer is passed instead. Fix it. Fixes: 652291601459 ("iwlwifi: mei: don't rely on the size from the shared area") Signed-off-by: Avraham Stern Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715220243.24cea60c6428.I42301010c31487b1458faa967b22c8320b0cfd23@changeid --- drivers/net/wireless/intel/iwlwifi/mei/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mei/main.c b/drivers/net/wireless/intel/iwlwifi/mei/main.c index c462c3b22ec1..ce92e26c42d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mei/main.c +++ b/drivers/net/wireless/intel/iwlwifi/mei/main.c @@ -458,7 +458,7 @@ static int iwl_mei_send_sap_msg_payload(struct mei_cl_device *cldev, notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF]; q_head = mei->shared_mem.q_head[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_NOTIF]; q_sz = mei->shared_mem.q_size[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_NOTIF]; - ret = iwl_mei_write_cyclic_buf(q_head, notif_q, q_head, hdr, q_sz); + ret = iwl_mei_write_cyclic_buf(cldev, notif_q, q_head, hdr, q_sz); if (ret < 0) return ret; From d4157cd3aeab49e994826733aba628f45c7fcf0a Mon Sep 17 00:00:00 2001 From: Chelsy Ratnawat Date: Thu, 9 Jul 2026 12:43:15 -0700 Subject: [PATCH 100/403] wifi: rtlwifi: rtl8192d: remove dead SMPS rate mask code mimo_ps is initialized to IEEE80211_SMPS_OFF and never modified in rtl92d_update_hal_rate_table(). Therefore, the IEEE80211_SMPS_STATIC case is unreachable. Remove the unused mimo_ps variable and the dead branch. Signed-off-by: Chelsy Ratnawat Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260709194315.157030-1-chelsyratnawat2001@gmail.com --- .../realtek/rtlwifi/rtl8192d/hw_common.c | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192d/hw_common.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192d/hw_common.c index 97e0d9c01e0a..cfefbe86380f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192d/hw_common.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192d/hw_common.c @@ -775,7 +775,6 @@ static void rtl92d_update_hal_rate_table(struct ieee80211_hw *hw, struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &rtlpriv->phy; enum wireless_mode wirelessmode; - u8 mimo_ps = IEEE80211_SMPS_OFF; u8 curtxbw_40mhz = mac->bw_40; u8 nmode = mac->ht_enable; u8 curshortgi_40mhz; @@ -784,6 +783,7 @@ static void rtl92d_update_hal_rate_table(struct ieee80211_hw *hw, u8 ratr_index = 0; u16 shortgi_rate; u32 ratr_value; + u32 ratr_mask; curshortgi_40mhz = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40); curshortgi_20mhz = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20); @@ -811,20 +811,15 @@ static void rtl92d_update_hal_rate_table(struct ieee80211_hw *hw, case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: nmode = 1; - if (mimo_ps == IEEE80211_SMPS_STATIC) { - ratr_value &= 0x0007F005; + + if (get_rf_type(rtlphy) == RF_1T2R || + get_rf_type(rtlphy) == RF_1T1R) { + ratr_mask = 0x000ff005; } else { - u32 ratr_mask; - - if (get_rf_type(rtlphy) == RF_1T2R || - get_rf_type(rtlphy) == RF_1T1R) { - ratr_mask = 0x000ff005; - } else { - ratr_mask = 0x0f0ff005; - } - - ratr_value &= ratr_mask; + ratr_mask = 0x0f0ff005; } + + ratr_value &= ratr_mask; break; default: if (rtlphy->rf_type == RF_1T2R) From af7f59e8e8763d4120f00f17683d3cf93801742a Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Sun, 12 Jul 2026 11:04:56 +0800 Subject: [PATCH 101/403] wifi: rtw89: coex: Add Wi-Fi role info version 10 Because the new generation Bluetooth will able to work on 5/6GHz band, it will suffer 5/6GHz Wi-Fi, the mechanism need to cover more scenario with different Wi-Fi/Bluetooth combination. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712030506.43438-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 2493 +++++++---------- drivers/net/wireless/realtek/rtw89/coex.h | 34 + drivers/net/wireless/realtek/rtw89/core.h | 153 +- drivers/net/wireless/realtek/rtw89/fw.c | 384 ++- drivers/net/wireless/realtek/rtw89/fw.h | 50 +- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 1 + 12 files changed, 1412 insertions(+), 1709 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 989344e93ab7..c02de0b4f7bf 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -436,11 +436,6 @@ union rtw89_fbtc_set_mon_reg { struct rtw89_btc_btf_set_mon_reg_v7 v7; } __packed; -struct _wl_rinfo_now { - u8 link_mode; - u32 dbcc_2g_phy: 2; -}; - enum btc_btf_set_cx_policy { CXPOLICY_TDMA = 0x0, CXPOLICY_SLOT = 0x1, @@ -570,9 +565,21 @@ enum btc_cx_poicy_type { /* TDMA off + pri: WL > BT, Block-BT*/ BTC_CXP_OFF_WL2 = (BTC_CXP_OFF << 8) | 12, - /* TDMA off+Bcn-Protect + pri: WL_Hi-Tx > BT_Hi_Rx, BT_Hi > WL > BT_Lo*/ + /* TDMA off+Bcn-Protect + pri: BT_Hi > WL > BT_Lo */ BTC_CXP_OFFB_BWB0 = (BTC_CXP_OFFB << 8) | 0, + /* TDMA off+Bcn-Protect + pri: BT_Hi_Tx = WL_Hi-Tx > BT_Hi_Rx, BT_Hi > WL > BT_Lo */ + BTC_CXP_OFFB_BWB1 = (BTC_CXP_OFFB << 8) | 1, + + /* TDMA off+Bcn-Protect + pri: WL_Hi-Tx > BT, BT_Hi > other-WL > BT_Lo */ + BTC_CXP_OFFB_BWB2 = (BTC_CXP_OFFB << 8) | 2, + + /* TDMA off+Bcn-Protect + pri: WL_Hi-Tx = BT*/ + BTC_CXP_OFFB_BWB3 = (BTC_CXP_OFFB << 8) | 3, + + /* TDMA off+Bcn-Protect + pri: BT > WL*/ + BTC_CXP_OFFB_BWB4 = (BTC_CXP_OFFB << 8) | 4, + /* TDMA off + Ext-Ctrl + pri: default */ BTC_CXP_OFFE_DEF = (BTC_CXP_OFFE << 8) | 0, @@ -752,19 +759,31 @@ enum btc_w2b_scoreboard { BTC_WSCB_ALL = GENMASK(23, 0), }; -enum btc_wl_link_mode { +enum btc_wl_link_mode_v0 { + BTC_WLINK_V0_NOLINK = 0x0, + BTC_WLINK_V0_2G_STA, + BTC_WLINK_V0_2G_AP, + BTC_WLINK_V0_2G_GO, + BTC_WLINK_V0_2G_GC, + BTC_WLINK_V0_2G_SCC, + BTC_WLINK_V0_2G_MCC, + BTC_WLINK_V0_25G_MCC, + BTC_WLINK_V0_25G_DBCC, + BTC_WLINK_V0_5G, + BTC_WLINK_V0_2G_NAN, + BTC_WLINK_V0_OTHER, + BTC_WLINK_V0_MAX +}; + +enum btc_wl_link_mode_v1 { BTC_WLINK_NOLINK = 0x0, - BTC_WLINK_2G_STA, - BTC_WLINK_2G_AP, - BTC_WLINK_2G_GO, - BTC_WLINK_2G_GC, - BTC_WLINK_2G_SCC, - BTC_WLINK_2G_MCC, - BTC_WLINK_25G_MCC, - BTC_WLINK_25G_DBCC, - BTC_WLINK_5G, - BTC_WLINK_2G_NAN, - BTC_WLINK_OTHER, + BTC_WLINK_STA, + BTC_WLINK_AP, + BTC_WLINK_GO, + BTC_WLINK_GC, + BTC_WLINK_SCC, + BTC_WLINK_SB_MCC, + BTC_WLINK_DB_MCC, /* 2+0/0+2 Dual-RF-band MCC */ BTC_WLINK_MAX }; @@ -774,16 +793,13 @@ static const char *id_to_linkmode(u8 id) { switch (id) { CASE_BTC_WL_LINK_MODE(NOLINK); - CASE_BTC_WL_LINK_MODE(2G_STA); - CASE_BTC_WL_LINK_MODE(2G_AP); - CASE_BTC_WL_LINK_MODE(2G_GO); - CASE_BTC_WL_LINK_MODE(2G_GC); - CASE_BTC_WL_LINK_MODE(2G_SCC); - CASE_BTC_WL_LINK_MODE(2G_MCC); - CASE_BTC_WL_LINK_MODE(25G_MCC); - CASE_BTC_WL_LINK_MODE(25G_DBCC); - CASE_BTC_WL_LINK_MODE(5G); - CASE_BTC_WL_LINK_MODE(OTHER); + CASE_BTC_WL_LINK_MODE(STA); + CASE_BTC_WL_LINK_MODE(AP); + CASE_BTC_WL_LINK_MODE(GO); + CASE_BTC_WL_LINK_MODE(GC); + CASE_BTC_WL_LINK_MODE(SCC); + CASE_BTC_WL_LINK_MODE(SB_MCC); + CASE_BTC_WL_LINK_MODE(DB_MCC); default: return "unknown"; } @@ -969,7 +985,7 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type) struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; struct rtw89_btc_wl_link_info *wl_linfo; - u8 i; + u8 i, j; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s\n", __func__); @@ -990,12 +1006,11 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type) if (type & BTC_RESET_DM) { memset(&btc->dm, 0, sizeof(btc->dm)); memset(bt_linfo->rssi_state, 0, sizeof(bt_linfo->rssi_state)); - for (i = 0; i < RTW89_PORT_NUM; i++) { - if (btc->ver->fwlrole == 8) - wl_linfo = &wl->rlink_info[i][0]; - else - wl_linfo = &wl->link_info[i]; - memset(wl_linfo->rssi_state, 0, sizeof(wl_linfo->rssi_state)); + for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) { + for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { + wl_linfo = &wl->rlink_info[i][j]; + memset(wl_linfo->rssi_state, 0, sizeof(wl_linfo->rssi_state)); + } } /* set the slot_now table to original */ @@ -2847,6 +2862,8 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) rtw89_fw_h2c_cxdrv_role_v7(rtwdev, index); else if (ver->fwlrole == 8) rtw89_fw_h2c_cxdrv_role_v8(rtwdev, index); + else if (ver->fwlrole == 10) + rtw89_fw_h2c_cxdrv_role_v10(rtwdev, index); break; case CXDRVINFO_CTRL: if (ver->drvinfo_ver != 0) @@ -3396,29 +3413,17 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_wl_role_info *r = &wl->role_info; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_bt_link_info *b = &bt->link_info; struct rtw89_btc_wl_smap *wl_smap = &wl->status.map; struct rtw89_btc_rf_trx_para_v9 para; - u8 lv, link_mode = 0, i, dbcc_2g_phy = 0; - u8 ul_para_num, dl_para_num; - u8 rf_band = RTW89_BAND_2G; - u8 bid = BTC_BT_1ST; - u32 wl_stb_chg = 0; - - if (ver->fwlrole == 0) { - link_mode = wl->role_info.link_mode; - for (i = 0; i < RTW89_PHY_NUM; i++) { - if (wl->dbcc_info.real_band[i] == RTW89_BAND_2G) - dbcc_2g_phy = i; - } - } else if (ver->fwlrole == 1) { - link_mode = wl->role_info_v1.link_mode; - dbcc_2g_phy = wl->role_info_v1.dbcc_2g_phy; - } else if (ver->fwlrole == 2) { - link_mode = wl->role_info_v2.link_mode; - dbcc_2g_phy = wl->role_info_v2.dbcc_2g_phy; - } + u8 bmode = dm->tdd_bind.wl_link_mode; + u8 rf_band = dm->tdd_bind.rf_band; + u8 i, ul_para_num, dl_para_num; + u8 mode = BTC_WLINK_NOLINK; + u8 bid = BTC_BT_1ST, lv; + u32 wl_stb_chg; if (ver->fcxtrx == 9 && chip->rf_para_ulink_v9) { ul_para_num = chip->rf_para_ulink_num_v9; @@ -3434,6 +3439,19 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) goto next; } + if (bmode == BTC_WLINK_NOLINK) { + return; + } else if (btc->ver->fwlrole == 10) { + if (rf_band == RTW89_BAND_5G) { + mode = BTC_WLINK_V0_5G; + } else if (rf_band == RTW89_BAND_2G && + bmode == BTC_WLINK_STA) { + mode = BTC_WLINK_V0_2G_STA; + } + } else { + mode = r->link_mode_v0; + } + /* decide trx_para_level */ if (btc->ant_type == BTC_ANT_SHARED) { /* fix LNA2 + TIA gain not change by GNT_BT */ @@ -3446,7 +3464,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) dm->trx_para_level = 5; /* modify trx_para if WK 2.4G-STA-DL + bt link */ if (b->link_cnt.now != 0 && - link_mode == BTC_WLINK_2G_STA && + mode == BTC_WLINK_V0_2G_STA && wl->status.map.traffic_dir & BIT(RTW89_TFC_UL)) { /* uplink */ if (wl->rssi_level == 4 && bt->rssi_level > 2) dm->trx_para_level = 6; @@ -3504,10 +3522,8 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) next: if (!bt->enable.now || dm->wl_only || wl_smap->rf_off || - wl_smap->lps == BTC_LPS_RF_OFF || - link_mode == BTC_WLINK_5G || - link_mode == BTC_WLINK_NOLINK || - (rtwdev->dbcc_en && dbcc_2g_phy != RTW89_PHY_1)) + wl_smap->lps == BTC_LPS_RF_OFF || mode == BTC_WLINK_V0_5G || + (rtwdev->dbcc_en && r->dbcc_2g_phy != RTW89_PHY_1)) wl_stb_chg = 0; else wl_stb_chg = 1; @@ -3544,282 +3560,372 @@ static void _update_btc_state_map(struct rtw89_dev *rtwdev) } } -static void _set_bt_afh_info_v0(struct rtw89_dev *rtwdev) +static u8 _get_wl_guard_bw(struct rtw89_dev *rtwdev, u8 index) { - const struct rtw89_chip_info *chip = rtwdev->chip; - struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; - struct rtw89_btc_bt_link_info *b = &bt->link_info; - struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; - struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; - struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; - struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; - struct rtw89_btc_wl_active_role *r; - struct rtw89_btc_wl_active_role_v1 *r1; - struct rtw89_btc_wl_active_role_v2 *r2; - struct rtw89_btc_wl_active_role_v7 *r7; - struct rtw89_btc_wl_rlink *rlink; - u8 en = 0, i, ch = 0, bw = 0; - u8 mode, connect_cnt; + struct rtw89_btc_dm *dm = &rtwdev->btc.dm; + u8 guard_ch = rtwdev->chip->afh_guard_ch; + u8 bw = 0; - if (btc->manual_ctrl || wl->status.map.scan) - return; - - if (ver->fwlrole == 0) { - mode = wl_rinfo->link_mode; - connect_cnt = wl_rinfo->connect_cnt; - } else if (ver->fwlrole == 1) { - mode = wl_rinfo_v1->link_mode; - connect_cnt = wl_rinfo_v1->connect_cnt; - } else if (ver->fwlrole == 2) { - mode = wl_rinfo_v2->link_mode; - connect_cnt = wl_rinfo_v2->connect_cnt; - } else if (ver->fwlrole == 7) { - mode = wl_rinfo_v7->link_mode; - connect_cnt = wl_rinfo_v7->connect_cnt; - } else if (ver->fwlrole == 8) { - mode = wl_rinfo_v8->link_mode; - connect_cnt = wl_rinfo_v8->connect_cnt; - } else { - return; - } - - if (wl->status.map.rf_off || bt->whql_test || - mode == BTC_WLINK_NOLINK || mode == BTC_WLINK_5G || - connect_cnt > BTC_TDMA_WLROLE_MAX) { - en = false; - } else if (mode == BTC_WLINK_2G_MCC || mode == BTC_WLINK_2G_SCC) { - en = true; - /* get p2p channel */ - for (i = 0; i < RTW89_PORT_NUM; i++) { - r = &wl_rinfo->active_role[i]; - r1 = &wl_rinfo_v1->active_role_v1[i]; - r2 = &wl_rinfo_v2->active_role_v2[i]; - r7 = &wl_rinfo_v7->active_role[i]; - rlink = &wl_rinfo_v8->rlink[i][0]; - - if (ver->fwlrole == 0 && - (r->role == RTW89_WIFI_ROLE_P2P_GO || - r->role == RTW89_WIFI_ROLE_P2P_CLIENT)) { - ch = r->ch; - bw = r->bw; - break; - } else if (ver->fwlrole == 1 && - (r1->role == RTW89_WIFI_ROLE_P2P_GO || - r1->role == RTW89_WIFI_ROLE_P2P_CLIENT)) { - ch = r1->ch; - bw = r1->bw; - break; - } else if (ver->fwlrole == 2 && - (r2->role == RTW89_WIFI_ROLE_P2P_GO || - r2->role == RTW89_WIFI_ROLE_P2P_CLIENT)) { - ch = r2->ch; - bw = r2->bw; - break; - } else if (ver->fwlrole == 7 && - (r7->role == RTW89_WIFI_ROLE_P2P_GO || - r7->role == RTW89_WIFI_ROLE_P2P_CLIENT)) { - ch = r7->ch; - bw = r7->bw; - break; - } else if (ver->fwlrole == 8 && - (rlink->role == RTW89_WIFI_ROLE_P2P_GO || - rlink->role == RTW89_WIFI_ROLE_P2P_CLIENT)) { - ch = rlink->ch; - bw = rlink->bw; - break; - } - } - } else { - en = true; - /* get 2g channel */ - for (i = 0; i < RTW89_PORT_NUM; i++) { - r = &wl_rinfo->active_role[i]; - r1 = &wl_rinfo_v1->active_role_v1[i]; - r2 = &wl_rinfo_v2->active_role_v2[i]; - r7 = &wl_rinfo_v7->active_role[i]; - rlink = &wl_rinfo_v8->rlink[i][0]; - - if (ver->fwlrole == 0 && - r->connected && r->band == RTW89_BAND_2G) { - ch = r->ch; - bw = r->bw; - break; - } else if (ver->fwlrole == 1 && - r1->connected && r1->band == RTW89_BAND_2G) { - ch = r1->ch; - bw = r1->bw; - break; - } else if (ver->fwlrole == 2 && - r2->connected && r2->band == RTW89_BAND_2G) { - ch = r2->ch; - bw = r2->bw; - break; - } else if (ver->fwlrole == 7 && - r7->connected && r7->band == RTW89_BAND_2G) { - ch = r7->ch; - bw = r7->bw; - break; - } else if (ver->fwlrole == 8 && - rlink->connected && rlink->rf_band == RTW89_BAND_2G) { - ch = rlink->ch; - bw = rlink->bw; - break; - } - } - } - - switch (bw) { + /* default AFH channel sapn = center-ch +- 6MHz */ + switch (index) { case RTW89_CHANNEL_WIDTH_20: - bw = 20 + chip->afh_guard_ch * 2; - break; - case RTW89_CHANNEL_WIDTH_40: - bw = 40 + chip->afh_guard_ch * 2; - break; - case RTW89_CHANNEL_WIDTH_5: - bw = 5 + chip->afh_guard_ch * 2; - break; - case RTW89_CHANNEL_WIDTH_10: - bw = 10 + chip->afh_guard_ch * 2; - break; - default: - bw = 0; - en = false; /* turn off AFH info if BW > 40 */ - break; - } - - if (wl->afh_info.en == en && - wl->afh_info.ch == ch && - wl->afh_info.bw == bw && - b->link_cnt.last == b->link_cnt.now) { - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): return because no change!\n", - __func__); - return; - } - - wl->afh_info.en = en; - wl->afh_info.ch = ch; - wl->afh_info.bw = bw; - - _send_fw_cmd(rtwdev, BTFC_SET, SET_BT_WL_CH_INFO, &wl->afh_info, 3); - - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): en=%d, ch=%d, bw=%d\n", - __func__, en, ch, bw); - wl->wcnt[BTC_WCNT_CH_UPDATE]++; -} - -static void _set_bt_afh_info_v1(struct rtw89_dev *rtwdev) -{ - const struct rtw89_chip_info *chip = rtwdev->chip; - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; - struct rtw89_btc_wl_afh_info *wl_afh = &wl->afh_info; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; - struct rtw89_btc_wl_rlink *rlink; - u8 en = 0, ch = 0, bw = 0, buf[3] = {}; - u8 i, j, link_mode; - - if (btc->manual_ctrl || wl->status.map.scan) - return; - - link_mode = wl_rinfo->link_mode; - - for (i = 0; i < btc->ver->max_role_num; i++) { - for (j = RTW89_MAC_0; j < RTW89_MAC_NUM; j++) { - if (wl->status.map.rf_off || bt->whql_test || - link_mode == BTC_WLINK_NOLINK || - link_mode == BTC_WLINK_5G) - break; - - rlink = &wl_rinfo->rlink[i][j]; - - /* Don't care no-connected/non-2G-band role */ - if (!rlink->connected || !rlink->active || - rlink->rf_band != RTW89_BAND_2G) - continue; - - en = 1; - ch = rlink->ch; - bw = rlink->bw; - - if (link_mode == BTC_WLINK_2G_MCC && - (rlink->role == RTW89_WIFI_ROLE_AP || - rlink->role == RTW89_WIFI_ROLE_P2P_GO || - rlink->role == RTW89_WIFI_ROLE_P2P_CLIENT)) { - /* for 2.4G MCC, take role = ap/go/gc */ - break; - } else if (link_mode != BTC_WLINK_2G_SCC || - rlink->bw == RTW89_CHANNEL_WIDTH_40) { - /* for 2.4G scc, take bw = 40M */ - break; - } - } - } - - /* default AFH channel sapn = center-ch +- 6MHz */ - switch (bw) { - case RTW89_CHANNEL_WIDTH_20: - if (btc->dm.freerun || btc->dm.fddt_train) + if (dm->freerun || dm->fddt_train) bw = 48; else - bw = 20 + chip->afh_guard_ch * 2; + bw = 20 + guard_ch * 2; break; case RTW89_CHANNEL_WIDTH_40: - if (btc->dm.freerun) - bw = 40 + chip->afh_guard_ch * 2; - else - bw = 40; + bw = 40 + guard_ch * 2; + break; + case RTW89_CHANNEL_WIDTH_80: + bw = 80 + guard_ch * 2; + break; + case RTW89_CHANNEL_WIDTH_160: + case RTW89_CHANNEL_WIDTH_80_80: + bw = 160 + guard_ch * 2; + break; + case RTW89_CHANNEL_WIDTH_320: + bw = 320 + guard_ch * 2; break; case RTW89_CHANNEL_WIDTH_5: - bw = 5 + chip->afh_guard_ch * 2; + bw = 5 + guard_ch * 2; break; case RTW89_CHANNEL_WIDTH_10: - bw = 10 + chip->afh_guard_ch * 2; + bw = 10 + guard_ch * 2; break; default: - en = false; /* turn off AFH info if invalid BW */ bw = 0; - ch = 0; break; } - if (!en || ch > 14 || ch == 0) { - en = false; - bw = 0; - ch = 0; + return bw; +} + +static void _set_wl_ch_map(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_wl_afh_info *afh; + u16 ch_start, ch_end, i; + u8 b_group, b_pos; /* byte-group, bit-position */ + u8 hwb; + + if (wl->afh_info[RTW89_MAC_0][RTW89_BAND_2G].en && + wl->afh_info[RTW89_MAC_1][RTW89_BAND_2G].en) { + /* select wilder bw if 1+1 */ + if (wl->afh_info[RTW89_MAC_1][RTW89_BAND_2G].bw > + wl->afh_info[RTW89_MAC_0][RTW89_BAND_2G].bw) + hwb = RTW89_MAC_1; + else + hwb = RTW89_MAC_0; + } else if (wl->afh_info[RTW89_MAC_1][RTW89_BAND_2G].en) { + hwb = RTW89_MAC_1; + } else { + hwb = RTW89_MAC_0; } - if (wl_afh->en == en && - wl_afh->ch == ch && - wl_afh->bw == bw && - (!bt->enable.now || bt->enable.last)) + afh = &wl->afh_info[hwb][RTW89_BAND_2G]; + + /* focus on WL 2.4GHz link */ + if (!afh->en || afh->band != RTW89_BAND_2G) return; - wl_afh->en = buf[0]; - wl_afh->ch = buf[1]; - wl_afh->bw = buf[2]; + memset(wl->ch_map, 0x0, sizeof(wl->ch_map)); + memset(wl->ch_map_le, 0x0, sizeof(wl->ch_map_le)); - if (_send_fw_cmd(rtwdev, BTFC_SET, SET_BT_WL_CH_INFO, &wl->afh_info, 3)) { - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): en=%d, ch=%d, bw=%d\n", - __func__, en, ch, bw); + ch_start = afh->ch * 5 + 2407 - afh->bw / 2; + ch_end = ch_start + afh->bw; - wl->wcnt[BTC_WCNT_CH_UPDATE]++; + for (i = ch_start; i < ch_end; i++) { + if (i < 2402 || i > 2480) + continue; + b_group = (i - 2402) / 8; + b_pos = 7 - ((i - 2402) % 8); + wl->ch_map[b_group] |= BIT(b_pos); + + if (i % 2) /* BLE ch = even-freq (ex:2402,2404) */ + continue; + + b_group = (i - 2402) / 16; + b_pos = 7 - (((i - 2402) / 2) % 8); + wl->ch_map_le[b_group] |= BIT(b_pos); + } +} + +static u8 _get_w2b_fbd_ch(struct rtw89_dev *rtwdev, + u8 bt_fb_band, u32 wl_vco_freq, + u32 *ch_lo_bound, u32 *ch_up_bound) +{ + u32 ch_lo, ch_pup, ch_min, ch_max; + u8 out_of_range = 0; + s32 val_lo, val_up; + u32 vco_lo_in_KHz; /* VCO pulling lower bound */ + u32 vco_up_in_KHz; /* VCO pulling Upper bound */ + u32 freq_start; + + vco_lo_in_KHz = wl_vco_freq - BTC_VCO_GUARD_W2B * 1000; /* KHz */ + vco_up_in_KHz = wl_vco_freq + BTC_VCO_GUARD_W2B * 1000; + + switch (bt_fb_band) { + default: + case BTC_BT_B2G: /* BT 2GHz forbiddened Channel */ + val_lo = vco_lo_in_KHz * BTC_LO_2G_DNM / BTC_LO_2G_NMR; + val_up = vco_up_in_KHz * BTC_LO_2G_DNM / BTC_LO_2G_NMR; + freq_start = BTC_FREQ_B2G * 1000; /* KHz */ + ch_min = BTC_CH_B2G_MIN; + ch_max = BTC_CH_B2G_MAX; + break; + case BTC_BT_B5G: /* BT 5/6GHz forbiddened Channel */ + val_lo = vco_lo_in_KHz * BTC_LO_6G_DNM / BTC_LO_6G_NMR; + val_up = vco_up_in_KHz * BTC_LO_6G_DNM / BTC_LO_6G_NMR; + freq_start = BTC_FREQ_B6G * 1000; /* KHz */ + ch_min = BTC_CH_B5G_MIN; + ch_max = BTC_CH_B5G_MAX; + break; + } + + /* Search forbiddened Channel (un-process range)*/ + ch_lo = (val_lo - freq_start) / 1000; + if ((val_lo - freq_start) % 1000 != 0) /* Round-UP */ + ch_lo++; + + ch_pup = (val_up - freq_start) / 1000; /* Round-down */ + + /* Search forbiddened Channel */ + if (ch_lo > ch_max || ch_pup < ch_min) { /* out-of-range -> NA */ + out_of_range = 1; + *ch_lo_bound = 0; + *ch_up_bound = 0; + } + + if (ch_lo < ch_min) + *ch_lo_bound = ch_min; + else if (ch_lo <= ch_max) + *ch_lo_bound = ch_lo; + + if (ch_pup > ch_max) + *ch_up_bound = ch_max; + else if (ch_pup >= ch_min) + *ch_up_bound = ch_pup; + + if (bt_fb_band == BTC_BT_B5G) { /* BT ch is 8MHz-spacing for 5/6GHz */ + *ch_lo_bound = *ch_lo_bound / 8; /* low-bound -> Round-down */ + + if (*ch_up_bound % 8 != 0) /* Up-bound -> cover wider range */ + *ch_up_bound = *ch_up_bound / 8 + 1; + else + *ch_up_bound = *ch_up_bound / 8; + } + + return out_of_range; +} + +static void _set_wl_ch_info(struct rtw89_dev *rtwdev) +{ + static const u8 path_hwb[BTC_RF_NUM] = {RTW89_PHY_0, RTW89_PHY_1}; + struct rtw89_btc_dm *dm = &rtwdev->btc.dm; + struct rtw89_btc_cx *cx = &rtwdev->btc.cx; + struct rtw89_btc_wl_info *wl = &cx->wl; + u32 vco_freq, ch_lo = 0, ch_up = 0; + u16 rf_band, freq, f_diff; + u8 i, j, ch; + + if (!(rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA)) /* only new-PTA require setup */ + return; + + for (i = RTW89_PHY_0; i <= RTW89_PHY_1; i++) { + if (wl->mlo_info.wmode[i] == RTW89_MR_WMODE_NONE) { /* no-connect */ + dm->ost_info.rf_center_freq[i] = 0; + dm->ost_info.fbd_group_en[i][0] = 0; + dm->ost_info.fbd_group_en[i][1] = 0; + dm->ost_info.fbd_group_bound[i][0] = 0; + dm->ost_info.fbd_group_bound[i][1] = 0; + continue; + } + + ch = wl->rf_ch_info[i].center_ch; + switch (wl->rf_ch_info[i].band) { + case RTW89_BAND_2G: + default: + rf_band = 0; + if (ch == 14) + freq = BTC_FREQ_W2G_CH14; + else + freq = BTC_FREQ_W2G + (ch - 1) * 5; + vco_freq = freq * 1000 * BTC_LO_2G_NMR / BTC_LO_2G_DNM; + break; + case RTW89_BAND_5G: + rf_band = BIT(15); + freq = BTC_FREQ_W5G + (ch - 1) * 5; + vco_freq = freq * 1000 * BTC_LO_5G_NMR / BTC_LO_5G_DNM; + break; + case RTW89_BAND_6G: + rf_band = BIT(15); + freq = BTC_FREQ_W6G + (ch - 1) * 5; + vco_freq = freq * 1000 * BTC_LO_6G_NMR / BTC_LO_6G_DNM; + break; + } + + dm->ost_info.rf_center_freq[i] = rf_band + freq; + + for (j = BTC_BT_B2G; j <= BTC_BT_B5G; j++) { + /* no WL->BT forbidden channel */ + if (_get_w2b_fbd_ch(rtwdev, j, vco_freq, &ch_lo, &ch_up)) { + dm->ost_info.fbd_group_en[i][j] = j << 1; + dm->ost_info.fbd_group_bound[i][j] = 0; + continue; + } + + dm->ost_info.fbd_group_en[i][j] = (j << 1) + 0x1; + dm->ost_info.fbd_group_bound[i][j] = (ch_up << 8) + ch_lo; + } + } + + for (i = BTC_RF_S0; i <= BTC_RF_S1; i++) { + for (j = BTC_BT_1ST; j <= BTC_BT_EXT; j++) { + /* + * If WL & BT share same ANT/Path, set freq-diff > 2GHz, + * because TDD will always run in 5/6GHz band. + * Otherwise, the frq-diff thres is decided by the RF + * out-of-channel rejection capability. + */ + if (dm->ant_xmap[i][j]) + f_diff = 0x7ff; /* 2047MHz */ + else + f_diff = rtwdev->chip->fdd_iso_freq; + + dm->ost_info.freq_diff_thres[path_hwb[i]][j] = f_diff; + } } } static void _set_bt_afh_info(struct rtw89_dev *rtwdev) { - if (rtwdev->chip->chip_id == RTL8922A) - _set_bt_afh_info_v1(rtwdev); - else - _set_bt_afh_info_v0(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_wl_role_info *wl_rinfo = &wl->role_info; + struct rtw89_btc_wl_rlink *rlink; + u8 h2c_func, hwb, bt_link_chg; + u8 i, j, sz, bw; + u8 buf[4]; + + if (btc->manual_ctrl || wl->status.map.rf_off || + (wl->status.val & btc_scanning_map.val)) + return; + + memset(wl->afh_info, 0, sizeof(wl->afh_info)); + + for (j = RTW89_MAC_0; j < RTW89_MAC_NUM; j++) { + /* skip HW-Band if no fdd/fdd bind */ + if ((!(btc->dm.tdd_bind.wl_hwb_sel & BIT(j))) && + (!(btc->dm.fdd_bind.wl_hwb_sel & BIT(j)))) + continue; + + for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { + rlink = &wl_rinfo->rlink[i][j]; + + /* Skip no-active case */ + if (!rlink->connected || !rlink->active || + rlink->rf_band >= RTW89_BAND_NUM) + continue; + + /* In addition to AP/P2P, skip if same-rf-band*/ + if (wl->afh_info[j][rlink->rf_band].en && + (rlink->role != RTW89_WIFI_ROLE_AP && + rlink->role != RTW89_WIFI_ROLE_P2P_GO && + rlink->role != RTW89_WIFI_ROLE_P2P_CLIENT)) + continue; + + bw = _get_wl_guard_bw(rtwdev, rlink->bw); + wl->afh_info[j][rlink->rf_band].en = 1; + wl->afh_info[j][rlink->rf_band].ch = rlink->ch; + wl->afh_info[j][rlink->rf_band].bw = bw; + wl->afh_info[j][rlink->rf_band].band = rlink->rf_band; + } + } + + for (j = RTW89_BAND_2G; j < RTW89_BAND_NUM; j++) { + /* skip non-2.4GHz if BT is 2.4GHz only*/ + if (!(rtwdev->chip->para_ver & BTC_FEAT_BT_6G)) { + if (j != RTW89_BAND_2G) + continue; + sz = 3; + } else { + sz = 4; + } + + if (wl->afh_info[RTW89_MAC_0][j].en && + wl->afh_info[RTW89_MAC_1][j].en) { + /* select wilder bw if 1+1 */ + if (wl->afh_info[RTW89_MAC_1][j].bw > + wl->afh_info[RTW89_MAC_0][j].bw) + hwb = RTW89_MAC_1; + else + hwb = RTW89_MAC_0; + } else if (wl->afh_info[RTW89_MAC_1][j].en) { + hwb = RTW89_MAC_1; + } else { + hwb = RTW89_MAC_0; + } + + if (j == RTW89_BAND_2G) { + if (cx->bt0.link_info.link_cnt.chg || + cx->bt1.link_info.link_cnt.chg) + bt_link_chg = 1; + else + bt_link_chg = 0; + } else { + if (cx->bt0.link_info_56g.link_cnt.chg || + cx->bt1.link_info_56g.link_cnt.chg) + bt_link_chg = 1; + else + bt_link_chg = 0; + } + + /* return if no-afh-info-change/no wl-bt link change */ + if (!memcmp(&wl->afh_info_last[hwb][j], + &wl->afh_info[hwb][j], + sizeof(struct rtw89_btc_wl_afh_info)) && + !bt_link_chg && + !wl->link_mode_chg) + continue; + + memcpy(&wl->afh_info_last[hwb][j], + &wl->afh_info[hwb][j], + sizeof(struct rtw89_btc_wl_afh_info)); + + buf[0] = wl->afh_info[hwb][j].en; + buf[1] = wl->afh_info[hwb][j].ch; + buf[2] = wl->afh_info[hwb][j].bw; + buf[3] = wl->afh_info[hwb][j].band; + + h2c_func = SET_BT_WL_CH_INFO; + if (cx->bt0.enable.now) + _send_fw_cmd(rtwdev, BTFC_SET, h2c_func, buf, sz); + + /* send mailbox to 2nd-BT if exist */ + if (cx->bt1.enable.now) { + h2c_func |= BT_H2C_FUNC_BT2ND; + _send_fw_cmd(rtwdev, BTFC_SET, h2c_func, buf, sz); + } + + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): en=%d, ch=%d, bw=%d, rf_band=%d\n", + __func__, buf[0], buf[1], buf[2], buf[3]); + + wl->wcnt[BTC_WCNT_CH_UPDATE]++; + + /* ToDo: Remove if BT update new-define to support 6GHz */ + if (j == RTW89_BAND_2G) + break; + } + + /* for AFH map display in coex log */ + _set_wl_ch_map(rtwdev); + + /* set HWB center-ch/forbidden group to ost_info for CR setup in FW */ + _set_wl_ch_info(rtwdev); } static bool _check_freerun(struct rtw89_dev *rtwdev) @@ -3828,39 +3934,24 @@ static bool _check_freerun(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; - struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; - struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; - struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; struct rtw89_btc_bt_hid_desc *hid = &bt_linfo->hid_desc; union rtw89_btc_module_info *md = &btc->mdinfo; const struct rtw89_btc_ver *ver = btc->ver; - u8 isolation, connect_cnt = 0; + u8 isolation; if (ver->fcxinit == 7) isolation = md->md_v7.ant.isolation; else isolation = md->md.ant.isolation; - if (ver->fwlrole == 0) - connect_cnt = wl_rinfo->connect_cnt; - else if (ver->fwlrole == 1) - connect_cnt = wl_rinfo_v1->connect_cnt; - else if (ver->fwlrole == 2) - connect_cnt = wl_rinfo_v2->connect_cnt; - else if (ver->fwlrole == 7) - connect_cnt = wl_rinfo_v7->connect_cnt; - else if (ver->fwlrole == 8) - connect_cnt = wl_rinfo_v8->connect_cnt; - if (btc->ant_type == BTC_ANT_SHARED) { btc->dm.trx_para_level = 0; return false; } /* The below is dedicated antenna case */ - if (connect_cnt > BTC_TDMA_WLROLE_MAX) { + if (wl_rinfo->connect_cnt > BTC_TDMA_WLROLE_MAX) { btc->dm.trx_para_level = 5; return true; } @@ -4097,11 +4188,26 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) case BTC_CXP_OFFB: /* TDMA off + beacon protect */ tdma_on = false; *t = t_def[CXTD_OFF_B2]; - s[CXST_OFF] = s_def[CXST_OFF]; + _slot_set_le(btc, CXST_OFF, s_def[CXST_OFF].dur, + s_def[CXST_OFF].cxtbl, s_def[CXST_OFF].cxtype); + _slot_set(btc, CXST_B2, 10, cxtbl[1], SLOT_ISO); + switch (policy_type) { case BTC_CXP_OFFB_BWB0: + _slot_set_tbl(btc, CXST_OFF, cxtbl[5]); + break; + case BTC_CXP_OFFB_BWB1: _slot_set_tbl(btc, CXST_OFF, cxtbl[8]); break; + case BTC_CXP_OFFB_BWB2: + _slot_set_tbl(btc, CXST_OFF, cxtbl[7]); + break; + case BTC_CXP_OFFB_BWB3: + _slot_set_tbl(btc, CXST_OFF, cxtbl[6]); + break; + case BTC_CXP_OFFB_BWB4: + _slot_set_tbl(btc, CXST_OFF, cxtbl[2]); + break; } break; case BTC_CXP_OFFE: /* TDMA off + beacon protect + Ext_control */ @@ -4182,7 +4288,7 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) case BTC_CXP_PFIX: /* PS-TDMA Fix-Slot */ tdma_on = true; *t = t_def[CXTD_PFIX]; - if (btc->cx.wl.role_info.role_map.role.ap) + if (btc->cx.wl.role_info.role_map & BIT(RTW89_WIFI_ROLE_AP)) _tdma_set_flctrl(btc, CXFLC_QOSNULL); switch (policy_type) { @@ -4461,12 +4567,23 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type) *t = t_def[CXTD_OFF_B2]; _slot_set_le(btc, CXST_OFF, s_def[CXST_OFF].dur, s_def[CXST_OFF].cxtbl, s_def[CXST_OFF].cxtype); + _slot_set(btc, CXST_B2, 10, cxtbl[1], SLOT_ISO); switch (policy_type) { case BTC_CXP_OFFB_BWB0: + _slot_set_tbl(btc, CXST_OFF, cxtbl[5]); + break; + case BTC_CXP_OFFB_BWB1: _slot_set_tbl(btc, CXST_OFF, cxtbl[8]); break; - default: + case BTC_CXP_OFFB_BWB2: + _slot_set_tbl(btc, CXST_OFF, cxtbl[7]); + break; + case BTC_CXP_OFFB_BWB3: + _slot_set_tbl(btc, CXST_OFF, cxtbl[6]); + break; + case BTC_CXP_OFFB_BWB4: + _slot_set_tbl(btc, CXST_OFF, cxtbl[2]); break; } break; @@ -4841,35 +4958,17 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec, struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_wl_role_info *r = &btc->cx.wl.role_info; struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; - u8 gwl, gwl0, gwl1, gbt, plt_ctrl, i, dbcc_2g_phy, b2g = 0; - bool dbcc_chg = false, dbcc_en = false; + u8 gwl, gwl0, gwl1, gbt, plt_ctrl, i, b2g = 0; u32 ant_path_type; ant_path_type = ((phy_map << 8) + type); - if (btc->ver->fwlrole == 1) { - dbcc_chg = wl->role_info_v1.dbcc_chg; - dbcc_en = wl->role_info_v1.dbcc_en; - dbcc_2g_phy = wl->role_info_v1.dbcc_2g_phy; - } else if (btc->ver->fwlrole == 2) { - dbcc_chg = wl->role_info_v2.dbcc_chg; - dbcc_en = wl->role_info_v2.dbcc_en; - dbcc_2g_phy = wl->role_info_v2.dbcc_2g_phy; - } else if (btc->ver->fwlrole == 7) { - dbcc_chg = wl->role_info_v7.dbcc_chg; - dbcc_en = wl->role_info_v7.dbcc_en; - dbcc_2g_phy = wl->role_info_v7.dbcc_2g_phy; - } else if (btc->ver->fwlrole == 8) { - dbcc_chg = wl->role_info_v8.dbcc_chg; - dbcc_en = wl->role_info_v8.dbcc_en; - dbcc_2g_phy = wl->role_info_v8.dbcc_2g_phy; - } - if (btc->dm.run_reason == BTC_RSN_NTFY_POWEROFF || btc->dm.run_reason == BTC_RSN_NTFY_RADIO_STATE || - btc->dm.run_reason == BTC_RSN_CMD_SET_COEX || dbcc_chg) + btc->dm.run_reason == BTC_RSN_CMD_SET_COEX || r->dbcc_chg) force_exec = FC_EXEC; if (!force_exec && ant_path_type == dm->set_ant_path) { @@ -4967,12 +5066,12 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec, default: gbt = BTC_GNT_HW; if ((rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA) || - !dbcc_en) { + !r->dbcc_en) { gwl = BTC_GNT_HW; _set_gnt(rtwdev, BTC_PHY_ALL, gwl, gbt); } else { /* for DBCC Only-1-PTA */ - if (dbcc_2g_phy == RTW89_PHY_0) { + if (r->dbcc_2g_phy == RTW89_PHY_0) { gwl0 = BTC_GNT_HW; gwl1 = BTC_GNT_SW_HI; } else { @@ -4994,7 +5093,7 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec, struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; + struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; u32 ant_path_type = rtw89_get_antpath_type(phy_map, type); struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; struct rtw89_btc_dm *dm = &btc->dm; @@ -5005,7 +5104,7 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec, btc->dm.run_reason == BTC_RSN_CMD_SET_COEX || wl_rinfo->dbcc_chg) force_exec = FC_EXEC; - if (wl_rinfo->link_mode != BTC_WLINK_25G_MCC && + if (wl_rinfo->link_mode != BTC_WLINK_DB_MCC && btc->dm.wl_btg_rx == 2) force_exec = FC_EXEC; @@ -5111,7 +5210,7 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec, static void _set_ant(struct rtw89_dev *rtwdev, bool force_exec, u8 phy_map, u8 type) { - if (rtwdev->chip->chip_id == RTL8922A) + if (rtwdev->chip->chip_id >= RTL8922A) _set_ant_v1(rtwdev, force_exec, phy_map, type); else _set_ant_v0(rtwdev, force_exec, phy_map, type); @@ -5134,29 +5233,24 @@ static void _action_wl_init(struct rtw89_dev *rtwdev) static void _action_wl_off(struct rtw89_dev *rtwdev, u8 mode) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_cx *cx = &btc->cx; + struct rtw89_btc_wl_info *wl = &cx->wl; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): !!\n", __func__); - if (wl->status.map.rf_off || btc->dm.bt_only) { + if (wl->status.map.rf_off || btc->dm.bt_only) _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_WOFF); - } else if (wl->status.map.lps == BTC_LPS_RF_ON) { - if (mode == BTC_WLINK_5G) - _set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_W5G); - else - _set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); - } + else if (wl->status.map.lps == BTC_LPS_RF_ON || + cx->bt0.whql_test || cx->bt1.whql_test) + _set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_PTA); - if (mode == BTC_WLINK_5G) { + if (dm->out_of_band) _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_OFF); - } else if (wl->status.map.lps == BTC_LPS_RF_ON) { - if (btc->cx.bt0.link_info.a2dp_desc.active) - _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_WL_OFF); - else - _set_policy(rtwdev, BTC_CXP_OFF_BWB1, BTC_ACT_WL_OFF); - } else { + else if (cx->bt0.whql_test || cx->bt1.whql_test) + _set_policy(rtwdev, BTC_CXP_OFFB_BWB4, BTC_ACT_WL_OFF); + else _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_WL_OFF); - } } static void _action_freerun(struct rtw89_dev *rtwdev) @@ -5563,48 +5657,29 @@ static void _action_wl_rfk(struct rtw89_dev *rtwdev) static void _set_btg_ctrl(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; - struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; - struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; - struct rtw89_btc_fbtc_outsrc_set_info *o_info = &btc->dm.ost_info; - struct rtw89_btc_wl_role_info *wl_rinfo_v0 = &wl->role_info; - const struct rtw89_chip_info *chip = rtwdev->chip; - const struct rtw89_btc_ver *ver = btc->ver; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; - struct _wl_rinfo_now wl_rinfo; + struct rtw89_btc_fbtc_outsrc_set_info *o_info = &dm->ost_info; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_btc_bt_info *bt = &btc->cx.bt0; u32 is_btg = BTC_BTGCTRL_DISABLE; if (btc->manual_ctrl) return; - if (ver->fwlrole == 0) - wl_rinfo.link_mode = wl_rinfo_v0->link_mode; - else if (ver->fwlrole == 1) - wl_rinfo.link_mode = wl_rinfo_v1->link_mode; - else if (ver->fwlrole == 2) - wl_rinfo.link_mode = wl_rinfo_v2->link_mode; - else if (ver->fwlrole == 7) - wl_rinfo.link_mode = wl_rinfo_v7->link_mode; - else if (ver->fwlrole == 8) - wl_rinfo.link_mode = wl_rinfo_v8->link_mode; - else - return; - /* notify halbb ignore GNT_BT or not for WL BB Rx-AGC control */ if (btc->ant_type == BTC_ANT_SHARED) { if (!(bt->run_patch_code && bt->enable.now)) is_btg = BTC_BTGCTRL_DISABLE; - else if (wl_rinfo.link_mode != BTC_WLINK_5G) + else if (wl_rinfo->link_mode_v0 != BTC_WLINK_V0_5G) is_btg = BTC_BTGCTRL_ENABLE; else is_btg = BTC_BTGCTRL_DISABLE; /* bb call ctrl_btg() in WL FW by slot */ - if (!ver->fcxosi && - wl_rinfo.link_mode == BTC_WLINK_25G_MCC) + if (!btc->ver->fcxosi && + wl_rinfo->link_mode_v0 == BTC_WLINK_V0_25G_MCC) is_btg = BTC_BTGCTRL_BB_GNT_FWCTRL; } @@ -5614,7 +5689,7 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev) dm->wl_btg_rx = is_btg; /* skip setup if btg_ctrl set by wl fw */ - if (!ver->fcxosi && is_btg > BTC_BTGCTRL_ENABLE) + if (!btc->ver->fcxosi && is_btg > BTC_BTGCTRL_ENABLE) return; /* Below flow is for BTC_FEAT_NEW_BBAPI_FLOW = 1 */ @@ -5633,7 +5708,7 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev) o_info->btg_rx[BTC_RF_S1] = is_btg; } - if (ver->fcxosi) + if (btc->ver->fcxosi) return; chip->ops->ctrl_btg_bt_rx(rtwdev, o_info->btg_rx[BTC_RF_S0], @@ -5651,40 +5726,20 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev) struct rtw89_btc_fbtc_outsrc_set_info *o_info = &btc->dm.ost_info; struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt0.link_info; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_role_info_v2 *rinfo_v2 = &wl->role_info_v2; - struct rtw89_btc_wl_role_info_v7 *rinfo_v7 = &wl->role_info_v7; - struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &wl->role_info_v8; + struct rtw89_btc_wl_role_info *rinfo = &wl->role_info; const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; - u8 is_preagc, val, link_mode, dbcc_2g_phy; - u8 role_ver = rtwdev->btc.ver->fwlrole; - bool dbcc_en; + u8 is_preagc, val; if (btc->manual_ctrl) return; - if (role_ver == 2) { - dbcc_en = rinfo_v2->dbcc_en; - link_mode = rinfo_v2->link_mode; - dbcc_2g_phy = rinfo_v2->dbcc_2g_phy; - } else if (role_ver == 7) { - dbcc_en = rinfo_v7->dbcc_en; - link_mode = rinfo_v7->link_mode; - dbcc_2g_phy = rinfo_v7->dbcc_2g_phy; - } else if (role_ver == 8) { - dbcc_en = rinfo_v8->dbcc_en; - link_mode = rinfo_v8->link_mode; - dbcc_2g_phy = rinfo_v7->dbcc_2g_phy; - } else { - return; - } - if (!(bt->run_patch_code && bt->enable.now)) { is_preagc = BTC_PREAGC_DISABLE; - } else if (link_mode == BTC_WLINK_5G) { + } else if (dm->tdd_bind.rf_band == BIT(RTW89_BAND_5G)) { is_preagc = BTC_PREAGC_DISABLE; - } else if (link_mode == BTC_WLINK_NOLINK || + } else if (rinfo->link_mode == BTC_WLINK_NOLINK || btc->cx.bt0.link_info.link_cnt.now == 0) { is_preagc = BTC_PREAGC_DISABLE; } else if (dm->tdma_now.type != CXTDMA_OFF && @@ -5692,7 +5747,7 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev) !bt_linfo->hid_desc.exist && dm->fddt_train == BTC_FDDT_DISABLE) { is_preagc = BTC_PREAGC_DISABLE; - } else if (dbcc_en && (dbcc_2g_phy != RTW89_PHY_1)) { + } else if (rinfo->dbcc_en && (rinfo->dbcc_2g_phy != RTW89_PHY_1)) { is_preagc = BTC_PREAGC_DISABLE; } else if (btc->ant_type == BTC_ANT_SHARED) { is_preagc = BTC_PREAGC_DISABLE; @@ -5700,7 +5755,7 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev) is_preagc = BTC_PREAGC_ENABLE; } - if (!btc->ver->fcxosi && link_mode == BTC_WLINK_25G_MCC) + if (!btc->ver->fcxosi && rinfo->link_mode == BTC_WLINK_DB_MCC) is_preagc = BTC_PREAGC_BB_FWCTRL; if (dm->wl_pre_agc_rb != dm->wl_pre_agc && @@ -5771,10 +5826,7 @@ static void __rtw89_tx_time_iter(struct rtw89_vif_link *rtwvif_link, u16 enable = iter_data->enable; bool reenable = iter_data->reenable; - if (btc->ver->fwlrole == 8) - plink = &wl->rlink_info[port][0]; - else - plink = &wl->link_info[port]; + plink = &wl->rlink_info[port][rtwsta_link->link_id]; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): port = %d\n", __func__, port); @@ -5840,39 +5892,23 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev) struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc; struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; - struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; - struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; - struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; struct rtw89_txtime_data data = {.rtwdev = rtwdev}; - u8 mode, igno_bt, tx_retry; + bool reenable = false; + u8 igno_bt, tx_retry; u32 tx_time; u16 enable; - bool reenable = false; if (btc->manual_ctrl) return; - if (ver->fwlrole == 0) - mode = wl_rinfo->link_mode; - else if (ver->fwlrole == 1) - mode = wl_rinfo_v1->link_mode; - else if (ver->fwlrole == 2) - mode = wl_rinfo_v2->link_mode; - else if (ver->fwlrole == 7) - mode = wl_rinfo_v7->link_mode; - else if (ver->fwlrole == 8) - mode = wl_rinfo_v8->link_mode; - else - return; - if (ver->fcxctrl == 7) igno_bt = btc->ctrl.ctrl_v7.igno_bt; else igno_bt = btc->ctrl.ctrl.igno_bt; if (btc->dm.freerun || igno_bt || b->link_cnt.now == 0 || - mode == BTC_WLINK_5G || mode == BTC_WLINK_NOLINK) { + dm->tdd_bind.rf_band == BIT(RTW89_BAND_5G) || + wl_rinfo->link_mode == BTC_WLINK_NOLINK) { enable = 0; tx_time = BTC_MAX_TX_TIME_DEF; tx_retry = BTC_MAX_TX_RETRY_DEF; @@ -5915,31 +5951,12 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev) static void _set_bt_rx_agc(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; - struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; - struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; - struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; bool bt_hi_lna_rx = false; - u8 mode; - if (ver->fwlrole == 0) - mode = wl_rinfo->link_mode; - else if (ver->fwlrole == 1) - mode = wl_rinfo_v1->link_mode; - else if (ver->fwlrole == 2) - mode = wl_rinfo_v2->link_mode; - else if (ver->fwlrole == 7) - mode = wl_rinfo_v7->link_mode; - else if (ver->fwlrole == 8) - mode = wl_rinfo_v8->link_mode; - else - return; - - if (mode != BTC_WLINK_NOLINK && btc->dm.wl_btg_rx) + if (wl_rinfo->link_mode != BTC_WLINK_NOLINK && btc->dm.wl_btg_rx) bt_hi_lna_rx = true; if (bt_hi_lna_rx == bt->hi_lna_rx) @@ -5985,20 +6002,51 @@ static void _wl_req_mac(struct rtw89_dev *rtwdev, u8 mac) rtw89_write32_set(rtwdev, add, B_AX_WL_SRC); } +static void _update_zb_coex_tbl(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + const struct rtw89_btc_ver *ver = btc->ver; + u32 zb_tbl0 = 0xda5a5a5a, zb_tbl1 = 0xda5a5a5a; + u8 link_mode_chg = btc->cx.wl.link_mode_chg; + u8 mode = btc->cx.wl.role_info.link_mode; + u8 wa_type; + + if (btc->dm.run_reason != BTC_RSN_NTFY_INIT && !link_mode_chg) + return; + + if (ver->fcxinit == 7) + wa_type = btc->mdinfo.md_v7.wa_type; + else + wa_type = btc->mdinfo.md.wa_type; + + if (!(wa_type & BTC_WA_HFP_ZB)) + return; + + if (btc->dm.tdd_bind.rf_band == BIT(RTW89_BAND_5G) || + rtwdev->btc.dm.freerun) { + zb_tbl0 = 0xffffffff; + zb_tbl1 = 0xffffffff; + } else if (mode == BTC_WLINK_DB_MCC) { + zb_tbl0 = 0xffffffff; /* for E5G slot */ + zb_tbl1 = 0xda5a5a5a; /* for E2G slot */ + } + rtw89_write32(rtwdev, R_BTC_ZB_COEX_TBL_0, zb_tbl0); + rtw89_write32(rtwdev, R_BTC_ZB_COEX_TBL_1, zb_tbl1); +} + static void _action_common(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &wl->role_info_v8; + struct rtw89_btc_wl_role_info *rinfo = &wl->role_info; struct rtw89_btc_wl_smap *wl_smap = &wl->status.map; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; u32 bt_rom_code_id, bt_fw_ver; u8 i; - if (btc->ver->fwlrole == 8) - _wl_req_mac(rtwdev, rinfo_v8->pta_req_band); - + _wl_req_mac(rtwdev, rinfo->pta_req_band); + _update_zb_coex_tbl(rtwdev); _set_btg_ctrl(rtwdev); _set_wl_preagc_ctrl(rtwdev); _set_wl_tx_limit(rtwdev); @@ -6041,7 +6089,10 @@ static void _action_common(struct rtw89_dev *rtwdev) } dm->tdma_instant_excute = 0; dm->lps_ctrl_change = false; + wl->role_info.link_mode_chg = 0; wl->pta_reg_mac_chg = false; + dm->pre_agc_chg = false; + wl->dbcc_chg = 0; } static void _action_by_bt(struct rtw89_dev *rtwdev) @@ -6407,473 +6458,29 @@ void _update_dbcc_band(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) btc->cx.wl.dbcc_info.op_band[phy_idx]; } -static void _update_wl_info(struct rtw89_dev *rtwdev) -{ - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info; - struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; - struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; - u8 i, cnt_connect = 0, cnt_connecting = 0, cnt_active = 0; - u8 cnt_2g = 0, cnt_5g = 0, phy; - u32 wl_2g_ch[2] = {0}, wl_5g_ch[2] = {0}; - bool b2g = false, b5g = false, client_joined = false; - - memset(wl_rinfo, 0, sizeof(*wl_rinfo)); - - for (i = 0; i < RTW89_PORT_NUM; i++) { - /* check if role active? */ - if (!wl_linfo[i].active) - continue; - - cnt_active++; - wl_rinfo->active_role[cnt_active - 1].role = wl_linfo[i].role; - wl_rinfo->active_role[cnt_active - 1].pid = wl_linfo[i].pid; - wl_rinfo->active_role[cnt_active - 1].phy = wl_linfo[i].phy; - wl_rinfo->active_role[cnt_active - 1].band = wl_linfo[i].band; - wl_rinfo->active_role[cnt_active - 1].noa = (u8)wl_linfo[i].noa; - wl_rinfo->active_role[cnt_active - 1].connected = 0; - - wl->port_id[wl_linfo[i].role] = wl_linfo[i].pid; - - phy = wl_linfo[i].phy; - - /* check dbcc role */ - if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) { - wl_dinfo->role[phy] = wl_linfo[i].role; - wl_dinfo->op_band[phy] = wl_linfo[i].band; - _update_dbcc_band(rtwdev, phy); - _fw_set_drv_info(rtwdev, CXDRVINFO_DBCC); - } - - if (wl_linfo[i].connected == MLME_NO_LINK) { - continue; - } else if (wl_linfo[i].connected == MLME_LINKING) { - cnt_connecting++; - } else { - cnt_connect++; - if ((wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO || - wl_linfo[i].role == RTW89_WIFI_ROLE_AP) && - wl_linfo[i].client_cnt > 1) - client_joined = true; - } - - wl_rinfo->role_map.val |= BIT(wl_linfo[i].role); - wl_rinfo->active_role[cnt_active - 1].ch = wl_linfo[i].ch; - wl_rinfo->active_role[cnt_active - 1].bw = wl_linfo[i].bw; - wl_rinfo->active_role[cnt_active - 1].connected = 1; - - /* only care 2 roles + BT coex */ - if (wl_linfo[i].band != RTW89_BAND_2G) { - if (cnt_5g <= ARRAY_SIZE(wl_5g_ch) - 1) - wl_5g_ch[cnt_5g] = wl_linfo[i].ch; - cnt_5g++; - b5g = true; - } else { - if (cnt_2g <= ARRAY_SIZE(wl_2g_ch) - 1) - wl_2g_ch[cnt_2g] = wl_linfo[i].ch; - cnt_2g++; - b2g = true; - } - } - - wl_rinfo->connect_cnt = cnt_connect; - - /* Be careful to change the following sequence!! */ - if (cnt_connect == 0) { - wl_rinfo->link_mode = BTC_WLINK_NOLINK; - wl_rinfo->role_map.role.none = 1; - } else if (!b2g && b5g) { - wl_rinfo->link_mode = BTC_WLINK_5G; - } else if (wl_rinfo->role_map.role.nan) { - wl_rinfo->link_mode = BTC_WLINK_2G_NAN; - } else if (cnt_connect > BTC_TDMA_WLROLE_MAX) { - wl_rinfo->link_mode = BTC_WLINK_OTHER; - } else if (b2g && b5g && cnt_connect == 2) { - if (rtwdev->dbcc_en) { - switch (wl_dinfo->role[RTW89_PHY_0]) { - case RTW89_WIFI_ROLE_STATION: - wl_rinfo->link_mode = BTC_WLINK_2G_STA; - break; - case RTW89_WIFI_ROLE_P2P_GO: - wl_rinfo->link_mode = BTC_WLINK_2G_GO; - break; - case RTW89_WIFI_ROLE_P2P_CLIENT: - wl_rinfo->link_mode = BTC_WLINK_2G_GC; - break; - case RTW89_WIFI_ROLE_AP: - wl_rinfo->link_mode = BTC_WLINK_2G_AP; - break; - default: - wl_rinfo->link_mode = BTC_WLINK_OTHER; - break; - } - } else { - wl_rinfo->link_mode = BTC_WLINK_25G_MCC; - } - } else if (!b5g && cnt_connect == 2) { - if (wl_rinfo->role_map.role.station && - (wl_rinfo->role_map.role.p2p_go || - wl_rinfo->role_map.role.p2p_gc || - wl_rinfo->role_map.role.ap)) { - if (wl_2g_ch[0] == wl_2g_ch[1]) - wl_rinfo->link_mode = BTC_WLINK_2G_SCC; - else - wl_rinfo->link_mode = BTC_WLINK_2G_MCC; - } else { - wl_rinfo->link_mode = BTC_WLINK_2G_MCC; - } - } else if (!b5g && cnt_connect == 1) { - if (wl_rinfo->role_map.role.station) - wl_rinfo->link_mode = BTC_WLINK_2G_STA; - else if (wl_rinfo->role_map.role.ap) - wl_rinfo->link_mode = BTC_WLINK_2G_AP; - else if (wl_rinfo->role_map.role.p2p_go) - wl_rinfo->link_mode = BTC_WLINK_2G_GO; - else if (wl_rinfo->role_map.role.p2p_gc) - wl_rinfo->link_mode = BTC_WLINK_2G_GC; - else - wl_rinfo->link_mode = BTC_WLINK_OTHER; - } - - /* if no client_joined, don't care P2P-GO/AP role */ - if (wl_rinfo->role_map.role.p2p_go || wl_rinfo->role_map.role.ap) { - if (!client_joined) { - if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC || - wl_rinfo->link_mode == BTC_WLINK_2G_MCC) { - wl_rinfo->link_mode = BTC_WLINK_2G_STA; - wl_rinfo->connect_cnt = 1; - } else if (wl_rinfo->link_mode == BTC_WLINK_2G_GO || - wl_rinfo->link_mode == BTC_WLINK_2G_AP) { - wl_rinfo->link_mode = BTC_WLINK_NOLINK; - wl_rinfo->connect_cnt = 0; - } - } - } - - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], cnt_connect = %d, connecting = %d, link_mode = %d\n", - cnt_connect, cnt_connecting, wl_rinfo->link_mode); - - _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); -} - -static void _update_wl_info_v1(struct rtw89_dev *rtwdev) -{ - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info; - struct rtw89_btc_wl_role_info_v1 *wl_rinfo = &wl->role_info_v1; - struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; - u8 cnt_connect = 0, cnt_connecting = 0, cnt_active = 0; - u8 cnt_2g = 0, cnt_5g = 0, phy; - u32 wl_2g_ch[2] = {}, wl_5g_ch[2] = {}; - bool b2g = false, b5g = false, client_joined = false; - u8 i; - - memset(wl_rinfo, 0, sizeof(*wl_rinfo)); - - for (i = 0; i < RTW89_PORT_NUM; i++) { - if (!wl_linfo[i].active) - continue; - - cnt_active++; - wl_rinfo->active_role_v1[cnt_active - 1].role = wl_linfo[i].role; - wl_rinfo->active_role_v1[cnt_active - 1].pid = wl_linfo[i].pid; - wl_rinfo->active_role_v1[cnt_active - 1].phy = wl_linfo[i].phy; - wl_rinfo->active_role_v1[cnt_active - 1].band = wl_linfo[i].band; - wl_rinfo->active_role_v1[cnt_active - 1].noa = (u8)wl_linfo[i].noa; - wl_rinfo->active_role_v1[cnt_active - 1].connected = 0; - - wl->port_id[wl_linfo[i].role] = wl_linfo[i].pid; - - phy = wl_linfo[i].phy; - - if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) { - wl_dinfo->role[phy] = wl_linfo[i].role; - wl_dinfo->op_band[phy] = wl_linfo[i].band; - _update_dbcc_band(rtwdev, phy); - _fw_set_drv_info(rtwdev, CXDRVINFO_DBCC); - } - - if (wl_linfo[i].connected == MLME_NO_LINK) { - continue; - } else if (wl_linfo[i].connected == MLME_LINKING) { - cnt_connecting++; - } else { - cnt_connect++; - if ((wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO || - wl_linfo[i].role == RTW89_WIFI_ROLE_AP) && - wl_linfo[i].client_cnt > 1) - client_joined = true; - } - - wl_rinfo->role_map.val |= BIT(wl_linfo[i].role); - wl_rinfo->active_role_v1[cnt_active - 1].ch = wl_linfo[i].ch; - wl_rinfo->active_role_v1[cnt_active - 1].bw = wl_linfo[i].bw; - wl_rinfo->active_role_v1[cnt_active - 1].connected = 1; - - /* only care 2 roles + BT coex */ - if (wl_linfo[i].band != RTW89_BAND_2G) { - if (cnt_5g <= ARRAY_SIZE(wl_5g_ch) - 1) - wl_5g_ch[cnt_5g] = wl_linfo[i].ch; - cnt_5g++; - b5g = true; - } else { - if (cnt_2g <= ARRAY_SIZE(wl_2g_ch) - 1) - wl_2g_ch[cnt_2g] = wl_linfo[i].ch; - cnt_2g++; - b2g = true; - } - } - - wl_rinfo->connect_cnt = cnt_connect; - - /* Be careful to change the following sequence!! */ - if (cnt_connect == 0) { - wl_rinfo->link_mode = BTC_WLINK_NOLINK; - wl_rinfo->role_map.role.none = 1; - } else if (!b2g && b5g) { - wl_rinfo->link_mode = BTC_WLINK_5G; - } else if (wl_rinfo->role_map.role.nan) { - wl_rinfo->link_mode = BTC_WLINK_2G_NAN; - } else if (cnt_connect > BTC_TDMA_WLROLE_MAX) { - wl_rinfo->link_mode = BTC_WLINK_OTHER; - } else if (b2g && b5g && cnt_connect == 2) { - if (rtwdev->dbcc_en) { - switch (wl_dinfo->role[RTW89_PHY_0]) { - case RTW89_WIFI_ROLE_STATION: - wl_rinfo->link_mode = BTC_WLINK_2G_STA; - break; - case RTW89_WIFI_ROLE_P2P_GO: - wl_rinfo->link_mode = BTC_WLINK_2G_GO; - break; - case RTW89_WIFI_ROLE_P2P_CLIENT: - wl_rinfo->link_mode = BTC_WLINK_2G_GC; - break; - case RTW89_WIFI_ROLE_AP: - wl_rinfo->link_mode = BTC_WLINK_2G_AP; - break; - default: - wl_rinfo->link_mode = BTC_WLINK_OTHER; - break; - } - } else { - wl_rinfo->link_mode = BTC_WLINK_25G_MCC; - } - } else if (!b5g && cnt_connect == 2) { - if (wl_rinfo->role_map.role.station && - (wl_rinfo->role_map.role.p2p_go || - wl_rinfo->role_map.role.p2p_gc || - wl_rinfo->role_map.role.ap)) { - if (wl_2g_ch[0] == wl_2g_ch[1]) - wl_rinfo->link_mode = BTC_WLINK_2G_SCC; - else - wl_rinfo->link_mode = BTC_WLINK_2G_MCC; - } else { - wl_rinfo->link_mode = BTC_WLINK_2G_MCC; - } - } else if (!b5g && cnt_connect == 1) { - if (wl_rinfo->role_map.role.station) - wl_rinfo->link_mode = BTC_WLINK_2G_STA; - else if (wl_rinfo->role_map.role.ap) - wl_rinfo->link_mode = BTC_WLINK_2G_AP; - else if (wl_rinfo->role_map.role.p2p_go) - wl_rinfo->link_mode = BTC_WLINK_2G_GO; - else if (wl_rinfo->role_map.role.p2p_gc) - wl_rinfo->link_mode = BTC_WLINK_2G_GC; - else - wl_rinfo->link_mode = BTC_WLINK_OTHER; - } - - /* if no client_joined, don't care P2P-GO/AP role */ - if (wl_rinfo->role_map.role.p2p_go || wl_rinfo->role_map.role.ap) { - if (!client_joined) { - if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC || - wl_rinfo->link_mode == BTC_WLINK_2G_MCC) { - wl_rinfo->link_mode = BTC_WLINK_2G_STA; - wl_rinfo->connect_cnt = 1; - } else if (wl_rinfo->link_mode == BTC_WLINK_2G_GO || - wl_rinfo->link_mode == BTC_WLINK_2G_AP) { - wl_rinfo->link_mode = BTC_WLINK_NOLINK; - wl_rinfo->connect_cnt = 0; - } - } - } - - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], cnt_connect = %d, connecting = %d, link_mode = %d\n", - cnt_connect, cnt_connecting, wl_rinfo->link_mode); - - _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); -} - -static void _update_wl_info_v2(struct rtw89_dev *rtwdev) -{ - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info; - struct rtw89_btc_wl_role_info_v2 *wl_rinfo = &wl->role_info_v2; - struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; - u8 cnt_connect = 0, cnt_connecting = 0, cnt_active = 0; - u8 cnt_2g = 0, cnt_5g = 0, phy; - u32 wl_2g_ch[2] = {}, wl_5g_ch[2] = {}; - bool b2g = false, b5g = false, client_joined = false; - u8 i; - - memset(wl_rinfo, 0, sizeof(*wl_rinfo)); - - for (i = 0; i < RTW89_PORT_NUM; i++) { - if (!wl_linfo[i].active) - continue; - - cnt_active++; - wl_rinfo->active_role_v2[cnt_active - 1].role = wl_linfo[i].role; - wl_rinfo->active_role_v2[cnt_active - 1].pid = wl_linfo[i].pid; - wl_rinfo->active_role_v2[cnt_active - 1].phy = wl_linfo[i].phy; - wl_rinfo->active_role_v2[cnt_active - 1].band = wl_linfo[i].band; - wl_rinfo->active_role_v2[cnt_active - 1].noa = (u8)wl_linfo[i].noa; - wl_rinfo->active_role_v2[cnt_active - 1].connected = 0; - - wl->port_id[wl_linfo[i].role] = wl_linfo[i].pid; - - phy = wl_linfo[i].phy; - - if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) { - wl_dinfo->role[phy] = wl_linfo[i].role; - wl_dinfo->op_band[phy] = wl_linfo[i].band; - _update_dbcc_band(rtwdev, phy); - _fw_set_drv_info(rtwdev, CXDRVINFO_DBCC); - } - - if (wl_linfo[i].connected == MLME_NO_LINK) { - continue; - } else if (wl_linfo[i].connected == MLME_LINKING) { - cnt_connecting++; - } else { - cnt_connect++; - if ((wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO || - wl_linfo[i].role == RTW89_WIFI_ROLE_AP) && - wl_linfo[i].client_cnt > 1) - client_joined = true; - } - - wl_rinfo->role_map.val |= BIT(wl_linfo[i].role); - wl_rinfo->active_role_v2[cnt_active - 1].ch = wl_linfo[i].ch; - wl_rinfo->active_role_v2[cnt_active - 1].bw = wl_linfo[i].bw; - wl_rinfo->active_role_v2[cnt_active - 1].connected = 1; - - /* only care 2 roles + BT coex */ - if (wl_linfo[i].band != RTW89_BAND_2G) { - if (cnt_5g <= ARRAY_SIZE(wl_5g_ch) - 1) - wl_5g_ch[cnt_5g] = wl_linfo[i].ch; - cnt_5g++; - b5g = true; - } else { - if (cnt_2g <= ARRAY_SIZE(wl_2g_ch) - 1) - wl_2g_ch[cnt_2g] = wl_linfo[i].ch; - cnt_2g++; - b2g = true; - } - } - - wl_rinfo->connect_cnt = cnt_connect; - - /* Be careful to change the following sequence!! */ - if (cnt_connect == 0) { - wl_rinfo->link_mode = BTC_WLINK_NOLINK; - wl_rinfo->role_map.role.none = 1; - } else if (!b2g && b5g) { - wl_rinfo->link_mode = BTC_WLINK_5G; - } else if (wl_rinfo->role_map.role.nan) { - wl_rinfo->link_mode = BTC_WLINK_2G_NAN; - } else if (cnt_connect > BTC_TDMA_WLROLE_MAX) { - wl_rinfo->link_mode = BTC_WLINK_OTHER; - } else if (b2g && b5g && cnt_connect == 2) { - if (rtwdev->dbcc_en) { - switch (wl_dinfo->role[RTW89_PHY_0]) { - case RTW89_WIFI_ROLE_STATION: - wl_rinfo->link_mode = BTC_WLINK_2G_STA; - break; - case RTW89_WIFI_ROLE_P2P_GO: - wl_rinfo->link_mode = BTC_WLINK_2G_GO; - break; - case RTW89_WIFI_ROLE_P2P_CLIENT: - wl_rinfo->link_mode = BTC_WLINK_2G_GC; - break; - case RTW89_WIFI_ROLE_AP: - wl_rinfo->link_mode = BTC_WLINK_2G_AP; - break; - default: - wl_rinfo->link_mode = BTC_WLINK_OTHER; - break; - } - } else { - wl_rinfo->link_mode = BTC_WLINK_25G_MCC; - } - } else if (!b5g && cnt_connect == 2) { - if (wl_rinfo->role_map.role.station && - (wl_rinfo->role_map.role.p2p_go || - wl_rinfo->role_map.role.p2p_gc || - wl_rinfo->role_map.role.ap)) { - if (wl_2g_ch[0] == wl_2g_ch[1]) - wl_rinfo->link_mode = BTC_WLINK_2G_SCC; - else - wl_rinfo->link_mode = BTC_WLINK_2G_MCC; - } else { - wl_rinfo->link_mode = BTC_WLINK_2G_MCC; - } - } else if (!b5g && cnt_connect == 1) { - if (wl_rinfo->role_map.role.station) - wl_rinfo->link_mode = BTC_WLINK_2G_STA; - else if (wl_rinfo->role_map.role.ap) - wl_rinfo->link_mode = BTC_WLINK_2G_AP; - else if (wl_rinfo->role_map.role.p2p_go) - wl_rinfo->link_mode = BTC_WLINK_2G_GO; - else if (wl_rinfo->role_map.role.p2p_gc) - wl_rinfo->link_mode = BTC_WLINK_2G_GC; - else - wl_rinfo->link_mode = BTC_WLINK_OTHER; - } - - /* if no client_joined, don't care P2P-GO/AP role */ - if (wl_rinfo->role_map.role.p2p_go || wl_rinfo->role_map.role.ap) { - if (!client_joined) { - if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC || - wl_rinfo->link_mode == BTC_WLINK_2G_MCC) { - wl_rinfo->link_mode = BTC_WLINK_2G_STA; - wl_rinfo->connect_cnt = 1; - } else if (wl_rinfo->link_mode == BTC_WLINK_2G_GO || - wl_rinfo->link_mode == BTC_WLINK_2G_AP) { - wl_rinfo->link_mode = BTC_WLINK_NOLINK; - wl_rinfo->connect_cnt = 0; - } - } - } - - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], cnt_connect = %d, connecting = %d, link_mode = %d\n", - cnt_connect, cnt_connecting, wl_rinfo->link_mode); - - _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); -} - #define BTC_CHK_HANG_MAX 3 #define BTC_SCB_INV_VALUE GENMASK(31, 0) -static u8 _get_role_link_mode(u8 role) +static u8 _get_role_link_mode(struct rtw89_btc_wl_role_info *r, u8 role, bool notv10) { switch (role) { case RTW89_WIFI_ROLE_STATION: - return BTC_WLINK_2G_STA; - case RTW89_WIFI_ROLE_P2P_GO: - return BTC_WLINK_2G_GO; - case RTW89_WIFI_ROLE_P2P_CLIENT: - return BTC_WLINK_2G_GC; - case RTW89_WIFI_ROLE_AP: - return BTC_WLINK_2G_AP; default: - return BTC_WLINK_OTHER; + if (notv10) + r->link_mode_v0 = BTC_WLINK_V0_2G_STA; + return BTC_WLINK_STA; + case RTW89_WIFI_ROLE_P2P_GO: + if (notv10) + r->link_mode_v0 = BTC_WLINK_V0_2G_GO; + return BTC_WLINK_GO; + case RTW89_WIFI_ROLE_P2P_CLIENT: + if (notv10) + r->link_mode_v0 = BTC_WLINK_V0_2G_GC; + return BTC_WLINK_GC; + case RTW89_WIFI_ROLE_AP: + if (notv10) + r->link_mode_v0 = BTC_WLINK_V0_2G_AP; + return BTC_WLINK_AP; } } @@ -6891,13 +6498,13 @@ static bool _chk_role_ch_group(const struct rtw89_btc_chdef *r1, } static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch, - u8 *phy, u8 *role, u8 link_cnt) + u8 *phy, u8 *role, u8 link_cnt, bool notv10) { struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl; - struct rtw89_btc_wl_role_info_v7 *rinfo_v7 = &wl->role_info_v7; - struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &wl->role_info_v8; + struct rtw89_btc_wl_role_info *rinfo = &wl->role_info; bool is_2g_ch_exist = false, is_multi_role_in_2g_phy = false; - u8 j, k, dbcc_2g_cid, dbcc_2g_cid2, dbcc_2g_phy, pta_req_band; + u8 j, k, dbcc_2g_cid = 0, dbcc_2g_cid2 = 0; + u8 mode; /* find out the 2G-PHY by connect-id ->ch */ for (j = 0; j < link_cnt; j++) { @@ -6908,34 +6515,31 @@ static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch, } /* If no any 2G-port exist, it's impossible because 5G-exclude */ - if (!is_2g_ch_exist) - return BTC_WLINK_5G; + if (!is_2g_ch_exist) { + mode = BTC_WLINK_SB_MCC; + if (notv10) + rinfo->link_mode_v0 = BTC_WLINK_V0_5G; + return mode; + } dbcc_2g_cid = j; - dbcc_2g_phy = phy[dbcc_2g_cid]; + rinfo->dbcc_2g_phy = phy[dbcc_2g_cid]; - if (dbcc_2g_phy == RTW89_PHY_1) - pta_req_band = RTW89_PHY_1; + if (rinfo->dbcc_2g_phy == RTW89_PHY_1) + rinfo->pta_req_band = RTW89_PHY_1; else - pta_req_band = RTW89_PHY_0; - - if (rtwdev->btc.ver->fwlrole == 7) { - rinfo_v7->dbcc_2g_phy = dbcc_2g_phy; - } else if (rtwdev->btc.ver->fwlrole == 8) { - rinfo_v8->dbcc_2g_phy = dbcc_2g_phy; - rinfo_v8->pta_req_band = pta_req_band; - } + rinfo->pta_req_band = RTW89_PHY_0; /* connect_cnt <= 2 */ if (link_cnt < BTC_TDMA_WLROLE_MAX) - return (_get_role_link_mode((role[dbcc_2g_cid]))); + return _get_role_link_mode(rinfo, role[dbcc_2g_cid], notv10); /* find the other-port in the 2G-PHY, ex: PHY-0:6G, PHY1: mcc/scc */ for (k = 0; k < link_cnt; k++) { if (k == dbcc_2g_cid) continue; - if (phy[k] == dbcc_2g_phy) { + if (phy[k] == rinfo->dbcc_2g_phy) { is_multi_role_in_2g_phy = true; dbcc_2g_cid2 = k; break; @@ -6944,319 +6548,151 @@ static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch, /* Single-role in 2G-PHY */ if (!is_multi_role_in_2g_phy) - return (_get_role_link_mode(role[dbcc_2g_cid])); + return _get_role_link_mode(rinfo, role[dbcc_2g_cid], notv10); /* 2-role in 2G-PHY */ - if (ch[dbcc_2g_cid2].center_ch > 14) - return BTC_WLINK_25G_MCC; - else if (_chk_role_ch_group(&ch[dbcc_2g_cid], &ch[dbcc_2g_cid2])) - return BTC_WLINK_2G_SCC; - else - return BTC_WLINK_2G_MCC; -} - -static void _update_role_link_mode(struct rtw89_dev *rtwdev, - bool client_joined, u32 noa) -{ - struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &rtwdev->btc.cx.wl.role_info_v8; - struct rtw89_btc_wl_role_info_v7 *rinfo_v7 = &rtwdev->btc.cx.wl.role_info_v7; - u8 role_ver = rtwdev->btc.ver->fwlrole; - u32 type = BTC_WLMROLE_NONE, dur = 0; - u8 link_mode, connect_cnt; - u32 wl_role; - - if (role_ver == 7) { - wl_role = rinfo_v7->role_map; - link_mode = rinfo_v7->link_mode; - connect_cnt = rinfo_v7->connect_cnt; - } else if (role_ver == 8) { - wl_role = rinfo_v8->role_map; - link_mode = rinfo_v8->link_mode; - connect_cnt = rinfo_v8->connect_cnt; + if (ch[dbcc_2g_cid2].center_ch > 14) { + if (notv10) + rinfo->link_mode_v0 = BTC_WLINK_V0_25G_MCC; + return BTC_WLINK_DB_MCC; + } else if (_chk_role_ch_group(&ch[dbcc_2g_cid], &ch[dbcc_2g_cid2])) { + if (notv10) + rinfo->link_mode_v0 = BTC_WLINK_V0_2G_SCC; + return BTC_WLINK_SCC; } else { - return; - } - - /* if no client_joined, don't care P2P-GO/AP role */ - if (((wl_role & BIT(RTW89_WIFI_ROLE_P2P_GO)) || - (wl_role & BIT(RTW89_WIFI_ROLE_AP))) && !client_joined) { - if (link_mode == BTC_WLINK_2G_SCC) { - if (role_ver == 7) { - rinfo_v7->link_mode = BTC_WLINK_2G_STA; - rinfo_v7->connect_cnt--; - } else if (role_ver == 8) { - rinfo_v8->link_mode = BTC_WLINK_2G_STA; - rinfo_v8->connect_cnt--; - } - } else if (link_mode == BTC_WLINK_2G_GO || - link_mode == BTC_WLINK_2G_AP) { - if (role_ver == 7) { - rinfo_v7->link_mode = BTC_WLINK_NOLINK; - rinfo_v7->connect_cnt--; - } else if (role_ver == 8) { - rinfo_v8->link_mode = BTC_WLINK_NOLINK; - rinfo_v8->connect_cnt--; - } - } - } - - /* Identify 2-Role type */ - if (connect_cnt >= 2 && - (link_mode == BTC_WLINK_2G_SCC || - link_mode == BTC_WLINK_2G_MCC || - link_mode == BTC_WLINK_25G_MCC || - link_mode == BTC_WLINK_5G)) { - if ((wl_role & BIT(RTW89_WIFI_ROLE_P2P_GO)) || - (wl_role & BIT(RTW89_WIFI_ROLE_AP))) - type = noa ? BTC_WLMROLE_STA_GO_NOA : BTC_WLMROLE_STA_GO; - else if (wl_role & BIT(RTW89_WIFI_ROLE_P2P_CLIENT)) - type = noa ? BTC_WLMROLE_STA_GC_NOA : BTC_WLMROLE_STA_GC; - else - type = BTC_WLMROLE_STA_STA; - - dur = noa; - } - - if (role_ver == 7) { - rinfo_v7->mrole_type = type; - rinfo_v7->mrole_noa_duration = dur; - } else if (role_ver == 8) { - rinfo_v8->mrole_type = type; - rinfo_v8->mrole_noa_duration = dur; + if (notv10) + rinfo->link_mode_v0 = BTC_WLINK_V0_2G_MCC; + return BTC_WLINK_SB_MCC; } } -static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid) +static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) { - struct rtw89_btc_chdef cid_ch[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER]; struct rtw89_btc *btc = &rtwdev->btc; + const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_role_info_v7 *wl_rinfo = &wl->role_info_v7; - struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; - struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info; - struct rtw89_btc_wl_active_role_v7 *act_role = NULL; - u8 i, mode, cnt = 0, cnt_2g = 0, cnt_5g = 0, phy_now = RTW89_PHY_NUM, phy_dbcc; - bool b2g = false, b5g = false, client_joined = false, client_inc_2g = false; - u8 client_cnt_last[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; - u8 cid_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; - u8 cid_phy[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; - u8 mac = RTW89_MAC_0, dbcc_2g_phy = RTW89_PHY_0; - u32 noa_duration = 0; - - memset(wl_rinfo, 0, sizeof(*wl_rinfo)); - - for (i = 0; i < RTW89_PORT_NUM; i++) { - if (!wl_linfo[i].active || wl_linfo[i].phy >= RTW89_PHY_NUM) - continue; - - act_role = &wl_rinfo->active_role[i]; - act_role->role = wl_linfo[i].role; - - /* check if role connect? */ - if (wl_linfo[i].connected == MLME_NO_LINK) { - act_role->connected = 0; - continue; - } else if (wl_linfo[i].connected == MLME_LINKING) { - continue; - } - - cnt++; - act_role->connected = 1; - act_role->pid = wl_linfo[i].pid; - act_role->phy = wl_linfo[i].phy; - act_role->band = wl_linfo[i].band; - act_role->ch = wl_linfo[i].ch; - act_role->bw = wl_linfo[i].bw; - act_role->noa = wl_linfo[i].noa; - act_role->noa_dur = wl_linfo[i].noa_duration; - cid_ch[cnt - 1] = wl_linfo[i].chdef; - cid_phy[cnt - 1] = wl_linfo[i].phy; - cid_role[cnt - 1] = wl_linfo[i].role; - wl_rinfo->role_map |= BIT(wl_linfo[i].role); - - if (rid == i) - phy_now = act_role->phy; - - if (wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO || - wl_linfo[i].role == RTW89_WIFI_ROLE_AP) { - if (wl_linfo[i].client_cnt > 1) - client_joined = true; - if (client_cnt_last[i] < wl_linfo[i].client_cnt && - wl_linfo[i].chdef.band == RTW89_BAND_2G) - client_inc_2g = true; - act_role->client_cnt = wl_linfo[i].client_cnt; - } else { - act_role->client_cnt = 0; - } - - if (act_role->noa && act_role->noa_dur > 0) - noa_duration = act_role->noa_dur; - - if (rtwdev->dbcc_en) { - phy_dbcc = wl_linfo[i].phy; - wl_dinfo->role[phy_dbcc] |= BIT(wl_linfo[i].role); - wl_dinfo->op_band[phy_dbcc] = wl_linfo[i].chdef.band; - } - - if (wl_linfo[i].chdef.band != RTW89_BAND_2G) { - cnt_5g++; - b5g = true; - } else { - if (((wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO || - wl_linfo[i].role == RTW89_WIFI_ROLE_AP) && - client_joined) || - wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_CLIENT) - wl_rinfo->p2p_2g = 1; - - if ((wl_linfo[i].mode & BIT(BTC_WL_MODE_11B)) || - (wl_linfo[i].mode & BIT(BTC_WL_MODE_11G))) - wl->bg_mode = 1; - else if (wl_linfo[i].mode & BIT(BTC_WL_MODE_HE)) - wl->he_mode = true; - - cnt_2g++; - b2g = true; - } - - if (act_role->band == RTW89_BAND_5G && act_role->ch >= 100) - wl->is_5g_hi_channel = 1; - else - wl->is_5g_hi_channel = 0; - } - - wl_rinfo->connect_cnt = cnt; - wl->client_cnt_inc_2g = client_inc_2g; - - if (cnt == 0) { - mode = BTC_WLINK_NOLINK; - wl_rinfo->role_map = BIT(RTW89_WIFI_ROLE_NONE); - } else if (!b2g && b5g) { - mode = BTC_WLINK_5G; - } else if (wl_rinfo->role_map & BIT(RTW89_WIFI_ROLE_NAN)) { - mode = BTC_WLINK_2G_NAN; - } else if (cnt > BTC_TDMA_WLROLE_MAX) { - mode = BTC_WLINK_OTHER; - } else if (rtwdev->dbcc_en) { - mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, cnt); - - /* correct 2G-located PHY band for gnt ctrl */ - if (dbcc_2g_phy < RTW89_PHY_NUM) - wl_dinfo->op_band[dbcc_2g_phy] = RTW89_BAND_2G; - } else if (b2g && b5g && cnt == 2) { - mode = BTC_WLINK_25G_MCC; - } else if (!b5g && cnt == 2) { /* cnt_connect = 2 */ - if (_chk_role_ch_group(&cid_ch[0], &cid_ch[cnt - 1])) - mode = BTC_WLINK_2G_SCC; - else - mode = BTC_WLINK_2G_MCC; - } else if (!b5g && cnt == 1) { /* cnt_connect = 1 */ - mode = _get_role_link_mode(cid_role[0]); - } else { - mode = BTC_WLINK_NOLINK; - } - - wl_rinfo->link_mode = mode; - _update_role_link_mode(rtwdev, client_joined, noa_duration); - - /* todo DBCC related event */ - rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] wl_info phy_now=%d\n", phy_now); - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC] rlink cnt_2g=%d cnt_5g=%d\n", cnt_2g, cnt_5g); - - if (wl_rinfo->dbcc_en != rtwdev->dbcc_en) { - wl_rinfo->dbcc_chg = 1; - wl_rinfo->dbcc_en = rtwdev->dbcc_en; - wl->wcnt[BTC_WCNT_DBCC_CHG]++; - } - - if (rtwdev->dbcc_en) { - wl_rinfo->dbcc_2g_phy = dbcc_2g_phy; - - if (dbcc_2g_phy == RTW89_PHY_1) - mac = RTW89_MAC_1; - - _update_dbcc_band(rtwdev, RTW89_PHY_0); - _update_dbcc_band(rtwdev, RTW89_PHY_1); - } - _wl_req_mac(rtwdev, mac); - _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); -} - -static u8 _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) -{ - struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl; struct rtw89_btc_wl_mlo_info *mlo_info = &wl->mlo_info; - u8 mode = BTC_WLINK_NOLINK; + struct rtw89_btc_wl_role_info *r = &wl->role_info; + u8 p2p_exist = wl->role_info.p2p_exist; + if (hw_band == RTW89_PHY_1) + p2p_exist = wl->role_info.p2p_exist_hb1; + + /* MLD: nLmR --> 2L1R: 2-Link by 1-RF, 2L2R: 2-link by 2-RF */ switch (type) { case RTW89_MR_WTYPE_NONE: /* no-link */ - mode = BTC_WLINK_NOLINK; + r->link_mode = BTC_WLINK_NOLINK; break; case RTW89_MR_WTYPE_NONMLD: /* Non_MLO 1-role 2+0/0+2 */ case RTW89_MR_WTYPE_MLD1L1R: /* MLO only-1 link 2+0/0+2 */ + if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1AP) { + r->link_mode = BTC_WLINK_GO; + } else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1CLIENT && + p2p_exist) { + r->link_mode = BTC_WLINK_GC; + } else { + r->link_mode = BTC_WLINK_STA; + } + + if (ver->fwlrole >= 10) + break; + if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { - mode = BTC_WLINK_5G; + r->link_mode_v0 = BTC_WLINK_V0_5G; } else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1AP) { - mode = BTC_WLINK_2G_GO; + r->link_mode_v0 = BTC_WLINK_V0_2G_GO; } else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1CLIENT) { - if (wl->role_info_v8.p2p_2g) - mode = BTC_WLINK_2G_GC; + if (wl->role_info.p2p_2g) + r->link_mode_v0 = BTC_WLINK_V0_2G_GC; else - mode = BTC_WLINK_2G_STA; + r->link_mode_v0 = BTC_WLINK_V0_2G_STA; } break; case RTW89_MR_WTYPE_NONMLD_NONMLD: /* Non_MLO 2-role 2+0/0+2 */ case RTW89_MR_WTYPE_MLD1L1R_NONMLD: /* MLO only-1 link + P2P 2+0/0+2 */ + if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_5GHZ || + mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_6GHZ) { + r->link_mode = BTC_WLINK_DB_MCC; + } else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ || + mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_5GHZ || + mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_6GHZ || + mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_5GHZ_6GHZ) { + r->link_mode = BTC_WLINK_SB_MCC; + } else { + r->link_mode = BTC_WLINK_SCC; + } + + if (ver->fwlrole >= 10) + break; + if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { - mode = BTC_WLINK_5G; + r->link_mode_v0 = BTC_WLINK_V0_5G; } else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_5GHZ || mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_6GHZ) { - mode = BTC_WLINK_25G_MCC; + r->link_mode_v0 = BTC_WLINK_V0_25G_MCC; } else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ) { - mode = BTC_WLINK_2G_MCC; + r->link_mode_v0 = BTC_WLINK_V0_2G_MCC; } else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX1_2GHZ) { - mode = BTC_WLINK_2G_SCC; + r->link_mode_v0 = BTC_WLINK_V0_2G_SCC; } break; case RTW89_MR_WTYPE_MLD2L1R: /* MLO_MLSR 2+0/0+2 */ - if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) - mode = BTC_WLINK_5G; - else if (wl->role_info_v8.p2p_2g) - mode = BTC_WLINK_2G_GC; + if (p2p_exist) /* MLO_MLSR only support STA/GC */ + r->link_mode = BTC_WLINK_GC; else - mode = BTC_WLINK_2G_STA; + r->link_mode = BTC_WLINK_STA; + + if (ver->fwlrole >= 10) + break; + + if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) + r->link_mode_v0 = BTC_WLINK_V0_5G; + else if (wl->role_info.p2p_2g) + r->link_mode_v0 = BTC_WLINK_V0_2G_GC; + else + r->link_mode_v0 = BTC_WLINK_V0_2G_STA; break; case RTW89_MR_WTYPE_MLD2L1R_NONMLD: /* MLO_MLSR + P2P 2+0/0+2 */ - case RTW89_MR_WTYPE_MLD2L2R_NONMLD: /* MLO_MLMR + P2P 1+1/2+2 */ - /* driver may doze 1-link to + case RTW89_MR_WTYPE_MLD2L2R_NONMLD: /* MLO_MLMR + P2P 1+1/2+2*/ + /* driver may doze 1-link (1+1) or 2+0->0+2->1+1 * 2G+5G -> TDMA slot switch by E2G/E5G * 5G only -> TDMA slot switch by E5G */ - mode = BTC_WLINK_25G_MCC; + r->link_mode = BTC_WLINK_DB_MCC; + + if (ver->fwlrole >= 10) + break; + + r->link_mode_v0 = BTC_WLINK_V0_25G_MCC; break; case RTW89_MR_WTYPE_MLD2L2R: /* MLO_MLMR 1+1/2+2 */ + /* MLMR only support STA now (2024) */ + r->link_mode = BTC_WLINK_STA; + + if (ver->fwlrole >= 10) + break; + if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { - mode = BTC_WLINK_5G; + r->link_mode_v0 = BTC_WLINK_V0_5G; } else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1AP) { - mode = BTC_WLINK_2G_GO; + r->link_mode_v0 = BTC_WLINK_V0_2G_GO; } else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1CLIENT) { - if (wl->role_info_v8.p2p_2g) - mode = BTC_WLINK_2G_GC; + if (wl->role_info.p2p_2g) + r->link_mode_v0 = BTC_WLINK_V0_2G_GC; else - mode = BTC_WLINK_2G_STA; + r->link_mode_v0 = BTC_WLINK_V0_2G_STA; } break; } - return mode; } -static void _update_wl_mlo_info(struct rtw89_dev *rtwdev) +static void _update_wl_mlo_info(struct rtw89_dev *rtwdev, u8 hw_band) { struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; + struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_wl_mlo_info *mlo_info = &wl->mlo_info; struct rtw89_mr_chanctx_info qinfo; - u8 track_band = RTW89_PHY_0; + struct rtw89_chanctx *ch; u8 rf_band = RTW89_BAND_2G; u8 i, type; @@ -7265,19 +6701,46 @@ static void _update_wl_mlo_info(struct rtw89_dev *rtwdev) memset(&qinfo, 0, sizeof(qinfo)); rtw89_query_mr_chanctx_info(rtwdev, i, &qinfo); + + switch (qinfo.ctxtype) { + default: + mlo_info->hwb_rf_band[i] = 0; + break; + case RTW89_MR_CTX1_2GHZ: + case RTW89_MR_CTX2_2GHZ: + mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_2G); + break; + case RTW89_MR_CTX1_5GHZ: + case RTW89_MR_CTX2_5GHZ: + mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_5G); + break; + case RTW89_MR_CTX1_6GHZ: + case RTW89_MR_CTX2_6GHZ: + mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_6G); + break; + case RTW89_MR_CTX2_2GHZ_5GHZ: + mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_2G) | + BIT(RTW89_BAND_5G); + break; + case RTW89_MR_CTX2_2GHZ_6GHZ: + mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_2G) | + BIT(RTW89_BAND_6G); + break; + case RTW89_MR_CTX2_5GHZ_6GHZ: + mlo_info->hwb_rf_band[i] = BIT(RTW89_BAND_5G) | + BIT(RTW89_BAND_6G); + break; + } mlo_info->wmode[i] = qinfo.wmode; mlo_info->ch_type[i] = qinfo.ctxtype; mlo_info->wtype = qinfo.wtype; - - if (mlo_info->ch_type[i] == RTW89_MR_CTX1_5GHZ || - mlo_info->ch_type[i] == RTW89_MR_CTX2_5GHZ || - mlo_info->ch_type[i] == RTW89_MR_CTX2_5GHZ_6GHZ) - mlo_info->hwb_rf_band[i] = RTW89_BAND_5G; - else if (mlo_info->ch_type[i] == RTW89_MR_CTX1_6GHZ || - mlo_info->ch_type[i] == RTW89_MR_CTX2_6GHZ) - mlo_info->hwb_rf_band[i] = RTW89_BAND_6G; - else /* check if "2G-included" or unknown in each HW-band */ - mlo_info->hwb_rf_band[i] = RTW89_BAND_2G; + wl->rf_band_map[i] = mlo_info->hwb_rf_band[i]; + ch = &rtwdev->hal.chanctx[i]; + wl->rf_ch_info[i].center_ch = ch->chan.channel; + wl->rf_ch_info[i].band = ch->chan.band_type; + wl->rf_ch_info[i].chan = ch->chan.channel; + wl->rf_ch_info[i].offset = ch->chan.pri_ch_idx; + wl->rf_ch_info[i].bw = ch->chan.band_width; } mlo_info->link_status = rtwdev->mlo_dbcc_mode; @@ -7312,71 +6775,64 @@ static void _update_wl_mlo_info(struct rtw89_dev *rtwdev) default: case MLO_2_PLUS_0_1RF: /* 2+0 */ case MLO_2_PLUS_0_2RF: - mlo_info->rf_combination = BTC_MLO_RF_2_PLUS_0; - track_band = RTW89_MAC_0; - rf_band = mlo_info->hwb_rf_band[RTW89_MAC_0]; - mlo_info->path_rf_band[BTC_RF_S0] = rf_band; - mlo_info->path_rf_band[BTC_RF_S1] = rf_band; - + _update_wl_link_mode(rtwdev, RTW89_MAC_0, type); + wl_rinfo->link_mode_hb1 = wl_rinfo->link_mode; wl_rinfo->pta_req_band = RTW89_MAC_0; wl_rinfo->dbcc_2g_phy = RTW89_PHY_0; wl_rinfo->dbcc_en = 0; + + rf_band = wl->rf_band_map[RTW89_PHY_0]; + mlo_info->path_rf_band[BTC_RF_S0] = rf_band; + mlo_info->path_rf_band[BTC_RF_S1] = rf_band; break; case MLO_0_PLUS_2_1RF: /* 0+2 */ case MLO_0_PLUS_2_2RF: - mlo_info->rf_combination = BTC_MLO_RF_0_PLUS_2; - track_band = RTW89_MAC_1; - rf_band = mlo_info->hwb_rf_band[RTW89_MAC_1]; - mlo_info->path_rf_band[BTC_RF_S0] = rf_band; - mlo_info->path_rf_band[BTC_RF_S1] = rf_band; - + _update_wl_link_mode(rtwdev, RTW89_MAC_1, type); + wl_rinfo->link_mode_hb1 = wl_rinfo->link_mode; wl_rinfo->pta_req_band = RTW89_MAC_1; wl_rinfo->dbcc_2g_phy = RTW89_PHY_1; wl_rinfo->dbcc_en = 0; + + rf_band = wl->rf_band_map[RTW89_PHY_1]; + mlo_info->path_rf_band[BTC_RF_S0] = rf_band; + mlo_info->path_rf_band[BTC_RF_S1] = rf_band; break; case MLO_1_PLUS_1_1RF: /* 1+1 */ case MLO_1_PLUS_1_2RF: /* 1+1 */ case MLO_2_PLUS_2_2RF: /* 2+2 */ case DBCC_LEGACY: /* DBCC 1+1 */ - if (mlo_info->link_status == MLO_2_PLUS_2_2RF) - mlo_info->rf_combination = BTC_MLO_RF_2_PLUS_2; - else - mlo_info->rf_combination = BTC_MLO_RF_1_PLUS_1; - - if (mlo_info->hwb_rf_band[RTW89_MAC_0] == RTW89_BAND_2G) - track_band = RTW89_MAC_0; - else - track_band = RTW89_MAC_1; - - mlo_info->path_rf_band[BTC_RF_S0] = - mlo_info->hwb_rf_band[RTW89_MAC_0]; - mlo_info->path_rf_band[BTC_RF_S1] = - mlo_info->hwb_rf_band[RTW89_MAC_1]; - - /* Check ch count from ch_type @ 2.4G HW-band, and modify type */ - if (mlo_info->ch_type[track_band] == RTW89_MR_CTX1_2GHZ) - type = RTW89_MR_WTYPE_NONMLD; /* only 1-role at 2G */ + /* Check ch count from ch_type and modify type*/ + if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX1_2GHZ || + mlo_info->ch_type[hw_band] == RTW89_MR_CTX1_5GHZ || + mlo_info->ch_type[hw_band] == RTW89_MR_CTX1_6GHZ) + type = RTW89_MR_WTYPE_NONMLD; /* only 1-role */ else type = RTW89_MR_WTYPE_NONMLD_NONMLD; - if (mlo_info->hwb_rf_band[RTW89_MAC_0] == RTW89_BAND_2G) { - wl_rinfo->pta_req_band = RTW89_MAC_0; - wl_rinfo->dbcc_2g_phy = RTW89_PHY_0; + rf_band = wl->rf_band_map[hw_band]; + _update_wl_link_mode(rtwdev, hw_band, type); + + if (hw_band == RTW89_MAC_0) { + mlo_info->path_rf_band[BTC_RF_S0] = rf_band; } else { - wl_rinfo->pta_req_band = RTW89_MAC_1; - wl_rinfo->dbcc_2g_phy = RTW89_PHY_1; + wl_rinfo->link_mode_hb1 = wl_rinfo->link_mode; + mlo_info->path_rf_band[BTC_RF_S1] = rf_band; } - if (mlo_info->wmode[RTW89_MAC_0] == RTW89_MR_WMODE_NONE && - mlo_info->wmode[RTW89_MAC_1] == RTW89_MR_WMODE_NONE) - wl_rinfo->dbcc_en = 0; - else - wl_rinfo->dbcc_en = 1; + /* set pta_req_band for 1-PTA architecture */ + if (wl->rf_band_map[RTW89_MAC_0] & BIT(RTW89_BAND_2G)) { + wl_rinfo->pta_req_band = RTW89_MAC_0; + wl_rinfo->dbcc_2g_phy = RTW89_PHY_0; + } else if (wl->rf_band_map[RTW89_MAC_1] & BIT(RTW89_BAND_2G)) { + wl_rinfo->pta_req_band = RTW89_MAC_1; + wl_rinfo->dbcc_2g_phy = RTW89_PHY_1; + } else { /* Both HW-BAND are not on 2.4G*/ + wl_rinfo->pta_req_band = RTW89_MAC_0; + wl_rinfo->dbcc_2g_phy = RTW89_PHY_0; + } break; } - wl_rinfo->link_mode = _update_wl_link_mode(rtwdev, track_band, type); - rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(), mode=%s, pta_band=%d", __func__, id_to_linkmode(wl_rinfo->link_mode), wl_rinfo->pta_req_band); @@ -7386,16 +6842,19 @@ static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev) { struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl; struct rtw89_btc_wl_rlink *rlink = NULL; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; + struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_chdef cid_ch[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; u8 cid_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; u8 cid_phy[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; bool b2g = false, b5g = false, outloop = false; + bool notv10 = rtwdev->btc.ver->fwlrole != 10; + u8 mode_v0 = BTC_WLINK_V0_NOLINK; u8 mode = BTC_WLINK_NOLINK; u8 cnt_2g = 0, cnt_5g = 0; u8 i, j, cnt = 0; for (j = RTW89_PHY_0; j < RTW89_PHY_NUM; j++) { + wl->rf_band_map[j] = 0; /* j=link id, is the link use which band */ for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { rlink = &wl_rinfo->rlink[i][j]; @@ -7419,6 +6878,7 @@ static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev) cnt_2g++; b2g = true; } + wl->rf_band_map[j] |= BIT(rlink->rf_band); } if (outloop) break; @@ -7427,56 +6887,73 @@ static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev) rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): cnt_2g=%d, cnt_5g=%d\n", __func__, cnt_2g, cnt_5g); + wl_rinfo->pta_req_band = RTW89_PHY_0; /* no 0+2 for non-MLO */ wl_rinfo->dbcc_en = rtwdev->dbcc_en; + /* Be careful to change the following sequence!! */ if (cnt == 0) { mode = BTC_WLINK_NOLINK; - } else if (!b2g && b5g) { - mode = BTC_WLINK_5G; + mode_v0 = BTC_WLINK_V0_NOLINK; } else if (wl_rinfo->dbcc_en) { - mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, cnt); + /* update pta_req_band to 2.4GHz HW-BAND for DBCC = 1*/ + mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, cnt, notv10); + mode_v0 = wl_rinfo->link_mode_v0; + } else if (!b2g && b5g && notv10) { + mode_v0 = BTC_WLINK_V0_5G; } else if (b2g && b5g) { - mode = BTC_WLINK_25G_MCC; - } else if (!b5g && cnt >= 2) { - if (_chk_role_ch_group(&cid_ch[0], &cid_ch[1])) - mode = BTC_WLINK_2G_SCC; - else - mode = BTC_WLINK_2G_MCC; - } else if (!b5g) { /* cnt_connect = 1 */ - mode = _get_role_link_mode(cid_role[0]); + mode = BTC_WLINK_DB_MCC; + mode_v0 = BTC_WLINK_V0_25G_MCC; + } else if (cnt >= 2) { + if (_chk_role_ch_group(&cid_ch[0], &cid_ch[1])) { + mode = BTC_WLINK_SCC; + mode_v0 = BTC_WLINK_V0_2G_SCC; + } else { + mode = BTC_WLINK_SB_MCC; + mode_v0 = BTC_WLINK_V0_2G_MCC; + } + } else { + mode = _get_role_link_mode(wl_rinfo, cid_role[0], notv10); } wl_rinfo->link_mode = mode; + wl_rinfo->link_mode_hb1 = mode; + wl_rinfo->link_mode_v0 = mode_v0; } -static void _modify_role_link_mode(struct rtw89_dev *rtwdev) +static void _modify_role_link_mode(struct rtw89_dev *rtwdev, u8 hw_band) { - struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; u8 go_cleint_exist = wl->go_client_exist; - u8 link_mode = wl_rinfo->link_mode; + u8 *link_mode = &wl_rinfo->link_mode; u32 role_map = wl_rinfo->role_map; u8 noa_exist = wl->noa_exist; u32 mrole = BTC_WLMROLE_NONE; + if (hw_band == RTW89_PHY_1) { + *link_mode = wl_rinfo->link_mode_hb1; + role_map = wl_rinfo->role_map_hb1; + go_cleint_exist = wl->go_client_exist_hb1; + } + /* if no client_joined, don't care P2P-GO/AP role */ if (((role_map & BIT(RTW89_WIFI_ROLE_P2P_GO)) || - (role_map & BIT(RTW89_WIFI_ROLE_AP))) && !go_cleint_exist) { - if (link_mode == BTC_WLINK_2G_SCC) { - wl_rinfo->link_mode = BTC_WLINK_2G_STA; - } else if (link_mode == BTC_WLINK_2G_GO || - link_mode == BTC_WLINK_2G_AP) { - wl_rinfo->link_mode = BTC_WLINK_NOLINK; + (role_map & BIT(RTW89_WIFI_ROLE_AP))) && !go_cleint_exist) { + if (*link_mode == BTC_WLINK_SCC) { + *link_mode = BTC_WLINK_STA; + } else if (*link_mode == BTC_WLINK_GO || + *link_mode == BTC_WLINK_AP) { + *link_mode = BTC_WLINK_NOLINK; } } - /* Identify 2-Role type */ - if (link_mode == BTC_WLINK_2G_SCC || - link_mode == BTC_WLINK_2G_MCC || - link_mode == BTC_WLINK_25G_MCC || - link_mode == BTC_WLINK_5G) { + /* Identify 2-Role type */ + if (*link_mode == BTC_WLINK_SCC || + *link_mode == BTC_WLINK_SB_MCC || + *link_mode == BTC_WLINK_DB_MCC) { if ((role_map & BIT(RTW89_WIFI_ROLE_P2P_GO)) || - (role_map & BIT(RTW89_WIFI_ROLE_AP))) { + (role_map & BIT(RTW89_WIFI_ROLE_AP))) { if (noa_exist) mrole = BTC_WLMROLE_STA_GO_NOA; else @@ -7489,127 +6966,147 @@ static void _modify_role_link_mode(struct rtw89_dev *rtwdev) } else { mrole = BTC_WLMROLE_STA_STA; } - } - wl_rinfo->mrole_type = mrole; + if (hw_band == RTW89_PHY_0) { + wl_rinfo->mrole_type &= GENMASK(31, 16); /* clear Low-Word */ + wl_rinfo->mrole_type |= mrole; + } else { + wl_rinfo->mrole_type &= GENMASK(15, 0); /* clear high-Word */ + wl_rinfo->mrole_type |= (mrole << 16); + } + } else { + wl_rinfo->mrole_type = BTC_WLMROLE_NONE; + } rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): link_mode=%s, mrole_type=%d\n", __func__, id_to_linkmode(wl_rinfo->link_mode), wl_rinfo->mrole_type); } -static void _update_wl_info_v8(struct rtw89_dev *rtwdev, u8 role_id, u8 rlink_id, - enum btc_role_state state) +static void _update_wl_info(struct rtw89_dev *rtwdev, struct rtw89_btc_wl_link_info *wl_linfo) { - struct rtw89_btc_wl_rlink *rlink = NULL; - struct rtw89_btc_wl_link_info *wl_linfo; struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; - bool client_joined = false, noa_exist = false, p2p_exist = false; - bool is_5g_hi_channel = false, bg_mode = false, dbcc_en_ori; - u8 i, j, link_mode_ori; + struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; + struct rtw89_btc_wl_rlink *rlink = NULL; + u8 dbcc_en_ori, is_5g_hi_ch = 0, bg_mode = 0, p2p_exist = 0; + u8 go_client_exist = 0, noa_exist = 0; + u8 rlink_id = wl_linfo->phy; + u8 role_id = wl_linfo->pid; + u8 i, link_mode_ori; u32 role_map = 0; - if (role_id >= RTW89_BE_BTC_WL_MAX_ROLE_NUMBER || rlink_id >= RTW89_MAC_NUM) + if (role_id >= RTW89_BE_BTC_WL_MAX_ROLE_NUMBER || rlink_id >= RTW89_BAND_NUM) return; - /* Extract wl->link_info[role_id][rlink_id] to wl->role_info + /* + * Extract wl->link_info[role_id][rlink_id] to wl->role_info * role_id: role index * rlink_id: rlink index (= HW-band index) * pid: port_index */ - - wl_linfo = &wl->rlink_info[role_id][rlink_id]; rlink = &wl_rinfo->rlink[role_id][rlink_id]; rlink->role = wl_linfo->role; rlink->active = wl_linfo->active; /* Doze or not */ rlink->pid = wl_linfo->pid; + rlink->mac_id = wl_linfo->mac_id; rlink->phy = wl_linfo->phy; - rlink->rf_band = wl_linfo->band; - rlink->ch = wl_linfo->ch; - rlink->bw = wl_linfo->bw; + rlink->rf_band = wl_linfo->chdef.band; + rlink->ch = wl_linfo->chdef.center_ch; + rlink->bw = wl_linfo->chdef.bw; rlink->noa = wl_linfo->noa; rlink->noa_dur = wl_linfo->noa_duration / 1000; rlink->client_cnt = wl_linfo->client_cnt; rlink->mode = wl_linfo->mode; + /* check if rlink connect? */ switch (wl_linfo->connected) { case MLME_NO_LINK: rlink->connected = 0; break; case MLME_LINKED: rlink->connected = 1; + + /* reset special-AP flag if station mode linked */ + dm->leak_ap = 0; break; default: return; } - for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) { - for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { - rlink = &wl_rinfo->rlink[i][j]; + /* update by HW-Band */ + for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { /* i = role_id */ + rlink = &wl_rinfo->rlink[i][rlink_id]; - if (!rlink->active || !rlink->connected) - continue; + if (!rlink->active || !rlink->connected) + continue; - role_map |= BIT(rlink->role); + role_map |= BIT(rlink->role); - /* only one noa-role exist */ - if (rlink->noa && rlink->noa_dur > 0) - noa_exist = true; - - /* for WL 5G-Rx interfered with BT issue */ - if (rlink->rf_band == RTW89_BAND_5G) { - if (rlink->ch >= 100) - is_5g_hi_channel = true; - - continue; - } - - /* only if client connect for p2p-Go/AP */ - if ((rlink->role == RTW89_WIFI_ROLE_P2P_GO || - rlink->role == RTW89_WIFI_ROLE_AP) && - rlink->client_cnt > 1) { - p2p_exist = true; - client_joined = true; - } - - /* Identify if P2P-Go (GO/GC/AP) exist at 2G band */ - if (rlink->role == RTW89_WIFI_ROLE_P2P_CLIENT) - p2p_exist = true; - - if ((rlink->mode & BIT(BTC_WL_MODE_11B)) || - (rlink->mode & BIT(BTC_WL_MODE_11G))) - bg_mode = true; + /* Identify if P2P-Go (GO/GC/AP) exist at 2GHz band */ + if (((rlink->role == RTW89_WIFI_ROLE_P2P_GO || + rlink->role == RTW89_WIFI_ROLE_AP) && + rlink->client_cnt > 1)) { + p2p_exist = 1; + go_client_exist = 1; } + + if (rlink->role == RTW89_WIFI_ROLE_P2P_CLIENT) + p2p_exist = 1; + + /* only one noa-role exist */ + if (rlink->noa && rlink->noa_dur > 0) + noa_exist = 1; + + /* for WL 5G-Rx interfered with BT issue */ + if (rlink->rf_band == RTW89_BAND_5G && rlink->ch >= 100) + is_5g_hi_ch = 1; + + if ((rlink->mode & BIT(BTC_WL_MODE_11B)) || + (rlink->mode & BIT(BTC_WL_MODE_11G))) + bg_mode = 1; } - link_mode_ori = wl_rinfo->link_mode; - wl->is_5g_hi_channel = is_5g_hi_channel; - wl->bg_mode = bg_mode; - wl->go_client_exist = client_joined; - wl->noa_exist = noa_exist; - wl_rinfo->p2p_2g = p2p_exist; - wl_rinfo->role_map = role_map; + if (rlink_id == RTW89_PHY_0) { + link_mode_ori = wl_rinfo->link_mode; + wl->is_5g_hi_ch = is_5g_hi_ch; + wl->bg_mode = bg_mode; + wl->go_client_exist = go_client_exist; + wl->noa_exist = noa_exist; + wl_rinfo->p2p_exist = p2p_exist; + wl_rinfo->role_map = role_map; + } else { + link_mode_ori = wl_rinfo->link_mode_hb1; + wl->is_5g_hi_ch_hb1 = is_5g_hi_ch; + wl->bg_mode_hb1 = bg_mode; + wl->go_client_exist_hb1 = go_client_exist; + wl->noa_exist_hb1 = noa_exist; + wl_rinfo->p2p_exist_hb1 = p2p_exist; + wl_rinfo->role_map_hb1 = role_map; + } dbcc_en_ori = wl_rinfo->dbcc_en; + /* for MLO-supported, link-mode from driver directly */ if (rtwdev->chip->para_ver & BTC_FEAT_MLO_SUPPORT) { - /* for MLO-supported, link-mode from driver directly */ - _update_wl_mlo_info(rtwdev); - } else { - /* for non-MLO-supported, link-mode by BTC */ + _update_wl_mlo_info(rtwdev, rlink_id); + } else { /* for non-MLO-supported, link-mode by BTC */ _update_wl_non_mlo_info(rtwdev); } - _modify_role_link_mode(rtwdev); + _modify_role_link_mode(rtwdev, rlink_id); - if (link_mode_ori != wl_rinfo->link_mode) - wl->link_mode_chg = true; + if ((rlink_id == RTW89_PHY_0 && wl_rinfo->link_mode != link_mode_ori) || + (rlink_id == RTW89_PHY_1 && wl_rinfo->link_mode_hb1 != link_mode_ori)) { + wl_rinfo->link_mode_chg = 1; + wl->link_mode_chg = 1; + } if (wl_rinfo->dbcc_en != dbcc_en_ori) { - wl->dbcc_chg = true; + wl->dbcc_chg = 1; + wl->role_info.dbcc_chg = 1; wl->wcnt[BTC_WCNT_DBCC_CHG]++; } } @@ -7685,25 +7182,21 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid) struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_bt_info *bt; u32 val, any_bt_connect, any_bt_6g_connect = 0; - u8 id, id_start, id_stop, mode; + u8 id, id_start, id_stop, mode = 0; bool bt_link_change = false; bool lps_ctrl = false; if (!rtwdev->chip->scbd || bid > BTC_ALL_BT) return; - if (ver->fwlrole == 0) - mode = wl->role_info.link_mode; - else if (ver->fwlrole == 1) - mode = wl->role_info_v1.link_mode; - else if (ver->fwlrole == 2) - mode = wl->role_info_v2.link_mode; - else if (ver->fwlrole == 7) - mode = wl->role_info_v7.link_mode; - else if (ver->fwlrole == 8) - mode = wl->role_info_v8.link_mode; - else - return; + if (ver->fwlrole == 10) { + if (wl->role_info.link_mode == BTC_WLINK_STA && + (dm->tdd_en && wl->rf_band_map[RTW89_MAC_0] & + BIT(RTW89_BAND_2G))) + mode = BTC_WLINK_V0_2G_STA; + } else { + mode = wl->role_info.link_mode_v0; + } if (bid == BTC_ALL_BT) { id_start = BTC_BT_1ST; @@ -7796,7 +7289,7 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid) if (((bt->link_info.a2dp_desc.exist || bt->link_info.pan_desc.exist || bt->link_info.hfp_desc.exist) && - mode == BTC_WLINK_2G_STA) || + mode == BTC_WLINK_V0_2G_STA) || bt->whql_test) lps_ctrl = true; @@ -7900,7 +7393,12 @@ static void _set_bind_info(struct rtw89_btc *btc, u8 type) * HW-Band is decided by wl->mlo_info.mrcx_act_hwb_map */ if (wl->mlo_info.wtype == RTW89_MR_WTYPE_MLD2L1R_NONMLD) { - /* TODO: Should patched WiFi mode & WiFi role patch */ + if (wl->role_info.link_mode != BTC_WLINK_DB_MCC) { /* mode chg */ + if (wl->mlo_info.mrcx_act_hwb_map == BIT(RTW89_MAC_1)) + path_hwb[BTC_RF_S0] = RTW89_PHY_1; /* S0/1->HWB1 */ + else + path_hwb[BTC_RF_S1] = RTW89_PHY_0; /* S0/1->HWB0 */ + } } else { /* * Dual-RF_band(HWB)TDMA if BT-profile is TDMA-type at both @@ -7933,7 +7431,22 @@ static void _set_bind_info(struct rtw89_btc *btc, u8 type) } } - /* TODO: Should patched WiFi mode & WiFi role patch */ + if (wl->mlo_info.wtype == RTW89_MR_WTYPE_MLD2L2R && + (bd->wl_hwb_sel == (BIT(RTW89_MAC_1) | BIT(RTW89_MAC_0)))) { + if (wl->role_info.link_mode == BTC_WLINK_AP || + wl->role_info.link_mode_hb1 == BTC_WLINK_AP) + bd->wl_link_mode = BTC_WLINK_DB_MCC; + else + bd->wl_link_mode = BTC_WLINK_STA; + bd->wl_bg_mode = wl->bg_mode | wl->bg_mode_hb1; + } else if (bd->wl_hwb_sel == (BIT(RTW89_MAC_1))) { + bd->wl_link_mode = wl->role_info.link_mode_hb1; + bd->wl_bg_mode = wl->bg_mode_hb1; + } else {/* HW-BAND-0 or no-hw-band */ + bd->wl_hwb_sel = BIT(RTW89_MAC_0); + bd->wl_link_mode = wl->role_info.link_mode; + bd->wl_bg_mode = wl->bg_mode; + } /* update Bind-BT status map for BT0/BT1 */ for (i = BTC_BT_1ST; i < BTC_ALL_BT_EZL; i++) { @@ -7966,9 +7479,9 @@ static void _set_bind_info(struct rtw89_btc *btc, u8 type) bd->bt_smap.a2dp_sink |= b->a2dp_desc.sink; bd->bt_smap.pan_active |= b->pan_desc.active; bd->bt_smap.connect |= b->status.map.connect; - bd->bt_smap.hid_cnt += (u8)b->hid_desc.pair_cnt; + bd->bt_smap.hid_cnt += b->hid_desc.pair_cnt; bd->bt_smap.hid_type |= b->hid_desc.type; - bd->bt_smap.cis_cnt += (u8)b->leaudio_desc.cis_cnt; + bd->bt_smap.cis_cnt += b->leaudio_desc.cis_cnt; bd->bt_smap.link_cnt += b->link_cnt.now; bd->bt_smap.inq_page |= b->status.map.inq_pag; bd->bt_smap.page |= b->pag; @@ -8028,7 +7541,7 @@ static void _set_coex_binding(struct rtw89_btc *btc) * ==> 1: interference, 0: no-interference * * fdm_map(Frequency-Division-Multiplexing): WL/BT RF_Band overlap-map - * 2bit-map: bit[1]:5GHz/6GHz, bit[0]:2.4GHz + * 2bit-map: bit[1]:5/6GHz, bit[0]:2.4GHz */ for (i = 0; i < RTW89_PHY_NUM; i++) { if (wl->rf_band_map[i] & BIT(RTW89_BAND_2G)) @@ -8050,9 +7563,7 @@ static void _set_coex_binding(struct rtw89_btc *btc) * In this case, BTC_RF_S0->HWB0, BTC_RF_S1->HWB1 */ if (wl->mlo_info.wtype == RTW89_MR_WTYPE_MLD2L1R_NONMLD) { - if (wl->role_info.link_mode != BTC_WLINK_2G_MCC && - wl->role_info.link_mode != BTC_WLINK_25G_MCC && - wl->role_info.link_mode != BTC_WLINK_25G_DBCC) {/* mode chg */ + if (wl->role_info.link_mode != BTC_WLINK_DB_MCC) {/* mode chg */ if (wl->mlo_info.mrcx_act_hwb_map == BIT(RTW89_PHY_1)) path_hwb[BTC_RF_S0] = RTW89_PHY_1;/* S0/1->HWB1 */ else @@ -8114,31 +7625,14 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; - struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; - struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; - struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; - struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; - u8 mode, igno_bt, always_freerun; + u8 mode = btc->dm.tdd_bind.wl_link_mode; + u8 mode_v0 = wl->role_info.link_mode_v0; + u8 igno_bt, always_freerun; lockdep_assert_wiphy(rtwdev->hw->wiphy); dm->run_reason = reason; _update_dm_step(rtwdev, reason); - _update_btc_state_map(rtwdev); - - if (ver->fwlrole == 0) - mode = wl_rinfo->link_mode; - else if (ver->fwlrole == 1) - mode = wl_rinfo_v1->link_mode; - else if (ver->fwlrole == 2) - mode = wl_rinfo_v2->link_mode; - else if (ver->fwlrole == 7) - mode = wl_rinfo_v7->link_mode; - else if (ver->fwlrole == 8) - mode = wl_rinfo_v8->link_mode; - else - return; if (ver->fcxctrl == 7) { igno_bt = btc->ctrl.ctrl_v7.igno_bt; @@ -8206,6 +7700,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) igno_bt = false; _set_coex_binding(btc); + _update_btc_state_map(rtwdev); dm->freerun_chk = _check_freerun(rtwdev); /* check if meet freerun */ @@ -8253,43 +7748,76 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) goto exit; } - switch (mode) { + if (mode == BTC_WLINK_NOLINK) { + mode_v0 = BTC_WLINK_V0_NOLINK; + } else if (btc->dm.tdd_bind.rf_band == BIT(RTW89_BAND_5G)) { + mode_v0 = BTC_WLINK_V0_5G; + } else if (btc->dm.tdd_bind.rf_band & BIT(RTW89_BAND_5G) && + btc->dm.tdd_bind.rf_band & BIT(RTW89_BAND_2G)) { + mode_v0 = BTC_WLINK_V0_25G_MCC; + } else if (btc->dm.tdd_bind.rf_band == BIT(RTW89_BAND_2G)) { + switch (mode) { + case BTC_WLINK_STA: + mode_v0 = BTC_WLINK_V0_2G_STA; + break; + case BTC_WLINK_AP: + mode_v0 = BTC_WLINK_V0_2G_AP; + break; + case BTC_WLINK_GO: + mode_v0 = BTC_WLINK_V0_2G_GO; + break; + case BTC_WLINK_GC: + mode_v0 = BTC_WLINK_V0_2G_GC; + break; + case BTC_WLINK_SCC: + mode_v0 = BTC_WLINK_V0_2G_SCC; + break; + case BTC_WLINK_SB_MCC: + mode_v0 = BTC_WLINK_V0_2G_MCC; + break; + default: + mode_v0 = BTC_WLINK_V0_OTHER; + break; + } + } + + switch (mode_v0) { case BTC_WLINK_NOLINK: _action_wl_nc(rtwdev); break; - case BTC_WLINK_2G_STA: + case BTC_WLINK_V0_2G_STA: if (wl->status.map.traffic_dir & BIT(RTW89_TFC_DL)) bt->scan_rx_low_pri = true; _action_wl_2g_sta(rtwdev); break; - case BTC_WLINK_2G_AP: + case BTC_WLINK_V0_2G_AP: bt->scan_rx_low_pri = true; _action_wl_2g_ap(rtwdev); break; - case BTC_WLINK_2G_GO: + case BTC_WLINK_V0_2G_GO: bt->scan_rx_low_pri = true; _action_wl_2g_go(rtwdev); break; - case BTC_WLINK_2G_GC: + case BTC_WLINK_V0_2G_GC: bt->scan_rx_low_pri = true; _action_wl_2g_gc(rtwdev); break; - case BTC_WLINK_2G_SCC: + case BTC_WLINK_V0_2G_SCC: bt->scan_rx_low_pri = true; _action_wl_2g_scc(rtwdev); break; - case BTC_WLINK_2G_MCC: + case BTC_WLINK_V0_2G_MCC: bt->scan_rx_low_pri = true; _action_wl_2g_mcc(rtwdev); break; - case BTC_WLINK_25G_MCC: + case BTC_WLINK_V0_25G_MCC: bt->scan_rx_low_pri = true; _action_wl_25g_mcc(rtwdev); break; - case BTC_WLINK_5G: + case BTC_WLINK_V0_5G: _action_wl_5g(rtwdev); break; - case BTC_WLINK_2G_NAN: + case BTC_WLINK_V0_2G_NAN: _action_wl_2g_nan(rtwdev); break; default: @@ -8678,22 +8206,6 @@ static u8 _update_bt_rssi_level(struct rtw89_dev *rtwdev, u8 rssi) return rssi_level; } -static void _update_zb_coex_tbl(struct rtw89_dev *rtwdev) -{ - u8 mode = rtwdev->btc.cx.wl.role_info.link_mode; - u32 zb_tbl0 = 0xda5a5a5a, zb_tbl1 = 0xda5a5a5a; - - if (mode == BTC_WLINK_5G || rtwdev->btc.dm.freerun) { - zb_tbl0 = 0xffffffff; - zb_tbl1 = 0xffffffff; - } else if (mode == BTC_WLINK_25G_MCC) { - zb_tbl0 = 0xffffffff; /* for E5G slot */ - zb_tbl1 = 0xda5a5a5a; /* for E2G slot */ - } - rtw89_write32(rtwdev, R_BTC_ZB_COEX_TBL_0, zb_tbl0); - rtw89_write32(rtwdev, R_BTC_ZB_COEX_TBL_1, zb_tbl1); -} - #define BT_PROFILE_PROTOCOL_MASK GENMASK(7, 4) static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) @@ -8842,11 +8354,10 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, struct ieee80211_bss_conf *bss_conf; struct ieee80211_link_sta *link_sta; struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_link_info r = {0}; struct rtw89_btc_wl_link_info *wlinfo = NULL; - u8 mode = 0, rlink_id, link_mode_ori, pta_req_mac_ori, wa_type; + u8 mode = 0; rcu_read_lock(); @@ -8894,66 +8405,32 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], wifi_role=%d\n", rtwvif_link->wifi_role); - r.role = rtwvif_link->wifi_role; - r.phy = rtwvif_link->phy_idx; - r.pid = rtwvif_link->port; - r.active = true; - r.connected = MLME_LINKED; - r.bcn_period = bss_conf->beacon_int; - r.dtim_period = bss_conf->dtim_period; - r.band = chan->band_type; - r.ch = chan->channel; - r.bw = chan->band_width; - r.chdef.band = chan->band_type; - r.chdef.center_ch = chan->channel; - r.chdef.bw = chan->band_width; - r.chdef.chan = chan->primary_channel; + wlinfo = &wl->rlink_info[rtwvif_link->port][rtwvif_link->phy_idx]; + + wlinfo->mode = mode; + wlinfo->role = rtwvif_link->wifi_role; + wlinfo->phy = rtwvif_link->phy_idx; + wlinfo->pid = rtwvif_link->port; + wlinfo->active = true; + wlinfo->connected = MLME_LINKED; + wlinfo->bcn_period = bss_conf->beacon_int; + wlinfo->dtim_period = bss_conf->dtim_period; + wlinfo->band = chan->band_type; + wlinfo->ch = chan->channel; + wlinfo->bw = chan->band_width; + wlinfo->chdef.band = chan->band_type; + wlinfo->chdef.center_ch = chan->channel; + wlinfo->chdef.bw = chan->band_width; + wlinfo->chdef.chan = chan->primary_channel; ether_addr_copy(r.mac_addr, rtwvif_link->mac_addr); rcu_read_unlock(); if (rtwsta_link && vif->type == NL80211_IFTYPE_STATION) - r.mac_id = rtwsta_link->mac_id; + wlinfo->mac_id = rtwsta_link->mac_id; btc->dm.cnt_notify[BTC_NCNT_ROLE_INFO]++; - wlinfo = &wl->link_info[r.pid]; - - if (ver->fwlrole == 0) { - *wlinfo = r; - _update_wl_info(rtwdev); - } else if (ver->fwlrole == 1) { - *wlinfo = r; - _update_wl_info_v1(rtwdev); - } else if (ver->fwlrole == 2) { - *wlinfo = r; - _update_wl_info_v2(rtwdev); - } else if (ver->fwlrole == 7) { - *wlinfo = r; - _update_wl_info_v7(rtwdev, r.pid); - } else if (ver->fwlrole == 8) { - rlink_id = rtwvif_link->mac_idx; - wlinfo = &wl->rlink_info[r.pid][rlink_id]; - *wlinfo = r; - link_mode_ori = wl->role_info_v8.link_mode; - pta_req_mac_ori = wl->pta_req_mac; - _update_wl_info_v8(rtwdev, r.pid, rlink_id, state); - - if (wl->role_info_v8.link_mode != link_mode_ori) { - wl->role_info_v8.link_mode_chg = 1; - if (ver->fcxinit == 7) - wa_type = btc->mdinfo.md_v7.wa_type; - else - wa_type = btc->mdinfo.md.wa_type; - - if (wa_type & BTC_WA_HFP_ZB) - _update_zb_coex_tbl(rtwdev); - } - - if (wl->pta_req_mac != pta_req_mac_ori) - wl->pta_reg_mac_chg = 1; - } - if (wlinfo->role == RTW89_WIFI_ROLE_STATION && wlinfo->connected == MLME_NO_LINK) btc->dm.leak_ap = 0; @@ -8967,6 +8444,8 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, state == BTC_ROLE_MSTS_STA_CONN_END) wl->status.map._4way = false; + _update_wl_info(rtwdev, wlinfo); + _run_coex(rtwdev, BTC_RSN_NTFY_ROLE_INFO); } @@ -9150,15 +8629,14 @@ void __rtw89_btc_ntfy_wl_sta_iter(struct rtw89_vif_link *rtwvif_link, struct rtw89_dev *rtwdev = iter_data->rtwdev; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_link_info *link_info = NULL; struct rtw89_traffic_stats *link_info_t = NULL; struct rtw89_traffic_stats *stats = &rtwvif->stats; const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc_wl_role_info *r; - struct rtw89_btc_wl_role_info_v1 *r1; u32 last_tx_rate, last_rx_rate; + u8 link = rtwvif_link->phy_idx; u16 last_tx_lvl, last_rx_lvl; u8 port = rtwvif_link->port; u8 rssi; @@ -9171,10 +8649,7 @@ void __rtw89_btc_ntfy_wl_sta_iter(struct rtw89_vif_link *rtwvif_link, rssi = ewma_rssi_read(&rtwsta_link->avg_rssi) >> RSSI_FACTOR; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], rssi=%d\n", rssi); - if (btc->ver->fwlrole != 8) - link_info = &wl->link_info[port]; - else - link_info = &wl->rlink_info[port][rtwvif_link->mac_idx]; + link_info = &wl->rlink_info[port][link]; link_info->stat.traffic = *stats; link_info_t = &link_info->stat.traffic; @@ -9214,7 +8689,6 @@ void __rtw89_btc_ntfy_wl_sta_iter(struct rtw89_vif_link *rtwvif_link, else dir = RTW89_TFC_DL; - link_info = &wl->link_info[port]; if (link_info->busy != busy || link_info->dir != dir) { is_sta_change = true; link_info->busy = busy; @@ -9244,19 +8718,11 @@ void __rtw89_btc_ntfy_wl_sta_iter(struct rtw89_vif_link *rtwvif_link, dm->trx_info.rx_rate = link_info_t->rx_rate; } - if (ver->fwlrole == 0) { - r = &wl->role_info; - r->active_role[port].tx_lvl = stats->tx_tfc_lv; - r->active_role[port].rx_lvl = stats->rx_tfc_lv; - r->active_role[port].tx_rate = rtwsta_link->ra_report.hw_rate; - r->active_role[port].rx_rate = rtwsta_link->rx_hw_rate; - } else if (ver->fwlrole == 1) { - r1 = &wl->role_info_v1; - r1->active_role_v1[port].tx_lvl = stats->tx_tfc_lv; - r1->active_role_v1[port].rx_lvl = stats->rx_tfc_lv; - r1->active_role_v1[port].tx_rate = rtwsta_link->ra_report.hw_rate; - r1->active_role_v1[port].rx_rate = rtwsta_link->rx_hw_rate; - } + r = &wl->role_info; + r->rlink[port][link].tx_lvl = stats->tx_tfc_lv; + r->rlink[port][link].rx_lvl = stats->rx_tfc_lv; + r->rlink[port][link].tx_rate = rtwsta_link->ra_report.hw_rate; + r->rlink[port][link].rx_rate = rtwsta_link->rx_hw_rate; dm->trx_info.tx_lvl = stats->tx_tfc_lv; dm->trx_info.rx_lvl = stats->rx_tfc_lv; @@ -9584,10 +9050,7 @@ static int _show_wl_role_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) for (i = 0; i < btc->ver->max_role_num; i++) { for (j = 0; j < RTW89_MAC_NUM; j++) { - if (btc->ver->fwlrole == 8) - plink = &btc->cx.wl.rlink_info[i][j]; - else - plink = &btc->cx.wl.link_info[i]; + plink = &btc->cx.wl.rlink_info[i][j]; if (!plink->active) continue; @@ -9630,14 +9093,9 @@ static int _show_wl_role_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; - struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; - struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; - struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7; - struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; char *p = buf, *end = buf + bufsz; u8 mode; @@ -9646,18 +9104,7 @@ static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, "========== [WL Status] ==========\n"); - if (ver->fwlrole == 0) - mode = wl_rinfo->link_mode; - else if (ver->fwlrole == 1) - mode = wl_rinfo_v1->link_mode; - else if (ver->fwlrole == 2) - mode = wl_rinfo_v2->link_mode; - else if (ver->fwlrole == 7) - mode = wl_rinfo_v7->link_mode; - else if (ver->fwlrole == 8) - mode = wl_rinfo_v8->link_mode; - else - goto out; + mode = wl_rinfo->link_mode; p += scnprintf(p, end - p, " %-15s : link_mode:%s, ", "[status]", id_to_linkmode(mode)); @@ -9677,7 +9124,6 @@ static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += _show_wl_role_info(rtwdev, p, end - p); -out: return p - buf; } @@ -9755,6 +9201,8 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) union rtw89_btc_module_info *md = &btc->mdinfo; s8 br_dbm = bt->link_info.bt_txpwr_desc.br_dbm; s8 le_dbm = bt->link_info.bt_txpwr_desc.le_dbm; + u8 hw_band = wl->role_info.pta_req_band; + struct rtw89_btc_wl_afh_info *wl_afh; char *p = buf, *end = buf + bufsz; u8 *afh = bt_linfo->afh_map; u8 *afh_le = bt_linfo->afh_map_le; @@ -9822,8 +9270,9 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) afh_le[0], afh_le[1], afh_le[2], afh_le[3], afh_le[4]); - p += scnprintf(p, end - p, "wl_ch_map[en:%d/ch:%d/bw:%d]\n", - wl->afh_info.en, wl->afh_info.ch, wl->afh_info.bw); + wl_afh = &wl->afh_info[hw_band][RTW89_BAND_2G]; + p += scnprintf(p, end - p, "wl_ch_map[hwb:%d/en:%d/ch:%d/bw:%d/rf_band:%d]\n", + hw_band, wl_afh->en, wl_afh->ch, wl_afh->bw, wl_afh->band); p += scnprintf(p, end - p, " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ", diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index 74027ca4eccc..c127bd80d31c 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -11,6 +11,40 @@ #define BTC_TLV_SLOT_ID_LEN_V7 1 #define BTC_SLOT_REQ_TH 2 +#define BTC_FREQ_W2G 2412 +#define BTC_FREQ_W5G 5005 +#define BTC_FREQ_W6G 5955 + +#define BTC_FREQ_B2G 2402 +#define BTC_FREQ_B6G 5125 + +#define BTC_FREQ_W2G_CH14 2484 + +#define BTC_LO_2G_DNM 125 /* LO Coefficient Denominator */ +#define BTC_LO_5G_DNM 125 +#define BTC_LO_6G_DNM 150 + +#define BTC_LO_2G_NMR 400 /* LO Coefficient Numerator */ +#define BTC_LO_5G_NMR 200 +#define BTC_LO_6G_NMR 200 + +#define BTC_CH_W2G_MIN 1 /* start from 2412MHz */ +#define BTC_CH_W2G_MAX 14 +#define BTC_CH_W5G_MIN 1 /* start from 5005MHz */ +#define BTC_CH_W5G_MAX 165 +#define BTC_CH_W6G_MIN 1 /* start from 5955MHz */ +#define BTC_CH_W6G_MAX 233 + +#define BTC_CH_B2G_MIN 0 /* start from 2402MHz */ +#define BTC_CH_B2G_MAX 78 + +#define BTC_CH_B5G_MIN 600 /* start from 5725MHz */ +#define BTC_CH_B5G_MAX 1300 + +#define BTC_VCO_GUARD_W2W 10 /* WiFi to WiFi VCO freq forbiddened range (MHZ) */ +#define BTC_VCO_GUARD_W2B 25 /* WiFi to WiFi VCO freq forbiddened range (MHZ) */ +#define BTC_VCO_GUARD_B2B 10 /* WiFi to WiFi VCO freq forbiddened range (MHZ) */ + enum btc_mode { BTC_MODE_NORMAL, BTC_MODE_WL, diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 174349c6bc58..ded788534651 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1854,7 +1854,7 @@ union rtw89_btc_wl_role_info_map { struct rtw89_btc_wl_role_info_bpos role; }; -struct rtw89_btc_wl_role_info { /* struct size must be n*4 bytes */ +struct rtw89_btc_wl_role_info_v0 { /* struct size must be n*4 bytes */ u8 connect_cnt; u8 link_mode; union rtw89_btc_wl_role_info_map role_map; @@ -1891,7 +1891,7 @@ struct rtw89_btc_wl_role_info_v2 { /* struct size must be n*4 bytes */ u32 rsvd: 27; }; -struct rtw89_btc_wl_rlink { /* H2C info, struct size must be n*4 bytes */ +struct rtw89_btc_wl_rlink_v0 { /* H2C info, struct size must be n*4 bytes */ u8 connected; u8 pid; u8 phy; @@ -1908,6 +1908,28 @@ struct rtw89_btc_wl_rlink { /* H2C info, struct size must be n*4 bytes */ u8 mode; /* wifi protocol */ } __packed; +struct rtw89_btc_wl_rlink_v10 { /* H2C info, struct size must be n*4 bytes */ + u8 connected; + u8 pid; + u8 phy; + u8 noa; + + u8 rf_band; /* enum band_type RF band: 2.4G/5G/6G */ + u8 active; /* 0:rlink is under doze */ + u8 bw; /* enum channel_width */ + u8 role; /*enum role_type */ + + u8 ch; + u8 noa_dur; /* ms */ + u8 client_cnt; /* for Role = P2P-Go/AP */ + u8 mode; /* wifi protocol */ + + u8 mac_id; + u8 rsvd0; + u8 rsvd1; + u8 rsvd2; +} __packed; + #define RTW89_BE_BTC_WL_MAX_ROLE_NUMBER 6 struct rtw89_btc_wl_role_info_v7 { /* struct size must be n*4 bytes */ u8 connect_cnt; @@ -1917,12 +1939,12 @@ struct rtw89_btc_wl_role_info_v7 { /* struct size must be n*4 bytes */ struct rtw89_btc_wl_active_role_v7 active_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER]; - u32 role_map; - u32 mrole_type; /* btc_wl_mrole_type */ - u32 mrole_noa_duration; /* ms */ - u32 dbcc_en; - u32 dbcc_chg; - u32 dbcc_2g_phy; /* which phy operate in 2G, HW_PHY_0 or HW_PHY_1 */ + __le32 role_map; + __le32 mrole_type; /* btc_wl_mrole_type */ + __le32 mrole_noa_duration; /* ms */ + __le32 dbcc_en; + __le32 dbcc_chg; + __le32 dbcc_2g_phy; /* which phy operate in 2G, HW_PHY_0 or HW_PHY_1 */ } __packed; struct rtw89_btc_wl_role_info_v8 { /* H2C info, struct size must be n*4 bytes */ @@ -1936,12 +1958,90 @@ struct rtw89_btc_wl_role_info_v8 { /* H2C info, struct size must be n*4 bytes */ u8 dbcc_chg; u8 dbcc_2g_phy; /* which phy operate in 2G, HW_PHY_0 or HW_PHY_1 */ + struct rtw89_btc_wl_rlink_v0 rlink[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER][RTW89_MAC_NUM]; + + __le32 role_map; + __le32 mrole_type; /* btc_wl_mrole_type */ + __le32 mrole_noa_duration; /* ms */ +} __packed; + +struct rtw89_btc_wl_role_info_v10 { /* H2C info, struct size must be n*4 bytes */ + struct rtw89_btc_wl_rlink_v10 rlink[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER][RTW89_MAC_NUM]; + u8 link_mode; + u8 link_mode_hb1; + u8 p2p_exist; + u8 p2p_exist_hb1; + + u8 pta_req_band; + u8 dbcc_en; /* 1+1 and 2.4G-included */ + u8 dbcc_2g_phy; /* which phy operate in 2G, HW_PHY_0 or HW_PHY_1 */ + u8 rsvd; + + __le32 role_map; + __le32 role_map_hb1; + __le32 mrole_type; /* btc_wl_mrole_type: [31:16]:band1, [15:0]:band0 */ +} __packed; + +struct rtw89_btc_wl_rlink { /* Logic dynamic using */ + u8 connected; + u8 pid; + u8 phy; + u8 noa; + + u8 rf_band; /* enum band_type RF band: 2.4G/5G/6G */ + u8 active; /* 0:rlink is under doze */ + u8 bw; /* enum channel_width */ + u8 role; /*enum role_type */ + + u8 ch; + u8 noa_dur; /* ms */ + u8 client_cnt; /* for Role = P2P-Go/AP */ + u8 mode; /* wifi protocol */ + + /*v0 v1*/ + u16 tx_lvl; + u16 rx_lvl; + u16 tx_rate; + u16 rx_rate; + + /* v7 */ + u8 client_ps; /*v7 v2 v1 v0*/ + + /* v10 */ + u8 mac_id; + u8 rsvd0; + u8 rsvd1; + u8 rsvd2; +}; + +struct rtw89_btc_wl_role_info { /* Logic dynamic using */ struct rtw89_btc_wl_rlink rlink[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER][RTW89_MAC_NUM]; + u8 link_mode; + u8 link_mode_hb1; + u8 p2p_exist; + u8 p2p_exist_hb1; + + u8 pta_req_band; + u8 dbcc_en; /* 1+1 and 2.4G-included */ + u8 dbcc_2g_phy; /* which phy operate in 2G, HW_PHY_0 or HW_PHY_1 */ + u8 rsvd; u32 role_map; - u32 mrole_type; /* btc_wl_mrole_type */ - u32 mrole_noa_duration; /* ms */ -} __packed; + u32 role_map_hb1; + u32 mrole_type; /* btc_wl_mrole_type: [31:16]:band1, [15:0]:band0 */ + + /* Before v10 use this linkmode */ + u8 link_mode_v0; + + /* v7 */ + u8 connect_cnt; + u32 dbcc_chg; /* v7 v2*/ + + /* v8 */ + u8 link_mode_chg; /* v8, v7 v2*/ + u8 p2p_2g; /* v8, v7 */ + u32 mrole_noa_duration; /* v8, v7 v2*/ +}; struct rtw89_btc_wl_ver_info { u32 fw_coex; /* match with which coex_ver */ @@ -1955,7 +2055,7 @@ struct rtw89_btc_wl_afh_info { u8 en; u8 ch; u8 bw; - u8 rsvd; + u8 band; } __packed; struct rtw89_btc_wl_rfk_info { @@ -2167,16 +2267,13 @@ struct rtw89_btc_wl_nhm { }; struct rtw89_btc_wl_info { - struct rtw89_btc_wl_link_info link_info[RTW89_PORT_NUM]; struct rtw89_btc_wl_link_info rlink_info[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER][RTW89_MAC_NUM]; + struct rtw89_btc_chdef rf_ch_info[RTW89_PHY_NUM]; struct rtw89_btc_wl_rfk_info rfk_info; - struct rtw89_btc_wl_ver_info ver_info; - struct rtw89_btc_wl_afh_info afh_info; + struct rtw89_btc_wl_ver_info ver_info; + struct rtw89_btc_wl_afh_info afh_info[RTW89_MAC_NUM][RTW89_BAND_NUM]; + struct rtw89_btc_wl_afh_info afh_info_last[RTW89_MAC_NUM][RTW89_BAND_NUM]; struct rtw89_btc_wl_role_info role_info; - struct rtw89_btc_wl_role_info_v1 role_info_v1; - struct rtw89_btc_wl_role_info_v2 role_info_v2; - struct rtw89_btc_wl_role_info_v7 role_info_v7; - struct rtw89_btc_wl_role_info_v8 role_info_v8; struct rtw89_btc_wl_scan_info scan_info; struct rtw89_btc_wl_dbcc_info dbcc_info; struct rtw89_btc_wl_mlo_info mlo_info; @@ -2191,12 +2288,18 @@ struct rtw89_btc_wl_info { u8 pta_req_mac; u8 bt_polut_type[RTW89_PHY_NUM]; /* BT polluted WL-Tx type for phy0/1 */ u8 rf_band_map[RTW89_PHY_NUM]; /* rf_band bit-map */ + u8 ch_map[12]; + u8 ch_map_le[5]; - bool is_5g_hi_channel; + bool is_5g_hi_ch; + bool is_5g_hi_ch_hb1; bool go_client_exist; + bool go_client_exist_hb1; bool noa_exist; + bool noa_exist_hb1; bool pta_reg_mac_chg; bool bg_mode; + bool bg_mode_hb1; bool he_mode; bool scbd_chg[BTC_ALL_BT]; bool fw_ver_mismatch; @@ -3280,6 +3383,14 @@ struct rtw89_btc_fbtc_outsrc_set_info { u8 rf_gbt_source; u8 bt_enable_state; u8 wl_btg_standby_chg; + /* bit[15]-> 0:2G/1:5G,6G, bit[14:0]-> WL HWBx ch freq in MHz */ + /* forbidden group-> bit[1]:fbd rf-band, bit[0]: fbd enable */ + u8 fbd_group_en[RTW89_MAC_NUM][2]; /* HWB0/1 +.Group0/1 */ + u16 rf_center_freq[RTW89_MAC_NUM]; /* HWB0/1 */ + /* forbidden group boundary: [15:8]->UP, [7:0]->LO */ + u16 fbd_group_bound[RTW89_MAC_NUM][2]; /* HWB0/1 +.Group0/1 */ + /* 11-bit in MHz, freq diff threshold */ + u16 freq_diff_thres[RTW89_MAC_NUM][BTC_ALL_BT_EZL]; /* HWB0/1 vs.BT0/1/2 */ } __packed; union rtw89_btc_fbtc_slot_u { @@ -3363,6 +3474,7 @@ struct rtw89_btc_dm { u8 lps_ctrl_change: 1; u8 scbd_write_instant; bool scbd_b2w_update; + bool pre_agc_chg; }; struct rtw89_btc_ctrl { @@ -4961,6 +5073,7 @@ struct rtw89_chip_info { u8 mailbox; u8 afh_guard_ch; + u16 fdd_iso_freq; const u8 *wl_rssi_thres; const u8 *bt_rssi_thres; u8 rssi_tol; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 0e7168605850..41c033d2ae7b 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -5922,14 +5922,14 @@ int rtw89_fw_h2c_cxdrv_init_v10(struct rtw89_dev *rtwdev, u8 type) int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_role_info *role_info = &wl->role_info; - struct rtw89_btc_wl_role_info_bpos *bpos = &role_info->role_map.role; - struct rtw89_btc_wl_active_role *active = role_info->active_role; + struct rtw89_btc_wl_role_info *r = &wl->role_info; + const struct rtw89_btc_ver *ver = btc->ver; + struct rtw89_btc_wl_rlink *rl; struct sk_buff *skb; - u32 len; + u32 rmap = r->role_map; u8 offset = 0; + u32 len; u8 *cmd; int ret; int i; @@ -5947,36 +5947,37 @@ int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev, u8 type) RTW89_SET_FWCMD_CXHDR_TYPE(cmd, type); RTW89_SET_FWCMD_CXHDR_LEN(cmd, len - H2C_LEN_CXDRVHDR); - RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, role_info->connect_cnt); - RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, role_info->link_mode); + RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, r->connect_cnt); + RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode); - RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, bpos->none); - RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, bpos->station); - RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, bpos->ap); - RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, bpos->vap); - RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, bpos->adhoc); - RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, bpos->adhoc_master); - RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, bpos->mesh); - RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, bpos->moniter); - RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, bpos->p2p_device); - RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, bpos->p2p_gc); - RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, bpos->p2p_go); - RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, bpos->nan); + RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NONE))); + RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_STATION))); + RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP))); + RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP_VLAN))); + RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC))); + RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC_MASTER))); + RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MESH_POINT))); + RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MONITOR))); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_DEVICE))); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_CLIENT))); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_GO))); + RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NAN))); - for (i = 0; i < RTW89_PORT_NUM; i++, active++) { - RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED(cmd, active->connected, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_PID(cmd, active->pid, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_PHY(cmd, active->phy, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_NOA(cmd, active->noa, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_BAND(cmd, active->band, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS(cmd, active->client_ps, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_BW(cmd, active->bw, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_ROLE(cmd, active->role, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_CH(cmd, active->ch, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_TX_LVL(cmd, active->tx_lvl, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_RX_LVL(cmd, active->rx_lvl, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_TX_RATE(cmd, active->tx_rate, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_RX_RATE(cmd, active->rx_rate, i, offset); + for (i = 0; i < RTW89_PORT_NUM; i++) { + rl = &r->rlink[i][RTW89_MAC_0]; + RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED(cmd, rl->connected, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_PID(cmd, rl->pid, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_PHY(cmd, rl->phy, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_NOA(cmd, rl->noa, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_BAND(cmd, rl->rf_band, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS(cmd, rl->client_ps, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_BW(cmd, rl->bw, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_ROLE(cmd, rl->role, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_CH(cmd, rl->ch, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_TX_LVL(cmd, rl->tx_lvl, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_RX_LVL(cmd, rl->rx_lvl, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_TX_RATE(cmd, rl->tx_rate, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_RX_RATE(cmd, rl->rx_rate, i, offset); } rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, @@ -6005,12 +6006,12 @@ int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type) struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_role_info_v1 *role_info = &wl->role_info_v1; - struct rtw89_btc_wl_role_info_bpos *bpos = &role_info->role_map.role; - struct rtw89_btc_wl_active_role_v1 *active = role_info->active_role_v1; + struct rtw89_btc_wl_role_info *r = &wl->role_info; + struct rtw89_btc_wl_rlink *rl; struct sk_buff *skb; - u32 len; + u32 rmap = r->role_map; u8 *cmd, offset; + u32 len; int ret; int i; @@ -6027,47 +6028,49 @@ int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type) RTW89_SET_FWCMD_CXHDR_TYPE(cmd, type); RTW89_SET_FWCMD_CXHDR_LEN(cmd, len - H2C_LEN_CXDRVHDR); - RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, role_info->connect_cnt); - RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, role_info->link_mode); + RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, r->connect_cnt); + RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode); - RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, bpos->none); - RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, bpos->station); - RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, bpos->ap); - RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, bpos->vap); - RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, bpos->adhoc); - RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, bpos->adhoc_master); - RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, bpos->mesh); - RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, bpos->moniter); - RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, bpos->p2p_device); - RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, bpos->p2p_gc); - RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, bpos->p2p_go); - RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, bpos->nan); + RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NONE))); + RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_STATION))); + RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP))); + RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP_VLAN))); + RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC))); + RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC_MASTER))); + RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MESH_POINT))); + RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MONITOR))); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_DEVICE))); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_CLIENT))); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_GO))); + RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NAN))); offset = PORT_DATA_OFFSET; - for (i = 0; i < RTW89_PORT_NUM; i++, active++) { - RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED(cmd, active->connected, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_PID(cmd, active->pid, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_PHY(cmd, active->phy, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_NOA(cmd, active->noa, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_BAND(cmd, active->band, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS(cmd, active->client_ps, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_BW(cmd, active->bw, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_ROLE(cmd, active->role, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_CH(cmd, active->ch, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_TX_LVL(cmd, active->tx_lvl, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_RX_LVL(cmd, active->rx_lvl, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_TX_RATE(cmd, active->tx_rate, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_RX_RATE(cmd, active->rx_rate, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_NOA_DUR(cmd, active->noa_duration, i, offset); + + for (i = 0; i < RTW89_PORT_NUM; i++) { + rl = &r->rlink[i][RTW89_MAC_0]; + RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED(cmd, rl->connected, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_PID(cmd, rl->pid, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_PHY(cmd, rl->phy, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_NOA(cmd, rl->noa, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_BAND(cmd, rl->rf_band, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS(cmd, rl->client_ps, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_BW(cmd, rl->bw, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_ROLE(cmd, rl->role, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_CH(cmd, rl->ch, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_TX_LVL(cmd, rl->tx_lvl, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_RX_LVL(cmd, rl->rx_lvl, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_TX_RATE(cmd, rl->tx_rate, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_RX_RATE(cmd, rl->rx_rate, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_NOA_DUR(cmd, rl->noa_dur, i, offset); } offset = len - H2C_LEN_CXDRVINFO_ROLE_DBCC_LEN; - RTW89_SET_FWCMD_CXROLE_MROLE_TYPE(cmd, role_info->mrole_type, offset); - RTW89_SET_FWCMD_CXROLE_MROLE_NOA(cmd, role_info->mrole_noa_duration, offset); - RTW89_SET_FWCMD_CXROLE_DBCC_EN(cmd, role_info->dbcc_en, offset); - RTW89_SET_FWCMD_CXROLE_DBCC_CHG(cmd, role_info->dbcc_chg, offset); - RTW89_SET_FWCMD_CXROLE_DBCC_2G_PHY(cmd, role_info->dbcc_2g_phy, offset); - RTW89_SET_FWCMD_CXROLE_LINK_MODE_CHG(cmd, role_info->link_mode_chg, offset); + RTW89_SET_FWCMD_CXROLE_MROLE_TYPE(cmd, r->mrole_type, offset); + RTW89_SET_FWCMD_CXROLE_MROLE_NOA(cmd, r->mrole_noa_duration, offset); + RTW89_SET_FWCMD_CXROLE_DBCC_EN(cmd, r->dbcc_en, offset); + RTW89_SET_FWCMD_CXROLE_DBCC_CHG(cmd, r->dbcc_chg, offset); + RTW89_SET_FWCMD_CXROLE_DBCC_2G_PHY(cmd, r->dbcc_2g_phy, offset); + RTW89_SET_FWCMD_CXROLE_LINK_MODE_CHG(cmd, r->link_mode_chg, offset); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, BTFC_SET, @@ -6095,10 +6098,10 @@ int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type) struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_role_info_v2 *role_info = &wl->role_info_v2; - struct rtw89_btc_wl_role_info_bpos *bpos = &role_info->role_map.role; - struct rtw89_btc_wl_active_role_v2 *active = role_info->active_role_v2; + struct rtw89_btc_wl_role_info *r = &wl->role_info; + struct rtw89_btc_wl_rlink *rl; struct sk_buff *skb; + u32 rmap = r->role_map; u32 len; u8 *cmd, offset; int ret; @@ -6117,43 +6120,45 @@ int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type) RTW89_SET_FWCMD_CXHDR_TYPE(cmd, type); RTW89_SET_FWCMD_CXHDR_LEN(cmd, len - H2C_LEN_CXDRVHDR); - RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, role_info->connect_cnt); - RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, role_info->link_mode); + RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, r->connect_cnt); + RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, r->link_mode); - RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, bpos->none); - RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, bpos->station); - RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, bpos->ap); - RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, bpos->vap); - RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, bpos->adhoc); - RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, bpos->adhoc_master); - RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, bpos->mesh); - RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, bpos->moniter); - RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, bpos->p2p_device); - RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, bpos->p2p_gc); - RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, bpos->p2p_go); - RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, bpos->nan); + RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NONE))); + RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_STATION))); + RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP))); + RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_AP_VLAN))); + RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC))); + RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_ADHOC_MASTER))); + RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MESH_POINT))); + RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_MONITOR))); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_DEVICE))); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_CLIENT))); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_P2P_GO))); + RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, !!(rmap & BIT(RTW89_WIFI_ROLE_NAN))); offset = PORT_DATA_OFFSET; - for (i = 0; i < RTW89_PORT_NUM; i++, active++) { - RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED_V2(cmd, active->connected, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_PID_V2(cmd, active->pid, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_PHY_V2(cmd, active->phy, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_NOA_V2(cmd, active->noa, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_BAND_V2(cmd, active->band, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS_V2(cmd, active->client_ps, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_BW_V2(cmd, active->bw, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_ROLE_V2(cmd, active->role, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_CH_V2(cmd, active->ch, i, offset); - RTW89_SET_FWCMD_CXROLE_ACT_NOA_DUR_V2(cmd, active->noa_duration, i, offset); + + for (i = 0; i < RTW89_PORT_NUM; i++) { + rl = &r->rlink[i][RTW89_MAC_0]; + RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED_V2(cmd, rl->connected, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_PID_V2(cmd, rl->pid, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_PHY_V2(cmd, rl->phy, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_NOA_V2(cmd, rl->noa, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_BAND_V2(cmd, rl->rf_band, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS_V2(cmd, rl->client_ps, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_BW_V2(cmd, rl->bw, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_ROLE_V2(cmd, rl->role, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_CH_V2(cmd, rl->ch, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_NOA_DUR_V2(cmd, rl->noa_dur, i, offset); } offset = len - H2C_LEN_CXDRVINFO_ROLE_DBCC_LEN; - RTW89_SET_FWCMD_CXROLE_MROLE_TYPE(cmd, role_info->mrole_type, offset); - RTW89_SET_FWCMD_CXROLE_MROLE_NOA(cmd, role_info->mrole_noa_duration, offset); - RTW89_SET_FWCMD_CXROLE_DBCC_EN(cmd, role_info->dbcc_en, offset); - RTW89_SET_FWCMD_CXROLE_DBCC_CHG(cmd, role_info->dbcc_chg, offset); - RTW89_SET_FWCMD_CXROLE_DBCC_2G_PHY(cmd, role_info->dbcc_2g_phy, offset); - RTW89_SET_FWCMD_CXROLE_LINK_MODE_CHG(cmd, role_info->link_mode_chg, offset); + RTW89_SET_FWCMD_CXROLE_MROLE_TYPE(cmd, r->mrole_type, offset); + RTW89_SET_FWCMD_CXROLE_MROLE_NOA(cmd, r->mrole_noa_duration, offset); + RTW89_SET_FWCMD_CXROLE_DBCC_EN(cmd, r->dbcc_en, offset); + RTW89_SET_FWCMD_CXROLE_DBCC_CHG(cmd, r->dbcc_chg, offset); + RTW89_SET_FWCMD_CXROLE_DBCC_2G_PHY(cmd, r->dbcc_2g_phy, offset); + RTW89_SET_FWCMD_CXROLE_LINK_MODE_CHG(cmd, r->link_mode_chg, offset); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, BTFC_SET, @@ -6175,12 +6180,14 @@ int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type) int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type) { - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_role_info_v7 *role = &btc->cx.wl.role_info_v7; + struct rtw89_btc_wl_role_info *r = &rtwdev->btc.cx.wl.role_info; struct rtw89_h2c_cxrole_v7 *h2c; + struct rtw89_btc_wl_rlink *rl; u32 len = sizeof(*h2c); struct sk_buff *skb; + __le32 rm; int ret; + u8 i; skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { @@ -6190,16 +6197,35 @@ int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type) skb_put(skb, len); h2c = (struct rtw89_h2c_cxrole_v7 *)skb->data; - h2c->hdr.type = type; - h2c->hdr.ver = btc->ver->fwlrole; - h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; - memcpy(&h2c->_u8, role, sizeof(h2c->_u8)); - h2c->_u32.role_map = cpu_to_le32(role->role_map); - h2c->_u32.mrole_type = cpu_to_le32(role->mrole_type); - h2c->_u32.mrole_noa_duration = cpu_to_le32(role->mrole_noa_duration); - h2c->_u32.dbcc_en = cpu_to_le32(role->dbcc_en); - h2c->_u32.dbcc_chg = cpu_to_le32(role->dbcc_chg); - h2c->_u32.dbcc_2g_phy = cpu_to_le32(role->dbcc_2g_phy); + h2c->r.connect_cnt = r->connect_cnt; + h2c->r.link_mode = r->link_mode; + h2c->r.link_mode_chg = r->link_mode_chg; + h2c->r.p2p_2g = r->p2p_2g; + + for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { + rl = &r->rlink[i][RTW89_MAC_0]; + h2c->r.active_role[i].connected = rl->connected; + h2c->r.active_role[i].pid = rl->pid; + h2c->r.active_role[i].phy = rl->phy; + h2c->r.active_role[i].noa = rl->noa; + + h2c->r.active_role[i].band = rl->rf_band; + h2c->r.active_role[i].client_ps = rl->client_ps; + h2c->r.active_role[i].bw = rl->bw; + h2c->r.active_role[i].role = rl->role; + + h2c->r.active_role[i].ch = rl->ch; + h2c->r.active_role[i].noa_dur = rl->noa_dur; + h2c->r.active_role[i].client_cnt = rl->client_cnt; + } + + rm = cpu_to_le32(r->role_map); + memcpy(&h2c->r.role_map, &rm, sizeof(h2c->r.role_map)); + h2c->r.mrole_type = cpu_to_le32(r->mrole_type); + h2c->r.mrole_noa_duration = cpu_to_le32(r->mrole_noa_duration); + h2c->r.dbcc_en = cpu_to_le32((u32)r->dbcc_en); + h2c->r.dbcc_chg = cpu_to_le32(r->dbcc_chg); + h2c->r.dbcc_2g_phy = cpu_to_le32(r->dbcc_2g_phy); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, BTFC_SET, @@ -6221,12 +6247,14 @@ int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type) int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type) { - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_role_info_v8 *role = &btc->cx.wl.role_info_v8; + struct rtw89_btc_wl_role_info *r = &rtwdev->btc.cx.wl.role_info; struct rtw89_h2c_cxrole_v8 *h2c; + struct rtw89_btc_wl_rlink *rl; u32 len = sizeof(*h2c); struct sk_buff *skb; + __le32 rm; int ret; + u8 i, j; skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { @@ -6237,12 +6265,118 @@ int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type) h2c = (struct rtw89_h2c_cxrole_v8 *)skb->data; h2c->hdr.type = type; - h2c->hdr.ver = btc->ver->fwlrole; + h2c->hdr.ver = rtwdev->btc.ver->fwlrole; h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; - memcpy(&h2c->_u8, role, sizeof(h2c->_u8)); - h2c->_u32.role_map = cpu_to_le32(role->role_map); - h2c->_u32.mrole_type = cpu_to_le32(role->mrole_type); - h2c->_u32.mrole_noa_duration = cpu_to_le32(role->mrole_noa_duration); + h2c->r.connect_cnt = r->connect_cnt; + h2c->r.link_mode = r->link_mode; + h2c->r.link_mode_chg = r->link_mode_chg; + h2c->r.p2p_2g = r->p2p_2g; + h2c->r.pta_req_band = r->pta_req_band; + h2c->r.dbcc_en = r->dbcc_en; + h2c->r.dbcc_chg = r->dbcc_chg; + h2c->r.dbcc_2g_phy = r->dbcc_2g_phy; + + for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) { + for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { + rl = &r->rlink[i][j]; + h2c->r.rlink[i][j].connected = rl->connected; + h2c->r.rlink[i][j].pid = rl->pid; + h2c->r.rlink[i][j].phy = rl->phy; + h2c->r.rlink[i][j].noa = rl->noa; + + h2c->r.rlink[i][j].rf_band = rl->rf_band; + h2c->r.rlink[i][j].active = rl->active; + h2c->r.rlink[i][j].bw = rl->bw; + h2c->r.rlink[i][j].role = rl->role; + + h2c->r.rlink[i][j].ch = rl->ch; + h2c->r.rlink[i][j].noa_dur = rl->noa_dur; + h2c->r.rlink[i][j].client_cnt = rl->client_cnt; + h2c->r.rlink[i][j].mode = rl->mode; + } + } + + rm = cpu_to_le32(r->role_map); + memcpy(&h2c->r.role_map, &rm, sizeof(h2c->r.role_map)); + h2c->r.mrole_type = cpu_to_le32(r->mrole_type); + h2c->r.mrole_noa_duration = cpu_to_le32(r->mrole_noa_duration); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, BTFC_SET, + SET_DRV_INFO, 0, 0, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc_wl_role_info *r = &rtwdev->btc.cx.wl.role_info; + struct rtw89_h2c_cxrole_v10 *h2c; + struct rtw89_btc_wl_rlink *rl; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + __le32 rm; + int ret; + u8 i, j; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_role\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxrole_v10 *)skb->data; + + h2c->hdr.type = type; + h2c->hdr.ver = rtwdev->btc.ver->fwlrole; + h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; + + for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) { + for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { + rl = &r->rlink[i][j]; + h2c->r.rlink[i][j].connected = rl->connected; + h2c->r.rlink[i][j].pid = rl->pid; + h2c->r.rlink[i][j].phy = rl->phy; + h2c->r.rlink[i][j].noa = rl->noa; + + h2c->r.rlink[i][j].rf_band = rl->rf_band; + h2c->r.rlink[i][j].active = rl->active; + h2c->r.rlink[i][j].bw = rl->bw; + h2c->r.rlink[i][j].role = rl->role; + + h2c->r.rlink[i][j].ch = rl->ch; + h2c->r.rlink[i][j].noa_dur = rl->noa_dur; + h2c->r.rlink[i][j].client_cnt = rl->client_cnt; + h2c->r.rlink[i][j].mode = rl->mode; + h2c->r.rlink[i][j].mac_id = rl->mac_id; + } + } + + h2c->r.link_mode = r->link_mode; + h2c->r.link_mode_hb1 = r->link_mode_hb1; + h2c->r.p2p_exist = r->p2p_exist; + h2c->r.p2p_exist_hb1 = r->p2p_exist_hb1; + + h2c->r.pta_req_band = r->pta_req_band; + h2c->r.dbcc_en = r->dbcc_en; + h2c->r.dbcc_2g_phy = r->dbcc_2g_phy; + + rm = cpu_to_le32(r->role_map); + memcpy(&h2c->r.role_map, &rm, sizeof(h2c->r.role_map)); + rm = cpu_to_le32(r->role_map_hb1); + memcpy(&h2c->r.role_map_hb1, &rm, sizeof(h2c->r.role_map_hb1)); + h2c->r.mrole_type = cpu_to_le32(r->mrole_type); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, BTFC_SET, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index af126d15a1fb..1e4267b22b36 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2433,54 +2433,19 @@ struct rtw89_h2c_cxctrl_v7 { #define H2C_LEN_CXDRVHDR sizeof(struct rtw89_h2c_cxhdr) #define H2C_LEN_CXDRVHDR_V7 sizeof(struct rtw89_h2c_cxhdr_v7) -struct rtw89_btc_wl_role_info_v7_u8 { - u8 connect_cnt; - u8 link_mode; - u8 link_mode_chg; - u8 p2p_2g; - - struct rtw89_btc_wl_active_role_v7 active_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER]; -} __packed; - -struct rtw89_btc_wl_role_info_v7_u32 { - __le32 role_map; - __le32 mrole_type; - __le32 mrole_noa_duration; - __le32 dbcc_en; - __le32 dbcc_chg; - __le32 dbcc_2g_phy; -} __packed; - struct rtw89_h2c_cxrole_v7 { struct rtw89_h2c_cxhdr_v7 hdr; - struct rtw89_btc_wl_role_info_v7_u8 _u8; - struct rtw89_btc_wl_role_info_v7_u32 _u32; -} __packed; - -struct rtw89_btc_wl_role_info_v8_u8 { - u8 connect_cnt; - u8 link_mode; - u8 link_mode_chg; - u8 p2p_2g; - - u8 pta_req_band; - u8 dbcc_en; - u8 dbcc_chg; - u8 dbcc_2g_phy; - - struct rtw89_btc_wl_rlink rlink[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER][RTW89_MAC_NUM]; -} __packed; - -struct rtw89_btc_wl_role_info_v8_u32 { - __le32 role_map; - __le32 mrole_type; - __le32 mrole_noa_duration; + struct rtw89_btc_wl_role_info_v7 r; } __packed; struct rtw89_h2c_cxrole_v8 { struct rtw89_h2c_cxhdr_v7 hdr; - struct rtw89_btc_wl_role_info_v8_u8 _u8; - struct rtw89_btc_wl_role_info_v8_u32 _u32; + struct rtw89_btc_wl_role_info_v8 r; +} __packed; + +struct rtw89_h2c_cxrole_v10 { + struct rtw89_h2c_cxhdr_v7 hdr; + struct rtw89_btc_wl_role_info_v10 r; } __packed; struct rtw89_h2c_cxosi { @@ -5413,6 +5378,7 @@ int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type); +int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 50480f72c96d..91d6cddc713e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2725,6 +2725,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .mailbox = 0x1, .afh_guard_ch = 6, + .fdd_iso_freq = 1000, .wl_rssi_thres = rtw89_btc_8851b_wl_rssi_thres, .bt_rssi_thres = rtw89_btc_8851b_bt_rssi_thres, .rssi_tol = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 0c7794742650..ed6dee63694f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2464,6 +2464,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .mailbox = 0x1, .afh_guard_ch = 6, + .fdd_iso_freq = 1000, .wl_rssi_thres = rtw89_btc_8852a_wl_rssi_thres, .bt_rssi_thres = rtw89_btc_8852a_bt_rssi_thres, .rssi_tol = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 5904127cc836..4f55a153d169 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -1060,6 +1060,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .mailbox = 0x1, .afh_guard_ch = 6, + .fdd_iso_freq = 1000, .wl_rssi_thres = rtw89_btc_8852b_wl_rssi_thres, .bt_rssi_thres = rtw89_btc_8852b_bt_rssi_thres, .rssi_tol = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index e68f73827fa5..9e0c48fde2fa 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -898,6 +898,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .mailbox = 0x1, .afh_guard_ch = 6, + .fdd_iso_freq = 1000, .wl_rssi_thres = rtw89_btc_8852bt_wl_rssi_thres, .bt_rssi_thres = rtw89_btc_8852bt_bt_rssi_thres, .rssi_tol = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 968666280c89..f21dd87e3846 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3266,6 +3266,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .mailbox = 0x1, .afh_guard_ch = 6, + .fdd_iso_freq = 1000, .wl_rssi_thres = rtw89_btc_8852c_wl_rssi_thres, .bt_rssi_thres = rtw89_btc_8852c_bt_rssi_thres, .rssi_tol = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index fe87b4929ddc..cdcce091b922 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -3249,6 +3249,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .mailbox = 0x1, .afh_guard_ch = 6, + .fdd_iso_freq = 1000, .wl_rssi_thres = rtw89_btc_8922a_wl_rssi_thres, .bt_rssi_thres = rtw89_btc_8922a_bt_rssi_thres, .rssi_tol = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 768434db14c6..aeade858ce95 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -3535,6 +3535,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .mailbox = 0x1, .afh_guard_ch = 6, + .fdd_iso_freq = 1000, .wl_rssi_thres = rtw89_btc_8922d_wl_rssi_thres, .bt_rssi_thres = rtw89_btc_8922d_bt_rssi_thres, .rssi_tol = 2, From 3b6dd05aee282bcbba5410babccebb1272cbf6b2 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Sun, 12 Jul 2026 11:04:57 +0800 Subject: [PATCH 102/403] wifi: rtw89: coex: Rearrange coexistence control structure The control structure will record some Wi-Fi/Bluetooth status, and packed send to firmware. The new generation chip had offloaded many mechanism control to firmware, firmware may need update these very often to make sure run in correct mechanism. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712030506.43438-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 154 ++++++++++----------- drivers/net/wireless/realtek/rtw89/core.h | 76 ++++++++-- drivers/net/wireless/realtek/rtw89/debug.c | 6 +- drivers/net/wireless/realtek/rtw89/fw.c | 63 ++++++++- drivers/net/wireless/realtek/rtw89/fw.h | 6 + 5 files changed, 207 insertions(+), 98 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index c02de0b4f7bf..72586179ec2c 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -295,10 +295,11 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { static const union rtw89_btc_wl_state_map btc_scanning_map = { .map = { .scan = 1, - .connecting = 1, + .dhcp = 1, .roaming = 1, - .dbccing = 1, + .transacting = 1, ._4way = 1, + .handshake = 1, }, }; @@ -998,8 +999,7 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type) if (type & BTC_RESET_CTRL) { memset(&btc->ctrl, 0, sizeof(btc->ctrl)); btc->manual_ctrl = false; - if (ver->fcxctrl != 7) - btc->ctrl.ctrl.trace_step = FCXDEF_STEP; + btc->ctrl.trace_step = FCXDEF_STEP; } /* Init Coex variables that are not zero */ @@ -1601,7 +1601,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, case BTC_RPT_TYPE_STEP: pcinfo = &pfwinfo->rpt_fbtc_step.cinfo; if (ver->fcxctrl != 7) - trace_step = btc->ctrl.ctrl.trace_step; + trace_step = btc->ctrl.trace_step; if (ver->fcxstep == 2) { pfinfo = &pfwinfo->rpt_fbtc_step.finfo.v2; @@ -3542,8 +3542,9 @@ static void _update_btc_state_map(struct rtw89_dev *rtwdev) struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; - if (wl->status.map.connecting || wl->status.map._4way || - wl->status.map.roaming || wl->status.map.dbccing) { + if (wl->status.map.dhcp || wl->status.map._4way || + wl->status.map.roaming || wl->status.map.transacting || + wl->status.map.handshake) { cx->state_map = BTC_WLINKING; } else if (wl->status.map.scan) { /* wl scan */ if (bt_linfo->status.map.inq_pag) @@ -5883,7 +5884,6 @@ static void rtw89_tx_time_iter(void *data, struct ieee80211_sta *sta) static void _set_wl_tx_limit(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &cx->wl; @@ -5894,19 +5894,14 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_txtime_data data = {.rtwdev = rtwdev}; bool reenable = false; - u8 igno_bt, tx_retry; + u8 tx_retry; u32 tx_time; u16 enable; if (btc->manual_ctrl) return; - if (ver->fcxctrl == 7) - igno_bt = btc->ctrl.ctrl_v7.igno_bt; - else - igno_bt = btc->ctrl.ctrl.igno_bt; - - if (btc->dm.freerun || igno_bt || b->link_cnt.now == 0 || + if (btc->dm.freerun || btc->ctrl.igno_bt || b->link_cnt.now == 0 || dm->tdd_bind.rf_band == BIT(RTW89_BAND_5G) || wl_rinfo->link_mode == BTC_WLINK_NOLINK) { enable = 0; @@ -6216,7 +6211,7 @@ static void _action_wl_scan(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; if (btc->cx.state_map != BTC_WLINKING && - RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) { + btc->ctrl.wl_ctrl_info.fw_scan) { _action_wl_25g_mcc(rtwdev); rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], Scan offload!\n"); } else if (rtwdev->dbcc_en) { @@ -7124,11 +7119,13 @@ void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__); dm->cnt_notify[BTC_NCNT_TIMER]++; - if (wl->status.map._4way) - wl->status.map._4way = false; - if (wl->status.map.connecting) - wl->status.map.connecting = false; - + if (wl->status.map.handshake || + wl->status.map._4way || + wl->status.map.dhcp) { + wl->status.map._4way = 0; + wl->status.map.dhcp = 0; + wl->status.map.handshake = 0; + } _run_coex(rtwdev, BTC_RSN_ACT1_WORK); } @@ -7616,32 +7613,53 @@ static void _set_coex_binding(struct rtw89_btc *btc) dm->ost_info.bt_enable_state = dm->bt_only ? _bind_is_btonly : val; } +static void _update_run_ctrl_info(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_ctrl ctrl = btc->ctrl; + struct rtw89_fbtc_wl_ctrl_info *wl_ctrl_info = &ctrl.wl_ctrl_info; + u8 i; + + ctrl.wl_only = dm->wl_only; + ctrl.bt_only = dm->bt_only; + ctrl.ntfy_type = dm->run_reason; + + wl_ctrl_info->smap_val = wl->status.val; + wl_ctrl_info->rfk_state = wl->rfk_info.state; + wl_ctrl_info->rfk_type = wl->rfk_info.type; + wl_ctrl_info->client_pstdma_on = dm->client_ps_tdma_on; + wl_ctrl_info->fw_scan = wl->scan_info.fw_scan; + + for (i = RTW89_MAC_0; i <= RTW89_MAC_1; i++) { + wl_ctrl_info->rf_band_map[i] = wl->rf_band_map[i]; + wl_ctrl_info->rf_ch[i] = wl->rf_ch_info[i].center_ch; + } + + if (!memcmp(&btc->ctrl, &ctrl, sizeof(struct rtw89_btc_ctrl))) { + memcpy(&btc->ctrl, &ctrl, sizeof(struct rtw89_btc_ctrl)); + if (btc->ver->fcxctrl >= 9) + _fw_set_drv_info(rtwdev, CXDRVINFO_CTRL); + } +} + static void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) { struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; u8 mode = btc->dm.tdd_bind.wl_link_mode; u8 mode_v0 = wl->role_info.link_mode_v0; - u8 igno_bt, always_freerun; lockdep_assert_wiphy(rtwdev->hw->wiphy); dm->run_reason = reason; _update_dm_step(rtwdev, reason); - if (ver->fcxctrl == 7) { - igno_bt = btc->ctrl.ctrl_v7.igno_bt; - always_freerun = btc->ctrl.ctrl_v7.always_freerun; - } else { - igno_bt = btc->ctrl.ctrl.igno_bt; - always_freerun = btc->ctrl.ctrl.always_freerun; - } - rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): reason=%d, mode=%d\n", __func__, reason, mode); rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): wl_only=%d, bt_only=%d\n", @@ -7655,15 +7673,6 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) return; } - if (igno_bt && - (reason == BTC_RSN_UPDATE_BT_INFO || - reason == BTC_RSN_UPDATE_BT_SCBD)) { - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): return for Stop Coex DM!!\n", - __func__); - return; - } - if (!wl->status.map.init_ok) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): return for WL init fail!!\n", @@ -7690,6 +7699,8 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) } } + _update_run_ctrl_info(rtwdev); + if (reason == BTC_RSN_NTFY_INIT || reason == BTC_RSN_NTFY_RADIO_STATE) _update_bt_scbd(rtwdev, false); @@ -7697,28 +7708,24 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) dm->cnt_dm[BTC_DCNT_RUN]++; dm->fddt_train = BTC_FDDT_DISABLE; bt->scan_rx_low_pri = false; - igno_bt = false; _set_coex_binding(btc); _update_btc_state_map(rtwdev); dm->freerun_chk = _check_freerun(rtwdev); /* check if meet freerun */ - if (always_freerun) { + if (btc->ctrl.always_freerun) { _action_freerun(rtwdev); - igno_bt = true; goto exit; } if (dm->wl_only) { _action_wl_only(rtwdev); - igno_bt = true; goto exit; } if (wl->status.map.rf_off || dm->bt_only || wl->status.map.lps) { _action_wl_off(rtwdev, mode); - igno_bt = true; goto exit; } @@ -7827,10 +7834,6 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) exit: rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): exit\n", __func__); - if (ver->fcxctrl == 7) - btc->ctrl.ctrl_v7.igno_bt = igno_bt; - else - btc->ctrl.ctrl.igno_bt = igno_bt; _action_common(rtwdev); } @@ -7839,7 +7842,6 @@ void rtw89_btc_init(struct rtw89_dev *rtwdev) const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - const struct rtw89_btc_ver *ver = btc->ver; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): Init %s !!\n", __func__, @@ -7849,10 +7851,6 @@ void rtw89_btc_init(struct rtw89_dev *rtwdev) btc->dm.run_reason = BTC_RSN_NONE; btc->dm.run_action = BTC_ACT_NONE; - if (ver->fcxctrl >= 7) - btc->ctrl.ctrl_v7.igno_bt = true; - else - btc->ctrl.ctrl.igno_bt = true; wl->status.map.init_ok = true; } @@ -7937,7 +7935,6 @@ void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode) struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_wl_info *wl = &btc->cx.wl; const struct rtw89_chip_info *chip = rtwdev->chip; - const struct rtw89_btc_ver *ver = btc->ver; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): mode=%d\n", __func__, mode); @@ -7948,10 +7945,7 @@ void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode) dm->bt_only = mode == BTC_MODE_BT ? 1 : 0; wl->status.map.rf_off = mode == BTC_MODE_WLOFF ? 1 : 0; dm->vid = rtwdev->custid; - if (ver->fcxctrl >= 7) - btc->ctrl.ctrl_v7.always_freerun = mode == BTC_MODE_COTX; - else - btc->ctrl.ctrl.always_freerun = mode == BTC_MODE_COTX; + btc->ctrl.always_freerun = mode == BTC_MODE_COTX; if (!wl->status.map.init_ok) { rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -8020,6 +8014,9 @@ void rtw89_btc_ntfy_scan_start(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band) _fw_set_drv_info(rtwdev, CXDRVINFO_DBCC); } + if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) + btc->ctrl.wl_ctrl_info.fw_scan = 1; + _run_coex(rtwdev, BTC_RSN_NTFY_SCAN_START); } @@ -8086,14 +8083,18 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev, cnt = ++wl->wcnt[BTC_WCNT_DHCP]; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): DHCP cnt=%d\n", __func__, cnt); - wl->status.map.connecting = true; + wl->status.map.handshake = 0; + wl->status.map._4way = 0; + wl->status.map.dhcp = 1; delay_work = true; break; case PACKET_EAPOL: cnt = ++wl->wcnt[BTC_WCNT_EAPOL]; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): EAPOL cnt=%d\n", __func__, cnt); - wl->status.map._4way = true; + wl->status.map.handshake = 0; + wl->status.map._4way = 1; + wl->status.map.dhcp = 0; delay_work = true; if (hfp->exist || hid->exist) delay /= 2; @@ -8103,7 +8104,7 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev, rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): EAPOL_End cnt=%d\n", __func__, cnt); - wl->status.map._4way = false; + wl->status.map._4way = 0; wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work); break; case PACKET_ARP: @@ -8435,10 +8436,16 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, wlinfo->connected == MLME_NO_LINK) btc->dm.leak_ap = 0; - if (state == BTC_ROLE_MSTS_STA_CONN_START) - wl->status.map.connecting = 1; - else - wl->status.map.connecting = 0; + if (state == BTC_ROLE_MSTS_STA_CONN_START) { + wl->status.map.transacting = 1; + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, + &rtwdev->coex_act1_work, + RTW89_COEX_ACT1_WORK_PERIOD); + } else { + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work); + wl->status.map.transacting = 0; + } if (state == BTC_ROLE_MSTS_STA_DIS_CONN || state == BTC_ROLE_MSTS_STA_CONN_END) @@ -9116,8 +9123,8 @@ static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) wl->scan_info.band[RTW89_PHY_0], wl->scan_info.phy_map); p += scnprintf(p, end - p, - "connecting:%s, roam:%s, 4way:%s, init_ok:%s\n", - wl->status.map.connecting ? "Y" : "N", + "handshake:%s, roam:%s, 4way:%s, init_ok:%s\n", + wl->status.map.handshake ? "Y" : "N", wl->status.map.roaming ? "Y" : "N", wl->status.map._4way ? "Y" : "N", wl->status.map.init_ok ? "Y" : "N"); @@ -9718,12 +9725,10 @@ static int _show_dm_step(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; char *p = buf, *end = buf + bufsz; - u8 igno_bt; if (!(dm->coex_info_map & BTC_COEX_INFO_DM)) return 0; @@ -9744,14 +9749,9 @@ static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += _show_dm_step(rtwdev, p, end - p); - if (ver->fcxctrl == 7) - igno_bt = btc->ctrl.ctrl_v7.igno_bt; - else - igno_bt = btc->ctrl.ctrl.igno_bt; - p += scnprintf(p, end - p, " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ", - "[dm_flag]", dm->wl_only, dm->bt_only, igno_bt, + "[dm_flag]", dm->wl_only, dm->bt_only, btc->ctrl.igno_bt, dm->freerun, btc->btc_ctrl_lps, dm->wl_mimo_ps); p += scnprintf(p, end - p, "leak_ap:%d, fw_offload:%s%s\n", @@ -10742,7 +10742,7 @@ static int _show_fbtc_step_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) if (ver->fcxctrl == 7 || ver->fcxctrl == 1) trace_step = 50; else - trace_step = btc->ctrl.ctrl.trace_step; + trace_step = btc->ctrl.trace_step; n_start = pos_old; if (pos_new >= pos_old) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ded788534651..0b20ee7d7a28 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1531,19 +1531,20 @@ enum rtw89_tfc_dir { struct rtw89_btc_wl_smap { u32 busy: 1; u32 scan: 1; - u32 connecting: 1; + u32 dhcp: 1; u32 roaming: 1; - u32 dbccing: 1; + u32 transacting: 1; u32 _4way: 1; + u32 handshake: 1; u32 rf_off: 1; - u32 lps: 2; - u32 ips: 1; - u32 init_ok: 1; - u32 traffic_dir : 2; u32 rf_off_pre: 1; + u32 ips: 1; + u32 lps: 2; u32 lps_pre: 2; u32 lps_exiting: 1; u32 emlsr: 1; + u32 init_ok: 1; + u32 traffic_dir : 2; }; enum rtw89_tfc_interval { @@ -1725,7 +1726,9 @@ struct rtw89_btc_u8_sta_chg { struct rtw89_btc_wl_scan_info { u8 band[RTW89_PHY_NUM]; u8 phy_map; - u8 rsvd; + u8 hw_band_map; + u8 type; + u8 fw_scan; }; struct rtw89_btc_wl_dbcc_info { @@ -3458,6 +3461,7 @@ struct rtw89_btc_dm { u8 run_action; u8 wl_tx_pwr_phy_map; u8 vid; + u8 client_ps_tdma_on; u8 wl_pre_agc: 2; u8 wl_lna2: 1; @@ -3477,13 +3481,38 @@ struct rtw89_btc_dm { bool pre_agc_chg; }; +struct rtw89_fbtc_wl_ctrl_info { + u8 rf_band_map[RTW89_MAC_NUM]; + u8 rf_ch[RTW89_MAC_NUM]; + + u8 client_pstdma_on; + u8 fw_scan; + u8 rfk_state; + u8 rfk_type; + + u32 smap_val; +}; + struct rtw89_btc_ctrl { + u32 manual; + u32 igno_bt: 1; + u32 always_freerun: 1; + u32 trace_step: 16; + + u8 wl_only; + u8 bt_only; + + u8 ntfy_type; + struct rtw89_fbtc_wl_ctrl_info wl_ctrl_info; +}; + +struct rtw89_btc_ctrl_v0 { u32 manual: 1; u32 igno_bt: 1; u32 always_freerun: 1; u32 trace_step: 16; u32 rsvd: 12; -}; +} __packed; struct rtw89_btc_ctrl_v7 { u8 manual; @@ -3492,10 +3521,31 @@ struct rtw89_btc_ctrl_v7 { u8 rsvd; } __packed; -union rtw89_btc_ctrl_list { - struct rtw89_btc_ctrl ctrl; - struct rtw89_btc_ctrl_v7 ctrl_v7; /* ver 8, 9 is the same */ -}; +struct rtw89_fbtc_wl_ctrl_info_v9 { + u8 rf_band_map[RTW89_MAC_NUM]; + u8 rf_ch[RTW89_MAC_NUM]; + + u8 client_pstdma_on; + u8 fw_scan; + u8 rfk_state; + u8 rfk_type; + + __le32 smap_val; +} __packed; + +struct rtw89_btc_ctrl_v9 { + u8 manual; + u8 always_freerun; + u8 wl_only; + u8 bt_only; + + u8 ntfy_type; + u8 rsvd0; + u8 rsvd1; + u8 rsvd2; + + struct rtw89_fbtc_wl_ctrl_info_v9 wl_ctrl_info; +} __packed; struct rtw89_btc_dbg { /* cmd "rb" */ @@ -3701,7 +3751,7 @@ struct rtw89_btc { struct rtw89_btc_cx cx; struct rtw89_btc_dm dm; - union rtw89_btc_ctrl_list ctrl; + struct rtw89_btc_ctrl ctrl; union rtw89_btc_module_info mdinfo; struct rtw89_btc_btf_fwinfo fwinfo; struct rtw89_btc_dbg dbg; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index e42dc5707576..8b41991963e8 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -3900,17 +3900,13 @@ static ssize_t rtw89_debug_priv_btc_manual_set(struct rtw89_dev *rtwdev, const char *buf, size_t count) { struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; int ret; ret = kstrtobool(buf, &btc->manual_ctrl); if (ret) return ret; - if (ver->fcxctrl == 7) - btc->ctrl.ctrl_v7.manual = btc->manual_ctrl; - else - btc->ctrl.ctrl.manual = btc->manual_ctrl; + btc->ctrl.manual = btc->manual_ctrl; return count; } diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 41c033d2ae7b..59e51e67d51b 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6441,7 +6441,7 @@ int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; - struct rtw89_btc_ctrl *ctrl = &btc->ctrl.ctrl; + struct rtw89_btc_ctrl *ctrl = &btc->ctrl; struct sk_buff *skb; u8 *cmd; int ret; @@ -6484,7 +6484,7 @@ int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type) int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_ctrl_v7 *ctrl = &btc->ctrl.ctrl_v7; + struct rtw89_btc_ctrl *ctrl = &btc->ctrl; struct rtw89_h2c_cxctrl_v7 *h2c; u32 len = sizeof(*h2c); struct sk_buff *skb; @@ -6501,7 +6501,64 @@ int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type) h2c->hdr.type = type; h2c->hdr.ver = btc->ver->fcxctrl; h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7; - h2c->ctrl = *ctrl; + + h2c->ctrl.manual = ctrl->manual; + h2c->ctrl.igno_bt = ctrl->igno_bt; + h2c->ctrl.always_freerun = ctrl->always_freerun; + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, BTFC_SET, + SET_DRV_INFO, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +int rtw89_fw_h2c_cxdrv_ctrl_v9(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_ctrl *ctrl = &btc->ctrl; + struct rtw89_fbtc_wl_ctrl_info_v9 *cinfo; + struct rtw89_h2c_cxctrl_v9 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_ctrl_v7\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxctrl_v9 *)skb->data; + cinfo = &h2c->ctrl.wl_ctrl_info; + + h2c->hdr.type = type; + h2c->hdr.ver = btc->ver->fcxctrl; + h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7; + + h2c->ctrl.manual = ctrl->manual; + h2c->ctrl.always_freerun = ctrl->always_freerun; + h2c->ctrl.wl_only = ctrl->wl_only; + h2c->ctrl.bt_only = ctrl->bt_only; + h2c->ctrl.ntfy_type = ctrl->ntfy_type; + memcpy(cinfo->rf_band_map, ctrl->wl_ctrl_info.rf_band_map, + sizeof(cinfo->rf_band_map)); + memcpy(cinfo->rf_ch, ctrl->wl_ctrl_info.rf_ch, sizeof(cinfo->rf_ch)); + cinfo->client_pstdma_on = ctrl->wl_ctrl_info.client_pstdma_on; + cinfo->fw_scan = ctrl->wl_ctrl_info.fw_scan; + cinfo->rfk_state = ctrl->wl_ctrl_info.rfk_state; + cinfo->rfk_type = ctrl->wl_ctrl_info.rfk_type; + cinfo->smap_val = cpu_to_le32(ctrl->wl_ctrl_info.smap_val); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, BTFC_SET, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 1e4267b22b36..72f56d32e3b0 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2430,6 +2430,11 @@ struct rtw89_h2c_cxctrl_v7 { struct rtw89_btc_ctrl_v7 ctrl; } __packed; +struct rtw89_h2c_cxctrl_v9 { + struct rtw89_h2c_cxhdr_v7 hdr; + struct rtw89_btc_ctrl_v9 ctrl; +} __packed; + #define H2C_LEN_CXDRVHDR sizeof(struct rtw89_h2c_cxhdr) #define H2C_LEN_CXDRVHDR_V7 sizeof(struct rtw89_h2c_cxhdr_v7) @@ -5382,6 +5387,7 @@ int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type); +int rtw89_fw_h2c_cxdrv_ctrl_v9(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_trx_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_trx_v9(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev, u8 type); From 404edeea3b182d08ccdcdba1a1c43ae4ed7f4f0f Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Sun, 12 Jul 2026 11:04:58 +0800 Subject: [PATCH 103/403] wifi: rtw89: coex: Update driver outsource info to firmware version 6 In order to make dual MAC Wi-Fi performance more stable, and take effect in time, offload more register/ hardware control to firmware. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712030506.43438-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 13 ++- drivers/net/wireless/realtek/rtw89/core.h | 89 +++++++++++++++++-- drivers/net/wireless/realtek/rtw89/fw.c | 101 +++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/fw.h | 8 +- 4 files changed, 199 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 72586179ec2c..78360e73d959 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -2923,7 +2923,12 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) else return; - rtw89_fw_h2c_cxdrv_osi_info(rtwdev, index); + if (ver->fcxosi == 1) + rtw89_fw_h2c_cxdrv_osi_info(rtwdev, index); + else if (ver->fcxosi == 6) + rtw89_fw_h2c_cxdrv_osi_info_v6(rtwdev, index); + else + return; break; default: break; @@ -3090,7 +3095,11 @@ static void _set_gnt_v1(struct rtw89_dev *rtwdev, u8 phy_map, } memcpy(osi->gnt_set, dm->gnt.band, sizeof(osi->gnt_set)); - memcpy(osi->wlact_set, dm->gnt.bt, sizeof(osi->wlact_set)); + + for (i = 0; i < BTC_ALL_BT; i++) { + osi->wlact_set[i].wlan_act_en = dm->gnt.bt[i].wlan_act_en; + osi->wlact_set[i].wlan_act = dm->gnt.bt[i].wlan_act; + } /* GBT source should be GBT_S1 in 1+1 (HWB0:5G + HWB1:2G) case */ if (osi->rf_band[BTC_RF_S0] == 1 && diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0b20ee7d7a28..676d3ba855d7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1322,6 +1322,17 @@ struct rtw89_mac_ax_gnt { u8 gnt_wl; } __packed; +struct rtw89_btc_gnt_ctrl { + u8 gnt_zb_sw_en; + u8 gnt_zb; + u8 gnt_bt1_sw_en; + u8 gnt_bt1; + u8 gnt_bt0_sw_en; + u8 gnt_bt0; + u8 gnt_wl_sw_en; + u8 gnt_wl; +} __packed; + struct rtw89_mac_ax_wl_act { u8 wlan_act_en; u8 wlan_act; @@ -3374,27 +3385,89 @@ enum btc_rf_path { BTC_RF_NUM, }; -struct rtw89_btc_fbtc_outsrc_set_info { - u8 rf_band[BTC_RF_NUM]; /* 0:2G, 1:non-2G */ +struct rtw89_btc_fbtc_outsrc_set_info_v1 { + u8 rf_band[BTC_RF_NUM]; u8 btg_rx[BTC_RF_NUM]; u8 nbtg_tx[BTC_RF_NUM]; - struct rtw89_mac_ax_gnt gnt_set[BTC_RF_NUM]; /* refer to btc_gnt_ctrl */ - struct rtw89_mac_ax_wl_act wlact_set[BTC_RF_NUM]; /* BT0/BT1 */ + struct rtw89_mac_ax_gnt gnt_set[BTC_RF_NUM]; + struct rtw89_mac_ax_wl_act wlact_set[BTC_ALL_BT]; u8 pta_req_hw_band; u8 rf_gbt_source; u8 bt_enable_state; u8 wl_btg_standby_chg; - /* bit[15]-> 0:2G/1:5G,6G, bit[14:0]-> WL HWBx ch freq in MHz */ + + u8 fbd_group_en[RTW89_MAC_NUM][2]; + __le16 rf_center_freq[RTW89_MAC_NUM]; + __le16 fbd_group_bound[RTW89_MAC_NUM][2]; + __le16 freq_diff_thres[RTW89_MAC_NUM][BTC_ALL_BT]; +} __packed; + +struct rtw89_btc_fbtc_outsrc_set_info_v6 { + u8 rf_band[BTC_RF_NUM]; + u8 btg_rx[BTC_RF_NUM]; + u8 nbtg_tx[BTC_RF_NUM]; + + struct rtw89_btc_gnt_ctrl gnt_set[RTW89_MAC_AX_COEX_GNT_NR]; + struct rtw89_mac_ax_wl_act wlact_set[BTC_ALL_BT_EZL]; + + u8 pta_req_hw_band; + u8 rf_gbt_source; + u8 bt_enable_state; + u8 bt_plut_type; + u8 wl_tx_limit_en; + u8 fc_exec; + u8 wl_btg_standby_chg; + u8 rsvd; + u8 bb_path_sel_bt[BTC_RF_NUM]; + u8 bb_phy_sel_bt[RTW89_PHY_NUM]; + u8 fbd_group_en[RTW89_MAC_NUM][2]; + __le16 rf_center_freq[RTW89_MAC_NUM]; + __le16 freq_diff_thres[RTW89_MAC_NUM][BTC_ALL_BT_EZL]; + __le16 fbd_group_bound[RTW89_MAC_NUM][2]; + __le32 wl_tx_limit_time; +} __packed; + +struct rtw89_btc_fbtc_outsrc_set_info { + u8 rf_band[BTC_RF_NUM]; /* 0:2GHz/1:5GHz for MPI_bb_hwsi_ignore_gnt_wl() */ + u8 btg_rx[BTC_RF_NUM]; /* for MPI_bb_btg_bt_rx() */ + u8 nbtg_tx[BTC_RF_NUM]; /* for MPI_bb_nbtg_bt_tx pre=AGC control */ + + struct rtw89_mac_ax_gnt gnt_set[BTC_RF_NUM]; /* refer to btc_gnt_ctrl */ + struct rtw89_btc_gnt_ctrl gnt_set_be[RTW89_MAC_AX_COEX_GNT_NR]; + struct rtw89_mac_ax_wl_act wlact_set[BTC_ALL_BT_EZL]; + + u8 pta_req_hw_band; /* Bind PTA to HWB0 or HWB1, only for 8922a 1-PTA */ + u8 rf_gbt_source; /* gbt from S0 or S1 for RF 0x2[9], only for 8922a */ + + /* The followngs are for 8922c/d new Multi-PTA design */ + /* 0:BT0/1:BT1/2:ZB on/off for MAC(0xe580[0]/0xe680[0])/ RF 0x4[3:2] */ + u8 bt_enable_state; + u8 bt_plut_type; /* BT polluted type, refer to enum btc_plt_map */ + + u8 wl_tx_limit_en; + u8 fc_exec; + u8 wl_btg_standby_chg; /* keep RX-IQGen on in standby mode */ + u8 rsvd; + + u8 bb_path_sel_bt[BTC_RF_NUM]; /* bb s0(1) select GNT_BT0 or BT1 */ + u8 bb_phy_sel_bt[RTW89_PHY_NUM]; /* bb phy0(1) select GNT_BT0 or BT1 */ + /* forbidden group-> bit[1]:fbd rf-band, bit[0]: fbd enable */ u8 fbd_group_en[RTW89_MAC_NUM][2]; /* HWB0/1 +.Group0/1 */ + + /* bit[15]-> 0:2G/1:5G,6G, bit[14:0]-> WL HWBx ch freq in MHz */ u16 rf_center_freq[RTW89_MAC_NUM]; /* HWB0/1 */ - /* forbidden group boundary: [15:8]->UP, [7:0]->LO */ - u16 fbd_group_bound[RTW89_MAC_NUM][2]; /* HWB0/1 +.Group0/1 */ + /* 11-bit in MHz, freq diff threshold */ u16 freq_diff_thres[RTW89_MAC_NUM][BTC_ALL_BT_EZL]; /* HWB0/1 vs.BT0/1/2 */ -} __packed; + + /* forbidden group boundary: [15:8]->UP, [7:0]->LO */ + u16 fbd_group_bound[RTW89_MAC_NUM][2]; /* HWB0/1 +.Group0/1 */ + + u32 wl_tx_limit_time; +}; union rtw89_btc_fbtc_slot_u { struct rtw89_btc_fbtc_slot v1[CXST_MAX]; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 59e51e67d51b..027f8121efd1 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6404,6 +6404,7 @@ int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type) u32 len = sizeof(*h2c); struct sk_buff *skb; int ret; + u8 i, j; skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { @@ -6416,7 +6417,105 @@ int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type) h2c->hdr.type = type; h2c->hdr.ver = btc->ver->fcxosi; h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; - h2c->osi = *osi; + + memcpy(h2c->osi.rf_band, osi->rf_band, sizeof(osi->rf_band)); + memcpy(h2c->osi.btg_rx, osi->btg_rx, sizeof(osi->btg_rx)); + memcpy(h2c->osi.nbtg_tx, osi->nbtg_tx, sizeof(osi->nbtg_tx)); + + for (i = 0; i < BTC_RF_NUM; i++) { + h2c->osi.gnt_set[i] = osi->gnt_set[i]; + h2c->osi.wlact_set[i] = osi->wlact_set[i]; + } + + h2c->osi.pta_req_hw_band = osi->pta_req_hw_band; + h2c->osi.rf_gbt_source = osi->rf_gbt_source; + h2c->osi.bt_enable_state = osi->bt_enable_state; + h2c->osi.wl_btg_standby_chg = osi->wl_btg_standby_chg; + + memcpy(h2c->osi.fbd_group_en, osi->fbd_group_en, sizeof(osi->fbd_group_en)); + + for (i = 0; i < RTW89_MAC_NUM; i++) { + h2c->osi.rf_center_freq[i] = cpu_to_le16(osi->rf_center_freq[i]); + for (j = 0; j < BTC_RF_NUM; j++) + h2c->osi.fbd_group_bound[i][j] = cpu_to_le16(osi->fbd_group_bound[i][j]); + } + + for (i = 0; i < RTW89_MAC_NUM; i++) { + for (j = 0; j < BTC_ALL_BT; j++) + h2c->osi.freq_diff_thres[i][j] = cpu_to_le16(osi->freq_diff_thres[i][j]); + } + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, BTFC_SET, + SET_DRV_INFO, 0, 0, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +int rtw89_fw_h2c_cxdrv_osi_info_v6(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_fbtc_outsrc_set_info *osi = &btc->dm.ost_info; + struct rtw89_h2c_cxosi_v6 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + u8 i, j; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_osi_v6\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxosi_v6 *)skb->data; + + h2c->hdr.type = type; + h2c->hdr.ver = btc->ver->fcxosi; + h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; + + memcpy(h2c->osi.rf_band, osi->rf_band, sizeof(osi->rf_band)); + memcpy(h2c->osi.btg_rx, osi->btg_rx, sizeof(osi->btg_rx)); + memcpy(h2c->osi.nbtg_tx, osi->nbtg_tx, sizeof(osi->nbtg_tx)); + memcpy(h2c->osi.gnt_set, osi->gnt_set, sizeof(osi->gnt_set)); + memcpy(h2c->osi.wlact_set, osi->wlact_set, sizeof(osi->wlact_set)); + + h2c->osi.pta_req_hw_band = osi->pta_req_hw_band; + h2c->osi.rf_gbt_source = osi->rf_gbt_source; + h2c->osi.bt_enable_state = osi->bt_enable_state; + h2c->osi.bt_plut_type = osi->bt_plut_type; + h2c->osi.wl_tx_limit_en = osi->wl_tx_limit_en; + h2c->osi.fc_exec = osi->fc_exec; + h2c->osi.wl_btg_standby_chg = osi->wl_btg_standby_chg; + h2c->osi.rsvd = osi->rsvd; + + memcpy(h2c->osi.bb_path_sel_bt, osi->bb_path_sel_bt, sizeof(osi->bb_path_sel_bt)); + memcpy(h2c->osi.bb_phy_sel_bt, osi->bb_phy_sel_bt, sizeof(osi->bb_phy_sel_bt)); + memcpy(h2c->osi.fbd_group_en, osi->fbd_group_en, sizeof(osi->fbd_group_en)); + + for (i = 0; i < RTW89_MAC_NUM; i++) { + h2c->osi.rf_center_freq[i] = cpu_to_le16(osi->rf_center_freq[i]); + for (j = 0; j < BTC_RF_NUM; j++) + h2c->osi.fbd_group_bound[i][j] = cpu_to_le16(osi->fbd_group_bound[i][j]); + } + + for (i = 0; i < RTW89_MAC_NUM; i++) { + for (j = 0; j < BTC_ALL_BT_EZL; j++) + h2c->osi.freq_diff_thres[i][j] = cpu_to_le16(osi->freq_diff_thres[i][j]); + } + + h2c->osi.wl_tx_limit_time = cpu_to_le32(osi->wl_tx_limit_time); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, BTFC_SET, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 72f56d32e3b0..c6131778f6a2 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2455,7 +2455,12 @@ struct rtw89_h2c_cxrole_v10 { struct rtw89_h2c_cxosi { struct rtw89_h2c_cxhdr_v7 hdr; - struct rtw89_btc_fbtc_outsrc_set_info osi; + struct rtw89_btc_fbtc_outsrc_set_info_v1 osi; +} __packed; + +struct rtw89_h2c_cxosi_v6 { + struct rtw89_h2c_cxhdr_v7 hdr; + struct rtw89_btc_fbtc_outsrc_set_info_v6 osi; } __packed; struct rtw89_h2c_cxinit { @@ -5385,6 +5390,7 @@ int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type); +int rtw89_fw_h2c_cxdrv_osi_info_v6(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_ctrl_v9(struct rtw89_dev *rtwdev, u8 type); From a20edfbc15a50ad5a445db2dc13a6a5780308deb Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Sun, 12 Jul 2026 11:04:59 +0800 Subject: [PATCH 104/403] wifi: rtw89: ceox: Update antenna & grant signal setting Merge set antenna & grant signal logic. Combine all information to big structure for runtime logic using, only separate to version format while it is going to assign value to register or offload to firmware. Add new format for dual-BT & external BT for RTL8922D. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712030506.43438-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 505 ++++++++------------ drivers/net/wireless/realtek/rtw89/core.h | 4 +- drivers/net/wireless/realtek/rtw89/mac.c | 27 +- drivers/net/wireless/realtek/rtw89/mac_be.c | 21 +- 4 files changed, 218 insertions(+), 339 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 78360e73d959..2ca6090e9f62 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -492,7 +492,7 @@ enum btc_ant_phase { BTC_ANT_WONLY, BTC_ANT_WOFF, BTC_ANT_W2G, - BTC_ANT_W5G, + BTC_ANT_FDD, BTC_ANT_W25G, BTC_ANT_FREERUN, BTC_ANT_WRFK, @@ -841,7 +841,9 @@ enum btc_gnt_state { BTC_GNT_HW = 0, BTC_GNT_SW_LO, BTC_GNT_SW_HI, - BTC_GNT_MAX + BTC_GNT_MAX, + + BTC_GNT_SET_SKIP = 0xff, }; enum btc_ctr_path { @@ -1787,8 +1789,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, dm->wl_fw_cx_offload = !!le32_to_cpu(prpt->v4.wl_fw_info.cx_offload); for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) - memcpy(&dm->gnt.band[i], &prpt->v4.gnt_val[i], - sizeof(dm->gnt.band[i])); + memcpy(&dm->gnt_set[i], &prpt->v4.gnt_val[i], + sizeof(dm->gnt_set[i])); bt->bcnt[BTC_BCNT_HIPRI_TX] = le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_HI_TX]); @@ -1819,8 +1821,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, dm->wl_fw_cx_offload = 0; for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) - memcpy(&dm->gnt.band[i], &prpt->v5.gnt_val[i][0], - sizeof(dm->gnt.band[i])); + memcpy(&dm->gnt_set[i], &prpt->v5.gnt_val[i], + sizeof(dm->gnt_set[i])); bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_HI_TX]); @@ -1846,8 +1848,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, dm->wl_fw_cx_offload = 0; for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) - memcpy(&dm->gnt.band[i], &prpt->v105.gnt_val[i][0], - sizeof(dm->gnt.band[i])); + memcpy(&dm->gnt_set[i], &prpt->v105.gnt_val[i], + sizeof(dm->gnt_set[i])); bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v105.bt_cnt[BTC_BCNT_HI_TX_V105]); @@ -1872,8 +1874,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw = le32_to_cpu(prpt->v7.rpt_info.fw_ver); for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) - memcpy(&dm->gnt.band[i], &prpt->v7.gnt_val[i][0], - sizeof(dm->gnt.band[i])); + memcpy(&dm->gnt_set[i], &prpt->v7.gnt_val[i], + sizeof(dm->gnt_set[i])); bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v7.bt_cnt[BTC_BCNT_HI_TX_V105]); @@ -1904,8 +1906,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw = le32_to_cpu(prpt->v8.rpt_info.fw_ver); for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) - memcpy(&dm->gnt.band[i], &prpt->v8.gnt_val[i][0], - sizeof(dm->gnt.band[i])); + memcpy(&dm->gnt_set[i], &prpt->v8.gnt_val[i], + sizeof(dm->gnt_set[i])); bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v8.bt_cnt[BTC_BCNT_HI_TX_V105]); @@ -2971,11 +2973,14 @@ void btc_fw_event(struct rtw89_dev *rtwdev, u8 evt_id, void *data, u32 len) btc->dm.scbd_b2w_update = 0; } -static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map, u8 wl_state, u8 bt_state) +static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map, + u8 wl_state, u8 bt_state, u8 wlact_state) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_mac_ax_gnt *g = dm->gnt.band; + struct rtw89_btc_fbtc_outsrc_set_info *o = &dm->ost_info; + struct rtw89_mac_ax_wl_act *wlact_set; + struct rtw89_mac_ax_coex_gnt mg; u8 i; if (phy_map > BTC_PHY_ALL) @@ -2987,126 +2992,118 @@ static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map, u8 wl_state, u8 bt_st switch (wl_state) { case BTC_GNT_HW: - g[i].gnt_wl_sw_en = 0; - g[i].gnt_wl = 0; + dm->gnt_set[i].gnt_wl_sw_en = 0; + dm->gnt_set[i].gnt_wl = 0; break; case BTC_GNT_SW_LO: - g[i].gnt_wl_sw_en = 1; - g[i].gnt_wl = 0; + dm->gnt_set[i].gnt_wl_sw_en = 1; + dm->gnt_set[i].gnt_wl = 0; break; case BTC_GNT_SW_HI: - g[i].gnt_wl_sw_en = 1; - g[i].gnt_wl = 1; + dm->gnt_set[i].gnt_wl_sw_en = 1; + dm->gnt_set[i].gnt_wl = 1; + break; + case BTC_GNT_SET_SKIP: + default: break; } switch (bt_state) { case BTC_GNT_HW: - g[i].gnt_bt_sw_en = 0; - g[i].gnt_bt = 0; + dm->gnt_set[i].gnt_bt0_sw_en = 0; + dm->gnt_set[i].gnt_bt0 = 0; + dm->gnt_set[i].gnt_bt1_sw_en = 0; + dm->gnt_set[i].gnt_bt1 = 0; + dm->gnt_set[i].gnt_zb_sw_en = 0; + dm->gnt_set[i].gnt_zb = 0; break; case BTC_GNT_SW_LO: - g[i].gnt_bt_sw_en = 1; - g[i].gnt_bt = 0; + dm->gnt_set[i].gnt_bt0_sw_en = 1; + dm->gnt_set[i].gnt_bt0 = 0; + dm->gnt_set[i].gnt_bt1_sw_en = 1; + dm->gnt_set[i].gnt_bt1 = 0; + dm->gnt_set[i].gnt_zb_sw_en = 1; + dm->gnt_set[i].gnt_zb = 0; break; case BTC_GNT_SW_HI: - g[i].gnt_bt_sw_en = 1; - g[i].gnt_bt = 1; + dm->gnt_set[i].gnt_bt0_sw_en = 1; + dm->gnt_set[i].gnt_bt0 = 1; + dm->gnt_set[i].gnt_bt1_sw_en = 1; + dm->gnt_set[i].gnt_bt1 = 1; + dm->gnt_set[i].gnt_zb_sw_en = 1; + dm->gnt_set[i].gnt_zb = 1; + break; + case BTC_GNT_SET_SKIP: + default: break; } } - rtw89_chip_mac_cfg_gnt(rtwdev, &dm->gnt); -} - -static void _set_gnt_v1(struct rtw89_dev *rtwdev, u8 phy_map, - u8 wl_state, u8 bt_state, u8 wlact_state) -{ - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_btc_fbtc_outsrc_set_info *osi = &dm->ost_info; - struct rtw89_mac_ax_wl_act *b = dm->gnt.bt; - struct rtw89_mac_ax_gnt *g = dm->gnt.band; - u8 i, bt_idx = dm->bt_select + 1; - - if (phy_map > BTC_PHY_ALL) - return; - - for (i = 0; i < RTW89_PHY_NUM; i++) { - if (!(phy_map & BIT(i))) - continue; - - switch (wl_state) { - case BTC_GNT_HW: - g[i].gnt_wl_sw_en = 0; - g[i].gnt_wl = 0; + for (i = 0; i < BTC_ALL_BT_EZL; i++) { + if (!(rtwdev->chip->para_ver & BTC_FEAT_WLAN_ACT_MUX)) break; - case BTC_GNT_SW_LO: - g[i].gnt_wl_sw_en = 1; - g[i].gnt_wl = 0; + + switch (wlact_state) { + case BTC_WLACT_HW: + dm->wlact_set[i].wlan_act_en = 0; + dm->wlact_set[i].wlan_act = 0; break; - case BTC_GNT_SW_HI: - g[i].gnt_wl_sw_en = 1; - g[i].gnt_wl = 1; + case BTC_WLACT_SW_LO: + dm->wlact_set[i].wlan_act_en = 1; + dm->wlact_set[i].wlan_act = 0; + break; + case BTC_WLACT_SW_HI: + dm->wlact_set[i].wlan_act_en = 1; + dm->wlact_set[i].wlan_act = 1; + break; + default: break; } - switch (bt_state) { - case BTC_GNT_HW: - g[i].gnt_bt_sw_en = 0; - g[i].gnt_bt = 0; + if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT)) break; - case BTC_GNT_SW_LO: - g[i].gnt_bt_sw_en = 1; - g[i].gnt_bt = 0; - break; - case BTC_GNT_SW_HI: - g[i].gnt_bt_sw_en = 1; - g[i].gnt_bt = 1; - break; - } } - if (rtwdev->chip->para_ver & BTC_FEAT_WLAN_ACT_MUX) { - for (i = 0; i < 2; i++) { - if (!(bt_idx & BIT(i))) - continue; + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): phy_map=0x%x, gnt_wl:%d, gnt_bt:%d, wl_act:%d\n", + __func__, phy_map, wl_state, bt_state, wlact_state); - switch (wlact_state) { - case BTC_WLACT_HW: - b[i].wlan_act_en = 0; - b[i].wlan_act = 0; - break; - case BTC_WLACT_SW_LO: - b[i].wlan_act_en = 1; - b[i].wlan_act = 0; - break; - case BTC_WLACT_SW_HI: - b[i].wlan_act_en = 1; - b[i].wlan_act = 1; - break; + if (rtwdev->chip->para_ver & BTC_FEAT_H2C_MACRO) { + if (o->rf_band[BTC_RF_S0] == 1 && o->rf_band[BTC_RF_S1] == 0) + o->rf_gbt_source = BTC_RF_S1; + else + o->rf_gbt_source = BTC_RF_S0; + + memcpy(o->wlact_set, dm->wlact_set, sizeof(o->wlact_set)); + + if (btc->ver->fcxosi == 6) { + memcpy(o->gnt_set_be, dm->gnt_set, sizeof(o->gnt_set_be)); + return; + } else if (btc->ver->fcxosi == 1) { + for (i = 0; i < RTW89_PHY_NUM; i++) { /* gnt[MAC0/MAC1] */ + mg.band[i].gnt_bt_sw_en = dm->gnt_set[i].gnt_bt0_sw_en; + mg.band[i].gnt_bt = dm->gnt_set[i].gnt_bt0; + mg.band[i].gnt_wl_sw_en = dm->gnt_set[i].gnt_wl_sw_en; + mg.band[i].gnt_wl = dm->gnt_set[i].gnt_wl; + memcpy(o->gnt_set, mg.band, sizeof(o->gnt_set)); } + return; } } - if (!btc->ver->fcxosi) { - rtw89_mac_cfg_gnt_v2(rtwdev, &dm->gnt); - return; + for (i = 0; i < RTW89_PHY_NUM; i++) { /* gnt[MAC0/MAC1] */ + mg.band[i].gnt_bt_sw_en = dm->gnt_set[i].gnt_bt0_sw_en; + mg.band[i].gnt_bt = dm->gnt_set[i].gnt_bt0; + mg.band[i].gnt_wl_sw_en = dm->gnt_set[i].gnt_wl_sw_en; + mg.band[i].gnt_wl = dm->gnt_set[i].gnt_wl; + } + for (i = 0; i < BTC_ALL_BT; i++) { /* wlact[BT0/BT1] */ + wlact_set = &dm->wlact_set[i]; + mg.bt[i].wlan_act_en = wlact_set->wlan_act_en; + mg.bt[i].wlan_act = wlact_set->wlan_act; } - memcpy(osi->gnt_set, dm->gnt.band, sizeof(osi->gnt_set)); - - for (i = 0; i < BTC_ALL_BT; i++) { - osi->wlact_set[i].wlan_act_en = dm->gnt.bt[i].wlan_act_en; - osi->wlact_set[i].wlan_act = dm->gnt.bt[i].wlan_act; - } - - /* GBT source should be GBT_S1 in 1+1 (HWB0:5G + HWB1:2G) case */ - if (osi->rf_band[BTC_RF_S0] == 1 && - osi->rf_band[BTC_RF_S1] == 0) - osi->rf_gbt_source = BTC_RF_S1; - else - osi->rf_gbt_source = BTC_RF_S0; + rtw89_chip_mac_cfg_gnt(rtwdev, &mg); } #define BTC_TDMA_WLROLE_MAX 3 @@ -4961,157 +4958,22 @@ static void _set_bt_plut(struct rtw89_dev *rtwdev, u8 phy_map, } } -static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec, - u8 phy_map, u8 type) +static void _set_ant(struct rtw89_dev *rtwdev, bool force_exec, + u8 phy_map, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_wl_role_info *r = &btc->cx.wl.role_info; - struct rtw89_btc_bt_info *bt = &cx->bt0; - struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; - u8 gwl, gwl0, gwl1, gbt, plt_ctrl, i, b2g = 0; - u32 ant_path_type; - - ant_path_type = ((phy_map << 8) + type); - - if (btc->dm.run_reason == BTC_RSN_NTFY_POWEROFF || - btc->dm.run_reason == BTC_RSN_NTFY_RADIO_STATE || - btc->dm.run_reason == BTC_RSN_CMD_SET_COEX || r->dbcc_chg) - force_exec = FC_EXEC; - - if (!force_exec && ant_path_type == dm->set_ant_path) { - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): return by no change!!\n", - __func__); - return; - } else if (bt->rfk_info.map.run) { - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): return by bt rfk!!\n", __func__); - return; - } else if (btc->dm.run_reason != BTC_RSN_NTFY_WL_RFK && - wl->rfk_info.state != BTC_WRFK_STOP) { - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): return by wl rfk!!\n", __func__); - return; - } - - dm->set_ant_path = ant_path_type; - - rtw89_debug(rtwdev, - RTW89_DBG_BTC, - "[BTC], %s(): path=0x%x, set_type=0x%x\n", - __func__, phy_map, dm->set_ant_path & 0xff); - - switch (type) { - case BTC_ANT_WPOWERON: - rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_BT); - break; - case BTC_ANT_WINIT: - if (bt->enable.now) - _set_gnt(rtwdev, phy_map, BTC_GNT_SW_LO, BTC_GNT_SW_HI); - else - _set_gnt(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO); - - rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL); - _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_BT, BTC_PLT_BT); - break; - case BTC_ANT_WONLY: - _set_gnt(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO); - rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL); - _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE); - break; - case BTC_ANT_WOFF: - rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_BT); - _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE); - break; - case BTC_ANT_W2G: - rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL); - if (rtwdev->dbcc_en) { - for (i = 0; i < RTW89_PHY_NUM; i++) { - b2g = (wl_dinfo->real_band[i] == RTW89_BAND_2G); - - gwl = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI; - gbt = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI; - /* BT should control by GNT_BT if WL_2G at S0 */ - if (i == 1 && - wl_dinfo->real_band[0] == RTW89_BAND_2G && - wl_dinfo->real_band[1] == RTW89_BAND_5G) - gbt = BTC_GNT_HW; - _set_gnt(rtwdev, BIT(i), gwl, gbt); - plt_ctrl = b2g ? BTC_PLT_BT : BTC_PLT_NONE; - _set_bt_plut(rtwdev, BIT(i), - plt_ctrl, plt_ctrl); - } - } else { - _set_gnt(rtwdev, phy_map, BTC_GNT_HW, BTC_GNT_HW); - _set_bt_plut(rtwdev, BTC_PHY_ALL, - BTC_PLT_BT, BTC_PLT_BT); - } - break; - case BTC_ANT_W5G: - rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL); - _set_gnt(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_HW); - _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE); - break; - case BTC_ANT_W25G: - rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL); - _set_gnt(rtwdev, phy_map, BTC_GNT_HW, BTC_GNT_HW); - _set_bt_plut(rtwdev, BTC_PHY_ALL, - BTC_PLT_GNT_WL, BTC_PLT_GNT_WL); - break; - case BTC_ANT_FREERUN: - rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL); - _set_gnt(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_HI); - _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE); - break; - case BTC_ANT_WRFK: - case BTC_ANT_WRFK2: - rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL); - _set_gnt(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO); - _set_bt_plut(rtwdev, phy_map, BTC_PLT_NONE, BTC_PLT_NONE); - break; - case BTC_ANT_PTA: - default: - gbt = BTC_GNT_HW; - if ((rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA) || - !r->dbcc_en) { - gwl = BTC_GNT_HW; - _set_gnt(rtwdev, BTC_PHY_ALL, gwl, gbt); - } else { - /* for DBCC Only-1-PTA */ - if (r->dbcc_2g_phy == RTW89_PHY_0) { - gwl0 = BTC_GNT_HW; - gwl1 = BTC_GNT_SW_HI; - } else { - gwl0 = BTC_GNT_SW_HI; - gwl1 = BTC_GNT_HW; - } - _set_gnt(rtwdev, BTC_PHY_0, gwl0, gbt); - _set_gnt(rtwdev, BTC_PHY_1, gwl1, gbt); - } - rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL); - _set_bt_plut(rtwdev, phy_map, BTC_PLT_NONE, BTC_PLT_NONE); - break; - } -} - -static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec, - u8 phy_map, u8 type) -{ - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; u32 ant_path_type = rtw89_get_antpath_type(phy_map, type); - struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; struct rtw89_btc_dm *dm = &btc->dm; u8 gwl = BTC_GNT_HW, gwl0, gwl1, gbt; + bool cx_ctrl; - if (btc->dm.run_reason == BTC_RSN_NTFY_POWEROFF || - btc->dm.run_reason == BTC_RSN_NTFY_RADIO_STATE || - btc->dm.run_reason == BTC_RSN_CMD_SET_COEX || wl_rinfo->dbcc_chg) + if (btc->cli_h2c_cmd || wl_rinfo->dbcc_chg || + dm->run_reason == BTC_RSN_NTFY_POWEROFF || + dm->run_reason == BTC_RSN_NTFY_RADIO_STATE || + dm->run_reason == BTC_RSN_CMD_SET_COEX) force_exec = FC_EXEC; if (wl_rinfo->link_mode != BTC_WLINK_DB_MCC && @@ -5123,10 +4985,6 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec, "[BTC], %s(): return by no change!!\n", __func__); return; - } else if (bt->rfk_info.map.run) { - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): return by bt rfk!!\n", __func__); - return; } else if (btc->dm.run_reason != BTC_RSN_NTFY_WL_RFK && wl->rfk_info.state != BTC_WRFK_STOP) { rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -5141,64 +4999,38 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec, __func__, phy_map, dm->set_ant_path & 0xff); switch (type) { + case BTC_ANT_WPOWERON: + cx_ctrl = BTC_CTRL_BY_BT; + break; case BTC_ANT_WINIT: + cx_ctrl = BTC_CTRL_BY_WL; /* To avoid BT MP driver case (bt_enable but no mailbox) */ - if (bt->enable.now && bt->run_patch_code) - _set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_LO, BTC_GNT_SW_HI, - BTC_WLACT_SW_LO); + if ((cx->bt0.enable.now && cx->bt0.run_patch_code) || + (cx->bt1.enable.now && cx->bt1.run_patch_code)) + _set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_LO, BTC_GNT_SW_HI, + BTC_WLACT_SW_LO); else - _set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO, - BTC_WLACT_SW_HI); + _set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_SW_LO, + BTC_WLACT_SW_HI); break; case BTC_ANT_WONLY: - _set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO, - BTC_WLACT_SW_HI); + cx_ctrl = BTC_CTRL_BY_WL; + _set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_SW_LO, + BTC_WLACT_SW_HI); break; case BTC_ANT_WOFF: - _set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_LO, BTC_GNT_SW_HI, - BTC_WLACT_SW_LO); - break; - case BTC_ANT_W2G: - case BTC_ANT_W25G: - if (wl_rinfo->dbcc_en) { - if (wl_dinfo->real_band[RTW89_PHY_0] == RTW89_BAND_2G) - gwl = BTC_GNT_HW; - else - gwl = BTC_GNT_SW_HI; - _set_gnt_v1(rtwdev, BTC_PHY_0, gwl, BTC_GNT_HW, BTC_WLACT_HW); - - if (wl_dinfo->real_band[RTW89_PHY_1] == RTW89_BAND_2G) - gwl = BTC_GNT_HW; - else - gwl = BTC_GNT_SW_HI; - _set_gnt_v1(rtwdev, BTC_PHY_1, gwl, BTC_GNT_HW, BTC_WLACT_HW); - } else { - gwl = BTC_GNT_HW; - _set_gnt_v1(rtwdev, phy_map, gwl, BTC_GNT_HW, BTC_WLACT_HW); - } - break; - case BTC_ANT_W5G: - _set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_HW, BTC_WLACT_HW); - break; - case BTC_ANT_FREERUN: - _set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_HI, - BTC_WLACT_SW_LO); - break; - case BTC_ANT_WRFK: - _set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO, - BTC_WLACT_HW); - break; - case BTC_ANT_WRFK2: - _set_gnt_v1(rtwdev, phy_map, BTC_GNT_SW_HI, BTC_GNT_SW_LO, - BTC_WLACT_SW_HI); /* no BT-Tx */ + cx_ctrl = BTC_CTRL_BY_BT; + _set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_LO, BTC_GNT_SW_HI, + BTC_WLACT_SW_LO); break; case BTC_ANT_PTA: default: + cx_ctrl = BTC_CTRL_BY_WL; gbt = BTC_GNT_HW; if ((rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA) || !wl_rinfo->dbcc_en) { gwl = BTC_GNT_HW; - _set_gnt_v1(rtwdev, BTC_PHY_ALL, gwl, gbt, BTC_WLACT_HW); + _set_gnt(rtwdev, BTC_PHY_ALL, gwl, gbt, BTC_WLACT_HW); } else { /* for DBCC Only-1-PTA */ if (wl_rinfo->dbcc_2g_phy == RTW89_PHY_0) { @@ -5208,24 +5040,36 @@ static void _set_ant_v1(struct rtw89_dev *rtwdev, bool force_exec, gwl0 = BTC_GNT_SW_HI; gwl1 = BTC_GNT_HW; } - _set_gnt_v1(rtwdev, BTC_PHY_0, gwl0, gbt, BTC_WLACT_HW); - _set_gnt_v1(rtwdev, BTC_PHY_1, gwl1, gbt, BTC_WLACT_HW); + _set_gnt(rtwdev, BTC_PHY_0, gwl0, gbt, BTC_WLACT_HW); + _set_gnt(rtwdev, BTC_PHY_1, gwl1, gbt, BTC_WLACT_HW); } break; + case BTC_ANT_FDD: + cx_ctrl = BTC_CTRL_BY_WL; + _set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_HW, + BTC_WLACT_HW); + break; + case BTC_ANT_FREERUN: + cx_ctrl = BTC_CTRL_BY_WL; + _set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_SW_HI, + BTC_WLACT_SW_LO); + break; + case BTC_ANT_WRFK: + cx_ctrl = BTC_CTRL_BY_WL; + _set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_SW_LO, + BTC_WLACT_HW); + break; + case BTC_ANT_WRFK2: + cx_ctrl = BTC_CTRL_BY_WL; + _set_gnt(rtwdev, BTC_PHY_ALL, BTC_GNT_SW_HI, BTC_GNT_SW_LO, + BTC_WLACT_SW_HI); /* no BT-Tx */ + break; } + rtw89_chip_cfg_ctrl_path(rtwdev, cx_ctrl); _set_bt_plut(rtwdev, phy_map, BTC_PLT_GNT_WL, BTC_PLT_GNT_WL); } -static void _set_ant(struct rtw89_dev *rtwdev, bool force_exec, - u8 phy_map, u8 type) -{ - if (rtwdev->chip->chip_id >= RTL8922A) - _set_ant_v1(rtwdev, force_exec, phy_map, type); - else - _set_ant_v0(rtwdev, force_exec, phy_map, type); -} - static void _action_wl_only(struct rtw89_dev *rtwdev) { _set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_WONLY); @@ -5619,7 +5463,7 @@ static void _action_bt_a2dp_pan_hid(struct rtw89_dev *rtwdev) static void _action_wl_5g(struct rtw89_dev *rtwdev) { - _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W5G); + _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_FDD); _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_5G); } @@ -9674,7 +9518,7 @@ static const char *id_to_ant(u32 id) CASE_BTC_ANTPATH_STR(WONLY); CASE_BTC_ANTPATH_STR(WOFF); CASE_BTC_ANTPATH_STR(W2G); - CASE_BTC_ANTPATH_STR(W5G); + CASE_BTC_ANTPATH_STR(FDD); CASE_BTC_ANTPATH_STR(W25G); CASE_BTC_ANTPATH_STR(FREERUN); CASE_BTC_ANTPATH_STR(WRFK); @@ -10898,6 +10742,7 @@ static int _show_fw_dm_msg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt_cfg) { const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_mac_ax_wl_act *bt; struct rtw89_mac_ax_gnt *gnt; u32 val, status; @@ -10932,7 +10777,39 @@ static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt gnt->gnt_bt = !!(status & B_AX_GNT_BT_RFC_S1); gnt->gnt_wl_sw_en = !!(val & B_AX_GNT_WL_RFC_S1_SWCTRL); gnt->gnt_wl = !!(status & B_AX_GNT_WL_RFC_S1); - } else { + + bt = &gnt_cfg->bt[0]; + bt->wlan_act_en = !!(val & B_BE_WL_ACT_SWCTRL); + bt->wlan_act = !!(status & B_BE_WL_ACT_VAL); + + bt = &gnt_cfg->bt[1]; + bt->wlan_act_en = !!(val & B_BE_WL_ACT2_SWCTRL); + bt->wlan_act = !!(status & B_BE_WL_ACT2_VAL); + } else if (chip->chip_id == RTL8922A) { + val = rtw89_read32(rtwdev, R_BE_GNT_SW_CTRL); + status = rtw89_read32(rtwdev, R_BE_PTA_GNT_VAL); + + gnt = &gnt_cfg->band[0]; + gnt->gnt_bt_sw_en = !!(val & B_BE_GNT_BT_BB0_SWCTRL); + gnt->gnt_bt = !!(status & B_BE_GNT_BT_BB0_VAL); + gnt->gnt_wl_sw_en = !!(val & B_BE_GNT_WL_BB0_SWCTRL); + gnt->gnt_wl = !!(status & B_BE_GNT_WL_BB0_VAL); + + gnt = &gnt_cfg->band[1]; + gnt->gnt_bt_sw_en = !!(val & B_BE_GNT_BT_BB1_SWCTRL); + gnt->gnt_bt = !!(status & B_BE_GNT_BT_BB1_VAL); + gnt->gnt_wl_sw_en = !!(val & B_BE_GNT_WL_BB1_SWCTRL); + gnt->gnt_wl = !!(status & B_BE_GNT_WL_BB1_VAL); + + bt = &gnt_cfg->bt[0]; + bt->wlan_act_en = !!(val & B_BE_WL_ACT_SWCTRL); + bt->wlan_act = !!(status & B_BE_WL_ACT_VAL); + + bt = &gnt_cfg->bt[1]; + bt->wlan_act_en = !!(val & B_BE_WL_ACT2_SWCTRL); + bt->wlan_act = !!(status & B_BE_WL_ACT2_VAL); + } else if (chip->chip_id == RTL8922D) { + /* Get from firmware */ return; } } @@ -11181,7 +11058,6 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_bt_info *bt0 = &cx->bt0; struct rtw89_btc_bt_info *bt1 = &cx->bt1; struct rtw89_btc_wl_info *wl = &cx->wl; - struct rtw89_mac_ax_gnt *gnt = NULL; struct rtw89_btc_dm *dm = &btc->dm; char *p = buf, *end = buf + bufsz; u8 i, type, cnt = 0; @@ -11219,18 +11095,19 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) wl->pta_req_mac, id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); - gnt = &dm->gnt.band[RTW89_PHY_0]; - p += scnprintf(p, end - p, ", phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]", - gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, - gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); + dm->gnt_set[RTW89_PHY_0].gnt_wl_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_0].gnt_wl, + dm->gnt_set[RTW89_PHY_0].gnt_bt0_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_0].gnt_bt0); if (rtwdev->dbcc_en) { - gnt = &dm->gnt.band[RTW89_PHY_1]; p += scnprintf(p, end - p, ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]", - gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, - gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); + dm->gnt_set[RTW89_PHY_1].gnt_wl_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_1].gnt_wl, + dm->gnt_set[RTW89_PHY_1].gnt_bt0_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_1].gnt_bt0); } pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 676d3ba855d7..a928140300b9 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3481,7 +3481,9 @@ struct rtw89_btc_dm { union rtw89_btc_fbtc_slot_u slot_now; struct rtw89_btc_fbtc_tdma tdma; struct rtw89_btc_fbtc_tdma tdma_now; - struct rtw89_mac_ax_coex_gnt gnt; + struct rtw89_btc_gnt_ctrl gnt_set[RTW89_MAC_AX_COEX_GNT_NR]; + struct rtw89_btc_gnt_ctrl gnt_val[RTW89_MAC_AX_COEX_GNT_NR]; + struct rtw89_mac_ax_wl_act wlact_set[BTC_ALL_BT_EZL]; union rtw89_btc_init_info_u init_info; /* pass to wl_fw if offload */ struct rtw89_btc_rf_trx_para_v9 rf_trx_para; struct rtw89_btc_wl_tx_limit_para wl_tx_limit; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 6e3da5e4a1b3..b9cd7fd8d76b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -6538,8 +6538,6 @@ int rtw89_mac_cfg_gnt_v1(struct rtw89_dev *rtwdev, if (gnt_cfg->band[0].gnt_bt) val |= B_AX_GNT_BT_RFC_S0_VAL | B_AX_GNT_BT_RX_VAL | B_AX_GNT_BT_TX_VAL; - else - val |= B_AX_WL_ACT_VAL; if (gnt_cfg->band[0].gnt_bt_sw_en) val |= B_AX_GNT_BT_RFC_S0_SWCTRL | B_AX_GNT_BT_RX_SWCTRL | @@ -6556,8 +6554,6 @@ int rtw89_mac_cfg_gnt_v1(struct rtw89_dev *rtwdev, if (gnt_cfg->band[1].gnt_bt) val |= B_AX_GNT_BT_RFC_S1_VAL | B_AX_GNT_BT_RX_VAL | B_AX_GNT_BT_TX_VAL; - else - val |= B_AX_WL_ACT_VAL; if (gnt_cfg->band[1].gnt_bt_sw_en) val |= B_AX_GNT_BT_RFC_S1_SWCTRL | B_AX_GNT_BT_RX_SWCTRL | @@ -6571,6 +6567,15 @@ int rtw89_mac_cfg_gnt_v1(struct rtw89_dev *rtwdev, val |= B_AX_GNT_WL_RFC_S1_SWCTRL | B_AX_GNT_WL_RX_SWCTRL | B_AX_GNT_WL_TX_SWCTRL | B_AX_GNT_WL_BB_SWCTRL; + if (gnt_cfg->bt[0].wlan_act_en) + val |= B_AX_WL_ACT_SWCTRL; + if (gnt_cfg->bt[0].wlan_act) + val |= B_AX_WL_ACT_VAL; + if (gnt_cfg->bt[1].wlan_act_en) + val |= B_AX_WL_ACT2_SWCTRL; + if (gnt_cfg->bt[1].wlan_act) + val |= B_AX_WL_ACT2_VAL; + rtw89_write32(rtwdev, R_AX_GNT_SW_CTRL, val); return 0; @@ -6645,22 +6650,20 @@ EXPORT_SYMBOL(rtw89_mac_cfg_ctrl_path); int rtw89_mac_cfg_ctrl_path_v1(struct rtw89_dev *rtwdev, bool wl) { - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_mac_ax_gnt *g = dm->gnt.band; + struct rtw89_mac_ax_coex_gnt gnt = {}; int i; if (wl) return 0; for (i = 0; i < RTW89_PHY_NUM; i++) { - g[i].gnt_bt_sw_en = 1; - g[i].gnt_bt = 1; - g[i].gnt_wl_sw_en = 1; - g[i].gnt_wl = 0; + gnt.band[i].gnt_bt_sw_en = 1; + gnt.band[i].gnt_bt = 1; + gnt.band[i].gnt_wl_sw_en = 1; + gnt.band[i].gnt_wl = 0; } - return rtw89_mac_cfg_gnt_v1(rtwdev, &dm->gnt); + return rtw89_mac_cfg_gnt_v1(rtwdev, &gnt); } EXPORT_SYMBOL(rtw89_mac_cfg_ctrl_path_v1); diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 4bdf20b7ba6d..33513b283d84 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -2466,29 +2466,26 @@ EXPORT_SYMBOL(rtw89_mac_cfg_gnt_v3); int rtw89_mac_cfg_ctrl_path_v2(struct rtw89_dev *rtwdev, bool wl) { - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_mac_ax_gnt *g = dm->gnt.band; - struct rtw89_mac_ax_wl_act *gbt = dm->gnt.bt; const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_mac_ax_coex_gnt gnt = {}; int i; if (wl) return 0; for (i = 0; i < RTW89_PHY_NUM; i++) { - g[i].gnt_bt_sw_en = 1; - g[i].gnt_bt = 1; - g[i].gnt_wl_sw_en = 1; - g[i].gnt_wl = 0; - gbt[i].wlan_act = 1; - gbt[i].wlan_act_en = 0; + gnt.band[i].gnt_bt_sw_en = 1; + gnt.band[i].gnt_bt = 1; + gnt.band[i].gnt_wl_sw_en = 1; + gnt.band[i].gnt_wl = 0; + gnt.bt[i].wlan_act = 1; + gnt.bt[i].wlan_act_en = 0; } if (chip->chip_id == RTL8922A) - return rtw89_mac_cfg_gnt_v2(rtwdev, &dm->gnt); + return rtw89_mac_cfg_gnt_v2(rtwdev, &gnt); else - return rtw89_mac_cfg_gnt_v3(rtwdev, &dm->gnt); + return rtw89_mac_cfg_gnt_v3(rtwdev, &gnt); } EXPORT_SYMBOL(rtw89_mac_cfg_ctrl_path_v2); From 5d5a5eb7ae2ed66ec77febd76807804dcdd0279f Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Sun, 12 Jul 2026 11:05:00 +0800 Subject: [PATCH 105/403] wifi: rtw89: coex: Rearrange Bluetooth firmware report entry To enable/disable firmware report once at the end of mechanism round. This can make the logic more clearly, and make sure every round the mechanism running can refresh the settings. It can avoid some report missing after driver status change. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712030506.43438-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 209 +++++++++++----------- 1 file changed, 104 insertions(+), 105 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 2ca6090e9f62..ff3345824a2a 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -2659,37 +2659,27 @@ static void rtw89_btc_fw_set_slots(struct rtw89_dev *rtwdev) } static void rtw89_btc_fw_en_rpt(struct rtw89_dev *rtwdev, - u32 rpt_map, bool rpt_state) + bool force_exec, u32 rpt_map) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_smap *wl_smap = &btc->cx.wl.status.map; struct rtw89_btc_btf_fwinfo *fwinfo = &btc->fwinfo; union rtw89_fbtc_rtp_ctrl r; - u32 val, bit_map; int ret; + u32 i; - if ((wl_smap->rf_off || wl_smap->lps != BTC_LPS_OFF) && rpt_state != 0) + if (!force_exec && rpt_map == fwinfo->rpt_en_map) return; - bit_map = rtw89_btc_fw_rpt_ver(rtwdev, rpt_map); + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): rpt_map=%x\n", + __func__, fwinfo->rpt_en_map); - rtw89_debug(rtwdev, RTW89_DBG_BTC, - "[BTC], %s(): rpt_map=%x, rpt_state=%x\n", - __func__, rpt_map, rpt_state); - - if (rpt_state) - val = fwinfo->rpt_en_map | bit_map; - else - val = fwinfo->rpt_en_map & ~bit_map; - - if (val == fwinfo->rpt_en_map) - return; - - if (btc->ver->fcxbtcrpt == 7 || btc->ver->fcxbtcrpt == 8) { + if (btc->ver->fcxbtcrpt == 7 || + btc->ver->fcxbtcrpt == 8 || + btc->ver->fcxbtcrpt == 11) { r.v8.type = SET_REPORT_EN; r.v8.fver = btc->ver->fcxbtcrpt; r.v8.len = sizeof(r.v8.map); - r.v8.map = cpu_to_le32(val); + r.v8.map = cpu_to_le32(rpt_map); ret = _send_fw_cmd(rtwdev, BTFC_SET, SET_REPORT_EN, &r.v8, sizeof(r.v8)); } else { @@ -2697,14 +2687,21 @@ static void rtw89_btc_fw_en_rpt(struct rtw89_dev *rtwdev, r.v1.fver = 5; else r.v1.fver = btc->ver->fcxbtcrpt; - r.v1.enable = cpu_to_le32(val); - r.v1.para = cpu_to_le32(rpt_state); - ret = _send_fw_cmd(rtwdev, BTFC_SET, SET_REPORT_EN, &r.v1, - sizeof(r.v1)); + + for (i = 0; i < RPT_EN_MONITER; i++) { + r.v1.enable = cpu_to_le32(i); + if (rpt_map & BIT(i)) + r.v1.para = cpu_to_le32(1); + else + r.v1.para = 0; + + ret = _send_fw_cmd(rtwdev, BTFC_SET, SET_REPORT_EN, + &r.v1, sizeof(r.v1)); + } } if (!ret) - fwinfo->rpt_en_map = val; + fwinfo->rpt_en_map = rpt_map; } static void btc_fw_set_monreg(struct rtw89_dev *rtwdev) @@ -2766,8 +2763,6 @@ static void btc_fw_set_monreg(struct rtw89_dev *rtwdev) rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): sz=%d ulen=%d n=%d\n", __func__, sz, ulen, n); - - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_MREG, 1); } static void _update_dm_step(struct rtw89_dev *rtwdev, @@ -5882,15 +5877,72 @@ static void _update_zb_coex_tbl(struct rtw89_dev *rtwdev) rtw89_write32(rtwdev, R_BTC_ZB_COEX_TBL_1, zb_tbl1); } +static void _set_fw_report_map(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_wl_smap *wl_smap = &btc->cx.wl.status.map; + struct rtw89_btc_bt_info *bt0 = &btc->cx.bt0; + struct rtw89_btc_bt_info *bt1 = &btc->cx.bt1; + u32 rpt_map = btc->fwinfo.rpt_en_map; + u32 bt_rom_code_id, bt_fw_ver; + bool bt_ver_unknown = false; + u32 bitmap; + + if (wl_smap->rf_off == 1 || wl_smap->lps != BTC_LPS_OFF) { + rtw89_btc_fw_en_rpt(rtwdev, false, 0); + return; + } + + rpt_map |= rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_MREG); + + bitmap = rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_SCAN_INFO); + if ((bt0->run_patch_code && bt0->enable.now) || + (bt1->run_patch_code && bt1->enable.now)) + rpt_map |= bitmap; + else + rpt_map &= ~bitmap; + + bt_rom_code_id = chip_id_to_bt_rom_code_id(rtwdev->btc.ver->chip_id); + bt_fw_ver = bt0->ver_info.fw & 0xffff; + if (bt_fw_ver == 0 || + (bt_fw_ver == bt_rom_code_id && bt0->run_patch_code)) + bt_ver_unknown = true; + + bitmap = rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_VER_INFO); + if (bt0->enable.now && bt_ver_unknown) + rpt_map |= bitmap; + else + rpt_map &= ~bitmap; + + bitmap = rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_AFH_MAP) | + rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_AFH_MAP_LE) | + rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_TX_PWR_LVL); + if (bt0->link_info.link_cnt.now || + bt0->link_info_56g.link_cnt.now || + bt1->link_info.link_cnt.now || + bt1->link_info_56g.link_cnt.now) + rpt_map |= bitmap; + else + rpt_map &= ~bitmap; + + bitmap = rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_BT_DEVICE_INFO); + if ((bt0->link_info.a2dp_desc.exist && + bt0->link_info.a2dp_desc.play_latency) || + (bt1->link_info.a2dp_desc.exist && + bt1->link_info.a2dp_desc.play_latency)) + rpt_map |= bitmap; + else + rpt_map &= ~bitmap; + + rtw89_btc_fw_en_rpt(rtwdev, false, rpt_map); +} + static void _action_common(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_role_info *rinfo = &wl->role_info; - struct rtw89_btc_wl_smap *wl_smap = &wl->status.map; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_dm *dm = &btc->dm; - u32 bt_rom_code_id, bt_fw_ver; u8 i; _wl_req_mac(rtwdev, rinfo->pta_req_band); @@ -5902,27 +5954,13 @@ static void _action_common(struct rtw89_dev *rtwdev) _set_bt_rx_agc(rtwdev); _set_rf_trx_para(rtwdev); _set_bt_rx_scan_pri(rtwdev); - - bt_rom_code_id = chip_id_to_bt_rom_code_id(rtwdev->btc.ver->chip_id); - bt_fw_ver = bt->ver_info.fw & 0xffff; - if (bt->enable.now && - (bt_fw_ver == 0 || - (bt_fw_ver == bt_rom_code_id && bt->run_patch_code && rtwdev->chip->scbd))) - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, 1); - else - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, 0); + _set_fw_report_map(rtwdev); if (dm->run_reason == BTC_RSN_NTFY_INIT || dm->run_reason == BTC_RSN_NTFY_RADIO_STATE || - dm->run_reason == BTC_RSN_NTFY_POWEROFF) { + dm->run_reason == BTC_RSN_NTFY_POWEROFF) _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); - if (wl_smap->rf_off == 1 || wl_smap->lps != BTC_LPS_OFF) - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_ALL, 0); - else - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_MREG, 1); - } - for (i = BTC_BT_1ST; i <= BTC_BT_2ND; i++) _sned_h2c_w2bscbd(rtwdev, false, i); @@ -7730,8 +7768,6 @@ void rtw89_btc_ntfy_poweroff(struct rtw89_dev *rtwdev) _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_ALL, false); _run_coex(rtwdev, BTC_RSN_NTFY_POWEROFF); - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_ALL, 0); - btc->cx.wl.status.map.rf_off_pre = btc->cx.wl.status.map.rf_off; } @@ -8344,12 +8380,10 @@ void rtw89_btc_ntfy_radio_state(struct rtw89_dev *rtwdev, enum btc_rfctrl rf_sta } if (rf_state == BTC_RFCTRL_WL_ON) { - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_MREG, true); val = BTC_WSCB_ACTIVE | BTC_WSCB_ON | BTC_WSCB_BTLOG; _write_scbd(rtwdev, BTC_ALL_BT, val, true); chip->ops->btc_init_cfg(rtwdev); } else { - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_ALL, false); if (rf_state == BTC_RFCTRL_FW_CTRL) _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_ACTIVE, false); else if (rf_state == BTC_RFCTRL_WL_OFF) @@ -8857,11 +8891,6 @@ static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) (bt->ver_info.fw_coex >= ver->bt_desired ? "Match" : "Mismatch"), ver->bt_desired); - if (bt->enable.now && bt->ver_info.fw == 0) - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, true); - else - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, false); - ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw); ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw); ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw); @@ -9056,7 +9085,6 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_btc_wl_info *wl = &cx->wl; - u32 ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw_coex); struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; union rtw89_btc_module_info *md = &btc->mdinfo; s8 br_dbm = bt->link_info.bt_txpwr_desc.br_dbm; @@ -9167,39 +9195,28 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) bt->bcnt[BTC_BCNT_LOPRI_TX], bt->bcnt[BTC_BCNT_POLLUTED]); - if (!bt->scan_info_update) { - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, true); - p += scnprintf(p, end - p, "\n"); - } else { - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, false); - if (ver->fcxbtscan == 1) { - p += scnprintf(p, end - p, - "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)", - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl)); - } else if (ver->fcxbtscan == 2) { - p += scnprintf(p, end - p, - "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)", - le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win), - le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl), - le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win), - le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl), - le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win), - le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl)); - } - p += scnprintf(p, end - p, "\n"); + if (ver->fcxbtscan == 1) { + p += scnprintf(p, end - p, + "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)", + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl)); + } else if (ver->fcxbtscan == 2) { + p += scnprintf(p, end - p, + "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)", + le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl), + le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl), + le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl)); } - - if (ver_main >= 9 && bt_linfo->link_cnt.now) - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_TX_PWR_LVL, true); - else - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_TX_PWR_LVL, false); + p += scnprintf(p, end - p, "\n"); if (bt->bcnt[BTC_BCNT_TXPWR_UPDATE]) { p += scnprintf(p, end - p, @@ -9218,24 +9235,6 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) } p += scnprintf(p, end - p, "\n"); - if (bt_linfo->link_cnt.now || bt_linfo->status.map.ble_connect) - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP, true); - else - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP, false); - - if (ver->fcxbtafh == 2 && bt_linfo->status.map.ble_connect) - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP_LE, true); - else - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP_LE, false); - - if (bt_linfo->a2dp_desc.exist && - (bt_linfo->a2dp_desc.flush_time == 0 || - bt_linfo->a2dp_desc.vendor_id == 0 || - bt_linfo->a2dp_desc.play_latency == 1)) - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, true); - else - rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, false); - return p - buf; } From a67bb858ba4dbd64e5be901cab414110c06e86d3 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Sun, 12 Jul 2026 11:05:01 +0800 Subject: [PATCH 106/403] wifi: rtw89: coex: Refine send firmware command function Because the coexistence offload more register/ hardware setting I/O to firmware by coexistence itself, and it goes with the same entry with other control action, so the firmware command entry need to add different condition to judge should it followed coexistence TLV format or not. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712030506.43438-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 107 +++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/core.h | 4 + 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index ff3345824a2a..07d054f59eb5 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -936,6 +936,14 @@ static void _run_coex(struct rtw89_dev *rtwdev, static void _write_scbd(struct rtw89_dev *rtwdev, u8 bid, u32 val, bool state); static u8 _sned_h2c_w2bscbd(struct rtw89_dev *rtwdev, bool force_exec, u8 bid); static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid); +static const char *id_to_h2c(u32 id); + +static void _reset_h2c_macro(struct rtw89_btc *btc) +{ + btc->hbuf_len = 0; + btc->hbuf_cnt = 0; + memset(btc->hbuf, 0, BTC_H2C_MAXLEN); +} static int _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, void *param, u16 len) @@ -945,6 +953,8 @@ static int _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_dm *dm = &btc->dm; + u8 h2c_func_mask, bid = BTC_BT_1ST; + u8 *buf = param; int ret; if (len > BTC_H2C_MAXLEN || len == 0) { @@ -966,7 +976,69 @@ static int _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, return -EINVAL; } - ret = rtw89_fw_h2c_raw_with_hdr(rtwdev, h2c_class, h2c_func, param, len, + h2c_func_mask = h2c_func & (~BT_H2C_FUNC_BT2ND); + if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) + bid = !!(h2c_func & BT_H2C_FUNC_BT2ND); + + if (btc->io_oflld_type != BTC_IO_OFLD_BTC_H2C || btc->cli_h2c_cmd || + h2c_func_mask == SET_H2C_MACRO || + (h2c_func_mask == SET_DRV_INFO && buf[0] == CXDRVINFO_TRX) || + (h2c_func_mask == SET_IOFLD_SCBD && dm->scbd_write_instant) || + h2c_func_mask == SET_BT_LNA_CONSTRAIN) { + h2c_class = BTFC_SET; + + ret = rtw89_fw_h2c_raw_with_hdr(rtwdev, h2c_class, h2c_func, + buf, len, false, true); + + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s():class=%d, bt%d-func=%s, len=%d\n", + __func__, h2c_class, bid, + id_to_h2c(h2c_func_mask), len); + + if (h2c_func_mask == SET_H2C_MACRO) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s():Send H2C-MACRO cnt=%d, len=%d\n", + __func__, btc->hbuf_cnt, btc->hbuf_len); + _reset_h2c_macro(btc); /* clear H2C MACRO buffer */ + } + + if (ret != 0) { /* Send H2C fail */ + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s():return by rtw_hal_mac_send_h2c\n", + __func__); + btc->fwinfo.cnt_h2c_fail++; + return 0; + } + + btc->fwinfo.cnt_h2c++; + } else { /* Fill H2C MACRO buffer(TLV format) temporarily */ + if (btc->hbuf_cnt == 0) + _reset_h2c_macro(btc); + + /* Type:1 byte, Length:2 Bytes, Data:len bytes */ + if (btc->hbuf_len + len + 3 >= BTC_H2C_MAXLEN) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s():return by MACRO buf full(%d)\n", + __func__, btc->hbuf_len + len + 3); + btc->fwinfo.cnt_h2c_fail++; + return 0; + } + + btc->hbuf[btc->hbuf_len] = h2c_func; + btc->hbuf[btc->hbuf_len + 1] = len & GENMASK(7, 0); + btc->hbuf[btc->hbuf_len + 2] = (len & GENMASK(15, 8)) >> 8; + + memcpy(&btc->hbuf[btc->hbuf_len + 3], buf, len); + btc->hbuf_len = btc->hbuf_len + len + 3; + btc->hbuf_cnt++; + + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s():Buffer H2C-MACRO cnt=%d/bt%d-func=%s/len=%d\n", + __func__, btc->hbuf_cnt, bid, + id_to_h2c(h2c_func_mask), len); + } + + ret = rtw89_fw_h2c_raw_with_hdr(rtwdev, h2c_class, h2c_func, buf, len, false, true); if (ret) pfwinfo->cnt_h2c_fail++; @@ -9249,6 +9321,7 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) #define CASE_BTC_POLUT_STR(e) case BTC_PLT_## e: return #e #define CASE_BTC_REGTYPE_STR(e) case REG_## e: return #e #define CASE_BTC_GDBG_STR(e) case BTC_DBG_## e: return #e +#define CASE_BTC_H2CCMD(e) case SET_##e: return #e static const char *id_to_polut(u32 id) { @@ -9529,6 +9602,38 @@ static const char *id_to_ant(u32 id) } } +static const char *id_to_h2c(u32 id) +{ + switch (id) { + CASE_BTC_H2CCMD(REPORT_EN); + CASE_BTC_H2CCMD(SLOT_TABLE); + CASE_BTC_H2CCMD(MREG_TABLE); + CASE_BTC_H2CCMD(CX_POLICY); + CASE_BTC_H2CCMD(GPIO_DBG); + CASE_BTC_H2CCMD(DRV_INFO); + CASE_BTC_H2CCMD(DRV_EVENT); + CASE_BTC_H2CCMD(BT_WREG_ADDR); + CASE_BTC_H2CCMD(BT_WREG_VAL); + CASE_BTC_H2CCMD(BT_RREG_ADDR); + CASE_BTC_H2CCMD(BT_WL_CH_INFO); + CASE_BTC_H2CCMD(BT_INFO_REPORT); + CASE_BTC_H2CCMD(BT_IGNORE_WLAN_ACT); + CASE_BTC_H2CCMD(BT_TX_PWR); + CASE_BTC_H2CCMD(BT_LNA_CONSTRAIN); + CASE_BTC_H2CCMD(BT_QUERY_DEV_LIST); + CASE_BTC_H2CCMD(BT_QUERY_DEV_INFO); + CASE_BTC_H2CCMD(BT_PSD_REPORT); + CASE_BTC_H2CCMD(H2C_TEST); + CASE_BTC_H2CCMD(IOFLD_RF); + CASE_BTC_H2CCMD(IOFLD_BB); + CASE_BTC_H2CCMD(IOFLD_MAC); + CASE_BTC_H2CCMD(IOFLD_SCBD); + CASE_BTC_H2CCMD(H2C_MACRO); + default: + return "unknown"; + } +} + static int scnprintf_segment(char *buf, size_t bufsz, const char *prefix, const u16 *data, u8 len, u8 seg_len, u8 start_idx, u8 ring_len) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index a928140300b9..cb3a740e4719 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3820,6 +3820,7 @@ struct rtw89_btc_btf_fwinfo { }; #define RTW89_BTC_POLICY_MAXLEN 512 +#define BTC_H2C_MAXLENC 2020 struct rtw89_btc { const struct rtw89_btc_ver *ver; @@ -3839,11 +3840,14 @@ struct rtw89_btc { u32 bt_req_len[RTW89_PHY_NUM]; u8 policy[RTW89_BTC_POLICY_MAXLEN]; + u8 hbuf[BTC_H2C_MAXLENC]; /* H2C Macro buffer */ + u8 hbuf_cnt; /* H2C cmd count in buffer */ u8 ant_type; u8 btg_pos; u8 io_oflld_type; u16 policy_len; u16 policy_type; + u16 hbuf_len; /* H2C used length, it sshould be <= BTC_H2C_MAXLEN */ u32 hubmsg_cnt; bool bt_req_en; bool update_policy_force; From aaba33e780f5be7d71d7119578012e964f1846c4 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Sun, 12 Jul 2026 11:05:02 +0800 Subject: [PATCH 107/403] wifi: rtw89: coex: Refine _reset_btc_var() To avoid the default value not match the real using scenario, it should after assign desired default value after variable reset. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712030506.43438-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 66 +++++++---- drivers/net/wireless/realtek/rtw89/coex.h | 8 ++ drivers/net/wireless/realtek/rtw89/core.h | 127 ++++++++++++++++++++++ 3 files changed, 179 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 07d054f59eb5..17290b2cb2c8 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1048,44 +1048,52 @@ static int _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, return ret; } -#define BTC_BT_DEF_BR_TX_PWR 4 -#define BTC_BT_DEF_LE_TX_PWR 4 +#define BTC_DEFAULT_SIR_THRES 51 /* dB, BT Pin - This = TDD/FDD swtch thres */ +#define BTC_FDDR_TP_SETUP_TIME 1 /* in second */ +#define BTC_FDDR_TP_HOLD_TIME 3 /* in second */ +#define BTC_FDDR_RX_LOW_RATE_THRES 4 /* low rate for TDD swotch */ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_cx *cx = &btc->cx; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_btc_bt_info *bt = &btc->cx.bt0; - struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; - struct rtw89_btc_wl_link_info *wl_linfo; - u8 i, j; + const struct rtw89_btc_ver *ver = btc->ver; + struct rtw89_btc_bt_info *bt0 = &cx->bt0; + struct rtw89_btc_bt_info *bt1 = &cx->bt1; + u8 i; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s\n", __func__); - if (type & BTC_RESET_CX) + if (type & BTC_RESET_CX) { memset(cx, 0, sizeof(*cx)); + cx->bt_ext.max_tx_pwr = RTW89_BTC_BT_DEF_LE_TX_PWR; + cx->bt_ext.ant_iso_to_wl = RTW89_BTC_DEFAULT_ANISO; + } - if (type & BTC_RESET_BTINFO) /* only for BT enable */ - memset(bt, 0, sizeof(*bt)); + if (type & BTC_RESET_BTINFO) {/* only for BT enable */ + memset(bt0, 0, sizeof(*bt0)); + bt0->link_info.bt_txpwr_desc.br_dbm = RTW89_BTC_BT_DEF_BR_TX_PWR; + bt0->link_info_56g.bt_txpwr_desc.br_dbm = RTW89_BTC_BT_DEF_BR_TX_PWR; + bt0->link_info.bt_txpwr_desc.le_dbm = RTW89_BTC_BT_DEF_LE_TX_PWR; + bt0->link_info_56g.bt_txpwr_desc.le_dbm = RTW89_BTC_BT_DEF_LE_TX_PWR; + bt0->ant_iso_to_wl = RTW89_BTC_DEFAULT_ANISO; + } else if (type & BTC_RESET_BTINFO2) { + memset(bt1, 0, sizeof(*bt1)); + bt1->link_info.bt_txpwr_desc.br_dbm = RTW89_BTC_BT_DEF_BR_TX_PWR; + bt1->link_info_56g.bt_txpwr_desc.br_dbm = RTW89_BTC_BT_DEF_BR_TX_PWR; + bt1->link_info.bt_txpwr_desc.le_dbm = RTW89_BTC_BT_DEF_LE_TX_PWR; + bt1->link_info_56g.bt_txpwr_desc.le_dbm = RTW89_BTC_BT_DEF_LE_TX_PWR; + bt1->ant_iso_to_wl = RTW89_BTC_DEFAULT_ANISO; + } if (type & BTC_RESET_CTRL) { memset(&btc->ctrl, 0, sizeof(btc->ctrl)); - btc->manual_ctrl = false; btc->ctrl.trace_step = FCXDEF_STEP; } /* Init Coex variables that are not zero */ if (type & BTC_RESET_DM) { memset(&btc->dm, 0, sizeof(btc->dm)); - memset(bt_linfo->rssi_state, 0, sizeof(bt_linfo->rssi_state)); - for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) { - for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { - wl_linfo = &wl->rlink_info[i][j]; - memset(wl_linfo->rssi_state, 0, sizeof(wl_linfo->rssi_state)); - } - } /* set the slot_now table to original */ btc->dm.tdma_now = t_def[CXTD_OFF]; @@ -1108,18 +1116,32 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type) btc->policy_len = 0; btc->bt_req_len[RTW89_PHY_0] = 0; btc->bt_req_len[RTW89_PHY_1] = 0; + btc->hubmsg_cnt = 0; + btc->dm.coex_info_map = BTC_COEX_INFO_ALL; btc->dm.wl_tx_limit.tx_time = BTC_MAX_TX_TIME_DEF; btc->dm.wl_tx_limit.tx_retry = BTC_MAX_TX_RETRY_DEF; + btc->dm.bt_slot_flood = BTC_B1_MAX; + btc->dm.sir_thres = BTC_DEFAULT_SIR_THRES; + btc->dm.fddr_info.tp_setup_time = BTC_FDDR_TP_SETUP_TIME; + btc->dm.fddr_info.tp_hold_time = BTC_FDDR_TP_HOLD_TIME; + btc->dm.fddr_info.wl_rx_rate_thres = BTC_FDDR_RX_LOW_RATE_THRES; + btc->dm.fddt_info.type = BTC_FDDT_TYPE_AUTO; + btc->dm.wl_pre_agc_rb = BTC_PREAGC_NOTFOUND; btc->dm.wl_btg_rx_rb = BTC_BTGCTRL_BB_GNT_NOTFOUND; } - if (type & BTC_RESET_MDINFO) + if (type & BTC_RESET_MDINFO) { memset(&btc->mdinfo, 0, sizeof(btc->mdinfo)); - bt->link_info.bt_txpwr_desc.br_dbm = BTC_BT_DEF_BR_TX_PWR; - bt->link_info.bt_txpwr_desc.le_dbm = BTC_BT_DEF_LE_TX_PWR; + if (ver->fcxinit == 10) + btc->mdinfo.md_v10.ant.isolation = RTW89_BTC_DEFAULT_ANISO; + else if (ver->fcxinit == 7) + btc->mdinfo.md_v7.ant.isolation = RTW89_BTC_DEFAULT_ANISO; + else + btc->mdinfo.md.ant.isolation = RTW89_BTC_DEFAULT_ANISO; + } } static u8 _search_reg_index(struct rtw89_dev *rtwdev, u8 mreg_num, u16 reg_type, u32 target) diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index c127bd80d31c..fdd63e2c0837 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -241,6 +241,14 @@ enum btc_3cx_type { BTC_3CX_MAX, }; +enum btc_fddt_type { + BTC_FDDT_TYPE_STOP, + BTC_FDDT_TYPE_AUTO, + BTC_FDDT_TYPE_FIX_TDD, + BTC_FDDT_TYPE_FIX_FULL_FDD, + BTC_FDDT_MAX, +}; + enum btc_chip_feature { BTC_FEAT_PTA_ONOFF_CTRL = BIT(0), /* on/off ctrl by HW (not 0x73[2]) */ BTC_FEAT_NONBTG_GWL_THRU = BIT(1), /* non-BTG GNT_WL!=0 if GNT_BT = 1 */ diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index cb3a740e4719..f784e8437292 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2462,6 +2462,9 @@ enum rtw89_btc_bt_func_type { #define RTW89_BTC_BTC_SCAN_V1_FLAG_ENABLE BIT(0) #define RTW89_BTC_BTC_SCAN_V1_FLAG_INTERLACE BIT(1) +#define RTW89_BTC_BT_DEF_BR_TX_PWR 4 +#define RTW89_BTC_BT_DEF_LE_TX_PWR 4 +#define RTW89_BTC_DEFAULT_ANISO 10 struct rtw89_btc_bt_scan_info_v1 { __le16 win; @@ -2521,6 +2524,7 @@ struct rtw89_btc_bt_info { u8 func_type; u8 tx_power_now; u8 tx_power_now_6g; + u8 ant_iso_to_wl; /* ant isolation between BTx and WL */ u8 fw_ver_mismatch: 1; u8 band_56G_support: 1; @@ -3474,6 +3478,123 @@ union rtw89_btc_fbtc_slot_u { struct rtw89_btc_fbtc_slot_v7 v7[CXST_MAX]; }; +struct rtw89_btc_fddr_cell { + u8 en; + u8 wl_rx_max; + u8 wl_rx_min; +}; + +struct rtw89_btc_fddr_result { + u8 wl_rx_limit; + u8 wl_rx_limit_step[6]; /* record search process */ + u8 search_cnt; /* the rx-limit serach count */ + u32 wl_tp; + u32 wl_tp_step[6]; /* record search process */ +}; + +struct rtw89_btc_fddr_train_info { + u8 rx_limit_pre; + u8 rx_limit_now; + + u32 tp_rec_cnt; + u32 tp_avg_cnt; + + u32 wl_tp_pre; + u32 wl_tp_now; +}; + +struct rtw89_btc_fddr_info { + u8 state; /* refer to enum btc_fddr_state */ + u8 cell_now; /* wl rssi_level after filter LNA != 6 */ + u8 cell_change; + u8 wl_low_rate; + + u8 tp_setup_time; /* calculate TP after this value (in second) */ + u8 tp_hold_time; /* TP calculation period (in second) */ + u8 wl_rssi_thres[BTC_WL_RSSI_THMAX]; /* index 0 -> Max RSSI */ + + u8 search_mode; /* 0: search, 1:look-up */ + u8 search_dir; /* 0: Max->Min, 1:Min->Max */ + + struct rtw89_btc_fddr_train_info tctrl; /* train flag */ + struct rtw89_btc_fddr_result cell_result[BTC_WL_RSSI_THMAX + 1]; + struct rtw89_btc_fddr_cell cell[BTC_WL_RSSI_THMAX + 1]; /* parameters */ + + u16 wl_rx_rate_thres; /* switch to TDD if rx_rate < this threshold */ + + u32 nrsn_map; /* the reason map for no-run fdd-traing */ + u32 wl_rx_rate_now; +}; + +struct rtw89_btc_rpt_ctrl_a2dp_empty { + u32 cnt_empty; /* a2dp empty count */ + u32 cnt_flowctrl; /* a2dp empty flow control counter */ + u32 cnt_tx; + u32 cnt_ack; + u32 cnt_nack; +}; + +struct rtw89_btc_fddt_bt_stat { + struct rtw89_btc_rpt_ctrl_a2dp_empty a2dp_last; + u32 retry_last; +}; + +struct rtw89_btc_fddt_cell { + s8 wl_pwr_min; + s8 wl_pwr_max; + s8 bt_pwr_dec_max; + s8 bt_rx_gain; +}; + +struct rtw89_btc_fddt_fail_check { /* for cell stay in training */ + u8 check_map; /* check pass condition if bit-map = 1 */ + u8 bt_no_empty_cnt; /* 0-fail if no bt-empty >= th in train_cycle */ + u8 wl_tp_ratio; /* 1-fail if wl tp rise ratio < th */ + u8 wl_kpibtr_ratio; /* 2-fail if phase_now_tp < phase_last_tp * kpibtr_ratio */ +}; + +struct rtw89_btc_fddt_break_check { /* for cell stay in training or train-ok */ + u8 check_map; /* check break condition if bit-map = 1 */ + u8 bt_no_empty_cnt; /* 0-break if no empty count >= th */ + u8 wl_tp_ratio; /* 1-break if wl tp ratio < th (%) */ + u8 wl_tp_low_bound; /* 2-break if wl tp (in Mbps) < th */ + + u8 cn; /* 3-break if (cn >= cn_limit) >= th cycle */ + u8 cell_chg; /* 4-break if non-matched-RSSI >= th cycle */ + s8 nhm_limit; /* 5-break if nhm >= th --> ill-condition */ + u8 cn_limit; /* if condition number >= th --> ill-condition */ +}; + +struct rtw89_btc_fddt_time_ctrl { + /* 1 TDD cycle = w1 + b1, FDD 1cycle = w1fdd-slot + b1fdd-slot */ + u8 m_cycle; /* KPI Moving-Average-Cycle: 1~32 cycles */ + u8 w_cycle; /* Start to calcul WKPI after this if train-phase change */ + u8 k_cycle; /* Total kpi-estimate cycles for each training-step */ + u8 rsvd; +}; + +struct rtw89_btc_fddt_train_info { + struct rtw89_btc_fddt_time_ctrl t_ctrl; + struct rtw89_btc_fddt_break_check b_chk; + struct rtw89_btc_fddt_fail_check f_chk; + struct rtw89_btc_fddt_cell cell_ul[5][5]; + struct rtw89_btc_fddt_cell cell_dl[5][5]; +}; + +struct rtw89_btc_fddt_info { + u8 type; /* refer to enum btc_fddt_type */ + u8 result; /* fw send fdd-training status by c2h */ + u8 state; /* refer to enum btc_fddt_state */ + + u8 wl_iot[6]; /* wl bssid */ + u16 bt_iot; /* bt vendor-id */ + + u32 nrsn_map; /* the reason map for no-run fdd-traing */ + struct rtw89_btc_fddt_bt_stat bt_stat; /* bt statistics */ + struct rtw89_btc_fddt_train_info train; + struct rtw89_btc_fddt_train_info train_now; +}; + struct rtw89_btc_dm { struct rtw89_btc_fbtc_outsrc_set_info ost_info_last; /* outsrc API setup info */ struct rtw89_btc_fbtc_outsrc_set_info ost_info; /* outsrc API setup info */ @@ -3493,6 +3614,8 @@ struct rtw89_btc_dm { union rtw89_btc_dm_error_map error; struct rtw89_btc_bind_info tdd_bind; struct rtw89_btc_bind_info fdd_bind; + struct rtw89_btc_fddt_info fddt_info; + struct rtw89_btc_fddr_info fddr_info; u32 cnt_dm[BTC_DCNT_NUM]; u32 cnt_notify[BTC_NCNT_NUM]; u8 ant_xmap[BTC_RF_NUM][BTC_ALL_BT_EZL]; /* WL-BT ANT interact-map */ @@ -3506,6 +3629,10 @@ struct rtw89_btc_dm { u8 sit_xmap_last[BTC_RF_NUM][BTC_ALL_BT_EZL]; u8 fit_xmap_last[RTW89_PHY_NUM][BTC_ALL_BT_EZL]; + u8 tdd_rssi_thres; /* The FDD/TDD switch RSSI (in %) */ + u8 sir_thres; /* WL(Signal) to BT(interference Pin) ratio */ + u8 sir_state[BTC_ALL_BT_EZL]; /* 1: WL RSSI > BTx-interference */ + u32 update_slot_map; u32 set_ant_path; u32 e2g_slot_limit; From f59f0767348ac196e94f4471585201e20d1cff6b Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Sun, 12 Jul 2026 11:05:03 +0800 Subject: [PATCH 108/403] wifi: rtw89: coex: Correct SET_RFE settings Because of dual-BT & dual-MAC, RTL8922D has more complex antenna settings. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712030506.43438-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.h | 31 +++ drivers/net/wireless/realtek/rtw89/core.h | 11 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 213 ++++++++++++++++-- 3 files changed, 231 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index fdd63e2c0837..e17407696d8a 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -143,6 +143,11 @@ enum btc_switch { BTC_SWITCH_EXTERNAL }; +enum btc_ant_switch_type { + BTC_SWITCH_V1_NONE = 0, /* independent antenna */ + BTC_SWITCH_V1_INTERNAL, /* internal-switch: BTGA structure */ +}; + enum btc_pkt_type { PACKET_DHCP, PACKET_ARP, @@ -262,6 +267,32 @@ enum btc_chip_feature { BTC_FEAT_DUAL_BTGA = BIT(9) /* the future A-Die */ }; +enum btc_efuse_ant_function_map { + BTC_EFMAP_NONE = 0, + BTC_EFMAP_BT0 = BIT(0), + BTC_EFMAP_BT1 = BIT(1), + BTC_EFMAP_ZB = BIT(2), /* ZB or thread */ + BTC_EFMAP_24GP = BIT(3), + BTC_EFMAP_ULL = BIT(4), +}; + +enum btc_extsoc_interface { /* cx->other.hw_coex */ + BTC_EXTSOC_INTF_NONE = 0, + BTC_EXTSOC_INTF_PTA = BIT(0), + BTC_EXTSOC_INTF_MBX = BIT(1), + BTC_EXTSOC_INTF_SWIO = BIT(2), + BTC_EXTSOC_INTF_MAX, +}; + +enum btc_esoc_type { + BTC_ESOC_NONE, + BTC_ESOC_8761, + BTC_ESOC_8771, + BTC_ESOC_SILAB_MG21, + BTC_ESOC_NORDI_NRF52840, + BTC_ESOC_MAX, +}; + enum btc_wl_mode { BTC_WL_MODE_11B = 0, BTC_WL_MODE_11A = 1, diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f784e8437292..d876de21482f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2434,6 +2434,15 @@ struct rtw89_btc_wl_tx_limit_para { u16 tx_retry; }; +struct rtw89_btc_wl_trx_nss_para { + u8 tx_limit; + u8 rx_limit; + u8 tx_ss; + u8 rx_ss; + u8 tx_path; + u8 rx_path; +}; + enum rtw89_btc_bt_scan_type { BTC_SCAN_INQ = 0, BTC_SCAN_PAGE, @@ -3616,6 +3625,7 @@ struct rtw89_btc_dm { struct rtw89_btc_bind_info fdd_bind; struct rtw89_btc_fddt_info fddt_info; struct rtw89_btc_fddr_info fddr_info; + struct rtw89_btc_wl_trx_nss_para wl_trx_nss; u32 cnt_dm[BTC_DCNT_NUM]; u32 cnt_notify[BTC_NCNT_NUM]; u8 ant_xmap[BTC_RF_NUM][BTC_ALL_BT_EZL]; /* WL-BT ANT interact-map */ @@ -3664,6 +3674,7 @@ struct rtw89_btc_dm { u8 wl_tx_pwr_phy_map; u8 vid; u8 client_ps_tdma_on; + u8 wl_trx_nss_en; u8 wl_pre_agc: 2; u8 wl_lna2: 1; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index aeade858ce95..c28670e82c3e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -3106,46 +3106,211 @@ static u32 rtw8922d_chan_to_rf18_val(struct rtw89_dev *rtwdev, static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) { + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_cx *cx = &btc->cx; union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; - struct rtw89_btc_module_v7 *module = &md->md_v7; + struct rtw89_btc_module_v10 *module = &md->md_v10; + u8 efuse_bt_func, efuse_ant_info, bt_sw_gpio_pos; + u8 is_combo, is_bt_share; + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s !!\n", __func__); + + /* get from final capability of device */ module->rfe_type = rtwdev->efuse.rfe_type; module->kt_ver = rtwdev->hal.cv; - module->bt_solo = 0; - module->switch_type = BTC_SWITCH_INTERNAL; + module->kt_ver_adie = rtwdev->hal.acv; module->wa_type = 0; + dm->wl_trx_nss_en = 0; - module->ant.type = BTC_ANT_SHARED; module->ant.num = 2; - module->ant.isolation = 10; - module->ant.diversity = 0; - module->ant.single_pos = RF_PATH_A; - module->ant.btg_pos = RF_PATH_B; + module->ant.single_pos = BTC_RF_S0; /* WL 1ss+1Ant 0:s0(A)/ 1:s1(B) */ - if (module->kt_ver <= 1) - module->wa_type |= BTC_WA_HFP_ZB; + /* set default antenna isolation */ + cx->bt0.ant_iso_to_wl = module->ant.isolation; + cx->bt1.ant_iso_to_wl = module->ant.isolation; - rtwdev->btc.cx.bt_ext.func_type = BTC_3CX_NONE; + module->ant.stream_cnt = 2; + module->ant.btg_pos = BTC_RF_S1; /* BTG0 at WL-S1 */ + module->ant.btg1_pos = BTC_RF_S0; /* BTG1 at WL-S0 if Dual-BTGA */ - if (module->rfe_type == 0) { - rtwdev->btc.dm.error.map.rfe_type0 = true; - return; + cx->bt0.band_56G_support = 1; + cx->bt1.band_56G_support = 1; + cx->bt0.func_type = BTC_BTF_BT; + cx->bt1.func_type = BTC_BTF_BT; + + efuse_bt_func = rtwdev->efuse.bt_setting_2; + + switch ((efuse_bt_func & 0xe0) >> 5) { /* 0xcd[7:5] */ + default: + case 0: + case 1: + bt_sw_gpio_pos = 5; + break; + case 2: + bt_sw_gpio_pos = 11; + break; + case 3: + bt_sw_gpio_pos = 15; + break; + case 4: + bt_sw_gpio_pos = 20; + break; } - module->ant.num = (module->rfe_type % 2) ? 2 : 3; + efuse_bt_func &= 0x1f; /* 0xcd[4:0] */ + efuse_ant_info = rtwdev->efuse.bt_setting_3; + module->ant.num = (efuse_ant_info & 0xe0) >> 5; /* 0xCE[7:5] */ + is_combo = (efuse_ant_info & 0xe) >> 1; /* 0xCE[3:1] */ + is_bt_share = efuse_ant_info & BIT(0); /* 0xCE[0] */ - if (module->kt_ver == 0) - module->ant.num = 2; + memset(dm->ant_xmap, 0, sizeof(dm->ant_xmap)); - if (module->ant.num == 3) { - module->ant.type = BTC_ANT_DEDICATED; - module->bt_pos = BTC_BT_ALONE; - } else { + /* To-Do: "RFE_TYpe" to "ant.num" translation */ + switch (module->ant.num) { + case 1: /* 1-Ant WL-S0 only & BT0 only */ module->ant.type = BTC_ANT_SHARED; - module->bt_pos = BTC_BT_BTG; + module->bt0_pos = BTC_BT_BTG; + module->bt0_sw_type = BTC_SWITCH_INTERNAL; + module->ant.btg_pos = BTC_RF_S0; /* BTG0 at WL-S0 */ + module->ant.stream_cnt = 1; + module->ant.func[0] = BTC_EFMAP_BT0; + dm->ant_xmap[BTC_RF_S0][BTC_BT_1ST] = 1; /* BT0 shared with S0*/ + dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 0; /* WL 1T1R no RF-S1 */ + break; + case 2: /* 2-Ant */ + default: + if (is_combo) { + if (efuse_bt_func == (BTC_EFMAP_BT0 | BTC_EFMAP_BT1)) + module->ant.func[0] = BTC_EFMAP_BT1; + + module->ant.func[1] = BTC_EFMAP_BT0; + } else { + module->ant.func[0] = BTC_EFMAP_NONE; + module->ant.func[1] = BTC_EFMAP_NONE; + } + + if (is_bt_share) { /* WL-S0 + (WL-S1 & BT0-S1) */ + module->ant.type = BTC_ANT_SHARED; + module->bt0_pos = BTC_BT_BTG; + module->bt0_sw_type = BTC_SWITCH_INTERNAL; + dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1; + } else { /* WL-S0 + BT0-S1 */ + module->ant.type = BTC_ANT_DEDICATED; + module->bt0_pos = BTC_BT_ALONE; + module->bt0_sw_type = BTC_SWITCH_V1_NONE; + } + + if (module->ant.func[0] == BTC_EFMAP_BT1) { /* if 2nd BT exist */ + dm->ant_xmap[BTC_RF_S0][BTC_BT_2ND] = 1; + if (module->rfe_type == 12) { /* WL-S0 & BT1 by SPDT */ + module->bt1_pos = BTC_BT_ALONE; + /* Todo: set SPDT GPIO-ctrl */ + module->bt1_sw_type = bt_sw_gpio_pos; + } else { /* WL-S0 & BT1-S1 by BTGA */ + module->bt1_pos = BTC_BT_BTG; + module->bt1_sw_type = BTC_SWITCH_INTERNAL; + } + } + + dm->wl_trx_nss_en = 1; /* 1ss MIMO-PS capability if 1-BT */ + break; + case 3: /* 3-Ant, 3 different BT-configuration */ + if (is_bt_share) { + module->ant.func[0] = BTC_EFMAP_NONE; + module->ant.func[1] = BTC_EFMAP_BT0; + module->ant.func[2] = efuse_bt_func & (~BTC_EFMAP_BT0); + module->ant.type = BTC_ANT_SHARED; + module->bt0_pos = BTC_BT_BTG; + module->bt0_sw_type = BTC_SWITCH_INTERNAL; + dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1; + dm->wl_trx_nss_en = 1; /* 1ss MIMO-PS capability */ + } else { + module->ant.func[0] = BTC_EFMAP_NONE; + module->ant.func[1] = BTC_EFMAP_NONE; + module->ant.func[2] = efuse_bt_func; + module->ant.type = BTC_ANT_DEDICATED; + module->bt0_pos = BTC_BT_ALONE; + module->bt0_sw_type = BTC_SWITCH_V1_NONE; + } + + module->bt1_pos = BTC_BT_ALONE; /* BT1 may exist or not */ + module->bt1_sw_type = BTC_SWITCH_V1_NONE; + break; + case 4: /* 4-Ant, WL-S0 + WL-S1 + BT0 + BT1 */ + module->ant.func[0] = BTC_EFMAP_NONE; + module->ant.func[1] = BTC_EFMAP_NONE; + module->ant.func[2] = BTC_EFMAP_BT0; + module->ant.func[3] = efuse_bt_func & (~BTC_EFMAP_BT0); + module->ant.type = BTC_ANT_DEDICATED; + module->bt0_pos = BTC_BT_ALONE; + module->bt0_sw_type = BTC_SWITCH_V1_NONE; + module->bt1_pos = BTC_BT_ALONE; + module->bt1_sw_type = BTC_SWITCH_V1_NONE; + break; + case 5: + module->ant.func[0] = BTC_EFMAP_NONE; + module->ant.func[1] = BTC_EFMAP_NONE; + module->ant.func[2] = BTC_EFMAP_BT0; + module->ant.func[3] = BTC_EFMAP_BT1; + module->ant.func[4] = BTC_EFMAP_ZB; + module->ant.type = BTC_ANT_DEDICATED; + module->bt0_pos = BTC_BT_ALONE; + module->bt0_sw_type = BTC_SWITCH_V1_NONE; + module->bt1_pos = BTC_BT_ALONE; + module->bt1_sw_type = BTC_SWITCH_V1_NONE; + break; } - rtwdev->btc.btg_pos = module->ant.btg_pos; - rtwdev->btc.ant_type = module->ant.type; + + /* + * if only BT0 at BTGA: 2-Ant, 3-Ant(BT1 used dedicated-ant) + * can setup dm->wl_trx_nss_en = 1, WL MIMO-PS to 1T1R + * (WL at WL-S0 only, BT0 at WL-S1) + * tx_limit/rx_limit is decided by _set_trx_nss() + */ + if (dm->wl_trx_nss_en && + (dm->wl_trx_nss.tx_limit && dm->wl_trx_nss.rx_limit)) { + module->ant.type = BTC_ANT_DEDICATED; + module->ant.stream_cnt = 1; + dm->ant_xmap[BTC_RF_S0][BTC_BT_1ST] = 0; /* wl 1ss-> RF-S0 */ + dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 0; /* BT0-> RF-S1 */ + dm->ant_xmap[BTC_RF_S0][BTC_BT_2ND] = 0; + dm->ant_xmap[BTC_RF_S1][BTC_BT_2ND] = 0; /* BT1 -> 3rd Ant */ + } + + /* + * Todo: call HALBB API to set BT0/1 at WL_S0 or WL_S1 + * r_sel_gnt_bt_rx_path0[1:0], r_sel_gnt_bt_rx_path1[1:0] + * WL_S0 with BT1 -> r_sel_gnt_bt_rx_path0[1:0]= 2b'10 + * WL_S1 with BT0 -> r_sel_gnt_bt_rx_path1[1:0]= 2b'01 + */ + + /* To Do: setup ext-SOC coex if exist */ + switch (cx->bt_ext.chip_id) { + default: + case BTC_ESOC_NONE: + memset(&cx->bt_ext, 0, sizeof(struct rtw89_btc_extsoc_info)); + cx->bt_ext.max_tx_pwr = RTW89_BTC_BT_DEF_LE_TX_PWR; + cx->bt_ext.ant_iso_to_wl = RTW89_BTC_DEFAULT_ANISO; + break; + case BTC_ESOC_8771: + cx->bt_ext.func_type = BTC_BTF_THREAD; + cx->bt_ext.hw_coex = BTC_EXTSOC_INTF_PTA; + cx->bt_ext.rf_band_map = 0x1; /* 2.4GHz only */ + cx->bt_ext.link_weight[BTC_BT_B2G] = 10; + cx->bt_ext.profile_map[BTC_BT_B2G] |= BTC_BT_THREAD; + /* use GPIO 12~15 for Ext-4-wire-PTA */ + cx->bt_ext.hpta_cfg = BIT(12) | BIT(13) | BIT(14) | BIT(15); + /* for Ext-SOC locate at Ant-2 */ + if (module->ant.num >= 5) + module->ant.func[4] = BTC_EFMAP_ZB; + else + module->ant.func[module->ant.num - 1] = BTC_EFMAP_ZB; + break; + } + + dm->ant_xmap[BTC_RF_S0][BTC_BT_EXT] = 0; + dm->ant_xmap[BTC_RF_S1][BTC_BT_EXT] = 0; } static void rtw8922d_btc_init_cfg(struct rtw89_dev *rtwdev) From 578f9b48276c07e3bc9a8a5f208a18b42da1e1bb Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Sun, 12 Jul 2026 11:05:04 +0800 Subject: [PATCH 109/403] wifi: rtw89: coex: Add firmware report control report v11 In the version 11 report control report, firmware will report firmware build date, version. And Bluetooth to Wi-Fi scoreboard value will be read at Wi-Fi firmware and update to Wi-Fi driver. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712030506.43438-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 218 +++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/core.h | 26 +++ 2 files changed, 243 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 17290b2cb2c8..c2356d6d9811 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1417,6 +1417,40 @@ static void _chk_btc_err(struct rtw89_dev *rtwdev, u8 type, u32 cnt) dm->error.map.bt_slot_drift = false; break; + case BTC_DCNT_WL_STA_NTFY: + cnt = dm->cnt_notify[BTC_NCNT_WL_STA] - + dm->cnt_notify[BTC_NCNT_WL_STA_LAST]; + + dm->cnt_notify[BTC_NCNT_WL_STA_LAST] = + dm->cnt_notify[BTC_NCNT_WL_STA]; + + if (cnt == 0 && + (dm->tdd_bind.wl_link_mode != BTC_WLINK_NOLINK || + dm->fdd_bind.wl_link_mode != BTC_WLINK_NOLINK)) + dm->cnt_dm[BTC_DCNT_WL_STA_NTFY]++; + else + dm->cnt_dm[BTC_DCNT_WL_STA_NTFY] = 0; + + if (dm->cnt_dm[BTC_DCNT_WL_STA_NTFY] >= BTC_CHK_HANG_MAX) + dm->error.map.wl_no_sta_ntfy = true; + else + dm->error.map.wl_no_sta_ntfy = false; + break; + case BTC_DCNT_W2B_SCBD_NOSYNC: + if (!(rtwdev->chip->para_ver & BTC_FEAT_MULTI_PTA)) + return; /* return if no-support W2B scbd readback */ + + if (wl->scbd_rb[BTC_BT_1ST] != wl->scbd[BTC_BT_1ST] || + wl->scbd_rb[BTC_BT_2ND] != wl->scbd[BTC_BT_2ND]) + dm->cnt_dm[BTC_DCNT_W2B_SCBD_NOSYNC]++; + else + dm->cnt_dm[BTC_DCNT_W2B_SCBD_NOSYNC] = 0; + + if (dm->cnt_dm[BTC_DCNT_W2B_SCBD_NOSYNC] >= BTC_CHK_HANG_MAX) + dm->error.map.w2b_scbd_no_sync = true; + else + dm->error.map.w2b_scbd_no_sync = false; + break; } } @@ -1564,6 +1598,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; + struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; union rtw89_btc_fbtc_rpt_ctrl_ver_info *prpt = NULL; @@ -1574,7 +1609,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, u16 wl_slot_set = 0, wl_slot_real = 0, val16; u32 trace_step = 0, rpt_len = 0, diff_t = 0; u32 cnt_leak_slot, bt_slot_real, bt_slot_set, cnt_rx_imr; - u8 i, val = 0, val1, val2; + u8 i, j, val = 0, val1, val2; + u32 *bt_cnt, start_idx; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): index:%d\n", @@ -1626,6 +1662,10 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, pfinfo = &pfwinfo->rpt_ctrl.finfo.v7; pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v7); fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v7.fver; + } else if (ver->fcxbtcrpt == 11) { + pfinfo = &pfwinfo->rpt_ctrl.finfo.v11; + pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v11); + fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v11.fver; } else { goto err; } @@ -2031,6 +2071,70 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, _chk_btc_err(rtwdev, BTC_DCNT_RPT_HANG, val1); _chk_btc_err(rtwdev, BTC_DCNT_WL_FW_VER_MATCH, 0); _chk_btc_err(rtwdev, BTC_DCNT_BTTX_HANG, 0); + } else if (ver->fcxbtcrpt == 11) { + prpt->v11 = pfwinfo->rpt_ctrl.finfo.v11; + pfwinfo->rpt_en_map = le32_to_cpu(prpt->v11.rpt_info.en); + wl->ver_info.fw_coex = le32_to_cpu(prpt->v11.rpt_info.cx_ver); + wl->ver_info.fw = le32_to_cpu(prpt->v11.rpt_info.fw_ver); + + memset(wl->ver_info.build_time, 0, + sizeof(wl->ver_info.build_time)); + memcpy(wl->ver_info.build_time, + prpt->v11.build_time, + sizeof(wl->ver_info.build_time)); + + memset(wl->ver_info.build_date, 0, + sizeof(wl->ver_info.build_date)); + memcpy(wl->ver_info.build_date, + prpt->v11.build_date, + sizeof(wl->ver_info.build_date)); + + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) + memcpy(&dm->gnt_set[i], &prpt->v11.gnt_val[i][0], + sizeof(dm->gnt_set[i])); + + for (i = BTC_BT_1ST; i <= BTC_BT_EXT; i++) { + if (i == BTC_BT_EXT) { + if (cx->bt_ext.func_type == BTC_BTF_NONE) + continue; + bt_cnt = cx->bt_ext.bcnt; + } else if (i == BTC_BT_2ND) { + if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT)) + continue; + bt_cnt = cx->bt1.bcnt; + wl->scbd_rb[i] = le32_to_cpu(prpt->v11.scbd_w2b[i]); + cx->bt1.scbd_rb = le32_to_cpu(prpt->v11.scbd_b2w[i]); + } else { + bt_cnt = cx->bt0.bcnt; + wl->scbd_rb[i] = le32_to_cpu(prpt->v11.scbd_w2b[i]); + cx->bt0.scbd_rb = le32_to_cpu(prpt->v11.scbd_b2w[i]); + } + + start_idx = BTC_BCNT_HIPRI_TX; + for (j = BTC_BCNT_HI_TX_V105; j <= BTC_BCNT_LO_RX_V105 ; j++) + bt_cnt[start_idx++] = le16_to_cpu(prpt->v11.bt_cnt[i][j]); + + /* check if polut counter increase */ + val1 = le16_to_cpu(prpt->v11.bt_cnt[i][BTC_BCNT_POLLUTED_V105]); + if (val1 > bt_cnt[BTC_BCNT_POLUT_NOW]) /* cnt increase*/ + val2 = val1 - bt_cnt[BTC_BCNT_POLUT_NOW]; + else + val2 = val1; /* overflow */ + + bt_cnt[BTC_BCNT_POLUT_DIFF] = val2; + bt_cnt[BTC_BCNT_POLUT_NOW] = val1; + + if (i <= BTC_BT_2ND) + bt_cnt[BTC_BCNT_SCBDREAD]++; + } + dm->scbd_w2b_update = true; + dm->scbd_b2w_update = true; + + _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); + _chk_btc_err(rtwdev, BTC_DCNT_WL_FW_VER_MATCH, 0); + _chk_btc_err(rtwdev, BTC_DCNT_BTTX_HANG, 0); + _chk_btc_err(rtwdev, BTC_DCNT_WL_STA_NTFY, 0); + _chk_btc_err(rtwdev, BTC_DCNT_W2B_SCBD_NOSYNC, 0); } else { goto err; } @@ -11953,6 +12057,116 @@ static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) return p - buf; } +static int _show_summary_v11(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) +{ + struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo; + struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; + struct rtw89_btc_fbtc_rpt_ctrl_v11 *prptctrl = NULL; + struct rtw89_btc_cx *cx = &rtwdev->btc.cx; + struct rtw89_btc_dm *dm = &rtwdev->btc.dm; + struct rtw89_btc_wl_info *wl = &cx->wl; + u32 *cnt = rtwdev->btc.dm.cnt_notify; + char *p = buf, *end = buf + bufsz; + u32 cnt_sum = 0; + u8 i; + + if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) + return 0; + + p += scnprintf(p, end - p, "%s", + "\n\r========== [Statistics] =========="); + + pcinfo = &pfwinfo->rpt_ctrl.cinfo; + if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF && + !wl->status.map.rf_off) { + prptctrl = &pfwinfo->rpt_ctrl.finfo.v11; + + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le16_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le16_to_cpu(prptctrl->rpt_info.cnt_c2h), + le16_to_cpu(prptctrl->rpt_info.len_c2h), + (prptctrl->rpt_len_max_h << 8) + prptctrl->rpt_len_max_l, + rtwdev->btc.ver->info_buf); + + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le16_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en)); + + if (dm->error.map.wl_fw_hang) + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + + p += scnprintf(p, end - p, + "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + + p += scnprintf(p, end - p, + "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", + "[RFK/LPS]", wl->wcnt[BTC_WCNT_RFK_REQ], + wl->wcnt[BTC_WCNT_RFK_GO], + wl->wcnt[BTC_WCNT_RFK_REJECT], + wl->wcnt[BTC_WCNT_RFK_TIMEOUT], + wl->rfk_info.proc_time); + + p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); + } else { + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", + "[summary]", + pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, + pfwinfo->cnt_c2h, + wl->status.map.lps, wl->status.map.rf_off); + } + + for (i = 0; i < BTC_NCNT_NUM; i++) + cnt_sum += dm->cnt_notify[i]; + + p += scnprintf(p, end - p, + "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", + cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); + + p += scnprintf(p, end - p, + "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", + "[notify_cnt]", + cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], + cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], + cnt[BTC_NCNT_SPECIAL_PACKET]); + + p += scnprintf(p, end - p, + "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], + rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], + cnt[BTC_NCNT_COUNTRYCODE]); + + return p - buf; +} + ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; @@ -12016,6 +12230,8 @@ ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += _show_summary_v7(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 8) p += _show_summary_v8(rtwdev, p, end - p); + else if (ver->fcxbtcrpt == 11) + p += _show_summary_v11(rtwdev, p, end - p); return p - buf; } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d876de21482f..e7e72f742847 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1406,6 +1406,7 @@ enum rtw89_btc_dcnt { BTC_DCNT_WL_FW_VER_MATCH, BTC_DCNT_NULL_TX_FAIL, BTC_DCNT_WL_STA_NTFY, + BTC_DCNT_W2B_SCBD_NOSYNC, BTC_DCNT_NUM, }; @@ -2058,6 +2059,8 @@ struct rtw89_btc_wl_role_info { /* Logic dynamic using */ }; struct rtw89_btc_wl_ver_info { + char build_time[12]; + char build_date[12]; u32 fw_coex; /* match with which coex_ver */ u32 fw; u32 mac; @@ -2245,6 +2248,7 @@ struct rtw89_btc_dm_emap { u32 h2c_buffer_over: 1; u32 bt_tx_hang: 1; /* for SNR too low bug, BT has no Tx req*/ u32 wl_no_sta_ntfy: 1; + u32 w2b_scbd_no_sync: 1; u32 h2c_bmap_mismatch: 1; u32 c2h_bmap_mismatch: 1; @@ -2784,6 +2788,26 @@ struct rtw89_btc_fbtc_rpt_ctrl_v8 { struct rtw89_btc_fbtc_rpt_ctrl_bt_mailbox bt_mbx_info; } __packed; +struct rtw89_btc_fbtc_rpt_ctrl_v11 { + u8 fver; + u8 rsvd0; + u8 rpt_len_max_l; /* BTC_RPT_MAX bit0~7 */ + u8 rpt_len_max_h; /* BTC_RPT_MAX bit8~15 */ + + u8 build_time[12]; + u8 build_date[12]; + + u8 gnt_val[RTW89_PHY_NUM][8]; /* gwl/gbt012 refer to struct btc_gnt_ctrl */ + __le16 bt_cnt[BTC_ALL_BT_EZL][BTC_BCNT_STA_MAX_V105]; + + struct rtw89_btc_fbtc_rpt_ctrl_info_v8 rpt_info; + struct rtw89_btc_fbtc_rpt_ctrl_bt_mailbox bt_mbx_info; + + __le32 error_code; + __le32 scbd_w2b[2]; + __le32 scbd_b2w[2]; +} __packed; + union rtw89_btc_fbtc_rpt_ctrl_ver_info { struct rtw89_btc_fbtc_rpt_ctrl_v1 v1; struct rtw89_btc_fbtc_rpt_ctrl_v4 v4; @@ -2791,6 +2815,7 @@ union rtw89_btc_fbtc_rpt_ctrl_ver_info { struct rtw89_btc_fbtc_rpt_ctrl_v105 v105; struct rtw89_btc_fbtc_rpt_ctrl_v7 v7; struct rtw89_btc_fbtc_rpt_ctrl_v8 v8; + struct rtw89_btc_fbtc_rpt_ctrl_v11 v11; }; enum rtw89_fbtc_ext_ctrl_type { @@ -3691,6 +3716,7 @@ struct rtw89_btc_dm { u8 lps_ctrl_change: 1; u8 scbd_write_instant; bool scbd_b2w_update; + bool scbd_w2b_update; bool pre_agc_chg; }; From aed0d7771d52d0e21eba4d3a4969fae4e450d23e Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Sun, 12 Jul 2026 11:05:05 +0800 Subject: [PATCH 110/403] wifi: rtw89: coex: update external control length by case Update recommend external control slot length to driver. Some of the Wi-Fi feature has its time slot requirement can not be simply controlled by coexistence firmware TDMA timer. For example: Wi-Fi scan/MCC etc. In the same time, coexistence need to tell driver the recommend Bluetooth slot length to make sure Bluetooth can still has enough time slot to traffic. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712030506.43438-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 70 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.c | 11 ++-- drivers/net/wireless/realtek/rtw89/core.h | 4 +- 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index c2356d6d9811..a6b1aa2cb24e 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -526,6 +526,14 @@ enum btc_cx_poicy_main_type { BTC_CXP_MAIN_MAX }; +enum btc_bslot_length { + BTC_BSLOT_A2DP_HID = 60, + BTC_BSLOT_A2DP = 50, + BTC_BSLOT_A2DP_2 = 40, + BTC_BSLOT_INQ = 30, + BTC_BSLOT_IDLE = 20, +}; + enum btc_cx_poicy_type { /* TDMA off + pri: BT > WL */ BTC_CXP_OFF_BT = (BTC_CXP_OFF << 8) | 0, @@ -5989,6 +5997,67 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev) &data); } +static void _set_phl_bt_slot_req(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_cx *cx = &btc->cx; + struct rtw89_btc_wl_info *wl = &cx->wl; + struct rtw89_btc_dm *dm = &btc->dm; + u8 len = 0; + u8 i; + + /* don't change bt slot req state during RFK for p2p/mcc case */ + if (dm->run_reason == BTC_RSN_NTFY_WL_RFK || + wl->status.map.transacting) + return; + + /* enable bt-slot req if ext-slot-control */ + if (dm->tdma_now.type == CXTDMA_OFF && + dm->tdma_now.ext_ctrl == CXECTL_EXT && + dm->tdd_bind.bt_sel != 0) { + if (btc->cx.wl.status.val & btc_scanning_map.val) + btc->bt_req_en = false; + else + btc->bt_req_en = true; + } else { + btc->bt_req_en = false; + } + + if (dm->slot_req_more) { + len = BTC_BSLOT_A2DP_HID; + } else { + if (dm->tdd_bind.bt_link_weight >= BTC_BSLOT_A2DP_HID) + len = BTC_BSLOT_A2DP_HID; + else if (dm->tdd_bind.bt_link_weight <= BTC_BSLOT_IDLE) + len = BTC_BSLOT_IDLE; + else + len = dm->tdd_bind.bt_link_weight; + } + + if (dm->tdd_bind.rf_band == BIT(RTW89_BAND_5G)) + len = 100 - len; + + if (!btc->bt_req_en) + len = 0; + + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) { + if (!(dm->tdd_bind.wl_hwb_sel & BIT(i))) + continue; + + if (len == btc->bt_req_len[i]) + continue; + + btc->bt_req_len[i] = len; + + rtw89_core_ntfy_btc_event(rtwdev, + RTW89_BTC_HMSG_SET_BT_REQ_SLOT, i); + + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): HWB%d bt_req_len = %d\n", + __func__, i, btc->bt_req_len[i]); + } +} + static void _set_bt_rx_agc(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; @@ -6148,6 +6217,7 @@ static void _action_common(struct rtw89_dev *rtwdev) _set_btg_ctrl(rtwdev); _set_wl_preagc_ctrl(rtwdev); _set_wl_tx_limit(rtwdev); + _set_phl_bt_slot_req(rtwdev); _set_bt_afh_info(rtwdev); _set_bt_rx_agc(rtwdev); _set_rf_trx_para(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 0343cd1a0ee1..30a8e337498e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -6539,15 +6539,18 @@ void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond, rtw89_complete_cond_resp(resp, data); } -void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event) +void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, + enum rtw89_btc_hmsg event, + enum rtw89_phy_idx phy_idx) { - u16 bt_req_len; + u16 bt_slot_req[RTW89_PHY_NUM]; switch (event) { case RTW89_BTC_HMSG_SET_BT_REQ_SLOT: - bt_req_len = rtw89_coex_query_bt_req_len(rtwdev, RTW89_PHY_0); + bt_slot_req[phy_idx] = rtw89_coex_query_bt_req_len(rtwdev, phy_idx); rtw89_debug(rtwdev, RTW89_DBG_BTC, - "coex updates BT req len to %d TU\n", bt_req_len); + "coex updates PHY-%d BT req len to %d TU\n", + phy_idx, bt_slot_req[phy_idx]); rtw89_queue_chanctx_change(rtwdev, RTW89_CHANCTX_BT_SLOT_CHANGE); break; default: diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index e7e72f742847..c8b5c9ed55be 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -8866,7 +8866,9 @@ int rtw89_reg_6ghz_recalc(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvi void rtw89_core_update_p2p_ps(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct ieee80211_bss_conf *bss_conf); -void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event); +void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, + enum rtw89_btc_hmsg event, + enum rtw89_phy_idx phy_idx); int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, unsigned int link_id); void rtw89_core_dm_disable_cfg(struct rtw89_dev *rtwdev, u32 new); From c2d96cd05c17c7b53f52395c766f629df3a78b3a Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Sun, 12 Jul 2026 11:05:06 +0800 Subject: [PATCH 111/403] wifi: rtw89: coex: Update coexistence version to 9.24.0 RTL8922D first release, add related feature support. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712030506.43438-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index a6b1aa2cb24e..b044b25aeade 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -11,7 +11,7 @@ #include "ps.h" #include "reg.h" -#define RTW89_COEX_VERSION 0x09000113 +#define RTW89_COEX_VERSION 0x09180013 #define FCXDEF_STEP 50 /* MUST <= FCXMAX_STEP and match with wl fw*/ #define BTC_E2G_LIMIT_DEF 80 From b45b22bbe9d51493e8d4b5466d3dc0c7b7e26278 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Sun, 12 Jul 2026 11:44:59 +0800 Subject: [PATCH 112/403] wifi: rtw89: pack I/O during bb_sethw to reduce API execution time Wrap rtw89_chip_bb_sethw() with rtw89_io_pack/unpack so all register writes during baseband hardware initialization are batched into a single bus transaction. This reduces API execution time from ~11000 us to ~4000 us on affected platforms. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712034506.53209-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c8b5c9ed55be..0ff090175ce5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -8216,8 +8216,12 @@ static inline void rtw89_chip_bb_sethw(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; + rtw89_io_pack(rtwdev); + if (chip->ops->bb_sethw) chip->ops->bb_sethw(rtwdev); + + rtw89_io_unpack(rtwdev); } static inline void rtw89_chip_rfk_init(struct rtw89_dev *rtwdev) From 07c26ead36e7dc9a598b815ed750d35440c440fa Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 12 Jul 2026 11:45:00 +0800 Subject: [PATCH 113/403] wifi: rtw89: mac: abstract register definition of firmware boot debug The registers of firmware boot debug are different between WiFi 6 and 7 chips. Add field to abstract it accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712034506.53209-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 5 +++-- drivers/net/wireless/realtek/rtw89/mac.c | 1 + drivers/net/wireless/realtek/rtw89/mac.h | 1 + drivers/net/wireless/realtek/rtw89/mac_be.c | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 027f8121efd1..f290bcdd4fda 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1939,13 +1939,14 @@ static void rtw89_fw_prog_cnt_dump(struct rtw89_dev *rtwdev) static void rtw89_fw_dl_fail_dump(struct rtw89_dev *rtwdev) { + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u32 val32; val32 = rtw89_read32(rtwdev, R_AX_WCPU_FW_CTRL); rtw89_err(rtwdev, "[ERR]fwdl 0x1E0 = 0x%x\n", val32); - val32 = rtw89_read32(rtwdev, R_AX_BOOT_DBG); - rtw89_err(rtwdev, "[ERR]fwdl 0x83F0 = 0x%x\n", val32); + val32 = rtw89_read32(rtwdev, mac->boot_dbg); + rtw89_err(rtwdev, "[ERR]fwdl 0x%x = 0x%x\n", mac->boot_dbg, val32); rtw89_fw_prog_cnt_dump(rtwdev); } diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index b9cd7fd8d76b..3e06a0bdf7e9 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -7445,6 +7445,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .agg_len_ht = R_AX_AGG_LEN_HT_0, .ps_status = R_AX_PPWRBIT_SETTING, .mu_gid = &rtw89_mac_mu_gid_addr_ax, + .boot_dbg = R_AX_BOOT_DBG, .muedca_ctrl = { .addr = R_AX_MUEDCA_EN, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index a5f1694af91a..c05f5ee0d2fd 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1067,6 +1067,7 @@ struct rtw89_mac_gen_def { u32 agg_len_ht; u32 ps_status; const struct rtw89_mac_mu_gid_addr *mu_gid; + u32 boot_dbg; struct rtw89_reg_def muedca_ctrl; struct rtw89_reg_def bfee_ctrl; diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 33513b283d84..8de0fe5a3b1d 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -3284,6 +3284,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .agg_len_ht = R_BE_AGG_LEN_HT_0, .ps_status = R_BE_WMTX_POWER_BE_BIT_CTL, .mu_gid = &rtw89_mac_mu_gid_addr_be, + .boot_dbg = R_BE_BOOT_DBG, .muedca_ctrl = { .addr = R_BE_MUEDCA_EN, From b9f582e939f6777df5b11d3c99549fec60737644 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 12 Jul 2026 11:45:01 +0800 Subject: [PATCH 114/403] wifi: rtw89: 8922d: add TX time limit for 2GHz band Fix 2.4GHz specific L-SIG length TX issue, causing interoperability problem with certain APs. Limit the A-MPDU duration to be workaround. For 8922DE, the MAC limit is 164 ticks, and BB limit is 4608 us. The conversion is 32.768us / tick. Since smaller limit should be adopted, BB limit is filled into newly added field. The units of register and CCTL table are tick and us/512 respectively. Convert to target unit when filling values. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712034506.53209-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac.c | 20 +++++++++++++++++-- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 9 +++++++++ 9 files changed, 34 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0ff090175ce5..11b05dbabee6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5418,6 +5418,7 @@ struct rtw89_chip_info { const struct wiphy_wowlan_support *wowlan_stub; const struct rtw89_xtal_info *xtal_info; unsigned long default_quirks; /* bitmap of rtw89_quirks */ + u16 txtime_limit_2ghz; }; struct rtw89_chip_variant { diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 3e06a0bdf7e9..d8f0add7e1b0 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -7050,11 +7050,23 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_ { #define MAC_AX_DFLT_TX_TIME 5280 const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + const struct rtw89_chip_info *chip = rtwdev->chip; u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx; u32 max_tx_time = tx_time == 0 ? MAC_AX_DFLT_TX_TIME : tx_time; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; u32 reg; int ret = 0; + if (chip->txtime_limit_2ghz) { + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[mac_idx]; + + if (chan->band_type == RTW89_BAND_2G) + max_tx_time = min_t(u32, max_tx_time, + chip->txtime_limit_2ghz); + } + if (rtwsta_link->cctl_tx_time) { rtwsta_link->ampdu_max_time = (max_tx_time - 512) >> 9; ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); @@ -7065,9 +7077,13 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_ return ret; } + if (chip->chip_gen == RTW89_CHIP_AX) + max_tx_time >>= 5; + else + max_tx_time = max_tx_time * 1000 >> 15; + reg = rtw89_mac_reg_by_idx(rtwdev, mac->agg_limit.addr, mac_idx); - rtw89_write32_mask(rtwdev, reg, mac->agg_limit.mask, - max_tx_time >> 5); + rtw89_write32_mask(rtwdev, reg, mac->agg_limit.mask, max_tx_time); } return ret; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 91d6cddc713e..35ca38fa30e8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2780,6 +2780,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { #endif .xtal_info = &rtw8851b_xtal_info, .default_quirks = 0, + .txtime_limit_2ghz = 0, }; EXPORT_SYMBOL(rtw8851b_chip_info); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index ed6dee63694f..f32c7c6a4075 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2518,6 +2518,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { #endif .xtal_info = &rtw8852a_xtal_info, .default_quirks = 0, + .txtime_limit_2ghz = 0, }; EXPORT_SYMBOL(rtw8852a_chip_info); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 4f55a153d169..a45f05309af8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -1116,6 +1116,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { #endif .xtal_info = NULL, .default_quirks = 0, + .txtime_limit_2ghz = 0, }; EXPORT_SYMBOL(rtw8852b_chip_info); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 9e0c48fde2fa..8d5dd93626b3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -952,6 +952,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { #endif .xtal_info = NULL, .default_quirks = 0, + .txtime_limit_2ghz = 0, }; EXPORT_SYMBOL(rtw8852bt_chip_info); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index f21dd87e3846..89c8b0d7a554 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3321,6 +3321,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { #endif .xtal_info = NULL, .default_quirks = 0, + .txtime_limit_2ghz = 0, }; EXPORT_SYMBOL(rtw8852c_chip_info); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index cdcce091b922..030866080cbd 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -3303,6 +3303,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { #endif .xtal_info = NULL, .default_quirks = 0, + .txtime_limit_2ghz = 0, }; EXPORT_SYMBOL(rtw8922a_chip_info); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index c28670e82c3e..50a896c0cee9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -1173,8 +1173,10 @@ static void rtw8922d_set_channel_mac(struct rtw89_dev *rtwdev, u32 sub_carr = rtw89_mac_reg_by_idx(rtwdev, R_BE_TX_SUB_BAND_VALUE, mac_idx); u32 chk_rate = rtw89_mac_reg_by_idx(rtwdev, R_BE_TXRATE_CHK, mac_idx); u32 rf_mod = rtw89_mac_reg_by_idx(rtwdev, R_BE_WMAC_RFMOD, mac_idx); + const struct rtw89_chip_info *chip = rtwdev->chip; u8 txsb20 = 0, txsb40 = 0, txsb80 = 0; u8 rf_mod_val, chk_rate_mask, sifs; + u16 tx_time = AMPDU_MAX_TIME_V1; u32 txsb; u32 reg; @@ -1255,6 +1257,12 @@ static void rtw8922d_set_channel_mac(struct rtw89_dev *rtwdev, reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_MUEDCA_EN, mac_idx); rtw89_write32_mask(rtwdev, reg, B_BE_SIFS_MACTXEN_TB_T1_DOT05US_MASK, sifs); + + if (chan->band_type == RTW89_BAND_2G && chip->txtime_limit_2ghz) + tx_time = min_t(u32, tx_time, chip->txtime_limit_2ghz * 1000 >> 15); + + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_AMPDU_AGG_LIMIT, mac_idx); + rtw89_write32_mask(rtwdev, reg, B_BE_AMPDU_MAX_TIME_MASK, tx_time); } static const u32 rtw8922d_sco_barker_threshold[14] = { @@ -3757,6 +3765,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { #endif .xtal_info = NULL, .default_quirks = BIT(RTW89_QUIRK_THERMAL_PROT_120C), + .txtime_limit_2ghz = 4608, }; EXPORT_SYMBOL(rtw8922d_chip_info); From ed74acea8320fa60d89abe0c3b6212d60fcd8227 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Sun, 12 Jul 2026 11:45:02 +0800 Subject: [PATCH 115/403] wifi: rtw89: introduce helper to get tx shape index TX shape has a set of parameters inside RFE (RF Front End) parameters. It also depends on regulation and even will depend on regulatory 6 GHz power type afterwards. Introduce a helper to encapsulate the access to TX shape index. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712034506.53209-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 13 +++++++++++++ drivers/net/wireless/realtek/rtw89/rtw8851b.c | 6 ++---- .../net/wireless/realtek/rtw89/rtw8852b_common.c | 6 ++---- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 6 ++---- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 9 ++------- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 9 ++------- 6 files changed, 23 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 11b05dbabee6..f554e2ca1d1c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -8448,6 +8448,19 @@ static inline u8 rtw89_regd_get(struct rtw89_dev *rtwdev, u8 band) return txpwr_regd; } +static inline u8 rtw89_get_tx_shape_idx(struct rtw89_dev *rtwdev, u8 band, + enum rtw89_rate_section rs) +{ + const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; + const struct rtw89_tx_shape *tx_shape = &rfe_parms->tx_shape; + u8 regd = rtw89_regd_get(rtwdev, band); + + if (unlikely(rs >= RTW89_RS_TX_SHAPE_NUM)) + rs = RTW89_RS_OFDM; + + return (*tx_shape->lmt)[band][rs][regd]; +} + static inline void rtw89_ctrl_btg_bt_rx(struct rtw89_dev *rtwdev, bool en, enum rtw89_phy_idx phy_idx) { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 35ca38fa30e8..e3a17f539ad6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -1918,11 +1918,9 @@ static void rtw8851b_set_tx_shape(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { - const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; u8 band = chan->band_type; - u8 regd = rtw89_regd_get(rtwdev, band); - u8 tx_shape_cck = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_CCK][regd]; - u8 tx_shape_ofdm = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_OFDM][regd]; + u8 tx_shape_cck = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_CCK); + u8 tx_shape_ofdm = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_OFDM); if (band == RTW89_BAND_2G) rtw8851b_bb_set_tx_shape_dfir(rtwdev, chan, tx_shape_cck, phy_idx); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c index 7d409a64869f..7c68260dce70 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c @@ -1321,11 +1321,9 @@ static void rtw8852bx_set_tx_shape(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { - const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; u8 band = chan->band_type; - u8 regd = rtw89_regd_get(rtwdev, band); - u8 tx_shape_cck = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_CCK][regd]; - u8 tx_shape_ofdm = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_OFDM][regd]; + u8 tx_shape_cck = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_CCK); + u8 tx_shape_ofdm = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_OFDM); if (band == RTW89_BAND_2G) rtw8852bx_bb_set_tx_shape_dfir(rtwdev, chan, tx_shape_cck, phy_idx); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 89c8b0d7a554..3dc6dfce082a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2193,11 +2193,9 @@ static void rtw8852c_set_tx_shape(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { - const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; u8 band = chan->band_type; - u8 regd = rtw89_regd_get(rtwdev, band); - u8 tx_shape_cck = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_CCK][regd]; - u8 tx_shape_ofdm = (*rfe_parms->tx_shape.lmt)[band][RTW89_RS_OFDM][regd]; + u8 tx_shape_cck = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_CCK); + u8 tx_shape_ofdm = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_OFDM); if (band == RTW89_BAND_2G) rtw8852c_bb_set_tx_shape_dfir(rtwdev, chan, tx_shape_cck, phy_idx); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 030866080cbd..854ef9a980bd 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2502,15 +2502,10 @@ static void rtw8922a_set_tx_shape(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { - const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; - const struct rtw89_tx_shape *tx_shape = &rfe_parms->tx_shape; + u8 band = chan->band_type; u8 tx_shape_idx; - u8 band, regd; - - band = chan->band_type; - regd = rtw89_regd_get(rtwdev, band); - tx_shape_idx = (*tx_shape->lmt)[band][RTW89_RS_OFDM][regd]; + tx_shape_idx = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_OFDM); if (tx_shape_idx == 0) rtw8922a_bb_tx_triangular(rtwdev, false, phy_idx); else diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 50a896c0cee9..929bcfc8089d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2854,16 +2854,11 @@ static void rtw8922d_set_tx_shape(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; - const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; - const struct rtw89_tx_shape *tx_shape = &rfe_parms->tx_shape; + u8 band = chan->band_type; u8 tx_shape_idx; - u8 band, regd; const u16 *th; - band = chan->band_type; - regd = rtw89_regd_get(rtwdev, band); - tx_shape_idx = (*tx_shape->lmt)[band][RTW89_RS_OFDM][regd]; - + tx_shape_idx = rtw89_get_tx_shape_idx(rtwdev, band, RTW89_RS_OFDM); if (tx_shape_idx == 0) goto disable; From 7054b03136e4bd5645d12cbd8d93b15afae4b8be Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Sun, 12 Jul 2026 11:45:03 +0800 Subject: [PATCH 116/403] wifi: rtw89: add tx shape v0 to keep built-in arrays compatible during transitions TX shape parameters can come from (old way) built-in arrays or (new way) FW elements. The built-in arrays will no longer be updated, but will be retained during a certain transition period. However, the format of newer TX shape parameters are going to be expanded. It will only be applied to FW elements. To keep built-in arrays compatible during transition period, add tx shape v0 for old format. The v0 fields can be removed along with built-in arrays once transition period ends. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712034506.53209-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 9 +++++++++ drivers/net/wireless/realtek/rtw89/rtw8851b_table.c | 8 ++++---- drivers/net/wireless/realtek/rtw89/rtw8852b_table.c | 4 ++-- drivers/net/wireless/realtek/rtw89/rtw8852c_table.c | 4 ++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f554e2ca1d1c..45e77d84f3b8 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4923,6 +4923,9 @@ struct rtw89_txpwr_rule_6ghz { struct rtw89_tx_shape { const u8 (*lmt)[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM]; const u8 (*lmt_ru)[RTW89_BAND_NUM][RTW89_REGD_NUM]; + + const u8 (*lmt_v0)[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM]; + const u8 (*lmt_ru_v0)[RTW89_BAND_NUM][RTW89_REGD_NUM]; }; struct rtw89_rfe_parms { @@ -8458,7 +8461,13 @@ static inline u8 rtw89_get_tx_shape_idx(struct rtw89_dev *rtwdev, u8 band, if (unlikely(rs >= RTW89_RS_TX_SHAPE_NUM)) rs = RTW89_RS_OFDM; + if (!tx_shape->lmt) + goto v0; + return (*tx_shape->lmt)[band][rs][regd]; + +v0: + return (*tx_shape->lmt_v0)[band][rs][regd]; } static inline void rtw89_ctrl_btg_bt_rx(struct rtw89_dev *rtwdev, bool en, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c b/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c index b8105f6e94f1..f875beaec555 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b_table.c @@ -14887,8 +14887,8 @@ const struct rtw89_rfe_parms rtw89_8851b_dflt_parms = { .lmt_ru = &rtw89_8851b_txpwr_lmt_ru_5g, }, .tx_shape = { - .lmt = &rtw89_8851b_tx_shape_lmt, - .lmt_ru = &rtw89_8851b_tx_shape_lmt_ru, + .lmt_v0 = &rtw89_8851b_tx_shape_lmt, + .lmt_ru_v0 = &rtw89_8851b_tx_shape_lmt_ru, }, }; @@ -14903,8 +14903,8 @@ static const struct rtw89_rfe_parms rtw89_8851b_rfe_parms_type2 = { .lmt_ru = &rtw89_8851b_txpwr_lmt_ru_5g_type2, }, .tx_shape = { - .lmt = &rtw89_8851b_tx_shape_lmt, - .lmt_ru = &rtw89_8851b_tx_shape_lmt_ru, + .lmt_v0 = &rtw89_8851b_tx_shape_lmt, + .lmt_ru_v0 = &rtw89_8851b_tx_shape_lmt_ru, }, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c index 96b18e9095b3..1485a8fc4bde 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_table.c @@ -22922,7 +22922,7 @@ const struct rtw89_rfe_parms rtw89_8852b_dflt_parms = { .lmt_ru = &rtw89_8852b_txpwr_lmt_ru_5g, }, .tx_shape = { - .lmt = &rtw89_8852b_tx_shape_lmt, - .lmt_ru = &rtw89_8852b_tx_shape_lmt_ru, + .lmt_v0 = &rtw89_8852b_tx_shape_lmt, + .lmt_ru_v0 = &rtw89_8852b_tx_shape_lmt_ru, }, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c index b4cf497e8524..c5ede63e7e23 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_table.c @@ -57154,7 +57154,7 @@ const struct rtw89_rfe_parms rtw89_8852c_dflt_parms = { .lmt_ru = &rtw89_8852c_txpwr_lmt_ru_6g, }, .tx_shape = { - .lmt = &rtw89_8852c_tx_shape_lmt, - .lmt_ru = &rtw89_8852c_tx_shape_lmt_ru, + .lmt_v0 = &rtw89_8852c_tx_shape_lmt, + .lmt_ru_v0 = &rtw89_8852c_tx_shape_lmt_ru, }, }; From b4eaac15cbb4ae90130bdde9952d2e50deeb40c3 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Sun, 12 Jul 2026 11:45:04 +0800 Subject: [PATCH 117/403] wifi: rtw89: extend tx shape format for regulatory 6 GHz power type Even under the same regulation, TX shape may need different settings for different 6 GHz power types. So, add one more dimension for that. Because TX shape parameters are not quite large, the 2/5/6 GHz sections are not divided into different structures. So, the 2/5 GHz sections will also get the new dimension. To 2/5 GHz sections, fill the TX shape settings with RTW89_REG_6GHZ_POWER_DFLT (0) field. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712034506.53209-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 17 ++++++++++++----- drivers/net/wireless/realtek/rtw89/fw.c | 16 ++++++++++++++-- drivers/net/wireless/realtek/rtw89/fw.h | 2 ++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 45e77d84f3b8..1ecaa14b790e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4921,8 +4921,9 @@ struct rtw89_txpwr_rule_6ghz { }; struct rtw89_tx_shape { - const u8 (*lmt)[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM]; - const u8 (*lmt_ru)[RTW89_BAND_NUM][RTW89_REGD_NUM]; + const u8 (*lmt)[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM] + [NUM_OF_RTW89_REG_6GHZ_POWER]; + const u8 (*lmt_ru)[RTW89_BAND_NUM][RTW89_REGD_NUM][NUM_OF_RTW89_REG_6GHZ_POWER]; const u8 (*lmt_v0)[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM]; const u8 (*lmt_ru_v0)[RTW89_BAND_NUM][RTW89_REGD_NUM]; @@ -5019,12 +5020,13 @@ struct rtw89_txpwr_lmt_ru_6ghz_data { struct rtw89_tx_shape_lmt_data { struct rtw89_txpwr_conf conf; - u8 v[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM]; + u8 v[RTW89_BAND_NUM][RTW89_RS_TX_SHAPE_NUM][RTW89_REGD_NUM] + [NUM_OF_RTW89_REG_6GHZ_POWER]; }; struct rtw89_tx_shape_lmt_ru_data { struct rtw89_txpwr_conf conf; - u8 v[RTW89_BAND_NUM][RTW89_REGD_NUM]; + u8 v[RTW89_BAND_NUM][RTW89_REGD_NUM][NUM_OF_RTW89_REG_6GHZ_POWER]; }; struct rtw89_rfe_data { @@ -8454,8 +8456,10 @@ static inline u8 rtw89_regd_get(struct rtw89_dev *rtwdev, u8 band) static inline u8 rtw89_get_tx_shape_idx(struct rtw89_dev *rtwdev, u8 band, enum rtw89_rate_section rs) { + struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; const struct rtw89_tx_shape *tx_shape = &rfe_parms->tx_shape; + u8 reg6_pwr = regulatory->reg_6ghz_power; u8 regd = rtw89_regd_get(rtwdev, band); if (unlikely(rs >= RTW89_RS_TX_SHAPE_NUM)) @@ -8464,7 +8468,10 @@ static inline u8 rtw89_get_tx_shape_idx(struct rtw89_dev *rtwdev, u8 band, if (!tx_shape->lmt) goto v0; - return (*tx_shape->lmt)[band][rs][regd]; + if (band != RTW89_BAND_6G) + reg6_pwr = RTW89_REG_6GHZ_POWER_DFLT; + + return (*tx_shape->lmt)[band][rs][regd][reg6_pwr]; v0: return (*tx_shape->lmt_v0)[band][rs][regd]; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index f290bcdd4fda..4df2ba5bfa44 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -11916,6 +11916,12 @@ fw_tx_shape_lmt_entry_valid(const struct rtw89_fw_tx_shape_lmt_entry *e, if (e->regd >= RTW89_REGD_NUM) return false; + /* ensure compatibility work with 2/5 GHz */ + static_assert(RTW89_REG_6GHZ_POWER_DFLT == 0); + + if (e->reg6_pwr >= NUM_OF_RTW89_REG_6GHZ_POWER) + return false; + return true; } @@ -11930,7 +11936,7 @@ void rtw89_fw_load_tx_shape_lmt(struct rtw89_tx_shape_lmt_data *data) if (!fw_tx_shape_lmt_entry_valid(&entry, cursor, conf)) continue; - data->v[entry.band][entry.tx_shape_rs][entry.regd] = entry.v; + data->v[entry.band][entry.tx_shape_rs][entry.regd][entry.reg6_pwr] = entry.v; } } @@ -11947,6 +11953,12 @@ fw_tx_shape_lmt_ru_entry_valid(const struct rtw89_fw_tx_shape_lmt_ru_entry *e, if (e->regd >= RTW89_REGD_NUM) return false; + /* ensure compatibility work with 2/5 GHz */ + static_assert(RTW89_REG_6GHZ_POWER_DFLT == 0); + + if (e->reg6_pwr >= NUM_OF_RTW89_REG_6GHZ_POWER) + return false; + return true; } @@ -11961,7 +11973,7 @@ void rtw89_fw_load_tx_shape_lmt_ru(struct rtw89_tx_shape_lmt_ru_data *data) if (!fw_tx_shape_lmt_ru_entry_valid(&entry, cursor, conf)) continue; - data->v[entry.band][entry.regd] = entry.v; + data->v[entry.band][entry.regd][entry.reg6_pwr] = entry.v; } } diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index c6131778f6a2..166c4bc9c1d0 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -5812,6 +5812,7 @@ struct rtw89_fw_tx_shape_lmt_entry { u8 tx_shape_rs; u8 regd; u8 v; + u8 reg6_pwr; } __packed; /* must consider compatibility; don't insert new in the mid */ @@ -5819,6 +5820,7 @@ struct rtw89_fw_tx_shape_lmt_ru_entry { u8 band; u8 regd; u8 v; + u8 reg6_pwr; } __packed; const struct rtw89_rfe_parms * From b5bf2dc96f95a34378df32ed0e271875c5242908 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sun, 12 Jul 2026 11:45:05 +0800 Subject: [PATCH 118/403] wifi: rtw89: fw: do bb_preinit before downloading firmware The firmware access BB registers while initialization, so driver should do bb_preinit before downloading firmware. Otherwise, it might get BB IO stuck and throw error. rtw89_8922de 0000:04:00.0: loaded firmware rtw89/rtw8922d_fw.bin rtw89_8922de 0000:04:00.0: Firmware version 0.35.111.7 (51c56e7b), cmd version 1, type 14 rtw89_8922de 0000:04:00.0: Firmware version 0.35.111.7 (51c56e7b), cmd version 1, type 15 rtw89_8922de 0000:04:00.0: fw unexpected status 6 rtw89_8922de 0000:04:00.0: download firmware fail rtw89_8922de 0000:04:00.0: [ERR]fwdl 0x1E0 = 0x8000012 rtw89_8922de 0000:04:00.0: [ERR]fwdl 0x78F0 = 0x290900 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]fw PC = 0x201445f2 rtw89_8922de 0000:04:00.0: [ERR]H2C path ready Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712034506.53209-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 12 +++++++++++- drivers/net/wireless/realtek/rtw89/mac.c | 7 ++++--- drivers/net/wireless/realtek/rtw89/mac.h | 3 ++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 30a8e337498e..ffb39440a254 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -7200,9 +7200,19 @@ int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + const struct rtw89_chip_info *chip = rtwdev->chip; + bool bb_preinit = false; int ret; - ret = rtw89_mac_partial_init(rtwdev, false); + /* + * Normally in probe stage, we don't need to do BB pre-initialization. + * However, RTL8922D firmware can access BB registers at firmware + * initial state, so initialize it to avoid BB IO stuck in firmware. + */ + if (chip->chip_id == RTL8922D) + bb_preinit = true; + + ret = rtw89_mac_partial_init(rtwdev, false, bb_preinit); if (ret) return ret; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index d8f0add7e1b0..db9fca828829 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4308,13 +4308,14 @@ int rtw89_mac_disable_bb_rf(struct rtw89_dev *rtwdev) } EXPORT_SYMBOL(rtw89_mac_disable_bb_rf); -int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb) +int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb, + bool bb_preinit) { int ret; rtw89_mac_ctrl_hci_dma_trx(rtwdev, true); - if (include_bb) { + if (include_bb || bb_preinit) { /* Only call BB preinit including configuration of BB MCU for * the chips which need to download BB MCU firmware. Otherwise, * calling preinit later to prevent touching registers affecting @@ -4367,7 +4368,7 @@ int rtw89_mac_init(struct rtw89_dev *rtwdev) bool include_bb = !!chip->bbmcu_nr; int ret; - ret = rtw89_mac_partial_init(rtwdev, include_bb); + ret = rtw89_mac_partial_init(rtwdev, include_bb, include_bb); if (ret) goto fail; diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index c05f5ee0d2fd..493d2b9626a6 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1302,7 +1302,8 @@ rtw89_write32_port_set(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev); void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev); -int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb); +int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb, + bool bb_preinit); int rtw89_mac_preinit(struct rtw89_dev *rtwdev); int rtw89_mac_init(struct rtw89_dev *rtwdev); int rtw89_mac_dle_init(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode, From 3c15399ef64e89270b8c4cddb857e2f8886cb140 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Sun, 12 Jul 2026 11:45:06 +0800 Subject: [PATCH 119/403] wifi: rtw89: debug: add diagnosis for RF Add debugfs diag_rf and show RFK (RF calibration) diagnosis things for now. Record channel related info before triggering RFK, and then record state of each kind of RFK from C2H event report. Besides, in track work, monitor TSSI status too. Both support history up to 10, and show records via debugfs. The following is an example of output. RFK (next index: 2) PHY-X = 0 0 0 0 0 0 0 0 0 0 S0-CH = 0012a 0012a 00000 00000 00000 00000 00000 00000 00000 00000 S0-CV = 0032c 0032c 00000 00000 00000 00000 00000 00000 00000 00000 S0-C5 = 10000 10000 00000 00000 00000 00000 00000 00000 00000 00000 S1-CH = 0012a 0012a 00000 00000 00000 00000 00000 00000 00000 00000 S1-CV = 0032d 0032d 00000 00000 00000 00000 00000 00000 00000 00000 S1-C5 = 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 PRE_NTFY = 0 0 0 0 0 0 0 0 0 0 TSSI = 1 1 0 0 0 0 0 0 0 0 IQK = 1 1 0 0 0 0 0 0 0 0 DPK = 1 1 0 0 0 0 0 0 0 0 TXGAPK = 1 1 0 0 0 0 0 0 0 0 DACK = 0 0 0 0 0 0 0 0 0 0 RX_DCK = 1 1 0 0 0 0 0 0 0 0 TX_IQK = 1 1 0 0 0 0 0 0 0 0 CIM3k = 1 1 0 0 0 0 0 0 0 0 TSSI-track (next index: 6) S0 = 00e 00e 00c 00d 00d 00e 00d 00d 00d 00e S1 = 00a 00b 009 00a 00a 00a 009 009 009 00a Debugfs diag_rf can also be used to manually trigger RFK when written by 1. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260712034506.53209-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 71 ++++++++++++- drivers/net/wireless/realtek/rtw89/core.h | 32 ++++++ drivers/net/wireless/realtek/rtw89/debug.c | 115 +++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.c | 62 +++++++++-- drivers/net/wireless/realtek/rtw89/phy.h | 3 + drivers/net/wireless/realtek/rtw89/reg.h | 1 + 6 files changed, 272 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index ffb39440a254..b4b0a451fed7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -486,6 +486,37 @@ void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev) __rtw89_core_set_chip_txpwr(rtwdev, conf.chans[1], RTW89_PHY_1); } +static void rtw89_core_rfk_record(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link, + bool start) +{ + struct rtw89_rfk_wait_info *info = &rtwdev->rfk_wait; + struct rtw89_rfk_record *record = &info->records[info->record_idx]; + const struct rtw89_chip_info *chip = rtwdev->chip; + u8 path_num = min(chip->rf_path_num, RTW89_RFK_RECORD_PATH_NR); + int path; + + if (!start) + goto end; + + info->record_ptr = record; + record->phy_idx = rtwvif_link->phy_idx; + + for (path = 0; path < path_num; path++) { + record->ch[path] = rtw89_read_rf(rtwdev, path, RR_CFGCH, + RR_CFGCH_BAND0 | RR_CFGCH_CH); + record->cv[path] = rtw89_read_rf(rtwdev, path, RR_VCO, + RR_VCO_SEL | RR_VCO_STS); + record->c5[path] = rtw89_read_rf(rtwdev, path, RR_SYNFB, RFREG_MASK); + } + + return; + +end: + info->record_idx = (info->record_idx + 1) % RTW89_RFK_RECORD_HISTORY_NR; + info->record_ptr = NULL; +} + void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { @@ -503,9 +534,14 @@ void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev, rtw89_set_channel(rtwdev); } - if (chip->ops->rfk_channel) + if (chip->ops->rfk_channel) { + rtw89_core_rfk_record(rtwdev, rtwvif_link, true); + chip->ops->rfk_channel(rtwdev, rtwvif_link); + rtw89_core_rfk_record(rtwdev, rtwvif_link, false); + } + if (prehdl_link) { rtw89_entity_force_hw(rtwdev, RTW89_PHY_NUM); rtw89_set_channel(rtwdev); @@ -5274,6 +5310,37 @@ static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev, } } +static void rtw89_core_rfk_tssi_record(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + u8 path_num = min(chip->rf_path_num, RTW89_RFK_RECORD_PATH_NR); + struct rtw89_rfk_wait_info *info = &rtwdev->rfk_wait; + int i = info->record_tssi_idx; + u32 base; + u32 mask; + int path; + + if (chip->chip_gen == RTW89_CHIP_AX) + return; + + info->record_tssi_idx = (info->record_tssi_idx + 1) % + RTW89_RFK_RECORD_HISTORY_NR; + + if (chip->chip_id == RTL8922A) { + base = 0x2ee10; + mask = 0x7fc00; + } else { + base = 0x2f918; + mask = 0x000001ff; + } + + for (path = 0; path < path_num; path++) { + u32 addr = base + 0x100 * path; + + info->tssi_code[i][path] = rtw89_read32_mask(rtwdev, addr, mask); + } +} + static void rtw89_core_rfk_track(struct rtw89_dev *rtwdev) { enum rtw89_entity_mode mode; @@ -5283,6 +5350,8 @@ static void rtw89_core_rfk_track(struct rtw89_dev *rtwdev) return; rtw89_chip_rfk_track(rtwdev); + + rtw89_core_rfk_tssi_record(rtwdev); } void rtw89_core_update_p2p_ps(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 1ecaa14b790e..c0795e0d10cd 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6134,11 +6134,43 @@ enum rtw89_rfk_report_state { RTW89_RFK_STATE_H2C_CMD_ERR = 0x4, }; +enum rtw89_rfk_report_types { + RTW89_RFK_REPORT_PRE_NTFY, + RTW89_RFK_REPORT_TSSI, + RTW89_RFK_REPORT_IQK, + RTW89_RFK_REPORT_DPK, + RTW89_RFK_REPORT_TXGAPK, + RTW89_RFK_REPORT_DACK, + RTW89_RFK_REPORT_RX_DCK, + RTW89_RFK_REPORT_TX_IQK, + RTW89_RFK_REPORT_CIM3k, + + NUM_OF_RTW89_RFK_REPORT_TYPES, +}; + +#define RTW89_RFK_RECORD_PATH_NR 2 +#define RTW89_RFK_RECORD_HISTORY_NR 10 + +struct rtw89_rfk_record { + u32 ch[RTW89_RFK_RECORD_PATH_NR]; + u32 cv[RTW89_RFK_RECORD_PATH_NR]; + u32 c5[RTW89_RFK_RECORD_PATH_NR]; + enum rtw89_phy_idx phy_idx; + enum rtw89_rfk_report_state states[NUM_OF_RTW89_RFK_REPORT_TYPES]; +}; + struct rtw89_rfk_wait_info { struct completion completion; ktime_t start_time; enum rtw89_rfk_report_state state; u8 version; + + int record_idx; + struct rtw89_rfk_record *record_ptr; + struct rtw89_rfk_record records[RTW89_RFK_RECORD_HISTORY_NR]; + + int record_tssi_idx; + u32 tssi_code[RTW89_RFK_RECORD_HISTORY_NR][RTW89_RFK_RECORD_PATH_NR]; }; #define RTW89_DACK_PATH_NR 2 diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 8b41991963e8..7e640c7166e4 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -93,6 +93,7 @@ struct rtw89_debugfs { struct rtw89_debugfs_priv beacon_info; struct rtw89_debugfs_priv diag_mac; struct rtw89_debugfs_priv diag_bb; + struct rtw89_debugfs_priv diag_rf; struct rtw89_debugfs_priv monitor_opts; }; @@ -5393,6 +5394,118 @@ rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev, return p - buf; } +static ssize_t +rtw89_debug_priv_diag_rf_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_rfk_wait_info *info = &rtwdev->rfk_wait; + char *p = buf, *end = buf + bufsz; + int i, j; + + p += scnprintf(p, end - p, "RFK (next index: %d)\n", info->record_idx); + + p += scnprintf(p, end - p, " PHY-X ="); + for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++) { + struct rtw89_rfk_record *record = &info->records[i]; + + p += scnprintf(p, end - p, " %4d ", record->phy_idx); + } + p += scnprintf(p, end - p, "\n"); + + for (j = 0; j < RTW89_RFK_RECORD_PATH_NR; j++) { + p += scnprintf(p, end - p, " S%d-CH =", j); + for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++) { + struct rtw89_rfk_record *record = &info->records[i]; + + p += scnprintf(p, end - p, " %05x", record->ch[j]); + } + p += scnprintf(p, end - p, "\n"); + + p += scnprintf(p, end - p, " S%d-CV =", j); + for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++) { + struct rtw89_rfk_record *record = &info->records[i]; + + p += scnprintf(p, end - p, " %05x", record->cv[j]); + } + p += scnprintf(p, end - p, "\n"); + + p += scnprintf(p, end - p, " S%d-C5 =", j); + for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++) { + struct rtw89_rfk_record *record = &info->records[i]; + + p += scnprintf(p, end - p, " %05x", record->c5[j]); + } + p += scnprintf(p, end - p, "\n"); + } + + for (j = 0; j < NUM_OF_RTW89_RFK_REPORT_TYPES; j++) { + p += scnprintf(p, end - p, " %-8s =", rtw89_rfk_report_names[j]); + for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++) { + struct rtw89_rfk_record *record = &info->records[i]; + + p += scnprintf(p, end - p, " %4d ", record->states[j]); + } + p += scnprintf(p, end - p, "\n"); + } + + p += scnprintf(p, end - p, "TSSI-track (next index: %d)\n", info->record_tssi_idx); + + for (j = 0; j < RTW89_RFK_RECORD_PATH_NR; j++) { + p += scnprintf(p, end - p, " S%d =", j); + for (i = 0; i < RTW89_RFK_RECORD_HISTORY_NR; i++) + p += scnprintf(p, end - p, " %03x", info->tssi_code[i][j]); + p += scnprintf(p, end - p, "\n"); + } + + return p - buf; +} + +static void rtw89_dbg_diag_rf_set_rfk(struct rtw89_dev *rtwdev) +{ + struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt; + struct rtw89_vif_link *rtwvif_link; + struct rtw89_vif *rtwvif; + unsigned int link_id; + + list_for_each_entry(rtwvif, &mgnt->active_list, mgnt_entry) + rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) + rtw89_chip_rfk_channel(rtwdev, rtwvif_link); +} + +enum rtw89_dbg_diag_rf_type { + RTW89_DBG_DIAG_RF_RSVD = 0, + RTW89_DBG_DIAG_RF_RFK = 1, +}; + +static ssize_t +rtw89_debug_priv_diag_rf_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) +{ + u8 type; + int ret; + + ret = kstrtou8(buf, 0, &type); + if (ret) + return -EINVAL; + + if (rtwdev->scanning) + rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif); + + rtw89_leave_ps_mode(rtwdev); + + switch (type) { + case RTW89_DBG_DIAG_RF_RFK: + rtw89_dbg_diag_rf_set_rfk(rtwdev); + break; + default: + return -EOPNOTSUPP; + } + + return count; +} + static ssize_t rtw89_debug_priv_monitor_opts_get(struct rtw89_dev *rtwdev, struct rtw89_debugfs_priv *debugfs_priv, @@ -5505,6 +5618,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = { .beacon_info = rtw89_debug_priv_get(beacon_info), .diag_mac = rtw89_debug_priv_get(diag_mac, RSIZE_16K, RLOCK), .diag_bb = rtw89_debug_priv_get(diag_bb, RSIZE_8K, RLOCK), + .diag_rf = rtw89_debug_priv_set_and_get(diag_rf, RWLOCK), .monitor_opts = rtw89_debug_priv_set_and_get(monitor_opts, RWLOCK), }; @@ -5562,6 +5676,7 @@ void rtw89_debugfs_add_sec2(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_r(beacon_info); rtw89_debugfs_add_r(diag_mac); rtw89_debugfs_add_r(diag_bb); + rtw89_debugfs_add_rw(diag_rf); rtw89_debugfs_add_rw(monitor_opts); } diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 30d5741da87e..c78107e92206 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -3968,6 +3968,23 @@ void (* const rtw89_phy_c2h_rfk_log_handler[])(struct rtw89_dev *rtwdev, [RTW89_PHY_C2H_RFK_LOG_FUNC_CIM3K] = rtw89_phy_c2h_rfk_log_cim3k, }; +#define NAME_RTW89_RFK_REPORT(type) \ + [RTW89_RFK_REPORT_ ## type] = #type + +const char * const rtw89_rfk_report_names[] = { + NAME_RTW89_RFK_REPORT(PRE_NTFY), + NAME_RTW89_RFK_REPORT(TSSI), + NAME_RTW89_RFK_REPORT(IQK), + NAME_RTW89_RFK_REPORT(DPK), + NAME_RTW89_RFK_REPORT(TXGAPK), + NAME_RTW89_RFK_REPORT(DACK), + NAME_RTW89_RFK_REPORT(RX_DCK), + NAME_RTW89_RFK_REPORT(TX_IQK), + NAME_RTW89_RFK_REPORT(CIM3k), +}; + +static_assert(ARRAY_SIZE(rtw89_rfk_report_names) == NUM_OF_RTW89_RFK_REPORT_TYPES); + static void rtw89_phy_rfk_report_prep(struct rtw89_dev *rtwdev) { @@ -3979,8 +3996,8 @@ void rtw89_phy_rfk_report_prep(struct rtw89_dev *rtwdev) } static -int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name, - unsigned int ms) +int ____rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name, + unsigned int ms) { struct rtw89_rfk_wait_info *wait = &rtwdev->rfk_wait; unsigned long time_left; @@ -4017,6 +4034,29 @@ int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name, return 0; } +static +int __rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, + enum rtw89_rfk_report_types rfk_type, + enum rtw89_phy_idx phy_idx, + const struct rtw89_chan *chan, + unsigned int ms) +{ + const char *rfk_name = rtw89_rfk_report_names[rfk_type]; + struct rtw89_rfk_wait_info *wait = &rtwdev->rfk_wait; + struct rtw89_rfk_record *record = wait->record_ptr; + int ret; + + ret = ____rtw89_phy_rfk_report_wait(rtwdev, rfk_name, ms); + + if (record) + record->states[rfk_type] = wait->state; + + return ret; +} + +#define rtw89_phy_rfk_report_wait(rtwdev, type, phy_idx, chan, ms) \ + __rtw89_phy_rfk_report_wait(rtwdev, RTW89_RFK_REPORT_ ## type, phy_idx, chan, ms) + static void rtw89_phy_c2h_rfk_report_state(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) { @@ -4122,7 +4162,7 @@ int rtw89_phy_rfk_pre_ntfy_and_wait(struct rtw89_dev *rtwdev, if (RTW89_CHK_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, &rtwdev->fw)) { rtw89_phy_rfk_report_prep(rtwdev); rtw89_fw_h2c_rf_pre_ntfy(rtwdev, phy_idx); - ret = rtw89_phy_rfk_report_wait(rtwdev, "PRE_NTFY", ms); + ret = rtw89_phy_rfk_report_wait(rtwdev, PRE_NTFY, phy_idx, NULL, ms); if (ret) return ret; } @@ -4152,7 +4192,7 @@ int rtw89_phy_rfk_tssi_and_wait(struct rtw89_dev *rtwdev, if (ret) return ret; - return rtw89_phy_rfk_report_wait(rtwdev, "TSSI", ms); + return rtw89_phy_rfk_report_wait(rtwdev, TSSI, phy_idx, chan, ms); } EXPORT_SYMBOL(rtw89_phy_rfk_tssi_and_wait); @@ -4169,7 +4209,7 @@ int rtw89_phy_rfk_iqk_and_wait(struct rtw89_dev *rtwdev, if (ret) return ret; - return rtw89_phy_rfk_report_wait(rtwdev, "IQK", ms); + return rtw89_phy_rfk_report_wait(rtwdev, IQK, phy_idx, chan, ms); } EXPORT_SYMBOL(rtw89_phy_rfk_iqk_and_wait); @@ -4186,7 +4226,7 @@ int rtw89_phy_rfk_dpk_and_wait(struct rtw89_dev *rtwdev, if (ret) return ret; - return rtw89_phy_rfk_report_wait(rtwdev, "DPK", ms); + return rtw89_phy_rfk_report_wait(rtwdev, DPK, phy_idx, chan, ms); } EXPORT_SYMBOL(rtw89_phy_rfk_dpk_and_wait); @@ -4203,7 +4243,7 @@ int rtw89_phy_rfk_txgapk_and_wait(struct rtw89_dev *rtwdev, if (ret) return ret; - return rtw89_phy_rfk_report_wait(rtwdev, "TXGAPK", ms); + return rtw89_phy_rfk_report_wait(rtwdev, TXGAPK, phy_idx, chan, ms); } EXPORT_SYMBOL(rtw89_phy_rfk_txgapk_and_wait); @@ -4220,7 +4260,7 @@ int rtw89_phy_rfk_dack_and_wait(struct rtw89_dev *rtwdev, if (ret) return ret; - return rtw89_phy_rfk_report_wait(rtwdev, "DACK", ms); + return rtw89_phy_rfk_report_wait(rtwdev, DACK, phy_idx, chan, ms); } EXPORT_SYMBOL(rtw89_phy_rfk_dack_and_wait); @@ -4237,7 +4277,7 @@ int rtw89_phy_rfk_rxdck_and_wait(struct rtw89_dev *rtwdev, if (ret) return ret; - return rtw89_phy_rfk_report_wait(rtwdev, "RX_DCK", ms); + return rtw89_phy_rfk_report_wait(rtwdev, RX_DCK, phy_idx, chan, ms); } EXPORT_SYMBOL(rtw89_phy_rfk_rxdck_and_wait); @@ -4254,7 +4294,7 @@ int rtw89_phy_rfk_txiqk_and_wait(struct rtw89_dev *rtwdev, if (ret) return ret; - return rtw89_phy_rfk_report_wait(rtwdev, "TX_IQK", ms); + return rtw89_phy_rfk_report_wait(rtwdev, TX_IQK, phy_idx, chan, ms); } EXPORT_SYMBOL(rtw89_phy_rfk_txiqk_and_wait); @@ -4271,7 +4311,7 @@ int rtw89_phy_rfk_cim3k_and_wait(struct rtw89_dev *rtwdev, if (ret) return ret; - return rtw89_phy_rfk_report_wait(rtwdev, "CIM3k", ms); + return rtw89_phy_rfk_report_wait(rtwdev, CIM3k, phy_idx, chan, ms); } EXPORT_SYMBOL(rtw89_phy_rfk_cim3k_and_wait); diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index b0c82bac72f0..0963cdfb1594 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -1150,6 +1150,9 @@ void rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev, bool rtw89_phy_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func); void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func); + +extern const char * const rtw89_rfk_report_names[]; + int rtw89_phy_rfk_pre_ntfy_and_wait(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, unsigned int ms); diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 4d2117de798b..3bf2865b81ab 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8763,6 +8763,7 @@ #define RR_VCO 0xb2 #define RR_VCO_VAL GENMASK(18, 14) #define RR_VCO_SEL GENMASK(9, 8) +#define RR_VCO_STS GENMASK(7, 0) #define RR_VCI 0xb3 #define RR_VCI_ON BIT(7) #define RR_LPF 0xb7 From 73aecc221e7df482b2dcf1a643e840ffce1b83c3 Mon Sep 17 00:00:00 2001 From: GuoHan Zhao Date: Wed, 15 Jul 2026 14:08:13 +0800 Subject: [PATCH 120/403] wifi: rtw89: wow: fix unsupported cipher debug messages Correct two WoWLAN debug messages to say "unsupported cipher". Signed-off-by: GuoHan Zhao Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260715060813.476245-1-zhaoguohan@kylinos.cn --- drivers/net/wireless/realtek/rtw89/wow.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index a7539f91264d..d6bf5cd8be3b 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -358,7 +358,7 @@ static void rtw89_wow_get_key_info_iter(struct ieee80211_hw *hw, key_info->gtk_keyidx = key->keyidx; break; default: - rtw89_debug(rtwdev, RTW89_DBG_WOW, "unsupport cipher %x\n", + rtw89_debug(rtwdev, RTW89_DBG_WOW, "unsupported cipher %x\n", key->cipher); goto err; } @@ -443,7 +443,7 @@ static void rtw89_wow_set_key_info_iter(struct ieee80211_hw *hw, case WLAN_CIPHER_SUITE_WEP104: break; default: - rtw89_debug(rtwdev, RTW89_DBG_WOW, "unsupport cipher %x\n", + rtw89_debug(rtwdev, RTW89_DBG_WOW, "unsupported cipher %x\n", key->cipher); goto err; } From 730dbda6dc70d29180eb2a7e9fa36823838bb042 Mon Sep 17 00:00:00 2001 From: Chia-Yuan Li Date: Tue, 14 Jul 2026 15:48:11 +0800 Subject: [PATCH 121/403] wifi: rtw89: fw: use MAC source for IO offload delay command The udelay/mdelay helpers set the command source to RTW89_FW_CMD_OFLD_SRC_OTHER (4), which does not fit the two-bit field RTW89_H2C_CMD_OFLD_W0_SRC (GENMASK(1, 0)). The le32_encode_bits() masks it down to 0 (RTW89_FW_CMD_OFLD_SRC_BB), and compiler throws __field_overflow() error. Fortunately it still works because firmware ignores the source field for a delay command. Use RTW89_FW_CMD_OFLD_SRC_MAC as the vendor driver does, and drop the unused RTW89_FW_CMD_OFLD_SRC_OTHER enumerator. Reported-by: Bitterblue Smith Closes: https://github.com/morrownr/rtw89/issues/111 Fixes: ae3d327515f2 ("wifi: rtw89: add IO offload support via firmware") Signed-off-by: Chia-Yuan Li Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260714074811.30124-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 4 ++-- drivers/net/wireless/realtek/rtw89/fw.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 4df2ba5bfa44..0db77120298f 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -12383,7 +12383,7 @@ static void rtw89_fw_cmd_ofld_write_rf(struct rtw89_dev *rtwdev, static void rtw89_fw_cmd_ofld_udelay(struct rtw89_dev *rtwdev, u32 us) { struct rtw89_fw_cmd_ofld_arg cmd = { - .src = RTW89_FW_CMD_OFLD_SRC_OTHER, + .src = RTW89_FW_CMD_OFLD_SRC_MAC, .type = RTW89_FW_CMD_OFLD_DELAY, .value = us, }; @@ -12397,7 +12397,7 @@ static void rtw89_fw_cmd_ofld_udelay(struct rtw89_dev *rtwdev, u32 us) static void rtw89_fw_cmd_ofld_mdelay(struct rtw89_dev *rtwdev, u32 ms) { struct rtw89_fw_cmd_ofld_arg cmd = { - .src = RTW89_FW_CMD_OFLD_SRC_OTHER, + .src = RTW89_FW_CMD_OFLD_SRC_MAC, .type = RTW89_FW_CMD_OFLD_DELAY, .value = ms * 1000, }; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 166c4bc9c1d0..a1aab8293c14 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3114,7 +3114,6 @@ enum rtw89_fw_cmd_ofld_arg_src { RTW89_FW_CMD_OFLD_SRC_RF, RTW89_FW_CMD_OFLD_SRC_MAC, RTW89_FW_CMD_OFLD_SRC_RF_DDIE, - RTW89_FW_CMD_OFLD_SRC_OTHER, }; enum rtw89_fw_cmd_ofld_arg_type { From c5cb9dd220ba9fdcf4ba485ce3d1d36ca32ac00b Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 17 Jul 2026 17:31:05 +0300 Subject: [PATCH 122/403] wifi: iwlwifi: mld: validate wake packet crypto overhead Wake packet parsing only accounted for FCS and missed per-key IV/ICV overhead for protected data frames. Prevent size underflow and bad packet trimming when notifications are malformed or truncated. Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260717172958.e06595623533.Ie09494b7e34e5872b750fd90e325648ee469d0da@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 63 +++++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index 3b785c53948f..301983428a63 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -59,6 +59,30 @@ struct iwl_mld_suspend_key_iter_data { __le32 bigtk_cipher; }; +struct iwl_mld_wake_pkt_iter_data { + bool multicast; + u32 ivlen; + u32 icvlen; +}; + +static void +iwl_mld_wake_pkt_key_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, void *_data) +{ + struct iwl_mld_wake_pkt_iter_data *data = _data; + bool is_group_key = !sta; + + /* ignore anything that is not a PTK / GTK */ + if (key->keyidx > 3) + return; + if (is_group_key != data->multicast) + return; + + data->ivlen = key->iv_len; + data->icvlen = key->icv_len; +} + struct iwl_mld_mcast_key_data { u8 key[WOWLAN_KEY_MAX_SIZE]; u8 len; @@ -743,11 +767,17 @@ iwl_mld_set_wake_packet(struct iwl_mld *mld, struct cfg80211_wowlan_wakeup *wakeup, struct sk_buff **_pkt) { - int pkt_bufsize = wowlan_status->wake_packet_bufsize; - int expected_pktlen = wowlan_status->wake_packet_length; + u32 pkt_bufsize = wowlan_status->wake_packet_bufsize; + u32 expected_pktlen = wowlan_status->wake_packet_length; const u8 *pktdata = wowlan_status->wake_packet; const struct ieee80211_hdr *hdr = (const void *)pktdata; - int truncated = expected_pktlen - pkt_bufsize; + u32 truncated; + + if (IWL_FW_CHECK(mld, pkt_bufsize < sizeof(*hdr), + "pkt_bufsize is too short: %u\n", pkt_bufsize)) + return; + + truncated = expected_pktlen - pkt_bufsize; if (ieee80211_is_data(hdr->frame_control)) { int hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -758,9 +788,18 @@ iwl_mld_set_wake_packet(struct iwl_mld *mld, if (!pkt) return; - skb_put_data(pkt, pktdata, hdrlen); - pktdata += hdrlen; - pkt_bufsize -= hdrlen; + if (ieee80211_has_protected(hdr->frame_control)) { + struct iwl_mld_wake_pkt_iter_data iter_data = { + .multicast = + is_multicast_ether_addr(hdr->addr1), + }; + + ieee80211_iter_keys(mld->hw, vif, + iwl_mld_wake_pkt_key_iter, + &iter_data); + ivlen = iter_data.ivlen; + icvlen += iter_data.icvlen; + } /* if truncated, FCS/ICV is (partially) gone */ if (truncated >= icvlen) { @@ -771,6 +810,13 @@ iwl_mld_set_wake_packet(struct iwl_mld *mld, truncated = 0; } + if (IWL_FW_CHECK(mld, pkt_bufsize <= hdrlen + ivlen + icvlen, + "pkt_bufsize is too small %u\n", pkt_bufsize)) + return; + + skb_put_data(pkt, pktdata, hdrlen); + pktdata += hdrlen; + pkt_bufsize -= hdrlen; pkt_bufsize -= ivlen + icvlen; pktdata += ivlen; @@ -792,6 +838,11 @@ iwl_mld_set_wake_packet(struct iwl_mld *mld, fcslen -= truncated; truncated = 0; } + + if (IWL_FW_CHECK(mld, pkt_bufsize <= fcslen, + "pkt_bufsize is too small %u\n", pkt_bufsize)) + return; + pkt_bufsize -= fcslen; wakeup->packet = wowlan_status->wake_packet; wakeup->packet_present_len = pkt_bufsize; From 6aa811062cd78ad961410a64db55694ff609da57 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 17 Jul 2026 17:31:06 +0300 Subject: [PATCH 123/403] wifi: iwlwifi: mld: initialize scan-abort status Initialize abort status before issuing the abort command so debug logging never reads an uninitialized value on error paths. Assisted-by: GitHubCopilot:GPT-5.3-Codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260717172958.9d804f466534.I4e10270bd1dde4a80940a47ef5d383729cc66cb1@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c index d80a1cfc2ed5..0faca402bc14 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -1681,7 +1681,8 @@ iwl_mld_scan_send_abort_cmd_status(struct iwl_mld *mld, int uid, u32 *status) static int iwl_mld_scan_abort(struct iwl_mld *mld, int type, int uid, bool *wait) { - enum iwl_umac_scan_abort_status status; + enum iwl_umac_scan_abort_status status = + IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND; int ret; *wait = true; From a790f60cc4d7dc64a2d7cadb94a3d9f60392bed4 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 17 Jul 2026 17:31:07 +0300 Subject: [PATCH 124/403] wifi: iwlwifi: mvm: ignore sync frames when sync is disabled Gate time-sync frame interception on the active flag so frames are not queued after time-sync teardown. Assisted-by: GitHubCopilot:GPT-5.3-Codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260717172958.ac73ee199a25.Ic1489244f9b02da93060f0a0e5b300a73527f265@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/time-sync.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.h b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.h index 2cfd0fb5e781..7bdbacb64078 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2022 Intel Corporation + * Copyright (C) 2026 Intel Corporation */ #ifndef __TIME_SYNC_H__ #define __TIME_SYNC_H__ @@ -19,6 +20,9 @@ int iwl_mvm_time_sync_config(struct iwl_mvm *mvm, const u8 *addr, static inline bool iwl_mvm_time_sync_frame(struct iwl_mvm *mvm, struct sk_buff *skb, u8 *addr) { + if (!mvm->time_sync.active) + return false; + if (ether_addr_equal(mvm->time_sync.peer_addr, addr) && (ieee80211_is_timing_measurement(skb) || ieee80211_is_ftm(skb))) { skb_queue_tail(&mvm->time_sync.frame_list, skb); From 4f5384b58b483e4aec576ce461dec45b41df18db Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Fri, 17 Jul 2026 17:31:08 +0300 Subject: [PATCH 125/403] wifi: iwlwifi: mld: drop connection on D3 resume failure When FW crashes on D3 exit, iwl_mld_nic_error() sets STATUS_RESET_PENDING and queues restart wk, but mac80211's resume callback synchronously calls iwl_trans_stop_device() which clears the flag. As a result restart wk skips sw_reset, and the FW error recovery buffer is never read. The new FW boots with empty BA state and initial sequence numbers, while the AP still holds its A-MPDU RX reorder window. This causes MPDUs to be dropped as IWL_RX_MPDU_REORDER_BA_OLD_SN until ADDBA is renegotiated. We don't know how long the firmware has been in an error state or whether the AP still considers us associated, so keeping the connection alive is not worth it. Call ieee80211_resume_disconnect() when iwl_mld_wait_d3_notif() fails, and let userspace reassociate. Signed-off-by: Ayala Beker Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260717172958.3e10c8498f53.Icf5644b42d79e984ecc16abfa873bd37f611e778@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index 301983428a63..55f9b809e764 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -2219,6 +2219,9 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld) ret = iwl_mld_wait_d3_notif(mld, &resume_data, true); if (ret) { IWL_ERR(mld, "Couldn't get the d3 notifs %d\n", ret); + + if (bss_vif->cfg.assoc) + ieee80211_resume_disconnect(bss_vif); goto err; } From 7e1d5ccac87ec618f393ec137743207282ef1af5 Mon Sep 17 00:00:00 2001 From: Avinash Bhatt Date: Fri, 17 Jul 2026 17:31:09 +0300 Subject: [PATCH 126/403] wifi: iwlwifi: fw: move SAR defines from acpi.h to regulatory.h IWL_SAR_ENABLE_MSK and IWL_REDUCE_POWER_FLAGS_POS describe the layout of the shared WRDS/SAR table format. They are not ACPI-specific: the same bit positions are used regardless of whether the data originates from ACPI, UEFI, or another BIOS source. IWL_SAR_ENABLE_MSK was already duplicated in regulatory.h; remove it from acpi.h to eliminate the duplication. Move IWL_REDUCE_POWER_FLAGS_POS to regulatory.h alongside IWL_SAR_ENABLE_MSK so that both SAR field descriptors live in the shared regulatory header, accessible to all BIOS configuration sources. No functional change. Signed-off-by: Avinash Bhatt Link: https://patch.msgid.link/20260717172958.32e5dcde4b90.I420c58b05ab6ab011c4c771ca9e4eb62740de549@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 5 +---- drivers/net/wireless/intel/iwlwifi/fw/regulatory.h | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 51a57e57de7a..45eb35ffb637 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023, 2025 Intel Corporation + * Copyright (C) 2018-2023, 2025-2026 Intel Corporation */ #ifndef __iwl_fw_acpi__ #define __iwl_fw_acpi__ @@ -111,9 +111,6 @@ #define ACPI_PPAG_WIFI_DATA_SIZE_V3 ((ACPI_PPAG_NUM_CHAINS * \ ACPI_PPAG_NUM_BANDS_V3) + 2) -#define IWL_SAR_ENABLE_MSK BIT(0) -#define IWL_REDUCE_POWER_FLAGS_POS 1 - /* The Inidcator whether UEFI WIFI GUID tables are locked is read from ACPI */ #define UEFI_WIFI_GUID_UNLOCKED 0 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index 6fffc032efd3..22c97c75b83c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2023-2025 Intel Corporation + * Copyright (C) 2023-2026 Intel Corporation */ #ifndef __fw_regulatory_h__ @@ -30,6 +30,7 @@ #define BIOS_GEO_MIN_PROFILE_NUM 3 #define IWL_SAR_ENABLE_MSK BIT(0) +#define IWL_REDUCE_POWER_FLAGS_POS 1 /* PPAG gain value bounds in 1/8 dBm */ #define IWL_PPAG_MIN_LB -16 From 340eafb30b35a600a66da333bb0c119a880b7062 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Fri, 17 Jul 2026 17:31:10 +0300 Subject: [PATCH 127/403] wifi: iwlwifi: mld: support update_mcc notification v2 New firmware will support version 2 of the update_mcc notification. The extra field is used for a new feature, but we does not support it. Keep the existing payload definition compatible with both versions and register version 2 in the MLD notification version table so the driver accepts the newer notification without changing the behavior. This preserves version 1 support and adds compatibility with firmware that sends version 2. Signed-off-by: Pagadala Yesu Anjaneyulu Link: https://patch.msgid.link/20260717172958.9c5a940d37dc.I955800c2377b802ffb99003349552cc4036ca4bd@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h | 5 ++++- drivers/net/wireless/intel/iwlwifi/mld/notif.c | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index 360b626a9572..9fbdb45e88db 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -411,7 +411,10 @@ struct iwl_mcc_chub_notif { __le16 mcc; u8 source_id; u8 reserved1; -} __packed; /* LAR_MCC_NOTIFY_S */ +} __packed; +/* LAR_MCC_NOTIFY_S_VER_1 + * LAR_MCC_NOTIFY_S_VER_2 + */ enum iwl_mcc_update_status { MCC_RESP_NEW_CHAN_PROFILE, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c index b3a899828db9..90733bbaf733 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c @@ -298,7 +298,8 @@ CMD_VERSIONS(channel_survey_notif, CMD_VERSIONS(mfuart_notif, CMD_VER_ENTRY(2, iwl_mfuart_load_notif)) CMD_VERSIONS(update_mcc, - CMD_VER_ENTRY(1, iwl_mcc_chub_notif)) + CMD_VER_ENTRY(1, iwl_mcc_chub_notif) + CMD_VER_ENTRY(2, iwl_mcc_chub_notif)) CMD_VERSIONS(session_prot_notif, CMD_VER_ENTRY(3, iwl_session_prot_notif)) CMD_VERSIONS(missed_beacon_notif, From ac8227e35ff7c8953a7d8adf7f5230127ad97aae Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Fri, 17 Jul 2026 17:31:11 +0300 Subject: [PATCH 128/403] wifi: iwlwifi: mld: add debug log after AP type command Add a radio debug trace when MCC_ALLOWED_AP_TYPE_CMD is sent successfully during AP type table initialization. This improves bring-up visibility without changing runtime behavior. Failures are still reported through the existing error log path. Signed-off-by: Pagadala Yesu Anjaneyulu Link: https://patch.msgid.link/20260717172958.18e1fc5ec109.I76dd832f62d00a8f358f8e4a705f25184ac53da2@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/regulatory.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c index 4a93ccbef495..be311f6c3d9f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -524,6 +524,8 @@ void iwl_mld_init_ap_type_tables(struct iwl_mld *mld) if (ret) IWL_ERR(mld, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n", ret); + else + IWL_DEBUG_RADIO(mld, "MCC_ALLOWED_AP_TYPE_CMD sent to FW\n"); } void iwl_mld_init_tas(struct iwl_mld *mld) From a47ab1b9c0827f5bd6717abb3f9e3f4f6eb5e00c Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Fri, 17 Jul 2026 17:31:12 +0300 Subject: [PATCH 129/403] wifi: iwlwifi: mld: move BIOS reading code to where it belongs We have a dedicated function to fetch all the BIOS tables when the opmode starts, and yet we read a couple of tables directly from iwl_op_mode_mld_start, which is already a large function that does multiple things. Move the reading of the sgom, puncturing, and RFI enablement to the dedicated iwl_mld_get_bios_tables. Link: https://patch.msgid.link/20260717172958.b19a33e0b507.I73f6b5e6a81d0f411f12589ceb30afa655c0a16b@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/mld.c | 2 -- drivers/net/wireless/intel/iwlwifi/mld/regulatory.c | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 7c49d0441fdf..6e991fb99ff3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -419,8 +419,6 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, iwl_mld_construct_fw_runtime(mld, trans, fw, dbgfs_dir); iwl_mld_get_bios_tables(mld); - iwl_uefi_get_sgom_table(trans, &mld->fwrt); - iwl_uefi_get_puncturing(&mld->fwrt); iwl_mld_hw_set_regulatory(mld); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c index be311f6c3d9f..ad899ce5c64a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -84,6 +84,9 @@ void iwl_mld_get_bios_tables(struct iwl_mld *mld) iwl_uefi_get_uneb_table(mld->trans, &mld->fwrt); iwl_bios_get_phy_filters(&mld->fwrt); + + iwl_uefi_get_sgom_table(mld->trans, &mld->fwrt); + iwl_uefi_get_puncturing(&mld->fwrt); } static int iwl_mld_geo_sar_init(struct iwl_mld *mld) From 01f41f5fd823fb70a2f28ea87f6cf85a268ef8b9 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Thu, 23 Jul 2026 14:23:36 +0300 Subject: [PATCH 130/403] wifi: iwlwifi: regulatory: add LARI v15 DSM support bitmap Firmware API v15 extends LARI config with the DSM function-0 support bitmap, and host must provide this information for regulatory handling. Without this, FW cannot use BIOS-reported DSM capability bits. Add oem_supported_dsm_bitmap to the host LARI config command definition and wire it into regulatory command construction. Populate it from DSM query data and keep backward compatibility by using the pre-v15 command size for command version 14. Behavior change: When DSM function-0 data is available, host includes it in the LARI config command sent to FW; older command versions remain compatible. Signed-off-by: Pagadala Yesu Anjaneyulu Link: https://patch.msgid.link/20260723142324.c576a8c7560b.I5cc1277d0ef31409f6a3364342a3f35cd9318ceb@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 3 ++- .../net/wireless/intel/iwlwifi/fw/api/nvm-reg.h | 5 +++++ drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 2 +- .../net/wireless/intel/iwlwifi/mld/regulatory.c | 15 +++++++++++++-- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 9f2f4a6af1ca..0b1e478fb7b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -181,6 +181,7 @@ static int iwl_acpi_load_dsm_values(struct iwl_fw_runtime *fwrt) fwrt->dsm_revision = ACPI_DSM_REV; fwrt->dsm_source = BIOS_SOURCE_ACPI; + fwrt->dsm_values[DSM_FUNC_QUERY] = (u32)query_func_val; IWL_DEBUG_RADIO(fwrt, "ACPI DSM validity bitmap 0x%x\n", (u32)query_func_val); @@ -246,7 +247,7 @@ int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, BUILD_BUG_ON(ARRAY_SIZE(fwrt->dsm_values) != DSM_FUNC_NUM_FUNCS); BUILD_BUG_ON(BITS_PER_TYPE(fwrt->dsm_funcs_valid) < DSM_FUNC_NUM_FUNCS); - if (WARN_ON(func >= ARRAY_SIZE(fwrt->dsm_values) || !func)) + if (WARN_ON(func >= ARRAY_SIZE(fwrt->dsm_values))) return -EINVAL; if (!(fwrt->dsm_funcs_valid & BIT(func))) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index 9fbdb45e88db..5f48e3b306eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -697,6 +697,8 @@ struct iwl_lari_config_change_cmd_v8 { * @wcpe_bitmap: bitmap of puncturing enablement per MCC * @bios_wbem_hdr: 320 MHz per-MCC WBEM config header * @reserved: reserved + * @oem_supported_dsm_bitmap: DSM function 0 bitmap describing supported + * DSM function indices */ struct iwl_lari_config_change_cmd { __le32 config_bitmap; @@ -720,10 +722,13 @@ struct iwl_lari_config_change_cmd { __le32 wcpe_bitmap; struct iwl_bios_config_hdr bios_wbem_hdr; __le32 reserved[10]; + /* since version 15 */ + __le32 oem_supported_dsm_bitmap; } __packed; /* LARI_CHANGE_CONF_CMD_S_VER_12 * LARI_CHANGE_CONF_CMD_S_VER_13 * LARI_CHANGE_CONF_CMD_S_VER_14 + * LARI_CHANGE_CONF_CMD_S_VER_15 */ /* Activate UNII-1 (5.2GHz) for World Wide */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 01e495e24ebc..f9187989c988 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -927,7 +927,7 @@ static int iwl_uefi_load_dsm_values(struct iwl_fw_runtime *fwrt) */ fwrt->dsm_funcs_valid |= BIT(DSM_FUNC_QUERY); - for (int func = 1; func < ARRAY_SIZE(fwrt->dsm_values); func++) { + for (int func = 0; func < ARRAY_SIZE(fwrt->dsm_values); func++) { if (!(fwrt->dsm_funcs_valid & BIT(func))) { IWL_DEBUG_RADIO(fwrt, "DSM func %d not in 0x%x\n", func, fwrt->dsm_funcs_valid); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c index ad899ce5c64a..533870cf443f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -16,8 +16,11 @@ static ssize_t iwl_mld_get_lari_config_cmd_size(u8 cmd_ver) { switch (cmd_ver) { - case 14: + case 15: return sizeof(struct iwl_lari_config_change_cmd); + case 14: + return offsetof(struct iwl_lari_config_change_cmd, + oem_supported_dsm_bitmap); case 13: return offsetof(struct iwl_lari_config_change_cmd, oem_uhb_allow_extension_bitmap); @@ -425,6 +428,10 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) if (!ret) cmd.oem_uhb_allow_extension_bitmap = cpu_to_le32(value); + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_QUERY, &value); + if (!ret) + cmd.oem_supported_dsm_bitmap = cpu_to_le32(value); + cmd.bios_wcpe_hdr.table_source = fwrt->puncturing_source; cmd.bios_wcpe_hdr.table_revision = fwrt->puncturing_revision; cmd.wcpe_bitmap = cpu_to_le32(fwrt->bios_puncturing); @@ -441,7 +448,8 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) !cmd.oem_11be_allow_bitmap && !cmd.oem_11bn_allow_bitmap && !cmd.oem_unii9_enable && - !cmd.wcpe_bitmap) + !cmd.wcpe_bitmap && + !cmd.oem_supported_dsm_bitmap) return; cmd.bios_hdr.table_source = fwrt->dsm_source; @@ -478,6 +486,9 @@ void iwl_mld_configure_lari(struct iwl_mld *mld) IWL_DEBUG_RADIO(mld, "sending LARI_CONFIG_CHANGE, wcpe_bitmap=0x%x\n", le32_to_cpu(cmd.wcpe_bitmap)); + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, oem_supported_dsm_bitmap=0x%x\n", + le32_to_cpu(cmd.oem_supported_dsm_bitmap)); ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(REGULATORY_AND_NVM_GROUP, From 4a2610a5a9fcf77eaad82bb030ec77ec5e381db8 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 23 Jul 2026 14:23:37 +0300 Subject: [PATCH 131/403] wifi: iwlwifi: bump core version for BZ/SC/DR Start supporting Core 107 FW on these devices. Link: https://patch.msgid.link/20260723142324.ed92b1d7f927.I6dd10d2f9a04a36751aea9e238fecfa62fe280a6@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 606362463dc7..1099d8cfe6aa 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware core release supported */ -#define IWL_BZ_UCODE_CORE_MAX 106 +#define IWL_BZ_UCODE_CORE_MAX 107 /* Lowest firmware core release supported */ #define IWL_BZ_UCODE_CORE_MIN 102 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c index 946975294b4f..a89a2c75687f 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c @@ -9,7 +9,7 @@ #include "fw/api/txq.h" /* Highest firmware core release supported */ -#define IWL_DR_UCODE_CORE_MAX 106 +#define IWL_DR_UCODE_CORE_MAX 107 /* Lowest firmware core release supported */ #define IWL_DR_UCODE_CORE_MIN 102 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index e8240c1782ac..16abf0147fe4 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware core release supported */ -#define IWL_SC_UCODE_CORE_MAX 106 +#define IWL_SC_UCODE_CORE_MAX 107 /* Lowest firmware core release supported */ #define IWL_SC_UCODE_CORE_MIN 102 From 888b1934642b796fd3fc00be5681cb36ce7eae4e Mon Sep 17 00:00:00 2001 From: Anirudh Srinivasan Date: Thu, 16 Jul 2026 18:05:10 -0500 Subject: [PATCH 132/403] PCI: Move Spacemit vendor and device IDs to linux/pci_ids.h Move the vendor and device ID for the existing Spacemit K1 PCIe Root Complex to include/linux/pci_ids.h. Also add K3's Root Complex device ID to this header. This is done so that these values can be referenced in the rtw89 driver to enable 36-bit DMA ability in it for WiFi to function on the K3 Pico ITX board. Acked-by: Bjorn Helgaas Signed-off-by: Anirudh Srinivasan Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260716-rtw89-spacemit-k3-v2-1-392b577ebf75@oss.tenstorrent.com --- drivers/pci/controller/dwc/pcie-spacemit-k1.c | 3 --- include/linux/pci_ids.h | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c index be20a520255b..f89c6d46c768 100644 --- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c +++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c @@ -21,9 +21,6 @@ #include "pcie-designware.h" -#define PCI_VENDOR_ID_SPACEMIT 0x201f -#define PCI_DEVICE_ID_SPACEMIT_K1 0x0001 - /* Offsets and field definitions for link management registers */ #define K1_PHY_AHB_IRQ_EN 0x0000 #define PCIE_INTERRUPT_EN BIT(0) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 1c9d40e09107..d6f26cacc8e3 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2640,6 +2640,10 @@ #define PCI_VENDOR_ID_SUNIX 0x1fd4 #define PCI_DEVICE_ID_SUNIX_1999 0x1999 +#define PCI_VENDOR_ID_SPACEMIT 0x201f +#define PCI_DEVICE_ID_SPACEMIT_K1 0x0001 +#define PCI_DEVICE_ID_SPACEMIT_K3 0x0002 + #define PCI_VENDOR_ID_HINT 0x3388 #define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013 From 27a5046cc56e851d2c498214b134170681ccb28a Mon Sep 17 00:00:00 2001 From: Anirudh Srinivasan Date: Thu, 16 Jul 2026 18:05:11 -0500 Subject: [PATCH 133/403] wifi: rtw89: pci: enable 36-bit DMA on spacemit K3 The Spacemit K3 Pico ITX Board has a RTL8852BE pcie card behind a PCIe root port, but the SoC doesn't have any 32 bit DMA addreseses which the rtw89 seems to use by default. Enable 36 bit DMA ability that the driver has when this particular root port is detected so that the driver can probe on this SoC. Tested-by: Aurelien Jarno Acked-by: Ping-Ke Shih Signed-off-by: Anirudh Srinivasan Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260716-rtw89-spacemit-k3-v2-2-392b577ebf75@oss.tenstorrent.com --- drivers/net/wireless/realtek/rtw89/pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 102bae488180..c5b82fc46d06 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -3326,6 +3326,10 @@ static bool rtw89_pci_is_dac_compatible_bridge(struct rtw89_dev *rtwdev) if (bridge->device == 0x2806) return true; break; + case PCI_VENDOR_ID_SPACEMIT: + if (bridge->device == PCI_DEVICE_ID_SPACEMIT_K3) + return true; + break; } return false; From d910631ff3522c54d20b6a5e03818d00d874ea50 Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Fri, 17 Jul 2026 14:19:02 +0800 Subject: [PATCH 134/403] wifi: rtw89: add LED support to reflect the wireless association status Add a new RTW89_LEDS Kconfig option, along with LED structures to describe flexible GPIO mappings for chip common LED definition. Core LED lifecycle and registration logic default to the mac80211 association trigger, and chips are wired up as the first user with a single-GPIO monochrome LED. Usage: - Auto-triggered (default): ON when connected, OFF when disconnected - Manual override: echo <1|0> > /sys/class/leds/rtw89-phyX/brightness Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717061910.54466-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/Kconfig | 7 ++ drivers/net/wireless/realtek/rtw89/Makefile | 1 + drivers/net/wireless/realtek/rtw89/core.c | 3 + drivers/net/wireless/realtek/rtw89/core.h | 22 ++++ drivers/net/wireless/realtek/rtw89/debug.h | 1 + drivers/net/wireless/realtek/rtw89/led.c | 123 ++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/led.h | 24 ++++ 7 files changed, 181 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/led.c create mode 100644 drivers/net/wireless/realtek/rtw89/led.h diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index 43e3b0ef44da..be31eaab7621 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -190,4 +190,11 @@ config RTW89_DEBUGFS If unsure, say Y to simplify debug problems +config RTW89_LEDS + bool + depends on RTW89_CORE + depends on LEDS_CLASS=y || LEDS_CLASS=MAC80211 + imply MAC80211_LEDS + default y + endif diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index 475bad743d75..3d47f13d1e66 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_RTW89_8922AU) += rtw89_8922au.o rtw89_8922au-objs := rtw8922au.o rtw89_core-$(CONFIG_RTW89_DEBUG) += debug.o +rtw89_core-$(CONFIG_RTW89_LEDS) += led.o obj-$(CONFIG_RTW89_PCI) += rtw89_pci.o rtw89_pci-y := pci.o pci_be.o diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index b4b0a451fed7..bb04f649bfb0 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -11,6 +11,7 @@ #include "core.h" #include "efuse.h" #include "fw.h" +#include "led.h" #include "mac.h" #include "phy.h" #include "ps.h" @@ -7588,6 +7589,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) rtw89_rfkill_polling_init(rtwdev); rtw89_btc_init(rtwdev); + rtw89_led_init(rtwdev); return 0; @@ -7601,6 +7603,7 @@ static void rtw89_core_unregister_hw(struct rtw89_dev *rtwdev) { struct ieee80211_hw *hw = rtwdev->hw; + rtw89_led_deinit(rtwdev); rtw89_rfkill_polling_deinit(rtwdev); ieee80211_unregister_hw(hw); } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c0795e0d10cd..41cc519317c9 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -5278,6 +5279,26 @@ struct rtw89_chanctx_listener { #define RTW89_NHM_TH_NUM 11 #define RTW89_NHM_RPT_NUM 12 +struct rtw89_led_gpio_entry { + u8 pin; + struct rtw89_reg3_def pinmux; + struct rtw89_reg2_def mode; + struct rtw89_reg2_def out; +}; + +struct rtw89_led_desc { + const struct rtw89_led_gpio_entry *gpios; + u8 n_gpio; +}; + +struct rtw89_led { + bool registered; + const struct rtw89_led_desc *desc; + struct led_classdev led; + enum led_brightness brightness_cache; + char name[32]; +}; + struct rtw89_chip_info { enum rtw89_core_chip_id chip_id; enum rtw89_chip_gen chip_gen; @@ -7245,6 +7266,7 @@ struct rtw89_dev { struct rtw89_debugfs *debugfs; struct rtw89_vif *pure_monitor_mode_vif; + struct rtw89_led led; /* HCI related data, keep last */ u8 priv[] __aligned(sizeof(void *)); diff --git a/drivers/net/wireless/realtek/rtw89/debug.h b/drivers/net/wireless/realtek/rtw89/debug.h index 7cdceb24f52d..f53d8acbd0d2 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.h +++ b/drivers/net/wireless/realtek/rtw89/debug.h @@ -32,6 +32,7 @@ enum rtw89_debug_mask { RTW89_DBG_ACPI = BIT(21), RTW89_DBG_EDCCA = BIT(22), RTW89_DBG_PS = BIT(23), + RTW89_DBG_LED = BIT(24), RTW89_DBG_UNEXP = BIT(31), }; diff --git a/drivers/net/wireless/realtek/rtw89/led.c b/drivers/net/wireless/realtek/rtw89/led.c new file mode 100644 index 000000000000..64e7eaa7ee39 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/led.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2026 Realtek Corporation + */ + +#include "core.h" +#include "debug.h" +#include "led.h" +#include "reg.h" + +static const struct rtw89_led_gpio_entry rtw89_common_led_gpios[] = {{ + .pin = 8, + .pinmux = {.addr = R_AX_GPIO8_15_FUNC_SEL, .mask = GENMASK(3, 0), .data = 0xf}, + .mode = {.addr = R_AX_GPIO_EXT_CTRL + 2, .data = BIT(0) | BIT(8)}, + .out = {.addr = R_AX_GPIO_EXT_CTRL + 1, .data = BIT(0)}, +}}; + +static const struct rtw89_led_desc rtw89_common_led_desc = { + .gpios = rtw89_common_led_gpios, + .n_gpio = ARRAY_SIZE(rtw89_common_led_gpios), +}; + +void rtw89_led_gpio_config(struct rtw89_dev *rtwdev, + const struct rtw89_led_gpio_entry *e) +{ + rtw89_write32_mask(rtwdev, e->pinmux.addr, e->pinmux.mask, e->pinmux.data); + rtw89_write16_clr(rtwdev, e->mode.addr, e->mode.data); +} + +void rtw89_led_gpio_set(struct rtw89_dev *rtwdev, + const struct rtw89_led_gpio_entry *e, + enum led_brightness brightness) +{ + if (brightness == LED_OFF) { + rtw89_write16_clr(rtwdev, e->mode.addr, e->mode.data); + } else { + rtw89_write16_set(rtwdev, e->mode.addr, e->mode.data); + rtw89_write8_clr(rtwdev, e->out.addr, e->out.data); + } +} + +static int rtw89_led_set(struct led_classdev *led, enum led_brightness brightness) +{ + struct rtw89_led *rtw_led = container_of(led, struct rtw89_led, led); + struct rtw89_dev *rtwdev = container_of(rtw_led, struct rtw89_dev, led); + const struct rtw89_led_desc *desc = rtw_led->desc; + const struct rtw89_led_gpio_entry *e = &desc->gpios[0]; + + if (rtw_led->brightness_cache == brightness) { + rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u skip (no change)\n", + e->pin); + return 0; + } + + wiphy_lock(rtwdev->hw->wiphy); + + rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u brightness=%u\n", + e->pin, brightness); + rtw89_led_gpio_set(rtwdev, e, brightness); + rtw_led->brightness_cache = brightness; + + wiphy_unlock(rtwdev->hw->wiphy); + + return 0; +} + +static int rtw89_led_sc_init(struct rtw89_dev *rtwdev, const struct rtw89_led_desc *desc) +{ + struct rtw89_led *rtw_led = &rtwdev->led; + int ret; + + snprintf(rtw_led->name, sizeof(rtw_led->name), "rtw89-%s", + wiphy_name(rtwdev->hw->wiphy)); + rtw_led->led.name = rtw_led->name; + rtw_led->led.brightness_set_blocking = rtw89_led_set; + rtw_led->led.max_brightness = LED_ON; + rtw_led->led.default_trigger = ieee80211_get_assoc_led_name(rtwdev->hw); + + ret = led_classdev_register(rtwdev->dev, &rtw_led->led); + if (ret) { + rtw89_warn(rtwdev, "failed to register LED, ret=%d\n", ret); + return ret; + } + + rtw89_led_gpio_config(rtwdev, &desc->gpios[0]); + + return 0; +} + +static void rtw89_led_sc_deinit(struct rtw89_dev *rtwdev) +{ + struct rtw89_led *rtw_led = &rtwdev->led; + + led_classdev_unregister(&rtw_led->led); +} + +void rtw89_led_init(struct rtw89_dev *rtwdev) +{ + const struct rtw89_led_desc *desc = &rtw89_common_led_desc; + struct rtw89_led *rtw_led = &rtwdev->led; + int ret; + + /* single-GPIO monochrome LED is the only supported layout */ + BUILD_BUG_ON(ARRAY_SIZE(rtw89_common_led_gpios) != 1); + + rtw_led->desc = desc; + rtw_led->brightness_cache = LED_OFF; + + ret = rtw89_led_sc_init(rtwdev, desc); + if (ret) + return; + + rtw_led->registered = true; +} + +void rtw89_led_deinit(struct rtw89_dev *rtwdev) +{ + struct rtw89_led *rtw_led = &rtwdev->led; + + if (!rtw_led->registered) + return; + + rtw89_led_sc_deinit(rtwdev); +} diff --git a/drivers/net/wireless/realtek/rtw89/led.h b/drivers/net/wireless/realtek/rtw89/led.h new file mode 100644 index 000000000000..27e782cff7c7 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/led.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2026 Realtek Corporation + */ + +#ifndef __RTW89_LED_H__ +#define __RTW89_LED_H__ + +#include "core.h" + +void rtw89_led_gpio_config(struct rtw89_dev *rtwdev, + const struct rtw89_led_gpio_entry *e); +void rtw89_led_gpio_set(struct rtw89_dev *rtwdev, + const struct rtw89_led_gpio_entry *e, + enum led_brightness brightness); + +#ifdef CONFIG_RTW89_LEDS +void rtw89_led_init(struct rtw89_dev *rtwdev); +void rtw89_led_deinit(struct rtw89_dev *rtwdev); +#else +static inline void rtw89_led_init(struct rtw89_dev *rtwdev) {} +static inline void rtw89_led_deinit(struct rtw89_dev *rtwdev) {} +#endif + +#endif From 721d90c8509aee195c3714b70c4cff612fbb3a1c Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Fri, 17 Jul 2026 14:19:03 +0800 Subject: [PATCH 135/403] wifi: rtw89: add multicolor LED support for RTL8852CU valve board Add multicolor LED support for the RTL8852CU valve board to reflect wireless connection status (by default, green LED is ON when associated and OFF when disconnected). Extend the rtw89 LED subsystem with a multicolor path via led_classdev_mc, for board-level variants with more than one LED GPIO channel. Add a new RTW89_LEDS_MC Kconfig option and support multicolor LEDs through led_classdev_mc, with per-channel caching to minimize redundant register writes. The RTL8852CU valve board is wired up as the first user, driving a multicolor WRGB LED over four GPIO channels (8, 18, 16, and 17) that map to LED_COLOR_ID_WHITE/RED/GREEN/BLUE. Usage: - Auto-triggered (default): Green LED ON when connected, OFF when disconnected - Manual override (e.g., set red; channel order: WHITE RED GREEN BLUE): echo "0 1 0 0" > /sys/class/leds/rtw89-phyX-multicolor/multi_intensity echo 1 > /sys/class/leds/rtw89-phyX-multicolor/brightness Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717061910.54466-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/Kconfig | 7 ++ drivers/net/wireless/realtek/rtw89/Makefile | 1 + drivers/net/wireless/realtek/rtw89/core.c | 1 + drivers/net/wireless/realtek/rtw89/core.h | 15 +++- drivers/net/wireless/realtek/rtw89/led.c | 28 +++++-- drivers/net/wireless/realtek/rtw89/led.h | 13 +++ drivers/net/wireless/realtek/rtw89/led_mc.c | 81 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/reg.h | 2 + .../net/wireless/realtek/rtw89/rtw8851be.c | 1 + .../net/wireless/realtek/rtw89/rtw8851bu.c | 1 + .../net/wireless/realtek/rtw89/rtw8852ae.c | 1 + .../net/wireless/realtek/rtw89/rtw8852au.c | 1 + .../net/wireless/realtek/rtw89/rtw8852be.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bte.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bu.c | 1 + .../net/wireless/realtek/rtw89/rtw8852ce.c | 1 + .../net/wireless/realtek/rtw89/rtw8852cu.c | 54 +++++++++++++ .../net/wireless/realtek/rtw89/rtw8922ae.c | 2 + .../net/wireless/realtek/rtw89/rtw8922au.c | 1 + .../net/wireless/realtek/rtw89/rtw8922de.c | 2 + 20 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 drivers/net/wireless/realtek/rtw89/led_mc.c diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index be31eaab7621..7200a944260f 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -197,4 +197,11 @@ config RTW89_LEDS imply MAC80211_LEDS default y +config RTW89_LEDS_MC + bool + depends on RTW89_LEDS + depends on LEDS_CLASS_MULTICOLOR + imply LEDS_TRIGGER_TIMER + default y + endif diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index 3d47f13d1e66..5536714e0268 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -93,6 +93,7 @@ rtw89_8922au-objs := rtw8922au.o rtw89_core-$(CONFIG_RTW89_DEBUG) += debug.o rtw89_core-$(CONFIG_RTW89_LEDS) += led.o +rtw89_core-$(CONFIG_RTW89_LEDS_MC) += led_mc.o obj-$(CONFIG_RTW89_PCI) += rtw89_pci.o rtw89_pci-y := pci.o pci_be.o diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index bb04f649bfb0..b87a72700b99 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -7701,6 +7701,7 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, rtwdev->ops = ops; rtwdev->chip = chip; rtwdev->variant = variant; + rtwdev->board = info->board; rtwdev->fw.req.firmware = firmware; rtwdev->fw.fw_format = fw_format; rtwdev->support_mlo = support_mlo; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 41cc519317c9..70cf6cbf4e8a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -5279,8 +5280,12 @@ struct rtw89_chanctx_listener { #define RTW89_NHM_TH_NUM 11 #define RTW89_NHM_RPT_NUM 12 +#define RTW89_LED_MAX_NUM 4 + struct rtw89_led_gpio_entry { u8 pin; + unsigned int color; + u8 intensity; struct rtw89_reg3_def pinmux; struct rtw89_reg2_def mode; struct rtw89_reg2_def out; @@ -5295,7 +5300,9 @@ struct rtw89_led { bool registered; const struct rtw89_led_desc *desc; struct led_classdev led; - enum led_brightness brightness_cache; + struct led_classdev_mc led_mc; + struct mc_subled subled[RTW89_LED_MAX_NUM]; + enum led_brightness brightness_cache[RTW89_LED_MAX_NUM]; char name[32]; }; @@ -5454,6 +5461,10 @@ struct rtw89_chip_variant { const struct rtw89_qta_def *qta_def_override; }; +struct rtw89_board_variant { + const struct rtw89_led_desc *led_desc; +}; + union rtw89_bus_info { const struct rtw89_pci_info *pci; const struct rtw89_usb_info *usb; @@ -5462,6 +5473,7 @@ union rtw89_bus_info { struct rtw89_driver_info { const struct rtw89_chip_info *chip; const struct rtw89_chip_variant *variant; + const struct rtw89_board_variant *board; const struct dmi_system_id *quirks; unsigned long dev_id_quirks; /* bitmap of rtw89_quirks */ union rtw89_bus_info bus; @@ -7136,6 +7148,7 @@ struct rtw89_dev { struct rtw89_hw_scan_info scan_info; const struct rtw89_chip_info *chip; const struct rtw89_chip_variant *variant; + const struct rtw89_board_variant *board; const struct rtw89_pci_info *pci_info; const struct rtw89_rfe_parms *rfe_parms; struct rtw89_hal hal; diff --git a/drivers/net/wireless/realtek/rtw89/led.c b/drivers/net/wireless/realtek/rtw89/led.c index 64e7eaa7ee39..ae74b7075d7c 100644 --- a/drivers/net/wireless/realtek/rtw89/led.c +++ b/drivers/net/wireless/realtek/rtw89/led.c @@ -45,7 +45,7 @@ static int rtw89_led_set(struct led_classdev *led, enum led_brightness brightnes const struct rtw89_led_desc *desc = rtw_led->desc; const struct rtw89_led_gpio_entry *e = &desc->gpios[0]; - if (rtw_led->brightness_cache == brightness) { + if (rtw_led->brightness_cache[0] == brightness) { rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u skip (no change)\n", e->pin); return 0; @@ -56,7 +56,7 @@ static int rtw89_led_set(struct led_classdev *led, enum led_brightness brightnes rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u brightness=%u\n", e->pin, brightness); rtw89_led_gpio_set(rtwdev, e, brightness); - rtw_led->brightness_cache = brightness; + rtw_led->brightness_cache[0] = brightness; wiphy_unlock(rtwdev->hw->wiphy); @@ -97,15 +97,27 @@ void rtw89_led_init(struct rtw89_dev *rtwdev) { const struct rtw89_led_desc *desc = &rtw89_common_led_desc; struct rtw89_led *rtw_led = &rtwdev->led; + const struct rtw89_board_variant *board = rtwdev->board; int ret; + int i; /* single-GPIO monochrome LED is the only supported layout */ BUILD_BUG_ON(ARRAY_SIZE(rtw89_common_led_gpios) != 1); - rtw_led->desc = desc; - rtw_led->brightness_cache = LED_OFF; + if (board) + desc = board->led_desc; + if (!desc->n_gpio || desc->n_gpio > RTW89_LED_MAX_NUM) + return; + + rtw_led->desc = desc; + for (i = 0; i < ARRAY_SIZE(rtw_led->brightness_cache); i++) + rtw_led->brightness_cache[i] = LED_OFF; + + if (desc->n_gpio == 1) + ret = rtw89_led_sc_init(rtwdev, desc); + else + ret = rtw89_led_mc_init(rtwdev, desc); - ret = rtw89_led_sc_init(rtwdev, desc); if (ret) return; @@ -115,9 +127,13 @@ void rtw89_led_init(struct rtw89_dev *rtwdev) void rtw89_led_deinit(struct rtw89_dev *rtwdev) { struct rtw89_led *rtw_led = &rtwdev->led; + const struct rtw89_led_desc *desc = rtw_led->desc; if (!rtw_led->registered) return; - rtw89_led_sc_deinit(rtwdev); + if (desc->n_gpio == 1) + rtw89_led_sc_deinit(rtwdev); + else + rtw89_led_mc_deinit(rtwdev); } diff --git a/drivers/net/wireless/realtek/rtw89/led.h b/drivers/net/wireless/realtek/rtw89/led.h index 27e782cff7c7..f5ccfe1d1752 100644 --- a/drivers/net/wireless/realtek/rtw89/led.h +++ b/drivers/net/wireless/realtek/rtw89/led.h @@ -21,4 +21,17 @@ static inline void rtw89_led_init(struct rtw89_dev *rtwdev) {} static inline void rtw89_led_deinit(struct rtw89_dev *rtwdev) {} #endif +#ifdef CONFIG_RTW89_LEDS_MC +int rtw89_led_mc_init(struct rtw89_dev *rtwdev, const struct rtw89_led_desc *desc); +void rtw89_led_mc_deinit(struct rtw89_dev *rtwdev); +#else +static inline int rtw89_led_mc_init(struct rtw89_dev *rtwdev, + const struct rtw89_led_desc *desc) +{ + return -EOPNOTSUPP; +} + +static inline void rtw89_led_mc_deinit(struct rtw89_dev *rtwdev) {} +#endif + #endif diff --git a/drivers/net/wireless/realtek/rtw89/led_mc.c b/drivers/net/wireless/realtek/rtw89/led_mc.c new file mode 100644 index 000000000000..7e9243baef68 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/led_mc.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2026 Realtek Corporation + */ + +#include "core.h" +#include "debug.h" +#include "led.h" + +static int rtw89_led_mc_brightness_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct led_classdev_mc *mcdev = lcdev_to_mccdev(led); + struct rtw89_led *rtw_led = container_of(mcdev, struct rtw89_led, led_mc); + struct rtw89_dev *rtwdev = container_of(rtw_led, struct rtw89_dev, led); + const struct rtw89_led_desc *desc = rtw_led->desc; + const struct rtw89_led_gpio_entry *e; + unsigned int i; + + led_mc_calc_color_components(mcdev, brightness); + + wiphy_lock(rtwdev->hw->wiphy); + + for (i = 0; i < mcdev->num_colors; i++) { + e = &desc->gpios[i]; + if (rtw_led->brightness_cache[i] == mcdev->subled_info[i].brightness) { + rtw89_debug(rtwdev, RTW89_DBG_LED, + "led_mc_set: pin=%u skip (no change)\n", e->pin); + continue; + } + + rtw89_debug(rtwdev, RTW89_DBG_LED, "led_mc_set: pin=%u brightness=%u\n", + e->pin, mcdev->subled_info[i].brightness); + rtw89_led_gpio_set(rtwdev, e, mcdev->subled_info[i].brightness); + rtw_led->brightness_cache[i] = mcdev->subled_info[i].brightness; + } + + wiphy_unlock(rtwdev->hw->wiphy); + + return 0; +} + +int rtw89_led_mc_init(struct rtw89_dev *rtwdev, const struct rtw89_led_desc *desc) +{ + struct rtw89_led *rtw_led = &rtwdev->led; + struct led_classdev_mc *led_mc = &rtw_led->led_mc; + int i, ret; + + led_mc->num_colors = desc->n_gpio; + led_mc->subled_info = rtw_led->subled; + + for (i = 0; i < desc->n_gpio; i++) { + const struct rtw89_led_gpio_entry *e = &desc->gpios[i]; + + rtw_led->subled[i].color_index = e->color; + rtw_led->subled[i].intensity = e->intensity; + rtw_led->subled[i].brightness = 0; + rtw_led->subled[i].channel = i; + + rtw89_led_gpio_config(rtwdev, e); + } + + snprintf(rtw_led->name, sizeof(rtw_led->name), "rtw89-%s-multicolor", + wiphy_name(rtwdev->hw->wiphy)); + led_mc->led_cdev.name = rtw_led->name; + led_mc->led_cdev.brightness_set_blocking = rtw89_led_mc_brightness_set; + led_mc->led_cdev.max_brightness = LED_ON; + led_mc->led_cdev.default_trigger = ieee80211_get_assoc_led_name(rtwdev->hw); + + ret = led_classdev_multicolor_register(rtwdev->dev, led_mc); + if (ret) + rtw89_warn(rtwdev, "failed to register multicolor LED, ret=%d\n", ret); + + return ret; +} + +void rtw89_led_mc_deinit(struct rtw89_dev *rtwdev) +{ + struct rtw89_led *rtw_led = &rtwdev->led; + + led_classdev_multicolor_unregister(&rtw_led->led_mc); +} diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 3bf2865b81ab..053028b9ef16 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -205,6 +205,8 @@ #define MAC_AX_HCI_SEL_PCIE_USB 3 #define MAC_AX_HCI_SEL_MULTI_SDIO 4 +#define R_AX_GPIO_16_TO_18_EXT_CTRL 0x0150 + #define R_AX_HALT_H2C_CTRL 0x0160 #define R_AX_HALT_H2C 0x0168 #define B_AX_HALT_H2C_TRIGGER BIT(0) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851be.c b/drivers/net/wireless/realtek/rtw89/rtw8851be.c index 640672eb0d26..459d2e778b83 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851be.c @@ -72,6 +72,7 @@ static const struct rtw89_pci_info rtw8851b_pci_info = { static const struct rtw89_driver_info rtw89_8851be_info = { .chip = &rtw8851b_chip_info, .variant = NULL, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c index 34ba5661d771..1e827205f254 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c @@ -30,6 +30,7 @@ static const struct rtw89_usb_info rtw8851b_usb_info = { static const struct rtw89_driver_info rtw89_8851bu_info = { .chip = &rtw8851b_chip_info, .variant = NULL, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c index 64306cdc1ee4..142a45a2e718 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c @@ -70,6 +70,7 @@ static const struct rtw89_pci_info rtw8852a_pci_info = { static const struct rtw89_driver_info rtw89_8852ae_info = { .chip = &rtw8852a_chip_info, .variant = NULL, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852au.c b/drivers/net/wireless/realtek/rtw89/rtw8852au.c index 29b7f7769370..065f4e5b17af 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852au.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852au.c @@ -32,6 +32,7 @@ static const struct rtw89_usb_info rtw8852a_usb_info = { static const struct rtw89_driver_info rtw89_8852au_info = { .chip = &rtw8852a_chip_info, .variant = NULL, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852be.c b/drivers/net/wireless/realtek/rtw89/rtw8852be.c index 5bc0a6a99d1d..1c622a99c070 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852be.c @@ -72,6 +72,7 @@ static const struct rtw89_pci_info rtw8852b_pci_info = { static const struct rtw89_driver_info rtw89_8852be_info = { .chip = &rtw8852b_chip_info, .variant = NULL, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c index 49a72ca835ac..73c7ebd33ab4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c @@ -78,6 +78,7 @@ static const struct rtw89_pci_info rtw8852bt_pci_info = { static const struct rtw89_driver_info rtw89_8852bte_info = { .chip = &rtw8852bt_chip_info, .variant = NULL, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c index 308d3d570ff3..de79a19a2824 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c @@ -30,6 +30,7 @@ static const struct rtw89_usb_info rtw8852b_usb_info = { static const struct rtw89_driver_info rtw89_8852bu_info = { .chip = &rtw8852b_chip_info, .variant = NULL, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c index 3c64c0539205..bb5d5745aa62 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c @@ -101,6 +101,7 @@ static const struct dmi_system_id rtw8852c_pci_quirks[] = { static const struct rtw89_driver_info rtw89_8852ce_info = { .chip = &rtw8852c_chip_info, .variant = NULL, + .board = NULL, .quirks = rtw8852c_pci_quirks, .dev_id_quirks = 0, .bus = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 81ee96b0a048..51dd8fb05366 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -32,6 +32,7 @@ static const struct rtw89_usb_info rtw8852c_usb_info = { static const struct rtw89_driver_info rtw89_8852cu_info = { .chip = &rtw8852c_chip_info, .variant = NULL, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { @@ -39,9 +40,62 @@ static const struct rtw89_driver_info rtw89_8852cu_info = { }, }; +static const struct rtw89_led_gpio_entry rtw8852cu_valve_led_gpios[] = { + { + .pin = 8, + .color = LED_COLOR_ID_WHITE, + .intensity = 0, + .pinmux = {.addr = R_AX_GPIO8_15_FUNC_SEL, + .mask = GENMASK(3, 0), + .data = 0xf}, + .mode = {.addr = R_AX_GPIO_EXT_CTRL + 2, .data = BIT(0) | BIT(8)}, + .out = {.addr = R_AX_GPIO_EXT_CTRL + 1, .data = BIT(0)}, + }, { + .pin = 18, + .color = LED_COLOR_ID_RED, + .intensity = 0, + .pinmux = {.addr = R_AX_GPIO16_23_FUNC_SEL, + .mask = GENMASK(11, 8), + .data = 0xf}, + .mode = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 2, + .data = BIT(2) | BIT(10)}, + .out = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 1, .data = BIT(2)}, + }, { + .pin = 16, + .color = LED_COLOR_ID_GREEN, + .intensity = 1, + .pinmux = {.addr = R_AX_GPIO16_23_FUNC_SEL, + .mask = GENMASK(3, 0), + .data = 0xf}, + .mode = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 2, + .data = BIT(0) | BIT(8)}, + .out = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 1, .data = BIT(0)}, + }, { + .pin = 17, + .color = LED_COLOR_ID_BLUE, + .intensity = 0, + .pinmux = {.addr = R_AX_GPIO16_23_FUNC_SEL, + .mask = GENMASK(7, 4), + .data = 0xf}, + .mode = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 2, + .data = BIT(1) | BIT(9)}, + .out = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 1, .data = BIT(1)}, + }, +}; + +static const struct rtw89_led_desc rtw8852cu_valve_led_desc = { + .gpios = rtw8852cu_valve_led_gpios, + .n_gpio = ARRAY_SIZE(rtw8852cu_valve_led_gpios), +}; + +static const struct rtw89_board_variant rtw89_8852cu_valve_board = { + .led_desc = &rtw8852cu_valve_led_desc, +}; + static const struct rtw89_driver_info rtw89_8852cu_valve_info = { .chip = &rtw8852c_chip_info, .variant = NULL, + .board = &rtw89_8852cu_valve_board, .quirks = NULL, .dev_id_quirks = BIT(RTW89_QUIRK_HW_INFO_SYSFS), .bus = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c index 5527a8db393b..8f3a1cd463d9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c @@ -76,6 +76,7 @@ static const struct rtw89_pci_info rtw8922a_pci_info = { static const struct rtw89_driver_info rtw89_8922ae_info = { .chip = &rtw8922a_chip_info, .variant = NULL, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { @@ -86,6 +87,7 @@ static const struct rtw89_driver_info rtw89_8922ae_info = { static const struct rtw89_driver_info rtw89_8922ae_vs_info = { .chip = &rtw8922a_chip_info, .variant = &rtw8922ae_vs_variant, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922au.c b/drivers/net/wireless/realtek/rtw89/rtw8922au.c index 2b81de501d62..56c79b1ec865 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922au.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922au.c @@ -31,6 +31,7 @@ static const struct rtw89_usb_info rtw8922a_usb_info = { static const struct rtw89_driver_info rtw89_8922au_info = { .chip = &rtw8922a_chip_info, .variant = NULL, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922de.c b/drivers/net/wireless/realtek/rtw89/rtw8922de.c index a1a81c338be3..09400a0efcfd 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922de.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922de.c @@ -72,6 +72,7 @@ static const struct rtw89_pci_info rtw8922d_pci_info = { static const struct rtw89_driver_info rtw89_8922de_vs_info = { .chip = &rtw8922d_chip_info, .variant = &rtw8922de_vs_variant, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { @@ -82,6 +83,7 @@ static const struct rtw89_driver_info rtw89_8922de_vs_info = { static const struct rtw89_driver_info rtw89_8922de_info = { .chip = &rtw8922d_chip_info, .variant = NULL, + .board = NULL, .quirks = NULL, .dev_id_quirks = 0, .bus = { From 355626a2c23271bfbb9accaf372d89557b0d64ba Mon Sep 17 00:00:00 2001 From: David Lee Date: Fri, 17 Jul 2026 14:19:04 +0800 Subject: [PATCH 136/403] wifi: rtw89: 8852cu: add quirk to disable 2.4 GHz band Add RTW89_QUIRK_DISABLE_2GHZ to the rtw89_quirks enum to allow per-device suppression of the 2.4 GHz band. Apply the quirk only for the 0x28de:0x2432 (VID:PID) USB device, which operates only in 5/6 GHz bands. Signed-off-by: David Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717061910.54466-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 3 +++ drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index b87a72700b99..77a0e2582dbe 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -6418,6 +6418,9 @@ static int rtw89_core_set_supported_band(struct rtw89_dev *rtwdev) u8 support_bands = rtwdev->chip->support_bands; int ret; + if (test_bit(RTW89_QUIRK_DISABLE_2GHZ, rtwdev->quirks)) + support_bands &= ~BIT(NL80211_BAND_2GHZ); + if (support_bands & BIT(NL80211_BAND_2GHZ)) { sband = rtw89_core_sband_dup(rtwdev, &rtw89_sband_2ghz); if (!sband) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 70cf6cbf4e8a..9d782cbfee70 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6057,6 +6057,7 @@ enum rtw89_quirks { RTW89_QUIRK_THERMAL_PROT_120C, RTW89_QUIRK_THERMAL_PROT_110C, RTW89_QUIRK_HW_INFO_SYSFS, + RTW89_QUIRK_DISABLE_2GHZ, NUM_OF_RTW89_QUIRKS, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 51dd8fb05366..2dec9b845481 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -97,7 +97,8 @@ static const struct rtw89_driver_info rtw89_8852cu_valve_info = { .variant = NULL, .board = &rtw89_8852cu_valve_board, .quirks = NULL, - .dev_id_quirks = BIT(RTW89_QUIRK_HW_INFO_SYSFS), + .dev_id_quirks = BIT(RTW89_QUIRK_HW_INFO_SYSFS) | + BIT(RTW89_QUIRK_DISABLE_2GHZ), .bus = { .usb = &rtw8852c_usb_info, }, From e92d5b02b4563bb1ad6cd34acfa06e5b8f7c92dc Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Fri, 17 Jul 2026 14:19:05 +0800 Subject: [PATCH 137/403] wifi: rtw89: rfk: update TXIQK H2C command format to v1 TX IQK is a RF calibration, the v1 format adds a field for the thermal re-calibration parameter for RTL8922D after FW 0.35.113.0. Update the format accordingly. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717061910.54466-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/fw.c | 35 ++++++++++++++++------- drivers/net/wireless/realtek/rtw89/fw.h | 7 ++++- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 9d782cbfee70..9b7f197a3af2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5599,6 +5599,7 @@ enum rtw89_fw_feature { ), RTW89_FW_FEATURE_RFK_RXDCK_V0, RTW89_FW_FEATURE_RFK_IQK_V0, + RTW89_FW_FEATURE_RFK_TXIQK_V0, RTW89_FW_FEATURE_NO_WOW_CPU_IO_RX, RTW89_FW_FEATURE_NOTIFY_AP_INFO, RTW89_FW_FEATURE_CH_INFO_BE_V0, diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 0db77120298f..ad87484f51a7 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -948,6 +948,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 104, 0, TX_HISTORY_V1), __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 108, 0, SIM_SER_L0L1_BY_HALT_H2C), __CFG_FW_FEAT(RTL8922D, lt, 0, 35, 109, 1, SCAN_OFFLOAD_BE_V1), + __CFG_FW_FEAT(RTL8922D, lt, 0, 35, 113, 0, RFK_TXIQK_V0), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, @@ -8464,29 +8465,43 @@ int rtw89_fw_h2c_rf_tas_trigger(struct rtw89_dev *rtwdev, bool enable) int rtw89_fw_h2c_rf_txiqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, const struct rtw89_chan *chan) { + struct rtw89_h2c_rf_txiqk_v0 *h2c_v0; struct rtw89_h2c_rf_txiqk *h2c; u32 len = sizeof(*h2c); struct sk_buff *skb; + u8 ver = U8_MAX; int ret; + if (RTW89_CHK_FW_FEATURE(RFK_TXIQK_V0, &rtwdev->fw)) { + len = sizeof(*h2c_v0); + ver = 0; + } + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { rtw89_err(rtwdev, "failed to alloc skb for h2c RF TXIQK\n"); return -ENOMEM; } skb_put(skb, len); + h2c_v0 = (struct rtw89_h2c_rf_txiqk_v0 *)skb->data; + + h2c_v0->len = len; + h2c_v0->phy = phy_idx; + h2c_v0->txiqk_enable = true; + h2c_v0->is_wb_txiqk = true; + h2c_v0->kpath = RF_AB; + h2c_v0->cur_band = chan->band_type; + h2c_v0->cur_bw = chan->band_width; + h2c_v0->cur_ch = chan->channel; + h2c_v0->txiqk_dbg_en = rtw89_debug_is_enabled(rtwdev, RTW89_DBG_RFK); + + if (ver == 0) + goto hdr; + h2c = (struct rtw89_h2c_rf_txiqk *)skb->data; + h2c->is_ther_rek = false; - h2c->len = len; - h2c->phy = phy_idx; - h2c->txiqk_enable = true; - h2c->is_wb_txiqk = true; - h2c->kpath = RF_AB; - h2c->cur_band = chan->band_type; - h2c->cur_bw = chan->band_width; - h2c->cur_ch = chan->channel; - h2c->txiqk_dbg_en = rtw89_debug_is_enabled(rtwdev, RTW89_DBG_RFK); - +hdr: rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_RFK, H2C_FUNC_RFK_TXIQK_OFFOAD, 0, 0, len); diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index a1aab8293c14..2654f8fd0e51 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -5042,7 +5042,7 @@ struct rtw89_h2c_rf_rxdck { u8 is_chl_k; } __packed; -struct rtw89_h2c_rf_txiqk { +struct rtw89_h2c_rf_txiqk_v0 { u8 len; u8 phy; u8 txiqk_enable; @@ -5054,6 +5054,11 @@ struct rtw89_h2c_rf_txiqk { u8 txiqk_dbg_en; } __packed; +struct rtw89_h2c_rf_txiqk { + struct rtw89_h2c_rf_txiqk_v0 v0; + u8 is_ther_rek; +} __packed; + struct rtw89_h2c_rf_cim3k { u8 len; u8 phy; From 89833570775ad001a79b6fc970d814c9fc70cf40 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Fri, 17 Jul 2026 14:19:06 +0800 Subject: [PATCH 138/403] wifi: rtw89: 8922d: bypass TXIQK when scan When connected to an AP and a scan is triggered, the FW may fail to transmit probe request because switching channels may load an uninitialized TXIQK table. Therefore, bypass TXIQK during scanning to avoid using invalid calibration values. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717061910.54466-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/reg.h | 10 +++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 64 ++++++++++++++++++- 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 9b7f197a3af2..2d9113cb43f9 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6284,6 +6284,7 @@ struct rtw89_iqk_info { u8 iqk_table_idx[RTW89_IQK_PATH_NR]; u32 lok_idac[RTW89_IQK_CHS_NR][RTW89_IQK_PATH_NR]; u32 lok_vbuf[RTW89_IQK_CHS_NR][RTW89_IQK_PATH_NR]; + u32 iqc_bak[2]; }; #define RTW89_DPK_RF_PATH 2 diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 053028b9ef16..756b94dcd475 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10121,6 +10121,7 @@ #define B_S1_DACKQ8_K GENMASK(15, 8) #define R_NCTL_CFG 0x8000 #define R_NCTL_CFG_BE4 0x38000 +#define B_NCTL_CFG_BE4_SPAGE GENMASK(25, 24) #define B_NCTL_CHK_EN BIT(3) #define B_NCTL_CFG_SPAGE GENMASK(2, 1) #define R_NCTL_RPT 0x8008 @@ -11061,6 +11062,15 @@ #define B_KTBL0_MLD_IDX0 GENMASK(25, 24) #define B_KTBL0_MLD_IDX1 GENMASK(27, 26) #define B_KTBL0_RST BIT(31) +#define R_CFIR_CTRL_A_BE4 0x38124 +#define R_CFIR_CTRL_B_BE4 0x38224 +#define B_CFIR_CTRL_KIDX0_EN BIT(8) +#define B_CFIR_CTRL_KIDX1_EN BIT(24) +#define R_TX_IQC_A_BE4 0x38138 +#define R_TX_IQC_B_BE4 0x38238 +#define B_TX_IQC_X GENMASK(31, 20) +#define B_TX_IQC_Y GENMASK(19, 8) +#define B_TX_IQC_DPK BIT(3) #define R_KTBL1A_BE4 0x38154 #define R_KTBL1B_BE4 0x38254 #define B_KTBL1_TBL0 BIT(3) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 929bcfc8089d..fde4117bcd49 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2765,14 +2765,74 @@ static void rtw8922d_rfk_band_changed(struct rtw89_dev *rtwdev, { } +static void __rtw8922d_txiqk_disable(struct rtw89_dev *rtwdev) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u8 path, kidx; + + for (path = RF_PATH_A; path <= RF_PATH_B; path++) { + kidx = rtw89_phy_read32_mask(rtwdev, R_KTBL0A_BE4 + (path << 8), + B_KTBL0_IDX0); + if (kidx == 0) { + rtw89_phy_write32_clr(rtwdev, R_CFIR_CTRL_A_BE4 + (path << 8), + B_CFIR_CTRL_KIDX0_EN); + } else if (kidx == 1) { + rtw89_phy_write32_clr(rtwdev, R_CFIR_CTRL_A_BE4 + (path << 8), + B_CFIR_CTRL_KIDX1_EN); + } else { + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG_BE4, + B_NCTL_CFG_BE4_SPAGE, 0x1); + rtw89_phy_write32_clr(rtwdev, R_CFIR_CTRL_A_BE4 + (path << 8), + B_CFIR_CTRL_KIDX0_EN); + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG_BE4, + B_NCTL_CFG_BE4_SPAGE, 0x0); + } + + iqk_info->iqc_bak[path] = + rtw89_phy_read32(rtwdev, R_TX_IQC_A_BE4 + (path << 8)); + rtw89_phy_write32(rtwdev, R_TX_IQC_A_BE4 + (path << 8), 0x40000002); + } +} + +static void __rtw8922d_txiqk_enable(struct rtw89_dev *rtwdev) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u8 path, kidx; + + for (path = RF_PATH_A; path <= RF_PATH_B; path++) { + kidx = rtw89_phy_read32_mask(rtwdev, R_KTBL0A_BE4 + (path << 8), + B_KTBL0_IDX0); + if (kidx == 0) { + rtw89_phy_write32_set(rtwdev, R_CFIR_CTRL_A_BE4 + (path << 8), + B_CFIR_CTRL_KIDX0_EN); + } else if (kidx == 1) { + rtw89_phy_write32_set(rtwdev, R_CFIR_CTRL_A_BE4 + (path << 8), + B_CFIR_CTRL_KIDX1_EN); + } else { + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG_BE4, + B_NCTL_CFG_BE4_SPAGE, 0x1); + rtw89_phy_write32_set(rtwdev, R_CFIR_CTRL_A_BE4 + (path << 8), + B_CFIR_CTRL_KIDX0_EN); + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG_BE4, + B_NCTL_CFG_BE4_SPAGE, 0x0); + } + + rtw89_phy_write32(rtwdev, R_TX_IQC_A_BE4 + (path << 8), + iqk_info->iqc_bak[path]); + } +} + static void rtw8922d_rfk_scan(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, bool start) { - if (start) + if (start) { __rtw8922d_tssi_disable(rtwdev, rtwvif_link->phy_idx); - else + __rtw8922d_txiqk_disable(rtwdev); + } else { __rtw8922d_tssi_enable(rtwdev, rtwvif_link->phy_idx); + __rtw8922d_txiqk_enable(rtwdev); + } } static void rtw8922d_rfk_track(struct rtw89_dev *rtwdev) From ce18a0cbc39b47f4b642b2e28fe1e37176f0b074 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 17 Jul 2026 14:19:07 +0800 Subject: [PATCH 139/403] wifi: rtw89: wow: extend timeout unit to avoid SER false alarm With original timeout unit, it might trigger SER false alarm causing WiFi card lost. Extend timeout unit to avoid wrong SER. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717061910.54466-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index 92c30c7f9fb2..339706d99f6c 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -1022,7 +1022,7 @@ #define B_BE_PL1_IGNORE_HOT_RST BIT(30) #define B_BE_PL1_TIMER_UNIT_MASK GENMASK(19, 17) #define PCIE_SER_TIMER_UNIT 0x2 -#define PCIE_SER_WOW_TIMER_UNIT 0x4 +#define PCIE_SER_WOW_TIMER_UNIT 0x7 #define B_BE_PL1_TIMER_CLEAR BIT(0) #define R_BE_REG_PL1_MASK 0x34B0 From 238bc3141cde49aef7d815d277d4a3282160c4fe Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 17 Jul 2026 14:19:08 +0800 Subject: [PATCH 140/403] wifi: rtw89: pci: set PCIe maximum TS1 for RTL8922DE Set TS1 (training sequence 1) to 1024 when PCIe enters recovery state to improve RF interference while entering and leaving L1ss. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717061910.54466-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.h | 1 + drivers/net/wireless/realtek/rtw89/pci_be.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index 339706d99f6c..0e1557cedd20 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -1138,6 +1138,7 @@ #define RTW89_PCIE_GEN1_SPEED 0x01 #define RTW89_PCIE_GEN2_SPEED 0x02 #define RTW89_PCIE_PHY_RATE 0x82 +#define RTW89_PCIE_EXTENDED_SYNCH BIT(7) #define RTW89_PCIE_PHY_RATE_MASK GENMASK(1, 0) #define RTW89_PCIE_LINK_CHANGE_SPEED 0xA0 #define RTW89_PCIE_L1SS_STS_V1 0x0168 diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c index 6390980b8ee0..d338bf633272 100644 --- a/drivers/net/wireless/realtek/rtw89/pci_be.c +++ b/drivers/net/wireless/realtek/rtw89/pci_be.c @@ -85,13 +85,22 @@ static void _patch_pcie_power_wake_be(struct rtw89_dev *rtwdev, bool power_up) static void _patch_pre_init_be(struct rtw89_dev *rtwdev) { + struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; struct rtw89_hal *hal = &rtwdev->hal; + struct pci_dev *pdev = rtwpci->pdev; - if (!(rtwdev->chip->chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)) + if (rtwdev->chip->chip_id != RTL8922D) return; + if (hal->cid != RTL8922D_CID7090) + goto set_ts1; + rtw89_write16_clr(rtwdev, R_RAC_DIRECT_OFFSET_BE_LANE0_G2 + RAC_ANA14 * RAC_MULT, EIEOS_L1SS_WAIT_CLKRDY); + +set_ts1: + pci_clear_and_set_config_dword(pdev, RTW89_PCIE_L1_STS_V1, + 0, RTW89_PCIE_EXTENDED_SYNCH); } static void rtw89_pci_set_io_rcy_be(struct rtw89_dev *rtwdev) From e738d2ac80c945c8fdf91b673e9cadeb394bfe6c Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 17 Jul 2026 14:19:09 +0800 Subject: [PATCH 141/403] wifi: rtw89: 8922d: reduce IO in power-on function To improve initial time, merge some IO to reduce IO times. Two registers are: 1. R_BE_SYS_PW_CTRL: 0x4[12:11]=0, 0x4[18]=1, 0x4[15]=0, and 0x4[10]=0 2. R_BE_SYS_ADIE_PAD_PWR_CTRL: Merge 0x18[6] and 0x18[5] Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717061910.54466-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index fde4117bcd49..fd4c92ba3f7b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -518,14 +518,15 @@ static int rtw8922d_pwr_on_func(struct rtw89_dev *rtwdev) } begin: - rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_AFSM_WLSUS_EN | - B_BE_AFSM_PCIE_SUS_EN); - rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_DIS_WLBT_PDNSUSEN_SOPC); - rtw89_write32_set(rtwdev, R_BE_WLLPS_CTRL, B_BE_DIS_WLBT_LPSEN_LOPC); + val32 = rtw89_read32(rtwdev, R_BE_SYS_PW_CTRL); + val32 &= ~(B_BE_AFSM_WLSUS_EN | B_BE_AFSM_PCIE_SUS_EN | B_BE_APFM_SWLPS); + val32 |= B_BE_DIS_WLBT_PDNSUSEN_SOPC; if (hal->cid != RTL8922D_CID7090) - rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APDM_HPDN); + val32 &= ~B_BE_APDM_HPDN; + rtw89_write32(rtwdev, R_BE_SYS_PW_CTRL, val32); + + rtw89_write32_set(rtwdev, R_BE_WLLPS_CTRL, B_BE_DIS_WLBT_LPSEN_LOPC); rtw89_write32_clr(rtwdev, R_BE_FWS1ISR, B_BE_FS_WL_HW_RADIO_OFF_INT); - rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS); ret = read_poll_timeout(rtw89_read32, val32, val32 & B_BE_RDY_SYSPWR, 1000, 3000000, false, rtwdev, R_BE_SYS_PW_CTRL); @@ -581,14 +582,13 @@ static int rtw8922d_pwr_on_func(struct rtw89_dev *rtwdev) if (ret) return ret; - rtw89_write32_set(rtwdev, R_BE_SYS_ADIE_PAD_PWR_CTRL, B_BE_SYM_PADPDN_WL_RFC1_1P3); + rtw89_write32_set(rtwdev, R_BE_SYS_ADIE_PAD_PWR_CTRL, + B_BE_SYM_PADPDN_WL_RFC1_1P3 | B_BE_SYM_PADPDN_WL_RFC0_1P3); ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0x40, 0x40); if (ret) return ret; - rtw89_write32_set(rtwdev, R_BE_SYS_ADIE_PAD_PWR_CTRL, B_BE_SYM_PADPDN_WL_RFC0_1P3); - ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0x20, 0x20); if (ret) return ret; From b3dc0a37fd3ad1feb248e7d4c51c49799359b0e2 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 17 Jul 2026 14:19:10 +0800 Subject: [PATCH 142/403] wifi: rtw89: phy: set CFR to manual mode for some 2GHz channels The CFR (Channel Frequency Response) manual mode condition for 2GHz is missing to limit on bandwidth for specific channels, which are channel 13 with 20MHz bandwidth and channel 11 with 40MHz bandwidth. Also add a band check to avoid affecting 5GHz/6GHz bands. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717061910.54466-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy_be.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index e471409a4b8f..c06927d1dc78 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -707,13 +707,19 @@ void rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { + bool cfr_manual_en = false; u32 reg; u32 val; val = rtw89_phy_bb_wrap_be_bandedge_decision(rtwdev, chan); + if (chan->band_type == RTW89_BAND_2G && + ((chan->primary_channel == 13 && chan->band_width == RTW89_CHANNEL_WIDTH_20) || + (chan->primary_channel == 11 && chan->band_width == RTW89_CHANNEL_WIDTH_40))) + cfr_manual_en = true; + rtw89_phy_write32_idx(rtwdev, R_TX_CFR_MANUAL_EN_BE4, B_TX_CFR_MANUAL_EN_BE4_M, - chan->primary_channel == 13, phy_idx); + cfr_manual_en, phy_idx); reg = rtw89_mac_reg_by_idx(rtwdev, R_BANDEDGE_DBWX_BE4, phy_idx); rtw89_write32_mask(rtwdev, reg, B_BANDEDGE_DBW20_BE4, val & BIT(0)); From 0427315a6ed1478199dadaed9b4d0f633f702391 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:27 +0800 Subject: [PATCH 143/403] wifi: rtw89: coex: Add version 107 TX/RX info for firmware feature The previous version 7 format is for Dual-Bluetooth using. This patch is for single Bluetooth solution using. Driver will summary Wi-Fi now status for firmware to train RF parameters & TDMA mechanism. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 27 ++++-- drivers/net/wireless/realtek/rtw89/fw.c | 112 ++++++++++++++++------ drivers/net/wireless/realtek/rtw89/fw.h | 24 +++++ 3 files changed, 126 insertions(+), 37 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index b044b25aeade..3addcdfd89eb 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -3081,10 +3081,15 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) if (ver->drvinfo_ver > 1) index = 3; + if (ver->fcxtrx == 0) + return; + if (ver->fcxtrx == 7) rtw89_fw_h2c_cxdrv_trx_v7(rtwdev, index); else if (ver->fcxtrx == 9) rtw89_fw_h2c_cxdrv_trx_v9(rtwdev, index); + else if (ver->fcxtrx == 107) + rtw89_fw_h2c_cxdrv_trx_v107(rtwdev, index); break; case CXDRVINFO_RFK: if (ver->drvinfo_ver != 0) @@ -3096,7 +3101,7 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) if (ver->drvinfo_ver == 3) index = 4; - if (ver->fcxtrx == 7) + if (ver->fcxtrx == 7 || ver->fcxtrx == 107) rtw89_fw_h2c_cxtxpwr_v7(rtwdev, index); else if (ver->fcxtrx == 9) rtw89_fw_h2c_cxtxpwr_v9(rtwdev, index); @@ -3358,7 +3363,8 @@ static void _set_wl_tx_power(struct rtw89_dev *rtwdev, u32 level, u8 phy_map) dm->rf_trx_para.wl_tx_power[RTW89_PHY_0], dm->rf_trx_para.wl_tx_power[RTW89_PHY_1]); - if (ver->fcxtrx == 7 && chip->chip_id == RTL8922A) { + if ((ver->fcxtrx == 7 && chip->chip_id == RTL8922A) || + ver->fcxtrx == 107) { _fw_set_drv_info(rtwdev, CXDRVINFO_TXPWR); } else if (ver->fcxtrx == 9) { _fw_set_drv_info(rtwdev, CXDRVINFO_TXPWR); @@ -3632,12 +3638,17 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) u8 bid = BTC_BT_1ST, lv; u32 wl_stb_chg; - if (ver->fcxtrx == 9 && chip->rf_para_ulink_v9) { - ul_para_num = chip->rf_para_ulink_num_v9; - dl_para_num = chip->rf_para_dlink_num_v9; - _set_rf_trx_para_v9(rtwdev); - return; - } else if (ver->fcxtrx == 0 && chip->rf_para_ulink_v0) { + if (ver->fcxtrx == 9) { + /* Early v9 need to assign UL/DL RF para at driver */ + if (chip->rf_para_ulink_v9) { + ul_para_num = chip->rf_para_ulink_num_v9; + dl_para_num = chip->rf_para_dlink_num_v9; + } else { + _set_rf_trx_para_v9(rtwdev); + return; + } + } else if ((ver->fcxtrx == 0 || ver->fcxtrx == 7 || ver->fcxtrx == 107) && + chip->rf_para_ulink_v0) { ul_para_num = chip->rf_para_ulink_num_v0; dl_para_num = chip->rf_para_dlink_num_v0; } else { diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index ad87484f51a7..533fb782bda8 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6678,16 +6678,12 @@ int rtw89_fw_h2c_cxdrv_ctrl_v9(struct rtw89_dev *rtwdev, u8 type) return ret; } -int rtw89_fw_h2c_cxdrv_trx_v7(struct rtw89_dev *rtwdev, u8 type) +static void rtw89_btc_load_trx_para(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_rf_trx_para_v9 rf_para = btc->dm.rf_trx_para; struct rtw89_btc_trx_info *trx = &btc->dm.trx_info; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - struct rtw89_h2c_cxtrx_v7 *h2c; - u32 len = sizeof(*h2c); - struct sk_buff *skb; - int ret; u8 i; for (i = 0; i < RTW89_PHY_NUM; i++) { @@ -6695,19 +6691,38 @@ int rtw89_fw_h2c_cxdrv_trx_v7(struct rtw89_dev *rtwdev, u8 type) RTW89_BTC_WL_DEF_TX_PWR); trx->wl_rx_gain[i] = u32_get_bits(rf_para.wl_rx_gain[i], RTW89_BTC_WL_DEF_TX_PWR); + if (btc->ver->fcxtrx == 107) + break; } + for (i = 0; i < BTC_ALL_BT; i++) { trx->bt_tx_power[i] = u32_get_bits(rf_para.bt_tx_power[i], RTW89_BTC_WL_DEF_TX_PWR); trx->bt_rx_gain[i] = u32_get_bits(rf_para.bt_rx_gain[i], RTW89_BTC_WL_DEF_TX_PWR); + if (btc->ver->fcxtrx == 107) + break; + trx->zb_tx_power[i] = u32_get_bits(rf_para.zb_tx_power[i], RTW89_BTC_WL_DEF_TX_PWR); trx->zb_rx_gain[i] = u32_get_bits(rf_para.zb_rx_gain[i], RTW89_BTC_WL_DEF_TX_PWR); } + trx->cn = wl->cn_report; trx->nhm = wl->nhm.pwr; +} + +int rtw89_fw_h2c_cxdrv_trx_v7(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_trx_info *trx = &btc->dm.trx_info; + struct rtw89_h2c_cxtrx_v7 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + rtw89_btc_load_trx_para(rtwdev); skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { @@ -6718,7 +6733,7 @@ int rtw89_fw_h2c_cxdrv_trx_v7(struct rtw89_dev *rtwdev, u8 type) h2c = (struct rtw89_h2c_cxtrx_v7 *)skb->data; h2c->hdr.type = type; - h2c->hdr.ver = btc->ver->fcxtrx; + h2c->hdr.ver = 7; h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7; h2c->v7_u8.tx_lvl = trx->tx_lvl; @@ -6761,33 +6776,14 @@ int rtw89_fw_h2c_cxdrv_trx_v7(struct rtw89_dev *rtwdev, u8 type) int rtw89_fw_h2c_cxdrv_trx_v9(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_rf_trx_para_v9 rf_para = btc->dm.rf_trx_para; struct rtw89_btc_trx_info *trx = &btc->dm.trx_info; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_h2c_cxtrx_v9 *h2c; u32 len = sizeof(*h2c); struct sk_buff *skb; int ret; u8 i; - for (i = 0; i < RTW89_PHY_NUM; i++) { - trx->wl_tx_power[i] = u32_get_bits(rf_para.wl_tx_power[i], - RTW89_BTC_WL_DEF_TX_PWR); - trx->wl_rx_gain[i] = u32_get_bits(rf_para.wl_rx_gain[i], - RTW89_BTC_WL_DEF_TX_PWR); - } - for (i = 0; i < BTC_ALL_BT; i++) { - trx->bt_tx_power[i] = u32_get_bits(rf_para.bt_tx_power[i], - RTW89_BTC_WL_DEF_TX_PWR); - trx->bt_rx_gain[i] = u32_get_bits(rf_para.bt_rx_gain[i], - RTW89_BTC_WL_DEF_TX_PWR); - trx->zb_tx_power[i] = u32_get_bits(rf_para.zb_tx_power[i], - RTW89_BTC_WL_DEF_TX_PWR); - trx->zb_rx_gain[i] = u32_get_bits(rf_para.zb_rx_gain[i], - RTW89_BTC_WL_DEF_TX_PWR); - } - trx->cn = wl->cn_report; - trx->nhm = wl->nhm.pwr; + rtw89_btc_load_trx_para(rtwdev); skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { @@ -6798,7 +6794,7 @@ int rtw89_fw_h2c_cxdrv_trx_v9(struct rtw89_dev *rtwdev, u8 type) h2c = (struct rtw89_h2c_cxtrx_v9 *)skb->data; h2c->hdr.type = type; - h2c->hdr.ver = btc->ver->fcxtrx; + h2c->hdr.ver = 9; h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7; h2c->v9_u8.tx_lvl = trx->tx_lvl; @@ -6844,6 +6840,64 @@ int rtw89_fw_h2c_cxdrv_trx_v9(struct rtw89_dev *rtwdev, u8 type) return ret; } +int rtw89_fw_h2c_cxdrv_trx_v107(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_trx_info *trx = &btc->dm.trx_info; + struct rtw89_h2c_cxtrx_v107 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + rtw89_btc_load_trx_para(rtwdev); + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxtrx_v107\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxtrx_v107 *)skb->data; + + h2c->hdr.type = type; + h2c->hdr.ver = 7; + h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7; + + h2c->v107_u8.tx_lvl = trx->tx_lvl; + h2c->v107_u8.rx_lvl = trx->rx_lvl; + h2c->v107_u8.wl_rssi = trx->wl_rssi; + h2c->v107_u8.bt_rssi = trx->bt_rssi; + h2c->v107_u8.wl_tx_power = trx->bt_tx_power[RTW89_PHY_0]; + h2c->v107_u8.wl_rx_gain = trx->wl_rx_gain[RTW89_PHY_0]; + h2c->v107_u8.bt_tx_power = trx->bt_tx_power[BTC_BT_1ST]; + h2c->v107_u8.bt_rx_gain = trx->bt_rx_gain[BTC_BT_1ST]; + h2c->v107_u8.cn = trx->cn; + h2c->v107_u8.nhm = trx->nhm; + h2c->v107_u8.bt_profile = trx->bt_profile; + h2c->v107_u8.rsvd2 = trx->rsvd2; + h2c->v7_le.tx_rate = cpu_to_le16(trx->tx_rate); + h2c->v7_le.rx_rate = cpu_to_le16(trx->rx_rate); + h2c->v7_le.tx_tp = cpu_to_le32(trx->tx_tp); + h2c->v7_le.rx_tp = cpu_to_le32(trx->rx_tp); + h2c->v7_le.rx_err_ratio = cpu_to_le32(trx->rx_err_ratio); + + 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; +} + #define H2C_LEN_CXDRVINFO_RFK (4 + H2C_LEN_CXDRVHDR) int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev, u8 type) { @@ -6908,7 +6962,7 @@ int rtw89_fw_h2c_cxtxpwr_v7(struct rtw89_dev *rtwdev, u8 type) h2c = (struct rtw89_h2c_cxtxpwr_v7 *)skb->data; h2c->hdr.type = type; - h2c->hdr.ver = btc->ver->fcxtrx; + h2c->hdr.ver = 7; h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7; h2c->pwr = rp.wl_tx_power[RTW89_PHY_0] & 0xff; @@ -6948,7 +7002,7 @@ int rtw89_fw_h2c_cxtxpwr_v9(struct rtw89_dev *rtwdev, u8 type) h2c = (struct rtw89_h2c_cxtxpwr_v9 *)skb->data; h2c->hdr.type = type; - h2c->hdr.ver = btc->ver->fcxtrx; + h2c->hdr.ver = 9; h2c->hdr.len = sizeof(*h2c) - H2C_LEN_CXDRVHDR_V7; if (dm->wl_tx_pwr_phy_map == BIT(RTW89_PHY_1)) h2c->pwr = rp.wl_tx_power[RTW89_PHY_1] & 0xff; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 2654f8fd0e51..67e9c8daff5f 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2517,6 +2517,23 @@ struct rtw89_btc_trx_info_v7_u8 { u8 rsvd2; } __packed; +struct rtw89_btc_trx_info_v107_u8 { + u8 tx_lvl; + u8 rx_lvl; + u8 wl_rssi; + u8 bt_rssi; + + s8 wl_tx_power; + s8 wl_rx_gain; + s8 bt_tx_power; + s8 bt_rx_gain; + + u8 cn; + s8 nhm; + u8 bt_profile; + u8 rsvd2; +} __packed; + struct rtw89_btc_trx_info_le { __le16 tx_rate; __le16 rx_rate; @@ -2538,6 +2555,12 @@ struct rtw89_h2c_cxtrx_v7 { struct rtw89_btc_trx_info_le v7_le; } __packed; +struct rtw89_h2c_cxtrx_v107 { + struct rtw89_h2c_cxhdr_v7 hdr; + struct rtw89_btc_trx_info_v107_u8 v107_u8; + struct rtw89_btc_trx_info_le v7_le; +} __packed; + struct rtw89_h2c_cxtxpwr_v7 { struct rtw89_h2c_cxhdr_v7 hdr; u8 pwr; @@ -5400,6 +5423,7 @@ int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_ctrl_v9(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_trx_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_trx_v9(struct rtw89_dev *rtwdev, u8 type); +int rtw89_fw_h2c_cxdrv_trx_v107(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxtxpwr_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxtxpwr_v9(struct rtw89_dev *rtwdev, u8 type); From 4bedf26e132c79ceb9a19556fd198f2b15b406c4 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:28 +0800 Subject: [PATCH 144/403] wifi: rtw89: coex: Add version 9 report control info WiFi firmware will save its build date/time and package into C2H event then send to driver. It helps to analyze what kind of firmware was loaded now. Remove unnecessary memory set. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 166 +++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/core.h | 18 +++ 2 files changed, 178 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 3addcdfd89eb..1bfd3f3d67de 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1670,6 +1670,10 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, pfinfo = &pfwinfo->rpt_ctrl.finfo.v7; pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v7); fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v7.fver; + } else if (ver->fcxbtcrpt == 9) { + pfinfo = &pfwinfo->rpt_ctrl.finfo.v9; + pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v9); + fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v9.fver; } else if (ver->fcxbtcrpt == 11) { pfinfo = &pfwinfo->rpt_ctrl.finfo.v11; pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v11); @@ -2075,6 +2079,46 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, else dm->error.map.h2c_c2h_buffer_mismatch = false; + _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); + _chk_btc_err(rtwdev, BTC_DCNT_RPT_HANG, val1); + _chk_btc_err(rtwdev, BTC_DCNT_WL_FW_VER_MATCH, 0); + _chk_btc_err(rtwdev, BTC_DCNT_BTTX_HANG, 0); + } else if (ver->fcxbtcrpt == 9) { + prpt->v9 = pfwinfo->rpt_ctrl.finfo.v9; + pfwinfo->rpt_en_map = le32_to_cpu(prpt->v9.rpt_info.en); + wl->ver_info.fw_coex = le32_to_cpu(prpt->v9.rpt_info.cx_ver); + wl->ver_info.fw = le32_to_cpu(prpt->v9.rpt_info.fw_ver); + + memcpy(wl->ver_info.build_time, + prpt->v9.build_time, + sizeof(wl->ver_info.build_time)); + memcpy(wl->ver_info.build_date, + prpt->v9.build_date, + sizeof(wl->ver_info.build_date)); + + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) + memcpy(&dm->gnt_val[i], &prpt->v9.gnt_val[i], + sizeof(dm->gnt_val[i])); + + bt->bcnt[BTC_BCNT_HIPRI_TX] = + le16_to_cpu(prpt->v9.bt_cnt[BTC_BCNT_HI_TX_V105]); + bt->bcnt[BTC_BCNT_HIPRI_RX] = + le16_to_cpu(prpt->v9.bt_cnt[BTC_BCNT_HI_RX_V105]); + bt->bcnt[BTC_BCNT_LOPRI_TX] = + le16_to_cpu(prpt->v9.bt_cnt[BTC_BCNT_LO_TX_V105]); + bt->bcnt[BTC_BCNT_LOPRI_RX] = + le16_to_cpu(prpt->v9.bt_cnt[BTC_BCNT_LO_RX_V105]); + + val1 = le16_to_cpu(prpt->v9.bt_cnt[BTC_BCNT_POLLUTED_V105]); + if (val1 > bt->bcnt[BTC_BCNT_POLUT_NOW]) + val1 -= bt->bcnt[BTC_BCNT_POLUT_NOW]; /* diff */ + + bt->bcnt[BTC_BCNT_POLUT_DIFF] = val1; + bt->bcnt[BTC_BCNT_POLUT_NOW] = + le16_to_cpu(prpt->v9.bt_cnt[BTC_BCNT_POLLUTED_V105]); + + dm->pta_owner = prpt->v9.pta_owner; + _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); _chk_btc_err(rtwdev, BTC_DCNT_RPT_HANG, val1); _chk_btc_err(rtwdev, BTC_DCNT_WL_FW_VER_MATCH, 0); @@ -2085,14 +2129,9 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw_coex = le32_to_cpu(prpt->v11.rpt_info.cx_ver); wl->ver_info.fw = le32_to_cpu(prpt->v11.rpt_info.fw_ver); - memset(wl->ver_info.build_time, 0, - sizeof(wl->ver_info.build_time)); memcpy(wl->ver_info.build_time, prpt->v11.build_time, sizeof(wl->ver_info.build_time)); - - memset(wl->ver_info.build_date, 0, - sizeof(wl->ver_info.build_date)); memcpy(wl->ver_info.build_date, prpt->v11.build_date, sizeof(wl->ver_info.build_date)); @@ -2881,6 +2920,7 @@ static void rtw89_btc_fw_en_rpt(struct rtw89_dev *rtwdev, if (btc->ver->fcxbtcrpt == 7 || btc->ver->fcxbtcrpt == 8 || + btc->ver->fcxbtcrpt == 9 || btc->ver->fcxbtcrpt == 11) { r.v8.type = SET_REPORT_EN; r.v8.fver = btc->ver->fcxbtcrpt; @@ -9175,9 +9215,10 @@ static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw); id_branch = FIELD_GET(GENMASK(7, 0), wl->ver_info.fw); p += scnprintf(p, end - p, - " %-15s : WL_FW:%d.%d.%d.%d, BT_FW:0x%x(%s)\n", + " %-15s : WL_FW:%d.%d.%d.%d(built:%.12s-%.12s), BT_FW:0x%x(%s)\n", "[sub_module]", ver_main, ver_sub, ver_hotfix, id_branch, + wl->ver_info.build_time, wl->ver_info.build_date, bt->ver_info.fw, bt->run_patch_code ? "patch" : "ROM"); if (ver->fcxinit == 7) { @@ -12138,6 +12179,117 @@ static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) return p - buf; } +static int _show_summary_v9(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) +{ + struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo; + struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; + struct rtw89_btc_fbtc_rpt_ctrl_v9 *prptctrl = NULL; + struct rtw89_btc_cx *cx = &rtwdev->btc.cx; + struct rtw89_btc_dm *dm = &rtwdev->btc.dm; + struct rtw89_btc_wl_info *wl = &cx->wl; + u32 *cnt = rtwdev->btc.dm.cnt_notify; + char *p = buf, *end = buf + bufsz; + u32 cnt_sum = 0; + u8 i; + + if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) + return 0; + + p += scnprintf(p, end - p, "%s", + "\n\r========== [Statistics] =========="); + + pcinfo = &pfwinfo->rpt_ctrl.cinfo; + if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF && + !wl->status.map.rf_off) { + prptctrl = &pfwinfo->rpt_ctrl.finfo.v9; + + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le16_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le16_to_cpu(prptctrl->rpt_info.cnt_c2h), + le16_to_cpu(prptctrl->rpt_info.len_c2h)); + + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le16_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en)); + + if (dm->error.map.wl_fw_hang) + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + + p += scnprintf(p, end - p, + "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + + p += scnprintf(p, end - p, + "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", + "[RFK/LPS]", wl->wcnt[BTC_WCNT_RFK_REQ], + wl->wcnt[BTC_WCNT_RFK_GO], + wl->wcnt[BTC_WCNT_RFK_REJECT], + wl->wcnt[BTC_WCNT_RFK_TIMEOUT], + wl->rfk_info.proc_time); + + p += scnprintf(p, end - p, ", bt_rfk[req:%d]", + le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + + p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); + } else { + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", + "[summary]", + pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, + pfwinfo->cnt_c2h, + wl->status.map.lps, wl->status.map.rf_off); + } + + for (i = 0; i < BTC_NCNT_NUM; i++) + cnt_sum += dm->cnt_notify[i]; + + p += scnprintf(p, end - p, + "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", + cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); + + p += scnprintf(p, end - p, + "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", + "[notify_cnt]", + cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], + cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], + cnt[BTC_NCNT_SPECIAL_PACKET]); + + p += scnprintf(p, end - p, + "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], + rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], + cnt[BTC_NCNT_COUNTRYCODE]); + + return p - buf; +} + static int _show_summary_v11(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo; @@ -12311,6 +12463,8 @@ ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += _show_summary_v7(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 8) p += _show_summary_v8(rtwdev, p, end - p); + else if (ver->fcxbtcrpt == 9) + p += _show_summary_v9(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 11) p += _show_summary_v11(rtwdev, p, end - p); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 2d9113cb43f9..fb4c95ed1904 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2790,6 +2790,23 @@ struct rtw89_btc_fbtc_rpt_ctrl_v8 { struct rtw89_btc_fbtc_rpt_ctrl_bt_mailbox bt_mbx_info; } __packed; +#define RTW89_BTC_TIME_DATE_FMT 12 +struct rtw89_btc_fbtc_rpt_ctrl_v9 { + u8 fver; + u8 ext_req_exist; + u8 pta_owner; + u8 rsvd; + + u8 build_time[RTW89_BTC_TIME_DATE_FMT]; + u8 build_date[RTW89_BTC_TIME_DATE_FMT]; + + u8 gnt_val[RTW89_PHY_NUM][4]; /* gwl/gbt012 refer to struct btc_gnt_ctrl */ + __le16 bt_cnt[BTC_BCNT_STA_MAX_V105]; + + struct rtw89_btc_fbtc_rpt_ctrl_info_v8 rpt_info; + struct rtw89_btc_fbtc_rpt_ctrl_bt_mailbox bt_mbx_info; +} __packed; + struct rtw89_btc_fbtc_rpt_ctrl_v11 { u8 fver; u8 rsvd0; @@ -2817,6 +2834,7 @@ union rtw89_btc_fbtc_rpt_ctrl_ver_info { struct rtw89_btc_fbtc_rpt_ctrl_v105 v105; struct rtw89_btc_fbtc_rpt_ctrl_v7 v7; struct rtw89_btc_fbtc_rpt_ctrl_v8 v8; + struct rtw89_btc_fbtc_rpt_ctrl_v9 v9; struct rtw89_btc_fbtc_rpt_ctrl_v11 v11; }; From b5bfb43844e1bb5681ad23d00a4d6930c4778fb1 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:29 +0800 Subject: [PATCH 145/403] wifi: rtw89: coex: Fix unexpected grant-signal assignee The C2H report is for knowing what the grant-signal setting is now, not for applying new grant-signal setting. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 1bfd3f3d67de..5f2658eb516c 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1935,8 +1935,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, dm->wl_fw_cx_offload = !!le32_to_cpu(prpt->v4.wl_fw_info.cx_offload); for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) - memcpy(&dm->gnt_set[i], &prpt->v4.gnt_val[i], - sizeof(dm->gnt_set[i])); + memcpy(&dm->gnt_val[i], &prpt->v4.gnt_val[i], + sizeof(dm->gnt_val[i])); bt->bcnt[BTC_BCNT_HIPRI_TX] = le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_HI_TX]); @@ -1967,8 +1967,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, dm->wl_fw_cx_offload = 0; for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) - memcpy(&dm->gnt_set[i], &prpt->v5.gnt_val[i], - sizeof(dm->gnt_set[i])); + memcpy(&dm->gnt_val[i], &prpt->v5.gnt_val[i], + sizeof(dm->gnt_val[i])); bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_HI_TX]); @@ -1994,8 +1994,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, dm->wl_fw_cx_offload = 0; for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) - memcpy(&dm->gnt_set[i], &prpt->v105.gnt_val[i], - sizeof(dm->gnt_set[i])); + memcpy(&dm->gnt_val[i], &prpt->v105.gnt_val[i], + sizeof(dm->gnt_val[i])); bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v105.bt_cnt[BTC_BCNT_HI_TX_V105]); @@ -2020,8 +2020,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw = le32_to_cpu(prpt->v7.rpt_info.fw_ver); for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) - memcpy(&dm->gnt_set[i], &prpt->v7.gnt_val[i], - sizeof(dm->gnt_set[i])); + memcpy(&dm->gnt_val[i], &prpt->v7.gnt_val[i], + sizeof(dm->gnt_val[i])); bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v7.bt_cnt[BTC_BCNT_HI_TX_V105]); @@ -2052,8 +2052,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw = le32_to_cpu(prpt->v8.rpt_info.fw_ver); for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) - memcpy(&dm->gnt_set[i], &prpt->v8.gnt_val[i], - sizeof(dm->gnt_set[i])); + memcpy(&dm->gnt_val[i], &prpt->v8.gnt_val[i], + sizeof(dm->gnt_val[i])); bt->bcnt[BTC_BCNT_HIPRI_TX] = le16_to_cpu(prpt->v8.bt_cnt[BTC_BCNT_HI_TX_V105]); @@ -2137,8 +2137,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, sizeof(wl->ver_info.build_date)); for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) - memcpy(&dm->gnt_set[i], &prpt->v11.gnt_val[i][0], - sizeof(dm->gnt_set[i])); + memcpy(&dm->gnt_val[i], &prpt->v11.gnt_val[i][0], + sizeof(dm->gnt_val[i])); for (i = BTC_BT_1ST; i <= BTC_BT_EXT; i++) { if (i == BTC_BT_EXT) { From 00961e2705b70c6af69e2bf83991e2349ff2a569 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:30 +0800 Subject: [PATCH 146/403] wifi: rtw89: coex: Branch out version 105 firmware report map index RTL8852B in firmware 0.29.133.X do not support BT-TX-PWR report, re-index and branch out to version 105. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 5f2658eb516c..b73040428c58 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -2707,6 +2707,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) case 3: case 4: case 5: + case 105: bit_map = BIT(5); break; default: @@ -2723,6 +2724,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) case 3: case 4: case 5: + case 105: bit_map = BIT(6); break; default: @@ -2737,6 +2739,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) case 3: case 4: case 5: + case 105: bit_map = BIT(7); break; default: @@ -2749,6 +2752,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) case 1: case 2: case 3: + case 105: break; case 4: case 5: @@ -2765,6 +2769,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) bit_map = BIT(7); break; case 3: + case 105: bit_map = BIT(8); break; case 4: @@ -2778,6 +2783,8 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) case RPT_EN_TEST: if (ver->frptmap == 5) bit_map = BIT(10); + else if (ver->frptmap == 105) + bit_map = BIT(9); else bit_map = BIT(31); break; @@ -2789,6 +2796,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) bit_map = GENMASK(2, 0); break; case 3: + case 105: bit_map = GENMASK(2, 0) | BIT(8); break; case 4: @@ -2809,6 +2817,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) bit_map = GENMASK(6, 3) | BIT(8); break; case 3: + case 105: bit_map = GENMASK(7, 3); break; case 4: @@ -2833,6 +2842,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) break; case 4: case 5: + case 105: bit_map = GENMASK(9, 0); break; default: @@ -2849,6 +2859,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map) bit_map = GENMASK(6, 2) | BIT(8); break; case 3: + case 105: bit_map = GENMASK(8, 2); break; case 4: From 49989a3fef378fffc60085864b33291c48706b7e Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:31 +0800 Subject: [PATCH 147/403] wifi: rtw89: coex: Refine chip initial related structure Due to the firmware version become more and more, the version divided branch coding method make the related code scattered everywhere, and too much version macro, rearrange the code, assign the value to version format only when H2C commands. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 121 ++++---------- drivers/net/wireless/realtek/rtw89/core.h | 89 +++++++++-- drivers/net/wireless/realtek/rtw89/fw.c | 101 +++++++++--- drivers/net/wireless/realtek/rtw89/fw.h | 5 + drivers/net/wireless/realtek/rtw89/rtw8851b.c | 120 +++++--------- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 62 +++----- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 62 +++----- .../net/wireless/realtek/rtw89/rtw8852bt.c | 65 ++++---- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 62 +++----- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 53 +++---- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 149 +++++++++--------- 11 files changed, 413 insertions(+), 476 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index b73040428c58..ad52274a3979 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1142,13 +1142,7 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type) if (type & BTC_RESET_MDINFO) { memset(&btc->mdinfo, 0, sizeof(btc->mdinfo)); - - if (ver->fcxinit == 10) - btc->mdinfo.md_v10.ant.isolation = RTW89_BTC_DEFAULT_ANISO; - else if (ver->fcxinit == 7) - btc->mdinfo.md_v7.ant.isolation = RTW89_BTC_DEFAULT_ANISO; - else - btc->mdinfo.md.ant.isolation = RTW89_BTC_DEFAULT_ANISO; + btc->mdinfo.ant.isolation = RTW89_BTC_DEFAULT_ANISO; } } @@ -1169,27 +1163,22 @@ static void _get_reg_status(struct rtw89_dev *rtwdev, u8 type, u8 *val) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; - union rtw89_btc_module_info *md = &btc->mdinfo; + struct rtw89_btc_module *md = &btc->mdinfo; union rtw89_btc_fbtc_mreg_val *pmreg; u32 pre_agc_addr = R_BTC_BB_PRE_AGC_S1; u32 reg_val; - u8 idx, switch_type; - - if (ver->fcxinit == 7) - switch_type = md->md_v7.switch_type; - else - switch_type = md->md.switch_type; + u8 idx; if (btc->btg_pos == RF_PATH_A) pre_agc_addr = R_BTC_BB_PRE_AGC_S0; switch (type) { case BTC_CSTATUS_TXDIV_POS: - if (switch_type == BTC_SWITCH_INTERNAL) + if (md->bt0_sw_type == BTC_SWITCH_INTERNAL) *val = BTC_ANT_DIV_MAIN; break; case BTC_CSTATUS_RXDIV_POS: - if (switch_type == BTC_SWITCH_INTERNAL) + if (md->bt0_sw_type == BTC_SWITCH_INTERNAL) *val = BTC_ANT_DIV_MAIN; break; case BTC_CSTATUS_BB_GNT_MUX: @@ -3098,7 +3087,7 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) switch (index) { case CXDRVINFO_INIT: - if (ver->fcxinit == 7) + if (ver->fcxinit == 7 || ver->fcxinit == 107) rtw89_fw_h2c_cxdrv_init_v7(rtwdev, index); else if (ver->fcxinit == 10) rtw89_fw_h2c_cxdrv_init_v10(rtwdev, index); @@ -4206,14 +4195,7 @@ static bool _check_freerun(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; struct rtw89_btc_bt_hid_desc *hid = &bt_linfo->hid_desc; - union rtw89_btc_module_info *md = &btc->mdinfo; - const struct rtw89_btc_ver *ver = btc->ver; - u8 isolation; - - if (ver->fcxinit == 7) - isolation = md->md_v7.ant.isolation; - else - isolation = md->md.ant.isolation; + struct rtw89_btc_module *md = &btc->mdinfo; if (btc->ant_type == BTC_ANT_SHARED) { btc->dm.trx_para_level = 0; @@ -4237,7 +4219,7 @@ static bool _check_freerun(struct rtw89_dev *rtwdev) } /* TODO get isolation by BT psd */ - if (isolation >= BTC_FREERUN_ANTISO_MIN) { + if (md->ant.isolation >= BTC_FREERUN_ANTISO_MIN) { btc->dm.trx_para_level = 5; return true; } @@ -6177,21 +6159,14 @@ static void _wl_req_mac(struct rtw89_dev *rtwdev, u8 mac) static void _update_zb_coex_tbl(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; u32 zb_tbl0 = 0xda5a5a5a, zb_tbl1 = 0xda5a5a5a; u8 link_mode_chg = btc->cx.wl.link_mode_chg; u8 mode = btc->cx.wl.role_info.link_mode; - u8 wa_type; if (btc->dm.run_reason != BTC_RSN_NTFY_INIT && !link_mode_chg) return; - if (ver->fcxinit == 7) - wa_type = btc->mdinfo.md_v7.wa_type; - else - wa_type = btc->mdinfo.md.wa_type; - - if (!(wa_type & BTC_WA_HFP_ZB)) + if (!(btc->mdinfo.wa_type & BTC_WA_HFP_ZB)) return; if (btc->dm.tdd_bind.rf_band == BIT(RTW89_BAND_5G) || @@ -8106,40 +8081,26 @@ static void _set_init_info(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; - if (ver->fcxinit == 10) { - dm->init_info.init_v10.init_mode = wl->coex_mode; - dm->init_info.init_v10.wl_init_ok = wl->status.map.init_ok; - dm->init_info.init_v10.endian_type = BTC_PLATFORM_LITTLE_ENDIAN; + dm->init_info.init_mode = wl->coex_mode; + dm->init_info.wl_init_ok = wl->status.map.init_ok; + dm->init_info.endian_type = BTC_PLATFORM_LITTLE_ENDIAN; + dm->init_info.bt0_function = cx->bt0.func_type; + dm->init_info.bt1_function = cx->bt1.func_type; + dm->init_info.bt2_function = cx->bt_ext.func_type; + dm->init_info.pta_mode = RTW89_MAC_AX_COEX_RTK_MODE; + dm->init_info.pta_direction = RTW89_MAC_AX_COEX_INNER; + dm->init_info.wl_only = dm->wl_only; + dm->init_info.bt_only = dm->bt_only; + dm->init_info.wl_init_ok = wl->status.map.init_ok; + dm->init_info.cx_other = btc->cx.bt_ext.func_type; + dm->init_info.wl_guard_ch = chip->afh_guard_ch; + dm->init_info.dbcc_en = rtwdev->dbcc_en; - dm->init_info.init_v10.module = btc->mdinfo.md_v10; - - dm->init_info.init_v10.bt0_function = cx->bt0.func_type; - dm->init_info.init_v10.bt1_function = cx->bt1.func_type; - dm->init_info.init_v10.bt2_function = cx->bt_ext.func_type; - - dm->init_info.init_v10.pta_mode = RTW89_MAC_AX_COEX_RTK_MODE; - dm->init_info.init_v10.pta_direction = RTW89_MAC_AX_COEX_INNER; - } else if (ver->fcxinit == 7) { - dm->init_info.init_v7.wl_only = (u8)dm->wl_only; - dm->init_info.init_v7.bt_only = (u8)dm->bt_only; - dm->init_info.init_v7.wl_init_ok = (u8)wl->status.map.init_ok; - dm->init_info.init_v7.cx_other = btc->cx.bt_ext.func_type; - dm->init_info.init_v7.wl_guard_ch = chip->afh_guard_ch; - dm->init_info.init_v7.module = btc->mdinfo.md_v7; - } else { - dm->init_info.init.wl_only = (u8)dm->wl_only; - dm->init_info.init.bt_only = (u8)dm->bt_only; - dm->init_info.init.wl_init_ok = (u8)wl->status.map.init_ok; - dm->init_info.init.dbcc_en = rtwdev->dbcc_en; - dm->init_info.init.cx_other = btc->cx.bt_ext.func_type; - dm->init_info.init.wl_guard_ch = chip->afh_guard_ch; - dm->init_info.init.module = btc->mdinfo.md; - } + dm->init_info.module = btc->mdinfo; _fw_set_drv_info(rtwdev, CXDRVINFO_INIT); _fw_set_drv_info(rtwdev, CXDRVINFO_CTRL); @@ -9175,7 +9136,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { - union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; + struct rtw89_btc_module *md = &rtwdev->btc.mdinfo; const struct rtw89_chip_info *chip = rtwdev->chip; const struct rtw89_btc_ver *ver = rtwdev->btc.ver; struct rtw89_hal *hal = &rtwdev->hal; @@ -9184,7 +9145,6 @@ static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_wl_info *wl = &btc->cx.wl; u32 ver_main = 0, ver_sub = 0, ver_hotfix = 0, id_branch = 0; - u8 cv, rfe, iso, ant_num, ant_single_pos; char *p = buf, *end = buf + bufsz; if (!(dm->coex_info_map & BTC_COEX_INFO_CX)) @@ -9232,25 +9192,12 @@ static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) wl->ver_info.build_time, wl->ver_info.build_date, bt->ver_info.fw, bt->run_patch_code ? "patch" : "ROM"); - if (ver->fcxinit == 7) { - cv = md->md_v7.kt_ver; - rfe = md->md_v7.rfe_type; - iso = md->md_v7.ant.isolation; - ant_num = md->md_v7.ant.num; - ant_single_pos = md->md_v7.ant.single_pos; - } else { - cv = md->md.cv; - rfe = md->md.rfe_type; - iso = md->md.ant.isolation; - ant_num = md->md.ant.num; - ant_single_pos = md->md.ant.single_pos; - } - p += scnprintf(p, end - p, " %-15s : cv:%x, rfe_type:0x%x, ant_iso:%d, ant_pg:%d, %s", - "[hw_info]", cv, rfe, iso, ant_num, - ant_num > 1 ? "" : - ant_single_pos ? "1Ant_Pos:S1, " : "1Ant_Pos:S0, "); + "[hw_info]", + md->kt_ver, md->rfe_type, md->ant.isolation, + md->ant.num, md->ant.num > 1 ? "" : + md->ant.single_pos ? "1Ant_Pos:S1, " : "1Ant_Pos:S0, "); p += scnprintf(p, end - p, "3rd_coex:%d, dbcc:%d, tx_num:%d, rx_num:%d\n", @@ -9417,7 +9364,7 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_bt_info *bt = &cx->bt0; struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; - union rtw89_btc_module_info *md = &btc->mdinfo; + struct rtw89_btc_module *md = &btc->mdinfo; s8 br_dbm = bt->link_info.bt_txpwr_desc.br_dbm; s8 le_dbm = bt->link_info.bt_txpwr_desc.le_dbm; u8 hw_band = wl->role_info.pta_req_band; @@ -9425,23 +9372,17 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) char *p = buf, *end = buf + bufsz; u8 *afh = bt_linfo->afh_map; u8 *afh_le = bt_linfo->afh_map_le; - u8 bt_pos; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_BT)) return 0; - if (ver->fcxinit == 7) - bt_pos = md->md_v7.bt_pos; - else - bt_pos = md->md.bt_pos; - p += scnprintf(p, end - p, "========== [BT Status] ==========\n"); p += scnprintf(p, end - p, " %-15s : enable:%s, btg:%s%s, connect:%s, ", "[status]", bt->enable.now ? "Y" : "N", bt->btg_type ? "Y" : "N", - (bt->enable.now && (bt->btg_type != bt_pos) ? + (bt->enable.now && (bt->btg_type != md->bt0_pos) ? "(efuse-mismatch!!)" : ""), (bt_linfo->status.map.connect ? "Y" : "N")); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index fb4c95ed1904..f7b3523c790c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1501,7 +1501,7 @@ enum rtw89_btc_bt_profile { BTC_PROFILE_MAX = 4, }; -struct rtw89_btc_ant_info { +struct rtw89_btc_ant_info_v0 { u8 type; /* shared, dedicated */ u8 num; u8 isolation; @@ -1537,6 +1537,21 @@ struct rtw89_btc_ant_info_v10 { u8 ant_xmap[2][4]; } __packed; +struct rtw89_btc_ant_info { + u8 type; /* shared, dedicated(non-shared) */ + u8 num; /* antenna count */ + u8 isolation; /* Ant-Iso between WL/BT */ + u8 single_pos; /* wifi 1ss-1ant at 0:S0 or 1:S1 */ + + u8 stream_cnt; /* spatial_stream count: Tx[7:4], Rx[3:0] */ + u8 btg_pos; /* BT0 btg-circuit at 0:WL-S0/1:WL-S1 */ + u8 btg1_pos; /* BT1 btg-circuit at 0:WL-S0/1:WL-S1 */ + u8 func[5]; /* function at 1~5 Ant refer to enum btc_bt_func_type */ + u8 ant_xmap[2][4]; + + u8 diversity; /* only for wifi use 1-antenna */ +}; + enum rtw89_tfc_dir { RTW89_TFC_UL, RTW89_TFC_DL, @@ -2331,8 +2346,8 @@ struct rtw89_btc_wl_info { u32 wcnt[BTC_WCNT_NUM]; }; -struct rtw89_btc_module { - struct rtw89_btc_ant_info ant; +struct rtw89_btc_module_v0 { + struct rtw89_btc_ant_info_v0 ant; u8 rfe_type; u8 cv; @@ -2373,11 +2388,27 @@ struct rtw89_btc_module_v10 { } __packed; union rtw89_btc_module_info { - struct rtw89_btc_module md; + struct rtw89_btc_module_v0 md_v0; struct rtw89_btc_module_v7 md_v7; struct rtw89_btc_module_v10 md_v10; }; +struct rtw89_btc_module { + u8 rfe_type; + u8 wa_type; /* Refer to enum btc_wa_type */ + u8 kt_ver; + u8 kt_ver_adie; + + u8 bt0_pos; /* wl-end view: get from efuse, must compare bt.btg_type*/ + u8 bt0_sw_type; /* BT Ant-switch: None(non-share), Int(BTG), Ext(SPDT)*/ + u8 bt1_pos; /* BTC_BT_ALONE or BTC_BT_BTG */ + u8 bt1_sw_type; + + u8 bt_solo; + + struct rtw89_btc_ant_info ant; +}; + #define RTW89_BTC_DM_MAXSTEP 30 #define RTW89_BTC_DM_CNT_MAX (RTW89_BTC_DM_MAXSTEP * 8) @@ -2387,8 +2418,8 @@ struct rtw89_btc_dm_step { bool step_ov; }; -struct rtw89_btc_init_info { - struct rtw89_btc_module module; +struct rtw89_btc_init_info_v0 { + struct rtw89_btc_module_v0 module; u8 wl_guard_ch; u8 wl_only: 1; @@ -2398,7 +2429,7 @@ struct rtw89_btc_init_info { u8 bt_only: 1; u16 rsvd; -}; +} __packed; struct rtw89_btc_init_info_v7 { u8 wl_guard_ch; @@ -2414,6 +2445,20 @@ struct rtw89_btc_init_info_v7 { struct rtw89_btc_module_v7 module; } __packed; +struct rtw89_btc_init_info_v107 { + u8 wl_guard_ch; + u8 wl_only; + u8 wl_init_ok; + u8 dbcc_en; + + u8 cx_other; + u8 bt_only; + u8 rsvd; + u8 rsvd1; + + struct rtw89_btc_module_v7 module; +} __packed; + struct rtw89_btc_init_info_v10 { u8 endian_type; /* 0: little-endian, 1:big-endian */ u8 init_mode; /* refer to enum BTC_MODE_xxx */ @@ -2426,12 +2471,34 @@ struct rtw89_btc_init_info_v10 { u8 pta_direction; struct rtw89_btc_module_v10 module; -}; +} __packed; union rtw89_btc_init_info_u { - struct rtw89_btc_init_info init; + struct rtw89_btc_init_info_v0 init_v0; struct rtw89_btc_init_info_v7 init_v7; struct rtw89_btc_init_info_v10 init_v10; + struct rtw89_btc_init_info_v107 init_v107; +}; + +struct rtw89_btc_init_info { + u8 endian_type; /* 0: little-endian, 1:big-endian */ + u8 init_mode; /* refer to enum BTC_MODE_xxx */ + u8 wl_init_ok; + u8 bt0_function; + + u8 bt1_function; + u8 bt2_function; + u8 pta_mode; + u8 pta_direction; + + u8 dbcc_en; + u8 cx_other; + u8 bt_only; + u8 wl_only; + + u8 wl_guard_ch; + + struct rtw89_btc_module module; }; struct rtw89_btc_wl_tx_limit_para { @@ -3659,7 +3726,7 @@ struct rtw89_btc_dm { struct rtw89_btc_gnt_ctrl gnt_set[RTW89_MAC_AX_COEX_GNT_NR]; struct rtw89_btc_gnt_ctrl gnt_val[RTW89_MAC_AX_COEX_GNT_NR]; struct rtw89_mac_ax_wl_act wlact_set[BTC_ALL_BT_EZL]; - union rtw89_btc_init_info_u init_info; /* pass to wl_fw if offload */ + struct rtw89_btc_init_info init_info; /* pass to wl_fw if offload */ struct rtw89_btc_rf_trx_para_v9 rf_trx_para; struct rtw89_btc_wl_tx_limit_para wl_tx_limit; struct rtw89_btc_dm_step dm_step; @@ -4012,7 +4079,7 @@ struct rtw89_btc { struct rtw89_btc_cx cx; struct rtw89_btc_dm dm; struct rtw89_btc_ctrl ctrl; - union rtw89_btc_module_info mdinfo; + struct rtw89_btc_module mdinfo; struct rtw89_btc_btf_fwinfo fwinfo; struct rtw89_btc_dbg dbg; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 533fb782bda8..520429bddb4f 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -5771,9 +5771,9 @@ int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_btc_init_info *init_info = &dm->init_info.init; - struct rtw89_btc_module *module = &init_info->module; - struct rtw89_btc_ant_info *ant = &module->ant; + struct rtw89_btc_init_info *init = &dm->init_info; + struct rtw89_btc_module *md = &init->module; + struct rtw89_btc_ant_info *ant = &md->ant; struct rtw89_h2c_cxinit *h2c; u32 len = sizeof(*h2c); struct sk_buff *skb; @@ -5799,22 +5799,22 @@ int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev, u8 type) u8_encode_bits(ant->btg_pos, RTW89_H2C_CXINIT_ANT_INFO_BTG_POS) | u8_encode_bits(ant->stream_cnt, RTW89_H2C_CXINIT_ANT_INFO_STREAM_CNT); - h2c->mod_rfe = module->rfe_type; - h2c->mod_cv = module->cv; + h2c->mod_rfe = md->rfe_type; + h2c->mod_cv = md->kt_ver; h2c->mod_info = - u8_encode_bits(module->bt_solo, RTW89_H2C_CXINIT_MOD_INFO_BT_SOLO) | - u8_encode_bits(module->bt_pos, RTW89_H2C_CXINIT_MOD_INFO_BT_POS) | - u8_encode_bits(module->switch_type, RTW89_H2C_CXINIT_MOD_INFO_SW_TYPE) | - u8_encode_bits(module->wa_type, RTW89_H2C_CXINIT_MOD_INFO_WA_TYPE); - h2c->mod_adie_kt = module->kt_ver_adie; - h2c->wl_gch = init_info->wl_guard_ch; + u8_encode_bits(md->bt_solo, RTW89_H2C_CXINIT_MOD_INFO_BT_SOLO) | + u8_encode_bits(md->bt0_pos, RTW89_H2C_CXINIT_MOD_INFO_BT_POS) | + u8_encode_bits(md->bt0_sw_type, RTW89_H2C_CXINIT_MOD_INFO_SW_TYPE) | + u8_encode_bits(md->wa_type, RTW89_H2C_CXINIT_MOD_INFO_WA_TYPE); + h2c->mod_adie_kt = md->kt_ver_adie; + h2c->wl_gch = init->wl_guard_ch; h2c->info = - u8_encode_bits(init_info->wl_only, RTW89_H2C_CXINIT_INFO_WL_ONLY) | - u8_encode_bits(init_info->wl_init_ok, RTW89_H2C_CXINIT_INFO_WL_INITOK) | - u8_encode_bits(init_info->dbcc_en, RTW89_H2C_CXINIT_INFO_DBCC_EN) | - u8_encode_bits(init_info->cx_other, RTW89_H2C_CXINIT_INFO_CX_OTHER) | - u8_encode_bits(init_info->bt_only, RTW89_H2C_CXINIT_INFO_BT_ONLY); + u8_encode_bits(init->wl_only, RTW89_H2C_CXINIT_INFO_WL_ONLY) | + u8_encode_bits(init->wl_init_ok, RTW89_H2C_CXINIT_INFO_WL_INITOK) | + u8_encode_bits(init->dbcc_en, RTW89_H2C_CXINIT_INFO_DBCC_EN) | + u8_encode_bits(init->cx_other, RTW89_H2C_CXINIT_INFO_CX_OTHER) | + u8_encode_bits(init->bt_only, RTW89_H2C_CXINIT_INFO_BT_ONLY); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, BTFC_SET, @@ -5838,7 +5838,9 @@ int rtw89_fw_h2c_cxdrv_init_v7(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_btc_init_info_v7 *init_info = &dm->init_info.init_v7; + struct rtw89_btc_init_info *init = &dm->init_info; + struct rtw89_btc_module *md = &init->module; + struct rtw89_btc_ant_info *ant = &md->ant; struct rtw89_h2c_cxinit_v7 *h2c; u32 len = sizeof(*h2c); struct sk_buff *skb; @@ -5853,9 +5855,33 @@ int rtw89_fw_h2c_cxdrv_init_v7(struct rtw89_dev *rtwdev, u8 type) h2c = (struct rtw89_h2c_cxinit_v7 *)skb->data; h2c->hdr.type = type; - h2c->hdr.ver = btc->ver->fcxinit; + h2c->hdr.ver = 7; h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; - h2c->init = *init_info; + + h2c->init.wl_guard_ch = init->wl_guard_ch; + h2c->init.wl_only = init->wl_only; + h2c->init.wl_init_ok = init->wl_init_ok; + h2c->init.cx_other = init->cx_other; + h2c->init.bt_only = init->bt_only; + h2c->init.pta_mode = init->pta_mode; + h2c->init.pta_direction = init->pta_direction; + h2c->init.rsvd3 = init->dbcc_en; /* available at v107 */ + + h2c->init.module.rfe_type = md->rfe_type; + h2c->init.module.kt_ver = md->kt_ver; + h2c->init.module.bt_solo = md->bt_solo; + h2c->init.module.bt_pos = md->bt0_pos; + h2c->init.module.switch_type = md->bt0_sw_type; + h2c->init.module.wa_type = md->wa_type; + h2c->init.module.kt_ver_adie = md->kt_ver_adie; + + h2c->init.module.ant.type = ant->type; + h2c->init.module.ant.num = ant->num; + h2c->init.module.ant.isolation = ant->isolation; + h2c->init.module.ant.single_pos = ant->single_pos; + h2c->init.module.ant.diversity = ant->diversity; + h2c->init.module.ant.btg_pos = ant->btg_pos; + h2c->init.module.ant.stream_cnt = ant->stream_cnt; rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, BTFC_SET, @@ -5879,7 +5905,9 @@ int rtw89_fw_h2c_cxdrv_init_v10(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; - struct rtw89_btc_init_info_v10 *init_info = &dm->init_info.init_v10; + struct rtw89_btc_init_info *init = &dm->init_info; + struct rtw89_btc_module *md = &init->module; + struct rtw89_btc_ant_info *ant = &md->ant; struct rtw89_h2c_cxinit_v10 *h2c; u32 len = sizeof(*h2c); struct sk_buff *skb; @@ -5894,9 +5922,38 @@ int rtw89_fw_h2c_cxdrv_init_v10(struct rtw89_dev *rtwdev, u8 type) h2c = (struct rtw89_h2c_cxinit_v10 *)skb->data; h2c->hdr.type = type; - h2c->hdr.ver = btc->ver->fcxinit; + h2c->hdr.ver = 10; h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; - h2c->init = *init_info; + + h2c->init.endian_type = init->endian_type; + h2c->init.init_mode = init->init_mode; + h2c->init.wl_init_ok = init->wl_init_ok; + h2c->init.bt0_function = init->bt0_function; + h2c->init.bt1_function = init->bt1_function; + h2c->init.bt2_function = init->bt2_function; + h2c->init.pta_mode = init->pta_mode; + h2c->init.pta_direction = init->pta_direction; + + h2c->init.module.rfe_type = md->rfe_type; + h2c->init.module.wa_type = md->wa_type; + h2c->init.module.kt_ver = md->kt_ver; + h2c->init.module.kt_ver_adie = md->kt_ver_adie; + h2c->init.module.bt0_pos = md->bt0_pos; + h2c->init.module.bt0_sw_type = md->bt0_sw_type; + h2c->init.module.bt1_pos = md->bt1_pos; + h2c->init.module.bt1_sw_type = md->bt1_sw_type; + + h2c->init.module.ant.type = ant->type; + h2c->init.module.ant.num = ant->num; + h2c->init.module.ant.isolation = ant->isolation; + h2c->init.module.ant.single_pos = ant->single_pos; + h2c->init.module.ant.stream_cnt = ant->stream_cnt; + h2c->init.module.ant.btg_pos = ant->btg_pos; + h2c->init.module.ant.btg1_pos = ant->btg1_pos; + + memcpy(h2c->init.module.ant.func, ant->func, sizeof(ant->func)); + memcpy(h2c->init.module.ant.ant_xmap, ant->ant_xmap, + sizeof(ant->ant_xmap)); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, BTFC_SET, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 67e9c8daff5f..38090105b412 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2593,6 +2593,11 @@ struct rtw89_h2c_cxinit_v7 { struct rtw89_btc_init_info_v7 init; } __packed; +struct rtw89_h2c_cxinit_v107 { + struct rtw89_h2c_cxhdr_v7 hdr; + struct rtw89_btc_init_info_v107 init; +} __packed; + struct rtw89_h2c_cxinit_v10 { struct rtw89_h2c_cxhdr_v7 hdr; struct rtw89_btc_init_info_v10 init; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index e3a17f539ad6..db37ad6653bc 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2118,82 +2118,43 @@ 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) { - const struct rtw89_btc_ver *ver = rtwdev->btc.ver; - union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; + struct rtw89_btc_module *md = &rtwdev->btc.mdinfo; - if (ver->fcxinit == 7) { - md->md_v7.rfe_type = rtwdev->efuse.rfe_type; - md->md_v7.kt_ver = rtwdev->hal.cv; - md->md_v7.bt_solo = 0; - md->md_v7.switch_type = BTC_SWITCH_INTERNAL; - md->md_v7.ant.isolation = 10; - md->md_v7.kt_ver_adie = rtwdev->hal.acv; + md->rfe_type = rtwdev->efuse.rfe_type; + md->kt_ver = rtwdev->hal.cv; + md->bt_solo = 0; + md->bt0_sw_type = BTC_SWITCH_INTERNAL; + md->ant.isolation = 10; + md->kt_ver_adie = rtwdev->hal.acv; - if (md->md_v7.rfe_type == 0) - return; + if (md->rfe_type == 0) + return; - /* rfe_type 3*n+1: 1-Ant(shared), - * 3*n+2: 2-Ant+Div(non-shared), - * 3*n+3: 2-Ant+no-Div(non-shared) - */ - md->md_v7.ant.num = (md->md_v7.rfe_type % 3 == 1) ? 1 : 2; - /* WL-1ss at S0, btg at s0 (On 1 WL RF) */ - md->md_v7.ant.single_pos = RF_PATH_A; - md->md_v7.ant.btg_pos = RF_PATH_A; - md->md_v7.ant.stream_cnt = 1; + /* rfe_type 3*n+1: 1-Ant(shared), + * 3*n+2: 2-Ant+Div(non-shared), + * 3*n+3: 2-Ant+no-Div(non-shared) + */ + md->ant.num = (md->rfe_type % 3 == 1) ? 1 : 2; + /* WL-1ss at S0, btg at s0 (On 1 WL RF) */ + md->ant.single_pos = RF_PATH_A; + md->ant.btg_pos = RF_PATH_A; + md->ant.stream_cnt = 1; - if (md->md_v7.ant.num == 1) { - md->md_v7.ant.type = BTC_ANT_SHARED; - md->md_v7.bt_pos = BTC_BT_BTG; - md->md_v7.wa_type = 1; - md->md_v7.ant.diversity = 0; - } else { /* ant.num == 2 */ - md->md_v7.ant.type = BTC_ANT_DEDICATED; - md->md_v7.bt_pos = BTC_BT_ALONE; - md->md_v7.switch_type = BTC_SWITCH_EXTERNAL; - md->md_v7.wa_type = 0; - if (md->md_v7.rfe_type % 3 == 2) - md->md_v7.ant.diversity = 1; - } - rtwdev->btc.btg_pos = md->md_v7.ant.btg_pos; - rtwdev->btc.ant_type = md->md_v7.ant.type; - } else { - md->md.rfe_type = rtwdev->efuse.rfe_type; - md->md.cv = rtwdev->hal.cv; - md->md.bt_solo = 0; - md->md.switch_type = BTC_SWITCH_INTERNAL; - md->md.ant.isolation = 10; - md->md.kt_ver_adie = rtwdev->hal.acv; - - if (md->md.rfe_type == 0) - return; - - /* rfe_type 3*n+1: 1-Ant(shared), - * 3*n+2: 2-Ant+Div(non-shared), - * 3*n+3: 2-Ant+no-Div(non-shared) - */ - md->md.ant.num = (md->md.rfe_type % 3 == 1) ? 1 : 2; - /* WL-1ss at S0, btg at s0 (On 1 WL RF) */ - md->md.ant.single_pos = RF_PATH_A; - md->md.ant.btg_pos = RF_PATH_A; - md->md.ant.stream_cnt = 1; - - if (md->md.ant.num == 1) { - md->md.ant.type = BTC_ANT_SHARED; - md->md.bt_pos = BTC_BT_BTG; - md->md.wa_type = 1; - md->md.ant.diversity = 0; - } else { /* ant.num == 2 */ - md->md.ant.type = BTC_ANT_DEDICATED; - md->md.bt_pos = BTC_BT_ALONE; - md->md.switch_type = BTC_SWITCH_EXTERNAL; - md->md.wa_type = 0; - if (md->md.rfe_type % 3 == 2) - md->md.ant.diversity = 1; - } - rtwdev->btc.btg_pos = md->md.ant.btg_pos; - rtwdev->btc.ant_type = md->md.ant.type; + if (md->ant.num == 1) { + md->ant.type = BTC_ANT_SHARED; + md->bt0_pos = BTC_BT_BTG; + md->wa_type = 1; + md->ant.diversity = 0; + } else { /* ant.num == 2 */ + md->ant.type = BTC_ANT_DEDICATED; + md->bt0_pos = BTC_BT_ALONE; + md->bt0_sw_type = BTC_SWITCH_EXTERNAL; + md->wa_type = 0; + if (md->rfe_type % 3 == 2) + md->ant.diversity = 1; } + rtwdev->btc.btg_pos = md->ant.btg_pos; + rtwdev->btc.ant_type = md->ant.type; } static @@ -2217,9 +2178,8 @@ static void rtw8851b_btc_init_cfg(struct rtw89_dev *rtwdev) }; const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; - union rtw89_btc_module_info *md = &btc->mdinfo; - const struct rtw89_btc_ver *ver = btc->ver; - u8 path, path_min, path_max, str_cnt, ant_sing_pos; + struct rtw89_btc_module *md = &btc->mdinfo; + u8 path, path_min, path_max; /* PTA init */ rtw89_mac_coex_init(rtwdev, &coex_params); @@ -2228,17 +2188,9 @@ static void rtw8851b_btc_init_cfg(struct rtw89_dev *rtwdev) chip->ops->btc_set_wl_pri(rtwdev, BTC_PRI_MASK_TX_RESP, true); chip->ops->btc_set_wl_pri(rtwdev, BTC_PRI_MASK_BEACON, true); - if (ver->fcxinit == 7) { - str_cnt = md->md_v7.ant.stream_cnt; - ant_sing_pos = md->md_v7.ant.single_pos; - } else { - str_cnt = md->md.ant.stream_cnt; - ant_sing_pos = md->md.ant.single_pos; - } - /* for 1-Ant && 1-ss case: only 1-path */ - if (str_cnt == 1) { - path_min = ant_sing_pos; + if (md->ant.stream_cnt == 1) { + path_min = md->ant.single_pos; path_max = path_min; } else { path_min = RF_PATH_A; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index f32c7c6a4075..5d7b5d1a24b1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -1827,56 +1827,30 @@ 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) { - const struct rtw89_btc_ver *ver = rtwdev->btc.ver; - union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; + struct rtw89_btc_module *md = &rtwdev->btc.mdinfo; - if (ver->fcxinit == 7) { - md->md_v7.rfe_type = rtwdev->efuse.rfe_type; - md->md_v7.kt_ver = rtwdev->hal.cv; - md->md_v7.bt_solo = 0; - md->md_v7.switch_type = BTC_SWITCH_INTERNAL; + md->rfe_type = rtwdev->efuse.rfe_type; + md->kt_ver = rtwdev->hal.cv; + md->bt_solo = 0; + md->bt0_sw_type = BTC_SWITCH_INTERNAL; - if (md->md_v7.rfe_type > 0) - md->md_v7.ant.num = (md->md_v7.rfe_type % 2 ? 2 : 3); - else - md->md_v7.ant.num = 2; + if (md->rfe_type > 0) + md->ant.num = (md->rfe_type % 2 ? 2 : 3); + else + md->ant.num = 2; - md->md_v7.ant.diversity = 0; - md->md_v7.ant.isolation = 10; + md->ant.diversity = 0; + md->ant.isolation = 10; - if (md->md_v7.ant.num == 3) { - md->md_v7.ant.type = BTC_ANT_DEDICATED; - md->md_v7.bt_pos = BTC_BT_ALONE; - } else { - md->md_v7.ant.type = BTC_ANT_SHARED; - md->md_v7.bt_pos = BTC_BT_BTG; - } - rtwdev->btc.btg_pos = md->md_v7.ant.btg_pos; - rtwdev->btc.ant_type = md->md_v7.ant.type; + if (md->ant.num == 3) { + md->ant.type = BTC_ANT_DEDICATED; + md->bt0_pos = BTC_BT_ALONE; } else { - md->md.rfe_type = rtwdev->efuse.rfe_type; - md->md.cv = rtwdev->hal.cv; - md->md.bt_solo = 0; - md->md.switch_type = BTC_SWITCH_INTERNAL; - - if (md->md.rfe_type > 0) - md->md.ant.num = (md->md.rfe_type % 2 ? 2 : 3); - else - md->md.ant.num = 2; - - md->md.ant.diversity = 0; - md->md.ant.isolation = 10; - - if (md->md.ant.num == 3) { - md->md.ant.type = BTC_ANT_DEDICATED; - md->md.bt_pos = BTC_BT_ALONE; - } else { - md->md.ant.type = BTC_ANT_SHARED; - md->md.bt_pos = BTC_BT_BTG; - } - rtwdev->btc.btg_pos = md->md.ant.btg_pos; - rtwdev->btc.ant_type = md->md.ant.type; + md->ant.type = BTC_ANT_SHARED; + md->bt0_pos = BTC_BT_BTG; } + rtwdev->btc.btg_pos = md->ant.btg_pos; + rtwdev->btc.ant_type = md->ant.type; } static diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index a45f05309af8..356623341f65 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -750,56 +750,30 @@ static void rtw8852b_rfk_track(struct rtw89_dev *rtwdev) static void rtw8852b_btc_set_rfe(struct rtw89_dev *rtwdev) { - const struct rtw89_btc_ver *ver = rtwdev->btc.ver; - union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; + struct rtw89_btc_module *md = &rtwdev->btc.mdinfo; - if (ver->fcxinit == 7) { - md->md_v7.rfe_type = rtwdev->efuse.rfe_type; - md->md_v7.kt_ver = rtwdev->hal.cv; - md->md_v7.bt_solo = 0; - md->md_v7.switch_type = BTC_SWITCH_INTERNAL; + md->rfe_type = rtwdev->efuse.rfe_type; + md->kt_ver = rtwdev->hal.cv; + md->bt_solo = 0; + md->bt0_sw_type = BTC_SWITCH_INTERNAL; - if (md->md_v7.rfe_type > 0) - md->md_v7.ant.num = (md->md_v7.rfe_type % 2 ? 2 : 3); - else - md->md_v7.ant.num = 2; + if (md->rfe_type > 0) + md->ant.num = (md->rfe_type % 2 ? 2 : 3); + else + md->ant.num = 2; - md->md_v7.ant.diversity = 0; - md->md_v7.ant.isolation = 10; + md->ant.diversity = 0; + md->ant.isolation = 10; - if (md->md_v7.ant.num == 3) { - md->md_v7.ant.type = BTC_ANT_DEDICATED; - md->md_v7.bt_pos = BTC_BT_ALONE; - } else { - md->md_v7.ant.type = BTC_ANT_SHARED; - md->md_v7.bt_pos = BTC_BT_BTG; - } - rtwdev->btc.btg_pos = md->md_v7.ant.btg_pos; - rtwdev->btc.ant_type = md->md_v7.ant.type; + if (md->ant.num == 3) { + md->ant.type = BTC_ANT_DEDICATED; + md->bt0_pos = BTC_BT_ALONE; } else { - md->md.rfe_type = rtwdev->efuse.rfe_type; - md->md.cv = rtwdev->hal.cv; - md->md.bt_solo = 0; - md->md.switch_type = BTC_SWITCH_INTERNAL; - - if (md->md.rfe_type > 0) - md->md.ant.num = (md->md.rfe_type % 2 ? 2 : 3); - else - md->md.ant.num = 2; - - md->md.ant.diversity = 0; - md->md.ant.isolation = 10; - - if (md->md.ant.num == 3) { - md->md.ant.type = BTC_ANT_DEDICATED; - md->md.bt_pos = BTC_BT_ALONE; - } else { - md->md.ant.type = BTC_ANT_SHARED; - md->md.bt_pos = BTC_BT_BTG; - } - rtwdev->btc.btg_pos = md->md.ant.btg_pos; - rtwdev->btc.ant_type = md->md.ant.type; + md->ant.type = BTC_ANT_SHARED; + md->bt0_pos = BTC_BT_BTG; } + rtwdev->btc.btg_pos = md->ant.btg_pos; + rtwdev->btc.ant_type = md->ant.type; } union rtw8852b_btc_wl_txpwr_ctrl { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 8d5dd93626b3..3d93cee562c2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -635,46 +635,41 @@ static void rtw8852bt_rfk_track(struct rtw89_dev *rtwdev) static void rtw8852bt_btc_set_rfe(struct rtw89_dev *rtwdev) { - const struct rtw89_btc_ver *ver = rtwdev->btc.ver; - union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; + struct rtw89_btc_module *md = &rtwdev->btc.mdinfo; - if (ver->fcxinit == 7) { - md->md_v7.rfe_type = rtwdev->efuse.rfe_type; - md->md_v7.kt_ver = rtwdev->hal.cv; - md->md_v7.kt_ver_adie = rtwdev->hal.acv; - md->md_v7.bt_solo = 0; - md->md_v7.bt_pos = BTC_BT_BTG; - md->md_v7.switch_type = BTC_SWITCH_INTERNAL; - md->md_v7.wa_type = 0; + md->rfe_type = rtwdev->efuse.rfe_type; + md->kt_ver = rtwdev->hal.cv; + md->kt_ver_adie = rtwdev->hal.acv; + md->bt_solo = 0; + md->bt0_pos = BTC_BT_BTG; + md->bt0_sw_type = BTC_SWITCH_INTERNAL; + md->wa_type = 0; - md->md_v7.ant.type = BTC_ANT_SHARED; - md->md_v7.ant.num = 2; - md->md_v7.ant.isolation = 10; - md->md_v7.ant.diversity = 0; - /* WL 1-stream+1-Ant is located at 0:s0(path-A) or 1:s1(path-B) */ - md->md_v7.ant.single_pos = RF_PATH_A; - md->md_v7.ant.btg_pos = RF_PATH_B; + md->ant.type = BTC_ANT_SHARED; + md->ant.num = 2; + md->ant.isolation = 10; + md->ant.diversity = 0; + /* WL 1-stream+1-Ant is located at 0:s0(path-A) or 1:s1(path-B) */ + md->ant.single_pos = RF_PATH_A; + md->ant.btg_pos = RF_PATH_B; - if (md->md_v7.rfe_type == 0) { - rtwdev->btc.dm.error.map.rfe_type0 = true; - return; - } - - md->md_v7.ant.num = (md->md_v7.rfe_type % 2) ? 2 : 3; - md->md_v7.ant.stream_cnt = 2; - md->md_v7.wa_type |= BTC_WA_INIT_SCAN; - - if (md->md_v7.ant.num == 2) { - md->md_v7.ant.type = BTC_ANT_SHARED; - md->md_v7.bt_pos = BTC_BT_BTG; - md->md_v7.wa_type |= BTC_WA_HFP_LAG; - } else { - md->md_v7.ant.type = BTC_ANT_DEDICATED; - md->md_v7.bt_pos = BTC_BT_ALONE; - } - } else { + if (md->rfe_type == 0) { + rtwdev->btc.dm.error.map.rfe_type0 = true; return; } + + md->ant.num = (md->rfe_type % 2) ? 2 : 3; + md->ant.stream_cnt = 2; + md->wa_type |= BTC_WA_INIT_SCAN; + + if (md->ant.num == 2) { + md->ant.type = BTC_ANT_SHARED; + md->bt0_pos = BTC_BT_BTG; + md->wa_type |= BTC_WA_HFP_LAG; + } else { + md->ant.type = BTC_ANT_DEDICATED; + md->bt0_pos = BTC_BT_ALONE; + } } static void diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 3dc6dfce082a..c68959b63a01 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2608,56 +2608,30 @@ 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) { - const struct rtw89_btc_ver *ver = rtwdev->btc.ver; - union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; + struct rtw89_btc_module *md = &rtwdev->btc.mdinfo; - if (ver->fcxinit == 7) { - md->md_v7.rfe_type = rtwdev->efuse.rfe_type; - md->md_v7.kt_ver = rtwdev->hal.cv; - md->md_v7.bt_solo = 0; - md->md_v7.switch_type = BTC_SWITCH_INTERNAL; + md->rfe_type = rtwdev->efuse.rfe_type; + md->kt_ver = rtwdev->hal.cv; + md->bt_solo = 0; + md->bt0_sw_type = BTC_SWITCH_INTERNAL; - if (md->md_v7.rfe_type > 0) - md->md_v7.ant.num = (md->md_v7.rfe_type % 2 ? 2 : 3); - else - md->md_v7.ant.num = 2; + if (md->rfe_type > 0) + md->ant.num = (md->rfe_type % 2 ? 2 : 3); + else + md->ant.num = 2; - md->md_v7.ant.diversity = 0; - md->md_v7.ant.isolation = 10; + md->ant.diversity = 0; + md->ant.isolation = 10; - if (md->md_v7.ant.num == 3) { - md->md_v7.ant.type = BTC_ANT_DEDICATED; - md->md_v7.bt_pos = BTC_BT_ALONE; - } else { - md->md_v7.ant.type = BTC_ANT_SHARED; - md->md_v7.bt_pos = BTC_BT_BTG; - } - rtwdev->btc.btg_pos = md->md_v7.ant.btg_pos; - rtwdev->btc.ant_type = md->md_v7.ant.type; + if (md->ant.num == 3) { + md->ant.type = BTC_ANT_DEDICATED; + md->bt0_pos = BTC_BT_ALONE; } else { - md->md.rfe_type = rtwdev->efuse.rfe_type; - md->md.cv = rtwdev->hal.cv; - md->md.bt_solo = 0; - md->md.switch_type = BTC_SWITCH_INTERNAL; - - if (md->md.rfe_type > 0) - md->md.ant.num = (md->md.rfe_type % 2 ? 2 : 3); - else - md->md.ant.num = 2; - - md->md.ant.diversity = 0; - md->md.ant.isolation = 10; - - if (md->md.ant.num == 3) { - md->md.ant.type = BTC_ANT_DEDICATED; - md->md.bt_pos = BTC_BT_ALONE; - } else { - md->md.ant.type = BTC_ANT_SHARED; - md->md.bt_pos = BTC_BT_BTG; - } - rtwdev->btc.btg_pos = md->md.ant.btg_pos; - rtwdev->btc.ant_type = md->md.ant.type; + md->ant.type = BTC_ANT_SHARED; + md->bt0_pos = BTC_BT_BTG; } + rtwdev->btc.btg_pos = md->ant.btg_pos; + rtwdev->btc.ant_type = md->ant.type; } static void rtw8852c_ctrl_btg_bt_rx(struct rtw89_dev *rtwdev, bool en, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 854ef9a980bd..2586fd2df8ab 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2721,46 +2721,45 @@ static u32 rtw8922a_chan_to_rf18_val(struct rtw89_dev *rtwdev, static void rtw8922a_btc_set_rfe(struct rtw89_dev *rtwdev) { - union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; - struct rtw89_btc_module_v7 *module = &md->md_v7; + struct rtw89_btc_module *md = &rtwdev->btc.mdinfo; - module->rfe_type = rtwdev->efuse.rfe_type; - module->kt_ver = rtwdev->hal.cv; - module->bt_solo = 0; - module->switch_type = BTC_SWITCH_INTERNAL; - module->wa_type = 0; + md->rfe_type = rtwdev->efuse.rfe_type; + md->kt_ver = rtwdev->hal.cv; + md->bt_solo = 0; + md->bt0_sw_type = BTC_SWITCH_INTERNAL; + md->wa_type = 0; - module->ant.type = BTC_ANT_SHARED; - module->ant.num = 2; - module->ant.isolation = 10; - module->ant.diversity = 0; - module->ant.single_pos = RF_PATH_A; - module->ant.btg_pos = RF_PATH_B; + md->ant.type = BTC_ANT_SHARED; + md->ant.num = 2; + md->ant.isolation = 10; + md->ant.diversity = 0; + md->ant.single_pos = RF_PATH_A; + md->ant.btg_pos = RF_PATH_B; - if (module->kt_ver <= 1) - module->wa_type |= BTC_WA_HFP_ZB; + if (md->kt_ver <= 1) + md->wa_type |= BTC_WA_HFP_ZB; rtwdev->btc.cx.bt_ext.func_type = BTC_3CX_NONE; - if (module->rfe_type == 0) { + if (md->rfe_type == 0) { rtwdev->btc.dm.error.map.rfe_type0 = true; return; } - module->ant.num = (module->rfe_type % 2) ? 2 : 3; + md->ant.num = (md->rfe_type % 2) ? 2 : 3; - if (module->kt_ver == 0) - module->ant.num = 2; + if (md->kt_ver == 0) + md->ant.num = 2; - if (module->ant.num == 3) { - module->ant.type = BTC_ANT_DEDICATED; - module->bt_pos = BTC_BT_ALONE; + if (md->ant.num == 3) { + md->ant.type = BTC_ANT_DEDICATED; + md->bt0_pos = BTC_BT_ALONE; } else { - module->ant.type = BTC_ANT_SHARED; - module->bt_pos = BTC_BT_BTG; + md->ant.type = BTC_ANT_SHARED; + md->bt0_pos = BTC_BT_BTG; } - rtwdev->btc.btg_pos = module->ant.btg_pos; - rtwdev->btc.ant_type = module->ant.type; + rtwdev->btc.btg_pos = md->ant.btg_pos; + rtwdev->btc.ant_type = md->ant.type; } static @@ -2773,7 +2772,7 @@ void rtw8922a_set_trx_mask(struct rtw89_dev *rtwdev, u8 path, u8 group, u32 val) static void rtw8922a_btc_init_cfg(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_ant_info_v7 *ant = &btc->mdinfo.md_v7.ant; + struct rtw89_btc_ant_info *ant = &btc->mdinfo.ant; u32 wl_pri, path_min, path_max; u8 path; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index fd4c92ba3f7b..12c96425a6ff 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -3170,32 +3170,31 @@ static u32 rtw8922d_chan_to_rf18_val(struct rtw89_dev *rtwdev, static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_module *md = &btc->mdinfo; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_cx *cx = &btc->cx; - union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; - struct rtw89_btc_module_v10 *module = &md->md_v10; u8 efuse_bt_func, efuse_ant_info, bt_sw_gpio_pos; u8 is_combo, is_bt_share; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s !!\n", __func__); /* get from final capability of device */ - module->rfe_type = rtwdev->efuse.rfe_type; - module->kt_ver = rtwdev->hal.cv; - module->kt_ver_adie = rtwdev->hal.acv; - module->wa_type = 0; + md->rfe_type = rtwdev->efuse.rfe_type; + md->kt_ver = rtwdev->hal.cv; + md->kt_ver_adie = rtwdev->hal.acv; + md->wa_type = 0; dm->wl_trx_nss_en = 0; - module->ant.num = 2; - module->ant.single_pos = BTC_RF_S0; /* WL 1ss+1Ant 0:s0(A)/ 1:s1(B) */ + md->ant.num = 2; + md->ant.single_pos = BTC_RF_S0; /* WL 1ss+1Ant 0:s0(A)/ 1:s1(B) */ /* set default antenna isolation */ - cx->bt0.ant_iso_to_wl = module->ant.isolation; - cx->bt1.ant_iso_to_wl = module->ant.isolation; + cx->bt0.ant_iso_to_wl = md->ant.isolation; + cx->bt1.ant_iso_to_wl = md->ant.isolation; - module->ant.stream_cnt = 2; - module->ant.btg_pos = BTC_RF_S1; /* BTG0 at WL-S1 */ - module->ant.btg1_pos = BTC_RF_S0; /* BTG1 at WL-S0 if Dual-BTGA */ + md->ant.stream_cnt = 2; + md->ant.btg_pos = BTC_RF_S1; /* BTG0 at WL-S1 */ + md->ant.btg1_pos = BTC_RF_S0; /* BTG1 at WL-S0 if Dual-BTGA */ cx->bt0.band_56G_support = 1; cx->bt1.band_56G_support = 1; @@ -3223,21 +3222,21 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) efuse_bt_func &= 0x1f; /* 0xcd[4:0] */ efuse_ant_info = rtwdev->efuse.bt_setting_3; - module->ant.num = (efuse_ant_info & 0xe0) >> 5; /* 0xCE[7:5] */ + md->ant.num = (efuse_ant_info & 0xe0) >> 5; /* 0xCE[7:5] */ is_combo = (efuse_ant_info & 0xe) >> 1; /* 0xCE[3:1] */ is_bt_share = efuse_ant_info & BIT(0); /* 0xCE[0] */ memset(dm->ant_xmap, 0, sizeof(dm->ant_xmap)); /* To-Do: "RFE_TYpe" to "ant.num" translation */ - switch (module->ant.num) { + switch (md->ant.num) { case 1: /* 1-Ant WL-S0 only & BT0 only */ - module->ant.type = BTC_ANT_SHARED; - module->bt0_pos = BTC_BT_BTG; - module->bt0_sw_type = BTC_SWITCH_INTERNAL; - module->ant.btg_pos = BTC_RF_S0; /* BTG0 at WL-S0 */ - module->ant.stream_cnt = 1; - module->ant.func[0] = BTC_EFMAP_BT0; + md->ant.type = BTC_ANT_SHARED; + md->bt0_pos = BTC_BT_BTG; + md->bt0_sw_type = BTC_SWITCH_INTERNAL; + md->ant.btg_pos = BTC_RF_S0; /* BTG0 at WL-S0 */ + md->ant.stream_cnt = 1; + md->ant.func[0] = BTC_EFMAP_BT0; dm->ant_xmap[BTC_RF_S0][BTC_BT_1ST] = 1; /* BT0 shared with S0*/ dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 0; /* WL 1T1R no RF-S1 */ break; @@ -3245,34 +3244,34 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) default: if (is_combo) { if (efuse_bt_func == (BTC_EFMAP_BT0 | BTC_EFMAP_BT1)) - module->ant.func[0] = BTC_EFMAP_BT1; + md->ant.func[0] = BTC_EFMAP_BT1; - module->ant.func[1] = BTC_EFMAP_BT0; + md->ant.func[1] = BTC_EFMAP_BT0; } else { - module->ant.func[0] = BTC_EFMAP_NONE; - module->ant.func[1] = BTC_EFMAP_NONE; + md->ant.func[0] = BTC_EFMAP_NONE; + md->ant.func[1] = BTC_EFMAP_NONE; } if (is_bt_share) { /* WL-S0 + (WL-S1 & BT0-S1) */ - module->ant.type = BTC_ANT_SHARED; - module->bt0_pos = BTC_BT_BTG; - module->bt0_sw_type = BTC_SWITCH_INTERNAL; + md->ant.type = BTC_ANT_SHARED; + md->bt0_pos = BTC_BT_BTG; + md->bt0_sw_type = BTC_SWITCH_INTERNAL; dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1; } else { /* WL-S0 + BT0-S1 */ - module->ant.type = BTC_ANT_DEDICATED; - module->bt0_pos = BTC_BT_ALONE; - module->bt0_sw_type = BTC_SWITCH_V1_NONE; + md->ant.type = BTC_ANT_DEDICATED; + md->bt0_pos = BTC_BT_ALONE; + md->bt0_sw_type = BTC_SWITCH_V1_NONE; } - if (module->ant.func[0] == BTC_EFMAP_BT1) { /* if 2nd BT exist */ + if (md->ant.func[0] == BTC_EFMAP_BT1) { /* if 2nd BT exist */ dm->ant_xmap[BTC_RF_S0][BTC_BT_2ND] = 1; - if (module->rfe_type == 12) { /* WL-S0 & BT1 by SPDT */ - module->bt1_pos = BTC_BT_ALONE; + if (md->rfe_type == 12) { /* WL-S0 & BT1 by SPDT */ + md->bt1_pos = BTC_BT_ALONE; /* Todo: set SPDT GPIO-ctrl */ - module->bt1_sw_type = bt_sw_gpio_pos; + md->bt1_sw_type = bt_sw_gpio_pos; } else { /* WL-S0 & BT1-S1 by BTGA */ - module->bt1_pos = BTC_BT_BTG; - module->bt1_sw_type = BTC_SWITCH_INTERNAL; + md->bt1_pos = BTC_BT_BTG; + md->bt1_sw_type = BTC_SWITCH_INTERNAL; } } @@ -3280,48 +3279,48 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) break; case 3: /* 3-Ant, 3 different BT-configuration */ if (is_bt_share) { - module->ant.func[0] = BTC_EFMAP_NONE; - module->ant.func[1] = BTC_EFMAP_BT0; - module->ant.func[2] = efuse_bt_func & (~BTC_EFMAP_BT0); - module->ant.type = BTC_ANT_SHARED; - module->bt0_pos = BTC_BT_BTG; - module->bt0_sw_type = BTC_SWITCH_INTERNAL; + md->ant.func[0] = BTC_EFMAP_NONE; + md->ant.func[1] = BTC_EFMAP_BT0; + md->ant.func[2] = efuse_bt_func & (~BTC_EFMAP_BT0); + md->ant.type = BTC_ANT_SHARED; + md->bt0_pos = BTC_BT_BTG; + md->bt0_sw_type = BTC_SWITCH_INTERNAL; dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1; dm->wl_trx_nss_en = 1; /* 1ss MIMO-PS capability */ } else { - module->ant.func[0] = BTC_EFMAP_NONE; - module->ant.func[1] = BTC_EFMAP_NONE; - module->ant.func[2] = efuse_bt_func; - module->ant.type = BTC_ANT_DEDICATED; - module->bt0_pos = BTC_BT_ALONE; - module->bt0_sw_type = BTC_SWITCH_V1_NONE; + md->ant.func[0] = BTC_EFMAP_NONE; + md->ant.func[1] = BTC_EFMAP_NONE; + md->ant.func[2] = efuse_bt_func; + md->ant.type = BTC_ANT_DEDICATED; + md->bt0_pos = BTC_BT_ALONE; + md->bt0_sw_type = BTC_SWITCH_V1_NONE; } - module->bt1_pos = BTC_BT_ALONE; /* BT1 may exist or not */ - module->bt1_sw_type = BTC_SWITCH_V1_NONE; + md->bt1_pos = BTC_BT_ALONE; /* BT1 may exist or not */ + md->bt1_sw_type = BTC_SWITCH_V1_NONE; break; case 4: /* 4-Ant, WL-S0 + WL-S1 + BT0 + BT1 */ - module->ant.func[0] = BTC_EFMAP_NONE; - module->ant.func[1] = BTC_EFMAP_NONE; - module->ant.func[2] = BTC_EFMAP_BT0; - module->ant.func[3] = efuse_bt_func & (~BTC_EFMAP_BT0); - module->ant.type = BTC_ANT_DEDICATED; - module->bt0_pos = BTC_BT_ALONE; - module->bt0_sw_type = BTC_SWITCH_V1_NONE; - module->bt1_pos = BTC_BT_ALONE; - module->bt1_sw_type = BTC_SWITCH_V1_NONE; + md->ant.func[0] = BTC_EFMAP_NONE; + md->ant.func[1] = BTC_EFMAP_NONE; + md->ant.func[2] = BTC_EFMAP_BT0; + md->ant.func[3] = efuse_bt_func & (~BTC_EFMAP_BT0); + md->ant.type = BTC_ANT_DEDICATED; + md->bt0_pos = BTC_BT_ALONE; + md->bt0_sw_type = BTC_SWITCH_V1_NONE; + md->bt1_pos = BTC_BT_ALONE; + md->bt1_sw_type = BTC_SWITCH_V1_NONE; break; case 5: - module->ant.func[0] = BTC_EFMAP_NONE; - module->ant.func[1] = BTC_EFMAP_NONE; - module->ant.func[2] = BTC_EFMAP_BT0; - module->ant.func[3] = BTC_EFMAP_BT1; - module->ant.func[4] = BTC_EFMAP_ZB; - module->ant.type = BTC_ANT_DEDICATED; - module->bt0_pos = BTC_BT_ALONE; - module->bt0_sw_type = BTC_SWITCH_V1_NONE; - module->bt1_pos = BTC_BT_ALONE; - module->bt1_sw_type = BTC_SWITCH_V1_NONE; + md->ant.func[0] = BTC_EFMAP_NONE; + md->ant.func[1] = BTC_EFMAP_NONE; + md->ant.func[2] = BTC_EFMAP_BT0; + md->ant.func[3] = BTC_EFMAP_BT1; + md->ant.func[4] = BTC_EFMAP_ZB; + md->ant.type = BTC_ANT_DEDICATED; + md->bt0_pos = BTC_BT_ALONE; + md->bt0_sw_type = BTC_SWITCH_V1_NONE; + md->bt1_pos = BTC_BT_ALONE; + md->bt1_sw_type = BTC_SWITCH_V1_NONE; break; } @@ -3333,8 +3332,8 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) */ if (dm->wl_trx_nss_en && (dm->wl_trx_nss.tx_limit && dm->wl_trx_nss.rx_limit)) { - module->ant.type = BTC_ANT_DEDICATED; - module->ant.stream_cnt = 1; + md->ant.type = BTC_ANT_DEDICATED; + md->ant.stream_cnt = 1; dm->ant_xmap[BTC_RF_S0][BTC_BT_1ST] = 0; /* wl 1ss-> RF-S0 */ dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 0; /* BT0-> RF-S1 */ dm->ant_xmap[BTC_RF_S0][BTC_BT_2ND] = 0; @@ -3365,10 +3364,10 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) /* use GPIO 12~15 for Ext-4-wire-PTA */ cx->bt_ext.hpta_cfg = BIT(12) | BIT(13) | BIT(14) | BIT(15); /* for Ext-SOC locate at Ant-2 */ - if (module->ant.num >= 5) - module->ant.func[4] = BTC_EFMAP_ZB; + if (md->ant.num >= 5) + md->ant.func[4] = BTC_EFMAP_ZB; else - module->ant.func[module->ant.num - 1] = BTC_EFMAP_ZB; + md->ant.func[md->ant.num - 1] = BTC_EFMAP_ZB; break; } From 81d30a7be825f0a27cb64a5385d57aa4aab70ca3 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:32 +0800 Subject: [PATCH 148/403] wifi: rtw89: coex: Add driver info H2C command index version 103 The 0.29.133.X firmware driver info H2C index maximum is 5, to prevent switch case fall through and send unexpected H2C commands, add version code 103 as judgment. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index ad52274a3979..10699c92273a 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -3138,7 +3138,7 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) rtw89_fw_h2c_cxdrv_rfk(rtwdev, index); break; case CXDRVINFO_TXPWR: - if (ver->drvinfo_ver == 3) + if (ver->drvinfo_ver == 3 || ver->drvinfo_ver == 103) index = 4; if (ver->fcxtrx == 7 || ver->fcxtrx == 107) @@ -3147,26 +3147,27 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) rtw89_fw_h2c_cxtxpwr_v9(rtwdev, index); break; case CXDRVINFO_FDDT: - if (ver->drvinfo_ver == 3) + if (ver->drvinfo_ver == 3 || ver->drvinfo_ver == 103) index = 5; else return; - rtw89_debug(rtwdev, RTW89_DBG_BTC, "drv_info FDDT index=%d\n", index); break; case CXDRVINFO_MLO: + if (!ver->fcxmlo || ver->drvinfo_ver == 103) + return; + if (ver->drvinfo_ver == 3) index = 6; else return; - rtw89_debug(rtwdev, RTW89_DBG_BTC, "drv_info MLO index=%d\n", index); break; case CXDRVINFO_OSI: - if (!ver->fcxosi) + if (!ver->fcxosi || ver->drvinfo_ver == 103) return; - if (ver->drvinfo_ver > 1) + if (ver->drvinfo_ver == 3) index = 7; else return; From 35b569b60aa0d339dd14473f19c215c67911ffce Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:33 +0800 Subject: [PATCH 149/403] wifi: rtw89: coex: Fix Wi-Fi role info H2C command header issue The function to filled up H2C command data is the last step in the driver, the next step is going to firmware. So the structure version number should not included driver local branch number (like firmware is v5, but driver branch to v105), it should be assigned as a explicit version number which paired with firmware. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 520429bddb4f..196de240fad1 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6256,6 +6256,10 @@ int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type) skb_put(skb, len); h2c = (struct rtw89_h2c_cxrole_v7 *)skb->data; + h2c->hdr.type = type; + h2c->hdr.ver = 7; + h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; + h2c->r.connect_cnt = r->connect_cnt; h2c->r.link_mode = r->link_mode; h2c->r.link_mode_chg = r->link_mode_chg; @@ -6324,7 +6328,7 @@ int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type) h2c = (struct rtw89_h2c_cxrole_v8 *)skb->data; h2c->hdr.type = type; - h2c->hdr.ver = rtwdev->btc.ver->fwlrole; + 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; @@ -6398,7 +6402,7 @@ int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type) h2c = (struct rtw89_h2c_cxrole_v10 *)skb->data; h2c->hdr.type = type; - h2c->hdr.ver = rtwdev->btc.ver->fwlrole; + h2c->hdr.ver = 10; h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) { From 58b1bde3712e74ead009b21c707f409b121ea51c Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:34 +0800 Subject: [PATCH 150/403] wifi: rtw89: coex: Add firmware 0.29.133.X support for RTL8852B family The new firmware modified GPIO setup structure format for third party chip set I/O control & offloaded Wi-Fi TRX status to firmware for training traffic RF-Parameters & TDMA mechanism. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 10699c92273a..6bbcc6cd5dae 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -142,6 +142,15 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .max_role_num = 6, .fcxosi = 6, .fcxmlo = 2, .bt_desired = 8, .fcxtrx = 9, }, + {RTL8852BT, RTW89_FW_VER_CODE(0, 29, 133, 0), + .fcxbtcrpt = 9, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, + .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 8, + .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, + .fwlrole = 7, .frptmap = 5, .fcxctrl = 7, .fcxinit = 107, + .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_ver = 103, .info_buf = 1800, + .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 8, + .fcxtrx = 107, + }, {RTL8852BT, RTW89_FW_VER_CODE(0, 29, 122, 0), .fcxbtcrpt = 8, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, @@ -187,6 +196,15 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, + {RTL8851B, RTW89_FW_VER_CODE(0, 29, 133, 0), + .fcxbtcrpt = 9, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, + .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 8, + .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, + .fwlrole = 7, .frptmap = 5, .fcxctrl = 7, .fcxinit = 107, + .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_ver = 103, .info_buf = 1800, + .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 8, + .fcxtrx = 107, + }, {RTL8851B, RTW89_FW_VER_CODE(0, 29, 29, 0), .fcxbtcrpt = 105, .fcxtdma = 3, .fcxslots = 1, .fcxcysta = 5, .fcxstep = 3, .fcxnullsta = 2, .fcxmreg = 2, .fcxgpiodbg = 1, @@ -223,6 +241,15 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, + {RTL8852B, RTW89_FW_VER_CODE(0, 29, 133, 0), + .fcxbtcrpt = 9, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, + .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 8, + .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, + .fwlrole = 7, .frptmap = 105, .fcxctrl = 7, .fcxinit = 107, + .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_ver = 103, .info_buf = 1800, + .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 8, + .fcxtrx = 107, + }, {RTL8852B, RTW89_FW_VER_CODE(0, 29, 122, 0), .fcxbtcrpt = 8, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, From 42a83b5f13e3d9820113f4de752e5b8353f3f7dd Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:35 +0800 Subject: [PATCH 151/403] wifi: rtw89: coex: Add TDMA version 4 TDMA version 4 uses new TLV-Header to package TDMA information, patch related entry for version 4. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 6bbcc6cd5dae..51108ce5ce31 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1705,7 +1705,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, pfinfo = &pfwinfo->rpt_fbtc_tdma.finfo.v1; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_tdma.finfo.v1); fwsubver->fcxtdma = 0; - } else if (ver->fcxtdma == 3 || ver->fcxtdma == 7) { + } else if (ver->fcxtdma == 3 || ver->fcxtdma == 4 || + ver->fcxtdma == 7) { pfinfo = &pfwinfo->rpt_fbtc_tdma.finfo.v3; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_tdma.finfo.v3); fwsubver->fcxtdma = pfwinfo->rpt_fbtc_tdma.finfo.v3.fver; @@ -2212,7 +2213,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, memcmp(&dm->tdma_now, &pfwinfo->rpt_fbtc_tdma.finfo.v1, sizeof(dm->tdma_now))); - else if (ver->fcxtdma == 3 || ver->fcxtdma == 7) + else if (ver->fcxtdma == 3 || ver->fcxtdma == 4 || + ver->fcxtdma == 7) _chk_btc_err(rtwdev, BTC_DCNT_TDMA_NONSYNC, memcmp(&dm->tdma_now, &pfwinfo->rpt_fbtc_tdma.finfo.v3.tdma, @@ -2559,7 +2561,7 @@ static void _append_tdma(struct rtw89_dev *rtwdev) tlv->len = sizeof(*v); *v = dm->tdma; btc->policy_len += BTC_TLV_HDR_LEN + sizeof(*v); - } else if (ver->fcxtdma == 7) { + } else if (ver->fcxtdma == 7 || ver->fcxtdma == 4) { tlv_v7 = (struct rtw89_btc_btf_tlv_v7 *)&btc->policy[len]; tlv_v7->len = sizeof(dm->tdma); tlv_v7->ver = ver->fcxtdma; From b87da03ee7e9a2e0137bada600add2f2097e11c3 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:36 +0800 Subject: [PATCH 152/403] wifi: rtw89: coex: Add slots version 2 Slots structure version 2 uses new TLV-Header to package slots information, patch related entry for version 2. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 54 ++++++++++++++++------- drivers/net/wireless/realtek/rtw89/coex.h | 8 ++-- drivers/net/wireless/realtek/rtw89/core.h | 9 ++++ 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 51108ce5ce31..65d4e9139342 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1133,7 +1133,7 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type) /* set the slot_now table to original */ btc->dm.tdma_now = t_def[CXTD_OFF]; btc->dm.tdma = t_def[CXTD_OFF]; - if (ver->fcxslots >= 7) { + if (ver->fcxslots >= 2) { for (i = 0; i < ARRAY_SIZE(s_def); i++) { btc->dm.slot.v7[i].dur = s_def[i].dur; btc->dm.slot.v7[i].cxtype = s_def[i].cxtype; @@ -1721,6 +1721,10 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, pfinfo = &pfwinfo->rpt_fbtc_slots.finfo.v1; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_slots.finfo.v1); fwsubver->fcxslots = pfwinfo->rpt_fbtc_slots.finfo.v1.fver; + } else if (ver->fcxslots == 2) { + pfinfo = &pfwinfo->rpt_fbtc_slots.finfo.v2; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_slots.finfo.v2); + fwsubver->fcxslots = pfwinfo->rpt_fbtc_slots.finfo.v2.fver; } else if (ver->fcxslots == 7) { pfinfo = &pfwinfo->rpt_fbtc_slots.finfo.v7; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_slots.finfo.v7); @@ -2232,6 +2236,15 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, memcmp(dm->slot_now.v7, pfwinfo->rpt_fbtc_slots.finfo.v7.slot, sizeof(dm->slot_now.v7))); + } else if (ver->fcxslots == 2) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): check %d %zu\n", + __func__, BTC_DCNT_SLOT_NONSYNC, + sizeof(dm->slot_now.v7)); + _chk_btc_err(rtwdev, BTC_DCNT_SLOT_NONSYNC, + memcmp(dm->slot_now.v7, + pfwinfo->rpt_fbtc_slots.finfo.v2.slot, + sizeof(dm->slot_now.v7))); } else if (ver->fcxslots == 1) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): check %d %zu\n", @@ -2687,7 +2700,7 @@ static void _append_slot(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - if (btc->ver->fcxslots == 7) + if (btc->ver->fcxslots == 2 || btc->ver->fcxslots == 7) _append_slot_v7(rtwdev); else _append_slot_v1(rtwdev); @@ -2902,7 +2915,7 @@ static void rtw89_btc_fw_set_slots(struct rtw89_dev *rtwdev) struct rtw89_btc_dm *dm = &btc->dm; u16 n, len; - if (ver->fcxslots == 7) { + if (ver->fcxslots == 2 || ver->fcxslots == 7) { len = sizeof(*tlv_v7) + sizeof(dm->slot.v7); tlv_v7 = kmalloc(len, GFP_KERNEL); if (!tlv_v7) @@ -3096,7 +3109,7 @@ static void _fw_set_policy(struct rtw89_dev *rtwdev, u16 policy_type, btc->policy, btc->policy_len); if (!ret) { memcpy(&dm->tdma_now, &dm->tdma, sizeof(dm->tdma_now)); - if (btc->ver->fcxslots == 7) + if (btc->ver->fcxslots == 2 || btc->ver->fcxslots == 7) memcpy(&dm->slot_now.v7, &dm->slot.v7, sizeof(dm->slot_now.v7)); else memcpy(&dm->slot_now.v1, &dm->slot.v1, sizeof(dm->slot_now.v1)); @@ -4404,7 +4417,6 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_fbtc_tdma *t = &dm->tdma; - struct rtw89_btc_fbtc_slot *s = dm->slot.v1; u8 type; u32 tbl_w1, tbl_b1, tbl_b4; bool tdma_on = false; @@ -4428,14 +4440,16 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) switch (type) { case BTC_CXP_USERDEF0: *t = t_def[CXTD_OFF]; - s[CXST_OFF] = s_def[CXST_OFF]; + _slot_set_le(btc, CXST_OFF, s_def[CXST_OFF].dur, + s_def[CXST_OFF].cxtbl, s_def[CXST_OFF].cxtype); _slot_set_tbl(btc, CXST_OFF, cxtbl[2]); btc->update_policy_force = true; break; case BTC_CXP_OFF: /* TDMA off */ tdma_on = false; *t = t_def[CXTD_OFF]; - s[CXST_OFF] = s_def[CXST_OFF]; + _slot_set_le(btc, CXST_OFF, s_def[CXST_OFF].dur, + s_def[CXST_OFF].cxtbl, s_def[CXST_OFF].cxtype); switch (policy_type) { case BTC_CXP_OFF_BT: @@ -4498,16 +4512,23 @@ void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type) *t = t_def[CXTD_OFF_EXT]; switch (policy_type) { case BTC_CXP_OFFE_DEF: - s[CXST_E2G] = s_def[CXST_E2G]; - s[CXST_E5G] = s_def[CXST_E5G]; - s[CXST_EBT] = s_def[CXST_EBT]; - s[CXST_ENULL] = s_def[CXST_ENULL]; + _slot_set_le(btc, CXST_E2G, s_def[CXST_E2G].dur, + s_def[CXST_E2G].cxtbl, s_def[CXST_E2G].cxtype); + _slot_set_le(btc, CXST_E5G, s_def[CXST_E5G].dur, + s_def[CXST_E5G].cxtbl, s_def[CXST_E5G].cxtype); + _slot_set_le(btc, CXST_EBT, s_def[CXST_EBT].dur, + s_def[CXST_EBT].cxtbl, s_def[CXST_EBT].cxtype); + _slot_set_le(btc, CXST_ENULL, s_def[CXST_ENULL].dur, + s_def[CXST_ENULL].cxtbl, s_def[CXST_ENULL].cxtype); break; case BTC_CXP_OFFE_DEF2: _slot_set(btc, CXST_E2G, 20, cxtbl[1], SLOT_ISO); - s[CXST_E5G] = s_def[CXST_E5G]; - s[CXST_EBT] = s_def[CXST_EBT]; - s[CXST_ENULL] = s_def[CXST_ENULL]; + _slot_set_le(btc, CXST_E5G, s_def[CXST_E5G].dur, + s_def[CXST_E5G].cxtbl, s_def[CXST_E5G].cxtype); + _slot_set_le(btc, CXST_EBT, s_def[CXST_EBT].dur, + s_def[CXST_EBT].cxtbl, s_def[CXST_EBT].cxtype); + _slot_set_le(btc, CXST_ENULL, s_def[CXST_ENULL].dur, + s_def[CXST_ENULL].cxtbl, s_def[CXST_ENULL].cxtype); break; } break; @@ -10081,6 +10102,7 @@ static int _show_fbtc_tdma(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) static int _show_fbtc_slots(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; + const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_dm *dm = &btc->dm; char *p = buf, *end = buf + bufsz; u16 dur, cxtype; @@ -10088,11 +10110,11 @@ static int _show_fbtc_slots(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) u8 i = 0; for (i = 0; i < CXST_MAX; i++) { - if (btc->ver->fcxslots == 1) { + if (ver->fcxslots == 1) { dur = le16_to_cpu(dm->slot_now.v1[i].dur); tbl = le32_to_cpu(dm->slot_now.v1[i].cxtbl); cxtype = le16_to_cpu(dm->slot_now.v1[i].cxtype); - } else if (btc->ver->fcxslots == 7) { + } else if (ver->fcxslots == 2 || ver->fcxslots == 7) { dur = le16_to_cpu(dm->slot_now.v7[i].dur); tbl = le32_to_cpu(dm->slot_now.v7[i].cxtbl); cxtype = le16_to_cpu(dm->slot_now.v7[i].cxtype); diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index e17407696d8a..3238c44429a9 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -424,7 +424,7 @@ void _slot_set_le(struct rtw89_btc *btc, u8 sid, __le16 dura, __le32 tbl, __le16 btc->dm.slot.v1[sid].dur = dura; btc->dm.slot.v1[sid].cxtbl = tbl; btc->dm.slot.v1[sid].cxtype = type; - } else if (btc->ver->fcxslots == 7) { + } else if (btc->ver->fcxslots == 2 || btc->ver->fcxslots == 7) { btc->dm.slot.v7[sid].dur = dura; btc->dm.slot.v7[sid].cxtype = type; btc->dm.slot.v7[sid].cxtbl = tbl; @@ -442,7 +442,7 @@ void _slot_set_dur(struct rtw89_btc *btc, u8 sid, u16 dura) { if (btc->ver->fcxslots == 1) btc->dm.slot.v1[sid].dur = cpu_to_le16(dura); - else if (btc->ver->fcxslots == 7) + else if (btc->ver->fcxslots == 2 || btc->ver->fcxslots == 7) btc->dm.slot.v7[sid].dur = cpu_to_le16(dura); } @@ -451,7 +451,7 @@ void _slot_set_type(struct rtw89_btc *btc, u8 sid, u16 type) { if (btc->ver->fcxslots == 1) btc->dm.slot.v1[sid].cxtype = cpu_to_le16(type); - else if (btc->ver->fcxslots == 7) + else if (btc->ver->fcxslots == 2 || btc->ver->fcxslots == 7) btc->dm.slot.v7[sid].cxtype = cpu_to_le16(type); } @@ -460,7 +460,7 @@ void _slot_set_tbl(struct rtw89_btc *btc, u8 sid, u32 tbl) { if (btc->ver->fcxslots == 1) btc->dm.slot.v1[sid].cxtbl = cpu_to_le32(tbl); - else if (btc->ver->fcxslots == 7) + else if (btc->ver->fcxslots == 2 || btc->ver->fcxslots == 7) btc->dm.slot.v7[sid].cxtbl = cpu_to_le32(tbl); } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f7b3523c790c..10d432d622cc 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3093,6 +3093,14 @@ struct rtw89_btc_fbtc_slot_v7 { __le32 cxtbl; } __packed; +struct rtw89_btc_fbtc_slots_v2 { + u8 fver; /* btc_ver::fcxslots */ + u8 tbl_num; + __le16 rsvd; + __le32 update_map; + struct rtw89_btc_fbtc_slot_v7 slot[CXST_MAX]; +} __packed; + struct rtw89_btc_fbtc_slot_u16 { __le16 dur; /* slot duration */ __le16 cxtype; @@ -3118,6 +3126,7 @@ struct rtw89_btc_fbtc_slots_v7 { union rtw89_btc_fbtc_slots_info { struct rtw89_btc_fbtc_slots v1; + struct rtw89_btc_fbtc_slots_v2 v2; struct rtw89_btc_fbtc_slots_v7 v7; } __packed; From b8892add7e51a472430f5ba0c3f3d0da6a20c800 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:37 +0800 Subject: [PATCH 153/403] wifi: rtw89: coex: Add cycle status report version 105 The exists version 5 format has FDDT(frequency divided training) related information. But the feature wasn't support for RTL8852C now, so firmware will not send the related reference value. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 197 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 21 +++ 2 files changed, 218 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 65d4e9139342..54c11af46f8d 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1757,6 +1757,11 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, pcysta->v5 = pfwinfo->rpt_fbtc_cysta.finfo.v5; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_cysta.finfo.v5); fwsubver->fcxcysta = pfwinfo->rpt_fbtc_cysta.finfo.v5.fver; + } else if (ver->fcxcysta == 105) { + pfinfo = &pfwinfo->rpt_fbtc_cysta.finfo.v105; + pcysta->v105 = pfwinfo->rpt_fbtc_cysta.finfo.v105; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_cysta.finfo.v105); + fwsubver->fcxcysta = pfwinfo->rpt_fbtc_cysta.finfo.v105.fver; } else if (ver->fcxcysta == 7) { pfinfo = &pfwinfo->rpt_fbtc_cysta.finfo.v7; pcysta->v7 = pfwinfo->rpt_fbtc_cysta.finfo.v7; @@ -2434,6 +2439,58 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, le16_to_cpu(pcysta->v5.slot_cnt[CXST_B1])); _chk_btc_err(rtwdev, BTC_DCNT_CYCLE_HANG, le16_to_cpu(pcysta->v5.cycles)); + } else if (ver->fcxcysta == 105) { + if (dm->fddt_train == BTC_FDDT_ENABLE) + break; + + cnt_leak_slot = le16_to_cpu(pcysta->v105.slot_cnt[CXST_LK]); + cnt_rx_imr = le32_to_cpu(pcysta->v105.leak_slot.cnt_rximr); + + /* Check Leak-AP */ + if (cnt_leak_slot != 0 && cnt_rx_imr != 0 && + dm->tdma_now.rxflctrl) { + if (le16_to_cpu(pcysta->v5.cycles) >= BTC_CYSTA_CHK_PERIOD && + cnt_leak_slot < BTC_LEAK_AP_TH * cnt_rx_imr) + dm->leak_ap = 1; + } + + /* Check diff time between real WL slot and W1 slot */ + if (dm->tdma_now.type == CXTDMA_OFF) { + if (ver->fcxslots == 1) + wl_slot_set = le16_to_cpu(dm->slot_now.v1[CXST_W1].dur); + else if (ver->fcxslots == 7) + wl_slot_set = le16_to_cpu(dm->slot_now.v7[CXST_W1].dur); + wl_slot_real = le16_to_cpu(pcysta->v105.cycle_time.tavg[CXT_WL]); + + if (wl_slot_real > wl_slot_set) + diff_t = wl_slot_real - wl_slot_set; + else + diff_t = wl_slot_set - wl_slot_real; + } + _chk_btc_err(rtwdev, BTC_DCNT_WL_SLOT_DRIFT, diff_t); + + /* Check diff time between real BT slot and EBT/E5G slot */ + bt_slot_set = btc->bt_req_len[RTW89_PHY_0]; + bt_slot_real = le16_to_cpu(pcysta->v105.cycle_time.tavg[CXT_BT]); + diff_t = 0; + if (dm->tdma_now.type == CXTDMA_OFF && + dm->tdma_now.ext_ctrl == CXECTL_EXT && + bt_slot_set != 0) { + if (bt_slot_set > bt_slot_real) + diff_t = bt_slot_set - bt_slot_real; + else + diff_t = bt_slot_real - bt_slot_set; + } + + _chk_btc_err(rtwdev, BTC_DCNT_BT_SLOT_DRIFT, diff_t); + _chk_btc_err(rtwdev, BTC_DCNT_E2G_HANG, + le16_to_cpu(pcysta->v105.slot_cnt[CXST_E2G])); + _chk_btc_err(rtwdev, BTC_DCNT_W1_HANG, + le16_to_cpu(pcysta->v105.slot_cnt[CXST_W1])); + _chk_btc_err(rtwdev, BTC_DCNT_B1_HANG, + le16_to_cpu(pcysta->v105.slot_cnt[CXST_B1])); + _chk_btc_err(rtwdev, BTC_DCNT_CYCLE_HANG, + le16_to_cpu(pcysta->v105.cycles)); } else if (ver->fcxcysta == 7) { if (dm->fddt_train == BTC_FDDT_ENABLE) break; @@ -10690,6 +10747,144 @@ static int _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz return p - buf; } +static int _show_fbtc_cysta_v105(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt0.link_info.a2dp_desc; + struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_fbtc_a2dp_trx_stat_v4 *a2dp_trx; + struct rtw89_btc_fbtc_cysta_v105 *pcysta; + struct rtw89_btc_rpt_cmn_info *pcinfo; + u8 i, cnt = 0, slot_pair, divide_cnt; + u16 cycle, c_begin, c_end, store_index; + char *p = buf, *end = buf + bufsz; + + pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; + if (!pcinfo->valid) + return 0; + + pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v105; + p += scnprintf(p, end - p, + " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", + "[cycle_cnt]", + le16_to_cpu(pcysta->cycles), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); + + for (i = 0; i < CXST_MAX; i++) { + if (!le16_to_cpu(pcysta->slot_cnt[i])) + continue; + + p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i), + le16_to_cpu(pcysta->slot_cnt[i])); + } + + if (dm->tdma_now.rxflctrl) + p += scnprintf(p, end - p, ", leak_rx:%d", + le32_to_cpu(pcysta->leak_slot.cnt_rximr)); + + if (pcysta->collision_cnt) + p += scnprintf(p, end - p, ", collision:%d", + pcysta->collision_cnt); + + if (le16_to_cpu(pcysta->skip_cnt)) + p += scnprintf(p, end - p, ", skip:%d", + le16_to_cpu(pcysta->skip_cnt)); + + p += scnprintf(p, end - p, "\n"); + + p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_time]", + le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tavg) / 1000, + le16_to_cpu(pcysta->leak_slot.tavg) % 1000); + p += scnprintf(p, end - p, + ", max_t[wl:%d/bt:%d/lk:%d.%03d]\n", + le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tmax) / 1000, + le16_to_cpu(pcysta->leak_slot.tmax) % 1000); + + cycle = le16_to_cpu(pcysta->cycles); + if (cycle <= 1) + goto out; + + /* 1 cycle record 1 wl-slot and 1 bt-slot */ + slot_pair = BTC_CYCLE_SLOT_MAX / 2; + + if (cycle <= slot_pair) + c_begin = 1; + else + c_begin = cycle - slot_pair + 1; + + c_end = cycle; + + if (a2dp->exist) + divide_cnt = 3; + else + divide_cnt = BTC_CYCLE_SLOT_MAX / 4; + + if (c_begin > c_end) + goto out; + + for (cycle = c_begin; cycle <= c_end; cycle++) { + cnt++; + store_index = ((cycle - 1) % slot_pair) * 2; + + if (cnt % divide_cnt == 1) + p += scnprintf(p, end - p, " %-15s : ", + "[cycle_step]"); + + p += scnprintf(p, end - p, "->b%02d", + le16_to_cpu(pcysta->slot_step_time[store_index])); + if (a2dp->exist) { + a2dp_trx = &pcysta->a2dp_trx[store_index]; + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); + } + p += scnprintf(p, end - p, "->w%02d", + le16_to_cpu(pcysta->slot_step_time[store_index + 1])); + if (a2dp->exist) { + a2dp_trx = &pcysta->a2dp_trx[store_index + 1]; + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); + } + if (cnt % divide_cnt == 0 || cnt == c_end) + p += scnprintf(p, end - p, "\n"); + } + + if (a2dp->exist) { + p += scnprintf(p, end - p, + " %-15s : a2dp_ept:%d, a2dp_late:%d", + "[a2dp_t_sta]", + le16_to_cpu(pcysta->a2dp_ept.cnt), + le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); + + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta->a2dp_ept.tavg), + le16_to_cpu(pcysta->a2dp_ept.tmax)); + + p += scnprintf(p, end - p, "\n"); + } + +out: + return p - buf; +} + static int _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt0; @@ -11081,6 +11276,8 @@ static int _show_fw_dm_msg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += _show_fbtc_cysta_v4(rtwdev, p, end - p); else if (ver->fcxcysta == 5) p += _show_fbtc_cysta_v5(rtwdev, p, end - p); + else if (ver->fcxcysta == 105) + p += _show_fbtc_cysta_v105(rtwdev, p, end - p); else if (ver->fcxcysta == 7) p += _show_fbtc_cysta_v7(rtwdev, p, end - p); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 10d432d622cc..7573f1965c98 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3354,6 +3354,26 @@ struct rtw89_btc_fbtc_cysta_v5 { /* statistics for cycles */ __le32 except_map; } __packed; +struct rtw89_btc_fbtc_cysta_v105 { + u8 fver; + u8 rsvd; + u8 collision_cnt; + u8 except_cnt; + u8 wl_rx_err_ratio[BTC_CYCLE_SLOT_MAX]; + + __le16 skip_cnt; + __le16 cycles; + + __le16 slot_step_time[BTC_CYCLE_SLOT_MAX]; + __le16 slot_cnt[CXST_MAX]; + __le16 bcn_cnt[CXBCN_MAX]; + struct rtw89_btc_fbtc_cycle_time_info_v5 cycle_time; + struct rtw89_btc_fbtc_cycle_leak_info leak_slot; + struct rtw89_btc_fbtc_cycle_a2dp_empty_info a2dp_ept; + struct rtw89_btc_fbtc_a2dp_trx_stat_v4 a2dp_trx[BTC_CYCLE_SLOT_MAX]; + __le32 except_map; +} __packed; + struct rtw89_btc_fbtc_cysta_v7 { /* statistics for cycles */ u8 fver; u8 rsvd; @@ -3383,6 +3403,7 @@ union rtw89_btc_fbtc_cysta_info { struct rtw89_btc_fbtc_cysta_v3 v3; struct rtw89_btc_fbtc_cysta_v4 v4; struct rtw89_btc_fbtc_cysta_v5 v5; + struct rtw89_btc_fbtc_cysta_v105 v105; struct rtw89_btc_fbtc_cysta_v7 v7; }; From 236badaa50b3a2d27543d8024ae5c78cd242615d Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:38 +0800 Subject: [PATCH 154/403] wifi: rtw89: coex: Add wifi role info version 101 The structure active_role which describes the using Wi-Fi role format is different with the exist v1. Add branch to cover the difference. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 13 ++-- drivers/net/wireless/realtek/rtw89/fw.c | 82 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 35 ++++++++++ 3 files changed, 125 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 54c11af46f8d..b95e34cb1567 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -3198,6 +3198,8 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) rtw89_fw_h2c_cxdrv_role(rtwdev, index); else if (ver->fwlrole == 1) rtw89_fw_h2c_cxdrv_role_v1(rtwdev, index); + else if (ver->fwlrole == 101) + rtw89_fw_h2c_cxdrv_role_v101(rtwdev, index); else if (ver->fwlrole == 2) rtw89_fw_h2c_cxdrv_role_v2(rtwdev, index); else if (ver->fwlrole == 7) @@ -6873,6 +6875,7 @@ static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) 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; + u8 rver = ver->fwlrole; if (hw_band == RTW89_PHY_1) p2p_exist = wl->role_info.p2p_exist_hb1; @@ -6893,7 +6896,7 @@ static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) r->link_mode = BTC_WLINK_STA; } - if (ver->fwlrole >= 10) + if (rver >= 10 && rver < 100) break; if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { @@ -6921,7 +6924,7 @@ static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) r->link_mode = BTC_WLINK_SCC; } - if (ver->fwlrole >= 10) + if (rver >= 10 && rver < 100) break; if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { @@ -6941,7 +6944,7 @@ static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) else r->link_mode = BTC_WLINK_STA; - if (ver->fwlrole >= 10) + if (rver >= 10 && rver < 100) break; if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) @@ -6959,7 +6962,7 @@ static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) */ r->link_mode = BTC_WLINK_DB_MCC; - if (ver->fwlrole >= 10) + if (rver >= 10 && rver < 100) break; r->link_mode_v0 = BTC_WLINK_V0_25G_MCC; @@ -6968,7 +6971,7 @@ static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) /* MLMR only support STA now (2024) */ r->link_mode = BTC_WLINK_STA; - if (ver->fwlrole >= 10) + if (rver >= 10 && rver < 100) break; if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 196de240fad1..3b8c144e0e98 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6149,6 +6149,88 @@ int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type) return ret; } +int rtw89_fw_h2c_cxdrv_role_v101(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_wl_role_info *role_info = &wl->role_info; + struct rtw89_btc_wl_rlink *active; + struct rtw89_h2c_cxrole_v101 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + u8 i; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_role_v101\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxrole_v101 *)skb->data; + + h2c->hdr.type = 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); + + for (i = 0; i < RTW89_PORT_NUM; i++) { + active = &role_info->rlink[i][0]; + h2c->act_role[i].map_role_status = + u8_encode_bits(active->connected, + RTW89_H2C_CXROLE_V101_ROLE_STAT_CONNTECTED) | + u8_encode_bits(active->pid, + RTW89_H2C_CXROLE_V101_ROLE_STAT_PID) | + u8_encode_bits(active->phy, + RTW89_H2C_CXROLE_V101_ROLE_STAT_PHY) | + u8_encode_bits(active->noa, + RTW89_H2C_CXROLE_V101_ROLE_STAT_NOA) | + u8_encode_bits(active->rf_band, + RTW89_H2C_CXROLE_V101_ROLE_STAT_BADN); + h2c->act_role[i].map_clips_bw = + u8_encode_bits(active->client_ps, + RTW89_H2C_CXROLE_V101_CLIPS_BW_CLIENTPS) | + u8_encode_bits(active->bw, + RTW89_H2C_CXROLE_V101_CLIPS_BW_BW); + h2c->act_role[i].role = active->role; + h2c->act_role[i].ch = active->ch; + h2c->act_role[i].noa_duration = cpu_to_le32(active->noa_dur); + } + + h2c->mrole_type = cpu_to_le32(role_info->mrole_type); + h2c->mrole_noa_duration = cpu_to_le32(role_info->mrole_noa_duration); + h2c->map_dbcc_linkmode_chg = + le32_encode_bits(role_info->dbcc_en, + RTW89_H2C_CXROLE_V101_DBCC_EN) | + le32_encode_bits(role_info->dbcc_chg, + RTW89_H2C_CXROLE_V101_DBCC_CHG) | + le32_encode_bits(role_info->dbcc_2g_phy, + RTW89_H2C_CXROLE_V101_DBCC_2G_PHY) | + le32_encode_bits(role_info->link_mode_chg, + RTW89_H2C_CXROLE_V101_DBCC_LINKMODE_CHG) | + le32_encode_bits(role_info->rsvd, + RTW89_H2C_CXROLE_V101_DBCC_RSVD); + + 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; +} + #define H2C_LEN_CXDRVINFO_ROLE_SIZE_V2(max_role_num) \ (4 + 8 * (max_role_num) + H2C_LEN_CXDRVINFO_ROLE_DBCC_LEN + H2C_LEN_CXDRVHDR) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 38090105b412..e86014ca249a 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2603,6 +2603,40 @@ struct rtw89_h2c_cxinit_v10 { struct rtw89_btc_init_info_v10 init; } __packed; +#define RTW89_H2C_CXROLE_V101_ROLE_STAT_CONNTECTED BIT(0) +#define RTW89_H2C_CXROLE_V101_ROLE_STAT_PID GENMASK(3, 1) +#define RTW89_H2C_CXROLE_V101_ROLE_STAT_PHY BIT(4) +#define RTW89_H2C_CXROLE_V101_ROLE_STAT_NOA BIT(5) +#define RTW89_H2C_CXROLE_V101_ROLE_STAT_BADN GENMASK(7, 6) + +#define RTW89_H2C_CXROLE_V101_CLIPS_BW_CLIENTPS BIT(0) +#define RTW89_H2C_CXROLE_V101_CLIPS_BW_BW GENMASK(7, 1) + +#define RTW89_H2C_CXROLE_V101_DBCC_EN BIT(0) +#define RTW89_H2C_CXROLE_V101_DBCC_CHG BIT(1) +#define RTW89_H2C_CXROLE_V101_DBCC_2G_PHY GENMASK(3, 2) +#define RTW89_H2C_CXROLE_V101_DBCC_LINKMODE_CHG BIT(4) +#define RTW89_H2C_CXROLE_V101_DBCC_RSVD GENMASK(31, 5) + +struct rtw89_btc_wl_active_role_v101 { + u8 map_role_status; + u8 map_clips_bw; + u8 role; + u8 ch; + __le32 noa_duration; +} __packed; + +struct rtw89_h2c_cxrole_v101 { + struct rtw89_h2c_cxhdr hdr; + u8 connect_cnt; + u8 link_mode; + __le16 role_map; + struct rtw89_btc_wl_active_role_v101 act_role[RTW89_PORT_NUM]; + __le32 mrole_type; + __le32 mrole_noa_duration; + __le32 map_dbcc_linkmode_chg; +} __packed; + static inline void RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(void *cmd, u8 val) { u8p_replace_bits((u8 *)(cmd) + 2, val, GENMASK(7, 0)); @@ -5417,6 +5451,7 @@ int rtw89_fw_h2c_cxdrv_init_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_init_v10(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type); +int rtw89_fw_h2c_cxdrv_role_v101(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type); From 38c58d541cfe286880cdadebc5d726fe18e3b615 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 17 Jul 2026 14:57:39 +0800 Subject: [PATCH 155/403] wifi: rtw89: coex: Add firmware 0.27.97.X support for RTL8852C Newer firmware is using the new TLV-Header format, without this patch it will lead driver run into length mismatch state. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260717065739.64124-14-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index b95e34cb1567..6a1888d8c476 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -214,6 +214,15 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, .fcxtrx = 0, }, + {RTL8852C, RTW89_FW_VER_CODE(0, 27, 97, 0), + .fcxbtcrpt = 4, .fcxtdma = 4, .fcxslots = 2, .fcxcysta = 105, + .fcxstep = 3, .fcxnullsta = 2, .fcxmreg = 2, .fcxgpiodbg = 1, + .fcxbtver = 1, .fcxbtscan = 2, .fcxbtafh = 2, .fcxbtdevinfo = 1, + .fwlrole = 101, .frptmap = 3, .fcxctrl = 1, .fcxinit = 0, + .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_ver = 0, .info_buf = 1280, + .max_role_num = 5, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 7, + .fcxtrx = 107, + }, {RTL8852C, RTW89_FW_VER_CODE(0, 27, 57, 0), .fcxbtcrpt = 4, .fcxtdma = 3, .fcxslots = 1, .fcxcysta = 3, .fcxstep = 3, .fcxnullsta = 2, .fcxmreg = 1, .fcxgpiodbg = 1, From b0034a2e415603daa1cf00363b407cc145bbcd31 Mon Sep 17 00:00:00 2001 From: Mihail Dimoski Date: Sat, 18 Jul 2026 14:40:45 +0200 Subject: [PATCH 156/403] wifi: rtw88: disable ASPM and deep PS on ASUS TUF Gaming A15 FA506II The RTL8822CE on the ASUS TUF Gaming A15 FA506II wedges during normal use. The driver watchdog toggles PCIe ASPM while leaving power save; the DBI read of the ASPM link-config register fails with -EIO, the PCIe link becomes unstable, and the device drops off the bus, taking Wi-Fi down until a cold power cycle: rtw88_8822ce 0000:03:00.0: failed to read ASPM, ret=-5 rtw88_8822ce 0000:03:00.0: firmware failed to leave lps state rtw88_8822ce 0000:03:00.0: mac power on failed This is the same platform ASPM inter-operability problem already handled for other machines through rtw_pci_quirks[]. Disabling PCI ASPM and deep power save on this model stops the failure. Add a DMI quirk so the workaround is applied automatically. Signed-off-by: Mihail Dimoski Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260718124045.23493-1-mihaildimoski@gmail.com --- drivers/net/wireless/realtek/rtw88/pci.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index 69f2840fed09..5b00c1e1eec6 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1779,6 +1779,16 @@ static const struct dmi_system_id rtw_pci_quirks[] = { .driver_data = (void *)(BIT(QUIRK_DIS_CAP_PCI_ASPM) | BIT(QUIRK_DIS_CAP_LPS_DEEP)), }, + { + .callback = rtw_pci_disable_caps, + .ident = "ASUS TUF Gaming A15 FA506II", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_BOARD_NAME, "FA506II"), + }, + .driver_data = (void *)(BIT(QUIRK_DIS_CAP_PCI_ASPM) | + BIT(QUIRK_DIS_CAP_LPS_DEEP)), + }, {} }; From c706dc5da6e1764b2e75132f7815f75e75a6a34a Mon Sep 17 00:00:00 2001 From: Srinivas Achary Date: Thu, 23 Jul 2026 19:15:50 +0530 Subject: [PATCH 157/403] wifi: cfg80211: change mesh_setup::ie_len to size_t The ie_len field in struct mesh_setup stores the length of the information elements (IEs) buffer. It is currently defined as u8, which limits the maximum supported length to 255 bytes. The IE length is derived from memory buffers whose size is naturally represented by size_t. Using u8 may truncate larger values and can result in incorrect length handling. Change ie_len to size_t so it can represent the full buffer length and match the type commonly used for memory sizes throughout the kernel. Signed-off-by: Ramakrishnan Rathinasamy Signed-off-by: Srinivas Achary Link: https://patch.msgid.link/20260723134550.35167-1-srinivas@aerlync.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 47bc5f55b147..a201979f066e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2823,7 +2823,7 @@ struct mesh_setup { u8 path_metric; u8 auth_id; const u8 *ie; - u8 ie_len; + size_t ie_len; bool is_authenticated; bool is_secure; bool user_mpm; From 36743788d685d9a8a7239c32d75f847a06e60d13 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 23 Jul 2026 09:27:50 -0700 Subject: [PATCH 158/403] rfkill: repair malformed kernel-doc and add some descriptions Use kernel-doc format for function descriptions and add the missing function parameter descriptions to avoid kernel-doc warnings: Warning: ../include/linux/rfkill.h:102 This comment starts with '/**', but isn't a kernel-doc comment. * rfkill_pause_polling(struct rfkill *rfkill) Warning: include/linux/rfkill.h:109 function parameter 'rfkill' not described in 'rfkill_pause_polling' Warning: ../include/linux/rfkill.h:112 This comment starts with '/**', but isn't a kernel-doc comment. * rfkill_resume_polling(struct rfkill *rfkill) Warning: include/linux/rfkill.h:117 function parameter 'rfkill' not described in 'rfkill_resume_polling' Warning: ../include/linux/rfkill.h:330 function parameter 'rfkill' not described in 'rfkill_get_led_trigger_name' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260723162750.167914-1-rdunlap@infradead.org Signed-off-by: Johannes Berg --- include/linux/rfkill.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index 6816e4c5f3f0..deea02a034c3 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -100,7 +100,8 @@ struct rfkill * __must_check rfkill_alloc(const char *name, int __must_check rfkill_register(struct rfkill *rfkill); /** - * rfkill_pause_polling(struct rfkill *rfkill) + * rfkill_pause_polling - Pause polling + * @rfkill: rfkill struct * * Pause polling -- say transmitter is off for other reasons. * NOTE: not necessary for suspend/resume -- in that case the @@ -110,9 +111,9 @@ int __must_check rfkill_register(struct rfkill *rfkill); void rfkill_pause_polling(struct rfkill *rfkill); /** - * rfkill_resume_polling(struct rfkill *rfkill) + * rfkill_resume_polling - Resume polling + * @rfkill: rfkill struct * - * Resume polling * NOTE: not necessary for suspend/resume -- in that case the * core stops polling anyway */ @@ -325,6 +326,8 @@ static inline enum rfkill_type rfkill_find_type(const char *name) #ifdef CONFIG_RFKILL_LEDS /** * rfkill_get_led_trigger_name - Get the LED trigger name for the button's LED. + * @rfkill: rfkill struct + * * This function might return a NULL pointer if registering of the * LED trigger failed. Use this as "default_trigger" for the LED. */ From 8e4f5ca8bf67efc6006c066e873f0535bd7a9cd9 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Fri, 24 Jul 2026 18:36:56 +0800 Subject: [PATCH 159/403] wifi: nxpwifi: reject zero-length extension elements in beacon IEs nxpwifi_update_bss_desc_with_ie() dispatches on elem->data[0] for WLAN_EID_EXTENSION without checking that the element has a payload. A well-formed extension element carries at least the element ID extension byte, but nothing enforces that in the IE stream, and the loop accepts a zero-length element because its header alone fits. elem->data[0] then reads the byte after the element, which is past the kmemdup()ed IE buffer when that element ends the stream. Fixes: 73b01e57ed3e ("wifi: nxp: add nxpwifi driver for IW61x") Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260724103656.2494129-1-lilinmao@kylinos.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/nxp/nxpwifi/scan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/nxp/nxpwifi/scan.c b/drivers/net/wireless/nxp/nxpwifi/scan.c index bb3ce2b6f4b9..b77056983e83 100644 --- a/drivers/net/wireless/nxp/nxpwifi/scan.c +++ b/drivers/net/wireless/nxp/nxpwifi/scan.c @@ -1255,6 +1255,9 @@ int nxpwifi_update_bss_desc_with_ie(struct nxpwifi_adapter *adapter, (u16)(current_ptr - bss_entry->beacon_buf); break; case WLAN_EID_EXTENSION: + if (!element_len) + return -EINVAL; + elem = (struct element *)current_ptr; switch (elem->data[0]) { From 094dc1619cb025b6fedbe609d09d3768cf645ab4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 11:54:26 +0000 Subject: [PATCH 160/403] wifi: mac80211: factor out part of ieee80211_calc_expected_tx_airtime Create ieee80211_rate_expected_tx_airtime helper function, which returns the expected tx airtime for a given rate and packet length in units of 1/1024 usec, for more accuracy. Signed-off-by: Felix Fietkau Link: https://patch.msgid.link/20260724115429.3921457-1-nbd@nbd.name Signed-off-by: Johannes Berg --- net/mac80211/airtime.c | 87 ++++++++++++++++++++++---------------- net/mac80211/ieee80211_i.h | 5 +++ 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/net/mac80211/airtime.c b/net/mac80211/airtime.c index c61df637232a..0c54cdbd753c 100644 --- a/net/mac80211/airtime.c +++ b/net/mac80211/airtime.c @@ -685,7 +685,7 @@ static int ieee80211_fill_rx_status(struct ieee80211_rx_status *stat, if (ieee80211_fill_rate_info(hw, stat, band, ri)) return 0; - if (!ieee80211_rate_valid(rate)) + if (!rate || !ieee80211_rate_valid(rate)) return -1; if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) @@ -753,6 +753,53 @@ u32 ieee80211_calc_tx_airtime(struct ieee80211_hw *hw, } EXPORT_SYMBOL_GPL(ieee80211_calc_tx_airtime); +u32 ieee80211_rate_expected_tx_airtime(struct ieee80211_hw *hw, + struct ieee80211_tx_rate *tx_rate, + struct rate_info *ri, + enum nl80211_band band, + bool ampdu, int len) +{ + struct ieee80211_rx_status stat; + u32 duration, overhead; + u8 agg_shift; + + if (ieee80211_fill_rx_status(&stat, hw, tx_rate, ri, band, len)) + return 0; + + if (stat.encoding == RX_ENC_LEGACY || !ampdu) + return ieee80211_calc_rx_airtime(hw, &stat, len) * 1024; + + duration = ieee80211_get_rate_duration(hw, &stat, &overhead); + + /* + * Assume that HT/VHT transmission on any AC except VO will + * use aggregation. Since we don't have reliable reporting + * of aggregation length, assume an average size based on the + * tx rate. + * This will not be very accurate, but much better than simply + * assuming un-aggregated tx in all cases. + */ + if (duration > 400 * 1024) /* <= VHT20 MCS2 1S */ + agg_shift = 1; + else if (duration > 250 * 1024) /* <= VHT20 MCS3 1S or MCS1 2S */ + agg_shift = 2; + else if (duration > 150 * 1024) /* <= VHT20 MCS5 1S or MCS2 2S */ + agg_shift = 3; + else if (duration > 70 * 1024) /* <= VHT20 MCS5 2S */ + agg_shift = 4; + else if (stat.encoding != RX_ENC_HE || + duration > 20 * 1024) /* <= HE40 MCS6 2S */ + agg_shift = 5; + else + agg_shift = 6; + + duration *= len; + duration /= AVG_PKT_SIZE; + duration += (overhead * 1024 >> agg_shift); + + return duration; +} + u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *pubsta, @@ -775,45 +822,13 @@ u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, if (pubsta) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); - struct ieee80211_rx_status stat; struct ieee80211_tx_rate *tx_rate = &sta->deflink.tx_stats.last_rate; struct rate_info *ri = &sta->deflink.tx_stats.last_rate_info; - u32 duration, overhead; - u8 agg_shift; + u32 duration; - if (ieee80211_fill_rx_status(&stat, hw, tx_rate, ri, band, len)) - return 0; - - if (stat.encoding == RX_ENC_LEGACY || !ampdu) - return ieee80211_calc_rx_airtime(hw, &stat, len); - - duration = ieee80211_get_rate_duration(hw, &stat, &overhead); - /* - * Assume that HT/VHT transmission on any AC except VO will - * use aggregation. Since we don't have reliable reporting - * of aggregation length, assume an average size based on the - * tx rate. - * This will not be very accurate, but much better than simply - * assuming un-aggregated tx in all cases. - */ - if (duration > 400 * 1024) /* <= VHT20 MCS2 1S */ - agg_shift = 1; - else if (duration > 250 * 1024) /* <= VHT20 MCS3 1S or MCS1 2S */ - agg_shift = 2; - else if (duration > 150 * 1024) /* <= VHT20 MCS5 1S or MCS2 2S */ - agg_shift = 3; - else if (duration > 70 * 1024) /* <= VHT20 MCS5 2S */ - agg_shift = 4; - else if (stat.encoding != RX_ENC_HE || - duration > 20 * 1024) /* <= HE40 MCS6 2S */ - agg_shift = 5; - else - agg_shift = 6; - - duration *= len; - duration /= AVG_PKT_SIZE; + duration = ieee80211_rate_expected_tx_airtime(hw, tx_rate, ri, + band, true, len); duration /= 1024; - duration += (overhead >> agg_shift); return max_t(u32, duration, 4); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 11f449e8ff00..4034b71a31cf 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2937,6 +2937,11 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, extern const struct ethtool_ops ieee80211_ethtool_ops; +u32 ieee80211_rate_expected_tx_airtime(struct ieee80211_hw *hw, + struct ieee80211_tx_rate *tx_rate, + struct rate_info *ri, + enum nl80211_band band, + bool ampdu, int len); u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *pubsta, From 2f925427e27ab684bedd37b0047c89105270747c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 11:54:27 +0000 Subject: [PATCH 161/403] wifi: mac80211: estimate expected throughput if not provided by driver/rc Estimate the tx throughput based on the expected per-packet tx time. This is useful for mesh implementations that rely on expected throughput, e.g. 802.11s or batman-adv. Signed-off-by: Felix Fietkau Link: https://patch.msgid.link/20260724115429.3921457-2-nbd@nbd.name Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 49 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 22eba0e6e54c..c03aca57eb73 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2797,6 +2797,28 @@ void sta_set_accumulated_removed_links_sinfo(struct sta_info *sta, } } +static u32 sta_estimate_expected_throughput(struct sta_info *sta, + struct rate_info *ri, + struct ieee80211_bss_conf *bss_conf) +{ + struct ieee80211_hw *hw = &sta->sdata->local->hw; + struct ieee80211_chanctx_conf *conf; + u32 duration; + u8 band; + + conf = rcu_dereference(bss_conf->chanctx_conf); + if (!conf) + return 0; + band = conf->def.chan->band; + + duration = ieee80211_rate_expected_tx_airtime(hw, NULL, ri, band, true, 1024); + duration += duration >> 4; /* add assumed packet error rate of ~6% */ + if (!duration) + return 0; + + return ((1024 * USEC_PER_SEC) / duration) * 8; +} + static void sta_set_link_sinfo(struct sta_info *sta, struct link_station_info *link_sinfo, struct ieee80211_link_data *link, @@ -3011,6 +3033,10 @@ static void sta_set_link_sinfo(struct sta_info *sta, link_sinfo->bss_param.beacon_interval = link->conf->beacon_int; thr = sta_get_expected_throughput(sta); + if (!thr && (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE))) + thr = sta_estimate_expected_throughput(sta, + &link_sinfo->txrate, + link->conf); if (thr != 0) { link_sinfo->filled |= @@ -3264,6 +3290,14 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, if (thr != 0) { sinfo->filled |= BIT_ULL(NL80211_STA_INFO_EXPECTED_THROUGHPUT); sinfo->expected_throughput = thr; + } else if (!sta->sta.valid_links && + (sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE))) { + thr = sta_estimate_expected_throughput(sta, &sinfo->txrate, + &sdata->vif.bss_conf); + if (thr) { + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_EXPECTED_THROUGHPUT); + sinfo->expected_throughput = thr; + } } if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL)) && @@ -3284,6 +3318,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, if (sta->sta.valid_links) { struct ieee80211_link_data *link; struct link_sta_info *link_sta; + u32 est_thr = 0; int link_id; sinfo->mlo_params_valid = true; @@ -3295,17 +3330,25 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, sinfo->valid_links = sta->sta.valid_links; for_each_valid_link(sinfo, link_id) { + struct link_station_info *link_sinfo = sinfo->links[link_id]; + link_sta = wiphy_dereference(sta->local->hw.wiphy, sta->link[link_id]); link = wiphy_dereference(sdata->local->hw.wiphy, sdata->link[link_id]); - if (!link_sta || !sinfo->links[link_id] || !link) { + if (!link_sta || !link_sinfo || !link) { sinfo->valid_links &= ~BIT(link_id); continue; } - sta_set_link_sinfo(sta, sinfo->links[link_id], - link, tidstats); + sta_set_link_sinfo(sta, link_sinfo, link, tidstats); + if (!thr && + (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_EXPECTED_THROUGHPUT))) + est_thr += link_sinfo->expected_throughput; + } + if (est_thr) { + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_EXPECTED_THROUGHPUT); + sinfo->expected_throughput = est_thr; } } } From 836c1addd3bf42d47ca5f7a5780a7ec52abf332e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 11:54:28 +0000 Subject: [PATCH 162/403] wifi: mac80211: add AQL support for multicast packets Excessive multicast traffic with little competing unicast traffic can easily flood hardware queues, leading to throughput issues. Additionally, filling the hardware queues with too many packets breaks FQ for multicast data. Fix this by enabling AQL for multicast packets. Signed-off-by: Felix Fietkau Link: https://patch.msgid.link/20260724115429.3921457-3-nbd@nbd.name Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 1 + include/net/mac80211.h | 3 ++- net/mac80211/debugfs.c | 13 ++++++++-- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/main.c | 1 + net/mac80211/sta_info.c | 17 ++++++++++++- net/mac80211/sta_info.h | 3 ++- net/mac80211/status.c | 5 ++-- net/mac80211/tx.c | 52 ++++++++++++++++++++------------------ 9 files changed, 66 insertions(+), 31 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a201979f066e..dcbb0e55c32e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3742,6 +3742,7 @@ enum wiphy_params_flags { /* The per TXQ device queue limit in airtime */ #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L 5000 #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H 12000 +#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_MC 50000 /* The per interface airtime threshold to switch to lower queue limit */ #define IEEE80211_AQL_THRESHOLD 24000 diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 999e5189f113..a68bccebc525 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1323,6 +1323,7 @@ ieee80211_rate_get_vht_nss(const struct ieee80211_tx_rate *rate) * @status_data: internal data for TX status handling, assigned privately, * see also &enum ieee80211_status_data for the internal documentation * @status_data_idr: indicates status data is IDR allocated ID for ack frame + * @tx_time_mc: TX time estimate is for a multicast frame, used internally * @tx_time_est: TX time estimate in units of 4us, used internally * @control: union part for control data * @control.rates: TX rates array to try @@ -1365,8 +1366,8 @@ struct ieee80211_tx_info { status_data_idr:1, status_data:13, hw_queue:4, + tx_time_mc:1, tx_time_est:10; - /* 1 free bit */ union { struct { diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index a4d5461f6480..8ebf5bcf3c0e 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -210,11 +210,13 @@ static ssize_t aql_pending_read(struct file *file, "VI %u us\n" "BE %u us\n" "BK %u us\n" + "MC %u us\n" "total %u us\n", atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]), atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]), atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]), atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]), + atomic_read(&local->aql_mc_pending_airtime), atomic_read(&local->aql_total_pending_airtime)); return simple_read_from_buffer(user_buf, count, ppos, buf, len); @@ -239,7 +241,8 @@ static ssize_t aql_txq_limit_read(struct file *file, "VO %u %u\n" "VI %u %u\n" "BE %u %u\n" - "BK %u %u\n", + "BK %u %u\n" + "MC %u\n", local->aql_txq_limit_low[IEEE80211_AC_VO], local->aql_txq_limit_high[IEEE80211_AC_VO], local->aql_txq_limit_low[IEEE80211_AC_VI], @@ -247,7 +250,8 @@ static ssize_t aql_txq_limit_read(struct file *file, local->aql_txq_limit_low[IEEE80211_AC_BE], local->aql_txq_limit_high[IEEE80211_AC_BE], local->aql_txq_limit_low[IEEE80211_AC_BK], - local->aql_txq_limit_high[IEEE80211_AC_BK]); + local->aql_txq_limit_high[IEEE80211_AC_BK], + local->aql_txq_limit_mc); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } @@ -273,6 +277,11 @@ static ssize_t aql_txq_limit_write(struct file *file, else buf[count] = '\0'; + if (sscanf(buf, "mcast %u", &q_limit_low) == 1) { + local->aql_txq_limit_mc = q_limit_low; + return count; + } + if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3) return -EINVAL; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4034b71a31cf..a1ef88fe846d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1464,10 +1464,12 @@ struct ieee80211_local { spinlock_t handle_wake_tx_queue_lock; u16 airtime_flags; + u32 aql_txq_limit_mc; u32 aql_txq_limit_low[IEEE80211_NUM_ACS]; u32 aql_txq_limit_high[IEEE80211_NUM_ACS]; u32 aql_threshold; atomic_t aql_total_pending_airtime; + atomic_t aql_mc_pending_airtime; atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS]; const struct ieee80211_ops *ops; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f996e15e3d9e..a59837b9f480 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -986,6 +986,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, spin_lock_init(&local->rx_path_lock); spin_lock_init(&local->queue_stop_reason_lock); + local->aql_txq_limit_mc = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_MC; for (i = 0; i < IEEE80211_NUM_ACS; i++) { INIT_LIST_HEAD(&local->active_txqs[i]); spin_lock_init(&local->active_txq_lock[i]); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index c03aca57eb73..d12aed9c1756 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2491,13 +2491,28 @@ EXPORT_SYMBOL(ieee80211_sta_recalc_aggregates); void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local, struct sta_info *sta, u8 ac, - u16 tx_airtime, bool tx_completed) + u16 tx_airtime, bool tx_completed, + bool mcast) { int tx_pending; if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) return; + if (mcast) { + if (!tx_completed) { + atomic_add(tx_airtime, &local->aql_mc_pending_airtime); + return; + } + + tx_pending = atomic_sub_return(tx_airtime, + &local->aql_mc_pending_airtime); + if (tx_pending < 0) + atomic_cmpxchg(&local->aql_mc_pending_airtime, + tx_pending, 0); + return; + } + if (!tx_completed) { if (sta) atomic_add(tx_airtime, diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 5da3142d8516..ee0d32877c5b 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -147,7 +147,8 @@ struct airtime_info { void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local, struct sta_info *sta, u8 ac, - u16 tx_airtime, bool tx_completed); + u16 tx_airtime, bool tx_completed, + bool mcast); struct sta_info; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index d635490f59d3..3d811f652603 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -777,7 +777,7 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local, ieee80211_sta_update_pending_airtime(local, sta, skb_get_queue_mapping(skb), tx_time_est, - true); + true, info->tx_time_mc); rcu_read_unlock(); } @@ -1189,10 +1189,11 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, /* Do this here to avoid the expensive lookup of the sta * in ieee80211_report_used_skb(). */ + bool mcast = IEEE80211_SKB_CB(skb)->tx_time_mc; ieee80211_sta_update_pending_airtime(local, sta, skb_get_queue_mapping(skb), tx_time_est, - true); + true, mcast); ieee80211_info_set_tx_time_est(IEEE80211_SKB_CB(skb), 0); } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 76489129e9a1..49ef9494cc94 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4064,20 +4064,20 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, encap_out: info->control.vif = vif; - if (tx.sta && - wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) { - bool ampdu = txq->ac != IEEE80211_AC_VO; + if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) { + bool ampdu = txq->sta && txq->ac != IEEE80211_AC_VO; u32 airtime; airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta, skb->len, ampdu); - if (airtime) { - airtime = ieee80211_info_set_tx_time_est(info, airtime); - ieee80211_sta_update_pending_airtime(local, tx.sta, - txq->ac, - airtime, - false); - } + if (!airtime) + return skb; + + airtime = ieee80211_info_set_tx_time_est(info, airtime); + info->tx_time_mc = !tx.sta; + ieee80211_sta_update_pending_airtime(local, tx.sta, txq->ac, + airtime, false, + info->tx_time_mc); } return skb; @@ -4129,6 +4129,7 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac) struct ieee80211_txq *ret = NULL; struct txq_info *txqi = NULL, *head = NULL; bool found_eligible_txq = false; + bool aql_check; spin_lock_bh(&local->active_txq_lock[ac]); @@ -4152,26 +4153,28 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac) if (!head) head = txqi; + aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq); + if (aql_check) + found_eligible_txq = true; + if (txqi->txq.sta) { struct sta_info *sta = container_of(txqi->txq.sta, struct sta_info, sta); - bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq); - s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac); - if (aql_check) - found_eligible_txq = true; - - if (deficit < 0) + if (ieee80211_sta_deficit(sta, txqi->txq.ac) < 0) { sta->airtime[txqi->txq.ac].deficit += sta->airtime_weight; - if (deficit < 0 || !aql_check) { - list_move_tail(&txqi->schedule_order, - &local->active_txqs[txqi->txq.ac]); - goto begin; + aql_check = false; } } + if (!aql_check) { + list_move_tail(&txqi->schedule_order, + &local->active_txqs[txqi->txq.ac]); + goto begin; + } + if (txqi->schedule_round == local->schedule_round[ac]) goto out; @@ -4238,7 +4241,8 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw, return true; if (!txq->sta) - return true; + return atomic_read(&local->aql_mc_pending_airtime) < + local->aql_txq_limit_mc; if (unlikely(txq->tid == IEEE80211_NUM_TIDS)) return true; @@ -4287,15 +4291,15 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw, spin_lock_bh(&local->active_txq_lock[ac]); - if (!txqi->txq.sta) - goto out; - if (list_empty(&txqi->schedule_order)) goto out; if (!ieee80211_txq_schedule_airtime_check(local, ac)) goto out; + if (!txqi->txq.sta) + goto out; + list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac], schedule_order) { if (iter == txqi) From 9a197e71eb7b45860e37a9f3bdf61a843781ae12 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 11:54:29 +0000 Subject: [PATCH 163/403] wifi: mac80211: add ieee80211_txq_aql_pending() Add a function to allow drivers to query the pending AQL airtime for a given txq, for both unicast and broadcast. This will be used for mt76 to limit buffering in AP mode for power-save stations. Signed-off-by: Felix Fietkau Link: https://patch.msgid.link/20260724115429.3921457-4-nbd@nbd.name Signed-off-by: Johannes Berg --- include/net/mac80211.h | 11 +++++++++++ net/mac80211/tx.c | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a68bccebc525..9d1fac6e8082 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -6888,6 +6888,17 @@ void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid, bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw, struct ieee80211_txq *txq); +/** + * ieee80211_txq_aql_pending - get pending AQL airtime for a txq + * + * @hw: pointer obtained from ieee80211_alloc_hw() + * @txq: pointer obtained from station or virtual interface + * + * Return: pending airtime (in usec) for the given txq. + */ +u32 ieee80211_txq_aql_pending(struct ieee80211_hw *hw, + struct ieee80211_txq *txq); + /** * ieee80211_iter_keys - iterate keys programmed into the device * @hw: pointer obtained from ieee80211_alloc_hw() diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 49ef9494cc94..0cf5f6ec75e6 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4262,6 +4262,24 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_txq_airtime_check); +u32 ieee80211_txq_aql_pending(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct sta_info *sta; + + if (unlikely(txq->tid == IEEE80211_NUM_TIDS)) + return 0; + + if (!txq->sta) + return atomic_read(&local->aql_mc_pending_airtime); + + sta = container_of(txq->sta, struct sta_info, sta); + + return atomic_read(&sta->airtime[txq->ac].aql_tx_pending); +} +EXPORT_SYMBOL(ieee80211_txq_aql_pending); + static bool ieee80211_txq_schedule_airtime_check(struct ieee80211_local *local, u8 ac) { From ef06882c7d8a7400b67d0d003b1008093dd589ed Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 24 Jul 2026 17:33:19 -0300 Subject: [PATCH 164/403] wifi: mwifiex: Detach sync cmd buffer on interrupted wait mwifiex synchronous commands keep the caller-provided data buffer in cmd_node->data_buf. Several callers pass stack-allocated objects there. If wait_event_interruptible_timeout() is interrupted, the caller can return and release that stack object while the firmware command is still the current command. A late firmware response then reaches the normal response handler, which can copy data through cmd_node->data_buf into the stale stack address. This fixes a stack corruption observed during repeated association and disassociation cycles. The panic trace showed the command wait being interrupted immediately before a bad pointer dereference: cmd_wait_q terminated: -512 Unable to handle kernel paging request at virtual address 002c583837384662 Kernel panic - not syncing: stack-protector: Kernel stack is corrupted ... Tainted: [M]=MACHINE_CHECK The fault address decodes as little-endian ASCII: 0x002c583837384662 -> "bF878X,\0" which is a fragment of the VERSION_EXT firmware string exposed as debugfs "verext": w8997o-V4, RF878X, FP92, 16.92.21.p153.7 The same runs also showed corrupted control data containing: 0x2400372e333531 -> "153.7\0$" which is the tail of the same VERSION_EXT string. This points at a late VERSION_EXT response writing through a stale stack-backed data_buf after the interrupted wait returned. After cancelling pending commands on an interrupted or timed-out wait, detach the caller-owned data buffer from the still-current command. This preserves the existing command cancellation behaviour while preventing a late response from writing through a pointer whose lifetime ended with the waiting caller. Tested on an i.MX8MP board using an 88W8997. Cc: stable@vger.kernel.org Fixes: 3d026d09b28d ("mwifiex: cancel pending commands for signal") Signed-off-by: Fabio Estevam Link: https://patch.msgid.link/20260724203320.78793-1-festevam@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index 9460d5352b23..19196848778c 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -57,6 +57,18 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n", status); mwifiex_cancel_all_pending_cmd(adapter); + + /* The command response path writes through cmd_node->data_buf. + * On an interrupted wait, the caller can return and release a + * stack-allocated data_buf before a late firmware response is + * processed. Detach the caller-owned buffer from the current + * command so a late response cannot corrupt freed stack memory. + */ + spin_lock_bh(&adapter->mwifiex_cmd_lock); + if (adapter->curr_cmd == cmd_queued) + adapter->curr_cmd->data_buf = NULL; + spin_unlock_bh(&adapter->mwifiex_cmd_lock); + return status; } From 058d979d4f418510279383e508ebc0795ab956f2 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 24 Jul 2026 17:33:20 -0300 Subject: [PATCH 165/403] wifi: mwifiex: Remove WQ_HIGHPRI from main workqueue The MWIFIEX_WORK_QUEUE handles command and event processing, including the commands used for scheduled scans. Running this work on the high-priority worker pool can interfere with latency-sensitive workloads. On an i.MX8MP-based audio system using an 88W8997, background scheduled scans caused audible glitches in USB audio playback. Remove WQ_HIGHPRI from the main workqueue so that command and scan processing use the normal-priority worker pool. Leave the RX and host MLME workqueues unchanged. Signed-off-by: Fabio Estevam Link: https://patch.msgid.link/20260724203320.78793-2-festevam@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index a8eab6b1e63b..c8df3edff675 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1550,7 +1550,7 @@ mwifiex_reinit_sw(struct mwifiex_adapter *adapter) adapter->workqueue = alloc_workqueue("MWIFIEX_WORK_QUEUE", - WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0); + WQ_MEM_RECLAIM | WQ_UNBOUND, 0); if (!adapter->workqueue) goto err_kmalloc; @@ -1716,7 +1716,7 @@ mwifiex_add_card(void *card, struct completion *fw_done, adapter->workqueue = alloc_workqueue("MWIFIEX_WORK_QUEUE", - WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0); + WQ_MEM_RECLAIM | WQ_UNBOUND, 0); if (!adapter->workqueue) goto err_kmalloc; From 7d86b0a8aceff34f7178e39261966903de01c2a1 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Mon, 27 Jul 2026 12:57:14 +0300 Subject: [PATCH 166/403] wifi: mac80211: simplify airtime_flags_write() Use 'kstrtou16_from_user()' to simplify 'airtime_flags_write()'. Signed-off-by: Dmitry Antipov Link: https://patch.msgid.link/20260727095714.347039-1-dmantipov@yandex.ru Signed-off-by: Johannes Berg --- net/mac80211/debugfs.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 8ebf5bcf3c0e..105653a16b68 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -171,23 +171,10 @@ static ssize_t airtime_flags_write(struct file *file, size_t count, loff_t *ppos) { struct ieee80211_local *local = file->private_data; - char buf[16]; + int ret; - if (count >= sizeof(buf)) - return -EINVAL; - - if (copy_from_user(buf, user_buf, count)) - return -EFAULT; - - if (count && buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - buf[count] = '\0'; - - if (kstrtou16(buf, 0, &local->airtime_flags)) - return -EINVAL; - - return count; + ret = kstrtou16_from_user(user_buf, count, 0, &local->airtime_flags); + return ret ? : count; } static const struct debugfs_short_fops airtime_flags_ops = { From 4a0bd262df757b25fc4e2a53c947317c119ced4e Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Tue, 28 Jul 2026 19:13:26 +0800 Subject: [PATCH 167/403] wifi: mac80211: fix per-STA profile length in cross-link CSA parsing ieee80211_mgd_check_cross_link_csa() starts parsing elements after the fixed per-STA profile header and the STA Info field, but subtracts only the STA Info length from the profile length. As a result, ieee802_11_parse_elems() is given sizeof(*prof) == 3 bytes beyond the current profile's element area, and data following the profile may be interpreted as belonging to it. Subtract the fixed profile header as well. The preceding ieee80211_mle_basic_sta_prof_size_ok() check guarantees that the corrected calculation cannot underflow, and ieee80211_rx_uhr_link_reconfig_req() uses the same calculation. The call site currently states that cross-link CSA parsing has no effect because the broader parsing is still incorrect. This patch does not address that broader problem; it only makes the per-STA profile parser stop at the end of that profile. No production allocation over-read or user-visible failure has been demonstrated. Fixes: 7ef8f6821d16 ("wifi: mac80211: mlme: handle cross-link CSA") Assisted-by: Codex:gpt-5.6-sol Assisted-by: Kimi:K3 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260728111326.63087-1-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 50587ab110d2..f51167f0fc46 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -8027,7 +8027,7 @@ ieee80211_mgd_check_cross_link_csa(struct ieee80211_sub_if_data *sdata, prof = (void *)sta_profiles[link_id]; prof_elems = ieee802_11_parse_elems(prof->variable + (prof->sta_info_len - 1), - len - + len - sizeof(*prof) - (prof->sta_info_len - 1), IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON, From 878654eb78c6aa0ff585baf1376567c775ca28ec Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Fri, 24 Jul 2026 08:56:13 -0700 Subject: [PATCH 168/403] wifi: ath12k: fix overreads in ath12k_wmi_process_csa_switch_count_event() There is no policy entry for WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT, so the parse infrastructure does not enforce a minimum length for the event struct. Additionally, the num_vdevs field is taken directly from firmware and used as a loop bound over the vdev_ids array without checking that it fits within the TLV payload. Either condition can cause an out-of-bounds read. Add a TLV policy entry for WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT so the parse infrastructure enforces a minimum length for the fixed-size event struct. Add a helper ath12k_wmi_tlv_data_len() to recover the payload length of a parsed TLV from the header preceding its data pointer. Use it in ath12k_wmi_process_csa_switch_count_event() to bound num_vdevs before the loop. Compile tested only. Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260724-ath12k_wmi_process_csa_switch_count_event-cleanup-v2-1-02a45d7246c0@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wmi.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 2b707ffc1a20..c2833d1e95d3 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -207,6 +207,8 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = { .min_len = sizeof(struct wmi_per_chain_rssi_stat_params) }, [WMI_TAG_OBSS_COLOR_COLLISION_EVT] = { .min_len = sizeof(struct wmi_obss_color_collision_event) }, + [WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT] = { + .min_len = sizeof(struct ath12k_wmi_pdev_csa_event) }, }; __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len) @@ -374,6 +376,13 @@ ath12k_wmi_tlv_parse(struct ath12k_base *ab, struct sk_buff *skb) return tb; } +static u32 ath12k_wmi_tlv_data_len(const void *data) +{ + const struct wmi_tlv *tlv = (const struct wmi_tlv *)data - 1; + + return le32_get_bits(tlv->header, WMI_TLV_LEN); +} + static int ath12k_wmi_cmd_send_nowait(struct ath12k_wmi_pdev *wmi, struct sk_buff *skb, u32 cmd_id) { @@ -9075,12 +9084,19 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab, const u32 *vdev_ids) { u32 current_switch_count = le32_to_cpu(ev->current_switch_count); + u32 vdev_ids_len = ath12k_wmi_tlv_data_len(vdev_ids); u32 num_vdevs = le32_to_cpu(ev->num_vdevs); struct ieee80211_bss_conf *conf; struct ath12k_link_vif *arvif; struct ath12k_vif *ahvif; int i; + if (num_vdevs > vdev_ids_len / sizeof(*vdev_ids)) { + ath12k_warn(ab, "csa switch count num_vdevs %u exceeds tlv array length %u\n", + num_vdevs, vdev_ids_len); + return; + } + rcu_read_lock(); for (i = 0; i < num_vdevs; i++) { arvif = ath12k_mac_get_arvif_by_vdev_id(ab, vdev_ids[i]); From 208d7fdb85976a737a715b81d54efaff6703880c Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Fri, 24 Jul 2026 08:56:14 -0700 Subject: [PATCH 169/403] wifi: ath11k: fix overreads in ath11k_wmi_process_csa_switch_count_event() There is no policy entry for WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT, so the parse infrastructure does not enforce a minimum length for the event struct. Additionally, the num_vdevs field is taken directly from firmware and used as a loop bound over the vdev_ids array without checking that it fits within the TLV payload. Either condition can cause an out-of-bounds read. Add a TLV policy entry for WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT so the parse infrastructure enforces a minimum length for the fixed-size event struct. Add a helper ath11k_wmi_tlv_data_len() to recover the payload length of a parsed TLV from the header preceding its data pointer. Use it in ath11k_wmi_process_csa_switch_count_event() to bound num_vdevs before the loop. Compile tested only. Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260724-ath12k_wmi_process_csa_switch_count_event-cleanup-v2-2-02a45d7246c0@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/wmi.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 4cbd7293845a..d6feaa710fe2 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -159,6 +159,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = { .min_len = sizeof(struct ath11k_wmi_p2p_noa_info) }, [WMI_TAG_P2P_NOA_EVENT] = { .min_len = sizeof(struct wmi_p2p_noa_event) }, + [WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT] = { + .min_len = sizeof(struct wmi_pdev_csa_switch_ev) }, }; #define PRIMAP(_hw_mode_) \ @@ -262,6 +264,13 @@ const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, return tb; } +static u32 ath11k_wmi_tlv_data_len(const void *data) +{ + const struct wmi_tlv *tlv = (const struct wmi_tlv *)data - 1; + + return FIELD_GET(WMI_TLV_LEN, tlv->header); +} + static int ath11k_wmi_cmd_send_nowait(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, u32 cmd_id) { @@ -8359,15 +8368,23 @@ ath11k_wmi_process_csa_switch_count_event(struct ath11k_base *ab, const struct wmi_pdev_csa_switch_ev *ev, const u32 *vdev_ids) { - int i; + u32 vdev_ids_len = ath11k_wmi_tlv_data_len(vdev_ids); + u32 num_vdevs = ev->num_vdevs; struct ath11k_vif *arvif; + int i; /* Finish CSA once the switch count becomes NULL */ if (ev->current_switch_count) return; + if (num_vdevs > vdev_ids_len / sizeof(*vdev_ids)) { + ath11k_warn(ab, "csa switch count num_vdevs %u exceeds tlv array length %u\n", + num_vdevs, vdev_ids_len); + return; + } + rcu_read_lock(); - for (i = 0; i < ev->num_vdevs; i++) { + for (i = 0; i < num_vdevs; i++) { arvif = ath11k_mac_get_arvif_by_vdev_id(ab, vdev_ids[i]); if (!arvif) { From 8e415b8068480d51a057197ded974e2637e8c42b Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 26 Jul 2026 16:02:07 -0700 Subject: [PATCH 170/403] wifi: ath12k: validate TLV length in process_tpc_stats() The outer skb->len guard only confirms the SKB is large enough to hold the full fixed_param struct, but the TLV's own WMI_TLV_LEN field is never checked. Firmware advertising a TLV length shorter than sizeof(*fixed_param) causes reads of pdev_id and event_count beyond the declared TLV payload. Add a check that the TLV length is at least sizeof(*fixed_param) before casting and dereferencing the pointer. Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260726-ath12k_wmi_process_tpc_stats-len-check-v1-1-c4ba2f84d9c6@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wmi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index c2833d1e95d3..e9e7566e0f69 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -9986,6 +9986,7 @@ static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, void *ptr = skb->data; struct ath12k *ar; u16 tlv_tag; + u16 tlv_len; u32 event_count; int ret; @@ -10001,6 +10002,7 @@ static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, tlv = (struct wmi_tlv *)ptr; tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG); + tlv_len = le32_get_bits(tlv->header, WMI_TLV_LEN); ptr += sizeof(*tlv); if (tlv_tag != WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM) { @@ -10008,6 +10010,12 @@ static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, return; } + if (tlv_len < sizeof(*fixed_param)) { + ath12k_warn(ab, "TPC stats fixed param tlv len %u too short\n", + tlv_len); + return; + } + fixed_param = (struct ath12k_wmi_pdev_tpc_stats_event_fixed_params *)ptr; rcu_read_lock(); ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(fixed_param->pdev_id) + 1); From 0702eddffff1b637a9e90187785a0b44542bb365 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sat, 25 Jul 2026 11:11:45 -0700 Subject: [PATCH 171/403] wifi: ath12k: move firmware_mode enum to qmi.h The enum ath12k_firmware_mode defines values that are part of the QMI ABI, so it belongs in qmi.h rather than core.h. Consolidate it there along with ATH12K_FIRMWARE_MODE_OFF, which is currently a bare macro. Rename the enum to ath12k_qmi_firmware_mode to align with the naming convention of the other enums in qmi.h, and place it with the other ath12k_qmi_* enums. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260725-consolidate-firmware_mode-v1-1-aedff0ce0ba5@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.c | 3 ++- drivers/net/wireless/ath/ath12k/core.h | 10 +--------- drivers/net/wireless/ath/ath12k/pci.c | 1 + drivers/net/wireless/ath/ath12k/qmi.c | 4 ++-- drivers/net/wireless/ath/ath12k/qmi.h | 14 ++++++++++++-- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index a9112760185f..a052a77828f3 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -23,6 +23,7 @@ #include "wow.h" #include "dp_cmn.h" #include "peer.h" +#include "qmi.h" unsigned int ath12k_debug_mask; module_param_named(debug_mask, ath12k_debug_mask, uint, 0644); @@ -1188,7 +1189,7 @@ static int ath12k_core_hw_group_start(struct ath12k_hw_group *ag) } static int ath12k_core_start_firmware(struct ath12k_base *ab, - enum ath12k_firmware_mode mode) + enum ath12k_qmi_firmware_mode mode) { int ret; diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 37a194e00248..ecb451d93f45 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -160,14 +160,6 @@ enum ath12k_hw_rev { ATH12K_HW_IPQ5424_HW10, }; -enum ath12k_firmware_mode { - /* the default mode, standard 802.11 functionality */ - ATH12K_FIRMWARE_MODE_NORMAL, - - /* factory tests etc */ - ATH12K_FIRMWARE_MODE_FTM, -}; - #define ATH12K_IRQ_NUM_MAX 57 #define ATH12K_EXT_IRQ_NUM_MAX 16 #define ATH12K_MAX_TCL_RING_NUM 3 @@ -1147,7 +1139,7 @@ struct ath12k_base { struct ath12k_hw_group *ag; struct ath12k_wsi_info wsi_info; - enum ath12k_firmware_mode fw_mode; + enum ath12k_qmi_firmware_mode fw_mode; struct ath12k_ftm_event_obj ftm_event_obj; bool hw_group_ref; diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index ad74140e0fa5..907d29b1020c 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -17,6 +17,7 @@ #include "mhi.h" #include "debug.h" #include "hal.h" +#include "qmi.h" #define ATH12K_PCI_BAR_NUM 0 #define ATH12K_PCI_DMA_MASK 36 diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index bb61c78e5c29..c466c3ae793a 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -3431,7 +3431,7 @@ int ath12k_qmi_wlanfw_aux_uc_info_send(struct ath12k_base *ab) } static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab, - u32 mode) + enum ath12k_qmi_firmware_mode mode) { struct qmi_wlanfw_wlan_mode_req_msg_v01 req = {}; struct qmi_wlanfw_wlan_mode_resp_msg_v01 resp = {}; @@ -3631,7 +3631,7 @@ void ath12k_qmi_firmware_stop(struct ath12k_base *ab) } int ath12k_qmi_firmware_start(struct ath12k_base *ab, - u32 mode) + enum ath12k_qmi_firmware_mode mode) { int ret; diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h index cbe5be30053a..27b69847a15e 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.h +++ b/drivers/net/wireless/ath/ath12k/qmi.h @@ -32,7 +32,6 @@ #define QMI_WLFW_FW_READY_IND_V01 0x0038 #define QMI_WLANFW_MAX_DATA_SIZE_V01 6144 -#define ATH12K_FIRMWARE_MODE_OFF 4 #define ATH12K_BOARD_ID_DEFAULT 0xFF @@ -602,6 +601,17 @@ enum ath12k_qmi_mem_mode { ATH12K_QMI_MEMORY_MODE_LOW_512_M, }; +enum ath12k_qmi_firmware_mode { + /* the default mode, standard 802.11 functionality */ + ATH12K_FIRMWARE_MODE_NORMAL, + + /* factory tests etc */ + ATH12K_FIRMWARE_MODE_FTM, + + /* firmware offline */ + ATH12K_FIRMWARE_MODE_OFF = 4, +}; + static inline void ath12k_qmi_set_event_block(struct ath12k_qmi *qmi, bool block) { lockdep_assert_held(&qmi->event_lock); @@ -617,7 +627,7 @@ static inline bool ath12k_qmi_get_event_block(struct ath12k_qmi *qmi) } int ath12k_qmi_firmware_start(struct ath12k_base *ab, - u32 mode); + enum ath12k_qmi_firmware_mode mode); void ath12k_qmi_firmware_stop(struct ath12k_base *ab); void ath12k_qmi_deinit_service(struct ath12k_base *ab); int ath12k_qmi_init_service(struct ath12k_base *ab); From 0090ec7ad252f1a597d782b7bce7eff7bdde00c0 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sat, 25 Jul 2026 11:11:46 -0700 Subject: [PATCH 172/403] wifi: ath12k: rename firmware_mode enum members to use QMI namespace The enumerator names ATH12K_FIRMWARE_MODE_* lack the QMI infix that all other constants in qmi.h use (ATH12K_QMI_FILE_TYPE_*, ATH12K_QMI_BDF_TYPE_*, ATH12K_QMI_MEMORY_MODE_*, etc.). Rename them to ATH12K_QMI_FIRMWARE_MODE_* for consistency and to prevent a future re-introduction of ATH12K_FIRMWARE_MODE_* names causing a silent collision. While here, add a comment noting that values 2-3 are reserved by the firmware QMI ABI to explain the gap before ATH12K_QMI_FIRMWARE_MODE_OFF = 4. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260725-consolidate-firmware_mode-v1-2-aedff0ce0ba5@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.c | 2 +- drivers/net/wireless/ath/ath12k/mac.c | 2 +- drivers/net/wireless/ath/ath12k/pci.c | 2 +- drivers/net/wireless/ath/ath12k/qmi.c | 4 ++-- drivers/net/wireless/ath/ath12k/qmi.h | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index a052a77828f3..d023c646478f 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -795,7 +795,7 @@ static int ath12k_core_soc_create(struct ath12k_base *ab) int ret; if (ath12k_ftm_mode) { - ab->fw_mode = ATH12K_FIRMWARE_MODE_FTM; + ab->fw_mode = ATH12K_QMI_FIRMWARE_MODE_FTM; ath12k_info(ab, "Booting in ftm mode\n"); } diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 310976247dbb..9a775602775d 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -859,7 +859,7 @@ struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id) return NULL; for (i = 0; i < ab->num_radios; i++) { - if (ab->fw_mode == ATH12K_FIRMWARE_MODE_FTM) + if (ab->fw_mode == ATH12K_QMI_FIRMWARE_MODE_FTM) pdev = &ab->pdevs[i]; else pdev = rcu_dereference(ab->pdevs_active[i]); diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 907d29b1020c..6441927b5382 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -1556,7 +1556,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev, ab_pci->ab = ab; ab_pci->pdev = pdev; ab->hif.ops = &ath12k_pci_hif_ops; - ab->fw_mode = ATH12K_FIRMWARE_MODE_NORMAL; + ab->fw_mode = ATH12K_QMI_FIRMWARE_MODE_NORMAL; pci_set_drvdata(pdev, ab); spin_lock_init(&ab_pci->window_lock); diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index c466c3ae793a..280e50a1f31d 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -3460,7 +3460,7 @@ static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab, ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH12K_QMI_WLANFW_TIMEOUT_MS)); if (ret < 0) { - if (mode == ATH12K_FIRMWARE_MODE_OFF && ret == -ENETRESET) { + if (mode == ATH12K_QMI_FIRMWARE_MODE_OFF && ret == -ENETRESET) { ath12k_warn(ab, "WLFW service is dis-connected\n"); return 0; } @@ -3623,7 +3623,7 @@ void ath12k_qmi_firmware_stop(struct ath12k_base *ab) clear_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, &ab->dev_flags); - ret = ath12k_qmi_wlanfw_mode_send(ab, ATH12K_FIRMWARE_MODE_OFF); + ret = ath12k_qmi_wlanfw_mode_send(ab, ATH12K_QMI_FIRMWARE_MODE_OFF); if (ret < 0) { ath12k_warn(ab, "qmi failed to send wlan mode off\n"); return; diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h index 27b69847a15e..6da10f3cb597 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.h +++ b/drivers/net/wireless/ath/ath12k/qmi.h @@ -603,13 +603,13 @@ enum ath12k_qmi_mem_mode { enum ath12k_qmi_firmware_mode { /* the default mode, standard 802.11 functionality */ - ATH12K_FIRMWARE_MODE_NORMAL, + ATH12K_QMI_FIRMWARE_MODE_NORMAL, /* factory tests etc */ - ATH12K_FIRMWARE_MODE_FTM, + ATH12K_QMI_FIRMWARE_MODE_FTM, - /* firmware offline */ - ATH12K_FIRMWARE_MODE_OFF = 4, + /* firmware offline; values 2-3 reserved by firmware ABI */ + ATH12K_QMI_FIRMWARE_MODE_OFF = 4, }; static inline void ath12k_qmi_set_event_block(struct ath12k_qmi *qmi, bool block) From 534459ac562b207ba1a9bb2c95dba77b5939e2da Mon Sep 17 00:00:00 2001 From: Pavankumar Nandeshwar Date: Tue, 21 Jul 2026 16:34:59 +0530 Subject: [PATCH 173/403] wifi: ath12k: Use different RX release ring sizes as per memory profiles Currently, the RX release ring size is hardcoded to 1024 entries via DP_RX_RELEASE_RING_SIZE. This value was sufficient for older generations, but is not adequate for Wi-Fi 7 scenarios with higher aggregation, parallel processing, and increased likelihood of error bursts. In Wi-Fi 7, a PPDU can carry up to 1024 MPDUs and each MPDU may contain multiple MSDUs. In error scenarios such as REO out-of-order (OOR) events, a large number of MSDUs can be pushed to the RX release ring in a short duration. With multiple PPDUs being processed in parallel (e.g. multi-core or MLO scenarios), this can lead to significant bursts of descriptors. Field observations have shown frequent OOR conditions and back-pressure issues with smaller ring sizes. Increasing the RX release ring size helps absorb these bursts and avoids back-pressure in the RXDMA/REO pipeline. Without sufficient ring capacity (e.g. 16K), back-pressure was observed under stress conditions. To address this, make the RX release ring size configurable per memory profile by adding rx_release_ring_size to ath12k_dp_profile_params: - Default memory profile: 16384 entries - Low memory profile (512M): 8192 entries The larger size in the default profile improves robustness under high traffic and error conditions by reducing the probability of ring overflow and pipeline stalls. The reduced size in the low memory profile balances memory usage while still providing sufficient headroom compared to the previous fixed value. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Pavankumar Nandeshwar Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260721110459.2203038-1-pavankumar.nandeshwar@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.c | 2 ++ drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/dp.c | 2 +- drivers/net/wireless/ath/ath12k/dp.h | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index d023c646478f..cb4ca93f625c 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -53,6 +53,7 @@ ath12k_mem_profile_based_param ath12k_mem_profile_based_param[] = { .rxdma_monitor_dst_ring_size = 8192, .num_pool_tx_desc = 32768, .rx_desc_count = 12288, + .rx_release_ring_size = 16384, }, }, [ATH12K_QMI_MEMORY_MODE_LOW_512_M] = { @@ -66,6 +67,7 @@ ath12k_mem_profile_based_param ath12k_mem_profile_based_param[] = { .rxdma_monitor_dst_ring_size = 512, .num_pool_tx_desc = 16384, .rx_desc_count = 6144, + .rx_release_ring_size = 8192, }, }, }; diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index ecb451d93f45..3bd71bf02afb 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -931,6 +931,7 @@ struct ath12k_dp_profile_params { u32 rxdma_monitor_dst_ring_size; u32 num_pool_tx_desc; u32 rx_desc_count; + u32 rx_release_ring_size; }; struct ath12k_mem_profile_based_param { diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index fbc0788b37a0..f9b37d75956d 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -487,7 +487,7 @@ static int ath12k_dp_srng_common_setup(struct ath12k_base *ab) ret = ath12k_dp_srng_setup(ab, &dp->rx_rel_ring, HAL_WBM2SW_RELEASE, HAL_WBM2SW_REL_ERR_RING_NUM, 0, - DP_RX_RELEASE_RING_SIZE); + DP_RX_RELEASE_RING_SIZE(ab)); if (ret) { ath12k_warn(ab, "failed to set up rx_rel ring :%d\n", ret); goto err; diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index a94bbc337df4..bef0f2ba0560 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -200,7 +200,8 @@ struct ath12k_pdev_dp { #define DP_REO_DST_RING_MAX 8 #define DP_REO_DST_RING_SIZE 2048 #define DP_REO_REINJECT_RING_SIZE 32 -#define DP_RX_RELEASE_RING_SIZE 1024 +#define DP_RX_RELEASE_RING_SIZE(ab) \ + ((ab)->profile_param->dp_params.rx_release_ring_size) #define DP_REO_EXCEPTION_RING_SIZE 128 #define DP_REO_CMD_RING_SIZE 256 #define DP_REO_STATUS_RING_SIZE 2048 From 43c521c11ce8fe904e14d1ae0566ffea931cf2e9 Mon Sep 17 00:00:00 2001 From: Pavankumar Nandeshwar Date: Thu, 23 Jul 2026 11:16:53 +0530 Subject: [PATCH 174/403] wifi: ath12k: skip MLO multicast links during crash recovery in Tx path In ath12k_wifi7_mac_op_tx(), the MLO multicast broadcast path iterates over all active links and copies the original skb for transmission on each link. When firmware crash recovery is underway (ATH12K_FLAG_CRASH_FLUSH set), the per-link copy is allocated and partially processed before ath12k_wifi7_dp_tx() eventually rejects it with -ESHUTDOWN. This wastes GFP_ATOMIC memory and produces spurious "failed to transmit frame" warnings for every active MLO link during the recovery window. The unicast and non-MLO paths are unaffected: they call ath12k_wifi7_dp_tx() directly, which already guards against the flag at its entry. Skip any link whose associated ath12k_base has ATH12K_FLAG_CRASH_FLUSH set before performing the skb_copy(), matching the behaviour of ath12k_wifi7_dp_tx() but avoiding the unnecessary allocation entirely. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Pavankumar Nandeshwar Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260723054653.2794550-1-pavankumar.nandeshwar@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/hw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index 4c1119edcaed..d890801d5822 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -1032,6 +1032,9 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, continue; tmp_ar = tmp_arvif->ar; + if (unlikely(test_bit(ATH12K_FLAG_CRASH_FLUSH, &tmp_ar->ab->dev_flags))) + continue; + tmp_dp = ath12k_ab_to_dp(tmp_ar->ab); tmp_dp_pdev = ath12k_dp_to_pdev_dp(tmp_dp, tmp_ar->pdev_idx); From 96f46607bbcee8aac00c4b5a1213b7d82ceee36d Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Tue, 21 Jul 2026 12:20:37 +0530 Subject: [PATCH 175/403] wifi: ath12k: add AHB platform descriptor support AHB-based platforms associate each device with a userPD ID that determines the firmware name and Peripheral Authentication Service ID (PASID) used during firmware authentication. Current implementation does not support platforms with multiple devices sharing the same compatible string but using different userPD IDs. As a result, the driver cannot uniquely identify each device for firmware selection and authentication. Add an AHB platform descriptor to store device-specific configuration. Implement userPD ID resolution by matching device tree reg properties, with node name matching as a fallback. Centralize platform configuration to simplify the probe path by removing hardware-specific conditionals. Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260721065038.126046-2-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/ahb.c | 4 +- drivers/net/wireless/ath/ath12k/ahb.h | 19 +++++ drivers/net/wireless/ath/ath12k/hw.h | 1 - drivers/net/wireless/ath/ath12k/wifi7/ahb.c | 90 +++++++++++++++++---- 4 files changed, 94 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c index 07bb83710b1f..14ee696960c7 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.c +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -704,7 +704,7 @@ static int ath12k_ahb_map_service_to_pipe(struct ath12k_base *ab, u16 service_id return 0; } -static const struct ath12k_hif_ops ath12k_ahb_hif_ops = { +const struct ath12k_hif_ops ath12k_ahb_hif_ops = { .start = ath12k_ahb_start, .stop = ath12k_ahb_stop, .read32 = ath12k_ahb_read32, @@ -715,6 +715,7 @@ static const struct ath12k_hif_ops ath12k_ahb_hif_ops = { .power_up = ath12k_ahb_power_up, .power_down = ath12k_ahb_power_down, }; +EXPORT_SYMBOL(ath12k_ahb_hif_ops); static irqreturn_t ath12k_userpd_irq_handler(int irq, void *data) { @@ -1038,7 +1039,6 @@ static int ath12k_ahb_probe(struct platform_device *pdev) ab_ahb = ath12k_ab_to_ahb(ab); ab_ahb->ab = ab; - ab->hif.ops = &ath12k_ahb_hif_ops; ab->pdev = pdev; platform_set_drvdata(pdev, ab); diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h index a153db6cf1d3..037347ccd21b 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.h +++ b/drivers/net/wireless/ath/ath12k/ahb.h @@ -30,6 +30,24 @@ #define ATH12K_USERPD_ID_MASK GENMASK(10, 8) #define ATH12K_USERPD_FW_NAME_LEN 35 +enum ath12k_ahb_userpd_id { + ATH12K_AHB_USERPD_ID_0 = 1, + ATH12K_AHB_USERPD_ID_1, + ATH12K_AHB_USERPD_ID_2, +}; + +struct ath12k_ahb_userpd_map { + phys_addr_t io_start; + const char *node_name; + u32 upd_id; +}; + +struct ath12k_ahb_desc { + enum ath12k_hw_rev hw_rev; + bool auth_enabled; + const struct ath12k_hif_ops *ops; +}; + enum ath12k_ahb_smp2p_msg_id { ATH12K_AHB_POWER_SAVE_ENTER = 1, ATH12K_AHB_POWER_SAVE_EXIT, @@ -43,6 +61,7 @@ enum ath12k_ahb_userpd_irq { }; struct ath12k_base; +extern const struct ath12k_hif_ops ath12k_ahb_hif_ops; struct ath12k_ahb_device_family_ops { int (*probe)(struct platform_device *pdev); diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index 49cfd5dfc70a..3ed38f8f2b48 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -100,7 +100,6 @@ struct ieee80211_rx_status; #define ATH12K_REGDB_FILE_NAME "regdb.bin" #define ATH12K_PCIE_MAX_PAYLOAD_SIZE 128 -#define ATH12K_IPQ5332_USERPD_ID 1 enum ath12k_hw_rate_cck { ATH12K_HW_RATE_CCK_LP_11M = 0, diff --git a/drivers/net/wireless/ath/ath12k/wifi7/ahb.c b/drivers/net/wireless/ath/ath12k/wifi7/ahb.c index 6a8b8b2a56f9..98a6606ffd76 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/ahb.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/ahb.c @@ -15,44 +15,100 @@ #include "dp.h" #include "core.h" +/* + * Node name to UserPD ID mapping + * + * The io_start field is used for additional validation when the reg + * property is present in the device tree. If io_start is 0, only + * node_name matching is performed. + * + * For platforms where not all WiFi nodes have a 'reg' property, set + * io_start to 0 for those entries. The driver will match purely by + * node name in such cases. + */ +static const struct ath12k_ahb_userpd_map ath12k_wifi7_ahb_userpd_map[] = { + { .io_start = 0x0c000000, .node_name = "wifi", .upd_id = ATH12K_AHB_USERPD_ID_0 }, +}; + +static const struct ath12k_ahb_desc ath12k_wifi7_ahb_desc[] = { + [ATH12K_HW_IPQ5332_HW10] = { + .hw_rev = ATH12K_HW_IPQ5332_HW10, + .auth_enabled = true, + .ops = &ath12k_ahb_hif_ops, + }, + [ATH12K_HW_IPQ5424_HW10] = { + .hw_rev = ATH12K_HW_IPQ5424_HW10, + .auth_enabled = false, + .ops = &ath12k_ahb_hif_ops, + }, +}; + static const struct of_device_id ath12k_wifi7_ahb_of_match[] = { { .compatible = "qcom,ipq5332-wifi", - .data = (void *)ATH12K_HW_IPQ5332_HW10, + .data = (void *)&ath12k_wifi7_ahb_desc[ATH12K_HW_IPQ5332_HW10], }, { .compatible = "qcom,ipq5424-wifi", - .data = (void *)ATH12K_HW_IPQ5424_HW10, + .data = (void *)&ath12k_wifi7_ahb_desc[ATH12K_HW_IPQ5424_HW10], }, { } }; MODULE_DEVICE_TABLE(of, ath12k_wifi7_ahb_of_match); +/* + * ath12k_wifi7_ahb_get_userpd_id - Resolve UserPD ID from DT properties + * @ab: ath12k base structure + * + * Returns: UserPD ID (1-based) on success, 0 on failure + * + * Resolution logic: + * 1. If reg property exist in DT, get userpd_id from io_start + * 2. If reg property is absent, get userpd_id from DT node name + * 3. Return 0 if no match found (probe will fail) + */ +static u32 ath12k_wifi7_ahb_get_userpd_id(struct ath12k_base *ab) +{ + const struct ath12k_ahb_userpd_map *map; + struct resource *res; + size_t i; + + res = platform_get_resource(ab->pdev, IORESOURCE_MEM, 0); + + for (i = 0; i < ARRAY_SIZE(ath12k_wifi7_ahb_userpd_map); i++) { + map = &ath12k_wifi7_ahb_userpd_map[i]; + + if (res) { + if (map->io_start && map->io_start == res->start) + return map->upd_id; + } else if (map->node_name && + of_node_name_eq(ab->dev->of_node, map->node_name)) { + return map->upd_id; + } + } + + return 0; +} + static int ath12k_wifi7_ahb_probe(struct platform_device *pdev) { + const struct ath12k_ahb_desc *desc; struct ath12k_ahb *ab_ahb; - enum ath12k_hw_rev hw_rev; struct ath12k_base *ab; int ret; ab = platform_get_drvdata(pdev); ab_ahb = ath12k_ab_to_ahb(ab); - - hw_rev = (enum ath12k_hw_rev)(kernel_ulong_t)of_device_get_match_data(&pdev->dev); - switch (hw_rev) { - case ATH12K_HW_IPQ5332_HW10: - ab_ahb->userpd_id = ATH12K_IPQ5332_USERPD_ID; - ab_ahb->scm_auth_enabled = true; - break; - case ATH12K_HW_IPQ5424_HW10: - ab_ahb->userpd_id = ATH12K_IPQ5332_USERPD_ID; - ab_ahb->scm_auth_enabled = false; - break; - default: + desc = of_device_get_match_data(&pdev->dev); + if (!desc) return -EOPNOTSUPP; - } ab->target_mem_mode = ATH12K_QMI_MEMORY_MODE_DEFAULT; - ab->hw_rev = hw_rev; + ab->hw_rev = desc->hw_rev; + ab->hif.ops = desc->ops; + ab_ahb->scm_auth_enabled = desc->auth_enabled; + ab_ahb->userpd_id = ath12k_wifi7_ahb_get_userpd_id(ab); + if (!ab_ahb->userpd_id) + return -EOPNOTSUPP; ret = ath12k_wifi7_hw_init(ab); if (ret) { From c6ab3b1dfa3e62dbf42c66121037de460a182642 Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Tue, 21 Jul 2026 12:20:38 +0530 Subject: [PATCH 176/403] wifi: ath12k: Share RootPD state across UserPDs to avoid duplicate operations Currently, each ath12k AHB device maintains its own RootPD-related information. However, RootPD is shared across all UserPD devices, so RootPD-related operations such as RootPD boot, and notifier registration, should be performed only once during the first UserPD boot up. Due to per-device RootPD information, the driver is unable to track shared RootPD state across multiple UserPDs, which can result in these operations being performed multiple times. Fix this by introducing a new ath12k_ahb_rproc_info structure to hold shared RootPD-related information such as notifier callbacks, boot state, and number of userPD. Allocate this structure during the first device probe in ath12k_ahb_rproc_info_alloc() and reuse the same structure for all subsequent device probes. Also handle rproc deconfiguration correctly when multiple UserPDs share a common RootPD. The RootPD provides shared firmware services and resources for all UserPDs. Therefore, do not shut down the RootPD while any UserPD remains powered on or is still in the boot process. In addition, a UserPD can be powered down before its associated resources are fully released. Defer g_rproc_info cleanup until all UserPD-related state and resources have been cleaned up. For intermediate UserPD removal, cleanup only per-device information and remove the UserPD from the tracking array while keeping the RootPD running for remaining active UserPDs. Note: UserPD IDs start from 1, as ID 0 is used by RootPD, which is completely handled by the remoteproc driver. The multi-PD architecture on AHB platforms operates as follows: +-----------------------------+ | Q6 RootPD (rproc) | | (Shared Resource) | | | | - Manages UserPD lifecycle | | - Provides SSR notifiers | +--------------+--------------+ | | Manages | +---------------------+---------------------+ | | | +----v----+ +----v----+ +----v----+ | UserPD1 | | UserPD2 | | UserPD3 | | ID=1 | | ID=2 | | ID=3 | | (Radio) | | (Radio) | | (Radio) | +---------+ +---------+ +---------+ | | | | | | ath12k_ahb ath12k_ahb ath12k_ahb (device 1) (device 2) (device 3) | | | +---------------------+---------------------+ | | All reference | +---------v----------+ | ath12k_ahb_rproc_ | | info (shared) | | | | - tgt_rproc | | - notifiers | | - rootpd_ready | | - num_userpd | | - userpd[] array | +--------------------+ Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260721065038.126046-3-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/ahb.c | 211 +++++++++++++++++++++----- drivers/net/wireless/ath/ath12k/ahb.h | 15 +- 2 files changed, 180 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c index 14ee696960c7..0fc55c9169e1 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.c +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -25,6 +25,22 @@ static const char ath12k_userpd_irq[][9] = {"spawn", "ready", "stop-ack"}; +/* + * Multi-UserPD Architecture: + * + * One Q6 RootPD (managed by separate rproc driver) supports multiple + * ath12k UserPDs. Each UserPD represents a WiFi radio instance. + * + * Lifecycle: + * - RootPD boots when first UserPD probes + * - All UserPDs share RootPD's SSR notifier + * + * Locking: + * - ath12k_rproc_info_lock: Protects g_rproc_info allocation/free + */ +static struct ath12k_ahb_rproc_info *g_rproc_info; +static DEFINE_MUTEX(ath12k_rproc_info_lock); + static const char *irq_name[ATH12K_IRQ_NUM_MAX] = { "misc-pulse1", "misc-latch", @@ -786,44 +802,85 @@ static int ath12k_ahb_config_rproc_irq(struct ath12k_base *ab) static int ath12k_ahb_root_pd_state_notifier(struct notifier_block *nb, const unsigned long event, void *data) { - struct ath12k_ahb *ab_ahb = container_of(nb, struct ath12k_ahb, root_pd_nb); - struct ath12k_base *ab = ab_ahb->ab; + struct ath12k_ahb_rproc_info *rproc_info = + container_of(nb, struct ath12k_ahb_rproc_info, root_pd_nb); if (event == ATH12K_RPROC_AFTER_POWERUP) { - ath12k_dbg(ab, ATH12K_DBG_AHB, "Root PD is UP\n"); - complete(&ab_ahb->rootpd_ready); + ath12k_generic_dbg(ATH12K_DBG_AHB, "Root PD is UP\n"); + complete(&rproc_info->rootpd_ready); } return 0; } -static int ath12k_ahb_register_rproc_notifier(struct ath12k_base *ab) +static int ath12k_ahb_register_rproc_notifier(void) { - struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + int ret; - ab_ahb->root_pd_nb.notifier_call = ath12k_ahb_root_pd_state_notifier; - init_completion(&ab_ahb->rootpd_ready); + lockdep_assert_held(&ath12k_rproc_info_lock); - ab_ahb->root_pd_notifier = qcom_register_ssr_notifier(ab_ahb->tgt_rproc->name, - &ab_ahb->root_pd_nb); - if (IS_ERR(ab_ahb->root_pd_notifier)) - return PTR_ERR(ab_ahb->root_pd_notifier); + if (g_rproc_info->root_pd_notifier) + return 0; + + g_rproc_info->root_pd_nb.notifier_call = ath12k_ahb_root_pd_state_notifier; + + g_rproc_info->root_pd_notifier = + qcom_register_ssr_notifier(g_rproc_info->tgt_rproc->name, + &g_rproc_info->root_pd_nb); + if (IS_ERR(g_rproc_info->root_pd_notifier)) { + ret = PTR_ERR(g_rproc_info->root_pd_notifier); + g_rproc_info->root_pd_notifier = NULL; + return ret; + } return 0; } -static void ath12k_ahb_unregister_rproc_notifier(struct ath12k_base *ab) +static void ath12k_ahb_unregister_rproc_notifier(void) { - struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + lockdep_assert_held(&ath12k_rproc_info_lock); - if (!ab_ahb->root_pd_notifier) { - ath12k_err(ab, "Rproc notifier not registered\n"); + if (!g_rproc_info->root_pd_notifier) return; - } - qcom_unregister_ssr_notifier(ab_ahb->root_pd_notifier, - &ab_ahb->root_pd_nb); - ab_ahb->root_pd_notifier = NULL; + qcom_unregister_ssr_notifier(g_rproc_info->root_pd_notifier, + &g_rproc_info->root_pd_nb); + g_rproc_info->root_pd_notifier = NULL; +} + +static void ath12k_ahb_cleanup_userpd(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + struct ath12k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info; + + lockdep_assert_held(&ath12k_rproc_info_lock); + + if (!rproc_info) + return; + + rproc_info->userpd[ab_ahb->userpd_id - 1] = NULL; + rproc_info->num_userpd--; + ab_ahb->rproc_info = NULL; +} + +static struct ath12k_ahb_rproc_info *ath12k_ahb_rproc_info_alloc(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + struct ath12k_ahb_rproc_info *rproc_info; + + lockdep_assert_held(&ath12k_rproc_info_lock); + + rproc_info = kzalloc_obj(*rproc_info, GFP_KERNEL); + if (!rproc_info) + return NULL; + + rproc_info->rootpd_booted_by_driver = false; + rproc_info->userpd[ab_ahb->userpd_id - 1] = ab_ahb; + rproc_info->num_userpd = 1; + init_completion(&rproc_info->rootpd_ready); + ab_ahb->rproc_info = rproc_info; + + return rproc_info; } static int ath12k_ahb_get_rproc(struct ath12k_base *ab) @@ -832,37 +889,69 @@ static int ath12k_ahb_get_rproc(struct ath12k_base *ab) struct device *dev = ab->dev; struct device_node *np; struct rproc *prproc; + int ret; + + lockdep_assert_held(&ath12k_rproc_info_lock); + + if (ab_ahb->userpd_id > ATH12K_MAX_DEVICES) + return -ENOSPC; + + if (g_rproc_info) { + if (g_rproc_info->num_userpd >= ATH12K_MAX_DEVICES) { + ath12k_err(ab, "Max UserPD limit reached\n"); + return -ENOSPC; + } + + g_rproc_info->userpd[ab_ahb->userpd_id - 1] = ab_ahb; + g_rproc_info->num_userpd++; + ab_ahb->rproc_info = g_rproc_info; + return 0; + } + + g_rproc_info = ath12k_ahb_rproc_info_alloc(ab); + if (!g_rproc_info) + return -ENOMEM; np = of_parse_phandle(dev->of_node, "qcom,rproc", 0); if (!np) { ath12k_err(ab, "failed to get q6_rproc handle\n"); - return -ENOENT; + ret = -ENOENT; + goto err_free_rproc_info; } prproc = rproc_get_by_phandle(np->phandle); of_node_put(np); - if (!prproc) - return dev_err_probe(&ab->pdev->dev, -EPROBE_DEFER, - "failed to get rproc\n"); - - ab_ahb->tgt_rproc = prproc; + if (!prproc) { + ret = dev_err_probe(&ab->pdev->dev, -EPROBE_DEFER, + "failed to get rproc\n"); + goto err_free_rproc_info; + } + g_rproc_info->tgt_rproc = prproc; return 0; + +err_free_rproc_info: + ab_ahb->rproc_info = NULL; + kfree(g_rproc_info); + g_rproc_info = NULL; + return ret; } static int ath12k_ahb_boot_root_pd(struct ath12k_base *ab) { - struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); unsigned long time_left; int ret; - ret = rproc_boot(ab_ahb->tgt_rproc); + lockdep_assert_held(&ath12k_rproc_info_lock); + reinit_completion(&g_rproc_info->rootpd_ready); + + ret = rproc_boot(g_rproc_info->tgt_rproc); if (ret < 0) { ath12k_err(ab, "RootPD boot failed\n"); return ret; } - time_left = wait_for_completion_timeout(&ab_ahb->rootpd_ready, + time_left = wait_for_completion_timeout(&g_rproc_info->rootpd_ready, ATH12K_ROOTPD_READY_TIMEOUT); if (!time_left) { ath12k_err(ab, "RootPD ready wait timed out\n"); @@ -874,44 +963,74 @@ static int ath12k_ahb_boot_root_pd(struct ath12k_base *ab) static int ath12k_ahb_configure_rproc(struct ath12k_base *ab) { - struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); int ret; - ret = ath12k_ahb_get_rproc(ab); - if (ret < 0) - return ret; + mutex_lock(&ath12k_rproc_info_lock); - ret = ath12k_ahb_register_rproc_notifier(ab); + ret = ath12k_ahb_get_rproc(ab); + if (ret < 0) { + mutex_unlock(&ath12k_rproc_info_lock); + return ret; + } + + ret = ath12k_ahb_register_rproc_notifier(); if (ret < 0) { ret = dev_err_probe(&ab->pdev->dev, ret, "failed to register rproc notifier\n"); - goto err_put_rproc; + goto err_cleanup_userpd; } - if (ab_ahb->tgt_rproc->state != RPROC_RUNNING) { + if (g_rproc_info->tgt_rproc->state != RPROC_RUNNING) { ret = ath12k_ahb_boot_root_pd(ab); if (ret < 0) { ath12k_err(ab, "failed to boot the remote processor Q6\n"); goto err_unreg_notifier; } + g_rproc_info->rootpd_booted_by_driver = true; } - return ath12k_ahb_config_rproc_irq(ab); + mutex_unlock(&ath12k_rproc_info_lock); + return 0; err_unreg_notifier: - ath12k_ahb_unregister_rproc_notifier(ab); + ath12k_ahb_unregister_rproc_notifier(); -err_put_rproc: - rproc_put(ab_ahb->tgt_rproc); +err_cleanup_userpd: + ath12k_ahb_cleanup_userpd(ab); + + if (g_rproc_info && !g_rproc_info->num_userpd) { + rproc_put(g_rproc_info->tgt_rproc); + kfree(g_rproc_info); + g_rproc_info = NULL; + } + + mutex_unlock(&ath12k_rproc_info_lock); return ret; } static void ath12k_ahb_deconfigure_rproc(struct ath12k_base *ab) { struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + struct ath12k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info; - ath12k_ahb_unregister_rproc_notifier(ab); - rproc_put(ab_ahb->tgt_rproc); + lockdep_assert_held(&ath12k_rproc_info_lock); + + if (!rproc_info || !g_rproc_info) + return; + + ath12k_ahb_cleanup_userpd(ab); + + if (!g_rproc_info->num_userpd) { + ath12k_ahb_unregister_rproc_notifier(); + + if (g_rproc_info->rootpd_booted_by_driver && + g_rproc_info->tgt_rproc->state == RPROC_RUNNING) + rproc_shutdown(g_rproc_info->tgt_rproc); + + rproc_put(g_rproc_info->tgt_rproc); + kfree(g_rproc_info); + g_rproc_info = NULL; + } } static int ath12k_ahb_resource_init(struct ath12k_base *ab) @@ -1094,6 +1213,10 @@ static int ath12k_ahb_probe(struct platform_device *pdev) if (ret) goto err_ce_free; + ret = ath12k_ahb_config_rproc_irq(ab); + if (ret) + goto err_rproc_deconfigure; + ret = ath12k_ahb_config_irq(ab); if (ret) { ath12k_err(ab, "failed to configure irq: %d\n", ret); @@ -1121,7 +1244,9 @@ static int ath12k_ahb_probe(struct platform_device *pdev) ab_ahb->device_family_ops->arch_deinit(ab); err_rproc_deconfigure: + mutex_lock(&ath12k_rproc_info_lock); ath12k_ahb_deconfigure_rproc(ab); + mutex_unlock(&ath12k_rproc_info_lock); err_ce_free: ath12k_ce_free_pipes(ab); @@ -1163,7 +1288,9 @@ static void ath12k_ahb_free_resources(struct ath12k_base *ab) ath12k_hal_srng_deinit(ab); ath12k_ce_free_pipes(ab); ath12k_ahb_resource_deinit(ab); + mutex_lock(&ath12k_rproc_info_lock); ath12k_ahb_deconfigure_rproc(ab); + mutex_unlock(&ath12k_rproc_info_lock); ab_ahb->device_family_ops->arch_deinit(ab); ath12k_core_free(ab); platform_set_drvdata(pdev, NULL); diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h index 037347ccd21b..cdb58b07338f 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.h +++ b/drivers/net/wireless/ath/ath12k/ahb.h @@ -69,13 +69,19 @@ struct ath12k_ahb_device_family_ops { void (*arch_deinit)(struct ath12k_base *ab); }; -struct ath12k_ahb { - struct ath12k_base *ab; +struct ath12k_ahb_rproc_info { struct rproc *tgt_rproc; - struct clk *xo_clk; - struct completion rootpd_ready; struct notifier_block root_pd_nb; void *root_pd_notifier; + struct completion rootpd_ready; + u8 num_userpd; + bool rootpd_booted_by_driver; + struct ath12k_ahb *userpd[ATH12K_MAX_DEVICES]; +}; + +struct ath12k_ahb { + struct ath12k_base *ab; + struct clk *xo_clk; struct qcom_smem_state *spawn_state; struct qcom_smem_state *stop_state; struct completion userpd_spawned; @@ -88,6 +94,7 @@ struct ath12k_ahb { const struct ath12k_ahb_ops *ahb_ops; const struct ath12k_ahb_device_family_ops *device_family_ops; bool scm_auth_enabled; + struct ath12k_ahb_rproc_info *rproc_info; }; struct ath12k_ahb_driver { From 799b5f45cb8194ebd06c9c89e0afdad5bedd2cc5 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 23 Jul 2026 13:06:40 +0200 Subject: [PATCH 177/403] wifi: rtl818x: initialize eeprom_93cx6 struct to zero Commit 7738a7ab9d12 ("misc: eeprom: eeprom_93cx6: Add quirk for extra read clock cycle") added extra 'quirk' field to struct eeprom_93cx6. Many existing users of eeprom_93cx6, including rtl818x drivers, allocate the structure on the stack without initializing all fields. As a result, the added quirk field has an undefined value and can randomly cause reading wrong data from the EEPROM. Fix by initializing the structures with {}. Fixes: 7738a7ab9d12 ("misc: eeprom: eeprom_93cx6: Add quirk for extra read clock cycle") Cc: stable@kernel.org # v6.13+ Signed-off-by: Stanislaw Gruszka Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260723110640.8588-1-stf_xl@wp.pl --- drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c | 2 +- drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c index 070c0431c482..4a5989172a5e 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c @@ -1652,7 +1652,7 @@ static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom) static void rtl8180_eeprom_read(struct rtl8180_priv *priv) { - struct eeprom_93cx6 eeprom; + struct eeprom_93cx6 eeprom = {}; int eeprom_cck_table_adr; u16 eeprom_val; int i; diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index 1d21c468a236..a766712187f7 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -1445,7 +1445,7 @@ static int rtl8187_probe(struct usb_interface *intf, struct usb_device *udev = interface_to_usbdev(intf); struct ieee80211_hw *dev; struct rtl8187_priv *priv; - struct eeprom_93cx6 eeprom; + struct eeprom_93cx6 eeprom = {}; struct ieee80211_channel *channel; const char *chip_name; u16 txpwr, reg; From 6496ce90845df2d22fb8e8ed235cd2936fad41c8 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 23 Jul 2026 17:15:37 +0530 Subject: [PATCH 178/403] wifi: rtlwifi: rtl8192du: Fix possible memory leak in rtl92du_init_sw_vars() The memory allocated inside rtl92du_init_shared_data() is not freed in any of the subsequent error paths in rtl92du_init_sw_vars(). Fix that by adding a call to rtl92du_deinit_shared_data() in the error path. Fixes: b5dc8873b6ff ("wifi: rtlwifi: Add rtl8192du/sw.c") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260723114539.136986-1-nihaal@cse.iitm.ac.in --- drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c index 7bafb051d5ec..88e8cc590c54 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c @@ -145,8 +145,10 @@ static int rtl92du_init_sw_vars(struct ieee80211_hw *hw) /* for firmware buf */ rtlpriv->rtlhal.pfirmware = kmalloc(0x8000, GFP_KERNEL); - if (!rtlpriv->rtlhal.pfirmware) - return -ENOMEM; + if (!rtlpriv->rtlhal.pfirmware) { + err = -ENOMEM; + goto error; + } rtlpriv->max_fw_size = 0x8000; pr_info("Driver for Realtek RTL8192DU WLAN interface\n"); @@ -160,10 +162,14 @@ static int rtl92du_init_sw_vars(struct ieee80211_hw *hw) pr_err("Failed to request firmware!\n"); kfree(rtlpriv->rtlhal.pfirmware); rtlpriv->rtlhal.pfirmware = NULL; - return err; + goto error; } return 0; + +error: + rtl92du_deinit_shared_data(hw); + return err; } static void rtl92du_deinit_sw_vars(struct ieee80211_hw *hw) From 3c2999d13eeb222ae56631aeb7ca248090f2b210 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Thu, 23 Jul 2026 17:31:15 +0530 Subject: [PATCH 179/403] wifi: rtlwifi: pci: fix error path in rtl_pci_probe() In the last error path in rtl_pci_probe(), the cleanup functions are skipped due to a wrong goto label. Moreover, the successful call to rtl_init_rfkill(), ieee80211_register_hw(), rtl_debug_add_one() have to be reverted. Fix this issue by updating the labels and adding the relevant cleanup functions to the last error path. Fixes: 0c8173385e54 ("rtl8192ce: Add new driver") Signed-off-by: Abdun Nihaal Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260723120118.145383-1-nihaal@cse.iitm.ac.in --- drivers/net/wireless/realtek/rtlwifi/pci.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 220fb4dc7927..d3b79d0d3eb6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -2244,13 +2244,17 @@ int rtl_pci_probe(struct pci_dev *pdev, rtl_dbg(rtlpriv, COMP_INIT, DBG_DMESG, "%s: failed to register IRQ handler\n", wiphy_name(hw->wiphy)); - goto fail3; + goto fail6; } rtlpci->irq_alloc = 1; set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status); return 0; +fail6: + rtl_deinit_rfkill(hw); + rtl_debug_remove_one(hw); + ieee80211_unregister_hw(hw); fail5: rtl_pci_deinit(hw); fail4: From 6f24d22a31af9d53853a3005714888cc66cf3e1d Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:27 +0800 Subject: [PATCH 180/403] wifi: rtw89: coex: Add chip initial info version 11 for RTL8922A/D The version 11 init info add current RF path control information. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 2 + drivers/net/wireless/realtek/rtw89/coex.h | 19 ++++ drivers/net/wireless/realtek/rtw89/core.h | 47 ++++++++++ drivers/net/wireless/realtek/rtw89/fw.c | 75 ++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 6 ++ drivers/net/wireless/realtek/rtw89/rtw8922a.c | 88 ++++++++++++++++--- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 40 ++++++--- 7 files changed, 251 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 6a1888d8c476..f94866e1f43a 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -3199,6 +3199,8 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) rtw89_fw_h2c_cxdrv_init_v7(rtwdev, index); else if (ver->fcxinit == 10) rtw89_fw_h2c_cxdrv_init_v10(rtwdev, index); + else if (ver->fcxinit == 11) + rtw89_fw_h2c_cxdrv_init_v11(rtwdev, index); else rtw89_fw_h2c_cxdrv_init(rtwdev, index); break; diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index 3238c44429a9..41a68ab21d80 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -127,6 +127,11 @@ enum btc_fddt_en { ((__rssi == BTC_RSSI_ST_LOW || \ __rssi == BTC_RSSI_ST_HIGH) ? 1 : 0); }) +/* Antenna TX/RX path position masks and helpers */ +#define BTC_ANT_TX_MASK 0xf0 +#define BTC_ANT_RX_MASK 0x0f +#define BTC_ANT_SHIFT 4 + enum btc_ant { BTC_ANT_SHARED = 0, BTC_ANT_DEDICATED, @@ -473,4 +478,18 @@ void btc_dw2b(u8 *buf, size_t idx, u32 val) buf[idx + 3] = u32_get_bits(val, MASKBYTE3); } +static inline +u8 _btc_get_rf_path_from_ant_num(struct rtw89_btc *btc, u8 antnum) +{ + switch (antnum) { + default: + case 1: + return btc->mdinfo.ant.single_pos; + case 2: + return RF_PATH_AB; + case 3: + return RF_PATH_ABC; + } +} + #endif diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 7573f1965c98..7147e186b4a5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1537,6 +1537,21 @@ struct rtw89_btc_ant_info_v10 { u8 ant_xmap[2][4]; } __packed; +struct rtw89_btc_ant_info_v11 { + u8 type; + u8 num; + u8 isolation; + u8 single_pos; + + u8 stream_cnt; + u8 path_pos; /* WL path position: Tx[7:4], Rx[3:0] */ + u8 btg_pos; + u8 btg1_pos; + u8 func[5]; + u8 ant_xmap[2][4]; + u8 rsvd0[3]; +} __packed; + struct rtw89_btc_ant_info { u8 type; /* shared, dedicated(non-shared) */ u8 num; /* antenna count */ @@ -1546,6 +1561,7 @@ struct rtw89_btc_ant_info { u8 stream_cnt; /* spatial_stream count: Tx[7:4], Rx[3:0] */ u8 btg_pos; /* BT0 btg-circuit at 0:WL-S0/1:WL-S1 */ u8 btg1_pos; /* BT1 btg-circuit at 0:WL-S0/1:WL-S1 */ + u8 path_pos; /* WL path position: Tx[7:4], Rx[3:0] */ u8 func[5]; /* function at 1~5 Ant refer to enum btc_bt_func_type */ u8 ant_xmap[2][4]; @@ -2387,10 +2403,25 @@ struct rtw89_btc_module_v10 { struct rtw89_btc_ant_info_v10 ant; } __packed; +struct rtw89_btc_module_v11 { + u8 rfe_type; + u8 wa_type; + u8 kt_ver; + u8 kt_ver_adie; + + u8 bt0_pos; + u8 bt0_sw_type; + u8 bt1_pos; + u8 bt1_sw_type; + + struct rtw89_btc_ant_info_v11 ant; +} __packed; + union rtw89_btc_module_info { struct rtw89_btc_module_v0 md_v0; struct rtw89_btc_module_v7 md_v7; struct rtw89_btc_module_v10 md_v10; + struct rtw89_btc_module_v11 md_v11; }; struct rtw89_btc_module { @@ -2473,11 +2504,26 @@ struct rtw89_btc_init_info_v10 { struct rtw89_btc_module_v10 module; } __packed; +struct rtw89_btc_init_info_v11 { + u8 endian_type; /* 0: little-endian, 1:big-endian */ + u8 init_mode; /* refer to enum BTC_MODE_xxx */ + u8 wl_init_ok; + u8 bt0_function; + + u8 bt1_function; + u8 bt2_function; + u8 pta_mode; + u8 pta_direction; + + struct rtw89_btc_module_v11 module; +}; + union rtw89_btc_init_info_u { struct rtw89_btc_init_info_v0 init_v0; struct rtw89_btc_init_info_v7 init_v7; struct rtw89_btc_init_info_v10 init_v10; struct rtw89_btc_init_info_v107 init_v107; + struct rtw89_btc_init_info_v11 init_v11; }; struct rtw89_btc_init_info { @@ -2547,6 +2593,7 @@ enum rtw89_btc_bt_func_type { #define RTW89_BTC_BT_DEF_BR_TX_PWR 4 #define RTW89_BTC_BT_DEF_LE_TX_PWR 4 #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/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 3b8c144e0e98..a3a04697e2e6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -5973,6 +5973,81 @@ int rtw89_fw_h2c_cxdrv_init_v10(struct rtw89_dev *rtwdev, u8 type) return ret; } +int rtw89_fw_h2c_cxdrv_init_v11(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_init_info *init = &dm->init_info; + struct rtw89_h2c_cxinit_v11 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_init_v11\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxinit_v11 *)skb->data; + + h2c->hdr.type = type; + h2c->hdr.ver = 11; + h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; + + h2c->init.endian_type = init->endian_type; + h2c->init.init_mode = init->init_mode; + h2c->init.wl_init_ok = init->wl_init_ok; + h2c->init.bt0_function = init->bt0_function; + + h2c->init.bt1_function = init->bt1_function; + h2c->init.bt2_function = init->bt2_function; + h2c->init.pta_mode = init->pta_mode; + h2c->init.pta_direction = init->pta_direction; + + h2c->init.module.rfe_type = init->module.rfe_type; + h2c->init.module.wa_type = init->module.wa_type; + h2c->init.module.kt_ver = init->module.kt_ver; + h2c->init.module.kt_ver_adie = init->module.kt_ver_adie; + + h2c->init.module.bt0_pos = init->module.bt0_pos; + h2c->init.module.bt0_sw_type = init->module.bt0_sw_type; + h2c->init.module.bt1_pos = init->module.bt1_pos; + h2c->init.module.bt1_sw_type = init->module.bt1_sw_type; + + h2c->init.module.ant.type = init->module.ant.type; + h2c->init.module.ant.num = init->module.ant.num; + h2c->init.module.ant.isolation = init->module.ant.isolation; + h2c->init.module.ant.single_pos = init->module.ant.single_pos; + + h2c->init.module.ant.stream_cnt = init->module.ant.stream_cnt; + h2c->init.module.ant.path_pos = init->module.ant.path_pos; + h2c->init.module.ant.btg_pos = init->module.ant.btg_pos; + h2c->init.module.ant.btg1_pos = init->module.ant.btg1_pos; + memcpy(h2c->init.module.ant.func, init->module.ant.func, + sizeof(init->module.ant.func)); + + memcpy(h2c->init.module.ant.ant_xmap, init->module.ant.ant_xmap, + sizeof(init->module.ant.ant_xmap)); + + 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; +} + #define PORT_DATA_OFFSET 4 #define H2C_LEN_CXDRVINFO_ROLE_DBCC_LEN 12 #define H2C_LEN_CXDRVINFO_ROLE_SIZE(max_role_num) \ diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index e86014ca249a..c0a00b721060 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2603,6 +2603,11 @@ struct rtw89_h2c_cxinit_v10 { struct rtw89_btc_init_info_v10 init; } __packed; +struct rtw89_h2c_cxinit_v11 { + struct rtw89_h2c_cxhdr_v7 hdr; + struct rtw89_btc_init_info_v11 init; +} __packed; + #define RTW89_H2C_CXROLE_V101_ROLE_STAT_CONNTECTED BIT(0) #define RTW89_H2C_CXROLE_V101_ROLE_STAT_PID GENMASK(3, 1) #define RTW89_H2C_CXROLE_V101_ROLE_STAT_PHY BIT(4) @@ -5456,6 +5461,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_init_v11(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_osi_info_v6(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 2586fd2df8ab..2d225e009b28 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2721,25 +2721,33 @@ static u32 rtw8922a_chan_to_rf18_val(struct rtw89_dev *rtwdev, static void rtw8922a_btc_set_rfe(struct rtw89_dev *rtwdev) { - struct rtw89_btc_module *md = &rtwdev->btc.mdinfo; + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_module *md = &btc->mdinfo; + u8 i, j; + u8 tx_path_pos, rx_path_pos; md->rfe_type = rtwdev->efuse.rfe_type; md->kt_ver = rtwdev->hal.cv; - md->bt_solo = 0; - md->bt0_sw_type = BTC_SWITCH_INTERNAL; + md->kt_ver_adie = rtwdev->hal.acv; md->wa_type = 0; - md->ant.type = BTC_ANT_SHARED; md->ant.num = 2; - md->ant.isolation = 10; - md->ant.diversity = 0; - md->ant.single_pos = RF_PATH_A; - md->ant.btg_pos = RF_PATH_B; + md->ant.single_pos = BTC_RF_S0; if (md->kt_ver <= 1) md->wa_type |= BTC_WA_HFP_ZB; - rtwdev->btc.cx.bt_ext.func_type = BTC_3CX_NONE; + btc->cx.bt0.ant_iso_to_wl = md->ant.isolation; + btc->cx.bt1.ant_iso_to_wl = md->ant.isolation; + + md->ant.stream_cnt = 2; + md->ant.btg_pos = RF_PATH_B; + md->ant.single_pos = RF_PATH_A; + + btc->cx.bt0.band_56G_support = 0; + btc->cx.bt1.band_56G_support = 0; + btc->cx.bt0.func_type = BTC_BTF_BT; + btc->cx.bt1.func_type = BTC_BTF_NONE; if (md->rfe_type == 0) { rtwdev->btc.dm.error.map.rfe_type0 = true; @@ -2747,17 +2755,69 @@ static void rtw8922a_btc_set_rfe(struct rtw89_dev *rtwdev) } md->ant.num = (md->rfe_type % 2) ? 2 : 3; - if (md->kt_ver == 0) md->ant.num = 2; - if (md->ant.num == 3) { - md->ant.type = BTC_ANT_DEDICATED; - md->bt0_pos = BTC_BT_ALONE; - } else { + memset(btc->dm.ant_xmap, 0, sizeof(btc->dm.ant_xmap)); + + switch (md->ant.num) { + case 1: md->ant.type = BTC_ANT_SHARED; md->bt0_pos = BTC_BT_BTG; + md->bt0_sw_type = BTC_SWITCH_V1_INTERNAL; + md->ant.btg_pos = BTC_RF_S0; + btc->dm.ant_xmap[BTC_RF_S0][BTC_BT_1ST] = 1; + md->ant.func[0] = BTC_EFMAP_BT0; + break; + case 2: /* 2-Ant */ + default: + md->ant.type = BTC_ANT_SHARED; + md->bt0_pos = BTC_BT_BTG; + md->bt0_sw_type = BTC_SWITCH_V1_INTERNAL; + btc->dm.wl_trx_nss_en = 1; + btc->dm.ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 1; + md->ant.func[1] = BTC_EFMAP_BT0; + break; + case 3: /* 3-Ant, 3 different BT-configuration */ + /* WL-S0 + WL-S1 + BT0-S0 */ + md->ant.type = BTC_ANT_DEDICATED; + md->bt0_pos = BTC_BT_ALONE; + md->bt0_sw_type = BTC_SWITCH_V1_NONE; + md->ant.func[2] = BTC_EFMAP_BT0; } + + tx_path_pos = _btc_get_rf_path_from_ant_num(btc, rtwdev->hal.tx_nss); + rx_path_pos = _btc_get_rf_path_from_ant_num(btc, rtwdev->hal.rx_nss); + /* Combine TX[7:4] and RX[3:0] into path_pos byte */ + md->ant.path_pos = ((tx_path_pos << BTC_ANT_SHIFT) & BTC_ANT_TX_MASK) | + (rx_path_pos & BTC_ANT_RX_MASK); + + switch (btc->cx.bt_ext.chip_id) { + 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.ant_iso_to_wl = RTW89_BTC_DEFAULT_ANISO; + break; + case BTC_ESOC_8771: + btc->cx.bt_ext.func_type = BTC_BTF_THREAD; + btc->cx.bt_ext.hw_coex = BTC_EXTSOC_INTF_PTA; + btc->cx.bt_ext.rf_band_map = 0x1; /* 2.4G only */ + btc->cx.bt_ext.link_weight[BTC_BT_B2G] = 10; + btc->cx.bt_ext.profile_map[BTC_BT_B2G] |= BTC_BT_THREAD; + /* use GPIO 12~15 for Ext-4-wire-PTA */ + btc->cx.bt_ext.hpta_cfg = BIT(12) | BIT(13) | BIT(14) | BIT(15); + md->ant.func[2] = BTC_EFMAP_ZB; + break; + } + + btc->dm.ant_xmap[BTC_RF_S0][BTC_BT_EXT] = 0; + btc->dm.ant_xmap[BTC_RF_S1][BTC_BT_EXT] = 0; + + for (i = BTC_RF_S0; i <= BTC_RF_S1; i++) + for (j = BTC_BT_1ST; j <= BTC_BT_EXT; j++) + md->ant.ant_xmap[i][j] = btc->dm.ant_xmap[i][j]; + rtwdev->btc.btg_pos = md->ant.btg_pos; rtwdev->btc.ant_type = md->ant.type; } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 12c96425a6ff..142e693c87bf 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -3174,7 +3174,9 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_cx *cx = &btc->cx; u8 efuse_bt_func, efuse_ant_info, bt_sw_gpio_pos; - u8 is_combo, is_bt_share; + u8 is_combo, is_bt_share, is_spdt; + u8 tx_path_pos, rx_path_pos; + u8 i, j; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s !!\n", __func__); @@ -3192,7 +3194,8 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) cx->bt0.ant_iso_to_wl = md->ant.isolation; cx->bt1.ant_iso_to_wl = md->ant.isolation; - md->ant.stream_cnt = 2; + md->ant.stream_cnt = (rtwdev->hal.tx_nss << BTC_ANT_SHIFT) + + rtwdev->hal.rx_nss; md->ant.btg_pos = BTC_RF_S1; /* BTG0 at WL-S1 */ md->ant.btg1_pos = BTC_RF_S0; /* BTG1 at WL-S0 if Dual-BTGA */ @@ -3203,19 +3206,20 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) efuse_bt_func = rtwdev->efuse.bt_setting_2; - switch ((efuse_bt_func & 0xe0) >> 5) { /* 0xcd[7:5] */ + is_spdt = !!(efuse_bt_func & BIT(5)); + + switch ((efuse_bt_func & 0xc0) >> 6) { /* 0xcd[7:6] */ default: case 0: - case 1: bt_sw_gpio_pos = 5; break; - case 2: + case 1: bt_sw_gpio_pos = 11; break; - case 3: + case 2: bt_sw_gpio_pos = 15; break; - case 4: + case 3: bt_sw_gpio_pos = 20; break; } @@ -3233,9 +3237,8 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) case 1: /* 1-Ant WL-S0 only & BT0 only */ 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; md->ant.btg_pos = BTC_RF_S0; /* BTG0 at WL-S0 */ - md->ant.stream_cnt = 1; md->ant.func[0] = BTC_EFMAP_BT0; dm->ant_xmap[BTC_RF_S0][BTC_BT_1ST] = 1; /* BT0 shared with S0*/ dm->ant_xmap[BTC_RF_S1][BTC_BT_1ST] = 0; /* WL 1T1R no RF-S1 */ @@ -3265,13 +3268,13 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) if (md->ant.func[0] == BTC_EFMAP_BT1) { /* if 2nd BT exist */ dm->ant_xmap[BTC_RF_S0][BTC_BT_2ND] = 1; - if (md->rfe_type == 12) { /* WL-S0 & BT1 by SPDT */ + if (is_spdt) { /* WL-S0 & BT1 by SPDT */ md->bt1_pos = BTC_BT_ALONE; /* Todo: set SPDT GPIO-ctrl */ md->bt1_sw_type = bt_sw_gpio_pos; } else { /* WL-S0 & BT1-S1 by BTGA */ md->bt1_pos = BTC_BT_BTG; - md->bt1_sw_type = BTC_SWITCH_INTERNAL; + md->bt1_sw_type = BTC_SWITCH_V1_INTERNAL; } } @@ -3284,7 +3287,7 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) md->ant.func[2] = efuse_bt_func & (~BTC_EFMAP_BT0); 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; dm->wl_trx_nss_en = 1; /* 1ss MIMO-PS capability */ } else { @@ -3324,6 +3327,12 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) break; } + tx_path_pos = _btc_get_rf_path_from_ant_num(btc, rtwdev->hal.tx_nss); + rx_path_pos = _btc_get_rf_path_from_ant_num(btc, rtwdev->hal.rx_nss); + /* Combine TX[7:4] and RX[3:0] into path_pos byte */ + md->ant.path_pos = ((tx_path_pos << BTC_ANT_SHIFT) & BTC_ANT_TX_MASK) | + (rx_path_pos & BTC_ANT_RX_MASK); + /* * if only BT0 at BTGA: 2-Ant, 3-Ant(BT1 used dedicated-ant) * can setup dm->wl_trx_nss_en = 1, WL MIMO-PS to 1T1R @@ -3373,6 +3382,13 @@ static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) dm->ant_xmap[BTC_RF_S0][BTC_BT_EXT] = 0; dm->ant_xmap[BTC_RF_S1][BTC_BT_EXT] = 0; + + for (i = BTC_RF_S0; i <= BTC_RF_S1; i++) + for (j = BTC_BT_1ST; j <= BTC_BT_EXT; j++) + md->ant.ant_xmap[i][j] = dm->ant_xmap[i][j]; + + rtwdev->btc.btg_pos = md->ant.btg_pos; + rtwdev->btc.ant_type = md->ant.type; } static void rtw8922d_btc_init_cfg(struct rtw89_dev *rtwdev) From 04852fcad5a9cdec07a162b387643887c3505ece Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:28 +0800 Subject: [PATCH 181/403] wifi: rtw89: coex: Add Wi-Fi MLO info version 2 H2C command The info included MLO status, hardware status, firmware will set corresponding register control to do coexistence (PTA slot priority, RF switch etc.) Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 3 ++ drivers/net/wireless/realtek/rtw89/core.h | 19 ++++++++ drivers/net/wireless/realtek/rtw89/fw.c | 58 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 6 +++ 4 files changed, 86 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index f94866e1f43a..0c5599a23b4a 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -3274,6 +3274,9 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) else return; + if (ver->fcxmlo == 2) + rtw89_fw_h2c_cxdrv_mlo_v2(rtwdev, index); + break; case CXDRVINFO_OSI: if (!ver->fcxosi || ver->drvinfo_ver == 103) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 7147e186b4a5..0ec63f9510bd 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1783,6 +1783,25 @@ struct rtw89_btc_wl_dbcc_info { u8 role[RTW89_PHY_NUM]; /* role in each phy */ }; +struct rtw89_btc_wl_mlo_info_v2 { + u8 wmode[RTW89_PHY_NUM]; + u8 ch_type[RTW89_PHY_NUM]; + u8 hwb_rf_band[RTW89_PHY_NUM]; + u8 path_rf_band[RTW89_PHY_NUM]; + + u8 wtype; + u8 mrcx_mode; + u8 mrcx_act_hwb_map; + u8 mrcx_bt_slot_rsp; + + u8 rf_combination; + u8 mlo_en; + u8 mlo_adie; + u8 dual_hw_band_en; + + __le32 link_status; +} __packed; + struct rtw89_btc_wl_mlo_info { u8 wmode[RTW89_PHY_NUM]; /* enum phl_mr_wmode */ u8 ch_type[RTW89_PHY_NUM]; /* enum phl_mr_ch_type */ diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index a3a04697e2e6..5ab80bda3ae5 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6616,6 +6616,64 @@ int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type) return ret; } +int rtw89_fw_h2c_cxdrv_mlo_v2(struct rtw89_dev *rtwdev, u8 type) +{ + struct rtw89_btc_wl_mlo_info *mlo = &rtwdev->btc.cx.wl.mlo_info; + struct rtw89_h2c_cxmlo_v2 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_mlo_v2\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cxmlo_v2 *)skb->data; + + h2c->hdr.type = type; + h2c->hdr.ver = 2; + h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; + + memcpy(h2c->mlo.wmode, mlo->wmode, + sizeof(h2c->mlo.wmode)); + memcpy(h2c->mlo.ch_type, mlo->ch_type, + sizeof(h2c->mlo.ch_type)); + memcpy(h2c->mlo.hwb_rf_band, mlo->hwb_rf_band, + sizeof(h2c->mlo.hwb_rf_band)); + memcpy(h2c->mlo.path_rf_band, mlo->path_rf_band, + sizeof(h2c->mlo.path_rf_band)); + + h2c->mlo.wtype = mlo->wtype; + h2c->mlo.mrcx_mode = mlo->mrcx_mode; + h2c->mlo.mrcx_act_hwb_map = mlo->mrcx_act_hwb_map; + h2c->mlo.mrcx_bt_slot_rsp = mlo->mrcx_bt_slot_rsp; + + h2c->mlo.rf_combination = mlo->rf_combination; + h2c->mlo.mlo_en = mlo->mlo_en; + h2c->mlo.mlo_adie = mlo->mlo_adie; + h2c->mlo.dual_hw_band_en = mlo->dual_hw_band_en; + h2c->mlo.link_status = cpu_to_le32(mlo->link_status); + + 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(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index c0a00b721060..538df0fb42cc 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2453,6 +2453,11 @@ struct rtw89_h2c_cxrole_v10 { struct rtw89_btc_wl_role_info_v10 r; } __packed; +struct rtw89_h2c_cxmlo_v2 { + struct rtw89_h2c_cxhdr_v7 hdr; + struct rtw89_btc_wl_mlo_info_v2 mlo; +} __packed; + struct rtw89_h2c_cxosi { struct rtw89_h2c_cxhdr_v7 hdr; struct rtw89_btc_fbtc_outsrc_set_info_v1 osi; @@ -5462,6 +5467,7 @@ int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role_v10(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_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_ctrl(struct rtw89_dev *rtwdev, u8 type); From eb70edecee469aa60421d4a105475008ca275096 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:29 +0800 Subject: [PATCH 182/403] wifi: rtw89: coex: Add firmware 0.35.111.X support for RTL8922A/D Add BTC version table entries for RTL8922A and RTL8922D. The new firmware need driver provide more chip initial related parameters. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-4-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 0c5599a23b4a..d873b9482e49 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, 111, 0), + .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 = 10, .frptmap = 5, .fcxctrl = 9, .fcxinit = 11, + .fwevntrptl = 1, .fwc2hfunc = 4, .drvinfo_ver = 3, .info_buf = 1800, + .max_role_num = 6, .fcxosi = 6, .fcxmlo = 2, .bt_desired = 8, + .fcxtrx = 9, + }, {RTL8922D, RTW89_FW_VER_CODE(0, 35, 94, 0), .fcxbtcrpt = 11, .fcxtdma = 8, .fcxslots = 7, .fcxcysta = 8, .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 8, @@ -169,6 +178,15 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { .max_role_num = 6, .fcxosi = 0, .fcxmlo = 0, .bt_desired = 8, .fcxtrx = 0, }, + {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, + .fcxbtver = 8, .fcxbtscan = 8, .fcxbtafh = 8, .fcxbtdevinfo = 8, + .fwlrole = 10, .frptmap = 5, .fcxctrl = 9, .fcxinit = 11, + .fwevntrptl = 1, .fwc2hfunc = 4, .drvinfo_ver = 3, .info_buf = 1800, + .max_role_num = 6, .fcxosi = 6, .fcxmlo = 2, .bt_desired = 8, + .fcxtrx = 9, + }, {RTL8922A, RTW89_FW_VER_CODE(0, 35, 71, 0), .fcxbtcrpt = 8, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, From 86f763e372485932c3f593b720faf31758ab3d62 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:30 +0800 Subject: [PATCH 183/403] wifi: rtw89: coex: Rearrange _ntfy_role_info info rtw89_btc_wl_link_info is duplicated declaring in the function, remove one of them. We need MAC Address only when Wi-Fi role is station, included the copy operation into if statement. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 77 +++++++++++++++-------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index d873b9482e49..9da3b23b2d14 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -8675,13 +8675,14 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); + struct ieee80211_p2p_noa_attr *noa_attr; + struct ieee80211_p2p_noa_desc *noa_desc; struct ieee80211_bss_conf *bss_conf; struct ieee80211_link_sta *link_sta; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_link_info r = {0}; - struct rtw89_btc_wl_link_info *wlinfo = NULL; - u8 mode = 0; + u8 i, mode = 0; rcu_read_lock(); @@ -8729,34 +8730,60 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], wifi_role=%d\n", rtwvif_link->wifi_role); - wlinfo = &wl->rlink_info[rtwvif_link->port][rtwvif_link->phy_idx]; + if (vif->type == NL80211_IFTYPE_P2P_GO || + vif->type == NL80211_IFTYPE_P2P_CLIENT) { + noa_attr = &bss_conf->p2p_noa_attr; - wlinfo->mode = mode; - wlinfo->role = rtwvif_link->wifi_role; - wlinfo->phy = rtwvif_link->phy_idx; - wlinfo->pid = rtwvif_link->port; - wlinfo->active = true; - wlinfo->connected = MLME_LINKED; - wlinfo->bcn_period = bss_conf->beacon_int; - wlinfo->dtim_period = bss_conf->dtim_period; - wlinfo->band = chan->band_type; - wlinfo->ch = chan->channel; - wlinfo->bw = chan->band_width; - wlinfo->chdef.band = chan->band_type; - wlinfo->chdef.center_ch = chan->channel; - wlinfo->chdef.bw = chan->band_width; - wlinfo->chdef.chan = chan->primary_channel; - ether_addr_copy(r.mac_addr, rtwvif_link->mac_addr); + for (i = 0; i < RTW89_P2P_MAX_NOA_NUM; i++) { + noa_desc = &noa_attr->desc[i]; + if (noa_desc->count != 0) { + r.noa = 1; + r.noa_duration = le32_to_cpu(noa_desc->duration); + break; + } + } + } + + r.mode = mode; + r.role = rtwvif_link->wifi_role; + r.phy = rtwvif_link->phy_idx; + r.pid = rtwvif_link->port; + r.active = true; + if (vif->active_links) { + if (rtwvif_link->link_id < 16) + r.active = !!(vif->active_links & BIT(rtwvif_link->link_id)); + } + r.bcn_period = bss_conf->beacon_int; + r.dtim_period = bss_conf->dtim_period; + r.band = chan->band_type; + r.ch = chan->channel; + r.bw = chan->band_width; + r.chdef.band = chan->band_type; + r.chdef.center_ch = chan->channel; + r.chdef.bw = chan->band_width; + r.chdef.chan = chan->primary_channel; + + if (rtwsta_link && vif->type == NL80211_IFTYPE_STATION) { + r.mac_id = rtwsta_link->mac_id; + ether_addr_copy(r.mac_addr, rtwvif_link->mac_addr); + } + + switch (state) { + case BTC_ROLE_MSTS_STA_CONN_END: + case BTC_ROLE_MSTS_AP_START: + r.connected = MLME_LINKED; + break; + default: + r.connected = MLME_NO_LINK; + break; + } rcu_read_unlock(); - if (rtwsta_link && vif->type == NL80211_IFTYPE_STATION) - wlinfo->mac_id = rtwsta_link->mac_id; - btc->dm.cnt_notify[BTC_NCNT_ROLE_INFO]++; - if (wlinfo->role == RTW89_WIFI_ROLE_STATION && - wlinfo->connected == MLME_NO_LINK) + if (r.role == RTW89_WIFI_ROLE_STATION && + r.connected == MLME_NO_LINK) btc->dm.leak_ap = 0; if (state == BTC_ROLE_MSTS_STA_CONN_START) { @@ -8774,7 +8801,7 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, state == BTC_ROLE_MSTS_STA_CONN_END) wl->status.map._4way = false; - _update_wl_info(rtwdev, wlinfo); + _update_wl_info(rtwdev, &r); _run_coex(rtwdev, BTC_RSN_NTFY_ROLE_INFO); } From a76501e1d148cc7052b1b6c5a00ea3e404dcae26 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:31 +0800 Subject: [PATCH 184/403] wifi: rtw89: coex: Separate _ntfy_role_info into two function To make logic more clearly, separate _ntfy_role_info into two function by data collecting and using. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 109 ++++++++++++++++------ 1 file changed, 80 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 9da3b23b2d14..ae458917ecf7 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -7233,6 +7233,7 @@ static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev) mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, cnt, notv10); mode_v0 = wl_rinfo->link_mode_v0; } else if (!b2g && b5g && notv10) { + mode = _get_role_link_mode(wl_rinfo, cid_role[0], notv10); mode_v0 = BTC_WLINK_V0_5G; } else if (b2g && b5g) { mode = BTC_WLINK_DB_MCC; @@ -7247,6 +7248,7 @@ static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev) } } else { mode = _get_role_link_mode(wl_rinfo, cid_role[0], notv10); + mode_v0 = wl_rinfo->link_mode_v0; } wl_rinfo->link_mode = mode; @@ -8667,6 +8669,78 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) _run_coex(rtwdev, BTC_RSN_UPDATE_BT_INFO); } +static void _ntfy_role_info(struct rtw89_dev *rtwdev, u8 rid, + struct rtw89_btc_wl_link_info *info, + enum btc_role_state reason) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_wl_link_info *wlinfo = NULL; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + const struct rtw89_btc_ver *ver = btc->ver; + u8 rlink_id = info->phy; /* 1 role_id has 2 rlink_id(by HW_Band0/1) */ + bool refresh_role = false; + + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(), role_id=%d, reason=%d\n", + __func__, rid, reason); + + if (rid >= ver->max_role_num || rlink_id >= RTW89_PHY_NUM) + return; + + btc->dm.cnt_notify[BTC_NCNT_ROLE_INFO]++; + + wlinfo = &wl->rlink_info[rid][rlink_id]; + memcpy(wlinfo, info, sizeof(struct rtw89_btc_wl_link_info)); + + switch (reason) { + case BTC_ROLE_START: + wlinfo->active = true; + return; + case BTC_ROLE_STOP: + wlinfo->active = false; + return; + case BTC_ROLE_MSTS_STA_CONN_START: + wl->status.map.transacting = 1; + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, + &rtwdev->coex_act1_work); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, + &rtwdev->coex_act1_work, + RTW89_COEX_ACT1_WORK_PERIOD); + break; + case BTC_ROLE_MSTS_STA_CONN_END: + wl->status.map.transacting = 0; + refresh_role = true; + break; + case BTC_ROLE_MSTS_STA_DIS_CONN: + wl->status.map.transacting = 1; + refresh_role = false; + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, + &rtwdev->coex_act1_work); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, + &rtwdev->coex_act1_work, + RTW89_COEX_ACT1_WORK_PERIOD); + break; + case BTC_ROLE_MSTS_AP_START: + case BTC_ROLE_MSTS_AP_STOP: + refresh_role = true; + break; + case BTC_ROLE_STATE_UNKNOWN: + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(), role_id=%d, Unknown reason return!\n", + __func__, rid); + return; + default: + return; + } + + if (refresh_role) { + _update_wl_info(rtwdev, wlinfo); + _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); + } + + _run_coex(rtwdev, BTC_RSN_NTFY_ROLE_INFO); +} + void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, @@ -8679,20 +8753,20 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, struct ieee80211_p2p_noa_desc *noa_desc; struct ieee80211_bss_conf *bss_conf; struct ieee80211_link_sta *link_sta; - struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_link_info r = {0}; - u8 i, mode = 0; + u8 i, role_id, mode = 0; rcu_read_lock(); bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, false); + role_id = rtwvif_link->port; + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], state=%d\n", state); rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], role is STA=%d\n", vif->type == NL80211_IFTYPE_STATION); - rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], port=%d\n", rtwvif_link->port); + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], port=%d\n", role_id); rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], band=%d ch=%d bw=%d\n", chan->band_type, chan->channel, chan->band_width); rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], associated=%d\n", @@ -8747,7 +8821,7 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, r.mode = mode; r.role = rtwvif_link->wifi_role; r.phy = rtwvif_link->phy_idx; - r.pid = rtwvif_link->port; + r.pid = role_id; r.active = true; if (vif->active_links) { if (rtwvif_link->link_id < 16) @@ -8780,30 +8854,7 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, rcu_read_unlock(); - btc->dm.cnt_notify[BTC_NCNT_ROLE_INFO]++; - - if (r.role == RTW89_WIFI_ROLE_STATION && - r.connected == MLME_NO_LINK) - btc->dm.leak_ap = 0; - - if (state == BTC_ROLE_MSTS_STA_CONN_START) { - wl->status.map.transacting = 1; - wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work); - wiphy_delayed_work_queue(rtwdev->hw->wiphy, - &rtwdev->coex_act1_work, - RTW89_COEX_ACT1_WORK_PERIOD); - } else { - wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work); - wl->status.map.transacting = 0; - } - - if (state == BTC_ROLE_MSTS_STA_DIS_CONN || - state == BTC_ROLE_MSTS_STA_CONN_END) - wl->status.map._4way = false; - - _update_wl_info(rtwdev, &r); - - _run_coex(rtwdev, BTC_RSN_NTFY_ROLE_INFO); + _ntfy_role_info(rtwdev, role_id, &r, state); } void rtw89_btc_ntfy_radio_state(struct rtw89_dev *rtwdev, enum btc_rfctrl rf_state) From b01bfd2fc752e598ba1acfd76c64152f646c4d72 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:32 +0800 Subject: [PATCH 185/403] wifi: rtw89: coex: Decrease Wi-Fi special packet protect time While Wi-Fi is doing special packet handshake, or going into some transient state, BT-Coexistence will held timer to fix control logic to protect the segment. Set the protection duration to 1 second, it is enough to cover the situation. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index 41a68ab21d80..355784941edd 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -69,7 +69,7 @@ enum btc_wl_rfk_type { #define NM_EXEC false #define FC_EXEC true -#define RTW89_COEX_ACT1_WORK_PERIOD round_jiffies_relative(HZ * 4) +#define RTW89_COEX_ACT1_WORK_PERIOD round_jiffies_relative(HZ) #define RTW89_COEX_BT_DEVINFO_WORK_PERIOD round_jiffies_relative(HZ * 16) #define RTW89_COEX_RFK_CHK_WORK_PERIOD msecs_to_jiffies(300) #define BTC_RFK_PATH_MAP GENMASK(3, 0) From 5c90259b9748f3a75522cbd3da33f55b203752e2 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:33 +0800 Subject: [PATCH 186/403] wifi: rtw89: coex: complete GPIO debug configuration handler Complete the implementation of _fw_set_gpio() function to support all GPIO control configuration types for coexistence. Included debug signal, antenna switch, external I2C mailbox, external PTA related GPIO configuration. This function is called during initialization and when BT re-enables. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 234 +++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/coex.h | 35 ---- drivers/net/wireless/realtek/rtw89/core.h | 162 ++++++++++++++- drivers/net/wireless/realtek/rtw89/fw.h | 10 + 4 files changed, 392 insertions(+), 49 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index ae458917ecf7..96ec5fa20422 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -3206,6 +3206,212 @@ static void _fw_set_policy(struct rtw89_dev *rtwdev, u16 policy_type, rtw89_set_coex_ctrl_lps(rtwdev, btc->btc_ctrl_lps); } +static u8 _get_gpiosig_for_ver(struct rtw89_dev *rtwdev, u8 sig_v8) +{ + const struct rtw89_btc_ver *ver = rtwdev->btc.ver; + + /* v8: gpio_ver >= 8, directly use v8 signal ID */ + if (ver->fcxgpiodbg >= 8) + return sig_v8; + + /* v7: gpio_ver < 8, convert v8 signal ID to v7 signal ID */ + if (sig_v8 <= BTC_DBG_GNT_WL) { + return sig_v8; + } else if (sig_v8 <= BTC_DBG_GNT_WL1) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): sig %d not supported in v7\n", + __func__, sig_v8); + return BTC_DBG_NUM; + } else if (sig_v8 >= BTC_DBG_NUM) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): sig %d not available\n", + __func__, sig_v8); + return BTC_DBG_NUM; + } else { + return sig_v8 - 2; + } +} + +static u32 _convert_gpio_enmap_to_ver(struct rtw89_dev *rtwdev, u32 en_map_v8) +{ + const struct rtw89_btc_ver *ver = rtwdev->btc.ver; + u32 en_map_v7 = 0; + u32 bit; + + /* v8: gpio_ver >= 8, directly use v8 en_map */ + if (ver->fcxgpiodbg >= 8) + return en_map_v8; + + /* v7: gpio_ver < 8, convert v8 en_map bitmap to v7 en_map bitmap */ + /* bit 0-1: GNT_BT, GNT_WL - same position in both versions */ + en_map_v7 |= en_map_v8 & GENMASK(1, 0); + + /* bit 2-3: GNT_BT1, GNT_WL1 - not supported in v7, skip these bits */ + + /* bit 4-31: shift right by 2 positions (become bit 2-29 in v7) */ + for (bit = BTC_DBG_BCN_EARLY; bit < BTC_DBG_NUM; bit++) { + if (en_map_v8 & BIT(bit)) + en_map_v7 |= BIT(bit - 2); + } + + return en_map_v7; +} + +static u32 _convert_gpio_enmap_from_ver(struct rtw89_dev *rtwdev, u32 en_map) +{ + const struct rtw89_btc_ver *ver = rtwdev->btc.ver; + u32 en_map_v8 = 0; + u32 bit; + + if (ver->fcxgpiodbg >= 8) + return en_map; + + /* bit 0-1: GNT_BT, GNT_WL - same position in both versions */ + en_map_v8 |= en_map & GENMASK(1, 0); + + /* bit 2-29: shift left by 2 positions (become bit 4-31 in v8) */ + for (bit = BTC_DBG_GNT_BT1; bit < BTC_DBG_NUM - 2; bit++) { + if (en_map & BIT(bit)) + en_map_v8 |= BIT(bit + 2); + } + + return en_map_v8; +} + +static void _fw_set_gpio(struct rtw89_dev *rtwdev, u8 type, u32 val) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_fbtc_h2c_set_gpio *gpio = &btc->gpio; + const struct rtw89_btc_ver *ver = btc->ver; + struct rtw89_fbtc_h2c_set_gpio_2b l2_h2c; + struct rtw89_fbtc_h2c_set_gpio_4b l4_h2c; + u8 gpio_ver = ver->fcxgpiodbg; + u8 buf[sizeof(l4_h2c)]; + u8 len; + + if (gpio_ver < 8 && type > CXDGPIO_MUX_MAP) + return; + + if (type >= CXDGPIO_MAX) + return; + + switch (type) { + case CXDGPIO_EN_MAP: /* GPIO debug signal en-map 0~31 */ + val = _convert_gpio_enmap_to_ver(rtwdev, val); + gpio->en_map.data.type = CXDGPIO_EN_MAP; + gpio->en_map.data.fver = gpio_ver; + gpio->en_map.data.dlen = CXDGPIO_SET_L4; + gpio->en_map.data.en_map = val; + l4_h2c = gpio->en_map.fmt; + put_unaligned_le32(val, (u32 *)l4_h2c.data); + memcpy(buf, &l4_h2c, sizeof(l4_h2c)); + len = sizeof(l4_h2c); + break; + case CXDGPIO_MUX_MAP: /* GPIO dbg: Signal to GPIO Mux */ + gpio->mux.data.type = CXDGPIO_MUX_MAP; + gpio->mux.data.fver = gpio_ver; + gpio->mux.data.dlen = CXDGPIO_SET_L2; + gpio->mux.data.sig = _get_gpiosig_for_ver(rtwdev, + FIELD_GET(GENMASK(7, 0), val)); + if (gpio->mux.data.sig == 0xff) + return; + gpio->mux.data.gpio = FIELD_GET(GENMASK(15, 8), val); + l2_h2c = gpio->mux.fmt; + memcpy(buf, &l2_h2c, sizeof(l2_h2c)); + len = sizeof(l2_h2c); + break; + case CXDGPIO_EXT_HPTA: /* GPIO config for Ext HW-PTA */ + gpio->ext_pta.data.type = CXDGPIO_EXT_HPTA; + gpio->ext_pta.data.fver = gpio_ver; + gpio->ext_pta.data.dlen = CXDGPIO_SET_L2; + gpio->ext_pta.data.map_low = FIELD_GET(GENMASK(7, 0), val); + gpio->ext_pta.data.map_high = FIELD_GET(GENMASK(15, 8), val); + l2_h2c = gpio->ext_pta.fmt; + memcpy(buf, &l2_h2c, sizeof(l2_h2c)); + len = sizeof(l2_h2c); + break; + case CXDGPIO_EXT_HMBX: /* GPIO config for Ext HW-mailbox */ + gpio->ext_mb.data.type = CXDGPIO_EXT_HMBX; + gpio->ext_mb.data.fver = gpio_ver; + gpio->ext_mb.data.dlen = CXDGPIO_SET_L2; + gpio->ext_mb.data.map_low = FIELD_GET(GENMASK(7, 0), val); + gpio->ext_mb.data.map_high = FIELD_GET(GENMASK(15, 8), val); + l2_h2c = gpio->ext_mb.fmt; + memcpy(buf, &l2_h2c, sizeof(l2_h2c)); + len = sizeof(l2_h2c); + break; + case CXDGPIO_EXT_SWOUT: /* GPIO config for Ext SW output (wlan_act) */ + gpio->ext_swout.data.type = CXDGPIO_EXT_SWOUT; + gpio->ext_swout.data.fver = gpio_ver; + gpio->ext_swout.data.dlen = CXDGPIO_SET_L2; + gpio->ext_swout.data.map_low = FIELD_GET(GENMASK(7, 0), val); + gpio->ext_swout.data.map_high = FIELD_GET(GENMASK(15, 8), val); + l2_h2c = gpio->ext_swout.fmt; + memcpy(buf, &l2_h2c, sizeof(l2_h2c)); + len = sizeof(l2_h2c); + break; + case CXDGPIO_EXT_SWIN: /* GPIO config for Ext SW input control */ + gpio->ext_swin.data.type = CXDGPIO_EXT_SWIN; + gpio->ext_swin.data.fver = gpio_ver; + gpio->ext_swin.data.dlen = CXDGPIO_SET_L4; + gpio->ext_swin.data.in_map_low = FIELD_GET(GENMASK(7, 0), val); + gpio->ext_swin.data.in_map_high = FIELD_GET(GENMASK(15, 8), val); + gpio->ext_swin.data.int_map_low = FIELD_GET(GENMASK(23, 16), val); + gpio->ext_swin.data.int_map_high = FIELD_GET(GENMASK(31, 24), val); + l4_h2c = gpio->ext_swin.fmt; + put_unaligned_le32(val, (u32 *)l4_h2c.data); + memcpy(buf, &l4_h2c, sizeof(l4_h2c)); + len = sizeof(l4_h2c); + break; + default: + return; + } + + _send_fw_cmd(rtwdev, BTFC_SET, SET_GPIO_DBG, buf, len); +} + +static void _set_ext_interface(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_cx *cx = &btc->cx; + u8 bt1_sw_type = BTC_SWITCH_INTERNAL; + u32 val; + + if (btc->ver->fcxgpiodbg < 7) + return; + + /* if BT1+WL-S0, route DBG_GNT_BT1 to control SPDT */ + if ((rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) && + btc->ver->fcxinit >= 10) { + if (btc->ver->fcxinit >= 10) + bt1_sw_type = btc->mdinfo.bt1_sw_type; + else + return; + + if (bt1_sw_type > BTC_SWITCH_INTERNAL) { + _fw_set_gpio(rtwdev, CXDGPIO_EN_MAP, + BIT(BTC_DBG_GNT_BT1)); + + val = (bt1_sw_type << 8) + BTC_DBG_GNT_BT1; + _fw_set_gpio(rtwdev, CXDGPIO_MUX_MAP, val); + } + } + + /* set GPIO as Ext-PTA interface wire */ + if (cx->bt_ext.hw_coex & BTC_EXTSOC_INTF_PTA) + _fw_set_gpio(rtwdev, CXDGPIO_EXT_HPTA, cx->bt_ext.hpta_cfg); + + /* set GPIO as Ext-mailbox interface wire */ + if (cx->bt_ext.hw_coex & BTC_EXTSOC_INTF_MBX) + _fw_set_gpio(rtwdev, CXDGPIO_EXT_HMBX, cx->bt_ext.hmbx_cfg); + + /* set GPIO as Ext-SWIO interface wire */ + if (cx->bt_ext.hw_coex & BTC_EXTSOC_INTF_SWIO) { + _fw_set_gpio(rtwdev, CXDGPIO_EXT_SWOUT, cx->bt_ext.swout_cfg); + _fw_set_gpio(rtwdev, CXDGPIO_EXT_SWIN, cx->bt_ext.swin_cfg); + } +} + static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) { struct rtw89_btc *btc = &rtwdev->btc; @@ -8251,6 +8457,7 @@ static void _set_init_info(struct rtw89_dev *rtwdev) _fw_set_drv_info(rtwdev, CXDRVINFO_CTRL); rtw89_btc_fw_set_slots(rtwdev); btc_fw_set_monreg(rtwdev); + _set_ext_interface(rtwdev); _set_wl_tx_power(rtwdev, RTW89_BTC_WL_DEF_TX_PWR, RTW89_PHY_0); } @@ -9779,6 +9986,8 @@ static const char *id_to_gdbg(u32 id) switch (id) { CASE_BTC_GDBG_STR(GNT_BT); CASE_BTC_GDBG_STR(GNT_WL); + CASE_BTC_GDBG_STR(GNT_BT1); + CASE_BTC_GDBG_STR(GNT_WL1); CASE_BTC_GDBG_STR(BCN_EARLY); CASE_BTC_GDBG_STR(WL_NULL0); CASE_BTC_GDBG_STR(WL_NULL1); @@ -9807,8 +10016,6 @@ static const char *id_to_gdbg(u32 id) CASE_BTC_GDBG_STR(SLOT_B1FDD); CASE_BTC_GDBG_STR(BT_CHANGE); CASE_BTC_GDBG_STR(WL_CCA); - CASE_BTC_GDBG_STR(BT_LEAUDIO); - CASE_BTC_GDBG_STR(USER_DEF); default: return "unknown"; } @@ -11487,8 +11694,8 @@ static int _show_gpio_dbg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; union rtw89_btc_fbtc_gpio_dbg *gdbg = NULL; char *p = buf, *end = buf + bufsz; - u8 *gpio_map, i; - u32 en_map; + u32 en_map_raw, en_map; + u8 *gpio_map, i, sig; pcinfo = &pfwinfo->rpt_fbtc_gpio_dbg.cinfo; gdbg = &rtwdev->btc.fwinfo.rpt_fbtc_gpio_dbg.finfo; @@ -11499,27 +11706,32 @@ static int _show_gpio_dbg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) goto out; } - if (ver->fcxgpiodbg == 7) { - en_map = le32_to_cpu(gdbg->v7.en_map); + if (ver->fcxgpiodbg == 7 || ver->fcxgpiodbg == 8) { + en_map_raw = le32_to_cpu(gdbg->v7.en_map); gpio_map = gdbg->v7.gpio_map; } else { - en_map = le32_to_cpu(gdbg->v1.en_map); + en_map_raw = le32_to_cpu(gdbg->v1.en_map); gpio_map = gdbg->v1.gpio_map; } + en_map = _convert_gpio_enmap_from_ver(rtwdev, en_map_raw); if (!en_map) goto out; - p += scnprintf(p, end - p, " %-15s : enable_map:0x%08x", + p += scnprintf(p, end - p, "\n\r %-15s : enable_map:0x%08x", "[gpio_dbg]", en_map); - for (i = 0; i < BTC_DBG_MAX1; i++) { + for (i = 0; i < BTC_DBG_NUM; i++) { if (!(en_map & BIT(i))) continue; + + sig = ver->fcxgpiodbg >= 8 ? i : _get_gpiosig_for_ver(rtwdev, i); + if (sig >= BTC_DBG_NUM) + continue; + p += scnprintf(p, end - p, ", %s->GPIO%d", id_to_gdbg(i), - gpio_map[i]); + gpio_map[sig]); } - p += scnprintf(p, end - p, "\n"); out: return p - buf; diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index 355784941edd..3e9e1510c44f 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -315,41 +315,6 @@ enum btc_mlo_rf_combin { BTC_MLO_RF_2_PLUS_2 = 3, }; -enum btc_wl_gpio_debug { - BTC_DBG_GNT_BT = 0, - BTC_DBG_GNT_WL = 1, - BTC_DBG_BCN_EARLY = 2, - BTC_DBG_WL_NULL0 = 3, - BTC_DBG_WL_NULL1 = 4, - BTC_DBG_WL_RXISR = 5, - BTC_DBG_TDMA_ENTRY = 6, - BTC_DBG_A2DP_EMPTY = 7, - BTC_DBG_BT_RETRY = 8, - BTC_DBG_BT_RELINK = 9, - BTC_DBG_SLOT_WL = 10, - BTC_DBG_SLOT_BT = 11, - BTC_DBG_WL_ERR = 12, - BTC_DBG_WL_OK = 13, - BTC_DBG_SLOT_B2W = 14, - BTC_DBG_SLOT_W1 = 15, - BTC_DBG_SLOT_W2 = 16, - BTC_DBG_SLOT_W2B = 17, - BTC_DBG_SLOT_B1 = 18, - BTC_DBG_SLOT_B2 = 19, - BTC_DBG_SLOT_B3 = 20, - BTC_DBG_SLOT_B4 = 21, - BTC_DBG_SLOT_LK = 22, - BTC_DBG_SLOT_E2G = 23, - BTC_DBG_SLOT_E5G = 24, - BTC_DBG_SLOT_EBT = 25, - BTC_DBG_SLOT_WLK = 26, - BTC_DBG_SLOT_B1FDD = 27, - BTC_DBG_BT_CHANGE = 28, - BTC_DBG_WL_CCA = 29, - BTC_DBG_BT_LEAUDIO = 30, - BTC_DBG_USER_DEF = 31, -}; - void rtw89_btc_init(struct rtw89_dev *rtwdev); void rtw89_btc_ntfy_poweron(struct rtw89_dev *rtwdev); void rtw89_btc_ntfy_poweroff(struct rtw89_dev *rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0ec63f9510bd..ba3a9b0d4aa2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3074,14 +3074,55 @@ enum rtw89_btc_afh_map_type { /*AFH MAP TYPE */ RPT_BT_AFH_SEQ_LE = 0x20 }; -#define BTC_DBG_MAX1 32 +enum btc_wl_gpio_debug { + BTC_DBG_GNT_BT = 0, + BTC_DBG_GNT_WL = 1, + BTC_DBG_GNT_BT1 = 2, + BTC_DBG_GNT_WL1 = 3, + /* The following signals should 0-1 tiggle by each function-call */ + BTC_DBG_BCN_EARLY = 4, + BTC_DBG_WL_NULL0 = 5, + BTC_DBG_WL_NULL1 = 6, + BTC_DBG_WL_RXISR = 7, + BTC_DBG_TDMA_ENTRY = 8, + BTC_DBG_A2DP_EMPTY = 9, + BTC_DBG_BT_RETRY = 10, + /* The following signals should 0-1 tiggle by state L/H */ + BTC_DBG_BT_RELINK = 11, + BTC_DBG_SLOT_WL = 12, + BTC_DBG_SLOT_BT = 13, + /* The following signals should 0-1 tiggle by external*/ + BTC_DBG_WL_ERR = 14, + BTC_DBG_WL_OK = 15, + /* The following signals appear only 1-active at same time*/ + BTC_DBG_SLOT_B2W = 16, + BTC_DBG_SLOT_W1 = 17, + BTC_DBG_SLOT_W2 = 18, + BTC_DBG_SLOT_W2B = 19, + BTC_DBG_SLOT_B1 = 20, + BTC_DBG_SLOT_B2 = 21, + BTC_DBG_SLOT_B3 = 22, + BTC_DBG_SLOT_B4 = 23, + BTC_DBG_SLOT_LK = 24, + BTC_DBG_SLOT_E2G = 25, + BTC_DBG_SLOT_E5G = 26, + BTC_DBG_SLOT_EBT = 27, + BTC_DBG_SLOT_WLK = 28, + BTC_DBG_SLOT_B1FDD = 29, + BTC_DBG_BT_CHANGE = 30, + /* The following signals should 0-1 tiggle by external*/ + BTC_DBG_WL_CCA = 31, + + BTC_DBG_NUM, +}; + struct rtw89_btc_fbtc_gpio_dbg_v1 { u8 fver; /* btc_ver::fcxgpiodbg */ u8 rsvd; __le16 rsvd2; __le32 en_map; /* which debug signal (see btc_wl_gpio_debug) is enable */ __le32 pre_state; /* the debug signal is 1 or 0 */ - u8 gpio_map[BTC_DBG_MAX1]; /*the debug signals to GPIO-Position */ + u8 gpio_map[BTC_DBG_NUM]; /*the debug signals to GPIO-Position */ } __packed; struct rtw89_btc_fbtc_gpio_dbg_v7 { @@ -3090,7 +3131,7 @@ struct rtw89_btc_fbtc_gpio_dbg_v7 { u8 rsvd1; u8 rsvd2; - u8 gpio_map[BTC_DBG_MAX1]; + u8 gpio_map[BTC_DBG_NUM]; __le32 en_map; __le32 pre_state; @@ -3101,6 +3142,120 @@ union rtw89_btc_fbtc_gpio_dbg { struct rtw89_btc_fbtc_gpio_dbg_v7 v7; }; +/* + * SET_GPIO_CTRL payload (max len = 7 bytes) + * + * type = CXDGPIO_EN_MAP + * data.val[31:0] = debug signal enable map + * + * type = CXDGPIO_MUX_MAP + * data.mux.sig = debug signal id + * data.mux.gpio = GPIO id + * + * type = CXDGPIO_EXT_HPTA / CXDGPIO_EXT_HMBX / CXDGPIO_EXT_SWOUT + * data.map.map_low = GPIO 7~0 map + * data.map.map_high = GPIO 15~8 map + * + * type = CXDGPIO_EXT_SWIN + * data.swin.in_map_low = GPIO 7~0 input-en-map + * data.swin.in_map_high = GPIO 15~8 input-en-map + * data.swin.int_map_low = GPIO 7~0 interrupt source map + * data.swin.int_map_high = GPIO 15~8 interrupt source map + */ +#define CXDGPIO_SET_L4 4 +#define CXDGPIO_SET_L2 2 +struct rtw89_fbtc_h2c_set_gpio_en_map { + u8 type; /* gpio_type */ + u8 fver; /* FCX_VER_GPIODBG */ + u8 dlen; + u32 en_map; +}; + +struct rtw89_fbtc_h2c_set_gpio_mux { + u8 type; /* gpio_type */ + u8 fver; /* FCX_VER_GPIODBG */ + u8 dlen; + u8 sig; + u8 gpio; +}; + +struct rtw89_fbtc_h2c_set_gpio_ext_pta { + u8 type; /* gpio_type */ + u8 fver; /* FCX_VER_GPIODBG */ + u8 dlen; + u8 map_low; + u8 map_high; +}; + +struct rtw89_fbtc_h2c_set_gpio_ext_mb { + u8 type; /* gpio_type */ + u8 fver; /* FCX_VER_GPIODBG */ + u8 dlen; + u8 map_low; + u8 map_high; +}; + +struct rtw89_fbtc_h2c_set_gpio_ext_swout { + u8 type; /* gpio_type */ + u8 fver; /* FCX_VER_GPIODBG */ + u8 dlen; + u8 map_low; + u8 map_high; +}; + +struct rtw89_fbtc_h2c_set_gpio_ext_swin { + u8 type; /* gpio_type */ + u8 fver; /* FCX_VER_GPIODBG */ + u8 dlen; + u8 in_map_low; + u8 in_map_high; + u8 int_map_low; + u8 int_map_high; +}; + +struct rtw89_fbtc_h2c_set_gpio_2b { + u8 type; /* gpio_type */ + u8 fver; /* FCX_VER_GPIODBG */ + u8 dlen; + u8 data[CXDGPIO_SET_L2]; +} __packed; + +struct rtw89_fbtc_h2c_set_gpio_4b { + u8 type; /* gpio_type */ + u8 fver; /* FCX_VER_GPIODBG */ + u8 dlen; + u8 data[CXDGPIO_SET_L4]; +} __packed; + +union rtw89_fbtc_h2c_set_gpio_en_map_u { + struct rtw89_fbtc_h2c_set_gpio_4b fmt; + struct rtw89_fbtc_h2c_set_gpio_en_map data; +}; + +union rtw89_fbtc_h2c_set_gpio_mux_u { + struct rtw89_fbtc_h2c_set_gpio_2b fmt; + struct rtw89_fbtc_h2c_set_gpio_mux data; +}; + +union rtw89_fbtc_h2c_set_gpio_ext_pta_u { + struct rtw89_fbtc_h2c_set_gpio_2b fmt; + struct rtw89_fbtc_h2c_set_gpio_ext_pta data; +}; + +union rtw89_fbtc_h2c_set_gpio_ext_swin_u { + struct rtw89_fbtc_h2c_set_gpio_4b fmt; + struct rtw89_fbtc_h2c_set_gpio_ext_swin data; +}; + +struct rtw89_fbtc_h2c_set_gpio { + union rtw89_fbtc_h2c_set_gpio_en_map_u en_map; + union rtw89_fbtc_h2c_set_gpio_mux_u mux; + union rtw89_fbtc_h2c_set_gpio_ext_pta_u ext_pta; + union rtw89_fbtc_h2c_set_gpio_ext_pta_u ext_mb; + union rtw89_fbtc_h2c_set_gpio_ext_pta_u ext_swout; + union rtw89_fbtc_h2c_set_gpio_ext_swin_u ext_swin; +}; + struct rtw89_btc_fbtc_mreg_val_v1 { u8 fver; /* btc_ver::fcxmreg */ u8 reg_num; @@ -4178,6 +4333,7 @@ struct rtw89_btc { struct rtw89_btc_module mdinfo; struct rtw89_btc_btf_fwinfo fwinfo; struct rtw89_btc_dbg dbg; + struct rtw89_fbtc_h2c_set_gpio gpio; struct wiphy_work eapol_notify_work; struct wiphy_work arp_notify_work; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 538df0fb42cc..6147960521ee 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2391,6 +2391,16 @@ enum rtw89_btc_cxdrvinfo { CXDRVINFO_MAX, }; +enum rtw89_fbtc_gpio_type { + CXDGPIO_EN_MAP = 0x0, + CXDGPIO_MUX_MAP = 0x1, + CXDGPIO_EXT_HPTA = 0x2, + CXDGPIO_EXT_HMBX = 0x3, + CXDGPIO_EXT_SWOUT = 0x4, + CXDGPIO_EXT_SWIN = 0x5, + CXDGPIO_MAX, +}; + enum rtw89_scan_mode { RTW89_SCAN_IMMEDIATE, RTW89_SCAN_DELAY, From 528e5e1d78253136ea1b6f400f09db956476dba3 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:34 +0800 Subject: [PATCH 187/403] wifi: rtw89: coex: Refine RF calibration notify flow BT-coexistence only needs to record RF calibration is doing or not, don't need to record the status of the calibration steps. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 96ec5fa20422..187e7e70e86d 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -9144,14 +9144,13 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path, wl->rfk_info.state = BTC_WRFK_START; btc->cx.wl.wcnt[BTC_WCNT_RFK_REQ]++; - btc->cx.wl.wcnt[BTC_WCNT_RFK_GO]++; btc->dm.cnt_notify[BTC_NCNT_WL_RFK]++; _write_scbd(rtwdev, BTC_ALL_BT, BTC_WSCB_WLRFK, true); break; case BTC_WRFK_ONESHOT_START: case BTC_WRFK_ONESHOT_STOP: - wl->rfk_info.state = state; + btc->cx.wl.wcnt[BTC_WCNT_RFK_GO]++; if (type != BTC_WRFKT_RXDCK) return BTC_WRFK_ALLOW; break; @@ -9165,7 +9164,7 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path, default: rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s() warning state=%d\n", __func__, state); - break; + return result; } if (result == BTC_WRFK_ALLOW) { From b23378a5645417720dc54bfc94603be91af4f4e5 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:35 +0800 Subject: [PATCH 188/403] wifi: rtw89: coex: Fix log dump format with proper newline Fix the log output format in _show_mreg_v7() where the phy-0 gnt_status line was missing the proper field label and newline. Use the standard " %-15s : " format with "[gnt_status]" label consistent with the rest of the dump output. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 167 ++++++++++++---------- 1 file changed, 88 insertions(+), 79 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 187e7e70e86d..5aa06ef430c8 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -9656,8 +9656,10 @@ static int _show_wl_role_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) plink->client_cnt - 1, plink->mode, plink->ch, plink->bw); - if (plink->connected == MLME_NO_LINK) + if (plink->connected == MLME_NO_LINK) { + p += scnprintf(p, end - p, "\n"); continue; + } p += scnprintf(p, end - p, ", mac_id=%d, max_tx_time=%dus, max_tx_retry=%d\n", @@ -9747,7 +9749,7 @@ static int _show_bt_profile_info(struct rtw89_dev *rtwdev, char *buf, size_t buf if (hid.exist) { p += scnprintf(p, end - p, - "\n\r %-15s : type:%s%s%s%s%s pair-cnt:%d, sut_pwr:%d, golden-rx:%d\n", + "\n %-15s : type:%s%s%s%s%s pair-cnt:%d, sut_pwr:%d, golden-rx:%d\n", "[HID]", hid.type & BTC_HID_218 ? "2/18," : "", hid.type & BTC_HID_418 ? "4/18," : "", @@ -10345,7 +10347,7 @@ static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) "" : "(Mismatch!!)")); p += scnprintf(p, end - p, - " %-15s : wl[rssi_lvl:%d/para:%d/tx_pwr:[%d %d]/rx_lvl:[%d %d]/lna2:%d/stb_chg:%d]\n ", + " %-15s : wl[rssi_lvl:%d/para:%d/tx_pwr:[%d %d]/rx_lvl:[%d %d]/lna2:%d/stb_chg:%d]\n", "[dm_rf_ctrl]", wl->rssi_level, dm->trx_para_level, dm->rf_trx_para.wl_tx_power[RTW89_PHY_0], @@ -10355,7 +10357,7 @@ static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) dm->wl_lna2, dm->wl_stb_chg); p += scnprintf(p, end - p, - " %-15s : pre_agc:%d, btg_rx:%d\n ", + " %-15s : pre_agc:%d, btg_rx:%d\n", "[dm_bb_ctrl]", dm->wl_pre_agc, dm->wl_btg_rx); p += scnprintf(p, end - p, @@ -10467,11 +10469,9 @@ static int _show_fbtc_tdma(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) t->bind, t->leak_n, t->ext_ctrl); p += scnprintf(p, end - p, - "policy_type:%d", + "policy_type:%d\n", (u32)btc->policy_type); - p += scnprintf(p, end - p, "\n"); - return p - buf; } @@ -10778,11 +10778,9 @@ static int _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, char *buf, size_t bufsz le16_to_cpu(pcysta->a2dp_ept.cnt), le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); - p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d\n", le16_to_cpu(pcysta->a2dp_ept.tavg), le16_to_cpu(pcysta->a2dp_ept.tmax)); - - p += scnprintf(p, end - p, "\n"); } out: @@ -10917,11 +10915,9 @@ static int _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz le16_to_cpu(pcysta->a2dp_ept.cnt), le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); - p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d\n", le16_to_cpu(pcysta->a2dp_ept.tavg), le16_to_cpu(pcysta->a2dp_ept.tmax)); - - p += scnprintf(p, end - p, "\n"); } out: @@ -11055,11 +11051,9 @@ static int _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz le16_to_cpu(pcysta->a2dp_ept.cnt), le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); - p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d\n", le16_to_cpu(pcysta->a2dp_ept.tavg), le16_to_cpu(pcysta->a2dp_ept.tmax)); - - p += scnprintf(p, end - p, "\n"); } out: @@ -11193,11 +11187,9 @@ static int _show_fbtc_cysta_v105(struct rtw89_dev *rtwdev, char *buf, size_t buf le16_to_cpu(pcysta->a2dp_ept.cnt), le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); - p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d\n", le16_to_cpu(pcysta->a2dp_ept.tavg), le16_to_cpu(pcysta->a2dp_ept.tmax)); - - p += scnprintf(p, end - p, "\n"); } out: @@ -11246,7 +11238,7 @@ static int _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz le16_to_cpu(pcysta->skip_cnt)); p += scnprintf(p, end - p, - "\n\r %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "\n %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", "[cycle_stat]", le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), @@ -11267,7 +11259,7 @@ static int _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz if (a2dp->exist) { p += scnprintf(p, end - p, - "\n\r %-15s : a2dp_ept:%d, a2dp_late:%d(streak 2S:%d/max:%d)", + "\n %-15s : a2dp_ept:%d, a2dp_late:%d(streak 2S:%d/max:%d)", "[a2dp_stat]", le16_to_cpu(pcysta->a2dp_ept.cnt), le16_to_cpu(pcysta->a2dp_ept.cnt_timeout), @@ -11306,10 +11298,10 @@ static int _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz if (cnt % divide_cnt == 1) { if (a2dp->exist) - p += scnprintf(p, end - p, "\n\r %-15s : ", + p += scnprintf(p, end - p, "\n %-15s : ", "[slotT_wermtan]"); else - p += scnprintf(p, end - p, "\n\r %-15s : ", + p += scnprintf(p, end - p, "\n %-15s : ", "[slotT_rxerr]"); } @@ -11370,7 +11362,7 @@ static int _show_fbtc_nullsta(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) ns = &pfwinfo->rpt_fbtc_nullsta.finfo; if (ver->fcxnullsta == 1) { for (i = 0; i < 2; i++) { - p += scnprintf(p, end - p, " %-15s : ", "\n[NULL-STA]"); + p += scnprintf(p, end - p, "\n %-15s : ", "[NULL-STA]"); p += scnprintf(p, end - p, "null-%d", i); p += scnprintf(p, end - p, "[ok:%d/", le32_to_cpu(ns->v1.result[i][1])); @@ -11389,7 +11381,7 @@ static int _show_fbtc_nullsta(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) } } else if (ver->fcxnullsta == 7) { for (i = 0; i < 2; i++) { - p += scnprintf(p, end - p, " %-15s : ", "\n[NULL-STA]"); + p += scnprintf(p, end - p, "\n %-15s : ", "[NULL-STA]"); p += scnprintf(p, end - p, "null-%d", i); p += scnprintf(p, end - p, "[Tx:%d/", le32_to_cpu(ns->v7.result[i][4])); @@ -11410,7 +11402,7 @@ static int _show_fbtc_nullsta(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) } } else { for (i = 0; i < 2; i++) { - p += scnprintf(p, end - p, " %-15s : ", "\n[NULL-STA]"); + p += scnprintf(p, end - p, "\n %-15s : ", "[NULL-STA]"); p += scnprintf(p, end - p, "null-%d", i); p += scnprintf(p, end - p, "[Tx:%d/", le32_to_cpu(ns->v2.result[i][4])); @@ -11717,7 +11709,7 @@ static int _show_gpio_dbg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) if (!en_map) goto out; - p += scnprintf(p, end - p, "\n\r %-15s : enable_map:0x%08x", + p += scnprintf(p, end - p, "\n %-15s : enable_map:0x%08x", "[gpio_dbg]", en_map); for (i = 0; i < BTC_DBG_NUM; i++) { @@ -11943,7 +11935,7 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) if (!(dm->coex_info_map & BTC_COEX_INFO_MREG)) return 0; - p += scnprintf(p, end - p, "\n\r========== [HW Status] =========="); + p += scnprintf(p, end - p, "\n========== [HW Status] ==========\n"); p += scnprintf(p, end - p, " %-15s : WL->BT0:0x%08x(cnt:%d), BT0->WL:0x%08x(total:%d, bt_update:%d)\n", @@ -11965,18 +11957,31 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) dm->pta_owner = rtw89_mac_get_ctrl_path(rtwdev); p += scnprintf(p, end - p, - "\n\r %-15s : pta_owner:%s, pta_req_mac:MAC%d, rf_gnt_source: polut_type:%s", + " %-15s : pta_owner:%s, pta_req_mac:MAC%d, rf_gnt_source: polut_type:%s\n", "[gnt_status]", rtwdev->chip->para_ver & BTC_FEAT_PTA_ONOFF_CTRL ? "HW" : dm->pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", wl->pta_req_mac, id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); - p += scnprintf(p, end - p, ", phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]", - dm->gnt_set[RTW89_PHY_0].gnt_wl_sw_en ? "SW" : "HW", - dm->gnt_set[RTW89_PHY_0].gnt_wl, - dm->gnt_set[RTW89_PHY_0].gnt_bt0_sw_en ? "SW" : "HW", - dm->gnt_set[RTW89_PHY_0].gnt_bt0); + if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) + p += scnprintf(p, end - p, + " %-15s : phy-0[gnt_wl:%s-%d/gnt_bt0:%s-%d/gnt_bt1:%s-%d]", + "[gnt_status]", + dm->gnt_set[RTW89_PHY_0].gnt_wl_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_0].gnt_wl, + dm->gnt_set[RTW89_PHY_0].gnt_bt0_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_0].gnt_bt0, + dm->gnt_set[RTW89_PHY_0].gnt_bt1_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_0].gnt_bt1); + else + p += scnprintf(p, end - p, + " %-15s : phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]", + "[gnt_status]", + dm->gnt_set[RTW89_PHY_0].gnt_wl_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_0].gnt_wl, + dm->gnt_set[RTW89_PHY_0].gnt_bt0_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_0].gnt_bt0); if (rtwdev->dbcc_en) { p += scnprintf(p, end - p, @@ -12000,7 +12005,7 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) if (cnt % 6 == 0) p += scnprintf(p, end - p, - "\n\r %-15s : %s_0x%x=0x%x", "[reg]", + "\n %-15s : %s_0x%x=0x%x", "[reg]", id_to_regtype(type), offset, val); else p += scnprintf(p, end - p, ", %s_0x%x=0x%x", @@ -12336,12 +12341,11 @@ static int _show_summary_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); p += scnprintf(p, end - p, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], cnt[BTC_NCNT_WL_STA]); - p += scnprintf(p, end - p, "\n"); p += scnprintf(p, end - p, " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", "[notify_cnt]", @@ -12457,12 +12461,11 @@ static int _show_summary_v105(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); p += scnprintf(p, end - p, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], cnt[BTC_NCNT_WL_STA]); - p += scnprintf(p, end - p, "\n"); p += scnprintf(p, end - p, " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", "[notify_cnt]", @@ -12495,7 +12498,7 @@ static int _show_summary_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) return 0; p += scnprintf(p, end - p, "%s", - "\n\r========== [Statistics] =========="); + "\n========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF && @@ -12503,7 +12506,7 @@ static int _show_summary_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) prptctrl = &pfwinfo->rpt_ctrl.finfo.v7; p += scnprintf(p, end - p, - "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d)," + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d)," "c2h_cnt=%d(fw_send:%d, len:%d, max:%d), ", "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, @@ -12521,16 +12524,17 @@ static int _show_summary_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) if (dm->error.map.wl_fw_hang) p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); p += scnprintf(p, end - p, - "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); p += scnprintf(p, end - p, - "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", + "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)\n", le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), @@ -12538,7 +12542,7 @@ static int _show_summary_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); p += scnprintf(p, end - p, - "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", "[RFK/LPS]", wl->wcnt[BTC_WCNT_RFK_REQ], wl->wcnt[BTC_WCNT_RFK_GO], wl->wcnt[BTC_WCNT_RFK_REJECT], @@ -12548,12 +12552,12 @@ static int _show_summary_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, ", bt_rfk[req:%d]", le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", + p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]\n", le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { p += scnprintf(p, end - p, - "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)\n", "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, @@ -12564,19 +12568,19 @@ static int _show_summary_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) cnt_sum += dm->cnt_notify[i]; p += scnprintf(p, end - p, - "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); p += scnprintf(p, end - p, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], cnt[BTC_NCNT_WL_STA]); p += scnprintf(p, end - p, - "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], @@ -12608,7 +12612,7 @@ static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) return 0; p += scnprintf(p, end - p, "%s", - "\n\r========== [Statistics] =========="); + "\n========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF && @@ -12616,7 +12620,7 @@ static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) prptctrl = &pfwinfo->rpt_ctrl.finfo.v8; p += scnprintf(p, end - p, - "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ", + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ", "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, le16_to_cpu(prptctrl->rpt_info.cnt_h2c), @@ -12634,16 +12638,17 @@ static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) if (dm->error.map.wl_fw_hang) p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); p += scnprintf(p, end - p, - "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); p += scnprintf(p, end - p, - "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", + "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)\n", le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), @@ -12651,7 +12656,7 @@ static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); p += scnprintf(p, end - p, - "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", "[RFK/LPS]", wl->wcnt[BTC_WCNT_RFK_REQ], wl->wcnt[BTC_WCNT_RFK_GO], wl->wcnt[BTC_WCNT_RFK_REJECT], @@ -12661,12 +12666,12 @@ static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, ", bt_rfk[req:%d]", le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", + p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]\n", le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { p += scnprintf(p, end - p, - "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)\n", "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, @@ -12677,19 +12682,19 @@ static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) cnt_sum += dm->cnt_notify[i]; p += scnprintf(p, end - p, - "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); p += scnprintf(p, end - p, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], cnt[BTC_NCNT_WL_STA]); p += scnprintf(p, end - p, - "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], @@ -12721,7 +12726,7 @@ static int _show_summary_v9(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) return 0; p += scnprintf(p, end - p, "%s", - "\n\r========== [Statistics] =========="); + "\n========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF && @@ -12729,7 +12734,7 @@ static int _show_summary_v9(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) prptctrl = &pfwinfo->rpt_ctrl.finfo.v9; p += scnprintf(p, end - p, - "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, ", + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, ", "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, le16_to_cpu(prptctrl->rpt_info.cnt_h2c), @@ -12745,16 +12750,17 @@ static int _show_summary_v9(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) if (dm->error.map.wl_fw_hang) p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); p += scnprintf(p, end - p, - "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); p += scnprintf(p, end - p, - "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", + "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)\n", le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), @@ -12762,7 +12768,7 @@ static int _show_summary_v9(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); p += scnprintf(p, end - p, - "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", "[RFK/LPS]", wl->wcnt[BTC_WCNT_RFK_REQ], wl->wcnt[BTC_WCNT_RFK_GO], wl->wcnt[BTC_WCNT_RFK_REJECT], @@ -12772,12 +12778,12 @@ static int _show_summary_v9(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += scnprintf(p, end - p, ", bt_rfk[req:%d]", le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", + p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]\n", le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { p += scnprintf(p, end - p, - "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)\n", "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, @@ -12788,19 +12794,19 @@ static int _show_summary_v9(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) cnt_sum += dm->cnt_notify[i]; p += scnprintf(p, end - p, - "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); p += scnprintf(p, end - p, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], cnt[BTC_NCNT_WL_STA]); p += scnprintf(p, end - p, - "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], @@ -12832,7 +12838,7 @@ static int _show_summary_v11(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) return 0; p += scnprintf(p, end - p, "%s", - "\n\r========== [Statistics] =========="); + "\n========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF && @@ -12840,7 +12846,7 @@ static int _show_summary_v11(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) prptctrl = &pfwinfo->rpt_ctrl.finfo.v11; p += scnprintf(p, end - p, - "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ", + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ", "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, le16_to_cpu(prptctrl->rpt_info.cnt_h2c), @@ -12858,16 +12864,17 @@ static int _show_summary_v11(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) if (dm->error.map.wl_fw_hang) p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); p += scnprintf(p, end - p, - "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); p += scnprintf(p, end - p, - "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", + "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)\n", le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), @@ -12875,19 +12882,19 @@ static int _show_summary_v11(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); p += scnprintf(p, end - p, - "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", "[RFK/LPS]", wl->wcnt[BTC_WCNT_RFK_REQ], wl->wcnt[BTC_WCNT_RFK_GO], wl->wcnt[BTC_WCNT_RFK_REJECT], wl->wcnt[BTC_WCNT_RFK_TIMEOUT], wl->rfk_info.proc_time); - p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", + p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]\n", le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { p += scnprintf(p, end - p, - "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)\n", "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, @@ -12898,19 +12905,19 @@ static int _show_summary_v11(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) cnt_sum += dm->cnt_notify[i]; p += scnprintf(p, end - p, - "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); p += scnprintf(p, end - p, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], cnt[BTC_NCNT_WL_STA]); p += scnprintf(p, end - p, - "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], @@ -12993,6 +13000,8 @@ ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) else if (ver->fcxbtcrpt == 11) p += _show_summary_v11(rtwdev, p, end - p); + p += scnprintf(p, end - p, "\n"); + return p - buf; } From 068daf7086fccf4f4297609cd0ec792460b4a3f2 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:36 +0800 Subject: [PATCH 189/403] wifi: rtw89: coex: Add BTC report version 8 support for BT sub-reports RTL8922A (FW >= 0.35.111) and RTL8922D (FW >= 0.35.94) set fcxbtver, fcxbtscan and fcxbtafh to 8, but the handler in _chk_btc_report only had branches for version 1 and 7. When version 8 arrived pfinfo was left NULL and pcinfo->req_len was left at zero, so the length check at validation stage rejected the report and bt->ver_info.fw was never written, causing BT_FW:0x0 in the BTC dump. BT-scan and BT-afh version 8 hit the goto err path for the same reason, making all BT sub-reports silently broken on these chips. The structural change in version 8 is that the previously reserved second byte in each struct is now bt_id (0 = BT0, 1 = BT1), allowing firmware to send separate reports for each Bluetooth device. All three structs are otherwise layout-compatible with version 7. Add rtw89_btc_fbtc_btver_v8, rtw89_btc_fbtc_btscan_v8 and rtw89_btc_fbtc_btafh_v8 structs with the bt_id field, extend the corresponding unions, add version 8 branches to _chk_btc_report, and update _update_bt_report to route each report to BT0 or BT1 according to BT ID. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 52 ++++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/core.h | 34 +++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 5aa06ef430c8..96bb68f09a01 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1529,7 +1529,14 @@ static void _update_bt_report(struct rtw89_dev *rtwdev, u8 rpt_type, u8 *pfinfo) switch (rpt_type) { case BTC_RPT_TYPE_BT_VER: - if (ver->fcxbtver == 7) { + if (ver->fcxbtver == 8) { + pver->v8 = *(struct rtw89_btc_fbtc_btver_v8 *)pfinfo; + bt = pver->v8.bt_id ? &btc->cx.bt1 : &btc->cx.bt0; + bt->ver_info.fw = le32_to_cpu(pver->v8.fw_ver); + bt->ver_info.fw_coex = le32_get_bits(pver->v8.coex_ver, + GENMASK(7, 0)); + bt->feature = le32_to_cpu(pver->v8.feature); + } else if (ver->fcxbtver == 7) { pver->v7 = *(struct rtw89_btc_fbtc_btver_v7 *)pfinfo; bt->ver_info.fw = le32_to_cpu(pver->v7.fw_ver); bt->ver_info.fw_coex = le32_get_bits(pver->v7.coex_ver, @@ -1570,6 +1577,19 @@ static void _update_bt_report(struct rtw89_dev *rtwdev, u8 rpt_type, u8 *pfinfo) pscan_v7->para[i].intvl == 0) scan_update = false; } + } else if (ver->fcxbtscan == 8) { + struct rtw89_btc_fbtc_btscan_v8 *pscan_v8 = + (struct rtw89_btc_fbtc_btscan_v8 *)pfinfo; + struct rtw89_btc_bt_info *tbt = + pscan_v8->bt_id ? &btc->cx.bt1 : &btc->cx.bt0; + + for (i = 0; i < CXSCAN_MAX; i++) { + tbt->scan_info_v2[i] = pscan_v8->para[i]; + if ((pscan_v8->type & BIT(i)) && + pscan_v8->para[i].win == 0 && + pscan_v8->para[i].intvl == 0) + scan_update = false; + } } if (scan_update) bt->scan_info_update = 1; @@ -1597,6 +1617,22 @@ static void _update_bt_report(struct rtw89_dev *rtwdev, u8 rpt_type, u8 *pfinfo) memcpy(&bt_linfo->afh_map_le[0], pafh_v7->afh_le_a, 4); memcpy(&bt_linfo->afh_map_le[4], pafh_v7->afh_le_b, 1); } + } else if (ver->fcxbtafh == 8) { + struct rtw89_btc_fbtc_btafh_v8 *pafh_v8 = + (struct rtw89_btc_fbtc_btafh_v8 *)pfinfo; + struct rtw89_btc_bt_info *tbt = + pafh_v8->bt_id ? &btc->cx.bt1 : &btc->cx.bt0; + struct rtw89_btc_bt_link_info *tbt_linfo = &tbt->link_info; + + if (pafh_v8->map_type & RPT_BT_AFH_SEQ_LEGACY) { + memcpy(&tbt_linfo->afh_map[0], pafh_v8->afh_l, 4); + memcpy(&tbt_linfo->afh_map[4], pafh_v8->afh_m, 4); + memcpy(&tbt_linfo->afh_map[8], pafh_v8->afh_h, 2); + } + if (pafh_v8->map_type & RPT_BT_AFH_SEQ_LE) { + memcpy(&tbt_linfo->afh_map_le[0], pafh_v8->afh_le_a, 4); + memcpy(&tbt_linfo->afh_map_le[4], pafh_v8->afh_le_b, 1); + } } else if (ver->fcxbtafh == 1) { pafh_v1 = (struct rtw89_btc_fbtc_btafh *)pfinfo; memcpy(&bt_linfo->afh_map[0], pafh_v1->afh_l, 4); @@ -1882,6 +1918,12 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, pfinfo = &pfwinfo->rpt_fbtc_btver.finfo.v7; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btver.finfo.v7); fwsubver->fcxbtver = pfwinfo->rpt_fbtc_btver.finfo.v7.fver; + } else if (ver->fcxbtver == 8) { + pfinfo = &pfwinfo->rpt_fbtc_btver.finfo.v8; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btver.finfo.v8); + fwsubver->fcxbtver = pfwinfo->rpt_fbtc_btver.finfo.v8.fver; + } else { + goto err; } pcinfo->req_fver = ver->fcxbtver; break; @@ -1899,6 +1941,10 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, pfinfo = &pfwinfo->rpt_fbtc_btscan.finfo.v7; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btscan.finfo.v7); fwsubver->fcxbtscan = pfwinfo->rpt_fbtc_btscan.finfo.v7.fver; + } else if (ver->fcxbtscan == 8) { + pfinfo = &pfwinfo->rpt_fbtc_btscan.finfo.v8; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btscan.finfo.v8); + fwsubver->fcxbtscan = pfwinfo->rpt_fbtc_btscan.finfo.v8.fver; } else { goto err; } @@ -1918,6 +1964,10 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, pfinfo = &pfwinfo->rpt_fbtc_btafh.finfo.v7; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo.v7); fwsubver->fcxbtafh = pfwinfo->rpt_fbtc_btafh.finfo.v7.fver; + } else if (ver->fcxbtafh == 8) { + pfinfo = &pfwinfo->rpt_fbtc_btafh.finfo.v8; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo.v8); + fwsubver->fcxbtafh = pfwinfo->rpt_fbtc_btafh.finfo.v8.fver; } else { goto err; } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ba3a9b0d4aa2..aaed155ea423 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2647,10 +2647,19 @@ struct rtw89_btc_fbtc_btscan_v7 { struct rtw89_btc_bt_scan_info_v2 para[CXSCAN_MAX]; } __packed; +struct rtw89_btc_fbtc_btscan_v8 { + u8 fver; /* btc_ver::fcxbtscan */ + u8 type; + u8 bt_id; /* 0:BT0, 1:BT1 */ + u8 rsvd1; + struct rtw89_btc_bt_scan_info_v2 para[CXSCAN_MAX]; +} __packed; + union rtw89_btc_fbtc_btscan { struct rtw89_btc_fbtc_btscan_v1 v1; struct rtw89_btc_fbtc_btscan_v2 v2; struct rtw89_btc_fbtc_btscan_v7 v7; + struct rtw89_btc_fbtc_btscan_v8 v8; }; struct rtw89_btc_bt_info { @@ -3683,9 +3692,21 @@ struct rtw89_btc_fbtc_btver_v7 { __le32 feature; } __packed; +struct rtw89_btc_fbtc_btver_v8 { + u8 fver; + u8 bt_id; /* 0:BT0, 1:BT1 */ + u8 rsvd1; + u8 rsvd2; + + __le32 coex_ver; /*bit[15:8]->shared, bit[7:0]->non-shared */ + __le32 fw_ver; + __le32 feature; +} __packed; + union rtw89_btc_fbtc_btver { struct rtw89_btc_fbtc_btver_v1 v1; struct rtw89_btc_fbtc_btver_v7 v7; + struct rtw89_btc_fbtc_btver_v8 v8; } __packed; struct rtw89_btc_fbtc_btafh { @@ -3721,6 +3742,18 @@ struct rtw89_btc_fbtc_btafh_v7 { u8 afh_le_b[4]; } __packed; +struct rtw89_btc_fbtc_btafh_v8 { + u8 fver; + u8 map_type; + u8 bt_id; /* 0:BT0, 1:BT1 */ + u8 rsvd1; + u8 afh_l[4]; /*bit0:2402, bit1:2403.... bit31:2433 */ + u8 afh_m[4]; /*bit0:2434, bit1:2435.... bit31:2465 */ + u8 afh_h[4]; /*bit0:2466, bit1:2467.....bit14:2480 */ + u8 afh_le_a[4]; + u8 afh_le_b[4]; +} __packed; + struct rtw89_btc_fbtc_btdevinfo { u8 fver; /* btc_ver::fcxbtdevinfo */ u8 rsvd; @@ -4194,6 +4227,7 @@ union rtw89_btc_fbtc_btafh_info { struct rtw89_btc_fbtc_btafh v1; struct rtw89_btc_fbtc_btafh_v2 v2; struct rtw89_btc_fbtc_btafh_v7 v7; + struct rtw89_btc_fbtc_btafh_v8 v8; }; struct rtw89_btc_report_ctrl_state { From 2951ecd83cc43c112b9f9883d248406677a41e6c Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:37 +0800 Subject: [PATCH 190/403] wifi: rtw89: coex: Add dual Bluetooth debug info dump As RTL8922D support dual Bluetooth, add BT debug info dump for it. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 51 ++++++++++++++++------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 96bb68f09a01..6a718c54de67 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -9779,10 +9779,13 @@ enum btc_bt_a2dp_type { BTC_A2DP_TWS_RELAY = 2, }; -static int _show_bt_profile_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) +static int _show_bt_profile_info(struct rtw89_dev *rtwdev, char *buf, + size_t bufsz, u8 bid) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt0.link_info; + struct rtw89_btc_bt_info *bt = (bid == BTC_BT_1ST) ? + &btc->cx.bt0 : &btc->cx.bt1; + struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; struct rtw89_btc_bt_hfp_desc hfp = bt_linfo->hfp_desc; struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc; struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; @@ -9835,15 +9838,16 @@ static int _show_bt_profile_info(struct rtw89_dev *rtwdev, char *buf, size_t buf return p - buf; } -static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) +static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, u8 bid) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_cx *cx = &btc->cx; - struct rtw89_btc_bt_info *bt = &cx->bt0; + struct rtw89_btc_bt_info *bt = (bid == BTC_BT_1ST) ? &cx->bt0 : &cx->bt1; struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; struct rtw89_btc_module *md = &btc->mdinfo; + u8 bt_pos = (bid == BTC_BT_1ST) ? md->bt0_pos : md->bt1_pos; s8 br_dbm = bt->link_info.bt_txpwr_desc.br_dbm; s8 le_dbm = bt->link_info.bt_txpwr_desc.le_dbm; u8 hw_band = wl->role_info.pta_req_band; @@ -9855,13 +9859,21 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) if (!(btc->dm.coex_info_map & BTC_COEX_INFO_BT)) return 0; - p += scnprintf(p, end - p, "========== [BT Status] ==========\n"); + if (bid == BTC_BT_2ND) { + if (!(rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT)) + return 0; + if (!bt->enable.now) + return 0; + } + + p += scnprintf(p, end - p, "========== [BT_%s Status] ==========\n", + bid == BTC_BT_1ST ? "1ST" : "2ND"); p += scnprintf(p, end - p, " %-15s : enable:%s, btg:%s%s, connect:%s, ", "[status]", bt->enable.now ? "Y" : "N", bt->btg_type ? "Y" : "N", - (bt->enable.now && (bt->btg_type != md->bt0_pos) ? + (bt->enable.now && (bt->btg_type != bt_pos) ? "(efuse-mismatch!!)" : ""), (bt_linfo->status.map.connect ? "Y" : "N")); @@ -9927,7 +9939,7 @@ static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) bt->bcnt[BTC_BCNT_INQPAG], bt->bcnt[BTC_BCNT_INQ], bt->bcnt[BTC_BCNT_PAGE], bt->bcnt[BTC_BCNT_IGNOWL]); - p += _show_bt_profile_info(rtwdev, p, end - p); + p += _show_bt_profile_info(rtwdev, p, end - p, bid); p += scnprintf(p, end - p, " %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)\n", @@ -12034,12 +12046,22 @@ static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) dm->gnt_set[RTW89_PHY_0].gnt_bt0); if (rtwdev->dbcc_en) { - p += scnprintf(p, end - p, - ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]", - dm->gnt_set[RTW89_PHY_1].gnt_wl_sw_en ? "SW" : "HW", - dm->gnt_set[RTW89_PHY_1].gnt_wl, - dm->gnt_set[RTW89_PHY_1].gnt_bt0_sw_en ? "SW" : "HW", - dm->gnt_set[RTW89_PHY_1].gnt_bt0); + if (rtwdev->chip->para_ver & BTC_FEAT_DUAL_BT) + p += scnprintf(p, end - p, + ", phy-1[gnt_wl:%s-%d/gnt_bt0:%s-%d/gnt_bt1:%s-%d]", + dm->gnt_set[RTW89_PHY_1].gnt_wl_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_1].gnt_wl, + dm->gnt_set[RTW89_PHY_1].gnt_bt0_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_1].gnt_bt0, + dm->gnt_set[RTW89_PHY_1].gnt_bt1_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_1].gnt_bt1); + else + p += scnprintf(p, end - p, + ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]", + dm->gnt_set[RTW89_PHY_1].gnt_wl_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_1].gnt_wl, + dm->gnt_set[RTW89_PHY_1].gnt_bt0_sw_en ? "SW" : "HW", + dm->gnt_set[RTW89_PHY_1].gnt_bt0); } pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo; @@ -13020,7 +13042,8 @@ ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) p += _show_cx_info(rtwdev, p, end - p); p += _show_wl_info(rtwdev, p, end - p); - p += _show_bt_info(rtwdev, p, end - p); + p += _show_bt_info(rtwdev, p, end - p, BTC_BT_1ST); + p += _show_bt_info(rtwdev, p, end - p, BTC_BT_2ND); p += _show_dm_info(rtwdev, p, end - p); p += _show_fw_dm_msg(rtwdev, p, end - p); From ffb7d6a1c62fa3411d6a27a9f664531d0df62272 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:38 +0800 Subject: [PATCH 191/403] wifi: rtw89: coex: Fix TDMA v8 handling to unblock BTC report parsing fcxtdma=8 was not handled in _chk_btc_report(), causing the parser to hit 'goto err' and return 0 when processing the TDMA sub-report. This broke the _parse_btc_report() loop before reaching BT_VER (type=9), leaving bt->ver_info.fw always zero on RTL8922A/D. TDMA v8 uses the same struct layout as v3/v4/v7 (rtw89_btc_fbtc_tdma_v3, 12 bytes), so add it to the existing v3/v4/v7 branch in both switch cases. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 6a718c54de67..7ab22c60a420 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1769,7 +1769,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_tdma.finfo.v1); fwsubver->fcxtdma = 0; } else if (ver->fcxtdma == 3 || ver->fcxtdma == 4 || - ver->fcxtdma == 7) { + ver->fcxtdma == 7 || ver->fcxtdma == 8) { pfinfo = &pfwinfo->rpt_fbtc_tdma.finfo.v3; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_tdma.finfo.v3); fwsubver->fcxtdma = pfwinfo->rpt_fbtc_tdma.finfo.v3.fver; @@ -2300,7 +2300,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, &pfwinfo->rpt_fbtc_tdma.finfo.v1, sizeof(dm->tdma_now))); else if (ver->fcxtdma == 3 || ver->fcxtdma == 4 || - ver->fcxtdma == 7) + ver->fcxtdma == 7 || ver->fcxtdma == 8) _chk_btc_err(rtwdev, BTC_DCNT_TDMA_NONSYNC, memcmp(&dm->tdma_now, &pfwinfo->rpt_fbtc_tdma.finfo.v3.tdma, From 4406b550ae2db9d80334471742d0b8ef4984d9cc Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:39 +0800 Subject: [PATCH 192/403] wifi: rtw89: coex: Fix H2C command redundant send & version fall through I/O offload higher priority sending event didn't return after H2C command was sent, add a return to prevent send twice in the same time. Update driver info entry which is handling module control info didn't handle the version 9 command format, add if condition to handle it. TX power update H2C command result checker logic was reversed, it will lead to the TX power value never update again after first update, fix the issue. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-14-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 7ab22c60a420..6b3e199398cf 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1073,6 +1073,7 @@ static int _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func, } btc->fwinfo.cnt_h2c++; + return 0; } else { /* Fill H2C MACRO buffer(TLV format) temporarily */ if (btc->hbuf_cnt == 0) _reset_h2c_macro(btc); @@ -3500,6 +3501,8 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 index) if (ver->fcxctrl == 7) rtw89_fw_h2c_cxdrv_ctrl_v7(rtwdev, index); + else if (ver->fcxctrl == 9) + rtw89_fw_h2c_cxdrv_ctrl_v9(rtwdev, index); else rtw89_fw_h2c_cxdrv_ctrl(rtwdev, index); break; @@ -3893,7 +3896,7 @@ static void _set_bt_tx_power(struct rtw89_dev *rtwdev, bool force_exec, u8 bid, if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) len = SET_RF_PARA_AX_LEN; - if (_send_fw_cmd(rtwdev, BTFC_SET, h2c_func, buf, len)) { + 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) bt->tx_power_now = level; From 6a43bbee78a54b01bb67718f4fc527c9f44d3804 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 24 Jul 2026 21:56:40 +0800 Subject: [PATCH 193/403] wifi: rtw89: coex: Handle Bluetooth LE-Audio related coexistence feature Implement event handler of BTF_EVNT_BT_LEAUDIO_INFO C2H command, and related coexistence mechanism for Bluetooth LE-Audio feature. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260724135640.3195044-15-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 199 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 35 +++- 2 files changed, 233 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 6b3e199398cf..0e42c720819a 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -951,6 +951,7 @@ enum btc_reason_and_action { BTC_RSN_ACT1_WORK, BTC_RSN_BT_DEVINFO_WORK, BTC_RSN_RFK_CHK_WORK, + BTC_RSN_UPDATE_BT_LEAUDINFO, BTC_RSN_NUM, BTC_ACT_NONE = 100, BTC_ACT_WL_ONLY, @@ -975,6 +976,8 @@ enum btc_reason_and_action { BTC_ACT_BT_A2DP_PAN, BTC_ACT_BT_PAN_HID, BTC_ACT_BT_A2DP_PAN_HID, + BTC_ACT_BT_BIS, + BTC_ACT_BT_CIS, BTC_ACT_WL_25G_MCC, BTC_ACT_WL_2G_MCC, BTC_ACT_WL_2G_SCC, @@ -6076,6 +6079,43 @@ static void _action_bt_pan_hid(struct rtw89_dev *rtwdev) } } +static void _action_bt_bis(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_dm *dm = &btc->dm; + u16 policy_type; + + if (dm->bis_tdma) + policy_type = BTC_CXP_OFFE_2GBWISOB; + else + policy_type = BTC_CXP_OFF_EQ0; + + _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_PTA); + _set_policy(rtwdev, policy_type, BTC_ACT_BT_BIS); +} + +static void _action_bt_cis(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt0.link_info; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_dm *dm = &btc->dm; + u16 policy_type; + + if (bt_linfo->a2dp_desc.active) { + dm->slot_dur[CXST_W1] = 80; + dm->slot_dur[CXST_B1] = 20; + policy_type = BTC_CXP_PFIX_TDW1B1; + } else if (wl->status.map.traffic_dir & BIT(RTW89_TFC_UL)) { + policy_type = BTC_CXP_OFF_BWB0; + } else { + policy_type = BTC_CXP_OFF_BWB1; + } + + _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_PTA); + _set_policy(rtwdev, policy_type, BTC_ACT_BT_CIS); +} + static void _action_bt_a2dp_pan_hid(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; @@ -6692,6 +6732,7 @@ static void _action_by_bt(struct rtw89_dev *rtwdev) struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; + struct rtw89_btc_bt_leaudio_desc leaudio = bt_linfo->leaudio_desc; struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc; struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; struct rtw89_btc_bt_pan_desc pan = bt_linfo->pan_desc; @@ -6715,6 +6756,12 @@ static void _action_by_bt(struct rtw89_dev *rtwdev) if (bt_linfo->pan_desc.exist) profile_map |= BTC_BT_PAN; + if (leaudio.bis_exist) + profile_map |= BTC_BT_BIS; + + if (leaudio.cis_exist) + profile_map |= BTC_BT_CIS; + switch (profile_map) { case BTC_BT_NOPROFILE: if (pan.active) @@ -6740,6 +6787,13 @@ static void _action_by_bt(struct rtw89_dev *rtwdev) case BTC_BT_PAN: _action_bt_pan(rtwdev); break; + case BTC_BT_BIS: + _action_bt_bis(rtwdev); + break; + case BTC_BT_CIS: + case BTC_BT_CIS | BTC_BT_HID: + _action_bt_cis(rtwdev); + break; case BTC_BT_A2DP | BTC_BT_HFP: case BTC_BT_A2DP | BTC_BT_HID: case BTC_BT_A2DP | BTC_BT_HFP | BTC_BT_HID: @@ -7909,6 +7963,140 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid) } } +static void _update_bt_link_cnt(struct rtw89_dev *rtwdev, + struct rtw89_btc_bt_info *bt, bool b56g) +{ + struct rtw89_btc_bt_link_info *b = b56g ? &bt->link_info_56g : + &bt->link_info; + struct rtw89_btc_bt_leaudio_desc *leaudio = &b->leaudio_desc; + struct rtw89_btc_bt_a2dp_desc *a2dp = &b->a2dp_desc; + struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc; + struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc; + struct rtw89_btc_bt_pan_desc *pan = &b->pan_desc; + + b->link_cnt.last = b->link_cnt.now; + b->link_cnt.now = 0; + b->link_cnt.chg = 0; + + b->link_cnt.now += hfp->exist; + b->link_cnt.now += hid->exist; + b->link_cnt.now += a2dp->exist; + b->link_cnt.now += pan->exist; + b->link_cnt.now += leaudio->bis_exist; + b->link_cnt.now += leaudio->cis_exist; +} + +static void _update_bt_leaudio_info(struct rtw89_dev *rtwdev, u8 bid, + void *buf, u32 len) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_cx *cx = &btc->cx; + struct rtw89_btc_bt_info *bt = (bid == BTC_BT_1ST) ? &cx->bt0 : &cx->bt1; + struct rtw89_btc_bt_link_info *b = &bt->link_info; + struct rtw89_btc_bt_leaudio_info *src = buf; + struct rtw89_btc_bt_leaudio_desc *leaudio; + bool bt_56g = false, cis_exist, bis_exist; + u8 bis_cnt, cis_cnt; + + if ((src->len & BIT(7)) && bt->band_56G_support) { + b = &bt->link_info_56g; + bt_56g = true; + } + + leaudio = &b->leaudio_desc; + + if (!memcmp(b->leaudio_raw_info, src, BTC_BTINFO_MAX)) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s return by leaudio-info duplicate!\n", + __func__); + bt->bcnt[BTC_BCNT_LEAUDIO_INFOSAME]++; + return; + } + + memcpy(b->leaudio_raw_info, src, BTC_BTINFO_MAX); + + bis_exist = u8_get_bits(src->bis_cis, RTW89_BTC_LEAU_INFO_L2_BIS_EX); + cis_exist = u8_get_bits(src->bis_cis, RTW89_BTC_LEAU_INFO_L2_CIS_EX); + bis_cnt = u8_get_bits(src->bis_cis, RTW89_BTC_LEAU_INFO_L2_BIS_CNT); + cis_cnt = u8_get_bits(src->bis_cis, RTW89_BTC_LEAU_INFO_L2_CIS_CNT); + + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s: bis_exist:%d bis_cnt:%d cis_exist:%d cis_cnt:%d rssi:%d\n", + __func__, bis_exist, bis_cnt, cis_exist, cis_cnt, src->rssi); + + if (!bis_exist && !cis_exist) + memset(leaudio, 0, sizeof(*leaudio)); + + leaudio->bis_cnt = bis_cnt; + leaudio->bis_exist = bis_exist; + leaudio->cis_cnt = cis_cnt; + leaudio->cis_exist = cis_exist; + + if (leaudio->bis_exist) + b->status.map.profile_map |= BTC_BT_BIS; + else + b->status.map.profile_map &= ~BTC_BT_BIS; + + if (leaudio->cis_exist) + b->status.map.profile_map |= BTC_BT_CIS; + else + b->status.map.profile_map &= ~BTC_BT_CIS; + + _update_bt_link_cnt(rtwdev, bt, bt_56g); + + leaudio->rssi = chip->ops->btc_get_bt_rssi(rtwdev, src->rssi); + + leaudio->bis_cnt_last = leaudio->bis_cnt; + leaudio->bis_exist_last = leaudio->bis_exist; + leaudio->cis_cnt_last = leaudio->cis_cnt; + leaudio->cis_exist_last = leaudio->cis_exist; +} + +static void _update_bt_bistdma_info(struct rtw89_dev *rtwdev, u8 bid, + void *buf, u32 len) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_cx *cx = &btc->cx; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_bt_info *bt = (bid == BTC_BT_1ST) ? &cx->bt0 : &cx->bt1; + struct rtw89_btc_bt_link_info *b = &bt->link_info; + struct rtw89_btc_bt_bistdma_info_le *src = buf; + struct rtw89_btc_bt_leaudio_desc *leaudio; + + if ((src->len & BIT(7)) && bt->band_56G_support) + b = &bt->link_info_56g; + + leaudio = &b->leaudio_desc; + + if (!memcmp(b->bistdma_raw_info, src, BTC_BTINFO_MAX)) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s return by bistdma-info duplicate!\n", + __func__); + bt->bcnt[BTC_BCNT_LEAUDIO_INFOSAME]++; + return; + } + + memcpy(b->bistdma_raw_info, src, BTC_BTINFO_MAX); + + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s: bis_start_end:%d bis_trx:%d\n", + __func__, + u8_get_bits(src->bis, RTW89_BTC_BIS_INFO_L2_START_END), + u8_get_bits(src->bis, RTW89_BTC_BIS_INFO_L2_TRX)); + + leaudio->bis_trx = u8_get_bits(src->bis, RTW89_BTC_BIS_INFO_L2_TRX); + leaudio->bis_start_end = u8_get_bits(src->bis, + RTW89_BTC_BIS_INFO_L2_START_END); + + leaudio->diff_t = (src->diff_t_hb * 256 + src->diff_t_lb) * 625 / 1000; + + if (btc->ant_type == BTC_ANT_SHARED && leaudio->diff_t > 13) + dm->bis_tdma = true; + else + dm->bis_tdma = false; +} + #define BTC_BTINFO_PWR_LEN 5 static void _update_bt_txpwr_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) { @@ -9606,6 +9794,14 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, case BTF_EVNT_CX_RUNINFO: btc->dm.cnt_dm[BTC_DCNT_CX_RUNINFO]++; break; + case BTF_EVNT_BT_LEAUDIO_INFO: + bt->bcnt[BTC_BCNT_LEAUDIO_INFOUPDATE]++; + if (buf[BTC_BTINFO_L0] == BTC_BTINFO_BISTDMA) + _update_bt_bistdma_info(rtwdev, bid, buf, len); + else + _update_bt_leaudio_info(rtwdev, bid, buf, len); + _run_coex(rtwdev, BTC_RSN_UPDATE_BT_LEAUDINFO); + break; case BTF_EVNT_BT_QUERY_TXPWR: bt->bcnt[BTC_BCNT_TXPWR_UPDATE]++; _update_bt_txpwr_info(rtwdev, buf, len); @@ -10107,6 +10303,7 @@ static const char *steps_to_str(u16 step) CASE_BTC_RSN_STR(ACT1_WORK); CASE_BTC_RSN_STR(BT_DEVINFO_WORK); CASE_BTC_RSN_STR(RFK_CHK_WORK); + CASE_BTC_RSN_STR(UPDATE_BT_LEAUDINFO); CASE_BTC_ACT_STR(NONE); CASE_BTC_ACT_STR(WL_ONLY); @@ -10131,6 +10328,8 @@ static const char *steps_to_str(u16 step) CASE_BTC_ACT_STR(BT_A2DP_PAN); CASE_BTC_ACT_STR(BT_PAN_HID); CASE_BTC_ACT_STR(BT_A2DP_PAN_HID); + CASE_BTC_ACT_STR(BT_BIS); + CASE_BTC_ACT_STR(BT_CIS); CASE_BTC_ACT_STR(WL_25G_MCC); CASE_BTC_ACT_STR(WL_2G_MCC); CASE_BTC_ACT_STR(WL_2G_SCC); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index aaed155ea423..09c19a6b9058 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1383,6 +1383,8 @@ enum rtw89_btc_btinfo { BTC_BTINFO_MAX }; +#define BTC_BTINFO_BISTDMA 0x48 /* cmd value that identifies BISTDMA data */ + enum rtw89_btc_dcnt { BTC_DCNT_RUN = 0x0, BTC_DCNT_CX_RUNINFO, @@ -2167,6 +2169,31 @@ struct rtw89_btc_bt_txpwr_desc { u8 le_gain_index; }; +struct rtw89_btc_bt_leaudio_info { + u8 cmd; + u8 len; + u8 bis_cis; +#define RTW89_BTC_LEAU_INFO_L2_BIS_EX BIT(0) +#define RTW89_BTC_LEAU_INFO_L2_BIS_CNT GENMASK(3, 1) +#define RTW89_BTC_LEAU_INFO_L2_CIS_EX BIT(4) +#define RTW89_BTC_LEAU_INFO_L2_CIS_CNT GENMASK(7, 5) + u8 rssi; + __le32 hbrsvd; +} __packed; + +struct rtw89_btc_bt_bistdma_info_le { + u8 cmd; + u8 len; + u8 bis; /* BIT(2) ~ BIT(7) is rsvd */ +#define RTW89_BTC_BIS_INFO_L2_START_END BIT(0) /* 0: BIS start, 1: BIS end */ +#define RTW89_BTC_BIS_INFO_L2_TRX BIT(1) /* 0: BIS Tx, 1: BIS Rx */ + u8 diff_t_lb; + u8 diff_t_hb; /* diff_t = (diff_t_hb * 256 + diff_t_lb) * 0.625 ms */ + u8 hb1rsvd; + u8 hb2rsvd; + u8 hb3rsvd; +} __packed; + struct rtw89_btc_bt_leaudio_desc { u32 bis_exist: 1; u32 bis_exist_last: 1; @@ -2177,7 +2204,9 @@ struct rtw89_btc_bt_leaudio_desc { u32 rssi: 8; u32 bis_cnt_last: 3; u32 cis_cnt_last: 3; - u32 rsvd: 8; + u32 bis_trx: 1; + u32 bis_start_end: 1; + u32 rsvd: 6; u16 diff_t; }; @@ -2213,6 +2242,9 @@ struct rtw89_btc_bt_link_info { u8 ble_scan_en: 1; u8 reinit: 1; u8 rsvd: 6; + + u8 leaudio_raw_info[BTC_BTINFO_MAX]; /* raw LE audio info from BT mailbox */ + u8 bistdma_raw_info[BTC_BTINFO_MAX]; /* raw BIS-TDMA info from BT mailbox */ }; struct rtw89_btc_bind_bt_status { @@ -4085,6 +4117,7 @@ struct rtw89_btc_dm { u8 lps_ctrl_scbd: 1; u8 lps_ctrl_scbd_last: 1; u8 lps_ctrl_change: 1; + u8 bis_tdma: 1; /* BIS TDMA mode active */ u8 scbd_write_instant; bool scbd_b2w_update; bool scbd_w2b_update; From c13ab5bed71662fda7ca8573da560bd572a60f03 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 26 Jul 2026 22:40:30 +0900 Subject: [PATCH 194/403] wifi: rtlwifi: rtl8821ae: remove conditional return with no effect in _rtl8821ae_llt_write() Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260726134034.1385834-2-ekffu200098@gmail.com --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index 9b119a51bc30..1ba53e671207 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -1462,12 +1462,7 @@ static bool _rtl8821ae_init_llt_table(struct ieee80211_hw *hw, u32 boundary) return status; } - status = _rtl8821ae_llt_write(hw, last_entry_of_txpktbuf, - txpktbuf_bndy); - if (!status) - return status; - - return status; + return _rtl8821ae_llt_write(hw, last_entry_of_txpktbuf, txpktbuf_bndy); } static bool _rtl8821ae_dynamic_rqpn(struct ieee80211_hw *hw, u32 boundary, From bbc18041ad5f35fd64211b82df2b585866bf75d1 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 26 Jul 2026 22:40:31 +0900 Subject: [PATCH 195/403] wifi: rtw89: remove conditional return with no effect in sys_init_*() Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260726134034.1385834-3-ekffu200098@gmail.com --- drivers/net/wireless/realtek/rtw89/mac.c | 6 +----- drivers/net/wireless/realtek/rtw89/mac_be.c | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index db9fca828829..2cbff2be9bbb 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1710,11 +1710,7 @@ static int sys_init_ax(struct rtw89_dev *rtwdev) if (ret) return ret; - ret = chip_func_en_ax(rtwdev); - if (ret) - return ret; - - return ret; + return chip_func_en_ax(rtwdev); } const struct rtw89_mac_size_set rtw89_mac_size = { diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 8de0fe5a3b1d..dfa0973e367c 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -929,11 +929,7 @@ static int sys_init_be(struct rtw89_dev *rtwdev) if (ret) return ret; - ret = chip_func_en_be(rtwdev); - if (ret) - return ret; - - return ret; + return chip_func_en_be(rtwdev); } static int mac_func_en_be(struct rtw89_dev *rtwdev) From 9f2948010764d708bda27369d09ce6f194abe8e3 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Mon, 27 Jul 2026 12:12:22 +0530 Subject: [PATCH 196/403] wifi: rtw88: Fix potential memory leak in rtw_txq_push_skb() The skb passed to the rtw_hci_tx_write() is expected to be freed when the function fails, but the error path in rtw_txq_push_skb() does not free the skb before returning. This can lead to a memory leak in rtw_txq_push() where a dequeued skb is passed to rtw_txq_push_skb(). Fixes: aaab5d0e6737 ("rtw88: kick off TX packets once for higher efficiency") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260727064223.61836-1-nihaal@cse.iitm.ac.in --- drivers/net/wireless/realtek/rtw88/tx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index 9d747a060b98..8786bbb421c2 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -619,6 +619,7 @@ static int rtw_txq_push_skb(struct rtw_dev *rtwdev, ret = rtw_hci_tx_write(rtwdev, &pkt_info, skb); if (ret) { rtw_err(rtwdev, "failed to write TX skb to HCI\n"); + ieee80211_free_txskb(rtwdev->hw, skb); return ret; } return 0; From 4c6eb712a91fa079be6f9f1419c96e0ad2227081 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Tue, 28 Jul 2026 18:05:28 -0700 Subject: [PATCH 197/403] wifi: ath12k: fix stride mismatch in mac_phy_caps_parse() Currently, in ath12k_wmi_mac_phy_caps_parse(), kzalloc() sizes the mac_phy_caps buffer as tot_phy_id * len, where len is clamped to min(firmware_len, sizeof(struct ath12k_wmi_mac_phy_caps_params)). The subsequent memcpy() destination advances by sizeof(full struct) per slot via C pointer arithmetic, not by the clamped len. When firmware sends short TLVs, the second and later slots are written past the end of the allocation. The reader in ath12k_pull_mac_phy_cap_svc_ready_ext() also indexes the buffer with full-struct pointer arithmetic, so the allocation must match that stride. Fix by using kzalloc_objs(), which derives the element size from the pointer type, making allocation size and pointer stride provably consistent regardless of what len the firmware provides. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260728-mac_phy_caps_parse-stride-mismatch-v1-1-27a9c1a3fbd0@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wmi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index e9e7566e0f69..d5160af60e00 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -4774,14 +4774,16 @@ static int ath12k_wmi_mac_phy_caps_parse(struct ath12k_base *soc, if (svc_rdy_ext->n_mac_phy_caps >= svc_rdy_ext->tot_phy_id) return -ENOBUFS; - len = min_t(u16, len, sizeof(struct ath12k_wmi_mac_phy_caps_params)); if (!svc_rdy_ext->n_mac_phy_caps) { - svc_rdy_ext->mac_phy_caps = kzalloc((svc_rdy_ext->tot_phy_id) * len, - GFP_ATOMIC); + svc_rdy_ext->mac_phy_caps = + kzalloc_objs(*svc_rdy_ext->mac_phy_caps, + svc_rdy_ext->tot_phy_id, + GFP_ATOMIC); if (!svc_rdy_ext->mac_phy_caps) return -ENOMEM; } + len = min_t(u16, len, sizeof(struct ath12k_wmi_mac_phy_caps_params)); memcpy(svc_rdy_ext->mac_phy_caps + svc_rdy_ext->n_mac_phy_caps, ptr, len); svc_rdy_ext->n_mac_phy_caps++; return 0; From 7a246c72132eb943b5844ba79dad597b47429dba Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Tue, 28 Jul 2026 18:05:29 -0700 Subject: [PATCH 198/403] wifi: ath11k: fix stride mismatch in mac_phy_caps_parse() Currently, in ath11k_wmi_tlv_mac_phy_caps_parse(), kcalloc() sizes the mac_phy_caps buffer as tot_phy_id * len, where len is clamped to min(firmware_len, sizeof(struct wmi_mac_phy_capabilities)). The subsequent memcpy() destination advances by sizeof(full struct) per slot via C pointer arithmetic, not by the clamped len. When firmware sends short TLVs, the second and later slots are written past the end of the allocation. The reader in ath11k_pull_mac_phy_cap_svc_ready_ext() also indexes the buffer with full-struct pointer arithmetic, so the allocation must match that stride. Fix by using kzalloc_objs(), which derives the element size from the pointer type, making allocation size and pointer stride provably consistent regardless of what len the firmware provides. Compile tested only. Fixes: 5b90fc760db5 ("ath11k: fix wmi service ready ext tlv parsing") Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260728-mac_phy_caps_parse-stride-mismatch-v1-2-27a9c1a3fbd0@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/wmi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index d6feaa710fe2..66547e9ee16c 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -4809,14 +4809,16 @@ static int ath11k_wmi_tlv_mac_phy_caps_parse(struct ath11k_base *soc, if (svc_rdy_ext->n_mac_phy_caps >= svc_rdy_ext->tot_phy_id) return -ENOBUFS; - len = min_t(u16, len, sizeof(struct wmi_mac_phy_capabilities)); if (!svc_rdy_ext->n_mac_phy_caps) { - svc_rdy_ext->mac_phy_caps = kcalloc(svc_rdy_ext->tot_phy_id, - len, GFP_ATOMIC); + svc_rdy_ext->mac_phy_caps = + kzalloc_objs(*svc_rdy_ext->mac_phy_caps, + svc_rdy_ext->tot_phy_id, + GFP_ATOMIC); if (!svc_rdy_ext->mac_phy_caps) return -ENOMEM; } + len = min_t(u16, len, sizeof(struct wmi_mac_phy_capabilities)); memcpy(svc_rdy_ext->mac_phy_caps + svc_rdy_ext->n_mac_phy_caps, ptr, len); svc_rdy_ext->n_mac_phy_caps++; return 0; From 8b8202b2e31367434a5079a6faee31588e5b4aa4 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Thu, 30 Jul 2026 01:04:56 +0900 Subject: [PATCH 199/403] wifi: ath6kl: return 0 explicitly in ath6kl_init_upload() status is always zero at the last return in ath6kl_init_upload(). Explicitly return 0 on the success path instead of returning status. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260729160458.201962-1-ekffu200098@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath6kl/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 782209dcb782..6481da4c1991 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1570,7 +1570,7 @@ static int ath6kl_init_upload(struct ath6kl *ar) if (status) return status; - return status; + return 0; } int ath6kl_init_hw_params(struct ath6kl *ar) From 333f7de560e1196034b67db16916b10a0c529e1d Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 23 Jul 2026 19:39:27 +0800 Subject: [PATCH 200/403] wifi: mm81x: prevent timers from outliving teardown The three timer teardown paths call timer_delete_sync_try() and ignore its return value. If a callback is running on another CPU it returns -1 without waiting, and it does not prevent a later rearm even when it does deactivate a pending timer. mm81x_skbq_tx_complete() can rearm the stale-status timer, and the rc and yaps callbacks queue work that rearms their timers. Teardown can therefore continue with a callback still running or the timer rearmed, so it fires after the associated state has been freed. Use timer_shutdown_sync() for these permanent teardowns: it waits for an in-flight callback and prevents any future rearm. In mm81x_rc_deinit() shut the timer down before cancel_work_sync() so the work can no longer recreate the timer/work cycle. Fixes: b1906cea00b0 ("wifi: mm81x: add mm81x Wi-Fi HaLow driver") Signed-off-by: Linmao Li Reviewed-by: Dan Callaghan Link: https://patch.msgid.link/20260723113927.2370301-1-lilinmao@kylinos.cn Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/mac.c | 2 +- drivers/net/wireless/morsemicro/mm81x/rc.c | 2 +- drivers/net/wireless/morsemicro/mm81x/yaps.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/morsemicro/mm81x/mac.c b/drivers/net/wireless/morsemicro/mm81x/mac.c index 392dae5d7ce9..08ca116a68b4 100644 --- a/drivers/net/wireless/morsemicro/mm81x/mac.c +++ b/drivers/net/wireless/morsemicro/mm81x/mac.c @@ -2349,7 +2349,7 @@ static void mm81x_stale_tx_status_timer(struct timer_list *t) static void mm81x_stale_tx_status_timer_finish(struct mm81x *mors) { - timer_delete_sync_try(&mors->stale_status.timer); + timer_shutdown_sync(&mors->stale_status.timer); } static void mm81x_mac_stale_tx_status_timer_init(struct mm81x *mors) diff --git a/drivers/net/wireless/morsemicro/mm81x/rc.c b/drivers/net/wireless/morsemicro/mm81x/rc.c index 04aff66de4bd..28dd293df966 100644 --- a/drivers/net/wireless/morsemicro/mm81x/rc.c +++ b/drivers/net/wireless/morsemicro/mm81x/rc.c @@ -60,8 +60,8 @@ void mm81x_rc_init(struct mm81x *mors) void mm81x_rc_deinit(struct mm81x *mors) { + timer_shutdown_sync(&mors->mrc.timer); cancel_work_sync(&mors->mrc.work); - timer_delete_sync_try(&mors->mrc.timer); } static void mm81x_rc_sta_config_guard_per_bw(struct ieee80211_sta *sta, diff --git a/drivers/net/wireless/morsemicro/mm81x/yaps.c b/drivers/net/wireless/morsemicro/mm81x/yaps.c index bdadb822bf9a..e98a2a58726f 100644 --- a/drivers/net/wireless/morsemicro/mm81x/yaps.c +++ b/drivers/net/wireless/morsemicro/mm81x/yaps.c @@ -597,7 +597,7 @@ static void mm81x_yaps_q_chip_full_timer_init(struct mm81x_yaps *yaps) static void mm81x_yaps_q_chip_full_timer_finish(struct mm81x_yaps *yaps) { - timer_delete_sync_try(&yaps->chip_queue_full.timer); + timer_shutdown_sync(&yaps->chip_queue_full.timer); } int mm81x_yaps_init(struct mm81x *mors) From 574bd79955d166c00c2b1531fed591ee70b6ba04 Mon Sep 17 00:00:00 2001 From: Dmitry Gomzyakov Date: Sun, 10 May 2026 15:29:10 +0500 Subject: [PATCH 201/403] wifi: mt76: connac: add MT7991A (0x7991) to is_mt7996() The MT7991A chipset uses PCI device ID 0x7991 (MT7996_DEVICE_ID_2), but is_mt7996() only checks for 0x7990. This causes MT7991A devices to use incorrect chip-specific settings, such as: - MSDU_CNT_V2 instead of MSDU_CNT in TX descriptors - Wrong WTBL BMC size (32 instead of 64) - Incorrect prefetch depth for MCU queues Fixes: 7014fe535860 ("wifi: mt76: mt7996: add macros for pci device ids") Signed-off-by: Dmitry Gomzyakov Link: https://patch.msgid.link/20260510102911.1883849-2-kyoto1337@protonmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 2aa6078993e9..d15cce296c1e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -245,7 +245,8 @@ static inline bool is_mt798x(struct mt76_dev *dev) static inline bool is_mt7996(struct mt76_dev *dev) { - return mt76_chip(dev) == 0x7990; + u16 chip = mt76_chip(dev); + return chip == 0x7990 || chip == 0x7991; } static inline bool is_mt7992(struct mt76_dev *dev) From bda8324270b1ac91bfba1df8928e0570e29759e8 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 12 Jun 2026 12:13:31 +0800 Subject: [PATCH 202/403] wifi: mt76: mt7615: avoid waiting for mac work under the mt76 mutex mt7615_suspend() acquired the mt76 mutex and then called cancel_delayed_work_sync() on mac_work. mt7615_mac_work() acquires the same mutex via mt7615_mutex_acquire() at the top of the worker, so if mac_work is already running and blocked on the mutex, the suspend path deadlocks waiting for the work it holds the mutex against. Flush scan_work and mac_work before taking the mutex, matching the suspend paths in mt7921 and mt7925. scan_work only takes the mt76 spinlock, but moving it keeps the sequence consistent. This also keeps mac_work from running over an already suspended HIF, which the previous split (async cancel under the lock, sync cancel after release) would have allowed. Fixes: c6bf20109a3f ("mt76: mt7615: add WoW support") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260612041331.2596331-1-runyu.xiao@seu.edu.cn Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7615/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index fc619acbb40d..67f56e428d9a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -1239,11 +1239,12 @@ static int mt7615_suspend(struct ieee80211_hw *hw, cancel_delayed_work_sync(&dev->pm.ps_work); mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); + cancel_delayed_work_sync(&phy->scan_work); + cancel_delayed_work_sync(&phy->mt76->mac_work); + mt7615_mutex_acquire(dev); clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); - cancel_delayed_work_sync(&phy->scan_work); - cancel_delayed_work_sync(&phy->mt76->mac_work); set_bit(MT76_STATE_SUSPEND, &phy->mt76->state); ieee80211_iterate_active_interfaces(hw, From 047dc4dc582066266643b2617373e62f162de215 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:50:38 +0800 Subject: [PATCH 203/403] wifi: mt76: mt792x: consolidate rx interrupt masks into all_complete_mask Add all_complete_mask to irq_map rx sub-struct and use it in mt792x_irq_tasklet() and mt792x_dma_enable() to replace individual per-ring mask OR expressions. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075042.2577193-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x.h | 1 + drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 8 ++------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 70073b43af54..83c729f8bb76 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -200,6 +200,7 @@ struct mt792x_irq_map { u32 mcu_complete_mask; } tx; struct { + u32 all_complete_mask; u32 data_complete_mask; u32 wm_complete_mask; u32 wm2_complete_mask; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index fc326447c792..946ff6d58954 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -39,9 +39,7 @@ void mt792x_irq_tasklet(unsigned long data) trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); - mask |= intr & (irq_map->rx.data_complete_mask | - irq_map->rx.wm_complete_mask | - irq_map->rx.wm2_complete_mask); + mask |= intr & irq_map->rx.all_complete_mask; if (intr & dev->irq_map->tx.mcu_complete_mask) mask |= dev->irq_map->tx.mcu_complete_mask; @@ -276,9 +274,7 @@ int mt792x_dma_enable(struct mt792x_dev *dev) /* enable interrupts for TX/RX rings */ mt76_connac_irq_enable(&dev->mt76, dev->irq_map->tx.all_complete_mask | - dev->irq_map->rx.data_complete_mask | - dev->irq_map->rx.wm2_complete_mask | - dev->irq_map->rx.wm_complete_mask | + dev->irq_map->rx.all_complete_mask | MT_INT_MCU_CMD); mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE); From 83c65e82e5b2470df71d5eb5480c16ac094c4462 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:50:39 +0800 Subject: [PATCH 204/403] wifi: mt76: connac2: apply new rx all_complete_mask Populate all_complete_mask in MT7921 irq_map and update pci_resume() and mac_reset() to use irq_map->rx.all_complete_mask instead of the hardcoded MT_INT_RX_DONE_ALL macro. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075042.2577193-2-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 4 +++- drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 7728c5ae6791..764de48c87e4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -293,6 +293,7 @@ static int mt7921_pci_probe(struct pci_dev *pdev, .mcu_complete_mask = MT_INT_TX_DONE_MCU, }, .rx = { + .all_complete_mask = MT_INT_RX_DONE_ALL, .data_complete_mask = MT_INT_RX_DONE_DATA, .wm_complete_mask = MT_INT_RX_DONE_WM, .wm2_complete_mask = MT_INT_RX_DONE_WM2, @@ -560,7 +561,8 @@ static int mt7921_pci_resume(struct device *device) mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); mt76_connac_irq_enable(&dev->mt76, dev->irq_map->tx.all_complete_mask | - MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD); + dev->irq_map->rx.all_complete_mask | + MT_INT_MCU_CMD); mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE); /* put dma enabled */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c index 0db7acb3a637..98b3469ac597 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c @@ -96,7 +96,8 @@ int mt7921e_mac_reset(struct mt792x_dev *dev) mt76_wr(dev, dev->irq_map->host_irq_enable, dev->irq_map->tx.all_complete_mask | - MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD); + dev->irq_map->rx.all_complete_mask | + MT_INT_MCU_CMD); mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); err = mt7921e_driver_own(dev); From c4edf9e3e0e5a6a6e195cd35fae5d9404a559413 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:50:40 +0800 Subject: [PATCH 205/403] wifi: mt76: mt7925: replace shared rx irq masks with per-chip definitions Replace shared MT_INT_RX_DONE_* macros with chip-specific MT7925_INT_RX_DONE_{DATA,WM,WM2,ALL} and populate all_complete_mask in mt7925_irq_map. Update resume and mac_reset paths accordingly. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075042.2577193-3-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/pci.c | 33 ++++++++++--------- .../wireless/mediatek/mt76/mt7925/pci_mac.c | 4 +-- .../net/wireless/mediatek/mt76/mt7925/regs.h | 9 ++--- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index ea64303283ed..95a0bd615167 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -298,6 +298,20 @@ static int mt7925_dma_init(struct mt792x_dev *dev) return mt792x_dma_enable(dev); } +static const struct mt792x_irq_map mt7925_irq_map = { + .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, + .tx = { + .all_complete_mask = MT_INT_TX_DONE_ALL, + .mcu_complete_mask = MT_INT_TX_DONE_MCU, + }, + .rx = { + .all_complete_mask = MT7925_INT_RX_DONE_ALL, + .data_complete_mask = MT7925_INT_RX_DONE_DATA, + .wm_complete_mask = MT7925_INT_RX_DONE_WM, + .wm2_complete_mask = MT_INT_RX_DONE_WM2, + }, +}; + static const struct mt792x_irq_map mt7927_irq_map = { .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, .tx = { @@ -339,17 +353,6 @@ static int mt7925_pci_probe(struct pci_dev *pdev, .drv_own = mt792xe_mcu_drv_pmctrl, .fw_own = mt792xe_mcu_fw_pmctrl, }; - static const struct mt792x_irq_map irq_map = { - .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, - .tx = { - .all_complete_mask = MT_INT_TX_DONE_ALL, - .mcu_complete_mask = MT_INT_TX_DONE_MCU, - }, - .rx = { - .data_complete_mask = HOST_RX_DONE_INT_ENA2, - .wm_complete_mask = HOST_RX_DONE_INT_ENA0, - }, - }; struct ieee80211_ops *ops; struct mt76_bus_ops *bus_ops; struct mt792x_dev *dev; @@ -407,7 +410,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev, dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &mt7925_pcie_ops; - dev->irq_map = is_mt7927_hw ? &mt7927_irq_map : &irq_map; + dev->irq_map = is_mt7927_hw ? &mt7927_irq_map : &mt7925_irq_map; mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev); @@ -456,7 +459,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev, if (ret) goto err_free_dev; - mt76_wr(dev, irq_map.host_irq_enable, 0); + mt76_wr(dev, dev->irq_map->host_irq_enable, 0); mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); @@ -613,9 +616,7 @@ static int _mt7925_pci_resume(struct device *device, bool restore) mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); mt76_connac_irq_enable(&dev->mt76, dev->irq_map->tx.all_complete_mask | - dev->irq_map->rx.data_complete_mask | - dev->irq_map->rx.wm_complete_mask | - dev->irq_map->rx.wm2_complete_mask | + dev->irq_map->rx.all_complete_mask | MT_INT_MCU_CMD); mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c index 97683949a305..d288739e1307 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c @@ -119,9 +119,7 @@ int mt7925e_mac_reset(struct mt792x_dev *dev) mt76_wr(dev, dev->irq_map->host_irq_enable, dev->irq_map->tx.all_complete_mask | - dev->irq_map->rx.data_complete_mask | - dev->irq_map->rx.wm_complete_mask | - dev->irq_map->rx.wm2_complete_mask | + dev->irq_map->rx.all_complete_mask | MT_INT_MCU_CMD); mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h index 24985bba1b90..aed90cc82858 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h @@ -43,11 +43,12 @@ #define HOST_TX_DONE_INT_ENA17 BIT(27) /* WFDMA interrupt */ -#define MT_INT_RX_DONE_DATA HOST_RX_DONE_INT_ENA2 -#define MT_INT_RX_DONE_WM HOST_RX_DONE_INT_ENA0 #define MT_INT_RX_DONE_WM2 HOST_RX_DONE_INT_ENA1 -#define MT_INT_RX_DONE_ALL (MT_INT_RX_DONE_DATA | \ - MT_INT_RX_DONE_WM | \ + +#define MT7925_INT_RX_DONE_DATA HOST_RX_DONE_INT_ENA2 +#define MT7925_INT_RX_DONE_WM HOST_RX_DONE_INT_ENA0 +#define MT7925_INT_RX_DONE_ALL (MT7925_INT_RX_DONE_DATA | \ + MT7925_INT_RX_DONE_WM | \ MT_INT_RX_DONE_WM2) #define MT_INT_TX_DONE_MCU_WM (HOST_TX_DONE_INT_ENA15 | \ From 4590ceed74133709a6f6df4d241f152a2a0625db Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:50:41 +0800 Subject: [PATCH 206/403] wifi: mt76: mt7925: add MT7927 per-chip rx irq definitions Add MT7927_INT_RX_DONE_{DATA,WM,WM2,ALL} macros and populate all_complete_mask in mt7927_irq_map. MT7927 maps RX_DONE_DATA to ENA4 and RX_DONE_WM to ENA6, differing from MT7925. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075042.2577193-4-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 8 +++++--- drivers/net/wireless/mediatek/mt76/mt7925/regs.h | 7 +++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 95a0bd615167..8a4fb53c718f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -319,11 +319,13 @@ static const struct mt792x_irq_map mt7927_irq_map = { .mcu_complete_mask = MT_INT_TX_DONE_MCU, }, .rx = { - .data_complete_mask = MT7927_RX_DONE_INT_ENA4, - .wm_complete_mask = MT7927_RX_DONE_INT_ENA6, - .wm2_complete_mask = MT7927_RX_DONE_INT_ENA7, + .all_complete_mask = MT7927_INT_RX_DONE_ALL, + .data_complete_mask = MT7927_INT_RX_DONE_DATA, + .wm_complete_mask = MT7927_INT_RX_DONE_WM, + .wm2_complete_mask = MT7927_INT_RX_DONE_WM2, }, }; + static int mt7925_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h index aed90cc82858..bb5969689337 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h @@ -51,6 +51,13 @@ MT7925_INT_RX_DONE_WM | \ MT_INT_RX_DONE_WM2) +#define MT7927_INT_RX_DONE_DATA MT7927_RX_DONE_INT_ENA4 +#define MT7927_INT_RX_DONE_WM MT7927_RX_DONE_INT_ENA6 +#define MT7927_INT_RX_DONE_WM2 MT7927_RX_DONE_INT_ENA7 +#define MT7927_INT_RX_DONE_ALL (MT7927_INT_RX_DONE_DATA | \ + MT7927_INT_RX_DONE_WM | \ + MT7927_INT_RX_DONE_WM2) + #define MT_INT_TX_DONE_MCU_WM (HOST_TX_DONE_INT_ENA15 | \ HOST_TX_DONE_INT_ENA17) From 9ad48ff235162ddda1cfc7d65aecf3010678bfbb Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 07:50:42 +0000 Subject: [PATCH 207/403] wifi: mt76: mt792x: add per-chip PCIe register struct Add a mt792x_pcie_reg struct and a pcie_reg pointer in mt792x_dev, so that each chip can supply its own PCIe register offsets. Users are converted in the following patches. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075042.2577193-5-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 83c729f8bb76..eacee63e13f3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -238,6 +238,11 @@ struct mt792x_hif_ops { int (*fw_own)(struct mt792x_dev *dev); }; +struct mt792x_pcie_reg { + u32 imask; + u32 pm; +}; + struct mt792x_dev { union { /* must be first */ struct mt76_dev mt76; @@ -273,6 +278,7 @@ struct mt792x_dev { struct mt76_connac_coredump coredump; const struct mt792x_hif_ops *hif_ops; const struct mt792x_irq_map *irq_map; + const struct mt792x_pcie_reg *pcie_reg; struct work_struct ipv6_ns_work; struct delayed_work mlo_pm_work; From 7e8c44d06a536aabff972766d29cf58d14742189 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:51:32 +0800 Subject: [PATCH 208/403] wifi: mt76: connac2: add per-chip PCIe register definitions Add MT_PCIE_MAC_{INT_ENABLE,PM} definitions to mt7921/regs.h and wire up mt7921_pcie_reg in mt7921_pci_probe() to provide connac2 series chips with their own PCIe register definitions. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Xiong Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075136.2577553-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 5 +++++ drivers/net/wireless/mediatek/mt76/mt7921/regs.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 764de48c87e4..2f51f653975f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -286,6 +286,10 @@ static int mt7921_pci_probe(struct pci_dev *pdev, .drv_own = mt792xe_mcu_drv_pmctrl, .fw_own = mt792xe_mcu_fw_pmctrl, }; + static const struct mt792x_pcie_reg mt7921_pcie_reg = { + .imask = MT_PCIE_MAC_INT_ENABLE, + .pm = MT_PCIE_MAC_PM, + }; static const struct mt792x_irq_map irq_map = { .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, .tx = { @@ -355,6 +359,7 @@ static int mt7921_pci_probe(struct pci_dev *pdev, dev->fw_features = features; dev->hif_ops = &mt7921_pcie_ops; + dev->pcie_reg = &mt7921_pcie_reg; dev->irq_map = &irq_map; mt76_mmio_init(&dev->mt76, regs); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regs.h b/drivers/net/wireless/mediatek/mt76/mt7921/regs.h index 4d9eaf1e0692..75b4e81c7224 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regs.h @@ -78,4 +78,9 @@ #define MT_WTBL_UPDATE_WLAN_IDX GENMASK(9, 0) #define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(12) +#define MT_PCIE_MAC_BASE 0x10000 +#define MT_PCIE_MAC(ofs) (MT_PCIE_MAC_BASE + (ofs)) +#define MT_PCIE_MAC_INT_ENABLE MT_PCIE_MAC(0x188) +#define MT_PCIE_MAC_PM MT_PCIE_MAC(0x194) + #endif From 0d0205dbf33599b0762f1813de337f8f54d7cede Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:51:33 +0800 Subject: [PATCH 209/403] wifi: mt76: mt7925: add per-chip PCIe register definitions Add MT7925_PCIE_MAC_{INT_ENABLE,PM} macros and mt7925_pcie_reg struct. Update all PCIe register accesses in pci.c, pci_mac.c, and pci_mcu.c to use dev->pcie_reg->{imask,pm}. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Xiong Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075136.2577553-2-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 15 ++++++++++----- .../net/wireless/mediatek/mt76/mt7925/pci_mac.c | 4 ++-- .../net/wireless/mediatek/mt76/mt7925/pci_mcu.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7925/regs.h | 5 +++++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 8a4fb53c718f..61349c260b12 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -298,6 +298,11 @@ static int mt7925_dma_init(struct mt792x_dev *dev) return mt792x_dma_enable(dev); } +static const struct mt792x_pcie_reg mt7925_pcie_reg = { + .imask = MT7925_PCIE_MAC_INT_ENABLE, + .pm = MT7925_PCIE_MAC_PM, +}; + static const struct mt792x_irq_map mt7925_irq_map = { .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, .tx = { @@ -413,6 +418,8 @@ static int mt7925_pci_probe(struct pci_dev *pdev, dev->fw_features = features; dev->hif_ops = &mt7925_pcie_ops; dev->irq_map = is_mt7927_hw ? &mt7927_irq_map : &mt7925_irq_map; + dev->pcie_reg = &mt7925_pcie_reg; + mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev); @@ -462,8 +469,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev, goto err_free_dev; mt76_wr(dev, dev->irq_map->host_irq_enable, 0); - - mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + mt76_wr(dev, dev->pcie_reg->imask, 0xff); ret = devm_request_irq(mdev->dev, pdev->irq, mt792x_irq_handler, IRQF_SHARED, KBUILD_MODNAME, dev); @@ -564,8 +570,7 @@ static int mt7925_pci_suspend(struct device *device) /* disable interrupt */ mt76_wr(dev, dev->irq_map->host_irq_enable, 0); - - mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0); + mt76_wr(dev, dev->pcie_reg->imask, 0x0); synchronize_irq(pdev->irq); tasklet_kill(&mdev->irq_tasklet); @@ -615,7 +620,7 @@ static int _mt7925_pci_resume(struct device *device, bool restore) mt792x_wpdma_reinit_cond(dev); /* enable interrupt */ - mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + mt76_wr(dev, dev->pcie_reg->imask, 0xff); mt76_connac_irq_enable(&dev->mt76, dev->irq_map->tx.all_complete_mask | dev->irq_map->rx.all_complete_mask | diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c index d288739e1307..8477d21abc66 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c @@ -78,7 +78,7 @@ int mt7925e_mac_reset(struct mt792x_dev *dev) mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); mt76_wr(dev, dev->irq_map->host_irq_enable, 0); - mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0); + mt76_wr(dev, dev->pcie_reg->imask, 0x0); set_bit(MT76_RESET, &dev->mphy.state); set_bit(MT76_MCU_RESET, &dev->mphy.state); @@ -121,7 +121,7 @@ int mt7925e_mac_reset(struct mt792x_dev *dev) dev->irq_map->tx.all_complete_mask | dev->irq_map->rx.all_complete_mask | MT_INT_MCU_CMD); - mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + mt76_wr(dev, dev->pcie_reg->imask, 0xff); err = mt792xe_mcu_fw_pmctrl(dev); if (err) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c index 6cceff88c656..72707eddc3db 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c @@ -43,7 +43,7 @@ int mt7925e_mcu_init(struct mt792x_dev *dev) if (err) return err; - mt76_rmw_field(dev, MT_PCIE_MAC_PM, MT_PCIE_MAC_PM_L0S_DIS, 1); + mt76_rmw_field(dev, dev->pcie_reg->pm, MT_PCIE_MAC_PM_L0S_DIS, 1); err = mt7925_run_firmware(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h index bb5969689337..85adde2ad597 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h @@ -97,4 +97,9 @@ #define MT_WTBL_UPDATE_WLAN_IDX GENMASK(11, 0) #define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14) +#define MT7925_PCIE_MAC_BASE 0x10000 +#define MT7925_PCIE_MAC(ofs) (MT7925_PCIE_MAC_BASE + (ofs)) +#define MT7925_PCIE_MAC_INT_ENABLE MT7925_PCIE_MAC(0x188) +#define MT7925_PCIE_MAC_PM MT7925_PCIE_MAC(0x194) + #endif From d63b19ece3e4bbc4ee2f6d800eb8d6c1ca171781 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 07:50:42 +0000 Subject: [PATCH 210/403] wifi: mt76: mt792x: replace shared PCIe MAC macros with per-chip struct Update mt792x_wpdma_reinit_cond() to use dev->pcie_reg and remove the now unused MT_PCIE_MAC_INT_ENABLE and MT_PCIE_MAC_PM macros from mt792x_regs.h. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075042.2577193-5-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 6 ++++-- drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 4 ---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index 946ff6d58954..2947504ef20b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -345,7 +345,8 @@ int mt792x_wpdma_reinit_cond(struct mt792x_dev *dev) if (mt792x_dma_need_reinit(dev)) { /* disable interrutpts */ mt76_wr(dev, dev->irq_map->host_irq_enable, 0); - mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0); + if (dev->pcie_reg) + mt76_wr(dev, dev->pcie_reg->imask, 0x0); err = mt792x_wpdma_reset(dev, false); if (err) { @@ -354,7 +355,8 @@ int mt792x_wpdma_reinit_cond(struct mt792x_dev *dev) } /* enable interrutpts */ - mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + if (dev->pcie_reg) + mt76_wr(dev, dev->pcie_reg->imask, 0xff); pm->stats.lp_wake++; } diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h index 4cd5b33b640e..6d174b158915 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h @@ -415,10 +415,6 @@ #define MT_HW_EMI_CTL 0x18011100 #define MT_HW_EMI_CTL_SLPPROT_EN BIT(1) -#define MT_PCIE_MAC_BASE 0x10000 -#define MT_PCIE_MAC(ofs) (MT_PCIE_MAC_BASE + (ofs)) -#define MT_PCIE_MAC_INT_ENABLE MT_PCIE_MAC(0x188) -#define MT_PCIE_MAC_PM MT_PCIE_MAC(0x194) #define MT_PCIE_MAC_PM_L0S_DIS BIT(8) #define MT_DMA_SHDL(ofs) (0x7c026000 + (ofs)) From 50a16805a95e3101740b3be218b3d6c209e08b00 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:51:34 +0800 Subject: [PATCH 211/403] wifi: mt76: mt792x: rename WFDMA DMASHDL enable bit to follow the convention Rename MT_WFDMA0_CSR_TX_DMASHDL_ENABLE to MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_EN to follow the register naming convention (parent register name as prefix). This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075136.2577553-3-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 2 +- drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index 2947504ef20b..b090ba9cd676 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -386,7 +386,7 @@ int mt792x_dma_disable(struct mt792x_dev *dev, bool force) /* disable dmashdl */ mt76_clear(dev, MT_WFDMA0_GLO_CFG_EXT0, - MT_WFDMA0_CSR_TX_DMASHDL_ENABLE); + MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_EN); mt76_set(dev, MT_DMASHDL_SW_CONTROL, MT_DMASHDL_DMASHDL_BYPASS); if (force) { diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h index 6d174b158915..0e297fd9468a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h @@ -335,7 +335,7 @@ #define MT_WFDMA0_INT_RX_PRI MT_WFDMA0(0x298) #define MT_WFDMA0_INT_TX_PRI MT_WFDMA0(0x29c) #define MT_WFDMA0_GLO_CFG_EXT0 MT_WFDMA0(0x2b0) -#define MT_WFDMA0_CSR_TX_DMASHDL_ENABLE BIT(6) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_EN BIT(6) #define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0) #define MT_WFDMA0_TX_RING0_EXT_CTRL MT_WFDMA0(0x600) From 3422e61141e79d5edd51f27b12d146e631124960 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:51:35 +0800 Subject: [PATCH 212/403] wifi: mt76: mt7925: rename WTBL registers to chip-specific format Rename MT_WTBLON_TOP_WDUCR and MT_WTBL_UPDATE to MT7925-prefixed versions since MT7928 uses different WTBL register offsets. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075136.2577553-4-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 4 ++-- drivers/net/wireless/mediatek/mt76/mt7925/mac.h | 2 +- drivers/net/wireless/mediatek/mt76/mt7925/regs.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 6b0cd1996ecb..778a0c68d98e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -12,10 +12,10 @@ bool mt7925_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask) { - mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, + mt76_rmw(dev, MT7925_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); - return mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, + return mt76_poll(dev, MT7925_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.h b/drivers/net/wireless/mediatek/mt76/mt7925/mac.h index 83ea9021daea..67148c87de76 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.h @@ -14,7 +14,7 @@ static inline u32 mt7925_mac_wtbl_lmac_addr(struct mt792x_dev *dev, u16 wcid, u8 dw) { - mt76_wr(dev, MT_WTBLON_TOP_WDUCR, + mt76_wr(dev, MT7925_WTBLON_TOP_WDUCR, FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7))); return MT_WTBL_LMAC_OFFS(wcid, dw); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h index 85adde2ad597..0bcfd1cf0338 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h @@ -90,10 +90,10 @@ #define MT_WFSYS_SW_RST_B 0x7c000140 -#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x370) +#define MT7925_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x370) #define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0) -#define MT_WTBL_UPDATE MT_WTBLON_TOP(0x380) +#define MT7925_WTBL_UPDATE MT_WTBLON_TOP(0x380) #define MT_WTBL_UPDATE_WLAN_IDX GENMASK(11, 0) #define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14) From 78ff45f849a7c24b96adbb38c901d84491e650e9 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:51:36 +0800 Subject: [PATCH 213/403] wifi: mt76: mt792x: add tx_done ring to common DMA queue allocation Add a tx_done field to mt792x_dma_layout and extend mt792x_dma_alloc_queues() to allocate the MT_RXQ_MCU_WA queue when tx_done.ring_base is configured. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075136.2577553-5-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x.h | 1 + drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index eacee63e13f3..b8dc7ce38c87 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -217,6 +217,7 @@ struct mt792x_dma_layout { struct mt792x_dma_ring tx_data0; struct mt792x_dma_ring tx_mcu; struct mt792x_dma_ring tx_fwdl; + struct mt792x_dma_ring tx_done; struct mt792x_dma_ring rx_data; struct mt792x_dma_ring rx_mcu; }; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index b090ba9cd676..4d4c62bb0a77 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -122,6 +122,17 @@ int mt792x_dma_alloc_queues(struct mt792x_dev *dev, if (ret) return ret; + /* tx done */ + if (layout->tx_done.ring_base) { + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], + layout->tx_done.qid, + layout->tx_done.n_desc, + MT_RX_BUF_SIZE, + layout->tx_done.ring_base); + if (ret) + return ret; + } + /* rx event */ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], layout->rx_mcu.qid, From e058739cbf846160678dc878aadc2bb14a2af83d Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:53:09 +0800 Subject: [PATCH 214/403] wifi: mt76: connac3: update basic rate table starting index Change MT792x_BASIC_RATES_TBL index from 11 to 14 to match the latest connac3 firmware rate table layout. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075313.2578154-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index b8dc7ce38c87..7d014de030c4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -33,7 +33,7 @@ #define MT792x_CHIP_CAP_MLO_EML_EN BIT(9) /* NOTE: used to map mt76_rates. idx may change if firmware expands table */ -#define MT792x_BASIC_RATES_TBL 11 +#define MT792x_BASIC_RATES_TBL 14 #define MT792x_WATCHDOG_TIME (HZ / 4) From e34e157e6e7cd048f764f2cc752dd4068031ddb1 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:53:10 +0800 Subject: [PATCH 215/403] wifi: mt76: mt7925: fix MMIO dynamic remap window size Fix the 0x7c500000 remap entry window size from 0x2000000 (32MB) to 0x200000 (2MB) to match the actual addressable range. This is a preparation patch before enabling MT7928 PCIe support. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075313.2578154-2-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 61349c260b12..e181cd0b6403 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -161,7 +161,7 @@ static u32 __mt7925_reg_addr(struct mt792x_dev *dev, u32 addr) { 0x7c060000, 0x0e0000, 0x0010000 }, /* CONN_INFRA, conn_host_csr_top */ { 0x7c000000, 0x0f0000, 0x0010000 }, /* CONN_INFRA */ { 0x70020000, 0x1f0000, 0x0010000 }, /* Reserved for CBTOP, can't switch */ - { 0x7c500000, 0x060000, 0x2000000 }, /* remap */ + { 0x7c500000, 0x060000, 0x200000 }, /* remap */ { 0x0, 0x0, 0x0 } /* End */ }; int i; From 4f8ded4d5f0e1b1707e0e439666e314c92df9d8f Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:53:11 +0800 Subject: [PATCH 216/403] wifi: mt76: mt7925: add MT7928 FWDL support Add CBMCU and PHY RAM firmware download flow for MT7928. The CBMCU firmware is loaded in sections before the main WM firmware. Register MT7928 firmware file names and is_mt7928() chip check. Signed-off-by: FC Wei Signed-off-by: Xiong Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075313.2578154-3-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt76_connac.h | 7 +- .../wireless/mediatek/mt76/mt76_connac_mcu.c | 251 +++++++++++++++++- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 40 +++ .../net/wireless/mediatek/mt76/mt7925/mcu.c | 15 +- drivers/net/wireless/mediatek/mt76/mt792x.h | 29 ++ .../net/wireless/mediatek/mt76/mt792x_core.c | 15 ++ 6 files changed, 353 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index d15cce296c1e..019d0275dc5c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -174,7 +174,7 @@ extern const struct wiphy_wowlan_support mt76_connac_wowlan_support; static inline bool is_connac3(struct mt76_dev *dev) { - return mt76_chip(dev) == 0x7925 || mt76_chip(dev) == 0x7927; + return mt76_chip(dev) == 0x7925 || mt76_chip(dev) == 0x7927 || mt76_chip(dev) == 0x7928; } static inline bool is_mt7925(struct mt76_dev *dev) @@ -187,6 +187,11 @@ static inline bool is_mt7927(struct mt76_dev *dev) return mt76_chip(dev) == 0x7927; } +static inline bool is_mt7928(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7928; +} + static inline bool is_320mhz_supported(struct mt76_dev *dev) { return mt76_chip(dev) == 0x7927; diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 58b0b15e4fd6..2cc5a5b67f30 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -67,7 +67,8 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) || (is_connac2(dev) && addr == 0x900000) || - (is_connac3(dev) && (addr == 0x900000 || addr == 0xe0002800)) || + ((is_mt7925(dev) || is_mt7927(dev)) && (addr == 0x900000 || addr == 0xe0002800)) || + (is_mt7928(dev) && (addr == 0x900000 || addr == 0xe0002000)) || (is_mt799x(dev) && addr == 0x900000)) cmd = MCU_CMD(PATCH_START_REQ); else @@ -77,6 +78,48 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, } EXPORT_SYMBOL_GPL(mt76_connac_mcu_init_download); +int mt76_connac_cb_mcu_patch_sem_ctrl(struct mt76_dev *dev, bool get) +{ + u8 op = get ? PATCH_SEM_GET : PATCH_SEM_RELEASE; + struct { + u8 op; + u8 reserved[3]; + } req = { + .op = op, + }; + + return mt76_mcu_send_msg(dev, MCU_CMD(CB_PATCH_SEM_CONTROL), + &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_cb_mcu_patch_sem_ctrl); + +int mt76_connac_cb_mcu_start_patch(struct mt76_dev *dev) +{ + struct { + __le32 reserved; + } req = { + .reserved = 0, + }; + + return mt76_mcu_send_msg(dev, MCU_CMD(CB_PATCH_FINISH_REQ), + &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_cb_mcu_start_patch); + +int mt76_connac_cb_mcu_init_download(struct mt76_dev *dev, u32 len) +{ + struct { + __le32 addr; + __le32 len; + } req = { + .addr = 0, /* addr is meaningless for cbmcu fwdl */ + .len = cpu_to_le32(len), + }; + + return mt76_mcu_send_msg(dev, MCU_CMD(CB_PATCH_START_REQ), &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_cb_mcu_init_download); + int mt76_connac_mcu_set_channel_domain(struct mt76_phy *phy) { int len, i, n_max_channels, n_2ch = 0, n_5ch = 0, n_6ch = 0; @@ -3044,6 +3087,57 @@ mt76_connac_mcu_send_ram_firmware(struct mt76_dev *dev, return mt76_connac_mcu_start_firmware(dev, override, option); } +static int +mt76_connac_mcu_send_phy_ram_firmware(struct mt76_dev *dev, + const struct mt76_connac2_fw_trailer *hdr, + const u8 *data) +{ + int i, offset = 0, max_len = mt76_is_sdio(dev) ? 2048 : 4096; + u32 override = 0, option = 0; + + for (i = 0; i < hdr->n_region; i++) { + const struct mt76_connac2_fw_region *region; + u32 len, addr, mode; + int err; + + region = (const void *)((const u8 *)hdr - + (hdr->n_region - i) * sizeof(*region)); + mode = mt76_connac_mcu_gen_dl_mode(dev, region->feature_set, + true); + len = le32_to_cpu(region->len); + addr = le32_to_cpu(region->addr); + + if (region->feature_set & FW_FEATURE_NON_DL) + goto next; + + if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR) + override = addr; + + err = mt76_connac_mcu_init_download(dev, addr, len, mode); + if (err) { + dev_err(dev->dev, + "The request to dowload PHY firmware failed.\n"); + return err; + } + + err = __mt76_mcu_send_firmware(dev, MCU_CMD(FW_SCATTER), + data + offset, len, max_len); + if (err) { + dev_err(dev->dev, "Failed to send PHY firmware.\n"); + return err; + } + +next: + offset += len; + } + + if (override) + option |= FW_START_OVERRIDE; + option |= FW_START_WORKING_PDA_DSP; + + return mt76_connac_mcu_start_firmware(dev, override, option); +} + int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm, const char *fw_wa) { @@ -3111,6 +3205,39 @@ int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm, } EXPORT_SYMBOL_GPL(mt76_connac2_load_ram); +int mt76_connac3_load_phy_ram(struct mt76_dev *dev, const char *fw_name) +{ + const struct mt76_connac2_fw_trailer *hdr; + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, fw_name, dev->dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->dev, "Invalid PHY firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); + dev_info(dev->dev, "PHY Firmware Version: %.10s, Build Time: %.15s\n", + hdr->fw_ver, hdr->build_date); + + ret = mt76_connac_mcu_send_phy_ram_firmware(dev, hdr, fw->data); + if (ret) { + dev_err(dev->dev, "Failed to start PHY firmware\n"); + goto out; + } + +out: + release_firmware(fw); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_connac3_load_phy_ram); + static u32 mt76_connac2_get_data_mode(struct mt76_dev *dev, u32 info) { u32 mode = DL_MODE_NEED_RSP; @@ -3224,6 +3351,128 @@ int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name) } EXPORT_SYMBOL_GPL(mt76_connac2_load_patch); +int mt76_connac3_load_cb_patch(struct mt76_dev *dev, const char *fw_name) +{ + const struct mt76_connac3_multi_header_v2_sec_raw_format *sect = NULL; + const struct mt76_connac3_multi_header_v2_raw_format *hdr = NULL; + int i, ret, sem, max_len = mt76_is_sdio(dev) ? 2048 : 4096; + const struct firmware *fw = NULL; + const u8 *dl; + u32 len; + + sem = mt76_connac_cb_mcu_patch_sem_ctrl(dev, true); + switch (sem) { + case CB_PATCH_IS_DL: + return 0; + case CB_PATCH_GET_SEM_NEED_PATCH: + break; + default: + dev_err(dev->dev, "Failed to get cb patch semaphore %u\n", sem); + return -EAGAIN; + } + + ret = request_firmware(&fw, fw_name, dev->dev); + if (ret) + goto out; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const void *)fw->data; + dev_info(dev->dev, "HW/SW Version: 0x%x, Pack Time: %.21s\n", + le32_to_cpu(hdr->patch_ver), hdr->pack_time); + + if (le32_to_cpu(hdr->sec_num) < 2) { + dev_err(dev->dev, "Invalid section numbers %u\n", + le32_to_cpu(hdr->sec_num)); + ret = -EINVAL; + goto out; + } + + sect = &hdr->sects[0]; + /* check the fw bin basic info */ + if (((le32_to_cpu(sect->type) & SEC_TYPE_SUBSYS_MASK) + != SEC_TYPE_SUBSYS_CBMCU) || + ((le32_to_cpu(sect->type) & SEC_TYPE_SUBSYS_SEC_MASK) + != SEC_TYPE_SUBSYS_SEC_IMG_SIGN)) { + dev_err(dev->dev, "Invalid CBMCU firmware\n"); + ret = -EINVAL; + goto out; + } + + /* download global desc + sect map + cert section */ + len = sizeof(struct mt76_connac3_multi_header_v2_raw_format) - + offsetof(struct mt76_connac3_multi_header_v2_raw_format, + global_desc_head) + + sizeof(struct mt76_connac3_multi_header_v2_sec_raw_format) * + le32_to_cpu(hdr->sec_num) + + le32_to_cpu(hdr->sects[0].size); + dl = &hdr->global_desc_head[0]; + + ret = mt76_connac_cb_mcu_init_download(dev, len); + if (ret) { + dev_err(dev->dev, "Download cb cert section request failed\n"); + goto out; + } + + ret = __mt76_mcu_send_firmware(dev, MCU_CMD(FW_SCATTER), + dl, len, max_len); + if (ret) { + dev_err(dev->dev, "Failed to send cb patch cert section\n"); + goto out; + } + + ret = mt76_connac_cb_mcu_start_patch(dev); + if (ret) { + dev_err(dev->dev, "CBMCU cert check fail\n"); + goto out; + } + + /* download cbmcu idlm */ + for (i = 1; i < le32_to_cpu(hdr->sec_num); i++) { + dl = (uint8_t *)hdr + le32_to_cpu(hdr->sects[i].offset); + len = le32_to_cpu(hdr->sects[i].size); + + ret = mt76_connac_cb_mcu_init_download(dev, len); + if (ret) { + dev_err(dev->dev, + "Download cb section %u request failed\n", i); + goto out; + } + + ret = __mt76_mcu_send_firmware(dev, MCU_CMD(FW_SCATTER), + dl, len, max_len); + if (ret) { + dev_err(dev->dev, + "Failed to send cb patch section %u\n", i); + goto out; + } + } + + ret = mt76_connac_cb_mcu_start_patch(dev); + if (ret) + dev_err(dev->dev, "Failed to start cb patch\n"); + +out: + sem = mt76_connac_cb_mcu_patch_sem_ctrl(dev, false); + switch (sem) { + case CB_PATCH_REL_SEM_SUCCESS: + break; + default: + ret = -EAGAIN; + dev_err(dev->dev, "Failed to release cb patch semaphore\n"); + break; + } + + release_firmware(fw); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_connac3_load_cb_patch); + int mt76_connac2_mcu_fill_message(struct mt76_dev *dev, struct sk_buff *skb, int cmd, int *wait_seq) { diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 78f633ad81a0..6f554d78da39 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -35,6 +35,12 @@ #define PATCH_SEC_ENC_SCRAMBLE_INFO_MASK GENMASK(15, 0) #define PATCH_SEC_ENC_AES_KEY_MASK GENMASK(7, 0) +#define SEC_TYPE_SUBSYS_MASK GENMASK(23, 16) +#define SEC_TYPE_SUBSYS_CBMCU BIT(16) + +#define SEC_TYPE_SUBSYS_SEC_MASK GENMASK(15, 0) +#define SEC_TYPE_SUBSYS_SEC_IMG_SIGN 0x05 + enum { FW_TYPE_DEFAULT = 0, FW_TYPE_CLC = 2, @@ -193,6 +199,25 @@ struct mt76_connac2_fw_region { u8 rsv1[14]; } __packed; +struct mt76_connac3_multi_header_v2_sec_raw_format { + __le32 type; + __le32 offset; + __le32 size; + u8 spec[52]; +} __packed; + +struct mt76_connac3_multi_header_v2_raw_format { + u8 pack_time[20]; + u8 chip_id_eco_ver[8]; + __le32 patch_ver; + u8 global_desc_head[4]; + __le32 global_subsys; + u8 rsv2[4]; + __le32 sec_num; + u8 rsv3[48]; + struct mt76_connac3_multi_header_v2_sec_raw_format sects[]; +} __packed; + struct tlv { __le16 tag; __le16 len; @@ -1104,6 +1129,13 @@ enum { PATCH_REL_SEM_SUCCESS }; +enum { + CB_PATCH_GET_SEM_NEED_PATCH, + CB_PATCH_IS_DL, + CB_PATCH_NO_SEM_NEED_PATCH, + CB_PATCH_REL_SEM_SUCCESS +}; + enum { FW_STATE_INITIAL, FW_STATE_FW_DOWNLOAD, @@ -1332,6 +1364,9 @@ enum { MCU_CMD_PATCH_START_REQ = 0x05, MCU_CMD_PATCH_FINISH_REQ = 0x07, MCU_CMD_PATCH_SEM_CONTROL = 0x10, + MCU_CMD_CB_PATCH_SEM_CONTROL = 0x30, + MCU_CMD_CB_PATCH_START_REQ = 0x31, + MCU_CMD_CB_PATCH_FINISH_REQ = 0x32, MCU_CMD_WA_PARAM = 0xc4, MCU_CMD_EXT_CID = 0xed, MCU_CMD_FW_SCATTER = 0xee, @@ -2019,6 +2054,9 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, u32 mode); int mt76_connac_mcu_start_patch(struct mt76_dev *dev); int mt76_connac_mcu_patch_sem_ctrl(struct mt76_dev *dev, bool get); +int mt76_connac_cb_mcu_init_download(struct mt76_dev *dev, u32 len); +int mt76_connac_cb_mcu_start_patch(struct mt76_dev *dev); +int mt76_connac_cb_mcu_patch_sem_ctrl(struct mt76_dev *dev, bool get); int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option); void mt76_connac_mcu_build_rnr_scan_param(struct mt76_dev *mdev, @@ -2102,7 +2140,9 @@ int mt76_connac_mcu_rdd_cmd(struct mt76_dev *dev, int cmd, u8 index, int mt76_connac_mcu_sta_wed_update(struct mt76_dev *dev, struct sk_buff *skb); int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm, const char *fw_wa); +int mt76_connac3_load_phy_ram(struct mt76_dev *dev, const char *fw_name); int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name); +int mt76_connac3_load_cb_patch(struct mt76_dev *dev, const char *fw_name); int mt76_connac2_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb, int cmd, int *wait_seq); #endif /* __MT76_CONNAC_MCU_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index cb265a6fc7ad..5602544d3853 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -26,13 +26,24 @@ int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd, } rxd = (struct mt7925_mcu_rxd *)skb->data; - if (seq != rxd->seq) - return -EAGAIN; + if (seq != rxd->seq) { + if (!is_mt7928(mdev)) + return -EAGAIN; + else if (is_mt7928(mdev) && + cmd != MCU_CMD(CB_PATCH_SEM_CONTROL) && + cmd != MCU_CMD(CB_PATCH_FINISH_REQ)) + return -EAGAIN; + } if (cmd == MCU_CMD(PATCH_SEM_CONTROL) || cmd == MCU_CMD(PATCH_FINISH_REQ)) { skb_pull(skb, sizeof(*rxd) - 4); ret = *skb->data; + } else if (is_mt7928(mdev) && + (cmd == MCU_CMD(CB_PATCH_SEM_CONTROL) || + cmd == MCU_CMD(CB_PATCH_FINISH_REQ))) { + skb_pull(skb, sizeof(*rxd) - 4); + ret = *skb->data; } else if (cmd == MCU_UNI_CMD(DEV_INFO_UPDATE) || cmd == MCU_UNI_CMD(BSS_INFO_UPDATE) || cmd == MCU_UNI_CMD(STA_REC_UPDATE) || diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 7d014de030c4..8c7ecd3ce126 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -47,6 +47,7 @@ #define MT7922_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7922_1.bin" #define MT7925_FIRMWARE_WM "mediatek/mt7925/WIFI_RAM_CODE_MT7925_1_1.bin" #define MT7927_FIRMWARE_WM "mediatek/mt7927/WIFI_RAM_CODE_MT6639_2_1.bin" +#define MT7928_FIRMWARE_WM "mediatek/mt7928/WIFI_RAM_CODE_MT7935_1_1.bin" #define MT7902_ROM_PATCH "mediatek/WIFI_MT7902_patch_mcu_1_1_hdr.bin" #define MT7920_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1a_2_hdr.bin" @@ -54,6 +55,10 @@ #define MT7922_ROM_PATCH "mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin" #define MT7925_ROM_PATCH "mediatek/mt7925/WIFI_MT7925_PATCH_MCU_1_1_hdr.bin" #define MT7927_ROM_PATCH "mediatek/mt7927/WIFI_MT6639_PATCH_MCU_2_1_hdr.bin" +#define MT7928_ROM_PATCH "mediatek/mt7928/WIFI_MT7935_PATCH_MCU_1_1_hdr.bin" + +#define MT7928_CB_ROM_PATCH "mediatek/mt7928/CBMCU_CODE_MT7935_1_1.bin" +#define MT7928_PHY_RAM "mediatek/mt7928/WIFI_MT7935_PHY_RAM_CODE_1_1.bin" #define MT792x_SDIO_HDR_TX_BYTES GENMASK(15, 0) #define MT792x_SDIO_HDR_PKT_TYPE GENMASK(17, 16) @@ -494,6 +499,8 @@ static inline char *mt792x_ram_name(struct mt792x_dev *dev) return MT7925_FIRMWARE_WM; case 0x7927: return MT7927_FIRMWARE_WM; + case 0x7928: + return MT7928_FIRMWARE_WM; default: return MT7921_FIRMWARE_WM; } @@ -512,11 +519,33 @@ static inline char *mt792x_patch_name(struct mt792x_dev *dev) return MT7925_ROM_PATCH; case 0x7927: return MT7927_ROM_PATCH; + case 0x7928: + return MT7928_ROM_PATCH; default: return MT7921_ROM_PATCH; } } +static inline char *mt792x_cb_patch_name(struct mt792x_dev *dev) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7928: + return MT7928_CB_ROM_PATCH; + default: + return NULL; + } +} + +static inline char *mt792x_phy_ram_name(struct mt792x_dev *dev) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7928: + return MT7928_PHY_RAM; + default: + return NULL; + } +} + int mt792x_load_firmware(struct mt792x_dev *dev); /* usb */ diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index b50825eccdaf..9bd679b7e889 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -985,6 +985,15 @@ int mt792x_load_firmware(struct mt792x_dev *dev) dev_warn(dev->mt76.dev, "MCU is not ready for firmware download\n"); + if (is_mt7928(&dev->mt76)) { + dev_info(dev->mt76.dev, "Loading CB firmware patch: %s\n", + mt792x_cb_patch_name(dev)); + ret = mt76_connac3_load_cb_patch(&dev->mt76, mt792x_cb_patch_name(dev)); + if (ret) + return ret; + } + + dev_info(dev->mt76.dev, "Loading firmware patch: %s\n", mt792x_patch_name(dev)); ret = mt76_connac2_load_patch(&dev->mt76, mt792x_patch_name(dev)); if (ret) return ret; @@ -996,6 +1005,12 @@ int mt792x_load_firmware(struct mt792x_dev *dev) ret = __mt792x_mcu_drv_pmctrl(dev); } + if (is_mt7928(&dev->mt76)) { + ret = mt76_connac3_load_phy_ram(&dev->mt76, mt792x_phy_ram_name(dev)); + if (ret) + return ret; + } + ret = mt76_connac2_load_ram(&dev->mt76, mt792x_ram_name(dev), NULL); if (ret) return ret; From d92d0b0c06e3120a3786b5109d163307f90e97c5 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:53:12 +0800 Subject: [PATCH 217/403] wifi: mt76: mt7925: add MT7928 irq_map with chip-specific rx masks MT7928 uses different RX interrupt bit assignments (RX_DONE_DATA on ENA0, RX_DONE_WM on ENA3). Add MT7928-specific irq_map and select it at probe time based on PCI device ID. Signed-off-by: FC Wei Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075313.2578154-4-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/pci.c | 27 ++++++++++++++++--- .../net/wireless/mediatek/mt76/mt7925/regs.h | 6 +++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index e181cd0b6403..6a65c630f85a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -331,6 +331,20 @@ static const struct mt792x_irq_map mt7927_irq_map = { }, }; +static const struct mt792x_irq_map mt7928_irq_map = { + .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, + .tx = { + .all_complete_mask = MT_INT_TX_DONE_ALL, + .mcu_complete_mask = MT_INT_TX_DONE_MCU, + }, + .rx = { + .all_complete_mask = MT7928_INT_RX_DONE_ALL, + .data_complete_mask = MT7928_INT_RX_DONE_DATA, + .wm_complete_mask = MT7928_INT_RX_DONE_WM, + .wm2_complete_mask = MT_INT_RX_DONE_WM2, + }, +}; + static int mt7925_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -360,11 +374,11 @@ static int mt7925_pci_probe(struct pci_dev *pdev, .drv_own = mt792xe_mcu_drv_pmctrl, .fw_own = mt792xe_mcu_fw_pmctrl, }; - struct ieee80211_ops *ops; + bool is_mt7927_hw, is_mt7928_hw; struct mt76_bus_ops *bus_ops; + struct ieee80211_ops *ops; struct mt792x_dev *dev; struct mt76_dev *mdev; - bool is_mt7927_hw; u8 features; int ret; u16 cmd; @@ -394,6 +408,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev, is_mt7927_hw = (pdev->device == 0x6639 || pdev->device == 0x7927 || pdev->device == 0x0738); + is_mt7928_hw = (pdev->device == 0x7928 || pdev->device == 0x7935); /* MT7927: ASPM L1 causes unreliable WFDMA register access */ if (mt7925_disable_aspm || is_mt7927_hw) @@ -417,9 +432,15 @@ static int mt7925_pci_probe(struct pci_dev *pdev, dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &mt7925_pcie_ops; - dev->irq_map = is_mt7927_hw ? &mt7927_irq_map : &mt7925_irq_map; dev->pcie_reg = &mt7925_pcie_reg; + if (is_mt7928_hw) + dev->irq_map = &mt7928_irq_map; + else if (is_mt7927_hw) + dev->irq_map = &mt7927_irq_map; + else + dev->irq_map = &mt7925_irq_map; + mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h index 0bcfd1cf0338..855a53c0748a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h @@ -58,6 +58,12 @@ MT7927_INT_RX_DONE_WM | \ MT7927_INT_RX_DONE_WM2) +#define MT7928_INT_RX_DONE_DATA HOST_RX_DONE_INT_ENA0 +#define MT7928_INT_RX_DONE_WM HOST_RX_DONE_INT_ENA3 +#define MT7928_INT_RX_DONE_ALL (MT7928_INT_RX_DONE_DATA | \ + MT7928_INT_RX_DONE_WM | \ + MT_INT_RX_DONE_WM2) + #define MT_INT_TX_DONE_MCU_WM (HOST_TX_DONE_INT_ENA15 | \ HOST_TX_DONE_INT_ENA17) From a01a222ab4aa80ef201b9a2893134e7533d62348 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:53:13 +0800 Subject: [PATCH 218/403] wifi: mt76: mt7925: add MT7928 DMA configuration Add MT7928 DMA queue layout, DMASHDL configuration, prefetch ring setup, and WFDMA interrupt priority initialization. Select the MT7928-specific layout and GLO_CFG path in mt7925_dma_init(). Signed-off-by: FC Wei Signed-off-by: Xiong Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075313.2578154-5-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/mcu.h | 21 +++++ .../wireless/mediatek/mt76/mt7925/mt7925.h | 8 ++ .../net/wireless/mediatek/mt76/mt7925/pci.c | 91 ++++++++++++++++-- .../net/wireless/mediatek/mt76/mt7925/regs.h | 21 +++++ .../net/wireless/mediatek/mt76/mt792x_dma.c | 94 ++++++++++++++++--- .../net/wireless/mediatek/mt76/mt792x_regs.h | 19 +++- 6 files changed, 233 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h index 293f173b23dd..1613c4765186 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h @@ -175,6 +175,27 @@ enum connac3_mcu_cipher_type { CONNAC3_CIPHER_GCMP_256 = 12, }; +enum DMASHDL_GROUP_IDX { + DMASHDL_GROUP_0 = 0, + DMASHDL_GROUP_1, + DMASHDL_GROUP_2, + DMASHDL_GROUP_3, + DMASHDL_GROUP_4, + DMASHDL_GROUP_5, + DMASHDL_GROUP_6, + DMASHDL_GROUP_7, + DMASHDL_GROUP_8, + DMASHDL_GROUP_9, + DMASHDL_GROUP_10, + DMASHDL_GROUP_11, + DMASHDL_GROUP_12, + DMASHDL_GROUP_13, + DMASHDL_GROUP_14, + DMASHDL_GROUP_15, + DMASHDL_GROUP_NUM, + DMASHDL_LITE_GROUP_NUM = 64 +}; + struct mt7925_mcu_scan_chinfo_event { u8 nr_chan; u8 alpha2[3]; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index 4cc259418afc..a5414fa2736f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -15,6 +15,7 @@ #define MT7925_RX_RING_SIZE 1536 #define MT7925_RX_MCU_RING_SIZE 512 +#define MT7928_RX_MCU_WA_RING_SIZE 512 #define MT7925_EEPROM_SIZE 3584 #define MT7925_TOKEN_SIZE 8192 @@ -139,6 +140,13 @@ enum mt7927_rxq_id { MT7927_RXQ_DATA2 = 7, }; +enum mt7928_rxq_id { + MT7928_RXQ_BAND0, + MT7928_RXQ_BAND1 = 2, + MT7928_RXQ_MCU_WM = 3, + MT7928_RXQ_MCU_WM2 = 1, /* for tx done */ +}; + enum { MODE_OPEN = 0, MODE_SHARED = 1, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 6a65c630f85a..f79d4143e38b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -252,6 +252,27 @@ static const struct mt792x_dma_layout mt7927_dma_layout = { MT_RX_DATA_RING_BASE), }; +static const struct mt792x_dma_layout mt7928_dma_layout = { + .tx_data0 = mt792x_dma_ring(MT7925_TXQ_BAND0, + MT7925_TX_RING_SIZE, + MT_TX_RING_BASE), + .tx_mcu = mt792x_dma_ring(MT7925_TXQ_MCU_WM, + MT7925_TX_MCU_RING_SIZE, + MT_TX_RING_BASE), + .tx_fwdl = mt792x_dma_ring(MT7925_TXQ_FWDL, + MT7925_TX_FWDL_RING_SIZE, + MT_TX_RING_BASE), + .tx_done = mt792x_dma_ring(MT7928_RXQ_MCU_WM2, + MT7928_RX_MCU_WA_RING_SIZE, + MT_RX_EVENT_RING_BASE), + .rx_mcu = mt792x_dma_ring(MT7928_RXQ_MCU_WM, + MT7925_RX_MCU_RING_SIZE, + MT_RX_EVENT_RING_BASE), + .rx_data = mt792x_dma_ring(MT7928_RXQ_BAND0, + MT7925_RX_RING_SIZE, + MT_RX_DATA_RING_BASE), +}; + static int mt7927_dma_init(struct mt792x_dev *dev) { int ret; @@ -279,11 +300,62 @@ static int mt7927_dma_init(struct mt792x_dev *dev) return mt792x_dma_enable(dev); } +static void mt7928_dma_shdl_lite_init(struct mt792x_dev *dev) +{ + u32 addr, idx, grp1_5_quota, grp15_quota; + u32 q2group[8] = { + 0x04000000, /* AC00->G0,..., AC03->G4 */ + 0x04010101, /* AC10->G1,..., AC13->G4 */ + 0x04020202, /* AC20->G2,..., AC23->G4 */ + 0x04030303, /* AC30->G3,..., AC33->G4 */ + 0x00000005, /* ALTX->G5,BMC->G0,BCN->G0 */ + 0x00000005, /* TGID=1 ALTX->G5 */ + 0x00000000, /* NAF/NBCN/FIXFID -> G0 */ + 0x00000005, /* TGID=2 ALTX->G5 */ + }; + + /* RST */ + mt76_wr(dev, MT_DMASHDL_LITE_MAIN_CONTROL, MT_DMASHDL_LITE_MAIN_CONTROL_SW_RST); + /* pse page size 0x10, ple page size 0x7e0 */ + mt76_wr(dev, MT_DMASHDL_LITE_PAGE_SIZE, + FIELD_PREP(MT_DMASHDL_LITE_PSE_PAGE_SIZE_MASK, 0x10) | + FIELD_PREP(MT_DMASHDL_LITE_PLE_PAGE_SIZE_MASK, 0x7e0)); + /* pse max page 8, ple max page 1 */ + mt76_wr(dev, MT_DMASHDL_LITE_PKT_MAX_SIZE, + FIELD_PREP(MT_DMASHDL_LITE_PSE_PKT_MAX_SIZE_MASK, 8) | + FIELD_PREP(MT_DMASHDL_LITE_PLE_PKT_MAX_SIZE_MASK, 1)); + /* SN/UDF check */ + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_SN_CHK0, 0xffffffff); + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_SN_CHK1, 0xffffffff); + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_UDF_CHK0, 0xffffffff); + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_UDF_CHK1, 0xffffffff); + /* q mapping */ + for (addr = MT_DMASHDL_LITE_Q_MAPPING0, idx = 0; + idx < ARRAY_SIZE(q2group); + idx++, addr += 4) + mt76_wr(dev, addr, q2group[idx]); + /* refill, set 0 to enable group 0,1,2,3,4,5 & 15 */ + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_DISABLE0, 0xffff7fc0); + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_DISABLE1, 0xffffffff); + /* max/min quota */ + grp1_5_quota = FIELD_PREP(MT_DMASHDL_LITE_GROUP_MAX_QUOTA_MASK, 0x3f0) | + FIELD_PREP(MT_DMASHDL_LITE_GROUP_MIN_QUOTA_MASK, 0x10); + grp15_quota = FIELD_PREP(MT_DMASHDL_LITE_GROUP_MAX_QUOTA_MASK, 0x30); + + for (addr = MT_DMASHDL_LITE_GROUP0_QUOTA, idx = 0; + idx < DMASHDL_LITE_GROUP_NUM; + idx++, addr += 4) + mt76_wr(dev, addr, (idx <= 5) ? grp1_5_quota : + ((idx == 15) ? grp15_quota : 0)); +} + static int mt7925_dma_init(struct mt792x_dev *dev) { int ret; + const struct mt792x_dma_layout *layout = + is_mt7928(&dev->mt76) ? &mt7928_dma_layout : &mt7925_dma_layout; - ret = mt792x_dma_alloc_queues(dev, &mt7925_dma_layout); + ret = mt792x_dma_alloc_queues(dev, layout); if (ret) return ret; @@ -291,6 +363,9 @@ static int mt7925_dma_init(struct mt792x_dev *dev) if (ret < 0) return ret; + if (is_mt7928(&dev->mt76)) + mt7928_dma_shdl_lite_init(dev); + netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, mt792x_poll_tx); napi_enable(&dev->mt76.tx_napi); @@ -402,14 +477,18 @@ static int mt7925_pci_probe(struct pci_dev *pdev, if (ret < 0) return ret; - ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) - goto err_free_pci_vec; - is_mt7927_hw = (pdev->device == 0x6639 || pdev->device == 0x7927 || pdev->device == 0x0738); is_mt7928_hw = (pdev->device == 0x7928 || pdev->device == 0x7935); + if (is_mt7928_hw) + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(34)); + else + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + + if (ret) + goto err_free_pci_vec; + /* MT7927: ASPM L1 causes unreliable WFDMA register access */ if (mt7925_disable_aspm || is_mt7927_hw) mt76_pci_disable_aspm(pdev); @@ -499,7 +578,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev, if (is_mt7927(&dev->mt76)) ret = mt7927_dma_init(dev); - else if (is_mt7925(&dev->mt76)) + else if (is_mt7925(&dev->mt76) || is_mt7928(&dev->mt76)) ret = mt7925_dma_init(dev); else ret = -EINVAL; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h index 855a53c0748a..253ba72310ec 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h @@ -108,4 +108,25 @@ #define MT7925_PCIE_MAC_INT_ENABLE MT7925_PCIE_MAC(0x188) #define MT7925_PCIE_MAC_PM MT7925_PCIE_MAC(0x194) +#define MT_DMASHDL_LITE_BASE 0x20026200 +#define MT_DMASHDL_LITE(ofs) (MT_DMASHDL_LITE_BASE + (ofs)) +#define MT_DMASHDL_LITE_MAIN_CONTROL MT_DMASHDL_LITE(0x004) +#define MT_DMASHDL_LITE_MAIN_CONTROL_SW_RST BIT(18) +#define MT_DMASHDL_LITE_PAGE_SIZE MT_DMASHDL_LITE(0x008) +#define MT_DMASHDL_LITE_PLE_PAGE_SIZE_MASK GENMASK(12, 0) +#define MT_DMASHDL_LITE_PSE_PAGE_SIZE_MASK GENMASK(28, 16) +#define MT_DMASHDL_LITE_PKT_MAX_SIZE MT_DMASHDL_LITE(0x00c) +#define MT_DMASHDL_LITE_PLE_PKT_MAX_SIZE_MASK GENMASK(12, 0) +#define MT_DMASHDL_LITE_PSE_PKT_MAX_SIZE_MASK GENMASK(28, 16) +#define MT_DMASHDL_LITE_GROUP_DISABLE0 MT_DMASHDL_LITE(0x010) +#define MT_DMASHDL_LITE_GROUP_DISABLE1 MT_DMASHDL_LITE(0x014) +#define MT_DMASHDL_LITE_GROUP_SN_CHK0 MT_DMASHDL_LITE(0x018) +#define MT_DMASHDL_LITE_GROUP_SN_CHK1 MT_DMASHDL_LITE(0x01c) +#define MT_DMASHDL_LITE_GROUP_UDF_CHK0 MT_DMASHDL_LITE(0x020) +#define MT_DMASHDL_LITE_GROUP_UDF_CHK1 MT_DMASHDL_LITE(0x024) +#define MT_DMASHDL_LITE_Q_MAPPING0 MT_DMASHDL_LITE(0x028) +#define MT_DMASHDL_LITE_GROUP0_QUOTA MT_DMASHDL_LITE(0x100) +#define MT_DMASHDL_LITE_GROUP_MIN_QUOTA_MASK GENMASK(12, 0) +#define MT_DMASHDL_LITE_GROUP_MAX_QUOTA_MASK GENMASK(28, 16) + #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index 4d4c62bb0a77..8ad94fa58340 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -206,9 +206,38 @@ static void mt7927_wfdma_setup(struct mt792x_dev *dev) mt76_set(dev, MT_WFDMA0_INT_TX_PRI, 0x7F00); } +static void mt7928_dma_prefetch_setup(struct mt792x_dev *dev) +{ + /* rx ring */ + mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x8)); + mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0080, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x00c0, 0x8)); + mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x0140, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING4_EXT_CTRL, PREFETCH(0x0180, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING5_EXT_CTRL, PREFETCH(0x01c0, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING6_EXT_CTRL, PREFETCH(0x0200, 0x4)); + /* tx ring */ + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0240, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0340, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0440, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x0540, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING4_EXT_CTRL, PREFETCH(0x0640, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING5_EXT_CTRL, PREFETCH(0x0740, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0840, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0880, 0x4)); +} + +static void mt7928_wfdma_setup(struct mt792x_dev *dev) +{ + mt76_wr(dev, MT_WFDMA0_INT_RX_PRI, 0); + mt76_wr(dev, MT_WFDMA0_INT_TX_PRI, 0); +} + static void mt792x_dma_prefetch(struct mt792x_dev *dev) { - if (is_mt7927(&dev->mt76)) { + if (is_mt7928(&dev->mt76)) { + mt7928_dma_prefetch_setup(dev); + } else if (is_mt7927(&dev->mt76)) { mt7927_dma_prefetch_setup(dev); } else if (is_mt7925(&dev->mt76)) { mt7925_dma_prefetch_setup(dev); @@ -250,32 +279,69 @@ static void mt792x_dma_prefetch(struct mt792x_dev *dev) int mt792x_dma_enable(struct mt792x_dev *dev) { - /* configure perfetch settings */ + u32 addr; + + /* configure prefetch settings */ mt792x_dma_prefetch(dev); /* reset dma idx */ mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0); - if (is_mt7925(&dev->mt76)) + if (is_mt7925(&dev->mt76) || is_mt7928(&dev->mt76)) mt76_wr(dev, MT_WFDMA0_RST_DRX_PTR, ~0); /* configure delay interrupt */ mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0, 0); - mt76_set(dev, MT_WFDMA0_GLO_CFG, - MT_WFDMA0_GLO_CFG_TX_WB_DDONE | - MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN | - MT_WFDMA0_GLO_CFG_CLK_GAT_DIS | - MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | - FIELD_PREP(MT_WFDMA0_GLO_CFG_DMA_SIZE, 3) | - MT_WFDMA0_GLO_CFG_FIFO_DIS_CHECK | - MT_WFDMA0_GLO_CFG_RX_WB_DDONE | - MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN | - MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + if (is_mt7928(&dev->mt76)) { + mt76_wr(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_WB_DDONE | + MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN | + MT_WFDMA0_GLO_CFG_CLK_GAT_DIS | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + FIELD_PREP(MT_WFDMA0_GLO_CFG_DMA_SIZE, 1) | + MT_WFDMA0_GLO_CFG_FIFO_DIS_CHECK | + MT_WFDMA0_GLO_CFG_RX_WB_DDONE | + MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN | + MT_WFDMA0_GLO_CFG_ADDR_EXT_EN | + MT_WFDMA0_GLO_CFG_CSR_LBK_RX_Q_SEL_EN); + /* set rxq threshold to 2 */ + for (addr = MT_WFDMA0_WPDMA_PAUSE_RXQ_TH10; + addr <= MT_WFDMA0_WPDMA_PAUSE_RXQ_TH76; + addr += 4) { + mt76_wr(dev, addr, + FIELD_PREP(MT_WFDMA0_WPDMA_PAUSE_RXQ_THXX_L_TH_MASK, 2) | + FIELD_PREP(MT_WFDMA0_WPDMA_PAUSE_RXQ_THXX_H_TH_MASK, 2)); + } + mt76_wr(dev, MT_WFDMA0_GLO_CFG_EXT0, + MT_WFDMA0_GLO_CFG_EXT0_CSR_MEM_ARB_LOCK_EN | + MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_LITE_EN | + MT_WFDMA0_GLO_CFG_EXT0_CSR_RX_WB_KEEP_RSVD | + MT_WFDMA0_GLO_CFG_EXT0_CSR_BID_CHECK_BYPASS_EN | + MT_WFDMA0_GLO_CFG_EXT0_CSR_RX_INFO_WB_EN | + MT_WFDMA0_GLO_CFG_EXT0_CSR_AXI_AWUSER_LOCK_EN | + FIELD_PREP(MT_WFDMA0_GLO_CFG_EXT0_CSR_MAX_PREFETCH_CNT_MASK, 3) | + FIELD_PREP(MT_WFDMA0_GLO_CFG_EXT0_CSR_MEM_BST_SIZE_MASK, 3) | + FIELD_PREP(MT_WFDMA0_GLO_CFG_EXT0_CSR_AXI_AW_OUTSTANDING_NUM_MASK, + 8)); + } else { + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_WB_DDONE | + MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN | + MT_WFDMA0_GLO_CFG_CLK_GAT_DIS | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + FIELD_PREP(MT_WFDMA0_GLO_CFG_DMA_SIZE, 3) | + MT_WFDMA0_GLO_CFG_FIFO_DIS_CHECK | + MT_WFDMA0_GLO_CFG_RX_WB_DDONE | + MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + } mt76_set(dev, MT_WFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); - if (is_mt7927(&dev->mt76)) + if (is_mt7928(&dev->mt76)) + mt7928_wfdma_setup(dev); + else if (is_mt7927(&dev->mt76)) mt7927_wfdma_setup(dev); else if (is_mt7925(&dev->mt76)) mt7925_wfdma_setup(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h index 0e297fd9468a..3587f61e098a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h @@ -331,11 +331,29 @@ #define MT_INT_MCU_CMD MCU2HOST_SW_INT_ENA #define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_TH10 MT_WFDMA0(0x260) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_TH32 MT_WFDMA0(0x264) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_TH54 MT_WFDMA0(0x268) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_TH76 MT_WFDMA0(0x26c) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_THXX_L_TH_MASK GENMASK(11, 0) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_THXX_H_TH_MASK GENMASK(27, 16) + #define MT_WFDMA0_RST_DRX_PTR MT_WFDMA0(0x280) #define MT_WFDMA0_INT_RX_PRI MT_WFDMA0(0x298) #define MT_WFDMA0_INT_TX_PRI MT_WFDMA0(0x29c) #define MT_WFDMA0_GLO_CFG_EXT0 MT_WFDMA0(0x2b0) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_MEM_ARB_LOCK_EN BIT(4) #define MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_EN BIT(6) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_LITE_EN BIT(7) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_RX_WB_KEEP_RSVD BIT(10) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_BID_CHECK_BYPASS_EN BIT(22) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_RX_INFO_WB_EN BIT(23) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_AXI_AWUSER_LOCK_EN BIT(29) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_MAX_PREFETCH_CNT_MASK GENMASK(1, 0) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_MEM_BST_SIZE_MASK GENMASK(3, 2) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_AXI_AW_OUTSTANDING_NUM_MASK GENMASK(27, 24) + +#define MT_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4) #define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0) #define MT_WFDMA0_TX_RING0_EXT_CTRL MT_WFDMA0(0x600) @@ -375,7 +393,6 @@ #define MT_WFDMA_PREFETCH_CFG1 MT_WFDMA_EXT_CSR(0xf4) #define MT_WFDMA_PREFETCH_CFG2 MT_WFDMA_EXT_CSR(0xf8) #define MT_WFDMA_PREFETCH_CFG3 MT_WFDMA_EXT_CSR(0xfc) -#define MT_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4) #define MT_SWDEF_BASE 0x41f200 #define MT_SWDEF(ofs) (MT_SWDEF_BASE + (ofs)) From 5e738c59e028d8998c8460a2f6431dff84de7e21 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:53:35 +0800 Subject: [PATCH 219/403] wifi: mt76: mt7925: add MT7928 TXD/TXS/TX_DONE support Add MT7928 TXD v2 fields, per-chip WTBL register addresses, UNI TxDone event parsing, and TXS format acceptance for MPDU/PPDU. Suppress HW AMSDU on management frames for MT7928. Signed-off-by: Xiong Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075339.2578327-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt76_connac.h | 1 + .../wireless/mediatek/mt76/mt76_connac3_mac.h | 42 ++++ .../net/wireless/mediatek/mt76/mt7925/mac.c | 221 +++++++++++++++++- .../net/wireless/mediatek/mt76/mt7925/mac.h | 9 +- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 24 ++ .../wireless/mediatek/mt76/mt7925/mt7925.h | 1 + .../net/wireless/mediatek/mt76/mt7925/regs.h | 6 + 7 files changed, 292 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 019d0275dc5c..0951038916d3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -301,6 +301,7 @@ static inline bool is_mt76_fw_txp(struct mt76_dev *dev) case 0x7902: case 0x7925: case 0x7927: + case 0x7928: case 0x7663: case 0x7622: return false; diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h index 247e2e7a47d8..d2d63767bfd0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h @@ -18,6 +18,11 @@ enum { MT_LMAC_PSMP0, }; +enum { + TXS_FM_MPDU = 0, + TXS_FM_PPDU = 2, +}; + #define MT_CT_PARSE_LEN 72 #define MT_CT_DMA_BUF_NUM 2 @@ -236,7 +241,9 @@ enum tx_frag_idx { #define MT_TXD2_HDR_PAD GENMASK(11, 10) #define MT_TXD2_RTS BIT(9) #define MT_TXD2_OWN_MAC_MAP BIT(8) +#define MT_TXD2_OWN_MAC_MAP_V2 BIT(9) #define MT_TXD2_BF_TYPE GENMASK(6, 7) +#define MT_TXD2_BF_TYPE_V2 GENMASK(6, 8) #define MT_TXD2_FRAME_TYPE GENMASK(5, 4) #define MT_TXD2_SUB_TYPE GENMASK(3, 0) @@ -261,6 +268,7 @@ enum tx_frag_idx { #define MT_TXD5_BYPASS_TBB BIT(14) #define MT_TXD5_BYPASS_RBB BIT(13) #define MT_TXD5_BSS_COLOR_ZERO BIT(12) +#define MT_TXD5_OCUP_BY_OTHER_LNK BIT(11) #define MT_TXD5_TX_STATUS_HOST BIT(10) #define MT_TXD5_TX_STATUS_MCU BIT(9) #define MT_TXD5_TX_STATUS_FMT BIT(8) @@ -270,15 +278,19 @@ enum tx_frag_idx { #define MT_TXD6_VTA BIT(28) #define MT_TXD6_FIXED_BW BIT(25) #define MT_TXD6_BW GENMASK(24, 22) +#define MT_TXD6_BW_V2 GENMASK(25, 22) #define MT_TXD6_TX_RATE GENMASK(21, 16) #define MT_TXD6_TIMESTAMP_OFS_EN BIT(15) +#define MT_TXD6_TIMESTAMP_OFS_EN_V2 GENMASK(15, 13) #define MT_TXD6_TIMESTAMP_OFS_IDX GENMASK(14, 10) +#define MT_TXD6_TIMESTAMP_OFS_IDX_V2 GENMASK(12, 8) #define MT_TXD6_TID_ADDBA GENMASK(10, 8) #define MT_TXD6_MSDU_CNT GENMASK(9, 4) #define MT_TXD6_MSDU_CNT_V2 GENMASK(15, 10) #define MT_TXD6_DIS_MAT BIT(3) #define MT_TXD6_DAS BIT(2) #define MT_TXD6_AMSDU_CAP BIT(1) +#define MT_TXD6_MLD BIT(0) #define MT_TXD7_TXD_LEN GENMASK(31, 30) #define MT_TXD7_IP_SUM BIT(29) @@ -287,6 +299,9 @@ enum tx_frag_idx { #define MT_TXD7_CTXD BIT(26) #define MT_TXD7_CTXD_CNT GENMASK(25, 22) #define MT_TXD7_UDP_TCP_SUM BIT(15) +#define MT_TXD7_IMMEDIATE_TX BIT(14) +#define MT_TXD7_FORCE_RTS_CTS BIT(13) +#define MT_TXD7_ENABLE_ICI BIT(12) #define MT_TXD7_TX_TIME GENMASK(9, 0) #define MT_TXD9_WLAN_IDX GENMASK(23, 8) @@ -397,4 +412,31 @@ enum tx_frag_idx { #define MT_TXS7_MPDU_RETRY_BYTE_SCALE BIT(15) #define MT_TXS7_MPDU_RETRY_BYTE GENMASK(14, 0) +struct mt7928_uni_txdone_event { + __le16 tag; + __le16 len; + + u8 pid; /* HW packet ID */ + u8 status; /* TX_RESULT_xx */ + __le16 seq; /* packet sequence number */ + + u8 wcid; /* WLAN index (WTBL) */ + u8 tx_count; /* TX attempts including retries */ + __le16 tx_rate; + + u8 flag; /* TXS_WITH_ADVANCED_INFO or TXS_IS_EXIST */ + u8 tid; + u8 rsp_rate; + u8 rate_tbl_idx; /* last TX rate index from WLAN table */ + + u8 bw; /* bandwidth used for this PPDU */ + u8 tx_pwr; /* dBm */ + u8 flush_reason; + u8 rsv[1]; + + __le32 tx_delay; /* unit: 32us, UMAC TX to TX status */ + __le32 timestamp; /* local TSF at first bit of MAC header */ + __le32 applied_flags; +} __packed; + #endif /* __MT76_CONNAC3_MAC_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 778a0c68d98e..a7eb80b22953 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -12,10 +12,17 @@ bool mt7925_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask) { - mt76_rmw(dev, MT7925_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, + u32 wtbl_update; + + if (is_mt7928(&dev->mt76)) + wtbl_update = MT7928_WTBL_UPDATE; + else + wtbl_update = MT7925_WTBL_UPDATE; + + mt76_rmw(dev, wtbl_update, MT_WTBL_UPDATE_WLAN_IDX, FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); - return mt76_poll(dev, MT7925_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, + return mt76_poll(dev, wtbl_update, MT_WTBL_UPDATE_BUSY, 0, 5000); } @@ -159,10 +166,17 @@ void mt7925_mac_set_fixed_rate_table(struct mt792x_dev *dev, { u32 ctrl = MT_WTBL_ITCR_WR | MT_WTBL_ITCR_EXEC | tbl_idx; - mt76_wr(dev, MT_WTBL_ITDR0, rate_idx); - /* use wtbl spe idx */ - mt76_wr(dev, MT_WTBL_ITDR1, MT_WTBL_SPE_IDX_SEL); - mt76_wr(dev, MT_WTBL_ITCR, ctrl); + if (is_mt7928(&dev->mt76)) { + mt76_wr(dev, MT7928_WTBL_ITDR0, rate_idx); + /* use wtbl spe idx */ + mt76_wr(dev, MT7928_WTBL_ITDR1, MT_WTBL_SPE_IDX_SEL); + mt76_wr(dev, MT7928_WTBL_ITCR, ctrl); + } else { + mt76_wr(dev, MT_WTBL_ITDR0, rate_idx); + /* use wtbl spe idx */ + mt76_wr(dev, MT_WTBL_ITDR1, MT_WTBL_SPE_IDX_SEL); + mt76_wr(dev, MT_WTBL_ITCR, ctrl); + } } /* The HW does not translate the mac header to 802.3 for mesh point */ @@ -705,6 +719,9 @@ mt7925_mac_write_txwi_80211(struct mt76_dev *dev, __le32 *txwi, txwi[2] |= cpu_to_le32(val); + if (is_mt7928(dev) && ieee80211_is_mgmt(hdr->frame_control)) + txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); + txwi[3] |= cpu_to_le32(FIELD_PREP(MT_TXD3_BCM, multicast)); if (ieee80211_is_beacon(fc)) txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); @@ -808,7 +825,11 @@ mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, txwi[5] = cpu_to_le32(val); - val = MT_TXD6_DAS | FIELD_PREP(MT_TXD6_MSDU_CNT, 1); + if (is_mt7928(dev)) + val = MT_TXD6_DAS | FIELD_PREP(MT_TXD6_MSDU_CNT_V2, 1); + else + val = MT_TXD6_DAS | FIELD_PREP(MT_TXD6_MSDU_CNT, 1); + if (vif && (!ieee80211_vif_is_mld(vif) || (q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0))) val |= MT_TXD6_DIS_MAT; @@ -836,6 +857,10 @@ mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, } txwi[6] |= cpu_to_le32(FIELD_PREP(MT_TXD6_TX_RATE, idx)); + + if (is_mt7928(dev)) + txwi[6] |= cpu_to_le32(FIELD_PREP(MT_TXD6_BW_V2, 8)); + txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); } } @@ -916,7 +941,6 @@ mt7925_mac_add_txs_skb(struct mt792x_dev *dev, struct mt76_wcid *wcid, goto out_no_skb; txs = le32_to_cpu(txs_data[0]); - info = IEEE80211_SKB_CB(skb); if (!(txs & MT_TXS0_ACK_ERROR_MASK)) info->flags |= IEEE80211_TX_STAT_ACK; @@ -1033,17 +1057,192 @@ mt7925_mac_add_txs_skb(struct mt792x_dev *dev, struct mt76_wcid *wcid, return !!skb; } -void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data) +static bool +mt7928_mac_add_txs_skb_msg(struct mt792x_dev *dev, struct mt76_wcid *wcid, + int pid, struct mt7928_uni_txdone_event *pevt) { + struct mt76_sta_stats *stats = &wcid->stats; + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_tx_info *info; + struct sk_buff_head list; + u32 txrate, mode, stbc; + struct rate_info rate; + struct mt76_phy *mphy; + struct sk_buff *skb; + bool cck = false; + + mt76_tx_status_lock(mdev, &list); + skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list); + if (!skb) + goto out_no_skb; + + info = IEEE80211_SKB_CB(skb); + if (!pevt->status) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.ampdu_len = 1; + info->status.ampdu_ack_len = !!(info->flags & + IEEE80211_TX_STAT_ACK); + + info->status.rates[0].idx = -1; + + txrate = le16_to_cpu(pevt->tx_rate); + + rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate); + rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1; + stbc = FIELD_GET(MT_TX_RATE_STBC, txrate); + + if (stbc && rate.nss > 1) + rate.nss >>= 1; + + if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss)) + stats->tx_nss[rate.nss - 1]++; + if (rate.mcs < ARRAY_SIZE(stats->tx_mcs)) + stats->tx_mcs[rate.mcs]++; + + mode = FIELD_GET(MT_TX_RATE_MODE, txrate); + switch (mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + mphy = mt76_dev_phy(mdev, wcid->phy_idx); + + if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck); + rate.legacy = sband->bitrates[rate.mcs].bitrate; + break; + case MT_PHY_TYPE_HT: + case MT_PHY_TYPE_HT_GF: + if (rate.mcs > 31) + goto out; + + rate.flags = RATE_INFO_FLAGS_MCS; + if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI) + rate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT_PHY_TYPE_VHT: + if (rate.mcs > 9) + goto out; + + rate.flags = RATE_INFO_FLAGS_VHT_MCS; + break; + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + case MT_PHY_TYPE_HE_MU: + if (rate.mcs > 11) + goto out; + + rate.he_gi = wcid->rate.he_gi; + rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate); + rate.flags = RATE_INFO_FLAGS_HE_MCS; + break; + case MT_PHY_TYPE_EHT_SU: + case MT_PHY_TYPE_EHT_TRIG: + case MT_PHY_TYPE_EHT_MU: + if (rate.mcs > 13) + goto out; + + rate.eht_gi = wcid->rate.eht_gi; + rate.flags = RATE_INFO_FLAGS_EHT_MCS; + break; + default: + goto out; + } + + stats->tx_mode[mode]++; + + switch (pevt->bw) { + case IEEE80211_STA_RX_BW_160: + rate.bw = RATE_INFO_BW_160; + stats->tx_bw[3]++; + break; + case IEEE80211_STA_RX_BW_80: + rate.bw = RATE_INFO_BW_80; + stats->tx_bw[2]++; + break; + case IEEE80211_STA_RX_BW_40: + rate.bw = RATE_INFO_BW_40; + stats->tx_bw[1]++; + break; + default: + rate.bw = RATE_INFO_BW_20; + stats->tx_bw[0]++; + break; + } + wcid->rate = rate; + +out: + mt76_tx_status_skb_done(mdev, skb, &list); + +out_no_skb: + mt76_tx_status_unlock(mdev, &list); + + return !!skb; +} + +void mt7928_mac_add_txs_msg(struct mt792x_dev *dev, + void *evt) +{ + struct mt7928_uni_txdone_event *done_evt; struct mt792x_link_sta *mlink = NULL; struct mt76_wcid *wcid; - __le32 *txs_data = data; u16 wcidx; u8 pid; - if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1) + done_evt = (struct mt7928_uni_txdone_event *)evt; + wcidx = done_evt->wcid; + pid = done_evt->pid; + + if (pid < MT_PACKET_ID_FIRST) return; + if (wcidx >= MT792x_WTBL_SIZE) + return; + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, wcidx); + if (!wcid) + goto out; + + mlink = container_of(wcid, struct mt792x_link_sta, wcid); + + mt7928_mac_add_txs_skb_msg(dev, wcid, pid, done_evt); + if (!wcid->sta) + goto out; + + mt76_wcid_add_poll(&dev->mt76, &mlink->wcid); + +out: + rcu_read_unlock(); +} + +void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data) +{ + struct mt792x_link_sta *mlink = NULL; + __le32 *txs_data = data; + struct mt76_wcid *wcid; + u8 pid, txs_fm; + u16 wcidx; + + txs_fm = le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT); + + if (is_mt7928(&dev->mt76)) { + if (txs_fm != TXS_FM_MPDU && txs_fm != TXS_FM_PPDU) + return; + } else if (txs_fm > 1) { + return; + } + wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID); pid = le32_get_bits(txs_data[3], MT_TXS3_PID); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.h b/drivers/net/wireless/mediatek/mt76/mt7925/mac.h index 67148c87de76..567d072cba02 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.h @@ -14,7 +14,14 @@ static inline u32 mt7925_mac_wtbl_lmac_addr(struct mt792x_dev *dev, u16 wcid, u8 dw) { - mt76_wr(dev, MT7925_WTBLON_TOP_WDUCR, + u32 wdu_cr; + + if (is_mt7928(&dev->mt76)) + wdu_cr = MT7928_WTBLON_TOP_WDUCR; + else + wdu_cr = MT7925_WTBLON_TOP_WDUCR; + + mt76_wr(dev, wdu_cr, FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7))); return MT_WTBL_LMAC_OFFS(wcid, dw); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 5602544d3853..6c7fae71fe5b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -437,6 +437,7 @@ mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb) u8 rsv[3]; u8 data[]; } __packed * txs; + struct mt7928_uni_txdone_event *evt; struct tlv *tlv; u32 tlv_len; @@ -446,6 +447,29 @@ mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb) while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) { switch (le16_to_cpu(tlv->tag)) { + case UNI_EVENT_TX_DONE_MSG: + if (!is_mt7928(&dev->mt76)) + break; + + evt = (struct mt7928_uni_txdone_event *)tlv; + if (evt->status) { + dev_info(dev->mt76.dev, + "TxDone: pid=%u status=%#x sn=%#x wcid=%u " + "cnt=%u rate=%#x flag=%#x tid=%u pwr=%u " + "rsp_rate=%#x rate_idx=%u bw=%u flush=%#x " + "delay=%#x ts=%#x flags=%#x\n", + evt->pid, evt->status, + le16_to_cpu(evt->seq), evt->wcid, + evt->tx_count, le16_to_cpu(evt->tx_rate), + evt->flag, evt->tid, evt->tx_pwr, + evt->rsp_rate, evt->rate_tbl_idx, + evt->bw, evt->flush_reason, + le32_to_cpu(evt->tx_delay), + le32_to_cpu(evt->timestamp), + le32_to_cpu(evt->applied_flags)); + } + mt7928_mac_add_txs_msg(dev, evt); + break; case UNI_EVENT_TX_DONE_RAW: txs = (struct mt7925_mcu_txs_event *)tlv->data; mt7925_mac_add_txs(dev, txs->data); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index a5414fa2736f..321e732347f2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -354,6 +354,7 @@ int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd, int mt7925e_mac_reset(struct mt792x_dev *dev); int mt7925e_mcu_init(struct mt792x_dev *dev); void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data); +void mt7928_mac_add_txs_msg(struct mt792x_dev *dev, void *evt); void mt7925_set_runtime_pm(struct mt792x_dev *dev); void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h index 253ba72310ec..af6291fc53cd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h @@ -97,12 +97,18 @@ #define MT_WFSYS_SW_RST_B 0x7c000140 #define MT7925_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x370) +#define MT7928_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x400) #define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0) #define MT7925_WTBL_UPDATE MT_WTBLON_TOP(0x380) +#define MT7928_WTBL_UPDATE MT_WTBLON_TOP(0x410) #define MT_WTBL_UPDATE_WLAN_IDX GENMASK(11, 0) #define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14) +#define MT7928_WTBL_ITCR MT_WTBLON_TOP(0x440) +#define MT7928_WTBL_ITDR0 MT_WTBLON_TOP(0x448) +#define MT7928_WTBL_ITDR1 MT_WTBLON_TOP(0x44c) + #define MT7925_PCIE_MAC_BASE 0x10000 #define MT7925_PCIE_MAC(ofs) (MT7925_PCIE_MAC_BASE + (ofs)) #define MT7925_PCIE_MAC_INT_ENABLE MT7925_PCIE_MAC(0x188) From d93e31ba9fe20a6c8b065838134eaac36f06d6d1 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:53:36 +0800 Subject: [PATCH 220/403] wifi: mt76: mt7925: add MMIO register remapping table for MT7928 MT7928 has a different physical address layout. Add a dedicated mt7928_fixed_map[] remapping table and select it at runtime. Set mdev->rev early in probe for correct chip revision detection. Signed-off-by: Leon Yen Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075339.2578327-2-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/pci.c | 109 +++++++++++++++++- 1 file changed, 103 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index f79d4143e38b..719f53ddf1eb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -110,7 +110,7 @@ static u32 mt7925_reg_map_l2(struct mt792x_dev *dev, u32 addr) static u32 __mt7925_reg_addr(struct mt792x_dev *dev, u32 addr) { - static const struct mt76_connac_reg_map fixed_map[] = { + static const struct mt76_connac_reg_map default_fixed_map[] = { { 0x830c0000, 0x000000, 0x0001000 }, /* WF_MCU_BUS_CR_REMAP */ { 0x54000000, 0x002000, 0x0001000 }, /* WFDMA PCIE0 MCU DMA0 */ { 0x55000000, 0x003000, 0x0001000 }, /* WFDMA PCIE0 MCU DMA1 */ @@ -164,14 +164,109 @@ static u32 __mt7925_reg_addr(struct mt792x_dev *dev, u32 addr) { 0x7c500000, 0x060000, 0x200000 }, /* remap */ { 0x0, 0x0, 0x0 } /* End */ }; - int i; + /* The remap table was ordered from highest to lowest frequency + * to improve lookup efficiency. + */ + static const struct mt76_connac_reg_map mt7928_fixed_map[] = { + {0x54000000, 0x002000, 0x01000}, /* WFDMA_0 (PCIE0 MCU DMA0) */ + {0x55000000, 0x003000, 0x01000}, /* WFDMA_1 (PCIE0 MCU DMA1) */ + {0x57000000, 0x005000, 0x01000}, /* WFDMA_3 (MCU wrap CR) */ + {0x58000000, 0x006000, 0x01000}, /* WFDMA_4 (PCIE1 MCU DMA0) */ + {0x59000000, 0x007000, 0x01000}, /* WFDMA_5 (PCIE1 MCU DMA1) */ + {0x56000000, 0x004000, 0x01000}, /* WFDMA_2 (Reserved) */ + {0x820D0000, 0x030000, 0x10000}, /* WF_LMAC_TOP (WF_WTBLON) */ + {0x820C4000, 0x0A8000, 0x04000}, /* WF_LMAC_TOP (WF_UWTBL) */ + {0x820C0000, 0x008000, 0x04000}, /* WF_UMAC_TOP (PLE) */ + {0x820C8000, 0x00C000, 0x02000}, /* WF_UMAC_TOP (PSE) */ + {0x820CC000, 0x00E000, 0x02000}, /* WF_UMAC_TOP (PP) */ + {0x820F0000, 0x0A0000, 0x00400}, /* WF_LMAC_TOP (WF_CFG) */ + {0x820F1000, 0x0A0600, 0x00200}, /* WF_LMAC_TOP (WF_TRB) */ + {0x820F2000, 0x0A0800, 0x00400}, /* WF_LMAC_TOP (WF_AGG) */ + {0x820F3000, 0x0A0C00, 0x00400}, /* WF_LMAC_TOP (WF_ARB) */ + {0x820F4000, 0x0A1000, 0x00400}, /* WF_LMAC_TOP (WF_TMAC) */ + {0x820F5000, 0x0A1400, 0x00800}, /* WF_LMAC_TOP (WF_RMAC) */ + {0x820F7000, 0x0A1E00, 0x00200}, /* WF_LMAC_TOP (WF_DMA) */ + {0x820F9000, 0x0A3400, 0x00200}, /* WF_LMAC_TOP (WF_WTBLOFF) */ + {0x820FA000, 0x0A4000, 0x00200}, /* WF_LMAC_TOP (WF_ETBF) */ + {0x820FB000, 0x0A4200, 0x00400}, /* WF_LMAC_TOP (WF_LPON) */ + {0x820FC000, 0x0A4600, 0x00200}, /* WF_LMAC_TOP (WF_INT) */ + {0x820FD000, 0x0A4800, 0x00800}, /* WF_LMAC_TOP (WF_MIB) */ + {0x820E0000, 0x020000, 0x00400}, /* WF_LMAC_TOP (WF_CFG) */ + {0x820E1000, 0x020400, 0x00200}, /* WF_LMAC_TOP (WF_TRB) */ + {0x820E2000, 0x020800, 0x00400}, /* WF_LMAC_TOP (WF_AGG) */ + {0x820E3000, 0x020C00, 0x00400}, /* WF_LMAC_TOP (WF_ARB) */ + {0x820E4000, 0x021000, 0x00400}, /* WF_LMAC_TOP (WF_TMAC) */ + {0x820E5000, 0x021400, 0x00800}, /* WF_LMAC_TOP (WF_RMAC) */ + {0x820CE000, 0x021C00, 0x00200}, /* WF_LMAC_TOP (WF_SEC) */ + {0x820E7000, 0x021E00, 0x00200}, /* WF_LMAC_TOP (WF_DMA) */ + {0x820CF000, 0x022000, 0x01000}, /* WF_LMAC_TOP (WF_PF) */ + {0x820E9000, 0x023400, 0x00200}, /* WF_LMAC_TOP (WF_WTBLOFF) */ + {0x820EA000, 0x024000, 0x00200}, /* WF_LMAC_TOP (WF_ETBF) */ + {0x820EB000, 0x024200, 0x00400}, /* WF_LMAC_TOP (WF_LPON) */ + {0x820EC000, 0x024600, 0x00200}, /* WF_LMAC_TOP (WF_INT) */ + {0x820ED000, 0x024800, 0x00800}, /* WF_LMAC_TOP (WF_MIB) */ + {0x820CA000, 0x026000, 0x02000}, /* WF_LMAC_TOP (WF_MUCOP) */ + {0x7C500000, 0x060000, 0x200000}, /* remap */ + {0x7C000000, 0x0F0000, 0x10000}, /* CONN_INFRA (off2on) */ + {0x7C060000, 0x0E0000, 0x10000}, /* remap MT_CONN_ON_LPCTL and MT_CONN_ON_MISC */ + {0x20060000, 0x0E0000, 0x10000}, /* CONN_INFRA conn_host_csr_top */ + {0x7C010000, 0x100000, 0x10000}, /* CONN_INFRA (gpio clkgen cfg) */ + {0x7C050000, 0x1A0000, 0x10000}, /* CONN_INFRA SYSRAM */ + {0x7C080000, 0x190000, 0x10000}, /* CONN_INFRA (coex, pta) */ + {0x7C070000, 0x180000, 0x10000}, /* CONN_INFRA Semaphore */ + {0x7C040000, 0x170000, 0x10000}, /* CONN_INFRA (bus, afe) */ + {0x7C026000, 0x0D6000, 0x0019C}, /* remap DMASHL TOP */ + {0x20020000, 0x0D0000, 0x0C000}, /* CONN_INFRA wf_dma_host_side_cr */ + {0x200B0000, 0x050000, 0x10000}, /* CONN_INFRA conn_von_sysram */ + {0x20090000, 0x150000, 0x08000}, /* CONN_INFRA von_connsys_s0-s7 */ + {0x7C098000, 0x158000, 0x08000}, /* CONN_INFRA von_connsys_hclk_s0-s7 */ + {0x20030000, 0x160000, 0x10000}, /* CONN_INFRA CCIF */ + {0x70000000, 0x1E0000, 0x10000}, /* CONN_INFRA CONN2AP */ + {0x830C0000, 0x000000, 0x01000}, /* WF_MCU_BUS_CR_REMAP */ + {0x81020000, 0x0C0000, 0x10000}, /* WF_TOP_MISC_ON */ + {0x80020000, 0x0B0000, 0x10000}, /* WF_TOP_MISC_OFF */ + {0x81040000, 0x120000, 0x01000}, /* WF_MCU_CFG_ON */ + {0x00400000, 0x080000, 0x10000}, /* WF_MCU_SYSRAM */ + {0x00410000, 0x090000, 0x10000}, /* WF_MCU_SYSRAM (Common driver) */ + {0x88000000, 0x140000, 0x10000}, /* WF_MCU_CFG_LS */ + {0x80010000, 0x124000, 0x01000}, /* WF_AXIDMA */ + {0x81050000, 0x121000, 0x01000}, /* WF_MCU_EINT */ + {0x81060000, 0x122000, 0x01000}, /* WF_MCU_GPT */ + {0x81070000, 0x123000, 0x01000}, /* WF_MCU_WDT */ + {0x830A0000, 0x040000, 0x10000}, /* WF_PHY_MAP0 */ + {0x83090000, 0x060000, 0x10000}, /* WF_PHY_MAP2 */ + {0x83000000, 0x110000, 0x10000}, /* WF_PHY_MAP3 */ + {0x83010000, 0x130000, 0x10000}, /* WF_PHY_MAP4 */ + {0x81030000, 0x0AE000, 0x00100}, /* WFSYS_AON */ + {0x81031000, 0x0AE100, 0x00100}, /* WFSYS_AON */ + {0x81032000, 0x0AE200, 0x00100}, /* WFSYS_AON */ + {0x81033000, 0x0AE300, 0x00100}, /* WFSYS_AON */ + {0x81034000, 0x0AE400, 0x00100}, /* WFSYS_AON */ + {0xE0400000, 0x070000, 0x10000}, /* WF_UMCA_SYSRAM */ + {0x70010000, 0x1C0000, 0x10000}, /* CB Infra1 */ + {0x70020000, 0x1F0000, 0x10000}, /* Reserved for CBTOP, can't switch */ + {0x74040000, 0x1D0000, 0x10000}, /* CB PCIe (cbtop remap) */ + {0x18010000, 0x100000, 0x10000}, /* remap MT_HW_EMI_CTRL */ + {0x00000000, 0x000000, 0x00000}, /* END */ + }; + const struct mt76_connac_reg_map *fixed_map; + size_t array_size; + u32 i; if (addr < 0x200000) return addr; mt7925_reg_remap_restore(dev); - for (i = 0; i < ARRAY_SIZE(fixed_map); i++) { + if (is_mt7928(&dev->mt76)) { + fixed_map = mt7928_fixed_map; + array_size = ARRAY_SIZE(mt7928_fixed_map); + } else { + fixed_map = default_fixed_map; + array_size = ARRAY_SIZE(default_fixed_map); + } + + for (i = 0; i < array_size; i++) { u32 ofs; if (addr < fixed_map[i].phys) @@ -513,12 +608,14 @@ static int mt7925_pci_probe(struct pci_dev *pdev, dev->hif_ops = &mt7925_pcie_ops; dev->pcie_reg = &mt7925_pcie_reg; - if (is_mt7928_hw) + if (is_mt7928_hw) { dev->irq_map = &mt7928_irq_map; - else if (is_mt7927_hw) + mdev->rev = 0x7928 << 16; + } else if (is_mt7927_hw) { dev->irq_map = &mt7927_irq_map; - else + } else { dev->irq_map = &mt7925_irq_map; + } mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev); From f26fda0c6ae2fb2347c16e65d158d9cc9e697751 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:53:37 +0800 Subject: [PATCH 221/403] wifi: mt76: mt7925: align scan IE and EFUSE TLV lengths to 4 bytes for MT7928 MT7928 firmware requires 4-byte aligned TLV payloads. Round up UNI_SCAN_IE allocation with ALIGN(..., 4) and track padded length. Signed-off-by: Xiong Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075339.2578327-3-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 6c7fae71fe5b..bc6b6fc2d6f5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -1581,7 +1581,8 @@ int mt7925_mcu_set_eeprom(struct mt792x_dev *dev) .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), .len = cpu_to_le16(sizeof(req) - 4), .buffer_mode = EE_MODE_EFUSE, - .format = EE_FORMAT_WHOLE + .format = EE_FORMAT_WHOLE, + .buf_len = 0 }; return mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(EFUSE_CTRL), @@ -3037,11 +3038,11 @@ mt7925_mcu_build_scan_ie_tlv(struct mt76_dev *mdev, struct ieee80211_scan_ies *scan_ies) { u32 max_len = sizeof(struct scan_ie_tlv) + MT76_CONNAC_SCAN_IE_LEN; + u32 ies_len, alloc_len; struct scan_ie_tlv *ie; enum nl80211_band i; struct tlv *tlv; const u8 *ies; - u16 ies_len; for (i = 0; i <= NL80211_BAND_6GHZ; i++) { if (i == NL80211_BAND_60GHZ) @@ -3053,11 +3054,16 @@ mt7925_mcu_build_scan_ie_tlv(struct mt76_dev *mdev, if (!ies || !ies_len) continue; - if (ies_len > max_len) + if (is_mt7928(mdev)) + alloc_len = ALIGN(sizeof(*ie) + ies_len, 4); + else + alloc_len = sizeof(*ie) + ies_len; + + if (alloc_len > max_len) return; tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_IE, - sizeof(*ie) + ies_len); + alloc_len); ie = (struct scan_ie_tlv *)tlv; memcpy(ie->ies, ies, ies_len); @@ -3075,7 +3081,7 @@ mt7925_mcu_build_scan_ie_tlv(struct mt76_dev *mdev, break; } - max_len -= (sizeof(*ie) + ies_len); + max_len -= alloc_len; } } From 8905038d58aa5ffa2a42e0efd4e41da4e25ce101 Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:53:38 +0800 Subject: [PATCH 222/403] wifi: mt76: mt7925: add MT7928 per-chip PCIe register definitions MT7928 maps PCIe MAC registers through base 0x74040000. Add MT7928_PCIE_MAC_{INT_ENABLE,PM} macros and override dev->pcie_reg at probe time for MT7928 hardware. Signed-off-by: Xiong Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075339.2578327-4-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 6 ++++++ drivers/net/wireless/mediatek/mt76/mt7925/regs.h | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 719f53ddf1eb..1ad5847d4a8c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -473,6 +473,11 @@ static const struct mt792x_pcie_reg mt7925_pcie_reg = { .pm = MT7925_PCIE_MAC_PM, }; +static const struct mt792x_pcie_reg mt7928_pcie_reg = { + .imask = MT7928_PCIE_MAC_INT_ENABLE, + .pm = MT7928_PCIE_MAC_PM, +}; + static const struct mt792x_irq_map mt7925_irq_map = { .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, .tx = { @@ -609,6 +614,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev, dev->pcie_reg = &mt7925_pcie_reg; if (is_mt7928_hw) { + dev->pcie_reg = &mt7928_pcie_reg; dev->irq_map = &mt7928_irq_map; mdev->rev = 0x7928 << 16; } else if (is_mt7927_hw) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h index af6291fc53cd..cb937d565a80 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h @@ -114,6 +114,11 @@ #define MT7925_PCIE_MAC_INT_ENABLE MT7925_PCIE_MAC(0x188) #define MT7925_PCIE_MAC_PM MT7925_PCIE_MAC(0x194) +#define MT7928_PCIE_MAC_BASE 0x74040000 +#define MT7928_PCIE_MAC(ofs) (MT7928_PCIE_MAC_BASE + (ofs)) +#define MT7928_PCIE_MAC_INT_ENABLE MT7928_PCIE_MAC(0x188) +#define MT7928_PCIE_MAC_PM MT7928_PCIE_MAC(0x194) + #define MT_DMASHDL_LITE_BASE 0x20026200 #define MT_DMASHDL_LITE(ofs) (MT_DMASHDL_LITE_BASE + (ofs)) #define MT_DMASHDL_LITE_MAIN_CONTROL MT_DMASHDL_LITE(0x004) From e65b4ca3391e80d043d010237d8c8562800eb6bf Mon Sep 17 00:00:00 2001 From: Emery Hsin Date: Fri, 12 Jun 2026 15:53:39 +0800 Subject: [PATCH 223/403] wifi: mt76: mt7925: add MT7928 PCIe support Register MT7928 (0x7928, 0x7935) in the PCI device table and declare MODULE_FIRMWARE for all four MT7928 firmware blobs. Signed-off-by: Emery Hsin Link: https://patch.msgid.link/20260612075339.2578327-5-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 1ad5847d4a8c..1514494f6c7b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -22,6 +22,10 @@ static const struct pci_device_id mt7925_pci_device_table[] = { .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM }, { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0738), .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7928), + .driver_data = (kernel_ulong_t)MT7928_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7935), + .driver_data = (kernel_ulong_t)MT7928_FIRMWARE_WM }, { }, }; @@ -663,6 +667,11 @@ static int mt7925_pci_probe(struct pci_dev *pdev, "MT7927 raw CHIPID=0x%04x, forcing chip=0x7927\n", mt76_chip(mdev)); mdev->rev = (0x7927 << 16) | (mdev->rev & 0xff); + } else if (is_mt7928_hw && mt76_chip(mdev) != 0x7928) { + dev_info(mdev->dev, + "MT7928 raw CHIPID=0x%04x, forcing chip=0x7928\n", + mt76_chip(mdev)); + mdev->rev = (0x7928 << 16) | (mdev->rev & 0xff); } mt76_rmw_field(dev, MT_HW_EMI_CTL, MT_HW_EMI_CTL_SLPPROT_EN, 1); @@ -913,6 +922,10 @@ MODULE_FIRMWARE(MT7925_FIRMWARE_WM); MODULE_FIRMWARE(MT7925_ROM_PATCH); MODULE_FIRMWARE(MT7927_FIRMWARE_WM); MODULE_FIRMWARE(MT7927_ROM_PATCH); +MODULE_FIRMWARE(MT7928_FIRMWARE_WM); +MODULE_FIRMWARE(MT7928_ROM_PATCH); +MODULE_FIRMWARE(MT7928_CB_ROM_PATCH); +MODULE_FIRMWARE(MT7928_PHY_RAM); MODULE_AUTHOR("Deren Wu "); MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_DESCRIPTION("MediaTek MT7925E (PCIe) wireless driver"); From a92cd5dd792a63a5d8a72142835382d4edbe9933 Mon Sep 17 00:00:00 2001 From: David Bauer Date: Sat, 16 May 2026 16:49:42 +0200 Subject: [PATCH 224/403] wifi: mt76: mt7915: configure noise floor reporting on reset When performing a full system recovery of the MCU on a dual-phy platform, band 0 (usually 2.4GHz) stops reading correct noise floor data. This is due to noise floor reporting only being configured correctly for the second device PHY. Configure the respective registers correctly after restarting the MCU firmware to fix reported noise-floor values. Signed-off-by: David Bauer Link: https://patch.msgid.link/20260516144944.2574053-1-mail@david-bauer.net Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 334c19ab2b22..9e93201e4186 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1292,6 +1292,7 @@ mt7915_mac_restart(struct mt7915_dev *dev) struct mt7915_phy *phy2; struct mt76_phy *ext_phy; struct mt76_dev *mdev = &dev->mt76; + bool run_main, run_ext; int i, ret; ext_phy = dev->mt76.phys[MT_BAND1]; @@ -1387,13 +1388,17 @@ mt7915_mac_restart(struct mt7915_dev *dev) mt7915_init_txpower(phy2); ret = mt7915_txbf_init(dev); - if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) { + run_main = test_and_clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); + run_ext = ext_phy && + test_and_clear_bit(MT76_STATE_RUNNING, &ext_phy->state); + + if (run_main) { ret = mt7915_run(dev->mphy.hw); if (ret) goto out; } - if (ext_phy && test_bit(MT76_STATE_RUNNING, &ext_phy->state)) { + if (run_ext) { ret = mt7915_run(ext_phy->hw); if (ret) goto out; From 5323d3e50c20ce53b9b393ef36b027d6cd1e6f11 Mon Sep 17 00:00:00 2001 From: Filip Bakreski Date: Tue, 9 Jun 2026 20:53:01 +1000 Subject: [PATCH 225/403] wifi: mt76: mt76u: use a threaded NAPI for the RX path The USB RX path delivers frames to the stack via mt76_rx_complete() with a NULL napi pointer, taking the netif_receive_skb_list() path, so it never benefits from GRO -- unlike the DMA-based mt76 drivers, which pass a real napi and use napi_gro_receive(). For bulk TCP traffic this is costly, as every segment traverses the stack individually. Service the MT_RXQ_MAIN queue from a threaded NAPI, reusing mt76_dev's existing napi_dev and napi[] rather than adding new fields. The URB completion handler schedules the napi; its poll drains the URBs, builds the skbs, resubmits and delivers them through napi_gro_receive(). The MCU queue stays on the existing RX worker. This enables GRO and moves RX processing into its own kernel thread, parallelising the datapath. On mt7921u at HE-MCS 11 (2x2, 80 MHz; fast.com, multiple streams) this averages ~588 Mbit/s, versus ~424 Mbit/s when the same napi is instead driven manually from the RX worker, and ~380 Mbit/s for the unmodified driver. Suggested-by: Lorenzo Bianconi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Filip Bakreski Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260609105301.196302-1-phial@phiality.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/usb.c | 64 +++++++++++++++++++++--- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index d9638a9b749b..e54b35e53b6a 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -580,7 +580,11 @@ static void mt76u_complete_rx(struct urb *urb) q->head = (q->head + 1) % q->ndesc; q->queued++; - mt76_worker_schedule(&dev->usb.rx_worker); + + if (q == &dev->q_rx[MT_RXQ_MAIN]) + napi_schedule(&dev->napi[MT_RXQ_MAIN]); + else + mt76_worker_schedule(&dev->usb.rx_worker); out: spin_unlock_irqrestore(&q->lock, flags); } @@ -618,11 +622,23 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) } mt76u_submit_rx_buf(dev, qid, urb); } - if (qid == MT_RXQ_MAIN) { - local_bh_disable(); - mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL); - local_bh_enable(); - } +} + +/* Threaded NAPI poll for the MAIN RX queue: drain URBs, build skbs, resubmit, + * then deliver through napi_gro_receive() and let napi_complete() flush GRO. + */ +static int mt76u_napi_poll(struct napi_struct *napi, int budget) +{ + struct mt76_dev *dev = mt76_priv(napi->dev); + + rcu_read_lock(); + mt76u_process_rx_queue(dev, &dev->q_rx[MT_RXQ_MAIN]); + mt76_rx_poll_complete(dev, MT_RXQ_MAIN, napi); + rcu_read_unlock(); + + napi_complete(napi); + + return 0; } static void mt76u_rx_worker(struct mt76_worker *w) @@ -632,8 +648,13 @@ static void mt76u_rx_worker(struct mt76_worker *w) int i; rcu_read_lock(); - mt76_for_each_q_rx(dev, i) + mt76_for_each_q_rx(dev, i) { + /* MT_RXQ_MAIN is serviced by the threaded NAPI poll */ + if (i == MT_RXQ_MAIN) + continue; + mt76u_process_rx_queue(dev, &dev->q_rx[i]); + } rcu_read_unlock(); } @@ -731,6 +752,13 @@ void mt76u_stop_rx(struct mt76_dev *dev) for (j = 0; j < q->ndesc; j++) usb_poison_urb(q->entry[j].urb); } + + /* The MAIN queue napi stays enabled for the device lifetime. The URBs + * are now poisoned, so mt76u_complete_rx() can no longer reschedule it; + * just drain any in-flight poll before the caller frees or resets. + */ + if (dev->napi_dev) + napi_synchronize(&dev->napi[MT_RXQ_MAIN]); } EXPORT_SYMBOL_GPL(mt76u_stop_rx); @@ -1051,6 +1079,13 @@ void mt76u_queues_deinit(struct mt76_dev *dev) mt76u_stop_rx(dev); mt76u_stop_tx(dev); + if (dev->napi_dev) { + napi_disable(&dev->napi[MT_RXQ_MAIN]); + netif_napi_del(&dev->napi[MT_RXQ_MAIN]); + free_netdev(dev->napi_dev); + dev->napi_dev = NULL; + } + mt76u_free_rx(dev); mt76u_free_tx(dev); } @@ -1078,6 +1113,7 @@ int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf, { struct usb_device *udev = interface_to_usbdev(intf); struct mt76_usb *usb = &dev->usb; + struct mt76_dev **priv; int err; INIT_WORK(&usb->stat_work, mt76u_tx_status_data); @@ -1115,6 +1151,20 @@ int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf, sched_set_fifo_low(usb->rx_worker.task); sched_set_fifo_low(usb->status_worker.task); + /* threaded NAPI on a dummy netdev (reusing mt76_dev's napi_dev/napi[]) + * services the MAIN RX queue and gives the RX path GRO + */ + dev->napi_dev = alloc_netdev_dummy(sizeof(struct mt76_dev *)); + if (!dev->napi_dev) + return -ENOMEM; + + priv = netdev_priv(dev->napi_dev); + *priv = dev; + strscpy(dev->napi_dev->name, "mt76u-rx", sizeof(dev->napi_dev->name)); + dev->napi_dev->threaded = 1; + netif_napi_add(dev->napi_dev, &dev->napi[MT_RXQ_MAIN], mt76u_napi_poll); + napi_enable(&dev->napi[MT_RXQ_MAIN]); + return 0; } EXPORT_SYMBOL_GPL(__mt76u_init); From a7c71a346591884eefbe5b3f52cd440671f46505 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 24 Jun 2026 19:18:26 -0500 Subject: [PATCH 226/403] wifi: mt76: mt792x: advertise mgmt frame registration Advertise multicast management frame registration support so userspace can subscribe to multicast management and action frames. This capability is required for NAN discovery and related operations. Co-developed-by: Stella Liu Signed-off-by: Stella Liu Co-developed-by: Jeremy Yu Signed-off-by: Jeremy Yu Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260625001834.475094-2-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 9bd679b7e889..8fc643fc0dfe 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -719,6 +719,7 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS); ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); ieee80211_hw_set(hw, HAS_RATE_CONTROL); From 9080164f3b6db2ef56043440615fa6392e4098c7 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 24 Jun 2026 19:18:27 -0500 Subject: [PATCH 227/403] wifi: mt76: mt7925: guard BSS capability lookups mt7925 BSS setup may dereference missing channel data or query HE 6 GHz capabilities for an iftype without HE support. Guard both lookups before adding NAN paths that can use partially configured BSS state. Co-developed-by: Stella Liu Signed-off-by: Stella Liu Co-developed-by: Jeremy Yu Signed-off-by: Jeremy Yu Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260625001834.475094-3-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index bc6b6fc2d6f5..9795ac27f28f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -2400,11 +2400,18 @@ void mt7925_mcu_bss_rlm_tlv(struct sk_buff *skb, struct mt76_phy *phy, { struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &link_conf->chanreq.oper; - int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2; - enum nl80211_band band = chandef->chan->band; struct bss_rlm_tlv *req; + enum nl80211_band band; + int freq1, freq2; struct tlv *tlv; + if (WARN_ON_ONCE(!chandef || !chandef->chan)) + return; + + freq1 = chandef->center_freq1; + freq2 = chandef->center_freq2; + band = chandef->chan->band; + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_RLM, sizeof(*req)); req = (struct bss_rlm_tlv *)tlv; req->control_channel = chandef->chan->hw_value; @@ -2542,8 +2549,8 @@ mt7925_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif, enum nl80211_band band, struct ieee80211_link_sta *link_sta) { - struct ieee80211_he_6ghz_capa *he_6ghz_capa; - const struct ieee80211_sta_eht_cap *eht_cap; + struct ieee80211_he_6ghz_capa *he_6ghz_capa = NULL; + const struct ieee80211_sta_eht_cap *eht_cap = NULL; __le16 capa = 0; u8 mode = 0; @@ -2551,11 +2558,18 @@ mt7925_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif, he_6ghz_capa = &link_sta->he_6ghz_capa; eht_cap = &link_sta->eht_cap; } else { + const struct ieee80211_sta_he_cap *he_cap; struct ieee80211_supported_band *sband; sband = phy->hw->wiphy->bands[band]; - capa = ieee80211_get_he_6ghz_capa(sband, vif->type); - he_6ghz_capa = (struct ieee80211_he_6ghz_capa *)&capa; + + he_cap = (band == NL80211_BAND_6GHZ) ? + ieee80211_get_he_iftype_cap(sband, vif->type) : NULL; + + if (he_cap) { + capa = ieee80211_get_he_6ghz_capa(sband, vif->type); + he_6ghz_capa = (struct ieee80211_he_6ghz_capa *)&capa; + } eht_cap = ieee80211_get_eht_iftype_cap(sband, vif->type); } From b7ab9f780dc8f6cc9ce30333e9f131ed3419c6b3 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 24 Jun 2026 19:18:28 -0500 Subject: [PATCH 228/403] wifi: mt76: connac: add NAN connection type Introduce a dedicated NAN connection type for connac firmware and use it for NAN interface device, BSS and station records. Add the common NAN MCU command and event IDs used by mt7925. Co-developed-by: Stella Liu Signed-off-by: Stella Liu Co-developed-by: Jeremy Yu Signed-off-by: Jeremy Yu Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260625001834.475094-4-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt76_connac_mcu.c | 14 ++++++++++++++ .../net/wireless/mediatek/mt76/mt76_connac_mcu.h | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 2cc5a5b67f30..8f56f568fa0b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -465,6 +465,10 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb, basic->conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC); basic->aid = cpu_to_le16(link_sta->sta->aid); break; + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + basic->conn_type = cpu_to_le32(CONNECTION_NAN); + break; default: WARN_ON(1); break; @@ -1260,6 +1264,11 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, case NL80211_IFTYPE_ADHOC: basic_req.basic.conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC); break; + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + basic_req.basic.conn_type = cpu_to_le32(CONNECTION_NAN); + basic_req.basic.conn_state = !enable; + break; default: WARN_ON(1); break; @@ -1670,6 +1679,11 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, case NL80211_IFTYPE_ADHOC: basic_req.basic.conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC); break; + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + basic_req.basic.conn_type = cpu_to_le32(CONNECTION_NAN); + basic_req.basic.active = enable; + break; default: WARN_ON(1); break; diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 6f554d78da39..8022ffd7af5a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -901,6 +901,7 @@ enum { #define NETWORK_P2P BIT(17) #define NETWORK_IBSS BIT(18) #define NETWORK_WDS BIT(21) +#define NETWORK_NAN BIT(22) #define SCAN_FUNC_RANDOM_MAC BIT(0) #define SCAN_FUNC_RNR_SCAN BIT(3) @@ -913,6 +914,7 @@ enum { #define CONNECTION_IBSS_ADHOC (STA_TYPE_ADHOC | NETWORK_IBSS) #define CONNECTION_WDS (STA_TYPE_WDS | NETWORK_WDS) #define CONNECTION_INFRA_BC (STA_TYPE_BC | NETWORK_INFRA) +#define CONNECTION_NAN (NETWORK_NAN) #define CONN_STATE_DISCONNECT 0 #define CONN_STATE_CONNECT 1 @@ -1099,6 +1101,7 @@ enum { MCU_UNI_EVENT_THERMAL = 0x35, MCU_UNI_EVENT_RSSI_MONITOR = 0x41, MCU_UNI_EVENT_NIC_CAPAB = 0x43, + MCU_UNI_EVENT_NAN = 0x56, MCU_UNI_EVENT_WED_RRO = 0x57, MCU_UNI_EVENT_PER_STA_INFO = 0x6d, MCU_UNI_EVENT_ALL_STA_INFO = 0x6e, @@ -1345,6 +1348,7 @@ enum { MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40, MCU_UNI_CMD_RSSI_MONITOR = 0x41, MCU_UNI_CMD_TESTMODE_CTRL = 0x46, + MCU_UNI_CMD_NAN = 0x56, MCU_UNI_CMD_RRO = 0x57, MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58, MCU_UNI_CMD_PER_STA_INFO = 0x6d, From a5487a6824068cba6fab02f3e5186940a59275ed Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 24 Jun 2026 19:18:29 -0500 Subject: [PATCH 229/403] wifi: mt76: mt7925: add NAN MCU helpers Add the mt7925 NAN MCU ABI and helpers for enable, disable, configuration updates, availability updates and peer schedule commands. Upper-layer integration is added by later patches. Co-developed-by: Stella Liu Signed-off-by: Stella Liu Co-developed-by: Jeremy Yu Signed-off-by: Jeremy Yu Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260625001834.475094-5-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7925/Makefile | 2 +- .../net/wireless/mediatek/mt76/mt7925/nan.c | 927 ++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/nan.h | 419 ++++++++ .../net/wireless/mediatek/mt76/mt7925/regd.c | 30 + .../net/wireless/mediatek/mt76/mt7925/regd.h | 3 + drivers/net/wireless/mediatek/mt76/mt792x.h | 38 + 6 files changed, 1418 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/nan.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/nan.h diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile index 8f1078ce3231..f9dcc0bba393 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile +++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o obj-$(CONFIG_MT7925E) += mt7925e.o obj-$(CONFIG_MT7925U) += mt7925u.o -mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o +mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o mt7925e-y := pci.o pci_mac.o pci_mcu.o mt7925u-y := usb.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.c b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c new file mode 100644 index 000000000000..74db344a6796 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c @@ -0,0 +1,927 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2025-2026 MediaTek Inc. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt7925.h" +#include "mcu.h" +#include "nan.h" +#include "regd.h" + +static void mt7925_nan_set_5g_channel(struct mt792x_dev *dev, + struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + struct ieee80211_channel *chan; + u32 ch5g = 0; + + chan = conf->band_cfgs[NL80211_BAND_5GHZ].chan; + + if (!chan) + return; + + if (!mt7925_regd_is_valid_channel(dev, NL80211_BAND_5GHZ, chan)) + return; + + req->config_5g_channel = 1; + + if (chan->hw_value == NAN_5G_LOW_DISC_CHANNEL) + ch5g |= BIT(0); + else if (chan->hw_value == NAN_5G_HIGH_DISC_CHANNEL) + ch5g |= BIT(1); + + req->channel_5g_val = cpu_to_le32(ch5g); +} + +static void mt7925_nan_set_cluster_id(struct mt7925_nan_enable_req_tlv *req, + const u8 *cluster_id) +{ + if (!cluster_id) + return; + + req->cluster_high = cpu_to_le16(cluster_id[4] | cluster_id[5] << 8); + req->cluster_low = cpu_to_le16((u16)cluster_id[3]); +} + +static void mt7925_nan_set_dw_interval(struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + if (conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval > 0) { + req->config_dw.config_2dot4g_dw_band = 1; + req->config_dw.dw_2dot4g_interval_val = + cpu_to_le32(conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval); + } + + if (conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval > 0) { + req->config_dw.config_5g_dw_band = 1; + req->config_dw.dw_5g_interval_val = + cpu_to_le32(conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval); + } +} + +static void mt7925_nan_set_disc_beacon(struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + if (conf->discovery_beacon_interval > 0) { + req->config_2dot4g_beacons = true; + req->beacon_2dot4g_val = conf->discovery_beacon_interval; + } +} + +static void mt7925_nan_set_rssi_thresholds(struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + if (conf->band_cfgs[NL80211_BAND_2GHZ].chan) { + req->config_2dot4g_rssi_close = 1; + req->rssi_close_2dot4g_val = + abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close); + req->config_2dot4g_rssi_middle = 1; + req->rssi_middle_2dot4g_val = + abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle); + } + + if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) { + req->config_5g_rssi_close = 1; + req->rssi_close_5g_val = + abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close); + req->config_5g_rssi_middle = 1; + req->rssi_middle_5g_val = + abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle); + } +} + +static void mt7925_nan_set_scan_params(struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + req->scan_params_val.scan_period[0] = + cpu_to_le16(conf->scan_period < 255 ? conf->scan_period : 255); + req->scan_params_val.dwell_time[0] = + conf->scan_dwell_time < 255 ? conf->scan_dwell_time : 255; +} + +static u16 +mt7925_nan_avail_attr_ctrl(const struct ieee80211_nan_sched_cfg *sched) +{ + if (sched->avail_blob_len < NAN_AVAIL_ATTR_CTRL_OFFSET + 2) + return 0; + + return sched->avail_blob[NAN_AVAIL_ATTR_CTRL_OFFSET] | + sched->avail_blob[NAN_AVAIL_ATTR_CTRL_OFFSET + 1] << 8; +} + +static void +mt7925_nan_update_conf(struct mt792x_vif *mvif, + const struct cfg80211_nan_conf *conf) +{ + mvif->nan.conf.master_pref = conf->master_pref; + mvif->nan.conf.bands = conf->bands; + mvif->nan.conf.discovery_beacon_interval = + conf->discovery_beacon_interval; + mvif->nan.conf.enable_dw_notification = + conf->enable_dw_notification; + + memcpy(mvif->nan.conf.cluster_id, conf->cluster_id, ETH_ALEN); +} + +int mt7925_nan_enable(struct ieee80211_vif *vif, + struct mt792x_dev *dev, + struct cfg80211_nan_conf *conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt76_dev *mdev = &dev->mt76; + struct { + u8 rsv[4]; + struct mt7925_nan_enable_req_tlv nan_req_tlv; + } nan_cmd = { + .rsv = { 0 }, + .nan_req_tlv = { + .tag = cpu_to_le16(NAN_UNI_CMD_ENABLE_REQUEST), + .len = cpu_to_le16(sizeof(struct mt7925_nan_enable_req_tlv)), + .config_random_factor_force = 0, + .random_factor_force_val = 0, + .config_hop_count_force = 0, + .hop_count_force_val = 0, + }, + }; + struct mt7925_nan_enable_req_tlv *p_nan_req_tlv = &nan_cmd.nan_req_tlv; + + if (!vif || !dev || !conf) + return -EINVAL; + + p_nan_req_tlv->master_pref = conf->master_pref; + + mt7925_nan_set_5g_channel(dev, p_nan_req_tlv, conf); + mt7925_nan_set_cluster_id(p_nan_req_tlv, conf->cluster_id); + mt7925_nan_set_dw_interval(p_nan_req_tlv, conf); + mt7925_nan_set_disc_beacon(p_nan_req_tlv, conf); + mt7925_nan_set_rssi_thresholds(p_nan_req_tlv, conf); + mt7925_nan_set_scan_params(p_nan_req_tlv, conf); + + mt7925_nan_update_conf(mvif, conf); + + return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nan_cmd, sizeof(nan_cmd), true); +} + +int mt7925_nan_disable(struct ieee80211_vif *vif, struct mt792x_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + struct { + u8 rsv[4]; + struct tlv nan_dis_tlv; + } nan_cmd = { + .rsv = { 0 }, + .nan_dis_tlv = { + .tag = cpu_to_le16(NAN_UNI_CMD_DISABLE_REQUEST), + .len = cpu_to_le16(sizeof(struct tlv)), + }, + }; + + if (!dev) + return -EINVAL; + + return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nan_cmd, sizeof(nan_cmd), true); +} + +static int +mt7925_nan_mp_tlv(struct sk_buff *skb, u8 master_pref) +{ + struct mt7925_nan_master_preference_tlv *mp_tlv = NULL; + struct tlv *tlv = NULL; + + if (!skb) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_MASTER_PREFERENCE, + sizeof(struct mt7925_nan_master_preference_tlv)); + if (!tlv) + return -ENOMEM; + + mp_tlv = (struct mt7925_nan_master_preference_tlv *)tlv; + + if (master_pref > NAN_MAX_MASTER_PREFERENCE) + return 0; + + mp_tlv->master_preference = master_pref; + + return 0; +} + +static int +mt7925_nan_dw_tlv(struct sk_buff *skb, struct cfg80211_nan_conf *conf) +{ + struct mt7925_nan_dw_interval_tlv *dw_tlv = NULL; + struct tlv *tlv = NULL; + u16 interval; + + if (!skb || !conf) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_DW_INTERVAL, + sizeof(struct mt7925_nan_dw_interval_tlv)); + + if (!tlv) + return -ENOMEM; + + dw_tlv = (struct mt7925_nan_dw_interval_tlv *)tlv; + + /* Set DW interval for 2.4GHz and 5GHz bands if available */ + if (conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval > 0) { + dw_tlv->dw_interval = conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval; + } else if (conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval > 0) { + dw_tlv->dw_interval = conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval; + } else { + /* Fallback to a default value or log a warning */ + dw_tlv->dw_interval = NAN_DEFAULT_DW_INTERVAL; + } + + /* Validate and set NAN Discovery Beacon Interval */ + interval = conf->discovery_beacon_interval > 0 ? + conf->discovery_beacon_interval : + NAN_DEFAULT_DISC_BCN_INTERVAL; + + dw_tlv->disc_bcn_interval = cpu_to_le16(interval); + + return 0; +} + +static int +mt7925_nan_cluster_id_tlv(struct sk_buff *skb, const u8 *cluster_id) +{ + struct mt7925_nan_cluster_id_tlv *cluster_tlv = NULL; + struct tlv *tlv = NULL; + + if (!skb || !cluster_id) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_CLUSTER_ID, + sizeof(struct mt7925_nan_cluster_id_tlv)); + + if (!tlv) + return -ENOMEM; + + cluster_tlv = (struct mt7925_nan_cluster_id_tlv *)tlv; + + memcpy(cluster_tlv->cluster_id, cluster_id, ETH_ALEN); + + return 0; +} + +static int +mt7925_nan_sync_rssi_tlv(struct sk_buff *skb, struct cfg80211_nan_conf *conf) +{ + struct mt7925_nan_sync_rssi_tlv *rssi_tlv = NULL; + struct tlv *tlv = NULL; + + if (!skb || !conf) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_SYNC_RSSI, + sizeof(struct mt7925_nan_sync_rssi_tlv)); + + if (!tlv) + return -ENOMEM; + + rssi_tlv = (struct mt7925_nan_sync_rssi_tlv *)tlv; + + if (conf->band_cfgs[NL80211_BAND_2GHZ].chan) { + rssi_tlv->rssi_close_2g = + conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close; + rssi_tlv->rssi_middle_2g = + conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle; + } + + if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) { + rssi_tlv->rssi_close_5g = + conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close; + rssi_tlv->rssi_middle_5g = + conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle; + } + + return 0; +} + +int mt7925_nan_change_configure(struct ieee80211_vif *vif, + struct mt792x_dev *dev, + struct cfg80211_nan_conf *conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt7925_nan_common_hdr *hdr = NULL; + struct mt76_dev *mdev = &dev->mt76; + struct sk_buff *skb = NULL; + + if (!vif || !dev || !conf) + return -EINVAL; + + skb = mt76_mcu_msg_alloc(mdev, NULL, MT7925_NAN_CONF_MAX_SIZE); + if (!skb) + return -ENOMEM; + + hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); + + if (mt7925_nan_mp_tlv(skb, conf->master_pref) || + mt7925_nan_dw_tlv(skb, conf) || + mt7925_nan_cluster_id_tlv(skb, conf->cluster_id) || + mt7925_nan_sync_rssi_tlv(skb, conf)) { + dev_kfree_skb(skb); + return -ENOMEM; + } + + mt7925_nan_update_conf(mvif, conf); + + return mt76_mcu_skb_send_msg(mdev, skb, + MCU_UNI_CMD(NAN), true); +} + +static void +mt7925_nan_handle_dw_ind(struct mt792x_dev *dev, struct tlv *tlv) +{ + struct ieee80211_channel *chan; + struct nan_rpt_dw_evt *evt; + struct wireless_dev *wdev; + u16 len, channel, dw_num; + struct mt792x_vif *mvif; + enum nl80211_band band; + int freq; + + if (!dev || !tlv) + return; + + len = le16_to_cpu(tlv->len); + if (len < sizeof(*tlv) + sizeof(*evt)) { + dev_warn(dev->mt76.dev, + "nan: short dw event tlv len=%u\n", len); + return; + } + + if (!dev->nan_vif || !ieee80211_vif_nan_started(dev->nan_vif)) + return; + + wdev = ieee80211_vif_to_wdev(dev->nan_vif); + if (!wdev) + return; + + mvif = (struct mt792x_vif *)dev->nan_vif->drv_priv; + if (!mvif->nan.conf.enable_dw_notification) + return; + + evt = (struct nan_rpt_dw_evt *)tlv->data; + channel = le16_to_cpu(evt->channel); + dw_num = le16_to_cpu(evt->dw_num); + + band = channel > 13 ? NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; + freq = ieee80211_channel_to_frequency(channel, band); + chan = ieee80211_get_channel(dev->mt76.hw->wiphy, freq); + if (!chan) { + dev_dbg(dev->mt76.dev, + "nan: no channel for dw end event ch=%u dw=%u\n", + channel, dw_num); + return; + } + + cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL); +} + +static void +mt7925_nan_mcu_handle_de_event(struct mt792x_dev *dev, struct tlv *tlv) +{ + u8 cluster_id[ETH_ALEN] __aligned(2) = {0x50, 0x6f, 0x9a, 0x01, 0x00, 0x00}; + struct mt7925_nan_de_event *de_evt = NULL; + u16 len; + + if (!dev || !tlv) { + if (dev) + dev_warn(dev->mt76.dev, "nan: failed to parse TLV\n"); + return; + } + + len = le16_to_cpu(tlv->len); + if (len < sizeof(*tlv) + sizeof(*de_evt)) { + dev_warn(dev->mt76.dev, + "nan: short de_event tlv len=%u\n", len); + return; + } + + de_evt = (struct mt7925_nan_de_event *)tlv->data; + if (!de_evt) { + dev_warn(dev->mt76.dev, "nan: missing DE event payload\n"); + return; + } + + if (de_evt->event_type == NAN_EVENT_ID_DISC_MAC_ADDR) + return; + + memcpy(cluster_id, de_evt->cluster_id, ETH_ALEN); + + dev_dbg(dev->mt76.dev, "nan: evt=%u cluster=%pM\n", + de_evt->event_type, de_evt->cluster_id); + + if (de_evt->event_type != NAN_EVENT_ID_JOINED_CLUSTER) + return; + + if (!ieee80211_vif_nan_started(dev->nan_vif)) { + dev_warn(dev->mt76.dev, "nan: joined-cluster event but NAN not started\n"); + return; + } + + dev_dbg(dev->mt76.dev, "nan: anchor_master_rank=%*phN\n", + NAN_ANCHOR_MASTER_RANK_NUM, de_evt->anchor_master_rank); + + dev_dbg(dev->mt76.dev, "nan: own_nmi=%pM master_nmi=%pM\n", + de_evt->own_nmi, de_evt->master_nmi); + + ieee80211_nan_cluster_joined(dev->nan_vif, cluster_id, true, GFP_KERNEL); +} + +void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct tlv *tlv; + u32 tlv_len; + + if (!dev || !skb) + return; + + if (skb->len < sizeof(struct mt7925_mcu_rxd) + 4) + return; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + tlv = (struct tlv *)skb->data; + tlv_len = skb->len; + + while (tlv_len >= sizeof(*tlv)) { + u16 len = le16_to_cpu(tlv->len); + + if (len < sizeof(*tlv) || len > tlv_len) + break; + + switch (le16_to_cpu(tlv->tag)) { + case NAN_UNI_EVENT_ID_DE_EVENT_IND: + mt7925_nan_mcu_handle_de_event(dev, tlv); + break; + case NAN_UNI_EVENT_REPORT_DW_END: + mt7925_nan_handle_dw_ind(dev, tlv); + break; + default: + break; + } + + tlv_len -= len; + tlv = (struct tlv *)((u8 *)tlv + len); + } +} + +static int mt7925_nan_avail_ctrl_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif) +{ + struct mt7925_nan_avail_ctrl_tlv *avail_ctrl_tlv; + struct ieee80211_nan_sched_cfg *sched; + struct tlv *tlv; + u8 seq_id = 0; + u16 ctrl = 0; + + if (!skb || !vif) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_UPDATE_AVAILABILITY_CTRL, + sizeof(struct mt7925_nan_avail_ctrl_tlv)); + + if (!tlv) + return -ENOMEM; + + sched = &vif->cfg.nan_sched; + + ctrl = mt7925_nan_avail_attr_ctrl(sched); + if (sched->avail_blob_len >= NAN_AVAIL_ATTR_CTRL_OFFSET + 2) + seq_id = sched->avail_blob[NAN_AVAIL_SEQ_ID_OFFSET]; + + avail_ctrl_tlv = (struct mt7925_nan_avail_ctrl_tlv *)tlv; + avail_ctrl_tlv->avail_ctrl = + cpu_to_le16(ctrl & NAN_AVAIL_CTRL_CHECK_FOR_CHANGED); + avail_ctrl_tlv->seq_id = seq_id; + + return 0; +} + +static u32 mt7925_nan_slot_to_bitmap(struct ieee80211_vif *vif, + struct mt7925_nan_ch_timeline *ch_list) +{ + struct ieee80211_nan_channel **slots = vif->cfg.nan_sched.schedule; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + u32 num_channels = 0; + u32 i, j; + + for (i = 0; i < ARRAY_SIZE(mvif->nan.local_sched); i++) { + struct cfg80211_chan_def *slot_chan = &mvif->nan.local_sched[i]; + struct ieee80211_nan_channel *slot = slots[i]; + bool is_found = false; + + if (slot && !IS_ERR(slot) && slot->chanctx_conf) { + *slot_chan = slot->chanctx_conf->def; + } else { + memset(slot_chan, 0, sizeof(*slot_chan)); + continue; + } + + for (j = 0; j < num_channels; j++) { + u32 raw = le32_to_cpu(ch_list[j].ch_info); + + if (FIELD_GET(NAN_CH_CTRL_PRIMARY_CH, raw) == + slot_chan->chan->hw_value) { + u32 map = le32_to_cpu(ch_list[j].avail_map[0]); + + ch_list[j].avail_map[0] = cpu_to_le32(map | BIT(i)); + le32_add_cpu(&ch_list[j].num, 1); + is_found = true; + break; + } + } + + if (!is_found && num_channels < NAN_TIMELINE_MGMT_CHNL_LIST_NUM) { + ch_list[num_channels].ch_info = + cpu_to_le32(FIELD_PREP(NAN_CH_CTRL_OP_CLASS, + slot->channel_entry[0]) | + FIELD_PREP(NAN_CH_CTRL_PRIMARY_CH, + slot_chan->chan->hw_value)); + ch_list[num_channels].avail_map[0] = cpu_to_le32(BIT(i)); + le32_add_cpu(&ch_list[num_channels].num, 1); + ch_list[num_channels].is_valid++; + num_channels++; + } + } + + return num_channels; +} + +static int mt7925_nan_avail_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif) +{ + struct mt7925_nan_avail_entry_tlv *avail_tlv; + struct ieee80211_nan_sched_cfg *sched; + struct tlv *tlv; + u16 ctrl = 0; + + if (!skb || !vif) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_UPDATE_AVAILABILITY, + sizeof(struct mt7925_nan_avail_entry_tlv)); + + if (!tlv) + return -ENOMEM; + + sched = &vif->cfg.nan_sched; + + ctrl = mt7925_nan_avail_attr_ctrl(sched); + + avail_tlv = (struct mt7925_nan_avail_entry_tlv *)tlv; + avail_tlv->map_id = ctrl & NAN_AVAIL_CTRL_MAPID; + avail_tlv->is_cond_avail = false; + avail_tlv->timeline_idx = 0; + + mt7925_nan_slot_to_bitmap(vif, avail_tlv->ch_list); + + avail_tlv->is_multi_map = false; + + return 0; +} + +void mt7925_nan_local_sched_changed(struct mt792x_dev *dev, + struct ieee80211_vif *vif) +{ + struct mt7925_nan_common_hdr *hdr; + struct mt76_dev *mdev; + struct sk_buff *skb; + + if (!dev || !vif) + return; + + mdev = &dev->mt76; + + skb = mt76_mcu_msg_alloc(mdev, NULL, MT7925_NAN_AVAIL_MAX_SIZE); + if (!skb) + return; + + hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); + + if (mt7925_nan_avail_ctrl_tlv(skb, vif) || + mt7925_nan_avail_tlv(skb, vif)) { + dev_kfree_skb(skb); + return; + } + + mt76_mcu_skb_send_msg(mdev, skb, + MCU_UNI_CMD(NAN), true); +} + +static int mt7925_nan_peer_rec_tlv(struct sk_buff *skb, + struct ieee80211_sta *sta, + struct mt792x_sta *msta, + u8 is_activate) +{ + struct mt7925_nan_sched_manage_peer_rec_tlv *peer_rec_tlv; + struct tlv *tlv; + + if (!skb || !sta || !msta) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_MANAGE_PEER_SCH_RECORD, + sizeof(struct mt7925_nan_sched_manage_peer_rec_tlv)); + + if (!tlv) + return -ENOMEM; + + peer_rec_tlv = (struct mt7925_nan_sched_manage_peer_rec_tlv *)tlv; + peer_rec_tlv->sch_idx = cpu_to_le32(msta->nan_sched.sch_idx); + peer_rec_tlv->is_activate = is_activate; + memcpy(peer_rec_tlv->nmi_addr, sta->addr, ETH_ALEN); + + return 0; +} + +static int mt7925_nan_peer_cap_tlv(struct sk_buff *skb, + struct ieee80211_sta *sta, + struct mt792x_sta *msta) +{ + struct mt7925_nan_sched_update_peer_cap_tlv *peer_cap_tlv; + struct ieee80211_nan_peer_sched *sched; + enum nl80211_band band; + struct tlv *tlv; + u16 primary_ch; + u32 i; + + if (!skb || !sta || !msta) + return -EINVAL; + + sched = sta->nan_sched; + if (!sched) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_UPDATE_PEER_CAPABILITY, + sizeof(struct mt7925_nan_sched_update_peer_cap_tlv)); + + if (!tlv) + return -ENOMEM; + + peer_cap_tlv = (struct mt7925_nan_sched_update_peer_cap_tlv *)tlv; + peer_cap_tlv->sch_idx = cpu_to_le32(msta->nan_sched.sch_idx); + peer_cap_tlv->supported_bands = BIT(NAN_SUPPORTED_BAND_ID_2P4G); + peer_cap_tlv->max_chnl_switch_time = cpu_to_le16(sched->max_chan_switch); + + for (i = 0; i < sched->n_channels; i++) { + if (!sched->channels[i].chanctx_conf) + continue; + + band = sched->channels[i].chanctx_conf->def.chan->band; + primary_ch = + sched->channels[i].chanctx_conf->def.chan->hw_value; + + if (band == NL80211_BAND_2GHZ) + peer_cap_tlv->peer_supported_bands |= + BIT(NAN_SUPPORTED_BN_2G); + else if (primary_ch >= UNII1_LOWER_BOUND && + primary_ch <= UNII1_UPPER_BOUND) + peer_cap_tlv->peer_supported_bands |= + BIT(NAN_SUPPORTED_BN_5G_LOW); + else if (primary_ch >= UNII3_LOWER_BOUND && + primary_ch <= UNII3_UPPER_BOUND) + peer_cap_tlv->peer_supported_bands |= + BIT(NAN_SUPPORTED_BN_5G_HIGH); + } + + return 0; +} + +static void +mt7925_nan_fill_crb_committed(struct mt7925_nan_sched_update_crb_tlv *crb_tlv, + struct ieee80211_nan_peer_sched *sched) +{ + u32 m, slot; + + if (!sched) + return; + + for (m = 0; m < CFG80211_NAN_MAX_PEER_MAPS && + m < NAN_TIMELINE_MGMT_SIZE; m++) { + struct mt7925_nan_sched_timeline *tl = + &crb_tlv->comm_faw_timeline[m]; + struct ieee80211_nan_peer_map *map = &sched->maps[m]; + + if (map->map_id == CFG80211_NAN_INVALID_MAP_ID) + continue; + + tl->map_id = map->map_id; + + /* + * Convert peer schedule slots to FW avail_map bitmap. + * Each bit in avail_map[0] represents one time slot where + * the peer has committed availability. + */ + for (slot = 0; slot < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; + slot++) { + struct ieee80211_nan_channel *ch = map->slots[slot]; + + if (!ch || !ch->chanctx_conf) + continue; + + tl->avail_map[0] |= cpu_to_le32(BIT(slot)); + } + } +} + +static int mt7925_nan_update_crb_tlv(struct sk_buff *skb, + struct ieee80211_sta *sta, + struct mt792x_sta *msta) +{ + struct mt7925_nan_sched_update_crb_tlv *crb_tlv; + struct tlv *tlv; + + if (!skb || !sta || !msta) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_UPDATE_CRB, + sizeof(struct mt7925_nan_sched_update_crb_tlv)); + + if (!tlv) + return -ENOMEM; + + crb_tlv = (struct mt7925_nan_sched_update_crb_tlv *)tlv; + crb_tlv->sch_idx = cpu_to_le32(msta->nan_sched.sch_idx); + crb_tlv->flags = NAN_CRB_USE_DATA_PATH; + crb_tlv->is_use_ranging = false; + crb_tlv->comm_ndc_ctrl.is_valid = false; + + mt7925_nan_fill_crb_committed(crb_tlv, sta->nan_sched); + + return 0; +} + +int mt792x_nan_set_peer_schedule(struct mt792x_dev *dev, + struct ieee80211_sta *sta) +{ + struct mt7925_nan_common_hdr *hdr; + struct mt792x_sta *msta; + struct mt792x_nan *nan; + struct mt76_dev *mdev; + struct sk_buff *skb; + + if (!dev || !sta) + return -EINVAL; + + mdev = &dev->mt76; + + skb = mt76_mcu_msg_alloc(mdev, NULL, MT7925_NAN_PEER_MAX_SIZE); + if (!skb) + return -ENOMEM; + + hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); + + msta = (struct mt792x_sta *)sta->drv_priv; + nan = &msta->vif->nan; + + /* Allocate connection index on first call for this peer */ + if (!msta->nan_sched.idx_assigned) { + int idx = find_first_zero_bit(&nan->conn_bitmap, + NAN_MAX_CONN_CFG); + if (idx >= NAN_MAX_CONN_CFG) { + dev_kfree_skb(skb); + return -ENOSPC; + } + + set_bit(idx, &nan->conn_bitmap); + msta->nan_sched.sch_idx = idx; + msta->nan_sched.idx_assigned = true; + + if (mt7925_nan_peer_rec_tlv(skb, sta, msta, true) || + mt7925_nan_peer_cap_tlv(skb, sta, msta)) { + dev_kfree_skb(skb); + return -ENOMEM; + } + } + + if (mt7925_nan_update_crb_tlv(skb, sta, msta)) { + dev_kfree_skb(skb); + return -ENOMEM; + } + + return mt76_mcu_skb_send_msg(mdev, skb, + MCU_UNI_CMD(NAN), true); +} + +int mt792x_nan_set_peer_rec(struct mt76_dev *mdev, + struct ieee80211_sta *sta) +{ + struct mt7925_nan_common_hdr *hdr; + struct mt792x_sta *msta; + struct mt792x_nan *nan; + struct sk_buff *skb; + + if (!mdev || !sta) + return -EINVAL; + + skb = mt76_mcu_msg_alloc(mdev, NULL, + sizeof(struct mt7925_nan_common_hdr) + + sizeof(struct mt7925_nan_sched_manage_peer_rec_tlv)); + if (!skb) + return -ENOMEM; + + hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); + + msta = (struct mt792x_sta *)sta->drv_priv; + nan = &msta->vif->nan; + + if (!msta->nan_sched.idx_assigned) { + dev_kfree_skb(skb); + return 0; + } + + if (mt7925_nan_peer_rec_tlv(skb, sta, msta, false)) { + dev_kfree_skb(skb); + return -ENOMEM; + } + + clear_bit(msta->nan_sched.sch_idx, &nan->conn_bitmap); + msta->nan_sched.idx_assigned = false; + + return mt76_mcu_skb_send_msg(mdev, skb, + MCU_UNI_CMD(NAN), true); +} + +int mt792x_nan_map_sta_rec(struct mt76_dev *mdev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7925_nan_sched_map_sta_rec_tlv *map_tlv; + struct mt7925_nan_common_hdr *hdr; + struct ieee80211_sta *nmi_sta; + struct mt792x_sta *nmi_msta; + struct mt792x_sta *msta; + u8 nmi_addr[ETH_ALEN]; + struct sk_buff *skb; + int ndp_ctx_id = 0; + struct tlv *tlv; + + if (!mdev || !vif || !sta) + return -EINVAL; + + msta = (struct mt792x_sta *)sta->drv_priv; + + rcu_read_lock(); + nmi_sta = rcu_dereference(sta->nmi); + if (!nmi_sta) { + rcu_read_unlock(); + dev_err(mdev->dev, "NAN: NMI sta not found for NDI sta %pM\n", + sta->addr); + return -EINVAL; + } + + memcpy(nmi_addr, nmi_sta->addr, ETH_ALEN); + nmi_msta = (struct mt792x_sta *)nmi_sta->drv_priv; + + ndp_ctx_id = find_first_zero_bit(&nmi_msta->nan_sched.ndp_ctx_bitmap, + NAN_MAX_NDP_CXT); + if (ndp_ctx_id < NAN_MAX_NDP_CXT) + set_bit(ndp_ctx_id, &nmi_msta->nan_sched.ndp_ctx_bitmap); + else + ndp_ctx_id = 0; + rcu_read_unlock(); + + msta->nan_sched.ndp_ctx_id = ndp_ctx_id; + + skb = mt76_mcu_msg_alloc(mdev, NULL, + sizeof(struct mt7925_nan_common_hdr) + + sizeof(struct mt7925_nan_sched_map_sta_rec_tlv)); + if (!skb) + return -ENOMEM; + + hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_MAP_STA_RECORD, + sizeof(struct mt7925_nan_sched_map_sta_rec_tlv)); + if (!tlv) { + dev_kfree_skb(skb); + return -ENOMEM; + } + + map_tlv = (struct mt7925_nan_sched_map_sta_rec_tlv *)tlv; + memcpy(map_tlv->nmi_addr, nmi_addr, ETH_ALEN); + map_tlv->sta_rec_idx = msta->deflink.wcid.idx; + map_tlv->ndp_ctx_id = ndp_ctx_id; + map_tlv->role_idx = 0; + memcpy(map_tlv->ndi_addr, vif->addr, ETH_ALEN); + + return mt76_mcu_skb_send_msg(mdev, skb, + MCU_UNI_CMD(NAN), true); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.h b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h new file mode 100644 index 000000000000..356d9ef7f664 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h @@ -0,0 +1,419 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2025-2026 MediaTek Inc. */ + +#ifndef __MT7925_NAN_H +#define __MT7925_NAN_H + +#include +#include + +#include "../mt76_connac_mcu.h" + +#define NAN_MAX_SOCIAL_CHANNELS 3 +#define NAN_ANCHOR_MASTER_RANK_NUM 8 +#define NAN_5G_LOW_DISC_CHANNEL 44 +#define NAN_5G_HIGH_DISC_CHANNEL 149 +#define NAN_MAX_MASTER_PREFERENCE 255 +#define NAN_DEFAULT_DW_INTERVAL 1 +#define NAN_DEFAULT_DISC_BCN_INTERVAL 100 +#define NAN_TOTAL_DW 16 +#define NAN_SUPPORTED_2G_FAW_CH_NUM 4 +#define NAN_SUPPORTED_5G_FAW_CH_NUM 4 +#define NAN_TIMELINE_MGMT_SIZE 2 +#define NAN_TIMELINE_MGMT_CHNL_LIST_NUM \ + ((NAN_SUPPORTED_2G_FAW_CH_NUM + \ + NAN_SUPPORTED_5G_FAW_CH_NUM) / NAN_TIMELINE_MGMT_SIZE) +#define NAN_NUM_AVAIL_DB 2 +#define NAN_NDC_ATTRIBUTE_ID_LENGTH 6 +#define NAN_MAX_CONN_CFG 8 +#define NAN_MAX_NDP_CXT 4 + +#define MT7925_NAN_CONF_MAX_SIZE \ + (sizeof(struct mt7925_nan_common_hdr) + \ + sizeof(struct mt7925_nan_master_preference_tlv) + \ + sizeof(struct mt7925_nan_dw_interval_tlv) + \ + sizeof(struct mt7925_nan_cluster_id_tlv) + \ + sizeof(struct mt7925_nan_sync_rssi_tlv)) + +#define MT7925_NAN_AVAIL_MAX_SIZE \ + (sizeof(struct mt7925_nan_common_hdr) + \ + sizeof(struct mt7925_nan_avail_ctrl_tlv) + \ + sizeof(struct mt7925_nan_avail_entry_tlv)) + +#define MT7925_NAN_PEER_MAX_SIZE \ + (sizeof(struct mt7925_nan_common_hdr) + \ + sizeof(struct mt7925_nan_sched_manage_peer_rec_tlv) + \ + sizeof(struct mt7925_nan_sched_update_peer_cap_tlv) + \ + sizeof(struct mt7925_nan_sched_update_crb_tlv)) + +/* NAN Availability Attribute */ +#define NAN_AVAIL_ATTR_ID_OFFSET 0 +#define NAN_AVAIL_ATTR_LEN_OFFSET 1 +#define NAN_AVAIL_SEQ_ID_OFFSET 3 +#define NAN_AVAIL_ATTR_CTRL_OFFSET 4 + +/* NAN Availability Attribute - Attribute Control Field */ +#define NAN_AVAIL_CTRL_MAPID GENMASK(3, 0) +#define NAN_AVAIL_CTRL_COMMIT_CHANGED BIT(4) +#define NAN_AVAIL_CTRL_POTN_CHANGED BIT(5) +#define NAN_AVAIL_CTRL_PUBLIC_AVAIL_CHANGED BIT(6) +#define NAN_AVAIL_CTRL_NDC_CHANGED BIT(7) +#define NAN_AVAIL_CTRL_CHECK_FOR_CHANGED GENMASK(7, 4) + +#define UNII1_LOWER_BOUND 36 +#define UNII1_UPPER_BOUND 50 +#define UNII3_LOWER_BOUND 149 +#define UNII3_UPPER_BOUND 165 + +enum nan_uni_cmd_tag { + NAN_UNI_CMD_SET_MASTER_PREFERENCE = 0, + NAN_UNI_CMD_ENABLE_REQUEST = 7, + NAN_UNI_CMD_DISABLE_REQUEST = 8, + NAN_UNI_CMD_UPDATE_AVAILABILITY = 9, + NAN_UNI_CMD_UPDATE_CRB = 10, + NAN_UNI_CMD_MANAGE_PEER_SCH_RECORD = 12, + NAN_UNI_CMD_MAP_STA_RECORD = 13, + NAN_UNI_CMD_UPDATE_AVAILABILITY_CTRL = 20, + NAN_UNI_CMD_UPDATE_PEER_CAPABILITY = 21, + NAN_UNI_CMD_CHANGE_NMI_ADDRESS = 24, + NAN_UNI_CMD_SET_DW_INTERVAL = 26, + NAN_UNI_CMD_SET_SYNC_RSSI = 39, + NAN_UNI_CMD_SET_CLUSTER_ID = 40, + NAN_UNI_CMD_KEY_MANAGEMENT = 53, +}; + +enum nan_uni_event_tag { + NAN_UNI_EVENT_ID_DE_EVENT_IND = 19, + NAN_UNI_EVENT_REPORT_DW_END = 60, +}; + +enum nan_disc_event_type { + NAN_EVENT_ID_DISC_MAC_ADDR = 0, + NAN_EVENT_ID_JOINED_CLUSTER = 2, +}; + +/* NAN 4.0 Table 79. Device Capability attribute format, Supported Bands */ +enum nan_supported_bands { + NAN_SUPPORTED_BAND_ID_2P4G = 2, + NAN_SUPPORTED_BAND_ID_5G = 4, + NAN_PROPRIETARY_BAND_ID_6G = 6, + NAN_SUPPORTED_BAND_ID_6G = 7, +}; + +enum nan_peer_supported_bands { + NAN_SUPPORTED_BN_2G = 0, + NAN_SUPPORTED_BN_5G_LOW, + NAN_SUPPORTED_BN_5G_HIGH, + NAN_SUPPORTED_BN_6G, + NAN_SUPPORTED_BN_NUM +}; + +#define NAN_CH_CTRL_OP_CLASS GENMASK(15, 8) +#define NAN_CH_CTRL_PRIMARY_CH GENMASK(23, 16) + +#define NAN_CRB_USE_DATA_PATH BIT(0) +#define NAN_CRB_AVAIL_6G_FORMAT GENMASK(2, 1) + +struct mt7925_nan_social_ch_scan_params { + u8 dwell_time[NAN_MAX_SOCIAL_CHANNELS]; + __le16 scan_period[NAN_MAX_SOCIAL_CHANNELS]; +} __packed; + +/* Firmware-reported NAN device information */ +struct nan_dev_info_evt { + u8 is_enabled; + u8 my_addr[ETH_ALEN]; + u8 en_fw_election; + __le32 nan_dev_role; + __le32 nan_dev_state; + u8 mst_preference; + u8 random_factor; + u8 cnt_hop; + u8 cluster_id[ETH_ALEN]; + u8 anchor_mst_addr[ETH_ALEN]; + u8 am_preference; + u8 am_random_factor; + u8 parent_mac[ETH_ALEN]; + u8 parent_am_preference; + u8 parent_am_factor; + __le32 ambtt; + __le32 tsf[2]; + u8 pn_igtk[6]; + u8 pn_bigtk[6]; +}; + +/* Firmware NAN discovery window event */ +struct nan_rpt_dw_evt { + struct nan_dev_info_evt device_info; + __le32 expected_tsf_h; + __le32 expected_tsf_l; + __le32 actual_tsf_h; + __le32 actual_tsf_l; + __le16 channel; + __le16 dw_num; +}; + +struct mt7925_nan_conf_dw { + u8 config_2dot4g_dw_band; + __le32 dw_2dot4g_interval_val; + + u8 config_5g_dw_band; + __le32 dw_5g_interval_val; +} __packed; + +struct mt7925_nan_enable_req_tlv { + __le16 tag; + __le16 len; + + u8 master_pref; + __le16 cluster_low; + __le16 cluster_high; + + u8 config_support_5g; + u8 support_5g_val; + + u8 config_sid_beacon; + u8 sid_beacon_val; + + u8 config_2dot4g_rssi_close; + u8 rssi_close_2dot4g_val; + u8 config_2dot4g_rssi_middle; + u8 rssi_middle_2dot4g_val; + + u8 config_2dot4g_rssi_proximity; + u8 rssi_proximity_2dot4g_val; + u8 config_hop_count_limit; + u8 hop_count_limit_val; + + u8 config_2dot4g_support; + u8 support_2dot4g_val; + + u8 config_2dot4g_beacons; + u8 beacon_2dot4g_val; + + u8 config_2dot4g_sdf; + u8 sdf_2dot4g_val; + + u8 config_5g_beacons; + u8 beacon_5g_val; + + u8 config_5g_sdf; + u8 sdf_5g_val; + + u8 config_5g_rssi_close; + u8 rssi_close_5g_val; + + u8 config_5g_rssi_middle; + u8 rssi_middle_5g_val; + + u8 config_5g_rssi_close_proximity; + u8 rssi_close_proximity_5g_val; + + u8 config_rssi_window_size; + u8 rssi_window_size_val; + + u8 config_oui; + __le32 oui_val; + + u8 config_intf_addr; + u8 intf_addr_val[ETH_ALEN]; + + u8 config_cluster_attribute_val; + + u8 config_scan_params; + struct mt7925_nan_social_ch_scan_params scan_params_val; + + u8 config_random_factor_force; + u8 random_factor_force_val; + + u8 config_hop_count_force; + u8 hop_count_force_val; + + u8 config_24g_channel; + __le32 channel_24g_val; + + u8 config_5g_channel; + __le32 channel_5g_val; + + struct mt7925_nan_conf_dw config_dw; + + u8 config_disc_mac_addr_randomization; + __le32 disc_mac_addr_rand_interval_sec; + + u8 discovery_indication_cfg; + + u8 config_subscribe_sid_beacon; + __le32 subscribe_sid_beacon_val; + + u8 enable_log_slot_statistics; +} __packed __aligned(4); + +struct mt7925_nan_common_hdr { + u8 reserved[4]; +}; + +struct mt7925_nan_master_preference_tlv { + __le16 tag; + __le16 len; + u8 master_preference; + u8 reserved[3]; +} __packed __aligned(4); + +struct mt7925_nan_dw_interval_tlv { + __le16 tag; + __le16 len; + u8 dw_interval; + u8 vendor_ioctl; + __le16 disc_bcn_interval; +} __packed __aligned(4); + +struct mt7925_nan_cluster_id_tlv { + __le16 tag; + __le16 len; + u8 cluster_id[ETH_ALEN]; + u8 reserved[2]; +} __packed __aligned(4); + +struct mt7925_nan_sync_rssi_tlv { + __le16 tag; + __le16 len; + s8 rssi_close_2g; + s8 rssi_middle_2g; + s8 rssi_close_5g; + s8 rssi_middle_5g; +} __packed __aligned(4); + +struct mt7925_nan_de_event { + u8 event_type; + u8 cluster_id[ETH_ALEN]; + u8 anchor_master_rank[NAN_ANCHOR_MASTER_RANK_NUM]; + u8 own_nmi[ETH_ALEN]; + u8 master_nmi[ETH_ALEN]; +}; + +struct mt7925_nan_nmi_addr_tlv { + __le16 tag; + __le16 len; + u8 nmi_addr[ETH_ALEN]; +} __packed __aligned(4); + +struct mt7925_nan_avail_ctrl_tlv { + __le16 tag; + __le16 len; + __le16 avail_ctrl; + u8 seq_id; + u8 reserved[1]; +} __packed __aligned(4); + +struct mt7925_nan_ch_timeline { + u8 is_valid; + u8 reserved[3]; + + __le32 ch_info; + + __le32 num; + __le32 avail_map[NAN_TOTAL_DW]; +}; + +struct mt7925_nan_avail_entry_tlv { + __le16 tag; + __le16 len; + u8 map_id; + u8 is_cond_avail; + u8 timeline_idx; + u8 is_multi_map; + + struct mt7925_nan_ch_timeline ch_list[NAN_TIMELINE_MGMT_CHNL_LIST_NUM]; +} __packed __aligned(4); + +struct mt7925_nan_sched_manage_peer_rec_tlv { + __le16 tag; + __le16 len; + __le32 sch_idx; + u8 is_activate; + u8 nmi_addr[ETH_ALEN]; + u8 reserved[1]; +} __packed __aligned(4); + +struct mt7925_nan_sched_update_peer_cap_tlv { + __le16 tag; + __le16 len; + __le32 sch_idx; + u8 supported_bands; + __le16 max_chnl_switch_time; + u8 peer_supported_bands; +} __packed __aligned(4); + +struct mt7925_nan_sched_timeline { + u8 map_id; + u8 local_map_id; + u8 reserved[2]; + union { + __le32 avail_map[NAN_TOTAL_DW]; + u8 avail_block[NAN_TOTAL_DW * 4]; + }; +}; + +struct mt7925_nan_sched_faw_ndc_timeline { + __le32 avail_map[NAN_TOTAL_DW]; +}; + +struct mt7925_nan_sched_ndc_ctrl { + u8 is_valid; + u8 ndc_id[NAN_NDC_ATTRIBUTE_ID_LENGTH]; + u8 ndc_idx; + struct mt7925_nan_sched_timeline timeline[NAN_NUM_AVAIL_DB]; +}; + +struct mt7925_nan_sched_update_crb_tlv { + __le16 tag; + __le16 len; + __le32 sch_idx; + u8 flags; + u8 is_use_ranging; + u8 reserved[2]; + struct mt7925_nan_sched_timeline comm_ranging_timeline[NAN_TIMELINE_MGMT_SIZE]; + struct mt7925_nan_sched_timeline comm_faw_timeline[NAN_TIMELINE_MGMT_SIZE]; + struct mt7925_nan_sched_ndc_ctrl comm_ndc_ctrl; + struct mt7925_nan_sched_faw_ndc_timeline faw_ndc_timeline[NAN_TIMELINE_MGMT_SIZE]; +} __packed __aligned(4); + +struct mt7925_nan_sched_map_sta_rec_tlv { + __le16 tag; + __le16 len; + u8 nmi_addr[ETH_ALEN]; + u8 sta_rec_idx; + u8 ndp_ctx_id; + + __le32 role_idx; + u8 ndi_addr[ETH_ALEN]; + u8 reserved[2]; +} __packed __aligned(4); + +int mt7925_nan_enable(struct ieee80211_vif *vif, + struct mt792x_dev *dev, + struct cfg80211_nan_conf *conf); + +int mt7925_nan_disable(struct ieee80211_vif *vif, + struct mt792x_dev *dev); + +int mt7925_nan_change_configure(struct ieee80211_vif *vif, + struct mt792x_dev *dev, + struct cfg80211_nan_conf *conf); + +void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb); + +void mt7925_nan_local_sched_changed(struct mt792x_dev *dev, + struct ieee80211_vif *vif); + +int mt792x_nan_set_peer_schedule(struct mt792x_dev *dev, + struct ieee80211_sta *sta); + +int mt792x_nan_set_peer_rec(struct mt76_dev *mdev, + struct ieee80211_sta *sta); + +int mt792x_nan_map_sta_rec(struct mt76_dev *mdev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c index 16f56ee879d4..0235437d11d5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c @@ -217,6 +217,36 @@ mt7925_regd_is_valid_alpha2(const char *alpha2) return false; } +bool +mt7925_regd_is_valid_channel(struct mt792x_dev *dev, + enum nl80211_band band, + struct ieee80211_channel *chan) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct wiphy *wiphy = hw->wiphy; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + int i; + + if (!chan) + return false; + + sband = wiphy->bands[band]; + if (!sband) + return false; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (ch->hw_value == chan->hw_value && + ((ch->flags & IEEE80211_CHAN_DISABLED) == 0)) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(mt7925_regd_is_valid_channel); + int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2) { struct wiphy *wiphy = phy->mt76->hw->wiphy; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.h b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h index 0767f078862e..0b0754cf8ae7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h @@ -13,6 +13,9 @@ void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2); void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req); bool mt7925_regd_clc_supported(struct mt792x_dev *dev); int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2); +bool mt7925_regd_is_valid_channel(struct mt792x_dev *dev, + enum nl80211_band band, + struct ieee80211_channel *chan); int mt7925_regd_init(struct mt792x_phy *phy); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 8c7ecd3ce126..337d6a100236 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -120,6 +120,18 @@ struct mt792x_link_sta { struct ieee80211_link_sta *pri_link; }; +struct mt792x_sta_nan_sched { + u16 committed_dw; + u32 sch_idx; + bool idx_assigned; + unsigned long ndp_ctx_bitmap; + u8 ndp_ctx_id; /* assigned NDP context ID (for NDI sta) */ + struct { + u8 map_id; + struct cfg80211_chan_def chans[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; + } maps[CFG80211_NAN_MAX_PEER_MAPS]; +}; + struct mt792x_sta { struct mt792x_link_sta deflink; /* must be first */ struct mt792x_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; @@ -128,6 +140,9 @@ struct mt792x_sta { u16 valid_links; u8 deflink_id; + + /* NAN peer schedule */ + struct mt792x_sta_nan_sched nan_sched; }; DECLARE_EWMA(rssi, 10, 8); @@ -144,6 +159,25 @@ struct mt792x_bss_conf { unsigned int link_id; }; +struct mt792x_nan_conf { + u8 master_pref; + u8 bands; + u8 cluster_id[ETH_ALEN]; + u32 discovery_beacon_interval; + bool enable_dw_notification; +}; + +struct mt792x_nan { + struct mt792x_nan_conf conf; + + /* Scheduler */ + struct cfg80211_chan_def local_sched[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; + u32 seq_id; + + /* Connection index bitmap, up to NAN_MAX_CONN_CFG peers */ + unsigned long conn_bitmap; +}; + struct mt792x_vif { struct mt792x_bss_conf bss_conf; /* must be first */ struct mt792x_bss_conf __rcu *link_conf[IEEE80211_MLD_MAX_NUM_LINKS]; @@ -158,6 +192,8 @@ struct mt792x_vif { struct work_struct csa_work; struct timer_list csa_timer; + + struct mt792x_nan nan; }; struct mt792x_phy { @@ -296,6 +332,8 @@ struct mt792x_dev { u32 backup_l2; struct ieee80211_chanctx_conf *new_ctx; + + struct ieee80211_vif *nan_vif; }; static inline struct mt792x_bss_conf * From 9824fb4edbf39468f9fa2f1f8c146aca74529c80 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 24 Jun 2026 19:18:30 -0500 Subject: [PATCH 230/403] wifi: mt76: mt7925: add NAN MCU handling Route NAN MCU responses and unsolicited events through the mt7925 MCU path, and handle NAN-specific BSS and station TLVs. Co-developed-by: Stella Liu Signed-off-by: Stella Liu Co-developed-by: Jeremy Yu Signed-off-by: Jeremy Yu Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260625001834.475094-6-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 99 ++++++++++++++++--- 1 file changed, 84 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 9795ac27f28f..8abae7585e7b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -7,10 +7,17 @@ #include "regd.h" #include "mcu.h" #include "mac.h" +#include "nan.h" #define MT_STA_BFER BIT(0) #define MT_STA_BFEE BIT(1) +static bool mt7925_vif_is_nan(struct ieee80211_vif *vif) +{ + return vif->type == NL80211_IFTYPE_NAN || + vif->type == NL80211_IFTYPE_NAN_DATA; +} + int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd, struct sk_buff *skb, int seq) { @@ -48,7 +55,8 @@ int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd, cmd == MCU_UNI_CMD(BSS_INFO_UPDATE) || cmd == MCU_UNI_CMD(STA_REC_UPDATE) || cmd == MCU_UNI_CMD(OFFLOAD) || - cmd == MCU_UNI_CMD(SUSPEND)) { + cmd == MCU_UNI_CMD(SUSPEND) || + cmd == MCU_UNI_CMD(NAN)) { struct mt7925_mcu_uni_event *event; skb_pull(skb, sizeof(*rxd)); @@ -666,6 +674,9 @@ mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev, dev->fw_assert = true; mt76_connac_mcu_coredump_event(&dev->mt76, skb, &dev->coredump); return; + case MCU_UNI_EVENT_NAN: + mt7925_nan_mcu_event(dev, skb); + break; default: break; } @@ -1871,9 +1882,20 @@ mt7925_mcu_sta_phy_tlv(struct sk_buff *skb, tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_PHY, sizeof(*phy)); phy = (struct sta_rec_phy *)tlv; - phy->phy_type = mt76_connac_get_phy_mode_v2(mvif->phy->mt76, vif, - chandef->chan->band, - link_sta); + + if (mt7925_vif_is_nan(vif)) { + enum nl80211_band band = chandef->chan ? chandef->chan->band + : NL80211_BAND_2GHZ; + phy->phy_type = PHY_TYPE_BIT_OFDM | PHY_TYPE_BIT_ERP; + phy->phy_type |= mt76_connac_get_phy_mode_v2(mvif->phy->mt76, vif, + band, + link_sta); + } else { + phy->phy_type = mt76_connac_get_phy_mode_v2(mvif->phy->mt76, vif, + chandef->chan->band, + link_sta); + } + phy->basic_rate = cpu_to_le16((u16)link_conf->basic_rates); if (link_sta->ht_cap.ht_supported) { af = link_sta->ht_cap.ampdu_factor; @@ -1946,11 +1968,15 @@ mt7925_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, mconf = mt792x_vif_to_link(mvif, link_sta->link_id); chandef = mconf->mt76.ctx ? &mconf->mt76.ctx->def : &link_conf->chanreq.oper; - band = chandef->chan->band; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra_info)); ra_info = (struct sta_rec_ra_info *)tlv; + if (mt7925_vif_is_nan(vif)) + band = chandef->chan ? chandef->chan->band : NL80211_BAND_2GHZ; + else + band = chandef->chan->band; + supp_rates = link_sta->supp_rates[band]; if (band == NL80211_BAND_2GHZ) supp_rates = FIELD_PREP(RA_LEGACY_OFDM, supp_rates >> 4) | @@ -2597,6 +2623,29 @@ mt7925_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif, return mode; } +static void +mt7925_mcu_bss_basic_tlv_nan(struct mt76_phy *phy, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct mt76_connac_bss_basic_tlv *basic_req) +{ + u8 mode_2g, mode_5g; + + mode_2g = mt7925_get_phy_mode_ext(phy, vif, NL80211_BAND_2GHZ, + link_sta); + mode_5g = mt7925_get_phy_mode_ext(phy, vif, NL80211_BAND_5GHZ, + link_sta); + basic_req->phymode_ext = mode_2g | mode_5g; + + basic_req->nonht_basic_phy = cpu_to_le16(PHY_TYPE_ERP_INDEX); + + mode_2g = mt76_connac_get_phy_mode(phy, vif, NL80211_BAND_2GHZ, + link_sta); + mode_5g = mt76_connac_get_phy_mode(phy, vif, NL80211_BAND_5GHZ, + link_sta); + basic_req->phymode = (mode_2g | mode_5g) & ~PHY_MODE_B; +} + static void mt7925_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, @@ -2611,7 +2660,7 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb, struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &link_conf->chanreq.oper; - enum nl80211_band band = chandef->chan->band; + enum nl80211_band band = NL80211_BAND_2GHZ; struct mt76_connac_bss_basic_tlv *basic_req; struct tlv *tlv; int conn_type; @@ -2624,16 +2673,25 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb, mconf->mt76.omac_idx; basic_req->hw_bss_idx = idx; - basic_req->phymode_ext = mt7925_get_phy_mode_ext(phy, vif, band, - link_sta); + if (mt7925_vif_is_nan(vif)) { + mt7925_mcu_bss_basic_tlv_nan(phy, vif, link_sta, basic_req); + } else { + band = chandef->chan->band; + basic_req->phymode_ext = mt7925_get_phy_mode_ext(phy, vif, band, + link_sta); - if (band == NL80211_BAND_2GHZ) - basic_req->nonht_basic_phy = cpu_to_le16(PHY_TYPE_ERP_INDEX); - else - basic_req->nonht_basic_phy = cpu_to_le16(PHY_TYPE_OFDM_INDEX); + if (band == NL80211_BAND_2GHZ) + basic_req->nonht_basic_phy = + cpu_to_le16(PHY_TYPE_ERP_INDEX); + else + basic_req->nonht_basic_phy = + cpu_to_le16(PHY_TYPE_OFDM_INDEX); + + memcpy(basic_req->bssid, link_conf->bssid, ETH_ALEN); + basic_req->phymode = mt76_connac_get_phy_mode(phy, vif, band, + link_sta); + } - memcpy(basic_req->bssid, link_conf->bssid, ETH_ALEN); - basic_req->phymode = mt76_connac_get_phy_mode(phy, vif, band, link_sta); basic_req->bcn_interval = cpu_to_le16(link_conf->beacon_int); basic_req->dtim_period = link_conf->dtim_period; basic_req->bmc_tx_wlan_idx = cpu_to_le16(bmc_tx_wlan_idx); @@ -2666,6 +2724,11 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb, basic_req->conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC); basic_req->active = true; break; + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + basic_req->conn_type = cpu_to_le32(CONNECTION_NAN); + basic_req->active = enable; + break; default: WARN_ON(1); break; @@ -2724,10 +2787,11 @@ mt7925_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt792x_phy *phy, struct ieee80211_chanctx_conf *ctx, struct ieee80211_bss_conf *link_conf) { + struct ieee80211_vif *vif = link_conf->vif; struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &link_conf->chanreq.oper; struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); - enum nl80211_band band = chandef->chan->band; + enum nl80211_band band = NL80211_BAND_2GHZ; struct mt76_vif_link *mvif = &mconf->mt76; struct bss_rate_tlv *bmc; struct tlv *tlv; @@ -2738,6 +2802,11 @@ mt7925_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt792x_phy *phy, bmc = (struct bss_rate_tlv *)tlv; + if (mt7925_vif_is_nan(vif)) + band = chandef->chan ? chandef->chan->band : NL80211_BAND_2GHZ; + else + band = chandef->chan->band; + if (band == NL80211_BAND_2GHZ) bmc->basic_rate = cpu_to_le16(HR_DSSS_ERP_BASIC_RATE); else From 4ee3d2a5f2cd5b6d0ba0d997913c1b763ef4d5c5 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 24 Jun 2026 19:18:31 -0500 Subject: [PATCH 231/403] wifi: mt76: add init_wiphy callback Add an optional callback for drivers to finalize wiphy state after mt76 has initialized the supported bands and before registration. Co-developed-by: Stella Liu Signed-off-by: Stella Liu Co-developed-by: Jeremy Yu Signed-off-by: Jeremy Yu Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260625001834.475094-7-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 7 +++++++ drivers/net/wireless/mediatek/mt76/mt76.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 13c4e8abe281..c4cbf7195b80 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -681,6 +681,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size, dev = hw->priv; dev->hw = hw; dev->dev = pdev; + dev->init_wiphy = NULL; dev->drv = drv_ops; dev->dma_dev = pdev; @@ -779,6 +780,12 @@ int mt76_register_device(struct mt76_dev *dev, bool vht, mt76_check_sband(&dev->phy, &phy->sband_5g, NL80211_BAND_5GHZ); mt76_check_sband(&dev->phy, &phy->sband_6g, NL80211_BAND_6GHZ); + if (dev->init_wiphy) { + ret = dev->init_wiphy(dev); + if (ret) + return ret; + } + if (IS_ENABLED(CONFIG_MT76_LEDS)) { ret = mt76_led_init(phy); if (ret) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 3822eb8fd88f..6cff136407d8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -940,6 +940,9 @@ struct mt76_dev { const struct mt76_bus_ops *bus; const struct mt76_driver_ops *drv; const struct mt76_mcu_ops *mcu_ops; + + /* Optional callback to finalize wiphy state before registration. */ + int (*init_wiphy)(struct mt76_dev *dev); struct device *dev; struct device *dma_dev; From 0f3605e4f8de07c1c12271b7f602754e494fec5c Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 24 Jun 2026 19:18:32 -0500 Subject: [PATCH 232/403] wifi: mt76: mt7925: wire up NAN operations Wire mac80211 NAN start, stop and change_conf callbacks to the mt7925 NAN MCU helpers. Track the active NAN vif and notify mac80211 on cluster join events. Initialize NAN PHY capabilities after the supported bands are ready. Co-developed-by: Stella Liu Signed-off-by: Stella Liu Co-developed-by: Jeremy Yu Signed-off-by: Jeremy Yu Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260625001834.475094-8-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/init.c | 29 +++ .../net/wireless/mediatek/mt76/mt7925/main.c | 201 ++++++++++++++- .../net/wireless/mediatek/mt76/mt7925/nan.c | 239 +++++++++++++++--- .../net/wireless/mediatek/mt76/mt7925/nan.h | 2 + drivers/net/wireless/mediatek/mt76/mt792x.h | 3 + 5 files changed, 430 insertions(+), 44 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index e85b0d104fbe..1b44f5c8fb0d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -152,6 +152,33 @@ static int mt7925_init_hardware(struct mt792x_dev *dev) return 0; } +static int mt7925_init_nan_cap(struct mt76_dev *mdev) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + const struct ieee80211_sta_he_cap *he_cap; + struct ieee80211_supported_band *sband; + struct wiphy *wiphy = mdev->hw->wiphy; + + if (!(dev->fw_features & MT792x_FW_CAP_NAN)) + return 0; + + sband = wiphy->bands[NL80211_BAND_2GHZ]; + if (sband) + wiphy->nan_capa.phy.ht = sband->ht_cap; + + sband = wiphy->bands[NL80211_BAND_5GHZ]; + if (sband) + wiphy->nan_capa.phy.vht = sband->vht_cap; + + sband = wiphy->bands[NL80211_BAND_2GHZ]; + he_cap = sband ? ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_NAN) + : NULL; + if (he_cap) + wiphy->nan_capa.phy.he = *he_cap; + + return 0; +} + static void mt7925_init_work(struct work_struct *work) { struct mt792x_dev *dev = container_of(work, struct mt792x_dev, @@ -172,6 +199,8 @@ static void mt7925_init_work(struct work_struct *work) return; } + dev->mt76.init_wiphy = mt7925_init_nan_cap; + ret = mt76_register_device(&dev->mt76, true, mt76_rates, ARRAY_SIZE(mt76_rates)); if (ret) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 2b6cc8e253c0..6be5b60b9bac 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -11,6 +11,7 @@ #include "regd.h" #include "mcu.h" #include "mac.h" +#include "nan.h" static void mt7925_init_he_caps(struct mt792x_phy *phy, enum nl80211_band band, @@ -412,7 +413,8 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev, 0 : mconf->mt76.idx % MT76_CONNAC_MAX_WMM_SETS; mconf->mt76.link_idx = hweight16(mvif->valid_links); - if (mvif->phy->mt76->chandef.chan->band != NL80211_BAND_2GHZ) + if (mvif->phy->mt76->chandef.chan && + mvif->phy->mt76->chandef.chan->band != NL80211_BAND_2GHZ) mconf->mt76.basic_rates_idx = MT792x_BASIC_RATES_TBL + 4; else mconf->mt76.basic_rates_idx = MT792x_BASIC_RATES_TBL; @@ -474,12 +476,32 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) INIT_WORK(&mvif->csa_work, mt7925_csa_work); timer_setup(&mvif->csa_timer, mt792x_csa_timer, 0); + if (vif->type == NL80211_IFTYPE_NAN) + dev->nan_vif = vif; out: mt792x_mutex_release(dev); return ret; } +static void +mt7925_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_bss_conf *mconf; + + mt792x_mutex_acquire(dev); + + if (dev->nan_vif == vif) + dev->nan_vif = NULL; + + mconf = mt792x_link_conf_to_mconf(&vif->bss_conf); + mt792x_mac_link_bss_remove(dev, mconf, &mvif->sta.deflink); + + mt792x_mutex_release(dev); +} + static void mt7925_roc_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) { @@ -1217,20 +1239,37 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev, int mt7925_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, enum mt76_sta_event ev) { + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); struct ieee80211_link_sta *link_sta = &sta->deflink; - if (ev != MT76_STA_EVENT_ASSOC) - return 0; + switch (ev) { + case MT76_STA_EVENT_ASSOC: + if (ieee80211_vif_is_mld(vif)) { + struct mt792x_sta *msta = + (struct mt792x_sta *)sta->drv_priv; - if (ieee80211_vif_is_mld(vif)) { - struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + link_sta = mt792x_sta_to_link_sta(vif, sta, + msta->deflink_id); + mt7925_mac_set_links(mdev, vif); + } - link_sta = mt792x_sta_to_link_sta(vif, sta, msta->deflink_id); - mt7925_mac_set_links(mdev, vif); + mt7925_mac_link_sta_assoc(mdev, vif, link_sta); + break; + case MT76_STA_EVENT_AUTHORIZE: + if (vif->type == NL80211_IFTYPE_NAN_DATA) { + int ret; + + mt792x_mutex_acquire(dev); + ret = mt792x_nan_map_sta_rec(mdev, vif, sta); + mt792x_mutex_release(dev); + + return ret; + } + break; + default: + break; } - mt7925_mac_link_sta_assoc(mdev, vif, link_sta); - return 0; } EXPORT_SYMBOL_GPL(mt7925_mac_sta_event); @@ -1357,6 +1396,36 @@ void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + /* Release NAN peer record before tearing down the STA. */ + if (vif->type == NL80211_IFTYPE_NAN || + vif->type == NL80211_IFTYPE_NAN_DATA) { + int ret = mt792x_nan_set_peer_rec(mdev, sta); + + if (ret) + dev_err(mdev->dev, + "NAN: failed to deactivate peer record: %d\n", + ret); + } + + /* Release NDP context ID for NAN_DATA sta. */ + if (vif->type == NL80211_IFTYPE_NAN_DATA) { + struct ieee80211_sta *nmi_sta; + + rcu_read_lock(); + nmi_sta = rcu_dereference(sta->nmi); + if (nmi_sta) { + struct mt792x_sta *nmi_msta = + (struct mt792x_sta *)nmi_sta->drv_priv; + + if (msta->nan_sched.ndp_ctx_assigned) { + clear_bit(msta->nan_sched.ndp_ctx_id, + &nmi_msta->nan_sched.ndp_ctx_bitmap); + msta->nan_sched.ndp_ctx_assigned = false; + } + } + rcu_read_unlock(); + } + if (ieee80211_vif_is_mld(vif)) { mt7925_mac_sta_remove_links(dev, vif, sta, msta->valid_links); mt7925_mcu_del_dev(mdev, vif); @@ -2062,6 +2131,11 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw, } mt792x_mutex_release(dev); + + if (vif->type == NL80211_IFTYPE_NAN && + changed & BSS_CHANGED_NAN_LOCAL_SCHED) { + mt7925_nan_local_sched_changed(dev, vif); + } } static void mt7925_link_info_changed(struct ieee80211_hw *hw, @@ -2484,12 +2558,115 @@ static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw, } } +static int mt7925_start_nan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_nan_conf *conf) +{ + struct ieee80211_bss_conf *link_conf = &vif->bss_conf; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct ieee80211_channel *chan; + int err = 0; + + mt792x_mutex_acquire(dev); + + chan = conf->band_cfgs[NL80211_BAND_2GHZ].chan; + if (!chan) { + err = -EINVAL; + goto out; + } + + cfg80211_chandef_create(&link_conf->chanreq.oper, chan, + NL80211_CHAN_NO_HT); + + err = mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf, + NULL, true); + if (err < 0) + goto out; + + dev->nan_vif = vif; + + err = mt7925_nan_set_nmi_addr(dev, vif->addr); + if (err) + goto rollback_bss; + + err = mt7925_nan_enable(vif, dev, conf); + if (err) + goto rollback_bss; + + goto out; + +rollback_bss: + dev->nan_vif = NULL; + mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf, NULL, false); + +out: + mt792x_mutex_release(dev); + + return err; +} + +static int mt7925_stop_nan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ieee80211_bss_conf *link_conf = &vif->bss_conf; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err, ret; + + mt792x_mutex_acquire(dev); + + err = mt7925_nan_disable(vif, dev); + + ret = mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf, + NULL, false); + if (!err) + err = ret; + + if (dev->nan_vif == vif) + dev->nan_vif = NULL; + + mt792x_mutex_release(dev); + + return err; +} + +static int mt7925_nan_change_conf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_nan_conf *conf, + u32 changes) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err = 0; + + mt792x_mutex_acquire(dev); + + err = mt7925_nan_change_configure(vif, dev, conf); + + mt792x_mutex_release(dev); + + return err; +} + +static int mt7925_nan_peer_sched_changed(struct ieee80211_hw *hw, + struct ieee80211_sta *sta) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err = 0; + + mt792x_mutex_acquire(dev); + + err = mt792x_nan_set_peer_schedule(dev, sta); + + mt792x_mutex_release(dev); + + return err; +} + const struct ieee80211_ops mt7925_ops = { .tx = mt792x_tx, .start = mt7925_start, .stop = mt792x_stop, .add_interface = mt7925_add_interface, - .remove_interface = mt792x_remove_interface, + .remove_interface = mt7925_remove_interface, .config = mt7925_config, .conf_tx = mt7925_conf_tx, .configure_filter = mt7925_configure_filter, @@ -2553,6 +2730,10 @@ const struct ieee80211_ops mt7925_ops = { .channel_switch = mt7925_channel_switch, .abort_channel_switch = mt7925_abort_channel_switch, .channel_switch_rx_beacon = mt7925_channel_switch_rx_beacon, + .start_nan = mt7925_start_nan, + .stop_nan = mt7925_stop_nan, + .nan_change_conf = mt7925_nan_change_conf, + .nan_peer_sched_changed = mt7925_nan_peer_sched_changed, }; EXPORT_SYMBOL_GPL(mt7925_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.c b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c index 74db344a6796..d260e803d056 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/nan.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c @@ -32,6 +32,8 @@ static void mt7925_nan_set_5g_channel(struct mt792x_dev *dev, if (!mt7925_regd_is_valid_channel(dev, NL80211_BAND_5GHZ, chan)) return; + req->config_support_5g = 1; + req->support_5g_val = 1; req->config_5g_channel = 1; if (chan->hw_value == NAN_5G_LOW_DISC_CHANNEL) @@ -42,6 +44,16 @@ static void mt7925_nan_set_5g_channel(struct mt792x_dev *dev, req->channel_5g_val = cpu_to_le32(ch5g); } +static void mt7925_nan_set_2g_support(struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + if (!conf->band_cfgs[NL80211_BAND_2GHZ].chan) + return; + + req->config_2dot4g_support = 1; + req->support_2dot4g_val = 1; +} + static void mt7925_nan_set_cluster_id(struct mt7925_nan_enable_req_tlv *req, const u8 *cluster_id) { @@ -132,6 +144,38 @@ mt7925_nan_update_conf(struct mt792x_vif *mvif, memcpy(mvif->nan.conf.cluster_id, conf->cluster_id, ETH_ALEN); } +int mt7925_nan_set_nmi_addr(struct mt792x_dev *dev, const u8 *addr) +{ + struct mt76_dev *mdev; + struct { + u8 rsv[4]; + struct mt7925_nan_nmi_addr_tlv nmi_addr_tlv; + } nmi_cmd = { + .rsv = { 0 }, + .nmi_addr_tlv = { + .tag = cpu_to_le16(NAN_UNI_CMD_CHANGE_NMI_ADDRESS), + .len = cpu_to_le16(sizeof(struct mt7925_nan_nmi_addr_tlv)), + }, + }; + int ret; + + if (!dev || !addr) + return -EINVAL; + + if (is_zero_ether_addr(addr) || is_multicast_ether_addr(addr)) { + dev_err(dev->mt76.dev, "NAN: invalid NMI address %pM\n", addr); + return -EINVAL; + } + + mdev = &dev->mt76; + memcpy(nmi_cmd.nmi_addr_tlv.nmi_addr, addr, ETH_ALEN); + + ret = mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nmi_cmd, + sizeof(nmi_cmd), true); + + return ret; +} + int mt7925_nan_enable(struct ieee80211_vif *vif, struct mt792x_dev *dev, struct cfg80211_nan_conf *conf) @@ -153,12 +197,14 @@ int mt7925_nan_enable(struct ieee80211_vif *vif, }, }; struct mt7925_nan_enable_req_tlv *p_nan_req_tlv = &nan_cmd.nan_req_tlv; + int ret; if (!vif || !dev || !conf) return -EINVAL; p_nan_req_tlv->master_pref = conf->master_pref; + mt7925_nan_set_2g_support(p_nan_req_tlv, conf); mt7925_nan_set_5g_channel(dev, p_nan_req_tlv, conf); mt7925_nan_set_cluster_id(p_nan_req_tlv, conf->cluster_id); mt7925_nan_set_dw_interval(p_nan_req_tlv, conf); @@ -168,7 +214,9 @@ int mt7925_nan_enable(struct ieee80211_vif *vif, mt7925_nan_update_conf(mvif, conf); - return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nan_cmd, sizeof(nan_cmd), true); + ret = mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nan_cmd, sizeof(nan_cmd), true); + + return ret; } int mt7925_nan_disable(struct ieee80211_vif *vif, struct mt792x_dev *dev) @@ -428,7 +476,7 @@ mt7925_nan_mcu_handle_de_event(struct mt792x_dev *dev, struct tlv *tlv) if (de_evt->event_type != NAN_EVENT_ID_JOINED_CLUSTER) return; - if (!ieee80211_vif_nan_started(dev->nan_vif)) { + if (!dev->nan_vif || !ieee80211_vif_nan_started(dev->nan_vif)) { dev_warn(dev->mt76.dev, "nan: joined-cluster event but NAN not started\n"); return; } @@ -599,16 +647,21 @@ void mt7925_nan_local_sched_changed(struct mt792x_dev *dev, { struct mt7925_nan_common_hdr *hdr; struct mt76_dev *mdev; + bool deferred; struct sk_buff *skb; + int ret = -ENOMEM; if (!dev || !vif) return; mdev = &dev->mt76; + deferred = vif->cfg.nan_sched.deferred; + + mt792x_mutex_acquire(dev); skb = mt76_mcu_msg_alloc(mdev, NULL, MT7925_NAN_AVAIL_MAX_SIZE); if (!skb) - return; + goto out; hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); memset(hdr, 0, sizeof(*hdr)); @@ -616,11 +669,22 @@ void mt7925_nan_local_sched_changed(struct mt792x_dev *dev, if (mt7925_nan_avail_ctrl_tlv(skb, vif) || mt7925_nan_avail_tlv(skb, vif)) { dev_kfree_skb(skb); - return; + goto out; } - mt76_mcu_skb_send_msg(mdev, skb, - MCU_UNI_CMD(NAN), true); + ret = mt76_mcu_skb_send_msg(mdev, skb, + MCU_UNI_CMD(NAN), true); +out: + mt792x_mutex_release(dev); + + if (deferred) { + if (ret) + dev_err(mdev->dev, + "NAN: local schedule update failed: %d\n", + ret); + + ieee80211_nan_sched_update_done(vif); + } } static int mt7925_nan_peer_rec_tlv(struct sk_buff *skb, @@ -648,6 +712,23 @@ static int mt7925_nan_peer_rec_tlv(struct sk_buff *skb, return 0; } +static u8 mt7925_nan_get_supported_bands(struct mt792x_vif *mvif) +{ + struct wiphy *wiphy; + u8 bands = 0; + + if (!mvif || !mvif->phy) + return BIT(NAN_SUPPORTED_BAND_ID_2P4G); + + wiphy = mvif->phy->mt76->hw->wiphy; + if (wiphy->nan_supported_bands & BIT(NL80211_BAND_2GHZ)) + bands |= BIT(NAN_SUPPORTED_BAND_ID_2P4G); + if (wiphy->nan_supported_bands & BIT(NL80211_BAND_5GHZ)) + bands |= BIT(NAN_SUPPORTED_BAND_ID_5G); + + return bands ?: BIT(NAN_SUPPORTED_BAND_ID_2P4G); +} + static int mt7925_nan_peer_cap_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, struct mt792x_sta *msta) @@ -674,7 +755,8 @@ static int mt7925_nan_peer_cap_tlv(struct sk_buff *skb, peer_cap_tlv = (struct mt7925_nan_sched_update_peer_cap_tlv *)tlv; peer_cap_tlv->sch_idx = cpu_to_le32(msta->nan_sched.sch_idx); - peer_cap_tlv->supported_bands = BIT(NAN_SUPPORTED_BAND_ID_2P4G); + peer_cap_tlv->supported_bands = + mt7925_nan_get_supported_bands(msta->vif); peer_cap_tlv->max_chnl_switch_time = cpu_to_le16(sched->max_chan_switch); for (i = 0; i < sched->n_channels; i++) { @@ -703,38 +785,52 @@ static int mt7925_nan_peer_cap_tlv(struct sk_buff *skb, static void mt7925_nan_fill_crb_committed(struct mt7925_nan_sched_update_crb_tlv *crb_tlv, + struct ieee80211_vif *vif, struct ieee80211_nan_peer_sched *sched) { + struct ieee80211_nan_sched_cfg *local_sched; + u8 local_map_id; u32 m, slot; - if (!sched) + if (!vif || !sched) return; + local_sched = &vif->cfg.nan_sched; + local_map_id = mt7925_nan_avail_attr_ctrl(local_sched) & + NAN_AVAIL_CTRL_MAPID; + for (m = 0; m < CFG80211_NAN_MAX_PEER_MAPS && m < NAN_TIMELINE_MGMT_SIZE; m++) { struct mt7925_nan_sched_timeline *tl = &crb_tlv->comm_faw_timeline[m]; struct ieee80211_nan_peer_map *map = &sched->maps[m]; + u32 avail_map = 0; if (map->map_id == CFG80211_NAN_INVALID_MAP_ID) continue; tl->map_id = map->map_id; + tl->local_map_id = local_map_id; - /* - * Convert peer schedule slots to FW avail_map bitmap. - * Each bit in avail_map[0] represents one time slot where - * the peer has committed availability. - */ for (slot = 0; slot < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; slot++) { - struct ieee80211_nan_channel *ch = map->slots[slot]; + struct ieee80211_nan_channel *local_ch; + struct ieee80211_nan_channel *peer_ch; - if (!ch || !ch->chanctx_conf) + local_ch = local_sched->schedule[slot]; + peer_ch = map->slots[slot]; + + if (!local_ch || !local_ch->chanctx_conf || + !peer_ch || !peer_ch->chanctx_conf) continue; - tl->avail_map[0] |= cpu_to_le32(BIT(slot)); + if (local_ch->chanctx_conf != peer_ch->chanctx_conf) + continue; + + avail_map |= BIT(slot); } + + tl->avail_map[0] = cpu_to_le32(avail_map); } } @@ -760,7 +856,8 @@ static int mt7925_nan_update_crb_tlv(struct sk_buff *skb, crb_tlv->is_use_ranging = false; crb_tlv->comm_ndc_ctrl.is_valid = false; - mt7925_nan_fill_crb_committed(crb_tlv, sta->nan_sched); + mt7925_nan_fill_crb_committed(crb_tlv, msta->vif->phy->dev->nan_vif, + sta->nan_sched); return 0; } @@ -769,10 +866,12 @@ int mt792x_nan_set_peer_schedule(struct mt792x_dev *dev, struct ieee80211_sta *sta) { struct mt7925_nan_common_hdr *hdr; + bool idx_allocated = false; struct mt792x_sta *msta; struct mt792x_nan *nan; struct mt76_dev *mdev; struct sk_buff *skb; + int ret; if (!dev || !sta) return -EINVAL; @@ -801,21 +900,36 @@ int mt792x_nan_set_peer_schedule(struct mt792x_dev *dev, set_bit(idx, &nan->conn_bitmap); msta->nan_sched.sch_idx = idx; msta->nan_sched.idx_assigned = true; + idx_allocated = true; if (mt7925_nan_peer_rec_tlv(skb, sta, msta, true) || mt7925_nan_peer_cap_tlv(skb, sta, msta)) { - dev_kfree_skb(skb); - return -ENOMEM; + ret = -ENOMEM; + goto free_skb; } } if (mt7925_nan_update_crb_tlv(skb, sta, msta)) { - dev_kfree_skb(skb); - return -ENOMEM; + ret = -ENOMEM; + goto free_skb; } - return mt76_mcu_skb_send_msg(mdev, skb, - MCU_UNI_CMD(NAN), true); + ret = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(NAN), true); + if (ret && idx_allocated) + goto clear_idx; + + return ret; + +free_skb: + dev_kfree_skb(skb); + if (!idx_allocated) + return ret; + +clear_idx: + clear_bit(msta->nan_sched.sch_idx, &nan->conn_bitmap); + msta->nan_sched.idx_assigned = false; + + return ret; } int mt792x_nan_set_peer_rec(struct mt76_dev *mdev, @@ -825,6 +939,7 @@ int mt792x_nan_set_peer_rec(struct mt76_dev *mdev, struct mt792x_sta *msta; struct mt792x_nan *nan; struct sk_buff *skb; + int ret; if (!mdev || !sta) return -EINVAL; @@ -851,11 +966,14 @@ int mt792x_nan_set_peer_rec(struct mt76_dev *mdev, return -ENOMEM; } + ret = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(NAN), true); + if (ret) + return ret; + clear_bit(msta->nan_sched.sch_idx, &nan->conn_bitmap); msta->nan_sched.idx_assigned = false; - return mt76_mcu_skb_send_msg(mdev, skb, - MCU_UNI_CMD(NAN), true); + return 0; } int mt792x_nan_map_sta_rec(struct mt76_dev *mdev, @@ -866,16 +984,21 @@ int mt792x_nan_map_sta_rec(struct mt76_dev *mdev, struct mt7925_nan_common_hdr *hdr; struct ieee80211_sta *nmi_sta; struct mt792x_sta *nmi_msta; + struct mt792x_vif *mvif; struct mt792x_sta *msta; u8 nmi_addr[ETH_ALEN]; struct sk_buff *skb; int ndp_ctx_id = 0; + int ret = -ENOMEM; + struct mt792x_dev *dev; struct tlv *tlv; if (!mdev || !vif || !sta) return -EINVAL; + dev = container_of(mdev, struct mt792x_dev, mt76); msta = (struct mt792x_sta *)sta->drv_priv; + mvif = (struct mt792x_vif *)vif->drv_priv; rcu_read_lock(); nmi_sta = rcu_dereference(sta->nmi); @@ -889,21 +1012,51 @@ int mt792x_nan_map_sta_rec(struct mt76_dev *mdev, memcpy(nmi_addr, nmi_sta->addr, ETH_ALEN); nmi_msta = (struct mt792x_sta *)nmi_sta->drv_priv; + if (!nmi_msta->nan_sched.idx_assigned) { + if (!nmi_sta->nan_sched) { + rcu_read_unlock(); + dev_err(mdev->dev, + "NAN: peer schedule missing for NDI sta %pM\n", + sta->addr); + return -EAGAIN; + } + + rcu_read_unlock(); + ret = mt792x_nan_set_peer_schedule(dev, nmi_sta); + if (ret) + return ret; + + rcu_read_lock(); + nmi_sta = rcu_dereference(sta->nmi); + if (!nmi_sta) { + rcu_read_unlock(); + dev_err(mdev->dev, + "NAN: NMI sta not found for NDI sta %pM\n", + sta->addr); + return -EINVAL; + } + + nmi_msta = (struct mt792x_sta *)nmi_sta->drv_priv; + } + ndp_ctx_id = find_first_zero_bit(&nmi_msta->nan_sched.ndp_ctx_bitmap, NAN_MAX_NDP_CXT); - if (ndp_ctx_id < NAN_MAX_NDP_CXT) - set_bit(ndp_ctx_id, &nmi_msta->nan_sched.ndp_ctx_bitmap); - else - ndp_ctx_id = 0; + if (ndp_ctx_id >= NAN_MAX_NDP_CXT) { + rcu_read_unlock(); + return -ENOSPC; + } + + set_bit(ndp_ctx_id, &nmi_msta->nan_sched.ndp_ctx_bitmap); rcu_read_unlock(); msta->nan_sched.ndp_ctx_id = ndp_ctx_id; + msta->nan_sched.ndp_ctx_assigned = true; skb = mt76_mcu_msg_alloc(mdev, NULL, sizeof(struct mt7925_nan_common_hdr) + sizeof(struct mt7925_nan_sched_map_sta_rec_tlv)); if (!skb) - return -ENOMEM; + goto clear_ndp_ctx; hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); memset(hdr, 0, sizeof(*hdr)); @@ -912,16 +1065,34 @@ int mt792x_nan_map_sta_rec(struct mt76_dev *mdev, sizeof(struct mt7925_nan_sched_map_sta_rec_tlv)); if (!tlv) { dev_kfree_skb(skb); - return -ENOMEM; + ret = -ENOMEM; + goto clear_ndp_ctx; } map_tlv = (struct mt7925_nan_sched_map_sta_rec_tlv *)tlv; memcpy(map_tlv->nmi_addr, nmi_addr, ETH_ALEN); map_tlv->sta_rec_idx = msta->deflink.wcid.idx; map_tlv->ndp_ctx_id = ndp_ctx_id; - map_tlv->role_idx = 0; + map_tlv->role_idx = cpu_to_le32(mvif->bss_conf.mt76.idx); memcpy(map_tlv->ndi_addr, vif->addr, ETH_ALEN); - return mt76_mcu_skb_send_msg(mdev, skb, - MCU_UNI_CMD(NAN), true); + ret = mt76_mcu_skb_send_msg(mdev, skb, + MCU_UNI_CMD(NAN), true); + if (ret) + goto clear_ndp_ctx; + + return 0; + +clear_ndp_ctx: + rcu_read_lock(); + nmi_sta = rcu_dereference(sta->nmi); + if (nmi_sta) { + nmi_msta = (struct mt792x_sta *)nmi_sta->drv_priv; + clear_bit(msta->nan_sched.ndp_ctx_id, + &nmi_msta->nan_sched.ndp_ctx_bitmap); + } + rcu_read_unlock(); + msta->nan_sched.ndp_ctx_assigned = false; + + return ret ?: -ENOMEM; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.h b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h index 356d9ef7f664..f55730e25f46 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/nan.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h @@ -403,6 +403,8 @@ int mt7925_nan_change_configure(struct ieee80211_vif *vif, void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb); +int mt7925_nan_set_nmi_addr(struct mt792x_dev *dev, const u8 *addr); + void mt7925_nan_local_sched_changed(struct mt792x_dev *dev, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 337d6a100236..319dc3ba8434 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -23,6 +23,7 @@ #define MT792x_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ #define MT792x_FW_TAG_FEATURE 4 +#define MT792x_FW_CAP_NAN BIT(5) #define MT792x_FW_CAP_CNM BIT(7) #define MT792x_CHIP_CAP_CLC_EVT_EN BIT(0) @@ -121,10 +122,12 @@ struct mt792x_link_sta { }; struct mt792x_sta_nan_sched { + /* protects NAN peer schedule state */ u16 committed_dw; u32 sch_idx; bool idx_assigned; unsigned long ndp_ctx_bitmap; + bool ndp_ctx_assigned; u8 ndp_ctx_id; /* assigned NDP context ID (for NDI sta) */ struct { u8 map_id; From 9bb39d09ab0f0f0e2f341d727e65fa1712c3bffa Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 24 Jun 2026 19:18:33 -0500 Subject: [PATCH 233/403] wifi: mt76: mt792x: build iface combinations dynamically Move mt792x interface combination selection into a helper and store the selected table in mt792x device state. This keeps the existing non-CNM and CNM combinations unchanged while making later firmware-gated extensions add combinations without touching the common wiphy setup path. Co-developed-by: Stella Liu Signed-off-by: Stella Liu Co-developed-by: Jeremy Yu Signed-off-by: Jeremy Yu Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260625001834.475094-9-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x.h | 2 ++ .../net/wireless/mediatek/mt76/mt792x_core.c | 36 ++++++++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 319dc3ba8434..e98c4fd81044 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -337,6 +337,8 @@ struct mt792x_dev { struct ieee80211_chanctx_conf *new_ctx; struct ieee80211_vif *nan_vif; + const struct ieee80211_iface_combination *iface_combinations; + int n_iface_combinations; }; static inline struct mt792x_bss_conf * diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 8fc643fc0dfe..0c74fedf07e3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -60,7 +60,7 @@ static const struct ieee80211_iface_limit if_limits_chanctx_scc[] = { } }; -static const struct ieee80211_iface_combination if_comb_chanctx[] = { +static const struct ieee80211_iface_combination if_comb_chanctx_base[] = { { .limits = if_limits_chanctx_mcc, .n_limits = ARRAY_SIZE(if_limits_chanctx_mcc), @@ -77,6 +77,22 @@ static const struct ieee80211_iface_combination if_comb_chanctx[] = { } }; +static int mt792x_setup_iface_combinations(struct mt792x_dev *dev) +{ + const bool cnm = !!(dev->fw_features & MT792x_FW_CAP_CNM); + + if (!cnm) { + dev->iface_combinations = if_comb; + dev->n_iface_combinations = ARRAY_SIZE(if_comb); + return 0; + } + + dev->iface_combinations = if_comb_chanctx_base; + dev->n_iface_combinations = ARRAY_SIZE(if_comb_chanctx_base); + + return 0; +} + void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { @@ -663,6 +679,7 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) struct mt792x_phy *phy = mt792x_hw_phy(hw); struct mt792x_dev *dev = phy->dev; struct wiphy *wiphy = hw->wiphy; + int err; hw->queues = 4; if (dev->has_eht) { @@ -683,15 +700,17 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) hw->vif_data_size = sizeof(struct mt792x_vif); hw->chanctx_data_size = sizeof(struct mt792x_chanctx); - if (dev->fw_features & MT792x_FW_CAP_CNM) { + if (dev->fw_features & MT792x_FW_CAP_CNM) wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; - wiphy->iface_combinations = if_comb_chanctx; - wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_chanctx); - } else { + else wiphy->flags &= ~WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; - wiphy->iface_combinations = if_comb; - wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); - } + + err = mt792x_setup_iface_combinations(dev); + if (err) + return err; + + wiphy->iface_combinations = dev->iface_combinations; + wiphy->n_iface_combinations = dev->n_iface_combinations; wiphy->flags &= ~(WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_4ADDR_AP | WIPHY_FLAG_4ADDR_STATION); wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | @@ -699,6 +718,7 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); + wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; wiphy->max_scan_ssids = 4; wiphy->max_sched_scan_plan_interval = From 420e0bbd52ab6fa05ebfdb4dbe5442d47e4bfdf9 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 24 Jun 2026 19:18:34 -0500 Subject: [PATCH 234/403] wifi: mt76: mt792x: advertise NAN data support Advertise NAN and NAN data support when firmware exposes NAN capability. Add NAN interface combinations on top of the dynamic combination framework, advertise 2.4 GHz and 5 GHz NAN bands, and enable secure NAN. Keep the base interface combinations unchanged when NAN is unavailable so existing STA/AP/P2P modes keep the same limits. Co-developed-by: Stella Liu Signed-off-by: Stella Liu Co-developed-by: Jeremy Yu Signed-off-by: Jeremy Yu Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260625001834.475094-10-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt792x_core.c | 96 ++++++++++++++++++- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 0c74fedf07e3..2a8384a44d9f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -3,6 +3,7 @@ #include #include +#include #include "mt792x.h" #include "dma.h" @@ -60,6 +61,40 @@ static const struct ieee80211_iface_limit if_limits_chanctx_scc[] = { } }; +static const struct ieee80211_iface_limit if_limits_nan_mcc[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_NAN), + }, + { + .max = 2, + .types = BIT(NL80211_IFTYPE_NAN_DATA), + }, +}; + +static const struct ieee80211_iface_limit if_limits_nan_scc[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_NAN), + }, + { + .max = 2, + .types = BIT(NL80211_IFTYPE_NAN_DATA), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + }, +}; + static const struct ieee80211_iface_combination if_comb_chanctx_base[] = { { .limits = if_limits_chanctx_mcc, @@ -77,9 +112,31 @@ static const struct ieee80211_iface_combination if_comb_chanctx_base[] = { } }; -static int mt792x_setup_iface_combinations(struct mt792x_dev *dev) +static const struct ieee80211_iface_combination if_comb_chanctx_nan[] = { + { + .limits = if_limits_nan_mcc, + .n_limits = ARRAY_SIZE(if_limits_nan_mcc), + .max_interfaces = MT792x_MAX_INTERFACES, + .num_different_channels = 2, + .beacon_int_infra_match = false, + }, + { + .limits = if_limits_nan_scc, + .n_limits = ARRAY_SIZE(if_limits_nan_scc), + .max_interfaces = MT792x_MAX_INTERFACES, + .num_different_channels = 1, + .beacon_int_infra_match = false, + }, +}; + +static int mt792x_setup_iface_combinations(struct mt792x_dev *dev, + struct wiphy *wiphy) { const bool cnm = !!(dev->fw_features & MT792x_FW_CAP_CNM); + const bool nan = !!(dev->fw_features & MT792x_FW_CAP_NAN); + const int n_base = ARRAY_SIZE(if_comb_chanctx_base); + const int n_nan = ARRAY_SIZE(if_comb_chanctx_nan); + struct ieee80211_iface_combination *comb; if (!cnm) { dev->iface_combinations = if_comb; @@ -87,8 +144,24 @@ static int mt792x_setup_iface_combinations(struct mt792x_dev *dev) return 0; } - dev->iface_combinations = if_comb_chanctx_base; - dev->n_iface_combinations = ARRAY_SIZE(if_comb_chanctx_base); + /* CNM enabled, NAN optional */ + if (!nan) { + dev->iface_combinations = if_comb_chanctx_base; + dev->n_iface_combinations = ARRAY_SIZE(if_comb_chanctx_base); + return 0; + } + + /* CNM + NAN: dynamically build base + nan list */ + comb = devm_kcalloc(&wiphy->dev, n_base + n_nan, sizeof(*comb), + GFP_KERNEL); + if (!comb) + return -ENOMEM; + + memcpy(comb, if_comb_chanctx_base, sizeof(if_comb_chanctx_base)); + memcpy(comb + n_base, if_comb_chanctx_nan, sizeof(if_comb_chanctx_nan)); + + dev->iface_combinations = comb; + dev->n_iface_combinations = n_base + n_nan; return 0; } @@ -705,7 +778,7 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) else wiphy->flags &= ~WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; - err = mt792x_setup_iface_combinations(dev); + err = mt792x_setup_iface_combinations(dev, wiphy); if (err) return err; @@ -719,6 +792,21 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); + if ((dev->fw_features & MT792x_FW_CAP_CNM) && + (dev->fw_features & MT792x_FW_CAP_NAN)) { + wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN) | + BIT(NL80211_IFTYPE_NAN_DATA); + wiphy->nan_supported_bands = BIT(NL80211_BAND_2GHZ) | + BIT(NL80211_BAND_5GHZ); + wiphy->nan_capa.flags = WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC | + WIPHY_NAN_FLAGS_USERSPACE_DE; + wiphy->nan_capa.op_mode = NAN_OP_MODE_PHY_MODE_MASK; + wiphy->nan_capa.n_antennas = 0x22; + wiphy->nan_capa.max_channel_switch_time = 12; + wiphy->nan_capa.dev_capabilities = NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED; + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SECURE_NAN); + } + wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; wiphy->max_scan_ssids = 4; wiphy->max_sched_scan_plan_interval = From 81497634d9f872fd3e8b03aada55574afff6f174 Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Fri, 12 Jun 2026 17:25:43 -0700 Subject: [PATCH 235/403] wifi: mt76: mt76x02: do not WARN on invalid rx descriptor length The MPDU length in the rx descriptor comes from the hardware. In monitor mode with the fcsfail filter enabled, the hardware passes up corrupted frames, and a corrupted frame can report a length larger than the received buffer. The bounds check correctly discards such frames, but its WARN_ON_ONCE wrapper means any over-the-air garbage frame taints the kernel, and panics it on the first such frame when panic_on_warn is set. Drop the WARN and discard the frame silently, matching what commit c2d4c8723dbf ("mt76x2: remove some harmless WARN_ONs in tx status and rx path") did for the neighboring rx and tx status paths. Observed immediately on rx with an MT7612U in fcsfail monitor mode on a busy channel. Fixes: 7bc04215a66b ("mt76: add driver code for MT76x2e") Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260613002544.27750-2-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76x02_mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c index 14ee5b3b94d3..aa525adb6743 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c @@ -848,7 +848,7 @@ int mt76x02_mac_process_rx(struct mt76x02_dev *dev, struct sk_buff *skb, } } - if (WARN_ON_ONCE(len > skb->len)) + if (len > skb->len) return -EINVAL; if (pskb_trim(skb, len)) From ddae0bcb01e71c1184bb3f526f9622e31dc38f3b Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Fri, 12 Jun 2026 17:25:44 -0700 Subject: [PATCH 236/403] wifi: mt76: mt76x02: report rx FCS errors to mac80211 When the fcsfail filter is enabled the hardware passes frames with a bad FCS up to the driver, but mt76x02_mac_process_rx() never checks MT_RXINFO_CRCERR and hands them to mac80211 without RX_FLAG_FAILED_FCS_CRC. In monitor mode the radiotap flags byte then never gets IEEE80211_RADIOTAP_F_BADFCS set and corrupted frames cannot be told apart from clean ones. Set RX_FLAG_FAILED_FCS_CRC from the descriptor CRC error bit, matching mt7603, mt7615, mt7915, mt7921, mt7925 and mt7996. Reported-by: 0072a70 <90307219+0072a70@users.noreply.github.com> Closes: https://github.com/morrownr/mt76/issues/38 Tested-by: 0072a70 <90307219+0072a70@users.noreply.github.com> Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260613002544.27750-3-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76x02_mac.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c index aa525adb6743..21f8b1e64101 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c @@ -792,6 +792,9 @@ int mt76x02_mac_process_rx(struct mt76x02_dev *dev, struct sk_buff *skb, if (rxinfo & MT_RXINFO_L2PAD) pad_len += 2; + if (rxinfo & MT_RXINFO_CRCERR) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + if (rxinfo & MT_RXINFO_DECRYPT) { status->flag |= RX_FLAG_DECRYPTED; status->flag |= RX_FLAG_MMIC_STRIPPED; From 31beda21fbe569435ba8e080dc61694458e877b8 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Tue, 9 Jun 2026 21:24:26 -0700 Subject: [PATCH 237/403] wifi: mt76: mt7925: remove code guarded by nonexistent config option A small piece of code in mt7925/regs.h depends on CONFIG_MT76_DEV, which has never been defined in the kernel. Remove this dead code. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Reviewed-by: Matthias Brugger Link: https://patch.msgid.link/20260610042429.222717-1-enelsonmoore@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/regs.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h index cb937d565a80..ab6c33b4a180 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h @@ -88,11 +88,7 @@ #define MT_HIF_REMAP_BASE_L1 0x130000 #define MT_HIF_REMAP_L2 0x0120 -#if IS_ENABLED(CONFIG_MT76_DEV) -#define MT_HIF_REMAP_BASE_L2 (0x7c500000 - (0x7c000000 - 0x18000000)) -#else #define MT_HIF_REMAP_BASE_L2 0x18500000 -#endif #define MT_WFSYS_SW_RST_B 0x7c000140 From 429e516d4f8887cfa8dc65fef92d021ddc72e22c Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Tue, 9 Jun 2026 21:10:47 -0700 Subject: [PATCH 238/403] wifi: mt76: mt7996: remove code guarded by nonexistent config option A small piece of code in mt7996.h depends on CONFIG_MTK_DEBUG, which has never been defined in the kernel. Remove this dead code. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Reviewed-by: Matthias Brugger Link: https://patch.msgid.link/20260610041050.206950-1-enelsonmoore@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 0d6488522ba7..d364fa8b8cf9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -941,10 +941,6 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, bool hif2, int *irq); u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id); -#ifdef CONFIG_MTK_DEBUG -int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir); -#endif - int mt7996_dma_rro_init(struct mt7996_dev *dev); void mt7996_dma_rro_start(struct mt7996_dev *dev); From 965cbdbdfbd37a659a05ff88e49c216975bd9d2e Mon Sep 17 00:00:00 2001 From: David Bauer Date: Thu, 11 Jun 2026 23:56:56 +0200 Subject: [PATCH 239/403] wifi: mt76: mt7603: free beacon SKB on error The SKB containing the generated beacon is not freed when the beacon queue is deected stuck and scheduled for recovery. Fixes potential memory leaks in case the beacon queue is detected stuck. Signed-off-by: David Bauer Link: https://patch.msgid.link/20260611215658.259324-1-mail@david-bauer.net Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7603/beacon.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c index 300a7f9c2ef1..acca98139f92 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c @@ -56,6 +56,7 @@ mt7603_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) FIELD_PREP(MT_DMA_FQCR0_TARGET_QID, MT_TX_HW_QUEUE_BCN)); if (!mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, 0, 5000)) { dev->beacon_check = MT7603_WATCHDOG_TIMEOUT; + dev_kfree_skb(skb); goto out; } @@ -63,6 +64,7 @@ mt7603_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) FIELD_PREP(MT_DMA_FQCR0_TARGET_QID, MT_TX_HW_QUEUE_BMC)); if (!mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, 0, 5000)) { dev->beacon_check = MT7603_WATCHDOG_TIMEOUT; + dev_kfree_skb(skb); goto out; } From 70869cc429fffc77de51e7777c0ecb651e8fca07 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Mon, 20 Jul 2026 17:01:02 +0800 Subject: [PATCH 240/403] wifi: mt76: fix handling channel context with different bands in mt76_switch_vif_chanctx() When performing channel switches on different radios within a short timeframe, channel contexts with different bands can be carried for each struct ieee80211_vif_chanctx_switch. Rework mt76_switch_vif_chanctx() to properly handle this scenario. Fixes: 82334623af0c ("wifi: mt76: add chanctx functions for multi-channel phy support") Co-developed-by: Rex Lu Signed-off-by: Rex Lu Signed-off-by: Shayne Chen Link: https://patch.msgid.link/20260720090102.190729-1-shayne.chen@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/channel.c | 98 +++++++++++--------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c index 6edcb3b8f279..28ad7bcaffd4 100644 --- a/drivers/net/wireless/mediatek/mt76/channel.c +++ b/drivers/net/wireless/mediatek/mt76/channel.c @@ -186,68 +186,74 @@ int mt76_switch_vif_chanctx(struct ieee80211_hw *hw, int n_vifs, enum ieee80211_chanctx_switch_mode mode) { - struct mt76_chanctx *old_ctx = (struct mt76_chanctx *)vifs->old_ctx->drv_priv; - struct mt76_chanctx *new_ctx = (struct mt76_chanctx *)vifs->new_ctx->drv_priv; - struct ieee80211_chanctx_conf *conf = vifs->new_ctx; - struct mt76_phy *old_phy = old_ctx->phy; - struct mt76_phy *phy = hw->priv; + struct ieee80211_vif_chanctx_switch *v; + struct mt76_chanctx *old_ctx, *new_ctx; + struct mt76_phy *old_phy, *phy = hw->priv; struct mt76_dev *dev = phy->dev; struct mt76_vif_link *mlink; - bool update_chan; + bool need_update[__MT_MAX_BAND] = {}; int i, ret = 0; - if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS) - phy = new_ctx->phy = dev->band_phys[conf->def.chan->band]; - else - phy = new_ctx->phy; - if (!phy) - return -EINVAL; + for (i = 0; i < n_vifs; i++) { + v = &vifs[i]; + new_ctx = (struct mt76_chanctx *)v->new_ctx->drv_priv; + if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS) + phy = new_ctx->phy = dev->band_phys[v->new_ctx->def.chan->band]; + else + phy = new_ctx->phy; - update_chan = phy->chanctx != new_ctx; - if (update_chan) { - if (dev->scan.phy == phy) - mt76_abort_scan(dev); + if (!phy) + return -EINVAL; - cancel_delayed_work_sync(&phy->mac_work); + if (need_update[phy->band_idx]) + continue; + + if (phy->chanctx != new_ctx) { + if (dev->scan.phy == phy) + mt76_abort_scan(dev); + + cancel_delayed_work_sync(&phy->mac_work); + need_update[phy->band_idx] = true; + } } mutex_lock(&dev->mutex); - if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS && - phy != old_phy && old_phy->chanctx == old_ctx) - old_phy->chanctx = NULL; - - if (update_chan) - ret = mt76_phy_update_channel(phy, vifs->new_ctx); - - if (ret) - goto out; - - if (old_phy == phy) - goto skip_link_replace; - for (i = 0; i < n_vifs; i++) { - mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf); + v = &vifs[i]; + old_ctx = (struct mt76_chanctx *)v->old_ctx->drv_priv; + old_phy = old_ctx->phy; + + new_ctx = (struct mt76_chanctx *)v->new_ctx->drv_priv; + phy = new_ctx->phy; + + if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS && old_phy->chanctx && + old_phy->chanctx == old_ctx && phy != old_phy) + old_phy->chanctx = NULL; + + if (need_update[phy->band_idx]) { + ret = mt76_phy_update_channel(phy, v->new_ctx); + if (ret) + goto out; + + need_update[phy->band_idx] = false; + } + + mlink = mt76_vif_conf_link(dev, v->vif, v->link_conf); if (!mlink) continue; - dev->drv->vif_link_remove(old_phy, vifs[i].vif, - vifs[i].link_conf, mlink); + if (old_phy != phy) { + dev->drv->vif_link_remove(old_phy, v->vif, v->link_conf, + mlink); - ret = dev->drv->vif_link_add(phy, vifs[i].vif, - vifs[i].link_conf, mlink); - if (ret) - goto out; + ret = dev->drv->vif_link_add(phy, v->vif, v->link_conf, + mlink); + if (ret) + goto out; + } - } - -skip_link_replace: - for (i = 0; i < n_vifs; i++) { - mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf); - if (!mlink) - continue; - - mlink->ctx = vifs->new_ctx; + mlink->ctx = v->new_ctx; if (mlink->beacon_mon_interval) WRITE_ONCE(mlink->beacon_mon_last, jiffies); } From 7e87dc3b2157967b14ded97129e677a764573ec7 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 13 Jun 2026 17:41:26 -0500 Subject: [PATCH 241/403] wifi: mt76: mt7925: stop init retries on hung bus Stop retrying hardware init once the bus is marked hung. The control path is no longer usable at that point, so more retries only issue failing device accesses, including MCU commands or register operations, and delay teardown. Exit early and let the failed device be torn down quickly. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260613224131.2396026-2-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/init.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index 1b44f5c8fb0d..cd22fcc021b1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -137,10 +137,18 @@ static int mt7925_init_hardware(struct mt792x_dev *dev) set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); for (i = 0; i < MT792x_MCU_INIT_RETRY_COUNT; i++) { + if (atomic_read(&dev->mt76.bus_hung)) { + ret = -EIO; + break; + } + ret = __mt7925_init_hardware(dev); if (!ret) break; + if (atomic_read(&dev->mt76.bus_hung)) + break; + mt792x_init_reset(dev); } From c781b74c3fd0c24db33ca7cfc0b9a3857d623543 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 13 Jun 2026 17:41:27 -0500 Subject: [PATCH 242/403] wifi: mt76: mt7925: skip reset work on hung bus Skip mt7925 reset handling once the bus is marked hung. A hung bus cannot be recovered by issuing another device reset. Continuing the reset path may only send more failing MCU or register accesses and delay teardown. Return early from reset work and the USB reset path so the failed device can be torn down quickly. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260613224131.2396026-3-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 6 ++++++ drivers/net/wireless/mediatek/mt76/mt7925/usb.c | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index a7eb80b22953..b9973c4eb7f6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1516,6 +1516,9 @@ void mt7925_mac_reset_work(struct work_struct *work) struct mt76_connac_pm *pm = &dev->pm; int i, ret; + if (atomic_read(&dev->mt76.bus_hung)) + return; + dev_dbg(dev->mt76.dev, "chip reset\n"); dev->hw_full_reset = true; ieee80211_stop_queues(hw); @@ -1533,6 +1536,9 @@ void mt7925_mac_reset_work(struct work_struct *work) break; } + if (atomic_read(&dev->mt76.bus_hung)) + return; + if (i == 10) dev_err(dev->mt76.dev, "chip reset failed\n"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c index e9f58492bf7d..49ad4fe9eb1b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c @@ -81,6 +81,13 @@ static int mt7925u_mac_reset(struct mt792x_dev *dev) { int err; + if (atomic_read(&dev->mt76.bus_hung)) + return 0; + + mt792xu_reset_on_bus_error(dev); + if (atomic_read(&dev->mt76.bus_hung)) + return 0; + mt76_txq_schedule_all(&dev->mphy); mt76_worker_disable(&dev->mt76.tx_worker); From e137e5fd245cb6c0ae378607cc8090c913d984b2 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 13 Jun 2026 17:41:28 -0500 Subject: [PATCH 243/403] wifi: mt76: mt792x: stop USB register access after bus hang Mark the mt792x USB bus hung on the first control timeout and switch register access to no-op bus ops. Each failed vendor request may spend up to MT_VEND_REQ_MAX_RETRY * MT_VEND_REQ_TOUT_MS, about 3 seconds, and teardown/reset paths can keep issuing such requests after the device has stopped responding. Also skip the USB WFSYS reset path after bus_hung is set, since it uses UHW vendor requests as well. mt7925u 1-2:1.3: vendor request req:63 off:0018 failed:-110 mt7925u 1-2:1.3: vendor request req:63 off:0018 failed:-110 mt7925u 1-2:1.3: vendor request req:63 off:0018 failed:-110 mt7925u 1-2:1.3: vendor request req:63 off:0018 failed:-110 mt7925u 1-2:1.3: vendor request req:63 off:0018 failed:-110 Avoid repeating those register reads after the bus is known to be hung by switching register access to no-op handlers. Fixes: 0d2afe09fad5 ("mt76: mt7921: add mt7921u driver") Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260613224131.2396026-4-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76.h | 1 + .../net/wireless/mediatek/mt76/mt792x_usb.c | 91 ++++++++++++++++--- drivers/net/wireless/mediatek/mt76/usb.c | 11 +++ 3 files changed, 90 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 6cff136407d8..640061276d76 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -672,6 +672,7 @@ struct mt76_usb { u8 out_ep[__MT_EP_OUT_MAX]; u8 in_ep[__MT_EP_IN_MAX]; + void (*ctrl_timeout)(struct mt76_dev *dev, int err); bool sg_en; struct mt76u_mcu { diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c index 910132e94956..47f80c9ec4e7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -31,10 +31,75 @@ static void mt792xu_reset_work(struct work_struct *work) atomic_set(&dev->usb_reset_pending, 0); } +static void mt792xu_queue_usb_reset(struct mt792x_dev *dev, int err) +{ + if (!atomic_xchg(&dev->usb_reset_pending, 1)) { + dev_warn(dev->mt76.dev, + "USB transport access failed (%d), queueing device reset\n", + err); + + schedule_work(&dev->usb_reset_work); + } +} + +static u32 mt792xu_bus_hung_rr(struct mt76_dev *mdev, u32 offset) +{ + return 0; +} + +static void mt792xu_bus_hung_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ +} + +static u32 mt792xu_bus_hung_rmw(struct mt76_dev *mdev, u32 offset, + u32 mask, u32 val) +{ + return 0; +} + +static void mt792xu_bus_hung_write_copy(struct mt76_dev *mdev, u32 offset, + const void *data, int len) +{ +} + +static void mt792xu_bus_hung_read_copy(struct mt76_dev *mdev, u32 offset, + void *data, int len) +{ + memset(data, 0, len); +} + +static const struct mt76_bus_ops mt792xu_bus_hung_ops = { + .rr = mt792xu_bus_hung_rr, + .wr = mt792xu_bus_hung_wr, + .rmw = mt792xu_bus_hung_rmw, + .write_copy = mt792xu_bus_hung_write_copy, + .read_copy = mt792xu_bus_hung_read_copy, + .type = MT76_BUS_USB, +}; + +static void mt792xu_set_bus_hung(struct mt792x_dev *dev) +{ + atomic_set(&dev->mt76.bus_hung, true); + + if (READ_ONCE(dev->mt76.bus) == &mt792xu_bus_hung_ops) + return; + + WRITE_ONCE(dev->mt76.bus, &mt792xu_bus_hung_ops); +} + +static void mt792xu_ctrl_timeout(struct mt76_dev *mdev, int err) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + + mt792xu_set_bus_hung(dev); + mt792xu_queue_usb_reset(dev, err); +} + void mt792xu_reset_work_init(struct mt792x_dev *dev) { INIT_WORK(&dev->usb_reset_work, mt792xu_reset_work); atomic_set(&dev->usb_reset_pending, 0); + dev->mt76.usb.ctrl_timeout = mt792xu_ctrl_timeout; } EXPORT_SYMBOL_GPL(mt792xu_reset_work_init); @@ -62,26 +127,23 @@ EXPORT_SYMBOL_GPL(mt792xu_check_bus); int mt792xu_reset_on_bus_error(struct mt792x_dev *dev) { - int err = 0; + int err; - if (!atomic_read(&dev->mt76.bus_hung)) - err = mt792xu_check_bus(dev); + /* Once hung, the no-op bus ops stay installed until the queued USB + * reset re-probes the device. Do not clear bus_hung here, or the caller + * would run a full reset over dropped register I/O and report success. + */ + if (atomic_read(&dev->mt76.bus_hung)) + return -EIO; + err = mt792xu_check_bus(dev); if (err) { - atomic_set(&dev->mt76.bus_hung, true); - - if (!atomic_xchg(&dev->usb_reset_pending, 1)) { - dev_warn(dev->mt76.dev, - "USB transport access failed (%d), queueing device reset\n", - err); - - schedule_work(&dev->usb_reset_work); - } + mt792xu_set_bus_hung(dev); + mt792xu_queue_usb_reset(dev, err); return err; } - atomic_set(&dev->mt76.bus_hung, false); return 0; } EXPORT_SYMBOL_GPL(mt792xu_reset_on_bus_error); @@ -344,6 +406,9 @@ int mt792xu_wfsys_reset(struct mt792x_dev *dev) u32 val; int i; + if (atomic_read(&dev->mt76.bus_hung)) + return -EIO; + mt792xu_epctl_rst_opt(dev, false); val = mt792xu_uhw_rr(&dev->mt76, desc->rst_reg); diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index e54b35e53b6a..a9af3aa6b80a 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -30,6 +30,8 @@ int __mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type, for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) { if (test_bit(MT76_REMOVED, &dev->phy.state)) return -EIO; + if (dev->usb.ctrl_timeout && atomic_read(&dev->bus_hung)) + return -EIO; ret = usb_control_msg(udev, pipe, req, req_type, val, offset, buf, len, MT_VEND_REQ_TOUT_MS); @@ -42,6 +44,15 @@ int __mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type, dev_err(dev->dev, "vendor request req:%02x off:%04x failed:%d\n", req, offset, ret); + + if (dev->usb.ctrl_timeout) { + atomic_set(&dev->bus_hung, true); + dev_err(dev->dev, "vendor request req:%02x off:%04x timed out, marking bus hung\n", + req, offset); + dev->usb.ctrl_timeout(dev, ret); + return ret; + } + return ret; } EXPORT_SYMBOL_GPL(__mt76u_vendor_request); From a4803d1801c701603e2403ba5eb2b8ed55667e58 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 13 Jun 2026 17:41:29 -0500 Subject: [PATCH 244/403] wifi: mt76: mt792x: drain USB UDMA before WFSYS reset Stop USB UDMA RX/TX and wait for idle before WFSYS reset. Warn if the engine remains busy. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260613224131.2396026-5-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt792x_usb.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c index 47f80c9ec4e7..9f7b307a2b33 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -11,6 +11,8 @@ #include "mt792x.h" #include "mt76_connac2_mac.h" +#define MT792X_USB_UDMA_IDLE_TIMEOUT 1000 + static int mt792xu_read32(struct mt76_dev *dev, u32 addr, void *buf) { return __mt76u_vendor_request(dev, MT_VEND_READ_EXT, @@ -343,6 +345,23 @@ static void mt792xu_epctl_rst_opt(struct mt792x_dev *dev, bool reset) mt792xu_uhw_wr(&dev->mt76, MT_SSUSB_EPCTL_CSR_EP_RST_OPT, val); } +static void mt792xu_wait_udma_idle(struct mt792x_dev *dev) +{ + u32 mask = MT_WL_RX_BUSY | MT_WL_TX_BUSY; + u32 val; + + mt76_set(dev, MT_UDMA_WLCFG_0, MT_WL_RX_FLUSH); + + if (mt76_poll_msec(dev, MT_UDMA_WLCFG_0, mask, 0, + MT792X_USB_UDMA_IDLE_TIMEOUT)) + return; + + val = mt76_rr(dev, MT_UDMA_WLCFG_0); + + dev_warn(dev->mt76.dev, + "UDMA busy before WFSYS reset: WLCFG0=0x%08x\n", val); +} + struct mt792xu_wfsys_desc { u32 rst_reg; u32 done_reg; @@ -409,6 +428,7 @@ int mt792xu_wfsys_reset(struct mt792x_dev *dev) if (atomic_read(&dev->mt76.bus_hung)) return -EIO; + mt792xu_wait_udma_idle(dev); mt792xu_epctl_rst_opt(dev, false); val = mt792xu_uhw_rr(&dev->mt76, desc->rst_reg); From b994cb409f21d5f82290370ebae8a035e7bff531 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 13 Jun 2026 17:41:30 -0500 Subject: [PATCH 245/403] wifi: mt76: mt792x: enable USB UDMA TX timeout Configure the USB UDMA TX timeout limit and enable timeout detection during DMA initialization, matching the vendor driver setup. Use a longer timeout to avoid false alarms. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260613224131.2396026-6-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x_usb.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c index 9f7b307a2b33..5b0b04106133 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -11,6 +11,7 @@ #include "mt792x.h" #include "mt76_connac2_mac.h" +#define MT792X_USB_TX_TIMEOUT_LIMIT 50000 #define MT792X_USB_UDMA_IDLE_TIMEOUT 1000 static int mt792xu_read32(struct mt76_dev *dev, u32 addr, void *buf) @@ -400,6 +401,10 @@ int mt792xu_dma_init(struct mt792x_dev *dev, bool resume) mt76_set(dev, MT_UDMA_WLCFG_0, MT_WL_RX_EN | MT_WL_TX_EN | MT_WL_RX_MPSZ_PAD0 | MT_TICK_1US_EN); + mt76_rmw(dev, MT_UDMA_WLCFG_1, MT_WL_TX_TMOUT_LMT, + FIELD_PREP(MT_WL_TX_TMOUT_LMT, + MT792X_USB_TX_TIMEOUT_LIMIT)); + mt76_set(dev, MT_UDMA_WLCFG_0, MT_WL_TX_TMOUT_FUNC_EN); mt76_clear(dev, MT_UDMA_WLCFG_0, MT_WL_RX_AGG_TO | MT_WL_RX_AGG_LMT); mt76_clear(dev, MT_UDMA_WLCFG_1, MT_WL_RX_AGG_PKT_LMT); From 7910bd565d8b287fd93888e0f5eb00e7067c91e4 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 13 Jun 2026 17:41:31 -0500 Subject: [PATCH 246/403] wifi: mt76: mt792x: quiesce USB paths on disconnect USB disconnect can leave reset/init work, TX worker, and MCU waiters active while the device is being removed. Stop those paths before unregistering the device to avoid teardown waiting on firmware or queue activity after disconnect. Run WFSYS reset after USB queue deinit so removal does not issue the reset while USB traffic may still be queued. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260613224131.2396026-7-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt792x_usb.c | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c index 5b0b04106133..850f6b2cfc4a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -234,9 +234,9 @@ EXPORT_SYMBOL_GPL(mt792xu_mcu_power_on); static void mt792xu_cleanup(struct mt792x_dev *dev) { clear_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); - mt792xu_wfsys_reset(dev); skb_queue_purge(&dev->mt76.mcu.res_q); mt76u_queues_deinit(&dev->mt76); + mt792xu_wfsys_reset(dev); } static u32 mt792xu_uhw_rr(struct mt76_dev *dev, u32 addr) @@ -498,13 +498,27 @@ void mt792xu_disconnect(struct usb_interface *usb_intf) { struct mt792x_dev *dev = usb_get_intfdata(usb_intf); - mt792xu_reset_work_cleanup(dev); - cancel_work_sync(&dev->init_work); - if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + if (!dev) return; + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + skb_queue_purge(&dev->mt76.mcu.res_q); + + cancel_work_sync(&dev->reset_work); + cancel_work_sync(&dev->init_work); + mt76_worker_disable(&dev->mt76.tx_worker); + mt792xu_reset_work_cleanup(dev); + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) { + set_bit(MT76_REMOVED, &dev->mphy.state); + return; + } + mt76_unregister_device(&dev->mt76); mt792xu_cleanup(dev); + set_bit(MT76_REMOVED, &dev->mphy.state); usb_set_intfdata(usb_intf, NULL); From 9417c5818a0146980c2608fda94c908e604eb033 Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Tue, 21 Jul 2026 10:17:40 +0000 Subject: [PATCH 247/403] wifi: mt76: mt7921: validate CLC firmware records The CLC region is supplied by firmware, but the loader trusts the region count and each record length. A malformed image can make the region table pointer precede the firmware buffer, make the record loop fail to advance, or index phy->clc past its end. Validate the table and record bounds before dereferencing or copying. Fixes: 23bdc5d8cadf ("wifi: mt76: mt7921: introduce Country Location Control support") Signed-off-by: Laxman Acharya Padhya Link: https://patch.msgid.link/CAMyXUJmh=WfwC4_KHupNxYR5e2Gy5QhBDL5TSG6XEW-XLa+X4Q@mail.gmail.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/mcu.c | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 25b9437250f7..564dd836e0b3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -415,7 +415,8 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) struct mt76_dev *mdev = &dev->mt76; struct mt792x_phy *phy = &dev->phy; const struct firmware *fw; - int ret, i, len, offset = 0; + size_t clc_len, fw_data_len, len, offset = 0; + int ret, i; u8 *clc_base = NULL, hw_encap = 0; dev->phy.clc_chan_conf = 0xff; @@ -441,13 +442,21 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) } hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); + if (hdr->n_region > (fw->size - sizeof(*hdr)) / sizeof(*region)) { + dev_err(mdev->dev, "Invalid firmware region table\n"); + ret = -EINVAL; + goto out; + } + fw_data_len = fw->size - sizeof(*hdr) - + hdr->n_region * sizeof(*region); + for (i = 0; i < hdr->n_region; i++) { region = (const void *)((const u8 *)hdr - (hdr->n_region - i) * sizeof(*region)); len = le32_to_cpu(region->len); /* check if we have valid buffer size */ - if (offset + len > fw->size) { + if (len > fw_data_len - offset) { dev_err(mdev->dev, "Invalid firmware region\n"); ret = -EINVAL; goto out; @@ -464,8 +473,19 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) if (!clc_base) goto out; - for (offset = 0; offset < len; offset += le32_to_cpu(clc->len)) { + for (offset = 0; offset < len; offset += clc_len) { + if (len - offset < sizeof(*clc)) { + ret = -EINVAL; + goto out; + } + clc = (const struct mt7921_clc *)(clc_base + offset); + clc_len = le32_to_cpu(clc->len); + if (clc_len < sizeof(*clc) || clc_len > len - offset || + clc->idx >= ARRAY_SIZE(phy->clc)) { + ret = -EINVAL; + goto out; + } /* do not init buf again if chip reset triggered */ if (phy->clc[clc->idx]) @@ -477,7 +497,7 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) continue; phy->clc[clc->idx] = devm_kmemdup(mdev->dev, clc, - le32_to_cpu(clc->len), + clc_len, GFP_KERNEL); if (!phy->clc[clc->idx]) { From 653c6e289b13cc6942f3e8f8e3c568e70fa42d1f Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Mon, 13 Jul 2026 17:39:12 +0545 Subject: [PATCH 248/403] wifi: mt76: mt7996: validate default EEPROM firmware size The default EEPROM firmware is parsed and copied as a full EEPROM without checking its length. A truncated file can make the driver read beyond the firmware buffer during variant validation or the fallback copy. Reject files shorter than MT7996_EEPROM_SIZE before parsing or copying the firmware. Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices") Cc: stable@vger.kernel.org Signed-off-by: Laxman Acharya Padhya Link: https://patch.msgid.link/20260713115412.67095-1-acharyalaxman8848@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c index ac05f7d75d63..ec33521db564 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c @@ -150,6 +150,12 @@ mt7996_eeprom_check_or_use_default(struct mt7996_dev *dev, bool use_default) goto out; } + if (fw->size < MT7996_EEPROM_SIZE) { + dev_err(dev->mt76.dev, "Invalid default bin size\n"); + ret = -EINVAL; + goto out; + } + if (!use_default && mt7996_eeprom_variant_valid(dev, fw->data)) goto out; From 8a177dabeeead47dda4d4f91331282790eed0097 Mon Sep 17 00:00:00 2001 From: Zhi-Jun You Date: Wed, 15 Jul 2026 23:21:12 +0800 Subject: [PATCH 249/403] wifi: mt76: wed: fix kernel panic on non-DBDC MT7986 In mt76_wed_init_rx_buf, it's hardcoded to use MT_RXQ_MAIN. But for non-DBDC MT7986 MT_RXQ_BAND1 is used for RX data queue which leads to kernel panic when attaching WED. Use the correct RX queue by checking WED version and band_idx. v2 and band 1 -> MT_RXQ_BAND1 Others -> MT_RXQ_MAIN Kernel panic: Unable to handle kernel access to user memory outside uaccess routines at virtual address 0000000000000000 Mem abort info: ESR = 0x0000000096000005 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x05: level 1 translation fault Data abort info: ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000 CM = 0, WnR = 0, TnD = 0, TagAccess = 0 GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 Internal error: Oops: 0000000096000005 [#1] SMP CPU: 1 UID: 0 PID: 925 Comm: kmodloader Tainted: G O 6.18.26 #0 NONE Tainted: [O]=OOT_MODULE Hardware name: Acer Connect Vero W6m (DT) pstate: 40400005 (nZcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : page_pool_alloc_frag_netmem+0x1c/0x1bc lr : page_pool_alloc_frag+0xc/0x34 sp : ffffffc081dab660 x29: ffffffc081dab660 x28: ffffffc081dabc60 x27: ffffff80091af040 x26: 0000008000000000 x25: ffffff80091a8898 x24: ffffff80091a5440 x23: 0000000000001000 x22: 0000000140000000 x21: ffffff80091a2040 x20: ffffff8003f1d780 x19: 0000000000000000 x18: 0000000000000020 x17: ffffffbfbf0ac000 x16: ffffffc080ee0000 x15: ffffff80049d47ca x14: 000000000000037b x13: 000000000000037b x12: 0000000000000001 x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000 x8 : ffffff8003f1d7c0 x7 : 0000000000000000 x6 : ffffff8003f1d780 x5 : 0000000000000680 x4 : 0000000000000000 x3 : 0000000000002824 x2 : 0000000000000000 x1 : ffffffc081dab71c x0 : 0000000000000000 Call trace: page_pool_alloc_frag_netmem+0x1c/0x1bc (P) page_pool_alloc_frag+0xc/0x34 mt76_wed_init_rx_buf+0xf8/0x2ac [mt76] mtk_wed_start+0x79c/0x12ac mt7915_dma_start+0x274/0x63c [mt7915e] mt7915_dma_start+0x5b4/0x63c [mt7915e] mt7915_dma_init+0x49c/0x81c [mt7915e] mt7915_register_device+0x24c/0x530 [mt7915e] mt7915_mmio_probe+0x91c/0x1980 [mt7915e] platform_probe+0x58/0xa0 really_probe+0xb8/0x2a8 __driver_probe_device+0x74/0x118 driver_probe_device+0x3c/0xe0 __driver_attach+0x88/0x154 bus_for_each_dev+0x60/0xb0 driver_attach+0x20/0x28 bus_add_driver+0xdc/0x200 driver_register+0x64/0x118 __platform_driver_register+0x20/0x30 init_module+0x74/0x1000 [mt7915e] do_one_initcall+0x4c/0x1f8 do_init_module+0x50/0x210 load_module+0x15f8/0x1b10 __do_sys_init_module+0x1a8/0x260 __arm64_sys_init_module+0x18/0x20 invoke_syscall.constprop.0+0x4c/0xd0 do_el0_svc+0x3c/0xd0 el0_svc+0x18/0x60 el0t_64_sync_handler+0x98/0xdc el0t_64_sync+0x158/0x15c Code: aa0003f3 a9025bf5 a90363f7 d2820017 (b9400000) Signed-off-by: Zhi-Jun You Link: https://patch.msgid.link/20260715152113.553-1-hujy652@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/wed.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/wed.c b/drivers/net/wireless/mediatek/mt76/wed.c index ed657d952de2..f210a0c57d81 100644 --- a/drivers/net/wireless/mediatek/mt76/wed.c +++ b/drivers/net/wireless/mediatek/mt76/wed.c @@ -33,10 +33,15 @@ u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size) { struct mtk_wed_bm_desc *desc = wed->rx_buf_ring.desc; struct mt76_dev *dev = mt76_wed_to_dev(wed); - struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; struct mt76_txwi_cache *t = NULL; + struct mt76_queue *q; int i; + if (wed->version == 2 && dev->phy.band_idx) + q = &dev->q_rx[MT_RXQ_BAND1]; + else + q = &dev->q_rx[MT_RXQ_MAIN]; + for (i = 0; i < size; i++) { dma_addr_t addr; u32 offset; From bade0d238b60c29dafcc7da17501fa489495d6ae Mon Sep 17 00:00:00 2001 From: Zhi-Jun You Date: Wed, 15 Jul 2026 23:21:13 +0800 Subject: [PATCH 250/403] wifi: mt76: mt7915: fix net_fill_forward_path for non-DBDC mt7986 Current implementation assumes that the hardware supports DBDC or single band and binds to band0. This causes net_fill_forward_path to select the wrong queue for non-DBDC mt7986 because it binds to band1 and getting the following in dmesg: ieee80211 phy2: WA: --> drop by reaseon:1, msdu id = 0xc002 but failed! mtk_wed1: error status=00000002 ieee80211 phy2: WA: txblk 10324e00 len = 128 DW0 : 10 00 00 00 DW1 : 00 00 00 00 DW2 : 00 00 00 00 DW3 : 72 0f 94 68 DW4 : 00 00 00 00 DW5 : ff 03 00 00 DW6 : 00 00 3c 40 DW7 : 00 17 dd 14 DW8 : 79 6f 00 00 DW9 : 02 c0 00 00 DW10 : 58 c5 34 10 DW11 : 00 00 00 00 DW12 : 00 06 3e 00 DW13 : 00 00 00 80 DW14 : 10 8c 00 00 DW15 : 00 00 00 00 DW16 : 00 00 00 00 DW17 : 00 00 00 00 DW18 : 00 00 00 00 DW19 : 00 00 00 00 DW20 : 00 00 00 00 DW21 : 00 00 00 00 DW22 : 00 00 00 00 DW23 : 00 00 00 00 DW24 : 00 00 00 00 DW25 : 00 00 00 00 DW26 : 00 00 00 00 DW27 : 00 00 00 00 DW28 : 00 00 00 00 DW29 : 00 00 00 00 DW30 : 00 00 00 00 DW31 : 00 00 00 00 Fix it by using phy->mt76->band_idx for queue which works for both non-DBDC and DBDC devices. Fixes: f68d67623dec ("mt76: mt7915: add Wireless Ethernet Dispatch support") Suggested-by: Benjamin Larsson Signed-off-by: Zhi-Jun You Link: https://patch.msgid.link/20260715152113.553-2-hujy652@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 51643a48ed15..044b592efe28 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -1743,7 +1743,7 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw, path->mtk_wdma.wdma_idx = wed->wdma_idx; path->mtk_wdma.bss = mvif->mt76.idx; path->mtk_wdma.wcid = is_mt7915(&dev->mt76) ? msta->wcid.idx : 0x3ff; - path->mtk_wdma.queue = phy != &dev->phy; + path->mtk_wdma.queue = phy->mt76->band_idx; ctx->dev = NULL; From 4361f1660101270611eb5dc39799f620b35f2e4d Mon Sep 17 00:00:00 2001 From: Prashant Rahul Date: Thu, 16 Jul 2026 21:55:10 +0530 Subject: [PATCH 251/403] wifi: mt76: mt7921: fix memory leak when skb_linearize fails in mcu rx event The ownership of sk_buff skb is passed to mt7921_queue_rx_skb, each path inside it under the switch case handles cleaning of skb and it is true for mt7921_mcu_rx_event as well. mt7921_mcu_rx_event, on a success path, either queues skb via mt76_mcu_rx_event or cleans it immediately inside mt7921_mcu_uni_rx_unsolicited_event. However inside mt7921_mcu_rx_event, if skb_linearize fails, the function returns immediately and never bothers cleaning skb which leaks skb. Since skb is fully owned at this point, it is safe to call dev_kfree_skb which fixes the leak. Granted, the skb_linearize failure is rare as it can only fail under heavy memory usage, but at the same time, leaking memory under heavy memory usage can worsen the OOM condition. Signed-off-by: Prashant Rahul Link: https://patch.msgid.link/20260716-mt7921-mem-leak-v1-1-6e9c0ea19f63@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 564dd836e0b3..17d97407b8f3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -351,8 +351,10 @@ void mt7921_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt76_connac2_mcu_rxd *rxd; - if (skb_linearize(skb)) + if (skb_linearize(skb)) { + dev_kfree_skb(skb); return; + } rxd = (struct mt76_connac2_mcu_rxd *)skb->data; From 44b5adfe49499f53002737f5fe81d608c08122fc Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 25 Jun 2026 07:10:26 -0500 Subject: [PATCH 252/403] wifi: mt76: mt7915: bound the device EEPROM address before the EFUSE copy mt7915_mcu_get_eeprom() copies a fixed EFUSE block into the driver's dev->mt76.eeprom.data buffer at the offset reported by the MCU response (res->addr, a device-controlled __le32) without checking it against the buffer size. A malicious or malfunctioning device can report an arbitrary address and drive a 16-byte out-of-bounds write past eeprom.data. Reject a response whose address would place the copy outside eeprom.data before deriving the destination pointer. Devices that echo the requested in-bounds offset are unaffected. Fixes: e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260625-b4-disp-16f99062-v1-1-aee52ecf61b9@proton.me Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index e8fe86f93309..bbb2fedacb25 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -2917,8 +2917,15 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf) return ret; res = (struct mt7915_mcu_eeprom_info *)skb->data; - if (!buf) - buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr); + if (!buf) { + u32 addr = le32_to_cpu(res->addr); + + if (addr > dev->mt76.eeprom.size - MT7915_EEPROM_BLOCK_SIZE) { + dev_kfree_skb(skb); + return -EINVAL; + } + buf = dev->mt76.eeprom.data + addr; + } memcpy(buf, res->data, MT7915_EEPROM_BLOCK_SIZE); dev_kfree_skb(skb); From 13b3c29a782033ce4a230be9e5618032813dbcd4 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 25 Jun 2026 07:10:27 -0500 Subject: [PATCH 253/403] wifi: mt76: mt7996: bound the device EEPROM address before the EFUSE copy mt7996_mcu_get_eeprom() derives the destination of the EFUSE/EXT block copy from the address reported by the MCU response (event->addr, a device-controlled __le32) and clamps only the copy length, never the destination offset into dev->mt76.eeprom.data. A malicious or malfunctioning device can report an arbitrary address and drive an out-of-bounds write of up to MT7996_EXT_EEPROM_BLOCK_SIZE bytes past eeprom.data. Reject a response whose address would place the copy outside eeprom.data before deriving the destination pointer. Devices that echo the requested in-bounds offset are unaffected. Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260625-b4-disp-16f99062-v1-2-aee52ecf61b9@proton.me Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 2e83f4b79c87..a1bae5db8500 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -4352,11 +4352,18 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_l event = (struct mt7996_mcu_eeprom_access_event *)skb->data; if (event->valid) { u32 ret_len = le32_to_cpu(event->eeprom.ext_eeprom.data_len); + u32 block = mode == EEPROM_MODE_EXT ? MT7996_EXT_EEPROM_BLOCK_SIZE : + MT7996_EEPROM_BLOCK_SIZE; addr = le32_to_cpu(event->addr); - if (!buf) + if (!buf) { + if (addr > dev->mt76.eeprom.size - block) { + dev_kfree_skb(skb); + return -EINVAL; + } buf = (u8 *)dev->mt76.eeprom.data + addr; + } switch (mode) { case EEPROM_MODE_EFUSE: From 3c004f73d68b415d231189b75a3cff1582a17e90 Mon Sep 17 00:00:00 2001 From: Kenneth Kasilag Date: Sat, 20 Jun 2026 01:38:50 +0000 Subject: [PATCH 254/403] wifi: mt76: mt7996: expose per-band MAC addresses to cfg80211 mt7996/mt7992 are single-wiphy, multi-band devices. The driver assigns each band its own MAC address from a per-band EEPROM entry, or derives it from the primary band's address when that entry is empty, however only the primary band's is published as perm_addr. The per-band addresses are not exposed to cfg80211, so consumers usually fall back to deriving one from perm_addr. Store each band's address in wiphy->addresses[], indexed by radio, so cfg80211 exposes the address the hardware actually uses for that radio. addresses[0] is the primary band and matches perm_addr, as cfg80211 requires. Link: https://github.com/openwrt/openwrt/issues/23578 Tested-on: Gemtek W1700K (MT7996) Signed-off-by: Kenneth Kasilag Link: https://patch.msgid.link/20260620013850.3949359-1-kenneth@kasilag.me Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/init.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c index d6f9aa1ab52d..dbea4887b7ad 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -463,6 +463,8 @@ mt7996_init_wiphy_band(struct ieee80211_hw *hw, struct mt7996_phy *phy) radio->n_freq_range = 1; radio->iface_combinations = &if_comb; radio->n_iface_combinations = 1; + memcpy(dev->radio_addrs[n_radios].addr, phy->mt76->macaddr, ETH_ALEN); + hw->wiphy->n_addresses++; hw->wiphy->n_radio++; wiphy->available_antennas_rx |= phy->mt76->chainmask; @@ -505,6 +507,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed) wiphy->n_iface_combinations = 1; wiphy->radio = dev->radios; + wiphy->addresses = dev->radio_addrs; wiphy->reg_notifier = mt7996_regd_notifier; wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH | diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index d364fa8b8cf9..e03699e968ff 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -411,6 +411,7 @@ struct mt7996_dev { struct mt7996_phy *radio_phy[MT7996_MAX_RADIOS]; struct wiphy_radio radios[MT7996_MAX_RADIOS]; struct wiphy_radio_freq_range radio_freqs[MT7996_MAX_RADIOS]; + struct mac_address radio_addrs[MT7996_MAX_RADIOS]; struct mt7996_hif *hif2; struct mt7996_reg_desc reg; From 217f9e7bb02558759be9d9ecfe532e9708741c50 Mon Sep 17 00:00:00 2001 From: Eason Lai Date: Wed, 1 Jul 2026 09:06:54 +0800 Subject: [PATCH 255/403] wifi: mt76: mt792x: fix use-after-free in mt76_rx_poll_complete A use-after-free issue occurs in mt76_rx_poll_complete due to a race condition. The STA has already been removed, but the rx_status still had a pointer to the wcid in the STA. Set the links' wcid pointers to be NULL for a MLD in mt7925_sta_pre_rcu_remove() BUG: KASAN: invalid-access in mt76_rx_poll_complete+0x280/0x470 Call trace: dump_backtrace+0xec/0x128 show_stack+0x18/0x28 dump_stack_lvl+0x40/0xc8 print_report+0x1b8/0x710 kasan_report+0xe0/0x144 do_bad_area+0x120/0x260 do_tag_check_fault+0x20/0x34 do_mem_abort+0x54/0xa8 el1_abort+0x3c/0x5c el1h_64_sync_handler+0x40/0xcc el1h_64_sync+0x7c/0x80 mt76_rx_poll_complete+0x280/0x470 mt76_dma_rx_poll+0x114/0x51c mt792x_poll_rx+0x60/0xf8 napi_threaded_poll_loop+0xe0/0x450 napi_threaded_poll+0x80/0x9c kthread+0x11c/0x158 ret_from_fork+0x10/0x20 Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Eason Lai Link: https://patch.msgid.link/20260701010654.956863-1-eason.lai@mediatek.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/main.c | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 6be5b60b9bac..abb56f6fea77 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -2661,6 +2661,40 @@ static int mt7925_nan_peer_sched_changed(struct ieee80211_hw *hw, return err; } +static void mt7925_sta_pre_rcu_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + + mutex_lock(&dev->mutex); + spin_lock_bh(&dev->status_lock); + + if (ieee80211_vif_is_mld(vif)) { + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + unsigned long valid = mvif->valid_links; + struct mt792x_link_sta *mlink; + unsigned int link_id; + + for_each_set_bit(link_id, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + mlink = mt792x_sta_to_link(msta, link_id); + if (!mlink || !mlink->wcid.sta) + continue; + if (mlink->wcid.idx < ARRAY_SIZE(dev->wcid)) + rcu_assign_pointer(dev->wcid[mlink->wcid.idx], + NULL); + } + } else { + rcu_assign_pointer(dev->wcid[wcid->idx], NULL); + } + + spin_unlock_bh(&dev->status_lock); + mutex_unlock(&dev->mutex); +} + const struct ieee80211_ops mt7925_ops = { .tx = mt792x_tx, .start = mt7925_start, @@ -2673,7 +2707,7 @@ const struct ieee80211_ops mt7925_ops = { .start_ap = mt7925_start_ap, .stop_ap = mt7925_stop_ap, .sta_state = mt76_sta_state, - .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .sta_pre_rcu_remove = mt7925_sta_pre_rcu_remove, .set_key = mt7925_set_key, .sta_set_decap_offload = mt7925_sta_set_decap_offload, #if IS_ENABLED(CONFIG_IPV6) From 313f1a27ebcb8687e3f8629e36631f7c593212eb Mon Sep 17 00:00:00 2001 From: Ryan Leung Date: Sun, 19 Jul 2026 02:14:19 +0000 Subject: [PATCH 256/403] wifi: mt76: mt7915: add thermal zone device registration Register the mt7915 phy as a thermal zone sensor using devm_thermal_of_zone_register() so that device tree thermal-zones nodes can reference the Wi-Fi chip as a temperature source. This allows the kernel thermal governor to control external cooling devices such as PWM fans based on Wi-Fi chip temperature. Registration is non-fatal: -ENODEV is returned when no thermal-sensors DT property references this device, which is the expected case on platforms without a thermal zone configured. Signed-off-by: Ryan Leung Link: https://patch.msgid.link/20260719-mt7915-thermal-zone-device-registration-v2-1-0eac68c2741e@protonmail.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 30 +++++++++++++++++++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 1 + 2 files changed, 31 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 250c2d2479b0..6568d7b6bc0a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -177,6 +177,25 @@ static const struct thermal_cooling_device_ops mt7915_thermal_ops = { .set_cur_state = mt7915_thermal_set_cur_throttle_state, }; +static int mt7915_thermal_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct mt7915_phy *phy = thermal_zone_device_priv(tz); + int val; + + mutex_lock(&phy->dev->mt76.mutex); + val = mt7915_mcu_get_temperature(phy); + mutex_unlock(&phy->dev->mt76.mutex); + if (val < 0) + return val; + + *temp = val * 1000; + return 0; +} + +static const struct thermal_zone_device_ops mt7915_tz_ops = { + .get_temp = mt7915_thermal_get_temp, +}; + static void mt7915_unregister_thermal(struct mt7915_phy *phy) { struct wiphy *wiphy = phy->mt76->hw->wiphy; @@ -213,6 +232,17 @@ static int mt7915_thermal_init(struct mt7915_phy *phy) phy->throttle_temp[MT7915_CRIT_TEMP_IDX] = MT7915_CRIT_TEMP; phy->throttle_temp[MT7915_MAX_TEMP_IDX] = MT7915_MAX_TEMP; + phy->tzone = devm_thermal_of_zone_register(phy->dev->mt76.dev, + phy->mt76->band_idx, phy, + &mt7915_tz_ops); + if (IS_ERR(phy->tzone)) { + if (PTR_ERR(phy->tzone) != -ENODEV) + dev_warn(phy->dev->mt76.dev, + "failed to register thermal zone: %ld\n", + PTR_ERR(phy->tzone)); + phy->tzone = NULL; + } + if (!IS_REACHABLE(CONFIG_HWMON)) return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index bf1d915a3ca2..92e0f9f0169c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -205,6 +205,7 @@ struct mt7915_phy { struct ieee80211_vif *monitor_vif; + struct thermal_zone_device *tzone; struct thermal_cooling_device *cdev; u8 cdev_state; u8 throttle_state; From d16447d7470502e998d82e806ca7ed49d1baa853 Mon Sep 17 00:00:00 2001 From: Ahmed Naseef Date: Sun, 19 Jul 2026 12:25:30 +0400 Subject: [PATCH 257/403] wifi: mt76: mt7603: add 0x7592 EEPROM chip ID Some EcoNet based routers ship an on-flash EEPROM whose chip-id is 0x7592 instead of the expected 0x7603. The device probes as PCI 14c3:7603 and the hardware MT_HW_CHIPID register reports 0x7603, independent of the EEPROM value. This is seen across multiple EcoNet EN751221 and EN7528 based devices (for example the Genexis Platinum 4410). Signed-off-by: Ahmed Naseef Link: https://patch.msgid.link/20260719082530.3879831-1-naseefkm@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c index b89db2db6573..05f7aaefaf89 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c @@ -142,6 +142,7 @@ static int mt7603_check_eeprom(struct mt76_dev *dev) case 0x7628: case 0x7603: case 0x7600: + case 0x7592: return 0; default: return -EINVAL; From ef3e34874d2332d0f63e72c2c35ce5c93568c125 Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Tue, 14 Jul 2026 19:33:48 -0700 Subject: [PATCH 258/403] wifi: mt76: mt7925: ensure tx headroom in usb_sdio_tx_prepare_skb mt7925_usb_sdio_tx_prepare_skb() pushes a TX descriptor and a USB header onto every skb and assumes the headroom for them is already there. That holds for locally generated traffic, where mac80211 reserves hw->extra_tx_headroom, but forwarded frames are sent through ieee80211_8023_xmit(), which does not reserve it. Bridge a wired interface to an mt7925u AP and the first forwarded frame that arrives short panics the kernel: skbuff: skb_under_panic: len:415 put:4 tail:0x19b end:0x640 dev:wlan1 kernel BUG at net/core/skbuff.c:212! Call trace: skb_panic+0x58/0x60 (P) skb_push+0x58/0x60 mt7925_usb_sdio_tx_prepare_skb+0xf8/0x1b8 [mt7925_common] mt76u_tx_queue_skb+0xa0/0x1f8 [mt76_usb] __mt76_tx_queue_skb+0x54/0xe8 [mt76] mt76_txq_schedule.part.0+0x204/0x478 [mt76] mt76_txq_schedule_all+0x50/0x80 [mt76] mt792x_tx_worker+0x68/0x100 [mt792x_lib] __mt76_worker_fn+0x84/0x150 [mt76] Whether a given setup hits it depends on how much headroom the ingress netdev leaves in its rx skbs. Reproduced on a Raspberry Pi 5 bridging onboard ethernet to a Netgear A9000; originally reported on an MT7986 router running OpenWrt. Nick Morrow's testing on a Pi 4 (bcmgenet), which leaves more headroom, helped narrow the trigger to the ingress path. The same bug was fixed on mt7921 by commit 98c4d0abf5c4 ("mt76: mt7921: don't assume adequate headroom for SDIO headers"), but mt7925 was copied from mt7921 without the fix. Add the same guard here. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Cc: stable@vger.kernel.org Link: https://github.com/morrownr/mt76/issues/52 Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260715023348.59506-1-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index b9973c4eb7f6..53ced437c018 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1639,6 +1639,10 @@ int mt7925_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, if (!wcid) wcid = &dev->mt76.global_wcid; + err = skb_cow_head(skb, MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE); + if (err) + return err; + if (sta) { struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; From eb628006a2c69228b32129a3937baa52b970a118 Mon Sep 17 00:00:00 2001 From: JB Tsai Date: Tue, 30 Jun 2026 17:06:10 +0800 Subject: [PATCH 259/403] wifi: mt76: mt7925: Fix unregister deadlock During device shutdown or removal, a deadlock can occur between the PCIe remove path and the driver's asynchronous reset work. The unregistration path calls napi_disable() before cancelling the reset work. If the reset work runs concurrently, it may re-enable NAPI and schedule it. Because the device is being unregistered, this can lead to NAPI state corruption where NAPI is marked as scheduled but never polled, causing subsequent napi_disable() calls to hang forever. Fix this by: 1. Moving cancel_work_sync(&dev->reset_work) to the very start of mt7925e_unregister_device(), ensuring it is stopped before NAPI is disabled. 2. Setting the MT76_REMOVED flag early in the PCI remove path to prevent new reset work from being queued. 3. Checking MT76_REMOVED in mt7925_mac_reset_work() and aborting the reset early if the device is being removed. Co-developed-by: Fei Shao Signed-off-by: JB Tsai Tested-by: Rafael Passos Link: https://patch.msgid.link/20260630090610.586954-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 7 +++++++ drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 53ced437c018..23ed18bd7332 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1528,6 +1528,9 @@ void mt7925_mac_reset_work(struct work_struct *work) cancel_work_sync(&pm->wake_work); for (i = 0; i < 10; i++) { + if (test_bit(MT76_REMOVED, &dev->mphy.state)) + goto out; + mutex_lock(&dev->mt76.mutex); ret = mt792x_dev_reset(dev); mutex_unlock(&dev->mt76.mutex); @@ -1550,8 +1553,12 @@ void mt7925_mac_reset_work(struct work_struct *work) ieee80211_scan_completed(dev->mphy.hw, &info); } +out: dev->hw_full_reset = false; pm->suspended = false; + if (test_bit(MT76_REMOVED, &dev->mphy.state)) + return; + ieee80211_wake_queues(hw); ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 1514494f6c7b..366541bab223 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -47,13 +47,13 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev) if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN) wiphy_rfkill_stop_polling(hw->wiphy); + cancel_work_sync(&dev->reset_work); cancel_work_sync(&dev->init_work); mt76_unregister_device(&dev->mt76); mt76_for_each_q_rx(&dev->mt76, i) napi_disable(&dev->mt76.napi[i]); cancel_delayed_work_sync(&pm->ps_work); cancel_work_sync(&pm->wake_work); - cancel_work_sync(&dev->reset_work); mt7925_tx_token_put(dev); __mt792x_mcu_drv_pmctrl(dev); @@ -721,8 +721,8 @@ static void mt7925_pci_remove(struct pci_dev *pdev) struct mt76_dev *mdev = pci_get_drvdata(pdev); struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); - mt7925e_unregister_device(dev); set_bit(MT76_REMOVED, &mdev->phy.state); + mt7925e_unregister_device(dev); devm_free_irq(&pdev->dev, pdev->irq, dev); mt76_free_device(&dev->mt76); pci_free_irq_vectors(pdev); From 2889e84282dda147f10b10d94cf0efd90a349c53 Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Tue, 30 Jun 2026 17:02:18 +0800 Subject: [PATCH 260/403] wifi: mt76: mt7925: cancel pending mlo_pm_work If the device is reset, suspended or unregistered within that window, the pending work can still run and access vif/bss data that may already be freed, or send MCU commands while the firmware is not available. Add cancel_delayed_work_sync(&dev->mlo_pm_work) in all relevant teardown and suspend paths: - mt7925_mac_reset_work() (chip reset recovery) - mt7925e_unregister_device() (PCIe unbind) - mt7925_pci_suspend() (PCIe bus suspend) - mt7925_suspend() (mac80211 suspend) - mt7925u_suspend() (USB bus / runtime suspend) This ensures the work is stopped before the device state becomes invalid. Assisted-by: kimi-cli:kimi-k2.7 code Assisted-by: atomcode:glm-5.2 #Reported-by Fixes: 276a568832577 ("wifi: mt76: mt7925: update the power-saving flow") Cc: stable@vger.kernel.org Signed-off-by: Wentao Guan Link: https://patch.msgid.link/20260630090218.3202029-1-guanwentao@uniontech.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 1 + drivers/net/wireless/mediatek/mt76/mt7925/main.c | 1 + drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 2 ++ drivers/net/wireless/mediatek/mt76/mt7925/usb.c | 1 + 4 files changed, 5 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 23ed18bd7332..2931f5176137 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1525,6 +1525,7 @@ void mt7925_mac_reset_work(struct work_struct *work) cancel_delayed_work_sync(&dev->mphy.mac_work); cancel_delayed_work_sync(&pm->ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); cancel_work_sync(&pm->wake_work); for (i = 0; i < 10; i++) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index abb56f6fea77..5d0eac8c4e14 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1709,6 +1709,7 @@ static int mt7925_suspend(struct ieee80211_hw *hw, cancel_delayed_work_sync(&phy->mt76->mac_work); cancel_delayed_work_sync(&dev->pm.ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); mt792x_mutex_acquire(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 366541bab223..4e734f4f65d1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -53,6 +53,7 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev) mt76_for_each_q_rx(&dev->mt76, i) napi_disable(&dev->mt76.napi[i]); cancel_delayed_work_sync(&pm->ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); cancel_work_sync(&pm->wake_work); mt7925_tx_token_put(dev); @@ -740,6 +741,7 @@ static int mt7925_pci_suspend(struct device *device) dev->hif_resumed = false; flush_work(&dev->reset_work); cancel_delayed_work_sync(&pm->ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); cancel_work_sync(&pm->wake_work); mt7925_roc_abort_sync(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c index 49ad4fe9eb1b..1757023ea70d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c @@ -276,6 +276,7 @@ static int mt7925u_suspend(struct usb_interface *intf, pm_message_t state) pm->suspended = true; dev->hif_resumed = false; flush_work(&dev->reset_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); mt76_connac_mcu_set_hif_suspend(&dev->mt76, true, false); ret = wait_event_timeout(dev->wait, From 81faf578320df2dfc682a96baa6e85851dd68b6f Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Sat, 27 Jun 2026 13:29:46 -0700 Subject: [PATCH 261/403] wifi: mt76: mt7925: cancel mlo_pm_work on stop mt7925 queues mlo_pm_work with a 5 second delay during multi-link power-save setup and never cancels it on the stop path. If the device is torn down inside that window, the work outlives the teardown and its timer fires afterwards, trying to queue onto the workqueue that is already gone: workqueue: cannot queue mt7925_mlo_pm_work [mt7925_common] on wq phy0 WARNING: kernel/workqueue.c:2283 at __queue_work+0x59/0xa0, CPU#1: swapper/1/0 call_timer_fn+0x2a/0x140 __run_timers+0x203/0x330 run_timer_softirq+0x86/0xf0 mt7921 already has its own stop callback, so add one for mt7925 that cancels the work before calling mt792x_stop(). mt7925_ops backs both the PCIe and USB drivers, so this covers both. Fixes: 276a56883257 ("wifi: mt76: mt7925: update the power-saving flow") Cc: stable@vger.kernel.org Tested-by: Traockl <281473483+Traockl@users.noreply.github.com> Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260627202946.25598-1-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 5d0eac8c4e14..ec0bc61a9158 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -2696,10 +2696,19 @@ static void mt7925_sta_pre_rcu_remove(struct ieee80211_hw *hw, mutex_unlock(&dev->mutex); } +static void mt7925_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + cancel_delayed_work_sync(&dev->mlo_pm_work); + + mt792x_stop(hw, suspend); +} + const struct ieee80211_ops mt7925_ops = { .tx = mt792x_tx, .start = mt7925_start, - .stop = mt792x_stop, + .stop = mt7925_stop, .add_interface = mt7925_add_interface, .remove_interface = mt7925_remove_interface, .config = mt7925_config, From 915672c5ae32deeb72f4572856d123f314791136 Mon Sep 17 00:00:00 2001 From: Eason Lai Date: Wed, 6 May 2026 15:04:58 +0800 Subject: [PATCH 262/403] wifi: mt76: mt7921: Add PCIe AER handler support to prevent system crash When an AER error occurs and the bus is hung, the register reads return 0xFFFFFFFF, causing the DMA queue state to be corrupted and resulting in an invalid memory access when accessing q->desc[] or q->entry[]. Unable to handle kernel paging request at virtual address ffffffc01099eac0 pc : mt76_dma_add_buf+0x124/0x188 [mt76] lr : mt76_dma_rx_fill+0x11c/0x1d8 [mt76] sp : ffffffc016d9bbf0 x29: ffffffc016d9bc10 x28: 0000000000000000 x27: 0000000000000000 x26: ffffffb7855e50b8 x25: ffffffb80d04f000 x24: 0000000000000000 x23: 0000000000000ec0 x22: ffffffb796803648 x21: ffffffb796801f80 x20: ffffffb7968035f8 x19: 0000000000000ec0 x18: 0000000000000000 x17: 000000004ec00000 x16: 000000000ec00000 x15: ffffffc01099eac0 x14: 000000004ec00000 x13: 00000000ffc5a000 x12: ffffffc016d9bc32 x11: 00000000ffffffff x10: 0000000000000002 x9 : 0000000000000000 x8 : 000000000000b4ac x7 : 0000000000000a20 x6 : ffffffb6c1806400 x5 : 0000000000000000 x4 : ffffffb80d04f000 x3 : 0000000000000000 x2 : 0000000000000001 x1 : 000000000ec04000 x0 : ffffffb7968035f8 Call trace: mt76_dma_add_buf+0x124/0x188 [mt76 (HASH:1029 4)] mt76_dma_rx_reset+0xe8/0xfc [mt76 (HASH:1029 4)] mt7921_wpdma_reset+0x188/0x1b0 [mt7921e (HASH:ee48 5)] mt7921e_mac_reset+0x128/0x418 [mt7921e (HASH:ee48 5)] mt7921_mac_reset_work+0xac/0x1a8 [mt7921_common (HASH:f721 6)] process_one_work+0x188/0x514 worker_thread+0x12c/0x300 kthread+0x140/0x1fc ret_from_fork+0x10/0x30 Fix the invalid memory access by validating the DMA index read from the hardware before it is used as a queue index. An out-of-range value, such as the 0xFFFFFFFF returned while the bus is hung, is now clamped so it can no longer corrupt q->head or q->tail. In addition, check the bus_hung flag in mt7921_mac_reset_work() before attempting the reset sequence, reject MCU messages while the bus is hung, and install no-op bus operations when an unrecoverable AER error is detected, preventing further invalid hardware accesses. Due to hardware limitations - such as the lack of a connected hardware reset pin or the absence of host re-probe functionality - affected Wi-Fi devices may not fully recover to a normal operational state after certain errors, even with AER enabled. Fixes: 17f1de56df05 ("mt76: add common code shared between multiple chipsets") Co-developed-by: Sean Wang Signed-off-by: Sean Wang Co-developed-by: Jeff Hsu Signed-off-by: Jeff Hsu Signed-off-by: Eason Lai Co-developed-by: Michael Lo Link: https://patch.msgid.link/20260506070458.3096180-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 27 +++-- drivers/net/wireless/mediatek/mt76/mcu.c | 12 +- .../net/wireless/mediatek/mt76/mt76_connac.h | 5 + .../net/wireless/mediatek/mt76/mt7921/mac.c | 3 + .../net/wireless/mediatek/mt76/mt7921/pci.c | 103 ++++++++++++++++++ 5 files changed, 139 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 322041859217..3c4abeb5440a 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -186,6 +186,18 @@ mt76_dma_queue_magic_cnt_init(struct mt76_dev *dev, struct mt76_queue *q) } } +/* A hung bus (e.g. after a PCIe AER error) reads 0xffffffff from every + * register, so clamp an out-of-range index to the fallback to keep it from + * corrupting q->head/q->tail. + */ +static int +mt76_dma_read_dma_idx(struct mt76_queue *q, int fallback) +{ + u32 idx = Q_READ(q, dma_idx); + + return idx < q->ndesc ? idx : fallback; +} + static void mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q) { @@ -201,7 +213,8 @@ mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q) } Q_WRITE(q, desc_base, q->desc_dma); - q->head = Q_READ(q, dma_idx); + + q->head = mt76_dma_read_dma_idx(q, 0); q->tail = q->head; } @@ -419,7 +432,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush) if (flush) last = -1; else - last = Q_READ(q, dma_idx); + last = mt76_dma_read_dma_idx(q, -1); while (q->queued > 0 && q->tail != last) { mt76_dma_tx_cleanup_idx(dev, q, q->tail, &entry); @@ -432,7 +445,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush) } if (!flush && q->tail == last) - last = Q_READ(q, dma_idx); + last = mt76_dma_read_dma_idx(q, -1); } spin_unlock_bh(&q->cleanup_lock); @@ -625,8 +638,8 @@ mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q, buf.len = skb->len; spin_lock_bh(&q->lock); - mt76_dma_add_buf(dev, q, &buf, 1, tx_info, skb, NULL); - mt76_dma_kick_queue(dev, q); + if (mt76_dma_add_buf(dev, q, &buf, 1, tx_info, skb, NULL) >= 0) + mt76_dma_kick_queue(dev, q); spin_unlock_bh(&q->lock); return 0; @@ -983,7 +996,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) if ((q->flags & MT_QFLAG_WED_RRO_EN) || (IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) && mt76_queue_is_wed_tx_free(q))) { - dma_idx = Q_READ(q, dma_idx); + dma_idx = mt76_dma_read_dma_idx(q, q->tail); check_ddone = true; } @@ -993,7 +1006,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) if (check_ddone) { if (q->tail == dma_idx) - dma_idx = Q_READ(q, dma_idx); + dma_idx = mt76_dma_read_dma_idx(q, q->tail); if (q->tail == dma_idx) break; diff --git a/drivers/net/wireless/mediatek/mt76/mcu.c b/drivers/net/wireless/mediatek/mt76/mcu.c index cbfb3bbec503..7149b2f7aafd 100644 --- a/drivers/net/wireless/mediatek/mt76/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mcu.c @@ -78,15 +78,19 @@ int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb, unsigned long expires; int ret, seq; - if (mt76_is_sdio(dev)) - if (test_bit(MT76_RESET, &dev->phy.state) && atomic_read(&dev->bus_hung)) - return -EIO; - if (ret_skb) *ret_skb = NULL; mutex_lock(&dev->mcu.mutex); + if ((mt76_is_mmio(dev) && atomic_read(&dev->bus_hung)) || + (mt76_is_sdio(dev) && test_bit(MT76_RESET, &dev->phy.state) && + atomic_read(&dev->bus_hung))) { + orig_skb = skb; + ret = -EIO; + goto out; + } + if (dev->mcu_ops->mcu_skb_prepare_msg) { orig_skb = skb; ret = dev->mcu_ops->mcu_skb_prepare_msg(dev, skb, cmd, &seq); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 0951038916d3..b1677ca24703 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -48,6 +48,11 @@ enum rx_pkt_type { #define MT_TXD_LEN_MSDU_LAST BIT(14) #define MT_TXD_LEN_AMSDU_LAST BIT(15) +/* PCIE part */ +#define PCIE_AER_UNC_STATUS_OFFSET 0x204 +#define PCIE_AER_UNC_MASK_OFFSET 0x208 +#define PCIE_AER_CO_STATUS_OFFSET 0x210 + enum { CMD_CBW_20MHZ = IEEE80211_STA_RX_BW_20, CMD_CBW_40MHZ = IEEE80211_STA_RX_BW_40, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index f7d54472da1b..17014b1f91e0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -674,6 +674,9 @@ void mt7921_mac_reset_work(struct work_struct *work) cancel_work_sync(&pm->wake_work); for (i = 0; i < 10; i++) { + if (atomic_read(&dev->mt76.bus_hung)) + return; + mutex_lock(&dev->mt76.mutex); ret = mt792x_dev_reset(dev); mutex_unlock(&dev->mt76.mutex); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 2f51f653975f..56a914aaa81e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -607,6 +607,108 @@ static int mt7921_pci_resume(struct device *device) return err; } +static u32 mt7921_aer_rr(struct mt76_dev *mdev, u32 offset) +{ + return 0; +} + +static void mt7921_aer_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + ; +} + +static u32 mt791_aer_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + return 0; +} + +static const struct mt76_bus_ops mt7921_aer_bus_hung_ops = { + .rr = mt7921_aer_rr, + .wr = mt7921_aer_wr, + .rmw = mt791_aer_rmw, + .type = MT76_BUS_MMIO +}; + +static void mt7921_pci_set_aer_bus_hung_ops(struct mt792x_dev *dev) +{ + if (READ_ONCE(dev->mt76.bus) == &mt7921_aer_bus_hung_ops) + return; + + atomic_set(&dev->mt76.bus_hung, true); + WRITE_ONCE(dev->mt76.bus, &mt7921_aer_bus_hung_ops); +} + +static pci_ers_result_t mt7921_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + u32 aer_unc_val = 0, aer_co_val = 0; + + dev_err(mdev->dev, "PCIE error detect state: %d\n", state); + + /* Clear SW IRQ tasklet first */ + tasklet_kill(&mdev->irq_tasklet); + + if (state == pci_channel_io_perm_failure) { + mt7921_pci_set_aer_bus_hung_ops(dev); + return PCI_ERS_RESULT_DISCONNECT; + } + + pci_read_config_dword(pdev, PCIE_AER_UNC_STATUS_OFFSET, &aer_unc_val); + pci_read_config_dword(pdev, PCIE_AER_CO_STATUS_OFFSET, &aer_co_val); + + dev_warn(mdev->dev, "PCIE_AER_UNC_STATUS_OFFSET: 0x%x\n", aer_unc_val); + dev_warn(mdev->dev, "PCIE_AER_CO_STATUS_OFFSET: 0x%x\n", aer_co_val); + + /** + * Due to this error is from link error and this AER is un-correctable, + * so can't covered by device + **/ + if (aer_unc_val != 0) { + mt7921_pci_set_aer_bus_hung_ops(dev); + return PCI_ERS_RESULT_DISCONNECT; + } + + /** + * Try to recover it when state is pci_channel_io_frozen or + * AER is correctable error + **/ + if (state == pci_channel_io_frozen || aer_co_val != 0) { + /* Disable PCIE activity first. */ + pci_disable_device(pdev); + return PCI_ERS_RESULT_NEED_RESET; + } + + return PCI_ERS_RESULT_NONE; +} + +static pci_ers_result_t mt7921_slot_reset(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + int ret = 0; + + ret = pci_enable_device_mem(pdev); + + if (ret) { + dev_err(mdev->dev, "pci_enable_device_mem failed: %d\n", ret); + return PCI_ERS_RESULT_DISCONNECT; + } + + pci_set_master(pdev); + pci_restore_state(pdev); + pci_save_state(pdev); + /* Also try do the vendor reset to let it more clear. */ + mt792x_reset(mdev); + + return PCI_ERS_RESULT_RECOVERED; +} + +static const struct pci_error_handlers mt7921_err_handler = { + .error_detected = mt7921_error_detected, + .slot_reset = mt7921_slot_reset, +}; + static void mt7921_pci_shutdown(struct pci_dev *pdev) { mt7921_pci_remove(pdev); @@ -621,6 +723,7 @@ static struct pci_driver mt7921_pci_driver = { .remove = mt7921_pci_remove, .shutdown = mt7921_pci_shutdown, .driver.pm = pm_sleep_ptr(&mt7921_pm_ops), + .err_handler = &mt7921_err_handler, }; module_pci_driver(mt7921_pci_driver); From c410102bbff0803c9dc44056b917aff90abcbaec Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 13 Jun 2026 17:51:43 -0500 Subject: [PATCH 263/403] wifi: mt76: mt7927: set band index for sniffer mode Use the active channel context to select the SNIFFER command band index on MT7927, and fall back to the PHY chandef when no channel context is available. Also pass the same band index to the sniffer channel configuration. This keeps monitor setup on the correct band, especially when multiple PHY band contexts are present. Fixes: 35a5dcc71735 ("wifi: mt76: mt7925: add MT7927 PCIe support") Signed-off-by: Sean Wang Tested-by: Devin Wittmayer Link: https://patch.msgid.link/20260613225144.2414283-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 8abae7585e7b..c792c1befc1e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -2236,6 +2236,8 @@ int mt7925_get_txpwr_info(struct mt792x_dev *dev, u8 band_idx, struct mt7925_txp int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif, bool enable) { + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct ieee80211_chanctx_conf *ctx = mvif->bss_conf.mt76.ctx; struct { struct { u8 band_idx; @@ -2258,6 +2260,15 @@ int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif, }, }; + if (is_mt7927(&dev->mt76)) { + struct ieee80211_channel *chan; + + chan = ctx ? ctx->def.chan : mvif->phy->mt76->chandef.chan; + + if (chan) + req.hdr.band_idx = mt7927_band_idx(chan->band); + } + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), true); } @@ -2317,6 +2328,9 @@ int mt7925_mcu_config_sniffer(struct mt792x_vif *vif, }, }; + if (is_mt7927(mphy->dev)) + req.hdr.band_idx = mt7927_band_idx(chandef->chan->band); + if (chandef->chan->band < ARRAY_SIZE(ch_band)) req.tlv.ch_band = ch_band[chandef->chan->band]; if (chandef->width < ARRAY_SIZE(ch_width)) From 7673a2a159d74e7b1227be1d09f3e1040edf66ea Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 13 Jun 2026 17:51:44 -0500 Subject: [PATCH 264/403] wifi: mt76: mt7927: use real monitor vifs for dual-band monitors MT7927 needs monitor interfaces to be passed to the driver as real vifs so each monitor interface can be configured with its own band context. This is required to support concurrent 2 GHz and 5 GHz monitor operation on the same hw. Keep the existing virtual monitor behavior for older chips. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260613225144.2414283-2-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 2a8384a44d9f..837d7af984e8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -833,7 +833,10 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) ieee80211_hw_set(hw, HAS_RATE_CONTROL); ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD); ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD); - ieee80211_hw_set(hw, WANT_MONITOR_VIF); + if (is_mt7927(&dev->mt76)) + ieee80211_hw_set(hw, NO_VIRTUAL_MONITOR); + else + ieee80211_hw_set(hw, WANT_MONITOR_VIF); ieee80211_hw_set(hw, SUPPORTS_PS); ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); From 525c3eb2351f7292f4bc3ee276b6bc6e0614be5d Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Mon, 15 Jun 2026 16:21:37 -0500 Subject: [PATCH 265/403] wifi: mt76: mt7925: support new WoW pattern TLV Newer mt7925 firmware uses a shorter WoW pattern TLV with rsv[3]. Select the v2 layout based on the firmware build date, while keeping the old layout for older firmware. This also makes the WoW pattern handling compatible with newer devices such as MT7928. Tested-by: Stella Liu Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260615212137.477893-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 30 +++++++++++++++++-- .../net/wireless/mediatek/mt76/mt7925/mcu.h | 3 ++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index c792c1befc1e..e7e553b4f716 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -11,6 +11,7 @@ #define MT_STA_BFER BIT(0) #define MT_STA_BFEE BIT(1) +#define MT7925_WOW_PATTERN_NEW_FW_DATE "20260414153105" static bool mt7925_vif_is_nan(struct ieee80211_vif *vif) { @@ -229,6 +230,23 @@ mt7925_connac_mcu_set_wow_ctrl(struct mt76_phy *phy, struct ieee80211_vif *vif, sizeof(req), true); } +static bool mt7925_mcu_wow_pattern_old_tlv(struct mt76_dev *dev) +{ + const char *fw_version = dev->hw->wiphy->fw_version; + const char *build_date = strrchr(fw_version, '-'); + + if (!is_mt7925(dev)) + return false; + + if (!build_date) + return false; + + build_date++; + + return strncmp(build_date, MT7925_WOW_PATTERN_NEW_FW_DATE, + strlen(MT7925_WOW_PATTERN_NEW_FW_DATE)) < 0; +} + static int mt7925_mcu_set_wow_pattern(struct mt76_dev *dev, struct ieee80211_vif *vif, @@ -238,6 +256,8 @@ mt7925_mcu_set_wow_pattern(struct mt76_dev *dev, struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct mt7925_wow_pattern_tlv *tlv; struct sk_buff *skb; + int tlv_len; + bool old_tlv; struct { u8 bss_idx; u8 pad[3]; @@ -245,14 +265,18 @@ mt7925_mcu_set_wow_pattern(struct mt76_dev *dev, .bss_idx = mvif->idx, }; - skb = mt76_mcu_msg_alloc(dev, NULL, sizeof(hdr) + sizeof(*tlv)); + old_tlv = mt7925_mcu_wow_pattern_old_tlv(dev); + tlv_len = old_tlv ? sizeof(struct mt7925_wow_pattern_tlv) : + MT7925_WOW_PATTERN_TLV_V2_SIZE; + + skb = mt76_mcu_msg_alloc(dev, NULL, sizeof(hdr) + tlv_len); if (!skb) return -ENOMEM; skb_put_data(skb, &hdr, sizeof(hdr)); - tlv = (struct mt7925_wow_pattern_tlv *)skb_put(skb, sizeof(*tlv)); + tlv = (struct mt7925_wow_pattern_tlv *)skb_put_zero(skb, tlv_len); tlv->tag = cpu_to_le16(UNI_SUSPEND_WOW_PATTERN); - tlv->len = cpu_to_le16(sizeof(*tlv)); + tlv->len = cpu_to_le16(tlv_len); tlv->bss_idx = 0xF; tlv->data_len = pattern->pattern_len; tlv->enable = enable; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h index 1613c4765186..154f792a56bc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h @@ -608,6 +608,9 @@ struct mt7925_wow_pattern_tlv { u8 rsv[4]; }; +#define MT7925_WOW_PATTERN_TLV_V2_SIZE \ + (offsetof(struct mt7925_wow_pattern_tlv, rsv) + 3) + struct roc_acquire_tlv { __le16 tag; __le16 len; From 8a27c5c764040fbc990cc416a927b1d7eadf559f Mon Sep 17 00:00:00 2001 From: "Jared.Huang" Date: Wed, 17 Jun 2026 15:13:05 +0800 Subject: [PATCH 266/403] wifi: mt76: mt7925: update clc before setting sar power table Fix the power table update sequence to ensure CLC is loaded before setting SAR power table. The firmware requires CLC baseline to be established first to properly calculate the final power limit as min(clc_limit, rate_limit,sar_limit). Fixes: 9557b6fe0c8b ("wifi: mt76: mt7925: refine the txpower initialization flow") Signed-off-by: Jared.Huang Link: https://patch.msgid.link/20260617071305.1808394-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index ec0bc61a9158..2d79a895713c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1897,9 +1897,15 @@ static int mt7925_set_sar_specs(struct ieee80211_hw *hw, int err; mt792x_mutex_acquire(dev); - err = mt7925_set_tx_sar_pwr(hw, sar); - mt792x_mutex_release(dev); + err = mt7925_mcu_set_clc(dev, dev->mt76.alpha2, + dev->country_ie_env); + if (err < 0) + goto out; + err = mt7925_set_tx_sar_pwr(hw, sar); + +out: + mt792x_mutex_release(dev); return err; } From 9ddb7487aa7cccb6e1880b4151ab5895109eb8d6 Mon Sep 17 00:00:00 2001 From: "Jared.Huang" Date: Wed, 17 Jun 2026 15:13:20 +0800 Subject: [PATCH 267/403] wifi: mt76: mt7925: fix msg len mismatch between driver and firmware The mt7925_tx_power_limit_tlv struct begins with a 4-byte rsv[] field that acts as a UNI command header prefix. The firmware dispatcher did not use the 4-byte rsv[] and will only check the payloads after the 4-byte rsv[] As a result, the total message length minus the 4-byte prefix. Fix this by setting len to msg_len - 4. Fixes: ccb186326bb6 ("wifi: mt76: mt7925: fix incorrect length field in txpower command") Signed-off-by: Jared.Huang Link: https://patch.msgid.link/20260617071320.1808499-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index e7e553b4f716..6994af9298a8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -3991,7 +3991,7 @@ mt7925_mcu_rate_txpower_band(struct mt76_phy *phy, memcpy(tx_power_tlv->alpha2, dev->alpha2, sizeof(dev->alpha2)); tx_power_tlv->n_chan = num_ch; tx_power_tlv->tag = cpu_to_le16(0x1); - tx_power_tlv->len = cpu_to_le16(msg_len); + tx_power_tlv->len = cpu_to_le16(msg_len - 4); switch (band) { case NL80211_BAND_2GHZ: From 808f2767d4217a5b96f674288573b9b89d432eed Mon Sep 17 00:00:00 2001 From: Eason Lai Date: Fri, 3 Jul 2026 08:59:45 +0800 Subject: [PATCH 268/403] wifi: mt76: mt792x: Fix memory leak in SDIO TX path When tx_prepare_skb() returns an error in the SDIO TX path, the skb is not freed, leading to a memory leak. This can occur when zero-length frames (such as WNM NULL frames) are dropped to prevent potential hardware TX hangs. Fix this by properly releasing the skb with ieee80211_tx_status_ext() when tx_prepare_skb() fails. Fixes: b747fa343817 ("mt76: mt7915: drop zero-length packet to avoid Tx hang") Signed-off-by: Eason Lai Link: https://patch.msgid.link/20260703005945.2244533-1-eason.lai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/sdio.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/sdio.c b/drivers/net/wireless/mediatek/mt76/sdio.c index 8bae77c761be..ba5f123f7e39 100644 --- a/drivers/net/wireless/mediatek/mt76/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/sdio.c @@ -519,6 +519,10 @@ mt76s_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q, enum mt76_txq_id qid, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_sta *sta) { + struct ieee80211_tx_status status = { + .sta = sta, + }; + struct mt76_tx_info tx_info = { .skb = skb, }; @@ -531,8 +535,13 @@ mt76s_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q, skb->prev = skb->next = NULL; err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info); - if (err < 0) + if (err < 0) { + status.skb = tx_info.skb; + spin_lock_bh(&dev->rx_lock); + ieee80211_tx_status_ext(dev->hw, &status); + spin_unlock_bh(&dev->rx_lock); return err; + } q->entry[q->head].skb = tx_info.skb; q->entry[q->head].buf_sz = len; From 1f83a8378eb26c47ff947709e67d7145a84a3907 Mon Sep 17 00:00:00 2001 From: Charlie-cy Wu Date: Mon, 29 Jun 2026 16:35:43 +0800 Subject: [PATCH 269/403] wifi: mt76: mt7921: refactor regd update to fix recursive mutex deadlock Split mt7921_mcu_regd_update() into two functions to prevent recursive mutex acquisition. Introduce __mt7921_mcu_regd_update() as the internal implementation that assumes the mutex is already held by the caller, while mt7921_mcu_regd_update() remains as the external interface that handles mutex acquisition and release. This fixes a deadlock issue when mt7921_regd_set_6ghz_power_type() is called with the device mutex already held. Without this change, calling mt7921_mcu_regd_update() would attempt to acquire the same mutex again, causing a recursive lock deadlock. The __mt7921_mcu_regd_update() function can be safely called when the caller has already acquired the device mutex, avoiding the deadlock while maintaining proper synchronization for regulatory domain updates. Fixes: e88098133ed4 ("wifi: mt76: mt7921: refactor regulatory notifier flow") Signed-off-by: Charlie-cy Wu Link: https://patch.msgid.link/20260629083543.153564-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/main.c | 2 +- .../net/wireless/mediatek/mt76/mt7921/regd.c | 31 ++++++++++++------- .../net/wireless/mediatek/mt76/mt7921/regd.h | 2 ++ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 3480205d5fb9..68a059504e83 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -802,7 +802,7 @@ mt7921_regd_set_6ghz_power_type(struct ieee80211_vif *vif, bool is_add) out: if (vif->bss_conf.chanreq.oper.chan->band == NL80211_BAND_6GHZ) - mt7921_mcu_regd_update(dev, dev->mt76.alpha2, dev->country_ie_env); + __mt7921_mcu_regd_update(dev, dev->mt76.alpha2, dev->country_ie_env); } int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c index c0e2b48a50bf..80d1712ebfca 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -71,36 +71,45 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) } } -int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, - enum environment_cap country_ie_env) +int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) { struct mt76_dev *mdev = &dev->mt76; struct ieee80211_hw *hw = mdev->hw; struct wiphy *wiphy = hw->wiphy; - int ret = 0; + int ret; - dev->regd_in_progress = true; + lockdep_assert_held(&dev->mt76.mutex); - mt792x_mutex_acquire(dev); if (!dev->regd_change) - goto err; + return 0; ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env); if (ret < 0) - goto err; + return ret; mt7921_regd_channel_update(wiphy, dev); ret = mt76_connac_mcu_set_channel_domain(hw->priv); if (ret < 0) - goto err; + return ret; ret = mt7921_set_tx_sar_pwr(hw, NULL); - if (ret < 0) - goto err; -err: + return ret; +} + +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) +{ + int ret; + + dev->regd_in_progress = true; + + mt792x_mutex_acquire(dev); + ret = __mt7921_mcu_regd_update(dev, alpha2, country_ie_env); mt792x_mutex_release(dev); + dev->regd_change = false; dev->regd_in_progress = false; wake_up(&dev->wait); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h index 571f31629e9e..5b24d0902c36 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h @@ -10,6 +10,8 @@ struct regulatory_request; int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, enum environment_cap country_ie_env); +int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env); void mt7921_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); bool mt7921_regd_clc_supported(struct mt792x_dev *dev); From e9f3f1cc133fc2ea51a11be7cfe51733081d176a Mon Sep 17 00:00:00 2001 From: Charlie-cy Wu Date: Tue, 9 Jun 2026 14:50:24 +0800 Subject: [PATCH 270/403] wifi: mt76: mt7925: add regulatory wiphy self manager support Introduce regulatory wiphy self-managed mode support for MT7925, allowing the driver to manage its own regulatory domain independently from the kernel's regulatory framework. Signed-off-by: Charlie-cy Wu Link: https://patch.msgid.link/20260609065024.577079-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 3 +- .../net/wireless/mediatek/mt76/mt7925/regd.c | 220 ++++++++++++++++-- .../net/wireless/mediatek/mt76/mt7925/regd.h | 54 +++++ drivers/net/wireless/mediatek/mt76/mt792x.h | 2 + 4 files changed, 260 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 6994af9298a8..a6f28aa51a2f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -3703,7 +3703,8 @@ int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, /* submit all clc config */ for (i = 0; i < ARRAY_SIZE(phy->clc); i++) { - if (i == MT792x_CLC_BE_CTRL) + if (i == MT792x_CLC_BE_CTRL || + i == MT792x_CLC_REGD) continue; ret = __mt7925_mcu_set_clc(dev, alpha2, env_cap, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c index 0235437d11d5..f4beb7f52043 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c @@ -9,6 +9,15 @@ static bool mt7925_disable_clc; module_param_named(disable_clc, mt7925_disable_clc, bool, 0644); MODULE_PARM_DESC(disable_clc, "disable CLC support"); +static struct ieee80211_regdomain mt7925_regd_ww = { + .n_reg_rules = 1, + .alpha2 = "00", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412 - 10, 2462 + 10, 40, 6, 20, 0), + } +}; + bool mt7925_regd_clc_supported(struct mt792x_dev *dev) { if (mt7925_disable_clc || @@ -128,35 +137,37 @@ mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) } } -int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, - enum environment_cap country_ie_env) +static int mt7925_mcu_apply_regd(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap env) { struct ieee80211_hw *hw = mt76_hw(dev); struct wiphy *wiphy = hw->wiphy; - int ret = 0; + int ret; - dev->regd_in_progress = true; - - mt792x_mutex_acquire(dev); - if (!dev->regd_change) - goto err; - - ret = mt7925_mcu_set_clc(dev, alpha2, country_ie_env); + ret = mt7925_mcu_set_clc(dev, alpha2, env); if (ret < 0) - goto err; + return ret; mt7925_regd_be_ctrl(dev, alpha2); mt7925_regd_channel_update(wiphy, dev); ret = mt7925_mcu_set_channel_domain(hw->priv); if (ret < 0) - goto err; + return ret; - ret = mt7925_set_tx_sar_pwr(hw, NULL); - if (ret < 0) - goto err; + return mt7925_set_tx_sar_pwr(hw, NULL); +} -err: +int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) +{ + int ret = 0; + + dev->regd_in_progress = true; + + mt792x_mutex_acquire(dev); + if (dev->regd_change) + ret = mt7925_mcu_apply_regd(dev, alpha2, country_ie_env); mt792x_mutex_release(dev); dev->regd_change = false; dev->regd_in_progress = false; @@ -197,11 +208,178 @@ void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req) /* postpone the mcu update to resume */ return; + if (MT7925_REGD_SUPPORTED(&dev->phy)) { + mt7925_regd_update(&dev->phy, req->alpha2); + + return; + } + mt7925_mcu_regd_update(dev, req->alpha2, req->country_ie_env); return; } +static struct sk_buff * +mt7925_regd_query_regdb(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt7925_clc *clc = phy->clc[MT792x_CLC_REGD]; + struct mt7925_regd_query_req *req; + struct mt7925_regd_cc *regd_cc; + struct sk_buff *ret_skb = NULL; + u8 *pos, *last_pos; + int ret = 0; + + if (!clc) + return NULL; + + pos = clc->data; + last_pos = pos + le32_to_cpu(clc->len) - sizeof(struct mt7925_clc); + while (pos < last_pos) { + u32 req_len = 0; + u32 rules_len = 0; + u32 sign_len = 4; + u32 n_reg_rules; + + if (pos + sizeof(*regd_cc) > last_pos) + break; + + regd_cc = (struct mt7925_regd_cc *)pos; + n_reg_rules = le32_to_cpu(regd_cc->n_reg_rules); + if (n_reg_rules > NL80211_MAX_SUPP_REG_RULES) + break; + + rules_len = sizeof(struct mt7925_regd_rule_header) + + sizeof(struct mt7925_regd_rule) * n_reg_rules; + + if (pos + sizeof(*regd_cc) + rules_len + sign_len > last_pos) + break; + + pos += sizeof(*regd_cc) + rules_len + sign_len; + if (memcmp(regd_cc->alpha2, alpha2, 2)) + continue; + + req_len = sizeof(*req) + rules_len + sign_len; + req = kzalloc(req_len, GFP_KERNEL); + + if (!req) + return NULL; + + req->tag = cpu_to_le16(0x6); + req->len = cpu_to_le16(req_len - 4); + req->ver = regd_cc->ver; + req->sign_type = regd_cc->sign_type; + req->size = cpu_to_le32(rules_len + sign_len); + req->n_reg_rules = regd_cc->n_reg_rules; + + memcpy(req->alpha2, regd_cc->alpha2, 2); + memcpy(req->data, regd_cc->data, rules_len + sign_len); + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_UNI_CMD(SET_POWER_LIMIT), + req, req_len, true, &ret_skb); + kfree(req); + + return ret < 0 ? NULL : ret_skb; + } + + return NULL; +} + +int mt7925_regd_update(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt7925_regd_rule *mt7925_rule; + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_regdomain *regd; + struct ieee80211_reg_rule *rule; + struct mt7925_regd_rule_ev *ev; + int i, num_of_rules = 0; + struct sk_buff *skb; + int ret = 0; + + if (dev->hw_full_reset) + return 0; + + if (!MT7925_REGD_SUPPORTED(phy)) + return -EOPNOTSUPP; + + mt792x_mutex_acquire(dev); + skb = mt7925_regd_query_regdb(phy, alpha2); + mt792x_mutex_release(dev); + + if (!skb) { + ret = -EINVAL; + goto err; + } + + if (skb->len < sizeof(*ev) + 4) { + ret = -EINVAL; + goto err; + } + + ev = (struct mt7925_regd_rule_ev *)(skb->data + 4); + num_of_rules = le32_to_cpu(ev->n_reg_rules); + + if (!num_of_rules || + WARN_ON_ONCE(num_of_rules > NL80211_MAX_SUPP_REG_RULES)) { + ret = -EINVAL; + goto err; + } + + if (skb->len < struct_size(ev, reg_rule, num_of_rules) + 4) { + ret = -EINVAL; + goto err; + } + + regd = kzalloc(struct_size(regd, reg_rules, num_of_rules), GFP_KERNEL); + if (!regd) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < num_of_rules; i++) { + mt7925_rule = &ev->reg_rule[i]; + rule = ®d->reg_rules[i]; + + rule->freq_range.start_freq_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7925_rule->start_freq)); + rule->freq_range.end_freq_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7925_rule->end_freq)); + rule->freq_range.max_bandwidth_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7925_rule->max_bw)); + /* not used by fw */ + rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); + rule->power_rule.max_eirp = DBM_TO_MBM(22); + rule->flags = le32_to_cpu(mt7925_rule->flags); + } + + regd->n_reg_rules = num_of_rules; + regd->dfs_region = ev->dfs_region; + + memcpy(regd->alpha2, alpha2, 2); + memcpy(mdev->alpha2, alpha2, 2); + + dev->regd_change = true; + mt7925_mcu_regd_update(dev, alpha2, ENVIRON_ANY); + + ret = regulatory_set_wiphy_regd(wiphy, regd); + + kfree(regd); +err: + dev_kfree_skb(skb); + + if (ret < 0) + return regulatory_set_wiphy_regd(wiphy, &mt7925_regd_ww); + + return ret; +} +EXPORT_SYMBOL_GPL(mt7925_regd_update); + static bool mt7925_regd_is_valid_alpha2(const char *alpha2) { @@ -270,7 +448,9 @@ int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2) if (!memcmp(alpha2, mdev->alpha2, 2)) return 0; - if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) { + if (MT7925_REGD_SUPPORTED(phy)) { + return mt7925_regd_update(phy, alpha2); + } else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) { return regulatory_hint(wiphy, alpha2); } else { return mt7925_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR); @@ -285,7 +465,11 @@ int mt7925_regd_init(struct mt792x_phy *phy) struct mt792x_dev *dev = mt792x_hw_dev(hw); struct mt76_dev *mdev = &dev->mt76; - if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) { + if (MT7925_REGD_SUPPORTED(phy)) { + wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED | + REGULATORY_DISABLE_BEACON_HINTS; + return mt7925_regd_update(phy, "00"); + } else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) { wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE | REGULATORY_DISABLE_BEACON_HINTS; } else { diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.h b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h index 0b0754cf8ae7..65ef2b140f51 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h @@ -6,12 +6,66 @@ #include "mt7925.h" +struct mt7925_regd_rule_header { + u8 alpha2[2]; + u8 dfs_region; + u8 rsv[13]; +}; + +struct mt7925_regd_rule { + __le32 start_freq; + __le32 end_freq; + __le32 max_bw; + __le32 eirp; + __le32 flags; + u8 rsv[12]; +}; + +struct mt7925_regd_cc { + u8 alpha2[2]; + u8 ver; + u8 rsv; + __le32 n_reg_rules; + u8 sign_type; + u8 rsv1[7]; + u8 data[]; +}; + +struct mt7925_regd_rule_ev { + __le16 tag; + __le16 len; + __le32 n_reg_rules; + u8 dfs_region; + u8 rsv[15]; + struct mt7925_regd_rule reg_rule[]; +}; + +struct mt7925_regd_query_req { + u8 rsv[4]; + __le16 tag; + __le16 len; + u8 ver; + u8 sign_type; + u8 rsv1[2]; + __le32 size; + u8 alpha2[2]; + u8 rsv2[2]; + __le32 n_reg_rules; + u8 rsv3[64]; + u8 data[]; +}; + +#define MT7925_REGD_SUPPORTED(phy) \ + (((phy)->chip_cap & MT792x_CHIP_CAP_REGD_EN) && \ + (phy)->clc[MT792x_CLC_REGD]) + int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, enum environment_cap country_ie_env); void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2); void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req); bool mt7925_regd_clc_supported(struct mt792x_dev *dev); +int mt7925_regd_update(struct mt792x_phy *phy, char *alpha2); int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2); bool mt7925_regd_is_valid_channel(struct mt792x_dev *dev, enum nl80211_band band, diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index e98c4fd81044..9567ff883b28 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -30,6 +30,7 @@ #define MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN BIT(1) #define MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN BIT(3) #define MT792x_CHIP_CAP_11D_EN BIT(4) +#define MT792x_CHIP_CAP_REGD_EN BIT(5) #define MT792x_CHIP_CAP_MLO_EN BIT(8) #define MT792x_CHIP_CAP_MLO_EML_EN BIT(9) @@ -83,6 +84,7 @@ enum { MT792x_CLC_POWER, MT792x_CLC_POWER_EXT, MT792x_CLC_BE_CTRL, + MT792x_CLC_REGD, MT792x_CLC_MAX_NUM, }; From 9b80bd9cab40c402a75e8dc850e97503ad3b8bbc Mon Sep 17 00:00:00 2001 From: Charlie-cy Wu Date: Tue, 9 Jun 2026 14:50:36 +0800 Subject: [PATCH 271/403] wifi: mt76: mt7921: add regulatory wiphy self manager support Introduce regulatory wiphy self-managed mode support for MT7921, allowing the driver to manage its own regulatory domain independently from the kernel's regulatory framework. Signed-off-by: Charlie-cy Wu Link: https://patch.msgid.link/20260609065036.577329-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 + .../net/wireless/mediatek/mt76/mt7921/mcu.c | 3 + .../net/wireless/mediatek/mt76/mt7921/regd.c | 188 +++++++++++++++++- .../net/wireless/mediatek/mt76/mt7921/regd.h | 55 ++++- 4 files changed, 242 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 8022ffd7af5a..8198efc6c05d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1403,6 +1403,7 @@ enum { MCU_CE_CMD_FWLOG_2_HOST = 0xc5, MCU_CE_CMD_GET_WTBL = 0xcd, MCU_CE_CMD_GET_TXPWR = 0xd0, + MCU_CE_CMD_SET_REGD_CH = 0xd1, }; enum { diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 17d97407b8f3..a118a301564c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -1425,6 +1425,9 @@ int mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, /* submit all clc config */ for (i = 0; i < ARRAY_SIZE(phy->clc); i++) { + if (i == MT792x_CLC_REGD) + continue; + ret = __mt7921_mcu_set_clc(dev, alpha2, env_cap, phy->clc[i], i); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c index 80d1712ebfca..4a8ea4624fee 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -10,6 +10,15 @@ static bool mt7921_disable_clc; module_param_named(disable_clc, mt7921_disable_clc, bool, 0644); MODULE_PARM_DESC(disable_clc, "disable CLC support"); +static struct ieee80211_regdomain mt7921_regd_ww = { + .n_reg_rules = 1, + .alpha2 = "00", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412 - 10, 2462 + 10, 40, 6, 20, 0), + } +}; + bool mt7921_regd_clc_supported(struct mt792x_dev *dev) { if (mt7921_disable_clc || @@ -33,6 +42,9 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) np = mt76_find_power_limits_node(mdev); sband = wiphy->bands[NL80211_BAND_5GHZ]; + if (!sband) + return; + band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL; for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; @@ -151,10 +163,176 @@ void mt7921_regd_notifier(struct wiphy *wiphy, if (pm->suspended) return; + if (MT7921_REGD_SUPPORTED(&dev->phy)) { + mt7921_regd_update(&dev->phy, req->alpha2); + + return; + } + mt7921_mcu_regd_update(dev, req->alpha2, req->country_ie_env); } +static struct sk_buff * +mt7921_regd_query_regdb(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt7921_clc *clc = phy->clc[MT792x_CLC_REGD]; + struct mt7921_regd_query_req *req; + struct mt7921_regd_cc *regd_cc; + struct sk_buff *ret_skb = NULL; + u8 *pos, *last_pos; + int ret = 0; + + if (!clc) + return NULL; + + pos = clc->data; + last_pos = pos + le32_to_cpu(clc->len) - sizeof(struct mt7921_clc); + while (pos < last_pos) { + u32 req_len = 0; + u32 rules_len = 0; + u32 sign_len = 4; + u32 n_reg_rules; + + if (pos + sizeof(*regd_cc) > last_pos) + break; + + regd_cc = (struct mt7921_regd_cc *)pos; + n_reg_rules = le32_to_cpu(regd_cc->n_reg_rules); + if (n_reg_rules > NL80211_MAX_SUPP_REG_RULES) + break; + + rules_len = sizeof(struct mt7921_regd_rule_header) + + sizeof(struct mt7921_regd_rule) * n_reg_rules; + + if (pos + sizeof(*regd_cc) + rules_len + sign_len > last_pos) + break; + + pos += sizeof(*regd_cc) + rules_len + sign_len; + if (memcmp(regd_cc->alpha2, alpha2, 2)) + continue; + + req_len = sizeof(*req) + rules_len + sign_len; + req = kzalloc(req_len, GFP_KERNEL); + + if (!req) + return NULL; + + req->ver = regd_cc->ver; + req->sign_type = regd_cc->sign_type; + req->size = cpu_to_le32(rules_len + sign_len); + req->n_reg_rules = regd_cc->n_reg_rules; + + memcpy(req->alpha2, regd_cc->alpha2, 2); + memcpy(req->data, regd_cc->data, rules_len + sign_len); + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_CE_CMD(SET_REGD_CH), + req, req_len, true, &ret_skb); + + kfree(req); + + return ret < 0 ? NULL : ret_skb; + } + + return NULL; +} + +int mt7921_regd_update(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt7921_regd_rule *mt7921_rule; + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_regdomain *regd; + struct ieee80211_reg_rule *rule; + struct mt7921_regd_rule_ev *ev; + int i, num_of_rules = 0; + struct sk_buff *skb; + int ret = 0; + + if (dev->hw_full_reset) + return 0; + + if (!MT7921_REGD_SUPPORTED(phy)) + return -EOPNOTSUPP; + + mt792x_mutex_acquire(dev); + skb = mt7921_regd_query_regdb(phy, alpha2); + mt792x_mutex_release(dev); + + if (!skb) { + ret = -EINVAL; + goto err; + } + + if (skb->len < sizeof(*ev) + 4) { + ret = -EINVAL; + goto err; + } + + ev = (struct mt7921_regd_rule_ev *)(skb->data + 4); + num_of_rules = le32_to_cpu(ev->n_reg_rules); + + if (!num_of_rules || + WARN_ON_ONCE(num_of_rules > NL80211_MAX_SUPP_REG_RULES)) { + ret = -EINVAL; + goto err; + } + + if (skb->len < struct_size(ev, reg_rule, num_of_rules) + 4) { + ret = -EINVAL; + goto err; + } + + regd = kzalloc(struct_size(regd, reg_rules, num_of_rules), GFP_KERNEL); + if (!regd) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < num_of_rules; i++) { + mt7921_rule = &ev->reg_rule[i]; + rule = ®d->reg_rules[i]; + + rule->freq_range.start_freq_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7921_rule->start_freq)); + rule->freq_range.end_freq_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7921_rule->end_freq)); + rule->freq_range.max_bandwidth_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7921_rule->max_bw)); + /* not used by fw */ + rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); + rule->power_rule.max_eirp = DBM_TO_MBM(22); + rule->flags = le32_to_cpu(mt7921_rule->flags); + } + + regd->n_reg_rules = num_of_rules; + regd->dfs_region = ev->dfs_region; + + memcpy(regd->alpha2, alpha2, 2); + memcpy(mdev->alpha2, alpha2, 2); + + dev->regd_change = true; + mt7921_mcu_regd_update(dev, alpha2, ENVIRON_ANY); + + ret = regulatory_set_wiphy_regd(wiphy, regd); + + kfree(regd); +err: + dev_kfree_skb(skb); + + if (ret < 0) + return regulatory_set_wiphy_regd(wiphy, &mt7921_regd_ww); + + return ret; +} +EXPORT_SYMBOL_GPL(mt7921_regd_update); + static bool mt7921_regd_is_valid_alpha2(const char *alpha2) { @@ -192,7 +370,9 @@ int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2) if (!memcmp(alpha2, mdev->alpha2, 2)) return 0; - if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) + if (MT7921_REGD_SUPPORTED(phy)) + return mt7921_regd_update(phy, alpha2); + else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) return regulatory_hint(wiphy, alpha2); else return mt7921_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR); @@ -205,7 +385,11 @@ int mt7921_regd_init(struct mt792x_phy *phy) struct mt792x_dev *dev = mt792x_hw_dev(hw); struct mt76_dev *mdev = &dev->mt76; - if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) + if (MT7921_REGD_SUPPORTED(phy)) { + wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED | + REGULATORY_DISABLE_BEACON_HINTS; + return mt7921_regd_update(phy, "00"); + } else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE | REGULATORY_DISABLE_BEACON_HINTS; else diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h index 5b24d0902c36..d4223ba8fbd5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h @@ -4,9 +4,57 @@ #ifndef __MT7921_REGD_H #define __MT7921_REGD_H -struct mt792x_dev; -struct wiphy; -struct regulatory_request; +#include "mt7921.h" + +struct mt7921_regd_rule_header { + u8 alpha2[2]; + u8 dfs_region; + u8 rsv[13]; +}; + +struct mt7921_regd_rule { + __le32 start_freq; + __le32 end_freq; + __le32 max_bw; + __le32 eirp; + __le32 flags; + u8 rsv[12]; +}; + +struct mt7921_regd_cc { + u8 alpha2[2]; + u8 ver; + u8 rsv; + __le32 n_reg_rules; + u8 sign_type; + u8 rsv1[7]; + u8 data[]; +}; + +struct mt7921_regd_rule_ev { + __le16 tag; + __le16 len; + __le32 n_reg_rules; + u8 dfs_region; + u8 rsv[15]; + struct mt7921_regd_rule reg_rule[]; +}; + +struct mt7921_regd_query_req { + u8 ver; + u8 sign_type; + u8 rsv1[2]; + __le32 size; + u8 alpha2[2]; + u8 rsv2[2]; + __le32 n_reg_rules; + u8 rsv3[64]; + u8 data[]; +}; + +#define MT7921_REGD_SUPPORTED(phy) \ + (((phy)->chip_cap & MT792x_CHIP_CAP_REGD_EN) && \ + (phy)->clc[MT792x_CLC_REGD]) int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, enum environment_cap country_ie_env); @@ -17,5 +65,6 @@ void mt7921_regd_notifier(struct wiphy *wiphy, bool mt7921_regd_clc_supported(struct mt792x_dev *dev); int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2); int mt7921_regd_init(struct mt792x_phy *phy); +int mt7921_regd_update(struct mt792x_phy *phy, char *alpha2); #endif From 6bb5066cfcd4296ac5e0b6872f43f96a43bfe566 Mon Sep 17 00:00:00 2001 From: Chad Monroe Date: Tue, 21 Jul 2026 18:53:32 +0000 Subject: [PATCH 272/403] wifi: mt76: mt7996: fix EAPOL source BSS for non-MLD stations A non-MLD station's EAPOL and data frames are tagged with link_id == IEEE80211_LINK_UNSPECIFIED, which now skips the per-link lookup in mt7996_mac_write_txwi() and leaves omac_idx/band_idx/wmm_idx at slot 0. When the radio also runs AP VAPs the station's omac is non-zero (get_omac_idx() prefers HW BSSID slots 1-3), so its EAPOL frames egress from the wrong BSS and the 4-way handshake times out even though association succeeds. In mt7996_tx_prepare_skb(), resolve the link from the peer wcid when link_id is UNSPECIFIED and the wcid is not the global entry, restoring the pre-MLO behaviour for station traffic. Fixes: 729c83a3330c ("wifi: mt76: mt7996: fix reading zeroed info->control.flags after mt76_tx_status_skb_add()") Signed-off-by: Chad Monroe Link: https://patch.msgid.link/20260721185333.2419297-1-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 0eebc8182ca9..01208eac2a25 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1030,6 +1030,11 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, IEEE80211_TX_CTRL_MLO_LINK); } + /* non-MLD frames are LINK_UNSPECIFIED; use the wcid's own link */ + if (link_id == IEEE80211_LINK_UNSPECIFIED && + wcid != &dev->mt76.global_wcid) + link_id = wcid->link_id; + if (link_id != wcid->link_id && link_id != IEEE80211_LINK_UNSPECIFIED) { if (msta) { struct mt7996_sta_link *msta_link = From 2087f029924b75324cfa9a41ba1a349a9620f06e Mon Sep 17 00:00:00 2001 From: Chad Monroe Date: Tue, 21 Jul 2026 18:53:33 +0000 Subject: [PATCH 273/403] wifi: mt76: mt7996: fix non-MLD station num_sta leak The MLO link-reconfiguration rework moved the per-phy num_sta decrement inside a link_valid guard. link_valid is only set for MLO links, but num_sta is incremented for every station link, including the non-MLO deflink. Non-MLO stations bump num_sta on association and never drop it on removal. A non-zero num_sta forces connected-mode off-channel scanning which prevents the directed probe exchange needed to find hidden APs. Decrement phy->num_sta on the actual link teardown, pairing it with the unconditional increment on link creation. Fixes: e8c819df0243 ("wifi: mt76: mt7996: Destroy active sta links in mt7996_mac_sta_remove()") Signed-off-by: Chad Monroe Link: https://patch.msgid.link/20260721185333.2419297-2-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index afbcc8c7b18b..574aeac286cc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1202,15 +1202,9 @@ void mt7996_mac_sta_remove_link(struct mt7996_dev *dev, mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid); if (msta_link->wcid.link_valid) { - struct mt7996_phy *phy; - mt7996_mac_wtbl_update(dev, msta_link->wcid.idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); - phy = __mt7996_phy(dev, msta_link->wcid.phy_idx); - if (phy) - phy->mt76->num_sta--; - if (msta->deflink_id == link_id) { msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; if (msta->seclink_id == link_id) { @@ -1236,6 +1230,12 @@ void mt7996_mac_sta_remove_link(struct mt7996_dev *dev, } if (flush) { + struct mt7996_phy *phy = + __mt7996_phy(dev, msta_link->wcid.phy_idx); + + if (phy) + phy->mt76->num_sta--; + rcu_assign_pointer(msta->link[link_id], NULL); rcu_assign_pointer(dev->mt76.wcid[msta_link->wcid.idx], NULL); mt76_wcid_mask_clear(dev->mt76.wcid_mask, msta_link->wcid.idx); From 29e889c4ada83c69d10a3937f5ae2934306e2e3d Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Fri, 13 Mar 2026 14:21:50 +0800 Subject: [PATCH 274/403] wifi: mt76: mt7996: fix capability of EHT-MCS 15 in MRU According to the definition in IEEE Std 802.11be-2024, Table 9-417r: - If 80 MHz is not supported, bit 1-3 are set to 0. - If 160 MHz is not supported, bit 2-3 are set to 0. - If 320 MHz is not supported, bit 3 is set to 0. Fixes: 348533eb968d ("wifi: mt76: mt7996: add EHT capability init") Signed-off-by: Shayne Chen Link: https://patch.msgid.link/20260313062150.3165433-2-shayne.chen@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/init.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c index dbea4887b7ad..2ff93bd8b6dc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -1564,7 +1564,6 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band, struct ieee80211_sta_eht_cap *eht_cap = &data->eht_cap; struct ieee80211_eht_cap_elem_fixed *eht_cap_elem = &eht_cap->eht_cap_elem; struct ieee80211_eht_mcs_nss_supp *eht_nss = &eht_cap->eht_mcs_nss_supp; - enum nl80211_chan_width width = phy->mt76->chandef.width; int nss = hweight8(phy->mt76->antenna_mask); int sts = hweight16(phy->mt76->chainmask); u8 val; @@ -1640,11 +1639,16 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band, u8_encode_bits(u8_get_bits(1, GENMASK(1, 0)), IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK); - val = width == NL80211_CHAN_WIDTH_320 ? 0xf : - width == NL80211_CHAN_WIDTH_160 ? 0x7 : - width == NL80211_CHAN_WIDTH_80 ? 0x3 : 0x1; - eht_cap_elem->phy_cap_info[6] = - u8_encode_bits(val, IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK); + eht_cap_elem->phy_cap_info[6] = IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK; + if (band != NL80211_BAND_6GHZ) { + eht_cap_elem->phy_cap_info[6] &= + ~IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_320MHZ; + + if (band != NL80211_BAND_5GHZ) + eht_cap_elem->phy_cap_info[6] &= + ~(IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_160MHZ | + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_80MHZ); + } val = u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_RX) | u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_TX); From 86897f106669c07eea4c34b54c3268d448d41426 Mon Sep 17 00:00:00 2001 From: Rex Lu Date: Wed, 22 Jul 2026 08:25:53 +0000 Subject: [PATCH 275/403] wifi: mt76: fix RX data queuing of RRO 3.0 For RRO 3.0, RX data released from a RRO data queue should be put to the indicator queue. The frames are processed and completed in the context of the indicator queue NAPI, which only polls skbs queued on the MT_RXQ_RRO_IND list; frames queued under the data queue id are left sitting on that list until the data queue NAPI happens to run, stalling and reordering RX data. Fixes: b1e58e137b61 ("wifi: mt76: mt7996: Introduce RRO MSDU callbacks") Signed-off-by: Rex Lu Signed-off-by: Shayne Chen Link: https://patch.msgid.link/20260722082610.2699628-1-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index c4cbf7195b80..f92277770488 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -893,6 +893,7 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q) struct sk_buff *skb = phy->rx_amsdu[q].head; struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; struct mt76_dev *dev = phy->dev; + struct mt76_queue *rxq = &dev->q_rx[q]; phy->rx_amsdu[q].head = NULL; phy->rx_amsdu[q].tail = NULL; @@ -921,6 +922,13 @@ static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q) return; } } + + /* RRO 3.0 data queue skbs are processed and completed in the context + * of the indicator queue NAPI, which only polls its own skb list + */ + if (mt76_queue_is_wed_rro_data(rxq) && dev->hwrro_mode == MT76_HWRRO_V3) + q = MT_RXQ_RRO_IND; + __skb_queue_tail(&dev->rx_skb[q], skb); } From ce35ecffc96e6d097d27b6fe30677a2cfe2e0461 Mon Sep 17 00:00:00 2001 From: Peter Chiu Date: Wed, 22 Jul 2026 08:25:55 +0000 Subject: [PATCH 276/403] wifi: mt76: mt7996: fix MLD ID in MAC TXD and HIF TXP Problem: MCU command timeout while the firmware state is normal, and the firmware keeps showing the error log "ERROR!! NO PAUSE...". Root cause: If the MLD_ID field in the TXD is neither the primary link id nor the secondary link id, it may lead to a firmware busy loop when the third link is in power saving mode. Remap frames directed to a third link to the primary link wcid. Since TX status events and txfree completions carry the wcid the firmware saw, use the remapped wcid for packet id tracking and non-AQL packet accounting as well, while the frame keeps its original link context for addressing, band and OMAC selection. Fixes: 85cd5534a3f2 ("wifi: mt76: mt7996: use correct link_id when filling TXD and TXP") Signed-off-by: Peter Chiu Link: https://patch.msgid.link/20260722082610.2699628-3-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/mac.c | 29 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7996/main.c | 2 +- .../wireless/mediatek/mt76/mt7996/mt7996.h | 1 + 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 01208eac2a25..a5a992bdbd15 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -854,6 +854,33 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT); } +/* The WLAN_IDX in the TXD and TXP must belong to the primary or secondary + * link of an MLD station; any other link id can make the firmware spin when + * that link is in powersave. Completion events carry the same index, so the + * wcid used for status tracking and accounting must match it + */ +struct mt76_wcid *mt7996_get_tx_wcid(struct mt76_wcid *wcid) +{ + struct mt7996_sta_link *msta_link; + struct mt7996_sta *msta; + + if (!wcid->sta) + return wcid; + + msta_link = container_of(wcid, struct mt7996_sta_link, wcid); + msta = msta_link->sta; + + if (!msta || wcid->link_id == msta->seclink_id || + wcid->link_id == msta->deflink_id) + return wcid; + + msta_link = mt7996_sta_link(msta, msta->deflink_id); + if (msta_link) + return &msta_link->wcid; + + return wcid; +} + void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, @@ -1091,6 +1118,8 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, tx_info->buf[1].len, DMA_TO_DEVICE); } + wcid = mt7996_get_tx_wcid(wcid); + pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); memset(txwi_ptr, 0, MT_TXD_SIZE); /* Transmit non qos data by 802.11 header and need to fill txd by host*/ diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 574aeac286cc..f291560ae93c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1579,7 +1579,7 @@ static void mt7996_tx(struct ieee80211_hw *hw, if (msta_link) wcid = &msta_link->wcid; } - mt76_tx(mphy, control->sta, wcid, skb); + mt76_tx(mphy, control->sta, mt7996_get_tx_wcid(wcid), skb); unlock: rcu_read_unlock(); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index e03699e968ff..60397a1b1a2b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -872,6 +872,7 @@ bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask); void mt7996_mac_reset_counters(struct mt7996_phy *phy); void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy); void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); +struct mt76_wcid *mt7996_get_tx_wcid(struct mt76_wcid *wcid); void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, From 8ae659743ba936b22ecb4620815887728e2820d6 Mon Sep 17 00:00:00 2001 From: Michael-CY Lee Date: Wed, 22 Jul 2026 08:25:56 +0000 Subject: [PATCH 277/403] wifi: mt76: fix non-AQL packet accounting for MLO stations __mt76_tx_queue_skb() overrides the wcid passed by the driver with sta->drv_priv, so the wcid might incorrectly be changed after TX, causing wcid->non_aql_packets to be counted on the wrong wcid. For example, on the AP side, if a station's setup link is the 5G link and the station uses 2G to transmit a frame, the value of non_aql_packets is increased on the 5G wcid but decreased on the 2G wcid. Once the inflated counter exceeds MT_MAX_NON_AQL_PKT, the TX scheduler permanently refuses to service the station. Drop the reassignment and account on the wcid used for transmission. This also records the actual wcid in the queue entry. Fixes: e1378e5228aa ("mt76: rely on AQL for burst size limits on tx queueing") Signed-off-by: Michael-CY Lee Link: https://patch.msgid.link/20260722082610.2699628-4-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/tx.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index f96d9c471853..de8af32f15e5 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -324,10 +324,6 @@ __mt76_tx_queue_skb(struct mt76_phy *phy, int qid, struct sk_buff *skb, if (idx < 0 || !sta) return idx; - wcid = (struct mt76_wcid *)sta->drv_priv; - if (!wcid->sta) - return idx; - q->entry[idx].wcid = wcid->idx; if (!non_aql) From f137fabc1313427e08af06a414d929ebd9fd37d6 Mon Sep 17 00:00:00 2001 From: Michael-CY Lee Date: Wed, 22 Jul 2026 08:25:57 +0000 Subject: [PATCH 278/403] wifi: mt76: assign link_id when sending probe request during scan The link_id in info->control.flags is required by mt7996 to select the correct mt76_wcid for transmission. Not assigning the link_id in info->control.flags is equivalent to assigning the link_id to 0, causing mt7996 to select link_id 0 for transmission, so probe requests sent on behalf of an MLD vif scanning via a different link were transmitted with the wrong per-link wcid. Fixes: 31083e38548f ("wifi: mt76: add code for emulating hardware scanning") Signed-off-by: Michael-CY Lee Link: https://patch.msgid.link/20260722082610.2699628-5-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/scan.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c index 7fe1b1fbb699..325638d587c9 100644 --- a/drivers/net/wireless/mediatek/mt76/scan.c +++ b/drivers/net/wireless/mediatek/mt76/scan.c @@ -48,6 +48,7 @@ mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid) struct mt76_phy *phy = dev->scan.phy; struct ieee80211_tx_info *info; struct sk_buff *skb; + u8 link_id; skb = ieee80211_probereq_get(phy->hw, vif->addr, ssid->ssid, ssid->ssid_len, req->ie_len); @@ -77,6 +78,10 @@ mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid) info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; info->control.flags |= IEEE80211_TX_CTRL_DONT_USE_RATE_MASK; + link_id = mvif->wcid ? mvif->wcid->link_id : IEEE80211_LINK_UNSPECIFIED; + info->control.flags &= ~IEEE80211_TX_CTRL_MLO_LINK; + info->control.flags |= u32_encode_bits(link_id, IEEE80211_TX_CTRL_MLO_LINK); + mt76_tx(phy, NULL, mvif->wcid, skb); out: From 2243778a5fae8329ab5f18e7adcd7e03b911a1b7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:25:58 +0000 Subject: [PATCH 279/403] wifi: mt76: mt7996: validate RX band_idx before dereferencing phys[] band_idx comes from a 2-bit descriptor field (0-3) and was used directly to index dev->mt76.phys[] (size __MT_MAX_BAND == 3) and dereference the result. A corrupt or reserved descriptor value could index out of bounds or hit a NULL phy on parts with fewer bands. Reject invalid band indices, mirroring mt7996_rx_get_wcid(). Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices") Link: https://patch.msgid.link/20260722082610.2699628-6-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index a5a992bdbd15..c93c3a0ab3da 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -441,7 +441,13 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q, memset(status, 0, sizeof(*status)); band_idx = FIELD_GET(MT_RXD1_NORMAL_BAND_IDX, rxd1); + if (!mt7996_band_valid(dev, band_idx)) + return -EINVAL; + mphy = dev->mt76.phys[band_idx]; + if (!mphy) + return -EINVAL; + phy = mphy->priv; status->phy_idx = mphy->band_idx; From 6469ae71e7e5d0132c934972f628f346ad0379cd Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:25:59 +0000 Subject: [PATCH 280/403] wifi: mt76: mt7996: set MT76_MCU_RESET before waking MCU waiters on full reset mt7996_mac_full_reset() called wake_up(&dev->mt76.mcu.wait) without first setting MT76_MCU_RESET. The MCU response wait condition only checks the response queue and that bit, so the wake-up released nobody: a thread blocked in an MCU command against the dead firmware (typically holding dev->mt76.mutex) stayed asleep until its multi-second timeout, stalling recovery. Set the bit before the wake-up, as mt7915 does. Fixes: 27015b6fbcca ("wifi: mt76: mt7996: enable full system reset support") Link: https://patch.msgid.link/20260722082610.2699628-7-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index c93c3a0ab3da..5d0f8ec32b4a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2436,6 +2436,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev) dev->recovery.hw_full_reset = true; + set_bit(MT76_MCU_RESET, &dev->mphy.state); wake_up(&dev->mt76.mcu.wait); ieee80211_stop_queues(hw); From 6486e11a6e2f679597af2d5bb48c3b07a2b2a7ba Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:26:00 +0000 Subject: [PATCH 281/403] wifi: mt76: mt7915: clear wcid mask under mutex after RCU pointer clear mt7915_remove_interface() cleared the wcid mask bit with no lock held and before clearing the RCU wcid pointer. The mask is a non-atomic RMW shared with the allocators, which all run under dev->mt76.mutex; on DBDC the two wiphys share one mt76_dev, so this raced add_interface/sta_add on the other band and could leak or double-hand-out a wcid. Clearing the bit before the RCU pointer also let a concurrent allocation reuse the index and publish its wcid, which the subsequent NULL assignment then wiped. Move the clear into the existing mutex section, after the RCU pointer is cleared. Fixes: f3049b88b2b3 ("wifi: mt76: mt7915: allocate vif wcid in the same range as stations") Link: https://patch.msgid.link/20260722082610.2699628-8-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 044b592efe28..c5eeafef3a90 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -294,7 +294,6 @@ static void mt7915_remove_interface(struct ieee80211_hw *hw, mt7915_mcu_add_bss_info(phy, vif, false); mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_DISCONNECT, false); - mt76_wcid_mask_clear(dev->mt76.wcid_mask, mvif->sta.wcid.idx); mutex_lock(&dev->mt76.mutex); mt76_testmode_reset(phy->mt76, true); @@ -310,6 +309,7 @@ static void mt7915_remove_interface(struct ieee80211_hw *hw, mutex_lock(&dev->mt76.mutex); dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx); phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx); + mt76_wcid_mask_clear(dev->mt76.wcid_mask, mvif->sta.wcid.idx); mutex_unlock(&dev->mt76.mutex); spin_lock_bh(&dev->mt76.sta_poll_lock); From 4a2f4be532e3ea4e2b536e411793a05aaa51af25 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:26:01 +0000 Subject: [PATCH 282/403] wifi: mt76: mt7915: avoid nss underflow in mt7915_mcu_get_sta_nss If a peer's VHT/HE MCS map has no supported spatial stream (all fields 0x3), the loop exits with nss == 0 and the function returned (u8)-1 (255), which was then written into the firmware sta_rec_bf beamforming fields. Clamp the result to 0. Fixes: 89029a85482c ("mt76: mt7915: add Tx beamformer support") Link: https://patch.msgid.link/20260722082610.2699628-9-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index bbb2fedacb25..119bd3582295 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -51,7 +51,7 @@ mt7915_mcu_get_sta_nss(u16 mcs_map) break; } - return nss - 1; + return nss ? nss - 1 : 0; } static void From d4d92ccded678c92c390003926097ddbb6516bc7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:26:02 +0000 Subject: [PATCH 283/403] wifi: mt76: mt7996: don't report a zero TX bitrate mt7996_sta_statistics() set NL80211_STA_INFO_TX_BITRATE unconditionally after the block that already sets it, so a station with no rate info yet was reported to userspace with a valid-but-zero TX rate. Drop the redundant unconditional assignments; the in-block ones are sufficient. Fixes: b34f346b917e ("wifi: mt76: mt7996: drop return in mt7996_sta_statistics") Link: https://patch.msgid.link/20260722082610.2699628-10-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index f291560ae93c..2487b00c3790 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1883,8 +1883,6 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw, sinfo->txrate.flags = txrate->flags; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); } - sinfo->txrate.flags = txrate->flags; - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); sinfo->tx_failed = msta_link->wcid.stats.tx_failed; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); From 236145737480c4c0c515e09061d0d5f77cf52d3f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:26:03 +0000 Subject: [PATCH 284/403] wifi: mt76: mt7915: write RX header translation bit to the correct register MT_MDP_DCR0_RX_HDR_TRANS_EN is a field of MT_MDP_DCR0, but monitor-mode handling applied it to the per-band MT_DMA_DCR0 register instead. As a result RX header translation was never disabled in the MDP when entering monitor mode, and an undocumented bit of MT_DMA_DCR0 was toggled. Target MT_MDP_DCR0, matching the mt7996 driver. Fixes: b2491018587a ("wifi: mt76: mt7915: fix monitor mode issues") Link: https://patch.msgid.link/20260722082610.2699628-11-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index c5eeafef3a90..4ed3d808654f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -493,7 +493,7 @@ static int mt7915_config(struct ieee80211_hw *hw, int radio_idx, mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN, enabled); - mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_MDP_DCR0_RX_HDR_TRANS_EN, + mt76_rmw_field(dev, MT_MDP_DCR0, MT_MDP_DCR0_RX_HDR_TRANS_EN, !dev->monitor_mask); mt76_testmode_reset(phy->mt76, true); mt76_wr(dev, MT_WF_RFCR(band), rxfilter); From 5caa622956166f6c445a384c7e964da4d553a501 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:26:04 +0000 Subject: [PATCH 285/403] wifi: mt76: fix 4th chain ACK RSSI bitmask in sta_poll The per-chain response-frame RSSI values are packed one per byte, but the 4th chain was extracted with GENMASK(31, 14) instead of GENMASK(31, 24). The wrong mask overlaps chains 1-3 and shifts by 14, producing a garbage chain-3 value that corrupts ack_signal/avg_ack_signal on 4x4 radios. Extract the correct byte. Fixes: a71b648e3527 ("wifi: mt76: mt7915: add ack signal support") Fixes: ea5d99d07fbf ("wifi: mt76: mt7996: enable ack signal support") Fixes: 67fc7a304bf5 ("wifi: mt76: mt7921: add ack signal support") Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Link: https://patch.msgid.link/20260722082610.2699628-12-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 9e93201e4186..1154d3c4b811 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -221,7 +221,7 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) rssi[0] = to_rssi(GENMASK(7, 0), val); rssi[1] = to_rssi(GENMASK(15, 8), val); rssi[2] = to_rssi(GENMASK(23, 16), val); - rssi[3] = to_rssi(GENMASK(31, 14), val); + rssi[3] = to_rssi(GENMASK(31, 24), val); msta->ack_signal = mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 17014b1f91e0..e69978184f68 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -156,7 +156,7 @@ static void mt7921_mac_sta_poll(struct mt792x_dev *dev) rssi[0] = to_rssi(GENMASK(7, 0), val); rssi[1] = to_rssi(GENMASK(15, 8), val); rssi[2] = to_rssi(GENMASK(23, 16), val); - rssi[3] = to_rssi(GENMASK(31, 14), val); + rssi[3] = to_rssi(GENMASK(31, 24), val); mlink->ack_signal = mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 2931f5176137..101f571b027f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -152,7 +152,7 @@ static void mt7925_mac_sta_poll(struct mt792x_dev *dev) rssi[0] = to_rssi(GENMASK(7, 0), val); rssi[1] = to_rssi(GENMASK(15, 8), val); rssi[2] = to_rssi(GENMASK(23, 16), val); - rssi[3] = to_rssi(GENMASK(31, 14), val); + rssi[3] = to_rssi(GENMASK(31, 24), val); mlink->ack_signal = mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 5d0f8ec32b4a..5667a89d488a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -166,7 +166,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) rssi[0] = to_rssi(GENMASK(7, 0), val); rssi[1] = to_rssi(GENMASK(15, 8), val); rssi[2] = to_rssi(GENMASK(23, 16), val); - rssi[3] = to_rssi(GENMASK(31, 14), val); + rssi[3] = to_rssi(GENMASK(31, 24), val); mlink = rcu_dereference(msta->vif->mt76.link[wcid->link_id]); if (mlink) { From deaa2e3656937fbbe312f0ee2616c756c6e2511f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:26:05 +0000 Subject: [PATCH 286/403] wifi: mt76: mt7996: fix TX DMA mapping leak for AddBA req frames mt7996/mt7992 hand the firmware a HW MAC-TXP for AddBA req action frames (MT_TXD7_MAC_TXD, set in mt7996_mac_write_txwi_80211()), but are otherwise FW-TXP devices. On tx free mt76_connac_txp_skb_unmap() therefore decodes the per-frame txp as a struct mt76_connac_fw_txp. For a MAC-TXP the fw_txp.nbuf byte aliases the AddBA TID word (MT_TXP1_TID_ADDBA), which is always zero, so the unmap loop runs zero times and the skb DMA mapping in buf[1] is never unmapped. buf[1].skip_unmap is set unconditionally, so the generic DMA-ring cleanup skips it as well. Each AddBA req therefore leaks one TX DMA mapping, roughly one per (re)association. With WED enabled these mappings are bounced through the WED swiotlb pool, so under continuous client reconnect churn the pool is exhausted after ~1-2 days, after which DMA mapping fails for WED, the WiFi MCU and other on-SoC consumers. Keep the deferred (token release) unmap that the design relies on, and add an mt7996-specific txp unmap that inspects MT_TXD7_MAC_TXD and unmaps buf[1] from the MAC-TXP layout for those frames, delegating to mt76_connac_txp_skb_unmap() otherwise. Cc: stable@vger.kernel.org Fixes: cb6ebbdffef2 ("wifi: mt76: mt7996: support writing MAC TXD for AddBA Request") Link: https://patch.msgid.link/20260722082610.2699628-13-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/mac.c | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 5667a89d488a..7024fadce204 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1279,6 +1279,30 @@ mt7996_tx_check_aggr(struct ieee80211_link_sta *link_sta, clear_bit(tid, &wcid->ampdu_state); } +static void +mt7996_txp_skb_unmap(struct mt76_dev *mdev, struct mt76_txwi_cache *t) +{ + u8 *txwi_ptr = mt76_get_txwi_ptr(mdev, t); + __le32 *txwi = (__le32 *)txwi_ptr; + __le32 *txp; + dma_addr_t addr; + u32 val; + + if (!(le32_to_cpu(txwi[7]) & MT_TXD7_MAC_TXD)) { + mt76_connac_txp_skb_unmap(mdev, t); + return; + } + + txp = (__le32 *)(txwi_ptr + MT_TXD_SIZE); + val = le32_to_cpu(txp[3]); + addr = le32_to_cpu(txp[2]); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + addr |= (dma_addr_t)FIELD_GET(MT_TXP3_DMA_ADDR_H, val) << 32; +#endif + dma_unmap_single(mdev->dma_dev, addr, FIELD_GET(MT_TXP_BUF_LEN, val), + DMA_TO_DEVICE); +} + static void mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t, struct ieee80211_link_sta *link_sta, @@ -1288,7 +1312,7 @@ mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t, __le32 *txwi; u16 wcid_idx; - mt76_connac_txp_skb_unmap(mdev, t); + mt7996_txp_skb_unmap(mdev, t); if (!t->skb) goto out; From 422dd2db28ae27c35a586acd9ad482f30000c090 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:26:06 +0000 Subject: [PATCH 287/403] wifi: mt76: fix stranded frames in mt76_txq_schedule_pending A wcid is added to phy->tx_list whenever either tx_pending or tx_offchannel becomes non-empty, but the requeue check after a partial schedule required BOTH queues to be non-empty. When mt76_txq_schedule_pending_wcid() returns -1 (queue stopped or MT76_RESET) it leaves frames in tx_pending while tx_offchannel is empty, so the wcid is dropped from every scheduling list and its frames stall until the next mt76_tx() for that wcid or wcid cleanup. This strands EAPOL/mgmt/nullfunc frames under momentary queue-full or across scan/channel-switch, causing association and 4-way-handshake timeouts. Requeue when either queue still holds frames, matching the enqueue condition. Fixes: 0b3be9d1d34e ("wifi: mt76: add separate tx scheduling queue for off-channel tx") Link: https://patch.msgid.link/20260722082610.2699628-14-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/tx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index de8af32f15e5..dc8407be2891 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -682,8 +682,8 @@ void mt76_txq_schedule_pending(struct mt76_phy *phy) ret = mt76_txq_schedule_pending_wcid(phy, wcid, &wcid->tx_pending); spin_lock(&phy->tx_lock); - if (!skb_queue_empty(&wcid->tx_pending) && - !skb_queue_empty(&wcid->tx_offchannel) && + if ((!skb_queue_empty(&wcid->tx_pending) || + !skb_queue_empty(&wcid->tx_offchannel)) && list_empty(&wcid->tx_list)) list_add_tail(&wcid->tx_list, &phy->tx_list); } From 6650c8532173f5a8d08b013a5e701abe5723f574 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:26:07 +0000 Subject: [PATCH 288/403] wifi: mt76: allow TX aggregation on the VO queue The TX aggregation check skipped TIDs 6 and 7, so all voice-priority traffic was sent without a BA session and therefore unaggregated, limiting throughput for stations that map bulk traffic to VO. The hardware handles aggregation on the VO queue fine, and a peer that prefers unaggregated voice frames can still decline the ADDBA request. Remove the skip from both the connac2 and the mt7996 aggregation setup paths. Link: https://patch.msgid.link/20260722082610.2699628-15-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 2 -- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index fc9f782032ef..0ac3ab42ea40 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -1152,8 +1152,6 @@ void mt76_connac2_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi) return; tid = le32_get_bits(txwi[1], MT_TXD1_TID); - if (tid >= 6) /* skip VO queue */ - return; val = le32_to_cpu(txwi[2]); fc = FIELD_GET(MT_TXD2_FRAME_TYPE, val) << 2 | diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 7024fadce204..c204327eaa37 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1252,8 +1252,6 @@ mt7996_tx_check_aggr(struct ieee80211_link_sta *link_sta, return; tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; - if (tid >= 6) /* skip VO queue */ - return; if (is_8023) { fc = IEEE80211_FTYPE_DATA | From d3ecac68f73b11828e72eaf7952a9beb5caea12b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:26:08 +0000 Subject: [PATCH 289/403] wifi: mt76: fix uninitialised RXDMAD_C descriptor info Unlike other WED-RRO queues, RXDMAD_C frames continue into the skb build path, but mt76_dma_get_buf() skips the desc->info read for RRO queues, so the uninitialised on-stack info was stored into skb->cb and passed to rx_skb(); initialise it to zero. Fixes: e50d4d710efd ("wifi: mt76: Add mt76_dma_get_rxdmad_c_buf utility routione") Link: https://patch.msgid.link/20260722082610.2699628-16-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 3c4abeb5440a..baa2a3525e1e 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -1002,7 +1002,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) while (done < budget) { bool drop = false; - u32 info; + u32 info = 0; if (check_ddone) { if (q->tail == dma_idx) From e1f97c10a4ec2b9db69a134b757304399ca903ce Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:26:09 +0000 Subject: [PATCH 290/403] wifi: mt76: fix RXDMAD_C buffer recycling race The RXDMAD_C buffers come from the RRO data queues' page pools, which are bound to a different NAPI, so the direct page-pool recycle used here could race the owning NAPI; take the non-direct path as is already done for WED RX queues. Fixes: e50d4d710efd ("wifi: mt76: Add mt76_dma_get_rxdmad_c_buf utility routione") Link: https://patch.msgid.link/20260722082610.2699628-17-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index baa2a3525e1e..946b7f90dc0f 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -990,7 +990,8 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) struct sk_buff *skb; unsigned char *data; bool check_ddone = false; - bool allow_direct = !mt76_queue_is_wed_rx(q); + bool allow_direct = !mt76_queue_is_wed_rx(q) && + !mt76_queue_is_wed_rro_rxdmad_c(q); bool more; if ((q->flags & MT_QFLAG_WED_RRO_EN) || From dd59a6126a8f1bd52bf6bd057bf0c8307f76a74b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 22 Jul 2026 08:26:10 +0000 Subject: [PATCH 291/403] wifi: mt76: mt7915: poll the correct SLP CTRL register for the second adie The clock enable path for the second adie sets MT_ADIE_SLP_CTRL_CK0(1) but polled the busy bit of MT_ADIE_SLP_CTRL_CK0(0), so dual-adie bring-up could proceed before the adie1 clock was stable. Fixes: 99ad32a4ca3a ("mt76: mt7915: add support for MT7986") Link: https://patch.msgid.link/20260722082610.2699628-18-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c index 54ff6de96f3e..13fba2a061c7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c @@ -908,7 +908,7 @@ static void mt7986_wmac_clock_enable(struct mt7915_dev *dev, u32 adie_type) read_poll_timeout(mt76_rr, cur, !(cur & MT_SLP_CTRL_BSY_MASK), USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, - dev, MT_ADIE_SLP_CTRL_CK0(0)); + dev, MT_ADIE_SLP_CTRL_CK0(1)); } mt76_wmac_spi_unlock(dev); From 3310e71a74b176d3613dfb42b6bc630d99e90cbb Mon Sep 17 00:00:00 2001 From: Rex Lu Date: Wed, 22 Jul 2026 08:25:54 +0000 Subject: [PATCH 292/403] wifi: mt76: check txfree done event on the WED hw path Check the txfree done event DW1 bit 15 when WED is enabled, to avoid the driver reading a txfree done event before WED has finished reading it. No need to check this flag on WED v2, otherwise SER will occur. The bit position was previously defined as MT_DMA_CTL_BURST, which is unused; rename it to match its function on the txfree ring. Fixes: 83eafc9251d6 ("wifi: mt76: mt7996: add wed tx support") Signed-off-by: Rex Lu Signed-off-by: Shayne Chen Link: https://patch.msgid.link/20260722082610.2699628-2-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 9 +++++++++ drivers/net/wireless/mediatek/mt76/dma.h | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 946b7f90dc0f..bd8ee5260acc 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -608,6 +608,15 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush, q->desc[idx].ctrl |= cpu_to_le32(MT_DMA_CTL_DMA_DONE); else if (!(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE))) return NULL; +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + /* on WED v3 the M_DONE bit signals that WED is done reading + * the txfree descriptor; WED v2 does not set it + */ + else if (dev->mmio.wed.version > 2 && + mt76_queue_is_wed_tx_free(q) && + !(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_M_DONE))) + return NULL; +#endif } done: q->tail = (q->tail + 1) % q->ndesc; diff --git a/drivers/net/wireless/mediatek/mt76/dma.h b/drivers/net/wireless/mediatek/mt76/dma.h index 2a0226c83f3c..a2cf82cfdaaa 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.h +++ b/drivers/net/wireless/mediatek/mt76/dma.h @@ -11,7 +11,7 @@ #define MT_DMA_CTL_SD_LEN1 GENMASK(13, 0) #define MT_DMA_CTL_LAST_SEC1 BIT(14) -#define MT_DMA_CTL_BURST BIT(15) +#define MT_DMA_CTL_M_DONE BIT(15) #define MT_DMA_CTL_SD_LEN0 GENMASK(29, 16) #define MT_DMA_CTL_LAST_SEC0 BIT(30) #define MT_DMA_CTL_DMA_DONE BIT(31) From cbeed7096794f748d16c78cd9d3e0004420d1e9d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:45 +0000 Subject: [PATCH 293/403] wifi: mt76: fix HE DCM max-RU capability encoding sta_rec_he.dcm_rx_max_nss was assigned twice: the second assignment, sourced from HE PHY capability byte 8 (DCM max RU), overwrote the RX-NSS value and left dcm_max_ru at zero. Every associated HE station advertising DCM support was configured in firmware with a wrong dcm_rx_max_nss and a zero dcm_max_ru. Store the DCM max-RU value in dcm_max_ru as intended. The same copy-paste error existed in both the shared connac2 path and the mt7915 path. Fixes: c336318f57a9 ("mt76: mt7915: add HE capabilities support for peers") Fixes: 67aa27431c7f ("mt76: mt7921: rely on mt76_connac_mcu common library") Link: https://patch.msgid.link/20260724124813.3961474-1-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 8f56f568fa0b..2f925d22b9aa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -800,7 +800,7 @@ mt76_connac_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) HE_PHY(CAP3_DCM_MAX_CONST_RX_MASK, elem->phy_cap_info[3]); he->dcm_rx_max_nss = HE_PHY(CAP3_DCM_MAX_RX_NSS_2, elem->phy_cap_info[3]); - he->dcm_rx_max_nss = + he->dcm_max_ru = HE_PHY(CAP8_DCM_MAX_RU_MASK, elem->phy_cap_info[8]); he->pkt_ext = 2; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 119bd3582295..96d2770f7c39 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -902,7 +902,7 @@ mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, HE_PHY(CAP3_DCM_MAX_CONST_RX_MASK, elem->phy_cap_info[3]); he->dcm_rx_max_nss = HE_PHY(CAP3_DCM_MAX_RX_NSS_2, elem->phy_cap_info[3]); - he->dcm_rx_max_nss = + he->dcm_max_ru = HE_PHY(CAP8_DCM_MAX_RU_MASK, elem->phy_cap_info[8]); he->pkt_ext = 2; From 44af52467e72094351a362bf69effd52f1d9c186 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:46 +0000 Subject: [PATCH 294/403] wifi: mt76: mt7996: bound TLV walk in mt7996_mcu_get_chip_config The response TLV loop advanced by tlv->len without a minimum, so a theoretical firmware response containing a zero-length TLV could spin forever, hanging the CPU during device probe. The u32 payload was also read without bounds checking. Reject a short fixed field, stop on a TLV whose length underruns the header or overruns the skb. Fixes: 5d33053be609 ("wifi: mt76: mt7996: add variants support") Link: https://patch.msgid.link/20260724124813.3961474-2-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index a1bae5db8500..f57d4a28cc27 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -4445,21 +4445,31 @@ int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap) return ret; /* fixed field */ + if (skb->len < 4) { + dev_kfree_skb(skb); + return -EINVAL; + } skb_pull(skb, 4); buf = skb->data; - while (buf - skb->data < skb->len) { + while (buf - skb->data + sizeof(struct tlv) <= skb->len) { struct tlv *tlv = (struct tlv *)buf; + u16 tlv_len = le16_to_cpu(tlv->len); + + if (tlv_len < sizeof(*tlv) || + tlv_len > skb->len - (buf - skb->data)) + break; switch (le16_to_cpu(tlv->tag)) { case UNI_EVENT_CHIP_CONFIG_EFUSE_VERSION: - *cap = le32_to_cpu(*(__le32 *)(buf + sizeof(*tlv))); + if (tlv_len >= sizeof(*tlv) + sizeof(__le32)) + *cap = le32_to_cpu(*(__le32 *)(buf + sizeof(*tlv))); break; default: break; } - buf += le16_to_cpu(tlv->len); + buf += tlv_len; } dev_kfree_skb(skb); From 6689b4a65e88d7b019e687fa9d6943ca070f3e43 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:47 +0000 Subject: [PATCH 295/403] wifi: mt76: fix out-of-bounds access in mmio copy helpers mt76_mmio_write_copy() and mt76_mmio_read_copy() iterate up to ALIGN(len, 4), so a length that is not a multiple of four reads past the source buffer (write_copy) or writes past the destination (read_copy). Copy the aligned body in the loop and handle the remaining tail through a 4-byte bounce buffer, keeping the register access width unchanged. Fixes: 2df00805f7db ("wifi: mt76: mmio_*_copy fix byte order and alignment") Link: https://patch.msgid.link/20260724124813.3961474-3-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mmio.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mmio.c b/drivers/net/wireless/mediatek/mt76/mmio.c index 05d74cd7248e..73d47608bf42 100644 --- a/drivers/net/wireless/mediatek/mt76/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mmio.c @@ -35,9 +35,16 @@ static void mt76_mmio_write_copy(struct mt76_dev *dev, u32 offset, { int i; - for (i = 0; i < ALIGN(len, 4); i += 4) + for (i = 0; i + 4 <= len; i += 4) writel(get_unaligned_le32(data + i), dev->mmio.regs + offset + i); + + if (i < len) { + u8 tmp[4] = {}; + + memcpy(tmp, data + i, len - i); + writel(get_unaligned_le32(tmp), dev->mmio.regs + offset + i); + } } static void mt76_mmio_read_copy(struct mt76_dev *dev, u32 offset, @@ -45,9 +52,16 @@ static void mt76_mmio_read_copy(struct mt76_dev *dev, u32 offset, { int i; - for (i = 0; i < ALIGN(len, 4); i += 4) + for (i = 0; i + 4 <= len; i += 4) put_unaligned_le32(readl(dev->mmio.regs + offset + i), data + i); + + if (i < len) { + u8 tmp[4]; + + put_unaligned_le32(readl(dev->mmio.regs + offset + i), tmp); + memcpy(data + i, tmp, len - i); + } } static int mt76_mmio_wr_rp(struct mt76_dev *dev, u32 base, From 2fb6480c52f611338e1b0abe5e6219be1fc9ab75 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:48 +0000 Subject: [PATCH 296/403] wifi: mt76: mt7915: unwind state on add_interface failure When mt76_wcid_alloc() fails, mt7915_add_interface() returned without clearing the vif_mask/omac_mask bits it had already set, without removing the firmware dev info added earlier, and without clearing a monitor_vif pointer to the vif mac80211 is about to free. mac80211 does not call remove_interface() for a failed add, so the indices and firmware dev entry leaked permanently and testmode could dereference the stale monitor_vif. Add a proper error unwind. Fixes: b619e01380ee ("mt76: fix MBSS index condition in DBDC mode") Link: https://patch.msgid.link/20260724124813.3961474-4-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/main.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 4ed3d808654f..d2130226de64 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -249,7 +249,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw, idx = mt76_wcid_alloc(dev->mt76.wcid_mask, mt7915_wtbl_size(dev)); if (idx < 0) { ret = -ENOSPC; - goto out; + goto err; } INIT_LIST_HEAD(&mvif->sta.rc_list); @@ -277,7 +277,17 @@ static int mt7915_add_interface(struct ieee80211_hw *hw, mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_PORT_SECURE, true); rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid); + mutex_unlock(&dev->mt76.mutex); + + return 0; + +err: + dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx); + phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx); + mt7915_mcu_add_dev_info(phy, vif, false); out: + if (phy->monitor_vif == vif) + phy->monitor_vif = NULL; mutex_unlock(&dev->mt76.mutex); return ret; From 6190db312b8230813f529f014b26247c6d9800d0 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:49 +0000 Subject: [PATCH 297/403] wifi: mt76: mt7996: hold dev->mt76.mutex while disabling tx worker in SER mt7996_mac_reset_work() parked the tx worker and disabled the RX/TX NAPIs before taking dev->mt76.mutex. mt76_worker_disable()/_enable() are plain kthread park/unpark, not refcounted, and __mt76_set_channel() toggles the same worker and the MT76_RESET bit under the mutex. An L1 SER racing a channel switch could therefore have the worker unparked and MT76_RESET cleared while the reset path resets the DMA rings, corrupting descriptors or tokens. Take the mutex before disabling the worker, as mt7915 does. Fixes: 27015b6fbcca ("wifi: mt76: mt7996: enable full system reset support") Link: https://patch.msgid.link/20260724124813.3961474-5-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index c204327eaa37..e0a1076ac706 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2573,6 +2573,8 @@ void mt7996_mac_reset_work(struct work_struct *work) cancel_delayed_work_sync(&phy->mt76->mac_work); } + mutex_lock(&dev->mt76.mutex); + mt76_worker_disable(&dev->mt76.tx_worker); mt76_for_each_q_rx(&dev->mt76, i) { if (mtk_wed_device_active(&dev->mt76.mmio.wed) && @@ -2590,8 +2592,6 @@ void mt7996_mac_reset_work(struct work_struct *work) } napi_disable(&dev->mt76.tx_napi); - mutex_lock(&dev->mt76.mutex); - mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED); if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) { From e8b76a6e0d04d13ae55dc746a270f382825c395b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:50 +0000 Subject: [PATCH 298/403] wifi: mt76: decode the full VHT Rx STBC capability field The Rx STBC subfield of the VHT capabilities is a 3-bit cumulative value, but the driver only tested the RXSTBC_1 bit when advertising the peer's Rx STBC support to firmware. A peer reporting Rx STBC of 2, 3 or 4 has that bit clear, so STBC was never used towards it. Test the full IEEE80211_VHT_CAP_RXSTBC_MASK, matching the HT path. Fixes: 046d2e7c50e3 ("mac80211: prepare sta handling for MLO support") Fixes: 2660fde82f65 ("wifi: mt76: mt7996: Update mt7996_mcu_add_rate_ctrl to MLO") Link: https://patch.msgid.link/20260724124813.3961474-6-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 96d2770f7c39..8e0616504f4d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -1625,7 +1625,7 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev, cap |= STA_CAP_VHT_SGI_160; if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) cap |= STA_CAP_VHT_TX_STBC; - if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1) + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) cap |= STA_CAP_VHT_RX_STBC; if (mvif->cap.vht_ldpc && (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index f57d4a28cc27..289c8390b5fa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -2483,7 +2483,7 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, cap |= STA_CAP_VHT_SGI_160; if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) cap |= STA_CAP_VHT_TX_STBC; - if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1) + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) cap |= STA_CAP_VHT_RX_STBC; if ((vif->type != NL80211_IFTYPE_AP || link_conf->vht_ldpc) && (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) From 29fe963d86e434b32d07958a6785be724e45c787 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:51 +0000 Subject: [PATCH 299/403] wifi: mt76: fix ER-SU 106-tone RU check in RX rate decode MT_PHY_TYPE_HE_EXT_SU is an enum value (9), not a bit flag, so the bitwise test "*mode & MT_PHY_TYPE_HE_EXT_SU" also matches OFDM, HT-GF and several HE/EHT modes. Only genuine ER-SU should be classified as a 106-tone RU at 40 MHz; use an equality comparison. Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices") Fixes: d832f5e73815 ("mt76: connac: move mt76_connac2_mac_fill_rx_rate in connac module") Link: https://patch.msgid.link/20260724124813.3961474-7-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index 0ac3ab42ea40..cbf6a987cf90 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -1114,7 +1114,7 @@ int mt76_connac2_mac_fill_rx_rate(struct mt76_dev *dev, case IEEE80211_STA_RX_BW_20: break; case IEEE80211_STA_RX_BW_40: - if (*mode & MT_PHY_TYPE_HE_EXT_SU && + if (*mode == MT_PHY_TYPE_HE_EXT_SU && (idx & MT_PRXV_TX_ER_SU_106T)) { status->bw = RATE_INFO_BW_HE_RU; status->he_ru = diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index e0a1076ac706..866752f2fa24 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -349,7 +349,7 @@ mt7996_mac_fill_rx_rate(struct mt7996_dev *dev, case IEEE80211_STA_RX_BW_20: break; case IEEE80211_STA_RX_BW_40: - if (*mode & MT_PHY_TYPE_HE_EXT_SU && + if (*mode == MT_PHY_TYPE_HE_EXT_SU && (idx & MT_PRXV_TX_ER_SU_106T)) { status->bw = RATE_INFO_BW_HE_RU; status->he_ru = From 50c66bab321140c49aa2ed779a3ec9d2f085b458 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:52 +0000 Subject: [PATCH 300/403] wifi: mt76: mt7996: reserve space for the CSA-abort countdown TLV When a CSA countdown is active, mt7996_mcu_beacon_cntdwn() emits two bss_bcn_cntdwn_tlv entries (the CSA countdown and the CCA-abort BCC), but MT7996_BEACON_UPDATE_SIZE only reserved one. With MBSSID enabled and a near-maximum beacon template the extra 8 bytes could push the offload command past MT7996_MAX_BSS_OFFLOAD_SIZE and trigger skb_over_panic(). Reserve room for both countdown TLVs. Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices") Link: https://patch.msgid.link/20260724124813.3961474-8-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mcu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index 8902e16508b7..c673e986ecb5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -917,7 +917,7 @@ enum { #define MT7996_BEACON_UPDATE_SIZE (sizeof(struct bss_req_hdr) + \ sizeof(struct bss_bcn_content_tlv) + \ 4 + MT_TXD_SIZE + \ - sizeof(struct bss_bcn_cntdwn_tlv) + \ + sizeof(struct bss_bcn_cntdwn_tlv) * 2 + \ sizeof(struct bss_bcn_mbss_tlv)) #define MT7996_MAX_BSS_OFFLOAD_SIZE 2048 #define MT7996_MAX_BEACON_SIZE (MT7996_MAX_BSS_OFFLOAD_SIZE - \ From 151a6cf0d12f5d333b93b634dbe5834ea0b77ce7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:53 +0000 Subject: [PATCH 301/403] wifi: mt76: mt7996: don't leak MLD group index on remap alloc failure mt7996_change_vif_links() sets the mld_idx_mask group bit before allocating the remap index. If the remap allocation fails it jumped to the exit without clearing that bit, permanently consuming one of the 16 MLD group slots. Release the group bit on the error path. Fixes: 4fb3b4e7d1ca ("wifi: mt76: mt7996: fix MLD group index assignment") Link: https://patch.msgid.link/20260724124813.3961474-9-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 2487b00c3790..57afe4e81666 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -2458,6 +2458,7 @@ mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, idx = get_free_idx(dev->mld_remap_idx_mask, 0, 15) - 1; if (idx < 0) { + dev->mld_idx_mask &= ~BIT_ULL(mvif->mld_group_idx); ret = -ENOSPC; goto out; } From e995d3dccc9d980af13a60ad37409ed1561f9eeb Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:54 +0000 Subject: [PATCH 302/403] wifi: mt76: cancel reset and rc work on device unregister Both drivers cancelled dump_work on unregister but left reset_work and rc_work to be flushed only by destroy_workqueue() in mt76_free_device(), which runs after the hw is unregistered and the hardware stopped. A reset_work that fires in that window calls ieee80211_restart_hw() and re-arms mac_work on an unregistered hw, and rc_work touches station state being torn down. Cancel both up front, alongside dump_work. Fixes: e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets") Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices") Link: https://patch.msgid.link/20260724124813.3961474-10-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/init.c | 2 ++ drivers/net/wireless/mediatek/mt76/mt7996/init.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 6568d7b6bc0a..0f6346731308 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -1325,6 +1325,8 @@ int mt7915_register_device(struct mt7915_dev *dev) void mt7915_unregister_device(struct mt7915_dev *dev) { cancel_work_sync(&dev->dump_work); + cancel_work_sync(&dev->reset_work); + cancel_work_sync(&dev->rc_work); mt7915_unregister_ext_phy(dev); mt7915_coredump_unregister(dev); mt7915_unregister_thermal(&dev->phy); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c index 2ff93bd8b6dc..a344829f5a01 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -1806,6 +1806,8 @@ void mt7996_unregister_device(struct mt7996_dev *dev) { cancel_work_sync(&dev->dump_work); cancel_work_sync(&dev->wed_rro.work); + cancel_work_sync(&dev->reset_work); + cancel_work_sync(&dev->rc_work); mt7996_unregister_phy(mt7996_phy3(dev)); mt7996_unregister_phy(mt7996_phy2(dev)); mt7996_unregister_thermal(&dev->phy); From 4238535e85d41abe6fad545bbcdb3d91d191637f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:55 +0000 Subject: [PATCH 303/403] wifi: mt76: report data NSS for STBC frames in RX rate decode The RX rate decoder set status->nss straight from the PRXV NSTS field, which for STBC frames is twice the data spatial-stream count. cfg80211 then reported a doubled RX bitrate in station dumps and radiotap. Halve nss for STBC, matching the TX status path. Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices") Fixes: d832f5e73815 ("mt76: connac: move mt76_connac2_mac_fill_rx_rate in connac module") Link: https://patch.msgid.link/20260724124813.3961474-11-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 4 ++++ drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index cbf6a987cf90..8a8d608dd56e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -1069,6 +1069,10 @@ int mt76_connac2_mac_fill_rx_rate(struct mt76_dev *dev, bw = FIELD_GET(MT_CRXV_FRAME_MODE, v2); } + /* the hardware reports NSTS; report the data NSS for STBC frames */ + if (stbc && nss > 1) + nss >>= 1; + switch (*mode) { case MT_PHY_TYPE_CCK: cck = true; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 866752f2fa24..4449dde33f4e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -294,6 +294,10 @@ mt7996_mac_fill_rx_rate(struct mt7996_dev *dev, dcm = FIELD_GET(MT_PRXV_DCM, v2); bw = FIELD_GET(MT_PRXV_FRAME_MODE, v2); + /* the hardware reports NSTS; report the data NSS for STBC frames */ + if (stbc && nss > 1) + nss >>= 1; + switch (*mode) { case MT_PHY_TYPE_CCK: cck = true; From 04280d0a56be4264720e7b205daaa332c715e5ec Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:56 +0000 Subject: [PATCH 304/403] wifi: mt76: mt7915: use little-endian for bss_info_ra wire fields train_up_high_thres, train_up_rule_rssi and low_traffic_thres were declared as host-native short in a firmware-facing TLV and assigned host-order constants, so on a big-endian host the firmware received byte-swapped rate-adaptation thresholds. Declare them __le16 and convert with cpu_to_le16(). Fixes: e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets") Link: https://patch.msgid.link/20260724124813.3961474-12-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 6 +++--- drivers/net/wireless/mediatek/mt76/mt7915/mcu.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 8e0616504f4d..75eb6d261033 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -576,9 +576,9 @@ mt7915_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, ra->rx_streams = max_nss; ra->algo = 4; ra->train_up_rule = 2; - ra->train_up_high_thres = 110; - ra->train_up_rule_rssi = -70; - ra->low_traffic_thres = 2; + ra->train_up_high_thres = cpu_to_le16(110); + ra->train_up_rule_rssi = cpu_to_le16(-70); + ra->low_traffic_thres = cpu_to_le16(2); ra->phy_cap = cpu_to_le32(0xfdf); ra->interval = cpu_to_le32(500); ra->fast_interval = cpu_to_le32(100); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 22f73a5ed425..7c472062a90e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -318,9 +318,9 @@ struct bss_info_ra { u8 antenna_idx; u8 train_up_rule; u8 rsv[3]; - unsigned short train_up_high_thres; - short train_up_rule_rssi; - unsigned short low_traffic_thres; + __le16 train_up_high_thres; + __le16 train_up_rule_rssi; + __le16 low_traffic_thres; __le16 max_phyrate; __le32 phy_cap; __le32 interval; From dbca5c4d29826cecd3185fb1ae2746205ab55127 Mon Sep 17 00:00:00 2001 From: StanleyYP Wang Date: Fri, 24 Jul 2026 12:47:57 +0000 Subject: [PATCH 305/403] wifi: mt76: mt7996: add missing rdd_idx check when enabling background radar Add the missing rdd idx check (< 0) in mt7996_mcu_rdd_background_enable(). mt7996_get_rdd_idx() returns -1 for phys without 5 GHz support, and the negative index was passed to the RDD MCU command unchecked. Fixes: 1529e335f93d ("wifi: mt76: mt7996: rework radar HWRDD idx") Signed-off-by: StanleyYP Wang Link: https://patch.msgid.link/20260724124813.3961474-13-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 289c8390b5fa..645a8b480871 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -4023,6 +4023,9 @@ int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, struct mt7996_dev *dev = phy->dev; int err, region, rdd_idx = mt7996_get_rdd_idx(phy, true); + if (rdd_idx < 0) + return -EINVAL; + if (!chandef) { /* disable offchain */ err = mt7996_mcu_rdd_cmd(dev, RDD_STOP, rdd_idx, 0); if (err) From 1df54335590bb025c3bd706a9ba9c6e73a1d3000 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:47:58 +0000 Subject: [PATCH 306/403] wifi: mt76: only consume the WO drop bit on WED v2 devices The RX path is handled by the WO MCU only on WED v2 hardware. On WED v3 the same buf1 bit does not carry drop information, so evaluating it there causes spurious RX drops. Fixes: e4d2b8bcac11 ("wifi: mt76: drop the incorrect scatter and gather frame") Link: https://patch.msgid.link/20260724124813.3961474-14-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index bd8ee5260acc..f695a1459606 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -547,8 +547,13 @@ mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx, t->ptr = NULL; mt76_put_rxwi(dev, t); - if (drop) +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + /* the WO MCU owns the RX path only on WED v2, on newer + * versions this buf1 bit carries no drop information + */ + if (drop && dev->mmio.wed.version == 2) *drop |= !!(buf1 & MT_DMA_CTL_WO_DROP); +#endif } else { dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0], SKB_WITH_OVERHEAD(q->buf_size), From 043e042666126064a833e45e31ef50ede6f7d9cf Mon Sep 17 00:00:00 2001 From: Peter Chiu Date: Fri, 24 Jul 2026 12:47:59 +0000 Subject: [PATCH 307/403] wifi: mt76: mt7996: add mcu command to set bssid mapping address When receiving a 4 address non-AMSDU packet, there is no bssid in the address fields, which breaks powersave handling for 4-address peers. Set the mcu command to use A1 as bssid when receiving 4 address non-AMSDU packets on mt7992 and mt7990. Also skip mt7996_mac_init_band() for invalid bands, so the command is only sent for bands that actually exist on the device. Signed-off-by: Peter Chiu Link: https://patch.msgid.link/20260724124813.3961474-15-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/init.c | 6 +++++ .../net/wireless/mediatek/mt76/mt7996/mcu.c | 26 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7996/mcu.h | 1 + .../wireless/mediatek/mt76/mt7996/mt7996.h | 1 + 4 files changed, 34 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c index a344829f5a01..ee1f7a9f4851 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -562,6 +562,9 @@ mt7996_mac_init_band(struct mt7996_dev *dev, u8 band) { u32 mask, set; + if (!mt7996_band_valid(dev, band)) + return; + /* clear estimated value of EIFS for Rx duration & OBSS time */ mt76_wr(dev, MT_WF_RMAC_RSVD0(band), MT_WF_RMAC_RSVD0_EIFS_CLR); @@ -593,6 +596,9 @@ mt7996_mac_init_band(struct mt7996_dev *dev, u8 band) * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set. */ mt76_set(dev, MT_AGG_ACR4(band), MT_AGG_ACR_PPDU_TXS2H); + + if (!is_mt7996(&dev->mt76)) + mt7996_mcu_set_bssid_mapping_addr(&dev->mt76, band); } static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 645a8b480871..6a775e682f74 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -5049,6 +5049,32 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, &req, sizeof(req), true); } +int mt7996_mcu_set_bssid_mapping_addr(struct mt76_dev *dev, u8 band_idx) +{ + enum { + BSSID_MAPPING_ADDR1, + BSSID_MAPPING_ADDR2, + BSSID_MAPPING_ADDR3, + }; + struct { + u8 band_idx; + u8 _rsv1[3]; + + __le16 tag; + __le16 len; + u8 addr; + u8 _rsv2[3]; + } __packed req = { + .band_idx = band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_BSSID_MAPPING_ADDR), + .len = cpu_to_le16(sizeof(req) - 4), + .addr = BSSID_MAPPING_ADDR1, + }; + + return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); +} + int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val) { struct { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index c673e986ecb5..b53a9e71c281 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -927,6 +927,7 @@ enum { UNI_BAND_CONFIG_RADIO_ENABLE, UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08, UNI_BAND_CONFIG_MAC_ENABLE_CTRL = 0x0c, + UNI_BAND_CONFIG_BSSID_MAPPING_ADDR = 0x12, }; enum { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 60397a1b1a2b..dcacc7d06e85 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -772,6 +772,7 @@ int mt7996_mcu_set_protection(struct mt7996_phy *phy, struct mt7996_vif_link *li u8 ht_mode, bool use_cts_prot); int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf); +int mt7996_mcu_set_bssid_mapping_addr(struct mt76_dev *dev, u8 band_idx); int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch); int mt7996_mcu_get_temperature(struct mt7996_phy *phy); int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state); From 8ab38bbac02298e3fb03008ed4a0227485c0fd62 Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Wed, 29 Jul 2026 10:00:05 +0800 Subject: [PATCH 308/403] wifi: ath11k: fix resource leak on error in ext IRQ setup In ath11k_ahb_config_irq(), when a CE request_irq() fails, the function returns the error immediately without freeing the CE IRQs that were successfully registered in previous loop iterations. The probe error path does not call ath11k_ahb_free_irq() either, so the previously registered CE IRQ handlers remain attached to the interrupt lines and are never released. In ath11k_ahb_config_ext_irq(), when an external request_irq() fails, the error is only logged and the loop continues. The function then returns 0 indicating success, leaving the device in a partially configured state where some external IRQs are not registered. This causes enable_irq()/disable_irq()/free_irq() to be called on unregistered IRQs during runtime and remove/shutdown, triggering WARN_ON(!desc->action), and missing interrupt handlers lead to data loss. Additionally, if alloc_netdev_dummy() fails for a later IRQ group, the function returns -ENOMEM without freeing the ext IRQs and napi_ndev that were successfully set up for earlier groups. Fix all three issues: propagate the error up to the caller and unwind all successfully registered IRQs and allocated resources on failure. Also move ab->irq_num[irq_idx] assignment after request_irq() succeeds in the ext IRQ path to match the CE IRQ path and avoid storing a stale IRQ number on failure. Signed-off-by: ZhaoJinming Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260729020005.219253-1-zhaojinming@uniontech.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/ahb.c | 79 +++++++++++++++++++-------- 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 1e1dea485760..ec5bf5c9fd79 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -431,36 +431,44 @@ static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab) ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id; } -static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) +static void ath11k_ahb_free_ext_irq_grp(struct ath11k_base *ab, + struct ath11k_ext_irq_grp *irq_grp) { - int i, j; + int j; - for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { - struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + for (j = 0; j < irq_grp->num_irq; j++) + free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); - for (j = 0; j < irq_grp->num_irq; j++) - free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); - - netif_napi_del(&irq_grp->napi); - free_netdev(irq_grp->napi_ndev); - } + netif_napi_del(&irq_grp->napi); + free_netdev(irq_grp->napi_ndev); } -static void ath11k_ahb_free_irq(struct ath11k_base *ab) +static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) { - int irq_idx; int i; - if (ab->hw_params.hybrid_bus_type) - return ath11k_pcic_free_irq(ab); + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) + ath11k_ahb_free_ext_irq_grp(ab, &ab->ext_irq_grp[i]); +} - for (i = 0; i < ab->hw_params.ce_count; i++) { +static void ath11k_ahb_free_ce_irqs(struct ath11k_base *ab, int max_idx) +{ + int irq_idx, i; + + for (i = 0; i < max_idx; i++) { if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) continue; irq_idx = ATH11K_IRQ_CE0_OFFSET + i; free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); } +} +static void ath11k_ahb_free_irq(struct ath11k_base *ab) +{ + if (ab->hw_params.hybrid_bus_type) + return ath11k_pcic_free_irq(ab); + + ath11k_ahb_free_ce_irqs(ab, ab->hw_params.ce_count); ath11k_ahb_free_ext_irq(ab); } @@ -524,20 +532,25 @@ static irqreturn_t ath11k_ahb_ext_interrupt_handler(int irq, void *arg) static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab) { struct ath11k_hw_params *hw = &ab->hw_params; + struct ath11k_ext_irq_grp *irq_grp; int i, j; int irq; int ret; for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { - struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; u32 num_irq = 0; + irq_grp = &ab->ext_irq_grp[i]; + irq_grp->ab = ab; irq_grp->grp_id = i; irq_grp->napi_ndev = alloc_netdev_dummy(0); - if (!irq_grp->napi_ndev) - return -ENOMEM; + if (!irq_grp->napi_ndev) { + ret = -ENOMEM; + irq_grp->num_irq = 0; + goto err_request_irq; + } netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, ath11k_ahb_ext_grp_napi_poll); @@ -585,14 +598,11 @@ static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab) } } } - irq_grp->num_irq = num_irq; - - for (j = 0; j < irq_grp->num_irq; j++) { + for (j = 0; j < num_irq; j++) { int irq_idx = irq_grp->irqs[j]; irq = platform_get_irq_byname(ab->pdev, irq_name[irq_idx]); - ab->irq_num[irq_idx] = irq; irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_DISABLE_UNLAZY); ret = request_irq(irq, ath11k_ahb_ext_interrupt_handler, IRQF_TRIGGER_RISING, @@ -600,11 +610,24 @@ static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab) if (ret) { ath11k_err(ab, "failed request_irq for %d\n", irq); + irq_grp->num_irq = j; + ath11k_ahb_free_ext_irq_grp(ab, irq_grp); + goto err_request_irq; } + ab->irq_num[irq_idx] = irq; } + + irq_grp->num_irq = num_irq; } return 0; + +err_request_irq: + for (i--; i >= 0; i--) { + irq_grp = &ab->ext_irq_grp[i]; + ath11k_ahb_free_ext_irq_grp(ab, irq_grp); + } + return ret; } static int ath11k_ahb_config_irq(struct ath11k_base *ab) @@ -629,16 +652,24 @@ static int ath11k_ahb_config_irq(struct ath11k_base *ab) ret = request_irq(irq, ath11k_ahb_ce_interrupt_handler, IRQF_TRIGGER_RISING, irq_name[irq_idx], ce_pipe); - if (ret) + if (ret) { + ath11k_err(ab, "failed request_irq for %d\n", irq); + ath11k_ahb_free_ce_irqs(ab, i); return ret; + } ab->irq_num[irq_idx] = irq; } /* Configure external interrupts */ ret = ath11k_ahb_config_ext_irq(ab); + if (ret) { + ath11k_err(ab, "failed to configure ext irq: %d\n", ret); + ath11k_ahb_free_ce_irqs(ab, ab->hw_params.ce_count); + return ret; + } - return ret; + return 0; } static int ath11k_ahb_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, From 0293be2212d319d59589082461abf2a9b626cd1c Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 27 Jul 2026 16:39:41 -0700 Subject: [PATCH 309/403] wifi: ath11k: fix leak in ath11k_service_ready_ext_event() Currently, during ath11k_service_ready_ext_event() processing, svc_rdy_ext.mac_phy_caps can be allocated during TLV parsing. This is a temporary allocation that is freed on the success path, but not on the error path. If parsing succeeds far enough to allocate mac_phy_caps and then fails on a later TLV, the allocation leaks. So free the allocation on the error path. Compile tested only. Fixes: 5b90fc760db5 ("ath11k: fix wmi service ready ext tlv parsing") Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260727-ath11k_service_ready_ext_event-memleak-v1-1-e8373d27bdd1@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/wmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 66547e9ee16c..bbca275a8289 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -5135,6 +5135,7 @@ static int ath11k_service_ready_ext_event(struct ath11k_base *ab, return 0; err: + kfree(svc_rdy_ext.mac_phy_caps); ath11k_wmi_free_dbring_caps(ab); return ret; } From 3bbd05723d15dd06f0560bcd94fbf9a91b5f5613 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Mon, 13 Jul 2026 23:32:51 +0200 Subject: [PATCH 310/403] wifi: ath6kl: clamp assoc request/response lengths before subtracting IE offsets ath6kl_cfg80211_connect_event() subtracts fixed IE offsets from assoc_req_len (-= 4) and assoc_resp_len (-= 6), both u8, with no lower bound. The aggregate check recently added to ath6kl_wmi_connect_event_rx() bounds the declared lengths from above (their sum must fit the received event), but an assoc request/response shorter than its fixed offset still underflows here: the u8 wraps to ~250, and cfg80211_connect_result() / cfg80211_roamed() then treat that wrapped value as the IE length and copy that many bytes out of the small assoc_info buffer to user space via nl80211, disclosing adjacent slab memory. Clamp both lengths to their offsets before subtracting. Found by 0sec (https://0sec.ai) using automated source analysis; the missing lower bound is evident from source. Compile-tested. Fixes: bdcd81707973 ("Add ath6kl cleaned up driver") Cc: stable@vger.kernel.org Assisted-by: 0sec:claude-opus-4-8 Signed-off-by: Doruk Tan Ozturk Link: https://patch.msgid.link/20260713213251.21161-1-doruk@0sec.ai Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index ecde91159b54..59cf1d0e7f19 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -754,6 +754,11 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len + assoc_resp_ie_offset; + if (assoc_req_len < assoc_req_ie_offset) + assoc_req_len = assoc_req_ie_offset; + if (assoc_resp_len < assoc_resp_ie_offset) + assoc_resp_len = assoc_resp_ie_offset; + assoc_req_len -= assoc_req_ie_offset; assoc_resp_len -= assoc_resp_ie_offset; From 490d81fd0cc1376ceb660ed2af602ad1bfe1ac77 Mon Sep 17 00:00:00 2001 From: Peter Chiu Date: Fri, 24 Jul 2026 12:48:00 +0000 Subject: [PATCH 311/403] wifi: mt76: mt7996: leave PS when a 4-address connection is established Because 4 address non-AMSDU packets do not have a bssid field, the hardware cannot get the bssid. Without the bssid, stations are not able to leave PS mode due to HW design. Wake up non-setup links when 4-address mode is established to prevent this issue. mt7992 and mt7990 handle this via the BSSID mapping band config instead, so restrict the command to mt7996. Signed-off-by: Peter Chiu Link: https://patch.msgid.link/20260724124813.3961474-16-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 + .../net/wireless/mediatek/mt76/mt7996/main.c | 8 ++++++++ .../net/wireless/mediatek/mt76/mt7996/mcu.c | 18 ++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7996/mcu.h | 6 ++++++ .../net/wireless/mediatek/mt76/mt7996/mt7996.h | 2 ++ 5 files changed, 35 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 8198efc6c05d..51380849d24e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -867,6 +867,7 @@ enum { STA_REC_HDRT = 0x28, STA_REC_EML_OP = 0x29, STA_REC_HDR_TRANS = 0x2B, + STA_REC_PS_LEAVE = 0x45, STA_REC_MAX_NUM }; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 57afe4e81666..f92bfe5d6197 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -2027,6 +2027,14 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw, continue; mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link); + + /* HW cannot derive the BSSID from 4-address non-AMSDU frames, + * so PS exit is missed on links other than the setup link. + * Let the firmware wake those links instead. + */ + if (enabled && msta->deflink_id != link_id && + is_mt7996(&dev->mt76)) + mt7996_mcu_ps_leave(dev, link, msta_link); } mutex_unlock(&dev->mt76.mutex); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 6a775e682f74..24ce87d928e8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -5191,6 +5191,24 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } +int mt7996_mcu_ps_leave(struct mt7996_dev *dev, struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link) +{ + struct sk_buff *skb; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt76_connac_mcu_add_tlv(skb, STA_REC_PS_LEAVE, + sizeof(struct sta_rec_ps_leave)); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx, u16 rate_idx, bool beacon) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index b53a9e71c281..1487d33a8dbb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -680,6 +680,12 @@ struct sta_rec_hdr_trans { u8 mesh; } __packed; +struct sta_rec_ps_leave { + __le16 tag; + __le16 len; + u8 __rsv[4]; +} __packed; + struct sta_rec_mld_setup { __le16 tag; __le16 len; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index dcacc7d06e85..2a0cdb56f822 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -928,6 +928,8 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link); +int mt7996_mcu_ps_leave(struct mt7996_dev *dev, struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link); int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode); int mt7996_mcu_set_emlsr_mode(struct mt7996_dev *dev, struct ieee80211_vif *vif, From 16a04441eab0dcd4d7126a6f66b370adbf28f96d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:48:01 +0000 Subject: [PATCH 312/403] wifi: mt76: mt7915: unlink TWT flow if the MCU rejects the agreement The flow is added to dev->twt_list before sending the agreement to the firmware, but the error path leaves it linked while flowid_mask is never set. The flow slot can then be reused and memset while still on the list, corrupting twt_list, and station removal leaves a dangling entry behind that mt7915_mac_twt_sched_list_add() later walks. Fixes: 3782b69d03e7 ("mt76: mt7915: introduce mt7915_mac_add_twt_setup routine") Link: https://patch.msgid.link/20260724124813.3961474-17-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 1154d3c4b811..ebe1376ffc2c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -2350,8 +2350,10 @@ void mt7915_mac_add_twt_setup(struct ieee80211_hw *hw, } flow->tsf = le64_to_cpu(twt_agrt->twt); - if (mt7915_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD)) + if (mt7915_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD)) { + list_del(&flow->list); goto unlock; + } setup_cmd = TWT_SETUP_CMD_ACCEPT; dev->twt.table_mask |= BIT(table_id); From ccb4bda277999959bc852480d8684b13b926e657 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:48:02 +0000 Subject: [PATCH 313/403] wifi: mt76: mt7996: skip key upload when adding an offchannel link No hw keys are ever uploaded for scanning/roc links and the link remove path already skips the key iteration for them. The add path still runs it, and since mt7996_set_hw_key() resolves the target through mvif->link[link_id] rather than the offchannel link, starting a scan on another band re-uploads the group keys of the link sharing the same link_id, re-sending its BSS cipher info and, for BIGTK with beacon protection on an AP link, toggling its beacons off and on. Skip the key iteration for offchannel links, mirroring the remove path. Fixes: 69d54ce7491d ("wifi: mt76: mt7996: switch to single multi-radio wiphy") Link: https://patch.msgid.link/20260724124813.3961474-18-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index f92bfe5d6197..3d30b9702a8f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -372,7 +372,8 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, CONN_STATE_PORT_SECURE, true); rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid); - ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, &it); + if (!mlink->wcid->offchannel) + ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, &it); if (!mlink->wcid->offchannel) { if (vif->txq && From 6f8d8c458010b597bf4114e6fb3162bff7050041 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:48:03 +0000 Subject: [PATCH 314/403] wifi: mt76: mt7996: wake MCU waiters before aborting scan in L1 SER The L1 reset path calls mt76_abort_scan() between setting MT76_MCU_RESET and waking mcu.wait. A scan work blocked on an in-flight MCU command does not re-evaluate its wait condition until woken, so the cancel_delayed_work_sync() inside the abort sleeps out the full MCU timeout before recovery can proceed, adding several seconds of SER latency. mt7996_mac_full_reset() and the mt7915 counterpart already order the wake-up first. Wake mcu.wait immediately after setting MT76_MCU_RESET so in-flight commands bail out before the abort synchronises against them. Fixes: b36d55610215 ("wifi: mt76: abort scan/roc on hw restart") Link: https://patch.msgid.link/20260724124813.3961474-19-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 4449dde33f4e..96589aea1548 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2567,8 +2567,8 @@ void mt7996_mac_reset_work(struct work_struct *work) set_bit(MT76_RESET, &dev->mphy.state); set_bit(MT76_MCU_RESET, &dev->mphy.state); - mt76_abort_scan(&dev->mt76); wake_up(&dev->mt76.mcu.wait); + mt76_abort_scan(&dev->mt76); cancel_work_sync(&dev->wed_rro.work); mt7996_for_each_phy(dev, phy) { From 1fdf2b5958ee0ae9c6f1006338fb8f46d44478fc Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:48:04 +0000 Subject: [PATCH 315/403] wifi: mt76: fix queue reg access when building WED and NPU together Move the offload specific parts of Q_READ/Q_WRITE into mt76_dma_handle_read/write, which return false to fall back to readl/writel. The WED and NPU #ifdefs now live inside those helpers instead of selecting between mutually exclusive macro definitions, so a kernel with both enabled supports both at runtime. Also fixes the NPU path dereferencing a hardcoded q instead of the macro argument. Link: https://patch.msgid.link/20260724124813.3961474-20-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.h | 110 ++++++++++++----------- 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.h b/drivers/net/wireless/mediatek/mt76/dma.h index a2cf82cfdaaa..7694764febe7 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.h +++ b/drivers/net/wireless/mediatek/mt76/dma.h @@ -5,6 +5,8 @@ #ifndef __MT76_DMA_H #define __MT76_DMA_H +#include + #define DMA_DUMMY_DATA ((void *)~0) #define MT_RING_SIZE 0x10 @@ -46,73 +48,75 @@ #define MT_FCE_INFO_LEN 4 #define MT_RX_RXWI_LEN 32 +static inline bool +mt76_dma_handle_read(struct mt76_queue *q, u32 offset, u32 *val) +{ #if IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) + if (q->flags & MT_QFLAG_WED) { + *val = mtk_wed_device_reg_read(q->wed, q->wed_regs + offset); + + return true; + } +#endif +#if IS_ENABLED(CONFIG_MT76_NPU) + if (q->flags & MT_QFLAG_NPU) { + struct airoha_npu *npu; + + *val = 0; + rcu_read_lock(); + npu = rcu_dereference(q->dev->mmio.npu); + if (npu) + regmap_read(npu->regmap, q->wed_regs + offset, val); + rcu_read_unlock(); + + return true; + } +#endif + + return false; +} + +static inline bool +mt76_dma_handle_write(struct mt76_queue *q, u32 offset, u32 val) +{ +#if IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) + if (q->flags & MT_QFLAG_WED) { + mtk_wed_device_reg_write(q->wed, q->wed_regs + offset, val); + + return true; + } +#endif +#if IS_ENABLED(CONFIG_MT76_NPU) + if (q->flags & MT_QFLAG_NPU) { + struct airoha_npu *npu; + + rcu_read_lock(); + npu = rcu_dereference(q->dev->mmio.npu); + if (npu) + regmap_write(npu->regmap, q->wed_regs + offset, val); + rcu_read_unlock(); + + return true; + } +#endif + + return false; +} #define Q_READ(_q, _field) ({ \ u32 _offset = offsetof(struct mt76_queue_regs, _field); \ u32 _val; \ - if ((_q)->flags & MT_QFLAG_WED) \ - _val = mtk_wed_device_reg_read((_q)->wed, \ - ((_q)->wed_regs + \ - _offset)); \ - else \ + if (!mt76_dma_handle_read(_q, _offset, &_val)) \ _val = readl(&(_q)->regs->_field); \ _val; \ }) #define Q_WRITE(_q, _field, _val) do { \ u32 _offset = offsetof(struct mt76_queue_regs, _field); \ - if ((_q)->flags & MT_QFLAG_WED) \ - mtk_wed_device_reg_write((_q)->wed, \ - ((_q)->wed_regs + _offset), \ - _val); \ - else \ + if (!mt76_dma_handle_write(_q, _offset, _val)) \ writel(_val, &(_q)->regs->_field); \ } while (0) -#elif IS_ENABLED(CONFIG_MT76_NPU) - -#define Q_READ(_q, _field) ({ \ - u32 _offset = offsetof(struct mt76_queue_regs, _field); \ - u32 _val = 0; \ - if ((_q)->flags & MT_QFLAG_NPU) { \ - struct airoha_npu *npu; \ - \ - rcu_read_lock(); \ - npu = rcu_dereference(q->dev->mmio.npu); \ - if (npu) \ - regmap_read(npu->regmap, \ - ((_q)->wed_regs + _offset), &_val); \ - rcu_read_unlock(); \ - } else { \ - _val = readl(&(_q)->regs->_field); \ - } \ - _val; \ -}) - -#define Q_WRITE(_q, _field, _val) do { \ - u32 _offset = offsetof(struct mt76_queue_regs, _field); \ - if ((_q)->flags & MT_QFLAG_NPU) { \ - struct airoha_npu *npu; \ - \ - rcu_read_lock(); \ - npu = rcu_dereference(q->dev->mmio.npu); \ - if (npu) \ - regmap_write(npu->regmap, \ - ((_q)->wed_regs + _offset), _val); \ - rcu_read_unlock(); \ - } else { \ - writel(_val, &(_q)->regs->_field); \ - } \ -} while (0) - -#else - -#define Q_READ(_q, _field) readl(&(_q)->regs->_field) -#define Q_WRITE(_q, _field, _val) writel(_val, &(_q)->regs->_field) - -#endif - struct mt76_desc { __le32 buf0; __le32 ctrl; From 54caf306b28e48054ee9edef711ac4ab689c967d Mon Sep 17 00:00:00 2001 From: Chad Monroe Date: Fri, 24 Jul 2026 12:48:05 +0000 Subject: [PATCH 316/403] wifi: mt76: mt7996: select net_setup_tc handler at runtime The net_setup_tc callback is chosen at compile time and the WED handler shadows the NPU one when CONFIG_NET_MEDIATEK_SOC_WED is enabled. mt76_wed_net_setup_tc() returns -EOPNOTSUPP without an active WED device, so on boards using the Airoha NPU with a WED enabled kernel the tc offload block is never bound and PPE flow offload for wireless traffic is silently disabled. Dispatch on the active offload backend instead: use the WED handler when a WED device is attached and fall back to the NPU handler otherwise. Builds without MT76_NPU keep the old behavior through the mt76_npu_net_setup_tc() stub. Signed-off-by: Chad Monroe Link: https://patch.msgid.link/20260724124813.3961474-21-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/main.c | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 3d30b9702a8f..21a928431c25 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -2443,6 +2443,23 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, return 0; } +#if defined(CONFIG_NET_MEDIATEK_SOC_WED) || defined(CONFIG_MT7996_NPU) +static int +mt7996_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct net_device *netdev, enum tc_setup_type type, + void *type_data) +{ +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) + return mt76_wed_net_setup_tc(hw, vif, netdev, type, + type_data); +#endif + return mt76_npu_net_setup_tc(hw, vif, netdev, type, type_data); +} +#endif + static int mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 old_links, u16 new_links, @@ -2570,10 +2587,8 @@ const struct ieee80211_ops mt7996_ops = { #endif .set_radar_background = mt7996_set_radar_background, .net_fill_forward_path = mt7996_net_fill_forward_path, -#ifdef CONFIG_NET_MEDIATEK_SOC_WED - .net_setup_tc = mt76_wed_net_setup_tc, -#elif defined(CONFIG_MT7996_NPU) - .net_setup_tc = mt76_npu_net_setup_tc, +#if defined(CONFIG_NET_MEDIATEK_SOC_WED) || defined(CONFIG_MT7996_NPU) + .net_setup_tc = mt7996_net_setup_tc, #endif .change_vif_links = mt7996_change_vif_links, .change_sta_links = mt7996_mac_sta_change_links, From e11fddd3471d6d277495412d0774fa6f3e71c164 Mon Sep 17 00:00:00 2001 From: Chad Monroe Date: Fri, 24 Jul 2026 12:48:06 +0000 Subject: [PATCH 317/403] wifi: mt76: fix stuck TX queues after SER with no active interfaces When a firmware watchdog triggers full SER recovery before any VAPs are up, mac80211 skips drv_reconfig_complete() because open_count is zero. This leaves the DRIVER queue stop reason set permanently, blocking all TX when interfaces eventually start. Signed-off-by: Chad Monroe Link: https://patch.msgid.link/20260724124813.3961474-22-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 4 ++++ drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index ebe1376ffc2c..350d4573ced2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1469,6 +1469,10 @@ mt7915_mac_full_reset(struct mt7915_dev *dev) mutex_unlock(&dev->mt76.mutex); + ieee80211_wake_queues(mt76_hw(dev)); + if (ext_phy) + ieee80211_wake_queues(ext_phy->hw); + ieee80211_restart_hw(mt76_hw(dev)); if (ext_phy) ieee80211_restart_hw(ext_phy->hw); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 96589aea1548..544da01b0411 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2512,6 +2512,8 @@ mt7996_mac_full_reset(struct mt7996_dev *dev) mutex_unlock(&dev->mt76.mutex); + ieee80211_wake_queues(mt76_hw(dev)); + ieee80211_restart_hw(mt76_hw(dev)); } From 98aff74779da9f417386c589c36848e9c568f0f9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:48:07 +0000 Subject: [PATCH 318/403] wifi: mt76: mt7615: fix NULL deref in mt7615_mac_set_rates mt7615_tx() falls back to the vif BSS wcid when mac80211 hands a frame over without a station, e.g. while a station is being torn down, and to the global wcid when there is no vif either. Both tx_prepare_skb implementations derive a mt7615_sta from that wcid unconditionally and, if the rate control probe flag is set, pass it to mt7615_mac_set_rates(), which dereferences the NULL vif backpointer of the per-vif embedded sta: Unable to handle kernel read from unreadable memory at virtual address 0000000000000002 ... pc : mt7615_mac_set_rates lr : mt7615_tx_prepare_skb Neither entry is rate controlled by mac80211: the embedded sta has an empty rate set, and for the global wcid the container_of does not yield a valid mt7615_sta at all. Only resolve the sta for wcids that belong to a station. Reported-by: Chad Monroe Link: https://patch.msgid.link/20260724124813.3961474-23-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c | 5 +++-- drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c index 53cb1eed1e4f..0aca590f2bae 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c @@ -68,10 +68,11 @@ int mt7615_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, int pid, id; u8 *txwi = (u8 *)txwi_ptr; struct mt76_txwi_cache *t; - struct mt7615_sta *msta; + struct mt7615_sta *msta = NULL; void *txp; - msta = wcid ? container_of(wcid, struct mt7615_sta, wcid) : NULL; + if (wcid && wcid->sta) + msta = container_of(wcid, struct mt7615_sta, wcid); if (!wcid) wcid = &dev->mt76.global_wcid; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c index f4169de939c4..9525d8062b44 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c @@ -187,10 +187,11 @@ int mt7663_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, struct sk_buff *skb = tx_info->skb; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_key_conf *key = info->control.hw_key; - struct mt7615_sta *msta; + struct mt7615_sta *msta = NULL; int pad, err, pktid; - msta = wcid ? container_of(wcid, struct mt7615_sta, wcid) : NULL; + if (wcid && wcid->sta) + msta = container_of(wcid, struct mt7615_sta, wcid); if (!wcid) wcid = &dev->mt76.global_wcid; From 496ea8ceb2113d0fdeb1b37437d3a4d282162b2f Mon Sep 17 00:00:00 2001 From: Chad Monroe Date: Fri, 24 Jul 2026 12:48:08 +0000 Subject: [PATCH 319/403] wifi: mt76: mt7915: trigger L1 SER on PLE MDP RIOC hang WM firmware can enter a partial failure state where RX is hung. Detect this condition by monitoring SER_PLE_ERR_1 for MDP_RIOC_HANG_ERR and trigger L1 SER to restore operation. Use transition detection on the error bit to fire only once per new occurrence, preventing an infinite SER loop when the bit remains set across checks. Signed-off-by: Chad Monroe Suggested-by: Ryder Lee Link: https://patch.msgid.link/20260724124813.3961474-24-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 13 ++++++++++++- drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h | 1 + drivers/net/wireless/mediatek/mt76/mt7915/regs.h | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 350d4573ced2..df6359c88b18 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1934,7 +1934,7 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) static void mt7915_mac_severe_check(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; - u32 trb; + u32 trb, ple_err; if (!phy->omac_mask) return; @@ -1954,6 +1954,17 @@ static void mt7915_mac_severe_check(struct mt7915_phy *phy) phy->mt76->band_idx); phy->trb_ts = trb; + + ple_err = mt76_rr(dev, MT_SWDEF_PLE1_STATS); + if ((ple_err & MT_SWDEF_PLE1_MDP_RIOC_HANG_ERR) && + !(dev->ple1_sts & MT_SWDEF_PLE1_MDP_RIOC_HANG_ERR)) { + dev_warn(dev->mt76.dev, + "band%d: PLE error 0x%x detected, triggering L1 SER\n", + phy->mt76->band_idx, ple_err); + mt7915_mcu_set_ser(dev, SER_RECOVER, SER_SET_RECOVER_L1, + phy->mt76->band_idx); + } + dev->ple1_sts = ple_err; } void mt7915_mac_sta_rc_work(struct work_struct *work) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 92e0f9f0169c..0f7e871dc7a1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -296,6 +296,7 @@ struct mt7915_dev { spinlock_t reg_lock; u32 hw_pattern; + u32 ple1_sts; bool dbdc_support; bool flash_mode; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 307bf6a75674..dc7bb9a1f223 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -1044,6 +1044,7 @@ enum offs_rev { #define MT_SWDEF_SER_STATS MT_SWDEF(0x040) #define MT_SWDEF_PLE_STATS MT_SWDEF(0x044) #define MT_SWDEF_PLE1_STATS MT_SWDEF(0x048) +#define MT_SWDEF_PLE1_MDP_RIOC_HANG_ERR BIT(3) #define MT_SWDEF_PLE_AMSDU_STATS MT_SWDEF(0x04C) #define MT_SWDEF_PSE_STATS MT_SWDEF(0x050) #define MT_SWDEF_PSE1_STATS MT_SWDEF(0x054) From 7e4208e9f6a876c2b7d28fdb6b86dff3b05db2f7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:48:09 +0000 Subject: [PATCH 320/403] wifi: mt76: mt7996: free vif links after clearing wcid entries on full reset mt7996_mac_reset_vif_iter() queues non-default vif links for kfree_rcu while dev->wcid[] still holds pointers to the wcid embedded in each freed link; mt76_reset_device() then dereferences those entries and runs mt76_wcid_cleanup() on them. If a grace period elapses in between, the cleanup operates on freed memory. Run mt76_reset_device() first, so the wcid entries are cleaned up and cleared while the links are still valid. Fixes: ace5d3b6b49e ("wifi: mt76: mt7996: improve hardware restart reliability") Link: https://patch.msgid.link/20260724124813.3961474-25-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 544da01b0411..ce8c5a5ea8cc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2485,10 +2485,10 @@ mt7996_mac_full_reset(struct mt7996_dev *dev) phy->omac_mask = 0; ieee80211_iterate_stations_atomic(hw, mt7996_mac_reset_sta_iter, dev); + mt76_reset_device(&dev->mt76); ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER, mt7996_mac_reset_vif_iter, dev); - mt76_reset_device(&dev->mt76); INIT_LIST_HEAD(&dev->sta_rc_list); INIT_LIST_HEAD(&dev->twt_list); From 75d2a4e2129b58bb0ddb842387299038495aad2a Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:48:10 +0000 Subject: [PATCH 321/403] wifi: mt76: mt7996: clear stale link state on full reset After a full chip reset, mac80211 reconfig replays interface, link and channel context setup. mt7996_vif_link_add() short-circuits when the link_id is still marked in mvif->valid_links, a state introduced for postponing link teardown to interface removal. The reset path frees the link structures without clearing those bits, so the replayed setup never re-creates dev_info/bss_info/STA records in the restarted firmware and never re-registers the link wcid, leaving the device inoperative. The reset path also leaks every allocated MLD index: per-link indices and the per-vif group/remap indices are re-allocated from scratch during reconfig, but the old bits stay set in the masks, so repeated full resets exhaust the index space. Clear valid_links in the reset vif iterator and reset the MLD index masks alongside the existing omac_mask clearing. Fixes: ace5d3b6b49e ("wifi: mt76: mt7996: improve hardware restart reliability") Fixes: 08813703ac41 ("wifi: mt76: mt7996: Destroy vif active links in mt7996_remove_interface()") Link: https://patch.msgid.link/20260724124813.3961474-26-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index ce8c5a5ea8cc..4794dacb3756 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2449,6 +2449,7 @@ mt7996_mac_reset_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) rcu_assign_pointer(mvif->link[i], NULL); kfree_rcu(mlink, rcu_head); } + mvif->valid_links = 0; rcu_read_unlock(); } @@ -2483,6 +2484,8 @@ mt7996_mac_full_reset(struct mt7996_dev *dev) mt7996_for_each_phy(dev, phy) phy->omac_mask = 0; + dev->mld_idx_mask = 0; + dev->mld_remap_idx_mask = 0; ieee80211_iterate_stations_atomic(hw, mt7996_mac_reset_sta_iter, dev); mt76_reset_device(&dev->mt76); From 6ff1c217a00335c92aa6a9b35b3ff3b128efbfea Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 24 Jul 2026 12:48:11 +0000 Subject: [PATCH 322/403] wifi: mt76: set MT76_SCANNING when starting a hw scan The scan state bit is cleared by mt76_scan_complete(), but nothing ever sets it: mt76_sw_scan() is only called for drivers without hw scan support. As a result, all MT76_SCANNING checks are inert for drivers using mt76_hw_scan(), including the DFS state handling in mt76_phy_dfs_state() and the guard against manually triggered radar detection while scanning. Set the bit when the scan request is accepted. Since every channel programmed while scanning now evaluates the DFS state as disabled and stops the radar detector, re-program the operating channel at scan completion regardless of the off-channel state, after the scanning bit has been cleared. Otherwise a scan whose last visited channel was the operating channel, or one that returned to it early because of associated stations, would leave radar detection stopped until the next channel switch. Fixes: 31083e38548f ("wifi: mt76: add code for emulating hardware scanning") Link: https://patch.msgid.link/20260724124813.3961474-27-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/scan.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c index 325638d587c9..3cb11689d4bf 100644 --- a/drivers/net/wireless/mediatek/mt76/scan.c +++ b/drivers/net/wireless/mediatek/mt76/scan.c @@ -16,10 +16,17 @@ static void mt76_scan_complete(struct mt76_dev *dev, bool abort) clear_bit(MT76_SCANNING, &phy->state); - if (dev->scan.chan && phy->main_chandef.chan && phy->offchannel && + /* Re-program the operating channel even when the scan never left it: + * any channel set during the scan ran with MT76_SCANNING held, which + * left DFS radar detection disabled + */ + if (phy->main_chandef.chan && !test_bit(MT76_MCU_RESET, &dev->phy.state)) { + bool offchannel = phy->offchannel; + mt76_set_channel(phy, &phy->main_chandef, false); - mt76_offchannel_notify(phy, false); + if (offchannel) + mt76_offchannel_notify(phy, false); } mt76_put_vif_phy_link(phy, dev->scan.vif, dev->scan.mlink); memset(&dev->scan, 0, sizeof(dev->scan)); @@ -211,6 +218,7 @@ int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, dev->scan.vif = vif; dev->scan.phy = phy; dev->scan.mlink = mlink; + set_bit(MT76_SCANNING, &phy->state); ieee80211_queue_delayed_work(dev->phy.hw, &dev->scan_work, 0); out: From 4c3cf4a8b15090bfff518c09903bf7d3ff0ae110 Mon Sep 17 00:00:00 2001 From: Chad Monroe Date: Fri, 24 Jul 2026 12:48:12 +0000 Subject: [PATCH 323/403] wifi: mt76: serialize scan-link teardown with dev->mutex The offchannel scan link is allocated in mt76_hw_scan() under dev->mutex, but torn down without it: mt76_scan_complete() runs from mt76_scan_work() on the mac80211 workqueue, or from mt76_abort_scan(), and calls mt76_put_vif_phy_link(), whose vif_link_remove clears the per-phy omac_mask and the device-wide vif_mask/mld_idx_mask with plain read-modify-write. A vif link add or remove for another interface, running concurrently under dev->mutex, can interleave with these unlocked writes and lose an update: a cleared bit belonging to a live link gets handed out again (two links sharing an omac/bss/wcid index, breaking own-MAC unicast RX for the first one), or a freed bit stays set until reboot and eventually exhausts the index space. Take dev->mutex around the scan completion, mirroring the ROC teardown in mt76_roc_complete_work()/mt76_abort_roc(), and switch the channel restore to __mt76_set_channel() since the caller now holds the lock. mt76_abort_scan() keeps cancelling the scan work before taking the mutex, so the work-vs-abort ordering is unchanged. Signed-off-by: Chad Monroe Link: https://patch.msgid.link/20260724124813.3961474-28-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/scan.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c index 3cb11689d4bf..3594b599662d 100644 --- a/drivers/net/wireless/mediatek/mt76/scan.c +++ b/drivers/net/wireless/mediatek/mt76/scan.c @@ -11,6 +11,8 @@ static void mt76_scan_complete(struct mt76_dev *dev, bool abort) .aborted = abort, }; + lockdep_assert_held(&dev->mutex); + if (!phy) return; @@ -24,7 +26,7 @@ static void mt76_scan_complete(struct mt76_dev *dev, bool abort) !test_bit(MT76_MCU_RESET, &dev->phy.state)) { bool offchannel = phy->offchannel; - mt76_set_channel(phy, &phy->main_chandef, false); + __mt76_set_channel(phy, &phy->main_chandef, false); if (offchannel) mt76_offchannel_notify(phy, false); } @@ -41,7 +43,10 @@ void mt76_abort_scan(struct mt76_dev *dev) spin_unlock_bh(&dev->scan_lock); cancel_delayed_work_sync(&dev->scan_work); + + mutex_lock(&dev->mutex); mt76_scan_complete(dev, true); + mutex_unlock(&dev->mutex); } EXPORT_SYMBOL_GPL(mt76_abort_scan); @@ -139,7 +144,9 @@ void mt76_scan_work(struct work_struct *work) goto probe; if (dev->scan.chan_idx >= req->n_channels) { + mutex_lock(&dev->mutex); mt76_scan_complete(dev, false); + mutex_unlock(&dev->mutex); return; } From a3da1f2a3e798553397b94d4cd0c55065f27035c Mon Sep 17 00:00:00 2001 From: Chad Monroe Date: Fri, 24 Jul 2026 12:48:13 +0000 Subject: [PATCH 324/403] wifi: mt76: mt7996: hold dev->mutex in remove_interface teardown mt7996_remove_interface() destroys the remaining vif links, clearing omac_mask, vif_mask and mld_idx_mask, with only the wiphy mutex held. Those masks are modified under dev->mutex everywhere else, so the unlocked clears can race the scan-link teardown and the reset work and lose updates. Take dev->mutex around the link destroy loop, matching mt7996_add_interface() and mt7915_remove_interface(). The mutex is released before mt76_vif_cleanup(), which aborts a pending scan and takes the mutex itself. Signed-off-by: Chad Monroe Link: https://patch.msgid.link/20260724124813.3961474-29-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 21a928431c25..54e79bd25995 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -606,6 +606,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw, unsigned int link_id; int i; + mutex_lock(&dev->mt76.mutex); + /* Remove all active links */ for_each_set_bit(link_id, &rem_links, IEEE80211_MLD_MAX_NUM_LINKS) { struct mt7996_vif_link *link; @@ -622,6 +624,8 @@ static void mt7996_remove_interface(struct ieee80211_hw *hw, mt7996_vif_link_destroy(phy, link, vif, NULL); } + mutex_unlock(&dev->mt76.mutex); + ieee80211_iterate_active_interfaces_mtx(hw, 0, mt7996_remove_iter, &rdata); mt76_vif_cleanup(&dev->mt76, vif); From d0b750072a29c8ff0504c3bc28c411c402f26716 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 27 Jul 2026 15:04:20 +0000 Subject: [PATCH 325/403] wifi: mt76: mt7996: fix MIB TX aggregation counter registers for mt7990 The MIB_TSCR0-7 counters read by mt7996_mac_update_stats() are hardcoded at the mt7996/mt7992 offsets 0x6b0-0x6d0, but mt7990 moved them to 0x750-0x770, so TX AMPDU statistics were read from unrelated registers on that chip. Move the offsets into the per-chip register tables. Fixes: f6c87411d15f ("wifi: mt76: mt7996: rework register mapping for mt7990") Link: https://patch.msgid.link/20260727150434.1778520-1-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/mmio.c | 24 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7996/regs.h | 24 ++++++++++++------- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index d9780bb425a7..de68e60a1e86 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -54,6 +54,14 @@ static const u32 mt7996_offs[] = { [MIB_BSCR7] = 0x9e8, [MIB_BSCR17] = 0xa10, [MIB_TRDR1] = 0xa28, + [MIB_TSCR0] = 0x6b0, + [MIB_TSCR1] = 0x6b4, + [MIB_TSCR2] = 0x6b8, + [MIB_TSCR3] = 0x6bc, + [MIB_TSCR4] = 0x6c0, + [MIB_TSCR5] = 0x6c4, + [MIB_TSCR6] = 0x6c8, + [MIB_TSCR7] = 0x6d0, [HIF_REMAP_L1] = 0x24, [HIF_REMAP_BASE_L1] = 0x130000, [HIF_REMAP_L2] = 0x1b4, @@ -91,6 +99,14 @@ static const u32 mt7992_offs[] = { [MIB_BSCR7] = 0xae4, [MIB_BSCR17] = 0xb0c, [MIB_TRDR1] = 0xb24, + [MIB_TSCR0] = 0x6b0, + [MIB_TSCR1] = 0x6b4, + [MIB_TSCR2] = 0x6b8, + [MIB_TSCR3] = 0x6bc, + [MIB_TSCR4] = 0x6c0, + [MIB_TSCR5] = 0x6c4, + [MIB_TSCR6] = 0x6c8, + [MIB_TSCR7] = 0x6d0, [HIF_REMAP_L1] = 0x8, [HIF_REMAP_BASE_L1] = 0x40000, [HIF_REMAP_L2] = 0x1b4, @@ -128,6 +144,14 @@ static const u32 mt7990_offs[] = { [MIB_BSCR7] = 0xbd4, [MIB_BSCR17] = 0xbfc, [MIB_TRDR1] = 0xc14, + [MIB_TSCR0] = 0x750, + [MIB_TSCR1] = 0x754, + [MIB_TSCR2] = 0x758, + [MIB_TSCR3] = 0x75c, + [MIB_TSCR4] = 0x760, + [MIB_TSCR5] = 0x764, + [MIB_TSCR6] = 0x768, + [MIB_TSCR7] = 0x770, [HIF_REMAP_L1] = 0x8, [HIF_REMAP_BASE_L1] = 0x40000, [HIF_REMAP_L2] = 0x1b8, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h index c6379933b6c3..8ff78cf6eb04 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h @@ -64,6 +64,14 @@ enum offs_rev { MIB_BSCR7, MIB_BSCR17, MIB_TRDR1, + MIB_TSCR0, + MIB_TSCR1, + MIB_TSCR2, + MIB_TSCR3, + MIB_TSCR4, + MIB_TSCR5, + MIB_TSCR6, + MIB_TSCR7, HIF_REMAP_L1, HIF_REMAP_BASE_L1, HIF_REMAP_L2, @@ -250,9 +258,9 @@ enum offs_rev { #define MT_MIB_BSCR7(_band) MT_WF_MIB(_band, __OFFS(MIB_BSCR7)) #define MT_MIB_BSCR17(_band) MT_WF_MIB(_band, __OFFS(MIB_BSCR17)) -#define MT_MIB_TSCR5(_band) MT_WF_MIB(_band, 0x6c4) -#define MT_MIB_TSCR6(_band) MT_WF_MIB(_band, 0x6c8) -#define MT_MIB_TSCR7(_band) MT_WF_MIB(_band, 0x6d0) +#define MT_MIB_TSCR5(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR5)) +#define MT_MIB_TSCR6(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR6)) +#define MT_MIB_TSCR7(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR7)) #define MT_MIB_RSCR1(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR1)) /* rx mpdu counter, full 32 bits */ @@ -268,14 +276,14 @@ enum offs_rev { #define MT_MIB_RSCR36(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR36)) /* tx ampdu cnt, full 32 bits */ -#define MT_MIB_TSCR0(_band) MT_WF_MIB(_band, 0x6b0) -#define MT_MIB_TSCR2(_band) MT_WF_MIB(_band, 0x6b8) +#define MT_MIB_TSCR0(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR0)) +#define MT_MIB_TSCR2(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR2)) /* counts all mpdus in ampdu, regardless of success */ -#define MT_MIB_TSCR3(_band) MT_WF_MIB(_band, 0x6bc) +#define MT_MIB_TSCR3(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR3)) /* counts all successfully tx'd mpdus in ampdu */ -#define MT_MIB_TSCR4(_band) MT_WF_MIB(_band, 0x6c0) +#define MT_MIB_TSCR4(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR4)) /* rx ampdu count, 32-bit */ #define MT_MIB_RSCR27(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR27)) @@ -299,7 +307,7 @@ enum offs_rev { #define MT_MIB_RVSR1(_band) MT_WF_MIB(_band, __OFFS(MIB_RVSR1)) /* rx blockack count, 32 bits */ -#define MT_MIB_TSCR1(_band) MT_WF_MIB(_band, 0x6b4) +#define MT_MIB_TSCR1(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR1)) #define MT_MIB_BTSCR0(_band) MT_WF_MIB(_band, 0x5e0) #define MT_MIB_BTSCR5(_band) MT_WF_MIB(_band, __OFFS(MIB_BTSCR5)) From 3ae8ad277e2819a281b0e36b55633c8515c16ce7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 27 Jul 2026 15:04:21 +0000 Subject: [PATCH 326/403] wifi: mt76: mt7915: fix double hif2 init on the non-WED path mt7915_pci_init_hif2() was called unconditionally and again inside the WED-inactive branch. The helper increments the global hif_idx, writes the PCIe RECOG_ID register and takes a get_device() reference via mt7915_pci_get_hif2(), while removal only drops one reference. On non-WED dual-hif hardware this double-incremented hif_idx, wrote RECOG_ID twice and leaked a device reference. Only the call inside the WED-inactive branch is correct; drop the unconditional one. hif2 is already initialised to NULL. Fixes: cacdd67812c6 ("mt76: mt7915: add mt7915_mmio_probe() as a common probing function") Link: https://patch.msgid.link/20260727150434.1778520-2-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c index f6b03211a879..12b3e2dd530a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c @@ -135,7 +135,6 @@ static int mt7915_pci_probe(struct pci_dev *pdev, mdev = &dev->mt76; mt7915_wfsys_reset(dev); - hif2 = mt7915_pci_init_hif2(pdev); ret = mt7915_mmio_wed_init(dev, pdev, true, &irq); if (ret < 0) From 15b960014f24dce5388d4a2e7274e6490cb3c421 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 27 Jul 2026 15:04:22 +0000 Subject: [PATCH 327/403] wifi: mt76: mt7915: fix ext PHY use-after-free on register error path After mt7915_register_ext_phy() succeeded, a failure of the main PHY mt7915_init_debugfs() or mt7915_coredump_register() unwound through free_phy2, which called ieee80211_free_hw() on the ext PHY hw while it was still registered with mac80211, since mt76_unregister_device() only unregisters the main hw. Unregister the ext PHY (thermal + phy + hw) first and skip the redundant free. Fixes: 7b8e1ae886e4 ("mt76: mt7915: rework hardware/phy initialization") Link: https://patch.msgid.link/20260727150434.1778520-3-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/init.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 0f6346731308..ca46a203aa48 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -1302,14 +1302,19 @@ int mt7915_register_device(struct mt7915_dev *dev) ret = mt7915_init_debugfs(&dev->phy); if (ret) - goto unreg_thermal; + goto unreg_ext_phy; ret = mt7915_coredump_register(dev); if (ret) - goto unreg_thermal; + goto unreg_ext_phy; return 0; +unreg_ext_phy: + if (phy2) { + mt7915_unregister_ext_phy(dev); + phy2 = NULL; + } unreg_thermal: mt7915_unregister_thermal(&dev->phy); unreg_dev: From 8370aebd26a9dfa2e0de665e3ab504c0e97ee730 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 27 Jul 2026 15:04:23 +0000 Subject: [PATCH 328/403] wifi: mt76: mt7915: release hif2 reference on probe IRQ failure The hif2 reference obtained by mt7915_pci_init_hif2() is only released on error paths that key off dev->hif2, which is not assigned until after the IRQ setup. If pci_alloc_irq_vectors() or the primary devm_request_irq() fails, the reference leaks. Drop it explicitly on those paths via mt7915_put_hif2(). Fixes: f68d67623dec ("mt76: mt7915: add Wireless Ethernet Dispatch support") Link: https://patch.msgid.link/20260727150434.1778520-4-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/pci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c index 12b3e2dd530a..8007e620048b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c @@ -144,16 +144,20 @@ static int mt7915_pci_probe(struct pci_dev *pdev, hif2 = mt7915_pci_init_hif2(pdev); ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); - if (ret < 0) + if (ret < 0) { + mt7915_put_hif2(hif2); goto free_device; + } irq = pdev->irq; } ret = devm_request_irq(mdev->dev, irq, mt7915_irq_handler, IRQF_SHARED, KBUILD_MODNAME, dev); - if (ret) + if (ret) { + mt7915_put_hif2(hif2); goto free_wed_or_irq_vector; + } /* master switch of PCIe tnterrupt enable */ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); From eb906eeff2d1e84b628dc210dada325269c71383 Mon Sep 17 00:00:00 2001 From: StanleyYP Wang Date: Mon, 27 Jul 2026 15:04:24 +0000 Subject: [PATCH 329/403] wifi: mt76: mt7996: fix reg addr remap when addr is 0 When addr is less than the hardcoded threshold in __mt7996_reg_addr, it indicates that remapping is unnecessary. Currently, the flow remaps address 0x0 to MT_HIF_REMAP_BASE_L2, which is incorrect. To address this, modify __mt7996_reg_addr to return INVALID_REG_ADDR if the address is not below the hardcoded value or is not present in the mt7996_reg_map array. Additionally, update the remap condition to check if addr is equal to INVALID_REG_ADDR. Fixes: 3687854d3e7e ("wifi: mt76: mt7996: add locking for accessing mapped registers") Signed-off-by: StanleyYP Wang Link: https://patch.msgid.link/20260727150434.1778520-5-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mmio.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index de68e60a1e86..0d7521d06981 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -17,6 +17,8 @@ static bool wed_enable; module_param(wed_enable, bool, 0644); +#define INVALID_REG_ADDR 0xffffffff + static const struct __base mt7996_reg_base[] = { [WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } }, [WF_ARB_BASE] = { { 0x820e3000, 0x820f3000, 0x830e3000 } }, @@ -358,7 +360,7 @@ static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr) return dev->reg.map[i].mapped + ofs; } - return 0; + return INVALID_REG_ADDR; } static u32 __mt7996_reg_remap_addr(struct mt7996_dev *dev, u32 addr) @@ -390,7 +392,7 @@ void mt7996_memcpy_fromio(struct mt7996_dev *dev, void *buf, u32 offset, { u32 addr = __mt7996_reg_addr(dev, offset); - if (addr) { + if (addr != INVALID_REG_ADDR) { memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len); return; } @@ -406,7 +408,7 @@ static u32 mt7996_rr(struct mt76_dev *mdev, u32 offset) struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); u32 addr = __mt7996_reg_addr(dev, offset), val; - if (addr) + if (addr != INVALID_REG_ADDR) return dev->bus_ops->rr(mdev, addr); spin_lock_bh(&dev->reg_lock); @@ -421,7 +423,7 @@ static void mt7996_wr(struct mt76_dev *mdev, u32 offset, u32 val) struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); u32 addr = __mt7996_reg_addr(dev, offset); - if (addr) { + if (addr != INVALID_REG_ADDR) { dev->bus_ops->wr(mdev, addr, val); return; } @@ -436,7 +438,7 @@ static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); u32 addr = __mt7996_reg_addr(dev, offset); - if (addr) + if (addr != INVALID_REG_ADDR) return dev->bus_ops->rmw(mdev, addr, mask, val); spin_lock_bh(&dev->reg_lock); From 7c1924332e986019c6bcddf55c843361cccac73f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 27 Jul 2026 15:04:25 +0000 Subject: [PATCH 330/403] wifi: mt76: mt7996: do not attach hif2 WED when the main WED attach failed If the WED attach for the primary PCIe function fails, the probe path still attached wed_hif2 for the secondary function, leaving the device in an inconsistent half-WED configuration that crashes later. The hif2 call also re-enabled hwrro_mode, which the failed primary attach had just turned off. Skip the hif2 WED setup when the primary WED device is not active. Fixes: 83eafc9251d6 ("wifi: mt76: mt7996: add wed tx support") Link: https://patch.msgid.link/20260727150434.1778520-6-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mmio.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index 0d7521d06981..ba064324a7cc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -490,6 +490,9 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, if (!wed_enable) return 0; + if (hif2 && !mtk_wed_device_active(&dev->mt76.mmio.wed)) + return 0; + dev->mt76.hwrro_mode = is_mt7996(&dev->mt76) ? MT76_HWRRO_V3 : MT76_HWRRO_V3_1; From 6a4cabff1203791797683cf6be8f56bee983dc73 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 27 Jul 2026 15:04:26 +0000 Subject: [PATCH 331/403] wifi: mt76: mt7996: do not leave state behind after a failed WED attach mt7996_mmio_wed_init() set dev->mt76.hwrro_mode and rx_token_size while building the WED configuration, before knowing whether the WED attach can succeed. A failed attach left the enlarged rx_token_size behind and reset hwrro_mode to MT76_HWRRO_OFF, clobbering the values that another RX datapath owner may have configured earlier in probe: on Airoha platforms with the wed_enable module parameter set, this broke the NPU offload configuration set up by mt76_npu_init() (NPU offload requires HW-RRO and a larger rx token space, and the attach always fails there since no SoC has both an Airoha NPU and MTK WED). Move both assignments after a successful attach, next to the existing success-only dma_dev/irq assignments. This is safe for the regular WED attach case: the first consumer of either field runs after probe continues (mtk_wed_device_attach() only invokes the init_buf callback; rx buffers are allocated via init_rx_buf from mtk_wed_start(), long after mt7996_mmio_wed_init() has returned). Within the WED configuration the HW-RRO checks were constant: the mode was assigned unconditionally right before them, and the hif2 path is only reachable after a successful main attach has set it. Resolve them to their constant values and drop the dead branches. Fixes: 377aa17d2aed ("wifi: mt76: mt7996: Add NPU offload support to MT7996 driver") Link: https://patch.msgid.link/20260727150434.1778520-7-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/mmio.c | 53 +++++++------------ 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index ba064324a7cc..ac81be5fe023 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -493,9 +493,6 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, if (hif2 && !mtk_wed_device_active(&dev->mt76.mmio.wed)) return 0; - dev->mt76.hwrro_mode = is_mt7996(&dev->mt76) ? MT76_HWRRO_V3 - : MT76_HWRRO_V3_1; - hif1_ofs = dev->hif2 ? MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0) : 0; if (hif2) @@ -520,23 +517,16 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, wed->wlan.wpdma_tx = wed->wlan.phy_base + hif1_ofs + MT_TXQ_RING_BASE(0) + MT7996_TXQ_BAND2 * MT_RING_SIZE; - if (mt7996_has_hwrro(dev)) { - if (is_mt7996(&dev->mt76)) { - wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1; - wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs + - MT_RXQ_RING_BASE(0) + - MT7996_RXQ_TXFREE2 * MT_RING_SIZE; - } else { - wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_BAND1_EXT) - 1; - wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs + - MT_RXQ_RING_BASE(0) + - MT7996_RXQ_MCU_WA_EXT * MT_RING_SIZE; - } - } else { + if (is_mt7996(&dev->mt76)) { + wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1; wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs + MT_RXQ_RING_BASE(0) + - MT7996_RXQ_MCU_WA_TRI * MT_RING_SIZE; - wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_TRI) - 1; + MT7996_RXQ_TXFREE2 * MT_RING_SIZE; + } else { + wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_BAND1_EXT) - 1; + wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs + + MT_RXQ_RING_BASE(0) + + MT7996_RXQ_MCU_WA_EXT * MT_RING_SIZE; } wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG; @@ -547,7 +537,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, wed->wlan.id = MT7996_DEVICE_ID_2; wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1; } else { - wed->wlan.hw_rro = mt7996_has_hwrro(dev); + wed->wlan.hw_rro = true; wed->wlan.wpdma_int = wed->wlan.phy_base + MT_INT_SOURCE_CSR; wed->wlan.wpdma_mask = wed->wlan.phy_base + MT_INT_MASK_CSR; wed->wlan.wpdma_tx = wed->wlan.phy_base + MT_TXQ_RING_BASE(0) + @@ -600,23 +590,15 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND0) - 1; wed->wlan.tx_tbit[1] = ffs(MT_INT_TX_DONE_BAND1) - 1; if (is_mt7996(&dev->mt76)) { - if (mt7996_has_hwrro(dev)) { - wed->wlan.wpdma_txfree = wed->wlan.phy_base + - MT_RXQ_RING_BASE(0) + - MT7996_RXQ_TXFREE0 * MT_RING_SIZE; - wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1; - } else { - wed->wlan.wpdma_txfree = wed->wlan.phy_base + - MT_RXQ_RING_BASE(0) + - MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE; - wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1; - } + wed->wlan.wpdma_txfree = wed->wlan.phy_base + + MT_RXQ_RING_BASE(0) + + MT7996_RXQ_TXFREE0 * MT_RING_SIZE; + wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1; } else { wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1; wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) + MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE; } - dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt; if (dev->hif2 && is_mt7992(&dev->mt76)) wed->wlan.id = 0x7992; @@ -639,9 +621,14 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, wed->wlan.reset_complete = mt76_wed_reset_complete; } - if (mtk_wed_device_attach(wed)) { - dev->mt76.hwrro_mode = MT76_HWRRO_OFF; + if (mtk_wed_device_attach(wed)) return 0; + + if (!hif2) { + dev->mt76.hwrro_mode = is_mt7996(&dev->mt76) ? MT76_HWRRO_V3 + : MT76_HWRRO_V3_1; + dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + + wed->wlan.rx_npkt; } *irq = wed->irq; From ea891799eccc9e43ebba0dc157d4b197ad6c1a0e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 27 Jul 2026 15:04:27 +0000 Subject: [PATCH 332/403] wifi: mt76: mt7915: fix chainmask handling for non-dbdc phys on band 1 On single-adie mt7986 the only phy is bound to band 1, but its chainmask is stored unshifted, because dev->chainshift is still zero while the eeprom is parsed for the main phy. mt7915_set_antenna() on the other hand shifts by chainshift * band_idx, so the representation of the chainmask changed as soon as the antenna configuration was touched. Until then, mt7915_mcu_set_chan_info() passed rx_path = 0 to the firmware, since shifting the unshifted mask down clears all bits. Keep the unshifted form for that case and add helpers for the band local chainmask, so that only the band 1 phy of a dbdc device uses the shifted form. Fixes: 3eb50cc90534 ("wifi: mt76: mt7915: rely on band_idx of mt76_phy") Link: https://patch.msgid.link/20260727150434.1778520-8-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/eeprom.c | 2 +- .../net/wireless/mediatek/mt76/mt7915/main.c | 6 +++--- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 2 +- .../net/wireless/mediatek/mt76/mt7915/mt7915.h | 18 ++++++++++++++++++ .../wireless/mediatek/mt76/mt7915/testmode.c | 5 +---- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c index eb92cbf1a284..fe7b29ebc0bf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c @@ -257,7 +257,7 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, nss = min_t(u8, min_t(u8, nss_max, nss), path); mphy->chainmask = BIT(path) - 1; - if (band) + if (band && dev->dbdc_support) mphy->chainmask <<= dev->chainshift; mphy->antenna_mask = BIT(nss) - 1; dev->chainmask |= mphy->chainmask; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index d2130226de64..4783e5f52d22 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -1138,7 +1138,7 @@ mt7915_set_antenna(struct ieee80211_hw *hw, int radio_idx, u32 tx_ant, u32 rx_an struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); int max_nss = hweight8(hw->wiphy->available_antennas_tx); - u8 chainshift = dev->chainshift; + u8 shift = mt7915_band_chainshift(phy); u8 band = phy->mt76->band_idx; if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) @@ -1151,9 +1151,9 @@ mt7915_set_antenna(struct ieee80211_hw *hw, int radio_idx, u32 tx_ant, u32 rx_an /* handle a variant of mt7916/mt7981 which has 3T3R but nss2 on 5 GHz band */ if ((is_mt7916(&dev->mt76) || is_mt7981(&dev->mt76)) && band && hweight8(tx_ant) == max_nss) - phy->mt76->chainmask = (dev->chainmask >> chainshift) << chainshift; + phy->mt76->chainmask = (dev->chainmask >> shift) << shift; else - phy->mt76->chainmask = tx_ant << (chainshift * band); + phy->mt76->chainmask = tx_ant << shift; mt76_set_stream_caps(phy->mt76, true); mt7915_set_stream_vht_txbf_caps(phy); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 75eb6d261033..88955aed62e2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -2804,7 +2804,7 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) .center_ch = ieee80211_frequency_to_channel(freq1), .bw = mt76_connac_chan_bw(chandef), .tx_path_num = hweight16(phy->mt76->chainmask), - .rx_path = phy->mt76->chainmask >> (dev->chainshift * band), + .rx_path = mt7915_band_chainmask(phy), .band_idx = band, .channel_band = ch_band[chandef->chan->band], }; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 0f7e871dc7a1..30fb119ce872 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -399,6 +399,24 @@ mt7915_ext_phy(struct mt7915_dev *dev) return phy->priv; } +/* without dbdc, the chainmask is stored unshifted, even if the phy is + * bound to band 1 + */ +static inline u8 mt7915_band_chainshift(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + + if (!dev->dbdc_support) + return 0; + + return phy->mt76->band_idx * dev->chainshift; +} + +static inline u16 mt7915_band_chainmask(struct mt7915_phy *phy) +{ + return phy->mt76->chainmask >> mt7915_band_chainshift(phy); +} + static inline u32 mt7915_check_adie(struct mt7915_dev *dev, bool sku) { u32 mask = sku ? MT_CONNINFRA_SKU_MASK : MT_ADIE_TYPE_MASK; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c index 618a5c2bdd29..7576973f4d4e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c @@ -694,9 +694,7 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb, { struct mt76_testmode_data *td = &mphy->test; struct mt7915_phy *phy = mphy->priv; - struct mt7915_dev *dev = phy->dev; - u32 chainmask = mphy->chainmask, changed = 0; - bool ext_phy = phy != &dev->phy; + u32 chainmask = mt7915_band_chainmask(phy), changed = 0; int i; BUILD_BUG_ON(NUM_TM_CHANGED >= 32); @@ -705,7 +703,6 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb, td->state == MT76_TM_STATE_OFF) return 0; - chainmask = ext_phy ? chainmask >> dev->chainshift : chainmask; if (td->tx_antenna_mask > chainmask) return -EINVAL; From b53c44fe65792608f58028c7b0953e610ad652ee Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 27 Jul 2026 15:04:28 +0000 Subject: [PATCH 333/403] wifi: mt76: mt7915: report RX chain signal for all RX paths status->chains was set from the antenna mask, which is derived from the number of spatial streams, while the chain_signal array is filled from all RCPI fields. On boards where the number of RX paths exceeds the stream count, e.g. the 3T3R mt7916/mt7981 variant with 2 streams on the 5 GHz band, the RSSI of the extra chains was never reported. Use the band local RX path chainmask instead. Fixes: e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets") Link: https://patch.msgid.link/20260727150434.1778520-9-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index df6359c88b18..b3d4abf6a9ed 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -437,7 +437,7 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb, if (v0 & MT_PRXV_HT_AD_CODE) status->enc_flags |= RX_ENC_FLAG_LDPC; - status->chains = mphy->antenna_mask; + status->chains = mt7915_band_chainmask(phy); status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v1); status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1); status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v1); From d22f7f29c6baec1d7ececef4936f250962813959 Mon Sep 17 00:00:00 2001 From: StanleyYP Wang Date: Mon, 27 Jul 2026 15:04:29 +0000 Subject: [PATCH 334/403] wifi: mt76: mt7996: remove repeater muar config Repeater mode is not supported currently, so remove it. get_omac_idx() only hands out HW_BSSID/EXT_BSSID indices, so the omac_idx >= REPEATER_BSSID_START call sites are unreachable. Signed-off-by: StanleyYP Wang Link: https://patch.msgid.link/20260727150434.1778520-10-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 24ce87d928e8..8aab91810135 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -1044,43 +1044,6 @@ mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct mt76_vif_link *mlink) sec->cipher = mlink->cipher; } -static int -mt7996_mcu_muar_config(struct mt7996_dev *dev, struct mt76_vif_link *mlink, - const u8 *addr, bool bssid, bool enable) -{ -#define UNI_MUAR_ENTRY 2 - u32 idx = mlink->omac_idx - REPEATER_BSSID_START; - struct { - struct { - u8 band; - u8 __rsv[3]; - } hdr; - - __le16 tag; - __le16 len; - - bool smesh; - u8 bssid; - u8 index; - u8 entry_add; - u8 addr[ETH_ALEN]; - u8 __rsv[2]; - } __packed req = { - .hdr.band = mlink->band_idx, - .tag = cpu_to_le16(UNI_MUAR_ENTRY), - .len = cpu_to_le16(sizeof(req) - sizeof(req.hdr)), - .smesh = false, - .index = idx * 2 + bssid, - .entry_add = true, - }; - - if (enable) - memcpy(req.addr, addr, ETH_ALEN); - - return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req, - sizeof(req), true); -} - static void mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct mt7996_phy *phy) { @@ -1210,11 +1173,6 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct mt7996_dev *dev = phy->dev; struct sk_buff *skb; - if (mlink->omac_idx >= REPEATER_BSSID_START) { - mt7996_mcu_muar_config(dev, mlink, link_conf->addr, false, enable); - mt7996_mcu_muar_config(dev, mlink, link_conf->bssid, true, enable); - } - skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, mlink, MT7996_BSS_UPDATE_MAX_SIZE); if (IS_ERR(skb)) @@ -2996,9 +2954,6 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, }, }; - if (mlink->omac_idx >= REPEATER_BSSID_START) - return mt7996_mcu_muar_config(dev, mlink, link_conf->addr, false, enable); - memcpy(data.tlv.omac_addr, link_conf->addr, ETH_ALEN); return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE), &data, sizeof(data), true); From 3999d15cfcc72a946ec419c4059b0e0cd7860053 Mon Sep 17 00:00:00 2001 From: Peter Chiu Date: Mon, 27 Jul 2026 15:04:30 +0000 Subject: [PATCH 335/403] wifi: mt76: fix queue assignment for disassoc packets Like deauth, a disassoc frame sent to a client in powersave mode can get stuck in a tx queue along with other buffered frames, filling up hardware queues with frames that are only released after the WTBL slot is reused for another client. Move disassoc packets to the ALTX queue, matching the existing deauth handling. Fixes: dedf2ec30fe4 ("wifi: mt76: fix queue assignment for deauth packets") Signed-off-by: Peter Chiu Link: https://patch.msgid.link/20260727150434.1778520-11-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/tx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index dc8407be2891..b03be0eb4712 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -631,6 +631,7 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid, !ieee80211_is_data_present(hdr->frame_control) && (!ieee80211_is_bufferable_mmpdu(skb) || ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control) || head == &wcid->tx_offchannel)) qid = MT_TXQ_PSD; From de3afeafbc5397711eb1c22367d286bd02dbcaab Mon Sep 17 00:00:00 2001 From: Howard Hsu Date: Mon, 27 Jul 2026 15:04:31 +0000 Subject: [PATCH 336/403] wifi: mt76: mt7996: reject iTWT setup requests from MLD stations The firmware does not support individual TWT agreements with non-AP MLDs, and the driver only tracks TWT flow state on the default link, which may not be the link the agreement was negotiated on. Reject TWT setup requests from MLD stations instead of programming an unsupported configuration. Signed-off-by: Howard Hsu Link: https://patch.msgid.link/20260727150434.1778520-12-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 4794dacb3756..1cba381a78b8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -3218,6 +3218,12 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, struct mt7996_twt_flow *flow; u8 flowid, table_id, exp; + /* the firmware does not support iTWT agreements with MLD peers, and + * driver TWT state is only tracked on the default link + */ + if (sta->mlo) + goto out; + if (mt7996_mac_check_twt_req(twt)) goto out; From 6613d2104f12046ae64c6611a3519d622935cf4e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 27 Jul 2026 15:04:32 +0000 Subject: [PATCH 337/403] wifi: mt76: mt7915: update SKU power limits after changing antennas Changing the antenna configuration updates the stream capabilities but left the per-path SKU power limits and path delta compensation stale until the next channel switch. Reapply the SKU table like mt7996 already does. Link: https://patch.msgid.link/20260727150434.1778520-13-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 4783e5f52d22..a8286f8becf9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -1158,6 +1158,7 @@ mt7915_set_antenna(struct ieee80211_hw *hw, int radio_idx, u32 tx_ant, u32 rx_an mt76_set_stream_caps(phy->mt76, true); mt7915_set_stream_vht_txbf_caps(phy); mt7915_set_stream_he_caps(phy); + mt7915_mcu_set_txpower_sku(phy); mutex_unlock(&dev->mt76.mutex); From d8eb7952fa1e35a350037aa351ff3c637285c735 Mon Sep 17 00:00:00 2001 From: StanleyYP Wang Date: Mon, 27 Jul 2026 15:04:33 +0000 Subject: [PATCH 338/403] wifi: mt76: mt7996: add scan dwell time hw cap Add NL80211_EXT_FEATURE_SET_SCAN_DWELL support in driver. This allows user to specify channel dwell time during scanning. Signed-off-by: StanleyYP Wang Link: https://patch.msgid.link/20260727150434.1778520-14-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c index ee1f7a9f4851..70902395bc96 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -527,6 +527,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL); if (mt7996_eeprom_has_background_radar(dev) && (!mdev->dev->of_node || From fc7801b7f11d99abdc2fa0e77e2682dede3e7f56 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 27 Jul 2026 15:04:34 +0000 Subject: [PATCH 339/403] wifi: mt76: mt7925: fix infinite loop in UNI event TLV parsing The event TLV loops accept a zero-length TLV, which advances neither the cursor nor the remaining length, so a malformed event hangs the caller. mt7925_mcu_uni_roc_event() additionally walked past the end of the skb, since it never checked the declared length against the remainder. Replace the five open-coded loops with a shared iterator that rejects lengths below the TLV header and beyond the remaining buffer, and check the per-tag payload sizes before dereferencing them. While here, make the RSSI monitor event read from the current TLV rather than from the start of the list. Link: https://patch.msgid.link/20260727150434.1778520-15-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/main.c | 10 ++--- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 38 +++++++++++-------- .../net/wireless/mediatek/mt76/mt7925/mcu.h | 19 ++++++++++ 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 2d79a895713c..475a580b348d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1562,7 +1562,7 @@ void mt7925_scan_work(struct work_struct *work) while (true) { struct sk_buff *skb; struct tlv *tlv; - int tlv_len; + u32 tlv_len; spin_lock_bh(&phy->dev->mt76.lock); skb = __skb_dequeue(&phy->scan_event_list); @@ -1575,7 +1575,7 @@ void mt7925_scan_work(struct work_struct *work) tlv = (struct tlv *)skb->data; tlv_len = skb->len; - while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) { + mt7925_for_each_tlv(tlv, tlv_len) { struct mt7925_mcu_scan_chinfo_event *evt; switch (le16_to_cpu(tlv->tag)) { @@ -1588,6 +1588,9 @@ void mt7925_scan_work(struct work_struct *work) } break; case UNI_EVENT_SCAN_DONE_CHNLINFO: + if (le16_to_cpu(tlv->len) < sizeof(*tlv) + sizeof(*evt)) + break; + evt = (struct mt7925_mcu_scan_chinfo_event *)tlv->data; mt7925_regd_change(phy, evt->alpha2); @@ -1599,9 +1602,6 @@ void mt7925_scan_work(struct work_struct *work) default: break; } - - tlv_len -= le16_to_cpu(tlv->len); - tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len)); } dev_kfree_skb(skb); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index a6f28aa51a2f..fa29c486a455 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -409,16 +409,18 @@ mt7925_mcu_uni_hif_ctrl_event(struct mt792x_dev *dev, struct sk_buff *skb) tlv = (struct tlv *)skb->data; tlv_len = skb->len; - while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) { + mt7925_for_each_tlv(tlv, tlv_len) { switch (le16_to_cpu(tlv->tag)) { case UNI_EVENT_HIF_CTRL_BASIC: + if (le16_to_cpu(tlv->len) < + sizeof(struct mt7925_mcu_hif_ctrl_basic_tlv)) + break; + mt7925_mcu_handle_hif_ctrl_basic(dev, tlv); break; default: break; } - tlv_len -= le16_to_cpu(tlv->len); - tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len)); } } @@ -426,22 +428,24 @@ static void mt7925_mcu_uni_roc_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct tlv *tlv; - int i = 0; + u32 tlv_len; skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + tlv = (struct tlv *)skb->data; + tlv_len = skb->len; - while (i < skb->len) { - tlv = (struct tlv *)(skb->data + i); - + mt7925_for_each_tlv(tlv, tlv_len) { switch (le16_to_cpu(tlv->tag)) { case UNI_EVENT_ROC_GRANT: + if (le16_to_cpu(tlv->len) < + sizeof(struct mt7925_roc_grant_tlv)) + break; + mt7925_mcu_roc_handle_grant(dev, tlv); break; case UNI_EVENT_ROC_GRANT_SUB_LINK: break; } - - i += le16_to_cpu(tlv->len); } } @@ -477,12 +481,15 @@ mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb) tlv = (struct tlv *)skb->data; tlv_len = skb->len; - while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) { + mt7925_for_each_tlv(tlv, tlv_len) { switch (le16_to_cpu(tlv->tag)) { case UNI_EVENT_TX_DONE_MSG: if (!is_mt7928(&dev->mt76)) break; + if (le16_to_cpu(tlv->len) < sizeof(*evt)) + break; + evt = (struct mt7928_uni_txdone_event *)tlv; if (evt->status) { dev_info(dev->mt76.dev, @@ -509,8 +516,6 @@ mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb) default: break; } - tlv_len -= le16_to_cpu(tlv->len); - tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len)); } } @@ -547,10 +552,13 @@ mt7925_mcu_rssi_monitor_event(struct mt792x_dev *dev, struct sk_buff *skb) tlv = (struct tlv *)skb->data; tlv_len = skb->len; - while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) { + mt7925_for_each_tlv(tlv, tlv_len) { switch (le16_to_cpu(tlv->tag)) { case UNI_EVENT_RSSI_MONITOR_INFO: - event = (struct mt7925_uni_rssi_monitor_event *)skb->data; + if (le16_to_cpu(tlv->len) < sizeof(*event)) + break; + + event = (struct mt7925_uni_rssi_monitor_event *)tlv; ieee80211_iterate_active_interfaces_atomic(dev->mt76.hw, IEEE80211_IFACE_ITER_RESUME_ALL, mt7925_mcu_rssi_monitor_iter, @@ -559,8 +567,6 @@ mt7925_mcu_rssi_monitor_event(struct mt792x_dev *dev, struct sk_buff *skb) default: break; } - tlv_len -= le16_to_cpu(tlv->len); - tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len)); } } diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h index 154f792a56bc..11f9eac13ffc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h @@ -699,6 +699,25 @@ mt7925_mcu_get_cipher(int cipher) } } +static inline bool +mt7925_mcu_tlv_valid(struct tlv *tlv, u32 rem) +{ + u16 len; + + if (rem < sizeof(*tlv)) + return false; + + len = le16_to_cpu(tlv->len); + + /* a length below the header size would not advance the cursor */ + return len >= sizeof(*tlv) && len <= rem; +} + +#define mt7925_for_each_tlv(tlv, rem) \ + for (; mt7925_mcu_tlv_valid(tlv, rem); \ + (rem) -= le16_to_cpu((tlv)->len), \ + (tlv) = (struct tlv *)((u8 *)(tlv) + le16_to_cpu((tlv)->len))) + int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable); int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, struct ieee80211_scan_request *scan_req); From 5f23b939a3f7a70bfd552800077ca1110fc55022 Mon Sep 17 00:00:00 2001 From: Jeff Hsu Date: Mon, 6 Jul 2026 10:43:06 +0800 Subject: [PATCH 340/403] wifi: mt76: mt7925: skip DMA re-init selectively Add a flag to skip it on chips that don't need it. Signed-off-by: Jeff Hsu Link: https://patch.msgid.link/20260706024306.47806-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt792x.h | 1 + drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 3 +++ 3 files changed, 7 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 4e734f4f65d1..02ef09dd797d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -650,6 +650,9 @@ static int mt7925_pci_probe(struct pci_dev *pdev, if (!mt7925_disable_aspm && mt76_pci_aspm_supported(pdev)) dev->aspm_supported = true; + if (is_mt7928(&dev->mt76)) + dev->skip_wpdma_reinit = true; + ret = __mt792x_mcu_fw_pmctrl(dev); if (ret) goto err_free_dev; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 9567ff883b28..9efc251cb745 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -312,6 +312,7 @@ struct mt792x_dev { bool hif_idle:1; bool hif_resumed:1; bool regd_change:1; + bool skip_wpdma_reinit:1; wait_queue_head_t wait; struct work_struct init_work; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index 8ad94fa58340..e74b95fdc767 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -418,6 +418,9 @@ int mt792x_wpdma_reinit_cond(struct mt792x_dev *dev) struct mt76_connac_pm *pm = &dev->pm; int err; + if (dev->skip_wpdma_reinit) + return 0; + /* check if the wpdma must be reinitialized */ if (mt792x_dma_need_reinit(dev)) { /* disable interrutpts */ From f0be077b7b66aa4e01b07b8c67ee1f347fe9c9f7 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Thu, 23 Jul 2026 21:04:30 +0300 Subject: [PATCH 341/403] wifi: mt76: mt7915: simplify mt7915_sys_recovery_set() Use convenient ' kstrtou16_from_user()' to simplify 'mt7915_sys_recovery_set()'. Signed-off-by: Dmitry Antipov Link: https://patch.msgid.link/20260723180430.747789-1-dmantipov@yandex.ru Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/debugfs.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 4d0854fe785b..0413b4fd2d9c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -52,23 +52,12 @@ mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, struct mt7915_phy *phy = file->private_data; struct mt7915_dev *dev = phy->dev; bool band = phy->mt76->band_idx; - char buf[16]; int ret = 0; u16 val; - if (count >= sizeof(buf)) - return -EINVAL; - - if (copy_from_user(buf, user_buf, count)) - return -EFAULT; - - if (count && buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - buf[count] = '\0'; - - if (kstrtou16(buf, 0, &val)) - return -EINVAL; + ret = kstrtou16_from_user(user_buf, count, 0, &val); + if (ret) + return ret; switch (val) { /* From 404c4e564f6b1eeffd10bf2b2d3b86620f5794c3 Mon Sep 17 00:00:00 2001 From: "shengwei.lu" Date: Thu, 23 Jul 2026 11:11:08 +0800 Subject: [PATCH 342/403] wifi: mt76: mt7925: Fix EHT Beamformee SS subfields to meet 802.11be minimum Per IEEE 802.11be, the Beamformee SS <= 80/160/320 MHz 3-bit subfields in the EHT PHY Capabilities are encoded as (Nss - 1) and are required to be >= 3 (i.e. at least 4 SS receive capability) whenever SU Beamformee is advertised. MT7925 is a 2x2 STA (sts = 2), so directly filling (sts - 1) = 1 violates the spec minimum. Clamp the encoded value to 3 when sts <= 3, otherwise use (sts - 1). This is applied consistently to the BEAMFORMEE_SS <= 80 MHz (split across phy_cap_info[0]/[1]), <= 160 MHz and <= 320 MHz (6 GHz only) subfields. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: shengwei.lu Link: https://patch.msgid.link/20260723031108.2017653-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 475a580b348d..84b55f008b3d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -188,19 +188,21 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band, eht_cap_elem->phy_cap_info[0] |= IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; + val = (sts > 3) ? sts - 1 : 3; + eht_cap_elem->phy_cap_info[0] |= - u8_encode_bits(u8_get_bits(sts - 1, BIT(0)), + u8_encode_bits(u8_get_bits(val, BIT(0)), IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK); eht_cap_elem->phy_cap_info[1] = - u8_encode_bits(u8_get_bits(sts - 1, GENMASK(2, 1)), + u8_encode_bits(u8_get_bits(val, GENMASK(2, 1)), IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK) | - u8_encode_bits(sts - 1, + u8_encode_bits(val, IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK); if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) eht_cap_elem->phy_cap_info[1] |= - u8_encode_bits(sts - 1, + u8_encode_bits(val, IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK); eht_cap_elem->phy_cap_info[2] = From a547b414bf7ea9877456b5630573293cc0e2cae7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 1 Aug 2026 14:53:25 +0000 Subject: [PATCH 343/403] wifi: mt76: add PS buffering support for HW-managed TIM drivers Add MT_DRV_HW_PS_BUFFERING flag for drivers where firmware controls the TIM bit based on buffered frames. Instead of blocking all TX to PS stations (which starves firmware and prevents TIM from being set), allow limited frame delivery using AQL pending airtime as the throttle. Replenish the firmware buffer on TX completion. Add mt76_sta_ps_transition() helper for drivers to call from MCU PS sync events. Link: https://patch.msgid.link/20260801145334.1166751-1-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76.h | 3 + .../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 + drivers/net/wireless/mediatek/mt76/tx.c | 77 +++++++++++++++++-- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 640061276d76..dc160d9ae537 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -539,6 +539,7 @@ struct mt76_hw_cap { #define MT_DRV_HW_MGMT_TXQ BIT(4) #define MT_DRV_AMSDU_OFFLOAD BIT(5) #define MT_DRV_IGNORE_TXS_FAILED BIT(6) +#define MT_DRV_HW_PS_BUFFERING BIT(7) struct mt76_driver_ops { u32 drv_flags; @@ -1541,6 +1542,8 @@ void mt76_release_buffered_frames(struct ieee80211_hw *hw, u16 tids, int nframes, enum ieee80211_frame_release_type reason, bool more_data); +void mt76_sta_ps_transition(struct mt76_dev *dev, struct mt76_wcid *wcid, + bool ps); bool mt76_has_tx_pending(struct mt76_phy *phy); int mt76_update_channel(struct mt76_phy *phy); void mt76_update_survey(struct mt76_phy *phy); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 51380849d24e..0ededa569e9a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1094,6 +1094,7 @@ enum { MCU_UNI_EVENT_IE_COUNTDOWN = 0x09, MCU_UNI_EVENT_COREDUMP = 0x0a, MCU_UNI_EVENT_BSS_BEACON_LOSS = 0x0c, + MCU_UNI_EVENT_PS_SYNC = 0x0d, MCU_UNI_EVENT_SCAN_DONE = 0x0e, MCU_UNI_EVENT_RDD_REPORT = 0x11, MCU_UNI_EVENT_ROC = 0x27, diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index b03be0eb4712..12c615b1294b 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -267,6 +267,21 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff * wcid = __mt76_wcid_ptr(dev, wcid_idx); mt76_tx_check_non_aql(dev, wcid, skb); + if (wcid && (dev->drv->drv_flags & MT_DRV_HW_PS_BUFFERING) && + test_bit(MT_WCID_FLAG_PS, &wcid->flags)) { + struct ieee80211_sta *sta = wcid_to_sta(wcid); + + if (sta) { + struct ieee80211_hw *hw = mt76_phy_hw(dev, wcid->phy_idx); + int i; + + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) + if (sta->txq[i]) + ieee80211_schedule_txq(hw, sta->txq[i]); + mt76_worker_schedule(&dev->tx_worker); + } + } + #ifdef CONFIG_NL80211_TESTMODE if (mt76_is_testmode_skb(dev, skb, &hw)) { struct mt76_phy *phy = hw->priv; @@ -476,8 +491,12 @@ mt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q, bool stop = false; int idx; - if (test_bit(MT_WCID_FLAG_PS, &wcid->flags)) - return 0; + if (test_bit(MT_WCID_FLAG_PS, &wcid->flags)) { + if (!(dev->drv->drv_flags & MT_DRV_HW_PS_BUFFERING)) + return 0; + if (ieee80211_txq_aql_pending(phy->hw, txq)) + return 0; + } if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT) return 0; @@ -497,6 +516,9 @@ mt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q, if (idx < 0) return idx; + if (test_bit(MT_WCID_FLAG_PS, &wcid->flags)) + goto out; + do { if (test_bit(MT76_RESET, &phy->state) || phy->offchannel) break; @@ -522,6 +544,7 @@ mt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q, n_frames++; } while (1); +out: spin_lock(&q->lock); dev->queue_ops->kick(dev, q); spin_unlock(&q->lock); @@ -548,10 +571,7 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid) mtxq = (struct mt76_txq *)txq->drv_priv; wcid = __mt76_wcid_ptr(dev, mtxq->wcid); - if (!wcid || test_bit(MT_WCID_FLAG_PS, &wcid->flags)) - continue; - - if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT) + if (!wcid) continue; phy = mt76_dev_phy(dev, wcid->phy_idx); @@ -559,6 +579,24 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid) continue; q = phy->q_tx[qid]; + + if (test_bit(MT_WCID_FLAG_PS, &wcid->flags)) { + if (!(dev->drv->drv_flags & MT_DRV_HW_PS_BUFFERING)) + continue; + + if (!mt76_txq_stopped(q)) + n_frames = mt76_txq_send_burst(phy, q, mtxq, wcid); + + ieee80211_return_txq(phy->hw, txq, false); + + if (unlikely(n_frames < 0)) + return n_frames; + ret += n_frames; + continue; + } + + if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT) + continue; if (dev->queue_ops->tx_cleanup && q->queued + 2 * MT_TXQ_FREE_THR >= q->ndesc) { dev->queue_ops->tx_cleanup(dev, q, false); @@ -765,6 +803,33 @@ void mt76_stop_tx_queues(struct mt76_phy *phy, struct ieee80211_sta *sta, } EXPORT_SYMBOL_GPL(mt76_stop_tx_queues); +void mt76_sta_ps_transition(struct mt76_dev *dev, struct mt76_wcid *wcid, + bool ps) +{ + struct ieee80211_sta *sta; + struct ieee80211_hw *hw; + int i; + + if (ps) { + set_bit(MT_WCID_FLAG_PS, &wcid->flags); + mt76_worker_schedule(&dev->tx_worker); + return; + } + + clear_bit(MT_WCID_FLAG_PS, &wcid->flags); + + sta = wcid_to_sta(wcid); + if (!sta) + return; + + hw = mt76_phy_hw(dev, wcid->phy_idx); + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) + if (sta->txq[i]) + ieee80211_schedule_txq(hw, sta->txq[i]); + mt76_worker_schedule(&dev->tx_worker); +} +EXPORT_SYMBOL_GPL(mt76_sta_ps_transition); + void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) { struct mt76_phy *phy = hw->priv; From dc930cf883c81799f11f4da33a3711cad983de24 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 1 Aug 2026 14:53:26 +0000 Subject: [PATCH 344/403] wifi: mt76: mt7915: handle MCU PS sync events Enable MT_DRV_HW_PS_BUFFERING and handle MCU_EXT_EVENT_PS_SYNC to track station power-save state via firmware notifications. Link: https://patch.msgid.link/20260801145334.1166751-2-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 18 ++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7915/mcu.h | 9 +++++++++ .../net/wireless/mediatek/mt76/mt7915/mmio.c | 3 ++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 88955aed62e2..5bfe0366562f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -402,6 +402,21 @@ mt7915_mcu_rx_bcc_notify(struct mt7915_dev *dev, struct sk_buff *skb) mt7915_mcu_cca_finish, mphy->hw); } +static void +mt7915_mcu_rx_ps_sync(struct mt7915_dev *dev, struct sk_buff *skb) +{ + struct mt7915_mcu_ps_notify *p = (void *)skb->data; + struct mt76_wcid *wcid; + u16 wcid_idx; + + wcid_idx = p->wtbl_lower | (p->wtbl_higher << 8); + wcid = mt76_wcid_ptr(dev, wcid_idx); + if (!wcid || !wcid_to_sta(wcid)) + return; + + mt76_sta_ps_transition(&dev->mt76, wcid, !!p->ps_bit); +} + static void mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb) { @@ -424,6 +439,9 @@ mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb) case MCU_EXT_EVENT_BCC_NOTIFY: mt7915_mcu_rx_bcc_notify(dev, skb); break; + case MCU_EXT_EVENT_PS_SYNC: + mt7915_mcu_rx_ps_sync(dev, skb); + break; default: break; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 7c472062a90e..76e39e899797 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -54,6 +54,15 @@ struct mt7915_mcu_bcc_notify { u8 rsv; } __packed; +struct mt7915_mcu_ps_notify { + struct mt76_connac2_mcu_rxd_hdr rxd; + + u8 wtbl_lower; + u8 ps_bit; + u8 wtbl_higher; + u8 rsv; +} __packed; + struct mt7915_mcu_rdd_report { struct mt76_connac2_mcu_rxd_hdr rxd; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 2708b1556f40..28377c964843 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -921,7 +921,8 @@ struct mt7915_dev *mt7915_mmio_probe(struct device *pdev, /* txwi_size = txd size + txp size */ .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_fw_txp), .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ | - MT_DRV_AMSDU_OFFLOAD, + MT_DRV_AMSDU_OFFLOAD | + MT_DRV_HW_PS_BUFFERING, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, From 2d16caccd278bdad4299a7093104f29ac73ebed2 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 1 Aug 2026 14:53:27 +0000 Subject: [PATCH 345/403] wifi: mt76: mt7996: handle UNI PS sync events Enable MT_DRV_HW_PS_BUFFERING and handle MCU_UNI_EVENT_PS_SYNC with all three TLV formats (single client, multi-client packed entries, and bitmap). Link: https://patch.msgid.link/20260801145334.1166751-3-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 86 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7996/mcu.h | 33 +++++++ .../net/wireless/mediatek/mt76/mt7996/mmio.c | 3 +- 3 files changed, 121 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 8aab91810135..cd7587ce5b8a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -826,6 +826,89 @@ mt7996_mcu_wed_rro_event(struct mt7996_dev *dev, struct sk_buff *skb) } } +static void +mt7996_mcu_ps_transition(struct mt7996_dev *dev, u16 wcid_idx, bool ps) +{ + struct mt76_wcid *wcid; + + wcid = mt76_wcid_ptr(dev, wcid_idx); + if (!wcid || !wcid_to_sta(wcid)) + return; + + mt76_sta_ps_transition(&dev->mt76, wcid, ps); +} + +static void +mt7996_mcu_rx_ps_sync(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_ps_sync_event *event = (void *)skb->data; + struct tlv *tlv; + int len; + + if (skb->len < sizeof(*event)) + return; + + skb_pull(skb, sizeof(*event)); + + len = skb->len; + while (len >= sizeof(*tlv)) { + u16 tag, tag_len; + + tlv = (struct tlv *)skb->data; + tag = le16_to_cpu(tlv->tag); + tag_len = le16_to_cpu(tlv->len); + /* a tag_len below the header size would not advance the buffer */ + if (tag_len < sizeof(*tlv) || tag_len > len) + break; + + switch (tag) { + case UNI_PS_CLIENT_INFO: { + struct mt7996_mcu_ps_client_info *info = (void *)tlv; + + if (tag_len < sizeof(*info)) + break; + + mt7996_mcu_ps_transition(dev, + le16_to_cpu(info->wlan_idx), + info->ps_bit); + break; + } + case UNI_PS_MULTI_CLIENT_INFO: { + struct mt7996_mcu_ps_multi_client_info *info = (void *)tlv; + u16 cnt; + int i; + + if (tag_len < sizeof(*info)) + break; + + cnt = min_t(u16, le16_to_cpu(info->sta_cnt), + (tag_len - sizeof(*info)) / sizeof(info->sta_ps_info[0])); + for (i = 0; i < cnt; i++) { + u16 entry = le16_to_cpu(info->sta_ps_info[i]); + + mt7996_mcu_ps_transition(dev, + FIELD_GET(MT7996_PS_MULTI_WCID, entry), + !!(entry & MT7996_PS_MULTI_PS_BIT)); + } + break; + } + case UNI_PS_MULTI_CLIENT_INFO_BITMAP: { + u8 *bitmap = tlv->data; + int bitmap_len = tag_len - sizeof(*tlv); + int i; + + for (i = 0; i < bitmap_len * 8; i++) + mt7996_mcu_ps_transition(dev, i, + !!(bitmap[i / 8] & BIT(i % 8))); + break; + } + } + + skb_pull(skb, tag_len); + len -= tag_len; + } +} + static void mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb) { @@ -847,6 +930,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb) case MCU_UNI_EVENT_WED_RRO: mt7996_mcu_wed_rro_event(dev, skb); break; + case MCU_UNI_EVENT_PS_SYNC: + mt7996_mcu_rx_ps_sync(dev, skb); + break; default: break; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index 1487d33a8dbb..74b70fb6da3d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -293,6 +293,39 @@ enum { UNI_WED_RRO_BA_SESSION_DELETE, }; +struct mt7996_mcu_ps_sync_event { + struct mt7996_mcu_rxd rxd; + + u8 bss_idx; + u8 __rsv[3]; +} __packed; + +struct mt7996_mcu_ps_client_info { + __le16 tag; + __le16 len; + u8 ps_bit; + u8 __rsv; + __le16 wlan_idx; + u8 buffer_size; + u8 __rsv2[3]; +} __packed; + +struct mt7996_mcu_ps_multi_client_info { + __le16 tag; + __le16 len; + __le16 sta_cnt; + __le16 sta_ps_info[]; +} __packed; + +#define MT7996_PS_MULTI_WCID GENMASK(10, 0) +#define MT7996_PS_MULTI_PS_BIT BIT(15) + +enum { + UNI_PS_CLIENT_INFO = 0, + UNI_PS_MULTI_CLIENT_INFO = 1, + UNI_PS_MULTI_CLIENT_INFO_BITMAP = 2, +}; + struct mt7996_mcu_thermal_notify { struct mt7996_mcu_rxd rxd; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index ac81be5fe023..fbfbb96a742f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -838,7 +838,8 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, .link_data_size = sizeof(struct mt7996_vif_link), .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_AMSDU_OFFLOAD | - MT_DRV_HW_MGMT_TXQ, + MT_DRV_HW_MGMT_TXQ | + MT_DRV_HW_PS_BUFFERING, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, From 0b8d2524716099ea4743fa65bc364f94b3106a31 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 1 Aug 2026 14:53:28 +0000 Subject: [PATCH 346/403] wifi: mt76: set the EOSP bit in the QoS header of the last released frame When the driver implements .release_buffered_frames, mac80211 leaves the U-APSD signalling entirely to the driver: "In this case it is also responsible for setting the EOSP flag in the QoS header of the frames" (include/net/mac80211.h). Only IEEE80211_TX_STATUS_EOSP was being set, which merely ends the service period inside mac80211, so on air the service period was never terminated. Clients that wait for EOSP before going back to doze keep the SP open and stop triggering, which stalls all downlink traffic for that station. Set the wire EOSP bit on the last frame of a U-APSD service period. EOSP has no meaning for a PS-Poll response, so pass the release reason down and leave those frames alone. Link: https://patch.msgid.link/20260801145334.1166751-4-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/tx.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index 12c615b1294b..3707ee19e4ae 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -412,16 +412,23 @@ mt76_txq_dequeue(struct mt76_phy *phy, struct mt76_txq *mtxq) static void mt76_queue_ps_skb(struct mt76_phy *phy, struct ieee80211_sta *sta, - struct sk_buff *skb, bool last) + struct sk_buff *skb, bool last, + enum ieee80211_frame_release_type reason) { struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE; - if (last) + if (last) { info->flags |= IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; + if (reason == IEEE80211_FRAME_RELEASE_UAPSD && + ieee80211_is_data_qos(hdr->frame_control)) + *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_EOSP; + } + mt76_skb_set_moredata(skb, !last); __mt76_tx_queue_skb(phy, MT_TXQ_PSD, skb, wcid, sta, NULL); } @@ -454,14 +461,15 @@ mt76_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta, nframes--; if (last_skb) - mt76_queue_ps_skb(phy, sta, last_skb, false); + mt76_queue_ps_skb(phy, sta, last_skb, false, + reason); last_skb = skb; } while (nframes); } if (last_skb) { - mt76_queue_ps_skb(phy, sta, last_skb, true); + mt76_queue_ps_skb(phy, sta, last_skb, true, reason); dev->queue_ops->kick(dev, hwq); } else { ieee80211_sta_eosp(sta); From 697badc27a9b3b2a8d86092abeed767714f735a2 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 1 Aug 2026 14:53:29 +0000 Subject: [PATCH 347/403] wifi: mt76: mt7603: fix U-APSD service period termination Frames released from the driver PS queue were all tagged with MORE_DATA and none of them ever carried the EOSP bit, so from the client's point of view a U-APSD service period was started but never finished. Clients that keep their receiver on until EOSP arrives stop sending trigger frames, and all downlink traffic for that station stalls until they give up. ieee80211_sta_eosp() only cleared the service period state inside mac80211, which is why the mismatch went unnoticed. Assign MORE_DATA per frame and set the wire EOSP bit on the last one. If the last released frame is a bufferable MMPDU it has no QoS control field to carry EOSP, so let mac80211 append a QoS-Null frame instead. Also stop handing the remaining frame budget to mt76_release_buffered_frames() once frames have been released from the PS queue: both would signal the end of the same service period. Releasing fewer frames than requested is allowed, and MORE_DATA tells the client to trigger again. Link: https://patch.msgid.link/20260801145334.1166751-5-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7603/main.c | 76 ++++++++++++++++--- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c index 0f3a7508996c..7c231995392b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c @@ -423,13 +423,33 @@ mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps) mt7603_ps_tx_list(dev, &list); } -static void -mt7603_ps_set_more_data(struct sk_buff *skb) +static struct ieee80211_hdr * +mt7603_ps_skb_hdr(struct sk_buff *skb) { - struct ieee80211_hdr *hdr; + return (struct ieee80211_hdr *)&skb->data[MT_TXD_SIZE]; +} - hdr = (struct ieee80211_hdr *)&skb->data[MT_TXD_SIZE]; - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); +/* + * Buffered frames can be recycled into the PS queue by mt7603_filter_tx(), so + * both bits have to be assigned, not just set. + */ +static void +mt7603_ps_set_flags(struct sk_buff *skb, bool more_data, bool eosp) +{ + struct ieee80211_hdr *hdr = mt7603_ps_skb_hdr(skb); + + if (more_data) + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + else + hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_MOREDATA); + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return; + + if (eosp) + *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_EOSP; + else + *ieee80211_get_qos_ctl(hdr) &= ~IEEE80211_QOS_CTL_EOSP; } static void @@ -442,7 +462,10 @@ mt7603_release_buffered_frames(struct ieee80211_hw *hw, struct mt7603_dev *dev = hw->priv; struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; struct sk_buff_head list; - struct sk_buff *skb, *tmp; + struct sk_buff *skb, *tmp, *last; + bool eosp_null, uapsd; + u16 pending = 0; + u8 last_tid; __skb_queue_head_init(&list); @@ -458,20 +481,49 @@ mt7603_release_buffered_frames(struct ieee80211_hw *hw, skb_set_queue_mapping(skb, MT_TXQ_PSD); __skb_unlink(skb, &msta->psq); - mt7603_ps_set_more_data(skb); __skb_queue_tail(&list, skb); nframes--; } + + skb_queue_walk(&msta->psq, skb) + pending |= BIT(skb->priority); spin_unlock_bh(&dev->ps_lock); - if (!skb_queue_empty(&list)) - ieee80211_sta_eosp(sta); + last = skb_peek_tail(&list); + if (!last) { + mt76_release_buffered_frames(hw, sta, tids, nframes, reason, + more_data); + return; + } + + /* + * End the service period here instead of passing the remaining frame + * budget on to mt76_release_buffered_frames(), which would signal the + * end of the same service period a second time. + */ + uapsd = reason == IEEE80211_FRAME_RELEASE_UAPSD; + more_data |= !!(pending & tids); + + skb_queue_walk(&list, skb) + mt7603_ps_set_flags(skb, skb != last || more_data, + skb == last && uapsd); + + /* + * EOSP lives in the QoS control field, so a bufferable MMPDU cannot + * terminate a U-APSD service period on its own. In that case mac80211 + * has to append a QoS-Null frame, which ends the SP through its tx + * status. + */ + eosp_null = uapsd && + !ieee80211_is_data_qos(mt7603_ps_skb_hdr(last)->frame_control); + last_tid = __fls(tids); mt7603_ps_tx_list(dev, &list); - if (nframes) - mt76_release_buffered_frames(hw, sta, tids, nframes, reason, - more_data); + if (eosp_null) + ieee80211_send_eosp_nullfunc(sta, last_tid); + else + ieee80211_sta_eosp(sta); } static int From d5001f493fefdefd7322839384625f2d73812f93 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 1 Aug 2026 14:53:30 +0000 Subject: [PATCH 348/403] wifi: mt76: mt7603: tell mac80211 when the PS queue has run empty mt7603_rx_loopback_skb() marks a TID as buffered when the hardware redirects a frame into the driver PS queue, but nothing ever clears that state again. mac80211 therefore keeps the TIM bit set for the station and keeps routing every service period to the driver, which also prevents it from releasing frames it has buffered itself. Clear the buffered state for every requested TID that has no frames left in the PS queue. Link: https://patch.msgid.link/20260801145334.1166751-6-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7603/main.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c index 7c231995392b..c968ee3d591a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c @@ -464,8 +464,10 @@ mt7603_release_buffered_frames(struct ieee80211_hw *hw, struct sk_buff_head list; struct sk_buff *skb, *tmp, *last; bool eosp_null, uapsd; + unsigned long drained; u16 pending = 0; u8 last_tid; + int i; __skb_queue_head_init(&list); @@ -489,6 +491,15 @@ mt7603_release_buffered_frames(struct ieee80211_hw *hw, pending |= BIT(skb->priority); spin_unlock_bh(&dev->ps_lock); + /* + * Without this, mac80211 keeps the TIM bit set for the station and + * keeps routing every service period to the driver, even though there + * is nothing left to release. + */ + drained = tids & ~pending; + for_each_set_bit(i, &drained, IEEE80211_NUM_TIDS) + ieee80211_sta_set_buffered(sta, i, false); + last = skb_peek_tail(&list); if (!last) { mt76_release_buffered_frames(hw, sta, tids, nframes, reason, From 636a883e2feef5e4ff95695d3c372ceff03960f5 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 1 Aug 2026 14:53:31 +0000 Subject: [PATCH 349/403] wifi: mt76: mt7603: restore hardware PS buffering after a service period Releasing buffered frames has to turn off the PSE redirect for the station, otherwise the released frames are looped straight back into the driver PS queue. Nothing ever turns it back on: mt7603_sta_ps() only runs on an observed PM bit transition, and MT_WCID_FLAG_PS keeps mt76 from reporting the same state twice. After the first service period the hardware therefore treats a dozing station as awake and transmits at it directly, which is where the retry storms and the packet loss reported against U-APSD clients come from. Re-arm hardware buffering from mt7603_mac_work() for every station that is still known to be asleep, once the PSD queue has drained and the released frames have passed the redirect stage. Frames that are still queued belong to the service period that was just served, so unlike on a sleep transition they must not be pulled back with mt7603_filter_tx(). Track the sleep state separately from the WTBL state, so a station that wakes up while the re-arm is pending is not put back to sleep. Link: https://patch.msgid.link/20260801145334.1166751-7-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7603/mac.c | 73 +++++++++++++++++-- .../net/wireless/mediatek/mt76/mt7603/main.c | 2 +- .../wireless/mediatek/mt76/mt7603/mt7603.h | 4 + 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c index d3110eeb45d7..fa0bf9a201ce 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c @@ -235,16 +235,17 @@ void mt7603_wtbl_set_smps(struct mt7603_dev *dev, struct mt7603_sta *sta, sta->smps = enabled; } -void mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, - bool enabled) +static void +__mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool enabled, bool filter) { int idx = sta->wcid.idx; u32 addr; - spin_lock_bh(&dev->ps_lock); + lockdep_assert_held(&dev->ps_lock); if (sta->ps == enabled) - goto out; + return; mt76_wr(dev, MT_PSE_RTA, FIELD_PREP(MT_PSE_RTA_TAG_ID, idx) | @@ -255,7 +256,7 @@ void mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, mt76_poll(dev, MT_PSE_RTA, MT_PSE_RTA_BUSY, 0, 5000); - if (enabled) + if (enabled && filter) mt7603_filter_tx(dev, sta->vif->idx, idx, false); addr = mt7603_wtbl1_addr(idx); @@ -264,8 +265,34 @@ void mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, enabled * MT_WTBL1_W3_POWER_SAVE); mt76_clear(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); sta->ps = enabled; +} -out: +void mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool enabled) +{ + spin_lock_bh(&dev->ps_lock); + __mt7603_wtbl_set_ps(dev, sta, enabled, enabled); + spin_unlock_bh(&dev->ps_lock); +} + +void mt7603_wtbl_sta_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, bool ps) +{ + spin_lock_bh(&dev->ps_lock); + sta->ps_sleeping = ps; + __mt7603_wtbl_set_ps(dev, sta, ps, ps); + spin_unlock_bh(&dev->ps_lock); +} + +void mt7603_wtbl_restore_ps(struct mt7603_dev *dev, struct mt7603_sta *sta) +{ + spin_lock_bh(&dev->ps_lock); + /* + * Frames that are already queued for the station belong to the service + * period that has just been served, so unlike on a sleep transition + * they must not be pulled back into the PS queue. + */ + if (sta->ps_sleeping) + __mt7603_wtbl_set_ps(dev, sta, true, false); spin_unlock_bh(&dev->ps_lock); } @@ -1816,6 +1843,39 @@ mt7603_false_cca_check(struct mt7603_dev *dev) mt7603_adjust_sensitivity(dev); } +/* + * Releasing buffered frames turns off the PSE redirect for a station, since + * the released frames would otherwise be looped back into the driver PS queue + * again. mac80211 never tells us when the service period is over, so hardware + * buffering has to be re-armed here for every station that is still known to + * be asleep. Waiting for the PSD queue to drain makes sure that the released + * frames have already passed the redirect stage. + */ +static void +mt7603_mac_ps_check(struct mt7603_dev *dev) +{ + int i; + + if (dev->mphy.q_tx[MT_TXQ_PSD]->queued) + return; + + rcu_read_lock(); + for (i = 0; i < MT7603_WTBL_STA; i++) { + struct mt76_wcid *wcid = mt76_wcid_ptr(dev, i); + struct mt7603_sta *msta; + + if (!wcid || !wcid->sta) + continue; + + msta = container_of(wcid, struct mt7603_sta, wcid); + if (msta->ps || !msta->ps_sleeping) + continue; + + mt7603_wtbl_restore_ps(dev, msta); + } + rcu_read_unlock(); +} + void mt7603_mac_work(struct work_struct *work) { struct mt7603_dev *dev = container_of(work, struct mt7603_dev, @@ -1830,6 +1890,7 @@ void mt7603_mac_work(struct work_struct *work) dev->mphy.mac_work_count++; mt76_update_survey(&dev->mphy); mt7603_edcca_check(dev); + mt7603_mac_ps_check(dev); for (i = 0, idx = 0; i < 2; i++) { u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i)); diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c index c968ee3d591a..757664e9e1f2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c @@ -410,7 +410,7 @@ mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps) struct sk_buff_head list; mt76_stop_tx_queues(&dev->mphy, sta, true); - mt7603_wtbl_set_ps(dev, msta, ps); + mt7603_wtbl_sta_ps(dev, msta, ps); if (ps) return; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h index 071bfab3af7c..5b0ecd01aad9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h @@ -80,6 +80,7 @@ struct mt7603_sta { u8 smps; u8 ps; + u8 ps_sleeping; }; struct mt7603_vif { @@ -229,6 +230,9 @@ int mt7603_wtbl_set_key(struct mt7603_dev *dev, int wcid, struct ieee80211_key_conf *key); void mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, bool enabled); +void mt7603_wtbl_sta_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool ps); +void mt7603_wtbl_restore_ps(struct mt7603_dev *dev, struct mt7603_sta *sta); void mt7603_wtbl_set_smps(struct mt7603_dev *dev, struct mt7603_sta *sta, bool enabled); void mt7603_filter_tx(struct mt7603_dev *dev, int mac_idx, int idx, bool abort); From 0bbd6c52dfff62b319963f54cd9370f9486e59d2 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 1 Aug 2026 14:53:32 +0000 Subject: [PATCH 350/403] wifi: mt76: mt7603: file buffered frames under the TID reported to mac80211 The PS queue filed redirected frames under the TID taken from the TXD, while mac80211 was told about the TID taken from the QoS header, or TID 0 for anything that is not a QoS data frame. When the two disagree, mt7603_release_buffered_frames() skips the frame because it does not match the requested TIDs, so it stays in the PS queue until the station wakes up. Buffered MMPDUs hit this whenever they were sent on a non-zero TID. Use one TID for both. Link: https://patch.msgid.link/20260801145334.1166751-8-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7603/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c index 3a16851524a0..477a9b7a8cf7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c @@ -39,7 +39,6 @@ mt7603_rx_loopback_skb(struct mt7603_dev *dev, struct sk_buff *skb) val = le32_to_cpu(txd[1]); idx = FIELD_GET(MT_TXD1_WLAN_IDX, val); - skb->priority = FIELD_GET(MT_TXD1_TID, val); if (idx >= MT7603_WTBL_STA - 1) goto free; @@ -72,6 +71,7 @@ mt7603_rx_loopback_skb(struct mt7603_dev *dev, struct sk_buff *skb) hwq = MT_TX_HW_QUEUE_MGMT; } + skb->priority = tid; ieee80211_sta_set_buffered(sta, tid, true); val = le32_to_cpu(txd[0]); From 9ba744a28c26eaa5cae930688a22e01888395308 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 1 Aug 2026 14:53:33 +0000 Subject: [PATCH 351/403] wifi: mt76: reject out-of-range link ids in mt76_vif_link() mt76_vif_link() indexes mvif->link[] without validating link_id, but callers pass mvif->deflink_id / msta->deflink_id, which hold IEEE80211_LINK_UNSPECIFIED (0xf) until the first link has been added. Since IEEE80211_MLD_MAX_NUM_LINKS is 15, that reads one element past the end of the array, aliasing mt76_vif_data.offchannel_link. Reachable via mt7996_set_tsf()/mt7996_offset_tsf() and mt7996_net_fill_forward_path(). Bounds check link_id and return NULL, matching mt7996_sta_link() and mt7996_sta_link_protected(). Fixes: a9384b36a42a ("wifi: mt76: mt7996: rework set/get_tsf callabcks to support MLO") Link: https://patch.msgid.link/20260801145334.1166751-9-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index dc160d9ae537..61ef1b80feb8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -2131,6 +2131,9 @@ mt76_vif_link(struct mt76_dev *dev, struct ieee80211_vif *vif, int link_id) if (!link_id) return mlink; + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + return mt76_dereference(mvif->link[link_id], dev); } From 4330a0ef9f75a54fde3548432a9a698f06bab635 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 1 Aug 2026 14:53:34 +0000 Subject: [PATCH 352/403] wifi: mt76: mt7996: fix out-of-bounds link array access in mt7996_tx() When mac80211 leaves the link unspecified, mt7996_tx() substitutes the primary link id of the station or vif. That value is IEEE80211_LINK_UNSPECIFIED (0xf) until the first link has been added, and it is then used unchecked to index vif->link_conf[], mvif->mt76.link[] and sta->link[], all of which hold IEEE80211_MLD_MAX_NUM_LINKS (15) entries. Clamp the primary link id to the default link before using it, and use the clamped value for the link_sta fallback as well. Fixes: 1609b014aa29 ("wifi: mt76: mt7996: Overwrite unspecified link_id in mt7996_tx()") Link: https://patch.msgid.link/20260801145334.1166751-10-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/main.c | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 54e79bd25995..e218856b0c45 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1515,20 +1515,26 @@ static void mt7996_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif = info->control.vif; struct mt7996_vif *mvif = vif ? (void *)vif->drv_priv : NULL; struct mt76_wcid *wcid = &dev->mt76.global_wcid; + u8 deflink_id = IEEE80211_LINK_UNSPECIFIED; u8 link_id = u32_get_bits(info->control.flags, IEEE80211_TX_CTRL_MLO_LINK); rcu_read_lock(); + if (msta) + deflink_id = msta->deflink_id; + else if (mvif) + deflink_id = mvif->mt76.deflink_id; + + /* the primary link is unset until the first link has been added */ + if (deflink_id >= IEEE80211_MLD_MAX_NUM_LINKS) + deflink_id = 0; + /* Use primary link_id if the value from mac80211 is set to * IEEE80211_LINK_UNSPECIFIED. */ - if (link_id == IEEE80211_LINK_UNSPECIFIED) { - if (msta) - link_id = msta->deflink_id; - else if (mvif) - link_id = mvif->mt76.deflink_id; - } + if (link_id == IEEE80211_LINK_UNSPECIFIED) + link_id = deflink_id; if (vif && ieee80211_vif_is_mld(vif)) { struct ieee80211_bss_conf *link_conf; @@ -1538,7 +1544,7 @@ static void mt7996_tx(struct ieee80211_hw *hw, link_sta = rcu_dereference(sta->link[link_id]); if (!link_sta) - link_sta = rcu_dereference(sta->link[msta->deflink_id]); + link_sta = rcu_dereference(sta->link[deflink_id]); if (link_sta) { memcpy(hdr->addr1, link_sta->addr, ETH_ALEN); From 1e33f8acd837420160ea088160d8648a3db54c3b Mon Sep 17 00:00:00 2001 From: Reshma Immaculate Rajkumar Date: Wed, 29 Jul 2026 22:47:32 +0530 Subject: [PATCH 353/403] wifi: ath12k: fix encrypted EAPOL TX in encap offload mode When a vif operates with IEEE80211_OFFLOAD_ENCAP_ENABLED, mac80211 delivers EAPOL frames to ath12k in native-WiFi format. Unencrypted EAPOL frames used during the initial 4-way handshake are already handled through the existing is_diff_encap path. However, EAPOL frames transmitted during GTK rekeying carry ATH12K_SKB_CIPHER_SET and continue through the normal native-WiFi transmit path. Firmware encryption requires RAW frames with cipher-specific IV and ICV fields correctly provisioned in the skb. Passing encrypted EAPOL frames in native-WiFi format results in incorrect IV provisioning, leading to an invalid ICV and frame drop. Fix this by detecting the EAPOL frames that need HW encryption and converting them to firmware-encrypted RAW frames before transmission. Reserve IV space after the MAC header, append ICV space at the tail, select the appropriate firmware encryption type and request firmware-side encryption. Introduce ath12k_dp_tx_crypto_iv_len() and ath12k_dp_tx_crypto_icv_len() helpers in the TX path to obtain cipher-specific IV and ICV lengths. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01270-QCAHKSWPL_SILICONZ-1 Fixes: d29591d5b52e ("wifi: ath12k: Advertise encapsulation/decapsulation offload support to mac80211") Signed-off-by: Reshma Immaculate Rajkumar Reviewed-by: Aishwarya R Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260729171732.668367-1-reshma.rajkumar@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_tx.c | 46 +++++++++++++ drivers/net/wireless/ath/ath12k/dp_tx.h | 2 + drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c | 64 ++++++++++++++++++- 3 files changed, 111 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index c10da6195c9c..9644f9ef2c74 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -82,6 +82,52 @@ enum hal_encrypt_type ath12k_dp_tx_get_encrypt_type(u32 cipher) } EXPORT_SYMBOL(ath12k_dp_tx_get_encrypt_type); +u8 ath12k_dp_tx_crypto_iv_len(enum hal_encrypt_type enc_type) +{ + switch (enc_type) { + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + return IEEE80211_TKIP_IV_LEN; + case HAL_ENCRYPT_TYPE_CCMP_128: + return IEEE80211_CCMP_HDR_LEN; + case HAL_ENCRYPT_TYPE_CCMP_256: + return IEEE80211_CCMP_256_HDR_LEN; + case HAL_ENCRYPT_TYPE_GCMP_128: + case HAL_ENCRYPT_TYPE_AES_GCMP_256: + return IEEE80211_GCMP_HDR_LEN; + case HAL_ENCRYPT_TYPE_WEP_40: + case HAL_ENCRYPT_TYPE_WEP_104: + case HAL_ENCRYPT_TYPE_WEP_128: + return IEEE80211_WEP_IV_LEN; + default: + return 0; + } +} +EXPORT_SYMBOL(ath12k_dp_tx_crypto_iv_len); + +u8 ath12k_dp_tx_crypto_icv_len(enum hal_encrypt_type enc_type) +{ + switch (enc_type) { + case HAL_ENCRYPT_TYPE_CCMP_128: + return IEEE80211_CCMP_MIC_LEN; + case HAL_ENCRYPT_TYPE_CCMP_256: + return IEEE80211_CCMP_256_MIC_LEN; + case HAL_ENCRYPT_TYPE_GCMP_128: + case HAL_ENCRYPT_TYPE_AES_GCMP_256: + return IEEE80211_GCMP_MIC_LEN; + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + return IEEE80211_TKIP_ICV_LEN; + case HAL_ENCRYPT_TYPE_WEP_40: + case HAL_ENCRYPT_TYPE_WEP_104: + case HAL_ENCRYPT_TYPE_WEP_128: + return IEEE80211_WEP_ICV_LEN; + default: + return 0; + } +} +EXPORT_SYMBOL(ath12k_dp_tx_crypto_icv_len); + void ath12k_dp_tx_release_txbuf(struct ath12k_dp *dp, struct ath12k_tx_desc_info *tx_desc, u8 pool_id) diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.h b/drivers/net/wireless/ath/ath12k/dp_tx.h index 7cef20540179..1af79af2ada2 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.h +++ b/drivers/net/wireless/ath/ath12k/dp_tx.h @@ -19,6 +19,8 @@ enum hal_tcl_encap_type ath12k_dp_tx_get_encap_type(struct ath12k_base *ab, struct sk_buff *skb); void ath12k_dp_tx_encap_nwifi(struct sk_buff *skb); u8 ath12k_dp_tx_get_tid(struct sk_buff *skb); +u8 ath12k_dp_tx_crypto_iv_len(enum hal_encrypt_type enc_type); +u8 ath12k_dp_tx_crypto_icv_len(enum hal_encrypt_type enc_type); void *ath12k_dp_metadata_align_skb(struct sk_buff *skb, u8 tail_len); int ath12k_dp_tx_align_payload(struct ath12k_dp *dp, struct sk_buff **pskb); void ath12k_dp_tx_release_txbuf(struct ath12k_dp *dp, diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index d2749de44553..587d58eeccfa 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -13,6 +13,49 @@ #include "hal.h" #include "hal_tx.h" +/* + * Convert an encrypted EAPOL frame from native-WiFi format to + * the layout expected by the firmware RAW encrypt pipeline: + * + * [802.11 hdr][IV (zeroed)][LLC/SNAP][EAPOL payload][ICV (zeroed)] + * + * mac80211 delivers the frame as [802.11 hdr][LLC/SNAP][EAPOL payload]. + * The MAC header length is read from the unmodified skb and is safe because + * ieee80211_hdrlen() only inspects the 2-byte frame_control field. + * pskb_expand_head() is used to grow both head (for the IV) and tail + * (for the ICV) in a single call and allocation. + */ +static int +ath12k_wifi7_dp_tx_encap_eapol(struct sk_buff *skb, + struct hal_tx_info *ti, + struct ath12k_skb_cb *skb_cb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + enum hal_encrypt_type enc_type = + ath12k_dp_tx_get_encrypt_type(skb_cb->cipher); + u16 mac_hdr_len = ieee80211_hdrlen(hdr->frame_control); + u8 iv_len = ath12k_dp_tx_crypto_iv_len(enc_type); + u8 icv_len = ath12k_dp_tx_crypto_icv_len(enc_type); + + if (pskb_expand_head(skb, iv_len, icv_len, GFP_ATOMIC)) + return -ENOMEM; + + if (iv_len) { + skb_push(skb, iv_len); + memmove(skb->data, skb->data + iv_len, mac_hdr_len); + memset(skb->data + mac_hdr_len, 0, iv_len); + } + + if (icv_len) + memset(skb_put(skb, icv_len), 0, icv_len); + + ti->flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW); + ti->encap_type = HAL_TCL_ENCAP_TYPE_RAW; + ti->encrypt_type = enc_type; + + return 0; +} + static void ath12k_wifi7_hal_tx_cmd_ext_desc_setup(struct ath12k_base *ab, struct hal_tx_msdu_ext_desc *tcl_ext_cmd, @@ -91,6 +134,7 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a u32 iova_mask = dp->hw_params->iova_mask; bool is_diff_encap = false; bool is_null_frame = false; + bool eapol_encap_done = false; if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags)) return -ESHUTDOWN; @@ -211,9 +255,27 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a case HAL_TCL_ENCAP_TYPE_NATIVE_WIFI: is_null_frame = ieee80211_is_nullfunc(hdr->frame_control); if (ahvif->vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) { - if (skb->protocol == cpu_to_be16(ETH_P_PAE) || is_null_frame) + if ((skb->protocol == cpu_to_be16(ETH_P_PAE) && + !(skb_cb->flags & ATH12K_SKB_CIPHER_SET)) || is_null_frame) is_diff_encap = true; + if (skb->protocol == cpu_to_be16(ETH_P_PAE) && + (skb_cb->flags & ATH12K_SKB_CIPHER_SET)) { + if (!eapol_encap_done) { + ret = ath12k_wifi7_dp_tx_encap_eapol(skb, &ti, + skb_cb); + if (ret) + goto fail_remove_tx_buf; + hdr = (void *)skb->data; + eapol_encap_done = true; + } else { + ti.flags0 |= u32_encode_bits(1, + HAL_TCL_DATA_CMD_INFO2_TO_FW); + ti.encap_type = HAL_TCL_ENCAP_TYPE_RAW; + ti.encrypt_type = + ath12k_dp_tx_get_encrypt_type(skb_cb->cipher); + } + } /* Firmware expects msdu ext descriptor for nwifi/raw packets * received in ETH mode. Without this, observed tx fail for * Multicast packets in ETH mode. From 35a3da9fe1b212a9012952a04f383b8dc3708dd7 Mon Sep 17 00:00:00 2001 From: Linghui Wu Date: Thu, 30 Jul 2026 08:02:26 +0530 Subject: [PATCH 354/403] wifi: ath10k: filter non-UTF testmode events When UTF monitor is enabled, ath10k forwards WMI events to nl80211 testmode. Non-UTF events can therefore be delivered to userspace and confuse FTM tools which expect only UTF responses. Only forward known UTF event IDs from WMI event namespaces that route events through ath10k_tm_event_wmi(), and drop other WMI events while UTF monitor is active. READY events are still handled by the normal WMI receive path. Tested-on: WCN3990 hw1.0 SNOC WLAN.HL.3.3.7.c5-00093.2-QCAHLSWMTPL-1 Signed-off-by: Linghui Wu Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260730023226.707008-1-linghui.wu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath10k/testmode.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index d3bd385694d6..282ae6e20c8e 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -156,6 +156,14 @@ static void ath10k_tm_event_segmented(struct ath10k *ar, u32 cmd_id, struct sk_b cfg80211_testmode_event(nl_skb, GFP_ATOMIC); } +static bool ath10k_tm_is_utf_event(u32 cmd_id) +{ + return cmd_id == WMI_10X_PDEV_UTF_EVENTID || + cmd_id == WMI_10_2_PDEV_UTF_EVENTID || + cmd_id == WMI_10_4_PDEV_UTF_EVENTID || + cmd_id == WMI_TLV_PDEV_UTF_EVENTID; +} + /* Returns true if callee consumes the skb and the skb should be discarded. * Returns false if skb is not used. Does not sleep. */ @@ -182,6 +190,12 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb) */ consumed = true; + if (!ath10k_tm_is_utf_event(cmd_id)) { + ath10k_dbg(ar, ATH10K_DBG_TESTMODE, + "testmode drop non-utf event cmd_id %u\n", cmd_id); + goto out; + } + if (ar->testmode.expected_seq != ATH10K_FTM_SEG_NONE) ath10k_tm_event_segmented(ar, cmd_id, skb); else From 4f25071afe9218aaae1c63fbf75e229aa6405319 Mon Sep 17 00:00:00 2001 From: Linghui Wu Date: Mon, 27 Jul 2026 12:56:29 +0530 Subject: [PATCH 355/403] wifi: ath10k: snoc: use memcpy_fromio() for MSA ramdump On WCN3990/SNOC the MSA region is mapped with devm_memremap(MEMREMAP_WT). On arm64 such a mapping is not Normal-cacheable, so unaligned accesses to it are not permitted. ath10k_msa_dump_memory() copies the region with a plain memcpy(), whose optimized __pi_memcpy_generic implementation issues wide/unaligned loads. This triggers an alignment fault (FSC=0x21) Oops in ath10k_snoc_fw_crashed_dump() while collecting the devcoredump: Unable to handle kernel paging request ... FSC=0x21: alignment fault pc : __pi_memcpy_generic lr : ath10k_snoc_fw_crashed_dump [ath10k_snoc] The Oops both leaves the firmware RAM dump buffer zeroed (no dump is captured) and crashes the kernel, which in turn breaks modem SSR recovery. Use memcpy_fromio(), which only performs accesses that are valid for such a device-memory mapping. The generic memcpy_fromio() implementation aligns the source before issuing word-sized reads and stores the destination with put_unaligned(), so it is also safe for the coherent DMA allocation used on the non-reserved-memory path. ath11k and ath12k use the same pattern when copying target memory into crash dumps, so call it unconditionally here too. The MEMREMAP_WT pointer is a plain void *, so an explicit __iomem cast is needed; use __force to keep sparse happy. Tested-on: WCN3990 hw1.0 SNOC WLAN.HL.3.3.7.c5-00107-QCAHLSWMTPL-1 Fixes: 3f14b73c3843 ("ath10k: Enable MSA region dump support for WCN3990") Signed-off-by: Linghui Wu Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260727072629.2297208-1-linghui.wu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath10k/snoc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 310650227578..33c98927e8fe 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -1475,11 +1476,15 @@ static void ath10k_msa_dump_memory(struct ath10k *ar, hdr->length = cpu_to_le32(ar->msa.mem_size); if (current_region->len < ar->msa.mem_size) { - memcpy(buf, ar->msa.vaddr, current_region->len); + memcpy_fromio(buf, + (const void __iomem __force *)ar->msa.vaddr, + current_region->len); ath10k_warn(ar, "msa dump length is less than msa size %x, %x\n", current_region->len, ar->msa.mem_size); } else { - memcpy(buf, ar->msa.vaddr, ar->msa.mem_size); + memcpy_fromio(buf, + (const void __iomem __force *)ar->msa.vaddr, + ar->msa.mem_size); } } From 6f6c9800e54cc7a9c5530797892b8b877335cf3c Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Tue, 21 Jul 2026 18:13:02 -0700 Subject: [PATCH 356/403] wifi: mt76: mt792x: do not advertise active monitor mt76_phy_init() sets NL80211_FEATURE_ACTIVE_MONITOR for every mt76 device, but mt792x firmware does not honor it: entering active monitor mode stops RX. Gate the feature behind a new per-phy no_active_monitor flag and set it for mt792x. Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260722011302.113060-1-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 5 +++-- drivers/net/wireless/mediatek/mt76/mt76.h | 1 + drivers/net/wireless/mediatek/mt76/mt792x_core.c | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index f92277770488..abbe65cbcd89 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -439,8 +439,9 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw) SET_IEEE80211_DEV(hw, dev->dev); SET_IEEE80211_PERM_ADDR(hw, phy->macaddr); - wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR | - NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE; + wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE; + if (!phy->no_active_monitor) + wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH | WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_AP_UAPSD; diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 61ef1b80feb8..62b41c8bb7c0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -873,6 +873,7 @@ struct mt76_phy { struct cfg80211_chan_def main_chandef; bool offchannel; bool radar_enabled; + bool no_active_monitor; struct delayed_work roc_work; struct ieee80211_vif *roc_vif; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 837d7af984e8..0ad33f74c228 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -820,6 +820,7 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + phy->mt76->no_active_monitor = true; wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT); From 4df22710a77d2365e56d720bc4106e54c1dfa2ff Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Thu, 2 Jul 2026 12:43:37 +0200 Subject: [PATCH 357/403] wifi: mt76: mt7996: remove beacon_int_min_gcd from ADHOC interface combinations The driver fails to register with error -22 (EINVAL) due to a cfg80211 validation failure in wiphy_verify_iface_combinations(). Commit 5ef0e8e2653b ("wifi: mt76: mt7996: fix iface combination for different chipsets") added beacon_int_min_gcd to if_comb_global and if_comb_global_7992, but these combinations include ADHOC (IBSS) interface type. This violates a cfg80211 rule from commit 56271da29c52 ("cfg80211: disallow beacon_int_min_gcd with IBSS") that explicitly forbids combining ADHOC with beacon_int_min_gcd. The restriction exists because beacon_int_min_gcd requires static, predictable beacon intervals to coordinate multiple beaconing interfaces, but ADHOC interfaces have dynamic beacon intervals that change when joining different networks, making the GCD constraint unenforceable. Remove beacon_int_min_gcd from the interface combinations that include ADHOC because they are not necessary for ADHOC operation. The if_comb combination (AP/MESH/STA only, without ADHOC) correctly retains beacon_int_min_gcd for multi-AP coordination. Fixes: 5ef0e8e2653b ("wifi: mt76: mt7996: fix iface combination for different chipsets") Signed-off-by: Jose Ignacio Tornos Martinez Tested-by: Alex Gavin Link: https://patch.msgid.link/20260702104337.679536-1-jtornosm@redhat.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/init.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c index 70902395bc96..fb635a092584 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -34,7 +34,6 @@ static const struct ieee80211_iface_combination if_comb_global = { BIT(NL80211_CHAN_WIDTH_40) | BIT(NL80211_CHAN_WIDTH_80) | BIT(NL80211_CHAN_WIDTH_160), - .beacon_int_min_gcd = 100, }; static const struct ieee80211_iface_combination if_comb_global_7992 = { @@ -47,7 +46,6 @@ static const struct ieee80211_iface_combination if_comb_global_7992 = { BIT(NL80211_CHAN_WIDTH_40) | BIT(NL80211_CHAN_WIDTH_80) | BIT(NL80211_CHAN_WIDTH_160), - .beacon_int_min_gcd = 100, }; static const struct ieee80211_iface_limit if_limits[] = { From 667c12782aaf8dd3cb2213e528fe63a73cb63345 Mon Sep 17 00:00:00 2001 From: "Yuhang.chen" Date: Wed, 29 Jul 2026 09:41:42 +0800 Subject: [PATCH 358/403] wifi: rtw89: pci: add .shutdown callback to stop rfkill polling on reboot Since the hardware rfkill polling was introduced, arm64 platforms can panic with an asynchronous SError during warm reboot: SError Interrupt on CPU8, code 0x00000000be000011 -- SError Workqueue: events_power_efficient rfkill_poll [rfkill] rtw89_pci_ops_read8+0x94/0x160 [rtw89_pci] rtw89_core_rfkill_poll+0x50/0x1e0 [rtw89_core] rtw89_ops_rfkill_poll+0x40/0x68 [rtw89_core] ieee80211_rfkill_poll+0x3c/0x70 [mac80211] cfg80211_rfkill_poll+0x40/0x2a0 [cfg80211] rfkill_poll+0x30/0x88 [rfkill] Kernel panic - not syncing: Asynchronous SError Interrupt On the reboot path the kernel only runs device_shutdown(), which calls each driver's .shutdown callback; .remove is not invoked. The rtw89 PCI driver had no .shutdown callback, so nothing stopped the rfkill polling work while the platform was tearing the PCIe link down. Once the link is gone, the next MMIO read from the poll handler targets a non-responding device and is reported as a fatal asynchronous SError on arm64. Add rtw89_pci_shutdown(), wired to all rtw89 PCI device drivers, which sets a new RTW89_FLAG_SHUTDOWN flag (mirroring the USB RTW89_FLAG_UNPLUGGED pattern). When the flag is set, rtw89_ops_rfkill_poll() returns early, so no MMIO read is issued to the chip after shutdown begins and the SError no longer occurs. This does not call the full .remove path from .shutdown, to keep the shutdown handler minimal and avoid running the non-idempotent teardown twice. Fixes: 0b38e6277aed ("wifi: rtw89: add support for hardware rfkill") Cc: stable@vger.kernel.org Suggested-by: Ping-Ke Shih Signed-off-by: Yuhang.chen Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260729014142.2746777-1-yhchen312@gmail.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac80211.c | 3 ++- drivers/net/wireless/realtek/rtw89/pci.c | 13 +++++++++++++ drivers/net/wireless/realtek/rtw89/pci.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8851be.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852ae.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852be.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852bte.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852ce.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922ae.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922de.c | 1 + 11 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 09c19a6b9058..889a94d7f534 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6453,6 +6453,7 @@ enum rtw89_flags { RTW89_FLAG_CHANGING_INTERFACE, RTW89_FLAG_HW_RFKILL_STATE, RTW89_FLAG_UNPLUGGED, + RTW89_FLAG_SHUTDOWN, NUM_OF_RTW89_FLAGS, }; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index e381aacda667..c1be69a3c192 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -2004,7 +2004,8 @@ static void rtw89_ops_rfkill_poll(struct ieee80211_hw *hw) lockdep_assert_wiphy(hw->wiphy); /* wl_disable GPIO get floating when entering LPS */ - if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) + if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags) || + test_bit(RTW89_FLAG_SHUTDOWN, rtwdev->flags)) return; rtw89_core_rfkill_poll(rtwdev, false); diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index c5b82fc46d06..8c3f4eb52cc5 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -4878,6 +4878,19 @@ void rtw89_pci_remove(struct pci_dev *pdev) } EXPORT_SYMBOL(rtw89_pci_remove); +void rtw89_pci_shutdown(struct pci_dev *pdev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct rtw89_dev *rtwdev; + + if (!hw) + return; + + rtwdev = hw->priv; + set_bit(RTW89_FLAG_SHUTDOWN, rtwdev->flags); +} +EXPORT_SYMBOL(rtw89_pci_shutdown); + MODULE_AUTHOR("Realtek Corporation"); MODULE_DESCRIPTION("Realtek PCI 802.11ax wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index 0e1557cedd20..cba52584ea68 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -1752,6 +1752,7 @@ struct pci_device_id; int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id); void rtw89_pci_remove(struct pci_dev *pdev); +void rtw89_pci_shutdown(struct pci_dev *pdev); void rtw89_pci_basic_cfg(struct rtw89_dev *rtwdev, bool resume); void rtw89_pci_ops_reset(struct rtw89_dev *rtwdev); int rtw89_pci_ltr_set(struct rtw89_dev *rtwdev, bool en); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851be.c b/drivers/net/wireless/realtek/rtw89/rtw8851be.c index 459d2e778b83..81c62296c32d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851be.c @@ -94,6 +94,7 @@ static struct pci_driver rtw89_8851be_driver = { .id_table = rtw89_8851be_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c index 142a45a2e718..3defb0748648 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c @@ -96,6 +96,7 @@ static struct pci_driver rtw89_8852ae_driver = { .id_table = rtw89_8852ae_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852be.c b/drivers/net/wireless/realtek/rtw89/rtw8852be.c index 1c622a99c070..9b48e70c2b56 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852be.c @@ -98,6 +98,7 @@ static struct pci_driver rtw89_8852be_driver = { .id_table = rtw89_8852be_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c index 73c7ebd33ab4..850ae9cbac18 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c @@ -100,6 +100,7 @@ static struct pci_driver rtw89_8852bte_driver = { .id_table = rtw89_8852bte_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c index bb5d5745aa62..daff152f66bc 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c @@ -123,6 +123,7 @@ static struct pci_driver rtw89_8852ce_driver = { .id_table = rtw89_8852ce_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c index 8f3a1cd463d9..e82c866dfa36 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c @@ -113,6 +113,7 @@ static struct pci_driver rtw89_8922ae_driver = { .id_table = rtw89_8922ae_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops_be, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922de.c b/drivers/net/wireless/realtek/rtw89/rtw8922de.c index 09400a0efcfd..a71e41f28544 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922de.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922de.c @@ -113,6 +113,7 @@ static struct pci_driver rtw89_8922de_driver = { .id_table = rtw89_8922de_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops_be, .err_handler = &rtw89_pci_err_handler, }; From 304720870f4cf24042bcad3ed6d7144710926d72 Mon Sep 17 00:00:00 2001 From: Dian-Syuan Yang Date: Wed, 29 Jul 2026 20:43:52 +0800 Subject: [PATCH 359/403] wifi: rtw89: refine RX filter configuration for WiFi 7 generation When an AP interface is registered and starts AP, mac80211 calls the configure_filter() to clear B_AX_A_UC_CAM_MATCH and B_AX_A_BC_CAM_MATCH so that frames from un-associated stations can be received. However, for a dedicated AP interface created via iw command, configure_filter() is only triggered on the initial AP startup. Since the interface remains up even after hostapd stops, and it isn't triggered again on later restarts. Additionally, each AP start causes IPS leave, reverting the RX filter to its default value. For WiFi 7 chips, the default value is hardcoded in rx_fltr_init_be(), so the reverted value does not match hal.rx_fltr. Therefore, refine the behavior of WiFi 7 chips to align with the WiFi 6 implementation. Signed-off-by: Dian-Syuan Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260729124354.3231368-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 3 ++- drivers/net/wireless/realtek/rtw89/mac.c | 1 + drivers/net/wireless/realtek/rtw89/mac.h | 1 + drivers/net/wireless/realtek/rtw89/mac_be.c | 7 ++----- drivers/net/wireless/realtek/rtw89/reg.h | 6 +++++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 77a0e2582dbe..097e9b0312a1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -6960,6 +6960,7 @@ void rtw89_sta_unset_link(struct rtw89_sta *rtwsta, unsigned int link_id) int rtw89_core_init(struct rtw89_dev *rtwdev) { + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; struct rtw89_btc *btc = &rtwdev->btc; u8 band; @@ -7016,7 +7017,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) rtw89_core_ppdu_sts_init(rtwdev); rtw89_traffic_stats_init(rtwdev, &rtwdev->stats); - rtwdev->hal.rx_fltr = DEFAULT_AX_RX_FLTR; + rtwdev->hal.rx_fltr = mac->default_rx_fltr; rtwdev->dbcc_en = false; rtwdev->mlo_dbcc_mode = MLO_DBCC_NOT_SUPPORT; rtwdev->mac.qta_mode = RTW89_QTA_SCC; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 2cbff2be9bbb..df396fbfca26 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -7454,6 +7454,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .mem_base_addrs = rtw89_mac_mem_base_addrs_ax, .mem_page_size = MAC_MEM_DUMP_PAGE_SIZE_AX, .rx_fltr = R_AX_RX_FLTR_OPT, + .default_rx_fltr = DEFAULT_AX_RX_FLTR, .port_base = &rtw89_port_base_ax, .agg_len_ht = R_AX_AGG_LEN_HT_0, .ps_status = R_AX_PPWRBIT_SETTING, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 493d2b9626a6..8419bcd3956a 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1063,6 +1063,7 @@ struct rtw89_mac_gen_def { const u32 *mem_base_addrs; u32 mem_page_size; u32 rx_fltr; + u32 default_rx_fltr; const struct rtw89_port_reg *port_base; u32 agg_len_ht; u32 ps_status; diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index dfa0973e367c..c8f343c8d545 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -1328,11 +1328,7 @@ static int rx_fltr_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_DATA, RTW89_FWD_TO_HOST, mac_idx); reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RX_FLTR_OPT, mac_idx); - val = B_BE_A_BC_CAM_MATCH | B_BE_A_UC_CAM_MATCH | B_BE_A_MC | - B_BE_A_BC | B_BE_A_A1_MATCH | - u32_encode_bits(15, B_BE_UID_FILTER_MASK); - rtw89_write32(rtwdev, reg, val); - u32p_replace_bits(&rtwdev->hal.rx_fltr, 15, B_BE_UID_FILTER_MASK); + rtw89_write32(rtwdev, reg, rtwdev->hal.rx_fltr); reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_PLCP_HDR_FLTR, mac_idx); val = B_BE_HE_SIGB_CRC_CHK | B_BE_VHT_MU_SIGB_CRC_CHK | @@ -3276,6 +3272,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .mem_base_addrs = rtw89_mac_mem_base_addrs_be, .mem_page_size = MAC_MEM_DUMP_PAGE_SIZE_BE, .rx_fltr = R_BE_RX_FLTR_OPT, + .default_rx_fltr = DEFAULT_BE_RX_FLTR, .port_base = &rtw89_port_base_be, .agg_len_ht = R_BE_AGG_LEN_HT_0, .ps_status = R_BE_WMTX_POWER_BE_BIT_CTL, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 756b94dcd475..abeeb61007dc 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -3348,7 +3348,7 @@ #define DEFAULT_AX_RX_FLTR (B_AX_A_A1_MATCH | B_AX_A_BC | B_AX_A_MC | \ B_AX_A_UC_CAM_MATCH | B_AX_A_BC_CAM_MATCH | \ B_AX_A_PWR_MGNT | B_AX_A_FTM_REQ | \ - u32_encode_bits(3, B_AX_UID_FILTER_MASK) | \ + FIELD_PREP_CONST(B_AX_UID_FILTER_MASK, 3) | \ B_AX_A_BCN_CHK_EN) #define B_AX_RX_FLTR_CFG_MASK ((u32)~B_AX_RX_MPDU_MAX_LEN_MASK) @@ -8209,6 +8209,10 @@ #define B_BE_A_BC BIT(2) #define B_BE_A_A1_MATCH BIT(1) #define B_BE_SNIFFER_MODE BIT(0) +#define DEFAULT_BE_RX_FLTR (B_BE_A_BC_CAM_MATCH | B_BE_A_UC_CAM_MATCH | \ + B_BE_A_MC | B_BE_A_BC | B_BE_A_A1_MATCH | \ + FIELD_PREP_CONST(B_BE_UID_FILTER_MASK, 15) | \ + B_BE_A_FTM_REQ | B_BE_A_BCN_CHK_EN) #define R_BE_CTRL_FLTR 0x11424 #define R_BE_CTRL_FLTR_C1 0x15424 From 58c0d447c53b4e5a50fdeaa30aa207df1406951f Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Wed, 29 Jul 2026 20:43:53 +0800 Subject: [PATCH 360/403] wifi: rtw89: fix scan offload version check logic The version check with less than should check for smallest upper-bound first, or some branch will be unreachable. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260729124354.3231368-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 5ab80bda3ae5..a3d72fbaba54 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -7748,12 +7748,12 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, rtw89_scan_get_6g_disabled_chan(rtwdev, option); - if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V1, &rtwdev->fw)) { - cfg_len = offsetofend(typeof(*h2c), w9); - scan_offload_ver = 1; - } else if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) { + if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) { cfg_len = offsetofend(typeof(*h2c), w8); scan_offload_ver = 0; + } else if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V1, &rtwdev->fw)) { + cfg_len = offsetofend(typeof(*h2c), w9); + scan_offload_ver = 1; } len = cfg_len + macc_role_size + opch_size; From d946a4e031167736c5f4446e5c55231d3543ef29 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Wed, 29 Jul 2026 20:43:54 +0800 Subject: [PATCH 361/403] wifi: rtw89: change scan offload format to V3 for WiFi 7 IC Adapt H2C format to fit firmware version after 0.35.113.2, which only supports active scanning with a provided SSID list. The wildcard_6ghz field is no longer required for WiFi 7 chips' new format, but not removed since WiFi 6 still requires it. Keep original H2C style also to support older firmwares. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260729124354.3231368-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 4 + drivers/net/wireless/realtek/rtw89/fw.c | 160 +++++++++++++++++++--- drivers/net/wireless/realtek/rtw89/fw.h | 6 + drivers/net/wireless/realtek/rtw89/wow.c | 20 +++ 4 files changed, 172 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 889a94d7f534..d82de04352e6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5989,6 +5989,7 @@ enum rtw89_fw_feature { RTW89_FW_FEATURE_MACID_PAUSE_SLEEP, RTW89_FW_FEATURE_SCAN_OFFLOAD_BE_V0, RTW89_FW_FEATURE_SCAN_OFFLOAD_BE_V1, + RTW89_FW_FEATURE_SCAN_OFFLOAD_BE_V2, RTW89_FW_FEATURE_WOW_REASON_V1, RTW89_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, RTW89_FW_FEATURE_RFK_PRE_NOTIFY_V0, @@ -7184,6 +7185,9 @@ struct rtw89_hw_scan_info { struct list_head chan_list; struct rtw89_chan op_chan; struct rtw89_hw_scan_extra_op extra_op; + u8 wildcard_pkt_id[NUM_NL80211_BANDS]; + u16 ssid_total_len; + int n_ssids; bool connected; bool abort; u16 delay; /* in unit of ms */ diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index a3d72fbaba54..0099c5c03e7a 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -934,6 +934,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 100, 0, SER_POST_RECOVER_DMAC), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 108, 0, SIM_SER_L0L1_BY_HALT_H2C), __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(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), @@ -949,6 +950,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922D, ge, 0, 35, 108, 0, SIM_SER_L0L1_BY_HALT_H2C), __CFG_FW_FEAT(RTL8922D, lt, 0, 35, 109, 1, SCAN_OFFLOAD_BE_V1), __CFG_FW_FEAT(RTL8922D, lt, 0, 35, 113, 0, RFK_TXIQK_V0), + __CFG_FW_FEAT(RTL8922D, lt, 0, 35, 113, 2, SCAN_OFFLOAD_BE_V2), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, @@ -7498,6 +7500,7 @@ int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num, struct rtw89_h2c_chinfo_elem_be *elem; struct rtw89_mac_chinfo_be *ch_info; struct rtw89_h2c_chinfo_be *h2c; + bool wildcard_by_drv; struct sk_buff *skb; unsigned int cond; u8 ver = U8_MAX; @@ -7516,6 +7519,10 @@ int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num, if (RTW89_CHK_FW_FEATURE(CH_INFO_BE_V0, &rtwdev->fw)) ver = 0; + wildcard_by_drv = !(RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw) || + RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V1, &rtwdev->fw) || + RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V2, &rtwdev->fw)); + skb_put(skb, sizeof(*h2c)); h2c = (struct rtw89_h2c_chinfo_be *)skb->data; @@ -7525,6 +7532,8 @@ int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num, RTW89_H2C_CHINFO_ARG_MAC_IDX_MASK); list_for_each_entry(ch_info, chan_list, list) { + bool with_probe_id = ch_info->probe_id != RTW89_SCANOFLD_PKT_NONE; + elem = (struct rtw89_h2c_chinfo_elem_be *)skb_put(skb, sizeof(*elem)); elem->w0 = le32_encode_bits(ch_info->dwell_time, RTW89_H2C_CHINFO_BE_W0_DWELL) | @@ -7542,7 +7551,7 @@ int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num, RTW89_H2C_CHINFO_BE_W1_RANDOM) | le32_encode_bits(ch_info->notify_action, RTW89_H2C_CHINFO_BE_W1_NOTIFY) | - le32_encode_bits(ch_info->probe_id != 0xff ? 1 : 0, + le32_encode_bits(wildcard_by_drv || with_probe_id, RTW89_H2C_CHINFO_BE_W1_PROBE) | le32_encode_bits(ch_info->leave_crit, RTW89_H2C_CHINFO_BE_W1_EARLY_LEAVE_CRIT) | @@ -7707,6 +7716,34 @@ static void rtw89_scan_get_6g_disabled_chan(struct rtw89_dev *rtwdev, } } +void rtw89_hw_scan_calc_req_ssid(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link, bool wowlan) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; + struct cfg80211_scan_request *req = rtwvif->scan_req; + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct cfg80211_sched_scan_request *nd_config; + struct cfg80211_ssid *s; + u16 sum = 0, n_ssids; + int i, num = 0; + + nd_config = rtw_wow->nd_config; + n_ssids = wowlan ? nd_config->n_match_sets : req->n_ssids; + + for (i = 0; i < n_ssids; i++) { + s = wowlan ? &nd_config->match_sets[i].ssid : &req->ssids[i]; + if (s->ssid_len == 0) + continue; + + num++; + sum += s->ssid_len + 1; + } + + scan_info->n_ssids = num; + scan_info->ssid_total_len = sum; +} + int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, struct rtw89_scan_option *option, struct rtw89_vif_link *rtwvif_link, @@ -7721,18 +7758,21 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, struct rtw89_hw_scan_extra_op scan_op[2] = {}; struct rtw89_chan *op = &scan_info->op_chan; struct rtw89_h2c_scanofld_be_opch *opch; - struct rtw89_pktofld_info *pkt_info; struct rtw89_h2c_scanofld_be *h2c; struct ieee80211_vif *vif; struct sk_buff *skb; u8 macc_role_size = sizeof(*macc_role) * option->num_macc_role; u8 opch_size = sizeof(*opch) * option->num_opch; + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct cfg80211_sched_scan_request *nd_config; + u8 *probe_id = scan_info->wildcard_pkt_id; enum rtw89_scan_be_opmode opmode; - u8 probe_id[NUM_NL80211_BANDS]; u8 scan_offload_ver = U8_MAX; u8 cfg_len = sizeof(*h2c); + struct cfg80211_ssid *s; unsigned int cond; u8 ver = U8_MAX; + u8 n_ssids = 0; u8 policy_val; void *ptr; u8 txnull; @@ -7754,9 +7794,14 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, } else if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V1, &rtwdev->fw)) { cfg_len = offsetofend(typeof(*h2c), w9); scan_offload_ver = 1; + } else if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V2, &rtwdev->fw)) { + scan_offload_ver = 2; } len = cfg_len + macc_role_size + opch_size; + if (scan_offload_ver > 2) + len += scan_info->ssid_total_len; + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { rtw89_err(rtwdev, "failed to alloc skb for h2c scan offload\n"); @@ -7767,21 +7812,9 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, h2c = (struct rtw89_h2c_scanofld_be *)skb->data; ptr = skb->data; - memset(probe_id, RTW89_SCANOFLD_PKT_NONE, sizeof(probe_id)); - if (RTW89_CHK_FW_FEATURE(CH_INFO_BE_V0, &rtwdev->fw)) ver = 0; - if (!wowlan) { - list_for_each_entry(pkt_info, &scan_info->pkt_list[NL80211_BAND_6GHZ], list) { - if (pkt_info->wildcard_6ghz) { - /* Provide wildcard as template */ - probe_id[NL80211_BAND_6GHZ] = pkt_info->id; - break; - } - } - } - h2c->w0 = le32_encode_bits(option->operation, RTW89_H2C_SCANOFLD_BE_W0_OP) | le32_encode_bits(option->scan_mode, RTW89_H2C_SCANOFLD_BE_W0_SCAN_MODE) | @@ -7800,7 +7833,10 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, le32_encode_bits(option->norm_cy, RTW89_H2C_SCANOFLD_BE_W2_NORM_CY) | le32_encode_bits(option->opch_end, RTW89_H2C_SCANOFLD_BE_W2_OPCH_END); - h2c->w3 = le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_W3_NUM_SSID) | + if (scan_offload_ver > 2) + n_ssids = scan_info->n_ssids; + + h2c->w3 = le32_encode_bits(n_ssids, RTW89_H2C_SCANOFLD_BE_W3_NUM_SSID) | le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_W3_NUM_SHORT_SSID) | le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_W3_NUM_BSSID) | le32_encode_bits(probe_id[NL80211_BAND_2GHZ], RTW89_H2C_SCANOFLD_BE_W3_PROBEID); @@ -7927,6 +7963,24 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, ptr += sizeof(*opch); } + if (scan_offload_ver <= 2) + goto set_hdr; + + nd_config = rtw_wow->nd_config; + n_ssids = wowlan ? nd_config->n_match_sets : req->n_ssids; + + for (i = 0; i < n_ssids; i++) { + s = wowlan ? &nd_config->match_sets[i].ssid : &req->ssids[i]; + if (s->ssid_len == 0) + continue; + + memcpy(ptr, &s->ssid_len, 1); + ptr++; + memcpy(ptr, s->ssid, s->ssid_len); + ptr += s->ssid_len; + } + +set_hdr: rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD, H2C_FUNC_SCANOFLD_BE, 1, 1, @@ -9256,14 +9310,19 @@ void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev) static void rtw89_hw_scan_release_pkt_list(struct rtw89_dev *rtwdev) { + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; struct list_head *pkt_list = rtwdev->scan_info.pkt_list; struct rtw89_pktofld_info *info, *tmp; - u8 idx; + u8 idx, wildcard_pkt_id; for (idx = NL80211_BAND_2GHZ; idx < NUM_NL80211_BANDS; idx++) { if (!(rtwdev->chip->support_bands & BIT(idx))) continue; + wildcard_pkt_id = scan_info->wildcard_pkt_id[idx]; + if (wildcard_pkt_id != RTW89_SCANOFLD_PKT_NONE) + rtw89_fw_h2c_del_pkt_offload(rtwdev, wildcard_pkt_id); + list_for_each_entry_safe(info, tmp, &pkt_list[idx], list) { if (test_bit(info->id, rtwdev->pkt_offload)) rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); @@ -9358,16 +9417,74 @@ static int rtw89_append_probe_req_ie(struct rtw89_dev *rtwdev, return ret; } +int rtw89_hw_scan_append_wildcard_probe_req(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link, + const u8 *mac_addr, bool wowlan) +{ + static const u8 basic_rate_ie[] = {WLAN_EID_SUPP_RATES, 0x08, 0x0c, 0x12, + 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c}; + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; + struct cfg80211_scan_request *req = rtwvif->scan_req; + struct ieee80211_scan_ies *ies = rtwvif->scan_ies; + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct cfg80211_sched_scan_request *nd_config; + enum nl80211_band band; + struct sk_buff *skb; + int ret; + u8 id; + + for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { + if (!(rtwdev->chip->support_bands & BIT(band))) + continue; + + if (wowlan) { + nd_config = rtw_wow->nd_config; + + skb = ieee80211_probereq_get(rtwdev->hw, rtwvif_link->mac_addr, NULL, 0, + nd_config->ie_len + + sizeof(basic_rate_ie)); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, basic_rate_ie, sizeof(basic_rate_ie)); + skb_put_data(skb, nd_config->ie, nd_config->ie_len); + } else { + skb = ieee80211_probereq_get(rtwdev->hw, mac_addr, + NULL, 0, req->ie_len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, ies->ies[band], ies->len[band]); + skb_put_data(skb, ies->common_ies, ies->common_ie_len); + } + + ret = rtw89_fw_h2c_add_pkt_offload(rtwdev, &id, skb); + kfree_skb(skb); + + if (ret) + return ret; + + scan_info->wildcard_pkt_id[band] = id; + } + + return 0; +} + static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, const u8 *mac_addr) { struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; struct cfg80211_scan_request *req = rtwvif->scan_req; - struct sk_buff *skb; u8 num = req->n_ssids, i; + struct sk_buff *skb; int ret; + ret = rtw89_hw_scan_append_wildcard_probe_req(rtwdev, rtwvif_link, mac_addr, false); + if (ret) + return ret; + for (i = 0; i < num; i++) { skb = ieee80211_probereq_get(rtwdev->hw, mac_addr, req->ssids[i].ssid, @@ -9675,6 +9792,9 @@ static void rtw89_hw_scan_add_chan_be(struct rtw89_dev *rtwdev, int chan_type, if (probe_count >= RTW89_SCANOFLD_MAX_SSID) break; } + + if (scan_info->n_ssids) + ch_info->fw_probe0_ssids = GENMASK(scan_info->n_ssids - 1, 0); } if (ch_info->ch_band == RTW89_BAND_6G) { @@ -10277,6 +10397,10 @@ int rtw89_hw_scan_start(struct rtw89_dev *rtwdev, rtwdev->scan_info.delay = 0; rtwvif->scan_ies = &scan_req->ies; rtwvif->scan_req = req; + rtw89_hw_scan_calc_req_ssid(rtwdev, rtwvif_link, false); + + memset(rtwdev->scan_info.wildcard_pkt_id, RTW89_SCANOFLD_PKT_NONE, + sizeof(rtwdev->scan_info.wildcard_pkt_id)); if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) get_random_mask_addr(mac_addr, req->mac_addr, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 6147960521ee..375ca0a243a6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3119,6 +3119,7 @@ struct rtw89_h2c_scanofld_be { __le32 w11; /* Added after SCAN_OFFLOAD_BE_V2 */ /* struct rtw89_h2c_scanofld_be_macc_role (flexible number) */ /* struct rtw89_h2c_scanofld_be_opch (flexible number) */ + /* probe SSID list (flexible number); Added after SCAN_OFFLOAD_BE_V3 */ } __packed; #define RTW89_H2C_SCANOFLD_BE_W0_OP GENMASK(1, 0) @@ -5599,6 +5600,11 @@ int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); int rtw89_pno_scan_add_chan_list_be(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); +int rtw89_hw_scan_append_wildcard_probe_req(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link, + const u8 *mac_addr, bool wowlan); +void rtw89_hw_scan_calc_req_ssid(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link, bool wowlan); int rtw89_fw_h2c_trigger_cpu_exception(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_pkt_drop(struct rtw89_dev *rtwdev, const struct rtw89_pkt_drop_params *params); diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index d6bf5cd8be3b..2dfa24c54e7d 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -1457,9 +1457,20 @@ static int rtw89_wow_disable_trx_post(struct rtw89_dev *rtwdev) static void rtw89_fw_release_pno_pkt_list(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; struct rtw89_wow_param *rtw_wow = &rtwdev->wow; struct list_head *pkt_list = &rtw_wow->pno_pkt_list; struct rtw89_pktofld_info *info, *tmp; + u8 idx, wildcard_pkt_id; + + for (idx = NL80211_BAND_2GHZ; idx < NUM_NL80211_BANDS; idx++) { + if (!(rtwdev->chip->support_bands & BIT(idx))) + continue; + + wildcard_pkt_id = scan_info->wildcard_pkt_id[idx]; + if (wildcard_pkt_id != RTW89_SCANOFLD_PKT_NONE) + rtw89_fw_h2c_del_pkt_offload(rtwdev, wildcard_pkt_id); + } list_for_each_entry_safe(info, tmp, pkt_list, list) { rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); @@ -1480,6 +1491,11 @@ static int rtw89_pno_scan_update_probe_req(struct rtw89_dev *rtwdev, struct sk_buff *skb; int ret; + ret = rtw89_hw_scan_append_wildcard_probe_req(rtwdev, rtwvif_link, + rtwvif_link->mac_addr, true); + if (ret) + return ret; + for (i = 0; i < num; i++) { skb = ieee80211_probereq_get(rtwdev->hw, rtwvif_link->mac_addr, nd_config->match_sets[i].ssid.ssid, @@ -1523,6 +1539,10 @@ static int rtw89_pno_scan_offload(struct rtw89_dev *rtwdev, bool enable) int ret; if (enable) { + rtw89_hw_scan_calc_req_ssid(rtwdev, rtwvif_link, true); + memset(rtwdev->scan_info.wildcard_pkt_id, RTW89_SCANOFLD_PKT_NONE, + sizeof(rtwdev->scan_info.wildcard_pkt_id)); + ret = rtw89_pno_scan_update_probe_req(rtwdev, rtwvif_link); if (ret) { rtw89_err(rtwdev, "Update probe request failed\n"); From e62f0baca06e1fc9a54e86d95cfcd3c7d5ff0325 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Thu, 30 Jul 2026 14:02:14 +0800 Subject: [PATCH 362/403] wifi: rtw89: coex: Fix Bluetooth link weight not updated on profile change _update_bt_link_cnt() was missing the bt->link_weight[]. Without it, link_weight stays at the initial value of 5 (set on BT re-enable) regardless of the active BT profile. _set_bind_info() uses link_weight to compute b2g_score/b5g_score, which determines tdd_bind.rf_band. With a stale weight of 5 (below the active profile threshold), tdd_bind.rf_band may not reflect the actual RF band, causing mode_v0 in _run_coex() to remain 0 (BTC_WLINK_NOLINK), which incorrectly triggers _action_wl_nc() instead of the BT-profile action. Add the link_weight calculation to _update_bt_link_cnt() using the same weighting table as Formal (BIS/A2DP-sink: 70, A2DP: 40-60, PAN/active: 30, no-profile: 5, else: 9) and call _update_bt_link_cnt() from _update_bt_info() after parsing the profile exist flags, replacing the open-coded link_cnt increment that omitted le-audio profiles. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260730060220.55844-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 30 ++++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 0e42c720819a..fd9ddd55c192 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -7973,6 +7973,8 @@ static void _update_bt_link_cnt(struct rtw89_dev *rtwdev, struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc; struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc; struct rtw89_btc_bt_pan_desc *pan = &b->pan_desc; + u8 bt_rf_band = b56g ? BTC_BT_B5G : BTC_BT_B2G; + u8 val; b->link_cnt.last = b->link_cnt.now; b->link_cnt.now = 0; @@ -7984,6 +7986,27 @@ static void _update_bt_link_cnt(struct rtw89_dev *rtwdev, b->link_cnt.now += pan->exist; b->link_cnt.now += leaudio->bis_exist; b->link_cnt.now += leaudio->cis_exist; + + if (leaudio->bis_exist || a2dp->sink) { + val = 70; + } else if (a2dp->exist) { + val = 40; + if (a2dp->vendor_id == 0x4c) + val += 10; + if (hid->exist) + val += 10; + } else if (pan->exist || pan->active || a2dp->active || + b->status.map.inq_pag) { + val = 30; + } else if (b->link_cnt.now == 0) { + val = 5; + } else { + val = 9; + } + bt->link_weight[bt_rf_band] = val; + + if (b->link_cnt.last != b->link_cnt.now) + b->link_cnt.chg = 1; } static void _update_bt_leaudio_info(struct rtw89_dev *rtwdev, u8 bid, @@ -9012,8 +9035,6 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) "[BTC], %s(): bt_info[2]=0x%02x\n", __func__, bt->raw_info[2]); - b->link_cnt.last = b->link_cnt.now; - b->link_cnt.now = 0; hid->type = 0; /* parse raw info low-Byte2 */ @@ -9026,13 +9047,10 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) bt->bcnt[BTC_BCNT_INQPAG] += !!(bt->inq_pag.now && !bt->inq_pag.last); hfp->exist = btinfo.lb2.hfp; - b->link_cnt.now += (u8)hfp->exist; hid->exist = btinfo.lb2.hid; - b->link_cnt.now += (u8)hid->exist; a2dp->exist = btinfo.lb2.a2dp; - b->link_cnt.now += (u8)a2dp->exist; pan->exist = btinfo.lb2.pan; - b->link_cnt.now += (u8)pan->exist; + _update_bt_link_cnt(rtwdev, bt, 0); btc->dm.trx_info.bt_profile = u32_get_bits(btinfo.val, BT_PROFILE_PROTOCOL_MASK); /* parse raw info low-Byte3 */ From cf7f33eb04618cb5e34f2397de209db5736acbc6 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Thu, 30 Jul 2026 14:02:15 +0800 Subject: [PATCH 363/403] wifi: rtw89: coex: Fix BT-info parsing for 5/6 GHz band & dual Bluetooth The _update_bt_info() function always parsed BT-info for bt0 and used a single raw_info buffer regardless of which BT device sent the packet or which RF band it belongs to. This caused two bugs: 1. BT-info packets from bt1 were incorrectly parsed into bt0's state. 2. BT-info packets from 5/6 GHz BT (L1 bit7=1) overwrote the 2.4 GHz link_info and raw_info, breaking duplicate detection across bands. Fix by adding the bid parameter to select bt0/bt1, detecting the 5/6 GHz band flag (L1 bit7) to route packets into link_info_56g with a dedicated raw_info_56g buffer, and updating the early-return mask to ignore bit7 when checking the length field. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260730060220.55844-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 61 ++++++++++++++++------- drivers/net/wireless/realtek/rtw89/core.h | 3 +- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index fd9ddd55c192..d5b1c68539d9 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -9005,23 +9005,46 @@ static u8 _update_bt_rssi_level(struct rtw89_dev *rtwdev, u8 rssi) #define BT_PROFILE_PROTOCOL_MASK GENMASK(7, 4) -static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) +static void _update_bt_info(struct rtw89_dev *rtwdev, u8 bid, u8 *buf, u32 len) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_bt_a2dp_desc *a2dp; struct rtw89_btc_cx *cx = &btc->cx; - struct rtw89_btc_bt_info *bt = &cx->bt0; - struct rtw89_btc_bt_link_info *b = &bt->link_info; - struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc; - struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc; - struct rtw89_btc_bt_a2dp_desc *a2dp = &b->a2dp_desc; - struct rtw89_btc_bt_pan_desc *pan = &b->pan_desc; + struct rtw89_btc_bt_hfp_desc *hfp; + struct rtw89_btc_bt_hid_desc *hid; + struct rtw89_btc_bt_pan_desc *pan; + struct rtw89_btc_bt_link_info *b; + struct rtw89_btc_bt_info *bt; union btc_btinfo btinfo; + u8 is_bt_56g = 0; + u8 *raw_info; - if (buf[BTC_BTINFO_L1] != 6) + /* Bit7 is used for RF-band: 0:BT_2.4 GHz, 1:BT_5/6 GHz */ + if ((buf[BTC_BTINFO_L1] & 0x7f) != 6) return; - if (!memcmp(bt->raw_info, buf, BTC_BTINFO_MAX)) { + 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; + is_bt_56g = 1; + if (b->status.map.connect) + bt->rf_band_map |= BIT(RTW89_BAND_5G); + else + bt->rf_band_map &= ~BIT(RTW89_BAND_5G); + } else { + b = &bt->link_info; + bt->rf_band_map |= BIT(RTW89_BAND_2G); + } + + hfp = &b->hfp_desc; + hid = &b->hid_desc; + a2dp = &b->a2dp_desc; + pan = &b->pan_desc; + raw_info = is_bt_56g ? bt->raw_info_56g : bt->raw_info; + + if (!memcmp(raw_info, buf, BTC_BTINFO_MAX)) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): return by bt-info duplicate!!\n", __func__); @@ -9029,16 +9052,16 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) return; } - memcpy(bt->raw_info, buf, BTC_BTINFO_MAX); + memcpy(raw_info, buf, BTC_BTINFO_MAX); rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): bt_info[2]=0x%02x\n", - __func__, bt->raw_info[2]); + __func__, raw_info[2]); hid->type = 0; /* parse raw info low-Byte2 */ - btinfo.val = bt->raw_info[BTC_BTINFO_L2]; + btinfo.val = raw_info[BTC_BTINFO_L2]; b->status.map.connect = btinfo.lb2.connect; b->status.map.sco_busy = btinfo.lb2.sco_busy; b->status.map.acl_busy = btinfo.lb2.acl_busy; @@ -9050,11 +9073,11 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) hid->exist = btinfo.lb2.hid; a2dp->exist = btinfo.lb2.a2dp; pan->exist = btinfo.lb2.pan; - _update_bt_link_cnt(rtwdev, bt, 0); + _update_bt_link_cnt(rtwdev, bt, is_bt_56g); btc->dm.trx_info.bt_profile = u32_get_bits(btinfo.val, BT_PROFILE_PROTOCOL_MASK); /* parse raw info low-Byte3 */ - btinfo.val = bt->raw_info[BTC_BTINFO_L3]; + btinfo.val = raw_info[BTC_BTINFO_L3]; if (btinfo.lb3.retry != 0) bt->bcnt[BTC_BCNT_RETRY]++; b->cqddr = btinfo.lb3.cqddr; @@ -9065,14 +9088,14 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) b->status.map.mesh_busy = btinfo.lb3.mesh_busy; /* parse raw info high-Byte0 */ - btinfo.val = bt->raw_info[BTC_BTINFO_H0]; + btinfo.val = raw_info[BTC_BTINFO_H0]; /* raw val is dBm unit, translate from -100~ 0dBm to 0~100%*/ b->rssi = chip->ops->btc_get_bt_rssi(rtwdev, btinfo.hb0.rssi); bt->rssi_level = _update_bt_rssi_level(rtwdev, b->rssi); btc->dm.trx_info.bt_rssi = bt->rssi_level; /* parse raw info high-Byte1 */ - btinfo.val = bt->raw_info[BTC_BTINFO_H1]; + btinfo.val = raw_info[BTC_BTINFO_H1]; b->status.map.ble_connect = btinfo.hb1.ble_connect; if (btinfo.hb1.ble_connect) { if (hid->exist) @@ -9101,7 +9124,7 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) b->multi_link.now = btinfo.hb1.multi_link; /* parse raw info high-Byte2 */ - btinfo.val = bt->raw_info[BTC_BTINFO_H2]; + btinfo.val = raw_info[BTC_BTINFO_H2]; pan->active = !!btinfo.hb2.pan_active; bt->bcnt[BTC_BCNT_AFH] += !!(btinfo.hb2.afh_update && !b->afh_update); @@ -9114,7 +9137,7 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) hid->type |= (hid->slot_info == BTC_HID_218 ? BTC_HID_218 : BTC_HID_418); /* parse raw info high-Byte3 */ - btinfo.val = bt->raw_info[BTC_BTINFO_H3]; + btinfo.val = raw_info[BTC_BTINFO_H3]; a2dp->bitpool = btinfo.hb3.a2dp_bitpool; if (b->tx_3m != (u32)btinfo.hb3.tx_3m) @@ -9785,7 +9808,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], handle C2H BT INFO with data %8ph\n", buf); bt->bcnt[BTC_BCNT_INFOUPDATE]++; - _update_bt_info(rtwdev, buf, len); + _update_bt_info(rtwdev, bid, buf, len); break; case BTF_EVNT_BT_SCBD: bt->bcnt[BTC_BCNT_SCBDUPDATE]++; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d82de04352e6..7f114c9a98ad 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2705,7 +2705,8 @@ struct rtw89_btc_bt_info { struct rtw89_btc_rf_para rf_para; union rtw89_btc_bt_rfk_info_map rfk_info; - u8 raw_info[BTC_BTINFO_MAX]; /* raw bt info from mailbox */ + u8 raw_info[BTC_BTINFO_MAX]; /* raw bt info from mailbox (2.4G) */ + u8 raw_info_56g[BTC_BTINFO_MAX]; /* raw bt info from mailbox (5/6G) */ u8 txpwr_info[BTC_BTINFO_MAX]; u8 link_weight[BTC_BT_BMAX]; /* Link Weight for RF-band/HWB selection */ u8 rssi_level; From 8847c1b12bdd501a188039d4f4ab53bed519f235 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Thu, 30 Jul 2026 14:02:16 +0800 Subject: [PATCH 364/403] wifi: rtw89: coex: Clear BT link weight when BT is disabled When a BT device is disabled, _update_bt_link_cnt() is no longer called for that device, so its link_weight[] retains stale values from the last active period. _set_bind_info() then computes a non-zero band score for the disabled device, which can lead to an incorrect tdd_bind.rf_band selection and ultimately wrong coexistence policy. Clear link_weight[] for any disabled BT device at the start of the _set_bind_info() loop so that stale scores are not carried forward. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260730060220.55844-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index d5b1c68539d9..ac3d0c63f930 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -8167,6 +8167,10 @@ static void _set_bind_info(struct rtw89_btc *btc, u8 type) memset(bd, 0, sizeof(*bd)); /* compare BT 2GHz/5GHz profile by link-weighting */ for (i = 0; i < BTC_BT_BMAX; i++) { + if (!cx->bt0.enable.now) + cx->bt0.link_weight[i] = 0; + if (!cx->bt1.enable.now) + cx->bt1.link_weight[i] = 0; link_weight[BTC_BT_1ST][i] = cx->bt0.link_weight[i]; link_weight[BTC_BT_2ND][i] = cx->bt1.link_weight[i]; link_weight[BTC_BT_EXT][i] = cx->bt_ext.link_weight[i]; From 4d9f08115092651fe5d99c1ea27b6600944ba03a Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Thu, 30 Jul 2026 14:02:17 +0800 Subject: [PATCH 365/403] wifi: rtw89: coex: Change Wi-Fi link_mode_v0 translating timing Logic should only get explicit link_mode only, to cover the old branch link_mode_v0 using (old branch firmware needed) should do translating after link_mode is settled. And the coexistence logic has already updated to new branch style, so the logic should use new link_mode, don't need to consider link_mode_v0. To prevent unexpected logic bug, refine the related logic. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260730060220.55844-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 247 ++++++++++------------ 1 file changed, 112 insertions(+), 135 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index ac3d0c63f930..2f06fcf0d67f 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -4095,15 +4095,10 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) if (bmode == BTC_WLINK_NOLINK) { return; - } else if (btc->ver->fwlrole == 10) { - if (rf_band == RTW89_BAND_5G) { - mode = BTC_WLINK_V0_5G; - } else if (rf_band == RTW89_BAND_2G && - bmode == BTC_WLINK_STA) { - mode = BTC_WLINK_V0_2G_STA; - } - } else { - mode = r->link_mode_v0; + } else if (rf_band == RTW89_BAND_5G) { + mode = BTC_WLINK_V0_5G; + } else if (rf_band == RTW89_BAND_2G && bmode == BTC_WLINK_STA) { + mode = BTC_WLINK_V0_2G_STA; } /* decide trx_para_level */ @@ -6212,14 +6207,15 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev) if (btc->ant_type == BTC_ANT_SHARED) { if (!(bt->run_patch_code && bt->enable.now)) is_btg = BTC_BTGCTRL_DISABLE; - else if (wl_rinfo->link_mode_v0 != BTC_WLINK_V0_5G) + else if (wl->rf_band_map[RTW89_MAC_0] & BIT(RTW89_BAND_2G) || + wl->rf_band_map[RTW89_MAC_1] & BIT(RTW89_BAND_2G)) is_btg = BTC_BTGCTRL_ENABLE; else is_btg = BTC_BTGCTRL_DISABLE; /* bb call ctrl_btg() in WL FW by slot */ if (!btc->ver->fcxosi && - wl_rinfo->link_mode_v0 == BTC_WLINK_V0_25G_MCC) + wl_rinfo->link_mode == BTC_WLINK_DB_MCC) is_btg = BTC_BTGCTRL_BB_GNT_FWCTRL; } @@ -7107,25 +7103,17 @@ void _update_dbcc_band(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) #define BTC_CHK_HANG_MAX 3 #define BTC_SCB_INV_VALUE GENMASK(31, 0) -static u8 _get_role_link_mode(struct rtw89_btc_wl_role_info *r, u8 role, bool notv10) +static u8 _get_role_link_mode(u8 role) { switch (role) { case RTW89_WIFI_ROLE_STATION: default: - if (notv10) - r->link_mode_v0 = BTC_WLINK_V0_2G_STA; return BTC_WLINK_STA; case RTW89_WIFI_ROLE_P2P_GO: - if (notv10) - r->link_mode_v0 = BTC_WLINK_V0_2G_GO; return BTC_WLINK_GO; case RTW89_WIFI_ROLE_P2P_CLIENT: - if (notv10) - r->link_mode_v0 = BTC_WLINK_V0_2G_GC; return BTC_WLINK_GC; case RTW89_WIFI_ROLE_AP: - if (notv10) - r->link_mode_v0 = BTC_WLINK_V0_2G_AP; return BTC_WLINK_AP; } } @@ -7144,13 +7132,12 @@ static bool _chk_role_ch_group(const struct rtw89_btc_chdef *r1, } static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch, - u8 *phy, u8 *role, u8 link_cnt, bool notv10) + u8 *phy, u8 *role, u8 link_cnt) { struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl; struct rtw89_btc_wl_role_info *rinfo = &wl->role_info; bool is_2g_ch_exist = false, is_multi_role_in_2g_phy = false; u8 j, k, dbcc_2g_cid = 0, dbcc_2g_cid2 = 0; - u8 mode; /* find out the 2G-PHY by connect-id ->ch */ for (j = 0; j < link_cnt; j++) { @@ -7161,12 +7148,8 @@ static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch, } /* If no any 2G-port exist, it's impossible because 5G-exclude */ - if (!is_2g_ch_exist) { - mode = BTC_WLINK_SB_MCC; - if (notv10) - rinfo->link_mode_v0 = BTC_WLINK_V0_5G; - return mode; - } + if (!is_2g_ch_exist) + return BTC_WLINK_SB_MCC; dbcc_2g_cid = j; rinfo->dbcc_2g_phy = phy[dbcc_2g_cid]; @@ -7178,7 +7161,7 @@ static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch, /* connect_cnt <= 2 */ if (link_cnt < BTC_TDMA_WLROLE_MAX) - return _get_role_link_mode(rinfo, role[dbcc_2g_cid], notv10); + return _get_role_link_mode(role[dbcc_2g_cid]); /* find the other-port in the 2G-PHY, ex: PHY-0:6G, PHY1: mcc/scc */ for (k = 0; k < link_cnt; k++) { @@ -7194,33 +7177,24 @@ static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch, /* Single-role in 2G-PHY */ if (!is_multi_role_in_2g_phy) - return _get_role_link_mode(rinfo, role[dbcc_2g_cid], notv10); + return _get_role_link_mode(role[dbcc_2g_cid]); /* 2-role in 2G-PHY */ - if (ch[dbcc_2g_cid2].center_ch > 14) { - if (notv10) - rinfo->link_mode_v0 = BTC_WLINK_V0_25G_MCC; + if (ch[dbcc_2g_cid2].center_ch > 14) return BTC_WLINK_DB_MCC; - } else if (_chk_role_ch_group(&ch[dbcc_2g_cid], &ch[dbcc_2g_cid2])) { - if (notv10) - rinfo->link_mode_v0 = BTC_WLINK_V0_2G_SCC; + else if (_chk_role_ch_group(&ch[dbcc_2g_cid], &ch[dbcc_2g_cid2])) return BTC_WLINK_SCC; - } else { - if (notv10) - rinfo->link_mode_v0 = BTC_WLINK_V0_2G_MCC; + else return BTC_WLINK_SB_MCC; - } } static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_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; - u8 rver = ver->fwlrole; if (hw_band == RTW89_PHY_1) p2p_exist = wl->role_info.p2p_exist_hb1; @@ -7240,20 +7214,6 @@ static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) } else { r->link_mode = BTC_WLINK_STA; } - - if (rver >= 10 && rver < 100) - break; - - if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { - r->link_mode_v0 = BTC_WLINK_V0_5G; - } else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1AP) { - r->link_mode_v0 = BTC_WLINK_V0_2G_GO; - } else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1CLIENT) { - if (wl->role_info.p2p_2g) - r->link_mode_v0 = BTC_WLINK_V0_2G_GC; - else - r->link_mode_v0 = BTC_WLINK_V0_2G_STA; - } break; case RTW89_MR_WTYPE_NONMLD_NONMLD: /* Non_MLO 2-role 2+0/0+2 */ case RTW89_MR_WTYPE_MLD1L1R_NONMLD: /* MLO only-1 link + P2P 2+0/0+2 */ @@ -7268,36 +7228,12 @@ static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) } else { r->link_mode = BTC_WLINK_SCC; } - - if (rver >= 10 && rver < 100) - break; - - if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { - r->link_mode_v0 = BTC_WLINK_V0_5G; - } else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_5GHZ || - mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_6GHZ) { - r->link_mode_v0 = BTC_WLINK_V0_25G_MCC; - } else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ) { - r->link_mode_v0 = BTC_WLINK_V0_2G_MCC; - } else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX1_2GHZ) { - r->link_mode_v0 = BTC_WLINK_V0_2G_SCC; - } break; case RTW89_MR_WTYPE_MLD2L1R: /* MLO_MLSR 2+0/0+2 */ if (p2p_exist) /* MLO_MLSR only support STA/GC */ r->link_mode = BTC_WLINK_GC; else r->link_mode = BTC_WLINK_STA; - - if (rver >= 10 && rver < 100) - break; - - if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) - r->link_mode_v0 = BTC_WLINK_V0_5G; - else if (wl->role_info.p2p_2g) - r->link_mode_v0 = BTC_WLINK_V0_2G_GC; - else - r->link_mode_v0 = BTC_WLINK_V0_2G_STA; break; case RTW89_MR_WTYPE_MLD2L1R_NONMLD: /* MLO_MLSR + P2P 2+0/0+2 */ case RTW89_MR_WTYPE_MLD2L2R_NONMLD: /* MLO_MLMR + P2P 1+1/2+2*/ @@ -7306,29 +7242,10 @@ static void _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) * 5G only -> TDMA slot switch by E5G */ r->link_mode = BTC_WLINK_DB_MCC; - - if (rver >= 10 && rver < 100) - break; - - r->link_mode_v0 = BTC_WLINK_V0_25G_MCC; break; case RTW89_MR_WTYPE_MLD2L2R: /* MLO_MLMR 1+1/2+2 */ /* MLMR only support STA now (2024) */ r->link_mode = BTC_WLINK_STA; - - if (rver >= 10 && rver < 100) - break; - - if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { - r->link_mode_v0 = BTC_WLINK_V0_5G; - } else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1AP) { - r->link_mode_v0 = BTC_WLINK_V0_2G_GO; - } else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1CLIENT) { - if (wl->role_info.p2p_2g) - r->link_mode_v0 = BTC_WLINK_V0_2G_GC; - else - r->link_mode_v0 = BTC_WLINK_V0_2G_STA; - } break; } } @@ -7494,8 +7411,6 @@ static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev) u8 cid_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; u8 cid_phy[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; bool b2g = false, b5g = false, outloop = false; - bool notv10 = rtwdev->btc.ver->fwlrole != 10; - u8 mode_v0 = BTC_WLINK_V0_NOLINK; u8 mode = BTC_WLINK_NOLINK; u8 cnt_2g = 0, cnt_5g = 0; u8 i, j, cnt = 0; @@ -7540,33 +7455,72 @@ static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev) /* Be careful to change the following sequence!! */ if (cnt == 0) { mode = BTC_WLINK_NOLINK; - mode_v0 = BTC_WLINK_V0_NOLINK; } else if (wl_rinfo->dbcc_en) { /* update pta_req_band to 2.4GHz HW-BAND for DBCC = 1*/ - mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, cnt, notv10); - mode_v0 = wl_rinfo->link_mode_v0; - } else if (!b2g && b5g && notv10) { - mode = _get_role_link_mode(wl_rinfo, cid_role[0], notv10); - mode_v0 = BTC_WLINK_V0_5G; + mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, cnt); + } else if (!b2g && b5g) { + mode = _get_role_link_mode(cid_role[0]); } else if (b2g && b5g) { mode = BTC_WLINK_DB_MCC; - mode_v0 = BTC_WLINK_V0_25G_MCC; } else if (cnt >= 2) { - if (_chk_role_ch_group(&cid_ch[0], &cid_ch[1])) { + if (_chk_role_ch_group(&cid_ch[0], &cid_ch[1])) mode = BTC_WLINK_SCC; - mode_v0 = BTC_WLINK_V0_2G_SCC; - } else { + else mode = BTC_WLINK_SB_MCC; - mode_v0 = BTC_WLINK_V0_2G_MCC; - } } else { - mode = _get_role_link_mode(wl_rinfo, cid_role[0], notv10); - mode_v0 = wl_rinfo->link_mode_v0; + mode = _get_role_link_mode(cid_role[0]); } wl_rinfo->link_mode = mode; wl_rinfo->link_mode_hb1 = mode; - wl_rinfo->link_mode_v0 = mode_v0; +} + +static void _sync_wl_link_mode_v0(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc_wl_role_info *r = &rtwdev->btc.cx.wl.role_info; + bool has_2g = false; + int i, j; + + for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER && !has_2g; i++) + for (j = 0; j < RTW89_MAC_NUM && !has_2g; j++) + if (r->rlink[i][j].connected && + r->rlink[i][j].rf_band == RTW89_BAND_2G) + has_2g = true; + + if (!has_2g && r->link_mode != BTC_WLINK_NOLINK) { + r->link_mode_v0 = BTC_WLINK_V0_5G; + return; + } + + switch (r->link_mode) { + case BTC_WLINK_NOLINK: + r->link_mode_v0 = BTC_WLINK_V0_NOLINK; + break; + case BTC_WLINK_STA: + r->link_mode_v0 = BTC_WLINK_V0_2G_STA; + break; + case BTC_WLINK_AP: + r->link_mode_v0 = BTC_WLINK_V0_2G_AP; + break; + case BTC_WLINK_GO: + r->link_mode_v0 = BTC_WLINK_V0_2G_GO; + break; + case BTC_WLINK_GC: + r->link_mode_v0 = BTC_WLINK_V0_2G_GC; + break; + case BTC_WLINK_SCC: + r->link_mode_v0 = BTC_WLINK_V0_2G_SCC; + break; + case BTC_WLINK_SB_MCC: + r->link_mode_v0 = BTC_WLINK_V0_2G_MCC; + break; + case BTC_WLINK_DB_MCC: + r->link_mode_v0 = BTC_WLINK_V0_25G_MCC; + break; + default: + r->link_mode_v0 = BTC_WLINK_V0_OTHER; + break; + } } static void _modify_role_link_mode(struct rtw89_dev *rtwdev, u8 hw_band) @@ -7746,6 +7700,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)) { @@ -7827,28 +7782,16 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid) { struct rtw89_btc_bt_link_info *bt_2g, *bt_56g; struct rtw89_btc *btc = &rtwdev->btc; - const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_cx *cx = &btc->cx; - struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_bt_info *bt; u32 val, any_bt_connect, any_bt_6g_connect = 0; - u8 id, id_start, id_stop, mode = 0; - bool bt_link_change = false; - bool lps_ctrl = false; + bool bt_link_change = false, lps_ctrl = false; + u8 id, id_start, id_stop; if (!rtwdev->chip->scbd || bid > BTC_ALL_BT) return; - if (ver->fwlrole == 10) { - if (wl->role_info.link_mode == BTC_WLINK_STA && - (dm->tdd_en && wl->rf_band_map[RTW89_MAC_0] & - BIT(RTW89_BAND_2G))) - mode = BTC_WLINK_V0_2G_STA; - } else { - mode = wl->role_info.link_mode_v0; - } - if (bid == BTC_ALL_BT) { id_start = BTC_BT_1ST; id_stop = BTC_BT_2ND; @@ -7937,10 +7880,9 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid) } /* if specific profile exist */ - if (((bt->link_info.a2dp_desc.exist || - bt->link_info.pan_desc.exist || - bt->link_info.hfp_desc.exist) && - mode == BTC_WLINK_V0_2G_STA) || + if (bt->link_info.a2dp_desc.exist || + bt->link_info.pan_desc.exist || + bt->link_info.hfp_desc.exist || bt->whql_test) lps_ctrl = true; @@ -8468,7 +8410,8 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt0; u8 mode = btc->dm.tdd_bind.wl_link_mode; - u8 mode_v0 = wl->role_info.link_mode_v0; + u8 mode_v0 = BTC_WLINK_V0_OTHER; + bool has_2ghz; lockdep_assert_wiphy(rtwdev->hw->wiphy); @@ -8601,6 +8544,40 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) mode_v0 = BTC_WLINK_V0_OTHER; break; } + } else { + /* No binding (out_of_band): derive mode_v0 from link_mode + rf_band_map */ + has_2ghz = wl->rf_band_map[RTW89_MAC_0] & BIT(RTW89_BAND_2G) || + wl->rf_band_map[RTW89_MAC_1] & BIT(RTW89_BAND_2G); + + if (!has_2ghz) { + mode_v0 = BTC_WLINK_V0_5G; + } else if (mode == BTC_WLINK_DB_MCC) { + mode_v0 = BTC_WLINK_V0_25G_MCC; + } else { + switch (mode) { + case BTC_WLINK_STA: + mode_v0 = BTC_WLINK_V0_2G_STA; + break; + case BTC_WLINK_AP: + mode_v0 = BTC_WLINK_V0_2G_AP; + break; + case BTC_WLINK_GO: + mode_v0 = BTC_WLINK_V0_2G_GO; + break; + case BTC_WLINK_GC: + mode_v0 = BTC_WLINK_V0_2G_GC; + break; + case BTC_WLINK_SCC: + mode_v0 = BTC_WLINK_V0_2G_SCC; + break; + case BTC_WLINK_SB_MCC: + mode_v0 = BTC_WLINK_V0_2G_MCC; + break; + default: + mode_v0 = BTC_WLINK_V0_OTHER; + break; + } + } } switch (mode_v0) { From dd7e70e76bf632c04a824e16fed034df74020109 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Thu, 30 Jul 2026 14:02:18 +0800 Subject: [PATCH 366/403] wifi: rtw89: coex: Port _update_bt_ctrl_lps() for BT profile-based LPS control The old lps_ctrl_scbd logic in _update_bt_scbd() blocked WiFi LPS only when mode == BTC_WLINK_V0_2G_STA, which created a dead-lock while WiFi already in LPS suppressed TDD binding so mode was never set to BTC_WLINK_V0_2G_STA, lps_ctrl_scbd stayed 0, and WiFi remained stuck in LPS. Port _update_bt_ctrl_lps() from the reference implementation to check BT profile existence (A2DP, HFP, PAN) for both radios directly, with out_of_band and freerun guards so LPS is only blocked when WiFi and Bluetooth actually share a band. Call it from both _update_bt_scbd() and _update_bt_info() so profile changes reported via either SCBD or BT info C2H correctly update lps_ctrl_scbd. Also remove the dead lps_ctrl_scbd_last field. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260730060220.55844-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 51 +++++++++++++++-------- drivers/net/wireless/realtek/rtw89/core.h | 1 - 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 2f06fcf0d67f..39dff9fdc1cd 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -3238,8 +3238,6 @@ static void _fw_set_policy(struct rtw89_dev *rtwdev, u16 policy_type, else btc->btc_ctrl_lps = dm->lps_ctrl_scbd; - dm->lps_ctrl_scbd_last = dm->lps_ctrl_scbd; - if (btc->btc_ctrl_lps == 1) rtw89_set_coex_ctrl_lps(rtwdev, btc->btc_ctrl_lps); @@ -7778,6 +7776,35 @@ void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work) } } +static void _update_bt_ctrl_lps(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_bt_info *bt0 = &btc->cx.bt0; + struct rtw89_btc_bt_info *bt1 = &btc->cx.bt1; + struct rtw89_btc_dm *dm = &btc->dm; + bool lps_ctrl = false; + + if (dm->out_of_band || dm->freerun) + lps_ctrl = false; + else if (bt0->whql_test || bt1->whql_test) + lps_ctrl = true; + else if (bt0->link_info.a2dp_desc.exist || + bt0->link_info.hfp_desc.exist || + bt0->link_info.pan_desc.exist) + lps_ctrl = true; + else if (bt1->link_info.a2dp_desc.exist || + bt1->link_info.hfp_desc.exist || + bt1->link_info.pan_desc.exist) + lps_ctrl = true; + + if (dm->lps_ctrl_scbd != lps_ctrl) { + dm->lps_ctrl_scbd = lps_ctrl; + dm->lps_ctrl_change = true; + } else { + dm->lps_ctrl_change = false; + } +} + static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid) { struct rtw89_btc_bt_link_info *bt_2g, *bt_56g; @@ -7786,7 +7813,7 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid) struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_bt_info *bt; u32 val, any_bt_connect, any_bt_6g_connect = 0; - bool bt_link_change = false, lps_ctrl = false; + bool bt_link_change = false; u8 id, id_start, id_stop; if (!rtwdev->chip->scbd || bid > BTC_ALL_BT) @@ -7879,25 +7906,12 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, u8 bid) bt_56g->status.map.connect = any_bt_6g_connect; } - /* if specific profile exist */ - if (bt->link_info.a2dp_desc.exist || - bt->link_info.pan_desc.exist || - bt->link_info.hfp_desc.exist || - bt->whql_test) - lps_ctrl = true; - - if (dm->lps_ctrl_scbd != lps_ctrl) { - dm->lps_ctrl_scbd = lps_ctrl; - bt_link_change = true; - dm->lps_ctrl_change = true; - } else { - dm->lps_ctrl_change = false; - } - bt->run_patch_code = !!(val & BTC_BSCB_PATCH_CODE); bt->scbd = val; } + _update_bt_ctrl_lps(rtwdev); + if (bt_link_change) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s: bt status change!!\n", __func__); @@ -9136,6 +9150,7 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 bid, u8 *buf, u32 len) RTW89_COEX_BT_DEVINFO_WORK_PERIOD); } + _update_bt_ctrl_lps(rtwdev); _run_coex(rtwdev, BTC_RSN_UPDATE_BT_INFO); } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 7f114c9a98ad..2b21d969ece7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4116,7 +4116,6 @@ struct rtw89_btc_dm { u8 fdd_en: 1; u8 tdd_en: 1; u8 lps_ctrl_scbd: 1; - u8 lps_ctrl_scbd_last: 1; u8 lps_ctrl_change: 1; u8 bis_tdma: 1; /* BIS TDMA mode active */ u8 scbd_write_instant; From 97b81c10fe77bebcd4bc638f92e7afc8f8f96248 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Thu, 30 Jul 2026 14:02:19 +0800 Subject: [PATCH 367/403] wifi: rtw89: coex: Update Wi-Fi/Bluetooth coexistence version to 9.24.1 The 9.24.1 coexistence version included firmware 0.35.111.X support for RTL8922A/D, 0.24.97.X support for RTL8852C, 0.29.133.X support for RTL8852B chip family. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260730060220.55844-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 39dff9fdc1cd..5a9dc4d8a00b 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -11,7 +11,7 @@ #include "ps.h" #include "reg.h" -#define RTW89_COEX_VERSION 0x09180013 +#define RTW89_COEX_VERSION 0x09180113 #define FCXDEF_STEP 50 /* MUST <= FCXMAX_STEP and match with wl fw*/ #define BTC_E2G_LIMIT_DEF 80 From 86c3125c75d79ee7fb4d045bb9ada4c5b067eacc Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 30 Jul 2026 14:02:20 +0800 Subject: [PATCH 368/403] wifi: rtw89: 8922d: enable Makefile and Kconfig for RTL8922DE RTL8922DE is a WiFi 7 chipset, supporting 2x2 2GHz/5GHz/6GHz 4096/1024-QAM 160MHz channels. As STA, AP and P2P modes work well, enable this chipset. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260730060220.55844-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/Kconfig | 15 +++++++++++++++ drivers/net/wireless/realtek/rtw89/Makefile | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index 7200a944260f..7c678dd1f6b3 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -41,6 +41,9 @@ config RTW89_8852C config RTW89_8922A tristate +config RTW89_8922D + tristate + config RTW89_8851BE tristate "Realtek 8851BE PCI wireless network (Wi-Fi 6) adapter" depends on PCI @@ -169,6 +172,18 @@ config RTW89_8922AU 802.11be USB wireless network (Wi-Fi 7) adapter supporting 2x2 2GHz/5GHz/6GHz 4096-QAM 160MHz channels. +config RTW89_8922DE + tristate "Realtek 8922DE/8922DE-VS PCI wireless network (Wi-Fi 7) adapter" + depends on PCI + select RTW89_CORE + select RTW89_PCI + select RTW89_8922D + help + Select this option will enable support for 8922DE/8922DE-VS chipset + + 802.11be PCIe wireless network (Wi-Fi 7) adapter + supporting 2x2 2GHz/5GHz/6GHz 4096/1024-QAM 160MHz channels. + config RTW89_DEBUG bool diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index 5536714e0268..a85b7785330c 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -91,6 +91,13 @@ rtw89_8922ae-objs := rtw8922ae.o obj-$(CONFIG_RTW89_8922AU) += rtw89_8922au.o rtw89_8922au-objs := rtw8922au.o +obj-$(CONFIG_RTW89_8922D) += rtw89_8922d.o +rtw89_8922d-objs := rtw8922d.o \ + rtw8922d_rfk.o + +obj-$(CONFIG_RTW89_8922DE) += rtw89_8922de.o +rtw89_8922de-objs := rtw8922de.o + rtw89_core-$(CONFIG_RTW89_DEBUG) += debug.o rtw89_core-$(CONFIG_RTW89_LEDS) += led.o rtw89_core-$(CONFIG_RTW89_LEDS_MC) += led_mc.o From 0ca7040f20998089d7004660ec819d4fef822fdf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 2 Aug 2026 17:33:39 +0200 Subject: [PATCH 369/403] Revert "wifi: mac80211: don't encrypt pre-auth (ETH_P_PREAUTH) frames" This reverts commit dd406779999fa2065ec6b7c4f80906b727041d2c. Never encrypting the frames broke a number of tests that do additional pre-authentication while already connected, and then the frames didn't go out correctly. Whatever this was intended to fix, this wasn't the right fix. Fixes: dd406779999f ("wifi: mac80211: don't encrypt pre-auth (ETH_P_PREAUTH) frames") Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0cf5f6ec75e6..14311aab7d18 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -557,9 +557,6 @@ ieee80211_tx_h_check_control_port_protocol(struct ieee80211_tx_data *tx) info->flags |= IEEE80211_TX_CTL_USE_MINRATE; } - if (tx->skb->protocol == htons(ETH_P_PREAUTH)) - info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; - return TX_CONTINUE; } From 996753804410e1eb6dee4a1481b8eb6bc389e337 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:34:57 +0200 Subject: [PATCH 370/403] wifi: cfg80211: pre-assign cookie for driver callbacks Having a single place for cookie assignment and keeping that responsibility in the cfg80211 subsystem is a logical choice as it handles the userspace nl80211 API. add_nan_func already does this: cfg80211 calls cfg80211_assign_cookie() before invoking the driver. Apply the same pattern to remain_on_channel, mgmt_tx, probe_peer and tx_control_port by pre-assigning the cookie in the nl80211 command handlers before the rdev_* call. For tx_control_port the cookie is only pre-assigned when the caller requests an ack (cookie pointer non-NULL). Drivers may still overwrite the value for now; subsequent patches will remove per-driver cookie generation. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-2-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 6 ++++-- net/wireless/nl80211.c | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index dcbb0e55c32e..ee395e3a021a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5108,7 +5108,8 @@ struct mgmt_frame_regs { * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). * * @probe_peer: probe a connected peer (AP: STA MAC required; STA: no MAC), - * must return a cookie that is later passed to cfg80211_probe_status(). + * must use the @cookie as provided which is later passed to + * cfg80211_probe_status(). * * @set_noack_map: Set the NoAck Map for the TIDs. * @@ -5219,7 +5220,8 @@ struct mgmt_frame_regs { * user space * * @tx_control_port: TX a control port frame (EAPoL). The noencrypt parameter - * tells the driver that the frame should not be encrypted. + * tells the driver that the frame should not be encrypted. When @cookie + * is non-NULL it is pre-assigned by cfg80211; drivers must not modify it. * * @get_ftm_responder_stats: Retrieve FTM responder statistics, if available. * Statistics should be cumulative, currently no way to reset is provided. diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8adbef5f0442..6ad1f9cf2dda 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14642,6 +14642,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, goto free_msg; } + cookie = cfg80211_assign_cookie(rdev); err = rdev_remain_on_channel(rdev, wdev, chandef.chan, duration, &cookie, rx_addr); @@ -14883,6 +14884,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } params.chan = chandef.chan; + cookie = cfg80211_assign_cookie(rdev); err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, &cookie); if (err) goto free_msg; @@ -16371,6 +16373,7 @@ static int nl80211_probe_peer(struct sk_buff *skb, struct genl_info *info) goto free_msg; } + cookie = cfg80211_assign_cookie(rdev); err = rdev_probe_peer(rdev, dev, addr, &cookie); if (err) goto free_msg; @@ -18586,6 +18589,8 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) link_id = nl80211_link_id_or_invalid(info->attrs); + if (!dont_wait_for_ack) + cookie = cfg80211_assign_cookie(rdev); err = rdev_tx_control_port(rdev, dev, buf, len, dest, cpu_to_be16(proto), noencrypt, link_id, dont_wait_for_ack ? NULL : &cookie); From fd6856890f3e90426b93c52161e22e8732a55606 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:34:58 +0200 Subject: [PATCH 371/403] wifi: mac80211: stop using ieee80211_mgmt_tx_cookie() Now that cfg80211 pre-assigns the cookie before calling into mac80211, stop calling ieee80211_mgmt_tx_cookie() in all affected paths: - ieee80211_start_roc_work(): for normal ROC use the pre-assigned value directly instead of generating a new one. - ieee80211_attach_ack_skb(): the cookie is already set by the caller; remove the ieee80211_mgmt_tx_cookie() call and store it in the ack SKB as-is. This covers both mgmt_tx and probe_peer since both call ieee80211_attach_ack_skb(). - ieee80211_mgmt_tx(): the dummy 0xffffffff assignment for the dont_wait_for_ack case is no longer needed; cfg80211_assign_cookie() guarantees a non-zero value which is sufficient for the internal ROC vs mgmt-tx distinction. - ieee80211_store_ack_skb(): same fix for the tx_control_port path. With no remaining callers, remove ieee80211_mgmt_tx_cookie() and the roc_cookie_counter field from struct ieee80211_local. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-3-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 14 -------------- net/mac80211/ieee80211_i.h | 3 --- net/mac80211/offchannel.c | 17 ++++------------- net/mac80211/tx.c | 4 +--- 4 files changed, 5 insertions(+), 33 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0a9247be26af..b6d02d2b28f5 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4831,19 +4831,6 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, return __ieee80211_channel_switch(wiphy, dev, params); } -u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local) -{ - lockdep_assert_wiphy(local->hw.wiphy); - - local->roc_cookie_counter++; - - /* wow, you wrapped 64 bits ... more likely a bug */ - if (WARN_ON(local->roc_cookie_counter == 0)) - local->roc_cookie_counter++; - - return local->roc_cookie_counter; -} - int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb, u64 *cookie, gfp_t gfp) { @@ -4868,7 +4855,6 @@ int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb, IEEE80211_SKB_CB(skb)->status_data_idr = 1; IEEE80211_SKB_CB(skb)->status_data = id; - *cookie = ieee80211_mgmt_tx_cookie(local); IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie; return 0; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a1ef88fe846d..3760319ab079 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1710,8 +1710,6 @@ struct ieee80211_local { struct list_head roc_list; struct wiphy_work hw_roc_start, hw_roc_done; unsigned long hw_roc_start_time; - u64 roc_cookie_counter; - struct idr ack_status_frames; spinlock_t ack_status_lock; @@ -1991,7 +1989,6 @@ u64 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata); void ieee80211_handle_queued_frames(struct ieee80211_local *local); -u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local); int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb, u64 *cookie, gfp_t gfp); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 2bceb73717c6..94be6497f259 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -602,14 +602,12 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, /* * cookie is either the roc cookie (for normal roc) - * or the SKB (for mgmt TX) + * or the mgmt_tx cookie; both are pre-assigned by cfg80211 */ - if (!txskb) { - roc->cookie = ieee80211_mgmt_tx_cookie(local); - *cookie = roc->cookie; - } else { + if (!txskb) + roc->cookie = *cookie; + else roc->mgmt_tx_cookie = *cookie; - } req = wiphy_dereference(local->hw.wiphy, local->scan_req); @@ -1021,13 +1019,6 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, kfree_skb(skb); goto out_unlock; } - } else { - /* Assign a dummy non-zero cookie, it's not sent to - * userspace in this case but we rely on its value - * internally in the need_offchan case to distinguish - * mgmt-tx from remain-on-channel. - */ - *cookie = 0xffffffff; } if (!need_offchan) { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 14311aab7d18..b62a41bda49d 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2602,10 +2602,8 @@ static u16 ieee80211_store_ack_skb(struct ieee80211_local *local, if (id >= 0) { info_id = id; *info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; - if (cookie) { - *cookie = ieee80211_mgmt_tx_cookie(local); + if (cookie) IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie; - } } else { kfree_skb(ack_skb); } From 5c4b13cb50cae52afb34d043b8599ff767135017 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:34:59 +0200 Subject: [PATCH 372/403] wifi: ath6kl: use pre-assigned cookie for remain_on_channel and mgmt_tx Stop generating cookies in ath6kl_remain_on_channel() and ath6kl_mgmt_tx(). cfg80211 now pre-assigns the cookie before calling into the driver. For remain_on_channel, store the pre-assigned cookie in vif->last_roc_id. Widen last_roc_id and last_cancel_roc_id from u32 to u64 to hold the full 64-bit cookie value. For mgmt_tx, store the pre-assigned cookie in wmi->last_mgmt_tx_cookie so the firmware TX status event handler can pass the correct cookie to cfg80211_mgmt_tx_status(). Thread the cookie through the powersave queue (ath6kl_mgmt_buff) so it is available when the frame is eventually dequeued and sent. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-4-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 18 ++++++------------ drivers/net/wireless/ath/ath6kl/core.h | 5 +++-- drivers/net/wireless/ath/ath6kl/main.c | 1 + drivers/net/wireless/ath/ath6kl/txrx.c | 1 + drivers/net/wireless/ath/ath6kl/wmi.c | 2 +- drivers/net/wireless/ath/ath6kl/wmi.h | 1 + 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index ecde91159b54..e9d6e6a53d7d 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3038,16 +3038,10 @@ static int ath6kl_remain_on_channel(struct wiphy *wiphy, { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); - u32 id; /* TODO: if already pending or ongoing remain-on-channel, * return -EBUSY */ - id = ++vif->last_roc_id; - if (id == 0) { - /* Do not use 0 as the cookie value */ - id = ++vif->last_roc_id; - } - *cookie = id; + vif->last_roc_id = *cookie; return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx, chan->center_freq, duration); @@ -3106,6 +3100,7 @@ static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif, static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif, u32 id, + u64 cookie, u32 freq, u32 wait, const u8 *buf, @@ -3138,6 +3133,7 @@ static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif, INIT_LIST_HEAD(&mgmt_buf->list); mgmt_buf->id = id; + mgmt_buf->cookie = cookie; mgmt_buf->freq = freq; mgmt_buf->wait = wait; mgmt_buf->len = len; @@ -3222,7 +3218,6 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, * Send Probe Response frame in GO mode using a separate WMI * command to allow the target to fill in the generic IEs. */ - *cookie = 0; /* TX status not supported */ return ath6kl_send_go_probe_resp(vif, buf, len, freq); } @@ -3235,16 +3230,15 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, id = vif->send_action_id++; } - *cookie = id; - /* AP mode Power saving processing */ if (vif->nw_type == AP_NETWORK) { - queued = ath6kl_mgmt_powersave_ap(vif, id, freq, wait, buf, len, - &more_data, no_cck); + queued = ath6kl_mgmt_powersave_ap(vif, id, *cookie, freq, wait, + buf, len, &more_data, no_cck); if (queued) return 0; } + ar->wmi->last_mgmt_tx_cookie = *cookie; return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, freq, wait, buf, len, no_cck); } diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 77e052336eb5..a0b236eeeab5 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -404,6 +404,7 @@ struct ath6kl_mgmt_buff { u32 freq; u32 wait; u32 id; + u64 cookie; bool no_cck; size_t len; u8 buf[]; @@ -631,8 +632,8 @@ struct ath6kl_vif { struct cfg80211_scan_request *scan_req; enum sme_state sme_state; int reconnect_flag; - u32 last_roc_id; - u32 last_cancel_roc_id; + u64 last_roc_id; + u64 last_cancel_roc_id; u32 send_action_id; bool probe_req_report; u16 assoc_bss_beacon_int; diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 8afc6589fc51..9c023d7ce305 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -898,6 +898,7 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid) spin_unlock_bh(&conn->psq_lock); conn->sta_flags |= STA_PS_POLLED; + ar->wmi->last_mgmt_tx_cookie = mgmt_buf->cookie; ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, mgmt_buf->id, mgmt_buf->freq, mgmt_buf->wait, mgmt_buf->buf, diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index d81825413906..609d458ca6c5 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1469,6 +1469,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) spin_unlock_bh(&conn->psq_lock); idx = vif->fw_vif_idx; + ar->wmi->last_mgmt_tx_cookie = mgmt->cookie; ath6kl_wmi_send_mgmt_cmd(ar->wmi, idx, mgmt->id, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 6c29f0bcec9f..2140fddba8ba 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -596,7 +596,7 @@ static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len, ath6kl_dbg(ATH6KL_DBG_WMI, "tx_status: id=%x ack_status=%u\n", id, ev->ack_status); if (wmi->last_mgmt_tx_frame) { - cfg80211_mgmt_tx_status(&vif->wdev, id, + cfg80211_mgmt_tx_status(&vif->wdev, wmi->last_mgmt_tx_cookie, wmi->last_mgmt_tx_frame, wmi->last_mgmt_tx_frame_len, !!ev->ack_status, GFP_ATOMIC); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 8fbece3fdad9..89ce2abbb1cd 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -125,6 +125,7 @@ struct wmi { u8 *last_mgmt_tx_frame; size_t last_mgmt_tx_frame_len; + u64 last_mgmt_tx_cookie; u8 saved_pwr_mode; }; From 70557cadc828cbfd17d79481f5bcfbace7dc648c Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:35:00 +0200 Subject: [PATCH 373/403] wifi: wil6210: use pre-assigned cookie for remain_on_channel, mgmt_tx and probe_peer Stop overwriting the pre-assigned cookie in wil_p2p_listen(), wil_cfg80211_mgmt_tx(), and wil_cfg80211_probe_peer(). For remain_on_channel, store the pre-assigned cookie in p2p->cookie instead of incrementing it. All cancel and expiry callbacks already read from p2p->cookie so they pick up the correct value. For mgmt_tx, remove the defensive "cookie ? *cookie : 0" guard; cfg80211 guarantees a non-NULL cookie pointer. For probe_peer, store the pre-assigned cookie in req->cookie instead of the CID value. The CID is still available via req->cid for STA lookup in wil_probe_client_handle(). Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-5-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wil6210/cfg80211.c | 6 ++---- drivers/net/wireless/ath/wil6210/p2p.c | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 5f2bd9a31faf..6ebd340c8ef7 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1488,8 +1488,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, */ tx_status = (rc == 0); rc = (rc == -EAGAIN) ? 0 : rc; - cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len, - tx_status, GFP_KERNEL); + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, tx_status, GFP_KERNEL); return rc; } @@ -2399,13 +2398,12 @@ static int wil_cfg80211_probe_peer(struct wiphy *wiphy, return -ENOMEM; req->cid = cid; - req->cookie = cid; + req->cookie = *cookie; mutex_lock(&vif->probe_client_mutex); list_add_tail(&req->list, &vif->probe_client_pending); mutex_unlock(&vif->probe_client_mutex); - *cookie = req->cookie; queue_work(wil->wq_service, &vif->probe_client_worker); return 0; } diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c index f20caf1a3905..6d2cadca259f 100644 --- a/drivers/net/wireless/ath/wil6210/p2p.c +++ b/drivers/net/wireless/ath/wil6210/p2p.c @@ -144,7 +144,7 @@ int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, } memcpy(&p2p->listen_chan, chan, sizeof(*chan)); - *cookie = ++p2p->cookie; + p2p->cookie = *cookie; p2p->listen_duration = duration; mutex_lock(&wil->vif_mutex); From c277330183a766e9c4531e56d54e391e980a0fc2 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:35:01 +0200 Subject: [PATCH 374/403] wifi: brcmfmac: use pre-assigned cookie for remain_on_channel and mgmt_tx Stop generating cookies in brcmf_p2p_remain_on_channel() and brcmf_cfg80211_mgmt_tx(). For remain_on_channel, remove the cookie increment from brcmf_p2p_discover_listen() and store the pre-assigned cookie in p2p->remain_on_channel_cookie. The expiry callback in brcmf_p2p_notify_listen_complete() already reads from that field. For mgmt_tx, remove the "*cookie = 0" assignments in brcmf_cfg80211_mgmt_tx() and the cyw extension. The pre-assigned cookie is then correctly passed to cfg80211_mgmt_tx_status() which already uses *cookie. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-6-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 2 -- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c | 1 - drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 6 ++---- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 6b2c34e3a796..03a7a4c18279 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -5573,8 +5573,6 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, brcmf_dbg(TRACE, "Enter\n"); - *cookie = 0; - mgmt = (const struct ieee80211_mgmt *)buf; if (!ieee80211_is_mgmt(mgmt->frame_control)) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c index 873754be5174..5c7a475cb5e5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c @@ -123,7 +123,6 @@ int brcmf_cyw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, if (!ieee80211_is_auth(mgmt->frame_control)) return brcmf_cfg80211_mgmt_tx(wiphy, wdev, params, cookie); - *cookie = 0; vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); reinit_completion(&vif->mgmt_tx); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index c7d7b35ab125..3d7bf25f1985 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -979,10 +979,8 @@ brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration) p2p->cfg->d11inf.encchspec(&ch); err = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_LISTEN, ch.chspec, (u16)duration); - if (!err) { + if (!err) set_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status); - p2p->remain_on_channel_cookie++; - } exit: return err; } @@ -1020,7 +1018,7 @@ int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, goto exit; memcpy(&p2p->remain_on_channel, channel, sizeof(*channel)); - *cookie = p2p->remain_on_channel_cookie; + p2p->remain_on_channel_cookie = *cookie; cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL); exit: From 63d42f17296c6fef30bd4ff069440c691e435c0b Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:35:02 +0200 Subject: [PATCH 375/403] wifi: mwifiex: use pre-assigned cookie for remain_on_channel and mgmt_tx Stop generating cookies via get_random_u32() in mwifiex_cfg80211_remain_on_channel() and mwifiex_cfg80211_mgmt_tx(). Use the pre-assigned cookie from cfg80211 instead. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-7-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 8ec2d22a8c33..102321cf8542 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -259,7 +259,6 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, tx_info->pkt_len = pkt_len; mwifiex_form_mgmt_frame(skb, buf, len); - *cookie = get_random_u32() | 1; if (ieee80211_is_action(mgmt->frame_control)) skb = mwifiex_clone_skb_for_tx_status(priv, @@ -326,7 +325,6 @@ mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, duration); if (!ret) { - *cookie = get_random_u32() | 1; priv->roc_cfg.cookie = *cookie; priv->roc_cfg.chan = *chan; From cdda9756ee41197da77721d1715aea2df833cb96 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:35:03 +0200 Subject: [PATCH 376/403] wifi: wilc1000: use pre-assigned cookie for remain_on_channel and mgmt_tx Stop generating cookies by incrementing inc_roc_cookie in remain_on_channel() and via get_random_u32() in mgmt_tx(). Use the pre-assigned cookie from cfg80211 instead. Remove the now-unused id local variable from remain_on_channel(). Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-8-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- drivers/net/wireless/microchip/wilc1000/cfg80211.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.c b/drivers/net/wireless/microchip/wilc1000/cfg80211.c index 6654fce4ded8..7c19ad34fda5 100644 --- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c +++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c @@ -1106,18 +1106,13 @@ static int remain_on_channel(struct wiphy *wiphy, int ret = 0; struct wilc_vif *vif = netdev_priv(wdev->netdev); struct wilc_priv *priv = &vif->priv; - u64 id; if (wdev->iftype == NL80211_IFTYPE_AP) { netdev_dbg(vif->ndev, "Required while in AP mode\n"); return ret; } - id = ++priv->inc_roc_cookie; - if (id == 0) - id = ++priv->inc_roc_cookie; - - ret = wilc_remain_on_channel(vif, id, chan->hw_value, + ret = wilc_remain_on_channel(vif, *cookie, chan->hw_value, wilc_wfi_remain_on_channel_expired); if (ret) return ret; @@ -1125,8 +1120,7 @@ static int remain_on_channel(struct wiphy *wiphy, vif->wilc->op_ch = chan->hw_value; priv->remain_on_ch_params.listen_ch = chan; - priv->remain_on_ch_params.listen_cookie = id; - *cookie = id; + priv->remain_on_ch_params.listen_cookie = *cookie; priv->p2p_listen_state = true; priv->remain_on_ch_params.listen_duration = duration; @@ -1170,7 +1164,6 @@ static int mgmt_tx(struct wiphy *wiphy, const u8 *vendor_ie; int ret = 0; - *cookie = get_random_u32(); priv->tx_cookie = *cookie; mgmt = (const struct ieee80211_mgmt *)buf; From ef15c65f89bd5af397cd762b1bdc50a46edbedb1 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:35:04 +0200 Subject: [PATCH 377/403] wifi: nxpwifi: use pre-assigned cookie for remain_on_channel and mgmt_tx Stop calling nxpwifi_roc_cookie() to generate cookies in nxpwifi_cfg80211_remain_on_channel() and nxpwifi_cfg80211_mgmt_tx(). Use the pre-assigned cookie from cfg80211 instead. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-9-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c index 4f9e20f72811..198174ac754e 100644 --- a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c +++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c @@ -212,7 +212,6 @@ nxpwifi_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, tx_info->pkt_len = pkt_len; nxpwifi_form_mgmt_frame(skb, buf, len); - *cookie = nxpwifi_roc_cookie(priv->adapter); if (ieee80211_is_action(mgmt->frame_control)) skb = nxpwifi_clone_skb_for_tx_status(priv, @@ -277,7 +276,6 @@ nxpwifi_cfg80211_remain_on_channel(struct wiphy *wiphy, duration); if (!ret) { - *cookie = nxpwifi_roc_cookie(adapter); priv->roc_cfg.cookie = *cookie; priv->roc_cfg.chan = *chan; From b6dfce6c0b97cc0c791c3fe384f68d384482023e Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:35:05 +0200 Subject: [PATCH 378/403] wifi: qtnfmac: use pre-assigned cookie for mgmt_tx Stop overwriting *cookie with a random value in qtnf_mgmt_tx(). The internal firmware frame identifier (short_cookie) is unchanged. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-10-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index 9e44c85d2051..284bc61c46d0 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -454,8 +454,6 @@ qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, u16 flags = 0; u16 freq; - *cookie = short_cookie; - if (params->offchan) flags |= QLINK_FRAME_TX_FLAG_OFFCHAN; From 51a87a1cb61cde446429e355cb66b3dbe4851a8e Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:35:06 +0200 Subject: [PATCH 379/403] wifi: rtl8723bs: use pre-assigned cookie for mgmt_tx Stop using params->buf address as cookie value and simply pass the pre-assigned cookie in frame tx status. This implementation seems to fire-and-forget the transmitted frame as the cookie is not used in any other way. Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-11-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 6a97afd89dc7..0e6b40336123 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -2552,9 +2552,6 @@ static int cfg80211_rtw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, padapter = rtw_netdev_priv(ndev); - /* cookie generation */ - *cookie = (unsigned long)buf; - /* indicate ack before issue frame to avoid racing with rsp frame */ rtw_cfg80211_mgmt_tx_status(padapter, *cookie, buf, len, ack, GFP_KERNEL); From 914781c72813989b512609a51f8abb68417f722d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:35:07 +0200 Subject: [PATCH 380/403] wifi: cfg80211: convert cookie output to input parameter The remain_on_channel, mgmt_tx, and probe_peer ops previously used a u64 *cookie output parameter. Now that cfg80211 pre-assigns the cookie value before invoking drivers, the parameter conveys a value from caller to driver, not the other way around. Convert it to a plain u64 input parameter across the ops struct (cfg80211.h), rdev-ops.h wrappers, nl80211.c/mlme.c call sites, mac80211, and all driver implementations. The tx_control_port op is excluded: its cookie pointer is nullable (passed as NULL when dont_wait_for_ack is set), so the nullable pointer semantics are still required. Internal mac80211 helpers ieee80211_start_roc_work() and ieee80211_attach_ack_skb() still take u64 *cookie because they assign to the pointee; their callers now pass &cookie to take the address of the local value parameter. wil6210's internal wil_p2p_listen() is also updated to take u64 cookie since it is called directly from the remain_on_channel callback. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-12-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 10 +++++----- drivers/net/wireless/ath/wil6210/cfg80211.c | 10 +++++----- drivers/net/wireless/ath/wil6210/debugfs.c | 2 +- drivers/net/wireless/ath/wil6210/p2p.c | 6 +++--- drivers/net/wireless/ath/wil6210/wil6210.h | 4 ++-- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 10 +++++----- .../broadcom/brcm80211/brcmfmac/cfg80211.h | 2 +- .../broadcom/brcm80211/brcmfmac/cyw/core.c | 7 +++---- .../wireless/broadcom/brcm80211/brcmfmac/p2p.c | 6 +++--- .../wireless/broadcom/brcm80211/brcmfmac/p2p.h | 2 +- .../net/wireless/marvell/mwifiex/cfg80211.c | 18 +++++++++--------- .../net/wireless/microchip/wilc1000/cfg80211.c | 12 ++++++------ drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 18 +++++++++--------- .../net/wireless/quantenna/qtnfmac/cfg80211.c | 2 +- .../staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 4 ++-- include/net/cfg80211.h | 6 +++--- net/mac80211/cfg.c | 4 ++-- net/mac80211/ieee80211_i.h | 4 ++-- net/mac80211/offchannel.c | 10 +++++----- net/wireless/core.h | 2 +- net/wireless/mlme.c | 2 +- net/wireless/nl80211.c | 6 +++--- net/wireless/rdev-ops.h | 12 ++++++------ 23 files changed, 79 insertions(+), 80 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index e9d6e6a53d7d..34025d64d615 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3034,14 +3034,14 @@ static int ath6kl_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, unsigned int duration, - u64 *cookie, const u8 *rx_addr) + u64 cookie, const u8 *rx_addr) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); /* TODO: if already pending or ongoing remain-on-channel, * return -EBUSY */ - vif->last_roc_id = *cookie; + vif->last_roc_id = cookie; return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, vif->fw_vif_idx, chan->center_freq, duration); @@ -3186,7 +3186,7 @@ static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len) } static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 cookie) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); @@ -3232,13 +3232,13 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* AP mode Power saving processing */ if (vif->nw_type == AP_NETWORK) { - queued = ath6kl_mgmt_powersave_ap(vif, id, *cookie, freq, wait, + queued = ath6kl_mgmt_powersave_ap(vif, id, cookie, freq, wait, buf, len, &more_data, no_cck); if (queued) return 0; } - ar->wmi->last_mgmt_tx_cookie = *cookie; + ar->wmi->last_mgmt_tx_cookie = cookie; return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, freq, wait, buf, len, no_cck); } diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 6ebd340c8ef7..9ac33d827f80 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1432,7 +1432,7 @@ static int wil_cfg80211_set_wiphy_params(struct wiphy *wiphy, int radio_idx, int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, - u64 *cookie) + u64 cookie) { const u8 *buf = params->buf; size_t len = params->len; @@ -1488,7 +1488,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, */ tx_status = (rc == 0); rc = (rc == -EAGAIN) ? 0 : rc; - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, tx_status, GFP_KERNEL); + cfg80211_mgmt_tx_status(wdev, cookie, buf, len, tx_status, GFP_KERNEL); return rc; } @@ -1734,7 +1734,7 @@ static int wil_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, unsigned int duration, - u64 *cookie, const u8 *rx_addr) + u64 cookie, const u8 *rx_addr) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); int rc; @@ -2380,7 +2380,7 @@ void wil_probe_client_flush(struct wil6210_vif *vif) static int wil_cfg80211_probe_peer(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u64 *cookie) + const u8 *peer, u64 cookie) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); struct wil6210_vif *vif = ndev_to_vif(dev); @@ -2398,7 +2398,7 @@ static int wil_cfg80211_probe_peer(struct wiphy *wiphy, return -ENOMEM; req->cid = cid; - req->cookie = *cookie; + req->cookie = cookie; mutex_lock(&vif->probe_client_mutex); list_add_tail(&req->list, &vif->probe_client_pending); diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index b8cb736a7185..e06e45323272 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -985,7 +985,7 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf, params.buf = frame; params.len = len; - rc = wil_cfg80211_mgmt_tx(wiphy, wdev, ¶ms, NULL); + rc = wil_cfg80211_mgmt_tx(wiphy, wdev, ¶ms, 0); kfree(frame); wil_info(wil, "-> %d\n", rc); diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c index 6d2cadca259f..acd8a2e35103 100644 --- a/drivers/net/wireless/ath/wil6210/p2p.c +++ b/drivers/net/wireless/ath/wil6210/p2p.c @@ -124,7 +124,7 @@ int wil_p2p_search(struct wil6210_vif *vif, int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, unsigned int duration, struct ieee80211_channel *chan, - u64 *cookie) + u64 cookie) { struct wil6210_vif *vif = wdev_to_vif(wil, wdev); struct wil_p2p_info *p2p = &vif->p2p; @@ -144,7 +144,7 @@ int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, } memcpy(&p2p->listen_chan, chan, sizeof(*chan)); - p2p->cookie = *cookie; + p2p->cookie = cookie; p2p->listen_duration = duration; mutex_lock(&wil->vif_mutex); @@ -166,7 +166,7 @@ int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, if (vif->mid == 0) wil->radio_wdev = wdev; - cfg80211_ready_on_channel(wdev, *cookie, chan, duration, + cfg80211_ready_on_channel(wdev, cookie, chan, duration, GFP_KERNEL); out: diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 31e107c81e2d..f8e60be631ca 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1301,7 +1301,7 @@ int wil_p2p_search(struct wil6210_vif *vif, struct cfg80211_scan_request *request); int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, unsigned int duration, struct ieee80211_channel *chan, - u64 *cookie); + u64 cookie); u8 wil_p2p_stop_discovery(struct wil6210_vif *vif); int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie); void wil_p2p_listen_expired(struct work_struct *work); @@ -1317,7 +1317,7 @@ int wmi_stop_discovery(struct wil6210_vif *vif); int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, - u64 *cookie); + u64 cookie); void wil_cfg80211_ap_recovery(struct wil6210_priv *wil); int wil_cfg80211_iface_combinations_from_fw( struct wil6210_priv *wil, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 03a7a4c18279..872c48806d09 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -5554,7 +5554,7 @@ brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy, int brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 cookie) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct ieee80211_channel *chan = params->chan; @@ -5603,7 +5603,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, BRCMF_VNDR_IE_PRBRSP_FLAG, &buf[ie_offset], ie_len); - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, + cfg80211_mgmt_tx_status(wdev, cookie, buf, len, true, GFP_KERNEL); } else if (ieee80211_is_action(mgmt->frame_control)) { if (len > BRCMF_FIL_ACTION_FRAME_SIZE + DOT11_MGMT_HDR_LEN) { @@ -5619,7 +5619,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, } action_frame = &af_params->action_frame; /* Add the packet Id */ - action_frame->packet_id = cpu_to_le32(*cookie); + action_frame->packet_id = cpu_to_le32(cookie); /* Add BSSID */ memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN); memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); @@ -5647,12 +5647,12 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, le16_to_cpu(action_frame->len)); brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, channel=%d\n", - *cookie, le16_to_cpu(action_frame->len), + cookie, le16_to_cpu(action_frame->len), le32_to_cpu(af_params->channel)); ack = brcmf_p2p_send_action_frame(vif->ifp, af_params); - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, + cfg80211_mgmt_tx_status(wdev, cookie, buf, len, ack, GFP_KERNEL); free: kfree(af_params); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 6ceb30142905..63e534523f51 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -497,6 +497,6 @@ void brcmf_cfg80211_free_vif(struct net_device *ndev); int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags); int brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie); + struct cfg80211_mgmt_tx_params *params, u64 cookie); #endif /* BRCMFMAC_CFG80211_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c index 5c7a475cb5e5..545eb9aae966 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c @@ -100,7 +100,7 @@ static int brcmf_cyw_activate_events(struct brcmf_if *ifp) static int brcmf_cyw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 cookie) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct ieee80211_channel *chan = params->chan; @@ -154,7 +154,7 @@ int brcmf_cyw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, memcpy(&mf_params->da[0], &mgmt->da[0], ETH_ALEN); memcpy(&mf_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); - mf_params->packet_id = cpu_to_le32(*cookie); + mf_params->packet_id = cpu_to_le32(cookie); memcpy(mf_params->data, &buf[DOT11_MGMT_HDR_LEN], le16_to_cpu(mf_params->len)); @@ -186,8 +186,7 @@ int brcmf_cyw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, } tx_status: - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, - GFP_KERNEL); + cfg80211_mgmt_tx_status(wdev, cookie, buf, len, ack, GFP_KERNEL); free: kfree(mf_params); return err; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index 3d7bf25f1985..32a015d6b769 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -998,7 +998,7 @@ brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration) */ int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *channel, - unsigned int duration, u64 *cookie, + unsigned int duration, u64 cookie, const u8 *rx_addr) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); @@ -1018,8 +1018,8 @@ int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, goto exit; memcpy(&p2p->remain_on_channel, channel, sizeof(*channel)); - p2p->remain_on_channel_cookie = *cookie; - cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL); + p2p->remain_on_channel_cookie = cookie; + cfg80211_ready_on_channel(wdev, cookie, channel, duration, GFP_KERNEL); exit: return err; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h index 9f3f01ade2b7..707800949170 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h @@ -157,7 +157,7 @@ int brcmf_p2p_scan_prep(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif); int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *channel, - unsigned int duration, u64 *cookie, + unsigned int duration, u64 cookie, const u8 *rx_addr); int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp, const struct brcmf_event_msg *e, diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 102321cf8542..7a1ba32f1fb3 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -196,7 +196,7 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) */ static int mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 cookie) { const u8 *buf = params->buf; size_t len = params->len; @@ -263,9 +263,9 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, if (ieee80211_is_action(mgmt->frame_control)) skb = mwifiex_clone_skb_for_tx_status(priv, skb, - MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, cookie); + MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, &cookie); else - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, + cfg80211_mgmt_tx_status(wdev, cookie, buf, len, true, GFP_ATOMIC); mwifiex_queue_tx_pkt(priv, skb); @@ -303,13 +303,13 @@ static int mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie, + unsigned int duration, u64 cookie, const u8 *rx_addr) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); int ret; - if (!chan || !cookie) { + if (!chan) { mwifiex_dbg(priv->adapter, ERROR, "Invalid parameter for ROC\n"); return -EINVAL; } @@ -325,14 +325,14 @@ mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, duration); if (!ret) { - priv->roc_cfg.cookie = *cookie; + priv->roc_cfg.cookie = cookie; priv->roc_cfg.chan = *chan; - cfg80211_ready_on_channel(wdev, *cookie, chan, + cfg80211_ready_on_channel(wdev, cookie, chan, duration, GFP_ATOMIC); mwifiex_dbg(priv->adapter, INFO, - "info: ROC, cookie = 0x%llx\n", *cookie); + "info: ROC, cookie = 0x%llx\n", cookie); } return ret; @@ -4558,7 +4558,7 @@ mwifiex_cfg80211_disassociate(struct wiphy *wiphy, static int mwifiex_cfg80211_probe_peer(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, - u64 *cookie) + u64 cookie) { /* hostapd looks for NL80211_CMD_PROBE_CLIENT support; otherwise, * it requires monitor-mode support (which mwifiex doesn't support). diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.c b/drivers/net/wireless/microchip/wilc1000/cfg80211.c index 7c19ad34fda5..bb2748a19329 100644 --- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c +++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c @@ -1100,7 +1100,7 @@ static void wilc_wfi_remain_on_channel_expired(struct wilc_vif *vif, u64 cookie) static int remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie, + unsigned int duration, u64 cookie, const u8 *rx_addr) { int ret = 0; @@ -1112,7 +1112,7 @@ static int remain_on_channel(struct wiphy *wiphy, return ret; } - ret = wilc_remain_on_channel(vif, *cookie, chan->hw_value, + ret = wilc_remain_on_channel(vif, cookie, chan->hw_value, wilc_wfi_remain_on_channel_expired); if (ret) return ret; @@ -1120,11 +1120,11 @@ static int remain_on_channel(struct wiphy *wiphy, vif->wilc->op_ch = chan->hw_value; priv->remain_on_ch_params.listen_ch = chan; - priv->remain_on_ch_params.listen_cookie = *cookie; + priv->remain_on_ch_params.listen_cookie = cookie; priv->p2p_listen_state = true; priv->remain_on_ch_params.listen_duration = duration; - cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL); + cfg80211_ready_on_channel(wdev, cookie, chan, duration, GFP_KERNEL); mod_timer(&vif->hif_drv->remain_on_ch_timer, jiffies + msecs_to_jiffies(duration + 1000)); @@ -1147,7 +1147,7 @@ static int cancel_remain_on_channel(struct wiphy *wiphy, static int mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, - u64 *cookie) + u64 cookie) { struct ieee80211_channel *chan = params->chan; unsigned int wait = params->wait; @@ -1164,7 +1164,7 @@ static int mgmt_tx(struct wiphy *wiphy, const u8 *vendor_ie; int ret = 0; - priv->tx_cookie = *cookie; + priv->tx_cookie = cookie; mgmt = (const struct ieee80211_mgmt *)buf; if (!ieee80211_is_mgmt(mgmt->frame_control)) diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c index 198174ac754e..c820f08d2835 100644 --- a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c +++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c @@ -150,7 +150,7 @@ nxpwifi_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) /* cfg80211 operation handler to transmit a management frame. */ static int nxpwifi_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 cookie) { const u8 *buf = params->buf; size_t len = params->len; @@ -216,9 +216,9 @@ nxpwifi_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, if (ieee80211_is_action(mgmt->frame_control)) skb = nxpwifi_clone_skb_for_tx_status(priv, skb, - NXPWIFI_BUF_FLAG_ACTION_TX_STATUS, cookie); + NXPWIFI_BUF_FLAG_ACTION_TX_STATUS, &cookie); else - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, + cfg80211_mgmt_tx_status(wdev, cookie, buf, len, true, GFP_ATOMIC); nxpwifi_queue_tx_pkt(priv, skb); @@ -253,14 +253,14 @@ static int nxpwifi_cfg80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie, + unsigned int duration, u64 cookie, const u8 *rx_addr) { struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev); struct nxpwifi_adapter *adapter = priv->adapter; int ret; - if (!chan || !cookie) { + if (!chan) { nxpwifi_dbg(adapter, ERROR, "Invalid parameter for ROC\n"); return -EINVAL; } @@ -276,14 +276,14 @@ nxpwifi_cfg80211_remain_on_channel(struct wiphy *wiphy, duration); if (!ret) { - priv->roc_cfg.cookie = *cookie; + priv->roc_cfg.cookie = cookie; priv->roc_cfg.chan = *chan; - cfg80211_ready_on_channel(wdev, *cookie, chan, + cfg80211_ready_on_channel(wdev, cookie, chan, duration, GFP_ATOMIC); nxpwifi_dbg(adapter, INFO, - "info: ROC, cookie = 0x%llx\n", *cookie); + "info: ROC, cookie = 0x%llx\n", cookie); } return ret; @@ -3616,7 +3616,7 @@ nxpwifi_cfg80211_disassociate(struct wiphy *wiphy, static int nxpwifi_cfg80211_probe_peer(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, - u64 *cookie) + u64 cookie) { /* * hostapd looks for NL80211_CMD_PROBE_CLIENT support; otherwise, diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index 284bc61c46d0..45e2b7ae9633 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -446,7 +446,7 @@ qtnf_update_mgmt_frame_registrations(struct wiphy *wiphy, static int qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 cookie) { struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); const struct ieee80211_mgmt *mgmt_frame = (void *)params->buf; diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 0e6b40336123..b58dda129ff6 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -2530,7 +2530,7 @@ static int _cfg80211_rtw_mgmt_tx(struct adapter *padapter, u8 tx_ch, const u8 *b static int cfg80211_rtw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, - u64 *cookie) + u64 cookie) { struct net_device *ndev = wdev_to_ndev(wdev); struct ieee80211_channel *chan = params->chan; @@ -2553,7 +2553,7 @@ static int cfg80211_rtw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, padapter = rtw_netdev_priv(ndev); /* indicate ack before issue frame to avoid racing with rsp frame */ - rtw_cfg80211_mgmt_tx_status(padapter, *cookie, buf, len, ack, GFP_KERNEL); + rtw_cfg80211_mgmt_tx_status(padapter, cookie, buf, len, ack, GFP_KERNEL); if (!rtw_action_frame_parse(buf, len, &category, &action)) goto exit; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ee395e3a021a..74e267f72c19 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5458,14 +5458,14 @@ struct cfg80211_ops { struct wireless_dev *wdev, struct ieee80211_channel *chan, unsigned int duration, - u64 *cookie, const u8 *rx_addr); + u64 cookie, const u8 *rx_addr); int (*cancel_remain_on_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); int (*mgmt_tx)(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, - u64 *cookie); + u64 cookie); int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); @@ -5512,7 +5512,7 @@ struct cfg80211_ops { const u8 *peer, enum nl80211_tdls_operation oper); int (*probe_peer)(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u64 *cookie); + const u8 *peer, u64 cookie); int (*set_noack_map)(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b6d02d2b28f5..0c2c7afd59c4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4940,7 +4940,7 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy, } static int ieee80211_probe_peer(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u64 *cookie) + const u8 *peer, u64 cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -5047,7 +5047,7 @@ static int ieee80211_probe_peer(struct wiphy *wiphy, struct net_device *dev, if (qos) nullfunc->qos_ctrl = cpu_to_le16(7); - ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_ATOMIC); + ret = ieee80211_attach_ack_skb(local, skb, &cookie, GFP_ATOMIC); if (ret) { kfree_skb(skb); return ret; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3760319ab079..d74da4e9ebf3 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2150,12 +2150,12 @@ void ieee80211_roc_purge(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie, + unsigned int duration, u64 cookie, const u8 *rx_addr); int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie); + struct cfg80211_mgmt_tx_params *params, u64 cookie); int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 94be6497f259..7acef80d5f1f 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -702,7 +702,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie, + unsigned int duration, u64 cookie, const u8 *rx_addr) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); @@ -711,7 +711,7 @@ int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, lockdep_assert_wiphy(local->hw.wiphy); return ieee80211_start_roc_work(local, sdata, chan, - duration, cookie, NULL, + duration, &cookie, NULL, IEEE80211_ROC_TYPE_NORMAL); } @@ -808,7 +808,7 @@ int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, } int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; @@ -1014,7 +1014,7 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* make a copy to preserve the frame contents * in case of encryption. */ - ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_KERNEL); + ret = ieee80211_attach_ack_skb(local, skb, &cookie, GFP_KERNEL); if (ret) { kfree_skb(skb); goto out_unlock; @@ -1035,7 +1035,7 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* This will handle all kinds of coalescing and immediate TX */ ret = ieee80211_start_roc_work(local, sdata, params->chan, - params->wait, cookie, skb, + params->wait, &cookie, skb, IEEE80211_ROC_TYPE_MGMT_TX); if (ret) ieee80211_free_txskb(&local->hw, skb); diff --git a/net/wireless/core.h b/net/wireless/core.h index 15d9f7eb58b4..b4610f6685dc 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -402,7 +402,7 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, - u64 *cookie); + u64 cookie); void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, const struct ieee80211_ht_cap *ht_capa_mask); void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 7824b7ac2770..a0d1cde26f0c 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -894,7 +894,7 @@ static bool cfg80211_allowed_random_address(struct wireless_dev *wdev, int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 cookie) { const struct ieee80211_mgmt *mgmt; u16 stype; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6ad1f9cf2dda..8a46101bfdd0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14644,7 +14644,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, cookie = cfg80211_assign_cookie(rdev); err = rdev_remain_on_channel(rdev, wdev, chandef.chan, - duration, &cookie, rx_addr); + duration, cookie, rx_addr); if (err) goto free_msg; @@ -14885,7 +14885,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) params.chan = chandef.chan; cookie = cfg80211_assign_cookie(rdev); - err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, &cookie); + err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, cookie); if (err) goto free_msg; @@ -16374,7 +16374,7 @@ static int nl80211_probe_peer(struct sk_buff *skb, struct genl_info *info) } cookie = cfg80211_assign_cookie(rdev); - err = rdev_probe_peer(rdev, dev, addr, &cookie); + err = rdev_probe_peer(rdev, dev, addr, cookie); if (err) goto free_msg; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 6c3bad8b2d6f..c46e97c90fdc 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -736,14 +736,14 @@ static inline int rdev_remain_on_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie, const u8 *rx_addr) + unsigned int duration, u64 cookie, const u8 *rx_addr) { int ret; trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration, rx_addr); ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan, duration, cookie, rx_addr); - trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); + trace_rdev_return_int_cookie(&rdev->wiphy, ret, cookie); return ret; } @@ -761,12 +761,12 @@ rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev, static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, - u64 *cookie) + u64 cookie) { int ret; trace_rdev_mgmt_tx(&rdev->wiphy, wdev, params); ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, params, cookie); - trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); + trace_rdev_return_int_cookie(&rdev->wiphy, ret, cookie); return ret; } @@ -950,12 +950,12 @@ static inline int rdev_tdls_oper(struct cfg80211_registered_device *rdev, static inline int rdev_probe_peer(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *peer, - u64 *cookie) + u64 cookie) { int ret; trace_rdev_probe_peer(&rdev->wiphy, dev, peer); ret = rdev->ops->probe_peer(&rdev->wiphy, dev, peer, cookie); - trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); + trace_rdev_return_int_cookie(&rdev->wiphy, ret, cookie); return ret; } From 51ba92052ebd84c36f93d6a17e779b5d024c8732 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:35:08 +0200 Subject: [PATCH 381/403] wifi: cfg80211: convert tx_control_port cookie to input parameter The tx_control_port op was excluded from the previous commit because a NULL cookie was affecting different behavior, ie. signalling that no TX status is wanted. Since cfg80211_assign_cookie() guarantees a non-zero value, cookie value 0 can be used instead. So pass 0 when dont_wait_for_ack is set, otherwise pass value returned from cfg80211_assign_cookie() call. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-13-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 6 +++--- net/mac80211/ieee80211_i.h | 4 ++-- net/mac80211/tdls.c | 2 +- net/mac80211/tx.c | 27 +++++++++++++-------------- net/wireless/nl80211.c | 7 +++---- net/wireless/rdev-ops.h | 4 ++-- 6 files changed, 24 insertions(+), 26 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 74e267f72c19..97c16d4ff127 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5220,8 +5220,8 @@ struct mgmt_frame_regs { * user space * * @tx_control_port: TX a control port frame (EAPoL). The noencrypt parameter - * tells the driver that the frame should not be encrypted. When @cookie - * is non-NULL it is pre-assigned by cfg80211; drivers must not modify it. + * tells the driver that the frame should not be encrypted. A @cookie + * value of 0 means the caller does not want TX status reporting. * * @get_ftm_responder_stats: Retrieve FTM responder statistics, if available. * Statistics should be cumulative, currently no way to reset is provided. @@ -5610,7 +5610,7 @@ struct cfg80211_ops { const u8 *buf, size_t len, const u8 *dest, const __be16 proto, const bool noencrypt, int link_id, - u64 *cookie); + u64 cookie); int (*get_ftm_responder_stats)(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d74da4e9ebf3..5761e9621491 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2234,7 +2234,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev, u32 info_flags, u32 ctrl_flags, - u64 *cookie); + u64 cookie); struct sk_buff * ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, u32 info_flags); @@ -2248,7 +2248,7 @@ void ieee80211_clear_fast_xmit(struct sta_info *sta); int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len, const u8 *dest, __be16 proto, bool unencrypted, - int link_id, u64 *cookie); + int link_id, u64 cookie); int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len); void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index ffd575a8d188..dc2f662fe4c4 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -1121,7 +1121,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, /* disable bottom halves when entering the Tx path */ local_bh_disable(); __ieee80211_subif_start_xmit(skb, dev, flags, - IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, NULL); + IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, 0); local_bh_enable(); return ret; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index b62a41bda49d..1ee2b1cfc756 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2580,7 +2580,7 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, static u16 ieee80211_store_ack_skb(struct ieee80211_local *local, struct sk_buff *skb, u32 *info_flags, - u64 *cookie) + u64 cookie) { struct sk_buff *ack_skb; u16 info_id = 0; @@ -2603,7 +2603,7 @@ static u16 ieee80211_store_ack_skb(struct ieee80211_local *local, info_id = id; *info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; if (cookie) - IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie; + IEEE80211_SKB_CB(ack_skb)->ack.cookie = cookie; } else { kfree_skb(ack_skb); } @@ -2648,7 +2648,7 @@ static void ieee80211_remove_ack_skb(struct ieee80211_local *local, u16 info_id) static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, u32 info_flags, struct sta_info *sta, u32 ctrl_flags, - u64 *cookie) + u64 cookie) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info; @@ -4369,7 +4369,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev, u32 info_flags, u32 ctrl_flags, - u64 *cookie) + u64 cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -4564,7 +4564,7 @@ static void ieee80211_mlo_multicast_tx_one(struct ieee80211_sub_if_data *sdata, return; ctrl_flags |= u32_encode_bits(link_id, IEEE80211_TX_CTRL_MLO_LINK); - __ieee80211_subif_start_xmit(out, sdata->dev, 0, ctrl_flags, NULL); + __ieee80211_subif_start_xmit(out, sdata->dev, 0, ctrl_flags, 0); } static void ieee80211_mlo_multicast_tx(struct net_device *dev, @@ -4579,8 +4579,7 @@ static void ieee80211_mlo_multicast_tx(struct net_device *dev, ctrl_flags |= u32_encode_bits(__ffs(links), IEEE80211_TX_CTRL_MLO_LINK); - __ieee80211_subif_start_xmit(skb, sdata->dev, 0, ctrl_flags, - NULL); + __ieee80211_subif_start_xmit(skb, sdata->dev, 0, ctrl_flags, 0); return; } @@ -4622,7 +4621,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, while ((skb = __skb_dequeue(&queue))) __ieee80211_subif_start_xmit(skb, dev, 0, IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, - NULL); + 0); } else if (ieee80211_vif_is_mld(&sdata->vif) && ((sdata->vif.type == NL80211_IFTYPE_AP && !ieee80211_hw_check(&sdata->local->hw, MLO_MCAST_MULTI_LINK_TX)) || @@ -4633,7 +4632,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, normal: __ieee80211_subif_start_xmit(skb, dev, 0, IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, - NULL); + 0); } return NETDEV_TX_OK; @@ -4732,7 +4731,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, /* fall back to non-offload slow path */ __ieee80211_subif_start_xmit(skb, dev, 0, IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, - NULL); + 0); return; } @@ -4768,7 +4767,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, if (unlikely(sk_requests_wifi_status(skb->sk))) { info->status_data = ieee80211_store_ack_skb(local, skb, - &info->flags, NULL); + &info->flags, 0); if (info->status_data) info->status_data_idr = 1; } @@ -4911,7 +4910,7 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, } skb = ieee80211_build_hdr(sdata, skb, info_flags, sta, - IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, NULL); + IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, 0); if (IS_ERR(skb)) goto out; @@ -6526,7 +6525,7 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len, const u8 *dest, __be16 proto, bool unencrypted, - int link_id, u64 *cookie) + int link_id, u64 cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -6661,7 +6660,7 @@ int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev, local_bh_disable(); __ieee80211_subif_start_xmit(skb, skb->dev, 0, IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP, - NULL); + 0); local_bh_enable(); return 0; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8a46101bfdd0..d12bef11b94f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -18589,12 +18589,11 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) link_id = nl80211_link_id_or_invalid(info->attrs); - if (!dont_wait_for_ack) - cookie = cfg80211_assign_cookie(rdev); + cookie = dont_wait_for_ack ? 0 : cfg80211_assign_cookie(rdev); err = rdev_tx_control_port(rdev, dev, buf, len, dest, cpu_to_be16(proto), noencrypt, link_id, - dont_wait_for_ack ? NULL : &cookie); - if (!err && !dont_wait_for_ack) + cookie); + if (!err && cookie) nl_set_extack_cookie_u64(info->extack, cookie); return err; } diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index c46e97c90fdc..46849fe8d0b3 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -775,7 +775,7 @@ static inline int rdev_tx_control_port(struct cfg80211_registered_device *rdev, const void *buf, size_t len, const u8 *dest, __be16 proto, const bool noencrypt, int link, - u64 *cookie) + u64 cookie) { int ret; trace_rdev_tx_control_port(&rdev->wiphy, dev, buf, len, @@ -783,7 +783,7 @@ static inline int rdev_tx_control_port(struct cfg80211_registered_device *rdev, ret = rdev->ops->tx_control_port(&rdev->wiphy, dev, buf, len, dest, proto, noencrypt, link, cookie); if (cookie) - trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); + trace_rdev_return_int_cookie(&rdev->wiphy, ret, cookie); else trace_rdev_return_int(&rdev->wiphy, ret); return ret; From 69ab1d978efba9a242c7c7a92ccf1b643f137630 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 31 Jul 2026 14:35:09 +0200 Subject: [PATCH 382/403] wifi: nl80211: send frame tx status event only for non-zero cookie The cookie value assigned by cfg80211_assign_cookie() is guaranteed to be non-zero. So the zero cookie value has special use in tx_control_port where userspace can indicate dont_wait_for_ack, ie. not interested in status. The wil6210 driver also uses the zero cookie when wil_cfg80211_mgmt_tx() is invoked from debugfs api the driver provides so the event is also redundant in that scenario. Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260731123509.1975281-14-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d12bef11b94f..ac895e02cd41 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -22046,6 +22046,10 @@ static void nl80211_frame_tx_status(struct wireless_dev *wdev, struct sk_buff *msg; void *hdr; + /* userspace not interested in zero-cookie status */ + if (!status->cookie) + return; + if (command == NL80211_CMD_FRAME_TX_STATUS) trace_cfg80211_mgmt_tx_status(wdev, status->cookie, status->ack); From cc3e7fdbf7c0fe8a78195e53d322126546b6212b Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 29 Jul 2026 11:37:15 -0700 Subject: [PATCH 383/403] wifi: nxpwifi: embed rx_reorder_ptr rx_reorder_ptr is a dynamically allocated array which is done near the main struct allocation. Combine the two to avoid freeing separately. Also fix the type to what it actually is. void is normally used to avoid casting but there's no need here. Signed-off-by: Rosen Penev Tested-by: Jeff Chen Reviewed-by: Jeff Chen Link: https://patch.msgid.link/20260729183715.691287-1-rosenp@gmail.com Signed-off-by: Johannes Berg --- .../net/wireless/nxp/nxpwifi/11n_rxreorder.c | 23 +++++-------------- drivers/net/wireless/nxp/nxpwifi/main.h | 2 +- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c index c5819f89b08c..65b628411543 100644 --- a/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c +++ b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c @@ -171,7 +171,6 @@ nxpwifi_del_rx_reorder_entry(struct nxpwifi_private *priv, list_del_rcu(&tbl->list); spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]); - kfree(tbl->rx_reorder_ptr); kfree_rcu(tbl, rcu); atomic_set(&priv->adapter->rx_ba_teardown_pending, 0); @@ -262,7 +261,6 @@ static void nxpwifi_11n_create_rx_reorder_tbl(struct nxpwifi_private *priv, u8 *ta, int tid, int win_size, int seq_num) { - int i; struct nxpwifi_rx_reorder_tbl *tbl, *new_node; u16 last_seq = 0; struct nxpwifi_sta_node *node; @@ -273,11 +271,16 @@ nxpwifi_11n_create_rx_reorder_tbl(struct nxpwifi_private *priv, u8 *ta, nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num); return; } + + if (win_size <= 0) + return; + /* if !tbl then create one */ - new_node = kzalloc_obj(*new_node, GFP_KERNEL); + new_node = kzalloc_flex(*new_node, rx_reorder_ptr, win_size); if (!new_node) return; + new_node->win_size = win_size; INIT_LIST_HEAD(&new_node->list); new_node->tid = tid; memcpy(new_node->ta, ta, ETH_ALEN); @@ -311,26 +314,12 @@ nxpwifi_11n_create_rx_reorder_tbl(struct nxpwifi_private *priv, u8 *ta, new_node->flags |= RXREOR_INIT_WINDOW_SHIFT; } - new_node->win_size = win_size; - - new_node->rx_reorder_ptr = kcalloc(win_size, sizeof(void *), - GFP_KERNEL); - if (!new_node->rx_reorder_ptr) { - kfree(new_node); - nxpwifi_dbg(priv->adapter, ERROR, - "%s: failed to alloc reorder_ptr\n", __func__); - return; - } - new_node->timer_context.ptr = new_node; new_node->timer_context.priv = priv; new_node->timer_context.timer_is_set = false; timer_setup(&new_node->timer_context.timer, nxpwifi_flush_data, 0); - for (i = 0; i < win_size; ++i) - new_node->rx_reorder_ptr[i] = NULL; - spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]); list_add_tail_rcu(&new_node->list, &priv->rx_reorder_tbl_ptr[tid]); spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]); diff --git a/drivers/net/wireless/nxp/nxpwifi/main.h b/drivers/net/wireless/nxp/nxpwifi/main.h index 4abf80771be2..349dfa4d3f85 100644 --- a/drivers/net/wireless/nxp/nxpwifi/main.h +++ b/drivers/net/wireless/nxp/nxpwifi/main.h @@ -656,10 +656,10 @@ struct nxpwifi_rx_reorder_tbl { int init_win; int start_win; int win_size; - void **rx_reorder_ptr; struct reorder_tmr_cnxt timer_context; u8 amsdu; u8 flags; + struct sk_buff *rx_reorder_ptr[] __counted_by(win_size); }; struct nxpwifi_bss_prio_node { From a28fcce6ee74be8a4526e6cfa16dc7786d62a784 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Thu, 30 Jul 2026 01:36:07 +0800 Subject: [PATCH 384/403] wifi: mac80211: send TWT teardown to peer after setup TX failure When an AP's TWT Setup response is not acknowledged, ieee80211_s1g_tx_twt_setup_fail() asks the driver to tear down the local agreement and sends a TWT teardown action as the peer notification. It uses the response SA as the destination, but ieee80211_s1g_send_twt_setup() built that response with SA set to the AP's address. The teardown is therefore queued with DA, SA and BSSID all set to the AP address and never reaches the station. The in-tree driver callbacks update local hardware state and emit no action frame. The station receives no notification that mac80211 asked the driver to remove the agreement and can keep following the TWT schedule, leaving the peers' power-save state desynchronized. Address the teardown to the response DA, the station to which the failed response was sent. This also matches the station lookup the transmit status path already performs on the same frame. Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode") Assisted-by: Codex:gpt-5.6-sol Assisted-by: Kimi:K3 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260729173607.13340-1-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/s1g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c index 5af4a0c6c642..825fcf3f909b 100644 --- a/net/mac80211/s1g.c +++ b/net/mac80211/s1g.c @@ -143,7 +143,7 @@ ieee80211_s1g_tx_twt_setup_fail(struct ieee80211_sub_if_data *sdata, drv_twt_teardown_request(sdata->local, sdata, &sta->sta, flowid); - ieee80211_s1g_send_twt_teardown(sdata, mgmt->sa, sdata->vif.addr, + ieee80211_s1g_send_twt_teardown(sdata, mgmt->da, sdata->vif.addr, flowid); } From 927ee844c47ac2aef22c8f7a35f098ff576b398b Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 31 Jul 2026 12:02:44 +0800 Subject: [PATCH 385/403] wifi: nl80211: clean up color-change beacon data on errors nl80211_color_change() calls nl80211_parse_beacon() for the beacon_next template, which can allocate params.beacon_next.mbssid_ies and .rnr_ies. A parsing failure returned directly instead of using the out: cleanup, leaking any allocations completed before the error. Allocate the nested attribute table before parsing beacon_next. Its allocation failure can then return before beacon data exists, while a later parsing failure uses out: to release the parsed data. Fixes: dc1e3cb8da8b ("nl80211: MBSSID and EMA support in AP mode") Assisted-by: Codex:gpt-5 Assisted-by: Claude:opus-4.8 Assisted-by: Kimi:K3 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260731120244.82628-1-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ac895e02cd41..44f2bad08670 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -18928,15 +18928,15 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) if (!wdev->links[params.link_id].ap.beacon_interval) return -EINVAL; + tb = kzalloc_objs(*tb, NL80211_ATTR_MAX + 1); + if (!tb) + return -ENOMEM; + err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_next, wdev->links[params.link_id].ap.chandef.chan, info->extack); if (err) - return err; - - tb = kzalloc_objs(*tb, NL80211_ATTR_MAX + 1); - if (!tb) - return -ENOMEM; + goto out; err = nla_parse_nested(tb, NL80211_ATTR_MAX, info->attrs[NL80211_ATTR_COLOR_CHANGE_ELEMS], From 0e4532ec658606f76f62eb277e7a933919d36cbb Mon Sep 17 00:00:00 2001 From: Slawomir Stepien Date: Thu, 30 Jul 2026 08:52:31 +0200 Subject: [PATCH 386/403] wifi: zd1211rw: reject secondary interfaces to prevent conflicts The zd1211rw driver is designed for single-function Wi-Fi dongles and hardcodes its USB endpoints. When a malformed USB device exposes multiple interfaces that match the driver's device ID, the driver blindly binds to all of them. During probe(), the driver calls usb_reset_device(), which iterates over all interfaces and invokes the pre_reset() callback for each bound interface. Since multiple interfaces are bound to zd1211rw, pre_reset() is called sequentially for each instance, acquiring their respective &mac->chip.mutex. Because all instances initialize their mutexes with the same lock class, lockdep detects a task acquiring a lock of the same class it already holds and flags it as a possible recursive deadlock: WARNING: possible recursive locking detected kworker/0:1/11 is trying to acquire lock: ffff88810371dde0 (&chip->mutex){+.+.}-{4:4}, at: zd_chip_disable_rxtx+0x20/0x50 drivers/net/wireless/zydas/zd1211rw/zd_chip.c:1465 but task is already holding lock: ffff8881138ddde0 (&chip->mutex){+.+.}-{4:4}, at: pre_reset+0x28c/0x380 drivers/net/wireless/zydas/zd1211rw/zd_usb.c:1505 Fix this by explicitly rejecting secondary interfaces (bInterfaceNumber != 0) during probe(). This ensures that only a single instance of the driver binds to the device, eliminating the recursive locking scenario. Fixes: e85d0918b54f ("[PATCH] ZyDAS ZD1211 USB-WLAN driver") Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot Reported-by: syzbot+0ec3d1a6cf1fbe79c153@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0ec3d1a6cf1fbe79c153 Link: https://syzkaller.appspot.com/ai_job?id=00724ef7-fd77-4cde-9779-895b8f63c2f6 Signed-off-by: Slawomir Stepien Link: https://patch.msgid.link/20260730065231.1644030-1-sst@poczta.fm Signed-off-by: Johannes Berg --- drivers/net/wireless/zydas/zd1211rw/zd_usb.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c index 966d8ccb0dbc..98102c663434 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c @@ -1353,6 +1353,14 @@ static int probe(struct usb_interface *intf, const struct usb_device_id *id) struct zd_usb *usb; struct ieee80211_hw *hw = NULL; + /* + * ZD1211 devices are single-function. Reject secondary interfaces + * to prevent multiple instances from conflicting on hardcoded endpoints + * and triggering recursive locking warnings. + */ + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + return -ENODEV; + print_id(udev); if (id->driver_info & DEVICE_INSTALLER) From fd2bf5e718108c00732eb07fd94a5d8830f62a9f Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Thu, 23 Jul 2026 09:10:01 +0800 Subject: [PATCH 387/403] wifi: mac80211: skip unused probe response countdown offsets mac80211 copies cfg80211's variable-length countdown offset list into a zero-initialized fixed-size array, leaving unused entries at zero. The beacon branch already skips those zero entries, but the AP probe-response branch writes through them unconditionally. When a probe-response template has no countdown offset, the write through an unused zero entry overwrites resp->data[0], corrupting the first byte of the template. cfg80211 already bounds explicitly supplied non-zero offsets in nl80211_parse_counter_offsets(), so this is a zero-sentinel bug, not an out-of-bounds write. Skip zero probe-response offsets, matching the beacon path. Fixes: af296bdb8da4 ("mac80211: move csa counters from sdata to beacon/presp") Link: https://lore.kernel.org/all/20260708195911.84365-6-enderaoelyther@gmail.com/ Assisted-by: Codex:gpt-5 Assisted-by: Claude:opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260723011001.76851-1-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 1ee2b1cfc756..3a1e2c9e1565 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -5275,7 +5275,8 @@ static void ieee80211_set_beacon_cntdwn(struct ieee80211_sub_if_data *sdata, if (sdata->vif.type == NL80211_IFTYPE_AP && resp) { u16 *resp_offsets = resp->cntdwn_counter_offsets; - resp->data[resp_offsets[i]] = count; + if (resp_offsets[i]) + resp->data[resp_offsets[i]] = count; } } } From 9d96e037ff90a2fc81a841b2002d500a5a4a5351 Mon Sep 17 00:00:00 2001 From: Mariano Baragiola Date: Tue, 28 Jul 2026 16:26:10 -0300 Subject: [PATCH 388/403] wifi: wilc1000: validate monitor transmit frame headers wilc_wfi_mon_xmit() reads the radiotap length before ensuring that the fixed header is present. After stripping that header, it reads the frame type and all three 802.11 addresses without checking how much frame data remains. A truncated monitor injection can therefore cause out-of-bounds reads. Validate the radiotap header first, use the common 802.11 helper to check the variable header length, and require a complete three-address header before using the addresses. This covers QoS and four-address data headers while rejecting short control headers that this path cannot classify. Signed-off-by: Mariano Baragiola Link: https://patch.msgid.link/20260728192610.2236361-1-mbaragiola@linux.com Signed-off-by: Johannes Berg --- drivers/net/wireless/microchip/wilc1000/mon.c | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/mon.c b/drivers/net/wireless/microchip/wilc1000/mon.c index b5cf6fa7a851..9b8c083b403d 100644 --- a/drivers/net/wireless/microchip/wilc1000/mon.c +++ b/drivers/net/wireless/microchip/wilc1000/mon.c @@ -142,6 +142,9 @@ static int mon_mgmt_tx(struct net_device *dev, const u8 *buf, size_t len) static netdev_tx_t wilc_wfi_mon_xmit(struct sk_buff *skb, struct net_device *dev) { + struct ieee80211_radiotap_header_fixed *rtap_hdr; + struct ieee80211_hdr_3addr *hdr; + unsigned int hdr_len; u32 rtap_len, ret = 0; struct wilc_wfi_mon_priv *mon_priv; struct sk_buff *skb2; @@ -153,13 +156,26 @@ static netdev_tx_t wilc_wfi_mon_xmit(struct sk_buff *skb, if (!mon_priv) return -EFAULT; + if (skb->len < sizeof(*rtap_hdr)) + goto drop; + + rtap_hdr = (void *)skb->data; + if (rtap_hdr->it_version) + goto drop; + rtap_len = ieee80211_get_radiotap_len(skb->data); - if (skb->len < rtap_len) - return -1; + if (rtap_len < sizeof(*rtap_hdr) || skb->len < rtap_len) + goto drop; skb_pull(skb, rtap_len); + hdr_len = ieee80211_get_hdrlen_from_skb(skb); + if (hdr_len < sizeof(*hdr)) + goto drop; - if (skb->data[0] == 0xc0 && is_broadcast_ether_addr(&skb->data[4])) { + hdr = (void *)skb->data; + + if (ieee80211_is_deauth(hdr->frame_control) && + is_broadcast_ether_addr(hdr->addr1)) { skb2 = dev_alloc_skb(skb->len + sizeof(*cb_hdr)); if (!skb2) return -ENOMEM; @@ -191,8 +207,8 @@ static netdev_tx_t wilc_wfi_mon_xmit(struct sk_buff *skb, } skb->dev = mon_priv->real_ndev; - ether_addr_copy(srcadd, &skb->data[10]); - ether_addr_copy(bssid, &skb->data[16]); + ether_addr_copy(srcadd, hdr->addr2); + ether_addr_copy(bssid, hdr->addr3); /* * Identify if data or mgmt packet, if source address and bssid * fields are equal send it to mgmt frames handler @@ -207,6 +223,10 @@ static netdev_tx_t wilc_wfi_mon_xmit(struct sk_buff *skb, } return ret; + +drop: + dev_kfree_skb(skb); + return NETDEV_TX_OK; } static const struct net_device_ops wilc_wfi_netdev_ops = { From aa0069bd920a0bc02d40d84fe39e7ef478c229e8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 2 Aug 2026 10:40:09 +0200 Subject: [PATCH 389/403] wifi: mac80211: fix RCU dereference in throughput estimate This is invoked with the wiphy mutex held, not in an RCU critical section, fix the dereference accordingly. Fixes: 2f925427e27a ("wifi: mac80211: estimate expected throughput if not provided by driver/rc") Link: https://patch.msgid.link/20260802104010.94bf0862c329.I0a05bf8ab999cb737c487d79082425257e10132a@changeid Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index d12aed9c1756..fdf00cbf49d8 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2821,7 +2821,7 @@ static u32 sta_estimate_expected_throughput(struct sta_info *sta, u32 duration; u8 band; - conf = rcu_dereference(bss_conf->chanctx_conf); + conf = sdata_dereference(bss_conf->chanctx_conf, sta->sdata); if (!conf) return 0; band = conf->def.chan->band; From b17e206e99598e51eb7862a719620647b35347c0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 2 Aug 2026 10:40:10 +0200 Subject: [PATCH 390/403] wifi: mac80211: fix RCU usage in peer probing Converting the station and chanctx lookups to wiphy_dereference() was correct for the function itself but removed the rcu_read_lock() for the later transmit, which requires it, as well. Fix that. Found with the ap_open_poll_sta hwsim test, which reports net/mac80211/tx.c:608 suspicious rcu_dereference_check() usage! (and four more like it). Fixes: 1c3f880ed00e ("wifi: mac80211: implement STA-mode peer probing") Link: https://patch.msgid.link/20260802104010.6c09477032c4.If024b480b96bf9fe7baa821ed48b80be322d1e44@changeid Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0c2c7afd59c4..23f4f9ec86d0 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -5054,7 +5054,9 @@ static int ieee80211_probe_peer(struct wiphy *wiphy, struct net_device *dev, } local_bh_disable(); + rcu_read_lock(); ieee80211_xmit(sdata, sta, skb); + rcu_read_unlock(); local_bh_enable(); return 0; From 32856f39fefdc75bd253cab999f996750628648e Mon Sep 17 00:00:00 2001 From: Can Peng Date: Thu, 23 Jul 2026 13:56:17 +0800 Subject: [PATCH 391/403] wifi: brcmfmac: validate msgbuf flowring IDs before use Firmware messages carry flow_ring_id values which brcmfmac converts to an internal flowid by subtracting BRCMF_H2D_MSGRING_FLOWRING_IDSTART. The resulting value is used as a bit index in txstatus_done_map and as an array index into msgbuf->flowrings and the flowring state. Validate the firmware supplied flow_ring_id before using it. This prevents flow_ring_id values below BRCMF_H2D_MSGRING_FLOWRING_IDSTART from underflowing and rejects values outside msgbuf->max_flowrings. In the tx status path, complete the packet with an error after removing a valid packet id so the skb is not leaked when the flow ring id is invalid. Signed-off-by: Can Peng Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260723055618.550834-1-pengcan@kylinos.cn Signed-off-by: Johannes Berg --- .../broadcom/brcm80211/brcmfmac/msgbuf.c | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index ba1ce1552e0f..8146d50d6f07 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -560,6 +560,28 @@ brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid) brcmf_flowring_delete(msgbuf->flow, flowid); } +static bool brcmf_msgbuf_get_flowid(struct brcmf_msgbuf *msgbuf, + u16 flow_ring_id, u16 *flowid) +{ + u32 id = flow_ring_id; + + if (id < BRCMF_H2D_MSGRING_FLOWRING_IDSTART) { + bphy_err(msgbuf->drvr, "invalid flowring id %u\n", + flow_ring_id); + return false; + } + + id -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART; + if (id >= msgbuf->max_flowrings) { + bphy_err(msgbuf->drvr, "invalid flowring id %u\n", + flow_ring_id); + return false; + } + + *flowid = id; + return true; +} + static struct brcmf_msgbuf_work_item * brcmf_msgbuf_dequeue_work(struct brcmf_msgbuf *msgbuf) @@ -880,17 +902,23 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) struct msgbuf_tx_status *tx_status; u32 idx; struct sk_buff *skb; + u16 flow_ring_id; u16 flowid; tx_status = (struct msgbuf_tx_status *)buf; idx = le32_to_cpu(tx_status->msg.request_id) - 1; - flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id); - flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART; + flow_ring_id = le16_to_cpu(tx_status->compl_hdr.flow_ring_id); skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, msgbuf->tx_pktids, idx); if (!skb) return; + if (!brcmf_msgbuf_get_flowid(msgbuf, flow_ring_id, &flowid)) { + brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx), + skb, false); + return; + } + set_bit(flowid, msgbuf->txstatus_done_map); commonring = msgbuf->flowrings[flowid]; atomic_dec(&commonring->outstanding_tx); @@ -1237,13 +1265,16 @@ brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf, struct msgbuf_flowring_create_resp *flowring_create_resp; u16 status; u16 flowid; + u16 flow_ring_id; flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf; - flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id); - flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART; + flow_ring_id = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id); status = le16_to_cpu(flowring_create_resp->compl_hdr.status); + if (!brcmf_msgbuf_get_flowid(msgbuf, flow_ring_id, &flowid)) + return; + if (status) { bphy_err(drvr, "Flowring creation failed, code %d\n", status); brcmf_msgbuf_remove_flowring(msgbuf, flowid); @@ -1266,13 +1297,16 @@ brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf, struct msgbuf_flowring_delete_resp *flowring_delete_resp; u16 status; u16 flowid; + u16 flow_ring_id; flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf; - flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id); - flowid -= BRCMF_H2D_MSGRING_FLOWRING_IDSTART; + flow_ring_id = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id); status = le16_to_cpu(flowring_delete_resp->compl_hdr.status); + if (!brcmf_msgbuf_get_flowid(msgbuf, flow_ring_id, &flowid)) + return; + if (status) { bphy_err(drvr, "Flowring deletion failed, code %d\n", status); brcmf_flowring_delete(msgbuf->flow, flowid); From f0ba5fd51ff23077ce66cb9be79d41a4b7c19c5e Mon Sep 17 00:00:00 2001 From: Can Peng Date: Fri, 24 Jul 2026 17:25:30 +0800 Subject: [PATCH 392/403] wifi: brcmfmac: Set DMA direction for msgbuf packet IDs brcmf_msgbuf_init_pktids() takes the DMA direction from its callers, but never stores it in the packet ID state. Since the state is zeroed, pktids->direction remains DMA_BIDIRECTIONAL for both the TX and RX packet ID pools. All msgbuf packet ID map and unmap paths use pktids->direction. As a result, TX buffers requested with DMA_TO_DEVICE and RX buffers requested with DMA_FROM_DEVICE are mapped and unmapped as DMA_BIDIRECTIONAL instead. Store the caller-provided direction when initializing the packet ID state. Signed-off-by: Can Peng Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260724092530.674624-1-pengcan@kylinos.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 8146d50d6f07..069ba7016654 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -310,6 +310,7 @@ brcmf_msgbuf_init_pktids(u32 nr_array_entries, } pktids->array = array; pktids->array_size = nr_array_entries; + pktids->direction = direction; return pktids; } From 1b1edb9ebed49099bdc924cef49a9aea8b552199 Mon Sep 17 00:00:00 2001 From: Jason Huang Date: Wed, 22 Jul 2026 16:26:08 +0800 Subject: [PATCH 393/403] wifi: brcmfmac: fix P2P action frame handling without device vif Some P2P action frame paths assume the P2P device vif is always available. That is not true when userspace sends non-P2P public action frames through the primary interface, or when action-frame abort runs after the P2P device vif has not been created. Fall back to the primary vif when aborting an action frame without a P2P device vif, and guard P2P device saved IE access before using it for peer channel search. Fixes: 30fb1b272909 ("brcmfmac: use actframe_abort to cancel ongoing action frame") Fixes: 6eda4e2c5425 ("brcmfmac: Add tx p2p off-channel support.") Signed-off-by: Jason Huang Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260722082608.412472-1-Jason.Huang2@infineon.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index 32a015d6b769..813cc15d8b87 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -1307,6 +1307,9 @@ static s32 brcmf_p2p_abort_action_frame(struct brcmf_cfg80211_info *cfg) brcmf_dbg(TRACE, "Enter\n"); vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + if (!vif) + vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; + err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe_abort", &int_val, sizeof(s32)); if (err) @@ -1845,6 +1848,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_if *ifp, /* validate channel and p2p ies */ if (config_af_params.search_channel && IS_P2P_SOCIAL_CHANNEL(le32_to_cpu(af_params->channel)) && + p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif && p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->saved_ie.probe_req_ie_len) { afx_hdl = &p2p->afx_hdl; afx_hdl->peer_listen_chan = le32_to_cpu(af_params->channel); From cf57f0a674cc3e3cda1a789359cc1238b61b9d7d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 2 Aug 2026 11:12:17 +0300 Subject: [PATCH 394/403] wifi: mac80211: disconnect on CSA to channel 0 The refactor for the CSA parsing erroneously equates channel zero and no information present, leading it to ignore a CSA on an AP that advertises a switch to that (invalid) channel. This leads to not disconnecting, which we should. For Intel devices, this can lead to a firmware crash. Fix this by using an int type for the channel number as well as the opclass, and using a (negative) value that cannot be encoded in the element to indicate it's not present. Fixes: 21c3f8f95554 ("wifi: mac80211: refactor STA CSA parsing flows") Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260802111213.3bc833515e40.I255c37c31ca8b0b34e351cf254e16b6071dd8fb3@changeid Signed-off-by: Johannes Berg --- net/mac80211/spectmgmt.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index ec622750e1c9..880f4625775d 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -227,7 +227,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, { enum nl80211_band new_band = current_band; int new_freq; - u8 new_chan_no = 0, new_op_class = 0; + int new_chan_no = -1, new_op_class = -1; struct ieee80211_channel *new_chan; struct cfg80211_chan_def new_chandef = {}; const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; @@ -256,7 +256,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, new_op_class = ext_chansw_elem->new_operating_class; if (!ieee80211_operating_class_to_band(new_op_class, &new_band)) { - new_op_class = 0; + new_op_class = -1; if (!unprot_action) sdata_info(sdata, "cannot understand ECSA IE operating class, %d, ignoring\n", @@ -268,14 +268,14 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, } } - if (!new_op_class && elems->ch_switch_ie) { + if (new_op_class < 0 && elems->ch_switch_ie) { new_chan_no = elems->ch_switch_ie->new_ch_num; csa_ie->count = elems->ch_switch_ie->count; csa_ie->mode = elems->ch_switch_ie->mode; } /* nothing here we understand */ - if (!new_chan_no) + if (new_chan_no < 0) return 1; /* Mesh Channel Switch Parameters Element */ @@ -349,7 +349,8 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, get_unaligned_le16(bwi->info.optional); } else if (!wide_bw_chansw_ie || !wbcs_elem_to_chandef(wide_bw_chansw_ie, &new_chandef)) { - if (!ieee80211_operating_class_to_chandef(new_op_class, new_chan, + if (new_op_class < 0 || + !ieee80211_operating_class_to_chandef(new_op_class, new_chan, &new_chandef)) new_chandef = csa_ie->chanreq.oper; } From 6c5fc504d0d6934132637aa3db4b9b58148eaa78 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 31 Jul 2026 15:11:03 +0800 Subject: [PATCH 395/403] wifi: cfg80211: stop PMSR before P2P and NAN teardown PMSR request teardown must abort active measurements while the wireless_dev is still present in the driver. cfg80211_leave_locked() and cfg80211_stop_pd() already do this before invoking the driver's stop callback, but cfg80211_stop_p2p_device() and cfg80211_stop_nan() do not. Those helpers are also called directly by nl80211, rfkill shutdown, and wireless_dev unregister paths. If one of these paths stops a P2P device or NAN interface with a pending request, it removes the mac80211 subinterface from the driver first. Subsequent request cleanup cannot reach the lower driver's abort callback, but cfg80211 frees the request regardless. Driver state can then retain a stale request and use it when it later reports a result. Call cfg80211_pmsr_wdev_down() before stopping the P2P device or NAN interface. This keeps lower-driver request state and cfg80211 request ownership in sync for all of the helpers' callers. Fixes: 9bb7e0f24e7e ("cfg80211: add peer measurement with FTM initiator API") Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260731071103.73563-1-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/wireless/core.c b/net/wireless/core.c index 610238d723ff..d13310fef691 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -237,6 +237,7 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, if (!wdev_running(wdev)) return; + cfg80211_pmsr_wdev_down(wdev); rdev_stop_p2p_device(rdev, wdev); wdev->is_running = false; @@ -264,6 +265,8 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, if (!wdev_running(wdev)) return; + cfg80211_pmsr_wdev_down(wdev); + /* * If there is a scheduled update pending, mark it as canceled, so the * empty schedule will be accepted From f0b48e031d84feeace2313625f02887277759d71 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 6 Aug 2026 12:06:43 +0200 Subject: [PATCH 396/403] wifi: nxp: NXPWIFI should be invisible and selected by its users All supported NXP WiFi wireless adapters have an SDIO interface. Hence there is no point in asking the user about these adapters when configuring a kernel without MMC support. Fix this by making the core driver symbol invisible, and selecting it by its user when needed. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/aefb37d8398175cb2fb520cb5f725a85bcd3049d.1786010763.git.geert+renesas@glider.be Signed-off-by: Johannes Berg --- drivers/net/wireless/nxp/nxpwifi/Kconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/nxp/nxpwifi/Kconfig b/drivers/net/wireless/nxp/nxpwifi/Kconfig index 3637068574b8..38a8820084dd 100644 --- a/drivers/net/wireless/nxp/nxpwifi/Kconfig +++ b/drivers/net/wireless/nxp/nxpwifi/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only config NXPWIFI - tristate "NXP WiFi Driver" + tristate "NXP WiFi Driver" if COMPILE_TEST depends on CFG80211 help This adds support for wireless adapters based on NXP @@ -11,7 +11,8 @@ config NXPWIFI config NXPWIFI_SDIO tristate "NXP WiFi Driver for IW61x" - depends on NXPWIFI && MMC + depends on CFG80211 && MMC + select NXPWIFI select FW_LOADER select WANT_DEV_COREDUMP help From ea21b21ca0536306adf811b1d4bad1139cf54759 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 6 Aug 2026 12:05:42 +0200 Subject: [PATCH 397/403] wifi: morsemicro: MM81X should be invisible and selected by its users Morse Micro MM81x wireless devices can have either SDIO or USB interfaces. Hence there is no point in asking the user about these devices when configuring a kernel without MMC or USB support. Fix this by making the core driver symbol invisible, and selecting it by its users when needed. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/3415bda97c2faf7c56eff7fe79a91b218d0d6731.1786010705.git.geert+renesas@glider.be Signed-off-by: Johannes Berg --- drivers/net/wireless/morsemicro/mm81x/Kconfig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/morsemicro/mm81x/Kconfig b/drivers/net/wireless/morsemicro/mm81x/Kconfig index 33cdcc0df4de..4c4fae77ff67 100644 --- a/drivers/net/wireless/morsemicro/mm81x/Kconfig +++ b/drivers/net/wireless/morsemicro/mm81x/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 config MM81X - tristate "Morse Micro MM81x wireless devices" + tristate "Morse Micro MM81x wireless devices" if COMPILE_TEST depends on MAC80211 select FW_LOADER select CRC7 @@ -11,14 +11,16 @@ config MM81X config MM81X_USB tristate "Morse Micro MM81x USB support" - depends on MM81X && USB + depends on MAC80211 && USB + select MM81X help This module adds support for the USB interface of devices using the Morse Micro MM81x chipset. config MM81X_SDIO tristate "Morse Micro MM81x SDIO support" - depends on MM81X && MMC + depends on MAC80211 && MMC + select MM81X help This module adds support for the SDIO interface of devices using the Morse Micro MM81x chipset. From 00c786a7581e62243608592543bca3c0ec3514cc Mon Sep 17 00:00:00 2001 From: Jeff Chen Date: Tue, 4 Aug 2026 00:27:41 +0800 Subject: [PATCH 398/403] wifi: nxpwifi: fix multiple static analysis errors and warnings Fix various development-phase bugs, code quality, and logical issues reported by the kernel test robot (using the Smatch static analysis tool). The following addressable fixes are included: - 11n.c & 11ax.c: Fix potential NULL pointer dereferences by correcting logical operators (&& to ||) in 11n.c and hoisting the bss_desc verification to the top of the function in 11ax.c. - 11n.c: Fix a severe Use-After-Free (UAF) memory corruption during RCU list traversal. Restore the proper list_for_each_entry_safe() loop structure along with the required array index [i] within the locked writer path. - sdio.c: Fix a missing unwind resource cleanup pathway where a protocol error branch returned directly via -EINVAL instead of using 'goto term_cmd', leaving the SDIO hardware state machine out of sync. - main.h: Fix a signedness mismatch bug where nxpwifi_get_unused_bss_num() could return -2 as an unsigned integer fallback. - util.c: Remove a redundant and dead condition check (position <= 15) which was always true for a 4-bit unsigned bit-field member variable. - cfg80211.c: Clean up a dead unreachable 'return 0' at the bottom of the switch-case logic. - uap_txrx.c: Clean up mismatched and inconsistent indentations within the handling of multicast RX forward paths. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202608020855.QwN5n7i5-lkp@intel.com/ Assisted-by: Gemini:unknown-model Signed-off-by: Jeff Chen Link: https://patch.msgid.link/20260803162741.438820-1-chunfan.chen@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/nxp/nxpwifi/11ax.c | 5 +- drivers/net/wireless/nxp/nxpwifi/11n.c | 7 +-- drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 14 ++++-- drivers/net/wireless/nxp/nxpwifi/main.c | 2 +- drivers/net/wireless/nxp/nxpwifi/main.h | 21 ++++++--- drivers/net/wireless/nxp/nxpwifi/sdio.c | 3 +- drivers/net/wireless/nxp/nxpwifi/uap_txrx.c | 4 +- drivers/net/wireless/nxp/nxpwifi/util.c | 51 ++++++++++----------- 8 files changed, 62 insertions(+), 45 deletions(-) diff --git a/drivers/net/wireless/nxp/nxpwifi/11ax.c b/drivers/net/wireless/nxp/nxpwifi/11ax.c index cc47c435eb70..96540914f3cf 100644 --- a/drivers/net/wireless/nxp/nxpwifi/11ax.c +++ b/drivers/net/wireless/nxp/nxpwifi/11ax.c @@ -413,7 +413,10 @@ bool nxpwifi_is_11ax_twt_supported(struct nxpwifi_private *priv, struct nxpwifi_ie_types_he_cap *user_he_cap; struct nxpwifi_ie_types_he_cap *hw_he_cap; - if (bss_desc && (!nxpwifi_is_ap_11ax_twt_supported(bss_desc))) { + if (!bss_desc) + return false; + + if (!nxpwifi_is_ap_11ax_twt_supported(bss_desc)) { nxpwifi_dbg(priv->adapter, MSG, "AP don't support twt feature\n"); return false; diff --git a/drivers/net/wireless/nxp/nxpwifi/11n.c b/drivers/net/wireless/nxp/nxpwifi/11n.c index e46c5053d509..c2a54d781b42 100644 --- a/drivers/net/wireless/nxp/nxpwifi/11n.c +++ b/drivers/net/wireless/nxp/nxpwifi/11n.c @@ -451,7 +451,7 @@ void nxpwifi_11n_delete_tx_ba_stream_tbl_entry(struct nxpwifi_private *priv, struct nxpwifi_tx_ba_stream_tbl *tbl) { - if (!tbl && nxpwifi_is_tx_ba_stream_ptr_valid(priv, tbl)) + if (!tbl || nxpwifi_is_tx_ba_stream_ptr_valid(priv, tbl)) return; nxpwifi_dbg(priv->adapter, INFO, @@ -694,7 +694,7 @@ int nxpwifi_get_tx_ba_stream_tbl(struct nxpwifi_private *priv, /* Delete Tx BA stream entry by RA. */ void nxpwifi_del_tx_ba_stream_tbl_by_ra(struct nxpwifi_private *priv, u8 *ra) { - struct nxpwifi_tx_ba_stream_tbl *tbl; + struct nxpwifi_tx_ba_stream_tbl *tbl, *tmp; int i; if (!ra) @@ -702,7 +702,8 @@ void nxpwifi_del_tx_ba_stream_tbl_by_ra(struct nxpwifi_private *priv, u8 *ra) for (i = 0; i < MAX_NUM_TID; i++) { spin_lock_bh(&priv->tx_ba_stream_tbl_lock[i]); - list_for_each_entry_rcu(tbl, &priv->tx_ba_stream_tbl_ptr[i], list) + list_for_each_entry_safe(tbl, tmp, + &priv->tx_ba_stream_tbl_ptr[i], list) if (!memcmp(tbl->ra, ra, ETH_ALEN)) nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, tbl); diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c index c820f08d2835..5cc8cdf594d3 100644 --- a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c +++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c @@ -717,6 +717,7 @@ nxpwifi_init_new_priv_params(struct nxpwifi_private *priv, enum nl80211_iftype type) { struct nxpwifi_adapter *adapter = priv->adapter; + int ret; nxpwifi_init_priv(priv); @@ -740,7 +741,15 @@ nxpwifi_init_new_priv_params(struct nxpwifi_private *priv, return -EOPNOTSUPP; } - priv->bss_num = nxpwifi_get_unused_bss_num(adapter, priv->bss_type); + ret = nxpwifi_get_unused_bss_num(adapter, priv->bss_type, + &priv->bss_num); + + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "%s: no unused bss_num for type %d\n", + dev->name, priv->bss_type); + return ret; + } flush_workqueue(adapter->workqueue); atomic_set(&adapter->iface_changing, 0); @@ -943,7 +952,6 @@ nxpwifi_cfg80211_change_virtual_intf(struct wiphy *wiphy, case NL80211_IFTYPE_STATION: return nxpwifi_change_vif_to_sta(dev, curr_iftype, type, params); - break; default: goto errnotsupp; } @@ -952,8 +960,6 @@ nxpwifi_cfg80211_change_virtual_intf(struct wiphy *wiphy, goto errnotsupp; } - return 0; - errnotsupp: nxpwifi_dbg(priv->adapter, ERROR, "unsupported interface type transition: %d to %d\n", diff --git a/drivers/net/wireless/nxp/nxpwifi/main.c b/drivers/net/wireless/nxp/nxpwifi/main.c index 4e01f45f3a00..b4c63829024a 100644 --- a/drivers/net/wireless/nxp/nxpwifi/main.c +++ b/drivers/net/wireless/nxp/nxpwifi/main.c @@ -204,7 +204,7 @@ static bool nxpwifi_drain_tx(struct nxpwifi_adapter *adapter) NXPWIFI_ASYNC_CMD); adapter->hs_activated_manually = false; } - nxpwifi_process_bypass_tx(adapter); + nxpwifi_process_bypass_tx(adapter); if (adapter->hs_activated) { clear_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags); diff --git a/drivers/net/wireless/nxp/nxpwifi/main.h b/drivers/net/wireless/nxp/nxpwifi/main.h index 349dfa4d3f85..b25a6a4f2936 100644 --- a/drivers/net/wireless/nxp/nxpwifi/main.h +++ b/drivers/net/wireless/nxp/nxpwifi/main.h @@ -1166,8 +1166,9 @@ nxpwifi_get_priv(struct nxpwifi_adapter *adapter, } /* find unused BSS number for new interface */ -static inline u8 -nxpwifi_get_unused_bss_num(struct nxpwifi_adapter *adapter, u8 bss_type) +static inline int +nxpwifi_get_unused_bss_num(struct nxpwifi_adapter *adapter, u8 bss_type, + u8 *bss_num) { u8 i, j; int index[NXPWIFI_MAX_BSS_NUM]; @@ -1179,9 +1180,14 @@ nxpwifi_get_unused_bss_num(struct nxpwifi_adapter *adapter, u8 bss_type) NL80211_IFTYPE_UNSPECIFIED)) { index[adapter->priv[i]->bss_num] = 1; } - for (j = 0; j < NXPWIFI_MAX_BSS_NUM; j++) - if (!index[j]) - return j; + + for (j = 0; j < NXPWIFI_MAX_BSS_NUM; j++) { + if (!index[j]) { + *bss_num = j; + return 0; + } + } + return -ENOENT; } @@ -1195,8 +1201,9 @@ nxpwifi_get_unused_priv_by_bss_type(struct nxpwifi_adapter *adapter, for (i = 0; i < adapter->priv_num; i++) if (adapter->priv[i]->bss_mode == NL80211_IFTYPE_UNSPECIFIED) { - adapter->priv[i]->bss_num = - nxpwifi_get_unused_bss_num(adapter, bss_type); + if (nxpwifi_get_unused_bss_num(adapter, bss_type, + &adapter->priv[i]->bss_num)) + return NULL; break; } diff --git a/drivers/net/wireless/nxp/nxpwifi/sdio.c b/drivers/net/wireless/nxp/nxpwifi/sdio.c index d8536354f093..8ef0f6eb49e2 100644 --- a/drivers/net/wireless/nxp/nxpwifi/sdio.c +++ b/drivers/net/wireless/nxp/nxpwifi/sdio.c @@ -1347,7 +1347,8 @@ static int nxpwifi_process_int_status(struct nxpwifi_adapter *adapter, u8 sdio_i ((rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE) > card->mpa_rx.buf_size))) { nxpwifi_dbg(adapter, ERROR, "invalid rx_len=%d\n", rx_len); - return -EINVAL; + ret = -EINVAL; + goto term_cmd; } rx_len = (u16)(rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE); diff --git a/drivers/net/wireless/nxp/nxpwifi/uap_txrx.c b/drivers/net/wireless/nxp/nxpwifi/uap_txrx.c index f3d24bf861ca..840dddfc4f5a 100644 --- a/drivers/net/wireless/nxp/nxpwifi/uap_txrx.c +++ b/drivers/net/wireless/nxp/nxpwifi/uap_txrx.c @@ -246,8 +246,8 @@ int nxpwifi_handle_uap_rx_forward(struct nxpwifi_private *priv, } else { nxpwifi_dbg(adapter, ERROR, "failed to copy skb for uAP\n"); - priv->stats.rx_dropped++; - dev_kfree_skb_any(skb); + priv->stats.rx_dropped++; + dev_kfree_skb_any(skb); return -ENOMEM; } } else { diff --git a/drivers/net/wireless/nxp/nxpwifi/util.c b/drivers/net/wireless/nxp/nxpwifi/util.c index 29ef031f8ec9..bbfefb81d8d3 100644 --- a/drivers/net/wireless/nxp/nxpwifi/util.c +++ b/drivers/net/wireless/nxp/nxpwifi/util.c @@ -799,34 +799,33 @@ int nxpwifi_recv_packet_to_monif(struct nxpwifi_private *priv, __le16 acc_le; u8 flags = 0; - if (ext.timestamp.position <= 15) { - hdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_TIMESTAMP)); - off = ALIGN(off, 8); + hdr->it_present |= + cpu_to_le32(BIT(IEEE80211_RADIOTAP_TIMESTAMP)); + off = ALIGN(off, 8); - if (ext.timestamp.flags & 0x01) { - flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT; - ts = (u32)ext.timestamp.device_timestamp; - } else { - flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_64BIT; - ts = ext.timestamp.device_timestamp; - } - - ts_le = cpu_to_le64(ts); - memcpy(rthdr + off, &ts_le, sizeof(ts_le)); - off += sizeof(ts_le); - - if (ext.timestamp.flags & 0x02) { - accuracy = ext.timestamp.accuracy; - flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY; - } - - acc_le = cpu_to_le16(accuracy); - memcpy(rthdr + off, &acc_le, sizeof(acc_le)); - off += sizeof(acc_le); - rthdr[off++] = (ext.timestamp.unit & 0x0f) | - ((ext.timestamp.position & 0x0f) << 4); - rthdr[off++] = flags; + if (ext.timestamp.flags & 0x01) { + flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT; + ts = (u32)ext.timestamp.device_timestamp; + } else { + flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_64BIT; + ts = ext.timestamp.device_timestamp; } + + ts_le = cpu_to_le64(ts); + memcpy(rthdr + off, &ts_le, sizeof(ts_le)); + off += sizeof(ts_le); + + if (ext.timestamp.flags & 0x02) { + accuracy = ext.timestamp.accuracy; + flags |= IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY; + } + + acc_le = cpu_to_le16(accuracy); + memcpy(rthdr + off, &acc_le, sizeof(acc_le)); + off += sizeof(acc_le); + rthdr[off++] = (ext.timestamp.unit & 0x0f) | + ((ext.timestamp.position & 0x0f) << 4); + rthdr[off++] = flags; } if (format == NXPWIFI_RATE_FORMAT_HE && has_ext) { From 4d8cfff012aaa971d50b01823b1015c1073c3ff6 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 4 Aug 2026 08:26:08 +0000 Subject: [PATCH 399/403] wifi: mac80211: skip default WMM setup for AP_VLAN links AP_VLAN interfaces are never passed to the driver, so setting default WMM parameters on their links trips the check-sdata-in-driver warning in drv_conf_tx(), as well as in the BSS_CHANGED_QOS link info notification. Skip it, matching the existing AP_VLAN handling in this function. Fixes: 2259d14499d1 ("wifi: mac80211: set default WMM parameters on all links") Signed-off-by: Felix Fietkau Link: https://patch.msgid.link/20260804082608.2011433-1-nbd@nbd.name Signed-off-by: Johannes Berg --- net/mac80211/link.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/link.c b/net/mac80211/link.c index dc68144dc363..931950a10508 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -360,7 +360,8 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, link = links[link_id]; ieee80211_link_init(sdata, link_id, &link->data, &link->conf); ieee80211_link_setup(&link->data); - ieee80211_set_wmm_default(&link->data, true, non_sta); + if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN) + ieee80211_set_wmm_default(&link->data, true, non_sta); } if (new_links == 0) From da308f6e471252ae5e8009dcea16bc5e9c9dc5dd Mon Sep 17 00:00:00 2001 From: Stefan Hansson Date: Tue, 4 Aug 2026 10:55:41 +0200 Subject: [PATCH 400/403] wifi: rsi: Fix types to appease CFI Avoids errors like: CFI failure at kthread+0x124/0x1cc (target: rsi_coex_scheduler_thread+0x0/0x1b4 [redpine_91x]; expected type: 0x89fb613d) As seen in the aforementioned error this was tested using the downstream redpine_91x driver found in the Librem 5's downstream source tree. However, it appears that this driver is a modified version of the rsi driver found in mainline Linux and as such I decided to port the changes here too. Signed-off-by: Stefan Hansson Link: https://patch.msgid.link/20260804-rsi-cfi-fix-v2-1-59679a520240@postmarketos.org Signed-off-by: Johannes Berg --- drivers/net/wireless/rsi/rsi_91x_coex.c | 3 ++- drivers/net/wireless/rsi/rsi_91x_main.c | 7 ++++--- drivers/net/wireless/rsi/rsi_91x_sdio_ops.c | 3 ++- drivers/net/wireless/rsi/rsi_91x_usb_ops.c | 7 ++++--- drivers/net/wireless/rsi/rsi_common.h | 2 +- drivers/net/wireless/rsi/rsi_sdio.h | 2 +- drivers/net/wireless/rsi/rsi_usb.h | 2 +- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_coex.c b/drivers/net/wireless/rsi/rsi_91x_coex.c index ee603a5173fb..023c539fc384 100644 --- a/drivers/net/wireless/rsi/rsi_91x_coex.c +++ b/drivers/net/wireless/rsi/rsi_91x_coex.c @@ -50,8 +50,9 @@ static void rsi_coex_sched_tx_pkts(struct rsi_coex_ctrl_block *coex_cb) } while (coex_q != RSI_COEX_Q_INVALID); } -static void rsi_coex_scheduler_thread(struct rsi_common *common) +static int rsi_coex_scheduler_thread(void *data) { + struct rsi_common *common = data; struct rsi_coex_ctrl_block *coex_cb = common->coex_cb; u32 timeout = EVENT_WAIT_FOREVER; diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c index 662e42d1e5e8..2ce514766620 100644 --- a/drivers/net/wireless/rsi/rsi_91x_main.c +++ b/drivers/net/wireless/rsi/rsi_91x_main.c @@ -246,12 +246,13 @@ EXPORT_SYMBOL_GPL(rsi_read_pkt); /** * rsi_tx_scheduler_thread() - This function is a kernel thread to send the * packets to the device. - * @common: Pointer to the driver private structure. + * @data: Pointer to the driver private structure. * - * Return: None. + * Return: 0. */ -static void rsi_tx_scheduler_thread(struct rsi_common *common) +static int rsi_tx_scheduler_thread(void *data) { + struct rsi_common *common = data; struct rsi_hw *adapter = common->priv; u32 timeout = EVENT_WAIT_FOREVER; diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c index 597b238e2294..18a28aa97446 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -62,8 +62,9 @@ int rsi_sdio_master_access_msword(struct rsi_hw *adapter, u16 ms_word) static void rsi_rx_handler(struct rsi_hw *adapter); -void rsi_sdio_rx_thread(struct rsi_common *common) +int rsi_sdio_rx_thread(void *data) { + struct rsi_common *common = data; struct rsi_hw *adapter = common->priv; struct rsi_91x_sdiodev *sdev = adapter->rsi_dev; diff --git a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c index 25c2b232394a..e899631b9aed 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c @@ -21,12 +21,13 @@ /** * rsi_usb_rx_thread() - This is a kernel thread to receive the packets from * the USB device. - * @common: Pointer to the driver private structure. + * @data: Pointer to the driver private structure. * - * Return: None. + * Return: 0. */ -void rsi_usb_rx_thread(struct rsi_common *common) +int rsi_usb_rx_thread(void *data) { + struct rsi_common *common = data; struct rsi_hw *adapter = common->priv; struct rsi_91x_usbdev *dev = adapter->rsi_dev; int status; diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 3cdf9ded876d..2a33a81f71a3 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -58,7 +58,7 @@ static inline void rsi_reset_event(struct rsi_event *event) static inline int rsi_create_kthread(struct rsi_common *common, struct rsi_thread *thread, - void *func_ptr, + int (*func_ptr)(void *data), u8 *name) { init_completion(&thread->completion); diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h index 7c91b126b350..eb2b7f36a7e4 100644 --- a/drivers/net/wireless/rsi/rsi_sdio.h +++ b/drivers/net/wireless/rsi/rsi_sdio.h @@ -134,5 +134,5 @@ int rsi_sdio_master_access_msword(struct rsi_hw *adapter, u16 ms_word); void rsi_sdio_ack_intr(struct rsi_hw *adapter, u8 int_bit); int rsi_sdio_determine_event_timeout(struct rsi_hw *adapter); int rsi_sdio_check_buffer_status(struct rsi_hw *adapter, u8 q_num); -void rsi_sdio_rx_thread(struct rsi_common *common); +int rsi_sdio_rx_thread(void *data); #endif diff --git a/drivers/net/wireless/rsi/rsi_usb.h b/drivers/net/wireless/rsi/rsi_usb.h index 961851748bc4..78067eaffe8b 100644 --- a/drivers/net/wireless/rsi/rsi_usb.h +++ b/drivers/net/wireless/rsi/rsi_usb.h @@ -81,5 +81,5 @@ static inline int rsi_usb_event_timeout(struct rsi_hw *adapter) return EVENT_WAIT_FOREVER; } -void rsi_usb_rx_thread(struct rsi_common *common); +int rsi_usb_rx_thread(void *data); #endif From 0d10db8e94fcb23a799789aaa696b4d8f937e207 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Mon, 3 Aug 2026 11:35:06 +0200 Subject: [PATCH 401/403] wifi: brcmfmac: Fix memory leak in brcmf_sdio_read_control() The memory allocated for buf is not freed in some of the error paths in brcmf_sdio_read_control(). Fix that by adding vfree() calls. Cc: stable@vger.kernel.org Fixes: dd43a01c5cdb ("brcmfmac: use dynamically allocated control frame buffer") Signed-off-by: Abdun Nihaal [arend: rework as suggested by Johannes] Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260803093506.1647790-1-arend.vanspriel@broadcom.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 9f7ed1d293a0..381801af3ac9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -1827,17 +1827,18 @@ brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) if (bus->rxctl) { brcmf_err("last control frame is being processed.\n"); spin_unlock_bh(&bus->rxctl_lock); - vfree(buf); goto done; } bus->rxctl = buf + doff; bus->rxctl_orig = buf; bus->rxlen = len - doff; spin_unlock_bh(&bus->rxctl_lock); + buf = NULL; done: /* Awake any waiters */ brcmf_sdio_dcmd_resp_wake(bus); + vfree(buf); } /* Pad read to blocksize for efficiency */ From 068986fd6f2a5f337d3ef18dda7821dc0b80c778 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Wed, 29 Jul 2026 20:47:13 +0800 Subject: [PATCH 402/403] wifi: nxpwifi: detach sync command buffer on interrupted wait nxpwifi synchronous commands keep the caller-provided data buffer in cmd_node->data_buf. Several callers pass stack-allocated objects there, for example nxpwifi_get_chan_type() and the timeshare_coex debugfs handlers. If wait_event_interruptible_timeout() is interrupted or times out, the caller can return and release that stack object while the command is still current. nxpwifi_cancel_all_pending_cmd() deliberately keeps the current command because a response may still arrive. A late firmware response can then write through cmd_node->data_buf into the stale stack address. After cancelling pending commands, detach the caller-owned buffer from the still-current command under nxpwifi_cmd_lock. Unlike the host command response path, several command response callbacks do not tolerate a NULL data buffer. Most of them ignore it or check it already, but nxpwifi_ret_sta_get_chan_info(), nxpwifi_ret_sta_hs_wakeup_reason() and nxpwifi_ret_sta_robust_coex() dereference it unconditionally, so let them discard a detached response. No caller passes a NULL buffer to these commands today, so this only affects the newly introduced detached state. nxpwifi was derived from mwifiex before commit ef06882c7d8a ("wifi: mwifiex: Detach sync cmd buffer on interrupted wait") and retains the same lifetime bug. Apply the equivalent buffer detachment here. Fixes: 73b01e57ed3e ("wifi: nxp: add nxpwifi driver for IW61x") Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260729124713.2849018-1-lilinmao@kylinos.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/nxp/nxpwifi/sta_cfg.c | 12 ++++++++++++ drivers/net/wireless/nxp/nxpwifi/sta_cmd.c | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c b/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c index 502c96dc4016..702fa1531da1 100644 --- a/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c +++ b/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c @@ -45,6 +45,18 @@ int nxpwifi_wait_queue_complete(struct nxpwifi_adapter *adapter, nxpwifi_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n", status); nxpwifi_cancel_all_pending_cmd(adapter); + + /* + * The response path writes through cmd_node->data_buf. The + * caller can release a stack-allocated data_buf after an + * interrupted wait while a late response is still pending. + * Detach it from the current command before returning. + */ + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + if (adapter->curr_cmd == cmd_queued) + adapter->curr_cmd->data_buf = NULL; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + return status; } diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c b/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c index 0b140e84916f..5e8ffd306b31 100644 --- a/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c +++ b/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c @@ -2133,6 +2133,9 @@ nxpwifi_ret_sta_robust_coex(struct nxpwifi_private *priv, u16 action = le16_to_cpu(coex->action); u32 mode; + if (!is_timeshare) + return 0; + coex_tlv = (struct nxpwifi_ie_types_robust_coex *)((u8 *)coex + sizeof(struct host_cmd_ds_robust_coex)); if (action == HOST_ACT_GEN_GET) { @@ -2679,6 +2682,10 @@ nxpwifi_ret_sta_hs_wakeup_reason(struct nxpwifi_private *priv, { struct host_cmd_ds_wakeup_reason *wakeup_reason = (struct host_cmd_ds_wakeup_reason *)data_buf; + + if (!wakeup_reason) + return 0; + wakeup_reason->wakeup_reason = resp->params.hs_wakeup_reason.wakeup_reason; @@ -2771,6 +2778,9 @@ nxpwifi_ret_sta_get_chan_info(struct nxpwifi_private *priv, (struct nxpwifi_channel_band *)data_buf; struct host_cmd_tlv_channel_band *tlv_band_channel; + if (!channel_band) + return 0; + tlv_band_channel = (struct host_cmd_tlv_channel_band *)sta_cfg_cmd->tlv_buffer; memcpy(&channel_band->band_config, &tlv_band_channel->band_config, From ca800a9302764c445de0da0e84d2252400a770ee Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Wed, 29 Jul 2026 16:24:57 +0800 Subject: [PATCH 403/403] wifi: nxpwifi: bound uAP association event IEs to the event buffer nxpwifi_uap_event_sta_assoc() exposes the association request IEs that the firmware reports in the uAP association event, which the driver copies into the fixed-size event_body[] buffer. event->len is supplied by firmware and is not validated. A value smaller than the header underflows the subtraction used for assoc_req_ies_len, while a larger value can make the IE range extend beyond event_body[]. Subsequent IE parsing can then read past the adapter object. Validate both bounds before using the firmware-reported length. nxpwifi was derived from mwifiex before commit f0858bfc7d3c ("wifi: mwifiex: bound uAP association event IEs to the event buffer") and retains the same unchecked length. Apply the equivalent bounds check here. Fixes: 73b01e57ed3e ("wifi: nxp: add nxpwifi driver for IW61x") Signed-off-by: Linmao Li Reviewed-by: Jeff Chen Link: https://patch.msgid.link/20260729082457.1897303-1-lilinmao@kylinos.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/nxp/nxpwifi/uap_event.c | 22 ++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/nxp/nxpwifi/uap_event.c b/drivers/net/wireless/nxp/nxpwifi/uap_event.c index ed8e24ae9c0a..9f717a3d7ec5 100644 --- a/drivers/net/wireless/nxp/nxpwifi/uap_event.c +++ b/drivers/net/wireless/nxp/nxpwifi/uap_event.c @@ -107,11 +107,29 @@ nxpwifi_uap_event_sta_assoc(struct nxpwifi_private *priv) len = ETH_ALEN; if (len != -1) { + u16 evt_len = le16_to_cpu(event->len); + sinfo->assoc_req_ies = &event->data[len]; len = (u8 *)sinfo->assoc_req_ies - (u8 *)&event->frame_control; - sinfo->assoc_req_ies_len = - le16_to_cpu(event->len) - (u16)len; + + /* + * event->len is reported by the device firmware + * and is not otherwise validated. Reject a length + * that underflows the header or that would place + * the association request IEs outside the fixed + * event_body[] buffer. + */ + if (evt_len < len || + (u8 *)&event->frame_control + evt_len > + adapter->event_body + MAX_EVENT_SIZE) { + nxpwifi_dbg(adapter, ERROR, + "invalid STA assoc event length\n"); + kfree(sinfo); + return -EINVAL; + } + + sinfo->assoc_req_ies_len = evt_len - (u16)len; } } cfg80211_new_sta(priv->netdev->ieee80211_ptr, event->sta_addr, sinfo,