mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3 0/3] Migrate the multiplexer subsystem to fwnode
@ 2026-09-29 20:51 Fabio Forni via B4 Relay
  2026-09-29 20:51 ` [PATCH v3 1/3] mux: convert to use fwnode interface Fabio Forni via B4 Relay
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Fabio Forni via B4 Relay @ 2026-09-29 20:51 UTC (permalink / raw)
  To: Peter Rosin, Linus Walleij
  Cc: linux-kernel, linux-gpio, xu.yang_2, Alvin Šipraga, Fabio Forni

This patch migrates the multiplexer subsystem from using the of_*          
family of functions and structs, to the more generic fwnode framework.     
                                                                           
It is a rebase of a single commit[1] contained in a old patch series[2]
submitted by Xu Yang. The original commit plus follow-up comments were
tested on kernel v6.12 on an arm64-based board, but this current rebase
isn't tested yet.                                                  

While at it, a use-after-free bug was fixed in mux_get(), as suggested by
Alvin Šipraga.
                                                                           
Link: https://lore.kernel.org/all/20220823195429.1243516-3-xu.yang_2@nxp.com [1]
Link: https://lore.kernel.org/all/20220823195429.1243516-1-xu.yang_2@nxp.com [2]

Signed-off-by: Fabio Forni <development@redaril.me>
---
Changes in v3:
- Expand documentation of mux_chip_find_by_fwnode().
- Fix use-after-free bug in mux_get().
- Link to v2: https://lore.kernel.org/r/20260916-mux_fwnode-v2-1-58f1d85b9dde@redaril.me

Changes in v2:
- Rename devm_mux_state_get_from_swnode into devm_mux_state_get_from_fwnode
- Link to v1: https://lore.kernel.org/r/20260915-mux_fwnode-v1-1-ed5a6d8202d4@redaril.me

---
Fabio Forni (3):
      mux: convert to use fwnode interface
      mux: Document mux_chip_find_by_fwnode()
      mux: Avoid use-after-free of args.fwnode in mux_get()

 drivers/mux/core.c                    | 134 ++++++++++++++++++++--------------
 drivers/pinctrl/pinctrl-generic-mux.c |   4 +-
 include/linux/mux/consumer.h          |   6 +-
 3 files changed, 86 insertions(+), 58 deletions(-)
---
base-commit: f6e7b42bf05b2427fb8a7a1d1c387a86638bb413
change-id: 20260915-mux_fwnode-a16593c39ffd

Best regards,
-- 
Fabio Forni <development@redaril.me>



^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3 1/3] mux: convert to use fwnode interface
  2026-09-29 20:51 [PATCH v3 0/3] Migrate the multiplexer subsystem to fwnode Fabio Forni via B4 Relay
@ 2026-09-29 20:51 ` Fabio Forni via B4 Relay
  2026-09-30  9:39   ` Alvin Šipraga
  2026-09-29 20:51 ` [PATCH v3 2/3] mux: Document mux_chip_find_by_fwnode() Fabio Forni via B4 Relay
  2026-09-29 20:51 ` [PATCH v3 3/3] mux: Avoid use-after-free of args.fwnode in mux_get() Fabio Forni via B4 Relay
  2 siblings, 1 reply; 7+ messages in thread
From: Fabio Forni via B4 Relay @ 2026-09-29 20:51 UTC (permalink / raw)
  To: Peter Rosin, Linus Walleij
  Cc: linux-kernel, linux-gpio, xu.yang_2, Alvin Šipraga, Fabio Forni

From: Fabio Forni <development@redaril.me>

As firmware node is a more common abstract, this will convert the whole
thing to fwnode interface.

Co-developed-by: Xu Yang <xu.yang_2@nxp.com>
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
Signed-off-by: Fabio Forni <development@redaril.me>
---
 drivers/mux/core.c                    | 96 ++++++++++++++++++-----------------
 drivers/pinctrl/pinctrl-generic-mux.c |  4 +-
 include/linux/mux/consumer.h          |  6 ++-
 3 files changed, 57 insertions(+), 49 deletions(-)

diff --git a/drivers/mux/core.c b/drivers/mux/core.c
index 5083e3d19606..56096a9139bd 100644
--- a/drivers/mux/core.c
+++ b/drivers/mux/core.c
@@ -18,7 +18,7 @@
 #include <linux/module.h>
 #include <linux/mux/consumer.h>
 #include <linux/mux/driver.h>
-#include <linux/of.h>
+#include <linux/property.h>
 #include <linux/slab.h>
 
 /*
@@ -118,6 +118,7 @@ struct mux_chip *mux_chip_alloc(struct device *dev,
 	mux_chip->dev.type = &mux_type;
 	mux_chip->dev.parent = dev;
 	mux_chip->dev.of_node = dev->of_node;
+	mux_chip->dev.fwnode = dev->fwnode;
 	dev_set_drvdata(&mux_chip->dev, mux_chip);
 
 	mux_chip->id = ida_alloc(&mux_ida, GFP_KERNEL);
@@ -517,11 +518,11 @@ int mux_state_deselect(struct mux_state *mstate)
 EXPORT_SYMBOL_GPL(mux_state_deselect);
 
 /* Note this function returns a reference to the mux_chip dev. */
-static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
+static struct mux_chip *mux_chip_find_by_fwnode(struct fwnode_handle *fwnode)
 {
 	struct device *dev;
 
-	dev = class_find_device_by_of_node(&mux_class, np);
+	dev = class_find_device_by_fwnode(&mux_class, fwnode);
 
 	return dev ? to_mux_chip(dev) : NULL;
 }
@@ -533,17 +534,17 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
  * @state: Pointer to where the requested state is returned, or NULL when
  *         the required multiplexer states are handled by other means.
  * @optional: Whether to return NULL and silence errors when mux doesn't exist.
- * @node: the device nodes, use dev->of_node if it is NULL.
+ * @node: the device nodes, use dev's fwnode if it is NULL.
  *
  * Return: Pointer to the mux-control on success, an ERR_PTR with a negative
  * errno on error, or NULL if optional is true and mux doesn't exist.
  */
 static struct mux_control *mux_get(struct device *dev, const char *mux_name,
 				   unsigned int *state, bool optional,
-				   struct device_node *node)
+				   struct fwnode_handle *node)
 {
-	struct device_node *np = node ? node : dev->of_node;
-	struct of_phandle_args args;
+	struct fwnode_handle *fwnode = node ? node : dev_fwnode(dev);
+	struct fwnode_reference_args args;
 	struct mux_chip *mux_chip;
 	unsigned int controller;
 	int index = 0;
@@ -551,11 +552,13 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
 
 	if (mux_name) {
 		if (state)
-			index = of_property_match_string(np, "mux-state-names",
-							 mux_name);
+			index = fwnode_property_match_string(fwnode,
+							     "mux-state-names",
+							     mux_name);
 		else
-			index = of_property_match_string(np, "mux-control-names",
-							 mux_name);
+			index = fwnode_property_match_string(fwnode,
+							     "mux-control-names",
+							     mux_name);
 		if (index < 0 && optional) {
 			return NULL;
 		} else if (index < 0) {
@@ -566,39 +569,40 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
 	}
 
 	if (state)
-		ret = of_parse_phandle_with_args(np,
-						 "mux-states", "#mux-state-cells",
-						 index, &args);
+		ret = fwnode_property_get_reference_args(fwnode, "mux-states",
+							 "#mux-state-cells", 0,
+							 index, &args);
 	else
-		ret = of_parse_phandle_with_args(np,
-						 "mux-controls", "#mux-control-cells",
-						 index, &args);
+		ret = fwnode_property_get_reference_args(fwnode,
+							 "mux-controls", "#mux-control-cells",
+							 0, index, &args);
+
 	if (ret) {
 		if (optional && ret == -ENOENT)
 			return NULL;
 
-		dev_err(dev, "%pOF: failed to get mux-%s %s(%i)\n",
-			np, state ? "state" : "control",
-			mux_name ?: "", index);
+		dev_err(dev, "%pfw: failed to get mux-%s %s(%i)\n",
+			fwnode, state ? "state" : "control", mux_name ?: "",
+			index);
 		return ERR_PTR(ret);
 	}
 
-	mux_chip = of_find_mux_chip_by_node(args.np);
-	of_node_put(args.np);
+	mux_chip = mux_chip_find_by_fwnode(args.fwnode);
+	fwnode_handle_put(args.fwnode);
 	if (!mux_chip)
 		return ERR_PTR(-EPROBE_DEFER);
 
 	controller = 0;
 	if (state) {
-		if (args.args_count > 2 || args.args_count == 0 ||
-		    (args.args_count < 2 && mux_chip->controllers > 1)) {
-			dev_err(dev, "%pOF: wrong #mux-state-cells for %pOF\n",
-				np, args.np);
+		if (args.nargs > 2 || args.nargs == 0 ||
+		    (args.nargs < 2 && mux_chip->controllers > 1)) {
+			dev_err(dev, "%pfw: wrong #mux-state-cells for %pfw\n",
+				fwnode, args.fwnode);
 			put_device(&mux_chip->dev);
 			return ERR_PTR(-EINVAL);
 		}
 
-		if (args.args_count == 2) {
+		if (args.nargs == 2) {
 			controller = args.args[0];
 			*state = args.args[1];
 		} else {
@@ -606,21 +610,21 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
 		}
 
 	} else {
-		if (args.args_count > 1 ||
-		    (!args.args_count && mux_chip->controllers > 1)) {
-			dev_err(dev, "%pOF: wrong #mux-control-cells for %pOF\n",
-				np, args.np);
+		if (args.nargs > 1 ||
+		    (!args.nargs && mux_chip->controllers > 1)) {
+			dev_err(dev, "%pfw: wrong #mux-control-cells for %pfw\n",
+				fwnode, args.fwnode);
 			put_device(&mux_chip->dev);
 			return ERR_PTR(-EINVAL);
 		}
 
-		if (args.args_count)
+		if (args.nargs)
 			controller = args.args[0];
 	}
 
 	if (controller >= mux_chip->controllers) {
-		dev_err(dev, "%pOF: bad mux controller %u specified in %pOF\n",
-			np, controller, args.np);
+		dev_err(dev, "%pfw: bad mux controller %u specified in %pfw\n",
+			fwnode, controller, args.fwnode);
 		put_device(&mux_chip->dev);
 		return ERR_PTR(-EINVAL);
 	}
@@ -714,14 +718,14 @@ EXPORT_SYMBOL_GPL(devm_mux_control_get);
  * @dev: The device that needs a mux-state.
  * @mux_name: The name identifying the mux-state.
  * @optional: Whether to return NULL and silence errors when mux doesn't exist.
- * @np: the device nodes, use dev->of_node if it is NULL.
+ * @node: the device nodes, use dev's fwnode if it is NULL.
  *
  * Return: Pointer to the mux-state on success, an ERR_PTR with a negative
  * errno on error, or NULL if optional is true and mux doesn't exist.
  */
 static struct mux_state *
 mux_state_get(struct device *dev, const char *mux_name, bool optional,
-	      struct device_node *np)
+	      struct fwnode_handle *node)
 {
 	struct mux_state *mstate;
 
@@ -729,7 +733,7 @@ mux_state_get(struct device *dev, const char *mux_name, bool optional,
 	if (!mstate)
 		return ERR_PTR(-ENOMEM);
 
-	mstate->mux = mux_get(dev, mux_name, &mstate->state, optional, np);
+	mstate->mux = mux_get(dev, mux_name, &mstate->state, optional, node);
 	if (IS_ERR(mstate->mux)) {
 		int err = PTR_ERR(mstate->mux);
 
@@ -771,7 +775,7 @@ static void devm_mux_state_release(struct device *dev, void *res)
  * @dev: The device that needs a mux-state.
  * @mux_name: The name identifying the mux-state.
  * @optional: Whether to return NULL and silence errors when mux doesn't exist.
- * @np: The device nodes, use dev->of_node if it is NULL.
+ * @node: The device nodes, use dev's fwnode if it is NULL.
  * @init: Optional function pointer for mux-state object initialisation.
  * @exit: Optional function pointer for mux-state object cleanup on release.
  *
@@ -779,7 +783,7 @@ static void devm_mux_state_release(struct device *dev, void *res)
  * errno on error, or NULL if optional is true and mux doesn't exist.
  */
 static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mux_name,
-					      bool optional, struct device_node *np,
+					      bool optional, struct fwnode_handle *node,
 					      int (*init)(struct mux_state *mstate),
 					      int (*exit)(struct mux_state *mstate))
 {
@@ -787,7 +791,7 @@ static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mu
 	struct mux_state *mstate;
 	int ret;
 
-	mstate = mux_state_get(dev, mux_name, optional, np);
+	mstate = mux_state_get(dev, mux_name, optional, node);
 	if (IS_ERR(mstate))
 		return ERR_CAST(mstate);
 	else if (optional && !mstate)
@@ -821,23 +825,23 @@ static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mu
 }
 
 /**
- * devm_mux_state_get_from_np() - Get the mux-state for a device, with resource
+ * devm_mux_state_get_from_fwnode() - Get the mux-state for a device, with resource
  *				  management.
  * @dev: The device that needs a mux-control.
  * @mux_name: The name identifying the mux-control.
- * @np: the device nodes, use dev->of_node if it is NULL.
+ * @node: the device nodes, use dev's fwnode if it is NULL.
  *
  * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
  *
  * The mux-state will automatically be freed on release.
  */
 struct mux_state *
-devm_mux_state_get_from_np(struct device *dev, const char *mux_name,
-			   struct device_node *np)
+devm_mux_state_get_from_fwnode(struct device *dev, const char *mux_name,
+			       struct fwnode_handle *node)
 {
-	return __devm_mux_state_get(dev, mux_name, false, np, NULL, NULL);
+	return __devm_mux_state_get(dev, mux_name, false, node, NULL, NULL);
 }
-EXPORT_SYMBOL_GPL(devm_mux_state_get_from_np);
+EXPORT_SYMBOL_GPL(devm_mux_state_get_from_fwnode);
 
 /**
  * devm_mux_state_get_optional() - Get the optional mux-state for a device,
diff --git a/drivers/pinctrl/pinctrl-generic-mux.c b/drivers/pinctrl/pinctrl-generic-mux.c
index 202b72351efb..6d5b6100c5ca 100644
--- a/drivers/pinctrl/pinctrl-generic-mux.c
+++ b/drivers/pinctrl/pinctrl-generic-mux.c
@@ -50,7 +50,9 @@ mux_pinmux_dt_node_to_map(struct pinctrl_dev *pctldev,
 	if (!group_names)
 		return -ENOMEM;
 
-	function->mux_state = devm_mux_state_get_from_np(pctldev->dev, NULL, np_config);
+	function->mux_state = devm_mux_state_get_from_fwnode(pctldev->dev,
+							     NULL,
+							     of_fwnode_handle(np_config));
 	if (IS_ERR(function->mux_state))
 		return PTR_ERR(function->mux_state);
 
diff --git a/include/linux/mux/consumer.h b/include/linux/mux/consumer.h
index 449e38e6e2c5..7d121217ca96 100644
--- a/include/linux/mux/consumer.h
+++ b/include/linux/mux/consumer.h
@@ -11,6 +11,7 @@
 #define _LINUX_MUX_CONSUMER_H
 
 #include <linux/compiler.h>
+#include <linux/device.h>
 
 struct device;
 struct mux_control;
@@ -62,7 +63,8 @@ void mux_control_put(struct mux_control *mux);
 struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name);
 
 struct mux_state *
-devm_mux_state_get_from_np(struct device *dev, const char *mux_name, struct device_node *np);
+devm_mux_state_get_from_fwnode(struct device *dev, const char *mux_name,
+			       struct fwnode_handle *node);
 
 struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name);
 struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name);
@@ -165,6 +167,6 @@ static inline struct mux_state *devm_mux_state_get_optional_selected(struct devi
 #endif /* CONFIG_MULTIPLEXER */
 
 #define devm_mux_state_get(dev, mux_name)		\
-	devm_mux_state_get_from_np(dev, mux_name, NULL)
+	devm_mux_state_get_from_fwnode(dev, mux_name, NULL)
 
 #endif /* _LINUX_MUX_CONSUMER_H */

-- 
2.55.0



^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3 2/3] mux: Document mux_chip_find_by_fwnode()
  2026-09-29 20:51 [PATCH v3 0/3] Migrate the multiplexer subsystem to fwnode Fabio Forni via B4 Relay
  2026-09-29 20:51 ` [PATCH v3 1/3] mux: convert to use fwnode interface Fabio Forni via B4 Relay
@ 2026-09-29 20:51 ` Fabio Forni via B4 Relay
  2026-09-30  9:39   ` Alvin Šipraga
  2026-09-29 20:51 ` [PATCH v3 3/3] mux: Avoid use-after-free of args.fwnode in mux_get() Fabio Forni via B4 Relay
  2 siblings, 1 reply; 7+ messages in thread
From: Fabio Forni via B4 Relay @ 2026-09-29 20:51 UTC (permalink / raw)
  To: Peter Rosin, Linus Walleij
  Cc: linux-kernel, linux-gpio, xu.yang_2, Alvin Šipraga, Fabio Forni

From: Fabio Forni <development@redaril.me>

What mux_chip_find_by_fwnode() does it pretty obvious. What's
not-so-obvious is that the reference count of mux_chip->dev must be
decreased when done working with it. This commit clarifies the
requirement.

Signed-off-by: Fabio Forni <development@redaril.me>
---
 drivers/mux/core.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/mux/core.c b/drivers/mux/core.c
index 56096a9139bd..d5121772c483 100644
--- a/drivers/mux/core.c
+++ b/drivers/mux/core.c
@@ -517,7 +517,16 @@ int mux_state_deselect(struct mux_state *mstate)
 }
 EXPORT_SYMBOL_GPL(mux_state_deselect);
 
-/* Note this function returns a reference to the mux_chip dev. */
+/**
+ * mux_chip_find_by_fwnode() - Find a mux-chip by its fwnode.
+ * @fwnode: The fwnode representing the mux-chip.
+ *
+ * When a mux-chip is found, the mux-chip increases the reference count of
+ * the underlying &struct device. The caller is responsible for calling
+ * put_device() on it.
+ *
+ * Return: Pointer to the mux-chip, or NULL if not found.
+ */
 static struct mux_chip *mux_chip_find_by_fwnode(struct fwnode_handle *fwnode)
 {
 	struct device *dev;

-- 
2.55.0



^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3 3/3] mux: Avoid use-after-free of args.fwnode in mux_get()
  2026-09-29 20:51 [PATCH v3 0/3] Migrate the multiplexer subsystem to fwnode Fabio Forni via B4 Relay
  2026-09-29 20:51 ` [PATCH v3 1/3] mux: convert to use fwnode interface Fabio Forni via B4 Relay
  2026-09-29 20:51 ` [PATCH v3 2/3] mux: Document mux_chip_find_by_fwnode() Fabio Forni via B4 Relay
@ 2026-09-29 20:51 ` Fabio Forni via B4 Relay
  2026-09-30  9:45   ` Alvin Šipraga
  2 siblings, 1 reply; 7+ messages in thread
From: Fabio Forni via B4 Relay @ 2026-09-29 20:51 UTC (permalink / raw)
  To: Peter Rosin, Linus Walleij
  Cc: linux-kernel, linux-gpio, xu.yang_2, Alvin Šipraga, Fabio Forni

From: Fabio Forni <development@redaril.me>

fwnode_handle_put(args.fwnode) was called right after
mux_chip_find_by_fwnode(), but it was too early because the error
handling code below would pass args.fwnode to dev_err().
Let's move all freeing functions to the bottom of mux_get() to avoid
use-after-free bugs.

Signed-off-by: Fabio Forni <development@redaril.me>
---
 drivers/mux/core.c | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/drivers/mux/core.c b/drivers/mux/core.c
index d5121772c483..8bf8c79bc634 100644
--- a/drivers/mux/core.c
+++ b/drivers/mux/core.c
@@ -545,6 +545,9 @@ static struct mux_chip *mux_chip_find_by_fwnode(struct fwnode_handle *fwnode)
  * @optional: Whether to return NULL and silence errors when mux doesn't exist.
  * @node: the device nodes, use dev's fwnode if it is NULL.
  *
+ * When a mux-control is found, it is the caller's responsibility to call
+ * mux_control_put() on it when it is no longer needed.
+ *
  * Return: Pointer to the mux-control on success, an ERR_PTR with a negative
  * errno on error, or NULL if optional is true and mux doesn't exist.
  */
@@ -597,9 +600,10 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
 	}
 
 	mux_chip = mux_chip_find_by_fwnode(args.fwnode);
-	fwnode_handle_put(args.fwnode);
-	if (!mux_chip)
-		return ERR_PTR(-EPROBE_DEFER);
+	if (!mux_chip) {
+		ret = -EPROBE_DEFER;
+		goto end;
+	}
 
 	controller = 0;
 	if (state) {
@@ -607,8 +611,8 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
 		    (args.nargs < 2 && mux_chip->controllers > 1)) {
 			dev_err(dev, "%pfw: wrong #mux-state-cells for %pfw\n",
 				fwnode, args.fwnode);
-			put_device(&mux_chip->dev);
-			return ERR_PTR(-EINVAL);
+			ret = -EINVAL;
+			goto end;
 		}
 
 		if (args.nargs == 2) {
@@ -623,8 +627,8 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
 		    (!args.nargs && mux_chip->controllers > 1)) {
 			dev_err(dev, "%pfw: wrong #mux-control-cells for %pfw\n",
 				fwnode, args.fwnode);
-			put_device(&mux_chip->dev);
-			return ERR_PTR(-EINVAL);
+			ret = -EINVAL;
+			goto end;
 		}
 
 		if (args.nargs)
@@ -634,10 +638,17 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
 	if (controller >= mux_chip->controllers) {
 		dev_err(dev, "%pfw: bad mux controller %u specified in %pfw\n",
 			fwnode, controller, args.fwnode);
-		put_device(&mux_chip->dev);
-		return ERR_PTR(-EINVAL);
+		ret = -EINVAL;
+		goto end;
 	}
 
+end:
+	fwnode_handle_put(args.fwnode);
+	if (ret < 0) {
+		if (mux_chip)
+			put_device(&mux_chip->dev);
+		return ERR_PTR(ret);
+	}
 	return &mux_chip->mux[controller];
 }
 

-- 
2.55.0



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 1/3] mux: convert to use fwnode interface
  2026-09-29 20:51 ` [PATCH v3 1/3] mux: convert to use fwnode interface Fabio Forni via B4 Relay
@ 2026-09-30  9:39   ` Alvin Šipraga
  0 siblings, 0 replies; 7+ messages in thread
From: Alvin Šipraga @ 2026-09-30  9:39 UTC (permalink / raw)
  To: development
  Cc: Peter Rosin, Linus Walleij, linux-kernel, linux-gpio, xu.yang_2

On Tue, Sep 29, 2026 at 10:51:46PM +0200, Fabio Forni via B4 Relay wrote:
> From: Fabio Forni <development@redaril.me>
> 
> As firmware node is a more common abstract, this will convert the whole
> thing to fwnode interface.
> 
> Co-developed-by: Xu Yang <xu.yang_2@nxp.com>
> Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
> Signed-off-by: Fabio Forni <development@redaril.me>

Reviewed-by: Alvin Šipraga <alvin.sipraga@analog.com>

> ---
>  drivers/mux/core.c                    | 96 ++++++++++++++++++-----------------
>  drivers/pinctrl/pinctrl-generic-mux.c |  4 +-
>  include/linux/mux/consumer.h          |  6 ++-
>  3 files changed, 57 insertions(+), 49 deletions(-)
> 
> diff --git a/drivers/mux/core.c b/drivers/mux/core.c
> index 5083e3d19606..56096a9139bd 100644
> --- a/drivers/mux/core.c
> +++ b/drivers/mux/core.c
> @@ -18,7 +18,7 @@
>  #include <linux/module.h>
>  #include <linux/mux/consumer.h>
>  #include <linux/mux/driver.h>
> -#include <linux/of.h>
> +#include <linux/property.h>
>  #include <linux/slab.h>
>  
>  /*
> @@ -118,6 +118,7 @@ struct mux_chip *mux_chip_alloc(struct device *dev,
>  	mux_chip->dev.type = &mux_type;
>  	mux_chip->dev.parent = dev;
>  	mux_chip->dev.of_node = dev->of_node;
> +	mux_chip->dev.fwnode = dev->fwnode;
>  	dev_set_drvdata(&mux_chip->dev, mux_chip);
>  
>  	mux_chip->id = ida_alloc(&mux_ida, GFP_KERNEL);
> @@ -517,11 +518,11 @@ int mux_state_deselect(struct mux_state *mstate)
>  EXPORT_SYMBOL_GPL(mux_state_deselect);
>  
>  /* Note this function returns a reference to the mux_chip dev. */
> -static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
> +static struct mux_chip *mux_chip_find_by_fwnode(struct fwnode_handle *fwnode)
>  {
>  	struct device *dev;
>  
> -	dev = class_find_device_by_of_node(&mux_class, np);
> +	dev = class_find_device_by_fwnode(&mux_class, fwnode);
>  
>  	return dev ? to_mux_chip(dev) : NULL;
>  }
> @@ -533,17 +534,17 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
>   * @state: Pointer to where the requested state is returned, or NULL when
>   *         the required multiplexer states are handled by other means.
>   * @optional: Whether to return NULL and silence errors when mux doesn't exist.
> - * @node: the device nodes, use dev->of_node if it is NULL.
> + * @node: the device nodes, use dev's fwnode if it is NULL.
>   *
>   * Return: Pointer to the mux-control on success, an ERR_PTR with a negative
>   * errno on error, or NULL if optional is true and mux doesn't exist.
>   */
>  static struct mux_control *mux_get(struct device *dev, const char *mux_name,
>  				   unsigned int *state, bool optional,
> -				   struct device_node *node)
> +				   struct fwnode_handle *node)
>  {
> -	struct device_node *np = node ? node : dev->of_node;
> -	struct of_phandle_args args;
> +	struct fwnode_handle *fwnode = node ? node : dev_fwnode(dev);
> +	struct fwnode_reference_args args;
>  	struct mux_chip *mux_chip;
>  	unsigned int controller;
>  	int index = 0;
> @@ -551,11 +552,13 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
>  
>  	if (mux_name) {
>  		if (state)
> -			index = of_property_match_string(np, "mux-state-names",
> -							 mux_name);
> +			index = fwnode_property_match_string(fwnode,
> +							     "mux-state-names",
> +							     mux_name);
>  		else
> -			index = of_property_match_string(np, "mux-control-names",
> -							 mux_name);
> +			index = fwnode_property_match_string(fwnode,
> +							     "mux-control-names",
> +							     mux_name);
>  		if (index < 0 && optional) {
>  			return NULL;
>  		} else if (index < 0) {
> @@ -566,39 +569,40 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
>  	}
>  
>  	if (state)
> -		ret = of_parse_phandle_with_args(np,
> -						 "mux-states", "#mux-state-cells",
> -						 index, &args);
> +		ret = fwnode_property_get_reference_args(fwnode, "mux-states",
> +							 "#mux-state-cells", 0,
> +							 index, &args);
>  	else
> -		ret = of_parse_phandle_with_args(np,
> -						 "mux-controls", "#mux-control-cells",
> -						 index, &args);
> +		ret = fwnode_property_get_reference_args(fwnode,
> +							 "mux-controls", "#mux-control-cells",
> +							 0, index, &args);
> +
>  	if (ret) {
>  		if (optional && ret == -ENOENT)
>  			return NULL;
>  
> -		dev_err(dev, "%pOF: failed to get mux-%s %s(%i)\n",
> -			np, state ? "state" : "control",
> -			mux_name ?: "", index);
> +		dev_err(dev, "%pfw: failed to get mux-%s %s(%i)\n",
> +			fwnode, state ? "state" : "control", mux_name ?: "",
> +			index);
>  		return ERR_PTR(ret);
>  	}
>  
> -	mux_chip = of_find_mux_chip_by_node(args.np);
> -	of_node_put(args.np);
> +	mux_chip = mux_chip_find_by_fwnode(args.fwnode);
> +	fwnode_handle_put(args.fwnode);
>  	if (!mux_chip)
>  		return ERR_PTR(-EPROBE_DEFER);
>  
>  	controller = 0;
>  	if (state) {
> -		if (args.args_count > 2 || args.args_count == 0 ||
> -		    (args.args_count < 2 && mux_chip->controllers > 1)) {
> -			dev_err(dev, "%pOF: wrong #mux-state-cells for %pOF\n",
> -				np, args.np);
> +		if (args.nargs > 2 || args.nargs == 0 ||
> +		    (args.nargs < 2 && mux_chip->controllers > 1)) {
> +			dev_err(dev, "%pfw: wrong #mux-state-cells for %pfw\n",
> +				fwnode, args.fwnode);
>  			put_device(&mux_chip->dev);
>  			return ERR_PTR(-EINVAL);
>  		}
>  
> -		if (args.args_count == 2) {
> +		if (args.nargs == 2) {
>  			controller = args.args[0];
>  			*state = args.args[1];
>  		} else {
> @@ -606,21 +610,21 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
>  		}
>  
>  	} else {
> -		if (args.args_count > 1 ||
> -		    (!args.args_count && mux_chip->controllers > 1)) {
> -			dev_err(dev, "%pOF: wrong #mux-control-cells for %pOF\n",
> -				np, args.np);
> +		if (args.nargs > 1 ||
> +		    (!args.nargs && mux_chip->controllers > 1)) {
> +			dev_err(dev, "%pfw: wrong #mux-control-cells for %pfw\n",
> +				fwnode, args.fwnode);
>  			put_device(&mux_chip->dev);
>  			return ERR_PTR(-EINVAL);
>  		}
>  
> -		if (args.args_count)
> +		if (args.nargs)
>  			controller = args.args[0];
>  	}
>  
>  	if (controller >= mux_chip->controllers) {
> -		dev_err(dev, "%pOF: bad mux controller %u specified in %pOF\n",
> -			np, controller, args.np);
> +		dev_err(dev, "%pfw: bad mux controller %u specified in %pfw\n",
> +			fwnode, controller, args.fwnode);
>  		put_device(&mux_chip->dev);
>  		return ERR_PTR(-EINVAL);
>  	}
> @@ -714,14 +718,14 @@ EXPORT_SYMBOL_GPL(devm_mux_control_get);
>   * @dev: The device that needs a mux-state.
>   * @mux_name: The name identifying the mux-state.
>   * @optional: Whether to return NULL and silence errors when mux doesn't exist.
> - * @np: the device nodes, use dev->of_node if it is NULL.
> + * @node: the device nodes, use dev's fwnode if it is NULL.
>   *
>   * Return: Pointer to the mux-state on success, an ERR_PTR with a negative
>   * errno on error, or NULL if optional is true and mux doesn't exist.
>   */
>  static struct mux_state *
>  mux_state_get(struct device *dev, const char *mux_name, bool optional,
> -	      struct device_node *np)
> +	      struct fwnode_handle *node)
>  {
>  	struct mux_state *mstate;
>  
> @@ -729,7 +733,7 @@ mux_state_get(struct device *dev, const char *mux_name, bool optional,
>  	if (!mstate)
>  		return ERR_PTR(-ENOMEM);
>  
> -	mstate->mux = mux_get(dev, mux_name, &mstate->state, optional, np);
> +	mstate->mux = mux_get(dev, mux_name, &mstate->state, optional, node);
>  	if (IS_ERR(mstate->mux)) {
>  		int err = PTR_ERR(mstate->mux);
>  
> @@ -771,7 +775,7 @@ static void devm_mux_state_release(struct device *dev, void *res)
>   * @dev: The device that needs a mux-state.
>   * @mux_name: The name identifying the mux-state.
>   * @optional: Whether to return NULL and silence errors when mux doesn't exist.
> - * @np: The device nodes, use dev->of_node if it is NULL.
> + * @node: The device nodes, use dev's fwnode if it is NULL.
>   * @init: Optional function pointer for mux-state object initialisation.
>   * @exit: Optional function pointer for mux-state object cleanup on release.
>   *
> @@ -779,7 +783,7 @@ static void devm_mux_state_release(struct device *dev, void *res)
>   * errno on error, or NULL if optional is true and mux doesn't exist.
>   */
>  static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mux_name,
> -					      bool optional, struct device_node *np,
> +					      bool optional, struct fwnode_handle *node,
>  					      int (*init)(struct mux_state *mstate),
>  					      int (*exit)(struct mux_state *mstate))
>  {
> @@ -787,7 +791,7 @@ static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mu
>  	struct mux_state *mstate;
>  	int ret;
>  
> -	mstate = mux_state_get(dev, mux_name, optional, np);
> +	mstate = mux_state_get(dev, mux_name, optional, node);
>  	if (IS_ERR(mstate))
>  		return ERR_CAST(mstate);
>  	else if (optional && !mstate)
> @@ -821,23 +825,23 @@ static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mu
>  }
>  
>  /**
> - * devm_mux_state_get_from_np() - Get the mux-state for a device, with resource
> + * devm_mux_state_get_from_fwnode() - Get the mux-state for a device, with resource
>   *				  management.
>   * @dev: The device that needs a mux-control.
>   * @mux_name: The name identifying the mux-control.
> - * @np: the device nodes, use dev->of_node if it is NULL.
> + * @node: the device nodes, use dev's fwnode if it is NULL.
>   *
>   * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
>   *
>   * The mux-state will automatically be freed on release.
>   */
>  struct mux_state *
> -devm_mux_state_get_from_np(struct device *dev, const char *mux_name,
> -			   struct device_node *np)
> +devm_mux_state_get_from_fwnode(struct device *dev, const char *mux_name,
> +			       struct fwnode_handle *node)
>  {
> -	return __devm_mux_state_get(dev, mux_name, false, np, NULL, NULL);
> +	return __devm_mux_state_get(dev, mux_name, false, node, NULL, NULL);
>  }
> -EXPORT_SYMBOL_GPL(devm_mux_state_get_from_np);
> +EXPORT_SYMBOL_GPL(devm_mux_state_get_from_fwnode);
>  
>  /**
>   * devm_mux_state_get_optional() - Get the optional mux-state for a device,
> diff --git a/drivers/pinctrl/pinctrl-generic-mux.c b/drivers/pinctrl/pinctrl-generic-mux.c
> index 202b72351efb..6d5b6100c5ca 100644
> --- a/drivers/pinctrl/pinctrl-generic-mux.c
> +++ b/drivers/pinctrl/pinctrl-generic-mux.c
> @@ -50,7 +50,9 @@ mux_pinmux_dt_node_to_map(struct pinctrl_dev *pctldev,
>  	if (!group_names)
>  		return -ENOMEM;
>  
> -	function->mux_state = devm_mux_state_get_from_np(pctldev->dev, NULL, np_config);
> +	function->mux_state = devm_mux_state_get_from_fwnode(pctldev->dev,
> +							     NULL,
> +							     of_fwnode_handle(np_config));
>  	if (IS_ERR(function->mux_state))
>  		return PTR_ERR(function->mux_state);
>  
> diff --git a/include/linux/mux/consumer.h b/include/linux/mux/consumer.h
> index 449e38e6e2c5..7d121217ca96 100644
> --- a/include/linux/mux/consumer.h
> +++ b/include/linux/mux/consumer.h
> @@ -11,6 +11,7 @@
>  #define _LINUX_MUX_CONSUMER_H
>  
>  #include <linux/compiler.h>
> +#include <linux/device.h>
>  
>  struct device;
>  struct mux_control;
> @@ -62,7 +63,8 @@ void mux_control_put(struct mux_control *mux);
>  struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name);
>  
>  struct mux_state *
> -devm_mux_state_get_from_np(struct device *dev, const char *mux_name, struct device_node *np);
> +devm_mux_state_get_from_fwnode(struct device *dev, const char *mux_name,
> +			       struct fwnode_handle *node);
>  
>  struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name);
>  struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name);
> @@ -165,6 +167,6 @@ static inline struct mux_state *devm_mux_state_get_optional_selected(struct devi
>  #endif /* CONFIG_MULTIPLEXER */
>  
>  #define devm_mux_state_get(dev, mux_name)		\
> -	devm_mux_state_get_from_np(dev, mux_name, NULL)
> +	devm_mux_state_get_from_fwnode(dev, mux_name, NULL)
>  
>  #endif /* _LINUX_MUX_CONSUMER_H */
> 
> -- 
> 2.55.0
> 
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 2/3] mux: Document mux_chip_find_by_fwnode()
  2026-09-29 20:51 ` [PATCH v3 2/3] mux: Document mux_chip_find_by_fwnode() Fabio Forni via B4 Relay
@ 2026-09-30  9:39   ` Alvin Šipraga
  0 siblings, 0 replies; 7+ messages in thread
From: Alvin Šipraga @ 2026-09-30  9:39 UTC (permalink / raw)
  To: development
  Cc: Peter Rosin, Linus Walleij, linux-kernel, linux-gpio, xu.yang_2

On Tue, Sep 29, 2026 at 10:51:47PM +0200, Fabio Forni via B4 Relay wrote:
> From: Fabio Forni <development@redaril.me>
> 
> What mux_chip_find_by_fwnode() does it pretty obvious. What's
> not-so-obvious is that the reference count of mux_chip->dev must be
> decreased when done working with it. This commit clarifies the
> requirement.
> 
> Signed-off-by: Fabio Forni <development@redaril.me>

Reviewed-by: Alvin Šipraga <alvin.sipraga@analog.com>

> ---
>  drivers/mux/core.c | 11 ++++++++++-
>  1 file changed, 10 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mux/core.c b/drivers/mux/core.c
> index 56096a9139bd..d5121772c483 100644
> --- a/drivers/mux/core.c
> +++ b/drivers/mux/core.c
> @@ -517,7 +517,16 @@ int mux_state_deselect(struct mux_state *mstate)
>  }
>  EXPORT_SYMBOL_GPL(mux_state_deselect);
>  
> -/* Note this function returns a reference to the mux_chip dev. */
> +/**
> + * mux_chip_find_by_fwnode() - Find a mux-chip by its fwnode.
> + * @fwnode: The fwnode representing the mux-chip.
> + *
> + * When a mux-chip is found, the mux-chip increases the reference count of
> + * the underlying &struct device. The caller is responsible for calling
> + * put_device() on it.
> + *
> + * Return: Pointer to the mux-chip, or NULL if not found.
> + */
>  static struct mux_chip *mux_chip_find_by_fwnode(struct fwnode_handle *fwnode)
>  {
>  	struct device *dev;
> 
> -- 
> 2.55.0
> 
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 3/3] mux: Avoid use-after-free of args.fwnode in mux_get()
  2026-09-29 20:51 ` [PATCH v3 3/3] mux: Avoid use-after-free of args.fwnode in mux_get() Fabio Forni via B4 Relay
@ 2026-09-30  9:45   ` Alvin Šipraga
  0 siblings, 0 replies; 7+ messages in thread
From: Alvin Šipraga @ 2026-09-30  9:45 UTC (permalink / raw)
  To: development
  Cc: Peter Rosin, Linus Walleij, linux-kernel, linux-gpio, xu.yang_2

On Tue, Sep 29, 2026 at 10:51:48PM +0200, Fabio Forni via B4 Relay wrote:
> From: Fabio Forni <development@redaril.me>
> 
> fwnode_handle_put(args.fwnode) was called right after
> mux_chip_find_by_fwnode(), but it was too early because the error
> handling code below would pass args.fwnode to dev_err().
> Let's move all freeing functions to the bottom of mux_get() to avoid
> use-after-free bugs.
> 
> Signed-off-by: Fabio Forni <development@redaril.me>

I meant to suggest that you apply this fix before your fwnode patch, so
that it can be applied to the stable trees. Since you put the fix
afterwards, it either needs backporting, or the fwnode patch needs to be
carried too.

You might also want a Fixes: tag for it to actually get picked up for
stable.

Up to Peter though - it's a minor bug and the kernel has tons of such
refcounting bloopers. As far as the code is concerned it looks fine
(minor nit below). Thanks!

Reviewed-by: Alvin Šipraga <alvin.sipraga@analog.com>

> ---
>  drivers/mux/core.c | 29 ++++++++++++++++++++---------
>  1 file changed, 20 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/mux/core.c b/drivers/mux/core.c
> index d5121772c483..8bf8c79bc634 100644
> --- a/drivers/mux/core.c
> +++ b/drivers/mux/core.c
> @@ -545,6 +545,9 @@ static struct mux_chip *mux_chip_find_by_fwnode(struct fwnode_handle *fwnode)
>   * @optional: Whether to return NULL and silence errors when mux doesn't exist.
>   * @node: the device nodes, use dev's fwnode if it is NULL.
>   *
> + * When a mux-control is found, it is the caller's responsibility to call
> + * mux_control_put() on it when it is no longer needed.
> + *
>   * Return: Pointer to the mux-control on success, an ERR_PTR with a negative
>   * errno on error, or NULL if optional is true and mux doesn't exist.
>   */
> @@ -597,9 +600,10 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
>  	}
>  
>  	mux_chip = mux_chip_find_by_fwnode(args.fwnode);
> -	fwnode_handle_put(args.fwnode);
> -	if (!mux_chip)
> -		return ERR_PTR(-EPROBE_DEFER);
> +	if (!mux_chip) {
> +		ret = -EPROBE_DEFER;
> +		goto end;
> +	}
>  
>  	controller = 0;
>  	if (state) {
> @@ -607,8 +611,8 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
>  		    (args.nargs < 2 && mux_chip->controllers > 1)) {
>  			dev_err(dev, "%pfw: wrong #mux-state-cells for %pfw\n",
>  				fwnode, args.fwnode);
> -			put_device(&mux_chip->dev);
> -			return ERR_PTR(-EINVAL);
> +			ret = -EINVAL;
> +			goto end;
>  		}
>  
>  		if (args.nargs == 2) {
> @@ -623,8 +627,8 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
>  		    (!args.nargs && mux_chip->controllers > 1)) {
>  			dev_err(dev, "%pfw: wrong #mux-control-cells for %pfw\n",
>  				fwnode, args.fwnode);
> -			put_device(&mux_chip->dev);
> -			return ERR_PTR(-EINVAL);
> +			ret = -EINVAL;
> +			goto end;
>  		}
>  
>  		if (args.nargs)
> @@ -634,10 +638,17 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
>  	if (controller >= mux_chip->controllers) {
>  		dev_err(dev, "%pfw: bad mux controller %u specified in %pfw\n",
>  			fwnode, controller, args.fwnode);
> -		put_device(&mux_chip->dev);
> -		return ERR_PTR(-EINVAL);
> +		ret = -EINVAL;
> +		goto end;
>  	}
>  
> +end:
> +	fwnode_handle_put(args.fwnode);
> +	if (ret < 0) {
> +		if (mux_chip)
> +			put_device(&mux_chip->dev);
> +		return ERR_PTR(ret);
> +	}
>  	return &mux_chip->mux[controller];

(nit) A newline between the above } and return would be nice.

>  }
>  
> 
> -- 
> 2.55.0
> 
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-09-30  9:46 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-29 20:51 [PATCH v3 0/3] Migrate the multiplexer subsystem to fwnode Fabio Forni via B4 Relay
2026-09-29 20:51 ` [PATCH v3 1/3] mux: convert to use fwnode interface Fabio Forni via B4 Relay
2026-09-30  9:39   ` Alvin Šipraga
2026-09-29 20:51 ` [PATCH v3 2/3] mux: Document mux_chip_find_by_fwnode() Fabio Forni via B4 Relay
2026-09-30  9:39   ` Alvin Šipraga
2026-09-29 20:51 ` [PATCH v3 3/3] mux: Avoid use-after-free of args.fwnode in mux_get() Fabio Forni via B4 Relay
2026-09-30  9:45   ` Alvin Šipraga

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®