Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5eca8317 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents 852f42a6 545ebd9a
Loading
Loading
Loading
Loading
+1 −47
Original line number Diff line number Diff line
@@ -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;
	return -ENXIO;
}

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_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;
}

+40 −11
Original line number Diff line number Diff line
@@ -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;

	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, &chip->gpiodev->descs[offset].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);