[PATCH v16 8/9] HISI LPC: Add ACPI support

2018-03-06 Thread John Garry
Based on the previous patches, this patch supports the
LPC host on hip06/hip07 for ACPI FW.

It is the responsibility of the LPC host driver to
enumerate the child devices, as the ACPI scan code will
not enumerate children of "indirect IO" hosts.

The ACPI table for the LPC host controller and the child
devices is in the following format:
  Device (LPC0) {
Name (_HID, "HISI0191")  // HiSi LPC
Name (_CRS, ResourceTemplate () {
  Memory32Fixed (ReadWrite, 0xa01b, 0x1000)
})
  }

  Device (LPC0.IPMI) {
Name (_HID, "IPI0001")
Name (LORS, ResourceTemplate() {
  QWordIO (
ResourceConsumer,
MinNotFixed, // _MIF
MaxNotFixed, // _MAF
PosDecode,
EntireRange,
0x0, // _GRA
0xe4,// _MIN
0x3fff,  // _MAX
0x0, // _TRA
0x04,// _LEN
, ,
BTIO
  )
})

Since the IO resources of the child devices need to be
translated from LPC bus addresses to logical PIO addresses,
and we shouldn't modify the resources of the devices
generated in the FW scan, a per-child MFD is created as
a substitute. The MFD IO resources will be the translated
bus addresses of the ACPI child.

Signed-off-by: John Garry 
Signed-off-by: Zhichang Yuan 
Signed-off-by: Gabriele Paoloni 
Reviewed-by: Andy Shevchenko 
Tested-by: dann frazier 
---
 drivers/bus/hisi_lpc.c | 202 +
 1 file changed, 202 insertions(+)

diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
index c331609..9d38cfa 100644
--- a/drivers/bus/hisi_lpc.c
+++ b/drivers/bus/hisi_lpc.c
@@ -12,6 +12,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -346,6 +347,204 @@ static void hisi_lpc_comm_out(void *hostdata, unsigned 
long pio,
.outs = hisi_lpc_comm_outs,
 };
 
+#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)
+{
+   unsigned long sys_port;
+   resource_size_t len = resource_size(res);
+
+   sys_port = logic_pio_trans_hwaddr(>fwnode, res->start, len);
+   if (sys_port == ~0UL)
+   return -EFAULT;
+
+   res->start = sys_port;
+   res->end = sys_port + len;
+
+   return 0;
+}
+
+/*
+ * hisi_lpc_acpi_set_io_res - set the resources for a child's MFD
+ * @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
+ * @num_res: pointer to variable to hold the number of translated resources
+ *
+ * Returns 0 when successful, and a negative value for failure.
+ *
+ * For a given host controller, each child device will have an associated
+ * host-relative address resource. This function will return the translated
+ * logical PIO addresses for each child devices resources.
+ */
+static int hisi_lpc_acpi_set_io_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;
+   int count;
+   int i;
+
+   if (!child || !hostdev)
+   return -EINVAL;
+
+   host = to_acpi_device(hostdev);
+   adev = to_acpi_device(child);
+
+   /* check the device state */
+   if (!adev->status.present) {
+   dev_dbg(child, "device is not present\n");
+   return -EIO;
+   }
+   /* whether the child had been enumerated? */
+   if (acpi_device_enumerated(adev)) {
+   dev_dbg(child, "has been enumerated\n");
+   return -EIO;
+   }
+
+   /*
+* The following code segment to retrieve the resources is common to
+* acpi_create_platform_device(), so consider a common helper function
+* in future.
+*/
+   count = acpi_dev_get_resources(adev, _list, NULL, NULL);
+   if (count <= 0) {
+   dev_dbg(child, "failed to get resources\n");
+   return count ? count : -EIO;
+   }
+
+   resources = devm_kcalloc(hostdev, count, sizeof(*resources),
+GFP_KERNEL);
+   if (!resources) {
+   

[PATCH v16 8/9] HISI LPC: Add ACPI support

2018-03-06 Thread John Garry
Based on the previous patches, this patch supports the
LPC host on hip06/hip07 for ACPI FW.

It is the responsibility of the LPC host driver to
enumerate the child devices, as the ACPI scan code will
not enumerate children of "indirect IO" hosts.

The ACPI table for the LPC host controller and the child
devices is in the following format:
  Device (LPC0) {
Name (_HID, "HISI0191")  // HiSi LPC
Name (_CRS, ResourceTemplate () {
  Memory32Fixed (ReadWrite, 0xa01b, 0x1000)
})
  }

  Device (LPC0.IPMI) {
Name (_HID, "IPI0001")
Name (LORS, ResourceTemplate() {
  QWordIO (
ResourceConsumer,
MinNotFixed, // _MIF
MaxNotFixed, // _MAF
PosDecode,
EntireRange,
0x0, // _GRA
0xe4,// _MIN
0x3fff,  // _MAX
0x0, // _TRA
0x04,// _LEN
, ,
BTIO
  )
})

Since the IO resources of the child devices need to be
translated from LPC bus addresses to logical PIO addresses,
and we shouldn't modify the resources of the devices
generated in the FW scan, a per-child MFD is created as
a substitute. The MFD IO resources will be the translated
bus addresses of the ACPI child.

Signed-off-by: John Garry 
Signed-off-by: Zhichang Yuan 
Signed-off-by: Gabriele Paoloni 
Reviewed-by: Andy Shevchenko 
Tested-by: dann frazier 
---
 drivers/bus/hisi_lpc.c | 202 +
 1 file changed, 202 insertions(+)

diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
index c331609..9d38cfa 100644
--- a/drivers/bus/hisi_lpc.c
+++ b/drivers/bus/hisi_lpc.c
@@ -12,6 +12,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -346,6 +347,204 @@ static void hisi_lpc_comm_out(void *hostdata, unsigned 
long pio,
.outs = hisi_lpc_comm_outs,
 };
 
+#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)
+{
+   unsigned long sys_port;
+   resource_size_t len = resource_size(res);
+
+   sys_port = logic_pio_trans_hwaddr(>fwnode, res->start, len);
+   if (sys_port == ~0UL)
+   return -EFAULT;
+
+   res->start = sys_port;
+   res->end = sys_port + len;
+
+   return 0;
+}
+
+/*
+ * hisi_lpc_acpi_set_io_res - set the resources for a child's MFD
+ * @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
+ * @num_res: pointer to variable to hold the number of translated resources
+ *
+ * Returns 0 when successful, and a negative value for failure.
+ *
+ * For a given host controller, each child device will have an associated
+ * host-relative address resource. This function will return the translated
+ * logical PIO addresses for each child devices resources.
+ */
+static int hisi_lpc_acpi_set_io_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;
+   int count;
+   int i;
+
+   if (!child || !hostdev)
+   return -EINVAL;
+
+   host = to_acpi_device(hostdev);
+   adev = to_acpi_device(child);
+
+   /* check the device state */
+   if (!adev->status.present) {
+   dev_dbg(child, "device is not present\n");
+   return -EIO;
+   }
+   /* whether the child had been enumerated? */
+   if (acpi_device_enumerated(adev)) {
+   dev_dbg(child, "has been enumerated\n");
+   return -EIO;
+   }
+
+   /*
+* The following code segment to retrieve the resources is common to
+* acpi_create_platform_device(), so consider a common helper function
+* in future.
+*/
+   count = acpi_dev_get_resources(adev, _list, NULL, NULL);
+   if (count <= 0) {
+   dev_dbg(child, "failed to get resources\n");
+   return count ? count : -EIO;
+   }
+
+   resources = devm_kcalloc(hostdev, count, sizeof(*resources),
+GFP_KERNEL);
+   if (!resources) {
+   dev_warn(hostdev, "could not allocate memory for %d 
resources\n",
+count);
+