1 1.10 chs /* $NetBSD: ar5315.c,v 1.10 2012/10/27 17:18:02 chs Exp $ */ 2 1.1 gdamore 3 1.1 gdamore /* 4 1.1 gdamore * Copyright (c) 2006 Urbana-Champaign Independent Media Center. 5 1.1 gdamore * Copyright (c) 2006 Garrett D'Amore. 6 1.1 gdamore * All rights reserved. 7 1.1 gdamore * 8 1.1 gdamore * Portions of this code were written by Garrett D'Amore for the 9 1.1 gdamore * Champaign-Urbana Community Wireless Network Project. 10 1.1 gdamore * 11 1.1 gdamore * Redistribution and use in source and binary forms, with or 12 1.1 gdamore * without modification, are permitted provided that the following 13 1.1 gdamore * conditions are met: 14 1.1 gdamore * 1. Redistributions of source code must retain the above copyright 15 1.1 gdamore * notice, this list of conditions and the following disclaimer. 16 1.1 gdamore * 2. Redistributions in binary form must reproduce the above 17 1.1 gdamore * copyright notice, this list of conditions and the following 18 1.1 gdamore * disclaimer in the documentation and/or other materials provided 19 1.1 gdamore * with the distribution. 20 1.1 gdamore * 3. All advertising materials mentioning features or use of this 21 1.1 gdamore * software must display the following acknowledgements: 22 1.1 gdamore * This product includes software developed by the Urbana-Champaign 23 1.1 gdamore * Independent Media Center. 24 1.1 gdamore * This product includes software developed by Garrett D'Amore. 25 1.1 gdamore * 4. Urbana-Champaign Independent Media Center's name and Garrett 26 1.1 gdamore * D'Amore's name may not be used to endorse or promote products 27 1.1 gdamore * derived from this software without specific prior written permission. 28 1.1 gdamore * 29 1.1 gdamore * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT 30 1.1 gdamore * MEDIA CENTER AND GARRETT D'AMORE ``AS IS'' AND ANY EXPRESS OR 31 1.1 gdamore * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 32 1.1 gdamore * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 1.1 gdamore * ARE DISCLAIMED. IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT 34 1.1 gdamore * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT, 35 1.1 gdamore * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 1.1 gdamore * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 1.1 gdamore * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 1.1 gdamore * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 1.1 gdamore * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 1.1 gdamore * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 41 1.1 gdamore * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 1.1 gdamore */ 43 1.1 gdamore 44 1.1 gdamore 45 1.1 gdamore /* 46 1.1 gdamore * This file includes a bunch of implementation specific bits for 47 1.1 gdamore * AR5315, which differs these from other members of the AR531X 48 1.1 gdamore * family. 49 1.1 gdamore */ 50 1.1 gdamore #include <sys/cdefs.h> 51 1.10 chs __KERNEL_RCSID(0, "$NetBSD: ar5315.c,v 1.10 2012/10/27 17:18:02 chs Exp $"); 52 1.1 gdamore 53 1.1 gdamore #include "opt_ddb.h" 54 1.1 gdamore #include "opt_kgdb.h" 55 1.8 matt #include "opt_memsize.h" 56 1.8 matt 57 1.8 matt #define __INTR_PRIVATE 58 1.1 gdamore 59 1.1 gdamore #include <sys/param.h> 60 1.1 gdamore #include <sys/systm.h> 61 1.1 gdamore #include <sys/kernel.h> 62 1.1 gdamore #include <sys/buf.h> 63 1.5 dyoung #include <sys/device.h> 64 1.1 gdamore 65 1.1 gdamore #include <mips/cache.h> 66 1.1 gdamore #include <mips/locore.h> 67 1.1 gdamore #include <mips/cpuregs.h> 68 1.1 gdamore 69 1.1 gdamore #include <net/if.h> 70 1.1 gdamore #include <net/if_ether.h> 71 1.1 gdamore 72 1.5 dyoung #include <prop/proplib.h> 73 1.5 dyoung 74 1.6 alc #include <ah_soc.h> /* XXX really doesn't belong in hal */ 75 1.1 gdamore 76 1.1 gdamore #include <mips/atheros/include/ar5315reg.h> 77 1.8 matt #include <mips/atheros/include/platform.h> 78 1.1 gdamore #include <mips/atheros/include/arbusvar.h> 79 1.1 gdamore 80 1.9 matt #include <mips/locore.h> 81 1.1 gdamore 82 1.1 gdamore /* helper macro for accessing system registers without bus space */ 83 1.1 gdamore #define REGVAL(x) *((volatile uint32_t *)(MIPS_PHYS_TO_KSEG1((x)))) 84 1.1 gdamore #define GETSYSREG(x) REGVAL((x) + AR5315_SYSREG_BASE) 85 1.1 gdamore #define PUTSYSREG(x,v) (REGVAL((x) + AR5315_SYSREG_BASE)) = (v) 86 1.1 gdamore #define GETPCIREG(x) REGVAL((x) + AR5315_PCI_BASE) 87 1.1 gdamore #define PUTPCIREG(x,v) (REGVAL((x) + AR5315_PCI_BASE)) = (v) 88 1.2 gdamore #define GETSDRAMREG(x) REGVAL((x) + AR5315_SDRAMCTL_BASE) 89 1.1 gdamore 90 1.8 matt static uint32_t 91 1.8 matt ar5315_get_memsize(void) 92 1.1 gdamore { 93 1.2 gdamore #ifndef MEMSIZE 94 1.2 gdamore uint32_t memsize = 0; 95 1.2 gdamore uint32_t memcfg, cw, rw, dw; 96 1.1 gdamore 97 1.1 gdamore /* 98 1.2 gdamore * Determine the memory size. We query the board info. 99 1.1 gdamore */ 100 1.2 gdamore memcfg = GETSDRAMREG(AR5315_SDRAMCTL_MEM_CFG); 101 1.8 matt cw = __SHIFTOUT(memcfg, AR5315_MEM_CFG_COL_WIDTH); 102 1.2 gdamore cw += 1; 103 1.8 matt rw = __SHIFTOUT(memcfg, AR5315_MEM_CFG_ROW_WIDTH); 104 1.2 gdamore rw += 1; 105 1.2 gdamore 106 1.2 gdamore /* XXX: according to redboot, this could be wrong if DDR SDRAM */ 107 1.8 matt dw = __SHIFTOUT(memcfg, AR5315_MEM_CFG_DATA_WIDTH); 108 1.2 gdamore dw += 1; 109 1.2 gdamore dw *= 8; /* bits */ 110 1.2 gdamore 111 1.2 gdamore /* not too sure about this math, but it _seems_ to add up */ 112 1.2 gdamore memsize = (1 << cw) * (1 << rw) * dw; 113 1.2 gdamore #if 0 114 1.2 gdamore printf("SDRAM_MEM_CFG =%x, cw=%d rw=%d dw=%d xmemsize=%d\n", memcfg, 115 1.2 gdamore cw, rw, dw, memsize); 116 1.1 gdamore #endif 117 1.1 gdamore 118 1.1 gdamore return (memsize); 119 1.2 gdamore #else 120 1.2 gdamore /* compile time value forced */ 121 1.2 gdamore return MEMSIZE; 122 1.2 gdamore #endif 123 1.1 gdamore } 124 1.1 gdamore 125 1.8 matt static void 126 1.8 matt ar5315_wdog_reload(uint32_t period) 127 1.1 gdamore { 128 1.1 gdamore 129 1.1 gdamore if (period == 0) { 130 1.1 gdamore PUTSYSREG(AR5315_SYSREG_WDOG_CTL, AR5315_WDOG_CTL_IGNORE); 131 1.1 gdamore PUTSYSREG(AR5315_SYSREG_WDOG_TIMER, 0); 132 1.1 gdamore } else { 133 1.1 gdamore PUTSYSREG(AR5315_SYSREG_WDOG_TIMER, period); 134 1.1 gdamore PUTSYSREG(AR5315_SYSREG_WDOG_CTL, AR5315_WDOG_CTL_RESET); 135 1.1 gdamore } 136 1.1 gdamore } 137 1.1 gdamore 138 1.8 matt static void 139 1.8 matt ar5315_bus_init(void) 140 1.1 gdamore { 141 1.1 gdamore /* 142 1.8 matt * Set CCA of KSEG0 access to 3 (actually any value other than 143 1.8 matt * 2 & 7 means that KSEG0 accesses are cached but 3 is standard 144 1.8 matt * value for writeback caching). 145 1.1 gdamore */ 146 1.8 matt mips3_cp0_config_write((mips3_cp0_config_read() & -8) | 3); 147 1.1 gdamore 148 1.1 gdamore PUTSYSREG(AR5315_SYSREG_AHB_ERR0, AR5315_AHB_ERROR_DET); 149 1.1 gdamore GETSYSREG(AR5315_SYSREG_AHB_ERR1); 150 1.1 gdamore } 151 1.1 gdamore 152 1.8 matt static void 153 1.8 matt ar5315_get_freqs(struct arfreqs *freqs) 154 1.1 gdamore { 155 1.8 matt static const uint8_t pll_divide_table[] = { 156 1.1 gdamore 2, 3, 4, 6, 3, 157 1.1 gdamore /* 158 1.1 gdamore * these entries are bogus, but it avoids a possible 159 1.1 gdamore * bad table dereference 160 1.1 gdamore */ 161 1.1 gdamore 1, 1, 1 162 1.1 gdamore }; 163 1.8 matt static const uint8_t pre_divide_table[] = { 164 1.1 gdamore 1, 2, 4, 5 165 1.1 gdamore }; 166 1.1 gdamore 167 1.8 matt const uint32_t pllc = GETSYSREG(AR5315_SYSREG_PLLC_CTL); 168 1.1 gdamore 169 1.8 matt const uint32_t refdiv = pre_divide_table[AR5315_PLLC_REF_DIV(pllc)]; 170 1.8 matt const uint32_t fbdiv = AR5315_PLLC_FB_DIV(pllc); 171 1.8 matt const uint32_t div2 = (AR5315_PLLC_DIV_2(pllc) + 1) * 2; /* results in 2 or 4 */ 172 1.1 gdamore 173 1.8 matt freqs->freq_ref = 40000000; 174 1.1 gdamore 175 1.8 matt /* 40MHz reference clk, reference and feedback dividers */ 176 1.8 matt freqs->freq_pll = (freqs->freq_ref / refdiv) * div2 * fbdiv; 177 1.1 gdamore 178 1.8 matt const uint32_t pllout[4] = { 179 1.8 matt /* CLKM select */ 180 1.8 matt [0] = freqs->freq_pll / pll_divide_table[AR5315_PLLC_CLKM(pllc)], 181 1.8 matt [1] = freqs->freq_pll / pll_divide_table[AR5315_PLLC_CLKM(pllc)], 182 1.1 gdamore 183 1.8 matt /* CLKC select */ 184 1.8 matt [2] = freqs->freq_pll / pll_divide_table[AR5315_PLLC_CLKC(pllc)], 185 1.1 gdamore 186 1.8 matt /* ref_clk select */ 187 1.8 matt [3] = freqs->freq_ref, /* use original reference clock */ 188 1.8 matt }; 189 1.1 gdamore 190 1.8 matt const uint32_t amba_clkctl = GETSYSREG(AR5315_SYSREG_AMBACLK); 191 1.8 matt uint32_t ambadiv = AR5315_CLOCKCTL_DIV(amba_clkctl); 192 1.8 matt ambadiv = ambadiv ? (ambadiv * 2) : 1; 193 1.8 matt freqs->freq_bus = pllout[AR5315_CLOCKCTL_SELECT(amba_clkctl)] / ambadiv; 194 1.1 gdamore 195 1.8 matt const uint32_t cpu_clkctl = GETSYSREG(AR5315_SYSREG_CPUCLK); 196 1.8 matt uint32_t cpudiv = AR5315_CLOCKCTL_DIV(cpu_clkctl); 197 1.8 matt cpudiv = cpudiv ? (cpudiv * 2) : 1; 198 1.8 matt freqs->freq_cpu = pllout[AR5315_CLOCKCTL_SELECT(cpu_clkctl)] / cpudiv; 199 1.1 gdamore 200 1.8 matt freqs->freq_mem = 0; 201 1.1 gdamore } 202 1.1 gdamore 203 1.1 gdamore static void 204 1.8 matt addprop_data(device_t dev, const char *name, const uint8_t *data, 205 1.1 gdamore int len) 206 1.1 gdamore { 207 1.1 gdamore prop_data_t pd; 208 1.1 gdamore pd = prop_data_create_data(data, len); 209 1.1 gdamore KASSERT(pd != NULL); 210 1.4 thorpej if (prop_dictionary_set(device_properties(dev), name, pd) == false) { 211 1.1 gdamore printf("WARNING: unable to set %s property for %s\n", 212 1.1 gdamore name, device_xname(dev)); 213 1.1 gdamore } 214 1.1 gdamore prop_object_release(pd); 215 1.1 gdamore } 216 1.1 gdamore 217 1.1 gdamore static void 218 1.8 matt addprop_integer(device_t dev, const char *name, uint32_t val) 219 1.1 gdamore { 220 1.1 gdamore prop_number_t pn; 221 1.1 gdamore pn = prop_number_create_integer(val); 222 1.1 gdamore KASSERT(pn != NULL); 223 1.4 thorpej if (prop_dictionary_set(device_properties(dev), name, pn) == false) { 224 1.1 gdamore printf("WARNING: unable to set %s property for %s", 225 1.1 gdamore name, device_xname(dev)); 226 1.1 gdamore } 227 1.1 gdamore prop_object_release(pn); 228 1.1 gdamore } 229 1.1 gdamore 230 1.8 matt static void 231 1.10 chs ar5315_device_register(device_t dev, void *aux) 232 1.1 gdamore { 233 1.8 matt const struct arbus_attach_args * const aa = aux; 234 1.8 matt const struct ar531x_boarddata * const info = atheros_get_board_info(); 235 1.8 matt 236 1.8 matt if (device_is_a(dev, "com")) { 237 1.8 matt addprop_integer(dev, "frequency", atheros_get_bus_freq()); 238 1.8 matt } 239 1.1 gdamore 240 1.1 gdamore if (info == NULL) { 241 1.1 gdamore /* nothing known about this board! */ 242 1.1 gdamore return; 243 1.1 gdamore } 244 1.1 gdamore 245 1.1 gdamore /* 246 1.1 gdamore * We don't ever know the boot device. But that's because the 247 1.1 gdamore * firmware only loads from the network. 248 1.1 gdamore */ 249 1.1 gdamore 250 1.1 gdamore /* Fetch the MAC addresses. */ 251 1.1 gdamore if (device_is_a(dev, "ae")) { 252 1.1 gdamore const uint8_t *enet; 253 1.1 gdamore 254 1.1 gdamore if (aa->aa_addr == AR5315_ENET_BASE) 255 1.1 gdamore enet = info->enet0Mac; 256 1.1 gdamore else 257 1.1 gdamore return; 258 1.1 gdamore 259 1.7 martin addprop_data(dev, "mac-address", enet, ETHER_ADDR_LEN); 260 1.1 gdamore } 261 1.1 gdamore 262 1.1 gdamore if (device_is_a(dev, "ath")) { 263 1.1 gdamore const uint8_t *enet; 264 1.1 gdamore 265 1.1 gdamore if (aa->aa_addr == AR5315_WLAN_BASE) 266 1.1 gdamore enet = info->wlan0Mac; 267 1.1 gdamore else 268 1.1 gdamore return; 269 1.1 gdamore 270 1.7 martin addprop_data(dev, "mac-address", enet, ETHER_ADDR_LEN); 271 1.1 gdamore 272 1.1 gdamore addprop_integer(dev, "wmac-rev", 273 1.1 gdamore GETSYSREG(AR5315_SYSREG_SREV)); 274 1.1 gdamore } 275 1.1 gdamore 276 1.1 gdamore if (device_is_a(dev, "argpio")) { 277 1.1 gdamore if (info->config & BD_RSTFACTORY) { 278 1.1 gdamore addprop_integer(dev, "reset-pin", 279 1.1 gdamore info->resetConfigGpio); 280 1.1 gdamore } 281 1.1 gdamore if (info->config & BD_SYSLED) { 282 1.1 gdamore addprop_integer(dev, "sysled-pin", 283 1.1 gdamore info->sysLedGpio); 284 1.1 gdamore } 285 1.1 gdamore } 286 1.1 gdamore } 287 1.1 gdamore 288 1.8 matt static int 289 1.8 matt ar5315_enable_device(const struct atheros_device *adv) 290 1.1 gdamore { 291 1.8 matt if (adv->adv_addr == AR5315_WLAN_BASE) { 292 1.1 gdamore /* enable arbitration for wlan */ 293 1.1 gdamore PUTSYSREG(AR5315_SYSREG_AHB_ARB_CTL, 294 1.1 gdamore GETSYSREG(AR5315_SYSREG_AHB_ARB_CTL) | AR5315_ARB_WLAN); 295 1.1 gdamore 296 1.1 gdamore /* set WLAN for big endian */ 297 1.1 gdamore PUTSYSREG(AR5315_SYSREG_ENDIAN, 298 1.1 gdamore GETSYSREG(AR5315_SYSREG_ENDIAN) | AR5315_ENDIAN_WLAN); 299 1.1 gdamore 300 1.1 gdamore /* wake up the mac */ 301 1.1 gdamore PUTPCIREG(AR5315_PCI_MAC_SCR, 302 1.1 gdamore (GETPCIREG(AR5315_PCI_MAC_SCR) & ~PCI_MAC_SCR_SLM_MASK) | 303 1.1 gdamore PCI_MAC_SCR_SLM_FWAKE); 304 1.1 gdamore 305 1.1 gdamore /* wait for it to wake up */ 306 1.1 gdamore while (GETPCIREG(AR5315_PCI_MAC_PCICFG) & 307 1.1 gdamore PCI_MAC_PCICFG_SPWR_DN); 308 1.1 gdamore } 309 1.1 gdamore return 0; 310 1.1 gdamore } 311 1.8 matt 312 1.8 matt static void 313 1.8 matt ar5315_intr_init(void) 314 1.8 matt { 315 1.8 matt atheros_intr_init(); 316 1.8 matt } 317 1.8 matt 318 1.8 matt static void 319 1.8 matt ar5315_reset(void) 320 1.8 matt { 321 1.8 matt PUTSYSREG(AR5315_SYSREG_COLDRESET, 322 1.8 matt AR5315_COLD_AHB | AR5315_COLD_APB | AR5315_COLD_CPU); 323 1.8 matt } 324 1.8 matt 325 1.8 matt const static struct atheros_device ar5315_devices[] = { 326 1.8 matt { 327 1.8 matt .adv_name = "com", 328 1.8 matt .adv_addr = AR5315_UART_BASE, 329 1.8 matt .adv_size = 0x1000, 330 1.8 matt .adv_cirq = AR5315_CPU_IRQ_MISC, 331 1.8 matt .adv_mirq = AR5315_MISC_IRQ_UART, 332 1.8 matt }, { 333 1.8 matt .adv_name = "ae", 334 1.8 matt .adv_addr = AR5315_ENET_BASE, 335 1.8 matt .adv_size = 0x100000, 336 1.8 matt .adv_cirq = AR5315_CPU_IRQ_ENET, 337 1.8 matt .adv_mirq = -1, 338 1.8 matt }, { 339 1.8 matt .adv_name = "ath", 340 1.8 matt .adv_addr = AR5315_WLAN_BASE, 341 1.8 matt .adv_size = 0x100000, 342 1.8 matt .adv_cirq = AR5315_CPU_IRQ_WLAN, 343 1.8 matt .adv_mirq = -1, 344 1.8 matt }, { 345 1.8 matt .adv_name = "arspi", 346 1.8 matt .adv_addr = AR5315_SPI_BASE, 347 1.8 matt .adv_size = 0x10, 348 1.8 matt .adv_cirq = AR5315_CPU_IRQ_MISC, 349 1.8 matt .adv_mirq = AR5315_MISC_IRQ_SPI, 350 1.8 matt }, { 351 1.8 matt .adv_name = NULL 352 1.8 matt } 353 1.8 matt }; 354 1.8 matt 355 1.8 matt static const struct ipl_sr_map ar5315_ipl_sr_map = { 356 1.8 matt .sr_bits = { 357 1.8 matt [IPL_NONE] = 0, 358 1.8 matt [IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0, 359 1.8 matt [IPL_SOFTBIO] = MIPS_SOFT_INT_MASK_0, 360 1.8 matt [IPL_SOFTNET] = MIPS_SOFT_INT_MASK, 361 1.8 matt [IPL_SOFTSERIAL] = MIPS_SOFT_INT_MASK, 362 1.8 matt [IPL_VM] = MIPS_SOFT_INT_MASK | MIPS_INT_MASK_0 363 1.8 matt | MIPS_INT_MASK_1 | MIPS_INT_MASK_2, 364 1.8 matt [IPL_SCHED] = MIPS_INT_MASK, 365 1.8 matt [IPL_DDB] = MIPS_INT_MASK, 366 1.8 matt [IPL_HIGH] = MIPS_INT_MASK, 367 1.8 matt }, 368 1.8 matt }; 369 1.8 matt 370 1.8 matt static const char * const ar5315_cpu_intrnames[] = { 371 1.8 matt "int 0 (misc)", 372 1.8 matt "int 1 (wlan)", 373 1.8 matt "int 2 (enet)", 374 1.8 matt }; 375 1.8 matt 376 1.8 matt static const char * const ar5315_misc_intrnames[] = { 377 1.8 matt "misc 0 (uart)", 378 1.8 matt "misc 1 (i2c)", 379 1.8 matt "misc 2 (spi)", 380 1.8 matt "misc 3 (ahb error)", 381 1.8 matt "misc 4 (apb error)", 382 1.8 matt "misc 5 (timer)", 383 1.8 matt "misc 6 (gpio)", 384 1.8 matt "misc 7 (watchdog)", 385 1.8 matt "misc 8 (ir)" 386 1.8 matt }; 387 1.8 matt 388 1.8 matt const struct atheros_platformsw ar5315_platformsw = { 389 1.8 matt .apsw_intrsw = &atheros_intrsw, 390 1.8 matt .apsw_intr_init = ar5315_intr_init, 391 1.8 matt .apsw_cpu_intrnames = ar5315_cpu_intrnames, 392 1.8 matt .apsw_misc_intrnames = ar5315_misc_intrnames, 393 1.8 matt .apsw_cpu_nintrs = __arraycount(ar5315_cpu_intrnames), 394 1.8 matt .apsw_misc_nintrs = __arraycount(ar5315_misc_intrnames), 395 1.8 matt .apsw_cpuirq_misc = AR5315_CPU_IRQ_MISC, 396 1.8 matt .apsw_ipl_sr_map = &ar5315_ipl_sr_map, 397 1.8 matt 398 1.8 matt .apsw_revision_id_addr = AR5315_SYSREG_BASE + AR5315_SYSREG_SREV, 399 1.8 matt .apsw_uart0_base = AR5315_UART_BASE, 400 1.8 matt .apsw_misc_intstat = AR5315_SYSREG_BASE + AR5315_SYSREG_MISC_INTSTAT, 401 1.8 matt .apsw_misc_intmask = AR5315_SYSREG_BASE + AR5315_SYSREG_MISC_INTMASK, 402 1.8 matt 403 1.8 matt /* 404 1.8 matt * CPU specific routines. 405 1.8 matt */ 406 1.8 matt .apsw_get_memsize = ar5315_get_memsize, 407 1.8 matt .apsw_wdog_reload = ar5315_wdog_reload, 408 1.8 matt .apsw_bus_init = ar5315_bus_init, 409 1.8 matt .apsw_reset = ar5315_reset, 410 1.8 matt 411 1.8 matt .apsw_get_freqs = ar5315_get_freqs, 412 1.8 matt .apsw_device_register = ar5315_device_register, 413 1.8 matt .apsw_enable_device = ar5315_enable_device, 414 1.8 matt .apsw_devices = ar5315_devices, 415 1.8 matt }; 416