gpiolib: allow simultaneous setting of multiple GPIO outputs
Subsystems (2)
PATCH v6 1/3 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
Documentation/gpio/consumer.txt | 27 ++++++ drivers/gpio/gpiolib.c | 168 ++++++++++++++++++++++++++++++++++++++++ include/linux/gpio/consumer.h | 38 +++++++++ include/linux/gpio/driver.h | 4 4 files changed, 237 insertions(+)
Documentation/gpio/consumer.txt +27 -0
@@ -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 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 -------------------- GPIO lines can quite often be used as IRQs. You can get the IRQ number
drivers/gpio/gpiolib.c +168 -0
186 lines not on this page · raw message
include/linux/gpio/consumer.h +38 -0
@@ -74,14 +74,24 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);/* Value get/set from non-sleeping context */ int gpiod_get_value (const struct gpio_desc *desc);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);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 */ int gpiod_get_value_cansleep (const struct gpio_desc *desc);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);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);
@@ -210,6 +220,13 @@ static inline void gpiod_set_value(struct gpio_desc *desc, int value)/* GPIO can never have been requested */ 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) {/* 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 */ 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) {
@@ -233,6 +257,13 @@ static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)/* GPIO can never have been requested */ 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) {/* 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 */ 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) {
include/linux/gpio/driver.h +4 -0
@@ -32,6 +32,7 @@ struct seq_file;* @get: returns value for signal "offset"; for output signals this * returns either the value actually sensed, or zero * @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 * interrupt triggered gpio chips * @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
@@ -89,6 +90,9 @@ struct gpio_chip { unsigned offset);void (*set)(struct gpio_chip *chip,unsigned offset,int value); +void (*set_multiple)(struct gpio_chip *chip, +unsigned long *mask, +unsigned long *bits);int (*set_debounce)(struct gpio_chip *chip,unsigned offset,unsigned debounce);
--
2.0.4
Quoted 20 lines · Rojhalat Ibrahim
Introduce new functions
gpiod_set_array&gpiod_set_raw_arrayto the consumer interface which allow setting multiple outputs with just one function call. Also add an optionalset_multiplefunction to the driver interface. Without an implementation of that function in the chip driver outputs are set sequentially.Implementing the
set_multiplefunction 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.
Quoted 3 lines
Signed-off-by Rojhalat Ibrahim <[email protected]>Reviewed-by Alexandre Courbot <[email protected]>Reviewed-by Mark Brown <[email protected]>
Great to see this series pushed until the end! I checked the first two patches again and my approval for them still stands.
Quoted 20 lines · Rojhalat Ibrahim
Introduce new functions
gpiod_set_array&gpiod_set_raw_arrayto the consumer interface which allow setting multiple outputs with just one function call. Also add an optionalset_multiplefunction to the driver interface. Without an implementation of that function in the chip driver outputs are set sequentially.Implementing the
set_multiplefunction 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.
Quoted 3 lines
Signed-off-by Rojhalat Ibrahim <[email protected]>Reviewed-by Alexandre Courbot <[email protected]>Reviewed-by Mark Brown <[email protected]>
OK let's do this.
Patch applied!
Yours,
Linus Walleij