diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-10-25 22:55:31 +0100 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-10-25 22:55:31 +0100 |
commit | ba210f5de42f4604730ffaea96bfb6e591740bde (patch) | |
tree | ed8e64cefdf21fc5339ee1b9fa9355c094ef5752 /drivers/acpi | |
parent | e3ed766b4958bf7889539f09aec3f6a72d2c4dd2 (diff) | |
parent | 02715e86b21955f107f376d84d165424ba9cd372 (diff) |
Merge branch 'acpi-pci'
* acpi-pci:
ia64/PCI/ACPI: Use common interface to support PCI host bridge
x86/PCI/ACPI: Use common interface to support PCI host bridge
ACPI/PCI: Reset acpi_root_dev->domain to 0 when pci_ignore_seg is set
PCI/ACPI: Add interface acpi_pci_root_create()
ia64/PCI: Use common struct resource_entry to replace struct iospace_resource
ia64/PCI/ACPI: Use common ACPI resource parsing interface for host bridge
ACPI/PCI: Enhance ACPI core to support sparse IO space
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/pci_root.c | 204 | ||||
-rw-r--r-- | drivers/acpi/resource.c | 9 |
2 files changed, 210 insertions, 3 deletions
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 393706a5261b..850d7bf0c873 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -652,6 +652,210 @@ static void acpi_pci_root_remove(struct acpi_device *device) kfree(root); } +/* + * Following code to support acpi_pci_root_create() is copied from + * arch/x86/pci/acpi.c and modified so it could be reused by x86, IA64 + * and ARM64. + */ +static void acpi_pci_root_validate_resources(struct device *dev, + struct list_head *resources, + unsigned long type) +{ + LIST_HEAD(list); + struct resource *res1, *res2, *root = NULL; + struct resource_entry *tmp, *entry, *entry2; + + BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0); + root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource; + + list_splice_init(resources, &list); + resource_list_for_each_entry_safe(entry, tmp, &list) { + bool free = false; + resource_size_t end; + + res1 = entry->res; + if (!(res1->flags & type)) + goto next; + + /* Exclude non-addressable range or non-addressable portion */ + end = min(res1->end, root->end); + if (end <= res1->start) { + dev_info(dev, "host bridge window %pR (ignored, not CPU addressable)\n", + res1); + free = true; + goto next; + } else if (res1->end != end) { + dev_info(dev, "host bridge window %pR ([%#llx-%#llx] ignored, not CPU addressable)\n", + res1, (unsigned long long)end + 1, + (unsigned long long)res1->end); + res1->end = end; + } + + resource_list_for_each_entry(entry2, resources) { + res2 = entry2->res; + if (!(res2->flags & type)) + continue; + + /* + * I don't like throwing away windows because then + * our resources no longer match the ACPI _CRS, but + * the kernel resource tree doesn't allow overlaps. + */ + if (resource_overlaps(res1, res2)) { + res2->start = min(res1->start, res2->start); + res2->end = max(res1->end, res2->end); + dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n", + res2, res1); + free = true; + goto next; + } + } + +next: + resource_list_del(entry); + if (free) + resource_list_free_entry(entry); + else + resource_list_add_tail(entry, resources); + } +} + +int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) +{ + int ret; + struct list_head *list = &info->resources; + struct acpi_device *device = info->bridge; + struct resource_entry *entry, *tmp; + unsigned long flags; + + flags = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_MEM_8AND16BIT; + ret = acpi_dev_get_resources(device, list, + acpi_dev_filter_resource_type_cb, + (void *)flags); + if (ret < 0) + dev_warn(&device->dev, + "failed to parse _CRS method, error code %d\n", ret); + else if (ret == 0) + dev_dbg(&device->dev, + "no IO and memory resources present in _CRS\n"); + else { + resource_list_for_each_entry_safe(entry, tmp, list) { + if (entry->res->flags & IORESOURCE_DISABLED) + resource_list_destroy_entry(entry); + else + entry->res->name = info->name; + } + acpi_pci_root_validate_resources(&device->dev, list, + IORESOURCE_MEM); + acpi_pci_root_validate_resources(&device->dev, list, + IORESOURCE_IO); + } + + return ret; +} + +static void pci_acpi_root_add_resources(struct acpi_pci_root_info *info) +{ + struct resource_entry *entry, *tmp; + struct resource *res, *conflict, *root = NULL; + + resource_list_for_each_entry_safe(entry, tmp, &info->resources) { + res = entry->res; + if (res->flags & IORESOURCE_MEM) + root = &iomem_resource; + else if (res->flags & IORESOURCE_IO) + root = &ioport_resource; + else + continue; + + conflict = insert_resource_conflict(root, res); + if (conflict) { + dev_info(&info->bridge->dev, + "ignoring host bridge window %pR (conflicts with %s %pR)\n", + res, conflict->name, conflict); + resource_list_destroy_entry(entry); + } + } +} + +static void __acpi_pci_root_release_info(struct acpi_pci_root_info *info) +{ + struct resource *res; + struct resource_entry *entry, *tmp; + + if (!info) + return; + + resource_list_for_each_entry_safe(entry, tmp, &info->resources) { + res = entry->res; + if (res->parent && + (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + release_resource(res); + resource_list_destroy_entry(entry); + } + + info->ops->release_info(info); +} + +static void acpi_pci_root_release_info(struct pci_host_bridge *bridge) +{ + struct resource *res; + struct resource_entry *entry; + + resource_list_for_each_entry(entry, &bridge->windows) { + res = entry->res; + if (res->parent && + (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + release_resource(res); + } + __acpi_pci_root_release_info(bridge->release_data); +} + +struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, + struct acpi_pci_root_ops *ops, + struct acpi_pci_root_info *info, + void *sysdata) +{ + int ret, busnum = root->secondary.start; + struct acpi_device *device = root->device; + int node = acpi_get_node(device->handle); + struct pci_bus *bus; + + info->root = root; + info->bridge = device; + info->ops = ops; + INIT_LIST_HEAD(&info->resources); + snprintf(info->name, sizeof(info->name), "PCI Bus %04x:%02x", + root->segment, busnum); + + if (ops->init_info && ops->init_info(info)) + goto out_release_info; + if (ops->prepare_resources) + ret = ops->prepare_resources(info); + else + ret = acpi_pci_probe_root_resources(info); + if (ret < 0) + goto out_release_info; + + pci_acpi_root_add_resources(info); + pci_add_resource(&info->resources, &root->secondary); + bus = pci_create_root_bus(NULL, busnum, ops->pci_ops, + sysdata, &info->resources); + if (!bus) + goto out_release_info; + + pci_scan_child_bus(bus); + pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge), + acpi_pci_root_release_info, info); + if (node != NUMA_NO_NODE) + dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node); + return bus; + +out_release_info: + __acpi_pci_root_release_info(info); + return NULL; +} + void __init acpi_pci_root_init(void) { acpi_hest_init(); diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 15d22db05054..cdc5c2599beb 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -119,7 +119,7 @@ bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res) EXPORT_SYMBOL_GPL(acpi_dev_resource_memory); static void acpi_dev_ioresource_flags(struct resource *res, u64 len, - u8 io_decode) + u8 io_decode, u8 translation_type) { res->flags = IORESOURCE_IO; @@ -131,6 +131,8 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len, if (io_decode == ACPI_DECODE_16) res->flags |= IORESOURCE_IO_16BIT_ADDR; + if (translation_type == ACPI_SPARSE_TRANSLATION) + res->flags |= IORESOURCE_IO_SPARSE; } static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, @@ -138,7 +140,7 @@ static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, { res->start = start; res->end = start + len - 1; - acpi_dev_ioresource_flags(res, len, io_decode); + acpi_dev_ioresource_flags(res, len, io_decode, 0); } /** @@ -231,7 +233,8 @@ static bool acpi_decode_space(struct resource_win *win, acpi_dev_memresource_flags(res, len, wp); break; case ACPI_IO_RANGE: - acpi_dev_ioresource_flags(res, len, iodec); + acpi_dev_ioresource_flags(res, len, iodec, + addr->info.io.translation_type); break; case ACPI_BUS_NUMBER_RANGE: res->flags = IORESOURCE_BUS; |