patch-2.3.20 linux/arch/i386/kernel/pci-pc.c
Next file: linux/arch/i386/kernel/pci-visws.c
Previous file: linux/arch/i386/kernel/pci-i386.h
Back to the patch index
Back to the overall index
- Lines: 1226
- Date:
Fri Oct 8 10:33:53 1999
- Orig file:
v2.3.19/linux/arch/i386/kernel/pci-pc.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.19/linux/arch/i386/kernel/pci-pc.c linux/arch/i386/kernel/pci-pc.c
@@ -0,0 +1,1225 @@
+/*
+ * Low-Level PCI Support for PC
+ *
+ * (c) 1999 Martin Mares <mj@ucw.cz>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#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>
+#include <asm/smp.h>
+
+#include "pci-i386.h"
+
+unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2;
+
+/*
+ * 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));
+
+/*
+ * Direct access to PCI hardware...
+ */
+
+#ifdef CONFIG_PCI_DIRECT
+
+/*
+ * Functions for accessing PCI configuration space with type 1 accesses
+ */
+
+#define CONFIG_CMD(dev, where) (0x80000000 | (dev->bus->number << 16) | (dev->devfn << 8) | (where & ~3))
+
+static int pci_conf1_read_config_byte(struct pci_dev *dev, int where, u8 *value)
+{
+ outl(CONFIG_CMD(dev,where), 0xCF8);
+ *value = inb(0xCFC + (where&3));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_read_config_word(struct pci_dev *dev, int where, u16 *value)
+{
+ outl(CONFIG_CMD(dev,where), 0xCF8);
+ *value = inw(0xCFC + (where&2));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_read_config_dword(struct pci_dev *dev, int where, u32 *value)
+{
+ outl(CONFIG_CMD(dev,where), 0xCF8);
+ *value = inl(0xCFC);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_write_config_byte(struct pci_dev *dev, int where, u8 value)
+{
+ outl(CONFIG_CMD(dev,where), 0xCF8);
+ outb(value, 0xCFC + (where&3));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_write_config_word(struct pci_dev *dev, int where, u16 value)
+{
+ outl(CONFIG_CMD(dev,where), 0xCF8);
+ outw(value, 0xCFC + (where&2));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_write_config_dword(struct pci_dev *dev, int where, u32 value)
+{
+ outl(CONFIG_CMD(dev,where), 0xCF8);
+ outl(value, 0xCFC);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+#undef CONFIG_CMD
+
+static struct pci_ops pci_direct_conf1 = {
+ pci_conf1_read_config_byte,
+ pci_conf1_read_config_word,
+ pci_conf1_read_config_dword,
+ pci_conf1_write_config_byte,
+ pci_conf1_write_config_word,
+ pci_conf1_write_config_dword
+};
+
+/*
+ * Functions for accessing PCI configuration space with type 2 accesses
+ */
+
+#define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where)
+#define FUNC(devfn) (((devfn & 7) << 1) | 0xf0)
+#define SET(dev) if (dev->devfn) return PCIBIOS_DEVICE_NOT_FOUND; \
+ outb(FUNC(dev->devfn), 0xCF8); \
+ outb(dev->bus->number, 0xCFA);
+
+static int pci_conf2_read_config_byte(struct pci_dev *dev, int where, u8 *value)
+{
+ SET(dev);
+ *value = inb(IOADDR(dev->devfn,where));
+ outb (0, 0xCF8);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_read_config_word(struct pci_dev *dev, int where, u16 *value)
+{
+ SET(dev);
+ *value = inw(IOADDR(dev->devfn,where));
+ outb (0, 0xCF8);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_read_config_dword(struct pci_dev *dev, int where, u32 *value)
+{
+ SET(dev);
+ *value = inl (IOADDR(dev->devfn,where));
+ outb (0, 0xCF8);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_byte(struct pci_dev *dev, int where, u8 value)
+{
+ SET(dev);
+ outb (value, IOADDR(dev->devfn,where));
+ outb (0, 0xCF8);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_word(struct pci_dev *dev, int where, u16 value)
+{
+ SET(dev);
+ outw (value, IOADDR(dev->devfn,where));
+ outb (0, 0xCF8);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_dword(struct pci_dev *dev, int where, u32 value)
+{
+ SET(dev);
+ outl (value, IOADDR(dev->devfn,where));
+ outb (0, 0xCF8);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+#undef SET
+#undef IOADDR
+#undef FUNC
+
+static struct pci_ops pci_direct_conf2 = {
+ pci_conf2_read_config_byte,
+ pci_conf2_read_config_word,
+ pci_conf2_read_config_dword,
+ pci_conf2_write_config_byte,
+ pci_conf2_write_config_word,
+ pci_conf2_write_config_dword
+};
+
+/*
+ * Before we decide to use direct hardware access mechanisms, we try to do some
+ * trivial checks to ensure it at least _seems_ to be working -- we just test
+ * whether bus 00 contains a host bridge (this is similar to checking
+ * techniques used in XFree86, but ours should be more reliable since we
+ * attempt to make use of direct access hints provided by the PCI BIOS).
+ *
+ * This should be close to trivial, but it isn't, because there are buggy
+ * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
+ */
+static int __init pci_sanity_check(struct pci_ops *o)
+{
+ u16 x;
+ struct pci_bus bus; /* Fake bus and device */
+ struct pci_dev dev;
+
+ if (pci_probe & PCI_NO_CHECKS)
+ return 1;
+ bus.number = 0;
+ dev.bus = &bus;
+ for(dev.devfn=0; dev.devfn < 0x100; dev.devfn++)
+ if ((!o->read_word(&dev, PCI_CLASS_DEVICE, &x) &&
+ (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) ||
+ (!o->read_word(&dev, PCI_VENDOR_ID, &x) &&
+ (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)))
+ return 1;
+ DBG("PCI: Sanity check failed\n");
+ return 0;
+}
+
+static struct pci_ops * __init pci_check_direct(void)
+{
+ unsigned int tmp;
+ unsigned long flags;
+
+ __save_flags(flags); __cli();
+
+ /*
+ * Check if configuration type 1 works.
+ */
+ if (pci_probe & PCI_PROBE_CONF1) {
+ outb (0x01, 0xCFB);
+ tmp = inl (0xCF8);
+ outl (0x80000000, 0xCF8);
+ if (inl (0xCF8) == 0x80000000 &&
+ pci_sanity_check(&pci_direct_conf1)) {
+ outl (tmp, 0xCF8);
+ __restore_flags(flags);
+ printk("PCI: Using configuration type 1\n");
+ return &pci_direct_conf1;
+ }
+ outl (tmp, 0xCF8);
+ }
+
+ /*
+ * Check if configuration type 2 works.
+ */
+ if (pci_probe & PCI_PROBE_CONF2) {
+ outb (0x00, 0xCFB);
+ outb (0x00, 0xCF8);
+ outb (0x00, 0xCFA);
+ if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00 &&
+ pci_sanity_check(&pci_direct_conf2)) {
+ __restore_flags(flags);
+ printk("PCI: Using configuration type 2\n");
+ return &pci_direct_conf2;
+ }
+ }
+
+ __restore_flags(flags);
+ return NULL;
+}
+
+#endif
+
+/*
+ * BIOS32 and PCI BIOS handling.
+ */
+
+#ifdef CONFIG_PCI_BIOS
+
+#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX
+#define PCIBIOS_PCI_BIOS_PRESENT 0xb101
+#define PCIBIOS_FIND_PCI_DEVICE 0xb102
+#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103
+#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106
+#define PCIBIOS_READ_CONFIG_BYTE 0xb108
+#define PCIBIOS_READ_CONFIG_WORD 0xb109
+#define PCIBIOS_READ_CONFIG_DWORD 0xb10a
+#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b
+#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
+#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
+#define PCIBIOS_GET_ROUTING_OPTIONS 0xb10e
+#define PCIBIOS_SET_PCI_HW_INT 0xb10f
+
+/* BIOS32 signature: "_32_" */
+#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
+
+/* PCI signature: "PCI " */
+#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24))
+
+/* PCI service signature: "$PCI" */
+#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24))
+
+/* PCI BIOS hardware mechanism flags */
+#define PCIBIOS_HW_TYPE1 0x01
+#define PCIBIOS_HW_TYPE2 0x02
+#define PCIBIOS_HW_TYPE1_SPEC 0x10
+#define PCIBIOS_HW_TYPE2_SPEC 0x20
+
+/*
+ * This is the standard structure used to identify the entry point
+ * to the BIOS32 Service Directory, as documented in
+ * Standard BIOS 32-bit Service Directory Proposal
+ * Revision 0.4 May 24, 1993
+ * Phoenix Technologies Ltd.
+ * Norwood, MA
+ * and the PCI BIOS specification.
+ */
+
+union bios32 {
+ struct {
+ unsigned long signature; /* _32_ */
+ unsigned long entry; /* 32 bit physical address */
+ unsigned char revision; /* Revision level, 0 */
+ unsigned char length; /* Length in paragraphs should be 01 */
+ unsigned char checksum; /* All bytes must add up to zero */
+ unsigned char reserved[5]; /* Must be zero */
+ } fields;
+ char chars[16];
+};
+
+/*
+ * Physical address of the service directory. I don't know if we're
+ * allowed to have more than one of these or not, so just in case
+ * we'll make pcibios_present() take a memory start parameter and store
+ * the array there.
+ */
+
+static struct {
+ unsigned long address;
+ unsigned short segment;
+} bios32_indirect = { 0, __KERNEL_CS };
+
+/*
+ * Returns the entry point for the given service, NULL on error
+ */
+
+static unsigned long bios32_service(unsigned long service)
+{
+ unsigned char return_code; /* %al */
+ unsigned long address; /* %ebx */
+ unsigned long length; /* %ecx */
+ unsigned long entry; /* %edx */
+ unsigned long flags;
+
+ __save_flags(flags); __cli();
+ __asm__("lcall (%%edi)"
+ : "=a" (return_code),
+ "=b" (address),
+ "=c" (length),
+ "=d" (entry)
+ : "0" (service),
+ "1" (0),
+ "D" (&bios32_indirect));
+ __restore_flags(flags);
+
+ switch (return_code) {
+ case 0:
+ return address + entry;
+ case 0x80: /* Not present */
+ printk("bios32_service(0x%lx): not present\n", service);
+ return 0;
+ default: /* Shouldn't happen */
+ printk("bios32_service(0x%lx): returned 0x%x -- BIOS bug!\n",
+ service, return_code);
+ return 0;
+ }
+}
+
+static struct {
+ unsigned long address;
+ unsigned short segment;
+} pci_indirect = { 0, __KERNEL_CS };
+
+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;
+ unsigned long flags, pcibios_entry;
+
+ if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
+ pci_indirect.address = pcibios_entry + PAGE_OFFSET;
+
+ __save_flags(flags); __cli();
+ __asm__(
+ "lcall (%%edi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=d" (signature),
+ "=a" (eax),
+ "=b" (ebx),
+ "=c" (ecx)
+ : "1" (PCIBIOS_PCI_BIOS_PRESENT),
+ "D" (&pci_indirect)
+ : "memory");
+ __restore_flags(flags);
+
+ status = (eax >> 8) & 0xff;
+ hw_mech = eax & 0xff;
+ major_ver = (ebx >> 8) & 0xff;
+ minor_ver = ebx & 0xff;
+ 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);
+ if (status || signature != PCI_SIGNATURE) {
+ printk (KERN_ERR "PCI: BIOS BUG #%x[%08x] found, report to <mj@ucw.cz>\n",
+ status, signature);
+ return 0;
+ }
+ printk("PCI: PCI BIOS revision %x.%02x entry at 0x%lx\n",
+ major_ver, minor_ver, pcibios_entry);
+#ifdef CONFIG_PCI_DIRECT
+ if (!(hw_mech & PCIBIOS_HW_TYPE1))
+ pci_probe &= ~PCI_PROBE_CONF1;
+ if (!(hw_mech & PCIBIOS_HW_TYPE2))
+ pci_probe &= ~PCI_PROBE_CONF2;
+#endif
+ return 1;
+ }
+ return 0;
+}
+
+static int __init pci_bios_find_device (unsigned short vendor, unsigned short device_id,
+ unsigned short index, unsigned char *bus, unsigned char *device_fn)
+{
+ unsigned short bx;
+ unsigned short ret;
+
+ __asm__("lcall (%%edi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=b" (bx),
+ "=a" (ret)
+ : "1" (PCIBIOS_FIND_PCI_DEVICE),
+ "c" (device_id),
+ "d" (vendor),
+ "S" ((int) index),
+ "D" (&pci_indirect));
+ *bus = (bx >> 8) & 0xff;
+ *device_fn = bx & 0xff;
+ return (int) (ret & 0xff00) >> 8;
+}
+
+static int pci_bios_read_config_byte(struct pci_dev *dev, int where, u8 *value)
+{
+ unsigned long ret;
+ unsigned long bx = (dev->bus->number << 8) | dev->devfn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_BYTE),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+static int pci_bios_read_config_word(struct pci_dev *dev, int where, u16 *value)
+{
+ unsigned long ret;
+ unsigned long bx = (dev->bus->number << 8) | dev->devfn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_WORD),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+static int pci_bios_read_config_dword(struct pci_dev *dev, int where, u32 *value)
+{
+ unsigned long ret;
+ unsigned long bx = (dev->bus->number << 8) | dev->devfn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_DWORD),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+static int pci_bios_write_config_byte(struct pci_dev *dev, int where, u8 value)
+{
+ unsigned long ret;
+ unsigned long bx = (dev->bus->number << 8) | dev->devfn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_BYTE),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+static int pci_bios_write_config_word(struct pci_dev *dev, int where, u16 value)
+{
+ unsigned long ret;
+ unsigned long bx = (dev->bus->number << 8) | dev->devfn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_WORD),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+static int pci_bios_write_config_dword(struct pci_dev *dev, int where, u32 value)
+{
+ unsigned long ret;
+ unsigned long bx = (dev->bus->number << 8) | dev->devfn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_DWORD),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+/*
+ * Function table for BIOS32 access
+ */
+
+static struct pci_ops pci_bios_access = {
+ pci_bios_read_config_byte,
+ pci_bios_read_config_word,
+ pci_bios_read_config_dword,
+ pci_bios_write_config_byte,
+ pci_bios_write_config_word,
+ pci_bios_write_config_dword
+};
+
+/*
+ * Try to find PCI BIOS.
+ */
+
+static struct pci_ops * __init pci_find_bios(void)
+{
+ union bios32 *check;
+ unsigned char sum;
+ int i, length;
+
+ /*
+ * Follow the standard procedure for locating the BIOS32 Service
+ * directory by scanning the permissible address range from
+ * 0xe0000 through 0xfffff for a valid BIOS32 structure.
+ */
+
+ for (check = (union bios32 *) __va(0xe0000);
+ check <= (union bios32 *) __va(0xffff0);
+ ++check) {
+ if (check->fields.signature != BIOS32_SIGNATURE)
+ continue;
+ length = check->fields.length * 16;
+ if (!length)
+ continue;
+ sum = 0;
+ for (i = 0; i < length ; ++i)
+ sum += check->chars[i];
+ if (sum != 0)
+ continue;
+ if (check->fields.revision != 0) {
+ printk("PCI: unsupported BIOS32 revision %d at 0x%p, report to <mj@ucw.cz>\n",
+ check->fields.revision, check);
+ continue;
+ }
+ DBG("PCI: BIOS32 Service Directory structure at 0x%p\n", check);
+ if (check->fields.entry >= 0x100000) {
+ printk("PCI: BIOS32 entry (0x%p) in high memory, cannot use.\n", check);
+ return NULL;
+ } else {
+ unsigned long bios32_entry = check->fields.entry;
+ DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
+ bios32_indirect.address = bios32_entry + PAGE_OFFSET;
+ if (check_pcibios())
+ return &pci_bios_access;
+ }
+ break; /* Hopefully more than one BIOS32 cannot happen... */
+ }
+
+ return NULL;
+}
+
+/*
+ * Sort the device list according to PCI BIOS. Nasty hack, but since some
+ * fool forgot to define the `correct' device order in the PCI BIOS specs
+ * and we want to be (possibly bug-to-bug ;-]) compatible with older kernels
+ * which used BIOS ordering, we are bound to do this...
+ */
+
+static void __init pcibios_sort(void)
+{
+ struct pci_dev *dev = pci_devices;
+ struct pci_dev **last = &pci_devices;
+ struct pci_dev *d, **dd, *e;
+ int idx;
+ unsigned char bus, devfn;
+
+ DBG("PCI: Sorting device list...\n");
+ while ((e = dev)) {
+ idx = 0;
+ while (pci_bios_find_device(e->vendor, e->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) {
+ idx++;
+ for(dd=&dev; (d = *dd); dd = &d->next) {
+ if (d->bus->number == bus && d->devfn == devfn) {
+ *dd = d->next;
+ *last = d;
+ last = &d->next;
+ break;
+ }
+ }
+ if (!d) {
+ printk("PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn);
+ /*
+ * We must not continue scanning as several buggy BIOSes
+ * return garbage after the last device. Grr.
+ */
+ break;
+ }
+ }
+ if (e == dev) {
+ printk("PCI: Device %02x:%02x not found by BIOS\n",
+ dev->bus->number, dev->devfn);
+ d = dev;
+ dev = dev->next;
+ *last = d;
+ last = &d->next;
+ }
+ }
+ *last = NULL;
+}
+
+/*
+ * Ask BIOS for IRQ Routing Table
+ */
+
+struct irq_routing_options {
+ u16 size;
+ struct irq_info *table;
+ 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_options opt;
+ struct irq_routing_table *rt;
+ int ret, map;
+
+ if (pci_probe & PCI_NO_IRQ_SCAN)
+ 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.segment = __KERNEL_DS;
+
+ DBG("PCI: Fetching IRQ routing table... ");
+ __asm__("push %%es\n\t"
+ "push %%ds\n\t"
+ "pop %%es\n\t"
+ "lcall (%%esi)\n\t"
+ "pop %%es\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret),
+ "=b" (map)
+ : "0" (PCIBIOS_GET_ROUTING_OPTIONS),
+ "1" (0),
+ "D" ((long) &opt),
+ "S" (&pci_indirect));
+ DBG("OK ret=%d, size=%d, map=%x\n", ret, opt.size, map);
+ if (ret & 0xff00) {
+ printk(KERN_ERR "PCI: Error %02x when fetching IRQ routing table.\n", (ret >> 8) & 0xff);
+ return 0;
+ }
+
+ 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");
+ return rt;
+}
+
+#endif
+
+/*
+ * Several buggy motherboards address only 16 devices and mirror
+ * them to next 16 IDs. We try to detect this `feature' on all
+ * primary buses (those containing host bridges as they are
+ * expected to be unique) and remove the ghost devices.
+ */
+
+static void __init pcibios_fixup_ghosts(struct pci_bus *b)
+{
+ struct pci_dev *d, *e, **z;
+ int mirror = PCI_DEVFN(16,0);
+ int seen_host_bridge = 0;
+ int i;
+
+ DBG("PCI: Scanning for ghost devices on bus %d\n", b->number);
+ for(d=b->devices; d && d->devfn < mirror; d=d->sibling) {
+ if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST)
+ seen_host_bridge++;
+ for(e=d->next; e; e=e->sibling) {
+ if (e->devfn != d->devfn + mirror ||
+ e->vendor != d->vendor ||
+ e->device != d->device ||
+ e->class != d->class)
+ continue;
+ for(i=0; i<PCI_NUM_RESOURCES; i++)
+ if (e->resource[i].start != d->resource[i].start ||
+ e->resource[i].end != d->resource[i].end ||
+ e->resource[i].flags != d->resource[i].flags)
+ continue;
+ break;
+ }
+ if (!e)
+ return;
+ }
+ if (!seen_host_bridge)
+ return;
+ printk("PCI: Ignoring ghost devices on bus %02x\n", b->number);
+ for(e=b->devices; e->sibling != d; e=e->sibling);
+ e->sibling = NULL;
+ for(z=&pci_devices; (d=*z);)
+ if (d->bus == b && d->devfn >= mirror) {
+ *z = d->next;
+ kfree_s(d, sizeof(*d));
+ } else
+ z = &d->next;
+}
+
+/*
+ * 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.
+ */
+static void __init pcibios_fixup_peer_bridges(void)
+{
+ struct pci_bus *b = pci_root;
+ int n, cnt=-1;
+ struct pci_dev *d;
+ struct pci_ops *ops = pci_root->ops;
+
+#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)
+ return;
+#endif
+
+ for(d=b->devices; d; d=d->sibling)
+ if ((d->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;
+ bus.number = n;
+ bus.ops = 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++;
+ }
+ 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++;
+ }
+}
+
+/*
+ * Exceptions for specific devices. Usually work-arounds for fatal design flaws.
+ */
+
+static void __init pci_fixup_i450nx(struct pci_dev *d)
+{
+ /*
+ * i450NX -- Find and scan all secondary buses on all PXB's.
+ */
+ int pxb, reg;
+ u8 busno, suba, subb;
+ printk("PCI: Searching for i450NX host bridges on %s\n", d->slot_name);
+ reg = 0xd0;
+ for(pxb=0; pxb<2; pxb++) {
+ pci_read_config_byte(d, reg++, &busno);
+ pci_read_config_byte(d, reg++, &suba);
+ pci_read_config_byte(d, reg++, &subb);
+ DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb);
+ if (busno)
+ pci_scan_bus(busno, pci_root->ops, NULL); /* Bus A */
+ if (suba < subb)
+ pci_scan_bus(suba+1, pci_root->ops, NULL); /* Bus B */
+ }
+ pci_probe |= PCI_NO_PEER_FIXUP;
+}
+
+static void __init pci_fixup_umc_ide(struct pci_dev *d)
+{
+ /*
+ * UM8886BF IDE controller sets region type bits incorrectly,
+ * therefore they look like memory despite of them being I/O.
+ */
+ int i;
+
+ printk("PCI: Fixing base address flags for device %s\n", d->slot_name);
+ for(i=0; i<4; i++)
+ d->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO;
+}
+
+static void __init pci_fixup_ide_bases(struct pci_dev *d)
+{
+ int i;
+
+ /*
+ * PCI IDE controllers use non-standard I/O port decoding, respect it.
+ */
+ if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE)
+ return;
+ DBG("PCI: IDE base address fixup for %s\n", d->slot_name);
+ for(i=0; i<4; i++) {
+ struct resource *r = &d->resource[i];
+ if ((r->start & ~0x80) == 0x374) {
+ r->start |= 2;
+ r->end = r->start;
+ }
+ }
+}
+
+struct pci_fixup pcibios_fixups[] = {
+ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx },
+ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide },
+ { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases },
+ { 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
+
+/*
+ * 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->ops, NULL))
+ printk("PCI: Discovered primary peer bus %02x [IRQ]\n", i);
+ pci_probe |= PCI_NO_PEER_FIXUP;
+}
+
+/*
+ * 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.
+ */
+
+static char * __init pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_table *rt, int pin)
+{
+ struct irq_info *q;
+ struct pci_dev *router;
+ int i, pirq, newirq;
+ u32 rtrid, mask;
+ u8 x;
+
+ 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);
+ if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
+ newirq = 0;
+ else for(newirq = 15; newirq && !(mask & (1 << newirq)); newirq--)
+ ;
+ 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);
+ dev->irq = x;
+ return "PIIX";
+ } else if (newirq) {
+ DBG(" -> [PIIX] set to %02x\n", newirq);
+ pci_write_config_byte(router, pirq, newirq);
+ dev->irq = newirq;
+ return "PIIX-NEW";
+ }
+ DBG(" -> [PIIX] sink\n");
+ return NULL;
+ default:
+ DBG(" -> unknown router %04x/%04x\n", rt->rtr_vendor, rt->rtr_device);
+ if (newirq && mask == (1 << newirq)) {
+ /* Only one IRQ available -> use it */
+ dev->irq = newirq;
+ return "guess";
+ }
+ return NULL;
+ }
+#undef ID
+}
+
+static void __init pcibios_fixup_irqs(void)
+{
+ struct irq_routing_table *rtable;
+ struct pci_dev *dev;
+ u8 pin;
+
+ rtable = 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);
+
+ for(dev=pci_devices; dev; dev=dev->next) {
+ 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);
+ 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);
+ }
+ 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;
+ }
+ }
+ rtable = NULL; /* Avoid IRQ assignment below */
+ }
+#endif
+ /*
+ * Fix out-of-range IRQ numbers and missing IRQs.
+ */
+ if (dev->irq >= NR_IRQS)
+ dev->irq = 0;
+ if (pin && !dev->irq && rtable && rtable->version) {
+ char *msg = pcibios_lookup_irq(dev, rtable, pin);
+ if (msg)
+ printk("PCI: Assigned IRQ %d to 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.
+ */
+
+void __init pcibios_fixup_bus(struct pci_bus *b)
+{
+ pcibios_fixup_ghosts(b);
+ pci_read_bridge_bases(b);
+}
+
+/*
+ * Initialization. Try all known PCI access methods. Note that we support
+ * using both PCI BIOS and direct access: in such cases, we use I/O ports
+ * to access config space, but we still keep BIOS order of cards to be
+ * compatible with 2.0.X. This should go away some day.
+ */
+
+void __init pcibios_init(void)
+{
+ struct pci_ops *bios = NULL;
+ struct pci_ops *dir = NULL;
+ struct pci_ops *ops;
+
+#ifdef CONFIG_PCI_BIOS
+ if ((pci_probe & PCI_PROBE_BIOS) && ((bios = pci_find_bios()))) {
+ pci_probe |= PCI_BIOS_SORT;
+ pci_bios_present = 1;
+ }
+#endif
+#ifdef CONFIG_PCI_DIRECT
+ if (pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2))
+ dir = pci_check_direct();
+#endif
+ if (dir)
+ ops = dir;
+ else if (bios)
+ ops = bios;
+ else {
+ printk("PCI: No PCI bus detected\n");
+ return;
+ }
+
+ printk("PCI: Probing PCI hardware\n");
+ pci_scan_bus(0, ops, NULL);
+
+ pcibios_fixup_irqs();
+ if (!(pci_probe & PCI_NO_PEER_FIXUP))
+ pcibios_fixup_peer_bridges();
+ pcibios_resource_survey();
+
+#ifdef CONFIG_PCI_BIOS
+ if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT))
+ pcibios_sort();
+#endif
+}
+
+char * __init pcibios_setup(char *str)
+{
+ if (!strcmp(str, "off")) {
+ pci_probe = 0;
+ return NULL;
+ }
+#ifdef CONFIG_PCI_BIOS
+ else if (!strcmp(str, "bios")) {
+ pci_probe = PCI_PROBE_BIOS;
+ return NULL;
+ } else if (!strcmp(str, "nobios")) {
+ pci_probe &= ~PCI_PROBE_BIOS;
+ return NULL;
+ } else if (!strcmp(str, "nosort")) {
+ pci_probe |= PCI_NO_SORT;
+ return NULL;
+ } else if (!strcmp(str, "noirq")) {
+ pci_probe |= PCI_NO_IRQ_SCAN;
+ return NULL;
+ }
+#endif
+#ifdef CONFIG_PCI_DIRECT
+ else if (!strcmp(str, "conf1")) {
+ pci_probe = PCI_PROBE_CONF1 | PCI_NO_CHECKS;
+ return NULL;
+ }
+ else if (!strcmp(str, "conf2")) {
+ pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS;
+ return NULL;
+ }
+#endif
+ else if (!strcmp(str, "nopeer")) {
+ pci_probe |= PCI_NO_PEER_FIXUP;
+ return NULL;
+ } else if (!strcmp(str, "rom")) {
+ pci_probe |= PCI_ASSIGN_ROMS;
+ return NULL;
+ }
+ return str;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)