|
@@ -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 */
|