DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0);
#endif
+/*
+ * Code common to ISA bridges.
+ */
+int
+isab_attach(device_t dev)
+{
+ device_t child;
+ child = device_add_child(dev, "isa", 0);
+ if (child != NULL)
+ return (bus_generic_attach(dev));
+ return (ENXIO);
+}
+++ /dev/null
-/*
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
- * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
- * Copyright (c) 2000, BSDi
- * 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 unmodified, 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.
- *
- * 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.
- *
- * $FreeBSD: src/sys/i386/isa/pci_cfgreg.c,v 1.1.2.7 2001/11/28 05:47:03 imp Exp $
- * $DragonFly: src/sys/bus/pci/amd64/pci_cfgreg.c,v 1.1 2008/08/02 05:22:19 dillon Exp $
- *
- */
-
-#include <sys/param.h> /* XXX trim includes */
-#include <sys/systm.h>
-#include <sys/bus.h>
-#include <sys/kernel.h>
-#include <sys/module.h>
-#include <sys/malloc.h>
-#include <sys/sysctl.h>
-#include <vm/vm.h>
-#include <vm/pmap.h>
-#include <machine/md_var.h>
-#include <machine/clock.h>
-#include <bus/pci/pcivar.h>
-#include <bus/pci/pcireg.h>
-#include <bus/isa/isavar.h>
-#include "pci_cfgreg.h"
-#include <machine/segments.h>
-#include <machine/pc/bios.h>
-#include <machine/smp.h>
-
-#define PRVERB(a) do { \
- if (bootverbose) \
- kprintf a ; \
-} while(0)
-
-static int pci_disable_bios_route = 0;
-SYSCTL_INT(_hw, OID_AUTO, pci_disable_bios_route, CTLFLAG_RD,
- &pci_disable_bios_route, 0, "disable interrupt routing via PCI-BIOS");
-TUNABLE_INT("hw.pci_disable_bios_route", &pci_disable_bios_route);
-
-static int cfgmech;
-static int devmax;
-
-#if 0
-static int pci_cfgintr_valid(struct PIR_entry *pe, int pin, int irq);
-static int pci_cfgintr_unique(struct PIR_entry *pe, int pin);
-static int pci_cfgintr_linked(struct PIR_entry *pe, int pin);
-static int pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin);
-static int pci_cfgintr_virgin(struct PIR_entry *pe, int pin);
-#endif
-
-static void pci_print_irqmask(u_int16_t irqs);
-#if 0
-static void pci_print_route_table(struct PIR_table *prt, int size);
-#endif
-static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
-static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
-static int pcireg_cfgopen(void);
-
-static struct PIR_table *pci_route_table;
-static int pci_route_count;
-
-/*
- * Some BIOS writers seem to want to ignore the spec and put
- * 0 in the intline rather than 255 to indicate none. Some use
- * numbers in the range 128-254 to indicate something strange and
- * apparently undocumented anywhere. Assume these are completely bogus
- * and map them to 255, which means "none".
- */
-static int
-pci_map_intline(int line)
-{
- if (line == 0 || line >= 128)
- return (PCI_INVALID_IRQ);
- return (line);
-}
-
-#if 0
-
-static u_int16_t
-pcibios_get_version(void)
-{
- struct bios_regs args;
-
- if (PCIbios.ventry == 0) {
- PRVERB(("pcibios: No call entry point\n"));
- return (0);
- }
- args.eax = PCIBIOS_BIOS_PRESENT;
- if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) {
- PRVERB(("pcibios: BIOS_PRESENT call failed\n"));
- return (0);
- }
- if (args.edx != 0x20494350) {
- PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n"));
- return (0);
- }
- return (args.ebx & 0xffff);
-}
-
-#endif
-
-/*
- * Initialise access to PCI configuration space
- */
-int
-pci_cfgregopen(void)
-{
- static int opened = 0;
- u_long sigaddr;
- static struct PIR_table *pt;
- u_int16_t v;
- u_int8_t ck, *cv;
- int i;
-
- if (opened)
- return (1);
-
- if (pcireg_cfgopen() == 0)
- return (0);
-
-#if 0
- v = pcibios_get_version();
- if (v > 0)
- kprintf("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8,
- v & 0xff);
- /*
- * Look for the interrupt routing table.
- *
- * We use PCI BIOS's PIR table if it's available $PIR is the
- * standard way to do this. Sadly some machines are not
- * standards conforming and have _PIR instead. We shrug and cope
- * by looking for both.
- */
- if (pcibios_get_version() >= 0x0210 && pt == NULL) {
- sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0);
- if (sigaddr == 0)
- sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0);
- if (sigaddr != 0) {
- pt = (struct PIR_table *)(uintptr_t)
- BIOS_PADDRTOVADDR(sigaddr);
- for (cv = (u_int8_t *)pt, ck = 0, i = 0;
- i < (pt->pt_header.ph_length); i++)
- ck += cv[i];
- if (ck == 0 && pt->pt_header.ph_length >
- sizeof(struct PIR_header)) {
- pci_route_table = pt;
- pci_route_count = (pt->pt_header.ph_length -
- sizeof(struct PIR_header)) /
- sizeof(struct PIR_entry);
- kprintf("Using $PIR table, %d entries at %p\n",
- pci_route_count, pci_route_table);
- if (bootverbose)
- pci_print_route_table(pci_route_table,
- pci_route_count);
- }
- }
- }
-#endif
- opened = 1;
- return (1);
-}
-
-/*
- * Read configuration space register
- */
-u_int32_t
-pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
-{
- uint32_t line;
-#ifdef APIC_IO
- uint32_t pin;
-
- /*
- * If we are using the APIC, the contents of the intline
- * register will probably be wrong (since they are set up for
- * use with the PIC. Rather than rewrite these registers
- * (maybe that would be smarter) we trap attempts to read them
- * and translate to our private vector numbers.
- */
- if ((reg == PCIR_INTLINE) && (bytes == 1)) {
-
- pin = pcireg_cfgread(bus, slot, func, PCIR_INTPIN, 1);
- line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
-
- if (pin != 0) {
- int airq;
-
- airq = pci_apic_irq(bus, slot, pin);
- if (airq >= 0) {
- /* PCI specific entry found in MP table */
- if (airq != line)
- undirect_pci_irq(line);
- return (airq);
- } else {
- /*
- * PCI interrupts might be redirected to the
- * ISA bus according to some MP tables. Use the
- * same methods as used by the ISA devices
- * devices to find the proper IOAPIC int pin.
- */
- airq = isa_apic_irq(line);
- if ((airq >= 0) && (airq != line)) {
- /* XXX: undirect_pci_irq() ? */
- undirect_isa_irq(line);
- return (airq);
- }
- }
- }
- return (line);
- }
-#else
- /*
- * Some BIOS writers seem to want to ignore the spec and put
- * 0 in the intline rather than 255 to indicate none. The rest of
- * the code uses 255 as an invalid IRQ.
- */
- if (reg == PCIR_INTLINE && bytes == 1) {
- line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
- return pci_map_intline(line);
- }
-#endif /* APIC_IO */
- return (pcireg_cfgread(bus, slot, func, reg, bytes));
-}
-
-/*
- * Write configuration space register
- */
-void
-pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
-{
- pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
-}
-
-int
-pci_cfgread(pcicfgregs *cfg, int reg, int bytes)
-{
- return (pci_cfgregread(cfg->bus, cfg->slot, cfg->func, reg, bytes));
-}
-
-void
-pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
-{
- pci_cfgregwrite(cfg->bus, cfg->slot, cfg->func, reg, data, bytes);
-}
-
-/*
- * XXX
- */
-int
-pci_cfgintr(int bus, int device, int pin, int oldirq)
-{
- return (PCI_INVALID_IRQ);
-}
-
-#if 0
-
-/*
- * Route a PCI interrupt
- */
-int
-pci_cfgintr(int bus, int device, int pin, int oldirq)
-{
- struct PIR_entry *pe;
- int i, irq;
- struct bios_regs args;
- u_int16_t v;
-
- int already = 0;
- int errok = 0;
-
- v = pcibios_get_version();
- if (v < 0x0210) {
- PRVERB((
- "pci_cfgintr: BIOS %x.%02x doesn't support interrupt routing\n",
- (v & 0xff00) >> 8, v & 0xff));
- return (PCI_INVALID_IRQ);
- }
- if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) ||
- (pin < 1) || (pin > 4))
- return (PCI_INVALID_IRQ);
-
- /*
- * Scan the entry table for a contender
- */
- for (i = 0, pe = &pci_route_table->pt_entry[0]; i < pci_route_count;
- i++, pe++) {
- if ((bus != pe->pe_bus) || (device != pe->pe_device))
- continue;
-
- /*
- * A link of 0 means that this intpin is not connected to
- * any other device's interrupt pins and is not connected to
- * any of the Interrupt Router's interrupt pins, so we can't
- * route it.
- */
- if (pe->pe_intpin[pin - 1].link == 0)
- continue;
-
- if (pci_cfgintr_valid(pe, pin, oldirq)) {
- kprintf("pci_cfgintr: %d:%d INT%c BIOS irq %d\n", bus,
- device, 'A' + pin - 1, oldirq);
- return (oldirq);
- }
-
- /*
- * We try to find a linked interrupt, then we look to see
- * if the interrupt is uniquely routed, then we look for
- * a virgin interrupt. The virgin interrupt should return
- * an interrupt we can route, but if that fails, maybe we
- * should try harder to route a different interrupt.
- * However, experience has shown that that's rarely the
- * failure mode we see.
- */
- irq = pci_cfgintr_linked(pe, pin);
- if (irq != PCI_INVALID_IRQ)
- already = 1;
- if (irq == PCI_INVALID_IRQ) {
- irq = pci_cfgintr_unique(pe, pin);
- if (irq != PCI_INVALID_IRQ)
- errok = 1;
- }
- if (irq == PCI_INVALID_IRQ)
- irq = pci_cfgintr_virgin(pe, pin);
-
- if (irq == PCI_INVALID_IRQ)
- break;
-
- if (pci_disable_bios_route != 0)
- break;
- /*
- * Ask the BIOS to route the interrupt. If we picked an
- * interrupt that failed, we should really try other
- * choices that the BIOS offers us.
- *
- * For uniquely routed interrupts, we need to try
- * to route them on some machines. Yet other machines
- * fail to route, so we have to pretend that in that
- * case it worked. Isn't PC hardware fun?
- *
- * NOTE: if we want to whack hardware to do this, then
- * I think the right way to do that would be to have
- * bridge drivers that do this. I'm not sure that the
- * $PIR table would be valid for those interrupt
- * routers.
- */
- args.eax = PCIBIOS_ROUTE_INTERRUPT;
- args.ebx = (bus << 8) | (device << 3);
- /* pin value is 0xa - 0xd */
- args.ecx = (irq << 8) | (0xa + pin -1);
- if (!already &&
- bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)) &&
- !errok) {
- PRVERB(("pci_cfgintr: ROUTE_INTERRUPT failed.\n"));
- return (PCI_INVALID_IRQ);
- }
- kprintf("pci_cfgintr: %d:%d INT%c routed to irq %d\n", bus,
- device, 'A' + pin - 1, irq);
- return(irq);
- }
-
- PRVERB(("pci_cfgintr: can't route an interrupt to %d:%d INT%c oldirq=%d\n", bus,
- device, 'A' + pin - 1, oldirq));
- return (PCI_INVALID_IRQ);
-}
-
-/*
- * Check to see if an existing IRQ setting is valid.
- */
-static int
-pci_cfgintr_valid(struct PIR_entry *pe, int pin, int irq)
-{
- uint32_t irqmask;
-
- if (!PCI_INTERRUPT_VALID(irq))
- return (0);
- irqmask = pe->pe_intpin[pin - 1].irqs;
- if (irqmask & (1 << irq)) {
- PRVERB(("pci_cfgintr_valid: BIOS irq %d is valid\n", irq));
- return (1);
- }
- return (0);
-}
-
-/*
- * Look to see if the routing table claims this pin is uniquely routed.
- */
-static int
-pci_cfgintr_unique(struct PIR_entry *pe, int pin)
-{
- int irq;
- uint32_t irqmask;
-
- irqmask = pe->pe_intpin[pin - 1].irqs;
- if(irqmask != 0 && powerof2(irqmask)) {
- irq = ffs(irqmask) - 1;
- PRVERB(("pci_cfgintr_unique: hard-routed to irq %d\n", irq));
- return (irq);
- }
- return (PCI_INVALID_IRQ);
-}
-
-/*
- * Look for another device which shares the same link byte and
- * already has a unique IRQ, or which has had one routed already.
- */
-static int
-pci_cfgintr_linked(struct PIR_entry *pe, int pin)
-{
- struct PIR_entry *oe;
- struct PIR_intpin *pi;
- int i, j, irq;
-
- /*
- * Scan table slots.
- */
- for (i = 0, oe = &pci_route_table->pt_entry[0]; i < pci_route_count;
- i++, oe++) {
- /* scan interrupt pins */
- for (j = 0, pi = &oe->pe_intpin[0]; j < 4; j++, pi++) {
-
- /* don't look at the entry we're trying to match */
- if ((pe == oe) && (i == (pin - 1)))
- continue;
- /* compare link bytes */
- if (pi->link != pe->pe_intpin[pin - 1].link)
- continue;
- /* link destination mapped to a unique interrupt? */
- if (pi->irqs != 0 && powerof2(pi->irqs)) {
- irq = ffs(pi->irqs) - 1;
- PRVERB(("pci_cfgintr_linked: linked (%x) to hard-routed irq %d\n",
- pi->link, irq));
- return(irq);
- }
-
- /*
- * look for the real PCI device that matches this
- * table entry
- */
- irq = pci_cfgintr_search(pe, oe->pe_bus, oe->pe_device,
- j + 1, pin);
- if (irq != PCI_INVALID_IRQ)
- return (irq);
- }
- }
- return (PCI_INVALID_IRQ);
-}
-
-/*
- * Scan for the real PCI device at (bus)/(device) using intpin (matchpin) and
- * see if it has already been assigned an interrupt.
- */
-static int
-pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin)
-{
- devclass_t pci_devclass;
- device_t *pci_devices;
- int pci_count;
- device_t *pci_children;
- int pci_childcount;
- device_t *busp, *childp;
- int i, j, irq;
-
- /*
- * Find all the PCI busses.
- */
- pci_count = 0;
- if ((pci_devclass = devclass_find("pci")) != NULL)
- devclass_get_devices(pci_devclass, &pci_devices, &pci_count);
-
- /*
- * Scan all the PCI busses/devices looking for this one.
- */
- irq = PCI_INVALID_IRQ;
- for (i = 0, busp = pci_devices; (i < pci_count) && (irq == PCI_INVALID_IRQ);
- i++, busp++) {
- pci_childcount = 0;
- device_get_children(*busp, &pci_children, &pci_childcount);
-
- for (j = 0, childp = pci_children; j < pci_childcount; j++,
- childp++) {
- if ((pci_get_bus(*childp) == bus) &&
- (pci_get_slot(*childp) == device) &&
- (pci_get_intpin(*childp) == matchpin)) {
- irq = pci_map_intline(pci_get_irq(*childp));
- if (irq != PCI_INVALID_IRQ)
- PRVERB(("pci_cfgintr_search: linked (%x) to configured irq %d at %d:%d:%d\n",
- pe->pe_intpin[pin - 1].link, irq,
- pci_get_bus(*childp),
- pci_get_slot(*childp),
- pci_get_function(*childp)));
- break;
- }
- }
- if (pci_children != NULL)
- kfree(pci_children, M_TEMP);
- }
- if (pci_devices != NULL)
- kfree(pci_devices, M_TEMP);
- return (irq);
-}
-
-/*
- * Pick a suitable IRQ from those listed as routable to this device.
- */
-static int
-pci_cfgintr_virgin(struct PIR_entry *pe, int pin)
-{
- int irq, ibit;
-
- /*
- * first scan the set of PCI-only interrupts and see if any of these
- * are routable
- */
- for (irq = 0; irq < 16; irq++) {
- ibit = (1 << irq);
-
- /* can we use this interrupt? */
- if ((pci_route_table->pt_header.ph_pci_irqs & ibit) &&
- (pe->pe_intpin[pin - 1].irqs & ibit)) {
- PRVERB(("pci_cfgintr_virgin: using routable PCI-only interrupt %d\n", irq));
- return (irq);
- }
- }
-
- /* life is tough, so just pick an interrupt */
- for (irq = 0; irq < 16; irq++) {
- ibit = (1 << irq);
-
- if (pe->pe_intpin[pin - 1].irqs & ibit) {
- PRVERB(("pci_cfgintr_virgin: using routable interrupt %d\n", irq));
- return (irq);
- }
- }
- return (PCI_INVALID_IRQ);
-}
-
-static void
-pci_print_irqmask(u_int16_t irqs)
-{
- int i, first;
-
- if (irqs == 0) {
- kprintf("none");
- return;
- }
- first = 1;
- for (i = 0; i < 16; i++, irqs >>= 1)
- if (irqs & 1) {
- if (!first)
- kprintf(" ");
- else
- first = 0;
- kprintf("%d", i);
- }
-}
-
-/*
- * Dump the contents of a PCI BIOS Interrupt Routing Table to the console.
- */
-static void
-pci_print_route_table(struct PIR_table *ptr, int size)
-{
- struct PIR_entry *entry;
- struct PIR_intpin *intpin;
- int i, pin;
-
- kprintf("PCI-Only Interrupts: ");
- pci_print_irqmask(ptr->pt_header.ph_pci_irqs);
- kprintf("\nLocation Bus Device Pin Link IRQs\n");
- entry = &ptr->pt_entry[0];
- for (i = 0; i < size; i++, entry++) {
- intpin = &entry->pe_intpin[0];
- for (pin = 0; pin < 4; pin++, intpin++)
- if (intpin->link != 0) {
- if (entry->pe_slot == 0)
- kprintf("embedded ");
- else
- kprintf("slot %-3d ", entry->pe_slot);
- kprintf(" %3d %3d %c 0x%02x ",
- entry->pe_bus, entry->pe_device,
- 'A' + pin, intpin->link);
- pci_print_irqmask(intpin->irqs);
- kprintf("\n");
- }
- }
-}
-
-/*
- * See if any interrupts for a given PCI bus are routed in the PIR. Don't
- * even bother looking if the BIOS doesn't support routing anyways.
- */
-int
-pci_probe_route_table(int bus)
-{
- int i;
- u_int16_t v;
-
- v = pcibios_get_version();
- if (v < 0x0210)
- return (0);
- for (i = 0; i < pci_route_count; i++)
- if (pci_route_table->pt_entry[i].pe_bus == bus)
- return (1);
- return (0);
-}
-
-#endif
-
-/*
- * Configuration space access using direct register operations
- */
-
-/* enable configuration space accesses and return data port address */
-static int
-pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
-{
- int dataport = 0;
-
- if (bus <= PCI_BUSMAX
- && slot < devmax
- && func <= PCI_FUNCMAX
- && reg <= PCI_REGMAX
- && bytes != 3
- && (unsigned) bytes <= 4
- && (reg & (bytes - 1)) == 0) {
- switch (cfgmech) {
- case 1:
- outl(CONF1_ADDR_PORT, (1 << 31)
- | (bus << 16) | (slot << 11)
- | (func << 8) | (reg & ~0x03));
- dataport = CONF1_DATA_PORT + (reg & 0x03);
- break;
- case 2:
- outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
- outb(CONF2_FORWARD_PORT, bus);
- dataport = 0xc000 | (slot << 8) | reg;
- break;
- }
- }
- return (dataport);
-}
-
-/* disable configuration space accesses */
-static void
-pci_cfgdisable(void)
-{
- switch (cfgmech) {
- case 1:
- outl(CONF1_ADDR_PORT, 0);
- break;
- case 2:
- outb(CONF2_ENABLE_PORT, 0);
- outb(CONF2_FORWARD_PORT, 0);
- break;
- }
-}
-
-static int
-pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
-{
- int data = -1;
- int port;
-
- port = pci_cfgenable(bus, slot, func, reg, bytes);
- if (port != 0) {
- switch (bytes) {
- case 1:
- data = inb(port);
- break;
- case 2:
- data = inw(port);
- break;
- case 4:
- data = inl(port);
- break;
- }
- pci_cfgdisable();
- }
- return (data);
-}
-
-static void
-pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
-{
- int port;
-
- port = pci_cfgenable(bus, slot, func, reg, bytes);
- if (port != 0) {
- switch (bytes) {
- case 1:
- outb(port, data);
- break;
- case 2:
- outw(port, data);
- break;
- case 4:
- outl(port, data);
- break;
- }
- pci_cfgdisable();
- }
-}
-
-/* check whether the configuration mechanism has been correctly identified */
-static int
-pci_cfgcheck(int maxdev)
-{
- uint32_t id, class;
- uint8_t header;
- uint8_t device;
- int port;
-
- if (bootverbose)
- kprintf("pci_cfgcheck:\tdevice ");
-
- for (device = 0; device < maxdev; device++) {
- if (bootverbose)
- kprintf("%d ", device);
-
- port = pci_cfgenable(0, device, 0, 0, 4);
- id = inl(port);
- if (id == 0 || id == 0xffffffff)
- continue;
-
- port = pci_cfgenable(0, device, 0, 8, 4);
- class = inl(port) >> 8;
- if (bootverbose)
- kprintf("[class=%06x] ", class);
- if (class == 0 || (class & 0xf870ff) != 0)
- continue;
-
- port = pci_cfgenable(0, device, 0, 14, 1);
- header = inb(port);
- if (bootverbose)
- kprintf("[hdr=%02x] ", header);
- if ((header & 0x7e) != 0)
- continue;
-
- if (bootverbose)
- kprintf("is there (id=%08x)\n", id);
-
- pci_cfgdisable();
- return (1);
- }
- if (bootverbose)
- kprintf("-- nothing found\n");
-
- pci_cfgdisable();
- return (0);
-}
-
-static int
-pcireg_cfgopen(void)
-{
- uint32_t mode1res,oldval1;
- uint8_t mode2res,oldval2;
-
- oldval1 = inl(CONF1_ADDR_PORT);
-
- if (bootverbose) {
- kprintf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
- oldval1);
- }
-
- if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
-
- cfgmech = 1;
- devmax = 32;
-
- outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
- DELAY(1);
- mode1res = inl(CONF1_ADDR_PORT);
- outl(CONF1_ADDR_PORT, oldval1);
-
- if (bootverbose)
- kprintf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",
- mode1res, CONF1_ENABLE_CHK);
-
- if (mode1res) {
- if (pci_cfgcheck(32))
- return (cfgmech);
- }
-
- outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
- mode1res = inl(CONF1_ADDR_PORT);
- outl(CONF1_ADDR_PORT, oldval1);
-
- if (bootverbose)
- kprintf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",
- mode1res, CONF1_ENABLE_CHK1);
-
- if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
- if (pci_cfgcheck(32))
- return (cfgmech);
- }
- }
-
- oldval2 = inb(CONF2_ENABLE_PORT);
-
- if (bootverbose) {
- kprintf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
- oldval2);
- }
-
- if ((oldval2 & 0xf0) == 0) {
-
- cfgmech = 2;
- devmax = 16;
-
- outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
- mode2res = inb(CONF2_ENABLE_PORT);
- outb(CONF2_ENABLE_PORT, oldval2);
-
- if (bootverbose)
- kprintf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
- mode2res, CONF2_ENABLE_CHK);
-
- if (mode2res == CONF2_ENABLE_RES) {
- if (bootverbose)
- kprintf("pci_open(2a):\tnow trying mechanism 2\n");
-
- if (pci_cfgcheck(16))
- return (cfgmech);
- }
- }
-
- cfgmech = 0;
- devmax = 0;
- return (cfgmech);
-}
+++ /dev/null
-/*
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
- * 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 unmodified, 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.
- *
- * 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.
- *
- * $FreeBSD: src/sys/i386/include/pci_cfgreg.h,v 1.4.2.1 2001/07/28 05:55:07 imp Exp $
- * $DragonFly: src/sys/bus/pci/amd64/pci_cfgreg.h,v 1.1 2008/08/02 05:22:19 dillon Exp $
- *
- */
-
-#ifndef _MACHINE_PCI_CFGREG_H_
-#define _MACHINE_PCI_CFGREG_H_
-
-#define CONF1_ADDR_PORT 0x0cf8
-#define CONF1_DATA_PORT 0x0cfc
-
-#define CONF1_ENABLE 0x80000000ul
-#define CONF1_ENABLE_CHK 0x80000000ul
-#define CONF1_ENABLE_MSK 0x7f000000ul
-#define CONF1_ENABLE_CHK1 0xff000001ul
-#define CONF1_ENABLE_MSK1 0x80000001ul
-#define CONF1_ENABLE_RES1 0x80000000ul
-
-#define CONF2_ENABLE_PORT 0x0cf8
-#define CONF2_FORWARD_PORT 0x0cfa
-
-#define CONF2_ENABLE_CHK 0x0e
-#define CONF2_ENABLE_RES 0x0e
-
-int pci_cfgregopen(void);
-u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes);
-void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes);
-int pci_cfgintr(int bus, int device, int pin, int oldirq);
-int pci_probe_route_table(int bus);
-
-#define PCI_INVALID_IRQ 255
-#define PCI_INTERRUPT_VALID(x) ((x) != PCI_INVALID_IRQ)
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
- * 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 unmodified, 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.
- *
- * 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.
- *
- * $FreeBSD: src/sys/i386/isa/pcibus.c,v 1.57.2.12 2003/08/07 06:19:26 imp Exp $
- * $DragonFly: src/sys/bus/pci/amd64/pcibus.c,v 1.2 2008/09/05 10:39:36 hasso Exp $
- *
- */
-
-#include "opt_pci.h"
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/bus.h>
-#include <sys/kernel.h>
-#include <sys/malloc.h>
-#include <sys/sysctl.h>
-
-#include <bus/pci/pcivar.h>
-#include <bus/pci/pcireg.h>
-#include "pcibus.h"
-#include <bus/isa/isavar.h>
-#include "pci_cfgreg.h"
-#include <machine/md_var.h>
-#include <machine/nexusvar.h>
-
-#include "pcib_if.h"
-
-static u_int32_t nexus_pcib_read_config(device_t, int, int, int, int, int);
-
-/*
- * Figure out if a PCI entity is a host bridge, return its name or NULL.
- */
-static const char *
-nexus_legacypci_is_host_bridge(int bus, int slot, int func,
- u_int32_t id, u_int8_t class, u_int8_t subclass,
- u_int8_t *busnum)
-{
- const char *s = NULL;
- static u_int8_t pxb[4]; /* hack for 450nx */
-
- *busnum = 0;
-
- switch (id) {
- case 0x12258086:
- s = "Intel 824?? host to PCI bridge";
- /* XXX This is a guess */
- /* *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x41, 1); */
- *busnum = bus;
- break;
- case 0x71208086:
- s = "Intel 82810 (i810 GMCH) Host To Hub bridge";
- break;
- case 0x71228086:
- s = "Intel 82810-DC100 (i810-DC100 GMCH) Host To Hub bridge";
- break;
- case 0x71248086:
- s = "Intel 82810E (i810E GMCH) Host To Hub bridge";
- break;
- case 0x71808086:
- s = "Intel 82443LX (440 LX) host to PCI bridge";
- break;
- case 0x71908086:
- s = "Intel 82443BX (440 BX) host to PCI bridge";
- break;
- case 0x71928086:
- s = "Intel 82443BX host to PCI bridge (AGP disabled)";
- break;
- case 0x71948086:
- s = "Intel 82443MX host to PCI bridge";
- break;
- case 0x71a08086:
- s = "Intel 82443GX host to PCI bridge";
- break;
- case 0x71a18086:
- s = "Intel 82443GX host to AGP bridge";
- break;
- case 0x71a28086:
- s = "Intel 82443GX host to PCI bridge (AGP disabled)";
- break;
- case 0x84c48086:
- s = "Intel 82454KX/GX (Orion) host to PCI bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x4a, 1);
- break;
- case 0x84ca8086:
- /*
- * For the 450nx chipset, there is a whole bundle of
- * things pretending to be host bridges. The MIOC will
- * be seen first and isn't really a pci bridge (the
- * actual busses are attached to the PXB's). We need to
- * read the registers of the MIOC to figure out the
- * bus numbers for the PXB channels.
- *
- * Since the MIOC doesn't have a pci bus attached, we
- * pretend it wasn't there.
- */
- pxb[0] = nexus_pcib_read_config(0, bus, slot, func,
- 0xd0, 1); /* BUSNO[0] */
- pxb[1] = nexus_pcib_read_config(0, bus, slot, func,
- 0xd1, 1) + 1; /* SUBA[0]+1 */
- pxb[2] = nexus_pcib_read_config(0, bus, slot, func,
- 0xd3, 1); /* BUSNO[1] */
- pxb[3] = nexus_pcib_read_config(0, bus, slot, func,
- 0xd4, 1) + 1; /* SUBA[1]+1 */
- return NULL;
- case 0x84cb8086:
- switch (slot) {
- case 0x12:
- s = "Intel 82454NX PXB#0, Bus#A";
- *busnum = pxb[0];
- break;
- case 0x13:
- s = "Intel 82454NX PXB#0, Bus#B";
- *busnum = pxb[1];
- break;
- case 0x14:
- s = "Intel 82454NX PXB#1, Bus#A";
- *busnum = pxb[2];
- break;
- case 0x15:
- s = "Intel 82454NX PXB#1, Bus#B";
- *busnum = pxb[3];
- break;
- }
- break;
- case 0x1A308086:
- s = "Intel 82845 Host to PCI bridge";
- break;
-
- /* AMD -- vendor 0x1022 */
- case 0x30001022:
- s = "AMD Elan SC520 host to PCI bridge";
-#ifdef CPU_ELAN
- init_AMD_Elan_sc520();
-#else
- kprintf("*** WARNING: kernel option CPU_ELAN missing");
- kprintf("-- timekeeping may be wrong\n");
-#endif
- break;
- case 0x70061022:
- s = "AMD-751 host to PCI bridge";
- break;
- case 0x700e1022:
- s = "AMD-761 host to PCI bridge";
- break;
-
- /* SiS -- vendor 0x1039 */
- case 0x04961039:
- s = "SiS 85c496";
- break;
- case 0x04061039:
- s = "SiS 85c501";
- break;
- case 0x06011039:
- s = "SiS 85c601";
- break;
- case 0x55911039:
- s = "SiS 5591 host to PCI bridge";
- break;
- case 0x00011039:
- s = "SiS 5591 host to AGP bridge";
- break;
-
- /* VLSI -- vendor 0x1004 */
- case 0x00051004:
- s = "VLSI 82C592 Host to PCI bridge";
- break;
-
- /* XXX Here is MVP3, I got the datasheet but NO M/B to test it */
- /* totally. Please let me know if anything wrong. -F */
- /* XXX need info on the MVP3 -- any takers? */
- case 0x05981106:
- s = "VIA 82C598MVP (Apollo MVP3) host bridge";
- break;
-
- /* AcerLabs -- vendor 0x10b9 */
- /* Funny : The datasheet told me vendor id is "10b8",sub-vendor */
- /* id is '10b9" but the register always shows "10b9". -Foxfair */
- case 0x154110b9:
- s = "AcerLabs M1541 (Aladdin-V) PCI host bridge";
- break;
-
- /* OPTi -- vendor 0x1045 */
- case 0xc7011045:
- s = "OPTi 82C700 host to PCI bridge";
- break;
- case 0xc8221045:
- s = "OPTi 82C822 host to PCI Bridge";
- break;
-
- /* ServerWorks -- vendor 0x1166 */
- case 0x00051166:
- s = "ServerWorks NB6536 2.0HE host to PCI bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
- break;
-
- case 0x00061166:
- /* FALLTHROUGH */
- case 0x00081166:
- /* FALLTHROUGH */
- case 0x02011166:
- /* FALLTHROUGH */
- case 0x010f1014: /* IBM re-badged ServerWorks chipset */
- /* FALLTHROUGH */
- s = "ServerWorks host to PCI bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
- break;
-
- case 0x00091166:
- s = "ServerWorks NB6635 3.0LE host to PCI bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
- break;
-
- case 0x00101166:
- s = "ServerWorks CIOB30 host to PCI bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
- break;
-
- case 0x00111166:
- /* FALLTHROUGH */
- case 0x03021014: /* IBM re-badged ServerWorks chipset */
- s = "ServerWorks CMIC-HE host to PCI-X bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
- break;
-
- /* XXX unknown chipset, but working */
- case 0x00171166:
- /* FALLTHROUGH */
- case 0x01011166:
- s = "ServerWorks host to PCI bridge(unknown chipset)";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
- break;
-
- /* Integrated Micro Solutions -- vendor 0x10e0 */
- case 0x884910e0:
- s = "Integrated Micro Solutions VL Bridge";
- break;
-
- default:
- if (class == PCIC_BRIDGE && subclass == PCIS_BRIDGE_HOST)
- s = "Host to PCI bridge";
- break;
- }
-
- return s;
-}
-
-/*
- * Identify the existance of the first pci bus and install a child to
- * nexus if we find it. Use an order of 1 so it gets probed after
- * any ACPI device installed under nexus. To avoid boot-time confusion,
- * we do not install any 'pcib' devices at this time.
- *
- * The identify method coupled with the driver spec of the same name
- * automatically installs it under the nexus.
- */
-static int
-nexus_legacypci_identify(driver_t *driver, device_t parent)
-{
- /*
- * Basically a static device, there's no point reinstalling it
- * on rescan.
- */
- if (device_get_state(parent) == DS_ATTACHED)
- return (0);
- if (device_get_state(parent) == DS_INPROGRESS)
- return (0);
-
- if (pci_cfgregopen() == 0)
- return (ENXIO);
-
- BUS_ADD_CHILD(parent, parent, 100, "legacypci", 0);
- return (0);
-}
-
-/*
- * Scan the first pci bus for host-pci bridges and add pcib instances
- * to the nexus for each bridge.
- */
-static int
-nexus_legacypci_probe(device_t dev)
-{
- int bus, slot, func;
- u_int8_t hdrtype;
- int found = 0;
- int pcifunchigh;
- int found824xx = 0;
- device_t child;
-
- /*
- * Do not install any pci busses ('pcib' devices) if the PCI
- * subsystem has already been claimed by someone else.
- */
- if (pcib_owner != NULL) {
- device_printf(dev, "PCI subsystem owned by %s, skipping scan\n",
- pcib_owner);
- return (ENXIO);
- }
- pcib_owner = "legacypci";
-
- if (pci_cfgregopen() == 0)
- return (ENXIO);
-
- /*
- * Scan away!
- */
- bus = 0;
- retry:
- for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
- func = 0;
- hdrtype = nexus_pcib_read_config(0, bus, slot, func,
- PCIR_HDRTYPE, 1);
- if ((hdrtype & ~PCIM_MFDEV) > 2)
- continue;
- if (hdrtype & PCIM_MFDEV)
- pcifunchigh = 7;
- else
- pcifunchigh = 0;
- for (func = 0; func <= pcifunchigh; func++) {
- /*
- * Read the IDs and class from the device.
- */
- u_int32_t id;
- u_int8_t class, subclass, busnum;
- const char *s;
- device_t *devs;
- int ndevs, i;
-
- id = nexus_pcib_read_config(0, bus, slot, func,
- PCIR_DEVVENDOR, 4);
- if (id == -1)
- continue;
- class = nexus_pcib_read_config(0, bus, slot, func,
- PCIR_CLASS, 1);
- subclass = nexus_pcib_read_config(0, bus, slot, func,
- PCIR_SUBCLASS, 1);
-
- s = nexus_legacypci_is_host_bridge(bus, slot, func,
- id, class, subclass,
- &busnum);
- if (s == NULL)
- continue;
-
- /*
- * Check to see if the physical bus has already
- * been seen. Eg: hybrid 32 and 64 bit host
- * bridges to the same logical bus.
- */
- if (device_get_children(dev, &devs, &ndevs) == 0) {
- for (i = 0; s != NULL && i < ndevs; i++) {
- if (strcmp(device_get_name(devs[i]),
- "pcib") != 0)
- continue;
- if (nexus_get_pcibus(devs[i]) == busnum)
- s = NULL;
- }
- kfree(devs, M_TEMP);
- }
-
- if (s == NULL)
- continue;
- /*
- * Add at priority 100+busnum to keep the scanning
- * order sane in the boot dmesg output.
- */
- child = BUS_ADD_CHILD(dev, dev, 100 + busnum,
- "pcib", busnum);
- device_set_desc(child, s);
- nexus_set_pcibus(child, busnum);
-
- found = 1;
- if (id == 0x12258086)
- found824xx = 1;
- }
- }
- if (found824xx && bus == 0) {
- bus++;
- goto retry;
- }
-
-#if 0
- /*
- * Now that we have installed the main PCI bridges, go
- * probe and attach each one.
- */
- bus_generic_attach(dev);
-#endif
-
- /*
- * Make sure we add at least one bridge since some old
- * hardware doesn't actually have a host-pci bridge device.
- * Note that pci_cfgregopen() thinks we have PCI devices..
- */
- if (!found) {
- if (bootverbose) {
- kprintf("nexus_pcib_identify: no bridge found, "
- "adding pcib0 anyway\n");
- }
- child = BUS_ADD_CHILD(dev, dev, 100, "pcib", 0);
- nexus_set_pcibus(child, 0);
- }
- return (0);
-}
-
-static int
-nexus_legacypci_attach(device_t dev)
-{
- bus_generic_attach(dev);
- return (0);
-}
-
-#ifdef PCI_MAP_FIXUP
-
-SYSCTL_DECL(_hw_pci);
-
-static unsigned long legacy_host_mem_start = 0xffffffff80000000;
-/* XXX need TUNABLE_ULONG? */
-TUNABLE_INT("hw.pci.host_mem_start", (int *)&legacy_host_mem_start);
-SYSCTL_ULONG(_hw_pci, OID_AUTO, host_mem_start, CTLFLAG_RD,
- &legacy_host_mem_start, 0x80000000,
- "Limit the host bridge memory to being above this address. "
- "Must be\n"
- "set at boot via hw.pci.host_mem_start tunable.");
-
-static struct resource *
-nexus_legacypci_alloc_resource(device_t dev, device_t child, int type, int *rid,
- u_long start, u_long end, u_long count,
- u_int flags)
-{
- /*
- * If no memory preference is given, use upper 32MB slot most
- * bioses use for their memory window. Typically other bridges
- * before us get in the way to assert their preferences on memory.
- * Hardcoding like this sucks, so a more MD/MI way needs to be
- * found to do it. This is typically only used on older laptops
- * that don't have pci busses behind pci bridge, so assuming > 32MB
- * is likely OK.
- *
- * However, this can cause problems for other chipsets, so we make
- * this tunable by hw.pci.host_mem_start.
- */
- if (type == SYS_RES_MEMORY && start == 0UL && end == ~0UL)
- start = legacy_host_mem_start;
- if (type == SYS_RES_IOPORT && start == 0UL && end == ~0UL)
- start = 0x1000;
- return bus_generic_alloc_resource(dev, child, type, rid,
- start, end, count, flags);
-}
-
-#endif /* PCI_MAP_FIXUP */
-
-static device_method_t legacypci_methods[] = {
- /* Device interface */
- DEVMETHOD(device_identify, nexus_legacypci_identify),
- DEVMETHOD(device_probe, nexus_legacypci_probe),
- DEVMETHOD(device_attach, nexus_legacypci_attach),
- DEVMETHOD(device_shutdown, bus_generic_shutdown),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
-
- /*
- * Bus interface - propogate through to the nexus. Note that
- * this means devices under us will have nexus ivars.
- */
- DEVMETHOD(bus_add_child, bus_generic_add_child),
- DEVMETHOD(bus_print_child, bus_generic_print_child),
- DEVMETHOD(bus_read_ivar, bus_generic_read_ivar),
- DEVMETHOD(bus_write_ivar, bus_generic_write_ivar),
-#ifdef PCI_MAP_FIXUP
- DEVMETHOD(bus_alloc_resource, nexus_legacypci_alloc_resource),
-#else
- DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
-#endif
- DEVMETHOD(bus_release_resource, bus_generic_release_resource),
- DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
- DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
- DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
- DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
- DEVMETHOD(bus_set_resource, bus_generic_set_resource),
- DEVMETHOD(bus_get_resource, bus_generic_get_resource),
- DEVMETHOD(bus_delete_resource, bus_generic_delete_resource),
- { 0, 0 }
-};
-
-static driver_t legacypci_driver = {
- "legacypci",
- legacypci_methods,
- 1,
-};
-
-static devclass_t legacypci_devclass;
-
-DRIVER_MODULE(legacypci, nexus, legacypci_driver, legacypci_devclass, 0, 0);
-
-/*
- * Legacypci Host-Bridge PCI BUS support. The underlying pcib devices
- * will only exist if we actually control the PCI bus. The actual PCI
- * bus driver is attached in our attach routine.
- *
- * There is no identify function because the legacypci placeholder will
- * have already scanned and added PCIB devices for the host-bridges found.
- */
-static int
-nexus_pcib_maxslots(device_t dev)
-{
- return 31;
-}
-
-/*
- * Read configuration space register.
- */
-static u_int32_t
-nexus_pcib_read_config(device_t dev, int bus, int slot, int func,
- int reg, int bytes)
-{
- return (pci_cfgregread(bus, slot, func, reg, bytes));
-}
-
-/*
- * Write configuration space register.
- */
-static void
-nexus_pcib_write_config(device_t dev, int bus, int slot, int func,
- int reg, u_int32_t data, int bytes)
-{
- pci_cfgregwrite(bus, slot, func, reg, data, bytes);
-}
-
-/*
- * Stack a pci device on top of the pci bridge bus device.
- */
-static int
-nexus_pcib_probe(device_t dev)
-{
- BUS_ADD_CHILD(dev, dev, 0, "pci", device_get_unit(dev));
- return (0);
-}
-
-static int
-nexus_pcib_attach(device_t dev)
-{
- int error;
-
- error = bus_generic_attach(dev);
- return (error);
-}
-
-/* route interrupt */
-
-static int
-nexus_pcib_route_interrupt(device_t pcib, device_t dev, int pin)
-{
- return(pci_cfgintr(pci_get_bus(dev), pci_get_slot(dev), pin,
- pci_get_irq(dev)));
-}
-
-static device_method_t nexus_pcib_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, nexus_pcib_probe),
- DEVMETHOD(device_attach, nexus_pcib_attach),
- DEVMETHOD(device_shutdown, bus_generic_shutdown),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
-
- /*
- * Bus interface - propogate through to the nexus. Note
- * that this means we will get nexus-managed ivars.
- */
- DEVMETHOD(bus_add_child, bus_generic_add_child),
- DEVMETHOD(bus_print_child, bus_generic_print_child),
- DEVMETHOD(bus_read_ivar, bus_generic_read_ivar),
- DEVMETHOD(bus_write_ivar, bus_generic_write_ivar),
- DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
- DEVMETHOD(bus_release_resource, bus_generic_release_resource),
- DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
- DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
- DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
- DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
-
- /* pcib interface */
- DEVMETHOD(pcib_maxslots, nexus_pcib_maxslots),
- DEVMETHOD(pcib_read_config, nexus_pcib_read_config),
- DEVMETHOD(pcib_write_config, nexus_pcib_write_config),
- DEVMETHOD(pcib_route_interrupt, nexus_pcib_route_interrupt),
-
- { 0, 0 }
-};
-
-static driver_t nexus_pcib_driver = {
- "pcib",
- nexus_pcib_methods,
- 1,
-};
-
-static devclass_t pcib_devclass;
-
-DRIVER_MODULE(pcib, legacypci, nexus_pcib_driver, pcib_devclass, 0, 0);
-
-
-/*
- * Provide a device to "eat" the host->pci bridges that we dug up above
- * and stop them showing up twice on the probes. This also stops them
- * showing up as 'none' in pciconf -l.
- *
- * Return an ultra-low priority so other devices can attach the bus before
- * our dummy attach.
- *
- * XXX may have to disable the registration entirely to support module-loaded
- * bridges such as agp.ko.
- */
-static int
-pci_hostb_probe(device_t dev)
-{
- if (pci_get_class(dev) == PCIC_BRIDGE &&
- pci_get_subclass(dev) == PCIS_BRIDGE_HOST) {
- device_set_desc(dev, "Host to PCI bridge");
- device_quiet(dev);
- return -10000;
- }
- return (ENXIO);
-}
-
-static int
-pci_hostb_attach(device_t dev)
-{
- return (0);
-}
-
-static device_method_t pci_hostb_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, pci_hostb_probe),
- DEVMETHOD(device_attach, pci_hostb_attach),
- DEVMETHOD(device_shutdown, bus_generic_shutdown),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
-
- { 0, 0 }
-};
-static driver_t pci_hostb_driver = {
- "hostb",
- pci_hostb_methods,
- 1,
-};
-static devclass_t pci_hostb_devclass;
-
-DRIVER_MODULE(hostb, pci, pci_hostb_driver, pci_hostb_devclass, 0, 0);
-
-
-/*
- * Install placeholder to claim the resources owned by the
- * PCI bus interface. This could be used to extract the
- * config space registers in the extreme case where the PnP
- * ID is available and the PCI BIOS isn't, but for now we just
- * eat the PnP ID and do nothing else.
- *
- * XXX we should silence this probe, as it will generally confuse
- * people.
- */
-static struct isa_pnp_id pcibus_pnp_ids[] = {
- { 0x030ad041 /* PNP030A */, "PCI Bus" },
- { 0 }
-};
-
-static int
-pcibus_pnp_probe(device_t dev)
-{
- int result;
-
- if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, pcibus_pnp_ids)) <= 0)
- device_quiet(dev);
- return (result);
-}
-
-static int
-pcibus_pnp_attach(device_t dev)
-{
- return(0);
-}
-
-static device_method_t pcibus_pnp_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, pcibus_pnp_probe),
- DEVMETHOD(device_attach, pcibus_pnp_attach),
- DEVMETHOD(device_detach, bus_generic_detach),
- DEVMETHOD(device_shutdown, bus_generic_shutdown),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
- { 0, 0 }
-};
-
-static driver_t pcibus_pnp_driver = {
- "pcibus_pnp",
- pcibus_pnp_methods,
- 1, /* no softc */
-};
-
-static devclass_t pcibus_pnp_devclass;
-
-DRIVER_MODULE(pcibus_pnp, isa, pcibus_pnp_driver, pcibus_pnp_devclass, 0, 0);
-
+++ /dev/null
-/*
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
- * 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 unmodified, 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.
- *
- * 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.
- *
- * $FreeBSD: src/sys/i386/isa/pcibus.h,v 1.2 1999/08/28 00:44:59 peter Exp $
- * $DragonFly: src/sys/bus/pci/amd64/pcibus.h,v 1.1 2008/08/02 05:22:19 dillon Exp $
- *
- */
-
-#define CONF1_ADDR_PORT 0x0cf8
-#define CONF1_DATA_PORT 0x0cfc
-
-#define CONF1_ENABLE 0x80000000ul
-#define CONF1_ENABLE_CHK 0x80000000ul
-#define CONF1_ENABLE_MSK 0x7f000000ul
-#define CONF1_ENABLE_CHK1 0xff000001ul
-#define CONF1_ENABLE_MSK1 0x80000001ul
-#define CONF1_ENABLE_RES1 0x80000000ul
-
-#define CONF2_ENABLE_PORT 0x0cf8
-#define CONF2_FORWARD_PORT 0x0cfa
-
-#define CONF2_ENABLE_CHK 0x0e
-#define CONF2_ENABLE_RES 0x0e
#define TULIP_GP_ZX346_FULLDUPLEX 0x00000004 /* Full Duplex Sensed */
#define TULIP_GP_ZX34X_LB102 0x00000002 /* 100tx twister LB */
#define TULIP_GP_ZX34X_NLB101 0x00000001 /* PDT/PDR LB */
-#define TULIP_GP_ZX34X_INIT 0x00000009
+#define TULIP_GP_ZX34X_INIT 0x00000009
/*
* Compex's OUI. We need to twiddle a bit on their 21041 card.
--- /dev/null
+/*-
+ * Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier
+ * Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2000 BSDi
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ * __FBSDID("$FreeBSD: src/sys/dev/pci/eisa_pci.c,v 1.6.28.1 2009/04/15 03:14:26 kensmith Exp $");
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * PCI:EISA bridge support
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcireg.h>
+
+static int eisab_probe(device_t dev);
+static int eisab_attach(device_t dev);
+
+static device_method_t eisab_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, eisab_probe),
+ DEVMETHOD(device_attach, eisab_attach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ { 0, 0 }
+};
+
+static driver_t eisab_driver = {
+ "eisab",
+ eisab_methods,
+ 0,
+};
+
+static devclass_t eisab_devclass;
+
+DRIVER_MODULE(eisab, pci, eisab_driver, eisab_devclass, 0, 0);
+
+static int
+eisab_probe(device_t dev)
+{
+ int matched = 0;
+
+ /*
+ * Generic match by class/subclass.
+ */
+ if ((pci_get_class(dev) == PCIC_BRIDGE) &&
+ (pci_get_subclass(dev) == PCIS_BRIDGE_EISA))
+ matched = 1;
+
+ /*
+ * Some bridges don't correctly report their class.
+ */
+ switch (pci_get_devid(dev)) {
+ case 0x04828086: /* may show up as PCI-HOST or 0:0 */
+ matched = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (matched) {
+ device_set_desc(dev, "PCI-EISA bridge");
+ return(-10000);
+ }
+ return(ENXIO);
+}
+
+static int
+eisab_attach(device_t dev)
+{
+ /*
+ * Attach an EISA bus. Note that we can only have one EISA bus.
+ */
+ if (!devclass_get_device(devclass_find("eisa"), 0))
+ device_add_child(dev, "eisa", -1);
+
+ /*
+ * Attach an ISA bus as well, since the EISA bus may have ISA
+ * cards installed, and we may have no EISA support in the system.
+ */
+ if (!devclass_get_device(devclass_find("isa"), 0))
+ device_add_child(dev, "isa", -1);
+
+ bus_generic_attach(dev);
+
+ return(0);
+}
--- /dev/null
+/*-
+ * Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier
+ * Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2000 BSDi
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ * __FBSDID("$FreeBSD: src/sys/dev/pci/fixup_pci.c,v 1.7.8.1 2009/04/15 03:14:26 kensmith Exp $");
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/types.h>
+
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcireg.h>
+
+/*
+ * Chipset fixups.
+ *
+ * These routines are invoked during the probe phase for devices which
+ * typically don't have specific device drivers, but which require
+ * some cleaning up.
+ */
+
+static int fixup_pci_probe(device_t dev);
+static void fixwsc_natoma(device_t dev);
+static void fixc1_nforce2(device_t dev);
+
+static device_method_t fixup_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, fixup_pci_probe),
+ DEVMETHOD(device_attach, bus_generic_attach),
+ { 0, 0 }
+};
+
+static driver_t fixup_pci_driver = {
+ "fixup_pci",
+ fixup_pci_methods,
+ 0,
+};
+
+static devclass_t fixup_pci_devclass;
+
+DRIVER_MODULE(fixup_pci, pci, fixup_pci_driver, fixup_pci_devclass, 0, 0);
+
+static int
+fixup_pci_probe(device_t dev)
+{
+ switch (pci_get_devid(dev)) {
+ case 0x12378086: /* Intel 82440FX (Natoma) */
+ fixwsc_natoma(dev);
+ break;
+ case 0x01e010de: /* nVidia nForce2 */
+ fixc1_nforce2(dev);
+ break;
+ }
+ return(ENXIO);
+}
+
+static void
+fixwsc_natoma(device_t dev)
+{
+ int pmccfg;
+
+ pmccfg = pci_read_config(dev, 0x50, 2);
+#if defined(SMP)
+ if (pmccfg & 0x8000) {
+ kprintf("Correcting Natoma config for SMP\n");
+ pmccfg &= ~0x8000;
+ pci_write_config(dev, 0x50, pmccfg, 2);
+ }
+#else
+ if ((pmccfg & 0x8000) == 0) {
+ kprintf("Correcting Natoma config for non-SMP\n");
+ pmccfg |= 0x8000;
+ pci_write_config(dev, 0x50, pmccfg, 2);
+ }
+#endif
+}
+
+/*
+ * Set the SYSTEM_IDLE_TIMEOUT to 80 ns on nForce2 systems to work
+ * around a hang that is triggered when the CPU generates a very fast
+ * CONNECT/HALT cycle sequence. Specifically, the hang can result in
+ * the lapic timer being stopped.
+ *
+ * This requires changing the value for config register at offset 0x6c
+ * for the Host-PCI bridge at bus/dev/function 0/0/0:
+ *
+ * Chip Current Value New Value
+ * ---- ---------- ----------
+ * C17 0x1F0FFF01 0x1F01FF01
+ * C18D 0x9F0FFF01 0x9F01FF01
+ *
+ * We do this by always clearing the bits in 0x000e0000.
+ *
+ * See also: http://lkml.org/lkml/2004/5/3/157
+ */
+static void
+fixc1_nforce2(device_t dev)
+{
+ uint32_t val;
+
+ if (pci_get_bus(dev) == 0 && pci_get_slot(dev) == 0 &&
+ pci_get_function(dev) == 0) {
+ val = pci_read_config(dev, 0x6c, 4);
+ if (val & 0x000e0000) {
+ kprintf("Correcting nForce2 C1 CPU disconnect hangs\n");
+ val &= ~0x000e0000;
+ pci_write_config(dev, 0x6c, val, 4);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
+ * 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 unmodified, 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.
+ *
+ * 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.
+ * __FBSDID("$FreeBSD: src/sys/dev/pci/hostb_pci.c,v 1.1.8.1 2009/04/15 03:14:26 kensmith Exp $");
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <sys/types.h>
+#include <sys/systm.h>
+
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcireg.h>
+
+/*
+ * Provide a device to "eat" the host->pci bridge devices that show up
+ * on PCI busses and stop them showing up twice on the probes. This also
+ * stops them showing up as 'none' in pciconf -l. If the host bridge
+ * provides an AGP capability then we create a child agp device for the
+ * agp GART driver to attach to.
+ */
+static int
+pci_hostb_probe(device_t dev)
+{
+ u_int32_t id;
+ id = pci_get_devid(dev);
+
+ switch (id) {
+
+ /* VIA VT82C596 Power Managment Function */
+ case 0x30501106:
+ return (ENXIO);
+
+ default:
+ break;
+ }
+
+ if (pci_get_class(dev) == PCIC_BRIDGE &&
+ pci_get_subclass(dev) == PCIS_BRIDGE_HOST) {
+ device_set_desc(dev, "Host to PCI bridge");
+ device_quiet(dev);
+ return (-10000);
+ }
+ return (ENXIO);
+}
+
+static int
+pci_hostb_attach(device_t dev)
+{
+
+ bus_generic_probe(dev);
+
+ /*
+ * If AGP capabilities are present on this device, then create
+ * an AGP child.
+ */
+ if (pci_find_extcap(dev, PCIY_AGP, NULL) == 0)
+ device_add_child(dev, "agp", -1);
+ bus_generic_attach(dev);
+ return (0);
+}
+
+/* Bus interface. */
+
+static int
+pci_hostb_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+
+ return (BUS_READ_IVAR(device_get_parent(dev), dev, which, result));
+}
+
+static int
+pci_hostb_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
+{
+
+ return (EINVAL);
+}
+
+static struct resource *
+pci_hostb_alloc_resource(device_t dev, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+
+ return (bus_alloc_resource(dev, type, rid, start, end, count, flags));
+}
+
+static int
+pci_hostb_release_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *r)
+{
+
+ return (bus_release_resource(dev, type, rid, r));
+}
+
+/* PCI interface. */
+
+static uint32_t
+pci_hostb_read_config(device_t dev, device_t child, int reg, int width)
+{
+
+ return (pci_read_config(dev, reg, width));
+}
+
+static void
+pci_hostb_write_config(device_t dev, device_t child, int reg,
+ uint32_t val, int width)
+{
+
+ pci_write_config(dev, reg, val, width);
+}
+
+static int
+pci_hostb_enable_busmaster(device_t dev, device_t child)
+{
+
+ device_printf(dev, "child %s requested pci_enable_busmaster\n",
+ device_get_nameunit(child));
+ return (pci_enable_busmaster(dev));
+}
+
+static int
+pci_hostb_disable_busmaster(device_t dev, device_t child)
+{
+
+ device_printf(dev, "child %s requested pci_disable_busmaster\n",
+ device_get_nameunit(child));
+ return (pci_disable_busmaster(dev));
+}
+
+static int
+pci_hostb_enable_io(device_t dev, device_t child, int space)
+{
+
+ device_printf(dev, "child %s requested pci_enable_io\n",
+ device_get_nameunit(child));
+ return (pci_enable_io(dev, space));
+}
+
+static int
+pci_hostb_disable_io(device_t dev, device_t child, int space)
+{
+
+ device_printf(dev, "child %s requested pci_disable_io\n",
+ device_get_nameunit(child));
+ return (pci_disable_io(dev, space));
+}
+
+static int
+pci_hostb_set_powerstate(device_t dev, device_t child, int state)
+{
+
+ device_printf(dev, "child %s requested pci_set_powerstate\n",
+ device_get_nameunit(child));
+ return (pci_set_powerstate(dev, state));
+}
+
+static int
+pci_hostb_get_powerstate(device_t dev, device_t child)
+{
+
+ device_printf(dev, "child %s requested pci_get_powerstate\n",
+ device_get_nameunit(child));
+ return (pci_get_powerstate(dev));
+}
+
+static int
+pci_hostb_assign_interrupt(device_t dev, device_t child)
+{
+
+ device_printf(dev, "child %s requested pci_assign_interrupt\n",
+ device_get_nameunit(child));
+ return (PCI_ASSIGN_INTERRUPT(device_get_parent(dev), dev));
+}
+
+static int
+pci_hostb_find_extcap(device_t dev, device_t child, int capability,
+ int *capreg)
+{
+
+ return (pci_find_extcap(dev, capability, capreg));
+}
+
+static device_method_t pci_hostb_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pci_hostb_probe),
+ DEVMETHOD(device_attach, pci_hostb_attach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, pci_hostb_read_ivar),
+ DEVMETHOD(bus_write_ivar, pci_hostb_write_ivar),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ DEVMETHOD(bus_alloc_resource, pci_hostb_alloc_resource),
+ DEVMETHOD(bus_release_resource, pci_hostb_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+
+ /* PCI interface */
+ DEVMETHOD(pci_read_config, pci_hostb_read_config),
+ DEVMETHOD(pci_write_config, pci_hostb_write_config),
+ DEVMETHOD(pci_enable_busmaster, pci_hostb_enable_busmaster),
+ DEVMETHOD(pci_disable_busmaster, pci_hostb_disable_busmaster),
+ DEVMETHOD(pci_enable_io, pci_hostb_enable_io),
+ DEVMETHOD(pci_disable_io, pci_hostb_disable_io),
+ DEVMETHOD(pci_get_powerstate, pci_hostb_get_powerstate),
+ DEVMETHOD(pci_set_powerstate, pci_hostb_set_powerstate),
+ DEVMETHOD(pci_assign_interrupt, pci_hostb_assign_interrupt),
+ DEVMETHOD(pci_find_extcap, pci_hostb_find_extcap),
+
+ { 0, 0 }
+};
+
+static driver_t pci_hostb_driver = {
+ "hostb",
+ pci_hostb_methods,
+ 1,
+};
+
+static devclass_t pci_hostb_devclass;
+
+DRIVER_MODULE(hostb, pci, pci_hostb_driver, pci_hostb_devclass, 0, 0);
--- /dev/null
+/*-
+ * Copyright 1998 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. 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.
+ * __FBSDID("$FreeBSD: src/sys/i386/i386/legacy.c,v 1.63.2.1.4.1 2009/04/15 03:14:26 kensmith Exp $");
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * This code implements a system driver for legacy systems that do not
+ * support ACPI or when ACPI support is not present in the kernel.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+
+#undef DEV_MCA
+#ifdef DEV_MCA
+#include <i386/bios/mca_machdep.h>
+#endif
+
+#include <machine/legacyvar.h>
+
+static MALLOC_DEFINE(M_LEGACYDEV, "legacydrv", "legacy system device");
+struct legacy_device {
+ int lg_pcibus;
+};
+
+#define DEVTOAT(dev) ((struct legacy_device *)device_get_ivars(dev))
+
+static void legacy_identify(driver_t *driver, device_t parent);
+static int legacy_probe(device_t);
+static int legacy_attach(device_t);
+static int legacy_print_child(device_t, device_t);
+static device_t legacy_add_child(device_t bus, device_t parent, int order, const char *name,
+ int unit);
+static int legacy_read_ivar(device_t, device_t, int, uintptr_t *);
+static int legacy_write_ivar(device_t, device_t, int, uintptr_t);
+
+static device_method_t legacy_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, legacy_identify),
+ DEVMETHOD(device_probe, legacy_probe),
+ DEVMETHOD(device_attach, legacy_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, legacy_print_child),
+ DEVMETHOD(bus_add_child, legacy_add_child),
+ DEVMETHOD(bus_read_ivar, legacy_read_ivar),
+ DEVMETHOD(bus_write_ivar, legacy_write_ivar),
+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ { 0, 0 }
+};
+
+static driver_t legacy_driver = {
+ "legacy",
+ legacy_methods,
+ 1, /* no softc */
+};
+static devclass_t legacy_devclass;
+
+DRIVER_MODULE(legacy, nexus, legacy_driver, legacy_devclass, 0, 0);
+
+static void
+legacy_identify(driver_t *driver, device_t parent)
+{
+
+ /*
+ * Add child device with order of 11 so it gets probed
+ * after ACPI (which is at order 10).
+ */
+ if (BUS_ADD_CHILD(parent, parent, 11, "legacy", 0) == NULL)
+ panic("legacy: could not attach");
+}
+
+static int
+legacy_probe(device_t dev)
+{
+ device_t acpi;
+
+ /*
+ * Fail to probe if ACPI is ok.
+ */
+ acpi = devclass_get_device(devclass_find("acpi"), 0);
+ if (acpi != NULL && device_is_alive(acpi))
+ return (ENXIO);
+ device_set_desc(dev, "legacy system");
+ device_quiet(dev);
+ return (0);
+}
+
+static int
+legacy_attach(device_t dev)
+{
+ device_t child;
+
+ /*
+ * Let our child drivers identify any child devices that they
+ * can find. Once that is done attach any devices that we
+ * found.
+ */
+ bus_generic_probe(dev);
+ bus_generic_attach(dev);
+
+#ifndef PC98
+ /*
+ * If we didn't see EISA or ISA on a pci bridge, create some
+ * connection points now so they show up "on motherboard".
+ */
+ if (!devclass_get_device(devclass_find("eisa"), 0)) {
+ child = BUS_ADD_CHILD(dev, dev, 0, "eisa", 0);
+ if (child == NULL)
+ panic("legacy_attach eisa");
+ device_probe_and_attach(child);
+ }
+#endif
+#ifdef DEV_MCA
+ if (MCA_system && !devclass_get_device(devclass_find("mca"), 0)) {
+ child = BUS_ADD_CHILD(dev, dev, 0, "mca", 0);
+ if (child == 0)
+ panic("legacy_probe mca");
+ device_probe_and_attach(child);
+ }
+#endif
+ if (!devclass_get_device(devclass_find("isa"), 0)) {
+ child = BUS_ADD_CHILD(dev, dev, 0, "isa", 0);
+ if (child == NULL)
+ panic("legacy_attach isa");
+ device_probe_and_attach(child);
+ }
+
+ return 0;
+}
+
+static int
+legacy_print_child(device_t bus, device_t child)
+{
+ struct legacy_device *atdev = DEVTOAT(child);
+ int retval = 0;
+
+ retval += bus_print_child_header(bus, child);
+ if (atdev->lg_pcibus != -1)
+ retval += kprintf(" pcibus %d", atdev->lg_pcibus);
+ retval += kprintf(" on motherboard\n"); /* XXX "motherboard", ick */
+
+ return (retval);
+}
+
+static device_t
+legacy_add_child(device_t bus, device_t parent, int order, const char *name, int unit)
+{
+ device_t child;
+ struct legacy_device *atdev;
+ atdev = kmalloc(sizeof(struct legacy_device), M_LEGACYDEV,
+ M_NOWAIT | M_ZERO);
+ if (atdev == NULL)
+ return(NULL);
+ atdev->lg_pcibus = -1;
+
+ child = device_add_child_ordered(bus, order, name, unit);
+ if (child == NULL) {
+ kfree(atdev, M_LEGACYDEV);
+ }
+ else {
+ /* should we kfree this in legacy_child_detached? */
+ device_set_ivars(child, atdev);
+ }
+ return (child);
+}
+
+static int
+legacy_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ struct legacy_device *atdev = DEVTOAT(child);
+
+ switch (which) {
+ case LEGACY_IVAR_PCIDOMAIN:
+ *result = 0;
+ break;
+ case LEGACY_IVAR_PCIBUS:
+ *result = atdev->lg_pcibus;
+ break;
+ default:
+ return ENOENT;
+ }
+ return 0;
+}
+
+static int
+legacy_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
+{
+ struct legacy_device *atdev = DEVTOAT(child);
+
+ switch (which) {
+ case LEGACY_IVAR_PCIDOMAIN:
+ return EINVAL;
+ case LEGACY_IVAR_PCIBUS:
+ atdev->lg_pcibus = value;
+ break;
+ default:
+ return ENOENT;
+ }
+ return 0;
+}
+
+#ifdef notyet
+/*
+ * Legacy CPU attachment when ACPI is not available. Drivers like
+ * cpufreq(4) hang off this.
+ */
+static void cpu_identify(driver_t *driver, device_t parent);
+static int cpu_read_ivar(device_t dev, device_t child, int index,
+ uintptr_t *result);
+static device_t cpu_add_child(device_t bus, int order, const char *name,
+ int unit);
+static struct resource_list *cpu_get_rlist(device_t dev, device_t child);
+
+struct cpu_device {
+ struct resource_list cd_rl;
+ struct pcpu *cd_pcpu;
+};
+
+static device_method_t cpu_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, cpu_identify),
+ DEVMETHOD(device_probe, bus_generic_probe),
+ DEVMETHOD(device_attach, bus_generic_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_add_child, cpu_add_child),
+ DEVMETHOD(bus_read_ivar, cpu_read_ivar),
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_get_resource_list, cpu_get_rlist),
+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
+ DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ { 0, 0 }
+};
+
+static driver_t cpu_driver = {
+ "cpu",
+ cpu_methods,
+ 1, /* no softc */
+};
+static devclass_t cpu_devclass;
+DRIVER_MODULE(cpu, legacy, cpu_driver, cpu_devclass, 0, 0);
+
+static void
+cpu_identify(driver_t *driver, device_t parent)
+{
+ device_t child;
+ int i;
+
+ /*
+ * Attach a cpuX device for each CPU. We use an order of 150
+ * so that these devices are attached after the Host-PCI
+ * bridges (which are added at order 100).
+ */
+ for (i = 0; i <= mp_maxid; i++)
+ if (!CPU_ABSENT(i)) {
+ child = BUS_ADD_CHILD(parent, 150, "cpu", i);
+ if (child == NULL)
+ panic("legacy_attach cpu");
+ }
+}
+
+static device_t
+cpu_add_child(device_t bus, int order, const char *name, int unit)
+{
+ struct cpu_device *cd;
+ device_t child;
+ struct pcpu *pc;
+
+ if ((cd = kmalloc(sizeof(*cd), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL)
+ return (NULL);
+
+ resource_list_init(&cd->cd_rl);
+ pc = pcpu_find(device_get_unit(bus));
+ cd->cd_pcpu = pc;
+
+ child = device_add_child_ordered(bus, order, name, unit);
+ if (child != NULL) {
+ pc->pc_device = child;
+ device_set_ivars(child, cd);
+ } else
+ kfree(cd, M_DEVBUF);
+ return (child);
+}
+
+static struct resource_list *
+cpu_get_rlist(device_t dev, device_t child)
+{
+ struct cpu_device *cpdev;
+
+ cpdev = device_get_ivars(child);
+ return (&cpdev->cd_rl);
+}
+
+static int
+cpu_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
+{
+ struct cpu_device *cpdev;
+
+ if (index != CPU_IVAR_PCPU)
+ return (ENOENT);
+ cpdev = device_get_ivars(child);
+ *result = (uintptr_t)cpdev->cd_pcpu;
+ return (0);
+}
+#endif
-/*
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
+/*-
+ * Copyright (c) 1997, Stefan Esser <se@kfreebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* 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.
- *
- * $FreeBSD: src/sys/i386/isa/pcibus.c,v 1.57.2.12 2003/08/07 06:19:26 imp Exp $
- * $DragonFly: src/sys/bus/pci/i386/pcibus.c,v 1.21 2008/09/05 10:39:36 hasso Exp $
- *
+ * __FBSDID("$FreeBSD: src/sys/i386/pci/pci_bus.c,v 1.128.8.1 2009/04/15 03:14:26 kensmith Exp $");
*/
+#include <sys/cdefs.h>
+
#include "opt_pci.h"
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
+#include <sys/module.h>
#include <sys/sysctl.h>
#include <bus/pci/pcivar.h>
#include <bus/pci/pcireg.h>
-#include "pcibus.h"
+#include <bus/pci/pcib_private.h>
#include <bus/isa/isavar.h>
-#include "pci_cfgreg.h"
#include <machine/md_var.h>
-#include <machine/nexusvar.h>
+#include <machine/legacyvar.h>
+#include "pci_cfgreg.h"
#include "pcib_if.h"
-static u_int32_t nexus_pcib_read_config(device_t, int, int, int, int, int);
+static int pcibios_pcib_route_interrupt(device_t pcib, device_t dev,
+ int pin);
+
+int
+legacy_pcib_maxslots(device_t dev)
+{
+ return 31;
+}
+
+/* read configuration space register */
+
+u_int32_t
+legacy_pcib_read_config(device_t dev, int bus, int slot, int func,
+ int reg, int bytes)
+{
+ return(pci_cfgregread(bus, slot, func, reg, bytes));
+}
+
+/* write configuration space register */
+
+void
+legacy_pcib_write_config(device_t dev, int bus, int slot, int func,
+ int reg, u_int32_t data, int bytes)
+{
+ pci_cfgregwrite(bus, slot, func, reg, data, bytes);
+}
+
+/* Pass MSI requests up to the nexus. */
+
+static int
+legacy_pcib_alloc_msi(device_t pcib, device_t dev, int count, int maxcount,
+ int *irqs)
+{
+ device_t bus;
+
+ bus = device_get_parent(pcib);
+ return (PCIB_ALLOC_MSI(device_get_parent(bus), dev, count, maxcount,
+ irqs));
+}
+
+static int
+legacy_pcib_alloc_msix(device_t pcib, device_t dev, int *irq)
+{
+ device_t bus;
+
+ bus = device_get_parent(pcib);
+ return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq));
+}
+
+static int
+legacy_pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr,
+ uint32_t *data)
+{
+ device_t bus;
+
+ bus = device_get_parent(pcib);
+ return (PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data));
+}
-/*
- * Figure out if a PCI entity is a host bridge, return its name or NULL.
- */
static const char *
-nexus_legacypci_is_host_bridge(int bus, int slot, int func,
- u_int32_t id, u_int8_t class, u_int8_t subclass,
- u_int8_t *busnum)
+legacy_pcib_is_host_bridge(int bus, int slot, int func,
+ uint32_t id, uint8_t class, uint8_t subclass,
+ uint8_t *busnum)
{
const char *s = NULL;
- static u_int8_t pxb[4]; /* hack for 450nx */
+ static uint8_t pxb[4]; /* hack for 450nx */
*busnum = 0;
case 0x12258086:
s = "Intel 824?? host to PCI bridge";
/* XXX This is a guess */
- /* *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x41, 1); */
+ /* *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x41, 1); */
*busnum = bus;
break;
case 0x71208086:
case 0x71248086:
s = "Intel 82810E (i810E GMCH) Host To Hub bridge";
break;
+ case 0x11308086:
+ s = "Intel 82815 (i815 GMCH) Host To Hub bridge";
+ break;
case 0x71808086:
s = "Intel 82443LX (440 LX) host to PCI bridge";
break;
break;
case 0x84c48086:
s = "Intel 82454KX/GX (Orion) host to PCI bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x4a, 1);
+ *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x4a, 1);
break;
case 0x84ca8086:
/*
* For the 450nx chipset, there is a whole bundle of
- * things pretending to be host bridges. The MIOC will
+ * things pretending to be host bridges. The MIOC will
* be seen first and isn't really a pci bridge (the
- * actual busses are attached to the PXB's). We need to
+ * actual busses are attached to the PXB's). We need to
* read the registers of the MIOC to figure out the
* bus numbers for the PXB channels.
*
* Since the MIOC doesn't have a pci bus attached, we
* pretend it wasn't there.
*/
- pxb[0] = nexus_pcib_read_config(0, bus, slot, func,
+ pxb[0] = legacy_pcib_read_config(0, bus, slot, func,
0xd0, 1); /* BUSNO[0] */
- pxb[1] = nexus_pcib_read_config(0, bus, slot, func,
+ pxb[1] = legacy_pcib_read_config(0, bus, slot, func,
0xd1, 1) + 1; /* SUBA[0]+1 */
- pxb[2] = nexus_pcib_read_config(0, bus, slot, func,
+ pxb[2] = legacy_pcib_read_config(0, bus, slot, func,
0xd3, 1); /* BUSNO[1] */
- pxb[3] = nexus_pcib_read_config(0, bus, slot, func,
+ pxb[3] = legacy_pcib_read_config(0, bus, slot, func,
0xd4, 1) + 1; /* SUBA[1]+1 */
return NULL;
case 0x84cb8086:
break;
}
break;
- case 0x1A308086:
- s = "Intel 82845 Host to PCI bridge";
- break;
/* AMD -- vendor 0x1022 */
case 0x30001022:
#ifdef CPU_ELAN
init_AMD_Elan_sc520();
#else
- kprintf("*** WARNING: kernel option CPU_ELAN missing");
- kprintf("-- timekeeping may be wrong\n");
+ kprintf(
+"*** WARNING: missing CPU_ELAN -- timekeeping may be wrong\n");
#endif
break;
case 0x70061022:
/* ServerWorks -- vendor 0x1166 */
case 0x00051166:
s = "ServerWorks NB6536 2.0HE host to PCI bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
+ *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1);
break;
-
+
case 0x00061166:
/* FALLTHROUGH */
case 0x00081166:
case 0x02011166:
/* FALLTHROUGH */
case 0x010f1014: /* IBM re-badged ServerWorks chipset */
- /* FALLTHROUGH */
s = "ServerWorks host to PCI bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
+ *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1);
break;
case 0x00091166:
s = "ServerWorks NB6635 3.0LE host to PCI bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
+ *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1);
break;
case 0x00101166:
s = "ServerWorks CIOB30 host to PCI bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
+ *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1);
break;
case 0x00111166:
/* FALLTHROUGH */
case 0x03021014: /* IBM re-badged ServerWorks chipset */
s = "ServerWorks CMIC-HE host to PCI-X bridge";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
+ *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1);
break;
/* XXX unknown chipset, but working */
/* FALLTHROUGH */
case 0x01011166:
s = "ServerWorks host to PCI bridge(unknown chipset)";
- *busnum = nexus_pcib_read_config(0, bus, slot, func, 0x44, 1);
+ *busnum = legacy_pcib_read_config(0, bus, slot, func, 0x44, 1);
+ break;
+
+ /* Compaq/HP -- vendor 0x0e11 */
+ case 0x60100e11:
+ s = "Compaq/HP Model 6010 HotPlug PCI Bridge";
+ *busnum = legacy_pcib_read_config(0, bus, slot, func, 0xc8, 1);
break;
/* Integrated Micro Solutions -- vendor 0x10e0 */
}
/*
- * Identify the existance of the first pci bus and install a child to
- * nexus if we find it. Use an order of 1 so it gets probed after
- * any ACPI device installed under nexus. To avoid boot-time confusion,
- * we do not install any 'pcib' devices at this time.
- *
- * The identify method coupled with the driver spec of the same name
- * automatically installs it under the nexus.
- */
-static int
-nexus_legacypci_identify(driver_t *driver, device_t parent)
-{
- /*
- * Basically a static device, there's no point reinstalling it
- * on rescan.
- */
- if (device_get_state(parent) == DS_ATTACHED)
- return (0);
- if (device_get_state(parent) == DS_INPROGRESS)
- return (0);
-
- if (pci_cfgregopen() == 0)
- return (ENXIO);
-
- BUS_ADD_CHILD(parent, parent, 100, "legacypci", 0);
- return (0);
-}
-
-/*
* Scan the first pci bus for host-pci bridges and add pcib instances
* to the nexus for each bridge.
*/
-static int
-nexus_legacypci_probe(device_t dev)
+static void
+legacy_pcib_identify(driver_t *driver, device_t parent)
{
int bus, slot, func;
u_int8_t hdrtype;
int found = 0;
int pcifunchigh;
int found824xx = 0;
+ int found_orion = 0;
device_t child;
+ devclass_t pci_devclass;
+ if (pci_cfgregopen() == 0)
+ return;
/*
- * Do not install any pci busses ('pcib' devices) if the PCI
- * subsystem has already been claimed by someone else.
+ * Check to see if we haven't already had a PCI bus added
+ * via some other means. If we have, bail since otherwise
+ * we're going to end up duplicating it.
*/
- if (pcib_owner != NULL) {
- device_printf(dev, "PCI subsystem owned by %s, skipping scan\n",
- pcib_owner);
- return (ENXIO);
- }
- pcib_owner = "legacypci";
+ if ((pci_devclass = devclass_find("pci")) &&
+ devclass_get_device(pci_devclass, 0))
+ return;
- if (pci_cfgregopen() == 0)
- return (ENXIO);
- /*
- * Scan away!
- */
bus = 0;
retry:
for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
func = 0;
- hdrtype = nexus_pcib_read_config(0, bus, slot, func,
+ hdrtype = legacy_pcib_read_config(0, bus, slot, func,
PCIR_HDRTYPE, 1);
- if ((hdrtype & ~PCIM_MFDEV) > 2)
+ /*
+ * When enumerating bus devices, the standard says that
+ * one should check the header type and ignore the slots whose
+ * header types that the software doesn't know about. We use
+ * this to filter out devices.
+ */
+ if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
continue;
- if (hdrtype & PCIM_MFDEV)
- pcifunchigh = 7;
+ if ((hdrtype & PCIM_MFDEV) &&
+ (!found_orion || hdrtype != 0xff))
+ pcifunchigh = PCI_FUNCMAX;
else
pcifunchigh = 0;
for (func = 0; func <= pcifunchigh; func++) {
device_t *devs;
int ndevs, i;
- id = nexus_pcib_read_config(0, bus, slot, func,
+ id = legacy_pcib_read_config(0, bus, slot, func,
PCIR_DEVVENDOR, 4);
if (id == -1)
continue;
- class = nexus_pcib_read_config(0, bus, slot, func,
+ class = legacy_pcib_read_config(0, bus, slot, func,
PCIR_CLASS, 1);
- subclass = nexus_pcib_read_config(0, bus, slot, func,
+ subclass = legacy_pcib_read_config(0, bus, slot, func,
PCIR_SUBCLASS, 1);
- s = nexus_legacypci_is_host_bridge(bus, slot, func,
+ s = legacy_pcib_is_host_bridge(bus, slot, func,
id, class, subclass,
&busnum);
if (s == NULL)
/*
* Check to see if the physical bus has already
- * been seen. Eg: hybrid 32 and 64 bit host
+ * been seen. Eg: hybrid 32 and 64 bit host
* bridges to the same logical bus.
*/
- if (device_get_children(dev, &devs, &ndevs) == 0) {
+ if (device_get_children(parent, &devs, &ndevs) == 0) {
for (i = 0; s != NULL && i < ndevs; i++) {
if (strcmp(device_get_name(devs[i]),
"pcib") != 0)
continue;
- if (nexus_get_pcibus(devs[i]) == busnum)
+ if (legacy_get_pcibus(devs[i]) == busnum)
s = NULL;
}
kfree(devs, M_TEMP);
if (s == NULL)
continue;
+
/*
- * Add at priority 100+busnum to keep the scanning
- * order sane in the boot dmesg output.
+ * Add at priority 100 to make sure we
+ * go after any motherboard resources
*/
- child = BUS_ADD_CHILD(dev, dev, 100 + busnum,
+ child = BUS_ADD_CHILD(parent, parent, 100 + busnum,
"pcib", busnum);
device_set_desc(child, s);
- nexus_set_pcibus(child, busnum);
+ legacy_set_pcibus(child, busnum);
found = 1;
if (id == 0x12258086)
found824xx = 1;
+ if (id == 0x84c48086)
+ found_orion = 1;
}
}
if (found824xx && bus == 0) {
goto retry;
}
-#if 0
- /*
- * Now that we have installed the main PCI bridges, go
- * probe and attach each one.
- */
- bus_generic_attach(dev);
-#endif
-
/*
* Make sure we add at least one bridge since some old
* hardware doesn't actually have a host-pci bridge device.
* Note that pci_cfgregopen() thinks we have PCI devices..
*/
if (!found) {
- if (bootverbose) {
- kprintf("nexus_pcib_identify: no bridge found, "
- "adding pcib0 anyway\n");
- }
- child = BUS_ADD_CHILD(dev, dev, 100, "pcib", 0);
- nexus_set_pcibus(child, 0);
+ if (bootverbose)
+ kprintf(
+ "legacy_pcib_identify: no bridge found, adding pcib0 anyway\n");
+ child = BUS_ADD_CHILD(parent, parent, 100, "pcib", 0);
+ legacy_set_pcibus(child, 0);
}
- return (0);
}
static int
-nexus_legacypci_attach(device_t dev)
+legacy_pcib_probe(device_t dev)
{
- bus_generic_attach(dev);
- return (0);
+ if (pci_cfgregopen() == 0)
+ return ENXIO;
+ return -100;
}
-#ifdef PCI_MAP_FIXUP
-
-SYSCTL_DECL(_hw_pci);
-
-static unsigned long legacy_host_mem_start = 0x80000000;
-/* XXX need TUNABLE_ULONG? */
-TUNABLE_INT("hw.pci.host_mem_start", (int *)&legacy_host_mem_start);
-SYSCTL_ULONG(_hw_pci, OID_AUTO, host_mem_start, CTLFLAG_RD,
- &legacy_host_mem_start, 0x80000000,
- "Limit the host bridge memory to being above this address. "
- "Must be\n"
- "set at boot via hw.pci.host_mem_start tunable.");
-
-static struct resource *
-nexus_legacypci_alloc_resource(device_t dev, device_t child, int type, int *rid,
- u_long start, u_long end, u_long count,
- u_int flags)
+static int
+legacy_pcib_attach(device_t dev)
{
- /*
- * If no memory preference is given, use upper 32MB slot most
- * bioses use for their memory window. Typically other bridges
- * before us get in the way to assert their preferences on memory.
- * Hardcoding like this sucks, so a more MD/MI way needs to be
- * found to do it. This is typically only used on older laptops
- * that don't have pci busses behind pci bridge, so assuming > 32MB
- * is likely OK.
- *
- * However, this can cause problems for other chipsets, so we make
- * this tunable by hw.pci.host_mem_start.
- */
- if (type == SYS_RES_MEMORY && start == 0UL && end == ~0UL)
- start = legacy_host_mem_start;
- if (type == SYS_RES_IOPORT && start == 0UL && end == ~0UL)
- start = 0x1000;
- return bus_generic_alloc_resource(dev, child, type, rid,
- start, end, count, flags);
-}
-
-#endif /* PCI_MAP_FIXUP */
-
-static device_method_t legacypci_methods[] = {
- /* Device interface */
- DEVMETHOD(device_identify, nexus_legacypci_identify),
- DEVMETHOD(device_probe, nexus_legacypci_probe),
- DEVMETHOD(device_attach, nexus_legacypci_attach),
- DEVMETHOD(device_shutdown, bus_generic_shutdown),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
+ device_t pir;
+ int bus;
/*
- * Bus interface - propogate through to the nexus. Note that
- * this means devices under us will have nexus ivars.
+ * Look for a PCI BIOS interrupt routing table as that will be
+ * our method of routing interrupts if we have one.
*/
- DEVMETHOD(bus_add_child, bus_generic_add_child),
- DEVMETHOD(bus_print_child, bus_generic_print_child),
- DEVMETHOD(bus_read_ivar, bus_generic_read_ivar),
- DEVMETHOD(bus_write_ivar, bus_generic_write_ivar),
-#ifdef PCI_MAP_FIXUP
- DEVMETHOD(bus_alloc_resource, nexus_legacypci_alloc_resource),
-#else
- DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
-#endif
- DEVMETHOD(bus_release_resource, bus_generic_release_resource),
- DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
- DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
- DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
- DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
- DEVMETHOD(bus_set_resource, bus_generic_set_resource),
- DEVMETHOD(bus_get_resource, bus_generic_get_resource),
- DEVMETHOD(bus_delete_resource, bus_generic_delete_resource),
- { 0, 0 }
-};
-
-static driver_t legacypci_driver = {
- "legacypci",
- legacypci_methods,
- 1,
-};
-
-static devclass_t legacypci_devclass;
-
-DRIVER_MODULE(legacypci, nexus, legacypci_driver, legacypci_devclass, 0, 0);
-
-/*
- * Legacypci Host-Bridge PCI BUS support. The underlying pcib devices
- * will only exist if we actually control the PCI bus. The actual PCI
- * bus driver is attached in our attach routine.
- *
- * There is no identify function because the legacypci placeholder will
- * have already scanned and added PCIB devices for the host-bridges found.
- */
-static int
-nexus_pcib_maxslots(device_t dev)
-{
- return 31;
-}
-
-/*
- * Read configuration space register.
- */
-static u_int32_t
-nexus_pcib_read_config(device_t dev, int bus, int slot, int func,
- int reg, int bytes)
-{
- return (pci_cfgregread(bus, slot, func, reg, bytes));
+ bus = pcib_get_bus(dev);
+ if (pci_pir_probe(bus, 0)) {
+ pir = BUS_ADD_CHILD(device_get_parent(dev), device_get_parent(dev), 0, "pir", 0);
+ if (pir != NULL)
+ device_probe_and_attach(pir);
+ }
+ device_add_child(dev, "pci", bus);
+ return bus_generic_attach(dev);
}
-/*
- * Write configuration space register.
- */
-static void
-nexus_pcib_write_config(device_t dev, int bus, int slot, int func,
- int reg, u_int32_t data, int bytes)
+int
+legacy_pcib_read_ivar(device_t dev, device_t child, int which,
+ uintptr_t *result)
{
- pci_cfgregwrite(bus, slot, func, reg, data, bytes);
-}
-/*
- * Stack a pci device on top of the pci bridge bus device.
- */
-static int
-nexus_pcib_probe(device_t dev)
-{
- BUS_ADD_CHILD(dev, dev, 0, "pci", device_get_unit(dev));
- return (0);
+ switch (which) {
+ case PCIB_IVAR_DOMAIN:
+ *result = 0;
+ return 0;
+ case PCIB_IVAR_BUS:
+ *result = legacy_get_pcibus(dev);
+ return 0;
+ }
+ return ENOENT;
}
-static int
-nexus_pcib_attach(device_t dev)
+int
+legacy_pcib_write_ivar(device_t dev, device_t child, int which,
+ uintptr_t value)
{
- int error;
- error = bus_generic_attach(dev);
- return (error);
+ switch (which) {
+ case PCIB_IVAR_DOMAIN:
+ return EINVAL;
+ case PCIB_IVAR_BUS:
+ legacy_set_pcibus(dev, value);
+ return 0;
+ }
+ return ENOENT;
}
-/* route interrupt */
+SYSCTL_DECL(_hw_pci);
-static int
-nexus_pcib_route_interrupt(device_t pcib, device_t dev, int pin)
+static unsigned long legacy_host_mem_start = 0x80000000;
+TUNABLE_INT("hw.pci.host_mem_start", &legacy_host_mem_start);
+SYSCTL_ULONG(_hw_pci, OID_AUTO, host_mem_start, CTLFLAG_RD,
+ &legacy_host_mem_start, 0x80000000,
+ "Limit the host bridge memory to being above this address. Must be\n\
+set at boot via a tunable.");
+
+struct resource *
+legacy_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
{
- return(pci_cfgintr(pci_get_bus(dev), pci_get_slot(dev), pin,
- pci_get_irq(dev)));
+ /*
+ * If no memory preference is given, use upper 32MB slot most
+ * bioses use for their memory window. Typically other bridges
+ * before us get in the way to assert their preferences on memory.
+ * Hardcoding like this sucks, so a more MD/MI way needs to be
+ * found to do it. This is typically only used on older laptops
+ * that don't have pci busses behind pci bridge, so assuming > 32MB
+ * is liekly OK.
+ *
+ * However, this can cause problems for other chipsets, so we make
+ * this tunable by hw.pci.host_mem_start.
+ */
+ if (type == SYS_RES_MEMORY && start == 0UL && end == ~0UL)
+ start = legacy_host_mem_start;
+ if (type == SYS_RES_IOPORT && start == 0UL && end == ~0UL)
+ start = 0x1000;
+ return (bus_generic_alloc_resource(dev, child, type, rid, start, end,
+ count, flags));
}
-static device_method_t nexus_pcib_methods[] = {
+static device_method_t legacy_pcib_methods[] = {
/* Device interface */
- DEVMETHOD(device_probe, nexus_pcib_probe),
- DEVMETHOD(device_attach, nexus_pcib_attach),
+ DEVMETHOD(device_identify, legacy_pcib_identify),
+ DEVMETHOD(device_probe, legacy_pcib_probe),
+ DEVMETHOD(device_attach, legacy_pcib_attach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
- /*
- * Bus interface - propogate through to the nexus. Note
- * that this means we will get nexus-managed ivars.
- */
- DEVMETHOD(bus_add_child, bus_generic_add_child),
+ /* Bus interface */
DEVMETHOD(bus_print_child, bus_generic_print_child),
- DEVMETHOD(bus_read_ivar, bus_generic_read_ivar),
- DEVMETHOD(bus_write_ivar, bus_generic_write_ivar),
- DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+ DEVMETHOD(bus_read_ivar, legacy_pcib_read_ivar),
+ DEVMETHOD(bus_write_ivar, legacy_pcib_write_ivar),
+ DEVMETHOD(bus_alloc_resource, legacy_pcib_alloc_resource),
DEVMETHOD(bus_release_resource, bus_generic_release_resource),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
/* pcib interface */
- DEVMETHOD(pcib_maxslots, nexus_pcib_maxslots),
- DEVMETHOD(pcib_read_config, nexus_pcib_read_config),
- DEVMETHOD(pcib_write_config, nexus_pcib_write_config),
- DEVMETHOD(pcib_route_interrupt, nexus_pcib_route_interrupt),
-
+ DEVMETHOD(pcib_maxslots, legacy_pcib_maxslots),
+ DEVMETHOD(pcib_read_config, legacy_pcib_read_config),
+ DEVMETHOD(pcib_write_config, legacy_pcib_write_config),
+ DEVMETHOD(pcib_route_interrupt, pcibios_pcib_route_interrupt),
+#ifdef notyet
+ DEVMETHOD(pcib_alloc_msi, legacy_pcib_alloc_msi),
+ DEVMETHOD(pcib_release_msi, pcib_release_msi),
+ DEVMETHOD(pcib_alloc_msix, legacy_pcib_alloc_msix),
+ DEVMETHOD(pcib_release_msix, pcib_release_msix),
+ DEVMETHOD(pcib_map_msi, legacy_pcib_map_msi),
+#endif
{ 0, 0 }
};
-static driver_t nexus_pcib_driver = {
- "pcib",
- nexus_pcib_methods,
- 1,
-};
+static devclass_t hostb_devclass;
-static devclass_t pcib_devclass;
-
-DRIVER_MODULE(pcib, legacypci, nexus_pcib_driver, pcib_devclass, 0, 0);
-
-
-/*
- * Provide a device to "eat" the host->pci bridges that we dug up above
- * and stop them showing up twice on the probes. This also stops them
- * showing up as 'none' in pciconf -l.
- *
- * Return an ultra-low priority so other devices can attach the bus before
- * our dummy attach.
- *
- * XXX may have to disable the registration entirely to support module-loaded
- * bridges such as agp.ko.
- */
-static int
-pci_hostb_probe(device_t dev)
-{
- if (pci_get_class(dev) == PCIC_BRIDGE &&
- pci_get_subclass(dev) == PCIS_BRIDGE_HOST) {
- device_set_desc(dev, "Host to PCI bridge");
- device_quiet(dev);
- return -10000;
- }
- return (ENXIO);
-}
-
-static int
-pci_hostb_attach(device_t dev)
-{
- return (0);
-}
-
-static device_method_t pci_hostb_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, pci_hostb_probe),
- DEVMETHOD(device_attach, pci_hostb_attach),
- DEVMETHOD(device_shutdown, bus_generic_shutdown),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
-
- { 0, 0 }
-};
-static driver_t pci_hostb_driver = {
- "hostb",
- pci_hostb_methods,
- 1,
-};
-static devclass_t pci_hostb_devclass;
-
-DRIVER_MODULE(hostb, pci, pci_hostb_driver, pci_hostb_devclass, 0, 0);
+DEFINE_CLASS_0(pcib, legacy_pcib_driver, legacy_pcib_methods, 1);
+DRIVER_MODULE(pcib, legacy, legacy_pcib_driver, hostb_devclass, 0, 0);
/*
* Install placeholder to claim the resources owned by the
- * PCI bus interface. This could be used to extract the
+ * PCI bus interface. This could be used to extract the
* config space registers in the extreme case where the PnP
* ID is available and the PCI BIOS isn't, but for now we just
* eat the PnP ID and do nothing else.
*
- * XXX we should silence this probe, as it will generally confuse
+ * XXX we should silence this probe, as it will generally confuse
* people.
*/
static struct isa_pnp_id pcibus_pnp_ids[] = {
- { 0x030ad041 /* PNP030A */, "PCI Bus" },
+ { 0x030ad041 /* PNP0A03 */, "PCI Bus" },
{ 0 }
};
pcibus_pnp_probe(device_t dev)
{
int result;
-
+
if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, pcibus_pnp_ids)) <= 0)
device_quiet(dev);
- return (result);
+ return(result);
}
static int
{ 0, 0 }
};
-static driver_t pcibus_pnp_driver = {
- "pcibus_pnp",
- pcibus_pnp_methods,
- 1, /* no softc */
-};
-
static devclass_t pcibus_pnp_devclass;
+DEFINE_CLASS_0(pcibus_pnp, pcibus_pnp_driver, pcibus_pnp_methods, 1);
DRIVER_MODULE(pcibus_pnp, isa, pcibus_pnp_driver, pcibus_pnp_devclass, 0, 0);
+
+/*
+ * Provide a PCI-PCI bridge driver for PCI busses behind PCI-PCI bridges
+ * that appear in the PCIBIOS Interrupt Routing Table to use the routing
+ * table for interrupt routing when possible.
+ */
+static int pcibios_pcib_probe(device_t bus);
+
+static device_method_t pcibios_pcib_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pcibios_pcib_probe),
+ DEVMETHOD(device_attach, pcib_attach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_read_ivar, pcib_read_ivar),
+ DEVMETHOD(bus_write_ivar, pcib_write_ivar),
+ DEVMETHOD(bus_alloc_resource, pcib_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, pcib_maxslots),
+ DEVMETHOD(pcib_read_config, pcib_read_config),
+ DEVMETHOD(pcib_write_config, pcib_write_config),
+ DEVMETHOD(pcib_route_interrupt, pcibios_pcib_route_interrupt),
+ DEVMETHOD(pcib_alloc_msi, pcib_alloc_msi),
+ DEVMETHOD(pcib_release_msi, pcib_release_msi),
+ DEVMETHOD(pcib_alloc_msix, pcib_alloc_msix),
+ DEVMETHOD(pcib_release_msix, pcib_release_msix),
+ DEVMETHOD(pcib_map_msi, pcib_map_msi),
+
+ {0, 0}
+};
+
+static devclass_t pcib_devclass;
+
+DEFINE_CLASS_0(pcib, pcibios_pcib_driver, pcibios_pcib_pci_methods,
+ sizeof(struct pcib_softc));
+DRIVER_MODULE(pcibios_pcib, pci, pcibios_pcib_driver, pcib_devclass, 0, 0);
+
+static int
+pcibios_pcib_probe(device_t dev)
+{
+ int bus;
+
+ if ((pci_get_class(dev) != PCIC_BRIDGE) ||
+ (pci_get_subclass(dev) != PCIS_BRIDGE_PCI))
+ return (ENXIO);
+ bus = pci_read_config(dev, PCIR_SECBUS_1, 1);
+ if (bus == 0)
+ return (ENXIO);
+ if (!pci_pir_probe(bus, 1))
+ return (ENXIO);
+ device_set_desc(dev, "PCIBIOS PCI-PCI bridge");
+ return (-2000);
+}
+
+static int
+pcibios_pcib_route_interrupt(device_t pcib, device_t dev, int pin)
+{
+ return (pci_pir_route_interrupt(pci_get_bus(dev), pci_get_slot(dev),
+ pci_get_function(dev), pin));
+}
-/*
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
- * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
+/*-
+ * Copyright (c) 1997, Stefan Esser <se@kfreebsd.org>
+ * Copyright (c) 2000, Michael Smith <msmith@kfreebsd.org>
* Copyright (c) 2000, BSDi
+ * Copyright (c) 2004, Scott Long <scottl@kfreebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* 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.
- *
- * $FreeBSD: src/sys/i386/isa/pci_cfgreg.c,v 1.1.2.7 2001/11/28 05:47:03 imp Exp $
- * $DragonFly: src/sys/bus/pci/i386/pci_cfgreg.c,v 1.15 2008/08/02 05:22:20 dillon Exp $
- *
+ * __FBSDID("$FreeBSD: src/sys/i386/pci/pci_cfgreg.c,v 1.124.2.2.6.1 2009/04/15 03:14:26 kensmith Exp $");
*/
-#include <sys/param.h> /* XXX trim includes */
+#if defined(__DragonFly__)
+#define mtx_init(a, b, c, d) spin_init(a)
+#define mtx_lock_spin(a) spin_lock_wr(a)
+#define mtx_unlock_spin(a) spin_unlock_wr(a)
+#endif
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
-#include <sys/kernel.h>
-#include <sys/module.h>
+#include <sys/lock.h>
#include <sys/malloc.h>
-#include <sys/sysctl.h>
-#include <vm/vm.h>
-#include <vm/pmap.h>
-#include <machine/md_var.h>
-#include <machine/clock.h>
+#include <sys/thread2.h>
+#include <sys/spinlock.h>
+#include <sys/spinlock2.h>
+#include <sys/queue.h>
#include <bus/pci/pcivar.h>
#include <bus/pci/pcireg.h>
-#include <bus/isa/isavar.h>
#include "pci_cfgreg.h"
-#include <machine/segments.h>
#include <machine/pc/bios.h>
-#include <machine/smp.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_extern.h>
+#include <vm/pmap.h>
+#include <machine/pmap.h>
#define PRVERB(a) do { \
if (bootverbose) \
kprintf a ; \
} while(0)
-static int pci_disable_bios_route = 0;
-SYSCTL_INT(_hw, OID_AUTO, pci_disable_bios_route, CTLFLAG_RD,
- &pci_disable_bios_route, 0, "disable interrupt routing via PCI-BIOS");
-TUNABLE_INT("hw.pci_disable_bios_route", &pci_disable_bios_route);
-
+#define PCIE_CACHE 8
+struct pcie_cfg_elem {
+ TAILQ_ENTRY(pcie_cfg_elem) elem;
+ vm_offset_t vapage;
+ vm_paddr_t papage;
+};
+
+enum {
+ CFGMECH_NONE = 0,
+ CFGMECH_1,
+ CFGMECH_2,
+ CFGMECH_PCIE,
+};
+
+static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
+static uint32_t pciebar;
static int cfgmech;
static int devmax;
+#if defined(__DragonFly__)
+static struct spinlock pcicfg_mtx;
+#else
+static struct mtx pcicfg_mtx;
+#endif
-static int pci_cfgintr_valid(struct PIR_entry *pe, int pin, int irq);
-static int pci_cfgintr_unique(struct PIR_entry *pe, int pin);
-static int pci_cfgintr_linked(struct PIR_entry *pe, int pin);
-static int pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin);
-static int pci_cfgintr_virgin(struct PIR_entry *pe, int pin);
-
-static void pci_print_irqmask(u_int16_t irqs);
-static void pci_print_route_table(struct PIR_table *prt, int size);
static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
static int pcireg_cfgopen(void);
-static struct PIR_table *pci_route_table;
-static int pci_route_count;
+static int pciereg_cfgopen(void);
+static int pciereg_cfgread(int bus, int slot, int func, int reg,
+ int bytes);
+static void pciereg_cfgwrite(int bus, int slot, int func, int reg,
+ int data, int bytes);
/*
* Some BIOS writers seem to want to ignore the spec and put
- * 0 in the intline rather than 255 to indicate none. Some use
+ * 0 in the intline rather than 255 to indicate none. Some use
* numbers in the range 128-254 to indicate something strange and
- * apparently undocumented anywhere. Assume these are completely bogus
+ * apparently undocumented anywhere. Assume these are completely bogus
* and map them to 255, which means "none".
*/
-static int
-pci_map_intline(int line)
+static __inline int
+pci_i386_map_intline(int line)
{
if (line == 0 || line >= 128)
return (PCI_INVALID_IRQ);
pci_cfgregopen(void)
{
static int opened = 0;
- u_long sigaddr;
- static struct PIR_table *pt;
+ u_int16_t vid, did;
u_int16_t v;
- u_int8_t ck, *cv;
- int i;
-
if (opened)
- return (1);
+ return(1);
if (pcireg_cfgopen() == 0)
- return (0);
+ return(0);
v = pcibios_get_version();
if (v > 0)
- kprintf("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8,
- v & 0xff);
+ PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8,
+ v & 0xff));
+ mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
+ opened = 1;
+
+ /* $PIR requires PCI BIOS 2.10 or greater. */
+ if (v >= 0x0210)
+ pci_pir_open();
/*
- * Look for the interrupt routing table.
- *
- * We use PCI BIOS's PIR table if it's available $PIR is the
- * standard way to do this. Sadly some machines are not
- * standards conforming and have _PIR instead. We shrug and cope
- * by looking for both.
+ * Grope around in the PCI config space to see if this is a
+ * chipset that is capable of doing memory-mapped config cycles.
+ * This also implies that it can do PCIe extended config cycles.
*/
- if (pcibios_get_version() >= 0x0210 && pt == NULL) {
- sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0);
- if (sigaddr == 0)
- sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0);
- if (sigaddr != 0) {
- pt = (struct PIR_table *)(uintptr_t)
- BIOS_PADDRTOVADDR(sigaddr);
- for (cv = (u_int8_t *)pt, ck = 0, i = 0;
- i < (pt->pt_header.ph_length); i++)
- ck += cv[i];
- if (ck == 0 && pt->pt_header.ph_length >
- sizeof(struct PIR_header)) {
- pci_route_table = pt;
- pci_route_count = (pt->pt_header.ph_length -
- sizeof(struct PIR_header)) /
- sizeof(struct PIR_entry);
- kprintf("Using $PIR table, %d entries at %p\n",
- pci_route_count, pci_route_table);
- if (bootverbose)
- pci_print_route_table(pci_route_table,
- pci_route_count);
- }
+
+ /* Check for supported chipsets */
+ vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
+ did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
+ if (vid == 0x8086) {
+ if (did == 0x3590 || did == 0x3592) {
+ /* Intel 7520 or 7320 */
+ pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
+ pciereg_cfgopen();
+ } else if (did == 0x2580 || did == 0x2584) {
+ /* Intel 915 or 925 */
+ pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
+ pciereg_cfgopen();
}
}
- opened = 1;
- return (1);
+
+ return(1);
}
/*
pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
{
uint32_t line;
-#ifdef APIC_IO
- uint32_t pin;
/*
- * If we are using the APIC, the contents of the intline
- * register will probably be wrong (since they are set up for
- * use with the PIC. Rather than rewrite these registers
- * (maybe that would be smarter) we trap attempts to read them
- * and translate to our private vector numbers.
- */
- if ((reg == PCIR_INTLINE) && (bytes == 1)) {
-
- pin = pcireg_cfgread(bus, slot, func, PCIR_INTPIN, 1);
- line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
-
- if (pin != 0) {
- int airq;
-
- airq = pci_apic_irq(bus, slot, pin);
- if (airq >= 0) {
- /* PCI specific entry found in MP table */
- if (airq != line)
- undirect_pci_irq(line);
- return (airq);
- } else {
- /*
- * PCI interrupts might be redirected to the
- * ISA bus according to some MP tables. Use the
- * same methods as used by the ISA devices
- * devices to find the proper IOAPIC int pin.
- */
- airq = isa_apic_irq(line);
- if ((airq >= 0) && (airq != line)) {
- /* XXX: undirect_pci_irq() ? */
- undirect_isa_irq(line);
- return (airq);
- }
- }
- }
- return (line);
- }
-#else
- /*
* Some BIOS writers seem to want to ignore the spec and put
* 0 in the intline rather than 255 to indicate none. The rest of
* the code uses 255 as an invalid IRQ.
*/
if (reg == PCIR_INTLINE && bytes == 1) {
line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
- return pci_map_intline(line);
+ return (pci_i386_map_intline(line));
}
-#endif /* APIC_IO */
return (pcireg_cfgread(bus, slot, func, reg, bytes));
}
void
pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
{
- pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
-}
-
-int
-pci_cfgread(pcicfgregs *cfg, int reg, int bytes)
-{
- return (pci_cfgregread(cfg->bus, cfg->slot, cfg->func, reg, bytes));
-}
-void
-pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
-{
- pci_cfgregwrite(cfg->bus, cfg->slot, cfg->func, reg, data, bytes);
+ pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
}
-
-/*
- * Route a PCI interrupt
+/*
+ * Configuration space access using direct register operations
*/
-int
-pci_cfgintr(int bus, int device, int pin, int oldirq)
-{
- struct PIR_entry *pe;
- int i, irq;
- struct bios_regs args;
- u_int16_t v;
- int already = 0;
- int errok = 0;
-
- v = pcibios_get_version();
- if (v < 0x0210) {
- PRVERB((
- "pci_cfgintr: BIOS %x.%02x doesn't support interrupt routing\n",
- (v & 0xff00) >> 8, v & 0xff));
- return (PCI_INVALID_IRQ);
- }
- if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) ||
- (pin < 1) || (pin > 4))
- return (PCI_INVALID_IRQ);
-
- /*
- * Scan the entry table for a contender
- */
- for (i = 0, pe = &pci_route_table->pt_entry[0]; i < pci_route_count;
- i++, pe++) {
- if ((bus != pe->pe_bus) || (device != pe->pe_device))
- continue;
+/* enable configuration space accesses and return data port address */
+static int
+pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
+{
+ int dataport = 0;
+#ifdef XBOX
+ if (arch_i386_is_xbox) {
/*
- * A link of 0 means that this intpin is not connected to
- * any other device's interrupt pins and is not connected to
- * any of the Interrupt Router's interrupt pins, so we can't
- * route it.
+ * The Xbox MCPX chipset is a derivative of the nForce 1
+ * chipset. It almost has the same bus layout; some devices
+ * cannot be used, because they have been removed.
*/
- if (pe->pe_intpin[pin - 1].link == 0)
- continue;
-
- if (pci_cfgintr_valid(pe, pin, oldirq)) {
- kprintf("pci_cfgintr: %d:%d INT%c BIOS irq %d\n", bus,
- device, 'A' + pin - 1, oldirq);
- return (oldirq);
- }
/*
- * We try to find a linked interrupt, then we look to see
- * if the interrupt is uniquely routed, then we look for
- * a virgin interrupt. The virgin interrupt should return
- * an interrupt we can route, but if that fails, maybe we
- * should try harder to route a different interrupt.
- * However, experience has shown that that's rarely the
- * failure mode we see.
+ * Devices 00:00.1 and 00:00.2 used to be memory controllers on
+ * the nForce chipset, but on the Xbox, using them will lockup
+ * the chipset.
*/
- irq = pci_cfgintr_linked(pe, pin);
- if (irq != PCI_INVALID_IRQ)
- already = 1;
- if (irq == PCI_INVALID_IRQ) {
- irq = pci_cfgintr_unique(pe, pin);
- if (irq != PCI_INVALID_IRQ)
- errok = 1;
- }
- if (irq == PCI_INVALID_IRQ)
- irq = pci_cfgintr_virgin(pe, pin);
-
- if (irq == PCI_INVALID_IRQ)
- break;
-
- if (pci_disable_bios_route != 0)
- break;
+ if (bus == 0 && slot == 0 && (func == 1 || func == 2))
+ return dataport;
+
/*
- * Ask the BIOS to route the interrupt. If we picked an
- * interrupt that failed, we should really try other
- * choices that the BIOS offers us.
- *
- * For uniquely routed interrupts, we need to try
- * to route them on some machines. Yet other machines
- * fail to route, so we have to pretend that in that
- * case it worked. Isn't PC hardware fun?
- *
- * NOTE: if we want to whack hardware to do this, then
- * I think the right way to do that would be to have
- * bridge drivers that do this. I'm not sure that the
- * $PIR table would be valid for those interrupt
- * routers.
+ * Bus 1 only contains a VGA controller at 01:00.0. When you try
+ * to probe beyond that device, you only get garbage, which
+ * could cause lockups.
*/
- args.eax = PCIBIOS_ROUTE_INTERRUPT;
- args.ebx = (bus << 8) | (device << 3);
- /* pin value is 0xa - 0xd */
- args.ecx = (irq << 8) | (0xa + pin -1);
- if (!already &&
- bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)) &&
- !errok) {
- PRVERB(("pci_cfgintr: ROUTE_INTERRUPT failed.\n"));
- return (PCI_INVALID_IRQ);
- }
- kprintf("pci_cfgintr: %d:%d INT%c routed to irq %d\n", bus,
- device, 'A' + pin - 1, irq);
- return(irq);
- }
-
- PRVERB(("pci_cfgintr: can't route an interrupt to %d:%d INT%c oldirq=%d\n", bus,
- device, 'A' + pin - 1, oldirq));
- return (PCI_INVALID_IRQ);
-}
-
-/*
- * Check to see if an existing IRQ setting is valid.
- */
-static int
-pci_cfgintr_valid(struct PIR_entry *pe, int pin, int irq)
-{
- uint32_t irqmask;
-
- if (!PCI_INTERRUPT_VALID(irq))
- return (0);
- irqmask = pe->pe_intpin[pin - 1].irqs;
- if (irqmask & (1 << irq)) {
- PRVERB(("pci_cfgintr_valid: BIOS irq %d is valid\n", irq));
- return (1);
- }
- return (0);
-}
-
-/*
- * Look to see if the routing table claims this pin is uniquely routed.
- */
-static int
-pci_cfgintr_unique(struct PIR_entry *pe, int pin)
-{
- int irq;
- uint32_t irqmask;
-
- irqmask = pe->pe_intpin[pin - 1].irqs;
- if(irqmask != 0 && powerof2(irqmask)) {
- irq = ffs(irqmask) - 1;
- PRVERB(("pci_cfgintr_unique: hard-routed to irq %d\n", irq));
- return (irq);
- }
- return (PCI_INVALID_IRQ);
-}
-
-/*
- * Look for another device which shares the same link byte and
- * already has a unique IRQ, or which has had one routed already.
- */
-static int
-pci_cfgintr_linked(struct PIR_entry *pe, int pin)
-{
- struct PIR_entry *oe;
- struct PIR_intpin *pi;
- int i, j, irq;
-
- /*
- * Scan table slots.
- */
- for (i = 0, oe = &pci_route_table->pt_entry[0]; i < pci_route_count;
- i++, oe++) {
- /* scan interrupt pins */
- for (j = 0, pi = &oe->pe_intpin[0]; j < 4; j++, pi++) {
-
- /* don't look at the entry we're trying to match */
- if ((pe == oe) && (j == (pin - 1)))
- continue;
- /* compare link bytes */
- if (pi->link != pe->pe_intpin[pin - 1].link)
- continue;
- /* link destination mapped to a unique interrupt? */
- if (pi->irqs != 0 && powerof2(pi->irqs)) {
- irq = ffs(pi->irqs) - 1;
- PRVERB(("pci_cfgintr_linked: linked (%x) to hard-routed irq %d\n",
- pi->link, irq));
- return(irq);
- }
-
- /*
- * look for the real PCI device that matches this
- * table entry
- */
- irq = pci_cfgintr_search(pe, oe->pe_bus, oe->pe_device,
- j + 1, pin);
- if (irq != PCI_INVALID_IRQ)
- return (irq);
- }
- }
- return (PCI_INVALID_IRQ);
-}
-
-/*
- * Scan for the real PCI device at (bus)/(device) using intpin (matchpin) and
- * see if it has already been assigned an interrupt.
- */
-static int
-pci_cfgintr_search(struct PIR_entry *pe, int bus, int device, int matchpin, int pin)
-{
- devclass_t pci_devclass;
- device_t *pci_devices;
- int pci_count;
- device_t *pci_children;
- int pci_childcount;
- device_t *busp, *childp;
- int i, j, irq;
-
- /*
- * Find all the PCI busses.
- */
- pci_count = 0;
- if ((pci_devclass = devclass_find("pci")) != NULL)
- devclass_get_devices(pci_devclass, &pci_devices, &pci_count);
-
- /*
- * Scan all the PCI busses/devices looking for this one.
- */
- irq = PCI_INVALID_IRQ;
- for (i = 0, busp = pci_devices; (i < pci_count) && (irq == PCI_INVALID_IRQ);
- i++, busp++) {
- pci_childcount = 0;
- device_get_children(*busp, &pci_children, &pci_childcount);
+ if (bus == 1 && (slot != 0 || func != 0))
+ return dataport;
- for (j = 0, childp = pci_children; j < pci_childcount; j++,
- childp++) {
- if ((pci_get_bus(*childp) == bus) &&
- (pci_get_slot(*childp) == device) &&
- (pci_get_intpin(*childp) == matchpin)) {
- irq = pci_map_intline(pci_get_irq(*childp));
- if (irq != PCI_INVALID_IRQ)
- PRVERB(("pci_cfgintr_search: linked (%x) to configured irq %d at %d:%d:%d\n",
- pe->pe_intpin[pin - 1].link, irq,
- pci_get_bus(*childp),
- pci_get_slot(*childp),
- pci_get_function(*childp)));
- break;
- }
- }
- if (pci_children != NULL)
- kfree(pci_children, M_TEMP);
- }
- if (pci_devices != NULL)
- kfree(pci_devices, M_TEMP);
- return (irq);
-}
-
-/*
- * Pick a suitable IRQ from those listed as routable to this device.
- */
-static int
-pci_cfgintr_virgin(struct PIR_entry *pe, int pin)
-{
- int irq, ibit;
-
- /*
- * first scan the set of PCI-only interrupts and see if any of these
- * are routable
- */
- for (irq = 0; irq < 16; irq++) {
- ibit = (1 << irq);
-
- /* can we use this interrupt? */
- if ((pci_route_table->pt_header.ph_pci_irqs & ibit) &&
- (pe->pe_intpin[pin - 1].irqs & ibit)) {
- PRVERB(("pci_cfgintr_virgin: using routable PCI-only interrupt %d\n", irq));
- return (irq);
- }
- }
-
- /* life is tough, so just pick an interrupt */
- for (irq = 0; irq < 16; irq++) {
- ibit = (1 << irq);
-
- if (pe->pe_intpin[pin - 1].irqs & ibit) {
- PRVERB(("pci_cfgintr_virgin: using routable interrupt %d\n", irq));
- return (irq);
- }
- }
- return (PCI_INVALID_IRQ);
-}
-
-static void
-pci_print_irqmask(u_int16_t irqs)
-{
- int i, first;
-
- if (irqs == 0) {
- kprintf("none");
- return;
- }
- first = 1;
- for (i = 0; i < 16; i++, irqs >>= 1)
- if (irqs & 1) {
- if (!first)
- kprintf(" ");
- else
- first = 0;
- kprintf("%d", i);
- }
-}
-
-/*
- * Dump the contents of a PCI BIOS Interrupt Routing Table to the console.
- */
-static void
-pci_print_route_table(struct PIR_table *ptr, int size)
-{
- struct PIR_entry *entry;
- struct PIR_intpin *intpin;
- int i, pin;
-
- kprintf("PCI-Only Interrupts: ");
- pci_print_irqmask(ptr->pt_header.ph_pci_irqs);
- kprintf("\nLocation Bus Device Pin Link IRQs\n");
- entry = &ptr->pt_entry[0];
- for (i = 0; i < size; i++, entry++) {
- intpin = &entry->pe_intpin[0];
- for (pin = 0; pin < 4; pin++, intpin++)
- if (intpin->link != 0) {
- if (entry->pe_slot == 0)
- kprintf("embedded ");
- else
- kprintf("slot %-3d ", entry->pe_slot);
- kprintf(" %3d %3d %c 0x%02x ",
- entry->pe_bus, entry->pe_device,
- 'A' + pin, intpin->link);
- pci_print_irqmask(intpin->irqs);
- kprintf("\n");
- }
+ /*
+ * Bus 2 used to contain the AGP controller, but the Xbox MCPX
+ * doesn't have one. Probing it can cause lockups.
+ */
+ if (bus >= 2)
+ return dataport;
}
-}
-
-/*
- * See if any interrupts for a given PCI bus are routed in the PIR. Don't
- * even bother looking if the BIOS doesn't support routing anyways.
- */
-int
-pci_probe_route_table(int bus)
-{
- int i;
- u_int16_t v;
-
- v = pcibios_get_version();
- if (v < 0x0210)
- return (0);
- for (i = 0; i < pci_route_count; i++)
- if (pci_route_table->pt_entry[i].pe_bus == bus)
- return (1);
- return (0);
-}
-
-/*
- * Configuration space access using direct register operations
- */
-
-/* enable configuration space accesses and return data port address */
-static int
-pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
-{
- int dataport = 0;
+#endif
if (bus <= PCI_BUSMAX
&& slot < devmax
&& (unsigned) bytes <= 4
&& (reg & (bytes - 1)) == 0) {
switch (cfgmech) {
- case 1:
+ case CFGMECH_1:
outl(CONF1_ADDR_PORT, (1 << 31)
- | (bus << 16) | (slot << 11)
- | (func << 8) | (reg & ~0x03));
+ | (bus << 16) | (slot << 11)
+ | (func << 8) | (reg & ~0x03));
dataport = CONF1_DATA_PORT + (reg & 0x03);
break;
- case 2:
+ case CFGMECH_2:
outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
outb(CONF2_FORWARD_PORT, bus);
dataport = 0xc000 | (slot << 8) | reg;
pci_cfgdisable(void)
{
switch (cfgmech) {
- case 1:
- outl(CONF1_ADDR_PORT, 0);
+ case CFGMECH_1:
+ /*
+ * Do nothing for the config mechanism 1 case.
+ * Writing a 0 to the address port can apparently
+ * confuse some bridges and cause spurious
+ * access failures.
+ */
break;
- case 2:
+ case CFGMECH_2:
outb(CONF2_ENABLE_PORT, 0);
- outb(CONF2_FORWARD_PORT, 0);
break;
}
}
int data = -1;
int port;
+ if (cfgmech == CFGMECH_PCIE) {
+ data = pciereg_cfgread(bus, slot, func, reg, bytes);
+ return (data);
+ }
+
+ mtx_lock_spin(&pcicfg_mtx);
port = pci_cfgenable(bus, slot, func, reg, bytes);
if (port != 0) {
switch (bytes) {
}
pci_cfgdisable();
}
+ mtx_unlock_spin(&pcicfg_mtx);
return (data);
}
{
int port;
+ if (cfgmech == CFGMECH_PCIE) {
+ pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
+ return;
+ }
+
+ mtx_lock_spin(&pcicfg_mtx);
port = pci_cfgenable(bus, slot, func, reg, bytes);
if (port != 0) {
switch (bytes) {
}
pci_cfgdisable();
}
+ mtx_unlock_spin(&pcicfg_mtx);
}
/* check whether the configuration mechanism has been correctly identified */
static int
pcireg_cfgopen(void)
{
- uint32_t mode1res,oldval1;
- uint8_t mode2res,oldval2;
+ uint32_t mode1res, oldval1;
+ uint8_t mode2res, oldval2;
+ /* Check for type #1 first. */
oldval1 = inl(CONF1_ADDR_PORT);
if (bootverbose) {
kprintf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
- oldval1);
+ oldval1);
}
- if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
-
- cfgmech = 1;
- devmax = 32;
+ cfgmech = CFGMECH_1;
+ devmax = 32;
- outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
- DELAY(1);
- mode1res = inl(CONF1_ADDR_PORT);
- outl(CONF1_ADDR_PORT, oldval1);
+ outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
+ DELAY(1);
+ mode1res = inl(CONF1_ADDR_PORT);
+ outl(CONF1_ADDR_PORT, oldval1);
- if (bootverbose)
- kprintf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",
- mode1res, CONF1_ENABLE_CHK);
+ if (bootverbose)
+ kprintf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", mode1res,
+ CONF1_ENABLE_CHK);
- if (mode1res) {
- if (pci_cfgcheck(32))
- return (cfgmech);
- }
+ if (mode1res) {
+ if (pci_cfgcheck(32))
+ return (cfgmech);
+ }
- outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
- mode1res = inl(CONF1_ADDR_PORT);
- outl(CONF1_ADDR_PORT, oldval1);
+ outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
+ mode1res = inl(CONF1_ADDR_PORT);
+ outl(CONF1_ADDR_PORT, oldval1);
- if (bootverbose)
- kprintf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",
- mode1res, CONF1_ENABLE_CHK1);
+ if (bootverbose)
+ kprintf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", mode1res,
+ CONF1_ENABLE_CHK1);
- if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
- if (pci_cfgcheck(32))
- return (cfgmech);
- }
+ if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
+ if (pci_cfgcheck(32))
+ return (cfgmech);
}
+ /* Type #1 didn't work, so try type #2. */
oldval2 = inb(CONF2_ENABLE_PORT);
if (bootverbose) {
kprintf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
- oldval2);
+ oldval2);
}
if ((oldval2 & 0xf0) == 0) {
- cfgmech = 2;
+ cfgmech = CFGMECH_2;
devmax = 16;
outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
outb(CONF2_ENABLE_PORT, oldval2);
if (bootverbose)
- kprintf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
- mode2res, CONF2_ENABLE_CHK);
+ kprintf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
+ mode2res, CONF2_ENABLE_CHK);
if (mode2res == CONF2_ENABLE_RES) {
if (bootverbose)
}
}
- cfgmech = 0;
+ /* Nothing worked, so punt. */
+ cfgmech = CFGMECH_NONE;
devmax = 0;
return (cfgmech);
}
+
+static int
+pciereg_cfgopen(void)
+{
+#ifdef PCIE_CFG_MECH
+ struct pcie_cfg_list *pcielist;
+ struct pcie_cfg_elem *pcie_array, *elem;
+#ifdef SMP
+ struct pcpu *pc;
+#endif
+ vm_offset_t va;
+ int i;
+
+ if (bootverbose)
+ kprintf("Setting up PCIe mappings for BAR 0x%x\n", pciebar);
+
+#ifdef SMP
+ SLIST_FOREACH(pc, &cpuhead, pc_allcpu)
+#endif
+ {
+
+ pcie_array = kmalloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE,
+ M_DEVBUF, M_NOWAIT);
+ if (pcie_array == NULL)
+ return (0);
+
+ va = kmem_alloc_nofault(&kernel_map, PCIE_CACHE * PAGE_SIZE);
+ if (va == 0) {
+ kfree(pcie_array, M_DEVBUF);
+ return (0);
+ }
+
+#ifdef SMP
+ pcielist = &pcie_list[pc->pc_cpuid];
+#else
+ pcielist = &pcie_list[0];
+#endif
+ TAILQ_INIT(pcielist);
+ for (i = 0; i < PCIE_CACHE; i++) {
+ elem = &pcie_array[i];
+ elem->vapage = va + (i * PAGE_SIZE);
+ elem->papage = 0;
+ TAILQ_INSERT_HEAD(pcielist, elem, elem);
+ }
+ }
+
+
+ cfgmech = CFGMECH_PCIE;
+ devmax = 32;
+ return (1);
+#else /* !PCIE_CFG_MECH */
+ return (0);
+#endif /* PCIE_CFG_MECH */
+}
+
+#define PCIE_PADDR(bar, reg, bus, slot, func) \
+ ((bar) | \
+ (((bus) & 0xff) << 20) | \
+ (((slot) & 0x1f) << 15) | \
+ (((func) & 0x7) << 12) | \
+ ((reg) & 0xfff))
+
+/*
+ * Find an element in the cache that matches the physical page desired, or
+ * create a new mapping from the least recently used element.
+ * A very simple LRU algorithm is used here, does it need to be more
+ * efficient?
+ */
+static __inline struct pcie_cfg_elem *
+pciereg_findelem(vm_paddr_t papage)
+{
+ struct pcie_cfg_list *pcielist;
+ struct pcie_cfg_elem *elem;
+ pcielist = &pcie_list[mycpuid];
+ TAILQ_FOREACH(elem, pcielist, elem) {
+ if (elem->papage == papage)
+ break;
+ }
+
+ if (elem == NULL) {
+ elem = TAILQ_LAST(pcielist, pcie_cfg_list);
+ if (elem->papage != 0) {
+ pmap_kremove(elem->vapage);
+ cpu_invlpg(&elem->vapage);
+ }
+ pmap_kenter(elem->vapage, papage);
+ elem->papage = papage;
+ }
+
+ if (elem != TAILQ_FIRST(pcielist)) {
+ TAILQ_REMOVE(pcielist, elem, elem);
+ TAILQ_INSERT_HEAD(pcielist, elem, elem);
+ }
+ return (elem);
+}
+
+static int
+pciereg_cfgread(int bus, int slot, int func, int reg, int bytes)
+{
+ struct pcie_cfg_elem *elem;
+ volatile vm_offset_t va;
+ vm_paddr_t pa, papage;
+ int data;
+
+ crit_enter();
+ pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
+ papage = pa & ~PAGE_MASK;
+ elem = pciereg_findelem(papage);
+ va = elem->vapage | (pa & PAGE_MASK);
+
+ switch (bytes) {
+ case 4:
+ data = *(volatile uint32_t *)(va);
+ break;
+ case 2:
+ data = *(volatile uint16_t *)(va);
+ break;
+ case 1:
+ data = *(volatile uint8_t *)(va);
+ break;
+ default:
+ panic("pciereg_cfgread: invalid width");
+ }
+
+ crit_exit();
+ return (data);
+}
+
+static void
+pciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
+{
+ struct pcie_cfg_elem *elem;
+ volatile vm_offset_t va;
+ vm_paddr_t pa, papage;
+
+ crit_enter();
+ pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
+ papage = pa & ~PAGE_MASK;
+ elem = pciereg_findelem(papage);
+ va = elem->vapage | (pa & PAGE_MASK);
+
+ switch (bytes) {
+ case 4:
+ *(volatile uint32_t *)(va) = data;
+ break;
+ case 2:
+ *(volatile uint16_t *)(va) = data;
+ break;
+ case 1:
+ *(volatile uint8_t *)(va) = data;
+ break;
+ default:
+ panic("pciereg_cfgwrite: invalid width");
+ }
+
+ crit_exit();
+}
-/*
+/*-
* Copyright (c) 1997, Stefan Esser <se@freebsd.org>
* All rights reserved.
*
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $FreeBSD: src/sys/i386/include/pci_cfgreg.h,v 1.4.2.1 2001/07/28 05:55:07 imp Exp $
- * $DragonFly: src/sys/bus/pci/i386/pci_cfgreg.h,v 1.3 2007/08/14 20:09:13 dillon Exp $
+ * $FreeBSD: src/sys/i386/include/pci_cfgreg.h,v 1.14.20.1 2009/04/15 03:14:26 kensmith Exp $
*
*/
-#ifndef _MACHINE_PCI_CFGREG_H_
-#define _MACHINE_PCI_CFGREG_H_
-
#define CONF1_ADDR_PORT 0x0cf8
#define CONF1_DATA_PORT 0x0cfc
int pci_cfgregopen(void);
u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes);
void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes);
-int pci_cfgintr(int bus, int device, int pin, int oldirq);
-int pci_probe_route_table(int bus);
-
-#define PCI_INVALID_IRQ 255
-#define PCI_INTERRUPT_VALID(x) ((x) != PCI_INVALID_IRQ)
-
-#endif
+void pci_pir_open(void);
+int pci_pir_probe(int bus, int require_parse);
+int pci_pir_route_interrupt(int bus, int device, int func, int pin);
--- /dev/null
+/*-
+ * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
+ * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2000, BSDi
+ * Copyright (c) 2004, John Baldwin <jhb@FreeBSD.org>
+ * 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 unmodified, 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.
+ *
+ * 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.
+ * __FBSDID("$FreeBSD: src/sys/i386/pci/pci_pir.c,v 1.120.2.1.4.1 2009/04/15 03:14:26 kensmith Exp $");
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_param.h>
+#include <machine/md_var.h>
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcireg.h>
+#include "pci_cfgreg.h"
+#include <machine/segments.h>
+#include <machine/pc/bios.h>
+
+#define NUM_ISA_INTERRUPTS 16
+
+/*
+ * A link device. Loosely based on the ACPI PCI link device. This doesn't
+ * try to support priorities for different ISA interrupts.
+ */
+struct pci_link {
+ TAILQ_ENTRY(pci_link) pl_links;
+ uint8_t pl_id;
+ uint8_t pl_irq;
+ uint16_t pl_irqmask;
+ int pl_references;
+ int pl_routed;
+};
+
+struct pci_link_lookup {
+ struct pci_link **pci_link_ptr;
+ int bus;
+ int device;
+ int pin;
+};
+
+struct pci_dev_lookup {
+ uint8_t link;
+ int bus;
+ int device;
+ int pin;
+};
+
+typedef void pir_entry_handler(struct PIR_entry *entry,
+ struct PIR_intpin* intpin, void *arg);
+
+static void pci_print_irqmask(u_int16_t irqs);
+static int pci_pir_biosroute(int bus, int device, int func, int pin,
+ int irq);
+static int pci_pir_choose_irq(struct pci_link *pci_link, int irqmask);
+static void pci_pir_create_links(struct PIR_entry *entry,
+ struct PIR_intpin *intpin, void *arg);
+static void pci_pir_dump_links(void);
+static struct pci_link *pci_pir_find_link(uint8_t link_id);
+static void pci_pir_find_link_handler(struct PIR_entry *entry,
+ struct PIR_intpin *intpin, void *arg);
+static void pci_pir_initial_irqs(struct PIR_entry *entry,
+ struct PIR_intpin *intpin, void *arg);
+static void pci_pir_parse(void);
+static uint8_t pci_pir_search_irq(int bus, int device, int pin);
+static int pci_pir_valid_irq(struct pci_link *pci_link, int irq);
+static void pci_pir_walk_table(pir_entry_handler *handler, void *arg);
+
+static MALLOC_DEFINE(M_PIR, "$PIR", "$PIR structures");
+
+static struct PIR_table *pci_route_table;
+static device_t pir_device;
+static int pci_route_count, pir_bios_irqs, pir_parsed;
+static TAILQ_HEAD(, pci_link) pci_links;
+static int pir_interrupt_weight[NUM_ISA_INTERRUPTS];
+
+/* sysctl vars */
+SYSCTL_DECL(_hw_pci);
+
+/* XXX this likely should live in a header file */
+#ifdef PC98
+/* IRQs 3, 5, 7, 9, 10, 11, 12, 13 */
+#define PCI_IRQ_OVERRIDE_MASK 0x3e68
+#else
+/* IRQs 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15 */
+#define PCI_IRQ_OVERRIDE_MASK 0xdef8
+#endif
+
+static uint32_t pci_irq_override_mask = PCI_IRQ_OVERRIDE_MASK;
+TUNABLE_INT("hw.pci.irq_override_mask", &pci_irq_override_mask);
+SYSCTL_INT(_hw_pci, OID_AUTO, irq_override_mask, CTLFLAG_RD,
+ &pci_irq_override_mask, PCI_IRQ_OVERRIDE_MASK,
+ "Mask of allowed irqs to try to route when it has no good clue about\n"
+ "which irqs it should use.");
+
+/*
+ * Look for the interrupt routing table.
+ *
+ * We use PCI BIOS's PIR table if it's available. $PIR is the standard way
+ * to do this. Sadly, some machines are not standards conforming and have
+ * _PIR instead. We shrug and cope by looking for both.
+ */
+void
+pci_pir_open(void)
+{
+ struct PIR_table *pt;
+ uint32_t sigaddr;
+ int i;
+ uint8_t ck, *cv;
+
+ /* Don't try if we've already found a table. */
+ if (pci_route_table != NULL)
+ return;
+
+ /* Look for $PIR and then _PIR. */
+ sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0);
+ if (sigaddr == 0)
+ sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0);
+ if (sigaddr == 0)
+ return;
+
+ /* If we found something, check the checksum and length. */
+ /* XXX - Use pmap_mapdev()? */
+ pt = (struct PIR_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
+ if (pt->pt_header.ph_length <= sizeof(struct PIR_header))
+ return;
+ for (cv = (u_int8_t *)pt, ck = 0, i = 0;
+ i < (pt->pt_header.ph_length); i++)
+ ck += cv[i];
+ if (ck != 0)
+ return;
+
+ /* Ok, we've got a valid table. */
+ pci_route_table = pt;
+ pci_route_count = (pt->pt_header.ph_length -
+ sizeof(struct PIR_header)) /
+ sizeof(struct PIR_entry);
+}
+
+/*
+ * Find the pci_link structure for a given link ID.
+ */
+static struct pci_link *
+pci_pir_find_link(uint8_t link_id)
+{
+ struct pci_link *pci_link;
+
+ TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
+ if (pci_link->pl_id == link_id)
+ return (pci_link);
+ }
+ return (NULL);
+}
+
+/*
+ * Find the link device associated with a PCI device in the table.
+ */
+static void
+pci_pir_find_link_handler(struct PIR_entry *entry, struct PIR_intpin *intpin,
+ void *arg)
+{
+ struct pci_link_lookup *lookup;
+
+ lookup = (struct pci_link_lookup *)arg;
+ if (entry->pe_bus == lookup->bus &&
+ entry->pe_device == lookup->device &&
+ intpin - entry->pe_intpin == lookup->pin)
+ *lookup->pci_link_ptr = pci_pir_find_link(intpin->link);
+}
+
+/*
+ * Check to see if a possible IRQ setting is valid.
+ */
+static int
+pci_pir_valid_irq(struct pci_link *pci_link, int irq)
+{
+
+ if (!PCI_INTERRUPT_VALID(irq))
+ return (0);
+ return (pci_link->pl_irqmask & (1 << irq));
+}
+
+/*
+ * Walk the $PIR executing the worker function for each valid intpin entry
+ * in the table. The handler is passed a pointer to both the entry and
+ * the intpin in the entry.
+ */
+static void
+pci_pir_walk_table(pir_entry_handler *handler, void *arg)
+{
+ struct PIR_entry *entry;
+ struct PIR_intpin *intpin;
+ int i, pin;
+
+ entry = &pci_route_table->pt_entry[0];
+ for (i = 0; i < pci_route_count; i++, entry++) {
+ intpin = &entry->pe_intpin[0];
+ for (pin = 0; pin < 4; pin++, intpin++)
+ if (intpin->link != 0)
+ handler(entry, intpin, arg);
+ }
+}
+
+static void
+pci_pir_create_links(struct PIR_entry *entry, struct PIR_intpin *intpin,
+ void *arg)
+{
+ struct pci_link *pci_link;
+
+ pci_link = pci_pir_find_link(intpin->link);
+ if (pci_link != NULL) {
+ pci_link->pl_references++;
+ if (intpin->irqs != pci_link->pl_irqmask) {
+ if (bootverbose)
+ kprintf(
+ "$PIR: Entry %d.%d.INT%c has different mask for link %#x, merging\n",
+ entry->pe_bus, entry->pe_device,
+ (intpin - entry->pe_intpin) + 'A',
+ pci_link->pl_id);
+ pci_link->pl_irqmask &= intpin->irqs;
+ }
+ } else {
+ pci_link = kmalloc(sizeof(struct pci_link), M_PIR, M_WAITOK);
+ pci_link->pl_id = intpin->link;
+ pci_link->pl_irqmask = intpin->irqs;
+ pci_link->pl_irq = PCI_INVALID_IRQ;
+ pci_link->pl_references = 1;
+ pci_link->pl_routed = 0;
+ TAILQ_INSERT_TAIL(&pci_links, pci_link, pl_links);
+ }
+}
+
+/*
+ * Look to see if any of the function on the PCI device at bus/device have
+ * an interrupt routed to intpin 'pin' by the BIOS.
+ */
+static uint8_t
+pci_pir_search_irq(int bus, int device, int pin)
+{
+ uint32_t value;
+ uint8_t func, maxfunc;
+
+ /* See if we have a valid device at function 0. */
+ value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1);
+ if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
+ return (PCI_INVALID_IRQ);
+ if (value & PCIM_MFDEV)
+ maxfunc = PCI_FUNCMAX;
+ else
+ maxfunc = 0;
+
+ /* Scan all possible functions at this device. */
+ for (func = 0; func <= maxfunc; func++) {
+ value = pci_cfgregread(bus, device, func, PCIR_DEVVENDOR, 4);
+ if (value == 0xffffffff)
+ continue;
+ value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1);
+
+ /*
+ * See if it uses the pin in question. Note that the passed
+ * in pin uses 0 for A, .. 3 for D whereas the intpin
+ * register uses 0 for no interrupt, 1 for A, .. 4 for D.
+ */
+ if (value != pin + 1)
+ continue;
+ value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1);
+ if (bootverbose)
+ kprintf(
+ "$PIR: Found matching pin for %d.%d.INT%c at func %d: %d\n",
+ bus, device, pin + 'A', func, value);
+ if (value != PCI_INVALID_IRQ)
+ return (value);
+ }
+ return (PCI_INVALID_IRQ);
+}
+
+/*
+ * Try to initialize IRQ based on this device's IRQ.
+ */
+static void
+pci_pir_initial_irqs(struct PIR_entry *entry, struct PIR_intpin *intpin,
+ void *arg)
+{
+ struct pci_link *pci_link;
+ uint8_t irq, pin;
+
+ pin = intpin - entry->pe_intpin;
+ pci_link = pci_pir_find_link(intpin->link);
+ irq = pci_pir_search_irq(entry->pe_bus, entry->pe_device, pin);
+ if (irq == PCI_INVALID_IRQ || irq == pci_link->pl_irq)
+ return;
+
+ /* Don't trust any BIOS IRQs greater than 15. */
+ if (irq >= NUM_ISA_INTERRUPTS) {
+ kprintf(
+ "$PIR: Ignoring invalid BIOS IRQ %d from %d.%d.INT%c for link %#x\n",
+ irq, entry->pe_bus, entry->pe_device, pin + 'A',
+ pci_link->pl_id);
+ return;
+ }
+
+ /*
+ * If we don't have an IRQ for this link yet, then we trust the
+ * BIOS, even if it seems invalid from the $PIR entries.
+ */
+ if (pci_link->pl_irq == PCI_INVALID_IRQ) {
+ if (!pci_pir_valid_irq(pci_link, irq))
+ kprintf(
+ "$PIR: Using invalid BIOS IRQ %d from %d.%d.INT%c for link %#x\n",
+ irq, entry->pe_bus, entry->pe_device, pin + 'A',
+ pci_link->pl_id);
+ pci_link->pl_irq = irq;
+ pci_link->pl_routed = 1;
+ return;
+ }
+
+ /*
+ * We have an IRQ and it doesn't match the current IRQ for this
+ * link. If the new IRQ is invalid, then warn about it and ignore
+ * it. If the old IRQ is invalid and the new IRQ is valid, then
+ * prefer the new IRQ instead. If both IRQs are valid, then just
+ * use the first one. Note that if we ever get into this situation
+ * we are having to guess which setting the BIOS actually routed.
+ * Perhaps we should just give up instead.
+ */
+ if (!pci_pir_valid_irq(pci_link, irq)) {
+ kprintf(
+ "$PIR: BIOS IRQ %d for %d.%d.INT%c is not valid for link %#x\n",
+ irq, entry->pe_bus, entry->pe_device, pin + 'A',
+ pci_link->pl_id);
+ } else if (!pci_pir_valid_irq(pci_link, pci_link->pl_irq)) {
+ kprintf(
+"$PIR: Preferring valid BIOS IRQ %d from %d.%d.INT%c for link %#x to IRQ %d\n",
+ irq, entry->pe_bus, entry->pe_device, pin + 'A',
+ pci_link->pl_id, pci_link->pl_irq);
+ pci_link->pl_irq = irq;
+ pci_link->pl_routed = 1;
+ } else
+ kprintf(
+ "$PIR: BIOS IRQ %d for %d.%d.INT%c does not match link %#x irq %d\n",
+ irq, entry->pe_bus, entry->pe_device, pin + 'A',
+ pci_link->pl_id, pci_link->pl_irq);
+}
+
+/*
+ * Parse $PIR to enumerate link devices and attempt to determine their
+ * initial state. This could perhaps be cleaner if we had drivers for the
+ * various interrupt routers as they could read the initial IRQ for each
+ * link.
+ */
+static void
+pci_pir_parse(void)
+{
+ char tunable_buffer[64];
+ struct pci_link *pci_link;
+ int i, irq;
+
+ /* Only parse once. */
+ if (pir_parsed)
+ return;
+ pir_parsed = 1;
+
+ /* Enumerate link devices. */
+ TAILQ_INIT(&pci_links);
+ pci_pir_walk_table(pci_pir_create_links, NULL);
+ if (bootverbose) {
+ kprintf("$PIR: Links after initial probe:\n");
+ pci_pir_dump_links();
+ }
+
+ /*
+ * Check to see if the BIOS has already routed any of the links by
+ * checking each device connected to each link to see if it has a
+ * valid IRQ.
+ */
+ pci_pir_walk_table(pci_pir_initial_irqs, NULL);
+ if (bootverbose) {
+ kprintf("$PIR: Links after initial IRQ discovery:\n");
+ pci_pir_dump_links();
+ }
+
+ /*
+ * Allow the user to override the IRQ for a given link device. We
+ * allow invalid IRQs to be specified but warn about them. An IRQ
+ * of 255 or 0 clears any preset IRQ.
+ */
+ i = 0;
+ TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
+ ksnprintf(tunable_buffer, sizeof(tunable_buffer),
+ "hw.pci.link.%#x.irq", pci_link->pl_id);
+ if (kgetenv_int(tunable_buffer, &irq) == 0)
+ continue;
+ if (irq == 0)
+ irq = PCI_INVALID_IRQ;
+ if (irq != PCI_INVALID_IRQ &&
+ !pci_pir_valid_irq(pci_link, irq) && bootverbose)
+ kprintf(
+ "$PIR: Warning, IRQ %d for link %#x is not listed as valid\n",
+ irq, pci_link->pl_id);
+ pci_link->pl_routed = 0;
+ pci_link->pl_irq = irq;
+ i = 1;
+ }
+ if (bootverbose && i) {
+ kprintf("$PIR: Links after tunable overrides:\n");
+ pci_pir_dump_links();
+ }
+
+ /*
+ * Build initial interrupt weights as well as bitmap of "known-good"
+ * IRQs that the BIOS has already used for PCI link devices.
+ */
+ TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
+ if (!PCI_INTERRUPT_VALID(pci_link->pl_irq))
+ continue;
+ pir_bios_irqs |= 1 << pci_link->pl_irq;
+ pir_interrupt_weight[pci_link->pl_irq] +=
+ pci_link->pl_references;
+ }
+ if (bootverbose) {
+ kprintf("$PIR: IRQs used by BIOS: ");
+ pci_print_irqmask(pir_bios_irqs);
+ kprintf("\n");
+ kprintf("$PIR: Interrupt Weights:\n[ ");
+ for (i = 0; i < NUM_ISA_INTERRUPTS; i++)
+ kprintf(" %3d", i);
+ kprintf(" ]\n[ ");
+ for (i = 0; i < NUM_ISA_INTERRUPTS; i++)
+ kprintf(" %3d", pir_interrupt_weight[i]);
+ kprintf(" ]\n");
+ }
+}
+
+/*
+ * Use the PCI BIOS to route an interrupt for a given device.
+ *
+ * Input:
+ * AX = PCIBIOS_ROUTE_INTERRUPT
+ * BH = bus
+ * BL = device [7:3] / function [2:0]
+ * CH = IRQ
+ * CL = Interrupt Pin (0x0A = A, ... 0x0D = D)
+ */
+static int
+pci_pir_biosroute(int bus, int device, int func, int pin, int irq)
+{
+ struct bios_regs args;
+
+ args.eax = PCIBIOS_ROUTE_INTERRUPT;
+ args.ebx = (bus << 8) | (device << 3) | func;
+ args.ecx = (irq << 8) | (0xa + pin);
+ return (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)));
+}
+
+
+/*
+ * Route a PCI interrupt using a link device from the $PIR.
+ */
+int
+pci_pir_route_interrupt(int bus, int device, int func, int pin)
+{
+ struct pci_link_lookup lookup;
+ struct pci_link *pci_link;
+ int error, irq;
+
+ if (pci_route_table == NULL)
+ return (PCI_INVALID_IRQ);
+
+ /* Lookup link device for this PCI device/pin. */
+ pci_link = NULL;
+ lookup.bus = bus;
+ lookup.device = device;
+ lookup.pin = pin - 1;
+ lookup.pci_link_ptr = &pci_link;
+ pci_pir_walk_table(pci_pir_find_link_handler, &lookup);
+ if (pci_link == NULL) {
+ kprintf("$PIR: No matching entry for %d.%d.INT%c\n", bus,
+ device, pin - 1 + 'A');
+ return (PCI_INVALID_IRQ);
+ }
+
+ /*
+ * Pick a new interrupt if we don't have one already. We look
+ * for an interrupt from several different sets. First, if
+ * this link only has one valid IRQ, use that. Second, we
+ * check the set of PCI only interrupts from the $PIR. Third,
+ * we check the set of known-good interrupts that the BIOS has
+ * already used. Lastly, we check the "all possible valid
+ * IRQs" set.
+ */
+ if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) {
+ if (pci_link->pl_irqmask != 0 && powerof2(pci_link->pl_irqmask))
+ irq = ffs(pci_link->pl_irqmask) - 1;
+ else
+ irq = pci_pir_choose_irq(pci_link,
+ pci_route_table->pt_header.ph_pci_irqs);
+ if (!PCI_INTERRUPT_VALID(irq))
+ irq = pci_pir_choose_irq(pci_link, pir_bios_irqs);
+ if (!PCI_INTERRUPT_VALID(irq))
+ irq = pci_pir_choose_irq(pci_link,
+ pci_irq_override_mask);
+ if (!PCI_INTERRUPT_VALID(irq)) {
+ if (bootverbose)
+ kprintf(
+ "$PIR: Failed to route interrupt for %d:%d INT%c\n",
+ bus, device, pin - 1 + 'A');
+ return (PCI_INVALID_IRQ);
+ }
+ pci_link->pl_irq = irq;
+ }
+
+ /* Ask the BIOS to route this IRQ if we haven't done so already. */
+ if (!pci_link->pl_routed) {
+ error = pci_pir_biosroute(bus, device, func, pin - 1,
+ pci_link->pl_irq);
+
+ /* Ignore errors when routing a unique interrupt. */
+ if (error && !powerof2(pci_link->pl_irqmask)) {
+ kprintf("$PIR: ROUTE_INTERRUPT failed.\n");
+ return (PCI_INVALID_IRQ);
+ }
+ pci_link->pl_routed = 1;
+
+ /* Ensure the interrupt is set to level/low trigger. */
+ KASSERT(pir_device != NULL, ("missing pir device"));
+ BUS_CONFIG_INTR(pir_device, pci_link->pl_irq,
+ INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW);
+ }
+ if (bootverbose)
+ kprintf("$PIR: %d:%d INT%c routed to irq %d\n", bus, device,
+ pin - 1 + 'A', pci_link->pl_irq);
+ return (pci_link->pl_irq);
+}
+
+/*
+ * Try to pick an interrupt for the specified link from the interrupts
+ * set in the mask.
+ */
+static int
+pci_pir_choose_irq(struct pci_link *pci_link, int irqmask)
+{
+ int i, irq, realmask;
+
+ /* XXX: Need to have a #define of known bad IRQs to also mask out? */
+ realmask = pci_link->pl_irqmask & irqmask;
+ if (realmask == 0)
+ return (PCI_INVALID_IRQ);
+
+ /* Find IRQ with lowest weight. */
+ irq = PCI_INVALID_IRQ;
+ for (i = 0; i < NUM_ISA_INTERRUPTS; i++) {
+ if (!(realmask & 1 << i))
+ continue;
+ if (irq == PCI_INVALID_IRQ ||
+ pir_interrupt_weight[i] < pir_interrupt_weight[irq])
+ irq = i;
+ }
+ if (bootverbose && PCI_INTERRUPT_VALID(irq)) {
+ kprintf("$PIR: Found IRQ %d for link %#x from ", irq,
+ pci_link->pl_id);
+ pci_print_irqmask(realmask);
+ kprintf("\n");
+ }
+ return (irq);
+}
+
+static void
+pci_print_irqmask(u_int16_t irqs)
+{
+ int i, first;
+
+ if (irqs == 0) {
+ kprintf("none");
+ return;
+ }
+ first = 1;
+ for (i = 0; i < 16; i++, irqs >>= 1)
+ if (irqs & 1) {
+ if (!first)
+ kprintf(" ");
+ else
+ first = 0;
+ kprintf("%d", i);
+ }
+}
+
+/*
+ * Display link devices.
+ */
+static void
+pci_pir_dump_links(void)
+{
+ struct pci_link *pci_link;
+
+ kprintf("Link IRQ Rtd Ref IRQs\n");
+ TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
+ kprintf("%#4x %3d %c %3d ", pci_link->pl_id,
+ pci_link->pl_irq, pci_link->pl_routed ? 'Y' : 'N',
+ pci_link->pl_references);
+ pci_print_irqmask(pci_link->pl_irqmask);
+ kprintf("\n");
+ }
+}
+
+/*
+ * See if any interrupts for a given PCI bus are routed in the PIR. Don't
+ * even bother looking if the BIOS doesn't support routing anyways. If we
+ * are probing a PCI-PCI bridge, then require_parse will be true and we should
+ * only succeed if a host-PCI bridge has already attached and parsed the PIR.
+ */
+int
+pci_pir_probe(int bus, int require_parse)
+{
+ int i;
+ if (pci_route_table == NULL || (require_parse && !pir_parsed))
+ return (0);
+ for (i = 0; i < pci_route_count; i++)
+ if (pci_route_table->pt_entry[i].pe_bus == bus)
+ return (1);
+ return (0);
+}
+
+/*
+ * The driver for the new-bus psuedo device pir0 for the $PIR table.
+ */
+
+static int
+pir_probe(device_t dev)
+{
+ char buf[64];
+kprintf("pir probe\n");
+ ksnprintf(buf, sizeof(buf), "PCI Interrupt Routing Table: %d Entries",
+ pci_route_count);
+ device_set_desc_copy(dev, buf);
+ return (0);
+}
+
+static int
+pir_attach(device_t dev)
+{
+
+ pci_pir_parse();
+ KASSERT(pir_device == NULL, ("Multiple pir devices"));
+ pir_device = dev;
+ return (0);
+}
+
+static void
+pir_resume_find_device(struct PIR_entry *entry, struct PIR_intpin *intpin,
+ void *arg)
+{
+ struct pci_dev_lookup *pd;
+
+ pd = (struct pci_dev_lookup *)arg;
+ if (intpin->link != pd->link || pd->bus != -1)
+ return;
+ pd->bus = entry->pe_bus;
+ pd->device = entry->pe_device;
+ pd->pin = intpin - entry->pe_intpin;
+}
+
+static int
+pir_resume(device_t dev)
+{
+ struct pci_dev_lookup pd;
+ struct pci_link *pci_link;
+ int error;
+
+ /* Ask the BIOS to re-route each link that was already routed. */
+ TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
+ if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) {
+ KASSERT(!pci_link->pl_routed,
+ ("link %#x is routed but has invalid PCI IRQ",
+ pci_link->pl_id));
+ continue;
+ }
+ if (pci_link->pl_routed) {
+ pd.bus = -1;
+ pd.link = pci_link->pl_id;
+ pci_pir_walk_table(pir_resume_find_device, &pd);
+ KASSERT(pd.bus != -1,
+ ("did not find matching entry for link %#x in the $PIR table",
+ pci_link->pl_id));
+ if (bootverbose)
+ device_printf(dev,
+ "Using %d.%d.INT%c to route link %#x to IRQ %d\n",
+ pd.bus, pd.device, pd.pin + 'A',
+ pci_link->pl_id, pci_link->pl_irq);
+ error = pci_pir_biosroute(pd.bus, pd.device, 0, pd.pin,
+ pci_link->pl_irq);
+ if (error)
+ device_printf(dev,
+ "ROUTE_INTERRUPT on resume for link %#x failed.\n",
+ pci_link->pl_id);
+ }
+ }
+ return (0);
+}
+
+static device_method_t pir_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pir_probe),
+ DEVMETHOD(device_attach, pir_attach),
+ DEVMETHOD(device_resume, pir_resume),
+
+ { 0, 0 }
+};
+
+static driver_t pir_driver = {
+ "pir",
+ pir_methods,
+ 1,
+};
+
+static devclass_t pir_devclass;
+
+DRIVER_MODULE(pir, legacy, pir_driver, pir_devclass, 0, 0);
--- /dev/null
+/*-
+ * Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2000 BSDi
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ * __FBSDID("$FreeBSD: src/sys/dev/pci/ignore_pci.c,v 1.4.28.1 2009/04/15 03:14:26 kensmith Exp $");
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * 'Ignore' driver - eats devices that show up errnoeously on PCI
+ * but shouldn't ever be listed or handled by a driver.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <bus/pci/pcivar.h>
+
+static int ignore_pci_probe(device_t dev);
+
+static device_method_t ignore_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ignore_pci_probe),
+ DEVMETHOD(device_attach, bus_generic_attach),
+ { 0, 0 }
+};
+
+static driver_t ignore_pci_driver = {
+ "ignore_pci",
+ ignore_pci_methods,
+ 0,
+};
+
+static devclass_t ignore_pci_devclass;
+
+DRIVER_MODULE(ignore_pci, pci, ignore_pci_driver, ignore_pci_devclass, 0, 0);
+
+static int
+ignore_pci_probe(device_t dev)
+{
+ switch (pci_get_devid(dev)) {
+ case 0x10001042ul: /* SMC 37C665 */
+ device_set_desc(dev, "ignored");
+ device_quiet(dev);
+ return(-10000);
+ }
+ return(ENXIO);
+}
--- /dev/null
+/*-
+ * Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier
+ * Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2000 BSDi
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ * __FBSDID("$FreeBSD: src/sys/dev/pci/isa_pci.c,v 1.13.8.1 2009/04/15 03:14:26 kensmith Exp $");
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * PCI:ISA bridge support
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <bus/isa/isavar.h>
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcireg.h>
+
+static int isab_probe(device_t dev);
+
+#if 0
+int
+isab_attach(device_t dev)
+{
+ return ENXIO;
+}
+#endif
+
+static device_method_t isab_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, isab_probe),
+ DEVMETHOD(device_attach, isab_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ { 0, 0 }
+};
+
+static driver_t isab_driver = {
+ "isab",
+ isab_methods,
+ 0,
+};
+
+devclass_t isab_devclass;
+
+DRIVER_MODULE(isab, pci, isab_driver, isab_devclass, 0, 0);
+
+/*
+ * XXX we need to add a quirk list here for bridges that don't correctly
+ * report themselves.
+ */
+static int
+isab_probe(device_t dev)
+{
+ int matched = 0;
+
+ /*
+ * Try for a generic match based on class/subclass.
+ */
+ if ((pci_get_class(dev) == PCIC_BRIDGE) &&
+ (pci_get_subclass(dev) == PCIS_BRIDGE_ISA)) {
+ matched = 1;
+ } else {
+ /*
+ * These are devices that we *know* are PCI:ISA bridges.
+ * Sometimes, however, they don't report themselves as
+ * such. Check in case one of them is pretending to be
+ * something else.
+ */
+ switch (pci_get_devid(dev)) {
+ case 0x04848086: /* Intel 82378ZB/82378IB */
+ case 0x122e8086: /* Intel 82371FB */
+ case 0x70008086: /* Intel 82371SB */
+ case 0x71108086: /* Intel 82371AB */
+ case 0x71988086: /* Intel 82443MX */
+ case 0x24108086: /* Intel 82801AA (ICH) */
+ case 0x24208086: /* Intel 82801AB (ICH0) */
+ case 0x24408086: /* Intel 82801AB (ICH2) */
+ case 0x00061004: /* VLSI 82C593 */
+ case 0x05861106: /* VIA 82C586 */
+ case 0x05961106: /* VIA 82C596 */
+ case 0x06861106: /* VIA 82C686 */
+ case 0x153310b9: /* AcerLabs M1533 */
+ case 0x154310b9: /* AcerLabs M1543 */
+ case 0x00081039: /* SiS 85c503 */
+ case 0x00001078: /* Cyrix Cx5510 */
+ case 0x01001078: /* Cyrix Cx5530 */
+ case 0xc7001045: /* OPTi 82C700 (FireStar) */
+ case 0x00011033: /* NEC 0001 (C-bus) */
+ case 0x002c1033: /* NEC 002C (C-bus) */
+ case 0x003b1033: /* NEC 003B (C-bus) */
+ case 0x886a1060: /* UMC UM8886 ISA */
+ case 0x02001166: /* ServerWorks IB6566 PCI */
+ if (bootverbose)
+ kprintf("PCI-ISA bridge with incorrect subclass 0x%x\n",
+ pci_get_subclass(dev));
+ matched = 1;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (matched) {
+ device_set_desc(dev, "PCI-ISA bridge");
+ return(-10000);
+ }
+ return(ENXIO);
+}
-/*
- * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
+/*-
+ * Copyright (c) 1997, Stefan Esser <se@kfreebsd.org>
+ * Copyright (c) 2000, Michael Smith <msmith@kfreebsd.org>
+ * Copyright (c) 2000, BSDi
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* 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.
- *
- * $FreeBSD: src/sys/pci/pci.c,v 1.141.2.15 2002/04/30 17:48:18 tmm Exp $
- * $DragonFly: src/sys/bus/pci/pci.c,v 1.58 2008/11/16 18:44:00 swildner Exp $
- *
+ * __FBSDID("$FreeBSD: src/sys/dev/pci/pci.c,v 1.355.2.9.2.1 2009/04/15 03:14:26 kensmith Exp $");
*/
-#include "opt_bus.h"
-#include "opt_pci.h"
+#include <sys/cdefs.h>
-#include "opt_compat_oldpci.h"
+#include "opt_bus.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/module.h>
+#include <sys/linker.h>
#include <sys/fcntl.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/queue.h>
-#include <sys/types.h>
#include <sys/sysctl.h>
-#include <sys/buf.h>
+#include <sys/endian.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <sys/bus.h>
#include <sys/rman.h>
-#include <machine/smp.h>
-#include "pci_cfgreg.h"
+#include <sys/device.h>
#include <sys/pciio.h>
-#include "pcireg.h"
-#include "pcivar.h"
-#include "pci_private.h"
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pci_private.h>
#include "pcib_if.h"
+#include "pci_if.h"
+
+#ifdef __HAVE_ACPI
+#include <contrib/dev/acpica/acpi.h>
+#include "acpi_if.h"
+#else
+#define ACPI_PWR_FOR_SLEEP(x, y, z)
+#endif
+
+static uint32_t pci_mapbase(unsigned mapreg);
+static const char *pci_maptype(unsigned mapreg);
+static int pci_mapsize(unsigned testval);
+static int pci_maprange(unsigned mapreg);
+static void pci_fixancient(pcicfgregs *cfg);
+
+static int pci_porten(device_t pcib, int b, int s, int f);
+static int pci_memen(device_t pcib, int b, int s, int f);
+static void pci_assign_interrupt(device_t bus, device_t dev,
+ int force_route);
+static int pci_add_map(device_t pcib, device_t bus, device_t dev,
+ int b, int s, int f, int reg,
+ struct resource_list *rl, int force, int prefetch);
+static int pci_probe(device_t dev);
+static int pci_attach(device_t dev);
+static void pci_load_vendor_data(void);
+static int pci_describe_parse_line(char **ptr, int *vendor,
+ int *device, char **desc);
+static char *pci_describe_device(device_t dev);
+static int pci_modevent(module_t mod, int what, void *arg);
+static void pci_hdrtypedata(device_t pcib, int b, int s, int f,
+ pcicfgregs *cfg);
+static void pci_read_extcap(device_t pcib, pcicfgregs *cfg);
+static int pci_read_vpd_reg(device_t pcib, pcicfgregs *cfg,
+ int reg, uint32_t *data);
+#if 0
+static int pci_write_vpd_reg(device_t pcib, pcicfgregs *cfg,
+ int reg, uint32_t data);
+#endif
+static void pci_read_vpd(device_t pcib, pcicfgregs *cfg);
+static void pci_disable_msi(device_t dev);
+static void pci_enable_msi(device_t dev, uint64_t address,
+ uint16_t data);
+static void pci_enable_msix(device_t dev, u_int index,
+ uint64_t address, uint32_t data);
+static void pci_mask_msix(device_t dev, u_int index);
+static void pci_unmask_msix(device_t dev, u_int index);
+static int pci_msi_blacklisted(void);
+static void pci_resume_msi(device_t dev);
+static void pci_resume_msix(device_t dev);
+
+static device_method_t pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pci_probe),
+ DEVMETHOD(device_attach, pci_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, pci_suspend),
+ DEVMETHOD(device_resume, pci_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, pci_print_child),
+ DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch),
+ DEVMETHOD(bus_read_ivar, pci_read_ivar),
+ DEVMETHOD(bus_write_ivar, pci_write_ivar),
+ DEVMETHOD(bus_driver_added, pci_driver_added),
+ DEVMETHOD(bus_setup_intr, pci_setup_intr),
+ DEVMETHOD(bus_teardown_intr, pci_teardown_intr),
+
+ DEVMETHOD(bus_get_resource_list,pci_get_resource_list),
+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
+ DEVMETHOD(bus_delete_resource, pci_delete_resource),
+ DEVMETHOD(bus_alloc_resource, pci_alloc_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method),
+ DEVMETHOD(bus_child_location_str, pci_child_location_str_method),
+
+ /* PCI interface */
+ DEVMETHOD(pci_read_config, pci_read_config_method),
+ DEVMETHOD(pci_write_config, pci_write_config_method),
+ DEVMETHOD(pci_enable_busmaster, pci_enable_busmaster_method),
+ DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method),
+ DEVMETHOD(pci_enable_io, pci_enable_io_method),
+ DEVMETHOD(pci_disable_io, pci_disable_io_method),
+ DEVMETHOD(pci_get_vpd_ident, pci_get_vpd_ident_method),
+ DEVMETHOD(pci_get_vpd_readonly, pci_get_vpd_readonly_method),
+ DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method),
+ DEVMETHOD(pci_set_powerstate, pci_set_powerstate_method),
+ DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method),
+ DEVMETHOD(pci_find_extcap, pci_find_extcap_method),
+ DEVMETHOD(pci_alloc_msi, pci_alloc_msi_method),
+ DEVMETHOD(pci_alloc_msix, pci_alloc_msix_method),
+ DEVMETHOD(pci_remap_msix, pci_remap_msix_method),
+ DEVMETHOD(pci_release_msi, pci_release_msi_method),
+ DEVMETHOD(pci_msi_count, pci_msi_count_method),
+ DEVMETHOD(pci_msix_count, pci_msix_count_method),
+
+ { 0, 0 }
+};
+
+DEFINE_CLASS_0(pci, pci_driver, pci_methods, 0);
-devclass_t pci_devclass;
-const char *pcib_owner;
+static devclass_t pci_devclass;
+DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0);
+MODULE_VERSION(pci, 1);
+
+static char *pci_vendordata;
+static size_t pci_vendordata_size;
-static void pci_read_capabilities(device_t dev, pcicfgregs *cfg);
-static int pcie_slotimpl(const pcicfgregs *);
struct pci_quirk {
- u_int32_t devid; /* Vendor/device of the card */
+ uint32_t devid; /* Vendor/device of the card */
int type;
-#define PCI_QUIRK_MAP_REG 1 /* PCI map register in weird place */
+#define PCI_QUIRK_MAP_REG 1 /* PCI map register in weird place */
+#define PCI_QUIRK_DISABLE_MSI 2 /* MSI/MSI-X doesn't work */
int arg1;
int arg2;
};
struct pci_quirk pci_quirks[] = {
- /*
- * The Intel 82371AB and 82443MX has a map register at offset 0x90.
- */
+ /* The Intel 82371AB and 82443MX has a map register at offset 0x90. */
{ 0x71138086, PCI_QUIRK_MAP_REG, 0x90, 0 },
{ 0x719b8086, PCI_QUIRK_MAP_REG, 0x90, 0 },
/* As does the Serverworks OSB4 (the SMBus mapping register) */
{ 0x02001166, PCI_QUIRK_MAP_REG, 0x90, 0 },
+ /*
+ * MSI doesn't work with the ServerWorks CNB20-HE Host Bridge
+ * or the CMIC-SL (AKA ServerWorks GC_LE).
+ */
+ { 0x00141166, PCI_QUIRK_DISABLE_MSI, 0, 0 },
+ { 0x00171166, PCI_QUIRK_DISABLE_MSI, 0, 0 },
+
+ /*
+ * MSI doesn't work on earlier Intel chipsets including
+ * E7500, E7501, E7505, 845, 865, 875/E7210, and 855.
+ */
+ { 0x25408086, PCI_QUIRK_DISABLE_MSI, 0, 0 },
+ { 0x254c8086, PCI_QUIRK_DISABLE_MSI, 0, 0 },
+ { 0x25508086, PCI_QUIRK_DISABLE_MSI, 0, 0 },
+ { 0x25608086, PCI_QUIRK_DISABLE_MSI, 0, 0 },
+ { 0x25708086, PCI_QUIRK_DISABLE_MSI, 0, 0 },
+ { 0x25788086, PCI_QUIRK_DISABLE_MSI, 0, 0 },
+ { 0x35808086, PCI_QUIRK_DISABLE_MSI, 0, 0 },
+
+ /*
+ * MSI doesn't work with devices behind the AMD 8131 HT-PCIX
+ * bridge.
+ */
+ { 0x74501022, PCI_QUIRK_DISABLE_MSI, 0, 0 },
+
{ 0 }
};
/* map register information */
-#define PCI_MAPMEM 0x01 /* memory map */
-#define PCI_MAPMEMP 0x02 /* prefetchable memory map */
-#define PCI_MAPPORT 0x04 /* port map */
+#define PCI_MAPMEM 0x01 /* memory map */
+#define PCI_MAPMEMP 0x02 /* prefetchable memory map */
+#define PCI_MAPPORT 0x04 /* port map */
+
+struct devlist pci_devq;
+uint32_t pci_generation;
+uint32_t pci_numdevs = 0;
+static int pcie_chipset, pcix_chipset;
+
+/* sysctl vars */
+SYSCTL_NODE(_hw, OID_AUTO, pci, CTLFLAG_RD, 0, "PCI bus tuning parameters");
+
+static int pci_enable_io_modes = 1;
+TUNABLE_INT("hw.pci.enable_io_modes", &pci_enable_io_modes);
+SYSCTL_INT(_hw_pci, OID_AUTO, enable_io_modes, CTLFLAG_RW,
+ &pci_enable_io_modes, 1,
+ "Enable I/O and memory bits in the config register. Some BIOSes do not\n\
+enable these bits correctly. We'd like to do this all the time, but there\n\
+are some peripherals that this causes problems with.");
-static STAILQ_HEAD(devlist, pci_devinfo) pci_devq;
-u_int32_t pci_numdevs = 0;
-static u_int32_t pci_generation = 0;
-
-SYSCTL_NODE(_hw, OID_AUTO, pci, CTLFLAG_RD, 0, "pci parameters");
static int pci_do_power_nodriver = 0;
TUNABLE_INT("hw.pci.do_power_nodriver", &pci_do_power_nodriver);
SYSCTL_INT(_hw_pci, OID_AUTO, do_power_nodriver, CTLFLAG_RW,
&pci_do_power_nodriver, 0,
"Place a function into D3 state when no driver attaches to it. 0 means\n\
disable. 1 means conservatively place devices into D3 state. 2 means\n\
-aggressively place devices into D3 state. 3 means put absolutely everything\n\
+agressively place devices into D3 state. 3 means put absolutely everything\n\
in D3 state.");
+static int pci_do_power_resume = 1;
+TUNABLE_INT("hw.pci.do_power_resume", &pci_do_power_resume);
+SYSCTL_INT(_hw_pci, OID_AUTO, do_power_resume, CTLFLAG_RW,
+ &pci_do_power_resume, 1,
+ "Transition from D3 -> D0 on resume.");
+
+static int pci_do_msi = 1;
+TUNABLE_INT("hw.pci.enable_msi", &pci_do_msi);
+SYSCTL_INT(_hw_pci, OID_AUTO, enable_msi, CTLFLAG_RW, &pci_do_msi, 1,
+ "Enable support for MSI interrupts");
+
+static int pci_do_msix = 1;
+TUNABLE_INT("hw.pci.enable_msix", &pci_do_msix);
+SYSCTL_INT(_hw_pci, OID_AUTO, enable_msix, CTLFLAG_RW, &pci_do_msix, 1,
+ "Enable support for MSI-X interrupts");
+
+static int pci_honor_msi_blacklist = 1;
+TUNABLE_INT("hw.pci.honor_msi_blacklist", &pci_honor_msi_blacklist);
+SYSCTL_INT(_hw_pci, OID_AUTO, honor_msi_blacklist, CTLFLAG_RD,
+ &pci_honor_msi_blacklist, 1, "Honor chipset blacklist for MSI");
+
+/* Find a device_t by bus/slot/function in domain 0 */
+
+device_t
+pci_find_bsf(uint8_t bus, uint8_t slot, uint8_t func)
+{
+
+ return (pci_find_dbsf(0, bus, slot, func));
+}
+
+/* Find a device_t by domain/bus/slot/function */
+
device_t
-pci_find_bsf(u_int8_t bus, u_int8_t slot, u_int8_t func)
+pci_find_dbsf(uint32_t domain, uint8_t bus, uint8_t slot, uint8_t func)
{
struct pci_devinfo *dinfo;
STAILQ_FOREACH(dinfo, &pci_devq, pci_links) {
- if ((dinfo->cfg.bus == bus) &&
+ if ((dinfo->cfg.domain == domain) &&
+ (dinfo->cfg.bus == bus) &&
(dinfo->cfg.slot == slot) &&
(dinfo->cfg.func == func)) {
return (dinfo->cfg.dev);
return (NULL);
}
+/* Find a device_t by vendor/device ID */
+
device_t
-pci_find_device(u_int16_t vendor, u_int16_t device)
+pci_find_device(uint16_t vendor, uint16_t device)
{
struct pci_devinfo *dinfo;
return (NULL);
}
-int
-pcie_slot_implemented(device_t dev)
-{
- struct pci_devinfo *dinfo = device_get_ivars(dev);
-
- return pcie_slotimpl(&dinfo->cfg);
-}
-
-void
-pcie_set_max_readrq(device_t dev, uint16_t rqsize)
-{
- uint8_t expr_ptr;
- uint16_t val;
-
- rqsize &= PCIEM_DEVCTL_MAX_READRQ_MASK;
- if (rqsize > PCIEM_DEVCTL_MAX_READRQ_4096) {
- panic("%s: invalid max read request size 0x%02x\n",
- device_get_nameunit(dev), rqsize);
- }
-
- expr_ptr = pci_get_pciecap_ptr(dev);
- if (!expr_ptr)
- panic("%s: not PCIe device\n", device_get_nameunit(dev));
-
- val = pci_read_config(dev, expr_ptr + PCIER_DEVCTRL, 2);
- if ((val & PCIEM_DEVCTL_MAX_READRQ_MASK) != rqsize) {
- if (bootverbose)
- device_printf(dev, "adjust device control 0x%04x", val);
-
- val &= ~PCIEM_DEVCTL_MAX_READRQ_MASK;
- val |= rqsize;
- pci_write_config(dev, expr_ptr + PCIER_DEVCTRL, val, 2);
-
- if (bootverbose)
- kprintf(" -> 0x%04x\n", val);
- }
-}
-
/* return base address of memory or port map */
-static u_int32_t
-pci_mapbase(unsigned mapreg)
+static uint32_t
+pci_mapbase(uint32_t mapreg)
{
- int mask = 0x03;
- if ((mapreg & 0x01) == 0)
- mask = 0x0f;
- return (mapreg & ~mask);
+
+ if (PCI_BAR_MEM(mapreg))
+ return (mapreg & PCIM_BAR_MEM_BASE);
+ else
+ return (mapreg & PCIM_BAR_IO_BASE);
}
/* return map type of memory or port map */
-static int
+static const char *
pci_maptype(unsigned mapreg)
{
- static u_int8_t maptype[0x10] = {
- PCI_MAPMEM, PCI_MAPPORT,
- PCI_MAPMEM, 0,
- PCI_MAPMEM, PCI_MAPPORT,
- 0, 0,
- PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT,
- PCI_MAPMEM|PCI_MAPMEMP, 0,
- PCI_MAPMEM|PCI_MAPMEMP, PCI_MAPPORT,
- 0, 0,
- };
- return maptype[mapreg & 0x0f];
+ if (PCI_BAR_IO(mapreg))
+ return ("I/O Port");
+ if (mapreg & PCIM_BAR_MEM_PREFETCH)
+ return ("Prefetchable Memory");
+ return ("Memory");
}
/* return log2 of map size decoded for memory or port map */
static int
-pci_mapsize(unsigned testval)
+pci_mapsize(uint32_t testval)
{
int ln2size;
pci_maprange(unsigned mapreg)
{
int ln2range = 0;
- switch (mapreg & 0x07) {
- case 0x00:
- case 0x01:
- case 0x05:
+
+ if (PCI_BAR_IO(mapreg))
ln2range = 32;
- break;
- case 0x02:
- ln2range = 20;
- break;
- case 0x04:
- ln2range = 64;
- break;
- }
+ else
+ switch (mapreg & PCIM_BAR_MEM_TYPE) {
+ case PCIM_BAR_MEM_32:
+ ln2range = 32;
+ break;
+ case PCIM_BAR_MEM_1MB:
+ ln2range = 20;
+ break;
+ case PCIM_BAR_MEM_64:
+ ln2range = 64;
+ break;
+ }
return (ln2range);
}
cfg->hdrtype = 1;
}
-/* read config data specific to header type 1 device (PCI to PCI bridge) */
-
-static void *
-pci_readppb(device_t pcib, int b, int s, int f)
-{
- pcih1cfgregs *p;
-
- p = kmalloc(sizeof (pcih1cfgregs), M_DEVBUF, M_WAITOK | M_ZERO);
-
- p->secstat = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_SECSTAT_1, 2);
- p->bridgectl = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_BRIDGECTL_1, 2);
-
- p->seclat = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_SECLAT_1, 1);
-
- p->iobase = PCI_PPBIOBASE (PCIB_READ_CONFIG(pcib, b, s, f,
- PCIR_IOBASEH_1, 2),
- PCIB_READ_CONFIG(pcib, b, s, f,
- PCIR_IOBASEL_1, 1));
- p->iolimit = PCI_PPBIOLIMIT (PCIB_READ_CONFIG(pcib, b, s, f,
- PCIR_IOLIMITH_1, 2),
- PCIB_READ_CONFIG(pcib, b, s, f,
- PCIR_IOLIMITL_1, 1));
-
- p->membase = PCI_PPBMEMBASE (0,
- PCIB_READ_CONFIG(pcib, b, s, f,
- PCIR_MEMBASE_1, 2));
- p->memlimit = PCI_PPBMEMLIMIT (0,
- PCIB_READ_CONFIG(pcib, b, s, f,
- PCIR_MEMLIMIT_1, 2));
-
- p->pmembase = PCI_PPBMEMBASE (
- (pci_addr_t)PCIB_READ_CONFIG(pcib, b, s, f, PCIR_PMBASEH_1, 4),
- PCIB_READ_CONFIG(pcib, b, s, f, PCIR_PMBASEL_1, 2));
-
- p->pmemlimit = PCI_PPBMEMLIMIT (
- (pci_addr_t)PCIB_READ_CONFIG(pcib, b, s, f,
- PCIR_PMLIMITH_1, 4),
- PCIB_READ_CONFIG(pcib, b, s, f, PCIR_PMLIMITL_1, 2));
-
- return (p);
-}
-
-/* read config data specific to header type 2 device (PCI to CardBus bridge) */
-
-static void *
-pci_readpcb(device_t pcib, int b, int s, int f)
-{
- pcih2cfgregs *p;
-
- p = kmalloc(sizeof (pcih2cfgregs), M_DEVBUF, M_WAITOK | M_ZERO);
-
- p->secstat = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_SECSTAT_2, 2);
- p->bridgectl = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_BRIDGECTL_2, 2);
-
- p->seclat = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_SECLAT_2, 1);
-
- p->membase0 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_MEMBASE0_2, 4);
- p->memlimit0 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_MEMLIMIT0_2, 4);
- p->membase1 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_MEMBASE1_2, 4);
- p->memlimit1 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_MEMLIMIT1_2, 4);
-
- p->iobase0 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_IOBASE0_2, 4);
- p->iolimit0 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_IOLIMIT0_2, 4);
- p->iobase1 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_IOBASE1_2, 4);
- p->iolimit1 = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_IOLIMIT1_2, 4);
-
- p->pccardif = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_PCCARDIF_2, 4);
- return p;
-}
-
/* extract header type specific config data */
static void
pci_hdrtypedata(device_t pcib, int b, int s, int f, pcicfgregs *cfg)
{
-#define REG(n,w) PCIB_READ_CONFIG(pcib, b, s, f, n, w)
+#define REG(n, w) PCIB_READ_CONFIG(pcib, b, s, f, n, w)
switch (cfg->hdrtype) {
case 0:
cfg->subvendor = REG(PCIR_SUBVEND_0, 2);
cfg->nummaps = PCI_MAXMAPS_0;
break;
case 1:
- cfg->subvendor = REG(PCIR_SUBVEND_1, 2);
- cfg->subdevice = REG(PCIR_SUBDEV_1, 2);
- cfg->secondarybus = REG(PCIR_SECBUS_1, 1);
- cfg->subordinatebus = REG(PCIR_SUBBUS_1, 1);
cfg->nummaps = PCI_MAXMAPS_1;
- cfg->hdrspec = pci_readppb(pcib, b, s, f);
break;
case 2:
cfg->subvendor = REG(PCIR_SUBVEND_2, 2);
cfg->subdevice = REG(PCIR_SUBDEV_2, 2);
- cfg->secondarybus = REG(PCIR_SECBUS_2, 1);
- cfg->subordinatebus = REG(PCIR_SUBBUS_2, 1);
cfg->nummaps = PCI_MAXMAPS_2;
- cfg->hdrspec = pci_readpcb(pcib, b, s, f);
break;
}
#undef REG
}
-/* read configuration header into pcicfgrect structure */
-
+/* read configuration header into pcicfgregs structure */
struct pci_devinfo *
-pci_read_device(device_t pcib, int b, int s, int f, size_t size)
+pci_read_device(device_t pcib, int d, int b, int s, int f, size_t size)
{
-#define REG(n, w) PCIB_READ_CONFIG(pcib, b, s, f, n, w)
-
+#define REG(n, w) PCIB_READ_CONFIG(pcib, b, s, f, n, w)
pcicfgregs *cfg = NULL;
struct pci_devinfo *devlist_entry;
struct devlist *devlist_head;
devlist_entry = NULL;
- if (PCIB_READ_CONFIG(pcib, b, s, f, PCIR_DEVVENDOR, 4) != -1) {
-
+ if (REG(PCIR_DEVVENDOR, 4) != -1) {
devlist_entry = kmalloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+ if (devlist_entry == NULL)
+ return (NULL);
cfg = &devlist_entry->cfg;
-
+
+ cfg->domain = d;
cfg->bus = b;
cfg->slot = s;
cfg->func = f;
cfg->intpin = REG(PCIR_INTPIN, 1);
cfg->intline = REG(PCIR_INTLINE, 1);
-#ifdef APIC_IO
- /*
- * If using the APIC the intpin is probably wrong, since it
- * is often setup by the BIOS with the PIC in mind.
- */
- if (cfg->intpin != 0) {
- int airq;
-
- airq = pci_apic_irq(cfg->bus, cfg->slot, cfg->intpin);
- if (airq >= 0) {
- /* PCI specific entry found in MP table */
- if (airq != cfg->intline) {
- undirect_pci_irq(cfg->intline);
- cfg->intline = airq;
- }
- } else {
- /*
- * PCI interrupts might be redirected to the
- * ISA bus according to some MP tables. Use the
- * same methods as used by the ISA devices
- * devices to find the proper IOAPIC int pin.
- */
- airq = isa_apic_irq(cfg->intline);
- if ((airq >= 0) && (airq != cfg->intline)) {
- /* XXX: undirect_pci_irq() ? */
- undirect_isa_irq(cfg->intline);
- cfg->intline = airq;
- }
- }
- }
-#endif /* APIC_IO */
-
cfg->mingnt = REG(PCIR_MINGNT, 1);
cfg->maxlat = REG(PCIR_MAXLAT, 1);
pci_fixancient(cfg);
pci_hdrtypedata(pcib, b, s, f, cfg);
- pci_read_capabilities(pcib, cfg);
+
+ if (REG(PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT)
+ pci_read_extcap(pcib, cfg);
STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links);
+ devlist_entry->conf.pc_sel.pc_domain = cfg->domain;
devlist_entry->conf.pc_sel.pc_bus = cfg->bus;
devlist_entry->conf.pc_sel.pc_dev = cfg->slot;
devlist_entry->conf.pc_sel.pc_func = cfg->func;
#undef REG
}
-static int
-pci_fixup_nextptr(int *nextptr0)
-{
- int nextptr = *nextptr0;
-
- /* "Next pointer" is only one byte */
- KASSERT(nextptr <= 0xff, ("Illegal next pointer %d\n", nextptr));
-
- if (nextptr & 0x3) {
- /*
- * PCI local bus spec 3.0:
- *
- * "... The bottom two bits of all pointers are reserved
- * and must be implemented as 00b although software must
- * mask them to allow for future uses of these bits ..."
- */
- if (bootverbose) {
- kprintf("Illegal PCI extended capability "
- "offset, fixup 0x%02x -> 0x%02x\n",
- nextptr, nextptr & ~0x3);
- }
- nextptr &= ~0x3;
- }
- *nextptr0 = nextptr;
-
- if (nextptr < 0x40) {
- if (nextptr != 0) {
- kprintf("Illegal PCI extended capability "
- "offset 0x%02x", nextptr);
- }
- return 0;
- }
- return 1;
-}
-
-static void
-pci_read_cap_pmgt(device_t pcib, int ptr, pcicfgregs *cfg)
-{
-#define REG(n, w) \
- PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, w)
-
- struct pcicfg_pmgt *pmgt = &cfg->pmgt;
-
- if (pmgt->pp_cap)
- return;
-
- pmgt->pp_cap = REG(ptr + PCIR_POWER_CAP, 2);
- pmgt->pp_status = ptr + PCIR_POWER_STATUS;
- pmgt->pp_pmcsr = ptr + PCIR_POWER_PMCSR;
- /*
- * XXX
- * Following way may be used to to test whether
- * 'data' register exists:
- * if 'data_select' register of
- * PCIR_POWER_STATUS(bits[12,9]) is read-only
- * then 'data' register is _not_ implemented.
- */
- pmgt->pp_data = 0;
-
-#undef REG
-}
-
-static int
-pcie_slotimpl(const pcicfgregs *cfg)
-{
- const struct pcicfg_expr *expr = &cfg->expr;
- uint16_t port_type;
-
- /*
- * Only version 1 can be parsed currently
- */
- if ((expr->expr_cap & PCIEM_CAP_VER_MASK) != PCIEM_CAP_VER_1)
- return 0;
-
- /*
- * - Slot implemented bit is meaningful iff current port is
- * root port or down stream port.
- * - Testing for root port or down stream port is meanningful
- * iff PCI configure has type 1 header.
- */
-
- if (cfg->hdrtype != 1)
- return 0;
-
- port_type = expr->expr_cap & PCIEM_CAP_PORT_TYPE;
- if (port_type != PCIE_ROOT_PORT && port_type != PCIE_DOWN_STREAM_PORT)
- return 0;
-
- if (!(expr->expr_cap & PCIEM_CAP_SLOT_IMPL))
- return 0;
-
- return 1;
-}
-
-static void
-pci_read_cap_expr(device_t pcib, int ptr, pcicfgregs *cfg)
-{
-#define REG(n, w) \
- PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, w)
-
- struct pcicfg_expr *expr = &cfg->expr;
-
- expr->expr_ptr = ptr;
- expr->expr_cap = REG(ptr + PCIER_CAPABILITY, 2);
-
- /*
- * Only version 1 can be parsed currently
- */
- if ((expr->expr_cap & PCIEM_CAP_VER_MASK) != PCIEM_CAP_VER_1)
- return;
-
- /*
- * Read slot capabilities. Slot capabilities exists iff
- * current port's slot is implemented
- */
- if (pcie_slotimpl(cfg))
- expr->expr_slotcap = REG(ptr + PCIER_SLOTCAP, 4);
-
-#undef REG
-}
-
static void
-pci_read_capabilities(device_t pcib, pcicfgregs *cfg)
+pci_read_extcap(device_t pcib, pcicfgregs *cfg)
{
-#define REG(n, w) \
- PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, w)
-
- int nextptr, ptrptr;
-
- if ((REG(PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT) == 0) {
- /* No capabilities */
- return;
- }
+#define REG(n, w) PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, w)
+#define WREG(n, v, w) PCIB_WRITE_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, v, w)
+#if defined(__i386__) || defined(__amd64__)
+ uint64_t addr;
+#endif
+ uint32_t val;
+ int ptr, nextptr, ptrptr;
- switch (cfg->hdrtype) {
+ switch (cfg->hdrtype & PCIM_HDRTYPE) {
case 0:
case 1:
ptrptr = PCIR_CAP_PTR;
break;
case 2:
- ptrptr = PCIR_CAP_PTR_2;
+ ptrptr = PCIR_CAP_PTR_2; /* cardbus capabilities ptr */
break;
default:
- return; /* No capabilities support */
+ return; /* no extended capabilities support */
}
- nextptr = REG(ptrptr, 1);
+ nextptr = REG(ptrptr, 1); /* sanity check? */
/*
* Read capability entries.
*/
- while (pci_fixup_nextptr(&nextptr)) {
- int ptr = nextptr;
+ while (nextptr != 0) {
+ /* Sanity check */
+ if (nextptr > 255) {
+ kprintf("illegal PCI extended capability offset %d\n",
+ nextptr);
+ return;
+ }
+ /* Find the next entry */
+ ptr = nextptr;
+ nextptr = REG(ptr + PCICAP_NEXTPTR, 1);
/* Process this entry */
switch (REG(ptr + PCICAP_ID, 1)) {
case PCIY_PMG: /* PCI power management */
- pci_read_cap_pmgt(pcib, ptr, cfg);
+ if (cfg->pp.pp_cap == 0) {
+ cfg->pp.pp_cap = REG(ptr + PCIR_POWER_CAP, 2);
+ cfg->pp.pp_status = ptr + PCIR_POWER_STATUS;
+ cfg->pp.pp_pmcsr = ptr + PCIR_POWER_PMCSR;
+ if ((nextptr - ptr) > PCIR_POWER_DATA)
+ cfg->pp.pp_data = ptr + PCIR_POWER_DATA;
+ }
break;
- case PCIY_PCIX: /* PCI-X */
- cfg->pcixcap_ptr = ptr;
+#if notyet
+#if defined(__i386__) || defined(__amd64__)
+ case PCIY_HT: /* HyperTransport */
+ /* Determine HT-specific capability type. */
+ val = REG(ptr + PCIR_HT_COMMAND, 2);
+ switch (val & PCIM_HTCMD_CAP_MASK) {
+ case PCIM_HTCAP_MSI_MAPPING:
+ if (!(val & PCIM_HTCMD_MSI_FIXED)) {
+ /* Sanity check the mapping window. */
+ addr = REG(ptr + PCIR_HTMSI_ADDRESS_HI,
+ 4);
+ addr <<= 32;
+ addr |= REG(ptr + PCIR_HTMSI_ADDRESS_LO,
+ 4);
+ if (addr != MSI_INTEL_ADDR_BASE)
+ device_printf(pcib,
+ "HT Bridge at pci%d:%d:%d:%d has non-default MSI window 0x%llx\n",
+ cfg->domain, cfg->bus,
+ cfg->slot, cfg->func,
+ (long long)addr);
+ } else
+ addr = MSI_INTEL_ADDR_BASE;
+
+ cfg->ht.ht_msimap = ptr;
+ cfg->ht.ht_msictrl = val;
+ cfg->ht.ht_msiaddr = addr;
+ break;
+ }
+ break;
+#endif
+ case PCIY_MSI: /* PCI MSI */
+ cfg->msi.msi_location = ptr;
+ cfg->msi.msi_ctrl = REG(ptr + PCIR_MSI_CTRL, 2);
+ cfg->msi.msi_msgnum = 1 << ((cfg->msi.msi_ctrl &
+ PCIM_MSICTRL_MMC_MASK)>>1);
break;
- case PCIY_EXPRESS: /* PCI Express */
- pci_read_cap_expr(pcib, ptr, cfg);
+ case PCIY_MSIX: /* PCI MSI-X */
+ cfg->msix.msix_location = ptr;
+ cfg->msix.msix_ctrl = REG(ptr + PCIR_MSIX_CTRL, 2);
+ cfg->msix.msix_msgnum = (cfg->msix.msix_ctrl &
+ PCIM_MSIXCTRL_TABLE_SIZE) + 1;
+ val = REG(ptr + PCIR_MSIX_TABLE, 4);
+ cfg->msix.msix_table_bar = PCIR_BAR(val &
+ PCIM_MSIX_BIR_MASK);
+ cfg->msix.msix_table_offset = val & ~PCIM_MSIX_BIR_MASK;
+ val = REG(ptr + PCIR_MSIX_PBA, 4);
+ cfg->msix.msix_pba_bar = PCIR_BAR(val &
+ PCIM_MSIX_BIR_MASK);
+ cfg->msix.msix_pba_offset = val & ~PCIM_MSIX_BIR_MASK;
+ break;
+#endif
+ case PCIY_VPD: /* PCI Vital Product Data */
+ cfg->vpd.vpd_reg = ptr;
+ break;
+ case PCIY_SUBVENDOR:
+ /* Should always be true. */
+ if ((cfg->hdrtype & PCIM_HDRTYPE) == 1) {
+ val = REG(ptr + PCIR_SUBVENDCAP_ID, 4);
+ cfg->subvendor = val & 0xffff;
+ cfg->subdevice = val >> 16;
+ }
+ break;
+ case PCIY_PCIX: /* PCI-X */
+ /*
+ * Assume we have a PCI-X chipset if we have
+ * at least one PCI-PCI bridge with a PCI-X
+ * capability. Note that some systems with
+ * PCI-express or HT chipsets might match on
+ * this check as well.
+ */
+ if ((cfg->hdrtype & PCIM_HDRTYPE) == 1)
+ pcix_chipset = 1;
break;
- case PCIY_VPD: /* Vital Product Data */
- cfg->vpdcap_ptr = ptr;
+ case PCIY_EXPRESS: /* PCI-express */
+ /*
+ * Assume we have a PCI-express chipset if we have
+ * at least one PCI-express device.
+ */
+ pcie_chipset = 1;
break;
default:
break;
}
-
- /* Find the next entry */
- nextptr = REG(ptr + PCICAP_NEXTPTR, 1);
}
-
-#undef REG
+/* REG and WREG use carry through to next functions */
}
-/* free pcicfgregs structure and all depending data structures */
+/*
+ * PCI Vital Product Data
+ */
+
+#define PCI_VPD_TIMEOUT 1000000
-int
-pci_freecfg(struct pci_devinfo *dinfo)
+static int
+pci_read_vpd_reg(device_t pcib, pcicfgregs *cfg, int reg, uint32_t *data)
{
- struct devlist *devlist_head;
+ int count = PCI_VPD_TIMEOUT;
- devlist_head = &pci_devq;
+ KASSERT((reg & 3) == 0, ("VPD register must by 4 byte aligned"));
- if (dinfo->cfg.hdrspec != NULL)
- kfree(dinfo->cfg.hdrspec, M_DEVBUF);
- /* XXX this hasn't been tested */
- STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links);
- kfree(dinfo, M_DEVBUF);
+ WREG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, reg, 2);
- /* increment the generation count */
- pci_generation++;
+ while ((REG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, 2) & 0x8000) != 0x8000) {
+ if (--count < 0)
+ return (ENXIO);
+ DELAY(1); /* limit looping */
+ }
+ *data = (REG(cfg->vpd.vpd_reg + PCIR_VPD_DATA, 4));
- /* we're losing one device */
- pci_numdevs--;
return (0);
}
-
-/*
- * PCI power manangement
- */
-int
-pci_set_powerstate_method(device_t dev, device_t child, int state)
+#if 0
+static int
+pci_write_vpd_reg(device_t pcib, pcicfgregs *cfg, int reg, uint32_t data)
{
- struct pci_devinfo *dinfo = device_get_ivars(child);
- pcicfgregs *cfg = &dinfo->cfg;
+ int count = PCI_VPD_TIMEOUT;
+
+ KASSERT((reg & 3) == 0, ("VPD register must by 4 byte aligned"));
+
+ WREG(cfg->vpd.vpd_reg + PCIR_VPD_DATA, data, 4);
+ WREG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, reg | 0x8000, 2);
+ while ((REG(cfg->vpd.vpd_reg + PCIR_VPD_ADDR, 2) & 0x8000) == 0x8000) {
+ if (--count < 0)
+ return (ENXIO);
+ DELAY(1); /* limit looping */
+ }
+
+ return (0);
+}
+#endif
+
+#undef PCI_VPD_TIMEOUT
+
+struct vpd_readstate {
+ device_t pcib;
+ pcicfgregs *cfg;
+ uint32_t val;
+ int bytesinval;
+ int off;
+ uint8_t cksum;
+};
+
+static int
+vpd_nextbyte(struct vpd_readstate *vrs, uint8_t *data)
+{
+ uint32_t reg;
+ uint8_t byte;
+
+ if (vrs->bytesinval == 0) {
+ if (pci_read_vpd_reg(vrs->pcib, vrs->cfg, vrs->off, ®))
+ return (ENXIO);
+ vrs->val = le32toh(reg);
+ vrs->off += 4;
+ byte = vrs->val & 0xff;
+ vrs->bytesinval = 3;
+ } else {
+ vrs->val = vrs->val >> 8;
+ byte = vrs->val & 0xff;
+ vrs->bytesinval--;
+ }
+
+ vrs->cksum += byte;
+ *data = byte;
+ return (0);
+}
+
+void
+pcie_set_max_readrq(device_t dev, uint16_t rqsize)
+{
+ int expr_ptr;
+ uint16_t val;
+ rqsize &= PCIEM_DEVCTL_MAX_READRQ_MASK;
+ if (rqsize > PCIEM_DEVCTL_MAX_READRQ_4096) {
+ panic("%s: invalid max read request size 0x%02x\n",
+ device_get_nameunit(dev), rqsize);
+ }
+#warning "this code is incorrect, I think"
+ pci_find_extcap_method(device_get_parent(dev), dev, PCIY_EXPRESS, &expr_ptr);
+ if(!expr_ptr)
+ panic("%s: not PCI Express\n", device_get_nameunit(dev));
+ val = pci_read_config(dev, expr_ptr + PCIER_DEVCTRL, 2);
+ if ((val & PCIEM_DEVCTL_MAX_READRQ_MASK) != rqsize) {
+ if (bootverbose)
+ device_printf(dev, "adjust device control 0x%04x", val);
+ val &= ~PCIEM_DEVCTL_MAX_READRQ_MASK;
+ val |= rqsize;
+ pci_write_config(dev, expr_ptr + PCIER_DEVCTRL, val, 2);
+
+ if (bootverbose)
+ kprintf(" -> 0x%04x\n", val);
+ }
+}
+
+static void
+pci_read_vpd(device_t pcib, pcicfgregs *cfg)
+{
+ struct vpd_readstate vrs;
+ int state;
+ int name;
+ int remain;
+ int i;
+ int alloc, off; /* alloc/off for RO/W arrays */
+ int cksumvalid;
+ int dflen;
+ uint8_t byte;
+ uint8_t byte2;
+
+ /* init vpd reader */
+ vrs.bytesinval = 0;
+ vrs.off = 0;
+ vrs.pcib = pcib;
+ vrs.cfg = cfg;
+ vrs.cksum = 0;
+
+ state = 0;
+ name = remain = i = 0; /* shut up stupid gcc */
+ alloc = off = 0; /* shut up stupid gcc */
+ dflen = 0; /* shut up stupid gcc */
+ cksumvalid = -1;
+ while (state >= 0) {
+ if (vpd_nextbyte(&vrs, &byte)) {
+ state = -2;
+ break;
+ }
+#if 0
+ kprintf("vpd: val: %#x, off: %d, bytesinval: %d, byte: %#hhx, " \
+ "state: %d, remain: %d, name: %#x, i: %d\n", vrs.val,
+ vrs.off, vrs.bytesinval, byte, state, remain, name, i);
+#endif
+ switch (state) {
+ case 0: /* item name */
+ if (byte & 0x80) {
+ if (vpd_nextbyte(&vrs, &byte2)) {
+ state = -2;
+ break;
+ }
+ remain = byte2;
+ if (vpd_nextbyte(&vrs, &byte2)) {
+ state = -2;
+ break;
+ }
+ remain |= byte2 << 8;
+ if (remain > (0x7f*4 - vrs.off)) {
+ state = -1;
+ kprintf(
+ "pci%d:%d:%d:%d: invalid VPD data, remain %#x\n",
+ cfg->domain, cfg->bus, cfg->slot,
+ cfg->func, remain);
+ }
+ name = byte & 0x7f;
+ } else {
+ remain = byte & 0x7;
+ name = (byte >> 3) & 0xf;
+ }
+ switch (name) {
+ case 0x2: /* String */
+ cfg->vpd.vpd_ident = kmalloc(remain + 1,
+ M_DEVBUF, M_WAITOK);
+ i = 0;
+ state = 1;
+ break;
+ case 0xf: /* End */
+ state = -1;
+ break;
+ case 0x10: /* VPD-R */
+ alloc = 8;
+ off = 0;
+ cfg->vpd.vpd_ros = kmalloc(alloc *
+ sizeof(*cfg->vpd.vpd_ros), M_DEVBUF,
+ M_WAITOK | M_ZERO);
+ state = 2;
+ break;
+ case 0x11: /* VPD-W */
+ alloc = 8;
+ off = 0;
+ cfg->vpd.vpd_w = kmalloc(alloc *
+ sizeof(*cfg->vpd.vpd_w), M_DEVBUF,
+ M_WAITOK | M_ZERO);
+ state = 5;
+ break;
+ default: /* Invalid data, abort */
+ state = -1;
+ break;
+ }
+ break;
+
+ case 1: /* Identifier String */
+ cfg->vpd.vpd_ident[i++] = byte;
+ remain--;
+ if (remain == 0) {
+ cfg->vpd.vpd_ident[i] = '\0';
+ state = 0;
+ }
+ break;
+
+ case 2: /* VPD-R Keyword Header */
+ if (off == alloc) {
+ cfg->vpd.vpd_ros = kreallocf(cfg->vpd.vpd_ros,
+ (alloc *= 2) * sizeof(*cfg->vpd.vpd_ros),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ }
+ cfg->vpd.vpd_ros[off].keyword[0] = byte;
+ if (vpd_nextbyte(&vrs, &byte2)) {
+ state = -2;
+ break;
+ }
+ cfg->vpd.vpd_ros[off].keyword[1] = byte2;
+ if (vpd_nextbyte(&vrs, &byte2)) {
+ state = -2;
+ break;
+ }
+ dflen = byte2;
+ if (dflen == 0 &&
+ strncmp(cfg->vpd.vpd_ros[off].keyword, "RV",
+ 2) == 0) {
+ /*
+ * if this happens, we can't trust the rest
+ * of the VPD.
+ */
+ kprintf(
+ "pci%d:%d:%d:%d: bad keyword length: %d\n",
+ cfg->domain, cfg->bus, cfg->slot,
+ cfg->func, dflen);
+ cksumvalid = 0;
+ state = -1;
+ break;
+ } else if (dflen == 0) {
+ cfg->vpd.vpd_ros[off].value = kmalloc(1 *
+ sizeof(*cfg->vpd.vpd_ros[off].value),
+ M_DEVBUF, M_WAITOK);
+ cfg->vpd.vpd_ros[off].value[0] = '\x00';
+ } else
+ cfg->vpd.vpd_ros[off].value = kmalloc(
+ (dflen + 1) *
+ sizeof(*cfg->vpd.vpd_ros[off].value),
+ M_DEVBUF, M_WAITOK);
+ remain -= 3;
+ i = 0;
+ /* keep in sync w/ state 3's transistions */
+ if (dflen == 0 && remain == 0)
+ state = 0;
+ else if (dflen == 0)
+ state = 2;
+ else
+ state = 3;
+ break;
+
+ case 3: /* VPD-R Keyword Value */
+ cfg->vpd.vpd_ros[off].value[i++] = byte;
+ if (strncmp(cfg->vpd.vpd_ros[off].keyword,
+ "RV", 2) == 0 && cksumvalid == -1) {
+ if (vrs.cksum == 0)
+ cksumvalid = 1;
+ else {
+ if (bootverbose)
+ kprintf(
+ "pci%d:%d:%d:%d: bad VPD cksum, remain %hhu\n",
+ cfg->domain, cfg->bus,
+ cfg->slot, cfg->func,
+ vrs.cksum);
+ cksumvalid = 0;
+ state = -1;
+ break;
+ }
+ }
+ dflen--;
+ remain--;
+ /* keep in sync w/ state 2's transistions */
+ if (dflen == 0)
+ cfg->vpd.vpd_ros[off++].value[i++] = '\0';
+ if (dflen == 0 && remain == 0) {
+ cfg->vpd.vpd_rocnt = off;
+ cfg->vpd.vpd_ros = kreallocf(cfg->vpd.vpd_ros,
+ off * sizeof(*cfg->vpd.vpd_ros),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ state = 0;
+ } else if (dflen == 0)
+ state = 2;
+ break;
+
+ case 4:
+ remain--;
+ if (remain == 0)
+ state = 0;
+ break;
+
+ case 5: /* VPD-W Keyword Header */
+ if (off == alloc) {
+ cfg->vpd.vpd_w = kreallocf(cfg->vpd.vpd_w,
+ (alloc *= 2) * sizeof(*cfg->vpd.vpd_w),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ }
+ cfg->vpd.vpd_w[off].keyword[0] = byte;
+ if (vpd_nextbyte(&vrs, &byte2)) {
+ state = -2;
+ break;
+ }
+ cfg->vpd.vpd_w[off].keyword[1] = byte2;
+ if (vpd_nextbyte(&vrs, &byte2)) {
+ state = -2;
+ break;
+ }
+ cfg->vpd.vpd_w[off].len = dflen = byte2;
+ cfg->vpd.vpd_w[off].start = vrs.off - vrs.bytesinval;
+ cfg->vpd.vpd_w[off].value = kmalloc((dflen + 1) *
+ sizeof(*cfg->vpd.vpd_w[off].value),
+ M_DEVBUF, M_WAITOK);
+ remain -= 3;
+ i = 0;
+ /* keep in sync w/ state 6's transistions */
+ if (dflen == 0 && remain == 0)
+ state = 0;
+ else if (dflen == 0)
+ state = 5;
+ else
+ state = 6;
+ break;
+
+ case 6: /* VPD-W Keyword Value */
+ cfg->vpd.vpd_w[off].value[i++] = byte;
+ dflen--;
+ remain--;
+ /* keep in sync w/ state 5's transistions */
+ if (dflen == 0)
+ cfg->vpd.vpd_w[off++].value[i++] = '\0';
+ if (dflen == 0 && remain == 0) {
+ cfg->vpd.vpd_wcnt = off;
+ cfg->vpd.vpd_w = kreallocf(cfg->vpd.vpd_w,
+ off * sizeof(*cfg->vpd.vpd_w),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ state = 0;
+ } else if (dflen == 0)
+ state = 5;
+ break;
+
+ default:
+ kprintf("pci%d:%d:%d:%d: invalid state: %d\n",
+ cfg->domain, cfg->bus, cfg->slot, cfg->func,
+ state);
+ state = -1;
+ break;
+ }
+ }
+
+ if (cksumvalid == 0 || state < -1) {
+ /* read-only data bad, clean up */
+ if (cfg->vpd.vpd_ros != NULL) {
+ for (off = 0; cfg->vpd.vpd_ros[off].value; off++)
+ kfree(cfg->vpd.vpd_ros[off].value, M_DEVBUF);
+ kfree(cfg->vpd.vpd_ros, M_DEVBUF);
+ cfg->vpd.vpd_ros = NULL;
+ }
+ }
+ if (state < -1) {
+ /* I/O error, clean up */
+ kprintf("pci%d:%d:%d:%d: failed to read VPD data.\n",
+ cfg->domain, cfg->bus, cfg->slot, cfg->func);
+ if (cfg->vpd.vpd_ident != NULL) {
+ kfree(cfg->vpd.vpd_ident, M_DEVBUF);
+ cfg->vpd.vpd_ident = NULL;
+ }
+ if (cfg->vpd.vpd_w != NULL) {
+ for (off = 0; cfg->vpd.vpd_w[off].value; off++)
+ kfree(cfg->vpd.vpd_w[off].value, M_DEVBUF);
+ kfree(cfg->vpd.vpd_w, M_DEVBUF);
+ cfg->vpd.vpd_w = NULL;
+ }
+ }
+ cfg->vpd.vpd_cached = 1;
+#undef REG
+#undef WREG
+}
+
+int
+pci_get_vpd_ident_method(device_t dev, device_t child, const char **identptr)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ pcicfgregs *cfg = &dinfo->cfg;
+
+ if (!cfg->vpd.vpd_cached && cfg->vpd.vpd_reg != 0)
+ pci_read_vpd(device_get_parent(dev), cfg);
+
+ *identptr = cfg->vpd.vpd_ident;
+
+ if (*identptr == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+int
+pci_get_vpd_readonly_method(device_t dev, device_t child, const char *kw,
+ const char **vptr)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ pcicfgregs *cfg = &dinfo->cfg;
+ int i;
+
+ if (!cfg->vpd.vpd_cached && cfg->vpd.vpd_reg != 0)
+ pci_read_vpd(device_get_parent(dev), cfg);
+
+ for (i = 0; i < cfg->vpd.vpd_rocnt; i++)
+ if (memcmp(kw, cfg->vpd.vpd_ros[i].keyword,
+ sizeof(cfg->vpd.vpd_ros[i].keyword)) == 0) {
+ *vptr = cfg->vpd.vpd_ros[i].value;
+ }
+
+ if (i != cfg->vpd.vpd_rocnt)
+ return (0);
+
+ *vptr = NULL;
+ return (ENXIO);
+}
+
+/*
+ * Return the offset in configuration space of the requested extended
+ * capability entry or 0 if the specified capability was not found.
+ */
+int
+pci_find_extcap_method(device_t dev, device_t child, int capability,
+ int *capreg)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ pcicfgregs *cfg = &dinfo->cfg;
+ u_int32_t status;
+ u_int8_t ptr;
+
+ /*
+ * Check the CAP_LIST bit of the PCI status register first.
+ */
+ status = pci_read_config(child, PCIR_STATUS, 2);
+ if (!(status & PCIM_STATUS_CAPPRESENT))
+ return (ENXIO);
+
+ /*
+ * Determine the start pointer of the capabilities list.
+ */
+ switch (cfg->hdrtype & PCIM_HDRTYPE) {
+ case 0:
+ case 1:
+ ptr = PCIR_CAP_PTR;
+ break;
+ case 2:
+ ptr = PCIR_CAP_PTR_2;
+ break;
+ default:
+ /* XXX: panic? */
+ return (ENXIO); /* no extended capabilities support */
+ }
+ ptr = pci_read_config(child, ptr, 1);
+
+ /*
+ * Traverse the capabilities list.
+ */
+ while (ptr != 0) {
+ if (pci_read_config(child, ptr + PCICAP_ID, 1) == capability) {
+ if (capreg != NULL)
+ *capreg = ptr;
+ return (0);
+ }
+ ptr = pci_read_config(child, ptr + PCICAP_NEXTPTR, 1);
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Support for MSI-X message interrupts.
+ */
+void
+pci_enable_msix(device_t dev, u_int index, uint64_t address, uint32_t data)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ struct pcicfg_msix *msix = &dinfo->cfg.msix;
+ uint32_t offset;
+
+ KASSERT(msix->msix_table_len > index, ("bogus index"));
+ offset = msix->msix_table_offset + index * 16;
+ bus_write_4(msix->msix_table_res, offset, address & 0xffffffff);
+ bus_write_4(msix->msix_table_res, offset + 4, address >> 32);
+ bus_write_4(msix->msix_table_res, offset + 8, data);
+
+ /* Enable MSI -> HT mapping. */
+ pci_ht_map_msi(dev, address);
+}
+
+void
+pci_mask_msix(device_t dev, u_int index)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ struct pcicfg_msix *msix = &dinfo->cfg.msix;
+ uint32_t offset, val;
+
+ KASSERT(msix->msix_msgnum > index, ("bogus index"));
+ offset = msix->msix_table_offset + index * 16 + 12;
+ val = bus_read_4(msix->msix_table_res, offset);
+ if (!(val & PCIM_MSIX_VCTRL_MASK)) {
+ val |= PCIM_MSIX_VCTRL_MASK;
+ bus_write_4(msix->msix_table_res, offset, val);
+ }
+}
+
+void
+pci_unmask_msix(device_t dev, u_int index)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ struct pcicfg_msix *msix = &dinfo->cfg.msix;
+ uint32_t offset, val;
+
+ KASSERT(msix->msix_table_len > index, ("bogus index"));
+ offset = msix->msix_table_offset + index * 16 + 12;
+ val = bus_read_4(msix->msix_table_res, offset);
+ if (val & PCIM_MSIX_VCTRL_MASK) {
+ val &= ~PCIM_MSIX_VCTRL_MASK;
+ bus_write_4(msix->msix_table_res, offset, val);
+ }
+}
+
+int
+pci_pending_msix(device_t dev, u_int index)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ struct pcicfg_msix *msix = &dinfo->cfg.msix;
+ uint32_t offset, bit;
+
+ KASSERT(msix->msix_table_len > index, ("bogus index"));
+ offset = msix->msix_pba_offset + (index / 32) * 4;
+ bit = 1 << index % 32;
+ return (bus_read_4(msix->msix_pba_res, offset) & bit);
+}
+
+/*
+ * Restore MSI-X registers and table during resume. If MSI-X is
+ * enabled then walk the virtual table to restore the actual MSI-X
+ * table.
+ */
+static void
+pci_resume_msix(device_t dev)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ struct pcicfg_msix *msix = &dinfo->cfg.msix;
+ struct msix_table_entry *mte;
+ struct msix_vector *mv;
+ int i;
+
+ if (msix->msix_alloc > 0) {
+ /* First, mask all vectors. */
+ for (i = 0; i < msix->msix_msgnum; i++)
+ pci_mask_msix(dev, i);
+
+ /* Second, program any messages with at least one handler. */
+ for (i = 0; i < msix->msix_table_len; i++) {
+ mte = &msix->msix_table[i];
+ if (mte->mte_vector == 0 || mte->mte_handlers == 0)
+ continue;
+ mv = &msix->msix_vectors[mte->mte_vector - 1];
+ pci_enable_msix(dev, i, mv->mv_address, mv->mv_data);
+ pci_unmask_msix(dev, i);
+ }
+ }
+ pci_write_config(dev, msix->msix_location + PCIR_MSIX_CTRL,
+ msix->msix_ctrl, 2);
+}
+
+/*
+ * Attempt to allocate *count MSI-X messages. The actual number allocated is
+ * returned in *count. After this function returns, each message will be
+ * available to the driver as SYS_RES_IRQ resources starting at rid 1.
+ */
+int
+pci_alloc_msix_method(device_t dev, device_t child, int *count)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ pcicfgregs *cfg = &dinfo->cfg;
+ struct resource_list_entry *rle;
+ int actual, error, i, irq, max;
+
+ /* Don't let count == 0 get us into trouble. */
+ if (*count == 0)
+ return (EINVAL);
+
+ /* If rid 0 is allocated, then fail. */
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 0);
+ if (rle != NULL && rle->res != NULL)
+ return (ENXIO);
+
+ /* Already have allocated messages? */
+ if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0)
+ return (ENXIO);
+
+ /* If MSI is blacklisted for this system, fail. */
+ if (pci_msi_blacklisted())
+ return (ENXIO);
+
+ /* MSI-X capability present? */
+ if (cfg->msix.msix_location == 0 || !pci_do_msix)
+ return (ENODEV);
+
+ /* Make sure the appropriate BARs are mapped. */
+ rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
+ cfg->msix.msix_table_bar);
+ if (rle == NULL || rle->res == NULL ||
+ !(rman_get_flags(rle->res) & RF_ACTIVE))
+ return (ENXIO);
+ cfg->msix.msix_table_res = rle->res;
+ if (cfg->msix.msix_pba_bar != cfg->msix.msix_table_bar) {
+ rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
+ cfg->msix.msix_pba_bar);
+ if (rle == NULL || rle->res == NULL ||
+ !(rman_get_flags(rle->res) & RF_ACTIVE))
+ return (ENXIO);
+ }
+ cfg->msix.msix_pba_res = rle->res;
+
+ if (bootverbose)
+ device_printf(child,
+ "attempting to allocate %d MSI-X vectors (%d supported)\n",
+ *count, cfg->msix.msix_msgnum);
+ max = min(*count, cfg->msix.msix_msgnum);
+ for (i = 0; i < max; i++) {
+ /* Allocate a message. */
+ error = PCIB_ALLOC_MSIX(device_get_parent(dev), child, &irq);
+ if (error)
+ break;
+ resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1, irq,
+ irq, 1);
+ }
+ actual = i;
+
+ if (bootverbose) {
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 1);
+ if (actual == 1)
+ device_printf(child, "using IRQ %lu for MSI-X\n",
+ rle->start);
+ else {
+ int run;
+
+ /*
+ * Be fancy and try to print contiguous runs of
+ * IRQ values as ranges. 'irq' is the previous IRQ.
+ * 'run' is true if we are in a range.
+ */
+ device_printf(child, "using IRQs %lu", rle->start);
+ irq = rle->start;
+ run = 0;
+ for (i = 1; i < actual; i++) {
+ rle = resource_list_find(&dinfo->resources,
+ SYS_RES_IRQ, i + 1);
+
+ /* Still in a run? */
+ if (rle->start == irq + 1) {
+ run = 1;
+ irq++;
+ continue;
+ }
+
+ /* Finish previous range. */
+ if (run) {
+ kprintf("-%d", irq);
+ run = 0;
+ }
+
+ /* Start new range. */
+ kprintf(",%lu", rle->start);
+ irq = rle->start;
+ }
+
+ /* Unfinished range? */
+ if (run)
+ kprintf("-%d", irq);
+ kprintf(" for MSI-X\n");
+ }
+ }
+
+ /* Mask all vectors. */
+ for (i = 0; i < cfg->msix.msix_msgnum; i++)
+ pci_mask_msix(child, i);
+
+ /* Allocate and initialize vector data and virtual table. */
+ cfg->msix.msix_vectors = kmalloc(sizeof(struct msix_vector) * actual,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ cfg->msix.msix_table = kmalloc(sizeof(struct msix_table_entry) * actual,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ for (i = 0; i < actual; i++) {
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
+ cfg->msix.msix_vectors[i].mv_irq = rle->start;
+ cfg->msix.msix_table[i].mte_vector = i + 1;
+ }
+
+ /* Update control register to enable MSI-X. */
+ cfg->msix.msix_ctrl |= PCIM_MSIXCTRL_MSIX_ENABLE;
+ pci_write_config(child, cfg->msix.msix_location + PCIR_MSIX_CTRL,
+ cfg->msix.msix_ctrl, 2);
+
+ /* Update counts of alloc'd messages. */
+ cfg->msix.msix_alloc = actual;
+ cfg->msix.msix_table_len = actual;
+ *count = actual;
+ return (0);
+}
+
+/*
+ * By default, pci_alloc_msix() will assign the allocated IRQ
+ * resources consecutively to the first N messages in the MSI-X table.
+ * However, device drivers may want to use different layouts if they
+ * either receive fewer messages than they asked for, or they wish to
+ * populate the MSI-X table sparsely. This method allows the driver
+ * to specify what layout it wants. It must be called after a
+ * successful pci_alloc_msix() but before any of the associated
+ * SYS_RES_IRQ resources are allocated via bus_alloc_resource().
+ *
+ * The 'vectors' array contains 'count' message vectors. The array
+ * maps directly to the MSI-X table in that index 0 in the array
+ * specifies the vector for the first message in the MSI-X table, etc.
+ * The vector value in each array index can either be 0 to indicate
+ * that no vector should be assigned to a message slot, or it can be a
+ * number from 1 to N (where N is the count returned from a
+ * succcessful call to pci_alloc_msix()) to indicate which message
+ * vector (IRQ) to be used for the corresponding message.
+ *
+ * On successful return, each message with a non-zero vector will have
+ * an associated SYS_RES_IRQ whose rid is equal to the array index +
+ * 1. Additionally, if any of the IRQs allocated via the previous
+ * call to pci_alloc_msix() are not used in the mapping, those IRQs
+ * will be kfreed back to the system automatically.
+ *
+ * For example, suppose a driver has a MSI-X table with 6 messages and
+ * asks for 6 messages, but pci_alloc_msix() only returns a count of
+ * 3. Call the three vectors allocated by pci_alloc_msix() A, B, and
+ * C. After the call to pci_alloc_msix(), the device will be setup to
+ * have an MSI-X table of ABC--- (where - means no vector assigned).
+ * If the driver ten passes a vector array of { 1, 0, 1, 2, 0, 2 },
+ * then the MSI-X table will look like A-AB-B, and the 'C' vector will
+ * be kfreed back to the system. This device will also have valid
+ * SYS_RES_IRQ rids of 1, 3, 4, and 6.
+ *
+ * In any case, the SYS_RES_IRQ rid X will always map to the message
+ * at MSI-X table index X - 1 and will only be valid if a vector is
+ * assigned to that table entry.
+ */
+int
+pci_remap_msix_method(device_t dev, device_t child, int count,
+ const u_int *vectors)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ struct pcicfg_msix *msix = &dinfo->cfg.msix;
+ struct resource_list_entry *rle;
+ int i, irq, j, *used;
+
+ /*
+ * Have to have at least one message in the table but the
+ * table can't be bigger than the actual MSI-X table in the
+ * device.
+ */
+ if (count == 0 || count > msix->msix_msgnum)
+ return (EINVAL);
+
+ /* Sanity check the vectors. */
+ for (i = 0; i < count; i++)
+ if (vectors[i] > msix->msix_alloc)
+ return (EINVAL);
+
+ /*
+ * Make sure there aren't any holes in the vectors to be used.
+ * It's a big pain to support it, and it doesn't really make
+ * sense anyway. Also, at least one vector must be used.
+ */
+ used = kmalloc(sizeof(int) * msix->msix_alloc, M_DEVBUF, M_WAITOK |
+ M_ZERO);
+ for (i = 0; i < count; i++)
+ if (vectors[i] != 0)
+ used[vectors[i] - 1] = 1;
+ for (i = 0; i < msix->msix_alloc - 1; i++)
+ if (used[i] == 0 && used[i + 1] == 1) {
+ kfree(used, M_DEVBUF);
+ return (EINVAL);
+ }
+ if (used[0] != 1) {
+ kfree(used, M_DEVBUF);
+ return (EINVAL);
+ }
+
+ /* Make sure none of the resources are allocated. */
+ for (i = 0; i < msix->msix_table_len; i++) {
+ if (msix->msix_table[i].mte_vector == 0)
+ continue;
+ if (msix->msix_table[i].mte_handlers > 0)
+ return (EBUSY);
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
+ KASSERT(rle != NULL, ("missing resource"));
+ if (rle->res != NULL)
+ return (EBUSY);
+ }
+
+ /* Free the existing resource list entries. */
+ for (i = 0; i < msix->msix_table_len; i++) {
+ if (msix->msix_table[i].mte_vector == 0)
+ continue;
+ resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1);
+ }
+
+ /*
+ * Build the new virtual table keeping track of which vectors are
+ * used.
+ */
+ kfree(msix->msix_table, M_DEVBUF);
+ msix->msix_table = kmalloc(sizeof(struct msix_table_entry) * count,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ for (i = 0; i < count; i++)
+ msix->msix_table[i].mte_vector = vectors[i];
+ msix->msix_table_len = count;
+
+ /* Free any unused IRQs and resize the vectors array if necessary. */
+ j = msix->msix_alloc - 1;
+ if (used[j] == 0) {
+ struct msix_vector *vec;
+
+ while (used[j] == 0) {
+ PCIB_RELEASE_MSIX(device_get_parent(dev), child,
+ msix->msix_vectors[j].mv_irq);
+ j--;
+ }
+ vec = kmalloc(sizeof(struct msix_vector) * (j + 1), M_DEVBUF,
+ M_WAITOK);
+ bcopy(msix->msix_vectors, vec, sizeof(struct msix_vector) *
+ (j + 1));
+ kfree(msix->msix_vectors, M_DEVBUF);
+ msix->msix_vectors = vec;
+ msix->msix_alloc = j + 1;
+ }
+ kfree(used, M_DEVBUF);
+
+ /* Map the IRQs onto the rids. */
+ for (i = 0; i < count; i++) {
+ if (vectors[i] == 0)
+ continue;
+ irq = msix->msix_vectors[vectors[i]].mv_irq;
+ resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1, irq,
+ irq, 1);
+ }
+
+ if (bootverbose) {
+ device_printf(child, "Remapped MSI-X IRQs as: ");
+ for (i = 0; i < count; i++) {
+ if (i != 0)
+ kprintf(", ");
+ if (vectors[i] == 0)
+ kprintf("---");
+ else
+ kprintf("%d",
+ msix->msix_vectors[vectors[i]].mv_irq);
+ }
+ kprintf("\n");
+ }
+
+ return (0);
+}
+
+static int
+pci_release_msix(device_t dev, device_t child)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ struct pcicfg_msix *msix = &dinfo->cfg.msix;
+ struct resource_list_entry *rle;
+ int i;
+
+ /* Do we have any messages to release? */
+ if (msix->msix_alloc == 0)
+ return (ENODEV);
+
+ /* Make sure none of the resources are allocated. */
+ for (i = 0; i < msix->msix_table_len; i++) {
+ if (msix->msix_table[i].mte_vector == 0)
+ continue;
+ if (msix->msix_table[i].mte_handlers > 0)
+ return (EBUSY);
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
+ KASSERT(rle != NULL, ("missing resource"));
+ if (rle->res != NULL)
+ return (EBUSY);
+ }
+
+ /* Update control register to disable MSI-X. */
+ msix->msix_ctrl &= ~PCIM_MSIXCTRL_MSIX_ENABLE;
+ pci_write_config(child, msix->msix_location + PCIR_MSIX_CTRL,
+ msix->msix_ctrl, 2);
+
+ /* Free the resource list entries. */
+ for (i = 0; i < msix->msix_table_len; i++) {
+ if (msix->msix_table[i].mte_vector == 0)
+ continue;
+ resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1);
+ }
+ kfree(msix->msix_table, M_DEVBUF);
+ msix->msix_table_len = 0;
+
+ /* Release the IRQs. */
+ for (i = 0; i < msix->msix_alloc; i++)
+ PCIB_RELEASE_MSIX(device_get_parent(dev), child,
+ msix->msix_vectors[i].mv_irq);
+ kfree(msix->msix_vectors, M_DEVBUF);
+ msix->msix_alloc = 0;
+ return (0);
+}
+
+/*
+ * Return the max supported MSI-X messages this device supports.
+ * Basically, assuming the MD code can alloc messages, this function
+ * should return the maximum value that pci_alloc_msix() can return.
+ * Thus, it is subject to the tunables, etc.
+ */
+int
+pci_msix_count_method(device_t dev, device_t child)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ struct pcicfg_msix *msix = &dinfo->cfg.msix;
+
+ if (pci_do_msix && msix->msix_location != 0)
+ return (msix->msix_msgnum);
+ return (0);
+}
+
+/*
+ * HyperTransport MSI mapping control
+ */
+void
+pci_ht_map_msi(device_t dev, uint64_t addr)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ struct pcicfg_ht *ht = &dinfo->cfg.ht;
+
+ if (!ht->ht_msimap)
+ return;
+
+ if (addr && !(ht->ht_msictrl & PCIM_HTCMD_MSI_ENABLE) &&
+ ht->ht_msiaddr >> 20 == addr >> 20) {
+ /* Enable MSI -> HT mapping. */
+ ht->ht_msictrl |= PCIM_HTCMD_MSI_ENABLE;
+ pci_write_config(dev, ht->ht_msimap + PCIR_HT_COMMAND,
+ ht->ht_msictrl, 2);
+ }
+
+ if (!addr && ht->ht_msictrl & PCIM_HTCMD_MSI_ENABLE) {
+ /* Disable MSI -> HT mapping. */
+ ht->ht_msictrl &= ~PCIM_HTCMD_MSI_ENABLE;
+ pci_write_config(dev, ht->ht_msimap + PCIR_HT_COMMAND,
+ ht->ht_msictrl, 2);
+ }
+}
+
+/*
+ * Support for MSI message signalled interrupts.
+ */
+void
+pci_enable_msi(device_t dev, uint64_t address, uint16_t data)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ struct pcicfg_msi *msi = &dinfo->cfg.msi;
+
+ /* Write data and address values. */
+ pci_write_config(dev, msi->msi_location + PCIR_MSI_ADDR,
+ address & 0xffffffff, 4);
+ if (msi->msi_ctrl & PCIM_MSICTRL_64BIT) {
+ pci_write_config(dev, msi->msi_location + PCIR_MSI_ADDR_HIGH,
+ address >> 32, 4);
+ pci_write_config(dev, msi->msi_location + PCIR_MSI_DATA_64BIT,
+ data, 2);
+ } else
+ pci_write_config(dev, msi->msi_location + PCIR_MSI_DATA, data,
+ 2);
+
+ /* Enable MSI in the control register. */
+ msi->msi_ctrl |= PCIM_MSICTRL_MSI_ENABLE;
+ pci_write_config(dev, msi->msi_location + PCIR_MSI_CTRL, msi->msi_ctrl,
+ 2);
+
+ /* Enable MSI -> HT mapping. */
+ pci_ht_map_msi(dev, address);
+}
+
+void
+pci_disable_msi(device_t dev)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ struct pcicfg_msi *msi = &dinfo->cfg.msi;
+
+ /* Disable MSI -> HT mapping. */
+ pci_ht_map_msi(dev, 0);
+
+ /* Disable MSI in the control register. */
+ msi->msi_ctrl &= ~PCIM_MSICTRL_MSI_ENABLE;
+ pci_write_config(dev, msi->msi_location + PCIR_MSI_CTRL, msi->msi_ctrl,
+ 2);
+}
+
+/*
+ * Restore MSI registers during resume. If MSI is enabled then
+ * restore the data and address registers in addition to the control
+ * register.
+ */
+static void
+pci_resume_msi(device_t dev)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ struct pcicfg_msi *msi = &dinfo->cfg.msi;
+ uint64_t address;
+ uint16_t data;
+
+ if (msi->msi_ctrl & PCIM_MSICTRL_MSI_ENABLE) {
+ address = msi->msi_addr;
+ data = msi->msi_data;
+ pci_write_config(dev, msi->msi_location + PCIR_MSI_ADDR,
+ address & 0xffffffff, 4);
+ if (msi->msi_ctrl & PCIM_MSICTRL_64BIT) {
+ pci_write_config(dev, msi->msi_location +
+ PCIR_MSI_ADDR_HIGH, address >> 32, 4);
+ pci_write_config(dev, msi->msi_location +
+ PCIR_MSI_DATA_64BIT, data, 2);
+ } else
+ pci_write_config(dev, msi->msi_location + PCIR_MSI_DATA,
+ data, 2);
+ }
+ pci_write_config(dev, msi->msi_location + PCIR_MSI_CTRL, msi->msi_ctrl,
+ 2);
+}
+
+int
+pci_remap_msi_irq(device_t dev, u_int irq)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ pcicfgregs *cfg = &dinfo->cfg;
+ struct resource_list_entry *rle;
+ struct msix_table_entry *mte;
+ struct msix_vector *mv;
+ device_t bus;
+ uint64_t addr;
+ uint32_t data;
+ int error, i, j;
+
+ bus = device_get_parent(dev);
+
+ /*
+ * Handle MSI first. We try to find this IRQ among our list
+ * of MSI IRQs. If we find it, we request updated address and
+ * data registers and apply the results.
+ */
+ if (cfg->msi.msi_alloc > 0) {
+
+ /* If we don't have any active handlers, nothing to do. */
+ if (cfg->msi.msi_handlers == 0)
+ return (0);
+ for (i = 0; i < cfg->msi.msi_alloc; i++) {
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ,
+ i + 1);
+ if (rle->start == irq) {
+ error = PCIB_MAP_MSI(device_get_parent(bus),
+ dev, irq, &addr, &data);
+ if (error)
+ return (error);
+ pci_disable_msi(dev);
+ dinfo->cfg.msi.msi_addr = addr;
+ dinfo->cfg.msi.msi_data = data;
+ pci_enable_msi(dev, addr, data);
+ return (0);
+ }
+ }
+ return (ENOENT);
+ }
+
+ /*
+ * For MSI-X, we check to see if we have this IRQ. If we do,
+ * we request the updated mapping info. If that works, we go
+ * through all the slots that use this IRQ and update them.
+ */
+ if (cfg->msix.msix_alloc > 0) {
+ for (i = 0; i < cfg->msix.msix_alloc; i++) {
+ mv = &cfg->msix.msix_vectors[i];
+ if (mv->mv_irq == irq) {
+ error = PCIB_MAP_MSI(device_get_parent(bus),
+ dev, irq, &addr, &data);
+ if (error)
+ return (error);
+ mv->mv_address = addr;
+ mv->mv_data = data;
+ for (j = 0; j < cfg->msix.msix_table_len; j++) {
+ mte = &cfg->msix.msix_table[j];
+ if (mte->mte_vector != i + 1)
+ continue;
+ if (mte->mte_handlers == 0)
+ continue;
+ pci_mask_msix(dev, j);
+ pci_enable_msix(dev, j, addr, data);
+ pci_unmask_msix(dev, j);
+ }
+ }
+ }
+ return (ENOENT);
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Returns true if the specified device is blacklisted because MSI
+ * doesn't work.
+ */
+int
+pci_msi_device_blacklisted(device_t dev)
+{
+ struct pci_quirk *q;
+
+ if (!pci_honor_msi_blacklist)
+ return (0);
+
+ for (q = &pci_quirks[0]; q->devid; q++) {
+ if (q->devid == pci_get_devid(dev) &&
+ q->type == PCI_QUIRK_DISABLE_MSI)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Determine if MSI is blacklisted globally on this sytem. Currently,
+ * we just check for blacklisted chipsets as represented by the
+ * host-PCI bridge at device 0:0:0. In the future, it may become
+ * necessary to check other system attributes, such as the kenv values
+ * that give the motherboard manufacturer and model number.
+ */
+static int
+pci_msi_blacklisted(void)
+{
+ device_t dev;
+
+ if (!pci_honor_msi_blacklist)
+ return (0);
+
+ /* Blacklist all non-PCI-express and non-PCI-X chipsets. */
+ if (!(pcie_chipset || pcix_chipset))
+ return (1);
+
+ dev = pci_find_bsf(0, 0, 0);
+ if (dev != NULL)
+ return (pci_msi_device_blacklisted(dev));
+ return (0);
+}
+
+/*
+ * Attempt to allocate *count MSI messages. The actual number allocated is
+ * returned in *count. After this function returns, each message will be
+ * available to the driver as SYS_RES_IRQ resources starting at a rid 1.
+ */
+int
+pci_alloc_msi_method(device_t dev, device_t child, int *count)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ pcicfgregs *cfg = &dinfo->cfg;
+ struct resource_list_entry *rle;
+ int actual, error, i, irqs[32];
+ uint16_t ctrl;
+
+ /* Don't let count == 0 get us into trouble. */
+ if (*count == 0)
+ return (EINVAL);
+
+ /* If rid 0 is allocated, then fail. */
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 0);
+ if (rle != NULL && rle->res != NULL)
+ return (ENXIO);
+
+ /* Already have allocated messages? */
+ if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0)
+ return (ENXIO);
+
+ /* If MSI is blacklisted for this system, fail. */
+ if (pci_msi_blacklisted())
+ return (ENXIO);
+
+ /* MSI capability present? */
+ if (cfg->msi.msi_location == 0 || !pci_do_msi)
+ return (ENODEV);
+
+ if (bootverbose)
+ device_printf(child,
+ "attempting to allocate %d MSI vectors (%d supported)\n",
+ *count, cfg->msi.msi_msgnum);
+
+ /* Don't ask for more than the device supports. */
+ actual = min(*count, cfg->msi.msi_msgnum);
+
+ /* Don't ask for more than 32 messages. */
+ actual = min(actual, 32);
+
+ /* MSI requires power of 2 number of messages. */
+ if (!powerof2(actual))
+ return (EINVAL);
+
+ for (;;) {
+ /* Try to allocate N messages. */
+ error = PCIB_ALLOC_MSI(device_get_parent(dev), child, actual,
+ cfg->msi.msi_msgnum, irqs);
+ if (error == 0)
+ break;
+ if (actual == 1)
+ return (error);
+
+ /* Try N / 2. */
+ actual >>= 1;
+ }
+
+ /*
+ * We now have N actual messages mapped onto SYS_RES_IRQ
+ * resources in the irqs[] array, so add new resources
+ * starting at rid 1.
+ */
+ for (i = 0; i < actual; i++)
+ resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1,
+ irqs[i], irqs[i], 1);
+
+ if (bootverbose) {
+ if (actual == 1)
+ device_printf(child, "using IRQ %d for MSI\n", irqs[0]);
+ else {
+ int run;
+
+ /*
+ * Be fancy and try to print contiguous runs
+ * of IRQ values as ranges. 'run' is true if
+ * we are in a range.
+ */
+ device_printf(child, "using IRQs %d", irqs[0]);
+ run = 0;
+ for (i = 1; i < actual; i++) {
+
+ /* Still in a run? */
+ if (irqs[i] == irqs[i - 1] + 1) {
+ run = 1;
+ continue;
+ }
+
+ /* Finish previous range. */
+ if (run) {
+ kprintf("-%d", irqs[i - 1]);
+ run = 0;
+ }
+
+ /* Start new range. */
+ kprintf(",%d", irqs[i]);
+ }
+
+ /* Unfinished range? */
+ if (run)
+ kprintf("-%d", irqs[actual - 1]);
+ kprintf(" for MSI\n");
+ }
+ }
+
+ /* Update control register with actual count. */
+ ctrl = cfg->msi.msi_ctrl;
+ ctrl &= ~PCIM_MSICTRL_MME_MASK;
+ ctrl |= (ffs(actual) - 1) << 4;
+ cfg->msi.msi_ctrl = ctrl;
+ pci_write_config(child, cfg->msi.msi_location + PCIR_MSI_CTRL, ctrl, 2);
+
+ /* Update counts of alloc'd messages. */
+ cfg->msi.msi_alloc = actual;
+ cfg->msi.msi_handlers = 0;
+ *count = actual;
+ return (0);
+}
+
+/* Release the MSI messages associated with this device. */
+int
+pci_release_msi_method(device_t dev, device_t child)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ struct pcicfg_msi *msi = &dinfo->cfg.msi;
+ struct resource_list_entry *rle;
+ int error, i, irqs[32];
+
+ /* Try MSI-X first. */
+ error = pci_release_msix(dev, child);
+ if (error != ENODEV)
+ return (error);
+
+ /* Do we have any messages to release? */
+ if (msi->msi_alloc == 0)
+ return (ENODEV);
+ KASSERT(msi->msi_alloc <= 32, ("more than 32 alloc'd messages"));
+
+ /* Make sure none of the resources are allocated. */
+ if (msi->msi_handlers > 0)
+ return (EBUSY);
+ for (i = 0; i < msi->msi_alloc; i++) {
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
+ KASSERT(rle != NULL, ("missing MSI resource"));
+ if (rle->res != NULL)
+ return (EBUSY);
+ irqs[i] = rle->start;
+ }
+
+ /* Update control register with 0 count. */
+ KASSERT(!(msi->msi_ctrl & PCIM_MSICTRL_MSI_ENABLE),
+ ("%s: MSI still enabled", __func__));
+ msi->msi_ctrl &= ~PCIM_MSICTRL_MME_MASK;
+ pci_write_config(child, msi->msi_location + PCIR_MSI_CTRL,
+ msi->msi_ctrl, 2);
+
+ /* Release the messages. */
+ PCIB_RELEASE_MSI(device_get_parent(dev), child, msi->msi_alloc, irqs);
+ for (i = 0; i < msi->msi_alloc; i++)
+ resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1);
+
+ /* Update alloc count. */
+ msi->msi_alloc = 0;
+ msi->msi_addr = 0;
+ msi->msi_data = 0;
+ return (0);
+}
+
+/*
+ * Return the max supported MSI messages this device supports.
+ * Basically, assuming the MD code can alloc messages, this function
+ * should return the maximum value that pci_alloc_msi() can return.
+ * Thus, it is subject to the tunables, etc.
+ */
+int
+pci_msi_count_method(device_t dev, device_t child)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ struct pcicfg_msi *msi = &dinfo->cfg.msi;
+
+ if (pci_do_msi && msi->msi_location != 0)
+ return (msi->msi_msgnum);
+ return (0);
+}
+
+/* kfree pcicfgregs structure and all depending data structures */
+
+int
+pci_freecfg(struct pci_devinfo *dinfo)
+{
+ struct devlist *devlist_head;
+ int i;
+
+ devlist_head = &pci_devq;
+
+ if (dinfo->cfg.vpd.vpd_reg) {
+ kfree(dinfo->cfg.vpd.vpd_ident, M_DEVBUF);
+ for (i = 0; i < dinfo->cfg.vpd.vpd_rocnt; i++)
+ kfree(dinfo->cfg.vpd.vpd_ros[i].value, M_DEVBUF);
+ kfree(dinfo->cfg.vpd.vpd_ros, M_DEVBUF);
+ for (i = 0; i < dinfo->cfg.vpd.vpd_wcnt; i++)
+ kfree(dinfo->cfg.vpd.vpd_w[i].value, M_DEVBUF);
+ kfree(dinfo->cfg.vpd.vpd_w, M_DEVBUF);
+ }
+ STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links);
+ kfree(dinfo, M_DEVBUF);
+
+ /* increment the generation count */
+ pci_generation++;
+
+ /* we're losing one device */
+ pci_numdevs--;
+ return (0);
+}
+
+/*
+ * PCI power manangement
+ */
+int
+pci_set_powerstate_method(device_t dev, device_t child, int state)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ pcicfgregs *cfg = &dinfo->cfg;
uint16_t status;
int result, oldstate, highest, delay;
- if (cfg->pmgt.pp_cap == 0)
+ if (cfg->pp.pp_cap == 0)
return (EOPNOTSUPP);
/*
delay = 200;
else
delay = 0;
- status = PCI_READ_CONFIG(dev, child, cfg->pmgt.pp_status, 2)
+ status = PCI_READ_CONFIG(dev, child, cfg->pp.pp_status, 2)
& ~PCIM_PSTAT_DMASK;
result = 0;
switch (state) {
status |= PCIM_PSTAT_D0;
break;
case PCI_POWERSTATE_D1:
- if ((cfg->pmgt.pp_cap & PCIM_PCAP_D1SUPP) == 0)
+ if ((cfg->pp.pp_cap & PCIM_PCAP_D1SUPP) == 0)
return (EOPNOTSUPP);
status |= PCIM_PSTAT_D1;
break;
case PCI_POWERSTATE_D2:
- if ((cfg->pmgt.pp_cap & PCIM_PCAP_D2SUPP) == 0)
+ if ((cfg->pp.pp_cap & PCIM_PCAP_D2SUPP) == 0)
return (EOPNOTSUPP);
status |= PCIM_PSTAT_D2;
break;
if (bootverbose)
kprintf(
- "pci%d:%d:%d: Transition from D%d to D%d\n",
- dinfo->cfg.bus, dinfo->cfg.slot, dinfo->cfg.func,
- oldstate, state);
+ "pci%d:%d:%d:%d: Transition from D%d to D%d\n",
+ dinfo->cfg.domain, dinfo->cfg.bus, dinfo->cfg.slot,
+ dinfo->cfg.func, oldstate, state);
- PCI_WRITE_CONFIG(dev, child, cfg->pmgt.pp_status, status, 2);
+ PCI_WRITE_CONFIG(dev, child, cfg->pp.pp_status, status, 2);
if (delay)
DELAY(delay);
return (0);
uint16_t status;
int result;
- if (cfg->pmgt.pp_cap != 0) {
- status = PCI_READ_CONFIG(dev, child, cfg->pmgt.pp_status, 2);
+ if (cfg->pp.pp_cap != 0) {
+ status = PCI_READ_CONFIG(dev, child, cfg->pp.pp_status, 2);
switch (status & PCIM_PSTAT_DMASK) {
case PCIM_PSTAT_D0:
result = PCI_POWERSTATE_D0;
*/
static __inline void
-pci_set_command_bit(device_t dev, device_t child, u_int16_t bit)
+pci_set_command_bit(device_t dev, device_t child, uint16_t bit)
{
- u_int16_t command;
+ uint16_t command;