[PATCH RESEND] gpio: pcf857x: implement get_direction()

Tapio Reijonen posted 1 patch 1 month ago
drivers/gpio/gpio-pcf857x.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
[PATCH RESEND] gpio: pcf857x: implement get_direction()
Posted by Tapio Reijonen 1 month ago
The GPIO core warns (and taints the kernel) when a gpiochip lacks
.get_direction() and a consumer queries a line's direction, for example
via /sys/kernel/debug/gpio. pcf857x provided direction_input/output but
no get_direction.

These quasi-bidirectional expanders cannot report direction in hardware,
and the 'out' software latch alone is ambiguous - a released (input)
line and an output driven high both read back as a set bit. Track the
direction explicitly in a 'dir' latch updated by the direction_input(),
direction_output() and set_multiple() paths, and return it from
get_direction(). Initialise it from the same reset state as 'out':
released lines are inputs, lines flagged in the power-on latch are
driven-low outputs.

Fixes: 15fae37d9f5f ("gpiolib: pcf857x i2c gpio expander support")
Signed-off-by: Tapio Reijonen <tapio.reijonen@vaisala.com>
Reviewed-by: Linus Walleij <linusw@kernel.org>
---
Found and HW-tested on an i.MX6 SoloX board with a pcf8574 I2C expander:
without this, "cat /sys/kernel/debug/gpio" triggers the gpiolib.c:429
WARNING on each requested line; with it the lines report their in/out
direction and the WARNING is gone.

Resend of v1 - the patch itself is unchanged, only rebased onto current
gpio/for-next and carrying Linus' Reviewed-by. It does not appear to
have been picked up:
https://lore.kernel.org/linux-gpio/20260604-b4-gpio-pcf857x-get-direction-v1-1-d9b13a7b4478@vaisala.com/
---
 drivers/gpio/gpio-pcf857x.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index c1f5e10a3c202672ef026e1b444112dd2759d42f..1ba6a09542f3c667dd623a88278b93afd6e93e50 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -71,8 +71,9 @@ MODULE_DEVICE_TABLE(of, pcf857x_of_table);
 struct pcf857x {
 	struct gpio_chip	chip;
 	struct i2c_client	*client;
-	struct mutex		lock;		/* protect 'out' */
+	struct mutex		lock;		/* protect 'out' and 'dir' */
 	unsigned int		out;		/* software latch */
+	unsigned int		dir;		/* direction latch (1 = input) */
 	unsigned int		status;		/* current status */
 	unsigned int		irq_enabled;	/* enabled irqs */
 
@@ -126,12 +127,21 @@ static int pcf857x_input(struct gpio_chip *chip, unsigned int offset)
 
 	mutex_lock(&gpio->lock);
 	gpio->out |= (1 << offset);
+	gpio->dir |= (1 << offset);
 	status = gpio->write(gpio->client, gpio->out);
 	mutex_unlock(&gpio->lock);
 
 	return status;
 }
 
+static int pcf857x_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+	struct pcf857x *gpio = gpiochip_get_data(chip);
+
+	return (gpio->dir & (1 << offset)) ? GPIO_LINE_DIRECTION_IN
+					   : GPIO_LINE_DIRECTION_OUT;
+}
+
 static int pcf857x_get(struct gpio_chip *chip, unsigned int offset)
 {
 	struct pcf857x *gpio = gpiochip_get_data(chip);
@@ -167,6 +177,7 @@ static int pcf857x_output(struct gpio_chip *chip, unsigned int offset, int value
 		gpio->out |= bit;
 	else
 		gpio->out &= ~bit;
+	gpio->dir &= ~bit;
 	status = gpio->write(gpio->client, gpio->out);
 	mutex_unlock(&gpio->lock);
 
@@ -187,6 +198,7 @@ static int pcf857x_set_multiple(struct gpio_chip *chip, unsigned long *mask,
 	mutex_lock(&gpio->lock);
 	gpio->out &= ~*mask;
 	gpio->out |= *bits & *mask;
+	gpio->dir &= ~*mask;
 	status = gpio->write(gpio->client, gpio->out);
 	mutex_unlock(&gpio->lock);
 
@@ -301,6 +313,7 @@ static int pcf857x_probe(struct i2c_client *client)
 	gpio->chip.set_multiple		= pcf857x_set_multiple;
 	gpio->chip.direction_input	= pcf857x_input;
 	gpio->chip.direction_output	= pcf857x_output;
+	gpio->chip.get_direction	= pcf857x_get_direction;
 	gpio->chip.ngpio		= (uintptr_t)i2c_get_match_data(client);
 
 	reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
@@ -396,6 +409,7 @@ static int pcf857x_probe(struct i2c_client *client)
 	 * reset state.  Otherwise it flags pins to be driven low.
 	 */
 	gpio->out = ~n_latch;
+	gpio->dir = ~n_latch;
 	gpio->status = gpio->read(gpio->client);
 
 	/* Enable irqchip if we have an interrupt */

---
base-commit: 146cc263e457ff6055fe7829e4f4f4b0b5d5dd86
change-id: 20260603-b4-gpio-pcf857x-get-direction-6f636aec468f

Best regards,
-- 
Tapio Reijonen <tapio.reijonen@vaisala.com>
Re: [PATCH RESEND] gpio: pcf857x: implement get_direction()
Posted by Bartosz Golaszewski 1 month ago
On Tue, 25 Aug 2026 04:15:57 +0000, Tapio Reijonen wrote:
> The GPIO core warns (and taints the kernel) when a gpiochip lacks
> .get_direction() and a consumer queries a line's direction, for example
> via /sys/kernel/debug/gpio. pcf857x provided direction_input/output but
> no get_direction.
> 
> These quasi-bidirectional expanders cannot report direction in hardware,
> and the 'out' software latch alone is ambiguous - a released (input)
> line and an output driven high both read back as a set bit. Track the
> direction explicitly in a 'dir' latch updated by the direction_input(),
> direction_output() and set_multiple() paths, and return it from
> get_direction(). Initialise it from the same reset state as 'out':
> released lines are inputs, lines flagged in the power-on latch are
> driven-low outputs.
> 
> [...]

Applied, thanks!

[1/1] gpio: pcf857x: implement get_direction()
      https://git.kernel.org/brgl/c/ed14364ae8c310cc3f3f649ffcbc7f82befe1579

Best regards,
-- 
Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>