/* * arch/ppc/platforms/adir_pci.c * * PCI support for SBS Adirondack * * By Michael Sokolov * based on the K2 version by Matt Porter */ #include #include #include #include #include #include #include #include #include #include #include "adir.h" #undef DEBUG #ifdef DEBUG #define DBG(x...) printk(x) #else #define DBG(x...) #endif /* DEBUG */ static inline int __init adir_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) { #define PCIIRQ(a,b,c,d) {ADIR_IRQ_##a,ADIR_IRQ_##b,ADIR_IRQ_##c,ADIR_IRQ_##d}, struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); /* * The three PCI devices on the motherboard have dedicated lines to the * CPLD interrupt controller, bypassing the standard PCI INTA-D and the * PC interrupt controller. All other PCI devices (slots) have usual * staggered INTA-D lines, resulting in 8 lines total (PCI0 INTA-D and * PCI1 INTA-D). All 8 go to the CPLD interrupt controller. PCI0 INTA-D * also go to the south bridge, so we have the option of taking them * via the CPLD interrupt controller or via the south bridge 8259 * 8258 thingy. PCI1 INTA-D can only be taken via the CPLD interrupt * controller. We take all PCI interrupts via the CPLD interrupt * controller as recommended by SBS. * * We also have some monkey business with the PCI devices within the * VT82C686B south bridge itself. This chip actually has 7 functions on * its IDSEL. Function 0 is the actual south bridge, function 1 is IDE, * and function 4 is some special stuff. The other 4 functions are just * regular PCI devices bundled in the chip. 2 and 3 are USB UHCIs and 5 * and 6 are audio (not supported on the Adirondack). * * This is where the monkey business begins. PCI devices are supposed * to signal normal PCI interrupts. But the 4 functions in question are * located in the south bridge chip, which is designed with the * assumption that it will be fielding PCI INTA-D interrupts rather * than generating them. Here's what it does. Each of the functions in * question routes its interrupt to one of the IRQs on the 8259 thingy. * Which one? It looks at the Interrupt Line register in the PCI config * space, even though the PCI spec says it's for BIOS/OS interaction * only. * * How do we deal with this? We take these interrupts via 8259 IRQs as * we have to. We return the desired IRQ numbers from this routine when * called for the functions in question. The PCI scan code will then * stick our return value into the Interrupt Line register in the PCI * config space, and the interrupt will actually go there. We identify * these functions within the south bridge IDSEL by their interrupt pin * numbers, as the VT82C686B has 04 in the Interrupt Pin register for * USB and 03 for audio. */ if (!hose->index) { static char pci_irq_table[][4] = /* * PCI IDSEL/INTPIN->INTLINE * A B C D */ { /* south bridge */ PCIIRQ(IDE0, NONE, VIA_AUDIO, VIA_USB) /* Ethernet 0 */ PCIIRQ(MBETH0, MBETH0, MBETH0, MBETH0) /* PCI0 slot 1 */ PCIIRQ(PCI0_INTB, PCI0_INTC, PCI0_INTD, PCI0_INTA) /* PCI0 slot 2 */ PCIIRQ(PCI0_INTC, PCI0_INTD, PCI0_INTA, PCI0_INTB) /* PCI0 slot 3 */ PCIIRQ(PCI0_INTD, PCI0_INTA, PCI0_INTB, PCI0_INTC) }; const long min_idsel = 3, max_idsel = 7, irqs_per_slot = 4; return PCI_IRQ_TABLE_LOOKUP; } else { static char pci_irq_table[][4] = /* * PCI IDSEL/INTPIN->INTLINE * A B C D */ { /* Ethernet 1 */ PCIIRQ(MBETH1, MBETH1, MBETH1, MBETH1) /* SCSI */ PCIIRQ(MBSCSI, MBSCSI, MBSCSI, MBSCSI) /* PCI1 slot 1 */ PCIIRQ(PCI1_INTB, PCI1_INTC, PCI1_INTD, PCI1_INTA) /* PCI1 slot 2 */ PCIIRQ(PCI1_INTC, PCI1_INTD, PCI1_INTA, PCI1_INTB) /* PCI1 slot 3 */ PCIIRQ(PCI1_INTD, PCI1_INTA, PCI1_INTB, PCI1_INTC) }; const long min_idsel = 3, max_idsel = 7, irqs_per_slot = 4; return PCI_IRQ_TABLE_LOOKUP; } #undef PCIIRQ } static void adir_pcibios_fixup_resources(struct pci_dev *dev) { int i; if ((dev->vendor == PCI_VENDOR_ID_IBM) && (dev->device == PCI_DEVICE_ID_IBM_CPC710_PCI64)) { DBG("Fixup CPC710 resources\n"); for (i=0; iresource[i].start = 0; dev->resource[i].end = 0; } } } /* * CPC710 DD3 has an errata causing it to hang the system if a type 0 config * cycle is attempted on its PCI32 interface with a device number > 21. * CPC710's PCI bridges map device numbers 1 through 21 to AD11 through AD31. * Per the PCI spec it MUST accept all other device numbers and do nothing, and * software MUST scan all device numbers without assuming how IDSELs are * mapped. However, as the CPC710 DD3's errata causes such correct scanning * procedure to hang the system, we have no choice but to introduce this hack * of knowingly avoiding device numbers > 21 on PCI0, */ static int adir_exclude_device(u_char bus, u_char devfn) { if ((bus == 0) && (PCI_SLOT(devfn) > 21)) return PCIBIOS_DEVICE_NOT_FOUND; else return PCIBIOS_SUCCESSFUL; } void adir_find_bridges(void) { struct pci_controller *hose_a, *hose_b; /* Setup PCI32 hose */ hose_a = pcibios_alloc_controller(); if (!hose_a) return; hose_a->first_busno = 0; hose_a->last_busno = 0xff; hose_a->pci_mem_offset = ADIR_PCI32_MEM_BASE; hose_a->io_space.start = 0; hose_a->io_space.end = ADIR_PCI32_VIRT_IO_SIZE - 1; hose_a->mem_space.start = 0; hose_a->mem_space.end = ADIR_PCI32_MEM_SIZE - 1; hose_a->io_resource.start = 0; hose_a->io_resource.end = ADIR_PCI32_VIRT_IO_SIZE - 1; hose_a->io_resource.flags = IORESOURCE_IO; hose_a->mem_resources[0].start = ADIR_PCI32_MEM_BASE; hose_a->mem_resources[0].end = ADIR_PCI32_MEM_BASE + ADIR_PCI32_MEM_SIZE - 1; hose_a->mem_resources[0].flags = IORESOURCE_MEM; hose_a->io_base_phys = ADIR_PCI32_IO_BASE; hose_a->io_base_virt = (void *) ADIR_PCI32_VIRT_IO_BASE; ppc_md.pci_exclude_device = adir_exclude_device; setup_indirect_pci(hose_a, ADIR_PCI32_CONFIG_ADDR, ADIR_PCI32_CONFIG_DATA); /* Initialize PCI32 bus registers */ early_write_config_byte(hose_a, hose_a->first_busno, PCI_DEVFN(0, 0), CPC710_BUS_NUMBER, hose_a->first_busno); early_write_config_byte(hose_a, hose_a->first_busno, PCI_DEVFN(0, 0), CPC710_SUB_BUS_NUMBER, hose_a->last_busno); hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); /* Write out correct max subordinate bus number for hose A */ early_write_config_byte(hose_a, hose_a->first_busno, PCI_DEVFN(0, 0), CPC710_SUB_BUS_NUMBER, hose_a->last_busno); /* Setup PCI64 hose */ hose_b = pcibios_alloc_controller(); if (!hose_b) return; hose_b->first_busno = hose_a->last_busno + 1; hose_b->last_busno = 0xff; hose_b->pci_mem_offset = ADIR_PCI64_MEM_BASE; hose_b->io_space.start = 0; hose_b->io_space.end = ADIR_PCI64_VIRT_IO_SIZE - 1; hose_b->mem_space.start = 0; hose_b->mem_space.end = ADIR_PCI64_MEM_SIZE - 1; hose_b->io_resource.start = 0; hose_b->io_resource.end = ADIR_PCI64_VIRT_IO_SIZE - 1; hose_b->io_resource.flags = IORESOURCE_IO; hose_b->mem_resources[0].start = ADIR_PCI64_MEM_BASE; hose_b->mem_resources[0].end = ADIR_PCI64_MEM_BASE + ADIR_PCI64_MEM_SIZE - 1; hose_b->mem_resources[0].flags = IORESOURCE_MEM; hose_b->io_base_phys = ADIR_PCI64_IO_BASE; hose_b->io_base_virt = (void *) ADIR_PCI64_VIRT_IO_BASE; setup_indirect_pci(hose_b, ADIR_PCI64_CONFIG_ADDR, ADIR_PCI64_CONFIG_DATA); /* Initialize PCI64 bus registers */ early_write_config_byte(hose_b, 0, PCI_DEVFN(0, 0), CPC710_SUB_BUS_NUMBER, 0xff); early_write_config_byte(hose_b, 0, PCI_DEVFN(0, 0), CPC710_BUS_NUMBER, hose_b->first_busno); hose_b->last_busno = pciauto_bus_scan(hose_b, hose_b->first_busno); /* Write out correct max subordinate bus number for hose B */ early_write_config_byte(hose_b, hose_b->first_busno, PCI_DEVFN(0, 0), CPC710_SUB_BUS_NUMBER, hose_b->last_busno); ppc_md.pcibios_fixup = NULL; ppc_md.pcibios_fixup_resources = adir_pcibios_fixup_resources; ppc_md.pci_swizzle = common_swizzle; ppc_md.pci_map_irq = adir_map_irq; }