xref: /linux/drivers/net/wireless/ath/ath11k/cfr.c (revision bf4afc53b77aeaa48b5409da5c8da6bb4eff7f43)
19b2e3b4eSVenkateswara Naralasetty // SPDX-License-Identifier: BSD-3-Clause-Clear
29b2e3b4eSVenkateswara Naralasetty /*
39b2e3b4eSVenkateswara Naralasetty  * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
49b2e3b4eSVenkateswara Naralasetty  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
59b2e3b4eSVenkateswara Naralasetty  */
69b2e3b4eSVenkateswara Naralasetty 
79b2e3b4eSVenkateswara Naralasetty #include <linux/relay.h>
89b2e3b4eSVenkateswara Naralasetty #include "core.h"
99b2e3b4eSVenkateswara Naralasetty #include "debug.h"
109b2e3b4eSVenkateswara Naralasetty 
1199cf7568SVenkateswara Naralasetty struct ath11k_dbring *ath11k_cfr_get_dbring(struct ath11k *ar)
1299cf7568SVenkateswara Naralasetty {
1399cf7568SVenkateswara Naralasetty 	if (ar->cfr_enabled)
1499cf7568SVenkateswara Naralasetty 		return &ar->cfr.rx_ring;
1599cf7568SVenkateswara Naralasetty 
1699cf7568SVenkateswara Naralasetty 	return NULL;
1799cf7568SVenkateswara Naralasetty }
1899cf7568SVenkateswara Naralasetty 
1999cf7568SVenkateswara Naralasetty static int ath11k_cfr_calculate_tones_from_dma_hdr(struct ath11k_cfr_dma_hdr *hdr)
2099cf7568SVenkateswara Naralasetty {
2199cf7568SVenkateswara Naralasetty 	u8 bw = FIELD_GET(CFIR_DMA_HDR_INFO1_UPLOAD_PKT_BW, hdr->info1);
2299cf7568SVenkateswara Naralasetty 	u8 preamble = FIELD_GET(CFIR_DMA_HDR_INFO1_PREAMBLE_TYPE, hdr->info1);
2399cf7568SVenkateswara Naralasetty 
2499cf7568SVenkateswara Naralasetty 	switch (preamble) {
2599cf7568SVenkateswara Naralasetty 	case ATH11K_CFR_PREAMBLE_TYPE_LEGACY:
2699cf7568SVenkateswara Naralasetty 		fallthrough;
2799cf7568SVenkateswara Naralasetty 	case ATH11K_CFR_PREAMBLE_TYPE_VHT:
2899cf7568SVenkateswara Naralasetty 		switch (bw) {
2999cf7568SVenkateswara Naralasetty 		case 0:
3099cf7568SVenkateswara Naralasetty 			return TONES_IN_20MHZ;
3199cf7568SVenkateswara Naralasetty 		case 1: /* DUP40/VHT40 */
3299cf7568SVenkateswara Naralasetty 			return TONES_IN_40MHZ;
3399cf7568SVenkateswara Naralasetty 		case 2: /* DUP80/VHT80 */
3499cf7568SVenkateswara Naralasetty 			return TONES_IN_80MHZ;
3599cf7568SVenkateswara Naralasetty 		case 3: /* DUP160/VHT160 */
3699cf7568SVenkateswara Naralasetty 			return TONES_IN_160MHZ;
3799cf7568SVenkateswara Naralasetty 		default:
3899cf7568SVenkateswara Naralasetty 			return TONES_INVALID;
3999cf7568SVenkateswara Naralasetty 		}
4099cf7568SVenkateswara Naralasetty 	case ATH11K_CFR_PREAMBLE_TYPE_HT:
4199cf7568SVenkateswara Naralasetty 		switch (bw) {
4299cf7568SVenkateswara Naralasetty 		case 0:
4399cf7568SVenkateswara Naralasetty 			return TONES_IN_20MHZ;
4499cf7568SVenkateswara Naralasetty 		case 1:
4599cf7568SVenkateswara Naralasetty 			return TONES_IN_40MHZ;
4699cf7568SVenkateswara Naralasetty 		default:
4799cf7568SVenkateswara Naralasetty 			return TONES_INVALID;
4899cf7568SVenkateswara Naralasetty 		}
4999cf7568SVenkateswara Naralasetty 	default:
5099cf7568SVenkateswara Naralasetty 		return TONES_INVALID;
5199cf7568SVenkateswara Naralasetty 	}
5299cf7568SVenkateswara Naralasetty }
5399cf7568SVenkateswara Naralasetty 
5499cf7568SVenkateswara Naralasetty void ath11k_cfr_release_lut_entry(struct ath11k_look_up_table *lut)
5599cf7568SVenkateswara Naralasetty {
5699cf7568SVenkateswara Naralasetty 	memset(lut, 0, sizeof(*lut));
5799cf7568SVenkateswara Naralasetty }
5899cf7568SVenkateswara Naralasetty 
5999cf7568SVenkateswara Naralasetty static void ath11k_cfr_rfs_write(struct ath11k *ar, const void *head,
6099cf7568SVenkateswara Naralasetty 				 u32 head_len, const void *data, u32 data_len,
6199cf7568SVenkateswara Naralasetty 				 const void *tail, int tail_data)
6299cf7568SVenkateswara Naralasetty {
6399cf7568SVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
6499cf7568SVenkateswara Naralasetty 
6599cf7568SVenkateswara Naralasetty 	if (!cfr->rfs_cfr_capture)
6699cf7568SVenkateswara Naralasetty 		return;
6799cf7568SVenkateswara Naralasetty 
6899cf7568SVenkateswara Naralasetty 	relay_write(cfr->rfs_cfr_capture, head, head_len);
6999cf7568SVenkateswara Naralasetty 	relay_write(cfr->rfs_cfr_capture, data, data_len);
7099cf7568SVenkateswara Naralasetty 	relay_write(cfr->rfs_cfr_capture, tail, tail_data);
7199cf7568SVenkateswara Naralasetty 	relay_flush(cfr->rfs_cfr_capture);
7299cf7568SVenkateswara Naralasetty }
7399cf7568SVenkateswara Naralasetty 
7499cf7568SVenkateswara Naralasetty static void ath11k_cfr_free_pending_dbr_events(struct ath11k *ar)
7599cf7568SVenkateswara Naralasetty {
7699cf7568SVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
7799cf7568SVenkateswara Naralasetty 	struct ath11k_look_up_table *lut;
7899cf7568SVenkateswara Naralasetty 	int i;
7999cf7568SVenkateswara Naralasetty 
8099cf7568SVenkateswara Naralasetty 	if (!cfr->lut)
8199cf7568SVenkateswara Naralasetty 		return;
8299cf7568SVenkateswara Naralasetty 
8399cf7568SVenkateswara Naralasetty 	for (i = 0; i < cfr->lut_num; i++) {
8499cf7568SVenkateswara Naralasetty 		lut = &cfr->lut[i];
8599cf7568SVenkateswara Naralasetty 		if (lut->dbr_recv && !lut->tx_recv &&
8699cf7568SVenkateswara Naralasetty 		    lut->dbr_tstamp < cfr->last_success_tstamp) {
8799cf7568SVenkateswara Naralasetty 			ath11k_dbring_bufs_replenish(ar, &cfr->rx_ring, lut->buff,
8899cf7568SVenkateswara Naralasetty 						     WMI_DIRECT_BUF_CFR);
8999cf7568SVenkateswara Naralasetty 			ath11k_cfr_release_lut_entry(lut);
9099cf7568SVenkateswara Naralasetty 			cfr->flush_dbr_cnt++;
9199cf7568SVenkateswara Naralasetty 		}
9299cf7568SVenkateswara Naralasetty 	}
9399cf7568SVenkateswara Naralasetty }
9499cf7568SVenkateswara Naralasetty 
9599cf7568SVenkateswara Naralasetty /**
9699cf7568SVenkateswara Naralasetty  * ath11k_cfr_correlate_and_relay() - Correlate and relay CFR events
9799cf7568SVenkateswara Naralasetty  * @ar: Pointer to ath11k structure
9899cf7568SVenkateswara Naralasetty  * @lut: Lookup table for correlation
9999cf7568SVenkateswara Naralasetty  * @event_type: Type of event received (TX or DBR)
10099cf7568SVenkateswara Naralasetty  *
10199cf7568SVenkateswara Naralasetty  * Correlates WMI_PDEV_DMA_RING_BUF_RELEASE_EVENT (DBR) and
10299cf7568SVenkateswara Naralasetty  * WMI_PEER_CFR_CAPTURE_EVENT (TX capture) by PPDU ID. If both events
10399cf7568SVenkateswara Naralasetty  * are present and the PPDU IDs match, returns CORRELATE_STATUS_RELEASE
10499cf7568SVenkateswara Naralasetty  * to relay thecorrelated data to userspace. Otherwise returns
10599cf7568SVenkateswara Naralasetty  * CORRELATE_STATUS_HOLD to wait for the other event.
10699cf7568SVenkateswara Naralasetty  *
10799cf7568SVenkateswara Naralasetty  * Also checks pending DBR events and clears them when no corresponding TX
10899cf7568SVenkateswara Naralasetty  * capture event is received for the PPDU.
10999cf7568SVenkateswara Naralasetty  *
11099cf7568SVenkateswara Naralasetty  * Return: CORRELATE_STATUS_RELEASE or CORRELATE_STATUS_HOLD
11199cf7568SVenkateswara Naralasetty  */
11299cf7568SVenkateswara Naralasetty 
11399cf7568SVenkateswara Naralasetty static enum ath11k_cfr_correlate_status
11499cf7568SVenkateswara Naralasetty ath11k_cfr_correlate_and_relay(struct ath11k *ar,
11599cf7568SVenkateswara Naralasetty 			       struct ath11k_look_up_table *lut,
11699cf7568SVenkateswara Naralasetty 			       u8 event_type)
11799cf7568SVenkateswara Naralasetty {
11899cf7568SVenkateswara Naralasetty 	enum ath11k_cfr_correlate_status status;
11999cf7568SVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
12099cf7568SVenkateswara Naralasetty 	u64 diff;
12199cf7568SVenkateswara Naralasetty 
12299cf7568SVenkateswara Naralasetty 	if (event_type == ATH11K_CORRELATE_TX_EVENT) {
12399cf7568SVenkateswara Naralasetty 		if (lut->tx_recv)
12499cf7568SVenkateswara Naralasetty 			cfr->cfr_dma_aborts++;
12599cf7568SVenkateswara Naralasetty 		cfr->tx_evt_cnt++;
12699cf7568SVenkateswara Naralasetty 		lut->tx_recv = true;
12799cf7568SVenkateswara Naralasetty 	} else if (event_type == ATH11K_CORRELATE_DBR_EVENT) {
12899cf7568SVenkateswara Naralasetty 		cfr->dbr_evt_cnt++;
12999cf7568SVenkateswara Naralasetty 		lut->dbr_recv = true;
13099cf7568SVenkateswara Naralasetty 	}
13199cf7568SVenkateswara Naralasetty 
13299cf7568SVenkateswara Naralasetty 	if (lut->dbr_recv && lut->tx_recv) {
13399cf7568SVenkateswara Naralasetty 		if (lut->dbr_ppdu_id == lut->tx_ppdu_id) {
13499cf7568SVenkateswara Naralasetty 			/*
13599cf7568SVenkateswara Naralasetty 			 * 64-bit counters make wraparound highly improbable,
13699cf7568SVenkateswara Naralasetty 			 * wraparound handling is omitted.
13799cf7568SVenkateswara Naralasetty 			 */
13899cf7568SVenkateswara Naralasetty 			cfr->last_success_tstamp = lut->dbr_tstamp;
13999cf7568SVenkateswara Naralasetty 			if (lut->dbr_tstamp > lut->txrx_tstamp) {
14099cf7568SVenkateswara Naralasetty 				diff = lut->dbr_tstamp - lut->txrx_tstamp;
14199cf7568SVenkateswara Naralasetty 				ath11k_dbg(ar->ab, ATH11K_DBG_CFR,
14299cf7568SVenkateswara Naralasetty 					   "txrx event -> dbr event delay = %u ms",
14399cf7568SVenkateswara Naralasetty 					   jiffies_to_msecs(diff));
14499cf7568SVenkateswara Naralasetty 			} else if (lut->txrx_tstamp > lut->dbr_tstamp) {
14599cf7568SVenkateswara Naralasetty 				diff = lut->txrx_tstamp - lut->dbr_tstamp;
14699cf7568SVenkateswara Naralasetty 				ath11k_dbg(ar->ab, ATH11K_DBG_CFR,
14799cf7568SVenkateswara Naralasetty 					   "dbr event -> txrx event delay = %u ms",
14899cf7568SVenkateswara Naralasetty 					   jiffies_to_msecs(diff));
14999cf7568SVenkateswara Naralasetty 			}
15099cf7568SVenkateswara Naralasetty 
15199cf7568SVenkateswara Naralasetty 			ath11k_cfr_free_pending_dbr_events(ar);
15299cf7568SVenkateswara Naralasetty 
15399cf7568SVenkateswara Naralasetty 			cfr->release_cnt++;
15499cf7568SVenkateswara Naralasetty 			status = ATH11K_CORRELATE_STATUS_RELEASE;
15599cf7568SVenkateswara Naralasetty 		} else {
15699cf7568SVenkateswara Naralasetty 			/*
15799cf7568SVenkateswara Naralasetty 			 * Discard TXRX event on PPDU ID mismatch because multiple PPDUs
15899cf7568SVenkateswara Naralasetty 			 * may share the same DMA address due to ucode aborts.
15999cf7568SVenkateswara Naralasetty 			 */
16099cf7568SVenkateswara Naralasetty 
16199cf7568SVenkateswara Naralasetty 			ath11k_dbg(ar->ab, ATH11K_DBG_CFR,
16299cf7568SVenkateswara Naralasetty 				   "Received dbr event twice for the same lut entry");
16399cf7568SVenkateswara Naralasetty 			lut->tx_recv = false;
16499cf7568SVenkateswara Naralasetty 			lut->tx_ppdu_id = 0;
16599cf7568SVenkateswara Naralasetty 			cfr->clear_txrx_event++;
16699cf7568SVenkateswara Naralasetty 			cfr->cfr_dma_aborts++;
16799cf7568SVenkateswara Naralasetty 			status = ATH11K_CORRELATE_STATUS_HOLD;
16899cf7568SVenkateswara Naralasetty 		}
16999cf7568SVenkateswara Naralasetty 	} else {
17099cf7568SVenkateswara Naralasetty 		status = ATH11K_CORRELATE_STATUS_HOLD;
17199cf7568SVenkateswara Naralasetty 	}
17299cf7568SVenkateswara Naralasetty 
17399cf7568SVenkateswara Naralasetty 	return status;
17499cf7568SVenkateswara Naralasetty }
17599cf7568SVenkateswara Naralasetty 
1769b2e3b4eSVenkateswara Naralasetty static int ath11k_cfr_process_data(struct ath11k *ar,
1779b2e3b4eSVenkateswara Naralasetty 				   struct ath11k_dbring_data *param)
1789b2e3b4eSVenkateswara Naralasetty {
17999cf7568SVenkateswara Naralasetty 	u32 end_magic = ATH11K_CFR_END_MAGIC;
18099cf7568SVenkateswara Naralasetty 	struct ath11k_csi_cfr_header *header;
18199cf7568SVenkateswara Naralasetty 	struct ath11k_cfr_dma_hdr *dma_hdr;
18299cf7568SVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
18399cf7568SVenkateswara Naralasetty 	struct ath11k_look_up_table *lut;
18499cf7568SVenkateswara Naralasetty 	struct ath11k_base *ab = ar->ab;
18599cf7568SVenkateswara Naralasetty 	u32 buf_id, tones, length;
18699cf7568SVenkateswara Naralasetty 	u8 num_chains;
18799cf7568SVenkateswara Naralasetty 	int status;
18899cf7568SVenkateswara Naralasetty 	u8 *data;
18999cf7568SVenkateswara Naralasetty 
19099cf7568SVenkateswara Naralasetty 	data = param->data;
19199cf7568SVenkateswara Naralasetty 	buf_id = param->buf_id;
19299cf7568SVenkateswara Naralasetty 
19399cf7568SVenkateswara Naralasetty 	if (param->data_sz < sizeof(*dma_hdr))
19499cf7568SVenkateswara Naralasetty 		return -EINVAL;
19599cf7568SVenkateswara Naralasetty 
19699cf7568SVenkateswara Naralasetty 	dma_hdr = (struct ath11k_cfr_dma_hdr *)data;
19799cf7568SVenkateswara Naralasetty 
19899cf7568SVenkateswara Naralasetty 	tones = ath11k_cfr_calculate_tones_from_dma_hdr(dma_hdr);
19999cf7568SVenkateswara Naralasetty 	if (tones == TONES_INVALID) {
20099cf7568SVenkateswara Naralasetty 		ath11k_warn(ar->ab, "Number of tones received is invalid\n");
20199cf7568SVenkateswara Naralasetty 		return -EINVAL;
20299cf7568SVenkateswara Naralasetty 	}
20399cf7568SVenkateswara Naralasetty 
20499cf7568SVenkateswara Naralasetty 	num_chains = FIELD_GET(CFIR_DMA_HDR_INFO1_NUM_CHAINS,
20599cf7568SVenkateswara Naralasetty 			       dma_hdr->info1);
20699cf7568SVenkateswara Naralasetty 
20799cf7568SVenkateswara Naralasetty 	length = sizeof(*dma_hdr);
20899cf7568SVenkateswara Naralasetty 	length += tones * (num_chains + 1);
20999cf7568SVenkateswara Naralasetty 
21099cf7568SVenkateswara Naralasetty 	spin_lock_bh(&cfr->lut_lock);
21199cf7568SVenkateswara Naralasetty 
21299cf7568SVenkateswara Naralasetty 	if (!cfr->lut) {
21399cf7568SVenkateswara Naralasetty 		spin_unlock_bh(&cfr->lut_lock);
21499cf7568SVenkateswara Naralasetty 		return -EINVAL;
21599cf7568SVenkateswara Naralasetty 	}
21699cf7568SVenkateswara Naralasetty 
21799cf7568SVenkateswara Naralasetty 	lut = &cfr->lut[buf_id];
21899cf7568SVenkateswara Naralasetty 
21999cf7568SVenkateswara Naralasetty 	ath11k_dbg_dump(ab, ATH11K_DBG_CFR_DUMP, "data_from_buf_rel:", "",
22099cf7568SVenkateswara Naralasetty 			data, length);
22199cf7568SVenkateswara Naralasetty 
22299cf7568SVenkateswara Naralasetty 	lut->buff = param->buff;
22399cf7568SVenkateswara Naralasetty 	lut->data = data;
22499cf7568SVenkateswara Naralasetty 	lut->data_len = length;
22599cf7568SVenkateswara Naralasetty 	lut->dbr_ppdu_id = dma_hdr->phy_ppdu_id;
22699cf7568SVenkateswara Naralasetty 	lut->dbr_tstamp = jiffies;
22799cf7568SVenkateswara Naralasetty 
22899cf7568SVenkateswara Naralasetty 	memcpy(&lut->hdr, dma_hdr, sizeof(*dma_hdr));
22999cf7568SVenkateswara Naralasetty 
23099cf7568SVenkateswara Naralasetty 	header = &lut->header;
23199cf7568SVenkateswara Naralasetty 	header->meta_data.channel_bw = FIELD_GET(CFIR_DMA_HDR_INFO1_UPLOAD_PKT_BW,
23299cf7568SVenkateswara Naralasetty 						 dma_hdr->info1);
23399cf7568SVenkateswara Naralasetty 	header->meta_data.length = length;
23499cf7568SVenkateswara Naralasetty 
23599cf7568SVenkateswara Naralasetty 	status = ath11k_cfr_correlate_and_relay(ar, lut,
23699cf7568SVenkateswara Naralasetty 						ATH11K_CORRELATE_DBR_EVENT);
23799cf7568SVenkateswara Naralasetty 	if (status == ATH11K_CORRELATE_STATUS_RELEASE) {
23899cf7568SVenkateswara Naralasetty 		ath11k_dbg(ab, ATH11K_DBG_CFR,
23999cf7568SVenkateswara Naralasetty 			   "releasing CFR data to user space");
24099cf7568SVenkateswara Naralasetty 		ath11k_cfr_rfs_write(ar, &lut->header,
24199cf7568SVenkateswara Naralasetty 				     sizeof(struct ath11k_csi_cfr_header),
24299cf7568SVenkateswara Naralasetty 				     lut->data, lut->data_len,
24399cf7568SVenkateswara Naralasetty 				     &end_magic, sizeof(u32));
24499cf7568SVenkateswara Naralasetty 		ath11k_cfr_release_lut_entry(lut);
24599cf7568SVenkateswara Naralasetty 	} else if (status == ATH11K_CORRELATE_STATUS_HOLD) {
24699cf7568SVenkateswara Naralasetty 		ath11k_dbg(ab, ATH11K_DBG_CFR,
24799cf7568SVenkateswara Naralasetty 			   "tx event is not yet received holding the buf");
24899cf7568SVenkateswara Naralasetty 	}
24999cf7568SVenkateswara Naralasetty 
25099cf7568SVenkateswara Naralasetty 	spin_unlock_bh(&cfr->lut_lock);
25199cf7568SVenkateswara Naralasetty 
25299cf7568SVenkateswara Naralasetty 	return status;
2539b2e3b4eSVenkateswara Naralasetty }
2549b2e3b4eSVenkateswara Naralasetty 
255ca765bedSVenkateswara Naralasetty static void ath11k_cfr_fill_hdr_info(struct ath11k *ar,
256ca765bedSVenkateswara Naralasetty 				     struct ath11k_csi_cfr_header *header,
257ca765bedSVenkateswara Naralasetty 				     struct ath11k_cfr_peer_tx_param *params)
258ca765bedSVenkateswara Naralasetty {
259ca765bedSVenkateswara Naralasetty 	struct ath11k_cfr *cfr;
260ca765bedSVenkateswara Naralasetty 
261ca765bedSVenkateswara Naralasetty 	cfr = &ar->cfr;
262ca765bedSVenkateswara Naralasetty 	header->cfr_metadata_version = ATH11K_CFR_META_VERSION_4;
263ca765bedSVenkateswara Naralasetty 	header->cfr_data_version = ATH11K_CFR_DATA_VERSION_1;
264ca765bedSVenkateswara Naralasetty 	header->cfr_metadata_len = sizeof(struct cfr_metadata);
265ca765bedSVenkateswara Naralasetty 	header->chip_type = ar->ab->hw_rev;
266ca765bedSVenkateswara Naralasetty 	header->meta_data.status = FIELD_GET(WMI_CFR_PEER_CAPTURE_STATUS,
267ca765bedSVenkateswara Naralasetty 					     params->status);
268ca765bedSVenkateswara Naralasetty 	header->meta_data.capture_bw = params->bandwidth;
269ca765bedSVenkateswara Naralasetty 
270ca765bedSVenkateswara Naralasetty 	/*
271ca765bedSVenkateswara Naralasetty 	 * FW reports phymode will always be HE mode.
272ca765bedSVenkateswara Naralasetty 	 * Replace it with cached phy mode during peer assoc
273ca765bedSVenkateswara Naralasetty 	 */
274ca765bedSVenkateswara Naralasetty 	header->meta_data.phy_mode = cfr->phymode;
275ca765bedSVenkateswara Naralasetty 
276ca765bedSVenkateswara Naralasetty 	header->meta_data.prim20_chan = params->primary_20mhz_chan;
277ca765bedSVenkateswara Naralasetty 	header->meta_data.center_freq1 = params->band_center_freq1;
278ca765bedSVenkateswara Naralasetty 	header->meta_data.center_freq2 = params->band_center_freq2;
279ca765bedSVenkateswara Naralasetty 
280ca765bedSVenkateswara Naralasetty 	/*
281ca765bedSVenkateswara Naralasetty 	 * CFR capture is triggered by the ACK of a QoS Null frame:
282ca765bedSVenkateswara Naralasetty 	 * - 20 MHz: Legacy ACK
283ca765bedSVenkateswara Naralasetty 	 * - 40/80/160 MHz: DUP Legacy ACK
284ca765bedSVenkateswara Naralasetty 	 */
285ca765bedSVenkateswara Naralasetty 	header->meta_data.capture_mode = params->bandwidth ?
286ca765bedSVenkateswara Naralasetty 		ATH11K_CFR_CAPTURE_DUP_LEGACY_ACK : ATH11K_CFR_CAPTURE_LEGACY_ACK;
287ca765bedSVenkateswara Naralasetty 	header->meta_data.capture_type = params->capture_method;
288ca765bedSVenkateswara Naralasetty 	header->meta_data.num_rx_chain = ar->num_rx_chains;
289ca765bedSVenkateswara Naralasetty 	header->meta_data.sts_count = params->spatial_streams;
290ca765bedSVenkateswara Naralasetty 	header->meta_data.timestamp = params->timestamp_us;
291ca765bedSVenkateswara Naralasetty 	ether_addr_copy(header->meta_data.peer_addr, params->peer_mac_addr);
292ca765bedSVenkateswara Naralasetty 	memcpy(header->meta_data.chain_rssi, params->chain_rssi,
293ca765bedSVenkateswara Naralasetty 	       sizeof(params->chain_rssi));
294ca765bedSVenkateswara Naralasetty 	memcpy(header->meta_data.chain_phase, params->chain_phase,
295ca765bedSVenkateswara Naralasetty 	       sizeof(params->chain_phase));
296ca765bedSVenkateswara Naralasetty 	memcpy(header->meta_data.agc_gain, params->agc_gain,
297ca765bedSVenkateswara Naralasetty 	       sizeof(params->agc_gain));
298ca765bedSVenkateswara Naralasetty }
299ca765bedSVenkateswara Naralasetty 
300ca765bedSVenkateswara Naralasetty int ath11k_process_cfr_capture_event(struct ath11k_base *ab,
301ca765bedSVenkateswara Naralasetty 				     struct ath11k_cfr_peer_tx_param *params)
302ca765bedSVenkateswara Naralasetty {
303ca765bedSVenkateswara Naralasetty 	struct ath11k_look_up_table *lut = NULL;
304ca765bedSVenkateswara Naralasetty 	u32 end_magic = ATH11K_CFR_END_MAGIC;
305ca765bedSVenkateswara Naralasetty 	struct ath11k_csi_cfr_header *header;
306ca765bedSVenkateswara Naralasetty 	struct ath11k_dbring_element *buff;
307ca765bedSVenkateswara Naralasetty 	struct ath11k_cfr *cfr;
308ca765bedSVenkateswara Naralasetty 	dma_addr_t buf_addr;
309ca765bedSVenkateswara Naralasetty 	struct ath11k *ar;
310ca765bedSVenkateswara Naralasetty 	u8 tx_status;
311ca765bedSVenkateswara Naralasetty 	int status;
312ca765bedSVenkateswara Naralasetty 	int i;
313ca765bedSVenkateswara Naralasetty 
314ca765bedSVenkateswara Naralasetty 	rcu_read_lock();
315ca765bedSVenkateswara Naralasetty 	ar = ath11k_mac_get_ar_by_vdev_id(ab, params->vdev_id);
316ca765bedSVenkateswara Naralasetty 	if (!ar) {
317ca765bedSVenkateswara Naralasetty 		rcu_read_unlock();
318ca765bedSVenkateswara Naralasetty 		ath11k_warn(ab, "Failed to get ar for vdev id %d\n",
319ca765bedSVenkateswara Naralasetty 			    params->vdev_id);
320ca765bedSVenkateswara Naralasetty 		return -ENOENT;
321ca765bedSVenkateswara Naralasetty 	}
322ca765bedSVenkateswara Naralasetty 
323ca765bedSVenkateswara Naralasetty 	cfr = &ar->cfr;
324ca765bedSVenkateswara Naralasetty 	rcu_read_unlock();
325ca765bedSVenkateswara Naralasetty 
326ca765bedSVenkateswara Naralasetty 	if (WMI_CFR_CAPTURE_STATUS_PEER_PS & params->status) {
327ca765bedSVenkateswara Naralasetty 		ath11k_warn(ab, "CFR capture failed as peer %pM is in powersave",
328ca765bedSVenkateswara Naralasetty 			    params->peer_mac_addr);
329ca765bedSVenkateswara Naralasetty 		return -EINVAL;
330ca765bedSVenkateswara Naralasetty 	}
331ca765bedSVenkateswara Naralasetty 
332ca765bedSVenkateswara Naralasetty 	if (!(WMI_CFR_PEER_CAPTURE_STATUS & params->status)) {
333ca765bedSVenkateswara Naralasetty 		ath11k_warn(ab, "CFR capture failed for the peer : %pM",
334ca765bedSVenkateswara Naralasetty 			    params->peer_mac_addr);
335ca765bedSVenkateswara Naralasetty 		cfr->tx_peer_status_cfr_fail++;
336ca765bedSVenkateswara Naralasetty 		return -EINVAL;
337ca765bedSVenkateswara Naralasetty 	}
338ca765bedSVenkateswara Naralasetty 
339ca765bedSVenkateswara Naralasetty 	tx_status = FIELD_GET(WMI_CFR_FRAME_TX_STATUS, params->status);
340ca765bedSVenkateswara Naralasetty 	if (tx_status != WMI_FRAME_TX_STATUS_OK) {
341ca765bedSVenkateswara Naralasetty 		ath11k_warn(ab, "WMI tx status %d for the peer %pM",
342ca765bedSVenkateswara Naralasetty 			    tx_status, params->peer_mac_addr);
343ca765bedSVenkateswara Naralasetty 		cfr->tx_evt_status_cfr_fail++;
344ca765bedSVenkateswara Naralasetty 		return -EINVAL;
345ca765bedSVenkateswara Naralasetty 	}
346ca765bedSVenkateswara Naralasetty 
347ca765bedSVenkateswara Naralasetty 	buf_addr = (((u64)FIELD_GET(WMI_CFR_CORRELATION_INFO2_BUF_ADDR_HIGH,
348ca765bedSVenkateswara Naralasetty 				    params->correlation_info_2)) << 32) |
349ca765bedSVenkateswara Naralasetty 		   params->correlation_info_1;
350ca765bedSVenkateswara Naralasetty 
351ca765bedSVenkateswara Naralasetty 	spin_lock_bh(&cfr->lut_lock);
352ca765bedSVenkateswara Naralasetty 
353ca765bedSVenkateswara Naralasetty 	if (!cfr->lut) {
354ca765bedSVenkateswara Naralasetty 		spin_unlock_bh(&cfr->lut_lock);
355ca765bedSVenkateswara Naralasetty 		return -EINVAL;
356ca765bedSVenkateswara Naralasetty 	}
357ca765bedSVenkateswara Naralasetty 
358ca765bedSVenkateswara Naralasetty 	for (i = 0; i < cfr->lut_num; i++) {
359ca765bedSVenkateswara Naralasetty 		struct ath11k_look_up_table *temp = &cfr->lut[i];
360ca765bedSVenkateswara Naralasetty 
361ca765bedSVenkateswara Naralasetty 		if (temp->dbr_address == buf_addr) {
362ca765bedSVenkateswara Naralasetty 			lut = &cfr->lut[i];
363ca765bedSVenkateswara Naralasetty 			break;
364ca765bedSVenkateswara Naralasetty 		}
365ca765bedSVenkateswara Naralasetty 	}
366ca765bedSVenkateswara Naralasetty 
367ca765bedSVenkateswara Naralasetty 	if (!lut) {
368ca765bedSVenkateswara Naralasetty 		spin_unlock_bh(&cfr->lut_lock);
369ca765bedSVenkateswara Naralasetty 		ath11k_warn(ab, "lut failure to process tx event\n");
370ca765bedSVenkateswara Naralasetty 		cfr->tx_dbr_lookup_fail++;
371ca765bedSVenkateswara Naralasetty 		return -EINVAL;
372ca765bedSVenkateswara Naralasetty 	}
373ca765bedSVenkateswara Naralasetty 
374ca765bedSVenkateswara Naralasetty 	lut->tx_ppdu_id = FIELD_GET(WMI_CFR_CORRELATION_INFO2_PPDU_ID,
375ca765bedSVenkateswara Naralasetty 				    params->correlation_info_2);
376ca765bedSVenkateswara Naralasetty 	lut->txrx_tstamp = jiffies;
377ca765bedSVenkateswara Naralasetty 
378ca765bedSVenkateswara Naralasetty 	header = &lut->header;
379ca765bedSVenkateswara Naralasetty 	header->start_magic_num = ATH11K_CFR_START_MAGIC;
380ca765bedSVenkateswara Naralasetty 	header->vendorid = VENDOR_QCA;
381ca765bedSVenkateswara Naralasetty 	header->platform_type = PLATFORM_TYPE_ARM;
382ca765bedSVenkateswara Naralasetty 
383ca765bedSVenkateswara Naralasetty 	ath11k_cfr_fill_hdr_info(ar, header, params);
384ca765bedSVenkateswara Naralasetty 
385ca765bedSVenkateswara Naralasetty 	status = ath11k_cfr_correlate_and_relay(ar, lut,
386ca765bedSVenkateswara Naralasetty 						ATH11K_CORRELATE_TX_EVENT);
387ca765bedSVenkateswara Naralasetty 	if (status == ATH11K_CORRELATE_STATUS_RELEASE) {
388ca765bedSVenkateswara Naralasetty 		ath11k_dbg(ab, ATH11K_DBG_CFR,
389ca765bedSVenkateswara Naralasetty 			   "Releasing CFR data to user space");
390ca765bedSVenkateswara Naralasetty 		ath11k_cfr_rfs_write(ar, &lut->header,
391ca765bedSVenkateswara Naralasetty 				     sizeof(struct ath11k_csi_cfr_header),
392ca765bedSVenkateswara Naralasetty 				     lut->data, lut->data_len,
393ca765bedSVenkateswara Naralasetty 				     &end_magic, sizeof(u32));
394ca765bedSVenkateswara Naralasetty 		buff = lut->buff;
395ca765bedSVenkateswara Naralasetty 		ath11k_cfr_release_lut_entry(lut);
396ca765bedSVenkateswara Naralasetty 
397ca765bedSVenkateswara Naralasetty 		ath11k_dbring_bufs_replenish(ar, &cfr->rx_ring, buff,
398ca765bedSVenkateswara Naralasetty 					     WMI_DIRECT_BUF_CFR);
399ca765bedSVenkateswara Naralasetty 	} else if (status == ATH11K_CORRELATE_STATUS_HOLD) {
400ca765bedSVenkateswara Naralasetty 		ath11k_dbg(ab, ATH11K_DBG_CFR,
401ca765bedSVenkateswara Naralasetty 			   "dbr event is not yet received holding buf\n");
402ca765bedSVenkateswara Naralasetty 	}
403ca765bedSVenkateswara Naralasetty 
404ca765bedSVenkateswara Naralasetty 	spin_unlock_bh(&cfr->lut_lock);
405ca765bedSVenkateswara Naralasetty 
406ca765bedSVenkateswara Naralasetty 	return 0;
407ca765bedSVenkateswara Naralasetty }
408ca765bedSVenkateswara Naralasetty 
409b3d43d89SVenkateswara Naralasetty /* Helper function to check whether the given peer mac address
410b3d43d89SVenkateswara Naralasetty  * is in unassociated peer pool or not.
411b3d43d89SVenkateswara Naralasetty  */
412b3d43d89SVenkateswara Naralasetty bool ath11k_cfr_peer_is_in_cfr_unassoc_pool(struct ath11k *ar, const u8 *peer_mac)
413b3d43d89SVenkateswara Naralasetty {
414b3d43d89SVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
415b3d43d89SVenkateswara Naralasetty 	struct cfr_unassoc_pool_entry *entry;
416b3d43d89SVenkateswara Naralasetty 	int i;
417b3d43d89SVenkateswara Naralasetty 
418b3d43d89SVenkateswara Naralasetty 	if (!ar->cfr_enabled)
419b3d43d89SVenkateswara Naralasetty 		return false;
420b3d43d89SVenkateswara Naralasetty 
421b3d43d89SVenkateswara Naralasetty 	spin_lock_bh(&cfr->lock);
422b3d43d89SVenkateswara Naralasetty 	for (i = 0; i < ATH11K_MAX_CFR_ENABLED_CLIENTS; i++) {
423b3d43d89SVenkateswara Naralasetty 		entry = &cfr->unassoc_pool[i];
424b3d43d89SVenkateswara Naralasetty 		if (!entry->is_valid)
425b3d43d89SVenkateswara Naralasetty 			continue;
426b3d43d89SVenkateswara Naralasetty 
427b3d43d89SVenkateswara Naralasetty 		if (ether_addr_equal(peer_mac, entry->peer_mac)) {
428b3d43d89SVenkateswara Naralasetty 			spin_unlock_bh(&cfr->lock);
429b3d43d89SVenkateswara Naralasetty 			return true;
430b3d43d89SVenkateswara Naralasetty 		}
431b3d43d89SVenkateswara Naralasetty 	}
432b3d43d89SVenkateswara Naralasetty 
433b3d43d89SVenkateswara Naralasetty 	spin_unlock_bh(&cfr->lock);
434b3d43d89SVenkateswara Naralasetty 
435b3d43d89SVenkateswara Naralasetty 	return false;
436b3d43d89SVenkateswara Naralasetty }
437b3d43d89SVenkateswara Naralasetty 
438b3d43d89SVenkateswara Naralasetty void ath11k_cfr_update_unassoc_pool_entry(struct ath11k *ar,
439b3d43d89SVenkateswara Naralasetty 					  const u8 *peer_mac)
440b3d43d89SVenkateswara Naralasetty {
441b3d43d89SVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
442b3d43d89SVenkateswara Naralasetty 	struct cfr_unassoc_pool_entry *entry;
443b3d43d89SVenkateswara Naralasetty 	int i;
444b3d43d89SVenkateswara Naralasetty 
445b3d43d89SVenkateswara Naralasetty 	spin_lock_bh(&cfr->lock);
446b3d43d89SVenkateswara Naralasetty 	for (i = 0; i < ATH11K_MAX_CFR_ENABLED_CLIENTS; i++) {
447b3d43d89SVenkateswara Naralasetty 		entry = &cfr->unassoc_pool[i];
448b3d43d89SVenkateswara Naralasetty 		if (!entry->is_valid)
449b3d43d89SVenkateswara Naralasetty 			continue;
450b3d43d89SVenkateswara Naralasetty 
451b3d43d89SVenkateswara Naralasetty 		if (ether_addr_equal(peer_mac, entry->peer_mac) &&
452b3d43d89SVenkateswara Naralasetty 		    entry->period == 0) {
453b3d43d89SVenkateswara Naralasetty 			memset(entry->peer_mac, 0, ETH_ALEN);
454b3d43d89SVenkateswara Naralasetty 			entry->is_valid = false;
455b3d43d89SVenkateswara Naralasetty 			cfr->cfr_enabled_peer_cnt--;
456b3d43d89SVenkateswara Naralasetty 			break;
457b3d43d89SVenkateswara Naralasetty 		}
458b3d43d89SVenkateswara Naralasetty 	}
459b3d43d89SVenkateswara Naralasetty 
460b3d43d89SVenkateswara Naralasetty 	spin_unlock_bh(&cfr->lock);
461b3d43d89SVenkateswara Naralasetty }
462b3d43d89SVenkateswara Naralasetty 
4639754d4baSVenkateswara Naralasetty void ath11k_cfr_decrement_peer_count(struct ath11k *ar,
4649754d4baSVenkateswara Naralasetty 				     struct ath11k_sta *arsta)
4659754d4baSVenkateswara Naralasetty {
4669754d4baSVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
4679754d4baSVenkateswara Naralasetty 
4689754d4baSVenkateswara Naralasetty 	spin_lock_bh(&cfr->lock);
4699754d4baSVenkateswara Naralasetty 
4709754d4baSVenkateswara Naralasetty 	if (arsta->cfr_capture.cfr_enable)
4719754d4baSVenkateswara Naralasetty 		cfr->cfr_enabled_peer_cnt--;
4729754d4baSVenkateswara Naralasetty 
4739754d4baSVenkateswara Naralasetty 	spin_unlock_bh(&cfr->lock);
4749754d4baSVenkateswara Naralasetty }
4759754d4baSVenkateswara Naralasetty 
4769754d4baSVenkateswara Naralasetty static enum ath11k_wmi_cfr_capture_bw
4779754d4baSVenkateswara Naralasetty ath11k_cfr_bw_to_fw_cfr_bw(enum ath11k_cfr_capture_bw bw)
4789754d4baSVenkateswara Naralasetty {
4799754d4baSVenkateswara Naralasetty 	switch (bw) {
4809754d4baSVenkateswara Naralasetty 	case ATH11K_CFR_CAPTURE_BW_20:
4819754d4baSVenkateswara Naralasetty 		return WMI_PEER_CFR_CAPTURE_BW_20;
4829754d4baSVenkateswara Naralasetty 	case ATH11K_CFR_CAPTURE_BW_40:
4839754d4baSVenkateswara Naralasetty 		return WMI_PEER_CFR_CAPTURE_BW_40;
4849754d4baSVenkateswara Naralasetty 	case ATH11K_CFR_CAPTURE_BW_80:
4859754d4baSVenkateswara Naralasetty 		return WMI_PEER_CFR_CAPTURE_BW_80;
4869754d4baSVenkateswara Naralasetty 	default:
4879754d4baSVenkateswara Naralasetty 		return WMI_PEER_CFR_CAPTURE_BW_MAX;
4889754d4baSVenkateswara Naralasetty 	}
4899754d4baSVenkateswara Naralasetty }
4909754d4baSVenkateswara Naralasetty 
4919754d4baSVenkateswara Naralasetty static enum ath11k_wmi_cfr_capture_method
4929754d4baSVenkateswara Naralasetty ath11k_cfr_method_to_fw_cfr_method(enum ath11k_cfr_capture_method method)
4939754d4baSVenkateswara Naralasetty {
4949754d4baSVenkateswara Naralasetty 	switch (method) {
4959754d4baSVenkateswara Naralasetty 	case ATH11K_CFR_CAPTURE_METHOD_NULL_FRAME:
4969754d4baSVenkateswara Naralasetty 		return WMI_CFR_CAPTURE_METHOD_NULL_FRAME;
4979754d4baSVenkateswara Naralasetty 	case ATH11K_CFR_CAPTURE_METHOD_NULL_FRAME_WITH_PHASE:
4989754d4baSVenkateswara Naralasetty 		return WMI_CFR_CAPTURE_METHOD_NULL_FRAME_WITH_PHASE;
4999754d4baSVenkateswara Naralasetty 	case ATH11K_CFR_CAPTURE_METHOD_PROBE_RESP:
5009754d4baSVenkateswara Naralasetty 		return WMI_CFR_CAPTURE_METHOD_PROBE_RESP;
5019754d4baSVenkateswara Naralasetty 	default:
5029754d4baSVenkateswara Naralasetty 		return WMI_CFR_CAPTURE_METHOD_MAX;
5039754d4baSVenkateswara Naralasetty 	}
5049754d4baSVenkateswara Naralasetty }
5059754d4baSVenkateswara Naralasetty 
5069754d4baSVenkateswara Naralasetty int ath11k_cfr_send_peer_cfr_capture_cmd(struct ath11k *ar,
5079754d4baSVenkateswara Naralasetty 					 struct ath11k_sta *arsta,
5089754d4baSVenkateswara Naralasetty 					 struct ath11k_per_peer_cfr_capture *params,
5099754d4baSVenkateswara Naralasetty 					 const u8 *peer_mac)
5109754d4baSVenkateswara Naralasetty {
5119754d4baSVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
5129754d4baSVenkateswara Naralasetty 	struct wmi_peer_cfr_capture_conf_arg arg;
5139754d4baSVenkateswara Naralasetty 	enum ath11k_wmi_cfr_capture_bw bw;
5149754d4baSVenkateswara Naralasetty 	enum ath11k_wmi_cfr_capture_method method;
5159754d4baSVenkateswara Naralasetty 	int ret = 0;
5169754d4baSVenkateswara Naralasetty 
5179754d4baSVenkateswara Naralasetty 	if (cfr->cfr_enabled_peer_cnt >= ATH11K_MAX_CFR_ENABLED_CLIENTS &&
5189754d4baSVenkateswara Naralasetty 	    !arsta->cfr_capture.cfr_enable) {
5199754d4baSVenkateswara Naralasetty 		ath11k_err(ar->ab, "CFR enable peer threshold reached %u\n",
5209754d4baSVenkateswara Naralasetty 			   cfr->cfr_enabled_peer_cnt);
5219754d4baSVenkateswara Naralasetty 		return -ENOSPC;
5229754d4baSVenkateswara Naralasetty 	}
5239754d4baSVenkateswara Naralasetty 
5249754d4baSVenkateswara Naralasetty 	if (params->cfr_enable == arsta->cfr_capture.cfr_enable &&
5259754d4baSVenkateswara Naralasetty 	    params->cfr_period == arsta->cfr_capture.cfr_period &&
5269754d4baSVenkateswara Naralasetty 	    params->cfr_method == arsta->cfr_capture.cfr_method &&
5279754d4baSVenkateswara Naralasetty 	    params->cfr_bw == arsta->cfr_capture.cfr_bw)
5289754d4baSVenkateswara Naralasetty 		return ret;
5299754d4baSVenkateswara Naralasetty 
5309754d4baSVenkateswara Naralasetty 	if (!params->cfr_enable && !arsta->cfr_capture.cfr_enable)
5319754d4baSVenkateswara Naralasetty 		return ret;
5329754d4baSVenkateswara Naralasetty 
5339754d4baSVenkateswara Naralasetty 	bw = ath11k_cfr_bw_to_fw_cfr_bw(params->cfr_bw);
5349754d4baSVenkateswara Naralasetty 	if (bw >= WMI_PEER_CFR_CAPTURE_BW_MAX) {
5359754d4baSVenkateswara Naralasetty 		ath11k_warn(ar->ab, "FW doesn't support configured bw %d\n",
5369754d4baSVenkateswara Naralasetty 			    params->cfr_bw);
5379754d4baSVenkateswara Naralasetty 		return -EINVAL;
5389754d4baSVenkateswara Naralasetty 	}
5399754d4baSVenkateswara Naralasetty 
5409754d4baSVenkateswara Naralasetty 	method = ath11k_cfr_method_to_fw_cfr_method(params->cfr_method);
5419754d4baSVenkateswara Naralasetty 	if (method >= WMI_CFR_CAPTURE_METHOD_MAX) {
5429754d4baSVenkateswara Naralasetty 		ath11k_warn(ar->ab, "FW doesn't support configured method %d\n",
5439754d4baSVenkateswara Naralasetty 			    params->cfr_method);
5449754d4baSVenkateswara Naralasetty 		return -EINVAL;
5459754d4baSVenkateswara Naralasetty 	}
5469754d4baSVenkateswara Naralasetty 
5479754d4baSVenkateswara Naralasetty 	arg.request = params->cfr_enable;
5489754d4baSVenkateswara Naralasetty 	arg.periodicity = params->cfr_period;
5499754d4baSVenkateswara Naralasetty 	arg.bw = bw;
5509754d4baSVenkateswara Naralasetty 	arg.method = method;
5519754d4baSVenkateswara Naralasetty 
5529754d4baSVenkateswara Naralasetty 	ret = ath11k_wmi_peer_set_cfr_capture_conf(ar, arsta->arvif->vdev_id,
5539754d4baSVenkateswara Naralasetty 						   peer_mac, &arg);
5549754d4baSVenkateswara Naralasetty 	if (ret) {
5559754d4baSVenkateswara Naralasetty 		ath11k_warn(ar->ab,
5569754d4baSVenkateswara Naralasetty 			    "failed to send cfr capture info: vdev_id %u peer %pM: %d\n",
5579754d4baSVenkateswara Naralasetty 			    arsta->arvif->vdev_id, peer_mac, ret);
5589754d4baSVenkateswara Naralasetty 		return ret;
5599754d4baSVenkateswara Naralasetty 	}
5609754d4baSVenkateswara Naralasetty 
5619754d4baSVenkateswara Naralasetty 	spin_lock_bh(&cfr->lock);
5629754d4baSVenkateswara Naralasetty 
5639754d4baSVenkateswara Naralasetty 	if (params->cfr_enable &&
5649754d4baSVenkateswara Naralasetty 	    params->cfr_enable != arsta->cfr_capture.cfr_enable)
5659754d4baSVenkateswara Naralasetty 		cfr->cfr_enabled_peer_cnt++;
5669754d4baSVenkateswara Naralasetty 	else if (!params->cfr_enable)
5679754d4baSVenkateswara Naralasetty 		cfr->cfr_enabled_peer_cnt--;
5689754d4baSVenkateswara Naralasetty 
5699754d4baSVenkateswara Naralasetty 	spin_unlock_bh(&cfr->lock);
5709754d4baSVenkateswara Naralasetty 
5719754d4baSVenkateswara Naralasetty 	arsta->cfr_capture.cfr_enable = params->cfr_enable;
5729754d4baSVenkateswara Naralasetty 	arsta->cfr_capture.cfr_period = params->cfr_period;
5739754d4baSVenkateswara Naralasetty 	arsta->cfr_capture.cfr_method = params->cfr_method;
5749754d4baSVenkateswara Naralasetty 	arsta->cfr_capture.cfr_bw = params->cfr_bw;
5759754d4baSVenkateswara Naralasetty 
5769754d4baSVenkateswara Naralasetty 	return ret;
5779754d4baSVenkateswara Naralasetty }
5789754d4baSVenkateswara Naralasetty 
579b3d43d89SVenkateswara Naralasetty void ath11k_cfr_update_unassoc_pool(struct ath11k *ar,
580b3d43d89SVenkateswara Naralasetty 				    struct ath11k_per_peer_cfr_capture *params,
581b3d43d89SVenkateswara Naralasetty 				    u8 *peer_mac)
582b3d43d89SVenkateswara Naralasetty {
583b3d43d89SVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
584b3d43d89SVenkateswara Naralasetty 	struct cfr_unassoc_pool_entry *entry;
585b3d43d89SVenkateswara Naralasetty 	int available_idx = -1;
586b3d43d89SVenkateswara Naralasetty 	int i;
587b3d43d89SVenkateswara Naralasetty 
588b3d43d89SVenkateswara Naralasetty 	guard(spinlock_bh)(&cfr->lock);
589b3d43d89SVenkateswara Naralasetty 
590b3d43d89SVenkateswara Naralasetty 	if (!params->cfr_enable) {
591b3d43d89SVenkateswara Naralasetty 		for (i = 0; i < ATH11K_MAX_CFR_ENABLED_CLIENTS; i++) {
592b3d43d89SVenkateswara Naralasetty 			entry = &cfr->unassoc_pool[i];
593b3d43d89SVenkateswara Naralasetty 			if (ether_addr_equal(peer_mac, entry->peer_mac)) {
594b3d43d89SVenkateswara Naralasetty 				memset(entry->peer_mac, 0, ETH_ALEN);
595b3d43d89SVenkateswara Naralasetty 				entry->is_valid = false;
596b3d43d89SVenkateswara Naralasetty 				cfr->cfr_enabled_peer_cnt--;
597b3d43d89SVenkateswara Naralasetty 				break;
598b3d43d89SVenkateswara Naralasetty 			}
599b3d43d89SVenkateswara Naralasetty 		}
600b3d43d89SVenkateswara Naralasetty 		return;
601b3d43d89SVenkateswara Naralasetty 	}
602b3d43d89SVenkateswara Naralasetty 
603b3d43d89SVenkateswara Naralasetty 	if (cfr->cfr_enabled_peer_cnt >= ATH11K_MAX_CFR_ENABLED_CLIENTS) {
604b3d43d89SVenkateswara Naralasetty 		ath11k_info(ar->ab, "Max cfr peer threshold reached\n");
605b3d43d89SVenkateswara Naralasetty 		return;
606b3d43d89SVenkateswara Naralasetty 	}
607b3d43d89SVenkateswara Naralasetty 
608b3d43d89SVenkateswara Naralasetty 	for (i = 0; i < ATH11K_MAX_CFR_ENABLED_CLIENTS; i++) {
609b3d43d89SVenkateswara Naralasetty 		entry = &cfr->unassoc_pool[i];
610b3d43d89SVenkateswara Naralasetty 
611b3d43d89SVenkateswara Naralasetty 		if (ether_addr_equal(peer_mac, entry->peer_mac)) {
612b3d43d89SVenkateswara Naralasetty 			ath11k_info(ar->ab,
613b3d43d89SVenkateswara Naralasetty 				    "peer entry already present updating params\n");
614b3d43d89SVenkateswara Naralasetty 			entry->period = params->cfr_period;
615b3d43d89SVenkateswara Naralasetty 			available_idx = -1;
616b3d43d89SVenkateswara Naralasetty 			break;
617b3d43d89SVenkateswara Naralasetty 		}
618b3d43d89SVenkateswara Naralasetty 
619b3d43d89SVenkateswara Naralasetty 		if (available_idx < 0 && !entry->is_valid)
620b3d43d89SVenkateswara Naralasetty 			available_idx = i;
621b3d43d89SVenkateswara Naralasetty 	}
622b3d43d89SVenkateswara Naralasetty 
623b3d43d89SVenkateswara Naralasetty 	if (available_idx >= 0) {
624b3d43d89SVenkateswara Naralasetty 		entry = &cfr->unassoc_pool[available_idx];
625b3d43d89SVenkateswara Naralasetty 		ether_addr_copy(entry->peer_mac, peer_mac);
626b3d43d89SVenkateswara Naralasetty 		entry->period = params->cfr_period;
627b3d43d89SVenkateswara Naralasetty 		entry->is_valid = true;
628b3d43d89SVenkateswara Naralasetty 		cfr->cfr_enabled_peer_cnt++;
629b3d43d89SVenkateswara Naralasetty 	}
630b3d43d89SVenkateswara Naralasetty }
631b3d43d89SVenkateswara Naralasetty 
6329754d4baSVenkateswara Naralasetty static ssize_t ath11k_read_file_enable_cfr(struct file *file,
6339754d4baSVenkateswara Naralasetty 					   char __user *user_buf,
6349754d4baSVenkateswara Naralasetty 					   size_t count, loff_t *ppos)
6359754d4baSVenkateswara Naralasetty {
6369754d4baSVenkateswara Naralasetty 	struct ath11k *ar = file->private_data;
6379754d4baSVenkateswara Naralasetty 	char buf[32] = {};
6389754d4baSVenkateswara Naralasetty 	size_t len;
6399754d4baSVenkateswara Naralasetty 
6409754d4baSVenkateswara Naralasetty 	mutex_lock(&ar->conf_mutex);
6419754d4baSVenkateswara Naralasetty 	len = scnprintf(buf, sizeof(buf), "%d\n", ar->cfr_enabled);
6429754d4baSVenkateswara Naralasetty 	mutex_unlock(&ar->conf_mutex);
6439754d4baSVenkateswara Naralasetty 
6449754d4baSVenkateswara Naralasetty 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
6459754d4baSVenkateswara Naralasetty }
6469754d4baSVenkateswara Naralasetty 
6479754d4baSVenkateswara Naralasetty static ssize_t ath11k_write_file_enable_cfr(struct file *file,
6489754d4baSVenkateswara Naralasetty 					    const char __user *ubuf,
6499754d4baSVenkateswara Naralasetty 					    size_t count, loff_t *ppos)
6509754d4baSVenkateswara Naralasetty {
6519754d4baSVenkateswara Naralasetty 	struct ath11k *ar = file->private_data;
6529754d4baSVenkateswara Naralasetty 	u32 enable_cfr;
6539754d4baSVenkateswara Naralasetty 	int ret;
6549754d4baSVenkateswara Naralasetty 
6559754d4baSVenkateswara Naralasetty 	if (kstrtouint_from_user(ubuf, count, 0, &enable_cfr))
6569754d4baSVenkateswara Naralasetty 		return -EINVAL;
6579754d4baSVenkateswara Naralasetty 
6589754d4baSVenkateswara Naralasetty 	guard(mutex)(&ar->conf_mutex);
6599754d4baSVenkateswara Naralasetty 
6609754d4baSVenkateswara Naralasetty 	if (ar->state != ATH11K_STATE_ON)
6619754d4baSVenkateswara Naralasetty 		return -ENETDOWN;
6629754d4baSVenkateswara Naralasetty 
6639754d4baSVenkateswara Naralasetty 	if (enable_cfr > 1)
6649754d4baSVenkateswara Naralasetty 		return -EINVAL;
6659754d4baSVenkateswara Naralasetty 
6669754d4baSVenkateswara Naralasetty 	if (ar->cfr_enabled == enable_cfr)
6679754d4baSVenkateswara Naralasetty 		return count;
6689754d4baSVenkateswara Naralasetty 
6699754d4baSVenkateswara Naralasetty 	ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PER_PEER_CFR_ENABLE,
6709754d4baSVenkateswara Naralasetty 					enable_cfr, ar->pdev->pdev_id);
6719754d4baSVenkateswara Naralasetty 	if (ret) {
6729754d4baSVenkateswara Naralasetty 		ath11k_warn(ar->ab,
6739754d4baSVenkateswara Naralasetty 			    "Failed to enable/disable per peer cfr %d\n", ret);
6749754d4baSVenkateswara Naralasetty 		return ret;
6759754d4baSVenkateswara Naralasetty 	}
6769754d4baSVenkateswara Naralasetty 
6779754d4baSVenkateswara Naralasetty 	ar->cfr_enabled = enable_cfr;
6789754d4baSVenkateswara Naralasetty 
6799754d4baSVenkateswara Naralasetty 	return count;
6809754d4baSVenkateswara Naralasetty }
6819754d4baSVenkateswara Naralasetty 
6829754d4baSVenkateswara Naralasetty static const struct file_operations fops_enable_cfr = {
6839754d4baSVenkateswara Naralasetty 	.read = ath11k_read_file_enable_cfr,
6849754d4baSVenkateswara Naralasetty 	.write = ath11k_write_file_enable_cfr,
6859754d4baSVenkateswara Naralasetty 	.open = simple_open,
6869754d4baSVenkateswara Naralasetty 	.owner = THIS_MODULE,
6879754d4baSVenkateswara Naralasetty 	.llseek = default_llseek,
6889754d4baSVenkateswara Naralasetty };
6899754d4baSVenkateswara Naralasetty 
690b3d43d89SVenkateswara Naralasetty static ssize_t ath11k_write_file_cfr_unassoc(struct file *file,
691b3d43d89SVenkateswara Naralasetty 					     const char __user *ubuf,
692b3d43d89SVenkateswara Naralasetty 					     size_t count, loff_t *ppos)
693b3d43d89SVenkateswara Naralasetty {
694b3d43d89SVenkateswara Naralasetty 	struct ath11k *ar = file->private_data;
695b3d43d89SVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
696b3d43d89SVenkateswara Naralasetty 	struct cfr_unassoc_pool_entry *entry;
697b3d43d89SVenkateswara Naralasetty 	char buf[64] = {};
698b3d43d89SVenkateswara Naralasetty 	u8 peer_mac[6];
699b3d43d89SVenkateswara Naralasetty 	u32 cfr_capture_enable;
700b3d43d89SVenkateswara Naralasetty 	u32 cfr_capture_period;
701b3d43d89SVenkateswara Naralasetty 	int available_idx = -1;
702b3d43d89SVenkateswara Naralasetty 	int ret, i;
703b3d43d89SVenkateswara Naralasetty 
704b3d43d89SVenkateswara Naralasetty 	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
705b3d43d89SVenkateswara Naralasetty 
706b3d43d89SVenkateswara Naralasetty 	guard(mutex)(&ar->conf_mutex);
707b3d43d89SVenkateswara Naralasetty 	guard(spinlock_bh)(&cfr->lock);
708b3d43d89SVenkateswara Naralasetty 
709b3d43d89SVenkateswara Naralasetty 	if (ar->state != ATH11K_STATE_ON)
710b3d43d89SVenkateswara Naralasetty 		return -ENETDOWN;
711b3d43d89SVenkateswara Naralasetty 
712b3d43d89SVenkateswara Naralasetty 	if (!ar->cfr_enabled) {
713b3d43d89SVenkateswara Naralasetty 		ath11k_err(ar->ab, "CFR is not enabled on this pdev %d\n",
714b3d43d89SVenkateswara Naralasetty 			   ar->pdev_idx);
715b3d43d89SVenkateswara Naralasetty 		return -EINVAL;
716b3d43d89SVenkateswara Naralasetty 	}
717b3d43d89SVenkateswara Naralasetty 
718b3d43d89SVenkateswara Naralasetty 	ret = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %u %u",
719b3d43d89SVenkateswara Naralasetty 		     &peer_mac[0], &peer_mac[1], &peer_mac[2], &peer_mac[3],
720b3d43d89SVenkateswara Naralasetty 		     &peer_mac[4], &peer_mac[5], &cfr_capture_enable,
721b3d43d89SVenkateswara Naralasetty 		     &cfr_capture_period);
722b3d43d89SVenkateswara Naralasetty 
723b3d43d89SVenkateswara Naralasetty 	if (ret < 1)
724b3d43d89SVenkateswara Naralasetty 		return -EINVAL;
725b3d43d89SVenkateswara Naralasetty 
726b3d43d89SVenkateswara Naralasetty 	if (cfr_capture_enable && ret != 8)
727b3d43d89SVenkateswara Naralasetty 		return -EINVAL;
728b3d43d89SVenkateswara Naralasetty 
729b3d43d89SVenkateswara Naralasetty 	if (!cfr_capture_enable) {
730b3d43d89SVenkateswara Naralasetty 		for (i = 0; i < ATH11K_MAX_CFR_ENABLED_CLIENTS; i++) {
731b3d43d89SVenkateswara Naralasetty 			entry = &cfr->unassoc_pool[i];
732b3d43d89SVenkateswara Naralasetty 			if (ether_addr_equal(peer_mac, entry->peer_mac)) {
733b3d43d89SVenkateswara Naralasetty 				memset(entry->peer_mac, 0, ETH_ALEN);
734b3d43d89SVenkateswara Naralasetty 				entry->is_valid = false;
735b3d43d89SVenkateswara Naralasetty 				cfr->cfr_enabled_peer_cnt--;
736b3d43d89SVenkateswara Naralasetty 			}
737b3d43d89SVenkateswara Naralasetty 		}
738b3d43d89SVenkateswara Naralasetty 
739b3d43d89SVenkateswara Naralasetty 		return count;
740b3d43d89SVenkateswara Naralasetty 	}
741b3d43d89SVenkateswara Naralasetty 
742b3d43d89SVenkateswara Naralasetty 	if (cfr->cfr_enabled_peer_cnt >= ATH11K_MAX_CFR_ENABLED_CLIENTS) {
743b3d43d89SVenkateswara Naralasetty 		ath11k_info(ar->ab, "Max cfr peer threshold reached\n");
744b3d43d89SVenkateswara Naralasetty 		return count;
745b3d43d89SVenkateswara Naralasetty 	}
746b3d43d89SVenkateswara Naralasetty 
747b3d43d89SVenkateswara Naralasetty 	for (i = 0; i < ATH11K_MAX_CFR_ENABLED_CLIENTS; i++) {
748b3d43d89SVenkateswara Naralasetty 		entry = &cfr->unassoc_pool[i];
749b3d43d89SVenkateswara Naralasetty 
750b3d43d89SVenkateswara Naralasetty 		if (available_idx < 0 && !entry->is_valid)
751b3d43d89SVenkateswara Naralasetty 			available_idx = i;
752b3d43d89SVenkateswara Naralasetty 
753b3d43d89SVenkateswara Naralasetty 		if (ether_addr_equal(peer_mac, entry->peer_mac)) {
754b3d43d89SVenkateswara Naralasetty 			ath11k_info(ar->ab,
755b3d43d89SVenkateswara Naralasetty 				    "peer entry already present updating params\n");
756b3d43d89SVenkateswara Naralasetty 			entry->period = cfr_capture_period;
757b3d43d89SVenkateswara Naralasetty 			return count;
758b3d43d89SVenkateswara Naralasetty 		}
759b3d43d89SVenkateswara Naralasetty 	}
760b3d43d89SVenkateswara Naralasetty 
761b3d43d89SVenkateswara Naralasetty 	if (available_idx >= 0) {
762b3d43d89SVenkateswara Naralasetty 		entry = &cfr->unassoc_pool[available_idx];
763b3d43d89SVenkateswara Naralasetty 		ether_addr_copy(entry->peer_mac, peer_mac);
764b3d43d89SVenkateswara Naralasetty 		entry->period = cfr_capture_period;
765b3d43d89SVenkateswara Naralasetty 		entry->is_valid = true;
766b3d43d89SVenkateswara Naralasetty 		cfr->cfr_enabled_peer_cnt++;
767b3d43d89SVenkateswara Naralasetty 	}
768b3d43d89SVenkateswara Naralasetty 
769b3d43d89SVenkateswara Naralasetty 	return count;
770b3d43d89SVenkateswara Naralasetty }
771b3d43d89SVenkateswara Naralasetty 
772b3d43d89SVenkateswara Naralasetty static ssize_t ath11k_read_file_cfr_unassoc(struct file *file,
773b3d43d89SVenkateswara Naralasetty 					    char __user *ubuf,
774b3d43d89SVenkateswara Naralasetty 					    size_t count, loff_t *ppos)
775b3d43d89SVenkateswara Naralasetty {
776b3d43d89SVenkateswara Naralasetty 	struct ath11k *ar = file->private_data;
777b3d43d89SVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
778b3d43d89SVenkateswara Naralasetty 	struct cfr_unassoc_pool_entry *entry;
779b3d43d89SVenkateswara Naralasetty 	char buf[512] = {};
780b3d43d89SVenkateswara Naralasetty 	int len = 0, i;
781b3d43d89SVenkateswara Naralasetty 
782b3d43d89SVenkateswara Naralasetty 	spin_lock_bh(&cfr->lock);
783b3d43d89SVenkateswara Naralasetty 
784b3d43d89SVenkateswara Naralasetty 	for (i = 0; i < ATH11K_MAX_CFR_ENABLED_CLIENTS; i++) {
785b3d43d89SVenkateswara Naralasetty 		entry = &cfr->unassoc_pool[i];
786b3d43d89SVenkateswara Naralasetty 		if (entry->is_valid)
787b3d43d89SVenkateswara Naralasetty 			len += scnprintf(buf + len, sizeof(buf) - len,
788b3d43d89SVenkateswara Naralasetty 					 "peer: %pM period: %u\n",
789b3d43d89SVenkateswara Naralasetty 					 entry->peer_mac, entry->period);
790b3d43d89SVenkateswara Naralasetty 	}
791b3d43d89SVenkateswara Naralasetty 
792b3d43d89SVenkateswara Naralasetty 	spin_unlock_bh(&cfr->lock);
793b3d43d89SVenkateswara Naralasetty 
794b3d43d89SVenkateswara Naralasetty 	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
795b3d43d89SVenkateswara Naralasetty }
796b3d43d89SVenkateswara Naralasetty 
797b3d43d89SVenkateswara Naralasetty static const struct file_operations fops_configure_cfr_unassoc = {
798b3d43d89SVenkateswara Naralasetty 	.write = ath11k_write_file_cfr_unassoc,
799b3d43d89SVenkateswara Naralasetty 	.read = ath11k_read_file_cfr_unassoc,
800b3d43d89SVenkateswara Naralasetty 	.open = simple_open,
801b3d43d89SVenkateswara Naralasetty 	.owner = THIS_MODULE,
802b3d43d89SVenkateswara Naralasetty 	.llseek = default_llseek,
803b3d43d89SVenkateswara Naralasetty };
804b3d43d89SVenkateswara Naralasetty 
8059754d4baSVenkateswara Naralasetty static void ath11k_cfr_debug_unregister(struct ath11k *ar)
8069754d4baSVenkateswara Naralasetty {
8079754d4baSVenkateswara Naralasetty 	debugfs_remove(ar->cfr.enable_cfr);
8089754d4baSVenkateswara Naralasetty 	ar->cfr.enable_cfr = NULL;
809b3d43d89SVenkateswara Naralasetty 	debugfs_remove(ar->cfr.cfr_unassoc);
810b3d43d89SVenkateswara Naralasetty 	ar->cfr.cfr_unassoc = NULL;
811c1bf6959SVenkateswara Naralasetty 
812c1bf6959SVenkateswara Naralasetty 	relay_close(ar->cfr.rfs_cfr_capture);
813c1bf6959SVenkateswara Naralasetty 	ar->cfr.rfs_cfr_capture = NULL;
8149754d4baSVenkateswara Naralasetty }
8159754d4baSVenkateswara Naralasetty 
816c1bf6959SVenkateswara Naralasetty static struct dentry *ath11k_cfr_create_buf_file_handler(const char *filename,
817c1bf6959SVenkateswara Naralasetty 							 struct dentry *parent,
818c1bf6959SVenkateswara Naralasetty 							 umode_t mode,
819c1bf6959SVenkateswara Naralasetty 							 struct rchan_buf *buf,
820c1bf6959SVenkateswara Naralasetty 							 int *is_global)
821c1bf6959SVenkateswara Naralasetty {
822c1bf6959SVenkateswara Naralasetty 	struct dentry *buf_file;
823c1bf6959SVenkateswara Naralasetty 
824c1bf6959SVenkateswara Naralasetty 	buf_file = debugfs_create_file(filename, mode, parent, buf,
825c1bf6959SVenkateswara Naralasetty 				       &relay_file_operations);
826c1bf6959SVenkateswara Naralasetty 	*is_global = 1;
827c1bf6959SVenkateswara Naralasetty 	return buf_file;
828c1bf6959SVenkateswara Naralasetty }
829c1bf6959SVenkateswara Naralasetty 
830c1bf6959SVenkateswara Naralasetty static int ath11k_cfr_remove_buf_file_handler(struct dentry *dentry)
831c1bf6959SVenkateswara Naralasetty {
832c1bf6959SVenkateswara Naralasetty 	debugfs_remove(dentry);
833c1bf6959SVenkateswara Naralasetty 
834c1bf6959SVenkateswara Naralasetty 	return 0;
835c1bf6959SVenkateswara Naralasetty }
836c1bf6959SVenkateswara Naralasetty 
837c1bf6959SVenkateswara Naralasetty static const struct rchan_callbacks rfs_cfr_capture_cb = {
838c1bf6959SVenkateswara Naralasetty 	.create_buf_file = ath11k_cfr_create_buf_file_handler,
839c1bf6959SVenkateswara Naralasetty 	.remove_buf_file = ath11k_cfr_remove_buf_file_handler,
840c1bf6959SVenkateswara Naralasetty };
841c1bf6959SVenkateswara Naralasetty 
8429754d4baSVenkateswara Naralasetty static void ath11k_cfr_debug_register(struct ath11k *ar)
8439754d4baSVenkateswara Naralasetty {
844c1bf6959SVenkateswara Naralasetty 	ar->cfr.rfs_cfr_capture = relay_open("cfr_capture",
845c1bf6959SVenkateswara Naralasetty 					     ar->debug.debugfs_pdev,
846c1bf6959SVenkateswara Naralasetty 					     ar->ab->hw_params.cfr_stream_buf_size,
847c1bf6959SVenkateswara Naralasetty 					     ar->ab->hw_params.cfr_num_stream_bufs,
848c1bf6959SVenkateswara Naralasetty 					     &rfs_cfr_capture_cb, NULL);
849c1bf6959SVenkateswara Naralasetty 
8509754d4baSVenkateswara Naralasetty 	ar->cfr.enable_cfr = debugfs_create_file("enable_cfr", 0600,
8519754d4baSVenkateswara Naralasetty 						 ar->debug.debugfs_pdev, ar,
8529754d4baSVenkateswara Naralasetty 						 &fops_enable_cfr);
853b3d43d89SVenkateswara Naralasetty 
854b3d43d89SVenkateswara Naralasetty 	ar->cfr.cfr_unassoc = debugfs_create_file("cfr_unassoc", 0600,
855b3d43d89SVenkateswara Naralasetty 						  ar->debug.debugfs_pdev, ar,
856b3d43d89SVenkateswara Naralasetty 						  &fops_configure_cfr_unassoc);
8579754d4baSVenkateswara Naralasetty }
8589754d4baSVenkateswara Naralasetty 
8599b2e3b4eSVenkateswara Naralasetty void ath11k_cfr_lut_update_paddr(struct ath11k *ar, dma_addr_t paddr,
8609b2e3b4eSVenkateswara Naralasetty 				 u32 buf_id)
8619b2e3b4eSVenkateswara Naralasetty {
8629b2e3b4eSVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
8639b2e3b4eSVenkateswara Naralasetty 
8649b2e3b4eSVenkateswara Naralasetty 	if (cfr->lut)
8659b2e3b4eSVenkateswara Naralasetty 		cfr->lut[buf_id].dbr_address = paddr;
8669b2e3b4eSVenkateswara Naralasetty }
8679b2e3b4eSVenkateswara Naralasetty 
868ca765bedSVenkateswara Naralasetty void ath11k_cfr_update_phymode(struct ath11k *ar, enum wmi_phy_mode phymode)
869ca765bedSVenkateswara Naralasetty {
870ca765bedSVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
871ca765bedSVenkateswara Naralasetty 
872ca765bedSVenkateswara Naralasetty 	cfr->phymode = phymode;
873ca765bedSVenkateswara Naralasetty }
874ca765bedSVenkateswara Naralasetty 
8759b2e3b4eSVenkateswara Naralasetty static void ath11k_cfr_ring_free(struct ath11k *ar)
8769b2e3b4eSVenkateswara Naralasetty {
8779b2e3b4eSVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
8789b2e3b4eSVenkateswara Naralasetty 
8799b2e3b4eSVenkateswara Naralasetty 	ath11k_dbring_buf_cleanup(ar, &cfr->rx_ring);
8809b2e3b4eSVenkateswara Naralasetty 	ath11k_dbring_srng_cleanup(ar, &cfr->rx_ring);
8819b2e3b4eSVenkateswara Naralasetty }
8829b2e3b4eSVenkateswara Naralasetty 
8839b2e3b4eSVenkateswara Naralasetty static int ath11k_cfr_ring_alloc(struct ath11k *ar,
8849b2e3b4eSVenkateswara Naralasetty 				 struct ath11k_dbring_cap *db_cap)
8859b2e3b4eSVenkateswara Naralasetty {
8869b2e3b4eSVenkateswara Naralasetty 	struct ath11k_cfr *cfr = &ar->cfr;
8879b2e3b4eSVenkateswara Naralasetty 	int ret;
8889b2e3b4eSVenkateswara Naralasetty 
8899b2e3b4eSVenkateswara Naralasetty 	ret = ath11k_dbring_srng_setup(ar, &cfr->rx_ring,
8909b2e3b4eSVenkateswara Naralasetty 				       ATH11K_CFR_NUM_RING_ENTRIES,
8919b2e3b4eSVenkateswara Naralasetty 				       db_cap->min_elem);
8929b2e3b4eSVenkateswara Naralasetty 	if (ret) {
8939b2e3b4eSVenkateswara Naralasetty 		ath11k_warn(ar->ab, "failed to setup db ring: %d\n", ret);
8949b2e3b4eSVenkateswara Naralasetty 		return ret;
8959b2e3b4eSVenkateswara Naralasetty 	}
8969b2e3b4eSVenkateswara Naralasetty 
8979b2e3b4eSVenkateswara Naralasetty 	ath11k_dbring_set_cfg(ar, &cfr->rx_ring,
8989b2e3b4eSVenkateswara Naralasetty 			      ATH11K_CFR_NUM_RESP_PER_EVENT,
8999b2e3b4eSVenkateswara Naralasetty 			      ATH11K_CFR_EVENT_TIMEOUT_MS,
9009b2e3b4eSVenkateswara Naralasetty 			      ath11k_cfr_process_data);
9019b2e3b4eSVenkateswara Naralasetty 
9029b2e3b4eSVenkateswara Naralasetty 	ret = ath11k_dbring_buf_setup(ar, &cfr->rx_ring, db_cap);
9039b2e3b4eSVenkateswara Naralasetty 	if (ret) {
9049b2e3b4eSVenkateswara Naralasetty 		ath11k_warn(ar->ab, "failed to setup db ring buffer: %d\n", ret);
9059b2e3b4eSVenkateswara Naralasetty 		goto srng_cleanup;
9069b2e3b4eSVenkateswara Naralasetty 	}
9079b2e3b4eSVenkateswara Naralasetty 
9089b2e3b4eSVenkateswara Naralasetty 	ret = ath11k_dbring_wmi_cfg_setup(ar, &cfr->rx_ring, WMI_DIRECT_BUF_CFR);
9099b2e3b4eSVenkateswara Naralasetty 	if (ret) {
9109b2e3b4eSVenkateswara Naralasetty 		ath11k_warn(ar->ab, "failed to setup db ring cfg: %d\n", ret);
9119b2e3b4eSVenkateswara Naralasetty 		goto buffer_cleanup;
9129b2e3b4eSVenkateswara Naralasetty 	}
9139b2e3b4eSVenkateswara Naralasetty 
9149b2e3b4eSVenkateswara Naralasetty 	return 0;
9159b2e3b4eSVenkateswara Naralasetty 
9169b2e3b4eSVenkateswara Naralasetty buffer_cleanup:
9179b2e3b4eSVenkateswara Naralasetty 	ath11k_dbring_buf_cleanup(ar, &cfr->rx_ring);
9189b2e3b4eSVenkateswara Naralasetty srng_cleanup:
9199b2e3b4eSVenkateswara Naralasetty 	ath11k_dbring_srng_cleanup(ar, &cfr->rx_ring);
9209b2e3b4eSVenkateswara Naralasetty 	return ret;
9219b2e3b4eSVenkateswara Naralasetty }
9229b2e3b4eSVenkateswara Naralasetty 
9239b2e3b4eSVenkateswara Naralasetty void ath11k_cfr_deinit(struct ath11k_base *ab)
9249b2e3b4eSVenkateswara Naralasetty {
9259b2e3b4eSVenkateswara Naralasetty 	struct ath11k_cfr *cfr;
9269b2e3b4eSVenkateswara Naralasetty 	struct ath11k *ar;
9279b2e3b4eSVenkateswara Naralasetty 	int i;
9289b2e3b4eSVenkateswara Naralasetty 
9299b2e3b4eSVenkateswara Naralasetty 	if (!test_bit(WMI_TLV_SERVICE_CFR_CAPTURE_SUPPORT, ab->wmi_ab.svc_map) ||
9309b2e3b4eSVenkateswara Naralasetty 	    !ab->hw_params.cfr_support)
9319b2e3b4eSVenkateswara Naralasetty 		return;
9329b2e3b4eSVenkateswara Naralasetty 
9339b2e3b4eSVenkateswara Naralasetty 	for (i = 0; i <  ab->num_radios; i++) {
9349b2e3b4eSVenkateswara Naralasetty 		ar = ab->pdevs[i].ar;
9359b2e3b4eSVenkateswara Naralasetty 		cfr = &ar->cfr;
9369b2e3b4eSVenkateswara Naralasetty 
9379b2e3b4eSVenkateswara Naralasetty 		if (!cfr->enabled)
9389b2e3b4eSVenkateswara Naralasetty 			continue;
9399b2e3b4eSVenkateswara Naralasetty 
9409754d4baSVenkateswara Naralasetty 		ath11k_cfr_debug_unregister(ar);
9419b2e3b4eSVenkateswara Naralasetty 		ath11k_cfr_ring_free(ar);
9429b2e3b4eSVenkateswara Naralasetty 
9439b2e3b4eSVenkateswara Naralasetty 		spin_lock_bh(&cfr->lut_lock);
9449b2e3b4eSVenkateswara Naralasetty 		kfree(cfr->lut);
9459b2e3b4eSVenkateswara Naralasetty 		cfr->lut = NULL;
9469b2e3b4eSVenkateswara Naralasetty 		cfr->enabled = false;
9479b2e3b4eSVenkateswara Naralasetty 		spin_unlock_bh(&cfr->lut_lock);
9489b2e3b4eSVenkateswara Naralasetty 	}
9499b2e3b4eSVenkateswara Naralasetty }
9509b2e3b4eSVenkateswara Naralasetty 
9519b2e3b4eSVenkateswara Naralasetty int ath11k_cfr_init(struct ath11k_base *ab)
9529b2e3b4eSVenkateswara Naralasetty {
9539b2e3b4eSVenkateswara Naralasetty 	struct ath11k_dbring_cap db_cap;
9549b2e3b4eSVenkateswara Naralasetty 	struct ath11k_cfr *cfr;
9559b2e3b4eSVenkateswara Naralasetty 	u32 num_lut_entries;
9569b2e3b4eSVenkateswara Naralasetty 	struct ath11k *ar;
9579b2e3b4eSVenkateswara Naralasetty 	int i, ret;
9589b2e3b4eSVenkateswara Naralasetty 
9599b2e3b4eSVenkateswara Naralasetty 	if (!test_bit(WMI_TLV_SERVICE_CFR_CAPTURE_SUPPORT, ab->wmi_ab.svc_map) ||
9609b2e3b4eSVenkateswara Naralasetty 	    !ab->hw_params.cfr_support)
9619b2e3b4eSVenkateswara Naralasetty 		return 0;
9629b2e3b4eSVenkateswara Naralasetty 
9639b2e3b4eSVenkateswara Naralasetty 	for (i = 0; i < ab->num_radios; i++) {
9649b2e3b4eSVenkateswara Naralasetty 		ar = ab->pdevs[i].ar;
9659b2e3b4eSVenkateswara Naralasetty 		cfr = &ar->cfr;
9669b2e3b4eSVenkateswara Naralasetty 
9679b2e3b4eSVenkateswara Naralasetty 		ret = ath11k_dbring_get_cap(ar->ab, ar->pdev_idx,
9689b2e3b4eSVenkateswara Naralasetty 					    WMI_DIRECT_BUF_CFR, &db_cap);
9699b2e3b4eSVenkateswara Naralasetty 		if (ret)
9709b2e3b4eSVenkateswara Naralasetty 			continue;
9719b2e3b4eSVenkateswara Naralasetty 
9729b2e3b4eSVenkateswara Naralasetty 		idr_init(&cfr->rx_ring.bufs_idr);
9739b2e3b4eSVenkateswara Naralasetty 		spin_lock_init(&cfr->rx_ring.idr_lock);
9749b2e3b4eSVenkateswara Naralasetty 		spin_lock_init(&cfr->lock);
9759b2e3b4eSVenkateswara Naralasetty 		spin_lock_init(&cfr->lut_lock);
9769b2e3b4eSVenkateswara Naralasetty 
9779b2e3b4eSVenkateswara Naralasetty 		num_lut_entries = min_t(u32, CFR_MAX_LUT_ENTRIES, db_cap.min_elem);
978*bf4afc53SLinus Torvalds 		cfr->lut = kzalloc_objs(*cfr->lut, num_lut_entries);
9799b2e3b4eSVenkateswara Naralasetty 		if (!cfr->lut) {
9809b2e3b4eSVenkateswara Naralasetty 			ret = -ENOMEM;
9819b2e3b4eSVenkateswara Naralasetty 			goto err;
9829b2e3b4eSVenkateswara Naralasetty 		}
9839b2e3b4eSVenkateswara Naralasetty 
9849b2e3b4eSVenkateswara Naralasetty 		ret = ath11k_cfr_ring_alloc(ar, &db_cap);
9859b2e3b4eSVenkateswara Naralasetty 		if (ret) {
9869b2e3b4eSVenkateswara Naralasetty 			ath11k_warn(ab, "failed to init cfr ring for pdev %d: %d\n",
9879b2e3b4eSVenkateswara Naralasetty 				    i, ret);
9889b2e3b4eSVenkateswara Naralasetty 			spin_lock_bh(&cfr->lut_lock);
9899b2e3b4eSVenkateswara Naralasetty 			kfree(cfr->lut);
9909b2e3b4eSVenkateswara Naralasetty 			cfr->lut = NULL;
9919b2e3b4eSVenkateswara Naralasetty 			cfr->enabled = false;
9929b2e3b4eSVenkateswara Naralasetty 			spin_unlock_bh(&cfr->lut_lock);
9939b2e3b4eSVenkateswara Naralasetty 			goto err;
9949b2e3b4eSVenkateswara Naralasetty 		}
9959b2e3b4eSVenkateswara Naralasetty 
9969b2e3b4eSVenkateswara Naralasetty 		cfr->lut_num = num_lut_entries;
9979b2e3b4eSVenkateswara Naralasetty 		cfr->enabled = true;
9989754d4baSVenkateswara Naralasetty 
9999754d4baSVenkateswara Naralasetty 		ath11k_cfr_debug_register(ar);
10009b2e3b4eSVenkateswara Naralasetty 	}
10019b2e3b4eSVenkateswara Naralasetty 
10029b2e3b4eSVenkateswara Naralasetty 	return 0;
10039b2e3b4eSVenkateswara Naralasetty 
10049b2e3b4eSVenkateswara Naralasetty err:
10059b2e3b4eSVenkateswara Naralasetty 	for (i = i - 1; i >= 0; i--) {
10069b2e3b4eSVenkateswara Naralasetty 		ar = ab->pdevs[i].ar;
10079b2e3b4eSVenkateswara Naralasetty 		cfr = &ar->cfr;
10089b2e3b4eSVenkateswara Naralasetty 
10099b2e3b4eSVenkateswara Naralasetty 		if (!cfr->enabled)
10109b2e3b4eSVenkateswara Naralasetty 			continue;
10119b2e3b4eSVenkateswara Naralasetty 
10129754d4baSVenkateswara Naralasetty 		ath11k_cfr_debug_unregister(ar);
10139b2e3b4eSVenkateswara Naralasetty 		ath11k_cfr_ring_free(ar);
10149b2e3b4eSVenkateswara Naralasetty 
10159b2e3b4eSVenkateswara Naralasetty 		spin_lock_bh(&cfr->lut_lock);
10169b2e3b4eSVenkateswara Naralasetty 		kfree(cfr->lut);
10179b2e3b4eSVenkateswara Naralasetty 		cfr->lut = NULL;
10189b2e3b4eSVenkateswara Naralasetty 		cfr->enabled = false;
10199b2e3b4eSVenkateswara Naralasetty 		spin_unlock_bh(&cfr->lut_lock);
10209b2e3b4eSVenkateswara Naralasetty 	}
10219b2e3b4eSVenkateswara Naralasetty 	return ret;
10229b2e3b4eSVenkateswara Naralasetty }
1023