diff options
| author | Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com> | 2026-09-13 22:56:50 +0300 |
|---|---|---|
| committer | Geert Uytterhoeven <geert+renesas@glider.be> | 2026-09-15 10:48:51 +0200 |
| commit | 7df755632142e59a5aec5f85f2654add49220102 (patch) | |
| tree | 17aca1bcb9e149a7c6217120618ff87fc77f731a | |
| parent | 3f3fe195e7d908dc15ad7ab99f38e9225225b538 (diff) | |
| download | linux-next-7df755632142e59a5aec5f85f2654add49220102.tar.gz linux-next-7df755632142e59a5aec5f85f2654add49220102.zip | |
pinctrl: renesas: rzt2h: Restore correct pin mode on IRQ free
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@kernel.org
Fixes: 829dde3369a9 ("pinctrl: renesas: rzt2h: Add GPIO IRQ chip to handle interrupts")
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://patch.msgid.link/20260913195654.3371385-2-cosmin-gabriel.tanislav.xa@renesas.com
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
| -rw-r--r-- | drivers/pinctrl/renesas/pinctrl-rzt2h.c | 43 |
1 files changed, 37 insertions, 6 deletions
diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index d29ef107c2c1..9c1ac836ffa6 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -95,6 +95,7 @@ struct rzt2h_pinctrl { struct device *dev; struct gpio_chip gpio_chip; 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; @@ -169,6 +170,26 @@ 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, + unsigned int pm) +{ + u16 reg; + + guard(raw_spinlock_irqsave)(&pctrl->lock); + + reg = rzt2h_pinctrl_readw(pctrl, port, PM(port)); + reg &= ~PM_PIN_MASK(pin); + reg |= 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) { @@ -1022,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; @@ -1049,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); } |
