Skip to content

Commit

Permalink
ath11k: Register DBR event handler for CFR data
Browse files Browse the repository at this point in the history
Firmware sends an WMI event WMI_PDEV_DMA_RING_BUF_RELEASE_EVENT
to host to indicate the CFR data availability in the DB ring.

Host will reap the CFR data from the DB ring buffers and invoke
correlate_and_relay API to correlate the CFR data with the meta
data coming from the other WMI event WMI_PEER_CFR_CAPTURE_EVENT.

If correlate and relay function returns success then release the
buffer to user space through relayfs, otherwise hold the buffer
until other WMI event comes from the firmware.

Tested-on: IPQ8074 WLAN.HK.2.5.0.1-00991-QCAHKSWPL_SILICONZ-1

Signed-off-by: Venkateswara Naralasetty <quic_vnaralas@quicinc.com>
  • Loading branch information
Venkateswara Naralasetty authored and intel-lab-lkp committed Feb 11, 2022
1 parent f43f535 commit 14cd77b
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 3 deletions.
235 changes: 234 additions & 1 deletion drivers/net/wireless/ath/ath11k/cfr.c
Expand Up @@ -8,10 +8,243 @@
#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 cfr_calculate_tones_from_dma_hdr(struct ath11k_cfir_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:
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:
break;
}

case ATH11K_CFR_PREAMBLE_TYPE_HT:
switch (bw) {
case 0:
return TONES_IN_20MHZ;
case 1:
return TONES_IN_40MHZ;
}
}

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 (!ar->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++;
}
}
}

/* Correlate and relay: This function correlate the data coming from
* WMI_PDEV_DMA_RING_BUF_RELEASE_EVENT(DBR event) and
* WMI_PEER_CFR_CAPTURE_EVENT(Tx capture event).
* If both the events are received and PPDU id matches from both the
* events, return CORRELATE_STATUS_RELEASE which means relay the
* correlated data to user space. Otherwise return CORRELATE_STATUS_HOLD
* which means wait for the second event to come.
*
* It also check for the pending DBR events and clear those events
* in case of corresponding TX capture event is not received for
* the PPDU.
*/

static enum ath11k_cfr_correlate_status
ath11k_cfr_correlate_and_relay(struct ath11k *ar,
struct ath11k_look_up_table *lut,
u8 event_type)
{
struct ath11k_cfr *cfr = &ar->cfr;
enum ath11k_cfr_correlate_status status;
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) {
/* We are using 64-bit counters here. So, it may take
* several year to hit wraparound. Hence, not handling
* the wraparound condition.
*/
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 {
/* When there is a ppdu id mismatch, discard the TXRX
* event since multiple PPDUs are likely to have same
* dma addr, 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;
struct ath11k_base *ab = ar->ab;
struct ath11k_cfr *cfr = &ar->cfr;
struct ath11k_look_up_table *lut;
struct ath11k_csi_cfr_header *header;
struct ath11k_cfir_dma_hdr *dma_hdr;
u8 *data;
u32 end_magic = ATH11K_CFR_END_MAGIC;
u32 buf_id;
u32 tones;
u32 length;
int status;
u8 num_chains;

data = param->data;
buf_id = param->buf_id;

if (param->data_sz < sizeof(*dma_hdr))
return -EINVAL;

dma_hdr = (struct ath11k_cfir_dma_hdr *)data;

tones = cfr_calculate_tones_from_dma_hdr(dma_hdr);
if (tones == TONES_INVALID) {
ath11k_warn(ar->ab, "Number of tones received is invalid");
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
Expand Down
80 changes: 80 additions & 0 deletions drivers/net/wireless/ath/ath11k/cfr.h
Expand Up @@ -13,6 +13,9 @@
#define ATH11K_CFR_NUM_RESP_PER_EVENT 1
#define ATH11K_CFR_EVENT_TIMEOUT_MS 1

#define ATH11K_CORRELATE_TX_EVENT 1
#define ATH11K_CORRELATE_DBR_EVENT 0

#define ATH11K_MAX_CFR_ENABLED_CLIENTS 10
#define CFR_MAX_LUT_ENTRIES 136

Expand All @@ -21,6 +24,70 @@
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_cfir_dma_hdr {
u16 info0;
u16 info1;
Expand All @@ -38,6 +105,7 @@ struct ath11k_look_up_table {
dma_addr_t dbr_address;
u32 tx_address1;
u32 tx_address2;
struct ath11k_csi_cfr_header header;
struct ath11k_cfir_dma_hdr hdr;
u64 txrx_tstamp;
u64 dbr_tstamp;
Expand Down Expand Up @@ -113,6 +181,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)
Expand Down Expand Up @@ -160,5 +230,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 */
10 changes: 9 additions & 1 deletion drivers/net/wireless/ath/ath11k/dbring.c
Expand Up @@ -282,6 +282,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;
Expand Down Expand Up @@ -311,6 +312,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",
Expand Down Expand Up @@ -358,8 +362,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;
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/wireless/ath/ath11k/dbring.h
Expand Up @@ -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 {
Expand Down

0 comments on commit 14cd77b

Please sign in to comment.