diff --git a/arch/ppc64/kernel/maple_pci.c b/arch/ppc64/kernel/maple_pci.c index b901470f55cc..633324b5e61b 100644 --- a/arch/ppc64/kernel/maple_pci.c +++ b/arch/ppc64/kernel/maple_pci.c @@ -359,7 +359,7 @@ static int __init add_bridge(struct device_node *dev) /* Interpret the "ranges" property */ /* This also maps the I/O region and sets isa_io/mem_base */ - pci_process_bridge_OF_ranges(hose, dev); + pci_process_bridge_OF_ranges(hose, dev, primary); pci_setup_phb_io(hose, primary); /* Fixup "bus-range" OF property */ diff --git a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c index feec06bbafc3..b2fb6746f00b 100644 --- a/arch/ppc64/kernel/pci.c +++ b/arch/ppc64/kernel/pci.c @@ -880,9 +880,9 @@ static void __devinit pci_process_ISA_OF_ranges(struct device_node *isa_node, } void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, - struct device_node *dev) + struct device_node *dev, int prim) { - unsigned int *ranges; + unsigned int *ranges, pci_space; unsigned long size; int rlen = 0; int memno = 0; @@ -905,16 +905,39 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, ranges = (unsigned int *) get_property(dev, "ranges", &rlen); while ((rlen -= np * sizeof(unsigned int)) >= 0) { res = NULL; - pci_addr = (unsigned long)ranges[1] << 32 | ranges[2]; + pci_space = ranges[0]; + pci_addr = ((unsigned long)ranges[1] << 32) | ranges[2]; cpu_phys_addr = ranges[3]; - if (na == 2) - cpu_phys_addr = cpu_phys_addr << 32 | ranges[4]; + if (na >= 2) + cpu_phys_addr = (cpu_phys_addr << 32) | ranges[4]; - size = (unsigned long)ranges[na+3] << 32 | ranges[na+4]; + size = ((unsigned long)ranges[na+3] << 32) | ranges[na+4]; + ranges += np; if (size == 0) continue; - switch ((ranges[0] >> 24) & 0x3) { + + /* Now consume following elements while they are contiguous */ + while (rlen >= np * sizeof(unsigned int)) { + unsigned long addr, phys; + + if (ranges[0] != pci_space) + break; + addr = ((unsigned long)ranges[1] << 32) | ranges[2]; + phys = ranges[3]; + if (na >= 2) + phys = (phys << 32) | ranges[4]; + if (addr != pci_addr + size || + phys != cpu_phys_addr + size) + break; + + size += ((unsigned long)ranges[na+3] << 32) + | ranges[na+4]; + ranges += np; + rlen -= np * sizeof(unsigned int); + } + + switch ((pci_space >> 24) & 0x3) { case 1: /* I/O space */ hose->io_base_phys = cpu_phys_addr; hose->pci_io_size = size; @@ -948,7 +971,6 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, res->sibling = NULL; res->child = NULL; } - ranges += np; } } diff --git a/arch/ppc64/kernel/rtas_pci.c b/arch/ppc64/kernel/rtas_pci.c index 20361bcd8cfb..3ad15c90fbbd 100644 --- a/arch/ppc64/kernel/rtas_pci.c +++ b/arch/ppc64/kernel/rtas_pci.c @@ -400,7 +400,7 @@ unsigned long __init find_and_init_phbs(void) if (!phb) continue; - pci_process_bridge_OF_ranges(phb, node); + pci_process_bridge_OF_ranges(phb, node, 0); pci_setup_phb_io(phb, index == 0); #ifdef CONFIG_PPC_PSERIES if (ppc64_interrupt_controller == IC_OPEN_PIC && pSeries_mpic) { @@ -450,7 +450,7 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) if (!phb) return NULL; - pci_process_bridge_OF_ranges(phb, dn); + pci_process_bridge_OF_ranges(phb, dn, primary); pci_setup_phb_io_dynamic(phb, primary); of_node_put(root); diff --git a/include/asm-ppc64/pci-bridge.h b/include/asm-ppc64/pci-bridge.h index 7d8ecb507a3d..60cf8c838af0 100644 --- a/include/asm-ppc64/pci-bridge.h +++ b/include/asm-ppc64/pci-bridge.h @@ -123,7 +123,7 @@ static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) } extern void pci_process_bridge_OF_ranges(struct pci_controller *hose, - struct device_node *dev); + struct device_node *dev, int primary); extern int pcibios_remove_root_bus(struct pci_controller *phb);