From a411e81e61df24023f40255145d8f023402002c9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Apr 2018 22:30:28 +0200 Subject: gpiolib: add hogs support for machine code Board files constitute a significant part of the users of the legacy GPIO framework. In many cases they only export a line and set its desired value. We could use GPIO hogs for that like we do for DT and ACPI but there's no support for that in machine code. This patch proposes to extend the machine.h API with support for registering hog tables in board files. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) (limited to 'drivers/gpio/gpiolib.c') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 43aeb07343ec..547adc149b62 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -71,6 +71,9 @@ static DEFINE_MUTEX(gpio_lookup_lock); static LIST_HEAD(gpio_lookup_list); LIST_HEAD(gpio_devices); +static DEFINE_MUTEX(gpio_machine_hogs_mutex); +static LIST_HEAD(gpio_machine_hogs); + static void gpiochip_free_hogs(struct gpio_chip *chip); static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, struct lock_class_key *lock_key, @@ -1171,6 +1174,41 @@ err_remove_device: return status; } +static void gpiochip_machine_hog(struct gpio_chip *chip, struct gpiod_hog *hog) +{ + struct gpio_desc *desc; + int rv; + + desc = gpiochip_get_desc(chip, hog->chip_hwnum); + if (IS_ERR(desc)) { + pr_err("%s: unable to get GPIO desc: %ld\n", + __func__, PTR_ERR(desc)); + return; + } + + if (desc->flags & FLAG_IS_HOGGED) + return; + + rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags); + if (rv) + pr_err("%s: unable to hog GPIO line (%s:%u): %d\n", + __func__, chip->label, hog->chip_hwnum, rv); +} + +static void machine_gpiochip_add(struct gpio_chip *chip) +{ + struct gpiod_hog *hog; + + mutex_lock(&gpio_machine_hogs_mutex); + + list_for_each_entry(hog, &gpio_machine_hogs, list) { + if (!strcmp(chip->label, hog->chip_label)) + gpiochip_machine_hog(chip, hog); + } + + mutex_unlock(&gpio_machine_hogs_mutex); +} + static void gpiochip_setup_devs(void) { struct gpio_device *gdev; @@ -1326,6 +1364,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, acpi_gpiochip_add(chip); + machine_gpiochip_add(chip); + /* * By first adding the chardev, and then adding the device, * we get a device node entry in sysfs under @@ -3462,6 +3502,33 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) } EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table); +/** + * gpiod_add_hogs() - register a set of GPIO hogs from machine code + * @hogs: table of gpio hog entries with a zeroed sentinel at the end + */ +void gpiod_add_hogs(struct gpiod_hog *hogs) +{ + struct gpio_chip *chip; + struct gpiod_hog *hog; + + mutex_lock(&gpio_machine_hogs_mutex); + + for (hog = &hogs[0]; hog->chip_label; hog++) { + list_add_tail(&hog->list, &gpio_machine_hogs); + + /* + * The chip may have been registered earlier, so check if it + * exists and, if so, try to hog the line now. + */ + chip = find_chip_by_name(hog->chip_label); + if (chip) + gpiochip_machine_hog(chip, hog); + } + + mutex_unlock(&gpio_machine_hogs_mutex); +} +EXPORT_SYMBOL_GPL(gpiod_add_hogs); + static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) { const char *dev_id = dev ? dev_name(dev) : NULL; -- cgit v1.2.3 From ba3efdff2cd214e4133b73f4891f6255b610c19b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 1 May 2018 10:36:39 +0300 Subject: gpiolib: fix gpiochip_machine_hog() There is a shifter vs vanilla mask bug here. We want to test if 1 << 11 is set but we're testing if 0xb is set. Fixes: 9a6c505f7df1 ("gpiolib: add hogs support for machine code") Signed-off-by: Dan Carpenter Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio/gpiolib.c') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 547adc149b62..d1171db66c30 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1186,7 +1186,7 @@ static void gpiochip_machine_hog(struct gpio_chip *chip, struct gpiod_hog *hog) return; } - if (desc->flags & FLAG_IS_HOGGED) + if (test_bit(FLAG_IS_HOGGED, &desc->flags)) return; rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags); -- cgit v1.2.3 From 3027743f83f867d85662f5134e835c2e199cb9be Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Mon, 21 May 2018 10:57:07 -0700 Subject: gpio: Remove VLA from gpiolib The new challenge is to remove VLAs from the kernel (see https://lkml.org/lkml/2018/3/7/621) to eventually turn on -Wvla. Using a kmalloc array is the easy way to fix this but kmalloc is still more expensive than stack allocation. Introduce a fast path with a fixed size stack array to cover most chip with gpios below some fixed amount. The slow path dynamically allocates an array to cover those chips with a large number of gpios. Reviewed-by: Geert Uytterhoeven Reviewed-by: Phil Reid Reviewed-and-tested-by: Lukas Wunner Signed-off-by: Lukas Wunner Signed-off-by: Laura Abbott Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 12 +++++++ drivers/gpio/gpiolib.c | 76 +++++++++++++++++++++++++++++++++---------- drivers/gpio/gpiolib.h | 2 +- include/linux/gpio/consumer.h | 10 +++--- 4 files changed, 78 insertions(+), 22 deletions(-) (limited to 'drivers/gpio/gpiolib.c') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b960f6f35abd..71c0ab46f216 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -22,6 +22,18 @@ menuconfig GPIOLIB if GPIOLIB +config GPIOLIB_FASTPATH_LIMIT + int "Maximum number of GPIOs for fast path" + range 32 512 + default 512 + help + This adjusts the point at which certain APIs will switch from + using a stack allocated buffer to a dynamically allocated buffer. + + You shouldn't need to change this unless you really need to + optimize either stack space or performance. Change this carefully + since setting an incorrect value could cause stack corruption. + config OF_GPIO def_bool y depends on OF diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d1171db66c30..487edf188138 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -61,6 +61,11 @@ static struct bus_type gpio_bus_type = { .name = "gpio", }; +/* + * Number of GPIOs to use for the fast path in set array + */ +#define FASTPATH_NGPIO CONFIG_GPIOLIB_FASTPATH_LIMIT + /* gpio_lock prevents conflicts during gpio_desc[] table updates. * While any GPIO is requested, its gpio_chip is not removable; * each GPIO's "requested" flag serves as a lock and refcount. @@ -453,12 +458,11 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, vals[i] = !!ghd.values[i]; /* Reuse the array setting function */ - gpiod_set_array_value_complex(false, + return gpiod_set_array_value_complex(false, true, lh->numdescs, lh->descs, vals); - return 0; } return -EINVAL; } @@ -1281,6 +1285,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, goto err_free_descs; } + if (chip->ngpio > FASTPATH_NGPIO) + chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n", + chip->ngpio, FASTPATH_NGPIO); + gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL); if (!gdev->label) { status = -ENOMEM; @@ -2758,16 +2766,28 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, while (i < array_size) { struct gpio_chip *chip = desc_array[i]->gdev->chip; - unsigned long mask[BITS_TO_LONGS(chip->ngpio)]; - unsigned long bits[BITS_TO_LONGS(chip->ngpio)]; + unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)]; + unsigned long *mask, *bits; int first, j, ret; + if (likely(chip->ngpio <= FASTPATH_NGPIO)) { + mask = fastpath; + } else { + mask = kmalloc_array(2 * BITS_TO_LONGS(chip->ngpio), + sizeof(*mask), + can_sleep ? GFP_KERNEL : GFP_ATOMIC); + if (!mask) + return -ENOMEM; + } + + bits = mask + BITS_TO_LONGS(chip->ngpio); + bitmap_zero(mask, chip->ngpio); + if (!can_sleep) WARN_ON(chip->can_sleep); /* collect all inputs belonging to the same chip */ first = i; - memset(mask, 0, sizeof(mask)); do { const struct gpio_desc *desc = desc_array[i]; int hwgpio = gpio_chip_hwgpio(desc); @@ -2778,8 +2798,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, (desc_array[i]->gdev->chip == chip)); ret = gpio_chip_get_multiple(chip, mask, bits); - if (ret) + if (ret) { + if (mask != fastpath) + kfree(mask); return ret; + } for (j = first; j < i; j++) { const struct gpio_desc *desc = desc_array[j]; @@ -2791,6 +2814,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, value_array[j] = value; trace_gpio_value(desc_to_gpio(desc), 1, value); } + + if (mask != fastpath) + kfree(mask); } return 0; } @@ -2974,7 +3000,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip, } } -void gpiod_set_array_value_complex(bool raw, bool can_sleep, +int gpiod_set_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, int *value_array) @@ -2983,14 +3009,26 @@ void gpiod_set_array_value_complex(bool raw, bool can_sleep, while (i < array_size) { struct gpio_chip *chip = desc_array[i]->gdev->chip; - unsigned long mask[BITS_TO_LONGS(chip->ngpio)]; - unsigned long bits[BITS_TO_LONGS(chip->ngpio)]; + unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)]; + unsigned long *mask, *bits; int count = 0; + if (likely(chip->ngpio <= FASTPATH_NGPIO)) { + mask = fastpath; + } else { + mask = kmalloc_array(2 * BITS_TO_LONGS(chip->ngpio), + sizeof(*mask), + can_sleep ? GFP_KERNEL : GFP_ATOMIC); + if (!mask) + return -ENOMEM; + } + + bits = mask + BITS_TO_LONGS(chip->ngpio); + bitmap_zero(mask, chip->ngpio); + 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); @@ -3021,7 +3059,11 @@ void gpiod_set_array_value_complex(bool raw, bool can_sleep, /* push collected bits to outputs */ if (count != 0) gpio_chip_set_multiple(chip, mask, bits); + + if (mask != fastpath) + kfree(mask); } + return 0; } /** @@ -3096,13 +3138,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value); * 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_value(unsigned int array_size, +int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, int *value_array) { if (!desc_array) - return; - gpiod_set_array_value_complex(true, false, array_size, desc_array, - value_array); + return -EINVAL; + return gpiod_set_array_value_complex(true, false, array_size, + desc_array, value_array); } EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value); @@ -3422,14 +3464,14 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); * * This function is to be called from contexts that can sleep. */ -void gpiod_set_raw_array_value_cansleep(unsigned int array_size, +int gpiod_set_raw_array_value_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_value_complex(true, true, array_size, desc_array, + return -EINVAL; + return gpiod_set_array_value_complex(true, true, array_size, desc_array, value_array); } EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index ad456b6f9d8b..1a8e20363861 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -188,7 +188,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, int *value_array); -void gpiod_set_array_value_complex(bool raw, bool can_sleep, +int gpiod_set_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, int *value_array); diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index dbd065963296..243112c7fa7d 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -116,7 +116,7 @@ int gpiod_get_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, int *value_array); void gpiod_set_raw_value(struct gpio_desc *desc, int value); -void gpiod_set_raw_array_value(unsigned int array_size, +int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, int *value_array); @@ -134,7 +134,7 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, int *value_array); void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value); -void gpiod_set_raw_array_value_cansleep(unsigned int array_size, +int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, int *value_array); @@ -369,12 +369,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_value(unsigned int array_size, +static inline int gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, int *value_array) { /* GPIO can never have been requested */ WARN_ON(1); + return 0; } static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc) @@ -423,12 +424,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_value_cansleep(unsigned int array_size, +static inline int gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, int *value_array) { /* GPIO can never have been requested */ WARN_ON(1); + return 0; } static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) -- cgit v1.2.3