[PATCH v7 5/5] PCI: ACPI: Add a generic ACPI based host controller
Bjorn Helgaas
helgaas at kernel.org
Thu Feb 4 16:19:07 PST 2016
Hi Jayachandran,
On Fri, Jan 29, 2016 at 02:35:40PM +0530, Jayachandran C wrote:
> Add a simple ACPI based PCI host controller under config option
> ACPI_PCI_HOST_GENERIC. This is done by providing an implementation
> of pci_acpi_scan_root().
>
> The pci_mmcfg_list handling is done by the ACPI code, so we keep a
> reference to the pci_mmcfg_region in sysdata. The ECAM region will
> be already mapped, so map_bus can be implemented by using the
> virt pointer for the pci_mmcfg_region. pci_generic_config_read
> and pci_generic_config_write are used for config space read/write.
>
> Also, we provide implementations of raw_pci_read and raw_pci_write
> hat are needed by ACPI based on the pci_mmcfg_list.
>
> pci_acpi_set_companion() and acpi_pci_get_segment() are defined
> using sysdata of generic ACPI host controller so that PCI domain
> and ACPI companion are set in PCI code rather than platform code.
>
> This code is currently enabled only for ARM64.
>
> Signed-off-by: Jayachandran C <jchandra at broadcom.com>
> ---
> drivers/acpi/Kconfig | 8 ++
> drivers/acpi/Makefile | 1 +
> drivers/acpi/pci_host_acpi.c | 186 +++++++++++++++++++++++++++++++++++++++++++
> include/linux/pci-acpi.h | 17 ++++
> 4 files changed, 212 insertions(+)
> create mode 100644 drivers/acpi/pci_host_acpi.c
I'm speaking a little bit out of turn here, because this is ACPI code,
but I'm confused about pci_host_acpi.c. We already have pci_root.c,
which is *supposed* to be arch-independent. I know pci_root.c is
crufty and could be improved, but it does work today on x86 and ia64,
and it handles some generic things that pci_host_acpi.c does not,
e.g., _OSC, NUMA, host bridge hotplug, etc.
I'd really like to see pci_root.c improved so it could work on x86,
ia64, and arm64. I'm sure that was probably the first thing you
tried, so likely there are issues there. Are they insurmountable?
Bjorn
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 82b96ee..65fb483 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -294,6 +294,14 @@ config ACPI_NUMA
> depends on (X86 || IA64)
> default y if IA64_GENERIC || IA64_SGI_SN2
>
> +config ACPI_PCI_HOST_GENERIC
> + 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 ACPI_CUSTOM_DSDT_FILE
> string "Custom DSDT Table file to include"
> default ""
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 74976f1..346101c 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -41,6 +41,7 @@ acpi-y += ec.o
> acpi-$(CONFIG_ACPI_DOCK) += dock.o
> acpi-y += pci_root.o pci_link.o pci_irq.o
> acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o
> +acpi-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_host_acpi.o
> acpi-y += acpi_lpss.o acpi_apd.o
> acpi-y += acpi_platform.o
> acpi-y += acpi_pnp.o
> diff --git a/drivers/acpi/pci_host_acpi.c b/drivers/acpi/pci_host_acpi.c
> new file mode 100644
> index 0000000..9dbdd81
> --- /dev/null
> +++ b/drivers/acpi/pci_host_acpi.c
> @@ -0,0 +1,186 @@
> +/*
> + * 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 <http://www.gnu.org/licenses/>.
> + *
> + * Copyright (c) 2015 Broadcom Corporation
> + *
> + * Based on drivers/pci/host/pci-host-generic.c
> + * Copyright (C) 2014 ARM Limited
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/acpi.h>
> +#include <linux/pci-acpi.h>
> +#include <linux/sfi_acpi.h>
> +#include <linux/slab.h>
> +
> +#define PREFIX "pci-host-acpi:"
> +
> +/* sysdata pointer is ->root_info */
> +struct gen_acpi_root_info {
> + struct acpi_pci_root_info common;
> + struct pci_mmcfg_region *mcfg;
> + bool mcfg_added;
> +};
> +
> +/* 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_root_info *pci = bus->sysdata;
> + struct pci_mmcfg_region *mcfg = pci->mcfg;
> +
> + if (bus->number < mcfg->start_bus || bus->number > mcfg->end_bus)
> + return NULL;
> +
> + return mcfg->virt +
> + PCI_MMCFG_OFFSET(bus->number - mcfg->start_bus, devfn) +
> + where;
> +}
> +
> +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,
> +};
> +
> +/* Insert the ECFG area for a root bus */
> +static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci)
> +{
> + struct gen_acpi_root_info *info;
> + struct acpi_pci_root *root = ci->root;
> + struct device *dev = &ci->bridge->dev;
> + int err;
> +
> + info = container_of(ci, struct gen_acpi_root_info, common);
> + err = pci_mmconfig_insert(dev, root->segment, root->secondary.start,
> + root->secondary.end, root->mcfg_addr);
> + if (err && err != -EEXIST)
> + return err;
> +
> + info->mcfg = pci_mmconfig_lookup(root->segment, root->secondary.start);
> + WARN_ON(info->mcfg == NULL);
> + info->mcfg_added = (err == -EEXIST);
> + return 0;
> +}
> +
> +static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci)
> +{
> + struct gen_acpi_root_info *info;
> + struct acpi_pci_root *root = ci->root;
> +
> + info = container_of(ci, struct gen_acpi_root_info, common);
> + if (info->mcfg_added)
> + pci_mmconfig_delete(root->segment, root->secondary.start,
> + root->secondary.end);
> + info->mcfg = NULL;
> +}
> +
> +static struct acpi_pci_root_ops pci_acpi_root_ops = {
> + .pci_ops = &gen_acpi_pci_ops,
> + .init_info = pci_acpi_root_init_info,
> + .release_info = pci_acpi_root_release_info,
> +};
> +
> +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
> +{
> + struct acpi_device *device = root->device;
> + struct gen_acpi_root_info *ri;
> + struct pci_bus *bus, *child;
> +
> + /* allocate acpi_info/sysdata */
> + ri = devm_kzalloc(&device->dev, sizeof(*ri), GFP_KERNEL);
> + if (!ri) {
> + dev_err(&device->dev,
> + "pci_bus %04x:%02x: ignored (out of memory)\n",
> + root->segment, (int)root->secondary.start);
> + return NULL;
> + }
> +
> + bus = acpi_pci_root_create(root, &pci_acpi_root_ops,
> + &ri->common, ri);
> + if (!bus) {
> + dev_err(&device->dev, "Scanning rootbus failed");
> + 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;
> +}
> +
> +int raw_pci_read(unsigned int seg, unsigned int bus,
> + unsigned int devfn, int reg, int len, u32 *val)
> +{
> + struct pci_mmcfg_region *mcfg;
> + void __iomem *addr;
> + int err = -EINVAL;
> +
> + rcu_read_lock();
> + mcfg = pci_mmconfig_lookup(seg, bus);
> + if (!mcfg || !mcfg->virt)
> + goto err;
> +
> + addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn);
> + switch (len) {
> + case 1:
> + *val = readb(addr + reg);
> + break;
> + case 2:
> + *val = readw(addr + reg);
> + break;
> + case 4:
> + *val = readl(addr + reg);
> + break;
> + }
> + err = 0;
> +err:
> + rcu_read_unlock();
> + return err;
> +}
> +
> +int raw_pci_write(unsigned int seg, unsigned int bus,
> + unsigned int devfn, int reg, int len, u32 val)
> +{
> + struct pci_mmcfg_region *mcfg;
> + void __iomem *addr;
> + int err = -EINVAL;
> +
> + rcu_read_lock();
> + mcfg = pci_mmconfig_lookup(seg, bus);
> + if (!mcfg || !mcfg->virt)
> + goto err;
> +
> + addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn);
> + switch (len) {
> + case 1:
> + writeb(val, addr + reg);
> + break;
> + case 2:
> + writew(val, addr + reg);
> + break;
> + case 4:
> + writel(val, addr + reg);
> + break;
> + }
> + err = 0;
> +err:
> + rcu_read_unlock();
> + return err;
> +}
> diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
> index d410885..f8d62e3 100644
> --- a/include/linux/pci-acpi.h
> +++ b/include/linux/pci-acpi.h
> @@ -143,6 +143,22 @@ static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
> static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }
> #endif /* CONFIG_ACPI */
>
> +#if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_PCI_HOST_GENERIC)
> +static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge)
> +{
> + struct pci_bus *b = bridge->bus;
> + struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)b->sysdata;
> +
> + ACPI_COMPANION_SET(&bridge->dev, ci->bridge);
> +}
> +
> +static inline u16 acpi_pci_get_segment(void *sysdata)
> +{
> + struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)sysdata;
> +
> + return ci->root->segment;
> +}
> +#else
> static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge)
> {
> /* leave it to the platform for now */
> @@ -152,6 +168,7 @@ static inline u16 acpi_pci_get_segment(void *sysdata)
> {
> return 0;
> }
> +#endif
>
> #ifdef CONFIG_ACPI_APEI
> extern bool aer_acpi_firmware_first(void);
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
More information about the linux-arm-kernel
mailing list