Commit 4e667a19 authored by Johan Korsnes's avatar Johan Korsnes Committed by Bartosz Golaszewski
Browse files

gpio: vf610: add locking to gpio direction functions



Add locking to `vf610_gpio_direction_input|output()` functions. Without
this locking, a race condition exists between concurrent calls to these
functions, potentially leading to incorrect GPIO direction settings.

To verify the correctness of this fix, a `trylock` patch was applied,
where after a couple of reboots the race was confirmed. I.e., one user
had to wait before acquiring the lock. With this patch the race has not
been encountered. It's worth mentioning that any type of debugging
(printing, tracing, etc.) would "resolve"/hide the issue.

Fixes: 659d8a62 ("gpio: vf610: add imx7ulp support")
Signed-off-by: default avatarJohan Korsnes <johan.korsnes@remarkable.no>
Reviewed-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Reviewed-by: default avatarHaibo Chen <haibo.chen@nxp.com>
Cc: Bartosz Golaszewski <brgl@bgdev.pl>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20250217091643.679644-1-johan.korsnes@remarkable.no


Signed-off-by: default avatarBartosz Golaszewski <bartosz.golaszewski@linaro.org>
parent 9d846b1a
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ struct vf610_gpio_port {
	struct clk *clk_port;
	struct clk *clk_gpio;
	int irq;
	spinlock_t lock; /* protect gpio direction registers */
};

#define GPIO_PDOR		0x00
@@ -124,6 +125,7 @@ static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned int gpio)
	u32 val;

	if (port->sdata->have_paddr) {
		guard(spinlock_irqsave)(&port->lock);
		val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
		val &= ~mask;
		vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
@@ -142,6 +144,7 @@ static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio
	vf610_gpio_set(chip, gpio, value);

	if (port->sdata->have_paddr) {
		guard(spinlock_irqsave)(&port->lock);
		val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
		val |= mask;
		vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
@@ -297,6 +300,7 @@ static int vf610_gpio_probe(struct platform_device *pdev)
		return -ENOMEM;

	port->sdata = device_get_match_data(dev);
	spin_lock_init(&port->lock);

	dual_base = port->sdata->have_dual_base;