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