[PATCH 2/7] pinctrl: renesas: rzt2h: restore correct pin mode on IRQ free

From: Cosmin Tanislav

Date: Mon Aug 17 2026 - 14:58:20 EST


rzt2h_gpio_irq_domain_free() calls rzt2h_pinctrl_set_gpio_en() with
false leaving the pin in interrupt function instead of returning it to
GPIO mode.

Pass true to rzt2h_pinctrl_set_gpio_en() to take the pin out of
interrupt function after we're done using it as an IRQ.

rzt2h_pinctrl_set_pfc_mode() switches the pin to Hi-Z, losing the
previous PM value.

Save the PM value before switching to Hi-Z, and restore it after the
IRQ is freed.

Cc: stable@xxxxxxxxxx
Fixes: 829dde3369a9 ("pinctrl: renesas: rzt2h: Add GPIO IRQ chip to handle interrupts")
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@xxxxxxxxxxx>
---
drivers/pinctrl/renesas/pinctrl-rzt2h.c | 42 +++++++++++++++++++++----
1 file changed, 36 insertions(+), 6 deletions(-)

diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c
index a37f1e399446..82fe9b5e8f72 100644
--- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c
+++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c
@@ -96,6 +96,7 @@ struct rzt2h_pinctrl {
struct gpio_chip gpio_chip;
struct pinctrl_gpio_range gpio_range;
DECLARE_BITMAP(used_irqs, RZT2H_INTERRUPTS_NUM);
+ u8 saved_pm[RZT2H_INTERRUPTS_NUM];
raw_spinlock_t lock; /* lock read/write registers */
struct mutex mutex; /* serialize adding groups and functions */
bool safety_port_enabled;
@@ -170,6 +171,25 @@ static int rzt2h_validate_pin(struct rzt2h_pinctrl *pctrl, unsigned int offset)
return (pincfg & BIT(pin)) ? 0 : -EINVAL;
}

+static u8 rzt2h_pin_read_pm(struct rzt2h_pinctrl *pctrl, u8 port, u8 pin)
+{
+ u16 reg = rzt2h_pinctrl_readw(pctrl, port, PM(port));
+
+ return field_get(PM_PIN_MASK(pin), reg);
+}
+
+static void rzt2h_pin_write_pm(struct rzt2h_pinctrl *pctrl, u8 port, u8 pin, u8 pm)
+{
+ u16 reg;
+
+ guard(raw_spinlock_irqsave)(&pctrl->lock);
+
+ reg = rzt2h_pinctrl_readw(pctrl, port, PM(port));
+ reg &= ~PM_PIN_MASK(pin);
+ reg |= (u16)pm << (pin * 2);
+ rzt2h_pinctrl_writew(pctrl, port, reg, PM(port));
+}
+
static void rzt2h_pinctrl_set_gpio_en(struct rzt2h_pinctrl *pctrl,
u8 port, u8 pin, bool en)
{
@@ -1023,16 +1043,23 @@ static int rzt2h_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
struct rzt2h_pinctrl *pctrl = gpiochip_get_data(gc);
u8 port = RZT2H_PIN_ID_TO_PORT(child);
u8 pin = RZT2H_PIN_ID_TO_PIN(child);
- u8 parent_irq;
+ u8 parent_irq, irq_idx;

parent_irq = rzt2h_gpio_irq_map[child];
if (parent_irq < RZT2H_INTERRUPTS_START)
return -EINVAL;

- if (test_and_set_bit(parent_irq - RZT2H_INTERRUPTS_START,
- pctrl->used_irqs))
+ irq_idx = parent_irq - RZT2H_INTERRUPTS_START;
+ if (test_and_set_bit(irq_idx, pctrl->used_irqs))
return -EBUSY;

+ /*
+ * rzt2h_pinctrl_set_pfc_mode() sets PM to Hi-Z before switching to the
+ * interrupt function, losing the previous PM value.
+ * Save it so it can be restored when the IRQ is freed.
+ */
+ pctrl->saved_pm[irq_idx] = rzt2h_pin_read_pm(pctrl, port, pin);
+
rzt2h_pinctrl_set_pfc_mode(pctrl, port, pin, PFC_FUNC_INTERRUPT);

*parent = parent_irq;
@@ -1050,14 +1077,17 @@ static void rzt2h_gpio_irq_domain_free(struct irq_domain *domain, unsigned int v
irq_hw_number_t hwirq = irqd_to_hwirq(d);
u8 port = RZT2H_PIN_ID_TO_PORT(hwirq);
u8 pin = RZT2H_PIN_ID_TO_PIN(hwirq);
- u8 parent_irq;
+ u8 parent_irq, irq_idx;

parent_irq = rzt2h_gpio_irq_map[hwirq];
if (parent_irq < RZT2H_INTERRUPTS_START)
return;

- if (test_and_clear_bit(parent_irq - RZT2H_INTERRUPTS_START, pctrl->used_irqs))
- rzt2h_pinctrl_set_gpio_en(pctrl, port, pin, false);
+ irq_idx = parent_irq - RZT2H_INTERRUPTS_START;
+ if (test_and_clear_bit(irq_idx, pctrl->used_irqs)) {
+ rzt2h_pin_write_pm(pctrl, port, pin, pctrl->saved_pm[irq_idx]);
+ rzt2h_pinctrl_set_gpio_en(pctrl, port, pin, true);
+ }

irq_domain_free_irqs_common(domain, virq, nr_irqs);
}
--
2.55.0