xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/ath/wil6210/debugfs.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: ISC
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
4*4882a593Smuzhiyun  * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/module.h>
8*4882a593Smuzhiyun #include <linux/debugfs.h>
9*4882a593Smuzhiyun #include <linux/seq_file.h>
10*4882a593Smuzhiyun #include <linux/pci.h>
11*4882a593Smuzhiyun #include <linux/rtnetlink.h>
12*4882a593Smuzhiyun #include <linux/power_supply.h>
13*4882a593Smuzhiyun #include "wil6210.h"
14*4882a593Smuzhiyun #include "wmi.h"
15*4882a593Smuzhiyun #include "txrx.h"
16*4882a593Smuzhiyun #include "pmc.h"
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun /* Nasty hack. Better have per device instances */
19*4882a593Smuzhiyun static u32 mem_addr;
20*4882a593Smuzhiyun static u32 dbg_txdesc_index;
21*4882a593Smuzhiyun static u32 dbg_ring_index; /* 24+ for Rx, 0..23 for Tx */
22*4882a593Smuzhiyun static u32 dbg_status_msg_index;
23*4882a593Smuzhiyun /* 0..wil->num_rx_status_rings-1 for Rx, wil->tx_sring_idx for Tx */
24*4882a593Smuzhiyun static u32 dbg_sring_index;
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun enum dbg_off_type {
27*4882a593Smuzhiyun 	doff_u32 = 0,
28*4882a593Smuzhiyun 	doff_x32 = 1,
29*4882a593Smuzhiyun 	doff_ulong = 2,
30*4882a593Smuzhiyun 	doff_io32 = 3,
31*4882a593Smuzhiyun 	doff_u8 = 4
32*4882a593Smuzhiyun };
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun /* offset to "wil" */
35*4882a593Smuzhiyun struct dbg_off {
36*4882a593Smuzhiyun 	const char *name;
37*4882a593Smuzhiyun 	umode_t mode;
38*4882a593Smuzhiyun 	ulong off;
39*4882a593Smuzhiyun 	enum dbg_off_type type;
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun 
wil_print_desc_edma(struct seq_file * s,struct wil6210_priv * wil,struct wil_ring * ring,char _s,char _h,int idx)42*4882a593Smuzhiyun static void wil_print_desc_edma(struct seq_file *s, struct wil6210_priv *wil,
43*4882a593Smuzhiyun 				struct wil_ring *ring,
44*4882a593Smuzhiyun 				char _s, char _h, int idx)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun 	u8 num_of_descs;
47*4882a593Smuzhiyun 	bool has_skb = false;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	if (ring->is_rx) {
50*4882a593Smuzhiyun 		struct wil_rx_enhanced_desc *rx_d =
51*4882a593Smuzhiyun 			(struct wil_rx_enhanced_desc *)
52*4882a593Smuzhiyun 			&ring->va[idx].rx.enhanced;
53*4882a593Smuzhiyun 		u16 buff_id = le16_to_cpu(rx_d->mac.buff_id);
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 		if (wil->rx_buff_mgmt.buff_arr &&
56*4882a593Smuzhiyun 		    wil_val_in_range(buff_id, 0, wil->rx_buff_mgmt.size))
57*4882a593Smuzhiyun 			has_skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb;
58*4882a593Smuzhiyun 		seq_printf(s, "%c", (has_skb) ? _h : _s);
59*4882a593Smuzhiyun 	} else {
60*4882a593Smuzhiyun 		struct wil_tx_enhanced_desc *d =
61*4882a593Smuzhiyun 			(struct wil_tx_enhanced_desc *)
62*4882a593Smuzhiyun 			&ring->va[idx].tx.enhanced;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 		num_of_descs = (u8)d->mac.d[2];
65*4882a593Smuzhiyun 		has_skb = ring->ctx && ring->ctx[idx].skb;
66*4882a593Smuzhiyun 		if (num_of_descs >= 1)
67*4882a593Smuzhiyun 			seq_printf(s, "%c", has_skb ? _h : _s);
68*4882a593Smuzhiyun 		else
69*4882a593Smuzhiyun 			/* num_of_descs == 0, it's a frag in a list of descs */
70*4882a593Smuzhiyun 			seq_printf(s, "%c", has_skb ? 'h' : _s);
71*4882a593Smuzhiyun 	}
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun 
wil_print_ring(struct seq_file * s,struct wil6210_priv * wil,const char * name,struct wil_ring * ring,char _s,char _h)74*4882a593Smuzhiyun static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil,
75*4882a593Smuzhiyun 			   const char *name, struct wil_ring *ring,
76*4882a593Smuzhiyun 			   char _s, char _h)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun 	void __iomem *x;
79*4882a593Smuzhiyun 	u32 v;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	seq_printf(s, "RING %s = {\n", name);
82*4882a593Smuzhiyun 	seq_printf(s, "  pa     = %pad\n", &ring->pa);
83*4882a593Smuzhiyun 	seq_printf(s, "  va     = 0x%p\n", ring->va);
84*4882a593Smuzhiyun 	seq_printf(s, "  size   = %d\n", ring->size);
85*4882a593Smuzhiyun 	if (wil->use_enhanced_dma_hw && ring->is_rx)
86*4882a593Smuzhiyun 		seq_printf(s, "  swtail = %u\n", *ring->edma_rx_swtail.va);
87*4882a593Smuzhiyun 	else
88*4882a593Smuzhiyun 		seq_printf(s, "  swtail = %d\n", ring->swtail);
89*4882a593Smuzhiyun 	seq_printf(s, "  swhead = %d\n", ring->swhead);
90*4882a593Smuzhiyun 	if (wil->use_enhanced_dma_hw) {
91*4882a593Smuzhiyun 		int ring_id = ring->is_rx ?
92*4882a593Smuzhiyun 			WIL_RX_DESC_RING_ID : ring - wil->ring_tx;
93*4882a593Smuzhiyun 		/* SUBQ_CONS is a table of 32 entries, one for each Q pair.
94*4882a593Smuzhiyun 		 * lower 16bits are for even ring_id and upper 16bits are for
95*4882a593Smuzhiyun 		 * odd ring_id
96*4882a593Smuzhiyun 		 */
97*4882a593Smuzhiyun 		x = wmi_addr(wil, RGF_DMA_SCM_SUBQ_CONS + 4 * (ring_id / 2));
98*4882a593Smuzhiyun 		v = readl_relaxed(x);
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 		v = (ring_id % 2 ? (v >> 16) : (v & 0xffff));
101*4882a593Smuzhiyun 		seq_printf(s, "  hwhead = %u\n", v);
102*4882a593Smuzhiyun 	}
103*4882a593Smuzhiyun 	seq_printf(s, "  hwtail = [0x%08x] -> ", ring->hwtail);
104*4882a593Smuzhiyun 	x = wmi_addr(wil, ring->hwtail);
105*4882a593Smuzhiyun 	if (x) {
106*4882a593Smuzhiyun 		v = readl(x);
107*4882a593Smuzhiyun 		seq_printf(s, "0x%08x = %d\n", v, v);
108*4882a593Smuzhiyun 	} else {
109*4882a593Smuzhiyun 		seq_puts(s, "???\n");
110*4882a593Smuzhiyun 	}
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	if (ring->va && (ring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) {
113*4882a593Smuzhiyun 		uint i;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 		for (i = 0; i < ring->size; i++) {
116*4882a593Smuzhiyun 			if ((i % 128) == 0 && i != 0)
117*4882a593Smuzhiyun 				seq_puts(s, "\n");
118*4882a593Smuzhiyun 			if (wil->use_enhanced_dma_hw) {
119*4882a593Smuzhiyun 				wil_print_desc_edma(s, wil, ring, _s, _h, i);
120*4882a593Smuzhiyun 			} else {
121*4882a593Smuzhiyun 				volatile struct vring_tx_desc *d =
122*4882a593Smuzhiyun 					&ring->va[i].tx.legacy;
123*4882a593Smuzhiyun 				seq_printf(s, "%c", (d->dma.status & BIT(0)) ?
124*4882a593Smuzhiyun 					   _s : (ring->ctx[i].skb ? _h : 'h'));
125*4882a593Smuzhiyun 			}
126*4882a593Smuzhiyun 		}
127*4882a593Smuzhiyun 		seq_puts(s, "\n");
128*4882a593Smuzhiyun 	}
129*4882a593Smuzhiyun 	seq_puts(s, "}\n");
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun 
ring_show(struct seq_file * s,void * data)132*4882a593Smuzhiyun static int ring_show(struct seq_file *s, void *data)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun 	uint i;
135*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	wil_print_ring(s, wil, "rx", &wil->ring_rx, 'S', '_');
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(wil->ring_tx); i++) {
140*4882a593Smuzhiyun 		struct wil_ring *ring = &wil->ring_tx[i];
141*4882a593Smuzhiyun 		struct wil_ring_tx_data *txdata = &wil->ring_tx_data[i];
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 		if (ring->va) {
144*4882a593Smuzhiyun 			int cid = wil->ring2cid_tid[i][0];
145*4882a593Smuzhiyun 			int tid = wil->ring2cid_tid[i][1];
146*4882a593Smuzhiyun 			u32 swhead = ring->swhead;
147*4882a593Smuzhiyun 			u32 swtail = ring->swtail;
148*4882a593Smuzhiyun 			int used = (ring->size + swhead - swtail)
149*4882a593Smuzhiyun 				   % ring->size;
150*4882a593Smuzhiyun 			int avail = ring->size - used - 1;
151*4882a593Smuzhiyun 			char name[10];
152*4882a593Smuzhiyun 			char sidle[10];
153*4882a593Smuzhiyun 			/* performance monitoring */
154*4882a593Smuzhiyun 			cycles_t now = get_cycles();
155*4882a593Smuzhiyun 			uint64_t idle = txdata->idle * 100;
156*4882a593Smuzhiyun 			uint64_t total = now - txdata->begin;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 			if (total != 0) {
159*4882a593Smuzhiyun 				do_div(idle, total);
160*4882a593Smuzhiyun 				snprintf(sidle, sizeof(sidle), "%3d%%",
161*4882a593Smuzhiyun 					 (int)idle);
162*4882a593Smuzhiyun 			} else {
163*4882a593Smuzhiyun 				snprintf(sidle, sizeof(sidle), "N/A");
164*4882a593Smuzhiyun 			}
165*4882a593Smuzhiyun 			txdata->begin = now;
166*4882a593Smuzhiyun 			txdata->idle = 0ULL;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 			snprintf(name, sizeof(name), "tx_%2d", i);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 			if (cid < wil->max_assoc_sta)
171*4882a593Smuzhiyun 				seq_printf(s,
172*4882a593Smuzhiyun 					   "\n%pM CID %d TID %d 1x%s BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n",
173*4882a593Smuzhiyun 					   wil->sta[cid].addr, cid, tid,
174*4882a593Smuzhiyun 					   txdata->dot1x_open ? "+" : "-",
175*4882a593Smuzhiyun 					   txdata->agg_wsize,
176*4882a593Smuzhiyun 					   txdata->agg_timeout,
177*4882a593Smuzhiyun 					   txdata->agg_amsdu ? "+" : "-",
178*4882a593Smuzhiyun 					   used, avail, sidle);
179*4882a593Smuzhiyun 			else
180*4882a593Smuzhiyun 				seq_printf(s,
181*4882a593Smuzhiyun 					   "\nBroadcast 1x%s [%3d|%3d] idle %s\n",
182*4882a593Smuzhiyun 					   txdata->dot1x_open ? "+" : "-",
183*4882a593Smuzhiyun 					   used, avail, sidle);
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 			wil_print_ring(s, wil, name, ring, '_', 'H');
186*4882a593Smuzhiyun 		}
187*4882a593Smuzhiyun 	}
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	return 0;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(ring);
192*4882a593Smuzhiyun 
wil_print_sring(struct seq_file * s,struct wil6210_priv * wil,struct wil_status_ring * sring)193*4882a593Smuzhiyun static void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil,
194*4882a593Smuzhiyun 			    struct wil_status_ring *sring)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun 	void __iomem *x;
197*4882a593Smuzhiyun 	int sring_idx = sring - wil->srings;
198*4882a593Smuzhiyun 	u32 v;
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	seq_printf(s, "Status Ring %s [ %d ] = {\n",
201*4882a593Smuzhiyun 		   sring->is_rx ? "RX" : "TX", sring_idx);
202*4882a593Smuzhiyun 	seq_printf(s, "  pa     = %pad\n", &sring->pa);
203*4882a593Smuzhiyun 	seq_printf(s, "  va     = 0x%pK\n", sring->va);
204*4882a593Smuzhiyun 	seq_printf(s, "  size   = %d\n", sring->size);
205*4882a593Smuzhiyun 	seq_printf(s, "  elem_size   = %zu\n", sring->elem_size);
206*4882a593Smuzhiyun 	seq_printf(s, "  swhead = %d\n", sring->swhead);
207*4882a593Smuzhiyun 	if (wil->use_enhanced_dma_hw) {
208*4882a593Smuzhiyun 		/* COMPQ_PROD is a table of 32 entries, one for each Q pair.
209*4882a593Smuzhiyun 		 * lower 16bits are for even ring_id and upper 16bits are for
210*4882a593Smuzhiyun 		 * odd ring_id
211*4882a593Smuzhiyun 		 */
212*4882a593Smuzhiyun 		x = wmi_addr(wil, RGF_DMA_SCM_COMPQ_PROD + 4 * (sring_idx / 2));
213*4882a593Smuzhiyun 		v = readl_relaxed(x);
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 		v = (sring_idx % 2 ? (v >> 16) : (v & 0xffff));
216*4882a593Smuzhiyun 		seq_printf(s, "  hwhead = %u\n", v);
217*4882a593Smuzhiyun 	}
218*4882a593Smuzhiyun 	seq_printf(s, "  hwtail = [0x%08x] -> ", sring->hwtail);
219*4882a593Smuzhiyun 	x = wmi_addr(wil, sring->hwtail);
220*4882a593Smuzhiyun 	if (x) {
221*4882a593Smuzhiyun 		v = readl_relaxed(x);
222*4882a593Smuzhiyun 		seq_printf(s, "0x%08x = %d\n", v, v);
223*4882a593Smuzhiyun 	} else {
224*4882a593Smuzhiyun 		seq_puts(s, "???\n");
225*4882a593Smuzhiyun 	}
226*4882a593Smuzhiyun 	seq_printf(s, "  desc_rdy_pol   = %d\n", sring->desc_rdy_pol);
227*4882a593Smuzhiyun 	seq_printf(s, "  invalid_buff_id_cnt   = %d\n",
228*4882a593Smuzhiyun 		   sring->invalid_buff_id_cnt);
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	if (sring->va && (sring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) {
231*4882a593Smuzhiyun 		uint i;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 		for (i = 0; i < sring->size; i++) {
234*4882a593Smuzhiyun 			u32 *sdword_0 =
235*4882a593Smuzhiyun 				(u32 *)(sring->va + (sring->elem_size * i));
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 			if ((i % 128) == 0 && i != 0)
238*4882a593Smuzhiyun 				seq_puts(s, "\n");
239*4882a593Smuzhiyun 			if (i == sring->swhead)
240*4882a593Smuzhiyun 				seq_printf(s, "%c", (*sdword_0 & BIT(31)) ?
241*4882a593Smuzhiyun 					   'X' : 'x');
242*4882a593Smuzhiyun 			else
243*4882a593Smuzhiyun 				seq_printf(s, "%c", (*sdword_0 & BIT(31)) ?
244*4882a593Smuzhiyun 					   '1' : '0');
245*4882a593Smuzhiyun 		}
246*4882a593Smuzhiyun 		seq_puts(s, "\n");
247*4882a593Smuzhiyun 	}
248*4882a593Smuzhiyun 	seq_puts(s, "}\n");
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun 
srings_show(struct seq_file * s,void * data)251*4882a593Smuzhiyun static int srings_show(struct seq_file *s, void *data)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
254*4882a593Smuzhiyun 	int i = 0;
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	for (i = 0; i < WIL6210_MAX_STATUS_RINGS; i++)
257*4882a593Smuzhiyun 		if (wil->srings[i].va)
258*4882a593Smuzhiyun 			wil_print_sring(s, wil, &wil->srings[i]);
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	return 0;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(srings);
263*4882a593Smuzhiyun 
wil_seq_hexdump(struct seq_file * s,void * p,int len,const char * prefix)264*4882a593Smuzhiyun static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
265*4882a593Smuzhiyun 			    const char *prefix)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun 	seq_hex_dump(s, prefix, DUMP_PREFIX_NONE, 16, 1, p, len, false);
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun 
wil_print_mbox_ring(struct seq_file * s,const char * prefix,void __iomem * off)270*4882a593Smuzhiyun static void wil_print_mbox_ring(struct seq_file *s, const char *prefix,
271*4882a593Smuzhiyun 				void __iomem *off)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
274*4882a593Smuzhiyun 	struct wil6210_mbox_ring r;
275*4882a593Smuzhiyun 	int rsize;
276*4882a593Smuzhiyun 	uint i;
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	wil_halp_vote(wil);
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	if (wil_mem_access_lock(wil)) {
281*4882a593Smuzhiyun 		wil_halp_unvote(wil);
282*4882a593Smuzhiyun 		return;
283*4882a593Smuzhiyun 	}
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	wil_memcpy_fromio_32(&r, off, sizeof(r));
286*4882a593Smuzhiyun 	wil_mbox_ring_le2cpus(&r);
287*4882a593Smuzhiyun 	/*
288*4882a593Smuzhiyun 	 * we just read memory block from NIC. This memory may be
289*4882a593Smuzhiyun 	 * garbage. Check validity before using it.
290*4882a593Smuzhiyun 	 */
291*4882a593Smuzhiyun 	rsize = r.size / sizeof(struct wil6210_mbox_ring_desc);
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	seq_printf(s, "ring %s = {\n", prefix);
294*4882a593Smuzhiyun 	seq_printf(s, "  base = 0x%08x\n", r.base);
295*4882a593Smuzhiyun 	seq_printf(s, "  size = 0x%04x bytes -> %d entries\n", r.size, rsize);
296*4882a593Smuzhiyun 	seq_printf(s, "  tail = 0x%08x\n", r.tail);
297*4882a593Smuzhiyun 	seq_printf(s, "  head = 0x%08x\n", r.head);
298*4882a593Smuzhiyun 	seq_printf(s, "  entry size = %d\n", r.entry_size);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	if (r.size % sizeof(struct wil6210_mbox_ring_desc)) {
301*4882a593Smuzhiyun 		seq_printf(s, "  ??? size is not multiple of %zd, garbage?\n",
302*4882a593Smuzhiyun 			   sizeof(struct wil6210_mbox_ring_desc));
303*4882a593Smuzhiyun 		goto out;
304*4882a593Smuzhiyun 	}
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	if (!wmi_addr(wil, r.base) ||
307*4882a593Smuzhiyun 	    !wmi_addr(wil, r.tail) ||
308*4882a593Smuzhiyun 	    !wmi_addr(wil, r.head)) {
309*4882a593Smuzhiyun 		seq_puts(s, "  ??? pointers are garbage?\n");
310*4882a593Smuzhiyun 		goto out;
311*4882a593Smuzhiyun 	}
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	for (i = 0; i < rsize; i++) {
314*4882a593Smuzhiyun 		struct wil6210_mbox_ring_desc d;
315*4882a593Smuzhiyun 		struct wil6210_mbox_hdr hdr;
316*4882a593Smuzhiyun 		size_t delta = i * sizeof(d);
317*4882a593Smuzhiyun 		void __iomem *x = wil->csr + HOSTADDR(r.base) + delta;
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 		wil_memcpy_fromio_32(&d, x, sizeof(d));
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 		seq_printf(s, "  [%2x] %s %s%s 0x%08x", i,
322*4882a593Smuzhiyun 			   d.sync ? "F" : "E",
323*4882a593Smuzhiyun 			   (r.tail - r.base == delta) ? "t" : " ",
324*4882a593Smuzhiyun 			   (r.head - r.base == delta) ? "h" : " ",
325*4882a593Smuzhiyun 			   le32_to_cpu(d.addr));
326*4882a593Smuzhiyun 		if (0 == wmi_read_hdr(wil, d.addr, &hdr)) {
327*4882a593Smuzhiyun 			u16 len = le16_to_cpu(hdr.len);
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 			seq_printf(s, " -> %04x %04x %04x %02x\n",
330*4882a593Smuzhiyun 				   le16_to_cpu(hdr.seq), len,
331*4882a593Smuzhiyun 				   le16_to_cpu(hdr.type), hdr.flags);
332*4882a593Smuzhiyun 			if (len <= MAX_MBOXITEM_SIZE) {
333*4882a593Smuzhiyun 				unsigned char databuf[MAX_MBOXITEM_SIZE];
334*4882a593Smuzhiyun 				void __iomem *src = wmi_buffer(wil, d.addr) +
335*4882a593Smuzhiyun 					sizeof(struct wil6210_mbox_hdr);
336*4882a593Smuzhiyun 				/*
337*4882a593Smuzhiyun 				 * No need to check @src for validity -
338*4882a593Smuzhiyun 				 * we already validated @d.addr while
339*4882a593Smuzhiyun 				 * reading header
340*4882a593Smuzhiyun 				 */
341*4882a593Smuzhiyun 				wil_memcpy_fromio_32(databuf, src, len);
342*4882a593Smuzhiyun 				wil_seq_hexdump(s, databuf, len, "      : ");
343*4882a593Smuzhiyun 			}
344*4882a593Smuzhiyun 		} else {
345*4882a593Smuzhiyun 			seq_puts(s, "\n");
346*4882a593Smuzhiyun 		}
347*4882a593Smuzhiyun 	}
348*4882a593Smuzhiyun  out:
349*4882a593Smuzhiyun 	seq_puts(s, "}\n");
350*4882a593Smuzhiyun 	wil_mem_access_unlock(wil);
351*4882a593Smuzhiyun 	wil_halp_unvote(wil);
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun 
mbox_show(struct seq_file * s,void * data)354*4882a593Smuzhiyun static int mbox_show(struct seq_file *s, void *data)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
357*4882a593Smuzhiyun 	int ret;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	ret = wil_pm_runtime_get(wil);
360*4882a593Smuzhiyun 	if (ret < 0)
361*4882a593Smuzhiyun 		return ret;
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	wil_print_mbox_ring(s, "tx", wil->csr + HOST_MBOX +
364*4882a593Smuzhiyun 		       offsetof(struct wil6210_mbox_ctl, tx));
365*4882a593Smuzhiyun 	wil_print_mbox_ring(s, "rx", wil->csr + HOST_MBOX +
366*4882a593Smuzhiyun 		       offsetof(struct wil6210_mbox_ctl, rx));
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	wil_pm_runtime_put(wil);
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	return 0;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(mbox);
373*4882a593Smuzhiyun 
wil_debugfs_iomem_x32_set(void * data,u64 val)374*4882a593Smuzhiyun static int wil_debugfs_iomem_x32_set(void *data, u64 val)
375*4882a593Smuzhiyun {
376*4882a593Smuzhiyun 	struct wil_debugfs_iomem_data *d = (struct
377*4882a593Smuzhiyun 					    wil_debugfs_iomem_data *)data;
378*4882a593Smuzhiyun 	struct wil6210_priv *wil = d->wil;
379*4882a593Smuzhiyun 	int ret;
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	ret = wil_pm_runtime_get(wil);
382*4882a593Smuzhiyun 	if (ret < 0)
383*4882a593Smuzhiyun 		return ret;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	writel_relaxed(val, (void __iomem *)d->offset);
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	wmb(); /* make sure write propagated to HW */
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	wil_pm_runtime_put(wil);
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	return 0;
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun 
wil_debugfs_iomem_x32_get(void * data,u64 * val)394*4882a593Smuzhiyun static int wil_debugfs_iomem_x32_get(void *data, u64 *val)
395*4882a593Smuzhiyun {
396*4882a593Smuzhiyun 	struct wil_debugfs_iomem_data *d = (struct
397*4882a593Smuzhiyun 					    wil_debugfs_iomem_data *)data;
398*4882a593Smuzhiyun 	struct wil6210_priv *wil = d->wil;
399*4882a593Smuzhiyun 	int ret;
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	ret = wil_pm_runtime_get(wil);
402*4882a593Smuzhiyun 	if (ret < 0)
403*4882a593Smuzhiyun 		return ret;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	*val = readl((void __iomem *)d->offset);
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	wil_pm_runtime_put(wil);
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	return 0;
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun DEFINE_DEBUGFS_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get,
413*4882a593Smuzhiyun 			 wil_debugfs_iomem_x32_set, "0x%08llx\n");
414*4882a593Smuzhiyun 
wil_debugfs_create_iomem_x32(const char * name,umode_t mode,struct dentry * parent,void * value,struct wil6210_priv * wil)415*4882a593Smuzhiyun static void wil_debugfs_create_iomem_x32(const char *name, umode_t mode,
416*4882a593Smuzhiyun 					 struct dentry *parent, void *value,
417*4882a593Smuzhiyun 					 struct wil6210_priv *wil)
418*4882a593Smuzhiyun {
419*4882a593Smuzhiyun 	struct wil_debugfs_iomem_data *data = &wil->dbg_data.data_arr[
420*4882a593Smuzhiyun 					      wil->dbg_data.iomem_data_count];
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	data->wil = wil;
423*4882a593Smuzhiyun 	data->offset = value;
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	debugfs_create_file_unsafe(name, mode, parent, data, &fops_iomem_x32);
426*4882a593Smuzhiyun 	wil->dbg_data.iomem_data_count++;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun 
wil_debugfs_ulong_set(void * data,u64 val)429*4882a593Smuzhiyun static int wil_debugfs_ulong_set(void *data, u64 val)
430*4882a593Smuzhiyun {
431*4882a593Smuzhiyun 	*(ulong *)data = val;
432*4882a593Smuzhiyun 	return 0;
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun 
wil_debugfs_ulong_get(void * data,u64 * val)435*4882a593Smuzhiyun static int wil_debugfs_ulong_get(void *data, u64 *val)
436*4882a593Smuzhiyun {
437*4882a593Smuzhiyun 	*val = *(ulong *)data;
438*4882a593Smuzhiyun 	return 0;
439*4882a593Smuzhiyun }
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun DEFINE_DEBUGFS_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get,
442*4882a593Smuzhiyun 			 wil_debugfs_ulong_set, "0x%llx\n");
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun /**
445*4882a593Smuzhiyun  * wil6210_debugfs_init_offset - create set of debugfs files
446*4882a593Smuzhiyun  * @wil: driver's context, used for printing
447*4882a593Smuzhiyun  * @dbg: directory on the debugfs, where files will be created
448*4882a593Smuzhiyun  * @base: base address used in address calculation
449*4882a593Smuzhiyun  * @tbl: table with file descriptions. Should be terminated with empty element.
450*4882a593Smuzhiyun  *
451*4882a593Smuzhiyun  * Creates files accordingly to the @tbl.
452*4882a593Smuzhiyun  */
wil6210_debugfs_init_offset(struct wil6210_priv * wil,struct dentry * dbg,void * base,const struct dbg_off * const tbl)453*4882a593Smuzhiyun static void wil6210_debugfs_init_offset(struct wil6210_priv *wil,
454*4882a593Smuzhiyun 					struct dentry *dbg, void *base,
455*4882a593Smuzhiyun 					const struct dbg_off * const tbl)
456*4882a593Smuzhiyun {
457*4882a593Smuzhiyun 	int i;
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 	for (i = 0; tbl[i].name; i++) {
460*4882a593Smuzhiyun 		switch (tbl[i].type) {
461*4882a593Smuzhiyun 		case doff_u32:
462*4882a593Smuzhiyun 			debugfs_create_u32(tbl[i].name, tbl[i].mode, dbg,
463*4882a593Smuzhiyun 					   base + tbl[i].off);
464*4882a593Smuzhiyun 			break;
465*4882a593Smuzhiyun 		case doff_x32:
466*4882a593Smuzhiyun 			debugfs_create_x32(tbl[i].name, tbl[i].mode, dbg,
467*4882a593Smuzhiyun 					   base + tbl[i].off);
468*4882a593Smuzhiyun 			break;
469*4882a593Smuzhiyun 		case doff_ulong:
470*4882a593Smuzhiyun 			debugfs_create_file_unsafe(tbl[i].name, tbl[i].mode,
471*4882a593Smuzhiyun 						   dbg, base + tbl[i].off,
472*4882a593Smuzhiyun 						   &wil_fops_ulong);
473*4882a593Smuzhiyun 			break;
474*4882a593Smuzhiyun 		case doff_io32:
475*4882a593Smuzhiyun 			wil_debugfs_create_iomem_x32(tbl[i].name, tbl[i].mode,
476*4882a593Smuzhiyun 						     dbg, base + tbl[i].off,
477*4882a593Smuzhiyun 						     wil);
478*4882a593Smuzhiyun 			break;
479*4882a593Smuzhiyun 		case doff_u8:
480*4882a593Smuzhiyun 			debugfs_create_u8(tbl[i].name, tbl[i].mode, dbg,
481*4882a593Smuzhiyun 					  base + tbl[i].off);
482*4882a593Smuzhiyun 			break;
483*4882a593Smuzhiyun 		}
484*4882a593Smuzhiyun 	}
485*4882a593Smuzhiyun }
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun static const struct dbg_off isr_off[] = {
488*4882a593Smuzhiyun 	{"ICC", 0644, offsetof(struct RGF_ICR, ICC), doff_io32},
489*4882a593Smuzhiyun 	{"ICR", 0644, offsetof(struct RGF_ICR, ICR), doff_io32},
490*4882a593Smuzhiyun 	{"ICM", 0644, offsetof(struct RGF_ICR, ICM), doff_io32},
491*4882a593Smuzhiyun 	{"ICS",	0244, offsetof(struct RGF_ICR, ICS), doff_io32},
492*4882a593Smuzhiyun 	{"IMV", 0644, offsetof(struct RGF_ICR, IMV), doff_io32},
493*4882a593Smuzhiyun 	{"IMS",	0244, offsetof(struct RGF_ICR, IMS), doff_io32},
494*4882a593Smuzhiyun 	{"IMC",	0244, offsetof(struct RGF_ICR, IMC), doff_io32},
495*4882a593Smuzhiyun 	{},
496*4882a593Smuzhiyun };
497*4882a593Smuzhiyun 
wil6210_debugfs_create_ISR(struct wil6210_priv * wil,const char * name,struct dentry * parent,u32 off)498*4882a593Smuzhiyun static void wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
499*4882a593Smuzhiyun 				       const char *name, struct dentry *parent,
500*4882a593Smuzhiyun 				       u32 off)
501*4882a593Smuzhiyun {
502*4882a593Smuzhiyun 	struct dentry *d = debugfs_create_dir(name, parent);
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr + off,
505*4882a593Smuzhiyun 				    isr_off);
506*4882a593Smuzhiyun }
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun static const struct dbg_off pseudo_isr_off[] = {
509*4882a593Smuzhiyun 	{"CAUSE",   0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE), doff_io32},
510*4882a593Smuzhiyun 	{"MASK_SW", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW), doff_io32},
511*4882a593Smuzhiyun 	{"MASK_FW", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW), doff_io32},
512*4882a593Smuzhiyun 	{},
513*4882a593Smuzhiyun };
514*4882a593Smuzhiyun 
wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv * wil,struct dentry * parent)515*4882a593Smuzhiyun static void wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil,
516*4882a593Smuzhiyun 					      struct dentry *parent)
517*4882a593Smuzhiyun {
518*4882a593Smuzhiyun 	struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent);
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr,
521*4882a593Smuzhiyun 				    pseudo_isr_off);
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun static const struct dbg_off lgc_itr_cnt_off[] = {
525*4882a593Smuzhiyun 	{"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_CNT_TRSH), doff_io32},
526*4882a593Smuzhiyun 	{"DATA", 0644, HOSTADDR(RGF_DMA_ITR_CNT_DATA), doff_io32},
527*4882a593Smuzhiyun 	{"CTL",  0644, HOSTADDR(RGF_DMA_ITR_CNT_CRL), doff_io32},
528*4882a593Smuzhiyun 	{},
529*4882a593Smuzhiyun };
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun static const struct dbg_off tx_itr_cnt_off[] = {
532*4882a593Smuzhiyun 	{"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_TRSH),
533*4882a593Smuzhiyun 	 doff_io32},
534*4882a593Smuzhiyun 	{"DATA", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_DATA),
535*4882a593Smuzhiyun 	 doff_io32},
536*4882a593Smuzhiyun 	{"CTL",  0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_CTL),
537*4882a593Smuzhiyun 	 doff_io32},
538*4882a593Smuzhiyun 	{"IDL_TRSH", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_TRSH),
539*4882a593Smuzhiyun 	 doff_io32},
540*4882a593Smuzhiyun 	{"IDL_DATA", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_DATA),
541*4882a593Smuzhiyun 	 doff_io32},
542*4882a593Smuzhiyun 	{"IDL_CTL",  0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_CTL),
543*4882a593Smuzhiyun 	 doff_io32},
544*4882a593Smuzhiyun 	{},
545*4882a593Smuzhiyun };
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun static const struct dbg_off rx_itr_cnt_off[] = {
548*4882a593Smuzhiyun 	{"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_TRSH),
549*4882a593Smuzhiyun 	 doff_io32},
550*4882a593Smuzhiyun 	{"DATA", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_DATA),
551*4882a593Smuzhiyun 	 doff_io32},
552*4882a593Smuzhiyun 	{"CTL",  0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_CTL),
553*4882a593Smuzhiyun 	 doff_io32},
554*4882a593Smuzhiyun 	{"IDL_TRSH", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_TRSH),
555*4882a593Smuzhiyun 	 doff_io32},
556*4882a593Smuzhiyun 	{"IDL_DATA", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_DATA),
557*4882a593Smuzhiyun 	 doff_io32},
558*4882a593Smuzhiyun 	{"IDL_CTL",  0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_CTL),
559*4882a593Smuzhiyun 	 doff_io32},
560*4882a593Smuzhiyun 	{},
561*4882a593Smuzhiyun };
562*4882a593Smuzhiyun 
wil6210_debugfs_create_ITR_CNT(struct wil6210_priv * wil,struct dentry * parent)563*4882a593Smuzhiyun static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
564*4882a593Smuzhiyun 					  struct dentry *parent)
565*4882a593Smuzhiyun {
566*4882a593Smuzhiyun 	struct dentry *d, *dtx, *drx;
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 	d = debugfs_create_dir("ITR_CNT", parent);
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 	dtx = debugfs_create_dir("TX", d);
571*4882a593Smuzhiyun 	drx = debugfs_create_dir("RX", d);
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr,
574*4882a593Smuzhiyun 				    lgc_itr_cnt_off);
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 	wil6210_debugfs_init_offset(wil, dtx, (void * __force)wil->csr,
577*4882a593Smuzhiyun 				    tx_itr_cnt_off);
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	wil6210_debugfs_init_offset(wil, drx, (void * __force)wil->csr,
580*4882a593Smuzhiyun 				    rx_itr_cnt_off);
581*4882a593Smuzhiyun 	return 0;
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun 
memread_show(struct seq_file * s,void * data)584*4882a593Smuzhiyun static int memread_show(struct seq_file *s, void *data)
585*4882a593Smuzhiyun {
586*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
587*4882a593Smuzhiyun 	void __iomem *a;
588*4882a593Smuzhiyun 	int ret;
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun 	ret = wil_pm_runtime_get(wil);
591*4882a593Smuzhiyun 	if (ret < 0)
592*4882a593Smuzhiyun 		return ret;
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	ret = wil_mem_access_lock(wil);
595*4882a593Smuzhiyun 	if (ret) {
596*4882a593Smuzhiyun 		wil_pm_runtime_put(wil);
597*4882a593Smuzhiyun 		return ret;
598*4882a593Smuzhiyun 	}
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun 	a = wmi_buffer(wil, cpu_to_le32(mem_addr));
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	if (a)
603*4882a593Smuzhiyun 		seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, readl(a));
604*4882a593Smuzhiyun 	else
605*4882a593Smuzhiyun 		seq_printf(s, "[0x%08x] = INVALID\n", mem_addr);
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	wil_mem_access_unlock(wil);
608*4882a593Smuzhiyun 	wil_pm_runtime_put(wil);
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	return 0;
611*4882a593Smuzhiyun }
612*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(memread);
613*4882a593Smuzhiyun 
wil_read_file_ioblob(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)614*4882a593Smuzhiyun static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
615*4882a593Smuzhiyun 				    size_t count, loff_t *ppos)
616*4882a593Smuzhiyun {
617*4882a593Smuzhiyun 	enum { max_count = 4096 };
618*4882a593Smuzhiyun 	struct wil_blob_wrapper *wil_blob = file->private_data;
619*4882a593Smuzhiyun 	struct wil6210_priv *wil = wil_blob->wil;
620*4882a593Smuzhiyun 	loff_t aligned_pos, pos = *ppos;
621*4882a593Smuzhiyun 	size_t available = wil_blob->blob.size;
622*4882a593Smuzhiyun 	void *buf;
623*4882a593Smuzhiyun 	size_t unaligned_bytes, aligned_count, ret;
624*4882a593Smuzhiyun 	int rc;
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	if (pos < 0)
627*4882a593Smuzhiyun 		return -EINVAL;
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 	if (pos >= available || !count)
630*4882a593Smuzhiyun 		return 0;
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	if (count > available - pos)
633*4882a593Smuzhiyun 		count = available - pos;
634*4882a593Smuzhiyun 	if (count > max_count)
635*4882a593Smuzhiyun 		count = max_count;
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun 	/* set pos to 4 bytes aligned */
638*4882a593Smuzhiyun 	unaligned_bytes = pos % 4;
639*4882a593Smuzhiyun 	aligned_pos = pos - unaligned_bytes;
640*4882a593Smuzhiyun 	aligned_count = count + unaligned_bytes;
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun 	buf = kmalloc(aligned_count, GFP_KERNEL);
643*4882a593Smuzhiyun 	if (!buf)
644*4882a593Smuzhiyun 		return -ENOMEM;
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun 	rc = wil_pm_runtime_get(wil);
647*4882a593Smuzhiyun 	if (rc < 0) {
648*4882a593Smuzhiyun 		kfree(buf);
649*4882a593Smuzhiyun 		return rc;
650*4882a593Smuzhiyun 	}
651*4882a593Smuzhiyun 
652*4882a593Smuzhiyun 	rc = wil_mem_access_lock(wil);
653*4882a593Smuzhiyun 	if (rc) {
654*4882a593Smuzhiyun 		kfree(buf);
655*4882a593Smuzhiyun 		wil_pm_runtime_put(wil);
656*4882a593Smuzhiyun 		return rc;
657*4882a593Smuzhiyun 	}
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun 	wil_memcpy_fromio_32(buf, (const void __iomem *)
660*4882a593Smuzhiyun 			     wil_blob->blob.data + aligned_pos, aligned_count);
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun 	ret = copy_to_user(user_buf, buf + unaligned_bytes, count);
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun 	wil_mem_access_unlock(wil);
665*4882a593Smuzhiyun 	wil_pm_runtime_put(wil);
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 	kfree(buf);
668*4882a593Smuzhiyun 	if (ret == count)
669*4882a593Smuzhiyun 		return -EFAULT;
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun 	count -= ret;
672*4882a593Smuzhiyun 	*ppos = pos + count;
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 	return count;
675*4882a593Smuzhiyun }
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun static const struct file_operations fops_ioblob = {
678*4882a593Smuzhiyun 	.read =		wil_read_file_ioblob,
679*4882a593Smuzhiyun 	.open =		simple_open,
680*4882a593Smuzhiyun 	.llseek =	default_llseek,
681*4882a593Smuzhiyun };
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun static
wil_debugfs_create_ioblob(const char * name,umode_t mode,struct dentry * parent,struct wil_blob_wrapper * wil_blob)684*4882a593Smuzhiyun struct dentry *wil_debugfs_create_ioblob(const char *name,
685*4882a593Smuzhiyun 					 umode_t mode,
686*4882a593Smuzhiyun 					 struct dentry *parent,
687*4882a593Smuzhiyun 					 struct wil_blob_wrapper *wil_blob)
688*4882a593Smuzhiyun {
689*4882a593Smuzhiyun 	return debugfs_create_file(name, mode, parent, wil_blob, &fops_ioblob);
690*4882a593Smuzhiyun }
691*4882a593Smuzhiyun 
692*4882a593Smuzhiyun /*---write channel 1..4 to rxon for it, 0 to rxoff---*/
wil_write_file_rxon(struct file * file,const char __user * buf,size_t len,loff_t * ppos)693*4882a593Smuzhiyun static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
694*4882a593Smuzhiyun 				   size_t len, loff_t *ppos)
695*4882a593Smuzhiyun {
696*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
697*4882a593Smuzhiyun 	int rc;
698*4882a593Smuzhiyun 	long channel;
699*4882a593Smuzhiyun 	bool on;
700*4882a593Smuzhiyun 
701*4882a593Smuzhiyun 	char *kbuf = memdup_user_nul(buf, len);
702*4882a593Smuzhiyun 
703*4882a593Smuzhiyun 	if (IS_ERR(kbuf))
704*4882a593Smuzhiyun 		return PTR_ERR(kbuf);
705*4882a593Smuzhiyun 	rc = kstrtol(kbuf, 0, &channel);
706*4882a593Smuzhiyun 	kfree(kbuf);
707*4882a593Smuzhiyun 	if (rc)
708*4882a593Smuzhiyun 		return rc;
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 	if ((channel < 0) || (channel > 4)) {
711*4882a593Smuzhiyun 		wil_err(wil, "Invalid channel %ld\n", channel);
712*4882a593Smuzhiyun 		return -EINVAL;
713*4882a593Smuzhiyun 	}
714*4882a593Smuzhiyun 	on = !!channel;
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	if (on) {
717*4882a593Smuzhiyun 		rc = wmi_set_channel(wil, (int)channel);
718*4882a593Smuzhiyun 		if (rc)
719*4882a593Smuzhiyun 			return rc;
720*4882a593Smuzhiyun 	}
721*4882a593Smuzhiyun 
722*4882a593Smuzhiyun 	rc = wmi_rxon(wil, on);
723*4882a593Smuzhiyun 	if (rc)
724*4882a593Smuzhiyun 		return rc;
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 	return len;
727*4882a593Smuzhiyun }
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun static const struct file_operations fops_rxon = {
730*4882a593Smuzhiyun 	.write = wil_write_file_rxon,
731*4882a593Smuzhiyun 	.open  = simple_open,
732*4882a593Smuzhiyun };
733*4882a593Smuzhiyun 
wil_write_file_rbufcap(struct file * file,const char __user * buf,size_t count,loff_t * ppos)734*4882a593Smuzhiyun static ssize_t wil_write_file_rbufcap(struct file *file,
735*4882a593Smuzhiyun 				      const char __user *buf,
736*4882a593Smuzhiyun 				      size_t count, loff_t *ppos)
737*4882a593Smuzhiyun {
738*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
739*4882a593Smuzhiyun 	int val;
740*4882a593Smuzhiyun 	int rc;
741*4882a593Smuzhiyun 
742*4882a593Smuzhiyun 	rc = kstrtoint_from_user(buf, count, 0, &val);
743*4882a593Smuzhiyun 	if (rc) {
744*4882a593Smuzhiyun 		wil_err(wil, "Invalid argument\n");
745*4882a593Smuzhiyun 		return rc;
746*4882a593Smuzhiyun 	}
747*4882a593Smuzhiyun 	/* input value: negative to disable, 0 to use system default,
748*4882a593Smuzhiyun 	 * 1..ring size to set descriptor threshold
749*4882a593Smuzhiyun 	 */
750*4882a593Smuzhiyun 	wil_info(wil, "%s RBUFCAP, descriptors threshold - %d\n",
751*4882a593Smuzhiyun 		 val < 0 ? "Disabling" : "Enabling", val);
752*4882a593Smuzhiyun 
753*4882a593Smuzhiyun 	if (!wil->ring_rx.va || val > wil->ring_rx.size) {
754*4882a593Smuzhiyun 		wil_err(wil, "Invalid descriptors threshold, %d\n", val);
755*4882a593Smuzhiyun 		return -EINVAL;
756*4882a593Smuzhiyun 	}
757*4882a593Smuzhiyun 
758*4882a593Smuzhiyun 	rc = wmi_rbufcap_cfg(wil, val < 0 ? 0 : 1, val < 0 ? 0 : val);
759*4882a593Smuzhiyun 	if (rc) {
760*4882a593Smuzhiyun 		wil_err(wil, "RBUFCAP config failed: %d\n", rc);
761*4882a593Smuzhiyun 		return rc;
762*4882a593Smuzhiyun 	}
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun 	return count;
765*4882a593Smuzhiyun }
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun static const struct file_operations fops_rbufcap = {
768*4882a593Smuzhiyun 	.write = wil_write_file_rbufcap,
769*4882a593Smuzhiyun 	.open  = simple_open,
770*4882a593Smuzhiyun };
771*4882a593Smuzhiyun 
772*4882a593Smuzhiyun /* block ack control, write:
773*4882a593Smuzhiyun  * - "add <ringid> <agg_size> <timeout>" to trigger ADDBA
774*4882a593Smuzhiyun  * - "del_tx <ringid> <reason>" to trigger DELBA for Tx side
775*4882a593Smuzhiyun  * - "del_rx <CID> <TID> <reason>" to trigger DELBA for Rx side
776*4882a593Smuzhiyun  */
wil_write_back(struct file * file,const char __user * buf,size_t len,loff_t * ppos)777*4882a593Smuzhiyun static ssize_t wil_write_back(struct file *file, const char __user *buf,
778*4882a593Smuzhiyun 			      size_t len, loff_t *ppos)
779*4882a593Smuzhiyun {
780*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
781*4882a593Smuzhiyun 	int rc;
782*4882a593Smuzhiyun 	char *kbuf = kmalloc(len + 1, GFP_KERNEL);
783*4882a593Smuzhiyun 	char cmd[9];
784*4882a593Smuzhiyun 	int p1, p2, p3;
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	if (!kbuf)
787*4882a593Smuzhiyun 		return -ENOMEM;
788*4882a593Smuzhiyun 
789*4882a593Smuzhiyun 	rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
790*4882a593Smuzhiyun 	if (rc != len) {
791*4882a593Smuzhiyun 		kfree(kbuf);
792*4882a593Smuzhiyun 		return rc >= 0 ? -EIO : rc;
793*4882a593Smuzhiyun 	}
794*4882a593Smuzhiyun 
795*4882a593Smuzhiyun 	kbuf[len] = '\0';
796*4882a593Smuzhiyun 	rc = sscanf(kbuf, "%8s %d %d %d", cmd, &p1, &p2, &p3);
797*4882a593Smuzhiyun 	kfree(kbuf);
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	if (rc < 0)
800*4882a593Smuzhiyun 		return rc;
801*4882a593Smuzhiyun 	if (rc < 2)
802*4882a593Smuzhiyun 		return -EINVAL;
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun 	if ((strcmp(cmd, "add") == 0) ||
805*4882a593Smuzhiyun 	    (strcmp(cmd, "del_tx") == 0)) {
806*4882a593Smuzhiyun 		struct wil_ring_tx_data *txdata;
807*4882a593Smuzhiyun 
808*4882a593Smuzhiyun 		if (p1 < 0 || p1 >= WIL6210_MAX_TX_RINGS) {
809*4882a593Smuzhiyun 			wil_err(wil, "BACK: invalid ring id %d\n", p1);
810*4882a593Smuzhiyun 			return -EINVAL;
811*4882a593Smuzhiyun 		}
812*4882a593Smuzhiyun 		txdata = &wil->ring_tx_data[p1];
813*4882a593Smuzhiyun 		if (strcmp(cmd, "add") == 0) {
814*4882a593Smuzhiyun 			if (rc < 3) {
815*4882a593Smuzhiyun 				wil_err(wil, "BACK: add require at least 2 params\n");
816*4882a593Smuzhiyun 				return -EINVAL;
817*4882a593Smuzhiyun 			}
818*4882a593Smuzhiyun 			if (rc < 4)
819*4882a593Smuzhiyun 				p3 = 0;
820*4882a593Smuzhiyun 			wmi_addba(wil, txdata->mid, p1, p2, p3);
821*4882a593Smuzhiyun 		} else {
822*4882a593Smuzhiyun 			if (rc < 3)
823*4882a593Smuzhiyun 				p2 = WLAN_REASON_QSTA_LEAVE_QBSS;
824*4882a593Smuzhiyun 			wmi_delba_tx(wil, txdata->mid, p1, p2);
825*4882a593Smuzhiyun 		}
826*4882a593Smuzhiyun 	} else if (strcmp(cmd, "del_rx") == 0) {
827*4882a593Smuzhiyun 		struct wil_sta_info *sta;
828*4882a593Smuzhiyun 
829*4882a593Smuzhiyun 		if (rc < 3) {
830*4882a593Smuzhiyun 			wil_err(wil,
831*4882a593Smuzhiyun 				"BACK: del_rx require at least 2 params\n");
832*4882a593Smuzhiyun 			return -EINVAL;
833*4882a593Smuzhiyun 		}
834*4882a593Smuzhiyun 		if (p1 < 0 || p1 >= wil->max_assoc_sta) {
835*4882a593Smuzhiyun 			wil_err(wil, "BACK: invalid CID %d\n", p1);
836*4882a593Smuzhiyun 			return -EINVAL;
837*4882a593Smuzhiyun 		}
838*4882a593Smuzhiyun 		if (rc < 4)
839*4882a593Smuzhiyun 			p3 = WLAN_REASON_QSTA_LEAVE_QBSS;
840*4882a593Smuzhiyun 		sta = &wil->sta[p1];
841*4882a593Smuzhiyun 		wmi_delba_rx(wil, sta->mid, p1, p2, p3);
842*4882a593Smuzhiyun 	} else {
843*4882a593Smuzhiyun 		wil_err(wil, "BACK: Unrecognized command \"%s\"\n", cmd);
844*4882a593Smuzhiyun 		return -EINVAL;
845*4882a593Smuzhiyun 	}
846*4882a593Smuzhiyun 
847*4882a593Smuzhiyun 	return len;
848*4882a593Smuzhiyun }
849*4882a593Smuzhiyun 
wil_read_back(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)850*4882a593Smuzhiyun static ssize_t wil_read_back(struct file *file, char __user *user_buf,
851*4882a593Smuzhiyun 			     size_t count, loff_t *ppos)
852*4882a593Smuzhiyun {
853*4882a593Smuzhiyun 	static const char text[] = "block ack control, write:\n"
854*4882a593Smuzhiyun 	" - \"add <ringid> <agg_size> <timeout>\" to trigger ADDBA\n"
855*4882a593Smuzhiyun 	"If missing, <timeout> defaults to 0\n"
856*4882a593Smuzhiyun 	" - \"del_tx <ringid> <reason>\" to trigger DELBA for Tx side\n"
857*4882a593Smuzhiyun 	" - \"del_rx <CID> <TID> <reason>\" to trigger DELBA for Rx side\n"
858*4882a593Smuzhiyun 	"If missing, <reason> set to \"STA_LEAVING\" (36)\n";
859*4882a593Smuzhiyun 
860*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, text,
861*4882a593Smuzhiyun 				       sizeof(text));
862*4882a593Smuzhiyun }
863*4882a593Smuzhiyun 
864*4882a593Smuzhiyun static const struct file_operations fops_back = {
865*4882a593Smuzhiyun 	.read = wil_read_back,
866*4882a593Smuzhiyun 	.write = wil_write_back,
867*4882a593Smuzhiyun 	.open  = simple_open,
868*4882a593Smuzhiyun };
869*4882a593Smuzhiyun 
870*4882a593Smuzhiyun /* pmc control, write:
871*4882a593Smuzhiyun  * - "alloc <num descriptors> <descriptor_size>" to allocate PMC
872*4882a593Smuzhiyun  * - "free" to release memory allocated for PMC
873*4882a593Smuzhiyun  */
wil_write_pmccfg(struct file * file,const char __user * buf,size_t len,loff_t * ppos)874*4882a593Smuzhiyun static ssize_t wil_write_pmccfg(struct file *file, const char __user *buf,
875*4882a593Smuzhiyun 				size_t len, loff_t *ppos)
876*4882a593Smuzhiyun {
877*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
878*4882a593Smuzhiyun 	int rc;
879*4882a593Smuzhiyun 	char *kbuf = kmalloc(len + 1, GFP_KERNEL);
880*4882a593Smuzhiyun 	char cmd[9];
881*4882a593Smuzhiyun 	int num_descs, desc_size;
882*4882a593Smuzhiyun 
883*4882a593Smuzhiyun 	if (!kbuf)
884*4882a593Smuzhiyun 		return -ENOMEM;
885*4882a593Smuzhiyun 
886*4882a593Smuzhiyun 	rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
887*4882a593Smuzhiyun 	if (rc != len) {
888*4882a593Smuzhiyun 		kfree(kbuf);
889*4882a593Smuzhiyun 		return rc >= 0 ? -EIO : rc;
890*4882a593Smuzhiyun 	}
891*4882a593Smuzhiyun 
892*4882a593Smuzhiyun 	kbuf[len] = '\0';
893*4882a593Smuzhiyun 	rc = sscanf(kbuf, "%8s %d %d", cmd, &num_descs, &desc_size);
894*4882a593Smuzhiyun 	kfree(kbuf);
895*4882a593Smuzhiyun 
896*4882a593Smuzhiyun 	if (rc < 0)
897*4882a593Smuzhiyun 		return rc;
898*4882a593Smuzhiyun 
899*4882a593Smuzhiyun 	if (rc < 1) {
900*4882a593Smuzhiyun 		wil_err(wil, "pmccfg: no params given\n");
901*4882a593Smuzhiyun 		return -EINVAL;
902*4882a593Smuzhiyun 	}
903*4882a593Smuzhiyun 
904*4882a593Smuzhiyun 	if (0 == strcmp(cmd, "alloc")) {
905*4882a593Smuzhiyun 		if (rc != 3) {
906*4882a593Smuzhiyun 			wil_err(wil, "pmccfg: alloc requires 2 params\n");
907*4882a593Smuzhiyun 			return -EINVAL;
908*4882a593Smuzhiyun 		}
909*4882a593Smuzhiyun 		wil_pmc_alloc(wil, num_descs, desc_size);
910*4882a593Smuzhiyun 	} else if (0 == strcmp(cmd, "free")) {
911*4882a593Smuzhiyun 		if (rc != 1) {
912*4882a593Smuzhiyun 			wil_err(wil, "pmccfg: free does not have any params\n");
913*4882a593Smuzhiyun 			return -EINVAL;
914*4882a593Smuzhiyun 		}
915*4882a593Smuzhiyun 		wil_pmc_free(wil, true);
916*4882a593Smuzhiyun 	} else {
917*4882a593Smuzhiyun 		wil_err(wil, "pmccfg: Unrecognized command \"%s\"\n", cmd);
918*4882a593Smuzhiyun 		return -EINVAL;
919*4882a593Smuzhiyun 	}
920*4882a593Smuzhiyun 
921*4882a593Smuzhiyun 	return len;
922*4882a593Smuzhiyun }
923*4882a593Smuzhiyun 
wil_read_pmccfg(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)924*4882a593Smuzhiyun static ssize_t wil_read_pmccfg(struct file *file, char __user *user_buf,
925*4882a593Smuzhiyun 			       size_t count, loff_t *ppos)
926*4882a593Smuzhiyun {
927*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
928*4882a593Smuzhiyun 	char text[256];
929*4882a593Smuzhiyun 	char help[] = "pmc control, write:\n"
930*4882a593Smuzhiyun 	" - \"alloc <num descriptors> <descriptor_size>\" to allocate pmc\n"
931*4882a593Smuzhiyun 	" - \"free\" to free memory allocated for pmc\n";
932*4882a593Smuzhiyun 
933*4882a593Smuzhiyun 	snprintf(text, sizeof(text), "Last command status: %d\n\n%s",
934*4882a593Smuzhiyun 		 wil_pmc_last_cmd_status(wil), help);
935*4882a593Smuzhiyun 
936*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, text,
937*4882a593Smuzhiyun 				       strlen(text) + 1);
938*4882a593Smuzhiyun }
939*4882a593Smuzhiyun 
940*4882a593Smuzhiyun static const struct file_operations fops_pmccfg = {
941*4882a593Smuzhiyun 	.read = wil_read_pmccfg,
942*4882a593Smuzhiyun 	.write = wil_write_pmccfg,
943*4882a593Smuzhiyun 	.open  = simple_open,
944*4882a593Smuzhiyun };
945*4882a593Smuzhiyun 
946*4882a593Smuzhiyun static const struct file_operations fops_pmcdata = {
947*4882a593Smuzhiyun 	.open		= simple_open,
948*4882a593Smuzhiyun 	.read		= wil_pmc_read,
949*4882a593Smuzhiyun 	.llseek		= wil_pmc_llseek,
950*4882a593Smuzhiyun };
951*4882a593Smuzhiyun 
wil_pmcring_seq_open(struct inode * inode,struct file * file)952*4882a593Smuzhiyun static int wil_pmcring_seq_open(struct inode *inode, struct file *file)
953*4882a593Smuzhiyun {
954*4882a593Smuzhiyun 	return single_open(file, wil_pmcring_read, inode->i_private);
955*4882a593Smuzhiyun }
956*4882a593Smuzhiyun 
957*4882a593Smuzhiyun static const struct file_operations fops_pmcring = {
958*4882a593Smuzhiyun 	.open		= wil_pmcring_seq_open,
959*4882a593Smuzhiyun 	.release	= single_release,
960*4882a593Smuzhiyun 	.read		= seq_read,
961*4882a593Smuzhiyun 	.llseek		= seq_lseek,
962*4882a593Smuzhiyun };
963*4882a593Smuzhiyun 
964*4882a593Smuzhiyun /*---tx_mgmt---*/
965*4882a593Smuzhiyun /* Write mgmt frame to this file to send it */
wil_write_file_txmgmt(struct file * file,const char __user * buf,size_t len,loff_t * ppos)966*4882a593Smuzhiyun static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
967*4882a593Smuzhiyun 				     size_t len, loff_t *ppos)
968*4882a593Smuzhiyun {
969*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
970*4882a593Smuzhiyun 	struct wiphy *wiphy = wil_to_wiphy(wil);
971*4882a593Smuzhiyun 	struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr;
972*4882a593Smuzhiyun 	struct cfg80211_mgmt_tx_params params;
973*4882a593Smuzhiyun 	int rc;
974*4882a593Smuzhiyun 	void *frame;
975*4882a593Smuzhiyun 
976*4882a593Smuzhiyun 	memset(&params, 0, sizeof(params));
977*4882a593Smuzhiyun 
978*4882a593Smuzhiyun 	if (!len)
979*4882a593Smuzhiyun 		return -EINVAL;
980*4882a593Smuzhiyun 
981*4882a593Smuzhiyun 	frame = memdup_user(buf, len);
982*4882a593Smuzhiyun 	if (IS_ERR(frame))
983*4882a593Smuzhiyun 		return PTR_ERR(frame);
984*4882a593Smuzhiyun 
985*4882a593Smuzhiyun 	params.buf = frame;
986*4882a593Smuzhiyun 	params.len = len;
987*4882a593Smuzhiyun 
988*4882a593Smuzhiyun 	rc = wil_cfg80211_mgmt_tx(wiphy, wdev, &params, NULL);
989*4882a593Smuzhiyun 
990*4882a593Smuzhiyun 	kfree(frame);
991*4882a593Smuzhiyun 	wil_info(wil, "-> %d\n", rc);
992*4882a593Smuzhiyun 
993*4882a593Smuzhiyun 	return len;
994*4882a593Smuzhiyun }
995*4882a593Smuzhiyun 
996*4882a593Smuzhiyun static const struct file_operations fops_txmgmt = {
997*4882a593Smuzhiyun 	.write = wil_write_file_txmgmt,
998*4882a593Smuzhiyun 	.open  = simple_open,
999*4882a593Smuzhiyun };
1000*4882a593Smuzhiyun 
1001*4882a593Smuzhiyun /* Write WMI command (w/o mbox header) to this file to send it
1002*4882a593Smuzhiyun  * WMI starts from wil6210_mbox_hdr_wmi header
1003*4882a593Smuzhiyun  */
wil_write_file_wmi(struct file * file,const char __user * buf,size_t len,loff_t * ppos)1004*4882a593Smuzhiyun static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
1005*4882a593Smuzhiyun 				  size_t len, loff_t *ppos)
1006*4882a593Smuzhiyun {
1007*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
1008*4882a593Smuzhiyun 	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
1009*4882a593Smuzhiyun 	struct wmi_cmd_hdr *wmi;
1010*4882a593Smuzhiyun 	void *cmd;
1011*4882a593Smuzhiyun 	int cmdlen = len - sizeof(struct wmi_cmd_hdr);
1012*4882a593Smuzhiyun 	u16 cmdid;
1013*4882a593Smuzhiyun 	int rc1;
1014*4882a593Smuzhiyun 
1015*4882a593Smuzhiyun 	if (cmdlen < 0 || *ppos != 0)
1016*4882a593Smuzhiyun 		return -EINVAL;
1017*4882a593Smuzhiyun 
1018*4882a593Smuzhiyun 	wmi = memdup_user(buf, len);
1019*4882a593Smuzhiyun 	if (IS_ERR(wmi))
1020*4882a593Smuzhiyun 		return PTR_ERR(wmi);
1021*4882a593Smuzhiyun 
1022*4882a593Smuzhiyun 	cmd = (cmdlen > 0) ? &wmi[1] : NULL;
1023*4882a593Smuzhiyun 	cmdid = le16_to_cpu(wmi->command_id);
1024*4882a593Smuzhiyun 
1025*4882a593Smuzhiyun 	rc1 = wmi_send(wil, cmdid, vif->mid, cmd, cmdlen);
1026*4882a593Smuzhiyun 	kfree(wmi);
1027*4882a593Smuzhiyun 
1028*4882a593Smuzhiyun 	wil_info(wil, "0x%04x[%d] -> %d\n", cmdid, cmdlen, rc1);
1029*4882a593Smuzhiyun 
1030*4882a593Smuzhiyun 	return len;
1031*4882a593Smuzhiyun }
1032*4882a593Smuzhiyun 
1033*4882a593Smuzhiyun static const struct file_operations fops_wmi = {
1034*4882a593Smuzhiyun 	.write = wil_write_file_wmi,
1035*4882a593Smuzhiyun 	.open  = simple_open,
1036*4882a593Smuzhiyun };
1037*4882a593Smuzhiyun 
wil_seq_print_skb(struct seq_file * s,struct sk_buff * skb)1038*4882a593Smuzhiyun static void wil_seq_print_skb(struct seq_file *s, struct sk_buff *skb)
1039*4882a593Smuzhiyun {
1040*4882a593Smuzhiyun 	int i = 0;
1041*4882a593Smuzhiyun 	int len = skb_headlen(skb);
1042*4882a593Smuzhiyun 	void *p = skb->data;
1043*4882a593Smuzhiyun 	int nr_frags = skb_shinfo(skb)->nr_frags;
1044*4882a593Smuzhiyun 
1045*4882a593Smuzhiyun 	seq_printf(s, "    len = %d\n", len);
1046*4882a593Smuzhiyun 	wil_seq_hexdump(s, p, len, "      : ");
1047*4882a593Smuzhiyun 
1048*4882a593Smuzhiyun 	if (nr_frags) {
1049*4882a593Smuzhiyun 		seq_printf(s, "    nr_frags = %d\n", nr_frags);
1050*4882a593Smuzhiyun 		for (i = 0; i < nr_frags; i++) {
1051*4882a593Smuzhiyun 			const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
1052*4882a593Smuzhiyun 
1053*4882a593Smuzhiyun 			len = skb_frag_size(frag);
1054*4882a593Smuzhiyun 			p = skb_frag_address_safe(frag);
1055*4882a593Smuzhiyun 			seq_printf(s, "    [%2d] : len = %d\n", i, len);
1056*4882a593Smuzhiyun 			wil_seq_hexdump(s, p, len, "      : ");
1057*4882a593Smuzhiyun 		}
1058*4882a593Smuzhiyun 	}
1059*4882a593Smuzhiyun }
1060*4882a593Smuzhiyun 
1061*4882a593Smuzhiyun /*---------Tx/Rx descriptor------------*/
txdesc_show(struct seq_file * s,void * data)1062*4882a593Smuzhiyun static int txdesc_show(struct seq_file *s, void *data)
1063*4882a593Smuzhiyun {
1064*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1065*4882a593Smuzhiyun 	struct wil_ring *ring;
1066*4882a593Smuzhiyun 	bool tx;
1067*4882a593Smuzhiyun 	int ring_idx = dbg_ring_index;
1068*4882a593Smuzhiyun 	int txdesc_idx = dbg_txdesc_index;
1069*4882a593Smuzhiyun 	volatile struct vring_tx_desc *d;
1070*4882a593Smuzhiyun 	volatile u32 *u;
1071*4882a593Smuzhiyun 	struct sk_buff *skb;
1072*4882a593Smuzhiyun 
1073*4882a593Smuzhiyun 	if (wil->use_enhanced_dma_hw) {
1074*4882a593Smuzhiyun 		/* RX ring index == 0 */
1075*4882a593Smuzhiyun 		if (ring_idx >= WIL6210_MAX_TX_RINGS) {
1076*4882a593Smuzhiyun 			seq_printf(s, "invalid ring index %d\n", ring_idx);
1077*4882a593Smuzhiyun 			return 0;
1078*4882a593Smuzhiyun 		}
1079*4882a593Smuzhiyun 		tx = ring_idx > 0; /* desc ring 0 is reserved for RX */
1080*4882a593Smuzhiyun 	} else {
1081*4882a593Smuzhiyun 		/* RX ring index == WIL6210_MAX_TX_RINGS */
1082*4882a593Smuzhiyun 		if (ring_idx > WIL6210_MAX_TX_RINGS) {
1083*4882a593Smuzhiyun 			seq_printf(s, "invalid ring index %d\n", ring_idx);
1084*4882a593Smuzhiyun 			return 0;
1085*4882a593Smuzhiyun 		}
1086*4882a593Smuzhiyun 		tx = (ring_idx < WIL6210_MAX_TX_RINGS);
1087*4882a593Smuzhiyun 	}
1088*4882a593Smuzhiyun 
1089*4882a593Smuzhiyun 	ring = tx ? &wil->ring_tx[ring_idx] : &wil->ring_rx;
1090*4882a593Smuzhiyun 
1091*4882a593Smuzhiyun 	if (!ring->va) {
1092*4882a593Smuzhiyun 		if (tx)
1093*4882a593Smuzhiyun 			seq_printf(s, "No Tx[%2d] RING\n", ring_idx);
1094*4882a593Smuzhiyun 		else
1095*4882a593Smuzhiyun 			seq_puts(s, "No Rx RING\n");
1096*4882a593Smuzhiyun 		return 0;
1097*4882a593Smuzhiyun 	}
1098*4882a593Smuzhiyun 
1099*4882a593Smuzhiyun 	if (txdesc_idx >= ring->size) {
1100*4882a593Smuzhiyun 		if (tx)
1101*4882a593Smuzhiyun 			seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n",
1102*4882a593Smuzhiyun 				   ring_idx, txdesc_idx, ring->size);
1103*4882a593Smuzhiyun 		else
1104*4882a593Smuzhiyun 			seq_printf(s, "RxDesc index (%d) >= size (%d)\n",
1105*4882a593Smuzhiyun 				   txdesc_idx, ring->size);
1106*4882a593Smuzhiyun 		return 0;
1107*4882a593Smuzhiyun 	}
1108*4882a593Smuzhiyun 
1109*4882a593Smuzhiyun 	/* use struct vring_tx_desc for Rx as well,
1110*4882a593Smuzhiyun 	 * only field used, .dma.length, is the same
1111*4882a593Smuzhiyun 	 */
1112*4882a593Smuzhiyun 	d = &ring->va[txdesc_idx].tx.legacy;
1113*4882a593Smuzhiyun 	u = (volatile u32 *)d;
1114*4882a593Smuzhiyun 	skb = NULL;
1115*4882a593Smuzhiyun 
1116*4882a593Smuzhiyun 	if (wil->use_enhanced_dma_hw) {
1117*4882a593Smuzhiyun 		if (tx) {
1118*4882a593Smuzhiyun 			skb = ring->ctx ? ring->ctx[txdesc_idx].skb : NULL;
1119*4882a593Smuzhiyun 		} else if (wil->rx_buff_mgmt.buff_arr) {
1120*4882a593Smuzhiyun 			struct wil_rx_enhanced_desc *rx_d =
1121*4882a593Smuzhiyun 				(struct wil_rx_enhanced_desc *)
1122*4882a593Smuzhiyun 				&ring->va[txdesc_idx].rx.enhanced;
1123*4882a593Smuzhiyun 			u16 buff_id = le16_to_cpu(rx_d->mac.buff_id);
1124*4882a593Smuzhiyun 
1125*4882a593Smuzhiyun 			if (!wil_val_in_range(buff_id, 0,
1126*4882a593Smuzhiyun 					      wil->rx_buff_mgmt.size))
1127*4882a593Smuzhiyun 				seq_printf(s, "invalid buff_id %d\n", buff_id);
1128*4882a593Smuzhiyun 			else
1129*4882a593Smuzhiyun 				skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb;
1130*4882a593Smuzhiyun 		}
1131*4882a593Smuzhiyun 	} else {
1132*4882a593Smuzhiyun 		skb = ring->ctx[txdesc_idx].skb;
1133*4882a593Smuzhiyun 	}
1134*4882a593Smuzhiyun 	if (tx)
1135*4882a593Smuzhiyun 		seq_printf(s, "Tx[%2d][%3d] = {\n", ring_idx,
1136*4882a593Smuzhiyun 			   txdesc_idx);
1137*4882a593Smuzhiyun 	else
1138*4882a593Smuzhiyun 		seq_printf(s, "Rx[%3d] = {\n", txdesc_idx);
1139*4882a593Smuzhiyun 	seq_printf(s, "  MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",
1140*4882a593Smuzhiyun 		   u[0], u[1], u[2], u[3]);
1141*4882a593Smuzhiyun 	seq_printf(s, "  DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n",
1142*4882a593Smuzhiyun 		   u[4], u[5], u[6], u[7]);
1143*4882a593Smuzhiyun 	seq_printf(s, "  SKB = 0x%p\n", skb);
1144*4882a593Smuzhiyun 
1145*4882a593Smuzhiyun 	if (skb) {
1146*4882a593Smuzhiyun 		skb_get(skb);
1147*4882a593Smuzhiyun 		wil_seq_print_skb(s, skb);
1148*4882a593Smuzhiyun 		kfree_skb(skb);
1149*4882a593Smuzhiyun 	}
1150*4882a593Smuzhiyun 	seq_puts(s, "}\n");
1151*4882a593Smuzhiyun 
1152*4882a593Smuzhiyun 	return 0;
1153*4882a593Smuzhiyun }
1154*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(txdesc);
1155*4882a593Smuzhiyun 
1156*4882a593Smuzhiyun /*---------Tx/Rx status message------------*/
status_msg_show(struct seq_file * s,void * data)1157*4882a593Smuzhiyun static int status_msg_show(struct seq_file *s, void *data)
1158*4882a593Smuzhiyun {
1159*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1160*4882a593Smuzhiyun 	int sring_idx = dbg_sring_index;
1161*4882a593Smuzhiyun 	struct wil_status_ring *sring;
1162*4882a593Smuzhiyun 	bool tx;
1163*4882a593Smuzhiyun 	u32 status_msg_idx = dbg_status_msg_index;
1164*4882a593Smuzhiyun 	u32 *u;
1165*4882a593Smuzhiyun 
1166*4882a593Smuzhiyun 	if (sring_idx >= WIL6210_MAX_STATUS_RINGS) {
1167*4882a593Smuzhiyun 		seq_printf(s, "invalid status ring index %d\n", sring_idx);
1168*4882a593Smuzhiyun 		return 0;
1169*4882a593Smuzhiyun 	}
1170*4882a593Smuzhiyun 
1171*4882a593Smuzhiyun 	sring = &wil->srings[sring_idx];
1172*4882a593Smuzhiyun 	tx = !sring->is_rx;
1173*4882a593Smuzhiyun 
1174*4882a593Smuzhiyun 	if (!sring->va) {
1175*4882a593Smuzhiyun 		seq_printf(s, "No %cX status ring\n", tx ? 'T' : 'R');
1176*4882a593Smuzhiyun 		return 0;
1177*4882a593Smuzhiyun 	}
1178*4882a593Smuzhiyun 
1179*4882a593Smuzhiyun 	if (status_msg_idx >= sring->size) {
1180*4882a593Smuzhiyun 		seq_printf(s, "%cxDesc index (%d) >= size (%d)\n",
1181*4882a593Smuzhiyun 			   tx ? 'T' : 'R', status_msg_idx, sring->size);
1182*4882a593Smuzhiyun 		return 0;
1183*4882a593Smuzhiyun 	}
1184*4882a593Smuzhiyun 
1185*4882a593Smuzhiyun 	u = sring->va + (sring->elem_size * status_msg_idx);
1186*4882a593Smuzhiyun 
1187*4882a593Smuzhiyun 	seq_printf(s, "%cx[%d][%3d] = {\n",
1188*4882a593Smuzhiyun 		   tx ? 'T' : 'R', sring_idx, status_msg_idx);
1189*4882a593Smuzhiyun 
1190*4882a593Smuzhiyun 	seq_printf(s, "  0x%08x 0x%08x 0x%08x 0x%08x\n",
1191*4882a593Smuzhiyun 		   u[0], u[1], u[2], u[3]);
1192*4882a593Smuzhiyun 	if (!tx && !wil->use_compressed_rx_status)
1193*4882a593Smuzhiyun 		seq_printf(s, "  0x%08x 0x%08x 0x%08x 0x%08x\n",
1194*4882a593Smuzhiyun 			   u[4], u[5], u[6], u[7]);
1195*4882a593Smuzhiyun 
1196*4882a593Smuzhiyun 	seq_puts(s, "}\n");
1197*4882a593Smuzhiyun 
1198*4882a593Smuzhiyun 	return 0;
1199*4882a593Smuzhiyun }
1200*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(status_msg);
1201*4882a593Smuzhiyun 
wil_print_rx_buff(struct seq_file * s,struct list_head * lh)1202*4882a593Smuzhiyun static int wil_print_rx_buff(struct seq_file *s, struct list_head *lh)
1203*4882a593Smuzhiyun {
1204*4882a593Smuzhiyun 	struct wil_rx_buff *it;
1205*4882a593Smuzhiyun 	int i = 0;
1206*4882a593Smuzhiyun 
1207*4882a593Smuzhiyun 	list_for_each_entry(it, lh, list) {
1208*4882a593Smuzhiyun 		if ((i % 16) == 0 && i != 0)
1209*4882a593Smuzhiyun 			seq_puts(s, "\n    ");
1210*4882a593Smuzhiyun 		seq_printf(s, "[%4d] ", it->id);
1211*4882a593Smuzhiyun 		i++;
1212*4882a593Smuzhiyun 	}
1213*4882a593Smuzhiyun 	seq_printf(s, "\nNumber of buffers: %u\n", i);
1214*4882a593Smuzhiyun 
1215*4882a593Smuzhiyun 	return i;
1216*4882a593Smuzhiyun }
1217*4882a593Smuzhiyun 
rx_buff_mgmt_show(struct seq_file * s,void * data)1218*4882a593Smuzhiyun static int rx_buff_mgmt_show(struct seq_file *s, void *data)
1219*4882a593Smuzhiyun {
1220*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1221*4882a593Smuzhiyun 	struct wil_rx_buff_mgmt *rbm = &wil->rx_buff_mgmt;
1222*4882a593Smuzhiyun 	int num_active;
1223*4882a593Smuzhiyun 	int num_free;
1224*4882a593Smuzhiyun 
1225*4882a593Smuzhiyun 	if (!rbm->buff_arr)
1226*4882a593Smuzhiyun 		return -EINVAL;
1227*4882a593Smuzhiyun 
1228*4882a593Smuzhiyun 	seq_printf(s, "  size = %zu\n", rbm->size);
1229*4882a593Smuzhiyun 	seq_printf(s, "  free_list_empty_cnt = %lu\n",
1230*4882a593Smuzhiyun 		   rbm->free_list_empty_cnt);
1231*4882a593Smuzhiyun 
1232*4882a593Smuzhiyun 	/* Print active list */
1233*4882a593Smuzhiyun 	seq_puts(s, "  Active list:\n");
1234*4882a593Smuzhiyun 	num_active = wil_print_rx_buff(s, &rbm->active);
1235*4882a593Smuzhiyun 	seq_puts(s, "\n  Free list:\n");
1236*4882a593Smuzhiyun 	num_free = wil_print_rx_buff(s, &rbm->free);
1237*4882a593Smuzhiyun 
1238*4882a593Smuzhiyun 	seq_printf(s, "  Total number of buffers: %u\n",
1239*4882a593Smuzhiyun 		   num_active + num_free);
1240*4882a593Smuzhiyun 
1241*4882a593Smuzhiyun 	return 0;
1242*4882a593Smuzhiyun }
1243*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(rx_buff_mgmt);
1244*4882a593Smuzhiyun 
1245*4882a593Smuzhiyun /*---------beamforming------------*/
wil_bfstatus_str(u32 status)1246*4882a593Smuzhiyun static char *wil_bfstatus_str(u32 status)
1247*4882a593Smuzhiyun {
1248*4882a593Smuzhiyun 	switch (status) {
1249*4882a593Smuzhiyun 	case 0:
1250*4882a593Smuzhiyun 		return "Failed";
1251*4882a593Smuzhiyun 	case 1:
1252*4882a593Smuzhiyun 		return "OK";
1253*4882a593Smuzhiyun 	case 2:
1254*4882a593Smuzhiyun 		return "Retrying";
1255*4882a593Smuzhiyun 	default:
1256*4882a593Smuzhiyun 		return "??";
1257*4882a593Smuzhiyun 	}
1258*4882a593Smuzhiyun }
1259*4882a593Smuzhiyun 
is_all_zeros(void * const x_,size_t sz)1260*4882a593Smuzhiyun static bool is_all_zeros(void * const x_, size_t sz)
1261*4882a593Smuzhiyun {
1262*4882a593Smuzhiyun 	/* if reply is all-0, ignore this CID */
1263*4882a593Smuzhiyun 	u32 *x = x_;
1264*4882a593Smuzhiyun 	int n;
1265*4882a593Smuzhiyun 
1266*4882a593Smuzhiyun 	for (n = 0; n < sz / sizeof(*x); n++)
1267*4882a593Smuzhiyun 		if (x[n])
1268*4882a593Smuzhiyun 			return false;
1269*4882a593Smuzhiyun 
1270*4882a593Smuzhiyun 	return true;
1271*4882a593Smuzhiyun }
1272*4882a593Smuzhiyun 
bf_show(struct seq_file * s,void * data)1273*4882a593Smuzhiyun static int bf_show(struct seq_file *s, void *data)
1274*4882a593Smuzhiyun {
1275*4882a593Smuzhiyun 	int rc;
1276*4882a593Smuzhiyun 	int i;
1277*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1278*4882a593Smuzhiyun 	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
1279*4882a593Smuzhiyun 	struct wmi_notify_req_cmd cmd = {
1280*4882a593Smuzhiyun 		.interval_usec = 0,
1281*4882a593Smuzhiyun 	};
1282*4882a593Smuzhiyun 	struct {
1283*4882a593Smuzhiyun 		struct wmi_cmd_hdr wmi;
1284*4882a593Smuzhiyun 		struct wmi_notify_req_done_event evt;
1285*4882a593Smuzhiyun 	} __packed reply;
1286*4882a593Smuzhiyun 
1287*4882a593Smuzhiyun 	memset(&reply, 0, sizeof(reply));
1288*4882a593Smuzhiyun 
1289*4882a593Smuzhiyun 	for (i = 0; i < wil->max_assoc_sta; i++) {
1290*4882a593Smuzhiyun 		u32 status;
1291*4882a593Smuzhiyun 
1292*4882a593Smuzhiyun 		cmd.cid = i;
1293*4882a593Smuzhiyun 		rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid,
1294*4882a593Smuzhiyun 			      &cmd, sizeof(cmd),
1295*4882a593Smuzhiyun 			      WMI_NOTIFY_REQ_DONE_EVENTID, &reply,
1296*4882a593Smuzhiyun 			      sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS);
1297*4882a593Smuzhiyun 		/* if reply is all-0, ignore this CID */
1298*4882a593Smuzhiyun 		if (rc || is_all_zeros(&reply.evt, sizeof(reply.evt)))
1299*4882a593Smuzhiyun 			continue;
1300*4882a593Smuzhiyun 
1301*4882a593Smuzhiyun 		status = le32_to_cpu(reply.evt.status);
1302*4882a593Smuzhiyun 		seq_printf(s, "CID %d {\n"
1303*4882a593Smuzhiyun 			   "  TSF = 0x%016llx\n"
1304*4882a593Smuzhiyun 			   "  TxMCS = %2d TxTpt = %4d\n"
1305*4882a593Smuzhiyun 			   "  SQI = %4d\n"
1306*4882a593Smuzhiyun 			   "  RSSI = %4d\n"
1307*4882a593Smuzhiyun 			   "  Status = 0x%08x %s\n"
1308*4882a593Smuzhiyun 			   "  Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n"
1309*4882a593Smuzhiyun 			   "  Goodput(rx:tx) %4d:%4d\n"
1310*4882a593Smuzhiyun 			   "}\n",
1311*4882a593Smuzhiyun 			   i,
1312*4882a593Smuzhiyun 			   le64_to_cpu(reply.evt.tsf),
1313*4882a593Smuzhiyun 			   le16_to_cpu(reply.evt.bf_mcs),
1314*4882a593Smuzhiyun 			   le32_to_cpu(reply.evt.tx_tpt),
1315*4882a593Smuzhiyun 			   reply.evt.sqi,
1316*4882a593Smuzhiyun 			   reply.evt.rssi,
1317*4882a593Smuzhiyun 			   status, wil_bfstatus_str(status),
1318*4882a593Smuzhiyun 			   le16_to_cpu(reply.evt.my_rx_sector),
1319*4882a593Smuzhiyun 			   le16_to_cpu(reply.evt.my_tx_sector),
1320*4882a593Smuzhiyun 			   le16_to_cpu(reply.evt.other_rx_sector),
1321*4882a593Smuzhiyun 			   le16_to_cpu(reply.evt.other_tx_sector),
1322*4882a593Smuzhiyun 			   le32_to_cpu(reply.evt.rx_goodput),
1323*4882a593Smuzhiyun 			   le32_to_cpu(reply.evt.tx_goodput));
1324*4882a593Smuzhiyun 	}
1325*4882a593Smuzhiyun 	return 0;
1326*4882a593Smuzhiyun }
1327*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(bf);
1328*4882a593Smuzhiyun 
1329*4882a593Smuzhiyun /*---------temp------------*/
print_temp(struct seq_file * s,const char * prefix,s32 t)1330*4882a593Smuzhiyun static void print_temp(struct seq_file *s, const char *prefix, s32 t)
1331*4882a593Smuzhiyun {
1332*4882a593Smuzhiyun 	switch (t) {
1333*4882a593Smuzhiyun 	case 0:
1334*4882a593Smuzhiyun 	case WMI_INVALID_TEMPERATURE:
1335*4882a593Smuzhiyun 		seq_printf(s, "%s N/A\n", prefix);
1336*4882a593Smuzhiyun 	break;
1337*4882a593Smuzhiyun 	default:
1338*4882a593Smuzhiyun 		seq_printf(s, "%s %s%d.%03d\n", prefix, (t < 0 ? "-" : ""),
1339*4882a593Smuzhiyun 			   abs(t / 1000), abs(t % 1000));
1340*4882a593Smuzhiyun 		break;
1341*4882a593Smuzhiyun 	}
1342*4882a593Smuzhiyun }
1343*4882a593Smuzhiyun 
temp_show(struct seq_file * s,void * data)1344*4882a593Smuzhiyun static int temp_show(struct seq_file *s, void *data)
1345*4882a593Smuzhiyun {
1346*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1347*4882a593Smuzhiyun 	int rc, i;
1348*4882a593Smuzhiyun 
1349*4882a593Smuzhiyun 	if (test_bit(WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF,
1350*4882a593Smuzhiyun 		     wil->fw_capabilities)) {
1351*4882a593Smuzhiyun 		struct wmi_temp_sense_all_done_event sense_all_evt;
1352*4882a593Smuzhiyun 
1353*4882a593Smuzhiyun 		wil_dbg_misc(wil,
1354*4882a593Smuzhiyun 			     "WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is supported");
1355*4882a593Smuzhiyun 		rc = wmi_get_all_temperatures(wil, &sense_all_evt);
1356*4882a593Smuzhiyun 		if (rc) {
1357*4882a593Smuzhiyun 			seq_puts(s, "Failed\n");
1358*4882a593Smuzhiyun 			return 0;
1359*4882a593Smuzhiyun 		}
1360*4882a593Smuzhiyun 		print_temp(s, "T_mac   =",
1361*4882a593Smuzhiyun 			   le32_to_cpu(sense_all_evt.baseband_t1000));
1362*4882a593Smuzhiyun 		seq_printf(s, "Connected RFs [0x%08x]\n",
1363*4882a593Smuzhiyun 			   sense_all_evt.rf_bitmap);
1364*4882a593Smuzhiyun 		for (i = 0; i < WMI_MAX_XIF_PORTS_NUM; i++) {
1365*4882a593Smuzhiyun 			seq_printf(s, "RF[%d]   = ", i);
1366*4882a593Smuzhiyun 			print_temp(s, "",
1367*4882a593Smuzhiyun 				   le32_to_cpu(sense_all_evt.rf_t1000[i]));
1368*4882a593Smuzhiyun 		}
1369*4882a593Smuzhiyun 	} else {
1370*4882a593Smuzhiyun 		s32 t_m, t_r;
1371*4882a593Smuzhiyun 
1372*4882a593Smuzhiyun 		wil_dbg_misc(wil,
1373*4882a593Smuzhiyun 			     "WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is not supported");
1374*4882a593Smuzhiyun 		rc = wmi_get_temperature(wil, &t_m, &t_r);
1375*4882a593Smuzhiyun 		if (rc) {
1376*4882a593Smuzhiyun 			seq_puts(s, "Failed\n");
1377*4882a593Smuzhiyun 			return 0;
1378*4882a593Smuzhiyun 		}
1379*4882a593Smuzhiyun 		print_temp(s, "T_mac   =", t_m);
1380*4882a593Smuzhiyun 		print_temp(s, "T_radio =", t_r);
1381*4882a593Smuzhiyun 	}
1382*4882a593Smuzhiyun 	return 0;
1383*4882a593Smuzhiyun }
1384*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(temp);
1385*4882a593Smuzhiyun 
1386*4882a593Smuzhiyun /*---------freq------------*/
freq_show(struct seq_file * s,void * data)1387*4882a593Smuzhiyun static int freq_show(struct seq_file *s, void *data)
1388*4882a593Smuzhiyun {
1389*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1390*4882a593Smuzhiyun 	struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr;
1391*4882a593Smuzhiyun 	u32 freq = wdev->chandef.chan ? wdev->chandef.chan->center_freq : 0;
1392*4882a593Smuzhiyun 
1393*4882a593Smuzhiyun 	seq_printf(s, "Freq = %d\n", freq);
1394*4882a593Smuzhiyun 
1395*4882a593Smuzhiyun 	return 0;
1396*4882a593Smuzhiyun }
1397*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(freq);
1398*4882a593Smuzhiyun 
1399*4882a593Smuzhiyun /*---------link------------*/
link_show(struct seq_file * s,void * data)1400*4882a593Smuzhiyun static int link_show(struct seq_file *s, void *data)
1401*4882a593Smuzhiyun {
1402*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1403*4882a593Smuzhiyun 	struct station_info *sinfo;
1404*4882a593Smuzhiyun 	int i, rc = 0;
1405*4882a593Smuzhiyun 
1406*4882a593Smuzhiyun 	sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
1407*4882a593Smuzhiyun 	if (!sinfo)
1408*4882a593Smuzhiyun 		return -ENOMEM;
1409*4882a593Smuzhiyun 
1410*4882a593Smuzhiyun 	for (i = 0; i < wil->max_assoc_sta; i++) {
1411*4882a593Smuzhiyun 		struct wil_sta_info *p = &wil->sta[i];
1412*4882a593Smuzhiyun 		char *status = "unknown";
1413*4882a593Smuzhiyun 		struct wil6210_vif *vif;
1414*4882a593Smuzhiyun 		u8 mid;
1415*4882a593Smuzhiyun 
1416*4882a593Smuzhiyun 		switch (p->status) {
1417*4882a593Smuzhiyun 		case wil_sta_unused:
1418*4882a593Smuzhiyun 			status = "unused   ";
1419*4882a593Smuzhiyun 			break;
1420*4882a593Smuzhiyun 		case wil_sta_conn_pending:
1421*4882a593Smuzhiyun 			status = "pending  ";
1422*4882a593Smuzhiyun 			break;
1423*4882a593Smuzhiyun 		case wil_sta_connected:
1424*4882a593Smuzhiyun 			status = "connected";
1425*4882a593Smuzhiyun 			break;
1426*4882a593Smuzhiyun 		}
1427*4882a593Smuzhiyun 		mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;
1428*4882a593Smuzhiyun 		seq_printf(s, "[%d][MID %d] %pM %s\n",
1429*4882a593Smuzhiyun 			   i, mid, p->addr, status);
1430*4882a593Smuzhiyun 
1431*4882a593Smuzhiyun 		if (p->status != wil_sta_connected)
1432*4882a593Smuzhiyun 			continue;
1433*4882a593Smuzhiyun 
1434*4882a593Smuzhiyun 		vif = (mid < GET_MAX_VIFS(wil)) ? wil->vifs[mid] : NULL;
1435*4882a593Smuzhiyun 		if (vif) {
1436*4882a593Smuzhiyun 			rc = wil_cid_fill_sinfo(vif, i, sinfo);
1437*4882a593Smuzhiyun 			if (rc)
1438*4882a593Smuzhiyun 				goto out;
1439*4882a593Smuzhiyun 
1440*4882a593Smuzhiyun 			seq_printf(s, "  Tx_mcs = %d\n", sinfo->txrate.mcs);
1441*4882a593Smuzhiyun 			seq_printf(s, "  Rx_mcs = %d\n", sinfo->rxrate.mcs);
1442*4882a593Smuzhiyun 			seq_printf(s, "  SQ     = %d\n", sinfo->signal);
1443*4882a593Smuzhiyun 		} else {
1444*4882a593Smuzhiyun 			seq_puts(s, "  INVALID MID\n");
1445*4882a593Smuzhiyun 		}
1446*4882a593Smuzhiyun 	}
1447*4882a593Smuzhiyun 
1448*4882a593Smuzhiyun out:
1449*4882a593Smuzhiyun 	kfree(sinfo);
1450*4882a593Smuzhiyun 	return rc;
1451*4882a593Smuzhiyun }
1452*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(link);
1453*4882a593Smuzhiyun 
1454*4882a593Smuzhiyun /*---------info------------*/
info_show(struct seq_file * s,void * data)1455*4882a593Smuzhiyun static int info_show(struct seq_file *s, void *data)
1456*4882a593Smuzhiyun {
1457*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1458*4882a593Smuzhiyun 	struct net_device *ndev = wil->main_ndev;
1459*4882a593Smuzhiyun 	int is_ac = power_supply_is_system_supplied();
1460*4882a593Smuzhiyun 	int rx = atomic_xchg(&wil->isr_count_rx, 0);
1461*4882a593Smuzhiyun 	int tx = atomic_xchg(&wil->isr_count_tx, 0);
1462*4882a593Smuzhiyun 	static ulong rxf_old, txf_old;
1463*4882a593Smuzhiyun 	ulong rxf = ndev->stats.rx_packets;
1464*4882a593Smuzhiyun 	ulong txf = ndev->stats.tx_packets;
1465*4882a593Smuzhiyun 	unsigned int i;
1466*4882a593Smuzhiyun 
1467*4882a593Smuzhiyun 	/* >0 : AC; 0 : battery; <0 : error */
1468*4882a593Smuzhiyun 	seq_printf(s, "AC powered : %d\n", is_ac);
1469*4882a593Smuzhiyun 	seq_printf(s, "Rx irqs:packets : %8d : %8ld\n", rx, rxf - rxf_old);
1470*4882a593Smuzhiyun 	seq_printf(s, "Tx irqs:packets : %8d : %8ld\n", tx, txf - txf_old);
1471*4882a593Smuzhiyun 	rxf_old = rxf;
1472*4882a593Smuzhiyun 	txf_old = txf;
1473*4882a593Smuzhiyun 
1474*4882a593Smuzhiyun #define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \
1475*4882a593Smuzhiyun 	" " __stringify(x) : ""
1476*4882a593Smuzhiyun 
1477*4882a593Smuzhiyun 	for (i = 0; i < ndev->num_tx_queues; i++) {
1478*4882a593Smuzhiyun 		struct netdev_queue *txq = netdev_get_tx_queue(ndev, i);
1479*4882a593Smuzhiyun 		unsigned long state = txq->state;
1480*4882a593Smuzhiyun 
1481*4882a593Smuzhiyun 		seq_printf(s, "Tx queue[%i] state : 0x%lx%s%s%s\n", i, state,
1482*4882a593Smuzhiyun 			   CHECK_QSTATE(DRV_XOFF),
1483*4882a593Smuzhiyun 			   CHECK_QSTATE(STACK_XOFF),
1484*4882a593Smuzhiyun 			   CHECK_QSTATE(FROZEN)
1485*4882a593Smuzhiyun 			  );
1486*4882a593Smuzhiyun 	}
1487*4882a593Smuzhiyun #undef CHECK_QSTATE
1488*4882a593Smuzhiyun 	return 0;
1489*4882a593Smuzhiyun }
1490*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(info);
1491*4882a593Smuzhiyun 
1492*4882a593Smuzhiyun /*---------recovery------------*/
1493*4882a593Smuzhiyun /* mode = [manual|auto]
1494*4882a593Smuzhiyun  * state = [idle|pending|running]
1495*4882a593Smuzhiyun  */
wil_read_file_recovery(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1496*4882a593Smuzhiyun static ssize_t wil_read_file_recovery(struct file *file, char __user *user_buf,
1497*4882a593Smuzhiyun 				      size_t count, loff_t *ppos)
1498*4882a593Smuzhiyun {
1499*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
1500*4882a593Smuzhiyun 	char buf[80];
1501*4882a593Smuzhiyun 	int n;
1502*4882a593Smuzhiyun 	static const char * const sstate[] = {"idle", "pending", "running"};
1503*4882a593Smuzhiyun 
1504*4882a593Smuzhiyun 	n = snprintf(buf, sizeof(buf), "mode = %s\nstate = %s\n",
1505*4882a593Smuzhiyun 		     no_fw_recovery ? "manual" : "auto",
1506*4882a593Smuzhiyun 		     sstate[wil->recovery_state]);
1507*4882a593Smuzhiyun 
1508*4882a593Smuzhiyun 	n = min_t(int, n, sizeof(buf));
1509*4882a593Smuzhiyun 
1510*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos,
1511*4882a593Smuzhiyun 				       buf, n);
1512*4882a593Smuzhiyun }
1513*4882a593Smuzhiyun 
wil_write_file_recovery(struct file * file,const char __user * buf_,size_t count,loff_t * ppos)1514*4882a593Smuzhiyun static ssize_t wil_write_file_recovery(struct file *file,
1515*4882a593Smuzhiyun 				       const char __user *buf_,
1516*4882a593Smuzhiyun 				       size_t count, loff_t *ppos)
1517*4882a593Smuzhiyun {
1518*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
1519*4882a593Smuzhiyun 	static const char run_command[] = "run";
1520*4882a593Smuzhiyun 	char buf[sizeof(run_command) + 1]; /* to detect "runx" */
1521*4882a593Smuzhiyun 	ssize_t rc;
1522*4882a593Smuzhiyun 
1523*4882a593Smuzhiyun 	if (wil->recovery_state != fw_recovery_pending) {
1524*4882a593Smuzhiyun 		wil_err(wil, "No recovery pending\n");
1525*4882a593Smuzhiyun 		return -EINVAL;
1526*4882a593Smuzhiyun 	}
1527*4882a593Smuzhiyun 
1528*4882a593Smuzhiyun 	if (*ppos != 0) {
1529*4882a593Smuzhiyun 		wil_err(wil, "Offset [%d]\n", (int)*ppos);
1530*4882a593Smuzhiyun 		return -EINVAL;
1531*4882a593Smuzhiyun 	}
1532*4882a593Smuzhiyun 
1533*4882a593Smuzhiyun 	if (count > sizeof(buf)) {
1534*4882a593Smuzhiyun 		wil_err(wil, "Input too long, len = %d\n", (int)count);
1535*4882a593Smuzhiyun 		return -EINVAL;
1536*4882a593Smuzhiyun 	}
1537*4882a593Smuzhiyun 
1538*4882a593Smuzhiyun 	rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, buf_, count);
1539*4882a593Smuzhiyun 	if (rc < 0)
1540*4882a593Smuzhiyun 		return rc;
1541*4882a593Smuzhiyun 
1542*4882a593Smuzhiyun 	buf[rc] = '\0';
1543*4882a593Smuzhiyun 	if (0 == strcmp(buf, run_command))
1544*4882a593Smuzhiyun 		wil_set_recovery_state(wil, fw_recovery_running);
1545*4882a593Smuzhiyun 	else
1546*4882a593Smuzhiyun 		wil_err(wil, "Bad recovery command \"%s\"\n", buf);
1547*4882a593Smuzhiyun 
1548*4882a593Smuzhiyun 	return rc;
1549*4882a593Smuzhiyun }
1550*4882a593Smuzhiyun 
1551*4882a593Smuzhiyun static const struct file_operations fops_recovery = {
1552*4882a593Smuzhiyun 	.read = wil_read_file_recovery,
1553*4882a593Smuzhiyun 	.write = wil_write_file_recovery,
1554*4882a593Smuzhiyun 	.open  = simple_open,
1555*4882a593Smuzhiyun };
1556*4882a593Smuzhiyun 
1557*4882a593Smuzhiyun /*---------Station matrix------------*/
wil_print_rxtid(struct seq_file * s,struct wil_tid_ampdu_rx * r)1558*4882a593Smuzhiyun static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
1559*4882a593Smuzhiyun {
1560*4882a593Smuzhiyun 	int i;
1561*4882a593Smuzhiyun 	u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size;
1562*4882a593Smuzhiyun 	unsigned long long drop_dup = r->drop_dup, drop_old = r->drop_old;
1563*4882a593Smuzhiyun 	unsigned long long drop_dup_mcast = r->drop_dup_mcast;
1564*4882a593Smuzhiyun 
1565*4882a593Smuzhiyun 	seq_printf(s, "([%2d]) 0x%03x [", r->buf_size, r->head_seq_num);
1566*4882a593Smuzhiyun 	for (i = 0; i < r->buf_size; i++) {
1567*4882a593Smuzhiyun 		if (i == index)
1568*4882a593Smuzhiyun 			seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|');
1569*4882a593Smuzhiyun 		else
1570*4882a593Smuzhiyun 			seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_');
1571*4882a593Smuzhiyun 	}
1572*4882a593Smuzhiyun 	seq_printf(s,
1573*4882a593Smuzhiyun 		   "] total %llu drop %llu (dup %llu + old %llu + dup mcast %llu) last 0x%03x\n",
1574*4882a593Smuzhiyun 		   r->total, drop_dup + drop_old + drop_dup_mcast, drop_dup,
1575*4882a593Smuzhiyun 		   drop_old, drop_dup_mcast, r->ssn_last_drop);
1576*4882a593Smuzhiyun }
1577*4882a593Smuzhiyun 
wil_print_rxtid_crypto(struct seq_file * s,int tid,struct wil_tid_crypto_rx * c)1578*4882a593Smuzhiyun static void wil_print_rxtid_crypto(struct seq_file *s, int tid,
1579*4882a593Smuzhiyun 				   struct wil_tid_crypto_rx *c)
1580*4882a593Smuzhiyun {
1581*4882a593Smuzhiyun 	int i;
1582*4882a593Smuzhiyun 
1583*4882a593Smuzhiyun 	for (i = 0; i < 4; i++) {
1584*4882a593Smuzhiyun 		struct wil_tid_crypto_rx_single *cc = &c->key_id[i];
1585*4882a593Smuzhiyun 
1586*4882a593Smuzhiyun 		if (cc->key_set)
1587*4882a593Smuzhiyun 			goto has_keys;
1588*4882a593Smuzhiyun 	}
1589*4882a593Smuzhiyun 	return;
1590*4882a593Smuzhiyun 
1591*4882a593Smuzhiyun has_keys:
1592*4882a593Smuzhiyun 	if (tid < WIL_STA_TID_NUM)
1593*4882a593Smuzhiyun 		seq_printf(s, "  [%2d] PN", tid);
1594*4882a593Smuzhiyun 	else
1595*4882a593Smuzhiyun 		seq_puts(s, "  [GR] PN");
1596*4882a593Smuzhiyun 
1597*4882a593Smuzhiyun 	for (i = 0; i < 4; i++) {
1598*4882a593Smuzhiyun 		struct wil_tid_crypto_rx_single *cc = &c->key_id[i];
1599*4882a593Smuzhiyun 
1600*4882a593Smuzhiyun 		seq_printf(s, " [%i%s]%6phN", i, cc->key_set ? "+" : "-",
1601*4882a593Smuzhiyun 			   cc->pn);
1602*4882a593Smuzhiyun 	}
1603*4882a593Smuzhiyun 	seq_puts(s, "\n");
1604*4882a593Smuzhiyun }
1605*4882a593Smuzhiyun 
sta_show(struct seq_file * s,void * data)1606*4882a593Smuzhiyun static int sta_show(struct seq_file *s, void *data)
1607*4882a593Smuzhiyun __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
1608*4882a593Smuzhiyun {
1609*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1610*4882a593Smuzhiyun 	int i, tid, mcs;
1611*4882a593Smuzhiyun 
1612*4882a593Smuzhiyun 	for (i = 0; i < wil->max_assoc_sta; i++) {
1613*4882a593Smuzhiyun 		struct wil_sta_info *p = &wil->sta[i];
1614*4882a593Smuzhiyun 		char *status = "unknown";
1615*4882a593Smuzhiyun 		u8 aid = 0;
1616*4882a593Smuzhiyun 		u8 mid;
1617*4882a593Smuzhiyun 		bool sta_connected = false;
1618*4882a593Smuzhiyun 
1619*4882a593Smuzhiyun 		switch (p->status) {
1620*4882a593Smuzhiyun 		case wil_sta_unused:
1621*4882a593Smuzhiyun 			status = "unused   ";
1622*4882a593Smuzhiyun 			break;
1623*4882a593Smuzhiyun 		case wil_sta_conn_pending:
1624*4882a593Smuzhiyun 			status = "pending  ";
1625*4882a593Smuzhiyun 			break;
1626*4882a593Smuzhiyun 		case wil_sta_connected:
1627*4882a593Smuzhiyun 			status = "connected";
1628*4882a593Smuzhiyun 			aid = p->aid;
1629*4882a593Smuzhiyun 			break;
1630*4882a593Smuzhiyun 		}
1631*4882a593Smuzhiyun 		mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;
1632*4882a593Smuzhiyun 		if (mid < GET_MAX_VIFS(wil)) {
1633*4882a593Smuzhiyun 			struct wil6210_vif *vif = wil->vifs[mid];
1634*4882a593Smuzhiyun 
1635*4882a593Smuzhiyun 			if (vif->wdev.iftype == NL80211_IFTYPE_STATION &&
1636*4882a593Smuzhiyun 			    p->status == wil_sta_connected)
1637*4882a593Smuzhiyun 				sta_connected = true;
1638*4882a593Smuzhiyun 		}
1639*4882a593Smuzhiyun 		/* print roam counter only for connected stations */
1640*4882a593Smuzhiyun 		if (sta_connected)
1641*4882a593Smuzhiyun 			seq_printf(s, "[%d] %pM connected (roam counter %d) MID %d AID %d\n",
1642*4882a593Smuzhiyun 				   i, p->addr, p->stats.ft_roams, mid, aid);
1643*4882a593Smuzhiyun 		else
1644*4882a593Smuzhiyun 			seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i,
1645*4882a593Smuzhiyun 				   p->addr, status, mid, aid);
1646*4882a593Smuzhiyun 
1647*4882a593Smuzhiyun 		if (p->status == wil_sta_connected) {
1648*4882a593Smuzhiyun 			spin_lock_bh(&p->tid_rx_lock);
1649*4882a593Smuzhiyun 			for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
1650*4882a593Smuzhiyun 				struct wil_tid_ampdu_rx *r = p->tid_rx[tid];
1651*4882a593Smuzhiyun 				struct wil_tid_crypto_rx *c =
1652*4882a593Smuzhiyun 						&p->tid_crypto_rx[tid];
1653*4882a593Smuzhiyun 
1654*4882a593Smuzhiyun 				if (r) {
1655*4882a593Smuzhiyun 					seq_printf(s, "  [%2d] ", tid);
1656*4882a593Smuzhiyun 					wil_print_rxtid(s, r);
1657*4882a593Smuzhiyun 				}
1658*4882a593Smuzhiyun 
1659*4882a593Smuzhiyun 				wil_print_rxtid_crypto(s, tid, c);
1660*4882a593Smuzhiyun 			}
1661*4882a593Smuzhiyun 			wil_print_rxtid_crypto(s, WIL_STA_TID_NUM,
1662*4882a593Smuzhiyun 					       &p->group_crypto_rx);
1663*4882a593Smuzhiyun 			spin_unlock_bh(&p->tid_rx_lock);
1664*4882a593Smuzhiyun 			seq_printf(s,
1665*4882a593Smuzhiyun 				   "Rx invalid frame: non-data %lu, short %lu, large %lu, replay %lu\n",
1666*4882a593Smuzhiyun 				   p->stats.rx_non_data_frame,
1667*4882a593Smuzhiyun 				   p->stats.rx_short_frame,
1668*4882a593Smuzhiyun 				   p->stats.rx_large_frame,
1669*4882a593Smuzhiyun 				   p->stats.rx_replay);
1670*4882a593Smuzhiyun 			seq_printf(s,
1671*4882a593Smuzhiyun 				   "mic error %lu, key error %lu, amsdu error %lu, csum error %lu\n",
1672*4882a593Smuzhiyun 				   p->stats.rx_mic_error,
1673*4882a593Smuzhiyun 				   p->stats.rx_key_error,
1674*4882a593Smuzhiyun 				   p->stats.rx_amsdu_error,
1675*4882a593Smuzhiyun 				   p->stats.rx_csum_err);
1676*4882a593Smuzhiyun 
1677*4882a593Smuzhiyun 			seq_puts(s, "Rx/MCS:");
1678*4882a593Smuzhiyun 			for (mcs = 0; mcs < ARRAY_SIZE(p->stats.rx_per_mcs);
1679*4882a593Smuzhiyun 			     mcs++)
1680*4882a593Smuzhiyun 				seq_printf(s, " %lld",
1681*4882a593Smuzhiyun 					   p->stats.rx_per_mcs[mcs]);
1682*4882a593Smuzhiyun 			seq_puts(s, "\n");
1683*4882a593Smuzhiyun 		}
1684*4882a593Smuzhiyun 	}
1685*4882a593Smuzhiyun 
1686*4882a593Smuzhiyun 	return 0;
1687*4882a593Smuzhiyun }
1688*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(sta);
1689*4882a593Smuzhiyun 
mids_show(struct seq_file * s,void * data)1690*4882a593Smuzhiyun static int mids_show(struct seq_file *s, void *data)
1691*4882a593Smuzhiyun {
1692*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1693*4882a593Smuzhiyun 	struct wil6210_vif *vif;
1694*4882a593Smuzhiyun 	struct net_device *ndev;
1695*4882a593Smuzhiyun 	int i;
1696*4882a593Smuzhiyun 
1697*4882a593Smuzhiyun 	mutex_lock(&wil->vif_mutex);
1698*4882a593Smuzhiyun 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
1699*4882a593Smuzhiyun 		vif = wil->vifs[i];
1700*4882a593Smuzhiyun 
1701*4882a593Smuzhiyun 		if (vif) {
1702*4882a593Smuzhiyun 			ndev = vif_to_ndev(vif);
1703*4882a593Smuzhiyun 			seq_printf(s, "[%d] %pM %s\n", i, ndev->dev_addr,
1704*4882a593Smuzhiyun 				   ndev->name);
1705*4882a593Smuzhiyun 		} else {
1706*4882a593Smuzhiyun 			seq_printf(s, "[%d] unused\n", i);
1707*4882a593Smuzhiyun 		}
1708*4882a593Smuzhiyun 	}
1709*4882a593Smuzhiyun 	mutex_unlock(&wil->vif_mutex);
1710*4882a593Smuzhiyun 
1711*4882a593Smuzhiyun 	return 0;
1712*4882a593Smuzhiyun }
1713*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(mids);
1714*4882a593Smuzhiyun 
wil_tx_latency_debugfs_show(struct seq_file * s,void * data)1715*4882a593Smuzhiyun static int wil_tx_latency_debugfs_show(struct seq_file *s, void *data)
1716*4882a593Smuzhiyun __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
1717*4882a593Smuzhiyun {
1718*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1719*4882a593Smuzhiyun 	int i, bin;
1720*4882a593Smuzhiyun 
1721*4882a593Smuzhiyun 	for (i = 0; i < wil->max_assoc_sta; i++) {
1722*4882a593Smuzhiyun 		struct wil_sta_info *p = &wil->sta[i];
1723*4882a593Smuzhiyun 		char *status = "unknown";
1724*4882a593Smuzhiyun 		u8 aid = 0;
1725*4882a593Smuzhiyun 		u8 mid;
1726*4882a593Smuzhiyun 
1727*4882a593Smuzhiyun 		if (!p->tx_latency_bins)
1728*4882a593Smuzhiyun 			continue;
1729*4882a593Smuzhiyun 
1730*4882a593Smuzhiyun 		switch (p->status) {
1731*4882a593Smuzhiyun 		case wil_sta_unused:
1732*4882a593Smuzhiyun 			status = "unused   ";
1733*4882a593Smuzhiyun 			break;
1734*4882a593Smuzhiyun 		case wil_sta_conn_pending:
1735*4882a593Smuzhiyun 			status = "pending  ";
1736*4882a593Smuzhiyun 			break;
1737*4882a593Smuzhiyun 		case wil_sta_connected:
1738*4882a593Smuzhiyun 			status = "connected";
1739*4882a593Smuzhiyun 			aid = p->aid;
1740*4882a593Smuzhiyun 			break;
1741*4882a593Smuzhiyun 		}
1742*4882a593Smuzhiyun 		mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;
1743*4882a593Smuzhiyun 		seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, p->addr, status,
1744*4882a593Smuzhiyun 			   mid, aid);
1745*4882a593Smuzhiyun 
1746*4882a593Smuzhiyun 		if (p->status == wil_sta_connected) {
1747*4882a593Smuzhiyun 			u64 num_packets = 0;
1748*4882a593Smuzhiyun 			u64 tx_latency_avg = p->stats.tx_latency_total_us;
1749*4882a593Smuzhiyun 
1750*4882a593Smuzhiyun 			seq_puts(s, "Tx/Latency bin:");
1751*4882a593Smuzhiyun 			for (bin = 0; bin < WIL_NUM_LATENCY_BINS; bin++) {
1752*4882a593Smuzhiyun 				seq_printf(s, " %lld",
1753*4882a593Smuzhiyun 					   p->tx_latency_bins[bin]);
1754*4882a593Smuzhiyun 				num_packets += p->tx_latency_bins[bin];
1755*4882a593Smuzhiyun 			}
1756*4882a593Smuzhiyun 			seq_puts(s, "\n");
1757*4882a593Smuzhiyun 			if (!num_packets)
1758*4882a593Smuzhiyun 				continue;
1759*4882a593Smuzhiyun 			do_div(tx_latency_avg, num_packets);
1760*4882a593Smuzhiyun 			seq_printf(s, "Tx/Latency min/avg/max (us): %d/%lld/%d",
1761*4882a593Smuzhiyun 				   p->stats.tx_latency_min_us,
1762*4882a593Smuzhiyun 				   tx_latency_avg,
1763*4882a593Smuzhiyun 				   p->stats.tx_latency_max_us);
1764*4882a593Smuzhiyun 
1765*4882a593Smuzhiyun 			seq_puts(s, "\n");
1766*4882a593Smuzhiyun 		}
1767*4882a593Smuzhiyun 	}
1768*4882a593Smuzhiyun 
1769*4882a593Smuzhiyun 	return 0;
1770*4882a593Smuzhiyun }
1771*4882a593Smuzhiyun 
wil_tx_latency_seq_open(struct inode * inode,struct file * file)1772*4882a593Smuzhiyun static int wil_tx_latency_seq_open(struct inode *inode, struct file *file)
1773*4882a593Smuzhiyun {
1774*4882a593Smuzhiyun 	return single_open(file, wil_tx_latency_debugfs_show,
1775*4882a593Smuzhiyun 			   inode->i_private);
1776*4882a593Smuzhiyun }
1777*4882a593Smuzhiyun 
wil_tx_latency_write(struct file * file,const char __user * buf,size_t len,loff_t * ppos)1778*4882a593Smuzhiyun static ssize_t wil_tx_latency_write(struct file *file, const char __user *buf,
1779*4882a593Smuzhiyun 				    size_t len, loff_t *ppos)
1780*4882a593Smuzhiyun {
1781*4882a593Smuzhiyun 	struct seq_file *s = file->private_data;
1782*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1783*4882a593Smuzhiyun 	int val, rc, i;
1784*4882a593Smuzhiyun 	bool enable;
1785*4882a593Smuzhiyun 
1786*4882a593Smuzhiyun 	rc = kstrtoint_from_user(buf, len, 0, &val);
1787*4882a593Smuzhiyun 	if (rc) {
1788*4882a593Smuzhiyun 		wil_err(wil, "Invalid argument\n");
1789*4882a593Smuzhiyun 		return rc;
1790*4882a593Smuzhiyun 	}
1791*4882a593Smuzhiyun 	if (val == 1)
1792*4882a593Smuzhiyun 		/* default resolution */
1793*4882a593Smuzhiyun 		val = 500;
1794*4882a593Smuzhiyun 	if (val && (val < 50 || val > 1000)) {
1795*4882a593Smuzhiyun 		wil_err(wil, "Invalid resolution %d\n", val);
1796*4882a593Smuzhiyun 		return -EINVAL;
1797*4882a593Smuzhiyun 	}
1798*4882a593Smuzhiyun 
1799*4882a593Smuzhiyun 	enable = !!val;
1800*4882a593Smuzhiyun 	if (wil->tx_latency == enable)
1801*4882a593Smuzhiyun 		return len;
1802*4882a593Smuzhiyun 
1803*4882a593Smuzhiyun 	wil_info(wil, "%s TX latency measurements (resolution %dusec)\n",
1804*4882a593Smuzhiyun 		 enable ? "Enabling" : "Disabling", val);
1805*4882a593Smuzhiyun 
1806*4882a593Smuzhiyun 	if (enable) {
1807*4882a593Smuzhiyun 		size_t sz = sizeof(u64) * WIL_NUM_LATENCY_BINS;
1808*4882a593Smuzhiyun 
1809*4882a593Smuzhiyun 		wil->tx_latency_res = val;
1810*4882a593Smuzhiyun 		for (i = 0; i < wil->max_assoc_sta; i++) {
1811*4882a593Smuzhiyun 			struct wil_sta_info *sta = &wil->sta[i];
1812*4882a593Smuzhiyun 
1813*4882a593Smuzhiyun 			kfree(sta->tx_latency_bins);
1814*4882a593Smuzhiyun 			sta->tx_latency_bins = kzalloc(sz, GFP_KERNEL);
1815*4882a593Smuzhiyun 			if (!sta->tx_latency_bins)
1816*4882a593Smuzhiyun 				return -ENOMEM;
1817*4882a593Smuzhiyun 			sta->stats.tx_latency_min_us = U32_MAX;
1818*4882a593Smuzhiyun 			sta->stats.tx_latency_max_us = 0;
1819*4882a593Smuzhiyun 			sta->stats.tx_latency_total_us = 0;
1820*4882a593Smuzhiyun 		}
1821*4882a593Smuzhiyun 	}
1822*4882a593Smuzhiyun 	wil->tx_latency = enable;
1823*4882a593Smuzhiyun 
1824*4882a593Smuzhiyun 	return len;
1825*4882a593Smuzhiyun }
1826*4882a593Smuzhiyun 
1827*4882a593Smuzhiyun static const struct file_operations fops_tx_latency = {
1828*4882a593Smuzhiyun 	.open		= wil_tx_latency_seq_open,
1829*4882a593Smuzhiyun 	.release	= single_release,
1830*4882a593Smuzhiyun 	.read		= seq_read,
1831*4882a593Smuzhiyun 	.write		= wil_tx_latency_write,
1832*4882a593Smuzhiyun 	.llseek		= seq_lseek,
1833*4882a593Smuzhiyun };
1834*4882a593Smuzhiyun 
wil_link_stats_print_basic(struct wil6210_vif * vif,struct seq_file * s,struct wmi_link_stats_basic * basic)1835*4882a593Smuzhiyun static void wil_link_stats_print_basic(struct wil6210_vif *vif,
1836*4882a593Smuzhiyun 				       struct seq_file *s,
1837*4882a593Smuzhiyun 				       struct wmi_link_stats_basic *basic)
1838*4882a593Smuzhiyun {
1839*4882a593Smuzhiyun 	char per[5] = "?";
1840*4882a593Smuzhiyun 
1841*4882a593Smuzhiyun 	if (basic->per_average != 0xff)
1842*4882a593Smuzhiyun 		snprintf(per, sizeof(per), "%d%%", basic->per_average);
1843*4882a593Smuzhiyun 
1844*4882a593Smuzhiyun 	seq_printf(s, "CID %d {\n"
1845*4882a593Smuzhiyun 		   "\tTxMCS %d TxTpt %d\n"
1846*4882a593Smuzhiyun 		   "\tGoodput(rx:tx) %d:%d\n"
1847*4882a593Smuzhiyun 		   "\tRxBcastFrames %d\n"
1848*4882a593Smuzhiyun 		   "\tRSSI %d SQI %d SNR %d PER %s\n"
1849*4882a593Smuzhiyun 		   "\tRx RFC %d Ant num %d\n"
1850*4882a593Smuzhiyun 		   "\tSectors(rx:tx) my %d:%d peer %d:%d\n"
1851*4882a593Smuzhiyun 		   "}\n",
1852*4882a593Smuzhiyun 		   basic->cid,
1853*4882a593Smuzhiyun 		   basic->bf_mcs, le32_to_cpu(basic->tx_tpt),
1854*4882a593Smuzhiyun 		   le32_to_cpu(basic->rx_goodput),
1855*4882a593Smuzhiyun 		   le32_to_cpu(basic->tx_goodput),
1856*4882a593Smuzhiyun 		   le32_to_cpu(basic->rx_bcast_frames),
1857*4882a593Smuzhiyun 		   basic->rssi, basic->sqi, basic->snr, per,
1858*4882a593Smuzhiyun 		   basic->selected_rfc, basic->rx_effective_ant_num,
1859*4882a593Smuzhiyun 		   basic->my_rx_sector, basic->my_tx_sector,
1860*4882a593Smuzhiyun 		   basic->other_rx_sector, basic->other_tx_sector);
1861*4882a593Smuzhiyun }
1862*4882a593Smuzhiyun 
wil_link_stats_print_global(struct wil6210_priv * wil,struct seq_file * s,struct wmi_link_stats_global * global)1863*4882a593Smuzhiyun static void wil_link_stats_print_global(struct wil6210_priv *wil,
1864*4882a593Smuzhiyun 					struct seq_file *s,
1865*4882a593Smuzhiyun 					struct wmi_link_stats_global *global)
1866*4882a593Smuzhiyun {
1867*4882a593Smuzhiyun 	seq_printf(s, "Frames(rx:tx) %d:%d\n"
1868*4882a593Smuzhiyun 		   "BA Frames(rx:tx) %d:%d\n"
1869*4882a593Smuzhiyun 		   "Beacons %d\n"
1870*4882a593Smuzhiyun 		   "Rx Errors (MIC:CRC) %d:%d\n"
1871*4882a593Smuzhiyun 		   "Tx Errors (no ack) %d\n",
1872*4882a593Smuzhiyun 		   le32_to_cpu(global->rx_frames),
1873*4882a593Smuzhiyun 		   le32_to_cpu(global->tx_frames),
1874*4882a593Smuzhiyun 		   le32_to_cpu(global->rx_ba_frames),
1875*4882a593Smuzhiyun 		   le32_to_cpu(global->tx_ba_frames),
1876*4882a593Smuzhiyun 		   le32_to_cpu(global->tx_beacons),
1877*4882a593Smuzhiyun 		   le32_to_cpu(global->rx_mic_errors),
1878*4882a593Smuzhiyun 		   le32_to_cpu(global->rx_crc_errors),
1879*4882a593Smuzhiyun 		   le32_to_cpu(global->tx_fail_no_ack));
1880*4882a593Smuzhiyun }
1881*4882a593Smuzhiyun 
wil_link_stats_debugfs_show_vif(struct wil6210_vif * vif,struct seq_file * s)1882*4882a593Smuzhiyun static void wil_link_stats_debugfs_show_vif(struct wil6210_vif *vif,
1883*4882a593Smuzhiyun 					    struct seq_file *s)
1884*4882a593Smuzhiyun {
1885*4882a593Smuzhiyun 	struct wil6210_priv *wil = vif_to_wil(vif);
1886*4882a593Smuzhiyun 	struct wmi_link_stats_basic *stats;
1887*4882a593Smuzhiyun 	int i;
1888*4882a593Smuzhiyun 
1889*4882a593Smuzhiyun 	if (!vif->fw_stats_ready) {
1890*4882a593Smuzhiyun 		seq_puts(s, "no statistics\n");
1891*4882a593Smuzhiyun 		return;
1892*4882a593Smuzhiyun 	}
1893*4882a593Smuzhiyun 
1894*4882a593Smuzhiyun 	seq_printf(s, "TSF %lld\n", vif->fw_stats_tsf);
1895*4882a593Smuzhiyun 	for (i = 0; i < wil->max_assoc_sta; i++) {
1896*4882a593Smuzhiyun 		if (wil->sta[i].status == wil_sta_unused)
1897*4882a593Smuzhiyun 			continue;
1898*4882a593Smuzhiyun 		if (wil->sta[i].mid != vif->mid)
1899*4882a593Smuzhiyun 			continue;
1900*4882a593Smuzhiyun 
1901*4882a593Smuzhiyun 		stats = &wil->sta[i].fw_stats_basic;
1902*4882a593Smuzhiyun 		wil_link_stats_print_basic(vif, s, stats);
1903*4882a593Smuzhiyun 	}
1904*4882a593Smuzhiyun }
1905*4882a593Smuzhiyun 
wil_link_stats_debugfs_show(struct seq_file * s,void * data)1906*4882a593Smuzhiyun static int wil_link_stats_debugfs_show(struct seq_file *s, void *data)
1907*4882a593Smuzhiyun {
1908*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1909*4882a593Smuzhiyun 	struct wil6210_vif *vif;
1910*4882a593Smuzhiyun 	int i, rc;
1911*4882a593Smuzhiyun 
1912*4882a593Smuzhiyun 	rc = mutex_lock_interruptible(&wil->vif_mutex);
1913*4882a593Smuzhiyun 	if (rc)
1914*4882a593Smuzhiyun 		return rc;
1915*4882a593Smuzhiyun 
1916*4882a593Smuzhiyun 	/* iterate over all MIDs and show per-cid statistics. Then show the
1917*4882a593Smuzhiyun 	 * global statistics
1918*4882a593Smuzhiyun 	 */
1919*4882a593Smuzhiyun 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
1920*4882a593Smuzhiyun 		vif = wil->vifs[i];
1921*4882a593Smuzhiyun 
1922*4882a593Smuzhiyun 		seq_printf(s, "MID %d ", i);
1923*4882a593Smuzhiyun 		if (!vif) {
1924*4882a593Smuzhiyun 			seq_puts(s, "unused\n");
1925*4882a593Smuzhiyun 			continue;
1926*4882a593Smuzhiyun 		}
1927*4882a593Smuzhiyun 
1928*4882a593Smuzhiyun 		wil_link_stats_debugfs_show_vif(vif, s);
1929*4882a593Smuzhiyun 	}
1930*4882a593Smuzhiyun 
1931*4882a593Smuzhiyun 	mutex_unlock(&wil->vif_mutex);
1932*4882a593Smuzhiyun 
1933*4882a593Smuzhiyun 	return 0;
1934*4882a593Smuzhiyun }
1935*4882a593Smuzhiyun 
wil_link_stats_seq_open(struct inode * inode,struct file * file)1936*4882a593Smuzhiyun static int wil_link_stats_seq_open(struct inode *inode, struct file *file)
1937*4882a593Smuzhiyun {
1938*4882a593Smuzhiyun 	return single_open(file, wil_link_stats_debugfs_show, inode->i_private);
1939*4882a593Smuzhiyun }
1940*4882a593Smuzhiyun 
wil_link_stats_write(struct file * file,const char __user * buf,size_t len,loff_t * ppos)1941*4882a593Smuzhiyun static ssize_t wil_link_stats_write(struct file *file, const char __user *buf,
1942*4882a593Smuzhiyun 				    size_t len, loff_t *ppos)
1943*4882a593Smuzhiyun {
1944*4882a593Smuzhiyun 	struct seq_file *s = file->private_data;
1945*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
1946*4882a593Smuzhiyun 	int cid, interval, rc, i;
1947*4882a593Smuzhiyun 	struct wil6210_vif *vif;
1948*4882a593Smuzhiyun 	char *kbuf = kmalloc(len + 1, GFP_KERNEL);
1949*4882a593Smuzhiyun 
1950*4882a593Smuzhiyun 	if (!kbuf)
1951*4882a593Smuzhiyun 		return -ENOMEM;
1952*4882a593Smuzhiyun 
1953*4882a593Smuzhiyun 	rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
1954*4882a593Smuzhiyun 	if (rc != len) {
1955*4882a593Smuzhiyun 		kfree(kbuf);
1956*4882a593Smuzhiyun 		return rc >= 0 ? -EIO : rc;
1957*4882a593Smuzhiyun 	}
1958*4882a593Smuzhiyun 
1959*4882a593Smuzhiyun 	kbuf[len] = '\0';
1960*4882a593Smuzhiyun 	/* specify cid (use -1 for all cids) and snapshot interval in ms */
1961*4882a593Smuzhiyun 	rc = sscanf(kbuf, "%d %d", &cid, &interval);
1962*4882a593Smuzhiyun 	kfree(kbuf);
1963*4882a593Smuzhiyun 	if (rc < 0)
1964*4882a593Smuzhiyun 		return rc;
1965*4882a593Smuzhiyun 	if (rc < 2 || interval < 0)
1966*4882a593Smuzhiyun 		return -EINVAL;
1967*4882a593Smuzhiyun 
1968*4882a593Smuzhiyun 	wil_info(wil, "request link statistics, cid %d interval %d\n",
1969*4882a593Smuzhiyun 		 cid, interval);
1970*4882a593Smuzhiyun 
1971*4882a593Smuzhiyun 	rc = mutex_lock_interruptible(&wil->vif_mutex);
1972*4882a593Smuzhiyun 	if (rc)
1973*4882a593Smuzhiyun 		return rc;
1974*4882a593Smuzhiyun 
1975*4882a593Smuzhiyun 	for (i = 0; i < GET_MAX_VIFS(wil); i++) {
1976*4882a593Smuzhiyun 		vif = wil->vifs[i];
1977*4882a593Smuzhiyun 		if (!vif)
1978*4882a593Smuzhiyun 			continue;
1979*4882a593Smuzhiyun 
1980*4882a593Smuzhiyun 		rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_BASIC,
1981*4882a593Smuzhiyun 					(cid == -1 ? 0xff : cid), interval);
1982*4882a593Smuzhiyun 		if (rc)
1983*4882a593Smuzhiyun 			wil_err(wil, "link statistics failed for mid %d\n", i);
1984*4882a593Smuzhiyun 	}
1985*4882a593Smuzhiyun 	mutex_unlock(&wil->vif_mutex);
1986*4882a593Smuzhiyun 
1987*4882a593Smuzhiyun 	return len;
1988*4882a593Smuzhiyun }
1989*4882a593Smuzhiyun 
1990*4882a593Smuzhiyun static const struct file_operations fops_link_stats = {
1991*4882a593Smuzhiyun 	.open		= wil_link_stats_seq_open,
1992*4882a593Smuzhiyun 	.release	= single_release,
1993*4882a593Smuzhiyun 	.read		= seq_read,
1994*4882a593Smuzhiyun 	.write		= wil_link_stats_write,
1995*4882a593Smuzhiyun 	.llseek		= seq_lseek,
1996*4882a593Smuzhiyun };
1997*4882a593Smuzhiyun 
1998*4882a593Smuzhiyun static int
wil_link_stats_global_debugfs_show(struct seq_file * s,void * data)1999*4882a593Smuzhiyun wil_link_stats_global_debugfs_show(struct seq_file *s, void *data)
2000*4882a593Smuzhiyun {
2001*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
2002*4882a593Smuzhiyun 
2003*4882a593Smuzhiyun 	if (!wil->fw_stats_global.ready)
2004*4882a593Smuzhiyun 		return 0;
2005*4882a593Smuzhiyun 
2006*4882a593Smuzhiyun 	seq_printf(s, "TSF %lld\n", wil->fw_stats_global.tsf);
2007*4882a593Smuzhiyun 	wil_link_stats_print_global(wil, s, &wil->fw_stats_global.stats);
2008*4882a593Smuzhiyun 
2009*4882a593Smuzhiyun 	return 0;
2010*4882a593Smuzhiyun }
2011*4882a593Smuzhiyun 
2012*4882a593Smuzhiyun static int
wil_link_stats_global_seq_open(struct inode * inode,struct file * file)2013*4882a593Smuzhiyun wil_link_stats_global_seq_open(struct inode *inode, struct file *file)
2014*4882a593Smuzhiyun {
2015*4882a593Smuzhiyun 	return single_open(file, wil_link_stats_global_debugfs_show,
2016*4882a593Smuzhiyun 			   inode->i_private);
2017*4882a593Smuzhiyun }
2018*4882a593Smuzhiyun 
2019*4882a593Smuzhiyun static ssize_t
wil_link_stats_global_write(struct file * file,const char __user * buf,size_t len,loff_t * ppos)2020*4882a593Smuzhiyun wil_link_stats_global_write(struct file *file, const char __user *buf,
2021*4882a593Smuzhiyun 			    size_t len, loff_t *ppos)
2022*4882a593Smuzhiyun {
2023*4882a593Smuzhiyun 	struct seq_file *s = file->private_data;
2024*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
2025*4882a593Smuzhiyun 	int interval, rc;
2026*4882a593Smuzhiyun 	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
2027*4882a593Smuzhiyun 
2028*4882a593Smuzhiyun 	/* specify snapshot interval in ms */
2029*4882a593Smuzhiyun 	rc = kstrtoint_from_user(buf, len, 0, &interval);
2030*4882a593Smuzhiyun 	if (rc || interval < 0) {
2031*4882a593Smuzhiyun 		wil_err(wil, "Invalid argument\n");
2032*4882a593Smuzhiyun 		return -EINVAL;
2033*4882a593Smuzhiyun 	}
2034*4882a593Smuzhiyun 
2035*4882a593Smuzhiyun 	wil_info(wil, "request global link stats, interval %d\n", interval);
2036*4882a593Smuzhiyun 
2037*4882a593Smuzhiyun 	rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_GLOBAL, 0, interval);
2038*4882a593Smuzhiyun 	if (rc)
2039*4882a593Smuzhiyun 		wil_err(wil, "global link stats failed %d\n", rc);
2040*4882a593Smuzhiyun 
2041*4882a593Smuzhiyun 	return rc ? rc : len;
2042*4882a593Smuzhiyun }
2043*4882a593Smuzhiyun 
2044*4882a593Smuzhiyun static const struct file_operations fops_link_stats_global = {
2045*4882a593Smuzhiyun 	.open		= wil_link_stats_global_seq_open,
2046*4882a593Smuzhiyun 	.release	= single_release,
2047*4882a593Smuzhiyun 	.read		= seq_read,
2048*4882a593Smuzhiyun 	.write		= wil_link_stats_global_write,
2049*4882a593Smuzhiyun 	.llseek		= seq_lseek,
2050*4882a593Smuzhiyun };
2051*4882a593Smuzhiyun 
wil_read_file_led_cfg(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)2052*4882a593Smuzhiyun static ssize_t wil_read_file_led_cfg(struct file *file, char __user *user_buf,
2053*4882a593Smuzhiyun 				     size_t count, loff_t *ppos)
2054*4882a593Smuzhiyun {
2055*4882a593Smuzhiyun 	char buf[80];
2056*4882a593Smuzhiyun 	int n;
2057*4882a593Smuzhiyun 
2058*4882a593Smuzhiyun 	n = snprintf(buf, sizeof(buf),
2059*4882a593Smuzhiyun 		     "led_id is set to %d, echo 1 to enable, 0 to disable\n",
2060*4882a593Smuzhiyun 		     led_id);
2061*4882a593Smuzhiyun 
2062*4882a593Smuzhiyun 	n = min_t(int, n, sizeof(buf));
2063*4882a593Smuzhiyun 
2064*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos,
2065*4882a593Smuzhiyun 				       buf, n);
2066*4882a593Smuzhiyun }
2067*4882a593Smuzhiyun 
wil_write_file_led_cfg(struct file * file,const char __user * buf_,size_t count,loff_t * ppos)2068*4882a593Smuzhiyun static ssize_t wil_write_file_led_cfg(struct file *file,
2069*4882a593Smuzhiyun 				      const char __user *buf_,
2070*4882a593Smuzhiyun 				      size_t count, loff_t *ppos)
2071*4882a593Smuzhiyun {
2072*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
2073*4882a593Smuzhiyun 	int val;
2074*4882a593Smuzhiyun 	int rc;
2075*4882a593Smuzhiyun 
2076*4882a593Smuzhiyun 	rc = kstrtoint_from_user(buf_, count, 0, &val);
2077*4882a593Smuzhiyun 	if (rc) {
2078*4882a593Smuzhiyun 		wil_err(wil, "Invalid argument\n");
2079*4882a593Smuzhiyun 		return rc;
2080*4882a593Smuzhiyun 	}
2081*4882a593Smuzhiyun 
2082*4882a593Smuzhiyun 	wil_info(wil, "%s led %d\n", val ? "Enabling" : "Disabling", led_id);
2083*4882a593Smuzhiyun 	rc = wmi_led_cfg(wil, val);
2084*4882a593Smuzhiyun 	if (rc) {
2085*4882a593Smuzhiyun 		wil_info(wil, "%s led %d failed\n",
2086*4882a593Smuzhiyun 			 val ? "Enabling" : "Disabling", led_id);
2087*4882a593Smuzhiyun 		return rc;
2088*4882a593Smuzhiyun 	}
2089*4882a593Smuzhiyun 
2090*4882a593Smuzhiyun 	return count;
2091*4882a593Smuzhiyun }
2092*4882a593Smuzhiyun 
2093*4882a593Smuzhiyun static const struct file_operations fops_led_cfg = {
2094*4882a593Smuzhiyun 	.read = wil_read_file_led_cfg,
2095*4882a593Smuzhiyun 	.write = wil_write_file_led_cfg,
2096*4882a593Smuzhiyun 	.open  = simple_open,
2097*4882a593Smuzhiyun };
2098*4882a593Smuzhiyun 
2099*4882a593Smuzhiyun /* led_blink_time, write:
2100*4882a593Smuzhiyun  * "<blink_on_slow> <blink_off_slow> <blink_on_med> <blink_off_med> <blink_on_fast> <blink_off_fast>
2101*4882a593Smuzhiyun  */
wil_write_led_blink_time(struct file * file,const char __user * buf,size_t len,loff_t * ppos)2102*4882a593Smuzhiyun static ssize_t wil_write_led_blink_time(struct file *file,
2103*4882a593Smuzhiyun 					const char __user *buf,
2104*4882a593Smuzhiyun 					size_t len, loff_t *ppos)
2105*4882a593Smuzhiyun {
2106*4882a593Smuzhiyun 	int rc;
2107*4882a593Smuzhiyun 	char *kbuf = kmalloc(len + 1, GFP_KERNEL);
2108*4882a593Smuzhiyun 
2109*4882a593Smuzhiyun 	if (!kbuf)
2110*4882a593Smuzhiyun 		return -ENOMEM;
2111*4882a593Smuzhiyun 
2112*4882a593Smuzhiyun 	rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
2113*4882a593Smuzhiyun 	if (rc != len) {
2114*4882a593Smuzhiyun 		kfree(kbuf);
2115*4882a593Smuzhiyun 		return rc >= 0 ? -EIO : rc;
2116*4882a593Smuzhiyun 	}
2117*4882a593Smuzhiyun 
2118*4882a593Smuzhiyun 	kbuf[len] = '\0';
2119*4882a593Smuzhiyun 	rc = sscanf(kbuf, "%d %d %d %d %d %d",
2120*4882a593Smuzhiyun 		    &led_blink_time[WIL_LED_TIME_SLOW].on_ms,
2121*4882a593Smuzhiyun 		    &led_blink_time[WIL_LED_TIME_SLOW].off_ms,
2122*4882a593Smuzhiyun 		    &led_blink_time[WIL_LED_TIME_MED].on_ms,
2123*4882a593Smuzhiyun 		    &led_blink_time[WIL_LED_TIME_MED].off_ms,
2124*4882a593Smuzhiyun 		    &led_blink_time[WIL_LED_TIME_FAST].on_ms,
2125*4882a593Smuzhiyun 		    &led_blink_time[WIL_LED_TIME_FAST].off_ms);
2126*4882a593Smuzhiyun 	kfree(kbuf);
2127*4882a593Smuzhiyun 
2128*4882a593Smuzhiyun 	if (rc < 0)
2129*4882a593Smuzhiyun 		return rc;
2130*4882a593Smuzhiyun 	if (rc < 6)
2131*4882a593Smuzhiyun 		return -EINVAL;
2132*4882a593Smuzhiyun 
2133*4882a593Smuzhiyun 	return len;
2134*4882a593Smuzhiyun }
2135*4882a593Smuzhiyun 
wil_read_led_blink_time(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)2136*4882a593Smuzhiyun static ssize_t wil_read_led_blink_time(struct file *file, char __user *user_buf,
2137*4882a593Smuzhiyun 				       size_t count, loff_t *ppos)
2138*4882a593Smuzhiyun {
2139*4882a593Smuzhiyun 	static char text[400];
2140*4882a593Smuzhiyun 
2141*4882a593Smuzhiyun 	snprintf(text, sizeof(text),
2142*4882a593Smuzhiyun 		 "To set led blink on/off time variables write:\n"
2143*4882a593Smuzhiyun 		 "<blink_on_slow> <blink_off_slow> <blink_on_med> "
2144*4882a593Smuzhiyun 		 "<blink_off_med> <blink_on_fast> <blink_off_fast>\n"
2145*4882a593Smuzhiyun 		 "The current values are:\n"
2146*4882a593Smuzhiyun 		 "%d %d %d %d %d %d\n",
2147*4882a593Smuzhiyun 		 led_blink_time[WIL_LED_TIME_SLOW].on_ms,
2148*4882a593Smuzhiyun 		 led_blink_time[WIL_LED_TIME_SLOW].off_ms,
2149*4882a593Smuzhiyun 		 led_blink_time[WIL_LED_TIME_MED].on_ms,
2150*4882a593Smuzhiyun 		 led_blink_time[WIL_LED_TIME_MED].off_ms,
2151*4882a593Smuzhiyun 		 led_blink_time[WIL_LED_TIME_FAST].on_ms,
2152*4882a593Smuzhiyun 		 led_blink_time[WIL_LED_TIME_FAST].off_ms);
2153*4882a593Smuzhiyun 
2154*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, text,
2155*4882a593Smuzhiyun 				       sizeof(text));
2156*4882a593Smuzhiyun }
2157*4882a593Smuzhiyun 
2158*4882a593Smuzhiyun static const struct file_operations fops_led_blink_time = {
2159*4882a593Smuzhiyun 	.read = wil_read_led_blink_time,
2160*4882a593Smuzhiyun 	.write = wil_write_led_blink_time,
2161*4882a593Smuzhiyun 	.open  = simple_open,
2162*4882a593Smuzhiyun };
2163*4882a593Smuzhiyun 
2164*4882a593Smuzhiyun /*---------FW capabilities------------*/
wil_fw_capabilities_debugfs_show(struct seq_file * s,void * data)2165*4882a593Smuzhiyun static int wil_fw_capabilities_debugfs_show(struct seq_file *s, void *data)
2166*4882a593Smuzhiyun {
2167*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
2168*4882a593Smuzhiyun 
2169*4882a593Smuzhiyun 	seq_printf(s, "fw_capabilities : %*pb\n", WMI_FW_CAPABILITY_MAX,
2170*4882a593Smuzhiyun 		   wil->fw_capabilities);
2171*4882a593Smuzhiyun 
2172*4882a593Smuzhiyun 	return 0;
2173*4882a593Smuzhiyun }
2174*4882a593Smuzhiyun 
wil_fw_capabilities_seq_open(struct inode * inode,struct file * file)2175*4882a593Smuzhiyun static int wil_fw_capabilities_seq_open(struct inode *inode, struct file *file)
2176*4882a593Smuzhiyun {
2177*4882a593Smuzhiyun 	return single_open(file, wil_fw_capabilities_debugfs_show,
2178*4882a593Smuzhiyun 			   inode->i_private);
2179*4882a593Smuzhiyun }
2180*4882a593Smuzhiyun 
2181*4882a593Smuzhiyun static const struct file_operations fops_fw_capabilities = {
2182*4882a593Smuzhiyun 	.open		= wil_fw_capabilities_seq_open,
2183*4882a593Smuzhiyun 	.release	= single_release,
2184*4882a593Smuzhiyun 	.read		= seq_read,
2185*4882a593Smuzhiyun 	.llseek		= seq_lseek,
2186*4882a593Smuzhiyun };
2187*4882a593Smuzhiyun 
2188*4882a593Smuzhiyun /*---------FW version------------*/
wil_fw_version_debugfs_show(struct seq_file * s,void * data)2189*4882a593Smuzhiyun static int wil_fw_version_debugfs_show(struct seq_file *s, void *data)
2190*4882a593Smuzhiyun {
2191*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
2192*4882a593Smuzhiyun 
2193*4882a593Smuzhiyun 	if (wil->fw_version[0])
2194*4882a593Smuzhiyun 		seq_printf(s, "%s\n", wil->fw_version);
2195*4882a593Smuzhiyun 	else
2196*4882a593Smuzhiyun 		seq_puts(s, "N/A\n");
2197*4882a593Smuzhiyun 
2198*4882a593Smuzhiyun 	return 0;
2199*4882a593Smuzhiyun }
2200*4882a593Smuzhiyun 
wil_fw_version_seq_open(struct inode * inode,struct file * file)2201*4882a593Smuzhiyun static int wil_fw_version_seq_open(struct inode *inode, struct file *file)
2202*4882a593Smuzhiyun {
2203*4882a593Smuzhiyun 	return single_open(file, wil_fw_version_debugfs_show,
2204*4882a593Smuzhiyun 			   inode->i_private);
2205*4882a593Smuzhiyun }
2206*4882a593Smuzhiyun 
2207*4882a593Smuzhiyun static const struct file_operations fops_fw_version = {
2208*4882a593Smuzhiyun 	.open		= wil_fw_version_seq_open,
2209*4882a593Smuzhiyun 	.release	= single_release,
2210*4882a593Smuzhiyun 	.read		= seq_read,
2211*4882a593Smuzhiyun 	.llseek		= seq_lseek,
2212*4882a593Smuzhiyun };
2213*4882a593Smuzhiyun 
2214*4882a593Smuzhiyun /*---------suspend_stats---------*/
wil_write_suspend_stats(struct file * file,const char __user * buf,size_t len,loff_t * ppos)2215*4882a593Smuzhiyun static ssize_t wil_write_suspend_stats(struct file *file,
2216*4882a593Smuzhiyun 				       const char __user *buf,
2217*4882a593Smuzhiyun 				       size_t len, loff_t *ppos)
2218*4882a593Smuzhiyun {
2219*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
2220*4882a593Smuzhiyun 
2221*4882a593Smuzhiyun 	memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats));
2222*4882a593Smuzhiyun 
2223*4882a593Smuzhiyun 	return len;
2224*4882a593Smuzhiyun }
2225*4882a593Smuzhiyun 
wil_read_suspend_stats(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)2226*4882a593Smuzhiyun static ssize_t wil_read_suspend_stats(struct file *file,
2227*4882a593Smuzhiyun 				      char __user *user_buf,
2228*4882a593Smuzhiyun 				      size_t count, loff_t *ppos)
2229*4882a593Smuzhiyun {
2230*4882a593Smuzhiyun 	struct wil6210_priv *wil = file->private_data;
2231*4882a593Smuzhiyun 	char *text;
2232*4882a593Smuzhiyun 	int n, ret, text_size = 500;
2233*4882a593Smuzhiyun 
2234*4882a593Smuzhiyun 	text = kmalloc(text_size, GFP_KERNEL);
2235*4882a593Smuzhiyun 	if (!text)
2236*4882a593Smuzhiyun 		return -ENOMEM;
2237*4882a593Smuzhiyun 
2238*4882a593Smuzhiyun 	n = snprintf(text, text_size,
2239*4882a593Smuzhiyun 		     "Radio on suspend statistics:\n"
2240*4882a593Smuzhiyun 		     "successful suspends:%ld failed suspends:%ld\n"
2241*4882a593Smuzhiyun 		     "successful resumes:%ld failed resumes:%ld\n"
2242*4882a593Smuzhiyun 		     "rejected by device:%ld\n"
2243*4882a593Smuzhiyun 		     "Radio off suspend statistics:\n"
2244*4882a593Smuzhiyun 		     "successful suspends:%ld failed suspends:%ld\n"
2245*4882a593Smuzhiyun 		     "successful resumes:%ld failed resumes:%ld\n"
2246*4882a593Smuzhiyun 		     "General statistics:\n"
2247*4882a593Smuzhiyun 		     "rejected by host:%ld\n",
2248*4882a593Smuzhiyun 		     wil->suspend_stats.r_on.successful_suspends,
2249*4882a593Smuzhiyun 		     wil->suspend_stats.r_on.failed_suspends,
2250*4882a593Smuzhiyun 		     wil->suspend_stats.r_on.successful_resumes,
2251*4882a593Smuzhiyun 		     wil->suspend_stats.r_on.failed_resumes,
2252*4882a593Smuzhiyun 		     wil->suspend_stats.rejected_by_device,
2253*4882a593Smuzhiyun 		     wil->suspend_stats.r_off.successful_suspends,
2254*4882a593Smuzhiyun 		     wil->suspend_stats.r_off.failed_suspends,
2255*4882a593Smuzhiyun 		     wil->suspend_stats.r_off.successful_resumes,
2256*4882a593Smuzhiyun 		     wil->suspend_stats.r_off.failed_resumes,
2257*4882a593Smuzhiyun 		     wil->suspend_stats.rejected_by_host);
2258*4882a593Smuzhiyun 
2259*4882a593Smuzhiyun 	n = min_t(int, n, text_size);
2260*4882a593Smuzhiyun 
2261*4882a593Smuzhiyun 	ret = simple_read_from_buffer(user_buf, count, ppos, text, n);
2262*4882a593Smuzhiyun 
2263*4882a593Smuzhiyun 	kfree(text);
2264*4882a593Smuzhiyun 
2265*4882a593Smuzhiyun 	return ret;
2266*4882a593Smuzhiyun }
2267*4882a593Smuzhiyun 
2268*4882a593Smuzhiyun static const struct file_operations fops_suspend_stats = {
2269*4882a593Smuzhiyun 	.read = wil_read_suspend_stats,
2270*4882a593Smuzhiyun 	.write = wil_write_suspend_stats,
2271*4882a593Smuzhiyun 	.open  = simple_open,
2272*4882a593Smuzhiyun };
2273*4882a593Smuzhiyun 
2274*4882a593Smuzhiyun /*---------compressed_rx_status---------*/
wil_compressed_rx_status_write(struct file * file,const char __user * buf,size_t len,loff_t * ppos)2275*4882a593Smuzhiyun static ssize_t wil_compressed_rx_status_write(struct file *file,
2276*4882a593Smuzhiyun 					      const char __user *buf,
2277*4882a593Smuzhiyun 					      size_t len, loff_t *ppos)
2278*4882a593Smuzhiyun {
2279*4882a593Smuzhiyun 	struct seq_file *s = file->private_data;
2280*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
2281*4882a593Smuzhiyun 	int compressed_rx_status;
2282*4882a593Smuzhiyun 	int rc;
2283*4882a593Smuzhiyun 
2284*4882a593Smuzhiyun 	rc = kstrtoint_from_user(buf, len, 0, &compressed_rx_status);
2285*4882a593Smuzhiyun 	if (rc) {
2286*4882a593Smuzhiyun 		wil_err(wil, "Invalid argument\n");
2287*4882a593Smuzhiyun 		return rc;
2288*4882a593Smuzhiyun 	}
2289*4882a593Smuzhiyun 
2290*4882a593Smuzhiyun 	if (wil_has_active_ifaces(wil, true, false)) {
2291*4882a593Smuzhiyun 		wil_err(wil, "cannot change edma config after iface is up\n");
2292*4882a593Smuzhiyun 		return -EPERM;
2293*4882a593Smuzhiyun 	}
2294*4882a593Smuzhiyun 
2295*4882a593Smuzhiyun 	wil_info(wil, "%sable compressed_rx_status\n",
2296*4882a593Smuzhiyun 		 compressed_rx_status ? "En" : "Dis");
2297*4882a593Smuzhiyun 
2298*4882a593Smuzhiyun 	wil->use_compressed_rx_status = compressed_rx_status;
2299*4882a593Smuzhiyun 
2300*4882a593Smuzhiyun 	return len;
2301*4882a593Smuzhiyun }
2302*4882a593Smuzhiyun 
2303*4882a593Smuzhiyun static int
wil_compressed_rx_status_show(struct seq_file * s,void * data)2304*4882a593Smuzhiyun wil_compressed_rx_status_show(struct seq_file *s, void *data)
2305*4882a593Smuzhiyun {
2306*4882a593Smuzhiyun 	struct wil6210_priv *wil = s->private;
2307*4882a593Smuzhiyun 
2308*4882a593Smuzhiyun 	seq_printf(s, "%d\n", wil->use_compressed_rx_status);
2309*4882a593Smuzhiyun 
2310*4882a593Smuzhiyun 	return 0;
2311*4882a593Smuzhiyun }
2312*4882a593Smuzhiyun 
2313*4882a593Smuzhiyun static int
wil_compressed_rx_status_seq_open(struct inode * inode,struct file * file)2314*4882a593Smuzhiyun wil_compressed_rx_status_seq_open(struct inode *inode, struct file *file)
2315*4882a593Smuzhiyun {
2316*4882a593Smuzhiyun 	return single_open(file, wil_compressed_rx_status_show,
2317*4882a593Smuzhiyun 			   inode->i_private);
2318*4882a593Smuzhiyun }
2319*4882a593Smuzhiyun 
2320*4882a593Smuzhiyun static const struct file_operations fops_compressed_rx_status = {
2321*4882a593Smuzhiyun 	.open  = wil_compressed_rx_status_seq_open,
2322*4882a593Smuzhiyun 	.release = single_release,
2323*4882a593Smuzhiyun 	.read = seq_read,
2324*4882a593Smuzhiyun 	.write = wil_compressed_rx_status_write,
2325*4882a593Smuzhiyun 	.llseek	= seq_lseek,
2326*4882a593Smuzhiyun };
2327*4882a593Smuzhiyun 
2328*4882a593Smuzhiyun /*----------------*/
wil6210_debugfs_init_blobs(struct wil6210_priv * wil,struct dentry * dbg)2329*4882a593Smuzhiyun static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
2330*4882a593Smuzhiyun 				       struct dentry *dbg)
2331*4882a593Smuzhiyun {
2332*4882a593Smuzhiyun 	int i;
2333*4882a593Smuzhiyun 	char name[32];
2334*4882a593Smuzhiyun 
2335*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
2336*4882a593Smuzhiyun 		struct wil_blob_wrapper *wil_blob = &wil->blobs[i];
2337*4882a593Smuzhiyun 		struct debugfs_blob_wrapper *blob = &wil_blob->blob;
2338*4882a593Smuzhiyun 		const struct fw_map *map = &fw_mapping[i];
2339*4882a593Smuzhiyun 
2340*4882a593Smuzhiyun 		if (!map->name)
2341*4882a593Smuzhiyun 			continue;
2342*4882a593Smuzhiyun 
2343*4882a593Smuzhiyun 		wil_blob->wil = wil;
2344*4882a593Smuzhiyun 		blob->data = (void * __force)wil->csr + HOSTADDR(map->host);
2345*4882a593Smuzhiyun 		blob->size = map->to - map->from;
2346*4882a593Smuzhiyun 		snprintf(name, sizeof(name), "blob_%s", map->name);
2347*4882a593Smuzhiyun 		wil_debugfs_create_ioblob(name, 0444, dbg, wil_blob);
2348*4882a593Smuzhiyun 	}
2349*4882a593Smuzhiyun }
2350*4882a593Smuzhiyun 
2351*4882a593Smuzhiyun /* misc files */
2352*4882a593Smuzhiyun static const struct {
2353*4882a593Smuzhiyun 	const char *name;
2354*4882a593Smuzhiyun 	umode_t mode;
2355*4882a593Smuzhiyun 	const struct file_operations *fops;
2356*4882a593Smuzhiyun } dbg_files[] = {
2357*4882a593Smuzhiyun 	{"mbox",	0444,		&mbox_fops},
2358*4882a593Smuzhiyun 	{"rings",	0444,		&ring_fops},
2359*4882a593Smuzhiyun 	{"stations", 0444,		&sta_fops},
2360*4882a593Smuzhiyun 	{"mids",	0444,		&mids_fops},
2361*4882a593Smuzhiyun 	{"desc",	0444,		&txdesc_fops},
2362*4882a593Smuzhiyun 	{"bf",		0444,		&bf_fops},
2363*4882a593Smuzhiyun 	{"mem_val",	0644,		&memread_fops},
2364*4882a593Smuzhiyun 	{"rxon",	0244,		&fops_rxon},
2365*4882a593Smuzhiyun 	{"tx_mgmt",	0244,		&fops_txmgmt},
2366*4882a593Smuzhiyun 	{"wmi_send", 0244,		&fops_wmi},
2367*4882a593Smuzhiyun 	{"back",	0644,		&fops_back},
2368*4882a593Smuzhiyun 	{"pmccfg",	0644,		&fops_pmccfg},
2369*4882a593Smuzhiyun 	{"pmcdata",	0444,		&fops_pmcdata},
2370*4882a593Smuzhiyun 	{"pmcring",	0444,		&fops_pmcring},
2371*4882a593Smuzhiyun 	{"temp",	0444,		&temp_fops},
2372*4882a593Smuzhiyun 	{"freq",	0444,		&freq_fops},
2373*4882a593Smuzhiyun 	{"link",	0444,		&link_fops},
2374*4882a593Smuzhiyun 	{"info",	0444,		&info_fops},
2375*4882a593Smuzhiyun 	{"recovery", 0644,		&fops_recovery},
2376*4882a593Smuzhiyun 	{"led_cfg",	0644,		&fops_led_cfg},
2377*4882a593Smuzhiyun 	{"led_blink_time",	0644,	&fops_led_blink_time},
2378*4882a593Smuzhiyun 	{"fw_capabilities",	0444,	&fops_fw_capabilities},
2379*4882a593Smuzhiyun 	{"fw_version",	0444,		&fops_fw_version},
2380*4882a593Smuzhiyun 	{"suspend_stats",	0644,	&fops_suspend_stats},
2381*4882a593Smuzhiyun 	{"compressed_rx_status", 0644,	&fops_compressed_rx_status},
2382*4882a593Smuzhiyun 	{"srings",	0444,		&srings_fops},
2383*4882a593Smuzhiyun 	{"status_msg",	0444,		&status_msg_fops},
2384*4882a593Smuzhiyun 	{"rx_buff_mgmt",	0444,	&rx_buff_mgmt_fops},
2385*4882a593Smuzhiyun 	{"tx_latency",	0644,		&fops_tx_latency},
2386*4882a593Smuzhiyun 	{"link_stats",	0644,		&fops_link_stats},
2387*4882a593Smuzhiyun 	{"link_stats_global",	0644,	&fops_link_stats_global},
2388*4882a593Smuzhiyun 	{"rbufcap",	0244,		&fops_rbufcap},
2389*4882a593Smuzhiyun };
2390*4882a593Smuzhiyun 
wil6210_debugfs_init_files(struct wil6210_priv * wil,struct dentry * dbg)2391*4882a593Smuzhiyun static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
2392*4882a593Smuzhiyun 				       struct dentry *dbg)
2393*4882a593Smuzhiyun {
2394*4882a593Smuzhiyun 	int i;
2395*4882a593Smuzhiyun 
2396*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(dbg_files); i++)
2397*4882a593Smuzhiyun 		debugfs_create_file(dbg_files[i].name, dbg_files[i].mode, dbg,
2398*4882a593Smuzhiyun 				    wil, dbg_files[i].fops);
2399*4882a593Smuzhiyun }
2400*4882a593Smuzhiyun 
2401*4882a593Smuzhiyun /* interrupt control blocks */
2402*4882a593Smuzhiyun static const struct {
2403*4882a593Smuzhiyun 	const char *name;
2404*4882a593Smuzhiyun 	u32 icr_off;
2405*4882a593Smuzhiyun } dbg_icr[] = {
2406*4882a593Smuzhiyun 	{"USER_ICR",		HOSTADDR(RGF_USER_USER_ICR)},
2407*4882a593Smuzhiyun 	{"DMA_EP_TX_ICR",	HOSTADDR(RGF_DMA_EP_TX_ICR)},
2408*4882a593Smuzhiyun 	{"DMA_EP_RX_ICR",	HOSTADDR(RGF_DMA_EP_RX_ICR)},
2409*4882a593Smuzhiyun 	{"DMA_EP_MISC_ICR",	HOSTADDR(RGF_DMA_EP_MISC_ICR)},
2410*4882a593Smuzhiyun };
2411*4882a593Smuzhiyun 
wil6210_debugfs_init_isr(struct wil6210_priv * wil,struct dentry * dbg)2412*4882a593Smuzhiyun static void wil6210_debugfs_init_isr(struct wil6210_priv *wil,
2413*4882a593Smuzhiyun 				     struct dentry *dbg)
2414*4882a593Smuzhiyun {
2415*4882a593Smuzhiyun 	int i;
2416*4882a593Smuzhiyun 
2417*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(dbg_icr); i++)
2418*4882a593Smuzhiyun 		wil6210_debugfs_create_ISR(wil, dbg_icr[i].name, dbg,
2419*4882a593Smuzhiyun 					   dbg_icr[i].icr_off);
2420*4882a593Smuzhiyun }
2421*4882a593Smuzhiyun 
2422*4882a593Smuzhiyun #define WIL_FIELD(name, mode, type) { __stringify(name), mode, \
2423*4882a593Smuzhiyun 	offsetof(struct wil6210_priv, name), type}
2424*4882a593Smuzhiyun 
2425*4882a593Smuzhiyun /* fields in struct wil6210_priv */
2426*4882a593Smuzhiyun static const struct dbg_off dbg_wil_off[] = {
2427*4882a593Smuzhiyun 	WIL_FIELD(status[0],	0644,	doff_ulong),
2428*4882a593Smuzhiyun 	WIL_FIELD(hw_version,	0444,	doff_x32),
2429*4882a593Smuzhiyun 	WIL_FIELD(recovery_count, 0444,	doff_u32),
2430*4882a593Smuzhiyun 	WIL_FIELD(discovery_mode, 0644,	doff_u8),
2431*4882a593Smuzhiyun 	WIL_FIELD(chip_revision, 0444,	doff_u8),
2432*4882a593Smuzhiyun 	WIL_FIELD(abft_len, 0644,		doff_u8),
2433*4882a593Smuzhiyun 	WIL_FIELD(wakeup_trigger, 0644,		doff_u8),
2434*4882a593Smuzhiyun 	WIL_FIELD(ring_idle_trsh, 0644,	doff_u32),
2435*4882a593Smuzhiyun 	WIL_FIELD(num_rx_status_rings, 0644,	doff_u8),
2436*4882a593Smuzhiyun 	WIL_FIELD(rx_status_ring_order, 0644,	doff_u32),
2437*4882a593Smuzhiyun 	WIL_FIELD(tx_status_ring_order, 0644,	doff_u32),
2438*4882a593Smuzhiyun 	WIL_FIELD(rx_buff_id_count, 0644,	doff_u32),
2439*4882a593Smuzhiyun 	WIL_FIELD(amsdu_en, 0644,	doff_u8),
2440*4882a593Smuzhiyun 	{},
2441*4882a593Smuzhiyun };
2442*4882a593Smuzhiyun 
2443*4882a593Smuzhiyun static const struct dbg_off dbg_wil_regs[] = {
2444*4882a593Smuzhiyun 	{"RGF_MAC_MTRL_COUNTER_0", 0444, HOSTADDR(RGF_MAC_MTRL_COUNTER_0),
2445*4882a593Smuzhiyun 		doff_io32},
2446*4882a593Smuzhiyun 	{"RGF_USER_USAGE_1", 0444, HOSTADDR(RGF_USER_USAGE_1), doff_io32},
2447*4882a593Smuzhiyun 	{"RGF_USER_USAGE_2", 0444, HOSTADDR(RGF_USER_USAGE_2), doff_io32},
2448*4882a593Smuzhiyun 	{},
2449*4882a593Smuzhiyun };
2450*4882a593Smuzhiyun 
2451*4882a593Smuzhiyun /* static parameters */
2452*4882a593Smuzhiyun static const struct dbg_off dbg_statics[] = {
2453*4882a593Smuzhiyun 	{"desc_index",	0644, (ulong)&dbg_txdesc_index, doff_u32},
2454*4882a593Smuzhiyun 	{"ring_index",	0644, (ulong)&dbg_ring_index, doff_u32},
2455*4882a593Smuzhiyun 	{"mem_addr",	0644, (ulong)&mem_addr, doff_u32},
2456*4882a593Smuzhiyun 	{"led_polarity", 0644, (ulong)&led_polarity, doff_u8},
2457*4882a593Smuzhiyun 	{"status_index", 0644, (ulong)&dbg_status_msg_index, doff_u32},
2458*4882a593Smuzhiyun 	{"sring_index",	0644, (ulong)&dbg_sring_index, doff_u32},
2459*4882a593Smuzhiyun 	{"drop_if_ring_full", 0644, (ulong)&drop_if_ring_full, doff_u8},
2460*4882a593Smuzhiyun 	{},
2461*4882a593Smuzhiyun };
2462*4882a593Smuzhiyun 
2463*4882a593Smuzhiyun static const int dbg_off_count = 4 * (ARRAY_SIZE(isr_off) - 1) +
2464*4882a593Smuzhiyun 				ARRAY_SIZE(dbg_wil_regs) - 1 +
2465*4882a593Smuzhiyun 				ARRAY_SIZE(pseudo_isr_off) - 1 +
2466*4882a593Smuzhiyun 				ARRAY_SIZE(lgc_itr_cnt_off) - 1 +
2467*4882a593Smuzhiyun 				ARRAY_SIZE(tx_itr_cnt_off) - 1 +
2468*4882a593Smuzhiyun 				ARRAY_SIZE(rx_itr_cnt_off) - 1;
2469*4882a593Smuzhiyun 
wil6210_debugfs_init(struct wil6210_priv * wil)2470*4882a593Smuzhiyun int wil6210_debugfs_init(struct wil6210_priv *wil)
2471*4882a593Smuzhiyun {
2472*4882a593Smuzhiyun 	struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,
2473*4882a593Smuzhiyun 			wil_to_wiphy(wil)->debugfsdir);
2474*4882a593Smuzhiyun 	if (IS_ERR_OR_NULL(dbg))
2475*4882a593Smuzhiyun 		return -ENODEV;
2476*4882a593Smuzhiyun 
2477*4882a593Smuzhiyun 	wil->dbg_data.data_arr = kcalloc(dbg_off_count,
2478*4882a593Smuzhiyun 					 sizeof(struct wil_debugfs_iomem_data),
2479*4882a593Smuzhiyun 					 GFP_KERNEL);
2480*4882a593Smuzhiyun 	if (!wil->dbg_data.data_arr) {
2481*4882a593Smuzhiyun 		debugfs_remove_recursive(dbg);
2482*4882a593Smuzhiyun 		wil->debug = NULL;
2483*4882a593Smuzhiyun 		return -ENOMEM;
2484*4882a593Smuzhiyun 	}
2485*4882a593Smuzhiyun 
2486*4882a593Smuzhiyun 	wil->dbg_data.iomem_data_count = 0;
2487*4882a593Smuzhiyun 
2488*4882a593Smuzhiyun 	wil_pmc_init(wil);
2489*4882a593Smuzhiyun 
2490*4882a593Smuzhiyun 	wil6210_debugfs_init_files(wil, dbg);
2491*4882a593Smuzhiyun 	wil6210_debugfs_init_isr(wil, dbg);
2492*4882a593Smuzhiyun 	wil6210_debugfs_init_blobs(wil, dbg);
2493*4882a593Smuzhiyun 	wil6210_debugfs_init_offset(wil, dbg, wil, dbg_wil_off);
2494*4882a593Smuzhiyun 	wil6210_debugfs_init_offset(wil, dbg, (void * __force)wil->csr,
2495*4882a593Smuzhiyun 				    dbg_wil_regs);
2496*4882a593Smuzhiyun 	wil6210_debugfs_init_offset(wil, dbg, NULL, dbg_statics);
2497*4882a593Smuzhiyun 
2498*4882a593Smuzhiyun 	wil6210_debugfs_create_pseudo_ISR(wil, dbg);
2499*4882a593Smuzhiyun 
2500*4882a593Smuzhiyun 	wil6210_debugfs_create_ITR_CNT(wil, dbg);
2501*4882a593Smuzhiyun 
2502*4882a593Smuzhiyun 	return 0;
2503*4882a593Smuzhiyun }
2504*4882a593Smuzhiyun 
wil6210_debugfs_remove(struct wil6210_priv * wil)2505*4882a593Smuzhiyun void wil6210_debugfs_remove(struct wil6210_priv *wil)
2506*4882a593Smuzhiyun {
2507*4882a593Smuzhiyun 	int i;
2508*4882a593Smuzhiyun 
2509*4882a593Smuzhiyun 	debugfs_remove_recursive(wil->debug);
2510*4882a593Smuzhiyun 	wil->debug = NULL;
2511*4882a593Smuzhiyun 
2512*4882a593Smuzhiyun 	kfree(wil->dbg_data.data_arr);
2513*4882a593Smuzhiyun 	for (i = 0; i < wil->max_assoc_sta; i++)
2514*4882a593Smuzhiyun 		kfree(wil->sta[i].tx_latency_bins);
2515*4882a593Smuzhiyun 
2516*4882a593Smuzhiyun 	/* free pmc memory without sending command to fw, as it will
2517*4882a593Smuzhiyun 	 * be reset on the way down anyway
2518*4882a593Smuzhiyun 	 */
2519*4882a593Smuzhiyun 	wil_pmc_free(wil, false);
2520*4882a593Smuzhiyun }
2521