Skip to content
This repository has been archived by the owner on Apr 10, 2024. It is now read-only.

Commit

Permalink
gpio: ts4900: Do not set DAT and OE together
Browse files Browse the repository at this point in the history
[ Upstream commit 03fe003547975680fdb9ff5ab0e41cb68276c4f2 ]

This works around an issue with the hardware where both OE and
DAT are exposed in the same register. If both are updated
simultaneously, the harware makes no guarantees that OE or DAT
will actually change in any given order and may result in a
glitch of a few ns on a GPIO pin when changing direction and value
in a single write.

Setting direction to input now only affects OE bit. Setting
direction to output updates DAT first, then OE.

Fixes: 9c66863 ("gpio: add Technologic I2C-FPGA gpio support")
Signed-off-by: Mark Featherston <[email protected]>
Signed-off-by: Kris Bahnsen <[email protected]>
Signed-off-by: Bartosz Golaszewski <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
  • Loading branch information
markfeathers authored and gregkh committed Mar 16, 2022
1 parent 205c4ec commit 0c9a947
Showing 1 changed file with 19 additions and 5 deletions.
24 changes: 19 additions & 5 deletions drivers/gpio/gpio-ts4900.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Digital I/O driver for Technologic Systems I2C FPGA Core
*
* Copyright (C) 2015 Technologic Systems
* Copyright (C) 2015, 2018 Technologic Systems
* Copyright (C) 2016 Savoir-Faire Linux
*
* This program is free software; you can redistribute it and/or
Expand Down Expand Up @@ -52,19 +52,33 @@ static int ts4900_gpio_direction_input(struct gpio_chip *chip,
{
struct ts4900_gpio_priv *priv = gpiochip_get_data(chip);

/*
* This will clear the output enable bit, the other bits are
* dontcare when this is cleared
/* Only clear the OE bit here, requires a RMW. Prevents potential issue
* with OE and data getting to the physical pin at different times.
*/
return regmap_write(priv->regmap, offset, 0);
return regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OE, 0);
}

static int ts4900_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct ts4900_gpio_priv *priv = gpiochip_get_data(chip);
unsigned int reg;
int ret;

/* If changing from an input to an output, we need to first set the
* proper data bit to what is requested and then set OE bit. This
* prevents a glitch that can occur on the IO line
*/
regmap_read(priv->regmap, offset, &reg);
if (!(reg & TS4900_GPIO_OE)) {
if (value)
reg = TS4900_GPIO_OUT;
else
reg &= ~TS4900_GPIO_OUT;

regmap_write(priv->regmap, offset, reg);
}

if (value)
ret = regmap_write(priv->regmap, offset, TS4900_GPIO_OE |
TS4900_GPIO_OUT);
Expand Down

0 comments on commit 0c9a947

Please sign in to comment.