From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Add pin configuration support for the Renesas RZ/T2H SoC. The RZ/T2H SoC
allows configuring several electrical characteristics through the DRCTLm
(I/O Buffer Function Switching) registers. These registers control bias
configuration, Schmitt trigger input, output slew rate, and drive
strength.
Implement pinconf_ops to allow reading and updating these properties
through the generic pin configuration framework. The implementation
supports bias-disable, bias-pull-up, bias-pull-down,
input-schmitt-enable, slew-rate, and drive-strength-microamp.
Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
---
v1->v2:
- Updated commit description
- Dropped 32 bit reg access for DRCTLm registers
- Switched using to guard for locking in rzt2h_pinctrl_drctl_rmwq
helper function
- Dropped using RENESAS_RZT2H_PIN_CONFIG_DRIVE_STRENGTH instead
switched to using the standard PIN_CONFIG_DRIVE_STRENGTH_UA
---
drivers/pinctrl/renesas/pinctrl-rzt2h.c | 259 ++++++++++++++++++++++++
1 file changed, 259 insertions(+)
diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c
index 5927744c7a96..6e60d1bb0c8b 100644
--- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c
+++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c
@@ -7,6 +7,7 @@
* Copyright (C) 2025 Renesas Electronics Corporation.
*/
+#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/bits.h>
@@ -43,6 +44,7 @@
#define PMC(m) (0x400 + (m))
#define PFC(m) (0x600 + 8 * (m))
#define PIN(m) (0x800 + (m))
+#define DRCTL(n) (0xa00 + 8 * (n))
#define RSELP(m) (0xc00 + (m))
#define PM_MASK GENMASK(1, 0)
@@ -54,6 +56,16 @@
#define PFC_PIN_MASK(pin) (PFC_MASK << ((pin) * 8))
#define PFC_FUNC_INTERRUPT 0
+#define DRCTL_PIN_SHIFT(pin) ((pin) << 3)
+#define DRCTL_DRV_PIN_MASK(pin) (GENMASK_ULL(1, 0) << DRCTL_PIN_SHIFT(pin))
+#define DRCTL_PUD_PIN_MASK(pin) (GENMASK_ULL(3, 2) << DRCTL_PIN_SHIFT(pin))
+#define DRCTL_SMT_PIN_MASK(pin) (BIT_ULL(4) << DRCTL_PIN_SHIFT(pin))
+#define DRCTL_SR_PIN_MASK(pin) (BIT_ULL(5) << DRCTL_PIN_SHIFT(pin))
+
+#define DRCTL_PUD_NONE 0
+#define DRCTL_PUD_PULL_UP 1
+#define DRCTL_PUD_PULL_DOWN 2
+
/*
* Use 16 lower bits [15:0] for pin identifier
* Use 8 higher bits [23:16] for pin mux function
@@ -91,6 +103,8 @@ struct rzt2h_pinctrl {
atomic_t wakeup_path;
};
+static const unsigned int rzt2h_drive_strength_ua[] = { 2500, 5000, 9000, 11800 };
+
#define RZT2H_GET_BASE(pctrl, port) \
((port) > RZT2H_MAX_SAFETY_PORTS ? (pctrl)->base0 : (pctrl)->base1)
@@ -110,6 +124,37 @@ RZT2H_PINCTRL_REG_ACCESS(b, u8)
RZT2H_PINCTRL_REG_ACCESS(w, u16)
RZT2H_PINCTRL_REG_ACCESS(q, u64)
+static int rzt2h_drive_strength_ua_to_idx(unsigned int ua)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(rzt2h_drive_strength_ua); i++) {
+ if (rzt2h_drive_strength_ua[i] == ua)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int rzt2h_drive_strength_idx_to_ua(unsigned int idx)
+{
+ if (idx >= ARRAY_SIZE(rzt2h_drive_strength_ua))
+ return -EINVAL;
+
+ return rzt2h_drive_strength_ua[idx];
+}
+
+static void rzt2h_pinctrl_drctl_rmwq(struct rzt2h_pinctrl *pctrl,
+ u32 port, u64 mask, u64 val)
+{
+ u32 offset = DRCTL(port);
+ u64 drctl;
+
+ guard(raw_spinlock_irqsave)(&pctrl->lock);
+ drctl = rzt2h_pinctrl_readq(pctrl, port, offset) & ~mask;
+ rzt2h_pinctrl_writeq(pctrl, port, drctl | val, offset);
+}
+
static int rzt2h_validate_pin(struct rzt2h_pinctrl *pctrl, unsigned int offset)
{
u8 port = RZT2H_PIN_ID_TO_PORT(offset);
@@ -443,6 +488,210 @@ static int rzt2h_dt_node_to_map(struct pinctrl_dev *pctldev,
return ret;
}
+static int rzt2h_pinctrl_pinconf_get(struct pinctrl_dev *pctldev,
+ unsigned int pin,
+ unsigned long *config)
+{
+ struct rzt2h_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ u32 port, param = pinconf_to_config_param(*config);
+ unsigned int arg;
+ u8 port_pin;
+ u64 drctl;
+ int ret;
+
+ ret = rzt2h_validate_pin(pctrl, pin);
+ if (ret)
+ return ret;
+
+ port = RZT2H_PIN_ID_TO_PORT(pin);
+ port_pin = RZT2H_PIN_ID_TO_PIN(pin);
+
+ switch (param) {
+ case PIN_CONFIG_SLEW_RATE:
+ drctl = rzt2h_pinctrl_readq(pctrl, port, DRCTL(port));
+ arg = field_get(DRCTL_SR_PIN_MASK(port_pin), drctl);
+ break;
+
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ drctl = rzt2h_pinctrl_readq(pctrl, port, DRCTL(port));
+ arg = field_get(DRCTL_PUD_PIN_MASK(port_pin), drctl);
+ /* for PIN_CONFIG_BIAS_PULL_UP/DOWN when enabled we just return 1 */
+ switch (arg) {
+ case DRCTL_PUD_NONE:
+ if (param != PIN_CONFIG_BIAS_DISABLE)
+ return -EINVAL;
+ break;
+ case DRCTL_PUD_PULL_UP:
+ if (param != PIN_CONFIG_BIAS_PULL_UP)
+ return -EINVAL;
+ arg = 1;
+ break;
+ case DRCTL_PUD_PULL_DOWN:
+ if (param != PIN_CONFIG_BIAS_PULL_DOWN)
+ return -EINVAL;
+ arg = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ drctl = rzt2h_pinctrl_readq(pctrl, port, DRCTL(port));
+ arg = field_get(DRCTL_SMT_PIN_MASK(port_pin), drctl);
+ if (!arg)
+ return -EINVAL;
+ break;
+
+ case PIN_CONFIG_DRIVE_STRENGTH_UA: {
+ int idx_drv;
+
+ drctl = rzt2h_pinctrl_readq(pctrl, port, DRCTL(port));
+ arg = field_get(DRCTL_DRV_PIN_MASK(port_pin), drctl);
+ idx_drv = rzt2h_drive_strength_idx_to_ua(arg);
+ if (idx_drv < 0)
+ return idx_drv;
+ arg = idx_drv;
+ break;
+ }
+
+ default:
+ return -ENOTSUPP;
+ }
+
+ *config = pinconf_to_config_packed(param, arg);
+ return 0;
+}
+
+static int rzt2h_pinctrl_pinconf_set(struct pinctrl_dev *pctldev,
+ unsigned int pin,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ struct rzt2h_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned int i;
+ u8 port_pin;
+ int ret;
+
+ ret = rzt2h_validate_pin(pctrl, pin);
+ if (ret)
+ return ret;
+
+ port_pin = RZT2H_PIN_ID_TO_PIN(pin);
+
+ for (i = 0; i < num_configs; i++) {
+ u32 arg = pinconf_to_config_argument(configs[i]);
+ u32 param = pinconf_to_config_param(configs[i]);
+ u64 mask, val;
+
+ switch (param) {
+ case PIN_CONFIG_SLEW_RATE:
+ mask = DRCTL_SR_PIN_MASK(port_pin);
+ val = field_prep(mask, !!arg);
+ break;
+
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ case PIN_CONFIG_BIAS_PULL_DOWN: {
+ u32 bias;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ bias = DRCTL_PUD_NONE;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ bias = DRCTL_PUD_PULL_UP;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ bias = DRCTL_PUD_PULL_DOWN;
+ break;
+ }
+
+ mask = DRCTL_PUD_PIN_MASK(port_pin);
+ val = field_prep(mask, bias);
+ break;
+ }
+
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ mask = DRCTL_SMT_PIN_MASK(port_pin);
+ val = field_prep(mask, !!arg);
+ break;
+
+ case PIN_CONFIG_DRIVE_STRENGTH_UA: {
+ int drv_idx;
+
+ drv_idx = rzt2h_drive_strength_ua_to_idx(arg);
+ if (drv_idx < 0)
+ return drv_idx;
+
+ mask = DRCTL_DRV_PIN_MASK(port_pin);
+ val = field_prep(mask, drv_idx);
+ break;
+ }
+
+ default:
+ return -ENOTSUPP;
+ }
+
+ rzt2h_pinctrl_drctl_rmwq(pctrl, RZT2H_PIN_ID_TO_PORT(pin), mask, val);
+ }
+
+ return 0;
+}
+
+static int rzt2h_pinctrl_pinconf_group_get(struct pinctrl_dev *pctldev,
+ unsigned int group,
+ unsigned long *config)
+{
+ unsigned long prev_config = 0;
+ const unsigned int *pins;
+ unsigned int i, npins;
+ int ret;
+
+ ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < npins; i++) {
+ ret = rzt2h_pinctrl_pinconf_get(pctldev, pins[i], config);
+ if (ret)
+ return ret;
+
+ /* Check config matches previous pins */
+ if (i && prev_config != *config)
+ return -ENOTSUPP;
+
+ prev_config = *config;
+ }
+
+ return 0;
+}
+
+static int rzt2h_pinctrl_pinconf_group_set(struct pinctrl_dev *pctldev,
+ unsigned int group,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ const unsigned int *pins;
+ unsigned int i, npins;
+ int ret;
+
+ ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < npins; i++) {
+ ret = rzt2h_pinctrl_pinconf_set(pctldev, pins[i], configs,
+ num_configs);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static const struct pinctrl_ops rzt2h_pinctrl_pctlops = {
.get_groups_count = pinctrl_generic_get_group_count,
.get_group_name = pinctrl_generic_get_group_name,
@@ -459,6 +708,15 @@ static const struct pinmux_ops rzt2h_pinctrl_pmxops = {
.strict = true,
};
+static const struct pinconf_ops rzt2h_pinctrl_confops = {
+ .is_generic = true,
+ .pin_config_get = rzt2h_pinctrl_pinconf_get,
+ .pin_config_set = rzt2h_pinctrl_pinconf_set,
+ .pin_config_group_set = rzt2h_pinctrl_pinconf_group_set,
+ .pin_config_group_get = rzt2h_pinctrl_pinconf_group_get,
+ .pin_config_config_dbg_show = pinconf_generic_dump_config,
+};
+
static int rzt2h_gpio_request(struct gpio_chip *chip, unsigned int offset)
{
struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip);
@@ -890,6 +1148,7 @@ static int rzt2h_pinctrl_register(struct rzt2h_pinctrl *pctrl)
desc->npins = pctrl->data->n_port_pins;
desc->pctlops = &rzt2h_pinctrl_pctlops;
desc->pmxops = &rzt2h_pinctrl_pmxops;
+ desc->confops = &rzt2h_pinctrl_confops;
desc->owner = THIS_MODULE;
pins = devm_kcalloc(dev, desc->npins, sizeof(*pins), GFP_KERNEL);
--
2.53.0
Hi Prabhakar,
On Thu, 19 Mar 2026 at 15:15, Prabhakar <prabhakar.csengg@gmail.com> wrote:
> From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
>
> Add pin configuration support for the Renesas RZ/T2H SoC. The RZ/T2H SoC
> allows configuring several electrical characteristics through the DRCTLm
> (I/O Buffer Function Switching) registers. These registers control bias
> configuration, Schmitt trigger input, output slew rate, and drive
> strength.
>
> Implement pinconf_ops to allow reading and updating these properties
> through the generic pin configuration framework. The implementation
> supports bias-disable, bias-pull-up, bias-pull-down,
> input-schmitt-enable, slew-rate, and drive-strength-microamp.
>
> Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> ---
> v1->v2:
> - Updated commit description
> - Dropped 32 bit reg access for DRCTLm registers
> - Switched using to guard for locking in rzt2h_pinctrl_drctl_rmwq
> helper function
> - Dropped using RENESAS_RZT2H_PIN_CONFIG_DRIVE_STRENGTH instead
> switched to using the standard PIN_CONFIG_DRIVE_STRENGTH_UA
Thanks for the update!
> --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c
> +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c
> @@ -54,6 +56,16 @@
> #define PFC_PIN_MASK(pin) (PFC_MASK << ((pin) * 8))
> #define PFC_FUNC_INTERRUPT 0
>
> +#define DRCTL_PIN_SHIFT(pin) ((pin) << 3)
"* 8" sounds more logical to me.
> +#define DRCTL_DRV_PIN_MASK(pin) (GENMASK_ULL(1, 0) << DRCTL_PIN_SHIFT(pin))
> +#define DRCTL_PUD_PIN_MASK(pin) (GENMASK_ULL(3, 2) << DRCTL_PIN_SHIFT(pin))
> +#define DRCTL_SMT_PIN_MASK(pin) (BIT_ULL(4) << DRCTL_PIN_SHIFT(pin))
> +#define DRCTL_SR_PIN_MASK(pin) (BIT_ULL(5) << DRCTL_PIN_SHIFT(pin))
I will drop DRCTL_PIN_SHIFT(), and replace it by "((pin) * 8)" while
applying, for consistency with e.g. PFC_PIN_MASK() above.
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
i.e. will queue in renesas-pinctrl for v7.1.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Hi Geert,
Thank you for the review.
On Thu, Mar 26, 2026 at 4:34 PM Geert Uytterhoeven <geert@linux-m68k.org> wrote:
>
> Hi Prabhakar,
>
> On Thu, 19 Mar 2026 at 15:15, Prabhakar <prabhakar.csengg@gmail.com> wrote:
> > From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> >
> > Add pin configuration support for the Renesas RZ/T2H SoC. The RZ/T2H SoC
> > allows configuring several electrical characteristics through the DRCTLm
> > (I/O Buffer Function Switching) registers. These registers control bias
> > configuration, Schmitt trigger input, output slew rate, and drive
> > strength.
> >
> > Implement pinconf_ops to allow reading and updating these properties
> > through the generic pin configuration framework. The implementation
> > supports bias-disable, bias-pull-up, bias-pull-down,
> > input-schmitt-enable, slew-rate, and drive-strength-microamp.
> >
> > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > ---
> > v1->v2:
> > - Updated commit description
> > - Dropped 32 bit reg access for DRCTLm registers
> > - Switched using to guard for locking in rzt2h_pinctrl_drctl_rmwq
> > helper function
> > - Dropped using RENESAS_RZT2H_PIN_CONFIG_DRIVE_STRENGTH instead
> > switched to using the standard PIN_CONFIG_DRIVE_STRENGTH_UA
>
> Thanks for the update!
>
> > --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c
> > +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c
>
> > @@ -54,6 +56,16 @@
> > #define PFC_PIN_MASK(pin) (PFC_MASK << ((pin) * 8))
> > #define PFC_FUNC_INTERRUPT 0
> >
> > +#define DRCTL_PIN_SHIFT(pin) ((pin) << 3)
>
> "* 8" sounds more logical to me.
>
Ok.
> > +#define DRCTL_DRV_PIN_MASK(pin) (GENMASK_ULL(1, 0) << DRCTL_PIN_SHIFT(pin))
> > +#define DRCTL_PUD_PIN_MASK(pin) (GENMASK_ULL(3, 2) << DRCTL_PIN_SHIFT(pin))
> > +#define DRCTL_SMT_PIN_MASK(pin) (BIT_ULL(4) << DRCTL_PIN_SHIFT(pin))
> > +#define DRCTL_SR_PIN_MASK(pin) (BIT_ULL(5) << DRCTL_PIN_SHIFT(pin))
>
> I will drop DRCTL_PIN_SHIFT(), and replace it by "((pin) * 8)" while
> applying, for consistency with e.g. PFC_PIN_MASK() above.
>
Ok, thank you for taking care of it.
Cheers,
Prabhakar
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> i.e. will queue in renesas-pinctrl for v7.1.
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
>
On Thu, Mar 19, 2026 at 3:15 PM Prabhakar <prabhakar.csengg@gmail.com> wrote: > From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> > > Add pin configuration support for the Renesas RZ/T2H SoC. The RZ/T2H SoC > allows configuring several electrical characteristics through the DRCTLm > (I/O Buffer Function Switching) registers. These registers control bias > configuration, Schmitt trigger input, output slew rate, and drive > strength. > > Implement pinconf_ops to allow reading and updating these properties > through the generic pin configuration framework. The implementation > supports bias-disable, bias-pull-up, bias-pull-down, > input-schmitt-enable, slew-rate, and drive-strength-microamp. > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> Reviewed-by: Linus Walleij <linusw@kernel.org> Yours, Linus Walleij
© 2016 - 2026 Red Hat, Inc.