linux-gpio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
@ 2025-09-08 14:42 Claudiu
  2025-09-11  9:53 ` Geert Uytterhoeven
  2025-09-11 10:43 ` Biju Das
  0 siblings, 2 replies; 7+ messages in thread
From: Claudiu @ 2025-09-08 14:42 UTC (permalink / raw)
  To: geert+renesas, linus.walleij, biju.das.jz
  Cc: claudiu.beznea, linux-renesas-soc, linux-gpio, linux-kernel,
	Claudiu Beznea, stable

From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

Commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL in
gpio_irq_{en,dis}able*()") dropped the configuration of ISEL from
rzg2l_gpio_irq_enable()/rzg2l_gpio_irq_disable() and moved it to
rzg2l_gpio_child_to_parent_hwirq()/rzg2l_gpio_irq_domain_free() to fix
spurious IRQs.

The resume code used rzg2l_gpio_irq_enable() (called from
rzg2l_gpio_irq_restore()) to reconfigure the wakeup interrupts. Some
drivers (e.g. Ethernet) may also reconfigure interrupts in their own code,
eventually calling rzg2l_gpio_irq_enable(), when these are not wakeup
interrupts.

After commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL
in gpio_irq_{en,dis}able*()"), ISEL was no longer configured properly after
resume.

Fix this by adding rzg2l_gpio_irq_endisable() back into
rzg2l_gpio_irq_enable(), and by using its unlocked variant in
rzg2l_gpio_irq_restore(). Having IRQs enable in rzg2l_gpio_irq_enable()
should be safe with respect to spurious IRQs, as in the probe case IRQs are
enabled anyway in rzg2l_gpio_child_to_parent_hwirq(). No spurious IRQs
were detected on suspend/resume tests (executed on RZ/G3S).

Fixes: 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL in gpio_irq_{en,dis}able*(")
Cc: stable@vger.kernel.org
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
 drivers/pinctrl/renesas/pinctrl-rzg2l.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
index b182b3b8a542..6ae1ee3ffc81 100644
--- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
+++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
@@ -2428,7 +2428,7 @@ static int rzg2l_gpio_get_gpioint(unsigned int virq, struct rzg2l_pinctrl *pctrl
 }
 
 static void rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
-				     unsigned int hwirq, bool enable)
+				     unsigned int hwirq, bool enable, bool lock)
 {
 	const struct pinctrl_pin_desc *pin_desc = &pctrl->desc.pins[hwirq];
 	u64 *pin_data = pin_desc->drv_data;
@@ -2443,12 +2443,16 @@ static void rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
 		addr += 4;
 	}
 
-	spin_lock_irqsave(&pctrl->lock, flags);
+	if (lock)
+		spin_lock_irqsave(&pctrl->lock, flags);
+
 	if (enable)
 		writel(readl(addr) | BIT(bit * 8), addr);
 	else
 		writel(readl(addr) & ~BIT(bit * 8), addr);
-	spin_unlock_irqrestore(&pctrl->lock, flags);
+
+	if (lock)
+		spin_unlock_irqrestore(&pctrl->lock, flags);
 }
 
 static void rzg2l_gpio_irq_disable(struct irq_data *d)
@@ -2460,15 +2464,22 @@ static void rzg2l_gpio_irq_disable(struct irq_data *d)
 	gpiochip_disable_irq(gc, hwirq);
 }
 
-static void rzg2l_gpio_irq_enable(struct irq_data *d)
+static void rzg2l_gpio_irq_enable_helper(struct irq_data *d, bool lock)
 {
 	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct rzg2l_pinctrl *pctrl = container_of(gc, struct rzg2l_pinctrl, gpio_chip);
 	unsigned int hwirq = irqd_to_hwirq(d);
 
 	gpiochip_enable_irq(gc, hwirq);
+	rzg2l_gpio_irq_endisable(pctrl, hwirq, true, lock);
 	irq_chip_enable_parent(d);
 }
 
+static void rzg2l_gpio_irq_enable(struct irq_data *d)
+{
+	rzg2l_gpio_irq_enable_helper(d, true);
+}
+
 static int rzg2l_gpio_irq_set_type(struct irq_data *d, unsigned int type)
 {
 	return irq_chip_set_type_parent(d, type);
@@ -2570,7 +2581,7 @@ static int rzg2l_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
 		goto err;
 	}
 
-	rzg2l_gpio_irq_endisable(pctrl, child, true);
+	rzg2l_gpio_irq_endisable(pctrl, child, true, true);
 	pctrl->hwirq[irq] = child;
 	irq += pctrl->data->hwcfg->tint_start_index;
 
@@ -2617,7 +2628,7 @@ static void rzg2l_gpio_irq_restore(struct rzg2l_pinctrl *pctrl)
 		spin_lock_irqsave(&pctrl->lock, flags);
 		ret = rzg2l_gpio_irq_set_type(data, irqd_get_trigger_type(data));
 		if (!ret && !irqd_irq_disabled(data))
-			rzg2l_gpio_irq_enable(data);
+			rzg2l_gpio_irq_enable_helper(data, false);
 		spin_unlock_irqrestore(&pctrl->lock, flags);
 
 		if (ret)
@@ -2640,7 +2651,7 @@ static void rzg2l_gpio_irq_domain_free(struct irq_domain *domain, unsigned int v
 
 		for (i = 0; i < RZG2L_TINT_MAX_INTERRUPT; i++) {
 			if (pctrl->hwirq[i] == hwirq) {
-				rzg2l_gpio_irq_endisable(pctrl, hwirq, false);
+				rzg2l_gpio_irq_endisable(pctrl, hwirq, false, true);
 				rzg2l_gpio_free(gc, hwirq);
 				spin_lock_irqsave(&pctrl->bitmap_lock, flags);
 				bitmap_release_region(pctrl->tint_slot, i, get_order(1));
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
  2025-09-08 14:42 [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume Claudiu
@ 2025-09-11  9:53 ` Geert Uytterhoeven
  2025-09-11 13:19   ` Claudiu Beznea
  2025-09-11 10:43 ` Biju Das
  1 sibling, 1 reply; 7+ messages in thread
From: Geert Uytterhoeven @ 2025-09-11  9:53 UTC (permalink / raw)
  To: Claudiu
  Cc: linus.walleij, biju.das.jz, linux-renesas-soc, linux-gpio,
	linux-kernel, Claudiu Beznea, stable

Hi Claudiu,

On Mon, 8 Sept 2025 at 16:42, Claudiu <claudiu.beznea@tuxon.dev> wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>
> Commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL in
> gpio_irq_{en,dis}able*()") dropped the configuration of ISEL from
> rzg2l_gpio_irq_enable()/rzg2l_gpio_irq_disable() and moved it to
> rzg2l_gpio_child_to_parent_hwirq()/rzg2l_gpio_irq_domain_free() to fix
> spurious IRQs.
>
> The resume code used rzg2l_gpio_irq_enable() (called from
> rzg2l_gpio_irq_restore()) to reconfigure the wakeup interrupts. Some
> drivers (e.g. Ethernet) may also reconfigure interrupts in their own code,
> eventually calling rzg2l_gpio_irq_enable(), when these are not wakeup
> interrupts.
>
> After commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL
> in gpio_irq_{en,dis}able*()"), ISEL was no longer configured properly after
> resume.
>
> Fix this by adding rzg2l_gpio_irq_endisable() back into
> rzg2l_gpio_irq_enable(), and by using its unlocked variant in
> rzg2l_gpio_irq_restore(). Having IRQs enable in rzg2l_gpio_irq_enable()

enabled

> should be safe with respect to spurious IRQs, as in the probe case IRQs are
> enabled anyway in rzg2l_gpio_child_to_parent_hwirq(). No spurious IRQs
> were detected on suspend/resume tests (executed on RZ/G3S).
>
> Fixes: 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL in gpio_irq_{en,dis}able*(")
> Cc: stable@vger.kernel.org
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>

Thanks for your patch!

I have to admit I don't fully understand what is going on...

> --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
> +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
> @@ -2428,7 +2428,7 @@ static int rzg2l_gpio_get_gpioint(unsigned int virq, struct rzg2l_pinctrl *pctrl
>  }
>
>  static void rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
> -                                    unsigned int hwirq, bool enable)
> +                                    unsigned int hwirq, bool enable, bool lock)
>  {
>         const struct pinctrl_pin_desc *pin_desc = &pctrl->desc.pins[hwirq];
>         u64 *pin_data = pin_desc->drv_data;
> @@ -2443,12 +2443,16 @@ static void rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
>                 addr += 4;
>         }
>
> -       spin_lock_irqsave(&pctrl->lock, flags);
> +       if (lock)
> +               spin_lock_irqsave(&pctrl->lock, flags);
> +
>         if (enable)
>                 writel(readl(addr) | BIT(bit * 8), addr);
>         else
>                 writel(readl(addr) & ~BIT(bit * 8), addr);
> -       spin_unlock_irqrestore(&pctrl->lock, flags);
> +
> +       if (lock)
> +               spin_unlock_irqrestore(&pctrl->lock, flags);
>  }

I am not so fond of these "if (lock) ..."-constructs, especially as
the function now takes two bool parameters, which is error-prone.

What about renaming rzg2l_gpio_irq_endisable() to
__rzg2l_gpio_irq_endisable(), and moving the locking to a wrapper
rzg2l_gpio_irq_endisable()?

    static void __rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
                                         unsigned int hwirq, bool enable)
    {
            /* old functionality without locking */
            ...
    }

    static void rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
                                        unsigned int hwirq, bool enable)
    {
            unsigned long flags;

            spin_lock_irqsave(&pctrl->lock, flags);
            __rzg2l_gpio_irq_endisable(pctrl, hwirq, enable);
            spin_unlock_irqrestore(&pctrl->lock, flags);
    }

Then no existing callers of rzg2l_gpio_irq_endisable() need to be
changed.

> @@ -2460,15 +2464,22 @@ static void rzg2l_gpio_irq_disable(struct irq_data *d)
>         gpiochip_disable_irq(gc, hwirq);
>  }
>
> -static void rzg2l_gpio_irq_enable(struct irq_data *d)
> +static void rzg2l_gpio_irq_enable_helper(struct irq_data *d, bool lock)

Here we can't do without the "lock" parameter, unless duplicating the
full body, so this is fine.  I'd rename it to __rzg2l_gpio_irq_enable(),
though.

>  {
>         struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +       struct rzg2l_pinctrl *pctrl = container_of(gc, struct rzg2l_pinctrl, gpio_chip);
>         unsigned int hwirq = irqd_to_hwirq(d);
>
>         gpiochip_enable_irq(gc, hwirq);
> +       rzg2l_gpio_irq_endisable(pctrl, hwirq, true, lock);

if (lock)
    rzg2l_gpio_irq_endisable(pctrl, hwirq, true);
else
    __rzg2l_gpio_irq_endisable(pctrl, hwirq, true);

>         irq_chip_enable_parent(d);
>  }
>
> +static void rzg2l_gpio_irq_enable(struct irq_data *d)
> +{
> +       rzg2l_gpio_irq_enable_helper(d, true);

__rzg2l_gpio_irq_enable(d, true);

> +}
> +
>  static int rzg2l_gpio_irq_set_type(struct irq_data *d, unsigned int type)
>  {
>         return irq_chip_set_type_parent(d, type);
> @@ -2617,7 +2628,7 @@ static void rzg2l_gpio_irq_restore(struct rzg2l_pinctrl *pctrl)
>                 spin_lock_irqsave(&pctrl->lock, flags);
>                 ret = rzg2l_gpio_irq_set_type(data, irqd_get_trigger_type(data));
>                 if (!ret && !irqd_irq_disabled(data))
> -                       rzg2l_gpio_irq_enable(data);
> +                       rzg2l_gpio_irq_enable_helper(data, false);

__rzg2l_gpio_irq_enable(data, false);

Before, the lock was taken again, while it was already held.
Didn't this cause a deadlock?

>                 spin_unlock_irqrestore(&pctrl->lock, flags);
>
>                 if (ret)

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
  2025-09-08 14:42 [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume Claudiu
  2025-09-11  9:53 ` Geert Uytterhoeven
@ 2025-09-11 10:43 ` Biju Das
  2025-09-11 13:23   ` Claudiu Beznea
  1 sibling, 1 reply; 7+ messages in thread
From: Biju Das @ 2025-09-11 10:43 UTC (permalink / raw)
  To: Claudiu.Beznea, geert+renesas@glider.be, linus.walleij@linaro.org
  Cc: Claudiu.Beznea, linux-renesas-soc@vger.kernel.org,
	linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org,
	Claudiu Beznea, stable@vger.kernel.org

Hi Claudiu,

> -----Original Message-----
> From: Claudiu <claudiu.beznea@tuxon.dev>
> Sent: 08 September 2025 15:43
> Subject: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
> 
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> 
> Commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL in
> gpio_irq_{en,dis}able*()") dropped the configuration of ISEL from
> rzg2l_gpio_irq_enable()/rzg2l_gpio_irq_disable() and moved it to
> rzg2l_gpio_child_to_parent_hwirq()/rzg2l_gpio_irq_domain_free() to fix spurious IRQs.
> 
> The resume code used rzg2l_gpio_irq_enable() (called from
> rzg2l_gpio_irq_restore()) to reconfigure the wakeup interrupts. Some drivers (e.g. Ethernet) may also
> reconfigure interrupts in their own code, eventually calling rzg2l_gpio_irq_enable(), when these are
> not wakeup interrupts.
> 
> After commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL in
> gpio_irq_{en,dis}able*()"), ISEL was no longer configured properly after resume.
> 
> Fix this by adding rzg2l_gpio_irq_endisable() back into rzg2l_gpio_irq_enable(), and by using its
> unlocked variant in rzg2l_gpio_irq_restore(). Having IRQs enable in rzg2l_gpio_irq_enable() should be
> safe with respect to spurious IRQs, as in the probe case IRQs are enabled anyway in
> rzg2l_gpio_child_to_parent_hwirq(). No spurious IRQs were detected on suspend/resume tests (executed on
> RZ/G3S).

IIRC, I believe the issue is ISEL is not restored during resume. Can we restore this register just like
Schmitt register suspend/restore[1]

[1] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?h=next-20250911&id=837afa592c6234be82acb5d23e0a39e9befdaa85

Cheers,
Biju

> 
> Fixes: 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL in gpio_irq_{en,dis}able*(")
> Cc: stable@vger.kernel.org
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
>  drivers/pinctrl/renesas/pinctrl-rzg2l.c | 25 ++++++++++++++++++-------
>  1 file changed, 18 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
> index b182b3b8a542..6ae1ee3ffc81 100644
> --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
> +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
> @@ -2428,7 +2428,7 @@ static int rzg2l_gpio_get_gpioint(unsigned int virq, struct rzg2l_pinctrl
> *pctrl  }
> 
>  static void rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
> -				     unsigned int hwirq, bool enable)
> +				     unsigned int hwirq, bool enable, bool lock)
>  {
>  	const struct pinctrl_pin_desc *pin_desc = &pctrl->desc.pins[hwirq];
>  	u64 *pin_data = pin_desc->drv_data;
> @@ -2443,12 +2443,16 @@ static void rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
>  		addr += 4;
>  	}
> 
> -	spin_lock_irqsave(&pctrl->lock, flags);
> +	if (lock)
> +		spin_lock_irqsave(&pctrl->lock, flags);
> +
>  	if (enable)
>  		writel(readl(addr) | BIT(bit * 8), addr);
>  	else
>  		writel(readl(addr) & ~BIT(bit * 8), addr);
> -	spin_unlock_irqrestore(&pctrl->lock, flags);
> +
> +	if (lock)
> +		spin_unlock_irqrestore(&pctrl->lock, flags);
>  }
> 
>  static void rzg2l_gpio_irq_disable(struct irq_data *d) @@ -2460,15 +2464,22 @@ static void
> rzg2l_gpio_irq_disable(struct irq_data *d)
>  	gpiochip_disable_irq(gc, hwirq);
>  }
> 
> -static void rzg2l_gpio_irq_enable(struct irq_data *d)
> +static void rzg2l_gpio_irq_enable_helper(struct irq_data *d, bool lock)
>  {
>  	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +	struct rzg2l_pinctrl *pctrl = container_of(gc, struct rzg2l_pinctrl,
> +gpio_chip);
>  	unsigned int hwirq = irqd_to_hwirq(d);
> 
>  	gpiochip_enable_irq(gc, hwirq);
> +	rzg2l_gpio_irq_endisable(pctrl, hwirq, true, lock);
>  	irq_chip_enable_parent(d);
>  }
> 
> +static void rzg2l_gpio_irq_enable(struct irq_data *d) {
> +	rzg2l_gpio_irq_enable_helper(d, true); }
> +
>  static int rzg2l_gpio_irq_set_type(struct irq_data *d, unsigned int type)  {
>  	return irq_chip_set_type_parent(d, type); @@ -2570,7 +2581,7 @@ static int
> rzg2l_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
>  		goto err;
>  	}
> 
> -	rzg2l_gpio_irq_endisable(pctrl, child, true);
> +	rzg2l_gpio_irq_endisable(pctrl, child, true, true);
>  	pctrl->hwirq[irq] = child;
>  	irq += pctrl->data->hwcfg->tint_start_index;
> 
> @@ -2617,7 +2628,7 @@ static void rzg2l_gpio_irq_restore(struct rzg2l_pinctrl *pctrl)
>  		spin_lock_irqsave(&pctrl->lock, flags);
>  		ret = rzg2l_gpio_irq_set_type(data, irqd_get_trigger_type(data));
>  		if (!ret && !irqd_irq_disabled(data))
> -			rzg2l_gpio_irq_enable(data);
> +			rzg2l_gpio_irq_enable_helper(data, false);
>  		spin_unlock_irqrestore(&pctrl->lock, flags);
> 
>  		if (ret)
> @@ -2640,7 +2651,7 @@ static void rzg2l_gpio_irq_domain_free(struct irq_domain *domain, unsigned int v
> 
>  		for (i = 0; i < RZG2L_TINT_MAX_INTERRUPT; i++) {
>  			if (pctrl->hwirq[i] == hwirq) {
> -				rzg2l_gpio_irq_endisable(pctrl, hwirq, false);
> +				rzg2l_gpio_irq_endisable(pctrl, hwirq, false, true);
>  				rzg2l_gpio_free(gc, hwirq);
>  				spin_lock_irqsave(&pctrl->bitmap_lock, flags);
>  				bitmap_release_region(pctrl->tint_slot, i, get_order(1));
> --
> 2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
  2025-09-11  9:53 ` Geert Uytterhoeven
@ 2025-09-11 13:19   ` Claudiu Beznea
  0 siblings, 0 replies; 7+ messages in thread
From: Claudiu Beznea @ 2025-09-11 13:19 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: linus.walleij, biju.das.jz, linux-renesas-soc, linux-gpio,
	linux-kernel, Claudiu Beznea, stable

Hi, Geert,

On 9/11/25 12:53, Geert Uytterhoeven wrote:
> Hi Claudiu,
> 
> On Mon, 8 Sept 2025 at 16:42, Claudiu <claudiu.beznea@tuxon.dev> wrote:
>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>
>> Commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL in
>> gpio_irq_{en,dis}able*()") dropped the configuration of ISEL from
>> rzg2l_gpio_irq_enable()/rzg2l_gpio_irq_disable() and moved it to
>> rzg2l_gpio_child_to_parent_hwirq()/rzg2l_gpio_irq_domain_free() to fix
>> spurious IRQs.
>>
>> The resume code used rzg2l_gpio_irq_enable() (called from
>> rzg2l_gpio_irq_restore()) to reconfigure the wakeup interrupts. Some
>> drivers (e.g. Ethernet) may also reconfigure interrupts in their own code,
>> eventually calling rzg2l_gpio_irq_enable(), when these are not wakeup
>> interrupts.
>>
>> After commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL
>> in gpio_irq_{en,dis}able*()"), ISEL was no longer configured properly after
>> resume.
>>
>> Fix this by adding rzg2l_gpio_irq_endisable() back into
>> rzg2l_gpio_irq_enable(), and by using its unlocked variant in
>> rzg2l_gpio_irq_restore(). Having IRQs enable in rzg2l_gpio_irq_enable()
> 
> enabled
> 
>> should be safe with respect to spurious IRQs, as in the probe case IRQs are
>> enabled anyway in rzg2l_gpio_child_to_parent_hwirq(). No spurious IRQs
>> were detected on suspend/resume tests (executed on RZ/G3S).
>>
>> Fixes: 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL in gpio_irq_{en,dis}able*(")
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> 
> Thanks for your patch!
> 
> I have to admit I don't fully understand what is going on...

Sorry about that. Basically, ISEL is not properly configured as a result of
removing rzg2l_gpio_irq_endisable() from rzg2l_gpio_irq_enable() which was
previously called on interrupt reconfiguration path.

> 
>> --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
>> +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
>> @@ -2428,7 +2428,7 @@ static int rzg2l_gpio_get_gpioint(unsigned int virq, struct rzg2l_pinctrl *pctrl
>>  }
>>
>>  static void rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
>> -                                    unsigned int hwirq, bool enable)
>> +                                    unsigned int hwirq, bool enable, bool lock)
>>  {
>>         const struct pinctrl_pin_desc *pin_desc = &pctrl->desc.pins[hwirq];
>>         u64 *pin_data = pin_desc->drv_data;
>> @@ -2443,12 +2443,16 @@ static void rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
>>                 addr += 4;
>>         }
>>
>> -       spin_lock_irqsave(&pctrl->lock, flags);
>> +       if (lock)
>> +               spin_lock_irqsave(&pctrl->lock, flags);
>> +
>>         if (enable)
>>                 writel(readl(addr) | BIT(bit * 8), addr);
>>         else
>>                 writel(readl(addr) & ~BIT(bit * 8), addr);
>> -       spin_unlock_irqrestore(&pctrl->lock, flags);
>> +
>> +       if (lock)
>> +               spin_unlock_irqrestore(&pctrl->lock, flags);
>>  }
> 
> I am not so fond of these "if (lock) ..."-constructs, especially as
> the function now takes two bool parameters, which is error-prone.
> 
> What about renaming rzg2l_gpio_irq_endisable() to
> __rzg2l_gpio_irq_endisable(), and moving the locking to a wrapper
> rzg2l_gpio_irq_endisable()?

That was my other option but, if I remember correctly, it generated
duplicated code, thus I ended up with this.

> 
>     static void __rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
>                                          unsigned int hwirq, bool enable)
>     {
>             /* old functionality without locking */
>             ...
>     }
> 
>     static void rzg2l_gpio_irq_endisable(struct rzg2l_pinctrl *pctrl,
>                                         unsigned int hwirq, bool enable)
>     {
>             unsigned long flags;
> 
>             spin_lock_irqsave(&pctrl->lock, flags);
>             __rzg2l_gpio_irq_endisable(pctrl, hwirq, enable);
>             spin_unlock_irqrestore(&pctrl->lock, flags);
>     }
> 
> Then no existing callers of rzg2l_gpio_irq_endisable() need to be
> changed.
> 
>> @@ -2460,15 +2464,22 @@ static void rzg2l_gpio_irq_disable(struct irq_data *d)
>>         gpiochip_disable_irq(gc, hwirq);
>>  }
>>
>> -static void rzg2l_gpio_irq_enable(struct irq_data *d)
>> +static void rzg2l_gpio_irq_enable_helper(struct irq_data *d, bool lock)
> 
> Here we can't do without the "lock" parameter, unless duplicating the
> full body, so this is fine.  I'd rename it to __rzg2l_gpio_irq_enable(),
> though.
> 
>>  {
>>         struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
>> +       struct rzg2l_pinctrl *pctrl = container_of(gc, struct rzg2l_pinctrl, gpio_chip);
>>         unsigned int hwirq = irqd_to_hwirq(d);
>>
>>         gpiochip_enable_irq(gc, hwirq);
>> +       rzg2l_gpio_irq_endisable(pctrl, hwirq, true, lock);
> 
> if (lock)
>     rzg2l_gpio_irq_endisable(pctrl, hwirq, true);
> else
>     __rzg2l_gpio_irq_endisable(pctrl, hwirq, true);
> 
>>         irq_chip_enable_parent(d);
>>  }
>>
>> +static void rzg2l_gpio_irq_enable(struct irq_data *d)
>> +{
>> +       rzg2l_gpio_irq_enable_helper(d, true);
> 
> __rzg2l_gpio_irq_enable(d, true);
> 
>> +}
>> +
>>  static int rzg2l_gpio_irq_set_type(struct irq_data *d, unsigned int type)
>>  {
>>         return irq_chip_set_type_parent(d, type);
>> @@ -2617,7 +2628,7 @@ static void rzg2l_gpio_irq_restore(struct rzg2l_pinctrl *pctrl)
>>                 spin_lock_irqsave(&pctrl->lock, flags);
>>                 ret = rzg2l_gpio_irq_set_type(data, irqd_get_trigger_type(data));
>>                 if (!ret && !irqd_irq_disabled(data))
>> -                       rzg2l_gpio_irq_enable(data);
>> +                       rzg2l_gpio_irq_enable_helper(data, false);
> 
> __rzg2l_gpio_irq_enable(data, false);
> 
> Before, the lock was taken again, while it was already held.
> Didn't this cause a deadlock?

The only locking issue I've seen around this code was fixed by commit
a39741d38c04 ("pinctrl: renesas: rzg2l: Use
spin_{lock,unlock}_irq{save,restore}"

I'll use the approach proposed by you in the next version.

Thank you for your review,
Claudiu

> 
>>                 spin_unlock_irqrestore(&pctrl->lock, flags);
>>
>>                 if (ret)
> 
> Gr{oetje,eeting}s,
> 
>                         Geert
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
  2025-09-11 10:43 ` Biju Das
@ 2025-09-11 13:23   ` Claudiu Beznea
  2025-09-11 13:39     ` Biju Das
  0 siblings, 1 reply; 7+ messages in thread
From: Claudiu Beznea @ 2025-09-11 13:23 UTC (permalink / raw)
  To: Biju Das, geert+renesas@glider.be, linus.walleij@linaro.org
  Cc: linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org,
	linux-kernel@vger.kernel.org, Claudiu Beznea,
	stable@vger.kernel.org

Hi, Biju,

On 9/11/25 13:43, Biju Das wrote:
> Hi Claudiu,
> 
>> -----Original Message-----
>> From: Claudiu <claudiu.beznea@tuxon.dev>
>> Sent: 08 September 2025 15:43
>> Subject: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
>>
>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>
>> Commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL in
>> gpio_irq_{en,dis}able*()") dropped the configuration of ISEL from
>> rzg2l_gpio_irq_enable()/rzg2l_gpio_irq_disable() and moved it to
>> rzg2l_gpio_child_to_parent_hwirq()/rzg2l_gpio_irq_domain_free() to fix spurious IRQs.
>>
>> The resume code used rzg2l_gpio_irq_enable() (called from
>> rzg2l_gpio_irq_restore()) to reconfigure the wakeup interrupts. Some drivers (e.g. Ethernet) may also
>> reconfigure interrupts in their own code, eventually calling rzg2l_gpio_irq_enable(), when these are
>> not wakeup interrupts.
>>
>> After commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL in
>> gpio_irq_{en,dis}able*()"), ISEL was no longer configured properly after resume.
>>
>> Fix this by adding rzg2l_gpio_irq_endisable() back into rzg2l_gpio_irq_enable(), and by using its
>> unlocked variant in rzg2l_gpio_irq_restore(). Having IRQs enable in rzg2l_gpio_irq_enable() should be
>> safe with respect to spurious IRQs, as in the probe case IRQs are enabled anyway in
>> rzg2l_gpio_child_to_parent_hwirq(). No spurious IRQs were detected on suspend/resume tests (executed on
>> RZ/G3S).
> 
> IIRC, I believe the issue is ISEL is not restored during resume.

Yes

> Can we restore this register just like
> Schmitt register suspend/restore[1]

The IA55 would have to be configured for interrupts as well. Doing it in
other order will lead to spurious interrupts while resuming. The commit
254203f9a94c ("pinctrl: renesas: rzg2l: Add suspend/resume support") that
introduced this approach, mentions the following:

    Because interrupt signals are routed to IA55 interrupt controller and
    IA55 interrupt controller resumes before pin controller, patch restores
    also the configured interrupts just after pin settings are restored to
    avoid invalid interrupts while resuming.

Thank you,
Claudiu

> 
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?h=next-20250911&id=837afa592c6234be82acb5d23e0a39e9befdaa85
> 
> Cheers,
> Biju
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
  2025-09-11 13:23   ` Claudiu Beznea
@ 2025-09-11 13:39     ` Biju Das
  2025-09-11 13:51       ` Claudiu Beznea
  0 siblings, 1 reply; 7+ messages in thread
From: Biju Das @ 2025-09-11 13:39 UTC (permalink / raw)
  To: Claudiu.Beznea, geert+renesas@glider.be, linus.walleij@linaro.org
  Cc: linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org,
	linux-kernel@vger.kernel.org, Claudiu Beznea,
	stable@vger.kernel.org

HI Caludiu,

> -----Original Message-----
> From: Claudiu Beznea <claudiu.beznea@tuxon.dev>
> Sent: 11 September 2025 14:24
> Subject: Re: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
> 
> Hi, Biju,
> 
> On 9/11/25 13:43, Biju Das wrote:
> > Hi Claudiu,
> >
> >> -----Original Message-----
> >> From: Claudiu <claudiu.beznea@tuxon.dev>
> >> Sent: 08 September 2025 15:43
> >> Subject: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
> >>
> >> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> >>
> >> Commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL
> >> in
> >> gpio_irq_{en,dis}able*()") dropped the configuration of ISEL from
> >> rzg2l_gpio_irq_enable()/rzg2l_gpio_irq_disable() and moved it to
> >> rzg2l_gpio_child_to_parent_hwirq()/rzg2l_gpio_irq_domain_free() to fix spurious IRQs.
> >>
> >> The resume code used rzg2l_gpio_irq_enable() (called from
> >> rzg2l_gpio_irq_restore()) to reconfigure the wakeup interrupts. Some
> >> drivers (e.g. Ethernet) may also reconfigure interrupts in their own
> >> code, eventually calling rzg2l_gpio_irq_enable(), when these are not wakeup interrupts.
> >>
> >> After commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid
> >> configuring ISEL in gpio_irq_{en,dis}able*()"), ISEL was no longer configured properly after resume.
> >>
> >> Fix this by adding rzg2l_gpio_irq_endisable() back into
> >> rzg2l_gpio_irq_enable(), and by using its unlocked variant in
> >> rzg2l_gpio_irq_restore(). Having IRQs enable in
> >> rzg2l_gpio_irq_enable() should be safe with respect to spurious IRQs,
> >> as in the probe case IRQs are enabled anyway in rzg2l_gpio_child_to_parent_hwirq(). No spurious IRQs
> were detected on suspend/resume tests (executed on RZ/G3S).
> >
> > IIRC, I believe the issue is ISEL is not restored during resume.
> 
> Yes
> 
> > Can we restore this register just like Schmitt register
> > suspend/restore[1]
> 
> The IA55 would have to be configured for interrupts as well. Doing it in other order will lead to
> spurious interrupts while resuming. The commit 254203f9a94c ("pinctrl: renesas: rzg2l: Add
> suspend/resume support") that introduced this approach, mentions the following:
> 
>     Because interrupt signals are routed to IA55 interrupt controller and
>     IA55 interrupt controller resumes before pin controller, patch restores
>     also the configured interrupts just after pin settings are restored to
>     avoid invalid interrupts while resuming.

OK. So enable/disable Keep ISEL configuration as it is, so the pin gpio int always.
Which commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoidconfiguring ISEL in gpio_irq_{en,dis}able*()")
is doing.

The new addition is suspend/resume restores ISEL along with reconfiguring interrupts.

Is it correct?

Cheers,
Biju




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
  2025-09-11 13:39     ` Biju Das
@ 2025-09-11 13:51       ` Claudiu Beznea
  0 siblings, 0 replies; 7+ messages in thread
From: Claudiu Beznea @ 2025-09-11 13:51 UTC (permalink / raw)
  To: Biju Das, geert+renesas@glider.be, linus.walleij@linaro.org
  Cc: linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org,
	linux-kernel@vger.kernel.org, Claudiu Beznea,
	stable@vger.kernel.org



On 9/11/25 16:39, Biju Das wrote:
> HI Caludiu,
> 
>> -----Original Message-----
>> From: Claudiu Beznea <claudiu.beznea@tuxon.dev>
>> Sent: 11 September 2025 14:24
>> Subject: Re: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
>>
>> Hi, Biju,
>>
>> On 9/11/25 13:43, Biju Das wrote:
>>> Hi Claudiu,
>>>
>>>> -----Original Message-----
>>>> From: Claudiu <claudiu.beznea@tuxon.dev>
>>>> Sent: 08 September 2025 15:43
>>>> Subject: [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume
>>>>
>>>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>>>
>>>> Commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid configuring ISEL
>>>> in
>>>> gpio_irq_{en,dis}able*()") dropped the configuration of ISEL from
>>>> rzg2l_gpio_irq_enable()/rzg2l_gpio_irq_disable() and moved it to
>>>> rzg2l_gpio_child_to_parent_hwirq()/rzg2l_gpio_irq_domain_free() to fix spurious IRQs.
>>>>
>>>> The resume code used rzg2l_gpio_irq_enable() (called from
>>>> rzg2l_gpio_irq_restore()) to reconfigure the wakeup interrupts. Some
>>>> drivers (e.g. Ethernet) may also reconfigure interrupts in their own
>>>> code, eventually calling rzg2l_gpio_irq_enable(), when these are not wakeup interrupts.
>>>>
>>>> After commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoid
>>>> configuring ISEL in gpio_irq_{en,dis}able*()"), ISEL was no longer configured properly after resume.
>>>>
>>>> Fix this by adding rzg2l_gpio_irq_endisable() back into
>>>> rzg2l_gpio_irq_enable(), and by using its unlocked variant in
>>>> rzg2l_gpio_irq_restore(). Having IRQs enable in
>>>> rzg2l_gpio_irq_enable() should be safe with respect to spurious IRQs,
>>>> as in the probe case IRQs are enabled anyway in rzg2l_gpio_child_to_parent_hwirq(). No spurious IRQs
>> were detected on suspend/resume tests (executed on RZ/G3S).
>>>
>>> IIRC, I believe the issue is ISEL is not restored during resume.
>>
>> Yes
>>
>>> Can we restore this register just like Schmitt register
>>> suspend/restore[1]
>>
>> The IA55 would have to be configured for interrupts as well. Doing it in other order will lead to
>> spurious interrupts while resuming. The commit 254203f9a94c ("pinctrl: renesas: rzg2l: Add
>> suspend/resume support") that introduced this approach, mentions the following:
>>
>>     Because interrupt signals are routed to IA55 interrupt controller and
>>     IA55 interrupt controller resumes before pin controller, patch restores
>>     also the configured interrupts just after pin settings are restored to
>>     avoid invalid interrupts while resuming.
> 
> OK. So enable/disable Keep ISEL configuration as it is, so the pin gpio int always.

Yes

> Which commit 1d2da79708cb ("pinctrl: renesas: rzg2l: Avoidconfiguring ISEL in gpio_irq_{en,dis}able*()")
> is doing.
> 
> The new addition is suspend/resume restores ISEL along with reconfiguring interrupts.
> 
> Is it correct?

This commit only fixes the ISEL restore on resume. The rest of interrupt
reconfiguration on resume was in place from previous commits.

Thank you,
Claudiu

> 
> Cheers,
> Biju
> 
> 
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2025-09-11 13:51 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-08 14:42 [PATCH] pinctrl: renesas: rzg2l: Fix ISEL restore on resume Claudiu
2025-09-11  9:53 ` Geert Uytterhoeven
2025-09-11 13:19   ` Claudiu Beznea
2025-09-11 10:43 ` Biju Das
2025-09-11 13:23   ` Claudiu Beznea
2025-09-11 13:39     ` Biju Das
2025-09-11 13:51       ` Claudiu Beznea

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).