diff options
| -rw-r--r-- | drivers/gpio/gpio-it87.c | 11 | ||||
| -rw-r--r-- | drivers/gpio/gpio-mpsse.c | 12 | ||||
| -rw-r--r-- | drivers/gpio/gpio-pca953x.c | 25 | ||||
| -rw-r--r-- | drivers/gpio/gpio-rockchip.c | 1 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-shared.c | 249 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib-shared.h | 4 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib.c | 136 |
7 files changed, 300 insertions, 138 deletions
diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c index 5d677bcfccf2..2ad3c239367b 100644 --- a/drivers/gpio/gpio-it87.c +++ b/drivers/gpio/gpio-it87.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/cleanup.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> @@ -241,23 +242,17 @@ static int it87_gpio_direction_out(struct gpio_chip *chip, mask = 1 << (gpio_num % 8); group = (gpio_num / 8); - spin_lock(&it87_gpio->lock); + guard(spinlock)(&it87_gpio->lock); rc = superio_enter(); if (rc) - goto exit; + return rc; /* set the output enable bit */ superio_set_mask(mask, group + it87_gpio->output_base); rc = it87_gpio_set(chip, gpio_num, val); - if (rc) - goto exit; - superio_exit(); - -exit: - spin_unlock(&it87_gpio->lock); return rc; } diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index ace652ba4df1..12191aeb6566 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -548,6 +548,13 @@ static void gpio_mpsse_ida_remove(void *data) ida_free(&gpio_mpsse_ida, priv->id); } +static void gpio_mpsse_usb_put_dev(void *data) +{ + struct mpsse_priv *priv = data; + + usb_put_dev(priv->udev); +} + static int mpsse_init_valid_mask(struct gpio_chip *chip, unsigned long *valid_mask, unsigned int ngpios) @@ -592,6 +599,10 @@ static int gpio_mpsse_probe(struct usb_interface *interface, INIT_LIST_HEAD(&priv->workers); priv->udev = usb_get_dev(interface_to_usbdev(interface)); + err = devm_add_action_or_reset(dev, gpio_mpsse_usb_put_dev, priv); + if (err) + return err; + priv->intf = interface; priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber; @@ -713,7 +724,6 @@ static void gpio_mpsse_disconnect(struct usb_interface *intf) priv->intf = NULL; usb_set_intfdata(intf, NULL); - usb_put_dev(priv->udev); } static struct usb_driver gpio_mpsse_driver = { diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 0a3916cc2772..8727ae54bc57 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -943,14 +943,35 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin DECLARE_BITMAP(old_stat, MAX_LINE); DECLARE_BITMAP(cur_stat, MAX_LINE); DECLARE_BITMAP(new_stat, MAX_LINE); + DECLARE_BITMAP(int_stat, MAX_LINE); DECLARE_BITMAP(trigger, MAX_LINE); DECLARE_BITMAP(edges, MAX_LINE); int ret; + if (chip->driver_data & PCA_PCAL) { + /* Read INT_STAT before it is cleared by the input-port read. */ + ret = pca953x_read_regs(chip, PCAL953X_INT_STAT, int_stat); + if (ret) + return false; + } + ret = pca953x_read_regs(chip, chip->regs->input, cur_stat); if (ret) return false; + if (chip->driver_data & PCA_PCAL) { + /* Detect short pulses via INT_STAT. */ + bitmap_and(trigger, int_stat, chip->irq_mask, gc->ngpio); + + /* Apply filter for rising/falling edge selection. */ + bitmap_replace(new_stat, chip->irq_trig_fall, chip->irq_trig_raise, + cur_stat, gc->ngpio); + + bitmap_and(int_stat, new_stat, trigger, gc->ngpio); + } else { + bitmap_zero(int_stat, gc->ngpio); + } + /* Remove output pins from the equation */ pca953x_read_regs(chip, chip->regs->direction, reg_direction); @@ -964,7 +985,8 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin if (bitmap_empty(chip->irq_trig_level_high, gc->ngpio) && bitmap_empty(chip->irq_trig_level_low, gc->ngpio)) { - if (bitmap_empty(trigger, gc->ngpio)) + if (bitmap_empty(trigger, gc->ngpio) && + bitmap_empty(int_stat, gc->ngpio)) return false; } @@ -972,6 +994,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin bitmap_and(old_stat, chip->irq_trig_raise, new_stat, gc->ngpio); bitmap_or(edges, old_stat, cur_stat, gc->ngpio); bitmap_and(pending, edges, trigger, gc->ngpio); + bitmap_or(pending, pending, int_stat, gc->ngpio); bitmap_and(cur_stat, new_stat, chip->irq_trig_level_high, gc->ngpio); bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio); diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index 47174eb3ba76..bae2061f15fc 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -593,6 +593,7 @@ static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank) gc->ngpio = bank->nr_pins; gc->label = bank->name; gc->parent = bank->dev; + gc->can_sleep = true; ret = gpiochip_add_data(gc, bank); if (ret) { diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index ba4b718d40a0..17343fdc9758 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -38,8 +38,10 @@ struct gpio_shared_ref { int dev_id; /* Protects the auxiliary device struct and the lookup table. */ struct mutex lock; + struct lock_class_key lock_key; struct auxiliary_device adev; struct gpiod_lookup_table *lookup; + bool is_reset_gpio; }; /* Represents a single GPIO pin. */ @@ -76,6 +78,60 @@ gpio_shared_find_entry(struct fwnode_handle *controller_node, return NULL; } +static struct gpio_shared_ref *gpio_shared_make_ref(struct fwnode_handle *fwnode, + const char *con_id, + enum gpiod_flags flags) +{ + char *con_id_cpy __free(kfree) = NULL; + + struct gpio_shared_ref *ref __free(kfree) = kzalloc(sizeof(*ref), GFP_KERNEL); + if (!ref) + return NULL; + + if (con_id) { + con_id_cpy = kstrdup(con_id, GFP_KERNEL); + if (!con_id_cpy) + return NULL; + } + + ref->dev_id = ida_alloc(&gpio_shared_ida, GFP_KERNEL); + if (ref->dev_id < 0) + return NULL; + + ref->flags = flags; + ref->con_id = no_free_ptr(con_id_cpy); + ref->fwnode = fwnode; + lockdep_register_key(&ref->lock_key); + mutex_init_with_key(&ref->lock, &ref->lock_key); + + return no_free_ptr(ref); +} + +static int gpio_shared_setup_reset_proxy(struct gpio_shared_entry *entry, + enum gpiod_flags flags) +{ + struct gpio_shared_ref *ref; + + list_for_each_entry(ref, &entry->refs, list) { + if (ref->is_reset_gpio) + /* Already set-up. */ + return 0; + } + + ref = gpio_shared_make_ref(NULL, "reset", flags); + if (!ref) + return -ENOMEM; + + ref->is_reset_gpio = true; + + list_add_tail(&ref->list, &entry->refs); + + pr_debug("Created a secondary shared GPIO reference for potential reset-gpio device for GPIO %u at %s\n", + entry->offset, fwnode_get_name(entry->fwnode)); + + return 0; +} + /* Handle all special nodes that we should ignore. */ static bool gpio_shared_of_node_ignore(struct device_node *node) { @@ -106,6 +162,7 @@ static int gpio_shared_of_traverse(struct device_node *curr) size_t con_id_len, suffix_len; struct fwnode_handle *fwnode; struct of_phandle_args args; + struct gpio_shared_ref *ref; struct property *prop; unsigned int offset; const char *suffix; @@ -138,6 +195,7 @@ static int gpio_shared_of_traverse(struct device_node *curr) for (i = 0; i < count; i++) { struct device_node *np __free(device_node) = NULL; + char *con_id __free(kfree) = NULL; ret = of_parse_phandle_with_args(curr, prop->name, "#gpio-cells", i, @@ -182,15 +240,6 @@ static int gpio_shared_of_traverse(struct device_node *curr) list_add_tail(&entry->list, &gpio_shared_list); } - struct gpio_shared_ref *ref __free(kfree) = - kzalloc(sizeof(*ref), GFP_KERNEL); - if (!ref) - return -ENOMEM; - - ref->fwnode = fwnode_handle_get(of_fwnode_handle(curr)); - ref->flags = args.args[1]; - mutex_init(&ref->lock); - if (strends(prop->name, "gpios")) suffix = "-gpios"; else if (strends(prop->name, "gpio")) @@ -202,27 +251,32 @@ static int gpio_shared_of_traverse(struct device_node *curr) /* We only set con_id if there's actually one. */ if (strcmp(prop->name, "gpios") && strcmp(prop->name, "gpio")) { - ref->con_id = kstrdup(prop->name, GFP_KERNEL); - if (!ref->con_id) + con_id = kstrdup(prop->name, GFP_KERNEL); + if (!con_id) return -ENOMEM; - con_id_len = strlen(ref->con_id); + con_id_len = strlen(con_id); suffix_len = strlen(suffix); - ref->con_id[con_id_len - suffix_len] = '\0'; + con_id[con_id_len - suffix_len] = '\0'; } - ref->dev_id = ida_alloc(&gpio_shared_ida, GFP_KERNEL); - if (ref->dev_id < 0) { - kfree(ref->con_id); + ref = gpio_shared_make_ref(fwnode_handle_get(of_fwnode_handle(curr)), + con_id, args.args[1]); + if (!ref) return -ENOMEM; - } if (!list_empty(&entry->refs)) pr_debug("GPIO %u at %s is shared by multiple firmware nodes\n", entry->offset, fwnode_get_name(entry->fwnode)); - list_add_tail(&no_free_ptr(ref)->list, &entry->refs); + list_add_tail(&ref->list, &entry->refs); + + if (strcmp(prop->name, "reset-gpios") == 0) { + ret = gpio_shared_setup_reset_proxy(entry, args.args[1]); + if (ret) + return ret; + } } } @@ -306,21 +360,17 @@ static bool gpio_shared_dev_is_reset_gpio(struct device *consumer, struct fwnode_handle *reset_fwnode = dev_fwnode(consumer); struct fwnode_reference_args ref_args, aux_args; struct device *parent = consumer->parent; + struct gpio_shared_ref *real_ref; bool match; int ret; + lockdep_assert_held(&ref->lock); + /* The reset-gpio device must have a parent AND a firmware node. */ if (!parent || !reset_fwnode) return false; /* - * FIXME: use device_is_compatible() once the reset-gpio drivers gains - * a compatible string which it currently does not have. - */ - if (!strstarts(dev_name(consumer), "reset.gpio.")) - return false; - - /* * Parent of the reset-gpio auxiliary device is the GPIO chip whose * fwnode we stored in the entry structure. */ @@ -328,33 +378,61 @@ static bool gpio_shared_dev_is_reset_gpio(struct device *consumer, return false; /* - * The device associated with the shared reference's firmware node is - * the consumer of the reset control exposed by the reset-gpio device. - * It must have a "reset-gpios" property that's referencing the entry's - * firmware node. - * - * The reference args must agree between the real consumer and the - * auxiliary reset-gpio device. + * Now we need to find the actual pin we want to assign to this + * reset-gpio device. To that end: iterate over the list of references + * of this entry and see if there's one, whose reset-gpios property's + * arguments match the ones from this consumer's node. */ - ret = fwnode_property_get_reference_args(ref->fwnode, "reset-gpios", - NULL, 2, 0, &ref_args); - if (ret) - return false; + list_for_each_entry(real_ref, &entry->refs, list) { + if (real_ref == ref) + continue; + + guard(mutex)(&real_ref->lock); + + if (!real_ref->fwnode) + continue; + + /* + * The device associated with the shared reference's firmware + * node is the consumer of the reset control exposed by the + * reset-gpio device. It must have a "reset-gpios" property + * that's referencing the entry's firmware node. + * + * The reference args must agree between the real consumer and + * the auxiliary reset-gpio device. + */ + ret = fwnode_property_get_reference_args(real_ref->fwnode, + "reset-gpios", + NULL, 2, 0, &ref_args); + if (ret) + continue; + + ret = fwnode_property_get_reference_args(reset_fwnode, "reset-gpios", + NULL, 2, 0, &aux_args); + if (ret) { + fwnode_handle_put(ref_args.fwnode); + continue; + } + + match = ((ref_args.fwnode == entry->fwnode) && + (aux_args.fwnode == entry->fwnode) && + (ref_args.args[0] == aux_args.args[0])); - ret = fwnode_property_get_reference_args(reset_fwnode, "reset-gpios", - NULL, 2, 0, &aux_args); - if (ret) { fwnode_handle_put(ref_args.fwnode); - return false; - } + fwnode_handle_put(aux_args.fwnode); + + if (!match) + continue; - match = ((ref_args.fwnode == entry->fwnode) && - (aux_args.fwnode == entry->fwnode) && - (ref_args.args[0] == aux_args.args[0])); + /* + * Reuse the fwnode of the real device, next time we'll use it + * in the normal path. + */ + ref->fwnode = fwnode_handle_get(reset_fwnode); + return true; + } - fwnode_handle_put(ref_args.fwnode); - fwnode_handle_put(aux_args.fwnode); - return match; + return false; } #else static bool gpio_shared_dev_is_reset_gpio(struct device *consumer, @@ -365,24 +443,33 @@ static bool gpio_shared_dev_is_reset_gpio(struct device *consumer, } #endif /* CONFIG_RESET_GPIO */ -int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags) +int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, + unsigned long lflags) { const char *dev_id = dev_name(consumer); + struct gpiod_lookup_table *lookup; struct gpio_shared_entry *entry; struct gpio_shared_ref *ref; - struct gpiod_lookup_table *lookup __free(kfree) = - kzalloc(struct_size(lookup, table, 2), GFP_KERNEL); - if (!lookup) - return -ENOMEM; - list_for_each_entry(entry, &gpio_shared_list, list) { list_for_each_entry(ref, &entry->refs, list) { - if (!device_match_fwnode(consumer, ref->fwnode) && - !gpio_shared_dev_is_reset_gpio(consumer, entry, ref)) + guard(mutex)(&ref->lock); + + /* + * FIXME: use device_is_compatible() once the reset-gpio + * drivers gains a compatible string which it currently + * does not have. + */ + if (!ref->fwnode && strstarts(dev_name(consumer), "reset.gpio.")) { + if (!gpio_shared_dev_is_reset_gpio(consumer, entry, ref)) + continue; + } else if (!device_match_fwnode(consumer, ref->fwnode)) { continue; + } - guard(mutex)(&ref->lock); + if ((!con_id && ref->con_id) || (con_id && !ref->con_id) || + (con_id && ref->con_id && strcmp(con_id, ref->con_id) != 0)) + continue; /* We've already done that on a previous request. */ if (ref->lookup) @@ -395,6 +482,10 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags) if (!key) return -ENOMEM; + lookup = kzalloc(struct_size(lookup, table, 2), GFP_KERNEL); + if (!lookup) + return -ENOMEM; + pr_debug("Adding machine lookup entry for a shared GPIO for consumer %s, with key '%s' and con_id '%s'\n", dev_id, key, ref->con_id ?: "none"); @@ -402,7 +493,7 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags) lookup->table[0] = GPIO_LOOKUP(no_free_ptr(key), 0, ref->con_id, lflags); - ref->lookup = no_free_ptr(lookup); + ref->lookup = lookup; gpiod_add_lookup_table(ref->lookup); return 0; @@ -466,8 +557,9 @@ int gpio_device_setup_shared(struct gpio_device *gdev) entry->offset, gpio_device_get_label(gdev)); list_for_each_entry(ref, &entry->refs, list) { - pr_debug("Setting up a shared GPIO entry for %s\n", - fwnode_get_name(ref->fwnode)); + pr_debug("Setting up a shared GPIO entry for %s (con_id: '%s')\n", + fwnode_get_name(ref->fwnode) ?: "(no fwnode)", + ref->con_id ?: "(none)"); ret = gpio_shared_make_adev(gdev, entry, ref); if (ret) @@ -487,15 +579,6 @@ void gpio_device_teardown_shared(struct gpio_device *gdev) if (!device_match_fwnode(&gdev->dev, entry->fwnode)) continue; - /* - * For some reason if we call synchronize_srcu() in GPIO core, - * descent here and take this mutex and then recursively call - * synchronize_srcu() again from gpiochip_remove() (which is - * totally fine) called after gpio_shared_remove_adev(), - * lockdep prints a false positive deadlock splat. Disable - * lockdep here. - */ - lockdep_off(); list_for_each_entry(ref, &entry->refs, list) { guard(mutex)(&ref->lock); @@ -508,7 +591,6 @@ void gpio_device_teardown_shared(struct gpio_device *gdev) gpio_shared_remove_adev(&ref->adev); } - lockdep_on(); } } @@ -604,6 +686,7 @@ static void gpio_shared_drop_ref(struct gpio_shared_ref *ref) { list_del(&ref->list); mutex_destroy(&ref->lock); + lockdep_unregister_key(&ref->lock_key); kfree(ref->con_id); ida_free(&gpio_shared_ida, ref->dev_id); fwnode_handle_put(ref->fwnode); @@ -635,12 +718,38 @@ static void __init gpio_shared_teardown(void) } } +static bool gpio_shared_entry_is_really_shared(struct gpio_shared_entry *entry) +{ + size_t num_nodes = list_count_nodes(&entry->refs); + struct gpio_shared_ref *ref; + + if (num_nodes <= 1) + return false; + + if (num_nodes > 2) + return true; + + /* Exactly two references: */ + list_for_each_entry(ref, &entry->refs, list) { + /* + * Corner-case: the second reference comes from the potential + * reset-gpio instance. However, this pin is not really shared + * as it would have three references in this case. Avoid + * creating unnecessary proxies. + */ + if (ref->is_reset_gpio) + return false; + } + + return true; +} + static void gpio_shared_free_exclusive(void) { struct gpio_shared_entry *entry, *epos; list_for_each_entry_safe(entry, epos, &gpio_shared_list, list) { - if (list_count_nodes(&entry->refs) > 1) + if (gpio_shared_entry_is_really_shared(entry)) continue; gpio_shared_drop_ref(list_first_entry(&entry->refs, diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h index 667dbdff3585..40568ef7364c 100644 --- a/drivers/gpio/gpiolib-shared.h +++ b/drivers/gpio/gpiolib-shared.h @@ -16,7 +16,8 @@ struct device; int gpio_device_setup_shared(struct gpio_device *gdev); void gpio_device_teardown_shared(struct gpio_device *gdev); -int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags); +int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id, + unsigned long lflags); #else @@ -28,6 +29,7 @@ static inline int gpio_device_setup_shared(struct gpio_device *gdev) static inline void gpio_device_teardown_shared(struct gpio_device *gdev) { } static inline int gpio_shared_add_proxy_lookup(struct device *consumer, + const char *con_id, unsigned long lflags) { return 0; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 91e0c384f34a..dcf427d3cf43 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1105,6 +1105,18 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev->ngpio = gc->ngpio; gdev->can_sleep = gc->can_sleep; + rwlock_init(&gdev->line_state_lock); + RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); + BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); + + ret = init_srcu_struct(&gdev->srcu); + if (ret) + goto err_free_label; + + ret = init_srcu_struct(&gdev->desc_srcu); + if (ret) + goto err_cleanup_gdev_srcu; + scoped_guard(mutex, &gpio_devices_lock) { /* * TODO: this allocates a Linux GPIO number base in the global @@ -1119,7 +1131,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (base < 0) { ret = base; base = 0; - goto err_free_label; + goto err_cleanup_desc_srcu; } /* @@ -1139,22 +1151,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ret = gpiodev_add_to_list_unlocked(gdev); if (ret) { gpiochip_err(gc, "GPIO integer space overlap, cannot add chip\n"); - goto err_free_label; + goto err_cleanup_desc_srcu; } } - rwlock_init(&gdev->line_state_lock); - RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); - BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); - - ret = init_srcu_struct(&gdev->srcu); - if (ret) - goto err_remove_from_list; - - ret = init_srcu_struct(&gdev->desc_srcu); - if (ret) - goto err_cleanup_gdev_srcu; - #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); #endif @@ -1164,11 +1164,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ret = gpiochip_set_names(gc); if (ret) - goto err_cleanup_desc_srcu; + goto err_remove_from_list; ret = gpiochip_init_valid_mask(gc); if (ret) - goto err_cleanup_desc_srcu; + goto err_remove_from_list; for (desc_index = 0; desc_index < gc->ngpio; desc_index++) { struct gpio_desc *desc = &gdev->descs[desc_index]; @@ -1248,10 +1248,6 @@ err_remove_of_chip: of_gpiochip_remove(gc); err_free_valid_mask: gpiochip_free_valid_mask(gc); -err_cleanup_desc_srcu: - cleanup_srcu_struct(&gdev->desc_srcu); -err_cleanup_gdev_srcu: - cleanup_srcu_struct(&gdev->srcu); err_remove_from_list: scoped_guard(mutex, &gpio_devices_lock) list_del_rcu(&gdev->list); @@ -1261,6 +1257,10 @@ err_remove_from_list: gpio_device_put(gdev); goto err_print_message; } +err_cleanup_desc_srcu: + cleanup_srcu_struct(&gdev->desc_srcu); +err_cleanup_gdev_srcu: + cleanup_srcu_struct(&gdev->srcu); err_free_label: kfree_const(gdev->label); err_free_descs: @@ -4508,45 +4508,41 @@ void gpiod_remove_hogs(struct gpiod_hog *hogs) } EXPORT_SYMBOL_GPL(gpiod_remove_hogs); -static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) +static bool gpiod_match_lookup_table(struct device *dev, + const struct gpiod_lookup_table *table) { const char *dev_id = dev ? dev_name(dev) : NULL; - struct gpiod_lookup_table *table; - list_for_each_entry(table, &gpio_lookup_list, list) { - if (table->dev_id && dev_id) { - /* - * Valid strings on both ends, must be identical to have - * a match - */ - if (!strcmp(table->dev_id, dev_id)) - return table; - } else { - /* - * One of the pointers is NULL, so both must be to have - * a match - */ - if (dev_id == table->dev_id) - return table; - } + lockdep_assert_held(&gpio_lookup_lock); + + if (table->dev_id && dev_id) { + /* + * Valid strings on both ends, must be identical to have + * a match + */ + if (!strcmp(table->dev_id, dev_id)) + return true; + } else { + /* + * One of the pointers is NULL, so both must be to have + * a match + */ + if (dev_id == table->dev_id) + return true; } - return NULL; + return false; } -static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, - unsigned int idx, unsigned long *flags) +static struct gpio_desc *gpio_desc_table_match(struct device *dev, const char *con_id, + unsigned int idx, unsigned long *flags, + struct gpiod_lookup_table *table) { - struct gpio_desc *desc = ERR_PTR(-ENOENT); - struct gpiod_lookup_table *table; + struct gpio_desc *desc; struct gpiod_lookup *p; struct gpio_chip *gc; - guard(mutex)(&gpio_lookup_lock); - - table = gpiod_find_lookup_table(dev); - if (!table) - return desc; + lockdep_assert_held(&gpio_lookup_lock); for (p = &table->table[0]; p->key; p++) { /* idx must always match exactly */ @@ -4600,7 +4596,30 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, return desc; } - return desc; + return NULL; +} + +static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, + unsigned int idx, unsigned long *flags) +{ + struct gpiod_lookup_table *table; + struct gpio_desc *desc; + + guard(mutex)(&gpio_lookup_lock); + + list_for_each_entry(table, &gpio_lookup_list, list) { + if (!gpiod_match_lookup_table(dev, table)) + continue; + + desc = gpio_desc_table_match(dev, con_id, idx, flags, table); + if (!desc) + continue; + + /* On IS_ERR() or match. */ + return desc; + } + + return ERR_PTR(-ENOENT); } static int platform_gpio_count(struct device *dev, const char *con_id) @@ -4610,14 +4629,16 @@ static int platform_gpio_count(struct device *dev, const char *con_id) unsigned int count = 0; scoped_guard(mutex, &gpio_lookup_lock) { - table = gpiod_find_lookup_table(dev); - if (!table) - return -ENOENT; + list_for_each_entry(table, &gpio_lookup_list, list) { + if (!gpiod_match_lookup_table(dev, table)) + continue; - for (p = &table->table[0]; p->key; p++) { - if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) || - (!con_id && !p->con_id)) - count++; + for (p = &table->table[0]; p->key; p++) { + if ((con_id && p->con_id && + !strcmp(con_id, p->con_id)) || + (!con_id && !p->con_id)) + count++; + } } } @@ -4696,7 +4717,8 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer, * lookup table for the proxy device as previously * we only knew the consumer's fwnode. */ - ret = gpio_shared_add_proxy_lookup(consumer, lookupflags); + ret = gpio_shared_add_proxy_lookup(consumer, con_id, + lookupflags); if (ret) return ERR_PTR(ret); |
