Enhance TX performance visibility for WiFi 7 chips by introducing TX
rate count tracking. This is critical for debugging and validation.
Additionally, introduce a new debugfs bb_info to enable and provide
baseband status.
Usage of bb_info debugfs:
$ echo enable 1 > bb_info // Start logging BB statistics information
$ echo mac_id 0 > bb_info // Specify mac_id for TX rate count tracking
The output of bb_info:
TP TX: 0 [0] Mbps, RX: 0 [0] Mbps
Avg packet length: TX=118, RX=136
TF: 0
TX count [0]:
Legacy: [0, 0, 0, 0]
OFDM: [0, 0, 0, 0, 0, 0, 0, 0]
MCS 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
MCS 2SS: [183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[PHY 0]
== RSSI/RX Rate
Beacon: 19 (-41 dBm)
RX count:
Legacy: [0, 0, 0, 0]
OFDM: [0, 0, 0, 0, 0, 0, 0, 0]
HT 0: [0, 0, 0, 0, 0, 0, 0, 0]
HT 1: [0, 0, 0, 0, 0, 0, 0, 0]
VHT 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0][0, 0]
VHT 2SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0][0, 0]
HE 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
HE 2SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
EHT 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0][0, 0]
EHT 2SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185]
TX rate [0, 2]: EHT 2SS MCS-0 GI:0.8 FB_G BW:160 (hw_rate=0x420) ==> agg_wait=-1 (1)
RX rate [0, 2]: EHT 2SS MCS-13 GI:0.8 BW:160 (hw_rate=0x42d)
RSSI: -43 dBm (raw=134, prev=135) [-43, -44]
EVM: [38.75, (41.50, 43.00)] SNR: 39
Signed-off-by: Kuan-Chung Chen <damon.chen@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20260429132625.1659182-4-pkshih@realtek.com
if (RTW89_CHK_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, &rtwdev->fw))
rtw89_chip_rfk_channel(rtwdev, target);
+ rtw89_fw_h2c_tx_history(rtwdev, target->mac_id);
+
rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR;
wake_queue:
bool get_stats;
};
+struct rtw89_bb_stat_cfg {
+ bool enable;
+ u16 mac_id;
+};
+
+struct rtw89_phy_info {
+ struct rtw89_bb_stat_cfg bb_stat_cfg;
+};
+
enum rtw89_chanctx_state {
RTW89_CHANCTX_STATE_MCC_START,
RTW89_CHANCTX_STATE_MCC_STOP,
RTW89_FW_FEATURE_SIM_SER_L0L1_BY_HALT_H2C,
RTW89_FW_FEATURE_LPS_ML_INFO_V1,
RTW89_FW_FEATURE_SER_POST_RECOVER_DMAC,
+ RTW89_FW_FEATURE_TX_HISTORY_V1,
NUM_OF_RTW89_FW_FEATURES,
};
DECLARE_EWMA(thermal, 4, 4);
+#define RTW89_TX_RATE_NR 40
struct rtw89_phy_stat {
struct ewma_thermal avg_thermal[RF_PATH_MAX];
u8 last_thermal_max;
+ u32 tx_rate_cnt[RTW89_TX_RATE_NR];
struct rtw89_beacon_stat bcn_stat;
};
struct rtw89_phy_efuse_gain efuse_gain;
struct rtw89_phy_ul_tb_info ul_tb_info;
struct rtw89_antdiv_info antdiv;
+ struct rtw89_phy_info phy_info;
struct rtw89_bb_ctx {
enum rtw89_phy_idx phy_idx;
struct rtw89_debugfs_priv btc_manual;
struct rtw89_debugfs_priv fw_log_manual;
struct rtw89_debugfs_priv phy_info;
+ struct rtw89_debugfs_priv bb_info;
struct rtw89_debugfs_priv stations;
struct rtw89_debugfs_priv disable_dm;
struct rtw89_debugfs_priv static_pd_th;
}
static int
-rtw89_debug_append_rx_rate(char *buf, size_t bufsz, struct rtw89_pkt_stat *pkt_stat,
- enum rtw89_hw_rate first_rate, int len)
+rtw89_debug_append_rate(char *buf, size_t bufsz, const u32 *rate_cnt,
+ int first_rate, int len)
{
char *p = buf, *end = buf + bufsz;
int i;
for (i = 0; i < len; i++)
p += scnprintf(p, end - p, "%s%u", i == 0 ? "" : ", ",
- pkt_stat->rx_rate_cnt[first_rate + i]);
+ rate_cnt[first_rate + i]);
return p - buf;
}
{FIRST_RATE_GEV1(EHT_NSS2_MCS0), 14, 0, "EHT 2SS:"},
};
+static const struct rtw89_tx_rate_cnt_info {
+ int first_rate;
+ int len;
+ const char *rate_mode;
+} rtw89_tx_rate_cnt_infos[] = {
+ {0, 4, "Legacy:"},
+ {4, 8, "OFDM:"},
+ {12, 14, "MCS 1SS:"},
+ {26, 14, "MCS 2SS:"},
+};
+
static int rtw89_get_rx_pkt_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb,
char *buf, size_t bufsz)
{
continue;
p += scnprintf(p, end - p, "%10s [", info->rate_mode);
- p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat,
- first_rate, info->len);
+ p += rtw89_debug_append_rate(p, end - p, pkt_stat->rx_rate_cnt,
+ first_rate, info->len);
if (info->ext) {
p += scnprintf(p, end - p, "][");
- p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat,
- first_rate + info->len, info->ext);
+ p += rtw89_debug_append_rate(p, end - p, pkt_stat->rx_rate_cnt,
+ first_rate + info->len,
+ info->ext);
}
p += scnprintf(p, end - p, "]\n");
}
return p - buf;
}
+static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb,
+ char *buf, size_t bufsz)
+{
+ char *p = buf, *end = buf + bufsz;
+
+ p += scnprintf(p, end - p, "\n[PHY %u]\n", bb->phy_idx);
+
+ p += scnprintf(p, end - p, "== RSSI/RX Rate\n");
+ p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p);
+
+ return p - buf;
+}
+
+static ssize_t
+rtw89_debug_priv_bb_info_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
+{
+ struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg;
+ struct rtw89_traffic_stats *stats = &rtwdev->stats;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ const struct rtw89_tx_rate_cnt_info *info;
+ struct rtw89_debugfs_iter_data iter_data;
+ char *p = buf, *end = buf + bufsz;
+ struct rtw89_bb_ctx *bb;
+ int i;
+
+ p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps, RX: %u [%u] Mbps\n",
+ stats->tx_throughput, stats->tx_throughput_raw,
+ stats->rx_throughput, stats->rx_throughput_raw);
+ p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n",
+ stats->tx_avg_len,
+ stats->rx_avg_len);
+ p += scnprintf(p, end - p, "TF: %u\n", stats->rx_tf_periodic);
+
+ if (chip->chip_gen != RTW89_CHIP_AX) {
+ p += scnprintf(p, end - p,
+ "TX count [0x%x]:\n", bb_stat->mac_id);
+
+ for (i = 0; i < ARRAY_SIZE(rtw89_tx_rate_cnt_infos); i++) {
+ info = &rtw89_tx_rate_cnt_infos[i];
+
+ p += scnprintf(p, end - p, "%10s [", info->rate_mode);
+ p += rtw89_debug_append_rate(p, end - p,
+ rtwdev->phystat.tx_rate_cnt,
+ info->first_rate, info->len);
+ p += scnprintf(p, end - p, "]\n");
+ }
+ }
+
+ rtw89_for_each_active_bb(rtwdev, bb)
+ p += rtw89_get_bb_stat(rtwdev, bb, p, end - p);
+ p += scnprintf(p, end - p, "\n");
+
+ rtw89_debugfs_iter_data_setup(&iter_data, p, end - p);
+ ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, &iter_data);
+ p += iter_data.written_sz;
+
+ return p - buf;
+}
+
+static ssize_t
+rtw89_debug_priv_bb_info_set(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ const char *buf, size_t count)
+{
+ struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg;
+ int val;
+
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ if (sscanf(buf, "enable %d", &val) == 1)
+ bb_stat->enable = !!val;
+ else if (sscanf(buf, "mac_id %x", &val) == 1)
+ rtw89_fw_h2c_tx_history(rtwdev, val);
+ else
+ return -EINVAL;
+
+ return count;
+}
+
static int rtw89_dump_addr_cam(struct rtw89_dev *rtwdev,
char *buf, size_t bufsz,
struct rtw89_addr_cam_entry *addr_cam)
.btc_manual = rtw89_debug_priv_set(btc_manual),
.fw_log_manual = rtw89_debug_priv_set(fw_log_manual, WLOCK),
.phy_info = rtw89_debug_priv_get(phy_info),
+ .bb_info = rtw89_debug_priv_set_and_get(bb_info, RWLOCK),
.stations = rtw89_debug_priv_get(stations, RLOCK),
.disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK),
.static_pd_th = rtw89_debug_priv_set_and_get(static_pd_th, RWLOCK),
rtw89_debugfs_add_w(btc_manual);
rtw89_debugfs_add_w(fw_log_manual);
rtw89_debugfs_add_r(phy_info);
+ rtw89_debugfs_add_rw(bb_info);
rtw89_debugfs_add_r(stations);
rtw89_debugfs_add_rw(disable_dm);
rtw89_debugfs_add_rw(static_pd_th);
__DIS_FW_FEAT(RTL8922A, ge, 0, 35, 84, 0, WITH_RFK_PRE_NOTIFY, G),
__CFG_FW_FEAT(RTL8922A, ge, 0, 35, 84, 0, RFK_PRE_NOTIFY_MCC_V1),
__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 84, 0, ADDR_CAM_V0),
+ __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 92, 0, TX_HISTORY_V1),
__CFG_FW_FEAT(RTL8922A, ge, 0, 35, 97, 0, SIM_SER_L0L1_BY_HALT_H2C),
__CFG_FW_FEAT(RTL8922A, ge, 0, 35, 100, 0, SER_POST_RECOVER_DMAC),
};
return ret;
}
+int rtw89_fw_h2c_tx_history(struct rtw89_dev *rtwdev, u16 mac_id)
+{
+ struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_h2c_ra_tx_history *h2c;
+ u32 len = sizeof(*h2c);
+ struct sk_buff *skb;
+ int ret;
+
+ if (chip->chip_gen == RTW89_CHIP_AX)
+ return 0;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c tx history\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, len);
+ h2c = (struct rtw89_h2c_ra_tx_history *)skb->data;
+
+ h2c->w0 = le32_encode_bits(mac_id, RTW89_H2C_RA_TX_HISTORY_W0_MACID) |
+ le32_encode_bits(0, RTW89_H2C_RA_TX_HISTORY_W0_PER_PPDU);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA,
+ H2C_FUNC_OUTSRC_RA_TX_HISTORY, 0, 0, len);
+
+ ret = rtw89_h2c_tx(rtwdev, skb, false);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ bb_stat->mac_id = mac_id;
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+
+int rtw89_fw_h2c_phy_ch_rpt(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_h2c_ra_phy_ch_rpt *h2c;
+ u32 len = sizeof(*h2c);
+ struct sk_buff *skb;
+ int ret;
+
+ if (chip->chip_gen == RTW89_CHIP_AX)
+ return 0;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c phy ch rpt\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, len);
+ h2c = (struct rtw89_h2c_ra_phy_ch_rpt *)skb->data;
+
+ h2c->w1 = le32_encode_bits(1, RTW89_H2C_RA_PHY_CH_RPT_W1_RPT_TX_COUNT);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA,
+ H2C_FUNC_OUTSRC_RA_PHY_CH_RPT, 0, 0, len);
+
+ ret = rtw89_h2c_tx(rtwdev, skb, false);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+
+int rtw89_fw_h2c_drv_ctrl_fw(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_h2c_ra_drv_ctrl_fw *h2c;
+ u32 len = sizeof(*h2c);
+ struct sk_buff *skb;
+ int ret;
+
+ if (chip->chip_gen == RTW89_CHIP_AX)
+ return 0;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c drv ctrl fw\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, len);
+ h2c = (struct rtw89_h2c_ra_drv_ctrl_fw *)skb->data;
+
+ h2c->w0 = le32_encode_bits(1, RTW89_H2C_RA_DRV_CTRL_FW_W0_RPT_TX_COUNT);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA,
+ H2C_FUNC_OUTSRC_RA_DRV_CTRL_FW, 0, 0, len);
+
+ ret = rtw89_h2c_tx(rtwdev, skb, false);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+
int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev, u8 type)
{
struct rtw89_btc *btc = &rtwdev->btc;
#define RTW89_H2C_RA_V1_W4_RAMASK_UHL16 GENMASK(31, 16)
#define RTW89_H2C_RA_V1_W5_RAMASK_UHH16 GENMASK(15, 0)
+struct rtw89_h2c_ra_tx_history {
+ __le32 w0;
+} __packed;
+
+#define RTW89_H2C_RA_TX_HISTORY_W0_MACID GENMASK(15, 0)
+#define RTW89_H2C_RA_TX_HISTORY_W0_PER_PPDU GENMASK(23, 16)
+
+struct rtw89_h2c_ra_phy_ch_rpt {
+ __le32 w0;
+ __le32 w1;
+ __le32 w2;
+ __le32 w3;
+} __packed;
+
+#define RTW89_H2C_RA_PHY_CH_RPT_W1_RPT_TX_COUNT BIT(10)
+
+struct rtw89_h2c_ra_drv_ctrl_fw {
+ __le32 w0;
+} __packed;
+
+#define RTW89_H2C_RA_DRV_CTRL_FW_W0_RPT_TX_COUNT BIT(13)
+
static inline void RTW89_SET_FWCMD_SEC_IDX(void *cmd, u32 val)
{
le32p_replace_bits((__le32 *)(cmd) + 0x00, val, GENMASK(7, 0));
*/
} __packed;
+struct rtw89_c2h_ra_tx_history {
+ struct rtw89_c2h_hdr hdr;
+ __le32 ra_tbtt_cnt;
+ __le32 tx_rate_tot_cnt_hist[RTW89_TX_RATE_NR];
+ __le32 tx_cat_cnt[3];
+} __packed;
+
struct rtw89_c2h_fw_scan_rpt {
struct rtw89_c2h_hdr hdr;
u8 phy_idx;
#define H2C_CL_OUTSRC_RA 0x1
#define H2C_FUNC_OUTSRC_RA_MACIDCFG 0x0
+#define H2C_FUNC_OUTSRC_RA_TX_HISTORY 0x9
+#define H2C_FUNC_OUTSRC_RA_PHY_CH_RPT 0xe
+#define H2C_FUNC_OUTSRC_RA_DRV_CTRL_FW 0xf
#define H2C_CL_OUTSRC_DM 0x2
#define H2C_FUNC_FW_MCC_DIG 0x6
struct rtw89_rx_phy_ppdu *phy_ppdu);
int rtw89_fw_h2c_tp_offload(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link);
int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi);
+int rtw89_fw_h2c_phy_ch_rpt(struct rtw89_dev *rtwdev);
+int rtw89_fw_h2c_tx_history(struct rtw89_dev *rtwdev, u16 mac_id);
+int rtw89_fw_h2c_drv_ctrl_fw(struct rtw89_dev *rtwdev);
int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev, u8 type);
int rtw89_fw_h2c_cxdrv_init_v7(struct rtw89_dev *rtwdev, u8 type);
int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev, u8 type);
if (changed & BSS_CHANGED_MLD_VALID_LINKS) {
struct rtw89_vif_link *cur = rtw89_get_designated_link(rtwvif);
+ u16 mac_id;
if (RTW89_CHK_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, &rtwdev->fw))
rtw89_chip_rfk_channel(rtwdev, cur);
- if (hweight16(vif->active_links) == 1)
+ if (hweight16(vif->active_links) == 1) {
+ mac_id = cur->mac_id;
rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR;
- else
+ } else {
+ /* Specify for all MAC ID */
+ mac_id = 0xffff;
rtwvif->mlo_mode = RTW89_MLO_MODE_EMLSR;
+ }
+
+ rtw89_fw_h2c_tx_history(rtwdev, mac_id);
}
}
&ra_data);
}
+static void
+rtw89_phy_c2h_tx_history(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+ const struct rtw89_c2h_ra_tx_history *history = (const void *)c2h->data;
+ u32 *tx_rate_cnt = rtwdev->phystat.tx_rate_cnt;
+ u32 i;
+
+ for (i = 0; i < RTW89_TX_RATE_NR; i++)
+ tx_rate_cnt[i] = le32_to_cpu(history->tx_rate_tot_cnt_hist[i]);
+}
+
static
void (* const rtw89_phy_c2h_ra_handler[])(struct rtw89_dev *rtwdev,
struct sk_buff *c2h, u32 len) = {
[RTW89_PHY_C2H_FUNC_STS_RPT] = rtw89_phy_c2h_ra_rpt,
[RTW89_PHY_C2H_FUNC_MU_GPTBL_RPT] = NULL,
[RTW89_PHY_C2H_FUNC_TXSTS] = NULL,
+ [RTW89_PHY_C2H_FUNC_TX_HISTORY] = rtw89_phy_c2h_tx_history,
[RTW89_PHY_C2H_FUNC_ACCELERATE_EN] = rtw89_fw_c2h_dummy_handler,
};
rtwdev->hal.thermal_prot_lv = 0;
}
+static void rtw89_phy_trigger_tx_count(struct rtw89_dev *rtwdev)
+{
+ if (RTW89_CHK_FW_FEATURE(TX_HISTORY_V1, &rtwdev->fw))
+ rtw89_fw_h2c_phy_ch_rpt(rtwdev);
+ else
+ rtw89_fw_h2c_drv_ctrl_fw(rtwdev);
+}
+
+static void rtw89_phy_stat_update(struct rtw89_dev *rtwdev)
+{
+ if (!rtwdev->phy_info.bb_stat_cfg.enable)
+ return;
+
+ rtw89_phy_trigger_tx_count(rtwdev);
+}
+
void rtw89_phy_stat_track(struct rtw89_dev *rtwdev)
{
struct rtw89_bb_ctx *bb;
rtw89_phy_stat_thermal_update(rtwdev);
rtw89_phy_thermal_protect(rtwdev);
rtw89_phy_stat_rssi_update(rtwdev);
+ rtw89_phy_stat_update(rtwdev);
rtw89_for_each_active_bb(rtwdev, bb) {
bb->last_pkt_stat = bb->cur_pkt_stat;
RTW89_PHY_C2H_FUNC_STS_RPT,
RTW89_PHY_C2H_FUNC_MU_GPTBL_RPT,
RTW89_PHY_C2H_FUNC_TXSTS,
+ RTW89_PHY_C2H_FUNC_TX_HISTORY = 0x4,
RTW89_PHY_C2H_FUNC_ACCELERATE_EN = 0x7,
RTW89_PHY_C2H_FUNC_RA_NUM,