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) | 					   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[]; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |  | ||||||
|  | @ -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, | ||||||
|  |  | ||||||
|  | @ -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[]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Janusz Krzysztofik
						Janusz Krzysztofik