Merge tag 'gpio-fixes-for-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

Pull gpio fixes from Bartosz Golaszewski:

 - gracefully handle unsupported fwnode-only, no-device GPIO lookup for
   shared pins

 - mark the GPIO controller as sleeping in gpio-nct6694

 - fix a GPIO device reference leak in kunit tests

 - fix memory leak in error path in gpiod_set_array_value_complex()

 - fix races in GPIO controller and its associated irqchip setup in
   gpio-mxc

 - use atomic bitops where races can occur in gpio-mxc

 - fix build failure in gpio tools

* tag 'gpio-fixes-for-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
  gpio: mxc: fix race between gpiochip registration and IRQ domain setup
  gpio: mxc: fix wakeup_pads bit operations
  gpio: mxc: fix both_edges bit operations
  gpio: mxc: fix race between chained IRQ handler install and probe completion
  gpiolib: fix memory leak in gpiod_set_array_value_complex() on error paths
  gpio: kunit: fix GPIO device reference leak with __free()
  gpio: shared: gracefully handle no-device lookup of shared GPIOs
  gpio: shared: fix use-after-free bug when adding shared proxies
  tools: gpio: fix building from the top level with O=
  gpio: nct6694: Mark the GPIO controller as sleeping
This commit is contained in:
Linus Torvalds committed 2026-10-16 08:14:37 -07:00
commit 586a28a179
6 files changed
+55 -26

No files matched your search

+22 -14
View File
@@ -7,6 +7,7 @@
// Authors: Daniel Mack, Juergen Beisert.
// Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -68,11 +69,11 @@ struct mxc_gpio_port {
struct irq_domain *domain;
struct gpio_generic_chip gen_gc;
struct device *dev;
u32 both_edges;
unsigned long both_edges;
struct mxc_gpio_reg_saved gpio_saved_reg;
bool power_off;
u32 wakeup_pads;
bool is_pad_wakeup;
unsigned long wakeup_pads;
u32 pad_type[32];
const struct mxc_gpio_hwdata *hwdata;
};
@@ -168,7 +169,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
int edge;
void __iomem *reg = port->base;
port->both_edges &= ~(1 << gpio_idx);
clear_bit(gpio_idx, &port->both_edges);
switch (type) {
case IRQ_TYPE_EDGE_RISING:
edge = GPIO_INT_RISE_EDGE;
@@ -188,7 +189,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
edge = GPIO_INT_HIGH_LEV;
pr_debug("mxc: set GPIO %d to high trigger\n", gpio_idx);
}
port->both_edges |= 1 << gpio_idx;
set_bit(gpio_idx, &port->both_edges);
}
break;
case IRQ_TYPE_LEVEL_LOW:
@@ -259,7 +260,7 @@ static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
while (irq_stat != 0) {
int irqoffset = fls(irq_stat) - 1;
if (port->both_edges & (1 << irqoffset))
if (test_bit(irqoffset, &port->both_edges))
mxc_flip_edge(port, irqoffset);
generic_handle_domain_irq(port->domain, irqoffset);
@@ -330,16 +331,18 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
ret = enable_irq_wake(port->irq_high);
else
ret = enable_irq_wake(port->irq);
port->wakeup_pads |= BIT(gpio_idx);
} else {
if (port->irq_high && (gpio_idx >= 16))
ret = disable_irq_wake(port->irq_high);
else
ret = disable_irq_wake(port->irq);
port->wakeup_pads &= ~BIT(gpio_idx);
}
if (ret)
return ret;
return ret;
assign_bit(gpio_idx, &port->wakeup_pads, enable);
return 0;
}
static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
@@ -474,8 +477,6 @@ static int mxc_gpio_probe(struct platform_device *pdev)
} else
port->mx_irq_handler = mx3_gpio_irq_handler;
mxc_update_irq_chained_handler(port, true);
config.dev = &pdev->dev;
config.sz = 4;
config.dat = port->base + GPIO_PSR;
@@ -499,10 +500,6 @@ static int mxc_gpio_probe(struct platform_device *pdev)
else /* silence boot time warning */
port->gen_gc.gc.base = -1;
err = devm_gpiochip_add_data(&pdev->dev, &port->gen_gc.gc, port);
if (err)
goto out_bgio;
irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0, 32, numa_node_id());
if (irq_base < 0) {
err = irq_base;
@@ -523,8 +520,19 @@ static int mxc_gpio_probe(struct platform_device *pdev)
if (err < 0)
goto out_irqdomain_remove;
/*
* Register the GPIO chip last: devm_gpiochip_add_data() makes the
* chip visible to consumers immediately, so the IRQ domain and
* generic irq chip must be ready before any to_irq() call.
*/
err = devm_gpiochip_add_data(&pdev->dev, &port->gen_gc.gc, port);
if (err)
goto out_irqdomain_remove;
list_add_tail(&port->node, &mxc_gpio_ports);
mxc_update_irq_chained_handler(port, true);
platform_set_drvdata(pdev, port);
pm_runtime_put_autosuspend(&pdev->dev);
+1 -1
View File
@@ -444,7 +444,7 @@ static int nct6694_gpio_probe(struct platform_device *pdev)
data->gpio.set_config = nct6694_set_config;
data->gpio.init_valid_mask = nct6694_init_valid_mask;
data->gpio.base = -1;
data->gpio.can_sleep = false;
data->gpio.can_sleep = true;
data->gpio.owner = THIS_MODULE;
data->gpio.ngpio = NCT6694_NR_GPIO;
+7 -2
View File
@@ -28,6 +28,8 @@
#define GPIO_TEST_PROVIDER_NGPIO 4
KUNIT_DEFINE_ACTION_WRAPPER(gpio_device_put_wrapper, gpio_device_put, struct gpio_device *);
/*
* The test provider tracks per-line direction and value so that lines can be
* driven as both inputs and outputs - this is needed to exercise input as well
@@ -770,6 +772,7 @@ static void gpio_hog_assert(struct kunit *test, unsigned int offset,
struct property_entry properties[2];
struct platform_device *pdev;
struct fwnode_handle *fwnode;
struct gpio_device *gdev;
struct gpio_desc *desc;
bool bound = true;
int ret;
@@ -789,10 +792,12 @@ static void gpio_hog_assert(struct kunit *test, unsigned int offset,
wait_for_device_probe();
/* The hog must have configured the line with the expected direction. */
struct gpio_device *gdev __free(gpio_device_put) =
gpio_device_find_by_label(GPIO_CONSUMER_NAME);
gdev = gpio_device_find_by_label(GPIO_CONSUMER_NAME);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gdev);
ret = kunit_add_action_or_reset(test, gpio_device_put_wrapper, gdev);
KUNIT_ASSERT_EQ(test, ret, 0);
desc = gpio_device_get_desc(gdev, offset);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc);
+14 -5
View File
@@ -449,10 +449,14 @@ static bool gpio_shared_dev_is_reset_gpio(struct device *consumer,
int gpio_shared_add_proxy_lookup(struct device *consumer, struct fwnode_handle *fwnode,
const char *con_id, unsigned long lflags)
{
const char *dev_id = dev_name(consumer);
struct gpiod_lookup_table *lookup;
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
const char *dev_id;
if (!consumer)
return -EOPNOTSUPP;
dev_id = dev_name(consumer);
list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) {
@@ -480,18 +484,22 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, struct fwnode_handle *
if (!key)
return -ENOMEM;
lookup = kzalloc_flex(*lookup, table, 2);
struct gpiod_lookup_table *lookup __free(kfree) =
kzalloc_flex(*lookup, table, 2);
if (!lookup)
return -ENOMEM;
pr_debug("Adding machine lookup entry for a shared GPIO for consumer %s, with key '%s' and con_id '%s'\n",
dev_id, key, ref->con_id ?: "none");
lookup->dev_id = dev_id;
lookup->dev_id = kstrdup(dev_id, GFP_KERNEL);
if (!lookup->dev_id)
return -ENOMEM;
lookup->table[0] = GPIO_LOOKUP(no_free_ptr(key), 0,
ref->con_id, lflags);
ref->lookup = lookup;
ref->lookup = no_free_ptr(lookup);
gpiod_add_lookup_table(ref->lookup);
return 0;
@@ -611,6 +619,7 @@ void gpio_device_teardown_shared(struct gpio_device *gdev)
if (ref->lookup) {
gpiod_remove_lookup_table(ref->lookup);
kfree(ref->lookup->table[0].key);
kfree(ref->lookup->dev_id);
kfree(ref->lookup);
ref->lookup = NULL;
}
+5 -4
View File
@@ -3954,16 +3954,17 @@ 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 */
if (count != 0) {
ret = 0;
if (count != 0)
ret = gpiochip_set_multiple(guard.gc, mask, bits);
if (ret)
return ret;
}
if (mask != fastpath_mask)
bitmap_free(mask);
if (bits != fastpath_bits)
bitmap_free(bits);
if (ret)
return ret;
}
return 0;
}
+6
View File
@@ -7,7 +7,13 @@ bindir ?= /usr/bin
# isn't set and when invoked from selftests build, where srctree
# is set to ".". building_out_of_srctree is undefined for in srctree
# builds
ifeq ($(srctree),)
update_srctree := 1
endif
ifndef building_out_of_srctree
update_srctree := 1
endif
ifeq ($(update_srctree),1)
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif