[PATCH] gpio: pcf857x: implement get_direction()
From: Tapio Reijonen
Date: Thu Jun 04 2026 - 01:25:59 EST
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@xxxxxxxxxxx>
---
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.
---
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 3b9de8c3d924c62da5fff4db6e613b98eed235be..026bae735a577eeedc96dd3b494b55d2d85bc66e 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -70,8 +70,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 */
@@ -124,12 +125,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);
@@ -165,6 +175,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);
@@ -185,6 +196,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);
@@ -299,6 +311,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);
@@ -394,6 +407,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: e7ae89a0c97ce2b68b0983cd01eda67cf373517d
change-id: 20260603-b4-gpio-pcf857x-get-direction-6f636aec468f
Best regards,
--
Tapio Reijonen <tapio.reijonen@xxxxxxxxxxx>