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