From: Ho Jie Feng <hjf3108@gmail.com>
To: Ping-Ke Shih <pkshih@realtek.com>
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
Bitterblue Smith <rtl8821cerfe2@gmail.com>,
Luka Gejak <luka.gejak@linux.dev>,
Ho Jie Feng <hjf3108@gmail.com>
Subject: [PATCH rtw-next v3 4/5] wifi: rtw88: 88xxa: correct carrier-frequency offset
Date: Fri, 13 Nov 2026 01:32:43 +0800 [thread overview]
Message-ID: <20261112173246.3201729-5-hjf3108@gmail.com> (raw)
In-Reply-To: <20261112173246.3201729-1-hjf3108@gmail.com>
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
next prev parent reply other threads:[~2026-11-12 17:33 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` [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 [this message]
2026-11-12 17:32 ` [PATCH rtw-next v3 5/5] wifi: rtw88: 8812a, 8821a: respect peer A-MPDU size limits Ho Jie Feng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261112173246.3201729-5-hjf3108@gmail.com \
--to=hjf3108@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-wireless@vger.kernel.org \
--cc=luka.gejak@linux.dev \
--cc=pkshih@realtek.com \
--cc=rtl8821cerfe2@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox