mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	gpiolib: Implement fast processing path in get/set array
Certain GPIO descriptor arrays returned by gpio_get_array() may contain information on direct mapping of array members to pins of a single GPIO chip in hardware order. In such cases, bitmaps of values can be passed directly from/to the chip's .get/set_multiple() callbacks without wasting time on iterations. Add respective code to gpiod_get/set_array_bitmap_complex() functions. Pins not applicable for fast path are processed as before, skipping over the 'fast' ones. 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
							
								
									77588c14ac
								
							
						
					
					
						commit
						b17566a6b0
					
				
					 3 changed files with 105 additions and 5 deletions
				
			
		| 
						 | 
					@ -193,3 +193,18 @@ And the table can be added to the board code as follows::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The line will be hogged as soon as the gpiochip is created or - in case the
 | 
					The line will be hogged as soon as the gpiochip is created or - in case the
 | 
				
			||||||
chip was created earlier - when the hog table is registered.
 | 
					chip was created earlier - when the hog table is registered.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Arrays of pins
 | 
				
			||||||
 | 
					--------------
 | 
				
			||||||
 | 
					In addition to requesting pins belonging to a function one by one, a device may
 | 
				
			||||||
 | 
					also request an array of pins assigned to the function.  The way those pins are
 | 
				
			||||||
 | 
					mapped to the device determines if the array qualifies for fast bitmap
 | 
				
			||||||
 | 
					processing.  If yes, a bitmap is passed over get/set array functions directly
 | 
				
			||||||
 | 
					between a caller and a respective .get/set_multiple() callback of a GPIO chip.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to qualify for fast bitmap processing, the pin mapping must meet the
 | 
				
			||||||
 | 
					following requirements:
 | 
				
			||||||
 | 
					- it must belong to the same chip as other 'fast' pins of the function,
 | 
				
			||||||
 | 
					- its index within the function must match its hardware number within the chip.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Open drain and open source pins are excluded from fast bitmap output processing.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -388,6 +388,14 @@ array_info should be set to NULL.
 | 
				
			||||||
Note that for optimal performance GPIOs belonging to the same chip should be
 | 
					Note that for optimal performance GPIOs belonging to the same chip should be
 | 
				
			||||||
contiguous within the array of descriptors.
 | 
					contiguous within the array of descriptors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Still better performance may be achieved if array indexes of the descriptors
 | 
				
			||||||
 | 
					match hardware pin numbers of a single chip.  If an array passed to a get/set
 | 
				
			||||||
 | 
					array function matches the one obtained from gpiod_get_array() and array_info
 | 
				
			||||||
 | 
					associated with the array is also passed, the function may take a fast bitmap
 | 
				
			||||||
 | 
					processing path, passing the value_bitmap argument directly to the respective
 | 
				
			||||||
 | 
					.get/set_multiple() callback of the chip.  That allows for utilization of GPIO
 | 
				
			||||||
 | 
					banks as data I/O ports without much loss of performance.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The return value of gpiod_get_array_value() and its variants is 0 on success
 | 
					The return value of gpiod_get_array_value() and its variants is 0 on success
 | 
				
			||||||
or negative on error. Note the difference to gpiod_get_value(), which returns
 | 
					or negative on error. Note the difference to gpiod_get_value(), which returns
 | 
				
			||||||
0 or 1 on success to convey the GPIO value. With the array functions, the GPIO
 | 
					0 or 1 on success to convey the GPIO value. With the array functions, the GPIO
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2789,7 +2789,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
				  struct gpio_array *array_info,
 | 
									  struct gpio_array *array_info,
 | 
				
			||||||
				  unsigned long *value_bitmap)
 | 
									  unsigned long *value_bitmap)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i = 0;
 | 
						int err, i = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Validate array_info against desc_array and its size.
 | 
				
			||||||
 | 
						 * It should immediately follow desc_array if both
 | 
				
			||||||
 | 
						 * have been obtained from the same gpiod_get_array() call.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (array_info && array_info->desc == desc_array &&
 | 
				
			||||||
 | 
						    array_size <= array_info->size &&
 | 
				
			||||||
 | 
						    (void *)array_info == desc_array + array_info->size) {
 | 
				
			||||||
 | 
							if (!can_sleep)
 | 
				
			||||||
 | 
								WARN_ON(array_info->chip->can_sleep);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = gpio_chip_get_multiple(array_info->chip,
 | 
				
			||||||
 | 
										     array_info->get_mask,
 | 
				
			||||||
 | 
										     value_bitmap);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
 | 
				
			||||||
 | 
								bitmap_xor(value_bitmap, value_bitmap,
 | 
				
			||||||
 | 
									   array_info->invert_mask, array_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (bitmap_full(array_info->get_mask, array_size))
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							i = find_first_zero_bit(array_info->get_mask, array_size);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							array_info = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (i < array_size) {
 | 
						while (i < array_size) {
 | 
				
			||||||
		struct gpio_chip *chip = desc_array[i]->gdev->chip;
 | 
							struct gpio_chip *chip = desc_array[i]->gdev->chip;
 | 
				
			||||||
| 
						 | 
					@ -2820,7 +2849,12 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
			int hwgpio = gpio_chip_hwgpio(desc);
 | 
								int hwgpio = gpio_chip_hwgpio(desc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			__set_bit(hwgpio, mask);
 | 
								__set_bit(hwgpio, mask);
 | 
				
			||||||
			i++;
 | 
					
 | 
				
			||||||
 | 
								if (array_info)
 | 
				
			||||||
 | 
									find_next_zero_bit(array_info->get_mask,
 | 
				
			||||||
 | 
											   array_size, i);
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									i++;
 | 
				
			||||||
		} while ((i < array_size) &&
 | 
							} while ((i < array_size) &&
 | 
				
			||||||
			 (desc_array[i]->gdev->chip == chip));
 | 
								 (desc_array[i]->gdev->chip == chip));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2831,7 +2865,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
			return ret;
 | 
								return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (j = first; j < i; j++) {
 | 
							for (j = first; j < i; ) {
 | 
				
			||||||
			const struct gpio_desc *desc = desc_array[j];
 | 
								const struct gpio_desc *desc = desc_array[j];
 | 
				
			||||||
			int hwgpio = gpio_chip_hwgpio(desc);
 | 
								int hwgpio = gpio_chip_hwgpio(desc);
 | 
				
			||||||
			int value = test_bit(hwgpio, bits);
 | 
								int value = test_bit(hwgpio, bits);
 | 
				
			||||||
| 
						 | 
					@ -2840,6 +2874,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
				value = !value;
 | 
									value = !value;
 | 
				
			||||||
			__assign_bit(j, value_bitmap, value);
 | 
								__assign_bit(j, value_bitmap, value);
 | 
				
			||||||
			trace_gpio_value(desc_to_gpio(desc), 1, value);
 | 
								trace_gpio_value(desc_to_gpio(desc), 1, value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (array_info)
 | 
				
			||||||
 | 
									find_next_zero_bit(array_info->get_mask, i, j);
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									j++;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (mask != fastpath)
 | 
							if (mask != fastpath)
 | 
				
			||||||
| 
						 | 
					@ -3043,6 +3082,32 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i = 0;
 | 
						int i = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Validate array_info against desc_array and its size.
 | 
				
			||||||
 | 
						 * It should immediately follow desc_array if both
 | 
				
			||||||
 | 
						 * have been obtained from the same gpiod_get_array() call.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (array_info && array_info->desc == desc_array &&
 | 
				
			||||||
 | 
						    array_size <= array_info->size &&
 | 
				
			||||||
 | 
						    (void *)array_info == desc_array + array_info->size) {
 | 
				
			||||||
 | 
							if (!can_sleep)
 | 
				
			||||||
 | 
								WARN_ON(array_info->chip->can_sleep);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
 | 
				
			||||||
 | 
								bitmap_xor(value_bitmap, value_bitmap,
 | 
				
			||||||
 | 
									   array_info->invert_mask, array_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
 | 
				
			||||||
 | 
									       value_bitmap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (bitmap_full(array_info->set_mask, array_size))
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							i = find_first_zero_bit(array_info->set_mask, array_size);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							array_info = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (i < array_size) {
 | 
						while (i < array_size) {
 | 
				
			||||||
		struct gpio_chip *chip = desc_array[i]->gdev->chip;
 | 
							struct gpio_chip *chip = desc_array[i]->gdev->chip;
 | 
				
			||||||
		unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
 | 
							unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
 | 
				
			||||||
| 
						 | 
					@ -3070,7 +3135,14 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
			int hwgpio = gpio_chip_hwgpio(desc);
 | 
								int hwgpio = gpio_chip_hwgpio(desc);
 | 
				
			||||||
			int value = test_bit(i, value_bitmap);
 | 
								int value = test_bit(i, value_bitmap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
 | 
								/*
 | 
				
			||||||
 | 
								 * Pins applicable for fast input but not for
 | 
				
			||||||
 | 
								 * fast output processing may have been already
 | 
				
			||||||
 | 
								 * inverted inside the fast path, skip them.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								if (!raw && !(array_info &&
 | 
				
			||||||
 | 
								    test_bit(i, array_info->invert_mask)) &&
 | 
				
			||||||
 | 
								    test_bit(FLAG_ACTIVE_LOW, &desc->flags))
 | 
				
			||||||
				value = !value;
 | 
									value = !value;
 | 
				
			||||||
			trace_gpio_value(desc_to_gpio(desc), 0, value);
 | 
								trace_gpio_value(desc_to_gpio(desc), 0, value);
 | 
				
			||||||
			/*
 | 
								/*
 | 
				
			||||||
| 
						 | 
					@ -3089,7 +3161,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
					__clear_bit(hwgpio, bits);
 | 
										__clear_bit(hwgpio, bits);
 | 
				
			||||||
				count++;
 | 
									count++;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			i++;
 | 
					
 | 
				
			||||||
 | 
								if (array_info)
 | 
				
			||||||
 | 
									find_next_zero_bit(array_info->set_mask,
 | 
				
			||||||
 | 
											   array_size, i);
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									i++;
 | 
				
			||||||
		} while ((i < array_size) &&
 | 
							} while ((i < array_size) &&
 | 
				
			||||||
			 (desc_array[i]->gdev->chip == chip));
 | 
								 (desc_array[i]->gdev->chip == chip));
 | 
				
			||||||
		/* push collected bits to outputs */
 | 
							/* push collected bits to outputs */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue