Subject: [PATCH v5] sparc/PCI: Use correct bus address to resource offset After we add 64bit mmio parsing, we got some "no compatible bridge window" warning on anther new model that support 64bit resource. It turns out that we can not use mem_space.start as 64bit mem space offset, aka mem_space.start != offset. Use child_phys_addr to calculate exact offset and recorder offset in pbm. After patch we get correct offset. /pci@305: PCI IO [io 0x2007e00000000-0x2007e0fffffff] offset 2007e00000000 /pci@305: PCI MEM [mem 0x2000000100000-0x200007effffff] offset 2000000000000 /pci@305: PCI MEM64 [mem 0x2000100000000-0x2000dffffffff] offset 2000000000000 ... pci_sun4v f02ae7f8: PCI host bridge to bus 0000:00 pci_bus 0000:00: root bus resource [io 0x2007e00000000-0x2007e0fffffff] (bus address [0x0000-0xfffffff]) pci_bus 0000:00: root bus resource [mem 0x2000000100000-0x200007effffff] (bus address [0x00100000-0x7effffff]) pci_bus 0000:00: root bus resource [mem 0x2000100000000-0x2000dffffffff] (bus address [0x100000000-0xdffffffff]) -v2: to make it simple, do not add mem64_offset, and assume mem64_offset == mem_offset even that is not same as mem_space.start, otherwise would make pci_mmap_resource() path too complicated. -v3: put back mem64_offset, as we found T4 has mem_offset != mem64_offset check overlapping between mem64_space and mem_space. -v5: use pcibios_bus_to_region() requested by Bjorn. add pci_find_root_bus_resource(), return root bus res We can return host bridge window res, so 1. we need direct parent for request_resource_conflict() calling in pci_register_legacy_regions(). 2. check if return is NULL to decide if region is valid or not. Signed-off-by: Yinghai Lu Tested-by: Khalid Aziz --- arch/sparc/kernel/pci.c | 50 ++++++++++++++++++----------------------- arch/sparc/kernel/pci_common.c | 32 +++++++++++++++++++------- arch/sparc/kernel/pci_impl.h | 4 +++ drivers/pci/pci.c | 36 ++++++++++++++++++++--------- include/linux/pci.h | 2 + 5 files changed, 77 insertions(+), 47 deletions(-) Index: linux-2.6/arch/sparc/kernel/pci.c =================================================================== --- linux-2.6.orig/arch/sparc/kernel/pci.c +++ linux-2.6/arch/sparc/kernel/pci.c @@ -654,12 +654,12 @@ struct pci_bus *pci_scan_one_pbm(struct printk("PCI: Scanning PBM %s\n", node->full_name); pci_add_resource_offset(&resources, &pbm->io_space, - pbm->io_space.start); + pbm->io_offset); pci_add_resource_offset(&resources, &pbm->mem_space, - pbm->mem_space.start); + pbm->mem_offset); if (pbm->mem64_space.flags) pci_add_resource_offset(&resources, &pbm->mem64_space, - pbm->mem_space.start); + pbm->mem64_offset); pbm->busn.start = pbm->pci_first_busno; pbm->busn.end = pbm->pci_last_busno; pbm->busn.flags = IORESOURCE_BUS; @@ -733,30 +733,28 @@ int pcibios_enable_device(struct pci_dev static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state) { - struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; - unsigned long space_size, user_offset, user_size; - - if (mmap_state == pci_mmap_io) { - space_size = resource_size(&pbm->io_space); - } else { - space_size = resource_size(&pbm->mem_space); - } + unsigned long user_offset, user_size; + struct resource res, *root_bus_res; + struct pci_bus_region region; /* Make sure the request is in range. */ user_offset = vma->vm_pgoff << PAGE_SHIFT; user_size = vma->vm_end - vma->vm_start; - if (user_offset >= space_size || - (user_offset + user_size) > space_size) + region.start = user_offset; + region.end = user_offset + user_size - 1; + memset(&res, 0, sizeof(res)); + if (mmap_state == pci_mmap_io) + res.flags = IORESOURCE_IO; + else + res.flags = IORESOURCE_MEM; + + pcibios_bus_to_resource(pdev->bus, &res, ®ion); + root_bus_res = pci_find_root_bus_resource(pdev->bus, &res); + if (!root_bus_res) return -EINVAL; - if (mmap_state == pci_mmap_io) { - vma->vm_pgoff = (pbm->io_space.start + - user_offset) >> PAGE_SHIFT; - } else { - vma->vm_pgoff = (pbm->mem_space.start + - user_offset) >> PAGE_SHIFT; - } + vma->vm_pgoff = res.start >> PAGE_SHIFT; return 0; } @@ -977,16 +975,12 @@ void pci_resource_to_user(const struct p const struct resource *rp, resource_size_t *start, resource_size_t *end) { - struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; - unsigned long offset; + struct pci_bus_region region; - if (rp->flags & IORESOURCE_IO) - offset = pbm->io_space.start; - else - offset = pbm->mem_space.start; + pcibios_resource_to_bus(pdev->bus, ®ion, (struct resource *)rp); - *start = rp->start - offset; - *end = rp->end - offset; + *start = region.start; + *end = region.end; } void pcibios_set_master(struct pci_dev *dev) Index: linux-2.6/arch/sparc/kernel/pci_common.c =================================================================== --- linux-2.6.orig/arch/sparc/kernel/pci_common.c +++ linux-2.6/arch/sparc/kernel/pci_common.c @@ -410,13 +410,16 @@ void pci_determine_mem_io_space(struct p for (i = 0; i < num_pbm_ranges; i++) { const struct linux_prom_pci_ranges *pr = &pbm_ranges[i]; - unsigned long a, size; + unsigned long a, size, region_a; u32 parent_phys_hi, parent_phys_lo; + u32 child_phys_mid, child_phys_lo; u32 size_hi, size_lo; int type; parent_phys_hi = pr->parent_phys_hi; parent_phys_lo = pr->parent_phys_lo; + child_phys_mid = pr->child_phys_mid; + child_phys_lo = pr->child_phys_lo; if (tlb_type == hypervisor) parent_phys_hi &= 0x0fffffff; @@ -426,6 +429,8 @@ void pci_determine_mem_io_space(struct p type = (pr->child_phys_hi >> 24) & 0x3; a = (((unsigned long)parent_phys_hi << 32UL) | ((unsigned long)parent_phys_lo << 0UL)); + region_a = (((unsigned long)child_phys_mid << 32UL) | + ((unsigned long)child_phys_lo << 0UL)); size = (((unsigned long)size_hi << 32UL) | ((unsigned long)size_lo << 0UL)); @@ -440,6 +445,7 @@ void pci_determine_mem_io_space(struct p pbm->io_space.start = a; pbm->io_space.end = a + size - 1UL; pbm->io_space.flags = IORESOURCE_IO; + pbm->io_offset = a - region_a; saw_io = 1; break; @@ -448,6 +454,7 @@ void pci_determine_mem_io_space(struct p pbm->mem_space.start = a; pbm->mem_space.end = a + size - 1UL; pbm->mem_space.flags = IORESOURCE_MEM; + pbm->mem_offset = a - region_a; saw_mem = 1; break; @@ -456,6 +463,7 @@ void pci_determine_mem_io_space(struct p pbm->mem64_space.start = a; pbm->mem64_space.end = a + size - 1UL; pbm->mem64_space.flags = IORESOURCE_MEM; + pbm->mem64_offset = a - region_a; saw_mem = 1; break; @@ -471,14 +479,22 @@ void pci_determine_mem_io_space(struct p prom_halt(); } - printk("%s: PCI IO[%llx] MEM[%llx]", - pbm->name, - pbm->io_space.start, - pbm->mem_space.start); + if (pbm->io_space.flags) + printk("%s: PCI IO %pR offset %llx\n", + pbm->name, &pbm->io_space, pbm->io_offset); + if (pbm->mem_space.flags) + printk("%s: PCI MEM %pR offset %llx\n", + pbm->name, &pbm->mem_space, pbm->mem_offset); + if (pbm->mem64_space.flags && pbm->mem_space.flags) { + if (pbm->mem64_space.start <= pbm->mem_space.end) + pbm->mem64_space.start = pbm->mem_space.end + 1; + if (pbm->mem64_space.start > pbm->mem64_space.end) + pbm->mem64_space.flags = 0; + } + if (pbm->mem64_space.flags) - printk(" MEM64[%llx]", - pbm->mem64_space.start); - printk("\n"); + printk("%s: PCI MEM64 %pR offset %llx\n", + pbm->name, &pbm->mem64_space, pbm->mem64_offset); pbm->io_space.name = pbm->mem_space.name = pbm->name; pbm->mem64_space.name = pbm->name; Index: linux-2.6/arch/sparc/kernel/pci_impl.h =================================================================== --- linux-2.6.orig/arch/sparc/kernel/pci_impl.h +++ linux-2.6/arch/sparc/kernel/pci_impl.h @@ -99,6 +99,10 @@ struct pci_pbm_info { struct resource mem_space; struct resource mem64_space; struct resource busn; + /* offset */ + resource_size_t io_offset; + resource_size_t mem_offset; + resource_size_t mem64_offset; /* Base of PCI Config space, can be per-PBM or shared. */ unsigned long config_space; Index: linux-2.6/include/linux/pci.h =================================================================== --- linux-2.6.orig/include/linux/pci.h +++ linux-2.6/include/linux/pci.h @@ -820,6 +820,8 @@ void pci_bus_add_device(struct pci_dev * void pci_read_bridge_bases(struct pci_bus *child); struct resource *pci_find_parent_resource(const struct pci_dev *dev, struct resource *res); +struct resource *pci_find_root_bus_resource(struct pci_bus *bus, + struct resource *res); u8 pci_swizzle_interrupt_pin(const struct pci_dev *dev, u8 pin); int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge); u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp); Index: linux-2.6/drivers/pci/pci.c =================================================================== --- linux-2.6.orig/drivers/pci/pci.c +++ linux-2.6/drivers/pci/pci.c @@ -415,18 +415,9 @@ int pci_find_ht_capability(struct pci_de } EXPORT_SYMBOL_GPL(pci_find_ht_capability); -/** - * pci_find_parent_resource - return resource region of parent bus of given region - * @dev: PCI device structure contains resources to be searched - * @res: child resource record for which parent is sought - * - * For given resource region of given device, return the resource - * region of parent bus the given region is contained in. - */ -struct resource *pci_find_parent_resource(const struct pci_dev *dev, - struct resource *res) +static struct resource *pci_find_bus_resource(const struct pci_bus *bus, + struct resource *res) { - const struct pci_bus *bus = dev->bus; struct resource *r; int i; @@ -456,8 +447,31 @@ struct resource *pci_find_parent_resourc } return NULL; } + +/** + * pci_find_parent_resource - return resource region of parent bus of given region + * @dev: PCI device structure contains resources to be searched + * @res: child resource record for which parent is sought + * + * For given resource region of given device, return the resource + * region of parent bus the given region is contained in. + */ +struct resource *pci_find_parent_resource(const struct pci_dev *dev, + struct resource *res) +{ + return pci_find_bus_resource(dev->bus, res); +} EXPORT_SYMBOL(pci_find_parent_resource); +struct resource *pci_find_root_bus_resource(struct pci_bus *bus, + struct resource *res) +{ + while (bus->parent) + bus = bus->parent; + + return pci_find_bus_resource(bus, res); +} + /** * pci_wait_for_pending - wait for @mask bit(s) to clear in status word @pos * @dev: the PCI device to operate on