summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/acpi/enumeration.txt27
-rw-r--r--drivers/mfd/mfd-core.c40
-rw-r--r--include/linux/mfd/core.h3
3 files changed, 70 insertions, 0 deletions
diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt
index e182be5e3c83..b60d2ab69497 100644
--- a/Documentation/acpi/enumeration.txt
+++ b/Documentation/acpi/enumeration.txt
@@ -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.
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 5d0fbe1e097a..f3338fe9d069 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -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);
diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h
index f543de91ce19..73e1709d4c09 100644
--- a/include/linux/mfd/core.h
+++ b/include/linux/mfd/core.h
@@ -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