]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
gpio: pca953x: fix pca953x_irq_bus_sync_unlock regmap lock
authorMark Tomlinson <mark.tomlinson@alliedtelesis.co.nz>
Thu, 9 Jul 2026 04:51:16 +0000 (16:51 +1200)
committerBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Thu, 16 Jul 2026 13:54:32 +0000 (15:54 +0200)
Locking is disabled in the regmap config as this driver uses its own
lock. This means that all calls to regmap functions (read or write) must
hold the i2c_lock. The function pca953x_irq_bus_sync_unlock() did not do
this, and it was therefore possible that multiple threads could cause an
incorrect register to be read/written.

A previous patch partly fixed this, but only protected the write to the
interrupt mask register, and not the read from the direction register.

Fixes: bfc6444b57dc ("gpio: pca953x: fix pca953x_irq_bus_sync_unlock race")
Cc: stable@vger.kernel.org
Signed-off-by: Mark Tomlinson <mark.tomlinson@alliedtelesis.co.nz>
Link: https://patch.msgid.link/20260709045116.2304246-1-mark.tomlinson@alliedtelesis.co.nz
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
drivers/gpio/gpio-pca953x.c

index f6b870b7b3521639880cede3185927deb18ef0c3..e03bf1b12091001850a2f653cc9b0c979a7315d7 100644 (file)
@@ -604,20 +604,28 @@ static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long *
        return 0;
 }
 
-static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
+static int pca953x_gpio_direction_input_unlocked(struct gpio_chip *gc,
+                                                unsigned int off)
 {
        struct pca953x_chip *chip = gpiochip_get_data(gc);
        u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off);
        u8 bit = pca953x_get_bit_mask(chip, off);
 
-       guard(mutex)(&chip->i2c_lock);
-
        if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE)
                return regmap_update_bits(chip->regmap, dirreg, bit, 0);
 
        return regmap_update_bits(chip->regmap, dirreg, bit, bit);
 }
 
+static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned int off)
+{
+       struct pca953x_chip *chip = gpiochip_get_data(gc);
+
+       guard(mutex)(&chip->i2c_lock);
+
+       return pca953x_gpio_direction_input_unlocked(gc, off);
+}
+
 static int pca953x_gpio_direction_output(struct gpio_chip *gc,
                unsigned off, int val)
 {
@@ -855,9 +863,10 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
        DECLARE_BITMAP(reg_direction, MAX_LINE);
        int level;
 
+       guard(mutex)(&chip->i2c_lock);
+
        if (chip->driver_data & PCA_PCAL) {
                DECLARE_BITMAP(latched_inputs, MAX_LINE);
-               guard(mutex)(&chip->i2c_lock);
 
                /* Enable latch on edge-triggered interrupt-enabled inputs */
                bitmap_or(latched_inputs, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio);
@@ -879,7 +888,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
 
        /* Look for any newly setup interrupt */
        for_each_andnot_bit(level, irq_mask, reg_direction, gc->ngpio)
-               pca953x_gpio_direction_input(&chip->gpio_chip, level);
+               pca953x_gpio_direction_input_unlocked(&chip->gpio_chip, level);
 
        mutex_unlock(&chip->irq_lock);
 }