|
@@ -16,6 +16,11 @@
|
|
|
#include <linux/gpio/driver.h>
|
|
|
#include <linux/gpio/machine.h>
|
|
|
#include <linux/pinctrl/consumer.h>
|
|
|
+#include <linux/idr.h>
|
|
|
+#include <linux/cdev.h>
|
|
|
+#include <linux/fs.h>
|
|
|
+#include <linux/uaccess.h>
|
|
|
+#include <uapi/linux/gpio.h>
|
|
|
|
|
|
#include "gpiolib.h"
|
|
|
|
|
@@ -42,6 +47,14 @@
|
|
|
#define extra_checks 0
|
|
|
#endif
|
|
|
|
|
|
+/* Device and char device-related information */
|
|
|
+static DEFINE_IDA(gpio_ida);
|
|
|
+static dev_t gpio_devt;
|
|
|
+#define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */
|
|
|
+static struct bus_type gpio_bus_type = {
|
|
|
+ .name = "gpio",
|
|
|
+};
|
|
|
+
|
|
|
/* gpio_lock prevents conflicts during gpio_desc[] table updates.
|
|
|
* While any GPIO is requested, its gpio_chip is not removable;
|
|
|
* each GPIO's "requested" flag serves as a lock and refcount.
|
|
@@ -50,8 +63,7 @@ DEFINE_SPINLOCK(gpio_lock);
|
|
|
|
|
|
static DEFINE_MUTEX(gpio_lookup_lock);
|
|
|
static LIST_HEAD(gpio_lookup_list);
|
|
|
-LIST_HEAD(gpio_chips);
|
|
|
-
|
|
|
+LIST_HEAD(gpio_devices);
|
|
|
|
|
|
static void gpiochip_free_hogs(struct gpio_chip *chip);
|
|
|
static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
|
|
@@ -67,15 +79,16 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
|
|
|
*/
|
|
|
struct gpio_desc *gpio_to_desc(unsigned gpio)
|
|
|
{
|
|
|
- struct gpio_chip *chip;
|
|
|
+ struct gpio_device *gdev;
|
|
|
unsigned long flags;
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
- list_for_each_entry(chip, &gpio_chips, list) {
|
|
|
- if (chip->base <= gpio && chip->base + chip->ngpio > gpio) {
|
|
|
+ list_for_each_entry(gdev, &gpio_devices, list) {
|
|
|
+ if (gdev->base <= gpio &&
|
|
|
+ gdev->base + gdev->ngpio > gpio) {
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
- return &chip->desc[gpio - chip->base];
|
|
|
+ return &gdev->descs[gpio - gdev->base];
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -94,10 +107,12 @@ EXPORT_SYMBOL_GPL(gpio_to_desc);
|
|
|
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
|
|
|
u16 hwnum)
|
|
|
{
|
|
|
- if (hwnum >= chip->ngpio)
|
|
|
+ struct gpio_device *gdev = chip->gpiodev;
|
|
|
+
|
|
|
+ if (hwnum >= gdev->ngpio)
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
- return &chip->desc[hwnum];
|
|
|
+ return &gdev->descs[hwnum];
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -107,7 +122,7 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
|
|
|
*/
|
|
|
int desc_to_gpio(const struct gpio_desc *desc)
|
|
|
{
|
|
|
- return desc->chip->base + (desc - &desc->chip->desc[0]);
|
|
|
+ return desc->gdev->base + (desc - &desc->gdev->descs[0]);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(desc_to_gpio);
|
|
|
|
|
@@ -118,23 +133,25 @@ EXPORT_SYMBOL_GPL(desc_to_gpio);
|
|
|
*/
|
|
|
struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc)
|
|
|
{
|
|
|
- return desc ? desc->chip : NULL;
|
|
|
+ if (!desc || !desc->gdev || !desc->gdev->chip)
|
|
|
+ return NULL;
|
|
|
+ return desc->gdev->chip;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiod_to_chip);
|
|
|
|
|
|
/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
|
|
|
static int gpiochip_find_base(int ngpio)
|
|
|
{
|
|
|
- struct gpio_chip *chip;
|
|
|
+ struct gpio_device *gdev;
|
|
|
int base = ARCH_NR_GPIOS - ngpio;
|
|
|
|
|
|
- list_for_each_entry_reverse(chip, &gpio_chips, list) {
|
|
|
+ list_for_each_entry_reverse(gdev, &gpio_devices, list) {
|
|
|
/* found a free space? */
|
|
|
- if (chip->base + chip->ngpio <= base)
|
|
|
+ if (gdev->base + gdev->ngpio <= base)
|
|
|
break;
|
|
|
else
|
|
|
/* nope, check the space right before the chip */
|
|
|
- base = chip->base - ngpio;
|
|
|
+ base = gdev->base - ngpio;
|
|
|
}
|
|
|
|
|
|
if (gpio_is_valid(base)) {
|
|
@@ -187,18 +204,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction);
|
|
|
* Return -EBUSY if the new chip overlaps with some other chip's integer
|
|
|
* space.
|
|
|
*/
|
|
|
-static int gpiochip_add_to_list(struct gpio_chip *chip)
|
|
|
+static int gpiodev_add_to_list(struct gpio_device *gdev)
|
|
|
{
|
|
|
- struct gpio_chip *iterator;
|
|
|
- struct gpio_chip *previous = NULL;
|
|
|
+ struct gpio_device *iterator;
|
|
|
+ struct gpio_device *previous = NULL;
|
|
|
|
|
|
- if (list_empty(&gpio_chips)) {
|
|
|
- list_add_tail(&chip->list, &gpio_chips);
|
|
|
+ if (!gdev->chip)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (list_empty(&gpio_devices)) {
|
|
|
+ list_add_tail(&gdev->list, &gpio_devices);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- list_for_each_entry(iterator, &gpio_chips, list) {
|
|
|
- if (iterator->base >= chip->base + chip->ngpio) {
|
|
|
+ list_for_each_entry(iterator, &gpio_devices, list) {
|
|
|
+ if (iterator->base >= gdev->base + gdev->ngpio) {
|
|
|
/*
|
|
|
* Iterator is the first GPIO chip so there is no
|
|
|
* previous one
|
|
@@ -212,7 +232,7 @@ static int gpiochip_add_to_list(struct gpio_chip *chip)
|
|
|
* and iterator chip.
|
|
|
*/
|
|
|
if (previous->base + previous->ngpio
|
|
|
- <= chip->base)
|
|
|
+ <= gdev->base)
|
|
|
goto found;
|
|
|
}
|
|
|
}
|
|
@@ -225,18 +245,18 @@ static int gpiochip_add_to_list(struct gpio_chip *chip)
|
|
|
* Let iterator point to the last chip in the list.
|
|
|
*/
|
|
|
|
|
|
- iterator = list_last_entry(&gpio_chips, struct gpio_chip, list);
|
|
|
- if (iterator->base + iterator->ngpio <= chip->base) {
|
|
|
- list_add(&chip->list, &iterator->list);
|
|
|
+ iterator = list_last_entry(&gpio_devices, struct gpio_device, list);
|
|
|
+ if (iterator->base + iterator->ngpio <= gdev->base) {
|
|
|
+ list_add(&gdev->list, &iterator->list);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- dev_err(chip->parent,
|
|
|
+ dev_err(&gdev->dev,
|
|
|
"GPIO integer space overlap, cannot add chip\n");
|
|
|
return -EBUSY;
|
|
|
|
|
|
found:
|
|
|
- list_add_tail(&chip->list, &iterator->list);
|
|
|
+ list_add_tail(&gdev->list, &iterator->list);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -245,23 +265,23 @@ found:
|
|
|
*/
|
|
|
static struct gpio_desc *gpio_name_to_desc(const char * const name)
|
|
|
{
|
|
|
- struct gpio_chip *chip;
|
|
|
+ struct gpio_device *gdev;
|
|
|
unsigned long flags;
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
- list_for_each_entry(chip, &gpio_chips, list) {
|
|
|
+ list_for_each_entry(gdev, &gpio_devices, list) {
|
|
|
int i;
|
|
|
|
|
|
- for (i = 0; i != chip->ngpio; ++i) {
|
|
|
- struct gpio_desc *gpio = &chip->desc[i];
|
|
|
+ for (i = 0; i != gdev->ngpio; ++i) {
|
|
|
+ struct gpio_desc *desc = &gdev->descs[i];
|
|
|
|
|
|
- if (!gpio->name || !name)
|
|
|
+ if (!desc->name || !name)
|
|
|
continue;
|
|
|
|
|
|
- if (!strcmp(gpio->name, name)) {
|
|
|
+ if (!strcmp(desc->name, name)) {
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
- return gpio;
|
|
|
+ return desc;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -279,6 +299,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
|
|
|
*/
|
|
|
static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
|
|
{
|
|
|
+ struct gpio_device *gdev = gc->gpiodev;
|
|
|
int i;
|
|
|
|
|
|
if (!gc->names)
|
|
@@ -290,18 +311,153 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
|
|
|
|
|
gpio = gpio_name_to_desc(gc->names[i]);
|
|
|
if (gpio)
|
|
|
- dev_warn(gc->parent, "Detected name collision for "
|
|
|
- "GPIO name '%s'\n",
|
|
|
+ dev_warn(&gdev->dev,
|
|
|
+ "Detected name collision for GPIO name '%s'\n",
|
|
|
gc->names[i]);
|
|
|
}
|
|
|
|
|
|
/* Then add all names to the GPIO descriptors */
|
|
|
for (i = 0; i != gc->ngpio; ++i)
|
|
|
- gc->desc[i].name = gc->names[i];
|
|
|
+ gdev->descs[i].name = gc->names[i];
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * gpio_ioctl() - ioctl handler for the GPIO chardev
|
|
|
+ */
|
|
|
+static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
|
+{
|
|
|
+ struct gpio_device *gdev = filp->private_data;
|
|
|
+ struct gpio_chip *chip = gdev->chip;
|
|
|
+ int __user *ip = (int __user *)arg;
|
|
|
+
|
|
|
+ /* We fail any subsequent ioctl():s when the chip is gone */
|
|
|
+ if (!chip)
|
|
|
+ return -ENODEV;
|
|
|
|
|
|
+ /* Fill in the struct and pass to userspace */
|
|
|
+ if (cmd == GPIO_GET_CHIPINFO_IOCTL) {
|
|
|
+ struct gpiochip_info 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;
|
|
|
+ struct gpio_desc *desc;
|
|
|
+
|
|
|
+ if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
|
|
|
+ return -EFAULT;
|
|
|
+ if (lineinfo.line_offset > gdev->ngpio)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ desc = &gdev->descs[lineinfo.line_offset];
|
|
|
+ if (desc->name) {
|
|
|
+ strncpy(lineinfo.name, desc->name,
|
|
|
+ sizeof(lineinfo.name));
|
|
|
+ lineinfo.name[sizeof(lineinfo.name)-1] = '\0';
|
|
|
+ } else {
|
|
|
+ lineinfo.name[0] = '\0';
|
|
|
+ }
|
|
|
+ if (desc->label) {
|
|
|
+ strncpy(lineinfo.label, desc->label,
|
|
|
+ sizeof(lineinfo.label));
|
|
|
+ lineinfo.label[sizeof(lineinfo.label)-1] = '\0';
|
|
|
+ } else {
|
|
|
+ lineinfo.label[0] = '\0';
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Userspace only need to know that the kernel is using
|
|
|
+ * this GPIO so it can't use it.
|
|
|
+ */
|
|
|
+ lineinfo.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))
|
|
|
+ lineinfo.flags |= GPIOLINE_FLAG_KERNEL;
|
|
|
+ if (test_bit(FLAG_IS_OUT, &desc->flags))
|
|
|
+ lineinfo.flags |= GPIOLINE_FLAG_IS_OUT;
|
|
|
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
|
|
+ lineinfo.flags |= GPIOLINE_FLAG_ACTIVE_LOW;
|
|
|
+ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
|
|
|
+ lineinfo.flags |= GPIOLINE_FLAG_OPEN_DRAIN;
|
|
|
+ if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
|
|
|
+ lineinfo.flags |= GPIOLINE_FLAG_OPEN_SOURCE;
|
|
|
+
|
|
|
+ if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
|
|
|
+ return -EFAULT;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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);
|
|
|
+
|
|
|
+ /* Fail on open if the backing gpiochip is gone */
|
|
|
+ if (!gdev || !gdev->chip)
|
|
|
+ return -ENODEV;
|
|
|
+ get_device(&gdev->dev);
|
|
|
+ filp->private_data = gdev;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * 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_device *gdev = container_of(inode->i_cdev,
|
|
|
+ struct gpio_device, chrdev);
|
|
|
+
|
|
|
+ if (!gdev)
|
|
|
+ return -ENODEV;
|
|
|
+ put_device(&gdev->dev);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static const struct file_operations gpio_fileops = {
|
|
|
+ .release = gpio_chrdev_release,
|
|
|
+ .open = gpio_chrdev_open,
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .llseek = noop_llseek,
|
|
|
+ .unlocked_ioctl = gpio_ioctl,
|
|
|
+ .compat_ioctl = gpio_ioctl,
|
|
|
+};
|
|
|
+
|
|
|
+static void gpiodevice_release(struct device *dev)
|
|
|
+{
|
|
|
+ struct gpio_device *gdev = dev_get_drvdata(dev);
|
|
|
+
|
|
|
+ cdev_del(&gdev->chrdev);
|
|
|
+ list_del(&gdev->list);
|
|
|
+ ida_simple_remove(&gpio_ida, gdev->id);
|
|
|
+ kfree(gdev);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* gpiochip_add_data() - register a gpio_chip
|
|
|
* @chip: the chip to register, with chip->base initialized
|
|
@@ -323,43 +479,107 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
int status = 0;
|
|
|
- unsigned id;
|
|
|
+ unsigned i;
|
|
|
int base = chip->base;
|
|
|
- struct gpio_desc *descs;
|
|
|
+ struct gpio_device *gdev;
|
|
|
|
|
|
- descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
|
|
|
- if (!descs)
|
|
|
+ /*
|
|
|
+ * First: allocate and populate the internal stat container, and
|
|
|
+ * set up the struct device.
|
|
|
+ */
|
|
|
+ gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
|
|
|
+ if (!gdev)
|
|
|
return -ENOMEM;
|
|
|
+ gdev->dev.bus = &gpio_bus_type;
|
|
|
+ gdev->chip = chip;
|
|
|
+ chip->gpiodev = gdev;
|
|
|
+ if (chip->parent) {
|
|
|
+ gdev->dev.parent = chip->parent;
|
|
|
+ gdev->dev.of_node = chip->parent->of_node;
|
|
|
+ } else {
|
|
|
+#ifdef CONFIG_OF_GPIO
|
|
|
+ /* If the gpiochip has an assigned OF node this takes precedence */
|
|
|
+ if (chip->of_node)
|
|
|
+ gdev->dev.of_node = chip->of_node;
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
|
|
|
+ if (gdev->id < 0) {
|
|
|
+ status = gdev->id;
|
|
|
+ goto err_free_gdev;
|
|
|
+ }
|
|
|
+ dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
|
|
|
+ device_initialize(&gdev->dev);
|
|
|
+ dev_set_drvdata(&gdev->dev, gdev);
|
|
|
+ if (chip->parent && chip->parent->driver)
|
|
|
+ gdev->owner = chip->parent->driver->owner;
|
|
|
+ else if (chip->owner)
|
|
|
+ /* TODO: remove chip->owner */
|
|
|
+ gdev->owner = chip->owner;
|
|
|
+ else
|
|
|
+ gdev->owner = THIS_MODULE;
|
|
|
|
|
|
- chip->data = data;
|
|
|
+ gdev->descs = devm_kcalloc(&gdev->dev, chip->ngpio,
|
|
|
+ sizeof(gdev->descs[0]), GFP_KERNEL);
|
|
|
+ if (!gdev->descs) {
|
|
|
+ status = -ENOMEM;
|
|
|
+ goto err_free_gdev;
|
|
|
+ }
|
|
|
|
|
|
if (chip->ngpio == 0) {
|
|
|
chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
|
|
|
- return -EINVAL;
|
|
|
+ status = -EINVAL;
|
|
|
+ goto err_free_gdev;
|
|
|
}
|
|
|
|
|
|
+ if (chip->label)
|
|
|
+ gdev->label = devm_kstrdup(&gdev->dev, chip->label, GFP_KERNEL);
|
|
|
+ else
|
|
|
+ gdev->label = devm_kstrdup(&gdev->dev, "unknown", GFP_KERNEL);
|
|
|
+ if (!gdev->label) {
|
|
|
+ status = -ENOMEM;
|
|
|
+ goto err_free_gdev;
|
|
|
+ }
|
|
|
+
|
|
|
+ gdev->ngpio = chip->ngpio;
|
|
|
+ gdev->data = data;
|
|
|
+
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
+ /*
|
|
|
+ * TODO: this allocates a Linux GPIO number base in the global
|
|
|
+ * GPIO numberspace for this chip. In the long run we want to
|
|
|
+ * get *rid* of this numberspace and use only descriptors, but
|
|
|
+ * it may be a pipe dream. It will not happen before we get rid
|
|
|
+ * of the sysfs interface anyways.
|
|
|
+ */
|
|
|
if (base < 0) {
|
|
|
base = gpiochip_find_base(chip->ngpio);
|
|
|
if (base < 0) {
|
|
|
status = base;
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
- goto err_free_descs;
|
|
|
+ goto err_free_gdev;
|
|
|
}
|
|
|
+ /*
|
|
|
+ * TODO: it should not be necessary to reflect the assigned
|
|
|
+ * base outside of the GPIO subsystem. Go over drivers and
|
|
|
+ * see if anyone makes use of this, else drop this and assign
|
|
|
+ * a poison instead.
|
|
|
+ */
|
|
|
chip->base = base;
|
|
|
}
|
|
|
+ gdev->base = base;
|
|
|
|
|
|
- status = gpiochip_add_to_list(chip);
|
|
|
+ status = gpiodev_add_to_list(gdev);
|
|
|
if (status) {
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
- goto err_free_descs;
|
|
|
+ goto err_free_gdev;
|
|
|
}
|
|
|
|
|
|
- for (id = 0; id < chip->ngpio; id++) {
|
|
|
- struct gpio_desc *desc = &descs[id];
|
|
|
+ for (i = 0; i < chip->ngpio; i++) {
|
|
|
+ struct gpio_desc *desc = &gdev->descs[i];
|
|
|
|
|
|
- desc->chip = chip;
|
|
|
+ desc->gdev = gdev;
|
|
|
|
|
|
/* REVISIT: most hardware initializes GPIOs as inputs (often
|
|
|
* with pullups enabled) so power usage is minimized. Linux
|
|
@@ -370,17 +590,12 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
|
|
|
desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
|
|
|
}
|
|
|
|
|
|
- chip->desc = descs;
|
|
|
-
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
#ifdef CONFIG_PINCTRL
|
|
|
- INIT_LIST_HEAD(&chip->pin_ranges);
|
|
|
+ INIT_LIST_HEAD(&gdev->pin_ranges);
|
|
|
#endif
|
|
|
|
|
|
- if (!chip->owner && chip->parent && chip->parent->driver)
|
|
|
- chip->owner = chip->parent->driver->owner;
|
|
|
-
|
|
|
status = gpiochip_set_desc_names(chip);
|
|
|
if (status)
|
|
|
goto err_remove_from_list;
|
|
@@ -391,36 +606,72 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
|
|
|
|
|
|
acpi_gpiochip_add(chip);
|
|
|
|
|
|
- status = gpiochip_sysfs_register(chip);
|
|
|
+ /*
|
|
|
+ * By first adding the chardev, and then adding the device,
|
|
|
+ * we get a device node entry in sysfs under
|
|
|
+ * /sys/bus/gpio/devices/gpiochipN/dev that can be used for
|
|
|
+ * coldplug of device nodes and other udev business.
|
|
|
+ */
|
|
|
+ cdev_init(&gdev->chrdev, &gpio_fileops);
|
|
|
+ gdev->chrdev.owner = THIS_MODULE;
|
|
|
+ gdev->chrdev.kobj.parent = &gdev->dev.kobj;
|
|
|
+ gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);
|
|
|
+ status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1);
|
|
|
+ if (status < 0)
|
|
|
+ chip_warn(chip, "failed to add char device %d:%d\n",
|
|
|
+ MAJOR(gpio_devt), gdev->id);
|
|
|
+ else
|
|
|
+ chip_dbg(chip, "added GPIO chardev (%d:%d)\n",
|
|
|
+ MAJOR(gpio_devt), gdev->id);
|
|
|
+ status = device_add(&gdev->dev);
|
|
|
+ if (status)
|
|
|
+ goto err_remove_chardev;
|
|
|
+
|
|
|
+ status = gpiochip_sysfs_register(gdev);
|
|
|
if (status)
|
|
|
- goto err_remove_chip;
|
|
|
+ goto err_remove_device;
|
|
|
|
|
|
- pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
|
|
|
- chip->base, chip->base + chip->ngpio - 1,
|
|
|
- chip->label ? : "generic");
|
|
|
+ /* From this point, the .release() function cleans up gpio_device */
|
|
|
+ gdev->dev.release = gpiodevice_release;
|
|
|
+ get_device(&gdev->dev);
|
|
|
+ pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)\n",
|
|
|
+ __func__, gdev->base, gdev->base + gdev->ngpio - 1,
|
|
|
+ dev_name(&gdev->dev), chip->label ? : "generic");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
+err_remove_device:
|
|
|
+ device_del(&gdev->dev);
|
|
|
+err_remove_chardev:
|
|
|
+ cdev_del(&gdev->chrdev);
|
|
|
err_remove_chip:
|
|
|
acpi_gpiochip_remove(chip);
|
|
|
gpiochip_free_hogs(chip);
|
|
|
of_gpiochip_remove(chip);
|
|
|
err_remove_from_list:
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
- list_del(&chip->list);
|
|
|
+ list_del(&gdev->list);
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
- chip->desc = NULL;
|
|
|
-err_free_descs:
|
|
|
- kfree(descs);
|
|
|
-
|
|
|
+err_free_gdev:
|
|
|
+ ida_simple_remove(&gpio_ida, gdev->id);
|
|
|
/* failures here can mean systems won't boot... */
|
|
|
pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
|
|
|
- chip->base, chip->base + chip->ngpio - 1,
|
|
|
- chip->label ? : "generic");
|
|
|
+ gdev->base, gdev->base + gdev->ngpio - 1,
|
|
|
+ chip->label ? : "generic");
|
|
|
+ kfree(gdev);
|
|
|
return status;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiochip_add_data);
|
|
|
|
|
|
+/**
|
|
|
+ * gpiochip_get_data() - get per-subdriver data for the chip
|
|
|
+ */
|
|
|
+void *gpiochip_get_data(struct gpio_chip *chip)
|
|
|
+{
|
|
|
+ return chip->gpiodev->data;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(gpiochip_get_data);
|
|
|
+
|
|
|
/**
|
|
|
* gpiochip_remove() - unregister a gpio_chip
|
|
|
* @chip: the chip to unregister
|
|
@@ -429,36 +680,46 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data);
|
|
|
*/
|
|
|
void gpiochip_remove(struct gpio_chip *chip)
|
|
|
{
|
|
|
+ struct gpio_device *gdev = chip->gpiodev;
|
|
|
struct gpio_desc *desc;
|
|
|
unsigned long flags;
|
|
|
- unsigned id;
|
|
|
+ unsigned i;
|
|
|
bool requested = false;
|
|
|
|
|
|
- gpiochip_sysfs_unregister(chip);
|
|
|
-
|
|
|
+ /* FIXME: should the legacy sysfs handling be moved to gpio_device? */
|
|
|
+ gpiochip_sysfs_unregister(gdev);
|
|
|
+ /* Numb the device, cancelling all outstanding operations */
|
|
|
+ gdev->chip = NULL;
|
|
|
gpiochip_irqchip_remove(chip);
|
|
|
-
|
|
|
acpi_gpiochip_remove(chip);
|
|
|
gpiochip_remove_pin_ranges(chip);
|
|
|
gpiochip_free_hogs(chip);
|
|
|
of_gpiochip_remove(chip);
|
|
|
+ /*
|
|
|
+ * We accept no more calls into the driver from this point, so
|
|
|
+ * NULL the driver data pointer
|
|
|
+ */
|
|
|
+ gdev->data = NULL;
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
- for (id = 0; id < chip->ngpio; id++) {
|
|
|
- desc = &chip->desc[id];
|
|
|
- desc->chip = NULL;
|
|
|
+ for (i = 0; i < gdev->ngpio; i++) {
|
|
|
+ desc = &gdev->descs[i];
|
|
|
if (test_bit(FLAG_REQUESTED, &desc->flags))
|
|
|
requested = true;
|
|
|
}
|
|
|
- list_del(&chip->list);
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
if (requested)
|
|
|
- dev_crit(chip->parent,
|
|
|
+ dev_crit(&gdev->dev,
|
|
|
"REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
|
|
|
|
|
|
- kfree(chip->desc);
|
|
|
- chip->desc = NULL;
|
|
|
+ /*
|
|
|
+ * The gpiochip side puts its use of the device to rest here:
|
|
|
+ * if there are no userspace clients, the chardev and device will
|
|
|
+ * be removed, else it will be dangling until the last user is
|
|
|
+ * gone.
|
|
|
+ */
|
|
|
+ put_device(&gdev->dev);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiochip_remove);
|
|
|
|
|
@@ -477,17 +738,21 @@ struct gpio_chip *gpiochip_find(void *data,
|
|
|
int (*match)(struct gpio_chip *chip,
|
|
|
void *data))
|
|
|
{
|
|
|
+ struct gpio_device *gdev;
|
|
|
struct gpio_chip *chip;
|
|
|
unsigned long flags;
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
- list_for_each_entry(chip, &gpio_chips, list)
|
|
|
- if (match(chip, data))
|
|
|
+ list_for_each_entry(gdev, &gpio_devices, list)
|
|
|
+ if (match(gdev->chip, data))
|
|
|
break;
|
|
|
|
|
|
/* No match? */
|
|
|
- if (&chip->list == &gpio_chips)
|
|
|
+ if (&gdev->list == &gpio_devices)
|
|
|
chip = NULL;
|
|
|
+ else
|
|
|
+ chip = gdev->chip;
|
|
|
+
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
return chip;
|
|
@@ -617,14 +882,14 @@ static int gpiochip_irq_reqres(struct irq_data *d)
|
|
|
{
|
|
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
|
|
|
- if (!try_module_get(chip->owner))
|
|
|
+ if (!try_module_get(chip->gpiodev->owner))
|
|
|
return -ENODEV;
|
|
|
|
|
|
if (gpiochip_lock_as_irq(chip, d->hwirq)) {
|
|
|
chip_err(chip,
|
|
|
"unable to lock HW IRQ %lu for IRQ\n",
|
|
|
d->hwirq);
|
|
|
- module_put(chip->owner);
|
|
|
+ module_put(chip->gpiodev->owner);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
return 0;
|
|
@@ -635,7 +900,7 @@ static void gpiochip_irq_relres(struct irq_data *d)
|
|
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
|
|
|
gpiochip_unlock_as_irq(chip, d->hwirq);
|
|
|
- module_put(chip->owner);
|
|
|
+ module_put(chip->gpiodev->owner);
|
|
|
}
|
|
|
|
|
|
static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
|
|
@@ -785,7 +1050,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {}
|
|
|
*/
|
|
|
int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset)
|
|
|
{
|
|
|
- return pinctrl_request_gpio(chip->base + offset);
|
|
|
+ return pinctrl_request_gpio(chip->gpiodev->base + offset);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiochip_generic_request);
|
|
|
|
|
@@ -796,7 +1061,7 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request);
|
|
|
*/
|
|
|
void gpiochip_generic_free(struct gpio_chip *chip, unsigned offset)
|
|
|
{
|
|
|
- pinctrl_free_gpio(chip->base + offset);
|
|
|
+ pinctrl_free_gpio(chip->gpiodev->base + offset);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiochip_generic_free);
|
|
|
|
|
@@ -814,6 +1079,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip,
|
|
|
unsigned int gpio_offset, const char *pin_group)
|
|
|
{
|
|
|
struct gpio_pin_range *pin_range;
|
|
|
+ struct gpio_device *gdev = chip->gpiodev;
|
|
|
int ret;
|
|
|
|
|
|
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
|
|
@@ -826,7 +1092,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip,
|
|
|
pin_range->range.id = gpio_offset;
|
|
|
pin_range->range.gc = chip;
|
|
|
pin_range->range.name = chip->label;
|
|
|
- pin_range->range.base = chip->base + gpio_offset;
|
|
|
+ pin_range->range.base = gdev->base + gpio_offset;
|
|
|
pin_range->pctldev = pctldev;
|
|
|
|
|
|
ret = pinctrl_get_group_pins(pctldev, pin_group,
|
|
@@ -843,7 +1109,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip,
|
|
|
gpio_offset, gpio_offset + pin_range->range.npins - 1,
|
|
|
pinctrl_dev_get_devname(pctldev), pin_group);
|
|
|
|
|
|
- list_add_tail(&pin_range->node, &chip->pin_ranges);
|
|
|
+ list_add_tail(&pin_range->node, &gdev->pin_ranges);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -863,6 +1129,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
|
|
|
unsigned int npins)
|
|
|
{
|
|
|
struct gpio_pin_range *pin_range;
|
|
|
+ struct gpio_device *gdev = chip->gpiodev;
|
|
|
int ret;
|
|
|
|
|
|
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
|
|
@@ -875,7 +1142,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
|
|
|
pin_range->range.id = gpio_offset;
|
|
|
pin_range->range.gc = chip;
|
|
|
pin_range->range.name = chip->label;
|
|
|
- pin_range->range.base = chip->base + gpio_offset;
|
|
|
+ pin_range->range.base = gdev->base + gpio_offset;
|
|
|
pin_range->range.pin_base = pin_offset;
|
|
|
pin_range->range.npins = npins;
|
|
|
pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
|
|
@@ -891,7 +1158,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
|
|
|
pinctl_name,
|
|
|
pin_offset, pin_offset + npins - 1);
|
|
|
|
|
|
- list_add_tail(&pin_range->node, &chip->pin_ranges);
|
|
|
+ list_add_tail(&pin_range->node, &gdev->pin_ranges);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -904,8 +1171,9 @@ EXPORT_SYMBOL_GPL(gpiochip_add_pin_range);
|
|
|
void gpiochip_remove_pin_ranges(struct gpio_chip *chip)
|
|
|
{
|
|
|
struct gpio_pin_range *pin_range, *tmp;
|
|
|
+ struct gpio_device *gdev = chip->gpiodev;
|
|
|
|
|
|
- list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
|
|
|
+ list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) {
|
|
|
list_del(&pin_range->node);
|
|
|
pinctrl_remove_gpio_range(pin_range->pctldev,
|
|
|
&pin_range->range);
|
|
@@ -922,7 +1190,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
|
|
|
*/
|
|
|
static int __gpiod_request(struct gpio_desc *desc, const char *label)
|
|
|
{
|
|
|
- struct gpio_chip *chip = desc->chip;
|
|
|
+ struct gpio_chip *chip = desc->gdev->chip;
|
|
|
int status;
|
|
|
unsigned long flags;
|
|
|
|
|
@@ -971,27 +1239,50 @@ done:
|
|
|
return status;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * This descriptor validation needs to be inserted verbatim into each
|
|
|
+ * function taking a descriptor, so we need to use a preprocessor
|
|
|
+ * macro to avoid endless duplication.
|
|
|
+ */
|
|
|
+#define VALIDATE_DESC(desc) do { \
|
|
|
+ if (!desc || !desc->gdev) { \
|
|
|
+ pr_warn("%s: invalid GPIO\n", __func__); \
|
|
|
+ return -EINVAL; \
|
|
|
+ } \
|
|
|
+ if ( !desc->gdev->chip ) { \
|
|
|
+ dev_warn(&desc->gdev->dev, \
|
|
|
+ "%s: backing chip is gone\n", __func__); \
|
|
|
+ return 0; \
|
|
|
+ } } while (0)
|
|
|
+
|
|
|
+#define VALIDATE_DESC_VOID(desc) do { \
|
|
|
+ if (!desc || !desc->gdev) { \
|
|
|
+ pr_warn("%s: invalid GPIO\n", __func__); \
|
|
|
+ return; \
|
|
|
+ } \
|
|
|
+ if (!desc->gdev->chip) { \
|
|
|
+ dev_warn(&desc->gdev->dev, \
|
|
|
+ "%s: backing chip is gone\n", __func__); \
|
|
|
+ return; \
|
|
|
+ } } while (0)
|
|
|
+
|
|
|
+
|
|
|
int gpiod_request(struct gpio_desc *desc, const char *label)
|
|
|
{
|
|
|
int status = -EPROBE_DEFER;
|
|
|
- struct gpio_chip *chip;
|
|
|
+ struct gpio_device *gdev;
|
|
|
|
|
|
- if (!desc) {
|
|
|
- pr_warn("%s: invalid GPIO\n", __func__);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
+ gdev = desc->gdev;
|
|
|
|
|
|
- chip = desc->chip;
|
|
|
- if (!chip)
|
|
|
- goto done;
|
|
|
-
|
|
|
- if (try_module_get(chip->owner)) {
|
|
|
+ if (try_module_get(gdev->owner)) {
|
|
|
status = __gpiod_request(desc, label);
|
|
|
if (status < 0)
|
|
|
- module_put(chip->owner);
|
|
|
+ module_put(gdev->owner);
|
|
|
+ else
|
|
|
+ get_device(&gdev->dev);
|
|
|
}
|
|
|
|
|
|
-done:
|
|
|
if (status)
|
|
|
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
|
|
|
|
|
@@ -1010,7 +1301,7 @@ static bool __gpiod_free(struct gpio_desc *desc)
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
|
|
- chip = desc->chip;
|
|
|
+ chip = desc->gdev->chip;
|
|
|
if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
|
|
|
if (chip->free) {
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
@@ -1033,10 +1324,12 @@ static bool __gpiod_free(struct gpio_desc *desc)
|
|
|
|
|
|
void gpiod_free(struct gpio_desc *desc)
|
|
|
{
|
|
|
- if (desc && __gpiod_free(desc))
|
|
|
- module_put(desc->chip->owner);
|
|
|
- else
|
|
|
+ if (desc && desc->gdev && __gpiod_free(desc)) {
|
|
|
+ module_put(desc->gdev->owner);
|
|
|
+ put_device(&desc->gdev->dev);
|
|
|
+ } else {
|
|
|
WARN_ON(extra_checks);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1059,7 +1352,7 @@ const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
|
|
|
if (offset >= chip->ngpio)
|
|
|
return NULL;
|
|
|
|
|
|
- desc = &chip->desc[offset];
|
|
|
+ desc = &chip->gpiodev->descs[offset];
|
|
|
|
|
|
if (test_bit(FLAG_REQUESTED, &desc->flags) == 0)
|
|
|
return NULL;
|
|
@@ -1111,7 +1404,8 @@ void gpiochip_free_own_desc(struct gpio_desc *desc)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
|
|
|
|
|
|
-/* Drivers MUST set GPIO direction before making get/set calls. In
|
|
|
+/*
|
|
|
+ * Drivers MUST set GPIO direction before making get/set calls. In
|
|
|
* some cases this is done in early boot, before IRQs are enabled.
|
|
|
*
|
|
|
* As a rule these aren't called more than once (except for drivers
|
|
@@ -1134,12 +1428,9 @@ int gpiod_direction_input(struct gpio_desc *desc)
|
|
|
struct gpio_chip *chip;
|
|
|
int status = -EINVAL;
|
|
|
|
|
|
- if (!desc || !desc->chip) {
|
|
|
- pr_warn("%s: invalid GPIO\n", __func__);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
+ chip = desc->gdev->chip;
|
|
|
|
|
|
- chip = desc->chip;
|
|
|
if (!chip->get || !chip->direction_input) {
|
|
|
gpiod_warn(desc,
|
|
|
"%s: missing get() or direction_input() operations\n",
|
|
@@ -1178,7 +1469,7 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
|
|
|
if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags))
|
|
|
return gpiod_direction_input(desc);
|
|
|
|
|
|
- chip = desc->chip;
|
|
|
+ chip = desc->gdev->chip;
|
|
|
if (!chip->set || !chip->direction_output) {
|
|
|
gpiod_warn(desc,
|
|
|
"%s: missing set() or direction_output() operations\n",
|
|
@@ -1207,10 +1498,7 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
|
|
|
*/
|
|
|
int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
- if (!desc || !desc->chip) {
|
|
|
- pr_warn("%s: invalid GPIO\n", __func__);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
return _gpiod_direction_output_raw(desc, value);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
|
|
@@ -1229,10 +1517,7 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
|
|
|
*/
|
|
|
int gpiod_direction_output(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
- if (!desc || !desc->chip) {
|
|
|
- pr_warn("%s: invalid GPIO\n", __func__);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
|
|
value = !value;
|
|
|
return _gpiod_direction_output_raw(desc, value);
|
|
@@ -1251,12 +1536,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
|
|
|
{
|
|
|
struct gpio_chip *chip;
|
|
|
|
|
|
- if (!desc || !desc->chip) {
|
|
|
- pr_warn("%s: invalid GPIO\n", __func__);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- chip = desc->chip;
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
+ chip = desc->gdev->chip;
|
|
|
if (!chip->set || !chip->set_debounce) {
|
|
|
gpiod_dbg(desc,
|
|
|
"%s: missing set() or set_debounce() operations\n",
|
|
@@ -1276,6 +1557,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_debounce);
|
|
|
*/
|
|
|
int gpiod_is_active_low(const struct gpio_desc *desc)
|
|
|
{
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
return test_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiod_is_active_low);
|
|
@@ -1308,7 +1590,7 @@ static int _gpiod_get_raw_value(const struct gpio_desc *desc)
|
|
|
int offset;
|
|
|
int value;
|
|
|
|
|
|
- chip = desc->chip;
|
|
|
+ chip = desc->gdev->chip;
|
|
|
offset = gpio_chip_hwgpio(desc);
|
|
|
value = chip->get ? chip->get(chip, offset) : -EIO;
|
|
|
value = value < 0 ? value : !!value;
|
|
@@ -1328,10 +1610,9 @@ static int _gpiod_get_raw_value(const struct gpio_desc *desc)
|
|
|
*/
|
|
|
int gpiod_get_raw_value(const struct gpio_desc *desc)
|
|
|
{
|
|
|
- if (!desc)
|
|
|
- return 0;
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
/* Should be using gpio_get_value_cansleep() */
|
|
|
- WARN_ON(desc->chip->can_sleep);
|
|
|
+ WARN_ON(desc->gdev->chip->can_sleep);
|
|
|
return _gpiod_get_raw_value(desc);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiod_get_raw_value);
|
|
@@ -1349,10 +1630,10 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_value);
|
|
|
int gpiod_get_value(const struct gpio_desc *desc)
|
|
|
{
|
|
|
int value;
|
|
|
- if (!desc)
|
|
|
- return 0;
|
|
|
+
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
/* Should be using gpio_get_value_cansleep() */
|
|
|
- WARN_ON(desc->chip->can_sleep);
|
|
|
+ WARN_ON(desc->gdev->chip->can_sleep);
|
|
|
|
|
|
value = _gpiod_get_raw_value(desc);
|
|
|
if (value < 0)
|
|
@@ -1373,7 +1654,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
|
|
|
static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
|
|
|
{
|
|
|
int err = 0;
|
|
|
- struct gpio_chip *chip = desc->chip;
|
|
|
+ struct gpio_chip *chip = desc->gdev->chip;
|
|
|
int offset = gpio_chip_hwgpio(desc);
|
|
|
|
|
|
if (value) {
|
|
@@ -1400,7 +1681,7 @@ static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
|
|
|
static void _gpio_set_open_source_value(struct gpio_desc *desc, bool value)
|
|
|
{
|
|
|
int err = 0;
|
|
|
- struct gpio_chip *chip = desc->chip;
|
|
|
+ struct gpio_chip *chip = desc->gdev->chip;
|
|
|
int offset = gpio_chip_hwgpio(desc);
|
|
|
|
|
|
if (value) {
|
|
@@ -1423,7 +1704,7 @@ static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
|
|
|
{
|
|
|
struct gpio_chip *chip;
|
|
|
|
|
|
- chip = desc->chip;
|
|
|
+ chip = desc->gdev->chip;
|
|
|
trace_gpio_value(desc_to_gpio(desc), 0, value);
|
|
|
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
|
|
|
_gpio_set_open_drain_value(desc, value);
|
|
@@ -1471,7 +1752,7 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep,
|
|
|
int i = 0;
|
|
|
|
|
|
while (i < array_size) {
|
|
|
- struct gpio_chip *chip = desc_array[i]->chip;
|
|
|
+ struct gpio_chip *chip = desc_array[i]->gdev->chip;
|
|
|
unsigned long mask[BITS_TO_LONGS(chip->ngpio)];
|
|
|
unsigned long bits[BITS_TO_LONGS(chip->ngpio)];
|
|
|
int count = 0;
|
|
@@ -1505,7 +1786,8 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep,
|
|
|
count++;
|
|
|
}
|
|
|
i++;
|
|
|
- } while ((i < array_size) && (desc_array[i]->chip == chip));
|
|
|
+ } while ((i < array_size) &&
|
|
|
+ (desc_array[i]->gdev->chip == chip));
|
|
|
/* push collected bits to outputs */
|
|
|
if (count != 0)
|
|
|
gpio_chip_set_multiple(chip, mask, bits);
|
|
@@ -1525,10 +1807,9 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep,
|
|
|
*/
|
|
|
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
- if (!desc)
|
|
|
- return;
|
|
|
+ VALIDATE_DESC_VOID(desc);
|
|
|
/* Should be using gpio_set_value_cansleep() */
|
|
|
- WARN_ON(desc->chip->can_sleep);
|
|
|
+ WARN_ON(desc->gdev->chip->can_sleep);
|
|
|
_gpiod_set_raw_value(desc, value);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiod_set_raw_value);
|
|
@@ -1546,10 +1827,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value);
|
|
|
*/
|
|
|
void gpiod_set_value(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
- if (!desc)
|
|
|
- return;
|
|
|
+ VALIDATE_DESC_VOID(desc);
|
|
|
/* Should be using gpio_set_value_cansleep() */
|
|
|
- WARN_ON(desc->chip->can_sleep);
|
|
|
+ WARN_ON(desc->gdev->chip->can_sleep);
|
|
|
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
|
|
value = !value;
|
|
|
_gpiod_set_raw_value(desc, value);
|
|
@@ -1607,9 +1887,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value);
|
|
|
*/
|
|
|
int gpiod_cansleep(const struct gpio_desc *desc)
|
|
|
{
|
|
|
- if (!desc)
|
|
|
- return 0;
|
|
|
- return desc->chip->can_sleep;
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
+ return desc->gdev->chip->can_sleep;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiod_cansleep);
|
|
|
|
|
@@ -1625,9 +1904,8 @@ int gpiod_to_irq(const struct gpio_desc *desc)
|
|
|
struct gpio_chip *chip;
|
|
|
int offset;
|
|
|
|
|
|
- if (!desc)
|
|
|
- return -EINVAL;
|
|
|
- chip = desc->chip;
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
+ chip = desc->gdev->chip;
|
|
|
offset = gpio_chip_hwgpio(desc);
|
|
|
return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO;
|
|
|
}
|
|
@@ -1646,14 +1924,14 @@ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
|
|
|
if (offset >= chip->ngpio)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (test_bit(FLAG_IS_OUT, &chip->desc[offset].flags)) {
|
|
|
+ if (test_bit(FLAG_IS_OUT, &chip->gpiodev->descs[offset].flags)) {
|
|
|
chip_err(chip,
|
|
|
"%s: tried to flag a GPIO set as output for IRQ\n",
|
|
|
__func__);
|
|
|
return -EIO;
|
|
|
}
|
|
|
|
|
|
- set_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags);
|
|
|
+ set_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
|
|
|
return 0;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq);
|
|
@@ -1671,10 +1949,37 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
|
|
|
if (offset >= chip->ngpio)
|
|
|
return;
|
|
|
|
|
|
- clear_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags);
|
|
|
+ clear_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
|
|
|
|
|
|
+bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset)
|
|
|
+{
|
|
|
+ if (offset >= chip->ngpio)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return test_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(gpiochip_line_is_irq);
|
|
|
+
|
|
|
+bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset)
|
|
|
+{
|
|
|
+ if (offset >= chip->ngpio)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return test_bit(FLAG_OPEN_DRAIN, &chip->gpiodev->descs[offset].flags);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain);
|
|
|
+
|
|
|
+bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset)
|
|
|
+{
|
|
|
+ if (offset >= chip->ngpio)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return test_bit(FLAG_OPEN_SOURCE, &chip->gpiodev->descs[offset].flags);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source);
|
|
|
+
|
|
|
/**
|
|
|
* gpiod_get_raw_value_cansleep() - return a gpio's raw value
|
|
|
* @desc: gpio whose value will be returned
|
|
@@ -1687,8 +1992,7 @@ EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
|
|
|
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
|
|
|
{
|
|
|
might_sleep_if(extra_checks);
|
|
|
- if (!desc)
|
|
|
- return 0;
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
return _gpiod_get_raw_value(desc);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiod_get_raw_value_cansleep);
|
|
@@ -1707,9 +2011,7 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc)
|
|
|
int value;
|
|
|
|
|
|
might_sleep_if(extra_checks);
|
|
|
- if (!desc)
|
|
|
- return 0;
|
|
|
-
|
|
|
+ VALIDATE_DESC(desc);
|
|
|
value = _gpiod_get_raw_value(desc);
|
|
|
if (value < 0)
|
|
|
return value;
|
|
@@ -1734,8 +2036,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
|
|
|
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
might_sleep_if(extra_checks);
|
|
|
- if (!desc)
|
|
|
- return;
|
|
|
+ VALIDATE_DESC_VOID(desc);
|
|
|
_gpiod_set_raw_value(desc, value);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep);
|
|
@@ -1753,9 +2054,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep);
|
|
|
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
|
|
|
{
|
|
|
might_sleep_if(extra_checks);
|
|
|
- if (!desc)
|
|
|
- return;
|
|
|
-
|
|
|
+ VALIDATE_DESC_VOID(desc);
|
|
|
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
|
|
value = !value;
|
|
|
_gpiod_set_raw_value(desc, value);
|
|
@@ -2358,8 +2657,8 @@ static void gpiochip_free_hogs(struct gpio_chip *chip)
|
|
|
int id;
|
|
|
|
|
|
for (id = 0; id < chip->ngpio; id++) {
|
|
|
- if (test_bit(FLAG_IS_HOGGED, &chip->desc[id].flags))
|
|
|
- gpiochip_free_own_desc(&chip->desc[id]);
|
|
|
+ if (test_bit(FLAG_IS_HOGGED, &chip->gpiodev->descs[id].flags))
|
|
|
+ gpiochip_free_own_desc(&chip->gpiodev->descs[id]);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2456,17 +2755,38 @@ void gpiod_put_array(struct gpio_descs *descs)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(gpiod_put_array);
|
|
|
|
|
|
+static int __init gpiolib_dev_init(void)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Register GPIO sysfs bus */
|
|
|
+ ret = bus_register(&gpio_bus_type);
|
|
|
+ if (ret < 0) {
|
|
|
+ pr_err("gpiolib: could not register GPIO bus type\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip");
|
|
|
+ if (ret < 0) {
|
|
|
+ pr_err("gpiolib: failed to allocate char dev region\n");
|
|
|
+ bus_unregister(&gpio_bus_type);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+core_initcall(gpiolib_dev_init);
|
|
|
+
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
|
|
-static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|
|
+static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
|
|
|
{
|
|
|
unsigned i;
|
|
|
- unsigned gpio = chip->base;
|
|
|
- struct gpio_desc *gdesc = &chip->desc[0];
|
|
|
+ struct gpio_chip *chip = gdev->chip;
|
|
|
+ unsigned gpio = gdev->base;
|
|
|
+ struct gpio_desc *gdesc = &gdev->descs[0];
|
|
|
int is_out;
|
|
|
int is_irq;
|
|
|
|
|
|
- for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
|
|
|
+ for (i = 0; i < gdev->ngpio; i++, gpio++, gdesc++) {
|
|
|
if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) {
|
|
|
if (gdesc->name) {
|
|
|
seq_printf(s, " gpio-%-3d (%-20.20s)\n",
|
|
@@ -2492,16 +2812,16 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|
|
static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
- struct gpio_chip *chip = NULL;
|
|
|
+ struct gpio_device *gdev = NULL;
|
|
|
loff_t index = *pos;
|
|
|
|
|
|
s->private = "";
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
- list_for_each_entry(chip, &gpio_chips, list)
|
|
|
+ list_for_each_entry(gdev, &gpio_devices, list)
|
|
|
if (index-- == 0) {
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
- return chip;
|
|
|
+ return gdev;
|
|
|
}
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
@@ -2511,14 +2831,14 @@ static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
|
|
|
static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
- struct gpio_chip *chip = v;
|
|
|
+ struct gpio_device *gdev = v;
|
|
|
void *ret = NULL;
|
|
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
- if (list_is_last(&chip->list, &gpio_chips))
|
|
|
+ if (list_is_last(&gdev->list, &gpio_devices))
|
|
|
ret = NULL;
|
|
|
else
|
|
|
- ret = list_entry(chip->list.next, struct gpio_chip, list);
|
|
|
+ ret = list_entry(gdev->list.next, struct gpio_device, list);
|
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
|
|
s->private = "\n";
|
|
@@ -2533,15 +2853,24 @@ static void gpiolib_seq_stop(struct seq_file *s, void *v)
|
|
|
|
|
|
static int gpiolib_seq_show(struct seq_file *s, void *v)
|
|
|
{
|
|
|
- struct gpio_chip *chip = v;
|
|
|
- struct device *dev;
|
|
|
+ struct gpio_device *gdev = v;
|
|
|
+ struct gpio_chip *chip = gdev->chip;
|
|
|
+ struct device *parent;
|
|
|
+
|
|
|
+ if (!chip) {
|
|
|
+ seq_printf(s, "%s%s: (dangling chip)", (char *)s->private,
|
|
|
+ dev_name(&gdev->dev));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
|
|
|
- seq_printf(s, "%sGPIOs %d-%d", (char *)s->private,
|
|
|
- chip->base, chip->base + chip->ngpio - 1);
|
|
|
- dev = chip->parent;
|
|
|
- if (dev)
|
|
|
- seq_printf(s, ", %s/%s", dev->bus ? dev->bus->name : "no-bus",
|
|
|
- dev_name(dev));
|
|
|
+ seq_printf(s, "%s%s: GPIOs %d-%d", (char *)s->private,
|
|
|
+ dev_name(&gdev->dev),
|
|
|
+ gdev->base, gdev->base + gdev->ngpio - 1);
|
|
|
+ parent = chip->parent;
|
|
|
+ if (parent)
|
|
|
+ seq_printf(s, ", parent: %s/%s",
|
|
|
+ parent->bus ? parent->bus->name : "no-bus",
|
|
|
+ dev_name(parent));
|
|
|
if (chip->label)
|
|
|
seq_printf(s, ", %s", chip->label);
|
|
|
if (chip->can_sleep)
|
|
@@ -2551,7 +2880,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v)
|
|
|
if (chip->dbg_show)
|
|
|
chip->dbg_show(s, chip);
|
|
|
else
|
|
|
- gpiolib_dbg_show(s, chip);
|
|
|
+ gpiolib_dbg_show(s, gdev);
|
|
|
|
|
|
return 0;
|
|
|
}
|