A bunch of GPIO fixes for the v4.7 series:

- Drop the lock before reading out the GPIO direction
   setting in drivers supporting the .get_direction()
   callback: some of them may be slowpath.
 
 - Flush GPIO direction setting before locking a GPIO as an
   IRQ: some electronics or other poking around in the
   registers behind our back may have happened, so flush
   the direction status before trying to lock the line for
   use by IRQs.
 
 - Bail out silently when asked to perform operations on
   NULL GPIO descriptors. That is what all the get_*_optional()
   is about: we get optional GPIO handles, if they are not
   there, we get NULL.
 
 - Handle compatible ioctl() correctly: we need to convert
   the ioctl() pointer using compat_ptr() here like everyone
   else.
 
 - Disable the broken .to_irq() on the LPC32xx platform.
   The whole irqchip infrastructure was replaced in the
   last merge window, and a new implementation will be
   needed.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJXTTtPAAoJEEEQszewGV1zOEIP/AuYXFdcjFDxHbx+DWdObNSh
 XKvpbRliFDyjyGy/omXCUQg0tl0/2SD1wm+I8kMwfKRwmN1S8bt5wIrVEIOOHTwC
 lZgj16qzJwS0exmAfRQ5ooQEmF3W8ZcN6aUOMlUy/uwgk8t0wbQPUChT9lSURgMw
 qsGnL3ci0QnVbgCngAR6h964LAHYttyOa1scBD6naVQwgrO8g4ad/KiC38vu2z/d
 eFsfrX8A5q9w/TS6Zz+Ue7RBBiZb4XRJ5dNsfgS4+W940NDCMq1F73cmE90Hzzs/
 4Lx+56Y5cXJn8C90DM1kU/uVfdXU0XPA9R8CC4lT4sO6HSkWEW1ZcOrMpNXshJfZ
 DR5h4W03WSD+QT6lZIt0kIo3xFWE/5HFS5xLLPPm2dOnNgwUSru7v0x82RWyClLl
 NOVcxJ7rsgUXLc4J+zi+r+Lhtb+2cMuwt2PS8QISeSzYcSSOXmiqRrMsO0SKsr0h
 JnEaCvC08Utm1RalATelmtoM4eIYl6kuODX14xdOI8hsvrSKeUF5cekzot6ivHO3
 /w286hovt102j51F3wMxRuAOmL5S3GuoypQBiNNDOQhkyMLtuzcjfz//38RW5Ooo
 CtoNbL59BnpR4zYT0Xdm5zPTHohJdC1B6/mANCuWZJM40IQ5VVoeJXEshdJ2YQe/
 U5m25QNqer2Xm4GJYfGh
 =5IlZ
 -----END PGP SIGNATURE-----

Merge tag 'gpio-v4.7-2' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio

Pull GPIO fixes from Linus Walleij:
 "A bunch of GPIO fixes for the v4.7 series:

   - Drop the lock before reading out the GPIO direction setting in
     drivers supporting the .get_direction() callback: some of them may
     be slowpath.

   - Flush GPIO direction setting before locking a GPIO as an IRQ: some
     electronics or other poking around in the registers behind our back
     may have happened, so flush the direction status before trying to
     lock the line for use by IRQs.

   - Bail out silently when asked to perform operations on NULL GPIO
     descriptors.  That is what all the get_*_optional() is about: we
     get optional GPIO handles, if they are not there, we get NULL.

   - Handle compatible ioctl() correctly: we need to convert the ioctl()
     pointer using compat_ptr() here like everyone else.

   - Disable the broken .to_irq() on the LPC32xx platform.  The whole
     irqchip infrastructure was replaced in the last merge window, and a
     new implementation will be needed"

* tag 'gpio-v4.7-2' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio:
  gpio: drop lock before reading GPIO direction
  gpio: bail out silently on NULL descriptors
  gpio: handle compatible ioctl() pointers
  gpio: flush direction status in gpiochip_lock_as_irq()
  gpio: lpc32xx: disable broken to_irq support
This commit is contained in:
Linus Torvalds 2016-05-31 09:27:00 -07:00
commit 5eca831742
2 changed files with 44 additions and 61 deletions

View File

@ -29,7 +29,6 @@
#include <mach/hardware.h>
#include <mach/platform.h>
#include <mach/irqs.h>
#define LPC32XX_GPIO_P3_INP_STATE _GPREG(0x000)
#define LPC32XX_GPIO_P3_OUTP_SET _GPREG(0x004)
@ -371,61 +370,16 @@ static int lpc32xx_gpio_request(struct gpio_chip *chip, unsigned pin)
static int lpc32xx_gpio_to_irq_p01(struct gpio_chip *chip, unsigned offset)
{
return IRQ_LPC32XX_P0_P1_IRQ;
}
static const char lpc32xx_gpio_to_irq_gpio_p3_table[] = {
IRQ_LPC32XX_GPIO_00,
IRQ_LPC32XX_GPIO_01,
IRQ_LPC32XX_GPIO_02,
IRQ_LPC32XX_GPIO_03,
IRQ_LPC32XX_GPIO_04,
IRQ_LPC32XX_GPIO_05,
};
static int lpc32xx_gpio_to_irq_gpio_p3(struct gpio_chip *chip, unsigned offset)
{
if (offset < ARRAY_SIZE(lpc32xx_gpio_to_irq_gpio_p3_table))
return lpc32xx_gpio_to_irq_gpio_p3_table[offset];
return -ENXIO;
}
static const char lpc32xx_gpio_to_irq_gpi_p3_table[] = {
IRQ_LPC32XX_GPI_00,
IRQ_LPC32XX_GPI_01,
IRQ_LPC32XX_GPI_02,
IRQ_LPC32XX_GPI_03,
IRQ_LPC32XX_GPI_04,
IRQ_LPC32XX_GPI_05,
IRQ_LPC32XX_GPI_06,
IRQ_LPC32XX_GPI_07,
IRQ_LPC32XX_GPI_08,
IRQ_LPC32XX_GPI_09,
-ENXIO, /* 10 */
-ENXIO, /* 11 */
-ENXIO, /* 12 */
-ENXIO, /* 13 */
-ENXIO, /* 14 */
-ENXIO, /* 15 */
-ENXIO, /* 16 */
-ENXIO, /* 17 */
-ENXIO, /* 18 */
IRQ_LPC32XX_GPI_19,
-ENXIO, /* 20 */
-ENXIO, /* 21 */
-ENXIO, /* 22 */
-ENXIO, /* 23 */
-ENXIO, /* 24 */
-ENXIO, /* 25 */
-ENXIO, /* 26 */
-ENXIO, /* 27 */
IRQ_LPC32XX_GPI_28,
};
static int lpc32xx_gpio_to_irq_gpio_p3(struct gpio_chip *chip, unsigned offset)
{
return -ENXIO;
}
static int lpc32xx_gpio_to_irq_gpi_p3(struct gpio_chip *chip, unsigned offset)
{
if (offset < ARRAY_SIZE(lpc32xx_gpio_to_irq_gpi_p3_table))
return lpc32xx_gpio_to_irq_gpi_p3_table[offset];
return -ENXIO;
}

View File

@ -20,6 +20,7 @@
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <uapi/linux/gpio.h>
#include "gpiolib.h"
@ -316,7 +317,7 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct gpio_device *gdev = filp->private_data;
struct gpio_chip *chip = gdev->chip;
int __user *ip = (int __user *)arg;
void __user *ip = (void __user *)arg;
/* We fail any subsequent ioctl():s when the chip is gone */
if (!chip)
@ -388,6 +389,14 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return -EINVAL;
}
#ifdef CONFIG_COMPAT
static long gpio_ioctl_compat(struct file *filp, unsigned int cmd,
unsigned long arg)
{
return gpio_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#endif
/**
* gpio_chrdev_open() - open the chardev for ioctl operations
* @inode: inode for this chardev
@ -431,7 +440,9 @@ static const struct file_operations gpio_fileops = {
.owner = THIS_MODULE,
.llseek = noop_llseek,
.unlocked_ioctl = gpio_ioctl,
.compat_ioctl = gpio_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = gpio_ioctl_compat,
#endif
};
static void gpiodevice_release(struct device *dev)
@ -618,6 +629,8 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
goto err_free_label;
}
spin_unlock_irqrestore(&gpio_lock, flags);
for (i = 0; i < chip->ngpio; i++) {
struct gpio_desc *desc = &gdev->descs[i];
@ -649,8 +662,6 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
}
}
spin_unlock_irqrestore(&gpio_lock, flags);
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
@ -1356,10 +1367,13 @@ static int __gpiod_request(struct gpio_desc *desc, const char *label)
/*
* This descriptor validation needs to be inserted verbatim into each
* function taking a descriptor, so we need to use a preprocessor
* macro to avoid endless duplication.
* macro to avoid endless duplication. If the desc is NULL it is an
* optional GPIO and calls should just bail out.
*/
#define VALIDATE_DESC(desc) do { \
if (!desc || !desc->gdev) { \
if (!desc) \
return 0; \
if (!desc->gdev) { \
pr_warn("%s: invalid GPIO\n", __func__); \
return -EINVAL; \
} \
@ -1370,7 +1384,9 @@ static int __gpiod_request(struct gpio_desc *desc, const char *label)
} } while (0)
#define VALIDATE_DESC_VOID(desc) do { \
if (!desc || !desc->gdev) { \
if (!desc) \
return; \
if (!desc->gdev) { \
pr_warn("%s: invalid GPIO\n", __func__); \
return; \
} \
@ -2066,17 +2082,30 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq);
*/
int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
{
if (offset >= chip->ngpio)
return -EINVAL;
struct gpio_desc *desc;
if (test_bit(FLAG_IS_OUT, &chip->gpiodev->descs[offset].flags)) {
desc = gpiochip_get_desc(chip, offset);
if (IS_ERR(desc))
return PTR_ERR(desc);
/* Flush direction if something changed behind our back */
if (chip->get_direction) {
int dir = chip->get_direction(chip, offset);
if (dir)
clear_bit(FLAG_IS_OUT, &desc->flags);
else
set_bit(FLAG_IS_OUT, &desc->flags);
}
if (test_bit(FLAG_IS_OUT, &desc->flags)) {
chip_err(chip,
"%s: tried to flag a GPIO set as output for IRQ\n",
__func__);
return -EIO;
}
set_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
set_bit(FLAG_USED_AS_IRQ, &desc->flags);
return 0;
}
EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq);