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 ...@@ -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 /* If we are underneath a PCI bridge, use PROM register
* property of the parent bridge which is closest to * property of the parent bridge which is closest to
* the PBM. * 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) { if (pdev->bus->number != pbm->pci_first_busno) {
struct pcidev_cookie *bus_pcp; struct pcidev_cookie *bus_pcp, *regs_pcp;
struct pci_dev *pwalk; struct pci_dev *bus_dev, *regs_dev;
int offset, plen; 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;
}
pwalk = pdev->bus->self; regs_pcp = regs_dev->sysdata;
while (pwalk->bus && pregs = regs_pcp->prom_regs;
pwalk->bus->number != pbm->pci_first_busno)
pwalk = pwalk->bus->self;
bus_pcp = pwalk->sysdata; bus_pcp = bus_dev->sysdata;
/* But if the PCI bridge has it's own interrupt map /* 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", plen = prom_getproperty(bus_pcp->prom_node, "interrupt-map",
(char *) &bridge_local_intmap[0], (char *) &bridge_local_intmap[0],
...@@ -605,38 +629,21 @@ static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt ...@@ -605,38 +629,21 @@ static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt
if (plen != -1) { if (plen != -1) {
intmap = &bridge_local_intmap[0]; intmap = &bridge_local_intmap[0];
num_intmap = plen / sizeof(struct linux_prom_pci_intmap); 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, (char *) &bridge_local_intmask,
sizeof(bridge_local_intmask)); sizeof(bridge_local_intmask));
if (plen == -1) { if (plen == -1) {
prom_printf("pbm_intmap_match: Bridge has intmap but " printk("pci_intmap_match: Warning! Bridge has intmap "
"no intmask.\n"); "but no intmask.\n");
prom_halt(); printk("pci_intmap_match: Trying to recover.\n");
} return 0;
goto check_intmap;
} }
} else {
pregs = bus_pcp->prom_regs; 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;
} }
} }
check_intmap:
hi = pregs->phys_hi & intmask->phys_hi; hi = pregs->phys_hi & intmask->phys_hi;
mid = pregs->phys_mid & intmask->phys_mid; mid = pregs->phys_mid & intmask->phys_mid;
lo = pregs->phys_lo & intmask->phys_lo; lo = pregs->phys_lo & intmask->phys_lo;
...@@ -652,12 +659,22 @@ static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt ...@@ -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); pdev->bus->number, pdev->devfn);
prom_printf("IRQ [%08x.%08x.%08x.%08x] not found in interrupt-map\n", prom_printf("IRQ [%08x.%08x.%08x.%08x] not found in interrupt-map\n",
pregs->phys_hi, pregs->phys_mid, pregs->phys_lo, *interrupt); pregs->phys_hi, pregs->phys_mid, pregs->phys_lo, *interrupt);
prom_printf("Please email this information to davem@redhat.com\n"); 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) static void __init pdev_fixup_irq(struct pci_dev *pdev)
...@@ -703,6 +720,20 @@ 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; 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? */ /* Can we find a matching entry in the interrupt-map? */
if (pci_intmap_match(pdev, &prom_irq)) { if (pci_intmap_match(pdev, &prom_irq)) {
pdev->irq = p->irq_build(pbm, pdev, (portid << 6) | 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) ...@@ -738,12 +769,19 @@ static void __init pdev_fixup_irq(struct pci_dev *pdev)
* ranges. -DaveM * ranges. -DaveM
*/ */
if (pdev->bus->number == pbm->pci_first_busno) { 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 { } else {
struct pci_dev *bus_dev;
/* Underneath a bridge, use slot number of parent /* 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; 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