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