summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Walleij <linusw@kernel.org>2026-05-11 22:43:44 +0300
committerBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>2026-05-12 12:54:37 +0300
commit806e7acf7f331008637b4f8ecf211eb0a082e6eb (patch)
tree0ecb88fd8671c5b2f7df28979bf24b5b02d02921
parent68d801eabda5219dcc25c9de98d3bbdb5b51b0a5 (diff)
downloadlinux-806e7acf7f331008637b4f8ecf211eb0a082e6eb.tar.xz
gpio: regmap: Don't set a fixed direction line
If a GPIO line has a fixed direction, report an error if a consumer anyway tries to set the direction to something other than what it is hardcoded to. This didn't happen much before because what we supported was all lines input or output and then the implementer would probably not specify the direction registers, but with sparse fixed direction we can have a mixture so let's take this into account. As a consequence, since gpio_regmap_set_direction() can now fail, alter the semantics in gpio_regmap_direction_output() such that we first check if we can set the direction to output before we set the value and the direction. Suggested-by: Sashiko <sashiko-bot@kernel.org> Link: https://sashiko.dev/#/patchset/20260507-regmap-gpio-sparse-fixed-dir-v1-1-a2e5855e2701%40kernel.org Signed-off-by: Linus Walleij <linusw@kernel.org> Reviewed-by: Michael Walle <mwalle@kernel.org> Reviewed-by: Alex Elder <elder@riscstar.com> Tested-by: Alex Elder <elder@riscstar.com> Link: https://patch.msgid.link/20260511-regmap-gpio-sparse-fixed-dir-v3-2-1429ec453be7@kernel.org Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
-rw-r--r--drivers/gpio/gpio-regmap.c37
1 files changed, 37 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index b3b4e77ec147..51b4d69b8740 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -196,6 +196,22 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
return GPIO_LINE_DIRECTION_IN;
}
+static int gpio_regmap_try_direction_fixed(struct gpio_regmap *gpio,
+ unsigned int offset, bool output)
+{
+ if (test_bit(offset, gpio->fixed_direction_output)) {
+ if (output)
+ return 0;
+ else
+ return -EINVAL;
+ } else {
+ if (output)
+ return -EINVAL;
+ else
+ return 0;
+ }
+}
+
static int gpio_regmap_set_direction(struct gpio_chip *chip,
unsigned int offset, bool output)
{
@@ -203,6 +219,13 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip,
unsigned int base, val, reg, mask;
int invert, ret;
+ /*
+ * If the direction is fixed, only accept the fixed
+ * direction in this call.
+ */
+ if (gpio_regmap_fixed_direction(gpio, offset))
+ return gpio_regmap_try_direction_fixed(gpio, offset, output);
+
if (gpio->reg_dir_out_base) {
base = gpio_regmap_addr(gpio->reg_dir_out_base);
invert = 0;
@@ -234,6 +257,20 @@ static int gpio_regmap_direction_input(struct gpio_chip *chip,
static int gpio_regmap_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
+ struct gpio_regmap *gpio = gpiochip_get_data(chip);
+ int ret;
+
+ /*
+ * First check if this is gonna work on a fixed direction line,
+ * if it doesn't (i.e. this is a fixed input line), then do not
+ * attempt to set the output value either and just bail out.
+ */
+ if (gpio_regmap_fixed_direction(gpio, offset)) {
+ ret = gpio_regmap_try_direction_fixed(gpio, offset, true);
+ if (ret)
+ return ret;
+ }
+
gpio_regmap_set(chip, offset, value);
return gpio_regmap_set_direction(chip, offset, true);