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