drivers/of/base.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of.h | 13 +++++++++++++ 2 files changed, 176 insertions(+)
Add a new helper function of_parse_map_iter() to simplify parsing of
nexus node maps as defined in the DT spec, section 2.5.1.
This function provides an iterator interface for traversing map entries,
handling the complexity of variable-sized entries based on #<stem>-cells
properties. Each map entry follows the format:
<child_specifier phandle parent_specifier>
The iterator extracts both the child specifier and parent phandle+args
for each entry, managing all the details of:
- Reading #<stem>-cells from both child and parent nodes
- Calculating variable entry sizes
- Resolving phandles
- Proper node reference management
This eliminates the need for subsystems to manually parse map properties,
reducing code duplication and potential bugs.
This code was developed in collaboration with Claude Code (model:
Sonnet 4.5), which needed some guidance to use existing OF helpers,
iterators etc.
Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
---
Changes in v2:
- Use helpers of_phandle_iterator_init() and of_phandle_iterator_next()
- add missing of_node_put() pointed out in v1
- Link to v1: https://patch.msgid.link/20251119-topic-lpm-of-map-iterator-v6-18-v1-1-1f0075d771a3@baylibre.com
---
drivers/of/base.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/of.h | 13 +++++++++++++
2 files changed, 176 insertions(+)
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 0b65039ece53..8392fe54cf60 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1641,6 +1641,169 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
}
EXPORT_SYMBOL(of_parse_phandle_with_args_map);
+/**
+ * of_parse_map_iter() - Iterate through entries in a nexus node map
+ * @np: pointer to a device tree node containing the map
+ * @stem_name: stem of property names (e.g., "power-domain" for "power-domain-map")
+ * @index: pointer to iteration index (set to 0 for first call)
+ * @child_args: pointer to structure to fill with child specifier (can be NULL)
+ * @parent_args: pointer to structure to fill with parent phandle and specifier
+ *
+ * This function iterates through a nexus node map property as defined in DT spec 2.5.1.
+ * Each map entry has the format: <child_specifier phandle parent_specifier>
+ *
+ * On each call, it extracts one map entry and fills child_args (if provided) with the
+ * child specifier and parent_args with the parent phandle and specifier.
+ * The index pointer is updated to point to the next entry for the following call.
+ *
+ * Example usage::
+ *
+ * int index = 0;
+ * struct of_phandle_args child_args, parent_args;
+ *
+ * while (!of_parse_map_iter(np, "power-domain", &index, &child_args, &parent_args)) {
+ * // Process child_args and parent_args
+ * of_node_put(parent_args.np);
+ * }
+ *
+ * Caller is responsible for calling of_node_put() on parent_args.np.
+ *
+ * Return: 0 on success, -ENOENT when iteration is complete, or negative error code on failure.
+ */
+int of_parse_map_iter(const struct device_node *np,
+ const char *stem_name,
+ int *index,
+ struct of_phandle_args *child_args,
+ struct of_phandle_args *parent_args)
+{
+ char *cells_name __free(kfree) = kasprintf(GFP_KERNEL, "#%s-cells", stem_name);
+ char *map_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map", stem_name);
+ char *mask_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name);
+ char *pass_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name);
+ static const __be32 dummy_mask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) };
+ static const __be32 dummy_pass[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(0) };
+ struct of_phandle_iterator it;
+ const __be32 *map, *mask, *pass;
+ __be32 child_spec[MAX_PHANDLE_ARGS];
+ u32 child_cells, parent_cells;
+ int i, entry_idx, ret;
+
+ if (!np || !stem_name || !index || !parent_args)
+ return -EINVAL;
+
+ if (!cells_name || !map_name || !mask_name || !pass_name)
+ return -ENOMEM;
+
+ /* Initialize iterator to get the map property */
+ ret = of_phandle_iterator_init(&it, np, map_name, cells_name, -1);
+ if (ret)
+ return ret;
+
+ map = it.cur;
+
+ /* Get child #cells */
+ if (of_property_read_u32(np, cells_name, &child_cells))
+ return -EINVAL;
+
+ /* Get the mask property (optional) */
+ mask = of_get_property(np, mask_name, NULL);
+ if (!mask)
+ mask = dummy_mask;
+
+ /* Get the pass-thru property (optional) */
+ pass = of_get_property(np, pass_name, NULL);
+ if (!pass)
+ pass = dummy_pass;
+
+ /* Iterate through map to find the entry at the requested index */
+ entry_idx = 0;
+ while (it.cur + child_cells + 1 < it.list_end) {
+ /* If this is the entry we're looking for, extract it */
+ if (entry_idx == *index) {
+ /* Save masked child specifier for pass-thru processing */
+ for (i = 0; i < child_cells && i < MAX_PHANDLE_ARGS; i++)
+ child_spec[i] = map[i] & mask[i];
+
+ /* Extract child specifier if requested */
+ if (child_args) {
+ child_args->np = (struct device_node *)np;
+ child_args->args_count = child_cells;
+ for (i = 0; i < child_cells && i < MAX_PHANDLE_ARGS; i++)
+ child_args->args[i] = be32_to_cpu(map[i]);
+ }
+
+ /* Move past child specifier */
+ it.cur += child_cells;
+
+ /* Use iterator to read phandle and get parent node/cells */
+ it.phandle_end = it.cur;
+ ret = of_phandle_iterator_next(&it);
+ if (ret) {
+ if (it.node)
+ of_node_put(it.node);
+ return ret;
+ }
+
+ parent_args->np = it.node;
+ it.node = NULL; /* Ownership transferred to parent_args */
+ parent_cells = it.cur_count;
+ map = it.cur;
+
+ /* Check for malformed properties */
+ if (WARN_ON(parent_cells > MAX_PHANDLE_ARGS)) {
+ of_node_put(parent_args->np);
+ return -EINVAL;
+ }
+
+ /*
+ * Copy parent specifier into the out_args structure, keeping
+ * the bits specified in <stem>-map-pass-thru per DT spec 2.5.1
+ */
+ parent_args->args_count = parent_cells;
+ for (i = 0; i < parent_cells; i++) {
+ __be32 val = map[i];
+
+ if (i < child_cells) {
+ val &= ~pass[i];
+ val |= child_spec[i] & pass[i];
+ }
+
+ parent_args->args[i] = be32_to_cpu(val);
+ }
+
+ /* Advance index for next iteration */
+ (*index)++;
+ return 0;
+ }
+
+ /* Skip this entry: child_cells + phandle + parent_cells */
+ it.cur += child_cells;
+
+ /* Use iterator to read phandle and skip parent cells */
+ it.phandle_end = it.cur;
+ ret = of_phandle_iterator_next(&it);
+ if (ret) {
+ if (it.node)
+ of_node_put(it.node);
+ return ret;
+ }
+
+ /* Move forward to next entry and clean up node reference */
+ it.cur = it.phandle_end;
+ map = it.cur;
+ of_node_put(it.node);
+ it.node = NULL;
+
+ entry_idx++;
+ }
+
+ /* Reached end of map without finding the requested index */
+ if (it.node)
+ of_node_put(it.node);
+ return -ENOENT;
+}
+EXPORT_SYMBOL(of_parse_map_iter);
+
/**
* of_count_phandle_with_args() - Find the number of phandles references in a property
* @np: pointer to a device tree node containing a list
diff --git a/include/linux/of.h b/include/linux/of.h
index 9bbdcf25a2b4..4908b78ac3e1 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -387,6 +387,10 @@ extern int __of_parse_phandle_with_args(const struct device_node *np,
extern int of_parse_phandle_with_args_map(const struct device_node *np,
const char *list_name, const char *stem_name, int index,
struct of_phandle_args *out_args);
+extern int of_parse_map_iter(const struct device_node *np,
+ const char *stem_name, int *index,
+ struct of_phandle_args *child_args,
+ struct of_phandle_args *parent_args);
extern int of_count_phandle_with_args(const struct device_node *np,
const char *list_name, const char *cells_name);
@@ -797,6 +801,15 @@ static inline int of_parse_phandle_with_args_map(const struct device_node *np,
return -ENOSYS;
}
+static inline int of_parse_map_iter(const struct device_node *np,
+ const char *stem_name,
+ int *index,
+ struct of_phandle_args *child_args,
+ struct of_phandle_args *parent_args)
+{
+ return -ENOSYS;
+}
+
static inline int of_count_phandle_with_args(const struct device_node *np,
const char *list_name,
const char *cells_name)
---
base-commit: 3e7f562e20ee87a25e104ef4fce557d39d62fa85
change-id: 20251119-topic-lpm-of-map-iterator-v6-18-a61447423adc
Best regards,
--
Kevin Hilman (TI) <khilman@baylibre.com>
On Wed, Jan 21, 2026 at 5:55 PM Kevin Hilman (TI) <khilman@baylibre.com> wrote:
>
> Add a new helper function of_parse_map_iter() to simplify parsing of
> nexus node maps as defined in the DT spec, section 2.5.1.
>
> This function provides an iterator interface for traversing map entries,
> handling the complexity of variable-sized entries based on #<stem>-cells
> properties. Each map entry follows the format:
> <child_specifier phandle parent_specifier>
>
> The iterator extracts both the child specifier and parent phandle+args
> for each entry, managing all the details of:
> - Reading #<stem>-cells from both child and parent nodes
> - Calculating variable entry sizes
> - Resolving phandles
> - Proper node reference management
>
> This eliminates the need for subsystems to manually parse map properties,
> reducing code duplication and potential bugs.
>
> This code was developed in collaboration with Claude Code (model:
> Sonnet 4.5), which needed some guidance to use existing OF helpers,
> iterators etc.
>
> Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
> ---
> Changes in v2:
> - Use helpers of_phandle_iterator_init() and of_phandle_iterator_next()
> - add missing of_node_put() pointed out in v1
> - Link to v1: https://patch.msgid.link/20251119-topic-lpm-of-map-iterator-v6-18-v1-1-1f0075d771a3@baylibre.com
> ---
> drivers/of/base.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/of.h | 13 +++++++++++++
> 2 files changed, 176 insertions(+)
>
> diff --git a/drivers/of/base.c b/drivers/of/base.c
> index 0b65039ece53..8392fe54cf60 100644
> --- a/drivers/of/base.c
> +++ b/drivers/of/base.c
> @@ -1641,6 +1641,169 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
> }
> EXPORT_SYMBOL(of_parse_phandle_with_args_map);
>
> +/**
> + * of_parse_map_iter() - Iterate through entries in a nexus node map
> + * @np: pointer to a device tree node containing the map
> + * @stem_name: stem of property names (e.g., "power-domain" for "power-domain-map")
> + * @index: pointer to iteration index (set to 0 for first call)
> + * @child_args: pointer to structure to fill with child specifier (can be NULL)
> + * @parent_args: pointer to structure to fill with parent phandle and specifier
> + *
> + * This function iterates through a nexus node map property as defined in DT spec 2.5.1.
> + * Each map entry has the format: <child_specifier phandle parent_specifier>
> + *
> + * On each call, it extracts one map entry and fills child_args (if provided) with the
> + * child specifier and parent_args with the parent phandle and specifier.
> + * The index pointer is updated to point to the next entry for the following call.
> + *
> + * Example usage::
> + *
> + * int index = 0;
> + * struct of_phandle_args child_args, parent_args;
> + *
> + * while (!of_parse_map_iter(np, "power-domain", &index, &child_args, &parent_args)) {
> + * // Process child_args and parent_args
> + * of_node_put(parent_args.np);
> + * }
> + *
> + * Caller is responsible for calling of_node_put() on parent_args.np.
> + *
> + * Return: 0 on success, -ENOENT when iteration is complete, or negative error code on failure.
> + */
> +int of_parse_map_iter(const struct device_node *np,
> + const char *stem_name,
> + int *index,
> + struct of_phandle_args *child_args,
> + struct of_phandle_args *parent_args)
> +{
> + char *cells_name __free(kfree) = kasprintf(GFP_KERNEL, "#%s-cells", stem_name);
> + char *map_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map", stem_name);
> + char *mask_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name);
> + char *pass_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name);
It's not great that we're doing allocs and frees on each iteration.
Can't we follow the same design Herve did for interrupt-map? You have
an init function you call once up front and then an iterator define
(e.g. for_each_of_imap_item()). The complication in this case would be
if we do the allocs in the init function, then we need a way to free
them. If they are part of the for loop init, then we could use the
scoped cleanup.
On thing I noticed is I think of_phandle_iterator and of_imap_parser
should probably be merged to one struct. They basically hold the same
information (pointers to property data).
Let me see if I can come up with something next week.
Rob
Rob Herring <robh@kernel.org> writes:
> On Wed, Jan 21, 2026 at 5:55 PM Kevin Hilman (TI) <khilman@baylibre.com> wrote:
>>
>> Add a new helper function of_parse_map_iter() to simplify parsing of
>> nexus node maps as defined in the DT spec, section 2.5.1.
>>
>> This function provides an iterator interface for traversing map entries,
>> handling the complexity of variable-sized entries based on #<stem>-cells
>> properties. Each map entry follows the format:
>> <child_specifier phandle parent_specifier>
>>
>> The iterator extracts both the child specifier and parent phandle+args
>> for each entry, managing all the details of:
>> - Reading #<stem>-cells from both child and parent nodes
>> - Calculating variable entry sizes
>> - Resolving phandles
>> - Proper node reference management
>>
>> This eliminates the need for subsystems to manually parse map properties,
>> reducing code duplication and potential bugs.
>>
>> This code was developed in collaboration with Claude Code (model:
>> Sonnet 4.5), which needed some guidance to use existing OF helpers,
>> iterators etc.
>>
>> Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
>> ---
>> Changes in v2:
>> - Use helpers of_phandle_iterator_init() and of_phandle_iterator_next()
>> - add missing of_node_put() pointed out in v1
>> - Link to v1: https://patch.msgid.link/20251119-topic-lpm-of-map-iterator-v6-18-v1-1-1f0075d771a3@baylibre.com
>> ---
>> drivers/of/base.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/of.h | 13 +++++++++++++
>> 2 files changed, 176 insertions(+)
>>
>> diff --git a/drivers/of/base.c b/drivers/of/base.c
>> index 0b65039ece53..8392fe54cf60 100644
>> --- a/drivers/of/base.c
>> +++ b/drivers/of/base.c
>> @@ -1641,6 +1641,169 @@ int of_parse_phandle_with_args_map(const struct device_node *np,
>> }
>> EXPORT_SYMBOL(of_parse_phandle_with_args_map);
>>
>> +/**
>> + * of_parse_map_iter() - Iterate through entries in a nexus node map
>> + * @np: pointer to a device tree node containing the map
>> + * @stem_name: stem of property names (e.g., "power-domain" for "power-domain-map")
>> + * @index: pointer to iteration index (set to 0 for first call)
>> + * @child_args: pointer to structure to fill with child specifier (can be NULL)
>> + * @parent_args: pointer to structure to fill with parent phandle and specifier
>> + *
>> + * This function iterates through a nexus node map property as defined in DT spec 2.5.1.
>> + * Each map entry has the format: <child_specifier phandle parent_specifier>
>> + *
>> + * On each call, it extracts one map entry and fills child_args (if provided) with the
>> + * child specifier and parent_args with the parent phandle and specifier.
>> + * The index pointer is updated to point to the next entry for the following call.
>> + *
>> + * Example usage::
>> + *
>> + * int index = 0;
>> + * struct of_phandle_args child_args, parent_args;
>> + *
>> + * while (!of_parse_map_iter(np, "power-domain", &index, &child_args, &parent_args)) {
>> + * // Process child_args and parent_args
>> + * of_node_put(parent_args.np);
>> + * }
>> + *
>> + * Caller is responsible for calling of_node_put() on parent_args.np.
>> + *
>> + * Return: 0 on success, -ENOENT when iteration is complete, or negative error code on failure.
>> + */
>> +int of_parse_map_iter(const struct device_node *np,
>> + const char *stem_name,
>> + int *index,
>> + struct of_phandle_args *child_args,
>> + struct of_phandle_args *parent_args)
>> +{
>> + char *cells_name __free(kfree) = kasprintf(GFP_KERNEL, "#%s-cells", stem_name);
>> + char *map_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map", stem_name);
>> + char *mask_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name);
>> + char *pass_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name);
>
> It's not great that we're doing allocs and frees on each iteration.
>
> Can't we follow the same design Herve did for interrupt-map? You have
> an init function you call once up front and then an iterator define
> (e.g. for_each_of_imap_item()). The complication in this case would be
> if we do the allocs in the init function, then we need a way to free
> them. If they are part of the for loop init, then we could use the
> scoped cleanup.
>
> On thing I noticed is I think of_phandle_iterator and of_imap_parser
> should probably be merged to one struct. They basically hold the same
> information (pointers to property data).
>
> Let me see if I can come up with something next week.
Thank you!
I would greatly appreciate some help getting this going in the right
direction as I'm definitely out of my comfort zone in this drivers/of
code.
Kevin
© 2016 - 2026 Red Hat, Inc.