patch-2.1.97 linux/arch/sparc64/kernel/psycho.c

Next file: linux/arch/sparc64/kernel/setup.c
Previous file: linux/arch/sparc64/kernel/process.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.96/linux/arch/sparc64/kernel/psycho.c linux/arch/sparc64/kernel/psycho.c
@@ -1,4 +1,4 @@
-/* $Id: psycho.c,v 1.31 1998/01/10 18:26:15 ecd Exp $
+/* $Id: psycho.c,v 1.50 1998/04/10 12:29:47 ecd Exp $
  * psycho.c: Ultra/AX U2P PCI controller support.
  *
  * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu)
@@ -8,6 +8,7 @@
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
+#include <linux/init.h>
 
 #include <asm/ebus.h>
 #include <asm/sbus.h> /* for sanity check... */
@@ -15,6 +16,7 @@
 #undef PROM_DEBUG
 #undef FIXUP_REGS_DEBUG
 #undef FIXUP_IRQ_DEBUG
+#undef FIXUP_VMA_DEBUG
 
 #ifdef PROM_DEBUG
 #define dprintf	prom_printf
@@ -22,6 +24,9 @@
 #define dprintf printk
 #endif
 
+unsigned long pci_dvma_offset = 0x00000000UL;
+unsigned long pci_dvma_mask   = 0xffffffffUL;
+
 #ifndef CONFIG_PCI
 
 int pcibios_present(void)
@@ -51,12 +56,12 @@
 
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
-#include <linux/bios32.h>
 #include <linux/pci.h>
 
 #include <asm/io.h>
 #include <asm/oplib.h>
 #include <asm/pbm.h>
+#include <asm/apb.h>
 #include <asm/uaccess.h>
 
 struct linux_psycho *psycho_root = NULL;
@@ -95,8 +100,9 @@
 		~(sizeof(unsigned long) - 1));
 }
 
-static unsigned long psycho_iommu_init(struct linux_psycho *psycho,
-				       unsigned long memory_start)
+__initfunc(static unsigned long psycho_iommu_init(struct linux_psycho *psycho,
+						  int tsbsize,
+						  unsigned long memory_start))
 {
 	unsigned long tsbbase = PAGE_ALIGN(memory_start);
 	unsigned long control, i;
@@ -114,10 +120,10 @@
 	control &= ~(IOMMU_CTRL_DENAB);
 	psycho->psycho_regs->iommu_control = control;
 
-	memory_start = (tsbbase + ((32 * 1024) * 8));
+	memory_start = (tsbbase + ((tsbsize * 1024) * 8));
 	iopte = (unsigned long *)tsbbase;
 
-	for(i = 0; i < (32 * 1024); i++) {
+	for(i = 0; i < (tsbsize * 1024); i++) {
 		*iopte = (IOPTE_VALID | IOPTE_64K |
 			  IOPTE_CACHE | IOPTE_WRITE);
 		*iopte |= (i << 16);
@@ -128,15 +134,215 @@
 
 	control = psycho->psycho_regs->iommu_control;
 	control &= ~(IOMMU_CTRL_TSBSZ);
-	control |= (IOMMU_TSBSZ_32K | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB);
+	control |= (IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB);
+	switch(tsbsize) {
+	case 8:
+		pci_dvma_mask   = 0x1fffffffUL;
+		control |= IOMMU_TSBSZ_8K;
+		break;
+	case 16:
+		pci_dvma_mask   = 0x3fffffffUL;
+		control |= IOMMU_TSBSZ_16K;
+		break;
+	case 32:
+		pci_dvma_mask   = 0x7fffffffUL;
+		control |= IOMMU_TSBSZ_32K;
+		break;
+	default:
+		prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize);
+		prom_halt();
+		break;
+	}
 	psycho->psycho_regs->iommu_control = control;
 
 	return memory_start;
 }
 
 extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm);
+extern void prom_pbm_intmap_init(int node, struct linux_pbm_info *pbm);
+
+/*
+ * Poor man's PCI...
+ */
+__initfunc(unsigned long sabre_init(int pnode, unsigned long memory_start))
+{
+	struct linux_prom64_registers pr_regs[2];
+	struct linux_psycho *sabre;
+	unsigned long ctrl;
+	int tsbsize, node, err;
+	u32 busrange[2];
+	u32 vdma[2];
+	u32 portid;
+	int bus;
+
+	sabre = (struct linux_psycho *)memory_start;
+	memory_start = long_align(memory_start + sizeof(struct linux_psycho));
+
+	portid = prom_getintdefault(pnode, "upa-portid", 0xff);
+
+	memset(sabre, 0, sizeof(*sabre));
+
+	sabre->next = psycho_root;
+	psycho_root = sabre;
+
+	sabre->upa_portid = portid;
+	sabre->index = linux_num_psycho++;
+
+	/*
+	 * Map in SABRE register set and report the presence of this SABRE.
+	 */
+	err = prom_getproperty(pnode, "reg",
+			       (char *)&pr_regs[0], sizeof(pr_regs));
+	if(err == 0 || err == -1) {
+		prom_printf("SABRE: Error, cannot get U2P registers "
+			    "from PROM.\n");
+		prom_halt();
+	}
+
+	/*
+	 * First REG in property is base of entire SABRE register space.
+	 */
+	sabre->psycho_regs =
+			sparc_alloc_io((pr_regs[0].phys_addr & 0xffffffff),
+				       NULL, sizeof(struct psycho_regs),
+				       "SABRE Registers",
+				       (pr_regs[0].phys_addr >> 32), 0);
+	if(sabre->psycho_regs == NULL) {
+		prom_printf("SABRE: Error, cannot map SABRE main registers.\n");
+		prom_halt();
+	}
+
+	printk("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs);
+#ifdef PROM_DEBUG
+	dprintf("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs);
+#endif
+
+	ctrl = sabre->psycho_regs->pci_a_control;
+	ctrl = (1UL << 36) | (1UL << 34) | (1UL << 21) | (1UL << 8) | 0x0fUL;
+	sabre->psycho_regs->pci_a_control = ctrl;
+
+	/* Now map in PCI config space for entire SABRE. */
+	sabre->pci_config_space =
+			sparc_alloc_io(((pr_regs[0].phys_addr & 0xffffffff)
+								+ 0x01000000),
+				       NULL, 0x01000000,
+				       "PCI Config Space",
+				       (pr_regs[0].phys_addr >> 32), 0);
+	if(sabre->pci_config_space == NULL) {
+		prom_printf("SABRE: Error, cannot map PCI config space.\n");
+		prom_halt();
+	}
+
+	/* Report some more info. */
+	printk("SABRE: PCI config space at %p\n", sabre->pci_config_space);
+#ifdef PROM_DEBUG
+	dprintf("SABRE: PCI config space at %p\n", sabre->pci_config_space);
+#endif
+
+	err = prom_getproperty(pnode, "virtual-dma",
+			       (char *)&vdma[0], sizeof(vdma));
+	if(err == 0 || err == -1) {
+		prom_printf("SABRE: Error, cannot get virtual-dma property "
+			    "from PROM.\n");
+		prom_halt();
+	}
+
+	switch(vdma[1]) {
+		case 0x20000000:
+			tsbsize = 8;
+			break;
+		case 0x40000000:
+			tsbsize = 16;
+			break;
+		case 0x80000000:
+			tsbsize = 32;
+			break;
+		default:
+			prom_printf("SABRE: strange virtual-dma size.\n");
+			prom_halt();
+	}
+
+	memory_start = psycho_iommu_init(sabre, tsbsize, memory_start);
+	pci_dvma_offset = vdma[0];
+
+	printk("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]);
+#ifdef PROM_DEBUG
+	dprintf("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]);
+#endif
+
+	err = prom_getproperty(pnode, "bus-range",
+				       (char *)&busrange[0], sizeof(busrange));
+	if(err == 0 || err == -1) {
+		prom_printf("SIMBA: Error, cannot get PCI bus-range "
+			    " from PROM.\n");
+		prom_halt();
+	}
+
+	sabre->pci_first_busno = busrange[0];
+	sabre->pci_last_busno = busrange[1];
+	sabre->pci_bus = &pci_root;
+
+	/*
+	 * Handle config space reads through any Simba on APB.
+	 */
+	for (bus = sabre->pci_first_busno; bus <= sabre->pci_last_busno; bus++)
+		bus2pbm[bus] = &sabre->pbm_A;
+
+	/*
+	 * Look for APB underneath.
+	 */
+	node = prom_getchild(pnode);
+	while ((node = prom_searchsiblings(node, "pci"))) {
+		struct linux_pbm_info *pbm;
+		char namebuf[128];
+
+		err = prom_getproperty(node, "model", namebuf, sizeof(namebuf));
+		if ((err <= 0) || strncmp(namebuf, "SUNW,simba", err))
+			goto next_pci;
+
+		err = prom_getproperty(node, "bus-range",
+				       (char *)&busrange[0], sizeof(busrange));
+		if(err == 0 || err == -1) {
+			prom_printf("SIMBA: Error, cannot get PCI bus-range "
+				    " from PROM.\n");
+			prom_halt();
+		}
+
+		if (busrange[0] == 1)
+			pbm = &sabre->pbm_B;
+		else
+			pbm = &sabre->pbm_A;
+
+		pbm->parent = sabre;
+		pbm->IO_assignments = NULL;
+		pbm->MEM_assignments = NULL;
+		pbm->prom_node = node;
+
+		prom_getstring(node, "name", namebuf, sizeof(namebuf));
+		strcpy(pbm->prom_name, namebuf);
+
+		/* Now the ranges. */
+		prom_pbm_ranges_init(pnode, pbm);
+		prom_pbm_intmap_init(node, pbm);
+
+		pbm->pci_first_busno = busrange[0];
+		pbm->pci_last_busno = busrange[1];
+		memset(&pbm->pci_bus, 0, sizeof(struct pci_bus));
+
+		for (bus = pbm->pci_first_busno;
+		     bus <= pbm->pci_last_busno; bus++)
+			bus2pbm[bus] = pbm;
+
+	next_pci:
+		node = prom_getsibling(node);
+		if (!node)
+			break;
+	}
 
-unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
+	return memory_start;
+}
+
+__initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end))
 {
 	struct linux_prom64_registers pr_regs[3];
 	struct linux_psycho *psycho;
@@ -144,9 +350,9 @@
 	u32 portid;
 	int node;
 
-	printk("PSYCHO: Probing for controllers.\n");
+	printk("PCI: Probing for controllers.\n");
 #ifdef PROM_DEBUG
-	dprintf("PSYCHO: Probing for controllers.\n");
+	dprintf("PCI: Probing for controllers.\n");
 #endif
 
 	memory_start = long_align(memory_start);
@@ -157,6 +363,12 @@
 		u32 busrange[2];
 		int err, is_pbm_a;
 
+		err = prom_getproperty(node, "model", namebuf, sizeof(namebuf));
+		if ((err > 0) && !strncmp(namebuf, "SUNW,sabre", err)) {
+			memory_start = sabre_init(node, memory_start);
+			goto next_pci;
+		}
+
 		psycho = (struct linux_psycho *)memory_start;
 
 		portid = prom_getintdefault(node, "upa-portid", 0xff);
@@ -200,34 +412,30 @@
 		 * Third REG in property is base of entire PSYCHO
 		 * register space.
 		 */
-		psycho->psycho_regs = sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff),
-						     NULL, sizeof(struct psycho_regs),
-						     "PSYCHO Registers",
-						     (pr_regs[2].phys_addr >> 32), 0);
+		psycho->psycho_regs =
+			sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff),
+				       NULL, sizeof(struct psycho_regs),
+				       "PSYCHO Registers",
+				       (pr_regs[2].phys_addr >> 32), 0);
 		if(psycho->psycho_regs == NULL) {
 			prom_printf("PSYCHO: Error, cannot map PSYCHO "
 				    "main registers.\n");
 			prom_halt();
 		}
 
-		printk("PSYCHO: Found controller, main regs at %p\n",
+		printk("PCI: Found PSYCHO, main regs at %p\n",
 		       psycho->psycho_regs);
 #ifdef PROM_DEBUG
-		dprintf("PSYCHO: Found controller, main regs at %p\n",
+		dprintf("PCI: Found PSYCHO, main regs at %p\n",
 			psycho->psycho_regs);
 #endif
 
 		psycho->psycho_regs->irq_retry = 0xff;
 
-#if 0
-		psycho->psycho_regs->ecc_control |= 1;
-		psycho->psycho_regs->sbuf_a_control = 0;
-		psycho->psycho_regs->sbuf_b_control = 0;
-#endif
-
 		/* Now map in PCI config space for entire PSYCHO. */
 		psycho->pci_config_space =
-			sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)+0x01000000),
+			sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)
+								+ 0x01000000),
 				       NULL, 0x01000000,
 				       "PCI Config Space",
 				       (pr_regs[2].phys_addr >> 32), 0);
@@ -244,7 +452,8 @@
 			psycho->pci_config_space);
 #endif
 
-		memory_start = psycho_iommu_init(psycho, memory_start);
+		memory_start = psycho_iommu_init(psycho, 32, memory_start);
+		pci_dvma_offset = 0x80000000UL;
 
 		is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000);
 
@@ -268,6 +477,7 @@
 
 		/* Now the ranges. */
 		prom_pbm_ranges_init(node, pbm);
+		prom_pbm_intmap_init(node, pbm);
 
 		/* Finally grab the pci bus root array for this pbm after
 		 * having found the bus range existing under it.
@@ -282,6 +492,7 @@
 		pbm->pci_last_busno = busrange[1];
 		memset(&pbm->pci_bus, 0, sizeof(struct pci_bus));
 
+	next_pci:
 		node = prom_getsibling(node);
 		if(!node)
 			break;
@@ -308,54 +519,18 @@
 	return psycho_root != NULL;
 }
 
-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
-			 unsigned short index, unsigned char *bus,
-			 unsigned char *devfn)
-{
-	unsigned int curr = 0;
-	struct pci_dev *dev;
-
-	for (dev = pci_devices; dev; dev = dev->next) {
-		if (dev->vendor == vendor && dev->device == device_id) {
-			if (curr == index) {
-				*devfn = dev->devfn;
-				*bus = dev->bus->number;
-				return PCIBIOS_SUCCESSFUL;
-			}
-			++curr;
-		}
-	}
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-int pcibios_find_class (unsigned int class_code, unsigned short index,
-			unsigned char *bus, unsigned char *devfn)
-{
-	unsigned int curr = 0;
-	struct pci_dev *dev;
-
-	for (dev = pci_devices; dev; dev = dev->next) {
-		if (dev->class == class_code) {
-			if (curr == index) {
-				*devfn = dev->devfn;
-				*bus = dev->bus->number;
-				return PCIBIOS_SUCCESSFUL;
-			}
-			++curr;
-		}
-	}
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
 static inline struct pci_vma *pci_find_vma(struct linux_pbm_info *pbm,
 					   unsigned long start,
-					   int io)
+					   unsigned int offset, int io)
 {
 	struct pci_vma *vp = (io ? pbm->IO_assignments : pbm->MEM_assignments);
 
-	while(vp) {
-		if(vp->end > start)
+	while (vp) {
+		if (offset && (vp->offset != offset))
+			goto next;
+		if (vp->end >= start)
 			break;
+	next:
 		vp = vp->next;
 	}
 	return vp;
@@ -391,7 +566,7 @@
 		/* Check for programming errors. */
 		if(vp &&
 		   ((vp->start >= new->start && vp->start < new->end) ||
-		    ((vp->end - 1) >= new->start && (vp->end - 1) < new->end))) {
+		    (vp->end >= new->start && vp->end < new->end))) {
 			prom_printf("pci_add_vma: Wheee, overlapping %s PCI vma's\n",
 				    io ? "IO" : "MEM");
 			prom_printf("pci_add_vma: vp[%016lx:%016lx] "
@@ -414,7 +589,7 @@
 	pci_alloc_arena = NULL;
 }
 
-static void *pci_init_alloc(int size)
+__initfunc(static void *pci_init_alloc(int size))
 {
 	unsigned long start = long_align(*pci_alloc_arena);
 	void *mp = (void *)start;
@@ -439,8 +614,8 @@
 }
 
 
-static void
-pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)
+__initfunc(static void
+pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus))
 {
 	unsigned int devfn, l, class;
 	unsigned char hdr_type = 0;
@@ -487,7 +662,7 @@
 	}
 }
 
-static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus)
+__initfunc(static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus))
 {
 	unsigned int nbus;
 
@@ -513,7 +688,79 @@
 }
 
 
-static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart)
+__initfunc(static void apb_init(struct linux_psycho *sabre))
+{
+	struct pci_dev *pdev;
+	unsigned short stmp;
+	unsigned int itmp;
+
+	for (pdev = sabre->pci_bus->devices; pdev; pdev = pdev->sibling) {
+		if (pdev->vendor == PCI_VENDOR_ID_SUN &&
+		    pdev->device == PCI_DEVICE_ID_SUN_SIMBA) {
+
+			pci_read_config_word(pdev, PCI_COMMAND, &stmp);
+			stmp |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+				PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY |
+				PCI_COMMAND_IO;
+			pci_write_config_word(pdev, PCI_COMMAND, stmp);
+
+			pci_write_config_word(pdev, PCI_STATUS, 0xffff);
+			pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff);
+
+			pci_read_config_word(pdev, PCI_BRIDGE_CONTROL, &stmp);
+			stmp = PCI_BRIDGE_CTL_MASTER_ABORT |
+			       PCI_BRIDGE_CTL_SERR |
+			       PCI_BRIDGE_CTL_PARITY;
+			pci_write_config_word(pdev, PCI_BRIDGE_CONTROL, stmp);
+
+			pci_read_config_dword(pdev, APB_PCI_CONTROL_HIGH, &itmp);
+			itmp = APB_PCI_CTL_HIGH_SERR |
+			       APB_PCI_CTL_HIGH_ARBITER_EN;
+			pci_write_config_dword(pdev, APB_PCI_CONTROL_HIGH, itmp);
+
+			pci_read_config_dword(pdev, APB_PCI_CONTROL_LOW, &itmp);
+			itmp = APB_PCI_CTL_LOW_ARB_PARK |
+			       APB_PCI_CTL_LOW_ERRINT_EN | 0x0f;
+			pci_write_config_dword(pdev, APB_PCI_CONTROL_LOW, itmp);
+
+			/*
+			 * Setup Registers for Guaranteed Completion.
+			 */
+			pci_write_config_byte(pdev, APB_PRIMARY_MASTER_RETRY_LIMIT, 0);
+			pci_write_config_byte(pdev, APB_SECONDARY_MASTER_RETRY_LIMIT, 0);
+			pci_write_config_byte(pdev, APB_PIO_TARGET_RETRY_LIMIT, 0x80);
+			pci_write_config_byte(pdev, APB_PIO_TARGET_LATENCY_TIMER, 0);
+			pci_write_config_byte(pdev, APB_DMA_TARGET_RETRY_LIMIT, 0x80);
+			pci_write_config_byte(pdev, APB_DMA_TARGET_LATENCY_TIMER, 0);
+		}
+	}
+}
+
+__initfunc(static void sabre_probe(struct linux_psycho *sabre,
+				   unsigned long *mstart))
+{
+	struct pci_bus *pbus = sabre->pci_bus;
+	static unsigned char busno = 0;
+
+	pbus->number = pbus->secondary = busno;
+	pbus->sysdata = sabre;
+
+	pbus->subordinate = pci_scan_bus(pbus, mstart);
+	busno = pbus->subordinate + 1;
+
+	for(pbus = pbus->children; pbus; pbus = pbus->next) {
+		if (pbus->number == sabre->pbm_A.pci_first_busno)
+			memcpy(&sabre->pbm_A.pci_bus, pbus, sizeof(*pbus));
+		if (pbus->number == sabre->pbm_B.pci_first_busno)
+			memcpy(&sabre->pbm_B.pci_bus, pbus, sizeof(*pbus));
+	}
+
+	apb_init(sabre);
+}
+
+
+__initfunc(static void pbm_probe(struct linux_pbm_info *pbm,
+				 unsigned long *mstart))
 {
 	static struct pci_bus *pchain = NULL;
 	struct pci_bus *pbus = &pbm->pci_bus;
@@ -552,9 +799,9 @@
 	}
 }
 
-static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
-				     struct pci_dev *pdev,
-				     int node)
+__initfunc(static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
+						struct pci_dev *pdev,
+						int node))
 {
 	struct linux_prom_pci_registers pregs[PROMREG_MAX];
 	int err;
@@ -583,7 +830,7 @@
 	return 0;
 }
 
-static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev)
+__initfunc(static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev))
 {
 	struct pcidev_cookie *pcp;
 	int node = prom_getchild(pbm->prom_node);
@@ -597,24 +844,37 @@
 	pdev->sysdata = pcp;
 }
 
-static void fill_in_pbm_cookies(struct linux_pbm_info *pbm)
+__initfunc(static void fill_in_pbm_cookies(struct pci_bus *pbus,
+					   struct linux_pbm_info *pbm))
 {
-	struct pci_bus *pbtmp, *pbus = &pbm->pci_bus;
 	struct pci_dev *pdev;
 
-	for(pbtmp = pbus->children; pbtmp; pbtmp = pbtmp->children)
-		pbtmp->sysdata = pbm;
+	pbus->sysdata = pbm;
+
+	for(pdev = pbus->devices; pdev; pdev = pdev->sibling)
+		pdev_cookie_fillin(pbm, pdev);
+
+	for(pbus = pbus->children; pbus; pbus = pbus->next)
+		fill_in_pbm_cookies(pbus, pbm);
+}
+
+__initfunc(static void sabre_cookie_fillin(struct linux_psycho *sabre))
+{
+	struct pci_bus *pbus = sabre->pci_bus;
 
-	for( ; pbus; pbus = pbus->children)
-		for(pdev = pbus->devices; pdev; pdev = pdev->sibling)
-			pdev_cookie_fillin(pbm, pdev);
+	for(pbus = pbus->children; pbus; pbus = pbus->next) {
+		if (pbus->number == sabre->pbm_A.pci_first_busno)
+			pdev_cookie_fillin(&sabre->pbm_A, pbus->self);
+		else if (pbus->number == sabre->pbm_B.pci_first_busno)
+			pdev_cookie_fillin(&sabre->pbm_B, pbus->self);
+	}
 }
 
 /* Walk PROM device tree under PBM, looking for 'assigned-address'
  * properties, and recording them in pci_vma's linked in via
  * PBM->assignments.
  */
-static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs)
+__initfunc(static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs))
 {
 	struct linux_prom_ebus_ranges erng[PROMREG_MAX];
 	int err, iter;
@@ -632,18 +892,26 @@
 		ap->phys_hi = ep->parent_phys_hi;
 		ap->phys_mid = ep->parent_phys_mid;
 		ap->phys_lo = ep->parent_phys_lo;
+
+		ap->size_hi = 0;
+		ap->size_lo = ep->size;
 	}
 	return err;
 }
 
-static void assignment_process(struct linux_pbm_info *pbm, int node)
+__initfunc(static void assignment_process(struct linux_pbm_info *pbm, int node))
 {
 	struct linux_prom_pci_registers aregs[PROMREG_MAX];
 	char pname[256];
 	int err, iter, numa;
 
 	err = prom_getproperty(node, "name", (char *)&pname[0], sizeof(pname));
-	if(strncmp(pname, "ebus", 4) == 0) {
+	if (err > 0)
+		pname[err] = 0;
+#ifdef FIXUP_VMA_DEBUG
+	dprintf("%s: %s\n", __FUNCTION__, err > 0 ? pname : "???");
+#endif
+	if(strcmp(pname, "ebus") == 0) {
 		numa = gimme_ebus_assignments(node, &aregs[0]);
 	} else {
 		err = prom_getproperty(node, "assigned-addresses",
@@ -653,7 +921,7 @@
 		if(err == 0 || err == -1)
 			return;
 
-		numa = (err / sizeof(struct linux_prom_pci_ranges));
+		numa = (err / sizeof(struct linux_prom_pci_registers));
 	}
 
 	for(iter = 0; iter < numa; iter++) {
@@ -667,8 +935,6 @@
 		io = (space == 1);
 
 		breg = (ap->phys_hi & 0xff);
-		if(breg == PCI_ROM_ADDRESS)
-			continue;
 
 		vp = pci_vma_alloc();
 
@@ -677,21 +943,20 @@
 		 * XXX either due to it's layout so...
 		 */
 		vp->start = ap->phys_lo;
-		vp->end = vp->start + ap->size_lo;
-		vp->base_reg = breg;
-
-		/* Sanity */
-		if(io && (vp->end & ~(0xffff))) {
-			prom_printf("assignment_process: Out of range PCI I/O "
-				    "[%08lx:%08lx]\n", vp->start, vp->end);
-			prom_halt();
-		}
+		vp->end = vp->start + ap->size_lo - 1;
+		vp->offset = (ap->phys_hi & 0xffffff);
 
 		pci_add_vma(pbm, vp, io);
+
+#ifdef FIXUP_VMA_DEBUG
+		dprintf("%s: BaseReg %02x", pname, breg);
+		dprintf(" %s vma [%08x,%08x]\n",
+			io ? "I/O" : breg == PCI_ROM_ADDRESS ? "ROM" : "MEM", vp->start, vp->end);
+#endif
 	}
 }
 
-static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node)
+__initfunc(static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node))
 {
 	while(node) {
 		int child = prom_getchild(node);
@@ -704,17 +969,112 @@
 	}
 }
 
-static void record_assignments(struct linux_pbm_info *pbm)
+static inline void record_assignments(struct linux_pbm_info *pbm)
 {
+	struct pci_vma *vp;
+
+	if (pbm->parent->pci_bus) {
+		/*
+		 * Disallow anything that is not in our IO/MEM map on SIMBA.
+		 */
+		struct pci_bus *pbus = pbm->parent->pci_bus;
+		struct pci_dev *pdev;
+		unsigned char map;
+		int bit;
+
+		for (pdev = pbus->devices; pdev; pdev = pdev->sibling) {
+			struct pcidev_cookie *pcp = pdev->sysdata;
+			if (!pcp) {
+				prom_printf("record_assignments: "
+					"no pcidev_cookie for pdev %02x\n",
+					pdev->devfn);
+				prom_halt();
+			}
+			if (pcp->pbm == pbm)
+				break;
+		}
+
+		if (!pdev) {
+			prom_printf("record_assignments: no pdev for PBM\n");
+			prom_halt();
+		}
+
+		pci_read_config_byte(pdev, APB_IO_ADDRESS_MAP, &map);
+#ifdef FIXUP_VMA_DEBUG
+		dprintf("%s: IO   %02x\n", __FUNCTION__, map);
+#endif
+		for (bit = 0; bit < 8; bit++) {
+			if (!(map & (1 << bit))) {
+				vp = pci_vma_alloc();
+				vp->start = (bit << 21);
+				vp->end = vp->start + (1 << 21) - 1;
+				vp->offset = 0;
+				pci_add_vma(pbm, vp, 1);
+#ifdef FIXUP_VMA_DEBUG
+				dprintf("%s: IO   prealloc vma [%08x,%08x]\n",
+					__FUNCTION__, vp->start, vp->end);
+#endif
+			}
+		}
+		pci_read_config_byte(pdev, APB_MEM_ADDRESS_MAP, &map);
+#ifdef FIXUP_VMA_DEBUG
+		dprintf("%s: MEM  %02x\n", __FUNCTION__, map);
+#endif
+		for (bit = 0; bit < 8; bit++) {
+			if (!(map & (1 << bit))) {
+				vp = pci_vma_alloc();
+				vp->start = (bit << 29);
+				vp->end = vp->start + (1 << 29) - 1;
+				vp->offset = 0;
+				pci_add_vma(pbm, vp, 0);
+#ifdef FIXUP_VMA_DEBUG
+				dprintf("%s: MEM  prealloc vma [%08x,%08x]\n",
+					__FUNCTION__, vp->start, vp->end);
+#endif
+			}
+		}
+	}
+
 	assignment_walk_siblings(pbm, prom_getchild(pbm->prom_node));
+
+	/*
+	 * Protect ISA IO space from being used.
+	 */
+	vp = pci_find_vma(pbm, 0, 0, 1);
+	if (!vp || 0x400 <= vp->start) {
+		vp = pci_vma_alloc();
+		vp->start = 0;
+		vp->end = vp->start + 0x400 - 1;
+		vp->offset = 0;
+		pci_add_vma(pbm, vp, 1);
+	}
+
+#ifdef FIXUP_VMA_DEBUG
+	dprintf("PROM IO assignments for PBM %s:\n",
+		pbm == &pbm->parent->pbm_A ? "A" : "B");
+	vp = pbm->IO_assignments;
+	while (vp) {
+		dprintf("  [%08x,%08x] (%s)\n", vp->start, vp->end,
+			vp->offset ? "Register" : "Unmapped");
+		vp = vp->next;
+	}
+	dprintf("PROM MEM assignments for PBM %s:\n",
+		pbm == &pbm->parent->pbm_A ? "A" : "B");
+	vp = pbm->MEM_assignments;
+	while (vp) {
+		dprintf("  [%08x,%08x] (%s)\n", vp->start, vp->end,
+			vp->offset ? "Register" : "Unmapped");
+		vp = vp->next;
+	}
+#endif
 }
 
-static void fixup_regs(struct pci_dev *pdev,
-		       struct linux_pbm_info *pbm,
-		       struct linux_prom_pci_registers *pregs,
-		       int nregs,
-		       struct linux_prom_pci_registers *assigned,
-		       int numaa)
+__initfunc(static void fixup_regs(struct pci_dev *pdev,
+				  struct linux_pbm_info *pbm,
+				  struct linux_prom_pci_registers *pregs,
+				  int nregs,
+				  struct linux_prom_pci_registers *assigned,
+				  int numaa))
 {
 	int preg, rng;
 	int IO_seen = 0;
@@ -724,34 +1084,39 @@
 		struct linux_prom_pci_registers *ap = NULL;
 		int bustype = (pregs[preg].phys_hi >> 24) & 0x3;
 		int bsreg, brindex;
+		unsigned int rtmp;
 		u64 pci_addr;
 
 		if(bustype == 0) {
 			/* Config space cookie, nothing to do. */
 			if(preg != 0)
-				printk("%s: strange, config space not 0\n",
-				       __FUNCTION__);
+				printk("%s %02x.%02x [%04x,%04x]: "
+				       "strange, config space not 0\n",
+				       __FUNCTION__,
+				       pdev->bus->number, pdev->devfn,
+				       pdev->vendor, pdev->device);
 			continue;
 		} else if(bustype == 3) {
 			/* XXX add support for this... */
-			printk("%s: Warning, ignoring 64-bit PCI memory space, "
+			printk("%s %02x.%02x [%04x,%04x]: "
+			       "Warning, ignoring 64-bit PCI memory space, "
 			       "tell Eddie C. Dost (ecd@skynet.be).\n",
-			       __FUNCTION__);
+			       __FUNCTION__,
+			       pdev->bus->number, pdev->devfn,
+			       pdev->vendor, pdev->device);
 			continue;
 		}
-		bsreg = (pregs[preg].phys_hi & 0xff);
 
-		/* We can safely ignore these. */
-		if(bsreg == PCI_ROM_ADDRESS)
-			continue;
+		bsreg = (pregs[preg].phys_hi & 0xff);
 
 		/* Sanity */
 		if((bsreg < PCI_BASE_ADDRESS_0) ||
-		   (bsreg > (PCI_BASE_ADDRESS_5 + 4)) ||
+		   ((bsreg > (PCI_BASE_ADDRESS_5 + 4)) && (bsreg != PCI_ROM_ADDRESS)) ||
 		   (bsreg & 3)) {
-			printk("%s: [%04x:%04x]: "
+			printk("%s %02x.%02x [%04x:%04x]: "
 			       "Warning, ignoring bogus basereg [%x]\n",
-			       __FUNCTION__, pdev->vendor, pdev->device, bsreg);
+			       __FUNCTION__, pdev->bus->number, pdev->devfn,
+			       pdev->vendor, pdev->device, bsreg);
 			printk("  PROM reg: %08x.%08x.%08x %08x.%08x\n",
 			       pregs[preg].phys_hi, pregs[preg].phys_mid,
 			       pregs[preg].phys_lo, pregs[preg].size_hi,
@@ -798,7 +1163,16 @@
 			/* AIEEE */
 			prom_printf("fixup_doit: YIEEE, cannot find PBM ranges\n");
 		}
-		pdev->base_address[brindex] = (unsigned long)__va(pci_addr);
+		if (bsreg == PCI_ROM_ADDRESS) {
+			pdev->rom_address = (unsigned long)__va(pci_addr);
+			pdev->rom_address |= 1;
+			/*
+			 * Enable access to the ROM.
+			 */
+			pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &rtmp);
+			pci_write_config_dword(pdev, PCI_ROM_ADDRESS, rtmp | 1);
+		} else
+			pdev->base_address[brindex] = (unsigned long)__va(pci_addr);
 
 		/* Preserve I/O space bit. */
 		if(bustype == 0x1) {
@@ -811,15 +1185,18 @@
 
 	/* Now handle assignments PROM did not take care of. */
 	if(nregs) {
+		unsigned int rtmp, ridx;
+		unsigned int offset, base;
+		struct pci_vma *vp;
+		u64 pci_addr;
 		int breg;
 
 		for(breg = PCI_BASE_ADDRESS_0; breg <= PCI_BASE_ADDRESS_5; breg += 4) {
-			unsigned int rtmp, ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2);
-			unsigned int base = (unsigned int)pdev->base_address[ridx];
-			struct pci_vma *vp;
-			u64 pci_addr;
 			int io;
 
+			ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2);
+			base = (unsigned int)pdev->base_address[ridx];
+
 			if(pdev->base_address[ridx] > PAGE_OFFSET)
 				continue;
 
@@ -827,19 +1204,14 @@
 			base &= ~((io ?
 				   PCI_BASE_ADDRESS_IO_MASK :
 				   PCI_BASE_ADDRESS_MEM_MASK));
-			vp = pci_find_vma(pbm, base, io);
+			offset = (pdev->bus->number << 16) | (pdev->devfn << 8) | breg;
+			vp = pci_find_vma(pbm, base, offset, io);
 			if(!vp || vp->start > base) {
 				unsigned int size, new_base;
 
-				pcibios_read_config_dword(pdev->bus->number,
-							  pdev->devfn,
-							  breg, &rtmp);
-				pcibios_write_config_dword(pdev->bus->number,
-							   pdev->devfn,
-							   breg, 0xffffffff);
-				pcibios_read_config_dword(pdev->bus->number,
-							  pdev->devfn,
-							  breg, &size);
+				pci_read_config_dword(pdev, breg, &rtmp);
+				pci_write_config_dword(pdev, breg, 0xffffffff);
+				pci_read_config_dword(pdev, breg, &size);
 				if(io)
 					size &= ~1;
 				size = (~(size) + 1);
@@ -847,7 +1219,8 @@
 					continue;
 
 				new_base = 0;
-				for(vp=pci_find_vma(pbm,new_base,io); ; vp=vp->next) {
+				for(vp = pci_find_vma(pbm, new_base, 0, io); ;
+				    vp = vp->next) {
 					if(!vp || new_base + size <= vp->start)
 						break;
 					new_base = (vp->end + (size - 1)) & ~(size-1);
@@ -859,26 +1232,27 @@
 				}
 				vp = pci_vma_alloc();
 				vp->start = new_base;
-				vp->end = vp->start + size;
-				vp->base_reg = breg;
+				vp->end = vp->start + size - 1;
+				vp->offset = offset;
 
-				/* Sanity */
-				if(io && vp->end & ~(0xffff)) {
-					prom_printf("PCI: Out of range PCI I/O "
-						    "[%08lx:%08lx] during fixup\n",
-						    vp->start, vp->end);
-					prom_halt();
-				}
 				pci_add_vma(pbm, vp, io);
 
+#ifdef FIXUP_VMA_DEBUG
+				dprintf("%02x.%02x.%x: BaseReg %02x",
+					pdev->bus->number,
+					PCI_SLOT(pdev->devfn),
+					PCI_FUNC(pdev->devfn),
+					breg);
+				dprintf(" %s vma [%08x,%08x]\n",
+					io ? "I/O" : breg == PCI_ROM_ADDRESS ? "ROM" : "MEM", vp->start, vp->end);
+#endif
 				rtmp = new_base;
+				pci_read_config_dword(pdev, breg, &base);
 				if(io)
-					rtmp |= (rtmp & PCI_BASE_ADDRESS_IO_MASK);
+					rtmp |= (base & ~PCI_BASE_ADDRESS_IO_MASK);
 				else
-					rtmp |= (rtmp & PCI_BASE_ADDRESS_MEM_MASK);
-				pcibios_write_config_dword(pdev->bus->number,
-							   pdev->devfn,
-							   breg, rtmp);
+					rtmp |= (base & ~PCI_BASE_ADDRESS_MEM_MASK);
+				pci_write_config_dword(pdev, breg, rtmp);
 
 				/* Apply PBM ranges and update pci_dev. */
 				pci_addr = new_base;
@@ -912,13 +1286,93 @@
 				}
 			}
 		}
+
+		/*
+		 * Handle PCI_ROM_ADDRESS.
+		 */
+		breg = PCI_ROM_ADDRESS;
+		base = (unsigned int)pdev->rom_address;
+
+		if(pdev->rom_address > PAGE_OFFSET)
+			goto rom_address_done;
+
+		base &= PCI_ROM_ADDRESS_MASK;
+		offset = (pdev->bus->number << 16) | (pdev->devfn << 8) | breg;
+		vp = pci_find_vma(pbm, base, offset, 0);
+		if(!vp || vp->start > base) {
+			unsigned int size, new_base;
+
+			pci_read_config_dword(pdev, breg, &rtmp);
+			pci_write_config_dword(pdev, breg, 0xffffffff);
+			pci_read_config_dword(pdev, breg, &size);
+			size &= ~1;
+			size = (~(size) + 1);
+			if(!size)
+				goto rom_address_done;
+
+			new_base = 0;
+			for(vp = pci_find_vma(pbm, new_base, 0, 0); ; vp = vp->next) {
+				if(!vp || new_base + size <= vp->start)
+					break;
+				new_base = (vp->end + (size - 1)) & ~(size-1);
+			}
+			if(vp && (new_base + size > vp->start)) {
+				prom_printf("PCI: Impossible full MEM space.\n");
+				prom_halt();
+			}
+			vp = pci_vma_alloc();
+			vp->start = new_base;
+			vp->end = vp->start + size - 1;
+			vp->offset = offset;
+
+			pci_add_vma(pbm, vp, 0);
+
+#ifdef FIXUP_VMA_DEBUG
+			dprintf("%02x.%02x.%x: BaseReg %02x",
+				pdev->bus->number,
+				PCI_SLOT(pdev->devfn),
+				PCI_FUNC(pdev->devfn),
+				breg);
+			dprintf(" %s vma [%08x,%08x]\n",
+				"ROM", vp->start, vp->end);
+#endif
+
+			rtmp = new_base;
+			pci_read_config_dword(pdev, breg, &base);
+			rtmp |= (base & ~PCI_ROM_ADDRESS_MASK);
+			pci_write_config_dword(pdev, breg, rtmp);
+
+			/* Apply PBM ranges and update pci_dev. */
+			pci_addr = new_base;
+			for(rng = 0; rng < pbm->num_pbm_ranges; rng++) {
+				struct linux_prom_pci_ranges *rp;
+				int rspace;
+
+				rp = &pbm->pbm_ranges[rng];
+				rspace = (rp->child_phys_hi >> 24) & 3;
+				if(rspace != 2)
+					continue;
+				pci_addr += ((u64)rp->parent_phys_lo);
+				pci_addr += (((u64)rp->parent_phys_hi)<<32UL);
+				break;
+			}
+			if(rng == pbm->num_pbm_ranges) {
+				/* AIEEE */
+				prom_printf("fixup_doit: YIEEE, cannot find "
+					    "PBM ranges\n");
+			}
+			pdev->rom_address = (unsigned long)__va(pci_addr);
+
+			pdev->rom_address |= (base & ~PCI_ROM_ADDRESS_MASK);
+			MEM_seen = 1;
+		}
+	rom_address_done:
+
 	}
 	if(IO_seen || MEM_seen) {
 		unsigned int l;
 
-		pcibios_read_config_dword(pdev->bus->number,
-					  pdev->devfn,
-					  PCI_COMMAND, &l);
+		pci_read_config_dword(pdev, PCI_COMMAND, &l);
 #ifdef FIXUP_REGS_DEBUG
 		dprintf("[");
 #endif
@@ -937,9 +1391,7 @@
 #ifdef FIXUP_REGS_DEBUG
 		dprintf("]");
 #endif
-		pcibios_write_config_dword(pdev->bus->number,
-					   pdev->devfn,
-					   PCI_COMMAND, l);
+		pci_write_config_dword(pdev, PCI_COMMAND, l);
 	}
 
 #ifdef FIXUP_REGS_DEBUG
@@ -955,7 +1407,7 @@
 #define imap_offset(__member) \
 	((unsigned long)(&(((struct psycho_regs *)0)->__member)))
 
-static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
+__initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino))
 {
 	unsigned int bus, slot;
 
@@ -963,17 +1415,20 @@
 	slot = (ino & 0x0c) >> 2;
 
 	if(bus == 0) {
-		/* Perform a sanity check, we might as well.
-		 * PBM A only has 2 PCI slots.
-		 */
-		if(slot > 1) {
-			prom_printf("pcislot_imap: Bogus slot on PBM A (%ld)\n", slot);
-			prom_halt();
-		}
-		if(slot == 0)
+		switch(slot) {
+		case 0:
 			return imap_offset(imap_a_slot0);
-		else
+		case 1:
 			return imap_offset(imap_a_slot1);
+		case 2:
+			return imap_offset(imap_a_slot2);
+		case 3:
+			return imap_offset(imap_a_slot3);
+		default:
+			prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n",
+				    bus, slot);
+			prom_halt();
+		}
 	} else {
 		switch(slot) {
 		case 0:
@@ -988,13 +1443,12 @@
 			prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n",
 				    bus, slot);
 			prom_halt();
-			return 0; /* Make gcc happy */
-		};
+		}
 	}
 }
 
 /* Exported for EBUS probing layer. */
-unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino)
+__initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino))
 {
 	unsigned long imap_off, ign, ino;
 
@@ -1089,9 +1543,62 @@
 	return pci_irq_encode(imap_off, pbm->parent->index, ign, ino);
 }
 
-static void fixup_irq(struct pci_dev *pdev,
-		      struct linux_pbm_info *pbm,
-		      int node)
+__initfunc(static int pbm_intmap_match(struct linux_pbm_info *pbm,
+				       struct pci_dev *pdev,
+				       struct linux_prom_pci_registers *preg,
+				       unsigned int *interrupt))
+{
+	struct linux_prom_pci_registers ppreg;
+	unsigned int hi, mid, lo, irq;
+	int i;
+
+	if (!pbm->num_pbm_intmap)
+		return 0;
+
+	/*
+	 * Underneath a bridge, use register of parent bridge.
+	 */
+	if (pdev->bus->number != pbm->pci_first_busno) {
+		struct pcidev_cookie *pcp = pdev->bus->self->sysdata;
+		int node;
+
+		if (!pcp)
+			goto out;
+
+		node = pcp->prom_node;
+
+		i = prom_getproperty(node, "reg", (char*)&ppreg, sizeof(ppreg));
+		if(i == 0 || i == -1)
+			goto out;
+
+		preg = &ppreg;
+	}
+
+	hi = preg->phys_hi & pbm->pbm_intmask.phys_hi;
+	mid = preg->phys_mid & pbm->pbm_intmask.phys_mid;
+	lo = preg->phys_lo & pbm->pbm_intmask.phys_lo;
+	irq = *interrupt & pbm->pbm_intmask.interrupt;
+	for (i = 0; i < pbm->num_pbm_intmap; i++) {
+		if ((pbm->pbm_intmap[i].phys_hi == hi) &&
+		    (pbm->pbm_intmap[i].phys_mid == mid) &&
+		    (pbm->pbm_intmap[i].phys_lo == lo) &&
+		    (pbm->pbm_intmap[i].interrupt == irq)) {
+			*interrupt = pbm->pbm_intmap[i].cinterrupt;
+			return *interrupt;
+		}
+	}
+
+out:
+	prom_printf("pbm_intmap_match: IRQ [%08x.%08x.%08x.%08x] "
+		    "not found in interrupt-map\n", preg->phys_hi,
+		    preg->phys_mid, preg->phys_lo, *interrupt);
+	prom_halt();
+}
+
+__initfunc(static void fixup_irq(struct pci_dev *pdev,
+				 struct linux_pbm_info *pbm,
+				 struct linux_prom_pci_registers *preg,
+				 int node))
 {
 	unsigned int prom_irq, portid = pbm->parent->upa_portid;
 	unsigned char pci_irq_line = pdev->irq;
@@ -1102,13 +1609,24 @@
 #endif
 	err = prom_getproperty(node, "interrupts", (void *)&prom_irq, sizeof(prom_irq));
 	if(err == 0 || err == -1) {
-		prom_printf("fixup_irq: No interrupts property for dev[%04x:%04x]\n",
-			    pdev->vendor, pdev->device);
-		prom_halt();
+#ifdef FIXUP_IRQ_DEBUG
+		dprintf("No interrupts property.\n");
+#endif
+		pdev->irq = 0;
+		return;
 	}
 
+	/* See if we find a matching interrupt-map entry. */
+	if (pbm_intmap_match(pbm, pdev, preg, &prom_irq)) {
+		pdev->irq = psycho_irq_build(pbm,
+					     (pbm->parent->upa_portid << 6)
+					     | prom_irq);
+#ifdef FIXUP_IRQ_DEBUG
+		dprintf("interrupt-map specified prom_irq[%x] pdev->irq[%x]",
+		        prom_irq, pdev->irq);
+#endif
 	/* See if fully specified already (ie. for onboard devices like hme) */
-	if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) {
+	} else if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) {
 		pdev->irq = psycho_irq_build(pbm, prom_irq);
 #ifdef FIXUP_IRQ_DEBUG
 		dprintf("fully specified prom_irq[%x] pdev->irq[%x]",
@@ -1147,7 +1665,7 @@
 				slot = (pdev->bus->self->devfn >> 3) - 2;
 
 			/* Use low slot number bits of child as IRQ line. */
-			line = (line + ((pdev->devfn >> 3) - 4)) % 4;
+			line = (pdev->devfn >> 3) & 0x03;
 		}
 		slot = (slot << 2);
 
@@ -1159,16 +1677,10 @@
 		do {
 			unsigned char iline, ipin;
 
-			(void)pcibios_read_config_byte(pdev->bus->number,
-						       pdev->devfn,
-						       PCI_INTERRUPT_PIN,
-						       &ipin);
-			(void)pcibios_read_config_byte(pdev->bus->number,
-						       pdev->devfn,
-						       PCI_INTERRUPT_LINE,
-						       &iline);
-			dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] irq[%x] "
-			        "iline[%x] ipin[%x] prom_irq[%x]",
+			pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &ipin);
+			pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &iline);
+			dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] "
+				"irq[%x] iline[%x] ipin[%x] prom_irq[%x]",
 			        portid, bus>>4, slot>>2, line, pdev->irq,
 			        iline, ipin, prom_irq);
 		} while(0);
@@ -1178,21 +1690,19 @@
 	/*
 	 * Write the INO to config space PCI_INTERRUPT_LINE.
 	 */
-	(void)pcibios_write_config_byte(pdev->bus->number,
-					pdev->devfn,
-					PCI_INTERRUPT_LINE,
-					pdev->irq & PCI_IRQ_INO);
+	pci_write_config_byte(pdev, PCI_INTERRUPT_LINE,
+			      pdev->irq & PCI_IRQ_INO);
 
 #ifdef FIXUP_IRQ_DEBUG
 	dprintf("\n");
 #endif
 }
 
-static void fixup_doit(struct pci_dev *pdev,
-		       struct linux_pbm_info *pbm,
-		       struct linux_prom_pci_registers *pregs,
-		       int nregs,
-		       int node)
+__initfunc(static void fixup_doit(struct pci_dev *pdev,
+				  struct linux_pbm_info *pbm,
+				  struct linux_prom_pci_registers *pregs,
+				  int nregs,
+				  int node))
 {
 	struct linux_prom_pci_registers assigned[PROMREG_MAX];
 	int numaa, err;
@@ -1209,12 +1719,12 @@
 	fixup_regs(pdev, pbm, pregs, nregs, &assigned[0], numaa);
 
 	/* Next, fixup interrupt numbers. */
-	fixup_irq(pdev, pbm, node);
+	fixup_irq(pdev, pbm, &pregs[0], node);
 }
 
-static void fixup_pci_dev(struct pci_dev *pdev,
-			  struct pci_bus *pbus,
-			  struct linux_pbm_info *pbm)
+__initfunc(static void fixup_pci_dev(struct pci_dev *pdev,
+				     struct pci_bus *pbus,
+				     struct linux_pbm_info *pbm))
 {
 	struct linux_prom_pci_registers pregs[PROMREG_MAX];
 	struct pcidev_cookie *pcp = pdev->sysdata;
@@ -1225,18 +1735,12 @@
 		unsigned short cmd;
 
 		/* First, enable bus mastering. */
-		pcibios_read_config_word(pdev->bus->number,
-					 pdev->devfn,
-					 PCI_COMMAND, &cmd);
+		pci_read_config_word(pdev, PCI_COMMAND, &cmd);
 		cmd |= PCI_COMMAND_MASTER;
-		pcibios_write_config_word(pdev->bus->number,
-					  pdev->devfn,
-					  PCI_COMMAND, cmd);
+		pci_write_config_word(pdev, PCI_COMMAND, cmd);
 
 		/* Now, set cache line size to 64-bytes. */
-		pcibios_write_config_byte(pdev->bus->number,
-					  pdev->devfn,
-					  PCI_CACHE_LINE_SIZE, 64);
+		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64);
 	}
 
 	/* Ignore if this is one of the PBM's, EBUS, or a
@@ -1246,8 +1750,17 @@
 	if((pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI) ||
 	   (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) ||
 	   (pdev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) ||
-	   (pcp == NULL))
+	   (pcp == NULL)) {
+		/*
+		 * Prevent access to PCI_ROM_ADDRESS, in case present
+		 * as we don't fixup the address.
+		 */
+		if (pdev->rom_address) {
+			pci_write_config_dword(pdev, PCI_ROM_ADDRESS, 0);
+			pdev->rom_address = 0;
+		}
 		return;
+	}
 
 	node = pcp->prom_node;
 
@@ -1260,20 +1773,30 @@
 	nregs = (err / sizeof(pregs[0]));
 
 	fixup_doit(pdev, pbm, &pregs[0], nregs, node);
+
+	/* Enable bus mastering on IDE interfaces. */
+	if ((pdev->class >> 8 == PCI_CLASS_STORAGE_IDE)
+	    && (pdev->class & 0x80)) {
+		unsigned short cmd;
+
+		pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+		cmd |= PCI_COMMAND_MASTER;
+		pci_write_config_word(pdev, PCI_COMMAND, cmd);
+	}
 }
 
-static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm)
+__initfunc(static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm))
 {
 	struct pci_dev *pdev;
 
 	for(pdev = pbus->devices; pdev; pdev = pdev->sibling)
 		fixup_pci_dev(pdev, pbus, pbm);
 
-	for(pbus = pbus->children; pbus; pbus = pbus->children)
+	for(pbus = pbus->children; pbus; pbus = pbus->next)
 		fixup_pci_bus(pbus, pbm);
 }
 
-static void fixup_addr_irq(struct linux_pbm_info *pbm)
+__initfunc(static void fixup_addr_irq(struct linux_pbm_info *pbm))
 {
 	struct pci_bus *pbus = &pbm->pci_bus;
 
@@ -1286,14 +1809,16 @@
 /* Walk all PCI devices probes, fixing up base registers and IRQ registers.
  * We use OBP for most of this work.
  */
-static void psycho_final_fixup(struct linux_psycho *psycho)
+__initfunc(static void psycho_final_fixup(struct linux_psycho *psycho))
 {
 	/* Second, fixup base address registers and IRQ lines... */
-	fixup_addr_irq(&psycho->pbm_A);
-	fixup_addr_irq(&psycho->pbm_B);
+	if (psycho->pbm_A.parent)
+		fixup_addr_irq(&psycho->pbm_A);
+	if (psycho->pbm_B.parent)
+		fixup_addr_irq(&psycho->pbm_B);
 }
 
-unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end)
+__initfunc(unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end))
 {
 	struct linux_psycho *psycho;
 
@@ -1312,11 +1837,16 @@
 	 */
 
 	for (psycho = psycho_root; psycho; psycho = psycho->next) {
-		/* Probe busses under PBM B. */
-		pbm_probe(&psycho->pbm_B, &memory_start);
+		/* Probe bus on builtin PCI. */
+		if (psycho->pci_bus)
+			sabre_probe(psycho, &memory_start);
+		else {
+			/* Probe busses under PBM B. */
+			pbm_probe(&psycho->pbm_B, &memory_start);
 
-		/* Probe busses under PBM A. */
-		pbm_probe(&psycho->pbm_A, &memory_start);
+			/* Probe busses under PBM A. */
+			pbm_probe(&psycho->pbm_A, &memory_start);
+		}
 	}
 
 	pci_init_alloc_init(&memory_start);
@@ -1327,8 +1857,13 @@
 	 * a pci_dev cookie (PBM+PROM_NODE, for pci_dev's).
 	 */
 	for (psycho = psycho_root; psycho; psycho = psycho->next) {
-		fill_in_pbm_cookies(&psycho->pbm_A);
-		fill_in_pbm_cookies(&psycho->pbm_B);
+		if (psycho->pci_bus)
+			sabre_cookie_fillin(psycho);
+
+		fill_in_pbm_cookies(&psycho->pbm_A.pci_bus,
+				    &psycho->pbm_A);
+		fill_in_pbm_cookies(&psycho->pbm_B.pci_bus,
+				    &psycho->pbm_B);
 
 		/* See what OBP has taken care of already. */
 		record_assignments(&psycho->pbm_A);
@@ -1373,11 +1908,157 @@
 static inline int
 out_of_range(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn)
 {
-	return (((pbm == &pbm->parent->pbm_B) && PCI_SLOT(devfn) > 4) ||
-		((pbm == &pbm->parent->pbm_A) && PCI_SLOT(devfn) > 6) ||
+	return ((pbm->parent == 0) ||
+		((pbm == &pbm->parent->pbm_B) && (bus == pbm->pci_first_busno) && PCI_SLOT(devfn) > 4) ||
+		((pbm == &pbm->parent->pbm_A) && (bus == pbm->pci_first_busno) && PCI_SLOT(devfn) > 6) ||
 		(pci_probe_enable == 0));
 }
 
+static inline int
+sabre_out_of_range(unsigned char devfn)
+{
+	return ((PCI_SLOT(devfn) == 0) && (PCI_FUNC(devfn) > 0)) ||
+	       ((PCI_SLOT(devfn) == 1) && (PCI_FUNC(devfn) > 1)) ||
+	       (PCI_SLOT(devfn) > 1);
+}
+
+static int
+sabre_read_config_byte(struct linux_pbm_info *pbm,
+		       unsigned char bus, unsigned char devfn,
+		       unsigned char where, unsigned char *value)
+{
+	if (bus)
+		return pbm_read_config_byte(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn)) {
+		*value = 0xff;
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	if (where < 8) {
+		unsigned short tmp;
+
+		pbm_read_config_word(pbm, bus, devfn, where & ~1, &tmp);
+		if (where & 1)
+			*value = tmp >> 8;
+		else
+			*value = tmp & 0xff;
+		return PCIBIOS_SUCCESSFUL;
+	} else
+		return pbm_read_config_byte(pbm, bus, devfn, where, value);
+}
+
+static int
+sabre_read_config_word(struct linux_pbm_info *pbm,
+		       unsigned char bus, unsigned char devfn,
+		       unsigned char where, unsigned short *value)
+{
+	if (bus)
+		return pbm_read_config_word(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn)) {
+		*value = 0xffff;
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	if (where < 8)
+		return pbm_read_config_word(pbm, bus, devfn, where, value);
+	else {
+		unsigned char tmp;
+
+		pbm_read_config_byte(pbm, bus, devfn, where, &tmp);
+		*value = tmp;
+		pbm_read_config_byte(pbm, bus, devfn, where + 1, &tmp);
+		*value |= tmp << 8;
+		return PCIBIOS_SUCCESSFUL;
+	}
+}
+
+static int
+sabre_read_config_dword(struct linux_pbm_info *pbm,
+			unsigned char bus, unsigned char devfn,
+			unsigned char where, unsigned int *value)
+{
+	unsigned short tmp;
+
+	if (bus)
+		return pbm_read_config_dword(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn)) {
+		*value = 0xffffffff;
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	sabre_read_config_word(pbm, bus, devfn, where, &tmp);
+	*value = tmp;
+	sabre_read_config_word(pbm, bus, devfn, where + 2, &tmp);
+	*value |= tmp << 16;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+sabre_write_config_byte(struct linux_pbm_info *pbm,
+			unsigned char bus, unsigned char devfn,
+			unsigned char where, unsigned char value)
+{
+	if (bus)
+		return pbm_write_config_byte(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn))
+		return PCIBIOS_SUCCESSFUL;
+
+	if (where < 8) {
+		unsigned short tmp;
+
+		pbm_read_config_word(pbm, bus, devfn, where & ~1, &tmp);
+		if (where & 1) {
+			value &= 0x00ff;
+			value |= tmp << 8;
+		} else {
+			value &= 0xff00;
+			value |= tmp;
+		}
+		return pbm_write_config_word(pbm, bus, devfn, where & ~1, tmp);
+	} else
+		return pbm_write_config_byte(pbm, bus, devfn, where, value);
+}
+
+static int
+sabre_write_config_word(struct linux_pbm_info *pbm,
+			unsigned char bus, unsigned char devfn,
+			unsigned char where, unsigned short value)
+{
+	if (bus)
+		return pbm_write_config_word(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn))
+		return PCIBIOS_SUCCESSFUL;
+
+	if (where < 8)
+		return pbm_write_config_word(pbm, bus, devfn, where, value);
+	else {
+		pbm_write_config_byte(pbm, bus, devfn, where, value & 0xff);
+		pbm_write_config_byte(pbm, bus, devfn, where + 1, value >> 8);
+		return PCIBIOS_SUCCESSFUL;
+	}
+}
+
+static int
+sabre_write_config_dword(struct linux_pbm_info *pbm,
+			 unsigned char bus, unsigned char devfn,
+			 unsigned char where, unsigned int value)
+{
+	if (bus)
+		return pbm_write_config_dword(pbm, bus, devfn, where, value);
+
+	if (sabre_out_of_range(devfn))
+		return PCIBIOS_SUCCESSFUL;
+
+	sabre_write_config_word(pbm, bus, devfn, where, value & 0xffff);
+	sabre_write_config_word(pbm, bus, devfn, where + 2, value >> 16);
+	return PCIBIOS_SUCCESSFUL;
+}
+
 static int
 pbm_read_config_byte(struct linux_pbm_info *pbm,
 		     unsigned char bus, unsigned char devfn,
@@ -1574,36 +2255,60 @@
 int pcibios_read_config_byte (unsigned char bus, unsigned char devfn,
 			      unsigned char where, unsigned char *value)
 {
-	return pbm_read_config_byte(bus2pbm[bus], bus, devfn, where, value);
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_read_config_byte(pbm, bus, devfn, where, value);
+	return pbm_read_config_byte(pbm, bus, devfn, where, value);
 }
 
 int pcibios_read_config_word (unsigned char bus, unsigned char devfn,
 			      unsigned char where, unsigned short *value)
 {
-	return pbm_read_config_word(bus2pbm[bus], bus, devfn, where, value);
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_read_config_word(pbm, bus, devfn, where, value);
+	return pbm_read_config_word(pbm, bus, devfn, where, value);
 }
 
 int pcibios_read_config_dword (unsigned char bus, unsigned char devfn,
 			       unsigned char where, unsigned int *value)
 {
-	return pbm_read_config_dword(bus2pbm[bus], bus, devfn, where, value);
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_read_config_dword(pbm, bus, devfn, where, value);
+	return pbm_read_config_dword(pbm, bus, devfn, where, value);
 }
 
 int pcibios_write_config_byte (unsigned char bus, unsigned char devfn,
 			       unsigned char where, unsigned char value)
 {
-	return pbm_write_config_byte(bus2pbm[bus], bus, devfn, where, value);
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_write_config_byte(pbm, bus, devfn, where, value);
+	return pbm_write_config_byte(pbm, bus, devfn, where, value);
 }
 
 int pcibios_write_config_word (unsigned char bus, unsigned char devfn,
 			       unsigned char where, unsigned short value)
 {
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_write_config_word(pbm, bus, devfn, where, value);
 	return pbm_write_config_word(bus2pbm[bus], bus, devfn, where, value);
 }
 
 int pcibios_write_config_dword (unsigned char bus, unsigned char devfn,
 			        unsigned char where, unsigned int value)
 {
+	struct linux_pbm_info *pbm = bus2pbm[bus];
+
+	if (pbm && pbm->parent && pbm->parent->pci_bus)
+		return sabre_write_config_dword(pbm, bus, devfn, where, value);
 	return pbm_write_config_dword(bus2pbm[bus], bus, devfn, where, value);
 }
 
@@ -1690,6 +2395,11 @@
 	unlock_kernel();
 
 	return err;
+}
+
+__initfunc(char *pcibios_setup(char *str))
+{
+	return str;
 }
 
 #endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov