lkml.org 
[lkml]   [2016]   [Mar]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC PATCH 3/4] ACPI: PCI: Add generic PCI host controller
    Date
    Add a generic ACPI based PCI host controller, and provide config
    option ACPI_PCI_HOST_GENERIC to enable it.

    The implementation selects PCI_MMCONFIG and implements function
    pci_mmcfg_late_init() to parse MCFG table and save its entries.
    PCI_GENERIC_ECAM is also selected and pci/ecam.c functions are
    used to map the config space of the PCI controller.

    acpi_pci_root_ops is setup so that init_info looks up the saved
    MCFG entries and sets up the config space mapping. release_info
    deletes the mapping and frees the memory. Generic PCI functions
    are used for accessing config space.

    Signed-off-by: Jayachandran C <jchandra@broadcom.com>
    ---
    drivers/acpi/Kconfig | 9 ++
    drivers/acpi/Makefile | 1 +
    drivers/acpi/pci_gen_host.c | 247 ++++++++++++++++++++++++++++++++++++++++++++
    3 files changed, 257 insertions(+)
    create mode 100644 drivers/acpi/pci_gen_host.c

    diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
    index 82b96ee..f178f2e 100644
    --- a/drivers/acpi/Kconfig
    +++ b/drivers/acpi/Kconfig
    @@ -343,6 +343,15 @@ 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 "Generic ACPI based PCI controller"
    + depends on ARM64
    + select PCI_MMCONFIG
    + select PCI_GENERIC_ECAM
    + help
    + Say Y if you want the generic ACPI based PCI controller
    + implementation.
    +
    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 cb648a4..ceab5b0 100644
    --- a/drivers/acpi/Makefile
    +++ b/drivers/acpi/Makefile
    @@ -65,6 +65,7 @@ obj-$(CONFIG_ACPI_BUTTON) += button.o
    obj-$(CONFIG_ACPI_FAN) += fan.o
    obj-$(CONFIG_ACPI_VIDEO) += video.o
    obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
    +obj-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_gen_host.o
    obj-$(CONFIG_ACPI_PROCESSOR) += processor.o
    obj-$(CONFIG_ACPI) += container.o
    obj-$(CONFIG_ACPI_THERMAL) += thermal.o
    diff --git a/drivers/acpi/pci_gen_host.c b/drivers/acpi/pci_gen_host.c
    new file mode 100644
    index 0000000..a43f2cee
    --- /dev/null
    +++ b/drivers/acpi/pci_gen_host.c
    @@ -0,0 +1,247 @@
    +/*
    + * Copyright 2016 Broadcom
    + *
    + * 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 <linux/kernel.h>
    +#include <linux/pci.h>
    +#include <linux/pci-acpi.h>
    +#include <linux/sfi_acpi.h>
    +#include <linux/slab.h>
    +
    +#define PREFIX "ACPI: "
    +
    +/*
    + * Structure to hold entries from the MCFG table, these need to be
    + * mapped until a pci host bridge claims them for raw_pci_read/write
    + * to work
    + */
    +struct mcfg_entry {
    + phys_addr_t addr;
    + u16 segment;
    + u8 bus_start;
    + u8 bus_end;
    +};
    +
    +/*
    + * Global to save mcfg entries
    + */
    +static struct {
    + struct mcfg_entry *cfg;
    + int size;
    +} mcfgsav;
    +
    +/* List of all ACPI PCI roots, needed for raw operations */
    +static struct list_head gen_acpi_pci_roots;
    +
    +/* lock for global table AND list above */
    +static struct mutex gen_acpi_pci_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 */
    + struct list_head node; /* node in acpi_pci_roots */
    +};
    +
    +/* Call generic map_bus after getting cfg pointer */
    +static void __iomem *gen_acpi_map_cfg_bus(struct pci_bus *bus,
    + unsigned int devfn, int where)
    +{
    + struct acpi_pci_generic_root_info *ri = bus->sysdata;
    +
    + return pci_generic_map_bus(ri->cfg, bus->number, devfn, where);
    +}
    +
    +static struct pci_ops acpi_pci_ops = {
    + .map_bus = gen_acpi_map_cfg_bus,
    + .read = pci_generic_config_read,
    + .write = pci_generic_config_write,
    +};
    +
    +/* find the entry in mcfgsav.cfg which contains range bus_start..bus_end */
    +static int mcfg_lookup(u16 seg, u8 bus_start, u8 bus_end)
    +{
    + struct mcfg_entry *e;
    + int i;
    +
    + for (i = 0, e = mcfgsav.cfg; i < mcfgsav.size; i++, e++) {
    + if (seg != e->segment)
    + continue;
    + if (bus_start >= e->bus_start && bus_start <= e->bus_end)
    + return (bus_end <= e->bus_end) ? i : -EINVAL;
    + else if (bus_end >= e->bus_start && bus_end <= e->bus_end)
    + return -EINVAL;
    + }
    + return -ENOENT;
    +}
    +
    +/*
    + * init_info - lookup the bus range for the domain in MCFG, and set up
    + * config space mapping.
    + */
    +static int pci_acpi_generic_init_info(struct acpi_pci_root_info *ci)
    +{
    + struct acpi_pci_generic_root_info *ri;
    + struct acpi_pci_root *root = ci->root;
    + u16 seg = root->segment;
    + u8 bus_start = root->secondary.start;
    + u8 bus_end = root->secondary.end;
    + phys_addr_t addr = root->mcfg_addr;
    + struct mcfg_entry *e;
    + int ret;
    +
    + ri = container_of(ci, struct acpi_pci_generic_root_info, common);
    +
    + mutex_lock(&gen_acpi_pci_lock);
    + ret = mcfg_lookup(seg, bus_start, bus_end);
    + switch (ret) {
    + case -ENOENT:
    + if (addr != 0) /* use address from _CBA */
    + break;
    + pr_err("%04x:%02x-%02x mcfg lookup failed\n", seg,
    + bus_start, bus_end);
    + goto err_out;
    + case -EINVAL:
    + pr_err("%04x:%02x-%02x bus range error\n", seg, bus_start,
    + bus_end);
    + goto err_out;
    + default:
    + e = &mcfgsav.cfg[ret];
    + if (addr == 0)
    + addr = e->addr;
    + if (bus_start != e->bus_start) {
    + pr_err("%04x:%02x-%02x bus range mismatch %02x\n",
    + seg, bus_start, bus_end, e->bus_start);
    + goto err_out;
    + }
    + if (addr != e->addr) {
    + pr_warn("%04x:%02x-%02x addr mismatch, ignoring MCFG\n",
    + seg, bus_start, bus_end);
    + } 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);
    + }
    + break;
    + }
    +
    + ri->cfg = pci_generic_map_config(addr, bus_start, bus_end, 20, 12);
    + if (IS_ERR(ri->cfg)) {
    + ret = PTR_ERR(ri->cfg);
    + pr_err("%04x:%02x-%02x error %d mapping CFG\n", seg, bus_start,
    + bus_end, ret);
    + goto err_out;
    + }
    + list_add_tail(&ri->node, &gen_acpi_pci_roots);
    +err_out:
    + mutex_unlock(&gen_acpi_pci_lock);
    + return ret;
    +}
    +
    +/* 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);
    +
    + mutex_lock(&gen_acpi_pci_lock);
    + list_del(&ri->node);
    + pci_generic_unmap_config(ri->cfg);
    + mutex_unlock(&gen_acpi_pci_lock);
    +
    + kfree(ri);
    +}
    +
    +static struct acpi_pci_root_ops acpi_pci_root_ops = {
    + .pci_ops = &acpi_pci_ops,
    + .init_info = pci_acpi_generic_init_info,
    + .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;
    +
    + ri = kzalloc_node(sizeof(*ri), GFP_KERNEL, node);
    + if (!ri)
    + return NULL;
    +
    + bus = acpi_pci_root_create(root, &acpi_pci_root_ops, &ri->common, ri);
    + 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 handle_mcfg(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;
    + }
    +
    + mcfgsav.cfg = arr;
    + mcfgsav.size = n;
    + return 0;
    +}
    +
    +/* Interface called by ACPI - parse and save MCFG table */
    +void __init pci_mmcfg_late_init(void)
    +{
    + int err;
    +
    + mutex_init(&gen_acpi_pci_lock);
    + INIT_LIST_HEAD(&gen_acpi_pci_roots);
    + err = acpi_sfi_table_parse(ACPI_SIG_MCFG, handle_mcfg);
    + if (err) {
    + pr_err(PREFIX " Failed to parse MCFG (%d)\n", err);
    + mcfgsav.size = 0;
    + } else {
    + pr_info(PREFIX " MCFG table at %p, %d entries.\n",
    + mcfgsav.cfg, mcfgsav.size);
    + }
    +}
    --
    1.9.1
    \
     
     \ /
      Last update: 2016-03-17 22:01    [W:3.870 / U:0.028 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site