summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-05-28 12:36:39 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-05-28 12:36:39 -0700
commitb0f908d785e19d53f0c41cb5d83639b038d2e489 (patch)
tree09e93b11d2387899a2cbc1e166d65cfa1dbadfc5 /drivers
parent43a1e3744548e6fd85873e6fb43e293eb4010694 (diff)
parent9500077678230e36d22bf16d2b9539c13e59a801 (diff)
downloadlinux-next-b0f908d785e19d53f0c41cb5d83639b038d2e489.tar.gz
linux-next-b0f908d785e19d53f0c41cb5d83639b038d2e489.zip
Merge tag 'gpio-fixes-for-v7.1-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio fixes from Bartosz Golaszewski: - fix interrupt handling in gpio-mxc - fix scoped_guard() usage in gpio-adnp - don't accept partial writes in gpio-virtuser debugfs interface as they can't really work correctly - fix resource leaks in gpio-rockchip - fix locking issues in remove path in shared GPIO management - undo the vote of a GPIO shared proxy virtual device on GPIO release * tag 'gpio-fixes-for-v7.1-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: gpio: rockchip: teardown bugs and resource leaks gpio: rockchip: convert bank->clk to devm_clk_get_enabled() gpio: virtuser: Fix uninitialized data bug in gpio_virtuser_direction_do_write() gpio: shared: fix lockdep false positive by removing unneeded lock gpio: shared: fix deadlock on shared proxy's parent removal gpio: adnp: fix flow control regression caused by scoped_guard() gpio: shared: undo the vote of the proxy on GPIO free gpio: mxc: fix irq_high handling
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/gpio-adnp.c4
-rw-r--r--drivers/gpio/gpio-mxc.c2
-rw-r--r--drivers/gpio/gpio-rockchip.c23
-rw-r--r--drivers/gpio/gpio-shared-proxy.c9
-rw-r--r--drivers/gpio/gpio-virtuser.c4
-rw-r--r--drivers/gpio/gpiolib-shared.c9
6 files changed, 35 insertions, 16 deletions
diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c
index e5ac2d211013..fe5bcaa90496 100644
--- a/drivers/gpio/gpio-adnp.c
+++ b/drivers/gpio/gpio-adnp.c
@@ -237,7 +237,9 @@ static irqreturn_t adnp_irq(int irq, void *data)
unsigned long pending;
int err;
- scoped_guard(mutex, &adnp->i2c_lock) {
+ {
+ guard(mutex)(&adnp->i2c_lock);
+
err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level);
if (err < 0)
continue;
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index 647b6f4861b7..12f11a6c9665 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -469,7 +469,7 @@ static int mxc_gpio_probe(struct platform_device *pdev)
* the handler is needed only once, but doing it for every port
* is more robust and easier.
*/
- port->irq_high = -1;
+ port->irq_high = 0;
port->mx_irq_handler = mx2_gpio_irq_handler;
} else
port->mx_irq_handler = mx3_gpio_irq_handler;
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index 44d7ebd12724..bc97d5d5d329 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -638,10 +638,17 @@ fail:
return ret;
}
+static void rockchip_clk_put(void *data)
+{
+ struct clk *clk = data;
+
+ clk_put(clk);
+}
+
static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
{
struct resource res;
- int id = 0;
+ int id = 0, ret;
if (of_address_to_resource(bank->of_node, 0, &res)) {
dev_err(bank->dev, "cannot find IO resource for bank\n");
@@ -656,11 +663,10 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
if (!bank->irq)
return -EINVAL;
- bank->clk = of_clk_get(bank->of_node, 0);
+ bank->clk = devm_clk_get_enabled(bank->dev, NULL);
if (IS_ERR(bank->clk))
return PTR_ERR(bank->clk);
- clk_prepare_enable(bank->clk);
id = readl(bank->reg_base + gpio_regs_v2.version_id);
switch (id) {
@@ -672,9 +678,13 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
bank->db_clk = of_clk_get(bank->of_node, 1);
if (IS_ERR(bank->db_clk)) {
dev_err(bank->dev, "cannot find debounce clk\n");
- clk_disable_unprepare(bank->clk);
return -EINVAL;
}
+
+ ret = devm_add_action_or_reset(bank->dev, rockchip_clk_put,
+ bank->db_clk);
+ if (ret)
+ return ret;
break;
case GPIO_TYPE_V1:
bank->gpio_regs = &gpio_regs_v1;
@@ -751,7 +761,6 @@ static int rockchip_gpio_probe(struct platform_device *pdev)
ret = rockchip_gpiolib_register(bank);
if (ret) {
- clk_disable_unprepare(bank->clk);
mutex_unlock(&bank->deferred_lock);
return ret;
}
@@ -792,7 +801,9 @@ static void rockchip_gpio_remove(struct platform_device *pdev)
{
struct rockchip_pin_bank *bank = platform_get_drvdata(pdev);
- clk_disable_unprepare(bank->clk);
+ irq_set_chained_handler_and_data(bank->irq, NULL, NULL);
+ if (bank->domain)
+ irq_domain_remove(bank->domain);
gpiochip_remove(&bank->gpio_chip);
}
diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c
index 29d7d2e4dfc0..6941e4be6cf1 100644
--- a/drivers/gpio/gpio-shared-proxy.c
+++ b/drivers/gpio/gpio-shared-proxy.c
@@ -103,9 +103,18 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+ int ret;
guard(gpio_shared_desc_lock)(shared_desc);
+ if (proxy->voted_high) {
+ ret = gpio_shared_proxy_set_unlocked(proxy,
+ shared_desc->can_sleep ? gpiod_set_value_cansleep : gpiod_set_value, 0);
+ if (ret)
+ dev_err(proxy->dev,
+ "Failed to unset the shared GPIO value on release: %d\n", ret);
+ }
+
proxy->shared_desc->usecnt--;
dev_dbg(proxy->dev, "Shared GPIO freed, number of users: %u\n",
diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c
index 128520d340d4..846f8688fec5 100644
--- a/drivers/gpio/gpio-virtuser.c
+++ b/drivers/gpio/gpio-virtuser.c
@@ -397,7 +397,7 @@ static ssize_t gpio_virtuser_direction_do_write(struct file *file,
char buf[32], *trimmed;
int ret, dir, val = 0;
- if (count >= sizeof(buf))
+ if (*ppos != 0 || count >= sizeof(buf))
return -EINVAL;
ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
@@ -622,7 +622,7 @@ static ssize_t gpio_virtuser_consumer_write(struct file *file,
char buf[GPIO_VIRTUSER_NAME_BUF_LEN + 2];
int ret;
- if (count >= sizeof(buf))
+ if (*ppos != 0 || count >= sizeof(buf))
return -EINVAL;
ret = simple_write_to_buffer(buf, GPIO_VIRTUSER_NAME_BUF_LEN, ppos,
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index e02d6b93a4ab..de72776fb154 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -53,7 +53,7 @@ struct gpio_shared_entry {
unsigned int offset;
/* Index in the property value array. */
size_t index;
- /* Synchronizes the modification of shared_desc. */
+ /* Synchronizes the modification of shared_desc and offset. */
struct mutex lock;
struct gpio_shared_desc *shared_desc;
struct kref ref;
@@ -598,16 +598,13 @@ void gpio_device_teardown_shared(struct gpio_device *gdev)
struct gpio_shared_ref *ref;
list_for_each_entry(entry, &gpio_shared_list, list) {
- guard(mutex)(&entry->lock);
-
if (!device_match_fwnode(&gdev->dev, entry->fwnode))
continue;
- gpiod_free_commit(&gdev->descs[entry->offset]);
+ scoped_guard(mutex, &entry->lock)
+ gpiod_free_commit(&gdev->descs[entry->offset]);
list_for_each_entry(ref, &entry->refs, list) {
- guard(mutex)(&ref->lock);
-
if (ref->lookup) {
gpiod_remove_lookup_table(ref->lookup);
kfree(ref->lookup->table[0].key);