summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-24 20:02:48 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-24 20:02:48 -1000
commit9628ee0c2219daaa6374b0c5f5a5c0d55e0e263b (patch)
tree43c5168bdacd79bd2f2356d2902b82db4e5e877e
parent44388c8f3e22b578c9089df53301ef7e53e7fb01 (diff)
parentdae739137441667d67fbfcc4d303a0ecc6618662 (diff)
Merge tag 'gpio-fixes-for-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio fixes from Bartosz Golaszewski: - add ACPI quirks for Thinkpad L13 Gen 2a and Acer Nitro 16S AI to ignore a wrong GPIO interrupt declaration - add missing return value check of devm_gpiochip_add_data() to gpio-lpc32xx - don't set GPIOD_FLAG_IS_OUT from character device code as GPIO core already does it where needed and it can lead to providing invalid info to user-space on request errors - don't report a bogus -ENOMEM for a line requested without a label - propagate errors occurring when setting line values for pins for which we emulate open-drain or open-source drive settings - fix interrupt handling bugs in gpio-pca953x and gpio-davinci - fix more memory leaks in GPIO core - clear event clock flags from pins on GPIO release * tag 'gpio-fixes-for-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: gpiolib: don't report -ENOMEM for a requested line without a label gpiolib: Propagate emulated drive errors when setting arrays gpio: pca953x: Handle direction register read errors in IRQ paths gpio: pca953x: Preserve wakeup accounting on parent IRQ failure gpio: davinci: Clear interrupt status in every bank on suspend gpiolib: Free chip resources when allocating the label fails gpiolib: cdev: don't set GPIOD_FLAG_IS_OUT gpiolib: clear the event clock flags when a line is released gpiolib: fix memory leak in gpiod_set_array_value_complex() EPERM error path gpiolib: acpi: Ignore GPIO 11 interrupt on Acer Nitro 16S AI gpio: lpc32xx: check devm_gpiochip_add_data() return value gpiolib: acpi: Ignore GPIO 11 interrupt on ThinkPad L13 Gen 2a
-rw-r--r--drivers/gpio/gpio-davinci.c6
-rw-r--r--drivers/gpio/gpio-lpc32xx.c9
-rw-r--r--drivers/gpio/gpio-pca953x.c21
-rw-r--r--drivers/gpio/gpiolib-acpi-quirks.c31
-rw-r--r--drivers/gpio/gpiolib-cdev.c5
-rw-r--r--drivers/gpio/gpiolib.c32
6 files changed, 81 insertions, 23 deletions
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index cccbaea1dec2..fc2b9bef7efc 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -611,10 +611,10 @@ static void davinci_gpio_save_context(struct davinci_gpio_controller *chips,
context->set_data = readl_relaxed(&g->set_data);
context->set_rising = readl_relaxed(&g->set_rising);
context->set_falling = readl_relaxed(&g->set_falling);
- }
- /* Clear all interrupt status registers */
- writel_relaxed(GENMASK(31, 0), &g->intstat);
+ /* Clear stale interrupt status for this bank. */
+ writel_relaxed(GENMASK(31, 0), &g->intstat);
+ }
}
static void davinci_gpio_restore_context(struct davinci_gpio_controller *chips,
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
index 37fc54fc7385..7fb46262e18c 100644
--- a/drivers/gpio/gpio-lpc32xx.c
+++ b/drivers/gpio/gpio-lpc32xx.c
@@ -510,7 +510,7 @@ static int lpc32xx_of_xlate(struct gpio_chip *gc,
static int lpc32xx_gpio_probe(struct platform_device *pdev)
{
- int i;
+ int i, ret;
void __iomem *reg_base;
reg_base = devm_platform_ioremap_resource(pdev, 0);
@@ -524,8 +524,11 @@ static int lpc32xx_gpio_probe(struct platform_device *pdev)
lpc32xx_gpiochip[i].chip.of_gpio_n_cells = 3;
lpc32xx_gpiochip[i].reg_base = reg_base;
}
- devm_gpiochip_add_data(&pdev->dev, &lpc32xx_gpiochip[i].chip,
- &lpc32xx_gpiochip[i]);
+ ret = devm_gpiochip_add_data(&pdev->dev, &lpc32xx_gpiochip[i].chip,
+ &lpc32xx_gpiochip[i]);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to register gpio chip %d\n", i);
}
return 0;
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index aac9ea7520b6..afec5ec23696 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -838,13 +838,18 @@ static int pca953x_irq_set_wake(struct irq_data *d, unsigned int on)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pca953x_chip *chip = gpiochip_get_data(gc);
+ int ret;
+
+ ret = irq_set_irq_wake(chip->client->irq, on);
+ if (ret)
+ return ret;
if (on)
atomic_inc(&chip->wakeup_path);
else
atomic_dec(&chip->wakeup_path);
- return irq_set_irq_wake(chip->client->irq, on);
+ return 0;
}
static void pca953x_irq_bus_lock(struct irq_data *d)
@@ -862,6 +867,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
DECLARE_BITMAP(irq_mask, MAX_LINE);
DECLARE_BITMAP(reg_direction, MAX_LINE);
int level;
+ int ret;
guard(mutex)(&chip->i2c_lock);
@@ -880,7 +886,9 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
}
/* Switch direction to input if needed */
- pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ ret = pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ if (ret)
+ goto out;
bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio);
bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio);
@@ -890,6 +898,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
for_each_andnot_bit(level, irq_mask, reg_direction, gc->ngpio)
pca953x_gpio_direction_input_unlocked(&chip->gpio_chip, level);
+out:
mutex_unlock(&chip->irq_lock);
}
@@ -984,7 +993,9 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin
}
/* Remove output pins from the equation */
- pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ ret = pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ if (ret)
+ return false;
bitmap_copy(old_stat, chip->irq_stat, gc->ngpio);
@@ -1084,7 +1095,9 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base)
* interrupt. We have to rely on the previous read for
* this purpose.
*/
- pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ ret = pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ if (ret)
+ return ret;
bitmap_and(chip->irq_stat, irq_stat, reg_direction, gc->ngpio);
mutex_init(&chip->irq_lock);
diff --git a/drivers/gpio/gpiolib-acpi-quirks.c b/drivers/gpio/gpiolib-acpi-quirks.c
index 9a523871800d..3f89a87601c1 100644
--- a/drivers/gpio/gpiolib-acpi-quirks.c
+++ b/drivers/gpio/gpiolib-acpi-quirks.c
@@ -361,6 +361,20 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
},
{
/*
+ * Same camera power enable on GPIO 11 as the Acer Nitro V 16S AI
+ * above. It also powers the internal microphones.
+ * Found in BIOS V1.53
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro 16S AI"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .ignore_interrupt = "AMDI0030:00@11",
+ },
+ },
+ {
+ /*
* Wakeup only works when keyboard backlight is turned off
* https://gitlab.freedesktop.org/drm/amd/-/issues/4169
*/
@@ -374,6 +388,23 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
},
{
/*
+ * ACPI incorrectly declares GPIO 11 as an interrupt in _AEI,
+ * although the EC uses the pin to control the Caps Lock LED.
+ * Requesting the interrupt configures the pin as an input and
+ * prevents the LED from working.
+ * Found in BIOS R1QET54W (1.40).
+ * https://lore.kernel.org/linux-gpio/CAAUQFRPW-rbcZV-=qV1NMjF_358sdJTiGvQuRxRqAsehY+J9Qg@mail.gmail.com/
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21AB"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .ignore_interrupt = "AMDI0030:00@11",
+ },
+ },
+ {
+ /*
* Spurious wakeups from TP_ATTN# pin
* Found in BIOS 5.35
* https://gitlab.freedesktop.org/drm/amd/-/issues/4482
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index a30564d1525a..3f1f8c9acfc3 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1231,11 +1231,6 @@ static void gpio_v2_line_config_flags_to_desc_flags(u64 lflags,
assign_bit(GPIOD_FLAG_ACTIVE_LOW, &flags,
lflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW);
- if (lflags & GPIO_V2_LINE_FLAG_OUTPUT)
- set_bit(GPIOD_FLAG_IS_OUT, &flags);
- else if (lflags & GPIO_V2_LINE_FLAG_INPUT)
- clear_bit(GPIOD_FLAG_IS_OUT, &flags);
-
assign_bit(GPIOD_FLAG_EDGE_RISING, &flags,
lflags & GPIO_V2_LINE_FLAG_EDGE_RISING);
assign_bit(GPIOD_FLAG_EDGE_FALLING, &flags,
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c880686f5fd7..fbdeda2f2787 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2648,10 +2648,13 @@ int gpiod_request_commit(struct gpio_desc *desc, const char *label)
ret = desc_set_label(desc, label ? : "?");
if (ret)
- goto out_clear_bit;
+ goto out_free_gpio;
return 0;
+out_free_gpio:
+ if (guard.gc->free)
+ guard.gc->free(guard.gc, offset);
out_clear_bit:
clear_bit(GPIOD_FLAG_REQUESTED, &desc->flags);
return ret;
@@ -2700,6 +2703,8 @@ void gpiod_free_commit(struct gpio_desc *desc)
clear_bit(GPIOD_FLAG_BIAS_DISABLE, &flags);
clear_bit(GPIOD_FLAG_EDGE_RISING, &flags);
clear_bit(GPIOD_FLAG_EDGE_FALLING, &flags);
+ clear_bit(GPIOD_FLAG_EVENT_CLOCK_REALTIME, &flags);
+ clear_bit(GPIOD_FLAG_EVENT_CLOCK_HTE, &flags);
clear_bit(GPIOD_FLAG_IS_HOGGED, &flags);
#ifdef CONFIG_OF_DYNAMIC
WRITE_ONCE(desc->hog, NULL);
@@ -2737,6 +2742,7 @@ void gpiod_free(struct gpio_desc *desc)
char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_desc *desc;
+ const char *name;
char *label;
desc = gpiochip_get_desc(gc, offset);
@@ -2748,7 +2754,11 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset)
guard(srcu)(&desc->gdev->desc_srcu);
- label = kstrdup(gpiod_get_label(desc), GFP_KERNEL);
+ name = gpiod_get_label(desc);
+ if (!name)
+ return NULL;
+
+ label = kstrdup(name, GFP_KERNEL);
if (!label)
return ERR_PTR(-ENOMEM);
@@ -3863,7 +3873,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned long *value_bitmap)
{
struct gpio_chip *gc;
- int i = 0, ret;
+ int i = 0, ret = 0;
/*
* Validate array_info against desc_array and its size.
@@ -3941,8 +3951,10 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpiod_hwgpio(desc);
int value = test_bit(i, value_bitmap);
- if (unlikely(!test_bit(GPIOD_FLAG_IS_OUT, &desc->flags)))
- return -EPERM;
+ if (unlikely(!test_bit(GPIOD_FLAG_IS_OUT, &desc->flags))) {
+ ret = -EPERM;
+ goto cleanup_bitmap;
+ }
/*
* Pins applicable for fast input but not for
@@ -3959,9 +3971,13 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
* open drain and open source outputs are set individually
*/
if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags) && !raw) {
- gpio_set_open_drain_value_commit(desc, value);
+ ret = gpio_set_open_drain_value_commit(desc, value);
+ if (ret)
+ goto cleanup_bitmap;
} else if (test_bit(GPIOD_FLAG_OPEN_SOURCE, &desc->flags) && !raw) {
- gpio_set_open_source_value_commit(desc, value);
+ ret = gpio_set_open_source_value_commit(desc, value);
+ if (ret)
+ goto cleanup_bitmap;
} else {
__set_bit(hwgpio, mask);
__assign_bit(hwgpio, bits, value);
@@ -3975,10 +3991,10 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
} while ((i < array_size) &&
gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc));
/* push collected bits to outputs */
- ret = 0;
if (count != 0)
ret = gpiochip_set_multiple(guard.gc, mask, bits);
+cleanup_bitmap:
if (mask != fastpath_mask)
bitmap_free(mask);
if (bits != fastpath_bits)