gpio fixes for v6.14-rc4
- check the return value of the get_direction() callback in struct gpio_chip - protect the multi-line get/set legs in GPIO core with SRCU - fix a race condition in gpio-vf610 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAme4kQQACgkQEacuoBRx 13Jv7Q//eHyg46Uj131uJJ7HeZq8dI0WIg6CWSfWk+IlHCbJfbnhRj189cY7pKO4 8g0ebk9Kr24BCZAkLfd1fIxJQ60dIMirH5p3NMOCppul+zSIHfKowYku+z/Ths2b YB/iQ2K6hSR6piV+hY4MFIz0HqvClMzrUqJIj6GXmsv6iCiLmwkVvNIOtZR9gilv VrCdOw7QwS9gVz1C7EuuaTd/i0jrjuHdN8PleNM24WcidrATv+oXBGOILDMh3fkR 3Akmx7ekYtJwE1d8dXEhEa8WOGukmux/lpGR/1/n3ikFYISOWKnJUt01m8+Rx+KN FJkL9GgdtHfxE6oEBmrBEb0q4Ssv4RCqwhpn7oXWyLYDm+wrdww2seeVBqE6Or9b CH2LBkB13NCA+7t/Lkr1ZJ9+d8LSDrraPJEEe5+j7bUM5UobjcWuNQ/X6z29nadP 8aBVvL5TYIIrew5RQUCBZUS5H7GF35hc2DA+GHocnSsAHkXzlb3BFwQyvAvU7Iaw XmuG529YkRZ71LW/3DL5jA/b3/LbeKBeCxhYIDF2efv024JqTAKifbv7rguLZspm VzELGSPkS0n01GRc191vFDqyuoZB0IIYgsGbSEsEzqFpi+4P0pAejYSke1MqPs6j BEgY5VwptieN50SkLX+6WBDNxs6y3eJa67G9Lg2oj/JuUsAs4NI= =1oGu -----END PGP SIGNATURE----- Merge tag 'gpio-fixes-for-v6.14-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux Pull gpio fixes from Bartosz Golaszewski: "There are two fixes for GPIO core: one adds missing retval checks to older code, while the second adds SRCU synchronization to legs in code that were missed during the big rework a few cycles back. There's also one small driver fix: - check the return value of the get_direction() callback in struct gpio_chip - protect the multi-line get/set legs in GPIO core with SRCU - fix a race condition in gpio-vf610" * tag 'gpio-fixes-for-v6.14-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: gpiolib: don't bail out if get_direction() fails in gpiochip_add_data() gpiolib: protect gpio_chip with SRCU in array_info paths in multi get/set gpio: vf610: add locking to gpio direction functions gpiolib: check the return value of gpio_chip::get_direction()
This commit is contained in:
commit
534a2c6217
3 changed files with 76 additions and 32 deletions
|
@ -36,6 +36,7 @@ struct vf610_gpio_port {
|
|||
struct clk *clk_port;
|
||||
struct clk *clk_gpio;
|
||||
int irq;
|
||||
spinlock_t lock; /* protect gpio direction registers */
|
||||
};
|
||||
|
||||
#define GPIO_PDOR 0x00
|
||||
|
@ -124,6 +125,7 @@ static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned int gpio)
|
|||
u32 val;
|
||||
|
||||
if (port->sdata->have_paddr) {
|
||||
guard(spinlock_irqsave)(&port->lock);
|
||||
val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
|
||||
val &= ~mask;
|
||||
vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
|
||||
|
@ -142,6 +144,7 @@ static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio
|
|||
vf610_gpio_set(chip, gpio, value);
|
||||
|
||||
if (port->sdata->have_paddr) {
|
||||
guard(spinlock_irqsave)(&port->lock);
|
||||
val = vf610_gpio_readl(port->gpio_base + GPIO_PDDR);
|
||||
val |= mask;
|
||||
vf610_gpio_writel(val, port->gpio_base + GPIO_PDDR);
|
||||
|
@ -297,6 +300,7 @@ static int vf610_gpio_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
port->sdata = device_get_match_data(dev);
|
||||
spin_lock_init(&port->lock);
|
||||
|
||||
dual_base = port->sdata->have_dual_base;
|
||||
|
||||
|
|
|
@ -1057,8 +1057,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
|||
desc->gdev = gdev;
|
||||
|
||||
if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) {
|
||||
assign_bit(FLAG_IS_OUT,
|
||||
&desc->flags, !gc->get_direction(gc, desc_index));
|
||||
ret = gc->get_direction(gc, desc_index);
|
||||
if (ret < 0)
|
||||
/*
|
||||
* FIXME: Bail-out here once all GPIO drivers
|
||||
* are updated to not return errors in
|
||||
* situations that can be considered normal
|
||||
* operation.
|
||||
*/
|
||||
dev_warn(&gdev->dev,
|
||||
"%s: get_direction failed: %d\n",
|
||||
__func__, ret);
|
||||
|
||||
assign_bit(FLAG_IS_OUT, &desc->flags, !ret);
|
||||
} else {
|
||||
assign_bit(FLAG_IS_OUT,
|
||||
&desc->flags, !gc->direction_input);
|
||||
|
@ -2728,13 +2739,18 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc)
|
|||
if (guard.gc->direction_input) {
|
||||
ret = guard.gc->direction_input(guard.gc,
|
||||
gpio_chip_hwgpio(desc));
|
||||
} else if (guard.gc->get_direction &&
|
||||
(guard.gc->get_direction(guard.gc,
|
||||
gpio_chip_hwgpio(desc)) != 1)) {
|
||||
gpiod_warn(desc,
|
||||
"%s: missing direction_input() operation and line is output\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
} else if (guard.gc->get_direction) {
|
||||
ret = guard.gc->get_direction(guard.gc,
|
||||
gpio_chip_hwgpio(desc));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != GPIO_LINE_DIRECTION_IN) {
|
||||
gpiod_warn(desc,
|
||||
"%s: missing direction_input() operation and line is output\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
clear_bit(FLAG_IS_OUT, &desc->flags);
|
||||
|
@ -2771,12 +2787,18 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
|
|||
gpio_chip_hwgpio(desc), val);
|
||||
} else {
|
||||
/* Check that we are in output mode if we can */
|
||||
if (guard.gc->get_direction &&
|
||||
guard.gc->get_direction(guard.gc, gpio_chip_hwgpio(desc))) {
|
||||
gpiod_warn(desc,
|
||||
"%s: missing direction_output() operation\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
if (guard.gc->get_direction) {
|
||||
ret = guard.gc->get_direction(guard.gc,
|
||||
gpio_chip_hwgpio(desc));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != GPIO_LINE_DIRECTION_OUT) {
|
||||
gpiod_warn(desc,
|
||||
"%s: missing direction_output() operation\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we can't actively set the direction, we are some
|
||||
|
@ -3129,6 +3151,8 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
|
|||
static int gpio_chip_get_multiple(struct gpio_chip *gc,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
lockdep_assert_held(&gc->gpiodev->srcu);
|
||||
|
||||
if (gc->get_multiple)
|
||||
return gc->get_multiple(gc, mask, bits);
|
||||
if (gc->get) {
|
||||
|
@ -3159,6 +3183,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
|
|||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
struct gpio_chip *gc;
|
||||
int ret, i = 0;
|
||||
|
||||
/*
|
||||
|
@ -3170,10 +3195,15 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
|
|||
array_size <= array_info->size &&
|
||||
(void *)array_info == desc_array + array_info->size) {
|
||||
if (!can_sleep)
|
||||
WARN_ON(array_info->chip->can_sleep);
|
||||
WARN_ON(array_info->gdev->can_sleep);
|
||||
|
||||
ret = gpio_chip_get_multiple(array_info->chip,
|
||||
array_info->get_mask,
|
||||
guard(srcu)(&array_info->gdev->srcu);
|
||||
gc = srcu_dereference(array_info->gdev->chip,
|
||||
&array_info->gdev->srcu);
|
||||
if (!gc)
|
||||
return -ENODEV;
|
||||
|
||||
ret = gpio_chip_get_multiple(gc, array_info->get_mask,
|
||||
value_bitmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -3454,6 +3484,8 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
|
|||
static void gpio_chip_set_multiple(struct gpio_chip *gc,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
lockdep_assert_held(&gc->gpiodev->srcu);
|
||||
|
||||
if (gc->set_multiple) {
|
||||
gc->set_multiple(gc, mask, bits);
|
||||
} else {
|
||||
|
@ -3471,6 +3503,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
|
|||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
struct gpio_chip *gc;
|
||||
int i = 0;
|
||||
|
||||
/*
|
||||
|
@ -3482,14 +3515,19 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
|
|||
array_size <= array_info->size &&
|
||||
(void *)array_info == desc_array + array_info->size) {
|
||||
if (!can_sleep)
|
||||
WARN_ON(array_info->chip->can_sleep);
|
||||
WARN_ON(array_info->gdev->can_sleep);
|
||||
|
||||
guard(srcu)(&array_info->gdev->srcu);
|
||||
gc = srcu_dereference(array_info->gdev->chip,
|
||||
&array_info->gdev->srcu);
|
||||
if (!gc)
|
||||
return -ENODEV;
|
||||
|
||||
if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
|
||||
bitmap_xor(value_bitmap, value_bitmap,
|
||||
array_info->invert_mask, array_size);
|
||||
|
||||
gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
|
||||
value_bitmap);
|
||||
gpio_chip_set_multiple(gc, array_info->set_mask, value_bitmap);
|
||||
|
||||
i = find_first_zero_bit(array_info->set_mask, array_size);
|
||||
if (i == array_size)
|
||||
|
@ -4751,9 +4789,10 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
|
|||
{
|
||||
struct gpio_desc *desc;
|
||||
struct gpio_descs *descs;
|
||||
struct gpio_device *gdev;
|
||||
struct gpio_array *array_info = NULL;
|
||||
struct gpio_chip *gc;
|
||||
int count, bitmap_size;
|
||||
unsigned long dflags;
|
||||
size_t descs_size;
|
||||
|
||||
count = gpiod_count(dev, con_id);
|
||||
|
@ -4774,7 +4813,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
|
|||
|
||||
descs->desc[descs->ndescs] = desc;
|
||||
|
||||
gc = gpiod_to_chip(desc);
|
||||
gdev = gpiod_to_gpio_device(desc);
|
||||
/*
|
||||
* If pin hardware number of array member 0 is also 0, select
|
||||
* its chip as a candidate for fast bitmap processing path.
|
||||
|
@ -4782,8 +4821,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
|
|||
if (descs->ndescs == 0 && gpio_chip_hwgpio(desc) == 0) {
|
||||
struct gpio_descs *array;
|
||||
|
||||
bitmap_size = BITS_TO_LONGS(gc->ngpio > count ?
|
||||
gc->ngpio : count);
|
||||
bitmap_size = BITS_TO_LONGS(gdev->ngpio > count ?
|
||||
gdev->ngpio : count);
|
||||
|
||||
array = krealloc(descs, descs_size +
|
||||
struct_size(array_info, invert_mask, 3 * bitmap_size),
|
||||
|
@ -4803,7 +4842,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
|
|||
|
||||
array_info->desc = descs->desc;
|
||||
array_info->size = count;
|
||||
array_info->chip = gc;
|
||||
array_info->gdev = gdev;
|
||||
bitmap_set(array_info->get_mask, descs->ndescs,
|
||||
count - descs->ndescs);
|
||||
bitmap_set(array_info->set_mask, descs->ndescs,
|
||||
|
@ -4816,7 +4855,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
|
|||
continue;
|
||||
|
||||
/* Unmark array members which don't belong to the 'fast' chip */
|
||||
if (array_info->chip != gc) {
|
||||
if (array_info->gdev != gdev) {
|
||||
__clear_bit(descs->ndescs, array_info->get_mask);
|
||||
__clear_bit(descs->ndescs, array_info->set_mask);
|
||||
}
|
||||
|
@ -4839,9 +4878,10 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
|
|||
array_info->set_mask);
|
||||
}
|
||||
} else {
|
||||
dflags = READ_ONCE(desc->flags);
|
||||
/* Exclude open drain or open source from fast output */
|
||||
if (gpiochip_line_is_open_drain(gc, descs->ndescs) ||
|
||||
gpiochip_line_is_open_source(gc, descs->ndescs))
|
||||
if (test_bit(FLAG_OPEN_DRAIN, &dflags) ||
|
||||
test_bit(FLAG_OPEN_SOURCE, &dflags))
|
||||
__clear_bit(descs->ndescs,
|
||||
array_info->set_mask);
|
||||
/* Identify 'fast' pins which require invertion */
|
||||
|
@ -4853,7 +4893,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
|
|||
if (array_info)
|
||||
dev_dbg(dev,
|
||||
"GPIO array info: chip=%s, size=%d, get_mask=%lx, set_mask=%lx, invert_mask=%lx\n",
|
||||
array_info->chip->label, array_info->size,
|
||||
array_info->gdev->label, array_info->size,
|
||||
*array_info->get_mask, *array_info->set_mask,
|
||||
*array_info->invert_mask);
|
||||
return descs;
|
||||
|
|
|
@ -114,7 +114,7 @@ extern const char *const gpio_suffixes[];
|
|||
*
|
||||
* @desc: Array of pointers to the GPIO descriptors
|
||||
* @size: Number of elements in desc
|
||||
* @chip: Parent GPIO chip
|
||||
* @gdev: Parent GPIO device
|
||||
* @get_mask: Get mask used in fastpath
|
||||
* @set_mask: Set mask used in fastpath
|
||||
* @invert_mask: Invert mask used in fastpath
|
||||
|
@ -126,7 +126,7 @@ extern const char *const gpio_suffixes[];
|
|||
struct gpio_array {
|
||||
struct gpio_desc **desc;
|
||||
unsigned int size;
|
||||
struct gpio_chip *chip;
|
||||
struct gpio_device *gdev;
|
||||
unsigned long *get_mask;
|
||||
unsigned long *set_mask;
|
||||
unsigned long invert_mask[];
|
||||
|
|
Loading…
Add table
Reference in a new issue