[PATCH] pinctrl: pinconf-generic: widen the packed config argument to unsigned long

From: Peng Fan

Date: Mon Nov 09 2026 - 07:33:46 EST


From: Peng Fan <peng.fan@xxxxxxx>

The packed pin configuration stores the parameter in the lower 8 bits and
the argument in the remaining bits of an unsigned long. However, the
argument was limited to 24 bits: pinconf_to_config_argument() masked it
with 0xffffff, and pinconf_to_config_packed() took a u32 argument, so even
on 64-bit systems the left shift in PIN_CONF_PACKED() discarded the upper
bits. For example, the pad configuration value 0x1080000 used by a new
i.MX SoC was packed as 0x08000000 and read back as 0x80000.

Allow the argument to use the full width of unsigned long so that such
values survive the pack/unpack round trip. Cast the argument in
PIN_CONF_PACKED() to unsigned long to keep direct users of the macro
consistent with pinconf_to_config_packed(), and print the full argument
in the debugfs dumps.

The packed argument only has BITS_PER_LONG - 8 bits available, so on
32-bit systems values above 0xffffff still cannot be represented. Reject
such firmware properties instead of silently truncating them.

Signed-off-by: Peng Fan <peng.fan@xxxxxxx>
---
drivers/pinctrl/pinconf-generic.c | 16 +++++++++++-----
include/linux/pinctrl/pinconf-generic.h | 13 ++++++++-----
2 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c
index 36845517984e..0876015a6575 100644
--- a/drivers/pinctrl/pinconf-generic.c
+++ b/drivers/pinctrl/pinconf-generic.c
@@ -95,12 +95,12 @@ static void pinconf_generic_dump_one(struct pinctrl_dev *pctldev,
seq_puts(s, item->display);
/* Print unit if available */
if (item->has_arg) {
- u32 val = pinconf_to_config_argument(config);
+ unsigned long val = pinconf_to_config_argument(config);

if (item->format)
- seq_printf(s, " (%u %s)", val, item->format);
+ seq_printf(s, " (%lu %s)", val, item->format);
else
- seq_printf(s, " (0x%x)", val);
+ seq_printf(s, " (0x%lx)", val);

if (item->values && item->num_values) {
if (val < item->num_values)
@@ -152,7 +152,7 @@ void pinconf_generic_dump_config(struct pinctrl_dev *pctldev,
for (i = 0; i < ARRAY_SIZE(conf_items); i++) {
if (pinconf_to_config_param(config) != conf_items[i].param)
continue;
- seq_printf(s, "%s: 0x%x", conf_items[i].display,
+ seq_printf(s, "%s: 0x%lx", conf_items[i].display,
pinconf_to_config_argument(config));
}

@@ -164,7 +164,7 @@ void pinconf_generic_dump_config(struct pinctrl_dev *pctldev,
if (pinconf_to_config_param(config) !=
pctldev->desc->custom_conf_items[i].param)
continue;
- seq_printf(s, "%s: 0x%x",
+ seq_printf(s, "%s: 0x%lx",
pctldev->desc->custom_conf_items[i].display,
pinconf_to_config_argument(config));
}
@@ -268,6 +268,12 @@ static int parse_fw_cfg(struct fwnode_handle *fwnode,
}
}

+ if (val & ~PIN_CONF_ARG_MASK) {
+ pr_err("%pfw: value %#x for %s does not fit in the pinconf argument\n",
+ fwnode, val, par->property);
+ return -EINVAL;
+ }
+
pr_debug("found %s with value %u\n", par->property, val);
cfg[*ncfg] = pinconf_to_config_packed(par->param, val);
(*ncfg)++;
diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h
index 1df63c732b99..9d28d4de1415 100644
--- a/include/linux/pinctrl/pinconf-generic.h
+++ b/include/linux/pinctrl/pinconf-generic.h
@@ -172,27 +172,30 @@ enum pin_config_param {
/*
* Helpful configuration macro to be used in tables etc.
*/
-#define PIN_CONF_PACKED(p, a) ((a << 8) | ((unsigned long) p & 0xffUL))
+#define PIN_CONF_PACKED(p, a) \
+ (((unsigned long) a << 8) | ((unsigned long) p & 0xffUL))

/*
* The following inlines stuffs a configuration parameter and data value
* into and out of an unsigned long argument, as used by the generic pin config
* system. We put the parameter in the lower 8 bits and the argument in the
- * upper 24 bits.
+ * upper BITS_PER_LONG - 8 bits.
*/

+#define PIN_CONF_ARG_MASK (~0UL >> 8)
+
static inline enum pin_config_param pinconf_to_config_param(unsigned long config)
{
return (enum pin_config_param) (config & 0xffUL);
}

-static inline u32 pinconf_to_config_argument(unsigned long config)
+static inline unsigned long pinconf_to_config_argument(unsigned long config)
{
- return (u32) ((config >> 8) & 0xffffffUL);
+ return (config >> 8) & PIN_CONF_ARG_MASK;
}

static inline unsigned long pinconf_to_config_packed(enum pin_config_param param,
- u32 argument)
+ unsigned long argument)
{
return PIN_CONF_PACKED(param, argument);
}

---
base-commit: ddfe51b0a0f5dc9e415cc2c8ad8db17532dd426b
change-id: 20261109-pinconf-widen-07813693c2af

Best regards,
--
Peng Fan <peng.fan@xxxxxxx>