wt., 10 wrz 2019 o 10:22 Bartosz Golaszewski <brgl@xxxxxxxx> napisał(a): > > From: Bartosz Golaszewski <bgolaszewski@xxxxxxxxxxxx> > > When emulating open-drain/open-source by not actively driving the output > lines - we're simply changing their mode to input. This is wrong as it > will then make it impossible to change the value of such line - it's now > considered to actually be in input mode. If we want to still use the > direction_input() callback for simplicity then we need to set FLAG_IS_OUT > manually in gpiod_direction_output() and not clear it in > gpio_set_open_drain_value_commit() and > gpio_set_open_source_value_commit(). > > Fixes: c663e5f56737 ("gpio: support native single-ended hardware drivers") > Cc: stable@xxxxxxxxxxxxxxx > Reported-by: Kent Gibson <warthog618@xxxxxxxxx> > Signed-off-by: Bartosz Golaszewski <bgolaszewski@xxxxxxxxxxxx> > --- > drivers/gpio/gpiolib.c | 27 +++++++++++++++++++-------- > 1 file changed, 19 insertions(+), 8 deletions(-) > > diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c > index cca749010cd0..6bb4191d3844 100644 > --- a/drivers/gpio/gpiolib.c > +++ b/drivers/gpio/gpiolib.c > @@ -2769,8 +2769,10 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) > if (!ret) > goto set_output_value; > /* Emulate open drain by not actively driving the line high */ > - if (value) > - return gpiod_direction_input(desc); > + if (value) { > + ret = gpiod_direction_input(desc); > + goto set_output_flag; > + } > } > else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) { > ret = gpio_set_config(gc, gpio_chip_hwgpio(desc), > @@ -2778,8 +2780,10 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) > if (!ret) > goto set_output_value; > /* Emulate open source by not actively driving the line low */ > - if (!value) > - return gpiod_direction_input(desc); > + if (!value) { > + ret = gpiod_direction_input(desc); > + goto set_output_flag; > + } > } else { > gpio_set_config(gc, gpio_chip_hwgpio(desc), > PIN_CONFIG_DRIVE_PUSH_PULL); > @@ -2787,6 +2791,17 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) > > set_output_value: > return gpiod_direction_output_raw_commit(desc, value); > + > +set_output_flag: > + /* > + * When emulating open-source or open-drain functionalities by not > + * actively driving the line (setting mode to input) we still need to > + * set the IS_OUT flag or otherwise we won't be able to set the line > + * value anymore. > + */ > + if (ret == 0) > + set_bit(FLAG_IS_OUT, &desc->flags); > + return ret; > } > EXPORT_SYMBOL_GPL(gpiod_direction_output); > > @@ -3147,8 +3162,6 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) > > if (value) { > err = chip->direction_input(chip, offset); > - if (!err) > - clear_bit(FLAG_IS_OUT, &desc->flags); > } else { > err = chip->direction_output(chip, offset, 0); > if (!err) > @@ -3178,8 +3191,6 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value > set_bit(FLAG_IS_OUT, &desc->flags); > } else { > err = chip->direction_input(chip, offset); > - if (!err) > - clear_bit(FLAG_IS_OUT, &desc->flags); > } > trace_gpio_direction(desc_to_gpio(desc), !value, err); > if (err < 0) > -- > 2.21.0 > Queued for fixes. Bart