1 /* $NetBSD: if_awi_pcmcia.c,v 1.22 2001/12/15 13:23:22 soren Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Bill Sommerfeld 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * PCMCIA attachment for BayStack 650 802.11FH PCMCIA card, 41 * based on the AMD 79c930 802.11 controller chip. 42 * 43 * This attachment can probably be trivally adapted for other FH and 44 * DS cards based on the same chipset. 45 */ 46 47 #include <sys/cdefs.h> 48 __KERNEL_RCSID(0, "$NetBSD: if_awi_pcmcia.c,v 1.22 2001/12/15 13:23:22 soren Exp $"); 49 50 #include <sys/param.h> 51 #include <sys/systm.h> 52 #include <sys/mbuf.h> 53 #include <sys/socket.h> 54 #include <sys/ioctl.h> 55 #include <sys/errno.h> 56 #include <sys/syslog.h> 57 #include <sys/select.h> 58 #include <sys/device.h> 59 60 #include <net/if.h> 61 #include <net/if_dl.h> 62 #include <net/if_ether.h> 63 #include <net/if_media.h> 64 #include <net/if_ieee80211.h> 65 66 #include <machine/cpu.h> 67 #include <machine/bus.h> 68 #include <machine/intr.h> 69 70 #include <dev/ic/am79c930reg.h> 71 #include <dev/ic/am79c930var.h> 72 #include <dev/ic/awireg.h> 73 #include <dev/ic/awivar.h> 74 75 #include <dev/pcmcia/pcmciareg.h> 76 #include <dev/pcmcia/pcmciavar.h> 77 #include <dev/pcmcia/pcmciadevs.h> 78 79 static int awi_pcmcia_match(struct device *, struct cfdata *, void *); 80 static void awi_pcmcia_attach(struct device *, struct device *, void *); 81 static int awi_pcmcia_detach(struct device *, int); 82 static int awi_pcmcia_enable(struct awi_softc *); 83 static void awi_pcmcia_disable(struct awi_softc *); 84 85 struct awi_pcmcia_softc { 86 struct awi_softc sc_awi; /* real "awi" softc */ 87 88 /* PCMCIA-specific goo */ 89 struct pcmcia_io_handle sc_pcioh; /* PCMCIA i/o space info */ 90 struct pcmcia_mem_handle sc_memh; /* PCMCIA memory space info */ 91 int sc_io_window; /* our i/o window */ 92 int sc_mem_window; /* our memory window */ 93 struct pcmcia_function *sc_pf; /* our PCMCIA function */ 94 void *sc_ih; /* interrupt handler */ 95 }; 96 97 static int awi_pcmcia_find __P((struct awi_pcmcia_softc *, 98 struct pcmcia_attach_args *, struct pcmcia_config_entry *)); 99 100 struct cfattach awi_pcmcia_ca = { 101 sizeof(struct awi_pcmcia_softc), awi_pcmcia_match, awi_pcmcia_attach, 102 awi_pcmcia_detach, awi_activate 103 }; 104 105 static const struct awi_pcmcia_product { 106 u_int32_t app_vendor; /* vendor ID */ 107 u_int32_t app_product; /* product ID */ 108 const char *app_cisinfo[4]; /* CIS information */ 109 const char *app_name; /* product name */ 110 } awi_pcmcia_products[] = { 111 { PCMCIA_VENDOR_BAY, PCMCIA_PRODUCT_BAY_STACK_650, 112 PCMCIA_CIS_BAY_STACK_650, PCMCIA_STR_BAY_STACK_650 }, 113 114 { PCMCIA_VENDOR_BAY, PCMCIA_PRODUCT_BAY_STACK_660, 115 PCMCIA_CIS_BAY_STACK_660, PCMCIA_STR_BAY_STACK_660 }, 116 117 { PCMCIA_VENDOR_BAY, PCMCIA_PRODUCT_BAY_SURFER_PRO, 118 PCMCIA_CIS_BAY_SURFER_PRO, PCMCIA_STR_BAY_SURFER_PRO }, 119 120 { PCMCIA_VENDOR_AMD, PCMCIA_PRODUCT_AMD_AM79C930, 121 PCMCIA_CIS_AMD_AM79C930, PCMCIA_STR_AMD_AM79C930 }, 122 123 { PCMCIA_VENDOR_ICOM, PCMCIA_PRODUCT_ICOM_SL200, 124 PCMCIA_CIS_ICOM_SL200, PCMCIA_STR_ICOM_SL200 }, 125 126 { PCMCIA_VENDOR_NOKIA, PCMCIA_PRODUCT_NOKIA_C020_WLAN, 127 PCMCIA_CIS_NOKIA_C020_WLAN, PCMCIA_STR_NOKIA_C020_WLAN }, 128 129 { PCMCIA_VENDOR_FARALLON, PCMCIA_PRODUCT_FARALLON_SKYLINE, 130 PCMCIA_CIS_FARALLON_SKYLINE, PCMCIA_STR_FARALLON_SKYLINE }, 131 132 { 0, 0, 133 { NULL, NULL, NULL, NULL }, NULL }, 134 }; 135 136 static const struct awi_pcmcia_product * 137 awi_pcmcia_lookup __P((struct pcmcia_attach_args *)); 138 139 static const struct awi_pcmcia_product * 140 awi_pcmcia_lookup(pa) 141 struct pcmcia_attach_args *pa; 142 { 143 const struct awi_pcmcia_product *app; 144 145 for (app = awi_pcmcia_products; app->app_name != NULL; app++) { 146 /* match by vendor/product id */ 147 if (pa->manufacturer != PCMCIA_VENDOR_INVALID && 148 pa->manufacturer == app->app_vendor && 149 pa->product != PCMCIA_PRODUCT_INVALID && 150 pa->product == app->app_product) 151 return (app); 152 153 /* match by CIS information */ 154 if (pa->card->cis1_info[0] != NULL && 155 app->app_cisinfo[0] != NULL && 156 strcmp(pa->card->cis1_info[0], app->app_cisinfo[0]) == 0 && 157 pa->card->cis1_info[1] != NULL && 158 app->app_cisinfo[1] != NULL && 159 strcmp(pa->card->cis1_info[1], app->app_cisinfo[1]) == 0) 160 return (app); 161 } 162 163 return (NULL); 164 } 165 166 static int 167 awi_pcmcia_enable(sc) 168 struct awi_softc *sc; 169 { 170 struct awi_pcmcia_softc *psc = (struct awi_pcmcia_softc *)sc; 171 struct pcmcia_function *pf = psc->sc_pf; 172 173 /* establish the interrupt. */ 174 psc->sc_ih = pcmcia_intr_establish(pf, IPL_NET, awi_intr, sc); 175 if (psc->sc_ih == NULL) { 176 printf("%s: couldn't establish interrupt\n", 177 sc->sc_dev.dv_xname); 178 return (1); 179 } 180 181 if (pcmcia_function_enable(pf)) { 182 pcmcia_intr_disestablish(pf, psc->sc_ih); 183 return (1); 184 } 185 DELAY(1000); 186 187 return (0); 188 } 189 190 static void 191 awi_pcmcia_disable(sc) 192 struct awi_softc *sc; 193 { 194 struct awi_pcmcia_softc *psc = (struct awi_pcmcia_softc *)sc; 195 struct pcmcia_function *pf = psc->sc_pf; 196 197 pcmcia_function_disable(pf); 198 pcmcia_intr_disestablish(pf, psc->sc_ih); 199 } 200 201 static int 202 awi_pcmcia_match(parent, match, aux) 203 struct device *parent; 204 struct cfdata *match; 205 void *aux; 206 { 207 struct pcmcia_attach_args *pa = aux; 208 209 if (awi_pcmcia_lookup(pa) != NULL) 210 return (1); 211 212 return (0); 213 } 214 215 static int 216 awi_pcmcia_find(psc, pa, cfe) 217 struct awi_pcmcia_softc *psc; 218 struct pcmcia_attach_args *pa; 219 struct pcmcia_config_entry *cfe; 220 { 221 struct awi_softc *sc = &psc->sc_awi; 222 int fail = 0; 223 224 /* 225 * see if we can read the firmware version sanely 226 * through the i/o ports. 227 * if not, try a different CIS string.. 228 */ 229 if (pcmcia_io_alloc(psc->sc_pf, cfe->iospace[0].start, 230 cfe->iospace[0].length, AM79C930_IO_ALIGN, 231 &psc->sc_pcioh) != 0) 232 goto fail; 233 234 if (pcmcia_io_map(psc->sc_pf, PCMCIA_WIDTH_AUTO, 0, psc->sc_pcioh.size, 235 &psc->sc_pcioh, &psc->sc_io_window)) 236 goto fail_io_free; 237 238 /* Enable the card. */ 239 pcmcia_function_init(psc->sc_pf, cfe); 240 if (pcmcia_function_enable(psc->sc_pf)) 241 goto fail_io_unmap; 242 243 sc->sc_chip.sc_bustype = AM79C930_BUS_PCMCIA; 244 sc->sc_chip.sc_iot = psc->sc_pcioh.iot; 245 sc->sc_chip.sc_ioh = psc->sc_pcioh.ioh; 246 am79c930_chip_init(&sc->sc_chip, 0); 247 248 DELAY(1000); 249 250 awi_read_bytes(sc, AWI_BANNER, sc->sc_banner, AWI_BANNER_LEN); 251 252 if (memcmp(sc->sc_banner, "PCnetMobile:", 12) == 0) 253 return (0); 254 255 fail++; 256 pcmcia_function_disable(psc->sc_pf); 257 258 fail_io_unmap: 259 fail++; 260 pcmcia_io_unmap(psc->sc_pf, psc->sc_io_window); 261 262 fail_io_free: 263 fail++; 264 pcmcia_io_free(psc->sc_pf, &psc->sc_pcioh); 265 fail: 266 fail++; 267 psc->sc_io_window = -1; 268 return (fail); 269 } 270 271 static void 272 awi_pcmcia_attach(parent, self, aux) 273 struct device *parent, *self; 274 void *aux; 275 { 276 struct awi_pcmcia_softc *psc = (void *)self; 277 struct awi_softc *sc = &psc->sc_awi; 278 const struct awi_pcmcia_product *app; 279 struct pcmcia_attach_args *pa = aux; 280 struct pcmcia_config_entry *cfe; 281 bus_size_t memoff; 282 283 app = awi_pcmcia_lookup(pa); 284 if (app == NULL) 285 panic("awi_pcmcia_attach: impossible"); 286 287 psc->sc_pf = pa->pf; 288 289 for (cfe = SIMPLEQ_FIRST(&pa->pf->cfe_head); cfe != NULL; 290 cfe = SIMPLEQ_NEXT(cfe, cfe_list)) { 291 if (cfe->iftype != PCMCIA_IFTYPE_IO) 292 continue; 293 if (cfe->num_iospace < 1) 294 continue; 295 if (cfe->iospace[0].length < AM79C930_IO_SIZE) 296 continue; 297 298 if (awi_pcmcia_find(psc, pa, cfe) == 0) 299 break; 300 } 301 if (cfe == NULL) { 302 printf(": no suitable CIS info found\n"); 303 goto no_config_entry; 304 } 305 306 sc->sc_enabled = 1; 307 printf(": %s\n", app->app_name); 308 309 psc->sc_mem_window = -1; 310 if (pcmcia_mem_alloc(psc->sc_pf, AM79C930_MEM_SIZE, 311 &psc->sc_memh) != 0) { 312 printf("%s: unable to allocate memory space; using i/o only\n", 313 sc->sc_dev.dv_xname); 314 } else if (pcmcia_mem_map(psc->sc_pf, 315 PCMCIA_WIDTH_MEM8|PCMCIA_MEM_COMMON, AM79C930_MEM_BASE, 316 AM79C930_MEM_SIZE, &psc->sc_memh, &memoff, &psc->sc_mem_window)) { 317 printf("%s: unable to map memory space; using i/o only\n", 318 sc->sc_dev.dv_xname); 319 pcmcia_mem_free(psc->sc_pf, &psc->sc_memh); 320 } else { 321 sc->sc_chip.sc_memt = psc->sc_memh.memt; 322 sc->sc_chip.sc_memh = psc->sc_memh.memh; 323 am79c930_chip_init(&sc->sc_chip, 1); 324 } 325 326 sc->sc_enable = awi_pcmcia_enable; 327 sc->sc_disable = awi_pcmcia_disable; 328 329 /* establish the interrupt. */ 330 psc->sc_ih = pcmcia_intr_establish(psc->sc_pf, IPL_NET, awi_intr, sc); 331 if (psc->sc_ih == NULL) { 332 printf("%s: couldn't establish interrupt\n", 333 sc->sc_dev.dv_xname); 334 goto no_interrupt; 335 } 336 sc->sc_cansleep = 1; 337 338 if (awi_attach(sc) != 0) { 339 printf("%s: failed to attach controller\n", 340 sc->sc_dev.dv_xname); 341 goto attach_failed; 342 } 343 344 sc->sc_enabled = 0; 345 /* disable device and disestablish the interrupt */ 346 awi_pcmcia_disable(sc); 347 return; 348 349 attach_failed: 350 pcmcia_intr_disestablish(psc->sc_pf, psc->sc_ih); 351 352 no_interrupt: 353 /* Unmap our memory window and space */ 354 if (psc->sc_mem_window != -1) { 355 pcmcia_mem_unmap(psc->sc_pf, psc->sc_mem_window); 356 pcmcia_mem_free(psc->sc_pf, &psc->sc_memh); 357 } 358 359 /* Unmap our i/o window and space */ 360 pcmcia_io_unmap(psc->sc_pf, psc->sc_io_window); 361 pcmcia_io_free(psc->sc_pf, &psc->sc_pcioh); 362 363 /* Disable the function */ 364 pcmcia_function_disable(psc->sc_pf); 365 366 no_config_entry: 367 psc->sc_io_window = -1; 368 } 369 370 371 static int 372 awi_pcmcia_detach(self, flags) 373 struct device *self; 374 int flags; 375 { 376 struct awi_pcmcia_softc *psc = (struct awi_pcmcia_softc *)self; 377 int error; 378 379 if (psc->sc_io_window == -1) 380 /* Nothing to detach. */ 381 return (0); 382 383 error = awi_detach(&psc->sc_awi); 384 if (error != 0) 385 return (error); 386 387 /* Unmap our memory window and free memory space */ 388 if (psc->sc_mem_window != -1) { 389 pcmcia_mem_unmap(psc->sc_pf, psc->sc_mem_window); 390 pcmcia_mem_free(psc->sc_pf, &psc->sc_memh); 391 } 392 393 /* Unmap our i/o window. */ 394 pcmcia_io_unmap(psc->sc_pf, psc->sc_io_window); 395 396 /* Free our i/o space. */ 397 pcmcia_io_free(psc->sc_pf, &psc->sc_pcioh); 398 return (0); 399 } 400