diff options
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r-- | drivers/gpio/gpiolib.c | 365 |
1 files changed, 270 insertions, 95 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 0c00ed2ab431..b8197502a5ac 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -26,6 +26,7 @@ #include <linux/slab.h> #include <linux/srcu.h> #include <linux/string.h> +#include <linux/string_choices.h> #include <linux/gpio.h> #include <linux/gpio/driver.h> @@ -341,6 +342,25 @@ static int gpiochip_find_base_unlocked(u16 ngpio) } } +static int gpiochip_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + int ret; + + lockdep_assert_held(&gc->gpiodev->srcu); + + if (WARN_ON(!gc->get_direction)) + return -EOPNOTSUPP; + + ret = gc->get_direction(gc, offset); + if (ret < 0) + return ret; + + if (ret != GPIO_LINE_DIRECTION_OUT && ret != GPIO_LINE_DIRECTION_IN) + ret = -EBADE; + + return ret; +} + /** * gpiod_get_direction - return the current direction of a GPIO * @desc: GPIO to get the direction of @@ -381,7 +401,7 @@ int gpiod_get_direction(struct gpio_desc *desc) if (!guard.gc->get_direction) return -ENOTSUPP; - ret = guard.gc->get_direction(guard.gc, offset); + ret = gpiochip_get_direction(guard.gc, offset); if (ret < 0) return ret; @@ -652,7 +672,7 @@ static int gpiochip_apply_reserved_ranges(struct gpio_chip *gc) if (start >= gc->ngpio || start + count > gc->ngpio) continue; - bitmap_clear(gc->valid_mask, start, count); + bitmap_clear(gc->gpiodev->valid_mask, start, count); } kfree(ranges); @@ -666,8 +686,8 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) if (!(gpiochip_count_reserved_ranges(gc) || gc->init_valid_mask)) return 0; - gc->valid_mask = gpiochip_allocate_mask(gc); - if (!gc->valid_mask) + gc->gpiodev->valid_mask = gpiochip_allocate_mask(gc); + if (!gc->gpiodev->valid_mask) return -ENOMEM; ret = gpiochip_apply_reserved_ranges(gc); @@ -676,7 +696,7 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) if (gc->init_valid_mask) return gc->init_valid_mask(gc, - gc->valid_mask, + gc->gpiodev->valid_mask, gc->ngpio); return 0; @@ -684,7 +704,7 @@ static int gpiochip_init_valid_mask(struct gpio_chip *gc) static void gpiochip_free_valid_mask(struct gpio_chip *gc) { - gpiochip_free_mask(&gc->valid_mask); + gpiochip_free_mask(&gc->gpiodev->valid_mask); } static int gpiochip_add_pin_ranges(struct gpio_chip *gc) @@ -703,13 +723,29 @@ static int gpiochip_add_pin_ranges(struct gpio_chip *gc) return 0; } +/** + * gpiochip_query_valid_mask - return the GPIO validity information + * @gc: gpio chip which validity information is queried + * + * Returns: bitmap representing valid GPIOs or NULL if all GPIOs are valid + * + * Some GPIO chips may support configurations where some of the pins aren't + * available. These chips can have valid_mask set to represent the valid + * GPIOs. This function can be used to retrieve this information. + */ +const unsigned long *gpiochip_query_valid_mask(const struct gpio_chip *gc) +{ + return gc->gpiodev->valid_mask; +} +EXPORT_SYMBOL_GPL(gpiochip_query_valid_mask); + bool gpiochip_line_is_valid(const struct gpio_chip *gc, unsigned int offset) { /* No mask means all valid */ - if (likely(!gc->valid_mask)) + if (likely(!gc->gpiodev->valid_mask)) return true; - return test_bit(offset, gc->valid_mask); + return test_bit(offset, gc->gpiodev->valid_mask); } EXPORT_SYMBOL_GPL(gpiochip_line_is_valid); @@ -882,13 +918,29 @@ void *gpiochip_get_data(struct gpio_chip *gc) } EXPORT_SYMBOL_GPL(gpiochip_get_data); +/* + * If the calling driver provides the specific firmware node, + * use it. Otherwise use the one from the parent device, if any. + */ +static struct fwnode_handle *gpiochip_choose_fwnode(struct gpio_chip *gc) +{ + if (gc->fwnode) + return gc->fwnode; + + if (gc->parent) + return dev_fwnode(gc->parent); + + return NULL; +} + int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev) { + struct fwnode_handle *fwnode = gpiochip_choose_fwnode(gc); u32 ngpios = gc->ngpio; int ret; if (ngpios == 0) { - ret = device_property_read_u32(dev, "ngpios", &ngpios); + ret = fwnode_property_read_u32(fwnode, "ngpios", &ngpios); if (ret == -ENODATA) /* * -ENODATA means that there is no property found and @@ -925,6 +977,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = 0; int ret = 0; + /* Only allow one set() and one set_multiple(). */ + if ((gc->set && gc->set_rv) || + (gc->set_multiple && gc->set_multiple_rv)) + return -EINVAL; + /* * First: allocate and populate the internal stat container, and * set up the struct device. @@ -941,14 +998,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gc->gpiodev = gdev; gpiochip_set_data(gc, data); - /* - * If the calling driver did not initialize firmware node, - * do it here using the parent device, if any. - */ - if (gc->fwnode) - device_set_node(&gdev->dev, gc->fwnode); - else if (gc->parent) - device_set_node(&gdev->dev, dev_fwnode(gc->parent)); + device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc)); gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL); if (gdev->id < 0) { @@ -1058,11 +1108,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, desc->gdev = gdev; /* - * We would typically want to check the return value of - * get_direction() here but we must not check the return value - * and bail-out as pin controllers can have pins configured to - * alternate functions and return -EINVAL. Also: there's no - * need to take the SRCU lock here. + * We would typically want to use gpiochip_get_direction() here + * but we must not check the return value and bail-out as pin + * controllers can have pins configured to alternate functions + * and return -EINVAL. Also: there's no need to take the SRCU + * lock here. */ if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) assign_bit(FLAG_IS_OUT, &desc->flags, @@ -2325,16 +2375,18 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) if (test_and_set_bit(FLAG_REQUESTED, &desc->flags)) return -EBUSY; + offset = gpio_chip_hwgpio(desc); + if (!gpiochip_line_is_valid(guard.gc, offset)) + return -EINVAL; + /* NOTE: gpio_request() can be called in early boot, * before IRQs are enabled, for non-sleeping (SOC) GPIOs. */ if (guard.gc->request) { - offset = gpio_chip_hwgpio(desc); - if (gpiochip_line_is_valid(guard.gc, offset)) - ret = guard.gc->request(guard.gc, offset); - else - ret = -EINVAL; + ret = guard.gc->request(guard.gc, offset); + if (ret > 0) + ret = -EBADE; if (ret) goto out_clear_bit; } @@ -2577,6 +2629,9 @@ int gpio_do_set_config(struct gpio_desc *desc, unsigned long config) return -ENOTSUPP; ret = guard.gc->set_config(guard.gc, gpio_chip_hwgpio(desc), config); + if (ret > 0) + ret = -EBADE; + #ifdef CONFIG_GPIO_CDEV /* * Special case - if we're setting debounce period, we need to store @@ -2682,6 +2737,39 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce) return ret; } +static int gpiochip_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + int ret; + + lockdep_assert_held(&gc->gpiodev->srcu); + + if (WARN_ON(!gc->direction_input)) + return -EOPNOTSUPP; + + ret = gc->direction_input(gc, offset); + if (ret > 0) + ret = -EBADE; + + return ret; +} + +static int gpiochip_direction_output(struct gpio_chip *gc, unsigned int offset, + int value) +{ + int ret; + + lockdep_assert_held(&gc->gpiodev->srcu); + + if (WARN_ON(!gc->direction_output)) + return -EOPNOTSUPP; + + ret = gc->direction_output(gc, offset, value); + if (ret > 0) + ret = -EBADE; + + return ret; +} + /** * gpiod_direction_input - set the GPIO direction to input * @desc: GPIO to set to input @@ -2733,11 +2821,10 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc) * assume we are in input mode after this. */ if (guard.gc->direction_input) { - ret = guard.gc->direction_input(guard.gc, - gpio_chip_hwgpio(desc)); + ret = gpiochip_direction_input(guard.gc, + gpio_chip_hwgpio(desc)); } else if (guard.gc->get_direction) { - dir = guard.gc->get_direction(guard.gc, - gpio_chip_hwgpio(desc)); + dir = gpiochip_get_direction(guard.gc, gpio_chip_hwgpio(desc)); if (dir < 0) return dir; @@ -2758,6 +2845,27 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc) return ret; } +static int gpiochip_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + int ret; + + lockdep_assert_held(&gc->gpiodev->srcu); + + if (WARN_ON(unlikely(!gc->set && !gc->set_rv))) + return -EOPNOTSUPP; + + if (gc->set_rv) { + ret = gc->set_rv(gc, offset, value); + if (ret > 0) + ret = -EBADE; + + return ret; + } + + gc->set(gc, offset, value); + return 0; +} + static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) { int val = !!value, ret = 0, dir; @@ -2779,13 +2887,13 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) } if (guard.gc->direction_output) { - ret = guard.gc->direction_output(guard.gc, - gpio_chip_hwgpio(desc), val); + ret = gpiochip_direction_output(guard.gc, + gpio_chip_hwgpio(desc), val); } else { /* Check that we are in output mode if we can */ if (guard.gc->get_direction) { - dir = guard.gc->get_direction(guard.gc, - gpio_chip_hwgpio(desc)); + dir = gpiochip_get_direction(guard.gc, + gpio_chip_hwgpio(desc)); if (dir < 0) return dir; @@ -2800,7 +2908,9 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) * If we can't actively set the direction, we are some * output-only chip, so just drive the output as desired. */ - guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), val); + ret = gpiochip_set(guard.gc, gpio_chip_hwgpio(desc), val); + if (ret) + return ret; } if (!ret) @@ -2890,19 +3000,15 @@ int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value) if (!ret) goto set_output_value; /* Emulate open drain by not actively driving the line high */ - if (value) { - ret = gpiod_direction_input_nonotify(desc); + if (value) goto set_output_flag; - } } else if (test_bit(FLAG_OPEN_SOURCE, &flags)) { ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_SOURCE); if (!ret) goto set_output_value; /* Emulate open source by not actively driving the line low */ - if (!value) { - ret = gpiod_direction_input_nonotify(desc); + if (!value) goto set_output_flag; - } } else { gpio_set_config(desc, PIN_CONFIG_DRIVE_PUSH_PULL); } @@ -2914,17 +3020,20 @@ set_output_value: return gpiod_direction_output_raw_commit(desc, value); set_output_flag: + ret = gpiod_direction_input_nonotify(desc); + if (ret) + return ret; /* * When emulating open-source or open-drain functionalities by not * actively driving the line (setting mode to input) we still need to * set the IS_OUT flag or otherwise we won't be able to set the line * value anymore. */ - if (ret == 0) - set_bit(FLAG_IS_OUT, &desc->flags); - return ret; + set_bit(FLAG_IS_OUT, &desc->flags); + return 0; } +#if IS_ENABLED(CONFIG_HTE) /** * gpiod_enable_hw_timestamp_ns - Enable hardware timestamp in nanoseconds. * @@ -2990,6 +3099,7 @@ int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) return ret; } EXPORT_SYMBOL_GPL(gpiod_disable_hw_timestamp_ns); +#endif /* CONFIG_HTE */ /** * gpiod_set_config - sets @config for a GPIO @@ -3096,9 +3206,23 @@ void gpiod_toggle_active_low(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_toggle_active_low); +static int gpiochip_get(struct gpio_chip *gc, unsigned int offset) +{ + int ret; + + lockdep_assert_held(&gc->gpiodev->srcu); + + /* Make sure this is called after checking for gc->get(). */ + ret = gc->get(gc, offset); + if (ret > 1) + ret = -EBADE; + + return ret; +} + static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *desc) { - return gc->get ? gc->get(gc, gpio_chip_hwgpio(desc)) : -EIO; + return gc->get ? gpiochip_get(gc, gpio_chip_hwgpio(desc)) : -EIO; } /* I/O calls are only valid after configuration completed; the relevant @@ -3147,15 +3271,21 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) static int gpio_chip_get_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { + int ret; + lockdep_assert_held(&gc->gpiodev->srcu); - if (gc->get_multiple) - return gc->get_multiple(gc, mask, bits); + if (gc->get_multiple) { + ret = gc->get_multiple(gc, mask, bits); + if (ret > 0) + return -EBADE; + } + if (gc->get) { int i, value; for_each_set_bit(i, mask, gc->ngpio) { - value = gc->get(gc, i); + value = gpiochip_get(gc, i); if (value < 0) return value; __assign_bit(i, bits, value); @@ -3408,18 +3538,18 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value); * @desc: gpio descriptor whose state need to be set. * @value: Non-zero for setting it HIGH otherwise it will set to LOW. */ -static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) +static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) { int ret = 0, offset = gpio_chip_hwgpio(desc); CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) - return; + return -ENODEV; if (value) { - ret = guard.gc->direction_input(guard.gc, offset); + ret = gpiochip_direction_input(guard.gc, offset); } else { - ret = guard.gc->direction_output(guard.gc, offset, 0); + ret = gpiochip_direction_output(guard.gc, offset, 0); if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); } @@ -3428,6 +3558,8 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) gpiod_err(desc, "%s: Error in set_value for open drain err %d\n", __func__, ret); + + return ret; } /* @@ -3435,36 +3567,38 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) * @desc: gpio descriptor whose state need to be set. * @value: Non-zero for setting it HIGH otherwise it will set to LOW. */ -static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value) +static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value) { int ret = 0, offset = gpio_chip_hwgpio(desc); CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) - return; + return -ENODEV; if (value) { - ret = guard.gc->direction_output(guard.gc, offset, 1); + ret = gpiochip_direction_output(guard.gc, offset, 1); if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); } else { - ret = guard.gc->direction_input(guard.gc, offset); + ret = gpiochip_direction_input(guard.gc, offset); } trace_gpio_direction(desc_to_gpio(desc), !value, ret); if (ret < 0) gpiod_err(desc, "%s: Error in set_value for open source err %d\n", __func__, ret); + + return ret; } -static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) +static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) - return; + return -ENODEV; trace_gpio_value(desc_to_gpio(desc), 0, value); - guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), value); + return gpiochip_set(guard.gc, gpio_chip_hwgpio(desc), value); } /* @@ -3476,21 +3610,38 @@ static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) * 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 + * + * Returns: 0 on success, negative error number on failure. */ -static void gpio_chip_set_multiple(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits) +static int gpiochip_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) { + unsigned int i; + int ret; + lockdep_assert_held(&gc->gpiodev->srcu); + if (gc->set_multiple_rv) { + ret = gc->set_multiple_rv(gc, mask, bits); + if (ret > 0) + ret = -EBADE; + + return ret; + } + if (gc->set_multiple) { gc->set_multiple(gc, mask, bits); - } else { - unsigned int i; + return 0; + } - /* set outputs if the corresponding mask bit is set */ - for_each_set_bit(i, mask, gc->ngpio) - gc->set(gc, i, test_bit(i, bits)); + /* set outputs if the corresponding mask bit is set */ + for_each_set_bit(i, mask, gc->ngpio) { + ret = gpiochip_set(gc, i, test_bit(i, bits)); + if (ret) + break; } + + return ret; } int gpiod_set_array_value_complex(bool raw, bool can_sleep, @@ -3500,7 +3651,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, unsigned long *value_bitmap) { struct gpio_chip *gc; - int i = 0; + int i = 0, ret; /* * Validate array_info against desc_array and its size. @@ -3523,7 +3674,10 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, bitmap_xor(value_bitmap, value_bitmap, array_info->invert_mask, array_size); - gpio_chip_set_multiple(gc, array_info->set_mask, value_bitmap); + ret = gpiochip_set_multiple(gc, array_info->set_mask, + value_bitmap); + if (ret) + return ret; i = find_first_zero_bit(array_info->set_mask, array_size); if (i == array_size) @@ -3600,8 +3754,11 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, } while ((i < array_size) && gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc)); /* push collected bits to outputs */ - if (count != 0) - gpio_chip_set_multiple(guard.gc, mask, bits); + if (count != 0) { + ret = gpiochip_set_multiple(guard.gc, mask, bits); + if (ret) + return ret; + } if (mask != fastpath_mask) bitmap_free(mask); @@ -3621,13 +3778,16 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, * * This function can be called from contexts where we cannot sleep, and will * complain if the GPIO chip functions potentially sleep. + * + * Returns: + * 0 on success, negative error number on failure. */ -void gpiod_set_raw_value(struct gpio_desc *desc, int value) +int gpiod_set_raw_value(struct gpio_desc *desc, int value) { - VALIDATE_DESC_VOID(desc); + VALIDATE_DESC(desc); /* Should be using gpiod_set_raw_value_cansleep() */ WARN_ON(desc->gdev->can_sleep); - gpiod_set_raw_value_commit(desc, value); + return gpiod_set_raw_value_commit(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_raw_value); @@ -3639,17 +3799,21 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value); * This sets the value of a GPIO line backing a descriptor, applying * different semantic quirks like active low and open drain/source * handling. + * + * Returns: + * 0 on success, negative error number on failure. */ -static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) +static int gpiod_set_value_nocheck(struct gpio_desc *desc, int value) { if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - gpio_set_open_drain_value_commit(desc, value); + return gpio_set_open_drain_value_commit(desc, value); else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - gpio_set_open_source_value_commit(desc, value); - else - gpiod_set_raw_value_commit(desc, value); + return gpio_set_open_source_value_commit(desc, value); + + return gpiod_set_raw_value_commit(desc, value); } /** @@ -3662,13 +3826,16 @@ static void gpiod_set_value_nocheck(struct gpio_desc *desc, int value) * * This function can be called from contexts where we cannot sleep, and will * complain if the GPIO chip functions potentially sleep. + * + * Returns: + * 0 on success, negative error number on failure. */ -void gpiod_set_value(struct gpio_desc *desc, int value) +int gpiod_set_value(struct gpio_desc *desc, int value) { - VALIDATE_DESC_VOID(desc); + VALIDATE_DESC(desc); /* Should be using gpiod_set_value_cansleep() */ WARN_ON(desc->gdev->can_sleep); - gpiod_set_value_nocheck(desc, value); + return gpiod_set_value_nocheck(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_value); @@ -4086,12 +4253,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); * regard for its ACTIVE_LOW status. * * This function is to be called from contexts that can sleep. + * + * Returns: + * 0 on success, negative error number on failure. */ -void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) +int gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { might_sleep(); - VALIDATE_DESC_VOID(desc); - gpiod_set_raw_value_commit(desc, value); + VALIDATE_DESC(desc); + return gpiod_set_raw_value_commit(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); @@ -4104,12 +4274,15 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); * account * * This function is to be called from contexts that can sleep. + * + * Returns: + * 0 on success, negative error number on failure. */ -void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) +int gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { might_sleep(); - VALIDATE_DESC_VOID(desc); - gpiod_set_value_nocheck(desc, value); + VALIDATE_DESC(desc); + return gpiod_set_value_nocheck(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); @@ -4746,10 +4919,10 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, return ret; } - gpiod_dbg(desc, "hogged as %s%s\n", + gpiod_dbg(desc, "hogged as %s/%s\n", (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input", (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? - (dflags & GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low" : ""); + str_high_low(dflags & GPIOD_FLAGS_BIT_DIR_VAL) : "?"); return 0; } @@ -5023,6 +5196,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) unsigned int gpio = gdev->base; struct gpio_desc *desc; struct gpio_chip *gc; + unsigned long flags; int value; guard(srcu)(&gdev->srcu); @@ -5035,16 +5209,17 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) for_each_gpio_desc(gc, desc) { guard(srcu)(&desc->gdev->desc_srcu); - is_irq = test_bit(FLAG_USED_AS_IRQ, &desc->flags); - if (is_irq || test_bit(FLAG_REQUESTED, &desc->flags)) { + flags = READ_ONCE(desc->flags); + is_irq = test_bit(FLAG_USED_AS_IRQ, &flags); + if (is_irq || test_bit(FLAG_REQUESTED, &flags)) { gpiod_get_direction(desc); - is_out = test_bit(FLAG_IS_OUT, &desc->flags); + is_out = test_bit(FLAG_IS_OUT, &flags); value = gpio_chip_get_value(gc, desc); - active_low = test_bit(FLAG_ACTIVE_LOW, &desc->flags); + active_low = test_bit(FLAG_ACTIVE_LOW, &flags); seq_printf(s, " gpio-%-3u (%-20.20s|%-20.20s) %s %s %s%s\n", gpio, desc->name ?: "", gpiod_get_label(desc), is_out ? "out" : "in ", - value >= 0 ? (value ? "hi" : "lo") : "? ", + value >= 0 ? str_hi_lo(value) : "? ", is_irq ? "IRQ " : "", active_low ? "ACTIVE LOW" : ""); } else if (desc->name) { |