Commit 8b26d801 authored by Olof Johansson's avatar Olof Johansson
Browse files

Merge tag 'hisi-drivers-for-4.18' of git://github.com/hisilicon/linux-hisi into next/drivers

ARM64: hisi: SoC driver updates for 4.18

- Update hisi LPC bus driver to use the platform driver APIs
  other than the MFD APIs to support connected device like UART

* tag 'hisi-drivers-for-4.18' of git://github.com/hisilicon/linux-hisi

:
  HISI LPC: Add ACPI UART support
  HISI LPC: Re-Add ACPI child enumeration support
  HISI LPC: Stop using MFD APIs

Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parents 7a0b8610 adf3457b
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@ config HISILICON_LPC
	bool "Support for ISA I/O space on HiSilicon Hip06/7"
	depends on ARM64 && (ARCH_HISI || COMPILE_TEST)
	select INDIRECT_PIO
	select MFD_CORE if ACPI
	help
	  Driver to enable I/O access to devices attached to the Low Pin
	  Count bus on the HiSilicon Hip06/7 SoC.
+97 −62
Original line number Diff line number Diff line
@@ -11,12 +11,12 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/logic_pio.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/serial_8250.h>
#include <linux/slab.h>

#define DRV_NAME "hisi-lpc"
@@ -341,15 +341,6 @@ static const struct logic_pio_host_ops hisi_lpc_ops = {
};

#ifdef CONFIG_ACPI
#define MFD_CHILD_NAME_PREFIX DRV_NAME"-"
#define MFD_CHILD_NAME_LEN (ACPI_ID_LEN + sizeof(MFD_CHILD_NAME_PREFIX) - 1)

struct hisi_lpc_mfd_cell {
	struct mfd_cell_acpi_match acpi_match;
	char name[MFD_CHILD_NAME_LEN];
	char pnpid[ACPI_ID_LEN];
};

static int hisi_lpc_acpi_xlat_io_res(struct acpi_device *adev,
				     struct acpi_device *host,
				     struct resource *res)
@@ -368,7 +359,7 @@ static int hisi_lpc_acpi_xlat_io_res(struct acpi_device *adev,
}

/*
 * hisi_lpc_acpi_set_io_res - set the resources for a child's MFD
 * hisi_lpc_acpi_set_io_res - set the resources for a child
 * @child: the device node to be updated the I/O resource
 * @hostdev: the device node associated with host controller
 * @res: double pointer to be set to the address of translated resources
@@ -452,78 +443,122 @@ static int hisi_lpc_acpi_set_io_res(struct device *child,
	return 0;
}

static int hisi_lpc_acpi_remove_subdev(struct device *dev, void *unused)
{
	platform_device_unregister(to_platform_device(dev));
	return 0;
}

struct hisi_lpc_acpi_cell {
	const char *hid;
	const char *name;
	void *pdata;
	size_t pdata_size;
};

/*
 * hisi_lpc_acpi_probe - probe children for ACPI FW
 * @hostdev: LPC host device pointer
 *
 * Returns 0 when successful, and a negative value for failure.
 *
 * Scan all child devices and create a per-device MFD with
 * logical PIO translated IO resources.
 * Create a platform device per child, fixing up the resources
 * from bus addresses to Logical PIO addresses.
 *
 */
static int hisi_lpc_acpi_probe(struct device *hostdev)
{
	struct acpi_device *adev = ACPI_COMPANION(hostdev);
	struct hisi_lpc_mfd_cell *hisi_lpc_mfd_cells;
	struct mfd_cell *mfd_cells;
	struct acpi_device *child;
	int size, ret, count = 0, cell_num = 0;

	list_for_each_entry(child, &adev->children, node)
		cell_num++;

	/* allocate the mfd cell and companion ACPI info, one per child */
	size = sizeof(*mfd_cells) + sizeof(*hisi_lpc_mfd_cells);
	mfd_cells = devm_kcalloc(hostdev, cell_num, size, GFP_KERNEL);
	if (!mfd_cells)
		return -ENOMEM;
	int ret;

	hisi_lpc_mfd_cells = (struct hisi_lpc_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 hisi_lpc_mfd_cell *hisi_lpc_mfd_cell =
					&hisi_lpc_mfd_cells[count];
		struct mfd_cell_acpi_match *acpi_match =
					&hisi_lpc_mfd_cell->acpi_match;
		char *name = hisi_lpc_mfd_cell[count].name;
		char *pnpid = hisi_lpc_mfd_cell[count].pnpid;
		struct mfd_cell_acpi_match match = {
			.pnpid = pnpid,
		};
		const char *hid = acpi_device_hid(child);
		const struct hisi_lpc_acpi_cell *cell;
		struct platform_device *pdev;
		const struct resource *res;
		bool found = false;
		int num_res;

		ret = hisi_lpc_acpi_set_io_res(&child->dev, &adev->dev, &res,
					       &num_res);
		if (ret) {
			dev_warn(hostdev, "set resource fail (%d)\n", ret);
			goto fail;
		}

		/*
		 * For any instances of this host controller (Hip06 and Hip07
		 * are the only chipsets), we would not have multiple slaves
		 * with the same HID. And in any system we would have just one
		 * controller active. So don't worrry about MFD name clashes.
		 */
		snprintf(name, MFD_CHILD_NAME_LEN, MFD_CHILD_NAME_PREFIX"%s",
			 acpi_device_hid(child));
		snprintf(pnpid, ACPI_ID_LEN, "%s", acpi_device_hid(child));
		cell = (struct hisi_lpc_acpi_cell []){
			/* ipmi */
			{
				.hid = "IPI0001",
				.name = "hisi-lpc-ipmi",
			},
			/* 8250-compatible uart */
			{
				.hid = "HISI1031",
				.name = "serial8250",
				.pdata = (struct plat_serial8250_port []) {
					{
						.iobase = res->start,
						.uartclk = 1843200,
						.iotype = UPIO_PORT,
						.flags = UPF_BOOT_AUTOCONF,
					},
					{}
				},
				.pdata_size = 2 *
					sizeof(struct plat_serial8250_port),
			},
			{}
		};

		memcpy(acpi_match, &match, sizeof(*acpi_match));
		mfd_cell->name = name;
		mfd_cell->acpi_match = acpi_match;
		for (; cell && cell->name; cell++) {
			if (!strcmp(cell->hid, hid)) {
				found = true;
				break;
			}
		}

		ret = hisi_lpc_acpi_set_io_res(&child->dev, &adev->dev,
					       &mfd_cell->resources,
					       &mfd_cell->num_resources);
		if (ret) {
			dev_warn(&child->dev, "set resource fail (%d)\n", ret);
			return ret;
		if (!found) {
			dev_warn(hostdev,
				 "could not find cell for child device (%s)\n",
				 hid);
			ret = -ENODEV;
			goto fail;
		}
		count++;

		pdev = platform_device_alloc(cell->name, PLATFORM_DEVID_AUTO);
		if (!pdev) {
			ret = -ENOMEM;
			goto fail;
		}

	ret = mfd_add_devices(hostdev, PLATFORM_DEVID_NONE,
			      mfd_cells, cell_num, NULL, 0, NULL);
	if (ret) {
		dev_err(hostdev, "failed to add mfd cells (%d)\n", ret);
		return ret;
		pdev->dev.parent = hostdev;
		ACPI_COMPANION_SET(&pdev->dev, child);

		ret = platform_device_add_resources(pdev, res, num_res);
		if (ret)
			goto fail;

		ret = platform_device_add_data(pdev, cell->pdata,
					       cell->pdata_size);
		if (ret)
			goto fail;

		ret = platform_device_add(pdev);
		if (ret)
			goto fail;

		acpi_device_set_enumerated(child);
	}

	return 0;

fail:
	device_for_each_child(hostdev, NULL,
			      hisi_lpc_acpi_remove_subdev);
	return ret;
}

static const struct acpi_device_id hisi_lpc_acpi_match[] = {