From patchwork Wed Dec 2 22:24:46 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Jayachandran C." X-Patchwork-Id: 7753661 Return-Path: X-Original-To: patchwork-linux-arm@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 589179F1C2 for ; Wed, 2 Dec 2015 22:06:43 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 3C3C220489 for ; Wed, 2 Dec 2015 22:06:42 +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 F01CC20544 for ; Wed, 2 Dec 2015 22:06:40 +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 1a4FW0-0001SC-Lx; Wed, 02 Dec 2015 22:04:44 +0000 Received: from mail-gw3-out.broadcom.com ([216.31.210.64]) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1a4FVc-0001GB-PS for linux-arm-kernel@lists.infradead.org; Wed, 02 Dec 2015 22:04:23 +0000 X-IronPort-AV: E=Sophos;i="5.20,375,1444719600"; d="scan'208";a="82026334" Received: from irvexchcas07.broadcom.com (HELO IRVEXCHCAS07.corp.ad.broadcom.com) ([10.9.208.55]) by mail-gw3-out.broadcom.com with ESMTP; 02 Dec 2015 14:37:21 -0800 Received: from IRVEXCHSMTP2.corp.ad.broadcom.com (10.9.207.52) by IRVEXCHCAS07.corp.ad.broadcom.com (10.9.208.55) with Microsoft SMTP Server (TLS) id 14.3.235.1; Wed, 2 Dec 2015 14:03:56 -0800 Received: from mail-irva-13.broadcom.com (10.10.10.20) by IRVEXCHSMTP2.corp.ad.broadcom.com (10.9.207.52) with Microsoft SMTP Server id 14.3.235.1; Wed, 2 Dec 2015 14:03:56 -0800 Received: from netl-snoppy.ban.broadcom.com (unknown [10.132.128.129]) by mail-irva-13.broadcom.com (Postfix) with ESMTP id 6859540FE8; Wed, 2 Dec 2015 14:00:34 -0800 (PST) From: Jayachandran C To: , Subject: [PATCH 3/3] pci/host : Add a generic ACPI based host controller Date: Thu, 3 Dec 2015 03:54:46 +0530 Message-ID: <1449095086-5138-4-git-send-email-jchandra@broadcom.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1449095086-5138-1-git-send-email-jchandra@broadcom.com> References: <1449095086-5138-1-git-send-email-jchandra@broadcom.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20151202_140421_035435_0C21C154 X-CRM114-Status: GOOD ( 21.14 ) X-Spam-Score: -4.2 (----) 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: Jayachandran C Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, 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 Add a simple ACPI based PCI host controller. This is done by providing a simple implementation of pci_acpi_scan_root(). The MCFG table is parsed early and saved so that it can be mapped when ACPI calls pci_acpi_scan_root. This is enabled only for ARM64 now. Signed-off-by: Jayachandran C --- drivers/pci/host/Kconfig | 7 ++ drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-host-acpi.c | 211 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 drivers/pci/host/pci-host-acpi.c diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index f131ba9..8321efc 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -53,6 +53,13 @@ config PCI_RCAR_GEN2_PCIE help Say Y here if you want PCIe controller support on R-Car Gen2 SoCs. +config PCI_HOST_GENERIC_ACPI + bool "Generic ACPI PCI host controller" + depends on ARM64 && ACPI + help + Say Y here if you want to support a simple generic ACPI PCI host + controller. + config PCI_HOST_GENERIC bool "Generic PCI host controller" depends on (ARM || ARM64) && OF diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 9d4d3c6..bc31852 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o +obj-$(CONFIG_PCI_HOST_GENERIC_ACPI) += pci-host-acpi.o obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o diff --git a/drivers/pci/host/pci-host-acpi.c b/drivers/pci/host/pci-host-acpi.c new file mode 100644 index 0000000..3aade43 --- /dev/null +++ b/drivers/pci/host/pci-host-acpi.c @@ -0,0 +1,211 @@ +/* + * Generic PCI host controller driver for ACPI based systems + * + * 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. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Copyright (c) 2015 Broadcom Corporation + * + * Based on drivers/pci/host/pci-host-generic.c + * Copyright (C) 2014 ARM Limited + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "pci-host-acpi:" +#define MCFG_NAMELEN 32 +#define MCFG_SHIFT 20 + +/* ECFG window for this root bus */ +struct gen_mcfg_window { + struct resource res; + unsigned int domain_nr; + unsigned int bus_start; + unsigned int bus_end; + char name[MCFG_NAMELEN]; + void __iomem *win; +}; + +/* sysdata pointer is ->root_info */ +struct gen_acpi_pci { + struct acpi_pci_root_info root_info; + struct gen_mcfg_window cfg; +}; + +/* MCFG entries */ +struct mcfg_entry { + int segment; + int bus_start; + int bus_end; + u64 addr; +}; + +static struct mcfg_entries { + int size; + struct mcfg_entry *entries; +} mcfg_sav; + +/* find mapping of a MCFG area */ +static void __iomem *gen_acpi_map_cfg_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + struct gen_acpi_pci *pci = bus->sysdata; + struct gen_mcfg_window *cfg = &pci->cfg; + + if (bus->number < cfg->bus_start || bus->number > cfg->bus_end) + return NULL; + + return cfg->win + ((bus->number - cfg->bus_start) << MCFG_SHIFT) + + ((devfn << 12) | where); +} + +/* Map the ECFG area for a root bus */ +static int gen_acpi_pci_map_mcfg(struct acpi_pci_root *root, + struct gen_acpi_pci *pci) +{ + struct gen_mcfg_window *cfg = &pci->cfg; + struct acpi_device *device = root->device; + void __iomem *vaddr; + + /* if there is info from _CBA, use that, otherwise use MCFG table */ + if (root->mcfg_addr) { + cfg->bus_start = root->secondary.start; + cfg->bus_end = root->secondary.end; + cfg->res.start = root->mcfg_addr; + } else { + struct mcfg_entry *e = mcfg_sav.entries; + int i, n = mcfg_sav.size; + + for (i = 0; i < n; i++, e++) + if (e->segment == root->segment) + break; + if (i >= n) + return -ENODEV; + cfg->bus_start = e->bus_start; + cfg->bus_end = e->bus_end; + cfg->res.start = e->addr; + } + + cfg->res.flags = IORESOURCE_MEM; + cfg->res.name = cfg->name; + cfg->res.end = cfg->res.start + + ((cfg->bus_end - cfg->bus_start + 1) << MCFG_SHIFT) - 1; + snprintf(cfg->name, MCFG_NAMELEN, "PCI MMCONFIG %04x [bus %02x-%02x]", + root->segment, cfg->bus_start, cfg->bus_end); + + /* map ECFG space for the bus range */ + vaddr = devm_ioremap_resource(&device->dev, &cfg->res); + if (IS_ERR(vaddr)) + return PTR_ERR(vaddr); + + cfg->win = vaddr; + return 0; +} + +static struct pci_ops gen_acpi_pci_ops = { + .map_bus = gen_acpi_map_cfg_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +static struct acpi_pci_root_ops pci_acpi_root_ops = { + .pci_ops = &gen_acpi_pci_ops, +}; + +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +{ + struct acpi_device *device = root->device; + struct gen_acpi_pci *pci; + struct pci_bus *bus, *child; + int err; + + pci = devm_kzalloc(&device->dev, sizeof(*pci), GFP_KERNEL); + if (!pci) { + dev_err(&device->dev, + "pci_bus %04x:%02x: ignored (out of memory)\n", + root->segment, (int)root->secondary.start); + return NULL; + } + + err = gen_acpi_pci_map_mcfg(root, pci); + if (err) { + dev_err(&device->dev, "MCFG lookup for domain %d failed", + root->segment); + return NULL; + } + bus = acpi_pci_root_create(root, &pci_acpi_root_ops, + &pci->root_info, pci); + if (!bus) { + dev_err(&device->dev, "Scanning rootbus failed"); + return NULL; + } + + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + pci_bus_add_devices(bus); + return bus; +} + +/* save MCFG entries */ +static __init int handle_mcfg(struct acpi_table_header *header) +{ + struct acpi_table_mcfg *mcfg; + struct acpi_mcfg_allocation *mptr; + struct mcfg_entry *e; + 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; + } + mcfg_sav.entries = e = kcalloc(n, sizeof(*e), GFP_KERNEL); + if (e == NULL) + return -ENOMEM; + mcfg_sav.size = n; + for (i = 0; i < n; i++, mptr++) { + e->segment = mptr->pci_segment; + e->bus_start = mptr->start_bus_number; + e->bus_end = mptr->end_bus_number; + e->addr = mptr->address; + } + return 0; +} + +static __init int parse_save_mcfg(void) +{ + int err; + + err = acpi_sfi_table_parse(ACPI_SIG_MCFG, handle_mcfg); + if (err) { + pr_err(PREFIX " Failed to parse MCFG (%d)\n", err); + mcfg_sav.size = -1; + } else { + pr_info(PREFIX " MCFG table at %p, %d entries.\n", + mcfg_sav.entries, mcfg_sav.size); + } + return err; +} + +arch_initcall(parse_save_mcfg);