From ac536de6d4247726977b1d6388fb7745c56f5188 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Fri, 15 May 2026 18:05:31 +0200 Subject: [PATCH] pinctrl: core: Make pin group callbacks optional for pin-only drivers Currently, the pinctrl core strictly requires all drivers to implement .get_groups_count and .get_group_name callbacks in their pinctrl_ops. However, for simple pinctrl drivers that act purely as GPIO controllers and pin-specific configuration proxies, without any concept of muxing or pin groups, this strict requirement forces the implementation of dummy callbacks just to satisfy pinctrl_check_ops(). Relax this requirement for pin-only drivers by making the group callbacks optional when no muxing or group pin configuration support is provided. Update the core and debugfs helpers to check for the existence of these callbacks before invoking them. Drivers that provide muxing or group pin configuration operations still must implement group enumeration and naming callbacks, and are rejected at registration time if they do not. Suggested-by: Linus Walleij Signed-off-by: Oleksij Rempel Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 40 ++++++++++++++++++++++++++++++++++----- drivers/pinctrl/pinconf.c | 9 +++++++-- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 6cbcaa6709da..3fcb7e584a93 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -622,8 +622,13 @@ static int pinctrl_generic_group_name_to_selector(struct pinctrl_dev *pctldev, const char *function) { const struct pinctrl_ops *ops = pctldev->desc->pctlops; - int ngroups = ops->get_groups_count(pctldev); int selector = 0; + int ngroups; + + if (!ops->get_groups_count || !ops->get_group_name) + return -EINVAL; + + ngroups = ops->get_groups_count(pctldev); /* See if this pctldev has this group */ while (selector < ngroups) { @@ -738,8 +743,15 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, const char *pin_group) { const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; - unsigned int ngroups = pctlops->get_groups_count(pctldev); unsigned int group_selector = 0; + unsigned int ngroups; + + if (!pctlops->get_groups_count || !pctlops->get_group_name) { + dev_err(pctldev->dev, "does not support pin groups\n"); + return -EINVAL; + } + + ngroups = pctlops->get_groups_count(pctldev); while (group_selector < ngroups) { const char *gname = pctlops->get_group_name(pctldev, @@ -1801,6 +1813,11 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) mutex_lock(&pctldev->mutex); + if (!ops->get_groups_count || !ops->get_group_name) { + mutex_unlock(&pctldev->mutex); + return 0; + } + ngroups = ops->get_groups_count(pctldev); seq_puts(s, "registered pin groups:\n"); @@ -2081,12 +2098,25 @@ static void pinctrl_remove_device_debugfs(struct pinctrl_dev *pctldev) static int pinctrl_check_ops(struct pinctrl_dev *pctldev) { const struct pinctrl_ops *ops = pctldev->desc->pctlops; + const struct pinconf_ops *confops = pctldev->desc->confops; + bool needs_groups = false; - if (!ops || - !ops->get_groups_count || - !ops->get_group_name) + if (!ops) return -EINVAL; + if (pctldev->desc->pmxops) + needs_groups = true; + + if (confops && (confops->pin_config_group_get || + confops->pin_config_group_set)) + needs_groups = true; + + if (needs_groups && (!ops->get_groups_count || !ops->get_group_name)) { + dev_err(pctldev->dev, + "driver needs group callbacks for mux or group config\n"); + return -EINVAL; + } + return 0; } diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index dca963633b5d..81686844dfa5 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -275,7 +275,7 @@ void pinconf_show_setting(struct seq_file *s, case PIN_MAP_TYPE_CONFIGS_GROUP: seq_printf(s, "group %s (%d)", pctlops->get_group_name(pctldev, - setting->data.configs.group_or_pin), + setting->data.configs.group_or_pin), setting->data.configs.group_or_pin); break; default: @@ -348,8 +348,13 @@ static int pinconf_groups_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev = s->private; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; - unsigned int ngroups = pctlops->get_groups_count(pctldev); unsigned int selector = 0; + unsigned int ngroups; + + if (!pctlops->get_groups_count || !pctlops->get_group_name) + return 0; + + ngroups = pctlops->get_groups_count(pctldev); seq_puts(s, "Pin config settings per pin group\n"); seq_puts(s, "Format: group (name): configs\n");