Commit 534a2c62 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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()
parents 33442609 96fa9ec4
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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;

+70 −30
Original line number Diff line number Diff line
@@ -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,14 +2739,19 @@ 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)) {
	} 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);
		ret = gpio_set_bias(desc);
@@ -2771,13 +2787,19 @@ 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))) {
		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
		 * output-only chip, so just drive the output as desired.
@@ -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;
+2 −2
Original line number Diff line number Diff line
@@ -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[];