From nobody Mon Feb 9 02:13:35 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BF402451054; Tue, 20 Jan 2026 18:16:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768932975; cv=none; b=kORqB+08jSlATTsYuzTTE3do00hGOrF+t6A0hLhYgfzIzyNxu2x/1myEmXKt/nOEJh+z9ggpoi1tPKF51YsHqWqNwtAir8YjnHLt6Ltuhwri5xz2qZgQUs0EuTWgNj08p8VP4E5Vr8TWjmrJ1QjKQSZfBWfNbboutimqm6W7r+E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768932975; c=relaxed/simple; bh=qrE0iyx32CrlLykRMkHpplSrDxoo8JgPJRkbCoPqrWE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qXaCvUOdU35H3sOLCyw8ppWjplHSYxYDqERlvyjW8sMtQPavwjNl2QPnVBx+PnA1bgx36ctwpvprMuGqY8RmXPfMbRmg8qHmNOCEvm69ruJ5F7gwHlIczf9cHo8m98VRI0IMPYbrLm9PCS49D7EbGtnwa6y8mkI84u0Y8ae+VaM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=X60bTRIp; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="X60bTRIp" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 12C24C16AAE; Tue, 20 Jan 2026 18:16:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768932973; bh=qrE0iyx32CrlLykRMkHpplSrDxoo8JgPJRkbCoPqrWE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=X60bTRIp6iBuYMCEmmE6ttaeDQ1DpsjYd/RErVlHlAwXwCdGrXLKFMRw6vdMQnW1I itm4eWufwADbx1/Ws41mI/N41OUfOUgvGPMr13kpCg3uwzJfSnV1P4B9qt0gGNyHpa 0fcUiOKhf9ePqWTnNG9spgZiTPf2v39le9q8MSXKjd8vX9snKRkv3ynlSjUZEUk4Bl hKZbK7pPcKy8ubYIPFM5XX6yM0xpvIlRdK0PDNGyBxYAaD4wNlIsoGEAO/0m03Smjc BzlsoD6EgBv0qY41x9Fy3FBAbtK/JI9W9rH/kknGghqabmmEcdaJipHBu5ngENICdB Wl/jt5EMCepWA== From: Conor Dooley To: linusw@kernel.org Cc: conor@kernel.org, Conor Dooley , Rob Herring , Krzysztof Kozlowski , linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, devicetree@vger.kernel.org, Valentina.FernandezAlanis@microchip.com Subject: [PATCH v4 2/5] pinctrl: add generic functions + pins mapper Date: Tue, 20 Jan 2026 18:15:40 +0000 Message-ID: <20260120-glucose-scary-de7fba2616b2@spud> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260120-elixir-salute-dd6ec3d9f5fe@spud> References: <20260120-elixir-salute-dd6ec3d9f5fe@spud> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8218; i=conor.dooley@microchip.com; h=from:subject:message-id; bh=zZ8BxvgX6/g/itLznDT3+eEO8kxCEFsQrBjzmr/TzIY=; b=owGbwMvMwCVWscWwfUFT0iXG02pJDJn5x7w+ztydr1ysXva09UrVde3NXDq7Em5d3uB55E7hg Zywxbn8HaUsDGJcDLJiiiyJt/tapNb/cdnh3PMWZg4rE8gQBi5OAZjIXXWG/1Er2kr1qp/vaZp4 KfnFzzVtikXnS37N/3pKkP1E5z2frN+MDCd/amTbyOaZyu+a5CJ9c7X9/12lEk9EtMwZDR9XOBZ FsQIA X-Developer-Key: i=conor.dooley@microchip.com; a=openpgp; fpr=F9ECA03CF54F12CD01F1655722E2C55B37CF380C Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Conor Dooley Add a generic function to allow creation of groups and functions at runtime based on devicetree content, before setting up mux mappings. It works similarly to pinconf_generic_dt_node_to_map(), and therefore parses pinconf properties and maps those too, allowing it to be used as the dt_node_to_map member of the pinctrl_ops struct. Signed-off-by: Conor Dooley --- drivers/pinctrl/Kconfig | 6 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinconf.h | 16 +++ drivers/pinctrl/pinctrl-generic.c | 189 ++++++++++++++++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-generic.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index d2a414450c16..6cc5e214f4f3 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -25,6 +25,12 @@ config GENERIC_PINCONF bool select PINCONF =20 +config GENERIC_PINCTRL + bool + depends on GENERIC_PINCONF + depends on GENERIC_PINCTRL_GROUPS + depends on GENERIC_PINMUX_FUNCTIONS + config DEBUG_PINCTRL bool "Debug PINCTRL calls" depends on DEBUG_KERNEL diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 05737b1afec9..f7d5d5f76d0c 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -7,6 +7,7 @@ obj-y +=3D core.o pinctrl-utils.o obj-$(CONFIG_PINMUX) +=3D pinmux.o obj-$(CONFIG_PINCONF) +=3D pinconf.o obj-$(CONFIG_GENERIC_PINCONF) +=3D pinconf-generic.o +obj-$(CONFIG_GENERIC_PINCTRL) +=3D pinctrl-generic.o obj-$(CONFIG_OF) +=3D devicetree.o =20 obj-$(CONFIG_PINCTRL_AMD) +=3D pinctrl-amd.o diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index e1ae71610526..2880adef476e 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -160,3 +160,19 @@ pinconf_generic_parse_dt_pinmux(struct device_node *np= , struct device *dev, return -ENOTSUPP; } #endif + +#if defined(CONFIG_GENERIC_PINCTRL) && defined (CONFIG_OF) +int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctld= ev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps); +#else +static inline int +pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps) +{ + return -ENOTSUPP; +} +#endif diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-ge= neric.c new file mode 100644 index 000000000000..efb39c6a6703 --- /dev/null +++ b/drivers/pinctrl/pinctrl-generic.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) "generic pinconfig core: " fmt + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "core.h" +#include "pinconf.h" +#include "pinctrl-utils.h" +#include "pinmux.h" + +static int pinctrl_generic_pins_function_dt_subnode_to_map(struct pinctrl_= dev *pctldev, + struct device_node *parent, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps, + unsigned int *num_reserved_maps, + const char **group_names, + unsigned int ngroups) +{ + struct device *dev =3D pctldev->dev; + const char **functions; + const char *group_name; + unsigned long *configs; + unsigned int num_configs, pin, *pins; + int npins, ret, reserve =3D 1; + + npins =3D of_property_count_u32_elems(np, "pins"); + + if (npins < 1) { + dev_err(dev, "invalid pinctrl group %pOFn.%pOFn %d\n", + parent, np, npins); + return npins; + } + + group_name =3D devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", parent, np); + if (!group_name) + return -ENOMEM; + + group_names[ngroups] =3D group_name; + + pins =3D devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + functions =3D devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL); + if (!functions) + return -ENOMEM; + + for (int i =3D 0; i < npins; i++) { + ret =3D of_property_read_u32_index(np, "pins", i, &pin); + if (ret) + return ret; + + pins[i] =3D pin; + + ret =3D of_property_read_string(np, "function", &functions[i]); + if (ret) + return ret; + } + + ret =3D pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_m= aps, reserve); + if (ret) + return ret; + + ret =3D pinctrl_utils_add_map_mux(pctldev, maps, num_reserved_maps, num_m= aps, group_name, + parent->name); + if (ret < 0) + return ret; + + ret =3D pinctrl_generic_add_group(pctldev, group_name, pins, npins, funct= ions); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to add group %s: %d\n", + group_name, ret); + + ret =3D pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_confi= gs); + if (ret) + return dev_err_probe(dev, ret, "failed to parse pin config of group %s\n= ", + group_name); + + if (num_configs =3D=3D 0) + return 0; + + ret =3D pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_m= aps, reserve); + if (ret) + return ret; + + ret =3D pinctrl_utils_add_map_configs(pctldev, maps, num_reserved_maps, n= um_maps, group_name, + configs, + num_configs, PIN_MAP_TYPE_CONFIGS_GROUP); + kfree(configs); + if (ret) + return ret; + + return 0; +}; + +/* + * For platforms that do not define groups or functions in the driver, but + * instead use the devicetree to describe them. This function will, unlike + * pinconf_generic_dt_node_to_map() etc which rely on driver defined groups + * and functions, create them in addition to parsing pinconf properties and + * adding mappings. + */ +int pinctrl_generic_pins_function_dt_node_to_map(struct pinctrl_dev *pctld= ev, + struct device_node *np, + struct pinctrl_map **maps, + unsigned int *num_maps) +{ + struct device *dev =3D pctldev->dev; + struct device_node *child_np; + const char **group_names; + unsigned int num_reserved_maps =3D 0; + int ngroups =3D 0; + int ret; + + *maps =3D NULL; + *num_maps =3D 0; + + /* + * Check if this is actually the pins node, or a parent containing + * multiple pins nodes. + */ + if (!of_property_present(np, "pins")) + goto parent; + + group_names =3D devm_kcalloc(dev, 1, sizeof(*group_names), GFP_KERNEL); + if (!group_names) + return -ENOMEM; + + ret =3D pinctrl_generic_pins_function_dt_subnode_to_map(pctldev, np, np, + maps, num_maps, + &num_reserved_maps, + group_names, + ngroups); + if (ret) { + pinctrl_utils_free_map(pctldev, *maps, *num_maps); + return dev_err_probe(dev, ret, "error figuring out mappings for %s\n", n= p->name); + } + + ret =3D pinmux_generic_add_function(pctldev, np->name, group_names, 1, NU= LL); + if (ret < 0) { + pinctrl_utils_free_map(pctldev, *maps, *num_maps); + return dev_err_probe(dev, ret, "error adding function %s\n", np->name); + } + + return 0; + +parent: + for_each_available_child_of_node(np, child_np) + ngroups +=3D 1; + + group_names =3D devm_kcalloc(dev, ngroups, sizeof(*group_names), GFP_KERN= EL); + if (!group_names) + return -ENOMEM; + + ngroups =3D 0; + for_each_available_child_of_node_scoped(np, child_np) { + ret =3D pinctrl_generic_pins_function_dt_subnode_to_map(pctldev, np, chi= ld_np, + maps, num_maps, + &num_reserved_maps, + group_names, + ngroups); + if (ret) { + pinctrl_utils_free_map(pctldev, *maps, *num_maps); + return dev_err_probe(dev, ret, "error figuring out mappings for %s\n", + np->name); + } + + ngroups++; + } + + ret =3D pinmux_generic_add_function(pctldev, np->name, group_names, ngrou= ps, NULL); + if (ret < 0) { + pinctrl_utils_free_map(pctldev, *maps, *num_maps); + return dev_err_probe(dev, ret, "error adding function %s\n", np->name); + } + + return 0; +} +EXPORT_SYMBOL_GPL(pinctrl_generic_pins_function_dt_node_to_map); --=20 2.51.0