summaryrefslogtreecommitdiff
path: root/sys/arch/i386
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/i386')
-rw-r--r--sys/arch/i386/pci/pci_intr_fixup.c79
1 files changed, 60 insertions, 19 deletions
diff --git a/sys/arch/i386/pci/pci_intr_fixup.c b/sys/arch/i386/pci/pci_intr_fixup.c
index 8dc53ad83da..bed4e666372 100644
--- a/sys/arch/i386/pci/pci_intr_fixup.c
+++ b/sys/arch/i386/pci/pci_intr_fixup.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pci_intr_fixup.c,v 1.14 2001/05/12 19:12:44 mickey Exp $ */
+/* $OpenBSD: pci_intr_fixup.c,v 1.15 2001/05/12 21:01:53 mickey Exp $ */
/* $NetBSD: pci_intr_fixup.c,v 1.10 2000/08/10 21:18:27 soda Exp $ */
/*
@@ -668,7 +668,6 @@ pci_intr_fixup(sc, pc, iot)
struct pcibios_pir_header *pirh = &pcibios_pir_header;
const struct pciintr_icu_table *piit = NULL;
pcitag_t icutag;
- pcireg_t icuid;
/*
* Attempt to initialize our PCI interrupt router. If
@@ -676,22 +675,33 @@ pci_intr_fixup(sc, pc, iot)
* specified by the PIR Table, and use the compat ID,
* if present. Otherwise, we have to look for the router
* ourselves (the PCI-ISA bridge).
+ *
+ * A number of buggy BIOS implementations leave the router
+ * entry as 000:00:0, which is typically not the correct
+ * device/function. If the router device address is set to
+ * this value, and the compatible router entry is undefined
+ * (zero is the correct value to indicate undefined), then we
+ * work on the basis it is most likely an error, and search
+ * the entire device-space of bus 0 (but obviously starting
+ * with 000:00:0, in case that really is the right one).
*/
- if (pirh->signature != 0) {
- icutag = pci_make_tag(pc, pirh->router_bus,
- PIR_DEVFUNC_DEVICE(pirh->router_devfunc),
- PIR_DEVFUNC_FUNCTION(pirh->router_devfunc));
- icuid = pirh->compat_router;
- if (icuid == 0 ||
- (piit = pciintr_icu_lookup(icuid)) == NULL) {
+ if (pirh->signature != 0 && (pirh->router_bus != 0 ||
+ pirh->router_devfunc != 0 || pirh->compat_router != 0)) {
+
+ if (pirh->compat_router == 0 ||
+ (piit = pciintr_icu_lookup(pirh->compat_router)) == NULL) {
/*
* No compat ID, or don't know the compat ID? Read
* it from the configuration header.
*/
- icuid = pci_conf_read(pc, icutag, PCI_ID_REG);
+ pirh->compat_router = pci_conf_read(pc,
+ pci_make_tag(pc, pirh->router_bus,
+ PIR_DEVFUNC_DEVICE(pirh->router_devfunc),
+ PIR_DEVFUNC_FUNCTION(pirh->router_devfunc)),
+ PCI_ID_REG);
}
if (piit == NULL)
- piit = pciintr_icu_lookup(icuid);
+ piit = pciintr_icu_lookup(pirh->compat_router);
} else {
int device, maxdevs = pci_bus_maxdevs(pc, 0);
@@ -700,6 +710,11 @@ pci_intr_fixup(sc, pc, iot)
* router.
*/
for (device = 0; device < maxdevs; device++) {
+ const struct pci_quirkdata *qd;
+ int function, nfuncs;
+ pcireg_t icuid;
+ pcireg_t bhlcr;
+
icutag = pci_make_tag(pc, 0, device, 0);
icuid = pci_conf_read(pc, icutag, PCI_ID_REG);
@@ -710,21 +725,47 @@ pci_intr_fixup(sc, pc, iot)
if (PCI_VENDOR(icuid) == 0)
continue;
- if ((piit = pciintr_icu_lookup(icuid))) {
- pirh->compat_router = icuid;
- pirh->router_bus = 0;
- pirh->router_devfunc =
- PIR_DEVFUNC_COMPOSE(device, 0);
- break;
+ qd = pci_lookup_quirkdata(PCI_VENDOR(icuid),
+ PCI_PRODUCT(icuid));
+
+ bhlcr = pci_conf_read(pc, icutag, PCI_BHLC_REG);
+ if (PCI_HDRTYPE_MULTIFN(bhlcr) || (qd != NULL &&
+ (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
+ nfuncs = 8;
+ else
+ nfuncs = 1;
+
+ for (function = 0; function < nfuncs; function++) {
+ icutag = pci_make_tag(pc, 0, device, function);
+ icuid = pci_conf_read(pc, icutag, PCI_ID_REG);
+
+ /* Invalid vendor ID value? */
+ if (PCI_VENDOR(icuid) == PCI_VENDOR_INVALID)
+ continue;
+ /* Not invalid, but we've done this ~forever. */
+ if (PCI_VENDOR(icuid) == 0)
+ continue;
+
+ if ((piit = pciintr_icu_lookup(icuid))) {
+ pirh->compat_router = icuid;
+ pirh->router_bus = 0;
+ pirh->router_devfunc =
+ PIR_DEVFUNC_COMPOSE(device, 0);
+ break;
+ }
}
+
+ if (piit != NULL)
+ break;
}
}
if (piit == NULL) {
printf("%s: no compatible PCI ICU found", sc->sc_dev.dv_xname);
- if (pirh->signature != 0 && icuid != 0)
+ if (pirh->signature != 0 && pirh->compat_router != 0)
printf(": ICU vendor 0x%04x product 0x%04x",
- PCI_VENDOR(icuid), PCI_PRODUCT(icuid));
+ PCI_VENDOR(pirh->compat_router),
+ PCI_PRODUCT(pirh->compat_router));
printf("\n");
if (!(pcibios_flags & PCIBIOS_INTR_GUESS)) {
if (pciintr_link_init(pc))