18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. 48c2ecf20Sopenharmony_ci * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 98c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 128c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 138c2ecf20Sopenharmony_ci#include "wil6210.h" 148c2ecf20Sopenharmony_ci#include "wmi.h" 158c2ecf20Sopenharmony_ci#include "txrx.h" 168c2ecf20Sopenharmony_ci#include "pmc.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Nasty hack. Better have per device instances */ 198c2ecf20Sopenharmony_cistatic u32 mem_addr; 208c2ecf20Sopenharmony_cistatic u32 dbg_txdesc_index; 218c2ecf20Sopenharmony_cistatic u32 dbg_ring_index; /* 24+ for Rx, 0..23 for Tx */ 228c2ecf20Sopenharmony_cistatic u32 dbg_status_msg_index; 238c2ecf20Sopenharmony_ci/* 0..wil->num_rx_status_rings-1 for Rx, wil->tx_sring_idx for Tx */ 248c2ecf20Sopenharmony_cistatic u32 dbg_sring_index; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cienum dbg_off_type { 278c2ecf20Sopenharmony_ci doff_u32 = 0, 288c2ecf20Sopenharmony_ci doff_x32 = 1, 298c2ecf20Sopenharmony_ci doff_ulong = 2, 308c2ecf20Sopenharmony_ci doff_io32 = 3, 318c2ecf20Sopenharmony_ci doff_u8 = 4 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* offset to "wil" */ 358c2ecf20Sopenharmony_cistruct dbg_off { 368c2ecf20Sopenharmony_ci const char *name; 378c2ecf20Sopenharmony_ci umode_t mode; 388c2ecf20Sopenharmony_ci ulong off; 398c2ecf20Sopenharmony_ci enum dbg_off_type type; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void wil_print_desc_edma(struct seq_file *s, struct wil6210_priv *wil, 438c2ecf20Sopenharmony_ci struct wil_ring *ring, 448c2ecf20Sopenharmony_ci char _s, char _h, int idx) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci u8 num_of_descs; 478c2ecf20Sopenharmony_ci bool has_skb = false; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (ring->is_rx) { 508c2ecf20Sopenharmony_ci struct wil_rx_enhanced_desc *rx_d = 518c2ecf20Sopenharmony_ci (struct wil_rx_enhanced_desc *) 528c2ecf20Sopenharmony_ci &ring->va[idx].rx.enhanced; 538c2ecf20Sopenharmony_ci u16 buff_id = le16_to_cpu(rx_d->mac.buff_id); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (wil->rx_buff_mgmt.buff_arr && 568c2ecf20Sopenharmony_ci wil_val_in_range(buff_id, 0, wil->rx_buff_mgmt.size)) 578c2ecf20Sopenharmony_ci has_skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; 588c2ecf20Sopenharmony_ci seq_printf(s, "%c", (has_skb) ? _h : _s); 598c2ecf20Sopenharmony_ci } else { 608c2ecf20Sopenharmony_ci struct wil_tx_enhanced_desc *d = 618c2ecf20Sopenharmony_ci (struct wil_tx_enhanced_desc *) 628c2ecf20Sopenharmony_ci &ring->va[idx].tx.enhanced; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci num_of_descs = (u8)d->mac.d[2]; 658c2ecf20Sopenharmony_ci has_skb = ring->ctx && ring->ctx[idx].skb; 668c2ecf20Sopenharmony_ci if (num_of_descs >= 1) 678c2ecf20Sopenharmony_ci seq_printf(s, "%c", has_skb ? _h : _s); 688c2ecf20Sopenharmony_ci else 698c2ecf20Sopenharmony_ci /* num_of_descs == 0, it's a frag in a list of descs */ 708c2ecf20Sopenharmony_ci seq_printf(s, "%c", has_skb ? 'h' : _s); 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil, 758c2ecf20Sopenharmony_ci const char *name, struct wil_ring *ring, 768c2ecf20Sopenharmony_ci char _s, char _h) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci void __iomem *x; 798c2ecf20Sopenharmony_ci u32 v; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci seq_printf(s, "RING %s = {\n", name); 828c2ecf20Sopenharmony_ci seq_printf(s, " pa = %pad\n", &ring->pa); 838c2ecf20Sopenharmony_ci seq_printf(s, " va = 0x%p\n", ring->va); 848c2ecf20Sopenharmony_ci seq_printf(s, " size = %d\n", ring->size); 858c2ecf20Sopenharmony_ci if (wil->use_enhanced_dma_hw && ring->is_rx) 868c2ecf20Sopenharmony_ci seq_printf(s, " swtail = %u\n", *ring->edma_rx_swtail.va); 878c2ecf20Sopenharmony_ci else 888c2ecf20Sopenharmony_ci seq_printf(s, " swtail = %d\n", ring->swtail); 898c2ecf20Sopenharmony_ci seq_printf(s, " swhead = %d\n", ring->swhead); 908c2ecf20Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 918c2ecf20Sopenharmony_ci int ring_id = ring->is_rx ? 928c2ecf20Sopenharmony_ci WIL_RX_DESC_RING_ID : ring - wil->ring_tx; 938c2ecf20Sopenharmony_ci /* SUBQ_CONS is a table of 32 entries, one for each Q pair. 948c2ecf20Sopenharmony_ci * lower 16bits are for even ring_id and upper 16bits are for 958c2ecf20Sopenharmony_ci * odd ring_id 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci x = wmi_addr(wil, RGF_DMA_SCM_SUBQ_CONS + 4 * (ring_id / 2)); 988c2ecf20Sopenharmony_ci v = readl_relaxed(x); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci v = (ring_id % 2 ? (v >> 16) : (v & 0xffff)); 1018c2ecf20Sopenharmony_ci seq_printf(s, " hwhead = %u\n", v); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci seq_printf(s, " hwtail = [0x%08x] -> ", ring->hwtail); 1048c2ecf20Sopenharmony_ci x = wmi_addr(wil, ring->hwtail); 1058c2ecf20Sopenharmony_ci if (x) { 1068c2ecf20Sopenharmony_ci v = readl(x); 1078c2ecf20Sopenharmony_ci seq_printf(s, "0x%08x = %d\n", v, v); 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci seq_puts(s, "???\n"); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (ring->va && (ring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) { 1138c2ecf20Sopenharmony_ci uint i; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci for (i = 0; i < ring->size; i++) { 1168c2ecf20Sopenharmony_ci if ((i % 128) == 0 && i != 0) 1178c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 1188c2ecf20Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 1198c2ecf20Sopenharmony_ci wil_print_desc_edma(s, wil, ring, _s, _h, i); 1208c2ecf20Sopenharmony_ci } else { 1218c2ecf20Sopenharmony_ci volatile struct vring_tx_desc *d = 1228c2ecf20Sopenharmony_ci &ring->va[i].tx.legacy; 1238c2ecf20Sopenharmony_ci seq_printf(s, "%c", (d->dma.status & BIT(0)) ? 1248c2ecf20Sopenharmony_ci _s : (ring->ctx[i].skb ? _h : 'h')); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci seq_puts(s, "}\n"); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int ring_show(struct seq_file *s, void *data) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci uint i; 1358c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci wil_print_ring(s, wil, "rx", &wil->ring_rx, 'S', '_'); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wil->ring_tx); i++) { 1408c2ecf20Sopenharmony_ci struct wil_ring *ring = &wil->ring_tx[i]; 1418c2ecf20Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[i]; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (ring->va) { 1448c2ecf20Sopenharmony_ci int cid = wil->ring2cid_tid[i][0]; 1458c2ecf20Sopenharmony_ci int tid = wil->ring2cid_tid[i][1]; 1468c2ecf20Sopenharmony_ci u32 swhead = ring->swhead; 1478c2ecf20Sopenharmony_ci u32 swtail = ring->swtail; 1488c2ecf20Sopenharmony_ci int used = (ring->size + swhead - swtail) 1498c2ecf20Sopenharmony_ci % ring->size; 1508c2ecf20Sopenharmony_ci int avail = ring->size - used - 1; 1518c2ecf20Sopenharmony_ci char name[10]; 1528c2ecf20Sopenharmony_ci char sidle[10]; 1538c2ecf20Sopenharmony_ci /* performance monitoring */ 1548c2ecf20Sopenharmony_ci cycles_t now = get_cycles(); 1558c2ecf20Sopenharmony_ci uint64_t idle = txdata->idle * 100; 1568c2ecf20Sopenharmony_ci uint64_t total = now - txdata->begin; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (total != 0) { 1598c2ecf20Sopenharmony_ci do_div(idle, total); 1608c2ecf20Sopenharmony_ci snprintf(sidle, sizeof(sidle), "%3d%%", 1618c2ecf20Sopenharmony_ci (int)idle); 1628c2ecf20Sopenharmony_ci } else { 1638c2ecf20Sopenharmony_ci snprintf(sidle, sizeof(sidle), "N/A"); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci txdata->begin = now; 1668c2ecf20Sopenharmony_ci txdata->idle = 0ULL; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "tx_%2d", i); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (cid < wil->max_assoc_sta) 1718c2ecf20Sopenharmony_ci seq_printf(s, 1728c2ecf20Sopenharmony_ci "\n%pM CID %d TID %d 1x%s BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n", 1738c2ecf20Sopenharmony_ci wil->sta[cid].addr, cid, tid, 1748c2ecf20Sopenharmony_ci txdata->dot1x_open ? "+" : "-", 1758c2ecf20Sopenharmony_ci txdata->agg_wsize, 1768c2ecf20Sopenharmony_ci txdata->agg_timeout, 1778c2ecf20Sopenharmony_ci txdata->agg_amsdu ? "+" : "-", 1788c2ecf20Sopenharmony_ci used, avail, sidle); 1798c2ecf20Sopenharmony_ci else 1808c2ecf20Sopenharmony_ci seq_printf(s, 1818c2ecf20Sopenharmony_ci "\nBroadcast 1x%s [%3d|%3d] idle %s\n", 1828c2ecf20Sopenharmony_ci txdata->dot1x_open ? "+" : "-", 1838c2ecf20Sopenharmony_ci used, avail, sidle); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci wil_print_ring(s, wil, name, ring, '_', 'H'); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ring); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil, 1948c2ecf20Sopenharmony_ci struct wil_status_ring *sring) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci void __iomem *x; 1978c2ecf20Sopenharmony_ci int sring_idx = sring - wil->srings; 1988c2ecf20Sopenharmony_ci u32 v; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci seq_printf(s, "Status Ring %s [ %d ] = {\n", 2018c2ecf20Sopenharmony_ci sring->is_rx ? "RX" : "TX", sring_idx); 2028c2ecf20Sopenharmony_ci seq_printf(s, " pa = %pad\n", &sring->pa); 2038c2ecf20Sopenharmony_ci seq_printf(s, " va = 0x%pK\n", sring->va); 2048c2ecf20Sopenharmony_ci seq_printf(s, " size = %d\n", sring->size); 2058c2ecf20Sopenharmony_ci seq_printf(s, " elem_size = %zu\n", sring->elem_size); 2068c2ecf20Sopenharmony_ci seq_printf(s, " swhead = %d\n", sring->swhead); 2078c2ecf20Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 2088c2ecf20Sopenharmony_ci /* COMPQ_PROD is a table of 32 entries, one for each Q pair. 2098c2ecf20Sopenharmony_ci * lower 16bits are for even ring_id and upper 16bits are for 2108c2ecf20Sopenharmony_ci * odd ring_id 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci x = wmi_addr(wil, RGF_DMA_SCM_COMPQ_PROD + 4 * (sring_idx / 2)); 2138c2ecf20Sopenharmony_ci v = readl_relaxed(x); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci v = (sring_idx % 2 ? (v >> 16) : (v & 0xffff)); 2168c2ecf20Sopenharmony_ci seq_printf(s, " hwhead = %u\n", v); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci seq_printf(s, " hwtail = [0x%08x] -> ", sring->hwtail); 2198c2ecf20Sopenharmony_ci x = wmi_addr(wil, sring->hwtail); 2208c2ecf20Sopenharmony_ci if (x) { 2218c2ecf20Sopenharmony_ci v = readl_relaxed(x); 2228c2ecf20Sopenharmony_ci seq_printf(s, "0x%08x = %d\n", v, v); 2238c2ecf20Sopenharmony_ci } else { 2248c2ecf20Sopenharmony_ci seq_puts(s, "???\n"); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci seq_printf(s, " desc_rdy_pol = %d\n", sring->desc_rdy_pol); 2278c2ecf20Sopenharmony_ci seq_printf(s, " invalid_buff_id_cnt = %d\n", 2288c2ecf20Sopenharmony_ci sring->invalid_buff_id_cnt); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (sring->va && (sring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) { 2318c2ecf20Sopenharmony_ci uint i; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci for (i = 0; i < sring->size; i++) { 2348c2ecf20Sopenharmony_ci u32 *sdword_0 = 2358c2ecf20Sopenharmony_ci (u32 *)(sring->va + (sring->elem_size * i)); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if ((i % 128) == 0 && i != 0) 2388c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 2398c2ecf20Sopenharmony_ci if (i == sring->swhead) 2408c2ecf20Sopenharmony_ci seq_printf(s, "%c", (*sdword_0 & BIT(31)) ? 2418c2ecf20Sopenharmony_ci 'X' : 'x'); 2428c2ecf20Sopenharmony_ci else 2438c2ecf20Sopenharmony_ci seq_printf(s, "%c", (*sdword_0 & BIT(31)) ? 2448c2ecf20Sopenharmony_ci '1' : '0'); 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci seq_puts(s, "}\n"); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int srings_show(struct seq_file *s, void *data) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 2548c2ecf20Sopenharmony_ci int i = 0; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci for (i = 0; i < WIL6210_MAX_STATUS_RINGS; i++) 2578c2ecf20Sopenharmony_ci if (wil->srings[i].va) 2588c2ecf20Sopenharmony_ci wil_print_sring(s, wil, &wil->srings[i]); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(srings); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic void wil_seq_hexdump(struct seq_file *s, void *p, int len, 2658c2ecf20Sopenharmony_ci const char *prefix) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci seq_hex_dump(s, prefix, DUMP_PREFIX_NONE, 16, 1, p, len, false); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic void wil_print_mbox_ring(struct seq_file *s, const char *prefix, 2718c2ecf20Sopenharmony_ci void __iomem *off) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 2748c2ecf20Sopenharmony_ci struct wil6210_mbox_ring r; 2758c2ecf20Sopenharmony_ci int rsize; 2768c2ecf20Sopenharmony_ci uint i; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci wil_halp_vote(wil); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (wil_mem_access_lock(wil)) { 2818c2ecf20Sopenharmony_ci wil_halp_unvote(wil); 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci wil_memcpy_fromio_32(&r, off, sizeof(r)); 2868c2ecf20Sopenharmony_ci wil_mbox_ring_le2cpus(&r); 2878c2ecf20Sopenharmony_ci /* 2888c2ecf20Sopenharmony_ci * we just read memory block from NIC. This memory may be 2898c2ecf20Sopenharmony_ci * garbage. Check validity before using it. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci rsize = r.size / sizeof(struct wil6210_mbox_ring_desc); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci seq_printf(s, "ring %s = {\n", prefix); 2948c2ecf20Sopenharmony_ci seq_printf(s, " base = 0x%08x\n", r.base); 2958c2ecf20Sopenharmony_ci seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize); 2968c2ecf20Sopenharmony_ci seq_printf(s, " tail = 0x%08x\n", r.tail); 2978c2ecf20Sopenharmony_ci seq_printf(s, " head = 0x%08x\n", r.head); 2988c2ecf20Sopenharmony_ci seq_printf(s, " entry size = %d\n", r.entry_size); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (r.size % sizeof(struct wil6210_mbox_ring_desc)) { 3018c2ecf20Sopenharmony_ci seq_printf(s, " ??? size is not multiple of %zd, garbage?\n", 3028c2ecf20Sopenharmony_ci sizeof(struct wil6210_mbox_ring_desc)); 3038c2ecf20Sopenharmony_ci goto out; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (!wmi_addr(wil, r.base) || 3078c2ecf20Sopenharmony_ci !wmi_addr(wil, r.tail) || 3088c2ecf20Sopenharmony_ci !wmi_addr(wil, r.head)) { 3098c2ecf20Sopenharmony_ci seq_puts(s, " ??? pointers are garbage?\n"); 3108c2ecf20Sopenharmony_ci goto out; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci for (i = 0; i < rsize; i++) { 3148c2ecf20Sopenharmony_ci struct wil6210_mbox_ring_desc d; 3158c2ecf20Sopenharmony_ci struct wil6210_mbox_hdr hdr; 3168c2ecf20Sopenharmony_ci size_t delta = i * sizeof(d); 3178c2ecf20Sopenharmony_ci void __iomem *x = wil->csr + HOSTADDR(r.base) + delta; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci wil_memcpy_fromio_32(&d, x, sizeof(d)); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci seq_printf(s, " [%2x] %s %s%s 0x%08x", i, 3228c2ecf20Sopenharmony_ci d.sync ? "F" : "E", 3238c2ecf20Sopenharmony_ci (r.tail - r.base == delta) ? "t" : " ", 3248c2ecf20Sopenharmony_ci (r.head - r.base == delta) ? "h" : " ", 3258c2ecf20Sopenharmony_ci le32_to_cpu(d.addr)); 3268c2ecf20Sopenharmony_ci if (0 == wmi_read_hdr(wil, d.addr, &hdr)) { 3278c2ecf20Sopenharmony_ci u16 len = le16_to_cpu(hdr.len); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci seq_printf(s, " -> %04x %04x %04x %02x\n", 3308c2ecf20Sopenharmony_ci le16_to_cpu(hdr.seq), len, 3318c2ecf20Sopenharmony_ci le16_to_cpu(hdr.type), hdr.flags); 3328c2ecf20Sopenharmony_ci if (len <= MAX_MBOXITEM_SIZE) { 3338c2ecf20Sopenharmony_ci unsigned char databuf[MAX_MBOXITEM_SIZE]; 3348c2ecf20Sopenharmony_ci void __iomem *src = wmi_buffer(wil, d.addr) + 3358c2ecf20Sopenharmony_ci sizeof(struct wil6210_mbox_hdr); 3368c2ecf20Sopenharmony_ci /* 3378c2ecf20Sopenharmony_ci * No need to check @src for validity - 3388c2ecf20Sopenharmony_ci * we already validated @d.addr while 3398c2ecf20Sopenharmony_ci * reading header 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci wil_memcpy_fromio_32(databuf, src, len); 3428c2ecf20Sopenharmony_ci wil_seq_hexdump(s, databuf, len, " : "); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci } else { 3458c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci out: 3498c2ecf20Sopenharmony_ci seq_puts(s, "}\n"); 3508c2ecf20Sopenharmony_ci wil_mem_access_unlock(wil); 3518c2ecf20Sopenharmony_ci wil_halp_unvote(wil); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int mbox_show(struct seq_file *s, void *data) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 3578c2ecf20Sopenharmony_ci int ret; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci ret = wil_pm_runtime_get(wil); 3608c2ecf20Sopenharmony_ci if (ret < 0) 3618c2ecf20Sopenharmony_ci return ret; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci wil_print_mbox_ring(s, "tx", wil->csr + HOST_MBOX + 3648c2ecf20Sopenharmony_ci offsetof(struct wil6210_mbox_ctl, tx)); 3658c2ecf20Sopenharmony_ci wil_print_mbox_ring(s, "rx", wil->csr + HOST_MBOX + 3668c2ecf20Sopenharmony_ci offsetof(struct wil6210_mbox_ctl, rx)); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci wil_pm_runtime_put(wil); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mbox); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int wil_debugfs_iomem_x32_set(void *data, u64 val) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct wil_debugfs_iomem_data *d = (struct 3778c2ecf20Sopenharmony_ci wil_debugfs_iomem_data *)data; 3788c2ecf20Sopenharmony_ci struct wil6210_priv *wil = d->wil; 3798c2ecf20Sopenharmony_ci int ret; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ret = wil_pm_runtime_get(wil); 3828c2ecf20Sopenharmony_ci if (ret < 0) 3838c2ecf20Sopenharmony_ci return ret; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci writel_relaxed(val, (void __iomem *)d->offset); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci wmb(); /* make sure write propagated to HW */ 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci wil_pm_runtime_put(wil); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int wil_debugfs_iomem_x32_get(void *data, u64 *val) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct wil_debugfs_iomem_data *d = (struct 3978c2ecf20Sopenharmony_ci wil_debugfs_iomem_data *)data; 3988c2ecf20Sopenharmony_ci struct wil6210_priv *wil = d->wil; 3998c2ecf20Sopenharmony_ci int ret; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci ret = wil_pm_runtime_get(wil); 4028c2ecf20Sopenharmony_ci if (ret < 0) 4038c2ecf20Sopenharmony_ci return ret; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci *val = readl((void __iomem *)d->offset); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci wil_pm_runtime_put(wil); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get, 4138c2ecf20Sopenharmony_ci wil_debugfs_iomem_x32_set, "0x%08llx\n"); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic void wil_debugfs_create_iomem_x32(const char *name, umode_t mode, 4168c2ecf20Sopenharmony_ci struct dentry *parent, void *value, 4178c2ecf20Sopenharmony_ci struct wil6210_priv *wil) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct wil_debugfs_iomem_data *data = &wil->dbg_data.data_arr[ 4208c2ecf20Sopenharmony_ci wil->dbg_data.iomem_data_count]; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci data->wil = wil; 4238c2ecf20Sopenharmony_ci data->offset = value; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci debugfs_create_file_unsafe(name, mode, parent, data, &fops_iomem_x32); 4268c2ecf20Sopenharmony_ci wil->dbg_data.iomem_data_count++; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic int wil_debugfs_ulong_set(void *data, u64 val) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci *(ulong *)data = val; 4328c2ecf20Sopenharmony_ci return 0; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic int wil_debugfs_ulong_get(void *data, u64 *val) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci *val = *(ulong *)data; 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get, 4428c2ecf20Sopenharmony_ci wil_debugfs_ulong_set, "0x%llx\n"); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci/** 4458c2ecf20Sopenharmony_ci * wil6210_debugfs_init_offset - create set of debugfs files 4468c2ecf20Sopenharmony_ci * @wil: driver's context, used for printing 4478c2ecf20Sopenharmony_ci * @dbg: directory on the debugfs, where files will be created 4488c2ecf20Sopenharmony_ci * @base: base address used in address calculation 4498c2ecf20Sopenharmony_ci * @tbl: table with file descriptions. Should be terminated with empty element. 4508c2ecf20Sopenharmony_ci * 4518c2ecf20Sopenharmony_ci * Creates files accordingly to the @tbl. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_cistatic void wil6210_debugfs_init_offset(struct wil6210_priv *wil, 4548c2ecf20Sopenharmony_ci struct dentry *dbg, void *base, 4558c2ecf20Sopenharmony_ci const struct dbg_off * const tbl) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci int i; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci for (i = 0; tbl[i].name; i++) { 4608c2ecf20Sopenharmony_ci switch (tbl[i].type) { 4618c2ecf20Sopenharmony_ci case doff_u32: 4628c2ecf20Sopenharmony_ci debugfs_create_u32(tbl[i].name, tbl[i].mode, dbg, 4638c2ecf20Sopenharmony_ci base + tbl[i].off); 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci case doff_x32: 4668c2ecf20Sopenharmony_ci debugfs_create_x32(tbl[i].name, tbl[i].mode, dbg, 4678c2ecf20Sopenharmony_ci base + tbl[i].off); 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci case doff_ulong: 4708c2ecf20Sopenharmony_ci debugfs_create_file_unsafe(tbl[i].name, tbl[i].mode, 4718c2ecf20Sopenharmony_ci dbg, base + tbl[i].off, 4728c2ecf20Sopenharmony_ci &wil_fops_ulong); 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci case doff_io32: 4758c2ecf20Sopenharmony_ci wil_debugfs_create_iomem_x32(tbl[i].name, tbl[i].mode, 4768c2ecf20Sopenharmony_ci dbg, base + tbl[i].off, 4778c2ecf20Sopenharmony_ci wil); 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci case doff_u8: 4808c2ecf20Sopenharmony_ci debugfs_create_u8(tbl[i].name, tbl[i].mode, dbg, 4818c2ecf20Sopenharmony_ci base + tbl[i].off); 4828c2ecf20Sopenharmony_ci break; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic const struct dbg_off isr_off[] = { 4888c2ecf20Sopenharmony_ci {"ICC", 0644, offsetof(struct RGF_ICR, ICC), doff_io32}, 4898c2ecf20Sopenharmony_ci {"ICR", 0644, offsetof(struct RGF_ICR, ICR), doff_io32}, 4908c2ecf20Sopenharmony_ci {"ICM", 0644, offsetof(struct RGF_ICR, ICM), doff_io32}, 4918c2ecf20Sopenharmony_ci {"ICS", 0244, offsetof(struct RGF_ICR, ICS), doff_io32}, 4928c2ecf20Sopenharmony_ci {"IMV", 0644, offsetof(struct RGF_ICR, IMV), doff_io32}, 4938c2ecf20Sopenharmony_ci {"IMS", 0244, offsetof(struct RGF_ICR, IMS), doff_io32}, 4948c2ecf20Sopenharmony_ci {"IMC", 0244, offsetof(struct RGF_ICR, IMC), doff_io32}, 4958c2ecf20Sopenharmony_ci {}, 4968c2ecf20Sopenharmony_ci}; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic void wil6210_debugfs_create_ISR(struct wil6210_priv *wil, 4998c2ecf20Sopenharmony_ci const char *name, struct dentry *parent, 5008c2ecf20Sopenharmony_ci u32 off) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct dentry *d = debugfs_create_dir(name, parent); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr + off, 5058c2ecf20Sopenharmony_ci isr_off); 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic const struct dbg_off pseudo_isr_off[] = { 5098c2ecf20Sopenharmony_ci {"CAUSE", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE), doff_io32}, 5108c2ecf20Sopenharmony_ci {"MASK_SW", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW), doff_io32}, 5118c2ecf20Sopenharmony_ci {"MASK_FW", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW), doff_io32}, 5128c2ecf20Sopenharmony_ci {}, 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic void wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, 5168c2ecf20Sopenharmony_ci struct dentry *parent) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr, 5218c2ecf20Sopenharmony_ci pseudo_isr_off); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic const struct dbg_off lgc_itr_cnt_off[] = { 5258c2ecf20Sopenharmony_ci {"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_CNT_TRSH), doff_io32}, 5268c2ecf20Sopenharmony_ci {"DATA", 0644, HOSTADDR(RGF_DMA_ITR_CNT_DATA), doff_io32}, 5278c2ecf20Sopenharmony_ci {"CTL", 0644, HOSTADDR(RGF_DMA_ITR_CNT_CRL), doff_io32}, 5288c2ecf20Sopenharmony_ci {}, 5298c2ecf20Sopenharmony_ci}; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic const struct dbg_off tx_itr_cnt_off[] = { 5328c2ecf20Sopenharmony_ci {"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_TRSH), 5338c2ecf20Sopenharmony_ci doff_io32}, 5348c2ecf20Sopenharmony_ci {"DATA", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_DATA), 5358c2ecf20Sopenharmony_ci doff_io32}, 5368c2ecf20Sopenharmony_ci {"CTL", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_CTL), 5378c2ecf20Sopenharmony_ci doff_io32}, 5388c2ecf20Sopenharmony_ci {"IDL_TRSH", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_TRSH), 5398c2ecf20Sopenharmony_ci doff_io32}, 5408c2ecf20Sopenharmony_ci {"IDL_DATA", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_DATA), 5418c2ecf20Sopenharmony_ci doff_io32}, 5428c2ecf20Sopenharmony_ci {"IDL_CTL", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_CTL), 5438c2ecf20Sopenharmony_ci doff_io32}, 5448c2ecf20Sopenharmony_ci {}, 5458c2ecf20Sopenharmony_ci}; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic const struct dbg_off rx_itr_cnt_off[] = { 5488c2ecf20Sopenharmony_ci {"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_TRSH), 5498c2ecf20Sopenharmony_ci doff_io32}, 5508c2ecf20Sopenharmony_ci {"DATA", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_DATA), 5518c2ecf20Sopenharmony_ci doff_io32}, 5528c2ecf20Sopenharmony_ci {"CTL", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_CTL), 5538c2ecf20Sopenharmony_ci doff_io32}, 5548c2ecf20Sopenharmony_ci {"IDL_TRSH", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_TRSH), 5558c2ecf20Sopenharmony_ci doff_io32}, 5568c2ecf20Sopenharmony_ci {"IDL_DATA", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_DATA), 5578c2ecf20Sopenharmony_ci doff_io32}, 5588c2ecf20Sopenharmony_ci {"IDL_CTL", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_CTL), 5598c2ecf20Sopenharmony_ci doff_io32}, 5608c2ecf20Sopenharmony_ci {}, 5618c2ecf20Sopenharmony_ci}; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, 5648c2ecf20Sopenharmony_ci struct dentry *parent) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct dentry *d, *dtx, *drx; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci d = debugfs_create_dir("ITR_CNT", parent); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci dtx = debugfs_create_dir("TX", d); 5718c2ecf20Sopenharmony_ci drx = debugfs_create_dir("RX", d); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr, 5748c2ecf20Sopenharmony_ci lgc_itr_cnt_off); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci wil6210_debugfs_init_offset(wil, dtx, (void * __force)wil->csr, 5778c2ecf20Sopenharmony_ci tx_itr_cnt_off); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci wil6210_debugfs_init_offset(wil, drx, (void * __force)wil->csr, 5808c2ecf20Sopenharmony_ci rx_itr_cnt_off); 5818c2ecf20Sopenharmony_ci return 0; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic int memread_show(struct seq_file *s, void *data) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 5878c2ecf20Sopenharmony_ci void __iomem *a; 5888c2ecf20Sopenharmony_ci int ret; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci ret = wil_pm_runtime_get(wil); 5918c2ecf20Sopenharmony_ci if (ret < 0) 5928c2ecf20Sopenharmony_ci return ret; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci ret = wil_mem_access_lock(wil); 5958c2ecf20Sopenharmony_ci if (ret) { 5968c2ecf20Sopenharmony_ci wil_pm_runtime_put(wil); 5978c2ecf20Sopenharmony_ci return ret; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci a = wmi_buffer(wil, cpu_to_le32(mem_addr)); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (a) 6038c2ecf20Sopenharmony_ci seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, readl(a)); 6048c2ecf20Sopenharmony_ci else 6058c2ecf20Sopenharmony_ci seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci wil_mem_access_unlock(wil); 6088c2ecf20Sopenharmony_ci wil_pm_runtime_put(wil); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(memread); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, 6158c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci enum { max_count = 4096 }; 6188c2ecf20Sopenharmony_ci struct wil_blob_wrapper *wil_blob = file->private_data; 6198c2ecf20Sopenharmony_ci struct wil6210_priv *wil = wil_blob->wil; 6208c2ecf20Sopenharmony_ci loff_t aligned_pos, pos = *ppos; 6218c2ecf20Sopenharmony_ci size_t available = wil_blob->blob.size; 6228c2ecf20Sopenharmony_ci void *buf; 6238c2ecf20Sopenharmony_ci size_t unaligned_bytes, aligned_count, ret; 6248c2ecf20Sopenharmony_ci int rc; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (pos < 0) 6278c2ecf20Sopenharmony_ci return -EINVAL; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (pos >= available || !count) 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (count > available - pos) 6338c2ecf20Sopenharmony_ci count = available - pos; 6348c2ecf20Sopenharmony_ci if (count > max_count) 6358c2ecf20Sopenharmony_ci count = max_count; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* set pos to 4 bytes aligned */ 6388c2ecf20Sopenharmony_ci unaligned_bytes = pos % 4; 6398c2ecf20Sopenharmony_ci aligned_pos = pos - unaligned_bytes; 6408c2ecf20Sopenharmony_ci aligned_count = count + unaligned_bytes; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci buf = kmalloc(aligned_count, GFP_KERNEL); 6438c2ecf20Sopenharmony_ci if (!buf) 6448c2ecf20Sopenharmony_ci return -ENOMEM; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci rc = wil_pm_runtime_get(wil); 6478c2ecf20Sopenharmony_ci if (rc < 0) { 6488c2ecf20Sopenharmony_ci kfree(buf); 6498c2ecf20Sopenharmony_ci return rc; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci rc = wil_mem_access_lock(wil); 6538c2ecf20Sopenharmony_ci if (rc) { 6548c2ecf20Sopenharmony_ci kfree(buf); 6558c2ecf20Sopenharmony_ci wil_pm_runtime_put(wil); 6568c2ecf20Sopenharmony_ci return rc; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci wil_memcpy_fromio_32(buf, (const void __iomem *) 6608c2ecf20Sopenharmony_ci wil_blob->blob.data + aligned_pos, aligned_count); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci ret = copy_to_user(user_buf, buf + unaligned_bytes, count); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci wil_mem_access_unlock(wil); 6658c2ecf20Sopenharmony_ci wil_pm_runtime_put(wil); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci kfree(buf); 6688c2ecf20Sopenharmony_ci if (ret == count) 6698c2ecf20Sopenharmony_ci return -EFAULT; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci count -= ret; 6728c2ecf20Sopenharmony_ci *ppos = pos + count; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci return count; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic const struct file_operations fops_ioblob = { 6788c2ecf20Sopenharmony_ci .read = wil_read_file_ioblob, 6798c2ecf20Sopenharmony_ci .open = simple_open, 6808c2ecf20Sopenharmony_ci .llseek = default_llseek, 6818c2ecf20Sopenharmony_ci}; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic 6848c2ecf20Sopenharmony_cistruct dentry *wil_debugfs_create_ioblob(const char *name, 6858c2ecf20Sopenharmony_ci umode_t mode, 6868c2ecf20Sopenharmony_ci struct dentry *parent, 6878c2ecf20Sopenharmony_ci struct wil_blob_wrapper *wil_blob) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci return debugfs_create_file(name, mode, parent, wil_blob, &fops_ioblob); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/*---write channel 1..4 to rxon for it, 0 to rxoff---*/ 6938c2ecf20Sopenharmony_cistatic ssize_t wil_write_file_rxon(struct file *file, const char __user *buf, 6948c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 6978c2ecf20Sopenharmony_ci int rc; 6988c2ecf20Sopenharmony_ci long channel; 6998c2ecf20Sopenharmony_ci bool on; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci char *kbuf = memdup_user_nul(buf, len); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (IS_ERR(kbuf)) 7048c2ecf20Sopenharmony_ci return PTR_ERR(kbuf); 7058c2ecf20Sopenharmony_ci rc = kstrtol(kbuf, 0, &channel); 7068c2ecf20Sopenharmony_ci kfree(kbuf); 7078c2ecf20Sopenharmony_ci if (rc) 7088c2ecf20Sopenharmony_ci return rc; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if ((channel < 0) || (channel > 4)) { 7118c2ecf20Sopenharmony_ci wil_err(wil, "Invalid channel %ld\n", channel); 7128c2ecf20Sopenharmony_ci return -EINVAL; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci on = !!channel; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (on) { 7178c2ecf20Sopenharmony_ci rc = wmi_set_channel(wil, (int)channel); 7188c2ecf20Sopenharmony_ci if (rc) 7198c2ecf20Sopenharmony_ci return rc; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci rc = wmi_rxon(wil, on); 7238c2ecf20Sopenharmony_ci if (rc) 7248c2ecf20Sopenharmony_ci return rc; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return len; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic const struct file_operations fops_rxon = { 7308c2ecf20Sopenharmony_ci .write = wil_write_file_rxon, 7318c2ecf20Sopenharmony_ci .open = simple_open, 7328c2ecf20Sopenharmony_ci}; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cistatic ssize_t wil_write_file_rbufcap(struct file *file, 7358c2ecf20Sopenharmony_ci const char __user *buf, 7368c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 7398c2ecf20Sopenharmony_ci int val; 7408c2ecf20Sopenharmony_ci int rc; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci rc = kstrtoint_from_user(buf, count, 0, &val); 7438c2ecf20Sopenharmony_ci if (rc) { 7448c2ecf20Sopenharmony_ci wil_err(wil, "Invalid argument\n"); 7458c2ecf20Sopenharmony_ci return rc; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci /* input value: negative to disable, 0 to use system default, 7488c2ecf20Sopenharmony_ci * 1..ring size to set descriptor threshold 7498c2ecf20Sopenharmony_ci */ 7508c2ecf20Sopenharmony_ci wil_info(wil, "%s RBUFCAP, descriptors threshold - %d\n", 7518c2ecf20Sopenharmony_ci val < 0 ? "Disabling" : "Enabling", val); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (!wil->ring_rx.va || val > wil->ring_rx.size) { 7548c2ecf20Sopenharmony_ci wil_err(wil, "Invalid descriptors threshold, %d\n", val); 7558c2ecf20Sopenharmony_ci return -EINVAL; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci rc = wmi_rbufcap_cfg(wil, val < 0 ? 0 : 1, val < 0 ? 0 : val); 7598c2ecf20Sopenharmony_ci if (rc) { 7608c2ecf20Sopenharmony_ci wil_err(wil, "RBUFCAP config failed: %d\n", rc); 7618c2ecf20Sopenharmony_ci return rc; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return count; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic const struct file_operations fops_rbufcap = { 7688c2ecf20Sopenharmony_ci .write = wil_write_file_rbufcap, 7698c2ecf20Sopenharmony_ci .open = simple_open, 7708c2ecf20Sopenharmony_ci}; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/* block ack control, write: 7738c2ecf20Sopenharmony_ci * - "add <ringid> <agg_size> <timeout>" to trigger ADDBA 7748c2ecf20Sopenharmony_ci * - "del_tx <ringid> <reason>" to trigger DELBA for Tx side 7758c2ecf20Sopenharmony_ci * - "del_rx <CID> <TID> <reason>" to trigger DELBA for Rx side 7768c2ecf20Sopenharmony_ci */ 7778c2ecf20Sopenharmony_cistatic ssize_t wil_write_back(struct file *file, const char __user *buf, 7788c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 7818c2ecf20Sopenharmony_ci int rc; 7828c2ecf20Sopenharmony_ci char *kbuf = kmalloc(len + 1, GFP_KERNEL); 7838c2ecf20Sopenharmony_ci char cmd[9]; 7848c2ecf20Sopenharmony_ci int p1, p2, p3; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (!kbuf) 7878c2ecf20Sopenharmony_ci return -ENOMEM; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); 7908c2ecf20Sopenharmony_ci if (rc != len) { 7918c2ecf20Sopenharmony_ci kfree(kbuf); 7928c2ecf20Sopenharmony_ci return rc >= 0 ? -EIO : rc; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci kbuf[len] = '\0'; 7968c2ecf20Sopenharmony_ci rc = sscanf(kbuf, "%8s %d %d %d", cmd, &p1, &p2, &p3); 7978c2ecf20Sopenharmony_ci kfree(kbuf); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (rc < 0) 8008c2ecf20Sopenharmony_ci return rc; 8018c2ecf20Sopenharmony_ci if (rc < 2) 8028c2ecf20Sopenharmony_ci return -EINVAL; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if ((strcmp(cmd, "add") == 0) || 8058c2ecf20Sopenharmony_ci (strcmp(cmd, "del_tx") == 0)) { 8068c2ecf20Sopenharmony_ci struct wil_ring_tx_data *txdata; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (p1 < 0 || p1 >= WIL6210_MAX_TX_RINGS) { 8098c2ecf20Sopenharmony_ci wil_err(wil, "BACK: invalid ring id %d\n", p1); 8108c2ecf20Sopenharmony_ci return -EINVAL; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci txdata = &wil->ring_tx_data[p1]; 8138c2ecf20Sopenharmony_ci if (strcmp(cmd, "add") == 0) { 8148c2ecf20Sopenharmony_ci if (rc < 3) { 8158c2ecf20Sopenharmony_ci wil_err(wil, "BACK: add require at least 2 params\n"); 8168c2ecf20Sopenharmony_ci return -EINVAL; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci if (rc < 4) 8198c2ecf20Sopenharmony_ci p3 = 0; 8208c2ecf20Sopenharmony_ci wmi_addba(wil, txdata->mid, p1, p2, p3); 8218c2ecf20Sopenharmony_ci } else { 8228c2ecf20Sopenharmony_ci if (rc < 3) 8238c2ecf20Sopenharmony_ci p2 = WLAN_REASON_QSTA_LEAVE_QBSS; 8248c2ecf20Sopenharmony_ci wmi_delba_tx(wil, txdata->mid, p1, p2); 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci } else if (strcmp(cmd, "del_rx") == 0) { 8278c2ecf20Sopenharmony_ci struct wil_sta_info *sta; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (rc < 3) { 8308c2ecf20Sopenharmony_ci wil_err(wil, 8318c2ecf20Sopenharmony_ci "BACK: del_rx require at least 2 params\n"); 8328c2ecf20Sopenharmony_ci return -EINVAL; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci if (p1 < 0 || p1 >= wil->max_assoc_sta) { 8358c2ecf20Sopenharmony_ci wil_err(wil, "BACK: invalid CID %d\n", p1); 8368c2ecf20Sopenharmony_ci return -EINVAL; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci if (rc < 4) 8398c2ecf20Sopenharmony_ci p3 = WLAN_REASON_QSTA_LEAVE_QBSS; 8408c2ecf20Sopenharmony_ci sta = &wil->sta[p1]; 8418c2ecf20Sopenharmony_ci wmi_delba_rx(wil, sta->mid, p1, p2, p3); 8428c2ecf20Sopenharmony_ci } else { 8438c2ecf20Sopenharmony_ci wil_err(wil, "BACK: Unrecognized command \"%s\"\n", cmd); 8448c2ecf20Sopenharmony_ci return -EINVAL; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci return len; 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic ssize_t wil_read_back(struct file *file, char __user *user_buf, 8518c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci static const char text[] = "block ack control, write:\n" 8548c2ecf20Sopenharmony_ci " - \"add <ringid> <agg_size> <timeout>\" to trigger ADDBA\n" 8558c2ecf20Sopenharmony_ci "If missing, <timeout> defaults to 0\n" 8568c2ecf20Sopenharmony_ci " - \"del_tx <ringid> <reason>\" to trigger DELBA for Tx side\n" 8578c2ecf20Sopenharmony_ci " - \"del_rx <CID> <TID> <reason>\" to trigger DELBA for Rx side\n" 8588c2ecf20Sopenharmony_ci "If missing, <reason> set to \"STA_LEAVING\" (36)\n"; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, text, 8618c2ecf20Sopenharmony_ci sizeof(text)); 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic const struct file_operations fops_back = { 8658c2ecf20Sopenharmony_ci .read = wil_read_back, 8668c2ecf20Sopenharmony_ci .write = wil_write_back, 8678c2ecf20Sopenharmony_ci .open = simple_open, 8688c2ecf20Sopenharmony_ci}; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci/* pmc control, write: 8718c2ecf20Sopenharmony_ci * - "alloc <num descriptors> <descriptor_size>" to allocate PMC 8728c2ecf20Sopenharmony_ci * - "free" to release memory allocated for PMC 8738c2ecf20Sopenharmony_ci */ 8748c2ecf20Sopenharmony_cistatic ssize_t wil_write_pmccfg(struct file *file, const char __user *buf, 8758c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 8788c2ecf20Sopenharmony_ci int rc; 8798c2ecf20Sopenharmony_ci char *kbuf = kmalloc(len + 1, GFP_KERNEL); 8808c2ecf20Sopenharmony_ci char cmd[9]; 8818c2ecf20Sopenharmony_ci int num_descs, desc_size; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci if (!kbuf) 8848c2ecf20Sopenharmony_ci return -ENOMEM; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); 8878c2ecf20Sopenharmony_ci if (rc != len) { 8888c2ecf20Sopenharmony_ci kfree(kbuf); 8898c2ecf20Sopenharmony_ci return rc >= 0 ? -EIO : rc; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci kbuf[len] = '\0'; 8938c2ecf20Sopenharmony_ci rc = sscanf(kbuf, "%8s %d %d", cmd, &num_descs, &desc_size); 8948c2ecf20Sopenharmony_ci kfree(kbuf); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (rc < 0) 8978c2ecf20Sopenharmony_ci return rc; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if (rc < 1) { 9008c2ecf20Sopenharmony_ci wil_err(wil, "pmccfg: no params given\n"); 9018c2ecf20Sopenharmony_ci return -EINVAL; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (0 == strcmp(cmd, "alloc")) { 9058c2ecf20Sopenharmony_ci if (rc != 3) { 9068c2ecf20Sopenharmony_ci wil_err(wil, "pmccfg: alloc requires 2 params\n"); 9078c2ecf20Sopenharmony_ci return -EINVAL; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci wil_pmc_alloc(wil, num_descs, desc_size); 9108c2ecf20Sopenharmony_ci } else if (0 == strcmp(cmd, "free")) { 9118c2ecf20Sopenharmony_ci if (rc != 1) { 9128c2ecf20Sopenharmony_ci wil_err(wil, "pmccfg: free does not have any params\n"); 9138c2ecf20Sopenharmony_ci return -EINVAL; 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci wil_pmc_free(wil, true); 9168c2ecf20Sopenharmony_ci } else { 9178c2ecf20Sopenharmony_ci wil_err(wil, "pmccfg: Unrecognized command \"%s\"\n", cmd); 9188c2ecf20Sopenharmony_ci return -EINVAL; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci return len; 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cistatic ssize_t wil_read_pmccfg(struct file *file, char __user *user_buf, 9258c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 9288c2ecf20Sopenharmony_ci char text[256]; 9298c2ecf20Sopenharmony_ci char help[] = "pmc control, write:\n" 9308c2ecf20Sopenharmony_ci " - \"alloc <num descriptors> <descriptor_size>\" to allocate pmc\n" 9318c2ecf20Sopenharmony_ci " - \"free\" to free memory allocated for pmc\n"; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci snprintf(text, sizeof(text), "Last command status: %d\n\n%s", 9348c2ecf20Sopenharmony_ci wil_pmc_last_cmd_status(wil), help); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, text, 9378c2ecf20Sopenharmony_ci strlen(text) + 1); 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic const struct file_operations fops_pmccfg = { 9418c2ecf20Sopenharmony_ci .read = wil_read_pmccfg, 9428c2ecf20Sopenharmony_ci .write = wil_write_pmccfg, 9438c2ecf20Sopenharmony_ci .open = simple_open, 9448c2ecf20Sopenharmony_ci}; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic const struct file_operations fops_pmcdata = { 9478c2ecf20Sopenharmony_ci .open = simple_open, 9488c2ecf20Sopenharmony_ci .read = wil_pmc_read, 9498c2ecf20Sopenharmony_ci .llseek = wil_pmc_llseek, 9508c2ecf20Sopenharmony_ci}; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic int wil_pmcring_seq_open(struct inode *inode, struct file *file) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci return single_open(file, wil_pmcring_read, inode->i_private); 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_cistatic const struct file_operations fops_pmcring = { 9588c2ecf20Sopenharmony_ci .open = wil_pmcring_seq_open, 9598c2ecf20Sopenharmony_ci .release = single_release, 9608c2ecf20Sopenharmony_ci .read = seq_read, 9618c2ecf20Sopenharmony_ci .llseek = seq_lseek, 9628c2ecf20Sopenharmony_ci}; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci/*---tx_mgmt---*/ 9658c2ecf20Sopenharmony_ci/* Write mgmt frame to this file to send it */ 9668c2ecf20Sopenharmony_cistatic ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf, 9678c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 9708c2ecf20Sopenharmony_ci struct wiphy *wiphy = wil_to_wiphy(wil); 9718c2ecf20Sopenharmony_ci struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr; 9728c2ecf20Sopenharmony_ci struct cfg80211_mgmt_tx_params params; 9738c2ecf20Sopenharmony_ci int rc; 9748c2ecf20Sopenharmony_ci void *frame; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci memset(¶ms, 0, sizeof(params)); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (!len) 9798c2ecf20Sopenharmony_ci return -EINVAL; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci frame = memdup_user(buf, len); 9828c2ecf20Sopenharmony_ci if (IS_ERR(frame)) 9838c2ecf20Sopenharmony_ci return PTR_ERR(frame); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci params.buf = frame; 9868c2ecf20Sopenharmony_ci params.len = len; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci rc = wil_cfg80211_mgmt_tx(wiphy, wdev, ¶ms, NULL); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci kfree(frame); 9918c2ecf20Sopenharmony_ci wil_info(wil, "-> %d\n", rc); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci return len; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic const struct file_operations fops_txmgmt = { 9978c2ecf20Sopenharmony_ci .write = wil_write_file_txmgmt, 9988c2ecf20Sopenharmony_ci .open = simple_open, 9998c2ecf20Sopenharmony_ci}; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci/* Write WMI command (w/o mbox header) to this file to send it 10028c2ecf20Sopenharmony_ci * WMI starts from wil6210_mbox_hdr_wmi header 10038c2ecf20Sopenharmony_ci */ 10048c2ecf20Sopenharmony_cistatic ssize_t wil_write_file_wmi(struct file *file, const char __user *buf, 10058c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 10088c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 10098c2ecf20Sopenharmony_ci struct wmi_cmd_hdr *wmi; 10108c2ecf20Sopenharmony_ci void *cmd; 10118c2ecf20Sopenharmony_ci int cmdlen = len - sizeof(struct wmi_cmd_hdr); 10128c2ecf20Sopenharmony_ci u16 cmdid; 10138c2ecf20Sopenharmony_ci int rc1; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if (cmdlen < 0 || *ppos != 0) 10168c2ecf20Sopenharmony_ci return -EINVAL; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci wmi = memdup_user(buf, len); 10198c2ecf20Sopenharmony_ci if (IS_ERR(wmi)) 10208c2ecf20Sopenharmony_ci return PTR_ERR(wmi); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci cmd = (cmdlen > 0) ? &wmi[1] : NULL; 10238c2ecf20Sopenharmony_ci cmdid = le16_to_cpu(wmi->command_id); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci rc1 = wmi_send(wil, cmdid, vif->mid, cmd, cmdlen); 10268c2ecf20Sopenharmony_ci kfree(wmi); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci wil_info(wil, "0x%04x[%d] -> %d\n", cmdid, cmdlen, rc1); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci return len; 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_cistatic const struct file_operations fops_wmi = { 10348c2ecf20Sopenharmony_ci .write = wil_write_file_wmi, 10358c2ecf20Sopenharmony_ci .open = simple_open, 10368c2ecf20Sopenharmony_ci}; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic void wil_seq_print_skb(struct seq_file *s, struct sk_buff *skb) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci int i = 0; 10418c2ecf20Sopenharmony_ci int len = skb_headlen(skb); 10428c2ecf20Sopenharmony_ci void *p = skb->data; 10438c2ecf20Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci seq_printf(s, " len = %d\n", len); 10468c2ecf20Sopenharmony_ci wil_seq_hexdump(s, p, len, " : "); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (nr_frags) { 10498c2ecf20Sopenharmony_ci seq_printf(s, " nr_frags = %d\n", nr_frags); 10508c2ecf20Sopenharmony_ci for (i = 0; i < nr_frags; i++) { 10518c2ecf20Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci len = skb_frag_size(frag); 10548c2ecf20Sopenharmony_ci p = skb_frag_address_safe(frag); 10558c2ecf20Sopenharmony_ci seq_printf(s, " [%2d] : len = %d\n", i, len); 10568c2ecf20Sopenharmony_ci wil_seq_hexdump(s, p, len, " : "); 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci/*---------Tx/Rx descriptor------------*/ 10628c2ecf20Sopenharmony_cistatic int txdesc_show(struct seq_file *s, void *data) 10638c2ecf20Sopenharmony_ci{ 10648c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 10658c2ecf20Sopenharmony_ci struct wil_ring *ring; 10668c2ecf20Sopenharmony_ci bool tx; 10678c2ecf20Sopenharmony_ci int ring_idx = dbg_ring_index; 10688c2ecf20Sopenharmony_ci int txdesc_idx = dbg_txdesc_index; 10698c2ecf20Sopenharmony_ci volatile struct vring_tx_desc *d; 10708c2ecf20Sopenharmony_ci volatile u32 *u; 10718c2ecf20Sopenharmony_ci struct sk_buff *skb; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 10748c2ecf20Sopenharmony_ci /* RX ring index == 0 */ 10758c2ecf20Sopenharmony_ci if (ring_idx >= WIL6210_MAX_TX_RINGS) { 10768c2ecf20Sopenharmony_ci seq_printf(s, "invalid ring index %d\n", ring_idx); 10778c2ecf20Sopenharmony_ci return 0; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci tx = ring_idx > 0; /* desc ring 0 is reserved for RX */ 10808c2ecf20Sopenharmony_ci } else { 10818c2ecf20Sopenharmony_ci /* RX ring index == WIL6210_MAX_TX_RINGS */ 10828c2ecf20Sopenharmony_ci if (ring_idx > WIL6210_MAX_TX_RINGS) { 10838c2ecf20Sopenharmony_ci seq_printf(s, "invalid ring index %d\n", ring_idx); 10848c2ecf20Sopenharmony_ci return 0; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci tx = (ring_idx < WIL6210_MAX_TX_RINGS); 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci ring = tx ? &wil->ring_tx[ring_idx] : &wil->ring_rx; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (!ring->va) { 10928c2ecf20Sopenharmony_ci if (tx) 10938c2ecf20Sopenharmony_ci seq_printf(s, "No Tx[%2d] RING\n", ring_idx); 10948c2ecf20Sopenharmony_ci else 10958c2ecf20Sopenharmony_ci seq_puts(s, "No Rx RING\n"); 10968c2ecf20Sopenharmony_ci return 0; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (txdesc_idx >= ring->size) { 11008c2ecf20Sopenharmony_ci if (tx) 11018c2ecf20Sopenharmony_ci seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n", 11028c2ecf20Sopenharmony_ci ring_idx, txdesc_idx, ring->size); 11038c2ecf20Sopenharmony_ci else 11048c2ecf20Sopenharmony_ci seq_printf(s, "RxDesc index (%d) >= size (%d)\n", 11058c2ecf20Sopenharmony_ci txdesc_idx, ring->size); 11068c2ecf20Sopenharmony_ci return 0; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci /* use struct vring_tx_desc for Rx as well, 11108c2ecf20Sopenharmony_ci * only field used, .dma.length, is the same 11118c2ecf20Sopenharmony_ci */ 11128c2ecf20Sopenharmony_ci d = &ring->va[txdesc_idx].tx.legacy; 11138c2ecf20Sopenharmony_ci u = (volatile u32 *)d; 11148c2ecf20Sopenharmony_ci skb = NULL; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 11178c2ecf20Sopenharmony_ci if (tx) { 11188c2ecf20Sopenharmony_ci skb = ring->ctx ? ring->ctx[txdesc_idx].skb : NULL; 11198c2ecf20Sopenharmony_ci } else if (wil->rx_buff_mgmt.buff_arr) { 11208c2ecf20Sopenharmony_ci struct wil_rx_enhanced_desc *rx_d = 11218c2ecf20Sopenharmony_ci (struct wil_rx_enhanced_desc *) 11228c2ecf20Sopenharmony_ci &ring->va[txdesc_idx].rx.enhanced; 11238c2ecf20Sopenharmony_ci u16 buff_id = le16_to_cpu(rx_d->mac.buff_id); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci if (!wil_val_in_range(buff_id, 0, 11268c2ecf20Sopenharmony_ci wil->rx_buff_mgmt.size)) 11278c2ecf20Sopenharmony_ci seq_printf(s, "invalid buff_id %d\n", buff_id); 11288c2ecf20Sopenharmony_ci else 11298c2ecf20Sopenharmony_ci skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci } else { 11328c2ecf20Sopenharmony_ci skb = ring->ctx[txdesc_idx].skb; 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci if (tx) 11358c2ecf20Sopenharmony_ci seq_printf(s, "Tx[%2d][%3d] = {\n", ring_idx, 11368c2ecf20Sopenharmony_ci txdesc_idx); 11378c2ecf20Sopenharmony_ci else 11388c2ecf20Sopenharmony_ci seq_printf(s, "Rx[%3d] = {\n", txdesc_idx); 11398c2ecf20Sopenharmony_ci seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", 11408c2ecf20Sopenharmony_ci u[0], u[1], u[2], u[3]); 11418c2ecf20Sopenharmony_ci seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", 11428c2ecf20Sopenharmony_ci u[4], u[5], u[6], u[7]); 11438c2ecf20Sopenharmony_ci seq_printf(s, " SKB = 0x%p\n", skb); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (skb) { 11468c2ecf20Sopenharmony_ci skb_get(skb); 11478c2ecf20Sopenharmony_ci wil_seq_print_skb(s, skb); 11488c2ecf20Sopenharmony_ci kfree_skb(skb); 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci seq_puts(s, "}\n"); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci return 0; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(txdesc); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci/*---------Tx/Rx status message------------*/ 11578c2ecf20Sopenharmony_cistatic int status_msg_show(struct seq_file *s, void *data) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 11608c2ecf20Sopenharmony_ci int sring_idx = dbg_sring_index; 11618c2ecf20Sopenharmony_ci struct wil_status_ring *sring; 11628c2ecf20Sopenharmony_ci bool tx; 11638c2ecf20Sopenharmony_ci u32 status_msg_idx = dbg_status_msg_index; 11648c2ecf20Sopenharmony_ci u32 *u; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci if (sring_idx >= WIL6210_MAX_STATUS_RINGS) { 11678c2ecf20Sopenharmony_ci seq_printf(s, "invalid status ring index %d\n", sring_idx); 11688c2ecf20Sopenharmony_ci return 0; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci sring = &wil->srings[sring_idx]; 11728c2ecf20Sopenharmony_ci tx = !sring->is_rx; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci if (!sring->va) { 11758c2ecf20Sopenharmony_ci seq_printf(s, "No %cX status ring\n", tx ? 'T' : 'R'); 11768c2ecf20Sopenharmony_ci return 0; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci if (status_msg_idx >= sring->size) { 11808c2ecf20Sopenharmony_ci seq_printf(s, "%cxDesc index (%d) >= size (%d)\n", 11818c2ecf20Sopenharmony_ci tx ? 'T' : 'R', status_msg_idx, sring->size); 11828c2ecf20Sopenharmony_ci return 0; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci u = sring->va + (sring->elem_size * status_msg_idx); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci seq_printf(s, "%cx[%d][%3d] = {\n", 11888c2ecf20Sopenharmony_ci tx ? 'T' : 'R', sring_idx, status_msg_idx); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x\n", 11918c2ecf20Sopenharmony_ci u[0], u[1], u[2], u[3]); 11928c2ecf20Sopenharmony_ci if (!tx && !wil->use_compressed_rx_status) 11938c2ecf20Sopenharmony_ci seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x\n", 11948c2ecf20Sopenharmony_ci u[4], u[5], u[6], u[7]); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci seq_puts(s, "}\n"); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci return 0; 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(status_msg); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_cistatic int wil_print_rx_buff(struct seq_file *s, struct list_head *lh) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci struct wil_rx_buff *it; 12058c2ecf20Sopenharmony_ci int i = 0; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci list_for_each_entry(it, lh, list) { 12088c2ecf20Sopenharmony_ci if ((i % 16) == 0 && i != 0) 12098c2ecf20Sopenharmony_ci seq_puts(s, "\n "); 12108c2ecf20Sopenharmony_ci seq_printf(s, "[%4d] ", it->id); 12118c2ecf20Sopenharmony_ci i++; 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci seq_printf(s, "\nNumber of buffers: %u\n", i); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci return i; 12168c2ecf20Sopenharmony_ci} 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_cistatic int rx_buff_mgmt_show(struct seq_file *s, void *data) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 12218c2ecf20Sopenharmony_ci struct wil_rx_buff_mgmt *rbm = &wil->rx_buff_mgmt; 12228c2ecf20Sopenharmony_ci int num_active; 12238c2ecf20Sopenharmony_ci int num_free; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (!rbm->buff_arr) 12268c2ecf20Sopenharmony_ci return -EINVAL; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci seq_printf(s, " size = %zu\n", rbm->size); 12298c2ecf20Sopenharmony_ci seq_printf(s, " free_list_empty_cnt = %lu\n", 12308c2ecf20Sopenharmony_ci rbm->free_list_empty_cnt); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci /* Print active list */ 12338c2ecf20Sopenharmony_ci seq_puts(s, " Active list:\n"); 12348c2ecf20Sopenharmony_ci num_active = wil_print_rx_buff(s, &rbm->active); 12358c2ecf20Sopenharmony_ci seq_puts(s, "\n Free list:\n"); 12368c2ecf20Sopenharmony_ci num_free = wil_print_rx_buff(s, &rbm->free); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci seq_printf(s, " Total number of buffers: %u\n", 12398c2ecf20Sopenharmony_ci num_active + num_free); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci return 0; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(rx_buff_mgmt); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci/*---------beamforming------------*/ 12468c2ecf20Sopenharmony_cistatic char *wil_bfstatus_str(u32 status) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci switch (status) { 12498c2ecf20Sopenharmony_ci case 0: 12508c2ecf20Sopenharmony_ci return "Failed"; 12518c2ecf20Sopenharmony_ci case 1: 12528c2ecf20Sopenharmony_ci return "OK"; 12538c2ecf20Sopenharmony_ci case 2: 12548c2ecf20Sopenharmony_ci return "Retrying"; 12558c2ecf20Sopenharmony_ci default: 12568c2ecf20Sopenharmony_ci return "??"; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic bool is_all_zeros(void * const x_, size_t sz) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci /* if reply is all-0, ignore this CID */ 12638c2ecf20Sopenharmony_ci u32 *x = x_; 12648c2ecf20Sopenharmony_ci int n; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci for (n = 0; n < sz / sizeof(*x); n++) 12678c2ecf20Sopenharmony_ci if (x[n]) 12688c2ecf20Sopenharmony_ci return false; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci return true; 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic int bf_show(struct seq_file *s, void *data) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci int rc; 12768c2ecf20Sopenharmony_ci int i; 12778c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 12788c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 12798c2ecf20Sopenharmony_ci struct wmi_notify_req_cmd cmd = { 12808c2ecf20Sopenharmony_ci .interval_usec = 0, 12818c2ecf20Sopenharmony_ci }; 12828c2ecf20Sopenharmony_ci struct { 12838c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 12848c2ecf20Sopenharmony_ci struct wmi_notify_req_done_event evt; 12858c2ecf20Sopenharmony_ci } __packed reply; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci memset(&reply, 0, sizeof(reply)); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 12908c2ecf20Sopenharmony_ci u32 status; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci cmd.cid = i; 12938c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, 12948c2ecf20Sopenharmony_ci &cmd, sizeof(cmd), 12958c2ecf20Sopenharmony_ci WMI_NOTIFY_REQ_DONE_EVENTID, &reply, 12968c2ecf20Sopenharmony_ci sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 12978c2ecf20Sopenharmony_ci /* if reply is all-0, ignore this CID */ 12988c2ecf20Sopenharmony_ci if (rc || is_all_zeros(&reply.evt, sizeof(reply.evt))) 12998c2ecf20Sopenharmony_ci continue; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci status = le32_to_cpu(reply.evt.status); 13028c2ecf20Sopenharmony_ci seq_printf(s, "CID %d {\n" 13038c2ecf20Sopenharmony_ci " TSF = 0x%016llx\n" 13048c2ecf20Sopenharmony_ci " TxMCS = %2d TxTpt = %4d\n" 13058c2ecf20Sopenharmony_ci " SQI = %4d\n" 13068c2ecf20Sopenharmony_ci " RSSI = %4d\n" 13078c2ecf20Sopenharmony_ci " Status = 0x%08x %s\n" 13088c2ecf20Sopenharmony_ci " Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n" 13098c2ecf20Sopenharmony_ci " Goodput(rx:tx) %4d:%4d\n" 13108c2ecf20Sopenharmony_ci "}\n", 13118c2ecf20Sopenharmony_ci i, 13128c2ecf20Sopenharmony_ci le64_to_cpu(reply.evt.tsf), 13138c2ecf20Sopenharmony_ci le16_to_cpu(reply.evt.bf_mcs), 13148c2ecf20Sopenharmony_ci le32_to_cpu(reply.evt.tx_tpt), 13158c2ecf20Sopenharmony_ci reply.evt.sqi, 13168c2ecf20Sopenharmony_ci reply.evt.rssi, 13178c2ecf20Sopenharmony_ci status, wil_bfstatus_str(status), 13188c2ecf20Sopenharmony_ci le16_to_cpu(reply.evt.my_rx_sector), 13198c2ecf20Sopenharmony_ci le16_to_cpu(reply.evt.my_tx_sector), 13208c2ecf20Sopenharmony_ci le16_to_cpu(reply.evt.other_rx_sector), 13218c2ecf20Sopenharmony_ci le16_to_cpu(reply.evt.other_tx_sector), 13228c2ecf20Sopenharmony_ci le32_to_cpu(reply.evt.rx_goodput), 13238c2ecf20Sopenharmony_ci le32_to_cpu(reply.evt.tx_goodput)); 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci return 0; 13268c2ecf20Sopenharmony_ci} 13278c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(bf); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci/*---------temp------------*/ 13308c2ecf20Sopenharmony_cistatic void print_temp(struct seq_file *s, const char *prefix, s32 t) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci switch (t) { 13338c2ecf20Sopenharmony_ci case 0: 13348c2ecf20Sopenharmony_ci case WMI_INVALID_TEMPERATURE: 13358c2ecf20Sopenharmony_ci seq_printf(s, "%s N/A\n", prefix); 13368c2ecf20Sopenharmony_ci break; 13378c2ecf20Sopenharmony_ci default: 13388c2ecf20Sopenharmony_ci seq_printf(s, "%s %s%d.%03d\n", prefix, (t < 0 ? "-" : ""), 13398c2ecf20Sopenharmony_ci abs(t / 1000), abs(t % 1000)); 13408c2ecf20Sopenharmony_ci break; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci} 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_cistatic int temp_show(struct seq_file *s, void *data) 13458c2ecf20Sopenharmony_ci{ 13468c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 13478c2ecf20Sopenharmony_ci int rc, i; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci if (test_bit(WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF, 13508c2ecf20Sopenharmony_ci wil->fw_capabilities)) { 13518c2ecf20Sopenharmony_ci struct wmi_temp_sense_all_done_event sense_all_evt; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci wil_dbg_misc(wil, 13548c2ecf20Sopenharmony_ci "WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is supported"); 13558c2ecf20Sopenharmony_ci rc = wmi_get_all_temperatures(wil, &sense_all_evt); 13568c2ecf20Sopenharmony_ci if (rc) { 13578c2ecf20Sopenharmony_ci seq_puts(s, "Failed\n"); 13588c2ecf20Sopenharmony_ci return 0; 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci print_temp(s, "T_mac =", 13618c2ecf20Sopenharmony_ci le32_to_cpu(sense_all_evt.baseband_t1000)); 13628c2ecf20Sopenharmony_ci seq_printf(s, "Connected RFs [0x%08x]\n", 13638c2ecf20Sopenharmony_ci sense_all_evt.rf_bitmap); 13648c2ecf20Sopenharmony_ci for (i = 0; i < WMI_MAX_XIF_PORTS_NUM; i++) { 13658c2ecf20Sopenharmony_ci seq_printf(s, "RF[%d] = ", i); 13668c2ecf20Sopenharmony_ci print_temp(s, "", 13678c2ecf20Sopenharmony_ci le32_to_cpu(sense_all_evt.rf_t1000[i])); 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci } else { 13708c2ecf20Sopenharmony_ci s32 t_m, t_r; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci wil_dbg_misc(wil, 13738c2ecf20Sopenharmony_ci "WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is not supported"); 13748c2ecf20Sopenharmony_ci rc = wmi_get_temperature(wil, &t_m, &t_r); 13758c2ecf20Sopenharmony_ci if (rc) { 13768c2ecf20Sopenharmony_ci seq_puts(s, "Failed\n"); 13778c2ecf20Sopenharmony_ci return 0; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci print_temp(s, "T_mac =", t_m); 13808c2ecf20Sopenharmony_ci print_temp(s, "T_radio =", t_r); 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci return 0; 13838c2ecf20Sopenharmony_ci} 13848c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(temp); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci/*---------freq------------*/ 13878c2ecf20Sopenharmony_cistatic int freq_show(struct seq_file *s, void *data) 13888c2ecf20Sopenharmony_ci{ 13898c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 13908c2ecf20Sopenharmony_ci struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr; 13918c2ecf20Sopenharmony_ci u32 freq = wdev->chandef.chan ? wdev->chandef.chan->center_freq : 0; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci seq_printf(s, "Freq = %d\n", freq); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci return 0; 13968c2ecf20Sopenharmony_ci} 13978c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(freq); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci/*---------link------------*/ 14008c2ecf20Sopenharmony_cistatic int link_show(struct seq_file *s, void *data) 14018c2ecf20Sopenharmony_ci{ 14028c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 14038c2ecf20Sopenharmony_ci struct station_info *sinfo; 14048c2ecf20Sopenharmony_ci int i, rc = 0; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); 14078c2ecf20Sopenharmony_ci if (!sinfo) 14088c2ecf20Sopenharmony_ci return -ENOMEM; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 14118c2ecf20Sopenharmony_ci struct wil_sta_info *p = &wil->sta[i]; 14128c2ecf20Sopenharmony_ci char *status = "unknown"; 14138c2ecf20Sopenharmony_ci struct wil6210_vif *vif; 14148c2ecf20Sopenharmony_ci u8 mid; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci switch (p->status) { 14178c2ecf20Sopenharmony_ci case wil_sta_unused: 14188c2ecf20Sopenharmony_ci status = "unused "; 14198c2ecf20Sopenharmony_ci break; 14208c2ecf20Sopenharmony_ci case wil_sta_conn_pending: 14218c2ecf20Sopenharmony_ci status = "pending "; 14228c2ecf20Sopenharmony_ci break; 14238c2ecf20Sopenharmony_ci case wil_sta_connected: 14248c2ecf20Sopenharmony_ci status = "connected"; 14258c2ecf20Sopenharmony_ci break; 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX; 14288c2ecf20Sopenharmony_ci seq_printf(s, "[%d][MID %d] %pM %s\n", 14298c2ecf20Sopenharmony_ci i, mid, p->addr, status); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (p->status != wil_sta_connected) 14328c2ecf20Sopenharmony_ci continue; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci vif = (mid < GET_MAX_VIFS(wil)) ? wil->vifs[mid] : NULL; 14358c2ecf20Sopenharmony_ci if (vif) { 14368c2ecf20Sopenharmony_ci rc = wil_cid_fill_sinfo(vif, i, sinfo); 14378c2ecf20Sopenharmony_ci if (rc) 14388c2ecf20Sopenharmony_ci goto out; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci seq_printf(s, " Tx_mcs = %d\n", sinfo->txrate.mcs); 14418c2ecf20Sopenharmony_ci seq_printf(s, " Rx_mcs = %d\n", sinfo->rxrate.mcs); 14428c2ecf20Sopenharmony_ci seq_printf(s, " SQ = %d\n", sinfo->signal); 14438c2ecf20Sopenharmony_ci } else { 14448c2ecf20Sopenharmony_ci seq_puts(s, " INVALID MID\n"); 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ciout: 14498c2ecf20Sopenharmony_ci kfree(sinfo); 14508c2ecf20Sopenharmony_ci return rc; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(link); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci/*---------info------------*/ 14558c2ecf20Sopenharmony_cistatic int info_show(struct seq_file *s, void *data) 14568c2ecf20Sopenharmony_ci{ 14578c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 14588c2ecf20Sopenharmony_ci struct net_device *ndev = wil->main_ndev; 14598c2ecf20Sopenharmony_ci int is_ac = power_supply_is_system_supplied(); 14608c2ecf20Sopenharmony_ci int rx = atomic_xchg(&wil->isr_count_rx, 0); 14618c2ecf20Sopenharmony_ci int tx = atomic_xchg(&wil->isr_count_tx, 0); 14628c2ecf20Sopenharmony_ci static ulong rxf_old, txf_old; 14638c2ecf20Sopenharmony_ci ulong rxf = ndev->stats.rx_packets; 14648c2ecf20Sopenharmony_ci ulong txf = ndev->stats.tx_packets; 14658c2ecf20Sopenharmony_ci unsigned int i; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci /* >0 : AC; 0 : battery; <0 : error */ 14688c2ecf20Sopenharmony_ci seq_printf(s, "AC powered : %d\n", is_ac); 14698c2ecf20Sopenharmony_ci seq_printf(s, "Rx irqs:packets : %8d : %8ld\n", rx, rxf - rxf_old); 14708c2ecf20Sopenharmony_ci seq_printf(s, "Tx irqs:packets : %8d : %8ld\n", tx, txf - txf_old); 14718c2ecf20Sopenharmony_ci rxf_old = rxf; 14728c2ecf20Sopenharmony_ci txf_old = txf; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci#define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \ 14758c2ecf20Sopenharmony_ci " " __stringify(x) : "" 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci for (i = 0; i < ndev->num_tx_queues; i++) { 14788c2ecf20Sopenharmony_ci struct netdev_queue *txq = netdev_get_tx_queue(ndev, i); 14798c2ecf20Sopenharmony_ci unsigned long state = txq->state; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci seq_printf(s, "Tx queue[%i] state : 0x%lx%s%s%s\n", i, state, 14828c2ecf20Sopenharmony_ci CHECK_QSTATE(DRV_XOFF), 14838c2ecf20Sopenharmony_ci CHECK_QSTATE(STACK_XOFF), 14848c2ecf20Sopenharmony_ci CHECK_QSTATE(FROZEN) 14858c2ecf20Sopenharmony_ci ); 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci#undef CHECK_QSTATE 14888c2ecf20Sopenharmony_ci return 0; 14898c2ecf20Sopenharmony_ci} 14908c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(info); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci/*---------recovery------------*/ 14938c2ecf20Sopenharmony_ci/* mode = [manual|auto] 14948c2ecf20Sopenharmony_ci * state = [idle|pending|running] 14958c2ecf20Sopenharmony_ci */ 14968c2ecf20Sopenharmony_cistatic ssize_t wil_read_file_recovery(struct file *file, char __user *user_buf, 14978c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 14988c2ecf20Sopenharmony_ci{ 14998c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 15008c2ecf20Sopenharmony_ci char buf[80]; 15018c2ecf20Sopenharmony_ci int n; 15028c2ecf20Sopenharmony_ci static const char * const sstate[] = {"idle", "pending", "running"}; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci n = snprintf(buf, sizeof(buf), "mode = %s\nstate = %s\n", 15058c2ecf20Sopenharmony_ci no_fw_recovery ? "manual" : "auto", 15068c2ecf20Sopenharmony_ci sstate[wil->recovery_state]); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci n = min_t(int, n, sizeof(buf)); 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, 15118c2ecf20Sopenharmony_ci buf, n); 15128c2ecf20Sopenharmony_ci} 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_cistatic ssize_t wil_write_file_recovery(struct file *file, 15158c2ecf20Sopenharmony_ci const char __user *buf_, 15168c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 15178c2ecf20Sopenharmony_ci{ 15188c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 15198c2ecf20Sopenharmony_ci static const char run_command[] = "run"; 15208c2ecf20Sopenharmony_ci char buf[sizeof(run_command) + 1]; /* to detect "runx" */ 15218c2ecf20Sopenharmony_ci ssize_t rc; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci if (wil->recovery_state != fw_recovery_pending) { 15248c2ecf20Sopenharmony_ci wil_err(wil, "No recovery pending\n"); 15258c2ecf20Sopenharmony_ci return -EINVAL; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci if (*ppos != 0) { 15298c2ecf20Sopenharmony_ci wil_err(wil, "Offset [%d]\n", (int)*ppos); 15308c2ecf20Sopenharmony_ci return -EINVAL; 15318c2ecf20Sopenharmony_ci } 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci if (count > sizeof(buf)) { 15348c2ecf20Sopenharmony_ci wil_err(wil, "Input too long, len = %d\n", (int)count); 15358c2ecf20Sopenharmony_ci return -EINVAL; 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, buf_, count); 15398c2ecf20Sopenharmony_ci if (rc < 0) 15408c2ecf20Sopenharmony_ci return rc; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci buf[rc] = '\0'; 15438c2ecf20Sopenharmony_ci if (0 == strcmp(buf, run_command)) 15448c2ecf20Sopenharmony_ci wil_set_recovery_state(wil, fw_recovery_running); 15458c2ecf20Sopenharmony_ci else 15468c2ecf20Sopenharmony_ci wil_err(wil, "Bad recovery command \"%s\"\n", buf); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci return rc; 15498c2ecf20Sopenharmony_ci} 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_cistatic const struct file_operations fops_recovery = { 15528c2ecf20Sopenharmony_ci .read = wil_read_file_recovery, 15538c2ecf20Sopenharmony_ci .write = wil_write_file_recovery, 15548c2ecf20Sopenharmony_ci .open = simple_open, 15558c2ecf20Sopenharmony_ci}; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci/*---------Station matrix------------*/ 15588c2ecf20Sopenharmony_cistatic void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r) 15598c2ecf20Sopenharmony_ci{ 15608c2ecf20Sopenharmony_ci int i; 15618c2ecf20Sopenharmony_ci u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size; 15628c2ecf20Sopenharmony_ci unsigned long long drop_dup = r->drop_dup, drop_old = r->drop_old; 15638c2ecf20Sopenharmony_ci unsigned long long drop_dup_mcast = r->drop_dup_mcast; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci seq_printf(s, "([%2d]) 0x%03x [", r->buf_size, r->head_seq_num); 15668c2ecf20Sopenharmony_ci for (i = 0; i < r->buf_size; i++) { 15678c2ecf20Sopenharmony_ci if (i == index) 15688c2ecf20Sopenharmony_ci seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|'); 15698c2ecf20Sopenharmony_ci else 15708c2ecf20Sopenharmony_ci seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_'); 15718c2ecf20Sopenharmony_ci } 15728c2ecf20Sopenharmony_ci seq_printf(s, 15738c2ecf20Sopenharmony_ci "] total %llu drop %llu (dup %llu + old %llu + dup mcast %llu) last 0x%03x\n", 15748c2ecf20Sopenharmony_ci r->total, drop_dup + drop_old + drop_dup_mcast, drop_dup, 15758c2ecf20Sopenharmony_ci drop_old, drop_dup_mcast, r->ssn_last_drop); 15768c2ecf20Sopenharmony_ci} 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_cistatic void wil_print_rxtid_crypto(struct seq_file *s, int tid, 15798c2ecf20Sopenharmony_ci struct wil_tid_crypto_rx *c) 15808c2ecf20Sopenharmony_ci{ 15818c2ecf20Sopenharmony_ci int i; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 15848c2ecf20Sopenharmony_ci struct wil_tid_crypto_rx_single *cc = &c->key_id[i]; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci if (cc->key_set) 15878c2ecf20Sopenharmony_ci goto has_keys; 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci return; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_cihas_keys: 15928c2ecf20Sopenharmony_ci if (tid < WIL_STA_TID_NUM) 15938c2ecf20Sopenharmony_ci seq_printf(s, " [%2d] PN", tid); 15948c2ecf20Sopenharmony_ci else 15958c2ecf20Sopenharmony_ci seq_puts(s, " [GR] PN"); 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 15988c2ecf20Sopenharmony_ci struct wil_tid_crypto_rx_single *cc = &c->key_id[i]; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci seq_printf(s, " [%i%s]%6phN", i, cc->key_set ? "+" : "-", 16018c2ecf20Sopenharmony_ci cc->pn); 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 16048c2ecf20Sopenharmony_ci} 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_cistatic int sta_show(struct seq_file *s, void *data) 16078c2ecf20Sopenharmony_ci__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 16108c2ecf20Sopenharmony_ci int i, tid, mcs; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 16138c2ecf20Sopenharmony_ci struct wil_sta_info *p = &wil->sta[i]; 16148c2ecf20Sopenharmony_ci char *status = "unknown"; 16158c2ecf20Sopenharmony_ci u8 aid = 0; 16168c2ecf20Sopenharmony_ci u8 mid; 16178c2ecf20Sopenharmony_ci bool sta_connected = false; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci switch (p->status) { 16208c2ecf20Sopenharmony_ci case wil_sta_unused: 16218c2ecf20Sopenharmony_ci status = "unused "; 16228c2ecf20Sopenharmony_ci break; 16238c2ecf20Sopenharmony_ci case wil_sta_conn_pending: 16248c2ecf20Sopenharmony_ci status = "pending "; 16258c2ecf20Sopenharmony_ci break; 16268c2ecf20Sopenharmony_ci case wil_sta_connected: 16278c2ecf20Sopenharmony_ci status = "connected"; 16288c2ecf20Sopenharmony_ci aid = p->aid; 16298c2ecf20Sopenharmony_ci break; 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX; 16328c2ecf20Sopenharmony_ci if (mid < GET_MAX_VIFS(wil)) { 16338c2ecf20Sopenharmony_ci struct wil6210_vif *vif = wil->vifs[mid]; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci if (vif->wdev.iftype == NL80211_IFTYPE_STATION && 16368c2ecf20Sopenharmony_ci p->status == wil_sta_connected) 16378c2ecf20Sopenharmony_ci sta_connected = true; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci /* print roam counter only for connected stations */ 16408c2ecf20Sopenharmony_ci if (sta_connected) 16418c2ecf20Sopenharmony_ci seq_printf(s, "[%d] %pM connected (roam counter %d) MID %d AID %d\n", 16428c2ecf20Sopenharmony_ci i, p->addr, p->stats.ft_roams, mid, aid); 16438c2ecf20Sopenharmony_ci else 16448c2ecf20Sopenharmony_ci seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, 16458c2ecf20Sopenharmony_ci p->addr, status, mid, aid); 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci if (p->status == wil_sta_connected) { 16488c2ecf20Sopenharmony_ci spin_lock_bh(&p->tid_rx_lock); 16498c2ecf20Sopenharmony_ci for (tid = 0; tid < WIL_STA_TID_NUM; tid++) { 16508c2ecf20Sopenharmony_ci struct wil_tid_ampdu_rx *r = p->tid_rx[tid]; 16518c2ecf20Sopenharmony_ci struct wil_tid_crypto_rx *c = 16528c2ecf20Sopenharmony_ci &p->tid_crypto_rx[tid]; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci if (r) { 16558c2ecf20Sopenharmony_ci seq_printf(s, " [%2d] ", tid); 16568c2ecf20Sopenharmony_ci wil_print_rxtid(s, r); 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci wil_print_rxtid_crypto(s, tid, c); 16608c2ecf20Sopenharmony_ci } 16618c2ecf20Sopenharmony_ci wil_print_rxtid_crypto(s, WIL_STA_TID_NUM, 16628c2ecf20Sopenharmony_ci &p->group_crypto_rx); 16638c2ecf20Sopenharmony_ci spin_unlock_bh(&p->tid_rx_lock); 16648c2ecf20Sopenharmony_ci seq_printf(s, 16658c2ecf20Sopenharmony_ci "Rx invalid frame: non-data %lu, short %lu, large %lu, replay %lu\n", 16668c2ecf20Sopenharmony_ci p->stats.rx_non_data_frame, 16678c2ecf20Sopenharmony_ci p->stats.rx_short_frame, 16688c2ecf20Sopenharmony_ci p->stats.rx_large_frame, 16698c2ecf20Sopenharmony_ci p->stats.rx_replay); 16708c2ecf20Sopenharmony_ci seq_printf(s, 16718c2ecf20Sopenharmony_ci "mic error %lu, key error %lu, amsdu error %lu, csum error %lu\n", 16728c2ecf20Sopenharmony_ci p->stats.rx_mic_error, 16738c2ecf20Sopenharmony_ci p->stats.rx_key_error, 16748c2ecf20Sopenharmony_ci p->stats.rx_amsdu_error, 16758c2ecf20Sopenharmony_ci p->stats.rx_csum_err); 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci seq_puts(s, "Rx/MCS:"); 16788c2ecf20Sopenharmony_ci for (mcs = 0; mcs < ARRAY_SIZE(p->stats.rx_per_mcs); 16798c2ecf20Sopenharmony_ci mcs++) 16808c2ecf20Sopenharmony_ci seq_printf(s, " %lld", 16818c2ecf20Sopenharmony_ci p->stats.rx_per_mcs[mcs]); 16828c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 16838c2ecf20Sopenharmony_ci } 16848c2ecf20Sopenharmony_ci } 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci return 0; 16878c2ecf20Sopenharmony_ci} 16888c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(sta); 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_cistatic int mids_show(struct seq_file *s, void *data) 16918c2ecf20Sopenharmony_ci{ 16928c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 16938c2ecf20Sopenharmony_ci struct wil6210_vif *vif; 16948c2ecf20Sopenharmony_ci struct net_device *ndev; 16958c2ecf20Sopenharmony_ci int i; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci mutex_lock(&wil->vif_mutex); 16988c2ecf20Sopenharmony_ci for (i = 0; i < GET_MAX_VIFS(wil); i++) { 16998c2ecf20Sopenharmony_ci vif = wil->vifs[i]; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci if (vif) { 17028c2ecf20Sopenharmony_ci ndev = vif_to_ndev(vif); 17038c2ecf20Sopenharmony_ci seq_printf(s, "[%d] %pM %s\n", i, ndev->dev_addr, 17048c2ecf20Sopenharmony_ci ndev->name); 17058c2ecf20Sopenharmony_ci } else { 17068c2ecf20Sopenharmony_ci seq_printf(s, "[%d] unused\n", i); 17078c2ecf20Sopenharmony_ci } 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci return 0; 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mids); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_cistatic int wil_tx_latency_debugfs_show(struct seq_file *s, void *data) 17168c2ecf20Sopenharmony_ci__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) 17178c2ecf20Sopenharmony_ci{ 17188c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 17198c2ecf20Sopenharmony_ci int i, bin; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 17228c2ecf20Sopenharmony_ci struct wil_sta_info *p = &wil->sta[i]; 17238c2ecf20Sopenharmony_ci char *status = "unknown"; 17248c2ecf20Sopenharmony_ci u8 aid = 0; 17258c2ecf20Sopenharmony_ci u8 mid; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci if (!p->tx_latency_bins) 17288c2ecf20Sopenharmony_ci continue; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci switch (p->status) { 17318c2ecf20Sopenharmony_ci case wil_sta_unused: 17328c2ecf20Sopenharmony_ci status = "unused "; 17338c2ecf20Sopenharmony_ci break; 17348c2ecf20Sopenharmony_ci case wil_sta_conn_pending: 17358c2ecf20Sopenharmony_ci status = "pending "; 17368c2ecf20Sopenharmony_ci break; 17378c2ecf20Sopenharmony_ci case wil_sta_connected: 17388c2ecf20Sopenharmony_ci status = "connected"; 17398c2ecf20Sopenharmony_ci aid = p->aid; 17408c2ecf20Sopenharmony_ci break; 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX; 17438c2ecf20Sopenharmony_ci seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, p->addr, status, 17448c2ecf20Sopenharmony_ci mid, aid); 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci if (p->status == wil_sta_connected) { 17478c2ecf20Sopenharmony_ci u64 num_packets = 0; 17488c2ecf20Sopenharmony_ci u64 tx_latency_avg = p->stats.tx_latency_total_us; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci seq_puts(s, "Tx/Latency bin:"); 17518c2ecf20Sopenharmony_ci for (bin = 0; bin < WIL_NUM_LATENCY_BINS; bin++) { 17528c2ecf20Sopenharmony_ci seq_printf(s, " %lld", 17538c2ecf20Sopenharmony_ci p->tx_latency_bins[bin]); 17548c2ecf20Sopenharmony_ci num_packets += p->tx_latency_bins[bin]; 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 17578c2ecf20Sopenharmony_ci if (!num_packets) 17588c2ecf20Sopenharmony_ci continue; 17598c2ecf20Sopenharmony_ci do_div(tx_latency_avg, num_packets); 17608c2ecf20Sopenharmony_ci seq_printf(s, "Tx/Latency min/avg/max (us): %d/%lld/%d", 17618c2ecf20Sopenharmony_ci p->stats.tx_latency_min_us, 17628c2ecf20Sopenharmony_ci tx_latency_avg, 17638c2ecf20Sopenharmony_ci p->stats.tx_latency_max_us); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci } 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci return 0; 17708c2ecf20Sopenharmony_ci} 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_cistatic int wil_tx_latency_seq_open(struct inode *inode, struct file *file) 17738c2ecf20Sopenharmony_ci{ 17748c2ecf20Sopenharmony_ci return single_open(file, wil_tx_latency_debugfs_show, 17758c2ecf20Sopenharmony_ci inode->i_private); 17768c2ecf20Sopenharmony_ci} 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_cistatic ssize_t wil_tx_latency_write(struct file *file, const char __user *buf, 17798c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 17808c2ecf20Sopenharmony_ci{ 17818c2ecf20Sopenharmony_ci struct seq_file *s = file->private_data; 17828c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 17838c2ecf20Sopenharmony_ci int val, rc, i; 17848c2ecf20Sopenharmony_ci bool enable; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci rc = kstrtoint_from_user(buf, len, 0, &val); 17878c2ecf20Sopenharmony_ci if (rc) { 17888c2ecf20Sopenharmony_ci wil_err(wil, "Invalid argument\n"); 17898c2ecf20Sopenharmony_ci return rc; 17908c2ecf20Sopenharmony_ci } 17918c2ecf20Sopenharmony_ci if (val == 1) 17928c2ecf20Sopenharmony_ci /* default resolution */ 17938c2ecf20Sopenharmony_ci val = 500; 17948c2ecf20Sopenharmony_ci if (val && (val < 50 || val > 1000)) { 17958c2ecf20Sopenharmony_ci wil_err(wil, "Invalid resolution %d\n", val); 17968c2ecf20Sopenharmony_ci return -EINVAL; 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci enable = !!val; 18008c2ecf20Sopenharmony_ci if (wil->tx_latency == enable) 18018c2ecf20Sopenharmony_ci return len; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci wil_info(wil, "%s TX latency measurements (resolution %dusec)\n", 18048c2ecf20Sopenharmony_ci enable ? "Enabling" : "Disabling", val); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci if (enable) { 18078c2ecf20Sopenharmony_ci size_t sz = sizeof(u64) * WIL_NUM_LATENCY_BINS; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci wil->tx_latency_res = val; 18108c2ecf20Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 18118c2ecf20Sopenharmony_ci struct wil_sta_info *sta = &wil->sta[i]; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci kfree(sta->tx_latency_bins); 18148c2ecf20Sopenharmony_ci sta->tx_latency_bins = kzalloc(sz, GFP_KERNEL); 18158c2ecf20Sopenharmony_ci if (!sta->tx_latency_bins) 18168c2ecf20Sopenharmony_ci return -ENOMEM; 18178c2ecf20Sopenharmony_ci sta->stats.tx_latency_min_us = U32_MAX; 18188c2ecf20Sopenharmony_ci sta->stats.tx_latency_max_us = 0; 18198c2ecf20Sopenharmony_ci sta->stats.tx_latency_total_us = 0; 18208c2ecf20Sopenharmony_ci } 18218c2ecf20Sopenharmony_ci } 18228c2ecf20Sopenharmony_ci wil->tx_latency = enable; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci return len; 18258c2ecf20Sopenharmony_ci} 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_cistatic const struct file_operations fops_tx_latency = { 18288c2ecf20Sopenharmony_ci .open = wil_tx_latency_seq_open, 18298c2ecf20Sopenharmony_ci .release = single_release, 18308c2ecf20Sopenharmony_ci .read = seq_read, 18318c2ecf20Sopenharmony_ci .write = wil_tx_latency_write, 18328c2ecf20Sopenharmony_ci .llseek = seq_lseek, 18338c2ecf20Sopenharmony_ci}; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_cistatic void wil_link_stats_print_basic(struct wil6210_vif *vif, 18368c2ecf20Sopenharmony_ci struct seq_file *s, 18378c2ecf20Sopenharmony_ci struct wmi_link_stats_basic *basic) 18388c2ecf20Sopenharmony_ci{ 18398c2ecf20Sopenharmony_ci char per[5] = "?"; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci if (basic->per_average != 0xff) 18428c2ecf20Sopenharmony_ci snprintf(per, sizeof(per), "%d%%", basic->per_average); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci seq_printf(s, "CID %d {\n" 18458c2ecf20Sopenharmony_ci "\tTxMCS %d TxTpt %d\n" 18468c2ecf20Sopenharmony_ci "\tGoodput(rx:tx) %d:%d\n" 18478c2ecf20Sopenharmony_ci "\tRxBcastFrames %d\n" 18488c2ecf20Sopenharmony_ci "\tRSSI %d SQI %d SNR %d PER %s\n" 18498c2ecf20Sopenharmony_ci "\tRx RFC %d Ant num %d\n" 18508c2ecf20Sopenharmony_ci "\tSectors(rx:tx) my %d:%d peer %d:%d\n" 18518c2ecf20Sopenharmony_ci "}\n", 18528c2ecf20Sopenharmony_ci basic->cid, 18538c2ecf20Sopenharmony_ci basic->bf_mcs, le32_to_cpu(basic->tx_tpt), 18548c2ecf20Sopenharmony_ci le32_to_cpu(basic->rx_goodput), 18558c2ecf20Sopenharmony_ci le32_to_cpu(basic->tx_goodput), 18568c2ecf20Sopenharmony_ci le32_to_cpu(basic->rx_bcast_frames), 18578c2ecf20Sopenharmony_ci basic->rssi, basic->sqi, basic->snr, per, 18588c2ecf20Sopenharmony_ci basic->selected_rfc, basic->rx_effective_ant_num, 18598c2ecf20Sopenharmony_ci basic->my_rx_sector, basic->my_tx_sector, 18608c2ecf20Sopenharmony_ci basic->other_rx_sector, basic->other_tx_sector); 18618c2ecf20Sopenharmony_ci} 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_cistatic void wil_link_stats_print_global(struct wil6210_priv *wil, 18648c2ecf20Sopenharmony_ci struct seq_file *s, 18658c2ecf20Sopenharmony_ci struct wmi_link_stats_global *global) 18668c2ecf20Sopenharmony_ci{ 18678c2ecf20Sopenharmony_ci seq_printf(s, "Frames(rx:tx) %d:%d\n" 18688c2ecf20Sopenharmony_ci "BA Frames(rx:tx) %d:%d\n" 18698c2ecf20Sopenharmony_ci "Beacons %d\n" 18708c2ecf20Sopenharmony_ci "Rx Errors (MIC:CRC) %d:%d\n" 18718c2ecf20Sopenharmony_ci "Tx Errors (no ack) %d\n", 18728c2ecf20Sopenharmony_ci le32_to_cpu(global->rx_frames), 18738c2ecf20Sopenharmony_ci le32_to_cpu(global->tx_frames), 18748c2ecf20Sopenharmony_ci le32_to_cpu(global->rx_ba_frames), 18758c2ecf20Sopenharmony_ci le32_to_cpu(global->tx_ba_frames), 18768c2ecf20Sopenharmony_ci le32_to_cpu(global->tx_beacons), 18778c2ecf20Sopenharmony_ci le32_to_cpu(global->rx_mic_errors), 18788c2ecf20Sopenharmony_ci le32_to_cpu(global->rx_crc_errors), 18798c2ecf20Sopenharmony_ci le32_to_cpu(global->tx_fail_no_ack)); 18808c2ecf20Sopenharmony_ci} 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_cistatic void wil_link_stats_debugfs_show_vif(struct wil6210_vif *vif, 18838c2ecf20Sopenharmony_ci struct seq_file *s) 18848c2ecf20Sopenharmony_ci{ 18858c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 18868c2ecf20Sopenharmony_ci struct wmi_link_stats_basic *stats; 18878c2ecf20Sopenharmony_ci int i; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci if (!vif->fw_stats_ready) { 18908c2ecf20Sopenharmony_ci seq_puts(s, "no statistics\n"); 18918c2ecf20Sopenharmony_ci return; 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci seq_printf(s, "TSF %lld\n", vif->fw_stats_tsf); 18958c2ecf20Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 18968c2ecf20Sopenharmony_ci if (wil->sta[i].status == wil_sta_unused) 18978c2ecf20Sopenharmony_ci continue; 18988c2ecf20Sopenharmony_ci if (wil->sta[i].mid != vif->mid) 18998c2ecf20Sopenharmony_ci continue; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci stats = &wil->sta[i].fw_stats_basic; 19028c2ecf20Sopenharmony_ci wil_link_stats_print_basic(vif, s, stats); 19038c2ecf20Sopenharmony_ci } 19048c2ecf20Sopenharmony_ci} 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_cistatic int wil_link_stats_debugfs_show(struct seq_file *s, void *data) 19078c2ecf20Sopenharmony_ci{ 19088c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 19098c2ecf20Sopenharmony_ci struct wil6210_vif *vif; 19108c2ecf20Sopenharmony_ci int i, rc; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci rc = mutex_lock_interruptible(&wil->vif_mutex); 19138c2ecf20Sopenharmony_ci if (rc) 19148c2ecf20Sopenharmony_ci return rc; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci /* iterate over all MIDs and show per-cid statistics. Then show the 19178c2ecf20Sopenharmony_ci * global statistics 19188c2ecf20Sopenharmony_ci */ 19198c2ecf20Sopenharmony_ci for (i = 0; i < GET_MAX_VIFS(wil); i++) { 19208c2ecf20Sopenharmony_ci vif = wil->vifs[i]; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci seq_printf(s, "MID %d ", i); 19238c2ecf20Sopenharmony_ci if (!vif) { 19248c2ecf20Sopenharmony_ci seq_puts(s, "unused\n"); 19258c2ecf20Sopenharmony_ci continue; 19268c2ecf20Sopenharmony_ci } 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci wil_link_stats_debugfs_show_vif(vif, s); 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci return 0; 19348c2ecf20Sopenharmony_ci} 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_cistatic int wil_link_stats_seq_open(struct inode *inode, struct file *file) 19378c2ecf20Sopenharmony_ci{ 19388c2ecf20Sopenharmony_ci return single_open(file, wil_link_stats_debugfs_show, inode->i_private); 19398c2ecf20Sopenharmony_ci} 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_cistatic ssize_t wil_link_stats_write(struct file *file, const char __user *buf, 19428c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 19438c2ecf20Sopenharmony_ci{ 19448c2ecf20Sopenharmony_ci struct seq_file *s = file->private_data; 19458c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 19468c2ecf20Sopenharmony_ci int cid, interval, rc, i; 19478c2ecf20Sopenharmony_ci struct wil6210_vif *vif; 19488c2ecf20Sopenharmony_ci char *kbuf = kmalloc(len + 1, GFP_KERNEL); 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci if (!kbuf) 19518c2ecf20Sopenharmony_ci return -ENOMEM; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); 19548c2ecf20Sopenharmony_ci if (rc != len) { 19558c2ecf20Sopenharmony_ci kfree(kbuf); 19568c2ecf20Sopenharmony_ci return rc >= 0 ? -EIO : rc; 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci kbuf[len] = '\0'; 19608c2ecf20Sopenharmony_ci /* specify cid (use -1 for all cids) and snapshot interval in ms */ 19618c2ecf20Sopenharmony_ci rc = sscanf(kbuf, "%d %d", &cid, &interval); 19628c2ecf20Sopenharmony_ci kfree(kbuf); 19638c2ecf20Sopenharmony_ci if (rc < 0) 19648c2ecf20Sopenharmony_ci return rc; 19658c2ecf20Sopenharmony_ci if (rc < 2 || interval < 0) 19668c2ecf20Sopenharmony_ci return -EINVAL; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci wil_info(wil, "request link statistics, cid %d interval %d\n", 19698c2ecf20Sopenharmony_ci cid, interval); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci rc = mutex_lock_interruptible(&wil->vif_mutex); 19728c2ecf20Sopenharmony_ci if (rc) 19738c2ecf20Sopenharmony_ci return rc; 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci for (i = 0; i < GET_MAX_VIFS(wil); i++) { 19768c2ecf20Sopenharmony_ci vif = wil->vifs[i]; 19778c2ecf20Sopenharmony_ci if (!vif) 19788c2ecf20Sopenharmony_ci continue; 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_BASIC, 19818c2ecf20Sopenharmony_ci (cid == -1 ? 0xff : cid), interval); 19828c2ecf20Sopenharmony_ci if (rc) 19838c2ecf20Sopenharmony_ci wil_err(wil, "link statistics failed for mid %d\n", i); 19848c2ecf20Sopenharmony_ci } 19858c2ecf20Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci return len; 19888c2ecf20Sopenharmony_ci} 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_cistatic const struct file_operations fops_link_stats = { 19918c2ecf20Sopenharmony_ci .open = wil_link_stats_seq_open, 19928c2ecf20Sopenharmony_ci .release = single_release, 19938c2ecf20Sopenharmony_ci .read = seq_read, 19948c2ecf20Sopenharmony_ci .write = wil_link_stats_write, 19958c2ecf20Sopenharmony_ci .llseek = seq_lseek, 19968c2ecf20Sopenharmony_ci}; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_cistatic int 19998c2ecf20Sopenharmony_ciwil_link_stats_global_debugfs_show(struct seq_file *s, void *data) 20008c2ecf20Sopenharmony_ci{ 20018c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci if (!wil->fw_stats_global.ready) 20048c2ecf20Sopenharmony_ci return 0; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci seq_printf(s, "TSF %lld\n", wil->fw_stats_global.tsf); 20078c2ecf20Sopenharmony_ci wil_link_stats_print_global(wil, s, &wil->fw_stats_global.stats); 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci return 0; 20108c2ecf20Sopenharmony_ci} 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_cistatic int 20138c2ecf20Sopenharmony_ciwil_link_stats_global_seq_open(struct inode *inode, struct file *file) 20148c2ecf20Sopenharmony_ci{ 20158c2ecf20Sopenharmony_ci return single_open(file, wil_link_stats_global_debugfs_show, 20168c2ecf20Sopenharmony_ci inode->i_private); 20178c2ecf20Sopenharmony_ci} 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_cistatic ssize_t 20208c2ecf20Sopenharmony_ciwil_link_stats_global_write(struct file *file, const char __user *buf, 20218c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 20228c2ecf20Sopenharmony_ci{ 20238c2ecf20Sopenharmony_ci struct seq_file *s = file->private_data; 20248c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 20258c2ecf20Sopenharmony_ci int interval, rc; 20268c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci /* specify snapshot interval in ms */ 20298c2ecf20Sopenharmony_ci rc = kstrtoint_from_user(buf, len, 0, &interval); 20308c2ecf20Sopenharmony_ci if (rc || interval < 0) { 20318c2ecf20Sopenharmony_ci wil_err(wil, "Invalid argument\n"); 20328c2ecf20Sopenharmony_ci return -EINVAL; 20338c2ecf20Sopenharmony_ci } 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci wil_info(wil, "request global link stats, interval %d\n", interval); 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_GLOBAL, 0, interval); 20388c2ecf20Sopenharmony_ci if (rc) 20398c2ecf20Sopenharmony_ci wil_err(wil, "global link stats failed %d\n", rc); 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci return rc ? rc : len; 20428c2ecf20Sopenharmony_ci} 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_cistatic const struct file_operations fops_link_stats_global = { 20458c2ecf20Sopenharmony_ci .open = wil_link_stats_global_seq_open, 20468c2ecf20Sopenharmony_ci .release = single_release, 20478c2ecf20Sopenharmony_ci .read = seq_read, 20488c2ecf20Sopenharmony_ci .write = wil_link_stats_global_write, 20498c2ecf20Sopenharmony_ci .llseek = seq_lseek, 20508c2ecf20Sopenharmony_ci}; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_cistatic ssize_t wil_read_file_led_cfg(struct file *file, char __user *user_buf, 20538c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 20548c2ecf20Sopenharmony_ci{ 20558c2ecf20Sopenharmony_ci char buf[80]; 20568c2ecf20Sopenharmony_ci int n; 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci n = snprintf(buf, sizeof(buf), 20598c2ecf20Sopenharmony_ci "led_id is set to %d, echo 1 to enable, 0 to disable\n", 20608c2ecf20Sopenharmony_ci led_id); 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci n = min_t(int, n, sizeof(buf)); 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, 20658c2ecf20Sopenharmony_ci buf, n); 20668c2ecf20Sopenharmony_ci} 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_cistatic ssize_t wil_write_file_led_cfg(struct file *file, 20698c2ecf20Sopenharmony_ci const char __user *buf_, 20708c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 20718c2ecf20Sopenharmony_ci{ 20728c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 20738c2ecf20Sopenharmony_ci int val; 20748c2ecf20Sopenharmony_ci int rc; 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci rc = kstrtoint_from_user(buf_, count, 0, &val); 20778c2ecf20Sopenharmony_ci if (rc) { 20788c2ecf20Sopenharmony_ci wil_err(wil, "Invalid argument\n"); 20798c2ecf20Sopenharmony_ci return rc; 20808c2ecf20Sopenharmony_ci } 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci wil_info(wil, "%s led %d\n", val ? "Enabling" : "Disabling", led_id); 20838c2ecf20Sopenharmony_ci rc = wmi_led_cfg(wil, val); 20848c2ecf20Sopenharmony_ci if (rc) { 20858c2ecf20Sopenharmony_ci wil_info(wil, "%s led %d failed\n", 20868c2ecf20Sopenharmony_ci val ? "Enabling" : "Disabling", led_id); 20878c2ecf20Sopenharmony_ci return rc; 20888c2ecf20Sopenharmony_ci } 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci return count; 20918c2ecf20Sopenharmony_ci} 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_cistatic const struct file_operations fops_led_cfg = { 20948c2ecf20Sopenharmony_ci .read = wil_read_file_led_cfg, 20958c2ecf20Sopenharmony_ci .write = wil_write_file_led_cfg, 20968c2ecf20Sopenharmony_ci .open = simple_open, 20978c2ecf20Sopenharmony_ci}; 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci/* led_blink_time, write: 21008c2ecf20Sopenharmony_ci * "<blink_on_slow> <blink_off_slow> <blink_on_med> <blink_off_med> <blink_on_fast> <blink_off_fast> 21018c2ecf20Sopenharmony_ci */ 21028c2ecf20Sopenharmony_cistatic ssize_t wil_write_led_blink_time(struct file *file, 21038c2ecf20Sopenharmony_ci const char __user *buf, 21048c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 21058c2ecf20Sopenharmony_ci{ 21068c2ecf20Sopenharmony_ci int rc; 21078c2ecf20Sopenharmony_ci char *kbuf = kmalloc(len + 1, GFP_KERNEL); 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_ci if (!kbuf) 21108c2ecf20Sopenharmony_ci return -ENOMEM; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); 21138c2ecf20Sopenharmony_ci if (rc != len) { 21148c2ecf20Sopenharmony_ci kfree(kbuf); 21158c2ecf20Sopenharmony_ci return rc >= 0 ? -EIO : rc; 21168c2ecf20Sopenharmony_ci } 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci kbuf[len] = '\0'; 21198c2ecf20Sopenharmony_ci rc = sscanf(kbuf, "%d %d %d %d %d %d", 21208c2ecf20Sopenharmony_ci &led_blink_time[WIL_LED_TIME_SLOW].on_ms, 21218c2ecf20Sopenharmony_ci &led_blink_time[WIL_LED_TIME_SLOW].off_ms, 21228c2ecf20Sopenharmony_ci &led_blink_time[WIL_LED_TIME_MED].on_ms, 21238c2ecf20Sopenharmony_ci &led_blink_time[WIL_LED_TIME_MED].off_ms, 21248c2ecf20Sopenharmony_ci &led_blink_time[WIL_LED_TIME_FAST].on_ms, 21258c2ecf20Sopenharmony_ci &led_blink_time[WIL_LED_TIME_FAST].off_ms); 21268c2ecf20Sopenharmony_ci kfree(kbuf); 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci if (rc < 0) 21298c2ecf20Sopenharmony_ci return rc; 21308c2ecf20Sopenharmony_ci if (rc < 6) 21318c2ecf20Sopenharmony_ci return -EINVAL; 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci return len; 21348c2ecf20Sopenharmony_ci} 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_cistatic ssize_t wil_read_led_blink_time(struct file *file, char __user *user_buf, 21378c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 21388c2ecf20Sopenharmony_ci{ 21398c2ecf20Sopenharmony_ci static char text[400]; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci snprintf(text, sizeof(text), 21428c2ecf20Sopenharmony_ci "To set led blink on/off time variables write:\n" 21438c2ecf20Sopenharmony_ci "<blink_on_slow> <blink_off_slow> <blink_on_med> " 21448c2ecf20Sopenharmony_ci "<blink_off_med> <blink_on_fast> <blink_off_fast>\n" 21458c2ecf20Sopenharmony_ci "The current values are:\n" 21468c2ecf20Sopenharmony_ci "%d %d %d %d %d %d\n", 21478c2ecf20Sopenharmony_ci led_blink_time[WIL_LED_TIME_SLOW].on_ms, 21488c2ecf20Sopenharmony_ci led_blink_time[WIL_LED_TIME_SLOW].off_ms, 21498c2ecf20Sopenharmony_ci led_blink_time[WIL_LED_TIME_MED].on_ms, 21508c2ecf20Sopenharmony_ci led_blink_time[WIL_LED_TIME_MED].off_ms, 21518c2ecf20Sopenharmony_ci led_blink_time[WIL_LED_TIME_FAST].on_ms, 21528c2ecf20Sopenharmony_ci led_blink_time[WIL_LED_TIME_FAST].off_ms); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, text, 21558c2ecf20Sopenharmony_ci sizeof(text)); 21568c2ecf20Sopenharmony_ci} 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_cistatic const struct file_operations fops_led_blink_time = { 21598c2ecf20Sopenharmony_ci .read = wil_read_led_blink_time, 21608c2ecf20Sopenharmony_ci .write = wil_write_led_blink_time, 21618c2ecf20Sopenharmony_ci .open = simple_open, 21628c2ecf20Sopenharmony_ci}; 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci/*---------FW capabilities------------*/ 21658c2ecf20Sopenharmony_cistatic int wil_fw_capabilities_debugfs_show(struct seq_file *s, void *data) 21668c2ecf20Sopenharmony_ci{ 21678c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci seq_printf(s, "fw_capabilities : %*pb\n", WMI_FW_CAPABILITY_MAX, 21708c2ecf20Sopenharmony_ci wil->fw_capabilities); 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci return 0; 21738c2ecf20Sopenharmony_ci} 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_cistatic int wil_fw_capabilities_seq_open(struct inode *inode, struct file *file) 21768c2ecf20Sopenharmony_ci{ 21778c2ecf20Sopenharmony_ci return single_open(file, wil_fw_capabilities_debugfs_show, 21788c2ecf20Sopenharmony_ci inode->i_private); 21798c2ecf20Sopenharmony_ci} 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_cistatic const struct file_operations fops_fw_capabilities = { 21828c2ecf20Sopenharmony_ci .open = wil_fw_capabilities_seq_open, 21838c2ecf20Sopenharmony_ci .release = single_release, 21848c2ecf20Sopenharmony_ci .read = seq_read, 21858c2ecf20Sopenharmony_ci .llseek = seq_lseek, 21868c2ecf20Sopenharmony_ci}; 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci/*---------FW version------------*/ 21898c2ecf20Sopenharmony_cistatic int wil_fw_version_debugfs_show(struct seq_file *s, void *data) 21908c2ecf20Sopenharmony_ci{ 21918c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci if (wil->fw_version[0]) 21948c2ecf20Sopenharmony_ci seq_printf(s, "%s\n", wil->fw_version); 21958c2ecf20Sopenharmony_ci else 21968c2ecf20Sopenharmony_ci seq_puts(s, "N/A\n"); 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci return 0; 21998c2ecf20Sopenharmony_ci} 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_cistatic int wil_fw_version_seq_open(struct inode *inode, struct file *file) 22028c2ecf20Sopenharmony_ci{ 22038c2ecf20Sopenharmony_ci return single_open(file, wil_fw_version_debugfs_show, 22048c2ecf20Sopenharmony_ci inode->i_private); 22058c2ecf20Sopenharmony_ci} 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_cistatic const struct file_operations fops_fw_version = { 22088c2ecf20Sopenharmony_ci .open = wil_fw_version_seq_open, 22098c2ecf20Sopenharmony_ci .release = single_release, 22108c2ecf20Sopenharmony_ci .read = seq_read, 22118c2ecf20Sopenharmony_ci .llseek = seq_lseek, 22128c2ecf20Sopenharmony_ci}; 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci/*---------suspend_stats---------*/ 22158c2ecf20Sopenharmony_cistatic ssize_t wil_write_suspend_stats(struct file *file, 22168c2ecf20Sopenharmony_ci const char __user *buf, 22178c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 22188c2ecf20Sopenharmony_ci{ 22198c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats)); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci return len; 22248c2ecf20Sopenharmony_ci} 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_cistatic ssize_t wil_read_suspend_stats(struct file *file, 22278c2ecf20Sopenharmony_ci char __user *user_buf, 22288c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 22298c2ecf20Sopenharmony_ci{ 22308c2ecf20Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 22318c2ecf20Sopenharmony_ci char *text; 22328c2ecf20Sopenharmony_ci int n, ret, text_size = 500; 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci text = kmalloc(text_size, GFP_KERNEL); 22358c2ecf20Sopenharmony_ci if (!text) 22368c2ecf20Sopenharmony_ci return -ENOMEM; 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci n = snprintf(text, text_size, 22398c2ecf20Sopenharmony_ci "Radio on suspend statistics:\n" 22408c2ecf20Sopenharmony_ci "successful suspends:%ld failed suspends:%ld\n" 22418c2ecf20Sopenharmony_ci "successful resumes:%ld failed resumes:%ld\n" 22428c2ecf20Sopenharmony_ci "rejected by device:%ld\n" 22438c2ecf20Sopenharmony_ci "Radio off suspend statistics:\n" 22448c2ecf20Sopenharmony_ci "successful suspends:%ld failed suspends:%ld\n" 22458c2ecf20Sopenharmony_ci "successful resumes:%ld failed resumes:%ld\n" 22468c2ecf20Sopenharmony_ci "General statistics:\n" 22478c2ecf20Sopenharmony_ci "rejected by host:%ld\n", 22488c2ecf20Sopenharmony_ci wil->suspend_stats.r_on.successful_suspends, 22498c2ecf20Sopenharmony_ci wil->suspend_stats.r_on.failed_suspends, 22508c2ecf20Sopenharmony_ci wil->suspend_stats.r_on.successful_resumes, 22518c2ecf20Sopenharmony_ci wil->suspend_stats.r_on.failed_resumes, 22528c2ecf20Sopenharmony_ci wil->suspend_stats.rejected_by_device, 22538c2ecf20Sopenharmony_ci wil->suspend_stats.r_off.successful_suspends, 22548c2ecf20Sopenharmony_ci wil->suspend_stats.r_off.failed_suspends, 22558c2ecf20Sopenharmony_ci wil->suspend_stats.r_off.successful_resumes, 22568c2ecf20Sopenharmony_ci wil->suspend_stats.r_off.failed_resumes, 22578c2ecf20Sopenharmony_ci wil->suspend_stats.rejected_by_host); 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci n = min_t(int, n, text_size); 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, text, n); 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci kfree(text); 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci return ret; 22668c2ecf20Sopenharmony_ci} 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_cistatic const struct file_operations fops_suspend_stats = { 22698c2ecf20Sopenharmony_ci .read = wil_read_suspend_stats, 22708c2ecf20Sopenharmony_ci .write = wil_write_suspend_stats, 22718c2ecf20Sopenharmony_ci .open = simple_open, 22728c2ecf20Sopenharmony_ci}; 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci/*---------compressed_rx_status---------*/ 22758c2ecf20Sopenharmony_cistatic ssize_t wil_compressed_rx_status_write(struct file *file, 22768c2ecf20Sopenharmony_ci const char __user *buf, 22778c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 22788c2ecf20Sopenharmony_ci{ 22798c2ecf20Sopenharmony_ci struct seq_file *s = file->private_data; 22808c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 22818c2ecf20Sopenharmony_ci int compressed_rx_status; 22828c2ecf20Sopenharmony_ci int rc; 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci rc = kstrtoint_from_user(buf, len, 0, &compressed_rx_status); 22858c2ecf20Sopenharmony_ci if (rc) { 22868c2ecf20Sopenharmony_ci wil_err(wil, "Invalid argument\n"); 22878c2ecf20Sopenharmony_ci return rc; 22888c2ecf20Sopenharmony_ci } 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci if (wil_has_active_ifaces(wil, true, false)) { 22918c2ecf20Sopenharmony_ci wil_err(wil, "cannot change edma config after iface is up\n"); 22928c2ecf20Sopenharmony_ci return -EPERM; 22938c2ecf20Sopenharmony_ci } 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci wil_info(wil, "%sable compressed_rx_status\n", 22968c2ecf20Sopenharmony_ci compressed_rx_status ? "En" : "Dis"); 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci wil->use_compressed_rx_status = compressed_rx_status; 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci return len; 23018c2ecf20Sopenharmony_ci} 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_cistatic int 23048c2ecf20Sopenharmony_ciwil_compressed_rx_status_show(struct seq_file *s, void *data) 23058c2ecf20Sopenharmony_ci{ 23068c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci seq_printf(s, "%d\n", wil->use_compressed_rx_status); 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci return 0; 23118c2ecf20Sopenharmony_ci} 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_cistatic int 23148c2ecf20Sopenharmony_ciwil_compressed_rx_status_seq_open(struct inode *inode, struct file *file) 23158c2ecf20Sopenharmony_ci{ 23168c2ecf20Sopenharmony_ci return single_open(file, wil_compressed_rx_status_show, 23178c2ecf20Sopenharmony_ci inode->i_private); 23188c2ecf20Sopenharmony_ci} 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_cistatic const struct file_operations fops_compressed_rx_status = { 23218c2ecf20Sopenharmony_ci .open = wil_compressed_rx_status_seq_open, 23228c2ecf20Sopenharmony_ci .release = single_release, 23238c2ecf20Sopenharmony_ci .read = seq_read, 23248c2ecf20Sopenharmony_ci .write = wil_compressed_rx_status_write, 23258c2ecf20Sopenharmony_ci .llseek = seq_lseek, 23268c2ecf20Sopenharmony_ci}; 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci/*----------------*/ 23298c2ecf20Sopenharmony_cistatic void wil6210_debugfs_init_blobs(struct wil6210_priv *wil, 23308c2ecf20Sopenharmony_ci struct dentry *dbg) 23318c2ecf20Sopenharmony_ci{ 23328c2ecf20Sopenharmony_ci int i; 23338c2ecf20Sopenharmony_ci char name[32]; 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { 23368c2ecf20Sopenharmony_ci struct wil_blob_wrapper *wil_blob = &wil->blobs[i]; 23378c2ecf20Sopenharmony_ci struct debugfs_blob_wrapper *blob = &wil_blob->blob; 23388c2ecf20Sopenharmony_ci const struct fw_map *map = &fw_mapping[i]; 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci if (!map->name) 23418c2ecf20Sopenharmony_ci continue; 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_ci wil_blob->wil = wil; 23448c2ecf20Sopenharmony_ci blob->data = (void * __force)wil->csr + HOSTADDR(map->host); 23458c2ecf20Sopenharmony_ci blob->size = map->to - map->from; 23468c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "blob_%s", map->name); 23478c2ecf20Sopenharmony_ci wil_debugfs_create_ioblob(name, 0444, dbg, wil_blob); 23488c2ecf20Sopenharmony_ci } 23498c2ecf20Sopenharmony_ci} 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci/* misc files */ 23528c2ecf20Sopenharmony_cistatic const struct { 23538c2ecf20Sopenharmony_ci const char *name; 23548c2ecf20Sopenharmony_ci umode_t mode; 23558c2ecf20Sopenharmony_ci const struct file_operations *fops; 23568c2ecf20Sopenharmony_ci} dbg_files[] = { 23578c2ecf20Sopenharmony_ci {"mbox", 0444, &mbox_fops}, 23588c2ecf20Sopenharmony_ci {"rings", 0444, &ring_fops}, 23598c2ecf20Sopenharmony_ci {"stations", 0444, &sta_fops}, 23608c2ecf20Sopenharmony_ci {"mids", 0444, &mids_fops}, 23618c2ecf20Sopenharmony_ci {"desc", 0444, &txdesc_fops}, 23628c2ecf20Sopenharmony_ci {"bf", 0444, &bf_fops}, 23638c2ecf20Sopenharmony_ci {"mem_val", 0644, &memread_fops}, 23648c2ecf20Sopenharmony_ci {"rxon", 0244, &fops_rxon}, 23658c2ecf20Sopenharmony_ci {"tx_mgmt", 0244, &fops_txmgmt}, 23668c2ecf20Sopenharmony_ci {"wmi_send", 0244, &fops_wmi}, 23678c2ecf20Sopenharmony_ci {"back", 0644, &fops_back}, 23688c2ecf20Sopenharmony_ci {"pmccfg", 0644, &fops_pmccfg}, 23698c2ecf20Sopenharmony_ci {"pmcdata", 0444, &fops_pmcdata}, 23708c2ecf20Sopenharmony_ci {"pmcring", 0444, &fops_pmcring}, 23718c2ecf20Sopenharmony_ci {"temp", 0444, &temp_fops}, 23728c2ecf20Sopenharmony_ci {"freq", 0444, &freq_fops}, 23738c2ecf20Sopenharmony_ci {"link", 0444, &link_fops}, 23748c2ecf20Sopenharmony_ci {"info", 0444, &info_fops}, 23758c2ecf20Sopenharmony_ci {"recovery", 0644, &fops_recovery}, 23768c2ecf20Sopenharmony_ci {"led_cfg", 0644, &fops_led_cfg}, 23778c2ecf20Sopenharmony_ci {"led_blink_time", 0644, &fops_led_blink_time}, 23788c2ecf20Sopenharmony_ci {"fw_capabilities", 0444, &fops_fw_capabilities}, 23798c2ecf20Sopenharmony_ci {"fw_version", 0444, &fops_fw_version}, 23808c2ecf20Sopenharmony_ci {"suspend_stats", 0644, &fops_suspend_stats}, 23818c2ecf20Sopenharmony_ci {"compressed_rx_status", 0644, &fops_compressed_rx_status}, 23828c2ecf20Sopenharmony_ci {"srings", 0444, &srings_fops}, 23838c2ecf20Sopenharmony_ci {"status_msg", 0444, &status_msg_fops}, 23848c2ecf20Sopenharmony_ci {"rx_buff_mgmt", 0444, &rx_buff_mgmt_fops}, 23858c2ecf20Sopenharmony_ci {"tx_latency", 0644, &fops_tx_latency}, 23868c2ecf20Sopenharmony_ci {"link_stats", 0644, &fops_link_stats}, 23878c2ecf20Sopenharmony_ci {"link_stats_global", 0644, &fops_link_stats_global}, 23888c2ecf20Sopenharmony_ci {"rbufcap", 0244, &fops_rbufcap}, 23898c2ecf20Sopenharmony_ci}; 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_cistatic void wil6210_debugfs_init_files(struct wil6210_priv *wil, 23928c2ecf20Sopenharmony_ci struct dentry *dbg) 23938c2ecf20Sopenharmony_ci{ 23948c2ecf20Sopenharmony_ci int i; 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dbg_files); i++) 23978c2ecf20Sopenharmony_ci debugfs_create_file(dbg_files[i].name, dbg_files[i].mode, dbg, 23988c2ecf20Sopenharmony_ci wil, dbg_files[i].fops); 23998c2ecf20Sopenharmony_ci} 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci/* interrupt control blocks */ 24028c2ecf20Sopenharmony_cistatic const struct { 24038c2ecf20Sopenharmony_ci const char *name; 24048c2ecf20Sopenharmony_ci u32 icr_off; 24058c2ecf20Sopenharmony_ci} dbg_icr[] = { 24068c2ecf20Sopenharmony_ci {"USER_ICR", HOSTADDR(RGF_USER_USER_ICR)}, 24078c2ecf20Sopenharmony_ci {"DMA_EP_TX_ICR", HOSTADDR(RGF_DMA_EP_TX_ICR)}, 24088c2ecf20Sopenharmony_ci {"DMA_EP_RX_ICR", HOSTADDR(RGF_DMA_EP_RX_ICR)}, 24098c2ecf20Sopenharmony_ci {"DMA_EP_MISC_ICR", HOSTADDR(RGF_DMA_EP_MISC_ICR)}, 24108c2ecf20Sopenharmony_ci}; 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_cistatic void wil6210_debugfs_init_isr(struct wil6210_priv *wil, 24138c2ecf20Sopenharmony_ci struct dentry *dbg) 24148c2ecf20Sopenharmony_ci{ 24158c2ecf20Sopenharmony_ci int i; 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dbg_icr); i++) 24188c2ecf20Sopenharmony_ci wil6210_debugfs_create_ISR(wil, dbg_icr[i].name, dbg, 24198c2ecf20Sopenharmony_ci dbg_icr[i].icr_off); 24208c2ecf20Sopenharmony_ci} 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci#define WIL_FIELD(name, mode, type) { __stringify(name), mode, \ 24238c2ecf20Sopenharmony_ci offsetof(struct wil6210_priv, name), type} 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci/* fields in struct wil6210_priv */ 24268c2ecf20Sopenharmony_cistatic const struct dbg_off dbg_wil_off[] = { 24278c2ecf20Sopenharmony_ci WIL_FIELD(status[0], 0644, doff_ulong), 24288c2ecf20Sopenharmony_ci WIL_FIELD(hw_version, 0444, doff_x32), 24298c2ecf20Sopenharmony_ci WIL_FIELD(recovery_count, 0444, doff_u32), 24308c2ecf20Sopenharmony_ci WIL_FIELD(discovery_mode, 0644, doff_u8), 24318c2ecf20Sopenharmony_ci WIL_FIELD(chip_revision, 0444, doff_u8), 24328c2ecf20Sopenharmony_ci WIL_FIELD(abft_len, 0644, doff_u8), 24338c2ecf20Sopenharmony_ci WIL_FIELD(wakeup_trigger, 0644, doff_u8), 24348c2ecf20Sopenharmony_ci WIL_FIELD(ring_idle_trsh, 0644, doff_u32), 24358c2ecf20Sopenharmony_ci WIL_FIELD(num_rx_status_rings, 0644, doff_u8), 24368c2ecf20Sopenharmony_ci WIL_FIELD(rx_status_ring_order, 0644, doff_u32), 24378c2ecf20Sopenharmony_ci WIL_FIELD(tx_status_ring_order, 0644, doff_u32), 24388c2ecf20Sopenharmony_ci WIL_FIELD(rx_buff_id_count, 0644, doff_u32), 24398c2ecf20Sopenharmony_ci WIL_FIELD(amsdu_en, 0644, doff_u8), 24408c2ecf20Sopenharmony_ci {}, 24418c2ecf20Sopenharmony_ci}; 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_cistatic const struct dbg_off dbg_wil_regs[] = { 24448c2ecf20Sopenharmony_ci {"RGF_MAC_MTRL_COUNTER_0", 0444, HOSTADDR(RGF_MAC_MTRL_COUNTER_0), 24458c2ecf20Sopenharmony_ci doff_io32}, 24468c2ecf20Sopenharmony_ci {"RGF_USER_USAGE_1", 0444, HOSTADDR(RGF_USER_USAGE_1), doff_io32}, 24478c2ecf20Sopenharmony_ci {"RGF_USER_USAGE_2", 0444, HOSTADDR(RGF_USER_USAGE_2), doff_io32}, 24488c2ecf20Sopenharmony_ci {}, 24498c2ecf20Sopenharmony_ci}; 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci/* static parameters */ 24528c2ecf20Sopenharmony_cistatic const struct dbg_off dbg_statics[] = { 24538c2ecf20Sopenharmony_ci {"desc_index", 0644, (ulong)&dbg_txdesc_index, doff_u32}, 24548c2ecf20Sopenharmony_ci {"ring_index", 0644, (ulong)&dbg_ring_index, doff_u32}, 24558c2ecf20Sopenharmony_ci {"mem_addr", 0644, (ulong)&mem_addr, doff_u32}, 24568c2ecf20Sopenharmony_ci {"led_polarity", 0644, (ulong)&led_polarity, doff_u8}, 24578c2ecf20Sopenharmony_ci {"status_index", 0644, (ulong)&dbg_status_msg_index, doff_u32}, 24588c2ecf20Sopenharmony_ci {"sring_index", 0644, (ulong)&dbg_sring_index, doff_u32}, 24598c2ecf20Sopenharmony_ci {"drop_if_ring_full", 0644, (ulong)&drop_if_ring_full, doff_u8}, 24608c2ecf20Sopenharmony_ci {}, 24618c2ecf20Sopenharmony_ci}; 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_cistatic const int dbg_off_count = 4 * (ARRAY_SIZE(isr_off) - 1) + 24648c2ecf20Sopenharmony_ci ARRAY_SIZE(dbg_wil_regs) - 1 + 24658c2ecf20Sopenharmony_ci ARRAY_SIZE(pseudo_isr_off) - 1 + 24668c2ecf20Sopenharmony_ci ARRAY_SIZE(lgc_itr_cnt_off) - 1 + 24678c2ecf20Sopenharmony_ci ARRAY_SIZE(tx_itr_cnt_off) - 1 + 24688c2ecf20Sopenharmony_ci ARRAY_SIZE(rx_itr_cnt_off) - 1; 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ciint wil6210_debugfs_init(struct wil6210_priv *wil) 24718c2ecf20Sopenharmony_ci{ 24728c2ecf20Sopenharmony_ci struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, 24738c2ecf20Sopenharmony_ci wil_to_wiphy(wil)->debugfsdir); 24748c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(dbg)) 24758c2ecf20Sopenharmony_ci return -ENODEV; 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci wil->dbg_data.data_arr = kcalloc(dbg_off_count, 24788c2ecf20Sopenharmony_ci sizeof(struct wil_debugfs_iomem_data), 24798c2ecf20Sopenharmony_ci GFP_KERNEL); 24808c2ecf20Sopenharmony_ci if (!wil->dbg_data.data_arr) { 24818c2ecf20Sopenharmony_ci debugfs_remove_recursive(dbg); 24828c2ecf20Sopenharmony_ci wil->debug = NULL; 24838c2ecf20Sopenharmony_ci return -ENOMEM; 24848c2ecf20Sopenharmony_ci } 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci wil->dbg_data.iomem_data_count = 0; 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci wil_pmc_init(wil); 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci wil6210_debugfs_init_files(wil, dbg); 24918c2ecf20Sopenharmony_ci wil6210_debugfs_init_isr(wil, dbg); 24928c2ecf20Sopenharmony_ci wil6210_debugfs_init_blobs(wil, dbg); 24938c2ecf20Sopenharmony_ci wil6210_debugfs_init_offset(wil, dbg, wil, dbg_wil_off); 24948c2ecf20Sopenharmony_ci wil6210_debugfs_init_offset(wil, dbg, (void * __force)wil->csr, 24958c2ecf20Sopenharmony_ci dbg_wil_regs); 24968c2ecf20Sopenharmony_ci wil6210_debugfs_init_offset(wil, dbg, NULL, dbg_statics); 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci wil6210_debugfs_create_pseudo_ISR(wil, dbg); 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci wil6210_debugfs_create_ITR_CNT(wil, dbg); 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_ci return 0; 25038c2ecf20Sopenharmony_ci} 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_civoid wil6210_debugfs_remove(struct wil6210_priv *wil) 25068c2ecf20Sopenharmony_ci{ 25078c2ecf20Sopenharmony_ci int i; 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci debugfs_remove_recursive(wil->debug); 25108c2ecf20Sopenharmony_ci wil->debug = NULL; 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci kfree(wil->dbg_data.data_arr); 25138c2ecf20Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) 25148c2ecf20Sopenharmony_ci kfree(wil->sta[i].tx_latency_bins); 25158c2ecf20Sopenharmony_ci 25168c2ecf20Sopenharmony_ci /* free pmc memory without sending command to fw, as it will 25178c2ecf20Sopenharmony_ci * be reset on the way down anyway 25188c2ecf20Sopenharmony_ci */ 25198c2ecf20Sopenharmony_ci wil_pmc_free(wil, false); 25208c2ecf20Sopenharmony_ci} 2521