diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2025-05-27 15:22:01 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2025-05-27 15:22:01 -0700 |
commit | aacc73ceeb8bf664426f0e53db2778a59325bd9f (patch) | |
tree | c34e850094c178bc2e81676c652463f54402f030 /drivers/gpio/gpiolib.c | |
parent | a9e6060bb2a6cae6d43a98ec0794844ad01273d3 (diff) | |
parent | 8b8ef309093ff196967a21ee51a9cdb404a13bf6 (diff) |
Merge tag 'gpio-updates-for-v6.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski:
"We have three new drivers, some refactoring in the GPIO core, lots of
various changes across many drivers, new configfs interface for the
virtual gpio-aggregator module and DT-bindings updates.
The treewide conversion of GPIO drivers to using the new value setter
callbacks is ongoing with another round of GPIO drivers updated. You
will also see these commits coming in from other subsystems as with
the relevant changes merged into mainline last cycle, I've started
converting GPIO providers located elsewhere than drivers/gpio/.
GPIO core:
- use more lock guards where applicable
- refactor GPIO ACPI code and shrink it in the process by 8%
- move GPIO ACPI quirks into a separate file
- remove unneeded #ifdef
- convert GPIO devres helpers to using devm_add_action() where
applicable which shrinks and simplifies the code
- refactor GPIO descriptor validation in GPIO consumer interfaces
- don't allow setting values on input lines in the GPIO core which
will take off the burden from GPIO drivers of checking this down
the line
- provide gpiod_is_equal() as a way of safely comparing two GPIO
descriptors (the only current user is in regulator core)
New drivers:
- add the GPIO module for the max77759 multifunction device
- add the GPIO driver for the VeriSilicon BLZP1600 GPIO controller
- add the GPIO driver for the Spacemit K1 SoC
Driver improvements:
- convert more drivers to using the new GPIO line value setter
callbacks
- convert more drivers to making the irq_chip immutable as is
recommended by the interrupt subsystem
- extend build testing coverage by enabling more modules to be built
with COMPILE_TEST=y
- extend the gpio-aggregator module with a configfs interface that
makes the setup easier for user-space than the existing
driver-level sysfs attributes and also adds more advanced
configuration features (such as referring to aggregated lines by
their original names or modifying their names as exposed by the
aggregated chip)
- add a missing mutex_destroy() in gpio-imx-scu
- add an OF polarity quirk for s5m8767
- allow building gpio-vf610 as a loadable module
- make gpio-mxc not hardcode its GPIO base number with GPIO SYSFS
interface disabled (another small step towards getting rid of the
global GPIO numberspace)
- add support for level-triggered interrupts to gpio-pca953x
- don't double-check the ngpios property in gpio-ds4520 as GPIO core
already does it
- don't double-check the number of GPIOs in gpio-imx-scu as GPIO core
already does it
- remove unused callbacks from gpio-max3191x
DT bindings:
- add device-tree bindings for max77759, spacemit,k1 and blzp1600
(new drivers added this cycle)
- document more properties for gpio-vf610 and gpio-tegra186
- document a new pca95xx variant
- fix style of examples in several GPIO DT-binding documents
Misc:
- TODO list updates"
* tag 'gpio-updates-for-v6.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (123 commits)
gpio: timberdale: select GPIOLIB_IRQCHIP
gpio: lpc18xx: select GPIOLIB_IRQCHIP
gpio: grgpio: select GPIOLIB_IRQCHIP
gpio: bcm-kona: select GPIOLIB_IRQCHIP
dt-bindings: gpio: vf610: add ngpios and gpio-reserved-ranges
gpio: davinci: select GPIOLIB_IRQCHIP
gpiolib-acpi: Update file references in the Documentation and MAINTAINERS
gpiolib: acpi: Move quirks to a separate file
gpiolib: acpi: Add acpi_gpio_need_run_edge_events_on_boot() getter
gpiolib: acpi: Handle deferred list via new API
gpiolib: acpi: Make sure we fill struct acpi_gpio_info
gpiolib: acpi: Switch to use enum in acpi_gpio_in_ignore_list()
gpiolib: acpi: Use temporary variable for struct acpi_gpio_info
gpiolib: remove unneeded #ifdef
gpio: mpc8xxx: select GPIOLIB_IRQCHIP
gpio: pxa: select GPIOLIB_IRQCHIP
gpio: pxa: Make irq_chip immutable
gpio: timberdale: Make irq_chip immutable
gpio: xgene-sb: Make irq_chip immutable
gpio: davinci: Make irq_chip immutable
...
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r-- | drivers/gpio/gpiolib.c | 173 |
1 files changed, 97 insertions, 76 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 113c5d90f2df..fdafa0df1b43 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -266,6 +266,20 @@ struct gpio_device *gpiod_to_gpio_device(struct gpio_desc *desc) EXPORT_SYMBOL_GPL(gpiod_to_gpio_device); /** + * gpiod_is_equal() - Check if two GPIO descriptors refer to the same pin. + * @desc: Descriptor to compare. + * @other: The second descriptor to compare against. + * + * Returns: + * True if the descriptors refer to the same physical pin. False otherwise. + */ +bool gpiod_is_equal(struct gpio_desc *desc, struct gpio_desc *other) +{ + return desc == other; +} +EXPORT_SYMBOL_GPL(gpiod_is_equal); + +/** * gpio_device_get_base() - Get the base GPIO number allocated by this device * @gdev: GPIO device * @@ -342,6 +356,37 @@ static int gpiochip_find_base_unlocked(u16 ngpio) } } +/* + * This descriptor validation needs to be inserted verbatim into each + * function taking a descriptor, so we need to use a preprocessor + * macro to avoid endless duplication. If the desc is NULL it is an + * optional GPIO and calls should just bail out. + */ +static int validate_desc(const struct gpio_desc *desc, const char *func) +{ + if (!desc) + return 0; + + if (IS_ERR(desc)) { + pr_warn("%s: invalid GPIO (errorpointer: %pe)\n", func, desc); + return PTR_ERR(desc); + } + + return 1; +} + +#define VALIDATE_DESC(desc) do { \ + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ + return __valid; \ + } while (0) + +#define VALIDATE_DESC_VOID(desc) do { \ + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ + return; \ + } while (0) + static int gpiochip_get_direction(struct gpio_chip *gc, unsigned int offset) { int ret; @@ -376,11 +421,8 @@ int gpiod_get_direction(struct gpio_desc *desc) unsigned int offset; int ret; - /* - * We cannot use VALIDATE_DESC() as we must not return 0 for a NULL - * descriptor like we usually do. - */ - if (IS_ERR_OR_NULL(desc)) + ret = validate_desc(desc, __func__); + if (ret <= 0) return -EINVAL; CLASS(gpio_chip_guard, guard)(desc); @@ -880,14 +922,12 @@ static void machine_gpiochip_add(struct gpio_chip *gc) { struct gpiod_hog *hog; - mutex_lock(&gpio_machine_hogs_mutex); + guard(mutex)(&gpio_machine_hogs_mutex); list_for_each_entry(hog, &gpio_machine_hogs, list) { if (!strcmp(gc->label, hog->chip_label)) gpiochip_machine_hog(gc, hog); } - - mutex_unlock(&gpio_machine_hogs_mutex); } static void gpiochip_setup_devs(void) @@ -981,7 +1021,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct gpio_device *gdev; unsigned int desc_index; int base = 0; - int ret = 0; + int ret; /* Only allow one set() and one set_multiple(). */ if ((gc->set && gc->set_rv) || @@ -1006,11 +1046,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc)); - gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL); - if (gdev->id < 0) { - ret = gdev->id; + ret = ida_alloc(&gpio_ida, GFP_KERNEL); + if (ret < 0) goto err_free_gdev; - } + gdev->id = ret; ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id); if (ret) @@ -1513,9 +1552,8 @@ static int gpiochip_hierarchy_irq_domain_translate(struct irq_domain *d, unsigned int *type) { /* We support standard DT translation */ - if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { - return irq_domain_translate_twocell(d, fwspec, hwirq, type); - } + if (is_of_node(fwspec->fwnode)) + return irq_domain_translate_twothreecell(d, fwspec, hwirq, type); /* This is for board files and others not using DT */ if (is_fwnode_irqchip(fwspec->fwnode)) { @@ -1817,11 +1855,26 @@ static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) irq_set_chip_data(irq, NULL); } +static int gpiochip_irq_select(struct irq_domain *d, struct irq_fwspec *fwspec, + enum irq_domain_bus_token bus_token) +{ + struct fwnode_handle *fwnode = fwspec->fwnode; + struct gpio_chip *gc = d->host_data; + unsigned int index = fwspec->param[0]; + + if (fwspec->param_count == 3 && is_of_node(fwnode)) + return of_gpiochip_instance_match(gc, index); + + /* Fallback for twocells */ + return (fwnode && (d->fwnode == fwnode) && (d->bus_token == bus_token)); +} + static const struct irq_domain_ops gpiochip_domain_ops = { .map = gpiochip_irq_map, .unmap = gpiochip_irq_unmap, + .select = gpiochip_irq_select, /* Virtually all GPIO irqchips are twocell:ed */ - .xlate = irq_domain_xlate_twocell, + .xlate = irq_domain_xlate_twothreecell, }; static struct irq_domain *gpiochip_simple_create_domain(struct gpio_chip *gc) @@ -1841,7 +1894,6 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset) { struct irq_domain *domain = gc->irq.domain; -#ifdef CONFIG_GPIOLIB_IRQCHIP /* * Avoid race condition with other code, which tries to lookup * an IRQ before the irqchip has been properly registered, @@ -1849,7 +1901,6 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset) */ if (!gc->irq.initialized) return -EPROBE_DEFER; -#endif if (!gpiochip_irqchip_irq_valid(gc, offset)) return -ENXIO; @@ -2411,37 +2462,6 @@ out_clear_bit: return ret; } -/* - * This descriptor validation needs to be inserted verbatim into each - * function taking a descriptor, so we need to use a preprocessor - * macro to avoid endless duplication. If the desc is NULL it is an - * optional GPIO and calls should just bail out. - */ -static int validate_desc(const struct gpio_desc *desc, const char *func) -{ - if (!desc) - return 0; - - if (IS_ERR(desc)) { - pr_warn("%s: invalid GPIO (errorpointer)\n", func); - return PTR_ERR(desc); - } - - return 1; -} - -#define VALIDATE_DESC(desc) do { \ - int __valid = validate_desc(desc, __func__); \ - if (__valid <= 0) \ - return __valid; \ - } while (0) - -#define VALIDATE_DESC_VOID(desc) do { \ - int __valid = validate_desc(desc, __func__); \ - if (__valid <= 0) \ - return; \ - } while (0) - int gpiod_request(struct gpio_desc *desc, const char *label) { int ret = -EPROBE_DEFER; @@ -3051,7 +3071,7 @@ set_output_flag: */ int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) { - int ret = 0; + int ret; VALIDATE_DESC(desc); @@ -3084,7 +3104,7 @@ EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns); */ int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) { - int ret = 0; + int ret; VALIDATE_DESC(desc); @@ -3599,6 +3619,9 @@ static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value) static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { + if (unlikely(!test_bit(FLAG_IS_OUT, &desc->flags))) + return -EPERM; + CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) return -ENODEV; @@ -3670,6 +3693,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, if (!can_sleep) WARN_ON(array_info->gdev->can_sleep); + for (i = 0; i < array_size; i++) { + if (unlikely(!test_bit(FLAG_IS_OUT, + &desc_array[i]->flags))) + return -EPERM; + } + guard(srcu)(&array_info->gdev->srcu); gc = srcu_dereference(array_info->gdev->chip, &array_info->gdev->srcu); @@ -3729,6 +3758,9 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, int hwgpio = gpio_chip_hwgpio(desc); int value = test_bit(i, value_bitmap); + if (unlikely(!test_bit(FLAG_IS_OUT, &desc->flags))) + return -EPERM; + /* * Pins applicable for fast input but not for * fast output processing may have been already @@ -3950,13 +3982,10 @@ int gpiod_to_irq(const struct gpio_desc *desc) struct gpio_device *gdev; struct gpio_chip *gc; int offset; + int ret; - /* - * Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics - * requires this function to not return zero on an invalid descriptor - * but rather a negative error number. - */ - if (IS_ERR_OR_NULL(desc)) + ret = validate_desc(desc, __func__); + if (ret <= 0) return -EINVAL; gdev = desc->gdev; @@ -3968,13 +3997,12 @@ int gpiod_to_irq(const struct gpio_desc *desc) offset = gpio_chip_hwgpio(desc); if (gc->to_irq) { - int retirq = gc->to_irq(gc, offset); + ret = gc->to_irq(gc, offset); + if (ret) + return ret; /* Zero means NO_IRQ */ - if (!retirq) - return -ENXIO; - - return retirq; + return -ENXIO; } #ifdef CONFIG_GPIOLIB_IRQCHIP if (gc->irq.chip) { @@ -4329,12 +4357,10 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) { unsigned int i; - mutex_lock(&gpio_lookup_lock); + guard(mutex)(&gpio_lookup_lock); for (i = 0; i < n; i++) list_add_tail(&tables[i]->list, &gpio_lookup_list); - - mutex_unlock(&gpio_lookup_lock); } /** @@ -4393,11 +4419,9 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) if (!table) return; - mutex_lock(&gpio_lookup_lock); + guard(mutex)(&gpio_lookup_lock); list_del(&table->list); - - mutex_unlock(&gpio_lookup_lock); } EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table); @@ -4409,7 +4433,7 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) { struct gpiod_hog *hog; - mutex_lock(&gpio_machine_hogs_mutex); + guard(mutex)(&gpio_machine_hogs_mutex); for (hog = &hogs[0]; hog->chip_label; hog++) { list_add_tail(&hog->list, &gpio_machine_hogs); @@ -4423,8 +4447,6 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) if (gdev) gpiochip_machine_hog(gpio_device_get_chip(gdev), hog); } - - mutex_unlock(&gpio_machine_hogs_mutex); } EXPORT_SYMBOL_GPL(gpiod_add_hogs); @@ -4432,10 +4454,10 @@ void gpiod_remove_hogs(struct gpiod_hog *hogs) { struct gpiod_hog *hog; - mutex_lock(&gpio_machine_hogs_mutex); + guard(mutex)(&gpio_machine_hogs_mutex); + for (hog = &hogs[0]; hog->chip_label; hog++) list_del(&hog->list); - mutex_unlock(&gpio_machine_hogs_mutex); } EXPORT_SYMBOL_GPL(gpiod_remove_hogs); @@ -5114,8 +5136,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); */ void gpiod_put(struct gpio_desc *desc) { - if (desc) - gpiod_free(desc); + gpiod_free(desc); } EXPORT_SYMBOL_GPL(gpiod_put); |