Merge branch 'chardev' into devel
authorLinus Walleij <linus.walleij@linaro.org>
Tue, 9 Feb 2016 11:58:19 +0000 (12:58 +0100)
committerLinus Walleij <linus.walleij@linaro.org>
Tue, 9 Feb 2016 11:58:19 +0000 (12:58 +0100)
15 files changed:
Documentation/ABI/obsolete/sysfs-gpio [new file with mode: 0644]
Documentation/ABI/testing/gpio-cdev [new file with mode: 0644]
Documentation/ABI/testing/sysfs-gpio [deleted file]
MAINTAINERS
drivers/gpio/gpiolib-sysfs.c
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h
include/linux/gpio/driver.h
include/uapi/linux/Kbuild
include/uapi/linux/gpio.h [new file with mode: 0644]
tools/Makefile
tools/gpio/Makefile [new file with mode: 0644]
tools/gpio/gpio-utils.c [new file with mode: 0644]
tools/gpio/gpio-utils.h [new file with mode: 0644]
tools/gpio/lsgpio.c [new file with mode: 0644]

diff --git a/Documentation/ABI/obsolete/sysfs-gpio b/Documentation/ABI/obsolete/sysfs-gpio
new file mode 100644 (file)
index 0000000..867c1fa
--- /dev/null
@@ -0,0 +1,30 @@
+What:          /sys/class/gpio/
+Date:          July 2008
+KernelVersion: 2.6.27
+Contact:       Linus Walleij <linusw@kernel.org>
+Description:
+
+  As a Kconfig option, individual GPIO signals may be accessed from
+  userspace.  GPIOs are only made available to userspace by an explicit
+  "export" operation.  If a given GPIO is not claimed for use by
+  kernel code, it may be exported by userspace (and unexported later).
+  Kernel code may export it for complete or partial access.
+
+  GPIOs are identified as they are inside the kernel, using integers in
+  the range 0..INT_MAX.  See Documentation/gpio.txt for more information.
+
+    /sys/class/gpio
+       /export ... asks the kernel to export a GPIO to userspace
+       /unexport ... to return a GPIO to the kernel
+       /gpioN ... for each exported GPIO #N OR
+       /<LINE-NAME> ... for a properly named GPIO line
+           /value ... always readable, writes fail for input GPIOs
+           /direction ... r/w as: in, out (default low); write: high, low
+           /edge ... r/w as: none, falling, rising, both
+       /gpiochipN ... for each gpiochip; #N is its first GPIO
+           /base ... (r/o) same as N
+           /label ... (r/o) descriptive, not necessarily unique
+           /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
+
+  This ABI is deprecated and will be removed after 2020. It is
+  replaced with the GPIO character device.
diff --git a/Documentation/ABI/testing/gpio-cdev b/Documentation/ABI/testing/gpio-cdev
new file mode 100644 (file)
index 0000000..7b265fb
--- /dev/null
@@ -0,0 +1,26 @@
+What:          /dev/gpiochip[0-9]+
+Date:          November 2015
+KernelVersion: 4.4
+Contact:       linux-gpio@vger.kernel.org
+Description:
+               The character device files /dev/gpiochip* are the interface
+               between GPIO chips and userspace.
+
+               The ioctl(2)-based ABI is defined and documented in
+               [include/uapi]<linux/gpio.h>.
+
+               The following file operations are supported:
+
+               open(2)
+               Currently the only useful flags are O_RDWR.
+
+               ioctl(2)
+               Initiate various actions.
+               See the inline documentation in [include/uapi]<linux/gpio.h>
+               for descriptions of all ioctls.
+
+               close(2)
+               Stops and free up the I/O contexts that was associated
+               with the file descriptor.
+
+Users:         TBD
diff --git a/Documentation/ABI/testing/sysfs-gpio b/Documentation/ABI/testing/sysfs-gpio
deleted file mode 100644 (file)
index 55ffa2d..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-What:          /sys/class/gpio/
-Date:          July 2008
-KernelVersion: 2.6.27
-Contact:       David Brownell <dbrownell@users.sourceforge.net>
-Description:
-
-  As a Kconfig option, individual GPIO signals may be accessed from
-  userspace.  GPIOs are only made available to userspace by an explicit
-  "export" operation.  If a given GPIO is not claimed for use by
-  kernel code, it may be exported by userspace (and unexported later).
-  Kernel code may export it for complete or partial access.
-
-  GPIOs are identified as they are inside the kernel, using integers in
-  the range 0..INT_MAX.  See Documentation/gpio.txt for more information.
-
-    /sys/class/gpio
-       /export ... asks the kernel to export a GPIO to userspace
-       /unexport ... to return a GPIO to the kernel
-       /gpioN ... for each exported GPIO #N OR
-       /<LINE-NAME> ... for a properly named GPIO line
-           /value ... always readable, writes fail for input GPIOs
-           /direction ... r/w as: in, out (default low); write: high, low
-           /edge ... r/w as: none, falling, rising, both
-       /gpiochipN ... for each gpiochip; #N is its first GPIO
-           /base ... (r/o) same as N
-           /label ... (r/o) descriptive, not necessarily unique
-           /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
-
index f043e957698d59754ee5dd7f36e09b8852ad3b5d..7b8c0583381a2d5463c2f5510fb36c8498497149 100644 (file)
@@ -4821,10 +4821,14 @@ L:      linux-gpio@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git
 S:     Maintained
 F:     Documentation/gpio/
+F:     Documentation/ABI/testing/gpio-cdev
+F:     Documentation/ABI/obsolete/sysfs-gpio
 F:     drivers/gpio/
 F:     include/linux/gpio/
 F:     include/linux/gpio.h
 F:     include/asm-generic/gpio.h
+F:     include/uapi/linux/gpio.h
+F:     tools/gpio/
 
 GRE DEMULTIPLEXER DRIVER
 M:     Dmitry Kozlov <xeb@mail.ru>
index 405dfcaadc4cf688d9cb8c4277f561031fe67f0f..28d3bf2328aa35754493e4352d93641762493500 100644 (file)
@@ -547,6 +547,7 @@ static struct class gpio_class = {
 int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
 {
        struct gpio_chip        *chip;
+       struct gpio_device      *gdev;
        struct gpiod_data       *data;
        unsigned long           flags;
        int                     status;
@@ -566,6 +567,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
        }
 
        chip = desc->chip;
+       gdev = chip->gpiodev;
 
        mutex_lock(&sysfs_lock);
 
@@ -605,7 +607,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
        if (chip->names && chip->names[offset])
                ioname = chip->names[offset];
 
-       dev = device_create_with_groups(&gpio_class, chip->parent,
+       dev = device_create_with_groups(&gpio_class, &gdev->dev,
                                        MKDEV(0, 0), data, gpio_groups,
                                        ioname ? ioname : "gpio%u",
                                        desc_to_gpio(desc));
@@ -771,7 +773,7 @@ static int __init gpiolib_sysfs_init(void)
 {
        int             status;
        unsigned long   flags;
-       struct gpio_chip *chip;
+       struct gpio_device *gdev;
 
        status = class_register(&gpio_class);
        if (status < 0)
@@ -784,8 +786,8 @@ static int __init gpiolib_sysfs_init(void)
         * registered, and so arch_initcall() can always gpio_export().
         */
        spin_lock_irqsave(&gpio_lock, flags);
-       list_for_each_entry(chip, &gpio_chips, list) {
-               if (chip->cdev)
+       list_for_each_entry(gdev, &gpio_devices, list) {
+               if (gdev->chip->cdev)
                        continue;
 
                /*
@@ -798,7 +800,7 @@ static int __init gpiolib_sysfs_init(void)
                 * gpio_lock prevents us from doing this.
                 */
                spin_unlock_irqrestore(&gpio_lock, flags);
-               status = gpiochip_sysfs_register(chip);
+               status = gpiochip_sysfs_register(gdev->chip);
                spin_lock_irqsave(&gpio_lock, flags);
        }
        spin_unlock_irqrestore(&gpio_lock, flags);
index 5c1ba879f889055865d5f2adc7df467ffb9a0a66..70e0fff0a8a7b951a767d86ac354471ccc5a9431 100644 (file)
 #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"
 
 #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->chip->base <= gpio &&
+                   gdev->chip->base + gdev->chip->ngpio > gpio) {
                        spin_unlock_irqrestore(&gpio_lock, flags);
-                       return &chip->desc[gpio - chip->base];
+                       return &gdev->chip->desc[gpio - gdev->chip->base];
                }
        }
 
@@ -125,16 +138,16 @@ 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->chip->base + gdev->chip->ngpio <= base)
                        break;
                else
                        /* nope, check the space right before the chip */
-                       base = chip->base - ngpio;
+                       base = gdev->chip->base - ngpio;
        }
 
        if (gpio_is_valid(base)) {
@@ -187,18 +200,28 @@ 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) {
+               /*
+                * The list may contain dangling GPIO devices with no
+                * live chip assigned.
+                */
+               if (!iterator->chip)
+                       continue;
+               if (iterator->chip->base >=
+                   gdev->chip->base + gdev->chip->ngpio) {
                        /*
                         * Iterator is the first GPIO chip so there is no
                         * previous one
@@ -211,8 +234,8 @@ static int gpiochip_add_to_list(struct gpio_chip *chip)
                                 * [base, base + ngpio - 1]) between previous
                                 * and iterator chip.
                                 */
-                               if (previous->base + previous->ngpio
-                                               <= chip->base)
+                               if (previous->chip->base + previous->chip->ngpio
+                                               <= gdev->chip->base)
                                        goto found;
                        }
                }
@@ -225,18 +248,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->chip->base + iterator->chip->ngpio <= gdev->chip->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,16 +268,16 @@ 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->chip->ngpio; ++i) {
+                       struct gpio_desc *gpio = &gdev->chip->desc[i];
 
                        if (!gpio->name || !name)
                                continue;
@@ -290,8 +313,8 @@ 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(&gc->gpiodev->dev,
+                                "Detected name collision for GPIO name '%s'\n",
                                 gc->names[i]);
        }
 
@@ -302,6 +325,88 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
        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;
+       struct gpiochip_info chipinfo;
+
+       /* We fail any subsequent ioctl():s when the chip is gone */
+       if (!chip)
+               return -ENODEV;
+
+       if (cmd == GPIO_GET_CHIPINFO_IOCTL) {
+               /* Fill in the struct and pass to userspace */
+               strncpy(chipinfo.name, dev_name(&gdev->dev),
+                       sizeof(chipinfo.name));
+               chipinfo.name[sizeof(chipinfo.name)-1] = '\0';
+               chipinfo.lines = chip->ngpio;
+               if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
+                       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);
+}
+
 /**
  * gpiochip_add_data() - register a gpio_chip
  * @chip: the chip to register, with chip->base initialized
@@ -323,19 +428,61 @@ 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 = kmalloc(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;
+
+       /* FIXME: devm_kcalloc() these and move to gpio_device */
+       descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
+       if (!descs) {
+               status = -ENOMEM;
+               goto err_free_gdev;
+       }
 
+       /* FIXME: move driver data into gpio_device dev_set_drvdata() */
        chip->data = data;
 
        if (chip->ngpio == 0) {
                chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
-               return -EINVAL;
+               status = -EINVAL;
+               goto err_free_descs;
        }
 
        spin_lock_irqsave(&gpio_lock, flags);
@@ -350,15 +497,16 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
                chip->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;
        }
 
-       for (id = 0; id < chip->ngpio; id++) {
-               struct gpio_desc *desc = &descs[id];
+       for (i = 0; i < chip->ngpio; i++) {
+               struct gpio_desc *desc = &descs[i];
 
+               /* REVISIT: maybe a pointer to gpio_device is better */
                desc->chip = chip;
 
                /* REVISIT: most hardware initializes GPIOs as inputs (often
@@ -369,18 +517,15 @@ 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
+       /* FIXME: move pin ranges to gpio_device */
        INIT_LIST_HEAD(&chip->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,28 +536,58 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
 
        acpi_gpiochip_add(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(chip);
        if (status)
-               goto err_remove_chip;
+               goto err_remove_device;
 
+       /* 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\n", __func__,
                chip->base, chip->base + chip->ngpio - 1,
                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);
+       kfree(gdev);
        /* 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,
@@ -429,15 +604,18 @@ 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;
        bool            requested = false;
 
-       gpiochip_sysfs_unregister(chip);
+       /* Numb the device, cancelling all outstanding operations */
+       gdev->chip = NULL;
 
+       /* FIXME: should the legacy sysfs handling be moved to gpio_device? */
+       gpiochip_sysfs_unregister(chip);
        gpiochip_irqchip_remove(chip);
-
        acpi_gpiochip_remove(chip);
        gpiochip_remove_pin_ranges(chip);
        gpiochip_free_hogs(chip);
@@ -450,15 +628,23 @@ void gpiochip_remove(struct gpio_chip *chip)
                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(&chip->gpiodev->dev,
                         "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
 
+       /* FIXME: need to be moved to gpio_device and held there */
        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 +663,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 +807,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 +825,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)
@@ -985,10 +1175,10 @@ int gpiod_request(struct gpio_desc *desc, const char *label)
        if (!chip)
                goto done;
 
-       if (try_module_get(chip->owner)) {
+       if (try_module_get(chip->gpiodev->owner)) {
                status = __gpiod_request(desc, label);
                if (status < 0)
-                       module_put(chip->owner);
+                       module_put(chip->gpiodev->owner);
        }
 
 done:
@@ -1034,7 +1224,7 @@ 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);
+               module_put(desc->chip->gpiodev->owner);
        else
                WARN_ON(extra_checks);
 }
@@ -2456,6 +2646,26 @@ 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)
@@ -2492,16 +2702,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 +2721,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 +2743,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),
+                  chip->base, chip->base + chip->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)
index 99ed3b00ffe916f65229e343c1ba5ebefd9f83b7..1524ba0ca99dca53e6c3ff4c3e589591c93eb1ee 100644 (file)
 #ifndef GPIOLIB_H
 #define GPIOLIB_H
 
+#include <linux/gpio/driver.h>
 #include <linux/err.h>
 #include <linux/device.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
 
 enum of_gpio_flags;
 enum gpiod_flags;
 struct acpi_device;
 
+/**
+ * struct gpio_device - internal state container for GPIO devices
+ * @id: numerical ID number for the GPIO chip
+ * @dev: the GPIO device struct
+ * @chrdev: character device for the GPIO device
+ * @owner: helps prevent removal of modules exporting active GPIOs
+ * @chip: pointer to the corresponding gpiochip, holding static
+ * data for this device
+ * @list: links gpio_device:s together for traversal
+ *
+ * This state container holds most of the runtime variable data
+ * for a GPIO device and can hold references and live on after the
+ * GPIO chip has been removed, if it is still being used from
+ * userspace.
+ */
+struct gpio_device {
+       int                     id;
+       struct device           dev;
+       struct cdev             chrdev;
+       struct module           *owner;
+       struct gpio_chip        *chip;
+       struct list_head        list;
+};
+
 /**
  * struct acpi_gpio_info - ACPI GPIO specific information
  * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
@@ -90,7 +117,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
 struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
 
 extern struct spinlock gpio_lock;
-extern struct list_head gpio_chips;
+extern struct list_head gpio_devices;
 
 struct gpio_desc {
        struct gpio_chip        *chip;
@@ -149,17 +176,17 @@ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc)
 /* With chip prefix */
 
 #define chip_emerg(chip, fmt, ...)                                     \
-       pr_emerg("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+       dev_emerg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
 #define chip_crit(chip, fmt, ...)                                      \
-       pr_crit("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+       dev_crit(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
 #define chip_err(chip, fmt, ...)                                       \
-       pr_err("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+       dev_err(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
 #define chip_warn(chip, fmt, ...)                                      \
-       pr_warn("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+       dev_warn(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
 #define chip_info(chip, fmt, ...)                                      \
-       pr_info("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+       dev_info(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
 #define chip_dbg(chip, fmt, ...)                                       \
-       pr_debug("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
+       dev_dbg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
 
 #ifdef CONFIG_GPIO_SYSFS
 
index 82fda487453fc2e43732e37a4bf38353241d5aa3..f3f1dbd43c9b6d05fd198384d2542ca0b2d9a5b6 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef __LINUX_GPIO_DRIVER_H
 #define __LINUX_GPIO_DRIVER_H
 
+#include <linux/device.h>
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/irq.h>
 #include <linux/pinctrl/pinctrl.h>
 #include <linux/kconfig.h>
 
-struct device;
 struct gpio_desc;
 struct of_phandle_args;
 struct device_node;
 struct seq_file;
+struct gpio_device;
 
 #ifdef CONFIG_GPIOLIB
 
 /**
  * struct gpio_chip - abstract a GPIO controller
  * @label: for diagnostics
+ * @gpiodev: the internal state holder, opaque struct
  * @parent: optional parent device providing the GPIOs
  * @cdev: class device used by sysfs interface (may be NULL)
  * @owner: helps prevent removal of modules exporting active GPIOs
  * @data: per-instance data assigned by the driver
- * @list: links gpio_chips together for traversal
  * @request: optional hook for chip-specific activation, such as
  *     enabling module power and clock; may sleep
  * @free: optional hook for chip-specific deactivation, such as
@@ -107,11 +108,11 @@ struct seq_file;
  */
 struct gpio_chip {
        const char              *label;
+       struct gpio_device      *gpiodev;
        struct device           *parent;
        struct device           *cdev;
        struct module           *owner;
        void                    *data;
-       struct list_head        list;
 
        int                     (*request)(struct gpio_chip *chip,
                                                unsigned offset);
index ebd10e6245984fb5c38b6ee13c3861b8e78ed040..5c9ae6a9b7f5e5b8e953d5a1efc16704a7b1fd69 100644 (file)
@@ -138,6 +138,7 @@ header-y += genetlink.h
 header-y += gen_stats.h
 header-y += gfs2_ondisk.h
 header-y += gigaset_dev.h
+header-y += gpio.h
 header-y += gsmmux.h
 header-y += hdlcdrv.h
 header-y += hdlc.h
diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h
new file mode 100644 (file)
index 0000000..3188a87
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * <linux/gpio.h> - userspace ABI for the GPIO character devices
+ *
+ * Copyright (C) 2015 Linus Walleij
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef _UAPI_GPIO_H_
+#define _UAPI_GPIO_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * struct gpiochip_info - Information about a certain GPIO chip
+ * @name: the name of this GPIO chip
+ * @lines: number of GPIO lines on this chip
+ */
+struct gpiochip_info {
+       char name[32];
+       __u32 lines;
+};
+
+#define GPIO_GET_CHIPINFO_IOCTL _IOR('o', 0x01, struct gpiochip_info)
+
+#endif /* _UAPI_GPIO_H_ */
index 6339f6ac3ccbdf1ef285df9871baf6ad34aad410..f41e7c6ea23e241d06c2941eef92053b2ad920ba 100644 (file)
@@ -13,6 +13,7 @@ help:
        @echo '  cpupower               - a tool for all things x86 CPU power'
        @echo '  firewire               - the userspace part of nosy, an IEEE-1394 traffic sniffer'
        @echo '  freefall               - laptop accelerometer program for disk protection'
+       @echo '  gpio                   - GPIO tools'
        @echo '  hv                     - tools used when in Hyper-V clients'
        @echo '  iio                    - IIO tools'
        @echo '  lguest                 - a minimal 32-bit x86 hypervisor'
@@ -53,7 +54,7 @@ acpi: FORCE
 cpupower: FORCE
        $(call descend,power/$@)
 
-cgroup firewire hv guest spi usb virtio vm net iio: FORCE
+cgroup firewire hv guest spi usb virtio vm net iio gpio: FORCE
        $(call descend,$@)
 
 liblockdep: FORCE
@@ -119,7 +120,7 @@ acpi_clean:
 cpupower_clean:
        $(call descend,power/cpupower,clean)
 
-cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean:
+cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean:
        $(call descend,$(@:_clean=),clean)
 
 liblockdep_clean:
@@ -155,6 +156,7 @@ build_clean:
 clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
                perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
                vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
-               freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean
+               freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
+               gpio_clean
 
 .PHONY: FORCE
diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile
new file mode 100644 (file)
index 0000000..4d198d5
--- /dev/null
@@ -0,0 +1,12 @@
+CC = $(CROSS_COMPILE)gcc
+CFLAGS += -Wall -g -D_GNU_SOURCE
+
+all: lsgpio
+
+lsgpio: lsgpio.o gpio-utils.o
+
+%.o: %.c gpio-utils.h
+
+.PHONY: clean
+clean:
+       rm -f *.o lsgpio
diff --git a/tools/gpio/gpio-utils.c b/tools/gpio/gpio-utils.c
new file mode 100644 (file)
index 0000000..8208718
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * GPIO tools - helpers library for the GPIO tools
+ *
+ * Copyright (C) 2015 Linus Walleij
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include "gpio-utils.h"
diff --git a/tools/gpio/gpio-utils.h b/tools/gpio/gpio-utils.h
new file mode 100644 (file)
index 0000000..b18209a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * GPIO tools - utility helpers library for the GPIO tools
+ *
+ * Copyright (C) 2015 Linus Walleij
+ *
+ * Portions copied from iio_utils and lssio:
+ * Copyright (c) 2010 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
+ * Copyright (c) 2008 Jonathan Cameron
+ * *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef _GPIO_UTILS_H_
+#define _GPIO_UTILS_H_
+
+#include <string.h>
+
+static inline int check_prefix(const char *str, const char *prefix)
+{
+       return strlen(str) > strlen(prefix) &&
+               strncmp(str, prefix, strlen(prefix)) == 0;
+}
+
+#endif /* _GPIO_UTILS_H_ */
diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c
new file mode 100644 (file)
index 0000000..4cfe29d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * lsgpio - example on how to list the GPIO lines on a system
+ *
+ * Copyright (C) 2015 Linus Walleij
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Usage:
+ *     lsgpio <-n device-name>
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <linux/gpio.h>
+
+#include "gpio-utils.h"
+
+int list_device(const char *device_name)
+{
+       struct gpiochip_info cinfo;
+       char *chrdev_name;
+       int fd;
+       int ret;
+
+       ret = asprintf(&chrdev_name, "/dev/%s", device_name);
+       if (ret < 0)
+               return -ENOMEM;
+
+       fd = open(chrdev_name, 0);
+       if (fd == -1) {
+               ret = -errno;
+               fprintf(stderr, "Failed to open %s\n", chrdev_name);
+               goto free_chrdev_name;
+       }
+
+       /* Inspect this GPIO chip */
+       ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo);
+       if (ret == -1) {
+               ret = -errno;
+               fprintf(stderr, "Failed to retrieve GPIO fd\n");
+               if (close(fd) == -1)
+                       perror("Failed to close GPIO character device file");
+
+               goto free_chrdev_name;
+       }
+       fprintf(stdout, "GPIO chip: %s, %u GPIO lines\n",
+               cinfo.name, cinfo.lines);
+
+       if (close(fd) == -1)  {
+               ret = -errno;
+               goto free_chrdev_name;
+       }
+
+free_chrdev_name:
+       free(chrdev_name);
+
+       return ret;
+
+}
+
+void print_usage(void)
+{
+       fprintf(stderr, "Usage: lsgpio [options]...\n"
+               "List GPIO chips, lines and states\n"
+               "  -n <name>  List GPIOs on a named device\n"
+               "  -?         This helptext\n"
+       );
+}
+
+int main(int argc, char **argv)
+{
+       const char *device_name;
+       int ret;
+       int c;
+
+       while ((c = getopt(argc, argv, "n:")) != -1) {
+               switch (c) {
+               case 'n':
+                       device_name = optarg;
+                       break;
+               case '?':
+                       print_usage();
+                       return -1;
+               }
+       }
+
+       if (device_name)
+               ret = list_device(device_name);
+       else {
+               const struct dirent *ent;
+               DIR *dp;
+
+               /* List all GPIO devices one at a time */
+               dp = opendir("/dev");
+               if (!dp) {
+                       ret = -errno;
+                       goto error_out;
+               }
+
+               ret = -ENOENT;
+               while (ent = readdir(dp), ent) {
+                       if (check_prefix(ent->d_name, "gpiochip")) {
+                               ret = list_device(ent->d_name);
+                               if (ret)
+                                       break;
+                       }
+               }
+
+               ret = 0;
+               if (closedir(dp) == -1) {
+                       perror("scanning devices: Failed to close directory");
+                       ret = -errno;
+               }
+       }
+error_out:
+       return ret;
+}
This page took 0.047276 seconds and 5 git commands to generate.