From e012d15a238f24795081ef1e43ffe2859b6538ed Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 15 Jun 2020 08:46:47 +0200 Subject: [PATCH 01/36] gpio: driver.h: fix kernel-doc markup There is one parameter with a wrong name at kernel-doc macro: ./include/linux/gpio/driver.h:499: warning: Function parameter or member 'gc' not described in 'gpiochip_add_data' ./include/linux/gpio/driver.h:499: warning: Excess function parameter 'chip' description in 'gpiochip_add_data' Fix it. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index c4f272af7af5..c11261f3c724 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -481,7 +481,7 @@ extern int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, /** * gpiochip_add_data() - register a gpio_chip - * @chip: the chip to register, with chip->base initialized + * @gc: the chip to register, with chip->base initialized * @data: driver-private data associated with this chip * * Context: potentially before irqs will work From 97cd738c44c85680d6c37f8583409ad2ef0ea17b Mon Sep 17 00:00:00 2001 From: Wang Qing Date: Fri, 12 Jun 2020 16:05:45 +0800 Subject: [PATCH 02/36] 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(-) 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; From 3d5bfbd9716318b1ca5c38488aa69f64d38a9aa5 Mon Sep 17 00:00:00 2001 From: Song Hui Date: Thu, 11 Jun 2020 18:28:09 +0800 Subject: [PATCH 03/36] 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(-) 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", From 3abda79aa12aba40964b593459866c312c513c23 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Tue, 9 Jun 2020 13:45:56 +0100 Subject: [PATCH 04/36] 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(-) 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. From ee0f054fb5379e828137e0ff04188477459ce44c Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 15 Jun 2020 15:22:28 +0200 Subject: [PATCH 05/36] dt-bindings: gpio: pca953x: add nxp,pcal9535 The NXP PCAL9535 is an extended variant of the PCA9535. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-pca953x.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt index dab537c20def..3126c3817e2a 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt @@ -19,6 +19,7 @@ Required properties: nxp,pca9698 nxp,pcal6416 nxp,pcal6524 + nxp,pcal9535 nxp,pcal9555a maxim,max7310 maxim,max7312 From 3ba3ff5c0966280bfd1d40b05b1d7c089f45cc9b Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 15 Jun 2020 15:22:29 +0200 Subject: [PATCH 06/36] 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(+) 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), }, From 1f6bd574e9d575bd79bca8312dfd3d9345389940 Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Wed, 10 Jun 2020 07:36:30 -0400 Subject: [PATCH 07/36] 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(-) 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) From 925ca36913fc7dfee9d0bb7f36d81dd108a7b80f Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Tue, 16 Jun 2020 17:36:15 +0800 Subject: [PATCH 08/36] 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 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); From b3337eb24831db058231ea87838f316d9eb86253 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 15 Jun 2020 18:05:41 +0300 Subject: [PATCH 09/36] gpiolib: Introduce for_each_requested_gpio_in_range() macro Introduce for_each_requested_gpio_in_range() macro which helps to iterate over requested GPIO in a range. There are already potential users of it, which are going to be converted by the following patches. For most of them for_each_requested_gpio() shortcut has been added. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200615150545.87964-2-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- include/linux/gpio/driver.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index c4f272af7af5..11cdcb195635 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -474,6 +474,22 @@ struct gpio_chip { extern const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset); +/** + * for_each_requested_gpio_in_range - iterates over requested GPIOs in a given range + * @chip: the chip to query + * @i: loop variable + * @base: first GPIO in the range + * @size: amount of GPIOs to check starting from @base + * @label: label of current GPIO + */ +#define for_each_requested_gpio_in_range(chip, i, base, size, label) \ + for (i = 0; i < size; i++) \ + if ((label = gpiochip_is_requested(chip, base + i)) == NULL) {} else + +/* Iterates over all requested GPIO of the given @chip */ +#define for_each_requested_gpio(chip, i, label) \ + for_each_requested_gpio_in_range(chip, i, 0, chip->ngpio, label) + /* add/remove chips */ extern int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *lock_key, From aed8fa12552aed54aebe6946a34d59cf18e91a62 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 15 Jun 2020 18:05:42 +0300 Subject: [PATCH 10/36] ARM/orion/gpio: Make use of for_each_requested_gpio() Make use of for_each_requested_gpio() instead of home grown analogue. Signed-off-by: Andy Shevchenko Reviewed-by: Andrew Lunn Cc: Jason Cooper Cc: Andrew Lunn Cc: Sebastian Hesselbarth Cc: Gregory Clement Link: https://lore.kernel.org/r/20200615150545.87964-3-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- arch/arm/plat-orion/gpio.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c index 26a531ebb6e9..734f0be4f14a 100644 --- a/arch/arm/plat-orion/gpio.c +++ b/arch/arm/plat-orion/gpio.c @@ -442,6 +442,7 @@ static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) struct orion_gpio_chip *ochip = gpiochip_get_data(chip); u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk; + const char *label; int i; out = readl_relaxed(GPIO_OUT(ochip)); @@ -453,15 +454,10 @@ static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) edg_msk = readl_relaxed(GPIO_EDGE_MASK(ochip)); lvl_msk = readl_relaxed(GPIO_LEVEL_MASK(ochip)); - 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 = 1 << i; is_out = !(io_conf & msk); From 86661fd7faacbb9a5a4d15755551cec43f6a3a14 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 15 Jun 2020 18:05:43 +0300 Subject: [PATCH 11/36] 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(-) 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); From 7796cdc911fc584a49ee8cfbe53baa00e57267d3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 15 Jun 2020 18:05:44 +0300 Subject: [PATCH 12/36] 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(-) 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", From 5bae1f08e2853d17ce2ceeca71fe5a3ee03a0a69 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 15 Jun 2020 18:05:45 +0300 Subject: [PATCH 13/36] pinctrl: at91: 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: Ludovic Desroches Cc: Nicolas Ferre Cc: Alexandre Belloni Link: https://lore.kernel.org/r/20200615150545.87964-6-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-at91.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 52386ad29f28..9c5213087659 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -1486,14 +1486,11 @@ static void at91_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) int i; struct at91_gpio_chip *at91_gpio = gpiochip_get_data(chip); void __iomem *pio = at91_gpio->regbase; + const char *gpio_label; - for (i = 0; i < chip->ngpio; i++) { + for_each_requested_gpio(chip, i, gpio_label) { unsigned mask = pin_to_mask(i); - const char *gpio_label; - gpio_label = gpiochip_is_requested(chip, i); - if (!gpio_label) - continue; mode = at91_gpio->ops->get_periph(pio, mask); seq_printf(s, "[%s] GPIO%s%d: ", gpio_label, chip->label, i); From 8346b92ecddfae624b1dd4631e840fb98f5430cd Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Sat, 20 Jun 2020 18:40:53 -0400 Subject: [PATCH 14/36] 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(-) 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) From fdcfec11b215b3bbc9c20240739cf76be921cca1 Mon Sep 17 00:00:00 2001 From: Glenn Langedock Date: Wed, 17 Jun 2020 17:07:21 +0530 Subject: [PATCH 15/36] 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(+) 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); From 3e1ad2b858dd1c1e795e3817aa31f0a301d6aeba Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 17 Jun 2020 17:07:22 +0530 Subject: [PATCH 16/36] dt-bindings: gpio: Add binding for Versal gpio Add binding for Versal binding. 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 --- Documentation/devicetree/bindings/gpio/gpio-zynq.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/gpio/gpio-zynq.txt b/Documentation/devicetree/bindings/gpio/gpio-zynq.txt index 4fa4eb5507cd..4806d846c0fc 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-zynq.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-zynq.txt @@ -6,7 +6,8 @@ Required properties: - First cell is the GPIO line number - Second cell is used to specify optional parameters (unused) -- compatible : Should be "xlnx,zynq-gpio-1.0" or "xlnx,zynqmp-gpio-1.0" +- compatible : Should be "xlnx,zynq-gpio-1.0" or + "xlnx,zynqmp-gpio-1.0" or "xlnx,versal-gpio-1.0 - clocks : Clock specifier (see clock bindings for details) - gpio-controller : Marks the device node as a GPIO controller. - interrupts : Interrupt specifier (see interrupt bindings for From c0178b8e24b2b1eed8ebfe5b155a36304d42a137 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 17 Jun 2020 17:07:23 +0530 Subject: [PATCH 17/36] dt-bindings: gpio: Add pmc gpio node to gpio-zynq Add the pmc gpio node to the device tree. 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 --- Documentation/devicetree/bindings/gpio/gpio-zynq.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-zynq.txt b/Documentation/devicetree/bindings/gpio/gpio-zynq.txt index 4806d846c0fc..f693e82b4c0f 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-zynq.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-zynq.txt @@ -8,6 +8,7 @@ Required properties: parameters (unused) - compatible : Should be "xlnx,zynq-gpio-1.0" or "xlnx,zynqmp-gpio-1.0" or "xlnx,versal-gpio-1.0 + or "xlnx,pmc-gpio-1.0 - clocks : Clock specifier (see clock bindings for details) - gpio-controller : Marks the device node as a GPIO controller. - interrupts : Interrupt specifier (see interrupt bindings for From 675002448eee5a9f96bfeb9b057e12a60a004b6c Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 17 Jun 2020 17:07:24 +0530 Subject: [PATCH 18/36] 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(-) 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; From 26ebdbf8c2e311759d88c0b0cf26126715e932c6 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 17 Jun 2020 17:07:25 +0530 Subject: [PATCH 19/36] 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(+) 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; From 73c612fe2a5f819bc54598b5babc630528bf29d1 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 17 Jun 2020 17:07:26 +0530 Subject: [PATCH 20/36] 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(+) 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); From 0077428118204d934382dcb74569899ac3dd2f58 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Wed, 17 Jun 2020 17:07:27 +0530 Subject: [PATCH 21/36] 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(-) 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); From e4c6a52c64607d825b0e81c82e548d4cc8199e53 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 23 Jun 2020 16:40:01 +0200 Subject: [PATCH 22/36] 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(-) 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 From f02a03985d06c5a39605a0b8a6206af0f9ec7b03 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 29 Jun 2020 09:41:14 -0700 Subject: [PATCH 23/36] 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(+) 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 = { From ae66eca000cfef3ef0e31842102e17b1b660b1ce Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 30 Jun 2020 12:21:46 +0300 Subject: [PATCH 24/36] 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(-) 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; } From 8cbdc5045df071b146b76a718dd8669e89714057 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 20 Jun 2020 22:12:48 +0200 Subject: [PATCH 25/36] gpio: Add gpio-charger to the documentation The GPIO-based charger is another of the helpful devices built on top of GPIO. Signed-off-by: Linus Walleij Cc: Sebastian Reichel Link: https://lore.kernel.org/r/20200620201248.28843-1-linus.walleij@linaro.org --- Documentation/driver-api/gpio/drivers-on-gpio.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/driver-api/gpio/drivers-on-gpio.rst b/Documentation/driver-api/gpio/drivers-on-gpio.rst index 820b403d50f6..41ec3cc72d32 100644 --- a/Documentation/driver-api/gpio/drivers-on-gpio.rst +++ b/Documentation/driver-api/gpio/drivers-on-gpio.rst @@ -89,6 +89,13 @@ hardware descriptions such as device tree or ACPI: Consumer Electronics Control bus using only GPIO. It is used to communicate with devices on the HDMI bus. +- gpio-charger: drivers/power/supply/gpio-charger.c is used if you need to do + battery charging and all you have to go by to check the presence of the + AC charger or more complex tasks such as indicating charging status using + nothing but GPIO lines, this driver provides that and also a clearly defined + way to pass the charging parameters from hardware descriptions such as the + device tree. + Apart from this there are special GPIO drivers in subsystems like MMC/SD to read card detect and write protect GPIO lines, and in the TTY serial subsystem to emulate MCTRL (modem control) signals CTS/RTS by using two GPIO lines. The From 2073ea3ab181d42b4fd179ec3f74d53acefb563a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 1 Jul 2020 13:42:11 +0200 Subject: [PATCH 26/36] 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(-) 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; From ec75039d55500ee033236aa25f7642a5b449a23c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 1 Jul 2020 13:42:12 +0200 Subject: [PATCH 27/36] 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 | 57 +++++++++++++++------------------- 1 file changed, 25 insertions(+), 32 deletions(-) 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'; + error = bitmap_parselist(offsets, bitmap, ARCH_NR_GPIOS); + if (error) { + pr_err("Cannot parse %s: %d\n", offsets, error); + goto free_bitmap; + } - 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; - } + 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, From c5a66b970aacd14639524c5b0c05a8491127d333 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:36 +0100 Subject: [PATCH 28/36] 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(+) 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 From 670647d7c418b4c4a4effc7eab80729310a97f0a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:37 +0100 Subject: [PATCH 29/36] 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(-) 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 */ From 3cd7f770fe8e230274e011198bf290a25dfaa570 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:39 +0100 Subject: [PATCH 30/36] 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(-) 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. From 392a58469894f8fe26874f66f1e064c20a9b91ee Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:40 +0100 Subject: [PATCH 31/36] 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(-) 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, From 5f3beb67b5d1c5f9906dfdf3af77e8992ca177be Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:41 +0100 Subject: [PATCH 32/36] 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(-) 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" */ From 4e2912901ec1ff0ef7aa8d4334e0ad476792f764 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:42 +0100 Subject: [PATCH 33/36] 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(-) 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. From 6ac2de953bb317c4dc2edaa56e025724fd159165 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:43 +0100 Subject: [PATCH 34/36] 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(-) 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 }, {} }; From 2f9bce5f38684cd10533ac41d8dd395a611cc8c5 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:44 +0100 Subject: [PATCH 35/36] 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(-) 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 }, {}, }; From a7a9ad232f02e6aa659cc9b58b72b1a30dccd1a6 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 30 Jun 2020 14:33:45 +0100 Subject: [PATCH 36/36] 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(+) 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);