mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 09:04:39 +02:00
wifi: ath11k: Register DBR event handler for CFR data
Add handler for WMI_PDEV_DMA_RING_BUF_RELEASE_EVENT which indicates CFR data availability in the DB ring. Add CFR data processing from DB ring buffers. Use correlate_and_relay API to match CFR data with metadata from WMI_PEER_CFR_CAPTURE_EVENT. Release buffer to userspace through relayfs on successful correlation, otherwise hold buffer waiting for matching WMI event from firmware. Add new debug masks: - ATH11K_DBG_CFR: Enables CFR-related debug logs. - ATH11K_DBG_CFR_DUMP: Enables detailed CFR data dump for analysis. Tested-on: IPQ8074 hw2.0 PCI IPQ8074 WLAN.HK.2.5.0.1-00991-QCAHKSWPL_SILICONZ-1 Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Signed-off-by: Venkateswara Naralasetty <quic_vnaralas@quicinc.com> Co-developed-by: Yu Zhang (Yuriy) <yu.zhang@oss.qualcomm.com> Signed-off-by: Yu Zhang (Yuriy) <yu.zhang@oss.qualcomm.com> Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com> Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com> Signed-off-by: Qian Zhang <qian.zhang@oss.qualcomm.com> Link: https://patch.msgid.link/20251230082520.3401007-6-qian.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
This commit is contained in:
parent
c1bf6959dd
commit
99cf756831
|
|
@ -8,10 +8,248 @@
|
|||
#include "core.h"
|
||||
#include "debug.h"
|
||||
|
||||
struct ath11k_dbring *ath11k_cfr_get_dbring(struct ath11k *ar)
|
||||
{
|
||||
if (ar->cfr_enabled)
|
||||
return &ar->cfr.rx_ring;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ath11k_cfr_calculate_tones_from_dma_hdr(struct ath11k_cfr_dma_hdr *hdr)
|
||||
{
|
||||
u8 bw = FIELD_GET(CFIR_DMA_HDR_INFO1_UPLOAD_PKT_BW, hdr->info1);
|
||||
u8 preamble = FIELD_GET(CFIR_DMA_HDR_INFO1_PREAMBLE_TYPE, hdr->info1);
|
||||
|
||||
switch (preamble) {
|
||||
case ATH11K_CFR_PREAMBLE_TYPE_LEGACY:
|
||||
fallthrough;
|
||||
case ATH11K_CFR_PREAMBLE_TYPE_VHT:
|
||||
switch (bw) {
|
||||
case 0:
|
||||
return TONES_IN_20MHZ;
|
||||
case 1: /* DUP40/VHT40 */
|
||||
return TONES_IN_40MHZ;
|
||||
case 2: /* DUP80/VHT80 */
|
||||
return TONES_IN_80MHZ;
|
||||
case 3: /* DUP160/VHT160 */
|
||||
return TONES_IN_160MHZ;
|
||||
default:
|
||||
return TONES_INVALID;
|
||||
}
|
||||
case ATH11K_CFR_PREAMBLE_TYPE_HT:
|
||||
switch (bw) {
|
||||
case 0:
|
||||
return TONES_IN_20MHZ;
|
||||
case 1:
|
||||
return TONES_IN_40MHZ;
|
||||
default:
|
||||
return TONES_INVALID;
|
||||
}
|
||||
default:
|
||||
return TONES_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
void ath11k_cfr_release_lut_entry(struct ath11k_look_up_table *lut)
|
||||
{
|
||||
memset(lut, 0, sizeof(*lut));
|
||||
}
|
||||
|
||||
static void ath11k_cfr_rfs_write(struct ath11k *ar, const void *head,
|
||||
u32 head_len, const void *data, u32 data_len,
|
||||
const void *tail, int tail_data)
|
||||
{
|
||||
struct ath11k_cfr *cfr = &ar->cfr;
|
||||
|
||||
if (!cfr->rfs_cfr_capture)
|
||||
return;
|
||||
|
||||
relay_write(cfr->rfs_cfr_capture, head, head_len);
|
||||
relay_write(cfr->rfs_cfr_capture, data, data_len);
|
||||
relay_write(cfr->rfs_cfr_capture, tail, tail_data);
|
||||
relay_flush(cfr->rfs_cfr_capture);
|
||||
}
|
||||
|
||||
static void ath11k_cfr_free_pending_dbr_events(struct ath11k *ar)
|
||||
{
|
||||
struct ath11k_cfr *cfr = &ar->cfr;
|
||||
struct ath11k_look_up_table *lut;
|
||||
int i;
|
||||
|
||||
if (!cfr->lut)
|
||||
return;
|
||||
|
||||
for (i = 0; i < cfr->lut_num; i++) {
|
||||
lut = &cfr->lut[i];
|
||||
if (lut->dbr_recv && !lut->tx_recv &&
|
||||
lut->dbr_tstamp < cfr->last_success_tstamp) {
|
||||
ath11k_dbring_bufs_replenish(ar, &cfr->rx_ring, lut->buff,
|
||||
WMI_DIRECT_BUF_CFR);
|
||||
ath11k_cfr_release_lut_entry(lut);
|
||||
cfr->flush_dbr_cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ath11k_cfr_correlate_and_relay() - Correlate and relay CFR events
|
||||
* @ar: Pointer to ath11k structure
|
||||
* @lut: Lookup table for correlation
|
||||
* @event_type: Type of event received (TX or DBR)
|
||||
*
|
||||
* Correlates WMI_PDEV_DMA_RING_BUF_RELEASE_EVENT (DBR) and
|
||||
* WMI_PEER_CFR_CAPTURE_EVENT (TX capture) by PPDU ID. If both events
|
||||
* are present and the PPDU IDs match, returns CORRELATE_STATUS_RELEASE
|
||||
* to relay thecorrelated data to userspace. Otherwise returns
|
||||
* CORRELATE_STATUS_HOLD to wait for the other event.
|
||||
*
|
||||
* Also checks pending DBR events and clears them when no corresponding TX
|
||||
* capture event is received for the PPDU.
|
||||
*
|
||||
* Return: CORRELATE_STATUS_RELEASE or CORRELATE_STATUS_HOLD
|
||||
*/
|
||||
|
||||
static enum ath11k_cfr_correlate_status
|
||||
ath11k_cfr_correlate_and_relay(struct ath11k *ar,
|
||||
struct ath11k_look_up_table *lut,
|
||||
u8 event_type)
|
||||
{
|
||||
enum ath11k_cfr_correlate_status status;
|
||||
struct ath11k_cfr *cfr = &ar->cfr;
|
||||
u64 diff;
|
||||
|
||||
if (event_type == ATH11K_CORRELATE_TX_EVENT) {
|
||||
if (lut->tx_recv)
|
||||
cfr->cfr_dma_aborts++;
|
||||
cfr->tx_evt_cnt++;
|
||||
lut->tx_recv = true;
|
||||
} else if (event_type == ATH11K_CORRELATE_DBR_EVENT) {
|
||||
cfr->dbr_evt_cnt++;
|
||||
lut->dbr_recv = true;
|
||||
}
|
||||
|
||||
if (lut->dbr_recv && lut->tx_recv) {
|
||||
if (lut->dbr_ppdu_id == lut->tx_ppdu_id) {
|
||||
/*
|
||||
* 64-bit counters make wraparound highly improbable,
|
||||
* wraparound handling is omitted.
|
||||
*/
|
||||
cfr->last_success_tstamp = lut->dbr_tstamp;
|
||||
if (lut->dbr_tstamp > lut->txrx_tstamp) {
|
||||
diff = lut->dbr_tstamp - lut->txrx_tstamp;
|
||||
ath11k_dbg(ar->ab, ATH11K_DBG_CFR,
|
||||
"txrx event -> dbr event delay = %u ms",
|
||||
jiffies_to_msecs(diff));
|
||||
} else if (lut->txrx_tstamp > lut->dbr_tstamp) {
|
||||
diff = lut->txrx_tstamp - lut->dbr_tstamp;
|
||||
ath11k_dbg(ar->ab, ATH11K_DBG_CFR,
|
||||
"dbr event -> txrx event delay = %u ms",
|
||||
jiffies_to_msecs(diff));
|
||||
}
|
||||
|
||||
ath11k_cfr_free_pending_dbr_events(ar);
|
||||
|
||||
cfr->release_cnt++;
|
||||
status = ATH11K_CORRELATE_STATUS_RELEASE;
|
||||
} else {
|
||||
/*
|
||||
* Discard TXRX event on PPDU ID mismatch because multiple PPDUs
|
||||
* may share the same DMA address due to ucode aborts.
|
||||
*/
|
||||
|
||||
ath11k_dbg(ar->ab, ATH11K_DBG_CFR,
|
||||
"Received dbr event twice for the same lut entry");
|
||||
lut->tx_recv = false;
|
||||
lut->tx_ppdu_id = 0;
|
||||
cfr->clear_txrx_event++;
|
||||
cfr->cfr_dma_aborts++;
|
||||
status = ATH11K_CORRELATE_STATUS_HOLD;
|
||||
}
|
||||
} else {
|
||||
status = ATH11K_CORRELATE_STATUS_HOLD;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int ath11k_cfr_process_data(struct ath11k *ar,
|
||||
struct ath11k_dbring_data *param)
|
||||
{
|
||||
return 0;
|
||||
u32 end_magic = ATH11K_CFR_END_MAGIC;
|
||||
struct ath11k_csi_cfr_header *header;
|
||||
struct ath11k_cfr_dma_hdr *dma_hdr;
|
||||
struct ath11k_cfr *cfr = &ar->cfr;
|
||||
struct ath11k_look_up_table *lut;
|
||||
struct ath11k_base *ab = ar->ab;
|
||||
u32 buf_id, tones, length;
|
||||
u8 num_chains;
|
||||
int status;
|
||||
u8 *data;
|
||||
|
||||
data = param->data;
|
||||
buf_id = param->buf_id;
|
||||
|
||||
if (param->data_sz < sizeof(*dma_hdr))
|
||||
return -EINVAL;
|
||||
|
||||
dma_hdr = (struct ath11k_cfr_dma_hdr *)data;
|
||||
|
||||
tones = ath11k_cfr_calculate_tones_from_dma_hdr(dma_hdr);
|
||||
if (tones == TONES_INVALID) {
|
||||
ath11k_warn(ar->ab, "Number of tones received is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
num_chains = FIELD_GET(CFIR_DMA_HDR_INFO1_NUM_CHAINS,
|
||||
dma_hdr->info1);
|
||||
|
||||
length = sizeof(*dma_hdr);
|
||||
length += tones * (num_chains + 1);
|
||||
|
||||
spin_lock_bh(&cfr->lut_lock);
|
||||
|
||||
if (!cfr->lut) {
|
||||
spin_unlock_bh(&cfr->lut_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lut = &cfr->lut[buf_id];
|
||||
|
||||
ath11k_dbg_dump(ab, ATH11K_DBG_CFR_DUMP, "data_from_buf_rel:", "",
|
||||
data, length);
|
||||
|
||||
lut->buff = param->buff;
|
||||
lut->data = data;
|
||||
lut->data_len = length;
|
||||
lut->dbr_ppdu_id = dma_hdr->phy_ppdu_id;
|
||||
lut->dbr_tstamp = jiffies;
|
||||
|
||||
memcpy(&lut->hdr, dma_hdr, sizeof(*dma_hdr));
|
||||
|
||||
header = &lut->header;
|
||||
header->meta_data.channel_bw = FIELD_GET(CFIR_DMA_HDR_INFO1_UPLOAD_PKT_BW,
|
||||
dma_hdr->info1);
|
||||
header->meta_data.length = length;
|
||||
|
||||
status = ath11k_cfr_correlate_and_relay(ar, lut,
|
||||
ATH11K_CORRELATE_DBR_EVENT);
|
||||
if (status == ATH11K_CORRELATE_STATUS_RELEASE) {
|
||||
ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||||
"releasing CFR data to user space");
|
||||
ath11k_cfr_rfs_write(ar, &lut->header,
|
||||
sizeof(struct ath11k_csi_cfr_header),
|
||||
lut->data, lut->data_len,
|
||||
&end_magic, sizeof(u32));
|
||||
ath11k_cfr_release_lut_entry(lut);
|
||||
} else if (status == ATH11K_CORRELATE_STATUS_HOLD) {
|
||||
ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||||
"tx event is not yet received holding the buf");
|
||||
}
|
||||
|
||||
spin_unlock_bh(&cfr->lut_lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Helper function to check whether the given peer mac address
|
||||
|
|
|
|||
|
|
@ -19,9 +19,78 @@
|
|||
|
||||
#define HOST_MAX_CHAINS 8
|
||||
|
||||
enum ath11k_cfr_correlate_event_type {
|
||||
ATH11K_CORRELATE_DBR_EVENT,
|
||||
ATH11K_CORRELATE_TX_EVENT,
|
||||
};
|
||||
|
||||
struct ath11k_sta;
|
||||
struct ath11k_per_peer_cfr_capture;
|
||||
|
||||
#define ATH11K_CFR_END_MAGIC 0xBEAFDEAD
|
||||
|
||||
enum ath11k_cfr_correlate_status {
|
||||
ATH11K_CORRELATE_STATUS_RELEASE,
|
||||
ATH11K_CORRELATE_STATUS_HOLD,
|
||||
ATH11K_CORRELATE_STATUS_ERR,
|
||||
};
|
||||
|
||||
enum ath11k_cfr_preamble_type {
|
||||
ATH11K_CFR_PREAMBLE_TYPE_LEGACY,
|
||||
ATH11K_CFR_PREAMBLE_TYPE_HT,
|
||||
ATH11K_CFR_PREAMBLE_TYPE_VHT,
|
||||
};
|
||||
|
||||
struct cfr_metadata {
|
||||
u8 peer_addr[ETH_ALEN];
|
||||
u8 status;
|
||||
u8 capture_bw;
|
||||
u8 channel_bw;
|
||||
u8 phy_mode;
|
||||
u16 prim20_chan;
|
||||
u16 center_freq1;
|
||||
u16 center_freq2;
|
||||
u8 capture_mode;
|
||||
u8 capture_type;
|
||||
u8 sts_count;
|
||||
u8 num_rx_chain;
|
||||
u32 timestamp;
|
||||
u32 length;
|
||||
u32 chain_rssi[HOST_MAX_CHAINS];
|
||||
u16 chain_phase[HOST_MAX_CHAINS];
|
||||
u32 cfo_measurement;
|
||||
u8 agc_gain[HOST_MAX_CHAINS];
|
||||
u32 rx_start_ts;
|
||||
} __packed;
|
||||
|
||||
struct ath11k_csi_cfr_header {
|
||||
u32 start_magic_num;
|
||||
u32 vendorid;
|
||||
u8 cfr_metadata_version;
|
||||
u8 cfr_data_version;
|
||||
u8 chip_type;
|
||||
u8 platform_type;
|
||||
u32 reserved;
|
||||
struct cfr_metadata meta_data;
|
||||
} __packed;
|
||||
|
||||
#define TONES_IN_20MHZ 256
|
||||
#define TONES_IN_40MHZ 512
|
||||
#define TONES_IN_80MHZ 1024
|
||||
#define TONES_IN_160MHZ 2048 /* 160 MHz isn't supported yet */
|
||||
#define TONES_INVALID 0
|
||||
|
||||
#define CFIR_DMA_HDR_INFO0_TAG GENMASK(7, 0)
|
||||
#define CFIR_DMA_HDR_INFO0_LEN GENMASK(13, 8)
|
||||
|
||||
#define CFIR_DMA_HDR_INFO1_UPLOAD_DONE GENMASK(0, 0)
|
||||
#define CFIR_DMA_HDR_INFO1_CAPTURE_TYPE GENMASK(3, 1)
|
||||
#define CFIR_DMA_HDR_INFO1_PREAMBLE_TYPE GENMASK(5, 4)
|
||||
#define CFIR_DMA_HDR_INFO1_NSS GENMASK(8, 6)
|
||||
#define CFIR_DMA_HDR_INFO1_NUM_CHAINS GENMASK(11, 9)
|
||||
#define CFIR_DMA_HDR_INFO1_UPLOAD_PKT_BW GENMASK(14, 12)
|
||||
#define CFIR_DMA_HDR_INFO1_SW_PEER_ID_VALID GENMASK(15, 15)
|
||||
|
||||
struct ath11k_cfr_dma_hdr {
|
||||
u16 info0;
|
||||
u16 info1;
|
||||
|
|
@ -37,6 +106,7 @@ struct ath11k_look_up_table {
|
|||
u16 dbr_ppdu_id;
|
||||
u16 tx_ppdu_id;
|
||||
dma_addr_t dbr_address;
|
||||
struct ath11k_csi_cfr_header header;
|
||||
struct ath11k_cfr_dma_hdr hdr;
|
||||
u64 txrx_tstamp;
|
||||
u64 dbr_tstamp;
|
||||
|
|
@ -109,6 +179,8 @@ int ath11k_cfr_send_peer_cfr_capture_cmd(struct ath11k *ar,
|
|||
struct ath11k_sta *arsta,
|
||||
struct ath11k_per_peer_cfr_capture *params,
|
||||
const u8 *peer_mac);
|
||||
struct ath11k_dbring *ath11k_cfr_get_dbring(struct ath11k *ar);
|
||||
void ath11k_cfr_release_lut_entry(struct ath11k_look_up_table *lut);
|
||||
|
||||
#else
|
||||
static inline int ath11k_cfr_init(struct ath11k_base *ab)
|
||||
|
|
@ -156,5 +228,15 @@ ath11k_cfr_send_peer_cfr_capture_cmd(struct ath11k *ar,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ath11k_cfr_release_lut_entry(struct ath11k_look_up_table *lut)
|
||||
{
|
||||
}
|
||||
|
||||
static inline
|
||||
struct ath11k_dbring *ath11k_cfr_get_dbring(struct ath11k *ar)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_ATH11K_CFR */
|
||||
#endif /* ATH11K_CFR_H */
|
||||
|
|
|
|||
|
|
@ -295,6 +295,7 @@ int ath11k_dbring_buffer_release_event(struct ath11k_base *ab,
|
|||
int size;
|
||||
dma_addr_t paddr;
|
||||
int ret = 0;
|
||||
int status;
|
||||
|
||||
pdev_idx = ev->fixed.pdev_id;
|
||||
module_id = ev->fixed.module_id;
|
||||
|
|
@ -328,6 +329,9 @@ int ath11k_dbring_buffer_release_event(struct ath11k_base *ab,
|
|||
case WMI_DIRECT_BUF_SPECTRAL:
|
||||
ring = ath11k_spectral_get_dbring(ar);
|
||||
break;
|
||||
case WMI_DIRECT_BUF_CFR:
|
||||
ring = ath11k_cfr_get_dbring(ar);
|
||||
break;
|
||||
default:
|
||||
ring = NULL;
|
||||
ath11k_warn(ab, "Recv dma buffer release ev on unsupp module %d\n",
|
||||
|
|
@ -378,8 +382,12 @@ int ath11k_dbring_buffer_release_event(struct ath11k_base *ab,
|
|||
handler_data.data = PTR_ALIGN(vaddr_unalign,
|
||||
ring->buf_align);
|
||||
handler_data.data_sz = ring->buf_sz;
|
||||
handler_data.buff = buff;
|
||||
handler_data.buf_id = buf_id;
|
||||
|
||||
ring->handler(ar, &handler_data);
|
||||
status = ring->handler(ar, &handler_data);
|
||||
if (status == ATH11K_CORRELATE_STATUS_HOLD)
|
||||
continue;
|
||||
}
|
||||
|
||||
buff->paddr = 0;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ struct ath11k_dbring_data {
|
|||
void *data;
|
||||
u32 data_sz;
|
||||
struct wmi_dma_buf_release_meta_data meta;
|
||||
struct ath11k_dbring_element *buff;
|
||||
u32 buf_id;
|
||||
};
|
||||
|
||||
struct ath11k_dbring_buf_release_event {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
|
||||
/*
|
||||
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#ifndef _ATH11K_DEBUG_H_
|
||||
|
|
@ -27,6 +27,8 @@ enum ath11k_debug_mask {
|
|||
ATH11K_DBG_DP_TX = 0x00002000,
|
||||
ATH11K_DBG_DP_RX = 0x00004000,
|
||||
ATH11K_DBG_CE = 0x00008000,
|
||||
ATH11K_DBG_CFR = 0x00010000,
|
||||
ATH11K_DBG_CFR_DUMP = 0x00020000,
|
||||
};
|
||||
|
||||
static inline const char *ath11k_dbg_str(enum ath11k_debug_mask mask)
|
||||
|
|
@ -64,6 +66,10 @@ static inline const char *ath11k_dbg_str(enum ath11k_debug_mask mask)
|
|||
return "dp_rx";
|
||||
case ATH11K_DBG_CE:
|
||||
return "ce";
|
||||
case ATH11K_DBG_CFR:
|
||||
return "cfr";
|
||||
case ATH11K_DBG_CFR_DUMP:
|
||||
return "cfr_dump";
|
||||
|
||||
/* no default handler to allow compiler to check that the
|
||||
* enum is fully handled
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user