[PATCH v22 08/13] mfd: core: Add firmware-node support to MFD cells

Bartosz Golaszewski brgl at kernel.org
Mon May 18 01:57:40 PDT 2026


On Thu, 14 May 2026 16:25:49 +0200, Shivendra Pratap
<shivendra.pratap at oss.qualcomm.com> said:
> MFD core has no way to register a child device using an explicit firmware
> node. This prevents drivers from registering child nodes when those nodes
> do not define a compatible string. One such example is the PSCI
> "reboot-mode" node, which omits a compatible string as it describes
> boot-states provided by the underlying firmware.
>
> Extend struct mfd_cell with a callback that allows drivers to provide an
> explicit firmware node. The node is added to the MFD child device during
> registration when none is assigned by device tree, ACPI, or software
> matching.
>
> Suggested-by: Bartosz Golaszewski <bartosz.golaszewski at oss.qualcomm.com>
> Signed-off-by: Shivendra Pratap <shivendra.pratap at oss.qualcomm.com>
> ---
>  drivers/mfd/mfd-core.c   | 30 ++++++++++++++++++++++++++++++
>  include/linux/mfd/core.h | 14 ++++++++++++++
>  2 files changed, 44 insertions(+)
>
> diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
> index 7aa32b90cf1eb7fa0a05bf3dc506e60a262c9850..cc2a2a924d6d3044e29a9f864b536ee325ed797b 100644
> --- a/drivers/mfd/mfd-core.c
> +++ b/drivers/mfd/mfd-core.c
> @@ -10,6 +10,7 @@
>  #include <linux/kernel.h>
>  #include <linux/platform_device.h>
>  #include <linux/acpi.h>
> +#include <linux/fwnode.h>
>  #include <linux/list.h>
>  #include <linux/property.h>
>  #include <linux/mfd/core.h>
> @@ -148,6 +149,11 @@ static int mfd_match_of_node_to_dev(struct platform_device *pdev,
>  	return 0;
>  }
>
> +static void mfd_child_fwnode_put(void *data)
> +{
> +	fwnode_handle_put(data);
> +}

Ah, this seems to answer my previous question, but...

> +
>  static int mfd_add_device(struct device *parent, int id,
>  			  const struct mfd_cell *cell,
>  			  struct resource *mem_base,
> @@ -156,6 +162,7 @@ static int mfd_add_device(struct device *parent, int id,
>  	struct resource *res;
>  	struct platform_device *pdev;
>  	struct mfd_of_node_entry *of_entry, *tmp;
> +	struct fwnode_handle *fwnode;
>  	bool disabled = false;
>  	int ret = -ENOMEM;
>  	int platform_id;
> @@ -224,6 +231,29 @@ static int mfd_add_device(struct device *parent, int id,
>
>  	mfd_acpi_add_device(cell, pdev);
>
> +	if (!pdev->dev.fwnode && cell->get_child_fwnode) {
> +		fwnode = cell->get_child_fwnode(parent);
> +		if (fwnode) {
> +			device_set_node(&pdev->dev, fwnode);
> +
> +			/*
> +			 * platform_device_release() drops only of_node refs.

Which is a separate problem we're discussing elsewhere. It should probably drop
the fwnode reference it holds, not the one of of_node.

> +			 * Track non-OF fwnodes explicitly so they are put on
> +			 * all teardown paths.
> +			 */
> +			if (!to_of_node(fwnode)) {
> +				ret = devm_add_action(&pdev->dev,
> +						      mfd_child_fwnode_put,
> +						      fwnode);

What if the device never gets bound to the driver? The release will never be
called, this is why it's wrong to schedule devres actions for unbound devices
and one of the reasons for patch 1 in this series.

What I suggest for now is: in tear-down path: see if the cell has the
get_child_fwnode() callback and - if so - drop the reference. Add a big, fat
comment saying that this must be removed if we decide to switch to dropping the
device's fwnode reference in platform driver core which may happen soon.

Bart

> +				if (ret) {
> +					device_set_node(&pdev->dev, NULL);
> +					fwnode_handle_put(fwnode);
> +					goto fail_of_entry;
> +				}
> +			}
> +		}
> +	}
> +
>  	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 faeea7abd688f223fb0b31cde0a9b69dfe2a61ff..abfc26c057d6ee46947ba2b6f2e99f420e74b127 100644
> --- a/include/linux/mfd/core.h
> +++ b/include/linux/mfd/core.h
> @@ -50,6 +50,7 @@
>  #define MFD_DEP_LEVEL_HIGH 1
>
>  struct irq_domain;
> +struct fwnode_handle;
>  struct software_node;
>
>  /* Matches ACPI PNP id, either _HID or _CID, or ACPI _ADR */
> @@ -80,6 +81,19 @@ struct mfd_cell {
>
>  	/* Software node for the device. */
>  	const struct software_node *swnode;
> +	/*
> +	 * Callback to return an explicit firmware node.
> +	 * @parent: MFD parent device passed to mfd_add_devices().
> +	 *
> +	 * Called only if OF/ACPI matching did not assign a fwnode.
> +	 * Ownership of the returned reference is transferred to MFD core.
> +	 *
> +	 * Return a referenced fwnode or NULL if none is available.
> +	 *
> +	 * mfd_cell must be zero-initialized or get_child_fwnode must be NULL
> +	 * when unused.
> +	 */
> +	struct fwnode_handle *(*get_child_fwnode)(struct device *parent);
>
>  	/*
>  	 * Device Tree compatible string
>
> --
> 2.34.1
>
>



More information about the linux-arm-kernel mailing list