From patchwork Tue Apr 7 04:31:10 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Liu X-Patchwork-Id: 6167151 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: X-Original-To: patchwork-linux-pci@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 5F13F9F72C for ; Tue, 7 Apr 2015 04:29:41 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 58772202F8 for ; Tue, 7 Apr 2015 04:29:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1F50220381 for ; Tue, 7 Apr 2015 04:29:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753193AbbDGE33 (ORCPT ); Tue, 7 Apr 2015 00:29:29 -0400 Received: from mga14.intel.com ([192.55.52.115]:34952 "EHLO mga14.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753196AbbDGE31 (ORCPT ); Tue, 7 Apr 2015 00:29:27 -0400 Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by fmsmga103.fm.intel.com with ESMTP; 06 Apr 2015 21:29:25 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.11,536,1422950400"; d="scan'208";a="477526040" Received: from gerry-dev.bj.intel.com ([10.238.158.72]) by FMSMGA003.fm.intel.com with ESMTP; 06 Apr 2015 21:29:23 -0700 From: Jiang Liu To: "Rafael J . Wysocki" , Bjorn Helgaas , Len Brown Cc: Jiang Liu , Lv Zheng , LKML , linux-pci@vger.kernel.org, linux-acpi@vger.kernel.org, "x86 @ kernel . org" Subject: [RFC 5/7] PCI/ACPI: Consolidate common PCI host bridge code into ACPI core Date: Tue, 7 Apr 2015 12:31:10 +0800 Message-Id: <1428381072-27486-6-git-send-email-jiang.liu@linux.intel.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1428381072-27486-1-git-send-email-jiang.liu@linux.intel.com> References: <1428381072-27486-1-git-send-email-jiang.liu@linux.intel.com> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Introduce common interface acpi_pci_root_create() and related data structures to create PCI root bus for ACPI PCI host bridges. It will be used to kill duplicated arch specific code for IA64 and x86. It may also help ARM64 in future. Tested-by: Tony Luck Signed-off-by: Jiang Liu --- drivers/acpi/pci_root.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci-acpi.h | 24 ++++++ 2 files changed, 239 insertions(+) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 68a5f712cd19..32d6a5ba5534 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -659,6 +659,221 @@ static void acpi_pci_root_remove(struct acpi_device *device) kfree(root); } +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); + } +} + +static int acpi_pci_probe_root_resources(struct acpi_pci_root_info_common *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_WINDOW | 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_common *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_common *info) +{ + struct resource *res; + struct resource_entry *entry, *tmp; + + if (!info) + return; + + if (info->ops && info->ops->release_info) + info->ops->release_info(info); + + 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); + } + + kfree(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, + size_t extra_size) +{ + int ret, busnum = root->secondary.start; + struct acpi_device *device = root->device; + struct acpi_pci_root_info_common *info; + struct pci_bus *bus; + + info = kzalloc(sizeof(*info) + extra_size, GFP_KERNEL); + if (!info) { + dev_err(&device->dev, + "pci_bus %04x:%02x: ignored (out of memory)\n", + root->segment, busnum); + return NULL; + } + + info->controller.segment = root->segment; + info->controller.node = acpi_get_node(device->handle); + info->controller.companion = device; + info->root = root; + info->bridge = device; + info->ops = ops; + INIT_LIST_HEAD(&info->resources); + snprintf(info->name, sizeof(info->name), "PCI Bus %04x:%02x", + info->controller.segment, busnum); + if (ops->init_info && ops->init_info(info)) { + kfree(info); + return NULL; + } + + ret = acpi_pci_probe_root_resources(info); + if (ops->prepare_resources) + ret = ops->prepare_resources(info, ret); + if (ret < 0) + goto out_release_info; + else if (ret > 0) + pci_acpi_root_add_resources(info); + pci_add_resource(&info->resources, &root->secondary); + + bus = pci_create_root_bus(NULL, busnum, ops->pci_ops, + &info->controller, &info->resources); + if (bus) { + pci_scan_child_bus(bus); + pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge), + acpi_pci_root_release_info, info); + if (info->controller.node != NUMA_NO_NODE) + dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", + info->controller.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/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 24c7728ca681..c237c97efe63 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -52,6 +52,30 @@ static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus) return ACPI_HANDLE(dev); } +struct acpi_pci_root; +struct acpi_pci_root_ops; + +struct acpi_pci_root_info_common { + struct pci_controller controller; + struct acpi_pci_root *root; + struct acpi_device *bridge; + struct acpi_pci_root_ops *ops; + struct list_head resources; + char name[16]; +}; + +struct acpi_pci_root_ops { + struct pci_ops *pci_ops; + int (*init_info)(struct acpi_pci_root_info_common *info); + void (*release_info)(struct acpi_pci_root_info_common *info); + int (*prepare_resources)(struct acpi_pci_root_info_common *info, + int status); +}; + +extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, + struct acpi_pci_root_ops *ops, + size_t extra_size); + void acpi_pci_add_bus(struct pci_bus *bus); void acpi_pci_remove_bus(struct pci_bus *bus);