Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf9346f5 authored by Janusz Krzysztofik's avatar Janusz Krzysztofik Committed by Linus Walleij
Browse files

gpiolib: Identify arrays matching GPIO hardware



Certain GPIO array lookup results may map directly to GPIO pins of a
single GPIO chip in hardware order.  If that condition is recognized
and handled efficiently, significant performance gain of get/set array
functions may be possible.

While processing a request for an array of GPIO descriptors, identify
those which represent corresponding pins of a single GPIO chip.  Skip
over pins which require open source or open drain special processing.
Moreover, identify pins which require inversion.  Pass a pointer to
that information with the array to the caller so it can benefit from
enhanced performance as soon as get/set array functions can accept and
make efficient use of it.

Cc: Jonathan Corbet <corbet@lwn.net>
Signed-off-by: default avatarJanusz Krzysztofik <jmkrzyszt@gmail.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent b9762beb
Loading
Loading
Loading
Loading
+3 −1
Original line number Original line Diff line number Diff line
@@ -109,9 +109,11 @@ For a function using multiple GPIOs all of those can be obtained with one call::
					   enum gpiod_flags flags)
					   enum gpiod_flags flags)


This function returns a struct gpio_descs which contains an array of
This function returns a struct gpio_descs which contains an array of
descriptors::
descriptors.  It also contains a pointer to a gpiolib private structure which,
if passed back to get/set array functions, may speed up I/O proocessing::


	struct gpio_descs {
	struct gpio_descs {
		struct gpio_array *info;
		unsigned int ndescs;
		unsigned int ndescs;
		struct gpio_desc *desc[];
		struct gpio_desc *desc[];
	}
	}
+71 −1
Original line number Original line Diff line number Diff line
@@ -4174,7 +4174,9 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
{
{
	struct gpio_desc *desc;
	struct gpio_desc *desc;
	struct gpio_descs *descs;
	struct gpio_descs *descs;
	int count;
	struct gpio_array *array_info = NULL;
	struct gpio_chip *chip;
	int count, bitmap_size;


	count = gpiod_count(dev, con_id);
	count = gpiod_count(dev, con_id);
	if (count < 0)
	if (count < 0)
@@ -4190,9 +4192,77 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
			gpiod_put_array(descs);
			gpiod_put_array(descs);
			return ERR_CAST(desc);
			return ERR_CAST(desc);
		}
		}

		descs->desc[descs->ndescs] = desc;
		descs->desc[descs->ndescs] = desc;

		chip = gpiod_to_chip(desc);
		/*
		 * Select a chip of first array member
		 * whose index matches its pin hardware number
		 * as a candidate for fast bitmap processing.
		 */
		if (!array_info && gpio_chip_hwgpio(desc) == descs->ndescs) {
			struct gpio_descs *array;

			bitmap_size = BITS_TO_LONGS(chip->ngpio > count ?
						    chip->ngpio : count);

			array = kzalloc(struct_size(descs, desc, count) +
					struct_size(array_info, invert_mask,
					3 * bitmap_size), GFP_KERNEL);
			if (!array) {
				gpiod_put_array(descs);
				return ERR_PTR(-ENOMEM);
			}

			memcpy(array, descs,
			       struct_size(descs, desc, descs->ndescs + 1));
			kfree(descs);

			descs = array;
			array_info = (void *)(descs->desc + count);
			array_info->get_mask = array_info->invert_mask +
						  bitmap_size;
			array_info->set_mask = array_info->get_mask +
						  bitmap_size;

			array_info->desc = descs->desc;
			array_info->size = count;
			array_info->chip = chip;
			bitmap_set(array_info->get_mask, descs->ndescs,
				   count - descs->ndescs);
			bitmap_set(array_info->set_mask, descs->ndescs,
				   count - descs->ndescs);
			descs->info = array_info;
		}
		/*
		 * Unmark members which don't qualify for fast bitmap
		 * processing (different chip, not in hardware order)
		 */
		if (array_info && (chip != array_info->chip ||
		    gpio_chip_hwgpio(desc) != descs->ndescs)) {
			__clear_bit(descs->ndescs, array_info->get_mask);
			__clear_bit(descs->ndescs, array_info->set_mask);
		} else if (array_info) {
			/* Exclude open drain or open source from fast output */
			if (gpiochip_line_is_open_drain(chip, descs->ndescs) ||
			    gpiochip_line_is_open_source(chip, descs->ndescs))
				__clear_bit(descs->ndescs,
					    array_info->set_mask);
			/* Identify 'fast' pins which require invertion */
			if (gpiod_is_active_low(desc))
				__set_bit(descs->ndescs,
					  array_info->invert_mask);
		}

		descs->ndescs++;
		descs->ndescs++;
	}
	}
	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->get_mask, *array_info->set_mask,
			*array_info->invert_mask);
	return descs;
	return descs;
}
}
EXPORT_SYMBOL_GPL(gpiod_get_array);
EXPORT_SYMBOL_GPL(gpiod_get_array);
+9 −0
Original line number Original line Diff line number Diff line
@@ -183,6 +183,15 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
}
}
#endif
#endif


struct gpio_array {
	struct gpio_desc	**desc;
	unsigned int		size;
	struct gpio_chip	*chip;
	unsigned long		*get_mask;
	unsigned long		*set_mask;
	unsigned long		invert_mask[];
};

struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
				  unsigned int array_size,
				  unsigned int array_size,
+9 −0
Original line number Original line Diff line number Diff line
@@ -17,11 +17,20 @@ struct device;
 */
 */
struct gpio_desc;
struct gpio_desc;


/**
 * Opaque descriptor for a structure of GPIO array attributes.  This structure
 * is attached to struct gpiod_descs obtained from gpiod_get_array() and can be
 * passed back to get/set array functions in order to activate fast processing
 * path if applicable.
 */
struct gpio_array;

/**
/**
 * Struct containing an array of descriptors that can be obtained using
 * Struct containing an array of descriptors that can be obtained using
 * gpiod_get_array().
 * gpiod_get_array().
 */
 */
struct gpio_descs {
struct gpio_descs {
	struct gpio_array *info;
	unsigned int ndescs;
	unsigned int ndescs;
	struct gpio_desc *desc[];
	struct gpio_desc *desc[];
};
};