+static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt)
+{
+ struct linux_prom_pci_intmap bridge_local_intmap[PROM_PCIIMAP_MAX], *intmap;
+ struct linux_prom_pci_intmask bridge_local_intmask, *intmask;
+ struct pcidev_cookie *dev_pcp = pdev->sysdata;
+ struct pci_pbm_info *pbm = dev_pcp->pbm;
+ struct linux_prom_pci_registers *pregs = dev_pcp->prom_regs;
+ unsigned int hi, mid, lo, irq;
+ int i, num_intmap, map_slot;
+
+ intmap = &pbm->pbm_intmap[0];
+ intmask = &pbm->pbm_intmask;
+ num_intmap = pbm->num_pbm_intmap;
+ map_slot = 0;
+
+ /* If we are underneath a PCI bridge, use PROM register
+ * property of the parent bridge which is closest to
+ * the PBM.
+ *
+ * However if that parent bridge has interrupt map/mask
+ * properties of its own we use the PROM register property
+ * of the next child device on the path to PDEV.
+ *
+ * In detail the two cases are (note that the 'X' below is the
+ * 'next child on the path to PDEV' mentioned above):
+ *
+ * 1) PBM --> PCI bus lacking int{map,mask} --> X ... PDEV
+ *
+ * Here we use regs of 'PCI bus' device.
+ *
+ * 2) PBM --> PCI bus with int{map,mask} --> X ... PDEV
+ *
+ * Here we use regs of 'X'. Note that X can be PDEV.
+ */
+ if (pdev->bus->number != pbm->pci_first_busno) {
+ struct pcidev_cookie *bus_pcp, *regs_pcp;
+ struct pci_dev *bus_dev, *regs_dev;
+ int plen;
+
+ bus_dev = pdev->bus->self;
+ regs_dev = pdev;
+
+ while (bus_dev->bus &&
+ bus_dev->bus->number != pbm->pci_first_busno) {
+ regs_dev = bus_dev;
+ bus_dev = bus_dev->bus->self;
+ }
+
+ regs_pcp = regs_dev->sysdata;
+ pregs = regs_pcp->prom_regs;
+
+ bus_pcp = bus_dev->sysdata;
+
+ /* But if the PCI bridge has it's own interrupt map
+ * and mask properties, use that and the regs of the
+ * PCI entity at the next level down on the path to the
+ * device.
+ */
+ plen = prom_getproperty(bus_pcp->prom_node, "interrupt-map",
+ (char *) &bridge_local_intmap[0],
+ sizeof(bridge_local_intmap));
+ if (plen != -1) {
+ intmap = &bridge_local_intmap[0];
+ num_intmap = plen / sizeof(struct linux_prom_pci_intmap);
+ plen = prom_getproperty(bus_pcp->prom_node,
+ "interrupt-map-mask",
+ (char *) &bridge_local_intmask,
+ sizeof(bridge_local_intmask));
+ if (plen == -1) {
+ printk("pci_intmap_match: Warning! Bridge has intmap "
+ "but no intmask.\n");
+ printk("pci_intmap_match: Trying to recover.\n");
+ return 0;
+ }
+
+ if (pdev->bus->self != bus_dev)
+ map_slot = 1;
+ } else {
+ pregs = bus_pcp->prom_regs;
+ map_slot = 1;
+ }
+ }
+
+ if (map_slot) {
+ *interrupt = ((*interrupt
+ - 1
+ + PCI_SLOT(pdev->devfn)) & 0x3) + 1;
+ }
+
+ hi = pregs->phys_hi & intmask->phys_hi;
+ mid = pregs->phys_mid & intmask->phys_mid;
+ lo = pregs->phys_lo & intmask->phys_lo;
+ irq = *interrupt & intmask->interrupt;
+
+ for (i = 0; i < num_intmap; i++) {
+ if (intmap[i].phys_hi == hi &&
+ intmap[i].phys_mid == mid &&
+ intmap[i].phys_lo == lo &&
+ intmap[i].interrupt == irq) {
+ *interrupt = intmap[i].cinterrupt;
+ printk("PCI-IRQ: Routing bus[%2x] slot[%2x] map[%d] to INO[%02x]\n",
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ map_slot, *interrupt);
+ return 1;
+ }
+ }
+
+ /* We will run this code even if pbm->num_pbm_intmap is zero, just so
+ * we can apply the slot mapping to the PROM interrupt property value.
+ * So do not spit out these warnings in that case.
+ */
+ if (num_intmap != 0) {
+ /* Print it both to OBP console and kernel one so that if bootup
+ * hangs here the user has the information to report.
+ */
+ prom_printf("pci_intmap_match: bus %02x, devfn %02x: ",
+ pdev->bus->number, pdev->devfn);
+ prom_printf("IRQ [%08x.%08x.%08x.%08x] not found in interrupt-map\n",
+ pregs->phys_hi, pregs->phys_mid, pregs->phys_lo, *interrupt);
+ prom_printf("Please email this information to davem@redhat.com\n");
+
+ printk("pci_intmap_match: bus %02x, devfn %02x: ",
+ pdev->bus->number, pdev->devfn);
+ printk("IRQ [%08x.%08x.%08x.%08x] not found in interrupt-map\n",
+ pregs->phys_hi, pregs->phys_mid, pregs->phys_lo, *interrupt);
+ printk("Please email this information to davem@redhat.com\n");
+ }
+
+ return 0;
+}
+