Message ID | 1365693969-23907-2-git-send-email-Andrew.Murray@arm.com |
---|---|
State | Not Applicable |
Headers | show |
Michal, Ben, Would you have some time to look at this patch and give your comments and/or ACK ? Since it touches the PowerPC and Microblaze core code, we need your agreement to merge this code, and quite a bit of code pending for 3.10 depends on this patch. Rob, alternatively, could we imagine doing a different version of the 'of/pci: Provide support for parsing PCI DT ranges property' that introduces the new API only, leaving the PowerPC and Microblaze rework as follow-up efforts, so that all the PCIe drivers that depend on this patch can get in for 3.10 ? I'd find it pretty sad if the Marvell PCIe driver that has been worked on since 4+ months does not get into 3.10 just because this patch cannot be merged. Thanks! Thomas On Thu, 11 Apr 2013 16:26:07 +0100, Andrew Murray wrote: > The pci_process_bridge_OF_ranges function, used to parse the "ranges" > property of a PCI host device, is found in both Microblaze and PowerPC > architectures. These implementations are nearly identical. This patch > moves this common code to a common place. > > Signed-off-by: Andrew Murray <Andrew.Murray@arm.com> > Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com> > Reviewed-by: Rob Herring <rob.herring@calxeda.com> > Tested-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > --- > arch/microblaze/include/asm/pci-bridge.h | 5 +- > arch/microblaze/pci/pci-common.c | 192 ---------------------------- > arch/powerpc/include/asm/pci-bridge.h | 5 +- > arch/powerpc/kernel/pci-common.c | 192 ---------------------------- > drivers/of/of_pci.c | 200 ++++++++++++++++++++++++++++++ > include/linux/of_pci.h | 4 + > 6 files changed, 206 insertions(+), 392 deletions(-) > > diff --git a/arch/microblaze/include/asm/pci-bridge.h b/arch/microblaze/include/asm/pci-bridge.h > index cb5d397..5783cd6 100644 > --- a/arch/microblaze/include/asm/pci-bridge.h > +++ b/arch/microblaze/include/asm/pci-bridge.h > @@ -10,6 +10,7 @@ > #include <linux/pci.h> > #include <linux/list.h> > #include <linux/ioport.h> > +#include <linux/of_pci.h> > > struct device_node; > > @@ -132,10 +133,6 @@ extern void setup_indirect_pci(struct pci_controller *hose, > extern struct pci_controller *pci_find_hose_for_OF_device( > struct device_node *node); > > -/* Fill up host controller resources from the OF node */ > -extern void pci_process_bridge_OF_ranges(struct pci_controller *hose, > - struct device_node *dev, int primary); > - > /* Allocate & free a PCI host bridge structure */ > extern struct pci_controller *pcibios_alloc_controller(struct device_node *dev); > extern void pcibios_free_controller(struct pci_controller *phb); > diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c > index 9ea521e..2735ad9 100644 > --- a/arch/microblaze/pci/pci-common.c > +++ b/arch/microblaze/pci/pci-common.c > @@ -622,198 +622,6 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, > *end = rsrc->end - offset; > } > > -/** > - * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree > - * @hose: newly allocated pci_controller to be setup > - * @dev: device node of the host bridge > - * @primary: set if primary bus (32 bits only, soon to be deprecated) > - * > - * This function will parse the "ranges" property of a PCI host bridge device > - * node and setup the resource mapping of a pci controller based on its > - * content. > - * > - * Life would be boring if it wasn't for a few issues that we have to deal > - * with here: > - * > - * - We can only cope with one IO space range and up to 3 Memory space > - * ranges. However, some machines (thanks Apple !) tend to split their > - * space into lots of small contiguous ranges. So we have to coalesce. > - * > - * - We can only cope with all memory ranges having the same offset > - * between CPU addresses and PCI addresses. Unfortunately, some bridges > - * are setup for a large 1:1 mapping along with a small "window" which > - * maps PCI address 0 to some arbitrary high address of the CPU space in > - * order to give access to the ISA memory hole. > - * The way out of here that I've chosen for now is to always set the > - * offset based on the first resource found, then override it if we > - * have a different offset and the previous was set by an ISA hole. > - * > - * - Some busses have IO space not starting at 0, which causes trouble with > - * the way we do our IO resource renumbering. The code somewhat deals with > - * it for 64 bits but I would expect problems on 32 bits. > - * > - * - Some 32 bits platforms such as 4xx can have physical space larger than > - * 32 bits so we need to use 64 bits values for the parsing > - */ > -void pci_process_bridge_OF_ranges(struct pci_controller *hose, > - struct device_node *dev, int primary) > -{ > - const u32 *ranges; > - int rlen; > - int pna = of_n_addr_cells(dev); > - int np = pna + 5; > - int memno = 0, isa_hole = -1; > - u32 pci_space; > - unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size; > - unsigned long long isa_mb = 0; > - struct resource *res; > - > - pr_info("PCI host bridge %s %s ranges:\n", > - dev->full_name, primary ? "(primary)" : ""); > - > - /* Get ranges property */ > - ranges = of_get_property(dev, "ranges", &rlen); > - if (ranges == NULL) > - return; > - > - /* Parse it */ > - pr_debug("Parsing ranges property...\n"); > - while ((rlen -= np * 4) >= 0) { > - /* Read next ranges element */ > - pci_space = ranges[0]; > - pci_addr = of_read_number(ranges + 1, 2); > - cpu_addr = of_translate_address(dev, ranges + 3); > - size = of_read_number(ranges + pna + 3, 2); > - > - pr_debug("pci_space: 0x%08x pci_addr:0x%016llx ", > - pci_space, pci_addr); > - pr_debug("cpu_addr:0x%016llx size:0x%016llx\n", > - cpu_addr, size); > - > - ranges += np; > - > - /* If we failed translation or got a zero-sized region > - * (some FW try to feed us with non sensical zero sized regions > - * such as power3 which look like some kind of attempt > - * at exposing the VGA memory hole) > - */ > - if (cpu_addr == OF_BAD_ADDR || size == 0) > - continue; > - > - /* Now consume following elements while they are contiguous */ > - for (; rlen >= np * sizeof(u32); > - ranges += np, rlen -= np * 4) { > - if (ranges[0] != pci_space) > - break; > - pci_next = of_read_number(ranges + 1, 2); > - cpu_next = of_translate_address(dev, ranges + 3); > - if (pci_next != pci_addr + size || > - cpu_next != cpu_addr + size) > - break; > - size += of_read_number(ranges + pna + 3, 2); > - } > - > - /* Act based on address space type */ > - res = NULL; > - switch ((pci_space >> 24) & 0x3) { > - case 1: /* PCI IO space */ > - pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n", > - cpu_addr, cpu_addr + size - 1, pci_addr); > - > - /* We support only one IO range */ > - if (hose->pci_io_size) { > - pr_info(" \\--> Skipped (too many) !\n"); > - continue; > - } > - /* On 32 bits, limit I/O space to 16MB */ > - if (size > 0x01000000) > - size = 0x01000000; > - > - /* 32 bits needs to map IOs here */ > - hose->io_base_virt = ioremap(cpu_addr, size); > - > - /* Expect trouble if pci_addr is not 0 */ > - if (primary) > - isa_io_base = > - (unsigned long)hose->io_base_virt; > - /* pci_io_size and io_base_phys always represent IO > - * space starting at 0 so we factor in pci_addr > - */ > - hose->pci_io_size = pci_addr + size; > - hose->io_base_phys = cpu_addr - pci_addr; > - > - /* Build resource */ > - res = &hose->io_resource; > - res->flags = IORESOURCE_IO; > - res->start = pci_addr; > - break; > - case 2: /* PCI Memory space */ > - case 3: /* PCI 64 bits Memory space */ > - pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", > - cpu_addr, cpu_addr + size - 1, pci_addr, > - (pci_space & 0x40000000) ? "Prefetch" : ""); > - > - /* We support only 3 memory ranges */ > - if (memno >= 3) { > - pr_info(" \\--> Skipped (too many) !\n"); > - continue; > - } > - /* Handles ISA memory hole space here */ > - if (pci_addr == 0) { > - isa_mb = cpu_addr; > - isa_hole = memno; > - if (primary || isa_mem_base == 0) > - isa_mem_base = cpu_addr; > - hose->isa_mem_phys = cpu_addr; > - hose->isa_mem_size = size; > - } > - > - /* We get the PCI/Mem offset from the first range or > - * the, current one if the offset came from an ISA > - * hole. If they don't match, bugger. > - */ > - if (memno == 0 || > - (isa_hole >= 0 && pci_addr != 0 && > - hose->pci_mem_offset == isa_mb)) > - hose->pci_mem_offset = cpu_addr - pci_addr; > - else if (pci_addr != 0 && > - hose->pci_mem_offset != cpu_addr - pci_addr) { > - pr_info(" \\--> Skipped (offset mismatch) !\n"); > - continue; > - } > - > - /* Build resource */ > - res = &hose->mem_resources[memno++]; > - res->flags = IORESOURCE_MEM; > - if (pci_space & 0x40000000) > - res->flags |= IORESOURCE_PREFETCH; > - res->start = cpu_addr; > - break; > - } > - if (res != NULL) { > - res->name = dev->full_name; > - res->end = res->start + size - 1; > - res->parent = NULL; > - res->sibling = NULL; > - res->child = NULL; > - } > - } > - > - /* If there's an ISA hole and the pci_mem_offset is -not- matching > - * the ISA hole offset, then we need to remove the ISA hole from > - * the resource list for that brige > - */ > - if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) { > - unsigned int next = isa_hole + 1; > - pr_info(" Removing ISA hole at 0x%016llx\n", isa_mb); > - if (next < memno) > - memmove(&hose->mem_resources[isa_hole], > - &hose->mem_resources[next], > - sizeof(struct resource) * (memno - next)); > - hose->mem_resources[--memno].flags = 0; > - } > -} > - > /* Decide whether to display the domain number in /proc */ > int pci_proc_domain(struct pci_bus *bus) > { > diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h > index 025a130..205bfba 100644 > --- a/arch/powerpc/include/asm/pci-bridge.h > +++ b/arch/powerpc/include/asm/pci-bridge.h > @@ -10,6 +10,7 @@ > #include <linux/pci.h> > #include <linux/list.h> > #include <linux/ioport.h> > +#include <linux/of_pci.h> > #include <asm-generic/pci-bridge.h> > > struct device_node; > @@ -231,10 +232,6 @@ extern int pcibios_map_io_space(struct pci_bus *bus); > extern struct pci_controller *pci_find_hose_for_OF_device( > struct device_node* node); > > -/* Fill up host controller resources from the OF node */ > -extern void pci_process_bridge_OF_ranges(struct pci_controller *hose, > - struct device_node *dev, int primary); > - > /* Allocate & free a PCI host bridge structure */ > extern struct pci_controller *pcibios_alloc_controller(struct device_node *dev); > extern void pcibios_free_controller(struct pci_controller *phb); > diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c > index fa12ae4..6edf396 100644 > --- a/arch/powerpc/kernel/pci-common.c > +++ b/arch/powerpc/kernel/pci-common.c > @@ -640,198 +640,6 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, > *end = rsrc->end - offset; > } > > -/** > - * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree > - * @hose: newly allocated pci_controller to be setup > - * @dev: device node of the host bridge > - * @primary: set if primary bus (32 bits only, soon to be deprecated) > - * > - * This function will parse the "ranges" property of a PCI host bridge device > - * node and setup the resource mapping of a pci controller based on its > - * content. > - * > - * Life would be boring if it wasn't for a few issues that we have to deal > - * with here: > - * > - * - We can only cope with one IO space range and up to 3 Memory space > - * ranges. However, some machines (thanks Apple !) tend to split their > - * space into lots of small contiguous ranges. So we have to coalesce. > - * > - * - We can only cope with all memory ranges having the same offset > - * between CPU addresses and PCI addresses. Unfortunately, some bridges > - * are setup for a large 1:1 mapping along with a small "window" which > - * maps PCI address 0 to some arbitrary high address of the CPU space in > - * order to give access to the ISA memory hole. > - * The way out of here that I've chosen for now is to always set the > - * offset based on the first resource found, then override it if we > - * have a different offset and the previous was set by an ISA hole. > - * > - * - Some busses have IO space not starting at 0, which causes trouble with > - * the way we do our IO resource renumbering. The code somewhat deals with > - * it for 64 bits but I would expect problems on 32 bits. > - * > - * - Some 32 bits platforms such as 4xx can have physical space larger than > - * 32 bits so we need to use 64 bits values for the parsing > - */ > -void pci_process_bridge_OF_ranges(struct pci_controller *hose, > - struct device_node *dev, int primary) > -{ > - const u32 *ranges; > - int rlen; > - int pna = of_n_addr_cells(dev); > - int np = pna + 5; > - int memno = 0, isa_hole = -1; > - u32 pci_space; > - unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size; > - unsigned long long isa_mb = 0; > - struct resource *res; > - > - printk(KERN_INFO "PCI host bridge %s %s ranges:\n", > - dev->full_name, primary ? "(primary)" : ""); > - > - /* Get ranges property */ > - ranges = of_get_property(dev, "ranges", &rlen); > - if (ranges == NULL) > - return; > - > - /* Parse it */ > - while ((rlen -= np * 4) >= 0) { > - /* Read next ranges element */ > - pci_space = ranges[0]; > - pci_addr = of_read_number(ranges + 1, 2); > - cpu_addr = of_translate_address(dev, ranges + 3); > - size = of_read_number(ranges + pna + 3, 2); > - ranges += np; > - > - /* If we failed translation or got a zero-sized region > - * (some FW try to feed us with non sensical zero sized regions > - * such as power3 which look like some kind of attempt at exposing > - * the VGA memory hole) > - */ > - if (cpu_addr == OF_BAD_ADDR || size == 0) > - continue; > - > - /* Now consume following elements while they are contiguous */ > - for (; rlen >= np * sizeof(u32); > - ranges += np, rlen -= np * 4) { > - if (ranges[0] != pci_space) > - break; > - pci_next = of_read_number(ranges + 1, 2); > - cpu_next = of_translate_address(dev, ranges + 3); > - if (pci_next != pci_addr + size || > - cpu_next != cpu_addr + size) > - break; > - size += of_read_number(ranges + pna + 3, 2); > - } > - > - /* Act based on address space type */ > - res = NULL; > - switch ((pci_space >> 24) & 0x3) { > - case 1: /* PCI IO space */ > - printk(KERN_INFO > - " IO 0x%016llx..0x%016llx -> 0x%016llx\n", > - cpu_addr, cpu_addr + size - 1, pci_addr); > - > - /* We support only one IO range */ > - if (hose->pci_io_size) { > - printk(KERN_INFO > - " \\--> Skipped (too many) !\n"); > - continue; > - } > -#ifdef CONFIG_PPC32 > - /* On 32 bits, limit I/O space to 16MB */ > - if (size > 0x01000000) > - size = 0x01000000; > - > - /* 32 bits needs to map IOs here */ > - hose->io_base_virt = ioremap(cpu_addr, size); > - > - /* Expect trouble if pci_addr is not 0 */ > - if (primary) > - isa_io_base = > - (unsigned long)hose->io_base_virt; > -#endif /* CONFIG_PPC32 */ > - /* pci_io_size and io_base_phys always represent IO > - * space starting at 0 so we factor in pci_addr > - */ > - hose->pci_io_size = pci_addr + size; > - hose->io_base_phys = cpu_addr - pci_addr; > - > - /* Build resource */ > - res = &hose->io_resource; > - res->flags = IORESOURCE_IO; > - res->start = pci_addr; > - break; > - case 2: /* PCI Memory space */ > - case 3: /* PCI 64 bits Memory space */ > - printk(KERN_INFO > - " MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", > - cpu_addr, cpu_addr + size - 1, pci_addr, > - (pci_space & 0x40000000) ? "Prefetch" : ""); > - > - /* We support only 3 memory ranges */ > - if (memno >= 3) { > - printk(KERN_INFO > - " \\--> Skipped (too many) !\n"); > - continue; > - } > - /* Handles ISA memory hole space here */ > - if (pci_addr == 0) { > - isa_mb = cpu_addr; > - isa_hole = memno; > - if (primary || isa_mem_base == 0) > - isa_mem_base = cpu_addr; > - hose->isa_mem_phys = cpu_addr; > - hose->isa_mem_size = size; > - } > - > - /* We get the PCI/Mem offset from the first range or > - * the, current one if the offset came from an ISA > - * hole. If they don't match, bugger. > - */ > - if (memno == 0 || > - (isa_hole >= 0 && pci_addr != 0 && > - hose->pci_mem_offset == isa_mb)) > - hose->pci_mem_offset = cpu_addr - pci_addr; > - else if (pci_addr != 0 && > - hose->pci_mem_offset != cpu_addr - pci_addr) { > - printk(KERN_INFO > - " \\--> Skipped (offset mismatch) !\n"); > - continue; > - } > - > - /* Build resource */ > - res = &hose->mem_resources[memno++]; > - res->flags = IORESOURCE_MEM; > - if (pci_space & 0x40000000) > - res->flags |= IORESOURCE_PREFETCH; > - res->start = cpu_addr; > - break; > - } > - if (res != NULL) { > - res->name = dev->full_name; > - res->end = res->start + size - 1; > - res->parent = NULL; > - res->sibling = NULL; > - res->child = NULL; > - } > - } > - > - /* If there's an ISA hole and the pci_mem_offset is -not- matching > - * the ISA hole offset, then we need to remove the ISA hole from > - * the resource list for that brige > - */ > - if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) { > - unsigned int next = isa_hole + 1; > - printk(KERN_INFO " Removing ISA hole at 0x%016llx\n", isa_mb); > - if (next < memno) > - memmove(&hose->mem_resources[isa_hole], > - &hose->mem_resources[next], > - sizeof(struct resource) * (memno - next)); > - hose->mem_resources[--memno].flags = 0; > - } > -} > - > /* Decide whether to display the domain number in /proc */ > int pci_proc_domain(struct pci_bus *bus) > { > diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c > index 13e37e2..1626172 100644 > --- a/drivers/of/of_pci.c > +++ b/drivers/of/of_pci.c > @@ -4,6 +4,10 @@ > #include <linux/of_pci.h> > #include <asm/prom.h> > > +#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) || defined(CONFIG_MICROBLAZE) > +#include <asm/pci-bridge.h> > +#endif > + > static inline int __of_pci_pci_compare(struct device_node *node, > unsigned int devfn) > { > @@ -40,3 +44,199 @@ struct device_node *of_pci_find_child_device(struct device_node *parent, > return NULL; > } > EXPORT_SYMBOL_GPL(of_pci_find_child_device); > + > +/** > + * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree > + * @hose: newly allocated pci_controller to be setup > + * @dev: device node of the host bridge > + * @primary: set if primary bus (32 bits only, soon to be deprecated) > + * > + * This function will parse the "ranges" property of a PCI host bridge device > + * node and setup the resource mapping of a pci controller based on its > + * content. > + * > + * Life would be boring if it wasn't for a few issues that we have to deal > + * with here: > + * > + * - We can only cope with one IO space range and up to 3 Memory space > + * ranges. However, some machines (thanks Apple !) tend to split their > + * space into lots of small contiguous ranges. So we have to coalesce. > + * > + * - We can only cope with all memory ranges having the same offset > + * between CPU addresses and PCI addresses. Unfortunately, some bridges > + * are setup for a large 1:1 mapping along with a small "window" which > + * maps PCI address 0 to some arbitrary high address of the CPU space in > + * order to give access to the ISA memory hole. > + * The way out of here that I've chosen for now is to always set the > + * offset based on the first resource found, then override it if we > + * have a different offset and the previous was set by an ISA hole. > + * > + * - Some busses have IO space not starting at 0, which causes trouble with > + * the way we do our IO resource renumbering. The code somewhat deals with > + * it for 64 bits but I would expect problems on 32 bits. > + * > + * - Some 32 bits platforms such as 4xx can have physical space larger than > + * 32 bits so we need to use 64 bits values for the parsing > + */ > +#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) || defined(CONFIG_MICROBLAZE) > +void pci_process_bridge_OF_ranges(struct pci_controller *hose, > + struct device_node *dev, int primary) > +{ > + const u32 *ranges; > + int rlen; > + int pna = of_n_addr_cells(dev); > + int np = pna + 5; > + int memno = 0, isa_hole = -1; > + u32 pci_space; > + unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size; > + unsigned long long isa_mb = 0; > + struct resource *res; > + > + pr_info("PCI host bridge %s %s ranges:\n", > + dev->full_name, primary ? "(primary)" : ""); > + > + /* Get ranges property */ > + ranges = of_get_property(dev, "ranges", &rlen); > + if (ranges == NULL) > + return; > + > + /* Parse it */ > + pr_debug("Parsing ranges property...\n"); > + while ((rlen -= np * 4) >= 0) { > + /* Read next ranges element */ > + pci_space = ranges[0]; > + pci_addr = of_read_number(ranges + 1, 2); > + cpu_addr = of_translate_address(dev, ranges + 3); > + size = of_read_number(ranges + pna + 3, 2); > + > + pr_debug("pci_space: 0x%08x pci_addr:0x%016llx ", > + pci_space, pci_addr); > + pr_debug("cpu_addr:0x%016llx size:0x%016llx\n", > + cpu_addr, size); > + > + ranges += np; > + > + /* If we failed translation or got a zero-sized region > + * (some FW try to feed us with non sensical zero sized regions > + * such as power3 which look like some kind of attempt > + * at exposing the VGA memory hole) > + */ > + if (cpu_addr == OF_BAD_ADDR || size == 0) > + continue; > + > + /* Now consume following elements while they are contiguous */ > + for (; rlen >= np * sizeof(u32); > + ranges += np, rlen -= np * 4) { > + if (ranges[0] != pci_space) > + break; > + pci_next = of_read_number(ranges + 1, 2); > + cpu_next = of_translate_address(dev, ranges + 3); > + if (pci_next != pci_addr + size || > + cpu_next != cpu_addr + size) > + break; > + size += of_read_number(ranges + pna + 3, 2); > + } > + > + /* Act based on address space type */ > + res = NULL; > + switch ((pci_space >> 24) & 0x3) { > + case 1: /* PCI IO space */ > + pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n", > + cpu_addr, cpu_addr + size - 1, pci_addr); > + > + /* We support only one IO range */ > + if (hose->pci_io_size) { > + pr_info(" \\--> Skipped (too many) !\n"); > + continue; > + } > +#if (!IS_ENABLED(CONFIG_64BIT)) > + /* On 32 bits, limit I/O space to 16MB */ > + if (size > 0x01000000) > + size = 0x01000000; > + > + /* 32 bits needs to map IOs here */ > + hose->io_base_virt = ioremap(cpu_addr, size); > + > + /* Expect trouble if pci_addr is not 0 */ > + if (primary) > + isa_io_base = > + (unsigned long)hose->io_base_virt; > +#endif /* !CONFIG_64BIT */ > + /* pci_io_size and io_base_phys always represent IO > + * space starting at 0 so we factor in pci_addr > + */ > + hose->pci_io_size = pci_addr + size; > + hose->io_base_phys = cpu_addr - pci_addr; > + > + /* Build resource */ > + res = &hose->io_resource; > + res->flags = IORESOURCE_IO; > + res->start = pci_addr; > + break; > + case 2: /* PCI Memory space */ > + case 3: /* PCI 64 bits Memory space */ > + pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", > + cpu_addr, cpu_addr + size - 1, pci_addr, > + (pci_space & 0x40000000) ? "Prefetch" : ""); > + > + /* We support only 3 memory ranges */ > + if (memno >= 3) { > + pr_info(" \\--> Skipped (too many) !\n"); > + continue; > + } > + /* Handles ISA memory hole space here */ > + if (pci_addr == 0) { > + isa_mb = cpu_addr; > + isa_hole = memno; > + if (primary || isa_mem_base == 0) > + isa_mem_base = cpu_addr; > + hose->isa_mem_phys = cpu_addr; > + hose->isa_mem_size = size; > + } > + > + /* We get the PCI/Mem offset from the first range or > + * the, current one if the offset came from an ISA > + * hole. If they don't match, bugger. > + */ > + if (memno == 0 || > + (isa_hole >= 0 && pci_addr != 0 && > + hose->pci_mem_offset == isa_mb)) > + hose->pci_mem_offset = cpu_addr - pci_addr; > + else if (pci_addr != 0 && > + hose->pci_mem_offset != cpu_addr - pci_addr) { > + pr_info(" \\--> Skipped (offset mismatch) !\n"); > + continue; > + } > + > + /* Build resource */ > + res = &hose->mem_resources[memno++]; > + res->flags = IORESOURCE_MEM; > + if (pci_space & 0x40000000) > + res->flags |= IORESOURCE_PREFETCH; > + res->start = cpu_addr; > + break; > + } > + if (res != NULL) { > + res->name = dev->full_name; > + res->end = res->start + size - 1; > + res->parent = NULL; > + res->sibling = NULL; > + res->child = NULL; > + } > + } > + > + /* If there's an ISA hole and the pci_mem_offset is -not- matching > + * the ISA hole offset, then we need to remove the ISA hole from > + * the resource list for that brige > + */ > + if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) { > + unsigned int next = isa_hole + 1; > + pr_info(" Removing ISA hole at 0x%016llx\n", isa_mb); > + if (next < memno) > + memmove(&hose->mem_resources[isa_hole], > + &hose->mem_resources[next], > + sizeof(struct resource) * (memno - next)); > + hose->mem_resources[--memno].flags = 0; > + } > +} > +#endif > diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h > index bb115de..33e8ead 100644 > --- a/include/linux/of_pci.h > +++ b/include/linux/of_pci.h > @@ -11,4 +11,8 @@ struct device_node; > struct device_node *of_pci_find_child_device(struct device_node *parent, > unsigned int devfn); > > +struct pci_controller; > +void pci_process_bridge_OF_ranges(struct pci_controller *hose, > + struct device_node *dev, int primary); > + > #endif
On Mon, 2013-04-15 at 14:57 +0200, Thomas Petazzoni wrote: > Michal, Ben, > > Would you have some time to look at this patch and give your comments > and/or ACK ? Since it touches the PowerPC and Microblaze core code, we > need your agreement to merge this code, and quite a bit of code pending > for 3.10 depends on this patch. I'm currently still on vacation. I will be able to look at this after I'm back in about a week. > Rob, alternatively, could we imagine doing a different version of the > 'of/pci: Provide support for parsing PCI DT ranges property' that > introduces the new API only, leaving the PowerPC and Microblaze rework > as follow-up efforts, so that all the PCIe drivers that depend on this > patch can get in for 3.10 ? I'd find it pretty sad if the Marvell PCIe > driver that has been worked on since 4+ months does not get into 3.10 > just because this patch cannot be merged. Cheers, Ben. > Thanks! > > Thomas > > On Thu, 11 Apr 2013 16:26:07 +0100, Andrew Murray wrote: > > The pci_process_bridge_OF_ranges function, used to parse the "ranges" > > property of a PCI host device, is found in both Microblaze and PowerPC > > architectures. These implementations are nearly identical. This patch > > moves this common code to a common place. > > > > Signed-off-by: Andrew Murray <Andrew.Murray@arm.com> > > Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com> > > Reviewed-by: Rob Herring <rob.herring@calxeda.com> > > Tested-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > > --- > > arch/microblaze/include/asm/pci-bridge.h | 5 +- > > arch/microblaze/pci/pci-common.c | 192 ---------------------------- > > arch/powerpc/include/asm/pci-bridge.h | 5 +- > > arch/powerpc/kernel/pci-common.c | 192 ---------------------------- > > drivers/of/of_pci.c | 200 ++++++++++++++++++++++++++++++ > > include/linux/of_pci.h | 4 + > > 6 files changed, 206 insertions(+), 392 deletions(-) > > > > diff --git a/arch/microblaze/include/asm/pci-bridge.h b/arch/microblaze/include/asm/pci-bridge.h > > index cb5d397..5783cd6 100644 > > --- a/arch/microblaze/include/asm/pci-bridge.h > > +++ b/arch/microblaze/include/asm/pci-bridge.h > > @@ -10,6 +10,7 @@ > > #include <linux/pci.h> > > #include <linux/list.h> > > #include <linux/ioport.h> > > +#include <linux/of_pci.h> > > > > struct device_node; > > > > @@ -132,10 +133,6 @@ extern void setup_indirect_pci(struct pci_controller *hose, > > extern struct pci_controller *pci_find_hose_for_OF_device( > > struct device_node *node); > > > > -/* Fill up host controller resources from the OF node */ > > -extern void pci_process_bridge_OF_ranges(struct pci_controller *hose, > > - struct device_node *dev, int primary); > > - > > /* Allocate & free a PCI host bridge structure */ > > extern struct pci_controller *pcibios_alloc_controller(struct device_node *dev); > > extern void pcibios_free_controller(struct pci_controller *phb); > > diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c > > index 9ea521e..2735ad9 100644 > > --- a/arch/microblaze/pci/pci-common.c > > +++ b/arch/microblaze/pci/pci-common.c > > @@ -622,198 +622,6 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, > > *end = rsrc->end - offset; > > } > > > > -/** > > - * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree > > - * @hose: newly allocated pci_controller to be setup > > - * @dev: device node of the host bridge > > - * @primary: set if primary bus (32 bits only, soon to be deprecated) > > - * > > - * This function will parse the "ranges" property of a PCI host bridge device > > - * node and setup the resource mapping of a pci controller based on its > > - * content. > > - * > > - * Life would be boring if it wasn't for a few issues that we have to deal > > - * with here: > > - * > > - * - We can only cope with one IO space range and up to 3 Memory space > > - * ranges. However, some machines (thanks Apple !) tend to split their > > - * space into lots of small contiguous ranges. So we have to coalesce. > > - * > > - * - We can only cope with all memory ranges having the same offset > > - * between CPU addresses and PCI addresses. Unfortunately, some bridges > > - * are setup for a large 1:1 mapping along with a small "window" which > > - * maps PCI address 0 to some arbitrary high address of the CPU space in > > - * order to give access to the ISA memory hole. > > - * The way out of here that I've chosen for now is to always set the > > - * offset based on the first resource found, then override it if we > > - * have a different offset and the previous was set by an ISA hole. > > - * > > - * - Some busses have IO space not starting at 0, which causes trouble with > > - * the way we do our IO resource renumbering. The code somewhat deals with > > - * it for 64 bits but I would expect problems on 32 bits. > > - * > > - * - Some 32 bits platforms such as 4xx can have physical space larger than > > - * 32 bits so we need to use 64 bits values for the parsing > > - */ > > -void pci_process_bridge_OF_ranges(struct pci_controller *hose, > > - struct device_node *dev, int primary) > > -{ > > - const u32 *ranges; > > - int rlen; > > - int pna = of_n_addr_cells(dev); > > - int np = pna + 5; > > - int memno = 0, isa_hole = -1; > > - u32 pci_space; > > - unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size; > > - unsigned long long isa_mb = 0; > > - struct resource *res; > > - > > - pr_info("PCI host bridge %s %s ranges:\n", > > - dev->full_name, primary ? "(primary)" : ""); > > - > > - /* Get ranges property */ > > - ranges = of_get_property(dev, "ranges", &rlen); > > - if (ranges == NULL) > > - return; > > - > > - /* Parse it */ > > - pr_debug("Parsing ranges property...\n"); > > - while ((rlen -= np * 4) >= 0) { > > - /* Read next ranges element */ > > - pci_space = ranges[0]; > > - pci_addr = of_read_number(ranges + 1, 2); > > - cpu_addr = of_translate_address(dev, ranges + 3); > > - size = of_read_number(ranges + pna + 3, 2); > > - > > - pr_debug("pci_space: 0x%08x pci_addr:0x%016llx ", > > - pci_space, pci_addr); > > - pr_debug("cpu_addr:0x%016llx size:0x%016llx\n", > > - cpu_addr, size); > > - > > - ranges += np; > > - > > - /* If we failed translation or got a zero-sized region > > - * (some FW try to feed us with non sensical zero sized regions > > - * such as power3 which look like some kind of attempt > > - * at exposing the VGA memory hole) > > - */ > > - if (cpu_addr == OF_BAD_ADDR || size == 0) > > - continue; > > - > > - /* Now consume following elements while they are contiguous */ > > - for (; rlen >= np * sizeof(u32); > > - ranges += np, rlen -= np * 4) { > > - if (ranges[0] != pci_space) > > - break; > > - pci_next = of_read_number(ranges + 1, 2); > > - cpu_next = of_translate_address(dev, ranges + 3); > > - if (pci_next != pci_addr + size || > > - cpu_next != cpu_addr + size) > > - break; > > - size += of_read_number(ranges + pna + 3, 2); > > - } > > - > > - /* Act based on address space type */ > > - res = NULL; > > - switch ((pci_space >> 24) & 0x3) { > > - case 1: /* PCI IO space */ > > - pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n", > > - cpu_addr, cpu_addr + size - 1, pci_addr); > > - > > - /* We support only one IO range */ > > - if (hose->pci_io_size) { > > - pr_info(" \\--> Skipped (too many) !\n"); > > - continue; > > - } > > - /* On 32 bits, limit I/O space to 16MB */ > > - if (size > 0x01000000) > > - size = 0x01000000; > > - > > - /* 32 bits needs to map IOs here */ > > - hose->io_base_virt = ioremap(cpu_addr, size); > > - > > - /* Expect trouble if pci_addr is not 0 */ > > - if (primary) > > - isa_io_base = > > - (unsigned long)hose->io_base_virt; > > - /* pci_io_size and io_base_phys always represent IO > > - * space starting at 0 so we factor in pci_addr > > - */ > > - hose->pci_io_size = pci_addr + size; > > - hose->io_base_phys = cpu_addr - pci_addr; > > - > > - /* Build resource */ > > - res = &hose->io_resource; > > - res->flags = IORESOURCE_IO; > > - res->start = pci_addr; > > - break; > > - case 2: /* PCI Memory space */ > > - case 3: /* PCI 64 bits Memory space */ > > - pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", > > - cpu_addr, cpu_addr + size - 1, pci_addr, > > - (pci_space & 0x40000000) ? "Prefetch" : ""); > > - > > - /* We support only 3 memory ranges */ > > - if (memno >= 3) { > > - pr_info(" \\--> Skipped (too many) !\n"); > > - continue; > > - } > > - /* Handles ISA memory hole space here */ > > - if (pci_addr == 0) { > > - isa_mb = cpu_addr; > > - isa_hole = memno; > > - if (primary || isa_mem_base == 0) > > - isa_mem_base = cpu_addr; > > - hose->isa_mem_phys = cpu_addr; > > - hose->isa_mem_size = size; > > - } > > - > > - /* We get the PCI/Mem offset from the first range or > > - * the, current one if the offset came from an ISA > > - * hole. If they don't match, bugger. > > - */ > > - if (memno == 0 || > > - (isa_hole >= 0 && pci_addr != 0 && > > - hose->pci_mem_offset == isa_mb)) > > - hose->pci_mem_offset = cpu_addr - pci_addr; > > - else if (pci_addr != 0 && > > - hose->pci_mem_offset != cpu_addr - pci_addr) { > > - pr_info(" \\--> Skipped (offset mismatch) !\n"); > > - continue; > > - } > > - > > - /* Build resource */ > > - res = &hose->mem_resources[memno++]; > > - res->flags = IORESOURCE_MEM; > > - if (pci_space & 0x40000000) > > - res->flags |= IORESOURCE_PREFETCH; > > - res->start = cpu_addr; > > - break; > > - } > > - if (res != NULL) { > > - res->name = dev->full_name; > > - res->end = res->start + size - 1; > > - res->parent = NULL; > > - res->sibling = NULL; > > - res->child = NULL; > > - } > > - } > > - > > - /* If there's an ISA hole and the pci_mem_offset is -not- matching > > - * the ISA hole offset, then we need to remove the ISA hole from > > - * the resource list for that brige > > - */ > > - if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) { > > - unsigned int next = isa_hole + 1; > > - pr_info(" Removing ISA hole at 0x%016llx\n", isa_mb); > > - if (next < memno) > > - memmove(&hose->mem_resources[isa_hole], > > - &hose->mem_resources[next], > > - sizeof(struct resource) * (memno - next)); > > - hose->mem_resources[--memno].flags = 0; > > - } > > -} > > - > > /* Decide whether to display the domain number in /proc */ > > int pci_proc_domain(struct pci_bus *bus) > > { > > diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h > > index 025a130..205bfba 100644 > > --- a/arch/powerpc/include/asm/pci-bridge.h > > +++ b/arch/powerpc/include/asm/pci-bridge.h > > @@ -10,6 +10,7 @@ > > #include <linux/pci.h> > > #include <linux/list.h> > > #include <linux/ioport.h> > > +#include <linux/of_pci.h> > > #include <asm-generic/pci-bridge.h> > > > > struct device_node; > > @@ -231,10 +232,6 @@ extern int pcibios_map_io_space(struct pci_bus *bus); > > extern struct pci_controller *pci_find_hose_for_OF_device( > > struct device_node* node); > > > > -/* Fill up host controller resources from the OF node */ > > -extern void pci_process_bridge_OF_ranges(struct pci_controller *hose, > > - struct device_node *dev, int primary); > > - > > /* Allocate & free a PCI host bridge structure */ > > extern struct pci_controller *pcibios_alloc_controller(struct device_node *dev); > > extern void pcibios_free_controller(struct pci_controller *phb); > > diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c > > index fa12ae4..6edf396 100644 > > --- a/arch/powerpc/kernel/pci-common.c > > +++ b/arch/powerpc/kernel/pci-common.c > > @@ -640,198 +640,6 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, > > *end = rsrc->end - offset; > > } > > > > -/** > > - * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree > > - * @hose: newly allocated pci_controller to be setup > > - * @dev: device node of the host bridge > > - * @primary: set if primary bus (32 bits only, soon to be deprecated) > > - * > > - * This function will parse the "ranges" property of a PCI host bridge device > > - * node and setup the resource mapping of a pci controller based on its > > - * content. > > - * > > - * Life would be boring if it wasn't for a few issues that we have to deal > > - * with here: > > - * > > - * - We can only cope with one IO space range and up to 3 Memory space > > - * ranges. However, some machines (thanks Apple !) tend to split their > > - * space into lots of small contiguous ranges. So we have to coalesce. > > - * > > - * - We can only cope with all memory ranges having the same offset > > - * between CPU addresses and PCI addresses. Unfortunately, some bridges > > - * are setup for a large 1:1 mapping along with a small "window" which > > - * maps PCI address 0 to some arbitrary high address of the CPU space in > > - * order to give access to the ISA memory hole. > > - * The way out of here that I've chosen for now is to always set the > > - * offset based on the first resource found, then override it if we > > - * have a different offset and the previous was set by an ISA hole. > > - * > > - * - Some busses have IO space not starting at 0, which causes trouble with > > - * the way we do our IO resource renumbering. The code somewhat deals with > > - * it for 64 bits but I would expect problems on 32 bits. > > - * > > - * - Some 32 bits platforms such as 4xx can have physical space larger than > > - * 32 bits so we need to use 64 bits values for the parsing > > - */ > > -void pci_process_bridge_OF_ranges(struct pci_controller *hose, > > - struct device_node *dev, int primary) > > -{ > > - const u32 *ranges; > > - int rlen; > > - int pna = of_n_addr_cells(dev); > > - int np = pna + 5; > > - int memno = 0, isa_hole = -1; > > - u32 pci_space; > > - unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size; > > - unsigned long long isa_mb = 0; > > - struct resource *res; > > - > > - printk(KERN_INFO "PCI host bridge %s %s ranges:\n", > > - dev->full_name, primary ? "(primary)" : ""); > > - > > - /* Get ranges property */ > > - ranges = of_get_property(dev, "ranges", &rlen); > > - if (ranges == NULL) > > - return; > > - > > - /* Parse it */ > > - while ((rlen -= np * 4) >= 0) { > > - /* Read next ranges element */ > > - pci_space = ranges[0]; > > - pci_addr = of_read_number(ranges + 1, 2); > > - cpu_addr = of_translate_address(dev, ranges + 3); > > - size = of_read_number(ranges + pna + 3, 2); > > - ranges += np; > > - > > - /* If we failed translation or got a zero-sized region > > - * (some FW try to feed us with non sensical zero sized regions > > - * such as power3 which look like some kind of attempt at exposing > > - * the VGA memory hole) > > - */ > > - if (cpu_addr == OF_BAD_ADDR || size == 0) > > - continue; > > - > > - /* Now consume following elements while they are contiguous */ > > - for (; rlen >= np * sizeof(u32); > > - ranges += np, rlen -= np * 4) { > > - if (ranges[0] != pci_space) > > - break; > > - pci_next = of_read_number(ranges + 1, 2); > > - cpu_next = of_translate_address(dev, ranges + 3); > > - if (pci_next != pci_addr + size || > > - cpu_next != cpu_addr + size) > > - break; > > - size += of_read_number(ranges + pna + 3, 2); > > - } > > - > > - /* Act based on address space type */ > > - res = NULL; > > - switch ((pci_space >> 24) & 0x3) { > > - case 1: /* PCI IO space */ > > - printk(KERN_INFO > > - " IO 0x%016llx..0x%016llx -> 0x%016llx\n", > > - cpu_addr, cpu_addr + size - 1, pci_addr); > > - > > - /* We support only one IO range */ > > - if (hose->pci_io_size) { > > - printk(KERN_INFO > > - " \\--> Skipped (too many) !\n"); > > - continue; > > - } > > -#ifdef CONFIG_PPC32 > > - /* On 32 bits, limit I/O space to 16MB */ > > - if (size > 0x01000000) > > - size = 0x01000000; > > - > > - /* 32 bits needs to map IOs here */ > > - hose->io_base_virt = ioremap(cpu_addr, size); > > - > > - /* Expect trouble if pci_addr is not 0 */ > > - if (primary) > > - isa_io_base = > > - (unsigned long)hose->io_base_virt; > > -#endif /* CONFIG_PPC32 */ > > - /* pci_io_size and io_base_phys always represent IO > > - * space starting at 0 so we factor in pci_addr > > - */ > > - hose->pci_io_size = pci_addr + size; > > - hose->io_base_phys = cpu_addr - pci_addr; > > - > > - /* Build resource */ > > - res = &hose->io_resource; > > - res->flags = IORESOURCE_IO; > > - res->start = pci_addr; > > - break; > > - case 2: /* PCI Memory space */ > > - case 3: /* PCI 64 bits Memory space */ > > - printk(KERN_INFO > > - " MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", > > - cpu_addr, cpu_addr + size - 1, pci_addr, > > - (pci_space & 0x40000000) ? "Prefetch" : ""); > > - > > - /* We support only 3 memory ranges */ > > - if (memno >= 3) { > > - printk(KERN_INFO > > - " \\--> Skipped (too many) !\n"); > > - continue; > > - } > > - /* Handles ISA memory hole space here */ > > - if (pci_addr == 0) { > > - isa_mb = cpu_addr; > > - isa_hole = memno; > > - if (primary || isa_mem_base == 0) > > - isa_mem_base = cpu_addr; > > - hose->isa_mem_phys = cpu_addr; > > - hose->isa_mem_size = size; > > - } > > - > > - /* We get the PCI/Mem offset from the first range or > > - * the, current one if the offset came from an ISA > > - * hole. If they don't match, bugger. > > - */ > > - if (memno == 0 || > > - (isa_hole >= 0 && pci_addr != 0 && > > - hose->pci_mem_offset == isa_mb)) > > - hose->pci_mem_offset = cpu_addr - pci_addr; > > - else if (pci_addr != 0 && > > - hose->pci_mem_offset != cpu_addr - pci_addr) { > > - printk(KERN_INFO > > - " \\--> Skipped (offset mismatch) !\n"); > > - continue; > > - } > > - > > - /* Build resource */ > > - res = &hose->mem_resources[memno++]; > > - res->flags = IORESOURCE_MEM; > > - if (pci_space & 0x40000000) > > - res->flags |= IORESOURCE_PREFETCH; > > - res->start = cpu_addr; > > - break; > > - } > > - if (res != NULL) { > > - res->name = dev->full_name; > > - res->end = res->start + size - 1; > > - res->parent = NULL; > > - res->sibling = NULL; > > - res->child = NULL; > > - } > > - } > > - > > - /* If there's an ISA hole and the pci_mem_offset is -not- matching > > - * the ISA hole offset, then we need to remove the ISA hole from > > - * the resource list for that brige > > - */ > > - if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) { > > - unsigned int next = isa_hole + 1; > > - printk(KERN_INFO " Removing ISA hole at 0x%016llx\n", isa_mb); > > - if (next < memno) > > - memmove(&hose->mem_resources[isa_hole], > > - &hose->mem_resources[next], > > - sizeof(struct resource) * (memno - next)); > > - hose->mem_resources[--memno].flags = 0; > > - } > > -} > > - > > /* Decide whether to display the domain number in /proc */ > > int pci_proc_domain(struct pci_bus *bus) > > { > > diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c > > index 13e37e2..1626172 100644 > > --- a/drivers/of/of_pci.c > > +++ b/drivers/of/of_pci.c > > @@ -4,6 +4,10 @@ > > #include <linux/of_pci.h> > > #include <asm/prom.h> > > > > +#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) || defined(CONFIG_MICROBLAZE) > > +#include <asm/pci-bridge.h> > > +#endif > > + > > static inline int __of_pci_pci_compare(struct device_node *node, > > unsigned int devfn) > > { > > @@ -40,3 +44,199 @@ struct device_node *of_pci_find_child_device(struct device_node *parent, > > return NULL; > > } > > EXPORT_SYMBOL_GPL(of_pci_find_child_device); > > + > > +/** > > + * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree > > + * @hose: newly allocated pci_controller to be setup > > + * @dev: device node of the host bridge > > + * @primary: set if primary bus (32 bits only, soon to be deprecated) > > + * > > + * This function will parse the "ranges" property of a PCI host bridge device > > + * node and setup the resource mapping of a pci controller based on its > > + * content. > > + * > > + * Life would be boring if it wasn't for a few issues that we have to deal > > + * with here: > > + * > > + * - We can only cope with one IO space range and up to 3 Memory space > > + * ranges. However, some machines (thanks Apple !) tend to split their > > + * space into lots of small contiguous ranges. So we have to coalesce. > > + * > > + * - We can only cope with all memory ranges having the same offset > > + * between CPU addresses and PCI addresses. Unfortunately, some bridges > > + * are setup for a large 1:1 mapping along with a small "window" which > > + * maps PCI address 0 to some arbitrary high address of the CPU space in > > + * order to give access to the ISA memory hole. > > + * The way out of here that I've chosen for now is to always set the > > + * offset based on the first resource found, then override it if we > > + * have a different offset and the previous was set by an ISA hole. > > + * > > + * - Some busses have IO space not starting at 0, which causes trouble with > > + * the way we do our IO resource renumbering. The code somewhat deals with > > + * it for 64 bits but I would expect problems on 32 bits. > > + * > > + * - Some 32 bits platforms such as 4xx can have physical space larger than > > + * 32 bits so we need to use 64 bits values for the parsing > > + */ > > +#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) || defined(CONFIG_MICROBLAZE) > > +void pci_process_bridge_OF_ranges(struct pci_controller *hose, > > + struct device_node *dev, int primary) > > +{ > > + const u32 *ranges; > > + int rlen; > > + int pna = of_n_addr_cells(dev); > > + int np = pna + 5; > > + int memno = 0, isa_hole = -1; > > + u32 pci_space; > > + unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size; > > + unsigned long long isa_mb = 0; > > + struct resource *res; > > + > > + pr_info("PCI host bridge %s %s ranges:\n", > > + dev->full_name, primary ? "(primary)" : ""); > > + > > + /* Get ranges property */ > > + ranges = of_get_property(dev, "ranges", &rlen); > > + if (ranges == NULL) > > + return; > > + > > + /* Parse it */ > > + pr_debug("Parsing ranges property...\n"); > > + while ((rlen -= np * 4) >= 0) { > > + /* Read next ranges element */ > > + pci_space = ranges[0]; > > + pci_addr = of_read_number(ranges + 1, 2); > > + cpu_addr = of_translate_address(dev, ranges + 3); > > + size = of_read_number(ranges + pna + 3, 2); > > + > > + pr_debug("pci_space: 0x%08x pci_addr:0x%016llx ", > > + pci_space, pci_addr); > > + pr_debug("cpu_addr:0x%016llx size:0x%016llx\n", > > + cpu_addr, size); > > + > > + ranges += np; > > + > > + /* If we failed translation or got a zero-sized region > > + * (some FW try to feed us with non sensical zero sized regions > > + * such as power3 which look like some kind of attempt > > + * at exposing the VGA memory hole) > > + */ > > + if (cpu_addr == OF_BAD_ADDR || size == 0) > > + continue; > > + > > + /* Now consume following elements while they are contiguous */ > > + for (; rlen >= np * sizeof(u32); > > + ranges += np, rlen -= np * 4) { > > + if (ranges[0] != pci_space) > > + break; > > + pci_next = of_read_number(ranges + 1, 2); > > + cpu_next = of_translate_address(dev, ranges + 3); > > + if (pci_next != pci_addr + size || > > + cpu_next != cpu_addr + size) > > + break; > > + size += of_read_number(ranges + pna + 3, 2); > > + } > > + > > + /* Act based on address space type */ > > + res = NULL; > > + switch ((pci_space >> 24) & 0x3) { > > + case 1: /* PCI IO space */ > > + pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n", > > + cpu_addr, cpu_addr + size - 1, pci_addr); > > + > > + /* We support only one IO range */ > > + if (hose->pci_io_size) { > > + pr_info(" \\--> Skipped (too many) !\n"); > > + continue; > > + } > > +#if (!IS_ENABLED(CONFIG_64BIT)) > > + /* On 32 bits, limit I/O space to 16MB */ > > + if (size > 0x01000000) > > + size = 0x01000000; > > + > > + /* 32 bits needs to map IOs here */ > > + hose->io_base_virt = ioremap(cpu_addr, size); > > + > > + /* Expect trouble if pci_addr is not 0 */ > > + if (primary) > > + isa_io_base = > > + (unsigned long)hose->io_base_virt; > > +#endif /* !CONFIG_64BIT */ > > + /* pci_io_size and io_base_phys always represent IO > > + * space starting at 0 so we factor in pci_addr > > + */ > > + hose->pci_io_size = pci_addr + size; > > + hose->io_base_phys = cpu_addr - pci_addr; > > + > > + /* Build resource */ > > + res = &hose->io_resource; > > + res->flags = IORESOURCE_IO; > > + res->start = pci_addr; > > + break; > > + case 2: /* PCI Memory space */ > > + case 3: /* PCI 64 bits Memory space */ > > + pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", > > + cpu_addr, cpu_addr + size - 1, pci_addr, > > + (pci_space & 0x40000000) ? "Prefetch" : ""); > > + > > + /* We support only 3 memory ranges */ > > + if (memno >= 3) { > > + pr_info(" \\--> Skipped (too many) !\n"); > > + continue; > > + } > > + /* Handles ISA memory hole space here */ > > + if (pci_addr == 0) { > > + isa_mb = cpu_addr; > > + isa_hole = memno; > > + if (primary || isa_mem_base == 0) > > + isa_mem_base = cpu_addr; > > + hose->isa_mem_phys = cpu_addr; > > + hose->isa_mem_size = size; > > + } > > + > > + /* We get the PCI/Mem offset from the first range or > > + * the, current one if the offset came from an ISA > > + * hole. If they don't match, bugger. > > + */ > > + if (memno == 0 || > > + (isa_hole >= 0 && pci_addr != 0 && > > + hose->pci_mem_offset == isa_mb)) > > + hose->pci_mem_offset = cpu_addr - pci_addr; > > + else if (pci_addr != 0 && > > + hose->pci_mem_offset != cpu_addr - pci_addr) { > > + pr_info(" \\--> Skipped (offset mismatch) !\n"); > > + continue; > > + } > > + > > + /* Build resource */ > > + res = &hose->mem_resources[memno++]; > > + res->flags = IORESOURCE_MEM; > > + if (pci_space & 0x40000000) > > + res->flags |= IORESOURCE_PREFETCH; > > + res->start = cpu_addr; > > + break; > > + } > > + if (res != NULL) { > > + res->name = dev->full_name; > > + res->end = res->start + size - 1; > > + res->parent = NULL; > > + res->sibling = NULL; > > + res->child = NULL; > > + } > > + } > > + > > + /* If there's an ISA hole and the pci_mem_offset is -not- matching > > + * the ISA hole offset, then we need to remove the ISA hole from > > + * the resource list for that brige > > + */ > > + if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) { > > + unsigned int next = isa_hole + 1; > > + pr_info(" Removing ISA hole at 0x%016llx\n", isa_mb); > > + if (next < memno) > > + memmove(&hose->mem_resources[isa_hole], > > + &hose->mem_resources[next], > > + sizeof(struct resource) * (memno - next)); > > + hose->mem_resources[--memno].flags = 0; > > + } > > +} > > +#endif > > diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h > > index bb115de..33e8ead 100644 > > --- a/include/linux/of_pci.h > > +++ b/include/linux/of_pci.h > > @@ -11,4 +11,8 @@ struct device_node; > > struct device_node *of_pci_find_child_device(struct device_node *parent, > > unsigned int devfn); > > > > +struct pci_controller; > > +void pci_process_bridge_OF_ranges(struct pci_controller *hose, > > + struct device_node *dev, int primary); > > + > > #endif > > > -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/arch/microblaze/include/asm/pci-bridge.h b/arch/microblaze/include/asm/pci-bridge.h index cb5d397..5783cd6 100644 --- a/arch/microblaze/include/asm/pci-bridge.h +++ b/arch/microblaze/include/asm/pci-bridge.h @@ -10,6 +10,7 @@ #include <linux/pci.h> #include <linux/list.h> #include <linux/ioport.h> +#include <linux/of_pci.h> struct device_node; @@ -132,10 +133,6 @@ extern void setup_indirect_pci(struct pci_controller *hose, extern struct pci_controller *pci_find_hose_for_OF_device( struct device_node *node); -/* Fill up host controller resources from the OF node */ -extern void pci_process_bridge_OF_ranges(struct pci_controller *hose, - struct device_node *dev, int primary); - /* Allocate & free a PCI host bridge structure */ extern struct pci_controller *pcibios_alloc_controller(struct device_node *dev); extern void pcibios_free_controller(struct pci_controller *phb); diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index 9ea521e..2735ad9 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -622,198 +622,6 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, *end = rsrc->end - offset; } -/** - * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree - * @hose: newly allocated pci_controller to be setup - * @dev: device node of the host bridge - * @primary: set if primary bus (32 bits only, soon to be deprecated) - * - * This function will parse the "ranges" property of a PCI host bridge device - * node and setup the resource mapping of a pci controller based on its - * content. - * - * Life would be boring if it wasn't for a few issues that we have to deal - * with here: - * - * - We can only cope with one IO space range and up to 3 Memory space - * ranges. However, some machines (thanks Apple !) tend to split their - * space into lots of small contiguous ranges. So we have to coalesce. - * - * - We can only cope with all memory ranges having the same offset - * between CPU addresses and PCI addresses. Unfortunately, some bridges - * are setup for a large 1:1 mapping along with a small "window" which - * maps PCI address 0 to some arbitrary high address of the CPU space in - * order to give access to the ISA memory hole. - * The way out of here that I've chosen for now is to always set the - * offset based on the first resource found, then override it if we - * have a different offset and the previous was set by an ISA hole. - * - * - Some busses have IO space not starting at 0, which causes trouble with - * the way we do our IO resource renumbering. The code somewhat deals with - * it for 64 bits but I would expect problems on 32 bits. - * - * - Some 32 bits platforms such as 4xx can have physical space larger than - * 32 bits so we need to use 64 bits values for the parsing - */ -void pci_process_bridge_OF_ranges(struct pci_controller *hose, - struct device_node *dev, int primary) -{ - const u32 *ranges; - int rlen; - int pna = of_n_addr_cells(dev); - int np = pna + 5; - int memno = 0, isa_hole = -1; - u32 pci_space; - unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size; - unsigned long long isa_mb = 0; - struct resource *res; - - pr_info("PCI host bridge %s %s ranges:\n", - dev->full_name, primary ? "(primary)" : ""); - - /* Get ranges property */ - ranges = of_get_property(dev, "ranges", &rlen); - if (ranges == NULL) - return; - - /* Parse it */ - pr_debug("Parsing ranges property...\n"); - while ((rlen -= np * 4) >= 0) { - /* Read next ranges element */ - pci_space = ranges[0]; - pci_addr = of_read_number(ranges + 1, 2); - cpu_addr = of_translate_address(dev, ranges + 3); - size = of_read_number(ranges + pna + 3, 2); - - pr_debug("pci_space: 0x%08x pci_addr:0x%016llx ", - pci_space, pci_addr); - pr_debug("cpu_addr:0x%016llx size:0x%016llx\n", - cpu_addr, size); - - ranges += np; - - /* If we failed translation or got a zero-sized region - * (some FW try to feed us with non sensical zero sized regions - * such as power3 which look like some kind of attempt - * at exposing the VGA memory hole) - */ - if (cpu_addr == OF_BAD_ADDR || size == 0) - continue; - - /* Now consume following elements while they are contiguous */ - for (; rlen >= np * sizeof(u32); - ranges += np, rlen -= np * 4) { - if (ranges[0] != pci_space) - break; - pci_next = of_read_number(ranges + 1, 2); - cpu_next = of_translate_address(dev, ranges + 3); - if (pci_next != pci_addr + size || - cpu_next != cpu_addr + size) - break; - size += of_read_number(ranges + pna + 3, 2); - } - - /* Act based on address space type */ - res = NULL; - switch ((pci_space >> 24) & 0x3) { - case 1: /* PCI IO space */ - pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n", - cpu_addr, cpu_addr + size - 1, pci_addr); - - /* We support only one IO range */ - if (hose->pci_io_size) { - pr_info(" \\--> Skipped (too many) !\n"); - continue; - } - /* On 32 bits, limit I/O space to 16MB */ - if (size > 0x01000000) - size = 0x01000000; - - /* 32 bits needs to map IOs here */ - hose->io_base_virt = ioremap(cpu_addr, size); - - /* Expect trouble if pci_addr is not 0 */ - if (primary) - isa_io_base = - (unsigned long)hose->io_base_virt; - /* pci_io_size and io_base_phys always represent IO - * space starting at 0 so we factor in pci_addr - */ - hose->pci_io_size = pci_addr + size; - hose->io_base_phys = cpu_addr - pci_addr; - - /* Build resource */ - res = &hose->io_resource; - res->flags = IORESOURCE_IO; - res->start = pci_addr; - break; - case 2: /* PCI Memory space */ - case 3: /* PCI 64 bits Memory space */ - pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", - cpu_addr, cpu_addr + size - 1, pci_addr, - (pci_space & 0x40000000) ? "Prefetch" : ""); - - /* We support only 3 memory ranges */ - if (memno >= 3) { - pr_info(" \\--> Skipped (too many) !\n"); - continue; - } - /* Handles ISA memory hole space here */ - if (pci_addr == 0) { - isa_mb = cpu_addr; - isa_hole = memno; - if (primary || isa_mem_base == 0) - isa_mem_base = cpu_addr; - hose->isa_mem_phys = cpu_addr; - hose->isa_mem_size = size; - } - - /* We get the PCI/Mem offset from the first range or - * the, current one if the offset came from an ISA - * hole. If they don't match, bugger. - */ - if (memno == 0 || - (isa_hole >= 0 && pci_addr != 0 && - hose->pci_mem_offset == isa_mb)) - hose->pci_mem_offset = cpu_addr - pci_addr; - else if (pci_addr != 0 && - hose->pci_mem_offset != cpu_addr - pci_addr) { - pr_info(" \\--> Skipped (offset mismatch) !\n"); - continue; - } - - /* Build resource */ - res = &hose->mem_resources[memno++]; - res->flags = IORESOURCE_MEM; - if (pci_space & 0x40000000) - res->flags |= IORESOURCE_PREFETCH; - res->start = cpu_addr; - break; - } - if (res != NULL) { - res->name = dev->full_name; - res->end = res->start + size - 1; - res->parent = NULL; - res->sibling = NULL; - res->child = NULL; - } - } - - /* If there's an ISA hole and the pci_mem_offset is -not- matching - * the ISA hole offset, then we need to remove the ISA hole from - * the resource list for that brige - */ - if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) { - unsigned int next = isa_hole + 1; - pr_info(" Removing ISA hole at 0x%016llx\n", isa_mb); - if (next < memno) - memmove(&hose->mem_resources[isa_hole], - &hose->mem_resources[next], - sizeof(struct resource) * (memno - next)); - hose->mem_resources[--memno].flags = 0; - } -} - /* Decide whether to display the domain number in /proc */ int pci_proc_domain(struct pci_bus *bus) { diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 025a130..205bfba 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -10,6 +10,7 @@ #include <linux/pci.h> #include <linux/list.h> #include <linux/ioport.h> +#include <linux/of_pci.h> #include <asm-generic/pci-bridge.h> struct device_node; @@ -231,10 +232,6 @@ extern int pcibios_map_io_space(struct pci_bus *bus); extern struct pci_controller *pci_find_hose_for_OF_device( struct device_node* node); -/* Fill up host controller resources from the OF node */ -extern void pci_process_bridge_OF_ranges(struct pci_controller *hose, - struct device_node *dev, int primary); - /* Allocate & free a PCI host bridge structure */ extern struct pci_controller *pcibios_alloc_controller(struct device_node *dev); extern void pcibios_free_controller(struct pci_controller *phb); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index fa12ae4..6edf396 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -640,198 +640,6 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, *end = rsrc->end - offset; } -/** - * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree - * @hose: newly allocated pci_controller to be setup - * @dev: device node of the host bridge - * @primary: set if primary bus (32 bits only, soon to be deprecated) - * - * This function will parse the "ranges" property of a PCI host bridge device - * node and setup the resource mapping of a pci controller based on its - * content. - * - * Life would be boring if it wasn't for a few issues that we have to deal - * with here: - * - * - We can only cope with one IO space range and up to 3 Memory space - * ranges. However, some machines (thanks Apple !) tend to split their - * space into lots of small contiguous ranges. So we have to coalesce. - * - * - We can only cope with all memory ranges having the same offset - * between CPU addresses and PCI addresses. Unfortunately, some bridges - * are setup for a large 1:1 mapping along with a small "window" which - * maps PCI address 0 to some arbitrary high address of the CPU space in - * order to give access to the ISA memory hole. - * The way out of here that I've chosen for now is to always set the - * offset based on the first resource found, then override it if we - * have a different offset and the previous was set by an ISA hole. - * - * - Some busses have IO space not starting at 0, which causes trouble with - * the way we do our IO resource renumbering. The code somewhat deals with - * it for 64 bits but I would expect problems on 32 bits. - * - * - Some 32 bits platforms such as 4xx can have physical space larger than - * 32 bits so we need to use 64 bits values for the parsing - */ -void pci_process_bridge_OF_ranges(struct pci_controller *hose, - struct device_node *dev, int primary) -{ - const u32 *ranges; - int rlen; - int pna = of_n_addr_cells(dev); - int np = pna + 5; - int memno = 0, isa_hole = -1; - u32 pci_space; - unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size; - unsigned long long isa_mb = 0; - struct resource *res; - - printk(KERN_INFO "PCI host bridge %s %s ranges:\n", - dev->full_name, primary ? "(primary)" : ""); - - /* Get ranges property */ - ranges = of_get_property(dev, "ranges", &rlen); - if (ranges == NULL) - return; - - /* Parse it */ - while ((rlen -= np * 4) >= 0) { - /* Read next ranges element */ - pci_space = ranges[0]; - pci_addr = of_read_number(ranges + 1, 2); - cpu_addr = of_translate_address(dev, ranges + 3); - size = of_read_number(ranges + pna + 3, 2); - ranges += np; - - /* If we failed translation or got a zero-sized region - * (some FW try to feed us with non sensical zero sized regions - * such as power3 which look like some kind of attempt at exposing - * the VGA memory hole) - */ - if (cpu_addr == OF_BAD_ADDR || size == 0) - continue; - - /* Now consume following elements while they are contiguous */ - for (; rlen >= np * sizeof(u32); - ranges += np, rlen -= np * 4) { - if (ranges[0] != pci_space) - break; - pci_next = of_read_number(ranges + 1, 2); - cpu_next = of_translate_address(dev, ranges + 3); - if (pci_next != pci_addr + size || - cpu_next != cpu_addr + size) - break; - size += of_read_number(ranges + pna + 3, 2); - } - - /* Act based on address space type */ - res = NULL; - switch ((pci_space >> 24) & 0x3) { - case 1: /* PCI IO space */ - printk(KERN_INFO - " IO 0x%016llx..0x%016llx -> 0x%016llx\n", - cpu_addr, cpu_addr + size - 1, pci_addr); - - /* We support only one IO range */ - if (hose->pci_io_size) { - printk(KERN_INFO - " \\--> Skipped (too many) !\n"); - continue; - } -#ifdef CONFIG_PPC32 - /* On 32 bits, limit I/O space to 16MB */ - if (size > 0x01000000) - size = 0x01000000; - - /* 32 bits needs to map IOs here */ - hose->io_base_virt = ioremap(cpu_addr, size); - - /* Expect trouble if pci_addr is not 0 */ - if (primary) - isa_io_base = - (unsigned long)hose->io_base_virt; -#endif /* CONFIG_PPC32 */ - /* pci_io_size and io_base_phys always represent IO - * space starting at 0 so we factor in pci_addr - */ - hose->pci_io_size = pci_addr + size; - hose->io_base_phys = cpu_addr - pci_addr; - - /* Build resource */ - res = &hose->io_resource; - res->flags = IORESOURCE_IO; - res->start = pci_addr; - break; - case 2: /* PCI Memory space */ - case 3: /* PCI 64 bits Memory space */ - printk(KERN_INFO - " MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", - cpu_addr, cpu_addr + size - 1, pci_addr, - (pci_space & 0x40000000) ? "Prefetch" : ""); - - /* We support only 3 memory ranges */ - if (memno >= 3) { - printk(KERN_INFO - " \\--> Skipped (too many) !\n"); - continue; - } - /* Handles ISA memory hole space here */ - if (pci_addr == 0) { - isa_mb = cpu_addr; - isa_hole = memno; - if (primary || isa_mem_base == 0) - isa_mem_base = cpu_addr; - hose->isa_mem_phys = cpu_addr; - hose->isa_mem_size = size; - } - - /* We get the PCI/Mem offset from the first range or - * the, current one if the offset came from an ISA - * hole. If they don't match, bugger. - */ - if (memno == 0 || - (isa_hole >= 0 && pci_addr != 0 && - hose->pci_mem_offset == isa_mb)) - hose->pci_mem_offset = cpu_addr - pci_addr; - else if (pci_addr != 0 && - hose->pci_mem_offset != cpu_addr - pci_addr) { - printk(KERN_INFO - " \\--> Skipped (offset mismatch) !\n"); - continue; - } - - /* Build resource */ - res = &hose->mem_resources[memno++]; - res->flags = IORESOURCE_MEM; - if (pci_space & 0x40000000) - res->flags |= IORESOURCE_PREFETCH; - res->start = cpu_addr; - break; - } - if (res != NULL) { - res->name = dev->full_name; - res->end = res->start + size - 1; - res->parent = NULL; - res->sibling = NULL; - res->child = NULL; - } - } - - /* If there's an ISA hole and the pci_mem_offset is -not- matching - * the ISA hole offset, then we need to remove the ISA hole from - * the resource list for that brige - */ - if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) { - unsigned int next = isa_hole + 1; - printk(KERN_INFO " Removing ISA hole at 0x%016llx\n", isa_mb); - if (next < memno) - memmove(&hose->mem_resources[isa_hole], - &hose->mem_resources[next], - sizeof(struct resource) * (memno - next)); - hose->mem_resources[--memno].flags = 0; - } -} - /* Decide whether to display the domain number in /proc */ int pci_proc_domain(struct pci_bus *bus) { diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 13e37e2..1626172 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -4,6 +4,10 @@ #include <linux/of_pci.h> #include <asm/prom.h> +#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) || defined(CONFIG_MICROBLAZE) +#include <asm/pci-bridge.h> +#endif + static inline int __of_pci_pci_compare(struct device_node *node, unsigned int devfn) { @@ -40,3 +44,199 @@ struct device_node *of_pci_find_child_device(struct device_node *parent, return NULL; } EXPORT_SYMBOL_GPL(of_pci_find_child_device); + +/** + * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree + * @hose: newly allocated pci_controller to be setup + * @dev: device node of the host bridge + * @primary: set if primary bus (32 bits only, soon to be deprecated) + * + * This function will parse the "ranges" property of a PCI host bridge device + * node and setup the resource mapping of a pci controller based on its + * content. + * + * Life would be boring if it wasn't for a few issues that we have to deal + * with here: + * + * - We can only cope with one IO space range and up to 3 Memory space + * ranges. However, some machines (thanks Apple !) tend to split their + * space into lots of small contiguous ranges. So we have to coalesce. + * + * - We can only cope with all memory ranges having the same offset + * between CPU addresses and PCI addresses. Unfortunately, some bridges + * are setup for a large 1:1 mapping along with a small "window" which + * maps PCI address 0 to some arbitrary high address of the CPU space in + * order to give access to the ISA memory hole. + * The way out of here that I've chosen for now is to always set the + * offset based on the first resource found, then override it if we + * have a different offset and the previous was set by an ISA hole. + * + * - Some busses have IO space not starting at 0, which causes trouble with + * the way we do our IO resource renumbering. The code somewhat deals with + * it for 64 bits but I would expect problems on 32 bits. + * + * - Some 32 bits platforms such as 4xx can have physical space larger than + * 32 bits so we need to use 64 bits values for the parsing + */ +#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64) || defined(CONFIG_MICROBLAZE) +void pci_process_bridge_OF_ranges(struct pci_controller *hose, + struct device_node *dev, int primary) +{ + const u32 *ranges; + int rlen; + int pna = of_n_addr_cells(dev); + int np = pna + 5; + int memno = 0, isa_hole = -1; + u32 pci_space; + unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size; + unsigned long long isa_mb = 0; + struct resource *res; + + pr_info("PCI host bridge %s %s ranges:\n", + dev->full_name, primary ? "(primary)" : ""); + + /* Get ranges property */ + ranges = of_get_property(dev, "ranges", &rlen); + if (ranges == NULL) + return; + + /* Parse it */ + pr_debug("Parsing ranges property...\n"); + while ((rlen -= np * 4) >= 0) { + /* Read next ranges element */ + pci_space = ranges[0]; + pci_addr = of_read_number(ranges + 1, 2); + cpu_addr = of_translate_address(dev, ranges + 3); + size = of_read_number(ranges + pna + 3, 2); + + pr_debug("pci_space: 0x%08x pci_addr:0x%016llx ", + pci_space, pci_addr); + pr_debug("cpu_addr:0x%016llx size:0x%016llx\n", + cpu_addr, size); + + ranges += np; + + /* If we failed translation or got a zero-sized region + * (some FW try to feed us with non sensical zero sized regions + * such as power3 which look like some kind of attempt + * at exposing the VGA memory hole) + */ + if (cpu_addr == OF_BAD_ADDR || size == 0) + continue; + + /* Now consume following elements while they are contiguous */ + for (; rlen >= np * sizeof(u32); + ranges += np, rlen -= np * 4) { + if (ranges[0] != pci_space) + break; + pci_next = of_read_number(ranges + 1, 2); + cpu_next = of_translate_address(dev, ranges + 3); + if (pci_next != pci_addr + size || + cpu_next != cpu_addr + size) + break; + size += of_read_number(ranges + pna + 3, 2); + } + + /* Act based on address space type */ + res = NULL; + switch ((pci_space >> 24) & 0x3) { + case 1: /* PCI IO space */ + pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n", + cpu_addr, cpu_addr + size - 1, pci_addr); + + /* We support only one IO range */ + if (hose->pci_io_size) { + pr_info(" \\--> Skipped (too many) !\n"); + continue; + } +#if (!IS_ENABLED(CONFIG_64BIT)) + /* On 32 bits, limit I/O space to 16MB */ + if (size > 0x01000000) + size = 0x01000000; + + /* 32 bits needs to map IOs here */ + hose->io_base_virt = ioremap(cpu_addr, size); + + /* Expect trouble if pci_addr is not 0 */ + if (primary) + isa_io_base = + (unsigned long)hose->io_base_virt; +#endif /* !CONFIG_64BIT */ + /* pci_io_size and io_base_phys always represent IO + * space starting at 0 so we factor in pci_addr + */ + hose->pci_io_size = pci_addr + size; + hose->io_base_phys = cpu_addr - pci_addr; + + /* Build resource */ + res = &hose->io_resource; + res->flags = IORESOURCE_IO; + res->start = pci_addr; + break; + case 2: /* PCI Memory space */ + case 3: /* PCI 64 bits Memory space */ + pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", + cpu_addr, cpu_addr + size - 1, pci_addr, + (pci_space & 0x40000000) ? "Prefetch" : ""); + + /* We support only 3 memory ranges */ + if (memno >= 3) { + pr_info(" \\--> Skipped (too many) !\n"); + continue; + } + /* Handles ISA memory hole space here */ + if (pci_addr == 0) { + isa_mb = cpu_addr; + isa_hole = memno; + if (primary || isa_mem_base == 0) + isa_mem_base = cpu_addr; + hose->isa_mem_phys = cpu_addr; + hose->isa_mem_size = size; + } + + /* We get the PCI/Mem offset from the first range or + * the, current one if the offset came from an ISA + * hole. If they don't match, bugger. + */ + if (memno == 0 || + (isa_hole >= 0 && pci_addr != 0 && + hose->pci_mem_offset == isa_mb)) + hose->pci_mem_offset = cpu_addr - pci_addr; + else if (pci_addr != 0 && + hose->pci_mem_offset != cpu_addr - pci_addr) { + pr_info(" \\--> Skipped (offset mismatch) !\n"); + continue; + } + + /* Build resource */ + res = &hose->mem_resources[memno++]; + res->flags = IORESOURCE_MEM; + if (pci_space & 0x40000000) + res->flags |= IORESOURCE_PREFETCH; + res->start = cpu_addr; + break; + } + if (res != NULL) { + res->name = dev->full_name; + res->end = res->start + size - 1; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; + } + } + + /* If there's an ISA hole and the pci_mem_offset is -not- matching + * the ISA hole offset, then we need to remove the ISA hole from + * the resource list for that brige + */ + if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) { + unsigned int next = isa_hole + 1; + pr_info(" Removing ISA hole at 0x%016llx\n", isa_mb); + if (next < memno) + memmove(&hose->mem_resources[isa_hole], + &hose->mem_resources[next], + sizeof(struct resource) * (memno - next)); + hose->mem_resources[--memno].flags = 0; + } +} +#endif diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index bb115de..33e8ead 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -11,4 +11,8 @@ struct device_node; struct device_node *of_pci_find_child_device(struct device_node *parent, unsigned int devfn); +struct pci_controller; +void pci_process_bridge_OF_ranges(struct pci_controller *hose, + struct device_node *dev, int primary); + #endif