[PATCH 1/5] pinctrl: sunxi: Rework IRQ remuxing to avoid fixed mux value

From: Andre Przywara

Date: Mon Mar 23 2026 - 07:10:11 EST


Some Allwinner SoCs cannot read the state of a GPIO line when the pin is
muxed to the IRQ function. To access that state anyway, we temporarily
mux that pin back to GPIO input, then return it to the IRQ mux
afterwards. This code assumes that the IRQ mux value is 0x6, even though
newer SoCs (D1/T113/A523/...) encode the IRQ mux with 0xe.

Avoid hardcoding the different IRQ mux values by saving the programmed
value before switching to GPIO input, then restoring the saved value
afterwards. This makes the code robust against future changes of the IRQ
mux value. This also avoids calling the sunxi_pmx_set() function twice,
each of which does a read/modify/write operation, fenced in by the pctl
lock. The new code takes the lock around the whole operation, which is
also safer since it avoids (probably theoretical) races against other
code touching the mux register meanwhile.

Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx>
---
drivers/pinctrl/sunxi/pinctrl-sunxi.c | 23 ++++++++++++++++-------
drivers/pinctrl/sunxi/pinctrl-sunxi.h | 1 -
2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index d3042e0c9712..6a86b7989b25 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -997,18 +997,27 @@ static int sunxi_pinctrl_gpio_get(struct gpio_chip *chip, unsigned offset)
struct sunxi_pinctrl *pctl = gpiochip_get_data(chip);
bool set_mux = pctl->desc->irq_read_needs_mux &&
gpiochip_line_is_irq(chip, offset);
- u32 pin = offset + chip->base;
+ u32 mreg, mshift, mmask, mval;
u32 reg, shift, mask, val;
+ unsigned long flags;

sunxi_data_reg(pctl, offset, &reg, &shift, &mask);
+ if (!set_mux)
+ return (readl(pctl->membase + reg) & mask) >> shift;

- if (set_mux)
- sunxi_pmx_set(pctl->pctl_dev, pin, SUN4I_FUNC_INPUT);
-
+ /*
+ * Some SoCs don't read the GPIO value registers correctly
+ * when the pinmux is not set to GPIO_INPUT. Temporarily switch
+ * to that mux, to read the correct value.
+ */
+ sunxi_mux_reg(pctl, offset, &mreg, &mshift, &mmask);
+ raw_spin_lock_irqsave(&pctl->lock, flags);
+ mval = readl(pctl->membase + mreg);
+ writel((mval & ~mmask) | SUN4I_FUNC_INPUT << mshift,
+ pctl->membase + mreg);
val = (readl(pctl->membase + reg) & mask) >> shift;
-
- if (set_mux)
- sunxi_pmx_set(pctl->pctl_dev, pin, SUN4I_FUNC_IRQ);
+ writel(mval, pctl->membase + mreg);
+ raw_spin_unlock_irqrestore(&pctl->lock, flags);

return val;
}
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
index 0daf7600e2fb..ec7c977655b5 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
@@ -85,7 +85,6 @@
#define IO_BIAS_MASK GENMASK(3, 0)

#define SUN4I_FUNC_INPUT 0
-#define SUN4I_FUNC_IRQ 6
#define SUN4I_FUNC_DISABLED_OLD 7
#define SUN4I_FUNC_DISABLED_NEW 15

--
2.43.0