mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	gpio: protect the pointer to gpio_chip in gpio_device with SRCU
Ensure we cannot crash if the GPIO device gets unregistered (and the chip pointer set to NULL) during any of the API calls. To that end: wait for all users of gdev->chip to exit their read-only SRCU critical sections in gpiochip_remove(). For brevity: add a guard class which can be instantiated at the top of every function requiring read-only access to the chip pointer and use it in all API calls taking a GPIO descriptor as argument. In places where we only deal with the GPIO device - use regular guard() helpers and rcu_dereference() for chip access. Do the same in API calls taking a const pointer to gpio_desc. Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Acked-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
This commit is contained in:
		
							parent
							
								
									47d8b4c1d8
								
							
						
					
					
						commit
						d83cee3d2b
					
				
					 4 changed files with 271 additions and 129 deletions
				
			
		| 
						 | 
					@ -24,7 +24,6 @@
 | 
				
			||||||
#include <linux/pinctrl/consumer.h>
 | 
					#include <linux/pinctrl/consumer.h>
 | 
				
			||||||
#include <linux/poll.h>
 | 
					#include <linux/poll.h>
 | 
				
			||||||
#include <linux/rbtree.h>
 | 
					#include <linux/rbtree.h>
 | 
				
			||||||
#include <linux/rwsem.h>
 | 
					 | 
				
			||||||
#include <linux/seq_file.h>
 | 
					#include <linux/seq_file.h>
 | 
				
			||||||
#include <linux/spinlock.h>
 | 
					#include <linux/spinlock.h>
 | 
				
			||||||
#include <linux/timekeeping.h>
 | 
					#include <linux/timekeeping.h>
 | 
				
			||||||
| 
						 | 
					@ -205,9 +204,9 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd,
 | 
				
			||||||
	unsigned int i;
 | 
						unsigned int i;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(rwsem_read)(&lh->gdev->sem);
 | 
						guard(srcu)(&lh->gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!lh->gdev->chip)
 | 
						if (!rcu_dereference(lh->gdev->chip))
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (cmd) {
 | 
						switch (cmd) {
 | 
				
			||||||
| 
						 | 
					@ -1520,9 +1519,9 @@ static long linereq_ioctl(struct file *file, unsigned int cmd,
 | 
				
			||||||
	struct linereq *lr = file->private_data;
 | 
						struct linereq *lr = file->private_data;
 | 
				
			||||||
	void __user *ip = (void __user *)arg;
 | 
						void __user *ip = (void __user *)arg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(rwsem_read)(&lr->gdev->sem);
 | 
						guard(srcu)(&lr->gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!lr->gdev->chip)
 | 
						if (!rcu_dereference(lr->gdev->chip))
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (cmd) {
 | 
						switch (cmd) {
 | 
				
			||||||
| 
						 | 
					@ -1551,9 +1550,9 @@ static __poll_t linereq_poll(struct file *file,
 | 
				
			||||||
	struct linereq *lr = file->private_data;
 | 
						struct linereq *lr = file->private_data;
 | 
				
			||||||
	__poll_t events = 0;
 | 
						__poll_t events = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(rwsem_read)(&lr->gdev->sem);
 | 
						guard(srcu)(&lr->gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!lr->gdev->chip)
 | 
						if (!rcu_dereference(lr->gdev->chip))
 | 
				
			||||||
		return EPOLLHUP | EPOLLERR;
 | 
							return EPOLLHUP | EPOLLERR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	poll_wait(file, &lr->wait, wait);
 | 
						poll_wait(file, &lr->wait, wait);
 | 
				
			||||||
| 
						 | 
					@ -1573,9 +1572,9 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
 | 
				
			||||||
	ssize_t bytes_read = 0;
 | 
						ssize_t bytes_read = 0;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(rwsem_read)(&lr->gdev->sem);
 | 
						guard(srcu)(&lr->gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!lr->gdev->chip)
 | 
						if (!rcu_dereference(lr->gdev->chip))
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (count < sizeof(le))
 | 
						if (count < sizeof(le))
 | 
				
			||||||
| 
						 | 
					@ -1874,9 +1873,9 @@ static __poll_t lineevent_poll(struct file *file,
 | 
				
			||||||
	struct lineevent_state *le = file->private_data;
 | 
						struct lineevent_state *le = file->private_data;
 | 
				
			||||||
	__poll_t events = 0;
 | 
						__poll_t events = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(rwsem_read)(&le->gdev->sem);
 | 
						guard(srcu)(&le->gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!le->gdev->chip)
 | 
						if (!rcu_dereference(le->gdev->chip))
 | 
				
			||||||
		return EPOLLHUP | EPOLLERR;
 | 
							return EPOLLHUP | EPOLLERR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	poll_wait(file, &le->wait, wait);
 | 
						poll_wait(file, &le->wait, wait);
 | 
				
			||||||
| 
						 | 
					@ -1912,9 +1911,9 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
 | 
				
			||||||
	ssize_t ge_size;
 | 
						ssize_t ge_size;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(rwsem_read)(&le->gdev->sem);
 | 
						guard(srcu)(&le->gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!le->gdev->chip)
 | 
						if (!rcu_dereference(le->gdev->chip))
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
| 
						 | 
					@ -1995,9 +1994,9 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd,
 | 
				
			||||||
	void __user *ip = (void __user *)arg;
 | 
						void __user *ip = (void __user *)arg;
 | 
				
			||||||
	struct gpiohandle_data ghd;
 | 
						struct gpiohandle_data ghd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(rwsem_read)(&le->gdev->sem);
 | 
						guard(srcu)(&le->gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!le->gdev->chip)
 | 
						if (!rcu_dereference(le->gdev->chip))
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
| 
						 | 
					@ -2295,10 +2294,13 @@ static void gpio_v2_line_info_changed_to_v1(
 | 
				
			||||||
static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
 | 
					static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
 | 
				
			||||||
				  struct gpio_v2_line_info *info)
 | 
									  struct gpio_v2_line_info *info)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *gc = desc->gdev->chip;
 | 
					 | 
				
			||||||
	unsigned long dflags;
 | 
						unsigned long dflags;
 | 
				
			||||||
	const char *label;
 | 
						const char *label;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset(info, 0, sizeof(*info));
 | 
						memset(info, 0, sizeof(*info));
 | 
				
			||||||
	info->offset = gpio_chip_hwgpio(desc);
 | 
						info->offset = gpio_chip_hwgpio(desc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2331,8 +2333,8 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
 | 
				
			||||||
	    test_bit(FLAG_USED_AS_IRQ, &dflags) ||
 | 
						    test_bit(FLAG_USED_AS_IRQ, &dflags) ||
 | 
				
			||||||
	    test_bit(FLAG_EXPORT, &dflags) ||
 | 
						    test_bit(FLAG_EXPORT, &dflags) ||
 | 
				
			||||||
	    test_bit(FLAG_SYSFS, &dflags) ||
 | 
						    test_bit(FLAG_SYSFS, &dflags) ||
 | 
				
			||||||
	    !gpiochip_line_is_valid(gc, info->offset) ||
 | 
						    !gpiochip_line_is_valid(guard.gc, info->offset) ||
 | 
				
			||||||
	    !pinctrl_gpio_can_use_line(gc, info->offset))
 | 
						    !pinctrl_gpio_can_use_line(guard.gc, info->offset))
 | 
				
			||||||
		info->flags |= GPIO_V2_LINE_FLAG_USED;
 | 
							info->flags |= GPIO_V2_LINE_FLAG_USED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (test_bit(FLAG_IS_OUT, &dflags))
 | 
						if (test_bit(FLAG_IS_OUT, &dflags))
 | 
				
			||||||
| 
						 | 
					@ -2505,10 +2507,10 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 | 
				
			||||||
	struct gpio_device *gdev = cdev->gdev;
 | 
						struct gpio_device *gdev = cdev->gdev;
 | 
				
			||||||
	void __user *ip = (void __user *)arg;
 | 
						void __user *ip = (void __user *)arg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(rwsem_read)(&gdev->sem);
 | 
						guard(srcu)(&gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* We fail any subsequent ioctl():s when the chip is gone */
 | 
						/* We fail any subsequent ioctl():s when the chip is gone */
 | 
				
			||||||
	if (!gdev->chip)
 | 
						if (!rcu_dereference(gdev->chip))
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Fill in the struct and pass to userspace */
 | 
						/* Fill in the struct and pass to userspace */
 | 
				
			||||||
| 
						 | 
					@ -2591,9 +2593,9 @@ static __poll_t lineinfo_watch_poll(struct file *file,
 | 
				
			||||||
	struct gpio_chardev_data *cdev = file->private_data;
 | 
						struct gpio_chardev_data *cdev = file->private_data;
 | 
				
			||||||
	__poll_t events = 0;
 | 
						__poll_t events = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(rwsem_read)(&cdev->gdev->sem);
 | 
						guard(srcu)(&cdev->gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!cdev->gdev->chip)
 | 
						if (!rcu_dereference(cdev->gdev->chip))
 | 
				
			||||||
		return EPOLLHUP | EPOLLERR;
 | 
							return EPOLLHUP | EPOLLERR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	poll_wait(file, &cdev->wait, pollt);
 | 
						poll_wait(file, &cdev->wait, pollt);
 | 
				
			||||||
| 
						 | 
					@ -2614,9 +2616,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
	size_t event_size;
 | 
						size_t event_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(rwsem_read)(&cdev->gdev->sem);
 | 
						guard(srcu)(&cdev->gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!cdev->gdev->chip)
 | 
						if (!rcu_dereference(cdev->gdev->chip))
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef CONFIG_GPIO_CDEV_V1
 | 
					#ifndef CONFIG_GPIO_CDEV_V1
 | 
				
			||||||
| 
						 | 
					@ -2691,10 +2693,10 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
 | 
				
			||||||
	struct gpio_chardev_data *cdev;
 | 
						struct gpio_chardev_data *cdev;
 | 
				
			||||||
	int ret = -ENOMEM;
 | 
						int ret = -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(rwsem_read)(&gdev->sem);
 | 
						guard(srcu)(&gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Fail on open if the backing gpiochip is gone */
 | 
						/* Fail on open if the backing gpiochip is gone */
 | 
				
			||||||
	if (!gdev->chip)
 | 
						if (!rcu_dereference(gdev->chip))
 | 
				
			||||||
		return -ENODEV;
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
 | 
						cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
 | 
				
			||||||
| 
						 | 
					@ -2781,6 +2783,7 @@ static const struct file_operations gpio_fileops = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
 | 
					int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct gpio_chip *gc;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cdev_init(&gdev->chrdev, &gpio_fileops);
 | 
						cdev_init(&gdev->chrdev, &gpio_fileops);
 | 
				
			||||||
| 
						 | 
					@ -2791,8 +2794,13 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
 | 
						guard(srcu)(&gdev->srcu);
 | 
				
			||||||
		 MAJOR(devt), gdev->id);
 | 
					
 | 
				
			||||||
 | 
						gc = rcu_dereference(gdev->chip);
 | 
				
			||||||
 | 
						if (!gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -171,6 +171,10 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
 | 
				
			||||||
	unsigned long irq_flags;
 | 
						unsigned long irq_flags;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data->irq = gpiod_to_irq(desc);
 | 
						data->irq = gpiod_to_irq(desc);
 | 
				
			||||||
	if (data->irq < 0)
 | 
						if (data->irq < 0)
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
| 
						 | 
					@ -195,7 +199,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
 | 
				
			||||||
	 *        Remove this redundant call (along with the corresponding
 | 
						 *        Remove this redundant call (along with the corresponding
 | 
				
			||||||
	 *        unlock) when those drivers have been fixed.
 | 
						 *        unlock) when those drivers have been fixed.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	ret = gpiochip_lock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
 | 
						ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		goto err_put_kn;
 | 
							goto err_put_kn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -209,7 +213,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
err_unlock:
 | 
					err_unlock:
 | 
				
			||||||
	gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
 | 
						gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
 | 
				
			||||||
err_put_kn:
 | 
					err_put_kn:
 | 
				
			||||||
	sysfs_put(data->value_kn);
 | 
						sysfs_put(data->value_kn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -225,9 +229,13 @@ static void gpio_sysfs_free_irq(struct device *dev)
 | 
				
			||||||
	struct gpiod_data *data = dev_get_drvdata(dev);
 | 
						struct gpiod_data *data = dev_get_drvdata(dev);
 | 
				
			||||||
	struct gpio_desc *desc = data->desc;
 | 
						struct gpio_desc *desc = data->desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data->irq_flags = 0;
 | 
						data->irq_flags = 0;
 | 
				
			||||||
	free_irq(data->irq, data);
 | 
						free_irq(data->irq, data);
 | 
				
			||||||
	gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
 | 
						gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
 | 
				
			||||||
	sysfs_put(data->value_kn);
 | 
						sysfs_put(data->value_kn);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -444,13 +452,12 @@ static ssize_t export_store(const struct class *class,
 | 
				
			||||||
				const char *buf, size_t len)
 | 
									const char *buf, size_t len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_desc *desc;
 | 
						struct gpio_desc *desc;
 | 
				
			||||||
	struct gpio_chip *gc;
 | 
					 | 
				
			||||||
	int status, offset;
 | 
						int status, offset;
 | 
				
			||||||
	long gpio;
 | 
						long gpio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	status = kstrtol(buf, 0, &gpio);
 | 
						status = kstrtol(buf, 0, &gpio);
 | 
				
			||||||
	if (status < 0)
 | 
						if (status)
 | 
				
			||||||
		goto done;
 | 
							return status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	desc = gpio_to_desc(gpio);
 | 
						desc = gpio_to_desc(gpio);
 | 
				
			||||||
	/* reject invalid GPIOs */
 | 
						/* reject invalid GPIOs */
 | 
				
			||||||
| 
						 | 
					@ -458,9 +465,13 @@ static ssize_t export_store(const struct class *class,
 | 
				
			||||||
		pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
 | 
							pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	gc = desc->gdev->chip;
 | 
					
 | 
				
			||||||
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	offset = gpio_chip_hwgpio(desc);
 | 
						offset = gpio_chip_hwgpio(desc);
 | 
				
			||||||
	if (!gpiochip_line_is_valid(gc, offset)) {
 | 
						if (!gpiochip_line_is_valid(guard.gc, offset)) {
 | 
				
			||||||
		pr_warn("%s: GPIO %ld masked\n", __func__, gpio);
 | 
							pr_warn("%s: GPIO %ld masked\n", __func__, gpio);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -563,7 +574,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
 | 
				
			||||||
	const char *ioname = NULL;
 | 
						const char *ioname = NULL;
 | 
				
			||||||
	struct gpio_device *gdev;
 | 
						struct gpio_device *gdev;
 | 
				
			||||||
	struct gpiod_data *data;
 | 
						struct gpiod_data *data;
 | 
				
			||||||
	struct gpio_chip *chip;
 | 
					 | 
				
			||||||
	struct device *dev;
 | 
						struct device *dev;
 | 
				
			||||||
	int status, offset;
 | 
						int status, offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -578,16 +588,19 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!test_and_set_bit(FLAG_EXPORT, &desc->flags))
 | 
						if (!test_and_set_bit(FLAG_EXPORT, &desc->flags))
 | 
				
			||||||
		return -EPERM;
 | 
							return -EPERM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gdev = desc->gdev;
 | 
						gdev = desc->gdev;
 | 
				
			||||||
	chip = gdev->chip;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&sysfs_lock);
 | 
						mutex_lock(&sysfs_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* check if chip is being removed */
 | 
						/* check if chip is being removed */
 | 
				
			||||||
	if (!chip || !gdev->mockdev) {
 | 
						if (!gdev->mockdev) {
 | 
				
			||||||
		status = -ENODEV;
 | 
							status = -ENODEV;
 | 
				
			||||||
		goto err_unlock;
 | 
							goto err_unlock;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -606,14 +619,14 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data->desc = desc;
 | 
						data->desc = desc;
 | 
				
			||||||
	mutex_init(&data->mutex);
 | 
						mutex_init(&data->mutex);
 | 
				
			||||||
	if (chip->direction_input && chip->direction_output)
 | 
						if (guard.gc->direction_input && guard.gc->direction_output)
 | 
				
			||||||
		data->direction_can_change = direction_may_change;
 | 
							data->direction_can_change = direction_may_change;
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		data->direction_can_change = false;
 | 
							data->direction_can_change = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	offset = gpio_chip_hwgpio(desc);
 | 
						offset = gpio_chip_hwgpio(desc);
 | 
				
			||||||
	if (chip->names && chip->names[offset])
 | 
						if (guard.gc->names && guard.gc->names[offset])
 | 
				
			||||||
		ioname = chip->names[offset];
 | 
							ioname = guard.gc->names[offset];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev = device_create_with_groups(&gpio_class, &gdev->dev,
 | 
						dev = device_create_with_groups(&gpio_class, &gdev->dev,
 | 
				
			||||||
					MKDEV(0, 0), data, gpio_groups,
 | 
										MKDEV(0, 0), data, gpio_groups,
 | 
				
			||||||
| 
						 | 
					@ -728,7 +741,7 @@ EXPORT_SYMBOL_GPL(gpiod_unexport);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int gpiochip_sysfs_register(struct gpio_device *gdev)
 | 
					int gpiochip_sysfs_register(struct gpio_device *gdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *chip = gdev->chip;
 | 
						struct gpio_chip *chip;
 | 
				
			||||||
	struct device *parent;
 | 
						struct device *parent;
 | 
				
			||||||
	struct device *dev;
 | 
						struct device *dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -741,6 +754,12 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
 | 
				
			||||||
	if (!class_is_registered(&gpio_class))
 | 
						if (!class_is_registered(&gpio_class))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						guard(srcu)(&gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						chip = rcu_dereference(gdev->chip);
 | 
				
			||||||
 | 
						if (!chip)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * For sysfs backward compatibility we need to preserve this
 | 
						 * For sysfs backward compatibility we need to preserve this
 | 
				
			||||||
	 * preferred parenting to the gpio_chip parent field, if set.
 | 
						 * preferred parenting to the gpio_chip parent field, if set.
 | 
				
			||||||
| 
						 | 
					@ -767,7 +786,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
 | 
				
			||||||
void gpiochip_sysfs_unregister(struct gpio_device *gdev)
 | 
					void gpiochip_sysfs_unregister(struct gpio_device *gdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_desc *desc;
 | 
						struct gpio_desc *desc;
 | 
				
			||||||
	struct gpio_chip *chip = gdev->chip;
 | 
						struct gpio_chip *chip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	scoped_guard(mutex, &sysfs_lock) {
 | 
						scoped_guard(mutex, &sysfs_lock) {
 | 
				
			||||||
		if (!gdev->mockdev)
 | 
							if (!gdev->mockdev)
 | 
				
			||||||
| 
						 | 
					@ -779,6 +798,12 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
 | 
				
			||||||
		gdev->mockdev = NULL;
 | 
							gdev->mockdev = NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						guard(srcu)(&gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						chip = rcu_dereference(gdev->chip);
 | 
				
			||||||
 | 
						if (chip)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* unregister gpiod class devices owned by sysfs */
 | 
						/* unregister gpiod class devices owned by sysfs */
 | 
				
			||||||
	for_each_gpio_desc_with_flag(chip, desc, FLAG_SYSFS) {
 | 
						for_each_gpio_desc_with_flag(chip, desc, FLAG_SYSFS) {
 | 
				
			||||||
		gpiod_unexport(desc);
 | 
							gpiod_unexport(desc);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -216,7 +216,7 @@ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!desc)
 | 
						if (!desc)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	return desc->gdev->chip;
 | 
						return rcu_dereference(desc->gdev->chip);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(gpiod_to_chip);
 | 
					EXPORT_SYMBOL_GPL(gpiod_to_chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -285,7 +285,7 @@ EXPORT_SYMBOL(gpio_device_get_label);
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev)
 | 
					struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return gdev->chip;
 | 
						return rcu_dereference(gdev->chip);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(gpio_device_get_chip);
 | 
					EXPORT_SYMBOL_GPL(gpio_device_get_chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -325,12 +325,21 @@ static int gpiochip_find_base_unlocked(int ngpio)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int gpiod_get_direction(struct gpio_desc *desc)
 | 
					int gpiod_get_direction(struct gpio_desc *desc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *gc;
 | 
					 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
	unsigned int offset;
 | 
						unsigned int offset;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gc = gpiod_to_chip(desc);
 | 
						/*
 | 
				
			||||||
 | 
						 * We cannot use VALIDATE_DESC() as we must not return 0 for a NULL
 | 
				
			||||||
 | 
						 * descriptor like we usually do.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (!desc || IS_ERR(desc))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	offset = gpio_chip_hwgpio(desc);
 | 
						offset = gpio_chip_hwgpio(desc);
 | 
				
			||||||
	flags = READ_ONCE(desc->flags);
 | 
						flags = READ_ONCE(desc->flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -342,10 +351,10 @@ int gpiod_get_direction(struct gpio_desc *desc)
 | 
				
			||||||
	    test_bit(FLAG_IS_OUT, &flags))
 | 
						    test_bit(FLAG_IS_OUT, &flags))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!gc->get_direction)
 | 
						if (!guard.gc->get_direction)
 | 
				
			||||||
		return -ENOTSUPP;
 | 
							return -ENOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = gc->get_direction(gc, offset);
 | 
						ret = guard.gc->get_direction(guard.gc, offset);
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -421,6 +430,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_device *gdev;
 | 
						struct gpio_device *gdev;
 | 
				
			||||||
	struct gpio_desc *desc;
 | 
						struct gpio_desc *desc;
 | 
				
			||||||
 | 
						struct gpio_chip *gc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!name)
 | 
						if (!name)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
| 
						 | 
					@ -429,7 +439,13 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry_srcu(gdev, &gpio_devices, list,
 | 
						list_for_each_entry_srcu(gdev, &gpio_devices, list,
 | 
				
			||||||
				 srcu_read_lock_held(&gpio_devices_srcu)) {
 | 
									 srcu_read_lock_held(&gpio_devices_srcu)) {
 | 
				
			||||||
		for_each_gpio_desc(gdev->chip, desc) {
 | 
							guard(srcu)(&gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							gc = rcu_dereference(gdev->chip);
 | 
				
			||||||
 | 
							if (!gc)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for_each_gpio_desc(gc, desc) {
 | 
				
			||||||
			if (desc->name && !strcmp(desc->name, name))
 | 
								if (desc->name && !strcmp(desc->name, name))
 | 
				
			||||||
				return desc;
 | 
									return desc;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -853,7 +869,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
 | 
				
			||||||
	gdev->dev.type = &gpio_dev_type;
 | 
						gdev->dev.type = &gpio_dev_type;
 | 
				
			||||||
	gdev->dev.bus = &gpio_bus_type;
 | 
						gdev->dev.bus = &gpio_bus_type;
 | 
				
			||||||
	gdev->dev.parent = gc->parent;
 | 
						gdev->dev.parent = gc->parent;
 | 
				
			||||||
	gdev->chip = gc;
 | 
						rcu_assign_pointer(gdev->chip, gc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gc->gpiodev = gdev;
 | 
						gc->gpiodev = gdev;
 | 
				
			||||||
	gpiochip_set_data(gc, data);
 | 
						gpiochip_set_data(gc, data);
 | 
				
			||||||
| 
						 | 
					@ -1097,7 +1113,8 @@ void gpiochip_remove(struct gpio_chip *gc)
 | 
				
			||||||
	synchronize_srcu(&gpio_devices_srcu);
 | 
						synchronize_srcu(&gpio_devices_srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Numb the device, cancelling all outstanding operations */
 | 
						/* Numb the device, cancelling all outstanding operations */
 | 
				
			||||||
	gdev->chip = NULL;
 | 
						rcu_assign_pointer(gdev->chip, NULL);
 | 
				
			||||||
 | 
						synchronize_srcu(&gdev->srcu);
 | 
				
			||||||
	gpiochip_irqchip_remove(gc);
 | 
						gpiochip_irqchip_remove(gc);
 | 
				
			||||||
	acpi_gpiochip_remove(gc);
 | 
						acpi_gpiochip_remove(gc);
 | 
				
			||||||
	of_gpiochip_remove(gc);
 | 
						of_gpiochip_remove(gc);
 | 
				
			||||||
| 
						 | 
					@ -1156,6 +1173,7 @@ struct gpio_device *gpio_device_find(void *data,
 | 
				
			||||||
						  const void *data))
 | 
											  const void *data))
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_device *gdev;
 | 
						struct gpio_device *gdev;
 | 
				
			||||||
 | 
						struct gpio_chip *gc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Not yet but in the future the spinlock below will become a mutex.
 | 
						 * Not yet but in the future the spinlock below will become a mutex.
 | 
				
			||||||
| 
						 | 
					@ -1166,8 +1184,13 @@ struct gpio_device *gpio_device_find(void *data,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	guard(srcu)(&gpio_devices_srcu);
 | 
						guard(srcu)(&gpio_devices_srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(gdev, &gpio_devices, list) {
 | 
						list_for_each_entry_srcu(gdev, &gpio_devices, list,
 | 
				
			||||||
		if (gdev->chip && match(gdev->chip, data))
 | 
									 srcu_read_lock_held(&gpio_devices_srcu)) {
 | 
				
			||||||
 | 
							guard(srcu)(&gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							gc = rcu_dereference(gdev->chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (gc && match(gc, data))
 | 
				
			||||||
			return gpio_device_get(gdev);
 | 
								return gpio_device_get(gdev);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2214,10 +2237,13 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
 | 
					static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *gc = desc->gdev->chip;
 | 
					 | 
				
			||||||
	unsigned int offset;
 | 
						unsigned int offset;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (test_and_set_bit(FLAG_REQUESTED, &desc->flags))
 | 
						if (test_and_set_bit(FLAG_REQUESTED, &desc->flags))
 | 
				
			||||||
		return -EBUSY;
 | 
							return -EBUSY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2231,17 +2257,17 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
 | 
				
			||||||
	 * before IRQs are enabled, for non-sleeping (SOC) GPIOs.
 | 
						 * before IRQs are enabled, for non-sleeping (SOC) GPIOs.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (gc->request) {
 | 
						if (guard.gc->request) {
 | 
				
			||||||
		offset = gpio_chip_hwgpio(desc);
 | 
							offset = gpio_chip_hwgpio(desc);
 | 
				
			||||||
		if (gpiochip_line_is_valid(gc, offset))
 | 
							if (gpiochip_line_is_valid(guard.gc, offset))
 | 
				
			||||||
			ret = gc->request(gc, offset);
 | 
								ret = guard.gc->request(guard.gc, offset);
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			ret = -EINVAL;
 | 
								ret = -EINVAL;
 | 
				
			||||||
		if (ret)
 | 
							if (ret)
 | 
				
			||||||
			goto out_clear_bit;
 | 
								goto out_clear_bit;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (gc->get_direction)
 | 
						if (guard.gc->get_direction)
 | 
				
			||||||
		gpiod_get_direction(desc);
 | 
							gpiod_get_direction(desc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = desc_set_label(desc, label ? : "?");
 | 
						ret = desc_set_label(desc, label ? : "?");
 | 
				
			||||||
| 
						 | 
					@ -2308,18 +2334,18 @@ int gpiod_request(struct gpio_desc *desc, const char *label)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool gpiod_free_commit(struct gpio_desc *desc)
 | 
					static bool gpiod_free_commit(struct gpio_desc *desc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *gc;
 | 
					 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
	bool ret = false;
 | 
						bool ret = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	might_sleep();
 | 
						might_sleep();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gc = desc->gdev->chip;
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	flags = READ_ONCE(desc->flags);
 | 
						flags = READ_ONCE(desc->flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (gc && test_bit(FLAG_REQUESTED, &flags)) {
 | 
						if (guard.gc && test_bit(FLAG_REQUESTED, &flags)) {
 | 
				
			||||||
		if (gc->free)
 | 
							if (guard.gc->free)
 | 
				
			||||||
			gc->free(gc, gpio_chip_hwgpio(desc));
 | 
								guard.gc->free(guard.gc, gpio_chip_hwgpio(desc));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clear_bit(FLAG_ACTIVE_LOW, &flags);
 | 
							clear_bit(FLAG_ACTIVE_LOW, &flags);
 | 
				
			||||||
		clear_bit(FLAG_REQUESTED, &flags);
 | 
							clear_bit(FLAG_REQUESTED, &flags);
 | 
				
			||||||
| 
						 | 
					@ -2476,11 +2502,14 @@ static int gpio_set_config_with_argument(struct gpio_desc *desc,
 | 
				
			||||||
					 enum pin_config_param mode,
 | 
										 enum pin_config_param mode,
 | 
				
			||||||
					 u32 argument)
 | 
										 u32 argument)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *gc = desc->gdev->chip;
 | 
					 | 
				
			||||||
	unsigned long config;
 | 
						unsigned long config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config = pinconf_to_config_packed(mode, argument);
 | 
						config = pinconf_to_config_packed(mode, argument);
 | 
				
			||||||
	return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config);
 | 
						return gpio_do_set_config(guard.gc, gpio_chip_hwgpio(desc), config);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int gpio_set_config_with_argument_optional(struct gpio_desc *desc,
 | 
					static int gpio_set_config_with_argument_optional(struct gpio_desc *desc,
 | 
				
			||||||
| 
						 | 
					@ -2570,18 +2599,20 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int gpiod_direction_input(struct gpio_desc *desc)
 | 
					int gpiod_direction_input(struct gpio_desc *desc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *gc;
 | 
					 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	VALIDATE_DESC(desc);
 | 
						VALIDATE_DESC(desc);
 | 
				
			||||||
	gc = desc->gdev->chip;
 | 
					
 | 
				
			||||||
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * It is legal to have no .get() and .direction_input() specified if
 | 
						 * It is legal to have no .get() and .direction_input() specified if
 | 
				
			||||||
	 * the chip is output-only, but you can't specify .direction_input()
 | 
						 * the chip is output-only, but you can't specify .direction_input()
 | 
				
			||||||
	 * and not support the .get() operation, that doesn't make sense.
 | 
						 * and not support the .get() operation, that doesn't make sense.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (!gc->get && gc->direction_input) {
 | 
						if (!guard.gc->get && guard.gc->direction_input) {
 | 
				
			||||||
		gpiod_warn(desc,
 | 
							gpiod_warn(desc,
 | 
				
			||||||
			   "%s: missing get() but have direction_input()\n",
 | 
								   "%s: missing get() but have direction_input()\n",
 | 
				
			||||||
			   __func__);
 | 
								   __func__);
 | 
				
			||||||
| 
						 | 
					@ -2594,10 +2625,12 @@ int gpiod_direction_input(struct gpio_desc *desc)
 | 
				
			||||||
	 * direction (if .get_direction() is supported) else we silently
 | 
						 * direction (if .get_direction() is supported) else we silently
 | 
				
			||||||
	 * assume we are in input mode after this.
 | 
						 * assume we are in input mode after this.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (gc->direction_input) {
 | 
						if (guard.gc->direction_input) {
 | 
				
			||||||
		ret = gc->direction_input(gc, gpio_chip_hwgpio(desc));
 | 
							ret = guard.gc->direction_input(guard.gc,
 | 
				
			||||||
	} else if (gc->get_direction &&
 | 
											gpio_chip_hwgpio(desc));
 | 
				
			||||||
		  (gc->get_direction(gc, gpio_chip_hwgpio(desc)) != 1)) {
 | 
						} else if (guard.gc->get_direction &&
 | 
				
			||||||
 | 
							  (guard.gc->get_direction(guard.gc,
 | 
				
			||||||
 | 
										   gpio_chip_hwgpio(desc)) != 1)) {
 | 
				
			||||||
		gpiod_warn(desc,
 | 
							gpiod_warn(desc,
 | 
				
			||||||
			   "%s: missing direction_input() operation and line is output\n",
 | 
								   "%s: missing direction_input() operation and line is output\n",
 | 
				
			||||||
			   __func__);
 | 
								   __func__);
 | 
				
			||||||
| 
						 | 
					@ -2616,28 +2649,31 @@ EXPORT_SYMBOL_GPL(gpiod_direction_input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
 | 
					static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *gc = desc->gdev->chip;
 | 
						int val = !!value, ret = 0;
 | 
				
			||||||
	int val = !!value;
 | 
					
 | 
				
			||||||
	int ret = 0;
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * It's OK not to specify .direction_output() if the gpiochip is
 | 
						 * It's OK not to specify .direction_output() if the gpiochip is
 | 
				
			||||||
	 * output-only, but if there is then not even a .set() operation it
 | 
						 * output-only, but if there is then not even a .set() operation it
 | 
				
			||||||
	 * is pretty tricky to drive the output line.
 | 
						 * is pretty tricky to drive the output line.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (!gc->set && !gc->direction_output) {
 | 
						if (!guard.gc->set && !guard.gc->direction_output) {
 | 
				
			||||||
		gpiod_warn(desc,
 | 
							gpiod_warn(desc,
 | 
				
			||||||
			   "%s: missing set() and direction_output() operations\n",
 | 
								   "%s: missing set() and direction_output() operations\n",
 | 
				
			||||||
			   __func__);
 | 
								   __func__);
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (gc->direction_output) {
 | 
						if (guard.gc->direction_output) {
 | 
				
			||||||
		ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val);
 | 
							ret = guard.gc->direction_output(guard.gc,
 | 
				
			||||||
 | 
											 gpio_chip_hwgpio(desc), val);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		/* Check that we are in output mode if we can */
 | 
							/* Check that we are in output mode if we can */
 | 
				
			||||||
		if (gc->get_direction &&
 | 
							if (guard.gc->get_direction &&
 | 
				
			||||||
		    gc->get_direction(gc, gpio_chip_hwgpio(desc))) {
 | 
							    guard.gc->get_direction(guard.gc, gpio_chip_hwgpio(desc))) {
 | 
				
			||||||
			gpiod_warn(desc,
 | 
								gpiod_warn(desc,
 | 
				
			||||||
				"%s: missing direction_output() operation\n",
 | 
									"%s: missing direction_output() operation\n",
 | 
				
			||||||
				__func__);
 | 
									__func__);
 | 
				
			||||||
| 
						 | 
					@ -2647,7 +2683,7 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
 | 
				
			||||||
		 * If we can't actively set the direction, we are some
 | 
							 * If we can't actively set the direction, we are some
 | 
				
			||||||
		 * output-only chip, so just drive the output as desired.
 | 
							 * output-only chip, so just drive the output as desired.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		gc->set(gc, gpio_chip_hwgpio(desc), val);
 | 
							guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), val);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!ret)
 | 
						if (!ret)
 | 
				
			||||||
| 
						 | 
					@ -2763,17 +2799,20 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output);
 | 
				
			||||||
int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
 | 
					int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
	struct gpio_chip *gc;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	VALIDATE_DESC(desc);
 | 
						VALIDATE_DESC(desc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gc = desc->gdev->chip;
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
	if (!gc->en_hw_timestamp) {
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!guard.gc->en_hw_timestamp) {
 | 
				
			||||||
		gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
 | 
							gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
 | 
				
			||||||
		return -ENOTSUPP;
 | 
							return -ENOTSUPP;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = gc->en_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags);
 | 
						ret = guard.gc->en_hw_timestamp(guard.gc,
 | 
				
			||||||
 | 
										gpio_chip_hwgpio(desc), flags);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		gpiod_warn(desc, "%s: hw ts request failed\n", __func__);
 | 
							gpiod_warn(desc, "%s: hw ts request failed\n", __func__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2792,17 +2831,20 @@ EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns);
 | 
				
			||||||
int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
 | 
					int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
	struct gpio_chip *gc;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	VALIDATE_DESC(desc);
 | 
						VALIDATE_DESC(desc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gc = desc->gdev->chip;
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
	if (!gc->dis_hw_timestamp) {
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!guard.gc->dis_hw_timestamp) {
 | 
				
			||||||
		gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
 | 
							gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
 | 
				
			||||||
		return -ENOTSUPP;
 | 
							return -ENOTSUPP;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = gc->dis_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags);
 | 
						ret = guard.gc->dis_hw_timestamp(guard.gc, gpio_chip_hwgpio(desc),
 | 
				
			||||||
 | 
										 flags);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		gpiod_warn(desc, "%s: hw ts release failed\n", __func__);
 | 
							gpiod_warn(desc, "%s: hw ts release failed\n", __func__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2821,12 +2863,13 @@ EXPORT_SYMBOL_GPL(gpiod_disable_hw_timestamp_ns);
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int gpiod_set_config(struct gpio_desc *desc, unsigned long config)
 | 
					int gpiod_set_config(struct gpio_desc *desc, unsigned long config)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *gc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	VALIDATE_DESC(desc);
 | 
						VALIDATE_DESC(desc);
 | 
				
			||||||
	gc = desc->gdev->chip;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config);
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return gpio_do_set_config(guard.gc, gpio_chip_hwgpio(desc), config);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(gpiod_set_config);
 | 
					EXPORT_SYMBOL_GPL(gpiod_set_config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2924,10 +2967,19 @@ static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *des
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
 | 
					static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct gpio_device *gdev;
 | 
				
			||||||
	struct gpio_chip *gc;
 | 
						struct gpio_chip *gc;
 | 
				
			||||||
	int value;
 | 
						int value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gc = desc->gdev->chip;
 | 
						/* FIXME Unable to use gpio_chip_guard due to const desc. */
 | 
				
			||||||
 | 
						gdev = desc->gdev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						guard(srcu)(&gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gc = rcu_dereference(gdev->chip);
 | 
				
			||||||
 | 
						if (!gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	value = gpio_chip_get_value(gc, desc);
 | 
						value = gpio_chip_get_value(gc, desc);
 | 
				
			||||||
	value = value < 0 ? value : !!value;
 | 
						value = value < 0 ? value : !!value;
 | 
				
			||||||
	trace_gpio_value(desc_to_gpio(desc), 1, value);
 | 
						trace_gpio_value(desc_to_gpio(desc), 1, value);
 | 
				
			||||||
| 
						 | 
					@ -2953,6 +3005,14 @@ static int gpio_chip_get_multiple(struct gpio_chip *gc,
 | 
				
			||||||
	return -EIO;
 | 
						return -EIO;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* The 'other' chip must be protected with its GPIO device's SRCU. */
 | 
				
			||||||
 | 
					static bool gpio_device_chip_cmp(struct gpio_device *gdev, struct gpio_chip *gc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						guard(srcu)(&gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return gc == rcu_dereference(gdev->chip);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
 | 
					int gpiod_get_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
				  unsigned int array_size,
 | 
									  unsigned int array_size,
 | 
				
			||||||
				  struct gpio_desc **desc_array,
 | 
									  struct gpio_desc **desc_array,
 | 
				
			||||||
| 
						 | 
					@ -2990,33 +3050,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (i < array_size) {
 | 
						while (i < array_size) {
 | 
				
			||||||
		struct gpio_chip *gc = desc_array[i]->gdev->chip;
 | 
					 | 
				
			||||||
		DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO);
 | 
							DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO);
 | 
				
			||||||
		DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO);
 | 
							DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO);
 | 
				
			||||||
		unsigned long *mask, *bits;
 | 
							unsigned long *mask, *bits;
 | 
				
			||||||
		int first, j;
 | 
							int first, j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (likely(gc->ngpio <= FASTPATH_NGPIO)) {
 | 
							CLASS(gpio_chip_guard, guard)(desc_array[i]);
 | 
				
			||||||
 | 
							if (!guard.gc)
 | 
				
			||||||
 | 
								return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (likely(guard.gc->ngpio <= FASTPATH_NGPIO)) {
 | 
				
			||||||
			mask = fastpath_mask;
 | 
								mask = fastpath_mask;
 | 
				
			||||||
			bits = fastpath_bits;
 | 
								bits = fastpath_bits;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC;
 | 
								gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			mask = bitmap_alloc(gc->ngpio, flags);
 | 
								mask = bitmap_alloc(guard.gc->ngpio, flags);
 | 
				
			||||||
			if (!mask)
 | 
								if (!mask)
 | 
				
			||||||
				return -ENOMEM;
 | 
									return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			bits = bitmap_alloc(gc->ngpio, flags);
 | 
								bits = bitmap_alloc(guard.gc->ngpio, flags);
 | 
				
			||||||
			if (!bits) {
 | 
								if (!bits) {
 | 
				
			||||||
				bitmap_free(mask);
 | 
									bitmap_free(mask);
 | 
				
			||||||
				return -ENOMEM;
 | 
									return -ENOMEM;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		bitmap_zero(mask, gc->ngpio);
 | 
							bitmap_zero(mask, guard.gc->ngpio);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!can_sleep)
 | 
							if (!can_sleep)
 | 
				
			||||||
			WARN_ON(gc->can_sleep);
 | 
								WARN_ON(guard.gc->can_sleep);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* collect all inputs belonging to the same chip */
 | 
							/* collect all inputs belonging to the same chip */
 | 
				
			||||||
		first = i;
 | 
							first = i;
 | 
				
			||||||
| 
						 | 
					@ -3031,9 +3094,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
				i = find_next_zero_bit(array_info->get_mask,
 | 
									i = find_next_zero_bit(array_info->get_mask,
 | 
				
			||||||
						       array_size, i);
 | 
											       array_size, i);
 | 
				
			||||||
		} while ((i < array_size) &&
 | 
							} while ((i < array_size) &&
 | 
				
			||||||
			 (desc_array[i]->gdev->chip == gc));
 | 
								 gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = gpio_chip_get_multiple(gc, mask, bits);
 | 
							ret = gpio_chip_get_multiple(guard.gc, mask, bits);
 | 
				
			||||||
		if (ret) {
 | 
							if (ret) {
 | 
				
			||||||
			if (mask != fastpath_mask)
 | 
								if (mask != fastpath_mask)
 | 
				
			||||||
				bitmap_free(mask);
 | 
									bitmap_free(mask);
 | 
				
			||||||
| 
						 | 
					@ -3174,14 +3237,16 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value);
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
 | 
					static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0, offset = gpio_chip_hwgpio(desc);
 | 
				
			||||||
	struct gpio_chip *gc = desc->gdev->chip;
 | 
					
 | 
				
			||||||
	int offset = gpio_chip_hwgpio(desc);
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (value) {
 | 
						if (value) {
 | 
				
			||||||
		ret = gc->direction_input(gc, offset);
 | 
							ret = guard.gc->direction_input(guard.gc, offset);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		ret = gc->direction_output(gc, offset, 0);
 | 
							ret = guard.gc->direction_output(guard.gc, offset, 0);
 | 
				
			||||||
		if (!ret)
 | 
							if (!ret)
 | 
				
			||||||
			set_bit(FLAG_IS_OUT, &desc->flags);
 | 
								set_bit(FLAG_IS_OUT, &desc->flags);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -3199,16 +3264,18 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value)
 | 
					static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0, offset = gpio_chip_hwgpio(desc);
 | 
				
			||||||
	struct gpio_chip *gc = desc->gdev->chip;
 | 
					
 | 
				
			||||||
	int offset = gpio_chip_hwgpio(desc);
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (value) {
 | 
						if (value) {
 | 
				
			||||||
		ret = gc->direction_output(gc, offset, 1);
 | 
							ret = guard.gc->direction_output(guard.gc, offset, 1);
 | 
				
			||||||
		if (!ret)
 | 
							if (!ret)
 | 
				
			||||||
			set_bit(FLAG_IS_OUT, &desc->flags);
 | 
								set_bit(FLAG_IS_OUT, &desc->flags);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		ret = gc->direction_input(gc, offset);
 | 
							ret = guard.gc->direction_input(guard.gc, offset);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	trace_gpio_direction(desc_to_gpio(desc), !value, ret);
 | 
						trace_gpio_direction(desc_to_gpio(desc), !value, ret);
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
| 
						 | 
					@ -3219,11 +3286,12 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
 | 
					static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *gc;
 | 
						CLASS(gpio_chip_guard, guard)(desc);
 | 
				
			||||||
 | 
						if (!guard.gc)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gc = desc->gdev->chip;
 | 
					 | 
				
			||||||
	trace_gpio_value(desc_to_gpio(desc), 0, value);
 | 
						trace_gpio_value(desc_to_gpio(desc), 0, value);
 | 
				
			||||||
	gc->set(gc, gpio_chip_hwgpio(desc), value);
 | 
						guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), value);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -3284,33 +3352,36 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (i < array_size) {
 | 
						while (i < array_size) {
 | 
				
			||||||
		struct gpio_chip *gc = desc_array[i]->gdev->chip;
 | 
					 | 
				
			||||||
		DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO);
 | 
							DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO);
 | 
				
			||||||
		DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO);
 | 
							DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO);
 | 
				
			||||||
		unsigned long *mask, *bits;
 | 
							unsigned long *mask, *bits;
 | 
				
			||||||
		int count = 0;
 | 
							int count = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (likely(gc->ngpio <= FASTPATH_NGPIO)) {
 | 
							CLASS(gpio_chip_guard, guard)(desc_array[i]);
 | 
				
			||||||
 | 
							if (!guard.gc)
 | 
				
			||||||
 | 
								return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (likely(guard.gc->ngpio <= FASTPATH_NGPIO)) {
 | 
				
			||||||
			mask = fastpath_mask;
 | 
								mask = fastpath_mask;
 | 
				
			||||||
			bits = fastpath_bits;
 | 
								bits = fastpath_bits;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC;
 | 
								gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			mask = bitmap_alloc(gc->ngpio, flags);
 | 
								mask = bitmap_alloc(guard.gc->ngpio, flags);
 | 
				
			||||||
			if (!mask)
 | 
								if (!mask)
 | 
				
			||||||
				return -ENOMEM;
 | 
									return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			bits = bitmap_alloc(gc->ngpio, flags);
 | 
								bits = bitmap_alloc(guard.gc->ngpio, flags);
 | 
				
			||||||
			if (!bits) {
 | 
								if (!bits) {
 | 
				
			||||||
				bitmap_free(mask);
 | 
									bitmap_free(mask);
 | 
				
			||||||
				return -ENOMEM;
 | 
									return -ENOMEM;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		bitmap_zero(mask, gc->ngpio);
 | 
							bitmap_zero(mask, guard.gc->ngpio);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!can_sleep)
 | 
							if (!can_sleep)
 | 
				
			||||||
			WARN_ON(gc->can_sleep);
 | 
								WARN_ON(guard.gc->can_sleep);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		do {
 | 
							do {
 | 
				
			||||||
			struct gpio_desc *desc = desc_array[i];
 | 
								struct gpio_desc *desc = desc_array[i];
 | 
				
			||||||
| 
						 | 
					@ -3346,10 +3417,10 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
 | 
				
			||||||
				i = find_next_zero_bit(array_info->set_mask,
 | 
									i = find_next_zero_bit(array_info->set_mask,
 | 
				
			||||||
						       array_size, i);
 | 
											       array_size, i);
 | 
				
			||||||
		} while ((i < array_size) &&
 | 
							} while ((i < array_size) &&
 | 
				
			||||||
			 (desc_array[i]->gdev->chip == gc));
 | 
								 gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc));
 | 
				
			||||||
		/* push collected bits to outputs */
 | 
							/* push collected bits to outputs */
 | 
				
			||||||
		if (count != 0)
 | 
							if (count != 0)
 | 
				
			||||||
			gpio_chip_set_multiple(gc, mask, bits);
 | 
								gpio_chip_set_multiple(guard.gc, mask, bits);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (mask != fastpath_mask)
 | 
							if (mask != fastpath_mask)
 | 
				
			||||||
			bitmap_free(mask);
 | 
								bitmap_free(mask);
 | 
				
			||||||
| 
						 | 
					@ -3505,6 +3576,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int gpiod_to_irq(const struct gpio_desc *desc)
 | 
					int gpiod_to_irq(const struct gpio_desc *desc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct gpio_device *gdev;
 | 
				
			||||||
	struct gpio_chip *gc;
 | 
						struct gpio_chip *gc;
 | 
				
			||||||
	int offset;
 | 
						int offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3516,7 +3588,13 @@ int gpiod_to_irq(const struct gpio_desc *desc)
 | 
				
			||||||
	if (!desc || IS_ERR(desc))
 | 
						if (!desc || IS_ERR(desc))
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gc = desc->gdev->chip;
 | 
						gdev = desc->gdev;
 | 
				
			||||||
 | 
						/* FIXME Cannot use gpio_chip_guard due to const desc. */
 | 
				
			||||||
 | 
						guard(srcu)(&gdev->srcu);
 | 
				
			||||||
 | 
						gc = rcu_dereference(gdev->chip);
 | 
				
			||||||
 | 
						if (!gc)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	offset = gpio_chip_hwgpio(desc);
 | 
						offset = gpio_chip_hwgpio(desc);
 | 
				
			||||||
	if (gc->to_irq) {
 | 
						if (gc->to_irq) {
 | 
				
			||||||
		int retirq = gc->to_irq(gc, offset);
 | 
							int retirq = gc->to_irq(gc, offset);
 | 
				
			||||||
| 
						 | 
					@ -4696,12 +4774,20 @@ core_initcall(gpiolib_dev_init);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
 | 
					static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *gc = gdev->chip;
 | 
					 | 
				
			||||||
	bool active_low, is_irq, is_out;
 | 
						bool active_low, is_irq, is_out;
 | 
				
			||||||
	unsigned int gpio = gdev->base;
 | 
						unsigned int gpio = gdev->base;
 | 
				
			||||||
	struct gpio_desc *desc;
 | 
						struct gpio_desc *desc;
 | 
				
			||||||
 | 
						struct gpio_chip *gc;
 | 
				
			||||||
	int value;
 | 
						int value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						guard(srcu)(&gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gc = rcu_dereference(gdev->chip);
 | 
				
			||||||
 | 
						if (!gc) {
 | 
				
			||||||
 | 
							seq_puts(s, "Underlying GPIO chip is gone\n");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for_each_gpio_desc(gc, desc) {
 | 
						for_each_gpio_desc(gc, desc) {
 | 
				
			||||||
		guard(srcu)(&desc->srcu);
 | 
							guard(srcu)(&desc->srcu);
 | 
				
			||||||
		if (test_bit(FLAG_REQUESTED, &desc->flags)) {
 | 
							if (test_bit(FLAG_REQUESTED, &desc->flags)) {
 | 
				
			||||||
| 
						 | 
					@ -4776,9 +4862,12 @@ static int gpiolib_seq_show(struct seq_file *s, void *v)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpiolib_seq_priv *priv = s->private;
 | 
						struct gpiolib_seq_priv *priv = s->private;
 | 
				
			||||||
	struct gpio_device *gdev = v;
 | 
						struct gpio_device *gdev = v;
 | 
				
			||||||
	struct gpio_chip *gc = gdev->chip;
 | 
						struct gpio_chip *gc;
 | 
				
			||||||
	struct device *parent;
 | 
						struct device *parent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						guard(srcu)(&gdev->srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gc = rcu_dereference(gdev->chip);
 | 
				
			||||||
	if (!gc) {
 | 
						if (!gc) {
 | 
				
			||||||
		seq_printf(s, "%s%s: (dangling chip)",
 | 
							seq_printf(s, "%s%s: (dangling chip)",
 | 
				
			||||||
			   priv->newline ? "\n" : "",
 | 
								   priv->newline ? "\n" : "",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,7 +63,7 @@ struct gpio_device {
 | 
				
			||||||
	int			id;
 | 
						int			id;
 | 
				
			||||||
	struct device		*mockdev;
 | 
						struct device		*mockdev;
 | 
				
			||||||
	struct module		*owner;
 | 
						struct module		*owner;
 | 
				
			||||||
	struct gpio_chip	*chip;
 | 
						struct gpio_chip __rcu	*chip;
 | 
				
			||||||
	struct gpio_desc	*descs;
 | 
						struct gpio_desc	*descs;
 | 
				
			||||||
	int			base;
 | 
						int			base;
 | 
				
			||||||
	u16			ngpio;
 | 
						u16			ngpio;
 | 
				
			||||||
| 
						 | 
					@ -193,6 +193,26 @@ struct gpio_desc {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define gpiod_not_found(desc)		(IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)
 | 
					#define gpiod_not_found(desc)		(IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct gpio_chip_guard {
 | 
				
			||||||
 | 
						struct gpio_device *gdev;
 | 
				
			||||||
 | 
						struct gpio_chip *gc;
 | 
				
			||||||
 | 
						int idx;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFINE_CLASS(gpio_chip_guard,
 | 
				
			||||||
 | 
						     struct gpio_chip_guard,
 | 
				
			||||||
 | 
						     srcu_read_unlock(&_T.gdev->srcu, _T.idx),
 | 
				
			||||||
 | 
						     ({
 | 
				
			||||||
 | 
							struct gpio_chip_guard _guard;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_guard.gdev = desc->gdev;
 | 
				
			||||||
 | 
							_guard.idx = srcu_read_lock(&_guard.gdev->srcu);
 | 
				
			||||||
 | 
							_guard.gc = rcu_dereference(_guard.gdev->chip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_guard;
 | 
				
			||||||
 | 
						     }),
 | 
				
			||||||
 | 
						     struct gpio_desc *desc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int gpiod_request(struct gpio_desc *desc, const char *label);
 | 
					int gpiod_request(struct gpio_desc *desc, const char *label);
 | 
				
			||||||
void gpiod_free(struct gpio_desc *desc);
 | 
					void gpiod_free(struct gpio_desc *desc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue