summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Villeneuve <hvilleneuve@dimonoff.com>2023-08-07 17:45:55 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2023-09-06 21:28:39 +0100
commit907216337bea333fff47fa5aa2efaf8ce4c460b9 (patch)
tree35f08777699272650995093ac7d1fc98ed4fb3d0
parent45d9611ebff25d74d7e7bc824cfc729140c7ba44 (diff)
serial: sc16is7xx: fix bug when first setting GPIO direction
commit 9baeea723c0fb9c3ba9a336369f758ed9bc6831d upstream. When configuring a pin as an output pin with a value of logic 0, we end up as having a value of logic 1 on the output pin. Setting a logic 0 a second time (or more) after that will correctly output a logic 0 on the output pin. By default, all GPIO pins are configured as inputs. When we enter sc16is7xx_gpio_direction_output() for the first time, we first set the desired value in IOSTATE, and then we configure the pin as an output. The datasheet states that writing to IOSTATE register will trigger a transfer of the value to the I/O pin configured as output, so if the pin is configured as an input, nothing will be transferred. Therefore, set the direction first in IODIR, and then set the desired value in IOSTATE. This is what is done in NXP application note AN10587. Fixes: dfeae619d781 ("serial: sc16is7xx") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Link: https://lore.kernel.org/r/20230807214556.540627-6-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/serial/sc16is7xx.c11
1 files changed, 10 insertions, 1 deletions
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 0dc66c17dc56..e8f8a94ad606 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -1170,9 +1170,18 @@ static int sc16is7xx_gpio_direction_output(struct gpio_chip *chip,
state |= BIT(offset);
else
state &= ~BIT(offset);
- sc16is7xx_port_write(port, SC16IS7XX_IOSTATE_REG, state);
+
+ /*
+ * If we write IOSTATE first, and then IODIR, the output value is not
+ * transferred to the corresponding I/O pin.
+ * The datasheet states that each register bit will be transferred to
+ * the corresponding I/O pin programmed as output when writing to
+ * IOSTATE. Therefore, configure direction first with IODIR, and then
+ * set value after with IOSTATE.
+ */
sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset),
BIT(offset));
+ sc16is7xx_port_write(port, SC16IS7XX_IOSTATE_REG, state);
return 0;
}