mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git
synced 2026-10-28 04:45:43 +01:00
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:
commit
586a28a179
6 files changed
+55
-26
No files matched your search
+22
-14
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user