summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSander Vanheule <sander@svanheule.net>2025-10-21 17:23:56 +0300
committerBartosz Golaszewski <bartosz.golaszewski@linaro.org>2025-10-23 15:05:26 +0300
commited2bd02d24947e36c9438bee1449d9bf87671b16 (patch)
treedfb4678ee036c2d78fbd877291da2085deb9f6a8
parentd5376026f9269601e239545e2ec4aea0cc62bf2a (diff)
downloadlinux-ed2bd02d24947e36c9438bee1449d9bf87671b16.tar.xz
gpio: regmap: Force writes for aliased data regs
GPIO chips often have data input and output fields aliased to the same offset. Since gpio-regmap performs a value update before the direction update (to prevent glitches), a pin currently configured as input may cause regmap_update_bits() to not perform a write. This may cause unexpected line states when the current input state equals the requested output state: OUT IN OUT DIR ''''''\...|.../'''''' pin ....../'''|'''\...... (1) (2) (3) 1. Line was configurad as out-low, but is reconfigured to input. External logic results in high value. 2. Set output value high. regmap_update_bits() sees the value is already high and discards the register write. 3. Line is switched to output, maintaining the stale output config (low) instead of the requested config (high). By switching to regmap_write_bits(), a write of the requested output value can be forced, irrespective of the read state. Do this only for aliased registers, so the more efficient regmap_update_bits() can still be used for distinct registers. Signed-off-by: Sander Vanheule <sander@svanheule.net> Reviewed-by: Michael Walle <mwalle@kernel.org> Link: https://lore.kernel.org/r/20251021142407.307753-2-sander@svanheule.net Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
-rw-r--r--drivers/gpio/gpio-regmap.c12
1 files changed, 9 insertions, 3 deletions
diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index f4267af00027..08f42ac4b89d 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -94,7 +94,7 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
unsigned int base = gpio_regmap_addr(gpio->reg_set_base);
- unsigned int reg, mask;
+ unsigned int reg, mask, mask_val;
int ret;
ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
@@ -102,9 +102,15 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
return ret;
if (val)
- ret = regmap_update_bits(gpio->regmap, reg, mask, mask);
+ mask_val = mask;
else
- ret = regmap_update_bits(gpio->regmap, reg, mask, 0);
+ mask_val = 0;
+
+ /* ignore input values which shadow the old output value */
+ if (gpio->reg_dat_base == gpio->reg_set_base)
+ ret = regmap_write_bits(gpio->regmap, reg, mask, mask_val);
+ else
+ ret = regmap_update_bits(gpio->regmap, reg, mask, mask_val);
return ret;
}