diff options
Diffstat (limited to 'drivers/gpio/gpiolib.c')
| -rw-r--r-- | drivers/gpio/gpiolib.c | 34 | 
1 files changed, 29 insertions, 5 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 40f2d7f69be2..182136d98b97 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1158,8 +1158,19 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,  				  struct gpioline_info *info)  {  	struct gpio_chip *gc = desc->gdev->chip; +	bool ok_for_pinctrl;  	unsigned long flags; +	/* +	 * This function takes a mutex so we must check this before taking +	 * the spinlock. +	 * +	 * FIXME: find a non-racy way to retrieve this information. Maybe a +	 * lock common to both frameworks? +	 */ +	ok_for_pinctrl = +		pinctrl_gpio_can_use_line(gc->base + info->line_offset); +  	spin_lock_irqsave(&gpio_lock, flags);  	if (desc->name) { @@ -1186,7 +1197,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,  	    test_bit(FLAG_USED_AS_IRQ, &desc->flags) ||  	    test_bit(FLAG_EXPORT, &desc->flags) ||  	    test_bit(FLAG_SYSFS, &desc->flags) || -	    !pinctrl_gpio_can_use_line(gc->base + info->line_offset)) +	    !ok_for_pinctrl)  		info->flags |= GPIOLINE_FLAG_KERNEL;  	if (test_bit(FLAG_IS_OUT, &desc->flags))  		info->flags |= GPIOLINE_FLAG_IS_OUT; @@ -1227,6 +1238,7 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  	void __user *ip = (void __user *)arg;  	struct gpio_desc *desc;  	__u32 offset; +	int hwgpio;  	/* We fail any subsequent ioctl():s when the chip is gone */  	if (!gc) @@ -1259,13 +1271,19 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  		if (IS_ERR(desc))  			return PTR_ERR(desc); +		hwgpio = gpio_chip_hwgpio(desc); + +		if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL && +		    test_bit(hwgpio, priv->watched_lines)) +			return -EBUSY; +  		gpio_desc_to_lineinfo(desc, &lineinfo);  		if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))  			return -EFAULT;  		if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) -			set_bit(gpio_chip_hwgpio(desc), priv->watched_lines); +			set_bit(hwgpio, priv->watched_lines);  		return 0;  	} else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) { @@ -1280,7 +1298,12 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  		if (IS_ERR(desc))  			return PTR_ERR(desc); -		clear_bit(gpio_chip_hwgpio(desc), priv->watched_lines); +		hwgpio = gpio_chip_hwgpio(desc); + +		if (!test_bit(hwgpio, priv->watched_lines)) +			return -EBUSY; + +		clear_bit(hwgpio, priv->watched_lines);  		return 0;  	}  	return -EINVAL; @@ -5289,8 +5312,9 @@ static int __init gpiolib_dev_init(void)  	gpiolib_initialized = true;  	gpiochip_setup_devs(); -	if (IS_ENABLED(CONFIG_OF_DYNAMIC)) -		WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier)); +#if IS_ENABLED(CONFIG_OF_DYNAMIC) && IS_ENABLED(CONFIG_OF_GPIO) +	WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier)); +#endif /* CONFIG_OF_DYNAMIC && CONFIG_OF_GPIO */  	return ret;  }  | 
