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. Tested on TI J722S (HS-FS): pinctrl probe 723 ms -> 22 ms, identical gpio-range list produced. Signed-off-by: Jordi Trepat Mur <[email protected]> --- 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 index 42980e09..44297951 100644 --- 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 char *cellname = "#pinctrl-single,gpio-range-cells"; struct single_gpiofunc_range *range; struct ofnode_phandle_args gpiospec; - int ret, i; - - for (i = 0; ; i++) { - ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), propname, - cellname, 0, i, &gpiospec); - /* Do not treat it as error. Only treat it as end condition. */ - if (ret) { - ret = 0; - break; + const __be32 *list; + int size, total_cells, cells_per_entry, entries, ret, i; + int ret_total = 0; + + /* + * Read the property once and iterate over the raw cell array in + * memory. Calling ofnode_parse_phandle_with_args() once per index + * re-walks the property from the start on every call and resolves + * the phandle (a full-FDT scan) at each step, which is O(N^2) in + * the number of entries and very slow pre-relocation with caches + * off. Resolve only the first entry's phandle to validate the + * cell layout; the target node itself is never used here. + */ + list = ofnode_get_property(dev_ofnode(dev), propname, &size); + if (!list || size <= 0) + return 0; + total_cells = size / sizeof(*list); + + /* Validate cell layout via the first entry (one phandle lookup total). */ + ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), propname, + cellname, 0, 0, &gpiospec); + if (ret) + return 0; + 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; + } + entries = total_cells / cells_per_entry; + + for (i = 0; i < entries; i++) { + const __be32 *entry = list + i * cells_per_entry; + u32 phandle_i = be32_to_cpup(entry + 0); + + if (phandle_i == 0) { + /* empty entry - match original: skip without creating range */ + continue; } range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL); if (!range) { - ret = -ENOMEM; + ret_total = -ENOMEM; break; } - range->offset = gpiospec.args[0]; - range->npins = gpiospec.args[1]; - range->gpiofunc = gpiospec.args[2]; + range->offset = be32_to_cpup(entry + 1); + range->npins = be32_to_cpup(entry + 2); + range->gpiofunc = be32_to_cpup(entry + 3); list_add_tail(&range->node, &priv->gpiofuncs); } - return ret; + return ret_total; } static int single_probe(struct udevice *dev) -- 2.34.1

