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:
Коммит
5eca831742
|
@ -29,7 +29,6 @@
|
||||||
|
|
||||||
#include <mach/hardware.h>
|
#include <mach/hardware.h>
|
||||||
#include <mach/platform.h>
|
#include <mach/platform.h>
|
||||||
#include <mach/irqs.h>
|
|
||||||
|
|
||||||
#define LPC32XX_GPIO_P3_INP_STATE _GPREG(0x000)
|
#define LPC32XX_GPIO_P3_INP_STATE _GPREG(0x000)
|
||||||
#define LPC32XX_GPIO_P3_OUTP_SET _GPREG(0x004)
|
#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)
|
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;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char lpc32xx_gpio_to_irq_gpi_p3_table[] = {
|
static int lpc32xx_gpio_to_irq_gpio_p3(struct gpio_chip *chip, unsigned offset)
|
||||||
IRQ_LPC32XX_GPI_00,
|
{
|
||||||
IRQ_LPC32XX_GPI_01,
|
return -ENXIO;
|
||||||
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_gpi_p3(struct gpio_chip *chip, unsigned offset)
|
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;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/cdev.h>
|
#include <linux/cdev.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/compat.h>
|
||||||
#include <uapi/linux/gpio.h>
|
#include <uapi/linux/gpio.h>
|
||||||
|
|
||||||
#include "gpiolib.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_device *gdev = filp->private_data;
|
||||||
struct gpio_chip *chip = gdev->chip;
|
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 */
|
/* We fail any subsequent ioctl():s when the chip is gone */
|
||||||
if (!chip)
|
if (!chip)
|
||||||
|
@ -388,6 +389,14 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
return -EINVAL;
|
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
|
* gpio_chrdev_open() - open the chardev for ioctl operations
|
||||||
* @inode: inode for this chardev
|
* @inode: inode for this chardev
|
||||||
|
@ -431,7 +440,9 @@ static const struct file_operations gpio_fileops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.llseek = noop_llseek,
|
.llseek = noop_llseek,
|
||||||
.unlocked_ioctl = gpio_ioctl,
|
.unlocked_ioctl = gpio_ioctl,
|
||||||
.compat_ioctl = gpio_ioctl,
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl = gpio_ioctl_compat,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static void gpiodevice_release(struct device *dev)
|
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;
|
goto err_free_label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||||
|
|
||||||
for (i = 0; i < chip->ngpio; i++) {
|
for (i = 0; i < chip->ngpio; i++) {
|
||||||
struct gpio_desc *desc = &gdev->descs[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
|
#ifdef CONFIG_PINCTRL
|
||||||
INIT_LIST_HEAD(&gdev->pin_ranges);
|
INIT_LIST_HEAD(&gdev->pin_ranges);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1356,10 +1367,13 @@ done:
|
||||||
/*
|
/*
|
||||||
* This descriptor validation needs to be inserted verbatim into each
|
* This descriptor validation needs to be inserted verbatim into each
|
||||||
* function taking a descriptor, so we need to use a preprocessor
|
* 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 { \
|
#define VALIDATE_DESC(desc) do { \
|
||||||
if (!desc || !desc->gdev) { \
|
if (!desc) \
|
||||||
|
return 0; \
|
||||||
|
if (!desc->gdev) { \
|
||||||
pr_warn("%s: invalid GPIO\n", __func__); \
|
pr_warn("%s: invalid GPIO\n", __func__); \
|
||||||
return -EINVAL; \
|
return -EINVAL; \
|
||||||
} \
|
} \
|
||||||
|
@ -1370,7 +1384,9 @@ done:
|
||||||
} } while (0)
|
} } while (0)
|
||||||
|
|
||||||
#define VALIDATE_DESC_VOID(desc) do { \
|
#define VALIDATE_DESC_VOID(desc) do { \
|
||||||
if (!desc || !desc->gdev) { \
|
if (!desc) \
|
||||||
|
return; \
|
||||||
|
if (!desc->gdev) { \
|
||||||
pr_warn("%s: invalid GPIO\n", __func__); \
|
pr_warn("%s: invalid GPIO\n", __func__); \
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
|
@ -2066,17 +2082,30 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq);
|
||||||
*/
|
*/
|
||||||
int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
|
int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
|
||||||
{
|
{
|
||||||
if (offset >= chip->ngpio)
|
struct gpio_desc *desc;
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
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,
|
chip_err(chip,
|
||||||
"%s: tried to flag a GPIO set as output for IRQ\n",
|
"%s: tried to flag a GPIO set as output for IRQ\n",
|
||||||
__func__);
|
__func__);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
|
set_bit(FLAG_USED_AS_IRQ, &desc->flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq);
|
EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче