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