From patchwork Fri Apr 15 17:06:44 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomasz Nowicki X-Patchwork-Id: 8853341 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id F2E7BBF29F for ; Fri, 15 Apr 2016 17:15:11 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 9D8EF201BB for ; Fri, 15 Apr 2016 17:15:10 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 44BF520160 for ; Fri, 15 Apr 2016 17:15:09 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1ar7J3-0000zE-5O; Fri, 15 Apr 2016 17:13:21 +0000 Received: from mail-wm0-f47.google.com ([74.125.82.47]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ar7Dk-0003Xh-6w for linux-arm-kernel@lists.infradead.org; Fri, 15 Apr 2016 17:07:56 +0000 Received: by mail-wm0-f47.google.com with SMTP id v188so39799775wme.1 for ; Fri, 15 Apr 2016 10:07:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=semihalf-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=htl63DVLUiJEqN/w8YQhxLsIW55xVBLP/EimA4zfr+k=; b=zIPicsRCO+b/KdzXwn0Hlm7M/kBPV40UjGZ9GCCb22/0g/RHY+9v2/ysw6Bh99Xb/J PM77venG7S4F99T39SdaSfPUsdFgkoj3sIS8tkOaxEvZ92qPaK1RK+K0VSVkGd04ozMA M8NvRFLuF1RnZaTVKaIexiT/tp3+GFKrIVupEFl2LeWQxjMrBGKwShdy+uIxYZdfg7UI QtxflmnKe7QcxfZ+9gyZptpvIE45SLPfo+0T/ddpLIA6+GfElia3ZBNsOBcwQ0we6y9w wqsbZz9U0Cndva/f5JRaY1NRPULtWu2u77UXw1S9oDPI9i9caLrp4o67CMArDcSxF0fL WSKg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=htl63DVLUiJEqN/w8YQhxLsIW55xVBLP/EimA4zfr+k=; b=WarY/RzkC6ydCTFDBLwbIrBLx7UnD1mDlv2pF0RLNDVBzw4HiYeYX/v0YeVx0KD2Bp NeIJkpB+IAq1kdtQLQgODi+qu2O1Gy9xerKimp6OaP2+790iXL4Dhm9Vsexg9c5eyxo9 uhgv/C6PR9eScFSY2FiPZpTiLH8Oe1DBk4BcatJr0TtH3iri5N2CaHX7rM0t8e3Nb/vm af+r14Ps+grV7fIrbtwbr+MTwTF9FHqotQe6dnwJ9EJm3aTRyigOJXoTwmwJuidgEArn AIfWgsBdFePAcyuxJqq/e+H4OuWEelbPhL9ErS40SMiRPx7cwMfHcYkoy2co0MErlsCs 40cA== X-Gm-Message-State: AOPr4FWUXoKroFxmn8mQnyxczi8z8fnxgVUKAQAKcFkGzHInA2PnFL2T+MChuYm0XnVOdA== X-Received: by 10.28.45.212 with SMTP id t203mr5618704wmt.71.1460740050219; Fri, 15 Apr 2016 10:07:30 -0700 (PDT) Received: from tn-HP-4.semihalf.local (cardhu.semihalf.com. [213.17.239.108]) by smtp.gmail.com with ESMTPSA id gr4sm14282723wjd.23.2016.04.15.10.07.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 15 Apr 2016 10:07:29 -0700 (PDT) From: Tomasz Nowicki To: helgaas@kernel.org, arnd@arndb.de, will.deacon@arm.com, catalin.marinas@arm.com, rafael@kernel.org, hanjun.guo@linaro.org, Lorenzo.Pieralisi@arm.com, okaya@codeaurora.org, jiang.liu@linux.intel.com, jchandra@broadcom.com Subject: [PATCH V6 09/13] pci, acpi: Support for ACPI based generic PCI host controller Date: Fri, 15 Apr 2016 19:06:44 +0200 Message-Id: <1460740008-19489-10-git-send-email-tn@semihalf.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1460740008-19489-1-git-send-email-tn@semihalf.com> References: <1460740008-19489-1-git-send-email-tn@semihalf.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160415_100752_663248_202FB5E4 X-CRM114-Status: GOOD ( 27.57 ) X-Spam-Score: -2.6 (--) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jcm@redhat.com, linaro-acpi@lists.linaro.org, linux-pci@vger.kernel.org, Liviu.Dudau@arm.com, ddaney@caviumnetworks.com, linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, robert.richter@caviumnetworks.com, Suravee.Suthikulpanit@amd.com, msalter@redhat.com, wangyijing@huawei.com, Tomasz Nowicki , mw@semihalf.com, linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-5.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,RP_MATCHES_RCVD,T_DKIM_INVALID,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 This patch is going to implement generic PCI host controller for ACPI world, similar to what pci-host-generic.c driver does for DT world. All such drivers, which we have seen so far, were implemented within arch/ directory since they had some arch assumptions (x86 and ia64). However, they all are doing similar thing, so it makes sense to find some common code and abstract it into the generic driver. In order to handle PCI config space regions properly, we define new MCFG interface which parses MCFG table and keep its entries in a list. New pci_mcfg_init call is defined so that we do not depend on PCI_MMCONFIG. Regions are not mapped until host bridge ask for it. The implementation of pci_acpi_scan_root() looks up the saved MCFG entries and sets up a new mapping. Generic PCI functions are used for accessing config space. Driver selects PCI_GENERIC_ECAM and uses functions from drivers/pci/ecam.h to create and access ECAM mappings. As mentioned in Kconfig help section, ACPI_PCI_HOST_GENERIC choice should be made on a per-architecture basis. This patch is heavily based on the updated version from Jayachandran C: https://lkml.org/lkml/2016/4/11/908 git: https://github.com/jchandra-brcm/linux/ (arm64-acpi-pci-v3) Signed-off-by: Tomasz Nowicki Signed-off-by: Jayachandran C --- drivers/acpi/Kconfig | 8 ++ drivers/acpi/Makefile | 1 + drivers/acpi/bus.c | 1 + drivers/acpi/pci_gen_host.c | 231 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 6 ++ 5 files changed, 247 insertions(+) create mode 100644 drivers/acpi/pci_gen_host.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 183ffa3..70272c5 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -346,6 +346,14 @@ config ACPI_PCI_SLOT i.e., segment/bus/device/function tuples, with physical slots in the system. If you are unsure, say N. +config ACPI_PCI_HOST_GENERIC + bool + select PCI_GENERIC_ECAM + help + Select this config option from the architecture Kconfig, + if it is preferred to enable ACPI PCI host controller driver which + has no arch-specific assumptions. + config X86_PM_TIMER bool "Power Management Timer Support" if EXPERT depends on X86 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 81e5cbc..b12fa64 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o +obj-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_gen_host.o acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index c068c82..803a1d7 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1107,6 +1107,7 @@ static int __init acpi_init(void) } pci_mmcfg_late_init(); + pci_mcfg_init(); acpi_scan_init(); acpi_ec_init(); acpi_debugfs_init(); diff --git a/drivers/acpi/pci_gen_host.c b/drivers/acpi/pci_gen_host.c new file mode 100644 index 0000000..fd360b5 --- /dev/null +++ b/drivers/acpi/pci_gen_host.c @@ -0,0 +1,231 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ +#include +#include +#include +#include +#include + +#include "../pci/ecam.h" + +#define PREFIX "ACPI: " + +/* Structure to hold entries from the MCFG table */ +struct mcfg_entry { + struct list_head list; + phys_addr_t addr; + u16 segment; + u8 bus_start; + u8 bus_end; +}; + +/* List to save mcfg entries */ +static LIST_HEAD(pci_mcfg_list); +static DEFINE_MUTEX(pci_mcfg_lock); + +/* ACPI info for generic ACPI PCI controller */ +struct acpi_pci_generic_root_info { + struct acpi_pci_root_info common; + struct pci_config_window *cfg; /* config space mapping */ +}; + +/* Find the entry in mcfg list which contains range bus_start */ +static struct mcfg_entry *pci_mcfg_lookup(u16 seg, u8 bus_start) +{ + struct mcfg_entry *e; + + list_for_each_entry(e, &pci_mcfg_list, list) { + if (e->segment == seg && + e->bus_start <= bus_start && bus_start <= e->bus_end) + return e; + } + + return NULL; +} + + +/* + * Lookup the bus range for the domain in MCFG, and set up config space + * mapping. + */ +static int pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root, + struct acpi_pci_generic_root_info *ri) +{ + u16 seg = root->segment; + u8 bus_start = root->secondary.start; + u8 bus_end = root->secondary.end; + struct pci_config_window *cfg; + struct mcfg_entry *e; + phys_addr_t addr; + int err = 0; + + mutex_lock(&pci_mcfg_lock); + e = pci_mcfg_lookup(seg, bus_start); + if (!e) { + addr = acpi_pci_root_get_mcfg_addr(root->device->handle); + if (addr == 0) { + pr_err(PREFIX"%04x:%02x-%02x bus range error\n", + seg, bus_start, bus_end); + err = -ENOENT; + goto err_out; + } + } else { + if (bus_start != e->bus_start) { + pr_err("%04x:%02x-%02x bus range mismatch %02x\n", + seg, bus_start, bus_end, e->bus_start); + err = -EINVAL; + goto err_out; + } else if (bus_end != e->bus_end) { + pr_warn("%04x:%02x-%02x bus end mismatch %02x\n", + seg, bus_start, bus_end, e->bus_end); + bus_end = min(bus_end, e->bus_end); + } + addr = e->addr; + } + + cfg = pci_generic_ecam_create(&root->device->dev, addr, bus_start, + bus_end, &pci_generic_ecam_default_ops); + if (IS_ERR(cfg)) { + err = PTR_ERR(cfg); + pr_err("%04x:%02x-%02x error %d mapping CAM\n", seg, + bus_start, bus_end, err); + goto err_out; + } + + cfg->domain = seg; + ri->cfg = cfg; +err_out: + mutex_unlock(&pci_mcfg_lock); + return err; +} + +/* release_info: free resrouces allocated by init_info */ +static void pci_acpi_generic_release_info(struct acpi_pci_root_info *ci) +{ + struct acpi_pci_generic_root_info *ri; + + ri = container_of(ci, struct acpi_pci_generic_root_info, common); + pci_generic_ecam_free(ri->cfg); + kfree(ri); +} + +static struct acpi_pci_root_ops acpi_pci_root_ops = { + .release_info = pci_acpi_generic_release_info, +}; + +/* Interface called from ACPI code to setup PCI host controller */ +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +{ + int node = acpi_get_node(root->device->handle); + struct acpi_pci_generic_root_info *ri; + struct pci_bus *bus, *child; + int err; + + ri = kzalloc_node(sizeof(*ri), GFP_KERNEL, node); + if (!ri) + return NULL; + + err = pci_acpi_setup_ecam_mapping(root, ri); + if (err) + return NULL; + + acpi_pci_root_ops.pci_ops = &ri->cfg->ops->pci_ops; + bus = acpi_pci_root_create(root, &acpi_pci_root_ops, &ri->common, + ri->cfg); + if (!bus) + return NULL; + + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + return bus; +} + +/* handle MCFG table entries */ +static __init int pci_mcfg_parse(struct acpi_table_header *header) +{ + struct acpi_table_mcfg *mcfg; + struct acpi_mcfg_allocation *mptr; + struct mcfg_entry *e, *arr; + int i, n; + + if (!header) + return -EINVAL; + + mcfg = (struct acpi_table_mcfg *)header; + mptr = (struct acpi_mcfg_allocation *) &mcfg[1]; + n = (header->length - sizeof(*mcfg)) / sizeof(*mptr); + if (n <= 0 || n > 255) { + pr_err(PREFIX " MCFG has incorrect entries (%d).\n", n); + return -EINVAL; + } + + arr = kcalloc(n, sizeof(*arr), GFP_KERNEL); + if (!arr) + return -ENOMEM; + + for (i = 0, e = arr; i < n; i++, mptr++, e++) { + e->segment = mptr->pci_segment; + e->addr = mptr->address; + e->bus_start = mptr->start_bus_number; + e->bus_end = mptr->end_bus_number; + list_add(&e->list, &pci_mcfg_list); + pr_info(PREFIX + "MCFG entry for domain %04x [bus %02x-%02x] (base %pa)\n", + e->segment, e->bus_start, e->bus_end, &e->addr); + } + + return 0; +} + +/* Interface called by ACPI - parse and save MCFG table */ +void __init pci_mcfg_init(void) +{ + int err = acpi_table_parse(ACPI_SIG_MCFG, pci_mcfg_parse); + if (err) + pr_err(PREFIX "Failed to parse MCFG (%d)\n", err); + else if (list_empty(&pci_mcfg_list)) + pr_info(PREFIX "No valid entries in MCFG table.\n"); + else { + struct mcfg_entry *e; + int i = 0; + list_for_each_entry(e, &pci_mcfg_list, list) + i++; + pr_info(PREFIX "MCFG table loaded, %d entries\n", i); + } +} + +/* Raw operations, works only for MCFG entries with an associated bus */ +int raw_pci_read(unsigned int domain, unsigned int busn, unsigned int devfn, + int reg, int len, u32 *val) +{ + struct pci_bus *bus = pci_find_bus(domain, busn); + + if (!bus) + return PCIBIOS_DEVICE_NOT_FOUND; + return bus->ops->read(bus, devfn, reg, len, val); +} + +int raw_pci_write(unsigned int domain, unsigned int busn, unsigned int devfn, + int reg, int len, u32 val) +{ + struct pci_bus *bus = pci_find_bus(domain, busn); + + if (!bus) + return PCIBIOS_DEVICE_NOT_FOUND; + return bus->ops->write(bus, devfn, reg, len, val); +} diff --git a/include/linux/pci.h b/include/linux/pci.h index df1f33d..c0422ea 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1729,6 +1729,12 @@ static inline void pci_mmcfg_early_init(void) { } static inline void pci_mmcfg_late_init(void) { } #endif +#ifdef CONFIG_ACPI_PCI_HOST_GENERIC +void __init pci_mcfg_init(void); +#else +static inline void pci_mcfg_init(void) { return; } +#endif + int pci_ext_cfg_avail(void); void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar);