// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/relay.h>
#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