gpiolib: allow simultaneous setting of multiple GPIO outputs
Introduce new functions gpiod_set_array & gpiod_set_raw_array to the consumer interface which allow setting multiple outputs with just one function call. Also add an optional set_multiple function to the driver interface. Without an implementation of that function in the chip driver outputs are set sequentially. Implementing the set_multiple function in a chip driver allows for: - Improved performance for certain use cases. The original motivation for this was the task of configuring an FPGA. In that specific case, where 9 GPIO lines have to be set many times, configuration time goes down from 48 s to 20 s when using the new function. - Simultaneous glitch-free setting of multiple pins on any kind of parallel bus attached to GPIOs provided they all reside on the same chip and bank. Limitations: Performance is only improved for normal high-low outputs. Open drain and open source outputs are always set separately from each other. Those kinds of outputs could probably be accelerated in a similar way if we could forgo the error checking when setting GPIO directions. Change log: v6: - rebase on current linux-gpio devel branch v5: - check can_sleep property per chip - remove superfluous checks - supplement documentation v4: - add gpiod_set_array function for setting logical values - change interface of the set_multiple driver function to use unsigned long as type for the bit fields - use generic bitops (which also use unsigned long for bit fields) - do not use ARCH_NR_GPIOS any more v3: - add documentation - change commit message v2: - use descriptor interface - allow arbitrary groups of GPIOs spanning multiple chips Signed-off-by: Rojhalat Ibrahim <imr@rtschenk.de> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Mark Brown <broonie@linaro.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
parent
b5b7b48743
commit
5f42424354
@ -199,6 +199,33 @@ The active-low state of a GPIO can also be queried using the following call:
|
|||||||
Note that these functions should only be used with great moderation ; a driver
|
Note that these functions should only be used with great moderation ; a driver
|
||||||
should not have to care about the physical line level.
|
should not have to care about the physical line level.
|
||||||
|
|
||||||
|
|
||||||
|
Set multiple GPIO outputs with a single function call
|
||||||
|
-----------------------------------------------------
|
||||||
|
The following functions set the output values of an array of GPIOs:
|
||||||
|
|
||||||
|
void gpiod_set_array(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array)
|
||||||
|
void gpiod_set_raw_array(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array)
|
||||||
|
void gpiod_set_array_cansleep(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array)
|
||||||
|
void gpiod_set_raw_array_cansleep(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array)
|
||||||
|
|
||||||
|
The array can be an arbitrary set of GPIOs. The functions will try to set
|
||||||
|
GPIOs belonging to the same bank or chip simultaneously if supported by the
|
||||||
|
corresponding chip driver. In that case a significantly improved performance
|
||||||
|
can be expected. If simultaneous setting is not possible the GPIOs will be set
|
||||||
|
sequentially.
|
||||||
|
Note that for optimal performance GPIOs belonging to the same chip should be
|
||||||
|
contiguous within the array of descriptors.
|
||||||
|
|
||||||
|
|
||||||
GPIOs mapped to IRQs
|
GPIOs mapped to IRQs
|
||||||
--------------------
|
--------------------
|
||||||
GPIO lines can quite often be used as IRQs. You can get the IRQ number
|
GPIO lines can quite often be used as IRQs. You can get the IRQ number
|
||||||
|
@ -1254,6 +1254,88 @@ static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
|
|||||||
chip->set(chip, gpio_chip_hwgpio(desc), value);
|
chip->set(chip, gpio_chip_hwgpio(desc), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set multiple outputs on the same chip;
|
||||||
|
* use the chip's set_multiple function if available;
|
||||||
|
* otherwise set the outputs sequentially;
|
||||||
|
* @mask: bit mask array; one bit per output; BITS_PER_LONG bits per word
|
||||||
|
* defines which outputs are to be changed
|
||||||
|
* @bits: bit value array; one bit per output; BITS_PER_LONG bits per word
|
||||||
|
* defines the values the outputs specified by mask are to be set to
|
||||||
|
*/
|
||||||
|
static void gpio_chip_set_multiple(struct gpio_chip *chip,
|
||||||
|
unsigned long *mask, unsigned long *bits)
|
||||||
|
{
|
||||||
|
if (chip->set_multiple) {
|
||||||
|
chip->set_multiple(chip, mask, bits);
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < chip->ngpio; i++) {
|
||||||
|
if (mask[BIT_WORD(i)] == 0) {
|
||||||
|
/* no more set bits in this mask word;
|
||||||
|
* skip ahead to the next word */
|
||||||
|
i = (BIT_WORD(i) + 1) * BITS_PER_LONG - 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* set outputs if the corresponding mask bit is set */
|
||||||
|
if (__test_and_clear_bit(i, mask)) {
|
||||||
|
chip->set(chip, i, test_bit(i, bits));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpiod_set_array_priv(bool raw, bool can_sleep,
|
||||||
|
unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (i < array_size) {
|
||||||
|
struct gpio_chip *chip = desc_array[i]->chip;
|
||||||
|
unsigned long mask[BITS_TO_LONGS(chip->ngpio)];
|
||||||
|
unsigned long bits[BITS_TO_LONGS(chip->ngpio)];
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
if (!can_sleep) {
|
||||||
|
WARN_ON(chip->can_sleep);
|
||||||
|
}
|
||||||
|
memset(mask, 0, sizeof(mask));
|
||||||
|
do {
|
||||||
|
struct gpio_desc *desc = desc_array[i];
|
||||||
|
int hwgpio = gpio_chip_hwgpio(desc);
|
||||||
|
int value = value_array[i];
|
||||||
|
|
||||||
|
if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
||||||
|
value = !value;
|
||||||
|
trace_gpio_value(desc_to_gpio(desc), 0, value);
|
||||||
|
/*
|
||||||
|
* collect all normal outputs belonging to the same chip
|
||||||
|
* open drain and open source outputs are set individually
|
||||||
|
*/
|
||||||
|
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
|
||||||
|
_gpio_set_open_drain_value(desc,value);
|
||||||
|
} else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
|
||||||
|
_gpio_set_open_source_value(desc, value);
|
||||||
|
} else {
|
||||||
|
__set_bit(hwgpio, mask);
|
||||||
|
if (value) {
|
||||||
|
__set_bit(hwgpio, bits);
|
||||||
|
} else {
|
||||||
|
__clear_bit(hwgpio, bits);
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
} while ((i < array_size) && (desc_array[i]->chip == chip));
|
||||||
|
/* push collected bits to outputs */
|
||||||
|
if (count != 0) {
|
||||||
|
gpio_chip_set_multiple(chip, mask, bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gpiod_set_raw_value() - assign a gpio's raw value
|
* gpiod_set_raw_value() - assign a gpio's raw value
|
||||||
* @desc: gpio whose value will be assigned
|
* @desc: gpio whose value will be assigned
|
||||||
@ -1298,6 +1380,48 @@ void gpiod_set_value(struct gpio_desc *desc, int value)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gpiod_set_value);
|
EXPORT_SYMBOL_GPL(gpiod_set_value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpiod_set_raw_array() - assign values to an array of GPIOs
|
||||||
|
* @array_size: number of elements in the descriptor / value arrays
|
||||||
|
* @desc_array: array of GPIO descriptors whose values will be assigned
|
||||||
|
* @value_array: array of values to assign
|
||||||
|
*
|
||||||
|
* Set the raw values of the GPIOs, i.e. the values of the physical lines
|
||||||
|
* without regard for their ACTIVE_LOW status.
|
||||||
|
*
|
||||||
|
* This function should be called from contexts where we cannot sleep, and will
|
||||||
|
* complain if the GPIO chip functions potentially sleep.
|
||||||
|
*/
|
||||||
|
void gpiod_set_raw_array(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array, int *value_array)
|
||||||
|
{
|
||||||
|
if (!desc_array)
|
||||||
|
return;
|
||||||
|
gpiod_set_array_priv(true, false, array_size, desc_array, value_array);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpiod_set_raw_array);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpiod_set_array() - assign values to an array of GPIOs
|
||||||
|
* @array_size: number of elements in the descriptor / value arrays
|
||||||
|
* @desc_array: array of GPIO descriptors whose values will be assigned
|
||||||
|
* @value_array: array of values to assign
|
||||||
|
*
|
||||||
|
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
|
||||||
|
* into account.
|
||||||
|
*
|
||||||
|
* This function should be called from contexts where we cannot sleep, and will
|
||||||
|
* complain if the GPIO chip functions potentially sleep.
|
||||||
|
*/
|
||||||
|
void gpiod_set_array(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array, int *value_array)
|
||||||
|
{
|
||||||
|
if (!desc_array)
|
||||||
|
return;
|
||||||
|
gpiod_set_array_priv(false, false, array_size, desc_array, value_array);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpiod_set_array);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gpiod_cansleep() - report whether gpio value access may sleep
|
* gpiod_cansleep() - report whether gpio value access may sleep
|
||||||
* @desc: gpio to check
|
* @desc: gpio to check
|
||||||
@ -1457,6 +1581,50 @@ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
|
EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpiod_set_raw_array_cansleep() - assign values to an array of GPIOs
|
||||||
|
* @array_size: number of elements in the descriptor / value arrays
|
||||||
|
* @desc_array: array of GPIO descriptors whose values will be assigned
|
||||||
|
* @value_array: array of values to assign
|
||||||
|
*
|
||||||
|
* Set the raw values of the GPIOs, i.e. the values of the physical lines
|
||||||
|
* without regard for their ACTIVE_LOW status.
|
||||||
|
*
|
||||||
|
* This function is to be called from contexts that can sleep.
|
||||||
|
*/
|
||||||
|
void gpiod_set_raw_array_cansleep(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array)
|
||||||
|
{
|
||||||
|
might_sleep_if(extra_checks);
|
||||||
|
if (!desc_array)
|
||||||
|
return;
|
||||||
|
gpiod_set_array_priv(true, true, array_size, desc_array, value_array);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_cansleep);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpiod_set_array_cansleep() - assign values to an array of GPIOs
|
||||||
|
* @array_size: number of elements in the descriptor / value arrays
|
||||||
|
* @desc_array: array of GPIO descriptors whose values will be assigned
|
||||||
|
* @value_array: array of values to assign
|
||||||
|
*
|
||||||
|
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
|
||||||
|
* into account.
|
||||||
|
*
|
||||||
|
* This function is to be called from contexts that can sleep.
|
||||||
|
*/
|
||||||
|
void gpiod_set_array_cansleep(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array)
|
||||||
|
{
|
||||||
|
might_sleep_if(extra_checks);
|
||||||
|
if (!desc_array)
|
||||||
|
return;
|
||||||
|
gpiod_set_array_priv(false, true, array_size, desc_array, value_array);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpiod_set_array_cansleep);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gpiod_add_lookup_table() - register GPIO device consumers
|
* gpiod_add_lookup_table() - register GPIO device consumers
|
||||||
* @table: table of consumers to register
|
* @table: table of consumers to register
|
||||||
|
@ -74,14 +74,24 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
|
|||||||
/* Value get/set from non-sleeping context */
|
/* Value get/set from non-sleeping context */
|
||||||
int gpiod_get_value(const struct gpio_desc *desc);
|
int gpiod_get_value(const struct gpio_desc *desc);
|
||||||
void gpiod_set_value(struct gpio_desc *desc, int value);
|
void gpiod_set_value(struct gpio_desc *desc, int value);
|
||||||
|
void gpiod_set_array(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array, int *value_array);
|
||||||
int gpiod_get_raw_value(const struct gpio_desc *desc);
|
int gpiod_get_raw_value(const struct gpio_desc *desc);
|
||||||
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
|
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
|
||||||
|
void gpiod_set_raw_array(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array, int *value_array);
|
||||||
|
|
||||||
/* Value get/set from sleeping context */
|
/* Value get/set from sleeping context */
|
||||||
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
|
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
|
||||||
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
|
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
|
||||||
|
void gpiod_set_array_cansleep(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array);
|
||||||
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
|
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
|
||||||
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
|
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
|
||||||
|
void gpiod_set_raw_array_cansleep(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array);
|
||||||
|
|
||||||
int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
|
int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
|
||||||
|
|
||||||
@ -210,6 +220,13 @@ static inline void gpiod_set_value(struct gpio_desc *desc, int value)
|
|||||||
/* GPIO can never have been requested */
|
/* GPIO can never have been requested */
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
}
|
}
|
||||||
|
static inline void gpiod_set_array(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array)
|
||||||
|
{
|
||||||
|
/* GPIO can never have been requested */
|
||||||
|
WARN_ON(1);
|
||||||
|
}
|
||||||
static inline int gpiod_get_raw_value(const struct gpio_desc *desc)
|
static inline int gpiod_get_raw_value(const struct gpio_desc *desc)
|
||||||
{
|
{
|
||||||
/* GPIO can never have been requested */
|
/* GPIO can never have been requested */
|
||||||
@ -221,6 +238,13 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value)
|
|||||||
/* GPIO can never have been requested */
|
/* GPIO can never have been requested */
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
}
|
}
|
||||||
|
static inline void gpiod_set_raw_array(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array)
|
||||||
|
{
|
||||||
|
/* GPIO can never have been requested */
|
||||||
|
WARN_ON(1);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc)
|
static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc)
|
||||||
{
|
{
|
||||||
@ -233,6 +257,13 @@ static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
|
|||||||
/* GPIO can never have been requested */
|
/* GPIO can never have been requested */
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
}
|
}
|
||||||
|
static inline void gpiod_set_array_cansleep(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array)
|
||||||
|
{
|
||||||
|
/* GPIO can never have been requested */
|
||||||
|
WARN_ON(1);
|
||||||
|
}
|
||||||
static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
|
static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
|
||||||
{
|
{
|
||||||
/* GPIO can never have been requested */
|
/* GPIO can never have been requested */
|
||||||
@ -245,6 +276,13 @@ static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc,
|
|||||||
/* GPIO can never have been requested */
|
/* GPIO can never have been requested */
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
}
|
}
|
||||||
|
static inline void gpiod_set_raw_array_cansleep(unsigned int array_size,
|
||||||
|
struct gpio_desc **desc_array,
|
||||||
|
int *value_array)
|
||||||
|
{
|
||||||
|
/* GPIO can never have been requested */
|
||||||
|
WARN_ON(1);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
|
static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
|
||||||
{
|
{
|
||||||
|
@ -32,6 +32,7 @@ struct seq_file;
|
|||||||
* @get: returns value for signal "offset"; for output signals this
|
* @get: returns value for signal "offset"; for output signals this
|
||||||
* returns either the value actually sensed, or zero
|
* returns either the value actually sensed, or zero
|
||||||
* @set: assigns output value for signal "offset"
|
* @set: assigns output value for signal "offset"
|
||||||
|
* @set_multiple: assigns output values for multiple signals defined by "mask"
|
||||||
* @set_debounce: optional hook for setting debounce time for specified gpio in
|
* @set_debounce: optional hook for setting debounce time for specified gpio in
|
||||||
* interrupt triggered gpio chips
|
* interrupt triggered gpio chips
|
||||||
* @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
|
* @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
|
||||||
@ -89,6 +90,9 @@ struct gpio_chip {
|
|||||||
unsigned offset);
|
unsigned offset);
|
||||||
void (*set)(struct gpio_chip *chip,
|
void (*set)(struct gpio_chip *chip,
|
||||||
unsigned offset, int value);
|
unsigned offset, int value);
|
||||||
|
void (*set_multiple)(struct gpio_chip *chip,
|
||||||
|
unsigned long *mask,
|
||||||
|
unsigned long *bits);
|
||||||
int (*set_debounce)(struct gpio_chip *chip,
|
int (*set_debounce)(struct gpio_chip *chip,
|
||||||
unsigned offset,
|
unsigned offset,
|
||||||
unsigned debounce);
|
unsigned debounce);
|
||||||
|
Loading…
Reference in New Issue
Block a user