From 7068c379cf9aa8afe4dce4d9d82390187aa9c4d0 Mon Sep 17 00:00:00 2001 From: Christos Longros Date: Sun, 29 Mar 2026 09:38:57 +0200 Subject: [PATCH 001/105] wifi: rtw89: fix wrong pci_get_drvdata type in AER handlers rtw89 stores an ieee80211_hw pointer via pci_set_drvdata() at probe time, but io_error_detected() and io_resume() retrieve it as a net_device pointer. This causes netif_device_detach/attach to operate on an ieee80211_hw struct, reading and writing at wrong offsets. The adjacent io_slot_reset() already does it correctly. Use ieee80211_stop_queues/wake_queues instead, consistent with every other queue stop/start path in the driver. Tested on RTL8852CE by calling the handlers from a test module before and after the fix. Fixes: 16e3d93c6183 ("wifi: rtw89: pci: add PCI Express error handling") Signed-off-by: Christos Longros Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260329073857.113081-1-chris.longros@gmail.com --- drivers/net/wireless/realtek/rtw89/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 43c61b3dc969..64554eb35a72 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -4624,9 +4624,9 @@ EXPORT_SYMBOL(rtw89_pm_ops); static pci_ers_result_t rtw89_pci_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); - netif_device_detach(netdev); + ieee80211_stop_queues(hw); return PCI_ERS_RESULT_NEED_RESET; } @@ -4643,12 +4643,12 @@ static pci_ers_result_t rtw89_pci_io_slot_reset(struct pci_dev *pdev) static void rtw89_pci_io_resume(struct pci_dev *pdev) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); /* ack any pending wake events, disable PME */ pci_enable_wake(pdev, PCI_D0, 0); - netif_device_attach(netdev); + ieee80211_wake_queues(hw); } const struct pci_error_handlers rtw89_pci_err_handler = { From d603f2cfa623e9446b44d0c9bcc2cdd92cbda594 Mon Sep 17 00:00:00 2001 From: Chelsy Ratnawat Date: Tue, 7 Apr 2026 05:34:27 -0700 Subject: [PATCH 002/105] wifi: rtlwifi: rtl8821ae: Remove dead code in rtl8821ae_update_hal_rate_table() The variable 'mimo_ps' is initialized to IEEE80211_SMPS_OFF and never modified throughout the function. This makes the condition checking for IEEE80211_SMPS_STATIC always evaluate to false, rendering the entire if-branch unreachable dead code. The error was reported by Coverity Scan. Signed-off-by: Chelsy Ratnawat Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260407123427.14612-1-chelsyratnawat2001@gmail.com --- .../wireless/realtek/rtlwifi/rtl8821ae/hw.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index 3a4a33476205..c4dcae2d375e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -3257,9 +3257,9 @@ static void rtl8821ae_update_hal_rate_table(struct ieee80211_hw *hw, u32 ratr_value; u8 ratr_index = 0; u8 b_nmode = mac->ht_enable; - u8 mimo_ps = IEEE80211_SMPS_OFF; u16 shortgi_rate; u32 tmp_ratr_value; + u32 ratr_mask; u8 curtxbw_40mhz = mac->bw_40; u8 b_curshortgi_40mhz = (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; @@ -3288,19 +3288,14 @@ static void rtl8821ae_update_hal_rate_table(struct ieee80211_hw *hw, case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: b_nmode = 1; - if (mimo_ps == IEEE80211_SMPS_STATIC) { - ratr_value &= 0x0007F005; - } else { - u32 ratr_mask; - if (get_rf_type(rtlphy) == RF_1T2R || - get_rf_type(rtlphy) == RF_1T1R) - ratr_mask = 0x000ff005; - else - ratr_mask = 0x0f0ff005; + 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_value &= ratr_mask; break; default: if (rtlphy->rf_type == RF_1T2R) From 706183dbef4a79d120d4e928f693bea50df496f8 Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Mon, 13 Apr 2026 14:59:26 +0800 Subject: [PATCH 003/105] wifi: rtw88: fix wrong pci_get_drvdata type in AER handlers rtw88 stores an ieee80211_hw pointer via pci_set_drvdata() at probe time, but io_error_detected() and io_resume() retrieve it as a net_device pointer. This causes netif_device_detach/attach to operate on an ieee80211_hw struct, reading and writing at wrong offsets. Use ieee80211_stop_queues/wake_queues instead, consistent with every other queue stop/start path in the driver. Fixes: cdb82c80b934 ("wifi: rtw88: pci: add PCI Express error handling") Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260413065926.17027-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw88/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index bba370ad510c..c56beacbb1b0 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1711,9 +1711,9 @@ static void rtw_pci_napi_deinit(struct rtw_dev *rtwdev) static pci_ers_result_t rtw_pci_io_err_detected(struct pci_dev *pdev, pci_channel_state_t state) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); - netif_device_detach(netdev); + ieee80211_stop_queues(hw); return PCI_ERS_RESULT_NEED_RESET; } @@ -1730,12 +1730,12 @@ static pci_ers_result_t rtw_pci_io_slot_reset(struct pci_dev *pdev) static void rtw_pci_io_resume(struct pci_dev *pdev) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); /* ack any pending wake events, disable PME */ pci_enable_wake(pdev, PCI_D0, 0); - netif_device_attach(netdev); + ieee80211_wake_queues(hw); } const struct pci_error_handlers rtw_pci_err_handler = { From 26769329c40a4d7f443f22f526de19eeedea67a2 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 14 Apr 2026 14:22:27 +0800 Subject: [PATCH 004/105] wifi: rtl8xxxu: validate action frame size before using in rtl8xxxu_dump_action() The rtl8xxxu_dump_action() is to print action frames when turning on debug mask. Validate the skb->len size to prevent potential broken in monitor mode injection. Compile tested only. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260414062229.21047-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index f20fade0c099..508137e4a87a 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -5126,7 +5126,7 @@ static void rtl8xxxu_tx_complete(struct urb *urb) } static void rtl8xxxu_dump_action(struct device *dev, - struct ieee80211_hdr *hdr) + struct ieee80211_hdr *hdr, unsigned int skb_len) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)hdr; u16 cap, timeout; @@ -5134,8 +5134,14 @@ static void rtl8xxxu_dump_action(struct device *dev, if (!(rtl8xxxu_debug & RTL8XXXU_DEBUG_ACTION)) return; + if (skb_len < IEEE80211_MIN_ACTION_SIZE(action_code)) + return; + switch (mgmt->u.action.action_code) { case WLAN_ACTION_ADDBA_RESP: + if (skb_len < IEEE80211_MIN_ACTION_SIZE(addba_resp)) + break; + cap = le16_to_cpu(mgmt->u.action.addba_resp.capab); timeout = le16_to_cpu(mgmt->u.action.addba_resp.timeout); dev_info(dev, "WLAN_ACTION_ADDBA_RESP: " @@ -5148,6 +5154,9 @@ static void rtl8xxxu_dump_action(struct device *dev, le16_to_cpu(mgmt->u.action.addba_resp.status)); break; case WLAN_ACTION_ADDBA_REQ: + if (skb_len < IEEE80211_MIN_ACTION_SIZE(addba_req)) + break; + cap = le16_to_cpu(mgmt->u.action.addba_req.capab); timeout = le16_to_cpu(mgmt->u.action.addba_req.timeout); dev_info(dev, "WLAN_ACTION_ADDBA_REQ: " @@ -5437,7 +5446,7 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, } if (ieee80211_is_action(hdr->frame_control)) - rtl8xxxu_dump_action(dev, hdr); + rtl8xxxu_dump_action(dev, hdr, skb->len); tx_info->rate_driver_data[0] = hw; From c9f10419c38ed619fadad7b8d72f05fca5b4ade8 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 14 Apr 2026 14:22:28 +0800 Subject: [PATCH 005/105] wifi: rtlwifi: validate action frame size in rtl_action_proc() Since action frames might be malformed from RX or injected TX, validate the size before using. More, use struct ieee80211_mgmt to access fields of action frames instead of counting barely. Tested with ping on RTL8723BE. The add BA request action frames of TX/RX can be handled properly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260414062229.21047-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtlwifi/base.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index aad377864e73..9e98c01bb90e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -1369,18 +1369,19 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct ieee80211_hdr *hdr = rtl_get_hdr(skb); struct rtl_priv *rtlpriv = rtl_priv(hw); + struct ieee80211_mgmt *mgmt; __le16 fc = rtl_get_fc(skb); - u8 *act = (u8 *)(((u8 *)skb->data + MAC80211_3ADDR_LEN)); - u8 category; if (!ieee80211_is_action(fc)) return true; - category = *act; - act++; - switch (category) { + mgmt = (void *)skb->data; + if (skb->len < IEEE80211_MIN_ACTION_SIZE(action_code)) + return true; + + switch (mgmt->u.action.category) { case ACT_CAT_BA: - switch (*act) { + switch (mgmt->u.action.action_code) { case ACT_ADDBAREQ: if (mac->act_scanning) return false; @@ -1394,9 +1395,11 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) struct ieee80211_sta *sta = NULL; struct rtl_sta_info *sta_entry = NULL; struct rtl_tid_data *tid_data; - struct ieee80211_mgmt *mgmt = (void *)skb->data; u16 capab = 0, tid = 0; + if (skb->len < IEEE80211_MIN_ACTION_SIZE(addba_req)) + return true; + rcu_read_lock(); sta = rtl_find_sta(hw, hdr->addr3); if (sta == NULL) { From 043f09bb78ddb98dd5d587bf8fe4836daeb3a735 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 14 Apr 2026 14:22:29 +0800 Subject: [PATCH 006/105] wifi: rtlwifi: validate action frame size before using in _rtl_pci_tx_isr() Since TX action frames might be malformed, validate the size before using. Tested on RTL8723BE. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260414062229.21047-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtlwifi/pci.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 9cc0a871ea3c..73018a0498b4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -505,9 +505,10 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio) } } if (ieee80211_is_action(fc)) { - struct ieee80211_mgmt *action_frame = - (struct ieee80211_mgmt *)skb->data; - if (action_frame->u.action.action_code == + struct ieee80211_mgmt *action_frame = (void *)skb->data; + + if (skb->len >= IEEE80211_MIN_ACTION_SIZE(action_code) && + action_frame->u.action.action_code == WLAN_HT_ACTION_SMPS) { dev_kfree_skb(skb); goto tx_status_ok; From d5e6f353ce1e1c25b8458ea390ed09d2377412c5 Mon Sep 17 00:00:00 2001 From: Panagiotis Petrakopoulos Date: Wed, 15 Apr 2026 08:29:59 +0300 Subject: [PATCH 007/105] wifi: rtw88: Add NULL check for chip->edcca_th in rtw_fw_adaptivity_result() It was recently reported that rtw_fw_adaptivity_result() in fw.c dereferences rtwdev->chip->edcca_th without a NULL check. The issue is that devices with the 8821CE chip don't define edcca_th in their chip info. As a result, when rtw_fw_adaptivity_result() tries to dereference it, the kernel triggers an oops. Add a NULL check for edcca_th before dereferencing it in rtw_fw_adaptivity_result() in fw.c. Placing the check at the function entry avoids logging any garbage values. This change does not address the root cause for this behavior, but it prevents the NULL dereference and the resulting oops while a more permanent solution is developed. Tested on a 8822CE chip which defines edcca_th, so this issue is not present on it, but it still uses this driver and I can verify there are no regressions. Suggested-by: Ping-Ke Shih Reported-by: Oleksandr Havrylov Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221286 Signed-off-by: Panagiotis Petrakopoulos Acked-by: Ping-Ke Shih Tested-by: Oleksandr Havrylov Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260415052959.14844-1-npetrakopoulos2003@gmail.com --- drivers/net/wireless/realtek/rtw88/fw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index 48207052e3f8..945fedcd375b 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -279,6 +279,9 @@ static void rtw_fw_adaptivity_result(struct rtw_dev *rtwdev, u8 *payload, const struct rtw_hw_reg_offset *edcca_th = rtwdev->chip->edcca_th; struct rtw_c2h_adaptivity *result = (struct rtw_c2h_adaptivity *)payload; + if (!edcca_th) + return; + rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "Adaptivity: density %x igi %x l2h_th_init %x l2h %x h2l %x option %x\n", result->density, result->igi, result->l2h_th_init, result->l2h, From 5055188134c3cc755333fa19e5b8a0f3cb6fbb9a Mon Sep 17 00:00:00 2001 From: Louis Kotze Date: Thu, 16 Apr 2026 06:55:36 +0200 Subject: [PATCH 008/105] wifi: rtw89: phy: increase RF calibration timeouts for USB transport USB transport adds significant latency to H2C/C2H round-trips used by RF calibration. The existing timeout values were designed for PCIe and are too tight for USB, causing "failed to wait RF DACK", "failed to wait RF TSSI" and similar errors on USB adapters. Apply a 4x timeout multiplier when the device uses USB transport. The multiplier is applied in rtw89_phy_rfk_report_wait() so all calibrations benefit without changing any call sites or PCIe timeout values. The 4x multiplier was chosen based on measured data from two independent testers (RTL8922AU, 6GHz MLO and 2.4/5GHz): Calibration PCIe timeout Max measured (USB) 4x timeout PRE_NTFY 5ms 1ms 20ms DACK 58ms 72ms 232ms RX_DCK 128ms 374ms 512ms TSSI normal 20ms 24ms 80ms TSSI scan 6ms 14ms 24ms TXGAPK 54ms 18ms 216ms IQK 84ms 53ms 336ms DPK 34ms 30ms 136ms Tested with RTL8922AU on 6GHz MLO (5GHz + 6GHz simultaneous): 25 connect/disconnect cycles with zero failures. The 4x multiplier was also verified under adverse host conditions on 5GHz. 5 cycles per scenario, stress-ng as the load generator, max observed time per calibration: Calibration PCIe 4x Baseline CPU stress Mem stress Combined PRE_NTFY 5 20 0 0 0 1 DACK 58 232 71 (!) 71 (!) 71 (!) 71 (!) RX_DCK 128 512 23 22 22 23 IQK 84 336 53 53 53 53 DPK 34 136 23 23 26 23 TSSI 20 80 6 9 14 9 TXGAPK 54 216 16 16 16 16 Legend: (!) = exceeds PCIe budget but within 4x budget. Two observations from that matrix: 1. DACK exceeds the stock PCIe budget (58ms) in baseline on 5GHz on this hardware. Without the 4x multiplier, DACK fails -ETIMEDOUT deterministically on every connect, no stress needed. This is the condition the patch addresses. 2. Calibration times appear dominated by USB transport round-trip latency rather than host load, though hardware and external component factors may also contribute. DACK stays at 71ms across all four scenarios. Host-side stress has essentially zero effect on observed calibration duration. Bumping the multiplier above 4x would not address a failure mode that this stress matrix produces. Reported-by: Devin Wittmayer Link: https://github.com/Lucid-Duck/rtw89-usb3-gap/tree/main/evidence/crash-2026-04-11 Signed-off-by: Louis Kotze Tested-by: Devin Wittmayer # RTL8922AU (BrosTrend BE6500) Tested-by: Devin Wittmayer # RTL8852AU (D-Link DWA-X1850 A1) Tested-by: Devin Wittmayer # RTL8852AU (D-Link DWA-X1850 B1) Tested-by: Devin Wittmayer # RTL8852BU (BrosTrend AX4L) Tested-by: Devin Wittmayer # RTL8852CU (EDUP AX5400) Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260416045536.817930-1-loukot@gmail.com --- drivers/net/wireless/realtek/rtw89/phy.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index e70d0e283987..1f249c297006 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -3956,6 +3956,14 @@ int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name, struct rtw89_rfk_wait_info *wait = &rtwdev->rfk_wait; unsigned long time_left; + /* + * USB transport adds latency to H2C/C2H round-trips, so RF + * calibrations take longer than on PCIe. Apply a 4x multiplier + * to avoid spurious timeouts. + */ + if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) + ms *= 4; + /* Since we can't receive C2H event during SER, use a fixed delay. */ if (test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags)) { fsleep(1000 * ms / 2); From 718c023ffe410ded96d5655bdec5c3a7239a369c Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 16:05:17 -0400 Subject: [PATCH 009/105] wifi: rtlwifi: fix typos in comments in rtl8821ae_card_disable() Fix typos in rtl8821ae_card_disable() comments. Signed-off-by: Hugo Villeneuve Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260417200520.3919437-1-hugo@hugovil.com --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index c4dcae2d375e..9b119a51bc30 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -2355,7 +2355,7 @@ void rtl8821ae_card_disable(struct ieee80211_hw *hw) rtl8821ae_set_fw_disconnect_decision_ctrl_cmd(hw, true); } - /* 3 <3> Hw Configutations */ + /* 3 <3> Hw Configurations */ /* Wait until Rx DMA Finished before host sleep. * FW Pause Rx DMA may happen when received packet doing DMA. From 8d722eeac1a7964cf6f77b464e87082418e9657e Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:40:10 +0300 Subject: [PATCH 010/105] wifi: rtw89: usb: Support 2 bulk in endpoints RTL8912AU has 2 bulk in endpoints, not 1, so raise the limit. The second bulk-in is for USB interrupt mode for SER (system error recovery) flow. SER is not currently implemented for USB devices in rtw89. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/3d30c8d1-fa25-48d0-b27d-7d634c5aa005@gmail.com --- drivers/net/wireless/realtek/rtw89/usb.c | 15 +++++++++++---- drivers/net/wireless/realtek/rtw89/usb.h | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 767a95f759b1..d3c7ed29cfe5 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -511,7 +511,7 @@ static void rtw89_usb_rx_resubmit(struct rtw89_usb *rtwusb, rxcb->rx_skb = rx_skb; usb_fill_bulk_urb(rxcb->rx_urb, rtwusb->udev, - usb_rcvbulkpipe(rtwusb->udev, rtwusb->in_pipe), + usb_rcvbulkpipe(rtwusb->udev, rtwusb->in_pipe[0]), rxcb->rx_skb->data, RTW89_USB_RECVBUF_SZ, rtw89_usb_read_port_complete, rxcb); @@ -948,6 +948,7 @@ static int rtw89_usb_parse(struct rtw89_dev *rtwdev, struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); struct usb_endpoint_descriptor *endpoint; int num_out_pipes = 0; + int num_in_pipes = 0; u8 num; int i; @@ -963,13 +964,14 @@ static int rtw89_usb_parse(struct rtw89_dev *rtwdev, if (usb_endpoint_dir_in(endpoint) && usb_endpoint_xfer_bulk(endpoint)) { - if (rtwusb->in_pipe) { + if (num_in_pipes >= RTW89_MAX_BULKIN_NUM) { rtw89_err(rtwdev, - "found more than 1 bulk in endpoint\n"); + "found more than %d bulk in endpoint\n", + RTW89_MAX_BULKIN_NUM); return -EINVAL; } - rtwusb->in_pipe = num; + rtwusb->in_pipe[num_in_pipes++] = num; } if (usb_endpoint_dir_out(endpoint) && @@ -985,6 +987,11 @@ static int rtw89_usb_parse(struct rtw89_dev *rtwdev, } } + if (num_in_pipes < 1) { + rtw89_err(rtwdev, "no bulk in endpoints found\n"); + return -EINVAL; + } + if (num_out_pipes < 1) { rtw89_err(rtwdev, "no bulk out endpoints found\n"); return -EINVAL; diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h index 507f61f58ed9..82de700eb142 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.h +++ b/drivers/net/wireless/realtek/rtw89/usb.h @@ -18,6 +18,7 @@ #define RTW89_USB_MOD512_PADDING 4 #define RTW89_MAX_ENDPOINT_NUM 9 +#define RTW89_MAX_BULKIN_NUM 2 #define RTW89_MAX_BULKOUT_NUM 7 #define R_AX_RXAGG_0_V1 0x6000 @@ -65,7 +66,7 @@ struct rtw89_usb { atomic_t continual_io_error; - u8 in_pipe; + u8 in_pipe[RTW89_MAX_BULKIN_NUM]; u8 out_pipe[RTW89_MAX_BULKOUT_NUM]; struct workqueue_struct *rxwq; From 6f4621340312d7cfd4f2acc3115cd5879fd770c3 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:40:59 +0300 Subject: [PATCH 011/105] wifi: rtw89: Fix rtw89_usb_ops_mac_lv1_rcvy() for RTL8922AU RTL8922AU uses a different register and mask to reset TX/RX. Add them here. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/fd6ae7f6-0035-45d8-8832-49a053fd217d@gmail.com --- drivers/net/wireless/realtek/rtw89/reg.h | 4 ++++ drivers/net/wireless/realtek/rtw89/usb.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 42ffe83931a3..570d5883761b 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -4806,6 +4806,10 @@ #define R_BE_LTR_LATENCY_IDX2_V1 0x361C #define R_BE_LTR_LATENCY_IDX3_V1 0x3620 +#define R_BE_USB2_WLAN_TRX_OPT_PAR2 0x41BC +#define B_BE_USB2_USBRX_RST BIT(1) +#define B_BE_USB2_USBTX_RST BIT(0) + #define R_BE_HCI_BUF_IMR 0x6018 #define B_BE_HCI_BUF_IMR_CLR 0xC0000303 #define B_BE_HCI_BUF_IMR_SET 0xC0000301 diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index d3c7ed29cfe5..fafc2b33e561 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -871,6 +871,10 @@ static int rtw89_usb_ops_mac_lv1_rcvy(struct rtw89_dev *rtwdev, reg = R_AX_USB_WLAN0_1_V1; mask = B_AX_USBRX_RST_V1 | B_AX_USBTX_RST_V1; break; + case RTL8922A: + reg = R_BE_USB2_WLAN_TRX_OPT_PAR2; + mask = B_BE_USB2_USBRX_RST | B_BE_USB2_USBTX_RST; + break; default: rtw89_err(rtwdev, "%s: fix me\n", __func__); return -EOPNOTSUPP; From 0cc43249ea97946cc8d5e315246bad8369c31f27 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:42:38 +0300 Subject: [PATCH 012/105] wifi: rtw89: Fix rtw89_usb_ops_mac_pre_init() for RTL8922AU Return early because RTL8922AU doesn't need to do anything here. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/58bf4dc4-dd4d-4caa-b6c0-e90749769cea@gmail.com --- drivers/net/wireless/realtek/rtw89/usb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index fafc2b33e561..3d9113ae7fb4 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -756,6 +756,9 @@ static int rtw89_usb_ops_mac_pre_init(struct rtw89_dev *rtwdev) const struct rtw89_usb_info *info = rtwusb->info; u32 val32; + if (rtwdev->chip->chip_id == RTL8922A) + return 0; + rtw89_write32_set(rtwdev, info->usb_host_request_2, B_AX_R_USBIO_MODE); From 0df7f0c22d96024f4a9db9f32693c6ea527c0c9d Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:43:12 +0300 Subject: [PATCH 013/105] wifi: rtw89: Fix rtw89_usb_ops_mac_post_init() for RTL8922AU Skip most of the function because RTL8922AU only needs to configure the RX aggregation here. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/71329cd6-0249-4654-be1d-74c8823f4888@gmail.com --- drivers/net/wireless/realtek/rtw89/usb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 3d9113ae7fb4..a08e43c8275e 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -827,6 +827,9 @@ static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev) enum usb_device_speed speed; u32 ep; + if (rtwdev->chip->chip_id == RTL8922A) + goto rx_agg_cfg; + rtw89_write32_clr(rtwdev, info->usb3_mac_npi_config_intf_0, B_AX_SSPHY_LFPS_FILTER); @@ -848,6 +851,7 @@ static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev) rtw89_write8(rtwdev, info->usb_endpoint_2 + 1, NUMP); } +rx_agg_cfg: rtw89_usb_rx_agg_cfg(rtwdev); return 0; From 32cee8a9bb66ceb434b9eae32beb17403287c4d5 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:43:53 +0300 Subject: [PATCH 014/105] wifi: rtw89: usb: Enable RX aggregation for RTL8922AU It uses the same settings as RTL8852CU, but the register and bit names have "BE" instead of "AX". Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/bd9e444f-257c-48c8-8adb-f58432b2c5c2@gmail.com --- drivers/net/wireless/realtek/rtw89/usb.c | 14 ++++++++++++++ drivers/net/wireless/realtek/rtw89/usb.h | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index a08e43c8275e..c6d55e669776 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -803,6 +803,17 @@ static void rtw89_usb_rx_agg_cfg_v2(struct rtw89_dev *rtwdev) rtw89_write32(rtwdev, R_AX_RXAGG_1_V1, 0x1F); } +static void rtw89_usb_rx_agg_cfg_v3(struct rtw89_dev *rtwdev) +{ + const u32 rxagg_0 = FIELD_PREP_CONST(B_BE_RXAGG_0_EN, 1) | + FIELD_PREP_CONST(B_BE_RXAGG_0_NUM_TH, 255) | + FIELD_PREP_CONST(B_BE_RXAGG_0_TIME_32US_TH, 32) | + FIELD_PREP_CONST(B_BE_RXAGG_0_BUF_SZ_1K, 20); + + rtw89_write32(rtwdev, R_BE_RXAGG_0_V1, rxagg_0); + rtw89_write32(rtwdev, R_BE_RXAGG_1_V1, 0x1F); +} + static void rtw89_usb_rx_agg_cfg(struct rtw89_dev *rtwdev) { switch (rtwdev->chip->chip_id) { @@ -814,6 +825,9 @@ static void rtw89_usb_rx_agg_cfg(struct rtw89_dev *rtwdev) case RTL8852C: rtw89_usb_rx_agg_cfg_v2(rtwdev); break; + case RTL8922A: + rtw89_usb_rx_agg_cfg_v3(rtwdev); + break; default: rtw89_warn(rtwdev, "%s: USB RX agg not support\n", __func__); return; diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h index 82de700eb142..bdf312559743 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.h +++ b/drivers/net/wireless/realtek/rtw89/usb.h @@ -29,6 +29,14 @@ #define R_AX_RXAGG_1_V1 0x6004 +#define R_BE_RXAGG_0_V1 0x6000 +#define B_BE_RXAGG_0_EN BIT(31) +#define B_BE_RXAGG_0_NUM_TH GENMASK(23, 16) +#define B_BE_RXAGG_0_TIME_32US_TH GENMASK(15, 8) +#define B_BE_RXAGG_0_BUF_SZ_1K GENMASK(7, 0) + +#define R_BE_RXAGG_1_V1 0x6004 + #define R_AX_RXAGG_0 0x8900 #define B_AX_RXAGG_0_BUF_SZ_4K GENMASK(7, 0) From c3dfef1e3552a25ce0daa6c8610caf048375c588 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:44:54 +0300 Subject: [PATCH 015/105] wifi: rtw89: Fix rtw8922a_pwr_{on,off}_func() for USB There are a few differences in the power on/off functions between PCIE and USB. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/dccdb944-c31d-483f-8d1a-a51bcfe23916@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 92 +++++++++++++------ 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 8f6cf64271e8..2bea44faa109 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -298,17 +298,24 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) rtw89_write32_clr(rtwdev, R_BE_FEN_RST_ENABLE, B_BE_R_SYM_ISO_ADDA_P02PP | B_BE_R_SYM_ISO_ADDA_P12PP); rtw89_write8_set(rtwdev, R_BE_PLATFORM_ENABLE, B_BE_PLATFORM_EN); - rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); - ret = read_poll_timeout(rtw89_read32, val32, val32 & B_BE_HAXIDMA_IO_ST, - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { + rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); - ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + ret = read_poll_timeout(rtw89_read32, val32, + val32 & B_BE_HAXIDMA_IO_ST, + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + + ret = read_poll_timeout(rtw89_read32, val32, + !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + } rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_EN); @@ -317,7 +324,9 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) if (ret) return ret; - rtw89_write32_clr(rtwdev, R_BE_SYS_SDIO_CTRL, B_BE_PCIE_FORCE_IBX_EN); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + rtw89_write32_clr(rtwdev, R_BE_SYS_SDIO_CTRL, + B_BE_PCIE_FORCE_IBX_EN); ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_PLL, 0x02, 0x02); if (ret) @@ -371,6 +380,10 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) if (ret) return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_SRAM_CTRL, 0, XTAL_SI_SRAM_DIS); + if (ret) + return ret; + if (hal->cv != CHIP_CAV) { rtw89_write32_set(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK); rtw89_write32_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_ISO_EB2CORE); @@ -382,14 +395,16 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) rtw89_write32_clr(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK); } - rtw89_write32_set(rtwdev, R_BE_DMAC_FUNC_EN, - B_BE_MAC_FUNC_EN | B_BE_DMAC_FUNC_EN | B_BE_MPDU_PROC_EN | - B_BE_WD_RLS_EN | B_BE_DLE_WDE_EN | B_BE_TXPKT_CTRL_EN | - B_BE_STA_SCH_EN | B_BE_DLE_PLE_EN | B_BE_PKT_BUF_EN | - B_BE_DMAC_TBL_EN | B_BE_PKT_IN_EN | B_BE_DLE_CPUIO_EN | - B_BE_DISPATCHER_EN | B_BE_BBRPT_EN | B_BE_MAC_SEC_EN | - B_BE_H_AXIDMA_EN | B_BE_DMAC_MLO_EN | B_BE_PLRLS_EN | - B_BE_P_AXIDMA_EN | B_BE_DLE_DATACPUIO_EN | B_BE_LTR_CTL_EN); + val32 = B_BE_MAC_FUNC_EN | B_BE_DMAC_FUNC_EN | B_BE_MPDU_PROC_EN | + B_BE_WD_RLS_EN | B_BE_DLE_WDE_EN | B_BE_TXPKT_CTRL_EN | + B_BE_STA_SCH_EN | B_BE_DLE_PLE_EN | B_BE_PKT_BUF_EN | + B_BE_DMAC_TBL_EN | B_BE_PKT_IN_EN | B_BE_DLE_CPUIO_EN | + B_BE_DISPATCHER_EN | B_BE_BBRPT_EN | B_BE_MAC_SEC_EN | + B_BE_H_AXIDMA_EN | B_BE_DMAC_MLO_EN | B_BE_PLRLS_EN | + B_BE_P_AXIDMA_EN | B_BE_DLE_DATACPUIO_EN; + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + val32 |= B_BE_LTR_CTL_EN; + rtw89_write32_set(rtwdev, R_BE_DMAC_FUNC_EN, val32); set_bit(RTW89_FLAG_DMAC_FUNC, rtwdev->flags); @@ -465,17 +480,23 @@ static int rtw8922a_pwr_off_func(struct rtw89_dev *rtwdev) if (ret) return ret; - rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { + rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); - ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HAXIDMA_IO_ST), - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + ret = read_poll_timeout(rtw89_read32, val32, + !(val32 & B_BE_HAXIDMA_IO_ST), + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; - ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + ret = read_poll_timeout(rtw89_read32, val32, + !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + } rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_EN); @@ -491,9 +512,22 @@ static int rtw8922a_pwr_off_func(struct rtw89_dev *rtwdev) if (ret) return ret; - rtw89_write32(rtwdev, R_BE_WLLPS_CTRL, 0x0000A1B2); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + rtw89_write32(rtwdev, R_BE_WLLPS_CTRL, 0x0000A1B2); + else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) + rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_SOP_EASWR); + rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_XTAL_OFF_A_DIE); - rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS); + + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { + rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS); + } else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) { + val32 = rtw89_read32(rtwdev, R_BE_SYS_PW_CTRL); + val32 |= B_BE_AFSM_WLSUS_EN; + val32 &= ~B_BE_AFSM_PCIE_SUS_EN; + rtw89_write32(rtwdev, R_BE_SYS_PW_CTRL, val32); + } + rtw89_write32(rtwdev, R_BE_UDM1, 0); return 0; From c423ed43c93cd1e44dbcd77f94aff747df7b20a1 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:45:23 +0300 Subject: [PATCH 016/105] wifi: rtw89: Let hfc_param_ini have separate settings for USB 2/3 RTL8912AU needs different settings for USB 2 and USB 3. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/588bf309-4b92-454f-bfd6-5e81248abee3@gmail.com --- drivers/net/wireless/realtek/rtw89/core.h | 2 +- drivers/net/wireless/realtek/rtw89/mac.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852bt.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 +- 9 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index fd29dbbb120d..2d7d3a11be16 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4544,7 +4544,7 @@ struct rtw89_chip_info { u16 max_rx_agg_num; bool dis_2g_40m_ul_ofdma; u32 rsvd_ple_ofst; - const struct rtw89_hfc_param_ini *hfc_param_ini[RTW89_HCI_TYPE_NUM]; + const struct rtw89_hfc_param_ini *hfc_param_ini[RTW89_HCI_DLE_TYPE_NUM]; const struct rtw89_dle_mem *dle_mem[RTW89_HCI_DLE_TYPE_NUM]; u8 wde_qempty_acq_grpnum; u8 wde_qempty_mgq_grpsel; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 54aad37485d6..8d5375ec33a2 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -898,7 +898,7 @@ static int hfc_reset_param(struct rtw89_dev *rtwdev) struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; u8 qta_mode = rtwdev->mac.dle_info.qta_mode; - param_inis = rtwdev->chip->hfc_param_ini[rtwdev->hci.type]; + param_inis = rtwdev->chip->hfc_param_ini[rtwdev->hci.dle_type]; if (!param_inis) return -EINVAL; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 84bdd39b3ceb..ff4113eb9114 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2600,6 +2600,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x2f800, .hfc_param_ini = {rtw8851b_hfc_param_ini_pcie, + rtw8851b_hfc_param_ini_usb, rtw8851b_hfc_param_ini_usb, NULL}, .dle_mem = {rtw8851b_dle_mem_pcie, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 1d4f1df524a1..ea4d22616512 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2336,6 +2336,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x6f800, .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie, + rtw8852a_hfc_param_ini_usb, rtw8852a_hfc_param_ini_usb, NULL}, .dle_mem = {rtw8852a_dle_mem_pcie, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 5e8738bb2dc2..417b1502a2dd 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -931,6 +931,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x2f800, .hfc_param_ini = {rtw8852b_hfc_param_ini_pcie, + rtw8852b_hfc_param_ini_usb, rtw8852b_hfc_param_ini_usb, NULL}, .dle_mem = {rtw8852b_dle_mem_pcie, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index ab4263bc8b9f..7d86e9598ca8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -776,7 +776,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852bt_hfc_param_ini_pcie, NULL, NULL}, + .hfc_param_ini = {rtw8852bt_hfc_param_ini_pcie, NULL, NULL, NULL}, .dle_mem = {rtw8852bt_dle_mem_pcie, NULL, NULL, NULL}, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 40db7e3c0d97..9c2c05f80393 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3126,6 +3126,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x6f800, .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie, + rtw8852c_hfc_param_ini_usb, rtw8852c_hfc_param_ini_usb, NULL}, .dle_mem = {rtw8852c_dle_mem_pcie, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 2bea44faa109..b247980cc9ec 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2969,7 +2969,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x8f800, - .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, NULL, NULL}, + .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, NULL, NULL, NULL}, .dle_mem = {rtw8922a_dle_mem_pcie, NULL, NULL, NULL}, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index e3b77cd23514..96e7b70a3b93 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2948,7 +2948,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .max_rx_agg_num = 256, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x5f800, - .hfc_param_ini = {rtw8922d_hfc_param_ini_pcie, NULL, NULL}, + .hfc_param_ini = {rtw8922d_hfc_param_ini_pcie, NULL, NULL, NULL}, .dle_mem = {rtw8922d_dle_mem_pcie, NULL, NULL, NULL}, .wde_qempty_acq_grpnum = 8, .wde_qempty_mgq_grpsel = 8, From bd87e927006c6f548c65e23a00169c2ee87825ea Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:45:55 +0300 Subject: [PATCH 017/105] wifi: rtw89: Add rtw8922a_hfc_param_ini_usb{2,3} "hfc" means "hci fc" which is "Host Control Interface Flow Control". These are some parameters needed for RTL8922AU. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/474eb37a-a2e1-434a-b648-9e4b78e604d2@gmail.com --- drivers/net/wireless/realtek/rtw89/mac.c | 3 + drivers/net/wireless/realtek/rtw89/mac.h | 3 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 125 +++++++++++++++++- 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 8d5375ec33a2..9058312322e0 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1716,6 +1716,9 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .hfc_preccfg_pcie = {2, 40, 0, 0, 1, 0, 0, 0}, .hfc_prec_cfg_c0 = {2, 32, 0, 0, 0, 0, 0, 0, 2, 32, 0, 0}, .hfc_prec_cfg_c2 = {0, 256, 0, 0, 0, 0, 0, 0, 0, 256, 0, 0}, + .hfc_prec_cfg_c3 = {18, 32, 148, 148, 1, 1, 1, 1}, + .hfc_prec_cfg_c5 = {18, 32, 148, 148, 1, 1, 0, 1}, + .hfc_prec_cfg_c6 = {8, 32, 148, 148, 1, 1, 0, 1}, /* PCIE 64 */ .wde_size0 = {RTW89_WDE_PG_64, 4095, 1,}, .wde_size0_v1 = {RTW89_WDE_PG_64, 3328, 0, 0,}, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 9db9ae219cd6..3aad234b2a0f 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -926,6 +926,9 @@ struct rtw89_mac_size_set { const struct rtw89_hfc_prec_cfg hfc_preccfg_pcie; const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c0; const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c2; + const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c3; + const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c5; + const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c6; const struct rtw89_dle_size wde_size0; const struct rtw89_dle_size wde_size1; const struct rtw89_dle_size wde_size0_v1; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index b247980cc9ec..380049ef15b5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -57,6 +57,126 @@ static const struct rtw89_hfc_param_ini rtw8922a_hfc_param_ini_pcie[] = { [RTW89_QTA_INVALID] = {NULL}, }; +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch7[] = { + {54, 222, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 222, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 222, grp_0}, /* ACH 4 */ + {0, 0, grp_0}, /* ACH 5 */ + {54, 222, grp_0}, /* ACH 6 */ + {0, 0, grp_0}, /* ACH 7 */ + {54, 222, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 222, grp_0}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p7 = { + 492, /* Group 0 */ + 0, /* Group 1 */ + 492, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch8[] = { + {24, 196, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 226, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 196, grp_1}, /* ACH 4 */ + {0, 0, grp_1}, /* ACH 5 */ + {54, 196, grp_1}, /* ACH 6 */ + {0, 0, grp_1}, /* ACH 7 */ + {54, 226, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 196, grp_1}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p8 = { + 304, /* Group 0 */ + 304, /* Group 1 */ + 608, /* Public Max */ + 96, /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8922a_hfc_param_ini_usb2[] = { + [RTW89_QTA_SCC] = {rtw8922a_hfc_chcfg_ch7, &rtw8922a_hfc_pubcfg_p7, + &rtw89_mac_size.hfc_prec_cfg_c5, RTW89_HCIFC_STF}, + [RTW89_QTA_DBCC] = {rtw8922a_hfc_chcfg_ch8, &rtw8922a_hfc_pubcfg_p8, + &rtw89_mac_size.hfc_prec_cfg_c6, RTW89_HCIFC_STF}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch4[] = { + {54, 606, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 606, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 606, grp_0}, /* ACH 4 */ + {0, 0, grp_0}, /* ACH 5 */ + {54, 606, grp_0}, /* ACH 6 */ + {0, 0, grp_0}, /* ACH 7 */ + {54, 606, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 606, grp_0}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p4 = { + 876, /* Group 0 */ + 0, /* Group 1 */ + 876, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch5[] = { + {54, 311, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 311, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 311, grp_1}, /* ACH 4 */ + {0, 0, grp_1}, /* ACH 5 */ + {54, 311, grp_1}, /* ACH 6 */ + {0, 0, grp_1}, /* ACH 7 */ + {54, 311, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 311, grp_1}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p5 = { + 419, /* Group 0 */ + 419, /* Group 1 */ + 838, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8922a_hfc_param_ini_usb3[] = { + [RTW89_QTA_SCC] = {rtw8922a_hfc_chcfg_ch4, &rtw8922a_hfc_pubcfg_p4, + &rtw89_mac_size.hfc_prec_cfg_c3, RTW89_HCIFC_STF}, + [RTW89_QTA_DBCC] = {rtw8922a_hfc_chcfg_ch5, &rtw8922a_hfc_pubcfg_p5, + &rtw89_mac_size.hfc_prec_cfg_c3, RTW89_HCIFC_STF}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + static const struct rtw89_dle_mem rtw8922a_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size0_v1, &rtw89_mac_size.ple_size0_v1, &rtw89_mac_size.wde_qt0_v1, @@ -2969,7 +3089,10 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x8f800, - .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, NULL, NULL, NULL}, + .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, + rtw8922a_hfc_param_ini_usb2, + rtw8922a_hfc_param_ini_usb3, + NULL}, .dle_mem = {rtw8922a_dle_mem_pcie, NULL, NULL, NULL}, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, From abe23df7c89f42741fd55f853e81be120996e991 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:46:31 +0300 Subject: [PATCH 018/105] wifi: rtw89: Add rtw8922a_dle_mem_usb{2,3} Add rtw8922a_dle_mem_usb2 and rtw8922a_dle_mem_usb3 and their various quotas and sizes in struct rtw89_mac_size_set. "dle" could be "Data Link Engine" or "Double Link Engine". These are some parameters needed for RTL8922AU. Also rename wde_size4_v1 to wde_size3_v1 because the name seemed incorrect. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/f9ac366b-ca8d-492e-83bf-440f8557c629@gmail.com --- drivers/net/wireless/realtek/rtw89/mac.c | 35 +++++++++++++- drivers/net/wireless/realtek/rtw89/mac.h | 22 +++++++++ drivers/net/wireless/realtek/rtw89/rtw8922a.c | 47 ++++++++++++++++++- 3 files changed, 101 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 9058312322e0..6f76ae2a99ce 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1725,12 +1725,18 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* 8852A USB */ .wde_size1 = {RTW89_WDE_PG_64, 768, 0,}, /* DLFW */ + .wde_size3_v1 = {RTW89_WDE_PG_64, 0, 3328, 0,}, .wde_size4 = {RTW89_WDE_PG_64, 0, 4096,}, - .wde_size4_v1 = {RTW89_WDE_PG_64, 0, 3328, 0,}, + /* 8922A USB3.0 */ + .wde_size4_v1 = {RTW89_WDE_PG_64, 892, 4, 0,}, + .wde_size5_v1 = {RTW89_WDE_PG_64, 864, 32, 0,}, /* PCIE 64 */ .wde_size6 = {RTW89_WDE_PG_64, 512, 0,}, /* 8852B PCIE SCC */ .wde_size7 = {RTW89_WDE_PG_64, 510, 2,}, + /* 8922A USB2.0 */ + .wde_size7_v1 = {RTW89_WDE_PG_64, 508, 4,}, + .wde_size8_v1 = {RTW89_WDE_PG_64, 634, 6,}, /* DLFW */ .wde_size9 = {RTW89_WDE_PG_64, 0, 1024,}, .wde_size16_v1 = {RTW89_WDE_PG_64, 639, 1, 0,}, @@ -1754,8 +1760,13 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_size3_v1 = {RTW89_PLE_PG_128, 2928, 0, 212992,}, /* DLFW */ .ple_size4 = {RTW89_PLE_PG_128, 64, 1472,}, + /* 8922A USB3.0 */ + .ple_size4_v1 = {RTW89_PLE_PG_256, 1963, 109, 57344,}, /* PCIE 64 */ .ple_size6 = {RTW89_PLE_PG_128, 496, 16,}, + /* 8922A USB2.0 */ + .ple_size6_v1 = {RTW89_PLE_PG_256, 2059, 109, 32768,}, + .ple_size7_v1 = {RTW89_PLE_PG_256, 2027, 109, 40960,}, /* DLFW */ .ple_size8 = {RTW89_PLE_PG_128, 64, 960,}, .ple_size9 = {RTW89_PLE_PG_128, 2288, 16,}, @@ -1781,10 +1792,16 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_qt3 = {0, 0, 0, 0,}, /* DLFW */ .wde_qt4 = {0, 0, 0, 0,}, + /* 8922A USB3.0 */ + .wde_qt4_v1 = {876, 6, 0, 10,}, + .wde_qt5_v1 = {838, 6, 0, 20,}, /* PCIE 64 */ .wde_qt6 = {448, 48, 0, 16,}, /* 8852B PCIE SCC */ .wde_qt7 = {446, 48, 0, 16,}, + /* 8922A USB2.0 */ + .wde_qt7_v1 = {492, 6, 0, 10,}, + .wde_qt8_v1 = {608, 6, 0, 20,}, /* 8852C USB3.0 */ .wde_qt16 = {344, 2, 0, 8,}, /* 8852C DLFW */ @@ -1804,9 +1821,22 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* PCIE SCC */ .ple_qt5 = {264, 0, 32, 20, 64, 13, 1101, 0, 64, 128, 120,}, .ple_qt5_v2 = {0, 0, 32, 256, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,}, + /* 8922A USB3.0 */ + .ple_qt6_v1 = {1806, 0, 16, 24, 7, 14, 57, 0, 12, 9, 1, 4, 0,}, + .ple_qt7_v1 = {1806, 0, 16, 24, 20, 27, 70, 0, 25, 9, 1, 17, 0,}, + .ple_qt8_v1 = {1638, 0, 16, 24, 7, 14, 99, 99, 24, 9, 1, 4, 0,}, + /* PCIE SCC */ .ple_qt9 = {0, 0, 32, 256, 0, 0, 0, 0, 0, 0, 1, 0, 0,}, + /* 8922A USB3.0 */ + .ple_qt9_v1 = {1638, 0, 16, 24, 35, 42, 127, 127, 52, 9, 1, 32, 0,}, + /* 8922A USB2.0 */ + .ple_qt12_v1 = {463, 0, 16, 24, 7, 14, 57, 0, 12, 9, 1, 4, 0,}, /* DLFW */ .ple_qt13 = {0, 0, 16, 48, 0, 0, 0, 0, 0, 0, 0,}, + /* 8922A USB2.0 */ + .ple_qt13_v1 = {463, 0, 16, 24, 1459, 1466, 1509, 0, 1464, 9, 1, 1456, 0,}, + .ple_qt14_v1 = {939, 0, 16, 24, 7, 14, 57, 57, 24, 9, 1, 4, 0,}, + .ple_qt15_v1 = {939, 0, 16, 24, 882, 889, 932, 932, 899, 9, 1, 879, 0,}, /* PCIE 64 */ .ple_qt18 = {147, 0, 16, 20, 17, 13, 89, 0, 32, 14, 8, 0,}, /* 8852A USB SCC */ @@ -1850,8 +1880,11 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_qt_51b_wow = {147, 0, 16, 20, 157, 13, 133, 0, 172, 14, 24, 0,}, .ple_rsvd_qt0 = {2, 107, 107, 6, 6, 6, 6, 0, 0, 0,}, .ple_rsvd_qt1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + .ple_rsvd_qt2 = {1, 56, 28, 6, 6, 6, 6, 0, 0, 0,}, .ple_rsvd_qt9 = {1, 44, 44, 6, 6, 6, 6, 69, 0, 0,}, .rsvd0_size0 = {212992, 0,}, + .rsvd0_size3 = {57344, 0,}, + .rsvd0_size5 = {32768, 0,}, .rsvd0_size6 = {40960, 0,}, .rsvd1_size0 = {587776, 2048,}, .rsvd1_size2 = {391168, 2048,}, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 3aad234b2a0f..75691a9549a7 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -932,10 +932,14 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size wde_size0; const struct rtw89_dle_size wde_size1; const struct rtw89_dle_size wde_size0_v1; + const struct rtw89_dle_size wde_size3_v1; const struct rtw89_dle_size wde_size4; const struct rtw89_dle_size wde_size4_v1; + const struct rtw89_dle_size wde_size5_v1; const struct rtw89_dle_size wde_size6; const struct rtw89_dle_size wde_size7; + const struct rtw89_dle_size wde_size7_v1; + const struct rtw89_dle_size wde_size8_v1; const struct rtw89_dle_size wde_size9; const struct rtw89_dle_size wde_size16_v1; const struct rtw89_dle_size wde_size17; @@ -949,8 +953,11 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size ple_size1; const struct rtw89_dle_size ple_size0_v1; const struct rtw89_dle_size ple_size3_v1; + const struct rtw89_dle_size ple_size4_v1; const struct rtw89_dle_size ple_size4; const struct rtw89_dle_size ple_size6; + const struct rtw89_dle_size ple_size6_v1; + const struct rtw89_dle_size ple_size7_v1; const struct rtw89_dle_size ple_size8; const struct rtw89_dle_size ple_size9; const struct rtw89_dle_size ple_size17; @@ -966,8 +973,12 @@ struct rtw89_mac_size_set { const struct rtw89_wde_quota wde_qt0_v1; const struct rtw89_wde_quota wde_qt3; const struct rtw89_wde_quota wde_qt4; + const struct rtw89_wde_quota wde_qt4_v1; + const struct rtw89_wde_quota wde_qt5_v1; const struct rtw89_wde_quota wde_qt6; const struct rtw89_wde_quota wde_qt7; + const struct rtw89_wde_quota wde_qt7_v1; + const struct rtw89_wde_quota wde_qt8_v1; const struct rtw89_wde_quota wde_qt16; const struct rtw89_wde_quota wde_qt17; const struct rtw89_wde_quota wde_qt18; @@ -980,8 +991,16 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt4; const struct rtw89_ple_quota ple_qt5; const struct rtw89_ple_quota ple_qt5_v2; + const struct rtw89_ple_quota ple_qt6_v1; + const struct rtw89_ple_quota ple_qt7_v1; + const struct rtw89_ple_quota ple_qt8_v1; const struct rtw89_ple_quota ple_qt9; + const struct rtw89_ple_quota ple_qt9_v1; + const struct rtw89_ple_quota ple_qt12_v1; const struct rtw89_ple_quota ple_qt13; + const struct rtw89_ple_quota ple_qt13_v1; + const struct rtw89_ple_quota ple_qt14_v1; + const struct rtw89_ple_quota ple_qt15_v1; const struct rtw89_ple_quota ple_qt18; const struct rtw89_ple_quota ple_qt25; const struct rtw89_ple_quota ple_qt26; @@ -1009,8 +1028,11 @@ struct rtw89_mac_size_set { const struct rtw89_rsvd_quota ple_rsvd_qt0; const struct rtw89_rsvd_quota ple_rsvd_qt1; const struct rtw89_rsvd_quota ple_rsvd_qt1_v1; + const struct rtw89_rsvd_quota ple_rsvd_qt2; const struct rtw89_rsvd_quota ple_rsvd_qt9; const struct rtw89_dle_rsvd_size rsvd0_size0; + const struct rtw89_dle_rsvd_size rsvd0_size3; + const struct rtw89_dle_rsvd_size rsvd0_size5; const struct rtw89_dle_rsvd_size rsvd0_size6; const struct rtw89_dle_rsvd_size rsvd1_size0; const struct rtw89_dle_rsvd_size rsvd1_size2; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 380049ef15b5..3f94a5e80afe 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -188,7 +188,47 @@ static const struct rtw89_dle_mem rtw8922a_dle_mem_pcie[] = { &rtw89_mac_size.wde_qt0_v1, &rtw89_mac_size.ple_qt0, &rtw89_mac_size.ple_qt1, &rtw89_mac_size.ple_rsvd_qt0, &rtw89_mac_size.rsvd0_size0, &rtw89_mac_size.rsvd1_size0}, - [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size4_v1, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size3_v1, + &rtw89_mac_size.ple_size3_v1, &rtw89_mac_size.wde_qt4, + &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt9, + &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.rsvd0_size0, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const struct rtw89_dle_mem rtw8922a_dle_mem_usb2[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size7_v1, + &rtw89_mac_size.ple_size6_v1, &rtw89_mac_size.wde_qt7_v1, + &rtw89_mac_size.wde_qt7_v1, &rtw89_mac_size.ple_qt12_v1, + &rtw89_mac_size.ple_qt13_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size5, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size8_v1, + &rtw89_mac_size.ple_size7_v1, &rtw89_mac_size.wde_qt8_v1, + &rtw89_mac_size.wde_qt8_v1, &rtw89_mac_size.ple_qt14_v1, + &rtw89_mac_size.ple_qt15_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size3_v1, + &rtw89_mac_size.ple_size3_v1, &rtw89_mac_size.wde_qt4, + &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt9, + &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.rsvd0_size0, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const struct rtw89_dle_mem rtw8922a_dle_mem_usb3[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size4_v1, + &rtw89_mac_size.ple_size4_v1, &rtw89_mac_size.wde_qt4_v1, + &rtw89_mac_size.wde_qt4_v1, &rtw89_mac_size.ple_qt6_v1, + &rtw89_mac_size.ple_qt7_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size3, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size5_v1, + &rtw89_mac_size.ple_size4_v1, &rtw89_mac_size.wde_qt5_v1, + &rtw89_mac_size.wde_qt5_v1, &rtw89_mac_size.ple_qt8_v1, + &rtw89_mac_size.ple_qt9_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size3, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size3_v1, &rtw89_mac_size.ple_size3_v1, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_rsvd_qt1, @@ -3093,7 +3133,10 @@ const struct rtw89_chip_info rtw8922a_chip_info = { rtw8922a_hfc_param_ini_usb2, rtw8922a_hfc_param_ini_usb3, NULL}, - .dle_mem = {rtw8922a_dle_mem_pcie, NULL, NULL, NULL}, + .dle_mem = {rtw8922a_dle_mem_pcie, + rtw8922a_dle_mem_usb2, + rtw8922a_dle_mem_usb3, + NULL}, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000, 0xf000}, From 6bb58cd6e17699e41fedd2944b875ef594bcc2bb Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:47:42 +0300 Subject: [PATCH 019/105] wifi: rtw89: Add rtw8922au.c This is the entry point of the new rtw89_8922au module. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/0d22cd72-4048-41ad-a4aa-511681e6ba51@gmail.com --- .../net/wireless/realtek/rtw89/rtw8922au.c | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8922au.c diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922au.c b/drivers/net/wireless/realtek/rtw89/rtw8922au.c new file mode 100644 index 000000000000..347bde171391 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8922au.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2026 Realtek Corporation + */ + +#include +#include +#include "rtw8922a.h" +#include "usb.h" + +static const struct rtw89_usb_info rtw8922a_usb_info = { + .usb_host_request_2 = 0, + .usb_wlan0_1 = 0, + .hci_func_en = 0, + .usb3_mac_npi_config_intf_0 = 0, + .usb_endpoint_0 = 0, + .usb_endpoint_2 = 0, + .rx_agg_alignment = 16, + .bulkout_id = { + [RTW89_DMA_ACH0] = 3, + [RTW89_DMA_ACH2] = 5, + [RTW89_DMA_ACH4] = 4, + [RTW89_DMA_ACH6] = 6, + [RTW89_DMA_B0MG] = 0, + [RTW89_DMA_B0HI] = 0, + [RTW89_DMA_B1MG] = 1, + [RTW89_DMA_B1HI] = 1, + [RTW89_DMA_H2C] = 2, + }, +}; + +static const struct rtw89_driver_info rtw89_8922au_info = { + .chip = &rtw8922a_chip_info, + .variant = NULL, + .quirks = NULL, + .bus = { + .usb = &rtw8922a_usb_info, + }, +}; + +static const struct usb_device_id rtw_8922au_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03ef, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0502, 0x76d7, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x4025, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x4026, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1bcf, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1bd2, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0x8912, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0xda0e, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x332b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3625, 0x010a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x37ad, 0x0100, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x37ad, 0x0101, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x3822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x4822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x5822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8922au_id_table); + +static struct usb_driver rtw_8922au_driver = { + .name = KBUILD_MODNAME, + .id_table = rtw_8922au_id_table, + .probe = rtw89_usb_probe, + .disconnect = rtw89_usb_disconnect, +}; +module_usb_driver(rtw_8922au_driver); + +MODULE_AUTHOR("Bitterblue Smith "); +MODULE_DESCRIPTION("Realtek 802.11be wireless 8922AU driver"); +MODULE_LICENSE("Dual BSD/GPL"); From f07d1009e9729f65adadcbc1cf7a2b10e807ba05 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:49:45 +0300 Subject: [PATCH 020/105] wifi: rtw89: Enable the new rtw89_8922au module Tested without MLO, mostly in station mode and a little in AP mode. It can reach 1.5 Gbps RX, 1 Gbps TX speed. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/c0bd60ba-4e2d-41cb-8b0c-38454a341d7a@gmail.com --- drivers/net/wireless/realtek/rtw89/Kconfig | 12 ++++++++++++ drivers/net/wireless/realtek/rtw89/Makefile | 3 +++ 2 files changed, 15 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index 44d8a7f32bf2..43e3b0ef44da 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -157,6 +157,18 @@ config RTW89_8922AE The variant 8922AE-VS has the same features except 1024-QAM. +config RTW89_8922AU + tristate "Realtek 8922AU USB wireless network (Wi-Fi 7) adapter" + depends on USB + select RTW89_CORE + select RTW89_USB + select RTW89_8922A + help + Select this option will enable support for 8922AU chipset + + 802.11be USB wireless network (Wi-Fi 7) adapter + supporting 2x2 2GHz/5GHz/6GHz 4096-QAM 160MHz channels. + config RTW89_DEBUG bool diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index 1be81f254fca..475bad743d75 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -88,6 +88,9 @@ rtw89_8922a-objs := rtw8922a.o \ obj-$(CONFIG_RTW89_8922AE) += rtw89_8922ae.o rtw89_8922ae-objs := rtw8922ae.o +obj-$(CONFIG_RTW89_8922AU) += rtw89_8922au.o +rtw89_8922au-objs := rtw8922au.o + rtw89_core-$(CONFIG_RTW89_DEBUG) += debug.o obj-$(CONFIG_RTW89_PCI) += rtw89_pci.o From d6a3aac6ed5d50aa2ce3d5dfed70d97ca6217f21 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 20 Apr 2026 11:40:36 +0800 Subject: [PATCH 021/105] wifi: rtw89: 8922d: fix typo rx_freq_frome_ie The commit 127ea8d0b068 ("wifi: rtw89: fix typo "frome" -> "from" in rx_freq_frome_ie") change the struct field to correct the typo, but miss to fix this for RTL8922D because it is submitting at the same time. As RTL8922D is not enabled yet, this build error doesn't appear. Fix it accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 96e7b70a3b93..a550ff8de8fc 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2988,7 +2988,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .support_noise = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, - .rx_freq_frome_ie = false, + .rx_freq_from_ie = false, .hw_sec_hdr = true, .hw_mgmt_tx_encrypt = true, .hw_tkip_crypto = true, From fdf0eb355182372e7f62d81837d3186e76b4bef9 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Mon, 20 Apr 2026 11:40:37 +0800 Subject: [PATCH 022/105] wifi: rtw89: 8852a: refine power save to lower latency Improve user experience while using interactive applications. Adjust power saving decisions under different scenarios. Minimize latency for delay-sensitive connections by exiting power-save on packet bursts and maintain active until a period of inactivity is reached. For connections that are not that sensitive to delays, keep the current aggressive power save logic. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 87 +++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/core.h | 9 +++ 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 70feab97dccb..c9c4ec1d93af 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -341,10 +341,26 @@ static const struct rtw89_hw_rate_def { }, }; +static void rtw89_core_tx_skb_proto_stats(struct rtw89_traffic_stats *stats, + struct sk_buff *skb) +{ + switch (ip_hdr(skb)->protocol) { + case IPPROTO_TCP: + stats->tcp++; + break; + case IPPROTO_UDP: + stats->udp++; + break; + default: + break; + } +} + static void __rtw89_traffic_stats_accu(struct rtw89_traffic_stats *stats, struct sk_buff *skb, bool tx) { if (tx) { + rtw89_core_tx_skb_proto_stats(stats, skb); stats->tx_cnt++; stats->tx_unicast += skb->len; } else { @@ -2902,6 +2918,23 @@ static void rtw89_core_bcn_track_reset(struct rtw89_dev *rtwdev) memset(&rtwdev->bcn_track, 0, sizeof(rtwdev->bcn_track)); } +static bool rtw89_core_proto_stats_can_lps(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + enum rtw89_tfc_interval interval) +{ + if (rtwdev->chip->chip_id != RTL8852A) + return true; + + if (rtwvif->burst_active) + return false; + + if (interval == RTW89_TFC_INTERVAL_100MS && + hweight8(rtwvif->stats_ps.active_histogram) < 3) + return false; + + return true; +} + static void rtw89_vif_rx_bcn_stat(struct rtw89_dev *rtwdev, struct sk_buff *skb) { #define RTW89_APPEND_TSF_2GHZ 384 @@ -4540,9 +4573,42 @@ static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev, return RTW89_TFC_ULTRA_LOW; } +static void rtw89_calc_vif_active_histogram(struct rtw89_dev *rtwdev, + struct rtw89_traffic_stats *stats, + enum rtw89_tfc_interval interval) +{ + struct rtw89_vif *rtwvif; + + stats->udp_ratio = stats->tx_cnt ? + DIV_ROUND_DOWN_ULL(stats->udp * 100, stats->tx_cnt) : 0; + stats->active_histogram <<= 1; + + switch (interval) { + case RTW89_TFC_INTERVAL_2SEC: + rtwvif = container_of(stats, struct rtw89_vif, stats); + + if (stats->tcp >= RTW89_TCP_TH && stats->tx_cnt >= stats->rx_cnt) + stats->active_histogram |= BIT(0); + + if (stats->active_histogram & RTW89_RECENT_ACTIVE_HIST) + rtwvif->burst_active = true; + else + rtwvif->burst_active = false; + + break; + case RTW89_TFC_INTERVAL_100MS: + if (stats->udp_ratio >= RTW89_UDP_RATIO_TH) + stats->active_histogram |= BIT(0); + break; + } + + stats->tcp = 0; + stats->udp = 0; +} + static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev, struct rtw89_traffic_stats *stats, - enum rtw89_tfc_interval interval) + enum rtw89_tfc_interval interval, bool by_vif) { enum rtw89_tfc_lv tx_tfc_lv = stats->tx_tfc_lv; enum rtw89_tfc_lv rx_tfc_lv = stats->rx_tfc_lv; @@ -4564,6 +4630,9 @@ static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev, stats->rx_avg_len = stats->rx_cnt ? DIV_ROUND_DOWN_ULL(stats->rx_unicast, stats->rx_cnt) : 0; + if (by_vif) + rtw89_calc_vif_active_histogram(rtwdev, stats, interval); + stats->tx_unicast = 0; stats->rx_unicast = 0; stats->tx_cnt = 0; @@ -4585,11 +4654,11 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev) bool tfc_changed; tfc_changed = rtw89_traffic_stats_calc(rtwdev, &rtwdev->stats, - RTW89_TFC_INTERVAL_2SEC); + RTW89_TFC_INTERVAL_2SEC, false); rtw89_for_each_rtwvif(rtwdev, rtwvif) { rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats, - RTW89_TFC_INTERVAL_2SEC); + RTW89_TFC_INTERVAL_2SEC, true); rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) rtw89_fw_h2c_tp_offload(rtwdev, rtwvif_link); @@ -4598,7 +4667,8 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev) return tfc_changed; } -static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev) +static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev, + enum rtw89_tfc_interval interval) { struct ieee80211_vif *vif; struct rtw89_vif *rtwvif; @@ -4622,6 +4692,9 @@ static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev) if (!rtw89_core_bcn_track_can_lps(rtwdev)) continue; + if (!rtw89_core_proto_stats_can_lps(rtwdev, rtwvif, interval)) + continue; + rtw89_enter_lps(rtwdev, rtwvif, true); } } @@ -4771,13 +4844,13 @@ static void rtw89_track_ps_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_for_each_rtwvif(rtwdev, rtwvif) rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats_ps, - RTW89_TFC_INTERVAL_100MS); + RTW89_TFC_INTERVAL_100MS, true); if (rtwdev->scanning) return; if (rtwdev->lps_enabled && !rtwdev->btc.lps) - rtw89_enter_lps_track(rtwdev); + rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_100MS); } static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) @@ -4825,7 +4898,7 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_core_mlo_track(rtwdev); if (rtwdev->lps_enabled && !rtwdev->btc.lps) - rtw89_enter_lps_track(rtwdev); + rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_2SEC); } void rtw89_core_dm_disable_cfg(struct rtw89_dev *rtwdev, u32 new) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 2d7d3a11be16..ea123567ee17 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1477,6 +1477,10 @@ enum rtw89_tfc_lv { DECLARE_EWMA(tp, 10, 2); +#define RTW89_TCP_TH 40 +#define RTW89_UDP_RATIO_TH 70 +#define RTW89_RECENT_ACTIVE_HIST GENMASK(4, 0) + struct rtw89_traffic_stats { /* units in bytes */ u64 tx_unicast; @@ -1504,6 +1508,10 @@ struct rtw89_traffic_stats { u16 tx_rate; u16 rx_rate; + + /* used by rtwvif only */ + u64 tcp, udp, udp_ratio; + u8 active_histogram; }; struct rtw89_btc_chdef { @@ -6354,6 +6362,7 @@ struct rtw89_vif { struct rtw89_roc roc; bool offchan; + bool burst_active; enum rtw89_mlo_mode mlo_mode; struct rtw89_vif_ml_trans ml_trans; From 3da737c77764878ee05aaf6ec467a6996b5a232e Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Mon, 20 Apr 2026 11:40:38 +0800 Subject: [PATCH 023/105] wifi: rtw89: debug: disable hw_scan for latency-sensitive scenarios This helps avoid scan-triggered off-channel activity during latency-sensitive scenarios. Add RTW89_DM_HW_SCAN to the disabled_dm bitmap via debugfs and check it in the hw_scan path. When set, the driver rejects hw_scan requests and returns -EBUSY. Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/debug.c | 1 + drivers/net/wireless/realtek/rtw89/mac80211.c | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ea123567ee17..b2dfed064555 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5178,6 +5178,7 @@ enum rtw89_dm_type { RTW89_DM_THERMAL_PROTECT, RTW89_DM_TAS, RTW89_DM_MLO, + RTW89_DM_HW_SCAN, }; #define RTW89_THERMAL_PROT_LV_MAX 5 diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 7d8d22311018..02386d1d07c6 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4347,6 +4347,7 @@ static const struct rtw89_disabled_dm_info { DM_INFO(THERMAL_PROTECT), DM_INFO(TAS), DM_INFO(MLO), + DM_INFO(HW_SCAN), }; static ssize_t diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 501c3af1da01..9ee2aa225976 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -1241,6 +1241,7 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; + struct rtw89_hal *hal = &rtwdev->hal; int ret; lockdep_assert_wiphy(hw->wiphy); @@ -1248,6 +1249,12 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) return 1; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_HW_SCAN)) { + rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, + "reject hw scan due to disabled_dm\n"); + return -EBUSY; + } + if (rtwdev->scanning || rtwvif->offchan) return -EBUSY; From bd2d1abf74e49ac4f0350f8f076f26e38986487d Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Mon, 20 Apr 2026 11:40:39 +0800 Subject: [PATCH 024/105] wifi: rtw89: debug: disable inactive power save to reduce bus overhead This helps avoid excessive bus traffic and I/O overhead on slower HCIs (e.g., USB, SDIO) caused by frequent power state transitions. Add RTW89_DM_INACTIVE_PS to the disabled_dm bitmap via debugfs and check it in the rtw89_enter_ips path. When set, the driver skips entering inactive power save and returns immediately. Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/debug.c | 1 + drivers/net/wireless/realtek/rtw89/ps.c | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b2dfed064555..ac399ecaea6b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5179,6 +5179,7 @@ enum rtw89_dm_type { RTW89_DM_TAS, RTW89_DM_MLO, RTW89_DM_HW_SCAN, + RTW89_DM_INACTIVE_PS, }; #define RTW89_THERMAL_PROT_LV_MAX 5 diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 02386d1d07c6..ebe3b1f9c39d 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4348,6 +4348,7 @@ static const struct rtw89_disabled_dm_info { DM_INFO(TAS), DM_INFO(MLO), DM_INFO(HW_SCAN), + DM_INFO(INACTIVE_PS), }; static ssize_t diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index 125cf14fa581..31bb5dcd284a 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -234,8 +234,14 @@ void rtw89_enter_ips(struct rtw89_dev *rtwdev) { struct rtw89_vif_link *rtwvif_link; struct rtw89_vif *rtwvif; + struct rtw89_hal *hal = &rtwdev->hal; unsigned int link_id; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_INACTIVE_PS)) { + rtw89_debug(rtwdev, RTW89_DBG_PS, "skip enter IPS due to disabled_dm\n"); + return; + } + set_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags); if (!test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) From e3ed82f19f13943d86990cba0263d2bd39f40a16 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Mon, 20 Apr 2026 11:40:40 +0800 Subject: [PATCH 025/105] wifi: rtw89: phy: support static PD level setting PD (Packet Detection) threshold is a key parameter in the DIG (Dynamic Initial Gain) algorithm that determines the sensitivity of packet detection. Current implementation only supports dynamic PD adjustment based on environment. This patch adds support for static PD threshold via debugfs allowing users to set a fixed PD value for testing or specific scenarios. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 4 + drivers/net/wireless/realtek/rtw89/debug.c | 94 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.c | 44 +++++++--- 3 files changed, 131 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ac399ecaea6b..0b212f6edc8b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5180,6 +5180,7 @@ enum rtw89_dm_type { RTW89_DM_MLO, RTW89_DM_HW_SCAN, RTW89_DM_INACTIVE_PS, + RTW89_DM_DIG_PD, }; #define RTW89_THERMAL_PROT_LV_MAX 5 @@ -5221,6 +5222,9 @@ struct rtw89_hal { u8 thermal_prot_th; u8 thermal_prot_lv; /* 0 ~ RTW89_THERMAL_PROT_LV_MAX */ + + u8 fixed_dig_pd_th; /* v = (X(dBm) + 102)/2 */ + s8 fixed_dig_cck_pd_th; /* dBm */ }; #define RTW89_MAX_MAC_ID_NUM 128 diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index ebe3b1f9c39d..2cb6e441b2f8 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -87,6 +87,7 @@ struct rtw89_debugfs { struct rtw89_debugfs_priv phy_info; struct rtw89_debugfs_priv stations; struct rtw89_debugfs_priv disable_dm; + struct rtw89_debugfs_priv static_pd_th; struct rtw89_debugfs_priv mlo_mode; struct rtw89_debugfs_priv beacon_info; struct rtw89_debugfs_priv diag_mac; @@ -4349,6 +4350,7 @@ static const struct rtw89_disabled_dm_info { DM_INFO(MLO), DM_INFO(HW_SCAN), DM_INFO(INACTIVE_PS), + DM_INFO(DIG_PD), }; static ssize_t @@ -4394,6 +4396,96 @@ rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev, return count; } +#define RTW89_DIG_PD_TH_MIN_DBM -102 +#define RTW89_DIG_PD_TH_MAX_DBM -40 +#define RTW89_DIG_PD_TH_STEP 2 + +static s8 rtw89_dig_pd_th_to_dbm(u8 reg_val) +{ + return RTW89_DIG_PD_TH_MIN_DBM + RTW89_DIG_PD_TH_STEP * reg_val; +} + +static u8 rtw89_dig_pd_th_dbm_to_reg(s8 dbm) +{ + return (dbm - RTW89_DIG_PD_TH_MIN_DBM) / RTW89_DIG_PD_TH_STEP; +} + +static ssize_t +rtw89_debug_priv_static_pd_th_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_hal *hal = &rtwdev->hal; + char *p = buf, *end = buf + bufsz; + bool disabled; + s8 pd_th_dbm; + s8 cck_pd_th; + + disabled = hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD); + + if (disabled) { + pd_th_dbm = rtw89_dig_pd_th_to_dbm(hal->fixed_dig_pd_th); + cck_pd_th = hal->fixed_dig_cck_pd_th; + + p += scnprintf(p, end - p, "DIG: static\n"); + p += scnprintf(p, end - p, "OFDM PD threshold: %d dBm\n", pd_th_dbm); + p += scnprintf(p, end - p, "CCK PD threshold: %d dBm\n", cck_pd_th); + } else { + p += scnprintf(p, end - p, "DIG: dynamic\n"); + } + + p += scnprintf(p, end - p, "\nUsage: echo [pd_th] > static_pd_th\n"); + p += scnprintf(p, end - p, " mode: 0 = dynamic, 1 = static\n"); + p += scnprintf(p, end - p, " pd_th: PD threshold in dBm (-102 ~ -40)\n"); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_static_pd_th_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) +{ + struct rtw89_hal *hal = &rtwdev->hal; + int ret; + u32 mode; + s32 pd_th_dbm; + + ret = sscanf(buf, "%u %d", &mode, &pd_th_dbm); + if (ret < 1) + return -EINVAL; + + if (mode > 1) + return -EINVAL; + + if (mode == 0) { + rtw89_core_dm_disable_clr(rtwdev, RTW89_DM_DIG_PD); + hal->fixed_dig_pd_th = 0; + hal->fixed_dig_cck_pd_th = 0; + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "DIG static mode disabled\n"); + } else { + if (ret < 2 || pd_th_dbm < RTW89_DIG_PD_TH_MIN_DBM || + pd_th_dbm > RTW89_DIG_PD_TH_MAX_DBM) + return -EINVAL; + + rtw89_core_dm_disable_set(rtwdev, RTW89_DM_DIG_PD); + hal->fixed_dig_pd_th = clamp(rtw89_dig_pd_th_dbm_to_reg(pd_th_dbm), + 0, 0x1f); + /* CCK uses dBm directly */ + hal->fixed_dig_cck_pd_th = pd_th_dbm; + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "DIG static mode: PD=0x%02x (%d dBm), CCK=%d dBm\n", + hal->fixed_dig_pd_th, + rtw89_dig_pd_th_to_dbm(hal->fixed_dig_pd_th), + hal->fixed_dig_cck_pd_th); + } + + return count; +} + static void rtw89_debug_mlo_mode_set_mlsr(struct rtw89_dev *rtwdev, unsigned int link_id) { @@ -4882,6 +4974,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = { .phy_info = rtw89_debug_priv_get(phy_info), .stations = rtw89_debug_priv_get(stations, RLOCK), .disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK), + .static_pd_th = rtw89_debug_priv_set_and_get(static_pd_th, RWLOCK), .mlo_mode = rtw89_debug_priv_set_and_get(mlo_mode, RWLOCK), .beacon_info = rtw89_debug_priv_get(beacon_info), .diag_mac = rtw89_debug_priv_get(diag_mac, RSIZE_16K, RLOCK), @@ -4930,6 +5023,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_r(phy_info); rtw89_debugfs_add_r(stations); rtw89_debugfs_add_rw(disable_dm); + rtw89_debugfs_add_rw(static_pd_th); rtw89_debugfs_add_rw(mlo_mode); rtw89_debugfs_add_r(beacon_info); rtw89_debugfs_add_r(diag_mac); diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 1f249c297006..2663fc102e77 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -7170,11 +7170,19 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; struct rtw89_dig_info *dig = &bb->dig; + struct rtw89_hal *hal = &rtwdev->hal; u8 final_rssi, under_region = dig->pd_low_th_ofst; s8 cck_cca_th; u32 pd_val; - pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi, enable, chan); + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) { + pd_val = hal->fixed_dig_pd_th; + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "Fixed DIG: PD_low=%d\n", pd_val); + } else { + pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi, enable, chan); + } dig->bak_dig = pd_val; rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, @@ -7182,17 +7190,24 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, dig_regs->pd_spatial_reuse_en, enable, bb->phy_idx); - if (!rtwdev->hal.support_cckpd) + if (!hal->support_cckpd) return; - final_rssi = min_t(u8, rssi, dig->igi_rssi); - under_region = rtw89_phy_dig_cal_under_region(rtwdev, bb, chan); - cck_cca_th = max_t(s8, final_rssi - under_region, CCKPD_TH_MIN_RSSI); - pd_val = (u32)(cck_cca_th - IGI_RSSI_MAX); + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) { + pd_val = hal->fixed_dig_cck_pd_th; - rtw89_debug(rtwdev, RTW89_DBG_DIG, - "igi=%d, cck_ccaTH=%d, backoff=%d, cck_PD_low=((%d))dB\n", - final_rssi, cck_cca_th, under_region, pd_val); + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "Fixed DIG: cck_PD_low=((%d))dB\n", pd_val); + } else { + final_rssi = min_t(u8, rssi, dig->igi_rssi); + under_region = rtw89_phy_dig_cal_under_region(rtwdev, bb, chan); + cck_cca_th = max_t(s8, final_rssi - under_region, CCKPD_TH_MIN_RSSI); + pd_val = (u32)(cck_cca_th - IGI_RSSI_MAX); + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "igi=%d, cck_ccaTH=%d, backoff=%d, cck_PD_low=((%d))dB\n", + final_rssi, cck_cca_th, under_region, pd_val); + } rtw89_phy_write32_idx(rtwdev, dig_regs->bmode_pd_reg, dig_regs->bmode_cca_rssi_limit_en, enable, bb->phy_idx); @@ -7323,6 +7338,7 @@ static void rtw89_phy_dig_ctrl(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb { const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; struct rtw89_dig_info *dig = &bb->dig; + struct rtw89_hal *hal = &rtwdev->hal; bool en_dig; u32 pd_val; @@ -7331,10 +7347,16 @@ static void rtw89_phy_dig_ctrl(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb if (pause_dig) { en_dig = false; - pd_val = 0; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) + pd_val = hal->fixed_dig_pd_th; + else + pd_val = 0; } else { en_dig = rtwdev->total_sta_assoc > 0; - pd_val = restore ? dig->bak_dig : 0; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) + pd_val = hal->fixed_dig_pd_th; + else + pd_val = restore ? dig->bak_dig : 0; } rtw89_debug(rtwdev, RTW89_DBG_DIG, "%s <%s> PD_low=%d", __func__, From 08fdcb529df6df3562dd2b0035f88dd5be8b3c68 Mon Sep 17 00:00:00 2001 From: Shin-Yi Lin Date: Mon, 20 Apr 2026 11:40:41 +0800 Subject: [PATCH 026/105] wifi: rtw89: Correct data type for scan index to avoid infinite loop A kernel soft lockup was observed during Wi-Fi scanning on the 6GHz band. The CPU becomes stuck in rtw89_hw_scan_add_chan_ax for over 20 seconds, leading to a system panic. RIP points to 0f b6 c3 (movzbl %bl, %eax), which zero-extends the low 8 bits of RBX into RAX. RBX (the counter i) has reached a huge value: 0x137466a1. watchdog: BUG: soft lockup - CPU#2 stuck for 26s! [kworker/u16:4:6124] Workqueue: events_unbound cfg80211_wiphy_work [cfg80211] RIP: 0010:rtw89_hw_scan_add_chan_ax+0xb3/0x6e0 [rtw89_core] Code: a0 48 89 45 a8 44 89 6d 9c 44 89 75 98 eb 29 66 66 2e 0f 1f 84 00 00 00 00 00 66 66 2e 0f 1f 84 00 00 00 00 00 66 90 83 c3 01 <0f> b6 c3 41 3b 44 24 74 0f 83 0b 02 00 00 0f b6 c3 48 8d 14 80 49 RSP: 0018:ffffcb48cbaa39f8 EFLAGS: 00000202 RAX: 0000000000000005 RBX: 00000000137466a1 RCX: 0000000000000000 RDX: ffff89ffc9d851a8 RSI: 0000000000004f0d RDI: 0000000096af0130 RBP: ffffcb48cbaa3a60 R08: 0000000000000000 R09: ffff8a00b7502080 R10: ffff8a00b75ff600 R11: 0000000000000000 R12: ffff89ffc7553870 R13: ffff8a00b7ac8f19 R14: ffff8a00b75020d8 R15: ffff89ffc3d54d80 FS: 0000000000000000(0000) GS:ffff8a014f962000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007558d7f9f4c4 CR3: 0000000178040001 CR4: 00000000001706f0 Call Trace: rtw89_hw_scan_prep_chan_list_ax+0x8a/0x400 [rtw89_core] rtw89_hw_scan_start+0x546/0x8a0 [rtw89_core] ? rtw89_fw_h2c_default_cmac_tbl+0x13c/0x1f0 [rtw89_core] rtw89_ops_hw_scan+0xae/0x120 [rtw89_core] drv_hw_scan+0xbb/0x180 [mac80211] __ieee80211_start_scan+0x2fc/0x750 [mac80211] ieee80211_request_scan+0xe/0x20 [mac80211] ieee80211_scan+0x123/0x190 [mac80211] rdev_scan+0x40/0x110 [cfg80211] cfg80211_scan_6ghz+0x5a1/0xa30 [cfg80211] By objdump with source: for (i = 0; i < req->n_6ghz_params; i++) { 5fbc0: 83 c3 01 add $0x1,%ebx --> i++ 5fbc3: 0f b6 c3 movzbl %bl,%eax --> get counter fbc6: 41 3b 44 24 74 cmp 0x74(%r12),%eax * RBX: 00000000137466a1 -> %bl = a1 -> EAX = 000000a1 (161) Fixes: c6aa9a9c4725 ("wifi: rtw89: add RNR support for 6 GHz scan") Signed-off-by: Shin-Yi Lin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index ffa04315a62b..b578f5316b5f 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -8318,7 +8318,7 @@ static int rtw89_update_6ghz_rnr_chan_ax(struct rtw89_dev *rtwdev, struct sk_buff *skb; bool found; int ret = 0; - u8 i; + u32 i; if (!req->n_6ghz_params) return 0; From 72b781d3bdd38c0bc8dd3218826918287b9602fd Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Mon, 20 Apr 2026 11:40:42 +0800 Subject: [PATCH 027/105] wifi: rtw89: 8852bt: configure support_noise field explicitly RTL8852BT missed to configure support_noise field in chip_info. Although RTL8852BT doesn't support that, add the configuration to make things clear. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852bt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 7d86e9598ca8..c2184b092395 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -811,6 +811,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .support_ant_gain = true, .support_tas = false, .support_sar_by_ant = true, + .support_noise = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, From ae3d327515f2f46e2ca403f4a93494ef2050c764 Mon Sep 17 00:00:00 2001 From: Chia-Yuan Li Date: Mon, 20 Apr 2026 11:40:43 +0800 Subject: [PATCH 028/105] wifi: rtw89: add IO offload support via firmware Offload register write operations to the firmware to improve IO efficiency. Instead of the host performing direct bus access for every register, the operations are now encapsulated into firmware commands. This implementation allows the host to aggregate multiple register write actions into a single command buffer. By batching these operations, we reduce the number of individual transactions on the bus and minimize per-transfer overhead, leading to faster overall IO performance. Add a support_fw_cmd_ofld field to the chip_info structure to control whether firmware-offloaded batch writes are enabled. Enable it by default for RTL8832CU. Co-developed-by: Eric Huang Signed-off-by: Eric Huang Co-developed-by: Johnson Tsai Signed-off-by: Johnson Tsai Signed-off-by: Chia-Yuan Li Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 + drivers/net/wireless/realtek/rtw89/core.h | 82 ++++- drivers/net/wireless/realtek/rtw89/fw.c | 347 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 57 +++ drivers/net/wireless/realtek/rtw89/phy.h | 30 +- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 1 + 12 files changed, 515 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index c9c4ec1d93af..7fe979e47ad5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -6302,6 +6302,8 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) struct rtw89_btc *btc = &rtwdev->btc; u8 band; + rtwdev->io = rtw89_fw_cmd_ofld_alloc_and_get_io_ops(rtwdev); + bitmap_or(rtwdev->quirks, rtwdev->quirks, &rtwdev->chip->default_quirks, NUM_OF_RTW89_QUIRKS); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0b212f6edc8b..bf5585c701ad 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -27,6 +27,7 @@ struct rtw89_phy_calc_efuse_gain; struct rtw89_debugfs; struct rtw89_regd_data; struct rtw89_wow_cam_info; +struct rtw89_fw_cmd_ofld_info; extern const struct ieee80211_ops rtw89_ops; @@ -4569,6 +4570,7 @@ struct rtw89_chip_info { bool support_tas; bool support_sar_by_ant; bool support_noise; + bool support_fw_cmd_ofld; bool ul_tb_waveform_ctrl; bool ul_tb_pwr_diff; bool rx_freq_from_ie; @@ -6202,10 +6204,26 @@ struct rtw89_tid_stats { bool started; }; +struct rtw89_io_ops { + int (*pack)(struct rtw89_dev *rtwdev); + int (*unpack)(struct rtw89_dev *rtwdev); + void (*do_udelay)(struct rtw89_dev *rtwdev, u32 us); + void (*do_mdelay)(struct rtw89_dev *rtwdev, u32 ms); + void (*write8)(struct rtw89_dev *rtwdev, u32 addr, u8 data); + void (*write16)(struct rtw89_dev *rtwdev, u32 addr, u16 data); + void (*write32)(struct rtw89_dev *rtwdev, u32 addr, u32 data); + void (*phy_write8)(struct rtw89_dev *rtwdev, u32 addr, u8 data); + void (*phy_write16)(struct rtw89_dev *rtwdev, u32 addr, u16 data); + void (*phy_write32)(struct rtw89_dev *rtwdev, u32 addr, u32 data); + void (*write_rf)(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask, u32 data); +}; + struct rtw89_dev { struct ieee80211_hw *hw; struct device *dev; const struct ieee80211_ops *ops; + const struct rtw89_io_ops *io; bool dbcc_en; bool support_mlo; @@ -6227,6 +6245,8 @@ struct rtw89_dev { struct rtw89_rfe_data *rfe_data; enum rtw89_custid custid; + struct rtw89_fw_cmd_ofld_info *fw_cmd_ofld_info; + struct rtw89_sta_link __rcu *assoc_link_on_macid[RTW89_MAX_MAC_ID_NUM]; refcount_t refcount_ap_info; @@ -6688,6 +6708,38 @@ struct rtw89_tx_skb_data *RTW89_TX_SKB_CB(struct sk_buff *skb) return (struct rtw89_tx_skb_data *)info->driver_data; } +static inline int rtw89_io_pack(struct rtw89_dev *rtwdev) +{ + if (rtwdev->io->pack) + return rtwdev->io->pack(rtwdev); + + return 0; +} + +static inline int rtw89_io_unpack(struct rtw89_dev *rtwdev) +{ + if (rtwdev->io->unpack) + return rtwdev->io->unpack(rtwdev); + + return 0; +} + +static inline void rtw89_io_udelay(struct rtw89_dev *rtwdev, u32 us) +{ + if (rtwdev->io->do_udelay) + rtwdev->io->do_udelay(rtwdev, us); + else + udelay(us); +} + +static inline void rtw89_io_mdelay(struct rtw89_dev *rtwdev, u32 ms) +{ + if (rtwdev->io->do_mdelay) + rtwdev->io->do_mdelay(rtwdev, ms); + else + mdelay(ms); +} + static inline u8 rtw89_read8(struct rtw89_dev *rtwdev, u32 addr) { return rtwdev->hci.ops->read8(rtwdev, addr); @@ -6703,21 +6755,36 @@ static inline u32 rtw89_read32(struct rtw89_dev *rtwdev, u32 addr) return rtwdev->hci.ops->read32(rtwdev, addr); } -static inline void rtw89_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +static inline void rtw89_raw_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) { rtwdev->hci.ops->write8(rtwdev, addr, data); } -static inline void rtw89_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +static inline void rtw89_raw_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) { rtwdev->hci.ops->write16(rtwdev, addr, data); } -static inline void rtw89_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +static inline void rtw89_raw_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) { rtwdev->hci.ops->write32(rtwdev, addr, data); } +static inline void rtw89_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +{ + rtwdev->io->write8(rtwdev, addr, data); +} + +static inline void rtw89_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +{ + rtwdev->io->write16(rtwdev, addr, data); +} + +static inline void rtw89_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +{ + rtwdev->io->write32(rtwdev, addr, data); +} + static inline void rtw89_write8_set(struct rtw89_dev *rtwdev, u32 addr, u8 bit) { @@ -6862,13 +6929,20 @@ rtw89_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, return rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask); } +static inline void +rtw89_raw_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask, u32 data) +{ + rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data); +} + static inline void rtw89_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask, u32 data) { lockdep_assert_wiphy(rtwdev->hw->wiphy); - rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data); + rtwdev->io->write_rf(rtwdev, rf_path, addr, mask, data); } static inline u32 rtw89_read32_pci_cfg(struct rtw89_dev *rtwdev, u32 addr) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index b578f5316b5f..b5d2540cf212 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -11344,3 +11344,350 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev, return parms; } + +static int rtw89_fw_cmd_ofld_pack(struct rtw89_dev *rtwdev) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + info->pack_level++; + + return 0; +} + +static void rtw89_fw_cmd_ofld_flush(struct rtw89_dev *rtwdev) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + struct sk_buff *skb; + int ret; + u32 len; + + len = info->cnt * sizeof(info->cmds[0]); + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "alloc skb fail\n"); + return; + } + + skb_put_data(skb, info->cmds, len); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_FW_OFLD, + H2C_FUNC_CMD_OFLD_PKT, 0, 0, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send cmd ofld\n"); + dev_kfree_skb_any(skb); + return; + } + + if (info->accu_delay) + fsleep(info->accu_delay); + + info->cnt = 0; + info->accu_delay = 0; +} + +static int rtw89_fw_cmd_ofld_unpack(struct rtw89_dev *rtwdev) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (info->pack_level == 0) + return -EFAULT; + + if (--info->pack_level != 0) + return 0; + + if (info->cnt == 0) + return 0; + + rtw89_fw_cmd_ofld_flush(rtwdev); + + return 0; +} + +static int rtw89_fw_cmd_ofld_enqueue(struct rtw89_dev *rtwdev, + const struct rtw89_fw_cmd_ofld_arg *cmd) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + struct rtw89_h2c_cmd_ofld *h2c; + u32 base_off; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (info->pack_level == 0) + return 1; + + if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) + return -EBUSY; + + if (cmd->type != RTW89_FW_CMD_OFLD_DELAY && + cmd->src != RTW89_FW_CMD_OFLD_SRC_RF && + cmd->src != RTW89_FW_CMD_OFLD_SRC_RF_DDIE && + !IS_ALIGNED(cmd->offset, 4)) + return -EFAULT; + + if (info->cnt >= ARRAY_SIZE(info->cmds)) + rtw89_fw_cmd_ofld_flush(rtwdev); + + h2c = &info->cmds[info->cnt++]; + + h2c->w0 = le32_encode_bits(cmd->src, RTW89_H2C_CMD_OFLD_W0_SRC) | + le32_encode_bits(cmd->type, RTW89_H2C_CMD_OFLD_W0_TYPE) | + le32_encode_bits(cmd->rf_path, RTW89_H2C_CMD_OFLD_W0_PATH) | + le32_encode_bits(info->cnt, RTW89_H2C_CMD_OFLD_W0_CMD_NUM) | + le32_encode_bits(cmd->offset, RTW89_H2C_CMD_OFLD_W0_OFFSET); + + if (cmd->src == RTW89_FW_CMD_OFLD_SRC_RF_DDIE) + base_off = 1; + else if (cmd->src == RTW89_FW_CMD_OFLD_SRC_RF) + base_off = 0; + else + base_off = cmd->offset >> 16; + + h2c->w1 = le32_encode_bits(cmd->id, RTW89_H2C_CMD_OFLD_W1_ID) | + le32_encode_bits(base_off, RTW89_H2C_CMD_OFLD_W1_BASE_OFFSET); + h2c->w2 = le32_encode_bits(cmd->value, RTW89_H2C_CMD_OFLD_W2_VALUE); + h2c->w3 = le32_encode_bits(cmd->mask, RTW89_H2C_CMD_OFLD_W3_MASK); + + if (cmd->type == RTW89_FW_CMD_OFLD_DELAY) + info->accu_delay += cmd->value; + + return 0; +} + +static const struct rtw89_io_ops rtw89_raw_io = { + .pack = NULL, + .unpack = NULL, + .write8 = rtw89_raw_write8, + .write16 = rtw89_raw_write16, + .write32 = rtw89_raw_write32, + .phy_write8 = rtw89_raw_phy_write8, + .phy_write16 = rtw89_raw_phy_write16, + .phy_write32 = rtw89_raw_phy_write32, + .write_rf = rtw89_raw_write_rf, +}; + +static +void rtw89_fw_cmd_ofld_phy_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 4), + .mask = RTW89_W8_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.phy_write8(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_phy_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 2), + .mask = RTW89_W16_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.phy_write16(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_phy_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = addr, + .mask = MASKDWORD, + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.phy_write32(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_MAC, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 4), + .mask = RTW89_W8_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write8(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_MAC, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 2), + .mask = RTW89_W16_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write16(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_MAC, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = addr, + .mask = MASKDWORD, + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write32(rtwdev, addr, data); +} + +static void rtw89_fw_cmd_ofld_write_rf_ddv(struct rtw89_dev *rtwdev, + struct rtw89_fw_cmd_ofld_arg *cmd, + enum rtw89_rf_path rf_path, u32 addr, u32 mask, + u32 data) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + *cmd = (typeof(*cmd)){ + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .rf_path = 0, /* Set rf_path to zero in rf_ddv */ + .offset = chip->rf_base_addr[rf_path] + ((addr & 0xff) << 2), + .mask = mask, + .value = data, + }; +} + +static void rtw89_fw_cmd_ofld_write_rf_dav(struct rtw89_dev *rtwdev, + struct rtw89_fw_cmd_ofld_arg *cmd, + enum rtw89_rf_path rf_path, u32 addr, u32 mask, + u32 data) +{ + *cmd = (typeof(*cmd)){ + .src = RTW89_FW_CMD_OFLD_SRC_RF, + .type = RTW89_FW_CMD_OFLD_WRITE, + .rf_path = rf_path, + .offset = addr, + .mask = mask, + .value = data, + }; +} + +static void rtw89_fw_cmd_ofld_write_rf(struct rtw89_dev *rtwdev, + enum rtw89_rf_path rf_path, u32 addr, u32 mask, + u32 data) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + bool ad_sel = u32_get_bits(addr, RTW89_RF_ADDR_ADSEL_MASK); + struct rtw89_fw_cmd_ofld_arg cmd; + int ret; + + if (unlikely(rf_path >= chip->rf_path_num)) { + rtw89_warn(rtwdev, "unsupported rf path (%d) in fw offload\n", rf_path); + return; + } + + if (ad_sel) + rtw89_fw_cmd_ofld_write_rf_ddv(rtwdev, &cmd, rf_path, addr, mask, data); + else + rtw89_fw_cmd_ofld_write_rf_dav(rtwdev, &cmd, rf_path, addr, mask, data); + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write_rf(rtwdev, rf_path, addr, mask, data); +} + +static void rtw89_fw_cmd_ofld_udelay(struct rtw89_dev *rtwdev, u32 us) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_OTHER, + .type = RTW89_FW_CMD_OFLD_DELAY, + .value = us, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + udelay(us); +} + +static void rtw89_fw_cmd_ofld_mdelay(struct rtw89_dev *rtwdev, u32 ms) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_OTHER, + .type = RTW89_FW_CMD_OFLD_DELAY, + .value = ms * 1000, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + mdelay(ms); +} + +static const struct rtw89_io_ops rtw89_fw_cmd_ofld_io = { + .pack = rtw89_fw_cmd_ofld_pack, + .unpack = rtw89_fw_cmd_ofld_unpack, + .do_udelay = rtw89_fw_cmd_ofld_udelay, + .do_mdelay = rtw89_fw_cmd_ofld_mdelay, + .write8 = rtw89_fw_cmd_ofld_write8, + .write16 = rtw89_fw_cmd_ofld_write16, + .write32 = rtw89_fw_cmd_ofld_write32, + .phy_write8 = rtw89_fw_cmd_ofld_phy_write8, + .phy_write16 = rtw89_fw_cmd_ofld_phy_write16, + .phy_write32 = rtw89_fw_cmd_ofld_phy_write32, + .write_rf = rtw89_fw_cmd_ofld_write_rf, +}; + +const struct rtw89_io_ops * +rtw89_fw_cmd_ofld_alloc_and_get_io_ops(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_fw_cmd_ofld_info *info; + + if (!(rtwdev->hci.type == RTW89_HCI_TYPE_USB && chip->support_fw_cmd_ofld)) + return &rtw89_raw_io; + + info = devm_kzalloc(rtwdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return &rtw89_raw_io; + + rtwdev->fw_cmd_ofld_info = info; + + return &rtw89_fw_cmd_ofld_io; +} diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index db252d45e498..4d94a7195708 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3115,6 +3115,59 @@ struct rtw89_h2c_trx_protect { #define RTW89_H2C_TRX_PROTECT_W1_CHINFO_EN BIT(0) #define RTW89_H2C_TRX_PROTECT_W1_DFS_EN BIT(1) +enum rtw89_fw_cmd_ofld_arg_src { + RTW89_FW_CMD_OFLD_SRC_BB, + RTW89_FW_CMD_OFLD_SRC_RF, + RTW89_FW_CMD_OFLD_SRC_MAC, + RTW89_FW_CMD_OFLD_SRC_RF_DDIE, + RTW89_FW_CMD_OFLD_SRC_OTHER, +}; + +enum rtw89_fw_cmd_ofld_arg_type { + RTW89_FW_CMD_OFLD_WRITE, + RTW89_FW_CMD_OFLD_COMPARE, + RTW89_FW_CMD_OFLD_DELAY, + RTW89_FW_CMD_OFLD_MOVE, +}; + +struct rtw89_fw_cmd_ofld_arg { + enum rtw89_fw_cmd_ofld_arg_src src; + enum rtw89_fw_cmd_ofld_arg_type type; + enum rtw89_rf_path rf_path; + u32 value; + u32 mask; + u32 offset; + u16 id; +}; + +struct rtw89_h2c_cmd_ofld { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 w3; +} __packed; + +#define RTW89_H2C_CMD_OFLD_W0_SRC GENMASK(1, 0) +#define RTW89_H2C_CMD_OFLD_W0_TYPE GENMASK(3, 2) +#define RTW89_H2C_CMD_OFLD_W0_LC BIT(4) +#define RTW89_H2C_CMD_OFLD_W0_PATH GENMASK(6, 5) +#define RTW89_H2C_CMD_OFLD_W0_CMD_NUM GENMASK(14, 8) +#define RTW89_H2C_CMD_OFLD_W0_OFFSET GENMASK(31, 16) +#define RTW89_H2C_CMD_OFLD_W1_ID GENMASK(15, 0) +#define RTW89_H2C_CMD_OFLD_W1_BASE_OFFSET GENMASK(31, 16) +#define RTW89_H2C_CMD_OFLD_W2_VALUE GENMASK(31, 0) +#define RTW89_H2C_CMD_OFLD_W3_MASK GENMASK(31, 0) +#define RTW89_W8_MASK_OF_ALIGNED_ADDR(offset) (0xff << (((offset) & 0x3) << 3)) +#define RTW89_W16_MASK_OF_ALIGNED_ADDR(offset) (0xffff << (((offset) & 0x2) * 8)) + +#define RTW89_FW_CMD_OFLD_NR 125 +struct rtw89_fw_cmd_ofld_info { + unsigned int pack_level; + u32 cnt; + u32 accu_delay; + struct rtw89_h2c_cmd_ofld cmds[RTW89_FW_CMD_OFLD_NR]; +}; + struct rtw89_h2c_fwips { __le32 w0; } __packed; @@ -4606,6 +4659,7 @@ enum rtw89_fw_ofld_h2c_func { H2C_FUNC_MAC_MACID_PAUSE = 0x8, H2C_FUNC_USR_EDCA = 0xF, H2C_FUNC_TSF32_TOGL = 0x10, + H2C_FUNC_CMD_OFLD_PKT = 0x13, H2C_FUNC_OFLD_CFG = 0x14, H2C_FUNC_ADD_SCANOFLD_CH = 0x16, H2C_FUNC_SCANOFLD = 0x17, @@ -5737,4 +5791,7 @@ enum rtw89_wow_wakeup_ver { RTW89_WOW_REASON_NUM, }; +const struct rtw89_io_ops * +rtw89_fw_cmd_ofld_alloc_and_get_io_ops(struct rtw89_dev *rtwdev); + #endif diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index bde419edf744..3f9d306ff1ca 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -576,30 +576,48 @@ extern const struct rtw89_phy_gen_def rtw89_phy_gen_ax; extern const struct rtw89_phy_gen_def rtw89_phy_gen_be; extern const struct rtw89_phy_gen_def rtw89_phy_gen_be_v1; -static inline void rtw89_phy_write8(struct rtw89_dev *rtwdev, - u32 addr, u8 data) +static inline void rtw89_raw_phy_write8(struct rtw89_dev *rtwdev, + u32 addr, u8 data) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; rtw89_write8(rtwdev, addr + phy->cr_base, data); } -static inline void rtw89_phy_write16(struct rtw89_dev *rtwdev, - u32 addr, u16 data) +static inline void rtw89_raw_phy_write16(struct rtw89_dev *rtwdev, + u32 addr, u16 data) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; rtw89_write16(rtwdev, addr + phy->cr_base, data); } -static inline void rtw89_phy_write32(struct rtw89_dev *rtwdev, - u32 addr, u32 data) +static inline void rtw89_raw_phy_write32(struct rtw89_dev *rtwdev, + u32 addr, u32 data) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; rtw89_write32(rtwdev, addr + phy->cr_base, data); } +static inline void rtw89_phy_write8(struct rtw89_dev *rtwdev, + u32 addr, u8 data) +{ + rtwdev->io->phy_write8(rtwdev, addr, data); +} + +static inline void rtw89_phy_write16(struct rtw89_dev *rtwdev, + u32 addr, u16 data) +{ + rtwdev->io->phy_write16(rtwdev, addr, data); +} + +static inline void rtw89_phy_write32(struct rtw89_dev *rtwdev, + u32 addr, u32 data) +{ + rtwdev->io->phy_write32(rtwdev, addr, data); +} + static inline void rtw89_phy_write32_set(struct rtw89_dev *rtwdev, u32 addr, u32 bits) { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index ff4113eb9114..d9a144b4bf58 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2640,6 +2640,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .support_tas = false, .support_sar_by_ant = false, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index ea4d22616512..9cd4c88ee57e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2377,6 +2377,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .support_tas = false, .support_sar_by_ant = false, .support_noise = true, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 417b1502a2dd..13c942127225 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -973,6 +973,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .support_tas = false, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index c2184b092395..3fd5990a8bc4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -812,6 +812,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .support_tas = false, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 9c2c05f80393..9ef469c1080e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3170,6 +3170,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .support_tas = true, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = true, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = true, .rx_freq_from_ie = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 3f94a5e80afe..ebb91fcbd6ab 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -3173,6 +3173,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .support_tas = true, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_from_ie = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index a550ff8de8fc..d79cdbc67103 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2986,6 +2986,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .support_tas = false, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_from_ie = false, From 63586e841cf32ed7f86ed2c31c0386fc838c4290 Mon Sep 17 00:00:00 2001 From: Chia-Yuan Li Date: Mon, 20 Apr 2026 11:40:44 +0800 Subject: [PATCH 029/105] wifi: rtw89: offload DMAC and CMAC init IO to firmware During the initialization of DMAC and CMAC, the driver performs a large number of consecutive register writes. Sending these I/O requests individually over the host interface introduces significant latency and delays the device bring-up process. Optimize the initialization flow by wrapping the register write sequences in mac.c and mac_be.c with rtw89_io_pack() and rtw89_io_unpack(). This batches the operations and offloads them to the firmware via H2C commands, which significantly accelerates the overall initialization time. (8852C USB2.0 AMD B450 1379 ms -> 1273 ms) Signed-off-by: Chia-Yuan Li Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 53 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac_be.c | 57 ++++++++++++++++++++- 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 6f76ae2a99ce..353d7b88fb1a 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -2336,6 +2336,8 @@ static int preload_init_set_ax(struct rtw89_dev *rtwdev, u8 mac_idx, { u32 reg, max_preld_size, min_rsvd_size; + rtw89_io_pack(rtwdev); + max_preld_size = (mac_idx == RTW89_MAC_0 ? PRELD_B0_ENT_NUM : PRELD_B1_ENT_NUM) * PRELD_AMSDU_SIZE; reg = mac_idx == RTW89_MAC_0 ? @@ -2349,6 +2351,8 @@ static int preload_init_set_ax(struct rtw89_dev *rtwdev, u8 mac_idx, rtw89_write32_mask(rtwdev, reg, B_AX_B0_PRELD_NXT_TXENDWIN_MASK, PRELD_NEXT_WND); rtw89_write32_mask(rtwdev, reg, B_AX_B0_PRELD_NXT_RSVMINSZ_MASK, min_rsvd_size); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2428,11 +2432,15 @@ static int sta_sch_init_ax(struct rtw89_dev *rtwdev) return ret; } + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, R_AX_SS_CTRL, B_AX_SS_WARM_INIT_FLG); rtw89_write32_clr(rtwdev, R_AX_SS_CTRL, B_AX_SS_NONEMPTY_SS2FINFO_EN); _patch_ss2f_path(rtwdev); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2444,12 +2452,16 @@ static int mpdu_proc_init_ax(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + rtw89_write32(rtwdev, R_AX_ACTION_FWD0, TRXCFG_MPDU_PROC_ACT_FRWD); rtw89_write32(rtwdev, R_AX_TF_FWD, TRXCFG_MPDU_PROC_TF_FRWD); rtw89_write32_set(rtwdev, R_AX_MPDU_PROC, B_AX_APPEND_FCS | B_AX_A_ICV_ERR); rtw89_write32(rtwdev, R_AX_CUT_AMSDU_CTRL, TRXCFG_MPDU_PROC_CUT_CTRL); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2463,6 +2475,8 @@ static int sec_eng_init_ax(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + val = rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL); /* init clock */ val |= (B_AX_CLK_EN_CGCMP | B_AX_CLK_EN_WAPI | B_AX_CLK_EN_WEP_TKIP); @@ -2488,6 +2502,8 @@ static int sec_eng_init_ax(struct rtw89_dev *rtwdev) rtw89_write32_mask(rtwdev, R_AX_SEC_DEBUG1, B_AX_TX_TIMEOUT_SEL_MASK, AX_TX_TO_VAL); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2571,6 +2587,8 @@ static int scheduler_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_PREBKF_CFG_1, mac_idx); if (rtwdev->chip->chip_id == RTL8852C) rtw89_write32_mask(rtwdev, reg, B_AX_SIFS_MACTXEN_T1_MASK, @@ -2599,6 +2617,8 @@ static int scheduler_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) SCH_PREBKF_24US); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -2667,6 +2687,8 @@ static int rx_fltr_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + for (i = RTW89_MGNT; i <= RTW89_DATA; i++) { ret = rtw89_mac_typ_fltr_opt_ax(rtwdev, i, RTW89_FWD_TO_HOST, mac_idx); @@ -2683,6 +2705,8 @@ static int rx_fltr_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write16(rtwdev, rtw89_mac_reg_by_idx(rtwdev, R_AX_PLCP_HDR_FLTR, mac_idx), plcp_ftlr); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2729,6 +2753,8 @@ static int cca_ctrl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_CCA_CONTROL, mac_idx); val = rtw89_read32(rtwdev, reg); val |= (B_AX_TB_CHK_BASIC_NAV | B_AX_TB_CHK_BTCCA | @@ -2749,16 +2775,22 @@ static int cca_ctrl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) _patch_dis_resp_chk(rtwdev, mac_idx); + rtw89_io_unpack(rtwdev); + return 0; } static int nav_ctrl_init_ax(struct rtw89_dev *rtwdev) { + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, R_AX_WMAC_NAV_CTL, B_AX_WMAC_PLCP_UP_NAV_EN | B_AX_WMAC_TF_UP_NAV_EN | B_AX_WMAC_NAV_UPPER_EN); rtw89_write32_mask(rtwdev, R_AX_WMAC_NAV_CTL, B_AX_WMAC_NAV_UPPER_MASK, NAV_25MS); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2770,12 +2802,17 @@ static int spatial_reuse_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL); if (ret) return ret; + + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_RX_SR_CTRL, mac_idx); rtw89_write8_clr(rtwdev, reg, B_AX_SR_EN); reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_BSSID_SRC_CTRL, mac_idx); rtw89_write8_set(rtwdev, reg, B_AX_PLCP_SRC_EN); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2788,6 +2825,8 @@ static int tmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_MAC_LOOPBACK, mac_idx); rtw89_write32_clr(rtwdev, reg, B_AX_MACLBK_EN); @@ -2798,6 +2837,8 @@ static int tmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_mask(rtwdev, reg, B_AX_TXDFIFO_HIGH_MCS_THRE_MASK, TXDFIFO_HIGH_MCS_THRE); rtw89_write32_mask(rtwdev, reg, B_AX_TXDFIFO_LOW_MCS_THRE_MASK, TXDFIFO_LOW_MCS_THRE); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2812,6 +2853,8 @@ static int trxptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_TRXPTCL_RESP_0, mac_idx); val = rtw89_read32(rtwdev, reg); val &= ~B_AX_WMAC_SPEC_SIFS_CCK_MASK; @@ -2842,6 +2885,8 @@ static int trxptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, rrsr->rsc.addr, mac_idx); rtw89_write32_mask(rtwdev, reg, rrsr->rsc.mask, rrsr->rsc.data); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2876,6 +2921,8 @@ static int rmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (mac_idx == RTW89_MAC_0) rst_bacam(rtwdev); @@ -2917,6 +2964,8 @@ static int rmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_PLCP_HDR_FLTR, mac_idx); rtw89_write8_clr(rtwdev, reg, B_AX_VHT_SU_SIGB_CRC_CHK); + rtw89_io_unpack(rtwdev); + return ret; } @@ -2968,6 +3017,8 @@ static int ptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_SIFS_SETTING, mac_idx); val = rtw89_read32(rtwdev, reg); @@ -3005,6 +3056,8 @@ static int ptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) B_AX_AMPDU_MAX_LEN_VHT_MASK, 0x3FF80); } + rtw89_io_unpack(rtwdev); + return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 39a28fd27412..a0fa8e059739 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -1000,6 +1000,8 @@ static int mpdu_proc_init_be(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, R_BE_MPDU_PROC, B_BE_APPEND_FCS); rtw89_write32(rtwdev, R_BE_CUT_AMSDU_CTRL, TRXCFG_MPDU_PROC_CUT_CTRL | B_BE_CA_CHK_ADDRCAM_EN); @@ -1019,6 +1021,8 @@ static int mpdu_proc_init_be(struct rtw89_dev *rtwdev) val32 = u32_replace_bits(val32, 1, B_BE_FWD_WLAN_CPU_TYPE_1_MASK); rtw89_write32(rtwdev, R_BE_DISP_FWD_WLAN_0, val32); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1031,6 +1035,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + val32 = rtw89_read32(rtwdev, R_BE_SEC_ENG_CTRL); val32 |= B_BE_CLK_EN_CGCMP | B_BE_CLK_EN_WAPI | B_BE_CLK_EN_WEP_TKIP | B_BE_SEC_TX_ENC | B_BE_SEC_RX_DEC | @@ -1041,6 +1047,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev) rtw89_write32_set(rtwdev, R_BE_SEC_MPDU_PROC, B_BE_APPEND_ICV | B_BE_APPEND_MIC); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1083,13 +1091,16 @@ static int mlo_init_be(struct rtw89_dev *rtwdev) u32 reg; int ret; - val32 = rtw89_read32(rtwdev, R_BE_MLO_INIT_CTL); + rtw89_io_pack(rtwdev); + val32 = rtw89_read32(rtwdev, R_BE_MLO_INIT_CTL); val32 |= B_BE_MLO_TABLE_REINIT; rtw89_write32(rtwdev, R_BE_MLO_INIT_CTL, val32); val32 &= ~B_BE_MLO_TABLE_REINIT; rtw89_write32(rtwdev, R_BE_MLO_INIT_CTL, val32); + rtw89_io_unpack(rtwdev); + ret = read_poll_timeout_atomic(rtw89_read32, val32, val32 & B_BE_MLO_TABLE_INIT_DONE, 1, 1000, false, rtwdev, R_BE_MLO_INIT_CTL); @@ -1101,9 +1112,13 @@ static int mlo_init_be(struct rtw89_dev *rtwdev) else reg = R_BE_SS_CTRL_V1; + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, reg, B_BE_MLO_HW_CHGLINK_EN); rtw89_write32_set(rtwdev, R_BE_CMAC_SHARE_ACQCHK_CFG_0, B_BE_R_MACID_ACQ_CHK_EN); + rtw89_io_unpack(rtwdev); + return ret; } @@ -1173,6 +1188,8 @@ static int scheduler_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (chip->chip_id == RTL8922D) { reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_SCH_EXT_CTRL, mac_idx); rtw89_write32_set(rtwdev, reg, B_BE_CWCNT_PLUS_MODE); @@ -1216,6 +1233,8 @@ static int scheduler_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_set(rtwdev, reg, B_BE_TX_NAV_RST_EDCA_EN); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1292,6 +1311,8 @@ static int rx_fltr_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) u32 reg; u32 val; + rtw89_io_pack(rtwdev); + rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_MGNT, RTW89_FWD_TO_HOST, mac_idx); rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_CTRL, RTW89_FWD_TO_HOST, mac_idx); rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_DATA, RTW89_FWD_TO_HOST, mac_idx); @@ -1309,6 +1330,8 @@ static int rx_fltr_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) B_BE_LSIG_PARITY_CHK_EN | B_BE_CCK_SIG_CHK | B_BE_CCK_CRC_CHK; rtw89_write16(rtwdev, reg, val); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1322,6 +1345,8 @@ static int nav_ctrl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) u32 val32; u32 reg; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_WMAC_NAV_CTL, mac_idx); val32 = rtw89_read32(rtwdev, reg); @@ -1337,6 +1362,8 @@ static int nav_ctrl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_TRXPTCL_RESP_0, mac_idx); rtw89_write32_set(rtwdev, reg, B_BE_WMAC_MBA_DUR_FORCE); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1349,12 +1376,16 @@ static int spatial_reuse_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RX_SR_CTRL, mac_idx); rtw89_write8_clr(rtwdev, reg, B_BE_SR_EN | B_BE_SR_CTRL_PLCP_EN); reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_BSSID_SRC_CTRL, mac_idx); rtw89_write8_set(rtwdev, reg, B_BE_PLCP_SRC_EN); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1364,6 +1395,8 @@ static int tmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) struct rtw89_hal *hal = &rtwdev->hal; u32 reg; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_TB_PPDU_CTRL, mac_idx); rtw89_write32_clr(rtwdev, reg, B_BE_QOSNULL_UPD_MUEDCA_EN); @@ -1378,6 +1411,8 @@ static int tmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_clr(rtwdev, reg, CLEAR_DTOP_DIS); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1394,6 +1429,8 @@ static int trxptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_MAC_LOOPBACK, mac_idx); val32 = rtw89_read32(rtwdev, reg); val32 = u32_replace_bits(val32, S_BE_MACLBK_PLCP_DLY_DEF, @@ -1453,6 +1490,8 @@ static int trxptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_mask(rtwdev, reg, B_BE_RSC_MASK, 1); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1495,6 +1534,8 @@ static int rmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) return ret; } + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_DLK_PROTECT_CTL, mac_idx); val16 = rtw89_read16(rtwdev, reg); val16 = u16_replace_bits(val16, TRXCFG_RMAC_DATA_TO, B_BE_RX_DLK_DATA_TIME_MASK); @@ -1534,6 +1575,8 @@ static int rmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_set(rtwdev, reg, B_BE_DIS_CHK_MIN_LEN); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1555,10 +1598,14 @@ static int resp_pktctl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) return ret; } + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RESP_CSI_RESERVED_PAGE, mac_idx); rtw89_write32_mask(rtwdev, reg, B_BE_CSI_RESERVED_START_PAGE_MASK, qt_cfg.pktid); rtw89_write32_mask(rtwdev, reg, B_BE_CSI_RESERVED_PAGE_NUM_MASK, qt_cfg.pg_num + 1); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1602,6 +1649,8 @@ static int ptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (is_qta_poh(rtwdev)) { reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_SIFS_SETTING, mac_idx); val32 = rtw89_read32(rtwdev, reg); @@ -1671,6 +1720,8 @@ static int ptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) B_BE_PRELD_HIQ_P0_EN); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1965,6 +2016,8 @@ static int preload_init_be(struct rtw89_dev *rtwdev, u8 mac_idx, if (!(chip->chip_id == RTL8922A || rtw89_mac_chk_preload_allow(rtwdev))) return 0; + rtw89_io_pack(rtwdev); + max_preld_size = mac_idx == RTW89_MAC_0 ? PRELD_B0_ENT_NUM : PRELD_B1_ENT_NUM; if (chip->chip_id == RTL8922D) @@ -1997,6 +2050,8 @@ static int preload_init_be(struct rtw89_dev *rtwdev, u8 mac_idx, val32 |= B_BE_B0_PRELD_FEN; rtw89_write32(rtwdev, reg, val32); + rtw89_io_unpack(rtwdev); + return 0; } From 850216ff7d120ff0f067b964d56a0ab5132f80f1 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Mon, 20 Apr 2026 11:40:45 +0800 Subject: [PATCH 030/105] wifi: rtw89: use firmware offload for PHY and RF batch register writes Use firmware offload to perform batch register writes for PHY and RF. This reduces I/O operations, improves bus transfer efficiency, and decreases hardware initialization time. Performance improvement on USB 2.0 HCI: - rtw89_phy_init_bb_reg: 61 ms -> 5 ms - rtw89_phy_init_rf_reg: 587 ms -> 106 ms Use offload-aware delay functions (do_udelay and do_mdelay) for the delay calls in rtw89_phy_init_reg, rtw89_phy_config_rf_reg, and rtw89_phy_config_rf_reg_v1 to ensure compatibility with batch register writes. Signed-off-by: Eric Huang Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 38 ++++++++++++++++-------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 2663fc102e77..02120132aa14 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -1352,17 +1352,17 @@ static void rtw89_phy_config_bb_reg(struct rtw89_dev *rtwdev, u32 addr; if (reg->addr == 0xfe) { - mdelay(50); + rtw89_io_mdelay(rtwdev, 50); } else if (reg->addr == 0xfd) { - mdelay(5); + rtw89_io_mdelay(rtwdev, 5); } else if (reg->addr == 0xfc) { - mdelay(1); + rtw89_io_mdelay(rtwdev, 1); } else if (reg->addr == 0xfb) { - udelay(50); + rtw89_io_udelay(rtwdev, 50); } else if (reg->addr == 0xfa) { - udelay(5); + rtw89_io_udelay(rtwdev, 5); } else if (reg->addr == 0xf9) { - udelay(1); + rtw89_io_udelay(rtwdev, 1); } else if (reg->data == BYPASS_CR_DATA) { rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "Bypass CR 0x%x\n", reg->addr); } else { @@ -1692,17 +1692,17 @@ void rtw89_phy_config_rf_reg(struct rtw89_dev *rtwdev, void *extra_data) { if (reg->addr == 0xfe) { - mdelay(50); + rtw89_io_mdelay(rtwdev, 50); } else if (reg->addr == 0xfd) { - mdelay(5); + rtw89_io_mdelay(rtwdev, 5); } else if (reg->addr == 0xfc) { - mdelay(1); + rtw89_io_mdelay(rtwdev, 1); } else if (reg->addr == 0xfb) { - udelay(50); + rtw89_io_udelay(rtwdev, 50); } else if (reg->addr == 0xfa) { - udelay(5); + rtw89_io_udelay(rtwdev, 5); } else if (reg->addr == 0xf9) { - udelay(1); + rtw89_io_udelay(rtwdev, 1); } else { rtw89_write_rf(rtwdev, rf_path, reg->addr, 0xfffff, reg->data); rtw89_phy_cofig_rf_reg_store(rtwdev, reg, rf_path, @@ -1715,6 +1715,11 @@ void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, void *extra_data) { + if (reg->addr == 0xfe) { + rtw89_io_mdelay(rtwdev, 50); + return; + } + rtw89_write_rf(rtwdev, rf_path, reg->addr, RFREG_MASK, reg->data); if (reg->addr < 0x100) @@ -1885,11 +1890,16 @@ void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev) const struct rtw89_phy_table *bb_gain_table; bb_table = elm_info->bb_tbl ? elm_info->bb_tbl : chip->bb_table; + + rtw89_io_pack(rtwdev); + rtw89_phy_init_reg(rtwdev, bb_table, false, rtw89_phy_config_bb_reg, NULL); if (rtwdev->dbcc_en) rtw89_phy_init_reg(rtwdev, bb_table, false, rtw89_phy_config_bb_reg, (void *)RTW89_PHY_1); + rtw89_io_unpack(rtwdev); + rtw89_chip_init_txpwr_unit(rtwdev); bb_gain_table = elm_info->bb_gain ? elm_info->bb_gain : chip->bb_gain_table; @@ -2016,7 +2026,11 @@ void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev, bool noio) else config = rf_table->config ? rf_table->config : rtw89_phy_config_rf_reg; + + rtw89_io_pack(rtwdev); rtw89_phy_init_reg(rtwdev, rf_table, by_acv, config, (void *)rf_reg_info); + rtw89_io_unpack(rtwdev); + if (rtw89_phy_config_rf_reg_fw(rtwdev, rf_reg_info)) rtw89_warn(rtwdev, "rf path %d reg h2c config failed\n", rf_reg_info->rf_path); From 89058548dd146ee9a56aeb0727d306a7f9720473 Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Mon, 20 Apr 2026 11:40:46 +0800 Subject: [PATCH 031/105] wifi: rtw89: 8832cu: Add ID 2c7c:8206 for RTL8832CU This ID is used by RTL8832CU-based Quectel USB dongle devices. Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 092d2812a4d5..790fd1dec66d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -53,6 +53,8 @@ static const struct usb_device_id rtw_8852cu_id_table[] = { .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x28de, 0x2432, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x8206, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x35b2, 0x0502, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0101, 0xff, 0xff, 0xff), From 8beba37615f2f59ea7e978843daf2f5ec7f03868 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Mon, 20 Apr 2026 11:40:47 +0800 Subject: [PATCH 032/105] wifi: rtw89: use struct to fill C2H recv ack This C2H packet is used to indicate that FW has received the corresponding H2C command. No logic is changed. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.h | 18 ++++++++++-------- drivers/net/wireless/realtek/rtw89/mac.c | 13 ++++++++----- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 4d94a7195708..6ef53fcd0cce 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3886,14 +3886,16 @@ struct rtw89_c2h_done_ack { #define RTW89_C2H_SCAN_DONE_ACK_RETURN GENMASK(5, 0) #define RTW89_C2H_DONE_ACK_W2_H2C_SEQ GENMASK(31, 24) -#define RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(1, 0)) -#define RTW89_GET_MAC_C2H_REV_ACK_CLASS(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(7, 2)) -#define RTW89_GET_MAC_C2H_REV_ACK_FUNC(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(15, 8)) -#define RTW89_GET_MAC_C2H_REV_ACK_H2C_SEQ(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(23, 16)) +struct rtw89_c2h_rev_ack { + __le32 w0; + __le32 w1; + __le32 w2; +} __packed; + +#define RTW89_C2H_REV_ACK_W2_CAT GENMASK(1, 0) +#define RTW89_C2H_REV_ACK_W2_CLASS GENMASK(7, 2) +#define RTW89_C2H_REV_ACK_W2_FUNC GENMASK(15, 8) +#define RTW89_C2H_REV_ACK_W2_H2C_SEQ GENMASK(23, 16) struct rtw89_fw_c2h_log_fmt { __le16 signature; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 353d7b88fb1a..3f8275bfe91b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5442,16 +5442,19 @@ rtw89_mac_c2h_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, } static void -rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 len) { /* N.B. This will run in interrupt context. */ + const struct rtw89_c2h_rev_ack *c2h = (const void *)skb_c2h->data; + + u8 h2c_cat = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_CAT); + u8 h2c_class = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_CLASS); + u8 h2c_func = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_FUNC); + u8 h2c_seq = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_H2C_SEQ); rtw89_debug(rtwdev, RTW89_DBG_FW, "C2H rev ack recv, cat: %d, class: %d, func: %d, seq : %d\n", - RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h->data), - RTW89_GET_MAC_C2H_REV_ACK_CLASS(c2h->data), - RTW89_GET_MAC_C2H_REV_ACK_FUNC(c2h->data), - RTW89_GET_MAC_C2H_REV_ACK_H2C_SEQ(c2h->data)); + h2c_cat, h2c_class, h2c_func, h2c_seq); } static void From 6a9b4babeb9b555a6b16df2e9e3238c68c61edbb Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Mon, 20 Apr 2026 11:40:48 +0800 Subject: [PATCH 033/105] wifi: rtw89: check scan C2H event recv ack instead of C2H event done ack The original flow checks the scan offload C2H event done ack to increase the scan sequence. However, the scan offload response C2H event may be received earlier than the scan offload C2H event done ack, which causes these C2H event to be dropped by rtw89_fw_c2h_purge_obsoleted_scan_events() check because the scan sequence of the scan offload response C2H event is less than the scan offload C2H event done ack. Therefore, checking the scan offload C2H event recv ack instead of C2H event done ack to fix this issue. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-14-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 3f8275bfe91b..87e3320b5a81 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5451,10 +5451,30 @@ rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 len u8 h2c_class = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_CLASS); u8 h2c_func = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_FUNC); u8 h2c_seq = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_H2C_SEQ); + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; rtw89_debug(rtwdev, RTW89_DBG_FW, "C2H rev ack recv, cat: %d, class: %d, func: %d, seq : %d\n", h2c_cat, h2c_class, h2c_func, h2c_seq); + + if (h2c_cat != H2C_CAT_MAC) + return; + + switch (h2c_class) { + default: + return; + case H2C_CL_MAC_FW_OFLD: + switch (h2c_func) { + default: + return; + case H2C_FUNC_SCANOFLD: + case H2C_FUNC_SCANOFLD_BE: + scan_info->seq++; + break; + } + + return; + } } static void @@ -5462,7 +5482,6 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le { /* N.B. This will run in interrupt context. */ struct rtw89_wait_info *fw_ofld_wait = &rtwdev->mac.fw_ofld_wait; - struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; struct rtw89_wait_info *ps_wait = &rtwdev->mac.ps_wait; const struct rtw89_c2h_done_ack *c2h = (const struct rtw89_c2h_done_ack *)skb_c2h->data; @@ -5505,11 +5524,9 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN; break; case H2C_FUNC_SCANOFLD: - scan_info->seq++; cond = RTW89_SCANOFLD_WAIT_COND_START; break; case H2C_FUNC_SCANOFLD_BE: - scan_info->seq++; cond = RTW89_SCANOFLD_BE_WAIT_COND_START; h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN; break; From 602b58bb45b2f2bf848215560406ff87dc9a0ab6 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Mon, 20 Apr 2026 11:40:49 +0800 Subject: [PATCH 034/105] wifi: rtw89: suspend DIG when remain-on-channel The PD (Packet Detection) lower bound is set after the STA interface connects. When the P2P interface attempts to find peers, it enters the remain-on-channel flow to listen for probe requests. However, if the RSSI of the connected AP is too high, the P2P interface fails to receive it. To resolve this, suspend the DIG during the remain-on-channel period. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-15-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 7fe979e47ad5..41eefe901ab2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4474,6 +4474,7 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx); rtw89_write32_clr(rtwdev, reg, B_AX_A_UC_CAM_MATCH | B_AX_A_BC_CAM_MATCH); + rtw89_phy_dig_suspend(rtwdev); ieee80211_ready_on_channel(hw); wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); wiphy_delayed_work_queue(hw->wiphy, &rtwvif->roc.roc_work, @@ -4518,6 +4519,7 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtw89_core_handle_sta_pending_tx(rtwdev, rtwvif_link); queue_work(rtwdev->txq_wq, &rtwdev->txq_work); + rtw89_phy_dig_resume(rtwdev, true); if (hw->conf.flags & IEEE80211_CONF_IDLE) wiphy_delayed_work_queue(hw->wiphy, &roc->roc_work, From 2da590b9ccf52d89fed87ec1c5399d1341ba568f Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Mon, 20 Apr 2026 11:40:50 +0800 Subject: [PATCH 035/105] wifi: rtw89: chan: introduce new helper to get entity current configuration The original helper can only queries target channel, but cannot determine current role's status, e.g. is it MLD or not. These things should be a set. Adding more and more helpers to query them individually without synchronous doesn't seem right. Introduce a new helper to query current channel set and role status. Besides, when single channel, e.g. MLO_2_PLUS_0_1RF and MLO_0_PLUS_2_1RF, the target channel pointer will be duplicated to fill the returned channel set. So, some callers can save trivial things for these cases. The returned channels will be non-NULL, so callers don't need trivial NULL check either. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-16-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 90 +++++++++++-------- drivers/net/wireless/realtek/rtw89/chan.h | 21 ++--- drivers/net/wireless/realtek/rtw89/core.c | 20 ++--- drivers/net/wireless/realtek/rtw89/phy.c | 12 ++- drivers/net/wireless/realtek/rtw89/phy_be.c | 8 +- .../net/wireless/realtek/rtw89/rtw8852b_rfk.c | 6 +- .../wireless/realtek/rtw89/rtw8852bt_rfk.c | 6 +- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 17 +--- .../net/wireless/realtek/rtw89/rtw8922a_rfk.c | 22 +---- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 28 ++++-- .../net/wireless/realtek/rtw89/rtw8922d_rfk.c | 22 +---- 11 files changed, 128 insertions(+), 124 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index ceb399fc2b94..cd846cb81f0c 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -398,43 +398,6 @@ static u8 rtw89_entity_role_get_index(struct rtw89_dev *rtwdev) } } -const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, - const char *caller_message, - u8 link_index, bool nullchk) -{ - struct rtw89_hal *hal = &rtwdev->hal; - struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt; - enum rtw89_chanctx_idx chanctx_idx; - u8 role_index; - - lockdep_assert_wiphy(rtwdev->hw->wiphy); - - if (unlikely(link_index >= __RTW89_MLD_MAX_LINK_NUM)) { - WARN(1, "link index %u is invalid (max link inst num: %d)\n", - link_index, __RTW89_MLD_MAX_LINK_NUM); - goto dflt; - } - - role_index = rtw89_entity_role_get_index(rtwdev); - - chanctx_idx = mgnt->chanctx_tbl[role_index][link_index]; - if (chanctx_idx == RTW89_CHANCTX_IDLE) - goto dflt; - - return rtw89_chan_get(rtwdev, chanctx_idx); - -dflt: - if (unlikely(nullchk)) - return NULL; - - rtw89_debug(rtwdev, RTW89_DBG_CHAN, - "%s (%s): prefetch NULL on link index %u\n", - __func__, caller_message ?: "", link_index); - - return rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); -} -EXPORT_SYMBOL(__rtw89_mgnt_chan_get); - bool rtw89_entity_check_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { switch (rtwdev->mlo_dbcc_mode) { @@ -457,6 +420,59 @@ void rtw89_entity_force_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) rtw89_debug(rtwdev, RTW89_DBG_CHAN, "%s: (none)\n", __func__); } +void rtw89_entity_get_conf(struct rtw89_dev *rtwdev, struct rtw89_entity_conf *conf) +{ + struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt; + enum rtw89_chanctx_idx idxes[ARRAY_SIZE(conf->chans)]; + struct rtw89_vif *role; + u8 ridx; + int i; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + memset(conf, 0, sizeof(*conf)); + + ridx = rtw89_entity_role_get_index(rtwdev); + role = mgnt->active_roles[ridx]; + if (role) { + struct ieee80211_vif *vif = rtwvif_to_vif(role); + + conf->is_mld = ieee80211_vif_is_mld(vif); + conf->en_emlsr = role->mlo_mode == RTW89_MLO_MODE_EMLSR; + } + + for (i = 0; i < ARRAY_SIZE(idxes); i++) + idxes[i] = RTW89_CHANCTX_IDLE; + + switch (rtwdev->mlo_dbcc_mode) { + default: + case MLO_2_PLUS_0_1RF: + set_bit(0, conf->hw_bitmap); + idxes[0] = mgnt->chanctx_tbl[ridx][0]; + idxes[1] = idxes[0]; + break; + case MLO_0_PLUS_2_1RF: + set_bit(1, conf->hw_bitmap); + idxes[1] = mgnt->chanctx_tbl[ridx][1]; + idxes[0] = idxes[1]; + break; + case MLO_1_PLUS_1_1RF: + set_bit(0, conf->hw_bitmap); + set_bit(1, conf->hw_bitmap); + idxes[0] = mgnt->chanctx_tbl[ridx][0]; + idxes[1] = mgnt->chanctx_tbl[ridx][1]; + break; + } + + for (i = 0; i < ARRAY_SIZE(idxes); i++) { + if (idxes[i] == RTW89_CHANCTX_IDLE) + idxes[i] = RTW89_CHANCTX_0; + + conf->chans[i] = rtw89_chan_get(rtwdev, idxes[i]); + } +} +EXPORT_SYMBOL(rtw89_entity_get_conf); + static enum rtw89_mlo_dbcc_mode rtw89_entity_sel_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws) { diff --git a/drivers/net/wireless/realtek/rtw89/chan.h b/drivers/net/wireless/realtek/rtw89/chan.h index c797cda2e763..a9a5f1b307a2 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.h +++ b/drivers/net/wireless/realtek/rtw89/chan.h @@ -116,6 +116,13 @@ struct rtw89_entity_weight { unsigned int active_roles; }; +struct rtw89_entity_conf { + bool is_mld; + bool en_emlsr; + DECLARE_BITMAP(hw_bitmap, __RTW89_MLD_MAX_LINK_NUM); + const struct rtw89_chan *chans[__RTW89_MLD_MAX_LINK_NUM]; +}; + static inline bool rtw89_get_entity_state(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { @@ -168,6 +175,7 @@ void rtw89_entity_init(struct rtw89_dev *rtwdev); enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev); bool rtw89_entity_check_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw89_entity_force_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +void rtw89_entity_get_conf(struct rtw89_dev *rtwdev, struct rtw89_entity_conf *conf); void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev); void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev, @@ -180,19 +188,6 @@ void rtw89_chanctx_pause(struct rtw89_dev *rtwdev, void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev, const struct rtw89_chanctx_cb_parm *cb_parm); -const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, - const char *caller_message, - u8 link_index, bool nullchk); - -#define rtw89_mgnt_chan_get(rtwdev, link_index) \ - __rtw89_mgnt_chan_get(rtwdev, __func__, link_index, false) - -static inline const struct rtw89_chan * -rtw89_mgnt_chan_get_or_null(struct rtw89_dev *rtwdev, u8 link_index) -{ - return __rtw89_mgnt_chan_get(rtwdev, NULL, link_index, true); -} - struct rtw89_mcc_links_info { struct rtw89_vif_link *links[NUM_OF_RTW89_MCC_ROLES]; }; diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 41eefe901ab2..aeeba52b8c5e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -474,16 +474,16 @@ static void __rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev, void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan; + struct rtw89_entity_conf conf; - chan = rtw89_mgnt_chan_get(rtwdev, 0); - __rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_0); + rtw89_entity_get_conf(rtwdev, &conf); + + __rtw89_core_set_chip_txpwr(rtwdev, conf.chans[0], RTW89_PHY_0); if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) return; - chan = rtw89_mgnt_chan_get(rtwdev, 1); - __rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_1); + __rtw89_core_set_chip_txpwr(rtwdev, conf.chans[1], RTW89_PHY_1); } void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev, @@ -562,7 +562,7 @@ static void __rtw89_set_channel(struct rtw89_dev *rtwdev, int rtw89_set_channel(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan; + struct rtw89_entity_conf conf; enum rtw89_entity_mode mode; mode = rtw89_entity_recalc(rtwdev); @@ -571,14 +571,14 @@ int rtw89_set_channel(struct rtw89_dev *rtwdev) return -EINVAL; } - chan = rtw89_mgnt_chan_get(rtwdev, 0); - __rtw89_set_channel(rtwdev, chan, RTW89_MAC_0, RTW89_PHY_0); + rtw89_entity_get_conf(rtwdev, &conf); + + __rtw89_set_channel(rtwdev, conf.chans[0], RTW89_MAC_0, RTW89_PHY_0); if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) return 0; - chan = rtw89_mgnt_chan_get(rtwdev, 1); - __rtw89_set_channel(rtwdev, chan, RTW89_MAC_1, RTW89_PHY_1); + __rtw89_set_channel(rtwdev, conf.chans[1], RTW89_MAC_1, RTW89_PHY_1); return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 02120132aa14..95b5ace2a27e 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -6847,11 +6847,15 @@ static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev, static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); struct rtw89_dig_info *dig = &bb->dig; bool is_linked = rtwdev->total_sta_assoc > 0; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; const u16 *fa_th_src = NULL; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[bb->phy_idx]; + switch (chan->band_type) { case RTW89_BAND_2G: dig->lna_gain = dig->lna_gain_g; @@ -7181,14 +7185,18 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, u8 rssi, bool enable) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; struct rtw89_dig_info *dig = &bb->dig; struct rtw89_hal *hal = &rtwdev->hal; u8 final_rssi, under_region = dig->pd_low_th_ofst; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; s8 cck_cca_th; u32 pd_val; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[bb->phy_idx]; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) { pd_val = hal->fixed_dig_pd_th; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 929fac1b10d2..25f1b068daa2 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -865,6 +865,7 @@ static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, { enum rtw89_phy_idx phy_idx = mac_idx != RTW89_MAC_0 ? RTW89_PHY_1 : RTW89_PHY_0; enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; + struct rtw89_entity_conf conf; const struct rtw89_chan *chan; if (chip_id != RTL8922D) @@ -879,9 +880,10 @@ static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, phy_idx); - chan = rtw89_mgnt_chan_get(rtwdev, phy_idx); - if (chan) - rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[phy_idx]; + + rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); } static void rtw89_phy_bb_wrap_ul_pwr(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index 70b1515c00fa..0d443edfe259 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -4167,11 +4167,15 @@ void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev, void rtw8852b_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, 0); struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; struct rtw89_rfk_chan_desc desc[__RTW89_RFK_CHS_NR_V0] = {}; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; u8 idx; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[0]; + for (idx = 0; idx < ARRAY_SIZE(desc); idx++) { struct rtw89_rfk_chan_desc *p = &desc[idx]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c index 961b26ba2d3c..433807e712e4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c @@ -4234,11 +4234,15 @@ void rtw8852bt_set_channel_rf(struct rtw89_dev *rtwdev, void rtw8852bt_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, 0); struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; struct rtw89_rfk_chan_desc desc[__RTW89_RFK_CHS_NR_V0] = {}; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; u8 idx; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[0]; + for (idx = 0; idx < ARRAY_SIZE(desc); idx++) { struct rtw89_rfk_chan_desc *p = &desc[idx]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index ebb91fcbd6ab..3c453b93c52e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2054,7 +2054,7 @@ static void rtw8922a_digital_pwr_comp(struct rtw89_dev *rtwdev, static int rtw8922a_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode) { - const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; if (mode == MLO_1_PLUS_1_1RF || mode == DBCC_LEGACY) { rtw89_phy_write32_mask(rtwdev, R_DBCC, B_DBCC_EN, 0x1); @@ -2067,19 +2067,10 @@ static int rtw8922a_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode return -EOPNOTSUPP; } - if (mode == MLO_1_PLUS_1_1RF) { - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - } else if (mode == MLO_0_PLUS_2_1RF) { - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - chan0 = chan1; - } else { - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = chan0; - } + rtw89_entity_get_conf(rtwdev, &conf); - rtw8922a_ctrl_afe_dac(rtwdev, chan0->band_width, RF_PATH_A); - rtw8922a_ctrl_afe_dac(rtwdev, chan1->band_width, RF_PATH_B); + rtw8922a_ctrl_afe_dac(rtwdev, conf.chans[0]->band_width, RF_PATH_A); + rtw8922a_ctrl_afe_dac(rtwdev, conf.chans[1]->band_width, RF_PATH_B); rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0x6180); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c index 98f14b31cf52..1e2b563f2bca 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c @@ -278,27 +278,13 @@ static u8 rtw8922a_chlk_reload_sel_tbl(struct rtw89_dev *rtwdev, static void rtw8922a_chlk_reload(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; u8 s0_tbl, s1_tbl; - switch (rtwdev->mlo_dbcc_mode) { - default: - case MLO_2_PLUS_0_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = chan0; - break; - case MLO_0_PLUS_2_1RF: - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - chan0 = chan1; - break; - case MLO_1_PLUS_1_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - break; - } + rtw89_entity_get_conf(rtwdev, &conf); - s0_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, chan0, 0); - s1_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, chan1, 1); + s0_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, conf.chans[0], 0); + s1_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, conf.chans[1], 1); rtw8922a_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl); rtw8922a_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index d79cdbc67103..aa58a0a0d554 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -1943,10 +1943,19 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, static void rtw8922d_digital_pwr_comp(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - const struct rtw89_chan *chan1 = rtw89_mgnt_chan_get(rtwdev, 1); + const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; - if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { + rtw89_entity_get_conf(rtwdev, &conf); + chan0 = conf.chans[0]; + chan1 = conf.chans[1]; + + if (conf.en_emlsr) { + rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 1, RF_PATH_A, RTW89_PHY_0); + rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 1, RF_PATH_B, RTW89_PHY_0); + rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 1, RF_PATH_A, RTW89_PHY_1); + rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 1, RF_PATH_B, RTW89_PHY_1); + } else if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 0, RF_PATH_A, RTW89_PHY_0); rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 0, RF_PATH_B, RTW89_PHY_1); } else { @@ -1958,7 +1967,6 @@ static void rtw8922d_digital_pwr_comp(struct rtw89_dev *rtwdev, static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode, bool pwr_comp) { - const struct rtw89_chan *chan1; u32 reg0, reg1; u8 cck_phy_idx; @@ -2016,8 +2024,10 @@ static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode rtw89_write32_mask(rtwdev, reg0, B_BBWRAP_ELMSR_EN_BE4, 0); rtw89_write32_mask(rtwdev, reg1, B_BBWRAP_ELMSR_EN_BE4, 0); } else if ((mode == MLO_1_PLUS_1_1RF) || (mode == DBCC_LEGACY)) { - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - cck_phy_idx = chan1->band_type == RTW89_BAND_2G ? + struct rtw89_entity_conf conf; + + rtw89_entity_get_conf(rtwdev, &conf); + cck_phy_idx = conf.chans[1]->band_type == RTW89_BAND_2G ? RTW89_PHY_1 : RTW89_PHY_0; rtw89_phy_write32_mask(rtwdev, R_SYS_DBCC_BE4, @@ -2475,9 +2485,11 @@ static void rtw8922d_set_txpwr(struct rtw89_dev *rtwdev, static void rtw8922d_set_txpwr_ctrl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, phy_idx); + struct rtw89_entity_conf conf; - rtw8922d_set_txpwr_ref(rtwdev, chan, phy_idx); + rtw89_entity_get_conf(rtwdev, &conf); + + rtw8922d_set_txpwr_ref(rtwdev, conf.chans[phy_idx], phy_idx); } static void rtw8922d_ctrl_trx_path(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c index 4e6a8e88a71e..7957f7b2d8e5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c @@ -187,27 +187,13 @@ static u8 rtw8922d_chlk_reload_sel_tbl(struct rtw89_dev *rtwdev, static void rtw8922d_chlk_reload(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; u8 s0_tbl, s1_tbl; - switch (rtwdev->mlo_dbcc_mode) { - default: - case MLO_2_PLUS_0_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = chan0; - break; - case MLO_0_PLUS_2_1RF: - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - chan0 = chan1; - break; - case MLO_1_PLUS_1_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - break; - } + rtw89_entity_get_conf(rtwdev, &conf); - s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, chan0, 0); - s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, chan1, 1); + s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[0], 0); + s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[1], 1); rtw8922d_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl); rtw8922d_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl); From b465bc7e18a7a2308db3658b1a4f7f55bad0fd3e Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Mon, 20 Apr 2026 11:40:51 +0800 Subject: [PATCH 036/105] wifi: rtw89: 8922d: update RF calibration flow for MLD RF calibration (RFK) will record the results in HW RFK tables. SW can select and configure the target HW RFK table to reuse the RFK result on the target channel. Though the above can work as before, when MLD runs eMLSR mode, a faster switching between links than SW is needed. So, introduce a new working flow for MLD. With the new settings, HW can easily switch RFK results between links during eMLSR mode. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-17-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 4 ++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 7 ++ .../net/wireless/realtek/rtw89/rtw8922d_rfk.c | 70 +++++++++++++++---- .../net/wireless/realtek/rtw89/rtw8922d_rfk.h | 1 + 4 files changed, 69 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 570d5883761b..1e1125235f0c 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8466,6 +8466,8 @@ #define RR_MODOPT_V1 0x10001 #define RR_SW_SEL BIT(19) #define RR_TXG_SEL GENMASK(19, 17) +#define RR_TBL_SEL_IDX1 GENMASK(9, 8) +#define RR_TBL_SEL_IDX0 GENMASK(7, 6) #define RR_MODOPT_M_TXPWR GENMASK(5, 0) #define RR_WLSEL 0x02 #define RR_WLSEL_AG GENMASK(18, 16) @@ -10691,6 +10693,8 @@ #define R_KTBL0B_BE4 0x38204 #define B_KTBL0_IDX0 GENMASK(1, 0) #define B_KTBL0_IDX1 GENMASK(9, 8) +#define B_KTBL0_MLD_IDX0 GENMASK(25, 24) +#define B_KTBL0_MLD_IDX1 GENMASK(27, 26) #define B_KTBL0_RST BIT(31) #define R_KTBL1A_BE4 0x38154 #define R_KTBL1B_BE4 0x38254 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index aa58a0a0d554..ba736b967880 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2332,12 +2332,16 @@ static void rtw8922d_rfk_tssi(struct rtw89_dev *rtwdev, static void rtw8922d_rfk_channel(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif_link->rtwvif); enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, chanctx_idx); enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, RF_AB, chanctx_idx); u32 tx_en; + if (ieee80211_vif_is_mld(vif)) + rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, true); + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_START); rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); _wait_rx_mode(rtwdev, RF_AB); @@ -2353,6 +2357,9 @@ static void rtw8922d_rfk_channel(struct rtw89_dev *rtwdev, rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_STOP); + + if (ieee80211_vif_is_mld(vif)) + rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, false); } static void rtw8922d_rfk_band_changed(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c index 7957f7b2d8e5..a465e6631a22 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c @@ -105,26 +105,60 @@ static void rtw8922d_set_syn01(struct rtw89_dev *rtwdev, enum _rf_syn_pow syn) } } -static void rtw8922d_chlk_ktbl_sel(struct rtw89_dev *rtwdev, u8 kpath, u8 idx) +void rtw8922d_chlk_ktbl_ctl_mld(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool manual) { - bool mlo_linking = false; + if (manual) { + struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; + u8 path = phy_idx; /* numerical equal for target */ + u8 idx = rfk_mcc[path].table_idx; - if (idx > 2) { - rtw89_warn(rtwdev, "[DBCC][ERROR]indx is out of limit!! index(%d)", idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_RST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_RST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_IDX0, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_IDX1, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_IDX0, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_IDX1, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL1A_BE4, B_KTBL1_TBL0, idx & BIT(0)); + rtw89_phy_write32_mask(rtwdev, R_KTBL1A_BE4, B_KTBL1_TBL1, (idx & BIT(1)) >> 1); + rtw89_phy_write32_mask(rtwdev, R_KTBL1B_BE4, B_KTBL1_TBL0, idx & BIT(0)); + rtw89_phy_write32_mask(rtwdev, R_KTBL1B_BE4, B_KTBL1_TBL1, (idx & BIT(1)) >> 1); return; } - if (mlo_linking) { - if (kpath & RF_A) { - rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_SW_SEL, 0x0); - rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_SW_SEL, 0x0); - } + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_RST, 0x0); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_RST, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_SW_SEL, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_SW_SEL, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_SW_SEL, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_SW_SEL, 0x0); +} - if (kpath & RF_B) { - rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_SW_SEL, 0x0); - rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_SW_SEL, 0x0); - } +static void rtw8922d_chlk_ktbl_sel_mld(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, u8 idx) +{ + if (phy_idx == RTW89_PHY_0) { + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_MLD_IDX0, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_MLD_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_TBL_SEL_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_TBL_SEL_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_TBL_SEL_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_TBL_SEL_IDX0, idx); + } else if (phy_idx == RTW89_PHY_1) { + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_MLD_IDX1, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_MLD_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_TBL_SEL_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_TBL_SEL_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_TBL_SEL_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_TBL_SEL_IDX1, idx); + } + rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, false); +} + +static void rtw8922d_chlk_ktbl_sel(struct rtw89_dev *rtwdev, u8 kpath, u8 idx) +{ + if (idx > 2) { + rtw89_warn(rtwdev, "[DBCC][ERROR]indx is out of limit!! index(%d)", idx); return; } @@ -195,8 +229,18 @@ static void rtw8922d_chlk_reload(struct rtw89_dev *rtwdev) s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[0], 0); s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[1], 1); + if (conf.is_mld) + goto mld; + rtw8922d_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl); rtw8922d_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl); + return; + +mld: + if (test_bit(RTW89_PHY_0, conf.hw_bitmap)) + rtw8922d_chlk_ktbl_sel_mld(rtwdev, RTW89_PHY_0, s0_tbl); + if (test_bit(RTW89_PHY_1, conf.hw_bitmap)) + rtw8922d_chlk_ktbl_sel_mld(rtwdev, RTW89_PHY_1, s1_tbl); } static enum _rf_syn_pow rtw8922d_get_syn_pow(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h index c5bbe0eb972a..11742b4a8f59 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h @@ -18,5 +18,6 @@ void rtw8922d_rfk_mlo_ctrl(struct rtw89_dev *rtwdev); void rtw8922d_pre_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8922d_post_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8922d_lck_track(struct rtw89_dev *rtwdev); +void rtw8922d_chlk_ktbl_ctl_mld(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool manual); #endif From 6e76e9ed273dfb4b3333a5ebbb94958cc5752ab6 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 11:14:34 +0000 Subject: [PATCH 037/105] wifi: rtw88: fix OOB read from firmware RX descriptor exceeding DMA buffer In rtw_pci_rx_napi(), new_len is computed as the sum of pkt_len (14-bit descriptor field, max 16383) and pkt_offset (drv_info_sz + shift, both firmware-controlled). The result can exceed RTK_PCI_RX_BUF_SIZE (11478), causing an out-of-bounds read from the pre-allocated DMA buffer when skb_put_data copies new_len bytes. The USB transport already validates this (rtw_usb_rx_data_put checks against RTW_USB_MAX_RECVBUF_SZ); the PCIe path does not. Add a check that new_len does not exceed the DMA buffer size. Fixes: e3037485c68e ("rtw88: new Realtek 802.11ac driver") Signed-off-by: Tristan Madani Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260421111434.3389674-1-tristmd@gmail.com --- drivers/net/wireless/realtek/rtw88/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index c56beacbb1b0..c2bf44e880cf 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1077,6 +1077,11 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, * discard the frame if none available */ new_len = pkt_stat.pkt_len + pkt_offset; + if (unlikely(new_len > RTK_PCI_RX_BUF_SIZE)) { + rtw_dbg(rtwdev, RTW_DBG_RX, + "oversized RX packet: %u\n", new_len); + goto next_rp; + } new = dev_alloc_skb(new_len); if (WARN_ONCE(!new, "rx routine starvation\n")) goto next_rp; From 6d88244bb129755acca696f9227200f4a2d106a6 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 11:14:42 +0000 Subject: [PATCH 038/105] wifi: rtw89: add bounds check on firmware mac_id in link lookup The mac_id field in RX descriptors is 8 bits wide (0-255), but assoc_link_on_macid[] has only RTW89_MAX_MAC_ID_NUM (128) entries. While the driver currently assigns mac_id values below 128, the descriptor value comes from firmware and is not validated before use as an array index. Add a defensive bounds check in rtw89_assoc_link_rcu_dereference() to guard against out-of-range firmware values. Fixes: 144c6cd24b35 ("wifi: rtw89: 8922a: configure AP_LINK_PS if FW supports") Signed-off-by: Tristan Madani Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260421111442.3395411-1-tristmd@gmail.com --- drivers/net/wireless/realtek/rtw89/core.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index bf5585c701ad..b290da650c70 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6527,6 +6527,9 @@ static inline void rtw89_assoc_link_clr(struct rtw89_sta_link *rtwsta_link) static inline struct rtw89_sta_link * rtw89_assoc_link_rcu_dereference(struct rtw89_dev *rtwdev, u8 macid) { + if (unlikely(macid >= RTW89_MAX_MAC_ID_NUM)) + return NULL; + return rcu_dereference(rtwdev->assoc_link_on_macid[macid]); } From 329e9fff98f85520843e7d75b7033339e5fb2b7e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 24 Apr 2026 15:25:50 +0800 Subject: [PATCH 039/105] wifi: rtw89: pci: no need to wait CLK ready for RTL8922DE The bit is to wait for CLK ready when entering PCIE L1SS. However, a certain host might not enable CLK for a situation, causing device state stuck and trigger PCIE SER. Unset this bit as default like earlier chips. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260424072552.59220-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.h | 2 ++ drivers/net/wireless/realtek/rtw89/pci_be.c | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index e7da37b9da7d..d0d1e2b99698 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -33,6 +33,8 @@ #define RAC_REG_REV2 0x1B #define BAC_CMU_EN_DLY_MASK GENMASK(15, 12) #define PCIE_DPHY_DLY_25US 0x1 +#define RAC_ANA14 0x14 +#define EIEOS_L1SS_WAIT_CLKRDY BIT(2) #define RAC_ANA19 0x19 #define B_PCIE_BIT_RD_SEL BIT(2) #define RAC_REG_FLD_0 0x1D diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c index dfffec1ff3c7..473d491eb3f4 100644 --- a/drivers/net/wireless/realtek/rtw89/pci_be.c +++ b/drivers/net/wireless/realtek/rtw89/pci_be.c @@ -83,6 +83,17 @@ static void _patch_pcie_power_wake_be(struct rtw89_dev *rtwdev, bool power_up) rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, BIT_WAKE_CTRL_V1); } +static void _patch_pre_init_be(struct rtw89_dev *rtwdev) +{ + struct rtw89_hal *hal = &rtwdev->hal; + + if (!(rtwdev->chip->chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)) + return; + + rtw89_write16_clr(rtwdev, R_RAC_DIRECT_OFFSET_BE_LANE0_G2 + + RAC_ANA14 * RAC_MULT, EIEOS_L1SS_WAIT_CLKRDY); +} + static void rtw89_pci_set_io_rcy_be(struct rtw89_dev *rtwdev) { const struct rtw89_pci_info *info = rtwdev->pci_info; @@ -475,6 +486,7 @@ static int rtw89_pci_ops_mac_pre_init_be(struct rtw89_dev *rtwdev) rtw89_pci_set_io_rcy_be(rtwdev); _patch_pcie_power_wake_be(rtwdev, true); + _patch_pre_init_be(rtwdev); rtw89_pci_ctrl_wpdma_pcie_be(rtwdev, false); rtw89_pci_ctrl_trxdma_pcie_be(rtwdev, MAC_AX_PCIE_DISABLE, MAC_AX_PCIE_DISABLE, MAC_AX_PCIE_DISABLE); From 1e53b826d5f8a3ca6d88fa6ed263acf54503214b Mon Sep 17 00:00:00 2001 From: Dian-Syuan Yang Date: Fri, 24 Apr 2026 15:25:51 +0800 Subject: [PATCH 040/105] wifi: rtw89: disable HTC field in AP mode Disable HE QoS HTC field when operating in AP mode to resolve interoperability issues with some stations. Signed-off-by: Dian-Syuan Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260424072552.59220-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index aeeba52b8c5e..7e058c071cc4 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -947,6 +947,7 @@ __rtw89_core_tx_check_he_qos_htc(struct rtw89_dev *rtwdev, enum btc_pkt_type pkt_type) { struct rtw89_sta_link *rtwsta_link = tx_req->rtwsta_link; + struct ieee80211_vif *vif = tx_req->vif; struct sk_buff *skb = tx_req->skb; struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_link_sta *link_sta; @@ -978,6 +979,9 @@ __rtw89_core_tx_check_he_qos_htc(struct rtw89_dev *rtwdev, if (rtwsta_link && rtwsta_link->ra_report.might_fallback_legacy) return false; + if (vif->type == NL80211_IFTYPE_AP) + return false; + return true; } From d1fba55228685a7a237681be739683eaf698b9bc Mon Sep 17 00:00:00 2001 From: Dian-Syuan Yang Date: Fri, 24 Apr 2026 15:25:52 +0800 Subject: [PATCH 041/105] wifi: rtw89: disable CSI STBC for VHT 160MHz Fix interoperability problem where CSI feedback with STBC enabled at VHT 160MHz BW cannot be properly decoded by certain APs, causing CSI reports to be rejected. This problem is specific to Wi-Fi 7 chips, as Wi-Fi 6 defaults to 20MHz CSI BW. Therefore, disable STBC encoding for CSI transmission in VHT 160MHz mode to ensure CSI feedback is accepted by these APs and maintain smooth throughput. Signed-off-by: Dian-Syuan Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260424072552.59220-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac_be.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index a0fa8e059739..dd0dbe78a1ed 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -2805,6 +2805,10 @@ static int rtw89_mac_set_csi_para_reg_be(struct rtw89_dev *rtwdev, sound_dim = min(sound_dim, t); } + /* Disable stbc_en in VHT 160MHz to avoid IOT issues with certain APs */ + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) + stbc_en = 0; + nc = min(nc, sound_dim); nr = min(nr, sound_dim); From 83d38df6929118c3f996b9e3351c2d5014073d87 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 25 Apr 2026 22:32:58 +0300 Subject: [PATCH 042/105] wifi: rtlwifi: rtl8821ae: Fix C2H bit location in RX descriptor Bit 28 of double word 2 in the RX descriptor indicates if the packet is a normal 802.11 frame, or a message from the wifi firmware to the driver (Card 2 Host). Commit f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation macros") mistakenly made the driver look for this bit in double word 1, causing packet loss and Bluetooth coexistence problems. Fixes: f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation macros") Cc: Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/04da7398-cedb-425a-a810-5772ab10139d@gmail.com --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h index 1155365348f3..d5de09d75f45 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h @@ -291,7 +291,7 @@ static inline int get_rx_desc_paggr(__le32 *__pdesc) static inline int get_rx_status_desc_rpt_sel(__le32 *__pdesc) { - return le32_get_bits(*(__pdesc + 1), BIT(28)); + return le32_get_bits(*(__pdesc + 2), BIT(28)); } static inline int get_rx_desc_rxmcs(__le32 *__pdesc) From ef771eabc79d5f21b63689cca0e0fa5493fa0a8a Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 29 Apr 2026 15:02:48 +0300 Subject: [PATCH 043/105] wifi: rtl8xxxu: Detect the maximum supported channel width Some devices malfunction when connected to a network with 40 MHz channel width, because they don't support that. RTL8188FU, RTL8192FU, and RTL8710BU (RTL8188GU) have a way to signal this (and some other capabilities) to the driver. Get this information from the hardware and advertise 40 MHz support only when the hardware can handle it. We assume the other chips can always handle it. RTL8710BU needs a different way to retrieve this information, which will be implemented some other time. Fixes: dbf9b7bb0edf ("wifi: rtl8xxxu: Enable 40 MHz width by default") Cc: stable@vger.kernel.org Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221394 Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/c57de68e-5d57-4c26-898f-8a284bb25381@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8188f.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8192c.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8192e.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8192f.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8710b.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8723a.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8723b.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/core.c | 64 +++++++++++++++++-- drivers/net/wireless/realtek/rtl8xxxu/regs.h | 2 + .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 7 ++ 11 files changed, 76 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c index 766a7a7c7d28..fea5aec9ced9 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c @@ -1866,6 +1866,7 @@ struct rtl8xxxu_fileops rtl8188eu_fops = { .has_tx_report = 1, .init_reg_pkt_life_time = 1, .gen2_thermal_meter = 1, + .hw_feature_report = 0, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, .adda_1t_path_on = 0x0bdb25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/8188f.c index 3abf14d7044f..5556aad1a7ad 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8188f.c @@ -1745,6 +1745,7 @@ struct rtl8xxxu_fileops rtl8188fu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 1, .ampdu_max_time = 0x70, .ustime_tsf_edca = 0x28, .max_aggr_num = 0x0c14, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c index 444872131c66..508322a8f310 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c @@ -723,6 +723,7 @@ struct rtl8xxxu_fileops rtl8192cu_fops = { .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), .supports_ap = 1, + .hw_feature_report = 0, .max_macid_num = 32, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/8192e.c index 8e123bbfc665..690ace303150 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192e.c @@ -1751,6 +1751,7 @@ struct rtl8xxxu_fileops rtl8192eu_fops = { .has_s0s1 = 0, .gen2_thermal_meter = 1, .needs_full_init = 1, + .hw_feature_report = 0, .supports_ap = 1, .max_macid_num = 128, .max_sec_cam_num = 64, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192f.c b/drivers/net/wireless/realtek/rtl8xxxu/8192f.c index cd2156b7a57a..5f076da4b550 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192f.c @@ -2074,6 +2074,7 @@ struct rtl8xxxu_fileops rtl8192fu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 1, .ampdu_max_time = 0x5e, .ustime_tsf_edca = 0x50, .max_aggr_num = 0x1f1f, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8710b.c b/drivers/net/wireless/realtek/rtl8xxxu/8710b.c index 11c63c320eae..a87e692bfc81 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8710b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8710b.c @@ -1852,6 +1852,7 @@ struct rtl8xxxu_fileops rtl8710bu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 0, /* TODO, it's different */ .ampdu_max_time = 0x5e, /* * The RTL8710BU vendor driver uses 0x50 here and it works fine, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c index 4f4493d0bfc2..18e038b82f49 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c @@ -632,6 +632,7 @@ struct rtl8xxxu_fileops rtl8723au_fops = { .rx_agg_buf_size = 16000, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), + .hw_feature_report = 0, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, .adda_1t_path_on = 0x0bdb25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c index cc2e60b06f64..e314ef991b38 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c @@ -1746,6 +1746,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops = { .gen2_thermal_meter = 1, .needs_full_init = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 0, .ampdu_max_time = 0x5e, .ustime_tsf_edca = 0x50, .max_aggr_num = 0x0c14, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 508137e4a87a..646fe76b086e 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -14,6 +14,7 @@ */ #include +#include #include "regs.h" #include "rtl8xxxu.h" @@ -3915,6 +3916,46 @@ static inline u8 rtl8xxxu_get_macid(struct rtl8xxxu_priv *priv, return sta_info->macid; } +static void rtl8xxxu_request_hw_feature(struct rtl8xxxu_priv *priv) +{ + if (!priv->fops->hw_feature_report) + return; + + rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, C2H_HW_FEATURE_DUMP); +} + +static int rtl8xxxu_dump_hw_feature(struct rtl8xxxu_priv *priv) +{ + static const u8 bw_map[8] = { 0, 0, 160, 5, 10, 20, 40, 80 }; + struct rtl8xxxu_hw_feature *hw_feature = &priv->hw_feature; + u8 feature[13]; + int i, ret; + u8 id, bw; + + if (!priv->fops->hw_feature_report) { + hw_feature->max_bw = 40; + return 0; + } + + ret = read_poll_timeout(rtl8xxxu_read8, id, + id == C2H_HW_FEATURE_REPORT, + 10000, 800000, false, + priv, REG_C2HEVT_MSG_NORMAL); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(feature); i++) + feature[i] = rtl8xxxu_read8(priv, REG_C2HEVT_MSG_NORMAL + 2 + i); + + rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, 0); + + bw = u8_get_bits(feature[6], GENMASK(2, 0)); + + hw_feature->max_bw = bw_map[bw]; + + return 0; +} + static int rtl8xxxu_init_device(struct ieee80211_hw *hw) { struct rtl8xxxu_priv *priv = hw->priv; @@ -3961,6 +4002,8 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) */ rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, fops->trxff_boundary); + rtl8xxxu_request_hw_feature(priv); + for (int retry = 5; retry >= 0 ; retry--) { ret = rtl8xxxu_download_firmware(priv); dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret); @@ -3976,6 +4019,12 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) if (ret) goto exit; + ret = rtl8xxxu_dump_hw_feature(priv); + if (ret) { + dev_err(dev, "failed to dump hw feature\n"); + goto exit; + } + if (fops->phy_init_antenna_selection) fops->phy_init_antenna_selection(priv); @@ -7844,15 +7893,20 @@ static int rtl8xxxu_probe(struct usb_interface *interface, sband->ht_cap.ht_supported = true; sband->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; sband->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; - sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20; + + if (priv->hw_feature.max_bw >= 40) { + sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } else { + dev_info(&udev->dev, "hardware doesn't support HT40\n"); + } + memset(&sband->ht_cap.mcs, 0, sizeof(sband->ht_cap.mcs)); sband->ht_cap.mcs.rx_mask[0] = 0xff; sband->ht_cap.mcs.rx_mask[4] = 0x01; - if (priv->rf_paths > 1) { + if (priv->rf_paths > 1) sband->ht_cap.mcs.rx_mask[1] = 0xff; - sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - } sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; hw->wiphy->bands[NL80211_BAND_2GHZ] = sband; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/regs.h b/drivers/net/wireless/realtek/rtl8xxxu/regs.h index 0741db8d08bf..eafaef1438b9 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/regs.h @@ -447,6 +447,8 @@ /* 8188EU */ #define REG_32K_CTRL 0x0194 #define REG_C2HEVT_MSG_NORMAL 0x01a0 +#define C2H_HW_FEATURE_REPORT 0x19 +#define C2H_HW_FEATURE_DUMP 0xfd /* 8192EU/8723BU/8812 */ #define REG_C2HEVT_CMD_ID_8723B 0x01ae #define REG_C2HEVT_CLEAR 0x01af diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 9fb2583ffffc..eeb18eb0e4c0 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1789,11 +1789,17 @@ struct rtl8xxxu_cfo_tracking { #define RTL8XXXU_BC_MC_MACID1 1 #define RTL8XXXU_MAX_SEC_CAM_NUM 64 +struct rtl8xxxu_hw_feature { + u8 max_bw; +}; + struct rtl8xxxu_priv { struct ieee80211_hw *hw; struct usb_device *udev; struct rtl8xxxu_fileops *fops; + struct rtl8xxxu_hw_feature hw_feature; + spinlock_t tx_urb_lock; struct list_head tx_urb_free_list; int tx_urb_free_count; @@ -2009,6 +2015,7 @@ struct rtl8xxxu_fileops { u8 init_reg_pkt_life_time:1; u8 init_reg_hmtfr:1; u8 supports_concurrent:1; + u8 hw_feature_report:1; u8 ampdu_max_time; u8 ustime_tsf_edca; u16 max_aggr_num; From 7284f5be5d298c901be1fc9fda6a2476f5ffdbaf Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:19 +0800 Subject: [PATCH 044/105] wifi: rtw89: mlo: rearrange MLSR link decision flow The original MLSR link decision refers to RSSI, but it should be based on the premise of an existing link. Otherwise, make a link decision to select a new link from any available band. Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 7e058c071cc4..03d80d012022 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4746,13 +4746,19 @@ static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, { unsigned int sel_link_id = IEEE80211_MLD_MAX_NUM_LINKS; struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + u8 decided_bands = BIT(RTW89_BAND_NUM) - 1; struct rtw89_vif_link *rtwvif_link; const struct rtw89_chan *chan; unsigned long usable_links; unsigned int link_id; - u8 decided_bands; u8 rssi; + usable_links = ieee80211_vif_usable_links(vif); + + rtwvif_link = rtw89_get_designated_link(rtwvif); + if (unlikely(!rtwvif_link)) + goto select; + rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); if (unlikely(!rssi)) return; @@ -4764,12 +4770,6 @@ static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, else return; - usable_links = ieee80211_vif_usable_links(vif); - - rtwvif_link = rtw89_get_designated_link(rtwvif); - if (unlikely(!rtwvif_link)) - goto select; - chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); if (decided_bands & BIT(chan->band_type)) return; From 09d369c66373de8708e442a30460ced17f254915 Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:20 +0800 Subject: [PATCH 045/105] wifi: rtw89: phy: support per PHY RX statistics Previously, RX statistics such as beacon RSSI and packet counters were shared across all PHYs. To support MLO, extend the statistics to be maintained per PHY. Update the debugfs output for phy_info and beacon_info to include a "[PHY X]" label for better clarity. The output of phy_info: TP TX: 0 [0] Mbps (lv: 0), RX: 0 [0] Mbps (lv: 0) Avg packet length: TX=0, RX=120 TF: 0 [PHY 0] Beacon: 19 (-45 dBm) RX count: Legacy: [0, 0, 0, 0] ... EHT 2SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] The output of beacon_info: [PHY 0] Beacon: 20 raw rssi: 131 hw rate: 4 length: 437 [Beacon info] interval: 100 dtim: 1 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 26 ++++-- drivers/net/wireless/realtek/rtw89/core.h | 6 +- drivers/net/wireless/realtek/rtw89/debug.c | 88 ++++++++++++------- drivers/net/wireless/realtek/rtw89/fw.c | 6 +- drivers/net/wireless/realtek/rtw89/phy.c | 17 ++-- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 5 +- 6 files changed, 97 insertions(+), 51 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 03d80d012022..81f3ae21dc18 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2736,12 +2736,14 @@ static u16 rtw89_bcn_get_histogram_bound(struct rtw89_dev *rtwdev, u8 target) } static u16 rtw89_bcn_get_rx_time(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan) + struct rtw89_vif_link *rtwvif_link) { #define RTW89_SYMBOL_TIME_2GHZ 192 #define RTW89_SYMBOL_TIME_5GHZ 20 #define RTW89_SYMBOL_TIME_6GHZ 20 - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; + 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 rtw89_pkt_stat *pkt_stat = &bb->cur_pkt_stat; u16 bitrate, val; if (!rtw89_legacy_rate_to_bitrate(rtwdev, pkt_stat->beacon_rate, &bitrate)) @@ -2772,15 +2774,15 @@ static void rtw89_bcn_calc_timeout(struct rtw89_dev *rtwdev, #define RTW89_BCN_TRACK_EXTEND_TIMEOUT 5 #define RTW89_BCN_TRACK_COVERAGE_TH 0 /* unit: TU */ #define RTW89_BCN_TRACK_STRONG_RSSI 80 - const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat; struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track; + struct rtw89_pkt_stat *pkt_stat = &bb->cur_pkt_stat; struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist; u16 outlier_high_bcn_th = bcn_track->outlier_high_bcn_th; u16 outlier_low_bcn_th = bcn_track->outlier_low_bcn_th; - u8 rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); u16 target_bcn_th = bcn_track->target_bcn_th; + u8 rssi = ewma_rssi_read(&bb->bcn_rssi); u16 low_bcn_th = bcn_track->low_bcn_th; u16 med_bcn_th = bcn_track->med_bcn_th; u16 beacon_int = bcn_track->beacon_int; @@ -2826,7 +2828,7 @@ static void rtw89_bcn_calc_timeout(struct rtw89_dev *rtwdev, bcn_timeout = bcn_stat->drift[target_bcn_th]; out: - bcn_track->bcn_timeout = bcn_timeout + rtw89_bcn_get_rx_time(rtwdev, chan); + bcn_track->bcn_timeout = bcn_timeout + rtw89_bcn_get_rx_time(rtwdev, rtwvif_link); } static void rtw89_bcn_update_timeout(struct rtw89_dev *rtwdev, @@ -2987,7 +2989,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct rtw89_vif_rx_stats_iter_data *iter_data = data; struct rtw89_dev *rtwdev = iter_data->rtwdev; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; struct rtw89_rx_desc_info *desc_info = iter_data->desc_info; struct sk_buff *skb = iter_data->skb; struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); @@ -2997,6 +2998,8 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct ieee80211_bss_conf *bss_conf; struct rtw89_vif_link *rtwvif_link; const u8 *bssid = iter_data->bssid; + struct rtw89_pkt_stat *pkt_stat; + struct rtw89_bb_ctx *bb; const u8 *target_bssid; if (rtwdev->scanning && @@ -3030,6 +3033,9 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, rx_status->link_id = rtwvif_link->link_id; } + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + pkt_stat = &bb->cur_pkt_stat; + if (ieee80211_is_beacon(hdr->frame_control)) { if (vif->type == NL80211_IFTYPE_STATION && !test_bit(RTW89_FLAG_WOWLAN, rtwdev->flags)) { @@ -3038,7 +3044,7 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, } if (phy_ppdu) { - ewma_rssi_add(&rtwdev->phystat.bcn_rssi, phy_ppdu->rssi_avg); + ewma_rssi_add(&bb->bcn_rssi, phy_ppdu->rssi_avg); if (!test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags)) rtwvif_link->bcn_bw_idx = phy_ppdu->bw_idx; } @@ -4750,6 +4756,7 @@ static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link; const struct rtw89_chan *chan; unsigned long usable_links; + struct rtw89_bb_ctx *bb; unsigned int link_id; u8 rssi; @@ -4759,7 +4766,8 @@ static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, if (unlikely(!rtwvif_link)) goto select; - rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + rssi = ewma_rssi_read(&bb->bcn_rssi); if (unlikely(!rssi)) return; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b290da650c70..f15a0c43ef6d 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5345,9 +5345,6 @@ DECLARE_EWMA(thermal, 4, 4); struct rtw89_phy_stat { struct ewma_thermal avg_thermal[RF_PATH_MAX]; u8 last_thermal_max; - struct ewma_rssi bcn_rssi; - struct rtw89_pkt_stat cur_pkt_stat; - struct rtw89_pkt_stat last_pkt_stat; struct rtw89_beacon_stat bcn_stat; }; @@ -6318,6 +6315,9 @@ struct rtw89_dev { struct rtw89_dig_info dig; struct rtw89_phy_ch_info ch_info; struct rtw89_edcca_bak edcca_bak; + struct ewma_rssi bcn_rssi; + struct rtw89_pkt_stat cur_pkt_stat; + struct rtw89_pkt_stat last_pkt_stat; } bbs[RTW89_PHY_NUM]; struct wiphy_delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 2cb6e441b2f8..2d953bec149b 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4051,38 +4051,20 @@ static const struct rtw89_rx_rate_cnt_info { {FIRST_RATE_GEV1(EHT_NSS2_MCS0), 14, 0, "EHT 2SS:"}, }; -static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, - struct rtw89_debugfs_priv *debugfs_priv, - char *buf, size_t bufsz) +static int rtw89_get_rx_pkt_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) { - struct rtw89_traffic_stats *stats = &rtwdev->stats; - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat; + struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; const struct rtw89_chip_info *chip = rtwdev->chip; - struct rtw89_debugfs_iter_data iter_data; const struct rtw89_rx_rate_cnt_info *info; - struct rtw89_hal *hal = &rtwdev->hal; + u8 rssi = ewma_rssi_read(&bb->bcn_rssi); char *p = buf, *end = buf + bufsz; enum rtw89_hw_rate first_rate; - u8 rssi; int i; - rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); - - p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps (lv: %d", - stats->tx_throughput, stats->tx_throughput_raw, - stats->tx_tfc_lv); - if (hal->thermal_prot_lv) - p += scnprintf(p, end - p, ", duty: %d%%", - 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); - p += scnprintf(p, end - p, "), RX: %u [%u] Mbps (lv: %d)\n", - stats->rx_throughput, stats->rx_throughput_raw, - stats->rx_tfc_lv); - p += scnprintf(p, end - p, "Beacon: %u (%d dBm), TF: %u\n", + p += scnprintf(p, end - p, "Beacon: %u (%d dBm)\n", pkt_stat->beacon_nr, - RTW89_RSSI_RAW_TO_DBM(rssi), stats->rx_tf_periodic); - p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", - stats->tx_avg_len, - stats->rx_avg_len); + RTW89_RSSI_RAW_TO_DBM(rssi)); p += scnprintf(p, end - p, "RX count:\n"); @@ -4103,6 +4085,39 @@ static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, p += scnprintf(p, end - p, "]\n"); } + return p - buf; +} + +static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_traffic_stats *stats = &rtwdev->stats; + struct rtw89_debugfs_iter_data iter_data; + struct rtw89_hal *hal = &rtwdev->hal; + char *p = buf, *end = buf + bufsz; + struct rtw89_bb_ctx *bb; + + p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps (lv: %d", + stats->tx_throughput, stats->tx_throughput_raw, + stats->tx_tfc_lv); + if (hal->thermal_prot_lv) + p += scnprintf(p, end - p, ", duty: %d%%", + 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); + p += scnprintf(p, end - p, "), RX: %u [%u] Mbps (lv: %d)\n", + stats->rx_throughput, stats->rx_throughput_raw, + stats->rx_tfc_lv); + p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", + stats->tx_avg_len, + stats->rx_avg_len); + p += scnprintf(p, end - p, "TF: %u\n", stats->rx_tf_periodic); + + rtw89_for_each_active_bb(rtwdev, bb) { + p += scnprintf(p, end - p, "\n[PHY %u]\n", bb->phy_idx); + p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); + } + p += scnprintf(p, end - p, "\n"); + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, &iter_data); p += iter_data.written_sz; @@ -4858,12 +4873,26 @@ rtw89_debug_priv_diag_mac_get(struct rtw89_dev *rtwdev, return rtw89_mac_diag_iter_all(rtwdev, buf, bufsz); } +static int rtw89_get_beacon_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) +{ + struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; + char *p = buf, *end = buf + bufsz; + + p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx); + p += scnprintf(p, end - p, "Beacon: %u\n", pkt_stat->beacon_nr); + p += scnprintf(p, end - p, "raw rssi: %lu\n", ewma_rssi_read(&bb->bcn_rssi)); + p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate); + p += scnprintf(p, end - p, "length: %u\n\n", pkt_stat->beacon_len); + + return p - buf; +} + static ssize_t rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, struct rtw89_debugfs_priv *debugfs_priv, char *buf, size_t bufsz) { - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat; struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track; struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat; struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist; @@ -4871,17 +4900,16 @@ rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, char *p = buf, *end = buf + bufsz; u16 *drift = bcn_stat->drift; u8 bcn_num = bcn_stat->num; + struct rtw89_bb_ctx *bb; u8 count; u8 i; + rtw89_for_each_active_bb(rtwdev, bb) + p += rtw89_get_beacon_info(rtwdev, bb, p, end - p); + p += scnprintf(p, end - p, "[Beacon info]\n"); - p += scnprintf(p, end - p, "count: %u\n", pkt_stat->beacon_nr); p += scnprintf(p, end - p, "interval: %u\n", bcn_track->beacon_int); p += scnprintf(p, end - p, "dtim: %u\n", bcn_track->dtim); - p += scnprintf(p, end - p, "raw rssi: %lu\n", - ewma_rssi_read(&rtwdev->phystat.bcn_rssi)); - p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate); - p += scnprintf(p, end - p, "length: %u\n", pkt_stat->beacon_len); p += scnprintf(p, end - p, "\n[Distribution]\n"); p += scnprintf(p, end - p, "tbtt\n"); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index b5d2540cf212..2a3662ed733f 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3223,14 +3223,15 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be; - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; static const u8 bcn_bw_ofst[] = {0, 0, 0, 3, 6, 9, 0, 12}; const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_efuse *efuse = &rtwdev->efuse; struct rtw89_h2c_lps_ml_cmn_info *h2c; struct rtw89_vif_link *rtwvif_link; + struct rtw89_pkt_stat *pkt_stat; const struct rtw89_chan *chan; u8 bw_idx = RTW89_BB_BW_20_40; + struct rtw89_bb_ctx *bb; u32 len = sizeof(*h2c); unsigned int link_id; struct sk_buff *skb; @@ -3261,11 +3262,14 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, path = rtwvif_link->phy_idx == RTW89_PHY_1 ? RF_PATH_B : RF_PATH_A; chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); gain_band = rtw89_subband_to_gain_band_be(chan->subband_type); + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); h2c->central_ch[rtwvif_link->phy_idx] = chan->channel; h2c->pri_ch[rtwvif_link->phy_idx] = chan->primary_channel; h2c->band[rtwvif_link->phy_idx] = chan->band_type; h2c->bw[rtwvif_link->phy_idx] = chan->band_width; + + pkt_stat = &bb->cur_pkt_stat; if (pkt_stat->beacon_rate < RTW89_HW_RATE_OFDM6) h2c->bcn_rate_type[rtwvif_link->phy_idx] = 0x1; else diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 95b5ace2a27e..29770cc5ea60 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5814,6 +5814,7 @@ static void rtw89_phy_stat_rssi_update(struct rtw89_dev *rtwdev) static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) { struct rtw89_phy_stat *phystat = &rtwdev->phystat; + struct rtw89_bb_ctx *bb; int i; for (i = 0; i < rtwdev->chip->rf_path_num; i++) @@ -5821,24 +5822,28 @@ static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) rtw89_phy_stat_thermal_update(rtwdev); - memset(&phystat->cur_pkt_stat, 0, sizeof(phystat->cur_pkt_stat)); - memset(&phystat->last_pkt_stat, 0, sizeof(phystat->last_pkt_stat)); + rtw89_for_each_capab_bb(rtwdev, bb) { + memset(&bb->cur_pkt_stat, 0, sizeof(bb->cur_pkt_stat)); + memset(&bb->last_pkt_stat, 0, sizeof(bb->last_pkt_stat)); - ewma_rssi_init(&phystat->bcn_rssi); + ewma_rssi_init(&bb->bcn_rssi); + } rtwdev->hal.thermal_prot_lv = 0; } void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) { - struct rtw89_phy_stat *phystat = &rtwdev->phystat; + struct rtw89_bb_ctx *bb; rtw89_phy_stat_thermal_update(rtwdev); rtw89_phy_thermal_protect(rtwdev); rtw89_phy_stat_rssi_update(rtwdev); - phystat->last_pkt_stat = phystat->cur_pkt_stat; - memset(&phystat->cur_pkt_stat, 0, sizeof(phystat->cur_pkt_stat)); + 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)); + } } static u16 rtw89_phy_ccx_us_to_idx(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 9cd4c88ee57e..b81eeb59be60 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2158,13 +2158,14 @@ static void rtw8852a_query_ppdu(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu, struct ieee80211_rx_status *status) { - u8 path; + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, phy_ppdu->phy_idx); u8 *rx_power = phy_ppdu->rssi; + u8 path; u8 raw; if (!status->signal) { if (phy_ppdu->to_self) - raw = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); + raw = ewma_rssi_read(&bb->bcn_rssi); else raw = max(rx_power[RF_PATH_A], rx_power[RF_PATH_B]); From 523d012f47e7371b653692cdff1fa2eab58fe3cb Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:21 +0800 Subject: [PATCH 046/105] wifi: rtw89: debug: bb_info entry including TX rate count for WiFi 7 chips Enhance TX performance visibility for WiFi 7 chips by introducing TX rate count tracking. This is critical for debugging and validation. Additionally, introduce a new debugfs bb_info to enable and provide baseband status. Usage of bb_info debugfs: $ echo enable 1 > bb_info // Start logging BB statistics information $ echo mac_id 0 > bb_info // Specify mac_id for TX rate count tracking The output of bb_info: TP TX: 0 [0] Mbps, RX: 0 [0] Mbps Avg packet length: TX=118, RX=136 TF: 0 TX count [0]: Legacy: [0, 0, 0, 0] OFDM: [0, 0, 0, 0, 0, 0, 0, 0] MCS 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] MCS 2SS: [183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [PHY 0] == RSSI/RX Rate Beacon: 19 (-41 dBm) RX count: Legacy: [0, 0, 0, 0] OFDM: [0, 0, 0, 0, 0, 0, 0, 0] HT 0: [0, 0, 0, 0, 0, 0, 0, 0] HT 1: [0, 0, 0, 0, 0, 0, 0, 0] VHT 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0][0, 0] VHT 2SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0][0, 0] HE 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] HE 2SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] EHT 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0][0, 0] EHT 2SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185] TX rate [0, 2]: EHT 2SS MCS-0 GI:0.8 FB_G BW:160 (hw_rate=0x420) ==> agg_wait=-1 (1) RX rate [0, 2]: EHT 2SS MCS-13 GI:0.8 BW:160 (hw_rate=0x42d) RSSI: -43 dBm (raw=134, prev=135) [-43, -44] EVM: [38.75, (41.50, 43.00)] SNR: 39 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 + drivers/net/wireless/realtek/rtw89/core.h | 13 ++ drivers/net/wireless/realtek/rtw89/debug.c | 110 +++++++++++++++- drivers/net/wireless/realtek/rtw89/fw.c | 122 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 35 +++++ drivers/net/wireless/realtek/rtw89/mac80211.c | 11 +- drivers/net/wireless/realtek/rtw89/phy.c | 29 +++++ drivers/net/wireless/realtek/rtw89/phy.h | 1 + 8 files changed, 314 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 81f3ae21dc18..d8f83623e54a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -6613,6 +6613,8 @@ int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, if (RTW89_CHK_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, &rtwdev->fw)) rtw89_chip_rfk_channel(rtwdev, target); + rtw89_fw_h2c_tx_history(rtwdev, target->mac_id); + rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; wake_queue: diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f15a0c43ef6d..c4396f0100be 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4511,6 +4511,15 @@ struct rtw89_antdiv_info { bool get_stats; }; +struct rtw89_bb_stat_cfg { + bool enable; + u16 mac_id; +}; + +struct rtw89_phy_info { + struct rtw89_bb_stat_cfg bb_stat_cfg; +}; + enum rtw89_chanctx_state { RTW89_CHANCTX_STATE_MCC_START, RTW89_CHANCTX_STATE_MCC_STOP, @@ -4831,6 +4840,7 @@ enum rtw89_fw_feature { RTW89_FW_FEATURE_SIM_SER_L0L1_BY_HALT_H2C, RTW89_FW_FEATURE_LPS_ML_INFO_V1, RTW89_FW_FEATURE_SER_POST_RECOVER_DMAC, + RTW89_FW_FEATURE_TX_HISTORY_V1, NUM_OF_RTW89_FW_FEATURES, }; @@ -5342,9 +5352,11 @@ struct rtw89_beacon_stat { DECLARE_EWMA(thermal, 4, 4); +#define RTW89_TX_RATE_NR 40 struct rtw89_phy_stat { struct ewma_thermal avg_thermal[RF_PATH_MAX]; u8 last_thermal_max; + u32 tx_rate_cnt[RTW89_TX_RATE_NR]; struct rtw89_beacon_stat bcn_stat; }; @@ -6308,6 +6320,7 @@ struct rtw89_dev { struct rtw89_phy_efuse_gain efuse_gain; struct rtw89_phy_ul_tb_info ul_tb_info; struct rtw89_antdiv_info antdiv; + struct rtw89_phy_info phy_info; struct rtw89_bb_ctx { enum rtw89_phy_idx phy_idx; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 2d953bec149b..b82b13645fb0 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -85,6 +85,7 @@ struct rtw89_debugfs { struct rtw89_debugfs_priv btc_manual; struct rtw89_debugfs_priv fw_log_manual; struct rtw89_debugfs_priv phy_info; + struct rtw89_debugfs_priv bb_info; struct rtw89_debugfs_priv stations; struct rtw89_debugfs_priv disable_dm; struct rtw89_debugfs_priv static_pd_th; @@ -4016,15 +4017,15 @@ static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta) } static int -rtw89_debug_append_rx_rate(char *buf, size_t bufsz, struct rtw89_pkt_stat *pkt_stat, - enum rtw89_hw_rate first_rate, int len) +rtw89_debug_append_rate(char *buf, size_t bufsz, const u32 *rate_cnt, + int first_rate, int len) { char *p = buf, *end = buf + bufsz; int i; for (i = 0; i < len; i++) p += scnprintf(p, end - p, "%s%u", i == 0 ? "" : ", ", - pkt_stat->rx_rate_cnt[first_rate + i]); + rate_cnt[first_rate + i]); return p - buf; } @@ -4051,6 +4052,17 @@ static const struct rtw89_rx_rate_cnt_info { {FIRST_RATE_GEV1(EHT_NSS2_MCS0), 14, 0, "EHT 2SS:"}, }; +static const struct rtw89_tx_rate_cnt_info { + int first_rate; + int len; + const char *rate_mode; +} rtw89_tx_rate_cnt_infos[] = { + {0, 4, "Legacy:"}, + {4, 8, "OFDM:"}, + {12, 14, "MCS 1SS:"}, + {26, 14, "MCS 2SS:"}, +}; + static int rtw89_get_rx_pkt_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, char *buf, size_t bufsz) { @@ -4075,12 +4087,13 @@ static int rtw89_get_rx_pkt_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx * continue; p += scnprintf(p, end - p, "%10s [", info->rate_mode); - p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, - first_rate, info->len); + p += rtw89_debug_append_rate(p, end - p, pkt_stat->rx_rate_cnt, + first_rate, info->len); if (info->ext) { p += scnprintf(p, end - p, "]["); - p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, - first_rate + info->len, info->ext); + p += rtw89_debug_append_rate(p, end - p, pkt_stat->rx_rate_cnt, + first_rate + info->len, + info->ext); } p += scnprintf(p, end - p, "]\n"); } @@ -4125,6 +4138,87 @@ static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, return p - buf; } +static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) +{ + char *p = buf, *end = buf + bufsz; + + p += scnprintf(p, end - p, "\n[PHY %u]\n", bb->phy_idx); + + p += scnprintf(p, end - p, "== RSSI/RX Rate\n"); + p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_bb_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg; + struct rtw89_traffic_stats *stats = &rtwdev->stats; + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_tx_rate_cnt_info *info; + struct rtw89_debugfs_iter_data iter_data; + char *p = buf, *end = buf + bufsz; + struct rtw89_bb_ctx *bb; + int i; + + p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps, RX: %u [%u] Mbps\n", + stats->tx_throughput, stats->tx_throughput_raw, + stats->rx_throughput, stats->rx_throughput_raw); + p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", + stats->tx_avg_len, + stats->rx_avg_len); + p += scnprintf(p, end - p, "TF: %u\n", stats->rx_tf_periodic); + + if (chip->chip_gen != RTW89_CHIP_AX) { + p += scnprintf(p, end - p, + "TX count [0x%x]:\n", bb_stat->mac_id); + + for (i = 0; i < ARRAY_SIZE(rtw89_tx_rate_cnt_infos); i++) { + info = &rtw89_tx_rate_cnt_infos[i]; + + p += scnprintf(p, end - p, "%10s [", info->rate_mode); + p += rtw89_debug_append_rate(p, end - p, + rtwdev->phystat.tx_rate_cnt, + info->first_rate, info->len); + p += scnprintf(p, end - p, "]\n"); + } + } + + rtw89_for_each_active_bb(rtwdev, bb) + p += rtw89_get_bb_stat(rtwdev, bb, p, end - p); + p += scnprintf(p, end - p, "\n"); + + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, &iter_data); + p += iter_data.written_sz; + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_bb_info_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) +{ + struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg; + int val; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (sscanf(buf, "enable %d", &val) == 1) + bb_stat->enable = !!val; + else if (sscanf(buf, "mac_id %x", &val) == 1) + rtw89_fw_h2c_tx_history(rtwdev, val); + else + return -EINVAL; + + return count; +} + static int rtw89_dump_addr_cam(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, struct rtw89_addr_cam_entry *addr_cam) @@ -5000,6 +5094,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = { .btc_manual = rtw89_debug_priv_set(btc_manual), .fw_log_manual = rtw89_debug_priv_set(fw_log_manual, WLOCK), .phy_info = rtw89_debug_priv_get(phy_info), + .bb_info = rtw89_debug_priv_set_and_get(bb_info, RWLOCK), .stations = rtw89_debug_priv_get(stations, RLOCK), .disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK), .static_pd_th = rtw89_debug_priv_set_and_get(static_pd_th, RWLOCK), @@ -5049,6 +5144,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_w(btc_manual); rtw89_debugfs_add_w(fw_log_manual); rtw89_debugfs_add_r(phy_info); + rtw89_debugfs_add_rw(bb_info); rtw89_debugfs_add_r(stations); rtw89_debugfs_add_rw(disable_dm); rtw89_debugfs_add_rw(static_pd_th); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 2a3662ed733f..ff3914a16b81 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -930,6 +930,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __DIS_FW_FEAT(RTL8922A, ge, 0, 35, 84, 0, WITH_RFK_PRE_NOTIFY, G), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 84, 0, RFK_PRE_NOTIFY_MCC_V1), __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 84, 0, ADDR_CAM_V0), + __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 92, 0, TX_HISTORY_V1), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 97, 0, SIM_SER_L0L1_BY_HALT_H2C), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 100, 0, SER_POST_RECOVER_DMAC), }; @@ -5588,6 +5589,127 @@ int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi return ret; } +int rtw89_fw_h2c_tx_history(struct rtw89_dev *rtwdev, u16 mac_id) +{ + struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_ra_tx_history *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + if (chip->chip_gen == RTW89_CHIP_AX) + return 0; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c tx history\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_ra_tx_history *)skb->data; + + h2c->w0 = le32_encode_bits(mac_id, RTW89_H2C_RA_TX_HISTORY_W0_MACID) | + le32_encode_bits(0, RTW89_H2C_RA_TX_HISTORY_W0_PER_PPDU); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA, + H2C_FUNC_OUTSRC_RA_TX_HISTORY, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + bb_stat->mac_id = mac_id; + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +int rtw89_fw_h2c_phy_ch_rpt(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_ra_phy_ch_rpt *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + if (chip->chip_gen == RTW89_CHIP_AX) + return 0; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c phy ch rpt\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_ra_phy_ch_rpt *)skb->data; + + h2c->w1 = le32_encode_bits(1, RTW89_H2C_RA_PHY_CH_RPT_W1_RPT_TX_COUNT); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA, + H2C_FUNC_OUTSRC_RA_PHY_CH_RPT, 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_drv_ctrl_fw(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_ra_drv_ctrl_fw *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + if (chip->chip_gen == RTW89_CHIP_AX) + return 0; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c drv ctrl fw\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_ra_drv_ctrl_fw *)skb->data; + + h2c->w0 = le32_encode_bits(1, RTW89_H2C_RA_DRV_CTRL_FW_W0_RPT_TX_COUNT); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA, + H2C_FUNC_OUTSRC_RA_DRV_CTRL_FW, 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_init(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 6ef53fcd0cce..cde8fd34723b 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -471,6 +471,28 @@ struct rtw89_h2c_ra_v1 { #define RTW89_H2C_RA_V1_W4_RAMASK_UHL16 GENMASK(31, 16) #define RTW89_H2C_RA_V1_W5_RAMASK_UHH16 GENMASK(15, 0) +struct rtw89_h2c_ra_tx_history { + __le32 w0; +} __packed; + +#define RTW89_H2C_RA_TX_HISTORY_W0_MACID GENMASK(15, 0) +#define RTW89_H2C_RA_TX_HISTORY_W0_PER_PPDU GENMASK(23, 16) + +struct rtw89_h2c_ra_phy_ch_rpt { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 w3; +} __packed; + +#define RTW89_H2C_RA_PHY_CH_RPT_W1_RPT_TX_COUNT BIT(10) + +struct rtw89_h2c_ra_drv_ctrl_fw { + __le32 w0; +} __packed; + +#define RTW89_H2C_RA_DRV_CTRL_FW_W0_RPT_TX_COUNT BIT(13) + static inline void RTW89_SET_FWCMD_SEC_IDX(void *cmd, u32 val) { le32p_replace_bits((__le32 *)(cmd) + 0x00, val, GENMASK(7, 0)); @@ -3973,6 +3995,13 @@ struct rtw89_c2h_lps_rpt { */ } __packed; +struct rtw89_c2h_ra_tx_history { + struct rtw89_c2h_hdr hdr; + __le32 ra_tbtt_cnt; + __le32 tx_rate_tot_cnt_hist[RTW89_TX_RATE_NR]; + __le32 tx_cat_cnt[3]; +} __packed; + struct rtw89_c2h_fw_scan_rpt { struct rtw89_c2h_hdr hdr; u8 phy_idx; @@ -4771,6 +4800,9 @@ enum rtw89_mrc_h2c_func { #define H2C_CL_OUTSRC_RA 0x1 #define H2C_FUNC_OUTSRC_RA_MACIDCFG 0x0 +#define H2C_FUNC_OUTSRC_RA_TX_HISTORY 0x9 +#define H2C_FUNC_OUTSRC_RA_PHY_CH_RPT 0xe +#define H2C_FUNC_OUTSRC_RA_DRV_CTRL_FW 0xf #define H2C_CL_OUTSRC_DM 0x2 #define H2C_FUNC_FW_MCC_DIG 0x6 @@ -5349,6 +5381,9 @@ int rtw89_fw_h2c_rssi_offload(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu); int rtw89_fw_h2c_tp_offload(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi); +int rtw89_fw_h2c_phy_ch_rpt(struct rtw89_dev *rtwdev); +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_role(struct rtw89_dev *rtwdev, u8 type); diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 9ee2aa225976..b72f6661fbd1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -721,14 +721,21 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_MLD_VALID_LINKS) { struct rtw89_vif_link *cur = rtw89_get_designated_link(rtwvif); + u16 mac_id; if (RTW89_CHK_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, &rtwdev->fw)) rtw89_chip_rfk_channel(rtwdev, cur); - if (hweight16(vif->active_links) == 1) + if (hweight16(vif->active_links) == 1) { + mac_id = cur->mac_id; rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; - else + } else { + /* Specify for all MAC ID */ + mac_id = 0xffff; rtwvif->mlo_mode = RTW89_MLO_MODE_EMLSR; + } + + rtw89_fw_h2c_tx_history(rtwdev, mac_id); } } diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 29770cc5ea60..3124a99d5381 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -3379,12 +3379,24 @@ rtw89_phy_c2h_ra_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) &ra_data); } +static void +rtw89_phy_c2h_tx_history(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + const struct rtw89_c2h_ra_tx_history *history = (const void *)c2h->data; + u32 *tx_rate_cnt = rtwdev->phystat.tx_rate_cnt; + u32 i; + + for (i = 0; i < RTW89_TX_RATE_NR; i++) + tx_rate_cnt[i] = le32_to_cpu(history->tx_rate_tot_cnt_hist[i]); +} + static void (* const rtw89_phy_c2h_ra_handler[])(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) = { [RTW89_PHY_C2H_FUNC_STS_RPT] = rtw89_phy_c2h_ra_rpt, [RTW89_PHY_C2H_FUNC_MU_GPTBL_RPT] = NULL, [RTW89_PHY_C2H_FUNC_TXSTS] = NULL, + [RTW89_PHY_C2H_FUNC_TX_HISTORY] = rtw89_phy_c2h_tx_history, [RTW89_PHY_C2H_FUNC_ACCELERATE_EN] = rtw89_fw_c2h_dummy_handler, }; @@ -5832,6 +5844,22 @@ static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) rtwdev->hal.thermal_prot_lv = 0; } +static void rtw89_phy_trigger_tx_count(struct rtw89_dev *rtwdev) +{ + if (RTW89_CHK_FW_FEATURE(TX_HISTORY_V1, &rtwdev->fw)) + rtw89_fw_h2c_phy_ch_rpt(rtwdev); + else + rtw89_fw_h2c_drv_ctrl_fw(rtwdev); +} + +static void rtw89_phy_stat_update(struct rtw89_dev *rtwdev) +{ + if (!rtwdev->phy_info.bb_stat_cfg.enable) + return; + + rtw89_phy_trigger_tx_count(rtwdev); +} + void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) { struct rtw89_bb_ctx *bb; @@ -5839,6 +5867,7 @@ void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) rtw89_phy_stat_thermal_update(rtwdev); rtw89_phy_thermal_protect(rtwdev); rtw89_phy_stat_rssi_update(rtwdev); + rtw89_phy_stat_update(rtwdev); rtw89_for_each_active_bb(rtwdev, bb) { bb->last_pkt_stat = bb->cur_pkt_stat; diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 3f9d306ff1ca..c6761cedc5a5 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -139,6 +139,7 @@ enum rtw89_phy_c2h_ra_func { RTW89_PHY_C2H_FUNC_STS_RPT, RTW89_PHY_C2H_FUNC_MU_GPTBL_RPT, RTW89_PHY_C2H_FUNC_TXSTS, + RTW89_PHY_C2H_FUNC_TX_HISTORY = 0x4, RTW89_PHY_C2H_FUNC_ACCELERATE_EN = 0x7, RTW89_PHY_C2H_FUNC_RA_NUM, From 3b851f36c3f1f6f182eca64cfb3cc7b065ed4039 Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:22 +0800 Subject: [PATCH 047/105] wifi: rtw89: debug: add PMAC counter in bb_info PMAC (Pseudo MAC) is a circuit within the baseband that can report various packet-related counters through registers, such as TX ON, TX EN, CCA, FA, CRC, etc. The driver periodically collects per PHY PMAC counters in track_work and exposes them through the bb_info debugfs for easier debugging. The output of PMAC counter: == PMAC TX [CCK_TXEN, CCK_TXON, OFDM_TXEN, OFDM_TXON]: [0, 0, 17, 17] CRC [CCK, OFDM, HT, VHT, HE, EHT, ALL, MPDU] ok: [0, 301, 0, 0, 5, 0, 306, 5] err: [0, 4, 0, 0, 0, 0, 4, 0] CCA [CCK, OFDM]: [0, 353] FA [CCK, OFDM]: [0, 39] CCA spoofing [CCK, OFDM]: [0, 0] CCK SFD: 0, SIG_GG: 0 OFDM Parity: 4, Rate: 2, LSIG_BRK_S: 0, LSIG_BRK_L: 7, SBD: 5 AMPDU miss: 0 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 87 ++++++++ drivers/net/wireless/realtek/rtw89/debug.c | 33 +++ drivers/net/wireless/realtek/rtw89/phy.c | 195 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/reg.h | 127 ++++++++++++ drivers/net/wireless/realtek/rtw89/rtw8851b.c | 49 +++++ drivers/net/wireless/realtek/rtw89/rtw8852a.c | 49 +++++ drivers/net/wireless/realtek/rtw89/rtw8852b.c | 49 +++++ .../net/wireless/realtek/rtw89/rtw8852bt.c | 49 +++++ drivers/net/wireless/realtek/rtw89/rtw8852c.c | 49 +++++ drivers/net/wireless/realtek/rtw89/rtw8922a.c | 49 +++++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 49 +++++ 11 files changed, 785 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c4396f0100be..7f869a339ee6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4487,6 +4487,54 @@ struct rtw89_edcca_regs { u32 tx_collision_t2r_st_mask; }; +struct rtw89_pmac_regs { + struct rtw89_reg_def cck_txon; + struct rtw89_reg_def cck_txen; + struct rtw89_reg_def cck_cca; + struct rtw89_reg_def cck_sfd_gg; + struct rtw89_reg_def cck_sig_gg; + struct rtw89_reg_def cck_spoofing; + struct rtw89_reg_def cck_brk; + struct rtw89_reg_def brk; + struct rtw89_reg_def brk_option; + struct rtw89_reg_def search_fail; + struct rtw89_reg_def lsig_brk_s_th; + struct rtw89_reg_def lsig_brk_l_th; + struct rtw89_reg_def rxl_err_parity; + struct rtw89_reg_def rxl_err_rate; + struct rtw89_reg_def ofdm_cca; + struct rtw89_reg_def cca_spoofing; + struct rtw89_reg_def ampdu_miss; + struct rtw89_reg_def r1b_rx_rpt_rst; + struct rtw89_reg_def r1b_rr_sel; + struct rtw89_reg_def enable_all_cnt; + struct rtw89_reg_def rst_all_cnt; + u32 cck_crc32; + u32 cck_crc32_ok_mask; + u32 cck_crc32_fail_mask; + u32 ofdm_txon; + u32 ofdm_txon_mask; + u32 ofdm_txen_mask; + u32 l_crc; + u32 l_crc_ok_mask; + u32 l_crc_err_mask; + u32 ht_crc; + u32 ht_crc_ok_mask; + u32 ht_crc_err_mask; + u32 vht_crc; + u32 vht_crc_ok_mask; + u32 vht_crc_err_mask; + u32 he_crc; + u32 he_crc_ok_mask; + u32 he_crc_err_mask; + u32 eht_crc; + u32 eht_crc_ok_mask; + u32 eht_crc_err_mask; + u32 ampdu_crc; + u32 ampdu_crc_ok_mask; + u32 ampdu_crc_err_mask; +}; + struct rtw89_phy_ul_tb_info { bool dyn_tb_tri_en; u8 def_if_bandedge; @@ -4682,6 +4730,7 @@ struct rtw89_chip_info { struct rtw89_sb_regs btc_sb; u32 dma_ch_mask; const struct rtw89_edcca_regs *edcca_regs; + const struct rtw89_pmac_regs *pmac_regs; const struct wiphy_wowlan_support *wowlan_stub; const struct rtw89_xtal_info *xtal_info; unsigned long default_quirks; /* bitmap of rtw89_quirks */ @@ -5505,6 +5554,43 @@ struct rtw89_phy_ch_info { u8 is_noisy; }; +struct rtw89_pmac_stat_info { + u32 cck_phy_txon; + u32 cck_mac_txen; + u32 ofdm_mac_txen; + u32 ofdm_phy_txon; + u32 cnt_ofdm_cca; + u32 cnt_cck_cca; + u32 cnt_cck_spoofing; + u32 cnt_ofdm_spoofing; + u32 cnt_ampdu_miss; + u32 cnt_ampdu_crc_error; + u32 cnt_ampdu_crc_ok; + u32 cnt_cck_crc32_error; + u32 cnt_cck_crc32_ok; + u32 cnt_ofdm_crc32_error; + u32 cnt_ofdm_crc32_ok; + u32 cnt_ht_crc32_error; + u32 cnt_ht_crc32_ok; + u32 cnt_vht_crc32_error; + u32 cnt_vht_crc32_ok; + u32 cnt_he_crc32_ok; + u32 cnt_he_crc32_error; + u32 cnt_eht_crc32_ok; + u32 cnt_eht_crc32_error; + u32 cnt_crc32_error_all; + u32 cnt_crc32_ok_all; + u32 cnt_sfd_gg; + u32 cnt_sig_gg; + u32 cnt_cck_fail; + u32 cnt_ofdm_fail; + u32 cnt_lsig_brk_s_th; + u32 cnt_lsig_brk_l_th; + u32 cnt_parity_fail; + u32 cnt_rate_illegal; + u32 cnt_sb_search_fail; +}; + struct rtw89_agc_gaincode_set { u8 lna_idx; u8 tia_idx; @@ -6331,6 +6417,7 @@ struct rtw89_dev { struct ewma_rssi bcn_rssi; struct rtw89_pkt_stat cur_pkt_stat; struct rtw89_pkt_stat last_pkt_stat; + struct rtw89_pmac_stat_info pmac_stat; } bbs[RTW89_PHY_NUM]; struct wiphy_delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index b82b13645fb0..d8a183bfc6da 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4141,10 +4141,43 @@ static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, char *buf, size_t bufsz) { + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; char *p = buf, *end = buf + bufsz; p += scnprintf(p, end - p, "\n[PHY %u]\n", bb->phy_idx); + p += scnprintf(p, end - p, "== PMAC\n"); + p += scnprintf(p, end - p, + "TX [CCK_TXEN, CCK_TXON, OFDM_TXEN, OFDM_TXON]: [%d, %d, %d, %d]\n", + pmac->cck_mac_txen, pmac->cck_phy_txon, + pmac->ofdm_mac_txen, pmac->ofdm_phy_txon); + p += scnprintf(p, end - p, "CRC [CCK, OFDM, HT, VHT, HE, EHT, ALL, MPDU]\n"); + p += scnprintf(p, end - p, " ok: [%d, %d, %d, %d, %d, %d, %d, %d]\n", + pmac->cnt_cck_crc32_ok, pmac->cnt_ofdm_crc32_ok, + pmac->cnt_ht_crc32_ok, pmac->cnt_vht_crc32_ok, + pmac->cnt_he_crc32_ok, pmac->cnt_eht_crc32_ok, + pmac->cnt_crc32_ok_all, pmac->cnt_ampdu_crc_ok); + p += scnprintf(p, end - p, "err: [%d, %d, %d, %d, %d, %d, %d, %d]\n", + pmac->cnt_cck_crc32_error, pmac->cnt_ofdm_crc32_error, + pmac->cnt_ht_crc32_error, pmac->cnt_vht_crc32_error, + pmac->cnt_he_crc32_error, pmac->cnt_eht_crc32_error, + pmac->cnt_crc32_error_all, pmac->cnt_ampdu_crc_error); + p += scnprintf(p, end - p, "CCA [CCK, OFDM]: [%d, %d]\n", + pmac->cnt_cck_cca, pmac->cnt_ofdm_cca); + p += scnprintf(p, end - p, "FA [CCK, OFDM]: [%d, %d]\n", + pmac->cnt_cck_fail, pmac->cnt_ofdm_fail); + + p += scnprintf(p, end - p, "CCA spoofing [CCK, OFDM]: [%d, %d]\n", + pmac->cnt_cck_spoofing, pmac->cnt_ofdm_spoofing); + p += scnprintf(p, end - p, "CCK SFD: %d, SIG_GG: %d\n", + pmac->cnt_sfd_gg, pmac->cnt_sig_gg); + p += scnprintf(p, end - p, + "OFDM Parity: %d, Rate: %d, LSIG_BRK_S: %d, LSIG_BRK_L: %d, SBD: %d\n", + pmac->cnt_parity_fail, pmac->cnt_rate_illegal, + pmac->cnt_lsig_brk_s_th, pmac->cnt_lsig_brk_l_th, + pmac->cnt_sb_search_fail); + p += scnprintf(p, end - p, "AMPDU miss: %d\n\n", pmac->cnt_ampdu_miss); + p += scnprintf(p, end - p, "== RSSI/RX Rate\n"); p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 3124a99d5381..80d358d80a38 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5844,6 +5844,196 @@ static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) rtwdev->hal.thermal_prot_lv = 0; } +static void rtw89_phy_pmac_stat_reset(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool cck) +{ + const struct rtw89_pmac_regs *regs = rtwdev->chip->pmac_regs; + + if (cck) { + rtw89_phy_write32_clr(rtwdev, regs->r1b_rx_rpt_rst.addr, + regs->r1b_rx_rpt_rst.mask); + rtw89_phy_write32_set(rtwdev, regs->r1b_rx_rpt_rst.addr, + regs->r1b_rx_rpt_rst.mask); + } + + rtw89_phy_write32_idx_set(rtwdev, regs->enable_all_cnt.addr, + regs->enable_all_cnt.mask, bb->phy_idx); + rtw89_phy_write32_idx_set(rtwdev, regs->rst_all_cnt.addr, + regs->rst_all_cnt.mask, bb->phy_idx); + rtw89_phy_write32_idx_clr(rtwdev, regs->rst_all_cnt.addr, + regs->rst_all_cnt.mask, bb->phy_idx); +} + +static void rtw89_phy_pmac_stat_cck(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + const struct rtw89_pmac_regs *regs = rtwdev->chip->pmac_regs; + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 val; + + pmac->cck_phy_txon = + rtw89_phy_read32_mask(rtwdev, regs->cck_txon.addr, regs->cck_txon.mask); + pmac->cck_mac_txen = + rtw89_phy_read32_mask(rtwdev, regs->cck_txen.addr, regs->cck_txen.mask); + + if (chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev)) + rtw89_phy_write32_mask(rtwdev, regs->r1b_rr_sel.addr, + regs->r1b_rr_sel.mask, 0x2); + + if (bb->phy_idx == RTW89_PHY_1) + pmac->cnt_cck_cca = + rtw89_phy_read32_mask(rtwdev, regs->cck_cca.addr + 8, + regs->cck_cca.mask); + else + pmac->cnt_cck_cca = + rtw89_phy_read32_mask(rtwdev, regs->cck_cca.addr, + regs->cck_cca.mask); + + if (chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev)) + rtw89_phy_write32_mask(rtwdev, regs->r1b_rr_sel.addr, + regs->r1b_rr_sel.mask, 0x1); + + if (bb->phy_idx == RTW89_PHY_1) + val = rtw89_phy_read32(rtwdev, regs->cck_crc32 + 8); + else + val = rtw89_phy_read32(rtwdev, regs->cck_crc32); + pmac->cnt_cck_crc32_ok = field_get(regs->cck_crc32_ok_mask, val); + pmac->cnt_cck_crc32_error = field_get(regs->cck_crc32_fail_mask, val); + + pmac->cnt_sfd_gg = + rtw89_phy_read32_idx(rtwdev, regs->cck_sfd_gg.addr, + regs->cck_sfd_gg.mask, bb->phy_idx); + pmac->cnt_sig_gg = + rtw89_phy_read32_idx(rtwdev, regs->cck_sig_gg.addr, + regs->cck_sig_gg.mask, bb->phy_idx); + pmac->cnt_cck_spoofing = + rtw89_phy_read32_idx(rtwdev, regs->cck_spoofing.addr, + regs->cck_spoofing.mask, bb->phy_idx); + + if (chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev)) + pmac->cnt_cck_fail = + pmac->cnt_cck_cca - pmac->cnt_cck_crc32_ok - + pmac->cnt_cck_crc32_error - pmac->cnt_cck_spoofing; + else + pmac->cnt_cck_fail = + rtw89_phy_read32_idx(rtwdev, regs->cck_brk.addr, + regs->cck_brk.mask, bb->phy_idx); +} + +static void rtw89_phy_pmac_stat_ofdm(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + const struct rtw89_pmac_regs *regs = rtwdev->chip->pmac_regs; + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 val; + + val = rtw89_phy_read32_idx(rtwdev, regs->ofdm_txon, MASKDWORD, bb->phy_idx); + pmac->ofdm_phy_txon = field_get(regs->ofdm_txon_mask, val); + pmac->ofdm_mac_txen = field_get(regs->ofdm_txen_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->l_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_ofdm_crc32_ok = field_get(regs->l_crc_ok_mask, val); + pmac->cnt_ofdm_crc32_error = field_get(regs->l_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->ht_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_ht_crc32_ok = field_get(regs->ht_crc_ok_mask, val); + pmac->cnt_ht_crc32_error = field_get(regs->ht_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->vht_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_vht_crc32_ok = field_get(regs->vht_crc_ok_mask, val); + pmac->cnt_vht_crc32_error = field_get(regs->vht_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->he_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_he_crc32_ok = field_get(regs->he_crc_ok_mask, val); + pmac->cnt_he_crc32_error = field_get(regs->he_crc_err_mask, val); + + if (chip->chip_gen == RTW89_CHIP_BE) { + val = rtw89_phy_read32_idx(rtwdev, regs->eht_crc, + MASKDWORD, bb->phy_idx); + pmac->cnt_eht_crc32_ok = field_get(regs->eht_crc_ok_mask, val); + pmac->cnt_eht_crc32_error = field_get(regs->eht_crc_err_mask, val); + } + + val = rtw89_phy_read32_idx(rtwdev, regs->ampdu_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_ampdu_crc_ok = field_get(regs->ampdu_crc_ok_mask, val); + pmac->cnt_ampdu_crc_error = field_get(regs->ampdu_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->brk.addr, regs->brk.mask, bb->phy_idx); + if (chip->chip_id == RTL8852C && + rtw89_phy_read32_idx(rtwdev, regs->brk_option.addr, + regs->brk_option.mask, bb->phy_idx) == 1) { + u32 tmp = pmac->ofdm_phy_txon + pmac->cck_phy_txon; + + val = (val > tmp) ? (val - tmp) : 0; + } + pmac->cnt_ofdm_fail = val; + + pmac->cnt_sb_search_fail = + rtw89_phy_read32_idx(rtwdev, regs->search_fail.addr, + regs->search_fail.mask, bb->phy_idx); + pmac->cnt_lsig_brk_s_th = + rtw89_phy_read32_idx(rtwdev, regs->lsig_brk_s_th.addr, + regs->lsig_brk_s_th.mask, bb->phy_idx); + pmac->cnt_lsig_brk_l_th = + rtw89_phy_read32_idx(rtwdev, regs->lsig_brk_l_th.addr, + regs->lsig_brk_l_th.mask, bb->phy_idx); + pmac->cnt_parity_fail = + rtw89_phy_read32_idx(rtwdev, regs->rxl_err_parity.addr, + regs->rxl_err_parity.mask, bb->phy_idx); + pmac->cnt_rate_illegal = + rtw89_phy_read32_idx(rtwdev, regs->rxl_err_rate.addr, + regs->rxl_err_rate.mask, bb->phy_idx); + pmac->cnt_ofdm_cca = + rtw89_phy_read32_idx(rtwdev, regs->ofdm_cca.addr, + regs->ofdm_cca.mask, bb->phy_idx); + pmac->cnt_ofdm_spoofing = + rtw89_phy_read32_idx(rtwdev, regs->cca_spoofing.addr, + regs->cca_spoofing.mask, bb->phy_idx); + pmac->cnt_ampdu_miss = + rtw89_phy_read32_idx(rtwdev, regs->ampdu_miss.addr, + regs->ampdu_miss.mask, bb->phy_idx); +} + +static void rtw89_phy_pmac_stat_update(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; + bool cck; + + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[bb->phy_idx]; + cck = chan->band_type == RTW89_BAND_2G; + + if (cck) + rtw89_phy_pmac_stat_cck(rtwdev, bb); + + rtw89_phy_pmac_stat_ofdm(rtwdev, bb); + + pmac->cnt_crc32_error_all = pmac->cnt_he_crc32_error + + pmac->cnt_vht_crc32_error + + pmac->cnt_ht_crc32_error + + pmac->cnt_ofdm_crc32_error + + pmac->cnt_cck_crc32_error; + + pmac->cnt_crc32_ok_all = pmac->cnt_he_crc32_ok + + pmac->cnt_vht_crc32_ok + + pmac->cnt_ht_crc32_ok + + pmac->cnt_ofdm_crc32_ok + + pmac->cnt_cck_crc32_ok; + + if (chip->chip_gen == RTW89_CHIP_BE) { + pmac->cnt_crc32_error_all += pmac->cnt_eht_crc32_error; + pmac->cnt_crc32_ok_all += pmac->cnt_eht_crc32_ok; + } + + rtw89_phy_pmac_stat_reset(rtwdev, bb, cck); +} + static void rtw89_phy_trigger_tx_count(struct rtw89_dev *rtwdev) { if (RTW89_CHK_FW_FEATURE(TX_HISTORY_V1, &rtwdev->fw)) @@ -5854,10 +6044,15 @@ static void rtw89_phy_trigger_tx_count(struct rtw89_dev *rtwdev) static void rtw89_phy_stat_update(struct rtw89_dev *rtwdev) { + struct rtw89_bb_ctx *bb; + if (!rtwdev->phy_info.bb_stat_cfg.enable) return; rtw89_phy_trigger_tx_count(rtwdev); + + rtw89_for_each_active_bb(rtwdev, bb) + rtw89_phy_pmac_stat_update(rtwdev, bb); } void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 1e1125235f0c..c054a402bd20 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8799,6 +8799,12 @@ #define R_P0_ANT_SW 0x0728 #define B_P0_HW_ANTSW_DIS_BY_GNT_BT BIT(12) #define B_P0_TRSW_TX_EXTEND GENMASK(3, 0) +#define R_RST_ALL_CNT 0x0730 +#define R_RST_ALL_CNT_BE4 0x20730 +#define B_RST_ALL_CNT BIT(0) +#define R_ENABLE_ALL_CNT 0x0730 +#define R_ENABLE_ALL_CNT_BE4 0x20730 +#define B_ENABLE_ALL_CNT BIT(1) #define R_MAC_PIN_SEL 0x0734 #define R_MAC_PIN_SEL_BE4 0x20734 #define B_CH_IDX_SEG0 GENMASK(23, 16) @@ -8949,6 +8955,8 @@ #define B_RXHT_MCS_LIMIT GENMASK(9, 8) #define R_RXVHT_MCS_LIMIT 0x0D18 #define B_RXVHT_MCS_LIMIT GENMASK(22, 21) +#define R_BRK_OPT 0x0D44 +#define B_BRK_OPT BIT(31) #define R_P0_EN_SOUND_WO_NDP 0x0D7C #define B_P0_EN_SOUND_WO_NDP BIT(1) #define R_RXHE 0x0D80 @@ -9004,6 +9012,21 @@ #define R_CFO_COMP_SEG0_L 0x1384 #define R_CFO_COMP_SEG0_H 0x1388 #define R_CFO_COMP_SEG0_CTRL 0x138C +#define R_CNT_CCKTXEN 0x1700 +#define R_CNT_CCKTXEN_V1 0x2E00 +#define R_CNT_CCKTXEN_BE4 0x2CF00 +#define B_CNT_CCKTXEN GENMASK(31, 16) +#define R_CNT_CCKTXON 0x1704 +#define R_CNT_CCKTXON_V1 0x2E04 +#define R_CNT_CCKTXON_BE4 0x2CF04 +#define B_CNT_CCKTXON GENMASK(15, 0) +#define R_CNT_CCK_CCA_P0 0x1710 +#define R_CNT_CCK_CCA_P0_V1 0x2E10 +#define B_CNT_CCK_CCA_P0 GENMASK(15, 0) +#define R_CNT_CCK_CRC32_P0 0x1714 +#define R_CNT_CCK_CRC32_P0_V1 0x2E14 +#define B_CNT_CCK_CRC32OK_P0 GENMASK(15, 0) +#define B_CNT_CCK_CRC32FAIL_P0 GENMASK(31, 16) #define R_DBG32_D 0x1730 #define R_EDCCA_RPT_A 0x1738 #define R_EDCCA_RPT_B 0x173c @@ -9019,7 +9042,73 @@ #define B_SWSI_W_BUSY_V1 BIT(24) #define B_SWSI_R_BUSY_V1 BIT(25) #define B_SWSI_R_DATA_DONE_V1 BIT(26) +#define R_CNT_LSIG_BRK_S_TH 0x1A00 +#define R_CNT_LSIG_BRK_S_TH_V1 0x0E00 +#define R_CNT_LSIG_BRK_S_TH_BE4 0x20E00 +#define B_CNT_LSIG_BRK_S_TH GENMASK(31, 16) +#define R_CNT_CCA_SPOOFING 0x1A00 +#define R_CNT_CCA_SPOOFING_V1 0x0E00 +#define R_CNT_CCA_SPOOFING_BE4 0x20E00 +#define B_CNT_CCA_SPOOFING GENMASK(15, 0) +#define R_CNT_LSIG_BRK_L_TH 0x1A04 +#define R_CNT_LSIG_BRK_L_TH_V1 0x0E04 +#define R_CNT_LSIG_BRK_L_TH_BE4 0x20E04 +#define B_CNT_LSIG_BRK_L_TH GENMASK(15, 0) +#define R_CNT_BRK 0x1A08 +#define R_CNT_BRK_V1 0x0E08 +#define R_CNT_BRK_BE4 0x20E08 +#define B_CNT_BRK GENMASK(31, 16) +#define R_CNT_RXL_ERR_PARITY 0x1A0C +#define R_CNT_RXL_ERR_PARITY_V1 0x0E0C +#define R_CNT_RXL_ERR_PARITY_BE4 0x20E0C +#define B_CNT_RXL_ERR_PARITY GENMASK(31, 16) +#define R_CNT_RXL_ERR_RATE 0x1A10 +#define R_CNT_RXL_ERR_RATE_V1 0x0E10 +#define R_CNT_RXL_ERR_RATE_BE4 0x20E10 +#define B_CNT_RXL_ERR_RATE GENMASK(15, 0) +#define R_CNT_SEARCH_FAIL 0x1A20 +#define R_CNT_SEARCH_FAIL_V1 0x0E20 +#define R_CNT_SEARCH_FAIL_BE4 0x20E20 +#define B_CNT_SEARCH_FAIL GENMASK(31, 16) +#define R_CNT_OFDM_CCA 0x1A24 +#define R_CNT_OFDM_CCA_V1 0x0E24 +#define R_CNT_OFDM_CCA_BE4 0x20E24 +#define B_CNT_OFDM_CCA GENMASK(15, 0) #define R_TX_COUNTER 0x1A40 +#define R_CNT_OFDMTXON 0x1A40 +#define R_CNT_OFDMTXON_V1 0x0E40 +#define R_CNT_OFDMTXON_BE4 0x20E40 +#define B_CNT_OFDMTXON GENMASK(15, 0) +#define B_CNT_OFDMTXEN GENMASK(31, 16) +#define R_CNT_HE_CRC 0x1A58 +#define R_CNT_HE_CRC_V1 0x0E58 +#define R_CNT_HE_CRC_BE4 0x20E58 +#define B_CNT_HE_CRC_OK GENMASK(15, 0) +#define B_CNT_HE_CRC_ERR GENMASK(31, 16) +#define R_CNT_VHT_CRC 0x1A5C +#define R_CNT_VHT_CRC_V1 0x0E5C +#define R_CNT_VHT_CRC_BE4 0x20E5C +#define B_CNT_VHT_CRC_OK GENMASK(15, 0) +#define B_CNT_VHT_CRC_ERR GENMASK(31, 16) +#define R_CNT_HT_CRC 0x1A60 +#define R_CNT_HT_CRC_V1 0x0E60 +#define R_CNT_HT_CRC_BE4 0x20E60 +#define B_CNT_HT_CRC_OK GENMASK(15, 0) +#define B_CNT_HT_CRC_ERR GENMASK(31, 16) +#define R_CNT_L_CRC 0x1A64 +#define R_CNT_L_CRC_V1 0x0E64 +#define R_CNT_L_CRC_BE4 0x20E64 +#define B_CNT_L_CRC_OK GENMASK(15, 0) +#define B_CNT_L_CRC_ERR GENMASK(31, 16) +#define R_CNT_AMPDU_MISS 0x1A7C +#define R_CNT_AMPDU_MISS_V1 0x0E7C +#define R_CNT_AMPDU_MISS_BE4 0x20E7C +#define B_CNT_AMPDU_MISS GENMASK(31, 16) +#define R_CNT_AMPDU_RX_CRC32 0x1A80 +#define R_CNT_AMPDU_RX_CRC32_V1 0x0E80 +#define R_CNT_AMPDU_RX_CRC32_BE4 0x20E80 +#define B_CNT_AMPDU_RX_CRC32_OK GENMASK(15, 0) +#define B_CNT_AMPDU_RX_CRC32_ERR GENMASK(31, 16) #define R_NHM_CNT0 0x1A88 #define B_NHM_CNT0_MSK GENMASK(15, 0) #define B_NHM_CNT1_MSK GENMASK(31, 16) @@ -9137,12 +9226,41 @@ #define B_RXCCA_DIS_V1 BIT(0) #define R_RXSC 0x237C #define B_RXSC_EN BIT(0) +#define R_R1B_RR_SEL 0x2388 +#define B_R1B_RR_SEL GENMASK(24, 23) +#define R_R1B_RX_RPT_RST 0x2388 +#define R_R1B_RX_RPT_RST_V1 0x2340 +#define R_R1B_RX_RPT_RST_BE 0x0540 +#define R_R1B_RX_RPT_RST_BE4 0x20540 +#define B_R1B_RX_RPT_RST BIT(15) +#define B_R1B_RX_RPT_RST_V1 BIT(7) +#define R_BRK_CNT 0x239C +#define R_BRK_CNT_V1 0x059C +#define R_BRK_CNT_BE4 0x2059C +#define B_BRK_CNT GENMASK(31, 16) #define R_RX_RPL_OFST 0x23AC #define B_RX_RPL_OFST_CCK_MASK GENMASK(6, 0) #define R_RXSCOBC 0x23B0 #define B_RXSCOBC_TH GENMASK(18, 0) #define R_RXSCOCCK 0x23B4 #define B_RXSCOCCK_TH GENMASK(18, 0) +#define R_SFD_GG_CNT 0x23E0 +#define R_SFD_GG_CNT_V1 0x23A0 +#define R_SFD_GG_CNT_V2 0x05A0 +#define R_SFD_GG_CNT_BE4 0x205A0 +#define B_SFD_GG_CNT GENMASK(15, 0) +#define R_SIG_GG_CNT 0x23E8 +#define R_SIG_GG_CNT_V1 0x23AC +#define R_SIG_GG_CNT_V2 0x05AC +#define R_SIG_GG_CNT_BE4 0x205AC +#define B_SIG_GG_CNT GENMASK(15, 0) +#define B_SIG_GG_CNT_V1 GENMASK(15, 8) +#define R_SPOOF_CNT 0x23EC +#define R_SPOOF_CNT_V1 0x23A8 +#define R_SPOOF_CNT_V2 0x05A8 +#define R_SPOOF_CNT_BE4 0x205A8 +#define B_SPOOF_CNT GENMASK(7, 0) +#define B_SPOOF_CNT_V1 GENMASK(31, 16) #define R_P80_AT_HIGH_FREQ_RU_ALLOC 0x2410 #define B_P80_AT_HIGH_FREQ_RU_ALLOC_PHY1 BIT(14) #define B_P80_AT_HIGH_FREQ_RU_ALLOC_PHY0 BIT(13) @@ -9179,6 +9297,10 @@ #define R_EDCCA_RPT_B_BE4_C1 0x2FE34 #define R_EDCCA_RPT_P1_A_BE 0x2E40 #define R_EDCCA_RPT_P1_B_BE 0x2E44 +#define R_CNT_EHT_CRC 0x2F00 +#define R_CNT_EHT_CRC_BE4 0x22F00 +#define B_CNT_EHT_CRC_OK GENMASK(15, 0) +#define B_CNT_EHT_CRC_ERR GENMASK(31, 16) #define R_S1_HW_SI_DIS 0x3200 #define B_S1_HW_SI_DIS_W_R_TRIG GENMASK(30, 28) #define R_P1_RXCK 0x32A0 @@ -10547,6 +10669,11 @@ #define R_IFS_T3_HIS_BE4 0x20F54 #define B_IFS_T3_HIS_BE4 GENMASK(15, 0) #define B_IFS_T4_HIS_BE4 GENMASK(31, 16) +#define R_CNT_CCK_CCA_BE4 0x20FE8 +#define B_CNT_CCK_CCA_BE4 GENMASK(15, 0) +#define R_CNT_CCK_CRC32_BE4 0x20FEC +#define B_CNT_CCK_CRC32OK_BE4 GENMASK(15, 0) +#define B_CNT_CCK_CRC32FAIL_BE4 GENMASK(31, 16) #define R_TX_ERROR_SEL_BE4 0x21254 #define B_TX_ERROR_PSDU_BE4 BIT(11) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index d9a144b4bf58..e047e716d9e3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -307,6 +307,54 @@ static const struct rtw89_edcca_regs rtw8851b_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8851b_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_btc_rf_trx_para rtw89_btc_8851b_rf_ul[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ @@ -2722,6 +2770,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8851b_edcca_regs, + .pmac_regs = &rtw8851b_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8851b, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index b81eeb59be60..686e489d42f2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -621,6 +621,54 @@ static const struct rtw89_edcca_regs rtw8852a_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852a_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static void rtw8852a_efuse_parsing_tssi(struct rtw89_dev *rtwdev, struct rtw8852a_efuse *map) { @@ -2459,6 +2507,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .btc_sb = {{{R_AX_SCOREBOARD, R_AX_SCOREBOARD},}}, .dma_ch_mask = 0, .edcca_regs = &rtw8852a_edcca_regs, + .pmac_regs = &rtw8852a_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852a, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 13c942127225..6ab99f72fda7 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -259,6 +259,54 @@ static const struct rtw89_edcca_regs rtw8852b_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852b_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_ul[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ @@ -1056,6 +1104,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8852b_edcca_regs, + .pmac_regs = &rtw8852b_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852b, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 3fd5990a8bc4..83de26273100 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -202,6 +202,54 @@ static const struct rtw89_edcca_regs rtw8852bt_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852bt_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_btc_rf_trx_para rtw89_btc_8852bt_rf_ul[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ @@ -893,6 +941,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8852bt_edcca_regs, + .pmac_regs = &rtw8852bt_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852bt, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 9ef469c1080e..cc278587b532 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -270,6 +270,54 @@ static const struct rtw89_edcca_regs rtw8852c_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852c_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT_V1, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT_V1, B_SIG_GG_CNT_V1}, + .cck_spoofing = {R_SPOOF_CNT_V1, B_SPOOF_CNT_V1}, + .cck_brk = {R_BRK_CNT, B_BRK_CNT}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {R_BRK_OPT, B_BRK_OPT}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST_V1, B_R1B_RX_RPT_RST_V1}, + .r1b_rr_sel = {}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static void rtw8852c_ctrl_btg_bt_rx(struct rtw89_dev *rtwdev, bool en, enum rtw89_phy_idx phy_idx); @@ -3252,6 +3300,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .btc_sb = {{{R_AX_SCOREBOARD, R_AX_SCOREBOARD},}}, .dma_ch_mask = 0, .edcca_regs = &rtw8852c_edcca_regs, + .pmac_regs = &rtw8852c_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852c, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 3c453b93c52e..e6f15ee2a86b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -383,6 +383,54 @@ static const struct rtw89_edcca_regs rtw8922a_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_BE_M, }; +static const struct rtw89_pmac_regs rtw8922a_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON_V1, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN_V1, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0_V1, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT_V2, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT_V2, B_SIG_GG_CNT_V1}, + .cck_spoofing = {R_SPOOF_CNT_V2, B_SPOOF_CNT_V1}, + .cck_brk = {R_BRK_CNT_V1, B_BRK_CNT}, + .brk = {R_CNT_BRK_V1, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL_V1, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH_V1, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH_V1, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY_V1, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE_V1, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA_V1, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING_V1, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS_V1, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST_BE, B_R1B_RX_RPT_RST_V1}, + .r1b_rr_sel = {}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0_V1, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON_V1, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC_V1, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC_V1, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC_V1, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC_V1, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = R_CNT_EHT_CRC, + .eht_crc_ok_mask = B_CNT_EHT_CRC_OK, + .eht_crc_err_mask = B_CNT_EHT_CRC_ERR, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32_V1, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_efuse_block_cfg rtw8922a_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_SYS] = {.offset = 0x00000, .size = 0x310}, [RTW89_EFUSE_BLOCK_RF] = {.offset = 0x10000, .size = 0x240}, @@ -3245,6 +3293,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .btc_sb = {{{R_BE_SCOREBOARD, R_BE_SCOREBOARD},}}, .dma_ch_mask = 0, .edcca_regs = &rtw8922a_edcca_regs, + .pmac_regs = &rtw8922a_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8922a, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index ba736b967880..b8b75fca0a1b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -227,6 +227,54 @@ static const struct rtw89_edcca_regs rtw8922d_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_BE_M, }; +static const struct rtw89_pmac_regs rtw8922d_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON_BE4, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN_BE4, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_BE4, B_CNT_CCK_CCA_BE4}, + .cck_sfd_gg = {R_SFD_GG_CNT_BE4, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT_BE4, B_SIG_GG_CNT_V1}, + .cck_spoofing = {R_SPOOF_CNT_BE4, B_SPOOF_CNT_V1}, + .cck_brk = {R_BRK_CNT_BE4, B_BRK_CNT}, + .brk = {R_CNT_BRK_BE4, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL_BE4, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH_BE4, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH_BE4, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY_BE4, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE_BE4, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA_BE4, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING_BE4, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS_BE4, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST_BE4, B_R1B_RX_RPT_RST_V1}, + .r1b_rr_sel = {}, + .enable_all_cnt = {R_ENABLE_ALL_CNT_BE4, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT_BE4, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_BE4, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_BE4, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_BE4, + .ofdm_txon = R_CNT_OFDMTXON_BE4, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC_BE4, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC_BE4, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC_BE4, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC_BE4, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = R_CNT_EHT_CRC_BE4, + .eht_crc_ok_mask = B_CNT_EHT_CRC_OK, + .eht_crc_err_mask = B_CNT_EHT_CRC_ERR, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32_BE4, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_SYS] = {.offset = 0x00000, .size = 0x310}, [RTW89_EFUSE_BLOCK_RF] = {.offset = 0x10000, .size = 0x240}, @@ -3085,6 +3133,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { BIT(RTW89_DMA_ACH5) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B0HI) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8922d_edcca_regs, + .pmac_regs = &rtw8922d_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8922d, #endif From 419ed7f4a0532e6211f1f73b311859500a63e141 Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:23 +0800 Subject: [PATCH 048/105] wifi: rtw89: debug: extend bb_info with TX status and PER Enhance bb_info debugfs by adding TX status information to aid debugging and performance analysis. A snapshot of TX-related registers, including PPDU type and subtype, bandwidth, TX power, STBC, etc. The information is collected per PHY during track_work and displayed via bb_info debugfs. TX status output: == TX General EHT_MU_SU DATA BW: 160, TX_SC: 0, TX_PATH_EN: 3, PATH_MAP: 0xe4 TXPWR TMAC: 11, P0: 11, P1: 11 dBm MCS: 9, STBC: 0 Info: [0x0000000b, 0xc1702c30, 0x0ff1e430, 0xc0000000, 0x99000009, 0x40000000] Common ctrl: [0x00000150, 0x40190140] In addition, include the retry ratio from RA reports and display it as PER (packet error rate) in station debug information. PER output: TX rate [0, 0]: HE 2SS MCS-4 GI:0.8 BW:20 (hw_rate=0x324) PER:23 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 24 +++ drivers/net/wireless/realtek/rtw89/debug.c | 206 ++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.c | 93 ++++++++- drivers/net/wireless/realtek/rtw89/phy.h | 23 +++ drivers/net/wireless/realtek/rtw89/phy_be.c | 46 +++++ drivers/net/wireless/realtek/rtw89/reg.h | 29 +++ 6 files changed, 420 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 7f869a339ee6..e8b03716d41e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3488,6 +3488,7 @@ struct rtw89_ra_report { struct rate_info txrate; u32 bit_rate; u16 hw_rate; + u8 retry_ratio; bool might_fallback_legacy; }; @@ -4175,6 +4176,12 @@ struct rtw89_reg5_def { u32 data; }; +#define RTW89_REGS_DEF(x) {x, ARRAY_SIZE(x)} +struct rtw89_regs_def { + const u32 *regs; + u32 reg_nr; +}; + struct rtw89_reg_imr { u32 addr; u32 clr; @@ -5591,6 +5598,22 @@ struct rtw89_pmac_stat_info { u32 cnt_sb_search_fail; }; +struct rtw89_tx_stat_info { + u32 info[6]; + u32 common_ctrl[2]; + u32 txpwr[2]; + u8 type; + u8 subtype; + u8 txcmd; + u8 txsc; + u8 bw; + u16 tmac_txpwr; + u8 tx_path_en; + u8 path_map; + u8 max_mcs; + bool stbc; +}; + struct rtw89_agc_gaincode_set { u8 lna_idx; u8 tia_idx; @@ -6418,6 +6441,7 @@ struct rtw89_dev { struct rtw89_pkt_stat cur_pkt_stat; struct rtw89_pkt_stat last_pkt_stat; struct rtw89_pmac_stat_info pmac_stat; + struct rtw89_tx_stat_info tx_stat; } bbs[RTW89_PHY_NUM]; struct wiphy_delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index d8a183bfc6da..d9a5bbae11f2 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -3926,6 +3926,7 @@ static int rtw89_sta_link_info_get_iter(struct rtw89_dev *rtwdev, rtw89_rate_info_bw_to_mhz(rate->bw)); p += scnprintf(p, end - p, " (hw_rate=0x%x)", rtwsta_link->ra_report.hw_rate); + p += scnprintf(p, end - p, " PER:%d", rtwsta_link->ra_report.retry_ratio); p += scnprintf(p, end - p, " ==> agg_wait=%d (%d)\n", rtwsta_link->max_agg_wait, max_rc_amsdu_len); @@ -4138,11 +4139,179 @@ static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, return p - buf; } +static const char *const lcck[] = {"L_CCK"}; +static const char *const scck[] = {"S_CCK"}; +static const char *const ht_gf[] = {"HT_GF"}; +static const char *const vht_mu[] = {"VHT_MU"}; +static const char *const he_er_su[] = {"HE_ER_SU"}; +static const char *const eht_tb[] = {"EHT_TB"}; +static const char *const legacy_ax[] = {"LEGACY"}; +static const char *const ht_ax[] = {"HT"}; +static const char *const vht_su_ax[] = {"VHT_SU"}; +static const char *const he_su_ax[] = {"HE_SU"}; +static const char *const he_mu_ax[] = {"HE_MU"}; +static const char *const he_tb_ax[] = {"HE_TB"}; +static const char *const legacy_be[] = { + "LEGACY", "LEGACY_DUP", "LEGACY_DUP_PUNC" +}; + +static const char *const ht_be[] = { + "HT_MF", "HT_SND_NDP" +}; + +static const char *const vht_su_be[] = { + "VHT_SU", "VHT_SND_NDP" +}; + +static const char *const he_su_be[] = { + "HE_SU", "HE_SND_NDP", "HE_SND_NDP_PUNC", "HE_RANG_NDP" +}; + +static const char *const he_mu_be[] = { + "HE_MU_RU", "HE_MU_MU", "HE_MU_RU_PUNC" +}; + +static const char *const he_tb_be[] = { + "HE_TB", "HE_TB_FB_NDP", "HE_MU_RANG_NDP" +}; + +static const char *const eht_mu[] = { + "EHT_MU_SU", "EHT_MU_ER", "EHT_MU_RU", "EHT_MU_MU", + "EHT_MU_SND_NDP", "EHT_MU_SU_PUNC", "EHT_MU_RU_PUNC", + "EHT_SND_NDP_PUNC", "EHT_MU_MU_PUNC" +}; + +#define PPDU_SAME(ppdu) \ + {.str = {ppdu, ppdu}, \ + .cnt = {ARRAY_SIZE(ppdu), ARRAY_SIZE(ppdu)} } +#define PPDU_VARIANT(ppdu) \ + {.str = {ppdu##_ax, ppdu##_be}, \ + .cnt = {ARRAY_SIZE(ppdu##_ax), ARRAY_SIZE(ppdu##_be)} } +#define PPDU_GEV1(ppdu) \ + {.str = {NULL, ppdu}, \ + .cnt = {0, ARRAY_SIZE(ppdu)} } + +static const struct rtw89_ppdu_info { + const char *const *str[RTW89_CHIP_GEN_NUM]; + u8 cnt[RTW89_CHIP_GEN_NUM]; +} rtw89_ppdu_infos[] = { + [0] = PPDU_SAME(lcck), + [1] = PPDU_SAME(scck), + [2] = PPDU_VARIANT(legacy), + [3] = PPDU_VARIANT(ht), + [4] = PPDU_SAME(ht_gf), + [5] = PPDU_VARIANT(vht_su), + [6] = PPDU_SAME(vht_mu), + [7] = PPDU_VARIANT(he_su), + [8] = PPDU_SAME(he_er_su), + [9] = PPDU_VARIANT(he_mu), + [10] = PPDU_VARIANT(he_tb), + [11] = PPDU_GEV1(eht_mu), + [12] = PPDU_GEV1(eht_tb), +}; + +#define TXCMD_SAME(txcmd) {txcmd, txcmd} +#define TXCMD_DIFF(txcmd, txcmd_v1) {txcmd, txcmd_v1} +#define TXCMD_GEV1(txcmd) {"RSVD", txcmd} + +static const struct rtw89_txcmd_info { + const char *str[RTW89_CHIP_GEN_NUM]; +} rtw89_txcmd_infos[] = { + [0] = {TXCMD_SAME("DATA")}, + [1] = {TXCMD_SAME("BCN")}, + [2] = {TXCMD_SAME("HT_NDPA")}, + [3] = {TXCMD_SAME("VHT_NDPA")}, + [4] = {TXCMD_SAME("HE_NDPA")}, + [5] = {TXCMD_GEV1("EHT_NDPA")}, + [6] = {TXCMD_GEV1("11MC_FTM")}, + [7] = {TXCMD_GEV1("11MC_FTM_ACK")}, + [8] = {TXCMD_SAME("RTS")}, + [9] = {TXCMD_SAME("CTS2S")}, + [10] = {TXCMD_SAME("CF_END")}, + [11] = {TXCMD_SAME("CMP_BAR")}, + [12] = {TXCMD_SAME("BFRP")}, + [13] = {TXCMD_SAME("NDP")}, + [14] = {TXCMD_SAME("QoS_NULL")}, + [15] = {TXCMD_GEV1("CTS_2_MURTS")}, + [16] = {TXCMD_SAME("ACK")}, + [17] = {TXCMD_SAME("CTS")}, + [18] = {TXCMD_SAME("CMP_BA")}, + [19] = {TXCMD_SAME("MSTA_BA")}, + [20] = {TXCMD_SAME("HT_CSI")}, + [21] = {TXCMD_SAME("VHT_CSI")}, + [22] = {TXCMD_SAME("HE_CSI")}, + [23] = {TXCMD_GEV1("EHT_CSI")}, + [24] = {TXCMD_GEV1("NTB_I2R_NDPA")}, + [25] = {TXCMD_GEV1("NTB_I2R_NDP")}, + [26] = {TXCMD_GEV1("NTB_I2R_LMR")}, + [27] = {TXCMD_GEV1("NTB_I2R_NDP")}, + [28] = {TXCMD_GEV1("NTB_I2R_LMR")}, + [29] = {TXCMD_GEV1("NTB_R2I_RANG_NDPA")}, + [30] = {TXCMD_GEV1("NTB_R2I_NDP")}, + [31] = {TXCMD_DIFF("TB_PPDU", "NTB_R2I_LMR")}, + [32] = {TXCMD_SAME("TRIG_BASIC")}, + [33] = {TXCMD_SAME("TRIG_BFRP")}, + [34] = {TXCMD_SAME("TRIG_MUBAR")}, + [35] = {TXCMD_SAME("TRIG_MURTS")}, + [36] = {TXCMD_SAME("TRIG_BSRP")}, + [37] = {TXCMD_SAME("TRIG_BQRP")}, + [38] = {TXCMD_SAME("TRIG_NFRP")}, + [39] = {TXCMD_GEV1("TRIG_BASIC_DATA")}, + [40] = {TXCMD_GEV1("TRIG_RANG_POLL")}, + [41] = {TXCMD_GEV1("TRIG_RANG_SNR")}, + [42] = {TXCMD_GEV1("TRIG_RANG_LMR")}, + [48] = {TXCMD_DIFF("TRIG_BASIC_DATA", "TRIG_TB_CSI")}, + [49] = {TXCMD_GEV1("TRIG_TB_CBA")}, + [50] = {TXCMD_GEV1("TRIG_TB_MBA")}, + [51] = {TXCMD_GEV1("TRIG_TB_BSR")}, + [52] = {TXCMD_GEV1("TRIG_TB_BQR")}, + [53] = {TXCMD_GEV1("TRIG_TB_ACK")}, + [54] = {TXCMD_GEV1("TRIG_TB_PPDU")}, + [55] = {TXCMD_GEV1("TRIG_TB_I2R_CTS2S")}, + [56] = {TXCMD_GEV1("TRIG_TB_I2R_NDP")}, + [57] = {TXCMD_GEV1("TRIG_TB_I2R_LMR")}, +}; + +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)) + return "RSVD"; + + ppdu_info = &rtw89_ppdu_infos[type]; + + if (!ppdu_info->str[chip->chip_gen] || + subtype >= ppdu_info->cnt[chip->chip_gen]) + return "RSVD"; + + return ppdu_info->str[chip->chip_gen][subtype]; +} + +static const char *rtw89_txcmd_str(struct rtw89_dev *rtwdev, u8 txcmd) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + if (txcmd < ARRAY_SIZE(rtw89_txcmd_infos)) + return rtw89_txcmd_infos[txcmd].str[chip->chip_gen] ?: "RSVD"; + + return "RSVD"; +} + static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, char *buf, size_t bufsz) { + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const struct rtw89_physts_regs *physts = phy->physts; struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + struct rtw89_tx_stat_info *tx_stat = &bb->tx_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; char *p = buf, *end = buf + bufsz; + u8 factor = chip->txpwr_factor_rf; + u32 reg_nr; + s32 val; + int i; p += scnprintf(p, end - p, "\n[PHY %u]\n", bb->phy_idx); @@ -4178,6 +4347,43 @@ static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, pmac->cnt_sb_search_fail); p += scnprintf(p, end - p, "AMPDU miss: %d\n\n", pmac->cnt_ampdu_miss); + p += scnprintf(p, end - p, "== TX General\n"); + p += scnprintf(p, end - p, "%s %s\n", + rtw89_ppdu_str(rtwdev, tx_stat->type, tx_stat->subtype), + rtw89_txcmd_str(rtwdev, tx_stat->txcmd)); + + p += scnprintf(p, end - p, "BW: %d, TX_SC: %d, TX_PATH_EN: %d, PATH_MAP: 0x%x\n", + 20 << tx_stat->bw, tx_stat->txsc, + tx_stat->tx_path_en, tx_stat->path_map); + + val = sign_extend32(tx_stat->tmac_txpwr, 8); + p += scnprintf(p, end - p, "TXPWR TMAC: %d,", val >> factor); + + reg_nr = min(chip->rf_path_num, ARRAY_SIZE(tx_stat->txpwr)); + for (i = 0; i < reg_nr; i++) { + val = sign_extend32(tx_stat->txpwr[i], 8); + p += scnprintf(p, end - p, " P%d: %d%s", + i, val >> factor, (i < reg_nr - 1) ? "," : ""); + } + p += scnprintf(p, end - p, " dBm\n"); + + p += scnprintf(p, end - p, "MCS: %d, STBC: %d\n", + tx_stat->max_mcs, tx_stat->stbc); + + p += scnprintf(p, end - p, "Info: ["); + reg_nr = min(physts->tx_info.reg_nr, ARRAY_SIZE(tx_stat->info)); + for (i = 0; i < reg_nr; i++) + p += scnprintf(p, end - p, "0x%08x%s", + tx_stat->info[i], (i < reg_nr - 1) ? ", " : ""); + p += scnprintf(p, end - p, "]\n"); + + p += scnprintf(p, end - p, "Common ctrl: ["); + reg_nr = min(physts->tx_common_ctrl.reg_nr, ARRAY_SIZE(tx_stat->common_ctrl)); + for (i = 0; i < reg_nr; i++) + p += scnprintf(p, end - p, "0x%08x%s", + tx_stat->common_ctrl[i], (i < reg_nr - 1) ? ", " : ""); + p += scnprintf(p, end - p, "]\n\n"); + p += scnprintf(p, end - p, "== RSSI/RX Rate\n"); p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 80d358d80a38..cf7382e36b75 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -3239,6 +3239,7 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, bool format_v1 = chip->chip_gen == RTW89_CHIP_BE; u8 mode, rate, bw, giltf, mac_id; u16 legacy_bitrate, amsdu_len; + u8 retry_ratio = 0; bool valid; u8 mcs = 0; u8 t; @@ -3259,6 +3260,7 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, bw |= u8_encode_bits(t, BIT(2)); t = le32_get_bits(c2h->w3, RTW89_C2H_RA_RPT_W3_MD_SEL_B2); mode |= u8_encode_bits(t, BIT(2)); + retry_ratio = le32_get_bits(c2h->w2, RTW89_C2H_RA_RPT_W2_RETRY_RATIO); } if (mode == RTW89_RA_RPT_MODE_LEGACY) { @@ -3334,6 +3336,7 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, u16_encode_bits(rate, RTW89_HW_RATE_V1_MASK_VAL) : u16_encode_bits(mode, RTW89_HW_RATE_MASK_MOD) | u16_encode_bits(rate, RTW89_HW_RATE_MASK_VAL); + ra_report->retry_ratio = retry_ratio; ra_report->might_fallback_legacy = mcs <= 2; amsdu_len = get_max_amsdu_len(rtwdev, ra_report); @@ -6034,6 +6037,71 @@ static void rtw89_phy_pmac_stat_update(struct rtw89_dev *rtwdev, rtw89_phy_pmac_stat_reset(rtwdev, bb, cck); } +static void rtw89_phy_tx_stat_update(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const struct rtw89_physts_regs *physts = phy->physts; + struct rtw89_tx_stat_info *tx_stat = &bb->tx_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 reg_nr; + u32 val; + u32 i; + + if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF && + bb->phy_idx != RTW89_PHY_0) + val = 1; + else + val = 0; + + rtw89_phy_write32_mask(rtwdev, physts->mac_phy_intf_sel.addr, + physts->mac_phy_intf_sel.mask, val); + + reg_nr = min(physts->tx_info.reg_nr, ARRAY_SIZE(tx_stat->info)); + for (i = 0; i < reg_nr; i++) + tx_stat->info[i] = rtw89_phy_read32(rtwdev, physts->tx_info.regs[i]); + + reg_nr = min(physts->tx_common_ctrl.reg_nr, ARRAY_SIZE(tx_stat->common_ctrl)); + for (i = 0; i < reg_nr; i++) + tx_stat->common_ctrl[i] = + rtw89_phy_read32(rtwdev, physts->tx_common_ctrl.regs[i]); + + reg_nr = min(chip->rf_path_num, ARRAY_SIZE(tx_stat->txpwr)); + for (i = 0; i < reg_nr; i++) + tx_stat->txpwr[i] = rtw89_phy_read32_mask(rtwdev, physts->txpwr[i].addr, + physts->txpwr[i].mask); + + tx_stat->type = u32_get_bits(tx_stat->info[0], TX_STATUS_TYPE); + + if (chip->chip_gen == RTW89_CHIP_AX) { + tx_stat->tx_path_en = u32_get_bits(tx_stat->info[0], TX_STATUS_TX_PATH_EN); + tx_stat->path_map = u32_get_bits(tx_stat->info[0], TX_STATUS_PATH_MAP); + tx_stat->txcmd = u32_get_bits(tx_stat->info[0], TX_STATUS_TXCMD); + tx_stat->txsc = u32_get_bits(tx_stat->info[1], TX_STATUS_TXSC); + tx_stat->bw = u32_get_bits(tx_stat->info[1], TX_STATUS_BW); + tx_stat->tmac_txpwr = u32_get_bits(tx_stat->info[1], TX_STATUS_TMAC_TXPWR); + + if (!(chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev))) + tx_stat->max_mcs = u32_get_bits(tx_stat->info[3], TX_STATUS_MAX_MCS); + } else { + tx_stat->type = u32_get_bits(tx_stat->info[0], TX_STATUS_TYPE); + tx_stat->subtype = u32_get_bits(tx_stat->info[0], TX_STATUS_SUBTYPE); + + if (chip->chip_id == RTL8922A) + tx_stat->txcmd = u32_get_bits(tx_stat->info[0], TX_STATUS_TXCMD_V1); + else + tx_stat->txcmd = u32_get_bits(tx_stat->info[0], TX_STATUS_TXCMD_V2); + + tx_stat->txsc = u32_get_bits(tx_stat->info[0], TX_STATUS_TXSC_V1); + tx_stat->bw = u32_get_bits(tx_stat->info[1], TX_STATUS_BW_V1); + tx_stat->tmac_txpwr = u32_get_bits(tx_stat->info[1], TX_STATUS_TMAC_TXPWR_V1); + tx_stat->tx_path_en = u32_get_bits(tx_stat->info[2], TX_STATUS_TX_PATH_EN_V1); + tx_stat->path_map = u32_get_bits(tx_stat->info[2], TX_STATUS_PATH_MAP_V1); + tx_stat->max_mcs = u32_get_bits(tx_stat->info[4], TX_STATUS_MAX_MCS_V1); + } + + tx_stat->stbc = !!(tx_stat->common_ctrl[0] & TX_STATUS_STBC); +} + static void rtw89_phy_trigger_tx_count(struct rtw89_dev *rtwdev) { if (RTW89_CHK_FW_FEATURE(TX_HISTORY_V1, &rtwdev->fw)) @@ -6051,8 +6119,10 @@ static void rtw89_phy_stat_update(struct rtw89_dev *rtwdev) rtw89_phy_trigger_tx_count(rtwdev); - rtw89_for_each_active_bb(rtwdev, bb) + rtw89_for_each_active_bb(rtwdev, bb) { rtw89_phy_pmac_stat_update(rtwdev, bb); + rtw89_phy_tx_stat_update(rtwdev, bb); + } } void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) @@ -8702,10 +8772,31 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_ax = { .nhm_pwr_method_msk = B_NHM_PWDB_METHOD_MSK, }; +static const u32 rtw89_txinfo_reg_ax[] = { + R_TX_INFO_0_0_COMB, + R_TX_INFO_0_1_COMB, + R_TX_INFO_1_0_COMB, + R_TX_INFO_1_1_COMB +}; + +static const u32 rtw89_tx_common_ctrl_reg_ax[] = { + R_TX_COMMON_CTRL_0_0_COMB, + R_TX_COMMON_CTRL_0_1_COMB +}; + +static const struct rtw89_reg_def rtw89_txpwr_ax[] = { + {.addr = R_PATH0_TXPWR, .mask = B_PATH0_TXPWR}, + {.addr = R_PATH1_TXPWR, .mask = B_PATH1_TXPWR} +}; + static const struct rtw89_physts_regs rtw89_physts_regs_ax = { .setting_addr = R_PLCP_HISTOGRAM, .dis_trigger_fail_mask = B_STS_DIS_TRIG_BY_FAIL, .dis_trigger_brk_mask = B_STS_DIS_TRIG_BY_BRK, + .mac_phy_intf_sel = {R_INTF_R_INTF_RPT_SEL, B_INTF_R_INTF_RPT_SEL}, + .txpwr = rtw89_txpwr_ax, + .tx_info = RTW89_REGS_DEF(rtw89_txinfo_reg_ax), + .tx_common_ctrl = RTW89_REGS_DEF(rtw89_tx_common_ctrl_reg_ax), }; static const struct rtw89_cfo_regs rtw89_cfo_regs_ax = { diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index c6761cedc5a5..830cabefca39 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -57,6 +57,25 @@ #define RA_MASK_EHT_4SS_MCS0_11 GENMASK_ULL(62, 60) #define RA_MASK_EHT_RATES GENMASK_ULL(62, 12) +#define TX_STATUS_TYPE GENMASK(3, 0) +#define TX_STATUS_SUBTYPE GENMASK(7, 4) +#define TX_STATUS_TXCMD GENMASK(29, 24) +#define TX_STATUS_TXCMD_V1 GENMASK(13, 8) +#define TX_STATUS_TXCMD_V2 GENMASK(15, 8) +#define TX_STATUS_TXSC GENMASK(7, 4) +#define TX_STATUS_TXSC_V1 GENMASK(27, 24) +#define TX_STATUS_BW GENMASK(17, 16) +#define TX_STATUS_BW_V1 GENMASK(6, 4) +#define TX_STATUS_TMAC_TXPWR GENMASK(26, 18) +#define TX_STATUS_TMAC_TXPWR_V1 GENMASK(16, 8) +#define TX_STATUS_TX_PATH_EN GENMASK(15, 12) +#define TX_STATUS_TX_PATH_EN_V1 GENMASK(7, 4) +#define TX_STATUS_PATH_MAP GENMASK(23, 16) +#define TX_STATUS_PATH_MAP_V1 GENMASK(15, 8) +#define TX_STATUS_MAX_MCS GENMASK(7, 4) +#define TX_STATUS_MAX_MCS_V1 GENMASK(3, 0) +#define TX_STATUS_STBC BIT(0) + #define CFO_TRK_ENABLE_TH (2 << 2) #define CFO_TRK_STOP_TH_4 (30 << 2) #define CFO_TRK_STOP_TH_3 (20 << 2) @@ -458,6 +477,10 @@ struct rtw89_physts_regs { u32 setting_addr; u32 dis_trigger_fail_mask; u32 dis_trigger_brk_mask; + struct rtw89_reg_def mac_phy_intf_sel; + const struct rtw89_reg_def *txpwr; + struct rtw89_regs_def tx_info; + struct rtw89_regs_def tx_common_ctrl; }; struct rtw89_cfo_regs { diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 25f1b068daa2..9caddd19384c 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -135,16 +135,62 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_be_v1 = { .ifs_total_mask = B_IFS_TOTAL_BE4, }; +static const u32 rtw89_tx_info_reg_be[] = { + R_TX_INFO_0_0_COMB_V1, + R_TX_INFO_0_1_COMB_V1, + R_TX_INFO_1_0_COMB_V1, + R_TX_INFO_1_1_COMB_V1, + R_TX_INFO_2_0_COMB_V1, + R_TX_INFO_2_1_COMB_V1 +}; + +static const u32 rtw89_tx_common_ctrl_reg_be[] = { + R_TX_COMMON_CTRL_0_0_COMB_V1, + R_TX_COMMON_CTRL_0_1_COMB_V1 +}; + +static const struct rtw89_reg_def rtw89_txpwr_be[] = { + {.addr = R_PATH0_TXPWR_V1, .mask = B_PATH0_TXPWR}, + {.addr = R_PATH1_TXPWR_V1, .mask = B_PATH1_TXPWR} +}; + static const struct rtw89_physts_regs rtw89_physts_regs_be = { .setting_addr = R_PLCP_HISTOGRAM, .dis_trigger_fail_mask = B_STS_DIS_TRIG_BY_FAIL, .dis_trigger_brk_mask = B_STS_DIS_TRIG_BY_BRK, + .mac_phy_intf_sel = {R_INTF_R_INTF_RPT_SEL, B_INTF_R_INTF_RPT_SEL}, + .txpwr = rtw89_txpwr_be, + .tx_info = RTW89_REGS_DEF(rtw89_tx_info_reg_be), + .tx_common_ctrl = RTW89_REGS_DEF(rtw89_tx_common_ctrl_reg_be), +}; + +static const u32 rtw89_tx_info_reg_be_v1[] = { + R_TX_INFO_0_0_COMB_BE4, + R_TX_INFO_0_1_COMB_BE4, + R_TX_INFO_1_0_COMB_BE4, + R_TX_INFO_1_1_COMB_BE4, + R_TX_INFO_2_0_COMB_BE4, + R_TX_INFO_2_1_COMB_BE4 +}; + +static const u32 rtw89_tx_common_ctrl_reg_be_v1[] = { + R_TX_COMMON_CTRL_0_0_COMB_BE4, + R_TX_COMMON_CTRL_0_1_COMB_BE4 +}; + +static const struct rtw89_reg_def rtw89_txpwr_be_v1[] = { + {.addr = R_PATH0_TXPWR_BE4, .mask = B_PATH0_TXPWR}, + {.addr = R_PATH1_TXPWR_BE4, .mask = B_PATH1_TXPWR} }; static const struct rtw89_physts_regs rtw89_physts_regs_be_v1 = { .setting_addr = R_PLCP_HISTOGRAM_BE_V1, .dis_trigger_fail_mask = B_STS_DIS_TRIG_BY_FAIL, .dis_trigger_brk_mask = B_STS_DIS_TRIG_BY_BRK, + .mac_phy_intf_sel = {R_INTF_R_INTF_RPT_SEL_BE4, B_INTF_R_INTF_RPT_SEL}, + .txpwr = rtw89_txpwr_be_v1, + .tx_info = RTW89_REGS_DEF(rtw89_tx_info_reg_be_v1), + .tx_common_ctrl = RTW89_REGS_DEF(rtw89_tx_common_ctrl_reg_be_v1), }; static const struct rtw89_cfo_regs rtw89_cfo_regs_be = { diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index c054a402bd20..f5151e1ad58d 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8741,6 +8741,9 @@ #define B_DFS_FFT_EN BIT(0) #define R_CHINFO_DATA 0x00C0 #define B_CHINFO_DATA_BITMAP GENMASK(22, 0) +#define R_INTF_R_INTF_RPT_SEL 0x0200 +#define R_INTF_R_INTF_RPT_SEL_BE4 0x20200 +#define B_INTF_R_INTF_RPT_SEL BIT(12) #define R_ANAPAR_PW15 0x030C #define B_ANAPAR_PW15 GENMASK(31, 24) #define B_ANAPAR_PW15_H GENMASK(27, 24) @@ -9042,6 +9045,20 @@ #define B_SWSI_W_BUSY_V1 BIT(24) #define B_SWSI_R_BUSY_V1 BIT(25) #define B_SWSI_R_DATA_DONE_V1 BIT(26) +#define R_TX_INFO_0_0_COMB 0x1800 +#define R_TX_INFO_0_0_COMB_V1 0x3E00 +#define R_TX_INFO_0_1_COMB 0x1804 +#define R_TX_INFO_0_1_COMB_V1 0x3E04 +#define R_TX_INFO_1_0_COMB 0x1808 +#define R_TX_INFO_1_0_COMB_V1 0x3E08 +#define R_TX_INFO_1_1_COMB 0x180C +#define R_TX_INFO_1_1_COMB_V1 0x3E0C +#define R_TX_INFO_2_0_COMB_V1 0x3E10 +#define R_TX_INFO_2_1_COMB_V1 0x3E14 +#define R_TX_COMMON_CTRL_0_0_COMB 0x1810 +#define R_TX_COMMON_CTRL_0_0_COMB_V1 0x3E20 +#define R_TX_COMMON_CTRL_0_1_COMB 0x1814 +#define R_TX_COMMON_CTRL_0_1_COMB_V1 0x3E24 #define R_CNT_LSIG_BRK_S_TH 0x1A00 #define R_CNT_LSIG_BRK_S_TH_V1 0x0E00 #define R_CNT_LSIG_BRK_S_TH_BE4 0x20E00 @@ -9184,6 +9201,8 @@ #define B_TXAGC_BB GENMASK(31, 24) #define B_TXAGC_RF GENMASK(5, 0) #define R_PATH0_TXPWR 0x1C78 +#define R_PATH0_TXPWR_V1 0xEE0C +#define R_PATH0_TXPWR_BE4 0x2F90C #define B_PATH0_TXPWR GENMASK(8, 0) #define R_S0_ADDCK 0x1E00 #define B_S0_ADDCK_I GENMASK(9, 0) @@ -9328,6 +9347,8 @@ #define B_TXAGC_BB_S1_OFT GENMASK(31, 16) #define B_TXAGC_BB_S1 GENMASK(31, 24) #define R_PATH1_TXPWR 0x3C78 +#define R_PATH1_TXPWR_V1 0xEF0C +#define R_PATH1_TXPWR_BE4 0x2FA0C #define B_PATH1_TXPWR GENMASK(8, 0) #define R_S1_ADDCK 0x3E00 #define B_S1_ADDCK_I GENMASK(9, 0) @@ -10813,6 +10834,14 @@ #define B_SW_SI_R_BUSY_BE4 BIT(25) #define B_SW_SI_READ_DATA_DONE_BE4 BIT(26) +#define R_TX_INFO_0_0_COMB_BE4 0x2DF00 +#define R_TX_INFO_0_1_COMB_BE4 0x2DF04 +#define R_TX_INFO_1_0_COMB_BE4 0x2DF08 +#define R_TX_INFO_1_1_COMB_BE4 0x2DF0C +#define R_TX_INFO_2_0_COMB_BE4 0x2DF10 +#define R_TX_INFO_2_1_COMB_BE4 0x2DF14 +#define R_TX_COMMON_CTRL_0_0_COMB_BE4 0x2DF20 +#define R_TX_COMMON_CTRL_0_1_COMB_BE4 0x2DF24 #define R_RX_PATH0_TBL0_BE4 0x2E028 #define R_RX_PATH1_TBL0_BE4 0x2E128 From c77bb66e244e7b6cb2943234274fef278da19745 Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:24 +0800 Subject: [PATCH 049/105] wifi: rtw89: debug: add RX statistics in bb_info Expand RX packet statistics including coding type, spatial diversity, and beamforming. These statistics are accumulated per PHY and displayed in bb_info debugfs. RX statistics output: == RX General LDPC: 190, BCC: 0, STBC: 0, SU_NON_BF: 0, SU_BF: 190, MU: 0 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 26 +++++++++++++++++----- drivers/net/wireless/realtek/rtw89/core.h | 10 +++++++++ drivers/net/wireless/realtek/rtw89/debug.c | 8 +++++++ drivers/net/wireless/realtek/rtw89/txrx.h | 2 ++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index d8f83623e54a..b85728ceb63c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2097,11 +2097,8 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, u32 t; phy_ppdu->chan_idx = le32_get_bits(ie->w0, RTW89_PHY_STS_IE01_W0_CH_IDX); - - if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) { - phy_ppdu->ldpc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_LDPC); - phy_ppdu->stbc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_STBC); - } + phy_ppdu->ldpc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_LDPC); + phy_ppdu->stbc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_STBC); if (!phy_ppdu->hdr_2_en) phy_ppdu->rx_path_en = @@ -2114,9 +2111,11 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, return; phy_ppdu->rpl_avg = le32_get_bits(ie->w0, RTW89_PHY_STS_IE01_W0_RSSI_AVG_FD); + phy_ppdu->su = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_SU); phy_ppdu->ofdm.avg_snr = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_AVG_SNR); phy_ppdu->ofdm.evm_max = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MAX); phy_ppdu->ofdm.evm_min = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MIN); + phy_ppdu->bf = le32_get_bits(ie->w3, RTW89_PHY_STS_IE01_W3_BF); phy_ppdu->ofdm.has = true; /* sign conversion for S(12,2) */ @@ -3062,6 +3061,23 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, if (desc_info->data_rate < RTW89_HW_RATE_NR) pkt_stat->rx_rate_cnt[desc_info->data_rate]++; + if (phy_ppdu && phy_ppdu->ofdm.has) { + if (phy_ppdu->ldpc) + pkt_stat->rx.ldpc++; + else + pkt_stat->rx.bcc++; + + if (phy_ppdu->stbc) + pkt_stat->rx.stbc++; + + if (!phy_ppdu->su) + pkt_stat->rx.mu++; + else if (phy_ppdu->bf) + pkt_stat->rx.su_bf++; + else + pkt_stat->rx.su_non_bf++; + } + rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, false, false); out: diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index e8b03716d41e..c0e176c4c3a6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -855,8 +855,10 @@ struct rtw89_rx_phy_ppdu { } ofdm; bool has_data; bool has_bcn; + bool su; bool ldpc; bool stbc; + bool bf; bool to_self; bool valid; bool hdr_2_en; @@ -5378,6 +5380,14 @@ struct rtw89_pkt_stat { u8 beacon_rate; u32 beacon_len; u32 rx_rate_cnt[RTW89_HW_RATE_NR]; + struct { + u32 ldpc; + u32 bcc; + u32 stbc; + u32 su_bf; + u32 su_non_bf; + u32 mu; + } rx; }; #define RTW89_BCN_TRACK_STAT_NR 32 diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index d9a5bbae11f2..103948aee5cb 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4303,6 +4303,7 @@ static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, char *buf, size_t bufsz) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; const struct rtw89_physts_regs *physts = phy->physts; struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; struct rtw89_tx_stat_info *tx_stat = &bb->tx_stat; @@ -4384,6 +4385,13 @@ static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, tx_stat->common_ctrl[i], (i < reg_nr - 1) ? ", " : ""); p += scnprintf(p, end - p, "]\n\n"); + p += scnprintf(p, end - p, "== RX General\n"); + p += scnprintf(p, end - p, + "LDPC: %d, BCC: %d, STBC: %d, SU_NON_BF: %d, SU_BF: %d, MU: %d\n\n", + pkt_stat->rx.ldpc, pkt_stat->rx.bcc, + pkt_stat->rx.stbc, pkt_stat->rx.su_non_bf, + pkt_stat->rx.su_bf, pkt_stat->rx.mu); + p += scnprintf(p, end - p, "== RSSI/RX Rate\n"); p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index b69a2529aefc..125ba2a9f145 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -634,8 +634,10 @@ struct rtw89_phy_sts_ie01 { #define RTW89_PHY_STS_IE01_W2_AVG_SNR GENMASK(5, 0) #define RTW89_PHY_STS_IE01_W2_EVM_MAX GENMASK(15, 8) #define RTW89_PHY_STS_IE01_W2_EVM_MIN GENMASK(23, 16) +#define RTW89_PHY_STS_IE01_W2_SU BIT(27) #define RTW89_PHY_STS_IE01_W2_LDPC BIT(28) #define RTW89_PHY_STS_IE01_W2_STBC BIT(30) +#define RTW89_PHY_STS_IE01_W3_BF BIT(8) struct rtw89_phy_sts_ie01_v2 { __le32 w0; From c1ed02655f9134d6af6a01a58b734329c2f4f22c Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:25 +0800 Subject: [PATCH 050/105] wifi: rtw89: debug: add BB diagnose Add BB diagnostic to track potential abnormal conditions. Currently, five diagnostic metrics are monitored: 1) Hang detection monitors consecutive absence of TX and RX activity. 2) PD maximum triggers when PD stays at its maximum threshold for a period. 3) No RX occurs when no CCA activity is detected over multiple consecutive cycles. 4) High FA indicates a high false alarm ratio, reflecting severe environmental interference. 5) EDCCA alerts when high EDCCA ratio, signaling a potential TX hang. These metrics are exposed via debugfs diag_bb. Output: [PHY 0] Diag bitmap = 0x0 Event{Hang, PD MAX, No RX, High FA, High EDCCA Ratio} = {0, 0, 0, 0, 0} consecutive_no_tx_cnt=0, consecutive_no_rx_cnt=0 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 23 ++++ drivers/net/wireless/realtek/rtw89/debug.c | 43 ++++++ drivers/net/wireless/realtek/rtw89/phy.c | 144 +++++++++++++++++++- drivers/net/wireless/realtek/rtw89/phy.h | 4 + drivers/net/wireless/realtek/rtw89/phy_be.c | 8 ++ drivers/net/wireless/realtek/rtw89/reg.h | 8 ++ 6 files changed, 227 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c0e176c4c3a6..bccfee7535a7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5578,6 +5578,7 @@ struct rtw89_pmac_stat_info { u32 ofdm_phy_txon; u32 cnt_ofdm_cca; u32 cnt_cck_cca; + u32 cnt_cca_all; u32 cnt_cck_spoofing; u32 cnt_ofdm_spoofing; u32 cnt_ampdu_miss; @@ -5601,6 +5602,7 @@ struct rtw89_pmac_stat_info { u32 cnt_sig_gg; u32 cnt_cck_fail; u32 cnt_ofdm_fail; + u32 cnt_fail_all; u32 cnt_lsig_brk_s_th; u32 cnt_lsig_brk_l_th; u32 cnt_parity_fail; @@ -5624,6 +5626,23 @@ struct rtw89_tx_stat_info { bool stbc; }; +enum rtw89_diag_bb_type { + RTW89_DIAG_BB_HANG, + RTW89_DIAG_BB_PD, + RTW89_DIAG_BB_NO_RX, + RTW89_DIAG_BB_FA, + RTW89_DIAG_BB_EDCCA, + + RTW89_DIAG_BB_NR, +}; + +struct rtw89_diag_bb { + u32 diag_bb_bitmap; /* bitmap of enum rtw89_diag_bb_type */ + u32 diag_bb_cnt[RTW89_DIAG_BB_NR]; + u16 consecutive_no_tx_cnt; + u16 consecutive_no_rx_cnt; +}; + struct rtw89_agc_gaincode_set { u8 lna_idx; u8 tia_idx; @@ -5639,6 +5658,7 @@ struct rtw89_dig_info { struct rtw89_agc_gaincode_set cur_gaincode; bool force_gaincode_idx_en; struct rtw89_agc_gaincode_set force_gaincode; + enum rtw89_dig_noisy_level noisy_lv; u8 igi_rssi_th[IGI_RSSI_TH_NUM]; u16 fa_th[FA_TH_NUM]; u8 igi_rssi; @@ -5648,6 +5668,7 @@ struct rtw89_dig_info { u8 dyn_igi_min; bool dyn_pd_th_en; u8 dyn_pd_th_max; + u8 dyn_pd_max_cnt; u8 pd_low_th_ofst; u8 ib_pbk; s8 ib_pkpwr; @@ -5912,6 +5933,7 @@ struct rtw89_env_monitor_info { u8 nhm_th[RTW89_NHM_RPT_NUM]; struct rtw89_nhm_report *nhm_his[RTW89_BAND_NUM]; struct list_head nhm_rpt_list; + u8 edcca_clm_ratio; }; enum rtw89_ser_rcvy_step { @@ -6452,6 +6474,7 @@ struct rtw89_dev { struct rtw89_pkt_stat last_pkt_stat; struct rtw89_pmac_stat_info pmac_stat; struct rtw89_tx_stat_info tx_stat; + struct rtw89_diag_bb diag; } bbs[RTW89_PHY_NUM]; struct wiphy_delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 103948aee5cb..597052261974 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -92,6 +92,7 @@ struct rtw89_debugfs { struct rtw89_debugfs_priv mlo_mode; struct rtw89_debugfs_priv beacon_info; struct rtw89_debugfs_priv diag_mac; + struct rtw89_debugfs_priv diag_bb; }; struct rtw89_debugfs_iter_data { @@ -5214,6 +5215,46 @@ rtw89_debug_priv_diag_mac_get(struct rtw89_dev *rtwdev, return rtw89_mac_diag_iter_all(rtwdev, buf, bufsz); } +static int rtw89_get_diag_bb(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) +{ + struct rtw89_diag_bb *diag = &bb->diag; + char *p = buf, *end = buf + bufsz; + + p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx); + p += scnprintf(p, end - p, "Diag bitmap = 0x%x\n", diag->diag_bb_bitmap); + p += scnprintf(p, end - p, + "Event{Hang, PD MAX, No RX, High FA, High EDCCA Ratio} = "); + p += scnprintf(p, end - p, "{%d, %d, %d, %d, %d}\n", + diag->diag_bb_cnt[RTW89_DIAG_BB_HANG], + diag->diag_bb_cnt[RTW89_DIAG_BB_PD], + diag->diag_bb_cnt[RTW89_DIAG_BB_NO_RX], + diag->diag_bb_cnt[RTW89_DIAG_BB_FA], + diag->diag_bb_cnt[RTW89_DIAG_BB_EDCCA]); + p += scnprintf(p, end - p, + "consecutive_no_tx_cnt=%d, consecutive_no_rx_cnt=%d\n\n", + diag->consecutive_no_tx_cnt, + diag->consecutive_no_rx_cnt); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + char *p = buf, *end = buf + bufsz; + struct rtw89_bb_ctx *bb; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + rtw89_for_each_active_bb(rtwdev, bb) + p += rtw89_get_diag_bb(rtwdev, bb, p, end - p); + + return p - buf; +} + static int rtw89_get_beacon_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, char *buf, size_t bufsz) { @@ -5348,6 +5389,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = { .mlo_mode = rtw89_debug_priv_set_and_get(mlo_mode, RWLOCK), .beacon_info = rtw89_debug_priv_get(beacon_info), .diag_mac = rtw89_debug_priv_get(diag_mac, RSIZE_16K, RLOCK), + .diag_bb = rtw89_debug_priv_get(diag_bb, RSIZE_8K, RLOCK), }; #define rtw89_debugfs_add(name, mode, fopname, parent) \ @@ -5398,6 +5440,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_rw(mlo_mode); rtw89_debugfs_add_r(beacon_info); rtw89_debugfs_add_r(diag_mac); + rtw89_debugfs_add_r(diag_bb); } void rtw89_debugfs_init(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index cf7382e36b75..f98a77f9fc1a 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5847,6 +5847,50 @@ static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) rtwdev->hal.thermal_prot_lv = 0; } +static void rtw89_phy_pmac_diag(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ +#define RTW89_DIAG_BB_FA_RATIO_TH 80 +#define RTW89_DIAG_BB_NO_RX_TH 5 +#define RTW89_DIAG_BB_HANG_TH 30 + struct rtw89_pmac_stat_info *pmac_stat = &bb->pmac_stat; + bool is_linked = rtwdev->total_sta_assoc > 0; + struct rtw89_diag_bb *diag = &bb->diag; + u8 fa_ratio = 0; + + if (!(pmac_stat->cck_mac_txen || pmac_stat->ofdm_mac_txen)) + diag->consecutive_no_tx_cnt++; + else + diag->consecutive_no_tx_cnt = 0; + + if (!pmac_stat->cnt_cca_all) + diag->consecutive_no_rx_cnt++; + else + diag->consecutive_no_rx_cnt = 0; + + if (pmac_stat->cnt_cca_all) + fa_ratio = pmac_stat->cnt_fail_all * PERCENT / pmac_stat->cnt_cca_all; + + diag->diag_bb_bitmap &= ~(BIT(RTW89_DIAG_BB_NO_RX) | + BIT(RTW89_DIAG_BB_HANG) | + BIT(RTW89_DIAG_BB_FA)); + + if (diag->consecutive_no_rx_cnt >= RTW89_DIAG_BB_NO_RX_TH && is_linked) { + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_NO_RX); + diag->diag_bb_cnt[RTW89_DIAG_BB_NO_RX]++; + } + + if (fa_ratio >= RTW89_DIAG_BB_FA_RATIO_TH && is_linked) { + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_FA); + diag->diag_bb_cnt[RTW89_DIAG_BB_FA]++; + } + + if (diag->consecutive_no_tx_cnt > RTW89_DIAG_BB_HANG_TH && + diag->consecutive_no_rx_cnt > RTW89_DIAG_BB_HANG_TH) { + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_HANG); + diag->diag_bb_cnt[RTW89_DIAG_BB_HANG]++; + } +} + static void rtw89_phy_pmac_stat_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, bool cck) { @@ -6017,6 +6061,14 @@ static void rtw89_phy_pmac_stat_update(struct rtw89_dev *rtwdev, rtw89_phy_pmac_stat_ofdm(rtwdev, bb); + pmac->cnt_fail_all = pmac->cnt_ofdm_fail; + pmac->cnt_cca_all = pmac->cnt_ofdm_cca; + + if (cck) { + pmac->cnt_fail_all += pmac->cnt_cck_fail; + pmac->cnt_cca_all += pmac->cnt_cck_cca; + } + pmac->cnt_crc32_error_all = pmac->cnt_he_crc32_error + pmac->cnt_vht_crc32_error + pmac->cnt_ht_crc32_error + @@ -6034,6 +6086,7 @@ static void rtw89_phy_pmac_stat_update(struct rtw89_dev *rtwdev, pmac->cnt_crc32_ok_all += pmac->cnt_eht_crc32_ok; } + rtw89_phy_pmac_diag(rtwdev, bb); rtw89_phy_pmac_stat_reset(rtwdev, bb, cck); } @@ -6722,6 +6775,29 @@ static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev, return true; } +static bool rtw89_phy_edcca_clm_get_result(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + struct rtw89_env_monitor_info *env = &bb->env_monitor; + const struct rtw89_ccx_regs *ccx = phy->ccx; + u32 val; + + val = rtw89_phy_read32_idx(rtwdev, ccx->edcca_clm_rdy, + ccx->edcca_clm_rdy_mask, bb->phy_idx); + if (!val) { + rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, + "Get EDCCA_CLM report Fail\n"); + return false; + } + + val = rtw89_phy_read32_idx(rtwdev, ccx->edcca_clm_cnt, + ccx->edcca_clm_cnt_mask, bb->phy_idx); + env->edcca_clm_ratio = rtw89_phy_ccx_get_report(rtwdev, bb, val, PERCENT); + + return true; +} + static void rtw89_phy_nhm_th_update(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { @@ -6889,6 +6965,9 @@ static void __rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev, if (rtw89_phy_ifs_clm_get_result(rtwdev, bb)) env->ccx_watchdog_result |= RTW89_PHY_ENV_MON_IFS_CLM; + if (rtw89_phy_edcca_clm_get_result(rtwdev, bb)) + env->ccx_watchdog_result |= RTW89_PHY_ENV_MON_EDCCA_CLM; + rtw89_phy_ccx_racing_release(rtwdev, bb); para.mntr_time = 1900; para.rac_lv = RTW89_RAC_LV_1; @@ -7195,6 +7274,7 @@ static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev, dig->dyn_igi_max = igi_max_performance_mode; dig->dyn_igi_min = dynamic_igi_min; dig->dyn_pd_th_max = dynamic_pd_threshold_max; + dig->dyn_pd_max_cnt = 0; dig->pd_low_th_ofst = pd_low_th_offset; dig->is_linked_pre = false; } @@ -7318,6 +7398,7 @@ static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev, igi_offset = min_t(u8, igi_offset, IGI_OFFSET_MAX); dig->fa_rssi_ofst = igi_offset; + dig->noisy_lv = noisy_lv; rtw89_debug(rtwdev, RTW89_DBG_DIG, "fa_th: [+6 (%d) +4 (%d) +2 (%d) 0 (%d) -2 ]\n", @@ -7548,10 +7629,33 @@ void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) rtw89_phy_dig_update_para(rtwdev, bb); } +static void rtw89_phy_dig_diag(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool is_linked) +{ +#define RTW89_DIAG_BB_PD_MAX_PERIOD 5 + struct rtw89_diag_bb *diag = &bb->diag; + struct rtw89_dig_info *dig = &bb->dig; + + diag->diag_bb_bitmap &= ~BIT(RTW89_DIAG_BB_PD); + + if (dig->dyn_igi_max != dig->igi_fa_rssi || + dig->noisy_lv < RTW89_DIG_NOISY_LEVEL_MAX) { + dig->dyn_pd_max_cnt = 0; + return; + } + + if (++dig->dyn_pd_max_cnt < RTW89_DIAG_BB_PD_MAX_PERIOD || !is_linked) + return; + + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_PD); + diag->diag_bb_cnt[RTW89_DIAG_BB_PD]++; +} + #define IGI_RSSI_MIN 10 #define ABS_IGI_MIN 0xc static -void rtw89_phy_cal_igi_fa_rssi(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +void rtw89_phy_cal_igi_fa_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool is_linked) { struct rtw89_dig_info *dig = &bb->dig; u8 igi_min; @@ -7569,6 +7673,8 @@ void rtw89_phy_cal_igi_fa_rssi(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb } else { dig->igi_fa_rssi = dig->dyn_igi_max; } + + rtw89_phy_dig_diag(rtwdev, bb, is_linked); } struct rtw89_phy_iter_mcc_dig { @@ -7597,7 +7703,7 @@ static void rtw89_phy_set_mcc_dig(struct rtw89_dev *rtwdev, } chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); - rtw89_phy_cal_igi_fa_rssi(rtwdev, bb); + rtw89_phy_cal_igi_fa_rssi(rtwdev, bb, is_linked); pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, dig->igi_fa_rssi, is_linked, chan); rtw89_fw_h2c_mcc_dig(rtwdev, rtwvif_link->chanctx_idx, @@ -7742,7 +7848,7 @@ static void __rtw89_phy_dig(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) } dig->is_linked_pre = is_linked; - rtw89_phy_cal_igi_fa_rssi(rtwdev, bb); + rtw89_phy_cal_igi_fa_rssi(rtwdev, bb, is_linked); rtw89_debug(rtwdev, RTW89_DBG_DIG, "rssi=%03d, dyn_joint(max,min)=(%d,%d), final_rssi=%d\n", @@ -7997,6 +8103,14 @@ static void rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev) __rtw89_phy_env_monitor_init(rtwdev, bb); } +static void rtw89_phy_diag_init(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb; + + rtw89_for_each_capab_bb(rtwdev, bb) + memset(&bb->diag, 0, sizeof(bb->diag)); +} + static void __rtw89_phy_edcca_init(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { @@ -8037,6 +8151,7 @@ void rtw89_phy_dm_init(struct rtw89_dev *rtwdev) rtw89_chip_bb_sethw(rtwdev); + rtw89_phy_diag_init(rtwdev); rtw89_phy_env_monitor_init(rtwdev); rtw89_phy_nhm_setting_init(rtwdev); rtw89_physts_parsing_init(rtwdev); @@ -8613,6 +8728,24 @@ void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb edcca_regs->ppdu_mask, th, bb->phy_idx); } +static void rtw89_phy_edcca_diag(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ +#define RTW89_DIAG_BB_EDCCA_RATIO_TH 50 +#define RTW89_DIAG_BB_EDCCA_TX_TH 10 + struct rtw89_env_monitor_info *env = &bb->env_monitor; + struct rtw89_traffic_stats *stats = &rtwdev->stats; + struct rtw89_diag_bb *diag = &bb->diag; + + diag->diag_bb_bitmap &= ~BIT(RTW89_DIAG_BB_EDCCA); + + if (stats->tx_throughput <= RTW89_DIAG_BB_EDCCA_TX_TH || + env->edcca_clm_ratio <= RTW89_DIAG_BB_EDCCA_RATIO_TH) + return; + + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_EDCCA); + diag->diag_bb_cnt[RTW89_DIAG_BB_EDCCA]++; +} + static void __rtw89_phy_edcca_track(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { @@ -8620,6 +8753,7 @@ void __rtw89_phy_edcca_track(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) rtw89_phy_edcca_thre_calc(rtwdev, bb); rtw89_phy_edcca_log(rtwdev, bb); + rtw89_phy_edcca_diag(rtwdev, bb); } void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev) @@ -8770,6 +8904,10 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_ax = { .nhm_en_mask = B_NHM_EN_MSK, .nhm_method = R_NHM_TH9, .nhm_pwr_method_msk = B_NHM_PWDB_METHOD_MSK, + .edcca_clm_rdy = R_CLM_EDCCA_RDY, + .edcca_clm_rdy_mask = B_CLM_EDCCA_RDY, + .edcca_clm_cnt = R_CLM_EDCCA_RESULT, + .edcca_clm_cnt_mask = B_CLM_EDCCA_RESULT, }; static const u32 rtw89_txinfo_reg_ax[] = { diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 830cabefca39..a6e685654037 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -471,6 +471,10 @@ struct rtw89_ccx_regs { u32 nhm_en_mask; u32 nhm_method; u32 nhm_pwr_method_msk; + u32 edcca_clm_rdy; + u32 edcca_clm_rdy_mask; + u32 edcca_clm_cnt; + u32 edcca_clm_cnt_mask; }; struct rtw89_physts_regs { diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 9caddd19384c..5cd298a2c91b 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -74,6 +74,10 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_be = { .nhm_en_mask = B_NHM_EN_MSK, .nhm_method = R_NHM_TH9, .nhm_pwr_method_msk = B_NHM_PWDB_METHOD_MSK, + .edcca_clm_rdy = R_CLM_EDCCA_RDY_V1, + .edcca_clm_rdy_mask = B_CLM_EDCCA_RDY, + .edcca_clm_cnt = R_CLM_EDCCA_RESULT_V1, + .edcca_clm_cnt_mask = B_CLM_EDCCA_RESULT, }; static const struct rtw89_ccx_regs rtw89_ccx_regs_be_v1 = { @@ -133,6 +137,10 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_be_v1 = { .ifs_total_addr = R_IFS_TOTAL_BE4, .ifs_cnt_done_mask = B_IFS_CNT_DONE_BE4, .ifs_total_mask = B_IFS_TOTAL_BE4, + .edcca_clm_rdy = R_CLM_EDCCA_RDY_BE4, + .edcca_clm_rdy_mask = B_CLM_EDCCA_RDY, + .edcca_clm_cnt = R_CLM_EDCCA_RESULT_BE4, + .edcca_clm_cnt_mask = B_CLM_EDCCA_RESULT, }; static const u32 rtw89_tx_info_reg_be[] = { diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index f5151e1ad58d..716747c3a10e 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -9146,6 +9146,14 @@ #define B_NHM_CNT11_MSK GENMASK(31, 16) #define R_NHM_AX 0x1AA4 #define B_NHM_READY_MSK BIT(16) +#define R_CLM_EDCCA_RESULT 0x1AC8 +#define R_CLM_EDCCA_RESULT_V1 0x0EC8 +#define R_CLM_EDCCA_RESULT_BE4 0x20EC8 +#define B_CLM_EDCCA_RESULT GENMASK(15, 0) +#define R_CLM_EDCCA_RDY 0x1AC8 +#define R_CLM_EDCCA_RDY_V1 0x0EC8 +#define R_CLM_EDCCA_RDY_BE4 0x20EC8 +#define B_CLM_EDCCA_RDY BIT(16) #define R_IFS_CLM_TX_CNT 0x1ACC #define R_IFS_CLM_TX_CNT_V1 0x0ECC #define R_IFS_CLM_TX_CNT_BE4 0x20ECC From 756dc69e5639cd339be3d501ad068b01a2b366de Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:47 +0800 Subject: [PATCH 051/105] wifi: rtw89: add AMPDU to radiotap The RX desc can report current frame is in AMPDU, but no way point out if it is a last one in AMPDU. Update AMPDU reference only. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 8 ++++++++ drivers/net/wireless/realtek/rtw89/core.h | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index b85728ceb63c..240502b25b2e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3568,6 +3568,7 @@ void rtw89_core_query_rxdesc(struct rtw89_dev *rtwdev, desc_info->hw_dec = le32_get_bits(rxd_s->dword3, AX_RXD_HW_DEC); desc_info->sw_dec = le32_get_bits(rxd_s->dword3, AX_RXD_SW_DEC); desc_info->addr1_match = le32_get_bits(rxd_s->dword3, AX_RXD_A1_MATCH); + desc_info->ampdu = le32_get_bits(rxd_s->dword3, AX_RXD_AMPDU); shift_len = desc_info->shift << 1; /* 2-byte unit */ drv_info_len = desc_info->drv_info_size << 3; /* 8-byte unit */ @@ -3624,6 +3625,7 @@ void rtw89_core_query_rxdesc_v2(struct rtw89_dev *rtwdev, desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC); desc_info->sw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_SW_DEC); desc_info->addr1_match = le32_get_bits(rxd_s->dword3, BE_RXD_A1_MATCH); + desc_info->ampdu = le32_get_bits(rxd_s->dword3, BE_RXD_AMPDU); desc_info->bw = le32_get_bits(rxd_s->dword4, BE_RXD_BW_MASK); desc_info->data_rate = le32_get_bits(rxd_s->dword4, BE_RXD_RX_DATARATE_MASK); @@ -3698,6 +3700,7 @@ void rtw89_core_query_rxdesc_v3(struct rtw89_dev *rtwdev, desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC); desc_info->sw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_SW_DEC); desc_info->addr1_match = le32_get_bits(rxd_s->dword3, BE_RXD_A1_MATCH); + desc_info->ampdu = le32_get_bits(rxd_s->dword3, BE_RXD_AMPDU); desc_info->bw = le32_get_bits(rxd_s->dword4, BE_RXD_BW_MASK); desc_info->data_rate = le32_get_bits(rxd_s->dword4, BE_RXD_RX_DATARATE_MASK); @@ -3830,6 +3833,11 @@ static void rtw89_core_update_rx_status(struct rtw89_dev *rtwdev, !(desc_info->sw_dec || desc_info->icv_err)) rx_status->flag |= RX_FLAG_DECRYPTED; + if (desc_info->ampdu) { + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + rx_status->ampdu_reference = desc_info->ppdu_cnt; + } + rx_status->bw = rtw89_hw_to_rate_info_bw(desc_info->bw); data_rate = desc_info->data_rate; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index bccfee7535a7..66dbb1fc3ca8 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1130,6 +1130,7 @@ struct rtw89_rx_desc_info { bool hw_dec; bool sw_dec; bool addr1_match; + bool ampdu; u8 frag; u16 seq; u8 frame_type; From 9535e2279ba994fcb38c58f508589cd394894f90 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:48 +0800 Subject: [PATCH 052/105] wifi: rtw89: add VHT beamformed to radiotap Set VHT beamformed bit by PHY status IE-01 report. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 240502b25b2e..d6bf1d57e8e1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2099,6 +2099,7 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, phy_ppdu->chan_idx = le32_get_bits(ie->w0, RTW89_PHY_STS_IE01_W0_CH_IDX); phy_ppdu->ldpc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_LDPC); phy_ppdu->stbc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_STBC); + phy_ppdu->bf = le32_get_bits(ie->w3, RTW89_PHY_STS_IE01_W3_BF); if (!phy_ppdu->hdr_2_en) phy_ppdu->rx_path_en = @@ -2115,7 +2116,6 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, phy_ppdu->ofdm.avg_snr = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_AVG_SNR); phy_ppdu->ofdm.evm_max = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MAX); phy_ppdu->ofdm.evm_min = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MIN); - phy_ppdu->bf = le32_get_bits(ie->w3, RTW89_PHY_STS_IE01_W3_BF); phy_ppdu->ofdm.has = true; /* sign conversion for S(12,2) */ @@ -3146,6 +3146,8 @@ void rtw89_core_update_rx_status_by_ppdu(struct rtw89_dev *rtwdev, if (!phy_ppdu) return; + if (phy_ppdu->bf) + rx_status->enc_flags |= RX_ENC_FLAG_BF; if (phy_ppdu->ldpc) rx_status->enc_flags |= RX_ENC_FLAG_LDPC; if (phy_ppdu->stbc) @@ -6848,7 +6850,8 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | IEEE80211_RADIOTAP_MCS_HAVE_STBC; - hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC; + hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC | + IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED; ieee80211_hw_set(hw, SIGNAL_DBM); ieee80211_hw_set(hw, HAS_RATE_CONTROL); From a371139d8be96f851c8663f9f8e4ffe3c3271bac Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:49 +0800 Subject: [PATCH 053/105] wifi: rtw89: SNIFFER_MODE bit along IEEE80211_CONF_MONITOR The SNIFFER_MODE bit can ignore filter rules, and receive packets to driver, so set the bit to accept all packets. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac80211.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index b72f6661fbd1..2c6711133c80 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -356,6 +356,11 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, } } + if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) + rtwdev->hal.rx_fltr |= B_AX_SNIFFER_MODE; + else + rtwdev->hal.rx_fltr &= ~B_AX_SNIFFER_MODE; + rx_fltr = rtwdev->hal.rx_fltr; /* mac80211 doesn't configure filter when HW scan, driver need to From c847d0c14c47c2878908c6e9bdcfb707bdb88b60 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:50 +0800 Subject: [PATCH 054/105] wifi: rtw89: phy: define PHY status IE length for generations Both RTL8922A and RTL8922D are WiFi 7 chips, but their IE length of PHY status are different. Define them accordingly. Generation 0: WiFi 6 chips Generation 1: WiFi 7 RTL8922A Generation 2: WiFi 7 RTL8922D Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 19 ++----------------- drivers/net/wireless/realtek/rtw89/phy.c | 4 ++++ drivers/net/wireless/realtek/rtw89/phy.h | 5 +++++ drivers/net/wireless/realtek/rtw89/phy_be.c | 8 ++++++++ 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index d6bf1d57e8e1..caedb2bd21d5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2040,29 +2040,14 @@ static void rtw89_core_rx_process_phy_ppdu_iter(void *data, } } -#define VAR_LEN 0xff -#define VAR_LEN_UNIT 8 static u16 rtw89_core_get_phy_status_ie_len(struct rtw89_dev *rtwdev, const struct rtw89_phy_sts_iehdr *iehdr) { - static const u8 physts_ie_len_tabs[RTW89_CHIP_GEN_NUM][32] = { - [RTW89_CHIP_AX] = { - 16, 32, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, - VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 16, 24, VAR_LEN, - VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32 - }, - [RTW89_CHIP_BE] = { - 32, 40, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, - VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 88, 56, VAR_LEN, - VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32 - }, - }; - const u8 *physts_ie_len_tab; + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const u8 *physts_ie_len_tab = phy->physt_ie_len; u16 ie_len; u8 ie; - physts_ie_len_tab = physts_ie_len_tabs[rtwdev->chip->chip_gen]; - ie = le32_get_bits(iehdr->w0, RTW89_PHY_STS_IEHDR_TYPE); if (physts_ie_len_tab[ie] != VAR_LEN) ie_len = physts_ie_len_tab[ie]; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index f98a77f9fc1a..4f82b1a9fa4c 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -8948,6 +8948,10 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_ax = { .cr_base = 0x10000, .physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START, .physt_bmp_eht = 0xfc, + .physt_ie_len = {16, 32, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, + VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 16, 24, VAR_LEN, + VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32}, + .physt_gen = 0, .ccx = &rtw89_ccx_regs_ax, .physts = &rtw89_physts_regs_ax, .cfo = &rtw89_cfo_regs_ax, diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index a6e685654037..d8038ae5ca86 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -154,6 +154,9 @@ #define EDCCA_UNIT_CONVER 128 #define EDCCA_PWROFST_DEFAULT 18 +#define VAR_LEN 0xff +#define VAR_LEN_UNIT 8 + enum rtw89_phy_c2h_ra_func { RTW89_PHY_C2H_FUNC_STS_RPT, RTW89_PHY_C2H_FUNC_MU_GPTBL_RPT, @@ -573,6 +576,8 @@ struct rtw89_phy_gen_def { u32 cr_base; u32 physt_bmp_start; u32 physt_bmp_eht; + u8 physt_ie_len[32]; + u8 physt_gen; const struct rtw89_ccx_regs *ccx; const struct rtw89_physts_regs *physts; const struct rtw89_cfo_regs *cfo; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 5cd298a2c91b..23137f2dbd4b 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -1561,6 +1561,10 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be = { .cr_base = 0x20000, .physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START, .physt_bmp_eht = R_PHY_STS_BITMAP_EHT, + .physt_ie_len = {32, 40, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, + VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 88, 56, VAR_LEN, + VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32}, + .physt_gen = 1, .ccx = &rtw89_ccx_regs_be, .physts = &rtw89_physts_regs_be, .cfo = &rtw89_cfo_regs_be, @@ -1582,6 +1586,10 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be_v1 = { .cr_base = 0x0, .physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START_BE4, .physt_bmp_eht = R_PHY_STS_BITMAP_EHT_BE4, + .physt_ie_len = {32, 40, 24, 24, 16, 16, 16, 16, VAR_LEN, VAR_LEN, VAR_LEN, 168, + VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 32, 56, + 96, VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32}, + .physt_gen = 2, .ccx = &rtw89_ccx_regs_be_v1, .physts = &rtw89_physts_regs_be_v1, .cfo = &rtw89_cfo_regs_be_v1, From 847758f15f466235df9bf64272dbdd3ded14520e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:51 +0800 Subject: [PATCH 055/105] wifi: rtw89: phy: enable IE-09/IE-10 PHY status report for monitor mode The IE-09/IE-10 of PHY status contain SIG-A/SIG-B respectively, so enable them in monitor mode to have rich information. If the parser detects length invalid, ignore to reference IE-09/IE-10 to prevent accessing out of range. The RTL8922D is generation 2 of PHY status, which doesn't report SIG-B by IE-10, so not enable it. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 43 +++++++++++++++++-- drivers/net/wireless/realtek/rtw89/core.h | 6 ++- drivers/net/wireless/realtek/rtw89/mac80211.c | 3 ++ drivers/net/wireless/realtek/rtw89/phy.c | 17 +++++++- drivers/net/wireless/realtek/rtw89/phy.h | 1 + drivers/net/wireless/realtek/rtw89/txrx.h | 9 ++++ 6 files changed, 74 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index caedb2bd21d5..f877c2707c84 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2152,14 +2152,38 @@ static void rtw89_core_parse_phy_status_ie00_v2(struct rtw89_dev *rtwdev, rpl_path[i] = tmp_rpl[i] >> 1; } +static void rtw89_core_parse_phy_status_ie09(struct rtw89_dev *rtwdev, + const struct rtw89_phy_sts_iehdr *iehdr, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + phy_ppdu->ie09 = (const void *)iehdr; +} + +static void rtw89_core_parse_phy_status_ie10(struct rtw89_dev *rtwdev, + const struct rtw89_phy_sts_iehdr *iehdr, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + phy_ppdu->ie10 = (const void *)iehdr; +} + static int rtw89_core_process_phy_status_ie(struct rtw89_dev *rtwdev, const struct rtw89_phy_sts_iehdr *iehdr, struct rtw89_rx_phy_ppdu *phy_ppdu) { + bool accept; u8 ie; ie = le32_get_bits(iehdr->w0, RTW89_PHY_STS_IEHDR_TYPE); + /* + * For normal mode, only parse ppdu_sts that are A1-matched, except for + * scanning that needs to get chan_idx in IE01. + */ + accept = phy_ppdu->to_self || ie == RTW89_PHYSTS_IE01_CMN_OFDM || + rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR; + if (!accept) + return 0; + switch (ie) { case RTW89_PHYSTS_IE00_CMN_CCK: rtw89_core_parse_phy_status_ie00(rtwdev, iehdr, phy_ppdu); @@ -2169,6 +2193,12 @@ static int rtw89_core_process_phy_status_ie(struct rtw89_dev *rtwdev, case RTW89_PHYSTS_IE01_CMN_OFDM: rtw89_core_parse_phy_status_ie01(rtwdev, iehdr, phy_ppdu); break; + case RTW89_PHYSTS_IE09_FTR_0: + rtw89_core_parse_phy_status_ie09(rtwdev, iehdr, phy_ppdu); + break; + case RTW89_PHYSTS_IE10_FTR_PLCP_EXT: + rtw89_core_parse_phy_status_ie10(rtwdev, iehdr, phy_ppdu); + break; default: break; } @@ -2228,11 +2258,14 @@ static int rtw89_core_rx_process_phy_ppdu(struct rtw89_dev *rtwdev, static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu) { - u16 ie_len; void *pos, *end; + bool accept; + u16 ie_len; - /* mark invalid reports and bypass them */ - if (phy_ppdu->ie < RTW89_CCK_PKT) + /* for normal mode, mark invalid reports and bypass them */ + accept = phy_ppdu->ie >= RTW89_CCK_PKT || + rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR; + if (!accept) return -EINVAL; pos = phy_ppdu->buf + PHY_STS_HDR_LEN; @@ -2246,6 +2279,10 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, rtw89_core_process_phy_status_ie(rtwdev, iehdr, phy_ppdu); pos += ie_len; if (pos > end || ie_len == 0) { + /* clear pointers to prevent accessing out of IE */ + phy_ppdu->ie09 = NULL; + phy_ppdu->ie10 = NULL; + rtw89_debug(rtwdev, RTW89_DBG_TXRX, "phy status parse failed\n"); return -EINVAL; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 66dbb1fc3ca8..ca716e95cb2c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -24,6 +24,8 @@ struct rtw89_h2c_rf_tssi; struct rtw89_fw_txpwr_track_cfg; struct rtw89_phy_rfk_log_fmt; struct rtw89_phy_calc_efuse_gain; +struct rtw89_phy_sts_ie09; +struct rtw89_phy_sts_ie10; struct rtw89_debugfs; struct rtw89_regd_data; struct rtw89_wow_cam_info; @@ -840,7 +842,7 @@ struct rtw89_rx_phy_ppdu { u8 mac_id; u8 chan_idx; u8 phy_idx; - u8 ie; + u8 ie; /* enum rtw89_phy_status_bitmap */ u16 rate; u8 rpl_avg; u8 rpl_path[RF_PATH_MAX]; @@ -862,6 +864,8 @@ struct rtw89_rx_phy_ppdu { bool to_self; bool valid; bool hdr_2_en; + const struct rtw89_phy_sts_ie09 *ie09; /* SIG-A */ + const struct rtw89_phy_sts_ie10 *ie10; /* SIG-B */ }; enum rtw89_mac_idx { diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 2c6711133c80..330ece51286d 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -98,6 +98,9 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) !rtwdev->scanning) rtw89_enter_ips(rtwdev); + if (changed & IEEE80211_CONF_CHANGE_MONITOR) + rtw89_physts_parsing_init(rtwdev); + return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 4f82b1a9fa4c..15483a86951d 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -7085,10 +7085,21 @@ static void rtw89_physts_enable_hdr_2(struct rtw89_dev *rtwdev, enum rtw89_phy_i static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; const struct rtw89_chip_info *chip = rtwdev->chip; + u32 monitor_mode_mu_ies = 0; + u32 monitor_mode_su_ies = 0; u32 val; u8 i; + if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) { + monitor_mode_mu_ies = BIT(RTW89_PHYSTS_IE09_FTR_0); + if (phy->physt_gen < 2) + monitor_mode_mu_ies |= BIT(RTW89_PHYSTS_IE10_FTR_PLCP_EXT); + + monitor_mode_su_ies = BIT(RTW89_PHYSTS_IE09_FTR_0); + } + rtw89_physts_enable_fail_report(rtwdev, false, phy_idx); /* enable hdr_2 for 8922D (PHYSTS_BE_GEN2 above) */ @@ -7102,6 +7113,7 @@ static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev, val = rtw89_physts_get_ie_bitmap(rtwdev, i, phy_idx); if (i == RTW89_HE_MU || i == RTW89_VHT_MU) { val |= BIT(RTW89_PHYSTS_IE13_DL_MU_DEF); + val |= monitor_mode_mu_ies; } else if (i == RTW89_TRIG_BASE_PPDU) { val |= BIT(RTW89_PHYSTS_IE13_DL_MU_DEF) | BIT(RTW89_PHYSTS_IE01_CMN_OFDM); @@ -7115,11 +7127,14 @@ static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev, val |= BIT(RTW89_PHYSTS_IE20_DBG_OFDM_FD_USER_SEG_0); } + if (i == RTW89_HE_PKT || i == RTW89_VHT_PKT) + val |= monitor_mode_su_ies; + rtw89_physts_set_ie_bitmap(rtwdev, i, val, phy_idx); } } -static void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev) +void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev) { __rtw89_physts_parsing_init(rtwdev, RTW89_PHY_0); if (rtwdev->dbcc_en) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index d8038ae5ca86..74fbf5baff58 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -914,6 +914,7 @@ void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, void rtw89_phy_dm_init(struct rtw89_dev *rtwdev); void rtw89_phy_dm_reinit(struct rtw89_dev *rtwdev); void rtw89_phy_dm_init_data(struct rtw89_dev *rtwdev); +void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev); void rtw89_phy_write32_idx(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u32 data, enum rtw89_phy_idx phy_idx); void rtw89_phy_write32_idx_set(struct rtw89_dev *rtwdev, u32 addr, u32 bits, diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index 125ba2a9f145..18fe6d3d0f83 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -658,6 +658,15 @@ struct rtw89_phy_sts_ie01_v2 { #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_C GENMASK(11, 4) #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_D GENMASK(23, 16) +struct rtw89_phy_sts_ie09 { + __le64 qw0; +} __packed; + +struct rtw89_phy_sts_ie10 { + __le64 qw0; + u8 sigb[]; +} __packed; + enum rtw89_tx_channel { RTW89_TXCH_ACH0 = 0, RTW89_TXCH_ACH1 = 1, From bc1006b68b89dcc2abc3dc79f9be6fa53642461c Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:52 +0800 Subject: [PATCH 056/105] wifi: rtw89: move HE radiotap to an individual function To implement more fields of HE radiotap, move the code to an individual function ahead. Not change logic at all. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 36 +++++++++++++---------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index f877c2707c84..a27e482e38a6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3176,6 +3176,24 @@ void rtw89_core_update_rx_status_by_ppdu(struct rtw89_dev *rtwdev, rx_status->enc_flags |= u8_encode_bits(1, RX_ENC_FLAG_STBC_MASK); } +static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) +{ + static const struct ieee80211_radiotap_he known_he = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), + }; + struct ieee80211_radiotap_he *he; + + rx_status->flag |= RX_FLAG_RADIOTAP_HE; + he = skb_push(skb, sizeof(*he)); + *he = known_he; +} + static const u8 rx_status_bw_to_radiotap_eht_usig[] = { [RATE_INFO_BW_20] = IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_20MHZ, [RATE_INFO_BW_5] = U8_MAX, @@ -3250,25 +3268,13 @@ static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status) { - static const struct ieee80211_radiotap_he known_he = { - .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), - .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), - }; - struct ieee80211_radiotap_he *he; - if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR)) return; - if (rx_status->encoding == RX_ENC_HE) { - rx_status->flag |= RX_FLAG_RADIOTAP_HE; - he = skb_push(skb, sizeof(*he)); - *he = known_he; - } else if (rx_status->encoding == RX_ENC_EHT) { + if (rx_status->encoding == RX_ENC_HE) + rtw89_core_update_radiotap_he(rtwdev, skb, rx_status); + else if (rx_status->encoding == RX_ENC_EHT) rtw89_core_update_radiotap_eht(rtwdev, skb, rx_status); - } } static void rtw89_core_validate_rx_signal(struct ieee80211_rx_status *rx_status) From 236b25d9308d345c8f05130da0bba1af0e7908a8 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:53 +0800 Subject: [PATCH 057/105] wifi: rtw89: fill VHT radiotap Fill VHT radiotap by PHY status IE-09 which contains VHT SIG-A. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 53 +++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/core.h | 4 +- drivers/net/wireless/realtek/rtw89/txrx.h | 26 +++++++++++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index a27e482e38a6..2a9326335c53 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3176,6 +3176,50 @@ void rtw89_core_update_rx_status_by_ppdu(struct rtw89_dev *rtwdev, rx_status->enc_flags |= u8_encode_bits(1, RX_ENC_FLAG_STBC_MASK); } +static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + const struct rtw89_phy_sts_ie09 *ie09; + struct ieee80211_radiotap_vht *vht; + u8 group_id; + u32 sig_a1; + u16 paid; + u8 nss; + + if (!phy_ppdu) + return; + + ie09 = phy_ppdu->ie09; + if (!ie09) + return; + + vht = skb_push(skb, sizeof(*vht)); + memset(vht, 0, sizeof(*vht)); + rx_status->flag |= RX_FLAG_RADIOTAP_VHT; + + sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK); + + group_id = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_GRP_ID); + vht->group_id = group_id; + + if (group_id == 0 || group_id == 63) { + paid = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_PAID); + vht->partial_aid = cpu_to_le16(paid); + } else { + /* let mac80211 fill vht->mcs_nss[0] */ +#define WITH_MCS_IS_NOT_KNOWN(nss) ((nss) ? (15 << 4) | (nss) : 0) + nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS1); + vht->mcs_nss[1] = WITH_MCS_IS_NOT_KNOWN(nss); + nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS2); + vht->mcs_nss[2] = WITH_MCS_IS_NOT_KNOWN(nss); + nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS3); + vht->mcs_nss[3] = WITH_MCS_IS_NOT_KNOWN(nss); +#undef WITH_MCS_IS_NOT_KNOWN + } +} + static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status) @@ -3266,12 +3310,15 @@ static void rtw89_core_update_radiotap_eht(struct rtw89_dev *rtwdev, static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) + struct ieee80211_rx_status *rx_status, + struct rtw89_rx_phy_ppdu *phy_ppdu) { if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR)) return; - if (rx_status->encoding == RX_ENC_HE) + if (rx_status->encoding == RX_ENC_VHT) + rtw89_core_update_radiotap_vht(rtwdev, skb, rx_status, phy_ppdu); + else if (rx_status->encoding == RX_ENC_HE) rtw89_core_update_radiotap_he(rtwdev, skb, rx_status); else if (rx_status->encoding == RX_ENC_EHT) rtw89_core_update_radiotap_eht(rtwdev, skb, rx_status); @@ -3484,7 +3531,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, rtw89_core_hw_to_sband_rate(rx_status); rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu); rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu); - rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status); + rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status, phy_ppdu); rtw89_core_validate_rx_signal(rx_status); rtw89_core_update_rx_freq_from_ie(rtwdev, skb_ppdu, rx_status); rtw89_core_correct_mcc_chan(rtwdev, desc_info, rx_status, phy_ppdu); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ca716e95cb2c..d1cd5997cf97 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -58,6 +58,7 @@ extern const struct ieee80211_ops rtw89_ops; #define RTW89_TX_DIV_RSSI_RAW_TH (2 << RSSI_FACTOR) #define DELTA_SWINGIDX_SIZE 30 +#define RTW89_RADIOTAP_ROOM_VHT sizeof(struct ieee80211_radiotap_vht) #define RTW89_RADIOTAP_ROOM_HE sizeof(struct ieee80211_radiotap_he) #define RTW89_RADIOTAP_ROOM_EHT \ (sizeof(struct ieee80211_radiotap_tlv) + \ @@ -65,7 +66,8 @@ extern const struct ieee80211_ops rtw89_ops; sizeof(struct ieee80211_radiotap_tlv) + \ ALIGN(sizeof(struct ieee80211_radiotap_eht_usig), 4)) #define RTW89_RADIOTAP_ROOM \ - ALIGN(max(RTW89_RADIOTAP_ROOM_HE, RTW89_RADIOTAP_ROOM_EHT), 64) + ALIGN(max3(RTW89_RADIOTAP_ROOM_VHT, RTW89_RADIOTAP_ROOM_HE, \ + RTW89_RADIOTAP_ROOM_EHT), 64) #define RTW89_HTC_MASK_VARIANT GENMASK(1, 0) #define RTW89_HTC_VARIANT_HE 3 diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index 18fe6d3d0f83..ddf8742d6712 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -662,6 +662,32 @@ struct rtw89_phy_sts_ie09 { __le64 qw0; } __packed; +#define RTW89_PHY_STS_IE09_L_SIG_MASK GENMASK(21, 5) /* Legacy/VHT/HE L-SIG */ +#define RTW89_PHY_STS_IE09_L_SIG_RATE GENMASK(3, 0) +#define RTW89_PHY_STS_IE09_L_SIG_RSVD BIT(4) +#define RTW89_PHY_STS_IE09_L_SIG_LENGTH GENMASK(16, 5) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK GENMASK_ULL(45, 22) /* VHT SIG-A1 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_BW GENMASK(1, 0) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD1 BIT(2) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_STBC BIT(3) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_GRP_ID GENMASK(9, 4) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_NSTS GENMASK(12, 10) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_PAID GENMASK(21, 13) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS0 GENMASK(12, 10) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS1 GENMASK(15, 13) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS2 GENMASK(18, 16) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS3 GENMASK(21, 19) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_TXOP_NOPS BIT(22) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD2 BIT(23) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK GENMASK_ULL(55, 46) /* VHT SIG-A2 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI BIT(0) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI_DISA BIT(1) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE0 BIT(2) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_LDPC BIT(3) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE3 GENMASK(7, 4) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_BF BIT(8) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_RSVD3 BIT(9) + struct rtw89_phy_sts_ie10 { __le64 qw0; u8 sigb[]; From 16a1becdd1fb9a600b46691694298be16101821a Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:54 +0800 Subject: [PATCH 058/105] wifi: rtw89: fill HE-SU/HE-TB/HE-MU/HE-EXT_SU radiotap Fill HE radiotap by PHY status IE-09/IE-10 which contains HE SIG-A/SIG-B respectively. The IE-10 may contain two content channels (if bandwidth is larger than 40MHz), and starting address of second content channel can be calculated by length of first content channel containing up to 15 user fields with 8-byte alignment. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 393 +++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/core.h | 18 +- drivers/net/wireless/realtek/rtw89/txrx.h | 54 +++ 3 files changed, 461 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 2a9326335c53..7fa479ce52e2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3220,9 +3220,361 @@ static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev, } } +static void rtw89_core_update_radiotap_he_su(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_he *he, + const struct rtw89_phy_sts_ie09 *ie09) +{ + u32 sig_a1, sig_a2; + u16 t; + + if (!ie09) + return; + + sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK); + sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK); + + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN | + 0); + + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN | + 0); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BEAM_CHANGE); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_ULDL); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_UL_DL); + + rx_status->he_dcm = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_DCM); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BSS_COLOR); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_SR); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_TXOP); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_LDPC_XSYMSEG); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_BEAMFORMED); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_TXBF); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PREFEC); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PE); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_DOPPLER); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_DOPPLER); +} + +static void rtw89_core_update_radiotap_he_tb(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_he *he, + const struct rtw89_phy_sts_ie09 *sig) +{ + u32 sig_a1, sig_a2; + u16 t; + + if (!sig) + return; + + sig_a1 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK); + sig_a2 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK); + + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN); + + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BSS_COLOR); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB1); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1); + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB2); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2); + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB3); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3); + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB4); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_TXOP); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP); +} + +static u8 rtw89_core_he_mu_get_n_user_fields_by_ru_alloc(struct rtw89_dev *rtwdev, + u8 ru_alloc) +{ + u8 mu_users; + + if (ru_alloc <= 15) { + static const u8 entries_0_15[16] = { + 9, 8, 8, 7, 8, 7, 7, 6, 8, 7, 7, 6, 7, 6, 6, 5, + }; + + return entries_0_15[ru_alloc]; + } + + if (ru_alloc >= 16 && ru_alloc <= 95) { + mu_users = ru_alloc & 0x07; + + switch (ru_alloc & 0xF8) { + case 16: case 24: return mu_users + 3; + case 32: case 64: return mu_users + 6; + case 40: case 48: case 72: case 80: return mu_users + 5; + case 56: case 88: return mu_users + 4; + } + } + + if (ru_alloc >= 96 && ru_alloc <= 111) { + mu_users = ru_alloc & 0x03; + mu_users += ru_alloc >> 2 & 0x03; + + return mu_users + 2; + } + + if (ru_alloc == 112) + return 4; + + if (ru_alloc >= 113 && ru_alloc <= 115) + return 0; + + if (ru_alloc >= 128 && ru_alloc <= 191) { + mu_users = ru_alloc & 0x07; + mu_users += ru_alloc >> 3 & 0x07; + + return mu_users + 3; + } + + if (ru_alloc >= 192 && ru_alloc <= 215) + return (ru_alloc & 0x07) + 1; + + return 0; +} + +static int rtw89_core_he_mu_get_n_ru_alloc(struct rtw89_dev *rtwdev, u8 bw) +{ + switch (bw) { + case 0: + case 1: + return 1; + case 2: + case 4: + case 5: + return 2; + case 3: + case 6: + case 7: + return 3; + } + + return 0; +} + +static void rtw89_core_he_mu_get_cc_ptr(struct rtw89_dev *rtwdev, + const struct rtw89_phy_sts_ie10 *ie10, + u8 bw, int n_ru, int n_center_26tone, + const u8 **c1, const u8 **c2) +{ + const u8 *sigb, *end_sigb; + int total_bits, c2_offset; + int n_user_fields = 0; + const u8 *ptr; + int i; + + sigb = &ie10->sigb[0]; + end_sigb = (const void *)ie10 + + rtw89_core_get_phy_status_ie_len(rtwdev, (const void *)ie10); + + ptr = sigb; + if (ptr + n_ru + n_center_26tone >= end_sigb) + return; + *c1 = ptr; + + if (bw == 0) /* 20MHz doesn't have content channel 2 */ + return; + + for (i = 0; i < n_ru; i++) + n_user_fields += + rtw89_core_he_mu_get_n_user_fields_by_ru_alloc(rtwdev, (*c1)[i]); + + /* hardware report max 15 recordes, align 8 */ + n_user_fields = min(n_user_fields, 15); + + total_bits = n_ru * 8 + n_center_26tone + 10 + + n_user_fields * 21 + DIV_ROUND_UP(n_user_fields, 2) * 10; + c2_offset = ALIGN(DIV_ROUND_UP(total_bits, 8), 8); + + ptr = &sigb[c2_offset]; + if (ptr + n_ru + n_center_26tone >= end_sigb) + return; + + *c2 = ptr; +} + +static void rtw89_core_update_radiotap_he_mu(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_he *he, + struct ieee80211_radiotap_he_mu *he_mu, + const struct rtw89_phy_sts_ie09 *ie09, + const struct rtw89_phy_sts_ie10 *ie10) +{ + const u8 *c1 = NULL, *c2 = NULL; + int n_center_26tone, n_ru, i; + bool doppler, comp; + u32 sig_a1, sig_a2; + u16 t; + u8 bw; + + if (!ie09) + return; + + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN); + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN); + + sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK); + sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_UL_DL); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BSS_COLOR); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SR); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE); + + doppler = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_DOPPLER); + he->data6 |= le16_encode_bits(doppler, IEEE80211_RADIOTAP_HE_DATA6_DOPPLER); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_TXOP); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP); + + if (doppler) { + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MID); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_MIDAMBLE_PDCTY); + } + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LPDC_XSYMSEG); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_FEC); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_PE); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG); + + he_mu->flags1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN); + he_mu->flags2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_MCS); + he_mu->flags1 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_DCM); + he_mu->flags1 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_SYM_USR); + he_mu->flags2 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS); + + comp = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_COM); + he_mu->flags2 |= le16_encode_bits(comp, IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP); + + bw = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BW); + he_mu->flags2 |= le16_encode_bits(bw, IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW); + + if (comp == 0 && bw <= 5) { + u8 punc = clamp_t(int, bw - 3, 0, 2); + + he_mu->flags2 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN); + he_mu->flags2 |= + le16_encode_bits(punc, IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW); + } + + /* the Common field in the HE-SIG-B field is not present */ + if (comp || !ie10) + return; + + n_ru = rtw89_core_he_mu_get_n_ru_alloc(rtwdev, bw); + n_center_26tone = bw >= 1; + + rtw89_core_he_mu_get_cc_ptr(rtwdev, ie10, bw, n_ru, n_center_26tone, &c1, &c2); + if (!c1) + return; + + he_mu->flags1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN); + + for (i = 0; i < n_ru; i++) { + he_mu->ru_ch1[i] = c1[i]; + if (c2) + he_mu->ru_ch2[i] = c2[i]; + } + + if (n_center_26tone) { + u8 ru; + + ru = c1[n_ru] & BIT(0); + he_mu->flags1 |= + le16_encode_bits(ru, IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU); + + ru = c2 ? c2[n_ru] & BIT(0) : 0; + he_mu->flags1 |= + le16_encode_bits(ru, IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU); + } +} + +static u16 rtw89_core_get_radiotap_he_format(struct rtw89_rx_desc_info *desc_info) +{ + switch (desc_info->ppdu_type) { + case RTW89_RX_PPDU_T_HE_MU: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU; + case RTW89_RX_PPDU_T_HE_ERSU: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU; + case RTW89_RX_PPDU_T_HE_TB: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG; + default: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU; + } +} + static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev, + struct rtw89_rx_desc_info *desc_info, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) + struct ieee80211_rx_status *rx_status, + struct rtw89_rx_phy_ppdu *phy_ppdu) { static const struct ieee80211_radiotap_he known_he = { .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | @@ -3231,11 +3583,45 @@ static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev, IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), }; + struct ieee80211_radiotap_he_mu *he_mu = NULL; + const struct rtw89_phy_sts_ie09 *ie09; + const struct rtw89_phy_sts_ie10 *ie10; struct ieee80211_radiotap_he *he; + u16 he_format; + + he_format = rtw89_core_get_radiotap_he_format(desc_info); + + /* Radiotap of HE-MU must be placed after HE (skb_push ahead) */ + if (he_format == IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU) { + rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU; + he_mu = skb_push(skb, sizeof(*he_mu)); + memset(he_mu, 0, sizeof(*he_mu)); + } rx_status->flag |= RX_FLAG_RADIOTAP_HE; he = skb_push(skb, sizeof(*he)); *he = known_he; + + he->data1 |= le16_encode_bits(he_format, IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MASK); + + if (!phy_ppdu) + return; + + ie09 = phy_ppdu->ie09; + ie10 = phy_ppdu->ie10; + + switch (he_format) { + case IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU: + rtw89_core_update_radiotap_he_mu(rtwdev, skb, rx_status, he, he_mu, + ie09, ie10); + break; + case IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG: + rtw89_core_update_radiotap_he_tb(rtwdev, skb, rx_status, he, ie09); + break; + default: + rtw89_core_update_radiotap_he_su(rtwdev, skb, rx_status, he, ie09); + break; + } } static const u8 rx_status_bw_to_radiotap_eht_usig[] = { @@ -3309,6 +3695,7 @@ static void rtw89_core_update_radiotap_eht(struct rtw89_dev *rtwdev, } static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, + struct rtw89_rx_desc_info *desc_info, struct sk_buff *skb, struct ieee80211_rx_status *rx_status, struct rtw89_rx_phy_ppdu *phy_ppdu) @@ -3319,7 +3706,7 @@ static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, if (rx_status->encoding == RX_ENC_VHT) rtw89_core_update_radiotap_vht(rtwdev, skb, rx_status, phy_ppdu); else if (rx_status->encoding == RX_ENC_HE) - rtw89_core_update_radiotap_he(rtwdev, skb, rx_status); + rtw89_core_update_radiotap_he(rtwdev, desc_info, skb, rx_status, phy_ppdu); else if (rx_status->encoding == RX_ENC_EHT) rtw89_core_update_radiotap_eht(rtwdev, skb, rx_status); } @@ -3531,7 +3918,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, rtw89_core_hw_to_sband_rate(rx_status); rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu); rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu); - rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status, phy_ppdu); + rtw89_core_update_radiotap(rtwdev, desc_info, skb_ppdu, rx_status, phy_ppdu); rtw89_core_validate_rx_signal(rx_status); rtw89_core_update_rx_freq_from_ie(rtwdev, skb_ppdu, rx_status); rtw89_core_correct_mcc_chan(rtwdev, desc_info, rx_status, phy_ppdu); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d1cd5997cf97..b85d7fa4296a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -59,7 +59,8 @@ extern const struct ieee80211_ops rtw89_ops; #define DELTA_SWINGIDX_SIZE 30 #define RTW89_RADIOTAP_ROOM_VHT sizeof(struct ieee80211_radiotap_vht) -#define RTW89_RADIOTAP_ROOM_HE sizeof(struct ieee80211_radiotap_he) +#define RTW89_RADIOTAP_ROOM_HE (sizeof(struct ieee80211_radiotap_he) + \ + sizeof(struct ieee80211_radiotap_he_mu)) #define RTW89_RADIOTAP_ROOM_EHT \ (sizeof(struct ieee80211_radiotap_tlv) + \ ALIGN(struct_size((struct ieee80211_radiotap_eht *)0, user_info, 1), 4) + \ @@ -955,6 +956,21 @@ enum rtw89_bandwidth { RTW89_CHANNEL_WIDTH_10 = 7, }; +enum rtw89_rx_ppdu_type { + RTW89_RX_PPDU_T_LCCK = 0, + RTW89_RX_PPDU_T_SCCK = 1, + RTW89_RX_PPDU_T_OFDM = 2, + RTW89_RX_PPDU_T_HT = 3, + RTW89_RX_PPDU_T_HTGF = 4, + RTW89_RX_PPDU_T_VHT_SU = 5, + RTW89_RX_PPDU_T_VHT_MU = 6, + RTW89_RX_PPDU_T_HE_SU = 7, + RTW89_RX_PPDU_T_HE_ERSU = 8, + RTW89_RX_PPDU_T_HE_MU = 9, + RTW89_RX_PPDU_T_HE_TB = 10, + RTW89_RX_PPDU_T_UNKNOWN = 15, +}; + enum rtw89_ps_mode { RTW89_PS_MODE_NONE = 0, RTW89_PS_MODE_RFOFF = 1, diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index ddf8742d6712..4a567d3fe3f7 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -687,6 +687,60 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE3 GENMASK(7, 4) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_BF BIT(8) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_RSVD3 BIT(9) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE SU SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_FORMAT BIT(0) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BEAM_CHANGE BIT(1) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_ULDL BIT(2) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MCS GENMASK(6, 3) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_DCM BIT(7) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BSS_COLOR GENMASK(13, 8) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_RSVD1 BIT(14) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_SR GENMASK(18, 15) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BW GENMASK(20, 19) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_GILTF GENMASK(22, 21) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_NSTS GENMASK(25, 23) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE SU SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_TXOP GENMASK(6, 0) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_CODING BIT(7) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_LDPC_XSYMSEG BIT(8) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_STBC BIT(9) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_BEAMFORMED BIT(10) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PREFEC GENMASK(12, 11) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PE BIT(13) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_RSVD2 BIT(14) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_DOPPLER BIT(15) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE TB SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_FORMAT BIT(0) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BSS_COLOR GENMASK(6, 1) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB1 GENMASK(10, 7) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB2 GENMASK(14, 11) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB3 GENMASK(18, 15) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB4 GENMASK(22, 19) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_RSVD1 BIT(23) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BW GENMASK(25, 24) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE TB SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_TXOP GENMASK(6, 0) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_RSVD1 GENMASK(15, 7) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE MU SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL BIT(0) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_MCS GENMASK(3, 1) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_DCM BIT(4) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BSS_COLOR GENMASK(10, 5) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SR GENMASK(14, 11) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BW GENMASK(17, 15) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_SYM_USR GENMASK(21, 18) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_COM BIT(22) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_GI_LTF GENMASK(24, 23) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_DOPPLER BIT(25) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE MU SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_TXOP GENMASK(6, 0) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_RSVD BIT(7) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LTF_MID GENMASK(10, 8) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MID BIT(10) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LPDC_XSYMSEG BIT(11) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_STBC BIT(12) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_FEC GENMASK(14, 13) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_PE BIT(15) struct rtw89_phy_sts_ie10 { __le64 qw0; From 41d78f3f35961ca533cff5bbc159e7fb3c32e3df Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:55 +0800 Subject: [PATCH 059/105] wifi: rtw89: debug: make implementation of beacon_info entry in order When adding more debugfs entries, the beacon_info entry becomes not in order. Move to correct location. Don't change logic at all. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 142 ++++++++++----------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 597052261974..6fe8e7254e80 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4919,6 +4919,77 @@ rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev, return count; } +static int rtw89_get_beacon_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) +{ + struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; + char *p = buf, *end = buf + bufsz; + + p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx); + p += scnprintf(p, end - p, "Beacon: %u\n", pkt_stat->beacon_nr); + p += scnprintf(p, end - p, "raw rssi: %lu\n", ewma_rssi_read(&bb->bcn_rssi)); + p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate); + p += scnprintf(p, end - p, "length: %u\n\n", pkt_stat->beacon_len); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track; + struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat; + struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist; + u16 upper, lower = bcn_stat->tbtt_tu_min; + char *p = buf, *end = buf + bufsz; + u16 *drift = bcn_stat->drift; + u8 bcn_num = bcn_stat->num; + struct rtw89_bb_ctx *bb; + u8 count; + u8 i; + + rtw89_for_each_active_bb(rtwdev, bb) + p += rtw89_get_beacon_info(rtwdev, bb, p, end - p); + + p += scnprintf(p, end - p, "[Beacon info]\n"); + p += scnprintf(p, end - p, "interval: %u\n", bcn_track->beacon_int); + p += scnprintf(p, end - p, "dtim: %u\n", bcn_track->dtim); + + p += scnprintf(p, end - p, "\n[Distribution]\n"); + p += scnprintf(p, end - p, "tbtt\n"); + for (i = 0; i < RTW89_BCN_TRACK_MAX_BIN_NUM; i++) { + upper = lower + RTW89_BCN_TRACK_BIN_WIDTH - 1; + if (i == RTW89_BCN_TRACK_MAX_BIN_NUM - 1) + upper = max(upper, bcn_stat->tbtt_tu_max); + + p += scnprintf(p, end - p, "%02u - %02u: %u\n", + lower, upper, bcn_dist->bins[i]); + + lower = upper + 1; + } + + p += scnprintf(p, end - p, "\ndrift\n"); + + for (i = 0; i < bcn_num; i += count) { + count = 1; + while (i + count < bcn_num && drift[i] == drift[i + count]) + count++; + + p += scnprintf(p, end - p, "%u: %u\n", drift[i], count); + } + p += scnprintf(p, end - p, "\nlower bound: %u\n", bcn_dist->lower_bound); + p += scnprintf(p, end - p, "upper bound: %u\n", bcn_dist->upper_bound); + p += scnprintf(p, end - p, "outlier count: %u\n", bcn_dist->outlier_count); + + p += scnprintf(p, end - p, "\n[Tracking]\n"); + p += scnprintf(p, end - p, "tbtt offset: %u\n", bcn_track->tbtt_offset); + p += scnprintf(p, end - p, "bcn timeout: %u\n", bcn_track->bcn_timeout); + + return p - buf; +} + enum __diag_mac_cmd { __CMD_EQUALV, __CMD_EQUALO, @@ -5255,77 +5326,6 @@ rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev, return p - buf; } -static int rtw89_get_beacon_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, - char *buf, size_t bufsz) -{ - struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; - char *p = buf, *end = buf + bufsz; - - p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx); - p += scnprintf(p, end - p, "Beacon: %u\n", pkt_stat->beacon_nr); - p += scnprintf(p, end - p, "raw rssi: %lu\n", ewma_rssi_read(&bb->bcn_rssi)); - p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate); - p += scnprintf(p, end - p, "length: %u\n\n", pkt_stat->beacon_len); - - return p - buf; -} - -static ssize_t -rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, - struct rtw89_debugfs_priv *debugfs_priv, - char *buf, size_t bufsz) -{ - struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track; - struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat; - struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist; - u16 upper, lower = bcn_stat->tbtt_tu_min; - char *p = buf, *end = buf + bufsz; - u16 *drift = bcn_stat->drift; - u8 bcn_num = bcn_stat->num; - struct rtw89_bb_ctx *bb; - u8 count; - u8 i; - - rtw89_for_each_active_bb(rtwdev, bb) - p += rtw89_get_beacon_info(rtwdev, bb, p, end - p); - - p += scnprintf(p, end - p, "[Beacon info]\n"); - p += scnprintf(p, end - p, "interval: %u\n", bcn_track->beacon_int); - p += scnprintf(p, end - p, "dtim: %u\n", bcn_track->dtim); - - p += scnprintf(p, end - p, "\n[Distribution]\n"); - p += scnprintf(p, end - p, "tbtt\n"); - for (i = 0; i < RTW89_BCN_TRACK_MAX_BIN_NUM; i++) { - upper = lower + RTW89_BCN_TRACK_BIN_WIDTH - 1; - if (i == RTW89_BCN_TRACK_MAX_BIN_NUM - 1) - upper = max(upper, bcn_stat->tbtt_tu_max); - - p += scnprintf(p, end - p, "%02u - %02u: %u\n", - lower, upper, bcn_dist->bins[i]); - - lower = upper + 1; - } - - p += scnprintf(p, end - p, "\ndrift\n"); - - for (i = 0; i < bcn_num; i += count) { - count = 1; - while (i + count < bcn_num && drift[i] == drift[i + count]) - count++; - - p += scnprintf(p, end - p, "%u: %u\n", drift[i], count); - } - p += scnprintf(p, end - p, "\nlower bound: %u\n", bcn_dist->lower_bound); - p += scnprintf(p, end - p, "upper bound: %u\n", bcn_dist->upper_bound); - p += scnprintf(p, end - p, "outlier count: %u\n", bcn_dist->outlier_count); - - p += scnprintf(p, end - p, "\n[Tracking]\n"); - p += scnprintf(p, end - p, "tbtt offset: %u\n", bcn_track->tbtt_offset); - p += scnprintf(p, end - p, "bcn timeout: %u\n", bcn_track->bcn_timeout); - - return p - buf; -} - #define rtw89_debug_priv_get(name, opts...) \ { \ .cb_read = rtw89_debug_priv_ ##name## _get, \ From cd92d278c1864852cb4840c5e51c1cd0eaa60e85 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:56 +0800 Subject: [PATCH 060/105] wifi: rtw89: add debugfs entry of monitor mode options to capture HE-MU packets To capture HE-MU packets, set BSS color and AID for specific connected station. The writing format: For example, $ echo 0x4 0x16 > monitor_opts Read this entry to get current setting: bss_color=0x4 aid=0x16 By the way, add another sec2() function to create debugfs entries to prevent running smatch timeout. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 57 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.c | 23 ++++++--- drivers/net/wireless/realtek/rtw89/phy.h | 2 + 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 6fe8e7254e80..8ee800c76cfe 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 monitor_opts; }; struct rtw89_debugfs_iter_data { @@ -5326,6 +5327,54 @@ rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev, return p - buf; } +static ssize_t +rtw89_debug_priv_monitor_opts_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + char *p = buf, *end = buf + bufsz; + u32 bss_color; + u32 aid; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + rtw89_leave_ps_mode(rtwdev); + + bss_color = rtw89_phy_read32_idx(rtwdev, chip->bss_clr_map_reg, + B_BSS_CLR_MAP_TGT, RTW89_PHY_0); + aid = rtw89_phy_read32_idx(rtwdev, chip->bss_clr_map_reg, + B_BSS_CLR_MAP_STAID, RTW89_PHY_0); + + p += scnprintf(p, end - p, "bss_color=0x%x aid=0x%x\n", bss_color, aid); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_monitor_opts_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) +{ + u32 bss_color; + u32 aid; + int num; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + num = sscanf(buf, "%x %x", &bss_color, &aid); + if (num != 2) { + rtw89_info(rtwdev, "valid format: \n"); + return -EINVAL; + } + + rtw89_leave_ps_mode(rtwdev); + + __rtw89_phy_set_bss_color(rtwdev, bss_color, aid, RTW89_PHY_0); + + return count; +} + #define rtw89_debug_priv_get(name, opts...) \ { \ .cb_read = rtw89_debug_priv_ ##name## _get, \ @@ -5390,6 +5439,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), + .monitor_opts = rtw89_debug_priv_set_and_get(monitor_opts, RWLOCK), }; #define rtw89_debugfs_add(name, mode, fopname, parent) \ @@ -5435,12 +5485,18 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_r(phy_info); rtw89_debugfs_add_rw(bb_info); rtw89_debugfs_add_r(stations); +} + +static +void rtw89_debugfs_add_sec2(struct rtw89_dev *rtwdev, struct dentry *debugfs_topdir) +{ rtw89_debugfs_add_rw(disable_dm); rtw89_debugfs_add_rw(static_pd_th); rtw89_debugfs_add_rw(mlo_mode); rtw89_debugfs_add_r(beacon_info); rtw89_debugfs_add_r(diag_mac); rtw89_debugfs_add_r(diag_bb); + rtw89_debugfs_add_rw(monitor_opts); } void rtw89_debugfs_init(struct rtw89_dev *rtwdev) @@ -5457,6 +5513,7 @@ void rtw89_debugfs_init(struct rtw89_dev *rtwdev) rtw89_debugfs_add_sec0(rtwdev, debugfs_topdir); rtw89_debugfs_add_sec1(rtwdev, debugfs_topdir); + rtw89_debugfs_add_sec2(rtwdev, debugfs_topdir); } void rtw89_debugfs_deinit(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 15483a86951d..759be4dab42b 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -8231,12 +8231,24 @@ void rtw89_phy_dm_init_data(struct rtw89_dev *rtwdev) __rtw89_phy_dm_init_data(rtwdev, bb); } +void __rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, u8 bss_color, u16 aid, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_reg_def *bss_clr_vld = &chip->bss_clr_vld; + + rtw89_phy_write32_idx(rtwdev, bss_clr_vld->addr, bss_clr_vld->mask, 0x1, + phy_idx); + rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_TGT, + bss_color, phy_idx); + rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_STAID, + aid, phy_idx); +} + void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); - const struct rtw89_chip_info *chip = rtwdev->chip; - const struct rtw89_reg_def *bss_clr_vld = &chip->bss_clr_vld; enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; struct ieee80211_bss_conf *bss_conf; u8 bss_color; @@ -8253,12 +8265,7 @@ void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, rcu_read_unlock(); - rtw89_phy_write32_idx(rtwdev, bss_clr_vld->addr, bss_clr_vld->mask, 0x1, - phy_idx); - rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_TGT, - bss_color, phy_idx); - rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_STAID, - vif->cfg.aid, phy_idx); + __rtw89_phy_set_bss_color(rtwdev, bss_color, vif->cfg.aid, phy_idx); } static bool rfk_chan_validate_desc(const struct rtw89_rfk_chan_desc *desc) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 74fbf5baff58..b4b818f786df 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -1128,6 +1128,8 @@ void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev); void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); +void __rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, u8 bss_color, u16 aid, + enum rtw89_phy_idx phy_idx); void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx, enum rtw89_tssi_bandedge_cfg bandedge_cfg); From 884495c39de1a02f42bd40051b921e2311d6ac91 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:57 +0800 Subject: [PATCH 061/105] wifi: rtw89: phy: check length before parsing PHY status IE Hardware might report PHY status IE with unexpected length, and parser might access out of range. Check the length ahead. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 7fa479ce52e2..7e1182a49942 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2276,7 +2276,6 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, const struct rtw89_phy_sts_iehdr *iehdr = pos; ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, iehdr); - rtw89_core_process_phy_status_ie(rtwdev, iehdr, phy_ppdu); pos += ie_len; if (pos > end || ie_len == 0) { /* clear pointers to prevent accessing out of IE */ @@ -2285,8 +2284,11 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, rtw89_debug(rtwdev, RTW89_DBG_TXRX, "phy status parse failed\n"); + return -EINVAL; } + + rtw89_core_process_phy_status_ie(rtwdev, iehdr, phy_ppdu); } rtw89_chip_convert_rpl_to_rssi(rtwdev, phy_ppdu); From 48402f736f93e6377ee47984d9dd2aa1b6815604 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:58 +0800 Subject: [PATCH 062/105] wifi: rtw89: phy: skip trailing 8-byte zeros of PHY status IE for RTL8922D Hardware reports a list of PHY status IE. In monitor mode, IE-09 of PHY status is enabled, and the report contains trailing 8-byte zeros, causing failed to parse and drop all IE information. The 8 zeros are recognize as IE type 0, but length of type 0 must be not 8 (reference to rtw89_phy_gen_def::physt_ie_len[0]). Check and skip them. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 7e1182a49942..fddb0c822c28 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2275,6 +2275,13 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, while (pos < end) { const struct rtw89_phy_sts_iehdr *iehdr = pos; + /* + * RTL8922D might reports 8 bytes zeros at end if IE09 presents. + * Check and ignore the zeros. + */ + if (unlikely(phy_ppdu->ie09 && end - pos == 8 && iehdr->w0 == 0)) + break; + ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, iehdr); pos += ie_len; if (pos > end || ie_len == 0) { From 0805ddc2fdb64c142eb2be8429497dfd8c9bf296 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:59 +0800 Subject: [PATCH 063/105] wifi: rtw89: phy: support PHY status IE-09 GEN2 for RTL8922D The format of PHY status IE-10 for RTL8922D is different from earlier chips. Fortunately only starting bit is different, but the layout is the same. Get the VHT/HE SIG-A value by corresponding mask accordingly. The IE-09 format of generation 0 and 1 are totally the same. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-14-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 72 +++++++++++++++++++---- drivers/net/wireless/realtek/rtw89/core.h | 4 +- drivers/net/wireless/realtek/rtw89/txrx.h | 23 +++++++- 3 files changed, 82 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index fddb0c822c28..c21737981b61 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2156,7 +2156,19 @@ static void rtw89_core_parse_phy_status_ie09(struct rtw89_dev *rtwdev, const struct rtw89_phy_sts_iehdr *iehdr, struct rtw89_rx_phy_ppdu *phy_ppdu) { - phy_ppdu->ie09 = (const void *)iehdr; + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const union rtw89_phy_sts_ie09 *ie09; + u16 ie_len; + + ie09 = (const void *)iehdr; + + if (phy->physt_gen >= 2) { + ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, (const void *)iehdr); + if (ie_len < sizeof(ie09->gen2)) + return; + } + + phy_ppdu->ie09 = ie09; } static void rtw89_core_parse_phy_status_ie10(struct rtw89_dev *rtwdev, @@ -3190,7 +3202,8 @@ static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev, struct ieee80211_rx_status *rx_status, struct rtw89_rx_phy_ppdu *phy_ppdu) { - const struct rtw89_phy_sts_ie09 *ie09; + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const union rtw89_phy_sts_ie09 *ie09; struct ieee80211_radiotap_vht *vht; u8 group_id; u32 sig_a1; @@ -3208,7 +3221,10 @@ static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev, memset(vht, 0, sizeof(*vht)); rx_status->flag |= RX_FLAG_RADIOTAP_VHT; - sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK); + if (phy->physt_gen >= 2) + sig_a1 = le64_get_bits(ie09->gen2.qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK_GEN2); + else + sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK); group_id = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_GRP_ID); vht->group_id = group_id; @@ -3233,16 +3249,26 @@ static void rtw89_core_update_radiotap_he_su(struct rtw89_dev *rtwdev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status, struct ieee80211_radiotap_he *he, - const struct rtw89_phy_sts_ie09 *ie09) + const union rtw89_phy_sts_ie09 *ie09) { + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; u32 sig_a1, sig_a2; u16 t; if (!ie09) return; - sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK); - sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK); + if (phy->physt_gen >= 2) { + sig_a1 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK_GEN2); + sig_a2 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_L0); + sig_a2 |= le64_get_bits(ie09->gen2.qw1, + RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_H6) << 6; + } else { + sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK); + sig_a2 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK); + } he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN | IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | @@ -3295,16 +3321,26 @@ static void rtw89_core_update_radiotap_he_tb(struct rtw89_dev *rtwdev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status, struct ieee80211_radiotap_he *he, - const struct rtw89_phy_sts_ie09 *sig) + const union rtw89_phy_sts_ie09 *sig) { + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; u32 sig_a1, sig_a2; u16 t; if (!sig) return; - sig_a1 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK); - sig_a2 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK); + if (phy->physt_gen >= 2) { + sig_a1 = le64_get_bits(sig->gen2.qw0, + RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK_GEN2); + sig_a2 = le64_get_bits(sig->gen2.qw0, + RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_L0); + sig_a2 |= le64_get_bits(sig->gen2.qw1, + RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_H6); + } else { + sig_a1 = le64_get_bits(sig->gen0.qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK); + sig_a2 = le64_get_bits(sig->gen0.qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK); + } he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | @@ -3445,9 +3481,10 @@ static void rtw89_core_update_radiotap_he_mu(struct rtw89_dev *rtwdev, struct ieee80211_rx_status *rx_status, struct ieee80211_radiotap_he *he, struct ieee80211_radiotap_he_mu *he_mu, - const struct rtw89_phy_sts_ie09 *ie09, + const union rtw89_phy_sts_ie09 *ie09, const struct rtw89_phy_sts_ie10 *ie10) { + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; const u8 *c1 = NULL, *c2 = NULL; int n_center_26tone, n_ru, i; bool doppler, comp; @@ -3468,8 +3505,17 @@ static void rtw89_core_update_radiotap_he_mu(struct rtw89_dev *rtwdev, IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN); - sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK); - sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK); + if (phy->physt_gen >= 2) { + sig_a1 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK_GEN2); + sig_a2 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_L0); + sig_a2 |= le64_get_bits(ie09->gen2.qw1, + RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_H6) << 6; + } else { + sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK); + sig_a2 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK); + } t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL); he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_UL_DL); @@ -3593,7 +3639,7 @@ static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev, .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), }; struct ieee80211_radiotap_he_mu *he_mu = NULL; - const struct rtw89_phy_sts_ie09 *ie09; + const union rtw89_phy_sts_ie09 *ie09; const struct rtw89_phy_sts_ie10 *ie10; struct ieee80211_radiotap_he *he; u16 he_format; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b85d7fa4296a..a6dee97d99f2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -24,7 +24,7 @@ struct rtw89_h2c_rf_tssi; struct rtw89_fw_txpwr_track_cfg; struct rtw89_phy_rfk_log_fmt; struct rtw89_phy_calc_efuse_gain; -struct rtw89_phy_sts_ie09; +union rtw89_phy_sts_ie09; struct rtw89_phy_sts_ie10; struct rtw89_debugfs; struct rtw89_regd_data; @@ -867,7 +867,7 @@ struct rtw89_rx_phy_ppdu { bool to_self; bool valid; bool hdr_2_en; - const struct rtw89_phy_sts_ie09 *ie09; /* SIG-A */ + const union rtw89_phy_sts_ie09 *ie09; /* SIG-A */ const struct rtw89_phy_sts_ie10 *ie10; /* SIG-B */ }; diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index 4a567d3fe3f7..4e7109fbe7c9 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -658,15 +658,23 @@ struct rtw89_phy_sts_ie01_v2 { #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_C GENMASK(11, 4) #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_D GENMASK(23, 16) -struct rtw89_phy_sts_ie09 { - __le64 qw0; +union rtw89_phy_sts_ie09 { + struct { + __le64 qw0; + } gen0; + struct { + __le64 qw0; + __le64 qw1; + } gen2; } __packed; #define RTW89_PHY_STS_IE09_L_SIG_MASK GENMASK(21, 5) /* Legacy/VHT/HE L-SIG */ +#define RTW89_PHY_STS_IE09_L_SIG_MASK_GEN2 GENMASK(31, 15) /* Legacy/VHT/HE L-SIG for GEN2 */ #define RTW89_PHY_STS_IE09_L_SIG_RATE GENMASK(3, 0) #define RTW89_PHY_STS_IE09_L_SIG_RSVD BIT(4) #define RTW89_PHY_STS_IE09_L_SIG_LENGTH GENMASK(16, 5) #define RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK GENMASK_ULL(45, 22) /* VHT SIG-A1 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK_GEN2 GENMASK_ULL(55, 32) /* VHT SIG-A1 for GEN2 */ #define RTW89_PHY_STS_IE09_VHT_SIG_A1_BW GENMASK(1, 0) #define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD1 BIT(2) #define RTW89_PHY_STS_IE09_VHT_SIG_A1_STBC BIT(3) @@ -680,6 +688,8 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_VHT_SIG_A1_TXOP_NOPS BIT(22) #define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD2 BIT(23) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK GENMASK_ULL(55, 46) /* VHT SIG-A2 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 56) /* VHT SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK_GEN2_H8 GENMASK_ULL(1, 0) /* VHT SIG-A2 for GEN2 */ #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI BIT(0) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI_DISA BIT(1) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE0 BIT(2) @@ -688,6 +698,7 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_VHT_SIG_A2_BF BIT(8) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_RSVD3 BIT(9) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE SU SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE SU SIG-A1 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_FORMAT BIT(0) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BEAM_CHANGE BIT(1) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_ULDL BIT(2) @@ -700,6 +711,8 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_GILTF GENMASK(22, 21) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_NSTS GENMASK(25, 23) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE SU SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE SU SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE SU SIG-A2 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_TXOP GENMASK(6, 0) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_CODING BIT(7) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_LDPC_XSYMSEG BIT(8) @@ -710,6 +723,7 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_RSVD2 BIT(14) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_DOPPLER BIT(15) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE TB SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE TB SIG-A1 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_FORMAT BIT(0) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BSS_COLOR GENMASK(6, 1) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB1 GENMASK(10, 7) @@ -719,9 +733,12 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_RSVD1 BIT(23) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BW GENMASK(25, 24) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE TB SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE TB SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE TB SIG-A2 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_TXOP GENMASK(6, 0) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_RSVD1 GENMASK(15, 7) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE MU SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE MU SIG-A1 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL BIT(0) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_MCS GENMASK(3, 1) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_DCM BIT(4) @@ -733,6 +750,8 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_GI_LTF GENMASK(24, 23) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_DOPPLER BIT(25) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE MU SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE MU SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE MU SIG-A2 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_TXOP GENMASK(6, 0) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_RSVD BIT(7) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LTF_MID GENMASK(10, 8) From f77199a0cf6660d45d72010e76c3ab4788e9da5f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:10:00 +0800 Subject: [PATCH 064/105] wifi: rtw89: check skb headroom before adding radiotap The radiotap headroom is allocated only if IEEE80211_CONF_MONITOR is set. However, it is potentially racing that SKB allocation without radiotap headroom but adding radiotap from matched PPDU status of another SKB. Add a check to avoid the case. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-15-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index c21737981b61..432d46dfd26a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3758,6 +3758,13 @@ static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR)) return; + /* + * At transient adding a monitor vif from a station vif, the headroom + * might not include radiotap. + */ + if (unlikely(skb_headroom(skb) < RTW89_RADIOTAP_ROOM + NET_SKB_PAD)) + return; + if (rx_status->encoding == RX_ENC_VHT) rtw89_core_update_radiotap_vht(rtwdev, skb, rx_status, phy_ppdu); else if (rx_status->encoding == RX_ENC_HE) From 4c13e00cc6798f8e63354982ec172b7829ae825a Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Thu, 7 May 2026 10:22:12 +0200 Subject: [PATCH 065/105] wifi: rtw88: remove rtw_txq_dequeue Remove the rtw_txq_dequeue helper. It's a wrapper around ieee80211_tx_dequeue with just one caller. Call ieee80211_tx_dequeue directly in rtw_txq_push. There's no need to fetch txq for every frame, we can do this once outside of the rcu lock. Make the loop variable i unsigned long, it should have the same type as frames. Signed-off-by: Martin Kaiser Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260507082238.889656-1-martin@kaiser.cx --- drivers/net/wireless/realtek/rtw88/tx.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index 3106edb84fb4..bea78971141e 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -619,31 +619,19 @@ static int rtw_txq_push_skb(struct rtw_dev *rtwdev, return 0; } -static struct sk_buff *rtw_txq_dequeue(struct rtw_dev *rtwdev, - struct rtw_txq *rtwtxq) -{ - struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); - struct sk_buff *skb; - - skb = ieee80211_tx_dequeue(rtwdev->hw, txq); - if (!skb) - return NULL; - - return skb; -} - static void rtw_txq_push(struct rtw_dev *rtwdev, struct rtw_txq *rtwtxq, unsigned long frames) { + struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); struct sk_buff *skb; + unsigned long i; int ret; - int i; rcu_read_lock(); for (i = 0; i < frames; i++) { - skb = rtw_txq_dequeue(rtwdev, rtwtxq); + skb = ieee80211_tx_dequeue(rtwdev->hw, txq); if (!skb) break; From 695e7f2e79010ca469d01098889c2f57340c7df1 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:38 +0800 Subject: [PATCH 066/105] wifi: rtw89: phy: define BB wrap data for RTL8922D variants The BB wrap is a hardware block to control TX power. Since RTL8922D has many variants with different CID and RFE types, prepare flow and dummy struct adopt to configuration functions for coming patches. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 4 ++ drivers/net/wireless/realtek/rtw89/core.h | 13 +++++++ drivers/net/wireless/realtek/rtw89/phy.h | 3 ++ 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 | 38 +++++++++++++++++++ 10 files changed, 64 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 432d46dfd26a..7b26aedc0883 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -7312,6 +7312,10 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev) if (ret) goto out; + ret = rtw89_chip_data_setup(rtwdev); + if (ret) + goto out; + rtw89_core_setup_rfe_parms(rtwdev); rtwdev->ps_mode = rtw89_update_ps_mode(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index a6dee97d99f2..75237fb23a99 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -29,6 +29,7 @@ struct rtw89_phy_sts_ie10; struct rtw89_debugfs; struct rtw89_regd_data; struct rtw89_wow_cam_info; +struct rtw89_bb_wrap_data; struct rtw89_fw_cmd_ofld_info; extern const struct ieee80211_ops rtw89_ops; @@ -3851,6 +3852,7 @@ struct rtw89_chip_ops { enum rtw89_efuse_block block); int (*read_phycap)(struct rtw89_dev *rtwdev, u8 *phycap_map); void (*fem_setup)(struct rtw89_dev *rtwdev); + int (*data_setup)(struct rtw89_dev *rtwdev); void (*rfe_gpio)(struct rtw89_dev *rtwdev); void (*rfk_hw_init)(struct rtw89_dev *rtwdev); void (*rfk_init)(struct rtw89_dev *rtwdev); @@ -4597,6 +4599,7 @@ struct rtw89_bb_stat_cfg { }; struct rtw89_phy_info { + const struct rtw89_bb_wrap_data *bb_wrap_data; struct rtw89_bb_stat_cfg bb_stat_cfg; }; @@ -7484,6 +7487,16 @@ static inline void rtw89_chip_fem_setup(struct rtw89_dev *rtwdev) chip->ops->fem_setup(rtwdev); } +static inline int rtw89_chip_data_setup(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + if (!chip->ops->data_setup) + return 0; + + return chip->ops->data_setup(rtwdev); +} + static inline void rtw89_chip_rfe_gpio(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index b4b818f786df..eabaf46f4f4b 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -572,6 +572,9 @@ struct rtw89_phy_rfk_log_fmt { const struct rtw89_fw_element_hdr *elm[RTW89_PHY_C2H_RFK_LOG_FUNC_NUM]; }; +struct rtw89_bb_wrap_data { +}; + struct rtw89_phy_gen_def { u32 cr_base; u32 physt_bmp_start; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index e047e716d9e3..2fb54e1fc7ab 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2556,6 +2556,7 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = { .read_efuse = rtw8851b_read_efuse, .read_phycap = rtw8851b_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = rtw8851b_rfe_gpio, .rfk_hw_init = NULL, .rfk_init = rtw8851b_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 686e489d42f2..d3b6e8a7f5bd 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2302,6 +2302,7 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = { .read_efuse = rtw8852a_read_efuse, .read_phycap = rtw8852a_read_phycap, .fem_setup = rtw8852a_fem_setup, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852a_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 6ab99f72fda7..989ac38fe359 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -883,6 +883,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .read_efuse = rtw8852bx_read_efuse, .read_phycap = rtw8852bx_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852b_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 83de26273100..b58561e141cb 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -729,6 +729,7 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = { .read_efuse = rtw8852bx_read_efuse, .read_phycap = rtw8852bx_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852bt_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index cc278587b532..9ecd140dcdd5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3091,6 +3091,7 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = { .read_efuse = rtw8852c_read_efuse, .read_phycap = rtw8852c_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852c_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index e6f15ee2a86b..07270e83b778 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -3086,6 +3086,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .read_efuse = rtw8922a_read_efuse, .read_phycap = rtw8922a_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = rtw8922a_rfk_hw_init, .rfk_init = rtw8922a_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index b8b75fca0a1b..66b05aedcf11 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -286,6 +286,15 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_ADIE] = {.offset = 0x70000, .size = 0x10}, }; +static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { +}; + +static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { +}; + +static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = { +}; + static void rtw8922d_sel_bt_rx_path(struct rtw89_dev *rtwdev, u8 val, enum rtw89_rf_path rx_path) { @@ -918,6 +927,34 @@ static void rtw8922d_power_trim(struct rtw89_dev *rtwdev) rtw8922d_pad_bias_trim(rtwdev); } +static int rtw8922d_data_setup(struct rtw89_dev *rtwdev) +{ + const struct rtw89_bb_wrap_data *data; + struct rtw89_hal *hal = &rtwdev->hal; + bool rfe35_41_44 = false; + + switch (rtwdev->efuse.rfe_type) { + case 35: + case 41: + case 44: + rfe35_41_44 = true; + break; + } + + if (hal->cid == RTL8922D_CID7025) { + data = &rtw8922d_bb_wrap_data_7025_default; + } else { + if (rfe35_41_44) + data = &rtw8922d_bb_wrap_data_7090_rfe35_41_44; + else + data = &rtw8922d_bb_wrap_data_7090_default; + } + + rtwdev->phy_info.bb_wrap_data = data; + + return 0; +} + static void rtw8922d_set_channel_mac(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, u8 mac_idx) @@ -2933,6 +2970,7 @@ static const struct rtw89_chip_ops rtw8922d_chip_ops = { .read_efuse = rtw8922d_read_efuse, .read_phycap = rtw8922d_read_phycap, .fem_setup = NULL, + .data_setup = rtw8922d_data_setup, .rfe_gpio = NULL, .rfk_hw_init = rtw8922d_rfk_hw_init, .rfk_init = rtw8922d_rfk_init, From 99237c755c279522410bf1ef951fd7991178933a Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:39 +0800 Subject: [PATCH 067/105] wifi: rtw89: phy: set BB wrap of out-of-band DPD Control the out-of-band DPD (digital pre-distortion) to ensure out-of-band signal under requirement. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 6 +++ drivers/net/wireless/realtek/rtw89/phy_be.c | 40 +++++++++++-------- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 3 ++ 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index eabaf46f4f4b..2933abd3aac8 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -572,7 +572,13 @@ struct rtw89_phy_rfk_log_fmt { const struct rtw89_fw_element_hdr *elm[RTW89_PHY_C2H_RFK_LOG_FUNC_NUM]; }; +enum rtw89_mdpd_onoff { + MDPD_ON = 0, + MDPD_OFF = 1, +}; + struct rtw89_bb_wrap_data { + u8 mdpd_by_dbw[4]; }; struct rtw89_phy_gen_def { diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 23137f2dbd4b..d3b46d973845 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -886,23 +886,31 @@ static void rtw89_phy_bb_set_oob_dpd_qam_comp_val(struct rtw89_dev *rtwdev, static void rtw89_phy_bb_set_mdpd_qam_comp_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH7_BE4, 0x0, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 th; - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW7_BE4, 0x0, mac_idx); + if (!d) + return; + + th = d->mdpd_by_dbw[0]; + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH7_BE4, th, mac_idx); + + th = d->mdpd_by_dbw[2]; + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW7_BE4, th, mac_idx); } static void rtw89_phy_bb_set_cim3k_val(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 66b05aedcf11..326d12da2962 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -287,12 +287,15 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { + .mdpd_by_dbw = {MDPD_ON, MDPD_ON, MDPD_ON, MDPD_ON}, }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { + .mdpd_by_dbw = {MDPD_OFF, MDPD_OFF, MDPD_ON, MDPD_ON}, }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = { + .mdpd_by_dbw = {MDPD_OFF, MDPD_OFF, MDPD_ON, MDPD_ON}, }; static void rtw8922d_sel_bt_rx_path(struct rtw89_dev *rtwdev, u8 val, From 9adb75af3f5a041bd53d59288c2c5c65f88187c2 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:40 +0800 Subject: [PATCH 068/105] wifi: rtw89: phy: set BB wrap of DPD by bandwidth Apply different settings to out-of-band DPD (digital pre-distortion) by bandwidth, as hardware defines separate registers. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 15 ++ drivers/net/wireless/realtek/rtw89/phy_be.c | 142 ++++++++++-------- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 27 ++++ 3 files changed, 120 insertions(+), 64 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 2933abd3aac8..880ffcb47cf2 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -572,12 +572,27 @@ struct rtw89_phy_rfk_log_fmt { const struct rtw89_fw_element_hdr *elm[RTW89_PHY_C2H_RFK_LOG_FUNC_NUM]; }; +enum rtw89_rfsi_ctrl_band { + RFSI_CTRL_BAND_5_6GHZ, + RFSI_CTRL_BAND_2GHZ, + + RFSI_CTRL_BAND_NUM, +}; + enum rtw89_mdpd_onoff { MDPD_ON = 0, MDPD_OFF = 1, }; +enum rtw89_oob_dpd_onoff { + OOB_DPD_OFF = 0, + OOB_DPD_ON = 1, +}; + struct rtw89_bb_wrap_data { + struct { + u8 oob_dpd_by_cbw[8]; + } bands[RFSI_CTRL_BAND_NUM]; u8 mdpd_by_dbw[4]; }; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index d3b46d973845..0d3192717869 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -810,77 +810,91 @@ static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_val(struct rtw89_dev *rtwdev, static void rtw89_phy_bb_set_oob_dpd_qam_comp_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK7_BE4, 0x0, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 th; - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK7_BE4, 0x0, mac_idx); + if (!d) + return; - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[0]; + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[1]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[2]; + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[3]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[4]; + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[5]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW7_BE4, th, mac_idx); + + th = d->bands[0].oob_dpd_by_cbw[6]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW7_BE4, th, mac_idx); + + th = d->bands[0].oob_dpd_by_cbw[7]; + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW7_BE4, th, mac_idx); } static void rtw89_phy_bb_set_mdpd_qam_comp_val(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 326d12da2962..2e5e264e75db 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -287,14 +287,41 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }}, .mdpd_by_dbw = {MDPD_ON, MDPD_ON, MDPD_ON, MDPD_ON}, }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }}, .mdpd_by_dbw = {MDPD_OFF, MDPD_OFF, MDPD_ON, MDPD_ON}, }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }}, .mdpd_by_dbw = {MDPD_OFF, MDPD_OFF, MDPD_ON, MDPD_ON}, }; From f791fe306078f71aa0914d50241a92eda5533805 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:41 +0800 Subject: [PATCH 069/105] wifi: rtw89: phy: set BB wrap of control options Set main options to control BB wrap functions. For example, enable options by data bandwidth or channel bandwidth. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 11 ++++++++ drivers/net/wireless/realtek/rtw89/phy_be.c | 11 ++++++-- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 27 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 880ffcb47cf2..8eb122eeabe8 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -589,7 +589,18 @@ enum rtw89_oob_dpd_onoff { OOB_DPD_ON = 1, }; +#define _8nibble(n0, n1, n2, n3, n4, n5, n6, n7) \ + ((n0) << 0 | (n1) << 4 | (n2) << 8 | (n3) << 12 | \ + (n4) << 16 | (n5) << 20 | (n6) << 24 | (n7) << 28) + +struct rtw89_bb_wrap_common_data { + struct { + u32 rfsi_ct_opt[2]; + } bands[RFSI_CTRL_BAND_NUM]; +}; + struct rtw89_bb_wrap_data { + const struct rtw89_bb_wrap_common_data *common; struct { u8 oob_dpd_by_cbw[8]; } bands[RFSI_CTRL_BAND_NUM]; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 0d3192717869..0d0f87f464a5 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -648,13 +648,20 @@ static u32 rtw89_phy_bb_wrap_be_bandedge_decision(struct rtw89_dev *rtwdev, void rtw89_phy_bb_wrap_set_rfsi_ct_opt(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const u32 *val; u32 reg; + if (!d || !d->common) + return; + + val = d->common->bands[0].rfsi_ct_opt; + reg = rtw89_mac_reg_by_idx(rtwdev, R_RFSI_CT_OPT_0_BE4, phy_idx); - rtw89_write32(rtwdev, reg, 0x00010001); + rtw89_write32(rtwdev, reg, val[0]); reg = rtw89_mac_reg_by_idx(rtwdev, R_RFSI_CT_OPT_8_BE4, phy_idx); - rtw89_write32(rtwdev, reg, 0x00010001); + rtw89_write32(rtwdev, reg, val[1]); } EXPORT_SYMBOL(rtw89_phy_bb_wrap_set_rfsi_ct_opt); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 2e5e264e75db..18d75184f82b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -286,7 +286,32 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_ADIE] = {.offset = 0x70000, .size = 0x10}, }; +static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7025 = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + }}, +}; + +static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + }}, +}; + static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { + .common = &rtw8922d_bb_wrap_common_data_7025, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, @@ -300,6 +325,7 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { + .common = &rtw8922d_bb_wrap_common_data_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, @@ -313,6 +339,7 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = { + .common = &rtw8922d_bb_wrap_common_data_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, From 7924c027474cef6ed3aab3198816a8f5af8f226d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:42 +0800 Subject: [PATCH 070/105] wifi: rtw89: phy: set BB wrap of QAM threshold Make hardware to consider which QAM (data rate) to apply BB wrapper parameters, which are set by other registers. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 12 +++ drivers/net/wireless/realtek/rtw89/phy_be.c | 74 +++++++++++-------- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 + 3 files changed, 56 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 8eb122eeabe8..34876de834d7 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -589,6 +589,17 @@ enum rtw89_oob_dpd_onoff { OOB_DPD_ON = 1, }; +enum rtw89_rfsi_ctrl_modulation { + RFSI_BPSK = 0, + RFSI_QPSK = 1, + RFSI_16QAM = 2, + RFSI_64QAM = 3, + RFSI_256QAM = 4, + RFSI_1024QAM = 5, + RFSI_4096QAM = 6, + RFSI_MAX, +}; + #define _8nibble(n0, n1, n2, n3, n4, n5, n6, n7) \ ((n0) << 0 | (n1) << 4 | (n2) << 8 | (n3) << 12 | \ (n4) << 16 | (n5) << 20 | (n6) << 24 | (n7) << 28) @@ -597,6 +608,7 @@ struct rtw89_bb_wrap_common_data { struct { u32 rfsi_ct_opt[2]; } bands[RFSI_CTRL_BAND_NUM]; + u8 qam_th[6]; }; struct rtw89_bb_wrap_data { diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 0d0f87f464a5..efd63f438424 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -691,42 +691,52 @@ EXPORT_SYMBOL(rtw89_phy_bb_wrap_set_rfsi_bandedge_ch); static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_init(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 th0, th1, th2; + + if (!d || !d->common) + return; + + th0 = d->common->qam_th[0]; + th1 = d->common->qam_th[1]; + th2 = d->common->qam_th[2]; + /* TH0 */ - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_0_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_3_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_1_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_4_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_7_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_0_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_3_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_6_BE4, 0x1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_0_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_3_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_1_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_4_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_7_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_0_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_3_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_6_BE4, th0, mac_idx); /* TH1 */ - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_1_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_4_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_2_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_5_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_8_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_1_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_4_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_7_BE4, 0x2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_1_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_4_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_2_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_5_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_8_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_1_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_4_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_7_BE4, th1, mac_idx); /* TH2 */ - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_2_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_0_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_3_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_6_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_9_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_2_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_5_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_8_BE4, 0x4, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_2_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_0_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_3_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_6_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_9_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_2_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_5_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_8_BE4, th2, mac_idx); /* DPD 160M */ - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_0_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_1_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_2_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_3_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_4_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_5_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_6_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_7_BE4, 0x1, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_0_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_1_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_2_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_3_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_4_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_5_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_6_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_7_BE4, th0, mac_idx); /* DPD 20M */ rtw89_write32_idx(rtwdev, R_DPD_CBW_TH0_BE4, B_DPD_CBW20_TH0_0_BE4, 0x2, mac_idx); rtw89_write32_idx(rtwdev, R_DPD_CBW_TH0_BE4, B_DPD_CBW20_TH0_1_BE4, 0x2, mac_idx); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 18d75184f82b..a867652907bc 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -296,6 +296,7 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7025 .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, }}, + .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, }; static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 = { @@ -308,6 +309,7 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, }}, + .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { From 7c4ef01bb3c37fb135e425245522a5fb2460ef59 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:43 +0800 Subject: [PATCH 071/105] wifi: rtw89: phy: set BB wrap of QAM options Apply these options to selected QAM to TX signal under requirements. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 15 ++++ drivers/net/wireless/realtek/rtw89/phy_be.c | 75 +++++++++++-------- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 36 +++++++++ 3 files changed, 94 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 34876de834d7..094ee7d7c10a 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -600,10 +600,21 @@ enum rtw89_rfsi_ctrl_modulation { RFSI_MAX, }; +#define MAX_TX_RFSI_CTRL_OPT 10 + #define _8nibble(n0, n1, n2, n3, n4, n5, n6, n7) \ ((n0) << 0 | (n1) << 4 | (n2) << 8 | (n3) << 12 | \ (n4) << 16 | (n5) << 20 | (n6) << 24 | (n7) << 28) +#define _qam_comp_code(c) ((((c) & (BIT(15) | BIT(14))) >> 11) | \ + (((c) & BIT(12)) >> 10) | \ + (((c) & (BIT(9) | BIT(8))) >> 8)) + +#define _10qam_comp_code(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9) \ + _qam_comp_code(c0), _qam_comp_code(c1), _qam_comp_code(c2), _qam_comp_code(c3), \ + _qam_comp_code(c4), _qam_comp_code(c5), _qam_comp_code(c6), _qam_comp_code(c7), \ + _qam_comp_code(c8), _qam_comp_code(c9) + struct rtw89_bb_wrap_common_data { struct { u32 rfsi_ct_opt[2]; @@ -614,6 +625,10 @@ struct rtw89_bb_wrap_common_data { struct rtw89_bb_wrap_data { const struct rtw89_bb_wrap_common_data *common; struct { + u16 qam_comp_th0[MAX_TX_RFSI_CTRL_OPT]; + u16 qam_comp_th1[MAX_TX_RFSI_CTRL_OPT]; /* encoded */ + u16 qam_comp_th2[MAX_TX_RFSI_CTRL_OPT]; /* encoded */ + u16 qam_comp_ow[MAX_TX_RFSI_CTRL_OPT]; u8 oob_dpd_by_cbw[8]; } bands[RFSI_CTRL_BAND_NUM]; u8 mdpd_by_dbw[4]; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index efd63f438424..dca36a5dfb0a 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -788,40 +788,51 @@ static void rtw89_phy_bb_wrap_tx_rfsi_scenario_def(struct rtw89_dev *rtwdev, static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKLWORD, 0x4010, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKHWORD, 0x4410, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKHWORD, 0x0, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const u16 *th; - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_L, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_M, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_M, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_M, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2L, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2M, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2M, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2M, 0x0, mac_idx); + if (!d || !d->common) + return; - rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKLWORD, 0x4010, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKHWORD, 0x4010, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKHWORD, 0x0, mac_idx); + th = d->bands[0].qam_comp_th0; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKLWORD, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKHWORD, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKLWORD, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKHWORD, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKLWORD, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKHWORD, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKLWORD, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKHWORD, th[7], mac_idx); + + th = d->bands[0].qam_comp_th1; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_L, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_M, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_H, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_L, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_M, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_H, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_L, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_M, th[7], mac_idx); + + th = d->bands[0].qam_comp_th2; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2L, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2M, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2H, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2L, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2M, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2H, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2L, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2M, th[7], mac_idx); + + th = d->bands[0].qam_comp_ow; + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKLWORD, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKHWORD, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKLWORD, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKHWORD, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKLWORD, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKHWORD, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKLWORD, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKHWORD, th[7], mac_idx); } static void rtw89_phy_bb_set_oob_dpd_qam_comp_val(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index a867652907bc..114f53ab9f7c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -316,10 +316,22 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { .common = &rtw8922d_bb_wrap_common_data_7025, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_comp_th0 = {0x4000, 0x6400, 0x6500, 0x6000, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x6400, 0x6500, 0x6000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0x4000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4400, 0x4400, 0x4000, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }, [RFSI_CTRL_BAND_2GHZ] = { + .qam_comp_th0 = {0x4000, 0x4400, 0x4500, 0, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x4400, 0x4500, 0, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4400, 0x4400, 0, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }}, @@ -330,10 +342,22 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { .common = &rtw8922d_bb_wrap_common_data_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_comp_th0 = {0x4000, 0x6420, 0x6520, 0x6000, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x6400, 0x6500, 0x6000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0x4000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }, [RFSI_CTRL_BAND_2GHZ] = { + .qam_comp_th0 = {0x4000, 0x4400, 0x4500, 0, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x4400, 0x4500, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }}, @@ -344,10 +368,22 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = .common = &rtw8922d_bb_wrap_common_data_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_comp_th0 = {0x4000, 0x6420, 0x6520, 0x6000, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x6400, 0x6500, 0x6000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0x4000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }, [RFSI_CTRL_BAND_2GHZ] = { + .qam_comp_th0 = {0x4000, 0x4400, 0x4500, 0, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x4400, 0x4500, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }}, From ce23c529c722fab7152d6797b4798acd9a18fb69 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:44 +0800 Subject: [PATCH 072/105] wifi: rtw89: phy: set BB wrap of trigger-base partial band Apply options to trigger-base partial band. For example, TX at RU-106 on a 160MHz bandwidth. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 1 + drivers/net/wireless/realtek/rtw89/phy_be.c | 8 +++++++- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 094ee7d7c10a..2298ba245050 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -618,6 +618,7 @@ enum rtw89_rfsi_ctrl_modulation { struct rtw89_bb_wrap_common_data { struct { u32 rfsi_ct_opt[2]; + u8 pb_tb; } bands[RFSI_CTRL_BAND_NUM]; u8 qam_th[6]; }; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index dca36a5dfb0a..a730b0329117 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -771,13 +771,19 @@ static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_init(struct rtw89_dev *rtwdev, static void rtw89_phy_bb_wrap_tx_rfsi_scenario_def(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 pb_tb = 0; + + if (d && d->common) + pb_tb = d->common->bands[0].pb_tb; + rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_ER_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_SUBF_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_FTM_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_SENS_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_DEF_BE, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_PB_BE4, 0x0, mac_idx); + rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_PB_BE4, pb_tb, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_DL_WO_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_DL_BF_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_MUMIMO_BE4, 0x0, mac_idx); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 114f53ab9f7c..15c44dd0d9fa 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -291,10 +291,12 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7025 [RFSI_CTRL_BAND_5_6GHZ] = { .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 3, }, [RFSI_CTRL_BAND_2GHZ] = { .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 0, }}, .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, }; @@ -304,10 +306,12 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 [RFSI_CTRL_BAND_5_6GHZ] = { .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 3, }, [RFSI_CTRL_BAND_2GHZ] = { .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 0, }}, .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, }; From 89ff0338727fde5931185b2d2091273aff8d78ea Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:45 +0800 Subject: [PATCH 073/105] wifi: rtw89: phy: set BB wrap of CIM3K The options to enable calibrations of CIM 3rd-order for threshold, non-bandedge and bandedge. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 13 ++++++++++ drivers/net/wireless/realtek/rtw89/phy_be.c | 24 +++++++++++++++---- drivers/net/wireless/realtek/rtw89/reg.h | 3 +++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 4 ++++ 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 2298ba245050..88a572bb2890 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -589,6 +589,16 @@ enum rtw89_oob_dpd_onoff { OOB_DPD_ON = 1, }; +enum rtw89_cim3k_onoff { + CIM3K_ON = 1, + CIM3K_OFF = 0, +}; + +enum rtw89_cim3k_en_dis { + CIM3K_ENABLE = 1, + CIM3K_DISABLE = 0, +}; + enum rtw89_rfsi_ctrl_modulation { RFSI_BPSK = 0, RFSI_QPSK = 1, @@ -617,6 +627,9 @@ enum rtw89_rfsi_ctrl_modulation { struct rtw89_bb_wrap_common_data { struct { + struct rtw89_bb_wrap_data_cim3k { + u8 th, ow, non_bandedge, bandedge; + } cim3k; u32 rfsi_ct_opt[2]; u8 pb_tb; } bands[RFSI_CTRL_BAND_NUM]; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index a730b0329117..cf7c52d4a0fc 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -964,10 +964,26 @@ static void rtw89_phy_bb_set_mdpd_qam_comp_val(struct rtw89_dev *rtwdev, static void rtw89_phy_bb_set_cim3k_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_TH_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_OW_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_NONBE_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_BANDEDGE_BE4, 0x1, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const struct rtw89_bb_wrap_data_cim3k *p; + + if (!d || !d->common) + return; + + p = &d->common->bands[0].cim3k; + + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_TH_BE4, p->th, mac_idx); + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_OW_BE4, p->ow, mac_idx); + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_NONBE_BE4, + p->non_bandedge, mac_idx); + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_BANDEDGE_BE4, + p->bandedge, mac_idx); + + if (rtwdev->chip->chip_id != RTL8922D) + return; + + rtw89_write32_idx(rtwdev, R_CIM3K_SU_FORCE, B_CIM3K_SU_FORCE_EN, 1, mac_idx); + rtw89_write32_idx(rtwdev, R_CIM3K_SU_FORCE, B_CIM3K_SU_FORCE_VAL, 0, mac_idx); } static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 716747c3a10e..e46c32ec8e90 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10596,6 +10596,9 @@ #define B_FBTB_CT_MUMIMO_BE4 GENMASK(19, 16) #define B_FBTB_CT_FTM_BE4 GENMASK(23, 20) #define B_FBTB_CT_SENS_BE4 GENMASK(27, 24) +#define R_CIM3K_SU_FORCE 0x119F8 +#define B_CIM3K_SU_FORCE_EN BIT(17) +#define B_CIM3K_SU_FORCE_VAL BIT(18) #define R_RFSI_CT_OPT_0_BE4 0x11A94 #define R_RFSI_CT_OPT_8_BE4 0x11A98 #define R_QAM_COMP_TH0_BE4 0x11A9C diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 15c44dd0d9fa..ec94067aa1a5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -289,11 +289,13 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7025 = { .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_ENABLE, CIM3K_ENABLE}, .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, .pb_tb = 3, }, [RFSI_CTRL_BAND_2GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_ENABLE, CIM3K_ENABLE}, .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, .pb_tb = 0, @@ -304,11 +306,13 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7025 static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 = { .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_DISABLE, CIM3K_ENABLE}, .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, .pb_tb = 3, }, [RFSI_CTRL_BAND_2GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_DISABLE, CIM3K_ENABLE}, .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, .pb_tb = 0, From a395d7eed2f822a340f946ed8a81d6b55bfa9dc4 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:46 +0800 Subject: [PATCH 074/105] wifi: rtw89: phy: change order to align register order Change the order to align what vendor driver does. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy_be.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index cf7c52d4a0fc..68de34886a76 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -587,8 +587,8 @@ static void rtw89_phy_bb_wrap_force_cr_init(struct rtw89_dev *rtwdev, addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_FORCE_LMT, mac_idx); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_LMT_ON, 0); - addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_BOOST, mac_idx); - rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RATE_ON, 0); + addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_RATE_CTRL, mac_idx); + rtw89_write32_mask(rtwdev, addr, B_BE_FORCE_PWR_BY_RATE_EN, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_OFST_RULMT, mac_idx); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ENON, 0); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ON, 0); @@ -596,8 +596,8 @@ static void rtw89_phy_bb_wrap_force_cr_init(struct rtw89_dev *rtwdev, rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_MACID_ALL, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_COEX_CTRL, mac_idx); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_COEX_ON, 0); - addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_RATE_CTRL, mac_idx); - rtw89_write32_mask(rtwdev, addr, B_BE_FORCE_PWR_BY_RATE_EN, 0); + addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_BOOST, mac_idx); + rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RATE_ON, 0); } static void rtw89_phy_bb_wrap_ftm_init(struct rtw89_dev *rtwdev, From 4f2b5c83657ed795b102f4378592086192cacc56 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:47 +0800 Subject: [PATCH 075/105] wifi: rtw89: phy: configure control options of BB wrapper by RFSI band The settings of control options are predefined per RFSI band (2GHz band or not 5/6GHz band) and used for fine-tuning transmit power. Calculate and set the RFSI band once creating channel context, and use it by BB wrapper configurations. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 2 ++ drivers/net/wireless/realtek/rtw89/core.h | 8 ++++++++ drivers/net/wireless/realtek/rtw89/phy.h | 8 +------- drivers/net/wireless/realtek/rtw89/phy_be.c | 5 +++-- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index cd846cb81f0c..6f11335b4968 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -150,6 +150,8 @@ void rtw89_chan_create(struct rtw89_chan *chan, u8 center_chan, u8 primary_chan, primary_freq); chan->pri_sb_idx = rtw89_get_primary_sb_idx(center_chan, primary_chan, bandwidth); + chan->rfsi_band = band == RTW89_BAND_2G ? RFSI_CTRL_BAND_2GHZ : + RFSI_CTRL_BAND_5_6GHZ; } static void _rtw89_chan_update_punctured(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 75237fb23a99..4666e3041ef3 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1015,6 +1015,13 @@ enum rtw89_sc_offset { RTW89_SC_40_LOWER = 10, }; +enum rtw89_rfsi_ctrl_band { + RFSI_CTRL_BAND_5_6GHZ, + RFSI_CTRL_BAND_2GHZ, + + RFSI_CTRL_BAND_NUM, +}; + /* only mgd features can be added to the enum */ enum rtw89_wow_flags { RTW89_WOW_FLAG_EN_MAGIC_PKT, @@ -1038,6 +1045,7 @@ struct rtw89_chan { enum rtw89_tx_comp_band tx_comp_band; enum rtw89_sc_offset pri_ch_idx; u8 pri_sb_idx; + enum rtw89_rfsi_ctrl_band rfsi_band; }; struct rtw89_chan_rcd { diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 88a572bb2890..ebf8a669f5d7 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -572,13 +572,6 @@ struct rtw89_phy_rfk_log_fmt { const struct rtw89_fw_element_hdr *elm[RTW89_PHY_C2H_RFK_LOG_FUNC_NUM]; }; -enum rtw89_rfsi_ctrl_band { - RFSI_CTRL_BAND_5_6GHZ, - RFSI_CTRL_BAND_2GHZ, - - RFSI_CTRL_BAND_NUM, -}; - enum rtw89_mdpd_onoff { MDPD_ON = 0, MDPD_OFF = 1, @@ -1032,6 +1025,7 @@ static inline void rtw89_phy_bb_wrap_init(struct rtw89_dev *rtwdev) } void rtw89_phy_bb_wrap_set_rfsi_ct_opt(struct rtw89_dev *rtwdev, + enum rtw89_rfsi_ctrl_band ctrl_band, enum rtw89_phy_idx phy_idx); void rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 68de34886a76..16f679589d90 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -646,6 +646,7 @@ static u32 rtw89_phy_bb_wrap_be_bandedge_decision(struct rtw89_dev *rtwdev, } void rtw89_phy_bb_wrap_set_rfsi_ct_opt(struct rtw89_dev *rtwdev, + enum rtw89_rfsi_ctrl_band rfsi_band, enum rtw89_phy_idx phy_idx) { const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; @@ -655,7 +656,7 @@ void rtw89_phy_bb_wrap_set_rfsi_ct_opt(struct rtw89_dev *rtwdev, if (!d || !d->common) return; - val = d->common->bands[0].rfsi_ct_opt; + val = d->common->bands[rfsi_band].rfsi_ct_opt; reg = rtw89_mac_reg_by_idx(rtwdev, R_RFSI_CT_OPT_0_BE4, phy_idx); rtw89_write32(rtwdev, reg, val[0]); @@ -1004,7 +1005,7 @@ static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, rtw89_phy_bb_set_mdpd_qam_comp_val(rtwdev, mac_idx); rtw89_phy_bb_set_cim3k_val(rtwdev, mac_idx); - rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, phy_idx); + rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, 0, phy_idx); rtw89_entity_get_conf(rtwdev, &conf); chan = conf.chans[phy_idx]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index ec94067aa1a5..ade89234afd6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2284,7 +2284,7 @@ static void rtw8922d_set_channel_bb(struct rtw89_dev *rtwdev, u8 pri_sb = chan->pri_sb_idx; u32 val; - rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, phy_idx); + rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, chan->rfsi_band, phy_idx); rtw8922d_ctrl_ch(rtwdev, chan, phy_idx); rtw8922d_ctrl_bw(rtwdev, pri_sb, chan->band_width, phy_idx); rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); From 7076af642955693935e60bc94546d105fb0395ca Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:48 +0800 Subject: [PATCH 076/105] wifi: rtw89: phy: add BB wrapper generation 3 for RTL8922D variant The BB wrapper generation 3 support more settings to fine tune power threshold and CCK CFIR/filter per RFSI band. Add them accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 18 +++ drivers/net/wireless/realtek/rtw89/phy_be.c | 112 +++++++++++++++++- drivers/net/wireless/realtek/rtw89/reg.h | 70 +++++++++++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 17 +++ 4 files changed, 212 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index ebf8a669f5d7..5af66656d0b8 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -603,6 +603,16 @@ enum rtw89_rfsi_ctrl_modulation { RFSI_MAX, }; +enum rtw89_cfir_onoff { + CFIR_ON = 1, + CFIR_OFF = 0, +}; + +enum rtw89_filter_onoff { + FILTER_A_ON = 1, + FILTER_A_OFF = 0, +}; + #define MAX_TX_RFSI_CTRL_OPT 10 #define _8nibble(n0, n1, n2, n3, n4, n5, n6, n7) \ @@ -629,8 +639,16 @@ struct rtw89_bb_wrap_common_data { u8 qam_th[6]; }; +struct rtw89_bb_wrap_common_data_gen3 { + struct { + u8 qam_th[6]; + } bands[RFSI_CTRL_BAND_NUM]; + u8 cck_val[2]; +}; + struct rtw89_bb_wrap_data { const struct rtw89_bb_wrap_common_data *common; + const struct rtw89_bb_wrap_common_data_gen3 *common_gen3; struct { u16 qam_comp_th0[MAX_TX_RFSI_CTRL_OPT]; u16 qam_comp_th1[MAX_TX_RFSI_CTRL_OPT]; /* encoded */ diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 16f679589d90..d0a6dfdbfea7 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -769,6 +769,75 @@ static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_init(struct rtw89_dev *rtwdev, rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_TH2_BE4, 0x2, mac_idx); } +static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_gen3_init(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_mac_idx mac_idx) +{ + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const struct rtw89_bb_wrap_common_data_gen3 *common_gen3; + const u8 *ths; + + if (!d || !d->common_gen3) + return; + + common_gen3 = d->common_gen3; + ths = common_gen3->bands[chan->rfsi_band].qam_th; + + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_0_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_1_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_2_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_3_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_4_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_5_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_6_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_7_BE4, ths[0], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH1_0_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH1_1_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_2_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_3_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_4_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_5_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_6_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_7_BE4, ths[1], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_0_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_1_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_2_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_3_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_4_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_5_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_6_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_7_BE4, ths[2], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_0_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_1_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_2_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_3_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_4_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_5_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH3_6_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH3_7_BE4, ths[3], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_0_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_1_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_2_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_3_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_4_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_5_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_6_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_7_BE4, ths[4], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_0_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_1_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_2_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_3_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_4_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_5_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_6_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_7_BE4, ths[5], mac_idx); +} + static void rtw89_phy_bb_wrap_tx_rfsi_scenario_def(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { @@ -987,6 +1056,39 @@ static void rtw89_phy_bb_set_cim3k_val(struct rtw89_dev *rtwdev, rtw89_write32_idx(rtwdev, R_CIM3K_SU_FORCE, B_CIM3K_SU_FORCE_VAL, 0, mac_idx); } +static void rtw89_phy_bb_set_cck_cfir_filter_val_gen3(struct rtw89_dev *rtwdev, + enum rtw89_mac_idx mac_idx) +{ + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const struct rtw89_bb_wrap_common_data_gen3 *common_gen3; + u8 cck0, cck1; + + if (!d || !d->common_gen3) + return; + + common_gen3 = d->common_gen3; + cck0 = common_gen3->cck_val[0]; + cck1 = common_gen3->cck_val[1]; + + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR0_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR1_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR2_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR3_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR4_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR5_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR6_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR7_BE4, cck0, mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR0_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR1_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR2_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR3_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR4_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR5_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR6_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR7_BE4, cck1, mac_idx); +} + static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { @@ -998,18 +1100,18 @@ static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, if (chip_id != RTL8922D) return; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[phy_idx]; + rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_init(rtwdev, mac_idx); + rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_gen3_init(rtwdev, chan, mac_idx); rtw89_phy_bb_wrap_tx_rfsi_scenario_def(rtwdev, mac_idx); rtw89_phy_bb_wrap_tx_rfsi_qam_comp_val(rtwdev, mac_idx); rtw89_phy_bb_set_oob_dpd_qam_comp_val(rtwdev, mac_idx); rtw89_phy_bb_set_mdpd_qam_comp_val(rtwdev, mac_idx); rtw89_phy_bb_set_cim3k_val(rtwdev, mac_idx); - + rtw89_phy_bb_set_cck_cfir_filter_val_gen3(rtwdev, mac_idx); rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, 0, phy_idx); - - rtw89_entity_get_conf(rtwdev, &conf); - chan = conf.chans[phy_idx]; - rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); } diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index e46c32ec8e90..c9bfb163c32e 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10599,6 +10599,17 @@ #define R_CIM3K_SU_FORCE 0x119F8 #define B_CIM3K_SU_FORCE_EN BIT(17) #define B_CIM3K_SU_FORCE_VAL BIT(18) +#define R_QAM3_TH0_BE4 0x119FC +#define B_QAM3_TH0_0_BE4 GENMASK(2, 0) +#define B_QAM3_TH0_1_BE4 GENMASK(5, 3) +#define B_QAM3_TH0_2_BE4 GENMASK(8, 6) +#define B_QAM3_TH0_3_BE4 GENMASK(11, 9) +#define B_QAM3_TH0_4_BE4 GENMASK(14, 12) +#define B_QAM3_TH0_5_BE4 GENMASK(17, 15) +#define B_QAM3_TH0_6_BE4 GENMASK(20, 18) +#define B_QAM3_TH0_7_BE4 GENMASK(23, 21) +#define B_QAM3_TH1_0_BE4 GENMASK(26, 24) +#define B_QAM3_TH1_1_BE4 GENMASK(29, 27) #define R_RFSI_CT_OPT_0_BE4 0x11A94 #define R_RFSI_CT_OPT_8_BE4 0x11A98 #define R_QAM_COMP_TH0_BE4 0x11A9C @@ -10634,6 +10645,65 @@ #define B_BANDEDGE_DBW80_BE4 BIT(31) #define R_BANDEDGE_DBWY_BE4 0x11AD0 #define B_BANDEDGE_DBW160_BE4 BIT(0) +#define R_QAM3_TH1_BE4 0x11BE8 +#define B_QAM3_TH1_2_BE4 GENMASK(2, 0) +#define B_QAM3_TH1_3_BE4 GENMASK(5, 3) +#define B_QAM3_TH1_4_BE4 GENMASK(8, 6) +#define B_QAM3_TH1_5_BE4 GENMASK(11, 9) +#define B_QAM3_TH1_6_BE4 GENMASK(14, 12) +#define B_QAM3_TH1_7_BE4 GENMASK(17, 15) +#define B_QAM3_TH2_0_BE4 GENMASK(20, 18) +#define B_QAM3_TH2_1_BE4 GENMASK(23, 21) +#define B_QAM3_TH2_2_BE4 GENMASK(26, 24) +#define B_QAM3_TH2_3_BE4 GENMASK(29, 27) +#define R_QAM3_TH2_BE4 0x11BEC +#define B_QAM3_TH2_4_BE4 GENMASK(2, 0) +#define B_QAM3_TH2_5_BE4 GENMASK(5, 3) +#define B_QAM3_TH2_6_BE4 GENMASK(8, 6) +#define B_QAM3_TH2_7_BE4 GENMASK(11, 9) +#define B_QAM3_TH3_0_BE4 GENMASK(14, 12) +#define B_QAM3_TH3_1_BE4 GENMASK(17, 15) +#define B_QAM3_TH3_2_BE4 GENMASK(20, 18) +#define B_QAM3_TH3_3_BE4 GENMASK(23, 21) +#define B_QAM3_TH3_4_BE4 GENMASK(26, 24) +#define B_QAM3_TH3_5_BE4 GENMASK(29, 27) +#define R_QAM3_TH3_BE4 0x11BF4 +#define B_QAM3_TH3_6_BE4 GENMASK(2, 0) +#define B_QAM3_TH3_7_BE4 GENMASK(5, 3) +#define B_QAM3_TH5_0_BE4 GENMASK(8, 6) +#define B_QAM3_TH5_1_BE4 GENMASK(11, 9) +#define B_QAM3_TH5_2_BE4 GENMASK(14, 12) +#define B_QAM3_TH5_3_BE4 GENMASK(17, 15) +#define B_QAM3_TH5_4_BE4 GENMASK(20, 18) +#define B_QAM3_TH5_5_BE4 GENMASK(23, 21) +#define B_QAM3_TH5_6_BE4 GENMASK(26, 24) +#define B_QAM3_TH5_7_BE4 GENMASK(29, 27) +#define R_QAM3_TH4_BE4 0x11BF8 +#define B_QAM3_TH4_0_BE4 GENMASK(2, 0) +#define B_QAM3_TH4_1_BE4 GENMASK(5, 3) +#define B_QAM3_TH4_2_BE4 GENMASK(8, 6) +#define B_QAM3_TH4_3_BE4 GENMASK(11, 9) +#define B_QAM3_TH4_4_BE4 GENMASK(14, 12) +#define B_QAM3_TH4_5_BE4 GENMASK(17, 15) +#define B_QAM3_TH4_6_BE4 GENMASK(20, 18) +#define B_QAM3_TH4_7_BE4 GENMASK(23, 21) +#define B_QAM3_CFIR0_BE4 BIT(24) +#define B_QAM3_CFIR1_BE4 BIT(25) +#define B_QAM3_CFIR2_BE4 BIT(26) +#define B_QAM3_CFIR3_BE4 BIT(27) +#define B_QAM3_CFIR4_BE4 BIT(28) +#define B_QAM3_CFIR5_BE4 BIT(29) +#define B_QAM3_CFIR6_BE4 BIT(30) +#define B_QAM3_CFIR7_BE4 BIT(31) +#define R_QAM3_FLTR_BE4 0x11BFC +#define B_QAM3_FLTR0_BE4 GENMASK(1, 0) +#define B_QAM3_FLTR1_BE4 GENMASK(3, 2) +#define B_QAM3_FLTR2_BE4 GENMASK(5, 4) +#define B_QAM3_FLTR3_BE4 GENMASK(7, 6) +#define B_QAM3_FLTR4_BE4 GENMASK(9, 8) +#define B_QAM3_FLTR5_BE4 GENMASK(11, 10) +#define B_QAM3_FLTR6_BE4 GENMASK(13, 12) +#define B_QAM3_FLTR7_BE4 GENMASK(15, 14) #define R_SYS_DBCC_BE4 0x20000 #define B_SYS_DBCC_BE4 BIT(0) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index ade89234afd6..c6c37e25e4c8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -320,8 +320,23 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, }; +static const +struct rtw89_bb_wrap_common_data_gen3 rtw8922d_bb_wrap_common_data_gen3_7090 = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_th = {RFSI_QPSK, RFSI_BPSK, RFSI_256QAM, RFSI_MAX, + RFSI_MAX, RFSI_MAX}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .qam_th = {RFSI_MAX, RFSI_MAX, RFSI_256QAM, RFSI_MAX, + RFSI_MAX, RFSI_MAX}, + }}, + .cck_val = {CFIR_OFF, FILTER_A_OFF}, +}; + static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { .common = &rtw8922d_bb_wrap_common_data_7025, + .common_gen3 = NULL, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .qam_comp_th0 = {0x4000, 0x6400, 0x6500, 0x6000, 0, 0, 0, 0, 0, 0}, @@ -348,6 +363,7 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { .common = &rtw8922d_bb_wrap_common_data_7090, + .common_gen3 = &rtw8922d_bb_wrap_common_data_gen3_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .qam_comp_th0 = {0x4000, 0x6420, 0x6520, 0x6000, 0, 0, 0, 0, 0, 0}, @@ -374,6 +390,7 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = { .common = &rtw8922d_bb_wrap_common_data_7090, + .common_gen3 = &rtw8922d_bb_wrap_common_data_gen3_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .qam_comp_th0 = {0x4000, 0x6420, 0x6520, 0x6000, 0, 0, 0, 0, 0, 0}, From e384397796eaf1ecba9e3d91a3f10ec63c7dbc76 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 15 May 2026 09:44:21 +0800 Subject: [PATCH 077/105] wifi: rtw89: debug: Wi-Fi 7 show count of SER L0 simulation Wi-Fi 7 has dedicated HW register to count SER L0 simulation, i.e. manually triggered by users. Show count of it in dbgfs ser_counters. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 8ee800c76cfe..e5976b8e7056 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -3760,6 +3760,7 @@ rtw89_debug_priv_fw_crash_set(struct rtw89_dev *rtwdev, } struct rtw89_dbg_ser_counters { + unsigned int l0_sim; unsigned int l0; unsigned int l1; unsigned int l0_to_l1; @@ -3780,6 +3781,7 @@ static void rtw89_dbg_get_ser_counters_be(struct rtw89_dev *rtwdev, { const u32 val = rtw89_read32(rtwdev, R_BE_SER_DBG_INFO); + cnt->l0_sim = rtw89_read8(rtwdev, R_BE_SER_L0_DBG_CNT1 + 3); cnt->l0 = u32_get_bits(val, B_BE_SER_L0_COUNTER_MASK); cnt->l1 = u32_get_bits(val, B_BE_SER_L1_COUNTER_MASK); cnt->l0_to_l1 = u32_get_bits(val, B_BE_SER_L0_PROMOTE_L1_EVENT_MASK); @@ -3810,6 +3812,10 @@ static ssize_t rtw89_debug_priv_ser_counters_get(struct rtw89_dev *rtwdev, p += scnprintf(p, end - p, "SER L1 SW Count: %u\n", sw_cnt->l1); p += scnprintf(p, end - p, "SER L2 SW Count: %u\n", sw_cnt->l2); + /* Some chipsets don't have dedicated cnt for SER simulation. */ + p += scnprintf(p, end - p, "---\n"); + p += scnprintf(p, end - p, "SER L0 Simulation Count: %d\n", cnt.l0_sim); + /* Some chipsets won't record SER simulation in HW cnt. */ p += scnprintf(p, end - p, "---\n"); p += scnprintf(p, end - p, "SER L0 Count: %d\n", cnt.l0); From becd1c37efc369694dbbaf0ea2a4c06b83c72546 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 15 May 2026 09:44:22 +0800 Subject: [PATCH 078/105] wifi: rtw89: debug: Wi-Fi 7 update simulation of SER L0/L1 by halt H2C command Wi-Fi 7 FW fixes support of triggering SER L0/L1 simulation via halt H2C command on v0.35.108.0. After that, the halt H2C command trigger for Wi-Fi 6 and Wi-Fi 7 can be the same. Update FW feature table and share the halt H2C command trigger function between Wi-Fi 6 and Wi-Fi 7. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 127 +++++++++------------ drivers/net/wireless/realtek/rtw89/fw.c | 2 +- 2 files changed, 58 insertions(+), 71 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index e5976b8e7056..c0fbb016cea4 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -3543,53 +3543,13 @@ rtw89_debug_priv_early_h2c_set(struct rtw89_dev *rtwdev, return count; } -static int rtw89_dbg_trigger_l1_error_by_halt_h2c_ax(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RESET_FORCE); -} - -static int rtw89_dbg_trigger_l1_error_by_halt_h2c_be(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - rtw89_leave_ps_mode(rtwdev); - - rtw89_write32_set(rtwdev, R_BE_FW_TRIGGER_IDCT_ISR, - B_BE_DMAC_FW_TRIG_IDCT | B_BE_DMAC_FW_ERR_IDCT_IMR); - - return 0; -} - -static int rtw89_dbg_trigger_l1_error_by_halt_h2c(struct rtw89_dev *rtwdev) -{ - const struct rtw89_chip_info *chip = rtwdev->chip; - - switch (chip->chip_gen) { - case RTW89_CHIP_AX: - return rtw89_dbg_trigger_l1_error_by_halt_h2c_ax(rtwdev); - case RTW89_CHIP_BE: - return rtw89_dbg_trigger_l1_error_by_halt_h2c_be(rtwdev); - default: - return -EOPNOTSUPP; - } -} - -static int rtw89_dbg_trigger_l1_error(struct rtw89_dev *rtwdev) +static int rtw89_dbg_trigger_l1_error_ax(struct rtw89_dev *rtwdev) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; - struct rtw89_cpuio_ctrl ctrl_para = {0}; + struct rtw89_cpuio_ctrl ctrl_para = {}; u16 pkt_id; int ret; - if (RTW89_CHK_FW_FEATURE(SIM_SER_L0L1_BY_HALT_H2C, &rtwdev->fw)) - return rtw89_dbg_trigger_l1_error_by_halt_h2c(rtwdev); - - rtw89_leave_ps_mode(rtwdev); - ret = mac->dle_buf_req(rtwdev, 0x20, true, &pkt_id); if (ret) return ret; @@ -3608,6 +3568,52 @@ static int rtw89_dbg_trigger_l1_error(struct rtw89_dev *rtwdev) return 0; } +static int rtw89_dbg_trigger_l1_error_be(struct rtw89_dev *rtwdev) +{ + int ret; + + ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_BE_FW_TRIGGER_IDCT_ISR, + B_BE_DMAC_FW_TRIG_IDCT | B_BE_DMAC_FW_ERR_IDCT_IMR); + + return 0; +} + +static int rtw89_dbg_trigger_l1_error_by_halt_h2c(struct rtw89_dev *rtwdev) +{ + if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) + return -EBUSY; + + return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RESET_FORCE); +} + +static int rtw89_dbg_trigger_l1_error(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + int (*sim_l1)(struct rtw89_dev *rtwdev); + + switch (chip->chip_gen) { + case RTW89_CHIP_AX: + sim_l1 = rtw89_dbg_trigger_l1_error_ax; + break; + case RTW89_CHIP_BE: + sim_l1 = rtw89_dbg_trigger_l1_error_be; + break; + default: + return -EOPNOTSUPP; + } + + if (RTW89_CHK_FW_FEATURE(SIM_SER_L0L1_BY_HALT_H2C, &rtwdev->fw)) + return rtw89_dbg_trigger_l1_error_by_halt_h2c(rtwdev); + + rtw89_leave_ps_mode(rtwdev); + + return sim_l1(rtwdev); +} + static int rtw89_dbg_trigger_l0_error_ax(struct rtw89_dev *rtwdev) { u16 val16; @@ -3632,55 +3638,36 @@ static int rtw89_dbg_trigger_l0_error_ax(struct rtw89_dev *rtwdev) static int rtw89_dbg_trigger_l0_error_be(struct rtw89_dev *rtwdev) { - u8 val8; int ret; ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_CMAC_SEL); if (ret) return ret; - val8 = rtw89_read8(rtwdev, R_BE_CMAC_FUNC_EN); - rtw89_write8(rtwdev, R_BE_CMAC_FUNC_EN, val8 & ~B_BE_TMAC_EN); - mdelay(1); - rtw89_write8(rtwdev, R_BE_CMAC_FUNC_EN, val8); - - return 0; -} - -static int rtw89_dbg_trigger_l0_error_by_halt_h2c_ax(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L0_RESET_FORCE); -} - -static int rtw89_dbg_trigger_l0_error_by_halt_h2c_be(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - rtw89_leave_ps_mode(rtwdev); - rtw89_write32_set(rtwdev, R_BE_CMAC_FW_TRIGGER_IDCT_ISR, B_BE_CMAC_FW_TRIG_IDCT | B_BE_CMAC_FW_ERR_IDCT_IMR); return 0; } +static int rtw89_dbg_trigger_l0_error_by_halt_h2c(struct rtw89_dev *rtwdev) +{ + if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) + return -EBUSY; + + return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L0_RESET_FORCE); +} + static int rtw89_dbg_trigger_l0_error(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; - int (*sim_l0_by_halt_h2c)(struct rtw89_dev *rtwdev); int (*sim_l0)(struct rtw89_dev *rtwdev); switch (chip->chip_gen) { case RTW89_CHIP_AX: - sim_l0_by_halt_h2c = rtw89_dbg_trigger_l0_error_by_halt_h2c_ax; sim_l0 = rtw89_dbg_trigger_l0_error_ax; break; case RTW89_CHIP_BE: - sim_l0_by_halt_h2c = rtw89_dbg_trigger_l0_error_by_halt_h2c_be; sim_l0 = rtw89_dbg_trigger_l0_error_be; break; default: @@ -3688,7 +3675,7 @@ static int rtw89_dbg_trigger_l0_error(struct rtw89_dev *rtwdev) } if (RTW89_CHK_FW_FEATURE(SIM_SER_L0L1_BY_HALT_H2C, &rtwdev->fw)) - return sim_l0_by_halt_h2c(rtwdev); + return rtw89_dbg_trigger_l0_error_by_halt_h2c(rtwdev); rtw89_leave_ps_mode(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index ff3914a16b81..c5a598a507f4 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -931,8 +931,8 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 84, 0, RFK_PRE_NOTIFY_MCC_V1), __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 84, 0, ADDR_CAM_V0), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 92, 0, TX_HISTORY_V1), - __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 97, 0, SIM_SER_L0L1_BY_HALT_H2C), __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), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, From 125fd4514fa8ca355a6b8e45bf4ff319c483a77b Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 15 May 2026 09:44:23 +0800 Subject: [PATCH 079/105] wifi: rtw89: fw: dump status of H2C command and C2H event for SER When SER (system error recovery) happens, there may be some handshake between FW and SW, e.g. SER Level 1. These handshake are based on H2C commands and C2H events. Dump the status of them to enhance SER debug. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index c5a598a507f4..29d8d13d11fe 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -8293,6 +8293,12 @@ void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev) rtw89_info(rtwdev, "FW MISC = 0x%x\n", rtw89_read32(rtwdev, R_AX_UDM3)); rtw89_info(rtwdev, "R_AX_HALT_C2H = 0x%x\n", rtw89_read32(rtwdev, R_AX_HALT_C2H)); + rtw89_info(rtwdev, "R_AX_HALT_C2H_CTRL = 0x%x\n", + rtw89_read32(rtwdev, R_AX_HALT_C2H_CTRL)); + rtw89_info(rtwdev, "R_AX_HALT_H2C = 0x%x\n", + rtw89_read32(rtwdev, R_AX_HALT_H2C)); + rtw89_info(rtwdev, "R_AX_HALT_H2C_CTRL = 0x%x\n", + rtw89_read32(rtwdev, R_AX_HALT_H2C_CTRL)); rtw89_info(rtwdev, "R_AX_SER_DBG_INFO = 0x%x\n", rtw89_read32(rtwdev, R_AX_SER_DBG_INFO)); From 779bbe1902f29d0ef131249ddd42a8dfbe21d0fb Mon Sep 17 00:00:00 2001 From: Dian-Syuan Yang Date: Fri, 15 May 2026 09:44:24 +0800 Subject: [PATCH 080/105] wifi: rtw89: pci: enable LTR based on pcie control register Originally, driver always transmits LTR (Latency Tolerance Reporting) to pcie host, but it may cause pcie link down on some platforms because LTR is not supported. As a result, driver will check the control register of LTR setting to decide whether to enable LTR feature. This applies to Wi-Fi 6 chips only. For Wi-Fi 7 chips, although the driver still issues LTR, the hardware has its own internal logic to determine whether to actually transmit it to pcie host. Signed-off-by: Dian-Syuan Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 64554eb35a72..f7107dc05b71 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -3039,6 +3039,17 @@ static int rtw89_pci_mode_op(struct rtw89_dev *rtwdev) return 0; } +static bool rtw89_pci_dev_ltr_enabled(struct rtw89_dev *rtwdev) +{ + struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + struct pci_dev *pdev = rtwpci->pdev; + u16 cap; + + pcie_capability_read_word(pdev, PCI_EXP_DEVCTL2, &cap); + + return !!(cap & PCI_EXP_DEVCTL2_LTR_EN); +} + static int rtw89_pci_ops_deinit(struct rtw89_dev *rtwdev) { const struct rtw89_pci_info *info = rtwdev->pci_info; @@ -3143,7 +3154,7 @@ int rtw89_pci_ltr_set(struct rtw89_dev *rtwdev, bool en) { u32 val; - if (!en) + if (!en || !rtw89_pci_dev_ltr_enabled(rtwdev)) return 0; val = rtw89_read32(rtwdev, R_AX_LTR_CTRL_0); @@ -3179,6 +3190,9 @@ int rtw89_pci_ltr_set_v1(struct rtw89_dev *rtwdev, bool en) u32 dec_ctrl; u32 val32; + if (!rtw89_pci_dev_ltr_enabled(rtwdev)) + return 0; + val32 = rtw89_read32(rtwdev, R_AX_LTR_CTRL_0); if (rtw89_pci_ltr_is_err_reg_val(val32)) return -EINVAL; From e0bb1246e3078912396fb6ad5a18fe067c38e462 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:25 +0800 Subject: [PATCH 081/105] wifi: rtw89: pci: not disable PCI completion timeout control for a variant of RTL8922DE The variant of RTL8922DE change the design, and no need to disable PCI completion timeout. Apply the setting accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index f7107dc05b71..fe1152c560bd 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -4380,10 +4380,20 @@ static void rtw89_pci_l1ss_cfg(struct rtw89_dev *rtwdev) static void rtw89_pci_cpl_timeout_cfg(struct rtw89_dev *rtwdev) { struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; + struct rtw89_hal *hal = &rtwdev->hal; struct pci_dev *pdev = rtwpci->pdev; + bool dis = true; - pcie_capability_set_word(pdev, PCI_EXP_DEVCTL2, - PCI_EXP_DEVCTL2_COMP_TMOUT_DIS); + if (chip_id == RTL8922D && hal->cid == RTL8922D_CID7090) + dis = false; + + if (dis) + pcie_capability_set_word(pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_COMP_TMOUT_DIS); + else + pcie_capability_clear_word(pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_COMP_TMOUT_DIS); } static int rtw89_pci_poll_io_idle_ax(struct rtw89_dev *rtwdev) From 8ee752289e1fc4a0fc55b042d467d6726da65cff Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:26 +0800 Subject: [PATCH 082/105] wifi: rtw89: pci: disable PCI PHY error flag 8 As the PHY error flag 8 works improperly, disable it to prevent false alarm causing SER. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.h | 4 +++- drivers/net/wireless/realtek/rtw89/pci_be.c | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index d0d1e2b99698..c3f2d0df5846 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(0)) +#define PHY_ERR_IMR_DIS (BIT(9) | BIT(8) | BIT(0)) #define RAC_ANA41 0x41 #define PHY_ERR_FLAG_EN BIT(6) @@ -963,6 +963,8 @@ #define R_BE_PCIE_HRPWM 0x30C0 #define R_BE_PCIE_CRPWM 0x30C4 +#define R_BE_PCIE_HCI2FW_ISR 0x30CC + #define R_BE_L1_2_CTRL_HCILDO 0x3110 #define B_BE_PM_CLKREQ_EXT_RB BIT(11) #define B_BE_PCIE_DIS_RTK_PRST_N_L1_2 BIT(10) diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c index 473d491eb3f4..6390980b8ee0 100644 --- a/drivers/net/wireless/realtek/rtw89/pci_be.c +++ b/drivers/net/wireless/realtek/rtw89/pci_be.c @@ -347,6 +347,7 @@ static void rtw89_pci_pcie_setting_be(struct rtw89_dev *rtwdev) rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_R_SYM_PRST_CPHY_RST); rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_USUS_OFFCAPC_EN); + rtw89_write32(rtwdev, R_BE_PCIE_HCI2FW_ISR, 0xFFFFFFFF); } static void rtw89_pci_ser_setting_be(struct rtw89_dev *rtwdev) @@ -843,6 +844,8 @@ static int __maybe_unused rtw89_pci_resume_be(struct device *dev) rtw89_write16_set(rtwdev, RAC_DIRECT_OFFESET_L0_G1 + RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN); } + + rtw89_write32(rtwdev, R_BE_PCIE_HCI2FW_ISR, 0xFFFFFFFF); } rtw89_pci_basic_cfg(rtwdev, true); From 8c9e5fb13434ca76a0f8247e8f9dc1bbfb298e34 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:27 +0800 Subject: [PATCH 083/105] wifi: rtw89: clear auto K delay value before downloading firmware Once downloading firmware, do calibration on delay function to ensure firmware can do proper delay for hardware IO. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 3 +++ drivers/net/wireless/realtek/rtw89/mac.h | 12 ++++++++++++ drivers/net/wireless/realtek/rtw89/mac_be.c | 7 +++++++ drivers/net/wireless/realtek/rtw89/reg.h | 4 ++++ 4 files changed, 26 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 87e3320b5a81..6153162ad9b0 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4318,6 +4318,8 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb) return ret; } + rtw89_mac_fwdl_preconfig(rtwdev); + ret = rtw89_fw_download(rtwdev, RTW89_FW_NORMAL, include_bb); if (ret) return ret; @@ -7477,6 +7479,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .reset_pwr_state = rtw89_mac_reset_pwr_state_ax, .disable_cpu = rtw89_mac_disable_cpu_ax, + .fwdl_preconfig = NULL, .fwdl_enable_wcpu = rtw89_mac_enable_cpu_ax, .fwdl_get_status = rtw89_fw_get_rdy_ax, .fwdl_check_path_ready = rtw89_fwdl_check_path_ready_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 75691a9549a7..69f8d7d818a0 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1112,6 +1112,7 @@ struct rtw89_mac_gen_def { int (*reset_pwr_state)(struct rtw89_dev *rtwdev); void (*disable_cpu)(struct rtw89_dev *rtwdev); + void (*fwdl_preconfig)(struct rtw89_dev *rtwdev); int (*fwdl_enable_wcpu)(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw, bool include_bb); u8 (*fwdl_get_status)(struct rtw89_dev *rtwdev, enum rtw89_fwdl_check_type type); @@ -1725,6 +1726,17 @@ static inline int rtw89_mac_efuse_read_ecv(struct rtw89_dev *rtwdev) return mac->efuse_read_ecv(rtwdev); } +static inline +void rtw89_mac_fwdl_preconfig(struct rtw89_dev *rtwdev) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + + if (!mac->fwdl_preconfig) + return; + + mac->fwdl_preconfig(rtwdev); +} + static inline void rtw89_fwdl_secure_idmem_share_mode(struct rtw89_dev *rtwdev, u8 mode) { diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index dd0dbe78a1ed..f24c119b99f1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -622,6 +622,12 @@ static void rtw89_mac_disable_cpu_be(struct rtw89_dev *rtwdev) rtw89_write32(rtwdev, R_BE_UDM2, 0); } +static void rtw89_mac_fwdl_preconfig_be(struct rtw89_dev *rtwdev) +{ + rtw89_write32_clr(rtwdev, R_BE_FW_AUTO_CAL_DELAY, B_BE_WCPU_FW_DELAY_COUNT_VALID); + rtw89_write32_mask(rtwdev, R_BE_FW_AUTO_CAL_DELAY, B_BE_WCPU_FW_DELAY_COUNT_MASK, 0); +} + static void set_cpu_en(struct rtw89_dev *rtwdev, bool include_bb) { u32 set = B_BE_WLANCPU_FWDL_EN; @@ -3288,6 +3294,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .reset_pwr_state = rtw89_mac_reset_pwr_state_be, .disable_cpu = rtw89_mac_disable_cpu_be, + .fwdl_preconfig = rtw89_mac_fwdl_preconfig_be, .fwdl_enable_wcpu = rtw89_mac_fwdl_enable_wcpu_be, .fwdl_get_status = fwdl_get_status_be, .fwdl_check_path_ready = rtw89_fwdl_check_path_ready_be, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index c9bfb163c32e..c2ffa6c196e4 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -4329,6 +4329,10 @@ #define R_BE_SECURE_BOOT_MALLOC_INFO 0x0184 +#define R_BE_FW_AUTO_CAL_DELAY 0x0188 +#define B_BE_WCPU_FW_DELAY_COUNT_VALID BIT(15) +#define B_BE_WCPU_FW_DELAY_COUNT_MASK GENMASK(14, 0) + #define R_BE_FWS0IMR 0x0190 #define B_BE_FS_HALT_H2C_INT_EN BIT(31) #define B_BE_FS_FSM_HIOE_TO_EVENT_INT_EN BIT(30) From dcf5c7262bffa2cbafc6523be63c287d15d8ae22 Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Fri, 15 May 2026 09:44:28 +0800 Subject: [PATCH 084/105] wifi: rtw89: wow: send ARP reply packets instead of Null packets to keep alive In WoWLAN mode, the firmware periodically sends Null packets to the AP to keep the connection alive and prevent the AP from disconnecting the client due to inactivity. However, it was observed that certain APs, such as TP-Link Archer BE800, do not recognize Null packets as valid activity and still drop the connection. Replacing Null packets with ARP reply packets effectively resolves this compatibility issue and maintains the connection. Specifically, while the firmware typically handles standard ARP offloads by dynamically modifying target IP/MAC fields, these keep-alive ARP reply packets are pre-filled by the driver with the DUT's own MAC and IP addresses for both sender and target fields. In this case, the firmware transmits the packets as-is without further modification. This approach ensures compatibility with APs that require valid Layer 3 traffic for activity monitoring while simplifying the firmware's processing logic during WoWLAN mode. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 29d8d13d11fe..eb5683d64461 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2958,6 +2958,8 @@ static struct sk_buff *rtw89_arp_response_get(struct rtw89_dev *rtwdev, ether_addr_copy(arp_skb->sender_hw, rtwvif_link->mac_addr); arp_skb->sender_ip = rtwvif->ip_addr; + ether_addr_copy(arp_skb->target_hw, rtwvif_link->mac_addr); + arp_skb->target_ip = rtwvif->ip_addr; return skb; } @@ -9617,7 +9619,7 @@ int rtw89_fw_h2c_keep_alive(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtw if (enable) { ret = rtw89_fw_h2c_add_general_pkt(rtwdev, rtwvif_link, - RTW89_PKT_OFLD_TYPE_NULL_DATA, + RTW89_PKT_OFLD_TYPE_ARP_RSP, &pkt_id); if (ret) return -EPERM; From 63ccdfac8677387dfdbd9d4336089e9823280704 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 15 May 2026 09:44:29 +0800 Subject: [PATCH 085/105] wifi: rtw89: correct drop logic for malformed AMPDU frames The previous commit aims to fix issue caused by malformed AMPDU frames. But the drop logic fails to deal with the first AMPDU packet paired with certain range of sequence number, and leads to unexpected packet drop. It is more likely to encounter this failure when there are busy traffic during rekey process and could lead to disconnection from the AP. Fix this by adding a initial state judgement and only reset status during pairwise rekey. Fixes: bda294ed0ed0 ("wifi: rtw89: Drop malformed AMPDU frames with abnormal PN") Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 3 ++- drivers/net/wireless/realtek/rtw89/mac80211.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 7b26aedc0883..0f3168d85369 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3948,7 +3948,8 @@ static bool rtw89_core_skb_pn_valid(struct rtw89_dev *rtwdev, last_pn = tid_stats->last_pn; if (pn > last_pn) { - if (ieee80211_sn_less(mpdu_sn, tid_stats->last_sn)) { + if (last_pn != -1LL && + ieee80211_sn_less(mpdu_sn, tid_stats->last_sn)) { dev_kfree_skb_any(skb); return false; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 330ece51286d..aade5c5b79e8 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -979,7 +979,8 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, rtw89_err(rtwdev, "failed to add key to sec cam\n"); return ret; } - rtw89_core_tid_rx_stats_reset(rtwdev); + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + rtw89_core_tid_rx_stats_reset(rtwdev); break; case DISABLE_KEY: flush_work(&rtwdev->txq_work); From f98b4684930f631d878bdcf412196fa570692f32 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:30 +0800 Subject: [PATCH 086/105] wifi: rtw89: 8922d: change naming number and update values for WDE/PLE quota The WDE/PLE quota are to configure memory size for TX/RX. Some quota are renamed, and some values are changed. Update them accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 7 ++++--- drivers/net/wireless/realtek/rtw89/mac.h | 7 ++++--- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 18 ++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 6153162ad9b0..8df1b9af719d 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1847,18 +1847,19 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_qt28 = {1040, 0, 32, 48, 43, 13, 208, 0, 62, 14, 24, 0,}, /* USB 52C USB3.0 */ .ple_qt42 = {1068, 0, 16, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,}, - .ple_qt42_v2 = {91, 91, 32, 16, 19, 13, 91, 91, 44, 18, 1, 4, 0, 0,}, /* USB 52C USB3.0 */ .ple_qt43 = {3068, 0, 32, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,}, - .ple_qt43_v2 = {645, 645, 32, 16, 2062, 2056, 2134, 2134, 2087, 2061, 1, 2047, 0, 0,}, /* DLFW 52C */ .ple_qt44 = {0, 0, 16, 256, 0, 0, 0, 0, 0, 0, 0, 0,}, + .ple_qt44_v2 = {91, 91, 32, 16, 19, 13, 91, 91, 44, 18, 1, 4, 0, 0,}, /* DLFW 52C */ .ple_qt45 = {0, 0, 32, 256, 0, 0, 0, 0, 0, 0, 0, 0,}, + .ple_qt45_v2 = {645, 645, 32, 2055, 2058, 2052, 2132, 2132, 2083, 2057, 1, 2043, 0, 0,}, /* 8852C PCIE SCC */ .ple_qt46 = {525, 0, 16, 20, 13, 13, 178, 0, 32, 62, 8, 16,}, /* 8852C PCIE SCC */ .ple_qt47 = {525, 0, 32, 20, 1034, 13, 1199, 0, 1053, 62, 160, 1037,}, + .ple_qt47_v2 = {0, 0, 32, 2703, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,}, .ple_qt57 = {147, 0, 16, 20, 13, 13, 178, 0, 32, 14, 8, 0,}, /* PCIE 64 */ .ple_qt58 = {147, 0, 16, 20, 157, 13, 229, 0, 172, 14, 24, 0,}, @@ -1889,7 +1890,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .rsvd1_size0 = {587776, 2048,}, .rsvd1_size2 = {391168, 2048,}, .dle_input3 = {0, 0, 0, 16384, 0, 2048, 0, 0,}, - .dle_input18 = {128, 128, 11454, 2048, 0, 2048, 24, 24,}, + .dle_input20 = {128, 128, 11454, 2048, 0, 2048, 24, 24,}, }; EXPORT_SYMBOL(rtw89_mac_size); diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 69f8d7d818a0..3d57f0acfba1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1007,13 +1007,14 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt27; const struct rtw89_ple_quota ple_qt28; const struct rtw89_ple_quota ple_qt42; - const struct rtw89_ple_quota ple_qt42_v2; const struct rtw89_ple_quota ple_qt43; - const struct rtw89_ple_quota ple_qt43_v2; const struct rtw89_ple_quota ple_qt44; + const struct rtw89_ple_quota ple_qt44_v2; const struct rtw89_ple_quota ple_qt45; + const struct rtw89_ple_quota ple_qt45_v2; const struct rtw89_ple_quota ple_qt46; const struct rtw89_ple_quota ple_qt47; + const struct rtw89_ple_quota ple_qt47_v2; const struct rtw89_ple_quota ple_qt57; const struct rtw89_ple_quota ple_qt58; const struct rtw89_ple_quota ple_qt59; @@ -1037,7 +1038,7 @@ struct rtw89_mac_size_set { const struct rtw89_dle_rsvd_size rsvd1_size0; const struct rtw89_dle_rsvd_size rsvd1_size2; const struct rtw89_dle_input dle_input3; - const struct rtw89_dle_input dle_input18; + const struct rtw89_dle_input dle_input20; }; extern const struct rtw89_mac_size_set rtw89_mac_size; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index c6c37e25e4c8..c1ce507a250a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -37,9 +37,7 @@ static const struct rtw89_hfc_ch_cfg rtw8922d_hfc_chcfg_pcie[] = { {0, 601, 0}, /* B0HIQ */ {2, 603, 0}, /* B1MGQ */ {0, 601, 0}, /* B1HIQ */ - {0, 0, 0}, /* FWCMDQ */ - {0, 0, 0}, /* BMC */ - {0, 0, 0}, /* H2D */ + {0, 0, 0}, /* GCQ */ }; static const struct rtw89_hfc_pub_cfg rtw8922d_hfc_pubcfg_pcie = { @@ -62,20 +60,20 @@ static const struct rtw89_hfc_param_ini rtw8922d_hfc_param_ini_pcie[] = { static const struct rtw89_dle_mem rtw8922d_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size16_v1, &rtw89_mac_size.ple_size20_v1, &rtw89_mac_size.wde_qt19_v1, - &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt42_v2, - &rtw89_mac_size.ple_qt43_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt44_v2, + &rtw89_mac_size.ple_qt45_v2, &rtw89_mac_size.ple_rsvd_qt9, &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, - &rtw89_mac_size.dle_input18}, + &rtw89_mac_size.dle_input20}, [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size16_v1, &rtw89_mac_size.ple_size20_v1, &rtw89_mac_size.wde_qt19_v1, - &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt42_v2, - &rtw89_mac_size.ple_qt43_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt44_v2, + &rtw89_mac_size.ple_qt45_v2, &rtw89_mac_size.ple_rsvd_qt9, &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, - &rtw89_mac_size.dle_input18}, + &rtw89_mac_size.dle_input20}, [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size18_v1, &rtw89_mac_size.ple_size22_v1, &rtw89_mac_size.wde_qt3, &rtw89_mac_size.wde_qt3, &rtw89_mac_size.ple_qt5_v2, - &rtw89_mac_size.ple_qt5_v2, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.ple_qt47_v2, &rtw89_mac_size.ple_rsvd_qt1, &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, &rtw89_mac_size.dle_input3}, [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, From d270863883b93fa058f36f57a261b4a518642b78 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:31 +0800 Subject: [PATCH 087/105] wifi: rtw89: mac: add field of release report size to DLE quota The release report size is defined to indicate quota for TX completion. Define the field as vendor driver does. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 4666e3041ef3..aaef2c8b35be 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4167,6 +4167,7 @@ struct rtw89_dle_input { u32 h2c_max_size; u32 rx_amsdu_size; /* unit: KB */ u32 c2h_max_size; + u32 rls_rpt_max_size; u32 mpdu_info_tbl_b0; u32 mpdu_info_tbl_b1; }; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 8df1b9af719d..5c22b2accf7c 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1889,8 +1889,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .rsvd0_size6 = {40960, 0,}, .rsvd1_size0 = {587776, 2048,}, .rsvd1_size2 = {391168, 2048,}, - .dle_input3 = {0, 0, 0, 16384, 0, 2048, 0, 0,}, - .dle_input20 = {128, 128, 11454, 2048, 0, 2048, 24, 24,}, + .dle_input3 = {0, 0, 0, 16384, 0, 2048, 0, 0, 0,}, + .dle_input20 = {128, 128, 11454, 2048, 0, 2048, 1024, 24, 24,}, }; EXPORT_SYMBOL(rtw89_mac_size); From 77be46f2819c141c6f970156e155fcfaf4c8dddd Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:32 +0800 Subject: [PATCH 088/105] wifi: rtw89: mac: consolidate quota into a struct for variant chips RTL8922D has many variants, using different quota tables. Consolidate the quota data into a struct, and then select corresponding table for the chip variant. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 20 +++++++++++++++++-- drivers/net/wireless/realtek/rtw89/mac.c | 6 ++++-- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 6 ++++-- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 6 ++++-- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 6 ++++-- .../net/wireless/realtek/rtw89/rtw8852bt.c | 6 ++++-- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 6 ++++-- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 7 +++++-- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 7 +++++-- 9 files changed, 52 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index aaef2c8b35be..27d9a916d58c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4239,6 +4239,11 @@ struct rtw89_fw_def { u16 fw_b_aid; }; +struct rtw89_qta_def { + const struct rtw89_hfc_param_ini *hfc_param_ini[RTW89_HCI_DLE_TYPE_NUM]; + const struct rtw89_dle_mem *dle_mem[RTW89_HCI_DLE_TYPE_NUM]; +}; + struct rtw89_phy_table { const struct rtw89_reg2_def *regs; u32 n_regs; @@ -4654,8 +4659,7 @@ struct rtw89_chip_info { u16 max_rx_agg_num; bool dis_2g_40m_ul_ofdma; u32 rsvd_ple_ofst; - const struct rtw89_hfc_param_ini *hfc_param_ini[RTW89_HCI_DLE_TYPE_NUM]; - const struct rtw89_dle_mem *dle_mem[RTW89_HCI_DLE_TYPE_NUM]; + struct rtw89_qta_def qta_def; u8 wde_qempty_acq_grpnum; u8 wde_qempty_mgq_grpsel; u32 rf_base_addr[2]; @@ -4784,6 +4788,7 @@ struct rtw89_chip_variant { bool no_mcs_12_13: 1; u32 fw_min_ver_code; const struct rtw89_fw_def *fw_def_override; + const struct rtw89_qta_def *qta_def_override; }; union rtw89_bus_info { @@ -7743,6 +7748,17 @@ const struct rtw89_fw_def *rtw89_chip_get_fw_def(struct rtw89_dev *rtwdev) return __rtw89_chip_get_fw_def(rtwdev->chip, rtwdev->variant); } +static inline +const struct rtw89_qta_def *rtw89_chip_get_qta_def(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_variant *variant = rtwdev->variant; + + if (variant && variant->qta_def_override) + return variant->qta_def_override; + + return &rtwdev->chip->qta_def; +} + static inline void rtw89_load_txpwr_table(struct rtw89_dev *rtwdev, const struct rtw89_txpwr_table *tbl) { diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 5c22b2accf7c..bd606d1da634 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -894,11 +894,12 @@ EXPORT_SYMBOL(rtw89_mac_set_err_status); static int hfc_reset_param(struct rtw89_dev *rtwdev) { + const struct rtw89_qta_def *qta_def = rtw89_chip_get_qta_def(rtwdev); const struct rtw89_hfc_param_ini *param_ini, *param_inis; struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; u8 qta_mode = rtwdev->mac.dle_info.qta_mode; - param_inis = rtwdev->chip->hfc_param_ini[rtwdev->hci.dle_type]; + param_inis = qta_def->hfc_param_ini[rtwdev->hci.dle_type]; if (!param_inis) return -EINVAL; @@ -1897,10 +1898,11 @@ EXPORT_SYMBOL(rtw89_mac_size); static const struct rtw89_dle_mem *get_dle_mem_cfg(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode) { + const struct rtw89_qta_def *qta_def = rtw89_chip_get_qta_def(rtwdev); struct rtw89_mac_info *mac = &rtwdev->mac; const struct rtw89_dle_mem *cfg, *cfgs; - cfgs = rtwdev->chip->dle_mem[rtwdev->hci.dle_type]; + cfgs = qta_def->dle_mem[rtwdev->hci.dle_type]; if (!cfgs) return NULL; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 2fb54e1fc7ab..60f362593696 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2648,14 +2648,16 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x2f800, - .hfc_param_ini = {rtw8851b_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8851b_hfc_param_ini_pcie, rtw8851b_hfc_param_ini_usb, rtw8851b_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8851b_dle_mem_pcie, + .dle_mem = {rtw8851b_dle_mem_pcie, rtw8851b_dle_mem_usb2, rtw8851b_dle_mem_usb3, NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000}, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index d3b6e8a7f5bd..2c1f166e687f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2385,14 +2385,16 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie, rtw8852a_hfc_param_ini_usb, rtw8852a_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8852a_dle_mem_pcie, + .dle_mem = {rtw8852a_dle_mem_pcie, rtw8852a_dle_mem_usb, rtw8852a_dle_mem_usb, NULL}, + }, .wde_qempty_acq_grpnum = 16, .wde_qempty_mgq_grpsel = 16, .rf_base_addr = {0xc000, 0xd000}, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 989ac38fe359..4e7b068aaa75 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -979,14 +979,16 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x2f800, - .hfc_param_ini = {rtw8852b_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8852b_hfc_param_ini_pcie, rtw8852b_hfc_param_ini_usb, rtw8852b_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8852b_dle_mem_pcie, + .dle_mem = {rtw8852b_dle_mem_pcie, rtw8852b_dle_mem_usb3, rtw8852b_dle_mem_usb3, NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000, 0xf000}, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index b58561e141cb..7fcc877f6ea0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -825,8 +825,10 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852bt_hfc_param_ini_pcie, NULL, NULL, NULL}, - .dle_mem = {rtw8852bt_dle_mem_pcie, NULL, NULL, NULL}, + .qta_def = { + .hfc_param_ini = {rtw8852bt_hfc_param_ini_pcie, NULL, NULL, NULL}, + .dle_mem = {rtw8852bt_dle_mem_pcie, NULL, NULL, NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000, 0xf000}, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 9ecd140dcdd5..7bb1264bcaef 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3174,14 +3174,16 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie, rtw8852c_hfc_param_ini_usb, rtw8852c_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8852c_dle_mem_pcie, + .dle_mem = {rtw8852c_dle_mem_pcie, rtw8852c_dle_mem_usb2, rtw8852c_dle_mem_usb3, NULL}, + }, .wde_qempty_acq_grpnum = 16, .wde_qempty_mgq_grpsel = 16, .rf_base_addr = {0xe000, 0xf000}, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 07270e83b778..ad3618dfd57d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -3169,14 +3169,16 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x8f800, - .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, rtw8922a_hfc_param_ini_usb2, rtw8922a_hfc_param_ini_usb3, NULL}, - .dle_mem = {rtw8922a_dle_mem_pcie, + .dle_mem = {rtw8922a_dle_mem_pcie, rtw8922a_dle_mem_usb2, rtw8922a_dle_mem_usb3, NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000, 0xf000}, @@ -3307,6 +3309,7 @@ const struct rtw89_chip_variant rtw8922ae_vs_variant = { .no_mcs_12_13 = true, .fw_min_ver_code = RTW89_FW_VER_CODE(0, 35, 54, 0), .fw_def_override = NULL, + .qta_def_override = NULL, }; EXPORT_SYMBOL(rtw8922ae_vs_variant); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index c1ce507a250a..fe3b0dc2a027 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -3171,8 +3171,10 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .max_rx_agg_num = 256, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x5f800, - .hfc_param_ini = {rtw8922d_hfc_param_ini_pcie, NULL, NULL, NULL}, - .dle_mem = {rtw8922d_dle_mem_pcie, NULL, NULL, NULL}, + .qta_def = { + .hfc_param_ini = {rtw8922d_hfc_param_ini_pcie, NULL, NULL, NULL}, + .dle_mem = {rtw8922d_dle_mem_pcie, NULL, NULL, NULL}, + }, .wde_qempty_acq_grpnum = 8, .wde_qempty_mgq_grpsel = 8, .rf_base_addr = {0x3e000, 0x3f000}, @@ -3308,6 +3310,7 @@ const struct rtw89_chip_variant rtw8922de_vs_variant = { .no_mcs_12_13 = true, .fw_min_ver_code = RTW89_FW_VER_CODE(0, 0, 0, 0), .fw_def_override = &rtw8922de_vs_fw_def, + .qta_def_override = NULL, }; EXPORT_SYMBOL(rtw8922de_vs_variant); From 9d69ffa4f8da8257e247a4b7163e0aae4b624d7e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:33 +0800 Subject: [PATCH 089/105] wifi: rtw89: 8922d: add quota for RTL8922DE variant The existing quota set is for RTL8922DE-VS, so rename the existing tables. Add new quota set for RTL8922DE, containing more memory to yield better performance, so rearrange quota accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-14-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 8 ++ drivers/net/wireless/realtek/rtw89/mac.h | 8 ++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 73 +++++++++++++++++-- 3 files changed, 83 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index bd606d1da634..8c395517bd2f 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1748,6 +1748,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_size18_v1 = {RTW89_WDE_PG_64, 0, 640, 0,}, /* 8852C PCIE SCC */ .wde_size19 = {RTW89_WDE_PG_64, 3328, 0,}, + .wde_size22_v1 = {RTW89_WDE_PG_128, 384, 0, 0,}, .wde_size23 = {RTW89_WDE_PG_64, 1022, 2,}, /* 8852B USB2.0/USB3.0 SCC turbo */ .wde_size30 = {RTW89_WDE_PG_64, 220, 36,}, @@ -1781,6 +1782,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_size22_v1 = {RTW89_PLE_PG_128, 2736, 0, 40960,}, /* 8851B USB2.0 SCC turbo */ .ple_size27 = {RTW89_PLE_PG_128, 1396, 12,}, + .ple_size29_v1 = {RTW89_PLE_PG_128, 1895, 182, 49152,}, /* 8852B USB3.0 SCC turbo */ .ple_size31 = {RTW89_PLE_PG_128, 1392, 16,}, /* 8852C USB2.0 */ @@ -1811,6 +1813,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_qt18 = {3228, 60, 0, 40,}, .wde_qt19_v1 = {613, 6, 0, 20,}, .wde_qt23 = {958, 48, 0, 16,}, + .wde_qt23_v1 = {371, 3, 0, 10,}, /* 8852B USB2.0/USB3.0 SCC turbo */ .wde_qt30 = {210, 2, 0, 8,}, /* 8852C USB2.0 */ @@ -1868,6 +1871,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* 8851B USB2.0 SCC turbo */ .ple_qt61 = {858, 0, 16, 48, 4, 13, 370, 0, 32, 14, 8, 0, 0,}, .ple_qt62 = {858, 0, 32, 48, 37, 13, 403, 0, 65, 14, 24, 0, 0,}, + .ple_qt64_v2 = {91, 91, 32, 16, 19, 13, 93, 93, 44, 34, 1, 4, 0, 0,}, + .ple_qt65_v2 = {645, 645, 32, 1380, 1383, 1377, 1457, 1457, 1408, 1398, 1, 1368,}, /* USB2.0 52C */ .ple_qt78 = {1560, 0, 16, 48, 13, 13, 390, 0, 32, 38, 8, 16, 0,}, /* USB2.0 52C */ @@ -1888,10 +1893,13 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .rsvd0_size3 = {57344, 0,}, .rsvd0_size5 = {32768, 0,}, .rsvd0_size6 = {40960, 0,}, + .rsvd0_size17 = {49152, 0,}, .rsvd1_size0 = {587776, 2048,}, .rsvd1_size2 = {391168, 2048,}, + .rsvd1_size3 = {315008, 78208,}, .dle_input3 = {0, 0, 0, 16384, 0, 2048, 0, 0, 0,}, .dle_input20 = {128, 128, 11454, 2048, 0, 2048, 1024, 24, 24,}, + .dle_input28 = {128, 128, 11454, 2048, 0, 2048, 2048, 24, 24,}, }; EXPORT_SYMBOL(rtw89_mac_size); diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 3d57f0acfba1..539061fc15e8 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -946,6 +946,7 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size wde_size18; const struct rtw89_dle_size wde_size18_v1; const struct rtw89_dle_size wde_size19; + const struct rtw89_dle_size wde_size22_v1; const struct rtw89_dle_size wde_size23; const struct rtw89_dle_size wde_size30; const struct rtw89_dle_size wde_size31; @@ -966,6 +967,7 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size ple_size20_v1; const struct rtw89_dle_size ple_size22_v1; const struct rtw89_dle_size ple_size27; + const struct rtw89_dle_size ple_size29_v1; const struct rtw89_dle_size ple_size31; const struct rtw89_dle_size ple_size34; const struct rtw89_wde_quota wde_qt0; @@ -984,6 +986,7 @@ struct rtw89_mac_size_set { const struct rtw89_wde_quota wde_qt18; const struct rtw89_wde_quota wde_qt19_v1; const struct rtw89_wde_quota wde_qt23; + const struct rtw89_wde_quota wde_qt23_v1; const struct rtw89_wde_quota wde_qt30; const struct rtw89_wde_quota wde_qt31; const struct rtw89_ple_quota ple_qt0; @@ -1020,6 +1023,8 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt59; const struct rtw89_ple_quota ple_qt61; const struct rtw89_ple_quota ple_qt62; + const struct rtw89_ple_quota ple_qt64_v2; + const struct rtw89_ple_quota ple_qt65_v2; const struct rtw89_ple_quota ple_qt78; const struct rtw89_ple_quota ple_qt79; const struct rtw89_ple_quota ple_qt_52a_wow; @@ -1035,10 +1040,13 @@ struct rtw89_mac_size_set { const struct rtw89_dle_rsvd_size rsvd0_size3; const struct rtw89_dle_rsvd_size rsvd0_size5; const struct rtw89_dle_rsvd_size rsvd0_size6; + const struct rtw89_dle_rsvd_size rsvd0_size17; const struct rtw89_dle_rsvd_size rsvd1_size0; const struct rtw89_dle_rsvd_size rsvd1_size2; + const struct rtw89_dle_rsvd_size rsvd1_size3; const struct rtw89_dle_input dle_input3; const struct rtw89_dle_input dle_input20; + const struct rtw89_dle_input dle_input28; }; extern const struct rtw89_mac_size_set rtw89_mac_size; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index fe3b0dc2a027..37d3bc1eafad 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -25,6 +25,62 @@ RTW89_GEN_MODULE_FWNAME(RTW8922DS_FW_BASENAME, RTW8922DS_FW_FORMAT_MAX) static const struct rtw89_hfc_ch_cfg rtw8922d_hfc_chcfg_pcie[] = { + {2, 732, 0}, /* ACH 0 */ + {0, 730, 0}, /* ACH 1 */ + {2, 732, 0}, /* ACH 2 */ + {0, 730, 0}, /* ACH 3 */ + {2, 732, 0}, /* ACH 4 */ + {0, 730, 0}, /* ACH 5 */ + {2, 732, 0}, /* ACH 6 */ + {0, 730, 0}, /* ACH 7 */ + {2, 732, 0}, /* B0MGQ */ + {0, 730, 0}, /* B0HIQ */ + {2, 732, 0}, /* B1MGQ */ + {0, 730, 0}, /* B1HIQ */ + {0, 0, 0}, /* GCQ */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922d_hfc_pubcfg_pcie = { + 742, /* Group 0 */ + 0, /* Group 1 */ + 742, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8922d_hfc_param_ini_pcie[] = { + [RTW89_QTA_SCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, + &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, + [RTW89_QTA_DBCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, + &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + +static const struct rtw89_dle_mem rtw8922d_dle_mem_pcie[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size22_v1, + &rtw89_mac_size.ple_size29_v1, &rtw89_mac_size.wde_qt23_v1, + &rtw89_mac_size.wde_qt23_v1, &rtw89_mac_size.ple_qt64_v2, + &rtw89_mac_size.ple_qt65_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.rsvd0_size17, &rtw89_mac_size.rsvd1_size3, + &rtw89_mac_size.dle_input28}, + [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size22_v1, + &rtw89_mac_size.ple_size29_v1, &rtw89_mac_size.wde_qt23_v1, + &rtw89_mac_size.wde_qt23_v1, &rtw89_mac_size.ple_qt64_v2, + &rtw89_mac_size.ple_qt65_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.rsvd0_size17, &rtw89_mac_size.rsvd1_size3, + &rtw89_mac_size.dle_input28}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size18_v1, + &rtw89_mac_size.ple_size22_v1, &rtw89_mac_size.wde_qt3, + &rtw89_mac_size.wde_qt3, &rtw89_mac_size.ple_qt5_v2, + &rtw89_mac_size.ple_qt47_v2, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, + &rtw89_mac_size.dle_input3}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const struct rtw89_hfc_ch_cfg rtw8922ds_hfc_chcfg_pcie[] = { {2, 603, 0}, /* ACH 0 */ {0, 601, 0}, /* ACH 1 */ {2, 603, 0}, /* ACH 2 */ @@ -40,24 +96,24 @@ static const struct rtw89_hfc_ch_cfg rtw8922d_hfc_chcfg_pcie[] = { {0, 0, 0}, /* GCQ */ }; -static const struct rtw89_hfc_pub_cfg rtw8922d_hfc_pubcfg_pcie = { +static const struct rtw89_hfc_pub_cfg rtw8922ds_hfc_pubcfg_pcie = { 613, /* Group 0 */ 0, /* Group 1 */ 613, /* Public Max */ 0, /* WP threshold */ }; -static const struct rtw89_hfc_param_ini rtw8922d_hfc_param_ini_pcie[] = { - [RTW89_QTA_SCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, +static const struct rtw89_hfc_param_ini rtw8922ds_hfc_param_ini_pcie[] = { + [RTW89_QTA_SCC] = {rtw8922ds_hfc_chcfg_pcie, &rtw8922ds_hfc_pubcfg_pcie, &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, - [RTW89_QTA_DBCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, + [RTW89_QTA_DBCC] = {rtw8922ds_hfc_chcfg_pcie, &rtw8922ds_hfc_pubcfg_pcie, &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, RTW89_HCIFC_POH}, [RTW89_QTA_INVALID] = {NULL}, }; -static const struct rtw89_dle_mem rtw8922d_dle_mem_pcie[] = { +static const struct rtw89_dle_mem rtw8922ds_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size16_v1, &rtw89_mac_size.ple_size20_v1, &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt44_v2, @@ -3306,11 +3362,16 @@ static const struct rtw89_fw_def rtw8922de_vs_fw_def = { .fw_b_aid = RTL8922D_AID7060, }; +static const struct rtw89_qta_def rtw8922ds_qta_def = { + .hfc_param_ini = {rtw8922ds_hfc_param_ini_pcie, NULL, NULL}, + .dle_mem = {rtw8922ds_dle_mem_pcie, NULL, NULL, NULL}, +}; + const struct rtw89_chip_variant rtw8922de_vs_variant = { .no_mcs_12_13 = true, .fw_min_ver_code = RTW89_FW_VER_CODE(0, 0, 0, 0), .fw_def_override = &rtw8922de_vs_fw_def, - .qta_def_override = NULL, + .qta_def_override = &rtw8922ds_qta_def, }; EXPORT_SYMBOL(rtw8922de_vs_variant); From cc9cdac8009979e5b77a9d05841ab2d9ca03e68e Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 16 May 2026 15:32:12 +0300 Subject: [PATCH 090/105] wifi: rtw89: Add missing TX queue mappings for RTL8922AU A user with MLO-capable router reports: [ 753.080409] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26 [ 753.080417] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26 [ 813.649426] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26 [ 813.649445] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26 [ 813.855983] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26 Map RTW89_TX_QSEL_B1_MGMT and RTW89_TX_QSEL_B1_HI to RTW89_TXCH_CH10. This is probably the right channel for these queues. This function is also used for RTL8852AU and RTL8852CU, but those currently aren't used in DBCC mode with rtw89 so it makes no difference for them. Link: https://github.com/morrownr/rtw89/issues/83#issuecomment-4314735734 Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/0c8e0cf1-13e9-4c67-a67f-5f6f79fd0658@gmail.com --- drivers/net/wireless/realtek/rtw89/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 0f3168d85369..c1df3e2ba11e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -854,6 +854,9 @@ u8 rtw89_core_get_ch_dma_v2(struct rtw89_dev *rtwdev, u8 qsel) case RTW89_TX_QSEL_B0_MGMT: case RTW89_TX_QSEL_B0_HI: return RTW89_TXCH_CH8; + case RTW89_TX_QSEL_B1_MGMT: + case RTW89_TX_QSEL_B1_HI: + return RTW89_TXCH_CH10; } } EXPORT_SYMBOL(rtw89_core_get_ch_dma_v2); From c80788f7c5aed8d420366b821f867a8a353d83a5 Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Mon, 18 May 2026 16:23:10 +0200 Subject: [PATCH 091/105] wifi: rtw88: increase TX report timeout to fix race condition The driver expects the firmware to report TX status within 500ms. However, a timeout can be triggered when the hardware performs background scans while under TX load. During these scans, the firmware stays off-channel for periods exceeding 500ms, delaying the delivery of TX reports back to the driver. When this occurs, the purge timer fires prematurely and drops the tracking skbs from the queue. This results in the host stack interpreting the missing status as packet loss, leading to TCP window collapse. In testing with iperf3, this causes throughput to drop from ~90 Mbps to near-zero for approximately 2 seconds until the connection recovers. Increase RTW_TX_PROBE_TIMEOUT to 2500ms for RTL8723DU. This duration is sufficient to accommodate off-channel dwell time during full background scans, ensuring the purge timer only trips during genuine firmware lockups and preventing unnecessary TCP retransmission cycles. Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support") Cc: stable@vger.kernel.org Acked-by: Ping-Ke Shih Tested-by: Luka Gejak Signed-off-by: Luka Gejak Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260518142311.10328-1-luka.gejak@linux.dev --- drivers/net/wireless/realtek/rtw88/tx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index bea78971141e..9d747a060b98 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -196,6 +196,7 @@ void rtw_tx_report_purge_timer(struct timer_list *t) void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn) { struct rtw_tx_report *tx_report = &rtwdev->tx_report; + unsigned long timeout = RTW_TX_PROBE_TIMEOUT; unsigned long flags; u8 *drv_data; @@ -207,7 +208,11 @@ void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn) __skb_queue_tail(&tx_report->queue, skb); spin_unlock_irqrestore(&tx_report->q_lock, flags); - mod_timer(&tx_report->purge_timer, jiffies + RTW_TX_PROBE_TIMEOUT); + if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D && + rtwdev->hci.type == RTW_HCI_TYPE_USB) + timeout = msecs_to_jiffies(2500); + + mod_timer(&tx_report->purge_timer, jiffies + timeout); } EXPORT_SYMBOL(rtw_tx_report_enqueue); From 6b964941bbfe6e0f18b1a5e008486dbb62df440a Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Mon, 18 May 2026 16:23:11 +0200 Subject: [PATCH 092/105] wifi: rtw88: usb: fix memory leaks on USB write failures When rtw_usb_write_port() fails to submit a USB Request Block (URB) (e.g., due to device disconnect or ENOMEM), the completion callback is never executed. Currently, the driver ignores the return value of rtw_usb_write_port() in rtw_usb_write_data() and rtw_usb_tx_agg_skb(). Because these functions rely on the completion callback to free the socket buffers (skbs) and the transaction control block (txcb), a submission failure results in: 1. A memory leak of the allocated skb in rtw_usb_write_data(). 2. A memory leak of the txcb structure and all aggregated skbs in rtw_usb_tx_agg_skb(). Fix this by checking the return value of rtw_usb_write_port(). If it fails, explicitly free the skb in rtw_usb_write_data(), and properly purge the tx_ack_queue and free the txcb in rtw_usb_tx_agg_skb(). The issue was discovered in practice during device disconnect/reconnect scenarios and memory pressure conditions. Tested by verifying normal TX operation continues after the fix without regressions. Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support") Cc: stable@vger.kernel.org Acked-by: Ping-Ke Shih Tested-by: Luka Gejak Signed-off-by: Luka Gejak Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260518142311.10328-2-luka.gejak@linux.dev --- drivers/net/wireless/realtek/rtw88/usb.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 718940ebba31..1a0bdbf52cb0 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -399,6 +399,7 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list int agg_num = 0; unsigned int align_next = 0; u8 qsel; + int ret; if (skb_queue_empty(list)) return false; @@ -456,7 +457,13 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list tx_desc = (struct rtw_tx_desc *)skb_head->data; qsel = le32_get_bits(tx_desc->w1, RTW_TX_DESC_W1_QSEL); - rtw_usb_write_port(rtwdev, qsel, skb_head, rtw_usb_write_port_tx_complete, txcb); + ret = rtw_usb_write_port(rtwdev, qsel, skb_head, + rtw_usb_write_port_tx_complete, txcb); + if (ret) { + ieee80211_purge_tx_queue(rtwdev->hw, &txcb->tx_ack_queue); + kfree(txcb); + return false; + } return true; } @@ -518,8 +525,10 @@ static int rtw_usb_write_data(struct rtw_dev *rtwdev, ret = rtw_usb_write_port(rtwdev, qsel, skb, rtw_usb_write_port_complete, skb); - if (unlikely(ret)) + if (unlikely(ret)) { rtw_err(rtwdev, "failed to do USB write, ret=%d\n", ret); + dev_kfree_skb_any(skb); + } return ret; } From f50cf5f5d6a3f0f13a8f1af26a77200d9f6b2e6d Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 20 May 2026 20:38:17 +0800 Subject: [PATCH 093/105] wifi: rtw89: fw: load TX power track element according to AID RF parameters has different TX power track table for different AID. FW elements may include multiple TX power track tables for different AID. So, load the corresponding one. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index eb5683d64461..3e6161743015 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1209,12 +1209,17 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev, { struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_hal *hal = &rtwdev->hal; + u16 aid = le16_to_cpu(elm->aid); u32 needed_bitmap = 0; u32 offset = 0; int subband; u32 bitmap; int type; + if (aid && aid != hal->aid) + return 1; + if (chip->support_bands & BIT(NL80211_BAND_6GHZ)) needed_bitmap |= RTW89_DEFAULT_NEEDED_FW_TXPWR_TRK_6GHZ; if (chip->support_bands & BIT(NL80211_BAND_5GHZ)) From 57efc7b096a6ff97d1bf69a832b27fec5c1c4e9e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 20 May 2026 20:38:18 +0800 Subject: [PATCH 094/105] wifi: rtw89: 8922d: refactor digital power compensation to support new format Because base settings of digital power compensation can be shared across all bands, the settings are divided into two parts -- base and individual values per bands. Refactor the code to be reuse with new format. No change logic at all. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 37d3bc1eafad..35014b5144fe 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2172,29 +2172,42 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, enum rtw89_phy_idx phy_idx) { -#define DIGITAL_PWR_COMP_REG_NUM 22 +#define DIGITAL_PWR_COMP_BASE_NUM 7 +#define DIGITAL_PWR_COMP_VALS_NUM 15 static const u32 pw_comp_cr[2] = {R_RX_PATH0_TBL0_BE4, R_RX_PATH1_TBL0_BE4}; - const __le32 (*pwr_comp_val)[2][RTW89_TX_COMP_BAND_NR] - [BB_PATH_NUM_8922D][DIGITAL_PWR_COMP_REG_NUM]; + const struct { + struct { + __le32 base[DIGITAL_PWR_COMP_BASE_NUM]; + __le32 vals[DIGITAL_PWR_COMP_VALS_NUM]; + } sets[2][RTW89_TX_COMP_BAND_NR][BB_PATH_NUM_8922D]; + } *pwr_comp_v0; struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; const struct rtw89_fw_element_hdr *txcomp_elm = elm_info->tx_comp; - const __le32 *digital_pwr_comp; + const __le32 (*comp_base)[DIGITAL_PWR_COMP_BASE_NUM]; + const __le32 (*comp_vals)[DIGITAL_PWR_COMP_VALS_NUM]; u32 addr, val; u32 i; - if (sizeof(*pwr_comp_val) != le32_to_cpu(txcomp_elm->size)) { + 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; + comp_vals = &pwr_comp_v0->sets[nss][chan->tx_comp_band][path].vals; + } else { rtw89_debug(rtwdev, RTW89_DBG_UNEXP, "incorrect power comp size %d\n", le32_to_cpu(txcomp_elm->size)); return; } - pwr_comp_val = (const void *)txcomp_elm->u.common.contents; - digital_pwr_comp = (*pwr_comp_val)[nss][chan->tx_comp_band][path]; addr = pw_comp_cr[path]; - for (i = 0; i < DIGITAL_PWR_COMP_REG_NUM; i++, addr += 4) { - val = le32_to_cpu(digital_pwr_comp[i]); + for (i = 0; i < ARRAY_SIZE(*comp_base); i++, addr += 4) { + val = le32_to_cpu((*comp_base)[i]); + rtw89_phy_write32_idx(rtwdev, addr, MASKDWORD, val, phy_idx); + } + + for (i = 0; i < ARRAY_SIZE(*comp_vals); i++, addr += 4) { + val = le32_to_cpu((*comp_vals)[i]); rtw89_phy_write32_idx(rtwdev, addr, MASKDWORD, val, phy_idx); } } From c77a155c96d7818626ad24b49aa50bc376af547b Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 20 May 2026 20:38:19 +0800 Subject: [PATCH 095/105] wifi: rtw89: 8922d: support new digital power compensation format The new format shared the base set (7 elements). As there are total 40 sets, save 7 * 39 elements. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-4-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 35014b5144fe..795efed6e683 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2181,6 +2181,12 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, __le32 vals[DIGITAL_PWR_COMP_VALS_NUM]; } sets[2][RTW89_TX_COMP_BAND_NR][BB_PATH_NUM_8922D]; } *pwr_comp_v0; + const struct { + __le32 base[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; const __le32 (*comp_base)[DIGITAL_PWR_COMP_BASE_NUM]; @@ -2188,7 +2194,11 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, u32 addr, val; u32 i; - if (sizeof(*pwr_comp_v0) == le32_to_cpu(txcomp_elm->size)) { + 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_vals = &pwr_comp->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; comp_vals = &pwr_comp_v0->sets[nss][chan->tx_comp_band][path].vals; From 89ab7535e428afe31d48fd2edd39fee5badb2456 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 20 May 2026 20:38:20 +0800 Subject: [PATCH 096/105] wifi: rtw89: fw: load TX compensation element by RFE type Originally driver uses TX compensation element by AID, and now tables are by RFE type. Add the logic accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 6 ++++++ drivers/net/wireless/realtek/rtw89/fw.h | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 3e6161743015..d6a594b75ab2 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1423,12 +1423,18 @@ int rtw89_build_tx_comp_from_elm(struct rtw89_dev *rtwdev, const union rtw89_fw_element_arg arg) { struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; + struct rtw89_efuse *efuse = &rtwdev->efuse; struct rtw89_hal *hal = &rtwdev->hal; + u8 rfe_type; u16 aid; aid = le16_to_cpu(elm->aid); + rfe_type = elm->u.tx_comp.rfe_type; + if (aid && aid != hal->aid) return 1; /* ignore if aid not matched */ + else if (rfe_type && rfe_type != efuse->rfe_type) + return 1; /* ignore if rfe_type not matched */ else if (elm_info->tx_comp) return 1; /* ignore if an element is existing */ diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index cde8fd34723b..20721d5209aa 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4548,6 +4548,11 @@ struct rtw89_fw_element_hdr { u8 rsvd[4]; u8 rules_and_msgs[]; } __packed diag_mac; + struct { + u8 rfe_type; + u8 priv[7]; + u8 contents[]; + } __packed tx_comp; struct __rtw89_fw_txpwr_element txpwr; struct __rtw89_fw_regd_element regd; } __packed u; From ed438b312d0758eaac868ab1275e743709fa4dac Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 20 May 2026 20:38:21 +0800 Subject: [PATCH 097/105] wifi: rtw89: Wi-Fi 7 configure TX power limit for large MRU Support of Large MRU (Multiple Resource Unit) starts from RTL8922D_CID7090, i.e. RTL8922A and RTL8922D-VS variant do not support it. There are the new corresponding control registers. So, configure them. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 3 + drivers/net/wireless/realtek/rtw89/phy.h | 10 ++ drivers/net/wireless/realtek/rtw89/phy_be.c | 163 ++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/reg.h | 9 ++ 4 files changed, 185 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 27d9a916d58c..fb32d2bd9cb0 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -998,6 +998,9 @@ enum rtw89_ru_bandwidth { RTW89_RU106 = 2, RTW89_RU52_26 = 3, RTW89_RU106_26 = 4, + RTW89_RU484_242 = 5, + RTW89_RU996_484 = 6, + RTW89_RU996_484_242 = 7, RTW89_RU_NUM, }; diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 5af66656d0b8..532232892831 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -568,6 +568,16 @@ struct rtw89_txpwr_limit_ru_be { s8 ru106_26[RTW89_RU_SEC_NUM_BE]; }; +#define RTW89_RU484_242_SEC_NUM_BE 4 +#define RTW89_RU996_484_SEC_NUM_BE 2 +#define RTW89_RU996_484_242_SEC_NUM_BE 2 + +struct rtw89_txpwr_limit_large_mru_be { + s8 ru484_242[RTW89_NSS_NUM][RTW89_RU484_242_SEC_NUM_BE]; + s8 ru996_484[RTW89_NSS_NUM][RTW89_RU996_484_SEC_NUM_BE]; + s8 ru996_484_242[RTW89_NSS_NUM][RTW89_RU996_484_242_SEC_NUM_BE]; +}; + struct rtw89_phy_rfk_log_fmt { const struct rtw89_fw_element_hdr *elm[RTW89_PHY_C2H_RFK_LOG_FUNC_NUM]; }; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index d0a6dfdbfea7..99263355e2f1 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -1699,11 +1699,168 @@ static void rtw89_phy_fill_limit_ru_be(struct rtw89_dev *rtwdev, } } +static +void rtw89_phy_fill_limit_ru484_242_be(struct rtw89_dev *rtwdev, + s8 (*lmt)[RTW89_RU484_242_SEC_NUM_BE], + u8 ntx, u8 band, u8 ch, u8 bw) +{ + switch (bw) { + case RTW89_CHANNEL_WIDTH_80: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch); + break; + case RTW89_CHANNEL_WIDTH_160: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch - 8); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch + 8); + break; + case RTW89_CHANNEL_WIDTH_320: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch - 24); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch - 8); + (*lmt)[2] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch + 8); + (*lmt)[3] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch + 24); + break; + } +} + +static +void rtw89_phy_fill_limit_ru996_484_be(struct rtw89_dev *rtwdev, + s8 (*lmt)[RTW89_RU996_484_SEC_NUM_BE], + u8 ntx, u8 band, u8 ch, u8 bw) +{ + switch (bw) { + case RTW89_CHANNEL_WIDTH_160: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484, + ntx, ch); + break; + case RTW89_CHANNEL_WIDTH_320: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484, + ntx, ch - 16); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484, + ntx, ch + 16); + break; + } +} + +static +void rtw89_phy_fill_limit_ru996_484_242_be(struct rtw89_dev *rtwdev, + s8 (*lmt)[RTW89_RU996_484_242_SEC_NUM_BE], + u8 ntx, u8 band, u8 ch, u8 bw) +{ + switch (bw) { + case RTW89_CHANNEL_WIDTH_160: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484_242, + ntx, ch); + break; + case RTW89_CHANNEL_WIDTH_320: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484_242, + ntx, ch - 16); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484_242, + ntx, ch + 16); + break; + } +} + +static +void rtw89_phy_fill_limit_large_mru_be(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + struct rtw89_txpwr_limit_large_mru_be *lmt, + bool has_bf) +{ + u8 band = chan->band_type; + u8 ch = chan->channel; + u8 bw = chan->band_width; + int i; + + memset(lmt, 0, sizeof(*lmt)); + + if (has_bf) + return; + + for (i = 0; i <= RTW89_NSS_2; i++) { + rtw89_phy_fill_limit_ru484_242_be(rtwdev, &lmt->ru484_242[i], + i, band, ch, bw); + rtw89_phy_fill_limit_ru996_484_be(rtwdev, &lmt->ru996_484[i], + i, band, ch, bw); + rtw89_phy_fill_limit_ru996_484_242_be(rtwdev, &lmt->ru996_484_242[i], + i, band, ch, bw); + } +} + +static +void rtw89_phy_conf_limit_large_mru_be(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx, + bool has_bf) +{ + struct rtw89_txpwr_limit_large_mru_be lmt_lmru; + u32 addr, val; + + rtw89_phy_fill_limit_large_mru_be(rtwdev, chan, &lmt_lmru, has_bf); + + addr = has_bf ? R_BE_TXAGC_MAX_1TX_BF_RU484_242_0 : + R_BE_TXAGC_MAX_1TX_RU484_242_0; + + val = u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][2], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][3], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); + + val = u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][2], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][3], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr + 4, val); + + addr = has_bf ? R_BE_TXAGC_MAX_1TX_BF_RU996_484_0 : + R_BE_TXAGC_MAX_1TX_RU996_484_0; + + val = u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_1][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_1][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_2][0], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_2][1], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); + + addr = has_bf ? R_BE_TXAGC_MAX_1TX_BF_RU996_484_242_0 : + R_BE_TXAGC_MAX_1TX_RU996_484_242_0; + + val = u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_1][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_1][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_2][0], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_2][1], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); +} + static void rtw89_phy_set_txpwr_limit_ru_be(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { + const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_txpwr_limit_ru_be lmt_ru; + struct rtw89_hal *hal = &rtwdev->hal; const s8 *ptr; u32 addr, val; u8 i, j; @@ -1730,6 +1887,12 @@ static void rtw89_phy_set_txpwr_limit_ru_be(struct rtw89_dev *rtwdev, rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); } } + + if (!(chip->chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)) + return; + + rtw89_phy_conf_limit_large_mru_be(rtwdev, chan, phy_idx, false); + rtw89_phy_conf_limit_large_mru_be(rtwdev, chan, phy_idx, true); } const struct rtw89_phy_gen_def rtw89_phy_gen_be = { diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index c2ffa6c196e4..2369fca3ede7 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8362,6 +8362,11 @@ #define R_BE_PWR_LISTEN_PATH 0x11988 #define B_BE_PWR_LISTEN_PATH_EN GENMASK(31, 28) +#define R_BE_TXAGC_MAX_1TX_RU484_242_0 0x11990 +#define R_BE_TXAGC_MAX_1TX_RU996_484_0 0x119A4 +#define R_BE_TXAGC_MAX_1TX_RU996_484_242_0 0x119AC +#define R_BE_TXAGC_MAX_1TX_BF_RU484_242_0 0x119DC + #define R_BE_PWR_REF_CTRL 0x11A20 #define B_BE_PWR_REF_CTRL_OFDM GENMASK(9, 1) #define B_BE_PWR_REF_CTRL_CCK GENMASK(18, 10) @@ -8399,6 +8404,8 @@ B_BE_PWR_FORCE_MACID_EN_VAL | \ B_BE_PWR_FORCE_MACID_EN_ON) +#define R_BE_TXAGC_MAX_1TX_BF_RU996_484_0 0x11A4C + #define R_BE_PWR_REG_CTRL 0x11A50 #define B_BE_PWR_BT_EN BIT(23) @@ -8412,6 +8419,8 @@ #define R_BE_PWR_TH 0x11A78 #define R_BE_PWR_RSSI_TARGET_LMT 0x11A84 +#define R_BE_TXAGC_MAX_1TX_BF_RU996_484_242_0 0x11ADC + #define R_BE_PWR_OFST_SW 0x11AE8 #define B_BE_PWR_OFST_SW_DB GENMASK(27, 24) From a153f9ce94e669b7b6bd78f637c38a9b24d906a6 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 20 May 2026 20:38:22 +0800 Subject: [PATCH 098/105] wifi: rtw89: debug: show large MRU in txpwr_table dbgfs Read and print the CR settings of large MRU. For now, only RTL8922D_CID7090 support it. For example, [TX power limit_large_mru] RU484_242 1TX - IDX_0 IDX_1 IDX_2 IDX_3 | 17, 0, 0, 0, dBm RU484_242 2TX - IDX_0 IDX_1 IDX_2 IDX_3 | 16, 0, 0, 0, dBm RU996_484 1TX - IDX_0 IDX_1 | 0, 0, dBm RU996_484 2TX - IDX_0 IDX_1 | 0, 0, dBm RU996_484_242 1TX - IDX_0 IDX_1 | 0, 0, dBm RU996_484_242 2TX - IDX_0 IDX_1 | 0, 0, dBm Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index c0fbb016cea4..8f5af873e09f 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -767,6 +767,45 @@ static const struct txpwr_map __txpwr_map_lmt_ru_be = { .addr_to_1ss = 0, /* not support */ }; +static const struct txpwr_ent __txpwr_ent_lmt_ru484_242_be[] = { + __GEN_TXPWR_ENT4("RU484_242 1TX ", "IDX_0 ", "IDX_1 ", "IDX_2 ", "IDX_3 "), + __GEN_TXPWR_ENT4("RU484_242 2TX ", "IDX_0 ", "IDX_1 ", "IDX_2 ", "IDX_3 "), +}; + +static const struct txpwr_map __txpwr_map_lmt_ru484_242_be = { + .ent = __txpwr_ent_lmt_ru484_242_be, + .size = ARRAY_SIZE(__txpwr_ent_lmt_ru484_242_be), + .addr_from = R_BE_TXAGC_MAX_1TX_RU484_242_0, + .addr_to = R_BE_TXAGC_MAX_1TX_RU484_242_0 + 4, + .addr_to_1ss = 0, /* not support */ +}; + +static const struct txpwr_ent __txpwr_ent_lmt_ru996_484_be[] = { + __GEN_TXPWR_ENT2("RU996_484 1TX ", "IDX_0 ", "IDX_1 "), + __GEN_TXPWR_ENT2("RU996_484 2TX ", "IDX_0 ", "IDX_1 "), +}; + +static const struct txpwr_map __txpwr_map_lmt_ru996_484_be = { + .ent = __txpwr_ent_lmt_ru996_484_be, + .size = ARRAY_SIZE(__txpwr_ent_lmt_ru996_484_be), + .addr_from = R_BE_TXAGC_MAX_1TX_RU996_484_0, + .addr_to = R_BE_TXAGC_MAX_1TX_RU996_484_0, + .addr_to_1ss = 0, /* not support */ +}; + +static const struct txpwr_ent __txpwr_ent_lmt_ru996_484_242_be[] = { + __GEN_TXPWR_ENT2("RU996_484_242 1TX ", "IDX_0 ", "IDX_1 "), + __GEN_TXPWR_ENT2("RU996_484_242 2TX ", "IDX_0 ", "IDX_1 "), +}; + +static const struct txpwr_map __txpwr_map_lmt_ru996_484_242_be = { + .ent = __txpwr_ent_lmt_ru996_484_242_be, + .size = ARRAY_SIZE(__txpwr_ent_lmt_ru996_484_242_be), + .addr_from = R_BE_TXAGC_MAX_1TX_RU996_484_242_0, + .addr_to = R_BE_TXAGC_MAX_1TX_RU996_484_242_0, + .addr_to_1ss = 0, /* not support */ +}; + static unsigned int __print_txpwr_ent(char *buf, size_t bufsz, const struct txpwr_ent *ent, const s8 *bufp, const unsigned int cur, unsigned int *ate) @@ -882,6 +921,9 @@ struct dbgfs_txpwr_table { const struct txpwr_map *byr; const struct txpwr_map *lmt; const struct txpwr_map *lmt_ru; + const struct txpwr_map *lmt_ru484_242; + const struct txpwr_map *lmt_ru996_484; + const struct txpwr_map *lmt_ru996_484_242; }; static const struct dbgfs_txpwr_table dbgfs_txpwr_table_ax = { @@ -894,6 +936,9 @@ static const struct dbgfs_txpwr_table dbgfs_txpwr_table_be = { .byr = &__txpwr_map_byr_be, .lmt = &__txpwr_map_lmt_be, .lmt_ru = &__txpwr_map_lmt_ru_be, + .lmt_ru484_242 = &__txpwr_map_lmt_ru484_242_be, + .lmt_ru996_484 = &__txpwr_map_lmt_ru996_484_be, + .lmt_ru996_484_242 = &__txpwr_map_lmt_ru996_484_242_be, }; static const struct dbgfs_txpwr_table *dbgfs_txpwr_tables[RTW89_CHIP_GEN_NUM] = { @@ -934,6 +979,8 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_sar_parm sar_parm = {}; const struct dbgfs_txpwr_table *tbl; const struct rtw89_chan *chan; @@ -979,6 +1026,35 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, return n; p += n; + switch (chip_gen) { + case RTW89_CHIP_AX: + goto out; + case RTW89_CHIP_BE: + if (!(chip->chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)) + goto out; + break; + default: + return -EOPNOTSUPP; + } + + p += scnprintf(p, end - p, "\n[TX power limit_large_mru]\n"); + + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru484_242); + if (n < 0) + return n; + p += n; + + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru996_484); + if (n < 0) + return n; + p += n; + + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru996_484_242); + if (n < 0) + return n; + p += n; + +out: return p - buf; } From 756abe2ebe3e14922c6e1317c89ce8004e85b6e9 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 20 May 2026 20:38:23 +0800 Subject: [PATCH 099/105] wifi: rtw89: 8922d: configure TX shape settings By default, BB enables triangular spectrum by a series of register settings. According to band and regulation, RF parameters determine whether TX shape needs to be restricted or not. So now, clear the corresponding settings if it has no need to do. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 4 ++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 61 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 2369fca3ede7..61709182f84c 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10626,6 +10626,8 @@ #define R_RFSI_CT_OPT_0_BE4 0x11A94 #define R_RFSI_CT_OPT_8_BE4 0x11A98 #define R_QAM_COMP_TH0_BE4 0x11A9C +#define B_QAM_COMP_TH_TRIANGULAR_L GENMASK(11, 10) +#define B_QAM_COMP_TH_TRIANGULAR_H GENMASK(27, 26) #define R_QAM_COMP_TH1_BE4 0x11AA0 #define R_QAM_COMP_TH2_BE4 0x11AA4 #define R_QAM_COMP_TH3_BE4 0x11AA8 @@ -10649,6 +10651,8 @@ #define B_QAM_COMP_TH6_2L GENMASK(9, 5) #define B_QAM_COMP_TH6_2M GENMASK(19, 15) #define R_OW_VAL_0_BE4 0x11AAC +#define B_OW_VAL_TRIANGULAR_L GENMASK(11, 10) +#define B_OW_VAL_TRIANGULAR_H GENMASK(27, 26) #define R_OW_VAL_1_BE4 0x11AB0 #define R_OW_VAL_2_BE4 0x11AB4 #define R_OW_VAL_3_BE4 0x11AB8 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 795efed6e683..838c26231897 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2734,6 +2734,66 @@ static void rtw8922d_set_txpwr_ref(struct rtw89_dev *rtwdev, B_BE_PWR_REF_CTRL_CCK, ref_cck); } +static void rtw8922d_set_tx_shape(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + 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 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]; + + if (tx_shape_idx == 0) + goto disable; + + th = d->bands[chan->rfsi_band].qam_comp_th0; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKLWORD, th[0], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKHWORD, th[1], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKLWORD, th[2], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKHWORD, th[3], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKLWORD, th[4], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKHWORD, th[5], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKLWORD, th[6], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKHWORD, th[7], phy_idx); + + th = d->bands[chan->rfsi_band].qam_comp_ow; + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKLWORD, th[0], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKHWORD, th[1], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKLWORD, th[2], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKHWORD, th[3], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKLWORD, th[4], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKHWORD, th[5], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKLWORD, th[6], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKHWORD, th[7], phy_idx); + + return; + +disable: + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); +} + static void rtw8922d_set_txpwr_sar_diff(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) @@ -2765,6 +2825,7 @@ static void rtw8922d_set_txpwr(struct rtw89_dev *rtwdev, { rtw89_phy_set_txpwr_byrate(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_offset(rtwdev, chan, phy_idx); + rtw8922d_set_tx_shape(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); rtw8922d_set_txpwr_ref(rtwdev, chan, phy_idx); From aa7d92e83811a0b557b75f7a0ce85315f4358bf2 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 20 May 2026 17:14:26 +0300 Subject: [PATCH 100/105] wifi: rtw88: Add more validation for the RX descriptor Some RTL8821CE cards can return frames with corrupted RX descriptor, causing warnings and crashes if they are passed to the upper layers. The PHY status size field is 4 bits wide, but in rtw88 its value should only be 0 or 4. Checking this catches most of the corrupt frames. If a PHY status is present, the PHY status size should not be 0. The frame size should not be less than or equal to 4 and should not exceed 11454. The rate should not exceed 4SS MCS9. Discard the frame if any of these checks fail. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221286 Signed-off-by: Bitterblue Smith Tested-by: Oleksandr Havrylov Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/5bfecba3-8a90-4e0f-9558-af5cd8a14975@gmail.com --- drivers/net/wireless/realtek/rtw88/pci.c | 16 +++++++----- drivers/net/wireless/realtek/rtw88/rx.c | 31 +++++++++++++++-------- drivers/net/wireless/realtek/rtw88/rx.h | 6 ++--- drivers/net/wireless/realtek/rtw88/sdio.c | 8 +++++- drivers/net/wireless/realtek/rtw88/usb.c | 9 ++++--- 5 files changed, 47 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index c2bf44e880cf..a30467228912 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1042,20 +1042,21 @@ static int rtw_pci_get_hw_rx_ring_nr(struct rtw_dev *rtwdev, static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, u8 hw_queue, u32 limit) { + struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU]; const struct rtw_chip_info *chip = rtwdev->chip; struct napi_struct *napi = &rtwpci->napi; - struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU]; - struct rtw_rx_pkt_stat pkt_stat; + u32 pkt_desc_sz = chip->rx_pkt_desc_sz; + u32 buf_desc_sz = chip->rx_buf_desc_sz; struct ieee80211_rx_status rx_status; + struct rtw_rx_pkt_stat pkt_stat; struct sk_buff *skb, *new; u32 cur_rp = ring->r.rp; u32 count, rx_done = 0; u32 pkt_offset; - u32 pkt_desc_sz = chip->rx_pkt_desc_sz; - u32 buf_desc_sz = chip->rx_buf_desc_sz; + dma_addr_t dma; u32 new_len; u8 *rx_desc; - dma_addr_t dma; + int ret; count = rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci); count = min(count, limit); @@ -1067,7 +1068,10 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, dma_sync_single_for_cpu(rtwdev->dev, dma, RTK_PCI_RX_BUF_SIZE, DMA_FROM_DEVICE); rx_desc = skb->data; - rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, &rx_status); + ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, + &pkt_stat, &rx_status); + if (ret) + goto next_rp; /* offset from rx_desc to payload */ pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c index d9e11343d498..01fd299abb7f 100644 --- a/drivers/net/wireless/realtek/rtw88/rx.c +++ b/drivers/net/wireless/realtek/rtw88/rx.c @@ -3,6 +3,7 @@ */ #include "main.h" +#include "mac.h" #include "rx.h" #include "ps.h" #include "debug.h" @@ -261,9 +262,9 @@ static void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev, } } -void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, - struct rtw_rx_pkt_stat *pkt_stat, - struct ieee80211_rx_status *rx_status) +int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, + struct rtw_rx_pkt_stat *pkt_stat, + struct ieee80211_rx_status *rx_status) { u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz; struct rtw_rx_desc *rx_desc = rx_desc8; @@ -295,20 +296,28 @@ void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, pkt_stat->tsf_low = le32_get_bits(rx_desc->w5, RTW_RX_DESC_W5_TSFL); - if (unlikely(pkt_stat->rate >= DESC_RATE_MAX)) { - rtw_dbg(rtwdev, RTW_DBG_UNEXP, - "unexpected RX rate=0x%x\n", pkt_stat->rate); + if (unlikely(pkt_stat->rate >= DESC_RATE_MAX)) + return -EINVAL; - pkt_stat->rate = DESC_RATE1M; - pkt_stat->bw = RTW_CHANNEL_WIDTH_20; - } + if (unlikely(pkt_stat->drv_info_sz && + pkt_stat->drv_info_sz != PHY_STATUS_SIZE)) + return -EINVAL; + + if (unlikely(pkt_stat->phy_status && !pkt_stat->drv_info_sz)) + return -EINVAL; + + if (unlikely(pkt_stat->pkt_len > IEEE80211_MAX_MPDU_LEN_VHT_11454)) + return -EINVAL; /* drv_info_sz is in unit of 8-bytes */ pkt_stat->drv_info_sz *= 8; /* c2h cmd pkt's rx/phy status is not interested */ if (pkt_stat->is_c2h) - return; + return 0; + + if (unlikely(pkt_stat->pkt_len <= FCS_LEN)) + return -EINVAL; phy_status = rx_desc8 + desc_sz + pkt_stat->shift; hdr = phy_status + pkt_stat->drv_info_sz; @@ -318,5 +327,7 @@ void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, rtwdev->chip->ops->query_phy_status(rtwdev, phy_status, pkt_stat); rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status); + + return 0; } EXPORT_SYMBOL(rtw_rx_query_rx_desc); diff --git a/drivers/net/wireless/realtek/rtw88/rx.h b/drivers/net/wireless/realtek/rtw88/rx.h index 6b7dee245c0a..74359f641c76 100644 --- a/drivers/net/wireless/realtek/rtw88/rx.h +++ b/drivers/net/wireless/realtek/rtw88/rx.h @@ -45,9 +45,9 @@ struct rtw_rx_desc { void rtw_rx_stats(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, struct sk_buff *skb); -void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, - struct rtw_rx_pkt_stat *pkt_stat, - struct ieee80211_rx_status *rx_status); +int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, + struct rtw_rx_pkt_stat *pkt_stat, + struct ieee80211_rx_status *rx_status); void rtw_update_rx_freq_from_ie(struct rtw_dev *rtwdev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status, struct rtw_rx_pkt_stat *pkt_stat); diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c index 1318e94f8524..5b40d74b16ee 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.c +++ b/drivers/net/wireless/realtek/rtw88/sdio.c @@ -995,7 +995,13 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len) while (true) { rx_desc = skb->data; - rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, &rx_status); + ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, + &pkt_stat, &rx_status); + if (ret) { + dev_kfree_skb_any(skb); + return; + } + pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + pkt_stat.shift; diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 1a0bdbf52cb0..64e1c3420e0a 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -619,8 +619,8 @@ static void rtw_usb_rx_handler(struct work_struct *work) u32 max_skb_len = pkt_desc_sz + PHY_STATUS_SIZE * 8 + IEEE80211_MAX_MPDU_LEN_VHT_11454; u32 pkt_offset, next_pkt, skb_len; + int limit, ret; u8 *rx_desc; - int limit; for (limit = 0; limit < 200; limit++) { rx_skb = skb_dequeue(&rtwusb->rx_queue); @@ -636,8 +636,11 @@ static void rtw_usb_rx_handler(struct work_struct *work) rx_desc = rx_skb->data; do { - rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, - &rx_status); + ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, + &pkt_stat, &rx_status); + if (ret) + break; + pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + pkt_stat.shift; From 8368970b62404ce2ce70d04c1cfff62700d7d8d5 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 20 May 2026 17:44:35 +0300 Subject: [PATCH 101/105] wifi: rtw89: usb: Support switching to USB 3 mode The Realtek wifi 6/7 devices which support USB 3 are weird: when first plugged in, they pretend to be USB 2. The driver needs to send some commands to the device, which make it disappear and come back as a USB 3 device. Implement the required commands in rtw89. Add a new function rtw89_usb_write32_quiet() to avoid the warnings when writing to R_{AX,BE}_PAD_CTRL2. Even though the write succeeds, usb_control_msg() returns -EPROTO, probably because the USB device disappears immediately. This results in some confusing warnings in the kernel log. When a USB 3 device is plugged into a USB 2 port, rtw89 will try to switch it to USB 3 mode only once. The device will disappear and come back still in USB 2 mode, of course. Tested with RTL8832AU, RTL8832BU, RTL8832CU, and RTL8912AU. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/e955451c-93a1-4d04-8024-d224a04f1d4a@gmail.com --- drivers/net/wireless/realtek/rtw89/reg.h | 27 ++++++ drivers/net/wireless/realtek/rtw89/usb.c | 104 ++++++++++++++++++++++- 2 files changed, 128 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 61709182f84c..086ef77ebb9f 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -164,6 +164,19 @@ #define R_AX_DBG_PORT_SEL 0x00C0 #define B_AX_DEBUG_ST_MASK GENMASK(31, 0) +#define R_AX_PAD_CTRL2 0x00C4 +#define B_AX_FORCE_U3_CK BIT(23) +#define B_AX_USB2_FORCE BIT(22) +#define B_AX_USB3_FORCE BIT(21) +#define B_AX_USB3_USB2_TRANSITION BIT(20) +#define B_AX_USB23_SW_MODE_V1 GENMASK(19, 18) +#define USB_MODE_U2 0x1 +#define USB_MODE_U3 0x2 +#define B_AX_NO_PDN_CHIPOFF_V1 BIT(17) +#define B_AX_RSM_EN_V1 BIT(16) +#define B_AX_MATCH_CNT GENMASK(15, 8) +#define USB_SWITCH_DELAY 0xF + #define R_AX_PMC_DBG_CTRL2 0x00CC #define B_AX_SYSON_DIS_PMCR_AX_WRMSK BIT(2) @@ -4235,6 +4248,20 @@ #define B_BE_TOGGLE BIT(31) #define B_BE_DATA_LINE_MASK GENMASK(30, 0) +#define R_BE_PAD_CTRL2 0x00C4 +#define B_BE_USB23_SW_MODE BIT(31) +#define B_BE_USB3_GEN_MODE BIT(30) +#define B_BE_USB3_LANE_MODE BIT(29) +#define B_BE_USB_AUTO_INSTALL_MASK BIT(28) +#define B_BE_FORCE_CLK_U2 BIT(25) +#define B_BE_FORCE_U2_CK BIT(24) +#define B_BE_FORCE_U3_CK BIT(23) +#define B_BE_USB2_FORCE BIT(22) +#define B_BE_USB3_FORCE BIT(21) +#define B_BE_NO_PDN_CHIPOFF_V1 BIT(17) +#define B_BE_RSM_EN_V1 BIT(16) +#define B_BE_MATCH_CNT GENMASK(15, 8) + #define R_BE_PMC_DBG_CTRL2 0x00CC #define B_BE_EFUSE_BURN_GNT_MASK GENMASK(31, 24) #define B_BE_DIS_IOWRAP_TIMEOUT BIT(16) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index c6d55e669776..198378018062 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -11,8 +11,8 @@ static void rtw89_usb_read_port_complete(struct urb *urb); -static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, - void *data, u16 len, u8 reqtype) +static void __rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, + void *data, u16 len, u8 reqtype, bool warn) { struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); struct usb_device *udev = rtwusb->udev; @@ -52,7 +52,7 @@ static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, if (ret == -ESHUTDOWN || ret == -ENODEV) set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags); - else if (ret < 0) + else if (ret < 0 && warn) rtw89_warn(rtwdev, "usb %s%u 0x%x fail ret=%d value=0x%x attempt=%d\n", str_read_write(reqtype == RTW89_USB_VENQT_READ), @@ -69,6 +69,12 @@ static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, } } +static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, + void *data, u16 len, u8 reqtype) +{ + __rtw89_usb_vendorreq(rtwdev, addr, data, len, reqtype, true); +} + static u32 rtw89_usb_read_cmac(struct rtw89_dev *rtwdev, u32 addr) { u32 addr32, val32, shift; @@ -157,6 +163,14 @@ static void rtw89_usb_ops_write32(struct rtw89_dev *rtwdev, u32 addr, u32 val) rtw89_usb_vendorreq(rtwdev, addr, &data, 4, RTW89_USB_VENQT_WRITE); } +static void rtw89_usb_write32_quiet(struct rtw89_dev *rtwdev, u32 addr, u32 val) +{ + __le32 data = cpu_to_le32(val); + + __rtw89_usb_vendorreq(rtwdev, addr, &data, 4, + RTW89_USB_VENQT_WRITE, false); +} + static u32 rtw89_usb_ops_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, u8 txch) @@ -1059,6 +1073,83 @@ static void rtw89_usb_intf_deinit(struct rtw89_dev *rtwdev, usb_set_intfdata(intf, NULL); } +static int rtw89_usb_switch_mode_ax(struct rtw89_dev *rtwdev) +{ + u32 pad_ctrl2; + + /* No known USB 3 devices with this chip. */ + if (rtwdev->chip->chip_id == RTL8851B) + return 0; + + pad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_AX_PAD_CTRL2); + + rtw89_debug(rtwdev, RTW89_DBG_HCI, "%s: pad_ctrl2: %#x\n", + __func__, pad_ctrl2); + + /* Already tried to switch but it's a USB 2 port. */ + if (u32_get_bits(pad_ctrl2, B_AX_MATCH_CNT) == USB_SWITCH_DELAY) + return 0; + + /* Add delay to prevent some platforms would not detect USB switch */ + u32p_replace_bits(&pad_ctrl2, USB_SWITCH_DELAY, B_AX_MATCH_CNT); + + pad_ctrl2 &= ~(B_AX_FORCE_U3_CK | B_AX_USB2_FORCE | + B_AX_USB3_FORCE | B_AX_USB3_USB2_TRANSITION); + + u32p_replace_bits(&pad_ctrl2, USB_MODE_U3, B_AX_USB23_SW_MODE_V1); + + pad_ctrl2 |= B_AX_NO_PDN_CHIPOFF_V1 | B_AX_RSM_EN_V1; + + rtw89_usb_write32_quiet(rtwdev, R_AX_PAD_CTRL2, pad_ctrl2); + + return 1; +} + +static int rtw89_usb_switch_mode_be(struct rtw89_dev *rtwdev) +{ + u32 pad_ctrl2; + + pad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_BE_PAD_CTRL2); + + rtw89_debug(rtwdev, RTW89_DBG_HCI, "%s: pad_ctrl2: %#x\n", + __func__, pad_ctrl2); + + /* Already tried to switch but it's a USB 2 port. */ + if (u32_get_bits(pad_ctrl2, B_BE_MATCH_CNT) == USB_SWITCH_DELAY) + return 0; + + /* Add delay to prevent some platforms would not detect USB switch */ + u32p_replace_bits(&pad_ctrl2, USB_SWITCH_DELAY, B_BE_MATCH_CNT); + + pad_ctrl2 |= B_BE_RSM_EN_V1 | B_BE_NO_PDN_CHIPOFF_V1 | + B_BE_USB_AUTO_INSTALL_MASK | B_BE_USB23_SW_MODE; + + pad_ctrl2 &= ~(B_BE_USB3_FORCE | B_BE_USB2_FORCE | + B_BE_FORCE_U3_CK | B_BE_FORCE_U2_CK | + B_BE_FORCE_CLK_U2 | B_BE_USB3_GEN_MODE | + B_BE_USB3_LANE_MODE); + + rtw89_usb_write32_quiet(rtwdev, R_BE_PAD_CTRL2, pad_ctrl2); + + return 1; +} + +static int rtw89_usb_switch_mode(struct rtw89_dev *rtwdev) +{ + struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + + if (rtwusb->udev->speed == USB_SPEED_SUPER) { + rtw89_info(rtwdev, + "2.4 GHz performance may be better in a USB 2 port\n"); + return 0; + } + + if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) + return rtw89_usb_switch_mode_ax(rtwdev); + + return rtw89_usb_switch_mode_be(rtwdev); +} + int rtw89_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1091,6 +1182,13 @@ int rtw89_usb_probe(struct usb_interface *intf, goto err_free_hw; } + ret = rtw89_usb_switch_mode(rtwdev); + if (ret) { + /* Not a fail, but we do need to skip rtw89_core_register. */ + ret = 0; + goto err_intf_deinit; + } + if (rtwusb->udev->speed == USB_SPEED_SUPER) rtwdev->hci.dle_type = RTW89_HCI_DLE_TYPE_USB3; else From 575e6a72bd68008b6795345113d3e94f3dccbf5d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 25 May 2026 09:17:28 +0800 Subject: [PATCH 102/105] wifi: rtw89: 8851bu: add Mercusys MA60XNB (2c4e:0128) Add the specific USB device ID which adapter tested fully functional on Fedora 44 with kernel 7.0.8-200.fc44.x86_64 and linux-firmware 20260410-1.fc44. Reported-by: Guillermo Servera Negre Tested-by: Guillermo Servera Negre Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260525011728.6836-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8851bu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c index 6a8d31544314..14f1b703902f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c @@ -44,6 +44,9 @@ static const struct usb_device_id rtw_8851bu_id_table[] = { /* D-Link AX9U rev. A1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x332a, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, + /* Mercusys MA60XNB */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2C4E, 0x0128, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, /* TP-Link Archer TX10UB Nano */ { USB_DEVICE_AND_INTERFACE_INFO(0x3625, 0x010b, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, From bf4a37f516f0382832c10a9d04414944d0d96591 Mon Sep 17 00:00:00 2001 From: David Lee Date: Mon, 25 May 2026 16:26:36 +0800 Subject: [PATCH 103/105] wifi: rtw89: usb: skip ACPI capability check for USB devices Skip the ACPI capability check for all USB devices by default, allowing them to use their default configurations. For USB dongles, customers will manage their own compliance and certification. This initial patch focuses on the generic USB skip infrastructure; specific customer certifications and localized configurations will be handled by quirks afterward. Signed-off-by: David Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260525082636.31105-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/regd.c | 24 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/sar.c | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 28466cb35ea2..14fab6b1a74a 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -360,6 +360,7 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev, struct wiphy *wiphy) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_chip_info *chip = rtwdev->chip; struct ieee80211_supported_band *sband; struct rtw89_acpi_dsm_result res = {}; @@ -377,6 +378,9 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev, return; } + if (skip_acpi_dsm) + return; + bitmap_fill(regulatory->block_unii4, RTW89_REGD_MAX_COUNTRY_NUM); ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_UNII4_SUP, &res); @@ -426,6 +430,7 @@ static void __rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev, bool block, static void rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_country_code *country; const struct rtw89_acpi_policy_6ghz *ptr; struct rtw89_acpi_dsm_result res = {}; @@ -433,6 +438,9 @@ static void rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev) int i, j; int ret; + if (skip_acpi_dsm) + return; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6G_BP, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_REGD, @@ -478,12 +486,16 @@ static void rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev) static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_policy_6ghz_sp *ptr; struct rtw89_acpi_dsm_result res = {}; bool enable; u8 index; int ret; + if (skip_acpi_dsm) + return; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6GHZ_SP_SUP, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_REGD, @@ -524,6 +536,7 @@ static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev) static void rtw89_regd_setup_policy_6ghz_vlp(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_policy_6ghz_vlp *ptr = NULL; struct rtw89_acpi_dsm_result res = {}; bool enable; @@ -531,6 +544,9 @@ static void rtw89_regd_setup_policy_6ghz_vlp(struct rtw89_dev *rtwdev) int ret; u8 val; + if (skip_acpi_dsm) + return; + /* By default, allow 6 GHz VLP on all countries except US and CA. */ val = ~(RTW89_ACPI_CONF_6GHZ_VLP_US | RTW89_ACPI_CONF_6GHZ_VLP_CA); @@ -574,6 +590,7 @@ static void rtw89_regd_setup_6ghz(struct rtw89_dev *rtwdev, struct wiphy *wiphy) { const struct rtw89_chip_info *chip = rtwdev->chip; bool chip_support_6ghz = chip->support_bands & BIT(NL80211_BAND_6GHZ); + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; bool regd_allow_6ghz = chip_support_6ghz; struct ieee80211_supported_band *sband; struct rtw89_acpi_dsm_result res = {}; @@ -583,6 +600,9 @@ static void rtw89_regd_setup_6ghz(struct rtw89_dev *rtwdev, struct wiphy *wiphy) if (!chip_support_6ghz) goto bottom; + if (skip_acpi_dsm) + goto bottom; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6G_DIS, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_REGD, @@ -661,10 +681,14 @@ const char *rtw89_regd_get_string(enum rtw89_regulation_type regd) static void rtw89_regd_setup_reg_rules(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_policy_reg_rules *ptr; struct rtw89_acpi_dsm_result res = {}; int ret; + if (skip_acpi_dsm) + return; + regulatory->txpwr_uk_follow_etsi = true; ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_REG_RULES_EN, &res); diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 7886ffaf5695..6fe3822eb598 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -693,6 +693,7 @@ static bool rtw89_tas_rolling_average(struct rtw89_dev *rtwdev) static void rtw89_tas_init(struct rtw89_dev *rtwdev) { + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_tas_info *tas = &rtwdev->tas; const struct rtw89_acpi_policy_tas *ptr; @@ -702,6 +703,9 @@ static void rtw89_tas_init(struct rtw89_dev *rtwdev) if (!chip->support_tas) return; + if (skip_acpi_dsm) + return; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_SAR, From 7554f6602a80471549df2c4e7b90b4d095b1d816 Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Fri, 29 May 2026 15:50:31 +0800 Subject: [PATCH 104/105] wifi: rtw89: add dev_id_quirks to driver_info for per-device quirk control Add a dev_id_quirks field to rtw89_driver_info so that per-device (VID/PID) quirks can be expressed independently of chip-level default_quirks. Apply the bitmap in rtw89_alloc_ieee80211_hw() so both USB and PCI probes benefit automatically. All existing driver_info structs initialize dev_id_quirks to 0; no behavior change. Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260529075032.16807-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 9 +++++++-- drivers/net/wireless/realtek/rtw89/core.h | 4 ++-- drivers/net/wireless/realtek/rtw89/pci.c | 3 +-- drivers/net/wireless/realtek/rtw89/rtw8851be.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8851bu.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852ae.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852au.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852be.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852bte.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852bu.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852ce.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922ae.c | 2 ++ drivers/net/wireless/realtek/rtw89/rtw8922au.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922de.c | 2 ++ drivers/net/wireless/realtek/rtw89/usb.c | 3 +-- 16 files changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index c1df3e2ba11e..68dad6090f87 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -7532,9 +7532,11 @@ EXPORT_SYMBOL(rtw89_core_unregister); struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, u32 bus_data_size, - const struct rtw89_chip_info *chip, - const struct rtw89_chip_variant *variant) + const struct rtw89_driver_info *info) { + const unsigned long *dev_id_quirks = &info->dev_id_quirks; + const struct rtw89_chip_variant *variant = info->variant; + const struct rtw89_chip_info *chip = info->chip; struct rtw89_fw_info early_fw = {}; const struct firmware *firmware; struct ieee80211_hw *hw; @@ -7600,6 +7602,9 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, rtwdev->fw.fw_format = fw_format; rtwdev->support_mlo = support_mlo; + bitmap_or(rtwdev->quirks, rtwdev->quirks, dev_id_quirks, + NUM_OF_RTW89_QUIRKS); + rtw89_debug(rtwdev, RTW89_DBG_CHAN, "probe driver %s chanctx\n", no_chanctx ? "without" : "with"); rtw89_debug(rtwdev, RTW89_DBG_CHAN, "probe driver %s MLO cap\n", diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index fb32d2bd9cb0..4c638c2bdc4f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4803,6 +4803,7 @@ struct rtw89_driver_info { const struct rtw89_chip_info *chip; const struct rtw89_chip_variant *variant; const struct dmi_system_id *quirks; + unsigned long dev_id_quirks; /* bitmap of rtw89_quirks */ union rtw89_bus_info bus; }; @@ -8129,8 +8130,7 @@ int rtw89_core_register(struct rtw89_dev *rtwdev); void rtw89_core_unregister(struct rtw89_dev *rtwdev); struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, u32 bus_data_size, - const struct rtw89_chip_info *chip, - const struct rtw89_chip_variant *variant); + const struct rtw89_driver_info *info); void rtw89_free_ieee80211_hw(struct rtw89_dev *rtwdev); u8 rtw89_acquire_mac_id(struct rtw89_dev *rtwdev); void rtw89_release_mac_id(struct rtw89_dev *rtwdev, u8 mac_id); diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index fe1152c560bd..102bae488180 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -4772,8 +4772,7 @@ int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) info = (const struct rtw89_driver_info *)id->driver_data; rtwdev = rtw89_alloc_ieee80211_hw(&pdev->dev, - sizeof(struct rtw89_pci), - info->chip, info->variant); + sizeof(struct rtw89_pci), info); if (!rtwdev) { dev_err(&pdev->dev, "failed to allocate hw\n"); return -ENOMEM; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851be.c b/drivers/net/wireless/realtek/rtw89/rtw8851be.c index ce59ac9f56ba..640672eb0d26 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851be.c @@ -73,6 +73,7 @@ static const struct rtw89_driver_info rtw89_8851be_info = { .chip = &rtw8851b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8851b_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c index 14f1b703902f..34ba5661d771 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c @@ -31,6 +31,7 @@ static const struct rtw89_driver_info rtw89_8851bu_info = { .chip = &rtw8851b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8851b_usb_info, } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c index 9e05e831569d..64306cdc1ee4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c @@ -71,6 +71,7 @@ static const struct rtw89_driver_info rtw89_8852ae_info = { .chip = &rtw8852a_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852a_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852au.c b/drivers/net/wireless/realtek/rtw89/rtw8852au.c index 4cced4619b7d..29b7f7769370 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852au.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852au.c @@ -33,6 +33,7 @@ static const struct rtw89_driver_info rtw89_8852au_info = { .chip = &rtw8852a_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8852a_usb_info, } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852be.c b/drivers/net/wireless/realtek/rtw89/rtw8852be.c index 12db0d0be547..5bc0a6a99d1d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852be.c @@ -73,6 +73,7 @@ static const struct rtw89_driver_info rtw89_8852be_info = { .chip = &rtw8852b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852b_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c index 8c995aa95325..49a72ca835ac 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c @@ -79,6 +79,7 @@ static const struct rtw89_driver_info rtw89_8852bte_info = { .chip = &rtw8852bt_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852bt_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c index 37111fed276f..308d3d570ff3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c @@ -31,6 +31,7 @@ static const struct rtw89_driver_info rtw89_8852bu_info = { .chip = &rtw8852b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8852b_usb_info, } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c index 150fed189414..3c64c0539205 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c @@ -102,6 +102,7 @@ static const struct rtw89_driver_info rtw89_8852ce_info = { .chip = &rtw8852c_chip_info, .variant = NULL, .quirks = rtw8852c_pci_quirks, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852c_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 790fd1dec66d..8f89f9a31455 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -33,6 +33,7 @@ static const struct rtw89_driver_info rtw89_8852cu_info = { .chip = &rtw8852c_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8852c_usb_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c index 90c62b757c57..5527a8db393b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c @@ -77,6 +77,7 @@ static const struct rtw89_driver_info rtw89_8922ae_info = { .chip = &rtw8922a_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922a_pci_info, }, @@ -86,6 +87,7 @@ static const struct rtw89_driver_info rtw89_8922ae_vs_info = { .chip = &rtw8922a_chip_info, .variant = &rtw8922ae_vs_variant, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922a_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922au.c b/drivers/net/wireless/realtek/rtw89/rtw8922au.c index 347bde171391..2b81de501d62 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922au.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922au.c @@ -32,6 +32,7 @@ static const struct rtw89_driver_info rtw89_8922au_info = { .chip = &rtw8922a_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8922a_usb_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922de.c b/drivers/net/wireless/realtek/rtw89/rtw8922de.c index f144e7fc76de..a1a81c338be3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922de.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922de.c @@ -73,6 +73,7 @@ static const struct rtw89_driver_info rtw89_8922de_vs_info = { .chip = &rtw8922d_chip_info, .variant = &rtw8922de_vs_variant, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922d_pci_info, }, @@ -82,6 +83,7 @@ static const struct rtw89_driver_info rtw89_8922de_info = { .chip = &rtw8922d_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922d_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 198378018062..67ebf2d9bb7d 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -1161,8 +1161,7 @@ int rtw89_usb_probe(struct usb_interface *intf, info = (const struct rtw89_driver_info *)id->driver_info; rtwdev = rtw89_alloc_ieee80211_hw(&intf->dev, - sizeof(struct rtw89_usb), - info->chip, info->variant); + sizeof(struct rtw89_usb), info); if (!rtwdev) { dev_err(&intf->dev, "failed to allocate hw\n"); return -ENOMEM; From 384cc80f30977d2ff095615f2241b21904499255 Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Fri, 29 May 2026 15:50:32 +0800 Subject: [PATCH 105/105] wifi: rtw89: usb: add serial_number and uuid sysfs attributes for 0x28de:0x2432 Expose the device's Serial Number (SN) and UUID from EFUSE via two read-only sysfs attributes, `serial_number` and `uuid`, on the ieee80211 phy device under the `rtw89_usb` attribute group. This hardware identification information is essential for user-space applications to uniquely identify, track, and manage specific Wi-Fi adapters. For example, in automated factory provisioning or device management systems, user-space tools rely on the EFUSE serial number and UUID to bind configurations to specific physical adapters. Currently, standard wireless APIs do not expose this low-level hardware information, making these sysfs nodes the only viable solution for user space to extract this data. The attributes are gated behind a new RTW89_QUIRK_HW_INFO_SYSFS quirk, enabled only for the VID 0x28de / PID 0x2432 device via the dev_id_quirks field in rtw89_driver_info. Example usage from user-space: $ cat /sys/class/ieee80211/phy0/rtw89_usb/serial_number 3642000123 $ cat /sys/class/ieee80211/phy0/rtw89_usb/uuid aaec2b7c-0a55-4727-8de0-b30febccbbaa Cc: Elliot Saba Cc: Charles Lohr Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260529075032.16807-3-pkshih@realtek.com --- .../ABI/testing/sysfs-class-ieee80211-rtw89 | 24 +++++++++ drivers/net/wireless/realtek/rtw89/core.h | 6 +++ drivers/net/wireless/realtek/rtw89/rtw8852c.c | 10 ++++ drivers/net/wireless/realtek/rtw89/rtw8852c.h | 6 ++- .../net/wireless/realtek/rtw89/rtw8852cu.c | 12 ++++- drivers/net/wireless/realtek/rtw89/usb.c | 52 +++++++++++++++++++ 6 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 diff --git a/Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 b/Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 new file mode 100644 index 000000000000..7dfdce08a42f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 @@ -0,0 +1,24 @@ +What: /sys/class/ieee80211/phyX/rtw89_usb/serial_number +Date: May 2026 +Contact: Johnson Tsai , linux-wireless@vger.kernel.org +Description: (Read) Serial number burned into EFUSE of the RTL8852CU-based + USB Wi-Fi adapter. Only present on devices that set the + RTW89_QUIRK_HW_INFO_SYSFS quirk (currently VID 0x28de / + PID 0x2432). + + Format: %10phN (5 raw bytes printed as 10 lowercase hex + digits, no separators). + + Example: 3642000123 + +What: /sys/class/ieee80211/phyX/rtw89_usb/uuid +Date: May 2026 +Contact: Johnson Tsai , linux-wireless@vger.kernel.org +Description: (Read) UUID burned into EFUSE of the RTL8852CU-based USB Wi-Fi + adapter. Only present on devices that set the + RTW89_QUIRK_HW_INFO_SYSFS quirk (currently VID 0x28de / + PID 0x2432). + + Format: %pUb (RFC 4122 UUID in lowercase with hyphens). + + Example: aaec2b7c-0a55-4727-8de0-b30febccbbaa diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 4c638c2bdc4f..5547888d7e67 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3618,6 +3618,9 @@ struct rtw89_sta_link { u32 data_tx_cnt_lmt:6; }; +#define RTW89_EFUSE_SN_LEN 5 +#define RTW89_EFUSE_UUID_LEN 16 + struct rtw89_efuse { bool valid; bool power_k_valid; @@ -3628,6 +3631,8 @@ struct rtw89_efuse { u8 adc_td; u8 bt_setting_2; u8 bt_setting_3; + u8 sn[RTW89_EFUSE_SN_LEN]; + u8 uuid[RTW89_EFUSE_UUID_LEN]; }; struct rtw89_phy_rate_pattern { @@ -5376,6 +5381,7 @@ enum rtw89_quirks { RTW89_QUIRK_PCI_BER, RTW89_QUIRK_THERMAL_PROT_120C, RTW89_QUIRK_THERMAL_PROT_110C, + RTW89_QUIRK_HW_INFO_SYSFS, NUM_OF_RTW89_QUIRKS, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 7bb1264bcaef..3861cce42b1b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -621,6 +621,15 @@ static void rtw8852c_efuse_parsing_gain_offset(struct rtw89_dev *rtwdev, gain->offset_valid = valid; } +static void rtw8852c_efuse_copy_sn_uuid_usb(struct rtw89_dev *rtwdev, + const struct rtw8852c_efuse *map) +{ + struct rtw89_efuse *efuse = &rtwdev->efuse; + + memcpy(efuse->sn, map->u.sn, sizeof(efuse->sn)); + memcpy(efuse->uuid, map->u.uuid, sizeof(efuse->uuid)); +} + static int rtw8852c_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, enum rtw89_efuse_block block) { @@ -640,6 +649,7 @@ static int rtw8852c_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, break; case RTW89_HCI_TYPE_USB: ether_addr_copy(efuse->addr, map->u.mac_addr); + rtw8852c_efuse_copy_sn_uuid_usb(rtwdev, map); break; default: return -ENOTSUPP; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.h b/drivers/net/wireless/realtek/rtw89/rtw8852c.h index 8585921ac6c4..b1d7c354c18e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.h @@ -13,7 +13,11 @@ struct rtw8852c_u_efuse { u8 rsvd[0x88]; u8 mac_addr[ETH_ALEN]; -}; + u8 rsvd1[8]; + u8 sn[RTW89_EFUSE_SN_LEN]; + u8 rsvd2[29]; + u8 uuid[RTW89_EFUSE_UUID_LEN]; +} __packed; struct rtw8852c_e_efuse { u8 mac_addr[ETH_ALEN]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 8f89f9a31455..81ee96b0a048 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -39,6 +39,16 @@ static const struct rtw89_driver_info rtw89_8852cu_info = { }, }; +static const struct rtw89_driver_info rtw89_8852cu_valve_info = { + .chip = &rtw8852c_chip_info, + .variant = NULL, + .quirks = NULL, + .dev_id_quirks = BIT(RTW89_QUIRK_HW_INFO_SYSFS), + .bus = { + .usb = &rtw8852c_usb_info, + }, +}; + static const struct usb_device_id rtw_8852cu_id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03a6, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, @@ -53,7 +63,7 @@ static const struct usb_device_id rtw_8852cu_id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0x991d, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x28de, 0x2432, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + .driver_info = (kernel_ulong_t)&rtw89_8852cu_valve_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x8206, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x35b2, 0x0502, 0xff, 0xff, 0xff), diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 67ebf2d9bb7d..aafa8b9e93df 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -1150,6 +1150,56 @@ static int rtw89_usb_switch_mode(struct rtw89_dev *rtwdev) return rtw89_usb_switch_mode_be(rtwdev); } +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wiphy *wiphy = container_of(dev, struct wiphy, dev); + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct rtw89_dev *rtwdev = hw->priv; + struct rtw89_efuse *efuse = &rtwdev->efuse; + + return sysfs_emit(buf, "%*phN\n", + (int)sizeof(efuse->sn), efuse->sn); +} +static DEVICE_ATTR_RO(serial_number); + +static ssize_t uuid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wiphy *wiphy = container_of(dev, struct wiphy, dev); + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct rtw89_dev *rtwdev = hw->priv; + struct rtw89_efuse *efuse = &rtwdev->efuse; + + return sysfs_emit(buf, "%pUb\n", efuse->uuid); +} +static DEVICE_ATTR_RO(uuid); + +static struct attribute *rtw89_usb_attrs[] = { + &dev_attr_serial_number.attr, + &dev_attr_uuid.attr, + NULL, +}; + +static bool rtw89_usb_group_visible(struct kobject *kobj) +{ + struct device *dev = kobj_to_dev(kobj); + struct wiphy *wiphy = container_of(dev, struct wiphy, dev); + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct rtw89_dev *rtwdev = hw->priv; + + return test_bit(RTW89_QUIRK_HW_INFO_SYSFS, rtwdev->quirks); +} + +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(rtw89_usb); + +static const struct attribute_group rtw89_usb_group = { + .name = "rtw89_usb", + .attrs = rtw89_usb_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(rtw89_usb), +}; +__ATTRIBUTE_GROUPS(rtw89_usb); + int rtw89_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1171,6 +1221,8 @@ int rtw89_usb_probe(struct usb_interface *intf, rtwusb->rtwdev = rtwdev; rtwusb->info = info->bus.usb; + rtwdev->hw->wiphy->dev.groups = rtw89_usb_groups; + rtwdev->hci.ops = &rtw89_usb_ops; rtwdev->hci.type = RTW89_HCI_TYPE_USB; rtwdev->hci.tx_rpt_enabled = true;