summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-04-19 13:55:33 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-04-19 13:55:33 +0000
commit7f974a7687129917e3c3ceb74287bdf9ba0fd5f3 (patch)
treee5a2ab040ca748a1d3e8f863af05b9f7ef674a76 /arch
parent46e045034336a2cc90c1798cd7cc07af744ddfd6 (diff)
Merge with Linux 2.3.99-pre5.
Diffstat (limited to 'arch')
-rw-r--r--arch/i386/kernel/Makefile2
-rw-r--r--arch/i386/kernel/pci-i386.h40
-rw-r--r--arch/i386/kernel/pci-irq.c431
-rw-r--r--arch/i386/kernel/pci-pc.c535
4 files changed, 546 insertions, 462 deletions
diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
index 64b3d0951..56db72ef8 100644
--- a/arch/i386/kernel/Makefile
+++ b/arch/i386/kernel/Makefile
@@ -24,7 +24,7 @@ O_OBJS += pci-i386.o
ifdef CONFIG_VISWS
O_OBJS += pci-visws.o
else
-O_OBJS += pci-pc.o
+O_OBJS += pci-pc.o pci-irq.o
endif
endif
diff --git a/arch/i386/kernel/pci-i386.h b/arch/i386/kernel/pci-i386.h
index 1be988b6c..5af43f661 100644
--- a/arch/i386/kernel/pci-i386.h
+++ b/arch/i386/kernel/pci-i386.h
@@ -18,7 +18,6 @@
#define PCI_NO_SORT 0x100
#define PCI_BIOS_SORT 0x200
#define PCI_NO_CHECKS 0x400
-#define PCI_PEER_FIXUP 0x800
#define PCI_ASSIGN_ROMS 0x1000
#define PCI_BIOS_IRQ_SCAN 0x2000
@@ -28,3 +27,42 @@ extern unsigned int pci_probe;
void pcibios_resource_survey(void);
int pcibios_enable_resources(struct pci_dev *);
+
+/* pci-pc.c */
+
+extern int pcibios_last_bus;
+extern struct pci_bus *pci_root_bus;
+extern struct pci_ops *pci_root_ops;
+
+struct irq_routing_table *pcibios_get_irq_routing_table(void);
+int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq);
+
+/* pci-irq.c */
+
+struct irq_info {
+ u8 bus, devfn; /* Bus, device and function */
+ struct {
+ u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
+ u16 bitmap; /* Available IRQs */
+ } __attribute__((packed)) irq[4];
+ u8 slot; /* Slot number, 0=onboard */
+ u8 rfu;
+} __attribute__((packed));
+
+struct irq_routing_table {
+ u32 signature; /* PIRQ_SIGNATURE should be here */
+ u16 version; /* PIRQ_VERSION */
+ u16 size; /* Table size in bytes */
+ u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
+ u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
+ u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
+ u32 miniport_data; /* Crap */
+ u8 rfu[11];
+ u8 checksum; /* Modulo 256 checksum must give zero */
+ struct irq_info slots[0];
+} __attribute__((packed));
+
+extern unsigned int pcibios_irq_mask;
+
+void pcibios_fixup_irqs(void);
+int pcibios_lookup_irq(struct pci_dev *dev, int assign);
diff --git a/arch/i386/kernel/pci-irq.c b/arch/i386/kernel/pci-irq.c
new file mode 100644
index 000000000..8dd3f5c82
--- /dev/null
+++ b/arch/i386/kernel/pci-irq.c
@@ -0,0 +1,431 @@
+/*
+ * Low-Level PCI Support for PC -- Routing of Interrupts
+ *
+ * (c) 1999--2000 Martin Mares <mj@suse.cz>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+
+#include "pci-i386.h"
+
+extern int skip_ioapic_setup;
+
+#define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
+#define PIRQ_VERSION 0x0100
+
+static struct irq_routing_table *pirq_table;
+
+/*
+ * Never use: 0, 1, 2 (timer, keyboard, and cascade)
+ * Avoid using: 13, 14 and 15 (FP error and IDE).
+ * Penalize: 3, 4, 7, 12 (known ISA uses: serial, parallel and mouse)
+ */
+unsigned int pcibios_irq_mask = ~0;
+
+static unsigned pirq_penalty[16] = {
+ 10000, 10000, 10000, 100, 100, 0, 0, 100,
+ 0, 0, 0, 0, 100, 1000, 1000, 1000
+};
+
+struct irq_router {
+ char *name;
+ u16 vendor, device;
+ int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq);
+ int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new);
+};
+
+/*
+ * Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table.
+ */
+
+static struct irq_routing_table * __init pirq_find_routing_table(void)
+{
+ u8 *addr;
+ struct irq_routing_table *rt;
+ int i;
+ u8 sum;
+
+ for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) {
+ rt = (struct irq_routing_table *) addr;
+ if (rt->signature != PIRQ_SIGNATURE ||
+ rt->version != PIRQ_VERSION ||
+ rt->size % 16 ||
+ rt->size < sizeof(struct irq_routing_table))
+ continue;
+ sum = 0;
+ for(i=0; i<rt->size; i++)
+ sum += addr[i];
+ if (!sum) {
+ DBG("PCI: Interrupt Routing Table found at 0x%p\n", rt);
+ return rt;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * If we have a IRQ routing table, use it to search for peer host
+ * bridges. It's a gross hack, but since there are no other known
+ * ways how to get a list of buses, we have to go this way.
+ */
+
+static void __init pirq_peer_trick(void)
+{
+ struct irq_routing_table *rt = pirq_table;
+ u8 busmap[256];
+ int i;
+ struct irq_info *e;
+
+ memset(busmap, 0, sizeof(busmap));
+ for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) {
+ e = &rt->slots[i];
+#ifdef DEBUG
+ {
+ int j;
+ DBG("%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot);
+ for(j=0; j<4; j++)
+ DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap);
+ DBG("\n");
+ }
+#endif
+ busmap[e->bus] = 1;
+ }
+ for(i=1; i<256; i++)
+ /*
+ * It might be a secondary bus, but in this case its parent is already
+ * known (ascending bus order) and therefore pci_scan_bus returns immediately.
+ */
+ if (busmap[i] && pci_scan_bus(i, pci_root_bus->ops, NULL))
+ printk("PCI: Discovered primary peer bus %02x [IRQ]\n", i);
+ pcibios_last_bus = -1;
+}
+
+/*
+ * Code for querying and setting of IRQ routes on various interrupt routers.
+ */
+
+static void eisa_set_level_irq(unsigned int irq)
+{
+ unsigned char mask = 1 << (irq & 7);
+ unsigned int port = 0x4d0 + (irq >> 3);
+ unsigned char val = inb(port);
+
+ if (!(val & mask)) {
+ DBG(" -> edge");
+ outb(val | mask, port);
+ }
+}
+
+static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+{
+ static unsigned char irqmap[16] = {
+ 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15
+ };
+ unsigned int val = irqmap[irq];
+ pirq--;
+ if (val && pirq < 8) {
+ u8 x;
+ unsigned reg = 0x48 + (pirq >> 1);
+ pci_read_config_byte(router, reg, &x);
+ x = (pirq & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val);
+ pci_write_config_byte(router, reg, x);
+ eisa_set_level_irq(irq);
+ return 1;
+ }
+ return 0;
+}
+
+static int pirq_piix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+{
+ u8 x;
+ pci_read_config_byte(router, pirq, &x);
+ return (x < 16) ? x : 0;
+}
+
+static int pirq_piix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+{
+ pci_write_config_byte(router, pirq, irq);
+ return 1;
+}
+
+static int pirq_via_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+{
+ u8 x;
+ int reg = 0x55 + (pirq >> 1);
+ pci_read_config_byte(router, reg, &x);
+ return (pirq & 1) ? (x >> 4) : (x & 0x0f);
+}
+
+static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+{
+ u8 x;
+ int reg = 0x55 + (pirq >> 1);
+ pci_read_config_byte(router, reg, &x);
+ x = (pirq & 1) ? ((x & 0x0f) | (irq << 4)) : ((x & 0xf0) | irq);
+ pci_write_config_byte(router, reg, x);
+ return 1;
+}
+
+static int pirq_opti_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+{
+ u8 x;
+ int reg = 0xb8 + (pirq >> 5);
+ pci_read_config_byte(router, reg, &x);
+ return (pirq & 0x10) ? (x >> 4) : (x & 0x0f);
+}
+
+static int pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+{
+ u8 x;
+ int reg = 0xb8 + (pirq >> 5);
+ pci_read_config_byte(router, reg, &x);
+ x = (pirq & 0x10) ? ((x & 0x0f) | (irq << 4)) : ((x & 0xf0) | irq);
+ pci_write_config_byte(router, reg, x);
+ return 1;
+}
+
+#ifdef CONFIG_PCI_BIOS
+
+static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+{
+ struct pci_dev *bridge;
+ int pin = pci_get_interrupt_pin(dev, &bridge);
+ return pcibios_set_irq_routing(bridge, pin, irq);
+}
+
+static struct irq_router pirq_bios_router =
+ { "BIOS", 0, 0, NULL, pirq_bios_set };
+
+#endif
+
+static struct irq_router pirq_routers[] = {
+ { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, pirq_piix_get, pirq_piix_set },
+ { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, pirq_piix_get, pirq_piix_set },
+ { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, pirq_piix_get, pirq_piix_set },
+ { "ALI", PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL, pirq_ali_set },
+ { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, pirq_via_get, pirq_via_set },
+ { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, pirq_via_get, pirq_via_set },
+ { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, pirq_via_get, pirq_via_set },
+ { "OPTI", PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C700, pirq_opti_get, pirq_opti_set },
+ { "default", 0, 0, NULL, NULL }
+};
+
+static struct irq_router *pirq_router;
+static struct pci_dev *pirq_router_dev;
+
+static void __init pirq_find_router(void)
+{
+ struct irq_routing_table *rt = pirq_table;
+ u16 rvendor, rdevice;
+ struct irq_router *r;
+
+#ifdef CONFIG_PCI_BIOS
+ if (!rt->signature) {
+ printk("PCI: Using BIOS for IRQ routing\n");
+ pirq_router = &pirq_bios_router;
+ return;
+ }
+#endif
+ if (!(pirq_router_dev = pci_find_slot(rt->rtr_bus, rt->rtr_devfn))) {
+ DBG("PCI: Interrupt router not found\n");
+ return;
+ }
+ if (rt->rtr_vendor) {
+ rvendor = rt->rtr_vendor;
+ rdevice = rt->rtr_device;
+ } else {
+ /*
+ * Several BIOSes forget to set the router type. In such cases, we
+ * use chip vendor/device. This doesn't guarantee us semantics of
+ * PIRQ values, but was found to work in practice and it's still
+ * better than not trying.
+ */
+ DBG("PCI: Guessed interrupt router ID from %s\n", pirq_router_dev->slot_name);
+ rvendor = pirq_router_dev->vendor;
+ rdevice = pirq_router_dev->device;
+ }
+ for(r=pirq_routers; r->vendor; r++)
+ if (r->vendor == rvendor && r->device == rdevice)
+ break;
+ pirq_router = r;
+ printk("PCI: Using IRQ router %s [%04x/%04x] at %s\n", r->name,
+ rvendor, rdevice, pirq_router_dev->slot_name);
+}
+
+static struct irq_info *pirq_get_info(struct pci_dev *dev, int pin)
+{
+ struct irq_routing_table *rt = pirq_table;
+ int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
+ struct irq_info *info;
+
+ for (info = rt->slots; entries--; info++)
+ if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn))
+ return info;
+ return NULL;
+}
+
+int pcibios_lookup_irq(struct pci_dev *dev, int assign)
+{
+ struct irq_info *info;
+ int i, pirq, pin, newirq;
+ int irq = 0;
+ u32 mask;
+ struct irq_router *r = pirq_router;
+ struct pci_dev *dev2, *d;
+ char *msg = NULL;
+
+ if (!pirq_table)
+ return 0;
+
+ /* Find IRQ routing entry */
+ pin = pci_get_interrupt_pin(dev, &d);
+ if (pin < 0) {
+ DBG(" -> no interrupt pin\n");
+ return 0;
+ }
+ DBG("IRQ for %s(%d) via %s", dev->slot_name, pin, d->slot_name);
+ info = pirq_get_info(d, pin);
+ if (!info) {
+ DBG(" -> not found in routing table\n");
+ return 0;
+ }
+ pirq = info->irq[pin].link;
+ mask = info->irq[pin].bitmap;
+ if (!pirq) {
+ DBG(" -> not routed\n");
+ return 0;
+ }
+ DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs);
+ if (pcibios_irq_mask != ~0)
+ mask &= pcibios_irq_mask;
+ else
+ mask &= pirq_table->exclusive_irqs;
+
+ /* Find the best IRQ to assign */
+ newirq = 0;
+ for (i = 0; i < 16; i++) {
+ if (!(mask & (1 << i)))
+ continue;
+ if (pirq_penalty[i] < pirq_penalty[newirq])
+ newirq = i;
+ }
+ DBG(" -> newirq=%d", newirq);
+
+ /* Try to get current IRQ */
+ if (r->get && (irq = r->get(pirq_router_dev, d, pirq))) {
+ DBG(" -> got IRQ %d\n", irq);
+ msg = "Found";
+ } else if (assign && newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
+ DBG(" -> assigning IRQ %d", newirq);
+ if (r->set(pirq_router_dev, d, pirq, newirq)) {
+ DBG(" ... OK\n");
+ msg = "Assigned";
+ irq = newirq;
+ }
+ }
+
+ if (!irq) {
+ DBG(" ... failed\n");
+ if (newirq && mask == (1 << newirq)) {
+ msg = "Guessed";
+ irq = newirq;
+ } else
+ return 0;
+ }
+ printk("PCI: %s IRQ %d for device %s\n", msg, irq, dev->slot_name);
+
+ /* Update IRQ for all devices with the same pirq value */
+ pci_for_each_dev(dev2) {
+ if ((pin = pci_get_interrupt_pin(dev2, &d)) >= 0 &&
+ (info = pirq_get_info(d, pin)) &&
+ info->irq[pin].link == pirq) {
+ dev2->irq = irq;
+ pirq_penalty[irq]++;
+ if (dev != dev2)
+ printk("PCI: The same IRQ used for device %s\n", dev2->slot_name);
+ }
+ }
+ return 1;
+}
+
+void __init pcibios_fixup_irqs(void)
+{
+ struct pci_dev *dev;
+ u8 pin;
+
+ DBG("PCI: IRQ fixup\n");
+ pirq_table = pirq_find_routing_table();
+#ifdef CONFIG_PCI_BIOS
+ if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN))
+ pirq_table = pcibios_get_irq_routing_table();
+#endif
+ if (pirq_table) {
+ pirq_peer_trick();
+ pirq_find_router();
+ }
+
+ pci_for_each_dev(dev) {
+ /*
+ * If the BIOS has set an out of range IRQ number, just ignore it.
+ * Also keep track of which IRQ's are already in use.
+ */
+ if (dev->irq >= 16) {
+ DBG("%s: ignoring bogus IRQ %d\n", dev->slot_name, dev->irq);
+ dev->irq = 0;
+ }
+ pirq_penalty[dev->irq]++;
+ }
+
+ pci_for_each_dev(dev) {
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+#if defined(CONFIG_X86_IO_APIC)
+ /*
+ * Recalculate IRQ numbers if we use the I/O APIC.
+ */
+ if (!skip_ioapic_setup)
+ {
+ int irq;
+
+ if (pin) {
+ pin--; /* interrupt pins are numbered starting from 1 */
+ irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
+/*
+ * Will be removed completely if things work out well with fuzzy parsing
+ */
+#if 0
+ if (irq < 0 && dev->bus->parent) { /* go back to the bridge */
+ struct pci_dev * bridge = dev->bus->self;
+
+ pin = (pin + PCI_SLOT(dev->devfn)) % 4;
+ irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number,
+ PCI_SLOT(bridge->devfn), pin);
+ if (irq >= 0)
+ printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n",
+ bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq);
+ }
+#endif
+ if (irq >= 0) {
+ printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",
+ dev->bus->number, PCI_SLOT(dev->devfn), pin, irq);
+ dev->irq = irq;
+ }
+ }
+ pirq_table = NULL; /* Avoid automatic IRQ assignment */
+ }
+#endif
+ /*
+ * Still no IRQ? Try to lookup one...
+ */
+ if (pin && !dev->irq)
+ pcibios_lookup_irq(dev, 0);
+ }
+}
diff --git a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c
index fb5d6b75f..6dbac9abd 100644
--- a/arch/i386/kernel/pci-pc.c
+++ b/arch/i386/kernel/pci-pc.c
@@ -10,9 +10,6 @@
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/init.h>
-#include <linux/malloc.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
#include <asm/segment.h>
#include <asm/io.h>
@@ -22,35 +19,9 @@
unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2;
-static struct pci_bus *pci_root_bus;
-static struct pci_ops *pci_root_ops;
-
-/*
- * IRQ routing table provided by the BIOS
- */
-
-struct irq_info {
- u8 bus, devfn; /* Bus, device and function */
- struct {
- u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
- u16 bitmap; /* Available IRQs */
- } __attribute__((packed)) irq[4];
- u8 slot; /* Slot number, 0=onboard */
- u8 rfu;
-} __attribute__((packed));
-
-struct irq_routing_table {
- u32 signature; /* PIRQ_SIGNATURE should be here */
- u16 version; /* PIRQ_VERSION */
- u16 size; /* Table size in bytes */
- u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
- u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
- u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
- u32 miniport_data; /* Crap */
- u8 rfu[11];
- u8 checksum; /* Modulo 256 checksum must give zero */
- struct irq_info slots[0];
-} __attribute__((packed));
+int pcibios_last_bus = -1;
+struct pci_bus *pci_root_bus;
+struct pci_ops *pci_root_ops;
/*
* Direct access to PCI hardware...
@@ -378,7 +349,7 @@ static int pci_bios_present;
static int __init check_pcibios(void)
{
u32 signature, eax, ebx, ecx;
- u8 status, major_ver, minor_ver, hw_mech, last_bus;
+ u8 status, major_ver, minor_ver, hw_mech;
unsigned long flags, pcibios_entry;
if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
@@ -403,16 +374,17 @@ static int __init check_pcibios(void)
hw_mech = eax & 0xff;
major_ver = (ebx >> 8) & 0xff;
minor_ver = ebx & 0xff;
- last_bus = ecx & 0xff;
+ if (pcibios_last_bus < 0)
+ pcibios_last_bus = ecx & 0xff;
DBG("PCI: BIOS probe returned s=%02x hw=%02x ver=%02x.%02x l=%02x\n",
- status, hw_mech, major_ver, minor_ver, last_bus);
+ status, hw_mech, major_ver, minor_ver, pcibios_last_bus);
if (status || signature != PCI_SIGNATURE) {
printk (KERN_ERR "PCI: BIOS BUG #%x[%08x] found, report to <mj@suse.cz>\n",
status, signature);
return 0;
}
- printk("PCI: PCI BIOS revision %x.%02x entry at 0x%lx\n",
- major_ver, minor_ver, pcibios_entry);
+ printk("PCI: PCI BIOS revision %x.%02x entry at 0x%lx, last bus=%d\n",
+ major_ver, minor_ver, pcibios_entry, pcibios_last_bus);
#ifdef CONFIG_PCI_DIRECT
if (!(hw_mech & PCIBIOS_HW_TYPE1))
pci_probe &= ~PCI_PROBE_CONF1;
@@ -670,7 +642,7 @@ static void __init pcibios_sort(void)
}
/*
- * Ask BIOS for IRQ Routing Table
+ * BIOS Functions for IRQ Routing
*/
struct irq_routing_options {
@@ -679,28 +651,20 @@ struct irq_routing_options {
u16 segment;
} __attribute__((packed));
-static unsigned long pcibios_irq_page __initdata = 0;
-
-static inline void __init pcibios_free_irq_routing_table(void)
-{
- if (pcibios_irq_page)
- free_page(pcibios_irq_page);
-}
-
-static struct irq_routing_table * __init pcibios_get_irq_routing_table(void)
+struct irq_routing_table * __init pcibios_get_irq_routing_table(void)
{
struct irq_routing_options opt;
- struct irq_routing_table *rt;
+ struct irq_routing_table *rt = NULL;
int ret, map;
+ unsigned long page;
- if (!(pci_probe & PCI_BIOS_IRQ_SCAN))
+ if (!pci_bios_present)
+ return NULL;
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
return NULL;
- pcibios_irq_page = __get_free_page(GFP_KERNEL);
- if (!pcibios_irq_page)
- return 0;
- rt = (void *) pcibios_irq_page;
- opt.table = rt->slots;
- opt.size = PAGE_SIZE - sizeof(struct irq_routing_table);
+ opt.table = (struct irq_info *) page;
+ opt.size = PAGE_SIZE;
opt.segment = __KERNEL_DS;
DBG("PCI: Fetching IRQ routing table... ");
@@ -719,17 +683,39 @@ static struct irq_routing_table * __init pcibios_get_irq_routing_table(void)
"D" ((long) &opt),
"S" (&pci_indirect));
DBG("OK ret=%d, size=%d, map=%x\n", ret, opt.size, map);
- if (ret & 0xff00) {
+ if (ret & 0xff00)
printk(KERN_ERR "PCI: Error %02x when fetching IRQ routing table.\n", (ret >> 8) & 0xff);
- return 0;
+ else if (opt.size) {
+ rt = kmalloc(sizeof(struct irq_routing_table) + opt.size, GFP_KERNEL);
+ if (rt) {
+ memset(rt, 0, sizeof(struct irq_routing_table));
+ rt->size = opt.size + sizeof(struct irq_routing_table);
+ rt->exclusive_irqs = map;
+ memcpy(rt->slots, (void *) page, opt.size);
+ printk("PCI: Using BIOS Interrupt Routing Table\n");
+ }
}
-
- memset(rt, 0, sizeof(struct irq_routing_table));
- rt->size = opt.size + sizeof(struct irq_routing_table);
- printk("PCI: Using BIOS Interrupt Routing Table\n");
+ free_page(page);
return rt;
}
+
+int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq)
+{
+ int ret;
+
+ __asm__("lcall (%%esi); cld\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_SET_PCI_HW_INT),
+ "b" ((dev->bus->number << 8) | dev->devfn),
+ "c" ((irq << 8) | (pin + 10)),
+ "S" (&pci_indirect));
+ return !(ret & 0xff00);
+}
+
#endif
/*
@@ -786,75 +772,33 @@ static void __init pcibios_fixup_ghosts(struct pci_bus *b)
}
/*
- * In case there are peer host bridges, scan bus behind each of them.
- * Although several sources claim that the host bridges should have
- * header type 1 and be assigned a bus number as for PCI2PCI bridges,
- * the reality doesn't pass this test and the bus number is usually
- * set by BIOS to the first free value.
+ * Discover remaining PCI buses in case there are peer host bridges.
+ * We use the number of last PCI bus provided by the PCI BIOS.
*/
static void __init pcibios_fixup_peer_bridges(void)
{
- struct list_head *ln;
- struct pci_bus *b = pci_root_bus;
- int n, cnt=-1;
- struct pci_ops *ops = pci_root_bus->ops;
+ int n;
+ struct pci_bus bus;
+ struct pci_dev dev;
+ u16 l;
-#ifdef CONFIG_PCI_DIRECT
- /*
- * Don't search for peer host bridges if we use config type 2
- * since it reads bogus values for non-existent buses and
- * chipsets supporting multiple primary buses use conf1 anyway.
- */
- if (ops == &pci_direct_conf2)
+ if (pcibios_last_bus <= 0 || pcibios_last_bus >= 0xff)
return;
-#endif
-
DBG("PCI: Peer bridge fixup\n");
-
- for(ln=b->devices.next; ln != &b->devices; ln=ln->next)
- if ((pci_dev_b(ln)->class >> 8) == PCI_CLASS_BRIDGE_HOST)
- cnt++;
- n = b->subordinate + 1;
- while (n <= 0xff) {
- int found = 0;
- u16 l;
- struct pci_bus bus;
- struct pci_dev dev;
+ for (n=0; n <= pcibios_last_bus; n++) {
+ if (pci_bus_exists(&pci_root_buses, n))
+ continue;
bus.number = n;
- bus.ops = ops;
+ bus.ops = pci_root_ops;
dev.bus = &bus;
for(dev.devfn=0; dev.devfn<256; dev.devfn += 8)
if (!pci_read_config_word(&dev, PCI_VENDOR_ID, &l) &&
l != 0x0000 && l != 0xffff) {
-#ifdef CONFIG_PCI_BIOS
- if (pci_bios_present) {
- int err, idx = 0;
- u8 bios_bus, bios_dfn;
- u16 d;
- pci_read_config_word(&dev, PCI_DEVICE_ID, &d);
- DBG("BIOS test for %02x:%02x (%04x:%04x)\n", n, dev.devfn, l, d);
- while (!(err = pci_bios_find_device(l, d, idx, &bios_bus, &bios_dfn)) &&
- (bios_bus != n || bios_dfn != dev.devfn))
- idx++;
- if (err)
- break;
- }
-#endif
- DBG("Found device at %02x:%02x\n", n, dev.devfn);
- found++;
- if (!pci_read_config_word(&dev, PCI_CLASS_DEVICE, &l) &&
- l == PCI_CLASS_BRIDGE_HOST)
- cnt++;
+ DBG("Found device at %02x:%02x [%04x]\n", n, dev.devfn, l);
+ printk("PCI: Discovered peer bus %02x\n", n);
+ pci_scan_bus(n, pci_root_ops, NULL);
+ break;
}
- if (cnt-- <= 0)
- break;
- if (found) {
- printk("PCI: Discovered primary peer bus %02x\n", n);
- b = pci_scan_bus(n, ops, NULL);
- if (b)
- n = b->subordinate;
- }
- n++;
}
}
@@ -881,6 +825,7 @@ static void __init pci_fixup_i450nx(struct pci_dev *d)
if (suba < subb)
pci_scan_bus(suba+1, pci_root_ops, NULL); /* Bus B */
}
+ pcibios_last_bus = -1;
}
static void __init pci_fixup_i450gx(struct pci_dev *d)
@@ -893,6 +838,7 @@ static void __init pci_fixup_i450gx(struct pci_dev *d)
pci_read_config_byte(d, 0x4a, &busno);
printk("PCI: i440KX/GX host bridge %s: secondary bus %02x\n", d->slot_name, busno);
pci_scan_bus(busno, pci_root_ops, NULL);
+ pcibios_last_bus = -1;
}
static void __init pci_fixup_rcc(struct pci_dev *d)
@@ -905,6 +851,7 @@ static void __init pci_fixup_rcc(struct pci_dev *d)
pci_read_config_byte(d, 0x44, &busno);
printk("PCI: RCC host bridge: secondary bus %02x\n", busno);
pci_scan_bus(busno, pci_root_ops, NULL);
+ pcibios_last_bus = -1;
}
static void __init pci_fixup_compaq(struct pci_dev *d)
@@ -917,6 +864,7 @@ static void __init pci_fixup_compaq(struct pci_dev *d)
pci_read_config_byte(d, 0xc8, &busno);
printk("PCI: Compaq host bridge: secondary bus %02x\n", busno);
pci_scan_bus(busno, pci_root_ops, NULL);
+ pcibios_last_bus = -1;
}
static void __init pci_fixup_umc_ide(struct pci_dev *d)
@@ -977,333 +925,6 @@ struct pci_fixup pcibios_fixups[] = {
};
/*
- * Fix up IRQs of all PCI devices.
- */
-
-extern int skip_ioapic_setup;
-
-#define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
-#define PIRQ_VERSION 0x0100
-
-static struct irq_routing_table *pirq_table;
-
-/*
- * Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table.
- */
-
-static struct irq_routing_table * __init pcibios_find_irq_routing_table(void)
-{
- u8 *addr;
- struct irq_routing_table *rt;
- int i;
- u8 sum;
-
- for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) {
- rt = (struct irq_routing_table *) addr;
- if (rt->signature != PIRQ_SIGNATURE ||
- rt->version != PIRQ_VERSION ||
- rt->size % 16 ||
- rt->size < sizeof(struct irq_routing_table))
- continue;
- sum = 0;
- for(i=0; i<rt->size; i++)
- sum += addr[i];
- if (!sum) {
- printk("PCI: Interrupt Routing Table found at 0x%p [router type %04x/%04x]\n",
- rt, rt->rtr_vendor, rt->rtr_device);
- return rt;
- }
- }
- return NULL;
-}
-
-/*
- * If we have a IRQ routing table, use it to search for peer host
- * bridges. It's a gross hack, but since there are no other known
- * ways how to get a list of buses, we have to go this way.
- */
-
-static void __init pcibios_irq_peer_trick(struct irq_routing_table *rt)
-{
- u8 busmap[256];
- int i;
- struct irq_info *e;
-
- memset(busmap, 0, sizeof(busmap));
- for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) {
- e = &rt->slots[i];
- DBG("b=%02x d=%02x s=%02x\n", e->bus, e->devfn, e->slot);
- busmap[e->bus] = 1;
- }
- for(i=1; i<256; i++)
- /*
- * It might be a secondary bus, but in this case its parent is already
- * known (ascending bus order) and therefore pci_scan_bus returns immediately.
- */
- if (busmap[i] && pci_scan_bus(i, pci_root_bus->ops, NULL))
- printk("PCI: Discovered primary peer bus %02x [IRQ]\n", i);
-}
-
-static void ali_set_level_irq(unsigned irq)
-{
- unsigned char mask = 1 << (irq & 7);
- unsigned int port = 0x4d0 + (irq >> 3);
- unsigned char val = inb(port);
-
- if (val & mask) {
- DBG("PCI irq %d was level\n", irq);
- return;
- }
- DBG("PCI irq %d was edge, turning into level-triggered\n", irq);
- outb(val | mask, port);
-}
-
-static int ali_set_irq(struct pci_dev *router, unsigned pirq, unsigned irq)
-{
- if (irq < 15) {
- static unsigned char irqmap[16] = {
- 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15
- };
- unsigned char val = irqmap[irq];
- if (val && pirq < 8) {
- u8 byte;
- unsigned offset = 0x48 + (pirq >> 1);
- unsigned shift = (pirq & 1) << 2;
- pci_read_config_byte(router, offset, &byte);
- DBG("ALI: old %04x=%02x\n", offset, byte);
- byte &= ~(0xf << shift);
- byte |= val << shift;
- DBG("ALI: new %04x=%02x\n", offset, byte);
- pci_write_config_byte(router, offset, byte);
- ali_set_level_irq(irq);
- return irq;
- }
- }
- return 0;
-}
-
-/*
- * In case BIOS forgets to tell us about IRQ, we try to look it up in the routing
- * table, but unfortunately we have to know the interrupt router chip.
- */
-
-/*
- * Never use: 0, 1, 2 (timer, keyboard, and cascade)
- * Avoid using: 13, 14 and 15 (FP error and IDE).
- * Penalize: 3, 4, 7, 12 (known ISA uses: serial, parallel and mouse)
- */
-static unsigned int pcibios_irq_mask = 0xfff8;
-
-static unsigned pcibios_irq_penalty[16] = {
- 10000, 10000, 10000, 100, 100, 0, 0, 100,
- 0, 0, 0, 0, 100, 1000, 1000, 1000
-};
-
-static char *pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_table *rt, int pin, int assign)
-{
- struct irq_info *q;
- struct pci_dev *router;
- int i, pirq, newirq, reg;
- u32 rtrid, mask;
- u8 x, y;
- char *msg = NULL;
-
- pin--;
- DBG("IRQ for %s(%d)", dev->slot_name, pin);
- while (dev->bus->self) {
- pin = (pin + PCI_SLOT(dev->devfn)) % 4;
- dev = dev->bus->self;
- DBG(" -> %s(%d)", dev->slot_name, pin);
- }
- for(q = rt->slots, i = rt->size - sizeof(struct irq_routing_table);
- i && (q->bus != dev->bus->number || PCI_SLOT(q->devfn) != PCI_SLOT(dev->devfn));
- i -= sizeof(struct irq_info), q++)
- ;
- if (!i) {
- DBG(" -> not found in routing table\n");
- return NULL;
- }
- pirq = q->irq[pin].link;
- mask = q->irq[pin].bitmap;
- if (!pirq) {
- DBG(" -> not routed\n");
- return NULL;
- }
- DBG(" -> PIRQ %02x, mask %04x", pirq, mask);
- newirq = 0;
- if (assign && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
- for (i = 0; i < 16; i++) {
- if (!(mask & pcibios_irq_mask & (1 << i)))
- continue;
- if (pcibios_irq_penalty[i] < pcibios_irq_penalty[newirq])
- newirq = i;
- }
- }
- if (!(router = pci_find_slot(rt->rtr_bus, rt->rtr_devfn))) {
- DBG(" -> router not found\n");
- return NULL;
- }
-#define ID(x,y) ((x << 16) | y)
- rtrid = ID(rt->rtr_vendor, rt->rtr_device);
- if (!rtrid) {
- /*
- * Several BIOSes forget to set the router type. In such cases, we
- * use chip vendor/device. This doesn't guarantee us semantics of
- * PIRQ values, but was found to work in practice and it's still
- * better than not trying.
- */
- DBG(" [%s]", router->slot_name);
- rtrid = ID(router->vendor, router->device);
- }
- switch (rtrid) {
- case ID(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0):
- case ID(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0):
- case ID(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0):
- /* Intel PIIX: PIRQ holds configuration register address */
- pci_read_config_byte(router, pirq, &x);
- if (x < 16) {
- DBG(" -> [PIIX] %02x\n", x);
- newirq = x;
- msg = "PIIX";
- } else if (newirq) {
- DBG(" -> [PIIX] set to %02x\n", newirq);
- pci_write_config_byte(router, pirq, newirq);
- msg = "PIIX-NEW";
- } else DBG(" -> [PIIX] sink\n");
- break;
- case ID(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533):
- newirq = ali_set_irq(router, pirq-1, newirq);
- if (newirq)
- msg = "ALI";
- break;
- case ID(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596):
- case ID(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686):
- reg = 0x55 + (pirq >> 1);
- pci_read_config_byte(router, reg, &x);
- y = (pirq & 1) ? (x >> 4) : (x & 0x0f);
- if (y) {
- DBG(" -> [VIA] %02x\n", y);
- newirq = y;
- msg = "VIA";
- } else if (newirq) {
- DBG(" -> [VIA] set to %02x\n", newirq);
- x = (pirq & 1) ? ((x & 0x0f) | (newirq << 4)) : ((x & 0xf0) | newirq);
- pci_write_config_byte(router, reg, x);
- msg = "VIA-NEW";
- } else DBG(" -> [VIA] sink\n");
- break;
- case ID(PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C700):
- reg = 0xb8 + (pirq >> 5);
- pci_read_config_byte(router, reg, &x);
- y = (pirq & 0x10) ? (x >> 4) : (x & 0x0f);
- if (y) {
- DBG(" -> [OPTI] %02x\n", y);
- newirq = y;
- msg = "OPTI";
- } else if (newirq) {
- DBG(" -> [OPTI] set to %02x\n", newirq);
- x = (pirq & 0x10) ? ((x & 0x0f) | (newirq << 4)) : ((x & 0xf0) | newirq);
- pci_write_config_byte(router, reg, y);
- msg = "OPTI-NEW";
- } else DBG(" -> [OPTI] sink\n");
- break;
- default:
- DBG(" -> unknown router %04x/%04x\n", rt->rtr_vendor, rt->rtr_device);
- if (newirq && mask == (1 << newirq)) {
- /* Only one IRQ available -> use it */
- msg = "guess";
- }
- }
-#undef ID
-
- if (msg) {
- dev->irq = newirq;
- pcibios_irq_penalty[newirq]++;
- }
- return msg;
-}
-
-static void __init pcibios_fixup_irqs(void)
-{
- struct irq_routing_table *rtable;
- struct pci_dev *dev;
- u8 pin;
-
- DBG("PCI: IRQ fixup\n");
- rtable = pirq_table = pcibios_find_irq_routing_table();
-#ifdef CONFIG_PCI_BIOS
- if (!rtable && pci_bios_present)
- rtable = pcibios_get_irq_routing_table();
-#endif
-
- if (rtable)
- pcibios_irq_peer_trick(rtable);
-
- pci_for_each_dev(dev) {
- /*
- * If the BIOS has set an out of range IRQ number, just ignore it.
- * Also keep track of which IRQ's are already in use.
- */
- if (dev->irq >= 16) {
- DBG("%s: ignoring bogus IRQ %d\n", dev->slot_name, dev->irq);
- dev->irq = 0;
- }
- pcibios_irq_penalty[dev->irq]++;
- }
-
- pci_for_each_dev(dev) {
- pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
-#if defined(CONFIG_X86_IO_APIC)
- /*
- * Recalculate IRQ numbers if we use the I/O APIC.
- */
- if(!skip_ioapic_setup)
- {
- int irq;
-
- if (pin) {
- pin--; /* interrupt pins are numbered starting from 1 */
- irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
-/*
- * Will be removed completely if things work out well with fuzzy parsing
- */
-#if 0
- if (irq < 0 && dev->bus->parent) { /* go back to the bridge */
- struct pci_dev * bridge = dev->bus->self;
-
- pin = (pin + PCI_SLOT(dev->devfn)) % 4;
- irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number,
- PCI_SLOT(bridge->devfn), pin);
- if (irq >= 0)
- printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n",
- bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq);
- }
-#endif
- if (irq >= 0) {
- printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",
- dev->bus->number, PCI_SLOT(dev->devfn), pin, irq);
- dev->irq = irq;
- }
- }
- pirq_table = NULL; /* Avoid automatic IRQ assignment */
- }
-#endif
- /*
- * Still no IRQ? Try to assign one...
- */
- if (pin && !dev->irq && pirq_table) {
- char *msg = pcibios_lookup_irq(dev, pirq_table, pin, 0);
- if (msg)
- printk("PCI: Found IRQ %d for device %s [%s]\n", dev->irq, dev->slot_name, msg);
- }
- }
-
-#ifdef CONFIG_PCI_BIOS
- pcibios_free_irq_routing_table();
-#endif
-}
-
-/*
* Called after each bus is probed, but before its children
* are examined.
*/
@@ -1349,8 +970,7 @@ void __init pcibios_init(void)
pci_root_bus = pci_scan_bus(0, pci_root_ops, NULL);
pcibios_fixup_irqs();
- if (pci_probe & PCI_PEER_FIXUP)
- pcibios_fixup_peer_bridges();
+ pcibios_fixup_peer_bridges();
pcibios_resource_survey();
#ifdef CONFIG_PCI_BIOS
@@ -1390,15 +1010,15 @@ char * __init pcibios_setup(char *str)
return NULL;
}
#endif
- else if (!strcmp(str, "peer")) {
- pci_probe |= PCI_PEER_FIXUP;
- return NULL;
- } else if (!strcmp(str, "rom")) {
+ else if (!strcmp(str, "rom")) {
pci_probe |= PCI_ASSIGN_ROMS;
return NULL;
} else if (!strncmp(str, "irqmask=", 8)) {
pcibios_irq_mask = simple_strtol(str+8, NULL, 0);
return NULL;
+ } else if (!strncmp(str, "lastbus=", 8)) {
+ pcibios_last_bus = simple_strtol(str+8, NULL, 0);
+ return NULL;
}
return str;
}
@@ -1412,15 +1032,10 @@ int pcibios_enable_device(struct pci_dev *dev)
if (!dev->irq) {
u8 pin;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
- if (pin) {
- char *msg;
- if (pirq_table && ((msg = pcibios_lookup_irq(dev, pirq_table, pin, 1))))
- printk("PCI: Assigned IRQ %d to device %s [%s]\n", dev->irq, dev->slot_name, msg);
- else
- printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
- 'A' + pin - 1, dev->slot_name,
- (pci_probe & PCI_BIOS_IRQ_SCAN) ? "" : " Please try using pci=biosirq.");
- }
+ if (pin && !pcibios_lookup_irq(dev, 1))
+ printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
+ 'A' + pin - 1, dev->slot_name,
+ (pci_probe & PCI_BIOS_IRQ_SCAN) ? "" : " Please try using pci=biosirq.");
}
return 0;
}