mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
gpio: pcf857x: implement get_direction()
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: 15fae37d9f ("gpiolib: pcf857x i2c gpio expander support")
Signed-off-by: Tapio Reijonen <tapio.reijonen@vaisala.com>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Link: https://patch.msgid.link/20260825-b4-gpio-pcf857x-get-direction-v1-1-de15996ae354@vaisala.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
This commit is contained in:
parent
78c0baf85c
commit
ed14364ae8
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user