diff mbox series

[v6,02/12] of: base: Add for_each_child_of_node_with_prefix_scoped()

Message ID 20240904090016.2841572-3-wenst@chromium.org
State Changes Requested
Headers show
Series platform/chrome: Introduce DT hardware prober | expand

Checks

Context Check Description
robh/checkpatch success
robh/patch-applied fail build log

Commit Message

Chen-Yu Tsai Sept. 4, 2024, 9 a.m. UTC
There are cases where drivers would go through child device nodes and
operate on only the ones whose node name starts with a given prefix.

Provide a helper for these users. This will mainly be used in a
subsequent patch that implements a hardware component prober for I2C
busses.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
---
Changes since v5:
- New patch
---
 drivers/of/base.c  | 35 +++++++++++++++++++++++++++++++++++
 include/linux/of.h |  9 +++++++++
 2 files changed, 44 insertions(+)

Comments

Andy Shevchenko Sept. 4, 2024, 1:03 p.m. UTC | #1
On Wed, Sep 04, 2024 at 05:00:04PM +0800, Chen-Yu Tsai wrote:
> There are cases where drivers would go through child device nodes and
> operate on only the ones whose node name starts with a given prefix.
> 
> Provide a helper for these users. This will mainly be used in a
> subsequent patch that implements a hardware component prober for I2C
> busses.

...

> +#define for_each_child_of_node_with_prefix_scoped(parent, child, prefix) \
> +	for (struct device_node *child __free(device_node) =		\
> +	     of_get_next_child_with_prefix(parent, NULL, prefix);	\
> +	     child != NULL;						\
> +	     child = of_get_next_child_with_prefix(parent, child, prefix))

I'm wondering if we may drop _scoped from day 1. Yeah, probably a bit confusing
as the rest of APIs without that suffix do require reference count handling on
the loop abrupt.
Rob Herring (Arm) Sept. 4, 2024, 1:43 p.m. UTC | #2
On Wed, Sep 04, 2024 at 04:03:47PM +0300, Andy Shevchenko wrote:
> On Wed, Sep 04, 2024 at 05:00:04PM +0800, Chen-Yu Tsai wrote:
> > There are cases where drivers would go through child device nodes and
> > operate on only the ones whose node name starts with a given prefix.
> > 
> > Provide a helper for these users. This will mainly be used in a
> > subsequent patch that implements a hardware component prober for I2C
> > busses.
> 
> ...
> 
> > +#define for_each_child_of_node_with_prefix_scoped(parent, child, prefix) \
> > +	for (struct device_node *child __free(device_node) =		\
> > +	     of_get_next_child_with_prefix(parent, NULL, prefix);	\
> > +	     child != NULL;						\
> > +	     child = of_get_next_child_with_prefix(parent, child, prefix))
> 
> I'm wondering if we may drop _scoped from day 1. Yeah, probably a bit confusing
> as the rest of APIs without that suffix do require reference count handling on
> the loop abrupt.

Yes, please drop. We have other new ones coming and they don't have 
"_scoped". I was on the fence, but if you use a scoped one like a 
non-scoped one (declaring child outside or using it outside the loop) 
you will get a compiler error.

Rob
diff mbox series

Patch

diff --git a/drivers/of/base.c b/drivers/of/base.c
index 20603d3c9931..d3c123b3261a 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -628,6 +628,41 @@  struct device_node *of_get_next_child(const struct device_node *node,
 }
 EXPORT_SYMBOL(of_get_next_child);
 
+/**
+ * of_get_next_child_with_prefix - Find the next child node with prefix
+ * @node:	parent node
+ * @prev:	previous child of the parent node, or NULL to get first
+ *
+ * This function is like of_get_next_child(), except that it automatically
+ * skips any nodes whose name doesn't have the given prefix.
+ *
+ * Return: A node pointer with refcount incremented, use
+ * of_node_put() on it when done.
+ */
+struct device_node *of_get_next_child_with_prefix(const struct device_node *node,
+						  struct device_node *prev,
+						  const char *prefix)
+{
+	struct device_node *next;
+	unsigned long flags;
+
+	if (!node)
+		return NULL;
+
+	raw_spin_lock_irqsave(&devtree_lock, flags);
+	next = prev ? prev->sibling : node->child;
+	for (; next; next = next->sibling) {
+		if (!of_node_name_prefix(next, prefix))
+			continue;
+		if (of_node_get(next))
+			break;
+	}
+	of_node_put(prev);
+	raw_spin_unlock_irqrestore(&devtree_lock, flags);
+	return next;
+}
+EXPORT_SYMBOL(of_get_next_child_with_prefix);
+
 static struct device_node *of_get_next_status_child(const struct device_node *node,
 						    struct device_node *prev,
 						    bool (*checker)(const struct device_node *))
diff --git a/include/linux/of.h b/include/linux/of.h
index 046283be1cd3..acc0d5b98417 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -289,6 +289,9 @@  extern struct device_node *of_get_parent(const struct device_node *node);
 extern struct device_node *of_get_next_parent(struct device_node *node);
 extern struct device_node *of_get_next_child(const struct device_node *node,
 					     struct device_node *prev);
+extern struct device_node *of_get_next_child_with_prefix(const struct device_node *node,
+							 struct device_node *prev,
+							 const char *prefix);
 extern struct device_node *of_get_next_available_child(
 	const struct device_node *node, struct device_node *prev);
 extern struct device_node *of_get_next_reserved_child(
@@ -1468,6 +1471,12 @@  static inline int of_property_read_s32(const struct device_node *np,
 	     child != NULL;						\
 	     child = of_get_next_child(parent, child))
 
+#define for_each_child_of_node_with_prefix_scoped(parent, child, prefix) \
+	for (struct device_node *child __free(device_node) =		\
+	     of_get_next_child_with_prefix(parent, NULL, prefix);	\
+	     child != NULL;						\
+	     child = of_get_next_child_with_prefix(parent, child, prefix))
+
 #define for_each_available_child_of_node(parent, child) \
 	for (child = of_get_next_available_child(parent, NULL); child != NULL; \
 	     child = of_get_next_available_child(parent, child))