diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d26cad6442bf..1b4af0f97e5a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -958,6 +958,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = 0; int ret = 0; + /* Only allow one set() and one set_multiple(). */ + if ((gc->set && gc->set_rv) || + (gc->set_multiple && gc->set_multiple_rv)) + return -EINVAL; + /* * First: allocate and populate the internal stat container, and * set up the struct device. @@ -2827,11 +2832,21 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc) static int gpiochip_set(struct gpio_chip *gc, unsigned int offset, int value) { + int ret; + lockdep_assert_held(&gc->gpiodev->srcu); - if (WARN_ON(unlikely(!gc->set))) + if (WARN_ON(unlikely(!gc->set && !gc->set_rv))) return -EOPNOTSUPP; + if (gc->set_rv) { + ret = gc->set_rv(gc, offset, value); + if (ret > 0) + ret = -EBADE; + + return ret; + } + gc->set(gc, offset, value); return 0; } @@ -3593,9 +3608,17 @@ static int gpiochip_set_multiple(struct gpio_chip *gc, lockdep_assert_held(&gc->gpiodev->srcu); - if (WARN_ON(unlikely(!gc->set_multiple && !gc->set))) + if (WARN_ON(unlikely(!gc->set_multiple && !gc->set_multiple_rv))) return -EOPNOTSUPP; + if (gc->set_multiple_rv) { + ret = gc->set_multiple_rv(gc, mask, bits); + if (ret > 0) + ret = -EBADE; + + return ret; + } + if (gc->set_multiple) { gc->set_multiple(gc, mask, bits); return 0; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index ae96a2f260fb..a2a1b6434321 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -348,6 +348,10 @@ struct gpio_irq_chip { * stores them in "bits", returns 0 on success or negative error * @set: assigns output value for signal "offset" * @set_multiple: assigns output values for multiple signals defined by "mask" + * @set_rv: assigns output value for signal "offset", returns 0 on success or + * negative error value + * @set_multiple_rv: assigns output values for multiple signals defined by + * "mask", returns 0 on success or negative error value * @set_config: optional hook for all kinds of settings. Uses the same * packed config format as generic pinconf. Must return 0 on success and * a negative error number on failure. @@ -445,6 +449,12 @@ struct gpio_chip { void (*set_multiple)(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits); + int (*set_rv)(struct gpio_chip *gc, + unsigned int offset, + int value); + int (*set_multiple_rv)(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits); int (*set_config)(struct gpio_chip *gc, unsigned int offset, unsigned long config);