diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2002-09-12 03:48:32 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2002-09-12 03:48:32 +0000 |
commit | da6f501ea51f976a2257c7085bae59af0cdc30c3 (patch) | |
tree | ccd6e35e5599e87c90ff6a26b28c28b51d7a39f5 /sys/dev | |
parent | 0fc86a2620db32369f8ac96d9432e18dfcc2b446 (diff) |
o break up wi_pci_attach() into device-specific parts for better readability
o kludge around a problem with Netgear MA301 hanging when booted w/o a card.
o better card detection w/ PLX adapters
o correct the info in some comments
mickey@ OK
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ic/if_wireg.h | 11 | ||||
-rw-r--r-- | sys/dev/pci/if_wi_pci.c | 370 |
2 files changed, 215 insertions, 166 deletions
diff --git a/sys/dev/ic/if_wireg.h b/sys/dev/ic/if_wireg.h index a2ecad45315..792ac998097 100644 --- a/sys/dev/ic/if_wireg.h +++ b/sys/dev/ic/if_wireg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_wireg.h,v 1.27 2002/09/10 08:21:35 fgsch Exp $ */ +/* $OpenBSD: if_wireg.h,v 1.28 2002/09/12 03:48:31 millert Exp $ */ /* * Copyright (c) 1997, 1998, 1999 @@ -291,19 +291,20 @@ #define WI_AUX_DATA 0x3E #define WI_COR_OFFSET 0x40 /* COR attribute offset of card */ +#define WI_COR_IOMODE 0x41 /* Enable i/o mode with level irqs */ #define WI_PLX_LOCALRES 0x14 /* PLX chip's local registers */ #define WI_PLX_MEMRES 0x18 /* Prism attribute memory (PLX) */ #define WI_PLX_IORES 0x1C /* Prism I/O space (PLX) */ #define WI_PLX_INTCSR 0x4C /* PLX Interrupt CSR */ -#define WI_PLX_INTEN 0x40 /* Interrupt Enable bit */ -#define WI_PLX_COR_VALUE 0x41 /* Enable with irq in level trigger */ +#define WI_PLX_INTEN 0x40 /* PCI Interrupt Enable bit */ +#define WI_PLX_LINT1STAT 0x04 /* Local interrupt 1 status bit */ #define WI_PLX_COR_OFFSET 0x3E0 /* COR attribute offset of card */ +#define WI_DRVR_MAGIC 0x4A2D /* Magic number for card detection */ + #define WI_TMD_LOCALRES 0x14 /* TMD chip's local registers */ #define WI_TMD_IORES 0x18 /* Prism I/O space (TMD) */ -#define WI_TMD_COR_OFFSET 0x00 /* COR attribute offset of Prism2 */ -#define WI_TMD_COR_VALUE 0x45 /* * PCI Host Interface Registers (HFA3842 Specific) diff --git a/sys/dev/pci/if_wi_pci.c b/sys/dev/pci/if_wi_pci.c index bb1a428b07a..f733a2bd973 100644 --- a/sys/dev/pci/if_wi_pci.c +++ b/sys/dev/pci/if_wi_pci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_wi_pci.c,v 1.29 2002/07/29 19:24:24 millert Exp $ */ +/* $OpenBSD: if_wi_pci.c,v 1.30 2002/09/12 03:48:31 millert Exp $ */ /* * Copyright (c) 2001, 2002 Todd C. Miller <Todd.Miller@courtesan.com> @@ -52,11 +52,9 @@ * * This driver also supports the TMD7160 dumb bridge chip which is used * on some versions of the NDC/Sohoware NCP130. The TMD7160 provides - * two PCI I/O registers. The first, at 0x14, is for the TMD7160 - * chip itself. The second, at 0x18, is for the Prism2 chip. + * two PCI I/O registers. The first, at 0x14, maps to the Prism2 COR. + * The second, at 0x18, is for the Prism2 chip itself. * The datasheet for the TMD7160 does not seem to be publicly available. - * The magic for initializing the chip was gleened from NDC's version of - * the Linux wlan driver. */ #include <sys/param.h> @@ -86,11 +84,6 @@ #include <dev/ic/if_wi_ieee.h> #include <dev/ic/if_wivar.h> -/* Values for pp_type */ -#define WI_PCI_PRISM 0x01 /* Intersil Mini-PCI */ -#define WI_PCI_PLX 0x02 /* PLX 905x dumb bridge */ -#define WI_PCI_TMD 0x03 /* TMD 7160 dumb bridge */ - /* For printing CIS of the actual PCMCIA card */ #define CIS_MFG_NAME_OFFSET 0x16 #define CIS_INFO_SIZE 256 @@ -98,7 +91,11 @@ const struct wi_pci_product *wi_pci_lookup(struct pci_attach_args *pa); int wi_pci_match(struct device *, void *, void *); void wi_pci_attach(struct device *, struct device *, void *); -int wi_pci_handle_cis(struct wi_softc *); +int wi_pci_plx_attach(struct pci_attach_args *pa, struct wi_softc *sc); +int wi_pci_tmd_attach(struct pci_attach_args *pa, struct wi_softc *sc); +int wi_pci_native_attach(struct pci_attach_args *pa, struct wi_softc *sc); +int wi_pci_common_attach(struct pci_attach_args *pa, struct wi_softc *sc); +void wi_pci_plx_print_cis(struct wi_softc *); struct cfattach wi_pci_ca = { sizeof (struct wi_softc), wi_pci_match, wi_pci_attach @@ -107,24 +104,23 @@ struct cfattach wi_pci_ca = { static const struct wi_pci_product { pci_vendor_id_t pp_vendor; pci_product_id_t pp_product; - int pp_type; + int (*pp_attach)(struct pci_attach_args *pa, struct wi_softc *sc); } wi_pci_products[] = { - { PCI_VENDOR_GLOBALSUN, PCI_PRODUCT_GLOBALSUN_GL24110P, WI_PCI_PLX }, - { PCI_VENDOR_GLOBALSUN, PCI_PRODUCT_GLOBALSUN_GL24110P02, WI_PCI_PLX }, - { PCI_VENDOR_EUMITCOM, PCI_PRODUCT_EUMITCOM_WL11000P, WI_PCI_PLX }, - { PCI_VENDOR_USR2, PCI_PRODUCT_USR2_WL11000P, WI_PCI_PLX }, - { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CRWE777A, WI_PCI_PLX }, - { PCI_VENDOR_NETGEAR, PCI_PRODUCT_NETGEAR_MA301, WI_PCI_PLX }, - { PCI_VENDOR_EFFICIENTNETS, PCI_PRODUCT_EFFICIENTNETS_SS1023, WI_PCI_PLX }, - { PCI_VENDOR_NDC, PCI_PRODUCT_NDC_NCP130, WI_PCI_PLX }, - { PCI_VENDOR_NDC, PCI_PRODUCT_NDC_NCP130A2, WI_PCI_TMD }, - { PCI_VENDOR_INTERSIL, PCI_PRODUCT_INTERSIL_MINI_PCI_WLAN, WI_PCI_PRISM }, - { 0, 0 } + { PCI_VENDOR_GLOBALSUN, PCI_PRODUCT_GLOBALSUN_GL24110P, wi_pci_plx_attach }, + { PCI_VENDOR_GLOBALSUN, PCI_PRODUCT_GLOBALSUN_GL24110P02, wi_pci_plx_attach }, + { PCI_VENDOR_EUMITCOM, PCI_PRODUCT_EUMITCOM_WL11000P, wi_pci_plx_attach }, + { PCI_VENDOR_USR2, PCI_PRODUCT_USR2_WL11000P, wi_pci_plx_attach }, + { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CRWE777A, wi_pci_plx_attach }, + { PCI_VENDOR_NETGEAR, PCI_PRODUCT_NETGEAR_MA301, wi_pci_plx_attach }, + { PCI_VENDOR_EFFICIENTNETS, PCI_PRODUCT_EFFICIENTNETS_SS1023, wi_pci_plx_attach }, + { PCI_VENDOR_NDC, PCI_PRODUCT_NDC_NCP130, wi_pci_plx_attach }, + { PCI_VENDOR_NDC, PCI_PRODUCT_NDC_NCP130A2, wi_pci_tmd_attach }, + { PCI_VENDOR_INTERSIL, PCI_PRODUCT_INTERSIL_MINI_PCI_WLAN, wi_pci_native_attach }, + { 0, 0, 0 } }; const struct wi_pci_product * -wi_pci_lookup(pa) - struct pci_attach_args *pa; +wi_pci_lookup(struct pci_attach_args *pa) { const struct wi_pci_product *pp; @@ -138,102 +134,213 @@ wi_pci_lookup(pa) } int -wi_pci_match(parent, match, aux) - struct device *parent; - void *match; - void *aux; +wi_pci_match(struct device *parent, void *match, void *aux) { return (wi_pci_lookup(aux) != NULL); } void -wi_pci_attach(parent, self, aux) - struct device *parent; - struct device *self; - void *aux; +wi_pci_attach(struct device *parent, struct device *self, void *aux) { struct wi_softc *sc = (struct wi_softc *)self; struct pci_attach_args *pa = aux; const struct wi_pci_product *pp; - pci_intr_handle_t ih; + + pp = wi_pci_lookup(pa); + if (pp->pp_attach(pa, sc) != 0) + return; + wi_attach(sc); +} + +int +wi_pci_plx_attach(struct pci_attach_args *pa, struct wi_softc *sc) +{ bus_space_handle_t localh, ioh, memh; bus_space_tag_t localt; bus_space_tag_t iot = pa->pa_iot; bus_space_tag_t memt = pa->pa_memt; bus_addr_t localbase; bus_size_t localsize; - pci_chipset_tag_t pc = pa->pa_pc; - u_int32_t command; - pcireg_t csr; - const char *intrstr; + u_int32_t intcsr; - pp = wi_pci_lookup(pa); - /* Map memory and I/O registers. */ - switch (pp->pp_type) { - case WI_PCI_PLX: - if (pci_mapreg_map(pa, WI_PLX_MEMRES, PCI_MAPREG_TYPE_MEM, 0, - &memt, &memh, NULL, NULL, 0) != 0) { - printf(": can't map mem space\n"); - return; - } - sc->wi_ltag = memt; - sc->wi_lhandle = memh; - if (pci_mapreg_map(pa, WI_PLX_IORES, - PCI_MAPREG_TYPE_IO, 0, &iot, &ioh, NULL, NULL, 0) != 0) { - printf(": can't map I/O space\n"); - return; + if (pci_mapreg_map(pa, WI_PLX_MEMRES, PCI_MAPREG_TYPE_MEM, 0, + &memt, &memh, NULL, NULL, 0) != 0) { + printf(": can't map mem space\n"); + return (ENXIO); + } + sc->wi_ltag = memt; + sc->wi_lhandle = memh; + + if (pci_mapreg_map(pa, WI_PLX_IORES, + PCI_MAPREG_TYPE_IO, 0, &iot, &ioh, NULL, NULL, 0) != 0) { + printf(": can't map I/O space\n"); + return (ENXIO); + } + sc->wi_btag = iot; + sc->wi_bhandle = ioh; + + /* + * Some cards, such as the PLX version of the NDC NCP130, + * don't have the PLX local registers mapped. In general + * this is OK since on those cards the serial EEPROM has + * already set things up for us. + * As such, we don't consider an error here to be fatal. + */ + localsize = 0; + if (pci_mapreg_type(pa->pa_pc, pa->pa_tag, WI_PLX_LOCALRES) + == PCI_MAPREG_TYPE_IO) { + if (pci_io_find(pa->pa_pc, pa->pa_tag, + WI_PLX_LOCALRES, &localbase, &localsize) != 0) + printf(": can't find PLX I/O space\n"); + if (localsize != 0) { + if (bus_space_map(pa->pa_iot, localbase, + localsize, 0, &localh) != 0) { + printf(": can't map PLX I/O space\n"); + localsize = 0; + } else + localt = pa->pa_iot; } + } + + if (wi_pci_common_attach(pa, sc) != 0) + return (ENXIO); + + if (localsize != 0) { + intcsr = bus_space_read_4(localt, localh, + WI_PLX_INTCSR); + /* - * Some cards, such as the PLX version of the NDC NCP130 - * don't have the PLX local registers mapped. In general - * this is OK since those card enable PLX interrupts for us. - * As such, we don't consider an error here to be fatal. + * The Netgear MA301 has local interrupt 1 active + * when there is no card in the adapter. We bail + * early in this case since our attempt to check + * for the presence of a card later will hang the + * MA301. */ - localsize = 0; - if (pci_mapreg_type(pa->pa_pc, pa->pa_tag, WI_PLX_LOCALRES) - == PCI_MAPREG_TYPE_IO) { - if (pci_io_find(pa->pa_pc, pa->pa_tag, - WI_PLX_LOCALRES, &localbase, &localsize) != 0) - printf(": can't find PLX I/O space\n"); - if (localsize != 0) { - if (bus_space_map(pa->pa_iot, localbase, - localsize, 0, &localh) != 0) { - printf(": can't map PLX I/O space\n"); - localsize = 0; - } else - localt = pa->pa_iot; - } - } - break; - case WI_PCI_PRISM: - if (pci_mapreg_map(pa, WI_PCI_CBMA, PCI_MAPREG_TYPE_MEM, - 0, &iot, &ioh, NULL, NULL, 0) != 0) { - printf(": can't map mem space\n"); - return; + if (intcsr & WI_PLX_LINT1STAT) { + printf("\n%s: no PCMCIA card detected in bridge card\n", + WI_PRT_ARG(sc)); + return (ENXIO); } - sc->sc_pci = 1; - sc->wi_ltag = iot; - sc->wi_lhandle = ioh; - break; - case WI_PCI_TMD: - if (pci_mapreg_map(pa, WI_TMD_LOCALRES, PCI_MAPREG_TYPE_IO, - 0, &localt, &localh, NULL, &localsize, 0) != 0) { - printf(": can't map TMD I/O space\n"); - return; + + /* + * Enable PCI interrupts on the PLX chip if they are + * not already enabled. On most adapters the serial + * EEPROM has done this for us but some (such as + * the Netgear MA301) do not. + */ + if (!(intcsr & WI_PLX_INTEN)) { + intcsr |= WI_PLX_INTEN; + bus_space_write_4(localt, localh, WI_PLX_INTCSR, + intcsr); } - sc->wi_ltag = localt; - sc->wi_lhandle = localh; - if (pci_mapreg_map(pa, WI_TMD_IORES, PCI_MAPREG_TYPE_IO, - 0, &iot, &ioh, NULL, NULL, 0) != 0) { - printf(": can't map I/O space\n"); - return; + } + + /* + * Enable I/O mode and level interrupts on the PCMCIA card. + * The PCMCIA card's COR is the first byte after the CIS. + */ + bus_space_write_1(memt, memh, WI_PLX_COR_OFFSET, WI_COR_IOMODE); + sc->wi_cor_offset = WI_PLX_COR_OFFSET; + + if (localsize != 0) { + /* + * Test the presence of a wi(4) card by writing + * a magic number to the first software support + * register and then reading it back. + */ + CSR_WRITE_2(sc, WI_SW0, WI_DRVR_MAGIC); + DELAY(1000); + if (CSR_READ_2(sc, WI_SW0) != WI_DRVR_MAGIC) { + printf("\n%s: no PCMCIA card detected in bridge card\n", + WI_PRT_ARG(sc)); + return (ENXIO); } - break; + + /* Unmap registers we no longer need access to. */ + bus_space_unmap(localt, localh, localsize); + + /* Print PCMCIA card's CIS strings. */ + wi_pci_plx_print_cis(sc); } + return (0); +} + +int +wi_pci_tmd_attach(struct pci_attach_args *pa, struct wi_softc *sc) +{ + bus_space_handle_t localh, ioh; + bus_space_tag_t localt; + bus_space_tag_t iot = pa->pa_iot; + bus_size_t localsize; + + if (pci_mapreg_map(pa, WI_TMD_LOCALRES, PCI_MAPREG_TYPE_IO, + 0, &localt, &localh, NULL, &localsize, 0) != 0) { + printf(": can't map TMD I/O space\n"); + return (ENXIO); + } + sc->wi_ltag = localt; + sc->wi_lhandle = localh; + + if (pci_mapreg_map(pa, WI_TMD_IORES, PCI_MAPREG_TYPE_IO, + 0, &iot, &ioh, NULL, NULL, 0) != 0) { + printf(": can't map I/O space\n"); + return (ENXIO); + } sc->wi_btag = iot; sc->wi_bhandle = ioh; + if (wi_pci_common_attach(pa, sc) != 0) + return (ENXIO); + + /* + * Enable I/O mode and level interrupts on the embedded PCMCIA + * card. The PCMCIA card's COR is the first byte of BAR 0. + */ + bus_space_write_1(localt, localh, 0, WI_COR_IOMODE); + sc->wi_cor_offset = 0; + + return (0); +} + +int +wi_pci_native_attach(struct pci_attach_args *pa, struct wi_softc *sc) +{ + bus_space_handle_t ioh; + bus_space_tag_t iot = pa->pa_iot; + + if (pci_mapreg_map(pa, WI_PCI_CBMA, PCI_MAPREG_TYPE_MEM, + 0, &iot, &ioh, NULL, NULL, 0) != 0) { + printf(": can't map mem space\n"); + return (ENXIO); + } + sc->wi_ltag = iot; + sc->wi_lhandle = ioh; + sc->wi_btag = iot; + sc->wi_bhandle = ioh; + sc->sc_pci = 1; + + if (wi_pci_common_attach(pa, sc) != 0) + return (ENXIO); + + /* Do a soft reset of the HFA3842 MAC core */ + bus_space_write_2(iot, ioh, WI_PCI_COR_OFFSET, WI_COR_SOFT_RESET); + DELAY(100*1000); /* 100 m sec */ + bus_space_write_2(iot, ioh, WI_PCI_COR_OFFSET, WI_COR_CLEAR); + DELAY(100*1000); /* 100 m sec */ + sc->wi_cor_offset = WI_PCI_COR_OFFSET; + + return (0); +} + +int +wi_pci_common_attach(struct pci_attach_args *pa, struct wi_softc *sc) +{ + pci_intr_handle_t ih; + pci_chipset_tag_t pc = pa->pa_pc; + pcireg_t csr; + const char *intrstr; + /* Enable the card. */ csr = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, @@ -246,7 +353,7 @@ wi_pci_attach(parent, self, aux) /* Map and establish the interrupt. */ if (pci_intr_map(pa, &ih)) { printf(": couldn't map interrupt\n"); - return; + return (ENXIO); } intrstr = pci_intr_string(pc, ih); sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, wi_intr, sc, @@ -256,69 +363,15 @@ wi_pci_attach(parent, self, aux) if (intrstr != NULL) printf(" at %s", intrstr); printf("\n"); - return; + return (ENXIO); } printf(": %s", intrstr); - switch (pp->pp_type) { - case WI_PCI_PLX: - /* - * Check that there really is a PCMCIA card inserted and - * print its CIS strings. - */ - if (localsize != 0 && wi_pci_handle_cis(sc) != 0) { - bus_space_unmap(localt, localh, localsize); - return; - } - - /* - * Tell the PLX chip to enable interrupts. In most cases - * the serial EEPROM has done this for us but some cards - * appear not to. - * Note that some PLX-based cards lack this I/O space. - */ - if (localsize != 0) { - command = bus_space_read_4(localt, localh, - WI_PLX_INTCSR); - command |= WI_PLX_INTEN; - bus_space_write_4(localt, localh, WI_PLX_INTCSR, - command); - } - - /* - * Setup the PLX chip for level interrupts and config index 1 - */ - bus_space_write_1(memt, memh, WI_PLX_COR_OFFSET, - WI_PLX_COR_VALUE); - sc->wi_cor_offset = WI_PLX_COR_OFFSET; - - /* Unmap registers we no longer need access to. */ - if (localsize != 0) - bus_space_unmap(localt, localh, localsize); - break; - case WI_PCI_PRISM: - bus_space_write_2(iot, ioh, WI_PCI_COR_OFFSET, - WI_COR_SOFT_RESET); - DELAY(100*1000); /* 100 m sec */ - bus_space_write_2(iot, ioh, WI_PCI_COR_OFFSET, WI_COR_CLEAR); - DELAY(100*1000); /* 100 m sec */ - sc->wi_cor_offset = WI_PCI_COR_OFFSET; - break; - case WI_PCI_TMD: - bus_space_write_1(localt, localh, WI_TMD_COR_OFFSET, - WI_TMD_COR_VALUE); - DELAY(1000); - if (bus_space_read_1(localt, localh, 0) != WI_TMD_COR_VALUE) - printf(": unable to initialize TMD7160 "); - sc->wi_cor_offset = WI_TMD_COR_OFFSET; - break; - } - wi_attach(sc); + return (0); } -int -wi_pci_handle_cis(sc) - struct wi_softc *sc; +void +wi_pci_plx_print_cis(struct wi_softc *sc) { int i, stringno; char cisbuf[CIS_INFO_SIZE]; @@ -328,14 +381,11 @@ wi_pci_handle_cis(sc) 0x01, 0x03, 0x00, 0x00, 0xff, 0x17, 0x04, 0x67 }; - /* Make sure there really is a card there. */ + /* Make sure the CIS data is valid. */ for (i = 0; i < 8; i++) { value = bus_space_read_1(sc->wi_ltag, sc->wi_lhandle, i * 2); - if (value != cis_magic[i]) { - printf("\n%s: no PCMCIA card detected in bridge card\n", - WI_PRT_ARG(sc)); - return (ENODEV); - } + if (value != cis_magic[i]) + return; } cis_strings[0] = cisbuf; @@ -349,6 +399,4 @@ wi_pci_handle_cis(sc) cisbuf[CIS_INFO_SIZE - 1] = '\0'; printf("\n%s: \"%s, %s, %s\"", WI_PRT_ARG(sc), cis_strings[0], cis_strings[1], cis_strings[2]); - - return (0); } |