[PATCH v2] pinctrl: renesas: rzg2l: Fix save/restore of {IOLH,IEN,PUPD,SMT} registers

Biju posted 1 patch 1 week, 1 day ago
There is a newer version of this series
drivers/pinctrl/renesas/pinctrl-rzg2l.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
[PATCH v2] pinctrl: renesas: rzg2l: Fix save/restore of {IOLH,IEN,PUPD,SMT} registers
Posted by Biju 1 week, 1 day ago
From: Biju Das <biju.das.jz@bp.renesas.com>

The rzg2l_pinctrl_pm_setup_regs() handles save/restore of
{IOLH,IEN,PUPD,SMT} registers during s2ram, but only for ports where all
pins share the same pincfg. Extend the code to also support ports with
variable pincfg per pin, so that {IOLH,IEN,PUPD,SMT} registers are
correctly saved and restored for all pins.

Fixes: 254203f9a94c ("pinctrl: renesas: rzg2l: Add suspend/resume support")
Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
---
v1->v2:
 * Updated commit description
 * Improved the logic.
---
 drivers/pinctrl/renesas/pinctrl-rzg2l.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
index 863e779dda02..e20d199833ee 100644
--- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
+++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
@@ -3001,9 +3001,12 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen
 {
 	u32 nports = pctrl->data->n_port_pins / RZG2L_PINS_PER_PORT;
 	struct rzg2l_pinctrl_reg_cache *cache = pctrl->cache;
+	u32 pin_off = 0;
 
-	for (u32 port = 0; port < nports; port++) {
+	for (u32 port = 0; port < nports; port++, pin_off += RZG2L_PINS_PER_PORT) {
+		const struct pinctrl_pin_desc *pin_desc = &pctrl->desc.pins[pin_off];
 		bool has_iolh, has_ien, has_pupd, has_smt;
+		u64 *pin_data = pin_desc->drv_data;
 		u32 off, caps;
 		u8 pincnt;
 		u64 cfg;
@@ -3012,6 +3015,11 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen
 		off = RZG2L_PIN_CFG_TO_PORT_OFFSET(cfg);
 		pincnt = hweight8(FIELD_GET(PIN_CFG_PIN_MAP_MASK, cfg));
 
+		if (cfg & RZG2L_VARIABLE_CFG) {
+			for (unsigned int i = 1; i < RZG2L_PINS_PER_PORT; i++)
+				cfg |= *pin_data++;
+		}
+
 		caps = FIELD_GET(PIN_CFG_MASK, cfg);
 		has_iolh = !!(caps & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C));
 		has_ien = !!(caps & PIN_CFG_IEN);
-- 
2.43.0
Re: [PATCH v2] pinctrl: renesas: rzg2l: Fix save/restore of {IOLH,IEN,PUPD,SMT} registers
Posted by Geert Uytterhoeven 1 week ago
Hi Biju,

On Wed, 25 Mar 2026 at 19:28, Biju <biju.das.au@gmail.com> wrote:
> From: Biju Das <biju.das.jz@bp.renesas.com>
>
> The rzg2l_pinctrl_pm_setup_regs() handles save/restore of
> {IOLH,IEN,PUPD,SMT} registers during s2ram, but only for ports where all
> pins share the same pincfg. Extend the code to also support ports with
> variable pincfg per pin, so that {IOLH,IEN,PUPD,SMT} registers are
> correctly saved and restored for all pins.
>
> Fixes: 254203f9a94c ("pinctrl: renesas: rzg2l: Add suspend/resume support")
> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>

Thanks for the update!

> ---
> v1->v2:
>  * Updated commit description
>  * Improved the logic.

You also fixed the double application of the pin_off index, right?

> --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
> +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
> @@ -3001,9 +3001,12 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen
>  {
>         u32 nports = pctrl->data->n_port_pins / RZG2L_PINS_PER_PORT;
>         struct rzg2l_pinctrl_reg_cache *cache = pctrl->cache;
> +       u32 pin_off = 0;
>
> -       for (u32 port = 0; port < nports; port++) {
> +       for (u32 port = 0; port < nports; port++, pin_off += RZG2L_PINS_PER_PORT) {
> +               const struct pinctrl_pin_desc *pin_desc = &pctrl->desc.pins[pin_off];

Here you index pctrl->desc.pins[]...

>                 bool has_iolh, has_ien, has_pupd, has_smt;
> +               u64 *pin_data = pin_desc->drv_data;

Here you get pin_data, so below you will actually index
pctrl->desc.pins[pin_off].drv_data[].  That is not only confusing,
but also only works because pctrl->desc.pins[i].drv_data is initialized
in rzg2l_pinctrl_register() like:

    pins[i].drv_data = &pin_data[i];

All these new variables (incl. pin_off) are only used inside the new
if () below, so please confine everything to that block.

>                 u32 off, caps;
>                 u8 pincnt;
>                 u64 cfg;
> @@ -3012,6 +3015,11 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen
>                 off = RZG2L_PIN_CFG_TO_PORT_OFFSET(cfg);
>                 pincnt = hweight8(FIELD_GET(PIN_CFG_PIN_MAP_MASK, cfg));
>
> +               if (cfg & RZG2L_VARIABLE_CFG) {
> +                       for (unsigned int i = 1; i < RZG2L_PINS_PER_PORT; i++)

Shouldn't the loop start at zero? The PIN_CFG_MASK part of a variable
config is always zero.

> +                               cfg |= *pin_data++;

Please use pctrl->desc.pins[pin_off + i].drv_data here.
And pin_off can be replaced by port * RZG2L_PINS_PER_PORT.

> +               }
> +
>                 caps = FIELD_GET(PIN_CFG_MASK, cfg);
>                 has_iolh = !!(caps & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C));
>                 has_ien = !!(caps & PIN_CFG_IEN);

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