diff options
author | Brad Smith <brad@cvs.openbsd.org> | 2006-03-19 02:43:39 +0000 |
---|---|---|
committer | Brad Smith <brad@cvs.openbsd.org> | 2006-03-19 02:43:39 +0000 |
commit | 2de1e0f40803f7f0de26e365f0c02a0b4cc4d330 (patch) | |
tree | e187e02df86e3a0e9b6c58f465d0407f474a2150 /sys/dev | |
parent | 4cc27836a57fbda13281aa7986a92b1f641e3181 (diff) |
rev 1.86
make the "generic" PCI bus enumeration code the standard case which
gets used if nothing else is defined in MD headers,
introduce a "PCI_MACHDEP_ENUMERATE_BUS" CPP definition which can
be used by MD headers (just 1 port atm) to plug in special code
rev 1.62
* Implement a machine-dependent pci_enumerate_bus() for sparc64 which
uses OFW device nodes to enumerate the bus. When a PCI bus that is
behind a bridge is attached, pci_attach_hook() allocates a new PCI
chipset tag for the new bus and sets it's "curnode" to the OFW node
of the bridge. This is used as a starting point when enumerating
that bus. Root busses get the OFW node of the host bridge (psycho).
rev 1.59
Split the code that enumerates the PCI bus and that actually probes
for a device into two functions:
* pci_probe_device() actually probes/attaches the device specified
by the provide pcitag_t.
* pci_enumerate_bus() enumerates the bus, and calls pci_probe_device()
for each device on the bus. A pci_enumerate_bus_generic() is provided
which implements the old method of doing this: If something found at
dev0/func0, determine number of functions and probe each one.
From NetBSD
ok kettenis@
Tested on a good number of amd64/i386/macppc/sparc64 systems
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/pci/pci.c | 334 | ||||
-rw-r--r-- | sys/dev/pci/pcivar.h | 10 |
2 files changed, 181 insertions, 163 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 24477ece9cb..e4e7a0b403e 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pci.c,v 1.40 2006/03/11 22:08:07 brad Exp $ */ +/* $OpenBSD: pci.c,v 1.41 2006/03/19 02:43:38 brad Exp $ */ /* $NetBSD: pci.c,v 1.31 1997/06/06 23:48:04 thorpej Exp $ */ /* @@ -75,6 +75,13 @@ struct cfdriver pci_cd = { int pciprint(void *, const char *); int pcisubmatch(struct device *, void *, void *); +#ifdef PCI_MACHDEP_ENUMERATE_BUS +#define pci_enumerate_bus PCI_MACHDEP_ENUMERATE_BUS +#else +int pci_enumerate_bus(struct pci_softc *, + int (*)(struct pci_attach_args *), struct pci_attach_args *); +#endif + /* * Important note about PCI-ISA bridges: * @@ -125,7 +132,7 @@ pcimatch(parent, match, aux) * XXX check other (hardware?) indicators */ - return 1; + return (1); } void @@ -134,171 +141,25 @@ pciattach(parent, self, aux) void *aux; { struct pcibus_attach_args *pba = aux; - bus_space_tag_t iot, memt; - pci_chipset_tag_t pc; - int bus, device, maxndevs, function, nfunctions; struct pci_softc *sc = (struct pci_softc *)self; - struct pci_dev *pd; - struct device *dev; -#ifdef __PCI_BUS_DEVORDER - char devs[32]; - int i; -#endif -#ifdef __PCI_DEV_FUNCORDER - char funcs[8]; - int j; -#endif pci_attach_hook(parent, self, pba); - printf("\n"); - iot = pba->pba_iot; - memt = pba->pba_memt; - pc = pba->pba_pc; - bus = pba->pba_bus; - maxndevs = pci_bus_maxdevs(pc, bus); + printf("\n"); - sc->sc_pc = pba->pba_pc; LIST_INIT(&sc->sc_devs); sc->sc_powerhook = powerhook_establish(pcipower, sc); -#ifdef USER_PCICONF - sc->sc_bus = bus; -#endif - -#ifdef __PCI_BUS_DEVORDER - pci_bus_devorder(pc, bus, devs); - for (i = 0; (device = devs[i]) < 32 && device >= 0; i++) { -#else - for (device = 0; device < maxndevs; device++) { -#endif - pcitag_t tag; - pcireg_t id, class, intr; -#ifndef __sparc64__ - pcireg_t bhlcr; -#endif - struct pci_attach_args pa; - int pin; - - tag = pci_make_tag(pc, bus, device, 0); - id = pci_conf_read(pc, tag, PCI_ID_REG); - -#ifdef __sparc64__ - pci_dev_funcorder(pc, bus, device, funcs); - nfunctions = 8; - - /* Invalid vendor ID value or 0 (see below for zero) - * ... of course, if the pci_dev_funcorder found - * functions other than zero, we probably want - * to attach them. - */ - if (PCI_VENDOR(id) == PCI_VENDOR_INVALID || PCI_VENDOR(id) == 0) - if (funcs[0] < 0) - continue; - - for (j = 0; (function = funcs[j]) < nfunctions && - function >= 0; j++) { -#else - /* Invalid vendor ID value? */ - if (PCI_VENDOR(id) == PCI_VENDOR_INVALID) - continue; - /* XXX Not invalid, but we've done this ~forever. */ - if (PCI_VENDOR(id) == 0) - continue; - - bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG); - nfunctions = PCI_HDRTYPE_MULTIFN(bhlcr) ? 8 : 1; - -#ifdef __PCI_DEV_FUNCORDER - pci_dev_funcorder(pc, bus, device, funcs); - for (j = 0; (function = funcs[j]) < nfunctions && - function >= 0; j++) { -#else - for (function = 0; function < nfunctions; function++) { -#endif -#endif - tag = pci_make_tag(pc, bus, device, function); - id = pci_conf_read(pc, tag, PCI_ID_REG); - - /* Invalid vendor ID value? */ - if (PCI_VENDOR(id) == PCI_VENDOR_INVALID) - continue; - /* XXX Not invalid, but we've done this ~forever. */ - if (PCI_VENDOR(id) == 0) - continue; - - class = pci_conf_read(pc, tag, PCI_CLASS_REG); - intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG); - - pa.pa_iot = iot; - pa.pa_memt = memt; - pa.pa_dmat = pba->pba_dmat; - pa.pa_pc = pc; - pa.pa_device = device; - pa.pa_function = function; - pa.pa_bus = bus; - pa.pa_tag = tag; - pa.pa_id = id; - pa.pa_class = class; - - /* This is a simplification of the NetBSD code. - We don't support turning off I/O or memory - on broken hardware. <csapuntz@stanford.edu> */ - pa.pa_flags = PCI_FLAGS_IO_ENABLED | PCI_FLAGS_MEM_ENABLED; -#ifdef __i386__ - /* - * on i386 we really need to know the device tag - * and not the pci bridge tag, in intr_map - * to be able to program the device and the - * pci interrupt router. - */ - pa.pa_intrtag = tag; - pa.pa_intrswiz = 0; -#else - if (bus == 0) { - pa.pa_intrswiz = 0; - pa.pa_intrtag = tag; - } else { - pa.pa_intrswiz = pba->pba_intrswiz + device; - pa.pa_intrtag = pba->pba_intrtag; - } -#endif - pin = PCI_INTERRUPT_PIN(intr); - pa.pa_rawintrpin = pin; - if (pin == PCI_INTERRUPT_PIN_NONE) { - /* no interrupt */ - pa.pa_intrpin = 0; - } else { - /* - * swizzle it based on the number of - * busses we're behind and our device - * number. - */ - pa.pa_intrpin = /* XXX */ - ((pin + pa.pa_intrswiz - 1) % 4) + 1; - } - pa.pa_intrline = PCI_INTERRUPT_LINE(intr); - - if ((dev = config_found_sm(self, &pa, pciprint, - pcisubmatch))) { - pcireg_t reg; - - /* skip header type != 0 */ - reg = pci_conf_read(pc, tag, PCI_BHLC_REG); - if (PCI_HDRTYPE_TYPE(reg) != 0) - continue; - if (pci_get_capability(pc, tag, - PCI_CAP_PWRMGMT, NULL, NULL) == 0) - continue; - if (!(pd = malloc(sizeof *pd, M_DEVBUF, - M_NOWAIT))) - continue; - pd->pd_tag = tag; - pd->pd_dev = dev; - LIST_INSERT_HEAD(&sc->sc_devs, pd, pd_next); - } - } - } + sc->sc_iot = pba->pba_iot; + sc->sc_memt = pba->pba_memt; + sc->sc_dmat = pba->pba_dmat; + sc->sc_pc = pba->pba_pc; + sc->sc_bus = pba->pba_bus; + sc->sc_bridgetag = pba->pba_bridgetag; + sc->sc_maxndevs = pci_bus_maxdevs(pba->pba_pc, pba->pba_bus); + sc->sc_intrswiz = pba->pba_intrswiz; + sc->sc_intrtag = pba->pba_intrtag; + pci_enumerate_bus(sc, NULL, NULL); } /* save and restore the pci config space */ @@ -373,10 +234,10 @@ pcisubmatch(parent, match, aux) if (cf->pcicf_dev != PCI_UNK_DEV && cf->pcicf_dev != pa->pa_device) - return 0; + return (0); if (cf->pcicf_function != PCI_UNK_FUNCTION && cf->pcicf_function != pa->pa_function) - return 0; + return (0); success = (*cf->cf_attach->ca_match)(parent, match, aux); @@ -399,6 +260,109 @@ pcisubmatch(parent, match, aux) } int +pci_probe_device(struct pci_softc *sc, pcitag_t tag, + int (*match)(struct pci_attach_args *), struct pci_attach_args *pap) +{ + pci_chipset_tag_t pc = sc->sc_pc; + struct pci_attach_args pa; + struct pci_dev *pd; + struct device *dev; + pcireg_t id, csr, class, intr, bhlcr; + int ret, pin, bus, device, function; + + pci_decompose_tag(pc, tag, &bus, &device, &function); + + id = pci_conf_read(pc, tag, PCI_ID_REG); + csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); + class = pci_conf_read(pc, tag, PCI_CLASS_REG); + intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG); + bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG); + + /* Invalid vendor ID value? */ + if (PCI_VENDOR(id) == PCI_VENDOR_INVALID) + return (0); + /* XXX Not invalid, but we've done this ~forever. */ + if (PCI_VENDOR(id) == 0) + return (0); + + pa.pa_iot = sc->sc_iot; + pa.pa_memt = sc->sc_memt; + pa.pa_dmat = sc->sc_dmat; + pa.pa_pc = pc; + pa.pa_bus = bus; + pa.pa_device = device; + pa.pa_function = function; + pa.pa_tag = tag; + pa.pa_id = id; + pa.pa_class = class; + + /* This is a simplification of the NetBSD code. + We don't support turning off I/O or memory + on broken hardware. <csapuntz@stanford.edu> */ + pa.pa_flags = PCI_FLAGS_IO_ENABLED | PCI_FLAGS_MEM_ENABLED; + +#ifdef __i386__ + /* + * on i386 we really need to know the device tag + * and not the pci bridge tag, in intr_map + * to be able to program the device and the + * pci interrupt router. + */ + pa.pa_intrtag = tag; + pa.pa_intrswiz = 0; +#else + if (sc->sc_bridgetag == NULL) { + pa.pa_intrswiz = 0; + pa.pa_intrtag = tag; + } else { + pa.pa_intrswiz = sc->sc_intrswiz + device; + pa.pa_intrtag = sc->sc_intrtag; + } +#endif + pin = PCI_INTERRUPT_PIN(intr); + pa.pa_rawintrpin = pin; + if (pin == PCI_INTERRUPT_PIN_NONE) { + /* no interrupt */ + pa.pa_intrpin = 0; + } else { + /* + * swizzle it based on the number of busses we're + * behind and our device number. + */ + pa.pa_intrpin = /* XXX */ + ((pin + pa.pa_intrswiz - 1) % 4) + 1; + } + pa.pa_intrline = PCI_INTERRUPT_LINE(intr); + + if (match != NULL) { + ret = (*match)(&pa); + if (ret != 0 && pap != NULL) + *pap = pa; + } else { + if ((dev = config_found_sm(&sc->sc_dev, &pa, pciprint, + pcisubmatch))) { + pcireg_t reg; + + /* skip header type != 0 */ + reg = pci_conf_read(pc, tag, PCI_BHLC_REG); + if (PCI_HDRTYPE_TYPE(reg) != 0) + return(0); + if (pci_get_capability(pc, tag, + PCI_CAP_PWRMGMT, NULL, NULL) == 0) + return(0); + if (!(pd = malloc(sizeof *pd, M_DEVBUF, + M_NOWAIT))) + return(0); + pd->pd_tag = tag; + pd->pd_dev = dev; + LIST_INSERT_HEAD(&sc->sc_devs, pd, pd_next); + } + } + + return (ret); +} + +int pci_get_capability(pc, tag, capid, offset, value) pci_chipset_tag_t pc; pcitag_t tag; @@ -446,6 +410,54 @@ pci_get_capability(pc, tag, capid, offset, value) return (0); } +#ifndef PCI_MACHDEP_ENUMERATE_BUS +/* + * Generic PCI bus enumeration routine. Used unless machine-dependent + * code needs to provide something else. + */ +int +pci_enumerate_bus(struct pci_softc *sc, + int (*match)(struct pci_attach_args *), struct pci_attach_args *pap) +{ + pci_chipset_tag_t pc = sc->sc_pc; + int device, function, nfunctions, ret; + const struct pci_quirkdata *qd; + pcireg_t id, bhlcr; + pcitag_t tag; + + for (device = 0; device < sc->sc_maxndevs; device++) { + tag = pci_make_tag(pc, sc->sc_bus, device, 0); + id = pci_conf_read(pc, tag, PCI_ID_REG); + + /* Invalid vendor ID value? */ + if (PCI_VENDOR(id) == PCI_VENDOR_INVALID) + continue; + /* XXX Not invalid, but we've done this ~forever. */ + if (PCI_VENDOR(id) == 0) + continue; + + qd = pci_lookup_quirkdata(PCI_VENDOR(id), PCI_PRODUCT(id)); + + bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG); + if (PCI_HDRTYPE_MULTIFN(bhlcr) || + (qd != NULL && + (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0)) + nfunctions = 8; + else + nfunctions = 1; + + for (function = 0; function < nfunctions; function++) { + tag = pci_make_tag(pc, sc->sc_bus, device, function); + ret = pci_probe_device(sc, tag, match, pap); + if (match != NULL && ret != 0) + return (ret); + } + } + + return (0); +} +#endif /* PCI_MACHDEP_ENUMERATE_BUS */ + int pci_matchbyid(struct pci_attach_args *pa, const struct pci_matchid *ids, int nent) diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index a3546de0868..43b91f938ec 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pcivar.h,v 1.44 2006/03/13 20:10:49 brad Exp $ */ +/* $OpenBSD: pcivar.h,v 1.45 2006/03/19 02:43:38 brad Exp $ */ /* $NetBSD: pcivar.h,v 1.23 1997/06/06 23:48:05 thorpej Exp $ */ /* @@ -151,11 +151,15 @@ struct pci_quirkdata { struct pci_softc { struct device sc_dev; + bus_space_tag_t sc_iot, sc_memt; + bus_dma_tag_t sc_dmat; pci_chipset_tag_t sc_pc; void *sc_powerhook; LIST_HEAD(, pci_dev) sc_devs; - int sc_bus; + int sc_bus, sc_maxndevs; pcitag_t *sc_bridgetag; + u_int sc_intrswiz; + pcitag_t sc_intrtag; }; /* @@ -204,6 +208,8 @@ int pci_matchbyid(struct pci_attach_args *, const struct pci_matchid *, int); * Helper functions for autoconfiguration. */ const char *pci_findvendor(pcireg_t); +int pci_probe_device(struct pci_softc *, pcitag_t tag, + int (*)(struct pci_attach_args *), struct pci_attach_args *); void pci_devinfo(pcireg_t, pcireg_t, int, char *, size_t); const struct pci_quirkdata * pci_lookup_quirkdata(pci_vendor_id_t, pci_product_id_t); |