The added API is aligned with the API in Linux, but has the caveat that barebox has no set_multiple callbacks implemented in the GPIO drivers, so switching may result in a bad intermediate state. We add a note about that and implement the API in the easiest possible way. Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx> --- drivers/gpio/gpiolib.c | 123 ++++++++++++++++++++++++++++++++++ include/linux/gpio/consumer.h | 31 +++++++++ 2 files changed, 154 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 3e57cf6239c3..40010262bd81 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -8,6 +8,7 @@ #include <gpio.h> #include <of_gpio.h> #include <linux/gpio/consumer.h> +#include <linux/overflow.h> #include <errno.h> #include <malloc.h> @@ -207,6 +208,21 @@ void gpiod_put(struct gpio_desc *desc) } EXPORT_SYMBOL(gpiod_put); +/** + * gpiod_put_array - dispose of multiple GPIO descriptors + * @descs: struct gpio_descs containing an array of descriptors + */ +void gpiod_put_array(struct gpio_descs *descs) +{ + unsigned int i; + + for (i = 0; i < descs->ndescs; i++) + gpiod_put(descs->desc[i]); + + kfree(descs); +} +EXPORT_SYMBOL_GPL(gpiod_put_array); + /** * gpiod_set_raw_value() - assign a gpio's raw value * @desc: gpio whose value will be assigned @@ -865,8 +881,115 @@ struct gpio_desc *dev_gpiod_get_index(struct device *dev, return ret ? ERR_PTR(ret): desc; } + +/** + * gpiod_count - return the number of GPIOs associated with a device / function + * or -ENOENT if no GPIO has been assigned to the requested function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @_con_id: function within the GPIO consumer + */ +int gpiod_count(struct device *dev, const char *con_id) +{ + struct device_node *np = dev_of_node(dev); + struct property *pp; + + if (!np) + return -ENODEV; + + pp = of_find_gpio_property(np, con_id); + if (!pp) + return -ENOENT; + + return of_gpio_named_count(np, pp->name); +} +EXPORT_SYMBOL_GPL(gpiod_count); + +/** + * gpiod_get_array - obtain multiple GPIOs from a multi-index GPIO function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * This function acquires all the GPIOs defined under a given function. + * + * Return a struct gpio_descs containing an array of descriptors, -ENOENT if + * no GPIO has been assigned to the requested function, or another IS_ERR() + * code if an error occurred while trying to acquire the GPIOs. + */ +struct gpio_descs *__must_check gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_desc *desc; + struct gpio_descs *descs; + int count; + + count = gpiod_count(dev, con_id); + if (count < 0) + return ERR_PTR(count); + + descs = kzalloc(struct_size(descs, desc, count), GFP_KERNEL); + if (!descs) + return ERR_PTR(-ENOMEM); + + for (descs->ndescs = 0; descs->ndescs < count; descs->ndescs++) { + desc = dev_gpiod_get_index(dev, dev_of_node(dev), con_id, + descs->ndescs, flags, NULL); + if (IS_ERR(desc)) { + gpiod_put_array(descs); + return ERR_CAST(desc); + } + + descs->desc[descs->ndescs] = desc; + } + + return descs; +} +EXPORT_SYMBOL_GPL(gpiod_get_array); + #endif +static int gpiod_set_array_value_complex(bool raw, + unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) +{ + int i; + + BUG_ON(array_info != NULL); + + for (i = 0; i < array_size; i++) + gpiod_set_value(desc_array[i], test_bit(i, value_bitmap)); + + return 0; +} + +/** + * gpiod_set_array_value() - assign values to an array of GPIOs + * @array_size: number of elements in the descriptor array / value bitmap + * @desc_array: array of GPIO descriptors whose values will be assigned + * @array_info: information on applicability of fast bitmap processing path + * @value_bitmap: bitmap of values to assign + * + * Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status + * into account. NOTE: This function has no special handling for GPIOs + * in the same bank that could've been set atomically: GPIO sequencing + * is not guaranteed to always remain in the same order. + */ +int gpiod_set_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) +{ + if (!desc_array) + return -EINVAL; + return gpiod_set_array_value_complex(false, array_size, + desc_array, array_info, + value_bitmap); +} +EXPORT_SYMBOL_GPL(gpiod_set_array_value); + int gpiochip_add(struct gpio_chip *chip) { int i; diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index c89b0c48ee2b..6ad3b265c835 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -25,6 +25,24 @@ enum gpiod_flags { #define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) struct gpio_desc; +struct gpio_array; + +/** + * struct gpio_descs - Struct containing an array of descriptors that can be + * obtained using gpiod_get_array() + * + * @info: Pointer to the opaque gpio_array structure + * @ndescs: Number of held descriptors + * @desc: Array of pointers to GPIO descriptors + */ +struct gpio_descs { + unsigned int ndescs; + /* info is used for fastpath, which we don't have in barebox. + * We define the member anyway, as not to change API + */ + struct gpio_array *info; + DECLARE_FLEX_ARRAY(struct gpio_desc *, desc); +}; #ifdef CONFIG_OFDEVICE @@ -103,4 +121,17 @@ int gpiod_get_value(const struct gpio_desc *desc); __state == (active), timeout_us); \ }) +int gpiod_count(struct device *dev, const char *con_id); + +struct gpio_descs *__must_check gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags); + +void gpiod_put_array(struct gpio_descs *descs); + +int gpiod_set_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap); + #endif -- 2.39.2