summaryrefslogtreecommitdiff
path: root/sys/arch/i386/pci
diff options
context:
space:
mode:
authorMichael Shalayeff <mickey@cvs.openbsd.org>2000-08-08 19:12:49 +0000
committerMichael Shalayeff <mickey@cvs.openbsd.org>2000-08-08 19:12:49 +0000
commit3bcae5645d7e92bd659dc69f655800a6b2a3c2bd (patch)
treeac4922ef747fad22861ca84e1d7981d5dbca4fc3 /sys/arch/i386/pci
parent8ff180700f66e734dd1931b3ee8172a664df89f3 (diff)
sync w/ netbsd almost -current code (;
fixes quite a few problems. adds new option PCIBIOS_ADDR_FIXUP. numerous extra fixes by me, also pointed out by other ppl, such as avoid panics, ifdef on pciverbose, not diagnostic, some other smaller knits; some have been forwarded back to netbsd developers, some will go soon too.
Diffstat (limited to 'sys/arch/i386/pci')
-rw-r--r--sys/arch/i386/pci/opti82c700.c81
-rw-r--r--sys/arch/i386/pci/opti82c700reg.h9
-rw-r--r--sys/arch/i386/pci/pci_addr_fixup.c356
-rw-r--r--sys/arch/i386/pci/pci_addr_fixup.h40
-rw-r--r--sys/arch/i386/pci/pci_bus_fixup.c21
-rw-r--r--sys/arch/i386/pci/pci_intr_fixup.c530
-rw-r--r--sys/arch/i386/pci/pci_machdep.h9
-rw-r--r--sys/arch/i386/pci/pcibios.c76
-rw-r--r--sys/arch/i386/pci/pcibios.h31
-rw-r--r--sys/arch/i386/pci/piix.c53
10 files changed, 925 insertions, 281 deletions
diff --git a/sys/arch/i386/pci/opti82c700.c b/sys/arch/i386/pci/opti82c700.c
index 73b7b9b588a..ff22439691c 100644
--- a/sys/arch/i386/pci/opti82c700.c
+++ b/sys/arch/i386/pci/opti82c700.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: opti82c700.c,v 1.3 2000/03/28 03:37:59 mickey Exp $ */
-/* $NetBSD: opti82c700.c,v 1.1 1999/11/17 01:21:20 thorpej Exp $ */
+/* $OpenBSD: opti82c700.c,v 1.4 2000/08/08 19:12:47 mickey Exp $ */
+/* $NetBSD: opti82c700.c,v 1.2 2000/07/18 11:07:20 soda Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -64,7 +64,7 @@
*/
/*
- * Support for the Opti 82c700 PCI-ISA bridge interrupt controller.
+ * Support for the Opti 82c700 FireStar PCI-ISA bridge interrupt controller.
*/
#include <sys/param.h>
@@ -82,6 +82,12 @@
#include <i386/pci/pci_intr_fixup.h>
#include <i386/pci/opti82c700reg.h>
+#ifdef FIRESTARDEBUG
+#define DPRINTF(arg) printf arg
+#else
+#define DPRINTF(arg)
+#endif
+
int opti82c700_getclink __P((pciintr_icu_handle_t, int, int *));
int opti82c700_get_intr __P((pciintr_icu_handle_t, int, int *));
int opti82c700_set_intr __P((pciintr_icu_handle_t, int, int));
@@ -102,6 +108,9 @@ struct opti82c700_handle {
};
int opti82c700_addr __P((int, int *, int *));
+#ifdef FIRESTARDEBUG
+void opti82c700_pir_dump __P((struct opti82c700_handle *));
+#endif
int
opti82c700_init(pc, iot, tag, ptagp, phandp)
@@ -119,7 +128,9 @@ opti82c700_init(pc, iot, tag, ptagp, phandp)
ph->ph_pc = pc;
ph->ph_tag = tag;
-
+#ifdef FIRESTARDEBUG
+ opti82c700_pir_dump(ph);
+#endif
*ptagp = &opti82c700_pci_icu;
*phandp = ph;
return (0);
@@ -146,6 +157,7 @@ opti82c700_addr(link, addrofs, ofs)
break;
case FIRESTAR_PIR_SELECT_PIRQ:
+ /* FALLTHROUGH */
case FIRESTAR_PIR_SELECT_BRIDGE:
if (regofs < 0 || regofs > 3)
return (1);
@@ -165,13 +177,30 @@ opti82c700_getclink(v, link, clinkp)
pciintr_icu_handle_t v;
int link, *clinkp;
{
+ DPRINTF(("FireStar link value 0x%x: ", link));
- if (FIRESTAR_LEGAL_LINK(link)) {
- *clinkp = link;
- return (0);
+ switch (FIRESTAR_PIR_SELECTSRC(link)) {
+ default:
+ DPRINTF(("bogus IRQ selection source\n"));
+ return (1);
+ case FIRESTAR_PIR_SELECT_NONE:
+ DPRINTF(("No interrupt connection\n"));
+ return (1);
+ case FIRESTAR_PIR_SELECT_IRQ:
+ DPRINTF(("FireStar IRQ pin"));
+ break;
+ case FIRESTAR_PIR_SELECT_PIRQ:
+ DPRINTF(("FireStar PIO pin or Serial IRQ PIRQ#"));
+ break;
+ case FIRESTAR_PIR_SELECT_BRIDGE:
+ DPRINTF(("FireBridge 1 INTx# pin"));
+ break;
}
- return (1);
+ DPRINTF((" REGOFST:%#x\n", FIRESTAR_PIR_REGOFS(link)));
+ *clinkp = link;
+
+ return (0);
}
int
@@ -183,15 +212,14 @@ opti82c700_get_intr(v, clink, irqp)
pcireg_t reg;
int val, addrofs, ofs;
- if (FIRESTAR_LEGAL_LINK(clink) == 0)
- return (1);
-
if (opti82c700_addr(clink, &addrofs, &ofs))
return (1);
reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
- *irqp = (val == FIRESTAR_PIRQ_NONE) ? 0xff : val;
+
+ *irqp = (val == FIRESTAR_PIRQ_NONE) ?
+ I386_PCI_INTERRUPT_LINE_NO_CONNECTION : val;
return (0);
}
@@ -205,7 +233,7 @@ opti82c700_set_intr(v, clink, irq)
int addrofs, ofs;
pcireg_t reg;
- if (FIRESTAR_LEGAL_LINK(clink) == 0 || FIRESTAR_LEGAL_IRQ(irq) == 0)
+ if (FIRESTAR_LEGAL_IRQ(irq) == 0)
return (1);
if (opti82c700_addr(clink, &addrofs, &ofs))
@@ -250,6 +278,20 @@ opti82c700_get_trigger(v, irq, triggerp)
return (0);
}
+ /*
+ * Search PIO PCIIRQ.
+ */
+ for (i = 0; i < 4; i++) {
+ opti82c700_addr(FIRESTAR_PIR_MAKELINK(FIRESTAR_PIR_SELECT_PIRQ,
+ i), &addrofs, &ofs);
+ reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
+ val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
+ if (val != irq)
+ continue;
+ *triggerp = IST_LEVEL;
+ return (0);
+ }
+
return (1);
}
@@ -287,5 +329,18 @@ opti82c700_set_trigger(v, irq, trigger)
return (0);
}
+ /*
+ * Search PIO PCIIRQ.
+ */
+ for (i = 0; i < 4; i++) {
+ opti82c700_addr(FIRESTAR_PIR_MAKELINK(FIRESTAR_PIR_SELECT_PIRQ,
+ i), &addrofs, &ofs);
+ reg = pci_conf_read(ph->ph_pc, ph->ph_tag, addrofs);
+ val = (reg >> ofs) & FIRESTAR_CFG_PIRQ_MASK;
+ if (val != irq)
+ continue;
+ return (trigger == IST_LEVEL ? 0 : 1);
+ }
+
return (1);
}
diff --git a/sys/arch/i386/pci/opti82c700reg.h b/sys/arch/i386/pci/opti82c700reg.h
index f36abe6fdfe..4dd743cb586 100644
--- a/sys/arch/i386/pci/opti82c700reg.h
+++ b/sys/arch/i386/pci/opti82c700reg.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: opti82c700reg.h,v 1.3 2000/03/28 03:37:59 mickey Exp $ */
-/* $NetBSD: opti82c700reg.h,v 1.1 1999/11/17 01:21:20 thorpej Exp $ */
+/* $OpenBSD: opti82c700reg.h,v 1.4 2000/08/08 19:12:47 mickey Exp $ */
+/* $NetBSD: opti82c700reg.h,v 1.1 1999/11/17 01:21:20 thorpej Exp $ */
/*
* Copyright (c) 1999, by UCHIYAMA Yasushi
@@ -35,11 +35,6 @@
#define FIRESTAR_CFG_INTR_PIRQ 0xb8 /* PCI configuration space */
#define FIRESTAR_PIRQ_NONE 0
-#define FIRESTAR_PIRQ_MIN FIRESTAR_CFG_INTR_IRQ
-#define FIRESTAR_PIRQ_MAX (FIRESTAR_CFG_INTR_PIRQ + 1)
-#define FIRESTAR_LEGAL_LINK(link) ((link) >= FIRESTAR_PIRQ_MIN && \
- (link) <= FIRESTAR_PIRQ_MAX)
-
#define FIRESTAR_PIRQ_MASK 0xdffa
#define FIRESTAR_LEGAL_IRQ(irq) ((irq) >= 0 && (irq) <= 15 && \
((1 << (irq)) & FIRESTAR_PIRQ_MASK) != 0)
diff --git a/sys/arch/i386/pci/pci_addr_fixup.c b/sys/arch/i386/pci/pci_addr_fixup.c
new file mode 100644
index 00000000000..f0b4ca476d3
--- /dev/null
+++ b/sys/arch/i386/pci/pci_addr_fixup.c
@@ -0,0 +1,356 @@
+/* $NetBSD: pci_addr_fixup.c,v 1.3 2000/05/31 16:38:55 uch Exp $ */
+
+/*-
+ * Copyright (c) 2000 UCHIYAMA Yasushi. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/extent.h>
+
+#include <machine/bus.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#include <i386/pci/pcibios.h>
+#include <i386/pci/pci_addr_fixup.h>
+
+struct pciaddr pciaddr;
+
+typedef int (*pciaddr_resource_manage_func_t)
+ (pci_chipset_tag_t, pcitag_t, int, struct extent *, int,
+ bus_addr_t *, bus_size_t);
+void pciaddr_resource_manage __P((pci_chipset_tag_t, pcitag_t,
+ pciaddr_resource_manage_func_t));
+void pciaddr_resource_reserve __P((pci_chipset_tag_t, pcitag_t));
+int pciaddr_do_resource_reserve __P((pci_chipset_tag_t, pcitag_t, int,
+ struct extent *, int, bus_addr_t *,
+ bus_size_t));
+void pciaddr_resource_allocate __P((pci_chipset_tag_t, pcitag_t));
+int pciaddr_do_resource_allocate __P((pci_chipset_tag_t, pcitag_t, int,
+ struct extent *, int, bus_addr_t *,
+ bus_size_t));
+bus_addr_t pciaddr_ioaddr __P((u_int32_t));
+void pciaddr_print_devid __P((pci_chipset_tag_t, pcitag_t));
+
+#define PCIADDR_MEM_START 0x0
+#define PCIADDR_MEM_END 0xffffffff
+#define PCIADDR_PORT_START 0x0
+#define PCIADDR_PORT_END 0xffff
+
+/* for ISA devices */
+#define PCIADDR_ISAPORT_RESERVE 0x5800 /* empirical value */
+#define PCIADDR_ISAMEM_RESERVE (16 * 1024 * 1024)
+
+void
+pci_addr_fixup(pc, bus)
+ pci_chipset_tag_t pc;
+ int bus;
+{
+ extern paddr_t avail_end;
+#ifdef PCIBIOSVERBOSE
+ const char *verbose_header =
+ "[%s]-----------------------\n"
+ " device vendor product\n"
+ " register space address size\n"
+ "--------------------------------------------\n";
+ const char *verbose_footer =
+ "--------------------------[%3d devices bogus]\n";
+#endif
+ const struct {
+ bus_addr_t start;
+ bus_size_t size;
+ char *name;
+ } system_reserve [] = {
+ { 0xfec00000, 0x100000, "I/O APIC" },
+ { 0xfee00000, 0x100000, "Local APIC" },
+ { 0xfffe0000, 0x20000, "BIOS PROM" },
+ { 0, 0, 0 }, /* terminator */
+ }, *srp;
+ paddr_t start;
+ int error;
+
+ pciaddr.extent_mem = extent_create("PCI I/O memory space",
+ PCIADDR_MEM_START,
+ PCIADDR_MEM_END,
+ M_DEVBUF, 0, 0, EX_NOWAIT);
+ KASSERT(pciaddr.extent_mem);
+ pciaddr.extent_port = extent_create("PCI I/O port space",
+ PCIADDR_PORT_START,
+ PCIADDR_PORT_END,
+ M_DEVBUF, 0, 0, EX_NOWAIT);
+ KASSERT(pciaddr.extent_port);
+
+ /*
+ * 1. check & reserve system BIOS setting.
+ */
+ PCIBIOS_PRINTV((verbose_header, "System BIOS Setting"));
+ pci_device_foreach(pc, bus, pciaddr_resource_reserve);
+ PCIBIOS_PRINTV((verbose_footer, pciaddr.nbogus));
+
+ /*
+ * 2. reserve non-PCI area.
+ */
+ for (srp = system_reserve; srp->size; srp++) {
+ error = extent_alloc_region(pciaddr.extent_mem, srp->start,
+ srp->size,
+ EX_NOWAIT| EX_MALLOCOK);
+ if (error != 0) {
+ printf("WARNING: can't reserve area for %s.\n",
+ srp->name);
+ }
+ }
+
+ /*
+ * 3. determine allocation space
+ */
+ start = i386_round_page(avail_end + 1);
+ if (start < PCIADDR_ISAMEM_RESERVE)
+ start = PCIADDR_ISAMEM_RESERVE;
+ pciaddr.mem_alloc_start = (start + 0x100000 + 1) & ~(0x100000 - 1);
+ pciaddr.port_alloc_start = PCIADDR_ISAPORT_RESERVE;
+ PCIBIOS_PRINTV((" Physical memory end: 0x%08x\n PCI memory mapped I/O "
+ "space start: 0x%08x\n", (unsigned)avail_end,
+ (unsigned)pciaddr.mem_alloc_start));
+
+ if (pciaddr.nbogus == 0)
+ return; /* no need to fixup */
+
+ /*
+ * 4. do fixup
+ */
+ PCIBIOS_PRINTV((verbose_header, "PCIBIOS fixup stage"));
+ pciaddr.nbogus = 0;
+ pci_device_foreach(pc, bus, pciaddr_resource_allocate);
+ PCIBIOS_PRINTV((verbose_footer, pciaddr.nbogus));
+
+}
+
+void
+pciaddr_resource_reserve(pc, tag)
+ pci_chipset_tag_t pc;
+ pcitag_t tag;
+{
+#ifdef PCIBIOSVERBOSE
+ if (pcibiosverbose)
+ pciaddr_print_devid(pc, tag);
+#endif
+ pciaddr_resource_manage(pc, tag, pciaddr_do_resource_reserve);
+}
+
+void
+pciaddr_resource_allocate(pc, tag)
+ pci_chipset_tag_t pc;
+ pcitag_t tag;
+{
+#ifdef PCIBIOSVERBOSE
+ if (pcibiosverbose)
+ pciaddr_print_devid(pc, tag);
+#endif
+ pciaddr_resource_manage(pc, tag, pciaddr_do_resource_allocate);
+}
+
+void
+pciaddr_resource_manage(pc, tag, func)
+ pci_chipset_tag_t pc;
+ pcitag_t tag;
+ pciaddr_resource_manage_func_t func;
+{
+ struct extent *ex;
+ pcireg_t val, mask;
+ bus_addr_t addr;
+ bus_size_t size;
+ int error, useport, usemem, mapreg, type, reg_start, reg_end;
+
+ val = pci_conf_read(pc, tag, PCI_BHLC_REG);
+ switch (PCI_HDRTYPE_TYPE(val)) {
+ default:
+ printf("WARNING: unknown PCI device header.");
+ pciaddr.nbogus++;
+ return;
+ case 0:
+ reg_start = PCI_MAPREG_START;
+ reg_end = PCI_MAPREG_END;
+ break;
+ case 1: /* PCI-PCI bridge */
+ reg_start = PCI_MAPREG_START;
+ reg_end = PCI_MAPREG_PPB_END;
+ break;
+ case 2: /* PCI-CardBus bridge */
+ reg_start = PCI_MAPREG_START;
+ reg_end = PCI_MAPREG_PCB_END;
+ break;
+ }
+ error = useport = usemem = 0;
+
+ for (mapreg = reg_start; mapreg < reg_end; mapreg += 4) {
+ /* inquire PCI device bus space requirement */
+ val = pci_conf_read(pc, tag, mapreg);
+ pci_conf_write(pc, tag, mapreg, ~0);
+
+ mask = pci_conf_read(pc, tag, mapreg);
+ pci_conf_write(pc, tag, mapreg, val);
+
+ type = PCI_MAPREG_TYPE(val);
+ if (type == PCI_MAPREG_TYPE_MEM) {
+ size = PCI_MAPREG_MEM_SIZE(mask);
+ ex = pciaddr.extent_mem;
+ } else {
+ size = PCI_MAPREG_IO_SIZE(mask);
+ ex = pciaddr.extent_port;
+ }
+ addr = pciaddr_ioaddr(val);
+
+ if (!size) /* unused register */
+ continue;
+
+ if (type == PCI_MAPREG_TYPE_MEM)
+ ++usemem;
+ else
+ ++useport;
+
+ /* reservation/allocation phase */
+ error += (*func) (pc, tag, mapreg, ex, type, &addr, size);
+
+ PCIBIOS_PRINTV(("\n\t%02xh %s 0x%08x 0x%08x",
+ mapreg, type ? "port" : "mem ",
+ (unsigned int)addr, (unsigned int)size));
+ }
+
+ /* enable/disable PCI device */
+ val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
+ if (error == 0)
+ val |= (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE |
+ PCI_COMMAND_MASTER_ENABLE);
+ else
+ val &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE |
+ PCI_COMMAND_MASTER_ENABLE);
+ pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, val);
+
+ if (error)
+ pciaddr.nbogus++;
+
+ PCIBIOS_PRINTV(("\n\t\t[%s]\n", error ? "NG" : "OK"));
+}
+
+int
+pciaddr_do_resource_allocate(pc, tag, mapreg, ex, type, addr, size)
+ pci_chipset_tag_t pc;
+ pcitag_t tag;
+ struct extent *ex;
+ int mapreg, type;
+ bus_addr_t *addr;
+ bus_size_t size;
+{
+ bus_addr_t start;
+ int error;
+
+ if (*addr) /* no need to allocate */
+ return (0);
+
+ start = type == PCI_MAPREG_TYPE_MEM ? pciaddr.mem_alloc_start
+ : pciaddr.port_alloc_start;
+ if (start < ex->ex_start || start + size - 1 >= ex->ex_end) {
+ PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
+ return (1);
+ }
+ error = extent_alloc_subregion(ex, start, start + size - 1, size,
+ size, 0,
+ EX_FAST|EX_NOWAIT|EX_MALLOCOK, addr);
+ if (error) {
+ PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
+ return (1);
+ }
+
+ /* write new address to PCI device configuration header */
+ pci_conf_write(pc, tag, mapreg, *addr);
+ /* check */
+#ifdef PCIBIOSVERBOSE
+ if (!pcibiosverbose) {
+ printf("pci_addr_fixup: ");
+ pciaddr_print_devid(pc, tag);
+ }
+#endif
+ if (pciaddr_ioaddr(pci_conf_read(pc, tag, mapreg)) != *addr) {
+ pci_conf_write(pc, tag, mapreg, 0); /* clear */
+ printf("fixup failed. (new address=%#x)\n", (unsigned)*addr);
+ return (1);
+ }
+ PCIBIOS_PRINTV(("new address 0x%08x\n", (unsigned)*addr));
+
+ return (0);
+}
+
+int
+pciaddr_do_resource_reserve(pc, tag, mapreg, ex, type, addr, size)
+ pci_chipset_tag_t pc;
+ pcitag_t tag;
+ struct extent *ex;
+ int type, mapreg;
+ bus_addr_t *addr;
+ bus_size_t size;
+{
+ int error;
+
+ if (*addr == 0)
+ return (1);
+
+ error = extent_alloc_region(ex, *addr, size, EX_NOWAIT| EX_MALLOCOK);
+ if (error) {
+ PCIBIOS_PRINTV(("Resource conflict.\n"));
+ pci_conf_write(pc, tag, mapreg, 0); /* clear */
+ return (1);
+ }
+
+ return (0);
+}
+
+bus_addr_t
+pciaddr_ioaddr(val)
+ u_int32_t val;
+{
+ return ((PCI_MAPREG_TYPE(val) == PCI_MAPREG_TYPE_MEM)
+ ? PCI_MAPREG_MEM_ADDR(val)
+ : PCI_MAPREG_IO_ADDR(val));
+}
+
+void
+pciaddr_print_devid(pc, tag)
+ pci_chipset_tag_t pc;
+ pcitag_t tag;
+{
+ int bus, device, function;
+ pcireg_t id;
+
+ id = pci_conf_read(pc, tag, PCI_ID_REG);
+ pci_decompose_tag(pc, tag, &bus, &device, &function);
+ printf("%03d:%02d:%d 0x%04x 0x%04x ", bus, device, function,
+ PCI_VENDOR(id), PCI_PRODUCT(id));
+}
diff --git a/sys/arch/i386/pci/pci_addr_fixup.h b/sys/arch/i386/pci/pci_addr_fixup.h
new file mode 100644
index 00000000000..f11329c3894
--- /dev/null
+++ b/sys/arch/i386/pci/pci_addr_fixup.h
@@ -0,0 +1,40 @@
+/* $OpenBSD: pci_addr_fixup.h,v 1.1 2000/08/08 19:12:47 mickey Exp $ */
+/* $NetBSD: pci_addr_fixup.h,v 1.2 2000/05/31 16:38:55 uch Exp $ */
+
+/*-
+ * Copyright (c) 2000 UCHIYAMA Yasushi. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+struct pciaddr {
+ struct extent *extent_mem;
+ struct extent *extent_port;
+ bus_addr_t mem_alloc_start;
+ bus_addr_t port_alloc_start;
+ int nbogus;
+};
+
+extern struct pciaddr pciaddr;
+
+void pci_addr_fixup __P((pci_chipset_tag_t, int));
diff --git a/sys/arch/i386/pci/pci_bus_fixup.c b/sys/arch/i386/pci/pci_bus_fixup.c
index 92f8c2b7eb1..0aaee94ce20 100644
--- a/sys/arch/i386/pci/pci_bus_fixup.c
+++ b/sys/arch/i386/pci/pci_bus_fixup.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pci_bus_fixup.c,v 1.4 2000/04/07 22:22:57 aaron Exp $ */
+/* $OpenBSD: pci_bus_fixup.c,v 1.5 2000/08/08 19:12:47 mickey Exp $ */
/* $NetBSD: pci_bus_fixup.c,v 1.1 1999/11/17 07:32:58 thorpej Exp $ */
/*
@@ -50,9 +50,11 @@ pci_bus_fixup(pc, bus)
pci_chipset_tag_t pc;
int bus;
{
- static int bus_total;
+#ifdef PCIBIOSVERBOSE
static int bridge_cnt;
- int device, maxdevs, function, nfuncs, bridge, bus_max, bus_sub;
+ int bridge;
+#endif
+ int device, maxdevs, function, nfuncs, bus_max, bus_sub;
const struct pci_quirkdata *qd;
pcireg_t reg;
pcitag_t tag;
@@ -60,15 +62,16 @@ pci_bus_fixup(pc, bus)
bus_max = bus;
bus_sub = 0;
- if (++bus_total > 256)
- panic("pci_bus_fixup: more than 256 PCI busses?");
-
maxdevs = pci_bus_maxdevs(pc, bus);
for (device = 0; device < maxdevs; device++) {
tag = pci_make_tag(pc, bus, device, 0);
reg = pci_conf_read(pc, tag, PCI_ID_REG);
+ /* can't be that many */
+ if (bus_max == 255)
+ break;
+
/* Invalid vendor ID value? */
if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID)
continue;
@@ -101,9 +104,6 @@ pci_bus_fixup(pc, bus)
if (PCI_CLASS(reg) == PCI_CLASS_BRIDGE &&
(PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_PCI ||
PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_CARDBUS)) {
- /* Assign the bridge #. */
- bridge = bridge_cnt++;
-
/* Assign the bridge's secondary bus #. */
bus_max++;
@@ -121,6 +121,9 @@ pci_bus_fixup(pc, bus)
pci_conf_write(pc, tag, PPB_REG_BUSINFO, reg);
#ifdef PCIBIOSVERBOSE
+ /* Assign the bridge #. */
+ bridge = bridge_cnt++;
+
printf("PCI bridge %d: primary %d, "
"secondary %d, subordinate %d\n",
bridge, bus, bus_max, bus_sub);
diff --git a/sys/arch/i386/pci/pci_intr_fixup.c b/sys/arch/i386/pci/pci_intr_fixup.c
index 0c9a01f4af8..9fb60cef950 100644
--- a/sys/arch/i386/pci/pci_intr_fixup.c
+++ b/sys/arch/i386/pci/pci_intr_fixup.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: pci_intr_fixup.c,v 1.4 2000/07/06 18:11:22 ho Exp $ */
-/* $NetBSD: pci_intr_fixup.c,v 1.4 2000/01/25 17:20:47 augustss Exp $ */
+/* $OpenBSD: pci_intr_fixup.c,v 1.5 2000/08/08 19:12:48 mickey Exp $ */
+/* $NetBSD: pci_intr_fixup.c,v 1.9 2000/07/22 17:43:36 soda Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -91,24 +91,28 @@ struct pciintr_link_map {
int irq;
u_int16_t bitmap;
int fixup_stage;
- int old_irq;
SIMPLEQ_ENTRY(pciintr_link_map) list;
};
pciintr_icu_tag_t pciintr_icu_tag;
pciintr_icu_handle_t pciintr_icu_handle;
-struct pciintr_link_map *pciintr_link_lookup_pin
- __P((struct pcibios_intr_routing *, int));
-struct pciintr_link_map *pciintr_link_lookup_link __P((int));
+#ifdef PCIBIOS_IRQS_HINT
+int pcibios_irqs_hint = PCIBIOS_IRQS_HINT;
+#endif
+
+struct pciintr_link_map *pciintr_link_lookup __P((int));
struct pciintr_link_map *pciintr_link_alloc __P((struct pcibios_intr_routing *,
int));
struct pcibios_intr_routing *pciintr_pir_lookup __P((int, int));
+static int pciintr_bitmap_count_irq __P((int, int *));
+static int pciintr_bitmap_find_lowest_irq __P((int, int *));
int pciintr_link_init __P((void));
int pciintr_link_fixup __P((void));
int pciintr_link_route __P((u_int16_t *));
int pciintr_irq_release __P((u_int16_t *));
int pciintr_header_fixup __P((pci_chipset_tag_t));
+void pciintr_do_header_fixup __P((pci_chipset_tag_t, pcitag_t));
SIMPLEQ_HEAD(, pciintr_link_map) pciintr_link_map_list;
@@ -165,16 +169,7 @@ pciintr_icu_lookup(id)
}
struct pciintr_link_map *
-pciintr_link_lookup_pin(pir, pin)
- struct pcibios_intr_routing *pir;
- int pin;
-{
-
- return (pciintr_link_lookup_link(pir->linkmap[pin].link));
-}
-
-struct pciintr_link_map *
-pciintr_link_lookup_link(link)
+pciintr_link_lookup(link)
int link;
{
struct pciintr_link_map *l;
@@ -193,16 +188,54 @@ pciintr_link_alloc(pir, pin)
struct pcibios_intr_routing *pir;
int pin;
{
+ int link = pir->linkmap[pin].link, clink, irq;
struct pciintr_link_map *l, *lstart;
+ /*
+ * Get the canonical link value for this entry.
+ */
+ if (pciintr_icu_getclink(pciintr_icu_tag, pciintr_icu_handle, link,
+ &clink) != 0) {
+ /*
+ * ICU doesn't understand the link value.
+ * Just ignore this PIR entry.
+ */
+#ifdef PCIBIOSVERBOSE
+ printf("pciintr_link_alloc: bus %d device %d: "
+ "ignoring link 0x%02x\n",
+ pir->bus, PIR_DEVFUNC_DEVICE(pir->device), link);
+#endif
+ return (NULL);
+ }
+
+ /*
+ * Check the link value by asking the ICU for the canonical link value.
+ * Also, determine if this PIRQ is mapped to an IRQ.
+ */
+ if (pciintr_icu_get_intr(pciintr_icu_tag, pciintr_icu_handle, clink,
+ &irq) != 0) {
+ /*
+ * ICU doesn't understand the canonical link value.
+ * Just ignore this PIR entry.
+ */
+#ifdef PCIBIOSVERBOSE
+ printf("pciintr_link_alloc: bus %d device %d link 0x%02x: "
+ "ignoring PIRQ 0x%02x\n",
+ pir->bus, PIR_DEVFUNC_DEVICE(pir->device), link, clink);
+#endif
+ return (NULL);
+ }
+
l = malloc(sizeof(*l), M_DEVBUF, M_NOWAIT);
if (l == NULL)
- panic("pciintr_link_alloc");
+ return (NULL);
memset(l, 0, sizeof(*l));
- l->link = pir->linkmap[pin].link;
+ l->link = link;
l->bitmap = pir->linkmap[pin].bitmap;
+ l->clink = clink;
+ l->irq = irq; /* may be I386_PCI_INTERRUPT_LINE_NO_CONNECTION */
lstart = SIMPLEQ_FIRST(&pciintr_link_map_list);
if (lstart == NULL || lstart->link < l->link)
@@ -225,17 +258,53 @@ pciintr_pir_lookup(bus, device)
for (entry = 0; entry < pcibios_pir_table_nentries; entry++) {
pir = &pcibios_pir_table[entry];
- if (pir->bus == bus && ((pir->device >> 3) & 0x1f) == device)
+ if (pir->bus == bus &&
+ PIR_DEVFUNC_DEVICE(pir->device) == device)
return (pir);
}
return (NULL);
}
+static int
+pciintr_bitmap_count_irq(irq_bitmap, irqp)
+ int irq_bitmap, *irqp;
+{
+ int i, bit, count = 0, irq = I386_PCI_INTERRUPT_LINE_NO_CONNECTION;
+
+ if (irq_bitmap != 0) {
+ for (i = 0, bit = 1; i < 16; i++, bit <<= 1) {
+ if (irq_bitmap & bit) {
+ irq = i;
+ count++;
+ }
+ }
+ }
+ *irqp = irq;
+ return (count);
+}
+
+static int
+pciintr_bitmap_find_lowest_irq(irq_bitmap, irqp)
+ int irq_bitmap, *irqp;
+{
+ int i, bit;
+
+ if (irq_bitmap != 0) {
+ for (i = 0, bit = 1; i < 16; i++, bit <<= 1) {
+ if (irq_bitmap & bit) {
+ *irqp = i;
+ return (1); /* found */
+ }
+ }
+ }
+ return (0); /* not found */
+}
+
int
pciintr_link_init()
{
- int entry, pin, error, link, clink;
+ int entry, pin, error, link;
struct pcibios_intr_routing *pir;
struct pciintr_link_map *l;
@@ -250,39 +319,37 @@ pciintr_link_init()
for (entry = 0; entry < pcibios_pir_table_nentries; entry++) {
pir = &pcibios_pir_table[entry];
- for (pin = 0; pin < 4; pin++) {
+ for (pin = 0; pin < PCI_INTERRUPT_PIN_MAX; pin++) {
link = pir->linkmap[pin].link;
if (link == 0) {
/* No connection for this pin. */
continue;
}
-
- /*
- * Check the link value by asking the ICU for
- * the canonical link value.
- */
- if (pciintr_icu_getclink(pciintr_icu_tag,
- pciintr_icu_handle, link, &clink) != 0) {
- /*
- * Table entry is bogus. Just ignore it.
- */
-#ifdef PCIINTR_DEBUG
- printf("pciintr_link_init: bad table entry: "
- "bus %d device %d link 0x%02x\n",
- pir->bus, (pir->device >> 3 & 0x1f), link);
-#endif
- continue;
- }
-
/*
* Multiple devices may be wired to the same
* interrupt; check to see if we've seen this
* one already. If not, allocate a new link
* map entry and stuff it in the map.
*/
- l = pciintr_link_lookup_pin(pir, pin);
- if (l == NULL)
+ l = pciintr_link_lookup(link);
+ if (l == NULL) {
(void) pciintr_link_alloc(pir, pin);
+ } else if (pir->linkmap[pin].bitmap != l->bitmap) {
+ /*
+ * violates PCI IRQ Routing Table Specification
+ */
+#ifdef PCIBIOSVERBOSE
+ printf("pciintr_link_init: "
+ "bus %d device %d link 0x%02x: "
+ "bad irq bitmap 0x%04x, "
+ "should be 0x%04x\n",
+ pir->bus, PIR_DEVFUNC_DEVICE(pir->device),
+ link, pir->linkmap[pin].bitmap, l->bitmap);
+#endif
+ /* safer value. */
+ l->bitmap &= pir->linkmap[pin].bitmap;
+ /* XXX - or, should ignore this entry? */
+ }
}
}
@@ -293,96 +360,51 @@ int
pciintr_link_fixup()
{
struct pciintr_link_map *l;
- u_int16_t pciirq, bitmap;
- int i, j, cnt, irq;
+ int irq;
+ u_int16_t pciirq = 0;
/*
* First stage: Attempt to connect PIRQs which aren't
* yet connected.
*/
- pciirq = 0;
-
for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL;
l = SIMPLEQ_NEXT(l, list)) {
- /*
- * Get the canonical link value for this entry.
- */
- if (pciintr_icu_getclink(pciintr_icu_tag, pciintr_icu_handle,
- l->link, &l->clink) != 0) {
+ if (l->irq != I386_PCI_INTERRUPT_LINE_NO_CONNECTION) {
/*
- * ICU doesn't understand this link value.
+ * Interrupt is already connected. Don't do
+ * anything to it.
+ * In this case, l->fixup_stage == 0.
*/
+ pciirq |= 1 << l->irq;
#ifdef PCIINTR_DEBUG
- printf("pciintr_link_fixup: link 0x%02x invalid\n",
- l->link);
+ printf("pciintr_link_fixup: PIRQ 0x%02x already "
+ "connected to IRQ %d\n", l->clink, l->irq);
#endif
- l->clink = -1;
continue;
}
-
/*
- * Determine if this PIRQ is mapped to an IRQ.
+ * Interrupt isn't connected. Attempt to assign it to an IRQ.
*/
- if (pciintr_icu_get_intr(pciintr_icu_tag, pciintr_icu_handle,
- l->clink, &irq) != 0) {
- /*
- * ICU doesn't understand this PIRQ value.
- */
- l->clink = -1;
#ifdef PCIINTR_DEBUG
- printf("pciintr_link_fixup: PIRQ %d invalid\n",
- l->clink);
+ printf("pciintr_link_fixup: PIRQ 0x%02x not connected",
+ l->clink);
#endif
- continue;
- }
-
- if (irq == 0xff) {
- /*
- * Interrupt isn't connected. Attempt to assign
- * it to an IRQ.
- */
-#ifdef PCIINTR_DEBUG
- printf("pciintr_link_fixup: PIRQ %d not connected",
- l->clink);
-#endif
- bitmap = l->bitmap;
- for (i = 0, j = 0xff, cnt = 0; i < 16; i++)
- if (bitmap & (1 << i))
- j = i, cnt++;
- /*
- * Just do the easy case now; we'll defer the
- * harder ones to Stage 2.
- */
- if (cnt == 1) {
- l->irq = j;
- l->old_irq = irq;
- l->fixup_stage = 1;
- pciirq |= 1 << j;
-#ifdef PCIINTR_DEBUG
- printf(", assigning IRQ %d", l->irq);
-#endif
- }
-#ifdef PCIINTR_DEBUG
- printf("\n");
-#endif
- } else {
- /*
- * Interrupt is already connected. Don't do
- * anything to it.
- */
+ /*
+ * Just do the easy case now; we'll defer the harder ones
+ * to Stage 2.
+ */
+ if (pciintr_bitmap_count_irq(l->bitmap, &irq) == 1) {
l->irq = irq;
+ l->fixup_stage = 1;
pciirq |= 1 << irq;
#ifdef PCIINTR_DEBUG
- printf("pciintr_link_fixup: PIRQ %d already connected "
- "to IRQ %d\n", l->clink, l->irq);
+ printf(", assigning IRQ %d", l->irq);
#endif
}
- }
-
-#ifdef PCIBIOS_IRQS
- /* In case the user supplied a mask for the PCI irqs we use it. */
- pciirq = PCIBIOS_IRQS;
+#ifdef PCIINTR_DEBUG
+ printf("\n");
#endif
+ }
/*
* Stage 2: Attempt to connect PIRQs which we didn't
@@ -390,37 +412,44 @@ pciintr_link_fixup()
*/
for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL;
l = SIMPLEQ_NEXT(l, list)) {
- if (l->irq == 0) {
- bitmap = l->bitmap;
- for (i = 0; i < 16; i++) {
- if ((pciirq & (1 << i)) != 0 &&
- (bitmap & (1 << i)) != 0) {
- /*
- * This IRQ is a valid PCI
- * IRQ already connected to
- * another PIRQ, and also an
- * IRQ our PIRQ can use; connect
- * it up!
- */
- l->irq = i;
- l->old_irq = 0xff;
- l->fixup_stage = 2;
+ if (l->irq != I386_PCI_INTERRUPT_LINE_NO_CONNECTION)
+ continue;
+ if (pciintr_bitmap_find_lowest_irq(l->bitmap & pciirq,
+ &l->irq)) {
+ /*
+ * This IRQ is a valid PCI IRQ already
+ * connected to another PIRQ, and also an
+ * IRQ our PIRQ can use; connect it up!
+ */
+ l->fixup_stage = 2;
#ifdef PCIINTR_DEBUG
- printf("pciintr_link_fixup: assigning "
- "IRQ %d to PIRQ %d\n", l->irq,
- l->clink);
+ printf("pciintr_link_fixup (stage 2): "
+ "assigning IRQ %d to PIRQ 0x%02x\n",
+ l->irq, l->clink);
#endif
- break;
- }
- }
}
}
+#ifdef PCIBIOS_IRQS_HINT
/*
- * Stage 3: Allow the user to specify interrupt routing
- * information, overriding what we've done above.
+ * Stage 3: The worst case. I need configuration hint that
+ * user supplied a mask for the PCI irqs
*/
- /* XXX Not implemented. */
+ for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL;
+ l = SIMPLEQ_NEXT(l, list)) {
+ if (l->irq != I386_PCI_INTERRUPT_LINE_NO_CONNECTION)
+ continue;
+ if (pciintr_bitmap_find_lowest_irq(
+ l->bitmap & pcibios_irqs_hint, &l->irq)) {
+ l->fixup_stage = 3;
+#ifdef PCIINTR_DEBUG
+ printf("pciintr_link_fixup (stage 3): "
+ "assigning IRQ %d to PIRQ 0x%02x\n",
+ l->irq, l->clink);
+#endif
+ }
+ }
+#endif /* PCIBIOS_IRQS_HINT */
return (0);
}
@@ -436,12 +465,38 @@ pciintr_link_route(pciirq)
for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL;
l = SIMPLEQ_NEXT(l, list)) {
+ if (l->fixup_stage == 0) {
+ if (l->irq == I386_PCI_INTERRUPT_LINE_NO_CONNECTION) {
+ /* Appropriate interrupt was not found. */
+#ifdef PCIBIOSVERBOSE
+ printf("pciintr_link_route: "
+ "PIRQ 0x%02x: no IRQ, try "
+ "\"options PCIBIOS_IRQS_HINT=0x%04x\"\n",
+ l->clink,
+ /* suggest irq 9/10/11, if possible */
+ (l->bitmap & 0x0e00) ? (l->bitmap & 0x0e00)
+ : l->bitmap);
+#endif
+ } else {
+ /* BIOS setting has no problem */
+#ifdef PCIINTR_DEBUG
+ printf("pciintr_link_route: "
+ "route of PIRQ 0x%02x -> "
+ "IRQ %d preserved BIOS setting\n",
+ l->clink, l->irq);
+#endif
+ *pciirq |= (1 << l->irq);
+ }
+ continue; /* nothing to do. */
+ }
+
if (pciintr_icu_set_intr(pciintr_icu_tag, pciintr_icu_handle,
l->clink, l->irq) != 0 ||
- pciintr_icu_set_trigger(pciintr_icu_tag, pciintr_icu_handle,
+ pciintr_icu_set_trigger(pciintr_icu_tag,
+ pciintr_icu_handle,
l->irq, IST_LEVEL) != 0) {
- printf("pciintr_link_route: route of PIRQ %d -> IRQ %d"
- " failed\n", l->clink, l->irq);
+ printf("pciintr_link_route: route of PIRQ 0x%02x -> "
+ "IRQ %d failed\n", l->clink, l->irq);
rv = 1;
} else {
/*
@@ -459,10 +514,10 @@ int
pciintr_irq_release(pciirq)
u_int16_t *pciirq;
{
- int i;
+ int i, bit;
- for (i = 0; i < 16; i++) {
- if ((*pciirq & (1 << i)) == 0)
+ for (i = 0, bit = 1; i < 16; i++, bit <<= 1) {
+ if ((*pciirq & bit) == 0)
(void) pciintr_icu_set_trigger(pciintr_icu_tag,
pciintr_icu_handle, i, IST_EDGE);
}
@@ -474,122 +529,115 @@ int
pciintr_header_fixup(pc)
pci_chipset_tag_t pc;
{
- const struct pci_quirkdata *qd;
+ PCIBIOS_PRINTV(("------------------------------------------\n"));
+ PCIBIOS_PRINTV((" device vendor product pin PIRQ IRQ stage\n"));
+ PCIBIOS_PRINTV(("------------------------------------------\n"));
+ pci_device_foreach(pc, pcibios_max_bus, pciintr_do_header_fixup);
+ PCIBIOS_PRINTV(("------------------------------------------\n"));
+
+ return (0);
+}
+
+void
+pciintr_do_header_fixup(pc, tag)
+ pci_chipset_tag_t pc;
+ pcitag_t tag;
+{
struct pcibios_intr_routing *pir;
struct pciintr_link_map *l;
- int pin, bus, device, function, maxdevs, nfuncs, irq, link;
- pcireg_t id, bhlcr, intr;
- pcitag_t tag;
+ int pin, irq, link;
+ int bus, device, function;
+ pcireg_t intr, id;
-#ifdef PCIBIOSVERBOSE
- printf("--------------------------------------------\n");
- printf(" device vendor product pin PIRQ IRQ stage\n");
- printf("--------------------------------------------\n");
-#endif
+ pci_decompose_tag(pc, tag, &bus, &device, &function);
+ id = pci_conf_read(pc, tag, PCI_ID_REG);
- for (bus = 0; bus <= pcibios_max_bus; bus++) {
- maxdevs = pci_bus_maxdevs(pc, bus);
- for (device = 0; device < maxdevs; device++) {
- tag = pci_make_tag(pc, bus, device, 0);
- id = pci_conf_read(pc, tag, PCI_ID_REG);
+ intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
+ pin = PCI_INTERRUPT_PIN(intr);
+ irq = PCI_INTERRUPT_LINE(intr);
- /* 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;
+ if (pin == 0) {
+ /*
+ * No interrupt used.
+ */
+ return;
+ }
- 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))
- nfuncs = 8;
- else
- nfuncs = 1;
-
- for (function = 0; function < nfuncs; function++) {
- tag = pci_make_tag(pc, bus, device, function);
- id = pci_conf_read(pc, tag, PCI_ID_REG);
- intr = pci_conf_read(pc, tag,
- PCI_INTERRUPT_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;
-
- pin = PCI_INTERRUPT_PIN(intr);
- irq = PCI_INTERRUPT_LINE(intr);
-
- if (pin == 0) {
- /*
- * No interrupt used.
- */
- continue;
- }
-
- pir = pciintr_pir_lookup(bus, device);
- if (pir == NULL ||
- (link = pir->linkmap[pin - 1].link) == 0) {
- /*
- * Interrupt not connected; no
- * need to change.
- */
- continue;
- }
-
- l = pciintr_link_lookup_link(link);
- if (l == NULL) {
- /*
- * No link map entry?!
- */
- printf("pciintr_header_fixup: no entry "
- "for link 0x%02x (%d:%d:%d:%c)\n",
- link, bus, device, function,
- '@' + pin);
- continue;
- }
+ pir = pciintr_pir_lookup(bus, device);
+ if (pir == NULL || (link = pir->linkmap[pin - 1].link) == 0) {
+ /*
+ * Interrupt not connected; no
+ * need to change.
+ */
+ return;
+ }
- /*
- * IRQs 14 and 15 are reserved for
- * PCI IDE interrupts; don't muck
- * with them.
- */
- if (irq == 14 || irq == 15)
- continue;
+ l = pciintr_link_lookup(link);
+ if (l == NULL) {
+#ifdef PCIINTR_DEBUG
+ /*
+ * No link map entry.
+ * Probably pciintr_icu_getclink() or pciintr_icu_get_intr()
+ * was failed.
+ */
+ printf("pciintr_header_fixup: no entry for link 0x%02x "
+ "(%d:%d:%d:%c)\n", link, bus, device, function,
+ '@' + pin);
+#endif
+ return;
+ }
#ifdef PCIBIOSVERBOSE
- printf("%03d:%02d:%d 0x%04x 0x%04x %c "
- "0x%02x %02d %d\n",
- bus, device, function,
- PCI_VENDOR(id), PCI_PRODUCT(id),
- '@' + pin, l->clink, l->irq,
- l->fixup_stage);
+ if (pcibiosverbose) {
+ printf("%03d:%02d:%d 0x%04x 0x%04x %c 0x%02x",
+ bus, device, function, PCI_VENDOR(id), PCI_PRODUCT(id),
+ '@' + pin, l->clink);
+ if (l->irq == I386_PCI_INTERRUPT_LINE_NO_CONNECTION)
+ printf(" -");
+ else
+ printf(" %3d", l->irq);
+ printf(" %d ", l->fixup_stage);
+ }
#endif
+
+ /*
+ * IRQs 14 and 15 are reserved for PCI IDE interrupts; don't muck
+ * with them.
+ */
+ if (irq == 14 || irq == 15) {
+ PCIBIOS_PRINTV((" WARNING: ignored\n"));
+ return;
+ }
- intr &= ~(PCI_INTERRUPT_LINE_MASK <<
- PCI_INTERRUPT_LINE_SHIFT);
- intr |= (l->irq << PCI_INTERRUPT_LINE_SHIFT);
- pci_conf_write(pc, tag, PCI_INTERRUPT_REG,
- intr);
- }
- }
+ if (l->irq == I386_PCI_INTERRUPT_LINE_NO_CONNECTION) {
+ /* Appropriate interrupt was not found. */
+ PCIBIOS_PRINTV((" WARNING: missing IRQ\n"));
+ return;
}
-#ifdef PCIBIOSVERBOSE
- printf("--------------------------------------------\n");
+ if (l->irq == irq) {
+ /* don't have to reconfigure */
+ PCIBIOS_PRINTV((" already assigned\n"));
+ return;
+ }
+
+ if (irq == 0 || irq == I386_PCI_INTERRUPT_LINE_NO_CONNECTION) {
+ PCIBIOS_PRINTV((" fixed up\n"));
+ } else {
+ /* routed by BIOS, but inconsistent */
+#ifdef PCIBIOS_INTR_FIXUP_FORCE
+ /* believe PCI IRQ Routing table */
+ PCIBIOS_PRINTV((" WARNING: overriding irq %d\n", irq));
+#else
+ /* believe PCI Interrupt Configuration Register (default) */
+ PCIBIOS_PRINTV((" WARNING: preserving irq %d\n", irq));
+ return;
#endif
+ }
- return (0);
+ intr &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT);
+ intr |= (l->irq << PCI_INTERRUPT_LINE_SHIFT);
+ pci_conf_write(pc, tag, PCI_INTERRUPT_REG, intr);
}
int
@@ -611,8 +659,8 @@ pci_intr_fixup(pc, iot, pciirq)
*/
if (pcibios_pir_header.signature != 0) {
icutag = pci_make_tag(pc, pcibios_pir_header.router_bus,
- (pcibios_pir_header.router_devfunc >> 3) & 0x1f,
- pcibios_pir_header.router_devfunc & 7);
+ PIR_DEVFUNC_DEVICE(pcibios_pir_header.router_devfunc),
+ PIR_DEVFUNC_FUNCTION(pcibios_pir_header.router_devfunc));
icuid = pcibios_pir_header.compat_router;
if (icuid == 0 ||
(piit = pciintr_icu_lookup(icuid)) == NULL) {
diff --git a/sys/arch/i386/pci/pci_machdep.h b/sys/arch/i386/pci/pci_machdep.h
index 35d75a09793..166c959cb40 100644
--- a/sys/arch/i386/pci/pci_machdep.h
+++ b/sys/arch/i386/pci/pci_machdep.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pci_machdep.h,v 1.5 1998/01/20 18:40:23 niklas Exp $ */
+/* $OpenBSD: pci_machdep.h,v 1.6 2000/08/08 19:12:48 mickey Exp $ */
/* $NetBSD: pci_machdep.h,v 1.7 1997/06/06 23:29:18 thorpej Exp $ */
/*
@@ -97,3 +97,10 @@ void pci_decompose_tag __P((pci_chipset_tag_t, pcitag_t,
void *pci_map_int __P((pcitag_t, int, int (*)(void *), void *));
int pci_map_io __P((pcitag_t, int, int *));
int pci_map_mem __P((pcitag_t, int, vm_offset_t *, vm_offset_t *));
+
+/*
+ * Section 6.2.4, `Miscellaneous Functions' of the PIC Specification,
+ * says that 255 means `unknown' or `no connection' to the interrupt
+ * controller on a PC.
+ */
+#define I386_PCI_INTERRUPT_LINE_NO_CONNECTION 0xff
diff --git a/sys/arch/i386/pci/pcibios.c b/sys/arch/i386/pci/pcibios.c
index 260cfff182e..48981281083 100644
--- a/sys/arch/i386/pci/pcibios.c
+++ b/sys/arch/i386/pci/pcibios.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: pcibios.c,v 1.5 2000/07/06 18:42:18 mickey Exp $ */
-/* $NetBSD: pcibios.c,v 1.2 1999/11/17 07:33:41 thorpej Exp $ */
+/* $OpenBSD: pcibios.c,v 1.6 2000/08/08 19:12:48 mickey Exp $ */
+/* $NetBSD: pcibios.c,v 1.4 2000/07/18 11:15:25 soda Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -77,6 +77,7 @@
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
#include <i386/pci/pcibios.h>
#ifdef PCIBIOS_INTR_FIXUP
@@ -85,6 +86,9 @@
#ifdef PCIBIOS_BUS_FIXUP
#include <i386/pci/pci_bus_fixup.h>
#endif
+#ifdef PCIBIOS_ADDR_FIXUP
+#include <i386/pci/pci_addr_fixup.h>
+#endif
#ifdef __NetBSD__
#include <machine/bios32.h>
@@ -92,6 +96,10 @@
#include <machine/biosvar.h>
#endif
+#ifdef PCIBIOSVERBOSE
+int pcibiosverbose = 1;
+#endif
+
int pcibios_present;
struct pcibios_pir_header pcibios_pir_header;
@@ -207,6 +215,10 @@ pcibios_init()
printf("PCI bus #%d is the last bus\n", pcibios_max_bus);
#endif
#endif
+
+#ifdef PCIBIOS_ADDR_FIXUP
+ pci_addr_fixup(NULL, pcibios_max_bus);
+#endif
}
void
@@ -268,8 +280,8 @@ pcibios_pir_init()
printf("PCI Interrupt Router at %03d:%02d:%01d",
pcibios_pir_header.router_bus,
- (pcibios_pir_header.router_devfunc >> 3) & 0x1f,
- pcibios_pir_header.router_devfunc & 7);
+ PIR_DEVFUNC_DEVICE(pcibios_pir_header.router_devfunc),
+ PIR_DEVFUNC_FUNCTION(pcibios_pir_header.router_devfunc));
if (pcibios_pir_header.compat_router != 0) {
pci_devinfo(pcibios_pir_header.compat_router, 0, 0,
devinfo);
@@ -464,7 +476,7 @@ pcibios_print_pir_table()
printf("PIR Entry %d:\n", i);
printf("\tBus: %d Device: %d\n",
pcibios_pir_table[i].bus,
- pcibios_pir_table[i].device >> 3);
+ PIR_DEVFUNC_DEVICE(pcibios_pir_table[i].device));
for (j = 0; j < 4; j++) {
printf("\t\tINT%c: link 0x%02x bitmap 0x%04x\n",
'A' + j,
@@ -474,3 +486,57 @@ pcibios_print_pir_table()
}
}
#endif
+
+void
+pci_device_foreach(pc, maxbus, func)
+ pci_chipset_tag_t pc;
+ int maxbus;
+ void (*func) __P((pci_chipset_tag_t, pcitag_t));
+{
+ const struct pci_quirkdata *qd;
+ int bus, device, function, maxdevs, nfuncs;
+ pcireg_t id, bhlcr;
+ pcitag_t tag;
+
+ for (bus = 0; bus <= maxbus; bus++) {
+ maxdevs = pci_bus_maxdevs(pc, bus);
+ for (device = 0; device < maxdevs; device++) {
+ tag = pci_make_tag(pc, 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))
+ nfuncs = 8;
+ else
+ nfuncs = 1;
+
+ for (function = 0; function < nfuncs; function++) {
+ 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;
+ (*func)(pc, tag);
+ }
+ }
+ }
+}
diff --git a/sys/arch/i386/pci/pcibios.h b/sys/arch/i386/pci/pcibios.h
index c1d375e478a..20738558942 100644
--- a/sys/arch/i386/pci/pcibios.h
+++ b/sys/arch/i386/pci/pcibios.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: pcibios.h,v 1.3 2000/03/28 03:37:59 mickey Exp $ */
-/* $NetBSD: pcibios.h,v 1.1 1999/11/17 01:16:37 thorpej Exp $ */
+/* $OpenBSD: pcibios.h,v 1.4 2000/08/08 19:12:48 mickey Exp $ */
+/* $NetBSD: pcibios.h,v 1.2 2000/04/28 17:15:16 uch Exp $ */
/*
* Copyright (c) 1999, by UCHIYAMA Yasushi
@@ -80,9 +80,36 @@ struct pcibios_pir_header {
u_int8_t checksum;
} __attribute__((__packed__));
+#define PIR_DEVFUNC_DEVICE(devfunc) (((devfunc) >> 3) & 0x1f)
+#define PIR_DEVFUNC_FUNCTION(devfunc) ((devfunc) & 7)
+
void pcibios_init __P((void));
extern struct pcibios_pir_header pcibios_pir_header;
extern struct pcibios_intr_routing *pcibios_pir_table;
extern int pcibios_pir_table_nentries;
extern int pcibios_max_bus;
+
+void pci_device_foreach __P((pci_chipset_tag_t, int,
+ void (*) (pci_chipset_tag_t, pcitag_t)));
+
+#ifdef PCIBIOSVERBOSE
+extern int pcibiosverbose;
+
+#define PCIBIOS_PRINTV(arg) \
+ do { \
+ if (pcibiosverbose) \
+ printf arg; \
+ } while (0)
+#define PCIBIOS_PRINTVN(n, arg) \
+ do { \
+ if (pcibiosverbose > (n)) \
+ printf arg; \
+ } while (0)
+#else
+#define PCIBIOS_PRINTV(arg)
+#define PCIBIOS_PRINTVN(n, arg)
+#endif
+
+void pci_device_foreach __P((pci_chipset_tag_t, int,
+ void (*) __P((pci_chipset_tag_t, pcitag_t))));
diff --git a/sys/arch/i386/pci/piix.c b/sys/arch/i386/pci/piix.c
index 99eb911266f..f00402a108d 100644
--- a/sys/arch/i386/pci/piix.c
+++ b/sys/arch/i386/pci/piix.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: piix.c,v 1.3 2000/03/28 03:37:59 mickey Exp $ */
-/* $NetBSD: piix.c,v 1.1 1999/11/17 01:21:20 thorpej Exp $ */
+/* $OpenBSD: piix.c,v 1.4 2000/08/08 19:12:48 mickey Exp $ */
+/* $NetBSD: piix.c,v 1.1 1999/11/17 01:21:20 thorpej Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -83,9 +83,18 @@
#include <i386/pci/piixreg.h>
#include <i386/pci/piixvar.h>
+#ifdef PIIX_DEBUG
+#define DPRINTF(arg) printf arg
+#else
+#define DPRINTF(arg)
+#endif
+
int piix_getclink __P((pciintr_icu_handle_t, int, int *));
int piix_get_intr __P((pciintr_icu_handle_t, int, int *));
int piix_set_intr __P((pciintr_icu_handle_t, int, int));
+#ifdef PIIX_DEBUG
+void piix_pir_dump __P((struct piix_handle *));
+#endif
const struct pciintr_icu piix_pci_icu = {
piix_getclink,
@@ -119,6 +128,9 @@ piix_init(pc, iot, tag, ptagp, phandp)
return (1);
}
+#ifdef PIIX_DEBUG
+ piix_pir_dump(ph);
+#endif
*ptagp = &piix_pci_icu;
*phandp = ph;
return (0);
@@ -129,19 +141,23 @@ piix_getclink(v, link, clinkp)
pciintr_icu_handle_t v;
int link, *clinkp;
{
+ DPRINTF(("PIIX link value 0x%x: ", link));
/* Pattern 1: simple. */
if (PIIX_LEGAL_LINK(link - 1)) {
*clinkp = link - 1;
+ DPRINTF(("PIRQ %d (simple)\n", *clinkp));
return (0);
}
/* Pattern 2: configuration register offset */
if (link >= 0x60 && link <= 0x63) {
*clinkp = link - 0x60;
+ DPRINTF(("PIRQ %d (register offset)\n", *clinkp));
return (0);
}
+ DPRINTF(("bogus IRQ selection source\n"));
return (1);
}
@@ -160,7 +176,7 @@ piix_get_intr(v, clink, irqp)
reg = pci_conf_read(ph->ph_pc, ph->ph_tag, PIIX_CFG_PIRQ);
shift = clink << 3;
if ((reg >> shift) & PIIX_CFG_PIRQ_NONE)
- *irqp = 0xff;
+ *irqp = I386_PCI_INTERRUPT_LINE_NO_CONNECTION;
else
*irqp = PIIX_PIRQ(reg, clink);
@@ -236,3 +252,34 @@ piix_set_trigger(v, irq, trigger)
return (0);
}
+
+#ifdef PIIX_DEBUG
+void
+piix_pir_dump(ph)
+ struct piix_handle *ph;
+{
+ int i, irq;
+ pcireg_t irqs = pci_conf_read(ph->ph_pc, ph->ph_tag, PIIX_CFG_PIRQ);
+ u_int8_t elcr[2];
+
+ elcr[0] = bus_space_read_1(ph->ph_iot, ph->ph_elcr_ioh, 0);
+ elcr[1] = bus_space_read_1(ph->ph_iot, ph->ph_elcr_ioh, 1);
+
+ for (i = 0; i < 4; i++) {
+ irq = PIIX_PIRQ(irqs, i);
+ if (irq & PIIX_CFG_PIRQ_NONE)
+ printf("PIIX PIRQ %d: irq none (0x%x)\n", i, irq);
+ else
+ printf("PIIX PIRQ %d: irq %d\n", i, irq);
+ }
+ printf("PIIX irq:");
+ for (i = 0; i < 16; i++)
+ printf(" %2d", i);
+ printf("\n");
+ printf(" trigger:");
+ for (i = 0; i < 16; i++)
+ printf(" %c", (elcr[(i & 8) ? 1 : 0] & (1 << (i & 7))) ?
+ 'L' : 'E');
+ printf("\n");
+}
+#endif /* PIIX_DEBUG */