* [PATCH rtw-next v3 0/5] wifi: rtw88: extend 88xxA support
@ 2026-11-12 17:32 Ho Jie Feng
2026-11-12 17:32 ` [PATCH rtw-next v3 1/5] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
` (4 more replies)
0 siblings, 5 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-11-12 17:32 UTC (permalink / raw)
To: Ping-Ke Shih
Cc: linux-wireless, linux-kernel, Bitterblue Smith, Luka Gejak, Ho Jie Feng
This series enables HT STBC transmission, VHT SU beamformed reception
and carrier-frequency-offset correction on supported 88xxA devices.
Tested on RTL8812AU:
TX went from 136.0 to 144.2.
RX went from 215.5 to 249.2.
Changelog v3:
- Remove EDCCA commits since it causes problems in some situations
- Beamformee: gate on spatial streams instead of the RX antennas,
in case an antenna is disabled
- Beamformee: writes RTL8814A use rtw_write32_mask() instead of
an unaligned 16-bit write
- Beamformee: drop the chip ID from the debug message, style fixes
- CFO: add RTW_XCAP_MASK to phy.h
- CFO: use 0x20 as the RTL8814A crystal cap when the efuse is empty,
following the vendor driver
- CFO: add comment on why min(...,2) is needed for rtl8814
- A-MPDU: set BIT(31) of REG_AMPDU_MAX_LENGTH, like the vendor driver
- A-MPDU: limit RTL8821A with IEEE80211_VHT_MAX_AMPDU_64K
Changelog v2:
- Split correcting beamform caps into its own commit
- Adjust EDCCA threshold on scan to avoid high latency
Ho Jie Feng (5):
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: 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/phy.c | 110 +++++++++++++++++-
drivers/net/wireless/realtek/rtw88/phy.h | 5 +
drivers/net/wireless/realtek/rtw88/reg.h | 1 +
drivers/net/wireless/realtek/rtw88/rtw8812a.c | 16 ++-
drivers/net/wireless/realtek/rtw88/rtw8814a.c | 70 +++++++++--
drivers/net/wireless/realtek/rtw88/rtw8821a.c | 15 ++-
drivers/net/wireless/realtek/rtw88/rtw8822c.c | 100 +---------------
drivers/net/wireless/realtek/rtw88/rtw8822c.h | 3 -
drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 108 +++++++++++++++--
drivers/net/wireless/realtek/rtw88/rtw88xxa.h | 3 +
12 files changed, 324 insertions(+), 123 deletions(-)
base-commit: 61a5eb87b2d9b4d5e353780b781cf691f39ba9e1
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH rtw-next v3 1/5] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions
2026-11-12 17:32 [PATCH rtw-next v3 0/5] wifi: rtw88: extend 88xxA support Ho Jie Feng
@ 2026-11-12 17:32 ` Ho Jie Feng
2026-11-12 17:32 ` [PATCH rtw-next v3 2/5] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
` (3 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-11-12 17:32 UTC (permalink / raw)
To: Ping-Ke Shih
Cc: linux-wireless, linux-kernel, Bitterblue Smith, Luka Gejak, Ho Jie Feng
8812a and 8814a support STBC, enable it.
8822c will lose reporting TX STBC capability if efuse reports
only 1 path, but this is correct, since it in that case it doesn't
actually support STBC.
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 f7708ad17b..e243738809 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -1583,7 +1583,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 781b66ccde..55f1d3d324 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 b6916d3dc6..e35c57c9d1 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.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH rtw-next v3 2/5] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
2026-11-12 17:32 [PATCH rtw-next v3 0/5] wifi: rtw88: extend 88xxA support Ho Jie Feng
2026-11-12 17:32 ` [PATCH rtw-next v3 1/5] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
@ 2026-11-12 17:32 ` Ho Jie Feng
2026-11-13 7:46 ` Luka Gejak
2026-11-12 17:32 ` [PATCH rtw-next v3 3/5] wifi: rtw88: prepare frequency-offset correction for more chips Ho Jie Feng
` (2 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Ho Jie Feng @ 2026-11-12 17:32 UTC (permalink / raw)
To: Ping-Ke Shih
Cc: linux-wireless, linux-kernel, Bitterblue Smith, Luka Gejak, Ho Jie Feng
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 | 37 ++++++++++-
drivers/net/wireless/realtek/rtw88/rtw8821a.c | 3 +-
drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 61 +++++++++++++++++++
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 a5d3010e6b..0aa78fb6a7 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 e243738809..fe93c93b1a 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -1603,6 +1603,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;
@@ -1621,10 +1622,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 afcdb66fdb..f8ec1e2931 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 55f1d3d324..5d58dff4fc 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 e35c57c9d1..c2a18f6be8 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,32 @@ 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_write32_mask(rtwdev, REG_SND_PTCL_CTRL, MASKBYTE1 | MASKBYTE2,
+ 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 +2098,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 +2261,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 6096d3b8e4..6d106ae70d 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 5ffa29d181..128a18ee33 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,66 @@ 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, "SU beamformee %s slot %u\n",
+ 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 (rtwdev->efuse.hw_cap.nss == 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, u8 *phy_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 6597ca0dfc..150e86ea74 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.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH rtw-next v3 3/5] wifi: rtw88: prepare frequency-offset correction for more chips
2026-11-12 17:32 [PATCH rtw-next v3 0/5] wifi: rtw88: extend 88xxA support Ho Jie Feng
2026-11-12 17:32 ` [PATCH rtw-next v3 1/5] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
2026-11-12 17:32 ` [PATCH rtw-next v3 2/5] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
@ 2026-11-12 17:32 ` Ho Jie Feng
2026-11-12 17:32 ` [PATCH rtw-next v3 4/5] wifi: rtw88: 88xxa: correct carrier-frequency offset Ho Jie Feng
2026-11-12 17:32 ` [PATCH rtw-next v3 5/5] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits Ho Jie Feng
4 siblings, 0 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-11-12 17:32 UTC (permalink / raw)
To: Ping-Ke Shih
Cc: linux-wireless, linux-kernel, Bitterblue Smith, Luka Gejak, Ho Jie Feng
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 e2515a662b..d0c1fb65be 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 8a843aac73..b551cb860d 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 09315d23e2..d7b0204e6d 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 fc62b67a15..c27e4c394a 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.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH rtw-next v3 4/5] wifi: rtw88: 88xxa: correct carrier-frequency offset
2026-11-12 17:32 [PATCH rtw-next v3 0/5] wifi: rtw88: extend 88xxA support Ho Jie Feng
` (2 preceding siblings ...)
2026-11-12 17:32 ` [PATCH rtw-next v3 3/5] wifi: rtw88: prepare frequency-offset correction for more chips Ho Jie Feng
@ 2026-11-12 17:32 ` Ho Jie Feng
2026-11-13 7:52 ` Luka Gejak
2026-11-12 17:32 ` [PATCH rtw-next v3 5/5] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits Ho Jie Feng
4 siblings, 1 reply; 9+ messages in thread
From: Ho Jie Feng @ 2026-11-12 17:32 UTC (permalink / raw)
To: Ping-Ke Shih
Cc: linux-wireless, linux-kernel, Bitterblue Smith, Luka Gejak, Ho Jie Feng
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 | 6 ++-
drivers/net/wireless/realtek/rtw88/phy.h | 3 ++
drivers/net/wireless/realtek/rtw88/rtw8812a.c | 2 +
drivers/net/wireless/realtek/rtw88/rtw8814a.c | 32 +++++++++++--
drivers/net/wireless/realtek/rtw88/rtw8821a.c | 2 +
drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 47 +++++++++++++++----
drivers/net/wireless/realtek/rtw88/rtw88xxa.h | 1 +
7 files changed, 79 insertions(+), 14 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
index d0c1fb65be..eed3e2cb60 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.c
+++ b/drivers/net/wireless/realtek/rtw88/phy.c
@@ -687,13 +687,15 @@ 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;
+ /* rtl8814a has 4 paths, but only 2 of them report cfo */
+ 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 +796,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/phy.h b/drivers/net/wireless/realtek/rtw88/phy.h
index b551cb860d..3a74035219 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.h
+++ b/drivers/net/wireless/realtek/rtw88/phy.h
@@ -204,4 +204,7 @@ enum rtw_phy_cck_pd_lv {
#define RRSR_RATE_ORDER_MAX 0xfffff
#define RRSR_RATE_ORDER_CCK_LEN 4
+/* crystal cap width of all chips except RTL8822C */
+#define RTW_XCAP_MASK GENMASK(5, 0)
+
#endif
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index 5d58dff4fc..c8008cf43d 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 c2a18f6be8..a66bd7c7e0 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8814a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c
@@ -146,6 +146,9 @@ static int rtw8814a_read_efuse(struct rtw_dev *rtwdev, u8 *log_map, u8 *phy_map)
efuse->rfe_option = map->rfe_option;
efuse->rf_board_option = map->rf_board_option;
efuse->crystal_cap = map->xtal_k;
+ if (efuse->crystal_cap == 0xff)
+ efuse->crystal_cap = 0x20;
+ efuse->crystal_cap &= RTW_XCAP_MASK;
efuse->channel_plan = map->channel_plan;
efuse->country_code[0] = map->country_code[0];
efuse->country_code[1] = map->country_code[1];
@@ -284,9 +287,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 +317,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 +1276,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);
}
}
@@ -2069,6 +2078,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, RTW_XCAP_MASK, 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)
@@ -2103,6 +2125,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 6d106ae70d..17d3677ca4 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 128a18ee33..a6c964e6c3 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
@@ -278,6 +278,7 @@ int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map, u8 *phy_map)
efuse->crystal_cap = map->xtal_k;
if (efuse->crystal_cap == 0xff)
efuse->crystal_cap = 0x20;
+ efuse->crystal_cap &= RTW_XCAP_MASK;
efuse->pa_type_2g = map->pa_type;
efuse->pa_type_5g = map->pa_type;
efuse->lna_type_2g = map->lna_type_2g;
@@ -631,9 +632,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);
@@ -652,13 +663,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);
}
static void rtw88xxa_phy_rf_config(struct rtw_dev *rtwdev)
@@ -1620,6 +1625,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);
@@ -2008,6 +2024,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, RTW_XCAP_MASK, 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 150e86ea74..188b8f3a4b 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.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH rtw-next v3 5/5] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits
2026-11-12 17:32 [PATCH rtw-next v3 0/5] wifi: rtw88: extend 88xxA support Ho Jie Feng
` (3 preceding siblings ...)
2026-11-12 17:32 ` [PATCH rtw-next v3 4/5] wifi: rtw88: 88xxa: correct carrier-frequency offset Ho Jie Feng
@ 2026-11-12 17:32 ` Ho Jie Feng
4 siblings, 0 replies; 9+ messages in thread
From: Ho Jie Feng @ 2026-11-12 17:32 UTC (permalink / raw)
To: Ping-Ke Shih
Cc: linux-wireless, linux-kernel, Bitterblue Smith, Luka Gejak, Ho Jie Feng
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 | 10 +++++++++-
drivers/net/wireless/realtek/rtw88/rtw8821a.c | 10 +++++++++-
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
index c8008cf43d..c8f5cdf31c 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
@@ -78,6 +78,14 @@ 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) | BIT(31));
+}
+
static void rtw8812a_do_lck(struct rtw_dev *rtwdev)
{
u32 cont_tx, lc_cal, i;
@@ -926,7 +934,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 17d3677ca4..796c100165 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -49,6 +49,14 @@ 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_VHT_MAX_AMPDU_64K);
+
+ rtw_write32(rtwdev, REG_AMPDU_MAX_LENGTH,
+ ((8192 << factor) - 1) | BIT(31));
+}
+
#define CAL_NUM_8821A 3
#define MACBB_REG_NUM_8821A 8
#define AFE_REG_NUM_8821A 4
@@ -872,7 +880,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.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH rtw-next v3 2/5] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
2026-11-12 17:32 ` [PATCH rtw-next v3 2/5] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
@ 2026-11-13 7:46 ` Luka Gejak
2026-11-13 8:01 ` Jie Feng
0 siblings, 1 reply; 9+ messages in thread
From: Luka Gejak @ 2026-11-13 7:46 UTC (permalink / raw)
To: Ho Jie Feng, Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Bitterblue Smith
Hi Jie Feng,
Thanks for the new version. A question and a comment below.
On November 12, 2026 6:32:41 PM GMT+01:00, Ho Jie Feng <hjf3108@gmail.com>
wrote:
> diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
> index e243738809..fe93c93b1a 100644
> --- a/drivers/net/wireless/realtek/rtw88/main.c
> +++ b/drivers/net/wireless/realtek/rtw88/main.c
[ ... ]
> @@ -1621,10 +1622,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;
In v2 this was its own patch, 3/7 "correctly expose beamformee
capabilities", after Bitterblue asked on v1 for it to be split out. Was
folding it back in here intended? I couldn't find it in the v3 changelog.
I can see a reason for it. Without this part, an AP that is also a MU
beamformer makes rtw_bf_assoc() take the MU branch and return at
bfer_mu_max_num, so the new config_bfee code would never run for it. If
that is why, maybe mention it in the changelog, and in the commit message
too, since this is core code and the support_bf check now hides the
beamformee caps on every chip when beamforming is turned off.
[ ... ]
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
> index 55f1d3d324..5d58dff4fc 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,
As far as I can see, 8812a, 8821a and 8814a are now the first chips that
can become a SU beamformee without a cfg_csi_rate op. Once rtw_bf_assoc()
has set bfee.role, the watchdog goes through
rtw_vif_watch_dog_iter()
rtw_dynamic_csi_rate()
and there new_csi_rate_idx is only written by that op:
u8 new_csi_rate_idx;
...
rtw_chip_cfg_csi_rate(rtwdev, rtwdev->dm_info.min_rssi,
bf_info->cur_csi_rpt_rate,
fix_rate_enable, &new_csi_rate_idx);
if (new_csi_rate_idx != bf_info->cur_csi_rpt_rate)
bf_info->cur_csi_rpt_rate = new_csi_rate_idx;
So if I read it right, it compares and stores an uninitialized value
there. cur_csi_rpt_rate has no other user on these chips, so it seems
harmless for now, but I think KMSAN would report it. Maybe
rtw_dynamic_csi_rate() could return early when the chip has no
cfg_csi_rate op?
Best regards,
Luka Gejak
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH rtw-next v3 4/5] wifi: rtw88: 88xxa: correct carrier-frequency offset
2026-11-12 17:32 ` [PATCH rtw-next v3 4/5] wifi: rtw88: 88xxa: correct carrier-frequency offset Ho Jie Feng
@ 2026-11-13 7:52 ` Luka Gejak
0 siblings, 0 replies; 9+ messages in thread
From: Luka Gejak @ 2026-11-13 7:52 UTC (permalink / raw)
To: Ho Jie Feng, Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Bitterblue Smith
Hi Jie Feng,
Thanks for adding the comment. One small comment below.
On November 12, 2026 6:32:43 PM GMT+01:00, Ho Jie Feng <hjf3108@gmail.com>
wrote:
> diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
> index d0c1fb65be..eed3e2cb60 100644
> --- a/drivers/net/wireless/realtek/rtw88/phy.c
> +++ b/drivers/net/wireless/realtek/rtw88/phy.c
> @@ -687,13 +687,15 @@ 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;
> + /* rtl8814a has 4 paths, but only 2 of them report cfo */
> + u8 path_num = min_t(u8, rtwdev->hal.rf_path_num, 2);
[ ... ]
> @@ -794,7 +796,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);
The new comment explains the 2 in rtw_phy_parsing_cfo_iter(), but
rtw_phy_cfo_xcap_track() has the same min_t() without it, so someone
reading that function still sees an unexplained 2.
Maybe add a define in phy.h and use it in both places, something like:
/* RTL8814A has 4 paths, but only paths A and B report CFO */
#define RTW_CFO_PATH_NUM_MAX 2
Best regards,
Luka Gejak
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH rtw-next v3 2/5] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions
2026-11-13 7:46 ` Luka Gejak
@ 2026-11-13 8:01 ` Jie Feng
0 siblings, 0 replies; 9+ messages in thread
From: Jie Feng @ 2026-11-13 8:01 UTC (permalink / raw)
To: Luka Gejak; +Cc: Ping-Ke Shih, linux-wireless, linux-kernel, Bitterblue Smith
On Fri, 13 Nov 2026 at 15:46, Luka Gejak <luka.gejak@linux.dev> wrote:
>
> Hi Jie Feng,
>
> Thanks for the new version. A question and a comment below.
>
> On November 12, 2026 6:32:41 PM GMT+01:00, Ho Jie Feng <hjf3108@gmail.com>
> wrote:
>
> > diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
> > index e243738809..fe93c93b1a 100644
> > --- a/drivers/net/wireless/realtek/rtw88/main.c
> > +++ b/drivers/net/wireless/realtek/rtw88/main.c
>
> [ ... ]
>
> > @@ -1621,10 +1622,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;
>
> In v2 this was its own patch, 3/7 "correctly expose beamformee
> capabilities", after Bitterblue asked on v1 for it to be split out. Was
> folding it back in here intended? I couldn't find it in the v3 changelog.
Sorry, looks like I used the wrong commit on my local tree, will resend as a v4.
Please ignore this v3.
> I can see a reason for it. Without this part, an AP that is also a MU
> beamformer makes rtw_bf_assoc() take the MU branch and return at
> bfer_mu_max_num, so the new config_bfee code would never run for it. If
> that is why, maybe mention it in the changelog, and in the commit message
> too, since this is core code and the support_bf check now hides the
> beamformee caps on every chip when beamforming is turned off.
>
> [ ... ]
>
> > diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c
> > index 55f1d3d324..5d58dff4fc 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,
>
> As far as I can see, 8812a, 8821a and 8814a are now the first chips that
> can become a SU beamformee without a cfg_csi_rate op. Once rtw_bf_assoc()
> has set bfee.role, the watchdog goes through
>
> rtw_vif_watch_dog_iter()
> rtw_dynamic_csi_rate()
>
> and there new_csi_rate_idx is only written by that op:
>
> u8 new_csi_rate_idx;
> ...
> rtw_chip_cfg_csi_rate(rtwdev, rtwdev->dm_info.min_rssi,
> bf_info->cur_csi_rpt_rate,
> fix_rate_enable, &new_csi_rate_idx);
>
> if (new_csi_rate_idx != bf_info->cur_csi_rpt_rate)
> bf_info->cur_csi_rpt_rate = new_csi_rate_idx;
>
> So if I read it right, it compares and stores an uninitialized value
> there. cur_csi_rpt_rate has no other user on these chips, so it seems
> harmless for now, but I think KMSAN would report it. Maybe
> rtw_dynamic_csi_rate() could return early when the chip has no
> cfg_csi_rate op?
>
> Best regards,
> Luka Gejak
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-11-13 8:02 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-12 17:32 [PATCH rtw-next v3 0/5] wifi: rtw88: extend 88xxA support Ho Jie Feng
2026-11-12 17:32 ` [PATCH rtw-next v3 1/5] wifi: rtw88: 8812a, 8814a: enable STBC for HT transmissions Ho Jie Feng
2026-11-12 17:32 ` [PATCH rtw-next v3 2/5] wifi: rtw88: 88xxa: receive VHT SU beamformed transmissions Ho Jie Feng
2026-11-13 7:46 ` Luka Gejak
2026-11-13 8:01 ` Jie Feng
2026-11-12 17:32 ` [PATCH rtw-next v3 3/5] wifi: rtw88: prepare frequency-offset correction for more chips Ho Jie Feng
2026-11-12 17:32 ` [PATCH rtw-next v3 4/5] wifi: rtw88: 88xxa: correct carrier-frequency offset Ho Jie Feng
2026-11-13 7:52 ` Luka Gejak
2026-11-12 17:32 ` [PATCH rtw-next v3 5/5] 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
all inboxes | Powered by JetHome®