forked from mirrors/linux
		
	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
	
	 Janusz Krzysztofik
						Janusz Krzysztofik