summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@kernel.crashing.org>2004-08-15 20:35:18 -0700
committerTom Rini <trini@kernel.crashing.org>2004-08-15 20:35:18 -0700
commitad2d538c50e7163dcc90e374195d55d06f8ff978 (patch)
tree4fd04f9247378f8f83dae5b0126d3d3449a92207
parent96b89248356c51996c3b2693c6c8d5412f17bda1 (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.c60
-rw-r--r--arch/ppc/platforms/residual.c60
-rw-r--r--include/asm-ppc/residual.h7
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,