patch-2.3.15 linux/arch/i386/kernel/bios32.c

Next file: linux/arch/i386/kernel/i386_ksyms.c
Previous file: linux/arch/i386/kernel/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.14/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c
@@ -75,6 +75,8 @@
  * Jan 23, 1999 : More improvements to peer host bridge logic. i450NX fixup. [mj]
  *
  * Feb 8,  1999 : Added UM8886BF I/O address fixup. [mj]
+ *
+ * August  1999 : New resource management and configuration access stuff. [mj]
  */
 
 #include <linux/config.h>
@@ -85,6 +87,7 @@
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/smp_lock.h>
+#include <linux/irq.h>
 
 #include <asm/page.h>
 #include <asm/segment.h>
@@ -93,8 +96,6 @@
 #include <asm/smp.h>
 #include <asm/spinlock.h>
 
-#include <linux/irq.h>
-
 #undef DEBUG
 
 #ifdef DEBUG
@@ -103,72 +104,6 @@
 #define DBG(x...)
 #endif
 
-/*
- * This interrupt-safe spinlock protects all accesses to PCI
- * configuration space.
- */
-
-spinlock_t pci_lock = SPIN_LOCK_UNLOCKED;
-
-/*
- * Generic PCI access -- indirect calls according to detected HW.
- */
-
-struct pci_access {
-    int pci_present;
-    int (*read_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char *);
-    int (*read_config_word)(unsigned char, unsigned char, unsigned char, unsigned short *);
-    int (*read_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int *);
-    int (*write_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char);
-    int (*write_config_word)(unsigned char, unsigned char, unsigned char, unsigned short);
-    int (*write_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int);
-};
-
-static int pci_stub(void)
-{
-	return PCIBIOS_FUNC_NOT_SUPPORTED;
-}
-
-static struct pci_access pci_access_none = {
-	0,		   		/* No PCI present */
-	(void *) pci_stub,
-	(void *) pci_stub,
-	(void *) pci_stub,
-	(void *) pci_stub,
-	(void *) pci_stub,
-	(void *) pci_stub
-};
-
-static struct pci_access *access_pci = &pci_access_none;
-
-int pcibios_present(void)
-{
-	return access_pci->pci_present;
-}
-
-#define PCI_byte_BAD 0
-#define PCI_word_BAD (pos & 1)
-#define PCI_dword_BAD (pos & 3)
-
-#define PCI_STUB(rw,size,type) \
-int pcibios_##rw##_config_##size (u8 bus, u8 dfn, u8 pos, type value) \
-{									\
-	int res;							\
-	unsigned long flags;						\
-	if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER;	\
-	spin_lock_irqsave(&pci_lock, flags);				\
-	res = access_pci->rw##_config_##size(bus, dfn, pos, value);	\
-	spin_unlock_irqrestore(&pci_lock, flags);			\
-	return res;							\
-}
-
-PCI_STUB(read, byte, u8 *)
-PCI_STUB(read, word, u16 *)
-PCI_STUB(read, dword, u32 *)
-PCI_STUB(write, byte, u8)
-PCI_STUB(write, word, u16)
-PCI_STUB(write, dword, u32)
-
 #define PCI_PROBE_BIOS 1
 #define PCI_PROBE_CONF1 2
 #define PCI_PROBE_CONF2 4
@@ -176,6 +111,7 @@
 #define PCI_BIOS_SORT 0x200
 #define PCI_NO_CHECKS 0x400
 #define PCI_NO_PEER_FIXUP 0x800
+#define PCI_ASSIGN_ROMS 0x1000
 
 static unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2;
 
@@ -189,60 +125,53 @@
  * Functions for accessing PCI configuration space with type 1 accesses
  */
 
-#define CONFIG_CMD(bus, device_fn, where)   (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
+#define CONFIG_CMD(dev, where)   (0x80000000 | (dev->bus->number << 16) | (dev->devfn << 8) | (where & ~3))
 
-static int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn,
-			       unsigned char where, unsigned char *value)
+static int pci_conf1_read_config_byte(struct pci_dev *dev, int where, u8 *value)
 {
-    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    outl(CONFIG_CMD(dev,where), 0xCF8);
     *value = inb(0xCFC + (where&3));
     return PCIBIOS_SUCCESSFUL;
 }
 
-static int pci_conf1_read_config_word (unsigned char bus,
-    unsigned char device_fn, unsigned char where, unsigned short *value)
+static int pci_conf1_read_config_word(struct pci_dev *dev, int where, u16 *value)
 {
-    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
+    outl(CONFIG_CMD(dev,where), 0xCF8);    
     *value = inw(0xCFC + (where&2));
     return PCIBIOS_SUCCESSFUL;    
 }
 
-static int pci_conf1_read_config_dword (unsigned char bus, unsigned char device_fn, 
-				 unsigned char where, unsigned int *value)
+static int pci_conf1_read_config_dword(struct pci_dev *dev, int where, u32 *value)
 {
-    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    outl(CONFIG_CMD(dev,where), 0xCF8);
     *value = inl(0xCFC);
     return PCIBIOS_SUCCESSFUL;    
 }
 
-static int pci_conf1_write_config_byte (unsigned char bus, unsigned char device_fn, 
-				 unsigned char where, unsigned char value)
+static int pci_conf1_write_config_byte(struct pci_dev *dev, int where, u8 value)
 {
-    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
+    outl(CONFIG_CMD(dev,where), 0xCF8);    
     outb(value, 0xCFC + (where&3));
     return PCIBIOS_SUCCESSFUL;
 }
 
-static int pci_conf1_write_config_word (unsigned char bus, unsigned char device_fn, 
-				 unsigned char where, unsigned short value)
+static int pci_conf1_write_config_word(struct pci_dev *dev, int where, u16 value)
 {
-    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    outl(CONFIG_CMD(dev,where), 0xCF8);
     outw(value, 0xCFC + (where&2));
     return PCIBIOS_SUCCESSFUL;
 }
 
-static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device_fn, 
-				  unsigned char where, unsigned int value)
+static int pci_conf1_write_config_dword(struct pci_dev *dev, int where, u32 value)
 {
-    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    outl(CONFIG_CMD(dev,where), 0xCF8);
     outl(value, 0xCFC);
     return PCIBIOS_SUCCESSFUL;
 }
 
 #undef CONFIG_CMD
 
-static struct pci_access pci_direct_conf1 = {
-      1,
+static struct pci_ops pci_direct_conf1 = {
       pci_conf1_read_config_byte,
       pci_conf1_read_config_word,
       pci_conf1_read_config_dword,
@@ -255,86 +184,65 @@
  * 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 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(unsigned char bus, unsigned char device_fn, 
-			       unsigned char where, unsigned char *value)
+static int pci_conf2_read_config_byte(struct pci_dev *dev, int where, u8 *value)
 {
-    if (device_fn & 0x80)
-	return PCIBIOS_DEVICE_NOT_FOUND;
-    outb (FUNC(device_fn), 0xCF8);
-    outb (bus, 0xCFA);
-    *value = inb(IOADDR(device_fn,where));
+    SET(dev);
+    *value = inb(IOADDR(dev->devfn,where));
     outb (0, 0xCF8);
     return PCIBIOS_SUCCESSFUL;
 }
 
-static int pci_conf2_read_config_word (unsigned char bus, unsigned char device_fn, 
-				unsigned char where, unsigned short *value)
+static int pci_conf2_read_config_word(struct pci_dev *dev, int where, u16 *value)
 {
-    if (device_fn & 0x80)
-	return PCIBIOS_DEVICE_NOT_FOUND;
-    outb (FUNC(device_fn), 0xCF8);
-    outb (bus, 0xCFA);
-    *value = inw(IOADDR(device_fn,where));
+    SET(dev);
+    *value = inw(IOADDR(dev->devfn,where));
     outb (0, 0xCF8);
     return PCIBIOS_SUCCESSFUL;
 }
 
-static int pci_conf2_read_config_dword (unsigned char bus, unsigned char device_fn, 
-				 unsigned char where, unsigned int *value)
+static int pci_conf2_read_config_dword(struct pci_dev *dev, int where, u32 *value)
 {
-    if (device_fn & 0x80)
-	return PCIBIOS_DEVICE_NOT_FOUND;
-    outb (FUNC(device_fn), 0xCF8);
-    outb (bus, 0xCFA);
-    *value = inl (IOADDR(device_fn,where));    
+    SET(dev);
+    *value = inl (IOADDR(dev->devfn,where));    
     outb (0, 0xCF8);    
     return PCIBIOS_SUCCESSFUL;
 }
 
-static int pci_conf2_write_config_byte (unsigned char bus, unsigned char device_fn, 
-				 unsigned char where, unsigned char value)
+static int pci_conf2_write_config_byte(struct pci_dev *dev, int where, u8 value)
 {
-    if (device_fn & 0x80)
-	return PCIBIOS_DEVICE_NOT_FOUND;
-    outb (FUNC(device_fn), 0xCF8);
-    outb (bus, 0xCFA);
-    outb (value, IOADDR(device_fn,where));
+    SET(dev);
+    outb (value, IOADDR(dev->devfn,where));
     outb (0, 0xCF8);    
     return PCIBIOS_SUCCESSFUL;
 }
 
-static int pci_conf2_write_config_word (unsigned char bus, unsigned char device_fn, 
-				 unsigned char where, unsigned short value)
+static int pci_conf2_write_config_word(struct pci_dev *dev, int where, u16 value)
 {
-    if (device_fn & 0x80)
-	return PCIBIOS_DEVICE_NOT_FOUND;
-    outb (FUNC(device_fn), 0xCF8);
-    outb (bus, 0xCFA);
-    outw (value, IOADDR(device_fn,where));
+    SET(dev);
+    outw (value, IOADDR(dev->devfn,where));
     outb (0, 0xCF8);    
     return PCIBIOS_SUCCESSFUL;
 }
 
-static int pci_conf2_write_config_dword (unsigned char bus, unsigned char device_fn, 
-				  unsigned char where, unsigned int value)
+static int pci_conf2_write_config_dword(struct pci_dev *dev, int where, u32 value)
 {
-    if (device_fn & 0x80)
-	return PCIBIOS_DEVICE_NOT_FOUND;
-    outb (FUNC(device_fn), 0xCF8);
-    outb (bus, 0xCFA);
-    outl (value, IOADDR(device_fn,where));    
+    SET(dev);
+    outl (value, IOADDR(dev->devfn,where));    
     outb (0, 0xCF8);    
     return PCIBIOS_SUCCESSFUL;
 }
 
+#undef SET
 #undef IOADDR
 #undef FUNC
 
-static struct pci_access pci_direct_conf2 = {
-      1,
+static struct pci_ops pci_direct_conf2 = {
       pci_conf2_read_config_byte,
       pci_conf2_read_config_word,
       pci_conf2_read_config_dword,
@@ -353,9 +261,11 @@
  * 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.
  */
-int __init pci_sanity_check(struct pci_access *a)
+static int __init pci_sanity_check(struct pci_ops *o)
 {
-	u16 dfn, x;
+	u16 x;
+	struct pci_bus bus;		/* Fake bus and device */
+	struct pci_dev dev;
 
 #ifdef CONFIG_VISWS
 	return 1;       /* Lithium PCI Bridges are non-standard */
@@ -363,17 +273,19 @@
 
 	if (pci_probe & PCI_NO_CHECKS)
 		return 1;
-	for(dfn=0; dfn < 0x100; dfn++)
-		if ((!a->read_config_word(0, dfn, PCI_CLASS_DEVICE, &x) &&
+	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)) ||
-		    (!a->read_config_word(0, dfn, PCI_VENDOR_ID, &x) &&
+		    (!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_access * __init pci_check_direct(void)
+static struct pci_ops * __init pci_check_direct(void)
 {
 	unsigned int tmp;
 	unsigned long flags;
@@ -497,7 +409,7 @@
 	unsigned long entry;		/* %edx */
 	unsigned long flags;
 
-	spin_lock_irqsave(&pci_lock, flags);
+	__save_flags(flags); __cli();
 	__asm__("lcall (%%edi)"
 		: "=a" (return_code),
 		  "=b" (address),
@@ -506,7 +418,7 @@
 		: "0" (service),
 		  "1" (0),
 		  "D" (&bios32_indirect));
-	spin_unlock_irqrestore(&pci_lock, flags);
+	__restore_flags(flags);
 
 	switch (return_code) {
 		case 0:
@@ -603,7 +515,7 @@
 #endif
 
 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 index, unsigned char *bus, unsigned char *device_fn)
 {
 	unsigned short bx;
 	unsigned short ret;
@@ -624,11 +536,10 @@
 	return (int) (ret & 0xff00) >> 8;
 }
 
-static int pci_bios_read_config_byte(unsigned char bus,
-	unsigned char device_fn, unsigned char where, unsigned char *value)
+static int pci_bios_read_config_byte(struct pci_dev *dev, int where, u8 *value)
 {
 	unsigned long ret;
-	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long bx = (dev->bus->number << 8) | dev->devfn;
 
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
@@ -643,11 +554,10 @@
 	return (int) (ret & 0xff00) >> 8;
 }
 
-static int pci_bios_read_config_word (unsigned char bus,
-	unsigned char device_fn, unsigned char where, unsigned short *value)
+static int pci_bios_read_config_word(struct pci_dev *dev, int where, u16 *value)
 {
 	unsigned long ret;
-	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long bx = (dev->bus->number << 8) | dev->devfn;
 
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
@@ -662,11 +572,10 @@
 	return (int) (ret & 0xff00) >> 8;
 }
 
-static int pci_bios_read_config_dword (unsigned char bus,
-	unsigned char device_fn, unsigned char where, unsigned int *value)
+static int pci_bios_read_config_dword(struct pci_dev *dev, int where, u32 *value)
 {
 	unsigned long ret;
-	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long bx = (dev->bus->number << 8) | dev->devfn;
 
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
@@ -681,11 +590,10 @@
 	return (int) (ret & 0xff00) >> 8;
 }
 
-static int pci_bios_write_config_byte (unsigned char bus,
-	unsigned char device_fn, unsigned char where, unsigned char value)
+static int pci_bios_write_config_byte(struct pci_dev *dev, int where, u8 value)
 {
 	unsigned long ret;
-	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long bx = (dev->bus->number << 8) | dev->devfn;
 
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
@@ -700,11 +608,10 @@
 	return (int) (ret & 0xff00) >> 8;
 }
 
-static int pci_bios_write_config_word (unsigned char bus,
-	unsigned char device_fn, unsigned char where, unsigned short value)
+static int pci_bios_write_config_word(struct pci_dev *dev, int where, u16 value)
 {
 	unsigned long ret;
-	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long bx = (dev->bus->number << 8) | dev->devfn;
 
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
@@ -719,11 +626,10 @@
 	return (int) (ret & 0xff00) >> 8;
 }
 
-static int pci_bios_write_config_dword (unsigned char bus,
-	unsigned char device_fn, unsigned char where, unsigned int value)
+static int pci_bios_write_config_dword(struct pci_dev *dev, int where, u32 value)
 {
 	unsigned long ret;
-	unsigned long bx = (bus << 8) | device_fn;
+	unsigned long bx = (dev->bus->number << 8) | dev->devfn;
 
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
@@ -742,8 +648,7 @@
  * Function table for BIOS32 access
  */
 
-static struct pci_access pci_bios_access = {
-      1,
+static struct pci_ops pci_bios_access = {
       pci_bios_read_config_byte,
       pci_bios_read_config_word,
       pci_bios_read_config_dword,
@@ -756,7 +661,7 @@
  * Try to find PCI BIOS.
  */
 
-static struct pci_access * __init pci_find_bios(void)
+static struct pci_ops * __init pci_find_bios(void)
 {
 	union bios32 *check;
 	unsigned char sum;
@@ -855,26 +760,15 @@
 #endif
 
 /*
- * Several BIOS'es forget to assign addresses to I/O ranges.
- * We try to fix it here, expecting there are free addresses
- * starting with 0x5800. Ugly, but until we come with better
- * resource management, it's the only simple solution.
+ * Several BIOS'es forget to assign addresses to I/O ranges. Try to fix it.
  */
 
-static int pci_last_io_addr __initdata = 0x5800;
-
 static void __init pcibios_fixup_io_addr(struct pci_dev *dev, int idx)
 {
-	unsigned short cmd;
 	unsigned int reg = PCI_BASE_ADDRESS_0 + 4*idx;
-	unsigned int size, addr, try;
-	unsigned int bus = dev->bus->number;
-	unsigned int devfn = dev->devfn;
+	struct resource *r = &dev->resource[idx];
+	unsigned int size = r->end - r->start + 1;
 
-	if (!pci_last_io_addr) {
-		printk("PCI: Unassigned I/O space for %02x:%02x\n", bus, devfn);
-		return;
-	}
 	if (((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && idx < 4) ||
 	     (dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
 		/*
@@ -888,38 +782,53 @@
 		 */
 		return;
 	}
-	pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
-	pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd & ~PCI_COMMAND_IO);
-	pcibios_write_config_dword(bus, devfn, reg, ~0);
-	pcibios_read_config_dword(bus, devfn, reg, &size);
-	size = (~(size & PCI_BASE_ADDRESS_IO_MASK) & 0xffff) + 1;
-	addr = 0;
-	if (!size || size > 0x100)
-		printk("PCI: Unable to handle I/O allocation for %02x:%02x (%04x), tell <mj@ucw.cz>\n", bus, devfn, size);
-	else {
-		do {
-			addr = (pci_last_io_addr + size - 1) & ~(size-1);
-			pci_last_io_addr = addr + size;
-		} while (check_region(addr, size));
-		printk("PCI: Assigning I/O space %04x-%04x to device %02x:%02x\n", addr, addr+size-1, bus, devfn);
-		pcibios_write_config_dword(bus, devfn, reg, addr | PCI_BASE_ADDRESS_SPACE_IO);
-		pcibios_read_config_dword(bus, devfn, reg, &try);
-		if ((try & PCI_BASE_ADDRESS_IO_MASK) != addr) {
-			addr = 0;
-			printk("PCI: Address setup failed, got %04x\n", try);
-		} else {
-			struct resource *res = dev->resource + idx;
-			res->start = addr;
-			res->end = addr + size - 1;
-			res->flags |= PCI_BASE_ADDRESS_IO_MASK;
+	/*
+	 * We need to avoid collisions with `mirrored' VGA ports and other strange
+	 * ISA hardware, so we always want the addresses kilobyte aligned.
+	 */
+	if (!size || size > 256) {
+		printk(KERN_ERR "PCI: Cannot assign I/O space to device %s, %d bytes are too much.\n", dev->name, size);
+		return;
+	} else {
+		u32 try;
+
+		if (allocate_resource(&ioport_resource, r, size, 0x1000, ~0, 1024)) {
+			printk(KERN_ERR "PCI: Unable to find free %d bytes of I/O space for device %s.\n", size, dev->name);
+			return;
+		}
+		printk("PCI: Assigning I/O space %04lx-%04lx to device %s\n", r->start, r->end, dev->name);
+		pci_write_config_dword(dev, reg, r->start | PCI_BASE_ADDRESS_SPACE_IO);
+		pci_read_config_dword(dev, reg, &try);
+		if ((try & PCI_BASE_ADDRESS_IO_MASK) != r->start) {
+			r->start = 0;
+			pci_write_config_dword(dev, reg, 0);
+			printk(KERN_ERR "PCI: I/O address setup failed, got %04x\n", try);
 		}
 	}
-	if (!addr) {
-		pcibios_write_config_dword(bus, devfn, reg, 0);
-		dev->resource[idx].start = 0;
-		dev->resource[idx].flags = 0;
+}
+
+/*
+ * Assign address to expansion ROM. This is a highly experimental feature
+ * and you must enable it by "pci=rom". It's even not guaranteed to work
+ * with all cards since the PCI specs allow address decoders to be shared
+ * between the ROM space and one of the standard regions (sigh!).
+ */
+static void __init pcibios_fixup_rom_addr(struct pci_dev *dev)
+{
+	int reg = (dev->hdr_type == 1) ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS;
+	struct resource *r = &dev->resource[PCI_ROM_RESOURCE];
+	unsigned long rom_size = r->end - r->start + 1;
+
+	if (allocate_resource(&iomem_resource, r, rom_size, 0xf0000000, ~0, rom_size) < 0) {
+		printk(KERN_ERR "PCI: Unable to find free space for expansion ROM of device %s (0x%lx bytes)\n",
+		       dev->name, rom_size);
+		r->start = 0;
+		r->end = rom_size - 1;
+	} else {
+		DBG("PCI: Assigned address %08lx to expansion ROM of %s (0x%lx bytes)\n", r->start, dev->name, rom_size);
+		pci_write_config_dword(dev, reg, r->start | PCI_ROM_ADDRESS_ENABLE);
+		r->flags |= PCI_ROM_ADDRESS_ENABLE;
 	}
-	pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
 }
 
 /*
@@ -934,18 +843,25 @@
 	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 &&
-			    !memcmp(e->resource, d->resource, sizeof(e->resource)))
-				break;
+		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;
 	}
@@ -971,12 +887,13 @@
  */
 static void __init pcibios_fixup_peer_bridges(void)
 {
-	struct pci_bus *b = &pci_root;
-	int i, n, cnt=-1;
+	struct pci_bus *b = pci_root;
+	int n, cnt=-1;
 	struct pci_dev *d;
+	struct pci_ops *ops = pci_root->ops;
 
 #ifdef CONFIG_VISWS
-	pci_scan_peer_bridge(1);
+	pci_scan_bus(1, ops, NULL);
 	return;
 #endif
 
@@ -986,7 +903,7 @@
 	 * since it reads bogus values for non-existent busses and
 	 * chipsets supporting multiple primary busses use conf1 anyway.
 	 */
-	if (access_pci == &pci_direct_conf2)
+	if (ops == &pci_direct_conf2)
 		return;
 #endif
 
@@ -997,26 +914,31 @@
 	while (n <= 0xff) {
 		int found = 0;
 		u16 l;
-		for(i=0; i<256; i += 8)
-			if (!pcibios_read_config_word(n, i, PCI_VENDOR_ID, &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;
-					pcibios_read_config_word(n, i, PCI_DEVICE_ID, &d);
-					DBG("BIOS test for %02x:%02x (%04x:%04x)\n", n, i, l, 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 != i))
+					       (bios_bus != n || bios_dfn != dev.devfn))
 						idx++;
 					if (err)
 						break;
 				}
 #endif
-				DBG("Found device at %02x:%02x\n", n, i);
+				DBG("Found device at %02x:%02x\n", n, dev.devfn);
 				found++;
-				if (!pcibios_read_config_word(n, i, PCI_CLASS_DEVICE, &l) &&
+				if (!pci_read_config_word(&dev, PCI_CLASS_DEVICE, &l) &&
 				    l == PCI_CLASS_BRIDGE_HOST)
 					cnt++;
 			}
@@ -1024,7 +946,7 @@
 			break;
 		if (found) {
 			printk("PCI: Discovered primary peer bus %02x\n", n);
-			b = pci_scan_peer_bridge(n);
+			b = pci_scan_bus(n, ops, NULL);
 			n = b->subordinate;
 		}
 		n++;
@@ -1042,6 +964,7 @@
 	 */
 	int pxb, reg;
 	u8 busno, suba, subb;
+	printk("PCI: Searching for i450NX host bridges on %s\n", d->name);
 	reg = 0xd0;
 	for(pxb=0; pxb<2; pxb++) {
 		pci_read_config_byte(d, reg++, &busno);
@@ -1049,13 +972,40 @@
 		pci_read_config_byte(d, reg++, &subb);
 		DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb);
 		if (busno)
-			pci_scan_peer_bridge(busno);	/* Bus A */
+			pci_scan_bus(busno, pci_root->ops, NULL);	/* Bus A */
 		if (suba < subb)
-			pci_scan_peer_bridge(suba+1);	/* Bus B */
+			pci_scan_bus(suba+1, pci_root->ops, NULL);	/* Bus B */
 	}
 	pci_probe |= PCI_NO_PEER_FIXUP;
 }
 
+static void __init pci_fixup_i440bx(struct pci_dev *d)
+{
+#if 0							    /* Temporarily disabled   FIXME */
+	/*
+	 * i440BX/ZX -- Occupy the AGP bridge windows.
+	 */
+	u16 a, b;
+	u8 u, v;
+	pci_read_config_byte(d, 0x1c, &u);
+	pci_read_config_byte(d, 0x1d, &v);
+	if (v >= u) {
+		a = u<<8;
+		b = ((v-u)<<8) + 0x100;
+		occupy_region(a, a+b, b, 1, &d->dev);
+	}
+	for (u = 0; u < 2; u++) {
+		pci_read_config_word(d, 0x20+(u*4), &a);
+		pci_read_config_word(d, 0x22+(u*4), &b);
+		if (b >= a) {
+			u32 m = a<<16;
+			u32 n = ((b-a)<<16) + 0x100000;
+			occupy_mem_region(m, m+n, n, 1, &d->dev);
+		}
+	}
+#endif
+}
+
 static void __init pci_fixup_umc_ide(struct pci_dev *d)
 {
 	/*
@@ -1064,34 +1014,36 @@
 	 */
 	int i;
 
+	printk("PCI: Fixing base address flags for device %s\n", d->name);
 	for(i=0; i<4; i++)
 		d->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO;
 }
 
-struct dev_ex {
-	u16 vendor, device;
-	void (*handler)(struct pci_dev *);
-	char *comment;
+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_INTEL,	PCI_DEVICE_ID_INTEL_82443BX_1,	pci_fixup_i440bx },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_UMC,	PCI_DEVICE_ID_UMC_UM8886BF,	pci_fixup_umc_ide },
+	{ 0 }
 };
 
-static struct dev_ex __initdata dev_ex_table[] = {
- { PCI_VENDOR_ID_INTEL,		PCI_DEVICE_ID_INTEL_82451NX,	pci_fixup_i450nx, 	"Scanning peer host bridges" },
- { PCI_VENDOR_ID_UMC,		PCI_DEVICE_ID_UMC_UM8886BF,	pci_fixup_umc_ide,	"Working around UM8886BF bugs" }
-};
+/*
+ * Allocate resources for all PCI devices. We need to do that before
+ * we try to fix up anything.
+ */
 
-static void __init pcibios_scan_buglist(struct pci_bus *b)
+static void __init pcibios_claim_resources(void)
 {
-	struct pci_dev *d;
-	int i;
+	struct pci_dev *dev;
+	int idx;
 
-	for(d=b->devices; d; d=d->sibling)
-		for(i=0; i<sizeof(dev_ex_table)/sizeof(dev_ex_table[0]); i++) {
-			struct dev_ex *e = &dev_ex_table[i];
-			if (e->vendor == d->vendor && e->device == d->device) {
-				printk("PCI: %02x:%02x [%04x/%04x]: %s\n",
-					b->number, d->devfn, d->vendor, d->device, e->comment);
-				e->handler(d);
-			}
+	for (dev=pci_devices; dev; dev=dev->next)
+		for (idx = 0; idx < PCI_NUM_RESOURCES; idx++) {
+			struct resource *r = &dev->resource[idx];
+			if (!r->start)
+				continue;
+			if (request_resource((r->flags & PCI_BASE_ADDRESS_SPACE_IO) ? &ioport_resource : &iomem_resource, r) < 0)
+				printk(KERN_ERR "PCI: Address space collision on region %d of device %s\n", idx, dev->name);
+				/* We probably should disable the region, shouldn't we? */
 		}
 }
 
@@ -1117,14 +1069,12 @@
 		 */
 		has_io = has_mem = 0;
 		for(i=0; i<6; i++) {
-			struct resource *res = dev->resource + i;
-			unsigned long a = res->flags;
-			if (a & PCI_BASE_ADDRESS_SPACE_IO) {
-				unsigned long addr = res->start;
+			struct resource *r = &dev->resource[i];
+			if (r->flags & PCI_BASE_ADDRESS_SPACE_IO) {
 				has_io = 1;
-				if (!addr || addr == PCI_BASE_ADDRESS_IO_MASK)
+				if (!r->start || r->start == PCI_BASE_ADDRESS_IO_MASK)
 					pcibios_fixup_io_addr(dev, i);
-			} else if (a & PCI_BASE_ADDRESS_MEM_MASK)
+			} else if (r->start)
 				has_mem = 1;
 		}
 		/*
@@ -1139,18 +1089,21 @@
 		    ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE)) {
 			pci_read_config_word(dev, PCI_COMMAND, &cmd);
 			if (has_io && !(cmd & PCI_COMMAND_IO)) {
-				printk("PCI: Enabling I/O for device %02x:%02x\n",
-					dev->bus->number, dev->devfn);
+				printk("PCI: Enabling I/O for device %s\n", dev->name);
 				cmd |= PCI_COMMAND_IO;
 				pci_write_config_word(dev, PCI_COMMAND, cmd);
 			}
 			if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) {
-				printk("PCI: Enabling memory for device %02x:%02x\n",
-					dev->bus->number, dev->devfn);
+				printk("PCI: Enabling memory for device %s\n", dev->name);
 				cmd |= PCI_COMMAND_MEMORY;
 				pci_write_config_word(dev, PCI_COMMAND, cmd);
 			}
 		}
+		/*
+		 * Assign address to expansion ROM if requested.
+		 */
+		if ((pci_probe & PCI_ASSIGN_ROMS) && dev->resource[PCI_ROM_RESOURCE].end)
+			pcibios_fixup_rom_addr(dev);
 #if defined(CONFIG_X86_IO_APIC)
 		/*
 		 * Recalculate IRQ numbers if we use the I/O APIC
@@ -1191,38 +1144,27 @@
 }
 
 /*
- * Arch-dependent fixups.
+ *  Called after each bus is probed, but before its children
+ *  are examined.
  */
 
-void __init pcibios_fixup(void)
-{
-	if (!(pci_probe & PCI_NO_PEER_FIXUP))
-		pcibios_fixup_peer_bridges();
-	pcibios_fixup_devices();
-
-#ifdef CONFIG_PCI_BIOS
-	if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT))
-		pcibios_sort();
-#endif
-}
-
 void __init pcibios_fixup_bus(struct pci_bus *b)
 {
 	pcibios_fixup_ghosts(b);
-	pcibios_scan_buglist(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 in 2.3.
+ * compatible with 2.0.X. This should go away some day.
  */
 
 void __init pcibios_init(void)
 {
-	struct pci_access *bios = NULL;
-	struct pci_access *dir = NULL;
+	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()))) {
@@ -1235,9 +1177,26 @@
 		dir = pci_check_direct();
 #endif
 	if (dir)
-		access_pci = dir;
+		ops = dir;
 	else if (bios)
-		access_pci = bios;
+		ops = bios;
+	else {
+		printk("PCI: No PCI bus detected\n");
+		return;
+	}
+
+	printk("PCI: Probing PCI hardware\n");
+	pci_scan_bus(0, ops, NULL);
+
+	if (!(pci_probe & PCI_NO_PEER_FIXUP))
+		pcibios_fixup_peer_bridges();
+	pcibios_claim_resources();
+	pcibios_fixup_devices();
+
+#ifdef CONFIG_PCI_BIOS
+	if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT))
+		pcibios_sort();
+#endif
 }
 
 char * __init pcibios_setup(char *str)
@@ -1245,13 +1204,6 @@
 	if (!strcmp(str, "off")) {
 		pci_probe = 0;
 		return NULL;
-	} else if (!strncmp(str, "io=", 3)) {
-		char *p;
-		unsigned int x = simple_strtoul(str+3, &p, 16);
-		if (p && *p)
-			return str;
-		pci_last_io_addr = x;
-		return NULL;
 	}
 #ifdef CONFIG_PCI_BIOS
 	else if (!strcmp(str, "bios")) {
@@ -1277,6 +1229,9 @@
 #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)