]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
gpio: sysfs interface
authorDavid Brownell <dbrownell@users.sourceforge.net>
Fri, 25 Jul 2008 08:46:07 +0000 (01:46 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 25 Jul 2008 17:53:30 +0000 (10:53 -0700)
This adds a simple sysfs interface for GPIOs.

    /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
    /value ... always readable, writes fail for input GPIOs
    /direction ... r/w as: in, out (default low); write high, low
/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 .. N+(ngpio - 1)

GPIOs claimed by kernel code may be exported by its owner using a new
gpio_export() call, which should be most useful for driver debugging.
Such exports may optionally be done without a "direction" attribute.

Userspace may ask to take over a GPIO by writing to a sysfs control file,
helping to cope with incomplete board support or other "one-off"
requirements that don't merit full kernel support:

  echo 23 > /sys/class/gpio/export
... will gpio_request(23, "sysfs") and gpio_export(23);
use /sys/class/gpio/gpio-23/direction to (re)configure it,
when that GPIO can be used as both input and output.
  echo 23 > /sys/class/gpio/unexport
... will gpio_free(23), when it was exported as above

The extra D-space footprint is a few hundred bytes, except for the sysfs
resources associated with each exported GPIO.  The additional I-space
footprint is about two thirds of the current size of gpiolib (!).  Since
no /dev node creation is involved, no "udev" support is needed.

Related changes:

  * This adds a device pointer to "struct gpio_chip".  When GPIO
    providers initialize that, sysfs gpio class devices become children of
    that device instead of being "virtual" devices.

  * The (few) gpio_chip providers which have such a device node have
    been updated.

  * Some gpio_chip drivers also needed to update their module "owner"
    field ...  for which missing kerneldoc was added.

  * Some gpio_chips don't support input GPIOs.  Those GPIOs are now
    flagged appropriately when the chip is registered.

Based on previous patches, and discussion both on and off LKML.

A Documentation/ABI/testing/sysfs-gpio update is ready to submit once this
merges to mainline.

[akpm@linux-foundation.org: a few maintenance build fixes]
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Cc: Guennadi Liakhovetski <g.liakhovetski@pengutronix.de>
Cc: Greg KH <greg@kroah.com>
Cc: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
12 files changed:
Documentation/gpio.txt
arch/arm/plat-omap/gpio.c
arch/avr32/mach-at32ap/pio.c
drivers/gpio/Kconfig
drivers/gpio/gpiolib.c
drivers/gpio/mcp23s08.c
drivers/gpio/pca953x.c
drivers/gpio/pcf857x.c
drivers/i2c/chips/tps65010.c
drivers/mfd/htc-egpio.c
include/asm-generic/gpio.h
include/linux/gpio.h

index c35ca9e40d4ca8ae0cbf7c06c639d8531ae8d9dd..8b69811a964249c7b346eefa7c316f5e7985e910 100644 (file)
@@ -347,15 +347,12 @@ necessarily be nonportable.
 Dynamic definition of GPIOs is not currently standard; for example, as
 a side effect of configuring an add-on board with some GPIO expanders.
 
 Dynamic definition of GPIOs is not currently standard; for example, as
 a side effect of configuring an add-on board with some GPIO expanders.
 
-These calls are purely for kernel space, but a userspace API could be built
-on top of them.
-
 
 GPIO implementor's framework (OPTIONAL)
 =======================================
 As noted earlier, there is an optional implementation framework making it
 easier for platforms to support different kinds of GPIO controller using
 
 GPIO implementor's framework (OPTIONAL)
 =======================================
 As noted earlier, there is an optional implementation framework making it
 easier for platforms to support different kinds of GPIO controller using
-the same programming interface.
+the same programming interface.  This framework is called "gpiolib".
 
 As a debugging aid, if debugfs is available a /sys/kernel/debug/gpio file
 will be found there.  That will list all the controllers registered through
 
 As a debugging aid, if debugfs is available a /sys/kernel/debug/gpio file
 will be found there.  That will list all the controllers registered through
@@ -439,4 +436,120 @@ becomes available.  That may mean the device should not be registered until
 calls for that GPIO can work.  One way to address such dependencies is for
 such gpio_chip controllers to provide setup() and teardown() callbacks to
 board specific code; those board specific callbacks would register devices
 calls for that GPIO can work.  One way to address such dependencies is for
 such gpio_chip controllers to provide setup() and teardown() callbacks to
 board specific code; those board specific callbacks would register devices
-once all the necessary resources are available.
+once all the necessary resources are available, and remove them later when
+the GPIO controller device becomes unavailable.
+
+
+Sysfs Interface for Userspace (OPTIONAL)
+========================================
+Platforms which use the "gpiolib" implementors framework may choose to
+configure a sysfs user interface to GPIOs.  This is different from the
+debugfs interface, since it provides control over GPIO direction and
+value instead of just showing a gpio state summary.  Plus, it could be
+present on production systems without debugging support.
+
+Given approprate hardware documentation for the system, userspace could
+know for example that GPIO #23 controls the write protect line used to
+protect boot loader segments in flash memory.  System upgrade procedures
+may need to temporarily remove that protection, first importing a GPIO,
+then changing its output state, then updating the code before re-enabling
+the write protection.  In normal use, GPIO #23 would never be touched,
+and the kernel would have no need to know about it.
+
+Again depending on appropriate hardware documentation, on some systems
+userspace GPIO can be used to determine system configuration data that
+standard kernels won't know about.  And for some tasks, simple userspace
+GPIO drivers could be all that the system really needs.
+
+Note that standard kernel drivers exist for common "LEDs and Buttons"
+GPIO tasks:  "leds-gpio" and "gpio_keys", respectively.  Use those
+instead of talking directly to the GPIOs; they integrate with kernel
+frameworks better than your userspace code could.
+
+
+Paths in Sysfs
+--------------
+There are three kinds of entry in /sys/class/gpio:
+
+   -   Control interfaces used to get userspace control over GPIOs;
+
+   -   GPIOs themselves; and
+
+   -   GPIO controllers ("gpio_chip" instances).
+
+That's in addition to standard files including the "device" symlink.
+
+The control interfaces are write-only:
+
+    /sys/class/gpio/
+
+       "export" ... Userspace may ask the kernel to export control of
+               a GPIO to userspace by writing its number to this file.
+
+               Example:  "echo 19 > export" will create a "gpio19" node
+               for GPIO #19, if that's not requested by kernel code.
+
+       "unexport" ... Reverses the effect of exporting to userspace.
+
+               Example:  "echo 19 > unexport" will remove a "gpio19"
+               node exported using the "export" file.
+
+GPIO signals have paths like /sys/class/gpio/gpio42/ (for GPIO #42)
+and have the following read/write attributes:
+
+    /sys/class/gpio/gpioN/
+
+       "direction" ... reads as either "in" or "out".  This value may
+               normally be written.  Writing as "out" defaults to
+               initializing the value as low.  To ensure glitch free
+               operation, values "low" and "high" may be written to
+               configure the GPIO as an output with that initial value.
+
+               Note that this attribute *will not exist* if the kernel
+               doesn't support changing the direction of a GPIO, or
+               it was exported by kernel code that didn't explicitly
+               allow userspace to reconfigure this GPIO's direction.
+
+       "value" ... reads as either 0 (low) or 1 (high).  If the GPIO
+               is configured as an output, this value may be written;
+               any nonzero value is treated as high.
+
+GPIO controllers have paths like /sys/class/gpio/chipchip42/ (for the
+controller implementing GPIOs starting at #42) and have the following
+read-only attributes:
+
+    /sys/class/gpio/gpiochipN/
+
+       "base" ... same as N, the first GPIO managed by this chip
+
+       "label" ... provided for diagnostics (not always unique)
+
+       "ngpio" ... how many GPIOs this manges (N to N + ngpio - 1)
+
+Board documentation should in most cases cover what GPIOs are used for
+what purposes.  However, those numbers are not always stable; GPIOs on
+a daughtercard might be different depending on the base board being used,
+or other cards in the stack.  In such cases, you may need to use the
+gpiochip nodes (possibly in conjunction with schematics) to determine
+the correct GPIO number to use for a given signal.
+
+
+Exporting from Kernel code
+--------------------------
+Kernel code can explicitly manage exports of GPIOs which have already been
+requested using gpio_request():
+
+       /* export the GPIO to userspace */
+       int gpio_export(unsigned gpio, bool direction_may_change);
+
+       /* reverse gpio_export() */
+       void gpio_unexport();
+
+After a kernel driver requests a GPIO, it may only be made available in
+the sysfs interface by gpio_export().  The driver can control whether the
+signal direction may change.  This helps drivers prevent userspace code
+from accidentally clobbering important system state.
+
+This explicit exporting can help with debugging (by making some kinds
+of experiments easier), or can provide an always-there interface that's
+suitable for documenting as part of a board support package.
index 1903a3491ee9ff7f330e90a3c9a7df5cd4897d22..d8e9c2c3f0f684ad8d9375744a36579363497d37 100644 (file)
@@ -1488,6 +1488,9 @@ static int __init _omap_gpio_init(void)
                bank->chip.set = gpio_set;
                if (bank_is_mpuio(bank)) {
                        bank->chip.label = "mpuio";
                bank->chip.set = gpio_set;
                if (bank_is_mpuio(bank)) {
                        bank->chip.label = "mpuio";
+#ifdef CONFIG_ARCH_OMAP1
+                       bank->chip.dev = &omap_mpuio_device.dev;
+#endif
                        bank->chip.base = OMAP_MPUIO(0);
                } else {
                        bank->chip.label = "gpio";
                        bank->chip.base = OMAP_MPUIO(0);
                } else {
                        bank->chip.label = "gpio";
index 60da03ba7117e0c9a62ad4756ac5fa0aecf7d756..296294f8ed81c42a92963c7794f3a39897e9fa81 100644 (file)
@@ -360,6 +360,8 @@ static int __init pio_probe(struct platform_device *pdev)
        pio->chip.label = pio->name;
        pio->chip.base = pdev->id * 32;
        pio->chip.ngpio = 32;
        pio->chip.label = pio->name;
        pio->chip.base = pdev->id * 32;
        pio->chip.ngpio = 32;
+       pio->chip.dev = &pdev->dev;
+       pio->chip.owner = THIS_MODULE;
 
        pio->chip.direction_input = direction_input;
        pio->chip.get = gpio_get;
 
        pio->chip.direction_input = direction_input;
        pio->chip.get = gpio_get;
index fced1909cbba0dd055c2a01cd73fc5303138750b..6ec0e35b98e32739cd8fad5fa78716d99fdb6727 100644 (file)
@@ -23,6 +23,21 @@ config DEBUG_GPIO
          slower.  The diagnostics help catch the type of setup errors
          that are most common when setting up new platforms or boards.
 
          slower.  The diagnostics help catch the type of setup errors
          that are most common when setting up new platforms or boards.
 
+config GPIO_SYSFS
+       bool "/sys/class/gpio/... (sysfs interface)"
+       depends on SYSFS && EXPERIMENTAL
+       help
+         Say Y here to add a sysfs interface for GPIOs.
+
+         This is mostly useful to work around omissions in a system's
+         kernel support.  Those are common in custom and semicustom
+         hardware assembled using standard kernels with a minimum of
+         custom patches.  In those cases, userspace code may import
+         a given GPIO from the kernel, if no kernel driver requested it.
+
+         Kernel drivers may also request that a particular GPIO be
+         exported to userspace; this can be useful when debugging.
+
 # put expanders in the right section, in alphabetical order
 
 comment "I2C GPIO expanders:"
 # put expanders in the right section, in alphabetical order
 
 comment "I2C GPIO expanders:"
index beaf6b3a37dcefb0b2c1d8c8f79b7db2713dba45..8d2940517c99221c33e3bf200ac53a82214cebee 100644 (file)
@@ -2,8 +2,11 @@
 #include <linux/module.h>
 #include <linux/irq.h>
 #include <linux/spinlock.h>
 #include <linux/module.h>
 #include <linux/irq.h>
 #include <linux/spinlock.h>
-
-#include <asm/gpio.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/gpio.h>
 
 
 /* Optional implementation infrastructure for GPIO interfaces.
 
 
 /* Optional implementation infrastructure for GPIO interfaces.
@@ -44,6 +47,8 @@ struct gpio_desc {
 #define FLAG_REQUESTED 0
 #define FLAG_IS_OUT    1
 #define FLAG_RESERVED  2
 #define FLAG_REQUESTED 0
 #define FLAG_IS_OUT    1
 #define FLAG_RESERVED  2
+#define FLAG_EXPORT    3       /* protected by sysfs_lock */
+#define FLAG_SYSFS     4       /* exported via /sys/class/gpio/control */
 
 #ifdef CONFIG_DEBUG_FS
        const char              *label;
 
 #ifdef CONFIG_DEBUG_FS
        const char              *label;
@@ -151,6 +156,482 @@ err:
        return ret;
 }
 
        return ret;
 }
 
+#ifdef CONFIG_GPIO_SYSFS
+
+/* lock protects against unexport_gpio() being called while
+ * sysfs files are active.
+ */
+static DEFINE_MUTEX(sysfs_lock);
+
+/*
+ * /sys/class/gpio/gpioN... only for GPIOs that are exported
+ *   /direction
+ *      * MAY BE OMITTED if kernel won't allow direction changes
+ *      * is read/write as "in" or "out"
+ *      * may also be written as "high" or "low", initializing
+ *        output value as specified ("out" implies "low")
+ *   /value
+ *      * always readable, subject to hardware behavior
+ *      * may be writable, as zero/nonzero
+ *
+ * REVISIT there will likely be an attribute for configuring async
+ * notifications, e.g. to specify polling interval or IRQ trigger type
+ * that would for example trigger a poll() on the "value".
+ */
+
+static ssize_t gpio_direction_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       const struct gpio_desc  *desc = dev_get_drvdata(dev);
+       ssize_t                 status;
+
+       mutex_lock(&sysfs_lock);
+
+       if (!test_bit(FLAG_EXPORT, &desc->flags))
+               status = -EIO;
+       else
+               status = sprintf(buf, "%s\n",
+                       test_bit(FLAG_IS_OUT, &desc->flags)
+                               ? "out" : "in");
+
+       mutex_unlock(&sysfs_lock);
+       return status;
+}
+
+static ssize_t gpio_direction_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       const struct gpio_desc  *desc = dev_get_drvdata(dev);
+       unsigned                gpio = desc - gpio_desc;
+       ssize_t                 status;
+
+       mutex_lock(&sysfs_lock);
+
+       if (!test_bit(FLAG_EXPORT, &desc->flags))
+               status = -EIO;
+       else if (sysfs_streq(buf, "high"))
+               status = gpio_direction_output(gpio, 1);
+       else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low"))
+               status = gpio_direction_output(gpio, 0);
+       else if (sysfs_streq(buf, "in"))
+               status = gpio_direction_input(gpio);
+       else
+               status = -EINVAL;
+
+       mutex_unlock(&sysfs_lock);
+       return status ? : size;
+}
+
+static const DEVICE_ATTR(direction, 0644,
+               gpio_direction_show, gpio_direction_store);
+
+static ssize_t gpio_value_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       const struct gpio_desc  *desc = dev_get_drvdata(dev);
+       unsigned                gpio = desc - gpio_desc;
+       ssize_t                 status;
+
+       mutex_lock(&sysfs_lock);
+
+       if (!test_bit(FLAG_EXPORT, &desc->flags))
+               status = -EIO;
+       else
+               status = sprintf(buf, "%d\n", gpio_get_value_cansleep(gpio));
+
+       mutex_unlock(&sysfs_lock);
+       return status;
+}
+
+static ssize_t gpio_value_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       const struct gpio_desc  *desc = dev_get_drvdata(dev);
+       unsigned                gpio = desc - gpio_desc;
+       ssize_t                 status;
+
+       mutex_lock(&sysfs_lock);
+
+       if (!test_bit(FLAG_EXPORT, &desc->flags))
+               status = -EIO;
+       else if (!test_bit(FLAG_IS_OUT, &desc->flags))
+               status = -EPERM;
+       else {
+               long            value;
+
+               status = strict_strtol(buf, 0, &value);
+               if (status == 0) {
+                       gpio_set_value_cansleep(gpio, value != 0);
+                       status = size;
+               }
+       }
+
+       mutex_unlock(&sysfs_lock);
+       return status;
+}
+
+static /*const*/ DEVICE_ATTR(value, 0644,
+               gpio_value_show, gpio_value_store);
+
+static const struct attribute *gpio_attrs[] = {
+       &dev_attr_direction.attr,
+       &dev_attr_value.attr,
+       NULL,
+};
+
+static const struct attribute_group gpio_attr_group = {
+       .attrs = (struct attribute **) gpio_attrs,
+};
+
+/*
+ * /sys/class/gpio/gpiochipN/
+ *   /base ... matching gpio_chip.base (N)
+ *   /label ... matching gpio_chip.label
+ *   /ngpio ... matching gpio_chip.ngpio
+ */
+
+static ssize_t chip_base_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       const struct gpio_chip  *chip = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", chip->base);
+}
+static DEVICE_ATTR(base, 0444, chip_base_show, NULL);
+
+static ssize_t chip_label_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       const struct gpio_chip  *chip = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%s\n", chip->label ? : "");
+}
+static DEVICE_ATTR(label, 0444, chip_label_show, NULL);
+
+static ssize_t chip_ngpio_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       const struct gpio_chip  *chip = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", chip->ngpio);
+}
+static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL);
+
+static const struct attribute *gpiochip_attrs[] = {
+       &dev_attr_base.attr,
+       &dev_attr_label.attr,
+       &dev_attr_ngpio.attr,
+       NULL,
+};
+
+static const struct attribute_group gpiochip_attr_group = {
+       .attrs = (struct attribute **) gpiochip_attrs,
+};
+
+/*
+ * /sys/class/gpio/export ... write-only
+ *     integer N ... number of GPIO to export (full access)
+ * /sys/class/gpio/unexport ... write-only
+ *     integer N ... number of GPIO to unexport
+ */
+static ssize_t export_store(struct class *class, const char *buf, size_t len)
+{
+       long    gpio;
+       int     status;
+
+       status = strict_strtol(buf, 0, &gpio);
+       if (status < 0)
+               goto done;
+
+       /* No extra locking here; FLAG_SYSFS just signifies that the
+        * request and export were done by on behalf of userspace, so
+        * they may be undone on its behalf too.
+        */
+
+       status = gpio_request(gpio, "sysfs");
+       if (status < 0)
+               goto done;
+
+       status = gpio_export(gpio, true);
+       if (status < 0)
+               gpio_free(gpio);
+       else
+               set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags);
+
+done:
+       if (status)
+               pr_debug("%s: status %d\n", __func__, status);
+       return status ? : len;
+}
+
+static ssize_t unexport_store(struct class *class, const char *buf, size_t len)
+{
+       long    gpio;
+       int     status;
+
+       status = strict_strtol(buf, 0, &gpio);
+       if (status < 0)
+               goto done;
+
+       status = -EINVAL;
+
+       /* reject bogus commands (gpio_unexport ignores them) */
+       if (!gpio_is_valid(gpio))
+               goto done;
+
+       /* No extra locking here; FLAG_SYSFS just signifies that the
+        * request and export were done by on behalf of userspace, so
+        * they may be undone on its behalf too.
+        */
+       if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) {
+               status = 0;
+               gpio_free(gpio);
+       }
+done:
+       if (status)
+               pr_debug("%s: status %d\n", __func__, status);
+       return status ? : len;
+}
+
+static struct class_attribute gpio_class_attrs[] = {
+       __ATTR(export, 0200, NULL, export_store),
+       __ATTR(unexport, 0200, NULL, unexport_store),
+       __ATTR_NULL,
+};
+
+static struct class gpio_class = {
+       .name =         "gpio",
+       .owner =        THIS_MODULE,
+
+       .class_attrs =  gpio_class_attrs,
+};
+
+
+/**
+ * gpio_export - export a GPIO through sysfs
+ * @gpio: gpio to make available, already requested
+ * @direction_may_change: true if userspace may change gpio direction
+ * Context: arch_initcall or later
+ *
+ * When drivers want to make a GPIO accessible to userspace after they
+ * have requested it -- perhaps while debugging, or as part of their
+ * public interface -- they may use this routine.  If the GPIO can
+ * change direction (some can't) and the caller allows it, userspace
+ * will see "direction" sysfs attribute which may be used to change
+ * the gpio's direction.  A "value" attribute will always be provided.
+ *
+ * Returns zero on success, else an error.
+ */
+int gpio_export(unsigned gpio, bool direction_may_change)
+{
+       unsigned long           flags;
+       struct gpio_desc        *desc;
+       int                     status = -EINVAL;
+
+       /* can't export until sysfs is available ... */
+       if (!gpio_class.p) {
+               pr_debug("%s: called too early!\n", __func__);
+               return -ENOENT;
+       }
+
+       if (!gpio_is_valid(gpio))
+               goto done;
+
+       mutex_lock(&sysfs_lock);
+
+       spin_lock_irqsave(&gpio_lock, flags);
+       desc = &gpio_desc[gpio];
+       if (test_bit(FLAG_REQUESTED, &desc->flags)
+                       && !test_bit(FLAG_EXPORT, &desc->flags)) {
+               status = 0;
+               if (!desc->chip->direction_input
+                               || !desc->chip->direction_output)
+                       direction_may_change = false;
+       }
+       spin_unlock_irqrestore(&gpio_lock, flags);
+
+       if (status == 0) {
+               struct device   *dev;
+
+               dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
+                                       desc, "gpio%d", gpio);
+               if (dev) {
+                       if (direction_may_change)
+                               status = sysfs_create_group(&dev->kobj,
+                                               &gpio_attr_group);
+                       else
+                               status = device_create_file(dev,
+                                               &dev_attr_value);
+                       if (status != 0)
+                               device_unregister(dev);
+               } else
+                       status = -ENODEV;
+               if (status == 0)
+                       set_bit(FLAG_EXPORT, &desc->flags);
+       }
+
+       mutex_unlock(&sysfs_lock);
+
+done:
+       if (status)
+               pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
+
+       return status;
+}
+EXPORT_SYMBOL_GPL(gpio_export);
+
+static int match_export(struct device *dev, void *data)
+{
+       return dev_get_drvdata(dev) == data;
+}
+
+/**
+ * gpio_unexport - reverse effect of gpio_export()
+ * @gpio: gpio to make unavailable
+ *
+ * This is implicit on gpio_free().
+ */
+void gpio_unexport(unsigned gpio)
+{
+       struct gpio_desc        *desc;
+       int                     status = -EINVAL;
+
+       if (!gpio_is_valid(gpio))
+               goto done;
+
+       mutex_lock(&sysfs_lock);
+
+       desc = &gpio_desc[gpio];
+       if (test_bit(FLAG_EXPORT, &desc->flags)) {
+               struct device   *dev = NULL;
+
+               dev = class_find_device(&gpio_class, NULL, desc, match_export);
+               if (dev) {
+                       clear_bit(FLAG_EXPORT, &desc->flags);
+                       put_device(dev);
+                       device_unregister(dev);
+                       status = 0;
+               } else
+                       status = -ENODEV;
+       }
+
+       mutex_unlock(&sysfs_lock);
+done:
+       if (status)
+               pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
+}
+EXPORT_SYMBOL_GPL(gpio_unexport);
+
+static int gpiochip_export(struct gpio_chip *chip)
+{
+       int             status;
+       struct device   *dev;
+
+       /* Many systems register gpio chips for SOC support very early,
+        * before driver model support is available.  In those cases we
+        * export this later, in gpiolib_sysfs_init() ... here we just
+        * verify that _some_ field of gpio_class got initialized.
+        */
+       if (!gpio_class.p)
+               return 0;
+
+       /* use chip->base for the ID; it's already known to be unique */
+       mutex_lock(&sysfs_lock);
+       dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip,
+                               "gpiochip%d", chip->base);
+       if (dev) {
+               status = sysfs_create_group(&dev->kobj,
+                               &gpiochip_attr_group);
+       } else
+               status = -ENODEV;
+       chip->exported = (status == 0);
+       mutex_unlock(&sysfs_lock);
+
+       if (status) {
+               unsigned long   flags;
+               unsigned        gpio;
+
+               spin_lock_irqsave(&gpio_lock, flags);
+               gpio = chip->base;
+               while (gpio_desc[gpio].chip == chip)
+                       gpio_desc[gpio++].chip = NULL;
+               spin_unlock_irqrestore(&gpio_lock, flags);
+
+               pr_debug("%s: chip %s status %d\n", __func__,
+                               chip->label, status);
+       }
+
+       return status;
+}
+
+static void gpiochip_unexport(struct gpio_chip *chip)
+{
+       int                     status;
+       struct device           *dev;
+
+       mutex_lock(&sysfs_lock);
+       dev = class_find_device(&gpio_class, NULL, chip, match_export);
+       if (dev) {
+               put_device(dev);
+               device_unregister(dev);
+               chip->exported = 0;
+               status = 0;
+       } else
+               status = -ENODEV;
+       mutex_unlock(&sysfs_lock);
+
+       if (status)
+               pr_debug("%s: chip %s status %d\n", __func__,
+                               chip->label, status);
+}
+
+static int __init gpiolib_sysfs_init(void)
+{
+       int             status;
+       unsigned long   flags;
+       unsigned        gpio;
+
+       status = class_register(&gpio_class);
+       if (status < 0)
+               return status;
+
+       /* Scan and register the gpio_chips which registered very
+        * early (e.g. before the class_register above was called).
+        *
+        * We run before arch_initcall() so chip->dev nodes can have
+        * registered, and so arch_initcall() can always gpio_export().
+        */
+       spin_lock_irqsave(&gpio_lock, flags);
+       for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {
+               struct gpio_chip        *chip;
+
+               chip = gpio_desc[gpio].chip;
+               if (!chip || chip->exported)
+                       continue;
+
+               spin_unlock_irqrestore(&gpio_lock, flags);
+               status = gpiochip_export(chip);
+               spin_lock_irqsave(&gpio_lock, flags);
+       }
+       spin_unlock_irqrestore(&gpio_lock, flags);
+
+
+       return status;
+}
+postcore_initcall(gpiolib_sysfs_init);
+
+#else
+static inline int gpiochip_export(struct gpio_chip *chip)
+{
+       return 0;
+}
+
+static inline void gpiochip_unexport(struct gpio_chip *chip)
+{
+}
+
+#endif /* CONFIG_GPIO_SYSFS */
+
 /**
  * gpiochip_add() - register a gpio_chip
  * @chip: the chip to register, with chip->base initialized
 /**
  * gpiochip_add() - register a gpio_chip
  * @chip: the chip to register, with chip->base initialized
@@ -160,6 +641,11 @@ err:
  * because the chip->base is invalid or already associated with a
  * different chip.  Otherwise it returns zero as a success code.
  *
  * because the chip->base is invalid or already associated with a
  * different chip.  Otherwise it returns zero as a success code.
  *
+ * When gpiochip_add() is called very early during boot, so that GPIOs
+ * can be freely used, the chip->dev device must be registered before
+ * the gpio framework's arch_initcall().  Otherwise sysfs initialization
+ * for GPIOs will fail rudely.
+ *
  * If chip->base is negative, this requests dynamic assignment of
  * a range of valid GPIOs.
  */
  * If chip->base is negative, this requests dynamic assignment of
  * a range of valid GPIOs.
  */
@@ -182,7 +668,7 @@ int gpiochip_add(struct gpio_chip *chip)
                base = gpiochip_find_base(chip->ngpio);
                if (base < 0) {
                        status = base;
                base = gpiochip_find_base(chip->ngpio);
                if (base < 0) {
                        status = base;
-                       goto fail_unlock;
+                       goto unlock;
                }
                chip->base = base;
        }
                }
                chip->base = base;
        }
@@ -197,12 +683,23 @@ int gpiochip_add(struct gpio_chip *chip)
        if (status == 0) {
                for (id = base; id < base + chip->ngpio; id++) {
                        gpio_desc[id].chip = chip;
        if (status == 0) {
                for (id = base; id < base + chip->ngpio; id++) {
                        gpio_desc[id].chip = chip;
-                       gpio_desc[id].flags = 0;
+
+                       /* REVISIT:  most hardware initializes GPIOs as
+                        * inputs (often with pullups enabled) so power
+                        * usage is minimized.  Linux code should set the
+                        * gpio direction first thing; but until it does,
+                        * we may expose the wrong direction in sysfs.
+                        */
+                       gpio_desc[id].flags = !chip->direction_input
+                               ? (1 << FLAG_IS_OUT)
+                               : 0;
                }
        }
 
                }
        }
 
-fail_unlock:
+unlock:
        spin_unlock_irqrestore(&gpio_lock, flags);
        spin_unlock_irqrestore(&gpio_lock, flags);
+       if (status == 0)
+               status = gpiochip_export(chip);
 fail:
        /* failures here can mean systems won't boot... */
        if (status)
 fail:
        /* failures here can mean systems won't boot... */
        if (status)
@@ -239,6 +736,10 @@ int gpiochip_remove(struct gpio_chip *chip)
        }
 
        spin_unlock_irqrestore(&gpio_lock, flags);
        }
 
        spin_unlock_irqrestore(&gpio_lock, flags);
+
+       if (status == 0)
+               gpiochip_unexport(chip);
+
        return status;
 }
 EXPORT_SYMBOL_GPL(gpiochip_remove);
        return status;
 }
 EXPORT_SYMBOL_GPL(gpiochip_remove);
@@ -296,6 +797,8 @@ void gpio_free(unsigned gpio)
                return;
        }
 
                return;
        }
 
+       gpio_unexport(gpio);
+
        spin_lock_irqsave(&gpio_lock, flags);
 
        desc = &gpio_desc[gpio];
        spin_lock_irqsave(&gpio_lock, flags);
 
        desc = &gpio_desc[gpio];
@@ -534,10 +1037,6 @@ EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
 
 #ifdef CONFIG_DEBUG_FS
 
 
 #ifdef CONFIG_DEBUG_FS
 
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-
-
 static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
 {
        unsigned                i;
 static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
 {
        unsigned                i;
@@ -614,17 +1113,28 @@ static int gpiolib_show(struct seq_file *s, void *unused)
        /* REVISIT this isn't locked against gpio_chip removal ... */
 
        for (gpio = 0; gpio_is_valid(gpio); gpio++) {
        /* REVISIT this isn't locked against gpio_chip removal ... */
 
        for (gpio = 0; gpio_is_valid(gpio); gpio++) {
+               struct device *dev;
+
                if (chip == gpio_desc[gpio].chip)
                        continue;
                chip = gpio_desc[gpio].chip;
                if (!chip)
                        continue;
 
                if (chip == gpio_desc[gpio].chip)
                        continue;
                chip = gpio_desc[gpio].chip;
                if (!chip)
                        continue;
 
-               seq_printf(s, "%sGPIOs %d-%d, %s%s:\n",
+               seq_printf(s, "%sGPIOs %d-%d",
                                started ? "\n" : "",
                                started ? "\n" : "",
-                               chip->base, chip->base + chip->ngpio - 1,
-                               chip->label ? : "generic",
-                               chip->can_sleep ? ", can sleep" : "");
+                               chip->base, chip->base + chip->ngpio - 1);
+               dev = chip->dev;
+               if (dev)
+                       seq_printf(s, ", %s/%s",
+                               dev->bus ? dev->bus->name : "no-bus",
+                               dev->bus_id);
+               if (chip->label)
+                       seq_printf(s, ", %s", chip->label);
+               if (chip->can_sleep)
+                       seq_printf(s, ", can sleep");
+               seq_printf(s, ":\n");
+
                started = 1;
                if (chip->dbg_show)
                        chip->dbg_show(s, chip);
                started = 1;
                if (chip->dbg_show)
                        chip->dbg_show(s, chip);
index 7f92fdd5f0e2888b2c719e67873a5a7f0b435267..7efd7d3a81f9f927c037d25f981f47c5e45eefa2 100644 (file)
@@ -239,6 +239,7 @@ static int mcp23s08_probe(struct spi_device *spi)
        mcp->chip.base = pdata->base;
        mcp->chip.ngpio = 8;
        mcp->chip.can_sleep = 1;
        mcp->chip.base = pdata->base;
        mcp->chip.ngpio = 8;
        mcp->chip.can_sleep = 1;
+       mcp->chip.dev = &spi->dev;
        mcp->chip.owner = THIS_MODULE;
 
        spi_set_drvdata(spi, mcp);
        mcp->chip.owner = THIS_MODULE;
 
        spi_set_drvdata(spi, mcp);
index a380730b61abe49800af8e655eaf5735d0524ad0..cc8468692ae0967b295e196c5073fb102c4894c7 100644 (file)
@@ -188,6 +188,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
        gc->base = chip->gpio_start;
        gc->ngpio = gpios;
        gc->label = chip->client->name;
        gc->base = chip->gpio_start;
        gc->ngpio = gpios;
        gc->label = chip->client->name;
+       gc->dev = &chip->client->dev;
        gc->owner = THIS_MODULE;
 }
 
        gc->owner = THIS_MODULE;
 }
 
index d25d356c4f200c9a197a5ac84373f7441e415e3a..fc9c6ae739ee74f8f77b8836bd5e7894001e5d9d 100644 (file)
@@ -200,6 +200,7 @@ static int pcf857x_probe(struct i2c_client *client,
 
        gpio->chip.base = pdata->gpio_base;
        gpio->chip.can_sleep = 1;
 
        gpio->chip.base = pdata->gpio_base;
        gpio->chip.can_sleep = 1;
+       gpio->chip.dev = &client->dev;
        gpio->chip.owner = THIS_MODULE;
 
        /* NOTE:  the OnSemi jlc1562b is also largely compatible with
        gpio->chip.owner = THIS_MODULE;
 
        /* NOTE:  the OnSemi jlc1562b is also largely compatible with
index 85949685191bb3218e75cb5547aab3750b7e291d..cf02e8fceb42a7a1ef4ee91c42d0fb8cfdd14ecb 100644 (file)
@@ -636,6 +636,8 @@ static int tps65010_probe(struct i2c_client *client,
                tps->outmask = board->outmask;
 
                tps->chip.label = client->name;
                tps->outmask = board->outmask;
 
                tps->chip.label = client->name;
+               tps->chip.dev = &client->dev;
+               tps->chip.owner = THIS_MODULE;
 
                tps->chip.set = tps65010_gpio_set;
                tps->chip.direction_output = tps65010_output;
 
                tps->chip.set = tps65010_gpio_set;
                tps->chip.direction_output = tps65010_output;
index 8872cc0775190c296ab63bb67160b03f99cb3723..6be43172dc656911cd5c9f41f8717426e2be9e54 100644 (file)
@@ -318,6 +318,8 @@ static int __init egpio_probe(struct platform_device *pdev)
                ei->chip[i].dev = &(pdev->dev);
                chip = &(ei->chip[i].chip);
                chip->label           = "htc-egpio";
                ei->chip[i].dev = &(pdev->dev);
                chip = &(ei->chip[i].chip);
                chip->label           = "htc-egpio";
+               chip->dev             = &pdev->dev;
+               chip->owner           = THIS_MODULE;
                chip->get             = egpio_get;
                chip->set             = egpio_set;
                chip->direction_input = egpio_direction_input;
                chip->get             = egpio_get;
                chip->set             = egpio_set;
                chip->direction_input = egpio_direction_input;
index 6be061d09da9d06fc2dabdb520148c028194621b..1beff5166e5361e4aaf98c9422bdf835fbd84277 100644 (file)
@@ -32,6 +32,8 @@ struct module;
 /**
  * struct gpio_chip - abstract a GPIO controller
  * @label: for diagnostics
 /**
  * struct gpio_chip - abstract a GPIO controller
  * @label: for diagnostics
+ * @dev: optional device providing the GPIOs
+ * @owner: helps prevent removal of modules exporting active GPIOs
  * @direction_input: configures signal "offset" as input, or returns error
  * @get: returns value for signal "offset"; for output signals this
  *     returns either the value actually sensed, or zero
  * @direction_input: configures signal "offset" as input, or returns error
  * @get: returns value for signal "offset"; for output signals this
  *     returns either the value actually sensed, or zero
@@ -59,6 +61,7 @@ struct module;
  */
 struct gpio_chip {
        char                    *label;
  */
 struct gpio_chip {
        char                    *label;
+       struct device           *dev;
        struct module           *owner;
 
        int                     (*direction_input)(struct gpio_chip *chip,
        struct module           *owner;
 
        int                     (*direction_input)(struct gpio_chip *chip,
@@ -74,6 +77,7 @@ struct gpio_chip {
        int                     base;
        u16                     ngpio;
        unsigned                can_sleep:1;
        int                     base;
        u16                     ngpio;
        unsigned                can_sleep:1;
+       unsigned                exported:1;
 };
 
 extern const char *gpiochip_is_requested(struct gpio_chip *chip,
 };
 
 extern const char *gpiochip_is_requested(struct gpio_chip *chip,
@@ -108,7 +112,18 @@ extern void __gpio_set_value(unsigned gpio, int value);
 extern int __gpio_cansleep(unsigned gpio);
 
 
 extern int __gpio_cansleep(unsigned gpio);
 
 
-#else
+#ifdef CONFIG_GPIO_SYSFS
+
+/*
+ * A sysfs interface can be exported by individual drivers if they want,
+ * but more typically is configured entirely from userspace.
+ */
+extern int gpio_export(unsigned gpio, bool direction_may_change);
+extern void gpio_unexport(unsigned gpio);
+
+#endif /* CONFIG_GPIO_SYSFS */
+
+#else  /* !CONFIG_HAVE_GPIO_LIB */
 
 static inline int gpio_is_valid(int number)
 {
 
 static inline int gpio_is_valid(int number)
 {
@@ -137,6 +152,20 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value)
        gpio_set_value(gpio, value);
 }
 
        gpio_set_value(gpio, value);
 }
 
-#endif
+#endif /* !CONFIG_HAVE_GPIO_LIB */
+
+#ifndef CONFIG_GPIO_SYSFS
+
+/* sysfs support is only available with gpiolib, where it's optional */
+
+static inline int gpio_export(unsigned gpio, bool direction_may_change)
+{
+       return -ENOSYS;
+}
+
+static inline void gpio_unexport(unsigned gpio)
+{
+}
+#endif /* CONFIG_GPIO_SYSFS */
 
 #endif /* _ASM_GENERIC_GPIO_H */
 
 #endif /* _ASM_GENERIC_GPIO_H */
index 98be6c5762b923fcb2a98ced684051c3ea7f773c..730a20b83576b4ab39d81810085c20d61eec0225 100644 (file)
@@ -79,6 +79,19 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value)
        WARN_ON(1);
 }
 
        WARN_ON(1);
 }
 
+static inline int gpio_export(unsigned gpio, bool direction_may_change)
+{
+       /* GPIO can never have been requested or set as {in,out}put */
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+static inline void gpio_unexport(unsigned gpio)
+{
+       /* GPIO can never have been exported */
+       WARN_ON(1);
+}
+
 static inline int gpio_to_irq(unsigned gpio)
 {
        /* GPIO can never have been requested or set as input */
 static inline int gpio_to_irq(unsigned gpio)
 {
        /* GPIO can never have been requested or set as input */