mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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: Janusz Krzysztofik <jmkrzyszt@gmail.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
		
							parent
							
								
									b9762bebc6
								
							
						
					
					
						commit
						bf9346f5d4
					
				
					 4 changed files with 92 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -109,9 +109,11 @@ For a function using multiple GPIOs all of those can be obtained with one call::
 | 
			
		|||
					   enum gpiod_flags flags)
 | 
			
		||||
 | 
			
		||||
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_array *info;
 | 
			
		||||
		unsigned int ndescs;
 | 
			
		||||
		struct gpio_desc *desc[];
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4174,7 +4174,9 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
 | 
			
		|||
{
 | 
			
		||||
	struct gpio_desc *desc;
 | 
			
		||||
	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);
 | 
			
		||||
	if (count < 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -4190,9 +4192,77 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
 | 
			
		|||
			gpiod_put_array(descs);
 | 
			
		||||
			return ERR_CAST(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++;
 | 
			
		||||
	}
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(gpiod_get_array);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -183,6 +183,15 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
 | 
			
		|||
}
 | 
			
		||||
#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);
 | 
			
		||||
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
 | 
			
		||||
				  unsigned int array_size,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,11 +17,20 @@ struct device;
 | 
			
		|||
 */
 | 
			
		||||
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
 | 
			
		||||
 * gpiod_get_array().
 | 
			
		||||
 */
 | 
			
		||||
struct gpio_descs {
 | 
			
		||||
	struct gpio_array *info;
 | 
			
		||||
	unsigned int ndescs;
 | 
			
		||||
	struct gpio_desc *desc[];
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue