[PATCH] pinctrl: single: parse gpio-range as a raw array (O(N^2) -> O(N))
Simon Glass
sjg at chromium.org
Thu Jun 25 16:36:31 CEST 2026
Hi Jordi,
On 2026-06-14T10:32:32, Jordi Trepat Mur <yordy1902 at gmail.com> wrote:
> pinctrl: single: parse gpio-range as a raw array (O(N^2) -> O(N))
>
> single_add_gpio_func() calls ofnode_parse_phandle_with_args() once per
> gpio-range entry. With a flat device tree, every call re-iterates the
> property from index 0 and, because cellname is set, performs an
> fdt_node_offset_by_phandle() (a full-FDT scan) at every step. The total
> cost is therefore quadratic in the number of entries and proportional
> to the size of the device tree.
>
> On a TI J722S board this makes the pinctrl probe take 723 ms
> pre-relocation (caches off) for only 7 entries; with this change it
> takes 22 ms (-97%). Any pinctrl-single user with a populated
> pinctrl-single,gpio-range property pays this cost.
>
> Read the property once with ofnode_get_property() and iterate over the
> raw cells in memory instead. The phandle target node is never used by
> this function (only the three argument cells are stored), so resolve
> only the first entry with ofnode_parse_phandle_with_args() to validate
> the cell layout. Empty entries (phandle == 0) are skipped, matching the
> previous behaviour.
> [...]
>
> drivers/pinctrl/pinctrl-single.c | 62 +++++++++++++++++++++++++++++++---------
> 1 file changed, 48 insertions(+), 14 deletions(-)
> diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
> @@ -511,27 +511,61 @@ static int single_add_gpio_func(struct udevice *dev)
> + if (phandle_i == 0) {
> + /* empty entry - match original: skip without creating range */
> + continue;
> + }
Not quite. ofnode_parse_phandle_with_args() returns an error for a
zero phandle, and the old loop treats any error as end-of-list and
breaks. So phandle == 0 used to terminate the scan; now it is silently
skipped. Either restore the break, or call out the behaviour change in
the commit message and update the comment.
> diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
> @@ -511,27 +511,61 @@ static int single_add_gpio_func(struct udevice *dev)
> + if (gpiospec.args_count != 3) {
> + dev_err(dev, "expected 3 gpio-range args/entry, got %d\n",
> + gpiospec.args_count);
> + return -EINVAL;
> + }
> + cells_per_entry = 1 + gpiospec.args_count; /* phandle + args */
> + if (total_cells % cells_per_entry) {
> + dev_err(dev, "gpio-range size %d cells not multiple of %d\n",
> + total_cells, cells_per_entry);
> + return -EINVAL;
> + }
These two turn a silent no-op (return 0) into a hard probe failure.
args_count is already pinned by #pinctrl-single,gpio-range-cells, and
the size-mismatch case did not exist as an error path before. Please
either keep the lenient behaviour (warn and return 0) or justify the
stricter validation in the commit message.
> diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
> @@ -511,27 +511,61 @@ static int single_add_gpio_func(struct udevice *dev)
> + const __be32 *list;
> + int size, total_cells, cells_per_entry, entries, ret, i;
Did you consider ofnode_read_u32_array() (or reading into a small u32
buffer) instead of open-coding the __be32 walk with be32_to_cpup() ?
> diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
> @@ -511,27 +511,61 @@ static int single_add_gpio_func(struct udevice *dev)
> + int ret_total = 0;
...
> - ret = -ENOMEM;
> + ret_total = -ENOMEM;
> break;
The ret/ret_total split is unnecessary - by the time you enter the
loop the earlier ret has been consumed. Just reuse ret.
> diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
> @@ -511,27 +511,61 @@ static int single_add_gpio_func(struct udevice *dev)
> + range->offset = be32_to_cpup(entry + 1);
> + range->npins = be32_to_cpup(entry + 2);
> + range->gpiofunc = be32_to_cpup(entry + 3);
Please drop the extra spacing - single space after the field name, as
in the rest of the file.
+1 to Anshui's suggestion of avoiding alloc. You can create a
variable-length local var, for example.
Is every entry in the 'propname' property always for the same device?
I might be a bit confused, but I am not sure where that is required in
the schema.
Regards,
Simon
More information about the U-Boot
mailing list