diff options
| author | Tom Rini <trini@kernel.crashing.org> | 2004-08-15 20:35:18 -0700 |
|---|---|---|
| committer | Tom Rini <trini@kernel.crashing.org> | 2004-08-15 20:35:18 -0700 |
| commit | ad2d538c50e7163dcc90e374195d55d06f8ff978 (patch) | |
| tree | 4fd04f9247378f8f83dae5b0126d3d3449a92207 | |
| parent | 96b89248356c51996c3b2693c6c8d5412f17bda1 (diff) | |
ppc32: On PReP, use residual data for PCI dev -> IRQ, and use it.
Signed-off-by: Leigh Brown <leigh@solinno.co.uk>
Signed-off-by: Tom Rini <trini@kernel.crashing.org>
| -rw-r--r-- | arch/ppc/platforms/prep_pci.c | 60 | ||||
| -rw-r--r-- | arch/ppc/platforms/residual.c | 60 | ||||
| -rw-r--r-- | include/asm-ppc/residual.h | 7 |
3 files changed, 104 insertions, 23 deletions
diff --git a/arch/ppc/platforms/prep_pci.c b/arch/ppc/platforms/prep_pci.c index efc80da43626..aa90aa4f2445 100644 --- a/arch/ppc/platforms/prep_pci.c +++ b/arch/ppc/platforms/prep_pci.c @@ -1171,38 +1171,52 @@ void __init prep_pcibios_fixup(void) { struct pci_dev *dev = NULL; + int irq; + int have_openpic = (OpenPIC_Addr != NULL); prep_route_pci_interrupts(); printk("Setting PCI interrupts for a \"%s\"\n", Motherboard_map_name); - if (OpenPIC_Addr) { - /* PCI interrupts are controlled by the OpenPIC */ - while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { - if (dev->bus->number == 0) { - dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]); - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - } else { - if (Motherboard_non0 != NULL) - Motherboard_non0(dev); - } - } - - /* Setup the Winbond or Via PIB */ - prep_pib_init(); - - return; - } - dev = NULL; + /* Iterate through all the PCI devices, setting the IRQ */ while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { /* - * Use our old hard-coded kludge to figure out what - * irq this device uses. This is necessary on things - * without residual data. -- Cort + * If we have residual data, then this is easy: query the + * residual data for the IRQ line allocated to the device. + * This works the same whether we have an OpenPic or not. + */ + if (have_residual_data) { + irq = residual_pcidev_irq(dev); + dev->irq = have_openpic ? openpic_to_irq(irq) : irq; + } + /* + * If we don't have residual data, then we need to use + * tables to determine the IRQ. The table organisation + * is different depending on whether there is an OpenPIC + * or not. The tables are only used for bus 0, so check + * this first. + */ + else if (dev->bus->number == 0) { + irq = Motherboard_map[PCI_SLOT(dev->devfn)]; + dev->irq = have_openpic ? openpic_to_irq(irq) + : Motherboard_routes[irq]; + } + /* + * Finally, if we don't have residual data and the bus is + * non-zero, use the callback (if provided) */ - unsigned char d = PCI_SLOT(dev->devfn); - dev->irq = Motherboard_routes[Motherboard_map[d]]; + else { + if (Motherboard_non0 != NULL) + Motherboard_non0(dev); + + continue; + } + + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); } + + /* Setup the Winbond or Via PIB */ + prep_pib_init(); } static void __init diff --git a/arch/ppc/platforms/residual.c b/arch/ppc/platforms/residual.c index 5c29bf7b3490..317b93e33b15 100644 --- a/arch/ppc/platforms/residual.c +++ b/arch/ppc/platforms/residual.c @@ -827,6 +827,66 @@ PPC_DEVICE __init *residual_find_device_id(unsigned long BusMask, return NULL; } +static int __init +residual_scan_pcibridge(PnP_TAG_PACKET * pkt, struct pci_dev *dev) +{ + int irq = -1; + +#define data pkt->L4_Pack.L4_Data.L4_PPCPack.PPCData + if (dev->bus->number == data[16]) { + int i, size; + + size = 3 + ld_le16((u_short *) (&pkt->L4_Pack.Count0)); + for (i = 20; i < size - 4; i += 12) { + unsigned char pin; + int line_irq; + + if (dev->devfn != data[i + 1]) + continue; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + line_irq = ld_le16((unsigned short *) + (&data[i + 4 + 2 * (pin - 1)])); + irq = (line_irq == 0xffff) ? 0 + : line_irq & 0x7fff; + } else + irq = 0; + + break; + } + } +#undef data + + return irq; +} + +int __init +residual_pcidev_irq(struct pci_dev *dev) +{ + int i = 0; + int irq = -1; + PPC_DEVICE *bridge; + + while ((bridge = residual_find_device + (-1, NULL, BridgeController, PCIBridge, -1, i++))) { + + PnP_TAG_PACKET *pkt; + if (bridge->AllocatedOffset) { + pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + + bridge->AllocatedOffset, 3, 0); + if (!pkt) + continue; + + irq = residual_scan_pcibridge(pkt, dev); + if (irq != -1) + break; + } + } + + return (irq < 0) ? 0 : irq; +} + PnP_TAG_PACKET *PnP_find_packet(unsigned char *p, unsigned packet_tag, int n) diff --git a/include/asm-ppc/residual.h b/include/asm-ppc/residual.h index 1ab4642692c5..2f466a50ff24 100644 --- a/include/asm-ppc/residual.h +++ b/include/asm-ppc/residual.h @@ -315,11 +315,18 @@ typedef struct _RESIDUAL { } RESIDUAL; +/* + * Forward declaration - we can't include <linux/pci.h> because it + * breaks the boot loader + */ +struct pci_dev; + extern RESIDUAL *res; extern void print_residual_device_info(void); extern PPC_DEVICE *residual_find_device(unsigned long BusMask, unsigned char * DevID, int BaseType, int SubType, int Interface, int n); +extern int residual_pcidev_irq(struct pci_dev *dev); extern PnP_TAG_PACKET *PnP_find_packet(unsigned char *p, unsigned packet_tag, int n); extern PnP_TAG_PACKET *PnP_find_small_vendor_packet(unsigned char *p, |
