Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH 0/6] wifi: rtw88: extend 88xxA support
@ 2026-10-29  5:52 Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 1/6] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
                   ` (5 more replies)
  0 siblings, 6 replies; 13+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng

This series enables HT STBC transmission, VHT SU beamformed reception,
carrier-frequency-offset correction and adaptivity on supported
88xxA devices.

Tested on RTL8812AU together with the driver-frame series.
TX went from 136.0 to 144.2.
RX went from 215.5 to 249.2.

Ho Jie Feng (6):
  wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions
  wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
  wifi: rtw88: prepare frequency-offset correction for more chips
  wifi: rtw88: 88xxa: correct carrier-frequency offset
  wifi: rtw88: 88xxa: adapt channel sensing to receiver gain
  wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits

 drivers/net/wireless/realtek/rtw88/bf.h       |   2 +
 drivers/net/wireless/realtek/rtw88/main.c     |  14 +-
 drivers/net/wireless/realtek/rtw88/main.h     |   1 +
 drivers/net/wireless/realtek/rtw88/phy.c      | 109 +++++++-
 drivers/net/wireless/realtek/rtw88/phy.h      |   2 +
 drivers/net/wireless/realtek/rtw88/reg.h      |   1 +
 drivers/net/wireless/realtek/rtw88/rtw8812a.c |  25 +-
 drivers/net/wireless/realtek/rtw88/rtw8814a.c |  68 ++++-
 drivers/net/wireless/realtek/rtw88/rtw8821a.c |  24 +-
 drivers/net/wireless/realtek/rtw88/rtw8822c.c | 100 +-------
 drivers/net/wireless/realtek/rtw88/rtw8822c.h |   3 -
 drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 232 +++++++++++++++++-
 drivers/net/wireless/realtek/rtw88/rtw88xxa.h |   8 +
 13 files changed, 465 insertions(+), 124 deletions(-)


base-commit: 9b0be45d4d52fd223994e151f169743a97abd290
-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/6] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

8812a and 8814a support STBC, enable it.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/main.c     | 2 +-
 drivers/net/wireless/realtek/rtw88/rtw8812a.c | 1 +
 drivers/net/wireless/realtek/rtw88/rtw8814a.c | 1 +
 3 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index 0f23498b5c96e..f7d7b66844862 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -1585,7 +1585,7 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
 
 	if (rtw_chip_has_rx_ldpc(rtwdev))
 		ht_cap->cap |= IEEE80211_HT_CAP_LDPC_CODING;
-	if (rtw_chip_has_tx_stbc(rtwdev))
+	if (rtw_chip_has_tx_stbc(rtwdev) && rtwdev->hal.rf_path_num > 1)
 		ht_cap->cap |= IEEE80211_HT_CAP_TX_STBC;
 
 	if (efuse->hw_cap.bw & BIT(RTW_CHANNEL_WIDTH_40))
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index 781b66ccde675..55f1d3d3240be 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -1077,6 +1077,7 @@ const struct rtw_chip_info rtw8812a_hw_spec = {
 	.rfe_defs = rtw8812a_rfe_defs,
 	.rfe_defs_size = ARRAY_SIZE(rtw8812a_rfe_defs),
 	.rx_ldpc = false,
+	.tx_stbc = true,
 	.amsdu_in_ampdu = true,
 	.hw_feature_report = false,
 	.c2h_ra_report_size = 4,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
index fa02aa299b5e9..5bb55606f14a1 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8814a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
@@ -2200,6 +2200,7 @@ const struct rtw_chip_info rtw8814a_hw_spec = {
 	.txgi_factor = 1,
 	.is_pwr_by_rate_dec = true,
 	.rx_ldpc = true,
+	.tx_stbc = true,
 	.max_power_index = 0x3f,
 	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_2,
 	.amsdu_in_ampdu = false, /* RX speed is better without AMSDU */
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 1/6] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  2026-10-29 12:33   ` Bitterblue Smith
  2026-10-29  5:52 ` [PATCH 3/6] wifi: rtw88: prepare frequency-offset correction for more chips Ho Jie Feng
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 13+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

Implement beamformee support for 88xxa since they have
support for it.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/bf.h       |  2 +
 drivers/net/wireless/realtek/rtw88/main.c     | 12 ++--
 drivers/net/wireless/realtek/rtw88/reg.h      |  1 +
 drivers/net/wireless/realtek/rtw88/rtw8812a.c |  3 +-
 drivers/net/wireless/realtek/rtw88/rtw8814a.c | 36 ++++++++++-
 drivers/net/wireless/realtek/rtw88/rtw8821a.c |  3 +-
 drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 62 +++++++++++++++++++
 drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  2 +
 8 files changed, 112 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw88/bf.h b/drivers/net/wireless/realtek/rtw88/bf.h
index a5d3010e6be6e..0aa78fb6a7e64 100644
--- a/drivers/net/wireless/realtek/rtw88/bf.h
+++ b/drivers/net/wireless/realtek/rtw88/bf.h
@@ -12,6 +12,8 @@
 #define REG_ASSOCIATED_BFMER0_INFO	0x06E4
 #define REG_ASSOCIATED_BFMER1_INFO	0x06EC
 #define REG_TX_CSI_RPT_PARAM_BW20	0x06F4
+#define REG_TX_CSI_RPT_PARAM_BW40	0x06F8
+#define REG_TX_CSI_RPT_PARAM_BW80	0x06FC
 #define REG_SND_PTCL_CTRL		0x0718
 #define BIT_DIS_CHK_VHTSIGB_CRC		BIT(6)
 #define BIT_DIS_CHK_VHTSIGA_CRC		BIT(5)
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index f7d7b66844862..0a1b5575b7299 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -1605,6 +1605,7 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
 static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
 			     struct ieee80211_sta_vht_cap *vht_cap)
 {
+	const struct rtw_chip_info *chip = rtwdev->chip;
 	struct rtw_efuse *efuse = &rtwdev->efuse;
 	u16 mcs_map = 0;
 	__le16 highest;
@@ -1623,10 +1624,13 @@ static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
 		       0;
 	if (rtwdev->hal.rf_path_num > 1)
 		vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
-	vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
-			IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
-	vht_cap->cap |= (rtwdev->hal.bfee_sts_cap <<
-			IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
+	if (rtw_bf_support && chip->bfer_su_max_num) {
+		vht_cap->cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+		vht_cap->cap |= rtwdev->hal.bfee_sts_cap <<
+				IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+	}
+	if (rtw_bf_support && chip->bfer_mu_max_num)
+		vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
 
 	if (rtw_chip_has_rx_ldpc(rtwdev))
 		vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h
index afcdb66fdb9a1..f8ec1e2931f63 100644
--- a/drivers/net/wireless/realtek/rtw88/reg.h
+++ b/drivers/net/wireless/realtek/rtw88/reg.h
@@ -671,6 +671,7 @@
 #define REG_IQK_COM96		0x0984
 
 #define REG_FAS			0x09a4
+#define REG_CSI_CONTENT_VALUE	0x09b4
 #define REG_RXSB		0x0a00
 #define BIT_RXSB_ANA_DIV	BIT(15)
 #define REG_CCK_RX		0x0a04
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index 55f1d3d3240be..5d58dff4fcee7 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -931,7 +931,7 @@ static const struct rtw_chip_ops rtw8812a_ops = {
 	.phy_calibration	= rtw8812a_phy_calibration,
 	.cck_pd_set		= rtw_phy_cck_pd_set,
 	.pwr_track		= rtw8812a_pwr_track,
-	.config_bfee		= NULL,
+	.config_bfee		= rtw88xxa_bf_config_bfee,
 	.set_gid_table		= NULL,
 	.cfg_csi_rate		= NULL,
 	.led_set		= rtw8812a_led_set,
@@ -1084,6 +1084,7 @@ const struct rtw_chip_info rtw8812a_hw_spec = {
 	.old_datarate_fb_limit = true,
 	.usb_tx_agg_desc_num = 1,
 	.iqk_threshold = 8,
+	.bfer_su_max_num = 2,
 	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
 	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
 
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
index 5bb55606f14a1..2db05a83766d6 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8814a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
@@ -4,6 +4,7 @@
 
 #include <linux/usb.h>
 #include "main.h"
+#include "bf.h"
 #include "coex.h"
 #include "tx.h"
 #include "phy.h"
@@ -115,10 +116,13 @@ static void rtw8814a_init_hwcap(struct rtw_dev *rtwdev)
 			   BIT(RTW_CHANNEL_WIDTH_80);
 	efuse->hw_cap.ptcl = EFUSE_HW_CAP_PTCL_VHT;
 
-	if (hal->rf_type == RF_3T3R)
+	if (hal->rf_type == RF_3T3R) {
 		efuse->hw_cap.nss = 3;
-	else
+		hal->bfee_sts_cap = 2;
+	} else {
 		efuse->hw_cap.nss = 2;
+		hal->bfee_sts_cap = 1;
+	}
 
 	rtw_dbg(rtwdev, RTW_DBG_EFUSE,
 		"hw cap: hci=0x%02x, bw=0x%02x, ptcl=0x%02x, ant_num=%d, nss=%d\n",
@@ -2039,6 +2043,31 @@ static void rtw8814a_led_set(struct led_classdev *led,
 	rtw_write32(rtwdev, REG_GPIO_PIN_CTRL_2, led_gpio_cfg);
 }
 
+#define RTW_NDP_RX_STANDBY_TIME_8814A	0x40
+#define RTW_SND_CTRL_SOUNDING_8814A	0xDB
+
+static void rtw8814a_bf_enable_bfee_su(struct rtw_dev *rtwdev,
+				       struct rtw_vif *vif,
+				       struct rtw_bfee *bfee)
+{
+	rtw_bf_enable_bfee_su(rtwdev, vif, bfee);
+
+	rtw_write16(rtwdev, REG_SND_PTCL_CTRL + 1, 0x0202);
+	rtw_write8(rtwdev, REG_SND_PTCL_CTRL, RTW_SND_CTRL_SOUNDING_8814A);
+	rtw_write8(rtwdev, REG_SND_PTCL_CTRL + 3,
+		   RTW_NDP_RX_STANDBY_TIME_8814A);
+}
+
+static void rtw8814a_bf_config_bfee(struct rtw_dev *rtwdev,
+				    struct rtw_vif *vif,
+				    struct rtw_bfee *bfee, bool enable)
+{
+	if (enable)
+		rtw8814a_bf_enable_bfee_su(rtwdev, vif, bfee);
+	else
+		rtw_bf_remove_bfee_su(rtwdev, bfee);
+}
+
 static void rtw8814a_fill_txdesc_checksum(struct rtw_dev *rtwdev,
 					  struct rtw_tx_pkt_info *pkt_info,
 					  u8 *txdesc)
@@ -2068,7 +2097,7 @@ static const struct rtw_chip_ops rtw8814a_ops = {
 	.phy_calibration	= rtw8814a_phy_calibration,
 	.cck_pd_set		= rtw8814a_phy_cck_pd_set,
 	.pwr_track		= rtw8814a_pwr_track,
-	.config_bfee		= NULL,
+	.config_bfee		= rtw8814a_bf_config_bfee,
 	.set_gid_table		= NULL,
 	.cfg_csi_rate		= NULL,
 	.led_set		= rtw8814a_led_set,
@@ -2231,6 +2260,7 @@ const struct rtw_chip_info rtw8814a_hw_spec = {
 	.rfe_defs = rtw8814a_rfe_defs,
 	.rfe_defs_size = ARRAY_SIZE(rtw8814a_rfe_defs),
 	.iqk_threshold = 8,
+	.bfer_su_max_num = 2,
 	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
 
 	.coex_para_ver = 0,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
index 6096d3b8e41be..6d106ae70d914 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -877,7 +877,7 @@ static const struct rtw_chip_ops rtw8821a_ops = {
 	.phy_calibration	= rtw8821a_phy_calibration,
 	.cck_pd_set		= rtw_phy_cck_pd_set,
 	.pwr_track		= rtw8821a_pwr_track,
-	.config_bfee		= NULL,
+	.config_bfee		= rtw88xxa_bf_config_bfee,
 	.set_gid_table		= NULL,
 	.cfg_csi_rate		= NULL,
 	.led_set		= rtw8821a_led_set,
@@ -1188,6 +1188,7 @@ const struct rtw_chip_info rtw8821a_hw_spec = {
 	.old_datarate_fb_limit = true,
 	.usb_tx_agg_desc_num = 6,
 	.iqk_threshold = 8,
+	.bfer_su_max_num = 2,
 	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
 	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
 
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
index c298fa0fb2a6b..d3dd0ebfb4024 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
@@ -138,6 +138,7 @@ static void rtw88xxa_read_usb_type(struct rtw_dev *rtwdev)
 		efuse->hw_cap.nss = 1;
 	else
 		efuse->hw_cap.nss = 2;
+	hal->bfee_sts_cap = 1;
 
 	if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
 		goto print_hw_cap;
@@ -197,6 +198,67 @@ static void rtw88xxa_read_usb_type(struct rtw_dev *rtwdev)
 		efuse->hw_cap.ant_num, efuse->hw_cap.nss);
 }
 
+#define RTW_NDP_RX_STANDBY_TIME_88XXA	0x50
+#define RTW_SND_CTRL_REMOVE_88XXA	0xC8
+#define RTW_SND_CTRL_SOUNDING_88XXA	0xCB
+
+void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
+			     struct rtw_bfee *bfee, bool enable)
+{
+	static const u32 csi_rpt_regs[] = {
+		REG_TX_CSI_RPT_PARAM_BW20,
+		REG_TX_CSI_RPT_PARAM_BW40,
+		REG_TX_CSI_RPT_PARAM_BW80,
+	};
+	struct rtw_bf_info *bf_info = &rtwdev->bf_info;
+	u8 slot = bfee->su_reg_index;
+	u32 addr_bfer_info;
+	u32 csi_param;
+	int i;
+
+	if (bfee->role != RTW_BFEE_SU || slot >= 2)
+		return;
+
+	addr_bfer_info = slot ? REG_ASSOCIATED_BFMER1_INFO :
+				      REG_ASSOCIATED_BFMER0_INFO;
+
+	rtw_dbg(rtwdev, RTW_DBG_BF, "chip %d SU beamformee %s slot %u\n",
+		rtwdev->chip->id, enable ? "enable" : "disable", slot);
+
+	if (!enable) {
+		rtw_write32(rtwdev, addr_bfer_info, 0);
+		rtw_write16(rtwdev, addr_bfer_info + 4, 0);
+
+		clear_bit(slot, bf_info->bfer_su_reg_maping);
+		bfee->su_reg_index = 0xff;
+
+		if (!bf_info->bfer_su_cnt)
+			rtw_write8(rtwdev, REG_SND_PTCL_CTRL,
+				   RTW_SND_CTRL_REMOVE_88XXA);
+		return;
+	}
+
+	if (hweight8(rtwdev->hal.antenna_rx) == 2) {
+		rtw_write32(rtwdev, REG_CSI_CONTENT_VALUE, 0);
+		csi_param = 0x01090109;
+	} else {
+		rtw_write32(rtwdev, REG_CSI_CONTENT_VALUE, 0x01081008);
+		csi_param = 0x01080108;
+	}
+
+	rtw_write8(rtwdev, REG_SND_PTCL_CTRL,
+		   RTW_SND_CTRL_SOUNDING_88XXA);
+	for (i = 0; i < ETH_ALEN; i++)
+		rtw_write8(rtwdev, addr_bfer_info + i, bfee->mac_addr[i]);
+
+	for (i = 0; i < ARRAY_SIZE(csi_rpt_regs); i++)
+		rtw_write32(rtwdev, csi_rpt_regs[i], csi_param);
+
+	rtw_write8(rtwdev, REG_SND_PTCL_CTRL + 3,
+		   RTW_NDP_RX_STANDBY_TIME_88XXA);
+}
+EXPORT_SYMBOL(rtw88xxa_bf_config_bfee);
+
 int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
 {
 	const struct rtw_chip_info *chip = rtwdev->chip;
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
index d9c8db58909ca..0aaf7aa4aafdd 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
@@ -170,5 +170,7 @@ bool rtw88xxa_iqk_finish(int average, int threshold,
 void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
 			   void (*do_lck)(struct rtw_dev *rtwdev),
 			   void (*do_iqk)(struct rtw_dev *rtwdev));
+void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
+			     struct rtw_bfee *bfee, bool enable);
 
 #endif
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/6] wifi: rtw88: prepare frequency-offset correction for more chips
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 1/6] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 4/6] wifi: rtw88: 88xxa: correct carrier-frequency offset Ho Jie Feng
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

Promote chip specific functions into the common core for cfo tracking.
These become helpers which can be used by the other drivers.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/phy.c      | 106 ++++++++++++++++++
 drivers/net/wireless/realtek/rtw88/phy.h      |   2 +
 drivers/net/wireless/realtek/rtw88/rtw8822c.c | 100 +----------------
 drivers/net/wireless/realtek/rtw88/rtw8822c.h |   3 -
 4 files changed, 113 insertions(+), 98 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
index e2515a662bb30..d0c1fb65be956 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.c
+++ b/drivers/net/wireless/realtek/rtw88/phy.c
@@ -720,6 +720,112 @@ void rtw_phy_parsing_cfo(struct rtw_dev *rtwdev,
 }
 EXPORT_SYMBOL(rtw_phy_parsing_cfo);
 
+#define CFO_TRK_ENABLE_TH	20
+#define CFO_TRK_STOP_TH		10
+#define CFO_TRK_ADJ_TH		10
+
+static u8 rtw_phy_cfo_tracking_reset(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+
+	cfo->is_adjust = true;
+
+	if (cfo->crystal_cap > rtwdev->efuse.crystal_cap)
+		return cfo->crystal_cap - 1;
+	else if (cfo->crystal_cap < rtwdev->efuse.crystal_cap)
+		return cfo->crystal_cap + 1;
+
+	return cfo->crystal_cap;
+}
+
+void rtw_phy_cfo_xcap_init(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+
+	cfo->crystal_cap = rtwdev->efuse.crystal_cap;
+	cfo->is_adjust = true;
+}
+EXPORT_SYMBOL(rtw_phy_cfo_xcap_init);
+
+#define REPORT_TO_KHZ(val) ({typeof(val) _v = (val); (_v << 1) + (_v >> 1); })
+static s32 rtw_phy_cfo_calc_avg(struct rtw_dev *rtwdev, u8 path_num)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+	s32 cfo_avg, cfo_path_sum = 0, cfo_rpt_sum;
+	u8 i;
+
+	for (i = 0; i < path_num; i++) {
+		cfo_rpt_sum = REPORT_TO_KHZ(cfo->cfo_tail[i]);
+
+		if (cfo->cfo_cnt[i])
+			cfo_avg = cfo_rpt_sum / cfo->cfo_cnt[i];
+		else
+			cfo_avg = 0;
+
+		cfo_path_sum += cfo_avg;
+	}
+
+	for (i = 0; i < path_num; i++) {
+		cfo->cfo_tail[i] = 0;
+		cfo->cfo_cnt[i] = 0;
+	}
+
+	return cfo_path_sum / path_num;
+}
+
+static void rtw_phy_cfo_need_adjust(struct rtw_dev *rtwdev, s32 cfo_avg)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+
+	if (!cfo->is_adjust) {
+		if (abs(cfo_avg) > CFO_TRK_ENABLE_TH)
+			cfo->is_adjust = true;
+	} else {
+		if (abs(cfo_avg) <= CFO_TRK_STOP_TH)
+			cfo->is_adjust = false;
+	}
+}
+
+u8 rtw_phy_cfo_xcap_track(struct rtw_dev *rtwdev, u8 xcap_max, bool bt_hold)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+	u8 path_num = rtwdev->hal.rf_path_num;
+	s8 crystal_cap = cfo->crystal_cap;
+	s32 cfo_avg = 0;
+
+	if (rtwdev->sta_cnt != 1)
+		return rtw_phy_cfo_tracking_reset(rtwdev);
+
+	if (cfo->packet_count == cfo->packet_count_pre)
+		return cfo->crystal_cap;
+
+	cfo->packet_count_pre = cfo->packet_count;
+	cfo_avg = rtw_phy_cfo_calc_avg(rtwdev, path_num);
+	rtw_phy_cfo_need_adjust(rtwdev, cfo_avg);
+
+	if (bt_hold) {
+		cfo->is_adjust = false;
+		return rtwdev->efuse.crystal_cap;
+	}
+
+	if (cfo->is_adjust) {
+		if (cfo_avg > CFO_TRK_ADJ_TH)
+			crystal_cap++;
+		else if (cfo_avg < -CFO_TRK_ADJ_TH)
+			crystal_cap--;
+
+		crystal_cap = clamp_t(s8, crystal_cap, 0, xcap_max);
+	}
+
+	return (u8)crystal_cap;
+}
+EXPORT_SYMBOL(rtw_phy_cfo_xcap_track);
+
 static void rtw_phy_cfo_track(struct rtw_dev *rtwdev)
 {
 	const struct rtw_chip_info *chip = rtwdev->chip;
diff --git a/drivers/net/wireless/realtek/rtw88/phy.h b/drivers/net/wireless/realtek/rtw88/phy.h
index 8a843aac7350a..b551cb860d625 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.h
+++ b/drivers/net/wireless/realtek/rtw88/phy.h
@@ -68,6 +68,8 @@ void rtw_phy_adaptivity_set_mode(struct rtw_dev *rtwdev);
 void rtw_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl);
 void rtw_phy_parsing_cfo(struct rtw_dev *rtwdev,
 			 struct rtw_rx_pkt_stat *pkt_stat);
+void rtw_phy_cfo_xcap_init(struct rtw_dev *rtwdev);
+u8 rtw_phy_cfo_xcap_track(struct rtw_dev *rtwdev, u8 xcap_max, bool bt_hold);
 void rtw_phy_tx_path_diversity(struct rtw_dev *rtwdev);
 
 struct rtw_txpwr_lmt_cfg_pair {
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
index 32d9709f2c243..54f012a238391 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
@@ -4243,103 +4243,13 @@ static void rtw8822c_set_crystal_cap(struct rtw_dev *rtwdev, u8 crystal_cap)
 	rtw8822c_set_crystal_cap_reg(rtwdev, crystal_cap);
 }
 
-static void rtw8822c_cfo_tracking_reset(struct rtw_dev *rtwdev)
-{
-	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
-	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-
-	cfo->is_adjust = true;
-
-	if (cfo->crystal_cap > rtwdev->efuse.crystal_cap)
-		rtw8822c_set_crystal_cap(rtwdev, cfo->crystal_cap - 1);
-	else if (cfo->crystal_cap < rtwdev->efuse.crystal_cap)
-		rtw8822c_set_crystal_cap(rtwdev, cfo->crystal_cap + 1);
-}
-
-static void rtw8822c_cfo_init(struct rtw_dev *rtwdev)
-{
-	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
-	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-
-	cfo->crystal_cap = rtwdev->efuse.crystal_cap;
-	cfo->is_adjust = true;
-}
-
-#define REPORT_TO_KHZ(val) ({typeof(val) _v = (val); (_v << 1) + (_v >> 1); })
-static s32 rtw8822c_cfo_calc_avg(struct rtw_dev *rtwdev, u8 path_num)
-{
-	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
-	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-	s32 cfo_avg, cfo_path_sum = 0, cfo_rpt_sum;
-	u8 i;
-
-	for (i = 0; i < path_num; i++) {
-		cfo_rpt_sum = REPORT_TO_KHZ(cfo->cfo_tail[i]);
-
-		if (cfo->cfo_cnt[i])
-			cfo_avg = cfo_rpt_sum / cfo->cfo_cnt[i];
-		else
-			cfo_avg = 0;
-
-		cfo_path_sum += cfo_avg;
-	}
-
-	for (i = 0; i < path_num; i++) {
-		cfo->cfo_tail[i] = 0;
-		cfo->cfo_cnt[i] = 0;
-	}
-
-	return cfo_path_sum / path_num;
-}
-
-static void rtw8822c_cfo_need_adjust(struct rtw_dev *rtwdev, s32 cfo_avg)
-{
-	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
-	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-
-	if (!cfo->is_adjust) {
-		if (abs(cfo_avg) > CFO_TRK_ENABLE_TH)
-			cfo->is_adjust = true;
-	} else {
-		if (abs(cfo_avg) <= CFO_TRK_STOP_TH)
-			cfo->is_adjust = false;
-	}
-
-	if (!rtw_coex_disabled(rtwdev)) {
-		cfo->is_adjust = false;
-		rtw8822c_set_crystal_cap(rtwdev, rtwdev->efuse.crystal_cap);
-	}
-}
-
 static void rtw8822c_cfo_track(struct rtw_dev *rtwdev)
 {
-	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
-	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-	u8 path_num = rtwdev->hal.rf_path_num;
-	s8 crystal_cap = cfo->crystal_cap;
-	s32 cfo_avg = 0;
-
-	if (rtwdev->sta_cnt != 1) {
-		rtw8822c_cfo_tracking_reset(rtwdev);
-		return;
-	}
-
-	if (cfo->packet_count == cfo->packet_count_pre)
-		return;
-
-	cfo->packet_count_pre = cfo->packet_count;
-	cfo_avg = rtw8822c_cfo_calc_avg(rtwdev, path_num);
-	rtw8822c_cfo_need_adjust(rtwdev, cfo_avg);
-
-	if (cfo->is_adjust) {
-		if (cfo_avg > CFO_TRK_ADJ_TH)
-			crystal_cap++;
-		else if (cfo_avg < -CFO_TRK_ADJ_TH)
-			crystal_cap--;
+	u8 crystal_cap;
 
-		crystal_cap = clamp_t(s8, crystal_cap, 0, XCAP_MASK);
-		rtw8822c_set_crystal_cap(rtwdev, (u8)crystal_cap);
-	}
+	crystal_cap = rtw_phy_cfo_xcap_track(rtwdev, XCAP_MASK,
+					     !rtw_coex_disabled(rtwdev));
+	rtw8822c_set_crystal_cap(rtwdev, crystal_cap);
 }
 
 static const struct rtw_phy_cck_pd_reg
@@ -4985,7 +4895,7 @@ static const struct rtw_chip_ops rtw8822c_ops = {
 	.cfg_csi_rate		= rtw_bf_cfg_csi_rate,
 	.adaptivity_init	= rtw8822c_adaptivity_init,
 	.adaptivity		= rtw8822c_adaptivity,
-	.cfo_init		= rtw8822c_cfo_init,
+	.cfo_init		= rtw_phy_cfo_xcap_init,
 	.cfo_track		= rtw8822c_cfo_track,
 	.config_tx_path		= rtw8822c_config_tx_path,
 	.config_txrx_mode	= rtw8822c_config_trx_mode,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.h b/drivers/net/wireless/realtek/rtw88/rtw8822c.h
index fc62b67a15f21..c27e4c394a435 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.h
@@ -181,9 +181,6 @@ const struct rtw_table name ## _tbl = {			\
 #define REG_ANAPARLDO_POW_MAC	0x0029
 #define BIT_LDOE25_PON		BIT(0)
 #define XCAP_MASK		GENMASK(6, 0)
-#define CFO_TRK_ENABLE_TH	20
-#define CFO_TRK_STOP_TH		10
-#define CFO_TRK_ADJ_TH		10
 
 #define REG_TXDFIR0		0x808
 #define REG_DFIRBW		0x810
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/6] wifi: rtw88: 88xxa: correct carrier-frequency offset
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
                   ` (2 preceding siblings ...)
  2026-10-29  5:52 ` [PATCH 3/6] wifi: rtw88: prepare frequency-offset correction for more chips Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain Ho Jie Feng
  2026-10-29  5:52 ` [PATCH 6/6] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits Ho Jie Feng
  5 siblings, 0 replies; 13+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

Use the newly promoted helpers to implement support for 88xxa

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/phy.c      |  5 +-
 drivers/net/wireless/realtek/rtw88/rtw8812a.c |  2 +
 drivers/net/wireless/realtek/rtw88/rtw8814a.c | 31 ++++++++++--
 drivers/net/wireless/realtek/rtw88/rtw8821a.c |  2 +
 drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 47 +++++++++++++++----
 drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  1 +
 6 files changed, 73 insertions(+), 15 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
index d0c1fb65be956..69998c0d7c1f8 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.c
+++ b/drivers/net/wireless/realtek/rtw88/phy.c
@@ -687,13 +687,14 @@ static void rtw_phy_parsing_cfo_iter(void *data, u8 *mac,
 	struct rtw_rx_pkt_stat *pkt_stat = iter_data->pkt_stat;
 	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
+	u8 path_num = min_t(u8, rtwdev->hal.rf_path_num, 2);
 	u8 *bssid = iter_data->bssid;
 	u8 i;
 
 	if (!ether_addr_equal(vif->bss_conf.bssid, bssid))
 		return;
 
-	for (i = 0; i < rtwdev->hal.rf_path_num; i++) {
+	for (i = 0; i < path_num; i++) {
 		cfo->cfo_tail[i] += pkt_stat->cfo_tail[i];
 		cfo->cfo_cnt[i]++;
 	}
@@ -794,7 +795,7 @@ u8 rtw_phy_cfo_xcap_track(struct rtw_dev *rtwdev, u8 xcap_max, bool bt_hold)
 {
 	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 	struct rtw_cfo_track *cfo = &dm_info->cfo_track;
-	u8 path_num = rtwdev->hal.rf_path_num;
+	u8 path_num = min_t(u8, rtwdev->hal.rf_path_num, 2);
 	s8 crystal_cap = cfo->crystal_cap;
 	s32 cfo_avg = 0;
 
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index 5d58dff4fcee7..c8008cf43da9d 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -943,6 +943,8 @@ static const struct rtw_chip_ops rtw8812a_ops = {
 	.coex_set_rfe_type	= rtw8821a_coex_cfg_rfe_type,
 	.coex_set_wl_tx_power	= rtw8821a_coex_cfg_wl_tx_power,
 	.coex_set_wl_rx_gain	= rtw8821a_coex_cfg_wl_rx_gain,
+	.cfo_init		= rtw_phy_cfo_xcap_init,
+	.cfo_track		= rtw88xxa_cfo_track,
 };
 
 static const struct rtw_page_table page_table_8812a[] = {
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
index 2db05a83766d6..3c65cd838ce15 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8814a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
@@ -145,7 +145,7 @@ static int rtw8814a_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
 	efuse->usb_mode_switch = u8_get_bits(map->usb_mode, BIT(4));
 	efuse->rfe_option = map->rfe_option;
 	efuse->rf_board_option = map->rf_board_option;
-	efuse->crystal_cap = map->xtal_k;
+	efuse->crystal_cap = map->xtal_k & 0x3f;
 	efuse->channel_plan = map->channel_plan;
 	efuse->country_code[0] = map->country_code[0];
 	efuse->country_code[1] = map->country_code[1];
@@ -284,9 +284,15 @@ static void rtw8814a_config_cck_rx_antenna_init(struct rtw_dev *rtwdev)
 	rtw_write32_mask(rtwdev, REG_CCKTX, BIT_CMB_CCA_2R, 1);
 }
 
+static void rtw8814a_set_crystal_cap(struct rtw_dev *rtwdev, u8 crystal_cap)
+{
+	rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x07ff8000,
+			 crystal_cap | (crystal_cap << 6));
+}
+
 static void rtw8814a_phy_set_param(struct rtw_dev *rtwdev)
 {
-	u32 crystal_cap, val32;
+	u32 val32;
 	u8 val8, rf_path;
 
 	/* power on BB/RF domain */
@@ -308,9 +314,7 @@ static void rtw8814a_phy_set_param(struct rtw_dev *rtwdev)
 	rtw_load_table(rtwdev, rtwdev->chip->bb_tbl);
 	rtw_load_table(rtwdev, rtwdev->chip->agc_tbl);
 
-	crystal_cap = rtwdev->efuse.crystal_cap & 0x3F;
-	crystal_cap |= crystal_cap << 6;
-	rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x07ff8000, crystal_cap);
+	rtw8814a_set_crystal_cap(rtwdev, rtwdev->efuse.crystal_cap);
 
 	rtw8814a_config_trx_path(rtwdev);
 
@@ -1269,6 +1273,8 @@ static void rtw8814a_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
 			pkt_stat->cfo_tail[i] = cfo[i];
 			dm_info->cfo_tail[i] = (cfo[i] * 5) >> 1;
 		}
+
+		rtw_phy_parsing_cfo(rtwdev, pkt_stat);
 	}
 }
 
@@ -2068,6 +2074,19 @@ static void rtw8814a_bf_config_bfee(struct rtw_dev *rtwdev,
 		rtw_bf_remove_bfee_su(rtwdev, bfee);
 }
 
+static void rtw8814a_cfo_track(struct rtw_dev *rtwdev)
+{
+	struct rtw_cfo_track *cfo = &rtwdev->dm_info.cfo_track;
+	u8 crystal_cap;
+
+	crystal_cap = rtw_phy_cfo_xcap_track(rtwdev, 0x3f, false);
+	if (crystal_cap == cfo->crystal_cap)
+		return;
+
+	cfo->crystal_cap = crystal_cap;
+	rtw8814a_set_crystal_cap(rtwdev, crystal_cap);
+}
+
 static void rtw8814a_fill_txdesc_checksum(struct rtw_dev *rtwdev,
 					  struct rtw_tx_pkt_info *pkt_info,
 					  u8 *txdesc)
@@ -2102,6 +2121,8 @@ static const struct rtw_chip_ops rtw8814a_ops = {
 	.cfg_csi_rate		= NULL,
 	.led_set		= rtw8814a_led_set,
 	.fill_txdesc_checksum	= rtw8814a_fill_txdesc_checksum,
+	.cfo_init		= rtw_phy_cfo_xcap_init,
+	.cfo_track		= rtw8814a_cfo_track,
 
 	.coex_set_init		= rtw8814a_coex_cfg_init,
 	.coex_set_ant_switch	= rtw8814a_coex_cfg_ant_switch,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
index 6d106ae70d914..17d3677ca463c 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -889,6 +889,8 @@ static const struct rtw_chip_ops rtw8821a_ops = {
 	.coex_set_rfe_type	= rtw8821a_coex_cfg_rfe_type,
 	.coex_set_wl_tx_power	= rtw8821a_coex_cfg_wl_tx_power,
 	.coex_set_wl_rx_gain	= rtw8821a_coex_cfg_wl_rx_gain,
+	.cfo_init		= rtw_phy_cfo_xcap_init,
+	.cfo_track		= rtw88xxa_cfo_track,
 };
 
 static const struct rtw_page_table page_table_8821a[] = {
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
index d3dd0ebfb4024..586bb57a10061 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
@@ -279,6 +279,7 @@ int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
 	efuse->crystal_cap = map->xtal_k;
 	if (efuse->crystal_cap == 0xff)
 		efuse->crystal_cap = 0x20;
+	efuse->crystal_cap &= 0x3F;
 	efuse->pa_type_2g = map->pa_type;
 	efuse->pa_type_5g = map->pa_type;
 	efuse->lna_type_2g = map->lna_type_2g;
@@ -632,9 +633,19 @@ static void rtw88xxa_init_beacon_parameters(struct rtw_dev *rtwdev)
 	rtw_write16(rtwdev, REG_BCNTCFG, 0x4413);
 }
 
+static void rtw88xxa_set_crystal_cap(struct rtw_dev *rtwdev, u8 crystal_cap)
+{
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
+		rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x7FF80000,
+				 crystal_cap | (crystal_cap << 6));
+	else
+		rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x00FFF000,
+				 crystal_cap | (crystal_cap << 6));
+}
+
 static void rtw88xxa_phy_bb_config(struct rtw_dev *rtwdev)
 {
-	u8 val8, crystal_cap;
+	u8 val8;
 
 	/* power on BB/RF domain */
 	val8 = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
@@ -653,13 +664,7 @@ static void rtw88xxa_phy_bb_config(struct rtw_dev *rtwdev)
 	rtw_load_table(rtwdev, rtwdev->chip->bb_tbl);
 	rtw_load_table(rtwdev, rtwdev->chip->agc_tbl);
 
-	crystal_cap = rtwdev->efuse.crystal_cap & 0x3F;
-	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
-		rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x7FF80000,
-				 crystal_cap | (crystal_cap << 6));
-	else
-		rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x00FFF000,
-				 crystal_cap | (crystal_cap << 6));
+	rtw88xxa_set_crystal_cap(rtwdev, rtwdev->efuse.crystal_cap & 0x3F);
 }
 
 static void rtw88xxa_phy_rf_config(struct rtw_dev *rtwdev)
@@ -1621,6 +1626,17 @@ void rtw88xxa_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
 			power_b = power_a;
 
 		pkt_stat->signal_power = max3(power_a, power_b, min_rx_power);
+
+		pkt_stat->cfo_tail[RF_PATH_A] =
+			(s8)le32_get_bits(rpt->w2, RTW_JGRPHY_W2_CFO_TAIL_A);
+		pkt_stat->cfo_tail[RF_PATH_B] =
+			(s8)le32_get_bits(rpt->w2, RTW_JGRPHY_W2_CFO_TAIL_B);
+
+		/* convert to kHz (used only for debugfs) */
+		for (i = RF_PATH_A; i < rtwdev->hal.rf_path_num; i++)
+			dm_info->cfo_tail[i] = (pkt_stat->cfo_tail[i] * 5) >> 1;
+
+		rtw_phy_parsing_cfo(rtwdev, pkt_stat);
 	}
 }
 EXPORT_SYMBOL(rtw88xxa_query_phy_status);
@@ -2009,6 +2025,21 @@ void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
 }
 EXPORT_SYMBOL(rtw88xxa_phy_pwrtrack);
 
+void rtw88xxa_cfo_track(struct rtw_dev *rtwdev)
+{
+	struct rtw_cfo_track *cfo = &rtwdev->dm_info.cfo_track;
+	bool bt_hold = rtwdev->efuse.btcoex && !rtw_coex_disabled(rtwdev);
+	u8 crystal_cap;
+
+	crystal_cap = rtw_phy_cfo_xcap_track(rtwdev, 0x3F, bt_hold);
+	if (crystal_cap == cfo->crystal_cap)
+		return;
+
+	cfo->crystal_cap = crystal_cap;
+	rtw88xxa_set_crystal_cap(rtwdev, crystal_cap);
+}
+EXPORT_SYMBOL(rtw88xxa_cfo_track);
+
 MODULE_AUTHOR("Realtek Corporation");
 MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821a/8811a/8812a common code");
 MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
index 0aaf7aa4aafdd..09b72d33f62e1 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
@@ -172,5 +172,6 @@ void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
 			   void (*do_iqk)(struct rtw_dev *rtwdev));
 void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
 			     struct rtw_bfee *bfee, bool enable);
+void rtw88xxa_cfo_track(struct rtw_dev *rtwdev);
 
 #endif
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
                   ` (3 preceding siblings ...)
  2026-10-29  5:52 ` [PATCH 4/6] wifi: rtw88: 88xxa: correct carrier-frequency offset Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  2026-10-30 14:57   ` Bitterblue Smith
  2026-10-29  5:52 ` [PATCH 6/6] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits Ho Jie Feng
  5 siblings, 1 reply; 13+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

Implement adaptivity for 88xxa, since they support it in
the hardware.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/main.h     |   1 +
 drivers/net/wireless/realtek/rtw88/rtw8812a.c |  10 ++
 drivers/net/wireless/realtek/rtw88/rtw8821a.c |  10 ++
 drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 123 ++++++++++++++++++
 drivers/net/wireless/realtek/rtw88/rtw88xxa.h |   5 +
 5 files changed, 149 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
index d59f6e323adf0..796fea308fdf7 100644
--- a/drivers/net/wireless/realtek/rtw88/main.h
+++ b/drivers/net/wireless/realtek/rtw88/main.h
@@ -1787,6 +1787,7 @@ struct rtw_dm_info {
 	bool is_bt_iqk_timeout;
 
 	s8 l2h_th_ini;
+	s8 l2h_th_lb;
 	enum rtw_edcca_mode edcca_mode;
 	u8 scan_density;
 };
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index c8008cf43da9d..d93c4776e0303 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -945,6 +945,8 @@ static const struct rtw_chip_ops rtw8812a_ops = {
 	.coex_set_wl_rx_gain	= rtw8821a_coex_cfg_wl_rx_gain,
 	.cfo_init		= rtw_phy_cfo_xcap_init,
 	.cfo_track		= rtw88xxa_cfo_track,
+	.adaptivity_init	= rtw88xxa_adaptivity_init,
+	.adaptivity		= rtw88xxa_adaptivity,
 };
 
 static const struct rtw_page_table page_table_8812a[] = {
@@ -999,6 +1001,11 @@ static const struct rtw_hw_reg rtw8812a_dig[] = {
 	[1] = { .addr = REG_RXIGI_B, .mask = 0x7f },
 };
 
+static const struct rtw_hw_reg_offset rtw8812a_edcca_th[] = {
+	[EDCCA_TH_L2H_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE0}, .offset = 0},
+	[EDCCA_TH_H2L_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE1}, .offset = 0},
+};
+
 static const struct rtw_rfe_def rtw8812a_rfe_defs[] = {
 	[0] = { .phy_pg_tbl	= &rtw8812a_bb_pg_tbl,
 		.txpwr_lmt_tbl	= &rtw8812a_txpwr_lmt_tbl,
@@ -1089,6 +1096,9 @@ const struct rtw_chip_info rtw8812a_hw_spec = {
 	.bfer_su_max_num = 2,
 	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
 	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
+	.edcca_th = rtw8812a_edcca_th,
+	.l2h_th_ini_cs = 10,
+	.l2h_th_ini_ad = -17,
 
 	.coex_para_ver = 0, /* no coex code in 8812au driver */
 	.bt_desired_ver = 0,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
index 17d3677ca463c..f7391f6aa3039 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -891,6 +891,8 @@ static const struct rtw_chip_ops rtw8821a_ops = {
 	.coex_set_wl_rx_gain	= rtw8821a_coex_cfg_wl_rx_gain,
 	.cfo_init		= rtw_phy_cfo_xcap_init,
 	.cfo_track		= rtw88xxa_cfo_track,
+	.adaptivity_init	= rtw88xxa_adaptivity_init,
+	.adaptivity		= rtw88xxa_adaptivity,
 };
 
 static const struct rtw_page_table page_table_8821a[] = {
@@ -944,6 +946,11 @@ static const struct rtw_hw_reg rtw8821a_dig[] = {
 	[0] = { .addr = REG_RXIGI_A, .mask = 0x7f },
 };
 
+static const struct rtw_hw_reg_offset rtw8821a_edcca_th[] = {
+	[EDCCA_TH_L2H_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE0}, .offset = 0},
+	[EDCCA_TH_H2L_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE1}, .offset = 0},
+};
+
 static const struct rtw_rfe_def rtw8821a_rfe_defs[] = {
 	[0] = { .phy_pg_tbl	= &rtw8821a_bb_pg_tbl,
 		.txpwr_lmt_tbl	= &rtw8821a_txpwr_lmt_tbl,
@@ -1193,6 +1200,9 @@ const struct rtw_chip_info rtw8821a_hw_spec = {
 	.bfer_su_max_num = 2,
 	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
 	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
+	.edcca_th = rtw8821a_edcca_th,
+	.l2h_th_ini_cs = 10,
+	.l2h_th_ini_ad = -17,
 
 	.coex_para_ver = 20190509, /* glcoex_ver_date_8821a_1ant */
 	.bt_desired_ver = 0x62, /* But for 2 ant it's 0x5c */
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
index 586bb57a10061..aff62d61f4719 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
@@ -2040,6 +2040,129 @@ void rtw88xxa_cfo_track(struct rtw_dev *rtwdev)
 }
 EXPORT_SYMBOL(rtw88xxa_cfo_track);
 
+#define EDCCA_TH_MAX_88XXA		0x7f
+#define EDCCA_L2H_CAP_88XXA		10
+#define EDCCA_L2H_INI_NORMAL_88XXA	20
+#define EDCCA_DBG_PORT_88XXA		0x209
+#define EDCCA_DC_BACKOFF_88XXA		2
+#define EDCCA_LB_START_88XXA		(EDCCA_L2H_H2L_DIFF - 30)
+#define EDCCA_LB_RETRY_88XXA		(EDCCA_LB_START_88XXA + 18)
+
+static void rtw88xxa_set_rf_mode_rx(struct rtw_dev *rtwdev, u32 data0,
+				    u32 data1)
+{
+	u8 path;
+
+	for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
+		rtw_write_rf(rtwdev, path, RF_LUTWE, BIT(19), 1);
+		rtw_write_rf(rtwdev, path, RF_MODE_TABLE_ADDR, RFREG_MASK,
+			     0x18000);
+		rtw_write_rf(rtwdev, path, RF_MODE_TABLE_DATA0, RFREG_MASK,
+			     data0);
+		rtw_write_rf(rtwdev, path, RF_MODE_TABLE_DATA1, RFREG_MASK,
+			     data1);
+		rtw_write_rf(rtwdev, path, RF_LUTWE, BIT(19), 0);
+	}
+}
+
+static void rtw88xxa_set_lna(struct rtw_dev *rtwdev, bool enable)
+{
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
+		rtw88xxa_set_rf_mode_rx(rtwdev, 0x3f7ff,
+					enable ? 0xc26bf : 0xc22bf);
+	else
+		rtw88xxa_set_rf_mode_rx(rtwdev, 0x0002f,
+					enable ? 0xfb0bb : 0xfb09b);
+}
+
+static u8 rtw88xxa_edcca_count(struct rtw_dev *rtwdev)
+{
+	u8 cnt = 0;
+	int i;
+
+	rtw_write32(rtwdev, REG_DBGSEL, 0);
+	for (i = 0; i < 3 &&
+	     rtw_read32(rtwdev, REG_DBGRPT) & BIT_DBG_CCA_BUSY; i++)
+		usleep_range(3000, 3500);
+
+	rtw_write32(rtwdev, REG_DBGSEL, EDCCA_DBG_PORT_88XXA);
+	for (i = 0; i < 20; i++)
+		if (rtw_read32(rtwdev, REG_DBGRPT) & BIT_DBG_EDCCA_BUSY)
+			cnt++;
+
+	return cnt;
+}
+
+static s8 rtw88xxa_edcca_search_floor(struct rtw_dev *rtwdev)
+{
+	s8 l2h = EDCCA_LB_START_88XXA;
+
+	rtw_phy_set_edcca_th(rtwdev, l2h, l2h - EDCCA_L2H_H2L_DIFF);
+	msleep(30);
+
+	while (l2h < EDCCA_L2H_CAP_88XXA && rtw88xxa_edcca_count(rtwdev) > 1) {
+		l2h++;
+		rtw_phy_set_edcca_th(rtwdev, l2h, l2h - EDCCA_L2H_H2L_DIFF);
+	}
+
+	return l2h;
+}
+
+void rtw88xxa_adaptivity_init(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u32 dbgsel = rtw_read32(rtwdev, REG_DBGSEL);
+	s8 l2h;
+
+	rtw_write32_clr(rtwdev, REG_TX_PTCL_CTRL, BIT_DIS_EDCCA);
+
+	rtw88xxa_set_lna(rtwdev, false);
+	l2h = rtw88xxa_edcca_search_floor(rtwdev);
+	if (l2h >= EDCCA_LB_RETRY_88XXA)
+		l2h = rtw88xxa_edcca_search_floor(rtwdev);
+	rtw88xxa_set_lna(rtwdev, true);
+	rtw_write32(rtwdev, REG_DBGSEL, dbgsel);
+
+	dm_info->l2h_th_lb = l2h + EDCCA_DC_BACKOFF_88XXA;
+	rtw_phy_set_edcca_th(rtwdev, EDCCA_TH_MAX_88XXA, EDCCA_TH_MAX_88XXA);
+
+	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "EDCCA L2H floor %d\n",
+		dm_info->l2h_th_lb);
+}
+EXPORT_SYMBOL(rtw88xxa_adaptivity_init);
+
+void rtw88xxa_adaptivity(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	int igi_diff = EDCCA_IGI_BASE - dm_info->igi_history[0];
+	s8 l2h_lb = dm_info->l2h_th_lb;
+	s8 l2h, h2l;
+
+	/* toggle the ADC mask to work around an EDCCA hang */
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A) {
+		rtw_write32_set(rtwdev, REG_FPGA0_RFMOD, BIT(10));
+		rtw_write32_clr(rtwdev, REG_FPGA0_RFMOD, BIT(10));
+	}
+
+	if (dm_info->edcca_mode == RTW_EDCCA_ADAPTIVITY) {
+		l2h = min(dm_info->l2h_th_ini + igi_diff, EDCCA_L2H_CAP_88XXA);
+		h2l = l2h - EDCCA_L2H_H2L_DIFF;
+	} else {
+		l2h = EDCCA_L2H_INI_NORMAL_88XXA + igi_diff;
+		h2l = l2h - EDCCA_L2H_H2L_DIFF_NORMAL;
+	}
+
+	l2h = max(l2h, l2h_lb);
+	h2l = max_t(s8, h2l, l2h_lb - EDCCA_L2H_H2L_DIFF);
+
+	rtw_phy_set_edcca_th(rtwdev, l2h, h2l);
+
+	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY,
+		"EDCCA mode %d, IGI 0x%x, L2H %d, H2L %d\n",
+		dm_info->edcca_mode, dm_info->igi_history[0], l2h, h2l);
+}
+EXPORT_SYMBOL(rtw88xxa_adaptivity);
+
 MODULE_AUTHOR("Realtek Corporation");
 MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821a/8811a/8812a common code");
 MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
index 09b72d33f62e1..b8719c278df73 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
@@ -139,6 +139,9 @@ struct rtw_jaguar_phy_status_rpt {
 
 #define RF18_BW_MASK		(BIT(11) | BIT(10))
 
+#define BIT_DBG_CCA_BUSY	BIT(3)
+#define BIT_DBG_EDCCA_BUSY	BIT(29)
+
 void rtw88xxa_efuse_grant(struct rtw_dev *rtwdev, bool on);
 int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map);
 void rtw88xxa_power_off(struct rtw_dev *rtwdev,
@@ -173,5 +176,7 @@ void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
 void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
 			     struct rtw_bfee *bfee, bool enable);
 void rtw88xxa_cfo_track(struct rtw_dev *rtwdev);
+void rtw88xxa_adaptivity_init(struct rtw_dev *rtwdev);
+void rtw88xxa_adaptivity(struct rtw_dev *rtwdev);
 
 #endif
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 6/6] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits
  2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
                   ` (4 preceding siblings ...)
  2026-10-29  5:52 ` [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain Ho Jie Feng
@ 2026-10-29  5:52 ` Ho Jie Feng
  5 siblings, 0 replies; 13+ messages in thread
From: Ho Jie Feng @ 2026-10-29  5:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, Ho Jie Feng, linux-kernel

Honor the peer-advertised receive limit for transmitted A-MPDUs.

Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/rtw8812a.c | 9 ++++++++-
 drivers/net/wireless/realtek/rtw88/rtw8821a.c | 9 ++++++++-
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index d93c4776e0303..a3b5fae5608e0 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -78,6 +78,13 @@ static void rtw8812a_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
 {
 }
 
+static void rtw8812a_set_ampdu_factor(struct rtw_dev *rtwdev, u8 factor)
+{
+	factor = min_t(u8, factor, IEEE80211_VHT_MAX_AMPDU_128K);
+
+	rtw_write32(rtwdev, REG_AMPDU_MAX_LENGTH, (8192 << factor) - 1);
+}
+
 static void rtw8812a_do_lck(struct rtw_dev *rtwdev)
 {
 	u32 cont_tx, lc_cal, i;
@@ -926,7 +933,7 @@ static const struct rtw_chip_ops rtw8812a_ops = {
 	.set_tx_power_index	= rtw88xxa_set_tx_power_index,
 	.cfg_ldo25		= rtw8812a_cfg_ldo25,
 	.efuse_grant		= rtw88xxa_efuse_grant,
-	.set_ampdu_factor	= NULL,
+	.set_ampdu_factor	= rtw8812a_set_ampdu_factor,
 	.false_alarm_statistics	= rtw88xxa_false_alarm_statistics,
 	.phy_calibration	= rtw8812a_phy_calibration,
 	.cck_pd_set		= rtw_phy_cck_pd_set,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
index f7391f6aa3039..f597edec290f6 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -49,6 +49,13 @@ static void rtw8821a_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
 {
 }
 
+static void rtw8821a_set_ampdu_factor(struct rtw_dev *rtwdev, u8 factor)
+{
+	factor = min_t(u8, factor, IEEE80211_HT_MAX_AMPDU_64K);
+
+	rtw_write32(rtwdev, REG_AMPDU_MAX_LENGTH, (8192 << factor) - 1);
+}
+
 #define CAL_NUM_8821A 3
 #define MACBB_REG_NUM_8821A 8
 #define AFE_REG_NUM_8821A 4
@@ -872,7 +879,7 @@ static const struct rtw_chip_ops rtw8821a_ops = {
 	.set_tx_power_index	= rtw88xxa_set_tx_power_index,
 	.cfg_ldo25		= rtw8821a_cfg_ldo25,
 	.efuse_grant		= rtw88xxa_efuse_grant,
-	.set_ampdu_factor	= NULL,
+	.set_ampdu_factor	= rtw8821a_set_ampdu_factor,
 	.false_alarm_statistics	= rtw88xxa_false_alarm_statistics,
 	.phy_calibration	= rtw8821a_phy_calibration,
 	.cck_pd_set		= rtw_phy_cck_pd_set,
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
  2026-10-29  5:52 ` [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
@ 2026-10-29 12:33   ` Bitterblue Smith
  2026-10-29 14:13     ` Ho Jie Feng
  0 siblings, 1 reply; 13+ messages in thread
From: Bitterblue Smith @ 2026-10-29 12:33 UTC (permalink / raw)
  To: linux-wireless

On 29/10/2026 07:52, Ho Jie Feng wrote:
> Implement beamformee support for 88xxa since they have
> support for it.
> 
> Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
> ---
>  drivers/net/wireless/realtek/rtw88/bf.h       |  2 +
>  drivers/net/wireless/realtek/rtw88/main.c     | 12 ++--
>  drivers/net/wireless/realtek/rtw88/reg.h      |  1 +
>  drivers/net/wireless/realtek/rtw88/rtw8812a.c |  3 +-
>  drivers/net/wireless/realtek/rtw88/rtw8814a.c | 36 ++++++++++-
>  drivers/net/wireless/realtek/rtw88/rtw8821a.c |  3 +-
>  drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 62 +++++++++++++++++++
>  drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  2 +
>  8 files changed, 112 insertions(+), 9 deletions(-)
> 

Is it really this simple? The vendor driver has a lot more
beamforming code than this.

> diff --git a/drivers/net/wireless/realtek/rtw88/bf.h b/drivers/net/wireless/realtek/rtw88/bf.h
> index a5d3010e6be6e..0aa78fb6a7e64 100644
> --- a/drivers/net/wireless/realtek/rtw88/bf.h
> +++ b/drivers/net/wireless/realtek/rtw88/bf.h
> @@ -12,6 +12,8 @@
>  #define REG_ASSOCIATED_BFMER0_INFO	0x06E4
>  #define REG_ASSOCIATED_BFMER1_INFO	0x06EC
>  #define REG_TX_CSI_RPT_PARAM_BW20	0x06F4
> +#define REG_TX_CSI_RPT_PARAM_BW40	0x06F8
> +#define REG_TX_CSI_RPT_PARAM_BW80	0x06FC
>  #define REG_SND_PTCL_CTRL		0x0718
>  #define BIT_DIS_CHK_VHTSIGB_CRC		BIT(6)
>  #define BIT_DIS_CHK_VHTSIGA_CRC		BIT(5)
> diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
> index f7d7b66844862..0a1b5575b7299 100644
> --- a/drivers/net/wireless/realtek/rtw88/main.c
> +++ b/drivers/net/wireless/realtek/rtw88/main.c
> @@ -1605,6 +1605,7 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
>  static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
>  			     struct ieee80211_sta_vht_cap *vht_cap)
>  {
> +	const struct rtw_chip_info *chip = rtwdev->chip;
>  	struct rtw_efuse *efuse = &rtwdev->efuse;
>  	u16 mcs_map = 0;
>  	__le16 highest;
> @@ -1623,10 +1624,13 @@ static void rtw_init_vht_cap(struct rtw_dev *rtwdev,
>  		       0;
>  	if (rtwdev->hal.rf_path_num > 1)
>  		vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
> -	vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
> -			IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
> -	vht_cap->cap |= (rtwdev->hal.bfee_sts_cap <<
> -			IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
> +	if (rtw_bf_support && chip->bfer_su_max_num) {
> +		vht_cap->cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
> +		vht_cap->cap |= rtwdev->hal.bfee_sts_cap <<
> +				IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
> +	}
> +	if (rtw_bf_support && chip->bfer_mu_max_num)
> +		vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
>  
>  	if (rtw_chip_has_rx_ldpc(rtwdev))
>  		vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;

This could be a separate patch, since it doesn't look related to 88xxa.

> diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h
> index afcdb66fdb9a1..f8ec1e2931f63 100644
> --- a/drivers/net/wireless/realtek/rtw88/reg.h
> +++ b/drivers/net/wireless/realtek/rtw88/reg.h
> @@ -671,6 +671,7 @@
>  #define REG_IQK_COM96		0x0984
>  
>  #define REG_FAS			0x09a4
> +#define REG_CSI_CONTENT_VALUE	0x09b4
>  #define REG_RXSB		0x0a00
>  #define BIT_RXSB_ANA_DIV	BIT(15)
>  #define REG_CCK_RX		0x0a04
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
> index 55f1d3d3240be..5d58dff4fcee7 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
> @@ -931,7 +931,7 @@ static const struct rtw_chip_ops rtw8812a_ops = {
>  	.phy_calibration	= rtw8812a_phy_calibration,
>  	.cck_pd_set		= rtw_phy_cck_pd_set,
>  	.pwr_track		= rtw8812a_pwr_track,
> -	.config_bfee		= NULL,
> +	.config_bfee		= rtw88xxa_bf_config_bfee,
>  	.set_gid_table		= NULL,
>  	.cfg_csi_rate		= NULL,
>  	.led_set		= rtw8812a_led_set,
> @@ -1084,6 +1084,7 @@ const struct rtw_chip_info rtw8812a_hw_spec = {
>  	.old_datarate_fb_limit = true,
>  	.usb_tx_agg_desc_num = 1,
>  	.iqk_threshold = 8,
> +	.bfer_su_max_num = 2,
>  	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
>  	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
>  
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
> index 5bb55606f14a1..2db05a83766d6 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8814a.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
> @@ -4,6 +4,7 @@
>  
>  #include <linux/usb.h>
>  #include "main.h"
> +#include "bf.h"
>  #include "coex.h"
>  #include "tx.h"
>  #include "phy.h"
> @@ -115,10 +116,13 @@ static void rtw8814a_init_hwcap(struct rtw_dev *rtwdev)
>  			   BIT(RTW_CHANNEL_WIDTH_80);
>  	efuse->hw_cap.ptcl = EFUSE_HW_CAP_PTCL_VHT;
>  
> -	if (hal->rf_type == RF_3T3R)
> +	if (hal->rf_type == RF_3T3R) {
>  		efuse->hw_cap.nss = 3;
> -	else
> +		hal->bfee_sts_cap = 2;
> +	} else {
>  		efuse->hw_cap.nss = 2;
> +		hal->bfee_sts_cap = 1;
> +	}
>  
>  	rtw_dbg(rtwdev, RTW_DBG_EFUSE,
>  		"hw cap: hci=0x%02x, bw=0x%02x, ptcl=0x%02x, ant_num=%d, nss=%d\n",
> @@ -2039,6 +2043,31 @@ static void rtw8814a_led_set(struct led_classdev *led,
>  	rtw_write32(rtwdev, REG_GPIO_PIN_CTRL_2, led_gpio_cfg);
>  }
>  
> +#define RTW_NDP_RX_STANDBY_TIME_8814A	0x40
> +#define RTW_SND_CTRL_SOUNDING_8814A	0xDB
> +
> +static void rtw8814a_bf_enable_bfee_su(struct rtw_dev *rtwdev,
> +				       struct rtw_vif *vif,
> +				       struct rtw_bfee *bfee)
> +{
> +	rtw_bf_enable_bfee_su(rtwdev, vif, bfee);
> +
> +	rtw_write16(rtwdev, REG_SND_PTCL_CTRL + 1, 0x0202);
> +	rtw_write8(rtwdev, REG_SND_PTCL_CTRL, RTW_SND_CTRL_SOUNDING_8814A);
> +	rtw_write8(rtwdev, REG_SND_PTCL_CTRL + 3,
> +		   RTW_NDP_RX_STANDBY_TIME_8814A);
> +}
> +
> +static void rtw8814a_bf_config_bfee(struct rtw_dev *rtwdev,
> +				    struct rtw_vif *vif,
> +				    struct rtw_bfee *bfee, bool enable)
> +{
> +	if (enable)
> +		rtw8814a_bf_enable_bfee_su(rtwdev, vif, bfee);
> +	else
> +		rtw_bf_remove_bfee_su(rtwdev, bfee);
> +}
> +
>  static void rtw8814a_fill_txdesc_checksum(struct rtw_dev *rtwdev,
>  					  struct rtw_tx_pkt_info *pkt_info,
>  					  u8 *txdesc)
> @@ -2068,7 +2097,7 @@ static const struct rtw_chip_ops rtw8814a_ops = {
>  	.phy_calibration	= rtw8814a_phy_calibration,
>  	.cck_pd_set		= rtw8814a_phy_cck_pd_set,
>  	.pwr_track		= rtw8814a_pwr_track,
> -	.config_bfee		= NULL,
> +	.config_bfee		= rtw8814a_bf_config_bfee,
>  	.set_gid_table		= NULL,
>  	.cfg_csi_rate		= NULL,
>  	.led_set		= rtw8814a_led_set,
> @@ -2231,6 +2260,7 @@ const struct rtw_chip_info rtw8814a_hw_spec = {
>  	.rfe_defs = rtw8814a_rfe_defs,
>  	.rfe_defs_size = ARRAY_SIZE(rtw8814a_rfe_defs),
>  	.iqk_threshold = 8,
> +	.bfer_su_max_num = 2,
>  	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
>  
>  	.coex_para_ver = 0,
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> index 6096d3b8e41be..6d106ae70d914 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> @@ -877,7 +877,7 @@ static const struct rtw_chip_ops rtw8821a_ops = {
>  	.phy_calibration	= rtw8821a_phy_calibration,
>  	.cck_pd_set		= rtw_phy_cck_pd_set,
>  	.pwr_track		= rtw8821a_pwr_track,
> -	.config_bfee		= NULL,
> +	.config_bfee		= rtw88xxa_bf_config_bfee,
>  	.set_gid_table		= NULL,
>  	.cfg_csi_rate		= NULL,
>  	.led_set		= rtw8821a_led_set,
> @@ -1188,6 +1188,7 @@ const struct rtw_chip_info rtw8821a_hw_spec = {
>  	.old_datarate_fb_limit = true,
>  	.usb_tx_agg_desc_num = 6,
>  	.iqk_threshold = 8,
> +	.bfer_su_max_num = 2,
>  	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
>  	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
>  
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
> index c298fa0fb2a6b..d3dd0ebfb4024 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
> @@ -138,6 +138,7 @@ static void rtw88xxa_read_usb_type(struct rtw_dev *rtwdev)
>  		efuse->hw_cap.nss = 1;
>  	else
>  		efuse->hw_cap.nss = 2;
> +	hal->bfee_sts_cap = 1;
>  
>  	if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
>  		goto print_hw_cap;
> @@ -197,6 +198,67 @@ static void rtw88xxa_read_usb_type(struct rtw_dev *rtwdev)
>  		efuse->hw_cap.ant_num, efuse->hw_cap.nss);
>  }
>  
> +#define RTW_NDP_RX_STANDBY_TIME_88XXA	0x50
> +#define RTW_SND_CTRL_REMOVE_88XXA	0xC8
> +#define RTW_SND_CTRL_SOUNDING_88XXA	0xCB
> +
> +void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
> +			     struct rtw_bfee *bfee, bool enable)
> +{
> +	static const u32 csi_rpt_regs[] = {
> +		REG_TX_CSI_RPT_PARAM_BW20,
> +		REG_TX_CSI_RPT_PARAM_BW40,
> +		REG_TX_CSI_RPT_PARAM_BW80,
> +	};
> +	struct rtw_bf_info *bf_info = &rtwdev->bf_info;
> +	u8 slot = bfee->su_reg_index;
> +	u32 addr_bfer_info;
> +	u32 csi_param;
> +	int i;
> +
> +	if (bfee->role != RTW_BFEE_SU || slot >= 2)
> +		return;
> +
> +	addr_bfer_info = slot ? REG_ASSOCIATED_BFMER1_INFO :
> +				      REG_ASSOCIATED_BFMER0_INFO;
> +
> +	rtw_dbg(rtwdev, RTW_DBG_BF, "chip %d SU beamformee %s slot %u\n",
> +		rtwdev->chip->id, enable ? "enable" : "disable", slot);
> +
> +	if (!enable) {
> +		rtw_write32(rtwdev, addr_bfer_info, 0);
> +		rtw_write16(rtwdev, addr_bfer_info + 4, 0);
> +
> +		clear_bit(slot, bf_info->bfer_su_reg_maping);
> +		bfee->su_reg_index = 0xff;
> +
> +		if (!bf_info->bfer_su_cnt)
> +			rtw_write8(rtwdev, REG_SND_PTCL_CTRL,
> +				   RTW_SND_CTRL_REMOVE_88XXA);
> +		return;
> +	}
> +
> +	if (hweight8(rtwdev->hal.antenna_rx) == 2) {
> +		rtw_write32(rtwdev, REG_CSI_CONTENT_VALUE, 0);
> +		csi_param = 0x01090109;
> +	} else {
> +		rtw_write32(rtwdev, REG_CSI_CONTENT_VALUE, 0x01081008);
> +		csi_param = 0x01080108;
> +	}
> +
> +	rtw_write8(rtwdev, REG_SND_PTCL_CTRL,
> +		   RTW_SND_CTRL_SOUNDING_88XXA);
> +	for (i = 0; i < ETH_ALEN; i++)
> +		rtw_write8(rtwdev, addr_bfer_info + i, bfee->mac_addr[i]);
> +
> +	for (i = 0; i < ARRAY_SIZE(csi_rpt_regs); i++)
> +		rtw_write32(rtwdev, csi_rpt_regs[i], csi_param);
> +
> +	rtw_write8(rtwdev, REG_SND_PTCL_CTRL + 3,
> +		   RTW_NDP_RX_STANDBY_TIME_88XXA);
> +}
> +EXPORT_SYMBOL(rtw88xxa_bf_config_bfee);
> +
>  int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
>  {
>  	const struct rtw_chip_info *chip = rtwdev->chip;
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
> index d9c8db58909ca..0aaf7aa4aafdd 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
> +++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
> @@ -170,5 +170,7 @@ bool rtw88xxa_iqk_finish(int average, int threshold,
>  void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
>  			   void (*do_lck)(struct rtw_dev *rtwdev),
>  			   void (*do_iqk)(struct rtw_dev *rtwdev));
> +void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
> +			     struct rtw_bfee *bfee, bool enable);
>  
>  #endif


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
  2026-10-29 12:33   ` Bitterblue Smith
@ 2026-10-29 14:13     ` Ho Jie Feng
  2026-10-29 21:06       ` Bitterblue Smith
  0 siblings, 1 reply; 13+ messages in thread
From: Ho Jie Feng @ 2026-10-29 14:13 UTC (permalink / raw)
  To: linux-wireless, Bitterblue Smith; +Cc: Ping-Ke Shih

On 29/10/2026 14:33, Bitterblue Smith wrote:
> On 29/10/2026 07:52, Ho Jie Feng wrote:
>> Implement beamformee support for 88xxa since they have
>> support for it.
>>
>> Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
>> ---
>>  drivers/net/wireless/realtek/rtw88/bf.h       |  2 +
>>  drivers/net/wireless/realtek/rtw88/main.c     | 12 ++--
>>  drivers/net/wireless/realtek/rtw88/reg.h      |  1 +
>>  drivers/net/wireless/realtek/rtw88/rtw8812a.c |  3 +-
>>  drivers/net/wireless/realtek/rtw88/rtw8814a.c | 36 ++++++++++-
>>  drivers/net/wireless/realtek/rtw88/rtw8821a.c |  3 +-
>>  drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 62 +++++++++++++++++++
>>  drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  2 +
>>  8 files changed, 112 insertions(+), 9 deletions(-)
>>
>
> Is it really this simple? The vendor driver has a lot more
> beamforming code than this.

Yes, this is the beamformee code, not the beamformer,
which has way more code than this. The RTL8812AU is acting as
the target for the AP to beamform to for this one.

I have the beamformer code, which the RTL8812AU
beamforms, but I dropped it since it reduces throughput.

I can put the patch out if you wish to see it, but
won't be enabling for 88xxa.

Reference:
https://github.com/aircrack-ng/rtl8812au/blob/v5.6.4.2/hal/phydm/txbf/haltxbfjaguar.c#L229

Vendor driver and rtw88 can be sometimes confusing for
the bfee vs bfer distinction.

>> ...
>>  	if (rtwdev->hal.rf_path_num > 1)
>>  		vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
>> -	vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
>> -			IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
>> -	vht_cap->cap |= (rtwdev->hal.bfee_sts_cap <<
>> -			IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
>> +	if (rtw_bf_support && chip->bfer_su_max_num) {
>> +		vht_cap->cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
>> +		vht_cap->cap |= rtwdev->hal.bfee_sts_cap <<
>> +				IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
>> +	}
>> +	if (rtw_bf_support && chip->bfer_mu_max_num)
>> +		vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
>>  
>>  	if (rtw_chip_has_rx_ldpc(rtwdev))
>>  		vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
>
> This could be a separate patch, since it doesn't look related to 88xxa.

I can put this in v2.

>> ...

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
  2026-10-29 14:13     ` Ho Jie Feng
@ 2026-10-29 21:06       ` Bitterblue Smith
  0 siblings, 0 replies; 13+ messages in thread
From: Bitterblue Smith @ 2026-10-29 21:06 UTC (permalink / raw)
  To: Ho Jie Feng, linux-wireless; +Cc: Ping-Ke Shih

On 29/10/2026 16:13, Ho Jie Feng wrote:
> On 29/10/2026 14:33, Bitterblue Smith wrote:
>> On 29/10/2026 07:52, Ho Jie Feng wrote:
>>> Implement beamformee support for 88xxa since they have
>>> support for it.
>>>
>>> Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
>>> ---
>>>  drivers/net/wireless/realtek/rtw88/bf.h       |  2 +
>>>  drivers/net/wireless/realtek/rtw88/main.c     | 12 ++--
>>>  drivers/net/wireless/realtek/rtw88/reg.h      |  1 +
>>>  drivers/net/wireless/realtek/rtw88/rtw8812a.c |  3 +-
>>>  drivers/net/wireless/realtek/rtw88/rtw8814a.c | 36 ++++++++++-
>>>  drivers/net/wireless/realtek/rtw88/rtw8821a.c |  3 +-
>>>  drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 62 +++++++++++++++++++
>>>  drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  2 +
>>>  8 files changed, 112 insertions(+), 9 deletions(-)
>>>
>>
>> Is it really this simple? The vendor driver has a lot more
>> beamforming code than this.
> 
> Yes, this is the beamformee code, not the beamformer,
> which has way more code than this. The RTL8812AU is acting as
> the target for the AP to beamform to for this one.
> 

Oh, I see. I didn't look very closely at the vendor code.

> I have the beamformer code, which the RTL8812AU
> beamforms, but I dropped it since it reduces throughput.
> 
> I can put the patch out if you wish to see it, but
> won't be enabling for 88xxa.
> 
> Reference:
> https://github.com/aircrack-ng/rtl8812au/blob/v5.6.4.2/hal/phydm/txbf/haltxbfjaguar.c#L229
> 

By the way, there is a newer version:
https://github.com/morrownr/8812au-20210820

I don't know if there are any differences in the beamforming code.

> Vendor driver and rtw88 can be sometimes confusing for
> the bfee vs bfer distinction.
> 
>>> ...
>>>  	if (rtwdev->hal.rf_path_num > 1)
>>>  		vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
>>> -	vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
>>> -			IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
>>> -	vht_cap->cap |= (rtwdev->hal.bfee_sts_cap <<
>>> -			IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
>>> +	if (rtw_bf_support && chip->bfer_su_max_num) {
>>> +		vht_cap->cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
>>> +		vht_cap->cap |= rtwdev->hal.bfee_sts_cap <<
>>> +				IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
>>> +	}
>>> +	if (rtw_bf_support && chip->bfer_mu_max_num)
>>> +		vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
>>>  
>>>  	if (rtw_chip_has_rx_ldpc(rtwdev))
>>>  		vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
>>
>> This could be a separate patch, since it doesn't look related to 88xxa.
> 
> I can put this in v2.
> 
>>> ...


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain
  2026-10-29  5:52 ` [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain Ho Jie Feng
@ 2026-10-30 14:57   ` Bitterblue Smith
  2026-10-30 18:16     ` Luka Gejak
  0 siblings, 1 reply; 13+ messages in thread
From: Bitterblue Smith @ 2026-10-30 14:57 UTC (permalink / raw)
  To: linux-wireless

On 29/10/2026 07:52, Ho Jie Feng wrote:
> Implement adaptivity for 88xxa, since they support it in
> the hardware.
> 
> Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>

This patch makes scans while connected to a network much slower:

[2026-10-30 16:28:31.822427]: wlp3s0f3u2 (phy #5): scan started
[2026-10-30 16:29:00.622378]: wlp3s0f3u2 (phy #5): scan finished: 2412 2417 2422 2427 2432 2437 2442 2447 2452 
								  2457 2462 2467 2472 5180 5200 5220 5240 5260 
								  5280 5300 5320 5500 5520 5540 5560 5580 5600 
								  5620 5640 5660 5680 5700 5720 5745 5765 5785 
								  5805 5825, ""

During the scans I see a lot of "timed out to flush queue 1" (and 3)
in dmesg. You need debug_mask=0x80000000 to see these.

Pinging the router during the scan:

PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=157 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=194 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=107 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=139 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=154 ms
64 bytes from 192.168.1.1: icmp_seq=6 ttl=64 time=186 ms
64 bytes from 192.168.1.1: icmp_seq=7 ttl=64 time=12639 ms
64 bytes from 192.168.1.1: icmp_seq=8 ttl=64 time=11612 ms
64 bytes from 192.168.1.1: icmp_seq=9 ttl=64 time=10588 ms
64 bytes from 192.168.1.1: icmp_seq=12 ttl=64 time=7822 ms
64 bytes from 192.168.1.1: icmp_seq=14 ttl=64 time=21134 ms
64 bytes from 192.168.1.1: icmp_seq=15 ttl=64 time=20110 ms
64 bytes from 192.168.1.1: icmp_seq=16 ttl=64 time=19086 ms
64 bytes from 192.168.1.1: icmp_seq=17 ttl=64 time=18062 ms
64 bytes from 192.168.1.1: icmp_seq=18 ttl=64 time=17038 ms
64 bytes from 192.168.1.1: icmp_seq=19 ttl=64 time=16014 ms
64 bytes from 192.168.1.1: icmp_seq=20 ttl=64 time=15014 ms
64 bytes from 192.168.1.1: icmp_seq=21 ttl=64 time=13967 ms
64 bytes from 192.168.1.1: icmp_seq=22 ttl=64 time=12942 ms
64 bytes from 192.168.1.1: icmp_seq=23 ttl=64 time=11918 ms
64 bytes from 192.168.1.1: icmp_seq=24 ttl=64 time=10895 ms
64 bytes from 192.168.1.1: icmp_seq=25 ttl=64 time=9871 ms
64 bytes from 192.168.1.1: icmp_seq=26 ttl=64 time=8847 ms
64 bytes from 192.168.1.1: icmp_seq=27 ttl=64 time=7823 ms
64 bytes from 192.168.1.1: icmp_seq=35 ttl=64 time=1.61 ms
64 bytes from 192.168.1.1: icmp_seq=36 ttl=64 time=2.10 ms
64 bytes from 192.168.1.1: icmp_seq=37 ttl=64 time=1.87 ms
^C
--- 192.168.1.1 ping statistics ---
37 packets transmitted, 27 received, 27.027% packet loss, time 36658ms
rtt min/avg/max/mdev = 1.611/9123.175/21133.788/7187.269 ms, pipe 21

Without this patch:

[2026-10-30 16:34:18.354110]: wlp3s0f3u2 (phy #7): scan started
[2026-10-30 16:34:31.141463]: wlp3s0f3u2 (phy #7): scan finished: 2412 2417 2422 2427 2432 2437 2442 2447 2452 
								  2457 2462 2467 2472 5180 5200 5220 5240 5260 
								  5280 5300 5320 5500 5520 5540 5560 5580 5600 
								  5620 5640 5660 5680 5700 5720 5745 5765 5785 
								  5805 5825, ""

PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=317 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=134 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=157 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=4.41 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=202 ms
64 bytes from 192.168.1.1: icmp_seq=6 ttl=64 time=122 ms
64 bytes from 192.168.1.1: icmp_seq=7 ttl=64 time=145 ms
64 bytes from 192.168.1.1: icmp_seq=8 ttl=64 time=168 ms
64 bytes from 192.168.1.1: icmp_seq=9 ttl=64 time=190 ms
64 bytes from 192.168.1.1: icmp_seq=10 ttl=64 time=106 ms
64 bytes from 192.168.1.1: icmp_seq=11 ttl=64 time=36.8 ms
64 bytes from 192.168.1.1: icmp_seq=12 ttl=64 time=2.45 ms
64 bytes from 192.168.1.1: icmp_seq=13 ttl=64 time=3.84 ms
64 bytes from 192.168.1.1: icmp_seq=14 ttl=64 time=1.78 ms
64 bytes from 192.168.1.1: icmp_seq=15 ttl=64 time=6.42 ms
64 bytes from 192.168.1.1: icmp_seq=16 ttl=64 time=148 ms
64 bytes from 192.168.1.1: icmp_seq=17 ttl=64 time=2.10 ms
64 bytes from 192.168.1.1: icmp_seq=18 ttl=64 time=27.8 ms
64 bytes from 192.168.1.1: icmp_seq=19 ttl=64 time=103 ms
64 bytes from 192.168.1.1: icmp_seq=20 ttl=64 time=2.71 ms
64 bytes from 192.168.1.1: icmp_seq=21 ttl=64 time=30.9 ms
64 bytes from 192.168.1.1: icmp_seq=22 ttl=64 time=1.52 ms
64 bytes from 192.168.1.1: icmp_seq=23 ttl=64 time=2.06 ms
64 bytes from 192.168.1.1: icmp_seq=24 ttl=64 time=5.16 ms
64 bytes from 192.168.1.1: icmp_seq=25 ttl=64 time=138 ms
64 bytes from 192.168.1.1: icmp_seq=26 ttl=64 time=162 ms
64 bytes from 192.168.1.1: icmp_seq=27 ttl=64 time=79.3 ms
64 bytes from 192.168.1.1: icmp_seq=28 ttl=64 time=204 ms
^C
--- 192.168.1.1 ping statistics ---
28 packets transmitted, 28 received, 0% packet loss, time 27043ms
rtt min/avg/max/mdev = 1.516/89.407/316.521/84.581 ms

(100-300 ms is normal for RTL8812A when power saving is enabled.)

My network is on channel 13, with 40 MHz width, WPA2-PSK.

> ---
>  drivers/net/wireless/realtek/rtw88/main.h     |   1 +
>  drivers/net/wireless/realtek/rtw88/rtw8812a.c |  10 ++
>  drivers/net/wireless/realtek/rtw88/rtw8821a.c |  10 ++
>  drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 123 ++++++++++++++++++
>  drivers/net/wireless/realtek/rtw88/rtw88xxa.h |   5 +
>  5 files changed, 149 insertions(+)
> 
> diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
> index d59f6e323adf0..796fea308fdf7 100644
> --- a/drivers/net/wireless/realtek/rtw88/main.h
> +++ b/drivers/net/wireless/realtek/rtw88/main.h
> @@ -1787,6 +1787,7 @@ struct rtw_dm_info {
>  	bool is_bt_iqk_timeout;
>  
>  	s8 l2h_th_ini;
> +	s8 l2h_th_lb;
>  	enum rtw_edcca_mode edcca_mode;
>  	u8 scan_density;
>  };
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
> index c8008cf43da9d..d93c4776e0303 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
> @@ -945,6 +945,8 @@ static const struct rtw_chip_ops rtw8812a_ops = {
>  	.coex_set_wl_rx_gain	= rtw8821a_coex_cfg_wl_rx_gain,
>  	.cfo_init		= rtw_phy_cfo_xcap_init,
>  	.cfo_track		= rtw88xxa_cfo_track,
> +	.adaptivity_init	= rtw88xxa_adaptivity_init,
> +	.adaptivity		= rtw88xxa_adaptivity,
>  };
>  
>  static const struct rtw_page_table page_table_8812a[] = {
> @@ -999,6 +1001,11 @@ static const struct rtw_hw_reg rtw8812a_dig[] = {
>  	[1] = { .addr = REG_RXIGI_B, .mask = 0x7f },
>  };
>  
> +static const struct rtw_hw_reg_offset rtw8812a_edcca_th[] = {
> +	[EDCCA_TH_L2H_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE0}, .offset = 0},
> +	[EDCCA_TH_H2L_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE1}, .offset = 0},
> +};
> +
>  static const struct rtw_rfe_def rtw8812a_rfe_defs[] = {
>  	[0] = { .phy_pg_tbl	= &rtw8812a_bb_pg_tbl,
>  		.txpwr_lmt_tbl	= &rtw8812a_txpwr_lmt_tbl,
> @@ -1089,6 +1096,9 @@ const struct rtw_chip_info rtw8812a_hw_spec = {
>  	.bfer_su_max_num = 2,
>  	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
>  	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
> +	.edcca_th = rtw8812a_edcca_th,
> +	.l2h_th_ini_cs = 10,
> +	.l2h_th_ini_ad = -17,
>  
>  	.coex_para_ver = 0, /* no coex code in 8812au driver */
>  	.bt_desired_ver = 0,
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> index 17d3677ca463c..f7391f6aa3039 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> @@ -891,6 +891,8 @@ static const struct rtw_chip_ops rtw8821a_ops = {
>  	.coex_set_wl_rx_gain	= rtw8821a_coex_cfg_wl_rx_gain,
>  	.cfo_init		= rtw_phy_cfo_xcap_init,
>  	.cfo_track		= rtw88xxa_cfo_track,
> +	.adaptivity_init	= rtw88xxa_adaptivity_init,
> +	.adaptivity		= rtw88xxa_adaptivity,
>  };
>  
>  static const struct rtw_page_table page_table_8821a[] = {
> @@ -944,6 +946,11 @@ static const struct rtw_hw_reg rtw8821a_dig[] = {
>  	[0] = { .addr = REG_RXIGI_A, .mask = 0x7f },
>  };
>  
> +static const struct rtw_hw_reg_offset rtw8821a_edcca_th[] = {
> +	[EDCCA_TH_L2H_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE0}, .offset = 0},
> +	[EDCCA_TH_H2L_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE1}, .offset = 0},
> +};
> +
>  static const struct rtw_rfe_def rtw8821a_rfe_defs[] = {
>  	[0] = { .phy_pg_tbl	= &rtw8821a_bb_pg_tbl,
>  		.txpwr_lmt_tbl	= &rtw8821a_txpwr_lmt_tbl,
> @@ -1193,6 +1200,9 @@ const struct rtw_chip_info rtw8821a_hw_spec = {
>  	.bfer_su_max_num = 2,
>  	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
>  	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
> +	.edcca_th = rtw8821a_edcca_th,
> +	.l2h_th_ini_cs = 10,
> +	.l2h_th_ini_ad = -17,
>  
>  	.coex_para_ver = 20190509, /* glcoex_ver_date_8821a_1ant */
>  	.bt_desired_ver = 0x62, /* But for 2 ant it's 0x5c */
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
> index 586bb57a10061..aff62d61f4719 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
> @@ -2040,6 +2040,129 @@ void rtw88xxa_cfo_track(struct rtw_dev *rtwdev)
>  }
>  EXPORT_SYMBOL(rtw88xxa_cfo_track);
>  
> +#define EDCCA_TH_MAX_88XXA		0x7f
> +#define EDCCA_L2H_CAP_88XXA		10
> +#define EDCCA_L2H_INI_NORMAL_88XXA	20
> +#define EDCCA_DBG_PORT_88XXA		0x209
> +#define EDCCA_DC_BACKOFF_88XXA		2
> +#define EDCCA_LB_START_88XXA		(EDCCA_L2H_H2L_DIFF - 30)
> +#define EDCCA_LB_RETRY_88XXA		(EDCCA_LB_START_88XXA + 18)
> +
> +static void rtw88xxa_set_rf_mode_rx(struct rtw_dev *rtwdev, u32 data0,
> +				    u32 data1)
> +{
> +	u8 path;
> +
> +	for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
> +		rtw_write_rf(rtwdev, path, RF_LUTWE, BIT(19), 1);
> +		rtw_write_rf(rtwdev, path, RF_MODE_TABLE_ADDR, RFREG_MASK,
> +			     0x18000);
> +		rtw_write_rf(rtwdev, path, RF_MODE_TABLE_DATA0, RFREG_MASK,
> +			     data0);
> +		rtw_write_rf(rtwdev, path, RF_MODE_TABLE_DATA1, RFREG_MASK,
> +			     data1);
> +		rtw_write_rf(rtwdev, path, RF_LUTWE, BIT(19), 0);
> +	}
> +}
> +
> +static void rtw88xxa_set_lna(struct rtw_dev *rtwdev, bool enable)
> +{
> +	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
> +		rtw88xxa_set_rf_mode_rx(rtwdev, 0x3f7ff,
> +					enable ? 0xc26bf : 0xc22bf);
> +	else
> +		rtw88xxa_set_rf_mode_rx(rtwdev, 0x0002f,
> +					enable ? 0xfb0bb : 0xfb09b);
> +}
> +
> +static u8 rtw88xxa_edcca_count(struct rtw_dev *rtwdev)
> +{
> +	u8 cnt = 0;
> +	int i;
> +
> +	rtw_write32(rtwdev, REG_DBGSEL, 0);
> +	for (i = 0; i < 3 &&
> +	     rtw_read32(rtwdev, REG_DBGRPT) & BIT_DBG_CCA_BUSY; i++)
> +		usleep_range(3000, 3500);
> +
> +	rtw_write32(rtwdev, REG_DBGSEL, EDCCA_DBG_PORT_88XXA);
> +	for (i = 0; i < 20; i++)
> +		if (rtw_read32(rtwdev, REG_DBGRPT) & BIT_DBG_EDCCA_BUSY)
> +			cnt++;
> +
> +	return cnt;
> +}
> +
> +static s8 rtw88xxa_edcca_search_floor(struct rtw_dev *rtwdev)
> +{
> +	s8 l2h = EDCCA_LB_START_88XXA;
> +
> +	rtw_phy_set_edcca_th(rtwdev, l2h, l2h - EDCCA_L2H_H2L_DIFF);
> +	msleep(30);
> +
> +	while (l2h < EDCCA_L2H_CAP_88XXA && rtw88xxa_edcca_count(rtwdev) > 1) {
> +		l2h++;
> +		rtw_phy_set_edcca_th(rtwdev, l2h, l2h - EDCCA_L2H_H2L_DIFF);
> +	}
> +
> +	return l2h;
> +}
> +
> +void rtw88xxa_adaptivity_init(struct rtw_dev *rtwdev)
> +{
> +	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
> +	u32 dbgsel = rtw_read32(rtwdev, REG_DBGSEL);
> +	s8 l2h;
> +
> +	rtw_write32_clr(rtwdev, REG_TX_PTCL_CTRL, BIT_DIS_EDCCA);
> +
> +	rtw88xxa_set_lna(rtwdev, false);
> +	l2h = rtw88xxa_edcca_search_floor(rtwdev);
> +	if (l2h >= EDCCA_LB_RETRY_88XXA)
> +		l2h = rtw88xxa_edcca_search_floor(rtwdev);
> +	rtw88xxa_set_lna(rtwdev, true);
> +	rtw_write32(rtwdev, REG_DBGSEL, dbgsel);
> +
> +	dm_info->l2h_th_lb = l2h + EDCCA_DC_BACKOFF_88XXA;
> +	rtw_phy_set_edcca_th(rtwdev, EDCCA_TH_MAX_88XXA, EDCCA_TH_MAX_88XXA);
> +
> +	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "EDCCA L2H floor %d\n",
> +		dm_info->l2h_th_lb);
> +}
> +EXPORT_SYMBOL(rtw88xxa_adaptivity_init);
> +
> +void rtw88xxa_adaptivity(struct rtw_dev *rtwdev)
> +{
> +	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
> +	int igi_diff = EDCCA_IGI_BASE - dm_info->igi_history[0];
> +	s8 l2h_lb = dm_info->l2h_th_lb;
> +	s8 l2h, h2l;
> +
> +	/* toggle the ADC mask to work around an EDCCA hang */
> +	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A) {
> +		rtw_write32_set(rtwdev, REG_FPGA0_RFMOD, BIT(10));
> +		rtw_write32_clr(rtwdev, REG_FPGA0_RFMOD, BIT(10));
> +	}
> +
> +	if (dm_info->edcca_mode == RTW_EDCCA_ADAPTIVITY) {
> +		l2h = min(dm_info->l2h_th_ini + igi_diff, EDCCA_L2H_CAP_88XXA);
> +		h2l = l2h - EDCCA_L2H_H2L_DIFF;
> +	} else {
> +		l2h = EDCCA_L2H_INI_NORMAL_88XXA + igi_diff;
> +		h2l = l2h - EDCCA_L2H_H2L_DIFF_NORMAL;
> +	}
> +
> +	l2h = max(l2h, l2h_lb);
> +	h2l = max_t(s8, h2l, l2h_lb - EDCCA_L2H_H2L_DIFF);
> +
> +	rtw_phy_set_edcca_th(rtwdev, l2h, h2l);
> +
> +	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY,
> +		"EDCCA mode %d, IGI 0x%x, L2H %d, H2L %d\n",
> +		dm_info->edcca_mode, dm_info->igi_history[0], l2h, h2l);
> +}
> +EXPORT_SYMBOL(rtw88xxa_adaptivity);
> +
>  MODULE_AUTHOR("Realtek Corporation");
>  MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821a/8811a/8812a common code");
>  MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
> index 09b72d33f62e1..b8719c278df73 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
> +++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
> @@ -139,6 +139,9 @@ struct rtw_jaguar_phy_status_rpt {
>  
>  #define RF18_BW_MASK		(BIT(11) | BIT(10))
>  
> +#define BIT_DBG_CCA_BUSY	BIT(3)
> +#define BIT_DBG_EDCCA_BUSY	BIT(29)
> +
>  void rtw88xxa_efuse_grant(struct rtw_dev *rtwdev, bool on);
>  int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map);
>  void rtw88xxa_power_off(struct rtw_dev *rtwdev,
> @@ -173,5 +176,7 @@ void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
>  void rtw88xxa_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif,
>  			     struct rtw_bfee *bfee, bool enable);
>  void rtw88xxa_cfo_track(struct rtw_dev *rtwdev);
> +void rtw88xxa_adaptivity_init(struct rtw_dev *rtwdev);
> +void rtw88xxa_adaptivity(struct rtw_dev *rtwdev);
>  
>  #endif


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain
  2026-10-30 14:57   ` Bitterblue Smith
@ 2026-10-30 18:16     ` Luka Gejak
  2026-10-30 18:50       ` Bitterblue Smith
  0 siblings, 1 reply; 13+ messages in thread
From: Luka Gejak @ 2026-10-30 18:16 UTC (permalink / raw)
  To: Bitterblue Smith, Ho Jie Feng; +Cc: Ping-Ke Shih, linux-wireless

Hi Bitterblue, Jie Feng,

Thanks for testing this Bitterblue. I had a look at why the scan gets so
slow and I think I found the cause.

October 30, 2026 at 3:57 PM, "Bitterblue Smith" <rtl8821cerfe2@gmail.com> wrote:

> 
> On 29/10/2026 07:52, Ho Jie Feng wrote:
> 
> During the scans I see a lot of "timed out to flush queue 1" (and 3)
> in dmesg. You need debug_mask=0x80000000 to see these.

[ ... ]

>> +void rtw88xxa_adaptivity(struct rtw_dev *rtwdev)
>> +{
>> +    struct rtw_dm_info *dm_info = &rtwdev->dm_info;
>> +    int igi_diff = EDCCA_IGI_BASE - dm_info->igi_history[0];

As far as I can see, igi_history[0] is not always the IGI in the hardware.
Since 3f90942473d2 ("wifi: rtw88: Increase the RX gain before scanning")
a sw scan writes IGI 0x1c without updating igi_history:

        rtw_core_scan_start()
          rtw_phy_dig_set_max_coverage()
            rtw_phy_dig_write(rtwdev, DIG_CVRG_MIN)

rtw_watch_dog_work() skips rtw_phy_dynamic_mechanism() while scanning, so
if I read it right 0x8a4 keeps the value computed for the associated IGI
for the whole scan.

In the 8812au vendor driver these chips are in ODM_IC_PWDB_EDCCA, and
phydm_adaptivity() computes the threshold from the current IGI
(cur_ig_value) with the same formula as here. So with the gain raised and
0x8a4 unchanged, EDCCA seems to get more sensitive by the difference
between the associated IGI and 0x1c. Since rtw88xxa_adaptivity_init()
clears BIT_DIS_EDCCA, the MAC would then hold the frames.

I think this matches the flush timeouts quoted above. Queue 1 and 3 are
the LOW and HIGH priority queues, and mac80211 flushes the queues every
time it leaves the home channel. Most of the delayed pings also came back
together at about the time the scan finished, which is when
rtw_core_scan_complete() writes the old IGI back.

The 8812A beacon loss branch in rtw_phy_dig() switches to DIG_CVRG_MIN
after rtw_phy_dig_recorder() has stored the other value, so the same
mismatch seems possible outside of scans too.

Jie Feng, maybe something like what rtw89 does in 280c444745aa
("wifi: rtw89: config EDCCA threshold during scan to prevent TX failed")
would work here, setting 0x8a4 to 0x7f/0x7f at scan start and restoring
it at scan complete. Or rtw88xxa_adaptivity() could use the IGI that is
actually in the hardware and run again when the core writes the IGI,
which would also cover the beacon loss case. I think this belongs in
this patch for v2.

And one more thing Bitterblue, is there a reason for your email only
being addressed to the mailing list. I am pretty certain that the
author hasn't subscribed to the mailing list so your test results
might now reach them.

Best regards,
Luka Gejak

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain
  2026-10-30 18:16     ` Luka Gejak
@ 2026-10-30 18:50       ` Bitterblue Smith
  0 siblings, 0 replies; 13+ messages in thread
From: Bitterblue Smith @ 2026-10-30 18:50 UTC (permalink / raw)
  To: Luka Gejak, Ho Jie Feng; +Cc: Ping-Ke Shih, linux-wireless

On 30/10/2026 20:16, Luka Gejak wrote:
> Hi Bitterblue, Jie Feng,
> 
> Thanks for testing this Bitterblue. I had a look at why the scan gets so
> slow and I think I found the cause.
> 
> October 30, 2026 at 3:57 PM, "Bitterblue Smith" <rtl8821cerfe2@gmail.com> wrote:
> 
>>
>> On 29/10/2026 07:52, Ho Jie Feng wrote:
>>
>> During the scans I see a lot of "timed out to flush queue 1" (and 3)
>> in dmesg. You need debug_mask=0x80000000 to see these.
> 
> [ ... ]
> 
>>> +void rtw88xxa_adaptivity(struct rtw_dev *rtwdev)
>>> +{
>>> +    struct rtw_dm_info *dm_info = &rtwdev->dm_info;
>>> +    int igi_diff = EDCCA_IGI_BASE - dm_info->igi_history[0];
> 
> As far as I can see, igi_history[0] is not always the IGI in the hardware.
> Since 3f90942473d2 ("wifi: rtw88: Increase the RX gain before scanning")
> a sw scan writes IGI 0x1c without updating igi_history:
> 
>         rtw_core_scan_start()
>           rtw_phy_dig_set_max_coverage()
>             rtw_phy_dig_write(rtwdev, DIG_CVRG_MIN)
> 

That appears to be the cause. If I disable rtw_phy_dig_set_max_coverage()
the problem goes away.

> rtw_watch_dog_work() skips rtw_phy_dynamic_mechanism() while scanning, so
> if I read it right 0x8a4 keeps the value computed for the associated IGI
> for the whole scan.
> 
> In the 8812au vendor driver these chips are in ODM_IC_PWDB_EDCCA, and
> phydm_adaptivity() computes the threshold from the current IGI
> (cur_ig_value) with the same formula as here. So with the gain raised and
> 0x8a4 unchanged, EDCCA seems to get more sensitive by the difference
> between the associated IGI and 0x1c. Since rtw88xxa_adaptivity_init()
> clears BIT_DIS_EDCCA, the MAC would then hold the frames.
> 
> I think this matches the flush timeouts quoted above. Queue 1 and 3 are
> the LOW and HIGH priority queues, and mac80211 flushes the queues every
> time it leaves the home channel. Most of the delayed pings also came back
> together at about the time the scan finished, which is when
> rtw_core_scan_complete() writes the old IGI back.
> 
> The 8812A beacon loss branch in rtw_phy_dig() switches to DIG_CVRG_MIN
> after rtw_phy_dig_recorder() has stored the other value, so the same
> mismatch seems possible outside of scans too.
> 
> Jie Feng, maybe something like what rtw89 does in 280c444745aa
> ("wifi: rtw89: config EDCCA threshold during scan to prevent TX failed")
> would work here, setting 0x8a4 to 0x7f/0x7f at scan start and restoring
> it at scan complete. Or rtw88xxa_adaptivity() could use the IGI that is
> actually in the hardware and run again when the core writes the IGI,
> which would also cover the beacon loss case. I think this belongs in
> this patch for v2.
> 
> And one more thing Bitterblue, is there a reason for your email only
> being addressed to the mailing list. I am pretty certain that the
> author hasn't subscribed to the mailing list so your test results
> might now reach them.
> 

That is Thunderbird being helpful. On some emails it replaces the
"Reply All" button with the very similar looking "Reply List". I didn't
notice until now.

> Best regards,
> Luka Gejak


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-10-30 18:50 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29  5:52 [PATCH 0/6] wifi: rtw88: extend 88xxA support Ho Jie Feng
2026-10-29  5:52 ` [PATCH 1/6] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
2026-10-29  5:52 ` [PATCH 2/6] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
2026-10-29 12:33   ` Bitterblue Smith
2026-10-29 14:13     ` Ho Jie Feng
2026-10-29 21:06       ` Bitterblue Smith
2026-10-29  5:52 ` [PATCH 3/6] wifi: rtw88: prepare frequency-offset correction for more chips Ho Jie Feng
2026-10-29  5:52 ` [PATCH 4/6] wifi: rtw88: 88xxa: correct carrier-frequency offset Ho Jie Feng
2026-10-29  5:52 ` [PATCH 5/6] wifi: rtw88: 88xxa: adapt channel sensing to receiver gain Ho Jie Feng
2026-10-30 14:57   ` Bitterblue Smith
2026-10-30 18:16     ` Luka Gejak
2026-10-30 18:50       ` Bitterblue Smith
2026-10-29  5:52 ` [PATCH 6/6] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits Ho Jie Feng

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