From 7f974a7687129917e3c3ceb74287bdf9ba0fd5f3 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 19 Apr 2000 13:55:33 +0000 Subject: Merge with Linux 2.3.99-pre5. --- arch/i386/kernel/Makefile | 2 +- arch/i386/kernel/pci-i386.h | 40 +++- arch/i386/kernel/pci-irq.c | 431 +++++++++++++++++++++++++++++++++++ arch/i386/kernel/pci-pc.c | 535 +++++++------------------------------------- 4 files changed, 546 insertions(+), 462 deletions(-) create mode 100644 arch/i386/kernel/pci-irq.c (limited to 'arch') 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; isize; 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 #include #include -#include -#include -#include #include #include @@ -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 \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) @@ -976,333 +924,6 @@ struct pci_fixup pcibios_fixups[] = { { 0 } }; -/* - * 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; isize; 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; } -- cgit v1.2.3