summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2002-09-12 03:48:32 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2002-09-12 03:48:32 +0000
commitda6f501ea51f976a2257c7085bae59af0cdc30c3 (patch)
treeccd6e35e5599e87c90ff6a26b28c28b51d7a39f5 /sys/dev
parent0fc86a2620db32369f8ac96d9432e18dfcc2b446 (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.h11
-rw-r--r--sys/dev/pci/if_wi_pci.c370
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);
}