mfd: Add ACPI support
authorMika Westerberg <mika.westerberg@linux.intel.com>
Tue, 16 Sep 2014 11:52:36 +0000 (14:52 +0300)
committerLee Jones <lee.jones@linaro.org>
Fri, 26 Sep 2014 07:24:05 +0000 (08:24 +0100)
If an MFD device is backed by ACPI namespace, we should allow subdevice
drivers to access their corresponding ACPI companion devices through normal
means (e.g using ACPI_COMPANION()).

This patch adds such support to the MFD core. If the MFD parent device
does not specify any ACPI _HID/_CID for the child device, the child
device will share the parent ACPI companion device. Otherwise the child
device will be assigned with the corresponding ACPI companion, if found
in the namespace below the parent.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Darren Hart <dvhart@linux.intel.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Documentation/acpi/enumeration.txt
drivers/mfd/mfd-core.c
include/linux/mfd/core.h

index e182be5e3c83cb553341f7969678370e649fcb2d..b60d2ab69497bc66b47e752d1f7588b3cdbb93ab 100644 (file)
@@ -312,3 +312,30 @@ a code like this:
 
 There are also devm_* versions of these functions which release the
 descriptors once the device is released.
+
+MFD devices
+~~~~~~~~~~~
+The MFD devices register their children as platform devices. For the child
+devices there needs to be an ACPI handle that they can use to reference
+parts of the ACPI namespace that relate to them. In the Linux MFD subsystem
+we provide two ways:
+
+       o The children share the parent ACPI handle.
+       o The MFD cell can specify the ACPI id of the device.
+
+For the first case, the MFD drivers do not need to do anything. The
+resulting child platform device will have its ACPI_COMPANION() set to point
+to the parent device.
+
+If the ACPI namespace has a device that we can match using an ACPI id,
+the id should be set like:
+
+       static struct mfd_cell my_subdevice_cell = {
+               .name = "my_subdevice",
+               /* set the resources relative to the parent */
+               .acpi_pnpid = "XYZ0001",
+       };
+
+The ACPI id "XYZ0001" is then used to lookup an ACPI device directly under
+the MFD device and if found, that ACPI companion device is bound to the
+resulting child platform device.
index 5d0fbe1e097a3b17873dacc714ebca756f57a131..f3338fe9d0691832ba9e13b984bf7aa428e65339 100644 (file)
@@ -78,6 +78,44 @@ static int mfd_platform_add_cell(struct platform_device *pdev,
        return 0;
 }
 
+#if IS_ENABLED(CONFIG_ACPI)
+static void mfd_acpi_add_device(const struct mfd_cell *cell,
+                               struct platform_device *pdev)
+{
+       struct acpi_device *parent_adev;
+       struct acpi_device *adev;
+
+       parent_adev = ACPI_COMPANION(pdev->dev.parent);
+       if (!parent_adev)
+               return;
+
+       /*
+        * MFD child device gets its ACPI handle either from the ACPI
+        * device directly under the parent that matches the acpi_pnpid or
+        * it will use the parent handle if is no acpi_pnpid is given.
+        */
+       adev = parent_adev;
+       if (cell->acpi_pnpid) {
+               struct acpi_device_id ids[2] = {};
+               struct acpi_device *child_adev;
+
+               strlcpy(ids[0].id, cell->acpi_pnpid, sizeof(ids[0].id));
+               list_for_each_entry(child_adev, &parent_adev->children, node)
+                       if (acpi_match_device_ids(child_adev, ids)) {
+                               adev = child_adev;
+                               break;
+                       }
+       }
+
+       ACPI_COMPANION_SET(&pdev->dev, adev);
+}
+#else
+static inline void mfd_acpi_add_device(const struct mfd_cell *cell,
+                                      struct platform_device *pdev)
+{
+}
+#endif
+
 static int mfd_add_device(struct device *parent, int id,
                          const struct mfd_cell *cell, atomic_t *usage_count,
                          struct resource *mem_base,
@@ -119,6 +157,8 @@ static int mfd_add_device(struct device *parent, int id,
                }
        }
 
+       mfd_acpi_add_device(cell, pdev);
+
        if (cell->pdata_size) {
                ret = platform_device_add_data(pdev,
                                        cell->platform_data, cell->pdata_size);
index f543de91ce19d91ae6c0eb065f8687061581ea64..73e1709d4c093de820fa0584f5f28f48ea41b543 100644 (file)
@@ -44,6 +44,9 @@ struct mfd_cell {
         */
        const char              *of_compatible;
 
+       /* Matches ACPI PNP id, either _HID or _CID */
+       const char              *acpi_pnpid;
+
        /*
         * These resources can be specified relative to the parent device.
         * For accessing hardware you should use resources from the platform dev