162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. 462306a36Sopenharmony_ci * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/debugfs.h> 962306a36Sopenharmony_ci#include <linux/seq_file.h> 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1262306a36Sopenharmony_ci#include <linux/power_supply.h> 1362306a36Sopenharmony_ci#include "wil6210.h" 1462306a36Sopenharmony_ci#include "wmi.h" 1562306a36Sopenharmony_ci#include "txrx.h" 1662306a36Sopenharmony_ci#include "pmc.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* Nasty hack. Better have per device instances */ 1962306a36Sopenharmony_cistatic u32 mem_addr; 2062306a36Sopenharmony_cistatic u32 dbg_txdesc_index; 2162306a36Sopenharmony_cistatic u32 dbg_ring_index; /* 24+ for Rx, 0..23 for Tx */ 2262306a36Sopenharmony_cistatic u32 dbg_status_msg_index; 2362306a36Sopenharmony_ci/* 0..wil->num_rx_status_rings-1 for Rx, wil->tx_sring_idx for Tx */ 2462306a36Sopenharmony_cistatic u32 dbg_sring_index; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cienum dbg_off_type { 2762306a36Sopenharmony_ci doff_u32 = 0, 2862306a36Sopenharmony_ci doff_x32 = 1, 2962306a36Sopenharmony_ci doff_ulong = 2, 3062306a36Sopenharmony_ci doff_io32 = 3, 3162306a36Sopenharmony_ci doff_u8 = 4 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* offset to "wil" */ 3562306a36Sopenharmony_cistruct dbg_off { 3662306a36Sopenharmony_ci const char *name; 3762306a36Sopenharmony_ci umode_t mode; 3862306a36Sopenharmony_ci ulong off; 3962306a36Sopenharmony_ci enum dbg_off_type type; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void wil_print_desc_edma(struct seq_file *s, struct wil6210_priv *wil, 4362306a36Sopenharmony_ci struct wil_ring *ring, 4462306a36Sopenharmony_ci char _s, char _h, int idx) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci u8 num_of_descs; 4762306a36Sopenharmony_ci bool has_skb = false; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (ring->is_rx) { 5062306a36Sopenharmony_ci struct wil_rx_enhanced_desc *rx_d = 5162306a36Sopenharmony_ci (struct wil_rx_enhanced_desc *) 5262306a36Sopenharmony_ci &ring->va[idx].rx.enhanced; 5362306a36Sopenharmony_ci u16 buff_id = le16_to_cpu(rx_d->mac.buff_id); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (wil->rx_buff_mgmt.buff_arr && 5662306a36Sopenharmony_ci wil_val_in_range(buff_id, 0, wil->rx_buff_mgmt.size)) 5762306a36Sopenharmony_ci has_skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; 5862306a36Sopenharmony_ci seq_printf(s, "%c", (has_skb) ? _h : _s); 5962306a36Sopenharmony_ci } else { 6062306a36Sopenharmony_ci struct wil_tx_enhanced_desc *d = 6162306a36Sopenharmony_ci (struct wil_tx_enhanced_desc *) 6262306a36Sopenharmony_ci &ring->va[idx].tx.enhanced; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci num_of_descs = (u8)d->mac.d[2]; 6562306a36Sopenharmony_ci has_skb = ring->ctx && ring->ctx[idx].skb; 6662306a36Sopenharmony_ci if (num_of_descs >= 1) 6762306a36Sopenharmony_ci seq_printf(s, "%c", has_skb ? _h : _s); 6862306a36Sopenharmony_ci else 6962306a36Sopenharmony_ci /* num_of_descs == 0, it's a frag in a list of descs */ 7062306a36Sopenharmony_ci seq_printf(s, "%c", has_skb ? 'h' : _s); 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil, 7562306a36Sopenharmony_ci const char *name, struct wil_ring *ring, 7662306a36Sopenharmony_ci char _s, char _h) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci void __iomem *x; 7962306a36Sopenharmony_ci u32 v; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci seq_printf(s, "RING %s = {\n", name); 8262306a36Sopenharmony_ci seq_printf(s, " pa = %pad\n", &ring->pa); 8362306a36Sopenharmony_ci seq_printf(s, " va = 0x%p\n", ring->va); 8462306a36Sopenharmony_ci seq_printf(s, " size = %d\n", ring->size); 8562306a36Sopenharmony_ci if (wil->use_enhanced_dma_hw && ring->is_rx) 8662306a36Sopenharmony_ci seq_printf(s, " swtail = %u\n", *ring->edma_rx_swtail.va); 8762306a36Sopenharmony_ci else 8862306a36Sopenharmony_ci seq_printf(s, " swtail = %d\n", ring->swtail); 8962306a36Sopenharmony_ci seq_printf(s, " swhead = %d\n", ring->swhead); 9062306a36Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 9162306a36Sopenharmony_ci int ring_id = ring->is_rx ? 9262306a36Sopenharmony_ci WIL_RX_DESC_RING_ID : ring - wil->ring_tx; 9362306a36Sopenharmony_ci /* SUBQ_CONS is a table of 32 entries, one for each Q pair. 9462306a36Sopenharmony_ci * lower 16bits are for even ring_id and upper 16bits are for 9562306a36Sopenharmony_ci * odd ring_id 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci x = wmi_addr(wil, RGF_DMA_SCM_SUBQ_CONS + 4 * (ring_id / 2)); 9862306a36Sopenharmony_ci v = readl_relaxed(x); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci v = (ring_id % 2 ? (v >> 16) : (v & 0xffff)); 10162306a36Sopenharmony_ci seq_printf(s, " hwhead = %u\n", v); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci seq_printf(s, " hwtail = [0x%08x] -> ", ring->hwtail); 10462306a36Sopenharmony_ci x = wmi_addr(wil, ring->hwtail); 10562306a36Sopenharmony_ci if (x) { 10662306a36Sopenharmony_ci v = readl(x); 10762306a36Sopenharmony_ci seq_printf(s, "0x%08x = %d\n", v, v); 10862306a36Sopenharmony_ci } else { 10962306a36Sopenharmony_ci seq_puts(s, "???\n"); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (ring->va && (ring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) { 11362306a36Sopenharmony_ci uint i; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci for (i = 0; i < ring->size; i++) { 11662306a36Sopenharmony_ci if ((i % 128) == 0 && i != 0) 11762306a36Sopenharmony_ci seq_puts(s, "\n"); 11862306a36Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 11962306a36Sopenharmony_ci wil_print_desc_edma(s, wil, ring, _s, _h, i); 12062306a36Sopenharmony_ci } else { 12162306a36Sopenharmony_ci volatile struct vring_tx_desc *d = 12262306a36Sopenharmony_ci &ring->va[i].tx.legacy; 12362306a36Sopenharmony_ci seq_printf(s, "%c", (d->dma.status & BIT(0)) ? 12462306a36Sopenharmony_ci _s : (ring->ctx[i].skb ? _h : 'h')); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci seq_puts(s, "\n"); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci seq_puts(s, "}\n"); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int ring_show(struct seq_file *s, void *data) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci uint i; 13562306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci wil_print_ring(s, wil, "rx", &wil->ring_rx, 'S', '_'); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wil->ring_tx); i++) { 14062306a36Sopenharmony_ci struct wil_ring *ring = &wil->ring_tx[i]; 14162306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[i]; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (ring->va) { 14462306a36Sopenharmony_ci int cid = wil->ring2cid_tid[i][0]; 14562306a36Sopenharmony_ci int tid = wil->ring2cid_tid[i][1]; 14662306a36Sopenharmony_ci u32 swhead = ring->swhead; 14762306a36Sopenharmony_ci u32 swtail = ring->swtail; 14862306a36Sopenharmony_ci int used = (ring->size + swhead - swtail) 14962306a36Sopenharmony_ci % ring->size; 15062306a36Sopenharmony_ci int avail = ring->size - used - 1; 15162306a36Sopenharmony_ci char name[10]; 15262306a36Sopenharmony_ci char sidle[10]; 15362306a36Sopenharmony_ci /* performance monitoring */ 15462306a36Sopenharmony_ci cycles_t now = get_cycles(); 15562306a36Sopenharmony_ci uint64_t idle = txdata->idle * 100; 15662306a36Sopenharmony_ci uint64_t total = now - txdata->begin; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (total != 0) { 15962306a36Sopenharmony_ci do_div(idle, total); 16062306a36Sopenharmony_ci snprintf(sidle, sizeof(sidle), "%3d%%", 16162306a36Sopenharmony_ci (int)idle); 16262306a36Sopenharmony_ci } else { 16362306a36Sopenharmony_ci snprintf(sidle, sizeof(sidle), "N/A"); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci txdata->begin = now; 16662306a36Sopenharmony_ci txdata->idle = 0ULL; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci snprintf(name, sizeof(name), "tx_%2d", i); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (cid < wil->max_assoc_sta) 17162306a36Sopenharmony_ci seq_printf(s, 17262306a36Sopenharmony_ci "\n%pM CID %d TID %d 1x%s BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n", 17362306a36Sopenharmony_ci wil->sta[cid].addr, cid, tid, 17462306a36Sopenharmony_ci txdata->dot1x_open ? "+" : "-", 17562306a36Sopenharmony_ci txdata->agg_wsize, 17662306a36Sopenharmony_ci txdata->agg_timeout, 17762306a36Sopenharmony_ci txdata->agg_amsdu ? "+" : "-", 17862306a36Sopenharmony_ci used, avail, sidle); 17962306a36Sopenharmony_ci else 18062306a36Sopenharmony_ci seq_printf(s, 18162306a36Sopenharmony_ci "\nBroadcast 1x%s [%3d|%3d] idle %s\n", 18262306a36Sopenharmony_ci txdata->dot1x_open ? "+" : "-", 18362306a36Sopenharmony_ci used, avail, sidle); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci wil_print_ring(s, wil, name, ring, '_', 'H'); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ring); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil, 19462306a36Sopenharmony_ci struct wil_status_ring *sring) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci void __iomem *x; 19762306a36Sopenharmony_ci int sring_idx = sring - wil->srings; 19862306a36Sopenharmony_ci u32 v; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci seq_printf(s, "Status Ring %s [ %d ] = {\n", 20162306a36Sopenharmony_ci sring->is_rx ? "RX" : "TX", sring_idx); 20262306a36Sopenharmony_ci seq_printf(s, " pa = %pad\n", &sring->pa); 20362306a36Sopenharmony_ci seq_printf(s, " va = 0x%pK\n", sring->va); 20462306a36Sopenharmony_ci seq_printf(s, " size = %d\n", sring->size); 20562306a36Sopenharmony_ci seq_printf(s, " elem_size = %zu\n", sring->elem_size); 20662306a36Sopenharmony_ci seq_printf(s, " swhead = %d\n", sring->swhead); 20762306a36Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 20862306a36Sopenharmony_ci /* COMPQ_PROD is a table of 32 entries, one for each Q pair. 20962306a36Sopenharmony_ci * lower 16bits are for even ring_id and upper 16bits are for 21062306a36Sopenharmony_ci * odd ring_id 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci x = wmi_addr(wil, RGF_DMA_SCM_COMPQ_PROD + 4 * (sring_idx / 2)); 21362306a36Sopenharmony_ci v = readl_relaxed(x); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci v = (sring_idx % 2 ? (v >> 16) : (v & 0xffff)); 21662306a36Sopenharmony_ci seq_printf(s, " hwhead = %u\n", v); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci seq_printf(s, " hwtail = [0x%08x] -> ", sring->hwtail); 21962306a36Sopenharmony_ci x = wmi_addr(wil, sring->hwtail); 22062306a36Sopenharmony_ci if (x) { 22162306a36Sopenharmony_ci v = readl_relaxed(x); 22262306a36Sopenharmony_ci seq_printf(s, "0x%08x = %d\n", v, v); 22362306a36Sopenharmony_ci } else { 22462306a36Sopenharmony_ci seq_puts(s, "???\n"); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci seq_printf(s, " desc_rdy_pol = %d\n", sring->desc_rdy_pol); 22762306a36Sopenharmony_ci seq_printf(s, " invalid_buff_id_cnt = %d\n", 22862306a36Sopenharmony_ci sring->invalid_buff_id_cnt); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (sring->va && (sring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) { 23162306a36Sopenharmony_ci uint i; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci for (i = 0; i < sring->size; i++) { 23462306a36Sopenharmony_ci u32 *sdword_0 = 23562306a36Sopenharmony_ci (u32 *)(sring->va + (sring->elem_size * i)); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if ((i % 128) == 0 && i != 0) 23862306a36Sopenharmony_ci seq_puts(s, "\n"); 23962306a36Sopenharmony_ci if (i == sring->swhead) 24062306a36Sopenharmony_ci seq_printf(s, "%c", (*sdword_0 & BIT(31)) ? 24162306a36Sopenharmony_ci 'X' : 'x'); 24262306a36Sopenharmony_ci else 24362306a36Sopenharmony_ci seq_printf(s, "%c", (*sdword_0 & BIT(31)) ? 24462306a36Sopenharmony_ci '1' : '0'); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci seq_puts(s, "\n"); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci seq_puts(s, "}\n"); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int srings_show(struct seq_file *s, void *data) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 25462306a36Sopenharmony_ci int i = 0; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci for (i = 0; i < WIL6210_MAX_STATUS_RINGS; i++) 25762306a36Sopenharmony_ci if (wil->srings[i].va) 25862306a36Sopenharmony_ci wil_print_sring(s, wil, &wil->srings[i]); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(srings); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void wil_seq_hexdump(struct seq_file *s, void *p, int len, 26562306a36Sopenharmony_ci const char *prefix) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci seq_hex_dump(s, prefix, DUMP_PREFIX_NONE, 16, 1, p, len, false); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void wil_print_mbox_ring(struct seq_file *s, const char *prefix, 27162306a36Sopenharmony_ci void __iomem *off) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 27462306a36Sopenharmony_ci struct wil6210_mbox_ring r; 27562306a36Sopenharmony_ci int rsize; 27662306a36Sopenharmony_ci uint i; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci wil_halp_vote(wil); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (wil_mem_access_lock(wil)) { 28162306a36Sopenharmony_ci wil_halp_unvote(wil); 28262306a36Sopenharmony_ci return; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci wil_memcpy_fromio_32(&r, off, sizeof(r)); 28662306a36Sopenharmony_ci wil_mbox_ring_le2cpus(&r); 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * we just read memory block from NIC. This memory may be 28962306a36Sopenharmony_ci * garbage. Check validity before using it. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci rsize = r.size / sizeof(struct wil6210_mbox_ring_desc); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci seq_printf(s, "ring %s = {\n", prefix); 29462306a36Sopenharmony_ci seq_printf(s, " base = 0x%08x\n", r.base); 29562306a36Sopenharmony_ci seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize); 29662306a36Sopenharmony_ci seq_printf(s, " tail = 0x%08x\n", r.tail); 29762306a36Sopenharmony_ci seq_printf(s, " head = 0x%08x\n", r.head); 29862306a36Sopenharmony_ci seq_printf(s, " entry size = %d\n", r.entry_size); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (r.size % sizeof(struct wil6210_mbox_ring_desc)) { 30162306a36Sopenharmony_ci seq_printf(s, " ??? size is not multiple of %zd, garbage?\n", 30262306a36Sopenharmony_ci sizeof(struct wil6210_mbox_ring_desc)); 30362306a36Sopenharmony_ci goto out; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!wmi_addr(wil, r.base) || 30762306a36Sopenharmony_ci !wmi_addr(wil, r.tail) || 30862306a36Sopenharmony_ci !wmi_addr(wil, r.head)) { 30962306a36Sopenharmony_ci seq_puts(s, " ??? pointers are garbage?\n"); 31062306a36Sopenharmony_ci goto out; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci for (i = 0; i < rsize; i++) { 31462306a36Sopenharmony_ci struct wil6210_mbox_ring_desc d; 31562306a36Sopenharmony_ci struct wil6210_mbox_hdr hdr; 31662306a36Sopenharmony_ci size_t delta = i * sizeof(d); 31762306a36Sopenharmony_ci void __iomem *x = wil->csr + HOSTADDR(r.base) + delta; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci wil_memcpy_fromio_32(&d, x, sizeof(d)); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci seq_printf(s, " [%2x] %s %s%s 0x%08x", i, 32262306a36Sopenharmony_ci d.sync ? "F" : "E", 32362306a36Sopenharmony_ci (r.tail - r.base == delta) ? "t" : " ", 32462306a36Sopenharmony_ci (r.head - r.base == delta) ? "h" : " ", 32562306a36Sopenharmony_ci le32_to_cpu(d.addr)); 32662306a36Sopenharmony_ci if (0 == wmi_read_hdr(wil, d.addr, &hdr)) { 32762306a36Sopenharmony_ci u16 len = le16_to_cpu(hdr.len); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci seq_printf(s, " -> %04x %04x %04x %02x\n", 33062306a36Sopenharmony_ci le16_to_cpu(hdr.seq), len, 33162306a36Sopenharmony_ci le16_to_cpu(hdr.type), hdr.flags); 33262306a36Sopenharmony_ci if (len <= MAX_MBOXITEM_SIZE) { 33362306a36Sopenharmony_ci unsigned char databuf[MAX_MBOXITEM_SIZE]; 33462306a36Sopenharmony_ci void __iomem *src = wmi_buffer(wil, d.addr) + 33562306a36Sopenharmony_ci sizeof(struct wil6210_mbox_hdr); 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * No need to check @src for validity - 33862306a36Sopenharmony_ci * we already validated @d.addr while 33962306a36Sopenharmony_ci * reading header 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci wil_memcpy_fromio_32(databuf, src, len); 34262306a36Sopenharmony_ci wil_seq_hexdump(s, databuf, len, " : "); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } else { 34562306a36Sopenharmony_ci seq_puts(s, "\n"); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci out: 34962306a36Sopenharmony_ci seq_puts(s, "}\n"); 35062306a36Sopenharmony_ci wil_mem_access_unlock(wil); 35162306a36Sopenharmony_ci wil_halp_unvote(wil); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int mbox_show(struct seq_file *s, void *data) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 35762306a36Sopenharmony_ci int ret; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ret = wil_pm_runtime_get(wil); 36062306a36Sopenharmony_ci if (ret < 0) 36162306a36Sopenharmony_ci return ret; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci wil_print_mbox_ring(s, "tx", wil->csr + HOST_MBOX + 36462306a36Sopenharmony_ci offsetof(struct wil6210_mbox_ctl, tx)); 36562306a36Sopenharmony_ci wil_print_mbox_ring(s, "rx", wil->csr + HOST_MBOX + 36662306a36Sopenharmony_ci offsetof(struct wil6210_mbox_ctl, rx)); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci wil_pm_runtime_put(wil); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mbox); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int wil_debugfs_iomem_x32_set(void *data, u64 val) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct wil_debugfs_iomem_data *d = (struct 37762306a36Sopenharmony_ci wil_debugfs_iomem_data *)data; 37862306a36Sopenharmony_ci struct wil6210_priv *wil = d->wil; 37962306a36Sopenharmony_ci int ret; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci ret = wil_pm_runtime_get(wil); 38262306a36Sopenharmony_ci if (ret < 0) 38362306a36Sopenharmony_ci return ret; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci writel_relaxed(val, (void __iomem *)d->offset); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci wmb(); /* make sure write propagated to HW */ 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci wil_pm_runtime_put(wil); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int wil_debugfs_iomem_x32_get(void *data, u64 *val) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct wil_debugfs_iomem_data *d = (struct 39762306a36Sopenharmony_ci wil_debugfs_iomem_data *)data; 39862306a36Sopenharmony_ci struct wil6210_priv *wil = d->wil; 39962306a36Sopenharmony_ci int ret; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci ret = wil_pm_runtime_get(wil); 40262306a36Sopenharmony_ci if (ret < 0) 40362306a36Sopenharmony_ci return ret; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci *val = readl((void __iomem *)d->offset); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci wil_pm_runtime_put(wil); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get, 41362306a36Sopenharmony_ci wil_debugfs_iomem_x32_set, "0x%08llx\n"); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void wil_debugfs_create_iomem_x32(const char *name, umode_t mode, 41662306a36Sopenharmony_ci struct dentry *parent, void *value, 41762306a36Sopenharmony_ci struct wil6210_priv *wil) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct wil_debugfs_iomem_data *data = &wil->dbg_data.data_arr[ 42062306a36Sopenharmony_ci wil->dbg_data.iomem_data_count]; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci data->wil = wil; 42362306a36Sopenharmony_ci data->offset = value; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci debugfs_create_file_unsafe(name, mode, parent, data, &fops_iomem_x32); 42662306a36Sopenharmony_ci wil->dbg_data.iomem_data_count++; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int wil_debugfs_ulong_set(void *data, u64 val) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci *(ulong *)data = val; 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int wil_debugfs_ulong_get(void *data, u64 *val) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci *val = *(ulong *)data; 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get, 44262306a36Sopenharmony_ci wil_debugfs_ulong_set, "0x%llx\n"); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/** 44562306a36Sopenharmony_ci * wil6210_debugfs_init_offset - create set of debugfs files 44662306a36Sopenharmony_ci * @wil: driver's context, used for printing 44762306a36Sopenharmony_ci * @dbg: directory on the debugfs, where files will be created 44862306a36Sopenharmony_ci * @base: base address used in address calculation 44962306a36Sopenharmony_ci * @tbl: table with file descriptions. Should be terminated with empty element. 45062306a36Sopenharmony_ci * 45162306a36Sopenharmony_ci * Creates files accordingly to the @tbl. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_cistatic void wil6210_debugfs_init_offset(struct wil6210_priv *wil, 45462306a36Sopenharmony_ci struct dentry *dbg, void *base, 45562306a36Sopenharmony_ci const struct dbg_off * const tbl) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci int i; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci for (i = 0; tbl[i].name; i++) { 46062306a36Sopenharmony_ci switch (tbl[i].type) { 46162306a36Sopenharmony_ci case doff_u32: 46262306a36Sopenharmony_ci debugfs_create_u32(tbl[i].name, tbl[i].mode, dbg, 46362306a36Sopenharmony_ci base + tbl[i].off); 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci case doff_x32: 46662306a36Sopenharmony_ci debugfs_create_x32(tbl[i].name, tbl[i].mode, dbg, 46762306a36Sopenharmony_ci base + tbl[i].off); 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci case doff_ulong: 47062306a36Sopenharmony_ci debugfs_create_file_unsafe(tbl[i].name, tbl[i].mode, 47162306a36Sopenharmony_ci dbg, base + tbl[i].off, 47262306a36Sopenharmony_ci &wil_fops_ulong); 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci case doff_io32: 47562306a36Sopenharmony_ci wil_debugfs_create_iomem_x32(tbl[i].name, tbl[i].mode, 47662306a36Sopenharmony_ci dbg, base + tbl[i].off, 47762306a36Sopenharmony_ci wil); 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci case doff_u8: 48062306a36Sopenharmony_ci debugfs_create_u8(tbl[i].name, tbl[i].mode, dbg, 48162306a36Sopenharmony_ci base + tbl[i].off); 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic const struct dbg_off isr_off[] = { 48862306a36Sopenharmony_ci {"ICC", 0644, offsetof(struct RGF_ICR, ICC), doff_io32}, 48962306a36Sopenharmony_ci {"ICR", 0644, offsetof(struct RGF_ICR, ICR), doff_io32}, 49062306a36Sopenharmony_ci {"ICM", 0644, offsetof(struct RGF_ICR, ICM), doff_io32}, 49162306a36Sopenharmony_ci {"ICS", 0244, offsetof(struct RGF_ICR, ICS), doff_io32}, 49262306a36Sopenharmony_ci {"IMV", 0644, offsetof(struct RGF_ICR, IMV), doff_io32}, 49362306a36Sopenharmony_ci {"IMS", 0244, offsetof(struct RGF_ICR, IMS), doff_io32}, 49462306a36Sopenharmony_ci {"IMC", 0244, offsetof(struct RGF_ICR, IMC), doff_io32}, 49562306a36Sopenharmony_ci {}, 49662306a36Sopenharmony_ci}; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic void wil6210_debugfs_create_ISR(struct wil6210_priv *wil, 49962306a36Sopenharmony_ci const char *name, struct dentry *parent, 50062306a36Sopenharmony_ci u32 off) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct dentry *d = debugfs_create_dir(name, parent); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr + off, 50562306a36Sopenharmony_ci isr_off); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic const struct dbg_off pseudo_isr_off[] = { 50962306a36Sopenharmony_ci {"CAUSE", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE), doff_io32}, 51062306a36Sopenharmony_ci {"MASK_SW", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW), doff_io32}, 51162306a36Sopenharmony_ci {"MASK_FW", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW), doff_io32}, 51262306a36Sopenharmony_ci {}, 51362306a36Sopenharmony_ci}; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic void wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, 51662306a36Sopenharmony_ci struct dentry *parent) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr, 52162306a36Sopenharmony_ci pseudo_isr_off); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic const struct dbg_off lgc_itr_cnt_off[] = { 52562306a36Sopenharmony_ci {"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_CNT_TRSH), doff_io32}, 52662306a36Sopenharmony_ci {"DATA", 0644, HOSTADDR(RGF_DMA_ITR_CNT_DATA), doff_io32}, 52762306a36Sopenharmony_ci {"CTL", 0644, HOSTADDR(RGF_DMA_ITR_CNT_CRL), doff_io32}, 52862306a36Sopenharmony_ci {}, 52962306a36Sopenharmony_ci}; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic const struct dbg_off tx_itr_cnt_off[] = { 53262306a36Sopenharmony_ci {"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_TRSH), 53362306a36Sopenharmony_ci doff_io32}, 53462306a36Sopenharmony_ci {"DATA", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_DATA), 53562306a36Sopenharmony_ci doff_io32}, 53662306a36Sopenharmony_ci {"CTL", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_CTL), 53762306a36Sopenharmony_ci doff_io32}, 53862306a36Sopenharmony_ci {"IDL_TRSH", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_TRSH), 53962306a36Sopenharmony_ci doff_io32}, 54062306a36Sopenharmony_ci {"IDL_DATA", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_DATA), 54162306a36Sopenharmony_ci doff_io32}, 54262306a36Sopenharmony_ci {"IDL_CTL", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_CTL), 54362306a36Sopenharmony_ci doff_io32}, 54462306a36Sopenharmony_ci {}, 54562306a36Sopenharmony_ci}; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic const struct dbg_off rx_itr_cnt_off[] = { 54862306a36Sopenharmony_ci {"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_TRSH), 54962306a36Sopenharmony_ci doff_io32}, 55062306a36Sopenharmony_ci {"DATA", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_DATA), 55162306a36Sopenharmony_ci doff_io32}, 55262306a36Sopenharmony_ci {"CTL", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_CTL), 55362306a36Sopenharmony_ci doff_io32}, 55462306a36Sopenharmony_ci {"IDL_TRSH", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_TRSH), 55562306a36Sopenharmony_ci doff_io32}, 55662306a36Sopenharmony_ci {"IDL_DATA", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_DATA), 55762306a36Sopenharmony_ci doff_io32}, 55862306a36Sopenharmony_ci {"IDL_CTL", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_CTL), 55962306a36Sopenharmony_ci doff_io32}, 56062306a36Sopenharmony_ci {}, 56162306a36Sopenharmony_ci}; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, 56462306a36Sopenharmony_ci struct dentry *parent) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct dentry *d, *dtx, *drx; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci d = debugfs_create_dir("ITR_CNT", parent); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci dtx = debugfs_create_dir("TX", d); 57162306a36Sopenharmony_ci drx = debugfs_create_dir("RX", d); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr, 57462306a36Sopenharmony_ci lgc_itr_cnt_off); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci wil6210_debugfs_init_offset(wil, dtx, (void * __force)wil->csr, 57762306a36Sopenharmony_ci tx_itr_cnt_off); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci wil6210_debugfs_init_offset(wil, drx, (void * __force)wil->csr, 58062306a36Sopenharmony_ci rx_itr_cnt_off); 58162306a36Sopenharmony_ci return 0; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic int memread_show(struct seq_file *s, void *data) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 58762306a36Sopenharmony_ci void __iomem *a; 58862306a36Sopenharmony_ci int ret; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci ret = wil_pm_runtime_get(wil); 59162306a36Sopenharmony_ci if (ret < 0) 59262306a36Sopenharmony_ci return ret; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci ret = wil_mem_access_lock(wil); 59562306a36Sopenharmony_ci if (ret) { 59662306a36Sopenharmony_ci wil_pm_runtime_put(wil); 59762306a36Sopenharmony_ci return ret; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci a = wmi_buffer(wil, cpu_to_le32(mem_addr)); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (a) 60362306a36Sopenharmony_ci seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, readl(a)); 60462306a36Sopenharmony_ci else 60562306a36Sopenharmony_ci seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci wil_mem_access_unlock(wil); 60862306a36Sopenharmony_ci wil_pm_runtime_put(wil); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(memread); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, 61562306a36Sopenharmony_ci size_t count, loff_t *ppos) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci enum { max_count = 4096 }; 61862306a36Sopenharmony_ci struct wil_blob_wrapper *wil_blob = file->private_data; 61962306a36Sopenharmony_ci struct wil6210_priv *wil = wil_blob->wil; 62062306a36Sopenharmony_ci loff_t aligned_pos, pos = *ppos; 62162306a36Sopenharmony_ci size_t available = wil_blob->blob.size; 62262306a36Sopenharmony_ci void *buf; 62362306a36Sopenharmony_ci size_t unaligned_bytes, aligned_count, ret; 62462306a36Sopenharmony_ci int rc; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (pos < 0) 62762306a36Sopenharmony_ci return -EINVAL; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (pos >= available || !count) 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (count > available - pos) 63362306a36Sopenharmony_ci count = available - pos; 63462306a36Sopenharmony_ci if (count > max_count) 63562306a36Sopenharmony_ci count = max_count; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* set pos to 4 bytes aligned */ 63862306a36Sopenharmony_ci unaligned_bytes = pos % 4; 63962306a36Sopenharmony_ci aligned_pos = pos - unaligned_bytes; 64062306a36Sopenharmony_ci aligned_count = count + unaligned_bytes; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci buf = kmalloc(aligned_count, GFP_KERNEL); 64362306a36Sopenharmony_ci if (!buf) 64462306a36Sopenharmony_ci return -ENOMEM; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci rc = wil_pm_runtime_get(wil); 64762306a36Sopenharmony_ci if (rc < 0) { 64862306a36Sopenharmony_ci kfree(buf); 64962306a36Sopenharmony_ci return rc; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci rc = wil_mem_access_lock(wil); 65362306a36Sopenharmony_ci if (rc) { 65462306a36Sopenharmony_ci kfree(buf); 65562306a36Sopenharmony_ci wil_pm_runtime_put(wil); 65662306a36Sopenharmony_ci return rc; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci wil_memcpy_fromio_32(buf, (const void __iomem *) 66062306a36Sopenharmony_ci wil_blob->blob.data + aligned_pos, aligned_count); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci ret = copy_to_user(user_buf, buf + unaligned_bytes, count); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci wil_mem_access_unlock(wil); 66562306a36Sopenharmony_ci wil_pm_runtime_put(wil); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci kfree(buf); 66862306a36Sopenharmony_ci if (ret == count) 66962306a36Sopenharmony_ci return -EFAULT; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci count -= ret; 67262306a36Sopenharmony_ci *ppos = pos + count; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return count; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic const struct file_operations fops_ioblob = { 67862306a36Sopenharmony_ci .read = wil_read_file_ioblob, 67962306a36Sopenharmony_ci .open = simple_open, 68062306a36Sopenharmony_ci .llseek = default_llseek, 68162306a36Sopenharmony_ci}; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic 68462306a36Sopenharmony_cistruct dentry *wil_debugfs_create_ioblob(const char *name, 68562306a36Sopenharmony_ci umode_t mode, 68662306a36Sopenharmony_ci struct dentry *parent, 68762306a36Sopenharmony_ci struct wil_blob_wrapper *wil_blob) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci return debugfs_create_file(name, mode, parent, wil_blob, &fops_ioblob); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/*---write channel 1..4 to rxon for it, 0 to rxoff---*/ 69362306a36Sopenharmony_cistatic ssize_t wil_write_file_rxon(struct file *file, const char __user *buf, 69462306a36Sopenharmony_ci size_t len, loff_t *ppos) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 69762306a36Sopenharmony_ci int rc; 69862306a36Sopenharmony_ci long channel; 69962306a36Sopenharmony_ci bool on; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci char *kbuf = memdup_user_nul(buf, len); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (IS_ERR(kbuf)) 70462306a36Sopenharmony_ci return PTR_ERR(kbuf); 70562306a36Sopenharmony_ci rc = kstrtol(kbuf, 0, &channel); 70662306a36Sopenharmony_ci kfree(kbuf); 70762306a36Sopenharmony_ci if (rc) 70862306a36Sopenharmony_ci return rc; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if ((channel < 0) || (channel > 4)) { 71162306a36Sopenharmony_ci wil_err(wil, "Invalid channel %ld\n", channel); 71262306a36Sopenharmony_ci return -EINVAL; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci on = !!channel; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (on) { 71762306a36Sopenharmony_ci rc = wmi_set_channel(wil, (int)channel); 71862306a36Sopenharmony_ci if (rc) 71962306a36Sopenharmony_ci return rc; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci rc = wmi_rxon(wil, on); 72362306a36Sopenharmony_ci if (rc) 72462306a36Sopenharmony_ci return rc; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return len; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic const struct file_operations fops_rxon = { 73062306a36Sopenharmony_ci .write = wil_write_file_rxon, 73162306a36Sopenharmony_ci .open = simple_open, 73262306a36Sopenharmony_ci}; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic ssize_t wil_write_file_rbufcap(struct file *file, 73562306a36Sopenharmony_ci const char __user *buf, 73662306a36Sopenharmony_ci size_t count, loff_t *ppos) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 73962306a36Sopenharmony_ci int val; 74062306a36Sopenharmony_ci int rc; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci rc = kstrtoint_from_user(buf, count, 0, &val); 74362306a36Sopenharmony_ci if (rc) { 74462306a36Sopenharmony_ci wil_err(wil, "Invalid argument\n"); 74562306a36Sopenharmony_ci return rc; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci /* input value: negative to disable, 0 to use system default, 74862306a36Sopenharmony_ci * 1..ring size to set descriptor threshold 74962306a36Sopenharmony_ci */ 75062306a36Sopenharmony_ci wil_info(wil, "%s RBUFCAP, descriptors threshold - %d\n", 75162306a36Sopenharmony_ci val < 0 ? "Disabling" : "Enabling", val); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (!wil->ring_rx.va || val > wil->ring_rx.size) { 75462306a36Sopenharmony_ci wil_err(wil, "Invalid descriptors threshold, %d\n", val); 75562306a36Sopenharmony_ci return -EINVAL; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci rc = wmi_rbufcap_cfg(wil, val < 0 ? 0 : 1, val < 0 ? 0 : val); 75962306a36Sopenharmony_ci if (rc) { 76062306a36Sopenharmony_ci wil_err(wil, "RBUFCAP config failed: %d\n", rc); 76162306a36Sopenharmony_ci return rc; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci return count; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic const struct file_operations fops_rbufcap = { 76862306a36Sopenharmony_ci .write = wil_write_file_rbufcap, 76962306a36Sopenharmony_ci .open = simple_open, 77062306a36Sopenharmony_ci}; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci/* block ack control, write: 77362306a36Sopenharmony_ci * - "add <ringid> <agg_size> <timeout>" to trigger ADDBA 77462306a36Sopenharmony_ci * - "del_tx <ringid> <reason>" to trigger DELBA for Tx side 77562306a36Sopenharmony_ci * - "del_rx <CID> <TID> <reason>" to trigger DELBA for Rx side 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_cistatic ssize_t wil_write_back(struct file *file, const char __user *buf, 77862306a36Sopenharmony_ci size_t len, loff_t *ppos) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 78162306a36Sopenharmony_ci int rc; 78262306a36Sopenharmony_ci char *kbuf = kmalloc(len + 1, GFP_KERNEL); 78362306a36Sopenharmony_ci char cmd[9]; 78462306a36Sopenharmony_ci int p1, p2, p3; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (!kbuf) 78762306a36Sopenharmony_ci return -ENOMEM; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); 79062306a36Sopenharmony_ci if (rc != len) { 79162306a36Sopenharmony_ci kfree(kbuf); 79262306a36Sopenharmony_ci return rc >= 0 ? -EIO : rc; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci kbuf[len] = '\0'; 79662306a36Sopenharmony_ci rc = sscanf(kbuf, "%8s %d %d %d", cmd, &p1, &p2, &p3); 79762306a36Sopenharmony_ci kfree(kbuf); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (rc < 0) 80062306a36Sopenharmony_ci return rc; 80162306a36Sopenharmony_ci if (rc < 2) 80262306a36Sopenharmony_ci return -EINVAL; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if ((strcmp(cmd, "add") == 0) || 80562306a36Sopenharmony_ci (strcmp(cmd, "del_tx") == 0)) { 80662306a36Sopenharmony_ci struct wil_ring_tx_data *txdata; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (p1 < 0 || p1 >= WIL6210_MAX_TX_RINGS) { 80962306a36Sopenharmony_ci wil_err(wil, "BACK: invalid ring id %d\n", p1); 81062306a36Sopenharmony_ci return -EINVAL; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci txdata = &wil->ring_tx_data[p1]; 81362306a36Sopenharmony_ci if (strcmp(cmd, "add") == 0) { 81462306a36Sopenharmony_ci if (rc < 3) { 81562306a36Sopenharmony_ci wil_err(wil, "BACK: add require at least 2 params\n"); 81662306a36Sopenharmony_ci return -EINVAL; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci if (rc < 4) 81962306a36Sopenharmony_ci p3 = 0; 82062306a36Sopenharmony_ci wmi_addba(wil, txdata->mid, p1, p2, p3); 82162306a36Sopenharmony_ci } else { 82262306a36Sopenharmony_ci if (rc < 3) 82362306a36Sopenharmony_ci p2 = WLAN_REASON_QSTA_LEAVE_QBSS; 82462306a36Sopenharmony_ci wmi_delba_tx(wil, txdata->mid, p1, p2); 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci } else if (strcmp(cmd, "del_rx") == 0) { 82762306a36Sopenharmony_ci struct wil_sta_info *sta; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if (rc < 3) { 83062306a36Sopenharmony_ci wil_err(wil, 83162306a36Sopenharmony_ci "BACK: del_rx require at least 2 params\n"); 83262306a36Sopenharmony_ci return -EINVAL; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci if (p1 < 0 || p1 >= wil->max_assoc_sta) { 83562306a36Sopenharmony_ci wil_err(wil, "BACK: invalid CID %d\n", p1); 83662306a36Sopenharmony_ci return -EINVAL; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci if (rc < 4) 83962306a36Sopenharmony_ci p3 = WLAN_REASON_QSTA_LEAVE_QBSS; 84062306a36Sopenharmony_ci sta = &wil->sta[p1]; 84162306a36Sopenharmony_ci wmi_delba_rx(wil, sta->mid, p1, p2, p3); 84262306a36Sopenharmony_ci } else { 84362306a36Sopenharmony_ci wil_err(wil, "BACK: Unrecognized command \"%s\"\n", cmd); 84462306a36Sopenharmony_ci return -EINVAL; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return len; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic ssize_t wil_read_back(struct file *file, char __user *user_buf, 85162306a36Sopenharmony_ci size_t count, loff_t *ppos) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci static const char text[] = "block ack control, write:\n" 85462306a36Sopenharmony_ci " - \"add <ringid> <agg_size> <timeout>\" to trigger ADDBA\n" 85562306a36Sopenharmony_ci "If missing, <timeout> defaults to 0\n" 85662306a36Sopenharmony_ci " - \"del_tx <ringid> <reason>\" to trigger DELBA for Tx side\n" 85762306a36Sopenharmony_ci " - \"del_rx <CID> <TID> <reason>\" to trigger DELBA for Rx side\n" 85862306a36Sopenharmony_ci "If missing, <reason> set to \"STA_LEAVING\" (36)\n"; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, text, 86162306a36Sopenharmony_ci sizeof(text)); 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic const struct file_operations fops_back = { 86562306a36Sopenharmony_ci .read = wil_read_back, 86662306a36Sopenharmony_ci .write = wil_write_back, 86762306a36Sopenharmony_ci .open = simple_open, 86862306a36Sopenharmony_ci}; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci/* pmc control, write: 87162306a36Sopenharmony_ci * - "alloc <num descriptors> <descriptor_size>" to allocate PMC 87262306a36Sopenharmony_ci * - "free" to release memory allocated for PMC 87362306a36Sopenharmony_ci */ 87462306a36Sopenharmony_cistatic ssize_t wil_write_pmccfg(struct file *file, const char __user *buf, 87562306a36Sopenharmony_ci size_t len, loff_t *ppos) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 87862306a36Sopenharmony_ci int rc; 87962306a36Sopenharmony_ci char *kbuf = kmalloc(len + 1, GFP_KERNEL); 88062306a36Sopenharmony_ci char cmd[9]; 88162306a36Sopenharmony_ci int num_descs, desc_size; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (!kbuf) 88462306a36Sopenharmony_ci return -ENOMEM; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); 88762306a36Sopenharmony_ci if (rc != len) { 88862306a36Sopenharmony_ci kfree(kbuf); 88962306a36Sopenharmony_ci return rc >= 0 ? -EIO : rc; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci kbuf[len] = '\0'; 89362306a36Sopenharmony_ci rc = sscanf(kbuf, "%8s %d %d", cmd, &num_descs, &desc_size); 89462306a36Sopenharmony_ci kfree(kbuf); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (rc < 0) 89762306a36Sopenharmony_ci return rc; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (rc < 1) { 90062306a36Sopenharmony_ci wil_err(wil, "pmccfg: no params given\n"); 90162306a36Sopenharmony_ci return -EINVAL; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (0 == strcmp(cmd, "alloc")) { 90562306a36Sopenharmony_ci if (rc != 3) { 90662306a36Sopenharmony_ci wil_err(wil, "pmccfg: alloc requires 2 params\n"); 90762306a36Sopenharmony_ci return -EINVAL; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci wil_pmc_alloc(wil, num_descs, desc_size); 91062306a36Sopenharmony_ci } else if (0 == strcmp(cmd, "free")) { 91162306a36Sopenharmony_ci if (rc != 1) { 91262306a36Sopenharmony_ci wil_err(wil, "pmccfg: free does not have any params\n"); 91362306a36Sopenharmony_ci return -EINVAL; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci wil_pmc_free(wil, true); 91662306a36Sopenharmony_ci } else { 91762306a36Sopenharmony_ci wil_err(wil, "pmccfg: Unrecognized command \"%s\"\n", cmd); 91862306a36Sopenharmony_ci return -EINVAL; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return len; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic ssize_t wil_read_pmccfg(struct file *file, char __user *user_buf, 92562306a36Sopenharmony_ci size_t count, loff_t *ppos) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 92862306a36Sopenharmony_ci char text[256]; 92962306a36Sopenharmony_ci char help[] = "pmc control, write:\n" 93062306a36Sopenharmony_ci " - \"alloc <num descriptors> <descriptor_size>\" to allocate pmc\n" 93162306a36Sopenharmony_ci " - \"free\" to free memory allocated for pmc\n"; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci snprintf(text, sizeof(text), "Last command status: %d\n\n%s", 93462306a36Sopenharmony_ci wil_pmc_last_cmd_status(wil), help); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, text, 93762306a36Sopenharmony_ci strlen(text) + 1); 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic const struct file_operations fops_pmccfg = { 94162306a36Sopenharmony_ci .read = wil_read_pmccfg, 94262306a36Sopenharmony_ci .write = wil_write_pmccfg, 94362306a36Sopenharmony_ci .open = simple_open, 94462306a36Sopenharmony_ci}; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic const struct file_operations fops_pmcdata = { 94762306a36Sopenharmony_ci .open = simple_open, 94862306a36Sopenharmony_ci .read = wil_pmc_read, 94962306a36Sopenharmony_ci .llseek = wil_pmc_llseek, 95062306a36Sopenharmony_ci}; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic int wil_pmcring_seq_open(struct inode *inode, struct file *file) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci return single_open(file, wil_pmcring_read, inode->i_private); 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic const struct file_operations fops_pmcring = { 95862306a36Sopenharmony_ci .open = wil_pmcring_seq_open, 95962306a36Sopenharmony_ci .release = single_release, 96062306a36Sopenharmony_ci .read = seq_read, 96162306a36Sopenharmony_ci .llseek = seq_lseek, 96262306a36Sopenharmony_ci}; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci/*---tx_mgmt---*/ 96562306a36Sopenharmony_ci/* Write mgmt frame to this file to send it */ 96662306a36Sopenharmony_cistatic ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf, 96762306a36Sopenharmony_ci size_t len, loff_t *ppos) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 97062306a36Sopenharmony_ci struct wiphy *wiphy = wil_to_wiphy(wil); 97162306a36Sopenharmony_ci struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr; 97262306a36Sopenharmony_ci struct cfg80211_mgmt_tx_params params; 97362306a36Sopenharmony_ci int rc; 97462306a36Sopenharmony_ci void *frame; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci memset(¶ms, 0, sizeof(params)); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (!len) 97962306a36Sopenharmony_ci return -EINVAL; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci frame = memdup_user(buf, len); 98262306a36Sopenharmony_ci if (IS_ERR(frame)) 98362306a36Sopenharmony_ci return PTR_ERR(frame); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci params.buf = frame; 98662306a36Sopenharmony_ci params.len = len; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci rc = wil_cfg80211_mgmt_tx(wiphy, wdev, ¶ms, NULL); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci kfree(frame); 99162306a36Sopenharmony_ci wil_info(wil, "-> %d\n", rc); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci return len; 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistatic const struct file_operations fops_txmgmt = { 99762306a36Sopenharmony_ci .write = wil_write_file_txmgmt, 99862306a36Sopenharmony_ci .open = simple_open, 99962306a36Sopenharmony_ci}; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci/* Write WMI command (w/o mbox header) to this file to send it 100262306a36Sopenharmony_ci * WMI starts from wil6210_mbox_hdr_wmi header 100362306a36Sopenharmony_ci */ 100462306a36Sopenharmony_cistatic ssize_t wil_write_file_wmi(struct file *file, const char __user *buf, 100562306a36Sopenharmony_ci size_t len, loff_t *ppos) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 100862306a36Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 100962306a36Sopenharmony_ci struct wmi_cmd_hdr *wmi; 101062306a36Sopenharmony_ci void *cmd; 101162306a36Sopenharmony_ci int cmdlen = len - sizeof(struct wmi_cmd_hdr); 101262306a36Sopenharmony_ci u16 cmdid; 101362306a36Sopenharmony_ci int rc1; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (cmdlen < 0 || *ppos != 0) 101662306a36Sopenharmony_ci return -EINVAL; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci wmi = memdup_user(buf, len); 101962306a36Sopenharmony_ci if (IS_ERR(wmi)) 102062306a36Sopenharmony_ci return PTR_ERR(wmi); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci cmd = (cmdlen > 0) ? &wmi[1] : NULL; 102362306a36Sopenharmony_ci cmdid = le16_to_cpu(wmi->command_id); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci rc1 = wmi_send(wil, cmdid, vif->mid, cmd, cmdlen); 102662306a36Sopenharmony_ci kfree(wmi); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci wil_info(wil, "0x%04x[%d] -> %d\n", cmdid, cmdlen, rc1); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci return len; 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic const struct file_operations fops_wmi = { 103462306a36Sopenharmony_ci .write = wil_write_file_wmi, 103562306a36Sopenharmony_ci .open = simple_open, 103662306a36Sopenharmony_ci}; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic void wil_seq_print_skb(struct seq_file *s, struct sk_buff *skb) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci int i = 0; 104162306a36Sopenharmony_ci int len = skb_headlen(skb); 104262306a36Sopenharmony_ci void *p = skb->data; 104362306a36Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci seq_printf(s, " len = %d\n", len); 104662306a36Sopenharmony_ci wil_seq_hexdump(s, p, len, " : "); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (nr_frags) { 104962306a36Sopenharmony_ci seq_printf(s, " nr_frags = %d\n", nr_frags); 105062306a36Sopenharmony_ci for (i = 0; i < nr_frags; i++) { 105162306a36Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci len = skb_frag_size(frag); 105462306a36Sopenharmony_ci p = skb_frag_address_safe(frag); 105562306a36Sopenharmony_ci seq_printf(s, " [%2d] : len = %d\n", i, len); 105662306a36Sopenharmony_ci wil_seq_hexdump(s, p, len, " : "); 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci/*---------Tx/Rx descriptor------------*/ 106262306a36Sopenharmony_cistatic int txdesc_show(struct seq_file *s, void *data) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 106562306a36Sopenharmony_ci struct wil_ring *ring; 106662306a36Sopenharmony_ci bool tx; 106762306a36Sopenharmony_ci int ring_idx = dbg_ring_index; 106862306a36Sopenharmony_ci int txdesc_idx = dbg_txdesc_index; 106962306a36Sopenharmony_ci volatile struct vring_tx_desc *d; 107062306a36Sopenharmony_ci volatile u32 *u; 107162306a36Sopenharmony_ci struct sk_buff *skb; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 107462306a36Sopenharmony_ci /* RX ring index == 0 */ 107562306a36Sopenharmony_ci if (ring_idx >= WIL6210_MAX_TX_RINGS) { 107662306a36Sopenharmony_ci seq_printf(s, "invalid ring index %d\n", ring_idx); 107762306a36Sopenharmony_ci return 0; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci tx = ring_idx > 0; /* desc ring 0 is reserved for RX */ 108062306a36Sopenharmony_ci } else { 108162306a36Sopenharmony_ci /* RX ring index == WIL6210_MAX_TX_RINGS */ 108262306a36Sopenharmony_ci if (ring_idx > WIL6210_MAX_TX_RINGS) { 108362306a36Sopenharmony_ci seq_printf(s, "invalid ring index %d\n", ring_idx); 108462306a36Sopenharmony_ci return 0; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci tx = (ring_idx < WIL6210_MAX_TX_RINGS); 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci ring = tx ? &wil->ring_tx[ring_idx] : &wil->ring_rx; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (!ring->va) { 109262306a36Sopenharmony_ci if (tx) 109362306a36Sopenharmony_ci seq_printf(s, "No Tx[%2d] RING\n", ring_idx); 109462306a36Sopenharmony_ci else 109562306a36Sopenharmony_ci seq_puts(s, "No Rx RING\n"); 109662306a36Sopenharmony_ci return 0; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci if (txdesc_idx >= ring->size) { 110062306a36Sopenharmony_ci if (tx) 110162306a36Sopenharmony_ci seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n", 110262306a36Sopenharmony_ci ring_idx, txdesc_idx, ring->size); 110362306a36Sopenharmony_ci else 110462306a36Sopenharmony_ci seq_printf(s, "RxDesc index (%d) >= size (%d)\n", 110562306a36Sopenharmony_ci txdesc_idx, ring->size); 110662306a36Sopenharmony_ci return 0; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci /* use struct vring_tx_desc for Rx as well, 111062306a36Sopenharmony_ci * only field used, .dma.length, is the same 111162306a36Sopenharmony_ci */ 111262306a36Sopenharmony_ci d = &ring->va[txdesc_idx].tx.legacy; 111362306a36Sopenharmony_ci u = (volatile u32 *)d; 111462306a36Sopenharmony_ci skb = NULL; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if (wil->use_enhanced_dma_hw) { 111762306a36Sopenharmony_ci if (tx) { 111862306a36Sopenharmony_ci skb = ring->ctx ? ring->ctx[txdesc_idx].skb : NULL; 111962306a36Sopenharmony_ci } else if (wil->rx_buff_mgmt.buff_arr) { 112062306a36Sopenharmony_ci struct wil_rx_enhanced_desc *rx_d = 112162306a36Sopenharmony_ci (struct wil_rx_enhanced_desc *) 112262306a36Sopenharmony_ci &ring->va[txdesc_idx].rx.enhanced; 112362306a36Sopenharmony_ci u16 buff_id = le16_to_cpu(rx_d->mac.buff_id); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (!wil_val_in_range(buff_id, 0, 112662306a36Sopenharmony_ci wil->rx_buff_mgmt.size)) 112762306a36Sopenharmony_ci seq_printf(s, "invalid buff_id %d\n", buff_id); 112862306a36Sopenharmony_ci else 112962306a36Sopenharmony_ci skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci } else { 113262306a36Sopenharmony_ci skb = ring->ctx[txdesc_idx].skb; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci if (tx) 113562306a36Sopenharmony_ci seq_printf(s, "Tx[%2d][%3d] = {\n", ring_idx, 113662306a36Sopenharmony_ci txdesc_idx); 113762306a36Sopenharmony_ci else 113862306a36Sopenharmony_ci seq_printf(s, "Rx[%3d] = {\n", txdesc_idx); 113962306a36Sopenharmony_ci seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", 114062306a36Sopenharmony_ci u[0], u[1], u[2], u[3]); 114162306a36Sopenharmony_ci seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", 114262306a36Sopenharmony_ci u[4], u[5], u[6], u[7]); 114362306a36Sopenharmony_ci seq_printf(s, " SKB = 0x%p\n", skb); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci if (skb) { 114662306a36Sopenharmony_ci skb_get(skb); 114762306a36Sopenharmony_ci wil_seq_print_skb(s, skb); 114862306a36Sopenharmony_ci kfree_skb(skb); 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci seq_puts(s, "}\n"); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci return 0; 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(txdesc); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci/*---------Tx/Rx status message------------*/ 115762306a36Sopenharmony_cistatic int status_msg_show(struct seq_file *s, void *data) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 116062306a36Sopenharmony_ci int sring_idx = dbg_sring_index; 116162306a36Sopenharmony_ci struct wil_status_ring *sring; 116262306a36Sopenharmony_ci bool tx; 116362306a36Sopenharmony_ci u32 status_msg_idx = dbg_status_msg_index; 116462306a36Sopenharmony_ci u32 *u; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if (sring_idx >= WIL6210_MAX_STATUS_RINGS) { 116762306a36Sopenharmony_ci seq_printf(s, "invalid status ring index %d\n", sring_idx); 116862306a36Sopenharmony_ci return 0; 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci sring = &wil->srings[sring_idx]; 117262306a36Sopenharmony_ci tx = !sring->is_rx; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (!sring->va) { 117562306a36Sopenharmony_ci seq_printf(s, "No %cX status ring\n", tx ? 'T' : 'R'); 117662306a36Sopenharmony_ci return 0; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci if (status_msg_idx >= sring->size) { 118062306a36Sopenharmony_ci seq_printf(s, "%cxDesc index (%d) >= size (%d)\n", 118162306a36Sopenharmony_ci tx ? 'T' : 'R', status_msg_idx, sring->size); 118262306a36Sopenharmony_ci return 0; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci u = sring->va + (sring->elem_size * status_msg_idx); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci seq_printf(s, "%cx[%d][%3d] = {\n", 118862306a36Sopenharmony_ci tx ? 'T' : 'R', sring_idx, status_msg_idx); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x\n", 119162306a36Sopenharmony_ci u[0], u[1], u[2], u[3]); 119262306a36Sopenharmony_ci if (!tx && !wil->use_compressed_rx_status) 119362306a36Sopenharmony_ci seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x\n", 119462306a36Sopenharmony_ci u[4], u[5], u[6], u[7]); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci seq_puts(s, "}\n"); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci return 0; 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(status_msg); 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic int wil_print_rx_buff(struct seq_file *s, struct list_head *lh) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci struct wil_rx_buff *it; 120562306a36Sopenharmony_ci int i = 0; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci list_for_each_entry(it, lh, list) { 120862306a36Sopenharmony_ci if ((i % 16) == 0 && i != 0) 120962306a36Sopenharmony_ci seq_puts(s, "\n "); 121062306a36Sopenharmony_ci seq_printf(s, "[%4d] ", it->id); 121162306a36Sopenharmony_ci i++; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci seq_printf(s, "\nNumber of buffers: %u\n", i); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci return i; 121662306a36Sopenharmony_ci} 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_cistatic int rx_buff_mgmt_show(struct seq_file *s, void *data) 121962306a36Sopenharmony_ci{ 122062306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 122162306a36Sopenharmony_ci struct wil_rx_buff_mgmt *rbm = &wil->rx_buff_mgmt; 122262306a36Sopenharmony_ci int num_active; 122362306a36Sopenharmony_ci int num_free; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (!rbm->buff_arr) 122662306a36Sopenharmony_ci return -EINVAL; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci seq_printf(s, " size = %zu\n", rbm->size); 122962306a36Sopenharmony_ci seq_printf(s, " free_list_empty_cnt = %lu\n", 123062306a36Sopenharmony_ci rbm->free_list_empty_cnt); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* Print active list */ 123362306a36Sopenharmony_ci seq_puts(s, " Active list:\n"); 123462306a36Sopenharmony_ci num_active = wil_print_rx_buff(s, &rbm->active); 123562306a36Sopenharmony_ci seq_puts(s, "\n Free list:\n"); 123662306a36Sopenharmony_ci num_free = wil_print_rx_buff(s, &rbm->free); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci seq_printf(s, " Total number of buffers: %u\n", 123962306a36Sopenharmony_ci num_active + num_free); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci return 0; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(rx_buff_mgmt); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci/*---------beamforming------------*/ 124662306a36Sopenharmony_cistatic char *wil_bfstatus_str(u32 status) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci switch (status) { 124962306a36Sopenharmony_ci case 0: 125062306a36Sopenharmony_ci return "Failed"; 125162306a36Sopenharmony_ci case 1: 125262306a36Sopenharmony_ci return "OK"; 125362306a36Sopenharmony_ci case 2: 125462306a36Sopenharmony_ci return "Retrying"; 125562306a36Sopenharmony_ci default: 125662306a36Sopenharmony_ci return "??"; 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci} 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cistatic bool is_all_zeros(void * const x_, size_t sz) 126162306a36Sopenharmony_ci{ 126262306a36Sopenharmony_ci /* if reply is all-0, ignore this CID */ 126362306a36Sopenharmony_ci u32 *x = x_; 126462306a36Sopenharmony_ci int n; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci for (n = 0; n < sz / sizeof(*x); n++) 126762306a36Sopenharmony_ci if (x[n]) 126862306a36Sopenharmony_ci return false; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci return true; 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_cistatic int bf_show(struct seq_file *s, void *data) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci int rc; 127662306a36Sopenharmony_ci int i; 127762306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 127862306a36Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 127962306a36Sopenharmony_ci struct wmi_notify_req_cmd cmd = { 128062306a36Sopenharmony_ci .interval_usec = 0, 128162306a36Sopenharmony_ci }; 128262306a36Sopenharmony_ci struct { 128362306a36Sopenharmony_ci struct wmi_cmd_hdr wmi; 128462306a36Sopenharmony_ci struct wmi_notify_req_done_event evt; 128562306a36Sopenharmony_ci } __packed reply; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci memset(&reply, 0, sizeof(reply)); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 129062306a36Sopenharmony_ci u32 status; 129162306a36Sopenharmony_ci u8 bf_mcs; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci cmd.cid = i; 129462306a36Sopenharmony_ci rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, 129562306a36Sopenharmony_ci &cmd, sizeof(cmd), 129662306a36Sopenharmony_ci WMI_NOTIFY_REQ_DONE_EVENTID, &reply, 129762306a36Sopenharmony_ci sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 129862306a36Sopenharmony_ci /* if reply is all-0, ignore this CID */ 129962306a36Sopenharmony_ci if (rc || is_all_zeros(&reply.evt, sizeof(reply.evt))) 130062306a36Sopenharmony_ci continue; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci status = le32_to_cpu(reply.evt.status); 130362306a36Sopenharmony_ci bf_mcs = le16_to_cpu(reply.evt.bf_mcs); 130462306a36Sopenharmony_ci seq_printf(s, "CID %d {\n" 130562306a36Sopenharmony_ci " TSF = 0x%016llx\n" 130662306a36Sopenharmony_ci " TxMCS = %s TxTpt = %4d\n" 130762306a36Sopenharmony_ci " SQI = %4d\n" 130862306a36Sopenharmony_ci " RSSI = %4d\n" 130962306a36Sopenharmony_ci " Status = 0x%08x %s\n" 131062306a36Sopenharmony_ci " Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n" 131162306a36Sopenharmony_ci " Goodput(rx:tx) %4d:%4d\n" 131262306a36Sopenharmony_ci "}\n", 131362306a36Sopenharmony_ci i, 131462306a36Sopenharmony_ci le64_to_cpu(reply.evt.tsf), 131562306a36Sopenharmony_ci WIL_EXTENDED_MCS_CHECK(bf_mcs), 131662306a36Sopenharmony_ci le32_to_cpu(reply.evt.tx_tpt), 131762306a36Sopenharmony_ci reply.evt.sqi, 131862306a36Sopenharmony_ci reply.evt.rssi, 131962306a36Sopenharmony_ci status, wil_bfstatus_str(status), 132062306a36Sopenharmony_ci le16_to_cpu(reply.evt.my_rx_sector), 132162306a36Sopenharmony_ci le16_to_cpu(reply.evt.my_tx_sector), 132262306a36Sopenharmony_ci le16_to_cpu(reply.evt.other_rx_sector), 132362306a36Sopenharmony_ci le16_to_cpu(reply.evt.other_tx_sector), 132462306a36Sopenharmony_ci le32_to_cpu(reply.evt.rx_goodput), 132562306a36Sopenharmony_ci le32_to_cpu(reply.evt.tx_goodput)); 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci return 0; 132862306a36Sopenharmony_ci} 132962306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(bf); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci/*---------temp------------*/ 133262306a36Sopenharmony_cistatic void print_temp(struct seq_file *s, const char *prefix, s32 t) 133362306a36Sopenharmony_ci{ 133462306a36Sopenharmony_ci switch (t) { 133562306a36Sopenharmony_ci case 0: 133662306a36Sopenharmony_ci case WMI_INVALID_TEMPERATURE: 133762306a36Sopenharmony_ci seq_printf(s, "%s N/A\n", prefix); 133862306a36Sopenharmony_ci break; 133962306a36Sopenharmony_ci default: 134062306a36Sopenharmony_ci seq_printf(s, "%s %s%d.%03d\n", prefix, (t < 0 ? "-" : ""), 134162306a36Sopenharmony_ci abs(t / 1000), abs(t % 1000)); 134262306a36Sopenharmony_ci break; 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistatic int temp_show(struct seq_file *s, void *data) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 134962306a36Sopenharmony_ci int rc, i; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci if (test_bit(WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF, 135262306a36Sopenharmony_ci wil->fw_capabilities)) { 135362306a36Sopenharmony_ci struct wmi_temp_sense_all_done_event sense_all_evt; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci wil_dbg_misc(wil, 135662306a36Sopenharmony_ci "WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is supported"); 135762306a36Sopenharmony_ci rc = wmi_get_all_temperatures(wil, &sense_all_evt); 135862306a36Sopenharmony_ci if (rc) { 135962306a36Sopenharmony_ci seq_puts(s, "Failed\n"); 136062306a36Sopenharmony_ci return 0; 136162306a36Sopenharmony_ci } 136262306a36Sopenharmony_ci print_temp(s, "T_mac =", 136362306a36Sopenharmony_ci le32_to_cpu(sense_all_evt.baseband_t1000)); 136462306a36Sopenharmony_ci seq_printf(s, "Connected RFs [0x%08x]\n", 136562306a36Sopenharmony_ci sense_all_evt.rf_bitmap); 136662306a36Sopenharmony_ci for (i = 0; i < WMI_MAX_XIF_PORTS_NUM; i++) { 136762306a36Sopenharmony_ci seq_printf(s, "RF[%d] = ", i); 136862306a36Sopenharmony_ci print_temp(s, "", 136962306a36Sopenharmony_ci le32_to_cpu(sense_all_evt.rf_t1000[i])); 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci } else { 137262306a36Sopenharmony_ci s32 t_m, t_r; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci wil_dbg_misc(wil, 137562306a36Sopenharmony_ci "WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is not supported"); 137662306a36Sopenharmony_ci rc = wmi_get_temperature(wil, &t_m, &t_r); 137762306a36Sopenharmony_ci if (rc) { 137862306a36Sopenharmony_ci seq_puts(s, "Failed\n"); 137962306a36Sopenharmony_ci return 0; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci print_temp(s, "T_mac =", t_m); 138262306a36Sopenharmony_ci print_temp(s, "T_radio =", t_r); 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci return 0; 138562306a36Sopenharmony_ci} 138662306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(temp); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci/*---------link------------*/ 138962306a36Sopenharmony_cistatic int link_show(struct seq_file *s, void *data) 139062306a36Sopenharmony_ci{ 139162306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 139262306a36Sopenharmony_ci struct station_info *sinfo; 139362306a36Sopenharmony_ci int i, rc = 0; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); 139662306a36Sopenharmony_ci if (!sinfo) 139762306a36Sopenharmony_ci return -ENOMEM; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 140062306a36Sopenharmony_ci struct wil_sta_info *p = &wil->sta[i]; 140162306a36Sopenharmony_ci char *status = "unknown"; 140262306a36Sopenharmony_ci struct wil6210_vif *vif; 140362306a36Sopenharmony_ci u8 mid; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci switch (p->status) { 140662306a36Sopenharmony_ci case wil_sta_unused: 140762306a36Sopenharmony_ci status = "unused "; 140862306a36Sopenharmony_ci break; 140962306a36Sopenharmony_ci case wil_sta_conn_pending: 141062306a36Sopenharmony_ci status = "pending "; 141162306a36Sopenharmony_ci break; 141262306a36Sopenharmony_ci case wil_sta_connected: 141362306a36Sopenharmony_ci status = "connected"; 141462306a36Sopenharmony_ci break; 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX; 141762306a36Sopenharmony_ci seq_printf(s, "[%d][MID %d] %pM %s\n", 141862306a36Sopenharmony_ci i, mid, p->addr, status); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci if (p->status != wil_sta_connected) 142162306a36Sopenharmony_ci continue; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci vif = (mid < GET_MAX_VIFS(wil)) ? wil->vifs[mid] : NULL; 142462306a36Sopenharmony_ci if (vif) { 142562306a36Sopenharmony_ci rc = wil_cid_fill_sinfo(vif, i, sinfo); 142662306a36Sopenharmony_ci if (rc) 142762306a36Sopenharmony_ci goto out; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci seq_printf(s, " Tx_mcs = %s\n", 143062306a36Sopenharmony_ci WIL_EXTENDED_MCS_CHECK(sinfo->txrate.mcs)); 143162306a36Sopenharmony_ci seq_printf(s, " Rx_mcs = %s\n", 143262306a36Sopenharmony_ci WIL_EXTENDED_MCS_CHECK(sinfo->rxrate.mcs)); 143362306a36Sopenharmony_ci seq_printf(s, " SQ = %d\n", sinfo->signal); 143462306a36Sopenharmony_ci } else { 143562306a36Sopenharmony_ci seq_puts(s, " INVALID MID\n"); 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ciout: 144062306a36Sopenharmony_ci kfree(sinfo); 144162306a36Sopenharmony_ci return rc; 144262306a36Sopenharmony_ci} 144362306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(link); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci/*---------info------------*/ 144662306a36Sopenharmony_cistatic int info_show(struct seq_file *s, void *data) 144762306a36Sopenharmony_ci{ 144862306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 144962306a36Sopenharmony_ci struct net_device *ndev = wil->main_ndev; 145062306a36Sopenharmony_ci int is_ac = power_supply_is_system_supplied(); 145162306a36Sopenharmony_ci int rx = atomic_xchg(&wil->isr_count_rx, 0); 145262306a36Sopenharmony_ci int tx = atomic_xchg(&wil->isr_count_tx, 0); 145362306a36Sopenharmony_ci static ulong rxf_old, txf_old; 145462306a36Sopenharmony_ci ulong rxf = ndev->stats.rx_packets; 145562306a36Sopenharmony_ci ulong txf = ndev->stats.tx_packets; 145662306a36Sopenharmony_ci unsigned int i; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci /* >0 : AC; 0 : battery; <0 : error */ 145962306a36Sopenharmony_ci seq_printf(s, "AC powered : %d\n", is_ac); 146062306a36Sopenharmony_ci seq_printf(s, "Rx irqs:packets : %8d : %8ld\n", rx, rxf - rxf_old); 146162306a36Sopenharmony_ci seq_printf(s, "Tx irqs:packets : %8d : %8ld\n", tx, txf - txf_old); 146262306a36Sopenharmony_ci rxf_old = rxf; 146362306a36Sopenharmony_ci txf_old = txf; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci#define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \ 146662306a36Sopenharmony_ci " " __stringify(x) : "" 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci for (i = 0; i < ndev->num_tx_queues; i++) { 146962306a36Sopenharmony_ci struct netdev_queue *txq = netdev_get_tx_queue(ndev, i); 147062306a36Sopenharmony_ci unsigned long state = txq->state; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci seq_printf(s, "Tx queue[%i] state : 0x%lx%s%s%s\n", i, state, 147362306a36Sopenharmony_ci CHECK_QSTATE(DRV_XOFF), 147462306a36Sopenharmony_ci CHECK_QSTATE(STACK_XOFF), 147562306a36Sopenharmony_ci CHECK_QSTATE(FROZEN) 147662306a36Sopenharmony_ci ); 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci#undef CHECK_QSTATE 147962306a36Sopenharmony_ci return 0; 148062306a36Sopenharmony_ci} 148162306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(info); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci/*---------recovery------------*/ 148462306a36Sopenharmony_ci/* mode = [manual|auto] 148562306a36Sopenharmony_ci * state = [idle|pending|running] 148662306a36Sopenharmony_ci */ 148762306a36Sopenharmony_cistatic ssize_t wil_read_file_recovery(struct file *file, char __user *user_buf, 148862306a36Sopenharmony_ci size_t count, loff_t *ppos) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 149162306a36Sopenharmony_ci char buf[80]; 149262306a36Sopenharmony_ci int n; 149362306a36Sopenharmony_ci static const char * const sstate[] = {"idle", "pending", "running"}; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci n = snprintf(buf, sizeof(buf), "mode = %s\nstate = %s\n", 149662306a36Sopenharmony_ci no_fw_recovery ? "manual" : "auto", 149762306a36Sopenharmony_ci sstate[wil->recovery_state]); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci n = min_t(int, n, sizeof(buf)); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, 150262306a36Sopenharmony_ci buf, n); 150362306a36Sopenharmony_ci} 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_cistatic ssize_t wil_write_file_recovery(struct file *file, 150662306a36Sopenharmony_ci const char __user *buf_, 150762306a36Sopenharmony_ci size_t count, loff_t *ppos) 150862306a36Sopenharmony_ci{ 150962306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 151062306a36Sopenharmony_ci static const char run_command[] = "run"; 151162306a36Sopenharmony_ci char buf[sizeof(run_command) + 1]; /* to detect "runx" */ 151262306a36Sopenharmony_ci ssize_t rc; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci if (wil->recovery_state != fw_recovery_pending) { 151562306a36Sopenharmony_ci wil_err(wil, "No recovery pending\n"); 151662306a36Sopenharmony_ci return -EINVAL; 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci if (*ppos != 0) { 152062306a36Sopenharmony_ci wil_err(wil, "Offset [%d]\n", (int)*ppos); 152162306a36Sopenharmony_ci return -EINVAL; 152262306a36Sopenharmony_ci } 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci if (count > sizeof(buf)) { 152562306a36Sopenharmony_ci wil_err(wil, "Input too long, len = %d\n", (int)count); 152662306a36Sopenharmony_ci return -EINVAL; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, buf_, count); 153062306a36Sopenharmony_ci if (rc < 0) 153162306a36Sopenharmony_ci return rc; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci buf[rc] = '\0'; 153462306a36Sopenharmony_ci if (0 == strcmp(buf, run_command)) 153562306a36Sopenharmony_ci wil_set_recovery_state(wil, fw_recovery_running); 153662306a36Sopenharmony_ci else 153762306a36Sopenharmony_ci wil_err(wil, "Bad recovery command \"%s\"\n", buf); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci return rc; 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_cistatic const struct file_operations fops_recovery = { 154362306a36Sopenharmony_ci .read = wil_read_file_recovery, 154462306a36Sopenharmony_ci .write = wil_write_file_recovery, 154562306a36Sopenharmony_ci .open = simple_open, 154662306a36Sopenharmony_ci}; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci/*---------Station matrix------------*/ 154962306a36Sopenharmony_cistatic void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r) 155062306a36Sopenharmony_ci{ 155162306a36Sopenharmony_ci int i; 155262306a36Sopenharmony_ci u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size; 155362306a36Sopenharmony_ci unsigned long long drop_dup = r->drop_dup, drop_old = r->drop_old; 155462306a36Sopenharmony_ci unsigned long long drop_dup_mcast = r->drop_dup_mcast; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci seq_printf(s, "([%2d]) 0x%03x [", r->buf_size, r->head_seq_num); 155762306a36Sopenharmony_ci for (i = 0; i < r->buf_size; i++) { 155862306a36Sopenharmony_ci if (i == index) 155962306a36Sopenharmony_ci seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|'); 156062306a36Sopenharmony_ci else 156162306a36Sopenharmony_ci seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_'); 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci seq_printf(s, 156462306a36Sopenharmony_ci "] total %llu drop %llu (dup %llu + old %llu + dup mcast %llu) last 0x%03x\n", 156562306a36Sopenharmony_ci r->total, drop_dup + drop_old + drop_dup_mcast, drop_dup, 156662306a36Sopenharmony_ci drop_old, drop_dup_mcast, r->ssn_last_drop); 156762306a36Sopenharmony_ci} 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_cistatic void wil_print_rxtid_crypto(struct seq_file *s, int tid, 157062306a36Sopenharmony_ci struct wil_tid_crypto_rx *c) 157162306a36Sopenharmony_ci{ 157262306a36Sopenharmony_ci int i; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 157562306a36Sopenharmony_ci struct wil_tid_crypto_rx_single *cc = &c->key_id[i]; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci if (cc->key_set) 157862306a36Sopenharmony_ci goto has_keys; 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci return; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_cihas_keys: 158362306a36Sopenharmony_ci if (tid < WIL_STA_TID_NUM) 158462306a36Sopenharmony_ci seq_printf(s, " [%2d] PN", tid); 158562306a36Sopenharmony_ci else 158662306a36Sopenharmony_ci seq_puts(s, " [GR] PN"); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 158962306a36Sopenharmony_ci struct wil_tid_crypto_rx_single *cc = &c->key_id[i]; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci seq_printf(s, " [%i%s]%6phN", i, cc->key_set ? "+" : "-", 159262306a36Sopenharmony_ci cc->pn); 159362306a36Sopenharmony_ci } 159462306a36Sopenharmony_ci seq_puts(s, "\n"); 159562306a36Sopenharmony_ci} 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_cistatic int sta_show(struct seq_file *s, void *data) 159862306a36Sopenharmony_ci__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) 159962306a36Sopenharmony_ci{ 160062306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 160162306a36Sopenharmony_ci int i, tid, mcs; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 160462306a36Sopenharmony_ci struct wil_sta_info *p = &wil->sta[i]; 160562306a36Sopenharmony_ci char *status = "unknown"; 160662306a36Sopenharmony_ci u8 aid = 0; 160762306a36Sopenharmony_ci u8 mid; 160862306a36Sopenharmony_ci bool sta_connected = false; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci switch (p->status) { 161162306a36Sopenharmony_ci case wil_sta_unused: 161262306a36Sopenharmony_ci status = "unused "; 161362306a36Sopenharmony_ci break; 161462306a36Sopenharmony_ci case wil_sta_conn_pending: 161562306a36Sopenharmony_ci status = "pending "; 161662306a36Sopenharmony_ci break; 161762306a36Sopenharmony_ci case wil_sta_connected: 161862306a36Sopenharmony_ci status = "connected"; 161962306a36Sopenharmony_ci aid = p->aid; 162062306a36Sopenharmony_ci break; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX; 162362306a36Sopenharmony_ci if (mid < GET_MAX_VIFS(wil)) { 162462306a36Sopenharmony_ci struct wil6210_vif *vif = wil->vifs[mid]; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci if (vif->wdev.iftype == NL80211_IFTYPE_STATION && 162762306a36Sopenharmony_ci p->status == wil_sta_connected) 162862306a36Sopenharmony_ci sta_connected = true; 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci /* print roam counter only for connected stations */ 163162306a36Sopenharmony_ci if (sta_connected) 163262306a36Sopenharmony_ci seq_printf(s, "[%d] %pM connected (roam counter %d) MID %d AID %d\n", 163362306a36Sopenharmony_ci i, p->addr, p->stats.ft_roams, mid, aid); 163462306a36Sopenharmony_ci else 163562306a36Sopenharmony_ci seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, 163662306a36Sopenharmony_ci p->addr, status, mid, aid); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci if (p->status == wil_sta_connected) { 163962306a36Sopenharmony_ci spin_lock_bh(&p->tid_rx_lock); 164062306a36Sopenharmony_ci for (tid = 0; tid < WIL_STA_TID_NUM; tid++) { 164162306a36Sopenharmony_ci struct wil_tid_ampdu_rx *r = p->tid_rx[tid]; 164262306a36Sopenharmony_ci struct wil_tid_crypto_rx *c = 164362306a36Sopenharmony_ci &p->tid_crypto_rx[tid]; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci if (r) { 164662306a36Sopenharmony_ci seq_printf(s, " [%2d] ", tid); 164762306a36Sopenharmony_ci wil_print_rxtid(s, r); 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci wil_print_rxtid_crypto(s, tid, c); 165162306a36Sopenharmony_ci } 165262306a36Sopenharmony_ci wil_print_rxtid_crypto(s, WIL_STA_TID_NUM, 165362306a36Sopenharmony_ci &p->group_crypto_rx); 165462306a36Sopenharmony_ci spin_unlock_bh(&p->tid_rx_lock); 165562306a36Sopenharmony_ci seq_printf(s, 165662306a36Sopenharmony_ci "Rx invalid frame: non-data %lu, short %lu, large %lu, replay %lu\n", 165762306a36Sopenharmony_ci p->stats.rx_non_data_frame, 165862306a36Sopenharmony_ci p->stats.rx_short_frame, 165962306a36Sopenharmony_ci p->stats.rx_large_frame, 166062306a36Sopenharmony_ci p->stats.rx_replay); 166162306a36Sopenharmony_ci seq_printf(s, 166262306a36Sopenharmony_ci "mic error %lu, key error %lu, amsdu error %lu, csum error %lu\n", 166362306a36Sopenharmony_ci p->stats.rx_mic_error, 166462306a36Sopenharmony_ci p->stats.rx_key_error, 166562306a36Sopenharmony_ci p->stats.rx_amsdu_error, 166662306a36Sopenharmony_ci p->stats.rx_csum_err); 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci seq_puts(s, "Rx/MCS:"); 166962306a36Sopenharmony_ci for (mcs = 0; mcs < ARRAY_SIZE(p->stats.rx_per_mcs); 167062306a36Sopenharmony_ci mcs++) 167162306a36Sopenharmony_ci seq_printf(s, " %lld", 167262306a36Sopenharmony_ci p->stats.rx_per_mcs[mcs]); 167362306a36Sopenharmony_ci seq_puts(s, "\n"); 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci return 0; 167862306a36Sopenharmony_ci} 167962306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(sta); 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_cistatic int mids_show(struct seq_file *s, void *data) 168262306a36Sopenharmony_ci{ 168362306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 168462306a36Sopenharmony_ci struct wil6210_vif *vif; 168562306a36Sopenharmony_ci struct net_device *ndev; 168662306a36Sopenharmony_ci int i; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci mutex_lock(&wil->vif_mutex); 168962306a36Sopenharmony_ci for (i = 0; i < GET_MAX_VIFS(wil); i++) { 169062306a36Sopenharmony_ci vif = wil->vifs[i]; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci if (vif) { 169362306a36Sopenharmony_ci ndev = vif_to_ndev(vif); 169462306a36Sopenharmony_ci seq_printf(s, "[%d] %pM %s\n", i, ndev->dev_addr, 169562306a36Sopenharmony_ci ndev->name); 169662306a36Sopenharmony_ci } else { 169762306a36Sopenharmony_ci seq_printf(s, "[%d] unused\n", i); 169862306a36Sopenharmony_ci } 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci return 0; 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mids); 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_cistatic int wil_tx_latency_debugfs_show(struct seq_file *s, void *data) 170762306a36Sopenharmony_ci__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) 170862306a36Sopenharmony_ci{ 170962306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 171062306a36Sopenharmony_ci int i, bin; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 171362306a36Sopenharmony_ci struct wil_sta_info *p = &wil->sta[i]; 171462306a36Sopenharmony_ci char *status = "unknown"; 171562306a36Sopenharmony_ci u8 aid = 0; 171662306a36Sopenharmony_ci u8 mid; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci if (!p->tx_latency_bins) 171962306a36Sopenharmony_ci continue; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci switch (p->status) { 172262306a36Sopenharmony_ci case wil_sta_unused: 172362306a36Sopenharmony_ci status = "unused "; 172462306a36Sopenharmony_ci break; 172562306a36Sopenharmony_ci case wil_sta_conn_pending: 172662306a36Sopenharmony_ci status = "pending "; 172762306a36Sopenharmony_ci break; 172862306a36Sopenharmony_ci case wil_sta_connected: 172962306a36Sopenharmony_ci status = "connected"; 173062306a36Sopenharmony_ci aid = p->aid; 173162306a36Sopenharmony_ci break; 173262306a36Sopenharmony_ci } 173362306a36Sopenharmony_ci mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX; 173462306a36Sopenharmony_ci seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, p->addr, status, 173562306a36Sopenharmony_ci mid, aid); 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci if (p->status == wil_sta_connected) { 173862306a36Sopenharmony_ci u64 num_packets = 0; 173962306a36Sopenharmony_ci u64 tx_latency_avg = p->stats.tx_latency_total_us; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci seq_puts(s, "Tx/Latency bin:"); 174262306a36Sopenharmony_ci for (bin = 0; bin < WIL_NUM_LATENCY_BINS; bin++) { 174362306a36Sopenharmony_ci seq_printf(s, " %lld", 174462306a36Sopenharmony_ci p->tx_latency_bins[bin]); 174562306a36Sopenharmony_ci num_packets += p->tx_latency_bins[bin]; 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci seq_puts(s, "\n"); 174862306a36Sopenharmony_ci if (!num_packets) 174962306a36Sopenharmony_ci continue; 175062306a36Sopenharmony_ci do_div(tx_latency_avg, num_packets); 175162306a36Sopenharmony_ci seq_printf(s, "Tx/Latency min/avg/max (us): %d/%lld/%d", 175262306a36Sopenharmony_ci p->stats.tx_latency_min_us, 175362306a36Sopenharmony_ci tx_latency_avg, 175462306a36Sopenharmony_ci p->stats.tx_latency_max_us); 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci seq_puts(s, "\n"); 175762306a36Sopenharmony_ci } 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci return 0; 176162306a36Sopenharmony_ci} 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_cistatic int wil_tx_latency_seq_open(struct inode *inode, struct file *file) 176462306a36Sopenharmony_ci{ 176562306a36Sopenharmony_ci return single_open(file, wil_tx_latency_debugfs_show, 176662306a36Sopenharmony_ci inode->i_private); 176762306a36Sopenharmony_ci} 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_cistatic ssize_t wil_tx_latency_write(struct file *file, const char __user *buf, 177062306a36Sopenharmony_ci size_t len, loff_t *ppos) 177162306a36Sopenharmony_ci{ 177262306a36Sopenharmony_ci struct seq_file *s = file->private_data; 177362306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 177462306a36Sopenharmony_ci int val, rc, i; 177562306a36Sopenharmony_ci bool enable; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci rc = kstrtoint_from_user(buf, len, 0, &val); 177862306a36Sopenharmony_ci if (rc) { 177962306a36Sopenharmony_ci wil_err(wil, "Invalid argument\n"); 178062306a36Sopenharmony_ci return rc; 178162306a36Sopenharmony_ci } 178262306a36Sopenharmony_ci if (val == 1) 178362306a36Sopenharmony_ci /* default resolution */ 178462306a36Sopenharmony_ci val = 500; 178562306a36Sopenharmony_ci if (val && (val < 50 || val > 1000)) { 178662306a36Sopenharmony_ci wil_err(wil, "Invalid resolution %d\n", val); 178762306a36Sopenharmony_ci return -EINVAL; 178862306a36Sopenharmony_ci } 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci enable = !!val; 179162306a36Sopenharmony_ci if (wil->tx_latency == enable) 179262306a36Sopenharmony_ci return len; 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci wil_info(wil, "%s TX latency measurements (resolution %dusec)\n", 179562306a36Sopenharmony_ci enable ? "Enabling" : "Disabling", val); 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci if (enable) { 179862306a36Sopenharmony_ci size_t sz = sizeof(u64) * WIL_NUM_LATENCY_BINS; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci wil->tx_latency_res = val; 180162306a36Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 180262306a36Sopenharmony_ci struct wil_sta_info *sta = &wil->sta[i]; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci kfree(sta->tx_latency_bins); 180562306a36Sopenharmony_ci sta->tx_latency_bins = kzalloc(sz, GFP_KERNEL); 180662306a36Sopenharmony_ci if (!sta->tx_latency_bins) 180762306a36Sopenharmony_ci return -ENOMEM; 180862306a36Sopenharmony_ci sta->stats.tx_latency_min_us = U32_MAX; 180962306a36Sopenharmony_ci sta->stats.tx_latency_max_us = 0; 181062306a36Sopenharmony_ci sta->stats.tx_latency_total_us = 0; 181162306a36Sopenharmony_ci } 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci wil->tx_latency = enable; 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci return len; 181662306a36Sopenharmony_ci} 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_cistatic const struct file_operations fops_tx_latency = { 181962306a36Sopenharmony_ci .open = wil_tx_latency_seq_open, 182062306a36Sopenharmony_ci .release = single_release, 182162306a36Sopenharmony_ci .read = seq_read, 182262306a36Sopenharmony_ci .write = wil_tx_latency_write, 182362306a36Sopenharmony_ci .llseek = seq_lseek, 182462306a36Sopenharmony_ci}; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_cistatic void wil_link_stats_print_basic(struct wil6210_vif *vif, 182762306a36Sopenharmony_ci struct seq_file *s, 182862306a36Sopenharmony_ci struct wmi_link_stats_basic *basic) 182962306a36Sopenharmony_ci{ 183062306a36Sopenharmony_ci char per[5] = "?"; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci if (basic->per_average != 0xff) 183362306a36Sopenharmony_ci snprintf(per, sizeof(per), "%d%%", basic->per_average); 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci seq_printf(s, "CID %d {\n" 183662306a36Sopenharmony_ci "\tTxMCS %s TxTpt %d\n" 183762306a36Sopenharmony_ci "\tGoodput(rx:tx) %d:%d\n" 183862306a36Sopenharmony_ci "\tRxBcastFrames %d\n" 183962306a36Sopenharmony_ci "\tRSSI %d SQI %d SNR %d PER %s\n" 184062306a36Sopenharmony_ci "\tRx RFC %d Ant num %d\n" 184162306a36Sopenharmony_ci "\tSectors(rx:tx) my %d:%d peer %d:%d\n" 184262306a36Sopenharmony_ci "}\n", 184362306a36Sopenharmony_ci basic->cid, 184462306a36Sopenharmony_ci WIL_EXTENDED_MCS_CHECK(basic->bf_mcs), 184562306a36Sopenharmony_ci le32_to_cpu(basic->tx_tpt), 184662306a36Sopenharmony_ci le32_to_cpu(basic->rx_goodput), 184762306a36Sopenharmony_ci le32_to_cpu(basic->tx_goodput), 184862306a36Sopenharmony_ci le32_to_cpu(basic->rx_bcast_frames), 184962306a36Sopenharmony_ci basic->rssi, basic->sqi, basic->snr, per, 185062306a36Sopenharmony_ci basic->selected_rfc, basic->rx_effective_ant_num, 185162306a36Sopenharmony_ci basic->my_rx_sector, basic->my_tx_sector, 185262306a36Sopenharmony_ci basic->other_rx_sector, basic->other_tx_sector); 185362306a36Sopenharmony_ci} 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_cistatic void wil_link_stats_print_global(struct wil6210_priv *wil, 185662306a36Sopenharmony_ci struct seq_file *s, 185762306a36Sopenharmony_ci struct wmi_link_stats_global *global) 185862306a36Sopenharmony_ci{ 185962306a36Sopenharmony_ci seq_printf(s, "Frames(rx:tx) %d:%d\n" 186062306a36Sopenharmony_ci "BA Frames(rx:tx) %d:%d\n" 186162306a36Sopenharmony_ci "Beacons %d\n" 186262306a36Sopenharmony_ci "Rx Errors (MIC:CRC) %d:%d\n" 186362306a36Sopenharmony_ci "Tx Errors (no ack) %d\n", 186462306a36Sopenharmony_ci le32_to_cpu(global->rx_frames), 186562306a36Sopenharmony_ci le32_to_cpu(global->tx_frames), 186662306a36Sopenharmony_ci le32_to_cpu(global->rx_ba_frames), 186762306a36Sopenharmony_ci le32_to_cpu(global->tx_ba_frames), 186862306a36Sopenharmony_ci le32_to_cpu(global->tx_beacons), 186962306a36Sopenharmony_ci le32_to_cpu(global->rx_mic_errors), 187062306a36Sopenharmony_ci le32_to_cpu(global->rx_crc_errors), 187162306a36Sopenharmony_ci le32_to_cpu(global->tx_fail_no_ack)); 187262306a36Sopenharmony_ci} 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_cistatic void wil_link_stats_debugfs_show_vif(struct wil6210_vif *vif, 187562306a36Sopenharmony_ci struct seq_file *s) 187662306a36Sopenharmony_ci{ 187762306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 187862306a36Sopenharmony_ci struct wmi_link_stats_basic *stats; 187962306a36Sopenharmony_ci int i; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci if (!vif->fw_stats_ready) { 188262306a36Sopenharmony_ci seq_puts(s, "no statistics\n"); 188362306a36Sopenharmony_ci return; 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci seq_printf(s, "TSF %lld\n", vif->fw_stats_tsf); 188762306a36Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) { 188862306a36Sopenharmony_ci if (wil->sta[i].status == wil_sta_unused) 188962306a36Sopenharmony_ci continue; 189062306a36Sopenharmony_ci if (wil->sta[i].mid != vif->mid) 189162306a36Sopenharmony_ci continue; 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci stats = &wil->sta[i].fw_stats_basic; 189462306a36Sopenharmony_ci wil_link_stats_print_basic(vif, s, stats); 189562306a36Sopenharmony_ci } 189662306a36Sopenharmony_ci} 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_cistatic int wil_link_stats_debugfs_show(struct seq_file *s, void *data) 189962306a36Sopenharmony_ci{ 190062306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 190162306a36Sopenharmony_ci struct wil6210_vif *vif; 190262306a36Sopenharmony_ci int i, rc; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci rc = mutex_lock_interruptible(&wil->vif_mutex); 190562306a36Sopenharmony_ci if (rc) 190662306a36Sopenharmony_ci return rc; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci /* iterate over all MIDs and show per-cid statistics. Then show the 190962306a36Sopenharmony_ci * global statistics 191062306a36Sopenharmony_ci */ 191162306a36Sopenharmony_ci for (i = 0; i < GET_MAX_VIFS(wil); i++) { 191262306a36Sopenharmony_ci vif = wil->vifs[i]; 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci seq_printf(s, "MID %d ", i); 191562306a36Sopenharmony_ci if (!vif) { 191662306a36Sopenharmony_ci seq_puts(s, "unused\n"); 191762306a36Sopenharmony_ci continue; 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci wil_link_stats_debugfs_show_vif(vif, s); 192162306a36Sopenharmony_ci } 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci return 0; 192662306a36Sopenharmony_ci} 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_cistatic int wil_link_stats_seq_open(struct inode *inode, struct file *file) 192962306a36Sopenharmony_ci{ 193062306a36Sopenharmony_ci return single_open(file, wil_link_stats_debugfs_show, inode->i_private); 193162306a36Sopenharmony_ci} 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_cistatic ssize_t wil_link_stats_write(struct file *file, const char __user *buf, 193462306a36Sopenharmony_ci size_t len, loff_t *ppos) 193562306a36Sopenharmony_ci{ 193662306a36Sopenharmony_ci struct seq_file *s = file->private_data; 193762306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 193862306a36Sopenharmony_ci int cid, interval, rc, i; 193962306a36Sopenharmony_ci struct wil6210_vif *vif; 194062306a36Sopenharmony_ci char *kbuf = kmalloc(len + 1, GFP_KERNEL); 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci if (!kbuf) 194362306a36Sopenharmony_ci return -ENOMEM; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); 194662306a36Sopenharmony_ci if (rc != len) { 194762306a36Sopenharmony_ci kfree(kbuf); 194862306a36Sopenharmony_ci return rc >= 0 ? -EIO : rc; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci kbuf[len] = '\0'; 195262306a36Sopenharmony_ci /* specify cid (use -1 for all cids) and snapshot interval in ms */ 195362306a36Sopenharmony_ci rc = sscanf(kbuf, "%d %d", &cid, &interval); 195462306a36Sopenharmony_ci kfree(kbuf); 195562306a36Sopenharmony_ci if (rc < 0) 195662306a36Sopenharmony_ci return rc; 195762306a36Sopenharmony_ci if (rc < 2 || interval < 0) 195862306a36Sopenharmony_ci return -EINVAL; 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci wil_info(wil, "request link statistics, cid %d interval %d\n", 196162306a36Sopenharmony_ci cid, interval); 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci rc = mutex_lock_interruptible(&wil->vif_mutex); 196462306a36Sopenharmony_ci if (rc) 196562306a36Sopenharmony_ci return rc; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci for (i = 0; i < GET_MAX_VIFS(wil); i++) { 196862306a36Sopenharmony_ci vif = wil->vifs[i]; 196962306a36Sopenharmony_ci if (!vif) 197062306a36Sopenharmony_ci continue; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_BASIC, 197362306a36Sopenharmony_ci (cid == -1 ? 0xff : cid), interval); 197462306a36Sopenharmony_ci if (rc) 197562306a36Sopenharmony_ci wil_err(wil, "link statistics failed for mid %d\n", i); 197662306a36Sopenharmony_ci } 197762306a36Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci return len; 198062306a36Sopenharmony_ci} 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_cistatic const struct file_operations fops_link_stats = { 198362306a36Sopenharmony_ci .open = wil_link_stats_seq_open, 198462306a36Sopenharmony_ci .release = single_release, 198562306a36Sopenharmony_ci .read = seq_read, 198662306a36Sopenharmony_ci .write = wil_link_stats_write, 198762306a36Sopenharmony_ci .llseek = seq_lseek, 198862306a36Sopenharmony_ci}; 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_cistatic int 199162306a36Sopenharmony_ciwil_link_stats_global_debugfs_show(struct seq_file *s, void *data) 199262306a36Sopenharmony_ci{ 199362306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci if (!wil->fw_stats_global.ready) 199662306a36Sopenharmony_ci return 0; 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci seq_printf(s, "TSF %lld\n", wil->fw_stats_global.tsf); 199962306a36Sopenharmony_ci wil_link_stats_print_global(wil, s, &wil->fw_stats_global.stats); 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci return 0; 200262306a36Sopenharmony_ci} 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_cistatic int 200562306a36Sopenharmony_ciwil_link_stats_global_seq_open(struct inode *inode, struct file *file) 200662306a36Sopenharmony_ci{ 200762306a36Sopenharmony_ci return single_open(file, wil_link_stats_global_debugfs_show, 200862306a36Sopenharmony_ci inode->i_private); 200962306a36Sopenharmony_ci} 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_cistatic ssize_t 201262306a36Sopenharmony_ciwil_link_stats_global_write(struct file *file, const char __user *buf, 201362306a36Sopenharmony_ci size_t len, loff_t *ppos) 201462306a36Sopenharmony_ci{ 201562306a36Sopenharmony_ci struct seq_file *s = file->private_data; 201662306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 201762306a36Sopenharmony_ci int interval, rc; 201862306a36Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci /* specify snapshot interval in ms */ 202162306a36Sopenharmony_ci rc = kstrtoint_from_user(buf, len, 0, &interval); 202262306a36Sopenharmony_ci if (rc || interval < 0) { 202362306a36Sopenharmony_ci wil_err(wil, "Invalid argument\n"); 202462306a36Sopenharmony_ci return -EINVAL; 202562306a36Sopenharmony_ci } 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci wil_info(wil, "request global link stats, interval %d\n", interval); 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_GLOBAL, 0, interval); 203062306a36Sopenharmony_ci if (rc) 203162306a36Sopenharmony_ci wil_err(wil, "global link stats failed %d\n", rc); 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci return rc ? rc : len; 203462306a36Sopenharmony_ci} 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_cistatic const struct file_operations fops_link_stats_global = { 203762306a36Sopenharmony_ci .open = wil_link_stats_global_seq_open, 203862306a36Sopenharmony_ci .release = single_release, 203962306a36Sopenharmony_ci .read = seq_read, 204062306a36Sopenharmony_ci .write = wil_link_stats_global_write, 204162306a36Sopenharmony_ci .llseek = seq_lseek, 204262306a36Sopenharmony_ci}; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_cistatic ssize_t wil_read_file_led_cfg(struct file *file, char __user *user_buf, 204562306a36Sopenharmony_ci size_t count, loff_t *ppos) 204662306a36Sopenharmony_ci{ 204762306a36Sopenharmony_ci char buf[80]; 204862306a36Sopenharmony_ci int n; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci n = snprintf(buf, sizeof(buf), 205162306a36Sopenharmony_ci "led_id is set to %d, echo 1 to enable, 0 to disable\n", 205262306a36Sopenharmony_ci led_id); 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci n = min_t(int, n, sizeof(buf)); 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, 205762306a36Sopenharmony_ci buf, n); 205862306a36Sopenharmony_ci} 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_cistatic ssize_t wil_write_file_led_cfg(struct file *file, 206162306a36Sopenharmony_ci const char __user *buf_, 206262306a36Sopenharmony_ci size_t count, loff_t *ppos) 206362306a36Sopenharmony_ci{ 206462306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 206562306a36Sopenharmony_ci int val; 206662306a36Sopenharmony_ci int rc; 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci rc = kstrtoint_from_user(buf_, count, 0, &val); 206962306a36Sopenharmony_ci if (rc) { 207062306a36Sopenharmony_ci wil_err(wil, "Invalid argument\n"); 207162306a36Sopenharmony_ci return rc; 207262306a36Sopenharmony_ci } 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci wil_info(wil, "%s led %d\n", val ? "Enabling" : "Disabling", led_id); 207562306a36Sopenharmony_ci rc = wmi_led_cfg(wil, val); 207662306a36Sopenharmony_ci if (rc) { 207762306a36Sopenharmony_ci wil_info(wil, "%s led %d failed\n", 207862306a36Sopenharmony_ci val ? "Enabling" : "Disabling", led_id); 207962306a36Sopenharmony_ci return rc; 208062306a36Sopenharmony_ci } 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci return count; 208362306a36Sopenharmony_ci} 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_cistatic const struct file_operations fops_led_cfg = { 208662306a36Sopenharmony_ci .read = wil_read_file_led_cfg, 208762306a36Sopenharmony_ci .write = wil_write_file_led_cfg, 208862306a36Sopenharmony_ci .open = simple_open, 208962306a36Sopenharmony_ci}; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci/* led_blink_time, write: 209262306a36Sopenharmony_ci * "<blink_on_slow> <blink_off_slow> <blink_on_med> <blink_off_med> <blink_on_fast> <blink_off_fast> 209362306a36Sopenharmony_ci */ 209462306a36Sopenharmony_cistatic ssize_t wil_write_led_blink_time(struct file *file, 209562306a36Sopenharmony_ci const char __user *buf, 209662306a36Sopenharmony_ci size_t len, loff_t *ppos) 209762306a36Sopenharmony_ci{ 209862306a36Sopenharmony_ci int rc; 209962306a36Sopenharmony_ci char *kbuf = kmalloc(len + 1, GFP_KERNEL); 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci if (!kbuf) 210262306a36Sopenharmony_ci return -ENOMEM; 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); 210562306a36Sopenharmony_ci if (rc != len) { 210662306a36Sopenharmony_ci kfree(kbuf); 210762306a36Sopenharmony_ci return rc >= 0 ? -EIO : rc; 210862306a36Sopenharmony_ci } 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci kbuf[len] = '\0'; 211162306a36Sopenharmony_ci rc = sscanf(kbuf, "%d %d %d %d %d %d", 211262306a36Sopenharmony_ci &led_blink_time[WIL_LED_TIME_SLOW].on_ms, 211362306a36Sopenharmony_ci &led_blink_time[WIL_LED_TIME_SLOW].off_ms, 211462306a36Sopenharmony_ci &led_blink_time[WIL_LED_TIME_MED].on_ms, 211562306a36Sopenharmony_ci &led_blink_time[WIL_LED_TIME_MED].off_ms, 211662306a36Sopenharmony_ci &led_blink_time[WIL_LED_TIME_FAST].on_ms, 211762306a36Sopenharmony_ci &led_blink_time[WIL_LED_TIME_FAST].off_ms); 211862306a36Sopenharmony_ci kfree(kbuf); 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci if (rc < 0) 212162306a36Sopenharmony_ci return rc; 212262306a36Sopenharmony_ci if (rc < 6) 212362306a36Sopenharmony_ci return -EINVAL; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci return len; 212662306a36Sopenharmony_ci} 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_cistatic ssize_t wil_read_led_blink_time(struct file *file, char __user *user_buf, 212962306a36Sopenharmony_ci size_t count, loff_t *ppos) 213062306a36Sopenharmony_ci{ 213162306a36Sopenharmony_ci static char text[400]; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci snprintf(text, sizeof(text), 213462306a36Sopenharmony_ci "To set led blink on/off time variables write:\n" 213562306a36Sopenharmony_ci "<blink_on_slow> <blink_off_slow> <blink_on_med> " 213662306a36Sopenharmony_ci "<blink_off_med> <blink_on_fast> <blink_off_fast>\n" 213762306a36Sopenharmony_ci "The current values are:\n" 213862306a36Sopenharmony_ci "%d %d %d %d %d %d\n", 213962306a36Sopenharmony_ci led_blink_time[WIL_LED_TIME_SLOW].on_ms, 214062306a36Sopenharmony_ci led_blink_time[WIL_LED_TIME_SLOW].off_ms, 214162306a36Sopenharmony_ci led_blink_time[WIL_LED_TIME_MED].on_ms, 214262306a36Sopenharmony_ci led_blink_time[WIL_LED_TIME_MED].off_ms, 214362306a36Sopenharmony_ci led_blink_time[WIL_LED_TIME_FAST].on_ms, 214462306a36Sopenharmony_ci led_blink_time[WIL_LED_TIME_FAST].off_ms); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, text, 214762306a36Sopenharmony_ci sizeof(text)); 214862306a36Sopenharmony_ci} 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_cistatic const struct file_operations fops_led_blink_time = { 215162306a36Sopenharmony_ci .read = wil_read_led_blink_time, 215262306a36Sopenharmony_ci .write = wil_write_led_blink_time, 215362306a36Sopenharmony_ci .open = simple_open, 215462306a36Sopenharmony_ci}; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci/*---------FW capabilities------------*/ 215762306a36Sopenharmony_cistatic int fw_capabilities_show(struct seq_file *s, void *data) 215862306a36Sopenharmony_ci{ 215962306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci seq_printf(s, "fw_capabilities : %*pb\n", WMI_FW_CAPABILITY_MAX, 216262306a36Sopenharmony_ci wil->fw_capabilities); 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci return 0; 216562306a36Sopenharmony_ci} 216662306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(fw_capabilities); 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci/*---------FW version------------*/ 216962306a36Sopenharmony_cistatic int fw_version_show(struct seq_file *s, void *data) 217062306a36Sopenharmony_ci{ 217162306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci if (wil->fw_version[0]) 217462306a36Sopenharmony_ci seq_printf(s, "%s\n", wil->fw_version); 217562306a36Sopenharmony_ci else 217662306a36Sopenharmony_ci seq_puts(s, "N/A\n"); 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci return 0; 217962306a36Sopenharmony_ci} 218062306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(fw_version); 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci/*---------suspend_stats---------*/ 218362306a36Sopenharmony_cistatic ssize_t wil_write_suspend_stats(struct file *file, 218462306a36Sopenharmony_ci const char __user *buf, 218562306a36Sopenharmony_ci size_t len, loff_t *ppos) 218662306a36Sopenharmony_ci{ 218762306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats)); 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci return len; 219262306a36Sopenharmony_ci} 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_cistatic ssize_t wil_read_suspend_stats(struct file *file, 219562306a36Sopenharmony_ci char __user *user_buf, 219662306a36Sopenharmony_ci size_t count, loff_t *ppos) 219762306a36Sopenharmony_ci{ 219862306a36Sopenharmony_ci struct wil6210_priv *wil = file->private_data; 219962306a36Sopenharmony_ci char *text; 220062306a36Sopenharmony_ci int n, ret, text_size = 500; 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci text = kmalloc(text_size, GFP_KERNEL); 220362306a36Sopenharmony_ci if (!text) 220462306a36Sopenharmony_ci return -ENOMEM; 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci n = snprintf(text, text_size, 220762306a36Sopenharmony_ci "Radio on suspend statistics:\n" 220862306a36Sopenharmony_ci "successful suspends:%ld failed suspends:%ld\n" 220962306a36Sopenharmony_ci "successful resumes:%ld failed resumes:%ld\n" 221062306a36Sopenharmony_ci "rejected by device:%ld\n" 221162306a36Sopenharmony_ci "Radio off suspend statistics:\n" 221262306a36Sopenharmony_ci "successful suspends:%ld failed suspends:%ld\n" 221362306a36Sopenharmony_ci "successful resumes:%ld failed resumes:%ld\n" 221462306a36Sopenharmony_ci "General statistics:\n" 221562306a36Sopenharmony_ci "rejected by host:%ld\n", 221662306a36Sopenharmony_ci wil->suspend_stats.r_on.successful_suspends, 221762306a36Sopenharmony_ci wil->suspend_stats.r_on.failed_suspends, 221862306a36Sopenharmony_ci wil->suspend_stats.r_on.successful_resumes, 221962306a36Sopenharmony_ci wil->suspend_stats.r_on.failed_resumes, 222062306a36Sopenharmony_ci wil->suspend_stats.rejected_by_device, 222162306a36Sopenharmony_ci wil->suspend_stats.r_off.successful_suspends, 222262306a36Sopenharmony_ci wil->suspend_stats.r_off.failed_suspends, 222362306a36Sopenharmony_ci wil->suspend_stats.r_off.successful_resumes, 222462306a36Sopenharmony_ci wil->suspend_stats.r_off.failed_resumes, 222562306a36Sopenharmony_ci wil->suspend_stats.rejected_by_host); 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci n = min_t(int, n, text_size); 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, text, n); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci kfree(text); 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci return ret; 223462306a36Sopenharmony_ci} 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_cistatic const struct file_operations fops_suspend_stats = { 223762306a36Sopenharmony_ci .read = wil_read_suspend_stats, 223862306a36Sopenharmony_ci .write = wil_write_suspend_stats, 223962306a36Sopenharmony_ci .open = simple_open, 224062306a36Sopenharmony_ci}; 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci/*---------compressed_rx_status---------*/ 224362306a36Sopenharmony_cistatic ssize_t wil_compressed_rx_status_write(struct file *file, 224462306a36Sopenharmony_ci const char __user *buf, 224562306a36Sopenharmony_ci size_t len, loff_t *ppos) 224662306a36Sopenharmony_ci{ 224762306a36Sopenharmony_ci struct seq_file *s = file->private_data; 224862306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 224962306a36Sopenharmony_ci int compressed_rx_status; 225062306a36Sopenharmony_ci int rc; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci rc = kstrtoint_from_user(buf, len, 0, &compressed_rx_status); 225362306a36Sopenharmony_ci if (rc) { 225462306a36Sopenharmony_ci wil_err(wil, "Invalid argument\n"); 225562306a36Sopenharmony_ci return rc; 225662306a36Sopenharmony_ci } 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci if (wil_has_active_ifaces(wil, true, false)) { 225962306a36Sopenharmony_ci wil_err(wil, "cannot change edma config after iface is up\n"); 226062306a36Sopenharmony_ci return -EPERM; 226162306a36Sopenharmony_ci } 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci wil_info(wil, "%sable compressed_rx_status\n", 226462306a36Sopenharmony_ci compressed_rx_status ? "En" : "Dis"); 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci wil->use_compressed_rx_status = compressed_rx_status; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci return len; 226962306a36Sopenharmony_ci} 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_cistatic int 227262306a36Sopenharmony_ciwil_compressed_rx_status_show(struct seq_file *s, void *data) 227362306a36Sopenharmony_ci{ 227462306a36Sopenharmony_ci struct wil6210_priv *wil = s->private; 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci seq_printf(s, "%d\n", wil->use_compressed_rx_status); 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci return 0; 227962306a36Sopenharmony_ci} 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_cistatic int 228262306a36Sopenharmony_ciwil_compressed_rx_status_seq_open(struct inode *inode, struct file *file) 228362306a36Sopenharmony_ci{ 228462306a36Sopenharmony_ci return single_open(file, wil_compressed_rx_status_show, 228562306a36Sopenharmony_ci inode->i_private); 228662306a36Sopenharmony_ci} 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_cistatic const struct file_operations fops_compressed_rx_status = { 228962306a36Sopenharmony_ci .open = wil_compressed_rx_status_seq_open, 229062306a36Sopenharmony_ci .release = single_release, 229162306a36Sopenharmony_ci .read = seq_read, 229262306a36Sopenharmony_ci .write = wil_compressed_rx_status_write, 229362306a36Sopenharmony_ci .llseek = seq_lseek, 229462306a36Sopenharmony_ci}; 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci/*----------------*/ 229762306a36Sopenharmony_cistatic void wil6210_debugfs_init_blobs(struct wil6210_priv *wil, 229862306a36Sopenharmony_ci struct dentry *dbg) 229962306a36Sopenharmony_ci{ 230062306a36Sopenharmony_ci int i; 230162306a36Sopenharmony_ci char name[32]; 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { 230462306a36Sopenharmony_ci struct wil_blob_wrapper *wil_blob = &wil->blobs[i]; 230562306a36Sopenharmony_ci struct debugfs_blob_wrapper *blob = &wil_blob->blob; 230662306a36Sopenharmony_ci const struct fw_map *map = &fw_mapping[i]; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci if (!map->name) 230962306a36Sopenharmony_ci continue; 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci wil_blob->wil = wil; 231262306a36Sopenharmony_ci blob->data = (void * __force)wil->csr + HOSTADDR(map->host); 231362306a36Sopenharmony_ci blob->size = map->to - map->from; 231462306a36Sopenharmony_ci snprintf(name, sizeof(name), "blob_%s", map->name); 231562306a36Sopenharmony_ci wil_debugfs_create_ioblob(name, 0444, dbg, wil_blob); 231662306a36Sopenharmony_ci } 231762306a36Sopenharmony_ci} 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci/* misc files */ 232062306a36Sopenharmony_cistatic const struct { 232162306a36Sopenharmony_ci const char *name; 232262306a36Sopenharmony_ci umode_t mode; 232362306a36Sopenharmony_ci const struct file_operations *fops; 232462306a36Sopenharmony_ci} dbg_files[] = { 232562306a36Sopenharmony_ci {"mbox", 0444, &mbox_fops}, 232662306a36Sopenharmony_ci {"rings", 0444, &ring_fops}, 232762306a36Sopenharmony_ci {"stations", 0444, &sta_fops}, 232862306a36Sopenharmony_ci {"mids", 0444, &mids_fops}, 232962306a36Sopenharmony_ci {"desc", 0444, &txdesc_fops}, 233062306a36Sopenharmony_ci {"bf", 0444, &bf_fops}, 233162306a36Sopenharmony_ci {"mem_val", 0644, &memread_fops}, 233262306a36Sopenharmony_ci {"rxon", 0244, &fops_rxon}, 233362306a36Sopenharmony_ci {"tx_mgmt", 0244, &fops_txmgmt}, 233462306a36Sopenharmony_ci {"wmi_send", 0244, &fops_wmi}, 233562306a36Sopenharmony_ci {"back", 0644, &fops_back}, 233662306a36Sopenharmony_ci {"pmccfg", 0644, &fops_pmccfg}, 233762306a36Sopenharmony_ci {"pmcdata", 0444, &fops_pmcdata}, 233862306a36Sopenharmony_ci {"pmcring", 0444, &fops_pmcring}, 233962306a36Sopenharmony_ci {"temp", 0444, &temp_fops}, 234062306a36Sopenharmony_ci {"link", 0444, &link_fops}, 234162306a36Sopenharmony_ci {"info", 0444, &info_fops}, 234262306a36Sopenharmony_ci {"recovery", 0644, &fops_recovery}, 234362306a36Sopenharmony_ci {"led_cfg", 0644, &fops_led_cfg}, 234462306a36Sopenharmony_ci {"led_blink_time", 0644, &fops_led_blink_time}, 234562306a36Sopenharmony_ci {"fw_capabilities", 0444, &fw_capabilities_fops}, 234662306a36Sopenharmony_ci {"fw_version", 0444, &fw_version_fops}, 234762306a36Sopenharmony_ci {"suspend_stats", 0644, &fops_suspend_stats}, 234862306a36Sopenharmony_ci {"compressed_rx_status", 0644, &fops_compressed_rx_status}, 234962306a36Sopenharmony_ci {"srings", 0444, &srings_fops}, 235062306a36Sopenharmony_ci {"status_msg", 0444, &status_msg_fops}, 235162306a36Sopenharmony_ci {"rx_buff_mgmt", 0444, &rx_buff_mgmt_fops}, 235262306a36Sopenharmony_ci {"tx_latency", 0644, &fops_tx_latency}, 235362306a36Sopenharmony_ci {"link_stats", 0644, &fops_link_stats}, 235462306a36Sopenharmony_ci {"link_stats_global", 0644, &fops_link_stats_global}, 235562306a36Sopenharmony_ci {"rbufcap", 0244, &fops_rbufcap}, 235662306a36Sopenharmony_ci}; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_cistatic void wil6210_debugfs_init_files(struct wil6210_priv *wil, 235962306a36Sopenharmony_ci struct dentry *dbg) 236062306a36Sopenharmony_ci{ 236162306a36Sopenharmony_ci int i; 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dbg_files); i++) 236462306a36Sopenharmony_ci debugfs_create_file(dbg_files[i].name, dbg_files[i].mode, dbg, 236562306a36Sopenharmony_ci wil, dbg_files[i].fops); 236662306a36Sopenharmony_ci} 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci/* interrupt control blocks */ 236962306a36Sopenharmony_cistatic const struct { 237062306a36Sopenharmony_ci const char *name; 237162306a36Sopenharmony_ci u32 icr_off; 237262306a36Sopenharmony_ci} dbg_icr[] = { 237362306a36Sopenharmony_ci {"USER_ICR", HOSTADDR(RGF_USER_USER_ICR)}, 237462306a36Sopenharmony_ci {"DMA_EP_TX_ICR", HOSTADDR(RGF_DMA_EP_TX_ICR)}, 237562306a36Sopenharmony_ci {"DMA_EP_RX_ICR", HOSTADDR(RGF_DMA_EP_RX_ICR)}, 237662306a36Sopenharmony_ci {"DMA_EP_MISC_ICR", HOSTADDR(RGF_DMA_EP_MISC_ICR)}, 237762306a36Sopenharmony_ci}; 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_cistatic void wil6210_debugfs_init_isr(struct wil6210_priv *wil, 238062306a36Sopenharmony_ci struct dentry *dbg) 238162306a36Sopenharmony_ci{ 238262306a36Sopenharmony_ci int i; 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dbg_icr); i++) 238562306a36Sopenharmony_ci wil6210_debugfs_create_ISR(wil, dbg_icr[i].name, dbg, 238662306a36Sopenharmony_ci dbg_icr[i].icr_off); 238762306a36Sopenharmony_ci} 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci#define WIL_FIELD(name, mode, type) { __stringify(name), mode, \ 239062306a36Sopenharmony_ci offsetof(struct wil6210_priv, name), type} 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci/* fields in struct wil6210_priv */ 239362306a36Sopenharmony_cistatic const struct dbg_off dbg_wil_off[] = { 239462306a36Sopenharmony_ci WIL_FIELD(status[0], 0644, doff_ulong), 239562306a36Sopenharmony_ci WIL_FIELD(hw_version, 0444, doff_x32), 239662306a36Sopenharmony_ci WIL_FIELD(recovery_count, 0444, doff_u32), 239762306a36Sopenharmony_ci WIL_FIELD(discovery_mode, 0644, doff_u8), 239862306a36Sopenharmony_ci WIL_FIELD(chip_revision, 0444, doff_u8), 239962306a36Sopenharmony_ci WIL_FIELD(abft_len, 0644, doff_u8), 240062306a36Sopenharmony_ci WIL_FIELD(wakeup_trigger, 0644, doff_u8), 240162306a36Sopenharmony_ci WIL_FIELD(ring_idle_trsh, 0644, doff_u32), 240262306a36Sopenharmony_ci WIL_FIELD(num_rx_status_rings, 0644, doff_u8), 240362306a36Sopenharmony_ci WIL_FIELD(rx_status_ring_order, 0644, doff_u32), 240462306a36Sopenharmony_ci WIL_FIELD(tx_status_ring_order, 0644, doff_u32), 240562306a36Sopenharmony_ci WIL_FIELD(rx_buff_id_count, 0644, doff_u32), 240662306a36Sopenharmony_ci WIL_FIELD(amsdu_en, 0644, doff_u8), 240762306a36Sopenharmony_ci {}, 240862306a36Sopenharmony_ci}; 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_cistatic const struct dbg_off dbg_wil_regs[] = { 241162306a36Sopenharmony_ci {"RGF_MAC_MTRL_COUNTER_0", 0444, HOSTADDR(RGF_MAC_MTRL_COUNTER_0), 241262306a36Sopenharmony_ci doff_io32}, 241362306a36Sopenharmony_ci {"RGF_USER_USAGE_1", 0444, HOSTADDR(RGF_USER_USAGE_1), doff_io32}, 241462306a36Sopenharmony_ci {"RGF_USER_USAGE_2", 0444, HOSTADDR(RGF_USER_USAGE_2), doff_io32}, 241562306a36Sopenharmony_ci {}, 241662306a36Sopenharmony_ci}; 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci/* static parameters */ 241962306a36Sopenharmony_cistatic const struct dbg_off dbg_statics[] = { 242062306a36Sopenharmony_ci {"desc_index", 0644, (ulong)&dbg_txdesc_index, doff_u32}, 242162306a36Sopenharmony_ci {"ring_index", 0644, (ulong)&dbg_ring_index, doff_u32}, 242262306a36Sopenharmony_ci {"mem_addr", 0644, (ulong)&mem_addr, doff_u32}, 242362306a36Sopenharmony_ci {"led_polarity", 0644, (ulong)&led_polarity, doff_u8}, 242462306a36Sopenharmony_ci {"status_index", 0644, (ulong)&dbg_status_msg_index, doff_u32}, 242562306a36Sopenharmony_ci {"sring_index", 0644, (ulong)&dbg_sring_index, doff_u32}, 242662306a36Sopenharmony_ci {"drop_if_ring_full", 0644, (ulong)&drop_if_ring_full, doff_u8}, 242762306a36Sopenharmony_ci {}, 242862306a36Sopenharmony_ci}; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_cistatic const int dbg_off_count = 4 * (ARRAY_SIZE(isr_off) - 1) + 243162306a36Sopenharmony_ci ARRAY_SIZE(dbg_wil_regs) - 1 + 243262306a36Sopenharmony_ci ARRAY_SIZE(pseudo_isr_off) - 1 + 243362306a36Sopenharmony_ci ARRAY_SIZE(lgc_itr_cnt_off) - 1 + 243462306a36Sopenharmony_ci ARRAY_SIZE(tx_itr_cnt_off) - 1 + 243562306a36Sopenharmony_ci ARRAY_SIZE(rx_itr_cnt_off) - 1; 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ciint wil6210_debugfs_init(struct wil6210_priv *wil) 243862306a36Sopenharmony_ci{ 243962306a36Sopenharmony_ci struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, 244062306a36Sopenharmony_ci wil_to_wiphy(wil)->debugfsdir); 244162306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dbg)) 244262306a36Sopenharmony_ci return -ENODEV; 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci wil->dbg_data.data_arr = kcalloc(dbg_off_count, 244562306a36Sopenharmony_ci sizeof(struct wil_debugfs_iomem_data), 244662306a36Sopenharmony_ci GFP_KERNEL); 244762306a36Sopenharmony_ci if (!wil->dbg_data.data_arr) { 244862306a36Sopenharmony_ci debugfs_remove_recursive(dbg); 244962306a36Sopenharmony_ci wil->debug = NULL; 245062306a36Sopenharmony_ci return -ENOMEM; 245162306a36Sopenharmony_ci } 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci wil->dbg_data.iomem_data_count = 0; 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci wil_pmc_init(wil); 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ci wil6210_debugfs_init_files(wil, dbg); 245862306a36Sopenharmony_ci wil6210_debugfs_init_isr(wil, dbg); 245962306a36Sopenharmony_ci wil6210_debugfs_init_blobs(wil, dbg); 246062306a36Sopenharmony_ci wil6210_debugfs_init_offset(wil, dbg, wil, dbg_wil_off); 246162306a36Sopenharmony_ci wil6210_debugfs_init_offset(wil, dbg, (void * __force)wil->csr, 246262306a36Sopenharmony_ci dbg_wil_regs); 246362306a36Sopenharmony_ci wil6210_debugfs_init_offset(wil, dbg, NULL, dbg_statics); 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_ci wil6210_debugfs_create_pseudo_ISR(wil, dbg); 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci wil6210_debugfs_create_ITR_CNT(wil, dbg); 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci return 0; 247062306a36Sopenharmony_ci} 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_civoid wil6210_debugfs_remove(struct wil6210_priv *wil) 247362306a36Sopenharmony_ci{ 247462306a36Sopenharmony_ci int i; 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci debugfs_remove_recursive(wil->debug); 247762306a36Sopenharmony_ci wil->debug = NULL; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci kfree(wil->dbg_data.data_arr); 248062306a36Sopenharmony_ci for (i = 0; i < wil->max_assoc_sta; i++) 248162306a36Sopenharmony_ci kfree(wil->sta[i].tx_latency_bins); 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci /* free pmc memory without sending command to fw, as it will 248462306a36Sopenharmony_ci * be reset on the way down anyway 248562306a36Sopenharmony_ci */ 248662306a36Sopenharmony_ci wil_pmc_free(wil, false); 248762306a36Sopenharmony_ci} 2488