Commit 455db04b authored by David S. Miller's avatar David S. Miller

Fix PCI IRQ probing for some bridging situations.

Use PCI_SLOT instead of hard-coded shift.
parent 31feabb8
......@@ -583,21 +583,45 @@ static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt
/* 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 it's 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;
struct pci_dev *pwalk;
int offset, plen;
struct pcidev_cookie *bus_pcp, *regs_pcp;
struct pci_dev *bus_dev, *regs_dev;
int plen;
bus_dev = pdev->bus->self;
regs_dev = pdev;
pwalk = pdev->bus->self;
while (pwalk->bus &&
pwalk->bus->number != pbm->pci_first_busno)
pwalk = pwalk->bus->self;
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 = pwalk->sysdata;
bus_pcp = bus_dev->sysdata;
/* But if the PCI bridge has it's own interrupt map
* and mask properties, use that and the device regs.
* 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],
......@@ -605,38 +629,21 @@ static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt
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",
plen = prom_getproperty(bus_pcp->prom_node,
"interrupt-map-mask",
(char *) &bridge_local_intmask,
sizeof(bridge_local_intmask));
if (plen == -1) {
prom_printf("pbm_intmap_match: Bridge has intmap but "
"no intmask.\n");
prom_halt();
printk("pci_intmap_match: Warning! Bridge has intmap "
"but no intmask.\n");
printk("pci_intmap_match: Trying to recover.\n");
return 0;
}
goto check_intmap;
}
pregs = bus_pcp->prom_regs;
offset = prom_getint(dev_pcp->prom_node,
"fcode-rom-offset");
/* Did PROM know better and assign an interrupt other
* than #INTA to the device? - We test here for presence of
* FCODE on the card, in this case we assume PROM has set
* correct 'interrupts' property, unless it is quadhme.
*/
if (offset == -1 ||
!strcmp(dev_pcp->prom_name, "SUNW,qfe") ||
!strcmp(dev_pcp->prom_name, "qfe")) {
/*
* No, use low slot number bits of child as IRQ line.
*/
*interrupt = ((*interrupt - 1 + PCI_SLOT(pdev->devfn)) & 3) + 1;
} else {
pregs = bus_pcp->prom_regs;
}
}
check_intmap:
hi = pregs->phys_hi & intmask->phys_hi;
mid = pregs->phys_mid & intmask->phys_mid;
lo = pregs->phys_lo & intmask->phys_lo;
......@@ -652,12 +659,22 @@ static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt
}
}
prom_printf("pbm_intmap_match: bus %02x, devfn %02x: ",
/* 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");
prom_halt();
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;
}
static void __init pdev_fixup_irq(struct pci_dev *pdev)
......@@ -703,6 +720,20 @@ static void __init pdev_fixup_irq(struct pci_dev *pdev)
goto have_irq;
}
/* Firmware gets quad-hme interrupts property totally
* wrong. It is 4 EBUS+HME devices behind a Digital bridge.
* For each of the 4 instances the EBUS has interrupt property
* '1' and the HME has interrupt property '2'. So we have to
* fix this up.
*/
if (!strcmp(pcp->prom_name, "SUNW,qfe") ||
!strcmp(pcp->prom_name, "qfe")) {
if (PCI_SLOT(pdev->devfn) & ~3)
BUG();
prom_irq = PCI_SLOT(pdev->devfn) + 1;
}
/* Can we find a matching entry in the interrupt-map? */
if (pci_intmap_match(pdev, &prom_irq)) {
pdev->irq = p->irq_build(pbm, pdev, (portid << 6) | prom_irq);
......@@ -738,12 +769,19 @@ static void __init pdev_fixup_irq(struct pci_dev *pdev)
* ranges. -DaveM
*/
if (pdev->bus->number == pbm->pci_first_busno) {
slot = (pdev->devfn >> 3) - pbm->pci_first_slot;
slot = PCI_SLOT(pdev->devfn) - pbm->pci_first_slot;
} else {
struct pci_dev *bus_dev;
/* Underneath a bridge, use slot number of parent
* bridge.
* bridge which is closest to the PBM.
*/
slot = (pdev->bus->self->devfn >> 3) - pbm->pci_first_slot;
bus_dev = pdev->bus->self;
while (bus_dev->bus &&
bus_dev->bus->number != pbm->pci_first_busno)
bus_dev = bus_dev->bus->self;
slot = PCI_SLOT(bus_dev->devfn) - pbm->pci_first_slot;
}
slot = slot << 2;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment