mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 00/15] reset: major reset core refactoring
@ 2026-01-05 14:15 Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 01/15] reset: gpio: remove unneeded OF-node put Bartosz Golaszewski
                   ` (14 more replies)
  0 siblings, 15 replies; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

Here is the promised refactoring of the reset core. The main goal of the
series is to make the reset subsystem fwnode-agnostic - meaning it can
work with all kinds of firmware nodes instead of being OF-centric - but
there are some other related changes in here as well. I'm sending it all
out for review to give Phillipp a better picture of the end result but
individual pieces can be picked up earlier if accepted.

The series is logically split into several parts:

Patches 1-5: Several reset-gpio improvements. Most are not very
controversial but I included a reworked version of the patch adding a
firmware device link between the auxiliary reset device and its
consumers.

Patches 6-8: Just general improvements.

Patch 9: Before we support all firmware nodes (even software nodes for
which no devlinks are created) we need to make sure reset drivers can
survive a sudden unbinding of the supplier with consumers still holding
references to the controller. This patch addresses it using SRCU.

Patches 10,11: Rework locking in reset core. Make locking fine-grained
instead of using a single global lock for everything.

Patches 12-15: Make reset core use fwnode as primary source of
device properties and references. Convert reset-gpio to becoming the
first fwnode-agnostic driver.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
Bartosz Golaszewski (15):
      reset: gpio: remove unneeded OF-node put
      reset: gpio: add a devlink between reset-gpio and its consumer
      reset: gpio: simplify fallback device matching
      reset: gpio: remove unneeded auxiliary_set_drvdata()
      reset: warn on reset-gpio release
      reset: fold ida_alloc() into reset_create_gpio_aux_device()
      reset: use lock guards in reset core
      reset: remove unneeded include
      reset: handle removing supplier before consumers
      reset: protect struct reset_controller_dev with its own mutex
      reset: protect struct reset_control with its own mutex
      reset: convert of_reset_control_get_count() to using firmware nodes
      reset: convert the core API to using firmware nodes
      reset: convert reset core to using firmware nodes
      reset: gpio: make the driver fwnode-agnostic

 drivers/reset/core.c             | 472 +++++++++++++++++++++++++--------------
 drivers/reset/reset-gpio.c       |  27 +--
 include/linux/reset-controller.h |  21 +-
 include/linux/reset.h            |  43 ++--
 4 files changed, 356 insertions(+), 207 deletions(-)
---
base-commit: 44610bf200345a31141cd9b1e78541bb08ba7bfb
change-id: 20251222-reset-core-refactor-5fad7197cbcf

Best regards,
-- 
Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>


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

* [PATCH 01/15] reset: gpio: remove unneeded OF-node put
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-08  9:02   ` Philipp Zabel
  2026-01-05 14:15 ` [PATCH 02/15] reset: gpio: add a devlink between reset-gpio and its consumer Bartosz Golaszewski
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

Tasking the reset-gpio driver with controlling the reference count of an
OF node set up in reset core is a weird inversion of responsability. But
it's also wrong in that the underlying device never actually gets
removed so the node should not be put at all and especially not at
driver detach. Remove the devres action.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/reset-gpio.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c
index e5512b3b596b5290af20e5fdd99a38f81e670d2b..003bad361b3d32560f931f6b92234de523f97cf7 100644
--- a/drivers/reset/reset-gpio.c
+++ b/drivers/reset/reset-gpio.c
@@ -56,18 +56,12 @@ static int reset_gpio_of_xlate(struct reset_controller_dev *rcdev,
 	return reset_spec->args[0];
 }
 
-static void reset_gpio_of_node_put(void *data)
-{
-	of_node_put(data);
-}
-
 static int reset_gpio_probe(struct auxiliary_device *adev,
 			    const struct auxiliary_device_id *id)
 {
 	struct device *dev = &adev->dev;
 	struct of_phandle_args *platdata = dev_get_platdata(dev);
 	struct reset_gpio_priv *priv;
-	int ret;
 
 	if (!platdata)
 		return -EINVAL;
@@ -87,10 +81,6 @@ static int reset_gpio_probe(struct auxiliary_device *adev,
 	priv->rc.owner = THIS_MODULE;
 	priv->rc.dev = dev;
 	priv->rc.of_args = platdata;
-	ret = devm_add_action_or_reset(dev, reset_gpio_of_node_put,
-				       priv->rc.of_node);
-	if (ret)
-		return ret;
 
 	/* Cells to match GPIO specifier, but it's not really used */
 	priv->rc.of_reset_n_cells = 2;

-- 
2.47.3


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

* [PATCH 02/15] reset: gpio: add a devlink between reset-gpio and its consumer
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 01/15] reset: gpio: remove unneeded OF-node put Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-08  9:02   ` Philipp Zabel
  2026-01-05 14:15 ` [PATCH 03/15] reset: gpio: simplify fallback device matching Bartosz Golaszewski
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

The device that requests the reset control managed by the reset-gpio
device is effectively its consumer but the devlink is only established
between it and the GPIO controller exposing the reset pin. Add a devlink
between the consumer of the reset control and its supplier. This will
allow us to simplify the GPIOLIB code managing shared GPIOs when
handling the corner case of reset-gpio and gpiolib-shared interacting.
While at it and since we need to store the address of the auxiliary
device: don't allocate memory for the device separately but fold it into
struct reset_gpio_lookup instead.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c | 80 ++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 56 insertions(+), 24 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 0666dfc41ca9af6fbf1768f17157b4faf7849962..813b85cb2758e129c72c09267a387108d7ad683b 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -20,6 +20,7 @@
 #include <linux/kref.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_platform.h>
 #include <linux/reset.h>
 #include <linux/reset-controller.h>
 #include <linux/slab.h>
@@ -77,11 +78,13 @@ struct reset_control_array {
  * @of_args: phandle to the reset controller with all the args like GPIO number
  * @swnode: Software node containing the reference to the GPIO provider
  * @list: list entry for the reset_gpio_lookup_list
+ * @adev: Auxiliary device representing the reset controller
  */
 struct reset_gpio_lookup {
 	struct of_phandle_args of_args;
 	struct fwnode_handle *swnode;
 	struct list_head list;
+	struct auxiliary_device adev;
 };
 
 static const char *rcdev_name(struct reset_controller_dev *rcdev)
@@ -824,49 +827,72 @@ static void __reset_control_put_internal(struct reset_control *rstc)
 
 static void reset_gpio_aux_device_release(struct device *dev)
 {
-	struct auxiliary_device *adev = to_auxiliary_dev(dev);
 
-	kfree(adev);
 }
 
-static int reset_add_gpio_aux_device(struct device *parent,
-				     struct fwnode_handle *swnode,
-				     int id, void *pdata)
+static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev,
+					struct device *parent, int id)
 {
-	struct auxiliary_device *adev;
+	struct auxiliary_device *adev = &rgpio_dev->adev;
 	int ret;
 
-	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
-	if (!adev)
-		return -ENOMEM;
-
 	adev->id = id;
 	adev->name = "gpio";
 	adev->dev.parent = parent;
-	adev->dev.platform_data = pdata;
+	adev->dev.platform_data = &rgpio_dev->of_args;
 	adev->dev.release = reset_gpio_aux_device_release;
-	device_set_node(&adev->dev, swnode);
+	device_set_node(&adev->dev, rgpio_dev->swnode);
 
 	ret = auxiliary_device_init(adev);
-	if (ret) {
-		kfree(adev);
+	if (ret)
 		return ret;
-	}
 
 	ret = __auxiliary_device_add(adev, "reset");
 	if (ret) {
 		auxiliary_device_uninit(adev);
-		kfree(adev);
 		return ret;
 	}
 
-	return ret;
+	return 0;
+}
+
+static void reset_gpio_add_devlink(struct device_node *np,
+				   struct reset_gpio_lookup *rgpio_dev)
+{
+	struct device *consumer;
+
+	/*
+	 * We must use get_dev_from_fwnode() and not of_find_device_by_node()
+	 * because the latter only considers the platform bus while we want to
+	 * get consumers of any kind that can be associated with firmware
+	 * nodes: auxiliary, soundwire, etc.
+	 */
+	consumer = get_dev_from_fwnode(of_fwnode_handle(np));
+	if (consumer) {
+		if (!device_link_add(consumer, &rgpio_dev->adev.dev,
+				     DL_FLAG_AUTOREMOVE_CONSUMER))
+			pr_warn("Failed to create a device link between reset-gpio and its consumer");
+
+		put_device(consumer);
+	}
+	/*
+	 * else { }
+	 *
+	 * TODO: If ever there's a case where we need to support shared
+	 * reset-gpios retrieved from a device node for which there's no
+	 * device present yet, this is where we'd set up a notifier waiting
+	 * for the device to appear in the system. This would be a lot of code
+	 * that would go unused for now so let's cross that bridge when and if
+	 * we get there.
+	 */
 }
 
 /*
- * @args:	phandle to the GPIO provider with all the args like GPIO number
+ * @np: OF-node associated with the consumer
+ * @args: phandle to the GPIO provider with all the args like GPIO number
  */
-static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
+static int __reset_add_reset_gpio_device(struct device_node *np,
+					 const struct of_phandle_args *args)
 {
 	struct property_entry properties[3] = { };
 	unsigned int offset, of_flags, lflags;
@@ -916,8 +942,14 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
 
 	list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) {
 		if (args->np == rgpio_dev->of_args.np) {
-			if (of_phandle_args_equal(args, &rgpio_dev->of_args))
-				return 0; /* Already on the list, done */
+			if (of_phandle_args_equal(args, &rgpio_dev->of_args)) {
+				/*
+				 * Already on the list, create the device link
+				 * and stop here.
+				 */
+				reset_gpio_add_devlink(np, rgpio_dev);
+				return 0;
+			}
 		}
 	}
 
@@ -951,11 +983,11 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
 		goto err_put_of_node;
 	}
 
-	ret = reset_add_gpio_aux_device(parent, rgpio_dev->swnode, id,
-					&rgpio_dev->of_args);
+	ret = reset_create_gpio_aux_device(rgpio_dev, parent, id);
 	if (ret)
 		goto err_del_swnode;
 
+	reset_gpio_add_devlink(np, rgpio_dev);
 	list_add(&rgpio_dev->list, &reset_gpio_lookup_list);
 
 	return 0;
@@ -1035,7 +1067,7 @@ __of_reset_control_get(struct device_node *node, const char *id, int index,
 
 		gpio_fallback = true;
 
-		ret = __reset_add_reset_gpio_device(&args);
+		ret = __reset_add_reset_gpio_device(node, &args);
 		if (ret) {
 			rstc = ERR_PTR(ret);
 			goto out_put;

-- 
2.47.3


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

* [PATCH 03/15] reset: gpio: simplify fallback device matching
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 01/15] reset: gpio: remove unneeded OF-node put Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 02/15] reset: gpio: add a devlink between reset-gpio and its consumer Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-08  9:26   ` Philipp Zabel
  2026-01-05 14:15 ` [PATCH 04/15] reset: gpio: remove unneeded auxiliary_set_drvdata() Bartosz Golaszewski
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

The of_args field of struct reset_controller_dev was introduced to allow
the reset-gpio driver to pass the phandle arguments back to reset core.
The thing is: it doesn't even have to do it. The core sets the platform
data of the auxiliary device *AND* has access to it later on during the
lookup. This means the field is unneeded and all can happen entirely in
reset core.

Remove the field from the public header and don't set it in
reset-gpio.c. Retrieve the platform data in reset core when needed
instead.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c             | 14 +++++---------
 drivers/reset/reset-gpio.c       |  5 -----
 include/linux/reset-controller.h |  4 ----
 3 files changed, 5 insertions(+), 18 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 813b85cb2758e129c72c09267a387108d7ad683b..ec9e4c39e8debd93d59851cfe72fcf44a79ed7e1 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -95,9 +95,6 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev)
 	if (rcdev->of_node)
 		return rcdev->of_node->full_name;
 
-	if (rcdev->of_args)
-		return rcdev->of_args->np->full_name;
-
 	return NULL;
 }
 
@@ -126,9 +123,6 @@ static int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
  */
 int reset_controller_register(struct reset_controller_dev *rcdev)
 {
-	if (rcdev->of_node && rcdev->of_args)
-		return -EINVAL;
-
 	if (!rcdev->of_xlate) {
 		rcdev->of_reset_n_cells = 1;
 		rcdev->of_xlate = of_reset_simple_xlate;
@@ -1007,13 +1001,15 @@ static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_a
 						       bool gpio_fallback)
 {
 	struct reset_controller_dev *rcdev;
+	struct of_phandle_args *rc_args;
 
 	lockdep_assert_held(&reset_list_mutex);
 
 	list_for_each_entry(rcdev, &reset_controller_list, list) {
-		if (gpio_fallback) {
-			if (rcdev->of_args && of_phandle_args_equal(args,
-								    rcdev->of_args))
+		if (gpio_fallback && device_is_compatible(rcdev->dev, "reset-gpio")) {
+			rc_args = dev_get_platdata(rcdev->dev);
+
+			if (of_phandle_args_equal(args, rc_args))
 				return rcdev;
 		} else {
 			if (args->np == rcdev->of_node)
diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c
index 003bad361b3d32560f931f6b92234de523f97cf7..100fa23b56eaa01d042101b11a823196ae5ff113 100644
--- a/drivers/reset/reset-gpio.c
+++ b/drivers/reset/reset-gpio.c
@@ -60,12 +60,8 @@ static int reset_gpio_probe(struct auxiliary_device *adev,
 			    const struct auxiliary_device_id *id)
 {
 	struct device *dev = &adev->dev;
-	struct of_phandle_args *platdata = dev_get_platdata(dev);
 	struct reset_gpio_priv *priv;
 
-	if (!platdata)
-		return -EINVAL;
-
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
@@ -80,7 +76,6 @@ static int reset_gpio_probe(struct auxiliary_device *adev,
 	priv->rc.ops = &reset_gpio_ops;
 	priv->rc.owner = THIS_MODULE;
 	priv->rc.dev = dev;
-	priv->rc.of_args = platdata;
 
 	/* Cells to match GPIO specifier, but it's not really used */
 	priv->rc.of_reset_n_cells = 2;
diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h
index 46514cb1b9e0626872fe71357b24d54ff0e81a8b..aa95b460fdf86f10002a465e285cd0119da6eeaf 100644
--- a/include/linux/reset-controller.h
+++ b/include/linux/reset-controller.h
@@ -35,9 +35,6 @@ struct of_phandle_args;
  * @reset_control_head: head of internal list of requested reset controls
  * @dev: corresponding driver model device struct
  * @of_node: corresponding device tree node as phandle target
- * @of_args: for reset-gpios controllers: corresponding phandle args with
- *           of_node and GPIO number complementing of_node; either this or
- *           of_node should be present
  * @of_reset_n_cells: number of cells in reset line specifiers
  * @of_xlate: translation function to translate from specifier as found in the
  *            device tree to id as given to the reset control ops, defaults
@@ -51,7 +48,6 @@ struct reset_controller_dev {
 	struct list_head reset_control_head;
 	struct device *dev;
 	struct device_node *of_node;
-	const struct of_phandle_args *of_args;
 	int of_reset_n_cells;
 	int (*of_xlate)(struct reset_controller_dev *rcdev,
 			const struct of_phandle_args *reset_spec);

-- 
2.47.3


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

* [PATCH 04/15] reset: gpio: remove unneeded auxiliary_set_drvdata()
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (2 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 03/15] reset: gpio: simplify fallback device matching Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-08  9:27   ` Philipp Zabel
  2026-01-05 14:15 ` [PATCH 05/15] reset: warn on reset-gpio release Bartosz Golaszewski
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

There's no user of the driver data so don't needlessly assign it.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/reset-gpio.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c
index 100fa23b56eaa01d042101b11a823196ae5ff113..6053a458b259f3876a14358cb4797bee8a2ee4de 100644
--- a/drivers/reset/reset-gpio.c
+++ b/drivers/reset/reset-gpio.c
@@ -66,8 +66,6 @@ static int reset_gpio_probe(struct auxiliary_device *adev,
 	if (!priv)
 		return -ENOMEM;
 
-	auxiliary_set_drvdata(adev, &priv->rc);
-
 	priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
 	if (IS_ERR(priv->reset))
 		return dev_err_probe(dev, PTR_ERR(priv->reset),

-- 
2.47.3


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

* [PATCH 05/15] reset: warn on reset-gpio release
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (3 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 04/15] reset: gpio: remove unneeded auxiliary_set_drvdata() Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 06/15] reset: fold ida_alloc() into reset_create_gpio_aux_device() Bartosz Golaszewski
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

While we implement an empty .release() callback for reset-gpio (driver
core requires it), this function will never actually be called as nobody
ever removes the device and the last reference is not dropped anywhere.

This is by design - once created, the reset-gpio device stays in memory.
Make the .release() callback emit a warning, should it ever be called
due to a programming bug.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index ec9e4c39e8debd93d59851cfe72fcf44a79ed7e1..e08056e0f67e86ca76ffd3c1951c543a7e490a23 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -821,7 +821,7 @@ static void __reset_control_put_internal(struct reset_control *rstc)
 
 static void reset_gpio_aux_device_release(struct device *dev)
 {
-
+	WARN("reset-gpio device %s should never have been removed", dev_name(dev));
 }
 
 static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev,

-- 
2.47.3


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

* [PATCH 06/15] reset: fold ida_alloc() into reset_create_gpio_aux_device()
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (4 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 05/15] reset: warn on reset-gpio release Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 07/15] reset: use lock guards in reset core Bartosz Golaszewski
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

We don't need to know the IDA value outside of the function that creates
the auxiliary reset-gpio device. Simplify error handling by folding it
into reset_create_gpio_aux_device().

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index e08056e0f67e86ca76ffd3c1951c543a7e490a23..39bedf990223212998fa8ed4cda517529f94ee23 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -825,10 +825,14 @@ static void reset_gpio_aux_device_release(struct device *dev)
 }
 
 static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev,
-					struct device *parent, int id)
+					struct device *parent)
 {
 	struct auxiliary_device *adev = &rgpio_dev->adev;
-	int ret;
+	int ret, id;
+
+	id = ida_alloc(&reset_gpio_ida, GFP_KERNEL);
+	if (id < 0)
+		return -ENOMEM;
 
 	adev->id = id;
 	adev->name = "gpio";
@@ -838,12 +842,15 @@ static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev,
 	device_set_node(&adev->dev, rgpio_dev->swnode);
 
 	ret = auxiliary_device_init(adev);
-	if (ret)
+	if (ret) {
+		ida_free(&reset_gpio_ida, id);
 		return ret;
+	}
 
 	ret = __auxiliary_device_add(adev, "reset");
 	if (ret) {
 		auxiliary_device_uninit(adev);
+		ida_free(&reset_gpio_ida, id);
 		return ret;
 	}
 
@@ -892,7 +899,7 @@ static int __reset_add_reset_gpio_device(struct device_node *np,
 	unsigned int offset, of_flags, lflags;
 	struct reset_gpio_lookup *rgpio_dev;
 	struct device *parent;
-	int id, ret, prop = 0;
+	int ret, prop = 0;
 
 	/*
 	 * Currently only #gpio-cells=2 is supported with the meaning of:
@@ -952,16 +959,10 @@ static int __reset_add_reset_gpio_device(struct device_node *np,
 	properties[prop++] = PROPERTY_ENTRY_STRING("compatible", "reset-gpio");
 	properties[prop++] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags);
 
-	id = ida_alloc(&reset_gpio_ida, GFP_KERNEL);
-	if (id < 0)
-		return id;
-
 	/* Not freed on success, because it is persisent subsystem data. */
 	rgpio_dev = kzalloc(sizeof(*rgpio_dev), GFP_KERNEL);
-	if (!rgpio_dev) {
-		ret = -ENOMEM;
-		goto err_ida_free;
-	}
+	if (!rgpio_dev)
+		return -ENOMEM;
 
 	rgpio_dev->of_args = *args;
 	/*
@@ -977,7 +978,7 @@ static int __reset_add_reset_gpio_device(struct device_node *np,
 		goto err_put_of_node;
 	}
 
-	ret = reset_create_gpio_aux_device(rgpio_dev, parent, id);
+	ret = reset_create_gpio_aux_device(rgpio_dev, parent);
 	if (ret)
 		goto err_del_swnode;
 
@@ -991,8 +992,6 @@ static int __reset_add_reset_gpio_device(struct device_node *np,
 err_put_of_node:
 	of_node_put(rgpio_dev->of_args.np);
 	kfree(rgpio_dev);
-err_ida_free:
-	ida_free(&reset_gpio_ida, id);
 
 	return ret;
 }

-- 
2.47.3


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

* [PATCH 07/15] reset: use lock guards in reset core
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (5 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 06/15] reset: fold ida_alloc() into reset_create_gpio_aux_device() Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-08  9:39   ` Philipp Zabel
  2026-01-05 14:15 ` [PATCH 08/15] reset: remove unneeded include Bartosz Golaszewski
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

Simplify the locking code in reset core by using lock guard from
linux/cleanup.h.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c | 51 +++++++++++++++++++++++----------------------------
 1 file changed, 23 insertions(+), 28 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 39bedf990223212998fa8ed4cda517529f94ee23..ffb811cbc6460b48dd833d3560d6538eea2fd5c0 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -130,9 +130,9 @@ int reset_controller_register(struct reset_controller_dev *rcdev)
 
 	INIT_LIST_HEAD(&rcdev->reset_control_head);
 
-	mutex_lock(&reset_list_mutex);
+	guard(mutex)(&reset_list_mutex);
+
 	list_add(&rcdev->list, &reset_controller_list);
-	mutex_unlock(&reset_list_mutex);
 
 	return 0;
 }
@@ -144,9 +144,9 @@ EXPORT_SYMBOL_GPL(reset_controller_register);
  */
 void reset_controller_unregister(struct reset_controller_dev *rcdev)
 {
-	mutex_lock(&reset_list_mutex);
+	guard(mutex)(&reset_list_mutex);
+
 	list_del(&rcdev->list);
-	mutex_unlock(&reset_list_mutex);
 }
 EXPORT_SYMBOL_GPL(reset_controller_unregister);
 
@@ -647,25 +647,20 @@ int reset_control_acquire(struct reset_control *rstc)
 	if (reset_control_is_array(rstc))
 		return reset_control_array_acquire(rstc_to_array(rstc));
 
-	mutex_lock(&reset_list_mutex);
+	guard(mutex)(&reset_list_mutex);
 
-	if (rstc->acquired) {
-		mutex_unlock(&reset_list_mutex);
+	if (rstc->acquired)
 		return 0;
-	}
 
 	list_for_each_entry(rc, &rstc->rcdev->reset_control_head, list) {
 		if (rstc != rc && rstc->id == rc->id) {
-			if (rc->acquired) {
-				mutex_unlock(&reset_list_mutex);
+			if (rc->acquired)
 				return -EBUSY;
-			}
 		}
 	}
 
 	rstc->acquired = true;
 
-	mutex_unlock(&reset_list_mutex);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(reset_control_acquire);
@@ -1069,22 +1064,23 @@ __of_reset_control_get(struct device_node *node, const char *id, int index,
 		}
 	}
 
-	mutex_lock(&reset_list_mutex);
+	guard(mutex)(&reset_list_mutex);
+
 	rcdev = __reset_find_rcdev(&args, gpio_fallback);
 	if (!rcdev) {
 		rstc = ERR_PTR(-EPROBE_DEFER);
-		goto out_unlock;
+		goto out_put;
 	}
 
 	if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) {
 		rstc = ERR_PTR(-EINVAL);
-		goto out_unlock;
+		goto out_put;
 	}
 
 	rstc_id = rcdev->of_xlate(rcdev, &args);
 	if (rstc_id < 0) {
 		rstc = ERR_PTR(rstc_id);
-		goto out_unlock;
+		goto out_put;
 	}
 
 	flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL;
@@ -1092,8 +1088,6 @@ __of_reset_control_get(struct device_node *node, const char *id, int index,
 	/* reset_list_mutex also protects the rcdev's reset_control list */
 	rstc = __reset_control_get_internal(rcdev, rstc_id, flags);
 
-out_unlock:
-	mutex_unlock(&reset_list_mutex);
 out_put:
 	of_node_put(args.np);
 
@@ -1135,10 +1129,11 @@ int __reset_control_bulk_get(struct device *dev, int num_rstcs,
 	return 0;
 
 err:
-	mutex_lock(&reset_list_mutex);
+	guard(mutex)(&reset_list_mutex);
+
 	while (i--)
 		__reset_control_put_internal(rstcs[i].rstc);
-	mutex_unlock(&reset_list_mutex);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(__reset_control_bulk_get);
@@ -1147,10 +1142,10 @@ static void reset_control_array_put(struct reset_control_array *resets)
 {
 	int i;
 
-	mutex_lock(&reset_list_mutex);
+	guard(mutex)(&reset_list_mutex);
+
 	for (i = 0; i < resets->num_rstcs; i++)
 		__reset_control_put_internal(resets->rstc[i]);
-	mutex_unlock(&reset_list_mutex);
 	kfree(resets);
 }
 
@@ -1168,9 +1163,9 @@ void reset_control_put(struct reset_control *rstc)
 		return;
 	}
 
-	mutex_lock(&reset_list_mutex);
+	guard(mutex)(&reset_list_mutex);
+
 	__reset_control_put_internal(rstc);
-	mutex_unlock(&reset_list_mutex);
 }
 EXPORT_SYMBOL_GPL(reset_control_put);
 
@@ -1181,10 +1176,10 @@ EXPORT_SYMBOL_GPL(reset_control_put);
  */
 void reset_control_bulk_put(int num_rstcs, struct reset_control_bulk_data *rstcs)
 {
-	mutex_lock(&reset_list_mutex);
+	guard(mutex)(&reset_list_mutex);
+
 	while (num_rstcs--)
 		__reset_control_put_internal(rstcs[num_rstcs].rstc);
-	mutex_unlock(&reset_list_mutex);
 }
 EXPORT_SYMBOL_GPL(reset_control_bulk_put);
 
@@ -1403,10 +1398,10 @@ of_reset_control_array_get(struct device_node *np, enum reset_control_flags flag
 	return &resets->base;
 
 err_rst:
-	mutex_lock(&reset_list_mutex);
+	guard(mutex)(&reset_list_mutex);
+
 	while (--i >= 0)
 		__reset_control_put_internal(resets->rstc[i]);
-	mutex_unlock(&reset_list_mutex);
 
 	kfree(resets);
 

-- 
2.47.3


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

* [PATCH 08/15] reset: remove unneeded include
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (6 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 07/15] reset: use lock guards in reset core Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-08  9:44   ` Philipp Zabel
  2026-01-05 14:15 ` [PATCH 09/15] reset: handle removing supplier before consumers Bartosz Golaszewski
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

Reset core does not use any symbols from linux/of_platform.h so there's
no need to include it.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index ffb811cbc6460b48dd833d3560d6538eea2fd5c0..48eb64654b6de08030674ce3b994021b5f57060e 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -20,7 +20,6 @@
 #include <linux/kref.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_platform.h>
 #include <linux/reset.h>
 #include <linux/reset-controller.h>
 #include <linux/slab.h>

-- 
2.47.3


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

* [PATCH 09/15] reset: handle removing supplier before consumers
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (7 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 08/15] reset: remove unneeded include Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 10/15] reset: protect struct reset_controller_dev with its own mutex Bartosz Golaszewski
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

Except for the reset-gpio, all reset drivers use device tree - and as
such - benefit from the device links set up by driver core. This means,
that no reset supplier will be unbound before all its consumers have
been. For this reason, nobody bothered making the reset core resiliant
to the object life-time issues that are plagueing the kernel. In this
case: reset control handles referencing the reset provider device with
no serialization or NULL-pointer checking.

We now want to make the reset core fwnode-agnostic but before we do, we
must make sure it can survive unbinding of suppliers with consumers
still holding reset control handles.

To that end: use SRCU to protect the rcdev pointer inside struct
reset_control. We protect all sections using the pointer with SRCU
read-only critical sections and synchronize SRCU after every
modification of the pointer.

This is in line with what the GPIO subsystem does and what the proposed
revocable API tries to generalize. When and if the latter makes its way
into the kernel, reset core could potentially also be generalized to use
it.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c | 108 +++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 91 insertions(+), 17 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 48eb64654b6de08030674ce3b994021b5f57060e..a7992b8ad1c1fbd974d221544da00d4ab3938f70 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -23,6 +23,7 @@
 #include <linux/reset.h>
 #include <linux/reset-controller.h>
 #include <linux/slab.h>
+#include <linux/srcu.h>
 
 static DEFINE_MUTEX(reset_list_mutex);
 static LIST_HEAD(reset_controller_list);
@@ -36,6 +37,7 @@ static DEFINE_IDA(reset_gpio_ida);
  * struct reset_control - a reset control
  * @rcdev: a pointer to the reset controller device
  *         this reset control belongs to
+ * @srcu: protects the rcdev pointer from removal during consumer access
  * @list: list entry for the rcdev's reset controller list
  * @id: ID of the reset controller in the reset
  *      controller device
@@ -49,7 +51,8 @@ static DEFINE_IDA(reset_gpio_ida);
  *                   will be either 0 or 1.
  */
 struct reset_control {
-	struct reset_controller_dev *rcdev;
+	struct reset_controller_dev __rcu *rcdev;
+	struct srcu_struct srcu;
 	struct list_head list;
 	unsigned int id;
 	struct kref refcnt;
@@ -137,15 +140,35 @@ int reset_controller_register(struct reset_controller_dev *rcdev)
 }
 EXPORT_SYMBOL_GPL(reset_controller_register);
 
+static void reset_controller_remove(struct reset_controller_dev *rcdev,
+				    struct reset_control *rstc)
+{
+	list_del(&rstc->list);
+	module_put(rcdev->owner);
+	put_device(rcdev->dev);
+}
+
 /**
  * reset_controller_unregister - unregister a reset controller device
  * @rcdev: a pointer to the reset controller device
  */
 void reset_controller_unregister(struct reset_controller_dev *rcdev)
 {
+	struct reset_control *rstc, *pos;
+
 	guard(mutex)(&reset_list_mutex);
 
 	list_del(&rcdev->list);
+
+	/*
+	 * Numb but don't free the remaining reset control handles that are
+	 * still held by consumers.
+	 */
+	list_for_each_entry_safe(rstc, pos, &rcdev->reset_control_head, list) {
+		rcu_assign_pointer(rstc->rcdev, NULL);
+		synchronize_srcu(&rstc->srcu);
+		reset_controller_remove(rcdev, rstc);
+	}
 }
 EXPORT_SYMBOL_GPL(reset_controller_unregister);
 
@@ -322,6 +345,7 @@ static inline bool reset_control_is_array(struct reset_control *rstc)
  */
 int reset_control_reset(struct reset_control *rstc)
 {
+	struct reset_controller_dev *rcdev;
 	int ret;
 
 	if (!rstc)
@@ -333,7 +357,13 @@ int reset_control_reset(struct reset_control *rstc)
 	if (reset_control_is_array(rstc))
 		return reset_control_array_reset(rstc_to_array(rstc));
 
-	if (!rstc->rcdev->ops->reset)
+	guard(srcu)(&rstc->srcu);
+
+	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
+	if (!rcdev)
+		return -ENODEV;
+
+	if (!rcdev->ops->reset)
 		return -ENOTSUPP;
 
 	if (rstc->shared) {
@@ -347,7 +377,7 @@ int reset_control_reset(struct reset_control *rstc)
 			return -EPERM;
 	}
 
-	ret = rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
+	ret = rcdev->ops->reset(rcdev, rstc->id);
 	if (rstc->shared && ret)
 		atomic_dec(&rstc->triggered_count);
 
@@ -437,6 +467,8 @@ EXPORT_SYMBOL_GPL(reset_control_rearm);
  */
 int reset_control_assert(struct reset_control *rstc)
 {
+	struct reset_controller_dev *rcdev;
+
 	if (!rstc)
 		return 0;
 
@@ -446,6 +478,12 @@ int reset_control_assert(struct reset_control *rstc)
 	if (reset_control_is_array(rstc))
 		return reset_control_array_assert(rstc_to_array(rstc));
 
+	guard(srcu)(&rstc->srcu);
+
+	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
+	if (!rcdev)
+		return -ENODEV;
+
 	if (rstc->shared) {
 		if (WARN_ON(atomic_read(&rstc->triggered_count) != 0))
 			return -EINVAL;
@@ -460,7 +498,7 @@ int reset_control_assert(struct reset_control *rstc)
 		 * Shared reset controls allow the reset line to be in any state
 		 * after this call, so doing nothing is a valid option.
 		 */
-		if (!rstc->rcdev->ops->assert)
+		if (!rcdev->ops->assert)
 			return 0;
 	} else {
 		/*
@@ -468,17 +506,17 @@ int reset_control_assert(struct reset_control *rstc)
 		 * is no way to guarantee that the reset line is asserted after
 		 * this call.
 		 */
-		if (!rstc->rcdev->ops->assert)
+		if (!rcdev->ops->assert)
 			return -ENOTSUPP;
 
 		if (!rstc->acquired) {
 			WARN(1, "reset %s (ID: %u) is not acquired\n",
-			     rcdev_name(rstc->rcdev), rstc->id);
+			     rcdev_name(rcdev), rstc->id);
 			return -EPERM;
 		}
 	}
 
-	return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id);
+	return rcdev->ops->assert(rcdev, rstc->id);
 }
 EXPORT_SYMBOL_GPL(reset_control_assert);
 
@@ -525,6 +563,8 @@ EXPORT_SYMBOL_GPL(reset_control_bulk_assert);
  */
 int reset_control_deassert(struct reset_control *rstc)
 {
+	struct reset_controller_dev *rcdev;
+
 	if (!rstc)
 		return 0;
 
@@ -534,6 +574,12 @@ int reset_control_deassert(struct reset_control *rstc)
 	if (reset_control_is_array(rstc))
 		return reset_control_array_deassert(rstc_to_array(rstc));
 
+	guard(srcu)(&rstc->srcu);
+
+	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
+	if (!rcdev)
+		return -ENODEV;
+
 	if (rstc->shared) {
 		if (WARN_ON(atomic_read(&rstc->triggered_count) != 0))
 			return -EINVAL;
@@ -543,7 +589,7 @@ int reset_control_deassert(struct reset_control *rstc)
 	} else {
 		if (!rstc->acquired) {
 			WARN(1, "reset %s (ID: %u) is not acquired\n",
-			     rcdev_name(rstc->rcdev), rstc->id);
+			     rcdev_name(rcdev), rstc->id);
 			return -EPERM;
 		}
 	}
@@ -555,10 +601,10 @@ int reset_control_deassert(struct reset_control *rstc)
 	 * case, the reset controller driver should implement .deassert() and
 	 * return -ENOTSUPP.
 	 */
-	if (!rstc->rcdev->ops->deassert)
+	if (!rcdev->ops->deassert)
 		return 0;
 
-	return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id);
+	return rcdev->ops->deassert(rcdev, rstc->id);
 }
 EXPORT_SYMBOL_GPL(reset_control_deassert);
 
@@ -600,14 +646,22 @@ EXPORT_SYMBOL_GPL(reset_control_bulk_deassert);
  */
 int reset_control_status(struct reset_control *rstc)
 {
+	struct reset_controller_dev *rcdev;
+
 	if (!rstc)
 		return 0;
 
 	if (WARN_ON(IS_ERR(rstc)) || reset_control_is_array(rstc))
 		return -EINVAL;
 
-	if (rstc->rcdev->ops->status)
-		return rstc->rcdev->ops->status(rstc->rcdev, rstc->id);
+	guard(srcu)(&rstc->srcu);
+
+	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
+	if (!rcdev)
+		return -ENODEV;
+
+	if (rcdev->ops->status)
+		return rcdev->ops->status(rcdev, rstc->id);
 
 	return -ENOTSUPP;
 }
@@ -635,6 +689,7 @@ EXPORT_SYMBOL_GPL(reset_control_status);
  */
 int reset_control_acquire(struct reset_control *rstc)
 {
+	struct reset_controller_dev *rcdev;
 	struct reset_control *rc;
 
 	if (!rstc)
@@ -651,7 +706,13 @@ int reset_control_acquire(struct reset_control *rstc)
 	if (rstc->acquired)
 		return 0;
 
-	list_for_each_entry(rc, &rstc->rcdev->reset_control_head, list) {
+	guard(srcu)(&rstc->srcu);
+
+	rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu);
+	if (!rcdev)
+		return -ENODEV;
+
+	list_for_each_entry(rc, &rcdev->reset_control_head, list) {
 		if (rstc != rc && rstc->id == rc->id) {
 			if (rc->acquired)
 				return -EBUSY;
@@ -743,6 +804,7 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev,
 	bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED;
 	bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED;
 	struct reset_control *rstc;
+	int ret;
 
 	lockdep_assert_held(&reset_list_mutex);
 
@@ -773,12 +835,19 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev,
 	if (!rstc)
 		return ERR_PTR(-ENOMEM);
 
+	ret = init_srcu_struct(&rstc->srcu);
+	if (ret) {
+		kfree(rstc);
+		return ERR_PTR(ret);
+	}
+
 	if (!try_module_get(rcdev->owner)) {
+		cleanup_srcu_struct(&rstc->srcu);
 		kfree(rstc);
 		return ERR_PTR(-ENODEV);
 	}
 
-	rstc->rcdev = rcdev;
+	rcu_assign_pointer(rstc->rcdev, rcdev);
 	list_add(&rstc->list, &rcdev->reset_control_head);
 	rstc->id = index;
 	kref_init(&rstc->refcnt);
@@ -793,13 +862,18 @@ static void __reset_control_release(struct kref *kref)
 {
 	struct reset_control *rstc = container_of(kref, struct reset_control,
 						  refcnt);
+	struct reset_controller_dev *rcdev;
 
 	lockdep_assert_held(&reset_list_mutex);
 
-	module_put(rstc->rcdev->owner);
+	scoped_guard(srcu, &rstc->srcu) {
+		rcdev = rcu_replace_pointer(rstc->rcdev, NULL, true);
+		if (rcdev)
+			reset_controller_remove(rcdev, rstc);
+	}
 
-	list_del(&rstc->list);
-	put_device(rstc->rcdev->dev);
+	synchronize_srcu(&rstc->srcu);
+	cleanup_srcu_struct(&rstc->srcu);
 	kfree(rstc);
 }
 

-- 
2.47.3


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

* [PATCH 10/15] reset: protect struct reset_controller_dev with its own mutex
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (8 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 09/15] reset: handle removing supplier before consumers Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 11/15] reset: protect struct reset_control " Bartosz Golaszewski
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

Currently we use a single, global mutex - misleadingly names
reset_list_mutex - to protect the global list of reset devices,
per-controller list of reset control handles and also internal fields of
struct reset_control. Locking can be made a lot more fine-grained if we
use a separate mutex for serializing operations on the list AND
accessing the reset controller device.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c             | 44 ++++++++++++++++++++++++----------------
 include/linux/reset-controller.h |  3 +++
 2 files changed, 30 insertions(+), 17 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index a7992b8ad1c1fbd974d221544da00d4ab3938f70..a53d445fbbfc4601c062e7e5f17278417bd37836 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -131,6 +131,7 @@ int reset_controller_register(struct reset_controller_dev *rcdev)
 	}
 
 	INIT_LIST_HEAD(&rcdev->reset_control_head);
+	mutex_init(&rcdev->lock);
 
 	guard(mutex)(&reset_list_mutex);
 
@@ -143,6 +144,8 @@ EXPORT_SYMBOL_GPL(reset_controller_register);
 static void reset_controller_remove(struct reset_controller_dev *rcdev,
 				    struct reset_control *rstc)
 {
+	lockdep_assert_held(&rcdev->lock);
+
 	list_del(&rstc->list);
 	module_put(rcdev->owner);
 	put_device(rcdev->dev);
@@ -156,19 +159,22 @@ void reset_controller_unregister(struct reset_controller_dev *rcdev)
 {
 	struct reset_control *rstc, *pos;
 
-	guard(mutex)(&reset_list_mutex);
-
-	list_del(&rcdev->list);
+	scoped_guard(mutex, &reset_list_mutex)
+		list_del(&rcdev->list);
 
-	/*
-	 * Numb but don't free the remaining reset control handles that are
-	 * still held by consumers.
-	 */
-	list_for_each_entry_safe(rstc, pos, &rcdev->reset_control_head, list) {
-		rcu_assign_pointer(rstc->rcdev, NULL);
-		synchronize_srcu(&rstc->srcu);
-		reset_controller_remove(rcdev, rstc);
+	scoped_guard(mutex, &rcdev->lock) {
+		/*
+		 * Numb but don't free the remaining reset control handles that are
+		 * still held by consumers.
+		 */
+		list_for_each_entry_safe(rstc, pos, &rcdev->reset_control_head, list) {
+			rcu_assign_pointer(rstc->rcdev, NULL);
+			synchronize_srcu(&rstc->srcu);
+			reset_controller_remove(rcdev, rstc);
+		}
 	}
+
+	mutex_destroy(&rcdev->lock);
 }
 EXPORT_SYMBOL_GPL(reset_controller_unregister);
 
@@ -712,10 +718,12 @@ int reset_control_acquire(struct reset_control *rstc)
 	if (!rcdev)
 		return -ENODEV;
 
-	list_for_each_entry(rc, &rcdev->reset_control_head, list) {
-		if (rstc != rc && rstc->id == rc->id) {
-			if (rc->acquired)
-				return -EBUSY;
+	scoped_guard(mutex, &rcdev->lock) {
+		list_for_each_entry(rc, &rcdev->reset_control_head, list) {
+			if (rstc != rc && rstc->id == rc->id) {
+				if (rc->acquired)
+					return -EBUSY;
+			}
 		}
 	}
 
@@ -806,7 +814,7 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev,
 	struct reset_control *rstc;
 	int ret;
 
-	lockdep_assert_held(&reset_list_mutex);
+	lockdep_assert_held(&rcdev->lock);
 
 	/* Expect callers to filter out OPTIONAL and DEASSERTED bits */
 	if (WARN_ON(flags & ~(RESET_CONTROL_FLAGS_BIT_SHARED |
@@ -868,8 +876,10 @@ static void __reset_control_release(struct kref *kref)
 
 	scoped_guard(srcu, &rstc->srcu) {
 		rcdev = rcu_replace_pointer(rstc->rcdev, NULL, true);
-		if (rcdev)
+		if (rcdev) {
+			guard(mutex)(&rcdev->lock);
 			reset_controller_remove(rcdev, rstc);
+		}
 	}
 
 	synchronize_srcu(&rstc->srcu);
diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h
index aa95b460fdf86f10002a465e285cd0119da6eeaf..2fecb4edeba1a2b1f5f5db2b34c867362c5f9c18 100644
--- a/include/linux/reset-controller.h
+++ b/include/linux/reset-controller.h
@@ -3,6 +3,7 @@
 #define _LINUX_RESET_CONTROLLER_H_
 
 #include <linux/list.h>
+#include <linux/mutex.h>
 
 struct reset_controller_dev;
 
@@ -40,6 +41,7 @@ struct of_phandle_args;
  *            device tree to id as given to the reset control ops, defaults
  *            to :c:func:`of_reset_simple_xlate`.
  * @nr_resets: number of reset controls in this reset controller device
+ * @lock: synchronizes concurrent access to the structure's fields
  */
 struct reset_controller_dev {
 	const struct reset_control_ops *ops;
@@ -52,6 +54,7 @@ struct reset_controller_dev {
 	int (*of_xlate)(struct reset_controller_dev *rcdev,
 			const struct of_phandle_args *reset_spec);
 	unsigned int nr_resets;
+	struct mutex lock;
 };
 
 #if IS_ENABLED(CONFIG_RESET_CONTROLLER)

-- 
2.47.3


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

* [PATCH 11/15] reset: protect struct reset_control with its own mutex
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (9 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 10/15] reset: protect struct reset_controller_dev with its own mutex Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 12/15] reset: convert of_reset_control_get_count() to using firmware nodes Bartosz Golaszewski
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

Currently we use a single, global mutex - misleadingly names
reset_list_mutex - to protect the global list of reset devices,
per-controller list of reset control handles and also internal fields of
struct reset_control. Locking can be made a lot more fine-grained if we
use a separate mutex for serializing operations on the list AND
accessing the reset control handle.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c | 38 ++++++++++++++------------------------
 1 file changed, 14 insertions(+), 24 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index a53d445fbbfc4601c062e7e5f17278417bd37836..d712cf4315477ada98ef81975fd9b535db477e9a 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -49,6 +49,7 @@ static DEFINE_IDA(reset_gpio_ida);
  * @triggered_count: Number of times this reset line has been reset. Currently
  *                   only used for shared resets, which means that the value
  *                   will be either 0 or 1.
+ * @lock: Serializes access to other fields of this structure
  */
 struct reset_control {
 	struct reset_controller_dev __rcu *rcdev;
@@ -61,6 +62,7 @@ struct reset_control {
 	bool array;
 	atomic_t deassert_count;
 	atomic_t triggered_count;
+	struct mutex lock;
 };
 
 /**
@@ -707,7 +709,7 @@ int reset_control_acquire(struct reset_control *rstc)
 	if (reset_control_is_array(rstc))
 		return reset_control_array_acquire(rstc_to_array(rstc));
 
-	guard(mutex)(&reset_list_mutex);
+	guard(mutex)(&rstc->lock);
 
 	if (rstc->acquired)
 		return 0;
@@ -859,6 +861,7 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev,
 	list_add(&rstc->list, &rcdev->reset_control_head);
 	rstc->id = index;
 	kref_init(&rstc->refcnt);
+	mutex_init(&rstc->lock);
 	rstc->acquired = acquired;
 	rstc->shared = shared;
 	get_device(rcdev->dev);
@@ -872,8 +875,6 @@ static void __reset_control_release(struct kref *kref)
 						  refcnt);
 	struct reset_controller_dev *rcdev;
 
-	lockdep_assert_held(&reset_list_mutex);
-
 	scoped_guard(srcu, &rstc->srcu) {
 		rcdev = rcu_replace_pointer(rstc->rcdev, NULL, true);
 		if (rcdev) {
@@ -882,15 +883,14 @@ static void __reset_control_release(struct kref *kref)
 		}
 	}
 
+	mutex_destroy(&rstc->lock);
 	synchronize_srcu(&rstc->srcu);
 	cleanup_srcu_struct(&rstc->srcu);
 	kfree(rstc);
 }
 
-static void __reset_control_put_internal(struct reset_control *rstc)
+static void reset_control_put_internal(struct reset_control *rstc)
 {
-	lockdep_assert_held(&reset_list_mutex);
-
 	if (IS_ERR_OR_NULL(rstc))
 		return;
 
@@ -1103,7 +1103,7 @@ __of_reset_control_get(struct device_node *node, const char *id, int index,
 {
 	bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
 	bool gpio_fallback = false;
-	struct reset_control *rstc;
+	struct reset_control *rstc = ERR_PTR(-EINVAL);
 	struct reset_controller_dev *rcdev;
 	struct of_phandle_args args;
 	int rstc_id;
@@ -1168,8 +1168,8 @@ __of_reset_control_get(struct device_node *node, const char *id, int index,
 
 	flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL;
 
-	/* reset_list_mutex also protects the rcdev's reset_control list */
-	rstc = __reset_control_get_internal(rcdev, rstc_id, flags);
+	scoped_guard(mutex, &rcdev->lock)
+		rstc = __reset_control_get_internal(rcdev, rstc_id, flags);
 
 out_put:
 	of_node_put(args.np);
@@ -1212,10 +1212,8 @@ int __reset_control_bulk_get(struct device *dev, int num_rstcs,
 	return 0;
 
 err:
-	guard(mutex)(&reset_list_mutex);
-
 	while (i--)
-		__reset_control_put_internal(rstcs[i].rstc);
+		reset_control_put_internal(rstcs[i].rstc);
 
 	return ret;
 }
@@ -1225,10 +1223,8 @@ static void reset_control_array_put(struct reset_control_array *resets)
 {
 	int i;
 
-	guard(mutex)(&reset_list_mutex);
-
 	for (i = 0; i < resets->num_rstcs; i++)
-		__reset_control_put_internal(resets->rstc[i]);
+		reset_control_put_internal(resets->rstc[i]);
 	kfree(resets);
 }
 
@@ -1246,9 +1242,7 @@ void reset_control_put(struct reset_control *rstc)
 		return;
 	}
 
-	guard(mutex)(&reset_list_mutex);
-
-	__reset_control_put_internal(rstc);
+	reset_control_put_internal(rstc);
 }
 EXPORT_SYMBOL_GPL(reset_control_put);
 
@@ -1259,10 +1253,8 @@ EXPORT_SYMBOL_GPL(reset_control_put);
  */
 void reset_control_bulk_put(int num_rstcs, struct reset_control_bulk_data *rstcs)
 {
-	guard(mutex)(&reset_list_mutex);
-
 	while (num_rstcs--)
-		__reset_control_put_internal(rstcs[num_rstcs].rstc);
+		reset_control_put_internal(rstcs[num_rstcs].rstc);
 }
 EXPORT_SYMBOL_GPL(reset_control_bulk_put);
 
@@ -1481,10 +1473,8 @@ of_reset_control_array_get(struct device_node *np, enum reset_control_flags flag
 	return &resets->base;
 
 err_rst:
-	guard(mutex)(&reset_list_mutex);
-
 	while (--i >= 0)
-		__reset_control_put_internal(resets->rstc[i]);
+		reset_control_put_internal(resets->rstc[i]);
 
 	kfree(resets);
 

-- 
2.47.3


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

* [PATCH 12/15] reset: convert of_reset_control_get_count() to using firmware nodes
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (10 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 11/15] reset: protect struct reset_control " Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 13/15] reset: convert the core API " Bartosz Golaszewski
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

Start the conversion of reset core to using firmware nodes by reworking
of_reset_control_get_count(). Unfortunately there is no fwnode-based
alternative to of_count_phandle_with_args() so we have to hand-code it.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c | 36 +++++++++++++++++++++++++++---------
 1 file changed, 27 insertions(+), 9 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index d712cf4315477ada98ef81975fd9b535db477e9a..e29b305e4f80558e0ea33efd547ca3f4eed1cfd3 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -12,6 +12,7 @@
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/export.h>
+#include <linux/fwnode.h>
 #include <linux/gpio/driver.h>
 #include <linux/gpio/machine.h>
 #include <linux/gpio/property.h>
@@ -20,6 +21,7 @@
 #include <linux/kref.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/property.h>
 #include <linux/reset.h>
 #include <linux/reset-controller.h>
 #include <linux/slab.h>
@@ -1415,21 +1417,35 @@ EXPORT_SYMBOL_GPL(__device_reset);
  */
 
 /**
- * of_reset_control_get_count - Count number of resets available with a device
+ * fwnode_reset_control_get_count - Count number of resets available with a device
  *
- * @node: device node that contains 'resets'.
+ * @fwnode: firmware node that contains 'resets'.
  *
  * Returns positive reset count on success, or error number on failure and
  * on count being zero.
  */
-static int of_reset_control_get_count(struct device_node *node)
+static int fwnode_reset_control_get_count(struct fwnode_handle *fwnode)
 {
-	int count;
+	struct fwnode_reference_args args;
+	int count = 0, ret;
 
-	if (!node)
+	if (!fwnode)
 		return -EINVAL;
 
-	count = of_count_phandle_with_args(node, "resets", "#reset-cells");
+	for (;;) {
+		ret = fwnode_property_get_reference_args(fwnode, "resets", "#reset-cells",
+							 0, count, &args);
+		if (ret) {
+			if (ret == -ENOENT)
+				break;
+
+			return ret;
+		}
+
+		fwnode_handle_put(args.fwnode);
+		count++;
+	}
+
 	if (count == 0)
 		count = -ENOENT;
 
@@ -1453,7 +1469,7 @@ of_reset_control_array_get(struct device_node *np, enum reset_control_flags flag
 	struct reset_control *rstc;
 	int num, i;
 
-	num = of_reset_control_get_count(np);
+	num = fwnode_reset_control_get_count(of_fwnode_handle(np));
 	if (num < 0)
 		return optional ? NULL : ERR_PTR(num);
 
@@ -1527,8 +1543,10 @@ EXPORT_SYMBOL_GPL(devm_reset_control_array_get);
  */
 int reset_control_get_count(struct device *dev)
 {
-	if (dev->of_node)
-		return of_reset_control_get_count(dev->of_node);
+	struct fwnode_handle *fwnode = dev_fwnode(dev);
+
+	if (fwnode)
+		return fwnode_reset_control_get_count(fwnode);
 
 	return -ENOENT;
 }

-- 
2.47.3


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

* [PATCH 13/15] reset: convert the core API to using firmware nodes
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (11 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 12/15] reset: convert of_reset_control_get_count() to using firmware nodes Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 14/15] reset: convert reset core " Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 15/15] reset: gpio: make the driver fwnode-agnostic Bartosz Golaszewski
  14 siblings, 0 replies; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

In order to simplify the commit converting the internals of reset core
to using firmware nodes, first convert the user-facing API. Modify the
signature of the core consumer functions but leave the specialized
wrappers as is to avoid modifying users for now.

No functional change intended.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c  | 33 ++++++++++++++++++---------------
 include/linux/reset.h | 43 ++++++++++++++++++++++++++++---------------
 2 files changed, 46 insertions(+), 30 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index e29b305e4f80558e0ea33efd547ca3f4eed1cfd3..11c5bd0fe2b23e76db0b4739c5e34e478d83608b 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -1047,7 +1047,7 @@ static int __reset_add_reset_gpio_device(struct device_node *np,
 	rgpio_dev->of_args = *args;
 	/*
 	 * We keep the device_node reference, but of_args.np is put at the end
-	 * of __of_reset_control_get(), so get it one more time.
+	 * of __fwnode_reset_control_get(), so get it one more time.
 	 * Hold reference as long as rgpio_dev memory is valid.
 	 */
 	of_node_get(rgpio_dev->of_args.np);
@@ -1100,18 +1100,19 @@ static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_a
 }
 
 struct reset_control *
-__of_reset_control_get(struct device_node *node, const char *id, int index,
-		       enum reset_control_flags flags)
+__fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int index,
+			   enum reset_control_flags flags)
 {
 	bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
 	bool gpio_fallback = false;
+	struct device_node *node = to_of_node(fwnode);
 	struct reset_control *rstc = ERR_PTR(-EINVAL);
 	struct reset_controller_dev *rcdev;
 	struct of_phandle_args args;
 	int rstc_id;
 	int ret;
 
-	if (!node)
+	if (!fwnode)
 		return ERR_PTR(-EINVAL);
 
 	if (id) {
@@ -1178,7 +1179,7 @@ __of_reset_control_get(struct device_node *node, const char *id, int index,
 
 	return rstc;
 }
-EXPORT_SYMBOL_GPL(__of_reset_control_get);
+EXPORT_SYMBOL_GPL(__fwnode_reset_control_get);
 
 struct reset_control *__reset_control_get(struct device *dev, const char *id,
 					  int index, enum reset_control_flags flags)
@@ -1186,12 +1187,13 @@ struct reset_control *__reset_control_get(struct device *dev, const char *id,
 	bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED;
 	bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED;
 	bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
+	struct fwnode_handle *fwnode = dev_fwnode(dev);
 
 	if (WARN_ON(shared && acquired))
 		return ERR_PTR(-EINVAL);
 
-	if (dev->of_node)
-		return __of_reset_control_get(dev->of_node, id, index, flags);
+	if (fwnode)
+		return __fwnode_reset_control_get(fwnode, id, index, flags);
 
 	return optional ? NULL : ERR_PTR(-ENOENT);
 }
@@ -1453,23 +1455,24 @@ static int fwnode_reset_control_get_count(struct fwnode_handle *fwnode)
 }
 
 /**
- * of_reset_control_array_get - Get a list of reset controls using
- *				device node.
+ * fwnode_reset_control_array_get - Get a list of reset controls using
+ *                                  a firmware node.
  *
- * @np: device node for the device that requests the reset controls array
+ * @fwnode: firmware node for the device that requests the reset controls array
  * @flags: whether reset controls are shared, optional, acquired
  *
  * Returns pointer to allocated reset_control on success or error on failure
  */
 struct reset_control *
-of_reset_control_array_get(struct device_node *np, enum reset_control_flags flags)
+fwnode_reset_control_array_get(struct fwnode_handle *fwnode,
+			       enum reset_control_flags flags)
 {
 	bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
 	struct reset_control_array *resets;
 	struct reset_control *rstc;
 	int num, i;
 
-	num = fwnode_reset_control_get_count(of_fwnode_handle(np));
+	num = fwnode_reset_control_get_count(fwnode);
 	if (num < 0)
 		return optional ? NULL : ERR_PTR(num);
 
@@ -1479,7 +1482,7 @@ of_reset_control_array_get(struct device_node *np, enum reset_control_flags flag
 	resets->num_rstcs = num;
 
 	for (i = 0; i < num; i++) {
-		rstc = __of_reset_control_get(np, NULL, i, flags);
+		rstc = __fwnode_reset_control_get(fwnode, NULL, i, flags);
 		if (IS_ERR(rstc))
 			goto err_rst;
 		resets->rstc[i] = rstc;
@@ -1496,7 +1499,7 @@ of_reset_control_array_get(struct device_node *np, enum reset_control_flags flag
 
 	return rstc;
 }
-EXPORT_SYMBOL_GPL(of_reset_control_array_get);
+EXPORT_SYMBOL_GPL(fwnode_reset_control_array_get);
 
 /**
  * devm_reset_control_array_get - Resource managed reset control array get
@@ -1520,7 +1523,7 @@ devm_reset_control_array_get(struct device *dev, enum reset_control_flags flags)
 	if (!ptr)
 		return ERR_PTR(-ENOMEM);
 
-	rstc = of_reset_control_array_get(dev->of_node, flags);
+	rstc = fwnode_reset_control_array_get(dev_fwnode(dev), flags);
 	if (IS_ERR_OR_NULL(rstc)) {
 		devres_free(ptr);
 		return rstc;
diff --git a/include/linux/reset.h b/include/linux/reset.h
index 44f9e3415f92c9d7591b898cdb4bf7f05f2b0568..9c391cf0c82298a06a4d84eebd293864ef8c48da 100644
--- a/include/linux/reset.h
+++ b/include/linux/reset.h
@@ -5,10 +5,12 @@
 #include <linux/bits.h>
 #include <linux/err.h>
 #include <linux/errno.h>
+#include <linux/of.h>
 #include <linux/types.h>
 
 struct device;
 struct device_node;
+struct fwnode_handle;
 struct reset_control;
 
 /**
@@ -84,7 +86,7 @@ int reset_control_bulk_deassert(int num_rstcs, struct reset_control_bulk_data *r
 int reset_control_bulk_acquire(int num_rstcs, struct reset_control_bulk_data *rstcs);
 void reset_control_bulk_release(int num_rstcs, struct reset_control_bulk_data *rstcs);
 
-struct reset_control *__of_reset_control_get(struct device_node *node,
+struct reset_control *__fwnode_reset_control_get(struct fwnode_handle *fwnode,
 				     const char *id, int index, enum reset_control_flags flags);
 struct reset_control *__reset_control_get(struct device *dev, const char *id,
 					  int index, enum reset_control_flags flags);
@@ -103,7 +105,8 @@ int __devm_reset_control_bulk_get(struct device *dev, int num_rstcs,
 
 struct reset_control *devm_reset_control_array_get(struct device *dev,
 						   enum reset_control_flags flags);
-struct reset_control *of_reset_control_array_get(struct device_node *np, enum reset_control_flags);
+struct reset_control *fwnode_reset_control_array_get(struct fwnode_handle *fwnode,
+						     enum reset_control_flags);
 
 int reset_control_get_count(struct device *dev);
 
@@ -152,8 +155,8 @@ static inline int __device_reset(struct device *dev, bool optional)
 	return optional ? 0 : -ENOTSUPP;
 }
 
-static inline struct reset_control *__of_reset_control_get(
-					struct device_node *node,
+static inline struct reset_control *__fwnode_reset_control_get(
+					struct fwnode_handle *fwnode,
 					const char *id, int index, enum reset_control_flags flags)
 {
 	bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
@@ -242,7 +245,7 @@ devm_reset_control_array_get(struct device *dev, enum reset_control_flags flags)
 }
 
 static inline struct reset_control *
-of_reset_control_array_get(struct device_node *np, enum reset_control_flags flags)
+fwnode_reset_control_array_get(struct fwnode_handle *fwnode, enum reset_control_flags flags)
 {
 	bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
 
@@ -500,7 +503,8 @@ reset_control_bulk_get_optional_shared(struct device *dev, int num_rstcs,
 static inline struct reset_control *of_reset_control_get_exclusive(
 				struct device_node *node, const char *id)
 {
-	return __of_reset_control_get(node, id, 0, RESET_CONTROL_EXCLUSIVE);
+	return __fwnode_reset_control_get(of_fwnode_handle(node), id, 0,
+					  RESET_CONTROL_EXCLUSIVE);
 }
 
 /**
@@ -520,7 +524,8 @@ static inline struct reset_control *of_reset_control_get_exclusive(
 static inline struct reset_control *of_reset_control_get_optional_exclusive(
 				struct device_node *node, const char *id)
 {
-	return __of_reset_control_get(node, id, 0, RESET_CONTROL_OPTIONAL_EXCLUSIVE);
+	return __fwnode_reset_control_get(of_fwnode_handle(node), id, 0,
+					  RESET_CONTROL_OPTIONAL_EXCLUSIVE);
 }
 
 /**
@@ -545,7 +550,8 @@ static inline struct reset_control *of_reset_control_get_optional_exclusive(
 static inline struct reset_control *of_reset_control_get_shared(
 				struct device_node *node, const char *id)
 {
-	return __of_reset_control_get(node, id, 0, RESET_CONTROL_SHARED);
+	return __fwnode_reset_control_get(of_fwnode_handle(node), id, 0,
+					  RESET_CONTROL_SHARED);
 }
 
 /**
@@ -562,7 +568,8 @@ static inline struct reset_control *of_reset_control_get_shared(
 static inline struct reset_control *of_reset_control_get_exclusive_by_index(
 					struct device_node *node, int index)
 {
-	return __of_reset_control_get(node, NULL, index, RESET_CONTROL_EXCLUSIVE);
+	return __fwnode_reset_control_get(of_fwnode_handle(node), NULL, index,
+					  RESET_CONTROL_EXCLUSIVE);
 }
 
 /**
@@ -590,7 +597,8 @@ static inline struct reset_control *of_reset_control_get_exclusive_by_index(
 static inline struct reset_control *of_reset_control_get_shared_by_index(
 					struct device_node *node, int index)
 {
-	return __of_reset_control_get(node, NULL, index, RESET_CONTROL_SHARED);
+	return __fwnode_reset_control_get(of_fwnode_handle(node), NULL, index,
+					  RESET_CONTROL_SHARED);
 }
 
 /**
@@ -1032,30 +1040,35 @@ devm_reset_control_array_get_optional_shared(struct device *dev)
 static inline struct reset_control *
 of_reset_control_array_get_exclusive(struct device_node *node)
 {
-	return of_reset_control_array_get(node, RESET_CONTROL_EXCLUSIVE);
+	return fwnode_reset_control_array_get(of_fwnode_handle(node),
+					      RESET_CONTROL_EXCLUSIVE);
 }
 
 static inline struct reset_control *
 of_reset_control_array_get_exclusive_released(struct device_node *node)
 {
-	return of_reset_control_array_get(node, RESET_CONTROL_EXCLUSIVE_RELEASED);
+	return fwnode_reset_control_array_get(of_fwnode_handle(node),
+					      RESET_CONTROL_EXCLUSIVE_RELEASED);
 }
 
 static inline struct reset_control *
 of_reset_control_array_get_shared(struct device_node *node)
 {
-	return of_reset_control_array_get(node, RESET_CONTROL_SHARED);
+	return fwnode_reset_control_array_get(of_fwnode_handle(node),
+					      RESET_CONTROL_SHARED);
 }
 
 static inline struct reset_control *
 of_reset_control_array_get_optional_exclusive(struct device_node *node)
 {
-	return of_reset_control_array_get(node, RESET_CONTROL_OPTIONAL_EXCLUSIVE);
+	return fwnode_reset_control_array_get(of_fwnode_handle(node),
+					      RESET_CONTROL_OPTIONAL_EXCLUSIVE);
 }
 
 static inline struct reset_control *
 of_reset_control_array_get_optional_shared(struct device_node *node)
 {
-	return of_reset_control_array_get(node, RESET_CONTROL_OPTIONAL_SHARED);
+	return fwnode_reset_control_array_get(of_fwnode_handle(node),
+					      RESET_CONTROL_OPTIONAL_SHARED);
 }
 #endif

-- 
2.47.3


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

* [PATCH 14/15] reset: convert reset core to using firmware nodes
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (12 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 13/15] reset: convert the core API " Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  2026-01-05 14:15 ` [PATCH 15/15] reset: gpio: make the driver fwnode-agnostic Bartosz Golaszewski
  14 siblings, 0 replies; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

With everything else now in place, we can convert the remaining parts of
the reset subsystem to becoming fwnode-agnostic - meaning it will work
with all kinds of firmware nodes, not only devicetree.

To that end: extend struct reset_controller_dev with fields taking
information relevant for using firmware nodes (which mirrors what we
already do for OF-nodes) and limit using of_ APIs only to where it's
absolutely necessary (mostly around the of_xlate callback).

For backward compatibility of existing drivers we still support OF-nodes
but firmware nodes become the preferred method.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/core.c             | 150 +++++++++++++++++++++++----------------
 include/linux/reset-controller.h |  14 +++-
 2 files changed, 101 insertions(+), 63 deletions(-)

diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 11c5bd0fe2b23e76db0b4739c5e34e478d83608b..310434ba5caae716a21e4f8ea7062dd201fb1f2c 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -81,13 +81,13 @@ struct reset_control_array {
 
 /**
  * struct reset_gpio_lookup - lookup key for ad-hoc created reset-gpio devices
- * @of_args: phandle to the reset controller with all the args like GPIO number
+ * @ref_args: Reference to the reset controller with all the args like GPIO number
  * @swnode: Software node containing the reference to the GPIO provider
  * @list: list entry for the reset_gpio_lookup_list
  * @adev: Auxiliary device representing the reset controller
  */
 struct reset_gpio_lookup {
-	struct of_phandle_args of_args;
+	struct fwnode_reference_args ref_args;
 	struct fwnode_handle *swnode;
 	struct list_head list;
 	struct auxiliary_device adev;
@@ -98,24 +98,24 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev)
 	if (rcdev->dev)
 		return dev_name(rcdev->dev);
 
-	if (rcdev->of_node)
-		return rcdev->of_node->full_name;
+	if (rcdev->fwnode)
+		return fwnode_get_name(rcdev->fwnode);
 
 	return NULL;
 }
 
 /**
- * of_reset_simple_xlate - translate reset_spec to the reset line number
+ * fwnode_reset_simple_xlate - translate reset_spec to the reset line number
  * @rcdev: a pointer to the reset controller device
- * @reset_spec: reset line specifier as found in the device tree
+ * @reset_spec: reset line specifier as found in firmware
  *
- * This static translation function is used by default if of_xlate in
+ * This static translation function is used by default if fwnode_xlate in
  * :c:type:`reset_controller_dev` is not set. It is useful for all reset
  * controllers with 1:1 mapping, where reset lines can be indexed by number
  * without gaps.
  */
-static int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
-				 const struct of_phandle_args *reset_spec)
+static int fwnode_reset_simple_xlate(struct reset_controller_dev *rcdev,
+				     const struct fwnode_reference_args *reset_spec)
 {
 	if (reset_spec->args[0] >= rcdev->nr_resets)
 		return -EINVAL;
@@ -129,9 +129,17 @@ static int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
  */
 int reset_controller_register(struct reset_controller_dev *rcdev)
 {
-	if (!rcdev->of_xlate) {
-		rcdev->of_reset_n_cells = 1;
-		rcdev->of_xlate = of_reset_simple_xlate;
+	if ((rcdev->of_node && rcdev->fwnode) || (rcdev->of_xlate && rcdev->fwnode_xlate))
+		return -EINVAL;
+
+	if (rcdev->of_node) {
+		rcdev->fwnode = of_fwnode_handle(rcdev->of_node);
+		rcdev->fwnode_reset_n_cells = rcdev->of_reset_n_cells;
+	}
+
+	if (rcdev->fwnode && !rcdev->fwnode_xlate) {
+		rcdev->fwnode_reset_n_cells = 1;
+		rcdev->fwnode_xlate = fwnode_reset_simple_xlate;
 	}
 
 	INIT_LIST_HEAD(&rcdev->reset_control_head);
@@ -917,7 +925,7 @@ static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev,
 	adev->id = id;
 	adev->name = "gpio";
 	adev->dev.parent = parent;
-	adev->dev.platform_data = &rgpio_dev->of_args;
+	adev->dev.platform_data = &rgpio_dev->ref_args;
 	adev->dev.release = reset_gpio_aux_device_release;
 	device_set_node(&adev->dev, rgpio_dev->swnode);
 
@@ -937,18 +945,18 @@ static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev,
 	return 0;
 }
 
-static void reset_gpio_add_devlink(struct device_node *np,
+static void reset_gpio_add_devlink(struct fwnode_handle *fwnode,
 				   struct reset_gpio_lookup *rgpio_dev)
 {
 	struct device *consumer;
 
 	/*
-	 * We must use get_dev_from_fwnode() and not of_find_device_by_node()
+	 * We must use get_dev_from_fwnode() and not ref_find_device_by_node()
 	 * because the latter only considers the platform bus while we want to
 	 * get consumers of any kind that can be associated with firmware
 	 * nodes: auxiliary, soundwire, etc.
 	 */
-	consumer = get_dev_from_fwnode(of_fwnode_handle(np));
+	consumer = get_dev_from_fwnode(fwnode);
 	if (consumer) {
 		if (!device_link_add(consumer, &rgpio_dev->adev.dev,
 				     DL_FLAG_AUTOREMOVE_CONSUMER))
@@ -968,15 +976,23 @@ static void reset_gpio_add_devlink(struct device_node *np,
 	 */
 }
 
+/* TODO: move it out into drivers/base/ */
+static bool fwnode_reference_args_equal(const struct fwnode_reference_args *left,
+					const struct fwnode_reference_args *right)
+{
+	return left->fwnode == right->fwnode && left->nargs == right->nargs &&
+	       !memcmp(left->args, right->args, sizeof(left->args[0]) * left->nargs);
+}
+
 /*
  * @np: OF-node associated with the consumer
- * @args: phandle to the GPIO provider with all the args like GPIO number
+ * @args: Reference to the GPIO provider with all the args like GPIO number
  */
-static int __reset_add_reset_gpio_device(struct device_node *np,
-					 const struct of_phandle_args *args)
+static int __reset_add_reset_gpio_device(struct fwnode_handle *fwnode,
+					 const struct fwnode_reference_args *args)
 {
 	struct property_entry properties[3] = { };
-	unsigned int offset, of_flags, lflags;
+	unsigned int offset, flags, lflags;
 	struct reset_gpio_lookup *rgpio_dev;
 	struct device *parent;
 	int ret, prop = 0;
@@ -987,7 +1003,7 @@ static int __reset_add_reset_gpio_device(struct device_node *np,
 	 * args[1]: GPIO flags
 	 * TODO: Handle other cases.
 	 */
-	if (args->args_count != 2)
+	if (args->nargs != 2)
 		return -ENOENT;
 
 	/*
@@ -998,7 +1014,7 @@ static int __reset_add_reset_gpio_device(struct device_node *np,
 	lockdep_assert_not_held(&reset_list_mutex);
 
 	offset = args->args[0];
-	of_flags = args->args[1];
+	flags = args->args[1];
 
 	/*
 	 * Later we map GPIO flags between OF and Linux, however not all
@@ -1008,33 +1024,31 @@ static int __reset_add_reset_gpio_device(struct device_node *np,
 	 * FIXME: Find a better way of translating OF flags to GPIO lookup
 	 * flags.
 	 */
-	if (of_flags > GPIO_ACTIVE_LOW) {
+	if (flags > GPIO_ACTIVE_LOW) {
 		pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n",
-		       of_flags, offset);
+		       flags, offset);
 		return -EINVAL;
 	}
 
 	struct gpio_device *gdev __free(gpio_device_put) =
-		gpio_device_find_by_fwnode(of_fwnode_handle(args->np));
+			gpio_device_find_by_fwnode(args->fwnode);
 	if (!gdev)
 		return -EPROBE_DEFER;
 
 	guard(mutex)(&reset_gpio_lookup_mutex);
 
 	list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) {
-		if (args->np == rgpio_dev->of_args.np) {
-			if (of_phandle_args_equal(args, &rgpio_dev->of_args)) {
-				/*
-				 * Already on the list, create the device link
-				 * and stop here.
-				 */
-				reset_gpio_add_devlink(np, rgpio_dev);
-				return 0;
-			}
+		if (fwnode_reference_args_equal(args, &rgpio_dev->ref_args)) {
+			/*
+			 * Already on the list, create the device link
+			 * and stop here.
+			 */
+			reset_gpio_add_devlink(fwnode, rgpio_dev);
+			return 0;
 		}
 	}
 
-	lflags = GPIO_PERSISTENT | (of_flags & GPIO_ACTIVE_LOW);
+	lflags = GPIO_PERSISTENT | (flags & GPIO_ACTIVE_LOW);
 	parent = gpio_device_to_device(gdev);
 	properties[prop++] = PROPERTY_ENTRY_STRING("compatible", "reset-gpio");
 	properties[prop++] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags);
@@ -1044,43 +1058,43 @@ static int __reset_add_reset_gpio_device(struct device_node *np,
 	if (!rgpio_dev)
 		return -ENOMEM;
 
-	rgpio_dev->of_args = *args;
+	rgpio_dev->ref_args = *args;
 	/*
-	 * We keep the device_node reference, but of_args.np is put at the end
-	 * of __fwnode_reset_control_get(), so get it one more time.
+	 * We keep the fwnode_handle reference, but ref_args.fwnode is put at
+	 * the end of __fwnode_reset_control_get(), so get it one more time.
 	 * Hold reference as long as rgpio_dev memory is valid.
 	 */
-	of_node_get(rgpio_dev->of_args.np);
+	fwnode_handle_get(rgpio_dev->ref_args.fwnode);
 
 	rgpio_dev->swnode = fwnode_create_software_node(properties, NULL);
 	if (IS_ERR(rgpio_dev->swnode)) {
 		ret = PTR_ERR(rgpio_dev->swnode);
-		goto err_put_of_node;
+		goto err_put_fwnode;
 	}
 
 	ret = reset_create_gpio_aux_device(rgpio_dev, parent);
 	if (ret)
 		goto err_del_swnode;
 
-	reset_gpio_add_devlink(np, rgpio_dev);
+	reset_gpio_add_devlink(fwnode, rgpio_dev);
 	list_add(&rgpio_dev->list, &reset_gpio_lookup_list);
 
 	return 0;
 
 err_del_swnode:
 	fwnode_remove_software_node(rgpio_dev->swnode);
-err_put_of_node:
-	of_node_put(rgpio_dev->of_args.np);
+err_put_fwnode:
+	fwnode_handle_put(rgpio_dev->ref_args.fwnode);
 	kfree(rgpio_dev);
 
 	return ret;
 }
 
-static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_args *args,
-						       bool gpio_fallback)
+static struct reset_controller_dev *
+__reset_find_rcdev(const struct fwnode_reference_args *args, bool gpio_fallback)
 {
 	struct reset_controller_dev *rcdev;
-	struct of_phandle_args *rc_args;
+	struct fwnode_reference_args *rc_args;
 
 	lockdep_assert_held(&reset_list_mutex);
 
@@ -1088,10 +1102,10 @@ static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_a
 		if (gpio_fallback && device_is_compatible(rcdev->dev, "reset-gpio")) {
 			rc_args = dev_get_platdata(rcdev->dev);
 
-			if (of_phandle_args_equal(args, rc_args))
+			if (fwnode_reference_args_equal(args, rc_args))
 				return rcdev;
 		} else {
-			if (args->np == rcdev->of_node)
+			if (args->fwnode == rcdev->fwnode)
 				return rcdev;
 		}
 	}
@@ -1105,27 +1119,26 @@ __fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int ind
 {
 	bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
 	bool gpio_fallback = false;
-	struct device_node *node = to_of_node(fwnode);
 	struct reset_control *rstc = ERR_PTR(-EINVAL);
 	struct reset_controller_dev *rcdev;
-	struct of_phandle_args args;
-	int rstc_id;
+	struct fwnode_reference_args args;
+	struct of_phandle_args of_args;
+	int rstc_id = -EINVAL;
 	int ret;
 
 	if (!fwnode)
 		return ERR_PTR(-EINVAL);
 
 	if (id) {
-		index = of_property_match_string(node,
-						 "reset-names", id);
+		index = fwnode_property_match_string(fwnode, "reset-names", id);
 		if (index == -EILSEQ)
 			return ERR_PTR(index);
 		if (index < 0)
 			return optional ? NULL : ERR_PTR(-ENOENT);
 	}
 
-	ret = of_parse_phandle_with_args(node, "resets", "#reset-cells",
-					 index, &args);
+	ret = fwnode_property_get_reference_args(fwnode, "resets", "#reset-cells",
+						 0, index, &args);
 	if (ret == -EINVAL)
 		return ERR_PTR(ret);
 	if (ret) {
@@ -1136,14 +1149,14 @@ __fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int ind
 		 * There can be only one reset-gpio for regular devices, so
 		 * don't bother with the "reset-gpios" phandle index.
 		 */
-		ret = of_parse_phandle_with_args(node, "reset-gpios", "#gpio-cells",
-						 0, &args);
+		ret = fwnode_property_get_reference_args(fwnode, "reset-gpios",
+							 "#gpio-cells", 0, 0, &args);
 		if (ret)
 			return optional ? NULL : ERR_PTR(ret);
 
 		gpio_fallback = true;
 
-		ret = __reset_add_reset_gpio_device(node, &args);
+		ret = __reset_add_reset_gpio_device(fwnode, &args);
 		if (ret) {
 			rstc = ERR_PTR(ret);
 			goto out_put;
@@ -1158,12 +1171,27 @@ __fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int ind
 		goto out_put;
 	}
 
-	if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) {
+	if (WARN_ON(args.nargs != rcdev->fwnode_reset_n_cells)) {
 		rstc = ERR_PTR(-EINVAL);
 		goto out_put;
 	}
 
-	rstc_id = rcdev->of_xlate(rcdev, &args);
+	if (rcdev->of_xlate && is_of_node(fwnode)) {
+		ret = of_parse_phandle_with_args(to_of_node(fwnode),
+						 gpio_fallback ? "reset-gpios" : "resets",
+						 gpio_fallback ? "#gpio-cells" : "#reset-cells",
+						 gpio_fallback ? 0 : index,
+						 &of_args);
+		if (ret) {
+			rstc = ERR_PTR(ret);
+			goto out_put;
+		}
+
+		rstc_id = rcdev->of_xlate(rcdev, &of_args);
+		of_node_put(of_args.np);
+	} else if (rcdev->fwnode_xlate) {
+		rstc_id = rcdev->fwnode_xlate(rcdev, &args);
+	}
 	if (rstc_id < 0) {
 		rstc = ERR_PTR(rstc_id);
 		goto out_put;
@@ -1175,7 +1203,7 @@ __fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int ind
 		rstc = __reset_control_get_internal(rcdev, rstc_id, flags);
 
 out_put:
-	of_node_put(args.np);
+	fwnode_handle_put(args.fwnode);
 
 	return rstc;
 }
diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h
index 2fecb4edeba1a2b1f5f5db2b34c867362c5f9c18..2f7a1217d0ec6dba4f8831fb8c66308188f7a339 100644
--- a/include/linux/reset-controller.h
+++ b/include/linux/reset-controller.h
@@ -5,6 +5,8 @@
 #include <linux/list.h>
 #include <linux/mutex.h>
 
+struct fwnode_handle;
+struct fwnode_reference_args;
 struct reset_controller_dev;
 
 /**
@@ -38,8 +40,12 @@ struct of_phandle_args;
  * @of_node: corresponding device tree node as phandle target
  * @of_reset_n_cells: number of cells in reset line specifiers
  * @of_xlate: translation function to translate from specifier as found in the
- *            device tree to id as given to the reset control ops, defaults
- *            to :c:func:`of_reset_simple_xlate`.
+ *            device tree to id as given to the reset control ops
+ * @fwnode: firmware node associated with this device
+ * @fwnode_reset_n_cells: number of cells in reset line specifiers
+ * @fwnode_xlate: translation function to translate from firmware specifier to
+ *                id as given to the reset control ops, defaults to
+ *                :c:func:`fwnode_reset_simple_xlate`
  * @nr_resets: number of reset controls in this reset controller device
  * @lock: synchronizes concurrent access to the structure's fields
  */
@@ -53,6 +59,10 @@ struct reset_controller_dev {
 	int of_reset_n_cells;
 	int (*of_xlate)(struct reset_controller_dev *rcdev,
 			const struct of_phandle_args *reset_spec);
+	struct fwnode_handle *fwnode;
+	int fwnode_reset_n_cells;
+	int (*fwnode_xlate)(struct reset_controller_dev *rcdev,
+			    const struct fwnode_reference_args *reset_spec);
 	unsigned int nr_resets;
 	struct mutex lock;
 };

-- 
2.47.3


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

* [PATCH 15/15] reset: gpio: make the driver fwnode-agnostic
  2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
                   ` (13 preceding siblings ...)
  2026-01-05 14:15 ` [PATCH 14/15] reset: convert reset core " Bartosz Golaszewski
@ 2026-01-05 14:15 ` Bartosz Golaszewski
  14 siblings, 0 replies; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-05 14:15 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Philipp Zabel; +Cc: linux-kernel, Bartosz Golaszewski

With reset core now being able to work with firmware nodes, we can make
reset-gpio node-agnostic and drop any OF dependencies.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/reset-gpio.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c
index 6053a458b259f3876a14358cb4797bee8a2ee4de..b4cec1baaffaca20f57a0518fd1af100585c9dca 100644
--- a/drivers/reset/reset-gpio.c
+++ b/drivers/reset/reset-gpio.c
@@ -2,9 +2,9 @@
 
 #include <linux/auxiliary_bus.h>
 #include <linux/gpio/consumer.h>
+#include <linux/fwnode.h>
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
-#include <linux/of.h>
 #include <linux/reset-controller.h>
 
 struct reset_gpio_priv {
@@ -50,8 +50,8 @@ static const struct reset_control_ops reset_gpio_ops = {
 	.status = reset_gpio_status,
 };
 
-static int reset_gpio_of_xlate(struct reset_controller_dev *rcdev,
-			       const struct of_phandle_args *reset_spec)
+static int reset_gpio_fwnode_xlate(struct reset_controller_dev *rcdev,
+				   const struct fwnode_reference_args *reset_spec)
 {
 	return reset_spec->args[0];
 }
@@ -76,8 +76,8 @@ static int reset_gpio_probe(struct auxiliary_device *adev,
 	priv->rc.dev = dev;
 
 	/* Cells to match GPIO specifier, but it's not really used */
-	priv->rc.of_reset_n_cells = 2;
-	priv->rc.of_xlate = reset_gpio_of_xlate;
+	priv->rc.fwnode_reset_n_cells = 2;
+	priv->rc.fwnode_xlate = reset_gpio_fwnode_xlate;
 	priv->rc.nr_resets = 1;
 
 	return devm_reset_controller_register(dev, &priv->rc);

-- 
2.47.3


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

* Re: [PATCH 01/15] reset: gpio: remove unneeded OF-node put
  2026-01-05 14:15 ` [PATCH 01/15] reset: gpio: remove unneeded OF-node put Bartosz Golaszewski
@ 2026-01-08  9:02   ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2026-01-08  9:02 UTC (permalink / raw)
  To: Bartosz Golaszewski, Krzysztof Kozlowski; +Cc: linux-kernel

On Mo, 2026-01-05 at 15:15 +0100, Bartosz Golaszewski wrote:
> Tasking the reset-gpio driver with controlling the reference count of an
> OF node set up in reset core is a weird inversion of responsability.

priv->rc.of_node isn't set anywhere.
The device node set and used by the core is priv->rc.of_args->np.
Could you change the commit message to reflect this?
Otherwise,

Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>

for the patch content. 


regards
Philipp

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

* Re: [PATCH 02/15] reset: gpio: add a devlink between reset-gpio and its consumer
  2026-01-05 14:15 ` [PATCH 02/15] reset: gpio: add a devlink between reset-gpio and its consumer Bartosz Golaszewski
@ 2026-01-08  9:02   ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2026-01-08  9:02 UTC (permalink / raw)
  To: Bartosz Golaszewski, Krzysztof Kozlowski; +Cc: linux-kernel

On Mo, 2026-01-05 at 15:15 +0100, Bartosz Golaszewski wrote:
> The device that requests the reset control managed by the reset-gpio
> device is effectively its consumer but the devlink is only established
> between it and the GPIO controller exposing the reset pin. Add a devlink
> between the consumer of the reset control and its supplier. This will
> allow us to simplify the GPIOLIB code managing shared GPIOs when
> handling the corner case of reset-gpio and gpiolib-shared interacting.
> While at it and since we need to store the address of the auxiliary
> device: don't allocate memory for the device separately but fold it into
> struct reset_gpio_lookup instead.
> 
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> ---
>  drivers/reset/core.c | 80 ++++++++++++++++++++++++++++++++++++----------------
>  1 file changed, 56 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/reset/core.c b/drivers/reset/core.c
> index 0666dfc41ca9af6fbf1768f17157b4faf7849962..813b85cb2758e129c72c09267a387108d7ad683b 100644
> --- a/drivers/reset/core.c
> +++ b/drivers/reset/core.c
> @@ -20,6 +20,7 @@
>  #include <linux/kref.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
> +#include <linux/of_platform.h>
>  #include <linux/reset.h>
>  #include <linux/reset-controller.h>
>  #include <linux/slab.h>
> @@ -77,11 +78,13 @@ struct reset_control_array {
>   * @of_args: phandle to the reset controller with all the args like GPIO number
>   * @swnode: Software node containing the reference to the GPIO provider
>   * @list: list entry for the reset_gpio_lookup_list
> + * @adev: Auxiliary device representing the reset controller
>   */
>  struct reset_gpio_lookup {
>  	struct of_phandle_args of_args;
>  	struct fwnode_handle *swnode;
>  	struct list_head list;
> +	struct auxiliary_device adev;
>  };

Now this looks like it should be renamed to struct reset_gpio_device.

[...]
> +static void reset_gpio_add_devlink(struct device_node *np,
> +				   struct reset_gpio_lookup *rgpio_dev)
> +{
> +	struct device *consumer;
> +
> +	/*
> +	 * We must use get_dev_from_fwnode() and not of_find_device_by_node()
> +	 * because the latter only considers the platform bus while we want to
> +	 * get consumers of any kind that can be associated with firmware
> +	 * nodes: auxiliary, soundwire, etc.
> +	 */
> +	consumer = get_dev_from_fwnode(of_fwnode_handle(np));
> +	if (consumer) {
> +		if (!device_link_add(consumer, &rgpio_dev->adev.dev,
> +				     DL_FLAG_AUTOREMOVE_CONSUMER))
> +			pr_warn("Failed to create a device link between reset-gpio and its consumer");
> +
> +		put_device(consumer);
> +	}
> +	/*
> +	 * else { }
> +	 *
> +	 * TODO: If ever there's a case where we need to support shared
> +	 * reset-gpios retrieved from a device node for which there's no
> +	 * device present yet, this is where we'd set up a notifier waiting
> +	 * for the device to appear in the system. This would be a lot of code
> +	 * that would go unused for now so let's cross that bridge when and if
> +	 * we get there.
> +	 */
>  }

Nice, no more DL_FLAG_STATELESS.

I still think getting back the consumer device from the device_node is
a bit more convoluted than it needs to be and that the right place to
call device_link_add() from would be fwnode_reset_control_get().

I'm fine with this as an intermediate step, though.

Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>

regards
Philipp

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

* Re: [PATCH 03/15] reset: gpio: simplify fallback device matching
  2026-01-05 14:15 ` [PATCH 03/15] reset: gpio: simplify fallback device matching Bartosz Golaszewski
@ 2026-01-08  9:26   ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2026-01-08  9:26 UTC (permalink / raw)
  To: Bartosz Golaszewski, Krzysztof Kozlowski; +Cc: linux-kernel

On Mo, 2026-01-05 at 15:15 +0100, Bartosz Golaszewski wrote:
> The of_args field of struct reset_controller_dev was introduced to allow
> the reset-gpio driver to pass the phandle arguments back to reset core.
> The thing is: it doesn't even have to do it. The core sets the platform
> data of the auxiliary device *AND* has access to it later on during the
> lookup. This means the field is unneeded and all can happen entirely in
> reset core.
> 
> Remove the field from the public header and don't set it in
> reset-gpio.c. Retrieve the platform data in reset core when needed
> instead.
> 
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> ---
>  drivers/reset/core.c             | 14 +++++---------
>  drivers/reset/reset-gpio.c       |  5 -----
>  include/linux/reset-controller.h |  4 ----
>  3 files changed, 5 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/reset/core.c b/drivers/reset/core.c
> index 813b85cb2758e129c72c09267a387108d7ad683b..ec9e4c39e8debd93d59851cfe72fcf44a79ed7e1 100644
> --- a/drivers/reset/core.c
> +++ b/drivers/reset/core.c
> @@ -95,9 +95,6 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev)
>  	if (rcdev->of_node)
>  		return rcdev->of_node->full_name;
>  
> -	if (rcdev->of_args)
> -		return rcdev->of_args->np->full_name;
> -
>  	return NULL;
>  }
>  
> @@ -126,9 +123,6 @@ static int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
>   */
>  int reset_controller_register(struct reset_controller_dev *rcdev)
>  {
> -	if (rcdev->of_node && rcdev->of_args)
> -		return -EINVAL;
> -
>  	if (!rcdev->of_xlate) {
>  		rcdev->of_reset_n_cells = 1;
>  		rcdev->of_xlate = of_reset_simple_xlate;
> @@ -1007,13 +1001,15 @@ static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_a
>  						       bool gpio_fallback)
>  {
>  	struct reset_controller_dev *rcdev;
> +	struct of_phandle_args *rc_args;
>  
>  	lockdep_assert_held(&reset_list_mutex);
>  
>  	list_for_each_entry(rcdev, &reset_controller_list, list) {
> -		if (gpio_fallback) {
> -			if (rcdev->of_args && of_phandle_args_equal(args,
> -								    rcdev->of_args))
> +		if (gpio_fallback && device_is_compatible(rcdev->dev, "reset-gpio")) {

This should check if rcdev->dev is set, otherwise
device_is_compatible() may try to dereference a NULL pointer in
dev_fwnode().

		if (gpio_fallback && rcdev->dev &&
		    device_is_compatible(rcdev->dev, "reset-gpio")) {


regards
Philipp

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

* Re: [PATCH 04/15] reset: gpio: remove unneeded auxiliary_set_drvdata()
  2026-01-05 14:15 ` [PATCH 04/15] reset: gpio: remove unneeded auxiliary_set_drvdata() Bartosz Golaszewski
@ 2026-01-08  9:27   ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2026-01-08  9:27 UTC (permalink / raw)
  To: Bartosz Golaszewski, Krzysztof Kozlowski; +Cc: linux-kernel

On Mo, 2026-01-05 at 15:15 +0100, Bartosz Golaszewski wrote:
> There's no user of the driver data so don't needlessly assign it.
> 
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>

regards
Philipp

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

* Re: [PATCH 07/15] reset: use lock guards in reset core
  2026-01-05 14:15 ` [PATCH 07/15] reset: use lock guards in reset core Bartosz Golaszewski
@ 2026-01-08  9:39   ` Philipp Zabel
  0 siblings, 0 replies; 23+ messages in thread
From: Philipp Zabel @ 2026-01-08  9:39 UTC (permalink / raw)
  To: Bartosz Golaszewski, Krzysztof Kozlowski; +Cc: linux-kernel

On Mo, 2026-01-05 at 15:15 +0100, Bartosz Golaszewski wrote:
> Simplify the locking code in reset core by using lock guard from
> linux/cleanup.h.
> 
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> ---
>  drivers/reset/core.c | 51 +++++++++++++++++++++++----------------------------
>  1 file changed, 23 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/reset/core.c b/drivers/reset/core.c
> index 39bedf990223212998fa8ed4cda517529f94ee23..ffb811cbc6460b48dd833d3560d6538eea2fd5c0 100644
> --- a/drivers/reset/core.c
> +++ b/drivers/reset/core.c
> @@ -1069,22 +1064,23 @@ __of_reset_control_get(struct device_node *node, const char *id, int index,
>  		}
>  	}
>  
> -	mutex_lock(&reset_list_mutex);
> +	guard(mutex)(&reset_list_mutex);
> +

This breaks the __reset_add_reset_gpio_device() error path,
which jumps to the of_put: label without reset_list_mutex locked.
See [1], where I made the same mistake:

[1] https://lore.kernel.org/all/20240928222702.GX3550746@ZenIV/

regards
Philipp

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

* Re: [PATCH 08/15] reset: remove unneeded include
  2026-01-05 14:15 ` [PATCH 08/15] reset: remove unneeded include Bartosz Golaszewski
@ 2026-01-08  9:44   ` Philipp Zabel
  2026-01-08 14:13     ` Bartosz Golaszewski
  0 siblings, 1 reply; 23+ messages in thread
From: Philipp Zabel @ 2026-01-08  9:44 UTC (permalink / raw)
  To: Bartosz Golaszewski, Krzysztof Kozlowski; +Cc: linux-kernel

On Mo, 2026-01-05 at 15:15 +0100, Bartosz Golaszewski wrote:
> Reset core does not use any symbols from linux/of_platform.h so there's
> no need to include it.
> 
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> ---
>  drivers/reset/core.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/reset/core.c b/drivers/reset/core.c
> index ffb811cbc6460b48dd833d3560d6538eea2fd5c0..48eb64654b6de08030674ce3b994021b5f57060e 100644
> --- a/drivers/reset/core.c
> +++ b/drivers/reset/core.c
> @@ -20,7 +20,6 @@
>  #include <linux/kref.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
> -#include <linux/of_platform.h>

You added this in patch 2. Can this be squashed into patch 2? Otherwise
squash it into whichever patch makes that addition unnecessary, please.

regards
Philipp

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

* Re: [PATCH 08/15] reset: remove unneeded include
  2026-01-08  9:44   ` Philipp Zabel
@ 2026-01-08 14:13     ` Bartosz Golaszewski
  0 siblings, 0 replies; 23+ messages in thread
From: Bartosz Golaszewski @ 2026-01-08 14:13 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: Bartosz Golaszewski, Krzysztof Kozlowski, linux-kernel

On Thu, Jan 8, 2026 at 2:59 PM Philipp Zabel <p.zabel@pengutronix.de> wrote:
>
> On Mo, 2026-01-05 at 15:15 +0100, Bartosz Golaszewski wrote:
> > Reset core does not use any symbols from linux/of_platform.h so there's
> > no need to include it.
> >
> > Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> > ---
> >  drivers/reset/core.c | 1 -
> >  1 file changed, 1 deletion(-)
> >
> > diff --git a/drivers/reset/core.c b/drivers/reset/core.c
> > index ffb811cbc6460b48dd833d3560d6538eea2fd5c0..48eb64654b6de08030674ce3b994021b5f57060e 100644
> > --- a/drivers/reset/core.c
> > +++ b/drivers/reset/core.c
> > @@ -20,7 +20,6 @@
> >  #include <linux/kref.h>
> >  #include <linux/module.h>
> >  #include <linux/of.h>
> > -#include <linux/of_platform.h>
>
> You added this in patch 2. Can this be squashed into patch 2? Otherwise
> squash it into whichever patch makes that addition unnecessary, please.
>

Gah! Should have git blamed it before complaining. :) Thanks for the catch.

Bartosz

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

end of thread, other threads:[~2026-01-08 14:14 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-05 14:15 [PATCH 00/15] reset: major reset core refactoring Bartosz Golaszewski
2026-01-05 14:15 ` [PATCH 01/15] reset: gpio: remove unneeded OF-node put Bartosz Golaszewski
2026-01-08  9:02   ` Philipp Zabel
2026-01-05 14:15 ` [PATCH 02/15] reset: gpio: add a devlink between reset-gpio and its consumer Bartosz Golaszewski
2026-01-08  9:02   ` Philipp Zabel
2026-01-05 14:15 ` [PATCH 03/15] reset: gpio: simplify fallback device matching Bartosz Golaszewski
2026-01-08  9:26   ` Philipp Zabel
2026-01-05 14:15 ` [PATCH 04/15] reset: gpio: remove unneeded auxiliary_set_drvdata() Bartosz Golaszewski
2026-01-08  9:27   ` Philipp Zabel
2026-01-05 14:15 ` [PATCH 05/15] reset: warn on reset-gpio release Bartosz Golaszewski
2026-01-05 14:15 ` [PATCH 06/15] reset: fold ida_alloc() into reset_create_gpio_aux_device() Bartosz Golaszewski
2026-01-05 14:15 ` [PATCH 07/15] reset: use lock guards in reset core Bartosz Golaszewski
2026-01-08  9:39   ` Philipp Zabel
2026-01-05 14:15 ` [PATCH 08/15] reset: remove unneeded include Bartosz Golaszewski
2026-01-08  9:44   ` Philipp Zabel
2026-01-08 14:13     ` Bartosz Golaszewski
2026-01-05 14:15 ` [PATCH 09/15] reset: handle removing supplier before consumers Bartosz Golaszewski
2026-01-05 14:15 ` [PATCH 10/15] reset: protect struct reset_controller_dev with its own mutex Bartosz Golaszewski
2026-01-05 14:15 ` [PATCH 11/15] reset: protect struct reset_control " Bartosz Golaszewski
2026-01-05 14:15 ` [PATCH 12/15] reset: convert of_reset_control_get_count() to using firmware nodes Bartosz Golaszewski
2026-01-05 14:15 ` [PATCH 13/15] reset: convert the core API " Bartosz Golaszewski
2026-01-05 14:15 ` [PATCH 14/15] reset: convert reset core " Bartosz Golaszewski
2026-01-05 14:15 ` [PATCH 15/15] reset: gpio: make the driver fwnode-agnostic Bartosz Golaszewski

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®