mirror of https://gitee.com/openkylin/linux.git
Some GPIO fixes for the v4.9 series:
- Fix a nasty file descriptor leak when getting line handles. - A fix for a cleanup that seemed innocent but created a problem for drivers instantiating several gpiochips for one single OF node. - Fix a unpredictable problem using irq_domain_simple() in the mvebu driver by converting it to a lineas irqdomain. -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJYHIVAAAoJEEEQszewGV1zsPoP/1ZhUaz+w+KvKkAj0/6mPqiY tdzuU+LvC9/jnD80EVfkRVITMGTxKFyK8mtKTdkVd5Y4ZZVpCi6dCxuVSYL7ZmRy dQnjE+H+o3GuhhSsc1sjYPqG3QqWAF6f2bCqraI3HtbLonD1l7DUphfYSrgpDQoX yVRG4bwti2vpYMuV4wjA/hKUonsyeVkuO/5QQVdG/xXurOcL9z0ByVC9g2vfRQKu hw5CWx3XthhE/IWxKg9hjDcj4bYYaHlXfPKaBEzXm3wzF6MatJelVC/gIUZUS3wT mQY7RdQ4flK+rKjMQkQpG6a+b2hufER687EA7LjQ90CFwWQGwpT7JS9ig8sEnvKd DUtpk5oQJ99nZbVlMJ32AmFRSnSwUf3snbO8iUZvAa/tELdbBjDeXanzM4WMR4tZ LExyOXQLksUZttkzUM1SF0G1I/QT83vjdyLec7ssOvxuC6FdmjAtp8x6r2deqijx wkBMmuLayUFJu+lGCt0ssfOZ+14XquYax+1uVi3Uxb4MMrqEAz8YzBiM+Fyfr9tS sIAT0g96htF3wdiDbi2WA2LttpAghYpjNj9Mkz7BEcvFQexg4+KneYXA33opLOu7 VVvTU5uJ5vegcAYxnEGjzaK4fgpNGgexyXzQZO3YMtMtnmXgfWmhKBV/7/fsB/E2 odeokW90GQumdU5lEGYS =QmJl -----END PGP SIGNATURE----- Merge tag 'gpio-v4.9-3' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio Pull GPIO fixes from Linus Walleij: "Some GPIO fixes for the v4.9 series: - Fix a nasty file descriptor leak when getting line handles. - A fix for a cleanup that seemed innocent but created a problem for drivers instantiating several gpiochips for one single OF node. - Fix a unpredictable problem using irq_domain_simple() in the mvebu driver by converting it to a lineas irqdomain" * tag 'gpio-v4.9-3' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: gpio/mvebu: Use irq_domain_add_linear gpio: of: fix GPIO drivers with multiple gpio_chip for a single node gpio: GPIO_GET_LINE{HANDLE,EVENT}_IOCTL: Fix file descriptor leak
This commit is contained in:
commit
594aef64b6
|
@ -293,10 +293,10 @@ static void mvebu_gpio_irq_ack(struct irq_data *d)
|
|||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
struct mvebu_gpio_chip *mvchip = gc->private;
|
||||
u32 mask = ~(1 << (d->irq - gc->irq_base));
|
||||
u32 mask = d->mask;
|
||||
|
||||
irq_gc_lock(gc);
|
||||
writel_relaxed(mask, mvebu_gpioreg_edge_cause(mvchip));
|
||||
writel_relaxed(~mask, mvebu_gpioreg_edge_cause(mvchip));
|
||||
irq_gc_unlock(gc);
|
||||
}
|
||||
|
||||
|
@ -305,7 +305,7 @@ static void mvebu_gpio_edge_irq_mask(struct irq_data *d)
|
|||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
struct mvebu_gpio_chip *mvchip = gc->private;
|
||||
struct irq_chip_type *ct = irq_data_get_chip_type(d);
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
u32 mask = d->mask;
|
||||
|
||||
irq_gc_lock(gc);
|
||||
ct->mask_cache_priv &= ~mask;
|
||||
|
@ -319,8 +319,7 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d)
|
|||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
struct mvebu_gpio_chip *mvchip = gc->private;
|
||||
struct irq_chip_type *ct = irq_data_get_chip_type(d);
|
||||
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
u32 mask = d->mask;
|
||||
|
||||
irq_gc_lock(gc);
|
||||
ct->mask_cache_priv |= mask;
|
||||
|
@ -333,8 +332,7 @@ static void mvebu_gpio_level_irq_mask(struct irq_data *d)
|
|||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
struct mvebu_gpio_chip *mvchip = gc->private;
|
||||
struct irq_chip_type *ct = irq_data_get_chip_type(d);
|
||||
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
u32 mask = d->mask;
|
||||
|
||||
irq_gc_lock(gc);
|
||||
ct->mask_cache_priv &= ~mask;
|
||||
|
@ -347,8 +345,7 @@ static void mvebu_gpio_level_irq_unmask(struct irq_data *d)
|
|||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
struct mvebu_gpio_chip *mvchip = gc->private;
|
||||
struct irq_chip_type *ct = irq_data_get_chip_type(d);
|
||||
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
u32 mask = d->mask;
|
||||
|
||||
irq_gc_lock(gc);
|
||||
ct->mask_cache_priv |= mask;
|
||||
|
@ -462,7 +459,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
|
|||
for (i = 0; i < mvchip->chip.ngpio; i++) {
|
||||
int irq;
|
||||
|
||||
irq = mvchip->irqbase + i;
|
||||
irq = irq_find_mapping(mvchip->domain, i);
|
||||
|
||||
if (!(cause & (1 << i)))
|
||||
continue;
|
||||
|
@ -655,6 +652,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
|||
struct irq_chip_type *ct;
|
||||
struct clk *clk;
|
||||
unsigned int ngpios;
|
||||
bool have_irqs;
|
||||
int soc_variant;
|
||||
int i, cpu, id;
|
||||
int err;
|
||||
|
@ -665,6 +663,9 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
|||
else
|
||||
soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION;
|
||||
|
||||
/* Some gpio controllers do not provide irq support */
|
||||
have_irqs = of_irq_count(np) != 0;
|
||||
|
||||
mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip),
|
||||
GFP_KERNEL);
|
||||
if (!mvchip)
|
||||
|
@ -697,6 +698,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
|||
mvchip->chip.get = mvebu_gpio_get;
|
||||
mvchip->chip.direction_output = mvebu_gpio_direction_output;
|
||||
mvchip->chip.set = mvebu_gpio_set;
|
||||
if (have_irqs)
|
||||
mvchip->chip.to_irq = mvebu_gpio_to_irq;
|
||||
mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK;
|
||||
mvchip->chip.ngpio = ngpios;
|
||||
|
@ -758,34 +760,30 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
|||
devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip);
|
||||
|
||||
/* Some gpio controllers do not provide irq support */
|
||||
if (!of_irq_count(np))
|
||||
if (!have_irqs)
|
||||
return 0;
|
||||
|
||||
/* Setup the interrupt handlers. Each chip can have up to 4
|
||||
* interrupt handlers, with each handler dealing with 8 GPIO
|
||||
* pins. */
|
||||
for (i = 0; i < 4; i++) {
|
||||
int irq = platform_get_irq(pdev, i);
|
||||
|
||||
if (irq < 0)
|
||||
continue;
|
||||
irq_set_chained_handler_and_data(irq, mvebu_gpio_irq_handler,
|
||||
mvchip);
|
||||
mvchip->domain =
|
||||
irq_domain_add_linear(np, ngpios, &irq_generic_chip_ops, NULL);
|
||||
if (!mvchip->domain) {
|
||||
dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",
|
||||
mvchip->chip.label);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1);
|
||||
if (mvchip->irqbase < 0) {
|
||||
dev_err(&pdev->dev, "no irqs\n");
|
||||
return mvchip->irqbase;
|
||||
}
|
||||
|
||||
gc = irq_alloc_generic_chip("mvebu_gpio_irq", 2, mvchip->irqbase,
|
||||
mvchip->membase, handle_level_irq);
|
||||
if (!gc) {
|
||||
dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n");
|
||||
return -ENOMEM;
|
||||
err = irq_alloc_domain_generic_chips(
|
||||
mvchip->domain, ngpios, 2, np->name, handle_level_irq,
|
||||
IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_LEVEL, 0, 0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "couldn't allocate irq chips %s (DT).\n",
|
||||
mvchip->chip.label);
|
||||
goto err_domain;
|
||||
}
|
||||
|
||||
/* NOTE: The common accessors cannot be used because of the percpu
|
||||
* access to the mask registers
|
||||
*/
|
||||
gc = irq_get_domain_generic_chip(mvchip->domain, 0);
|
||||
gc->private = mvchip;
|
||||
ct = &gc->chip_types[0];
|
||||
ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
|
||||
|
@ -803,27 +801,23 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
|||
ct->handler = handle_edge_irq;
|
||||
ct->chip.name = mvchip->chip.label;
|
||||
|
||||
irq_setup_generic_chip(gc, IRQ_MSK(ngpios), 0,
|
||||
IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE);
|
||||
/* Setup the interrupt handlers. Each chip can have up to 4
|
||||
* interrupt handlers, with each handler dealing with 8 GPIO
|
||||
* pins.
|
||||
*/
|
||||
for (i = 0; i < 4; i++) {
|
||||
int irq = platform_get_irq(pdev, i);
|
||||
|
||||
/* Setup irq domain on top of the generic chip. */
|
||||
mvchip->domain = irq_domain_add_simple(np, mvchip->chip.ngpio,
|
||||
mvchip->irqbase,
|
||||
&irq_domain_simple_ops,
|
||||
if (irq < 0)
|
||||
continue;
|
||||
irq_set_chained_handler_and_data(irq, mvebu_gpio_irq_handler,
|
||||
mvchip);
|
||||
if (!mvchip->domain) {
|
||||
dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",
|
||||
mvchip->chip.label);
|
||||
err = -ENODEV;
|
||||
goto err_generic_chip;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_generic_chip:
|
||||
irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
|
||||
IRQ_LEVEL | IRQ_NOPROBE);
|
||||
kfree(gc);
|
||||
err_domain:
|
||||
irq_domain_remove(mvchip->domain);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -26,14 +26,18 @@
|
|||
|
||||
#include "gpiolib.h"
|
||||
|
||||
static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
|
||||
static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data)
|
||||
{
|
||||
return chip->gpiodev->dev.of_node == data;
|
||||
struct of_phandle_args *gpiospec = data;
|
||||
|
||||
return chip->gpiodev->dev.of_node == gpiospec->np &&
|
||||
chip->of_xlate(chip, gpiospec, NULL) >= 0;
|
||||
}
|
||||
|
||||
static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np)
|
||||
static struct gpio_chip *of_find_gpiochip_by_xlate(
|
||||
struct of_phandle_args *gpiospec)
|
||||
{
|
||||
return gpiochip_find(np, of_gpiochip_match_node);
|
||||
return gpiochip_find(gpiospec, of_gpiochip_match_node_and_xlate);
|
||||
}
|
||||
|
||||
static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
|
||||
|
@ -79,7 +83,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
chip = of_find_gpiochip_by_node(gpiospec.np);
|
||||
chip = of_find_gpiochip_by_xlate(&gpiospec);
|
||||
if (!chip) {
|
||||
desc = ERR_PTR(-EPROBE_DEFER);
|
||||
goto out;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/timekeeping.h>
|
||||
|
@ -423,6 +424,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
|
|||
{
|
||||
struct gpiohandle_request handlereq;
|
||||
struct linehandle_state *lh;
|
||||
struct file *file;
|
||||
int fd, i, ret;
|
||||
|
||||
if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
|
||||
|
@ -499,26 +501,41 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
|
|||
i--;
|
||||
lh->numdescs = handlereq.lines;
|
||||
|
||||
fd = anon_inode_getfd("gpio-linehandle",
|
||||
&linehandle_fileops,
|
||||
lh,
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
ret = fd;
|
||||
goto out_free_descs;
|
||||
}
|
||||
|
||||
file = anon_inode_getfile("gpio-linehandle",
|
||||
&linehandle_fileops,
|
||||
lh,
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto out_put_unused_fd;
|
||||
}
|
||||
|
||||
handlereq.fd = fd;
|
||||
if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
|
||||
ret = -EFAULT;
|
||||
goto out_free_descs;
|
||||
/*
|
||||
* fput() will trigger the release() callback, so do not go onto
|
||||
* the regular error cleanup path here.
|
||||
*/
|
||||
fput(file);
|
||||
put_unused_fd(fd);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
fd_install(fd, file);
|
||||
|
||||
dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
|
||||
lh->numdescs);
|
||||
|
||||
return 0;
|
||||
|
||||
out_put_unused_fd:
|
||||
put_unused_fd(fd);
|
||||
out_free_descs:
|
||||
for (; i >= 0; i--)
|
||||
gpiod_free(lh->descs[i]);
|
||||
|
@ -721,6 +738,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
|
|||
struct gpioevent_request eventreq;
|
||||
struct lineevent_state *le;
|
||||
struct gpio_desc *desc;
|
||||
struct file *file;
|
||||
u32 offset;
|
||||
u32 lflags;
|
||||
u32 eflags;
|
||||
|
@ -815,23 +833,38 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
|
|||
if (ret)
|
||||
goto out_free_desc;
|
||||
|
||||
fd = anon_inode_getfd("gpio-event",
|
||||
&lineevent_fileops,
|
||||
le,
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
ret = fd;
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
file = anon_inode_getfile("gpio-event",
|
||||
&lineevent_fileops,
|
||||
le,
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto out_put_unused_fd;
|
||||
}
|
||||
|
||||
eventreq.fd = fd;
|
||||
if (copy_to_user(ip, &eventreq, sizeof(eventreq))) {
|
||||
ret = -EFAULT;
|
||||
goto out_free_irq;
|
||||
/*
|
||||
* fput() will trigger the release() callback, so do not go onto
|
||||
* the regular error cleanup path here.
|
||||
*/
|
||||
fput(file);
|
||||
put_unused_fd(fd);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
fd_install(fd, file);
|
||||
|
||||
return 0;
|
||||
|
||||
out_put_unused_fd:
|
||||
put_unused_fd(fd);
|
||||
out_free_irq:
|
||||
free_irq(le->irq, le);
|
||||
out_free_desc:
|
||||
|
|
Loading…
Reference in New Issue