Commit | Line | Data |
---|---|---|
572ff6f6 MD |
1 | /*- |
2 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting | |
3 | * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd | |
4 | * All rights reserved. | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer, | |
11 | * without modification. | |
12 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer | |
13 | * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any | |
14 | * redistribution must be conditioned upon including a substantially | |
15 | * similar Disclaimer requirement for further binary redistribution. | |
16 | * | |
17 | * NO WARRANTY | |
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY | |
21 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | |
22 | * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, | |
23 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | |
26 | * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
28 | * THE POSSIBILITY OF SUCH DAMAGES. | |
29 | */ | |
30 | ||
31 | #include <sys/cdefs.h> | |
572ff6f6 MD |
32 | |
33 | /* | |
34 | * AHB bus front-end for the Atheros Wireless LAN controller driver. | |
35 | */ | |
36 | ||
37 | #include "opt_ath.h" | |
38 | ||
39 | #include <sys/param.h> | |
40 | #include <sys/systm.h> | |
41 | #include <sys/malloc.h> | |
42 | #include <sys/module.h> | |
43 | #include <sys/kernel.h> | |
44 | #include <sys/lock.h> | |
45 | #include <sys/mutex.h> | |
46 | #include <sys/errno.h> | |
47 | ||
572ff6f6 MD |
48 | #include <sys/bus.h> |
49 | #include <sys/rman.h> | |
50 | ||
51 | #include <sys/socket.h> | |
52 | ||
53 | #include <net/if.h> | |
54 | #include <net/if_media.h> | |
55 | #include <net/if_arp.h> | |
56 | #include <net/ethernet.h> | |
57 | ||
5cd80a8c | 58 | #include <netproto/802_11/ieee80211_var.h> |
572ff6f6 | 59 | |
5cd80a8c | 60 | #include <dev/netif/ath/ath/if_athvar.h> |
572ff6f6 | 61 | |
3133c5e3 MD |
62 | #define MIPS_KSEG1_START ((intptr_t)(int32_t)0xa0000000) |
63 | #define MIPS_PHYS_TO_KSEG1(x) ((uintptr_t)(x) | MIPS_KSEG1_START) | |
572ff6f6 | 64 | |
3133c5e3 MD |
65 | #include <dev/netif/ath/ath_hal/ar71xx/ar71xxreg.h> |
66 | #include <dev/netif/ath/ath_hal/ar91xx/ar91xxreg.h> | |
67 | #include <dev/netif/ath/ath_hal/ar71xx/ar71xx_cpudef.h> | |
5cd80a8c | 68 | |
572ff6f6 MD |
69 | /* |
70 | * bus glue. | |
71 | */ | |
72 | ||
73 | /* number of 16 bit words */ | |
74 | #define ATH_EEPROM_DATA_SIZE 2048 | |
75 | ||
76 | struct ath_ahb_softc { | |
77 | struct ath_softc sc_sc; | |
78 | struct resource *sc_sr; /* memory resource */ | |
79 | struct resource *sc_irq; /* irq resource */ | |
80 | struct resource *sc_eeprom; /* eeprom location */ | |
81 | void *sc_ih; /* interrupt handler */ | |
82 | }; | |
83 | ||
84 | #define VENDOR_ATHEROS 0x168c | |
85 | #define AR9130_DEVID 0x000b | |
86 | ||
87 | static int | |
88 | ath_ahb_probe(device_t dev) | |
89 | { | |
90 | int vendor_id, device_id; | |
91 | const char* devname; | |
92 | ||
93 | /* | |
94 | * Check if a device/vendor ID is provided in hints. | |
95 | */ | |
96 | if (resource_int_value(device_get_name(dev), device_get_unit(dev), | |
97 | "vendor_id", &vendor_id) != 0) { | |
98 | vendor_id = VENDOR_ATHEROS; | |
99 | } | |
100 | ||
101 | if (resource_int_value(device_get_name(dev), device_get_unit(dev), | |
102 | "device_id", &device_id) != 0) { | |
103 | device_id = AR9130_DEVID; | |
104 | } | |
105 | ||
106 | device_printf(dev, "Vendor=0x%04x, Device=0x%04x\n", | |
107 | vendor_id & 0xffff, | |
108 | device_id & 0xffff); | |
109 | ||
110 | /* Attempt to probe */ | |
111 | devname = ath_hal_probe(vendor_id, device_id); | |
112 | ||
113 | if (devname != NULL) { | |
114 | device_set_desc(dev, devname); | |
115 | return BUS_PROBE_DEFAULT; | |
116 | } | |
117 | return ENXIO; | |
118 | } | |
119 | ||
120 | static int | |
121 | ath_ahb_attach(device_t dev) | |
122 | { | |
123 | struct ath_ahb_softc *psc = device_get_softc(dev); | |
124 | struct ath_softc *sc = &psc->sc_sc; | |
125 | int error = ENXIO; | |
126 | int rid; | |
127 | long eepromaddr; | |
128 | int eepromsize; | |
129 | uint8_t *p; | |
130 | int device_id, vendor_id; | |
131 | ||
132 | sc->sc_dev = dev; | |
133 | ||
134 | rid = 0; | |
135 | psc->sc_sr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); | |
136 | if (psc->sc_sr == NULL) { | |
137 | device_printf(dev, "cannot map register space\n"); | |
138 | goto bad; | |
139 | } | |
140 | ||
141 | if (resource_long_value(device_get_name(dev), device_get_unit(dev), | |
142 | "eepromaddr", &eepromaddr) != 0) { | |
143 | device_printf(dev, "cannot fetch 'eepromaddr' from hints\n"); | |
144 | goto bad0; | |
145 | } | |
146 | ||
147 | /* | |
148 | * The default EEPROM size is 2048 * 16 bit words. | |
149 | * Later EEPROM/OTP/flash regions may be quite a bit bigger. | |
150 | */ | |
151 | if (resource_int_value(device_get_name(dev), device_get_unit(dev), | |
152 | "eepromsize", &eepromsize) != 0) { | |
153 | eepromsize = ATH_EEPROM_DATA_SIZE * 2; | |
154 | } | |
155 | ||
156 | ||
157 | rid = 0; | |
158 | device_printf(sc->sc_dev, "eeprom @ %p (%d bytes)\n", | |
159 | (void *) eepromaddr, eepromsize); | |
160 | psc->sc_eeprom = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, (uintptr_t) eepromaddr, | |
161 | (uintptr_t) eepromaddr + (uintptr_t) (eepromsize - 1), 0, RF_ACTIVE); | |
162 | if (psc->sc_eeprom == NULL) { | |
163 | device_printf(dev, "cannot map eeprom space\n"); | |
164 | goto bad0; | |
165 | } | |
166 | ||
167 | /* XXX uintptr_t is a bandaid for ia64; to be fixed */ | |
168 | sc->sc_st = (HAL_BUS_TAG)(uintptr_t) rman_get_bustag(psc->sc_sr); | |
169 | sc->sc_sh = (HAL_BUS_HANDLE) rman_get_bushandle(psc->sc_sr); | |
170 | /* | |
171 | * Mark device invalid so any interrupts (shared or otherwise) | |
172 | * that arrive before the HAL is setup are discarded. | |
173 | */ | |
174 | sc->sc_invalid = 1; | |
175 | ||
176 | /* Copy the EEPROM data out */ | |
3133c5e3 | 177 | sc->sc_eepromdata = kmalloc(eepromsize, M_TEMP, M_INTWAIT | M_ZERO); |
572ff6f6 MD |
178 | if (sc->sc_eepromdata == NULL) { |
179 | device_printf(dev, "cannot allocate memory for eeprom data\n"); | |
180 | goto bad1; | |
181 | } | |
182 | device_printf(sc->sc_dev, "eeprom data @ %p\n", (void *) rman_get_bushandle(psc->sc_eeprom)); | |
183 | /* XXX why doesn't this work? -adrian */ | |
184 | #if 0 | |
185 | bus_space_read_multi_1( | |
186 | rman_get_bustag(psc->sc_eeprom), | |
187 | rman_get_bushandle(psc->sc_eeprom), | |
188 | 0, (u_int8_t *) sc->sc_eepromdata, eepromsize); | |
189 | #endif | |
190 | p = (void *) rman_get_bushandle(psc->sc_eeprom); | |
191 | memcpy(sc->sc_eepromdata, p, eepromsize); | |
192 | ||
193 | /* | |
194 | * Arrange interrupt line. | |
195 | */ | |
196 | rid = 0; | |
197 | psc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE|RF_ACTIVE); | |
198 | if (psc->sc_irq == NULL) { | |
199 | device_printf(dev, "could not map interrupt\n"); | |
200 | goto bad1; | |
201 | } | |
202 | if (bus_setup_intr(dev, psc->sc_irq, | |
3133c5e3 MD |
203 | INTR_MPSAFE, ath_intr, |
204 | sc, &psc->sc_ih, | |
205 | &wlan_global_serializer)) { | |
572ff6f6 MD |
206 | device_printf(dev, "could not establish interrupt\n"); |
207 | goto bad2; | |
208 | } | |
209 | ||
210 | /* | |
211 | * Setup DMA descriptor area. | |
212 | */ | |
213 | if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ | |
3133c5e3 | 214 | 4, 0, /* alignment, bounds */ |
572ff6f6 MD |
215 | BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ |
216 | BUS_SPACE_MAXADDR, /* highaddr */ | |
217 | NULL, NULL, /* filter, filterarg */ | |
218 | 0x3ffff, /* maxsize XXX */ | |
219 | ATH_MAX_SCATTER, /* nsegments */ | |
220 | 0x3ffff, /* maxsegsize XXX */ | |
221 | BUS_DMA_ALLOCNOW, /* flags */ | |
572ff6f6 MD |
222 | &sc->sc_dmat)) { |
223 | device_printf(dev, "cannot allocate DMA tag\n"); | |
224 | goto bad3; | |
225 | } | |
226 | ||
227 | /* | |
228 | * Check if a device/vendor ID is provided in hints. | |
229 | */ | |
230 | if (resource_int_value(device_get_name(dev), device_get_unit(dev), | |
231 | "vendor_id", &vendor_id) != 0) { | |
232 | vendor_id = VENDOR_ATHEROS; | |
233 | } | |
234 | ||
235 | if (resource_int_value(device_get_name(dev), device_get_unit(dev), | |
236 | "device_id", &device_id) != 0) { | |
237 | device_id = AR9130_DEVID; | |
238 | } | |
239 | ||
240 | ATH_LOCK_INIT(sc); | |
241 | ATH_PCU_LOCK_INIT(sc); | |
242 | ATH_RX_LOCK_INIT(sc); | |
243 | ATH_TX_LOCK_INIT(sc); | |
244 | ATH_TX_IC_LOCK_INIT(sc); | |
245 | ATH_TXSTATUS_LOCK_INIT(sc); | |
246 | ||
247 | error = ath_attach(device_id, sc); | |
248 | if (error == 0) /* success */ | |
249 | return 0; | |
250 | ||
251 | ATH_TXSTATUS_LOCK_DESTROY(sc); | |
252 | ATH_RX_LOCK_DESTROY(sc); | |
253 | ATH_TX_LOCK_DESTROY(sc); | |
254 | ATH_TX_IC_LOCK_DESTROY(sc); | |
255 | ATH_PCU_LOCK_DESTROY(sc); | |
256 | ATH_LOCK_DESTROY(sc); | |
257 | bus_dma_tag_destroy(sc->sc_dmat); | |
258 | bad3: | |
259 | bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); | |
260 | bad2: | |
261 | bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); | |
262 | bad1: | |
263 | bus_release_resource(dev, SYS_RES_MEMORY, 0, psc->sc_eeprom); | |
264 | bad0: | |
265 | bus_release_resource(dev, SYS_RES_MEMORY, 0, psc->sc_sr); | |
266 | bad: | |
267 | /* XXX?! */ | |
268 | if (sc->sc_eepromdata) | |
3133c5e3 | 269 | kfree(sc->sc_eepromdata, M_TEMP); |
572ff6f6 MD |
270 | return (error); |
271 | } | |
272 | ||
273 | static int | |
274 | ath_ahb_detach(device_t dev) | |
275 | { | |
276 | struct ath_ahb_softc *psc = device_get_softc(dev); | |
277 | struct ath_softc *sc = &psc->sc_sc; | |
278 | ||
279 | /* check if device was removed */ | |
280 | sc->sc_invalid = !bus_child_present(dev); | |
281 | ||
282 | ath_detach(sc); | |
283 | ||
284 | bus_generic_detach(dev); | |
285 | bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); | |
286 | bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); | |
287 | ||
288 | bus_dma_tag_destroy(sc->sc_dmat); | |
289 | bus_release_resource(dev, SYS_RES_MEMORY, 0, psc->sc_sr); | |
290 | bus_release_resource(dev, SYS_RES_MEMORY, 0, psc->sc_eeprom); | |
291 | /* XXX?! */ | |
292 | if (sc->sc_eepromdata) | |
3133c5e3 | 293 | kfree(sc->sc_eepromdata, M_TEMP); |
572ff6f6 MD |
294 | |
295 | ATH_TXSTATUS_LOCK_DESTROY(sc); | |
296 | ATH_RX_LOCK_DESTROY(sc); | |
297 | ATH_TX_LOCK_DESTROY(sc); | |
298 | ATH_TX_IC_LOCK_DESTROY(sc); | |
299 | ATH_PCU_LOCK_DESTROY(sc); | |
300 | ATH_LOCK_DESTROY(sc); | |
301 | ||
302 | return (0); | |
303 | } | |
304 | ||
305 | static int | |
306 | ath_ahb_shutdown(device_t dev) | |
307 | { | |
308 | struct ath_ahb_softc *psc = device_get_softc(dev); | |
309 | ||
310 | ath_shutdown(&psc->sc_sc); | |
311 | return (0); | |
312 | } | |
313 | ||
314 | static int | |
315 | ath_ahb_suspend(device_t dev) | |
316 | { | |
317 | struct ath_ahb_softc *psc = device_get_softc(dev); | |
318 | ||
319 | ath_suspend(&psc->sc_sc); | |
320 | ||
321 | return (0); | |
322 | } | |
323 | ||
324 | static int | |
325 | ath_ahb_resume(device_t dev) | |
326 | { | |
327 | struct ath_ahb_softc *psc = device_get_softc(dev); | |
328 | ||
329 | ath_resume(&psc->sc_sc); | |
330 | ||
331 | return (0); | |
332 | } | |
333 | ||
334 | static device_method_t ath_ahb_methods[] = { | |
335 | /* Device interface */ | |
336 | DEVMETHOD(device_probe, ath_ahb_probe), | |
337 | DEVMETHOD(device_attach, ath_ahb_attach), | |
338 | DEVMETHOD(device_detach, ath_ahb_detach), | |
339 | DEVMETHOD(device_shutdown, ath_ahb_shutdown), | |
340 | DEVMETHOD(device_suspend, ath_ahb_suspend), | |
341 | DEVMETHOD(device_resume, ath_ahb_resume), | |
342 | ||
343 | { 0,0 } | |
344 | }; | |
345 | static driver_t ath_ahb_driver = { | |
346 | "ath", | |
347 | ath_ahb_methods, | |
348 | sizeof (struct ath_ahb_softc) | |
349 | }; | |
350 | static devclass_t ath_devclass; | |
3133c5e3 MD |
351 | DRIVER_MODULE(ath_ahb, nexus, ath_ahb_driver, ath_devclass, 0, 0); |
352 | MODULE_VERSION(ath_ahb, 1); | |
353 | MODULE_DEPEND(ath_ahb, wlan, 1, 1, 1); /* 802.11 media layer */ | |
354 | MODULE_DEPEND(ath_ahb, if_ath, 1, 1, 1); /* if_ath driver */ | |
355 | MODULE_DEPEND(ath_ahb, ath_hal, 1, 1, 1); /* Atheros HAL */ | |
356 | MODULE_DEPEND(ath_ahb, ath_rate, 1, 1, 1); /* rate control alg */ | |
357 | MODULE_DEPEND(ath_ahb, ath_dfs, 1, 1, 1); /* wtf */ |