Skip to content

Commit d578b31

Browse files
author
Bartosz Golaszewski
committed
gpio: shared: fix a false-positive sharing detection with reset-gpios
After scanning the devicetree, we remove all entries that have only one reference, while creating GPIO shared proxies for the remaining, shared entries. However: for the reset-gpio corner-case, we will have two references for a "reset-gpios" pin that's not really shared. In this case one will come from the actual consumer fwnode and the other from the potential auxiliary reset-gpio device. This causes the GPIO core to create unnecessary GPIO shared proxy devices for pins that are not really shared. Add a function that can detect this situation and remove entries that have exactly two references but one of them is a reset-gpio. Fixes: 7b78b26 ("gpio: shared: handle the reset-gpios corner case") Link: https://lore.kernel.org/r/20260108-gpio-shared-false-positive-v1-1-5dbf8d1b2f7d@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
1 parent 36f597b commit d578b31

1 file changed

Lines changed: 32 additions & 2 deletions

File tree

drivers/gpio/gpiolib-shared.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ struct gpio_shared_ref {
4141
struct lock_class_key lock_key;
4242
struct auxiliary_device adev;
4343
struct gpiod_lookup_table *lookup;
44+
bool is_reset_gpio;
4445
};
4546

4647
/* Represents a single GPIO pin. */
@@ -112,14 +113,17 @@ static int gpio_shared_setup_reset_proxy(struct gpio_shared_entry *entry,
112113
struct gpio_shared_ref *ref;
113114

114115
list_for_each_entry(ref, &entry->refs, list) {
115-
if (!ref->fwnode && ref->con_id && strcmp(ref->con_id, "reset") == 0)
116+
if (ref->is_reset_gpio)
117+
/* Already set-up. */
116118
return 0;
117119
}
118120

119121
ref = gpio_shared_make_ref(NULL, "reset", flags);
120122
if (!ref)
121123
return -ENOMEM;
122124

125+
ref->is_reset_gpio = true;
126+
123127
list_add_tail(&ref->list, &entry->refs);
124128

125129
pr_debug("Created a secondary shared GPIO reference for potential reset-gpio device for GPIO %u at %s\n",
@@ -714,12 +718,38 @@ static void __init gpio_shared_teardown(void)
714718
}
715719
}
716720

721+
static bool gpio_shared_entry_is_really_shared(struct gpio_shared_entry *entry)
722+
{
723+
size_t num_nodes = list_count_nodes(&entry->refs);
724+
struct gpio_shared_ref *ref;
725+
726+
if (num_nodes <= 1)
727+
return false;
728+
729+
if (num_nodes > 2)
730+
return true;
731+
732+
/* Exactly two references: */
733+
list_for_each_entry(ref, &entry->refs, list) {
734+
/*
735+
* Corner-case: the second reference comes from the potential
736+
* reset-gpio instance. However, this pin is not really shared
737+
* as it would have three references in this case. Avoid
738+
* creating unnecessary proxies.
739+
*/
740+
if (ref->is_reset_gpio)
741+
return false;
742+
}
743+
744+
return true;
745+
}
746+
717747
static void gpio_shared_free_exclusive(void)
718748
{
719749
struct gpio_shared_entry *entry, *epos;
720750

721751
list_for_each_entry_safe(entry, epos, &gpio_shared_list, list) {
722-
if (list_count_nodes(&entry->refs) > 1)
752+
if (gpio_shared_entry_is_really_shared(entry))
723753
continue;
724754

725755
gpio_shared_drop_ref(list_first_entry(&entry->refs,

0 commit comments

Comments
 (0)