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
|
@ -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
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
|
@ -2789,7 +2789,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
|
|||
struct gpio_array *array_info,
|
||||
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) {
|
||||
struct gpio_chip *chip = desc_array[i]->gdev->chip;
|
||||
|
@ -2820,6 +2849,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
|
|||
int hwgpio = gpio_chip_hwgpio(desc);
|
||||
|
||||
__set_bit(hwgpio, mask);
|
||||
|
||||
if (array_info)
|
||||
find_next_zero_bit(array_info->get_mask,
|
||||
array_size, i);
|
||||
else
|
||||
i++;
|
||||
} while ((i < array_size) &&
|
||||
(desc_array[i]->gdev->chip == chip));
|
||||
|
@ -2831,7 +2865,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
|
|||
return ret;
|
||||
}
|
||||
|
||||
for (j = first; j < i; j++) {
|
||||
for (j = first; j < i; ) {
|
||||
const struct gpio_desc *desc = desc_array[j];
|
||||
int hwgpio = gpio_chip_hwgpio(desc);
|
||||
int value = test_bit(hwgpio, bits);
|
||||
|
@ -2840,6 +2874,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
|
|||
value = !value;
|
||||
__assign_bit(j, value_bitmap, 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)
|
||||
|
@ -3043,6 +3082,32 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
|
|||
{
|
||||
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) {
|
||||
struct gpio_chip *chip = desc_array[i]->gdev->chip;
|
||||
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 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;
|
||||
trace_gpio_value(desc_to_gpio(desc), 0, value);
|
||||
/*
|
||||
|
@ -3089,6 +3161,11 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
|
|||
__clear_bit(hwgpio, bits);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (array_info)
|
||||
find_next_zero_bit(array_info->set_mask,
|
||||
array_size, i);
|
||||
else
|
||||
i++;
|
||||
} while ((i < array_size) &&
|
||||
(desc_array[i]->gdev->chip == chip));
|
||||
|
|
Loading…
Reference in a new issue