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)
 | 
										   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