diff options
-rw-r--r-- | drivers/gpio/gpio-arizona.c | 7 | ||||
-rw-r--r-- | drivers/gpio/gpio-pca953x.c | 99 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-cdev.c | 385 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-sysfs.c | 1 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-sysfs.h | 24 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 15 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.h | 20 | ||||
-rw-r--r-- | include/uapi/linux/gpio.h | 2 | ||||
-rw-r--r-- | tools/gpio/gpio-event-mon.c | 3 | ||||
-rw-r--r-- | tools/gpio/gpio-utils.c | 4 | ||||
-rw-r--r-- | tools/gpio/lsgpio.c | 3 |
11 files changed, 317 insertions, 246 deletions
diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index 5640efe5e750..5bda38e0780f 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -64,6 +64,7 @@ static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset) ret = pm_runtime_get_sync(chip->parent); if (ret < 0) { dev_err(chip->parent, "Failed to resume: %d\n", ret); + pm_runtime_put_autosuspend(chip->parent); return ret; } @@ -72,12 +73,15 @@ static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset) if (ret < 0) { dev_err(chip->parent, "Failed to drop cache: %d\n", ret); + pm_runtime_put_autosuspend(chip->parent); return ret; } ret = regmap_read(arizona->regmap, reg, &val); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_autosuspend(chip->parent); return ret; + } pm_runtime_mark_last_busy(chip->parent); pm_runtime_put_autosuspend(chip->parent); @@ -106,6 +110,7 @@ static int arizona_gpio_direction_out(struct gpio_chip *chip, ret = pm_runtime_get_sync(chip->parent); if (ret < 0) { dev_err(chip->parent, "Failed to resume: %d\n", ret); + pm_runtime_put(chip->parent); return ret; } } diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index cd5fb522563e..9c90cf3aac5a 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -108,6 +108,84 @@ static const struct i2c_device_id pca953x_id[] = { }; MODULE_DEVICE_TABLE(i2c, pca953x_id); +#ifdef CONFIG_GPIO_PCA953X_IRQ + +#include <linux/dmi.h> +#include <linux/gpio.h> +#include <linux/list.h> + +static const struct dmi_system_id pca953x_dmi_acpi_irq_info[] = { + { + /* + * On Intel Galileo Gen 2 board the IRQ pin of one of + * the I²C GPIO expanders, which has GpioInt() resource, + * is provided as an absolute number instead of being + * relative. Since first controller (gpio-sch.c) and + * second (gpio-dwapb.c) are at the fixed bases, we may + * safely refer to the number in the global space to get + * an IRQ out of it. + */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2"), + }, + }, + {} +}; + +#ifdef CONFIG_ACPI +static int pca953x_acpi_get_pin(struct acpi_resource *ares, void *data) +{ + struct acpi_resource_gpio *agpio; + int *pin = data; + + if (acpi_gpio_get_irq_resource(ares, &agpio)) + *pin = agpio->pin_table[0]; + return 1; +} + +static int pca953x_acpi_find_pin(struct device *dev) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + int pin = -ENOENT, ret; + LIST_HEAD(r); + + ret = acpi_dev_get_resources(adev, &r, pca953x_acpi_get_pin, &pin); + acpi_dev_free_resource_list(&r); + if (ret < 0) + return ret; + + return pin; +} +#else +static inline int pca953x_acpi_find_pin(struct device *dev) { return -ENXIO; } +#endif + +static int pca953x_acpi_get_irq(struct device *dev) +{ + int pin, ret; + + pin = pca953x_acpi_find_pin(dev); + if (pin < 0) + return pin; + + dev_info(dev, "Applying ACPI interrupt quirk (GPIO %d)\n", pin); + + if (!gpio_is_valid(pin)) + return -EINVAL; + + ret = gpio_request(pin, "pca953x interrupt"); + if (ret) + return ret; + + ret = gpio_to_irq(pin); + + /* When pin is used as an IRQ, no need to keep it requested */ + gpio_free(pin); + + return ret; +} +#endif + static const struct acpi_device_id pca953x_acpi_ids[] = { { "INT3491", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { } @@ -323,6 +401,7 @@ static const struct regmap_config pca953x_ai_i2c_regmap = { .writeable_reg = pca953x_writeable_register, .volatile_reg = pca953x_volatile_register, + .disable_locking = true, .cache_type = REGCACHE_RBTREE, .max_register = 0x7f, }; @@ -624,8 +703,6 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) DECLARE_BITMAP(reg_direction, MAX_LINE); int level; - pca953x_read_regs(chip, chip->regs->direction, reg_direction); - if (chip->driver_data & PCA_PCAL) { /* Enable latch on interrupt-enabled inputs */ pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask); @@ -636,7 +713,11 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) pca953x_write_regs(chip, PCAL953X_INT_MASK, irq_mask); } + /* Switch direction to input if needed */ + pca953x_read_regs(chip, chip->regs->direction, reg_direction); + bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio); + bitmap_complement(reg_direction, reg_direction, gc->ngpio); bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio); /* Look for any newly setup interrupt */ @@ -735,14 +816,16 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid) struct gpio_chip *gc = &chip->gpio_chip; DECLARE_BITMAP(pending, MAX_LINE); int level; + bool ret; - if (!pca953x_irq_pending(chip, pending)) - return IRQ_NONE; + mutex_lock(&chip->i2c_lock); + ret = pca953x_irq_pending(chip, pending); + mutex_unlock(&chip->i2c_lock); for_each_set_bit(level, pending, gc->ngpio) handle_nested_irq(irq_find_mapping(gc->irq.domain, level)); - return IRQ_HANDLED; + return IRQ_RETVAL(ret); } static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) @@ -753,6 +836,12 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) DECLARE_BITMAP(irq_stat, MAX_LINE); int ret; + if (dmi_first_match(pca953x_dmi_acpi_irq_info)) { + ret = pca953x_acpi_get_irq(&client->dev); + if (ret > 0) + client->irq = ret; + } + if (!client->irq) return 0; diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index b8b872724628..e6c9b78adfc2 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -1,24 +1,24 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/anon_inodes.h> #include <linux/bitmap.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irqreturn.h> -#include <linux/spinlock.h> +#include <linux/cdev.h> +#include <linux/compat.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/file.h> #include <linux/gpio.h> #include <linux/gpio/driver.h> -#include <linux/pinctrl/consumer.h> -#include <linux/cdev.h> -#include <linux/uaccess.h> -#include <linux/compat.h> -#include <linux/anon_inodes.h> -#include <linux/file.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/kernel.h> #include <linux/kfifo.h> +#include <linux/module.h> +#include <linux/pinctrl/consumer.h> #include <linux/poll.h> +#include <linux/spinlock.h> #include <linux/timekeeping.h> +#include <linux/uaccess.h> #include <uapi/linux/gpio.h> #include "gpiolib.h" @@ -39,13 +39,13 @@ * @gdev: the GPIO device the handle pertains to * @label: consumer label used to tag descriptors * @descs: the GPIO descriptors held by this handle - * @numdescs: the number of descriptors held in the descs array + * @num_descs: the number of descriptors held in the descs array */ struct linehandle_state { struct gpio_device *gdev; const char *label; struct gpio_desc *descs[GPIOHANDLES_MAX]; - u32 numdescs; + u32 num_descs; }; #define GPIOHANDLE_REQUEST_VALID_FLAGS \ @@ -98,7 +98,7 @@ static int linehandle_validate_flags(u32 flags) /* Only one bias flag can be set. */ if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && (flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | - GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || ((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) return -EINVAL; @@ -106,6 +106,22 @@ static int linehandle_validate_flags(u32 flags) return 0; } +static void linehandle_flags_to_desc_flags(u32 lflags, unsigned long *flagsp) +{ + assign_bit(FLAG_ACTIVE_LOW, flagsp, + lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); + assign_bit(FLAG_OPEN_DRAIN, flagsp, + lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); + assign_bit(FLAG_OPEN_SOURCE, flagsp, + lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); + assign_bit(FLAG_PULL_UP, flagsp, + lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); + assign_bit(FLAG_PULL_DOWN, flagsp, + lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); + assign_bit(FLAG_BIAS_DISABLE, flagsp, + lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); +} + static long linehandle_set_config(struct linehandle_state *lh, void __user *ip) { @@ -113,7 +129,6 @@ static long linehandle_set_config(struct linehandle_state *lh, struct gpio_desc *desc; int i, ret; u32 lflags; - unsigned long *flagsp; if (copy_from_user(&gcnf, ip, sizeof(gcnf))) return -EFAULT; @@ -123,27 +138,9 @@ static long linehandle_set_config(struct linehandle_state *lh, if (ret) return ret; - for (i = 0; i < lh->numdescs; i++) { + for (i = 0; i < lh->num_descs; i++) { desc = lh->descs[i]; - flagsp = &desc->flags; - - assign_bit(FLAG_ACTIVE_LOW, flagsp, - lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); - - assign_bit(FLAG_OPEN_DRAIN, flagsp, - lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); - - assign_bit(FLAG_OPEN_SOURCE, flagsp, - lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); - - assign_bit(FLAG_PULL_UP, flagsp, - lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); - - assign_bit(FLAG_PULL_DOWN, flagsp, - lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); - - assign_bit(FLAG_BIAS_DISABLE, flagsp, - lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); + linehandle_flags_to_desc_flags(gcnf.flags, &desc->flags); /* * Lines have to be requested explicitly for input @@ -161,16 +158,16 @@ static long linehandle_set_config(struct linehandle_state *lh, return ret; } - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_CONFIG, desc); + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_CONFIG, desc); } return 0; } -static long linehandle_ioctl(struct file *filep, unsigned int cmd, +static long linehandle_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct linehandle_state *lh = filep->private_data; + struct linehandle_state *lh = file->private_data; void __user *ip = (void __user *)arg; struct gpiohandle_data ghd; DECLARE_BITMAP(vals, GPIOHANDLES_MAX); @@ -180,7 +177,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, /* NOTE: It's ok to read values of output lines. */ int ret = gpiod_get_array_value_complex(false, true, - lh->numdescs, + lh->num_descs, lh->descs, NULL, vals); @@ -188,7 +185,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, return ret; memset(&ghd, 0, sizeof(ghd)); - for (i = 0; i < lh->numdescs; i++) + for (i = 0; i < lh->num_descs; i++) ghd.values[i] = test_bit(i, vals); if (copy_to_user(ip, &ghd, sizeof(ghd))) @@ -207,16 +204,16 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, return -EFAULT; /* Clamp all values to [0,1] */ - for (i = 0; i < lh->numdescs; i++) + for (i = 0; i < lh->num_descs; i++) __assign_bit(i, vals, ghd.values[i]); /* Reuse the array setting function */ return gpiod_set_array_value_complex(false, - true, - lh->numdescs, - lh->descs, - NULL, - vals); + true, + lh->num_descs, + lh->descs, + NULL, + vals); } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) { return linehandle_set_config(lh, ip); } @@ -224,24 +221,28 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, } #ifdef CONFIG_COMPAT -static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd, - unsigned long arg) +static long linehandle_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg) { - return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); + return linehandle_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } #endif -static int linehandle_release(struct inode *inode, struct file *filep) +static void linehandle_free(struct linehandle_state *lh) { - struct linehandle_state *lh = filep->private_data; - struct gpio_device *gdev = lh->gdev; int i; - for (i = 0; i < lh->numdescs; i++) - gpiod_free(lh->descs[i]); + for (i = 0; i < lh->num_descs; i++) + if (lh->descs[i]) + gpiod_free(lh->descs[i]); kfree(lh->label); + put_device(&lh->gdev->dev); kfree(lh); - put_device(&gdev->dev); +} + +static int linehandle_release(struct inode *inode, struct file *file) +{ + linehandle_free(file->private_data); return 0; } @@ -260,7 +261,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) struct gpiohandle_request handlereq; struct linehandle_state *lh; struct file *file; - int fd, i, count = 0, ret; + int fd, i, ret; u32 lflags; if (copy_from_user(&handlereq, ip, sizeof(handlereq))) @@ -291,6 +292,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) } } + lh->num_descs = handlereq.lines; + /* Request each GPIO */ for (i = 0; i < handlereq.lines; i++) { u32 offset = handlereq.lineoffsets[i]; @@ -298,31 +301,18 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) if (IS_ERR(desc)) { ret = PTR_ERR(desc); - goto out_free_descs; + goto out_free_lh; } ret = gpiod_request(desc, lh->label); if (ret) - goto out_free_descs; + goto out_free_lh; lh->descs[i] = desc; - count = i + 1; - - if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) - set_bit(FLAG_ACTIVE_LOW, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) - set_bit(FLAG_OPEN_DRAIN, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) - set_bit(FLAG_OPEN_SOURCE, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) - set_bit(FLAG_BIAS_DISABLE, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) - set_bit(FLAG_PULL_DOWN, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) - set_bit(FLAG_PULL_UP, &desc->flags); + linehandle_flags_to_desc_flags(handlereq.flags, &desc->flags); ret = gpiod_set_transitory(desc, false); if (ret < 0) - goto out_free_descs; + goto out_free_lh; /* * Lines have to be requested explicitly for input @@ -333,27 +323,24 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) ret = gpiod_direction_output(desc, val); if (ret) - goto out_free_descs; + goto out_free_lh; } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { ret = gpiod_direction_input(desc); if (ret) - goto out_free_descs; + goto out_free_lh; } - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_REQUESTED, desc); + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", offset); } - /* Let i point at the last handle */ - i--; - lh->numdescs = handlereq.lines; fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); if (fd < 0) { ret = fd; - goto out_free_descs; + goto out_free_lh; } file = anon_inode_getfile("gpio-linehandle", @@ -379,19 +366,14 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) fd_install(fd, file); dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", - lh->numdescs); + lh->num_descs); return 0; out_put_unused_fd: put_unused_fd(fd); -out_free_descs: - for (i = 0; i < count; i++) - gpiod_free(lh->descs[i]); - kfree(lh->label); out_free_lh: - kfree(lh); - put_device(&gdev->dev); + linehandle_free(lh); return ret; } @@ -427,13 +409,13 @@ struct lineevent_state { (GPIOEVENT_REQUEST_RISING_EDGE | \ GPIOEVENT_REQUEST_FALLING_EDGE) -static __poll_t lineevent_poll(struct file *filep, - struct poll_table_struct *wait) +static __poll_t lineevent_poll(struct file *file, + struct poll_table_struct *wait) { - struct lineevent_state *le = filep->private_data; + struct lineevent_state *le = file->private_data; __poll_t events = 0; - poll_wait(filep, &le->wait, wait); + poll_wait(file, &le->wait, wait); if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock)) events = EPOLLIN | EPOLLRDNORM; @@ -442,12 +424,12 @@ static __poll_t lineevent_poll(struct file *filep, } -static ssize_t lineevent_read(struct file *filep, +static ssize_t lineevent_read(struct file *file, char __user *buf, size_t count, loff_t *f_ps) { - struct lineevent_state *le = filep->private_data; + struct lineevent_state *le = file->private_data; struct gpioevent_data ge; ssize_t bytes_read = 0; int ret; @@ -463,7 +445,7 @@ static ssize_t lineevent_read(struct file *filep, return bytes_read; } - if (filep->f_flags & O_NONBLOCK) { + if (file->f_flags & O_NONBLOCK) { spin_unlock(&le->wait.lock); return -EAGAIN; } @@ -496,23 +478,27 @@ static ssize_t lineevent_read(struct file *filep, return bytes_read; } -static int lineevent_release(struct inode *inode, struct file *filep) +static void lineevent_free(struct lineevent_state *le) { - struct lineevent_state *le = filep->private_data; - struct gpio_device *gdev = le->gdev; - - free_irq(le->irq, le); - gpiod_free(le->desc); + if (le->irq) + free_irq(le->irq, le); + if (le->desc) + gpiod_free(le->desc); kfree(le->label); + put_device(&le->gdev->dev); kfree(le); - put_device(&gdev->dev); +} + +static int lineevent_release(struct inode *inode, struct file *file) +{ + lineevent_free(file->private_data); return 0; } -static long lineevent_ioctl(struct file *filep, unsigned int cmd, +static long lineevent_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct lineevent_state *le = filep->private_data; + struct lineevent_state *le = file->private_data; void __user *ip = (void __user *)arg; struct gpiohandle_data ghd; @@ -539,10 +525,10 @@ static long lineevent_ioctl(struct file *filep, unsigned int cmd, } #ifdef CONFIG_COMPAT -static long lineevent_ioctl_compat(struct file *filep, unsigned int cmd, +static long lineevent_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) { - return lineevent_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); + return lineevent_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } #endif @@ -630,7 +616,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) u32 eflags; int fd; int ret; - int irqflags = 0; + int irq, irqflags = 0; if (copy_from_user(&eventreq, ip, sizeof(eventreq))) return -EFAULT; @@ -681,31 +667,25 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) ret = gpiod_request(desc, le->label); if (ret) - goto out_free_label; + goto out_free_le; le->desc = desc; le->eflags = eflags; - if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) - set_bit(FLAG_ACTIVE_LOW, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) - set_bit(FLAG_BIAS_DISABLE, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) - set_bit(FLAG_PULL_DOWN, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) - set_bit(FLAG_PULL_UP, &desc->flags); + linehandle_flags_to_desc_flags(lflags, &desc->flags); ret = gpiod_direction_input(desc); if (ret) - goto out_free_desc; + goto out_free_le; - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_REQUESTED, desc); + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); - le->irq = gpiod_to_irq(desc); - if (le->irq <= 0) { + irq = gpiod_to_irq(desc); + if (irq <= 0) { ret = -ENODEV; - goto out_free_desc; + goto out_free_le; } + le->irq = irq; if (eflags & GPIOEVENT_REQUEST_RISING_EDGE) irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? @@ -720,18 +700,18 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) /* Request a thread to read the events */ ret = request_threaded_irq(le->irq, - lineevent_irq_handler, - lineevent_irq_thread, - irqflags, - le->label, - le); + lineevent_irq_handler, + lineevent_irq_thread, + irqflags, + le->label, + le); if (ret) - goto out_free_desc; + goto out_free_le; fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); if (fd < 0) { ret = fd; - goto out_free_irq; + goto out_free_le; } file = anon_inode_getfile("gpio-event", @@ -760,15 +740,8 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) out_put_unused_fd: put_unused_fd(fd); -out_free_irq: - free_irq(le->irq, le); -out_free_desc: - gpiod_free(le->desc); -out_free_label: - kfree(le->label); out_free_le: - kfree(le); - put_device(&gdev->dev); + lineevent_free(le); return ret; } @@ -848,15 +821,14 @@ struct gpio_chardev_data { /* * gpio_ioctl() - ioctl handler for the GPIO chardev */ -static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct gpio_chardev_data *priv = filp->private_data; - struct gpio_device *gdev = priv->gdev; + struct gpio_chardev_data *cdev = file->private_data; + struct gpio_device *gdev = cdev->gdev; struct gpio_chip *gc = gdev->chip; 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) @@ -884,12 +856,11 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) return -EFAULT; + /* this doubles as a range check on line_offset */ desc = gpiochip_get_desc(gc, lineinfo.line_offset); if (IS_ERR(desc)) return PTR_ERR(desc); - hwgpio = gpio_chip_hwgpio(desc); - gpio_desc_to_lineinfo(desc, &lineinfo); if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) @@ -905,46 +876,42 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) return -EFAULT; + /* this doubles as a range check on line_offset */ desc = gpiochip_get_desc(gc, lineinfo.line_offset); if (IS_ERR(desc)) return PTR_ERR(desc); - hwgpio = gpio_chip_hwgpio(desc); - - if (test_bit(hwgpio, priv->watched_lines)) + if (test_and_set_bit(lineinfo.line_offset, cdev->watched_lines)) return -EBUSY; gpio_desc_to_lineinfo(desc, &lineinfo); - if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) + if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) { + clear_bit(lineinfo.line_offset, cdev->watched_lines); return -EFAULT; + } - set_bit(hwgpio, priv->watched_lines); return 0; } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) { if (copy_from_user(&offset, ip, sizeof(offset))) return -EFAULT; - desc = gpiochip_get_desc(gc, offset); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - hwgpio = gpio_chip_hwgpio(desc); + if (offset >= cdev->gdev->ngpio) + return -EINVAL; - if (!test_bit(hwgpio, priv->watched_lines)) + if (!test_and_clear_bit(offset, cdev->watched_lines)) return -EBUSY; - clear_bit(hwgpio, priv->watched_lines); return 0; } return -EINVAL; } #ifdef CONFIG_COMPAT -static long gpio_ioctl_compat(struct file *filp, unsigned int cmd, +static long gpio_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) { - return gpio_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); + return gpio_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } #endif @@ -957,12 +924,12 @@ to_gpio_chardev_data(struct notifier_block *nb) static int lineinfo_changed_notify(struct notifier_block *nb, unsigned long action, void *data) { - struct gpio_chardev_data *priv = to_gpio_chardev_data(nb); + struct gpio_chardev_data *cdev = to_gpio_chardev_data(nb); struct gpioline_info_changed chg; struct gpio_desc *desc = data; int ret; - if (!test_bit(gpio_chip_hwgpio(desc), priv->watched_lines)) + if (!test_bit(gpio_chip_hwgpio(desc), cdev->watched_lines)) return NOTIFY_DONE; memset(&chg, 0, sizeof(chg)); @@ -971,34 +938,34 @@ static int lineinfo_changed_notify(struct notifier_block *nb, chg.timestamp = ktime_get_ns(); gpio_desc_to_lineinfo(desc, &chg.info); - ret = kfifo_in_spinlocked(&priv->events, &chg, 1, &priv->wait.lock); + ret = kfifo_in_spinlocked(&cdev->events, &chg, 1, &cdev->wait.lock); if (ret) - wake_up_poll(&priv->wait, EPOLLIN); + wake_up_poll(&cdev->wait, EPOLLIN); else pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n"); return NOTIFY_OK; } -static __poll_t lineinfo_watch_poll(struct file *filep, +static __poll_t lineinfo_watch_poll(struct file *file, struct poll_table_struct *pollt) { - struct gpio_chardev_data *priv = filep->private_data; + struct gpio_chardev_data *cdev = file->private_data; __poll_t events = 0; - poll_wait(filep, &priv->wait, pollt); + poll_wait(file, &cdev->wait, pollt); - if (!kfifo_is_empty_spinlocked_noirqsave(&priv->events, - &priv->wait.lock)) + if (!kfifo_is_empty_spinlocked_noirqsave(&cdev->events, + &cdev->wait.lock)) events = EPOLLIN | EPOLLRDNORM; return events; } -static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf, +static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, size_t count, loff_t *off) { - struct gpio_chardev_data *priv = filep->private_data; + struct gpio_chardev_data *cdev = file->private_data; struct gpioline_info_changed event; ssize_t bytes_read = 0; int ret; @@ -1007,28 +974,28 @@ static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf, return -EINVAL; do { - spin_lock(&priv->wait.lock); - if (kfifo_is_empty(&priv->events)) { + spin_lock(&cdev->wait.lock); + if (kfifo_is_empty(&cdev->events)) { if (bytes_read) { - spin_unlock(&priv->wait.lock); + spin_unlock(&cdev->wait.lock); return bytes_read; } - if (filep->f_flags & O_NONBLOCK) { - spin_unlock(&priv->wait.lock); + if (file->f_flags & O_NONBLOCK) { + spin_unlock(&cdev->wait.lock); return -EAGAIN; } - ret = wait_event_interruptible_locked(priv->wait, - !kfifo_is_empty(&priv->events)); + ret = wait_event_interruptible_locked(cdev->wait, + !kfifo_is_empty(&cdev->events)); if (ret) { - spin_unlock(&priv->wait.lock); + spin_unlock(&cdev->wait.lock); return ret; } } - ret = kfifo_out(&priv->events, &event, 1); - spin_unlock(&priv->wait.lock); + ret = kfifo_out(&cdev->events, &event, 1); + spin_unlock(&cdev->wait.lock); if (ret != 1) { ret = -EIO; break; @@ -1046,73 +1013,73 @@ static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf, /** * gpio_chrdev_open() - open the chardev for ioctl operations * @inode: inode for this chardev - * @filp: file struct for storing private data + * @file: file struct for storing private data * Returns 0 on success */ -static int gpio_chrdev_open(struct inode *inode, struct file *filp) +static int gpio_chrdev_open(struct inode *inode, struct file *file) { struct gpio_device *gdev = container_of(inode->i_cdev, - struct gpio_device, chrdev); - struct gpio_chardev_data *priv; + struct gpio_device, chrdev); + struct gpio_chardev_data *cdev; int ret = -ENOMEM; /* Fail on open if the backing gpiochip is gone */ if (!gdev->chip) return -ENODEV; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) return -ENOMEM; - priv->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); - if (!priv->watched_lines) - goto out_free_priv; + cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); + if (!cdev->watched_lines) + goto out_free_cdev; - init_waitqueue_head(&priv->wait); - INIT_KFIFO(priv->events); - priv->gdev = gdev; + init_waitqueue_head(&cdev->wait); + INIT_KFIFO(cdev->events); + cdev->gdev = gdev; - priv->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; - ret = atomic_notifier_chain_register(&gdev->notifier, - &priv->lineinfo_changed_nb); + cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; + ret = blocking_notifier_chain_register(&gdev->notifier, + &cdev->lineinfo_changed_nb); if (ret) goto out_free_bitmap; get_device(&gdev->dev); - filp->private_data = priv; + file->private_data = cdev; - ret = nonseekable_open(inode, filp); + ret = nonseekable_open(inode, file); if (ret) goto out_unregister_notifier; return ret; out_unregister_notifier: - atomic_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); + blocking_notifier_chain_unregister(&gdev->notifier, + &cdev->lineinfo_changed_nb); out_free_bitmap: - bitmap_free(priv->watched_lines); -out_free_priv: - kfree(priv); + bitmap_free(cdev->watched_lines); +out_free_cdev: + kfree(cdev); return ret; } /** * gpio_chrdev_release() - close chardev after ioctl operations * @inode: inode for this chardev - * @filp: file struct for storing private data + * @file: file struct for storing private data * Returns 0 on success */ -static int gpio_chrdev_release(struct inode *inode, struct file *filp) +static int gpio_chrdev_release(struct inode *inode, struct file *file) { - struct gpio_chardev_data *priv = filp->private_data; - struct gpio_device *gdev = priv->gdev; + struct gpio_chardev_data *cdev = file->private_data; + struct gpio_device *gdev = cdev->gdev; - bitmap_free(priv->watched_lines); - atomic_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); + bitmap_free(cdev->watched_lines); + blocking_notifier_chain_unregister(&gdev->notifier, + &cdev->lineinfo_changed_nb); put_device(&gdev->dev); - kfree(priv); + kfree(cdev); return 0; } diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 82371fe2ccc6..728f6c687182 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -11,6 +11,7 @@ #include <linux/ctype.h> #include "gpiolib.h" +#include "gpiolib-sysfs.h" #define GPIO_IRQF_TRIGGER_FALLING BIT(0) #define GPIO_IRQF_TRIGGER_RISING BIT(1) diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h new file mode 100644 index 000000000000..ddd0e503f8eb --- /dev/null +++ b/drivers/gpio/gpiolib-sysfs.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef GPIOLIB_SYSFS_H +#define GPIOLIB_SYSFS_H + +#ifdef CONFIG_GPIO_SYSFS + +int gpiochip_sysfs_register(struct gpio_device *gdev); +void gpiochip_sysfs_unregister(struct gpio_device *gdev); + +#else + +static inline int gpiochip_sysfs_register(struct gpio_device *gdev) +{ + return 0; +} + +static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev) +{ +} + +#endif /* CONFIG_GPIO_SYSFS */ + +#endif /* GPIOLIB_SYSFS_H */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 291c088a5964..80137c1b3cdc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -26,6 +26,7 @@ #include "gpiolib-of.h" #include "gpiolib-acpi.h" #include "gpiolib-cdev.h" +#include "gpiolib-sysfs.h" #define CREATE_TRACE_POINTS #include <trace/events/gpio.h> @@ -614,7 +615,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, spin_unlock_irqrestore(&gpio_lock, flags); - ATOMIC_INIT_NOTIFIER_HEAD(&gdev->notifier); + BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier); #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); @@ -2048,8 +2049,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) } spin_unlock_irqrestore(&gpio_lock, flags); - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_RELEASED, desc); + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_RELEASED, desc); return ret; } @@ -3926,8 +3927,8 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, return ERR_PTR(ret); } - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_REQUESTED, desc); + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); return desc; } @@ -3994,8 +3995,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, return ERR_PTR(ret); } - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_REQUESTED, desc); + blocking_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); return desc; } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 9ed242316414..6709f79c02dd 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -56,7 +56,7 @@ struct gpio_device { const char *label; void *data; struct list_head list; - struct atomic_notifier_head notifier; + struct blocking_notifier_head notifier; #ifdef CONFIG_PINCTRL /* @@ -175,22 +175,4 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc) #define chip_dbg(gc, fmt, ...) \ dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#ifdef CONFIG_GPIO_SYSFS - -int gpiochip_sysfs_register(struct gpio_device *gdev); -void gpiochip_sysfs_unregister(struct gpio_device *gdev); - -#else - -static inline int gpiochip_sysfs_register(struct gpio_device *gdev) -{ - return 0; -} - -static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev) -{ -} - -#endif /* CONFIG_GPIO_SYSFS */ - #endif /* GPIOLIB_H */ diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h index 0206383c0383..9c27cecf406f 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -71,8 +71,8 @@ enum { * of a GPIO line * @info: updated line information * @timestamp: estimate of time of status change occurrence, in nanoseconds - * and GPIOLINE_CHANGED_CONFIG * @event_type: one of GPIOLINE_CHANGED_REQUESTED, GPIOLINE_CHANGED_RELEASED + * and GPIOLINE_CHANGED_CONFIG * * Note: struct gpioline_info embedded here has 32-bit alignment on its own, * but it works fine with 64-bit alignment too. With its 72 byte size, we can diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c index 30ed0e06f52a..1a303a81aeef 100644 --- a/tools/gpio/gpio-event-mon.c +++ b/tools/gpio/gpio-event-mon.c @@ -45,7 +45,7 @@ int monitor_device(const char *device_name, if (fd == -1) { ret = -errno; fprintf(stderr, "Failed to open %s\n", chrdev_name); - goto exit_close_error; + goto exit_free_name; } req.lineoffset = line; @@ -117,6 +117,7 @@ int monitor_device(const char *device_name, exit_close_error: if (close(fd) == -1) perror("Failed to close GPIO character device file"); +exit_free_name: free(chrdev_name); return ret; } diff --git a/tools/gpio/gpio-utils.c b/tools/gpio/gpio-utils.c index 06003789e7c7..16a5d9cb9da2 100644 --- a/tools/gpio/gpio-utils.c +++ b/tools/gpio/gpio-utils.c @@ -75,7 +75,7 @@ int gpiotools_request_linehandle(const char *device_name, unsigned int *lines, ret = -errno; fprintf(stderr, "Failed to open %s, %s\n", chrdev_name, strerror(errno)); - goto exit_close_error; + goto exit_free_name; } for (i = 0; i < nlines; i++) @@ -94,9 +94,9 @@ int gpiotools_request_linehandle(const char *device_name, unsigned int *lines, "GPIO_GET_LINEHANDLE_IOCTL", ret, strerror(errno)); } -exit_close_error: if (close(fd) == -1) perror("Failed to close GPIO character device file"); +exit_free_name: free(chrdev_name); return ret < 0 ? ret : req.fd; } diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c index 8a71ad36f83b..b08d7a5e779b 100644 --- a/tools/gpio/lsgpio.c +++ b/tools/gpio/lsgpio.c @@ -94,7 +94,7 @@ int list_device(const char *device_name) if (fd == -1) { ret = -errno; fprintf(stderr, "Failed to open %s\n", chrdev_name); - goto exit_close_error; + goto exit_free_name; } /* Inspect this GPIO chip */ @@ -141,6 +141,7 @@ int list_device(const char *device_name) exit_close_error: if (close(fd) == -1) perror("Failed to close GPIO character device file"); +exit_free_name: free(chrdev_name); return ret; } |