From: Conor Dooley <conor@kernel.org>
To: linusw@kernel.org
Cc: conor@kernel.org, Conor Dooley <conor.dooley@microchip.com>,
Linus Walleij <linus.walleij@linaro.org>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org,
devicetree@vger.kernel.org,
Valentina.FernandezAlanis@microchip.com
Subject: [PATCH v3 2/6] pinctrl: add generic functions + pins mapper
Date: Mon, 19 Jan 2026 11:03:53 +0000 [thread overview]
Message-ID: <20260119-altitude-hardhead-dbf4fb6f1798@spud> (raw)
In-Reply-To: <20260119-rearrange-germproof-3e3096cc0da4@spud>
From: Conor Dooley <conor.dooley@microchip.com>
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 <conor.dooley@microchip.com>
---
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
+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 += core.o pinctrl-utils.o
obj-$(CONFIG_PINMUX) += pinmux.o
obj-$(CONFIG_PINCONF) += pinconf.o
obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
+obj-$(CONFIG_GENERIC_PINCTRL) += pinctrl-generic.o
obj-$(CONFIG_OF) += devicetree.o
obj-$(CONFIG_PINCTRL_AMD) += 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 *pctldev,
+ 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-generic.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 <linux/array_size.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#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 = pctldev->dev;
+ const char **functions;
+ const char *group_name;
+ unsigned long *configs;
+ unsigned int num_configs, pin, *pins;
+ int npins, ret, reserve = 1;
+
+ npins = 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 = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn", parent, np);
+ if (!group_name)
+ return -ENOMEM;
+
+ group_names[ngroups] = group_name;
+
+ pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ functions = devm_kcalloc(dev, npins, sizeof(*functions), GFP_KERNEL);
+ if (!functions)
+ return -ENOMEM;
+
+ for (int i = 0; i < npins; i++) {
+ ret = of_property_read_u32_index(np, "pins", i, &pin);
+ if (ret)
+ return ret;
+
+ pins[i] = pin;
+
+ ret = of_property_read_string(np, "function", &functions[i]);
+ if (ret)
+ return ret;
+ }
+
+ ret = pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_maps, reserve);
+ if (ret)
+ return ret;
+
+ ret = pinctrl_utils_add_map_mux(pctldev, maps, num_reserved_maps, num_maps, group_name,
+ parent->name);
+ if (ret < 0)
+ return ret;
+
+ ret = pinctrl_generic_add_group(pctldev, group_name, pins, npins, functions);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to add group %s: %d\n",
+ group_name, ret);
+
+ ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to parse pin config of group %s\n",
+ group_name);
+
+ if (num_configs == 0)
+ return 0;
+
+ ret = pinctrl_utils_reserve_map(pctldev, maps, num_reserved_maps, num_maps, reserve);
+ if (ret)
+ return ret;
+
+ ret = pinctrl_utils_add_map_configs(pctldev, maps, num_reserved_maps, num_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 *pctldev,
+ struct device_node *np,
+ struct pinctrl_map **maps,
+ unsigned int *num_maps)
+{
+ struct device *dev = pctldev->dev;
+ struct device_node *child_np;
+ const char **group_names;
+ unsigned int num_reserved_maps = 0;
+ int ngroups = 0;
+ int ret;
+
+ *maps = NULL;
+ *num_maps = 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 = devm_kcalloc(dev, 1, sizeof(*group_names), GFP_KERNEL);
+ if (!group_names)
+ return -ENOMEM;
+
+ ret = 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", np->name);
+ }
+
+ ret = pinmux_generic_add_function(pctldev, np->name, group_names, 1, 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;
+
+parent:
+ for_each_available_child_of_node(np, child_np)
+ ngroups += 1;
+
+ group_names = devm_kcalloc(dev, ngroups, sizeof(*group_names), GFP_KERNEL);
+ if (!group_names)
+ return -ENOMEM;
+
+ ngroups = 0;
+ for_each_available_child_of_node_scoped(np, child_np) {
+ ret = pinctrl_generic_pins_function_dt_subnode_to_map(pctldev, np, child_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 = pinmux_generic_add_function(pctldev, np->name, group_names, ngroups, 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);
--
2.51.0
next prev parent reply other threads:[~2026-01-19 11:04 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-19 11:03 [PATCH v3 0/6] Microchip mpfs/pic64gx pinctrl part 2 Conor Dooley
2026-01-19 11:03 ` [PATCH v3 1/6] pinctrl: move microchip riscv pinctrl drivers to a folder Conor Dooley
2026-01-19 11:03 ` Conor Dooley [this message]
2026-01-19 11:03 ` [PATCH v3 3/6] dt-bindings: pinctrl: document polarfire soc mssio pin controller Conor Dooley
2026-01-19 11:03 ` [PATCH v3 4/6] pinctrl: add polarfire soc mssio pinctrl driver Conor Dooley
2026-01-19 11:03 ` [PATCH v3 5/6] MAINTAINERS: add Microchip mpfs mssio driver/bindings to entry Conor Dooley
2026-01-19 11:03 ` [PATCH v3 6/6] riscv: dts: microchip: add pinctrl nodes for mpfs/icicle kit Conor Dooley
2026-04-17 14:51 ` Conor Dooley
2026-01-19 23:45 ` [PATCH v3 0/6] Microchip mpfs/pic64gx pinctrl part 2 Linus Walleij
2026-01-20 18:05 ` Conor Dooley
2026-02-26 18:05 ` (subset) " Conor Dooley
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260119-altitude-hardhead-dbf4fb6f1798@spud \
--to=conor@kernel.org \
--cc=Valentina.FernandezAlanis@microchip.com \
--cc=conor.dooley@microchip.com \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linus.walleij@linaro.org \
--cc=linusw@kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox