1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer,
12 *    without modification.
13 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15 *    redistribution must be conditioned upon including a substantially
16 *    similar Disclaimer requirement for further binary redistribution.
17 *
18 * NO WARRANTY
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29 * THE POSSIBILITY OF SUCH DAMAGES.
30 */
31#include "opt_ah.h"
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/kernel.h>
36#include <sys/module.h>
37#include <sys/sysctl.h>
38#include <sys/bus.h>
39#include <sys/malloc.h>
40#include <sys/proc.h>
41#include <sys/pcpu.h>
42#include <sys/lock.h>
43#include <sys/mutex.h>
44#include <sys/conf.h>
45
46#include <machine/stdarg.h>
47
48#include <net/ethernet.h>		/* XXX for ether_sprintf */
49
50#include <dev/ath/ath_hal/ah.h>
51#include <dev/ath/ath_hal/ah_debug.h>
52
53/*
54 * WiSoC boards overload the bus tag with information about the
55 * board layout.  We must extract the bus space tag from that
56 * indirect structure.  For everyone else the tag is passed in
57 * directly.
58 * XXX cache indirect ref privately
59 */
60#ifdef AH_SUPPORT_AR5312
61#define	BUSTAG(ah) \
62	((bus_space_tag_t) ((struct ar531x_config *)((ah)->ah_st))->tag)
63#else
64#define	BUSTAG(ah)	((ah)->ah_st)
65#endif
66
67/*
68 * This lock is used to seralise register access for chips which have
69 * problems w/ SMP CPUs issuing concurrent PCI transactions.
70 *
71 * XXX This is a global lock for now; it should be pushed to
72 * a per-device lock in some platform-independent fashion.
73 */
74struct mtx ah_regser_mtx;
75MTX_SYSINIT(ah_regser, &ah_regser_mtx, "Atheros register access mutex",
76    MTX_SPIN);
77
78extern	void ath_hal_printf(struct ath_hal *, const char*, ...)
79		__printflike(2,3);
80extern	void ath_hal_vprintf(struct ath_hal *, const char*, __va_list)
81		__printflike(2, 0);
82extern	const char* ath_hal_ether_sprintf(const u_int8_t *mac);
83extern	void *ath_hal_malloc(size_t);
84extern	void ath_hal_free(void *);
85#ifdef AH_ASSERT
86extern	void ath_hal_assert_failed(const char* filename,
87		int lineno, const char* msg);
88#endif
89#ifdef AH_DEBUG
90extern	void DO_HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...);
91#endif /* AH_DEBUG */
92
93/* NB: put this here instead of the driver to avoid circular references */
94SYSCTL_NODE(_hw, OID_AUTO, ath, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
95    "Atheros driver parameters");
96static SYSCTL_NODE(_hw_ath, OID_AUTO, hal, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
97    "Atheros HAL parameters");
98
99#ifdef AH_DEBUG
100int ath_hal_debug = 0;
101SYSCTL_INT(_hw_ath_hal, OID_AUTO, debug, CTLFLAG_RWTUN, &ath_hal_debug,
102    0, "Atheros HAL debugging printfs");
103#endif /* AH_DEBUG */
104
105static MALLOC_DEFINE(M_ATH_HAL, "ath_hal", "ath hal data");
106
107void*
108ath_hal_malloc(size_t size)
109{
110	return malloc(size, M_ATH_HAL, M_NOWAIT | M_ZERO);
111}
112
113void
114ath_hal_free(void* p)
115{
116	free(p, M_ATH_HAL);
117}
118
119void
120ath_hal_vprintf(struct ath_hal *ah, const char* fmt, va_list ap)
121{
122	vprintf(fmt, ap);
123}
124
125void
126ath_hal_printf(struct ath_hal *ah, const char* fmt, ...)
127{
128	va_list ap;
129	va_start(ap, fmt);
130	ath_hal_vprintf(ah, fmt, ap);
131	va_end(ap);
132}
133
134const char*
135ath_hal_ether_sprintf(const u_int8_t *mac)
136{
137	return ether_sprintf(mac);
138}
139
140#ifdef AH_DEBUG
141
142/*
143 * XXX This is highly relevant only for the AR5416 and later
144 * PCI/PCIe NICs.  It'll need adjustment for other hardware
145 * variations.
146 */
147static int
148ath_hal_reg_whilst_asleep(struct ath_hal *ah, uint32_t reg)
149{
150
151	if (reg >= 0x4000 && reg < 0x5000)
152		return (1);
153	if (reg >= 0x6000 && reg < 0x7000)
154		return (1);
155	if (reg >= 0x7000 && reg < 0x8000)
156		return (1);
157	return (0);
158}
159
160void
161DO_HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...)
162{
163	if ((mask == HAL_DEBUG_UNMASKABLE) ||
164	    (ah != NULL && ah->ah_config.ah_debug & mask) ||
165	    (ath_hal_debug & mask)) {
166		__va_list ap;
167		va_start(ap, fmt);
168		ath_hal_vprintf(ah, fmt, ap);
169		va_end(ap);
170	}
171}
172#undef	HAL_DEBUG_UNMASKABLE
173#endif /* AH_DEBUG */
174
175#ifdef AH_DEBUG_ALQ
176/*
177 * ALQ register tracing support.
178 *
179 * Setting hw.ath.hal.alq=1 enables tracing of all register reads and
180 * writes to the file /tmp/ath_hal.log.  The file format is a simple
181 * fixed-size array of records.  When done logging set hw.ath.hal.alq=0
182 * and then decode the file with the arcode program (that is part of the
183 * HAL).  If you start+stop tracing the data will be appended to an
184 * existing file.
185 *
186 * NB: doesn't handle multiple devices properly; only one DEVICE record
187 *     is emitted and the different devices are not identified.
188 */
189#include <sys/alq.h>
190#include <sys/pcpu.h>
191#include <dev/ath/ath_hal/ah_decode.h>
192
193static	struct alq *ath_hal_alq;
194static	int ath_hal_alq_emitdev;	/* need to emit DEVICE record */
195static	u_int ath_hal_alq_lost;		/* count of lost records */
196static	char ath_hal_logfile[MAXPATHLEN] = "/tmp/ath_hal.log";
197
198SYSCTL_STRING(_hw_ath_hal, OID_AUTO, alq_logfile, CTLFLAG_RW,
199    &ath_hal_logfile, sizeof(kernelname), "Name of ALQ logfile");
200
201static	u_int ath_hal_alq_qsize = 64*1024;
202
203static int
204ath_hal_setlogging(int enable)
205{
206	int error;
207
208	if (enable) {
209		error = alq_open(&ath_hal_alq, ath_hal_logfile,
210			curthread->td_ucred, ALQ_DEFAULT_CMODE,
211			sizeof (struct athregrec), ath_hal_alq_qsize);
212		ath_hal_alq_lost = 0;
213		ath_hal_alq_emitdev = 1;
214		printf("ath_hal: logging to %s enabled\n",
215			ath_hal_logfile);
216	} else {
217		if (ath_hal_alq)
218			alq_close(ath_hal_alq);
219		ath_hal_alq = NULL;
220		printf("ath_hal: logging disabled\n");
221		error = 0;
222	}
223	return (error);
224}
225
226static int
227sysctl_hw_ath_hal_log(SYSCTL_HANDLER_ARGS)
228{
229	int error, enable;
230
231	enable = (ath_hal_alq != NULL);
232        error = sysctl_handle_int(oidp, &enable, 0, req);
233        if (error || !req->newptr)
234                return (error);
235	else
236		return (ath_hal_setlogging(enable));
237}
238SYSCTL_PROC(_hw_ath_hal, OID_AUTO, alq,
239    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
240    0, 0, sysctl_hw_ath_hal_log, "I",
241    "Enable HAL register logging");
242SYSCTL_INT(_hw_ath_hal, OID_AUTO, alq_size, CTLFLAG_RW,
243	&ath_hal_alq_qsize, 0, "In-memory log size (#records)");
244SYSCTL_INT(_hw_ath_hal, OID_AUTO, alq_lost, CTLFLAG_RW,
245	&ath_hal_alq_lost, 0, "Register operations not logged");
246
247static struct ale *
248ath_hal_alq_get(struct ath_hal *ah)
249{
250	struct ale *ale;
251
252	if (ath_hal_alq_emitdev) {
253		ale = alq_get(ath_hal_alq, ALQ_NOWAIT);
254		if (ale) {
255			struct athregrec *r =
256				(struct athregrec *) ale->ae_data;
257			r->op = OP_DEVICE;
258			r->reg = 0;
259			r->val = ah->ah_devid;
260			alq_post(ath_hal_alq, ale);
261			ath_hal_alq_emitdev = 0;
262		} else
263			ath_hal_alq_lost++;
264	}
265	ale = alq_get(ath_hal_alq, ALQ_NOWAIT);
266	if (!ale)
267		ath_hal_alq_lost++;
268	return ale;
269}
270
271void
272ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val)
273{
274	bus_space_tag_t tag = BUSTAG(ah);
275	bus_space_handle_t h = ah->ah_sh;
276
277#ifdef	AH_DEBUG
278	/* Debug - complain if we haven't fully waken things up */
279	if (! ath_hal_reg_whilst_asleep(ah, reg) &&
280	    ah->ah_powerMode != HAL_PM_AWAKE) {
281		ath_hal_printf(ah, "%s: reg=0x%08x, val=0x%08x, pm=%d\n",
282		    __func__, reg, val, ah->ah_powerMode);
283	}
284#endif
285
286	if (ath_hal_alq) {
287		struct ale *ale = ath_hal_alq_get(ah);
288		if (ale) {
289			struct athregrec *r = (struct athregrec *) ale->ae_data;
290			r->threadid = curthread->td_tid;
291			r->op = OP_WRITE;
292			r->reg = reg;
293			r->val = val;
294			alq_post(ath_hal_alq, ale);
295		}
296	}
297	if (ah->ah_config.ah_serialise_reg_war)
298		mtx_lock_spin(&ah_regser_mtx);
299	bus_space_write_4(tag, h, reg, val);
300	OS_BUS_BARRIER_REG(ah, reg, OS_BUS_BARRIER_WRITE);
301	if (ah->ah_config.ah_serialise_reg_war)
302		mtx_unlock_spin(&ah_regser_mtx);
303}
304
305u_int32_t
306ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg)
307{
308	bus_space_tag_t tag = BUSTAG(ah);
309	bus_space_handle_t h = ah->ah_sh;
310	u_int32_t val;
311
312#ifdef	AH_DEBUG
313	/* Debug - complain if we haven't fully waken things up */
314	if (! ath_hal_reg_whilst_asleep(ah, reg) &&
315	    ah->ah_powerMode != HAL_PM_AWAKE) {
316		ath_hal_printf(ah, "%s: reg=0x%08x, pm=%d\n",
317		    __func__, reg, ah->ah_powerMode);
318	}
319#endif
320
321	if (ah->ah_config.ah_serialise_reg_war)
322		mtx_lock_spin(&ah_regser_mtx);
323	OS_BUS_BARRIER_REG(ah, reg, OS_BUS_BARRIER_READ);
324	val = bus_space_read_4(tag, h, reg);
325	if (ah->ah_config.ah_serialise_reg_war)
326		mtx_unlock_spin(&ah_regser_mtx);
327	if (ath_hal_alq) {
328		struct ale *ale = ath_hal_alq_get(ah);
329		if (ale) {
330			struct athregrec *r = (struct athregrec *) ale->ae_data;
331			r->threadid = curthread->td_tid;
332			r->op = OP_READ;
333			r->reg = reg;
334			r->val = val;
335			alq_post(ath_hal_alq, ale);
336		}
337	}
338	return val;
339}
340
341void
342OS_MARK(struct ath_hal *ah, u_int id, u_int32_t v)
343{
344	if (ath_hal_alq) {
345		struct ale *ale = ath_hal_alq_get(ah);
346		if (ale) {
347			struct athregrec *r = (struct athregrec *) ale->ae_data;
348			r->threadid = curthread->td_tid;
349			r->op = OP_MARK;
350			r->reg = id;
351			r->val = v;
352			alq_post(ath_hal_alq, ale);
353		}
354	}
355}
356#else /* AH_DEBUG_ALQ */
357
358/*
359 * Memory-mapped device register read/write.  These are here
360 * as routines when debugging support is enabled and/or when
361 * explicitly configured to use function calls.  The latter is
362 * for architectures that might need to do something before
363 * referencing memory (e.g. remap an i/o window).
364 *
365 * NB: see the comments in ah_osdep.h about byte-swapping register
366 *     reads and writes to understand what's going on below.
367 */
368
369void
370ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val)
371{
372	bus_space_tag_t tag = BUSTAG(ah);
373	bus_space_handle_t h = ah->ah_sh;
374
375#ifdef	AH_DEBUG
376	/* Debug - complain if we haven't fully waken things up */
377	if (! ath_hal_reg_whilst_asleep(ah, reg) &&
378	    ah->ah_powerMode != HAL_PM_AWAKE) {
379		ath_hal_printf(ah, "%s: reg=0x%08x, val=0x%08x, pm=%d\n",
380		    __func__, reg, val, ah->ah_powerMode);
381	}
382#endif
383
384	if (ah->ah_config.ah_serialise_reg_war)
385		mtx_lock_spin(&ah_regser_mtx);
386	bus_space_write_4(tag, h, reg, val);
387	OS_BUS_BARRIER_REG(ah, reg, OS_BUS_BARRIER_WRITE);
388	if (ah->ah_config.ah_serialise_reg_war)
389		mtx_unlock_spin(&ah_regser_mtx);
390}
391
392u_int32_t
393ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg)
394{
395	bus_space_tag_t tag = BUSTAG(ah);
396	bus_space_handle_t h = ah->ah_sh;
397	u_int32_t val;
398
399#ifdef	AH_DEBUG
400	/* Debug - complain if we haven't fully waken things up */
401	if (! ath_hal_reg_whilst_asleep(ah, reg) &&
402	    ah->ah_powerMode != HAL_PM_AWAKE) {
403		ath_hal_printf(ah, "%s: reg=0x%08x, pm=%d\n",
404		    __func__, reg, ah->ah_powerMode);
405	}
406#endif
407
408	if (ah->ah_config.ah_serialise_reg_war)
409		mtx_lock_spin(&ah_regser_mtx);
410	OS_BUS_BARRIER_REG(ah, reg, OS_BUS_BARRIER_READ);
411	val = bus_space_read_4(tag, h, reg);
412	if (ah->ah_config.ah_serialise_reg_war)
413		mtx_unlock_spin(&ah_regser_mtx);
414	return val;
415}
416#endif /* AH_DEBUG_ALQ */
417
418#ifdef AH_ASSERT
419void
420ath_hal_assert_failed(const char* filename, int lineno, const char *msg)
421{
422	printf("Atheros HAL assertion failure: %s: line %u: %s\n",
423		filename, lineno, msg);
424	panic("ath_hal_assert");
425}
426#endif /* AH_ASSERT */
427
428static int
429ath_hal_modevent(module_t mod __unused, int type, void *data __unused)
430{
431	int error = 0;
432
433	switch (type) {
434	case MOD_LOAD:
435		if (bootverbose)
436			printf("[ath_hal] loaded\n");
437		break;
438
439	case MOD_UNLOAD:
440		if (bootverbose)
441			printf("[ath_hal] unloaded\n");
442		break;
443
444	case MOD_SHUTDOWN:
445		break;
446
447	default:
448		error = EOPNOTSUPP;
449		break;
450	}
451	return (error);
452}
453
454DEV_MODULE(ath_hal, ath_hal_modevent, NULL);
455MODULE_VERSION(ath_hal, 1);
456#if	defined(AH_DEBUG_ALQ)
457MODULE_DEPEND(ath_hal, alq, 1, 1, 1);
458#endif
459