[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <0a30452f-34eb-d0b5-2001-ab6b866c53e2@huawei.com>
Date: Thu, 1 Feb 2018 11:32:50 +0000
From: John Garry <john.garry@...wei.com>
To: <mika.westerberg@...ux.intel.com>, <rafael@...nel.org>,
<lorenzo.pieralisi@....com>, <rjw@...ysocki.net>
CC: <robh+dt@...nel.org>, <bhelgaas@...gle.com>, <arnd@...db.de>,
<mark.rutland@....com>, <olof@...om.net>, <hanjun.guo@...aro.org>,
<dann.frazier@...onical.com>, <benh@...nel.crashing.org>,
<linux-kernel@...r.kernel.org>, <linux-acpi@...r.kernel.org>,
<linuxarm@...wei.com>, <linux-pci@...r.kernel.org>,
<minyard@....org>, <devicetree@...r.kernel.org>,
<linux-arch@...r.kernel.org>, <rdunlap@...radead.org>
Subject: Re: [PATCH v12 7/9] ACPI: Translate the I/O range of non-MMIO devices
before scanning
On 23/01/2018 16:36, John Garry wrote:
> On some platforms(such as Hip06/Hip07), the legacy ISA/LPC devices access
> I/O with some special host-local I/O ports known on x86. As their I/O space
> are not memory mapped like PCI/PCIE MMIO host bridges, this patch is meant
> to support a new class of I/O host controllers where the local IO ports of
> the children devices are translated into the Indirect I/O address space.
>
> Through the handler attach callback, all the I/O translations are done
> before starting the enumeration on children devices and the translated
> addresses are replaced in the children resources.
>
Hi Lorenzo, Rafael, Mika,
I would really appreciate if you could have a look at this patch.
The current solution here is to scan the ACPI devices under these
indirectIO hosts and create MFDs for the children.
I think another solution - which you may prefer - is to avoid adding
this scan handler (and all this other scan code) and add a check like
acpi_is_serial_bus_slave() [which checks the device parent versus a list
of known indirectIO hosts] to not enumerate these children, and do it
from the LLDD host probe instead (https://lkml.org/lkml/2017/6/16/250)
John
> Signed-off-by: John Garry <john.garry@...wei.com>
> Signed-off-by: Zhichang Yuan <yuanzhichang@...ilicon.com>
> Signed-off-by: Gabriele Paoloni <gabriele.paoloni@...wei.com>
> ---
> drivers/acpi/arm64/Makefile | 1 +
> drivers/acpi/arm64/acpi_indirectio.c | 273 +++++++++++++++++++++++++++++++++++
> drivers/acpi/internal.h | 5 +
> drivers/acpi/scan.c | 1 +
> 4 files changed, 280 insertions(+)
> create mode 100644 drivers/acpi/arm64/acpi_indirectio.c
>
> diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
> index 1017def..f4a7f46 100644
> --- a/drivers/acpi/arm64/Makefile
> +++ b/drivers/acpi/arm64/Makefile
> @@ -1,2 +1,3 @@
> obj-$(CONFIG_ACPI_IORT) += iort.o
> obj-$(CONFIG_ACPI_GTDT) += gtdt.o
> +obj-$(CONFIG_INDIRECT_PIO) += acpi_indirectio.o
> diff --git a/drivers/acpi/arm64/acpi_indirectio.c b/drivers/acpi/arm64/acpi_indirectio.c
> new file mode 100644
> index 0000000..2649f57
> --- /dev/null
> +++ b/drivers/acpi/arm64/acpi_indirectio.c
> @@ -0,0 +1,273 @@
> +/*
> + * ACPI support for indirect-IO bus.
> + *
> + * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
> + * Author: Gabriele Paoloni <gabriele.paoloni@...wei.com>
> + * Author: Zhichang Yuan <yuanzhichang@...ilicon.com>
> + * Author: John Garry <john.garry@...wei.com>
> + *
> + * 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.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/logic_pio.h>
> +#include <linux/mfd/core.h>
> +#include <linux/platform_device.h>
> +
> +ACPI_MODULE_NAME("indirect IO");
> +
> +#define ACPI_INDIRECTIO_NAME_LENGTH 255
> +
> +#define INDIRECT_IO_INFO(desc) ((unsigned long)&desc)
> +
> +struct acpi_indirectio_mfd_cell {
> + struct mfd_cell_acpi_match acpi_match;
> + char name[ACPI_INDIRECTIO_NAME_LENGTH];
> + char pnpid[ACPI_INDIRECTIO_NAME_LENGTH];
> +};
> +
> +struct acpi_indirectio_host_data {
> + resource_size_t io_size;
> + resource_size_t io_start;
> +};
> +
> +struct acpi_indirectio_device_desc {
> + struct acpi_indirectio_host_data pdata; /* device relevant info data */
> + int (*pre_setup)(struct acpi_device *adev,
> + struct acpi_indirectio_host_data *pdata);
> +};
> +
> +static int acpi_translate_logicio_res(struct acpi_device *adev,
> + struct acpi_device *host, struct resource *resource)
> +{
> + unsigned long sys_port;
> + struct device *dev = &adev->dev;
> + resource_size_t length = resource->end - resource->start;
> +
> + sys_port = logic_pio_trans_hwaddr(&host->fwnode, resource->start,
> + length);
> +
> + if (sys_port == -1) {
> + dev_err(dev, "translate bus-addr(0x%llx) fail!\n",
> + resource->start);
> + return -EFAULT;
> + }
> +
> + resource->start = sys_port;
> + resource->end = sys_port + length;
> +
> + return 0;
> +}
> +
> +/*
> + * update/set the current I/O resource of the designated device node.
> + * after this calling, the enumeration can be started as the I/O resource
> + * had been translated to logicial I/O from bus-local I/O.
> + *
> + * @child: the device node to be updated the I/O resource;
> + * @hostdev: the device node where 'adev' is attached, which can be not
> + * the parent of 'adev';
> + * @res: double pointer to be set to the address of the updated resources
> + * @num_res: address of the variable to contain the number of updated resources
> + *
> + * return 0 when successful, negative is for failure.
> + */
> +int acpi_indirectio_set_logicio_res(struct device *child,
> + struct device *hostdev,
> + const struct resource **res,
> + int *num_res)
> +{
> + struct acpi_device *adev;
> + struct acpi_device *host;
> + struct resource_entry *rentry;
> + LIST_HEAD(resource_list);
> + struct resource *resources = NULL;
> + int count;
> + int i;
> + int ret = -EIO;
> +
> + if (!child || !hostdev)
> + return -EINVAL;
> +
> + host = to_acpi_device(hostdev);
> + adev = to_acpi_device(child);
> +
> + /* check the device state */
> + if (!adev->status.present) {
> + dev_info(child, "ACPI: device is not present!\n");
> + return 0;
> + }
> + /* whether the child had been enumerated? */
> + if (acpi_device_enumerated(adev)) {
> + dev_info(child, "ACPI: had been enumerated!\n");
> + return 0;
> + }
> +
> + count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
> + if (count <= 0) {
> + dev_err(&adev->dev, "failed to get ACPI resources\n");
> + return count ? count : -EIO;
> + }
> +
> + resources = kcalloc(count, sizeof(struct resource), GFP_KERNEL);
> + if (!resources) {
> + acpi_dev_free_resource_list(&resource_list);
> + return -ENOMEM;
> + }
> + count = 0;
> + list_for_each_entry(rentry, &resource_list, node)
> + resources[count++] = *rentry->res;
> +
> + acpi_dev_free_resource_list(&resource_list);
> +
> + /* translate the I/O resources */
> + for (i = 0; i < count; i++) {
> + if (resources[i].flags & IORESOURCE_IO) {
> + ret = acpi_translate_logicio_res(adev, host,
> + &resources[i]);
> + if (ret) {
> + kfree(resources);
> + dev_err(child,
> + "Translate I/O range failed (%d)!\n",
> + ret);
> + return ret;
> + }
> + }
> + }
> + *res = resources;
> + *num_res = count;
> +
> + return ret;
> +}
> +
> +int
> +acpi_indirectio_pre_setup(struct acpi_device *adev,
> + struct acpi_indirectio_host_data *pdata)
> +{
> + struct platform_device *pdev;
> + struct mfd_cell *mfd_cells;
> + struct logic_pio_hwaddr *range;
> + struct acpi_device *child;
> + struct acpi_indirectio_mfd_cell *acpi_indirectio_mfd_cells;
> + int size, ret, count = 0, cell_num = 0;
> +
> + range = kzalloc(sizeof(*range), GFP_KERNEL);
> + if (!range)
> + return -ENOMEM;
> + range->fwnode = &adev->fwnode;
> + range->flags = PIO_INDIRECT;
> + range->size = pdata->io_size;
> + range->hw_start = pdata->io_start;
> +
> + ret = logic_pio_register_range(range);
> + if (ret)
> + goto free_range;
> +
> + list_for_each_entry(child, &adev->children, node)
> + cell_num++;
> +
> + /* allocate the mfd cells */
> + size = sizeof(*mfd_cells) + sizeof(*acpi_indirectio_mfd_cells);
> + mfd_cells = kcalloc(cell_num, size, GFP_KERNEL);
> + if (!mfd_cells) {
> + ret = -ENOMEM;
> + goto free_range;
> + }
> +
> + acpi_indirectio_mfd_cells = (struct acpi_indirectio_mfd_cell *)
> + &mfd_cells[cell_num];
> + /* Only consider the children of the host */
> + list_for_each_entry(child, &adev->children, node) {
> + struct mfd_cell *mfd_cell = &mfd_cells[count];
> + struct acpi_indirectio_mfd_cell *acpi_indirectio_mfd_cell =
> + &acpi_indirectio_mfd_cells[count];
> + const struct mfd_cell_acpi_match *acpi_match =
> + &acpi_indirectio_mfd_cell->acpi_match;
> + char *name = &acpi_indirectio_mfd_cell[count].name[0];
> + char *pnpid = &acpi_indirectio_mfd_cell[count].pnpid[0];
> + struct mfd_cell_acpi_match match = {
> + .pnpid = pnpid,
> + };
> +
> + snprintf(name, ACPI_INDIRECTIO_NAME_LENGTH, "indirect-io-%s",
> + acpi_device_hid(child));
> + snprintf(pnpid, ACPI_INDIRECTIO_NAME_LENGTH, "%s",
> + acpi_device_hid(child));
> +
> + memcpy((void *)acpi_match, (void *)&match, sizeof(*acpi_match));
> + mfd_cell->name = name;
> + mfd_cell->acpi_match = acpi_match;
> +
> + ret =
> + acpi_indirectio_set_logicio_res(&child->dev,
> + &adev->dev,
> + &mfd_cell->resources,
> + &mfd_cell->num_resources);
> + if (ret) {
> + dev_err(&child->dev, "set resource failed (%d)\n", ret);
> + goto free_mfd_res;
> + }
> + count++;
> + }
> +
> + pdev = acpi_create_platform_device(adev, NULL);
> + if (IS_ERR_OR_NULL(pdev)) {
> + dev_err(&adev->dev, "Create platform device for host failed!\n");
> + ret = PTR_ERR(pdev);
> + goto free_mfd_res;
> + }
> + acpi_device_set_enumerated(adev);
> +
> + ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
> + mfd_cells, cell_num, NULL, 0, NULL);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to add mfd cells (%d)\n", ret);
> + goto free_mfd_res;
> + }
> +
> + return ret;
> +
> +free_mfd_res:
> + while (cell_num--)
> + kfree(mfd_cells[cell_num].resources);
> + kfree(mfd_cells);
> +free_range:
> + kfree(range);
> +
> + return ret;
> +}
> +
> +/* All the host devices which apply indirect-IO can be listed here. */
> +static const struct acpi_device_id acpi_indirect_host_id[] = {
> + {""},
> +};
> +
> +static int acpi_indirectio_attach(struct acpi_device *adev,
> + const struct acpi_device_id *id)
> +{
> + struct acpi_indirectio_device_desc *hostdata;
> + int ret;
> +
> + hostdata = (struct acpi_indirectio_device_desc *)id->driver_data;
> + if (!hostdata || !hostdata->pre_setup)
> + return -EINVAL;
> +
> + ret = hostdata->pre_setup(adev, &hostdata->pdata);
> +
> + if (ret < 0)
> + return ret;
> +
> + return 1;
> +}
> +
> +static struct acpi_scan_handler acpi_indirect_handler = {
> + .ids = acpi_indirect_host_id,
> + .attach = acpi_indirectio_attach,
> +};
> +
> +void __init acpi_indirectio_scan_init(void)
> +{
> + acpi_scan_add_handler(&acpi_indirect_handler);
> +}
> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
> index 1d0a501..d6b1a95 100644
> --- a/drivers/acpi/internal.h
> +++ b/drivers/acpi/internal.h
> @@ -31,6 +31,11 @@
> void acpi_platform_init(void);
> void acpi_pnp_init(void);
> void acpi_int340x_thermal_init(void);
> +#ifdef CONFIG_INDIRECT_PIO
> +void acpi_indirectio_scan_init(void);
> +#else
> +static inline void acpi_indirectio_scan_init(void) {}
> +#endif
> #ifdef CONFIG_ARM_AMBA
> void acpi_amba_init(void);
> #else
> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> index b0fe527..8ea6f69 100644
> --- a/drivers/acpi/scan.c
> +++ b/drivers/acpi/scan.c
> @@ -2141,6 +2141,7 @@ int __init acpi_scan_init(void)
> acpi_amba_init();
> acpi_watchdog_init();
> acpi_init_lpit();
> + acpi_indirectio_scan_init();
>
> acpi_scan_add_handler(&generic_device_handler);
>
>
Powered by blists - more mailing lists