From 97cd738c44c85680d6c37f8583409ad2ef0ea17b Mon Sep 17 00:00:00 2001 From: Wang Qing Date: Fri, 12 Jun 2020 16:05:45 +0800 Subject: gpiolib: sysfs: use kobj_to_dev Use kobj_to_dev() API instead of container_of(). Signed-off-by: Wang Qing Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 23e3d335cd54..82371fe2ccc6 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -365,7 +365,7 @@ static DEVICE_ATTR_RW(active_low); static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; umode_t mode = attr->mode; -- cgit v1.2.3-58-ga151 From 3d5bfbd9716318b1ca5c38488aa69f64d38a9aa5 Mon Sep 17 00:00:00 2001 From: Song Hui Date: Thu, 11 Jun 2020 18:28:09 +0800 Subject: gpio: mpc8xxx: change the gpio interrupt flags. Delete the interrupt IRQF_NO_THREAD flags in order to gpio interrupts can be threaded to allow high-priority processes to preempt. Signed-off-by: Song Hui Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mpc8xxx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 604dfec353a1..1e866524a4bd 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -417,7 +417,7 @@ static int mpc8xxx_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, mpc8xxx_gc->irqn, mpc8xxx_gpio_irq_cascade, - IRQF_NO_THREAD | IRQF_SHARED, "gpio-cascade", + IRQF_SHARED, "gpio-cascade", mpc8xxx_gc); if (ret) { dev_err(&pdev->dev, "%s: failed to devm_request_irq(%d), ret = %d\n", -- cgit v1.2.3-58-ga151 From 3abda79aa12aba40964b593459866c312c513c23 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Tue, 9 Jun 2020 13:45:56 +0100 Subject: drivers: gpio: Fix trivial spelling The word 'descriptor' is misspelled throughout the tree. Fix it up accordingly: decriptors -> descriptors Signed-off-by: Kieran Bingham Signed-off-by: Bartosz Golaszewski --- drivers/gpio/TODO | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index b989c9352da2..e560e45e84f8 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -5,7 +5,7 @@ subsystem. GPIO descriptors Starting with commit 79a9becda894 the GPIO subsystem embarked on a journey -to move away from the global GPIO numberspace and toward a decriptor-based +to move away from the global GPIO numberspace and toward a descriptor-based approach. This means that GPIO consumers, drivers and machine descriptions ideally have no use or idea of the global GPIO numberspace that has/was used in the inception of the GPIO subsystem. -- cgit v1.2.3-58-ga151 From 3ba3ff5c0966280bfd1d40b05b1d7c089f45cc9b Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 15 Jun 2020 15:22:29 +0200 Subject: gpio: pca953x: Add support for the PCAL9535 The PCAL9535 is compatible to the PCA9535. Additionally, it comes with interrupt support and input latching. Other features are not supported by the GPIO subsystem. Datasheet: https://www.nxp.com/docs/en/data-sheet/PCAL9535A.pdf Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 1fca8dd7824f..cd5fb522563e 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -89,6 +89,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, }, + { "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "max7310", 8 | PCA953X_TYPE, }, @@ -1145,6 +1146,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), }, + { .compatible = "nxp,pcal9535", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "nxp,pcal9555a", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "maxim,max7310", .data = OF_953X( 8, 0), }, -- cgit v1.2.3-58-ga151 From 1f6bd574e9d575bd79bca8312dfd3d9345389940 Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Wed, 10 Jun 2020 07:36:30 -0400 Subject: gpio: max732x: remove redundant check The pdata is already checked for its validity. Remove this redundant check. Signed-off-by: Gaurav Singh Link: https://lore.kernel.org/r/20200610113630.11922-1-gaurav1086@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-max732x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 5fb0bcf31142..63472f308857 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -703,7 +703,7 @@ static int max732x_probe(struct i2c_client *client, if (ret) return ret; - if (pdata && pdata->setup) { + if (pdata->setup) { ret = pdata->setup(client, chip->gpio_chip.base, chip->gpio_chip.ngpio, pdata->context); if (ret < 0) -- cgit v1.2.3-58-ga151 From 925ca36913fc7dfee9d0bb7f36d81dd108a7b80f Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Tue, 16 Jun 2020 17:36:15 +0800 Subject: gpiolib: split character device into gpiolib-cdev Split the cdev specific functionality out of gpiolib.c and into gpiolib-cdev.c. This improves the readability and maintainability of both the cdev and core gpiolib code. Suggested-by: Bartosz Golaszewski Signed-off-by: Kent Gibson Link: https://lore.kernel.org/r/20200616093615.5167-1-warthog618@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/Makefile | 1 + drivers/gpio/gpiolib-cdev.c | 1154 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib-cdev.h | 11 + drivers/gpio/gpiolib.c | 1119 +---------------------------------------- 4 files changed, 1170 insertions(+), 1115 deletions(-) create mode 100644 drivers/gpio/gpiolib-cdev.c create mode 100644 drivers/gpio/gpiolib-cdev.h (limited to 'drivers/gpio') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1e4894e0bf0f..ef666cfef9d0 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIOLIB) += gpiolib-devres.o obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o obj-$(CONFIG_GPIOLIB) += gpiolib-devprop.o +obj-$(CONFIG_GPIOLIB) += gpiolib-cdev.o obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c new file mode 100644 index 000000000000..b8b872724628 --- /dev/null +++ b/drivers/gpio/gpiolib-cdev.c @@ -0,0 +1,1154 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpiolib.h" +#include "gpiolib-cdev.h" + +/* Character device interface to GPIO. + * + * The GPIO character device, /dev/gpiochipN, provides userspace an + * interface to gpiolib GPIOs via ioctl()s. + */ + +/* + * GPIO line handle management + */ + +/** + * struct linehandle_state - contains the state of a userspace handle + * @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 + */ +struct linehandle_state { + struct gpio_device *gdev; + const char *label; + struct gpio_desc *descs[GPIOHANDLES_MAX]; + u32 numdescs; +}; + +#define GPIOHANDLE_REQUEST_VALID_FLAGS \ + (GPIOHANDLE_REQUEST_INPUT | \ + GPIOHANDLE_REQUEST_OUTPUT | \ + GPIOHANDLE_REQUEST_ACTIVE_LOW | \ + GPIOHANDLE_REQUEST_BIAS_PULL_UP | \ + GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \ + GPIOHANDLE_REQUEST_BIAS_DISABLE | \ + GPIOHANDLE_REQUEST_OPEN_DRAIN | \ + GPIOHANDLE_REQUEST_OPEN_SOURCE) + +static int linehandle_validate_flags(u32 flags) +{ + /* Return an error if an unknown flag is set */ + if (flags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) + return -EINVAL; + + /* + * Do not allow both INPUT & OUTPUT flags to be set as they are + * contradictory. + */ + if ((flags & GPIOHANDLE_REQUEST_INPUT) && + (flags & GPIOHANDLE_REQUEST_OUTPUT)) + return -EINVAL; + + /* + * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If + * the hardware actually supports enabling both at the same time the + * electrical result would be disastrous. + */ + if ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && + (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) + return -EINVAL; + + /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ + if (!(flags & GPIOHANDLE_REQUEST_OUTPUT) && + ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || + (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) + return -EINVAL; + + /* Bias flags only allowed for input or output mode. */ + if (!((flags & GPIOHANDLE_REQUEST_INPUT) || + (flags & GPIOHANDLE_REQUEST_OUTPUT)) && + ((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) || + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) + return -EINVAL; + + /* Only one bias flag can be set. */ + if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && + (flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + ((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) + return -EINVAL; + + return 0; +} + +static long linehandle_set_config(struct linehandle_state *lh, + void __user *ip) +{ + struct gpiohandle_config gcnf; + struct gpio_desc *desc; + int i, ret; + u32 lflags; + unsigned long *flagsp; + + if (copy_from_user(&gcnf, ip, sizeof(gcnf))) + return -EFAULT; + + lflags = gcnf.flags; + ret = linehandle_validate_flags(lflags); + if (ret) + return ret; + + for (i = 0; i < lh->numdescs; 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); + + /* + * Lines have to be requested explicitly for input + * or output, else the line will be treated "as is". + */ + if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { + int val = !!gcnf.default_values[i]; + + ret = gpiod_direction_output(desc, val); + if (ret) + return ret; + } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { + ret = gpiod_direction_input(desc); + if (ret) + return ret; + } + + atomic_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_CONFIG, desc); + } + return 0; +} + +static long linehandle_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct linehandle_state *lh = filep->private_data; + void __user *ip = (void __user *)arg; + struct gpiohandle_data ghd; + DECLARE_BITMAP(vals, GPIOHANDLES_MAX); + int i; + + if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { + /* NOTE: It's ok to read values of output lines. */ + int ret = gpiod_get_array_value_complex(false, + true, + lh->numdescs, + lh->descs, + NULL, + vals); + if (ret) + return ret; + + memset(&ghd, 0, sizeof(ghd)); + for (i = 0; i < lh->numdescs; i++) + ghd.values[i] = test_bit(i, vals); + + if (copy_to_user(ip, &ghd, sizeof(ghd))) + return -EFAULT; + + return 0; + } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) { + /* + * All line descriptors were created at once with the same + * flags so just check if the first one is really output. + */ + if (!test_bit(FLAG_IS_OUT, &lh->descs[0]->flags)) + return -EPERM; + + if (copy_from_user(&ghd, ip, sizeof(ghd))) + return -EFAULT; + + /* Clamp all values to [0,1] */ + for (i = 0; i < lh->numdescs; 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); + } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) { + return linehandle_set_config(lh, ip); + } + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static int linehandle_release(struct inode *inode, struct file *filep) +{ + 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]); + kfree(lh->label); + kfree(lh); + put_device(&gdev->dev); + return 0; +} + +static const struct file_operations linehandle_fileops = { + .release = linehandle_release, + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = linehandle_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = linehandle_ioctl_compat, +#endif +}; + +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; + u32 lflags; + + if (copy_from_user(&handlereq, ip, sizeof(handlereq))) + return -EFAULT; + if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX)) + return -EINVAL; + + lflags = handlereq.flags; + + ret = linehandle_validate_flags(lflags); + if (ret) + return ret; + + lh = kzalloc(sizeof(*lh), GFP_KERNEL); + if (!lh) + return -ENOMEM; + lh->gdev = gdev; + get_device(&gdev->dev); + + /* Make sure this is terminated */ + handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0'; + if (strlen(handlereq.consumer_label)) { + lh->label = kstrdup(handlereq.consumer_label, + GFP_KERNEL); + if (!lh->label) { + ret = -ENOMEM; + goto out_free_lh; + } + } + + /* Request each GPIO */ + for (i = 0; i < handlereq.lines; i++) { + u32 offset = handlereq.lineoffsets[i]; + struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); + + if (IS_ERR(desc)) { + ret = PTR_ERR(desc); + goto out_free_descs; + } + + ret = gpiod_request(desc, lh->label); + if (ret) + goto out_free_descs; + 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); + + ret = gpiod_set_transitory(desc, false); + if (ret < 0) + goto out_free_descs; + + /* + * Lines have to be requested explicitly for input + * or output, else the line will be treated "as is". + */ + if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { + int val = !!handlereq.default_values[i]; + + ret = gpiod_direction_output(desc, val); + if (ret) + goto out_free_descs; + } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { + ret = gpiod_direction_input(desc); + if (ret) + goto out_free_descs; + } + + atomic_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; + } + + file = anon_inode_getfile("gpio-linehandle", + &linehandle_fileops, + lh, + O_RDONLY | O_CLOEXEC); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto out_put_unused_fd; + } + + handlereq.fd = fd; + if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { + /* + * fput() will trigger the release() callback, so do not go onto + * the regular error cleanup path here. + */ + fput(file); + put_unused_fd(fd); + return -EFAULT; + } + + fd_install(fd, file); + + dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", + lh->numdescs); + + 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); + return ret; +} + +/* + * GPIO line event management + */ + +/** + * struct lineevent_state - contains the state of a userspace event + * @gdev: the GPIO device the event pertains to + * @label: consumer label used to tag descriptors + * @desc: the GPIO descriptor held by this event + * @eflags: the event flags this line was requested with + * @irq: the interrupt that trigger in response to events on this GPIO + * @wait: wait queue that handles blocking reads of events + * @events: KFIFO for the GPIO events + * @timestamp: cache for the timestamp storing it between hardirq + * and IRQ thread, used to bring the timestamp close to the actual + * event + */ +struct lineevent_state { + struct gpio_device *gdev; + const char *label; + struct gpio_desc *desc; + u32 eflags; + int irq; + wait_queue_head_t wait; + DECLARE_KFIFO(events, struct gpioevent_data, 16); + u64 timestamp; +}; + +#define GPIOEVENT_REQUEST_VALID_FLAGS \ + (GPIOEVENT_REQUEST_RISING_EDGE | \ + GPIOEVENT_REQUEST_FALLING_EDGE) + +static __poll_t lineevent_poll(struct file *filep, + struct poll_table_struct *wait) +{ + struct lineevent_state *le = filep->private_data; + __poll_t events = 0; + + poll_wait(filep, &le->wait, wait); + + if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock)) + events = EPOLLIN | EPOLLRDNORM; + + return events; +} + + +static ssize_t lineevent_read(struct file *filep, + char __user *buf, + size_t count, + loff_t *f_ps) +{ + struct lineevent_state *le = filep->private_data; + struct gpioevent_data ge; + ssize_t bytes_read = 0; + int ret; + + if (count < sizeof(ge)) + return -EINVAL; + + do { + spin_lock(&le->wait.lock); + if (kfifo_is_empty(&le->events)) { + if (bytes_read) { + spin_unlock(&le->wait.lock); + return bytes_read; + } + + if (filep->f_flags & O_NONBLOCK) { + spin_unlock(&le->wait.lock); + return -EAGAIN; + } + + ret = wait_event_interruptible_locked(le->wait, + !kfifo_is_empty(&le->events)); + if (ret) { + spin_unlock(&le->wait.lock); + return ret; + } + } + + ret = kfifo_out(&le->events, &ge, 1); + spin_unlock(&le->wait.lock); + if (ret != 1) { + /* + * This should never happen - we were holding the lock + * from the moment we learned the fifo is no longer + * empty until now. + */ + ret = -EIO; + break; + } + + if (copy_to_user(buf + bytes_read, &ge, sizeof(ge))) + return -EFAULT; + bytes_read += sizeof(ge); + } while (count >= bytes_read + sizeof(ge)); + + return bytes_read; +} + +static int lineevent_release(struct inode *inode, struct file *filep) +{ + struct lineevent_state *le = filep->private_data; + struct gpio_device *gdev = le->gdev; + + free_irq(le->irq, le); + gpiod_free(le->desc); + kfree(le->label); + kfree(le); + put_device(&gdev->dev); + return 0; +} + +static long lineevent_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct lineevent_state *le = filep->private_data; + void __user *ip = (void __user *)arg; + struct gpiohandle_data ghd; + + /* + * We can get the value for an event line but not set it, + * because it is input by definition. + */ + if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { + int val; + + memset(&ghd, 0, sizeof(ghd)); + + val = gpiod_get_value_cansleep(le->desc); + if (val < 0) + return val; + ghd.values[0] = val; + + if (copy_to_user(ip, &ghd, sizeof(ghd))) + return -EFAULT; + + return 0; + } + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long lineevent_ioctl_compat(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + return lineevent_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static const struct file_operations lineevent_fileops = { + .release = lineevent_release, + .read = lineevent_read, + .poll = lineevent_poll, + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = lineevent_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = lineevent_ioctl_compat, +#endif +}; + +static irqreturn_t lineevent_irq_thread(int irq, void *p) +{ + struct lineevent_state *le = p; + struct gpioevent_data ge; + int ret; + + /* Do not leak kernel stack to userspace */ + memset(&ge, 0, sizeof(ge)); + + /* + * We may be running from a nested threaded interrupt in which case + * we didn't get the timestamp from lineevent_irq_handler(). + */ + if (!le->timestamp) + ge.timestamp = ktime_get_ns(); + else + ge.timestamp = le->timestamp; + + if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE + && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { + int level = gpiod_get_value_cansleep(le->desc); + + if (level) + /* Emit low-to-high event */ + ge.id = GPIOEVENT_EVENT_RISING_EDGE; + else + /* Emit high-to-low event */ + ge.id = GPIOEVENT_EVENT_FALLING_EDGE; + } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) { + /* Emit low-to-high event */ + ge.id = GPIOEVENT_EVENT_RISING_EDGE; + } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { + /* Emit high-to-low event */ + ge.id = GPIOEVENT_EVENT_FALLING_EDGE; + } else { + return IRQ_NONE; + } + + ret = kfifo_in_spinlocked_noirqsave(&le->events, &ge, + 1, &le->wait.lock); + if (ret) + wake_up_poll(&le->wait, EPOLLIN); + else + pr_debug_ratelimited("event FIFO is full - event dropped\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t lineevent_irq_handler(int irq, void *p) +{ + struct lineevent_state *le = p; + + /* + * Just store the timestamp in hardirq context so we get it as + * close in time as possible to the actual event. + */ + le->timestamp = ktime_get_ns(); + + return IRQ_WAKE_THREAD; +} + +static int lineevent_create(struct gpio_device *gdev, void __user *ip) +{ + struct gpioevent_request eventreq; + struct lineevent_state *le; + struct gpio_desc *desc; + struct file *file; + u32 offset; + u32 lflags; + u32 eflags; + int fd; + int ret; + int irqflags = 0; + + if (copy_from_user(&eventreq, ip, sizeof(eventreq))) + return -EFAULT; + + offset = eventreq.lineoffset; + lflags = eventreq.handleflags; + eflags = eventreq.eventflags; + + desc = gpiochip_get_desc(gdev->chip, offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + /* Return an error if a unknown flag is set */ + if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) || + (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) + return -EINVAL; + + /* This is just wrong: we don't look for events on output lines */ + if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) || + (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || + (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) + return -EINVAL; + + /* Only one bias flag can be set. */ + if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && + (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && + (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) + return -EINVAL; + + le = kzalloc(sizeof(*le), GFP_KERNEL); + if (!le) + return -ENOMEM; + le->gdev = gdev; + get_device(&gdev->dev); + + /* Make sure this is terminated */ + eventreq.consumer_label[sizeof(eventreq.consumer_label)-1] = '\0'; + if (strlen(eventreq.consumer_label)) { + le->label = kstrdup(eventreq.consumer_label, + GFP_KERNEL); + if (!le->label) { + ret = -ENOMEM; + goto out_free_le; + } + } + + ret = gpiod_request(desc, le->label); + if (ret) + goto out_free_label; + 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); + + ret = gpiod_direction_input(desc); + if (ret) + goto out_free_desc; + + atomic_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); + + le->irq = gpiod_to_irq(desc); + if (le->irq <= 0) { + ret = -ENODEV; + goto out_free_desc; + } + + if (eflags & GPIOEVENT_REQUEST_RISING_EDGE) + irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? + IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; + if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE) + irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; + irqflags |= IRQF_ONESHOT; + + INIT_KFIFO(le->events); + init_waitqueue_head(&le->wait); + + /* Request a thread to read the events */ + ret = request_threaded_irq(le->irq, + lineevent_irq_handler, + lineevent_irq_thread, + irqflags, + le->label, + le); + if (ret) + goto out_free_desc; + + fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); + if (fd < 0) { + ret = fd; + goto out_free_irq; + } + + file = anon_inode_getfile("gpio-event", + &lineevent_fileops, + le, + O_RDONLY | O_CLOEXEC); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto out_put_unused_fd; + } + + eventreq.fd = fd; + if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { + /* + * fput() will trigger the release() callback, so do not go onto + * the regular error cleanup path here. + */ + fput(file); + put_unused_fd(fd); + return -EFAULT; + } + + fd_install(fd, file); + + return 0; + +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); + return ret; +} + +static void gpio_desc_to_lineinfo(struct gpio_desc *desc, + struct gpioline_info *info) +{ + struct gpio_chip *gc = desc->gdev->chip; + bool ok_for_pinctrl; + unsigned long flags; + + /* + * This function takes a mutex so we must check this before taking + * the spinlock. + * + * FIXME: find a non-racy way to retrieve this information. Maybe a + * lock common to both frameworks? + */ + ok_for_pinctrl = + pinctrl_gpio_can_use_line(gc->base + info->line_offset); + + spin_lock_irqsave(&gpio_lock, flags); + + if (desc->name) { + strncpy(info->name, desc->name, sizeof(info->name)); + info->name[sizeof(info->name) - 1] = '\0'; + } else { + info->name[0] = '\0'; + } + + if (desc->label) { + strncpy(info->consumer, desc->label, sizeof(info->consumer)); + info->consumer[sizeof(info->consumer) - 1] = '\0'; + } else { + info->consumer[0] = '\0'; + } + + /* + * Userspace only need to know that the kernel is using this GPIO so + * it can't use it. + */ + info->flags = 0; + if (test_bit(FLAG_REQUESTED, &desc->flags) || + test_bit(FLAG_IS_HOGGED, &desc->flags) || + test_bit(FLAG_USED_AS_IRQ, &desc->flags) || + test_bit(FLAG_EXPORT, &desc->flags) || + test_bit(FLAG_SYSFS, &desc->flags) || + !ok_for_pinctrl) + info->flags |= GPIOLINE_FLAG_KERNEL; + if (test_bit(FLAG_IS_OUT, &desc->flags)) + info->flags |= GPIOLINE_FLAG_IS_OUT; + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + info->flags |= GPIOLINE_FLAG_ACTIVE_LOW; + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) + info->flags |= (GPIOLINE_FLAG_OPEN_DRAIN | + GPIOLINE_FLAG_IS_OUT); + if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) + info->flags |= (GPIOLINE_FLAG_OPEN_SOURCE | + GPIOLINE_FLAG_IS_OUT); + if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) + info->flags |= GPIOLINE_FLAG_BIAS_DISABLE; + if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + info->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; + if (test_bit(FLAG_PULL_UP, &desc->flags)) + info->flags |= GPIOLINE_FLAG_BIAS_PULL_UP; + + spin_unlock_irqrestore(&gpio_lock, flags); +} + +struct gpio_chardev_data { + struct gpio_device *gdev; + wait_queue_head_t wait; + DECLARE_KFIFO(events, struct gpioline_info_changed, 32); + struct notifier_block lineinfo_changed_nb; + unsigned long *watched_lines; +}; + +/* + * gpio_ioctl() - ioctl handler for the GPIO chardev + */ +static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct gpio_chardev_data *priv = filp->private_data; + struct gpio_device *gdev = priv->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) + return -ENODEV; + + /* Fill in the struct and pass to userspace */ + if (cmd == GPIO_GET_CHIPINFO_IOCTL) { + struct gpiochip_info chipinfo; + + memset(&chipinfo, 0, sizeof(chipinfo)); + + strncpy(chipinfo.name, dev_name(&gdev->dev), + sizeof(chipinfo.name)); + chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; + strncpy(chipinfo.label, gdev->label, + sizeof(chipinfo.label)); + chipinfo.label[sizeof(chipinfo.label)-1] = '\0'; + chipinfo.lines = gdev->ngpio; + if (copy_to_user(ip, &chipinfo, sizeof(chipinfo))) + return -EFAULT; + return 0; + } else if (cmd == GPIO_GET_LINEINFO_IOCTL) { + struct gpioline_info lineinfo; + + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + + 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))) + return -EFAULT; + return 0; + } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) { + return linehandle_create(gdev, ip); + } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) { + return lineevent_create(gdev, ip); + } else if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) { + struct gpioline_info lineinfo; + + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + + 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)) + return -EBUSY; + + gpio_desc_to_lineinfo(desc, &lineinfo); + + if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) + 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 (!test_bit(hwgpio, priv->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, + unsigned long arg) +{ + return gpio_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static struct gpio_chardev_data * +to_gpio_chardev_data(struct notifier_block *nb) +{ + return container_of(nb, struct gpio_chardev_data, lineinfo_changed_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 gpioline_info_changed chg; + struct gpio_desc *desc = data; + int ret; + + if (!test_bit(gpio_chip_hwgpio(desc), priv->watched_lines)) + return NOTIFY_DONE; + + memset(&chg, 0, sizeof(chg)); + chg.info.line_offset = gpio_chip_hwgpio(desc); + chg.event_type = action; + chg.timestamp = ktime_get_ns(); + gpio_desc_to_lineinfo(desc, &chg.info); + + ret = kfifo_in_spinlocked(&priv->events, &chg, 1, &priv->wait.lock); + if (ret) + wake_up_poll(&priv->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, + struct poll_table_struct *pollt) +{ + struct gpio_chardev_data *priv = filep->private_data; + __poll_t events = 0; + + poll_wait(filep, &priv->wait, pollt); + + if (!kfifo_is_empty_spinlocked_noirqsave(&priv->events, + &priv->wait.lock)) + events = EPOLLIN | EPOLLRDNORM; + + return events; +} + +static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf, + size_t count, loff_t *off) +{ + struct gpio_chardev_data *priv = filep->private_data; + struct gpioline_info_changed event; + ssize_t bytes_read = 0; + int ret; + + if (count < sizeof(event)) + return -EINVAL; + + do { + spin_lock(&priv->wait.lock); + if (kfifo_is_empty(&priv->events)) { + if (bytes_read) { + spin_unlock(&priv->wait.lock); + return bytes_read; + } + + if (filep->f_flags & O_NONBLOCK) { + spin_unlock(&priv->wait.lock); + return -EAGAIN; + } + + ret = wait_event_interruptible_locked(priv->wait, + !kfifo_is_empty(&priv->events)); + if (ret) { + spin_unlock(&priv->wait.lock); + return ret; + } + } + + ret = kfifo_out(&priv->events, &event, 1); + spin_unlock(&priv->wait.lock); + if (ret != 1) { + ret = -EIO; + break; + /* We should never get here. See lineevent_read(). */ + } + + if (copy_to_user(buf + bytes_read, &event, sizeof(event))) + return -EFAULT; + bytes_read += sizeof(event); + } while (count >= bytes_read + sizeof(event)); + + return bytes_read; +} + +/** + * gpio_chrdev_open() - open the chardev for ioctl operations + * @inode: inode for this chardev + * @filp: file struct for storing private data + * Returns 0 on success + */ +static int gpio_chrdev_open(struct inode *inode, struct file *filp) +{ + struct gpio_device *gdev = container_of(inode->i_cdev, + struct gpio_device, chrdev); + struct gpio_chardev_data *priv; + 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) + return -ENOMEM; + + priv->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); + if (!priv->watched_lines) + goto out_free_priv; + + init_waitqueue_head(&priv->wait); + INIT_KFIFO(priv->events); + priv->gdev = gdev; + + priv->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; + ret = atomic_notifier_chain_register(&gdev->notifier, + &priv->lineinfo_changed_nb); + if (ret) + goto out_free_bitmap; + + get_device(&gdev->dev); + filp->private_data = priv; + + ret = nonseekable_open(inode, filp); + if (ret) + goto out_unregister_notifier; + + return ret; + +out_unregister_notifier: + atomic_notifier_chain_unregister(&gdev->notifier, + &priv->lineinfo_changed_nb); +out_free_bitmap: + bitmap_free(priv->watched_lines); +out_free_priv: + kfree(priv); + return ret; +} + +/** + * gpio_chrdev_release() - close chardev after ioctl operations + * @inode: inode for this chardev + * @filp: file struct for storing private data + * Returns 0 on success + */ +static int gpio_chrdev_release(struct inode *inode, struct file *filp) +{ + struct gpio_chardev_data *priv = filp->private_data; + struct gpio_device *gdev = priv->gdev; + + bitmap_free(priv->watched_lines); + atomic_notifier_chain_unregister(&gdev->notifier, + &priv->lineinfo_changed_nb); + put_device(&gdev->dev); + kfree(priv); + + return 0; +} + +static const struct file_operations gpio_fileops = { + .release = gpio_chrdev_release, + .open = gpio_chrdev_open, + .poll = lineinfo_watch_poll, + .read = lineinfo_watch_read, + .owner = THIS_MODULE, + .llseek = no_llseek, + .unlocked_ioctl = gpio_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = gpio_ioctl_compat, +#endif +}; + +int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) +{ + int ret; + + cdev_init(&gdev->chrdev, &gpio_fileops); + gdev->chrdev.owner = THIS_MODULE; + gdev->dev.devt = MKDEV(MAJOR(devt), gdev->id); + + ret = cdev_device_add(&gdev->chrdev, &gdev->dev); + if (ret) + return ret; + + chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n", + MAJOR(devt), gdev->id); + + return 0; +} + +void gpiolib_cdev_unregister(struct gpio_device *gdev) +{ + cdev_device_del(&gdev->chrdev, &gdev->dev); +} diff --git a/drivers/gpio/gpiolib-cdev.h b/drivers/gpio/gpiolib-cdev.h new file mode 100644 index 000000000000..973578e7ad10 --- /dev/null +++ b/drivers/gpio/gpiolib-cdev.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef GPIOLIB_CDEV_H +#define GPIOLIB_CDEV_H + +#include + +int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt); +void gpiolib_cdev_unregister(struct gpio_device *gdev); + +#endif /* GPIOLIB_CDEV_H */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4fa075d49fbc..4abd751314a5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -17,20 +17,15 @@ #include #include #include -#include #include -#include #include -#include #include -#include -#include -#include #include #include "gpiolib.h" #include "gpiolib-of.h" #include "gpiolib-acpi.h" +#include "gpiolib-cdev.h" #define CREATE_TRACE_POINTS #include @@ -425,1105 +420,6 @@ bool gpiochip_line_is_valid(const struct gpio_chip *gc, } EXPORT_SYMBOL_GPL(gpiochip_line_is_valid); -/* - * GPIO line handle management - */ - -/** - * struct linehandle_state - contains the state of a userspace handle - * @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 - */ -struct linehandle_state { - struct gpio_device *gdev; - const char *label; - struct gpio_desc *descs[GPIOHANDLES_MAX]; - u32 numdescs; -}; - -#define GPIOHANDLE_REQUEST_VALID_FLAGS \ - (GPIOHANDLE_REQUEST_INPUT | \ - GPIOHANDLE_REQUEST_OUTPUT | \ - GPIOHANDLE_REQUEST_ACTIVE_LOW | \ - GPIOHANDLE_REQUEST_BIAS_PULL_UP | \ - GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \ - GPIOHANDLE_REQUEST_BIAS_DISABLE | \ - GPIOHANDLE_REQUEST_OPEN_DRAIN | \ - GPIOHANDLE_REQUEST_OPEN_SOURCE) - -static int linehandle_validate_flags(u32 flags) -{ - /* Return an error if an unknown flag is set */ - if (flags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) - return -EINVAL; - - /* - * Do not allow both INPUT & OUTPUT flags to be set as they are - * contradictory. - */ - if ((flags & GPIOHANDLE_REQUEST_INPUT) && - (flags & GPIOHANDLE_REQUEST_OUTPUT)) - return -EINVAL; - - /* - * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If - * the hardware actually supports enabling both at the same time the - * electrical result would be disastrous. - */ - if ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && - (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) - return -EINVAL; - - /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ - if (!(flags & GPIOHANDLE_REQUEST_OUTPUT) && - ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || - (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) - return -EINVAL; - - /* Bias flags only allowed for input or output mode. */ - if (!((flags & GPIOHANDLE_REQUEST_INPUT) || - (flags & GPIOHANDLE_REQUEST_OUTPUT)) && - ((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) || - (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || - (flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) - return -EINVAL; - - /* Only one bias flag can be set. */ - if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && - (flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | - GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || - ((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && - (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) - return -EINVAL; - - return 0; -} - -static long linehandle_set_config(struct linehandle_state *lh, - void __user *ip) -{ - struct gpiohandle_config gcnf; - struct gpio_desc *desc; - int i, ret; - u32 lflags; - unsigned long *flagsp; - - if (copy_from_user(&gcnf, ip, sizeof(gcnf))) - return -EFAULT; - - lflags = gcnf.flags; - ret = linehandle_validate_flags(lflags); - if (ret) - return ret; - - for (i = 0; i < lh->numdescs; 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); - - /* - * Lines have to be requested explicitly for input - * or output, else the line will be treated "as is". - */ - if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { - int val = !!gcnf.default_values[i]; - - ret = gpiod_direction_output(desc, val); - if (ret) - return ret; - } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { - ret = gpiod_direction_input(desc); - if (ret) - return ret; - } - - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_CONFIG, desc); - } - return 0; -} - -static long linehandle_ioctl(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - struct linehandle_state *lh = filep->private_data; - void __user *ip = (void __user *)arg; - struct gpiohandle_data ghd; - DECLARE_BITMAP(vals, GPIOHANDLES_MAX); - int i; - - if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { - /* NOTE: It's ok to read values of output lines. */ - int ret = gpiod_get_array_value_complex(false, - true, - lh->numdescs, - lh->descs, - NULL, - vals); - if (ret) - return ret; - - memset(&ghd, 0, sizeof(ghd)); - for (i = 0; i < lh->numdescs; i++) - ghd.values[i] = test_bit(i, vals); - - if (copy_to_user(ip, &ghd, sizeof(ghd))) - return -EFAULT; - - return 0; - } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) { - /* - * All line descriptors were created at once with the same - * flags so just check if the first one is really output. - */ - if (!test_bit(FLAG_IS_OUT, &lh->descs[0]->flags)) - return -EPERM; - - if (copy_from_user(&ghd, ip, sizeof(ghd))) - return -EFAULT; - - /* Clamp all values to [0,1] */ - for (i = 0; i < lh->numdescs; 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); - } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) { - return linehandle_set_config(lh, ip); - } - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static int linehandle_release(struct inode *inode, struct file *filep) -{ - 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]); - kfree(lh->label); - kfree(lh); - put_device(&gdev->dev); - return 0; -} - -static const struct file_operations linehandle_fileops = { - .release = linehandle_release, - .owner = THIS_MODULE, - .llseek = noop_llseek, - .unlocked_ioctl = linehandle_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = linehandle_ioctl_compat, -#endif -}; - -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; - u32 lflags; - - if (copy_from_user(&handlereq, ip, sizeof(handlereq))) - return -EFAULT; - if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX)) - return -EINVAL; - - lflags = handlereq.flags; - - ret = linehandle_validate_flags(lflags); - if (ret) - return ret; - - lh = kzalloc(sizeof(*lh), GFP_KERNEL); - if (!lh) - return -ENOMEM; - lh->gdev = gdev; - get_device(&gdev->dev); - - /* Make sure this is terminated */ - handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0'; - if (strlen(handlereq.consumer_label)) { - lh->label = kstrdup(handlereq.consumer_label, - GFP_KERNEL); - if (!lh->label) { - ret = -ENOMEM; - goto out_free_lh; - } - } - - /* Request each GPIO */ - for (i = 0; i < handlereq.lines; i++) { - u32 offset = handlereq.lineoffsets[i]; - struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); - - if (IS_ERR(desc)) { - ret = PTR_ERR(desc); - goto out_free_descs; - } - - ret = gpiod_request(desc, lh->label); - if (ret) - goto out_free_descs; - 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); - - ret = gpiod_set_transitory(desc, false); - if (ret < 0) - goto out_free_descs; - - /* - * Lines have to be requested explicitly for input - * or output, else the line will be treated "as is". - */ - if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { - int val = !!handlereq.default_values[i]; - - ret = gpiod_direction_output(desc, val); - if (ret) - goto out_free_descs; - } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { - ret = gpiod_direction_input(desc); - if (ret) - goto out_free_descs; - } - - atomic_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; - } - - file = anon_inode_getfile("gpio-linehandle", - &linehandle_fileops, - lh, - O_RDONLY | O_CLOEXEC); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto out_put_unused_fd; - } - - handlereq.fd = fd; - if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { - /* - * fput() will trigger the release() callback, so do not go onto - * the regular error cleanup path here. - */ - fput(file); - put_unused_fd(fd); - return -EFAULT; - } - - fd_install(fd, file); - - dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", - lh->numdescs); - - 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); - return ret; -} - -/* - * GPIO line event management - */ - -/** - * struct lineevent_state - contains the state of a userspace event - * @gdev: the GPIO device the event pertains to - * @label: consumer label used to tag descriptors - * @desc: the GPIO descriptor held by this event - * @eflags: the event flags this line was requested with - * @irq: the interrupt that trigger in response to events on this GPIO - * @wait: wait queue that handles blocking reads of events - * @events: KFIFO for the GPIO events - * @timestamp: cache for the timestamp storing it between hardirq - * and IRQ thread, used to bring the timestamp close to the actual - * event - */ -struct lineevent_state { - struct gpio_device *gdev; - const char *label; - struct gpio_desc *desc; - u32 eflags; - int irq; - wait_queue_head_t wait; - DECLARE_KFIFO(events, struct gpioevent_data, 16); - u64 timestamp; -}; - -#define GPIOEVENT_REQUEST_VALID_FLAGS \ - (GPIOEVENT_REQUEST_RISING_EDGE | \ - GPIOEVENT_REQUEST_FALLING_EDGE) - -static __poll_t lineevent_poll(struct file *filep, - struct poll_table_struct *wait) -{ - struct lineevent_state *le = filep->private_data; - __poll_t events = 0; - - poll_wait(filep, &le->wait, wait); - - if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock)) - events = EPOLLIN | EPOLLRDNORM; - - return events; -} - - -static ssize_t lineevent_read(struct file *filep, - char __user *buf, - size_t count, - loff_t *f_ps) -{ - struct lineevent_state *le = filep->private_data; - struct gpioevent_data ge; - ssize_t bytes_read = 0; - int ret; - - if (count < sizeof(ge)) - return -EINVAL; - - do { - spin_lock(&le->wait.lock); - if (kfifo_is_empty(&le->events)) { - if (bytes_read) { - spin_unlock(&le->wait.lock); - return bytes_read; - } - - if (filep->f_flags & O_NONBLOCK) { - spin_unlock(&le->wait.lock); - return -EAGAIN; - } - - ret = wait_event_interruptible_locked(le->wait, - !kfifo_is_empty(&le->events)); - if (ret) { - spin_unlock(&le->wait.lock); - return ret; - } - } - - ret = kfifo_out(&le->events, &ge, 1); - spin_unlock(&le->wait.lock); - if (ret != 1) { - /* - * This should never happen - we were holding the lock - * from the moment we learned the fifo is no longer - * empty until now. - */ - ret = -EIO; - break; - } - - if (copy_to_user(buf + bytes_read, &ge, sizeof(ge))) - return -EFAULT; - bytes_read += sizeof(ge); - } while (count >= bytes_read + sizeof(ge)); - - return bytes_read; -} - -static int lineevent_release(struct inode *inode, struct file *filep) -{ - struct lineevent_state *le = filep->private_data; - struct gpio_device *gdev = le->gdev; - - free_irq(le->irq, le); - gpiod_free(le->desc); - kfree(le->label); - kfree(le); - put_device(&gdev->dev); - return 0; -} - -static long lineevent_ioctl(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - struct lineevent_state *le = filep->private_data; - void __user *ip = (void __user *)arg; - struct gpiohandle_data ghd; - - /* - * We can get the value for an event line but not set it, - * because it is input by definition. - */ - if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { - int val; - - memset(&ghd, 0, sizeof(ghd)); - - val = gpiod_get_value_cansleep(le->desc); - if (val < 0) - return val; - ghd.values[0] = val; - - if (copy_to_user(ip, &ghd, sizeof(ghd))) - return -EFAULT; - - return 0; - } - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -static long lineevent_ioctl_compat(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - return lineevent_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static const struct file_operations lineevent_fileops = { - .release = lineevent_release, - .read = lineevent_read, - .poll = lineevent_poll, - .owner = THIS_MODULE, - .llseek = noop_llseek, - .unlocked_ioctl = lineevent_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = lineevent_ioctl_compat, -#endif -}; - -static irqreturn_t lineevent_irq_thread(int irq, void *p) -{ - struct lineevent_state *le = p; - struct gpioevent_data ge; - int ret; - - /* Do not leak kernel stack to userspace */ - memset(&ge, 0, sizeof(ge)); - - /* - * We may be running from a nested threaded interrupt in which case - * we didn't get the timestamp from lineevent_irq_handler(). - */ - if (!le->timestamp) - ge.timestamp = ktime_get_ns(); - else - ge.timestamp = le->timestamp; - - if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE - && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { - int level = gpiod_get_value_cansleep(le->desc); - if (level) - /* Emit low-to-high event */ - ge.id = GPIOEVENT_EVENT_RISING_EDGE; - else - /* Emit high-to-low event */ - ge.id = GPIOEVENT_EVENT_FALLING_EDGE; - } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) { - /* Emit low-to-high event */ - ge.id = GPIOEVENT_EVENT_RISING_EDGE; - } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { - /* Emit high-to-low event */ - ge.id = GPIOEVENT_EVENT_FALLING_EDGE; - } else { - return IRQ_NONE; - } - - ret = kfifo_in_spinlocked_noirqsave(&le->events, &ge, - 1, &le->wait.lock); - if (ret) - wake_up_poll(&le->wait, EPOLLIN); - else - pr_debug_ratelimited("event FIFO is full - event dropped\n"); - - return IRQ_HANDLED; -} - -static irqreturn_t lineevent_irq_handler(int irq, void *p) -{ - struct lineevent_state *le = p; - - /* - * Just store the timestamp in hardirq context so we get it as - * close in time as possible to the actual event. - */ - le->timestamp = ktime_get_ns(); - - return IRQ_WAKE_THREAD; -} - -static int lineevent_create(struct gpio_device *gdev, void __user *ip) -{ - struct gpioevent_request eventreq; - struct lineevent_state *le; - struct gpio_desc *desc; - struct file *file; - u32 offset; - u32 lflags; - u32 eflags; - int fd; - int ret; - int irqflags = 0; - - if (copy_from_user(&eventreq, ip, sizeof(eventreq))) - return -EFAULT; - - offset = eventreq.lineoffset; - lflags = eventreq.handleflags; - eflags = eventreq.eventflags; - - desc = gpiochip_get_desc(gdev->chip, offset); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - /* Return an error if a unknown flag is set */ - if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) || - (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) - return -EINVAL; - - /* This is just wrong: we don't look for events on output lines */ - if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) || - (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || - (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) - return -EINVAL; - - /* Only one bias flag can be set. */ - if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && - (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | - GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || - ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && - (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) - return -EINVAL; - - le = kzalloc(sizeof(*le), GFP_KERNEL); - if (!le) - return -ENOMEM; - le->gdev = gdev; - get_device(&gdev->dev); - - /* Make sure this is terminated */ - eventreq.consumer_label[sizeof(eventreq.consumer_label)-1] = '\0'; - if (strlen(eventreq.consumer_label)) { - le->label = kstrdup(eventreq.consumer_label, - GFP_KERNEL); - if (!le->label) { - ret = -ENOMEM; - goto out_free_le; - } - } - - ret = gpiod_request(desc, le->label); - if (ret) - goto out_free_label; - 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); - - ret = gpiod_direction_input(desc); - if (ret) - goto out_free_desc; - - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_REQUESTED, desc); - - le->irq = gpiod_to_irq(desc); - if (le->irq <= 0) { - ret = -ENODEV; - goto out_free_desc; - } - - if (eflags & GPIOEVENT_REQUEST_RISING_EDGE) - irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? - IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; - if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE) - irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? - IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; - irqflags |= IRQF_ONESHOT; - - INIT_KFIFO(le->events); - init_waitqueue_head(&le->wait); - - /* Request a thread to read the events */ - ret = request_threaded_irq(le->irq, - lineevent_irq_handler, - lineevent_irq_thread, - irqflags, - le->label, - le); - if (ret) - goto out_free_desc; - - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - ret = fd; - goto out_free_irq; - } - - file = anon_inode_getfile("gpio-event", - &lineevent_fileops, - le, - O_RDONLY | O_CLOEXEC); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto out_put_unused_fd; - } - - eventreq.fd = fd; - if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { - /* - * fput() will trigger the release() callback, so do not go onto - * the regular error cleanup path here. - */ - fput(file); - put_unused_fd(fd); - return -EFAULT; - } - - fd_install(fd, file); - - return 0; - -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); - return ret; -} - -static void gpio_desc_to_lineinfo(struct gpio_desc *desc, - struct gpioline_info *info) -{ - struct gpio_chip *gc = desc->gdev->chip; - bool ok_for_pinctrl; - unsigned long flags; - - /* - * This function takes a mutex so we must check this before taking - * the spinlock. - * - * FIXME: find a non-racy way to retrieve this information. Maybe a - * lock common to both frameworks? - */ - ok_for_pinctrl = - pinctrl_gpio_can_use_line(gc->base + info->line_offset); - - spin_lock_irqsave(&gpio_lock, flags); - - if (desc->name) { - strncpy(info->name, desc->name, sizeof(info->name)); - info->name[sizeof(info->name) - 1] = '\0'; - } else { - info->name[0] = '\0'; - } - - if (desc->label) { - strncpy(info->consumer, desc->label, sizeof(info->consumer)); - info->consumer[sizeof(info->consumer) - 1] = '\0'; - } else { - info->consumer[0] = '\0'; - } - - /* - * Userspace only need to know that the kernel is using this GPIO so - * it can't use it. - */ - info->flags = 0; - if (test_bit(FLAG_REQUESTED, &desc->flags) || - test_bit(FLAG_IS_HOGGED, &desc->flags) || - test_bit(FLAG_USED_AS_IRQ, &desc->flags) || - test_bit(FLAG_EXPORT, &desc->flags) || - test_bit(FLAG_SYSFS, &desc->flags) || - !ok_for_pinctrl) - info->flags |= GPIOLINE_FLAG_KERNEL; - if (test_bit(FLAG_IS_OUT, &desc->flags)) - info->flags |= GPIOLINE_FLAG_IS_OUT; - if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) - info->flags |= GPIOLINE_FLAG_ACTIVE_LOW; - if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - info->flags |= (GPIOLINE_FLAG_OPEN_DRAIN | - GPIOLINE_FLAG_IS_OUT); - if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - info->flags |= (GPIOLINE_FLAG_OPEN_SOURCE | - GPIOLINE_FLAG_IS_OUT); - if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_DISABLE; - if (test_bit(FLAG_PULL_DOWN, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; - if (test_bit(FLAG_PULL_UP, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_PULL_UP; - - spin_unlock_irqrestore(&gpio_lock, flags); -} - -struct gpio_chardev_data { - struct gpio_device *gdev; - wait_queue_head_t wait; - DECLARE_KFIFO(events, struct gpioline_info_changed, 32); - struct notifier_block lineinfo_changed_nb; - unsigned long *watched_lines; -}; - -/* - * gpio_ioctl() - ioctl handler for the GPIO chardev - */ -static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct gpio_chardev_data *priv = filp->private_data; - struct gpio_device *gdev = priv->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) - return -ENODEV; - - /* Fill in the struct and pass to userspace */ - if (cmd == GPIO_GET_CHIPINFO_IOCTL) { - struct gpiochip_info chipinfo; - - memset(&chipinfo, 0, sizeof(chipinfo)); - - strncpy(chipinfo.name, dev_name(&gdev->dev), - sizeof(chipinfo.name)); - chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; - strncpy(chipinfo.label, gdev->label, - sizeof(chipinfo.label)); - chipinfo.label[sizeof(chipinfo.label)-1] = '\0'; - chipinfo.lines = gdev->ngpio; - if (copy_to_user(ip, &chipinfo, sizeof(chipinfo))) - return -EFAULT; - return 0; - } else if (cmd == GPIO_GET_LINEINFO_IOCTL) { - struct gpioline_info lineinfo; - - if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) - return -EFAULT; - - 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))) - return -EFAULT; - return 0; - } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) { - return linehandle_create(gdev, ip); - } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) { - return lineevent_create(gdev, ip); - } else if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) { - struct gpioline_info lineinfo; - - if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) - return -EFAULT; - - 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)) - return -EBUSY; - - gpio_desc_to_lineinfo(desc, &lineinfo); - - if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) - 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 (!test_bit(hwgpio, priv->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, - unsigned long arg) -{ - return gpio_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static struct gpio_chardev_data * -to_gpio_chardev_data(struct notifier_block *nb) -{ - return container_of(nb, struct gpio_chardev_data, lineinfo_changed_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 gpioline_info_changed chg; - struct gpio_desc *desc = data; - int ret; - - if (!test_bit(gpio_chip_hwgpio(desc), priv->watched_lines)) - return NOTIFY_DONE; - - memset(&chg, 0, sizeof(chg)); - chg.info.line_offset = gpio_chip_hwgpio(desc); - chg.event_type = action; - chg.timestamp = ktime_get_ns(); - gpio_desc_to_lineinfo(desc, &chg.info); - - ret = kfifo_in_spinlocked(&priv->events, &chg, 1, &priv->wait.lock); - if (ret) - wake_up_poll(&priv->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, - struct poll_table_struct *pollt) -{ - struct gpio_chardev_data *priv = filep->private_data; - __poll_t events = 0; - - poll_wait(filep, &priv->wait, pollt); - - if (!kfifo_is_empty_spinlocked_noirqsave(&priv->events, - &priv->wait.lock)) - events = EPOLLIN | EPOLLRDNORM; - - return events; -} - -static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf, - size_t count, loff_t *off) -{ - struct gpio_chardev_data *priv = filep->private_data; - struct gpioline_info_changed event; - ssize_t bytes_read = 0; - int ret; - - if (count < sizeof(event)) - return -EINVAL; - - do { - spin_lock(&priv->wait.lock); - if (kfifo_is_empty(&priv->events)) { - if (bytes_read) { - spin_unlock(&priv->wait.lock); - return bytes_read; - } - - if (filep->f_flags & O_NONBLOCK) { - spin_unlock(&priv->wait.lock); - return -EAGAIN; - } - - ret = wait_event_interruptible_locked(priv->wait, - !kfifo_is_empty(&priv->events)); - if (ret) { - spin_unlock(&priv->wait.lock); - return ret; - } - } - - ret = kfifo_out(&priv->events, &event, 1); - spin_unlock(&priv->wait.lock); - if (ret != 1) { - ret = -EIO; - break; - /* We should never get here. See lineevent_read(). */ - } - - if (copy_to_user(buf + bytes_read, &event, sizeof(event))) - return -EFAULT; - bytes_read += sizeof(event); - } while (count >= bytes_read + sizeof(event)); - - return bytes_read; -} - -/** - * gpio_chrdev_open() - open the chardev for ioctl operations - * @inode: inode for this chardev - * @filp: file struct for storing private data - * Returns 0 on success - */ -static int gpio_chrdev_open(struct inode *inode, struct file *filp) -{ - struct gpio_device *gdev = container_of(inode->i_cdev, - struct gpio_device, chrdev); - struct gpio_chardev_data *priv; - 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) - return -ENOMEM; - - priv->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); - if (!priv->watched_lines) - goto out_free_priv; - - init_waitqueue_head(&priv->wait); - INIT_KFIFO(priv->events); - priv->gdev = gdev; - - priv->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; - ret = atomic_notifier_chain_register(&gdev->notifier, - &priv->lineinfo_changed_nb); - if (ret) - goto out_free_bitmap; - - get_device(&gdev->dev); - filp->private_data = priv; - - ret = nonseekable_open(inode, filp); - if (ret) - goto out_unregister_notifier; - - return ret; - -out_unregister_notifier: - atomic_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); -out_free_bitmap: - bitmap_free(priv->watched_lines); -out_free_priv: - kfree(priv); - return ret; -} - -/** - * gpio_chrdev_release() - close chardev after ioctl operations - * @inode: inode for this chardev - * @filp: file struct for storing private data - * Returns 0 on success - */ -static int gpio_chrdev_release(struct inode *inode, struct file *filp) -{ - struct gpio_chardev_data *priv = filp->private_data; - struct gpio_device *gdev = priv->gdev; - - bitmap_free(priv->watched_lines); - atomic_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); - put_device(&gdev->dev); - kfree(priv); - - return 0; -} - -static const struct file_operations gpio_fileops = { - .release = gpio_chrdev_release, - .open = gpio_chrdev_open, - .poll = lineinfo_watch_poll, - .read = lineinfo_watch_read, - .owner = THIS_MODULE, - .llseek = no_llseek, - .unlocked_ioctl = gpio_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = gpio_ioctl_compat, -#endif -}; - static void gpiodevice_release(struct device *dev) { struct gpio_device *gdev = dev_get_drvdata(dev); @@ -1539,17 +435,10 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) { int ret; - cdev_init(&gdev->chrdev, &gpio_fileops); - gdev->chrdev.owner = THIS_MODULE; - gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id); - - ret = cdev_device_add(&gdev->chrdev, &gdev->dev); + ret = gpiolib_cdev_register(gdev, gpio_devt); if (ret) return ret; - chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n", - MAJOR(gpio_devt), gdev->id); - ret = gpiochip_sysfs_register(gdev); if (ret) goto err_remove_device; @@ -1562,7 +451,7 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) return 0; err_remove_device: - cdev_device_del(&gdev->chrdev, &gdev->dev); + gpiolib_cdev_unregister(gdev); return ret; } @@ -1884,7 +773,7 @@ void gpiochip_remove(struct gpio_chip *gc) * be removed, else it will be dangling until the last user is * gone. */ - cdev_device_del(&gdev->chrdev, &gdev->dev); + gpiolib_cdev_unregister(gdev); put_device(&gdev->dev); } EXPORT_SYMBOL_GPL(gpiochip_remove); -- cgit v1.2.3-58-ga151 From 86661fd7faacbb9a5a4d15755551cec43f6a3a14 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 15 Jun 2020 18:05:43 +0300 Subject: gpio: mvebu: Make use of for_each_requested_gpio() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of for_each_requested_gpio() instead of home grown analogue. Signed-off-by: Andy Shevchenko Cc: Thierry Reding Cc: "Uwe Kleine-König" Cc: Lee Jones Link: https://lore.kernel.org/r/20200615150545.87964-4-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mvebu.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index bd65114eb170..433e2c3f3fd5 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -846,6 +846,7 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk; + const char *label; int i; regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &out); @@ -857,15 +858,10 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) edg_msk = mvebu_gpio_read_edge_mask(mvchip); lvl_msk = mvebu_gpio_read_level_mask(mvchip); - for (i = 0; i < chip->ngpio; i++) { - const char *label; + for_each_requested_gpio(chip, i, label) { u32 msk; bool is_out; - label = gpiochip_is_requested(chip, i); - if (!label) - continue; - msk = BIT(i); is_out = !(io_conf & msk); -- cgit v1.2.3-58-ga151 From 7796cdc911fc584a49ee8cfbe53baa00e57267d3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 15 Jun 2020 18:05:44 +0300 Subject: gpio: xra1403: Make use of for_each_requested_gpio() Make use of for_each_requested_gpio() instead of home grown analogue. Signed-off-by: Andy Shevchenko Cc: Nandor Han Cc: Semi Malinen Link: https://lore.kernel.org/r/20200615150545.87964-5-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xra1403.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index 31b5072b2df0..e2cac12092af 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -121,6 +121,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip) struct xra1403 *xra = gpiochip_get_data(chip); int value[XRA_LAST]; int i; + const char *label; unsigned int gcr; unsigned int gsr; @@ -136,12 +137,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip) gcr = value[XRA_GCR + 1] << 8 | value[XRA_GCR]; gsr = value[XRA_GSR + 1] << 8 | value[XRA_GSR]; - for (i = 0; i < chip->ngpio; i++) { - const char *label = gpiochip_is_requested(chip, i); - - if (!label) - continue; - + for_each_requested_gpio(chip, i, label) { seq_printf(s, " gpio-%-3d (%-12s) %s %s\n", chip->base + i, label, (gcr & BIT(i)) ? "in" : "out", -- cgit v1.2.3-58-ga151 From 8346b92ecddfae624b1dd4631e840fb98f5430cd Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Sat, 20 Jun 2020 18:40:53 -0400 Subject: gpio: max732x: remove redundant check from probe() The pdata is already checked for its validity. Remove the redundant check. Signed-off-by: Gaurav Singh Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-max732x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 5fb0bcf31142..63472f308857 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -703,7 +703,7 @@ static int max732x_probe(struct i2c_client *client, if (ret) return ret; - if (pdata && pdata->setup) { + if (pdata->setup) { ret = pdata->setup(client, chip->gpio_chip.base, chip->gpio_chip.ngpio, pdata->context); if (ret < 0) -- cgit v1.2.3-58-ga151 From fdcfec11b215b3bbc9c20240739cf76be921cca1 Mon Sep 17 00:00:00 2001 From: Glenn Langedock Date: Wed, 17 Jun 2020 17:07:21 +0530 Subject: gpio: zynq: protect direction in/out with a spinlock Fix race condition when changing the direction (in/out) of the GPIO pin. The read-modify-write sequence (as coded in the driver) isn't atomic and requires synchronization (spinlock). Signed-off-by: Glenn Langedock Signed-off-by: Michal Simek Signed-off-by: Srinivas Neeli Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zynq.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 05ba16fffdad..fb93b35ab19e 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +117,7 @@ struct gpio_regs { * @irq: interrupt for the GPIO device * @p_data: pointer to platform data * @context: context registers + * @dirlock: lock used for direction in/out synchronization */ struct zynq_gpio { struct gpio_chip chip; @@ -124,6 +126,7 @@ struct zynq_gpio { int irq; const struct zynq_platform_data *p_data; struct gpio_regs context; + spinlock_t dirlock; /* lock */ }; /** @@ -297,6 +300,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) { u32 reg; unsigned int bank_num, bank_pin_num; + unsigned long flags; struct zynq_gpio *gpio = gpiochip_get_data(chip); zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); @@ -310,9 +314,11 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) return -EINVAL; /* clear the bit in direction mode reg to set the pin as input */ + spin_lock_irqsave(&gpio->dirlock, flags); reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); reg &= ~BIT(bank_pin_num); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + spin_unlock_irqrestore(&gpio->dirlock, flags); return 0; } @@ -334,11 +340,13 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, { u32 reg; unsigned int bank_num, bank_pin_num; + unsigned long flags; struct zynq_gpio *gpio = gpiochip_get_data(chip); zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); /* set the GPIO pin as output */ + spin_lock_irqsave(&gpio->dirlock, flags); reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); reg |= BIT(bank_pin_num); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); @@ -347,6 +355,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); reg |= BIT(bank_pin_num); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); + spin_unlock_irqrestore(&gpio->dirlock, flags); /* set the state of the pin */ zynq_gpio_set_value(chip, pin, state); @@ -885,6 +894,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) return ret; } + spin_lock_init(&gpio->dirlock); + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); -- cgit v1.2.3-58-ga151 From 675002448eee5a9f96bfeb9b057e12a60a004b6c Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 17 Jun 2020 17:07:24 +0530 Subject: gpio: zynq: Add Versal support Add Versal support in gpio. Only bank 0 and 3 are connected to the Multiplexed Input output pins. Bank 0 to mio and bank3 to fabric Multiplexed input output pins. Versal devices are the industry's first adaptive compute acceleration platforms. https://www.xilinx.com/support/documentation/data_sheets/ds950-versal-overview.pdf On the Versal platform, we are using two customized GPIO controllers(IP) which were used in Zynq/ZynqMp platform. One of them present in the Platform Management Controller(PMC) block and other in Processing System(PS) block. In PMC_GPIO only Bank0,1,3 & 4 are enabled and in PS_GPIO only Bank 0 & 3 are enabled. You can find more details of GPIO IP in ZynqMP TRM General Purpose I/O(Chapter-27). https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf Signed-off-by: Shubhrajyoti Datta Signed-off-by: Michal Simek Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zynq.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index fb93b35ab19e..9ac69144a0eb 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -22,6 +22,8 @@ /* Maximum banks */ #define ZYNQ_GPIO_MAX_BANK 4 #define ZYNQMP_GPIO_MAX_BANK 6 +#define VERSAL_GPIO_MAX_BANK 4 +#define VERSAL_UNUSED_BANKS 2 #define ZYNQ_GPIO_BANK0_NGPIO 32 #define ZYNQ_GPIO_BANK1_NGPIO 22 @@ -96,6 +98,7 @@ /* set to differentiate zynq from zynqmp, 0=zynqmp, 1=zynq */ #define ZYNQ_GPIO_QUIRK_IS_ZYNQ BIT(0) #define GPIO_QUIRK_DATA_RO_BUG BIT(1) +#define GPIO_QUIRK_VERSAL BIT(2) struct gpio_regs { u32 datamsw[ZYNQMP_GPIO_MAX_BANK]; @@ -199,6 +202,8 @@ static inline void zynq_gpio_get_bank_pin(unsigned int pin_num, gpio->p_data->bank_min[bank]; return; } + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank = bank + VERSAL_UNUSED_BANKS; } /* default */ @@ -656,6 +661,8 @@ static void zynq_gpio_irqhandler(struct irq_desc *desc) int_enb = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_INTMASK_OFFSET(bank_num)); zynq_gpio_handle_bank_irq(gpio, bank_num, int_sts & ~int_enb); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; } chained_irq_exit(irqchip, desc); @@ -685,6 +692,8 @@ static void zynq_gpio_save_context(struct zynq_gpio *gpio) gpio->context.int_any[bank_num] = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_INTANY_OFFSET(bank_num)); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; } } @@ -716,6 +725,8 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio) writel_relaxed(~(gpio->context.int_en[bank_num]), gpio->base_addr + ZYNQ_GPIO_INTEN_OFFSET(bank_num)); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; } } @@ -787,6 +798,17 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = { zynq_gpio_runtime_resume, NULL) }; +static const struct zynq_platform_data versal_gpio_def = { + .label = "versal_gpio", + .quirks = GPIO_QUIRK_VERSAL, + .ngpio = 58, + .max_bank = VERSAL_GPIO_MAX_BANK, + .bank_min[0] = 0, + .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */ + .bank_min[3] = 26, + .bank_max[3] = 57, /* Bank 3 is connected to FMIOs (32 pins) */ +}; + static const struct zynq_platform_data zynqmp_gpio_def = { .label = "zynqmp_gpio", .quirks = GPIO_QUIRK_DATA_RO_BUG, @@ -824,6 +846,7 @@ static const struct zynq_platform_data zynq_gpio_def = { static const struct of_device_id zynq_gpio_of_match[] = { { .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def }, { .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def }, + { .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def }, { /* end of table */ } }; MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); @@ -903,9 +926,12 @@ static int zynq_gpio_probe(struct platform_device *pdev) goto err_pm_dis; /* disable interrupts for all banks */ - for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) + for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) { writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr + ZYNQ_GPIO_INTDIS_OFFSET(bank_num)); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; + } /* Set up the GPIO irqchip */ girq = &chip->irq; -- cgit v1.2.3-58-ga151 From 26ebdbf8c2e311759d88c0b0cf26126715e932c6 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 17 Jun 2020 17:07:25 +0530 Subject: gpio: zynq: Disable the irq if it is not a wakeup source If gpio is not set to wake disable the interrupt. ATF set all slaves with enabled interrupts as wakeup sources and if gpio is used in r5 then it wakes up linux. Signed-off-by: Shubhrajyoti Datta Signed-off-by: Michal Simek Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zynq.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 9ac69144a0eb..b6261a3aec55 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -735,6 +735,9 @@ static int __maybe_unused zynq_gpio_suspend(struct device *dev) struct zynq_gpio *gpio = dev_get_drvdata(dev); struct irq_data *data = irq_get_irq_data(gpio->irq); + if (!device_may_wakeup(dev)) + disable_irq(gpio->irq); + if (!irqd_is_wakeup_set(data)) { zynq_gpio_save_context(gpio); return pm_runtime_force_suspend(dev); @@ -749,6 +752,9 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev) struct irq_data *data = irq_get_irq_data(gpio->irq); int ret; + if (!device_may_wakeup(dev)) + enable_irq(gpio->irq); + if (!irqd_is_wakeup_set(data)) { ret = pm_runtime_force_resume(dev); zynq_gpio_restore_context(gpio); @@ -956,6 +962,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) goto err_pm_put; } + irq_set_status_flags(gpio->irq, IRQ_DISABLE_UNLAZY); + device_init_wakeup(&pdev->dev, 1); pm_runtime_put(&pdev->dev); return 0; -- cgit v1.2.3-58-ga151 From 73c612fe2a5f819bc54598b5babc630528bf29d1 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 17 Jun 2020 17:07:26 +0530 Subject: gpio: zynq: Add pmc gpio support Add PMC gpio support. Only bank 0,1, 3 and 4 are connected to the multiplexed Input output pins. Bank 0 and 1 to mio and bank 3 and 4 to extended multiplexed input output pins. Versal devices are the industry's first adaptive compute acceleration platforms. https://www.xilinx.com/support/documentation/data_sheets/ds950-versal-overview.pdf On the Versal platform, we are using two customized GPIO controllers(IP) which were used in Zynq/ZynqMp platform. One of them present in the Platform Management Controller(PMC) block and other in Processing System(PS) block. In PMC_GPIO only Bank0,1,3 & 4 are enabled and in PS_GPIO only Bank 0 & 3 are enabled. You can find more details of GPIO IP in ZynqMP TRM General Purpose I/O(Chapter-27). https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf Signed-off-by: Shubhrajyoti Datta Signed-off-by: Michal Simek Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zynq.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index b6261a3aec55..2ddb59b242e7 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -23,6 +23,7 @@ #define ZYNQ_GPIO_MAX_BANK 4 #define ZYNQMP_GPIO_MAX_BANK 6 #define VERSAL_GPIO_MAX_BANK 4 +#define PMC_GPIO_MAX_BANK 5 #define VERSAL_UNUSED_BANKS 2 #define ZYNQ_GPIO_BANK0_NGPIO 32 @@ -815,6 +816,20 @@ static const struct zynq_platform_data versal_gpio_def = { .bank_max[3] = 57, /* Bank 3 is connected to FMIOs (32 pins) */ }; +static const struct zynq_platform_data pmc_gpio_def = { + .label = "pmc_gpio", + .ngpio = 116, + .max_bank = PMC_GPIO_MAX_BANK, + .bank_min[0] = 0, + .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */ + .bank_min[1] = 26, + .bank_max[1] = 51, /* Bank 1 are connected to MIOs (26 pins) */ + .bank_min[3] = 52, + .bank_max[3] = 83, /* Bank 3 is connected to EMIOs (32 pins) */ + .bank_min[4] = 84, + .bank_max[4] = 115, /* Bank 4 is connected to EMIOs (32 pins) */ +}; + static const struct zynq_platform_data zynqmp_gpio_def = { .label = "zynqmp_gpio", .quirks = GPIO_QUIRK_DATA_RO_BUG, @@ -853,6 +868,7 @@ static const struct of_device_id zynq_gpio_of_match[] = { { .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def }, { .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def }, { .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def }, + { .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def }, { /* end of table */ } }; MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); -- cgit v1.2.3-58-ga151 From 0077428118204d934382dcb74569899ac3dd2f58 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 17 Jun 2020 17:07:27 +0530 Subject: gpio: zynq: Remove error prints in EPROBE_DEFER In case of probe is deferred do not print the errors. Signed-off-by: Shubhrajyoti Datta Signed-off-by: Michal Simek Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zynq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 2ddb59b242e7..53d1387592fd 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -930,7 +930,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) /* Retrieve GPIO clock */ gpio->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(gpio->clk)) { - dev_err(&pdev->dev, "input clock not found.\n"); + if (PTR_ERR(gpio->clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "input clock not found.\n"); return PTR_ERR(gpio->clk); } ret = clk_prepare_enable(gpio->clk); -- cgit v1.2.3-58-ga151 From e4c6a52c64607d825b0e81c82e548d4cc8199e53 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 23 Jun 2020 16:40:01 +0200 Subject: gpio: Drop superfluous dependencies on GPIOLIB All config options for GPIO drivers are inside a big "if GPIOLIB ... endif" block, so there is no reason for individual config options to have expicit dependencies on GPIOLIB. Hence remove them. Signed-off-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c6b5c65c8405..05e0801c6a78 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -410,7 +410,7 @@ config GPIO_MXS config GPIO_OCTEON tristate "Cavium OCTEON GPIO" - depends on GPIOLIB && CAVIUM_OCTEON_SOC + depends on CAVIUM_OCTEON_SOC default y help Say yes here to support the on-chip GPIO lines on the OCTEON @@ -1117,7 +1117,7 @@ config GPIO_DLN2 config HTC_EGPIO bool "HTC EGPIO support" - depends on GPIOLIB && ARM + depends on ARM help This driver supports the CPLD egpio chip present on several HTC phones. It provides basic support for input -- cgit v1.2.3-58-ga151 From f02a03985d06c5a39605a0b8a6206af0f9ec7b03 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 29 Jun 2020 09:41:14 -0700 Subject: gpio: omap: Add missing PM ops for suspend We've had the legacy platform code take care of suspend for us but this no longer is the case when probed without legacy mode with ti-sysc. We need to configure PM ops like standard Linux device drivers do. As we still have some SoCs booting also the legacy mode, we need to add omap_gpio_suspend() and omap_gpio_resume(), and check for the is_suspended flag to avoid legacy _od_suspend_noirq() calling them on an already suspended GPIO instance. Once we have no SoCs booting in legacy mode, we can just switch to using the standard PM ops with pm_runtime_force_suspend() and pm_runtime_force_resume(). Signed-off-by: Tony Lindgren Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-omap.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index b8e2ecc3eade..b3afe39027bd 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -60,6 +60,7 @@ struct gpio_bank { struct clk *dbck; struct notifier_block nb; unsigned int is_suspended:1; + unsigned int needs_resume:1; u32 mod_usage; u32 irq_usage; u32 dbck_enable_mask; @@ -1504,9 +1505,34 @@ static int __maybe_unused omap_gpio_runtime_resume(struct device *dev) return 0; } +static int omap_gpio_suspend(struct device *dev) +{ + struct gpio_bank *bank = dev_get_drvdata(dev); + + if (bank->is_suspended) + return 0; + + bank->needs_resume = 1; + + return omap_gpio_runtime_suspend(dev); +} + +static int omap_gpio_resume(struct device *dev) +{ + struct gpio_bank *bank = dev_get_drvdata(dev); + + if (!bank->needs_resume) + return 0; + + bank->needs_resume = 0; + + return omap_gpio_runtime_resume(dev); +} + static const struct dev_pm_ops gpio_pm_ops = { SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume) }; static struct platform_driver omap_gpio_driver = { -- cgit v1.2.3-58-ga151 From ae66eca000cfef3ef0e31842102e17b1b660b1ce Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 30 Jun 2020 12:21:46 +0300 Subject: gpiolib: Deduplicate find_first_zero_bit() call bitmap_full() is a shortcut to find_first_zero_bit(). Thus, no need to call it twice. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4fa075d49fbc..fa01455be4f0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3705,10 +3705,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, bitmap_xor(value_bitmap, value_bitmap, array_info->invert_mask, array_size); - if (bitmap_full(array_info->get_mask, array_size)) - return 0; - i = find_first_zero_bit(array_info->get_mask, array_size); + if (i == array_size) + return 0; } else { array_info = NULL; } @@ -3989,10 +3988,9 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, gpio_chip_set_multiple(array_info->chip, array_info->set_mask, value_bitmap); - if (bitmap_full(array_info->set_mask, array_size)) - return 0; - i = find_first_zero_bit(array_info->set_mask, array_size); + if (i == array_size) + return 0; } else { array_info = NULL; } -- cgit v1.2.3-58-ga151 From 85b565c99eca5305d6f55e7855e27a427a81e6e6 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:38 +0100 Subject: gpio: gpio-ml-ioh: Fix missing ':' in 'struct ioh_gpio_reg_data 'struct ioh_gpio_reg_data's 'ien_reg' property is missing a ':' which confuses the kerneldoc tooling/parsers/validators. Replacing it squashes the following W=1 warning: drivers/gpio/gpio-ml-ioh.c:63: warning: Function parameter or member 'ien_reg' not described in 'ioh_gpio_reg_data' Cc: Andy Shevchenko Signed-off-by: Lee Jones Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-ml-ioh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 92b6e958cfed..53d4abefa6ff 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -44,7 +44,7 @@ struct ioh_regs { /** * struct ioh_gpio_reg_data - The register store data. - * @ien_reg To store contents of interrupt enable register. + * @ien_reg: To store contents of interrupt enable register. * @imask_reg: To store contents of interrupt mask regist * @po_reg: To store contents of PO register. * @pm_reg: To store contents of PM register. -- cgit v1.2.3-58-ga151 From 532e762d51d06291e3c40d5f7be8e42dce3b3d05 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 1 Jul 2020 18:20:46 +0300 Subject: gpio: pch: Move IRQ status message to verbose debug level If one of the devices which share the same IRQ line doesn't care about interrupt GPIO will spam the log with status equal to 0x00. Move IRQ status message to verbose debug level (it still might be useful). Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-pch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index e96d28bf43b4..adceee8263b2 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -304,9 +304,10 @@ static irqreturn_t pch_gpio_handler(int irq, void *dev_id) unsigned long reg_val = ioread32(&chip->reg->istatus); int i; - dev_dbg(chip->dev, "irq=%d status=0x%lx\n", irq, reg_val); + dev_vdbg(chip->dev, "irq=%d status=0x%lx\n", irq, reg_val); reg_val &= BIT(gpio_pins[chip->ioh]) - 1; + for_each_set_bit(i, ®_val, gpio_pins[chip->ioh]) generic_handle_irq(chip->irq_base + i); -- cgit v1.2.3-58-ga151 From 2073ea3ab181d42b4fd179ec3f74d53acefb563a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 1 Jul 2020 13:42:11 +0200 Subject: gpio: aggregator: Drop pre-initialization in get_arg() In get_arg(), the variable start is pre-initialized, but overwritten again in the first statement. Rework the assignment to not rely on pre-initialization, to make the code easier to read. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200701114212.8520-2-geert+renesas@glider.be Signed-off-by: Linus Walleij --- drivers/gpio/gpio-aggregator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 9b0adbdddbfc..62a3fcbd4b4b 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -38,9 +38,9 @@ static DEFINE_IDR(gpio_aggregator_idr); static char *get_arg(char **args) { - char *start = *args, *end; + char *start, *end; - start = skip_spaces(start); + start = skip_spaces(*args); if (!*start) return NULL; -- cgit v1.2.3-58-ga151 From ec75039d55500ee033236aa25f7642a5b449a23c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 1 Jul 2020 13:42:12 +0200 Subject: gpio: aggregator: Use bitmap_parselist() for parsing GPIO offsets Replace the custom code to parse GPIO offsets and/or GPIO offset ranges by a call to bitmap_parselist(), and an iteration over the returned bit mask. This should have no impact on the format of the configuration parameters written to the "new_device" virtual file in sysfs. Suggested-by: Andy Shevchenko Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200701114212.8520-3-geert+renesas@glider.be Signed-off-by: Linus Walleij --- drivers/gpio/gpio-aggregator.c | 59 +++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 33 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 62a3fcbd4b4b..424a3d25350b 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -111,55 +112,45 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, static int aggr_parse(struct gpio_aggregator *aggr) { - unsigned int first_index, last_index, i, n = 0; - char *name, *offsets, *first, *last, *next; char *args = aggr->args; - int error; + unsigned long *bitmap; + unsigned int i, n = 0; + char *name, *offsets; + int error = 0; + + bitmap = bitmap_alloc(ARCH_NR_GPIOS, GFP_KERNEL); + if (!bitmap) + return -ENOMEM; for (name = get_arg(&args), offsets = get_arg(&args); name; offsets = get_arg(&args)) { if (IS_ERR(name)) { pr_err("Cannot get GPIO specifier: %pe\n", name); - return PTR_ERR(name); + error = PTR_ERR(name); + goto free_bitmap; } if (!isrange(offsets)) { /* Named GPIO line */ error = aggr_add_gpio(aggr, name, U16_MAX, &n); if (error) - return error; + goto free_bitmap; name = offsets; continue; } /* GPIO chip + offset(s) */ - for (first = offsets; *first; first = next) { - next = strchrnul(first, ','); - if (*next) - *next++ = '\0'; - - last = strchr(first, '-'); - if (last) - *last++ = '\0'; - - if (kstrtouint(first, 10, &first_index)) { - pr_err("Cannot parse GPIO index %s\n", first); - return -EINVAL; - } - - if (!last) { - last_index = first_index; - } else if (kstrtouint(last, 10, &last_index)) { - pr_err("Cannot parse GPIO index %s\n", last); - return -EINVAL; - } - - for (i = first_index; i <= last_index; i++) { - error = aggr_add_gpio(aggr, name, i, &n); - if (error) - return error; - } + error = bitmap_parselist(offsets, bitmap, ARCH_NR_GPIOS); + if (error) { + pr_err("Cannot parse %s: %d\n", offsets, error); + goto free_bitmap; + } + + for_each_set_bit(i, bitmap, ARCH_NR_GPIOS) { + error = aggr_add_gpio(aggr, name, i, &n); + if (error) + goto free_bitmap; } name = get_arg(&args); @@ -167,10 +158,12 @@ static int aggr_parse(struct gpio_aggregator *aggr) if (!n) { pr_err("No GPIOs specified\n"); - return -EINVAL; + error = -EINVAL; } - return 0; +free_bitmap: + bitmap_free(bitmap); + return error; } static ssize_t new_device_store(struct device_driver *driver, const char *buf, -- cgit v1.2.3-58-ga151 From c5a66b970aacd14639524c5b0c05a8491127d333 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:36 +0100 Subject: gpio: gpiolib-of: Provide documentation for of_gpio_spi_cs_get_count() Descriptions for of_gpio_spi_cs_get_count()'s 2 arguments are missing. Document both 'dev' and 'con_id'. Fixes the following W=1 kernel build warnings: drivers/gpio/gpiolib-of.c:36: warning: Function parameter or member 'dev' not described in 'of_gpio_spi_cs_get_count' drivers/gpio/gpiolib-of.c:36: warning: Function parameter or member 'con_id' not described in 'of_gpio_spi_cs_get_count' Signed-off-by: Lee Jones Cc: Anton Vorontsov Link: https://lore.kernel.org/r/20200630133345.2232932-2-lee.jones@linaro.org Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 219eb0054233..236985714ea9 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -25,6 +25,9 @@ /** * of_gpio_spi_cs_get_count() - special GPIO counting for SPI + * @dev: Consuming device + * @con_id: Function within the GPIO consumer + * * Some elder GPIO controllers need special quirks. Currently we handle * the Freescale GPIO controller with bindings that doesn't use the * established "cs-gpios" for chip selects but instead rely on -- cgit v1.2.3-58-ga151 From 670647d7c418b4c4a4effc7eab80729310a97f0a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:37 +0100 Subject: gpio: gpio-altera: Add missing kerneldoc entry and demote comment 'struct altera_gpio_chip's 'irq_chip' property is undocumented. So add property description to the struct's kerneldoc header. Also demote comment block which is clearly not in kerneldoc format. Fixes the following W=1 warnings: drivers/gpio/gpio-altera.c:34: warning: Function parameter or member 'irq_chip' not described in 'altera_gpio_chip' drivers/gpio/gpio-altera.c:78: warning: Function parameter or member 'd' not described in 'altera_gpio_irq_set_type' drivers/gpio/gpio-altera.c:78: warning: Function parameter or member 'type' not described in 'altera_gpio_irq_set_type' Signed-off-by: Lee Jones Cc: Joyce Ooi Cc: Tien Hock Loh Link: https://lore.kernel.org/r/20200630133345.2232932-3-lee.jones@linaro.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-altera.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index cc4ba71e4fe3..b7932ecc3b61 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -24,6 +24,7 @@ * @interrupt_trigger : specifies the hardware configured IRQ trigger type * (rising, falling, both, high) * @mapped_irq : kernel mapped irq number. +* @irq_chip : IRQ chip configuration */ struct altera_gpio_chip { struct of_mm_gpio_chip mmchip; @@ -69,7 +70,7 @@ static void altera_gpio_irq_mask(struct irq_data *d) raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); } -/** +/* * This controller's IRQ type is synthesized in hardware, so this function * just checks if the requested set_type matches the synthesized IRQ type */ -- cgit v1.2.3-58-ga151 From 3cd7f770fe8e230274e011198bf290a25dfaa570 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:39 +0100 Subject: gpio: gpio-pmic-eic-sprd: Fix incorrectly named property 'map' A good attempt has been made to properly document 'struct sprd_pmic_eic', but 'map' has been incorrectly described as 'regmap' since the driver's inception in 2018. Fixes the following W=1 kernel build warning: drivers/gpio/gpio-pmic-eic-sprd.c:65: warning: Function parameter or member 'map' not described in 'sprd_pmic_eic' Signed-off-by: Lee Jones Reviewed-by: Baolin Wang Cc: Orson Zhai Cc: Baolin Wang Cc: Chunyan Zhang Link: https://lore.kernel.org/r/20200630133345.2232932-5-lee.jones@linaro.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pmic-eic-sprd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index 05000cace9b2..938285190566 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -48,7 +48,7 @@ enum { * struct sprd_pmic_eic - PMIC EIC controller * @chip: the gpio_chip structure. * @intc: the irq_chip structure. - * @regmap: the regmap from the parent device. + * @map: the regmap from the parent device. * @offset: the EIC controller's offset address of the PMIC. * @reg: the array to cache the EIC registers. * @buslock: for bus lock/sync and unlock. -- cgit v1.2.3-58-ga151 From 392a58469894f8fe26874f66f1e064c20a9b91ee Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:40 +0100 Subject: gpio: gpio-sama5d2-piobu: Demote all kerneldoc headers to basic comment blocks No attempt has been made to provide proper descriptions for each of the function arguments throughout the file. Simply demote all kerneldoc headers to basic function headers. Fixes the following W=1 kernel build warnings: drivers/gpio/gpio-sama5d2-piobu.c:59: warning: Function parameter or member 'chip' not described in 'sama5d2_piobu_setup_pin' drivers/gpio/gpio-sama5d2-piobu.c:59: warning: Function parameter or member 'pin' not described in 'sama5d2_piobu_setup_pin' drivers/gpio/gpio-sama5d2-piobu.c:81: warning: Function parameter or member 'chip' not described in 'sama5d2_piobu_write_value' drivers/gpio/gpio-sama5d2-piobu.c:81: warning: Function parameter or member 'pin' not described in 'sama5d2_piobu_write_value' drivers/gpio/gpio-sama5d2-piobu.c:81: warning: Function parameter or member 'mask' not described in 'sama5d2_piobu_write_value' drivers/gpio/gpio-sama5d2-piobu.c:81: warning: Function parameter or member 'value' not described in 'sama5d2_piobu_write_value' drivers/gpio/gpio-sama5d2-piobu.c:97: warning: Function parameter or member 'chip' not described in 'sama5d2_piobu_read_value' drivers/gpio/gpio-sama5d2-piobu.c:97: warning: Function parameter or member 'pin' not described in 'sama5d2_piobu_read_value' drivers/gpio/gpio-sama5d2-piobu.c:97: warning: Function parameter or member 'mask' not described in 'sama5d2_piobu_read_value' drivers/gpio/gpio-sama5d2-piobu.c:116: warning: Function parameter or member 'chip' not described in 'sama5d2_piobu_get_direction' drivers/gpio/gpio-sama5d2-piobu.c:116: warning: Function parameter or member 'pin' not described in 'sama5d2_piobu_get_direction' drivers/gpio/gpio-sama5d2-piobu.c:131: warning: Function parameter or member 'chip' not described in 'sama5d2_piobu_direction_input' drivers/gpio/gpio-sama5d2-piobu.c:131: warning: Function parameter or member 'pin' not described in 'sama5d2_piobu_direction_input' drivers/gpio/gpio-sama5d2-piobu.c:140: warning: Function parameter or member 'chip' not described in 'sama5d2_piobu_direction_output' drivers/gpio/gpio-sama5d2-piobu.c:140: warning: Function parameter or member 'pin' not described in 'sama5d2_piobu_direction_output' drivers/gpio/gpio-sama5d2-piobu.c:140: warning: Function parameter or member 'value' not described in 'sama5d2_piobu_direction_output' drivers/gpio/gpio-sama5d2-piobu.c:154: warning: Function parameter or member 'chip' not described in 'sama5d2_piobu_get' drivers/gpio/gpio-sama5d2-piobu.c:154: warning: Function parameter or member 'pin' not described in 'sama5d2_piobu_get' drivers/gpio/gpio-sama5d2-piobu.c:174: warning: Function parameter or member 'chip' not described in 'sama5d2_piobu_set' drivers/gpio/gpio-sama5d2-piobu.c:174: warning: Function parameter or member 'pin' not described in 'sama5d2_piobu_set' drivers/gpio/gpio-sama5d2-piobu.c:174: warning: Function parameter or member 'value' not described in 'sama5d2_piobu_set' Signed-off-by: Lee Jones Acked-by: Ludovic Desroches Cc: Ludovic Desroches Cc: Andrei Stefanescu Link: https://lore.kernel.org/r/20200630133345.2232932-6-lee.jones@linaro.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-sama5d2-piobu.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index 4d47b2c41186..b7c950658170 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -49,7 +49,7 @@ struct sama5d2_piobu { struct regmap *regmap; }; -/** +/* * sama5d2_piobu_setup_pin() - prepares a pin for set_direction call * * Do not consider pin for tamper detection (normal and backup modes) @@ -73,7 +73,7 @@ static int sama5d2_piobu_setup_pin(struct gpio_chip *chip, unsigned int pin) return regmap_update_bits(piobu->regmap, PIOBU_WKPR, mask, 0); } -/** +/* * sama5d2_piobu_write_value() - writes value & mask at the pin's PIOBU register */ static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin, @@ -88,7 +88,7 @@ static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin, return regmap_update_bits(piobu->regmap, reg, mask, value); } -/** +/* * sama5d2_piobu_read_value() - read the value with masking from the pin's PIOBU * register */ @@ -108,7 +108,7 @@ static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin, return val & mask; } -/** +/* * sama5d2_piobu_get_direction() - gpiochip get_direction */ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, @@ -123,7 +123,7 @@ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, GPIO_LINE_DIRECTION_OUT; } -/** +/* * sama5d2_piobu_direction_input() - gpiochip direction_input */ static int sama5d2_piobu_direction_input(struct gpio_chip *chip, @@ -132,7 +132,7 @@ static int sama5d2_piobu_direction_input(struct gpio_chip *chip, return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN); } -/** +/* * sama5d2_piobu_direction_output() - gpiochip direction_output */ static int sama5d2_piobu_direction_output(struct gpio_chip *chip, @@ -147,7 +147,7 @@ static int sama5d2_piobu_direction_output(struct gpio_chip *chip, val); } -/** +/* * sama5d2_piobu_get() - gpiochip get */ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin) @@ -166,7 +166,7 @@ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin) return !!ret; } -/** +/* * sama5d2_piobu_set() - gpiochip set */ static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin, -- cgit v1.2.3-58-ga151 From 5f3beb67b5d1c5f9906dfdf3af77e8992ca177be Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:41 +0100 Subject: gpio: gpio-syscon: Fix formatting issues which confuse kerneldoc Kerneldoc expects struct properties to be documented using the syntax '@.*: ', but no '@' has been provided in 'struct syscon_gpio_data's header. Add them to stop confusing kerneldoc. Fixes the following W=1 warnings: drivers/gpio/gpio-syscon.c:48: warning: Function parameter or member 'compatible' not described in 'syscon_gpio_data' drivers/gpio/gpio-syscon.c:48: warning: Function parameter or member 'flags' not described in 'syscon_gpio_data' drivers/gpio/gpio-syscon.c:48: warning: Function parameter or member 'bit_count' not described in 'syscon_gpio_data' drivers/gpio/gpio-syscon.c:48: warning: Function parameter or member 'dat_bit_offset' not described in 'syscon_gpio_data' drivers/gpio/gpio-syscon.c:48: warning: Function parameter or member 'dir_bit_offset' not described in 'syscon_gpio_data' drivers/gpio/gpio-syscon.c:48: warning: Function parameter or member 'set' not described in 'syscon_gpio_data' Signed-off-by: Lee Jones Cc: Alexander Shiyan Link: https://lore.kernel.org/r/20200630133345.2232932-7-lee.jones@linaro.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-syscon.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 31f332074d7d..fdd3d497b535 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -24,16 +24,16 @@ /** * struct syscon_gpio_data - Configuration for the device. - * compatible: SYSCON driver compatible string. - * flags: Set of GPIO_SYSCON_FEAT_ flags: + * @compatible: SYSCON driver compatible string. + * @flags: Set of GPIO_SYSCON_FEAT_ flags: * GPIO_SYSCON_FEAT_IN: GPIOs supports input, * GPIO_SYSCON_FEAT_OUT: GPIOs supports output, * GPIO_SYSCON_FEAT_DIR: GPIOs supports switch direction. - * bit_count: Number of bits used as GPIOs. - * dat_bit_offset: Offset (in bits) to the first GPIO bit. - * dir_bit_offset: Optional offset (in bits) to the first bit to switch + * @bit_count: Number of bits used as GPIOs. + * @dat_bit_offset: Offset (in bits) to the first GPIO bit. + * @dir_bit_offset: Optional offset (in bits) to the first bit to switch * GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag). - * set: HW specific callback to assigns output value + * @set: HW specific callback to assigns output value * for signal "offset" */ -- cgit v1.2.3-58-ga151 From 4e2912901ec1ff0ef7aa8d4334e0ad476792f764 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:42 +0100 Subject: gpio: gpio-it87: Fix formatting issues which confuse kerneldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kerneldoc expects struct properties to be documented using the syntax '@.*: ', but no ':' has been provided in 'struct it87_gpio's header. Add them to stop confusing kerneldoc. Fixes the following W=1 warnings: drivers/gpio/gpio-it87.c:69: warning: Function parameter or member 'chip' not described in 'it87_gpio' drivers/gpio/gpio-it87.c:69: warning: Function parameter or member 'lock' not described in 'it87_gpio' drivers/gpio/gpio-it87.c:69: warning: Function parameter or member 'io_base' not described in 'it87_gpio' drivers/gpio/gpio-it87.c:69: warning: Function parameter or member 'io_size' not described in 'it87_gpio' drivers/gpio/gpio-it87.c:69: warning: Function parameter or member 'output_base' not described in 'it87_gpio' drivers/gpio/gpio-it87.c:69: warning: Function parameter or member 'simple_base' not described in 'it87_gpio' drivers/gpio/gpio-it87.c:69: warning: Function parameter or member 'simple_size' not described in 'it87_gpio' Signed-off-by: Lee Jones Cc: "Diego Elio Pettenò" Link: https://lore.kernel.org/r/20200630133345.2232932-8-lee.jones@linaro.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-it87.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c index b497a1d18ca9..8f1be34953ce 100644 --- a/drivers/gpio/gpio-it87.c +++ b/drivers/gpio/gpio-it87.c @@ -47,13 +47,13 @@ /** * struct it87_gpio - it87-specific GPIO chip - * @chip the underlying gpio_chip structure - * @lock a lock to avoid races between operations - * @io_base base address for gpio ports - * @io_size size of the port rage starting from io_base. - * @output_base Super I/O register address for Output Enable register - * @simple_base Super I/O 'Simple I/O' Enable register - * @simple_size Super IO 'Simple I/O' Enable register size; this is + * @chip: the underlying gpio_chip structure + * @lock: a lock to avoid races between operations + * @io_base: base address for gpio ports + * @io_size: size of the port rage starting from io_base. + * @output_base: Super I/O register address for Output Enable register + * @simple_base: Super I/O 'Simple I/O' Enable register + * @simple_size: Super IO 'Simple I/O' Enable register size; this is * required because IT87xx chips might only provide Simple I/O * switches on a subset of lines, whereas the others keep the * same status all time. -- cgit v1.2.3-58-ga151 From 6ac2de953bb317c4dc2edaa56e025724fd159165 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:43 +0100 Subject: gpio: gpio-mlxbf: Tell the compiler that ACPI functions may not be used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... as is the case when !CONFIG_ACPI. Fixes the following W=1 kernel build warning: drivers/gpio/gpio-mlxbf.c:130:36: warning: ‘mlxbf_gpio_acpi_match’ defined but not used [-Wunused-const-variable=] 130 | static const struct acpi_device_id mlxbf_gpio_acpi_match[] = { | ^~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Lee Jones Cc: Shravan Kumar Ramani Link: https://lore.kernel.org/r/20200630133345.2232932-9-lee.jones@linaro.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mlxbf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mlxbf.c b/drivers/gpio/gpio-mlxbf.c index 894aaf55fc96..1fa9973f55b9 100644 --- a/drivers/gpio/gpio-mlxbf.c +++ b/drivers/gpio/gpio-mlxbf.c @@ -127,7 +127,7 @@ static int mlxbf_gpio_resume(struct platform_device *pdev) } #endif -static const struct acpi_device_id mlxbf_gpio_acpi_match[] = { +static const struct acpi_device_id __maybe_unused mlxbf_gpio_acpi_match[] = { { "MLNXBF02", 0 }, {} }; -- cgit v1.2.3-58-ga151 From 2f9bce5f38684cd10533ac41d8dd395a611cc8c5 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:44 +0100 Subject: gpio: gpio-mlxbf2: Tell the compiler that ACPI functions may not be use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... as is the case when !CONFIG_ACPI. Fixes the following W=1 kernel build warning: drivers/gpio/gpio-mlxbf2.c:312:36: warning: ‘mlxbf2_gpio_acpi_match’ defined but not used [-Wunused-const-variable=] 312 | static const struct acpi_device_id mlxbf2_gpio_acpi_match[] = { | ^~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Lee Jones Reviewed-by: Asmaa Mnebhi Cc: Asmaa Mnebhi Link: https://lore.kernel.org/r/20200630133345.2232932-10-lee.jones@linaro.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mlxbf2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 94d5efce1721..861a8d0a84be 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -309,7 +309,7 @@ static int mlxbf2_gpio_resume(struct platform_device *pdev) } #endif -static const struct acpi_device_id mlxbf2_gpio_acpi_match[] = { +static const struct acpi_device_id __maybe_unused mlxbf2_gpio_acpi_match[] = { { "MLNXBF22", 0 }, {}, }; -- cgit v1.2.3-58-ga151 From a7a9ad232f02e6aa659cc9b58b72b1a30dccd1a6 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:45 +0100 Subject: gpio: gpio-mlxbf2.c: Provide __releases() annotation to stop confusing Sparse Sparse cannot peer into other functions to see when and if locks are acquired and released, thus it simply warns that a 'context imbalance' is detected instead. Let's be kind to Sparse and let it know that this behaviour is intentional. drivers/gpio/gpio-mlxbf2.c:125:12: warning: context imbalance in 'mlxbf2_gpio_lock_acquire' - different lock contexts for basic block drivers/gpio/gpio-mlxbf2.c:151:13: warning: context imbalance in 'mlxbf2_gpio_lock_release' - unexpected unlock Signed-off-by: Lee Jones Reviewed-by: Asmaa Mnebhi Cc: Asmaa Mnebhi Link: https://lore.kernel.org/r/20200630133345.2232932-11-lee.jones@linaro.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mlxbf2.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 861a8d0a84be..befa5e109943 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -149,6 +149,8 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs) * Release the YU arm_gpio_lock after changing the direction mode. */ static void mlxbf2_gpio_lock_release(struct mlxbf2_gpio_context *gs) + __releases(&gs->gc.bgpio_lock) + __releases(yu_arm_gpio_lock_param.lock) { writel(YU_ARM_GPIO_LOCK_RELEASE, yu_arm_gpio_lock_param.io); spin_unlock(&gs->gc.bgpio_lock); -- cgit v1.2.3-58-ga151 From ef087d8e95d9d225f83e92d02c3eae1a41955c5f Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:44 +0800 Subject: gpiolib: move gpiolib-sysfs function declarations into their own header Move gpiolib-sysfs function declarations into their own header. These functions are in gpiolib-sysfs.c, and are only required by gpiolib.c, and so should be in a module header, not gpiolib.h. This brings gpiolib-sysfs into line with gpiolib-cdev, and is another step towards removing the sysfs inferface. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 1 + drivers/gpio/gpiolib-sysfs.h | 24 ++++++++++++++++++++++++ drivers/gpio/gpiolib.c | 1 + drivers/gpio/gpiolib.h | 18 ------------------ 4 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 drivers/gpio/gpiolib-sysfs.h (limited to 'drivers/gpio') 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 #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..4d267c69482c 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 diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 9ed242316414..2dee4e1e12dc 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -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 */ -- cgit v1.2.3-58-ga151 From d189f6270fe755e8540bf2ae1a9f8da693253d87 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:45 +0800 Subject: gpiolib: cdev: sort includes Sort the includes of gpiolib-cdev.c to make it easier to identify if a module is included and to avoid duplication. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index b8b872724628..55a9b7b44304 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -1,24 +1,24 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include -#include -#include -#include -#include -#include +#include +#include #include #include +#include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include +#include +#include #include +#include #include +#include #include #include "gpiolib.h" -- cgit v1.2.3-58-ga151 From a18512e3f110637d5a4426c7d1d8761566a7e15e Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:46 +0800 Subject: gpiolib: cdev: minor indentation fixes Make indentation consistent with other use to improve readability. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 55a9b7b44304..889ed2dc9e58 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -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; @@ -212,11 +212,11 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, /* Reuse the array setting function */ return gpiod_set_array_value_complex(false, - true, - lh->numdescs, - lh->descs, - NULL, - vals); + true, + lh->numdescs, + lh->descs, + NULL, + vals); } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) { return linehandle_set_config(lh, ip); } @@ -225,7 +225,7 @@ 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) + unsigned long arg) { return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); } @@ -428,7 +428,7 @@ struct lineevent_state { GPIOEVENT_REQUEST_FALLING_EDGE) static __poll_t lineevent_poll(struct file *filep, - struct poll_table_struct *wait) + struct poll_table_struct *wait) { struct lineevent_state *le = filep->private_data; __poll_t events = 0; @@ -720,11 +720,11 @@ 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; @@ -1052,7 +1052,7 @@ static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf, static int gpio_chrdev_open(struct inode *inode, struct file *filp) { struct gpio_device *gdev = container_of(inode->i_cdev, - struct gpio_device, chrdev); + struct gpio_device, chrdev); struct gpio_chardev_data *priv; int ret = -ENOMEM; -- cgit v1.2.3-58-ga151 From c274b58a4b8a3bfbf0bb0b5f5332e3e049d75b65 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:47 +0800 Subject: gpiolib: cdev: refactor gpiohandle_flags_to_desc_flags Refactor the mapping from handle flags to desc flags into a helper function. The assign_bit is overkill where it is replacing the set_bit cases, as is rechecking bits known to be clear in some circumstances, but the DRY simplification more than makes up for any performance degradation, especially as this is not a hot path. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 60 ++++++++++++++------------------------------- 1 file changed, 19 insertions(+), 41 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 889ed2dc9e58..e64613b8d0ba 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -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; @@ -125,25 +140,7 @@ static long linehandle_set_config(struct linehandle_state *lh, for (i = 0; i < lh->numdescs; 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 @@ -306,19 +303,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) goto out_free_descs; 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) @@ -685,14 +670,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) 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) -- cgit v1.2.3-58-ga151 From 49bc52798d7bb66e958d7ea7378c38ec258b9037 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:48 +0800 Subject: gpiolib: cdev: rename 'filep' and 'filp' to 'file' to be consistent with other use Rename 'filep' and 'filp' to 'file' to be consistent with other use and improve readability. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 70 ++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 35 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index e64613b8d0ba..0d3a799e09ae 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -164,10 +164,10 @@ static long linehandle_set_config(struct linehandle_state *lh, 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); @@ -221,16 +221,16 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, } #ifdef CONFIG_COMPAT -static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd, +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 int linehandle_release(struct inode *inode, struct file *file) { - struct linehandle_state *lh = filep->private_data; + struct linehandle_state *lh = file->private_data; struct gpio_device *gdev = lh->gdev; int i; @@ -412,13 +412,13 @@ struct lineevent_state { (GPIOEVENT_REQUEST_RISING_EDGE | \ GPIOEVENT_REQUEST_FALLING_EDGE) -static __poll_t lineevent_poll(struct file *filep, +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; @@ -427,12 +427,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; @@ -448,7 +448,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; } @@ -481,9 +481,9 @@ static ssize_t lineevent_read(struct file *filep, return bytes_read; } -static int lineevent_release(struct inode *inode, struct file *filep) +static int lineevent_release(struct inode *inode, struct file *file) { - struct lineevent_state *le = filep->private_data; + struct lineevent_state *le = file->private_data; struct gpio_device *gdev = le->gdev; free_irq(le->irq, le); @@ -494,10 +494,10 @@ static int lineevent_release(struct inode *inode, struct file *filep) 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; @@ -524,10 +524,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 @@ -826,9 +826,9 @@ 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_chardev_data *priv = file->private_data; struct gpio_device *gdev = priv->gdev; struct gpio_chip *gc = gdev->chip; void __user *ip = (void __user *)arg; @@ -919,10 +919,10 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } #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 @@ -958,13 +958,13 @@ static int lineinfo_changed_notify(struct notifier_block *nb, 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 *priv = file->private_data; __poll_t events = 0; - poll_wait(filep, &priv->wait, pollt); + poll_wait(file, &priv->wait, pollt); if (!kfifo_is_empty_spinlocked_noirqsave(&priv->events, &priv->wait.lock)) @@ -973,10 +973,10 @@ static __poll_t lineinfo_watch_poll(struct file *filep, 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 *priv = file->private_data; struct gpioline_info_changed event; ssize_t bytes_read = 0; int ret; @@ -992,7 +992,7 @@ static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf, return bytes_read; } - if (filep->f_flags & O_NONBLOCK) { + if (file->f_flags & O_NONBLOCK) { spin_unlock(&priv->wait.lock); return -EAGAIN; } @@ -1024,10 +1024,10 @@ 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); @@ -1057,9 +1057,9 @@ static int gpio_chrdev_open(struct inode *inode, struct file *filp) goto out_free_bitmap; get_device(&gdev->dev); - filp->private_data = priv; + file->private_data = priv; - ret = nonseekable_open(inode, filp); + ret = nonseekable_open(inode, file); if (ret) goto out_unregister_notifier; @@ -1078,12 +1078,12 @@ out_free_priv: /** * 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_chardev_data *priv = file->private_data; struct gpio_device *gdev = priv->gdev; bitmap_free(priv->watched_lines); -- cgit v1.2.3-58-ga151 From 52b7b596dd4d7ca1d8d7c6af07bb9372e56d3463 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:49 +0800 Subject: gpiolib: cdev: rename numdescs to num_descs Rename numdescs to num_descs to be more consistent with the naming of other counters and improve readability. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 0d3a799e09ae..b39e7ef8c0d4 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -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 \ @@ -138,7 +138,7 @@ 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]; linehandle_flags_to_desc_flags(gcnf.flags, &desc->flags); @@ -177,7 +177,7 @@ static long linehandle_ioctl(struct file *file, 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); @@ -185,7 +185,7 @@ static long linehandle_ioctl(struct file *file, 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))) @@ -204,13 +204,13 @@ static long linehandle_ioctl(struct file *file, 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->num_descs, lh->descs, NULL, vals); @@ -234,7 +234,7 @@ static int linehandle_release(struct inode *inode, struct file *file) struct gpio_device *gdev = lh->gdev; int i; - for (i = 0; i < lh->numdescs; i++) + for (i = 0; i < lh->num_descs; i++) gpiod_free(lh->descs[i]); kfree(lh->label); kfree(lh); @@ -333,7 +333,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) } /* Let i point at the last handle */ i--; - lh->numdescs = handlereq.lines; + lh->num_descs = handlereq.lines; fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); if (fd < 0) { @@ -364,7 +364,7 @@ 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; -- cgit v1.2.3-58-ga151 From 0cdc85a371c7c612a6fd04dd486a60f58350505b Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:50 +0800 Subject: gpiolib: cdev: remove pointless decrement of i Remove pointless decrement of variable, and associated comment. While i is used subsequently, it is re-initialized so this decrement serves no purpose. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index b39e7ef8c0d4..d50339ef6f05 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -331,8 +331,6 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", offset); } - /* Let i point at the last handle */ - i--; lh->num_descs = handlereq.lines; fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); -- cgit v1.2.3-58-ga151 From 6accc376a7482ecec2537e3e092b4f06e11d5994 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:51 +0800 Subject: gpiolib: cdev: use blocking notifier call chain instead of atomic Replace usage of atomic_notifier_call_chain with blocking_notifier_call_chain as the notifier function, lineinfo_changed_notify, calls gpio_desc_to_lineinfo, which calls pinctrl_gpio_can_use_line, which can sleep. The chain isn't being called from an atomic context so the the blocking notifier is a suitable substitute. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 24 ++++++++++++------------ drivers/gpio/gpiolib.c | 14 +++++++------- drivers/gpio/gpiolib.h | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index d50339ef6f05..352d815bbd07 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -158,8 +158,8 @@ 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; } @@ -325,8 +325,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) goto out_free_descs; } - 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); @@ -674,8 +674,8 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) if (ret) goto out_free_desc; - 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) { @@ -1049,8 +1049,8 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) priv->gdev = gdev; priv->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; - ret = atomic_notifier_chain_register(&gdev->notifier, - &priv->lineinfo_changed_nb); + ret = blocking_notifier_chain_register(&gdev->notifier, + &priv->lineinfo_changed_nb); if (ret) goto out_free_bitmap; @@ -1064,8 +1064,8 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) return ret; out_unregister_notifier: - atomic_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); + blocking_notifier_chain_unregister(&gdev->notifier, + &priv->lineinfo_changed_nb); out_free_bitmap: bitmap_free(priv->watched_lines); out_free_priv: @@ -1085,8 +1085,8 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file) struct gpio_device *gdev = priv->gdev; bitmap_free(priv->watched_lines); - atomic_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); + blocking_notifier_chain_unregister(&gdev->notifier, + &priv->lineinfo_changed_nb); put_device(&gdev->dev); kfree(priv); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4d267c69482c..80137c1b3cdc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -615,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); @@ -2049,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; } @@ -3927,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; } @@ -3995,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 2dee4e1e12dc..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 /* -- cgit v1.2.3-58-ga151 From e2b781c5f0dd45f632b3e15bbca702d1d140fce7 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:52 +0800 Subject: gpiolib: cdev: rename priv to cdev Rename priv to cdev to improve readability. The name "priv" indicates that the object is pointed to by file->private_data, not what the object is actually is. As it is always used to point to a struct gpio_chardev_data, renaming it to cdev is more appropriate. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 90 ++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 45 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 352d815bbd07..fe1b385deecc 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -826,8 +826,8 @@ struct gpio_chardev_data { */ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct gpio_chardev_data *priv = file->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; @@ -887,7 +887,7 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) hwgpio = gpio_chip_hwgpio(desc); - if (test_bit(hwgpio, priv->watched_lines)) + if (test_bit(hwgpio, cdev->watched_lines)) return -EBUSY; gpio_desc_to_lineinfo(desc, &lineinfo); @@ -895,7 +895,7 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) return -EFAULT; - set_bit(hwgpio, priv->watched_lines); + set_bit(hwgpio, cdev->watched_lines); return 0; } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) { if (copy_from_user(&offset, ip, sizeof(offset))) @@ -907,10 +907,10 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) hwgpio = gpio_chip_hwgpio(desc); - if (!test_bit(hwgpio, priv->watched_lines)) + if (!test_bit(hwgpio, cdev->watched_lines)) return -EBUSY; - clear_bit(hwgpio, priv->watched_lines); + clear_bit(hwgpio, cdev->watched_lines); return 0; } return -EINVAL; @@ -933,12 +933,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)); @@ -947,9 +947,9 @@ 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"); @@ -959,13 +959,13 @@ static int lineinfo_changed_notify(struct notifier_block *nb, static __poll_t lineinfo_watch_poll(struct file *file, struct poll_table_struct *pollt) { - struct gpio_chardev_data *priv = file->private_data; + struct gpio_chardev_data *cdev = file->private_data; __poll_t events = 0; - poll_wait(file, &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; @@ -974,7 +974,7 @@ static __poll_t lineinfo_watch_poll(struct file *file, static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, size_t count, loff_t *off) { - struct gpio_chardev_data *priv = file->private_data; + struct gpio_chardev_data *cdev = file->private_data; struct gpioline_info_changed event; ssize_t bytes_read = 0; int ret; @@ -983,28 +983,28 @@ static ssize_t lineinfo_watch_read(struct file *file, 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 (file->f_flags & O_NONBLOCK) { - spin_unlock(&priv->wait.lock); + 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; @@ -1029,33 +1029,33 @@ 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_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; + cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; ret = blocking_notifier_chain_register(&gdev->notifier, - &priv->lineinfo_changed_nb); + &cdev->lineinfo_changed_nb); if (ret) goto out_free_bitmap; get_device(&gdev->dev); - file->private_data = priv; + file->private_data = cdev; ret = nonseekable_open(inode, file); if (ret) @@ -1065,11 +1065,11 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) out_unregister_notifier: blocking_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); + &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; } @@ -1081,14 +1081,14 @@ out_free_priv: */ static int gpio_chrdev_release(struct inode *inode, struct file *file) { - struct gpio_chardev_data *priv = file->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); + bitmap_free(cdev->watched_lines); blocking_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); + &cdev->lineinfo_changed_nb); put_device(&gdev->dev); - kfree(priv); + kfree(cdev); return 0; } -- cgit v1.2.3-58-ga151 From f30ef3e8376380c5be9de121517c713527cf0813 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:53 +0800 Subject: gpiolib: cdev: fix minor race in GET_LINEINFO_WATCH Merge separate usage of test_bit/set_bit into test_and_set_bit to remove the possibility of a race between the test and set. Similarly test_bit and clear_bit. In the existing code it is possible for two threads to race past the test_bit and then set or clear the watch bit, and neither return EBUSY. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index fe1b385deecc..b2b26dc25051 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -887,15 +887,16 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) hwgpio = gpio_chip_hwgpio(desc); - if (test_bit(hwgpio, cdev->watched_lines)) + if (test_and_set_bit(hwgpio, 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(hwgpio, cdev->watched_lines); return -EFAULT; + } - set_bit(hwgpio, cdev->watched_lines); return 0; } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) { if (copy_from_user(&offset, ip, sizeof(offset))) @@ -907,10 +908,9 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) hwgpio = gpio_chip_hwgpio(desc); - if (!test_bit(hwgpio, cdev->watched_lines)) + if (!test_and_clear_bit(hwgpio, cdev->watched_lines)) return -EBUSY; - clear_bit(hwgpio, cdev->watched_lines); return 0; } return -EINVAL; -- cgit v1.2.3-58-ga151 From 1bf7ba400173c3a134aae3187b550d79c2a5f585 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:54 +0800 Subject: gpiolib: cdev: remove recalculation of offset Remove recalculation of offset from desc, where desc itself was calculated from offset. There is no benefit from the desc -> hwgpio conversion in this context. The only implicit benefit of the offset -> desc -> hwgpio is the range check in the offset -> desc, but where desc is required you still get that, and where desc isn't required it is simpler to perform the range check directly. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index b2b26dc25051..c86fb9305681 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -832,7 +832,6 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) void __user *ip = (void __user *)arg; struct gpio_desc *desc; __u32 offset; - int hwgpio; /* We fail any subsequent ioctl():s when the chip is gone */ if (!gc) @@ -860,12 +859,11 @@ static long gpio_ioctl(struct file *file, 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))) @@ -881,19 +879,18 @@ static long gpio_ioctl(struct file *file, 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_and_set_bit(hwgpio, cdev->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))) { - clear_bit(hwgpio, cdev->watched_lines); + clear_bit(lineinfo.line_offset, cdev->watched_lines); return -EFAULT; } @@ -902,13 +899,10 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 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_and_clear_bit(hwgpio, cdev->watched_lines)) + if (!test_and_clear_bit(offset, cdev->watched_lines)) return -EBUSY; return 0; -- cgit v1.2.3-58-ga151 From 883f919818437122462754174e21c38991a18934 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:55 +0800 Subject: gpiolib: cdev: refactor linehandle cleanup into linehandle_free Consolidate the cleanup of linehandles, currently duplicated in linehandle_create and linehandle_release, into a helper function linehandle_free. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index c86fb9305681..d56b367239cc 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -228,17 +228,21 @@ static long linehandle_ioctl_compat(struct file *file, unsigned int cmd, } #endif -static int linehandle_release(struct inode *inode, struct file *file) +static void linehandle_free(struct linehandle_state *lh) { - struct linehandle_state *lh = file->private_data; - struct gpio_device *gdev = lh->gdev; int i; for (i = 0; i < lh->num_descs; i++) - gpiod_free(lh->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; } @@ -257,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))) @@ -288,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]; @@ -295,19 +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; 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 @@ -318,11 +323,11 @@ 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; } blocking_notifier_call_chain(&desc->gdev->notifier, @@ -331,12 +336,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", offset); } - lh->num_descs = 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", @@ -368,13 +372,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) 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; } -- cgit v1.2.3-58-ga151 From 468242724143a8e732f82f664b1e77432d149618 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 8 Jul 2020 12:15:56 +0800 Subject: gpiolib: cdev: refactor lineevent cleanup into lineevent_free Consolidate the cleanup of lineevents, currently duplicated in lineevent_create and lineevent_release, into a helper function lineevent_free. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index d56b367239cc..e6c9b78adfc2 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -478,16 +478,20 @@ static ssize_t lineevent_read(struct file *file, return bytes_read; } -static int lineevent_release(struct inode *inode, struct file *file) +static void lineevent_free(struct lineevent_state *le) { - struct lineevent_state *le = file->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; } @@ -612,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; @@ -663,7 +667,7 @@ 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; @@ -671,16 +675,17 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) ret = gpiod_direction_input(desc); if (ret) - goto out_free_desc; + goto out_free_le; 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) ? @@ -701,12 +706,12 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) 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", @@ -735,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; } -- cgit v1.2.3-58-ga151 From 4ee82256ee57db5c929d3c2f81f6620a0b31b4cd Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 9 Jul 2020 20:11:58 +0300 Subject: gpio: max77620: Replace 8 with MAX77620_GPIO_NR The MAX77620_GPIO_NR enum value represents the total number of GPIOs, let's use it instead of a raw value in order to improve the code's readability a tad. Signed-off-by: Dmitry Osipenko Reviewed-by: Andy Shevchenko Acked-by: Laxman Dewangan Link: https://lore.kernel.org/r/20200709171203.12950-2-digetx@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-max77620.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 313bd02dd893..4c0c9ec2587d 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -19,8 +19,8 @@ struct max77620_gpio { struct regmap *rmap; struct device *dev; struct mutex buslock; /* irq_bus_lock */ - unsigned int irq_type[8]; - bool irq_enabled[8]; + unsigned int irq_type[MAX77620_GPIO_NR]; + bool irq_enabled[MAX77620_GPIO_NR]; }; static irqreturn_t max77620_gpio_irqhandler(int irq, void *data) @@ -38,7 +38,7 @@ static irqreturn_t max77620_gpio_irqhandler(int irq, void *data) pending = value; - for_each_set_bit(offset, &pending, 8) { + for_each_set_bit(offset, &pending, MAX77620_GPIO_NR) { unsigned int virq; virq = irq_find_mapping(gpio->gpio_chip.irq.domain, offset); -- cgit v1.2.3-58-ga151 From 2a5e6f7eede8cd1c4bac0b8ec6491cec4e75c99a Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 9 Jul 2020 20:11:59 +0300 Subject: gpio: max77620: Fix missing release of interrupt The requested interrupt is never released by the driver. Fix this by using the resource-managed variant of request_threaded_irq(). Fixes: ab3dd9cc24d4 ("gpio: max77620: Fix interrupt handling") Signed-off-by: Dmitry Osipenko Reviewed-by: Andy Shevchenko Acked-by: Laxman Dewangan Cc: # 5.5+ Link: https://lore.kernel.org/r/20200709171203.12950-3-digetx@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-max77620.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 4c0c9ec2587d..7f7e8d4bf0d3 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -305,8 +305,9 @@ static int max77620_gpio_probe(struct platform_device *pdev) gpiochip_irqchip_add_nested(&mgpio->gpio_chip, &max77620_gpio_irqchip, 0, handle_edge_irq, IRQ_TYPE_NONE); - ret = request_threaded_irq(gpio_irq, NULL, max77620_gpio_irqhandler, - IRQF_ONESHOT, "max77620-gpio", mgpio); + ret = devm_request_threaded_irq(&pdev->dev, gpio_irq, NULL, + max77620_gpio_irqhandler, IRQF_ONESHOT, + "max77620-gpio", mgpio); if (ret < 0) { dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret); return ret; -- cgit v1.2.3-58-ga151 From 78934d885eb237910fc9194e0eeeb96ca54b24ae Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 9 Jul 2020 20:12:00 +0300 Subject: gpio: max77620: Don't set of_node The gpiochip_add_data() takes care of setting the of_node to the parent's device of_node, hence there is no need to do it manually in the driver's code. This patch corrects the parent's device pointer and removes the unnecessary setting of the of_node. Suggested-by: Andy Shevchenko Signed-off-by: Dmitry Osipenko Reviewed-by: Andy Shevchenko Acked-by: Laxman Dewangan Link: https://lore.kernel.org/r/20200709171203.12950-4-digetx@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-max77620.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 7f7e8d4bf0d3..39d431da2dbc 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -279,7 +279,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) mgpio->dev = &pdev->dev; mgpio->gpio_chip.label = pdev->name; - mgpio->gpio_chip.parent = &pdev->dev; + mgpio->gpio_chip.parent = pdev->dev.parent; mgpio->gpio_chip.direction_input = max77620_gpio_dir_input; mgpio->gpio_chip.get = max77620_gpio_get; mgpio->gpio_chip.direction_output = max77620_gpio_dir_output; @@ -288,9 +288,6 @@ static int max77620_gpio_probe(struct platform_device *pdev) mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR; mgpio->gpio_chip.can_sleep = 1; mgpio->gpio_chip.base = -1; -#ifdef CONFIG_OF_GPIO - mgpio->gpio_chip.of_node = pdev->dev.parent->of_node; -#endif platform_set_drvdata(pdev, mgpio); -- cgit v1.2.3-58-ga151 From c607e5e2e4f4dc6e91da6fd81280f0474fdb6cd4 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 9 Jul 2020 20:12:01 +0300 Subject: gpio: max77620: Don't shadow error code of platform_get_irq() The platform_get_irq() returns a positive interrupt number on success and negative error code on failure (zero shouldn't ever happen in practice, it would produce a noisy warning). Hence let's return the error code directly instead of overriding it with -ENODEV. Suggested-by: Andy Shevchenko Signed-off-by: Dmitry Osipenko Reviewed-by: Andy Shevchenko Acked-by: Laxman Dewangan Link: https://lore.kernel.org/r/20200709171203.12950-5-digetx@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-max77620.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 39d431da2dbc..9121d2507f60 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -264,12 +264,14 @@ static int max77620_gpio_probe(struct platform_device *pdev) { struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent); struct max77620_gpio *mgpio; - int gpio_irq; + unsigned int gpio_irq; int ret; - gpio_irq = platform_get_irq(pdev, 0); - if (gpio_irq <= 0) - return -ENODEV; + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + + gpio_irq = ret; mgpio = devm_kzalloc(&pdev->dev, sizeof(*mgpio), GFP_KERNEL); if (!mgpio) -- cgit v1.2.3-58-ga151 From 15d9e7e847c04e7e118fc3edb969068b201bd0b3 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 9 Jul 2020 20:12:02 +0300 Subject: gpio: max77620: Use irqchip template This change addresses one of the GPIO-core TODOs for the MAX77620 driver which requires modern drivers to use the irqchip template. Instead of using the GPIO's irqchip-helpers for creating the IRQ domain, the gpio_irq_chip structure is now filled by the driver itself and then gpiochip_add_data() takes care of instantiating the IRQ domain for us. Suggested-by: Andy Shevchenko Signed-off-by: Dmitry Osipenko Reviewed-by: Andy Shevchenko Acked-by: Laxman Dewangan Link: https://lore.kernel.org/r/20200709171203.12950-6-digetx@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-max77620.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 9121d2507f60..6c516aa7732d 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -277,6 +277,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) if (!mgpio) return -ENOMEM; + mutex_init(&mgpio->buslock); mgpio->rmap = chip->rmap; mgpio->dev = &pdev->dev; @@ -291,6 +292,11 @@ static int max77620_gpio_probe(struct platform_device *pdev) mgpio->gpio_chip.can_sleep = 1; mgpio->gpio_chip.base = -1; + mgpio->gpio_chip.irq.chip = &max77620_gpio_irqchip; + mgpio->gpio_chip.irq.default_type = IRQ_TYPE_NONE; + mgpio->gpio_chip.irq.handler = handle_edge_irq; + mgpio->gpio_chip.irq.threaded = true; + platform_set_drvdata(pdev, mgpio); ret = devm_gpiochip_add_data(&pdev->dev, &mgpio->gpio_chip, mgpio); @@ -299,11 +305,6 @@ static int max77620_gpio_probe(struct platform_device *pdev) return ret; } - mutex_init(&mgpio->buslock); - - gpiochip_irqchip_add_nested(&mgpio->gpio_chip, &max77620_gpio_irqchip, - 0, handle_edge_irq, IRQ_TYPE_NONE); - ret = devm_request_threaded_irq(&pdev->dev, gpio_irq, NULL, max77620_gpio_irqhandler, IRQF_ONESHOT, "max77620-gpio", mgpio); @@ -312,9 +313,6 @@ static int max77620_gpio_probe(struct platform_device *pdev) return ret; } - gpiochip_set_nested_irqchip(&mgpio->gpio_chip, &max77620_gpio_irqchip, - gpio_irq); - return 0; } -- cgit v1.2.3-58-ga151 From e6827bc3faa4da29ddbf48f48d04e87ca7c1c3c7 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 9 Jul 2020 20:12:03 +0300 Subject: gpio: max77620: Initialize hardware state of interrupts I noticed on Nexus 7 that after rebooting from downstream kernel to upstream, the GPIO interrupt is triggering non-stop despite interrupts being disabled for all of GPIOs. This happens because Nexus 7 uses a soft-reboot, meaning that bootloader should take care of resetting hardware, but the bootloader doesn't do it well. As a result, GPIO interrupt may be left ON at a boot time. Let's mask all GPIO interrupts at the driver's initialization time in order to resolve the issue. Signed-off-by: Dmitry Osipenko Reviewed-by: Andy Shevchenko Acked-by: Laxman Dewangan Link: https://lore.kernel.org/r/20200709171203.12950-7-digetx@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-max77620.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 6c516aa7732d..e090979659eb 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -260,6 +260,30 @@ static int max77620_gpio_set_config(struct gpio_chip *gc, unsigned int offset, return -ENOTSUPP; } +static int max77620_gpio_irq_init_hw(struct gpio_chip *gc) +{ + struct max77620_gpio *gpio = gpiochip_get_data(gc); + unsigned int i; + int err; + + /* + * GPIO interrupts may be left ON after bootloader, hence let's + * pre-initialize hardware to the expected state by disabling all + * the interrupts. + */ + for (i = 0; i < MAX77620_GPIO_NR; i++) { + err = regmap_update_bits(gpio->rmap, GPIO_REG_ADDR(i), + MAX77620_CNFG_GPIO_INT_MASK, 0); + if (err < 0) { + dev_err(gpio->dev, + "failed to disable interrupt: %d\n", err); + return err; + } + } + + return 0; +} + static int max77620_gpio_probe(struct platform_device *pdev) { struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -295,6 +319,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) mgpio->gpio_chip.irq.chip = &max77620_gpio_irqchip; mgpio->gpio_chip.irq.default_type = IRQ_TYPE_NONE; mgpio->gpio_chip.irq.handler = handle_edge_irq; + mgpio->gpio_chip.irq.init_hw = max77620_gpio_irq_init_hw, mgpio->gpio_chip.irq.threaded = true; platform_set_drvdata(pdev, mgpio); -- cgit v1.2.3-58-ga151 From 16d44b6085c1d90884b264deb938a34ab85a9c6d Mon Sep 17 00:00:00 2001 From: Sungbo Eo Date: Thu, 9 Jul 2020 22:48:29 +0900 Subject: gpio: pca9570: add GPO driver for PCA9570 NXP PCA9570 is a 4-bit I2C GPO expander without interrupt functionality. Its ports are controlled only by a data byte without register address. Signed-off-by: Sungbo Eo Reviewed-by: Andy Shevchenko Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA9570.pdf Link: https://lore.kernel.org/r/20200709134829.216393-1-mans0n@gorani.run Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 8 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-pca9570.c | 146 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 drivers/gpio/gpio-pca9570.c (limited to 'drivers/gpio') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 05e0801c6a78..8030fd91a3cc 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -962,6 +962,14 @@ config GPIO_PCA953X_IRQ Say yes here to enable the pca953x to be used as an interrupt controller. It requires the driver to be built in the kernel. +config GPIO_PCA9570 + tristate "PCA9570 4-Bit I2C GPO expander" + help + Say yes here to enable the GPO driver for the NXP PCA9570 chip. + + To compile this driver as a module, choose M here: the module will + be called gpio-pca9570. + config GPIO_PCF857X tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders" select GPIOLIB_IRQCHIP diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ef666cfef9d0..4f9abff4f2dc 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -111,6 +111,7 @@ obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o +obj-$(CONFIG_GPIO_PCA9570) += gpio-pca9570.o obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PCIE_IDIO_24) += gpio-pcie-idio-24.o diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c new file mode 100644 index 000000000000..cb2b2f735c15 --- /dev/null +++ b/drivers/gpio/gpio-pca9570.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for PCA9570 I2C GPO expander + * + * Copyright (C) 2020 Sungbo Eo + * + * Based on gpio-tpic2810.c + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis + */ + +#include +#include +#include +#include +#include + +/** + * struct pca9570 - GPIO driver data + * @chip: GPIO controller chip + * @lock: Protects write sequences + * @out: Buffer for device register + */ +struct pca9570 { + struct gpio_chip chip; + struct mutex lock; + u8 out; +}; + +static int pca9570_read(struct pca9570 *gpio, u8 *value) +{ + struct i2c_client *client = to_i2c_client(gpio->chip.parent); + int ret; + + ret = i2c_smbus_read_byte(client); + if (ret < 0) + return ret; + + *value = ret; + return 0; +} + +static int pca9570_write(struct pca9570 *gpio, u8 value) +{ + struct i2c_client *client = to_i2c_client(gpio->chip.parent); + + return i2c_smbus_write_byte(client, value); +} + +static int pca9570_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + /* This device always output */ + return GPIO_LINE_DIRECTION_OUT; +} + +static int pca9570_get(struct gpio_chip *chip, unsigned offset) +{ + struct pca9570 *gpio = gpiochip_get_data(chip); + u8 buffer; + int ret; + + ret = pca9570_read(gpio, &buffer); + if (ret) + return ret; + + return !!(buffer & BIT(offset)); +} + +static void pca9570_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct pca9570 *gpio = gpiochip_get_data(chip); + u8 buffer; + int ret; + + mutex_lock(&gpio->lock); + + buffer = gpio->out; + if (value) + buffer |= BIT(offset); + else + buffer &= ~BIT(offset); + + ret = pca9570_write(gpio, buffer); + if (ret) + goto out; + + gpio->out = buffer; + +out: + mutex_unlock(&gpio->lock); +} + +static int pca9570_probe(struct i2c_client *client) +{ + struct pca9570 *gpio; + + gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->chip.label = client->name; + gpio->chip.parent = &client->dev; + gpio->chip.owner = THIS_MODULE; + gpio->chip.get_direction = pca9570_get_direction; + gpio->chip.get = pca9570_get; + gpio->chip.set = pca9570_set; + gpio->chip.base = -1; + gpio->chip.ngpio = (uintptr_t)device_get_match_data(&client->dev); + gpio->chip.can_sleep = true; + + mutex_init(&gpio->lock); + + /* Read the current output level */ + pca9570_read(gpio, &gpio->out); + + i2c_set_clientdata(client, gpio); + + return devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio); +} + +static const struct i2c_device_id pca9570_id_table[] = { + { "pca9570", 4 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, pca9570_id_table); + +static const struct of_device_id pca9570_of_match_table[] = { + { .compatible = "nxp,pca9570", .data = (void *)4 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pca9570_of_match_table); + +static struct i2c_driver pca9570_driver = { + .driver = { + .name = "pca9570", + .of_match_table = pca9570_of_match_table, + }, + .probe_new = pca9570_probe, + .id_table = pca9570_id_table, +}; +module_i2c_driver(pca9570_driver); + +MODULE_AUTHOR("Sungbo Eo "); +MODULE_DESCRIPTION("GPIO expander driver for PCA9570"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-58-ga151 From 761b5c30c206ba1788c6ceb23e5812f319548b52 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 13 Jul 2020 18:44:29 +0300 Subject: gpio: mmio: replace open-coded for_each_set_bit() Use for_each_set_bit() instead of open-coding it to simplify the code. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200713154429.23662-1-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mmio.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index b778f33cc6af..c335a0309ba3 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -195,8 +195,7 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, *bits &= ~*mask; /* Create a mirrored mask */ - bit = -1; - while ((bit = find_next_bit(mask, gc->ngpio, bit + 1)) < gc->ngpio) + for_each_set_bit(bit, mask, gc->ngpio) readmask |= bgpio_line2mask(gc, bit); /* Read the register */ @@ -206,8 +205,7 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, * Mirror the result into the "bits" result, this will give line 0 * in bit 0 ... line 31 in bit 31 for a 32bit register. */ - bit = -1; - while ((bit = find_next_bit(&val, gc->ngpio, bit + 1)) < gc->ngpio) + for_each_set_bit(bit, &val, gc->ngpio) *bits |= bgpio_line2mask(gc, bit); return 0; @@ -272,15 +270,11 @@ static void bgpio_multiple_get_masks(struct gpio_chip *gc, *set_mask = 0; *clear_mask = 0; - for (i = 0; i < gc->bgpio_bits; i++) { - if (*mask == 0) - break; - if (__test_and_clear_bit(i, mask)) { - if (test_bit(i, bits)) - *set_mask |= bgpio_line2mask(gc, i); - else - *clear_mask |= bgpio_line2mask(gc, i); - } + for_each_set_bit(i, mask, gc->bgpio_bits) { + if (test_bit(i, bits)) + *set_mask |= bgpio_line2mask(gc, i); + else + *clear_mask |= bgpio_line2mask(gc, i); } } -- cgit v1.2.3-58-ga151 From 80606cb24161d504acb4d89f406d68f72196575e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 16 Jul 2020 11:28:35 +0200 Subject: gpio: max77620: Use helper variable and clarify Most other drivers fill out the gpio_irq_chip using a struct gpio_irq_chip *girq helper variable for ease of reading. We also make a habit of explicitly assigning NULL and zero to the parent IRQs when using ordinary IRQ handlers in the driver, mostly for code readability and maintenance. Signed-off-by: Linus Walleij Reviewed-by: Dmitry Osipenko Cc: Dmitry Osipenko Link: https://lore.kernel.org/r/20200716092835.69176-1-linus.walleij@linaro.org --- drivers/gpio/gpio-max77620.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index e090979659eb..7c0a9ef0b500 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -288,6 +288,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) { struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent); struct max77620_gpio *mgpio; + struct gpio_irq_chip *girq; unsigned int gpio_irq; int ret; @@ -316,11 +317,16 @@ static int max77620_gpio_probe(struct platform_device *pdev) mgpio->gpio_chip.can_sleep = 1; mgpio->gpio_chip.base = -1; - mgpio->gpio_chip.irq.chip = &max77620_gpio_irqchip; - mgpio->gpio_chip.irq.default_type = IRQ_TYPE_NONE; - mgpio->gpio_chip.irq.handler = handle_edge_irq; - mgpio->gpio_chip.irq.init_hw = max77620_gpio_irq_init_hw, - mgpio->gpio_chip.irq.threaded = true; + girq = &mgpio->gpio_chip.irq; + girq->chip = &max77620_gpio_irqchip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_edge_irq; + girq->init_hw = max77620_gpio_irq_init_hw, + girq->threaded = true; platform_set_drvdata(pdev, mgpio); -- cgit v1.2.3-58-ga151 From 0fcfd9aa61034ac6f0352520379927bfc6576f1c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 16 Jul 2020 11:34:59 +0200 Subject: gpio: tc35892: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit calls to gpiochip_irqchip_add_nested() and gpiochip_set_nested_irqchip(). The irqchip is instead added while adding the gpiochip. Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20200716093459.76378-1-linus.walleij@linaro.org --- drivers/gpio/gpio-tc3589x.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 6be0684cfa49..58b0da9eb76f 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -289,6 +289,7 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent); struct device_node *np = pdev->dev.of_node; struct tc3589x_gpio *tc3589x_gpio; + struct gpio_irq_chip *girq; int ret; int irq; @@ -317,6 +318,16 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) tc3589x_gpio->chip.base = -1; tc3589x_gpio->chip.of_node = np; + girq = &tc3589x_gpio->chip.irq; + girq->chip = &tc3589x_gpio_irq_chip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; + /* Bring the GPIO module out of reset */ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_RSTCTRL_GPIRST, 0); @@ -339,21 +350,6 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) return ret; } - ret = gpiochip_irqchip_add_nested(&tc3589x_gpio->chip, - &tc3589x_gpio_irq_chip, - 0, - handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&pdev->dev, - "could not connect irqchip to gpiochip\n"); - return ret; - } - - gpiochip_set_nested_irqchip(&tc3589x_gpio->chip, - &tc3589x_gpio_irq_chip, - irq); - platform_set_drvdata(pdev, tc3589x_gpio); return 0; -- cgit v1.2.3-58-ga151 From 9745079609dfa14c8d88f88d06d7e720b0f29acd Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 16 Jul 2020 12:06:38 +0200 Subject: gpio: stmpe: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit calls to gpiochip_irqchip_add_nested() and gpiochip_set_nested_irqchip(). The irqchip is instead added while adding the gpiochip. Signed-off-by: Linus Walleij Cc: Patrice Chotard Cc: Alexandre TORGUE Link: https://lore.kernel.org/r/20200716100638.112451-1-linus.walleij@linaro.org --- drivers/gpio/gpio-stmpe.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 542706a852e6..395ee51445ea 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -507,6 +507,8 @@ static int stmpe_gpio_probe(struct platform_device *pdev) } if (irq > 0) { + struct gpio_irq_chip *girq; + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT, "stmpe-gpio", stmpe_gpio); @@ -514,20 +516,16 @@ static int stmpe_gpio_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to get irq: %d\n", ret); goto out_disable; } - ret = gpiochip_irqchip_add_nested(&stmpe_gpio->chip, - &stmpe_gpio_irq_chip, - 0, - handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&pdev->dev, - "could not connect irqchip to gpiochip\n"); - goto out_disable; - } - gpiochip_set_nested_irqchip(&stmpe_gpio->chip, - &stmpe_gpio_irq_chip, - irq); + girq = &stmpe_gpio->chip.irq; + girq->chip = &stmpe_gpio_irq_chip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; } platform_set_drvdata(pdev, stmpe_gpio); -- cgit v1.2.3-58-ga151 From 565a0e9ab8131a00f3317998722b9c0d67d7e016 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 16 Jul 2020 14:03:18 +0200 Subject: gpio: adnp: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit calls to gpiochip_irqchip_add_nested() and gpiochip_set_nested_irqchip(). The irqchip is instead added while adding the gpiochip. Signed-off-by: Linus Walleij Cc: Roland Stigge Cc: Lars Poeschel Link: https://lore.kernel.org/r/20200716120318.127176-1-linus.walleij@linaro.org --- drivers/gpio/gpio-adnp.c | 95 ++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 47 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index b9fcaab2a931..8eedfc6451df 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -238,36 +238,6 @@ unlock: mutex_unlock(&adnp->i2c_lock); } -static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios) -{ - struct gpio_chip *chip = &adnp->gpio; - int err; - - adnp->reg_shift = get_count_order(num_gpios) - 3; - - chip->direction_input = adnp_gpio_direction_input; - chip->direction_output = adnp_gpio_direction_output; - chip->get = adnp_gpio_get; - chip->set = adnp_gpio_set; - chip->can_sleep = true; - - if (IS_ENABLED(CONFIG_DEBUG_FS)) - chip->dbg_show = adnp_gpio_dbg_show; - - chip->base = -1; - chip->ngpio = num_gpios; - chip->label = adnp->client->name; - chip->parent = &adnp->client->dev; - chip->of_node = chip->parent->of_node; - chip->owner = THIS_MODULE; - - err = devm_gpiochip_add_data(&adnp->client->dev, chip, adnp); - if (err) - return err; - - return 0; -} - static irqreturn_t adnp_irq(int irq, void *data) { struct adnp *adnp = data; @@ -464,18 +434,54 @@ static int adnp_irq_setup(struct adnp *adnp) return err; } - err = gpiochip_irqchip_add_nested(chip, - &adnp_irq_chip, - 0, - handle_simple_irq, - IRQ_TYPE_NONE); - if (err) { - dev_err(chip->parent, - "could not connect irqchip to gpiochip\n"); - return err; + return 0; +} + +static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios, + bool is_irq_controller) +{ + struct gpio_chip *chip = &adnp->gpio; + int err; + + adnp->reg_shift = get_count_order(num_gpios) - 3; + + chip->direction_input = adnp_gpio_direction_input; + chip->direction_output = adnp_gpio_direction_output; + chip->get = adnp_gpio_get; + chip->set = adnp_gpio_set; + chip->can_sleep = true; + + if (IS_ENABLED(CONFIG_DEBUG_FS)) + chip->dbg_show = adnp_gpio_dbg_show; + + chip->base = -1; + chip->ngpio = num_gpios; + chip->label = adnp->client->name; + chip->parent = &adnp->client->dev; + chip->of_node = chip->parent->of_node; + chip->owner = THIS_MODULE; + + if (is_irq_controller) { + struct gpio_irq_chip *girq; + + err = adnp_irq_setup(adnp); + if (err) + return err; + + girq = &chip->irq; + girq->chip = &adnp_irq_chip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; } - gpiochip_set_nested_irqchip(chip, &adnp_irq_chip, adnp->client->irq); + err = devm_gpiochip_add_data(&adnp->client->dev, chip, adnp); + if (err) + return err; return 0; } @@ -503,16 +509,11 @@ static int adnp_i2c_probe(struct i2c_client *client, mutex_init(&adnp->i2c_lock); adnp->client = client; - err = adnp_gpio_setup(adnp, num_gpios); + err = adnp_gpio_setup(adnp, num_gpios, + of_property_read_bool(np, "interrupt-controller")); if (err) return err; - if (of_find_property(np, "interrupt-controller", NULL)) { - err = adnp_irq_setup(adnp); - if (err) - return err; - } - i2c_set_clientdata(client, adnp); return 0; -- cgit v1.2.3-58-ga151 From dfc3a26deb5c55db5d774b6ed83f6505285deb62 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 16 Jul 2020 17:05:02 +0200 Subject: gpio: adp5588: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit calls to gpiochip_irqchip_add_nested() and gpiochip_set_nested_irqchip(). The irqchip is instead added while adding the gpiochip. Signed-off-by: Linus Walleij Acked-by: Michael Hennerich Cc: Nikolaus Voss Cc: Michael Hennerich Link: https://lore.kernel.org/r/20200716150502.195821-1-linus.walleij@linaro.org --- drivers/gpio/gpio-adp5588.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index 49f423d7beba..f1e4ac90e7d3 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -272,13 +272,24 @@ static irqreturn_t adp5588_irq_handler(int irq, void *devid) return IRQ_HANDLED; } + +static int adp5588_irq_init_hw(struct gpio_chip *gc) +{ + struct adp5588_gpio *dev = gpiochip_get_data(gc); + /* Enable IRQs after registering chip */ + adp5588_gpio_write(dev->client, CFG, + ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN); + + return 0; +} + static int adp5588_irq_setup(struct adp5588_gpio *dev) { struct i2c_client *client = dev->client; int ret; struct adp5588_gpio_platform_data *pdata = dev_get_platdata(&client->dev); - int irq_base = pdata ? pdata->irq_base : 0; + struct gpio_irq_chip *girq; adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC); adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */ @@ -294,21 +305,19 @@ static int adp5588_irq_setup(struct adp5588_gpio *dev) client->irq); return ret; } - ret = gpiochip_irqchip_add_nested(&dev->gpio_chip, - &adp5588_irq_chip, irq_base, - handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&client->dev, - "could not connect irqchip to gpiochip\n"); - return ret; - } - gpiochip_set_nested_irqchip(&dev->gpio_chip, - &adp5588_irq_chip, - client->irq); - adp5588_gpio_write(client, CFG, - ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN); + /* This will be registered in the call to devm_gpiochip_add_data() */ + girq = &dev->gpio_chip.irq; + girq->chip = &adp5588_irq_chip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->first = pdata ? pdata->irq_base : 0; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->init_hw = adp5588_irq_init_hw; + girq->threaded = true; return 0; } -- cgit v1.2.3-58-ga151 From 50787be3ad732d804ee3ce828ee83b725e6491c8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 17 Jul 2020 16:48:35 +0200 Subject: gpio: pcf857x: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit calls to gpiochip_irqchip_add_nested() and gpiochip_set_nested_irqchip(). The irqchip is instead added while adding the gpiochip. Signed-off-by: Linus Walleij Cc: Geert Uytterhoeven Cc: Anders Darander Cc: Roger Quadros Link: https://lore.kernel.org/r/20200717144835.68150-1-linus.walleij@linaro.org --- drivers/gpio/gpio-pcf857x.c | 47 +++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 23 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 14fb8f6a1ad2..a2a8d155c75e 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -334,29 +334,19 @@ static int pcf857x_probe(struct i2c_client *client, gpio->out = ~n_latch; gpio->status = gpio->out; - status = devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio); - if (status < 0) - goto fail; - /* Enable irqchip if we have an interrupt */ if (client->irq) { - gpio->irqchip.name = "pcf857x", - gpio->irqchip.irq_enable = pcf857x_irq_enable, - gpio->irqchip.irq_disable = pcf857x_irq_disable, - gpio->irqchip.irq_ack = noop, - gpio->irqchip.irq_mask = noop, - gpio->irqchip.irq_unmask = noop, - gpio->irqchip.irq_set_wake = pcf857x_irq_set_wake, - gpio->irqchip.irq_bus_lock = pcf857x_irq_bus_lock, - gpio->irqchip.irq_bus_sync_unlock = pcf857x_irq_bus_sync_unlock, - status = gpiochip_irqchip_add_nested(&gpio->chip, - &gpio->irqchip, - 0, handle_level_irq, - IRQ_TYPE_NONE); - if (status) { - dev_err(&client->dev, "cannot add irqchip\n"); - goto fail; - } + struct gpio_irq_chip *girq; + + gpio->irqchip.name = "pcf857x"; + gpio->irqchip.irq_enable = pcf857x_irq_enable; + gpio->irqchip.irq_disable = pcf857x_irq_disable; + gpio->irqchip.irq_ack = noop; + gpio->irqchip.irq_mask = noop; + gpio->irqchip.irq_unmask = noop; + gpio->irqchip.irq_set_wake = pcf857x_irq_set_wake; + gpio->irqchip.irq_bus_lock = pcf857x_irq_bus_lock; + gpio->irqchip.irq_bus_sync_unlock = pcf857x_irq_bus_sync_unlock; status = devm_request_threaded_irq(&client->dev, client->irq, NULL, pcf857x_irq, IRQF_ONESHOT | @@ -365,10 +355,21 @@ static int pcf857x_probe(struct i2c_client *client, if (status) goto fail; - gpiochip_set_nested_irqchip(&gpio->chip, &gpio->irqchip, - client->irq); + girq = &gpio->chip.irq; + girq->chip = &gpio->irqchip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; + girq->threaded = true; } + status = devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio); + if (status < 0) + goto fail; + /* Let platform code set up the GPIOs and their users. * Now is the first time anyone could use them. */ -- cgit v1.2.3-58-ga151 From 75dec56710dfafd37daa95e756c5d1840932ba90 Mon Sep 17 00:00:00 2001 From: Drew Fustini Date: Fri, 17 Jul 2020 21:40:43 +0200 Subject: gpio: omap: handle pin config bias flags Modify omap_gpio_set_config() to handle pin config bias flags by calling gpiochip_generic_config(). The pin group for the gpio line must have the corresponding pinconf properties: PIN_CONFIG_BIAS_PULL_UP requires "pinctrl-single,bias-pullup" PIN_CONFIG_BIAS_PULL_DOWN requires "pinctrl-single,bias-pulldown" This is necessary for pcs_pinconf_set() to find the requested bias parameter in the PIN_MAP_TYPE_CONFIGS_GROUP pinctrl map. Signed-off-by: Drew Fustini Signed-off-by: Linus Walleij Acked-by: Grygorii Strashko Acked-by: Tony Lindgren Link: https://lore.kernel.org/r/20200715213738.1640030-1-drew@beagleboard.org Link: https://lore.kernel.org/r/20200717194043.1774643-1-drew@beagleboard.org --- drivers/gpio/gpio-omap.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index b3afe39027bd..e0eada82178c 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -897,12 +897,18 @@ static int omap_gpio_set_config(struct gpio_chip *chip, unsigned offset, unsigned long config) { u32 debounce; + int ret = -ENOTSUPP; + + if ((pinconf_to_config_param(config) == PIN_CONFIG_BIAS_DISABLE) || + (pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_UP) || + (pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_DOWN)) { + ret = gpiochip_generic_config(chip, offset, config); + } else if (pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE) { + debounce = pinconf_to_config_argument(config); + ret = omap_gpio_debounce(chip, offset, debounce); + } - if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) - return -ENOTSUPP; - - debounce = pinconf_to_config_argument(config); - return omap_gpio_debounce(chip, offset, debounce); + return ret; } static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -- cgit v1.2.3-58-ga151 From edee3bc6589484ec8c32b04c3b83497acfa6102f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 17 Jul 2020 16:40:40 +0200 Subject: gpio: pca953x: Use irqchip template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit calls to gpiochip_irqchip_add_nested() and gpiochip_set_nested_irqchip(). The irqchip is instead added while adding the gpiochip. Signed-off-by: Linus Walleij Tested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Cc: Andy Shevchenko Cc: Marek Vasut Cc: Uwe Kleine-König Cc: Adam Ford Cc: Vignesh Raghavendra Link: https://lore.kernel.org/r/20200717144040.63253-1-linus.walleij@linaro.org --- drivers/gpio/gpio-pca953x.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 9c90cf3aac5a..ab22152bf3e8 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -834,6 +834,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) struct irq_chip *irq_chip = &chip->irq_chip; DECLARE_BITMAP(reg_direction, MAX_LINE); DECLARE_BITMAP(irq_stat, MAX_LINE); + struct gpio_irq_chip *girq; int ret; if (dmi_first_match(pca953x_dmi_acpi_irq_info)) { @@ -883,16 +884,16 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) irq_chip->irq_set_type = pca953x_irq_set_type; irq_chip->irq_shutdown = pca953x_irq_shutdown; - ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, irq_chip, - irq_base, handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&client->dev, - "could not connect irqchip to gpiochip\n"); - return ret; - } - - gpiochip_set_nested_irqchip(&chip->gpio_chip, irq_chip, client->irq); + girq = &chip->gpio_chip.irq; + girq->chip = irq_chip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; + girq->first = irq_base; /* FIXME: get rid of this */ return 0; } @@ -1080,11 +1081,11 @@ static int pca953x_probe(struct i2c_client *client, if (ret) goto err_exit; - ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip); + ret = pca953x_irq_setup(chip, irq_base); if (ret) goto err_exit; - ret = pca953x_irq_setup(chip, irq_base); + ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip); if (ret) goto err_exit; -- cgit v1.2.3-58-ga151 From 0c106a23d6927533df560bce7096454b0c228833 Mon Sep 17 00:00:00 2001 From: Abanoub Sameh Date: Tue, 21 Jul 2020 16:50:45 +0200 Subject: gpio: pch: changed every 'unsigned' to 'unsigned int' Changed 'unsigned' to 'unsigned int'. This makes the code more uniform, and compliant with the kernel coding style. Signed-off-by: Abanoub Sameh Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-pch.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index adceee8263b2..1475d20bce54 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -95,7 +95,7 @@ struct pch_gpio { spinlock_t spinlock; }; -static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) +static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) { u32 reg_val; struct pch_gpio *chip = gpiochip_get_data(gpio); @@ -112,14 +112,14 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) spin_unlock_irqrestore(&chip->spinlock, flags); } -static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr) +static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr) { struct pch_gpio *chip = gpiochip_get_data(gpio); return !!(ioread32(&chip->reg->pi) & BIT(nr)); } -static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, +static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, int val) { struct pch_gpio *chip = gpiochip_get_data(gpio); @@ -146,7 +146,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, return 0; } -static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) { struct pch_gpio *chip = gpiochip_get_data(gpio); u32 pm; @@ -196,7 +196,7 @@ static void __maybe_unused pch_gpio_restore_reg_conf(struct pch_gpio *chip) iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg, &chip->reg->gpio_use_sel); } -static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset) +static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned int offset) { struct pch_gpio *chip = gpiochip_get_data(gpio); return chip->irq_base + offset; -- cgit v1.2.3-58-ga151 From 37ceab74f12f00900734109ebf9b7a6a7791a9d0 Mon Sep 17 00:00:00 2001 From: Abanoub Sameh Date: Tue, 21 Jul 2020 16:50:46 +0200 Subject: gpio: pch: Add a blank line between declaration and code Added a lined between a declaration and other statements according to the kernel coding style. Signed-off-by: Abanoub Sameh Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-pch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 1475d20bce54..a552df298a97 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -199,6 +199,7 @@ static void __maybe_unused pch_gpio_restore_reg_conf(struct pch_gpio *chip) static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned int offset) { struct pch_gpio *chip = gpiochip_get_data(gpio); + return chip->irq_base + offset; } -- cgit v1.2.3-58-ga151 From 3abbdbe3dcd39547478e8ff089026a315a124376 Mon Sep 17 00:00:00 2001 From: Abanoub Sameh Date: Tue, 21 Jul 2020 16:48:32 +0200 Subject: gpio: crystalcove: changed every 'unsigned' to 'unsigned int' Changed 'unsigned' to 'unsigned int'. This makes the code more uniform, and compliant with the kernel coding style. Signed-off-by: Abanoub Sameh Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-crystalcove.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 14d1f4c933b6..9391722ec107 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -129,7 +129,7 @@ static void crystalcove_update_irq_ctrl(struct crystalcove_gpio *cg, int gpio) regmap_update_bits(cg->regmap, reg, CTLI_INTCNT_BE, cg->intcnt_value); } -static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned gpio) +static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio) { struct crystalcove_gpio *cg = gpiochip_get_data(chip); int reg = to_reg(gpio, CTRL_OUT); @@ -140,7 +140,7 @@ static int crystalcove_gpio_dir_in(struct gpio_chip *chip, unsigned gpio) return regmap_write(cg->regmap, reg, CTLO_INPUT_SET); } -static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned gpio, +static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio, int value) { struct crystalcove_gpio *cg = gpiochip_get_data(chip); @@ -152,7 +152,7 @@ static int crystalcove_gpio_dir_out(struct gpio_chip *chip, unsigned gpio, return regmap_write(cg->regmap, reg, CTLO_OUTPUT_SET | value); } -static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned gpio) +static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) { struct crystalcove_gpio *cg = gpiochip_get_data(chip); unsigned int val; @@ -169,7 +169,7 @@ static int crystalcove_gpio_get(struct gpio_chip *chip, unsigned gpio) } static void crystalcove_gpio_set(struct gpio_chip *chip, - unsigned gpio, int value) + unsigned int gpio, int value) { struct crystalcove_gpio *cg = gpiochip_get_data(chip); int reg = to_reg(gpio, CTRL_OUT); @@ -183,7 +183,7 @@ static void crystalcove_gpio_set(struct gpio_chip *chip, regmap_update_bits(cg->regmap, reg, 1, 0); } -static int crystalcove_irq_type(struct irq_data *data, unsigned type) +static int crystalcove_irq_type(struct irq_data *data, unsigned int type) { struct crystalcove_gpio *cg = gpiochip_get_data(irq_data_get_irq_chip_data(data)); -- cgit v1.2.3-58-ga151 From 945e72db36bd12767601b332b2aa50c888537afa Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 21 Jul 2020 16:01:53 +0200 Subject: gpio: crystalcove: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit calls to gpiochip_irqchip_add_nested() and gpiochip_set_nested_irqchip(). The irqchip is instead added while adding the gpiochip. Cc: Andy Shevchenko Cc: Kuppuswamy Sathyanarayanan Cc: Hans de Goede Signed-off-by: Linus Walleij Reviewed-by: Kuppuswamy Sathyanarayanan Tested-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-crystalcove.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 9391722ec107..f60ff7579cd0 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -330,6 +330,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) int retval; struct device *dev = pdev->dev.parent; struct intel_soc_pmic *pmic = dev_get_drvdata(dev); + struct gpio_irq_chip *girq; if (irq < 0) return irq; @@ -353,14 +354,15 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) cg->chip.dbg_show = crystalcove_gpio_dbg_show; cg->regmap = pmic->regmap; - retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg); - if (retval) { - dev_warn(&pdev->dev, "add gpio chip error: %d\n", retval); - return retval; - } - - gpiochip_irqchip_add_nested(&cg->chip, &crystalcove_irqchip, 0, - handle_simple_irq, IRQ_TYPE_NONE); + girq = &cg->chip.irq; + girq->chip = &crystalcove_irqchip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; retval = request_threaded_irq(irq, NULL, crystalcove_gpio_irq_handler, IRQF_ONESHOT, KBUILD_MODNAME, cg); @@ -370,7 +372,11 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) return retval; } - gpiochip_set_nested_irqchip(&cg->chip, &crystalcove_irqchip, irq); + retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg); + if (retval) { + dev_warn(&pdev->dev, "add gpio chip error: %d\n", retval); + return retval; + } return 0; } -- cgit v1.2.3-58-ga151 From 22f61d4e6fea30bd571474c7ad335f8f1f609abf Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 17 Jul 2020 17:19:55 +0200 Subject: gpio: wcove: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit calls to gpiochip_irqchip_add_nested() and gpiochip_set_nested_irqchip(). The irqchip is instead added while adding the gpiochip. Cc: Bin Gao Cc: Andy Shevchenko Cc: Hans de Goede Signed-off-by: Linus Walleij Tested-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-wcove.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 8b481b3c1ebe..135645096575 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -400,6 +400,7 @@ static int wcove_gpio_probe(struct platform_device *pdev) struct wcove_gpio *wg; int virq, ret, irq; struct device *dev; + struct gpio_irq_chip *girq; /* * This gpio platform device is created by a mfd device (see @@ -442,19 +443,6 @@ static int wcove_gpio_probe(struct platform_device *pdev) wg->dev = dev; wg->regmap = pmic->regmap; - ret = devm_gpiochip_add_data(dev, &wg->chip, wg); - if (ret) { - dev_err(dev, "Failed to add gpiochip: %d\n", ret); - return ret; - } - - ret = gpiochip_irqchip_add_nested(&wg->chip, &wcove_irqchip, 0, - handle_simple_irq, IRQ_TYPE_NONE); - if (ret) { - dev_err(dev, "Failed to add irqchip: %d\n", ret); - return ret; - } - virq = regmap_irq_get_virq(wg->regmap_irq_chip, irq); if (virq < 0) { dev_err(dev, "Failed to get virq by irq %d\n", irq); @@ -468,7 +456,21 @@ static int wcove_gpio_probe(struct platform_device *pdev) return ret; } - gpiochip_set_nested_irqchip(&wg->chip, &wcove_irqchip, virq); + girq = &wg->chip.irq; + girq->chip = &wcove_irqchip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; + + ret = devm_gpiochip_add_data(dev, &wg->chip, wg); + if (ret) { + dev_err(dev, "Failed to add gpiochip: %d\n", ret); + return ret; + } /* Enable GPIO0 interrupts */ ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE, GPIO_IRQ0_MASK, -- cgit v1.2.3-58-ga151 From ae84f15c651ac47aee2e0301059025813a041d19 Mon Sep 17 00:00:00 2001 From: Abanoub Sameh Date: Tue, 21 Jul 2020 16:49:02 +0200 Subject: gpio: ich: changed every 'unsigned' to 'unsigned int' Changed 'unsigned' to 'unsigned int'. This makes the code more uniform, and compliant with the kernel coding style. Signed-off-by: Abanoub Sameh Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-ich.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index 9960bb8b0f5b..de56c013a658 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -74,8 +74,8 @@ struct ichx_desc { u32 use_sel_ignore[3]; /* Some chipsets have quirks, let these use their own request/get */ - int (*request)(struct gpio_chip *chip, unsigned offset); - int (*get)(struct gpio_chip *chip, unsigned offset); + int (*request)(struct gpio_chip *chip, unsigned int offset); + int (*get)(struct gpio_chip *chip, unsigned int offset); /* * Some chipsets don't let reading output values on GPIO_LVL register @@ -100,7 +100,7 @@ static int modparam_gpiobase = -1; /* dynamic */ module_param_named(gpiobase, modparam_gpiobase, int, 0444); MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default."); -static int ichx_write_bit(int reg, unsigned nr, int val, int verify) +static int ichx_write_bit(int reg, unsigned int nr, int val, int verify) { unsigned long flags; u32 data, tmp; @@ -132,7 +132,7 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify) return (verify && data != tmp) ? -EPERM : 0; } -static int ichx_read_bit(int reg, unsigned nr) +static int ichx_read_bit(int reg, unsigned int nr) { unsigned long flags; u32 data; @@ -152,12 +152,12 @@ static int ichx_read_bit(int reg, unsigned nr) return !!(data & BIT(bit)); } -static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr) +static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned int nr) { return !!(ichx_priv.use_gpio & BIT(nr / 32)); } -static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned nr) +static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned int nr) { if (ichx_read_bit(GPIO_IO_SEL, nr)) return GPIO_LINE_DIRECTION_IN; @@ -165,7 +165,7 @@ static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned nr) return GPIO_LINE_DIRECTION_OUT; } -static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) { /* * Try setting pin as an input and verify it worked since many pins @@ -174,7 +174,7 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) return ichx_write_bit(GPIO_IO_SEL, nr, 1, 1); } -static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, +static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, int val) { /* Disable blink hardware which is available for GPIOs from 0 to 31. */ @@ -191,12 +191,12 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, return ichx_write_bit(GPIO_IO_SEL, nr, 0, 1); } -static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr) +static int ichx_gpio_get(struct gpio_chip *chip, unsigned int nr) { return ichx_read_bit(GPIO_LVL, nr); } -static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr) +static int ich6_gpio_get(struct gpio_chip *chip, unsigned int nr) { unsigned long flags; u32 data; @@ -223,7 +223,7 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr) } } -static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr) +static int ichx_gpio_request(struct gpio_chip *chip, unsigned int nr) { if (!ichx_gpio_check_available(chip, nr)) return -ENXIO; @@ -240,7 +240,7 @@ static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr) return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV; } -static int ich6_gpio_request(struct gpio_chip *chip, unsigned nr) +static int ich6_gpio_request(struct gpio_chip *chip, unsigned int nr) { /* * Fixups for bits 16 and 17 are necessary on the Intel ICH6/3100 @@ -254,7 +254,7 @@ static int ich6_gpio_request(struct gpio_chip *chip, unsigned nr) return ichx_gpio_request(chip, nr); } -static void ichx_gpio_set(struct gpio_chip *chip, unsigned nr, int val) +static void ichx_gpio_set(struct gpio_chip *chip, unsigned int nr, int val) { ichx_write_bit(GPIO_LVL, nr, val, 0); } -- cgit v1.2.3-58-ga151 From 2c58e44a428984167618bcf7c75ceb635f9dee5d Mon Sep 17 00:00:00 2001 From: Abanoub Sameh Date: Tue, 21 Jul 2020 16:51:03 +0200 Subject: gpio: sch: changed every 'unsigned' to 'unsigned int' Changed 'unsigned' to 'unsigned int'. This makes the code more uniform, and compliant with the kernel coding style. Signed-off-by: Abanoub Sameh Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-sch.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index c65f35b68202..d7cade67717b 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -26,10 +26,10 @@ struct sch_gpio { unsigned short resume_base; }; -static unsigned sch_gpio_offset(struct sch_gpio *sch, unsigned gpio, - unsigned reg) +static unsigned int sch_gpio_offset(struct sch_gpio *sch, unsigned int gpio, + unsigned int reg) { - unsigned base = 0; + unsigned int base = 0; if (gpio >= sch->resume_base) { gpio -= sch->resume_base; @@ -39,14 +39,14 @@ static unsigned sch_gpio_offset(struct sch_gpio *sch, unsigned gpio, return base + reg + gpio / 8; } -static unsigned sch_gpio_bit(struct sch_gpio *sch, unsigned gpio) +static unsigned int sch_gpio_bit(struct sch_gpio *sch, unsigned int gpio) { if (gpio >= sch->resume_base) gpio -= sch->resume_base; return gpio % 8; } -static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned gpio, unsigned reg) +static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned int gpio, unsigned int reg) { unsigned short offset, bit; u8 reg_val; @@ -59,7 +59,7 @@ static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned gpio, unsigned reg) return reg_val; } -static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned gpio, unsigned reg, +static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned int gpio, unsigned int reg, int val) { unsigned short offset, bit; @@ -76,7 +76,7 @@ static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned gpio, unsigned reg, outb((reg_val & ~BIT(bit)), sch->iobase + offset); } -static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num) +static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) { struct sch_gpio *sch = gpiochip_get_data(gc); @@ -86,13 +86,13 @@ static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num) return 0; } -static int sch_gpio_get(struct gpio_chip *gc, unsigned gpio_num) +static int sch_gpio_get(struct gpio_chip *gc, unsigned int gpio_num) { struct sch_gpio *sch = gpiochip_get_data(gc); return sch_gpio_reg_get(sch, gpio_num, GLV); } -static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val) +static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) { struct sch_gpio *sch = gpiochip_get_data(gc); @@ -101,7 +101,7 @@ static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val) spin_unlock(&sch->lock); } -static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num, +static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, int val) { struct sch_gpio *sch = gpiochip_get_data(gc); @@ -123,7 +123,7 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num, return 0; } -static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned gpio_num) +static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num) { struct sch_gpio *sch = gpiochip_get_data(gc); -- cgit v1.2.3-58-ga151 From 4941b8dedb3f33dcca5c6aef919ea518ff8947ce Mon Sep 17 00:00:00 2001 From: Abanoub Sameh Date: Tue, 21 Jul 2020 16:51:04 +0200 Subject: gpio: sch: Add a blank line between declaration and code Added a lined between a declaration and other statements according to the kernel coding style. Signed-off-by: Abanoub Sameh Signed-off-by: Andy Shevchenko --- drivers/gpio/gpio-sch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index d7cade67717b..3a1b1adb08c6 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -89,6 +89,7 @@ static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) static int sch_gpio_get(struct gpio_chip *gc, unsigned int gpio_num) { struct sch_gpio *sch = gpiochip_get_data(gc); + return sch_gpio_reg_get(sch, gpio_num, GLV); } -- cgit v1.2.3-58-ga151 From ecb55df8f3a8f30cb188a8bd3c9d0b977eb92e09 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Jul 2020 09:34:26 +0200 Subject: gpio: dln2: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit calls to gpiochip_irqchip_add(). The irqchip is instead added while adding the gpiochip. Signed-off-by: Linus Walleij Tested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Reviewed-by: Daniel Baluta Cc: Daniel Baluta Cc: Octavian Purdila Link: https://lore.kernel.org/r/20200722073426.38890-1-linus.walleij@linaro.org --- drivers/gpio/gpio-dln2.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c index 26b40c8b8a12..4c5f6d0c8d74 100644 --- a/drivers/gpio/gpio-dln2.c +++ b/drivers/gpio/gpio-dln2.c @@ -440,6 +440,7 @@ static int dln2_gpio_probe(struct platform_device *pdev) { struct dln2_gpio *dln2; struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; int pins; int ret; @@ -476,6 +477,15 @@ static int dln2_gpio_probe(struct platform_device *pdev) dln2->gpio.direction_output = dln2_gpio_direction_output; dln2->gpio.set_config = dln2_gpio_set_config; + girq = &dln2->gpio.irq; + girq->chip = &dln2_gpio_irqchip; + /* The event comes from the outside so no parent handler */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + platform_set_drvdata(pdev, dln2); ret = devm_gpiochip_add_data(dev, &dln2->gpio, dln2); @@ -484,13 +494,6 @@ static int dln2_gpio_probe(struct platform_device *pdev) return ret; } - ret = gpiochip_irqchip_add(&dln2->gpio, &dln2_gpio_irqchip, 0, - handle_simple_irq, IRQ_TYPE_NONE); - if (ret < 0) { - dev_err(dev, "failed to add irq chip: %d\n", ret); - return ret; - } - ret = dln2_register_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV, dln2_gpio_event); if (ret) { -- cgit v1.2.3-58-ga151 From bde8c0e64c78633612aaf283692f72bef0bbc549 Mon Sep 17 00:00:00 2001 From: Drew Fustini Date: Wed, 22 Jul 2020 14:07:56 +0200 Subject: gpio: omap: improve coding style for pin config flags Change the handling of pin config flags from if/else to switch statement to make the code more readable and cleaner. Suggested-by: Gustavo A. R. Silva Signed-off-by: Drew Fustini Acked-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200722120755.230741-1-drew@beagleboard.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index e0eada82178c..7fbe0c9e1fc1 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -899,13 +899,18 @@ static int omap_gpio_set_config(struct gpio_chip *chip, unsigned offset, u32 debounce; int ret = -ENOTSUPP; - if ((pinconf_to_config_param(config) == PIN_CONFIG_BIAS_DISABLE) || - (pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_UP) || - (pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_DOWN)) { + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: ret = gpiochip_generic_config(chip, offset, config); - } else if (pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE) { + break; + case PIN_CONFIG_INPUT_DEBOUNCE: debounce = pinconf_to_config_argument(config); ret = omap_gpio_debounce(chip, offset, debounce); + break; + default: + break; } return ret; -- cgit v1.2.3-58-ga151 From fceb7ab3854b7b8d13d771b9164ef0be546af673 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Jul 2020 12:19:38 +0200 Subject: gpio: ws16c48: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit call to gpiochip_irqchip_add(). The irqchip is instead added while adding the gpiochip. Also move the IRQ initialization to the special .init_hw() callback. Signed-off-by: Linus Walleij Acked-by: William Breathitt Gray Cc: William Breathitt Gray Link: https://lore.kernel.org/r/20200722101938.151265-1-linus.walleij@linaro.org --- drivers/gpio/gpio-ws16c48.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index cb510df2b014..2d89d0529135 100644 --- a/drivers/gpio/gpio-ws16c48.c +++ b/drivers/gpio/gpio-ws16c48.c @@ -365,10 +365,25 @@ static const char *ws16c48_names[WS16C48_NGPIO] = { "Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7" }; +static int ws16c48_irq_init_hw(struct gpio_chip *gc) +{ + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc); + + /* Disable IRQ by default */ + outb(0x80, ws16c48gpio->base + 7); + outb(0, ws16c48gpio->base + 8); + outb(0, ws16c48gpio->base + 9); + outb(0, ws16c48gpio->base + 10); + outb(0xC0, ws16c48gpio->base + 7); + + return 0; +} + static int ws16c48_probe(struct device *dev, unsigned int id) { struct ws16c48_gpio *ws16c48gpio; const char *const name = dev_name(dev); + struct gpio_irq_chip *girq; int err; ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL); @@ -396,6 +411,16 @@ static int ws16c48_probe(struct device *dev, unsigned int id) ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple; ws16c48gpio->base = base[id]; + girq = &ws16c48gpio->chip.irq; + girq->chip = &ws16c48_irqchip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_edge_irq; + girq->init_hw = ws16c48_irq_init_hw; + raw_spin_lock_init(&ws16c48gpio->lock); err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio); @@ -404,20 +429,6 @@ static int ws16c48_probe(struct device *dev, unsigned int id) return err; } - /* Disable IRQ by default */ - outb(0x80, base[id] + 7); - outb(0, base[id] + 8); - outb(0, base[id] + 9); - outb(0, base[id] + 10); - outb(0xC0, base[id] + 7); - - err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); - if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); - return err; - } - err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED, name, ws16c48gpio); if (err) { -- cgit v1.2.3-58-ga151 From 2fa1d392cdf49d9785f2fc67acca3c8db9a99036 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Jul 2020 12:39:15 +0200 Subject: gpio: 104-dio-48e: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit call to gpiochip_irqchip_add(). The irqchip is instead added while adding the gpiochip. Also move the IRQ initialization to the special .init_hw() callback. Signed-off-by: Linus Walleij Acked-by: William Breathitt Gray Cc: William Breathitt Gray Link: https://lore.kernel.org/r/20200722103915.162156-1-linus.walleij@linaro.org --- drivers/gpio/gpio-104-dio-48e.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index 1f7d9bbec0fc..7a9021c4fa48 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -368,10 +368,21 @@ static const char *dio48e_names[DIO48E_NGPIO] = { "PPI Group 1 Port C 5", "PPI Group 1 Port C 6", "PPI Group 1 Port C 7" }; +static int dio48e_irq_init_hw(struct gpio_chip *gc) +{ + struct dio48e_gpio *const dio48egpio = gpiochip_get_data(gc); + + /* Disable IRQ by default */ + inb(dio48egpio->base + 0xB); + + return 0; +} + static int dio48e_probe(struct device *dev, unsigned int id) { struct dio48e_gpio *dio48egpio; const char *const name = dev_name(dev); + struct gpio_irq_chip *girq; int err; dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL); @@ -399,13 +410,17 @@ static int dio48e_probe(struct device *dev, unsigned int id) dio48egpio->chip.set_multiple = dio48e_gpio_set_multiple; dio48egpio->base = base[id]; - raw_spin_lock_init(&dio48egpio->lock); + girq = &dio48egpio->chip.irq; + girq->chip = &dio48e_irqchip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_edge_irq; + girq->init_hw = dio48e_irq_init_hw; - err = devm_gpiochip_add_data(dev, &dio48egpio->chip, dio48egpio); - if (err) { - dev_err(dev, "GPIO registering failed (%d)\n", err); - return err; - } + raw_spin_lock_init(&dio48egpio->lock); /* initialize all GPIO as output */ outb(0x80, base[id] + 3); @@ -419,13 +434,9 @@ static int dio48e_probe(struct device *dev, unsigned int id) outb(0x00, base[id] + 6); outb(0x00, base[id] + 7); - /* disable IRQ by default */ - inb(base[id] + 0xB); - - err = gpiochip_irqchip_add(&dio48egpio->chip, &dio48e_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); + err = devm_gpiochip_add_data(dev, &dio48egpio->chip, dio48egpio); if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); + dev_err(dev, "GPIO registering failed (%d)\n", err); return err; } -- cgit v1.2.3-58-ga151 From 44b01cf5d24286fe54301dca7a58afb8d01a1deb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Jul 2020 12:48:20 +0200 Subject: gpio: 104-idi-48: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit call to gpiochip_irqchip_add(). The irqchip is instead added while adding the gpiochip. Also move the IRQ initialization to the special .init_hw() callback. Signed-off-by: Linus Walleij Acked-by: William Breathitt Gray Cc: William Breathitt Gray Link: https://lore.kernel.org/r/20200722104820.174654-1-linus.walleij@linaro.org --- drivers/gpio/gpio-104-idi-48.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index d350ac0de06b..94c3a9bc4e75 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -247,10 +247,22 @@ static const char *idi48_names[IDI48_NGPIO] = { "Bit 18 B", "Bit 19 B", "Bit 20 B", "Bit 21 B", "Bit 22 B", "Bit 23 B" }; +static int idi_48_irq_init_hw(struct gpio_chip *gc) +{ + struct idi_48_gpio *const idi48gpio = gpiochip_get_data(gc); + + /* Disable IRQ by default */ + outb(0, idi48gpio->base + 7); + inb(idi48gpio->base + 7); + + return 0; +} + static int idi_48_probe(struct device *dev, unsigned int id) { struct idi_48_gpio *idi48gpio; const char *const name = dev_name(dev); + struct gpio_irq_chip *girq; int err; idi48gpio = devm_kzalloc(dev, sizeof(*idi48gpio), GFP_KERNEL); @@ -275,6 +287,16 @@ static int idi_48_probe(struct device *dev, unsigned int id) idi48gpio->chip.get_multiple = idi_48_gpio_get_multiple; idi48gpio->base = base[id]; + girq = &idi48gpio->chip.irq; + girq->chip = &idi_48_irqchip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_edge_irq; + girq->init_hw = idi_48_irq_init_hw; + raw_spin_lock_init(&idi48gpio->lock); spin_lock_init(&idi48gpio->ack_lock); @@ -284,17 +306,6 @@ static int idi_48_probe(struct device *dev, unsigned int id) return err; } - /* Disable IRQ by default */ - outb(0, base[id] + 7); - inb(base[id] + 7); - - err = gpiochip_irqchip_add(&idi48gpio->chip, &idi_48_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); - if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); - return err; - } - err = devm_request_irq(dev, irq[id], idi_48_irq_handler, IRQF_SHARED, name, idi48gpio); if (err) { -- cgit v1.2.3-58-ga151 From 82e4613d3d14c6551dd6d96896fd1b9e966afbab Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Jul 2020 12:55:17 +0200 Subject: gpio: 104-idio-16: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit call to gpiochip_irqchip_add(). The irqchip is instead added while adding the gpiochip. Also move the IRQ initialization to the special .init_hw() callback. Signed-off-by: Linus Walleij Acked-by: William Breathitt Gray Cc: William Breathitt Gray Link: https://lore.kernel.org/r/20200722105517.186137-1-linus.walleij@linaro.org --- drivers/gpio/gpio-104-idio-16.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index 5752d9dab148..50ad0280fd78 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -224,10 +224,22 @@ static const char *idio_16_names[IDIO_16_NGPIO] = { "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15" }; +static int idio_16_irq_init_hw(struct gpio_chip *gc) +{ + struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc); + + /* Disable IRQ by default */ + outb(0, idio16gpio->base + 2); + outb(0, idio16gpio->base + 1); + + return 0; +} + static int idio_16_probe(struct device *dev, unsigned int id) { struct idio_16_gpio *idio16gpio; const char *const name = dev_name(dev); + struct gpio_irq_chip *girq; int err; idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); @@ -256,6 +268,16 @@ static int idio_16_probe(struct device *dev, unsigned int id) idio16gpio->base = base[id]; idio16gpio->out_state = 0xFFFF; + girq = &idio16gpio->chip.irq; + girq->chip = &idio_16_irqchip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_edge_irq; + girq->init_hw = idio_16_irq_init_hw; + raw_spin_lock_init(&idio16gpio->lock); err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio); @@ -264,17 +286,6 @@ static int idio_16_probe(struct device *dev, unsigned int id) return err; } - /* Disable IRQ by default */ - outb(0, base[id] + 2); - outb(0, base[id] + 1); - - err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); - if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); - return err; - } - err = devm_request_irq(dev, irq[id], idio_16_irq_handler, 0, name, idio16gpio); if (err) { -- cgit v1.2.3-58-ga151 From 866e863edb9bdffb06b216908998d4639a7b165d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Jul 2020 13:00:27 +0200 Subject: gpio: pcie-idio-24: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit call to gpiochip_irqchip_add(). The irqchip is instead added while adding the gpiochip. Signed-off-by: Linus Walleij Acked-by: William Breathitt Gray Cc: William Breathitt Gray Link: https://lore.kernel.org/r/20200722110027.192782-1-linus.walleij@linaro.org --- drivers/gpio/gpio-pcie-idio-24.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c index 1d475794a50f..a68941d19ac6 100644 --- a/drivers/gpio/gpio-pcie-idio-24.c +++ b/drivers/gpio/gpio-pcie-idio-24.c @@ -457,6 +457,7 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id) int err; const size_t pci_bar_index = 2; const char *const name = pci_name(pdev); + struct gpio_irq_chip *girq; idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL); if (!idio24gpio) @@ -490,6 +491,15 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id) idio24gpio->chip.set = idio_24_gpio_set; idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple; + girq = &idio24gpio->chip.irq; + girq->chip = &idio_24_irqchip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_edge_irq; + raw_spin_lock_init(&idio24gpio->lock); /* Software board reset */ @@ -501,13 +511,6 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id) return err; } - err = gpiochip_irqchip_add(&idio24gpio->chip, &idio_24_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); - if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); - return err; - } - err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED, name, idio24gpio); if (err) { -- cgit v1.2.3-58-ga151 From 4530a840e74eface949dc4255b66ad640c0521b9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Jul 2020 13:06:49 +0200 Subject: gpio: pci-idio-16: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit call to gpiochip_irqchip_add(). The irqchip is instead added while adding the gpiochip. Also move the IRQ initialization to the special .init_hw() callback. Signed-off-by: Linus Walleij Acked-by: William Breathitt Gray Cc: William Breathitt Gray Link: https://lore.kernel.org/r/20200722110649.202223-1-linus.walleij@linaro.org --- drivers/gpio/gpio-pci-idio-16.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c index 638d6656ce73..9acec76e0b51 100644 --- a/drivers/gpio/gpio-pci-idio-16.c +++ b/drivers/gpio/gpio-pci-idio-16.c @@ -280,6 +280,17 @@ static const char *idio_16_names[IDIO_16_NGPIO] = { "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15" }; +static int idio_16_irq_init_hw(struct gpio_chip *gc) +{ + struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc); + + /* Disable IRQ by default and clear any pending interrupt */ + iowrite8(0, &idio16gpio->reg->irq_ctl); + iowrite8(0, &idio16gpio->reg->in0_7); + + return 0; +} + static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct device *const dev = &pdev->dev; @@ -287,6 +298,7 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) int err; const size_t pci_bar_index = 2; const char *const name = pci_name(pdev); + struct gpio_irq_chip *girq; idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); if (!idio16gpio) @@ -323,6 +335,16 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) idio16gpio->chip.set = idio_16_gpio_set; idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple; + girq = &idio16gpio->chip.irq; + girq->chip = &idio_16_irqchip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_edge_irq; + girq->init_hw = idio_16_irq_init_hw; + raw_spin_lock_init(&idio16gpio->lock); err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio); @@ -331,17 +353,6 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) return err; } - /* Disable IRQ by default and clear any pending interrupt */ - iowrite8(0, &idio16gpio->reg->irq_ctl); - iowrite8(0, &idio16gpio->reg->in0_7); - - err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0, - handle_edge_irq, IRQ_TYPE_NONE); - if (err) { - dev_err(dev, "Could not add irqchip (%d)\n", err); - return err; - } - err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED, name, idio16gpio); if (err) { -- cgit v1.2.3-58-ga151 From b470cef1ffac1acc327fe3343a2ce3ef3a526a21 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Jul 2020 13:31:41 +0200 Subject: gpio: rcar: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit call to gpiochip_irqchip_add(). The irqchip is instead added while adding the gpiochip. Signed-off-by: Linus Walleij Cc: Biju Das Cc: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200722113141.243163-1-linus.walleij@linaro.org --- drivers/gpio/gpio-rcar.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index eac1582c70da..3ef19cef8da9 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -438,6 +438,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) struct resource *irq; struct gpio_chip *gpio_chip; struct irq_chip *irq_chip; + struct gpio_irq_chip *girq; struct device *dev = &pdev->dev; const char *name = dev_name(dev); unsigned int npins; @@ -496,19 +497,21 @@ static int gpio_rcar_probe(struct platform_device *pdev) irq_chip->irq_set_wake = gpio_rcar_irq_set_wake; irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND; + girq = &gpio_chip->irq; + girq->chip = irq_chip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; + ret = gpiochip_add_data(gpio_chip, p); if (ret) { dev_err(dev, "failed to add GPIO controller\n"); goto err0; } - ret = gpiochip_irqchip_add(gpio_chip, irq_chip, 0, handle_level_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(dev, "cannot add irqchip\n"); - goto err1; - } - p->irq_parent = irq->start; if (devm_request_irq(dev, irq->start, gpio_rcar_irq_handler, IRQF_SHARED, name, p)) { -- cgit v1.2.3-58-ga151 From beb3fb41659a3cff2121f126a83fd7167b601a63 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 28 Jul 2020 09:27:06 +0200 Subject: gpio: stmpe: Move chip registration Make sure to register the GPIO chip after requesting the interrupt and setting up the IRQ members of the irqchip. Fixes: 9745079609df ("gpio: stmpe: Use irqchip template") Reported-by: Serge Semin Signed-off-by: Linus Walleij Cc: Patrice Chotard Link: https://lore.kernel.org/r/20200728072706.348725-1-linus.walleij@linaro.org --- drivers/gpio/gpio-stmpe.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 395ee51445ea..6c48809d0505 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -500,12 +500,6 @@ static int stmpe_gpio_probe(struct platform_device *pdev) if (ret) goto out_free; - ret = gpiochip_add_data(&stmpe_gpio->chip, stmpe_gpio); - if (ret) { - dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); - goto out_disable; - } - if (irq > 0) { struct gpio_irq_chip *girq; @@ -528,6 +522,12 @@ static int stmpe_gpio_probe(struct platform_device *pdev) girq->threaded = true; } + ret = gpiochip_add_data(&stmpe_gpio->chip, stmpe_gpio); + if (ret) { + dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); + goto out_disable; + } + platform_set_drvdata(pdev, stmpe_gpio); return 0; -- cgit v1.2.3-58-ga151 From bb58a47a120b888de6b996472b282fea02210f38 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 27 Jul 2020 00:12:59 +0200 Subject: gpio: max732x: Use irqchip template This makes the driver use the irqchip template to assign properties to the gpio_irq_chip instead of using the explicit calls to gpiochip_irqchip_add_nested() and gpiochip_set_nested_irqchip(). The irqchip is instead added while adding the gpiochip. Signed-off-by: Linus Walleij Tested-by: Sam Protsenko Reviewed-by: Sam Protsenko Cc: Sam Protsenko Link: https://lore.kernel.org/r/20200726221259.133536-1-linus.walleij@linaro.org --- drivers/gpio/gpio-max732x.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 63472f308857..238cbe926b9f 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -503,6 +503,8 @@ static int max732x_irq_setup(struct max732x_chip *chip, if (((pdata && pdata->irq_base) || client->irq) && has_irq != INT_NONE) { + struct gpio_irq_chip *girq; + if (pdata) irq_base = pdata->irq_base; chip->irq_features = has_irq; @@ -517,19 +519,17 @@ static int max732x_irq_setup(struct max732x_chip *chip, client->irq); return ret; } - ret = gpiochip_irqchip_add_nested(&chip->gpio_chip, - &max732x_irq_chip, - irq_base, - handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&client->dev, - "could not connect irqchip to gpiochip\n"); - return ret; - } - gpiochip_set_nested_irqchip(&chip->gpio_chip, - &max732x_irq_chip, - client->irq); + + girq = &chip->gpio_chip.irq; + girq->chip = &max732x_irq_chip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; + girq->first = irq_base; /* FIXME: get rid of this */ } return 0; @@ -695,11 +695,11 @@ static int max732x_probe(struct i2c_client *client, return ret; } - ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip); + ret = max732x_irq_setup(chip, id); if (ret) return ret; - ret = max732x_irq_setup(chip, id); + ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip); if (ret) return ret; -- cgit v1.2.3-58-ga151 From 5f402bb17533113c21d61c2d4bc4ef4a6fa1c9a5 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Fri, 31 Jul 2020 14:38:36 +0200 Subject: gpio: don't use same lockdep class for all devm_gpiochip_add_data users Commit 959bc7b22bd2 ("gpio: Automatically add lockdep keys") documents in its commits message its intention to "create a unique class key for each driver". It does so by having gpiochip_add_data add in-place the definition of two static lockdep classes for LOCKDEP use. That way, every caller of the macro adds their gpiochip with unique lockdep classes. There are many indirect callers of gpiochip_add_data, however, via use of devm_gpiochip_add_data. devm_gpiochip_add_data has external linkage and all its users will share the same lockdep classes, which probably is not intended. Fix this by replicating the gpio_chip_add_data statics-in-macro for the devm_ version as well. Fixes: 959bc7b22bd2 ("gpio: Automatically add lockdep keys") Signed-off-by: Ahmad Fatoum Reviewed-by: Andy Shevchenko Reviewed-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20200731123835.8003-1-a.fatoum@pengutronix.de Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-devres.c | 13 ++++++++----- include/linux/gpio/driver.h | 13 +++++++++++-- 2 files changed, 19 insertions(+), 7 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 5c91c4365da1..7dbce4c4ebdf 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -487,10 +487,12 @@ static void devm_gpio_chip_release(struct device *dev, void *res) } /** - * devm_gpiochip_add_data() - Resource managed gpiochip_add_data() + * devm_gpiochip_add_data_with_key() - Resource managed gpiochip_add_data_with_key() * @dev: pointer to the device that gpio_chip belongs to. * @gc: the GPIO chip to register * @data: driver-private data associated with this chip + * @lock_key: lockdep class for IRQ lock + * @request_key: lockdep class for IRQ request * * Context: potentially before irqs will work * @@ -501,8 +503,9 @@ static void devm_gpio_chip_release(struct device *dev, void *res) * gc->base is invalid or already associated with a different chip. * Otherwise it returns zero as a success code. */ -int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *gc, - void *data) +int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, void *data, + struct lock_class_key *lock_key, + struct lock_class_key *request_key) { struct gpio_chip **ptr; int ret; @@ -512,7 +515,7 @@ int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *gc, if (!ptr) return -ENOMEM; - ret = gpiochip_add_data(gc, data); + ret = gpiochip_add_data_with_key(gc, data, lock_key, request_key); if (ret < 0) { devres_free(ptr); return ret; @@ -523,4 +526,4 @@ int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *gc, return 0; } -EXPORT_SYMBOL_GPL(devm_gpiochip_add_data); +EXPORT_SYMBOL_GPL(devm_gpiochip_add_data_with_key); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 6e9f1826ecd7..d1cef5c2715c 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -525,8 +525,16 @@ extern int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gpiochip_add_data_with_key(gc, data, &lock_key, \ &request_key); \ }) +#define devm_gpiochip_add_data(dev, gc, data) ({ \ + static struct lock_class_key lock_key; \ + static struct lock_class_key request_key; \ + devm_gpiochip_add_data_with_key(dev, gc, data, &lock_key, \ + &request_key); \ + }) #else #define gpiochip_add_data(gc, data) gpiochip_add_data_with_key(gc, data, NULL, NULL) +#define devm_gpiochip_add_data(dev, gc, data) \ + devm_gpiochip_add_data_with_key(dev, gc, data, NULL, NULL) #endif /* CONFIG_LOCKDEP */ static inline int gpiochip_add(struct gpio_chip *gc) @@ -534,8 +542,9 @@ static inline int gpiochip_add(struct gpio_chip *gc) return gpiochip_add_data(gc, NULL); } extern void gpiochip_remove(struct gpio_chip *gc); -extern int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *gc, - void *data); +extern int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, void *data, + struct lock_class_key *lock_key, + struct lock_class_key *request_key); extern struct gpio_chip *gpiochip_find(void *data, int (*match)(struct gpio_chip *gc, void *data)); -- cgit v1.2.3-58-ga151 From 2a93a0da06f8fcee96a7c49c756fd7c5891b0b0d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Jul 2020 15:55:02 +0300 Subject: gpio: pca953x: Request IRQ after all initialisation done There is logically better to request IRQ when we initialise all structures. Align the driver with the rest on the same matter. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200728125504.27786-1-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pca953x.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index ab22152bf3e8..bd2e96c34f82 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -865,17 +865,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) bitmap_and(chip->irq_stat, irq_stat, reg_direction, chip->gpio_chip.ngpio); mutex_init(&chip->irq_lock); - ret = devm_request_threaded_irq(&client->dev, client->irq, - NULL, pca953x_irq_handler, - IRQF_ONESHOT | IRQF_SHARED, - dev_name(&client->dev), chip); - if (ret) { - dev_err(&client->dev, "failed to request irq %d\n", - client->irq); - return ret; - } - - irq_chip->name = dev_name(&chip->client->dev); + irq_chip->name = dev_name(&client->dev); irq_chip->irq_mask = pca953x_irq_mask; irq_chip->irq_unmask = pca953x_irq_unmask; irq_chip->irq_set_wake = pca953x_irq_set_wake; @@ -895,6 +885,16 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) girq->threaded = true; girq->first = irq_base; /* FIXME: get rid of this */ + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, pca953x_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + dev_name(&client->dev), chip); + if (ret) { + dev_err(&client->dev, "failed to request irq %d\n", + client->irq); + return ret; + } + return 0; } -- cgit v1.2.3-58-ga151 From a1cdaa64aab217a282fc3577f7c560ffaa75dcd0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Jul 2020 15:55:03 +0300 Subject: gpio: crystalcove: Free IRQ on error path It appears that all, but request_irq(), calls in the driver are device managed. In unlikely case of devm_gpiochip_add_data() failure the IRQ left requested. Free IRQ on error path by switching to devm_request_threaded_irq() API. Byproduct of this change is a drop of ->remove() callback completely. Fixes: 945e72db36bd ("gpio: crystalcove: Use irqchip template") Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200728125504.27786-2-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-crystalcove.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index f60ff7579cd0..2ba225720086 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -364,9 +364,9 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) girq->handler = handle_simple_irq; girq->threaded = true; - retval = request_threaded_irq(irq, NULL, crystalcove_gpio_irq_handler, - IRQF_ONESHOT, KBUILD_MODNAME, cg); - + retval = devm_request_threaded_irq(&pdev->dev, irq, NULL, + crystalcove_gpio_irq_handler, + IRQF_ONESHOT, KBUILD_MODNAME, cg); if (retval) { dev_warn(&pdev->dev, "request irq failed: %d\n", retval); return retval; @@ -381,24 +381,12 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) return 0; } -static int crystalcove_gpio_remove(struct platform_device *pdev) -{ - struct crystalcove_gpio *cg = platform_get_drvdata(pdev); - int irq = platform_get_irq(pdev, 0); - - if (irq >= 0) - free_irq(irq, cg); - return 0; -} - static struct platform_driver crystalcove_gpio_driver = { .probe = crystalcove_gpio_probe, - .remove = crystalcove_gpio_remove, .driver = { .name = "crystal_cove_gpio", }, }; - module_platform_driver(crystalcove_gpio_driver); MODULE_AUTHOR("Yang, Bin "); -- cgit v1.2.3-58-ga151 From 22cc422070d9a9a399f8a70b89f1b852945444cb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Jul 2020 15:55:04 +0300 Subject: gpio: wcove: Request IRQ after all initialisation done There is logically better to request IRQ when we initialise all structures. Align the driver with the rest on the same matter. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200728125504.27786-3-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-wcove.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 135645096575..b5fbba5a783a 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -449,13 +449,6 @@ static int wcove_gpio_probe(struct platform_device *pdev) return virq; } - ret = devm_request_threaded_irq(dev, virq, NULL, - wcove_gpio_irq_handler, IRQF_ONESHOT, pdev->name, wg); - if (ret) { - dev_err(dev, "Failed to request irq %d\n", virq); - return ret; - } - girq = &wg->chip.irq; girq->chip = &wcove_irqchip; /* This will let us handle the parent IRQ in the driver */ @@ -466,6 +459,13 @@ static int wcove_gpio_probe(struct platform_device *pdev) girq->handler = handle_simple_irq; girq->threaded = true; + ret = devm_request_threaded_irq(dev, virq, NULL, wcove_gpio_irq_handler, + IRQF_ONESHOT, pdev->name, wg); + if (ret) { + dev_err(dev, "Failed to request irq %d\n", virq); + return ret; + } + ret = devm_gpiochip_add_data(dev, &wg->chip, wg); if (ret) { dev_err(dev, "Failed to add gpiochip: %d\n", ret); -- cgit v1.2.3-58-ga151