diff options
author | Michael Shalayeff <mickey@cvs.openbsd.org> | 2000-08-08 19:12:49 +0000 |
---|---|---|
committer | Michael Shalayeff <mickey@cvs.openbsd.org> | 2000-08-08 19:12:49 +0000 |
commit | 3bcae5645d7e92bd659dc69f655800a6b2a3c2bd (patch) | |
tree | ac4922ef747fad22861ca84e1d7981d5dbca4fc3 /sys/arch/i386/pci | |
parent | 8ff180700f66e734dd1931b3ee8172a664df89f3 (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.c | 81 | ||||
-rw-r--r-- | sys/arch/i386/pci/opti82c700reg.h | 9 | ||||
-rw-r--r-- | sys/arch/i386/pci/pci_addr_fixup.c | 356 | ||||
-rw-r--r-- | sys/arch/i386/pci/pci_addr_fixup.h | 40 | ||||
-rw-r--r-- | sys/arch/i386/pci/pci_bus_fixup.c | 21 | ||||
-rw-r--r-- | sys/arch/i386/pci/pci_intr_fixup.c | 530 | ||||
-rw-r--r-- | sys/arch/i386/pci/pci_machdep.h | 9 | ||||
-rw-r--r-- | sys/arch/i386/pci/pcibios.c | 76 | ||||
-rw-r--r-- | sys/arch/i386/pci/pcibios.h | 31 | ||||
-rw-r--r-- | sys/arch/i386/pci/piix.c | 53 |
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 */ |