Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpiolib: allow multiple lookup tables per consumer

The GPIO machine lookup mechanism dates back to old ARM board files
where lookup tables would all be defined in a single place. Since then,
lookup tables have also been used to address various DT and ACPI quirks
like missing GPIOs and - as of recently - setting up shared GPIO proxy
devices.

The lookup itself stops when we find the first matching table for a
consumer and - if it doesn't contain the right entry - the lookup fails.

Since the tables can now be defined in multiple places and we can't know
how many there are, effectively limiting a consumer to only a single
lookup table is no longer viable.

Rework the code to always look through all tables matching the consumer.

Link: https://lore.kernel.org/r/20251222-gpio-shared-reset-gpio-proxy-v1-1-8d4bba7d8c14@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

+56 -35
+56 -35
drivers/gpio/gpiolib.c
··· 4508 4508 } 4509 4509 EXPORT_SYMBOL_GPL(gpiod_remove_hogs); 4510 4510 4511 - static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) 4511 + static bool gpiod_match_lookup_table(struct device *dev, 4512 + const struct gpiod_lookup_table *table) 4512 4513 { 4513 4514 const char *dev_id = dev ? dev_name(dev) : NULL; 4514 - struct gpiod_lookup_table *table; 4515 4515 4516 - list_for_each_entry(table, &gpio_lookup_list, list) { 4517 - if (table->dev_id && dev_id) { 4518 - /* 4519 - * Valid strings on both ends, must be identical to have 4520 - * a match 4521 - */ 4522 - if (!strcmp(table->dev_id, dev_id)) 4523 - return table; 4524 - } else { 4525 - /* 4526 - * One of the pointers is NULL, so both must be to have 4527 - * a match 4528 - */ 4529 - if (dev_id == table->dev_id) 4530 - return table; 4531 - } 4516 + lockdep_assert_held(&gpio_lookup_lock); 4517 + 4518 + if (table->dev_id && dev_id) { 4519 + /* 4520 + * Valid strings on both ends, must be identical to have 4521 + * a match 4522 + */ 4523 + if (!strcmp(table->dev_id, dev_id)) 4524 + return true; 4525 + } else { 4526 + /* 4527 + * One of the pointers is NULL, so both must be to have 4528 + * a match 4529 + */ 4530 + if (dev_id == table->dev_id) 4531 + return true; 4532 4532 } 4533 4533 4534 - return NULL; 4534 + return false; 4535 4535 } 4536 4536 4537 - static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, 4538 - unsigned int idx, unsigned long *flags) 4537 + static struct gpio_desc *gpio_desc_table_match(struct device *dev, const char *con_id, 4538 + unsigned int idx, unsigned long *flags, 4539 + struct gpiod_lookup_table *table) 4539 4540 { 4540 - struct gpio_desc *desc = ERR_PTR(-ENOENT); 4541 - struct gpiod_lookup_table *table; 4541 + struct gpio_desc *desc; 4542 4542 struct gpiod_lookup *p; 4543 4543 struct gpio_chip *gc; 4544 4544 4545 - guard(mutex)(&gpio_lookup_lock); 4546 - 4547 - table = gpiod_find_lookup_table(dev); 4548 - if (!table) 4549 - return desc; 4545 + lockdep_assert_held(&gpio_lookup_lock); 4550 4546 4551 4547 for (p = &table->table[0]; p->key; p++) { 4552 4548 /* idx must always match exactly */ ··· 4596 4600 return desc; 4597 4601 } 4598 4602 4603 + return NULL; 4604 + } 4605 + 4606 + static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, 4607 + unsigned int idx, unsigned long *flags) 4608 + { 4609 + struct gpio_desc *desc = ERR_PTR(-ENOENT); 4610 + struct gpiod_lookup_table *table; 4611 + 4612 + guard(mutex)(&gpio_lookup_lock); 4613 + 4614 + list_for_each_entry(table, &gpio_lookup_list, list) { 4615 + if (!gpiod_match_lookup_table(dev, table)) 4616 + continue; 4617 + 4618 + desc = gpio_desc_table_match(dev, con_id, idx, flags, table); 4619 + if (!desc) 4620 + continue; 4621 + 4622 + /* On IS_ERR() or match. */ 4623 + return desc; 4624 + } 4625 + 4599 4626 return desc; 4600 4627 } 4601 4628 ··· 4629 4610 unsigned int count = 0; 4630 4611 4631 4612 scoped_guard(mutex, &gpio_lookup_lock) { 4632 - table = gpiod_find_lookup_table(dev); 4633 - if (!table) 4634 - return -ENOENT; 4613 + list_for_each_entry(table, &gpio_lookup_list, list) { 4614 + if (!gpiod_match_lookup_table(dev, table)) 4615 + continue; 4635 4616 4636 - for (p = &table->table[0]; p->key; p++) { 4637 - if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) || 4638 - (!con_id && !p->con_id)) 4639 - count++; 4617 + for (p = &table->table[0]; p->key; p++) { 4618 + if ((con_id && p->con_id && 4619 + !strcmp(con_id, p->con_id)) || 4620 + (!con_id && !p->con_id)) 4621 + count++; 4622 + } 4640 4623 } 4641 4624 } 4642 4625