1da3a9d3cSKalle Valo // SPDX-License-Identifier: BSD-3-Clause-Clear 2da3a9d3cSKalle Valo /* 3da3a9d3cSKalle Valo * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. 4da3a9d3cSKalle Valo */ 5da3a9d3cSKalle Valo 615fcb103SJakub Kicinski #include <linux/vmalloc.h> 715fcb103SJakub Kicinski 8da3a9d3cSKalle Valo #include "debugfs.h" 9da3a9d3cSKalle Valo 10da3a9d3cSKalle Valo #include "core.h" 11da3a9d3cSKalle Valo #include "debug.h" 12da3a9d3cSKalle Valo #include "wmi.h" 13da3a9d3cSKalle Valo #include "hal_rx.h" 14da3a9d3cSKalle Valo #include "dp_tx.h" 1556292162SKalle Valo #include "debugfs_htt_stats.h" 16da3a9d3cSKalle Valo #include "peer.h" 17da3a9d3cSKalle Valo 18da3a9d3cSKalle Valo static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = { 19da3a9d3cSKalle Valo "REO2SW1_RING", 20da3a9d3cSKalle Valo "REO2SW2_RING", 21da3a9d3cSKalle Valo "REO2SW3_RING", 22da3a9d3cSKalle Valo "REO2SW4_RING", 23da3a9d3cSKalle Valo "WBM2REO_LINK_RING", 24da3a9d3cSKalle Valo "REO2TCL_RING", 25da3a9d3cSKalle Valo "REO2FW_RING", 26da3a9d3cSKalle Valo "RELEASE_RING", 27da3a9d3cSKalle Valo "PPE_RELEASE_RING", 28da3a9d3cSKalle Valo "TCL2TQM_RING", 29da3a9d3cSKalle Valo "TQM_RELEASE_RING", 30da3a9d3cSKalle Valo "REO_RELEASE_RING", 31da3a9d3cSKalle Valo "WBM2SW0_RELEASE_RING", 32da3a9d3cSKalle Valo "WBM2SW1_RELEASE_RING", 33da3a9d3cSKalle Valo "WBM2SW2_RELEASE_RING", 34da3a9d3cSKalle Valo "WBM2SW3_RELEASE_RING", 35da3a9d3cSKalle Valo "REO_CMD_RING", 36da3a9d3cSKalle Valo "REO_STATUS_RING", 37da3a9d3cSKalle Valo }; 38da3a9d3cSKalle Valo 39da3a9d3cSKalle Valo static const char *htt_bp_lmac_ring[HTT_SW_LMAC_RING_IDX_MAX] = { 40da3a9d3cSKalle Valo "FW2RXDMA_BUF_RING", 41da3a9d3cSKalle Valo "FW2RXDMA_STATUS_RING", 42da3a9d3cSKalle Valo "FW2RXDMA_LINK_RING", 43da3a9d3cSKalle Valo "SW2RXDMA_BUF_RING", 44da3a9d3cSKalle Valo "WBM2RXDMA_LINK_RING", 45da3a9d3cSKalle Valo "RXDMA2FW_RING", 46da3a9d3cSKalle Valo "RXDMA2SW_RING", 47da3a9d3cSKalle Valo "RXDMA2RELEASE_RING", 48da3a9d3cSKalle Valo "RXDMA2REO_RING", 49da3a9d3cSKalle Valo "MONITOR_STATUS_RING", 50da3a9d3cSKalle Valo "MONITOR_BUF_RING", 51da3a9d3cSKalle Valo "MONITOR_DESC_RING", 52da3a9d3cSKalle Valo "MONITOR_DEST_RING", 53da3a9d3cSKalle Valo }; 54da3a9d3cSKalle Valo 55691425b4SVenkateswara Naralasetty void ath11k_debugfs_add_dbring_entry(struct ath11k *ar, 56691425b4SVenkateswara Naralasetty enum wmi_direct_buffer_module id, 57691425b4SVenkateswara Naralasetty enum ath11k_dbg_dbr_event event, 58691425b4SVenkateswara Naralasetty struct hal_srng *srng) 59691425b4SVenkateswara Naralasetty { 60691425b4SVenkateswara Naralasetty struct ath11k_debug_dbr *dbr_debug; 61691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_data *dbr_data; 62691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_entry *entry; 63691425b4SVenkateswara Naralasetty 64691425b4SVenkateswara Naralasetty if (id >= WMI_DIRECT_BUF_MAX || event >= ATH11K_DBG_DBR_EVENT_MAX) 65691425b4SVenkateswara Naralasetty return; 66691425b4SVenkateswara Naralasetty 67691425b4SVenkateswara Naralasetty dbr_debug = ar->debug.dbr_debug[id]; 68691425b4SVenkateswara Naralasetty if (!dbr_debug) 69691425b4SVenkateswara Naralasetty return; 70691425b4SVenkateswara Naralasetty 71691425b4SVenkateswara Naralasetty if (!dbr_debug->dbr_debug_enabled) 72691425b4SVenkateswara Naralasetty return; 73691425b4SVenkateswara Naralasetty 74691425b4SVenkateswara Naralasetty dbr_data = &dbr_debug->dbr_dbg_data; 75691425b4SVenkateswara Naralasetty 76691425b4SVenkateswara Naralasetty spin_lock_bh(&dbr_data->lock); 77691425b4SVenkateswara Naralasetty 78691425b4SVenkateswara Naralasetty if (dbr_data->entries) { 79691425b4SVenkateswara Naralasetty entry = &dbr_data->entries[dbr_data->dbr_debug_idx]; 80691425b4SVenkateswara Naralasetty entry->hp = srng->u.src_ring.hp; 81691425b4SVenkateswara Naralasetty entry->tp = *srng->u.src_ring.tp_addr; 82691425b4SVenkateswara Naralasetty entry->timestamp = jiffies; 83691425b4SVenkateswara Naralasetty entry->event = event; 84691425b4SVenkateswara Naralasetty 85691425b4SVenkateswara Naralasetty dbr_data->dbr_debug_idx++; 86691425b4SVenkateswara Naralasetty if (dbr_data->dbr_debug_idx == 87691425b4SVenkateswara Naralasetty dbr_data->num_ring_debug_entries) 88691425b4SVenkateswara Naralasetty dbr_data->dbr_debug_idx = 0; 89691425b4SVenkateswara Naralasetty } 90691425b4SVenkateswara Naralasetty 91691425b4SVenkateswara Naralasetty spin_unlock_bh(&dbr_data->lock); 92691425b4SVenkateswara Naralasetty } 93691425b4SVenkateswara Naralasetty 94da3a9d3cSKalle Valo static void ath11k_fw_stats_pdevs_free(struct list_head *head) 95da3a9d3cSKalle Valo { 96da3a9d3cSKalle Valo struct ath11k_fw_stats_pdev *i, *tmp; 97da3a9d3cSKalle Valo 98da3a9d3cSKalle Valo list_for_each_entry_safe(i, tmp, head, list) { 99da3a9d3cSKalle Valo list_del(&i->list); 100da3a9d3cSKalle Valo kfree(i); 101da3a9d3cSKalle Valo } 102da3a9d3cSKalle Valo } 103da3a9d3cSKalle Valo 104da3a9d3cSKalle Valo static void ath11k_fw_stats_vdevs_free(struct list_head *head) 105da3a9d3cSKalle Valo { 106da3a9d3cSKalle Valo struct ath11k_fw_stats_vdev *i, *tmp; 107da3a9d3cSKalle Valo 108da3a9d3cSKalle Valo list_for_each_entry_safe(i, tmp, head, list) { 109da3a9d3cSKalle Valo list_del(&i->list); 110da3a9d3cSKalle Valo kfree(i); 111da3a9d3cSKalle Valo } 112da3a9d3cSKalle Valo } 113da3a9d3cSKalle Valo 114da3a9d3cSKalle Valo static void ath11k_fw_stats_bcn_free(struct list_head *head) 115da3a9d3cSKalle Valo { 116da3a9d3cSKalle Valo struct ath11k_fw_stats_bcn *i, *tmp; 117da3a9d3cSKalle Valo 118da3a9d3cSKalle Valo list_for_each_entry_safe(i, tmp, head, list) { 119da3a9d3cSKalle Valo list_del(&i->list); 120da3a9d3cSKalle Valo kfree(i); 121da3a9d3cSKalle Valo } 122da3a9d3cSKalle Valo } 123da3a9d3cSKalle Valo 124cb4e57dbSKalle Valo static void ath11k_debugfs_fw_stats_reset(struct ath11k *ar) 125da3a9d3cSKalle Valo { 126da3a9d3cSKalle Valo spin_lock_bh(&ar->data_lock); 127da3a9d3cSKalle Valo ar->debug.fw_stats_done = false; 128da3a9d3cSKalle Valo ath11k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs); 129da3a9d3cSKalle Valo ath11k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs); 130da3a9d3cSKalle Valo spin_unlock_bh(&ar->data_lock); 131da3a9d3cSKalle Valo } 132da3a9d3cSKalle Valo 133cb4e57dbSKalle Valo void ath11k_debugfs_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb) 134da3a9d3cSKalle Valo { 135da3a9d3cSKalle Valo struct ath11k_fw_stats stats = {}; 136da3a9d3cSKalle Valo struct ath11k *ar; 137da3a9d3cSKalle Valo struct ath11k_pdev *pdev; 138da3a9d3cSKalle Valo bool is_end; 139da3a9d3cSKalle Valo static unsigned int num_vdev, num_bcn; 140da3a9d3cSKalle Valo size_t total_vdevs_started = 0; 141da3a9d3cSKalle Valo int i, ret; 142da3a9d3cSKalle Valo 143da3a9d3cSKalle Valo INIT_LIST_HEAD(&stats.pdevs); 144da3a9d3cSKalle Valo INIT_LIST_HEAD(&stats.vdevs); 145da3a9d3cSKalle Valo INIT_LIST_HEAD(&stats.bcn); 146da3a9d3cSKalle Valo 147da3a9d3cSKalle Valo ret = ath11k_wmi_pull_fw_stats(ab, skb, &stats); 148da3a9d3cSKalle Valo if (ret) { 149da3a9d3cSKalle Valo ath11k_warn(ab, "failed to pull fw stats: %d\n", ret); 150da3a9d3cSKalle Valo goto free; 151da3a9d3cSKalle Valo } 152da3a9d3cSKalle Valo 153da3a9d3cSKalle Valo rcu_read_lock(); 154da3a9d3cSKalle Valo ar = ath11k_mac_get_ar_by_pdev_id(ab, stats.pdev_id); 155da3a9d3cSKalle Valo if (!ar) { 156da3a9d3cSKalle Valo rcu_read_unlock(); 157da3a9d3cSKalle Valo ath11k_warn(ab, "failed to get ar for pdev_id %d: %d\n", 158da3a9d3cSKalle Valo stats.pdev_id, ret); 159da3a9d3cSKalle Valo goto free; 160da3a9d3cSKalle Valo } 161da3a9d3cSKalle Valo 162da3a9d3cSKalle Valo spin_lock_bh(&ar->data_lock); 163da3a9d3cSKalle Valo 164da3a9d3cSKalle Valo if (stats.stats_id == WMI_REQUEST_PDEV_STAT) { 165da3a9d3cSKalle Valo list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs); 166da3a9d3cSKalle Valo ar->debug.fw_stats_done = true; 167da3a9d3cSKalle Valo goto complete; 168da3a9d3cSKalle Valo } 169da3a9d3cSKalle Valo 170b488c766SWen Gong if (stats.stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) { 171b488c766SWen Gong ar->debug.fw_stats_done = true; 172b488c766SWen Gong goto complete; 173b488c766SWen Gong } 174b488c766SWen Gong 175da3a9d3cSKalle Valo if (stats.stats_id == WMI_REQUEST_VDEV_STAT) { 176da3a9d3cSKalle Valo if (list_empty(&stats.vdevs)) { 177da3a9d3cSKalle Valo ath11k_warn(ab, "empty vdev stats"); 178da3a9d3cSKalle Valo goto complete; 179da3a9d3cSKalle Valo } 180da3a9d3cSKalle Valo /* FW sends all the active VDEV stats irrespective of PDEV, 181da3a9d3cSKalle Valo * hence limit until the count of all VDEVs started 182da3a9d3cSKalle Valo */ 183da3a9d3cSKalle Valo for (i = 0; i < ab->num_radios; i++) { 184da3a9d3cSKalle Valo pdev = rcu_dereference(ab->pdevs_active[i]); 185da3a9d3cSKalle Valo if (pdev && pdev->ar) 186da3a9d3cSKalle Valo total_vdevs_started += ar->num_started_vdevs; 187da3a9d3cSKalle Valo } 188da3a9d3cSKalle Valo 189da3a9d3cSKalle Valo is_end = ((++num_vdev) == total_vdevs_started); 190da3a9d3cSKalle Valo 191da3a9d3cSKalle Valo list_splice_tail_init(&stats.vdevs, 192da3a9d3cSKalle Valo &ar->debug.fw_stats.vdevs); 193da3a9d3cSKalle Valo 194da3a9d3cSKalle Valo if (is_end) { 195da3a9d3cSKalle Valo ar->debug.fw_stats_done = true; 196da3a9d3cSKalle Valo num_vdev = 0; 197da3a9d3cSKalle Valo } 198da3a9d3cSKalle Valo goto complete; 199da3a9d3cSKalle Valo } 200da3a9d3cSKalle Valo 201da3a9d3cSKalle Valo if (stats.stats_id == WMI_REQUEST_BCN_STAT) { 202da3a9d3cSKalle Valo if (list_empty(&stats.bcn)) { 203da3a9d3cSKalle Valo ath11k_warn(ab, "empty bcn stats"); 204da3a9d3cSKalle Valo goto complete; 205da3a9d3cSKalle Valo } 206da3a9d3cSKalle Valo /* Mark end until we reached the count of all started VDEVs 207da3a9d3cSKalle Valo * within the PDEV 208da3a9d3cSKalle Valo */ 209da3a9d3cSKalle Valo is_end = ((++num_bcn) == ar->num_started_vdevs); 210da3a9d3cSKalle Valo 211da3a9d3cSKalle Valo list_splice_tail_init(&stats.bcn, 212da3a9d3cSKalle Valo &ar->debug.fw_stats.bcn); 213da3a9d3cSKalle Valo 214da3a9d3cSKalle Valo if (is_end) { 215da3a9d3cSKalle Valo ar->debug.fw_stats_done = true; 216da3a9d3cSKalle Valo num_bcn = 0; 217da3a9d3cSKalle Valo } 218da3a9d3cSKalle Valo } 219da3a9d3cSKalle Valo complete: 220da3a9d3cSKalle Valo complete(&ar->debug.fw_stats_complete); 221da3a9d3cSKalle Valo rcu_read_unlock(); 222da3a9d3cSKalle Valo spin_unlock_bh(&ar->data_lock); 223da3a9d3cSKalle Valo 224da3a9d3cSKalle Valo free: 225da3a9d3cSKalle Valo ath11k_fw_stats_pdevs_free(&stats.pdevs); 226da3a9d3cSKalle Valo ath11k_fw_stats_vdevs_free(&stats.vdevs); 227da3a9d3cSKalle Valo ath11k_fw_stats_bcn_free(&stats.bcn); 228da3a9d3cSKalle Valo } 229da3a9d3cSKalle Valo 230cb4e57dbSKalle Valo static int ath11k_debugfs_fw_stats_request(struct ath11k *ar, 231da3a9d3cSKalle Valo struct stats_request_params *req_param) 232da3a9d3cSKalle Valo { 233da3a9d3cSKalle Valo struct ath11k_base *ab = ar->ab; 234da3a9d3cSKalle Valo unsigned long timeout, time_left; 235da3a9d3cSKalle Valo int ret; 236da3a9d3cSKalle Valo 237da3a9d3cSKalle Valo lockdep_assert_held(&ar->conf_mutex); 238da3a9d3cSKalle Valo 239da3a9d3cSKalle Valo /* FW stats can get split when exceeding the stats data buffer limit. 240da3a9d3cSKalle Valo * In that case, since there is no end marking for the back-to-back 241da3a9d3cSKalle Valo * received 'update stats' event, we keep a 3 seconds timeout in case, 242da3a9d3cSKalle Valo * fw_stats_done is not marked yet 243da3a9d3cSKalle Valo */ 244c8f2d41bSWen Gong timeout = jiffies + msecs_to_jiffies(3 * 1000); 245da3a9d3cSKalle Valo 246cb4e57dbSKalle Valo ath11k_debugfs_fw_stats_reset(ar); 247da3a9d3cSKalle Valo 248da3a9d3cSKalle Valo reinit_completion(&ar->debug.fw_stats_complete); 249da3a9d3cSKalle Valo 250da3a9d3cSKalle Valo ret = ath11k_wmi_send_stats_request_cmd(ar, req_param); 251da3a9d3cSKalle Valo 252da3a9d3cSKalle Valo if (ret) { 253da3a9d3cSKalle Valo ath11k_warn(ab, "could not request fw stats (%d)\n", 254da3a9d3cSKalle Valo ret); 255da3a9d3cSKalle Valo return ret; 256da3a9d3cSKalle Valo } 257da3a9d3cSKalle Valo 258da3a9d3cSKalle Valo time_left = 259da3a9d3cSKalle Valo wait_for_completion_timeout(&ar->debug.fw_stats_complete, 260da3a9d3cSKalle Valo 1 * HZ); 261da3a9d3cSKalle Valo if (!time_left) 262da3a9d3cSKalle Valo return -ETIMEDOUT; 263da3a9d3cSKalle Valo 264da3a9d3cSKalle Valo for (;;) { 265da3a9d3cSKalle Valo if (time_after(jiffies, timeout)) 266da3a9d3cSKalle Valo break; 267da3a9d3cSKalle Valo 268da3a9d3cSKalle Valo spin_lock_bh(&ar->data_lock); 269da3a9d3cSKalle Valo if (ar->debug.fw_stats_done) { 270da3a9d3cSKalle Valo spin_unlock_bh(&ar->data_lock); 271da3a9d3cSKalle Valo break; 272da3a9d3cSKalle Valo } 273da3a9d3cSKalle Valo spin_unlock_bh(&ar->data_lock); 274da3a9d3cSKalle Valo } 275da3a9d3cSKalle Valo return 0; 276da3a9d3cSKalle Valo } 277da3a9d3cSKalle Valo 278b488c766SWen Gong int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id, 279b488c766SWen Gong u32 vdev_id, u32 stats_id) 280b488c766SWen Gong { 281b488c766SWen Gong struct ath11k_base *ab = ar->ab; 282b488c766SWen Gong struct stats_request_params req_param; 283b488c766SWen Gong int ret; 284b488c766SWen Gong 285b488c766SWen Gong mutex_lock(&ar->conf_mutex); 286b488c766SWen Gong 287b488c766SWen Gong if (ar->state != ATH11K_STATE_ON) { 288b488c766SWen Gong ret = -ENETDOWN; 289b488c766SWen Gong goto err_unlock; 290b488c766SWen Gong } 291b488c766SWen Gong 292b488c766SWen Gong req_param.pdev_id = pdev_id; 293b488c766SWen Gong req_param.vdev_id = vdev_id; 294b488c766SWen Gong req_param.stats_id = stats_id; 295b488c766SWen Gong 296b488c766SWen Gong ret = ath11k_debugfs_fw_stats_request(ar, &req_param); 297b488c766SWen Gong if (ret) 298b488c766SWen Gong ath11k_warn(ab, "failed to request fw stats: %d\n", ret); 299b488c766SWen Gong 300b488c766SWen Gong ath11k_dbg(ab, ATH11K_DBG_WMI, 301b488c766SWen Gong "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n", 302b488c766SWen Gong pdev_id, vdev_id, stats_id); 303b488c766SWen Gong 304b488c766SWen Gong err_unlock: 305b488c766SWen Gong mutex_unlock(&ar->conf_mutex); 306b488c766SWen Gong 307b488c766SWen Gong return ret; 308b488c766SWen Gong } 309b488c766SWen Gong 310da3a9d3cSKalle Valo static int ath11k_open_pdev_stats(struct inode *inode, struct file *file) 311da3a9d3cSKalle Valo { 312da3a9d3cSKalle Valo struct ath11k *ar = inode->i_private; 313da3a9d3cSKalle Valo struct ath11k_base *ab = ar->ab; 314da3a9d3cSKalle Valo struct stats_request_params req_param; 315da3a9d3cSKalle Valo void *buf = NULL; 316da3a9d3cSKalle Valo int ret; 317da3a9d3cSKalle Valo 318da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex); 319da3a9d3cSKalle Valo 320da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) { 321da3a9d3cSKalle Valo ret = -ENETDOWN; 322da3a9d3cSKalle Valo goto err_unlock; 323da3a9d3cSKalle Valo } 324da3a9d3cSKalle Valo 325da3a9d3cSKalle Valo buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); 326da3a9d3cSKalle Valo if (!buf) { 327da3a9d3cSKalle Valo ret = -ENOMEM; 328da3a9d3cSKalle Valo goto err_unlock; 329da3a9d3cSKalle Valo } 330da3a9d3cSKalle Valo 331da3a9d3cSKalle Valo req_param.pdev_id = ar->pdev->pdev_id; 332da3a9d3cSKalle Valo req_param.vdev_id = 0; 333da3a9d3cSKalle Valo req_param.stats_id = WMI_REQUEST_PDEV_STAT; 334da3a9d3cSKalle Valo 335cb4e57dbSKalle Valo ret = ath11k_debugfs_fw_stats_request(ar, &req_param); 336da3a9d3cSKalle Valo if (ret) { 337da3a9d3cSKalle Valo ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret); 338da3a9d3cSKalle Valo goto err_free; 339da3a9d3cSKalle Valo } 340da3a9d3cSKalle Valo 341da3a9d3cSKalle Valo ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, 342da3a9d3cSKalle Valo buf); 343da3a9d3cSKalle Valo 344da3a9d3cSKalle Valo file->private_data = buf; 345da3a9d3cSKalle Valo 346da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 347da3a9d3cSKalle Valo return 0; 348da3a9d3cSKalle Valo 349da3a9d3cSKalle Valo err_free: 350da3a9d3cSKalle Valo vfree(buf); 351da3a9d3cSKalle Valo 352da3a9d3cSKalle Valo err_unlock: 353da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 354da3a9d3cSKalle Valo return ret; 355da3a9d3cSKalle Valo } 356da3a9d3cSKalle Valo 357da3a9d3cSKalle Valo static int ath11k_release_pdev_stats(struct inode *inode, struct file *file) 358da3a9d3cSKalle Valo { 359da3a9d3cSKalle Valo vfree(file->private_data); 360da3a9d3cSKalle Valo 361da3a9d3cSKalle Valo return 0; 362da3a9d3cSKalle Valo } 363da3a9d3cSKalle Valo 364da3a9d3cSKalle Valo static ssize_t ath11k_read_pdev_stats(struct file *file, 365da3a9d3cSKalle Valo char __user *user_buf, 366da3a9d3cSKalle Valo size_t count, loff_t *ppos) 367da3a9d3cSKalle Valo { 368da3a9d3cSKalle Valo const char *buf = file->private_data; 369da3a9d3cSKalle Valo size_t len = strlen(buf); 370da3a9d3cSKalle Valo 371da3a9d3cSKalle Valo return simple_read_from_buffer(user_buf, count, ppos, buf, len); 372da3a9d3cSKalle Valo } 373da3a9d3cSKalle Valo 374da3a9d3cSKalle Valo static const struct file_operations fops_pdev_stats = { 375da3a9d3cSKalle Valo .open = ath11k_open_pdev_stats, 376da3a9d3cSKalle Valo .release = ath11k_release_pdev_stats, 377da3a9d3cSKalle Valo .read = ath11k_read_pdev_stats, 378da3a9d3cSKalle Valo .owner = THIS_MODULE, 379da3a9d3cSKalle Valo .llseek = default_llseek, 380da3a9d3cSKalle Valo }; 381da3a9d3cSKalle Valo 382da3a9d3cSKalle Valo static int ath11k_open_vdev_stats(struct inode *inode, struct file *file) 383da3a9d3cSKalle Valo { 384da3a9d3cSKalle Valo struct ath11k *ar = inode->i_private; 385da3a9d3cSKalle Valo struct stats_request_params req_param; 386da3a9d3cSKalle Valo void *buf = NULL; 387da3a9d3cSKalle Valo int ret; 388da3a9d3cSKalle Valo 389da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex); 390da3a9d3cSKalle Valo 391da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) { 392da3a9d3cSKalle Valo ret = -ENETDOWN; 393da3a9d3cSKalle Valo goto err_unlock; 394da3a9d3cSKalle Valo } 395da3a9d3cSKalle Valo 396da3a9d3cSKalle Valo buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); 397da3a9d3cSKalle Valo if (!buf) { 398da3a9d3cSKalle Valo ret = -ENOMEM; 399da3a9d3cSKalle Valo goto err_unlock; 400da3a9d3cSKalle Valo } 401da3a9d3cSKalle Valo 402da3a9d3cSKalle Valo req_param.pdev_id = ar->pdev->pdev_id; 403da3a9d3cSKalle Valo /* VDEV stats is always sent for all active VDEVs from FW */ 404da3a9d3cSKalle Valo req_param.vdev_id = 0; 405da3a9d3cSKalle Valo req_param.stats_id = WMI_REQUEST_VDEV_STAT; 406da3a9d3cSKalle Valo 407cb4e57dbSKalle Valo ret = ath11k_debugfs_fw_stats_request(ar, &req_param); 408da3a9d3cSKalle Valo if (ret) { 409da3a9d3cSKalle Valo ath11k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret); 410da3a9d3cSKalle Valo goto err_free; 411da3a9d3cSKalle Valo } 412da3a9d3cSKalle Valo 413da3a9d3cSKalle Valo ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, 414da3a9d3cSKalle Valo buf); 415da3a9d3cSKalle Valo 416da3a9d3cSKalle Valo file->private_data = buf; 417da3a9d3cSKalle Valo 418da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 419da3a9d3cSKalle Valo return 0; 420da3a9d3cSKalle Valo 421da3a9d3cSKalle Valo err_free: 422da3a9d3cSKalle Valo vfree(buf); 423da3a9d3cSKalle Valo 424da3a9d3cSKalle Valo err_unlock: 425da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 426da3a9d3cSKalle Valo return ret; 427da3a9d3cSKalle Valo } 428da3a9d3cSKalle Valo 429da3a9d3cSKalle Valo static int ath11k_release_vdev_stats(struct inode *inode, struct file *file) 430da3a9d3cSKalle Valo { 431da3a9d3cSKalle Valo vfree(file->private_data); 432da3a9d3cSKalle Valo 433da3a9d3cSKalle Valo return 0; 434da3a9d3cSKalle Valo } 435da3a9d3cSKalle Valo 436da3a9d3cSKalle Valo static ssize_t ath11k_read_vdev_stats(struct file *file, 437da3a9d3cSKalle Valo char __user *user_buf, 438da3a9d3cSKalle Valo size_t count, loff_t *ppos) 439da3a9d3cSKalle Valo { 440da3a9d3cSKalle Valo const char *buf = file->private_data; 441da3a9d3cSKalle Valo size_t len = strlen(buf); 442da3a9d3cSKalle Valo 443da3a9d3cSKalle Valo return simple_read_from_buffer(user_buf, count, ppos, buf, len); 444da3a9d3cSKalle Valo } 445da3a9d3cSKalle Valo 446da3a9d3cSKalle Valo static const struct file_operations fops_vdev_stats = { 447da3a9d3cSKalle Valo .open = ath11k_open_vdev_stats, 448da3a9d3cSKalle Valo .release = ath11k_release_vdev_stats, 449da3a9d3cSKalle Valo .read = ath11k_read_vdev_stats, 450da3a9d3cSKalle Valo .owner = THIS_MODULE, 451da3a9d3cSKalle Valo .llseek = default_llseek, 452da3a9d3cSKalle Valo }; 453da3a9d3cSKalle Valo 454da3a9d3cSKalle Valo static int ath11k_open_bcn_stats(struct inode *inode, struct file *file) 455da3a9d3cSKalle Valo { 456da3a9d3cSKalle Valo struct ath11k *ar = inode->i_private; 457da3a9d3cSKalle Valo struct ath11k_vif *arvif; 458da3a9d3cSKalle Valo struct stats_request_params req_param; 459da3a9d3cSKalle Valo void *buf = NULL; 460da3a9d3cSKalle Valo int ret; 461da3a9d3cSKalle Valo 462da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex); 463da3a9d3cSKalle Valo 464da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) { 465da3a9d3cSKalle Valo ret = -ENETDOWN; 466da3a9d3cSKalle Valo goto err_unlock; 467da3a9d3cSKalle Valo } 468da3a9d3cSKalle Valo 469da3a9d3cSKalle Valo buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); 470da3a9d3cSKalle Valo if (!buf) { 471da3a9d3cSKalle Valo ret = -ENOMEM; 472da3a9d3cSKalle Valo goto err_unlock; 473da3a9d3cSKalle Valo } 474da3a9d3cSKalle Valo 475da3a9d3cSKalle Valo req_param.stats_id = WMI_REQUEST_BCN_STAT; 476da3a9d3cSKalle Valo req_param.pdev_id = ar->pdev->pdev_id; 477da3a9d3cSKalle Valo 478da3a9d3cSKalle Valo /* loop all active VDEVs for bcn stats */ 479da3a9d3cSKalle Valo list_for_each_entry(arvif, &ar->arvifs, list) { 480da3a9d3cSKalle Valo if (!arvif->is_up) 481da3a9d3cSKalle Valo continue; 482da3a9d3cSKalle Valo 483da3a9d3cSKalle Valo req_param.vdev_id = arvif->vdev_id; 484cb4e57dbSKalle Valo ret = ath11k_debugfs_fw_stats_request(ar, &req_param); 485da3a9d3cSKalle Valo if (ret) { 486da3a9d3cSKalle Valo ath11k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret); 487da3a9d3cSKalle Valo goto err_free; 488da3a9d3cSKalle Valo } 489da3a9d3cSKalle Valo } 490da3a9d3cSKalle Valo 491da3a9d3cSKalle Valo ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, 492da3a9d3cSKalle Valo buf); 493da3a9d3cSKalle Valo 494da3a9d3cSKalle Valo /* since beacon stats request is looped for all active VDEVs, saved fw 495da3a9d3cSKalle Valo * stats is not freed for each request until done for all active VDEVs 496da3a9d3cSKalle Valo */ 497da3a9d3cSKalle Valo spin_lock_bh(&ar->data_lock); 498da3a9d3cSKalle Valo ath11k_fw_stats_bcn_free(&ar->debug.fw_stats.bcn); 499da3a9d3cSKalle Valo spin_unlock_bh(&ar->data_lock); 500da3a9d3cSKalle Valo 501da3a9d3cSKalle Valo file->private_data = buf; 502da3a9d3cSKalle Valo 503da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 504da3a9d3cSKalle Valo return 0; 505da3a9d3cSKalle Valo 506da3a9d3cSKalle Valo err_free: 507da3a9d3cSKalle Valo vfree(buf); 508da3a9d3cSKalle Valo 509da3a9d3cSKalle Valo err_unlock: 510da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 511da3a9d3cSKalle Valo return ret; 512da3a9d3cSKalle Valo } 513da3a9d3cSKalle Valo 514da3a9d3cSKalle Valo static int ath11k_release_bcn_stats(struct inode *inode, struct file *file) 515da3a9d3cSKalle Valo { 516da3a9d3cSKalle Valo vfree(file->private_data); 517da3a9d3cSKalle Valo 518da3a9d3cSKalle Valo return 0; 519da3a9d3cSKalle Valo } 520da3a9d3cSKalle Valo 521da3a9d3cSKalle Valo static ssize_t ath11k_read_bcn_stats(struct file *file, 522da3a9d3cSKalle Valo char __user *user_buf, 523da3a9d3cSKalle Valo size_t count, loff_t *ppos) 524da3a9d3cSKalle Valo { 525da3a9d3cSKalle Valo const char *buf = file->private_data; 526da3a9d3cSKalle Valo size_t len = strlen(buf); 527da3a9d3cSKalle Valo 528da3a9d3cSKalle Valo return simple_read_from_buffer(user_buf, count, ppos, buf, len); 529da3a9d3cSKalle Valo } 530da3a9d3cSKalle Valo 531da3a9d3cSKalle Valo static const struct file_operations fops_bcn_stats = { 532da3a9d3cSKalle Valo .open = ath11k_open_bcn_stats, 533da3a9d3cSKalle Valo .release = ath11k_release_bcn_stats, 534da3a9d3cSKalle Valo .read = ath11k_read_bcn_stats, 535da3a9d3cSKalle Valo .owner = THIS_MODULE, 536da3a9d3cSKalle Valo .llseek = default_llseek, 537da3a9d3cSKalle Valo }; 538da3a9d3cSKalle Valo 539da3a9d3cSKalle Valo static ssize_t ath11k_read_simulate_fw_crash(struct file *file, 540da3a9d3cSKalle Valo char __user *user_buf, 541da3a9d3cSKalle Valo size_t count, loff_t *ppos) 542da3a9d3cSKalle Valo { 543da3a9d3cSKalle Valo const char buf[] = 544da3a9d3cSKalle Valo "To simulate firmware crash write one of the keywords to this file:\n" 545da3a9d3cSKalle Valo "`assert` - this will send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n" 546da3a9d3cSKalle Valo "`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n"; 547da3a9d3cSKalle Valo 548da3a9d3cSKalle Valo return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); 549da3a9d3cSKalle Valo } 550da3a9d3cSKalle Valo 551da3a9d3cSKalle Valo /* Simulate firmware crash: 552da3a9d3cSKalle Valo * 'soft': Call wmi command causing firmware hang. This firmware hang is 553da3a9d3cSKalle Valo * recoverable by warm firmware reset. 554da3a9d3cSKalle Valo * 'hard': Force firmware crash by setting any vdev parameter for not allowed 555da3a9d3cSKalle Valo * vdev id. This is hard firmware crash because it is recoverable only by cold 556da3a9d3cSKalle Valo * firmware reset. 557da3a9d3cSKalle Valo */ 558da3a9d3cSKalle Valo static ssize_t ath11k_write_simulate_fw_crash(struct file *file, 559da3a9d3cSKalle Valo const char __user *user_buf, 560da3a9d3cSKalle Valo size_t count, loff_t *ppos) 561da3a9d3cSKalle Valo { 562da3a9d3cSKalle Valo struct ath11k_base *ab = file->private_data; 563da3a9d3cSKalle Valo struct ath11k_pdev *pdev; 564da3a9d3cSKalle Valo struct ath11k *ar = ab->pdevs[0].ar; 565da3a9d3cSKalle Valo char buf[32] = {0}; 566da3a9d3cSKalle Valo ssize_t rc; 567da3a9d3cSKalle Valo int i, ret, radioup = 0; 568da3a9d3cSKalle Valo 569da3a9d3cSKalle Valo for (i = 0; i < ab->num_radios; i++) { 570da3a9d3cSKalle Valo pdev = &ab->pdevs[i]; 571da3a9d3cSKalle Valo ar = pdev->ar; 572da3a9d3cSKalle Valo if (ar && ar->state == ATH11K_STATE_ON) { 573da3a9d3cSKalle Valo radioup = 1; 574da3a9d3cSKalle Valo break; 575da3a9d3cSKalle Valo } 576da3a9d3cSKalle Valo } 577da3a9d3cSKalle Valo /* filter partial writes and invalid commands */ 578da3a9d3cSKalle Valo if (*ppos != 0 || count >= sizeof(buf) || count == 0) 579da3a9d3cSKalle Valo return -EINVAL; 580da3a9d3cSKalle Valo 581da3a9d3cSKalle Valo rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); 582da3a9d3cSKalle Valo if (rc < 0) 583da3a9d3cSKalle Valo return rc; 584da3a9d3cSKalle Valo 585da3a9d3cSKalle Valo /* drop the possible '\n' from the end */ 586da3a9d3cSKalle Valo if (buf[*ppos - 1] == '\n') 587da3a9d3cSKalle Valo buf[*ppos - 1] = '\0'; 588da3a9d3cSKalle Valo 589da3a9d3cSKalle Valo if (radioup == 0) { 590da3a9d3cSKalle Valo ret = -ENETDOWN; 591da3a9d3cSKalle Valo goto exit; 592da3a9d3cSKalle Valo } 593da3a9d3cSKalle Valo 594da3a9d3cSKalle Valo if (!strcmp(buf, "assert")) { 595da3a9d3cSKalle Valo ath11k_info(ab, "simulating firmware assert crash\n"); 596da3a9d3cSKalle Valo ret = ath11k_wmi_force_fw_hang_cmd(ar, 597da3a9d3cSKalle Valo ATH11K_WMI_FW_HANG_ASSERT_TYPE, 598da3a9d3cSKalle Valo ATH11K_WMI_FW_HANG_DELAY); 59978e3e609SWen Gong } else if (!strcmp(buf, "hw-restart")) { 60078e3e609SWen Gong ath11k_info(ab, "user requested hw restart\n"); 60178e3e609SWen Gong queue_work(ab->workqueue_aux, &ab->reset_work); 60278e3e609SWen Gong ret = 0; 603da3a9d3cSKalle Valo } else { 604da3a9d3cSKalle Valo ret = -EINVAL; 605da3a9d3cSKalle Valo goto exit; 606da3a9d3cSKalle Valo } 607da3a9d3cSKalle Valo 608da3a9d3cSKalle Valo if (ret) { 609da3a9d3cSKalle Valo ath11k_warn(ab, "failed to simulate firmware crash: %d\n", ret); 610da3a9d3cSKalle Valo goto exit; 611da3a9d3cSKalle Valo } 612da3a9d3cSKalle Valo 613da3a9d3cSKalle Valo ret = count; 614da3a9d3cSKalle Valo 615da3a9d3cSKalle Valo exit: 616da3a9d3cSKalle Valo return ret; 617da3a9d3cSKalle Valo } 618da3a9d3cSKalle Valo 619da3a9d3cSKalle Valo static const struct file_operations fops_simulate_fw_crash = { 620da3a9d3cSKalle Valo .read = ath11k_read_simulate_fw_crash, 621da3a9d3cSKalle Valo .write = ath11k_write_simulate_fw_crash, 622da3a9d3cSKalle Valo .open = simple_open, 623da3a9d3cSKalle Valo .owner = THIS_MODULE, 624da3a9d3cSKalle Valo .llseek = default_llseek, 625da3a9d3cSKalle Valo }; 626da3a9d3cSKalle Valo 627da3a9d3cSKalle Valo static ssize_t ath11k_write_enable_extd_tx_stats(struct file *file, 628da3a9d3cSKalle Valo const char __user *ubuf, 629da3a9d3cSKalle Valo size_t count, loff_t *ppos) 630da3a9d3cSKalle Valo { 631da3a9d3cSKalle Valo struct ath11k *ar = file->private_data; 632da3a9d3cSKalle Valo u32 filter; 633da3a9d3cSKalle Valo int ret; 634da3a9d3cSKalle Valo 635da3a9d3cSKalle Valo if (kstrtouint_from_user(ubuf, count, 0, &filter)) 636da3a9d3cSKalle Valo return -EINVAL; 637da3a9d3cSKalle Valo 638da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex); 639da3a9d3cSKalle Valo 640da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) { 641da3a9d3cSKalle Valo ret = -ENETDOWN; 642da3a9d3cSKalle Valo goto out; 643da3a9d3cSKalle Valo } 644da3a9d3cSKalle Valo 645da3a9d3cSKalle Valo if (filter == ar->debug.extd_tx_stats) { 646da3a9d3cSKalle Valo ret = count; 647da3a9d3cSKalle Valo goto out; 648da3a9d3cSKalle Valo } 649da3a9d3cSKalle Valo 650da3a9d3cSKalle Valo ar->debug.extd_tx_stats = filter; 651da3a9d3cSKalle Valo ret = count; 652da3a9d3cSKalle Valo 653da3a9d3cSKalle Valo out: 654da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 655da3a9d3cSKalle Valo return ret; 656da3a9d3cSKalle Valo } 657da3a9d3cSKalle Valo 658da3a9d3cSKalle Valo static ssize_t ath11k_read_enable_extd_tx_stats(struct file *file, 659da3a9d3cSKalle Valo char __user *ubuf, 660da3a9d3cSKalle Valo size_t count, loff_t *ppos) 661da3a9d3cSKalle Valo 662da3a9d3cSKalle Valo { 663da3a9d3cSKalle Valo char buf[32] = {0}; 664da3a9d3cSKalle Valo struct ath11k *ar = file->private_data; 665da3a9d3cSKalle Valo int len = 0; 666da3a9d3cSKalle Valo 667da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex); 668da3a9d3cSKalle Valo len = scnprintf(buf, sizeof(buf) - len, "%08x\n", 669da3a9d3cSKalle Valo ar->debug.extd_tx_stats); 670da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 671da3a9d3cSKalle Valo 672da3a9d3cSKalle Valo return simple_read_from_buffer(ubuf, count, ppos, buf, len); 673da3a9d3cSKalle Valo } 674da3a9d3cSKalle Valo 675da3a9d3cSKalle Valo static const struct file_operations fops_extd_tx_stats = { 676da3a9d3cSKalle Valo .read = ath11k_read_enable_extd_tx_stats, 677da3a9d3cSKalle Valo .write = ath11k_write_enable_extd_tx_stats, 678da3a9d3cSKalle Valo .open = simple_open 679da3a9d3cSKalle Valo }; 680da3a9d3cSKalle Valo 681da3a9d3cSKalle Valo static ssize_t ath11k_write_extd_rx_stats(struct file *file, 682da3a9d3cSKalle Valo const char __user *ubuf, 683da3a9d3cSKalle Valo size_t count, loff_t *ppos) 684da3a9d3cSKalle Valo { 685da3a9d3cSKalle Valo struct ath11k *ar = file->private_data; 686da3a9d3cSKalle Valo struct ath11k_base *ab = ar->ab; 687da3a9d3cSKalle Valo struct htt_rx_ring_tlv_filter tlv_filter = {0}; 688da3a9d3cSKalle Valo u32 enable, rx_filter = 0, ring_id; 689da3a9d3cSKalle Valo int i; 690da3a9d3cSKalle Valo int ret; 691da3a9d3cSKalle Valo 692da3a9d3cSKalle Valo if (kstrtouint_from_user(ubuf, count, 0, &enable)) 693da3a9d3cSKalle Valo return -EINVAL; 694da3a9d3cSKalle Valo 695da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex); 696da3a9d3cSKalle Valo 697da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) { 698da3a9d3cSKalle Valo ret = -ENETDOWN; 699da3a9d3cSKalle Valo goto exit; 700da3a9d3cSKalle Valo } 701da3a9d3cSKalle Valo 702da3a9d3cSKalle Valo if (enable > 1) { 703da3a9d3cSKalle Valo ret = -EINVAL; 704da3a9d3cSKalle Valo goto exit; 705da3a9d3cSKalle Valo } 706da3a9d3cSKalle Valo 707da3a9d3cSKalle Valo if (enable == ar->debug.extd_rx_stats) { 708da3a9d3cSKalle Valo ret = count; 709da3a9d3cSKalle Valo goto exit; 710da3a9d3cSKalle Valo } 711da3a9d3cSKalle Valo 71267a9d399SMiles Hu if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) { 71367a9d399SMiles Hu ar->debug.extd_rx_stats = enable; 71467a9d399SMiles Hu ret = count; 71567a9d399SMiles Hu goto exit; 71667a9d399SMiles Hu } 71767a9d399SMiles Hu 718da3a9d3cSKalle Valo if (enable) { 719da3a9d3cSKalle Valo rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START; 720da3a9d3cSKalle Valo rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START; 721da3a9d3cSKalle Valo rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END; 722da3a9d3cSKalle Valo rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS; 723da3a9d3cSKalle Valo rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT; 724da3a9d3cSKalle Valo rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE; 725da3a9d3cSKalle Valo 726da3a9d3cSKalle Valo tlv_filter.rx_filter = rx_filter; 727da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0; 728da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1; 729da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2; 730da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 | 731da3a9d3cSKalle Valo HTT_RX_FP_DATA_FILTER_FLASG3; 732da3a9d3cSKalle Valo } else { 733da3a9d3cSKalle Valo tlv_filter = ath11k_mac_mon_status_filter_default; 734da3a9d3cSKalle Valo } 735da3a9d3cSKalle Valo 736da3a9d3cSKalle Valo ar->debug.rx_filter = tlv_filter.rx_filter; 737da3a9d3cSKalle Valo 738da3a9d3cSKalle Valo for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { 739da3a9d3cSKalle Valo ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; 740da3a9d3cSKalle Valo ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, 741da3a9d3cSKalle Valo HAL_RXDMA_MONITOR_STATUS, 742da3a9d3cSKalle Valo DP_RX_BUFFER_SIZE, &tlv_filter); 743da3a9d3cSKalle Valo 744da3a9d3cSKalle Valo if (ret) { 745da3a9d3cSKalle Valo ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); 746da3a9d3cSKalle Valo goto exit; 747da3a9d3cSKalle Valo } 748da3a9d3cSKalle Valo } 749da3a9d3cSKalle Valo 750da3a9d3cSKalle Valo ar->debug.extd_rx_stats = enable; 751da3a9d3cSKalle Valo ret = count; 752da3a9d3cSKalle Valo exit: 753da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 754da3a9d3cSKalle Valo return ret; 755da3a9d3cSKalle Valo } 756da3a9d3cSKalle Valo 757da3a9d3cSKalle Valo static ssize_t ath11k_read_extd_rx_stats(struct file *file, 758da3a9d3cSKalle Valo char __user *ubuf, 759da3a9d3cSKalle Valo size_t count, loff_t *ppos) 760da3a9d3cSKalle Valo { 761da3a9d3cSKalle Valo struct ath11k *ar = file->private_data; 762da3a9d3cSKalle Valo char buf[32]; 763da3a9d3cSKalle Valo int len = 0; 764da3a9d3cSKalle Valo 765da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex); 766da3a9d3cSKalle Valo len = scnprintf(buf, sizeof(buf) - len, "%d\n", 767da3a9d3cSKalle Valo ar->debug.extd_rx_stats); 768da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 769da3a9d3cSKalle Valo 770da3a9d3cSKalle Valo return simple_read_from_buffer(ubuf, count, ppos, buf, len); 771da3a9d3cSKalle Valo } 772da3a9d3cSKalle Valo 773da3a9d3cSKalle Valo static const struct file_operations fops_extd_rx_stats = { 774da3a9d3cSKalle Valo .read = ath11k_read_extd_rx_stats, 775da3a9d3cSKalle Valo .write = ath11k_write_extd_rx_stats, 776da3a9d3cSKalle Valo .open = simple_open, 777da3a9d3cSKalle Valo }; 778da3a9d3cSKalle Valo 779da3a9d3cSKalle Valo static int ath11k_fill_bp_stats(struct ath11k_base *ab, 780da3a9d3cSKalle Valo struct ath11k_bp_stats *bp_stats, 781da3a9d3cSKalle Valo char *buf, int len, int size) 782da3a9d3cSKalle Valo { 783da3a9d3cSKalle Valo lockdep_assert_held(&ab->base_lock); 784da3a9d3cSKalle Valo 785da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "count: %u\n", 786da3a9d3cSKalle Valo bp_stats->count); 787da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "hp: %u\n", 788da3a9d3cSKalle Valo bp_stats->hp); 789da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "tp: %u\n", 790da3a9d3cSKalle Valo bp_stats->tp); 791da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "seen before: %ums\n\n", 792da3a9d3cSKalle Valo jiffies_to_msecs(jiffies - bp_stats->jiffies)); 793da3a9d3cSKalle Valo return len; 794da3a9d3cSKalle Valo } 795da3a9d3cSKalle Valo 796cb4e57dbSKalle Valo static ssize_t ath11k_debugfs_dump_soc_ring_bp_stats(struct ath11k_base *ab, 797da3a9d3cSKalle Valo char *buf, int size) 798da3a9d3cSKalle Valo { 799da3a9d3cSKalle Valo struct ath11k_bp_stats *bp_stats; 800da3a9d3cSKalle Valo bool stats_rxd = false; 801da3a9d3cSKalle Valo u8 i, pdev_idx; 802da3a9d3cSKalle Valo int len = 0; 803da3a9d3cSKalle Valo 804da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "\nBackpressure Stats\n"); 805da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "==================\n"); 806da3a9d3cSKalle Valo 807da3a9d3cSKalle Valo spin_lock_bh(&ab->base_lock); 808da3a9d3cSKalle Valo for (i = 0; i < HTT_SW_UMAC_RING_IDX_MAX; i++) { 809da3a9d3cSKalle Valo bp_stats = &ab->soc_stats.bp_stats.umac_ring_bp_stats[i]; 810da3a9d3cSKalle Valo 811da3a9d3cSKalle Valo if (!bp_stats->count) 812da3a9d3cSKalle Valo continue; 813da3a9d3cSKalle Valo 814da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "Ring: %s\n", 815da3a9d3cSKalle Valo htt_bp_umac_ring[i]); 816da3a9d3cSKalle Valo len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size); 817da3a9d3cSKalle Valo stats_rxd = true; 818da3a9d3cSKalle Valo } 819da3a9d3cSKalle Valo 820da3a9d3cSKalle Valo for (i = 0; i < HTT_SW_LMAC_RING_IDX_MAX; i++) { 821da3a9d3cSKalle Valo for (pdev_idx = 0; pdev_idx < MAX_RADIOS; pdev_idx++) { 822da3a9d3cSKalle Valo bp_stats = 823da3a9d3cSKalle Valo &ab->soc_stats.bp_stats.lmac_ring_bp_stats[i][pdev_idx]; 824da3a9d3cSKalle Valo 825da3a9d3cSKalle Valo if (!bp_stats->count) 826da3a9d3cSKalle Valo continue; 827da3a9d3cSKalle Valo 828da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "Ring: %s\n", 829da3a9d3cSKalle Valo htt_bp_lmac_ring[i]); 830da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "pdev: %d\n", 831da3a9d3cSKalle Valo pdev_idx); 832da3a9d3cSKalle Valo len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size); 833da3a9d3cSKalle Valo stats_rxd = true; 834da3a9d3cSKalle Valo } 835da3a9d3cSKalle Valo } 836da3a9d3cSKalle Valo spin_unlock_bh(&ab->base_lock); 837da3a9d3cSKalle Valo 838da3a9d3cSKalle Valo if (!stats_rxd) 839da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, 840da3a9d3cSKalle Valo "No Ring Backpressure stats received\n\n"); 841da3a9d3cSKalle Valo 842da3a9d3cSKalle Valo return len; 843da3a9d3cSKalle Valo } 844da3a9d3cSKalle Valo 845cb4e57dbSKalle Valo static ssize_t ath11k_debugfs_dump_soc_dp_stats(struct file *file, 846da3a9d3cSKalle Valo char __user *user_buf, 847da3a9d3cSKalle Valo size_t count, loff_t *ppos) 848da3a9d3cSKalle Valo { 849da3a9d3cSKalle Valo struct ath11k_base *ab = file->private_data; 850da3a9d3cSKalle Valo struct ath11k_soc_dp_stats *soc_stats = &ab->soc_stats; 851da3a9d3cSKalle Valo int len = 0, i, retval; 852da3a9d3cSKalle Valo const int size = 4096; 853da3a9d3cSKalle Valo static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = { 854da3a9d3cSKalle Valo "Overflow", "MPDU len", "FCS", "Decrypt", "TKIP MIC", 855da3a9d3cSKalle Valo "Unencrypt", "MSDU len", "MSDU limit", "WiFi parse", 856da3a9d3cSKalle Valo "AMSDU parse", "SA timeout", "DA timeout", 857da3a9d3cSKalle Valo "Flow timeout", "Flush req"}; 858da3a9d3cSKalle Valo static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = { 859da3a9d3cSKalle Valo "Desc addr zero", "Desc inval", "AMPDU in non BA", 860da3a9d3cSKalle Valo "Non BA dup", "BA dup", "Frame 2k jump", "BAR 2k jump", 861da3a9d3cSKalle Valo "Frame OOR", "BAR OOR", "No BA session", 862da3a9d3cSKalle Valo "Frame SN equal SSN", "PN check fail", "2k err", 863da3a9d3cSKalle Valo "PN err", "Desc blocked"}; 864da3a9d3cSKalle Valo 865da3a9d3cSKalle Valo char *buf; 866da3a9d3cSKalle Valo 867da3a9d3cSKalle Valo buf = kzalloc(size, GFP_KERNEL); 868da3a9d3cSKalle Valo if (!buf) 869da3a9d3cSKalle Valo return -ENOMEM; 870da3a9d3cSKalle Valo 871da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "SOC RX STATS:\n\n"); 872da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "err ring pkts: %u\n", 873da3a9d3cSKalle Valo soc_stats->err_ring_pkts); 874da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n", 875da3a9d3cSKalle Valo soc_stats->invalid_rbm); 876da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "RXDMA errors:\n"); 877da3a9d3cSKalle Valo for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++) 878da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "%s: %u\n", 879da3a9d3cSKalle Valo rxdma_err[i], soc_stats->rxdma_error[i]); 880da3a9d3cSKalle Valo 881da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "\nREO errors:\n"); 882da3a9d3cSKalle Valo for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++) 883da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "%s: %u\n", 884da3a9d3cSKalle Valo reo_err[i], soc_stats->reo_error[i]); 885da3a9d3cSKalle Valo 886da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n"); 887da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, 888da3a9d3cSKalle Valo "ring0: %u\nring1: %u\nring2: %u\nring3: %u\n", 889da3a9d3cSKalle Valo soc_stats->hal_reo_error[0], 890da3a9d3cSKalle Valo soc_stats->hal_reo_error[1], 891da3a9d3cSKalle Valo soc_stats->hal_reo_error[2], 892da3a9d3cSKalle Valo soc_stats->hal_reo_error[3]); 893da3a9d3cSKalle Valo 894da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "\nSOC TX STATS:\n"); 895da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n"); 896da3a9d3cSKalle Valo 89731582373SBaochen Qiang for (i = 0; i < ab->hw_params.max_tx_ring; i++) 898da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, "ring%d: %u\n", 899da3a9d3cSKalle Valo i, soc_stats->tx_err.desc_na[i]); 900da3a9d3cSKalle Valo 901da3a9d3cSKalle Valo len += scnprintf(buf + len, size - len, 902da3a9d3cSKalle Valo "\nMisc Transmit Failures: %d\n", 903da3a9d3cSKalle Valo atomic_read(&soc_stats->tx_err.misc_fail)); 904da3a9d3cSKalle Valo 905cb4e57dbSKalle Valo len += ath11k_debugfs_dump_soc_ring_bp_stats(ab, buf + len, size - len); 906da3a9d3cSKalle Valo 907da3a9d3cSKalle Valo if (len > size) 908da3a9d3cSKalle Valo len = size; 909da3a9d3cSKalle Valo retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); 910da3a9d3cSKalle Valo kfree(buf); 911da3a9d3cSKalle Valo 912da3a9d3cSKalle Valo return retval; 913da3a9d3cSKalle Valo } 914da3a9d3cSKalle Valo 915da3a9d3cSKalle Valo static const struct file_operations fops_soc_dp_stats = { 916cb4e57dbSKalle Valo .read = ath11k_debugfs_dump_soc_dp_stats, 917da3a9d3cSKalle Valo .open = simple_open, 918da3a9d3cSKalle Valo .owner = THIS_MODULE, 919da3a9d3cSKalle Valo .llseek = default_llseek, 920da3a9d3cSKalle Valo }; 921da3a9d3cSKalle Valo 922f295ad91SSeevalamuthu Mariappan static ssize_t ath11k_write_fw_dbglog(struct file *file, 923f295ad91SSeevalamuthu Mariappan const char __user *user_buf, 924f295ad91SSeevalamuthu Mariappan size_t count, loff_t *ppos) 925f295ad91SSeevalamuthu Mariappan { 926f295ad91SSeevalamuthu Mariappan struct ath11k *ar = file->private_data; 927f295ad91SSeevalamuthu Mariappan char buf[128] = {0}; 928f295ad91SSeevalamuthu Mariappan struct ath11k_fw_dbglog dbglog; 929f295ad91SSeevalamuthu Mariappan unsigned int param, mod_id_index, is_end; 930f295ad91SSeevalamuthu Mariappan u64 value; 931f295ad91SSeevalamuthu Mariappan int ret, num; 932f295ad91SSeevalamuthu Mariappan 933f295ad91SSeevalamuthu Mariappan ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, 934f295ad91SSeevalamuthu Mariappan user_buf, count); 935f295ad91SSeevalamuthu Mariappan if (ret <= 0) 936f295ad91SSeevalamuthu Mariappan return ret; 937f295ad91SSeevalamuthu Mariappan 938f295ad91SSeevalamuthu Mariappan num = sscanf(buf, "%u %llx %u %u", ¶m, &value, &mod_id_index, &is_end); 939f295ad91SSeevalamuthu Mariappan 940f295ad91SSeevalamuthu Mariappan if (num < 2) 941f295ad91SSeevalamuthu Mariappan return -EINVAL; 942f295ad91SSeevalamuthu Mariappan 943f295ad91SSeevalamuthu Mariappan mutex_lock(&ar->conf_mutex); 944f295ad91SSeevalamuthu Mariappan if (param == WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP || 945f295ad91SSeevalamuthu Mariappan param == WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP) { 946f295ad91SSeevalamuthu Mariappan if (num != 4 || mod_id_index > (MAX_MODULE_ID_BITMAP_WORDS - 1)) { 947f295ad91SSeevalamuthu Mariappan ret = -EINVAL; 948f295ad91SSeevalamuthu Mariappan goto out; 949f295ad91SSeevalamuthu Mariappan } 950f295ad91SSeevalamuthu Mariappan ar->debug.module_id_bitmap[mod_id_index] = upper_32_bits(value); 951f295ad91SSeevalamuthu Mariappan if (!is_end) { 952f295ad91SSeevalamuthu Mariappan ret = count; 953f295ad91SSeevalamuthu Mariappan goto out; 954f295ad91SSeevalamuthu Mariappan } 955f295ad91SSeevalamuthu Mariappan } else { 956f295ad91SSeevalamuthu Mariappan if (num != 2) { 957f295ad91SSeevalamuthu Mariappan ret = -EINVAL; 958f295ad91SSeevalamuthu Mariappan goto out; 959f295ad91SSeevalamuthu Mariappan } 960f295ad91SSeevalamuthu Mariappan } 961f295ad91SSeevalamuthu Mariappan 962f295ad91SSeevalamuthu Mariappan dbglog.param = param; 963f295ad91SSeevalamuthu Mariappan dbglog.value = lower_32_bits(value); 964f295ad91SSeevalamuthu Mariappan ret = ath11k_wmi_fw_dbglog_cfg(ar, ar->debug.module_id_bitmap, &dbglog); 965f295ad91SSeevalamuthu Mariappan if (ret) { 966f295ad91SSeevalamuthu Mariappan ath11k_warn(ar->ab, "fw dbglog config failed from debugfs: %d\n", 967f295ad91SSeevalamuthu Mariappan ret); 968f295ad91SSeevalamuthu Mariappan goto out; 969f295ad91SSeevalamuthu Mariappan } 970f295ad91SSeevalamuthu Mariappan 971f295ad91SSeevalamuthu Mariappan ret = count; 972f295ad91SSeevalamuthu Mariappan 973f295ad91SSeevalamuthu Mariappan out: 974f295ad91SSeevalamuthu Mariappan mutex_unlock(&ar->conf_mutex); 975f295ad91SSeevalamuthu Mariappan return ret; 976f295ad91SSeevalamuthu Mariappan } 977f295ad91SSeevalamuthu Mariappan 978f295ad91SSeevalamuthu Mariappan static const struct file_operations fops_fw_dbglog = { 979f295ad91SSeevalamuthu Mariappan .write = ath11k_write_fw_dbglog, 980f295ad91SSeevalamuthu Mariappan .open = simple_open, 981f295ad91SSeevalamuthu Mariappan .owner = THIS_MODULE, 982f295ad91SSeevalamuthu Mariappan .llseek = default_llseek, 983f295ad91SSeevalamuthu Mariappan }; 984f295ad91SSeevalamuthu Mariappan 985cb4e57dbSKalle Valo int ath11k_debugfs_pdev_create(struct ath11k_base *ab) 986da3a9d3cSKalle Valo { 987da3a9d3cSKalle Valo if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) 988da3a9d3cSKalle Valo return 0; 989da3a9d3cSKalle Valo 990da3a9d3cSKalle Valo ab->debugfs_soc = debugfs_create_dir(ab->hw_params.name, ab->debugfs_ath11k); 991da3a9d3cSKalle Valo if (IS_ERR(ab->debugfs_soc)) 992da3a9d3cSKalle Valo return PTR_ERR(ab->debugfs_soc); 993da3a9d3cSKalle Valo 994da3a9d3cSKalle Valo debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab, 995da3a9d3cSKalle Valo &fops_simulate_fw_crash); 996da3a9d3cSKalle Valo 997da3a9d3cSKalle Valo debugfs_create_file("soc_dp_stats", 0600, ab->debugfs_soc, ab, 998da3a9d3cSKalle Valo &fops_soc_dp_stats); 999da3a9d3cSKalle Valo 1000da3a9d3cSKalle Valo return 0; 1001da3a9d3cSKalle Valo } 1002da3a9d3cSKalle Valo 1003cb4e57dbSKalle Valo void ath11k_debugfs_pdev_destroy(struct ath11k_base *ab) 1004da3a9d3cSKalle Valo { 1005089ba909SCarl Huang debugfs_remove_recursive(ab->debugfs_soc); 1006089ba909SCarl Huang ab->debugfs_soc = NULL; 1007da3a9d3cSKalle Valo } 1008da3a9d3cSKalle Valo 1009cb4e57dbSKalle Valo int ath11k_debugfs_soc_create(struct ath11k_base *ab) 1010da3a9d3cSKalle Valo { 1011da3a9d3cSKalle Valo ab->debugfs_ath11k = debugfs_create_dir("ath11k", NULL); 1012da3a9d3cSKalle Valo 1013476c1d3cSAlex Dewar return PTR_ERR_OR_ZERO(ab->debugfs_ath11k); 1014da3a9d3cSKalle Valo } 1015da3a9d3cSKalle Valo 1016cb4e57dbSKalle Valo void ath11k_debugfs_soc_destroy(struct ath11k_base *ab) 1017da3a9d3cSKalle Valo { 1018089ba909SCarl Huang debugfs_remove_recursive(ab->debugfs_ath11k); 1019089ba909SCarl Huang ab->debugfs_ath11k = NULL; 1020da3a9d3cSKalle Valo } 102161a57e51SAnilkumar Kolli EXPORT_SYMBOL(ath11k_debugfs_soc_destroy); 1022da3a9d3cSKalle Valo 1023cb4e57dbSKalle Valo void ath11k_debugfs_fw_stats_init(struct ath11k *ar) 1024da3a9d3cSKalle Valo { 1025da3a9d3cSKalle Valo struct dentry *fwstats_dir = debugfs_create_dir("fw_stats", 1026da3a9d3cSKalle Valo ar->debug.debugfs_pdev); 1027da3a9d3cSKalle Valo 1028da3a9d3cSKalle Valo ar->debug.fw_stats.debugfs_fwstats = fwstats_dir; 1029da3a9d3cSKalle Valo 1030da3a9d3cSKalle Valo /* all stats debugfs files created are under "fw_stats" directory 1031da3a9d3cSKalle Valo * created per PDEV 1032da3a9d3cSKalle Valo */ 1033da3a9d3cSKalle Valo debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar, 1034da3a9d3cSKalle Valo &fops_pdev_stats); 1035da3a9d3cSKalle Valo debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar, 1036da3a9d3cSKalle Valo &fops_vdev_stats); 1037da3a9d3cSKalle Valo debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar, 1038da3a9d3cSKalle Valo &fops_bcn_stats); 1039da3a9d3cSKalle Valo 1040da3a9d3cSKalle Valo INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs); 1041da3a9d3cSKalle Valo INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs); 1042da3a9d3cSKalle Valo INIT_LIST_HEAD(&ar->debug.fw_stats.bcn); 1043da3a9d3cSKalle Valo 1044da3a9d3cSKalle Valo init_completion(&ar->debug.fw_stats_complete); 1045da3a9d3cSKalle Valo } 1046da3a9d3cSKalle Valo 1047da3a9d3cSKalle Valo static ssize_t ath11k_write_pktlog_filter(struct file *file, 1048da3a9d3cSKalle Valo const char __user *ubuf, 1049da3a9d3cSKalle Valo size_t count, loff_t *ppos) 1050da3a9d3cSKalle Valo { 1051da3a9d3cSKalle Valo struct ath11k *ar = file->private_data; 1052da3a9d3cSKalle Valo struct ath11k_base *ab = ar->ab; 1053da3a9d3cSKalle Valo struct htt_rx_ring_tlv_filter tlv_filter = {0}; 1054da3a9d3cSKalle Valo u32 rx_filter = 0, ring_id, filter, mode; 1055da3a9d3cSKalle Valo u8 buf[128] = {0}; 1056ab18e3bcSAnilkumar Kolli int i, ret, rx_buf_sz = 0; 1057da3a9d3cSKalle Valo ssize_t rc; 1058da3a9d3cSKalle Valo 1059da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex); 1060da3a9d3cSKalle Valo if (ar->state != ATH11K_STATE_ON) { 1061da3a9d3cSKalle Valo ret = -ENETDOWN; 1062da3a9d3cSKalle Valo goto out; 1063da3a9d3cSKalle Valo } 1064da3a9d3cSKalle Valo 1065da3a9d3cSKalle Valo rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); 1066da3a9d3cSKalle Valo if (rc < 0) { 1067da3a9d3cSKalle Valo ret = rc; 1068da3a9d3cSKalle Valo goto out; 1069da3a9d3cSKalle Valo } 1070da3a9d3cSKalle Valo buf[rc] = '\0'; 1071da3a9d3cSKalle Valo 1072da3a9d3cSKalle Valo ret = sscanf(buf, "0x%x %u", &filter, &mode); 1073da3a9d3cSKalle Valo if (ret != 2) { 1074da3a9d3cSKalle Valo ret = -EINVAL; 1075da3a9d3cSKalle Valo goto out; 1076da3a9d3cSKalle Valo } 1077da3a9d3cSKalle Valo 1078da3a9d3cSKalle Valo if (filter) { 1079da3a9d3cSKalle Valo ret = ath11k_wmi_pdev_pktlog_enable(ar, filter); 1080da3a9d3cSKalle Valo if (ret) { 1081da3a9d3cSKalle Valo ath11k_warn(ar->ab, 1082da3a9d3cSKalle Valo "failed to enable pktlog filter %x: %d\n", 1083da3a9d3cSKalle Valo ar->debug.pktlog_filter, ret); 1084da3a9d3cSKalle Valo goto out; 1085da3a9d3cSKalle Valo } 1086da3a9d3cSKalle Valo } else { 1087da3a9d3cSKalle Valo ret = ath11k_wmi_pdev_pktlog_disable(ar); 1088da3a9d3cSKalle Valo if (ret) { 1089da3a9d3cSKalle Valo ath11k_warn(ar->ab, "failed to disable pktlog: %d\n", ret); 1090da3a9d3cSKalle Valo goto out; 1091da3a9d3cSKalle Valo } 1092da3a9d3cSKalle Valo } 1093da3a9d3cSKalle Valo 1094ab18e3bcSAnilkumar Kolli /* Clear rx filter set for monitor mode and rx status */ 1095ab18e3bcSAnilkumar Kolli for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { 1096ab18e3bcSAnilkumar Kolli ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; 1097ab18e3bcSAnilkumar Kolli ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, 1098ab18e3bcSAnilkumar Kolli HAL_RXDMA_MONITOR_STATUS, 1099ab18e3bcSAnilkumar Kolli rx_buf_sz, &tlv_filter); 1100ab18e3bcSAnilkumar Kolli if (ret) { 1101ab18e3bcSAnilkumar Kolli ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); 1102ab18e3bcSAnilkumar Kolli goto out; 1103ab18e3bcSAnilkumar Kolli } 1104ab18e3bcSAnilkumar Kolli } 1105da3a9d3cSKalle Valo #define HTT_RX_FILTER_TLV_LITE_MODE \ 1106da3a9d3cSKalle Valo (HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ 1107da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ 1108da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ 1109da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ 1110da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \ 1111da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_MPDU_START) 1112da3a9d3cSKalle Valo 1113da3a9d3cSKalle Valo if (mode == ATH11K_PKTLOG_MODE_FULL) { 1114da3a9d3cSKalle Valo rx_filter = HTT_RX_FILTER_TLV_LITE_MODE | 1115da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_MSDU_START | 1116da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_MSDU_END | 1117da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_MPDU_END | 1118da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | 1119da3a9d3cSKalle Valo HTT_RX_FILTER_TLV_FLAGS_ATTENTION; 1120ab18e3bcSAnilkumar Kolli rx_buf_sz = DP_RX_BUFFER_SIZE; 1121da3a9d3cSKalle Valo } else if (mode == ATH11K_PKTLOG_MODE_LITE) { 1122da3a9d3cSKalle Valo ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar, 1123da3a9d3cSKalle Valo HTT_PPDU_STATS_TAG_PKTLOG); 1124da3a9d3cSKalle Valo if (ret) { 1125da3a9d3cSKalle Valo ath11k_err(ar->ab, "failed to enable pktlog lite: %d\n", ret); 1126da3a9d3cSKalle Valo goto out; 1127da3a9d3cSKalle Valo } 1128da3a9d3cSKalle Valo 1129da3a9d3cSKalle Valo rx_filter = HTT_RX_FILTER_TLV_LITE_MODE; 1130ab18e3bcSAnilkumar Kolli rx_buf_sz = DP_RX_BUFFER_SIZE_LITE; 1131da3a9d3cSKalle Valo } else { 1132ab18e3bcSAnilkumar Kolli rx_buf_sz = DP_RX_BUFFER_SIZE; 1133ab18e3bcSAnilkumar Kolli tlv_filter = ath11k_mac_mon_status_filter_default; 1134ab18e3bcSAnilkumar Kolli rx_filter = tlv_filter.rx_filter; 1135ab18e3bcSAnilkumar Kolli 1136da3a9d3cSKalle Valo ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar, 1137da3a9d3cSKalle Valo HTT_PPDU_STATS_TAG_DEFAULT); 1138da3a9d3cSKalle Valo if (ret) { 1139da3a9d3cSKalle Valo ath11k_err(ar->ab, "failed to send htt ppdu stats req: %d\n", 1140da3a9d3cSKalle Valo ret); 1141da3a9d3cSKalle Valo goto out; 1142da3a9d3cSKalle Valo } 1143da3a9d3cSKalle Valo } 1144da3a9d3cSKalle Valo 1145da3a9d3cSKalle Valo tlv_filter.rx_filter = rx_filter; 1146da3a9d3cSKalle Valo if (rx_filter) { 1147da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0; 1148da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1; 1149da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2; 1150da3a9d3cSKalle Valo tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 | 1151da3a9d3cSKalle Valo HTT_RX_FP_DATA_FILTER_FLASG3; 1152da3a9d3cSKalle Valo } 1153da3a9d3cSKalle Valo 1154da3a9d3cSKalle Valo for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { 1155da3a9d3cSKalle Valo ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; 1156da3a9d3cSKalle Valo ret = ath11k_dp_tx_htt_rx_filter_setup(ab, ring_id, 1157da3a9d3cSKalle Valo ar->dp.mac_id + i, 1158da3a9d3cSKalle Valo HAL_RXDMA_MONITOR_STATUS, 1159ab18e3bcSAnilkumar Kolli rx_buf_sz, &tlv_filter); 1160da3a9d3cSKalle Valo 1161da3a9d3cSKalle Valo if (ret) { 1162da3a9d3cSKalle Valo ath11k_warn(ab, "failed to set rx filter for monitor status ring\n"); 1163da3a9d3cSKalle Valo goto out; 1164da3a9d3cSKalle Valo } 1165da3a9d3cSKalle Valo } 1166da3a9d3cSKalle Valo 1167ab18e3bcSAnilkumar Kolli ath11k_info(ab, "pktlog mode %s\n", 1168ab18e3bcSAnilkumar Kolli ((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite")); 1169da3a9d3cSKalle Valo 1170da3a9d3cSKalle Valo ar->debug.pktlog_filter = filter; 1171da3a9d3cSKalle Valo ar->debug.pktlog_mode = mode; 1172da3a9d3cSKalle Valo ret = count; 1173da3a9d3cSKalle Valo 1174da3a9d3cSKalle Valo out: 1175da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 1176da3a9d3cSKalle Valo return ret; 1177da3a9d3cSKalle Valo } 1178da3a9d3cSKalle Valo 1179da3a9d3cSKalle Valo static ssize_t ath11k_read_pktlog_filter(struct file *file, 1180da3a9d3cSKalle Valo char __user *ubuf, 1181da3a9d3cSKalle Valo size_t count, loff_t *ppos) 1182da3a9d3cSKalle Valo 1183da3a9d3cSKalle Valo { 1184da3a9d3cSKalle Valo char buf[32] = {0}; 1185da3a9d3cSKalle Valo struct ath11k *ar = file->private_data; 1186da3a9d3cSKalle Valo int len = 0; 1187da3a9d3cSKalle Valo 1188da3a9d3cSKalle Valo mutex_lock(&ar->conf_mutex); 1189da3a9d3cSKalle Valo len = scnprintf(buf, sizeof(buf) - len, "%08x %08x\n", 1190da3a9d3cSKalle Valo ar->debug.pktlog_filter, 1191da3a9d3cSKalle Valo ar->debug.pktlog_mode); 1192da3a9d3cSKalle Valo mutex_unlock(&ar->conf_mutex); 1193da3a9d3cSKalle Valo 1194da3a9d3cSKalle Valo return simple_read_from_buffer(ubuf, count, ppos, buf, len); 1195da3a9d3cSKalle Valo } 1196da3a9d3cSKalle Valo 1197da3a9d3cSKalle Valo static const struct file_operations fops_pktlog_filter = { 1198da3a9d3cSKalle Valo .read = ath11k_read_pktlog_filter, 1199da3a9d3cSKalle Valo .write = ath11k_write_pktlog_filter, 1200da3a9d3cSKalle Valo .open = simple_open 1201da3a9d3cSKalle Valo }; 1202da3a9d3cSKalle Valo 1203da3a9d3cSKalle Valo static ssize_t ath11k_write_simulate_radar(struct file *file, 1204da3a9d3cSKalle Valo const char __user *user_buf, 1205da3a9d3cSKalle Valo size_t count, loff_t *ppos) 1206da3a9d3cSKalle Valo { 1207da3a9d3cSKalle Valo struct ath11k *ar = file->private_data; 1208da3a9d3cSKalle Valo int ret; 1209da3a9d3cSKalle Valo 1210da3a9d3cSKalle Valo ret = ath11k_wmi_simulate_radar(ar); 1211da3a9d3cSKalle Valo if (ret) 1212da3a9d3cSKalle Valo return ret; 1213da3a9d3cSKalle Valo 1214da3a9d3cSKalle Valo return count; 1215da3a9d3cSKalle Valo } 1216da3a9d3cSKalle Valo 1217da3a9d3cSKalle Valo static const struct file_operations fops_simulate_radar = { 1218da3a9d3cSKalle Valo .write = ath11k_write_simulate_radar, 1219da3a9d3cSKalle Valo .open = simple_open 1220da3a9d3cSKalle Valo }; 1221da3a9d3cSKalle Valo 1222691425b4SVenkateswara Naralasetty static ssize_t ath11k_debug_dump_dbr_entries(struct file *file, 1223691425b4SVenkateswara Naralasetty char __user *user_buf, 1224691425b4SVenkateswara Naralasetty size_t count, loff_t *ppos) 1225691425b4SVenkateswara Naralasetty { 1226691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_data *dbr_dbg_data = file->private_data; 1227691425b4SVenkateswara Naralasetty static const char * const event_id_to_string[] = {"empty", "Rx", "Replenish"}; 1228691425b4SVenkateswara Naralasetty int size = ATH11K_DEBUG_DBR_ENTRIES_MAX * 100; 1229691425b4SVenkateswara Naralasetty char *buf; 1230691425b4SVenkateswara Naralasetty int i, ret; 1231691425b4SVenkateswara Naralasetty int len = 0; 1232691425b4SVenkateswara Naralasetty 1233691425b4SVenkateswara Naralasetty buf = kzalloc(size, GFP_KERNEL); 1234691425b4SVenkateswara Naralasetty if (!buf) 1235691425b4SVenkateswara Naralasetty return -ENOMEM; 1236691425b4SVenkateswara Naralasetty 1237691425b4SVenkateswara Naralasetty len += scnprintf(buf + len, size - len, 1238691425b4SVenkateswara Naralasetty "-----------------------------------------\n"); 1239691425b4SVenkateswara Naralasetty len += scnprintf(buf + len, size - len, 1240691425b4SVenkateswara Naralasetty "| idx | hp | tp | timestamp | event |\n"); 1241691425b4SVenkateswara Naralasetty len += scnprintf(buf + len, size - len, 1242691425b4SVenkateswara Naralasetty "-----------------------------------------\n"); 1243691425b4SVenkateswara Naralasetty 1244691425b4SVenkateswara Naralasetty spin_lock_bh(&dbr_dbg_data->lock); 1245691425b4SVenkateswara Naralasetty 1246691425b4SVenkateswara Naralasetty for (i = 0; i < dbr_dbg_data->num_ring_debug_entries; i++) { 1247691425b4SVenkateswara Naralasetty len += scnprintf(buf + len, size - len, 1248691425b4SVenkateswara Naralasetty "|%4u|%8u|%8u|%11llu|%8s|\n", i, 1249691425b4SVenkateswara Naralasetty dbr_dbg_data->entries[i].hp, 1250691425b4SVenkateswara Naralasetty dbr_dbg_data->entries[i].tp, 1251691425b4SVenkateswara Naralasetty dbr_dbg_data->entries[i].timestamp, 1252691425b4SVenkateswara Naralasetty event_id_to_string[dbr_dbg_data->entries[i].event]); 1253691425b4SVenkateswara Naralasetty } 1254691425b4SVenkateswara Naralasetty 1255691425b4SVenkateswara Naralasetty spin_unlock_bh(&dbr_dbg_data->lock); 1256691425b4SVenkateswara Naralasetty 1257691425b4SVenkateswara Naralasetty ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); 1258691425b4SVenkateswara Naralasetty kfree(buf); 1259691425b4SVenkateswara Naralasetty 1260691425b4SVenkateswara Naralasetty return ret; 1261691425b4SVenkateswara Naralasetty } 1262691425b4SVenkateswara Naralasetty 1263691425b4SVenkateswara Naralasetty static const struct file_operations fops_debug_dump_dbr_entries = { 1264691425b4SVenkateswara Naralasetty .read = ath11k_debug_dump_dbr_entries, 1265691425b4SVenkateswara Naralasetty .open = simple_open, 1266691425b4SVenkateswara Naralasetty .owner = THIS_MODULE, 1267691425b4SVenkateswara Naralasetty .llseek = default_llseek, 1268691425b4SVenkateswara Naralasetty }; 1269691425b4SVenkateswara Naralasetty 1270691425b4SVenkateswara Naralasetty static void ath11k_debugfs_dbr_dbg_destroy(struct ath11k *ar, int dbr_id) 1271691425b4SVenkateswara Naralasetty { 1272691425b4SVenkateswara Naralasetty struct ath11k_debug_dbr *dbr_debug; 1273691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_data *dbr_dbg_data; 1274691425b4SVenkateswara Naralasetty 1275691425b4SVenkateswara Naralasetty if (!ar->debug.dbr_debug[dbr_id]) 1276691425b4SVenkateswara Naralasetty return; 1277691425b4SVenkateswara Naralasetty 1278691425b4SVenkateswara Naralasetty dbr_debug = ar->debug.dbr_debug[dbr_id]; 1279691425b4SVenkateswara Naralasetty dbr_dbg_data = &dbr_debug->dbr_dbg_data; 1280691425b4SVenkateswara Naralasetty 1281691425b4SVenkateswara Naralasetty debugfs_remove_recursive(dbr_debug->dbr_debugfs); 1282691425b4SVenkateswara Naralasetty kfree(dbr_dbg_data->entries); 1283691425b4SVenkateswara Naralasetty kfree(dbr_debug); 1284691425b4SVenkateswara Naralasetty ar->debug.dbr_debug[dbr_id] = NULL; 1285691425b4SVenkateswara Naralasetty } 1286691425b4SVenkateswara Naralasetty 1287691425b4SVenkateswara Naralasetty static int ath11k_debugfs_dbr_dbg_init(struct ath11k *ar, int dbr_id) 1288691425b4SVenkateswara Naralasetty { 1289691425b4SVenkateswara Naralasetty struct ath11k_debug_dbr *dbr_debug; 1290691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_data *dbr_dbg_data; 1291691425b4SVenkateswara Naralasetty static const char * const dbr_id_to_str[] = {"spectral", "CFR"}; 1292691425b4SVenkateswara Naralasetty 1293691425b4SVenkateswara Naralasetty if (ar->debug.dbr_debug[dbr_id]) 1294691425b4SVenkateswara Naralasetty return 0; 1295691425b4SVenkateswara Naralasetty 1296691425b4SVenkateswara Naralasetty ar->debug.dbr_debug[dbr_id] = kzalloc(sizeof(*dbr_debug), 1297691425b4SVenkateswara Naralasetty GFP_KERNEL); 1298691425b4SVenkateswara Naralasetty 1299691425b4SVenkateswara Naralasetty if (!ar->debug.dbr_debug[dbr_id]) 1300691425b4SVenkateswara Naralasetty return -ENOMEM; 1301691425b4SVenkateswara Naralasetty 1302691425b4SVenkateswara Naralasetty dbr_debug = ar->debug.dbr_debug[dbr_id]; 1303691425b4SVenkateswara Naralasetty dbr_dbg_data = &dbr_debug->dbr_dbg_data; 1304691425b4SVenkateswara Naralasetty 1305691425b4SVenkateswara Naralasetty if (dbr_debug->dbr_debugfs) 1306691425b4SVenkateswara Naralasetty return 0; 1307691425b4SVenkateswara Naralasetty 1308691425b4SVenkateswara Naralasetty dbr_debug->dbr_debugfs = debugfs_create_dir(dbr_id_to_str[dbr_id], 1309691425b4SVenkateswara Naralasetty ar->debug.debugfs_pdev); 1310691425b4SVenkateswara Naralasetty if (IS_ERR_OR_NULL(dbr_debug->dbr_debugfs)) { 1311691425b4SVenkateswara Naralasetty if (IS_ERR(dbr_debug->dbr_debugfs)) 1312691425b4SVenkateswara Naralasetty return PTR_ERR(dbr_debug->dbr_debugfs); 1313691425b4SVenkateswara Naralasetty return -ENOMEM; 1314691425b4SVenkateswara Naralasetty } 1315691425b4SVenkateswara Naralasetty 1316691425b4SVenkateswara Naralasetty dbr_debug->dbr_debug_enabled = true; 1317691425b4SVenkateswara Naralasetty dbr_dbg_data->num_ring_debug_entries = ATH11K_DEBUG_DBR_ENTRIES_MAX; 1318691425b4SVenkateswara Naralasetty dbr_dbg_data->dbr_debug_idx = 0; 1319691425b4SVenkateswara Naralasetty dbr_dbg_data->entries = kcalloc(ATH11K_DEBUG_DBR_ENTRIES_MAX, 1320691425b4SVenkateswara Naralasetty sizeof(struct ath11k_dbg_dbr_entry), 1321691425b4SVenkateswara Naralasetty GFP_KERNEL); 1322691425b4SVenkateswara Naralasetty if (!dbr_dbg_data->entries) 1323691425b4SVenkateswara Naralasetty return -ENOMEM; 1324691425b4SVenkateswara Naralasetty 1325691425b4SVenkateswara Naralasetty spin_lock_init(&dbr_dbg_data->lock); 1326691425b4SVenkateswara Naralasetty 1327691425b4SVenkateswara Naralasetty debugfs_create_file("dump_dbr_debug", 0444, dbr_debug->dbr_debugfs, 1328691425b4SVenkateswara Naralasetty dbr_dbg_data, &fops_debug_dump_dbr_entries); 1329691425b4SVenkateswara Naralasetty 1330691425b4SVenkateswara Naralasetty return 0; 1331691425b4SVenkateswara Naralasetty } 1332691425b4SVenkateswara Naralasetty 1333691425b4SVenkateswara Naralasetty static ssize_t ath11k_debugfs_write_enable_dbr_dbg(struct file *file, 1334691425b4SVenkateswara Naralasetty const char __user *ubuf, 1335691425b4SVenkateswara Naralasetty size_t count, loff_t *ppos) 1336691425b4SVenkateswara Naralasetty { 1337691425b4SVenkateswara Naralasetty struct ath11k *ar = file->private_data; 1338691425b4SVenkateswara Naralasetty char buf[32] = {0}; 1339691425b4SVenkateswara Naralasetty u32 dbr_id, enable; 1340691425b4SVenkateswara Naralasetty int ret; 1341691425b4SVenkateswara Naralasetty 1342691425b4SVenkateswara Naralasetty mutex_lock(&ar->conf_mutex); 1343691425b4SVenkateswara Naralasetty 1344691425b4SVenkateswara Naralasetty if (ar->state != ATH11K_STATE_ON) { 1345691425b4SVenkateswara Naralasetty ret = -ENETDOWN; 1346691425b4SVenkateswara Naralasetty goto out; 1347691425b4SVenkateswara Naralasetty } 1348691425b4SVenkateswara Naralasetty 1349691425b4SVenkateswara Naralasetty ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); 1350691425b4SVenkateswara Naralasetty if (ret < 0) 1351691425b4SVenkateswara Naralasetty goto out; 1352691425b4SVenkateswara Naralasetty 1353691425b4SVenkateswara Naralasetty buf[ret] = '\0'; 1354691425b4SVenkateswara Naralasetty ret = sscanf(buf, "%u %u", &dbr_id, &enable); 1355691425b4SVenkateswara Naralasetty if (ret != 2 || dbr_id > 1 || enable > 1) { 1356691425b4SVenkateswara Naralasetty ret = -EINVAL; 1357691425b4SVenkateswara Naralasetty ath11k_warn(ar->ab, "usage: echo <dbr_id> <val> dbr_id:0-Spectral 1-CFR val:0-disable 1-enable\n"); 1358691425b4SVenkateswara Naralasetty goto out; 1359691425b4SVenkateswara Naralasetty } 1360691425b4SVenkateswara Naralasetty 1361691425b4SVenkateswara Naralasetty if (enable) { 1362691425b4SVenkateswara Naralasetty ret = ath11k_debugfs_dbr_dbg_init(ar, dbr_id); 1363691425b4SVenkateswara Naralasetty if (ret) { 1364691425b4SVenkateswara Naralasetty ath11k_warn(ar->ab, "db ring module debugfs init failed: %d\n", 1365691425b4SVenkateswara Naralasetty ret); 1366691425b4SVenkateswara Naralasetty goto out; 1367691425b4SVenkateswara Naralasetty } 1368691425b4SVenkateswara Naralasetty } else { 1369691425b4SVenkateswara Naralasetty ath11k_debugfs_dbr_dbg_destroy(ar, dbr_id); 1370691425b4SVenkateswara Naralasetty } 1371691425b4SVenkateswara Naralasetty 1372691425b4SVenkateswara Naralasetty ret = count; 1373691425b4SVenkateswara Naralasetty out: 1374691425b4SVenkateswara Naralasetty mutex_unlock(&ar->conf_mutex); 1375691425b4SVenkateswara Naralasetty return ret; 1376691425b4SVenkateswara Naralasetty } 1377691425b4SVenkateswara Naralasetty 1378691425b4SVenkateswara Naralasetty static const struct file_operations fops_dbr_debug = { 1379691425b4SVenkateswara Naralasetty .write = ath11k_debugfs_write_enable_dbr_dbg, 1380691425b4SVenkateswara Naralasetty .open = simple_open, 1381691425b4SVenkateswara Naralasetty .owner = THIS_MODULE, 1382691425b4SVenkateswara Naralasetty .llseek = default_llseek, 1383691425b4SVenkateswara Naralasetty }; 1384691425b4SVenkateswara Naralasetty 1385cb4e57dbSKalle Valo int ath11k_debugfs_register(struct ath11k *ar) 1386da3a9d3cSKalle Valo { 1387da3a9d3cSKalle Valo struct ath11k_base *ab = ar->ab; 1388da3a9d3cSKalle Valo char pdev_name[5]; 1389da3a9d3cSKalle Valo char buf[100] = {0}; 1390da3a9d3cSKalle Valo 1391da3a9d3cSKalle Valo snprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx); 1392da3a9d3cSKalle Valo 1393da3a9d3cSKalle Valo ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc); 1394da3a9d3cSKalle Valo if (IS_ERR(ar->debug.debugfs_pdev)) 1395da3a9d3cSKalle Valo return PTR_ERR(ar->debug.debugfs_pdev); 1396da3a9d3cSKalle Valo 1397da3a9d3cSKalle Valo /* Create a symlink under ieee80211/phy* */ 1398da3a9d3cSKalle Valo snprintf(buf, 100, "../../ath11k/%pd2", ar->debug.debugfs_pdev); 1399da3a9d3cSKalle Valo debugfs_create_symlink("ath11k", ar->hw->wiphy->debugfsdir, buf); 1400da3a9d3cSKalle Valo 140156292162SKalle Valo ath11k_debugfs_htt_stats_init(ar); 1402da3a9d3cSKalle Valo 1403cb4e57dbSKalle Valo ath11k_debugfs_fw_stats_init(ar); 1404da3a9d3cSKalle Valo 1405da3a9d3cSKalle Valo debugfs_create_file("ext_tx_stats", 0644, 1406da3a9d3cSKalle Valo ar->debug.debugfs_pdev, ar, 1407da3a9d3cSKalle Valo &fops_extd_tx_stats); 1408da3a9d3cSKalle Valo debugfs_create_file("ext_rx_stats", 0644, 1409da3a9d3cSKalle Valo ar->debug.debugfs_pdev, ar, 1410da3a9d3cSKalle Valo &fops_extd_rx_stats); 1411da3a9d3cSKalle Valo debugfs_create_file("pktlog_filter", 0644, 1412da3a9d3cSKalle Valo ar->debug.debugfs_pdev, ar, 1413da3a9d3cSKalle Valo &fops_pktlog_filter); 1414f295ad91SSeevalamuthu Mariappan debugfs_create_file("fw_dbglog_config", 0600, 1415f295ad91SSeevalamuthu Mariappan ar->debug.debugfs_pdev, ar, 1416f295ad91SSeevalamuthu Mariappan &fops_fw_dbglog); 1417da3a9d3cSKalle Valo 1418da3a9d3cSKalle Valo if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) { 1419da3a9d3cSKalle Valo debugfs_create_file("dfs_simulate_radar", 0200, 1420da3a9d3cSKalle Valo ar->debug.debugfs_pdev, ar, 1421da3a9d3cSKalle Valo &fops_simulate_radar); 1422da3a9d3cSKalle Valo debugfs_create_bool("dfs_block_radar_events", 0200, 1423da3a9d3cSKalle Valo ar->debug.debugfs_pdev, 1424da3a9d3cSKalle Valo &ar->dfs_block_radar_events); 1425da3a9d3cSKalle Valo } 1426da3a9d3cSKalle Valo 1427691425b4SVenkateswara Naralasetty if (ab->hw_params.dbr_debug_support) 1428691425b4SVenkateswara Naralasetty debugfs_create_file("enable_dbr_debug", 0200, ar->debug.debugfs_pdev, 1429691425b4SVenkateswara Naralasetty ar, &fops_dbr_debug); 1430691425b4SVenkateswara Naralasetty 1431da3a9d3cSKalle Valo return 0; 1432da3a9d3cSKalle Valo } 1433da3a9d3cSKalle Valo 1434cb4e57dbSKalle Valo void ath11k_debugfs_unregister(struct ath11k *ar) 1435da3a9d3cSKalle Valo { 1436691425b4SVenkateswara Naralasetty struct ath11k_debug_dbr *dbr_debug; 1437691425b4SVenkateswara Naralasetty struct ath11k_dbg_dbr_data *dbr_dbg_data; 1438691425b4SVenkateswara Naralasetty int i; 1439691425b4SVenkateswara Naralasetty 1440691425b4SVenkateswara Naralasetty for (i = 0; i < WMI_DIRECT_BUF_MAX; i++) { 1441691425b4SVenkateswara Naralasetty dbr_debug = ar->debug.dbr_debug[i]; 1442691425b4SVenkateswara Naralasetty if (!dbr_debug) 1443691425b4SVenkateswara Naralasetty continue; 1444691425b4SVenkateswara Naralasetty 1445691425b4SVenkateswara Naralasetty dbr_dbg_data = &dbr_debug->dbr_dbg_data; 1446691425b4SVenkateswara Naralasetty kfree(dbr_dbg_data->entries); 1447691425b4SVenkateswara Naralasetty debugfs_remove_recursive(dbr_debug->dbr_debugfs); 1448691425b4SVenkateswara Naralasetty kfree(dbr_debug); 1449691425b4SVenkateswara Naralasetty ar->debug.dbr_debug[i] = NULL; 1450691425b4SVenkateswara Naralasetty } 1451da3a9d3cSKalle Valo } 1452fe98a613SJohn Crispin 1453fe98a613SJohn Crispin static ssize_t ath11k_write_twt_add_dialog(struct file *file, 1454fe98a613SJohn Crispin const char __user *ubuf, 1455fe98a613SJohn Crispin size_t count, loff_t *ppos) 1456fe98a613SJohn Crispin { 1457fe98a613SJohn Crispin struct ath11k_vif *arvif = file->private_data; 1458fe98a613SJohn Crispin struct wmi_twt_add_dialog_params params = { 0 }; 1459*9e2747c3SManikanta Pubbisetty struct wmi_twt_enable_params twt_params = {0}; 1460*9e2747c3SManikanta Pubbisetty struct ath11k *ar = arvif->ar; 1461fe98a613SJohn Crispin u8 buf[128] = {0}; 1462fe98a613SJohn Crispin int ret; 1463fe98a613SJohn Crispin 1464*9e2747c3SManikanta Pubbisetty if (ar->twt_enabled == 0) { 1465*9e2747c3SManikanta Pubbisetty ath11k_err(ar->ab, "twt support is not enabled\n"); 1466fe98a613SJohn Crispin return -EOPNOTSUPP; 1467fe98a613SJohn Crispin } 1468fe98a613SJohn Crispin 1469fe98a613SJohn Crispin ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); 1470fe98a613SJohn Crispin if (ret < 0) 1471fe98a613SJohn Crispin return ret; 1472fe98a613SJohn Crispin 1473fe98a613SJohn Crispin buf[ret] = '\0'; 1474fe98a613SJohn Crispin ret = sscanf(buf, 1475fe98a613SJohn Crispin "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u %u %u %hhu %hhu %hhu %hhu %hhu", 1476fe98a613SJohn Crispin ¶ms.peer_macaddr[0], 1477fe98a613SJohn Crispin ¶ms.peer_macaddr[1], 1478fe98a613SJohn Crispin ¶ms.peer_macaddr[2], 1479fe98a613SJohn Crispin ¶ms.peer_macaddr[3], 1480fe98a613SJohn Crispin ¶ms.peer_macaddr[4], 1481fe98a613SJohn Crispin ¶ms.peer_macaddr[5], 1482fe98a613SJohn Crispin ¶ms.dialog_id, 1483fe98a613SJohn Crispin ¶ms.wake_intvl_us, 1484fe98a613SJohn Crispin ¶ms.wake_intvl_mantis, 1485fe98a613SJohn Crispin ¶ms.wake_dura_us, 1486fe98a613SJohn Crispin ¶ms.sp_offset_us, 1487fe98a613SJohn Crispin ¶ms.twt_cmd, 1488fe98a613SJohn Crispin ¶ms.flag_bcast, 1489fe98a613SJohn Crispin ¶ms.flag_trigger, 1490fe98a613SJohn Crispin ¶ms.flag_flow_type, 1491fe98a613SJohn Crispin ¶ms.flag_protection); 1492fe98a613SJohn Crispin if (ret != 16) 1493fe98a613SJohn Crispin return -EINVAL; 1494fe98a613SJohn Crispin 1495*9e2747c3SManikanta Pubbisetty /* In the case of station vif, TWT is entirely handled by 1496*9e2747c3SManikanta Pubbisetty * the firmware based on the input parameters in the TWT enable 1497*9e2747c3SManikanta Pubbisetty * WMI command that is sent to the target during assoc. 1498*9e2747c3SManikanta Pubbisetty * For manually testing the TWT feature, we need to first disable 1499*9e2747c3SManikanta Pubbisetty * TWT and send enable command again with TWT input parameter 1500*9e2747c3SManikanta Pubbisetty * sta_cong_timer_ms set to 0. 1501*9e2747c3SManikanta Pubbisetty */ 1502*9e2747c3SManikanta Pubbisetty if (arvif->vif->type == NL80211_IFTYPE_STATION) { 1503*9e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id); 1504*9e2747c3SManikanta Pubbisetty 1505*9e2747c3SManikanta Pubbisetty ath11k_wmi_fill_default_twt_params(&twt_params); 1506*9e2747c3SManikanta Pubbisetty twt_params.sta_cong_timer_ms = 0; 1507*9e2747c3SManikanta Pubbisetty 1508*9e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params); 1509*9e2747c3SManikanta Pubbisetty } 1510*9e2747c3SManikanta Pubbisetty 1511fe98a613SJohn Crispin params.vdev_id = arvif->vdev_id; 1512fe98a613SJohn Crispin 1513fe98a613SJohn Crispin ret = ath11k_wmi_send_twt_add_dialog_cmd(arvif->ar, ¶ms); 1514fe98a613SJohn Crispin if (ret) 1515*9e2747c3SManikanta Pubbisetty goto err_twt_add_dialog; 1516fe98a613SJohn Crispin 1517fe98a613SJohn Crispin return count; 1518*9e2747c3SManikanta Pubbisetty 1519*9e2747c3SManikanta Pubbisetty err_twt_add_dialog: 1520*9e2747c3SManikanta Pubbisetty if (arvif->vif->type == NL80211_IFTYPE_STATION) { 1521*9e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id); 1522*9e2747c3SManikanta Pubbisetty ath11k_wmi_fill_default_twt_params(&twt_params); 1523*9e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params); 1524*9e2747c3SManikanta Pubbisetty } 1525*9e2747c3SManikanta Pubbisetty 1526*9e2747c3SManikanta Pubbisetty return ret; 1527fe98a613SJohn Crispin } 1528fe98a613SJohn Crispin 1529fe98a613SJohn Crispin static ssize_t ath11k_write_twt_del_dialog(struct file *file, 1530fe98a613SJohn Crispin const char __user *ubuf, 1531fe98a613SJohn Crispin size_t count, loff_t *ppos) 1532fe98a613SJohn Crispin { 1533fe98a613SJohn Crispin struct ath11k_vif *arvif = file->private_data; 1534fe98a613SJohn Crispin struct wmi_twt_del_dialog_params params = { 0 }; 1535*9e2747c3SManikanta Pubbisetty struct wmi_twt_enable_params twt_params = {0}; 1536*9e2747c3SManikanta Pubbisetty struct ath11k *ar = arvif->ar; 1537fe98a613SJohn Crispin u8 buf[64] = {0}; 1538fe98a613SJohn Crispin int ret; 1539fe98a613SJohn Crispin 1540*9e2747c3SManikanta Pubbisetty if (ar->twt_enabled == 0) { 1541*9e2747c3SManikanta Pubbisetty ath11k_err(ar->ab, "twt support is not enabled\n"); 1542fe98a613SJohn Crispin return -EOPNOTSUPP; 1543fe98a613SJohn Crispin } 1544fe98a613SJohn Crispin 1545fe98a613SJohn Crispin ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); 1546fe98a613SJohn Crispin if (ret < 0) 1547fe98a613SJohn Crispin return ret; 1548fe98a613SJohn Crispin 1549fe98a613SJohn Crispin buf[ret] = '\0'; 1550fe98a613SJohn Crispin ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u", 1551fe98a613SJohn Crispin ¶ms.peer_macaddr[0], 1552fe98a613SJohn Crispin ¶ms.peer_macaddr[1], 1553fe98a613SJohn Crispin ¶ms.peer_macaddr[2], 1554fe98a613SJohn Crispin ¶ms.peer_macaddr[3], 1555fe98a613SJohn Crispin ¶ms.peer_macaddr[4], 1556fe98a613SJohn Crispin ¶ms.peer_macaddr[5], 1557fe98a613SJohn Crispin ¶ms.dialog_id); 1558fe98a613SJohn Crispin if (ret != 7) 1559fe98a613SJohn Crispin return -EINVAL; 1560fe98a613SJohn Crispin 1561fe98a613SJohn Crispin params.vdev_id = arvif->vdev_id; 1562fe98a613SJohn Crispin 1563fe98a613SJohn Crispin ret = ath11k_wmi_send_twt_del_dialog_cmd(arvif->ar, ¶ms); 1564fe98a613SJohn Crispin if (ret) 1565fe98a613SJohn Crispin return ret; 1566fe98a613SJohn Crispin 1567*9e2747c3SManikanta Pubbisetty if (arvif->vif->type == NL80211_IFTYPE_STATION) { 1568*9e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id); 1569*9e2747c3SManikanta Pubbisetty ath11k_wmi_fill_default_twt_params(&twt_params); 1570*9e2747c3SManikanta Pubbisetty ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params); 1571*9e2747c3SManikanta Pubbisetty } 1572*9e2747c3SManikanta Pubbisetty 1573fe98a613SJohn Crispin return count; 1574fe98a613SJohn Crispin } 1575fe98a613SJohn Crispin 1576fe98a613SJohn Crispin static ssize_t ath11k_write_twt_pause_dialog(struct file *file, 1577fe98a613SJohn Crispin const char __user *ubuf, 1578fe98a613SJohn Crispin size_t count, loff_t *ppos) 1579fe98a613SJohn Crispin { 1580fe98a613SJohn Crispin struct ath11k_vif *arvif = file->private_data; 1581fe98a613SJohn Crispin struct wmi_twt_pause_dialog_params params = { 0 }; 1582fe98a613SJohn Crispin u8 buf[64] = {0}; 1583fe98a613SJohn Crispin int ret; 1584fe98a613SJohn Crispin 1585fe98a613SJohn Crispin if (arvif->ar->twt_enabled == 0) { 1586fe98a613SJohn Crispin ath11k_err(arvif->ar->ab, "twt support is not enabled\n"); 1587fe98a613SJohn Crispin return -EOPNOTSUPP; 1588fe98a613SJohn Crispin } 1589fe98a613SJohn Crispin 1590fe98a613SJohn Crispin ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); 1591fe98a613SJohn Crispin if (ret < 0) 1592fe98a613SJohn Crispin return ret; 1593fe98a613SJohn Crispin 1594fe98a613SJohn Crispin buf[ret] = '\0'; 1595fe98a613SJohn Crispin ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u", 1596fe98a613SJohn Crispin ¶ms.peer_macaddr[0], 1597fe98a613SJohn Crispin ¶ms.peer_macaddr[1], 1598fe98a613SJohn Crispin ¶ms.peer_macaddr[2], 1599fe98a613SJohn Crispin ¶ms.peer_macaddr[3], 1600fe98a613SJohn Crispin ¶ms.peer_macaddr[4], 1601fe98a613SJohn Crispin ¶ms.peer_macaddr[5], 1602fe98a613SJohn Crispin ¶ms.dialog_id); 1603fe98a613SJohn Crispin if (ret != 7) 1604fe98a613SJohn Crispin return -EINVAL; 1605fe98a613SJohn Crispin 1606fe98a613SJohn Crispin params.vdev_id = arvif->vdev_id; 1607fe98a613SJohn Crispin 1608fe98a613SJohn Crispin ret = ath11k_wmi_send_twt_pause_dialog_cmd(arvif->ar, ¶ms); 1609fe98a613SJohn Crispin if (ret) 1610fe98a613SJohn Crispin return ret; 1611fe98a613SJohn Crispin 1612fe98a613SJohn Crispin return count; 1613fe98a613SJohn Crispin } 1614fe98a613SJohn Crispin 1615fe98a613SJohn Crispin static ssize_t ath11k_write_twt_resume_dialog(struct file *file, 1616fe98a613SJohn Crispin const char __user *ubuf, 1617fe98a613SJohn Crispin size_t count, loff_t *ppos) 1618fe98a613SJohn Crispin { 1619fe98a613SJohn Crispin struct ath11k_vif *arvif = file->private_data; 1620fe98a613SJohn Crispin struct wmi_twt_resume_dialog_params params = { 0 }; 1621fe98a613SJohn Crispin u8 buf[64] = {0}; 1622fe98a613SJohn Crispin int ret; 1623fe98a613SJohn Crispin 1624fe98a613SJohn Crispin if (arvif->ar->twt_enabled == 0) { 1625fe98a613SJohn Crispin ath11k_err(arvif->ar->ab, "twt support is not enabled\n"); 1626fe98a613SJohn Crispin return -EOPNOTSUPP; 1627fe98a613SJohn Crispin } 1628fe98a613SJohn Crispin 1629fe98a613SJohn Crispin ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); 1630fe98a613SJohn Crispin if (ret < 0) 1631fe98a613SJohn Crispin return ret; 1632fe98a613SJohn Crispin 1633fe98a613SJohn Crispin buf[ret] = '\0'; 1634fe98a613SJohn Crispin ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u", 1635fe98a613SJohn Crispin ¶ms.peer_macaddr[0], 1636fe98a613SJohn Crispin ¶ms.peer_macaddr[1], 1637fe98a613SJohn Crispin ¶ms.peer_macaddr[2], 1638fe98a613SJohn Crispin ¶ms.peer_macaddr[3], 1639fe98a613SJohn Crispin ¶ms.peer_macaddr[4], 1640fe98a613SJohn Crispin ¶ms.peer_macaddr[5], 1641fe98a613SJohn Crispin ¶ms.dialog_id, 1642fe98a613SJohn Crispin ¶ms.sp_offset_us, 1643fe98a613SJohn Crispin ¶ms.next_twt_size); 1644fe98a613SJohn Crispin if (ret != 9) 1645fe98a613SJohn Crispin return -EINVAL; 1646fe98a613SJohn Crispin 1647fe98a613SJohn Crispin params.vdev_id = arvif->vdev_id; 1648fe98a613SJohn Crispin 1649fe98a613SJohn Crispin ret = ath11k_wmi_send_twt_resume_dialog_cmd(arvif->ar, ¶ms); 1650fe98a613SJohn Crispin if (ret) 1651fe98a613SJohn Crispin return ret; 1652fe98a613SJohn Crispin 1653fe98a613SJohn Crispin return count; 1654fe98a613SJohn Crispin } 1655fe98a613SJohn Crispin 1656fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_add_dialog = { 1657fe98a613SJohn Crispin .write = ath11k_write_twt_add_dialog, 1658fe98a613SJohn Crispin .open = simple_open 1659fe98a613SJohn Crispin }; 1660fe98a613SJohn Crispin 1661fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_del_dialog = { 1662fe98a613SJohn Crispin .write = ath11k_write_twt_del_dialog, 1663fe98a613SJohn Crispin .open = simple_open 1664fe98a613SJohn Crispin }; 1665fe98a613SJohn Crispin 1666fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_pause_dialog = { 1667fe98a613SJohn Crispin .write = ath11k_write_twt_pause_dialog, 1668fe98a613SJohn Crispin .open = simple_open 1669fe98a613SJohn Crispin }; 1670fe98a613SJohn Crispin 1671fe98a613SJohn Crispin static const struct file_operations ath11k_fops_twt_resume_dialog = { 1672fe98a613SJohn Crispin .write = ath11k_write_twt_resume_dialog, 1673fe98a613SJohn Crispin .open = simple_open 1674fe98a613SJohn Crispin }; 1675fe98a613SJohn Crispin 1676fe98a613SJohn Crispin int ath11k_debugfs_add_interface(struct ath11k_vif *arvif) 1677fe98a613SJohn Crispin { 1678*9e2747c3SManikanta Pubbisetty struct ath11k_base *ab = arvif->ar->ab; 1679*9e2747c3SManikanta Pubbisetty 1680*9e2747c3SManikanta Pubbisetty if (arvif->vif->type != NL80211_IFTYPE_AP && 1681*9e2747c3SManikanta Pubbisetty !(arvif->vif->type == NL80211_IFTYPE_STATION && 1682*9e2747c3SManikanta Pubbisetty test_bit(WMI_TLV_SERVICE_STA_TWT, ab->wmi_ab.svc_map))) 1683*9e2747c3SManikanta Pubbisetty return 0; 1684*9e2747c3SManikanta Pubbisetty 1685*9e2747c3SManikanta Pubbisetty if (!arvif->debugfs_twt) { 1686fe98a613SJohn Crispin arvif->debugfs_twt = debugfs_create_dir("twt", 1687fe98a613SJohn Crispin arvif->vif->debugfs_dir); 1688fe98a613SJohn Crispin if (!arvif->debugfs_twt || IS_ERR(arvif->debugfs_twt)) { 1689*9e2747c3SManikanta Pubbisetty ath11k_warn(ab, "failed to create directory %p\n", 1690fe98a613SJohn Crispin arvif->debugfs_twt); 1691fe98a613SJohn Crispin arvif->debugfs_twt = NULL; 1692fe98a613SJohn Crispin return -1; 1693fe98a613SJohn Crispin } 1694fe98a613SJohn Crispin 1695fe98a613SJohn Crispin debugfs_create_file("add_dialog", 0200, arvif->debugfs_twt, 1696fe98a613SJohn Crispin arvif, &ath11k_fops_twt_add_dialog); 1697fe98a613SJohn Crispin 1698fe98a613SJohn Crispin debugfs_create_file("del_dialog", 0200, arvif->debugfs_twt, 1699fe98a613SJohn Crispin arvif, &ath11k_fops_twt_del_dialog); 1700fe98a613SJohn Crispin 1701fe98a613SJohn Crispin debugfs_create_file("pause_dialog", 0200, arvif->debugfs_twt, 1702fe98a613SJohn Crispin arvif, &ath11k_fops_twt_pause_dialog); 1703fe98a613SJohn Crispin 1704fe98a613SJohn Crispin debugfs_create_file("resume_dialog", 0200, arvif->debugfs_twt, 1705fe98a613SJohn Crispin arvif, &ath11k_fops_twt_resume_dialog); 1706fe98a613SJohn Crispin } 1707fe98a613SJohn Crispin return 0; 1708fe98a613SJohn Crispin } 1709fe98a613SJohn Crispin 1710fe98a613SJohn Crispin void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif) 1711fe98a613SJohn Crispin { 1712fe98a613SJohn Crispin debugfs_remove_recursive(arvif->debugfs_twt); 1713fe98a613SJohn Crispin arvif->debugfs_twt = NULL; 1714fe98a613SJohn Crispin } 1715