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:
Janusz Krzysztofik
2018-09-05 23:50:08 +02:00
committed by Linus Walleij
parent 77588c14ac
commit b17566a6b0
3 changed files with 105 additions and 5 deletions

View File

@@ -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,7 +2849,12 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);
__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) &&
(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,7 +3161,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
__clear_bit(hwgpio, bits);
count++;
}
i++;
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));
/* push collected bits to outputs */