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
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index c74da29..bd18548 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -737,7 +737,6 @@
 	depends on ARC_PLAT_TB10X || COMPILE_TEST
 	select GPIO_GENERIC
 	select GENERIC_IRQ_CHIP
-	select OF_GPIO
 
 config GPIO_TEGRA
 	tristate "NVIDIA Tegra GPIO support"
@@ -1568,6 +1567,7 @@
 	tristate "NXP QIXIS FPGA GPIO support"
 	depends on MFD_SIMPLE_MFD_I2C || COMPILE_TEST
 	select GPIO_REGMAP
+	select REGMAP_MMIO
 	help
 	  This enables support for the GPIOs found in the QIXIS FPGA which is
 	  integrated on some NXP Layerscape boards such as LX2160ARDB and
diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
index b3a26a0..5daf962 100644
--- a/drivers/gpio/gpio-mmio.c
+++ b/drivers/gpio/gpio-mmio.c
@@ -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);
 	unsigned long mask = gpio_mmio_line2mask(gc, gpio);
 
-	guard(raw_spinlock)(&chip->lock);
+	guard(raw_spinlock_irqsave)(&chip->lock);
 
 	if (val)
 		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);
 	unsigned long mask = gpio_mmio_line2mask(gc, gpio);
 
-	guard(raw_spinlock)(&chip->lock);
+	guard(raw_spinlock_irqsave)(&chip->lock);
 
 	if (val)
 		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);
 	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);
 
@@ -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);
 
-	scoped_guard(raw_spinlock, &chip->lock) {
+	scoped_guard(raw_spinlock_irqsave, &chip->lock) {
 		chip->sdir &= ~gpio_mmio_line2mask(gc, gpio);
 
 		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);
 
-	guard(raw_spinlock)(&chip->lock);
+	guard(raw_spinlock_irqsave)(&chip->lock);
 
 	chip->sdir |= gpio_mmio_line2mask(gc, gpio);
 
diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index e5ba38e..9581bd5 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -338,7 +338,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
 						 config->regmap_irq_line, config->regmap_irq_flags,
 						 0, config->regmap_irq_chip, &gpio->irq_chip_data);
 		if (ret)
-			goto err_free_bitmap;
+			goto err_remove_gpiochip;
 
 		irq_domain = regmap_irq_get_domain(gpio->irq_chip_data);
 	} else
diff --git a/drivers/gpio/gpiolib-acpi-quirks.c b/drivers/gpio/gpiolib-acpi-quirks.c
index 7b95d1b..a0116f0 100644
--- a/drivers/gpio/gpiolib-acpi-quirks.c
+++ b/drivers/gpio/gpiolib-acpi-quirks.c
@@ -370,6 +370,28 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
 			.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 */
 };
 
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index 8bdd107..ba4b718 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -36,6 +36,8 @@ struct gpio_shared_ref {
 	enum gpiod_flags flags;
 	char *con_id;
 	int dev_id;
+	/* Protects the auxiliary device struct and the lookup table. */
+	struct mutex lock;
 	struct auxiliary_device adev;
 	struct gpiod_lookup_table *lookup;
 };
@@ -49,6 +51,7 @@ struct gpio_shared_entry {
 	unsigned int offset;
 	/* Index in the property value array. */
 	size_t index;
+	/* Synchronizes the modification of shared_desc. */
 	struct mutex lock;
 	struct gpio_shared_desc *shared_desc;
 	struct kref ref;
@@ -56,7 +59,6 @@ struct gpio_shared_entry {
 };
 
 static LIST_HEAD(gpio_shared_list);
-static DEFINE_MUTEX(gpio_shared_lock);
 static DEFINE_IDA(gpio_shared_ida);
 
 #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. */
 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
 	 * 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->flags = args.args[1];
+			mutex_init(&ref->lock);
 
 			if (strends(prop->name, "gpios"))
 				suffix = "-gpios";
@@ -254,7 +261,7 @@ static int gpio_shared_make_adev(struct gpio_device *gdev,
 	struct auxiliary_device *adev = &ref->adev;
 	int ret;
 
-	lockdep_assert_held(&gpio_shared_lock);
+	guard(mutex)(&ref->lock);
 
 	memset(adev, 0, sizeof(*adev));
 
@@ -369,14 +376,14 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags)
 	if (!lookup)
 		return -ENOMEM;
 
-	guard(mutex)(&gpio_shared_lock);
-
 	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))
 				continue;
 
+			guard(mutex)(&ref->lock);
+
 			/* We've already done that on a previous request. */
 			if (ref->lookup)
 				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,
 						       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;
 		}
@@ -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)
 {
-	lockdep_assert_held(&gpio_shared_lock);
-
-	auxiliary_device_uninit(adev);
 	auxiliary_device_delete(adev);
+	auxiliary_device_uninit(adev);
 }
 
 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;
 	int ret;
 
-	guard(mutex)(&gpio_shared_lock);
-
 	list_for_each_entry(entry, &gpio_shared_list, list) {
 		list_for_each_entry(ref, &entry->refs, list) {
 			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_ref *ref;
 
-	guard(mutex)(&gpio_shared_lock);
-
 	list_for_each_entry(entry, &gpio_shared_list, list) {
 		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) {
-			gpiod_remove_lookup_table(ref->lookup);
-			kfree(ref->lookup->table[0].key);
-			kfree(ref->lookup);
-			ref->lookup = NULL;
+			guard(mutex)(&ref->lock);
+
+			if (ref->lookup) {
+				gpiod_remove_lookup_table(ref->lookup);
+				kfree(ref->lookup->table[0].key);
+				kfree(ref->lookup);
+				ref->lookup = NULL;
+			}
+
 			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;
 
-	lockdep_assert_not_held(&gpio_shared_lock);
-
 	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;
 	int ret;
 
-	lockdep_assert_not_held(&gpio_shared_lock);
-
 	entry = dev_get_platdata(dev);
 	if (WARN_ON(!entry))
 		/* Programmer bug */
@@ -590,6 +603,7 @@ EXPORT_SYMBOL_GPL(devm_gpiod_shared_get);
 static void gpio_shared_drop_ref(struct gpio_shared_ref *ref)
 {
 	list_del(&ref->list);
+	mutex_destroy(&ref->lock);
 	kfree(ref->con_id);
 	ida_free(&gpio_shared_ida, ref->dev_id);
 	fwnode_handle_put(ref->fwnode);