gpio updates for v6.19-rc1

- fix spinlock op type after conversion to lock guards
 - fix a memory leak in error path in gpio-regmap
 - Kconfig fixes in GPIO drivers
 - add a GPIO ACPI quirk for Dell Precision 7780
 - set of fixes for shared GPIO management
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEkeUTLeW1Rh17omX8BZ0uy/82hMMFAmk7a9QACgkQBZ0uy/82
 hMMPORAAnZ1I1j6NqZTnx+6Jhe2BeIqodXHT+iJLvyvYvfDguMqC4v+JZ4eOStY3
 UN17bQv/UeKkfGHpD8oe5cJZkF+088Dy2+f7nBpsBw7sj+nty+PZy6QcGEqnmdGb
 lhmjBv8UQ6jNz7Kg7pzY/Rp4p2Vt1m6Br/7XwwlOAoVEreofGfrtQIZg72xtT6rg
 yacr37z7PAxlHDh9/oMWU0/p+E/deN7Vi/UVaiP+C23PmcyyUtTC+gML7BMMM6cr
 +3mrX4ae3HOfWLZinxF4Ike2J7TacqPmub7imHRFCgz+RQ8ZCmlucHs5L8C+jPXf
 HRykRIssTC3n9WvXrmlatv5xhJI5wUisMFjgxFeb57UZZG0oJPHEaN/SNU4KvPQS
 ea2mdiA1hb6EjaU7fAgdLvy6KNmxOew3PjSuMB0cnEpsVUqaKwTIRLuUYzXLBkwE
 M0K3zJoZLvzlfy1uaKw6PR2TAjF7BhZGT/KnQOvICVopFEW7EhYTYtprfVNnkt8D
 U2chdPLFCBVrKdkg5hV9UAH9r7TTe2eG3BjX2TazWnOZLpPSelYGrKzoWorQOS83
 3B78tn7Tx62NXYXlkTbwGvVs6cUQC+JGdaEoLmKK7LhAW1u/g2DO10JQJLgZePwi
 czfUdKE29wfQy/piwn9+O+pbe15Iqbx54DmIZ80EgZOOLVya9TA=
 =oxk1
 -----END PGP SIGNATURE-----

Merge tag 'gpio-fixes-for-v6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

Pull gpio updates from Bartosz Golaszewski:

 - fix spinlock op type after conversion to lock guards

 - fix a memory leak in error path in gpio-regmap

 - Kconfig fixes in GPIO drivers

 - add a GPIO ACPI quirk for Dell Precision 7780

 - set of fixes for shared GPIO management

* tag 'gpio-fixes-for-v6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
  gpio: shared: make locking more fine-grained
  gpio: shared: fix auxiliary device cleanup order
  gpio: shared: check if a reference is populated before cleaning its resources
  gpio: shared: fix NULL-pointer dereference in teardown path
  gpio: shared: ignore disabled nodes when traversing the device-tree
  gpiolib: acpi: Add quirk for Dell Precision 7780
  gpio: tb10x: fix OF_GPIO dependency
  gpio: qixis: select CONFIG_REGMAP_MMIO
  gpio: regmap: Fix memleak in error path in gpio_regmap_register()
  gpio: mmio: fix bad guard conversion
pull/1354/merge
Linus Torvalds 2025-12-13 16:36:57 +12:00
commit a6bb419c1c
5 changed files with 63 additions and 27 deletions

View File

@ -737,7 +737,6 @@ config GPIO_TB10X
depends on ARC_PLAT_TB10X || COMPILE_TEST depends on ARC_PLAT_TB10X || COMPILE_TEST
select GPIO_GENERIC select GPIO_GENERIC
select GENERIC_IRQ_CHIP select GENERIC_IRQ_CHIP
select OF_GPIO
config GPIO_TEGRA config GPIO_TEGRA
tristate "NVIDIA Tegra GPIO support" tristate "NVIDIA Tegra GPIO support"
@ -1568,6 +1567,7 @@ config GPIO_QIXIS_FPGA
tristate "NXP QIXIS FPGA GPIO support" tristate "NXP QIXIS FPGA GPIO support"
depends on MFD_SIMPLE_MFD_I2C || COMPILE_TEST depends on MFD_SIMPLE_MFD_I2C || COMPILE_TEST
select GPIO_REGMAP select GPIO_REGMAP
select REGMAP_MMIO
help help
This enables support for the GPIOs found in the QIXIS FPGA which is This enables support for the GPIOs found in the QIXIS FPGA which is
integrated on some NXP Layerscape boards such as LX2160ARDB and integrated on some NXP Layerscape boards such as LX2160ARDB and

View File

@ -231,7 +231,7 @@ static int gpio_mmio_set(struct gpio_chip *gc, unsigned int gpio, int val)
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long mask = gpio_mmio_line2mask(gc, gpio); unsigned long mask = gpio_mmio_line2mask(gc, gpio);
guard(raw_spinlock)(&chip->lock); guard(raw_spinlock_irqsave)(&chip->lock);
if (val) if (val)
chip->sdata |= mask; chip->sdata |= mask;
@ -262,7 +262,7 @@ static int gpio_mmio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long mask = gpio_mmio_line2mask(gc, gpio); unsigned long mask = gpio_mmio_line2mask(gc, gpio);
guard(raw_spinlock)(&chip->lock); guard(raw_spinlock_irqsave)(&chip->lock);
if (val) if (val)
chip->sdata |= mask; chip->sdata |= mask;
@ -302,7 +302,7 @@ static void gpio_mmio_set_multiple_single_reg(struct gpio_chip *gc,
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long set_mask, clear_mask; unsigned long set_mask, clear_mask;
guard(raw_spinlock)(&chip->lock); guard(raw_spinlock_irqsave)(&chip->lock);
gpio_mmio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask); gpio_mmio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
@ -391,7 +391,7 @@ static int gpio_mmio_dir_in(struct gpio_chip *gc, unsigned int gpio)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
scoped_guard(raw_spinlock, &chip->lock) { scoped_guard(raw_spinlock_irqsave, &chip->lock) {
chip->sdir &= ~gpio_mmio_line2mask(gc, gpio); chip->sdir &= ~gpio_mmio_line2mask(gc, gpio);
if (chip->reg_dir_in) if (chip->reg_dir_in)
@ -431,7 +431,7 @@ static void gpio_mmio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
{ {
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc); struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
guard(raw_spinlock)(&chip->lock); guard(raw_spinlock_irqsave)(&chip->lock);
chip->sdir |= gpio_mmio_line2mask(gc, gpio); chip->sdir |= gpio_mmio_line2mask(gc, gpio);

View File

@ -338,7 +338,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
config->regmap_irq_line, config->regmap_irq_flags, config->regmap_irq_line, config->regmap_irq_flags,
0, config->regmap_irq_chip, &gpio->irq_chip_data); 0, config->regmap_irq_chip, &gpio->irq_chip_data);
if (ret) if (ret)
goto err_free_bitmap; goto err_remove_gpiochip;
irq_domain = regmap_irq_get_domain(gpio->irq_chip_data); irq_domain = regmap_irq_get_domain(gpio->irq_chip_data);
} else } else

View File

@ -370,6 +370,28 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
.ignore_wake = "ASCP1A00:00@8", .ignore_wake = "ASCP1A00:00@8",
}, },
}, },
{
/*
* Spurious wakeups, likely from touchpad controller
* Dell Precision 7780
* Found in BIOS 1.24.1
*
* Found in touchpad firmware, installed by Dell Touchpad Firmware Update Utility version 1160.4196.9, A01
* ( Dell-Touchpad-Firmware-Update-Utility_VYGNN_WIN64_1160.4196.9_A00.EXE ),
* released on 11 Jul 2024
*
* https://lore.kernel.org/linux-i2c/197ae95ffd8.dc819e60457077.7692120488609091556@zohomail.com/
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_FAMILY, "Precision"),
DMI_MATCH(DMI_PRODUCT_NAME, "Precision 7780"),
DMI_MATCH(DMI_BOARD_NAME, "0C6JVW"),
},
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
.ignore_wake = "VEN_0488:00@355",
},
},
{} /* Terminating entry */ {} /* Terminating entry */
}; };

View File

@ -36,6 +36,8 @@ struct gpio_shared_ref {
enum gpiod_flags flags; enum gpiod_flags flags;
char *con_id; char *con_id;
int dev_id; int dev_id;
/* Protects the auxiliary device struct and the lookup table. */
struct mutex lock;
struct auxiliary_device adev; struct auxiliary_device adev;
struct gpiod_lookup_table *lookup; struct gpiod_lookup_table *lookup;
}; };
@ -49,6 +51,7 @@ struct gpio_shared_entry {
unsigned int offset; unsigned int offset;
/* Index in the property value array. */ /* Index in the property value array. */
size_t index; size_t index;
/* Synchronizes the modification of shared_desc. */
struct mutex lock; struct mutex lock;
struct gpio_shared_desc *shared_desc; struct gpio_shared_desc *shared_desc;
struct kref ref; struct kref ref;
@ -56,7 +59,6 @@ struct gpio_shared_entry {
}; };
static LIST_HEAD(gpio_shared_list); static LIST_HEAD(gpio_shared_list);
static DEFINE_MUTEX(gpio_shared_lock);
static DEFINE_IDA(gpio_shared_ida); static DEFINE_IDA(gpio_shared_ida);
#if IS_ENABLED(CONFIG_OF) #if IS_ENABLED(CONFIG_OF)
@ -77,6 +79,10 @@ gpio_shared_find_entry(struct fwnode_handle *controller_node,
/* Handle all special nodes that we should ignore. */ /* Handle all special nodes that we should ignore. */
static bool gpio_shared_of_node_ignore(struct device_node *node) static bool gpio_shared_of_node_ignore(struct device_node *node)
{ {
/* Ignore disabled devices. */
if (!of_device_is_available(node))
return true;
/* /*
* __symbols__ is a special, internal node and should not be considered * __symbols__ is a special, internal node and should not be considered
* when scanning for shared GPIOs. * when scanning for shared GPIOs.
@ -183,6 +189,7 @@ static int gpio_shared_of_traverse(struct device_node *curr)
ref->fwnode = fwnode_handle_get(of_fwnode_handle(curr)); ref->fwnode = fwnode_handle_get(of_fwnode_handle(curr));
ref->flags = args.args[1]; ref->flags = args.args[1];
mutex_init(&ref->lock);
if (strends(prop->name, "gpios")) if (strends(prop->name, "gpios"))
suffix = "-gpios"; suffix = "-gpios";
@ -254,7 +261,7 @@ static int gpio_shared_make_adev(struct gpio_device *gdev,
struct auxiliary_device *adev = &ref->adev; struct auxiliary_device *adev = &ref->adev;
int ret; int ret;
lockdep_assert_held(&gpio_shared_lock); guard(mutex)(&ref->lock);
memset(adev, 0, sizeof(*adev)); memset(adev, 0, sizeof(*adev));
@ -369,14 +376,14 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags)
if (!lookup) if (!lookup)
return -ENOMEM; return -ENOMEM;
guard(mutex)(&gpio_shared_lock);
list_for_each_entry(entry, &gpio_shared_list, list) { list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) { list_for_each_entry(ref, &entry->refs, list) {
if (!device_match_fwnode(consumer, ref->fwnode) && if (!device_match_fwnode(consumer, ref->fwnode) &&
!gpio_shared_dev_is_reset_gpio(consumer, entry, ref)) !gpio_shared_dev_is_reset_gpio(consumer, entry, ref))
continue; continue;
guard(mutex)(&ref->lock);
/* We've already done that on a previous request. */ /* We've already done that on a previous request. */
if (ref->lookup) if (ref->lookup)
return 0; return 0;
@ -395,7 +402,8 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags)
lookup->table[0] = GPIO_LOOKUP(no_free_ptr(key), 0, lookup->table[0] = GPIO_LOOKUP(no_free_ptr(key), 0,
ref->con_id, lflags); ref->con_id, lflags);
gpiod_add_lookup_table(no_free_ptr(lookup)); ref->lookup = no_free_ptr(lookup);
gpiod_add_lookup_table(ref->lookup);
return 0; return 0;
} }
@ -408,10 +416,8 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags)
static void gpio_shared_remove_adev(struct auxiliary_device *adev) static void gpio_shared_remove_adev(struct auxiliary_device *adev)
{ {
lockdep_assert_held(&gpio_shared_lock);
auxiliary_device_uninit(adev);
auxiliary_device_delete(adev); auxiliary_device_delete(adev);
auxiliary_device_uninit(adev);
} }
int gpio_device_setup_shared(struct gpio_device *gdev) int gpio_device_setup_shared(struct gpio_device *gdev)
@ -421,8 +427,6 @@ int gpio_device_setup_shared(struct gpio_device *gdev)
unsigned long *flags; unsigned long *flags;
int ret; int ret;
guard(mutex)(&gpio_shared_lock);
list_for_each_entry(entry, &gpio_shared_list, list) { list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) { list_for_each_entry(ref, &entry->refs, list) {
if (gdev->dev.parent == &ref->adev.dev) { if (gdev->dev.parent == &ref->adev.dev) {
@ -479,19 +483,32 @@ void gpio_device_teardown_shared(struct gpio_device *gdev)
struct gpio_shared_entry *entry; struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref; struct gpio_shared_ref *ref;
guard(mutex)(&gpio_shared_lock);
list_for_each_entry(entry, &gpio_shared_list, list) { list_for_each_entry(entry, &gpio_shared_list, list) {
if (!device_match_fwnode(&gdev->dev, entry->fwnode)) if (!device_match_fwnode(&gdev->dev, entry->fwnode))
continue; 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) { list_for_each_entry(ref, &entry->refs, list) {
guard(mutex)(&ref->lock);
if (ref->lookup) {
gpiod_remove_lookup_table(ref->lookup); gpiod_remove_lookup_table(ref->lookup);
kfree(ref->lookup->table[0].key); kfree(ref->lookup->table[0].key);
kfree(ref->lookup); kfree(ref->lookup);
ref->lookup = NULL; ref->lookup = NULL;
}
gpio_shared_remove_adev(&ref->adev); gpio_shared_remove_adev(&ref->adev);
} }
lockdep_on();
} }
} }
@ -515,8 +532,6 @@ static void gpiod_shared_put(void *data)
{ {
struct gpio_shared_entry *entry = data; struct gpio_shared_entry *entry = data;
lockdep_assert_not_held(&gpio_shared_lock);
kref_put(&entry->ref, gpio_shared_release); kref_put(&entry->ref, gpio_shared_release);
} }
@ -554,8 +569,6 @@ struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev)
struct gpio_shared_entry *entry; struct gpio_shared_entry *entry;
int ret; int ret;
lockdep_assert_not_held(&gpio_shared_lock);
entry = dev_get_platdata(dev); entry = dev_get_platdata(dev);
if (WARN_ON(!entry)) if (WARN_ON(!entry))
/* Programmer bug */ /* Programmer bug */
@ -590,6 +603,7 @@ EXPORT_SYMBOL_GPL(devm_gpiod_shared_get);
static void gpio_shared_drop_ref(struct gpio_shared_ref *ref) static void gpio_shared_drop_ref(struct gpio_shared_ref *ref)
{ {
list_del(&ref->list); list_del(&ref->list);
mutex_destroy(&ref->lock);
kfree(ref->con_id); kfree(ref->con_id);
ida_free(&gpio_shared_ida, ref->dev_id); ida_free(&gpio_shared_ida, ref->dev_id);
fwnode_handle_put(ref->fwnode); fwnode_handle_put(ref->fwnode);