1185743Ssam/*-
2185743Ssam * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3185743Ssam * All rights reserved.
4185743Ssam *
5185743Ssam * Redistribution and use in source and binary forms, with or without
6185743Ssam * modification, are permitted provided that the following conditions
7185743Ssam * are met:
8185743Ssam * 1. Redistributions of source code must retain the above copyright
9185743Ssam *    notice, this list of conditions and the following disclaimer,
10185743Ssam *    without modification.
11185743Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12185743Ssam *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13185743Ssam *    redistribution must be conditioned upon including a substantially
14185743Ssam *    similar Disclaimer requirement for further binary redistribution.
15185743Ssam *
16185743Ssam * NO WARRANTY
17185743Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18185743Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19185743Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20185743Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21185743Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22185743Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23185743Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24185743Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25185743Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26185743Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27185743Ssam * THE POSSIBILITY OF SUCH DAMAGES.
28185743Ssam *
29185743Ssam * $FreeBSD$
30185743Ssam */
31185743Ssam#include "diag.h"
32185743Ssam
33185743Ssam#include "ah.h"
34185743Ssam#include "ah_internal.h"
35185743Ssam/* XXX cheat, 5212 has a superset of the key table defs */
36185743Ssam#include "ar5212/ar5212reg.h"
37185743Ssam
38185743Ssam#include "dumpregs.h"
39185743Ssam
40185743Ssam#include <getopt.h>
41185743Ssam#include <stdlib.h>
42185743Ssam#include <string.h>
43185743Ssam#include <ctype.h>
44217680Sadrian#include <err.h>
45185743Ssam
46296154Sadrian#include "ctrl.h"
47296154Sadrian
48185743Ssamtypedef struct {
49185743Ssam	HAL_REVS revs;
50185743Ssam	u_int32_t regdata[0xffff / sizeof(u_int32_t)];
51185743Ssam#define	MAXREGS	5*1024
52185743Ssam	struct dumpreg *regs[MAXREGS];
53185743Ssam	u_int nregs;
54185743Ssam	u_int	show_names	: 1,
55185743Ssam		show_addrs	: 1;
56185743Ssam} dumpregs_t;
57185743Ssamstatic	dumpregs_t state;
58185743Ssam
59185743Ssam#undef OS_REG_READ
60185743Ssam#define	OS_REG_READ(ah, off)	state.regdata[(off) >> 2]
61185743Ssam
62185743Ssamstatic	int ath_hal_anyregs(int what);
63185743Ssamstatic	int ath_hal_setupregs(struct ath_diag *atd, int what);
64185743Ssamstatic	u_int ath_hal_setupdiagregs(const HAL_REGRANGE regs[], u_int nr);
65185743Ssamstatic	void ath_hal_dumpregs(FILE *fd, int what);
66185743Ssamstatic	void ath_hal_dumprange(FILE *fd, u_int a, u_int b);
67185743Ssamstatic	void ath_hal_dumpkeycache(FILE *fd, int nkeys);
68185743Ssamstatic	void ath_hal_dumpint(FILE *fd, int what);
69185743Ssamstatic	void ath_hal_dumpqcu(FILE *fd, int what);
70185743Ssamstatic	void ath_hal_dumpdcu(FILE *fd, int what);
71185743Ssamstatic	void ath_hal_dumpbb(FILE *fd, int what);
72185743Ssam
73185743Ssamstatic void
74185743Ssamusage(void)
75185743Ssam{
76185743Ssam	fprintf(stderr, "usage: athregs [-i interface] [-abdkmqxz]\n");
77185743Ssam	fprintf(stderr, "-a	display all registers\n");
78185743Ssam	fprintf(stderr, "-b	display baseband registers\n");
79185743Ssam	fprintf(stderr, "-d	display DCU registers\n");
80185743Ssam	fprintf(stderr, "-k	display key cache registers\n");
81185743Ssam	fprintf(stderr, "-m	display \"MAC\" registers (default)\n");
82185743Ssam	fprintf(stderr, "-q	display QCU registers\n");
83185743Ssam	fprintf(stderr, "-x	display XR registers\n");
84185743Ssam	fprintf(stderr, "-z	display interrupt registers\n");
85185743Ssam	fprintf(stderr, "\n");
86185743Ssam	fprintf(stderr, "-A	display register address\n");
87185743Ssam	fprintf(stderr, "-N	suppress display of register name\n");
88185743Ssam	exit(-1);
89185743Ssam}
90185743Ssam
91185743Ssamint
92185743Ssammain(int argc, char *argv[])
93185743Ssam{
94185743Ssam	struct ath_diag atd;
95185743Ssam	const char *ifname;
96185743Ssam	u_int32_t *data;
97185743Ssam	u_int32_t *dp, *ep;
98296154Sadrian	int what, c, i;
99296154Sadrian	struct ath_driver_req req;
100185743Ssam
101296154Sadrian	ath_driver_req_init(&req);
102296154Sadrian
103185743Ssam	ifname = getenv("ATH");
104185743Ssam	if (!ifname)
105185743Ssam		ifname = ATH_DEFAULT;
106185743Ssam
107185743Ssam	what = 0;
108185743Ssam	state.show_addrs = 0;
109185743Ssam	state.show_names = 1;
110185743Ssam	while ((c = getopt(argc, argv, "i:aAbdkmNqxz")) != -1)
111185743Ssam		switch (c) {
112185743Ssam		case 'a':
113185743Ssam			what |= DUMP_ALL;
114185743Ssam			break;
115185743Ssam		case 'A':
116185743Ssam			state.show_addrs = 1;
117185743Ssam			break;
118185743Ssam		case 'b':
119185743Ssam			what |= DUMP_BASEBAND;
120185743Ssam			break;
121185743Ssam		case 'd':
122185743Ssam			what |= DUMP_DCU;
123185743Ssam			break;
124185743Ssam		case 'k':
125185743Ssam			what |= DUMP_KEYCACHE;
126185743Ssam			break;
127185743Ssam		case 'i':
128185743Ssam			ifname = optarg;
129185743Ssam			break;
130185743Ssam		case 'm':
131185743Ssam			what |= DUMP_BASIC;
132185743Ssam			break;
133185743Ssam		case 'N':
134185743Ssam			state.show_names = 0;
135185743Ssam			break;
136185743Ssam		case 'q':
137185743Ssam			what |= DUMP_QCU;
138185743Ssam			break;
139185743Ssam		case 'x':
140185743Ssam			what |= DUMP_XR;
141185743Ssam			break;
142185743Ssam		case 'z':
143185743Ssam			what |= DUMP_INTERRUPT;
144185743Ssam			break;
145185743Ssam		default:
146185743Ssam			usage();
147185743Ssam			/*NOTREACHED*/
148185743Ssam		}
149296154Sadrian
150296154Sadrian	/* Initialise the driver interface */
151296154Sadrian	if (ath_driver_req_open(&req, ifname) < 0) {
152296154Sadrian		exit(127);
153296154Sadrian	}
154296154Sadrian
155296154Sadrian	/*
156296154Sadrian	 * Whilst we're doing the ath_diag pieces, we have to set this
157296154Sadrian	 * ourselves.
158296154Sadrian	 */
159185743Ssam	strncpy(atd.ad_name, ifname, sizeof (atd.ad_name));
160185743Ssam
161185743Ssam	argc -= optind;
162185743Ssam	argv += optind;
163185743Ssam	if (what == 0)
164185743Ssam		what = DUMP_BASIC;
165185743Ssam
166185743Ssam	atd.ad_id = HAL_DIAG_REVS;
167185743Ssam	atd.ad_out_data = (caddr_t) &state.revs;
168185743Ssam	atd.ad_out_size = sizeof(state.revs);
169296154Sadrian
170296154Sadrian	if (ath_driver_req_fetch_diag(&req, SIOCGATHDIAG, &atd) < 0)
171295390Sadrian		err(1, "%s", atd.ad_name);
172185743Ssam
173185743Ssam	if (ath_hal_setupregs(&atd, what) == 0)
174185743Ssam		errx(-1, "no registers are known for this part "
175185743Ssam		    "(devid 0x%x mac %d.%d phy %d)", state.revs.ah_devid,
176185743Ssam		    state.revs.ah_macVersion, state.revs.ah_macRev,
177185743Ssam		    state.revs.ah_phyRev);
178185743Ssam
179185743Ssam	atd.ad_out_size = ath_hal_setupdiagregs((HAL_REGRANGE *) atd.ad_in_data,
180185743Ssam		atd.ad_in_size / sizeof(HAL_REGRANGE));
181185743Ssam	atd.ad_out_data = (caddr_t) malloc(atd.ad_out_size);
182185743Ssam	if (atd.ad_out_data == NULL) {
183185743Ssam		fprintf(stderr, "Cannot malloc output buffer, size %u\n",
184185743Ssam			atd.ad_out_size);
185185743Ssam		exit(-1);
186185743Ssam	}
187185743Ssam	atd.ad_id = HAL_DIAG_REGS | ATH_DIAG_IN | ATH_DIAG_DYN;
188296154Sadrian
189296154Sadrian	if (ath_driver_req_fetch_diag(&req, SIOCGATHDIAG, &atd) < 0)
190295390Sadrian		err(1, "%s", atd.ad_name);
191185743Ssam
192185743Ssam	/*
193185743Ssam	 * Expand register data into global space that can be
194185743Ssam	 * indexed directly by register offset.
195185743Ssam	 */
196185743Ssam	dp = (u_int32_t *)atd.ad_out_data;
197185743Ssam	ep = (u_int32_t *)(atd.ad_out_data + atd.ad_out_size);
198185743Ssam	while (dp < ep) {
199269761Sadrian		u_int r = dp[0];	/* start of range */
200269761Sadrian		u_int e = dp[1];	/* end of range */
201185743Ssam		dp++;
202269761Sadrian		dp++;
203185743Ssam		/* convert offsets to indices */
204185743Ssam		r >>= 2; e >>= 2;
205185743Ssam		do {
206185743Ssam			if (dp >= ep) {
207185743Ssam				fprintf(stderr, "Warning, botched return data;"
208185743Ssam					"register at offset 0x%x not present\n",
209185743Ssam					r << 2);
210185743Ssam				break;
211185743Ssam			}
212185743Ssam			state.regdata[r++] = *dp++;
213185743Ssam		} while (r <= e);
214185743Ssam	}
215185743Ssam
216185743Ssam	if (what & DUMP_BASIC)
217185743Ssam		ath_hal_dumpregs(stdout, DUMP_BASIC);
218185743Ssam	if ((what & DUMP_INTERRUPT) && ath_hal_anyregs(DUMP_INTERRUPT)) {
219185743Ssam		if (what & DUMP_BASIC)
220185743Ssam			putchar('\n');
221185743Ssam		if (state.show_addrs)
222185743Ssam			ath_hal_dumpregs(stdout, DUMP_INTERRUPT);
223185743Ssam		else
224185743Ssam			ath_hal_dumpint(stdout, what);
225185743Ssam	}
226185743Ssam	if ((what & DUMP_QCU) && ath_hal_anyregs(DUMP_QCU)) {
227185743Ssam		if (what & (DUMP_BASIC|DUMP_INTERRUPT))
228185743Ssam			putchar('\n');
229185743Ssam		if (state.show_addrs)
230185743Ssam			ath_hal_dumpregs(stdout, DUMP_QCU);
231185743Ssam		else
232185743Ssam			ath_hal_dumpqcu(stdout, what);
233185743Ssam	}
234185743Ssam	if ((what & DUMP_DCU) && ath_hal_anyregs(DUMP_DCU)) {
235185743Ssam		if (what & (DUMP_BASIC|DUMP_INTERRUPT|DUMP_QCU))
236185743Ssam			putchar('\n');
237185743Ssam		if (state.show_addrs)
238185743Ssam			ath_hal_dumpregs(stdout, DUMP_DCU);
239185743Ssam		else
240185743Ssam			ath_hal_dumpdcu(stdout, what);
241185743Ssam	}
242185743Ssam	if (what & DUMP_KEYCACHE) {
243185743Ssam		if (state.show_addrs) {
244185743Ssam			if (what & (DUMP_BASIC|DUMP_INTERRUPT|DUMP_QCU|DUMP_DCU))
245185743Ssam				putchar('\n');
246185743Ssam			ath_hal_dumpregs(stdout, DUMP_KEYCACHE);
247185743Ssam		} else
248185743Ssam			ath_hal_dumpkeycache(stdout, 128);
249185743Ssam	}
250185743Ssam	if (what & DUMP_BASEBAND) {
251185743Ssam		if (what &~ DUMP_BASEBAND)
252185743Ssam			fprintf(stdout, "\n");
253185743Ssam		ath_hal_dumpbb(stdout, what);
254185743Ssam	}
255296154Sadrian	ath_driver_req_close(&req);
256185743Ssam	return 0;
257185743Ssam}
258185743Ssam
259185743Ssamstatic int
260185743Ssamregcompar(const void *a, const void *b)
261185743Ssam{
262185743Ssam	const struct dumpreg *ra = *(const struct dumpreg **)a;
263185743Ssam	const struct dumpreg *rb = *(const struct dumpreg **)b;
264185743Ssam	return ra->addr - rb->addr;
265185743Ssam}
266185743Ssam
267185743Ssamvoid
268185743Ssamregister_regs(struct dumpreg *chipregs, u_int nchipregs,
269185743Ssam	int def_srev_min, int def_srev_max, int def_phy_min, int def_phy_max)
270185743Ssam{
271185743Ssam	const int existing_regs = state.nregs;
272185743Ssam	int i, j;
273185743Ssam
274185743Ssam	for (i = 0; i < nchipregs; i++) {
275185743Ssam		struct dumpreg *nr = &chipregs[i];
276185743Ssam		if (nr->srevMin == 0)
277185743Ssam			nr->srevMin = def_srev_min;
278185743Ssam		if (nr->srevMax == 0)
279185743Ssam			nr->srevMax = def_srev_max;
280185743Ssam		if (nr->phyMin == 0)
281185743Ssam			nr->phyMin = def_phy_min;
282185743Ssam		if (nr->phyMax == 0)
283185743Ssam			nr->phyMax = def_phy_max;
284185743Ssam		for (j = 0; j < existing_regs; j++) {
285185743Ssam			struct dumpreg *r = state.regs[j];
286185743Ssam			/*
287185743Ssam			 * Check if we can just expand the mac+phy
288185743Ssam			 * coverage for the existing entry.
289185743Ssam			 */
290185743Ssam			if (nr->addr == r->addr &&
291185743Ssam			    (nr->name == r->name ||
292185743Ssam			     nr->name != NULL && r->name != NULL &&
293185743Ssam			     strcmp(nr->name, r->name) == 0)) {
294185743Ssam				if (nr->srevMin < r->srevMin &&
295185743Ssam				    (r->srevMin <= nr->srevMax &&
296185743Ssam				     nr->srevMax+1 <= r->srevMax)) {
297185743Ssam					r->srevMin = nr->srevMin;
298185743Ssam					goto skip;
299185743Ssam				}
300185743Ssam				if (nr->srevMax > r->srevMax &&
301185743Ssam				    (r->srevMin <= nr->srevMin &&
302185743Ssam				     nr->srevMin <= r->srevMax)) {
303185743Ssam					r->srevMax = nr->srevMax;
304185743Ssam					goto skip;
305185743Ssam				}
306185743Ssam			}
307185743Ssam			if (r->addr > nr->addr)
308185743Ssam				break;
309185743Ssam		}
310185743Ssam		/*
311185743Ssam		 * New item, add to the end, it'll be sorted below.
312185743Ssam		 */
313185743Ssam		if (state.nregs == MAXREGS)
314185743Ssam			errx(-1, "too many registers; bump MAXREGS");
315185743Ssam		state.regs[state.nregs++] = nr;
316185743Ssam	skip:
317185743Ssam		;
318185743Ssam	}
319185743Ssam	qsort(state.regs, state.nregs, sizeof(struct dumpreg *), regcompar);
320185743Ssam}
321185743Ssam
322185743Ssamvoid
323185743Ssamregister_keycache(u_int nslots,
324185743Ssam	int def_srev_min, int def_srev_max, int def_phy_min, int def_phy_max)
325185743Ssam{
326185743Ssam#define	SET(r, a) do { \
327185743Ssam	r->addr = a; r->type = DUMP_KEYCACHE; r++; \
328185743Ssam} while(0)
329185743Ssam	struct dumpreg *keyregs, *r;
330185743Ssam	int i;
331185743Ssam
332185743Ssam	keyregs = (struct dumpreg *) calloc(nslots, 8*sizeof(struct dumpreg));
333185743Ssam	if (keyregs == NULL)
334185743Ssam		errx(-1, "no space to %d keycache slots\n", nslots);
335185743Ssam	r = keyregs;
336185743Ssam	for (i = 0; i < nslots; i++) {
337185743Ssam		SET(r, AR_KEYTABLE_KEY0(i));
338185743Ssam		SET(r, AR_KEYTABLE_KEY1(i));
339185743Ssam		SET(r, AR_KEYTABLE_KEY2(i));
340185743Ssam		SET(r, AR_KEYTABLE_KEY3(i));
341185743Ssam		SET(r, AR_KEYTABLE_KEY4(i));
342185743Ssam		SET(r, AR_KEYTABLE_TYPE(i));
343185743Ssam		SET(r, AR_KEYTABLE_MAC0(i));
344185743Ssam		SET(r, AR_KEYTABLE_MAC1(i));
345185743Ssam	}
346185743Ssam	register_regs(keyregs, 8*nslots,
347185743Ssam	    def_srev_min, def_srev_max, def_phy_min, def_phy_max);
348185743Ssam#undef SET
349185743Ssam}
350185743Ssam
351185743Ssamvoid
352185743Ssamregister_range(u_int brange, u_int erange, int type,
353185743Ssam	int def_srev_min, int def_srev_max, int def_phy_min, int def_phy_max)
354185743Ssam{
355185743Ssam	struct dumpreg *bbregs, *r;
356185743Ssam	int i, nregs;
357185743Ssam
358185743Ssam	nregs = (erange - brange) / sizeof(uint32_t);
359185743Ssam	bbregs = (struct dumpreg *) calloc(nregs, sizeof(struct dumpreg));
360185743Ssam	if (bbregs == NULL)
361185743Ssam		errx(-1, "no space for %d register slots (type %d)\n",
362185743Ssam		    nregs, type);
363185743Ssam	r = bbregs;
364185743Ssam	for (i = 0; i < nregs; i++) {
365185743Ssam		r->addr = brange + (i<<2);
366185743Ssam		r->type = type;
367185743Ssam		r++;
368185743Ssam	}
369185743Ssam	register_regs(bbregs, nregs,
370185743Ssam	    def_srev_min, def_srev_max, def_phy_min, def_phy_max);
371185743Ssam}
372185743Ssam
373217680Sadrianstatic __inline int
374185743Ssammatch(const struct dumpreg *dr, const HAL_REVS *revs)
375185743Ssam{
376185743Ssam	if (!MAC_MATCH(dr, revs->ah_macVersion, revs->ah_macRev))
377185743Ssam		return 0;
378185743Ssam	if ((dr->type & DUMP_BASEBAND) && !PHY_MATCH(dr, revs->ah_phyRev))
379185743Ssam		return 0;
380185743Ssam	return 1;
381185743Ssam}
382185743Ssam
383185743Ssamstatic int
384185743Ssamath_hal_anyregs(int what)
385185743Ssam{
386185743Ssam	const HAL_REVS *revs = &state.revs;
387185743Ssam	int i;
388185743Ssam
389185743Ssam	for (i = 0; i < state.nregs; i++) {
390185743Ssam		const struct dumpreg *dr = state.regs[i];
391185743Ssam		if ((what & dr->type) && match(dr, revs))
392185743Ssam			return 1;
393185743Ssam	}
394185743Ssam	return 0;
395185743Ssam}
396185743Ssam
397185743Ssamstatic int
398185743Ssamath_hal_setupregs(struct ath_diag *atd, int what)
399185743Ssam{
400185743Ssam	const HAL_REVS *revs = &state.revs;
401185743Ssam	HAL_REGRANGE r;
402185743Ssam	size_t space = 0;
403185743Ssam	u_int8_t *cp;
404185743Ssam	int i, brun, erun;
405185743Ssam
406185743Ssam	brun = erun = -1;
407185743Ssam	for (i = 0; i < state.nregs; i++) {
408185743Ssam		const struct dumpreg *dr = state.regs[i];
409185743Ssam		if ((what & dr->type) && match(dr, revs)) {
410185743Ssam			if (erun + 4 != dr->addr) {
411185743Ssam				if (brun != -1)
412185743Ssam					space += sizeof(HAL_REGRANGE);
413185743Ssam				brun = erun = dr->addr;
414185743Ssam			} else
415185743Ssam				erun = dr->addr;
416185743Ssam		}
417185743Ssam	}
418185743Ssam	space += sizeof(HAL_REGRANGE);
419185743Ssam
420185743Ssam	atd->ad_in_data = (caddr_t) malloc(space);
421185743Ssam	if (atd->ad_in_data == NULL) {
422185743Ssam		fprintf(stderr, "Cannot malloc memory for registers!\n");
423185743Ssam		exit(-1);
424185743Ssam	}
425185743Ssam	atd->ad_in_size = space;
426185743Ssam	cp = (u_int8_t *) atd->ad_in_data;
427185743Ssam	brun = erun = -1;
428185743Ssam	for (i = 0; i < state.nregs; i++) {
429185743Ssam		const struct dumpreg *dr = state.regs[i];
430185743Ssam		if ((what & dr->type) && match(dr, revs)) {
431185743Ssam			if (erun + 4 != dr->addr) {
432185743Ssam				if (brun != -1) {
433185743Ssam					r.start = brun, r.end = erun;
434185743Ssam					memcpy(cp, &r, sizeof(r));
435185743Ssam					cp += sizeof(r);
436185743Ssam				}
437185743Ssam				brun = erun = dr->addr;
438185743Ssam			} else
439185743Ssam				erun = dr->addr;
440185743Ssam		}
441185743Ssam	}
442185743Ssam	if (brun != -1) {
443185743Ssam		r.start = brun, r.end = erun;
444185743Ssam		memcpy(cp, &r, sizeof(r));
445185743Ssam		cp += sizeof(r);
446185743Ssam	}
447185743Ssam	return space / sizeof(uint32_t);
448185743Ssam}
449185743Ssam
450185743Ssamstatic void
451185743Ssamath_hal_dumpregs(FILE *fd, int what)
452185743Ssam{
453185743Ssam	const HAL_REVS *revs = &state.revs;
454185743Ssam	const char *sep = "";
455185743Ssam	int i, count, itemsperline;
456185743Ssam
457185743Ssam	count = 0;
458185743Ssam	itemsperline = 4;
459185743Ssam	if (state.show_names && state.show_addrs)
460185743Ssam		itemsperline--;
461185743Ssam	for (i = 0; i < state.nregs; i++) {
462185743Ssam		const struct dumpreg *dr = state.regs[i];
463185743Ssam		if ((what & dr->type) && match(dr, revs)) {
464185743Ssam			if (state.show_names && dr->name != NULL) {
465185743Ssam				fprintf(fd, "%s%-8s", sep, dr->name);
466185743Ssam				if (state.show_addrs)
467185743Ssam					fprintf(fd, " [%04x]", dr->addr);
468185743Ssam			} else
469185743Ssam				fprintf(fd, "%s%04x", sep, dr->addr);
470185743Ssam			fprintf(fd, " %08x", OS_REG_READ(ah, dr->addr));
471185743Ssam			sep = " ";
472185743Ssam			if ((++count % itemsperline) == 0)
473185743Ssam				sep = "\n";
474185743Ssam		}
475185743Ssam	}
476185743Ssam	if (count)
477185743Ssam		fprintf(fd, "\n");
478185743Ssam}
479185743Ssam
480185743Ssamstatic void
481185743Ssamath_hal_dumprange(FILE *fd, u_int a, u_int b)
482185743Ssam{
483185743Ssam	u_int r;
484185743Ssam
485185743Ssam	for (r = a; r+16 <= b; r += 5*4)
486185743Ssam		fprintf(fd,
487185743Ssam			"%04x %08x  %04x %08x  %04x %08x  %04x %08x  %04x %08x\n"
488185743Ssam			, r, OS_REG_READ(ah, r)
489185743Ssam			, r+4, OS_REG_READ(ah, r+4)
490185743Ssam			, r+8, OS_REG_READ(ah, r+8)
491185743Ssam			, r+12, OS_REG_READ(ah, r+12)
492185743Ssam			, r+16, OS_REG_READ(ah, r+16)
493185743Ssam		);
494185743Ssam	switch (b-r) {
495185743Ssam	case 16:
496185743Ssam		fprintf(fd
497185743Ssam			, "%04x %08x  %04x %08x  %04x %08x  %04x %08x\n"
498185743Ssam			, r, OS_REG_READ(ah, r)
499185743Ssam			, r+4, OS_REG_READ(ah, r+4)
500185743Ssam			, r+8, OS_REG_READ(ah, r+8)
501185743Ssam			, r+12, OS_REG_READ(ah, r+12)
502185743Ssam		);
503185743Ssam		break;
504185743Ssam	case 12:
505185743Ssam		fprintf(fd, "%04x %08x  %04x %08x  %04x %08x\n"
506185743Ssam			, r, OS_REG_READ(ah, r)
507185743Ssam			, r+4, OS_REG_READ(ah, r+4)
508185743Ssam			, r+8, OS_REG_READ(ah, r+8)
509185743Ssam		);
510185743Ssam		break;
511185743Ssam	case 8:
512185743Ssam		fprintf(fd, "%04x %08x  %04x %08x\n"
513185743Ssam			, r, OS_REG_READ(ah, r)
514185743Ssam			, r+4, OS_REG_READ(ah, r+4)
515185743Ssam		);
516185743Ssam		break;
517185743Ssam	case 4:
518185743Ssam		fprintf(fd, "%04x %08x\n"
519185743Ssam			, r, OS_REG_READ(ah, r)
520185743Ssam		);
521185743Ssam		break;
522185743Ssam	}
523185743Ssam}
524185743Ssam
525185743Ssamstatic void
526185743Ssamath_hal_dumpint(FILE *fd, int what)
527185743Ssam{
528185743Ssam	int i;
529185743Ssam
530185743Ssam	/* Interrupt registers */
531185743Ssam	fprintf(fd, "IMR: %08x S0 %08x S1 %08x S2 %08x S3 %08x S4 %08x\n"
532185743Ssam		, OS_REG_READ(ah, AR_IMR)
533185743Ssam		, OS_REG_READ(ah, AR_IMR_S0)
534185743Ssam		, OS_REG_READ(ah, AR_IMR_S1)
535185743Ssam		, OS_REG_READ(ah, AR_IMR_S2)
536185743Ssam		, OS_REG_READ(ah, AR_IMR_S3)
537185743Ssam		, OS_REG_READ(ah, AR_IMR_S4)
538185743Ssam	);
539185743Ssam	fprintf(fd, "ISR: %08x S0 %08x S1 %08x S2 %08x S3 %08x S4 %08x\n"
540185743Ssam		, OS_REG_READ(ah, AR_ISR)
541185743Ssam		, OS_REG_READ(ah, AR_ISR_S0)
542185743Ssam		, OS_REG_READ(ah, AR_ISR_S1)
543185743Ssam		, OS_REG_READ(ah, AR_ISR_S2)
544185743Ssam		, OS_REG_READ(ah, AR_ISR_S3)
545185743Ssam		, OS_REG_READ(ah, AR_ISR_S4)
546185743Ssam	);
547185743Ssam}
548185743Ssam
549185743Ssamstatic void
550185743Ssamath_hal_dumpqcu(FILE *fd, int what)
551185743Ssam{
552185743Ssam	int i;
553185743Ssam
554185743Ssam	/* QCU registers */
555185743Ssam	fprintf(fd, "%-8s %08x  %-8s %08x  %-8s %08x\n"
556185743Ssam		, "Q_TXE", OS_REG_READ(ah, AR_Q_TXE)
557185743Ssam		, "Q_TXD", OS_REG_READ(ah, AR_Q_TXD)
558185743Ssam		, "Q_RDYTIMSHD", OS_REG_READ(ah, AR_Q_RDYTIMESHDN)
559185743Ssam	);
560185743Ssam	fprintf(fd, "Q_ONESHOTARM_SC %08x  Q_ONESHOTARM_CC %08x\n"
561185743Ssam		, OS_REG_READ(ah, AR_Q_ONESHOTARM_SC)
562185743Ssam		, OS_REG_READ(ah, AR_Q_ONESHOTARM_CC)
563185743Ssam	);
564185743Ssam	for (i = 0; i < 10; i++)
565185743Ssam		fprintf(fd, "Q[%u] TXDP %08x CBR %08x RDYT %08x MISC %08x STS %08x\n"
566185743Ssam			, i
567185743Ssam			, OS_REG_READ(ah, AR_QTXDP(i))
568185743Ssam			, OS_REG_READ(ah, AR_QCBRCFG(i))
569185743Ssam			, OS_REG_READ(ah, AR_QRDYTIMECFG(i))
570185743Ssam			, OS_REG_READ(ah, AR_QMISC(i))
571185743Ssam			, OS_REG_READ(ah, AR_QSTS(i))
572185743Ssam		);
573185743Ssam}
574185743Ssam
575185743Ssamstatic void
576185743Ssamath_hal_dumpdcu(FILE *fd, int what)
577185743Ssam{
578185743Ssam	int i;
579185743Ssam
580185743Ssam	/* DCU registers */
581185743Ssam	for (i = 0; i < 10; i++)
582185743Ssam		fprintf(fd, "D[%u] MASK %08x IFS %08x RTRY %08x CHNT %08x MISC %06x\n"
583185743Ssam			, i
584185743Ssam			, OS_REG_READ(ah, AR_DQCUMASK(i))
585185743Ssam			, OS_REG_READ(ah, AR_DLCL_IFS(i))
586185743Ssam			, OS_REG_READ(ah, AR_DRETRY_LIMIT(i))
587185743Ssam			, OS_REG_READ(ah, AR_DCHNTIME(i))
588185743Ssam			, OS_REG_READ(ah, AR_DMISC(i))
589185743Ssam		);
590185743Ssam}
591185743Ssam
592185743Ssamstatic void
593185743Ssamath_hal_dumpbb(FILE *fd, int what)
594185743Ssam{
595185743Ssam	const HAL_REVS *revs = &state.revs;
596185743Ssam	int i, brun, erun;
597185743Ssam
598185743Ssam	brun = erun = 0;
599185743Ssam	for (i = 0; i < state.nregs; i++) {
600185743Ssam		const struct dumpreg *dr = state.regs[i];
601185743Ssam		if (!match(dr, revs))
602185743Ssam			continue;
603185743Ssam		if (dr->type & DUMP_BASEBAND) {
604185743Ssam			if (brun == 0) {
605185743Ssam				brun = erun = dr->addr;
606185743Ssam			} else if (dr->addr == erun + sizeof(uint32_t)) {
607185743Ssam				erun = dr->addr;
608185743Ssam			} else {
609185743Ssam				ath_hal_dumprange(fd, brun, erun);
610185743Ssam				brun = erun = dr->addr;
611185743Ssam			}
612185743Ssam		} else {
613185743Ssam			if (brun != 0)
614185743Ssam				ath_hal_dumprange(fd, brun, erun);
615185743Ssam			brun = erun = 0;
616185743Ssam		}
617185743Ssam	}
618185743Ssam	if (brun != 0)
619185743Ssam		ath_hal_dumprange(fd, brun, erun);
620185743Ssam}
621185743Ssam
622185743Ssamstatic u_int
623185743Ssamath_hal_setupdiagregs(const HAL_REGRANGE regs[], u_int nr)
624185743Ssam{
625185743Ssam	u_int space;
626185743Ssam	int i;
627185743Ssam
628185743Ssam	space = 0;
629185743Ssam	for (i = 0; i < nr; i++) {
630269761Sadrian		u_int n = sizeof(HAL_REGRANGE) + sizeof(u_int32_t);	/* reg range + first */
631185743Ssam		if (regs[i].end) {
632185743Ssam			if (regs[i].end < regs[i].start) {
633185743Ssam				fprintf(stderr, "%s: bad register range, "
634185743Ssam					"end 0x%x < start 0x%x\n",
635185743Ssam					__func__, regs[i].end, regs[i].end);
636185743Ssam				exit(-1);
637185743Ssam			}
638185743Ssam			n += regs[i].end - regs[i].start;
639185743Ssam		}
640185743Ssam		space += n;
641185743Ssam	}
642185743Ssam	return space;
643185743Ssam}
644185743Ssam
645185743Ssam/*
646185743Ssam * Format an Ethernet MAC for printing.
647185743Ssam */
648185743Ssamstatic const char*
649185743Ssamether_sprintf(const u_int8_t *mac)
650185743Ssam{
651185743Ssam	static char etherbuf[18];
652185743Ssam	snprintf(etherbuf, sizeof(etherbuf), "%02x:%02x:%02x:%02x:%02x:%02x",
653185743Ssam		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
654185743Ssam	return etherbuf;
655185743Ssam}
656185743Ssam
657185743Ssam#ifndef isclr
658185743Ssam#define	setbit(a,i)	((a)[(i)/NBBY] |= 1<<((i)%NBBY))
659185743Ssam#define	clrbit(a,i)	((a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
660185743Ssam#define	isset(a,i)	((a)[(i)/NBBY] & (1<<((i)%NBBY)))
661185743Ssam#define	isclr(a,i)	(((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
662185743Ssam#endif
663185743Ssam
664185743Ssamstatic void
665185743Ssamath_hal_dumpkeycache(FILE *fd, int nkeys)
666185743Ssam{
667185743Ssam	static const char *keytypenames[] = {
668185743Ssam		"WEP-40", 	/* AR_KEYTABLE_TYPE_40 */
669185743Ssam		"WEP-104",	/* AR_KEYTABLE_TYPE_104 */
670185743Ssam		"#2",
671185743Ssam		"WEP-128",	/* AR_KEYTABLE_TYPE_128 */
672185743Ssam		"TKIP",		/* AR_KEYTABLE_TYPE_TKIP */
673185743Ssam		"AES-OCB",	/* AR_KEYTABLE_TYPE_AES */
674185743Ssam		"AES-CCM",	/* AR_KEYTABLE_TYPE_CCM */
675185743Ssam		"CLR",		/* AR_KEYTABLE_TYPE_CLR */
676185743Ssam	};
677185743Ssam	int micEnabled = SREV(state.revs.ah_macVersion, state.revs.ah_macRev) < SREV(4,8) ? 0 :
678185743Ssam	       OS_REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_CRPT_MIC_ENABLE;
679185743Ssam	u_int8_t mac[IEEE80211_ADDR_LEN];
680185743Ssam	u_int8_t ismic[128/NBBY];
681185743Ssam	int entry;
682185743Ssam	int first = 1;
683185743Ssam
684185743Ssam	memset(ismic, 0, sizeof(ismic));
685185743Ssam	for (entry = 0; entry < nkeys; entry++) {
686185743Ssam		u_int32_t macLo, macHi, type;
687185743Ssam		u_int32_t key0, key1, key2, key3, key4;
688185743Ssam
689185743Ssam		macHi = OS_REG_READ(ah, AR_KEYTABLE_MAC1(entry));
690185743Ssam		if ((macHi & AR_KEYTABLE_VALID) == 0 && isclr(ismic, entry))
691185743Ssam			continue;
692185743Ssam		macLo = OS_REG_READ(ah, AR_KEYTABLE_MAC0(entry));
693185743Ssam		macHi <<= 1;
694185743Ssam		if (macLo & (1<<31))
695185743Ssam			macHi |= 1;
696185743Ssam		macLo <<= 1;
697185743Ssam		mac[4] = macHi & 0xff;
698185743Ssam		mac[5] = macHi >> 8;
699185743Ssam		mac[0] = macLo & 0xff;
700185743Ssam		mac[1] = macLo >> 8;
701185743Ssam		mac[2] = macLo >> 16;
702185743Ssam		mac[3] = macLo >> 24;
703185743Ssam		type = OS_REG_READ(ah, AR_KEYTABLE_TYPE(entry));
704185743Ssam		if ((type & 7) == AR_KEYTABLE_TYPE_TKIP && micEnabled)
705185743Ssam			setbit(ismic, entry+64);
706185743Ssam		key0 = OS_REG_READ(ah, AR_KEYTABLE_KEY0(entry));
707185743Ssam		key1 = OS_REG_READ(ah, AR_KEYTABLE_KEY1(entry));
708185743Ssam		key2 = OS_REG_READ(ah, AR_KEYTABLE_KEY2(entry));
709185743Ssam		key3 = OS_REG_READ(ah, AR_KEYTABLE_KEY3(entry));
710185743Ssam		key4 = OS_REG_READ(ah, AR_KEYTABLE_KEY4(entry));
711185743Ssam		if (first) {
712185743Ssam			fprintf(fd, "\n");
713185743Ssam			first = 0;
714185743Ssam		}
715185743Ssam		fprintf(fd, "KEY[%03u] MAC %s %-7s %02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x\n"
716185743Ssam			, entry
717185743Ssam			, ether_sprintf(mac)
718185743Ssam			, isset(ismic, entry) ? "MIC" : keytypenames[type & 7]
719185743Ssam			, (key0 >>  0) & 0xff
720185743Ssam			, (key0 >>  8) & 0xff
721185743Ssam			, (key0 >> 16) & 0xff
722185743Ssam			, (key0 >> 24) & 0xff
723185743Ssam			, (key1 >>  0) & 0xff
724185743Ssam			, (key1 >>  8) & 0xff
725185743Ssam			, (key2 >>  0) & 0xff
726185743Ssam			, (key2 >>  8) & 0xff
727185743Ssam			, (key2 >> 16) & 0xff
728185743Ssam			, (key2 >> 24) & 0xff
729185743Ssam			, (key3 >>  0) & 0xff
730185743Ssam			, (key3 >>  8) & 0xff
731185743Ssam			, (key4 >>  0) & 0xff
732185743Ssam			, (key4 >>  8) & 0xff
733185743Ssam			, (key4 >> 16) & 0xff
734185743Ssam			, (key4 >> 24) & 0xff
735185743Ssam		);
736185743Ssam	}
737185743Ssam}
738