From 3772aea7d6f36cfa1dae17f04ffed64b4d747aab Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:37 +0800 Subject: ia64/PCI/ACPI: Use common ACPI resource parsing interface for host bridge Use common ACPI resource parsing interface to parse ACPI resources for PCI host bridge, so we could share more code between IA64 and x86. Later we will consolidate arch specific implementations into ACPI core. Tested-by: Tony Luck Signed-off-by: Jiang Liu Reviewed-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki --- arch/ia64/pci/pci.c | 414 ++++++++++++++++++++++++---------------------------- 1 file changed, 193 insertions(+), 221 deletions(-) (limited to 'arch/ia64') diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 7cc3be9fa7c6..d20db9e48014 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -115,29 +115,12 @@ struct pci_ops pci_root_ops = { .write = pci_write, }; -/* Called by ACPI when it finds a new root bus. */ - -static struct pci_controller *alloc_pci_controller(int seg) -{ - struct pci_controller *controller; - - controller = kzalloc(sizeof(*controller), GFP_KERNEL); - if (!controller) - return NULL; - - controller->segment = seg; - return controller; -} - struct pci_root_info { + struct pci_controller controller; struct acpi_device *bridge; - struct pci_controller *controller; struct list_head resources; - struct resource *res; - resource_size_t *res_offset; - unsigned int res_num; struct list_head io_resources; - char *name; + char name[16]; }; static unsigned int @@ -168,11 +151,11 @@ new_space (u64 phys_base, int sparse) return i; } -static u64 add_io_space(struct pci_root_info *info, - struct acpi_resource_address64 *addr) +static int add_io_space(struct device *dev, struct pci_root_info *info, + struct resource_entry *entry) { struct iospace_resource *iospace; - struct resource *resource; + struct resource *resource, *res = entry->res; char *name; unsigned long base, min, max, base_port; unsigned int sparse = 0, space_nr, len; @@ -180,27 +163,24 @@ static u64 add_io_space(struct pci_root_info *info, len = strlen(info->name) + 32; iospace = kzalloc(sizeof(*iospace) + len, GFP_KERNEL); if (!iospace) { - dev_err(&info->bridge->dev, - "PCI: No memory for %s I/O port space\n", - info->name); - goto out; + dev_err(dev, "PCI: No memory for %s I/O port space\n", + info->name); + return -ENOMEM; } - name = (char *)(iospace + 1); - - min = addr->address.minimum; - max = min + addr->address.address_length - 1; - if (addr->info.io.translation_type == ACPI_SPARSE_TRANSLATION) + if (res->flags & IORESOURCE_IO_SPARSE) sparse = 1; - - space_nr = new_space(addr->address.translation_offset, sparse); + space_nr = new_space(entry->offset, sparse); if (space_nr == ~0) goto free_resource; + name = (char *)(iospace + 1); + min = res->start - entry->offset; + max = res->end - entry->offset; base = __pa(io_space[space_nr].mmio_base); base_port = IO_SPACE_BASE(space_nr); snprintf(name, len, "%s I/O Ports %08lx-%08lx", info->name, - base_port + min, base_port + max); + base_port + min, base_port + max); /* * The SDM guarantees the legacy 0-64K space is sparse, but if the @@ -216,153 +196,195 @@ static u64 add_io_space(struct pci_root_info *info, resource->start = base + (sparse ? IO_SPACE_SPARSE_ENCODING(min) : min); resource->end = base + (sparse ? IO_SPACE_SPARSE_ENCODING(max) : max); if (insert_resource(&iomem_resource, resource)) { - dev_err(&info->bridge->dev, - "can't allocate host bridge io space resource %pR\n", - resource); + dev_err(dev, + "can't allocate host bridge io space resource %pR\n", + resource); goto free_resource; } + entry->offset = base_port; + res->start = min + base_port; + res->end = max + base_port; list_add_tail(&iospace->list, &info->io_resources); - return base_port; + + return 0; free_resource: kfree(iospace); -out: - return ~0; + return -ENOSPC; +} + +/* + * An IO port or MMIO resource assigned to a PCI host bridge may be + * consumed by the host bridge itself or available to its child + * bus/devices. The ACPI specification defines a bit (Producer/Consumer) + * to tell whether the resource is consumed by the host bridge itself, + * but firmware hasn't used that bit consistently, so we can't rely on it. + * + * On x86 and IA64 platforms, all IO port and MMIO resources are assumed + * to be available to child bus/devices except one special case: + * IO port [0xCF8-0xCFF] is consumed by the host bridge itself + * to access PCI configuration space. + * + * So explicitly filter out PCI CFG IO ports[0xCF8-0xCFF]. + */ +static bool resource_is_pcicfg_ioport(struct resource *res) +{ + return (res->flags & IORESOURCE_IO) && + res->start == 0xCF8 && res->end == 0xCFF; } -static acpi_status resource_to_window(struct acpi_resource *resource, - struct acpi_resource_address64 *addr) +static int +probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, + int busnum, int domain) { - acpi_status status; + int ret; + struct list_head *list = &info->resources; + struct resource_entry *entry, *tmp; - /* - * We're only interested in _CRS descriptors that are - * - address space descriptors for memory or I/O space - * - non-zero size - */ - status = acpi_resource_to_address64(resource, addr); - if (ACPI_SUCCESS(status) && - (addr->resource_type == ACPI_MEMORY_RANGE || - addr->resource_type == ACPI_IO_RANGE) && - addr->address.address_length) - return AE_OK; - - return AE_ERROR; -} - -static acpi_status count_window(struct acpi_resource *resource, void *data) -{ - unsigned int *windows = (unsigned int *) data; - struct acpi_resource_address64 addr; - acpi_status status; - - status = resource_to_window(resource, &addr); - if (ACPI_SUCCESS(status)) - (*windows)++; - - return AE_OK; -} - -static acpi_status add_window(struct acpi_resource *res, void *data) -{ - struct pci_root_info *info = data; - struct resource *resource; - struct acpi_resource_address64 addr; - acpi_status status; - unsigned long flags, offset = 0; - struct resource *root; - - /* Return AE_OK for non-window resources to keep scanning for more */ - status = resource_to_window(res, &addr); - if (!ACPI_SUCCESS(status)) - return AE_OK; - - if (addr.resource_type == ACPI_MEMORY_RANGE) { - flags = IORESOURCE_MEM; - root = &iomem_resource; - offset = addr.address.translation_offset; - } else if (addr.resource_type == ACPI_IO_RANGE) { - flags = IORESOURCE_IO; - root = &ioport_resource; - offset = add_io_space(info, &addr); - if (offset == ~0) - return AE_OK; - } else - return AE_OK; - - resource = &info->res[info->res_num]; - resource->name = info->name; - resource->flags = flags; - resource->start = addr.address.minimum + offset; - resource->end = resource->start + addr.address.address_length - 1; - info->res_offset[info->res_num] = offset; - - if (insert_resource(root, resource)) { - dev_err(&info->bridge->dev, - "can't allocate host bridge window %pR\n", - resource); - } else { - if (offset) - dev_info(&info->bridge->dev, "host bridge window %pR " - "(PCI address [%#llx-%#llx])\n", - resource, - resource->start - offset, - resource->end - offset); - else - dev_info(&info->bridge->dev, - "host bridge window %pR\n", resource); - } - /* HP's firmware has a hack to work around a Windows bug. - * Ignore these tiny memory ranges */ - if (!((resource->flags & IORESOURCE_MEM) && - (resource->end - resource->start < 16))) - pci_add_resource_offset(&info->resources, resource, - info->res_offset[info->res_num]); + ret = acpi_dev_get_resources(device, list, + acpi_dev_filter_resource_type_cb, + (void *)(IORESOURCE_IO | IORESOURCE_MEM)); + 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_is_pcicfg_ioport(entry->res)) + resource_list_destroy_entry(entry); + else + entry->res->name = info->name; + } - info->res_num++; - return AE_OK; + return ret; } -static void free_pci_root_info_res(struct pci_root_info *info) -{ - struct iospace_resource *iospace, *tmp; +static void 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; + } - list_for_each_entry_safe(iospace, tmp, &info->io_resources, list) - kfree(iospace); + 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; + } + } - kfree(info->name); - kfree(info->res); - info->res = NULL; - kfree(info->res_offset); - info->res_offset = NULL; - info->res_num = 0; - kfree(info->controller); - info->controller = NULL; +next: + resource_list_del(entry); + if (free) + resource_list_free_entry(entry); + else + resource_list_add_tail(entry, resources); + } +} + +static void add_resources(struct pci_root_info *info, struct device *dev) +{ + struct resource_entry *entry, *tmp; + struct resource *res, *conflict, *root = NULL; + struct list_head *list = &info->resources; + + validate_resources(dev, list, IORESOURCE_MEM); + validate_resources(dev, list, IORESOURCE_IO); + + resource_list_for_each_entry_safe(entry, tmp, list) { + res = entry->res; + if (res->flags & IORESOURCE_MEM) { + root = &iomem_resource; + /* + * HP's firmware has a hack to work around a Windows + * bug. Ignore these tiny memory ranges. + */ + if (resource_size(res) <= 16) { + resource_list_destroy_entry(entry); + continue; + } + } else if (res->flags & IORESOURCE_IO) { + root = &ioport_resource; + if (add_io_space(&info->bridge->dev, info, entry)) { + resource_list_destroy_entry(entry); + continue; + } + } else { + BUG_ON(res); + } + + conflict = insert_resource_conflict(root, res); + if (conflict) { + dev_info(dev, + "ignoring host bridge window %pR (conflicts with %s %pR)\n", + res, conflict->name, conflict); + resource_list_destroy_entry(entry); + } + } } static void __release_pci_root_info(struct pci_root_info *info) { - int i; struct resource *res; - struct iospace_resource *iospace; + struct iospace_resource *iospace, *tmp; + struct resource_entry *entry, *tentry; - list_for_each_entry(iospace, &info->io_resources, list) + list_for_each_entry_safe(iospace, tmp, &info->io_resources, list) { release_resource(&iospace->res); + kfree(iospace); + } - for (i = 0; i < info->res_num; i++) { - res = &info->res[i]; - - if (!res->parent) - continue; - - if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) - continue; - - release_resource(res); + resource_list_for_each_entry_safe(entry, tentry, &info->resources) { + res = entry->res; + if (res->parent && + (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + release_resource(res); + resource_list_destroy_entry(entry); } - free_pci_root_info_res(info); kfree(info); } @@ -373,99 +395,49 @@ static void release_pci_root_info(struct pci_host_bridge *bridge) __release_pci_root_info(info); } -static int -probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, - int busnum, int domain) -{ - char *name; - - name = kmalloc(16, GFP_KERNEL); - if (!name) - return -ENOMEM; - - sprintf(name, "PCI Bus %04x:%02x", domain, busnum); - info->bridge = device; - info->name = name; - - acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_window, - &info->res_num); - if (info->res_num) { - info->res = - kzalloc_node(sizeof(*info->res) * info->res_num, - GFP_KERNEL, info->controller->node); - if (!info->res) { - kfree(name); - return -ENOMEM; - } - - info->res_offset = - kzalloc_node(sizeof(*info->res_offset) * info->res_num, - GFP_KERNEL, info->controller->node); - if (!info->res_offset) { - kfree(name); - kfree(info->res); - info->res = NULL; - return -ENOMEM; - } - - info->res_num = 0; - acpi_walk_resources(device->handle, METHOD_NAME__CRS, - add_window, info); - } else - kfree(name); - - return 0; -} - struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) { struct acpi_device *device = root->device; int domain = root->segment; int bus = root->secondary.start; - struct pci_controller *controller; - struct pci_root_info *info = NULL; - int busnum = root->secondary.start; + struct pci_root_info *info; struct pci_bus *pbus; int ret; - controller = alloc_pci_controller(domain); - if (!controller) - return NULL; - - controller->companion = device; - controller->node = acpi_get_node(device->handle); - info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { dev_err(&device->dev, - "pci_bus %04x:%02x: ignored (out of memory)\n", - domain, busnum); - kfree(controller); + "pci_bus %04x:%02x: ignored (out of memory)\n", + domain, bus); return NULL; } - info->controller = controller; - INIT_LIST_HEAD(&info->io_resources); + info->controller.segment = domain; + info->controller.companion = device; + info->controller.node = acpi_get_node(device->handle); + info->bridge = device; INIT_LIST_HEAD(&info->resources); + INIT_LIST_HEAD(&info->io_resources); + snprintf(info->name, sizeof(info->name), + "PCI Bus %04x:%02x", domain, bus); - ret = probe_pci_root_info(info, device, busnum, domain); - if (ret) { - kfree(info->controller); + ret = probe_pci_root_info(info, device, bus, domain); + if (ret <= 0) { kfree(info); return NULL; } - /* insert busn resource at first */ + add_resources(info, &info->bridge->dev); pci_add_resource(&info->resources, &root->secondary); + /* * See arch/x86/pci/acpi.c. * The desired pci bus might already be scanned in a quirk. We * should handle the case here, but it appears that IA64 hasn't * such quirk. So we just ignore the case now. */ - pbus = pci_create_root_bus(NULL, bus, &pci_root_ops, controller, - &info->resources); + pbus = pci_create_root_bus(NULL, bus, &pci_root_ops, + &info->controller, &info->resources); if (!pbus) { - pci_free_resource_list(&info->resources); __release_pci_root_info(info); return NULL; } -- cgit v1.2.3-70-g09d2 From 3f7abdefc07755d67e2b2b63608d3128f6e0b3c5 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:38 +0800 Subject: ia64/PCI: Use common struct resource_entry to replace struct iospace_resource Use common struct resource_entry to replace private struct iospace_resource. Signed-off-by: Jiang Liu Reviewed-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki --- arch/ia64/include/asm/pci.h | 5 ----- arch/ia64/pci/pci.c | 17 ++++++++--------- 2 files changed, 8 insertions(+), 14 deletions(-) (limited to 'arch/ia64') diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index 36d2c1e3928b..07039d168f37 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -64,11 +64,6 @@ extern int pci_mmap_legacy_page_range(struct pci_bus *bus, #define pci_legacy_read platform_pci_legacy_read #define pci_legacy_write platform_pci_legacy_write -struct iospace_resource { - struct list_head list; - struct resource res; -}; - struct pci_controller { struct acpi_device *companion; void *iommu; diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index d20db9e48014..b1846b891ea5 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -154,14 +154,14 @@ new_space (u64 phys_base, int sparse) static int add_io_space(struct device *dev, struct pci_root_info *info, struct resource_entry *entry) { - struct iospace_resource *iospace; + struct resource_entry *iospace; struct resource *resource, *res = entry->res; char *name; unsigned long base, min, max, base_port; unsigned int sparse = 0, space_nr, len; len = strlen(info->name) + 32; - iospace = kzalloc(sizeof(*iospace) + len, GFP_KERNEL); + iospace = resource_list_create_entry(NULL, len); if (!iospace) { dev_err(dev, "PCI: No memory for %s I/O port space\n", info->name); @@ -190,7 +190,7 @@ static int add_io_space(struct device *dev, struct pci_root_info *info, if (space_nr == 0) sparse = 1; - resource = &iospace->res; + resource = iospace->res; resource->name = name; resource->flags = IORESOURCE_MEM; resource->start = base + (sparse ? IO_SPACE_SPARSE_ENCODING(min) : min); @@ -205,12 +205,12 @@ static int add_io_space(struct device *dev, struct pci_root_info *info, entry->offset = base_port; res->start = min + base_port; res->end = max + base_port; - list_add_tail(&iospace->list, &info->io_resources); + resource_list_add_tail(iospace, &info->io_resources); return 0; free_resource: - kfree(iospace); + resource_list_free_entry(iospace); return -ENOSPC; } @@ -369,12 +369,11 @@ static void add_resources(struct pci_root_info *info, struct device *dev) static void __release_pci_root_info(struct pci_root_info *info) { struct resource *res; - struct iospace_resource *iospace, *tmp; struct resource_entry *entry, *tentry; - list_for_each_entry_safe(iospace, tmp, &info->io_resources, list) { - release_resource(&iospace->res); - kfree(iospace); + resource_list_for_each_entry_safe(entry, tentry, &info->io_resources) { + release_resource(entry->res); + resource_list_destroy_entry(entry); } resource_list_for_each_entry_safe(entry, tentry, &info->resources) { -- cgit v1.2.3-70-g09d2 From 02715e86b21955f107f376d84d165424ba9cd372 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:42 +0800 Subject: ia64/PCI/ACPI: Use common interface to support PCI host bridge Use common interface to simplify PCI host bridge implementation. Tested-by: Tony Luck Signed-off-by: Jiang Liu Signed-off-by: Rafael J. Wysocki --- arch/ia64/pci/pci.c | 235 +++++++++++----------------------------------------- 1 file changed, 48 insertions(+), 187 deletions(-) (limited to 'arch/ia64') diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index b1846b891ea5..8f6ac2f8ae4c 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -116,15 +116,12 @@ struct pci_ops pci_root_ops = { }; struct pci_root_info { + struct acpi_pci_root_info common; struct pci_controller controller; - struct acpi_device *bridge; - struct list_head resources; struct list_head io_resources; - char name[16]; }; -static unsigned int -new_space (u64 phys_base, int sparse) +static unsigned int new_space(u64 phys_base, int sparse) { u64 mmio_base; int i; @@ -160,11 +157,11 @@ static int add_io_space(struct device *dev, struct pci_root_info *info, unsigned long base, min, max, base_port; unsigned int sparse = 0, space_nr, len; - len = strlen(info->name) + 32; + len = strlen(info->common.name) + 32; iospace = resource_list_create_entry(NULL, len); if (!iospace) { dev_err(dev, "PCI: No memory for %s I/O port space\n", - info->name); + info->common.name); return -ENOMEM; } @@ -179,7 +176,7 @@ static int add_io_space(struct device *dev, struct pci_root_info *info, max = res->end - entry->offset; base = __pa(io_space[space_nr].mmio_base); base_port = IO_SPACE_BASE(space_nr); - snprintf(name, len, "%s I/O Ports %08lx-%08lx", info->name, + snprintf(name, len, "%s I/O Ports %08lx-%08lx", info->common.name, base_port + min, base_port + max); /* @@ -234,217 +231,81 @@ static bool resource_is_pcicfg_ioport(struct resource *res) res->start == 0xCF8 && res->end == 0xCFF; } -static int -probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, - int busnum, int domain) +static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci) { - int ret; - struct list_head *list = &info->resources; + struct device *dev = &ci->bridge->dev; + struct pci_root_info *info; + struct resource *res; struct resource_entry *entry, *tmp; - - ret = acpi_dev_get_resources(device, list, - acpi_dev_filter_resource_type_cb, - (void *)(IORESOURCE_IO | IORESOURCE_MEM)); - 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_is_pcicfg_ioport(entry->res)) - resource_list_destroy_entry(entry); - else - entry->res->name = info->name; - } - - return ret; -} - -static void 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; + int status; + + status = acpi_pci_probe_root_resources(ci); + if (status > 0) { + info = container_of(ci, struct pci_root_info, common); + resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { + res = entry->res; + if (res->flags & IORESOURCE_MEM) { + /* + * HP's firmware has a hack to work around a + * Windows bug. Ignore these tiny memory ranges. + */ + if (resource_size(res) <= 16) { + resource_list_del(entry); + insert_resource(&iomem_resource, + entry->res); + resource_list_add_tail(entry, + &info->io_resources); + } + } else if (res->flags & IORESOURCE_IO) { + if (resource_is_pcicfg_ioport(entry->res)) + resource_list_destroy_entry(entry); + else if (add_io_space(dev, info, entry)) + resource_list_destroy_entry(entry); } } - -next: - resource_list_del(entry); - if (free) - resource_list_free_entry(entry); - else - resource_list_add_tail(entry, resources); } -} -static void add_resources(struct pci_root_info *info, struct device *dev) -{ - struct resource_entry *entry, *tmp; - struct resource *res, *conflict, *root = NULL; - struct list_head *list = &info->resources; - - validate_resources(dev, list, IORESOURCE_MEM); - validate_resources(dev, list, IORESOURCE_IO); - - resource_list_for_each_entry_safe(entry, tmp, list) { - res = entry->res; - if (res->flags & IORESOURCE_MEM) { - root = &iomem_resource; - /* - * HP's firmware has a hack to work around a Windows - * bug. Ignore these tiny memory ranges. - */ - if (resource_size(res) <= 16) { - resource_list_destroy_entry(entry); - continue; - } - } else if (res->flags & IORESOURCE_IO) { - root = &ioport_resource; - if (add_io_space(&info->bridge->dev, info, entry)) { - resource_list_destroy_entry(entry); - continue; - } - } else { - BUG_ON(res); - } - - conflict = insert_resource_conflict(root, res); - if (conflict) { - dev_info(dev, - "ignoring host bridge window %pR (conflicts with %s %pR)\n", - res, conflict->name, conflict); - resource_list_destroy_entry(entry); - } - } + return status; } -static void __release_pci_root_info(struct pci_root_info *info) +static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci) { - struct resource *res; - struct resource_entry *entry, *tentry; + struct pci_root_info *info; + struct resource_entry *entry, *tmp; - resource_list_for_each_entry_safe(entry, tentry, &info->io_resources) { + info = container_of(ci, struct pci_root_info, common); + resource_list_for_each_entry_safe(entry, tmp, &info->io_resources) { release_resource(entry->res); resource_list_destroy_entry(entry); } - - resource_list_for_each_entry_safe(entry, tentry, &info->resources) { - res = entry->res; - if (res->parent && - (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) - release_resource(res); - resource_list_destroy_entry(entry); - } - kfree(info); } -static void release_pci_root_info(struct pci_host_bridge *bridge) -{ - struct pci_root_info *info = bridge->release_data; - - __release_pci_root_info(info); -} +static struct acpi_pci_root_ops pci_acpi_root_ops = { + .pci_ops = &pci_root_ops, + .release_info = pci_acpi_root_release_info, + .prepare_resources = pci_acpi_root_prepare_resources, +}; struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) { struct acpi_device *device = root->device; - int domain = root->segment; - int bus = root->secondary.start; struct pci_root_info *info; - struct pci_bus *pbus; - int ret; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { dev_err(&device->dev, "pci_bus %04x:%02x: ignored (out of memory)\n", - domain, bus); + root->segment, (int)root->secondary.start); return NULL; } - info->controller.segment = domain; + info->controller.segment = root->segment; info->controller.companion = device; info->controller.node = acpi_get_node(device->handle); - info->bridge = device; - INIT_LIST_HEAD(&info->resources); INIT_LIST_HEAD(&info->io_resources); - snprintf(info->name, sizeof(info->name), - "PCI Bus %04x:%02x", domain, bus); - - ret = probe_pci_root_info(info, device, bus, domain); - if (ret <= 0) { - kfree(info); - return NULL; - } - add_resources(info, &info->bridge->dev); - pci_add_resource(&info->resources, &root->secondary); - - /* - * See arch/x86/pci/acpi.c. - * The desired pci bus might already be scanned in a quirk. We - * should handle the case here, but it appears that IA64 hasn't - * such quirk. So we just ignore the case now. - */ - pbus = pci_create_root_bus(NULL, bus, &pci_root_ops, - &info->controller, &info->resources); - if (!pbus) { - __release_pci_root_info(info); - return NULL; - } - - pci_set_host_bridge_release(to_pci_host_bridge(pbus->bridge), - release_pci_root_info, info); - pci_scan_child_bus(pbus); - return pbus; + return acpi_pci_root_create(root, &pci_acpi_root_ops, + &info->common, &info->controller); } int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) -- cgit v1.2.3-70-g09d2