diff mbox series

[v10,8/9] LPC: Add the ACPI LPC support

Message ID 1509120687-7352-9-git-send-email-gabriele.paoloni@huawei.com
State Not Applicable
Headers show
Series LPC: legacy ISA I/O support | expand

Commit Message

Gabriele Paoloni Oct. 27, 2017, 4:11 p.m. UTC
From: "zhichang.yuan" <yuanzhichang@hisilicon.com>

Based on the provious patches, this patch supports the ACPI LPC host on
Hip06/Hip07.

Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Tested-by: dann frazier <dann.frazier@canonical.com>
---
 drivers/acpi/arm64/acpi_indirectio.c |   3 +
 drivers/acpi/arm64/acpi_indirectio.h |   4 +
 drivers/bus/hisi_lpc.c               | 164 +++++++++++++++++++++++++++++++----
 3 files changed, 154 insertions(+), 17 deletions(-)
diff mbox series

Patch

diff --git a/drivers/acpi/arm64/acpi_indirectio.c b/drivers/acpi/arm64/acpi_indirectio.c
index 013db6f..424f3bd 100644
--- a/drivers/acpi/arm64/acpi_indirectio.c
+++ b/drivers/acpi/arm64/acpi_indirectio.c
@@ -123,6 +123,9 @@  int acpi_set_logicio_resource(struct device *child,
 
 /* All the host devices which apply indirect-IO can be listed here. */
 static const struct acpi_device_id acpi_indirect_host_id[] = {
+#ifdef CONFIG_HISILICON_LPC
+	{"HISI0191", INDIRECT_IO_INFO(lpc_host_desc)},
+#endif
 	{""},
 };
 
diff --git a/drivers/acpi/arm64/acpi_indirectio.h b/drivers/acpi/arm64/acpi_indirectio.h
index 8102ea0..1b18590 100644
--- a/drivers/acpi/arm64/acpi_indirectio.h
+++ b/drivers/acpi/arm64/acpi_indirectio.h
@@ -18,6 +18,10 @@  struct indirectio_device_desc {
 	int (*pre_setup)(struct acpi_device *adev, void *pdata);
 };
 
+#ifdef CONFIG_HISILICON_LPC
+extern const struct indirectio_device_desc lpc_host_desc;
+#endif
+
 int acpi_set_logicio_resource(struct device *child,
 		struct device *hostdev, const struct resource **res,
 		int *num_res);
diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
index c885483..26043f7 100644
--- a/drivers/bus/hisi_lpc.c
+++ b/drivers/bus/hisi_lpc.c
@@ -438,7 +438,6 @@  static int hisilpc_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct resource *res;
 	struct hisilpc_dev *lpcdev;
-	struct logic_pio_hwaddr *range;
 	int ret = 0;
 
 	lpcdev = devm_kzalloc(dev, sizeof(struct hisilpc_dev), GFP_KERNEL);
@@ -460,21 +459,32 @@  static int hisilpc_probe(struct platform_device *pdev)
 	}
 
 	/* register the LPC host PIO resources */
-	range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
-	if (!range)
-		return -ENOMEM;
-	range->fwnode = dev->fwnode;
-	range->flags = PIO_INDIRECT;
-	range->size = LPC_BUS_IO_SIZE;
-	range->hw_start = LPC_MIN_BUS_RANGE;
-
-	ret = logic_pio_register_range(range);
-	if (ret) {
-		kfree(range);
-		dev_err(dev, "OF: register IO range FAIL!\n");
-		return -ret;
+	if (has_acpi_companion(dev)) {
+		lpcdev->io_host = find_io_range_by_fwnode(dev->fwnode);
+		if (!lpcdev->io_host) {
+			dev_err(dev, "HiSilicon LPC range not registered!\n");
+			return -EFAULT;
+		}
+	} else {
+		struct logic_pio_hwaddr *range;
+
+		range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
+		if (!range)
+			return -ENOMEM;
+		range->fwnode = dev->fwnode;
+		range->flags = PIO_INDIRECT;
+		range->size = LPC_BUS_IO_SIZE;
+		range->hw_start = LPC_MIN_BUS_RANGE;
+
+		ret = logic_pio_register_range(range);
+		if (ret) {
+			kfree(range);
+			dev_err(dev, "OF: register IO range FAIL!\n");
+			return -ret;
+		}
+		lpcdev->io_host = range;
 	}
-	lpcdev->io_host = range;
+
 	lpcdev->io_host->devpara = lpcdev;
 	lpcdev->io_host->ops = &hisi_lpc_ops;
 
@@ -486,8 +496,11 @@  static int hisilpc_probe(struct platform_device *pdev)
 	 * to avoid some children which complete the scanning trigger the
 	 * MMIO accesses which will probably cause panic.
 	 */
-	dev_info(dev, " calling of_platform_populate");
-	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+	if (!has_acpi_companion(dev)) {
+		dev_info(dev, " calling of_platform_populate");
+		ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+	}
+
 	if (ret) {
 		/*
 		 * When LPC probing is not completely successful, set 'devpara'
@@ -515,10 +528,127 @@  static const struct of_device_id hisilpc_of_match[] = {
 	{},
 };
 
+#ifdef CONFIG_ACPI
+#include "../acpi/arm64/acpi_indirectio.h"
+#include <linux/mfd/core.h>
+
+struct lpc_private_data {
+	resource_size_t io_size;
+	resource_size_t io_start;
+};
+
+static struct lpc_private_data lpc_data = {
+	.io_size = LPC_BUS_IO_SIZE,
+	.io_start = LPC_MIN_BUS_RANGE,
+};
+
+static struct mfd_cell_acpi_match hisi_lpc_ipmi_acpi_match = {
+	.pnpid = "IPI0001",
+};
+
+static const char *hisi_lpc_ipmi_name = "hisi_lpc_ipmi";
+
+static int lpc_host_io_setup(struct acpi_device *adev, void *pdata)
+{
+	int ret;
+	struct platform_device *pdev;
+	struct mfd_cell *ipmi_devs;
+	struct logic_pio_hwaddr *range;
+	struct lpc_private_data *lpc_private;
+	struct acpi_device *child;
+	int ipmi_dev_num = 0;
+
+	lpc_private = pdata;
+	range = kzalloc(sizeof(*range), GFP_KERNEL);
+	if (!range)
+		return -ENOMEM;
+	range->fwnode = &adev->fwnode;
+	range->flags = PIO_INDIRECT;
+	range->size = lpc_private->io_size;
+	range->hw_start = lpc_private->io_start;
+
+	ret = logic_pio_register_range(range);
+	if (ret)
+		goto free_range;
+
+	/* count the ipmi children first */
+	list_for_each_entry(child, &adev->children, node) {
+		if (!strcmp(hisi_lpc_ipmi_acpi_match.pnpid,
+			    acpi_device_hid(child)))
+			ipmi_dev_num++;
+	}
+
+	/* allocate the mfd cells */
+	ipmi_devs = kcalloc(ipmi_dev_num, sizeof(*ipmi_devs), GFP_KERNEL);
+	if (!ipmi_devs) {
+		dev_err(&adev->dev, "ipmi_devs kzalloc failed!\n");
+		ret = -ENOMEM;
+		goto free_range;
+	}
+
+	ipmi_dev_num = 0;
+	/* For hisilpc, only care about the sons of host. */
+	list_for_each_entry(child, &adev->children, node) {
+		if (!strcmp(hisi_lpc_ipmi_acpi_match.pnpid,
+			    acpi_device_hid(child))) {
+			ipmi_devs[ipmi_dev_num].name = hisi_lpc_ipmi_name;
+			ipmi_devs[ipmi_dev_num].acpi_match =
+					&hisi_lpc_ipmi_acpi_match;
+			ret = acpi_set_logicio_resource(&child->dev, &adev->dev,
+					&ipmi_devs[ipmi_dev_num].resources,
+					&ipmi_devs[ipmi_dev_num].num_resources);
+			if (ret) {
+				dev_err(&child->dev, "set resource failed..\n");
+				goto free_ipmi_devs;
+			}
+			ipmi_dev_num++;
+		}
+	}
+
+	pdev = acpi_create_platform_device(adev, NULL);
+	if (IS_ERR_OR_NULL(pdev)) {
+		dev_err(&adev->dev, "Create platform device for host FAIL!\n");
+		ret = -EFAULT;
+		goto free_ipmi_devs;
+	}
+	acpi_device_set_enumerated(adev);
+
+	ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
+			ipmi_devs, ipmi_dev_num, NULL, 0, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to add ipmi mfd cell\n");
+		goto free_ipmi_devs;
+	}
+
+	return ret;
+
+free_ipmi_devs:
+	while (ipmi_dev_num--)
+		kfree(ipmi_devs[ipmi_dev_num].resources);
+	kfree(ipmi_devs);
+free_range:
+	kfree(range);
+
+	return ret;
+}
+
+static const struct acpi_device_id hisilpc_acpi_match[] = {
+	{"HISI0191", },
+	{},
+};
+
+const struct indirectio_device_desc lpc_host_desc = {
+	.pdata = &lpc_data,
+	.pre_setup = lpc_host_io_setup,
+};
+
+#endif
+
 static struct platform_driver hisilpc_driver = {
 	.driver = {
 		.name           = "hisi_lpc",
 		.of_match_table = hisilpc_of_match,
+		.acpi_match_table = ACPI_PTR(hisilpc_acpi_match),
 	},
 	.probe = hisilpc_probe,
 };