Commit 594aef64 authored by Linus Torvalds's avatar Linus Torvalds

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
parents fb415f22 812d4788
...@@ -293,10 +293,10 @@ static void mvebu_gpio_irq_ack(struct irq_data *d) ...@@ -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 irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mvebu_gpio_chip *mvchip = gc->private; struct mvebu_gpio_chip *mvchip = gc->private;
u32 mask = ~(1 << (d->irq - gc->irq_base)); u32 mask = d->mask;
irq_gc_lock(gc); irq_gc_lock(gc);
writel_relaxed(mask, mvebu_gpioreg_edge_cause(mvchip)); writel_relaxed(~mask, mvebu_gpioreg_edge_cause(mvchip));
irq_gc_unlock(gc); irq_gc_unlock(gc);
} }
...@@ -305,7 +305,7 @@ static void mvebu_gpio_edge_irq_mask(struct irq_data *d) ...@@ -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 irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mvebu_gpio_chip *mvchip = gc->private; struct mvebu_gpio_chip *mvchip = gc->private;
struct irq_chip_type *ct = irq_data_get_chip_type(d); 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); irq_gc_lock(gc);
ct->mask_cache_priv &= ~mask; ct->mask_cache_priv &= ~mask;
...@@ -319,8 +319,7 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) ...@@ -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 irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mvebu_gpio_chip *mvchip = gc->private; struct mvebu_gpio_chip *mvchip = gc->private;
struct irq_chip_type *ct = irq_data_get_chip_type(d); struct irq_chip_type *ct = irq_data_get_chip_type(d);
u32 mask = d->mask;
u32 mask = 1 << (d->irq - gc->irq_base);
irq_gc_lock(gc); irq_gc_lock(gc);
ct->mask_cache_priv |= mask; ct->mask_cache_priv |= mask;
...@@ -333,8 +332,7 @@ static void mvebu_gpio_level_irq_mask(struct irq_data *d) ...@@ -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 irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mvebu_gpio_chip *mvchip = gc->private; struct mvebu_gpio_chip *mvchip = gc->private;
struct irq_chip_type *ct = irq_data_get_chip_type(d); struct irq_chip_type *ct = irq_data_get_chip_type(d);
u32 mask = d->mask;
u32 mask = 1 << (d->irq - gc->irq_base);
irq_gc_lock(gc); irq_gc_lock(gc);
ct->mask_cache_priv &= ~mask; ct->mask_cache_priv &= ~mask;
...@@ -347,8 +345,7 @@ static void mvebu_gpio_level_irq_unmask(struct irq_data *d) ...@@ -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 irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mvebu_gpio_chip *mvchip = gc->private; struct mvebu_gpio_chip *mvchip = gc->private;
struct irq_chip_type *ct = irq_data_get_chip_type(d); struct irq_chip_type *ct = irq_data_get_chip_type(d);
u32 mask = d->mask;
u32 mask = 1 << (d->irq - gc->irq_base);
irq_gc_lock(gc); irq_gc_lock(gc);
ct->mask_cache_priv |= mask; ct->mask_cache_priv |= mask;
...@@ -462,7 +459,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) ...@@ -462,7 +459,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
for (i = 0; i < mvchip->chip.ngpio; i++) { for (i = 0; i < mvchip->chip.ngpio; i++) {
int irq; int irq;
irq = mvchip->irqbase + i; irq = irq_find_mapping(mvchip->domain, i);
if (!(cause & (1 << i))) if (!(cause & (1 << i)))
continue; continue;
...@@ -655,6 +652,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev) ...@@ -655,6 +652,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
struct irq_chip_type *ct; struct irq_chip_type *ct;
struct clk *clk; struct clk *clk;
unsigned int ngpios; unsigned int ngpios;
bool have_irqs;
int soc_variant; int soc_variant;
int i, cpu, id; int i, cpu, id;
int err; int err;
...@@ -665,6 +663,9 @@ static int mvebu_gpio_probe(struct platform_device *pdev) ...@@ -665,6 +663,9 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
else else
soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION; 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), mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip),
GFP_KERNEL); GFP_KERNEL);
if (!mvchip) if (!mvchip)
...@@ -697,7 +698,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev) ...@@ -697,7 +698,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
mvchip->chip.get = mvebu_gpio_get; mvchip->chip.get = mvebu_gpio_get;
mvchip->chip.direction_output = mvebu_gpio_direction_output; mvchip->chip.direction_output = mvebu_gpio_direction_output;
mvchip->chip.set = mvebu_gpio_set; mvchip->chip.set = mvebu_gpio_set;
mvchip->chip.to_irq = mvebu_gpio_to_irq; if (have_irqs)
mvchip->chip.to_irq = mvebu_gpio_to_irq;
mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK; mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK;
mvchip->chip.ngpio = ngpios; mvchip->chip.ngpio = ngpios;
mvchip->chip.can_sleep = false; mvchip->chip.can_sleep = false;
...@@ -758,34 +760,30 @@ static int mvebu_gpio_probe(struct platform_device *pdev) ...@@ -758,34 +760,30 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip); devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip);
/* Some gpio controllers do not provide irq support */ /* Some gpio controllers do not provide irq support */
if (!of_irq_count(np)) if (!have_irqs)
return 0; return 0;
/* Setup the interrupt handlers. Each chip can have up to 4 mvchip->domain =
* interrupt handlers, with each handler dealing with 8 GPIO irq_domain_add_linear(np, ngpios, &irq_generic_chip_ops, NULL);
* pins. */ if (!mvchip->domain) {
for (i = 0; i < 4; i++) { dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",
int irq = platform_get_irq(pdev, i); mvchip->chip.label);
return -ENODEV;
if (irq < 0)
continue;
irq_set_chained_handler_and_data(irq, mvebu_gpio_irq_handler,
mvchip);
}
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, err = irq_alloc_domain_generic_chips(
mvchip->membase, handle_level_irq); mvchip->domain, ngpios, 2, np->name, handle_level_irq,
if (!gc) { IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_LEVEL, 0, 0);
dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n"); if (err) {
return -ENOMEM; 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; gc->private = mvchip;
ct = &gc->chip_types[0]; ct = &gc->chip_types[0];
ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW; ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
...@@ -803,27 +801,23 @@ static int mvebu_gpio_probe(struct platform_device *pdev) ...@@ -803,27 +801,23 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
ct->handler = handle_edge_irq; ct->handler = handle_edge_irq;
ct->chip.name = mvchip->chip.label; ct->chip.name = mvchip->chip.label;
irq_setup_generic_chip(gc, IRQ_MSK(ngpios), 0, /* Setup the interrupt handlers. Each chip can have up to 4
IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE); * 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. */ if (irq < 0)
mvchip->domain = irq_domain_add_simple(np, mvchip->chip.ngpio, continue;
mvchip->irqbase, irq_set_chained_handler_and_data(irq, mvebu_gpio_irq_handler,
&irq_domain_simple_ops, mvchip);
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; return 0;
err_generic_chip: err_domain:
irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST, irq_domain_remove(mvchip->domain);
IRQ_LEVEL | IRQ_NOPROBE);
kfree(gc);
return err; return err;
} }
......
...@@ -26,14 +26,18 @@ ...@@ -26,14 +26,18 @@
#include "gpiolib.h" #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, 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, ...@@ -79,7 +83,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
return ERR_PTR(ret); return ERR_PTR(ret);
} }
chip = of_find_gpiochip_by_node(gpiospec.np); chip = of_find_gpiochip_by_xlate(&gpiospec);
if (!chip) { if (!chip) {
desc = ERR_PTR(-EPROBE_DEFER); desc = ERR_PTR(-EPROBE_DEFER);
goto out; goto out;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/anon_inodes.h> #include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/timekeeping.h> #include <linux/timekeeping.h>
...@@ -423,6 +424,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) ...@@ -423,6 +424,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
{ {
struct gpiohandle_request handlereq; struct gpiohandle_request handlereq;
struct linehandle_state *lh; struct linehandle_state *lh;
struct file *file;
int fd, i, ret; int fd, i, ret;
if (copy_from_user(&handlereq, ip, sizeof(handlereq))) if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
...@@ -499,26 +501,41 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) ...@@ -499,26 +501,41 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
i--; i--;
lh->numdescs = handlereq.lines; lh->numdescs = handlereq.lines;
fd = anon_inode_getfd("gpio-linehandle", fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
&linehandle_fileops,
lh,
O_RDONLY | O_CLOEXEC);
if (fd < 0) { if (fd < 0) {
ret = fd; ret = fd;
goto out_free_descs; 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; handlereq.fd = fd;
if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { 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", dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
lh->numdescs); lh->numdescs);
return 0; return 0;
out_put_unused_fd:
put_unused_fd(fd);
out_free_descs: out_free_descs:
for (; i >= 0; i--) for (; i >= 0; i--)
gpiod_free(lh->descs[i]); gpiod_free(lh->descs[i]);
...@@ -721,6 +738,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) ...@@ -721,6 +738,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
struct gpioevent_request eventreq; struct gpioevent_request eventreq;
struct lineevent_state *le; struct lineevent_state *le;
struct gpio_desc *desc; struct gpio_desc *desc;
struct file *file;
u32 offset; u32 offset;
u32 lflags; u32 lflags;
u32 eflags; u32 eflags;
...@@ -815,23 +833,38 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) ...@@ -815,23 +833,38 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
if (ret) if (ret)
goto out_free_desc; goto out_free_desc;
fd = anon_inode_getfd("gpio-event", fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
&lineevent_fileops,
le,
O_RDONLY | O_CLOEXEC);
if (fd < 0) { if (fd < 0) {
ret = fd; ret = fd;
goto out_free_irq; 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; eventreq.fd = fd;
if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { 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; return 0;
out_put_unused_fd:
put_unused_fd(fd);
out_free_irq: out_free_irq:
free_irq(le->irq, le); free_irq(le->irq, le);
out_free_desc: out_free_desc:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment