[kernel] 2.6.26: backport gpio sysfs support form 2.6.27-rcX
authorGabor Juhos <juhosg@openwrt.org>
Thu, 4 Sep 2008 13:34:36 +0000 (13:34 +0000)
committerGabor Juhos <juhosg@openwrt.org>
Thu, 4 Sep 2008 13:34:36 +0000 (13:34 +0000)
SVN-Revision: 12526

target/linux/generic-2.6/config-2.6.26
target/linux/generic-2.6/patches-2.6.26/980-backport_gpio_sysfs_support.patch [new file with mode: 0644]

index dbf2b23b984eff1f051098eba26030b20eea65b7..474668b7015c45ebb5da06eb93b1f8b6db67d33e 100644 (file)
@@ -389,6 +389,7 @@ CONFIG_GENERIC_IRQ_PROBE=y
 CONFIG_GENERIC_TIME=y
 # CONFIG_GFS2_FS is not set
 CONFIG_GPIO_DEVICE=m
+# CONFIG_GPIO_SYSFS is not set
 # CONFIG_GROUP_SCHED is not set
 # CONFIG_HAMACHI is not set
 CONFIG_HAMRADIO=y
diff --git a/target/linux/generic-2.6/patches-2.6.26/980-backport_gpio_sysfs_support.patch b/target/linux/generic-2.6/patches-2.6.26/980-backport_gpio_sysfs_support.patch
new file mode 100644 (file)
index 0000000..44d126c
--- /dev/null
@@ -0,0 +1,1020 @@
+From: David Brownell <dbrownell@users.sourceforge.net>
+Date: Fri, 25 Jul 2008 08:46:07 +0000 (-0700)
+Subject: gpio: sysfs interface
+X-Git-Tag: v2.6.27-rc1~449
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=d8f388d8dc8d4f36539dd37c1fff62cc404ea0fc
+
+gpio: sysfs interface
+
+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>
+---
+
+--- a/Documentation/gpio.txt
++++ b/Documentation/gpio.txt
+@@ -347,15 +347,12 @@
+ 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
+-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
+@@ -439,4 +436,120 @@
+ 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.
+--- a/arch/arm/plat-omap/gpio.c
++++ b/arch/arm/plat-omap/gpio.c
+@@ -1488,6 +1488,9 @@
+               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";
+--- a/arch/avr32/mach-at32ap/pio.c
++++ b/arch/avr32/mach-at32ap/pio.c
+@@ -358,6 +358,8 @@
+       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;
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -23,6 +23,21 @@
+         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:"
+--- a/drivers/gpio/gpiolib.c
++++ b/drivers/gpio/gpiolib.c
+@@ -2,8 +2,11 @@
+ #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.
+@@ -44,6 +47,8 @@
+ #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;
+@@ -151,6 +156,482 @@
+       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
+@@ -160,6 +641,11 @@
+  * 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.
+  */
+@@ -182,7 +668,7 @@
+               base = gpiochip_find_base(chip->ngpio);
+               if (base < 0) {
+                       status = base;
+-                      goto fail_unlock;
++                      goto unlock;
+               }
+               chip->base = base;
+       }
+@@ -197,12 +683,23 @@
+       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);
++      if (status == 0)
++              status = gpiochip_export(chip);
+ fail:
+       /* failures here can mean systems won't boot... */
+       if (status)
+@@ -239,6 +736,10 @@
+       }
+       spin_unlock_irqrestore(&gpio_lock, flags);
++
++      if (status == 0)
++              gpiochip_unexport(chip);
++
+       return status;
+ }
+ EXPORT_SYMBOL_GPL(gpiochip_remove);
+@@ -296,6 +797,8 @@
+               return;
+       }
++      gpio_unexport(gpio);
++
+       spin_lock_irqsave(&gpio_lock, flags);
+       desc = &gpio_desc[gpio];
+@@ -534,10 +1037,6 @@
+ #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;
+@@ -614,17 +1113,28 @@
+       /* 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;
+-              seq_printf(s, "%sGPIOs %d-%d, %s%s:\n",
++              seq_printf(s, "%sGPIOs %d-%d",
+                               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);
+--- a/drivers/gpio/mcp23s08.c
++++ b/drivers/gpio/mcp23s08.c
+@@ -239,6 +239,7 @@
+       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);
+--- a/drivers/gpio/pca953x.c
++++ b/drivers/gpio/pca953x.c
+@@ -188,6 +188,7 @@
+       gc->base = chip->gpio_start;
+       gc->ngpio = gpios;
+       gc->label = chip->client->name;
++      gc->dev = &chip->client->dev;
+       gc->owner = THIS_MODULE;
+ }
+--- a/drivers/gpio/pcf857x.c
++++ b/drivers/gpio/pcf857x.c
+@@ -175,6 +175,7 @@
+       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
+--- a/drivers/i2c/chips/tps65010.c
++++ b/drivers/i2c/chips/tps65010.c
+@@ -636,6 +636,8 @@
+               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;
+--- a/drivers/mfd/htc-egpio.c
++++ b/drivers/mfd/htc-egpio.c
+@@ -318,6 +318,8 @@
+               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;
+--- a/include/asm-generic/gpio.h
++++ b/include/asm-generic/gpio.h
+@@ -32,6 +32,8 @@
+ /**
+  * 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
+@@ -59,6 +61,7 @@
+  */
+ struct gpio_chip {
+       char                    *label;
++      struct device           *dev;
+       struct module           *owner;
+       int                     (*direction_input)(struct gpio_chip *chip,
+@@ -74,6 +77,7 @@
+       int                     base;
+       u16                     ngpio;
+       unsigned                can_sleep:1;
++      unsigned                exported:1;
+ };
+ extern const char *gpiochip_is_requested(struct gpio_chip *chip,
+@@ -108,7 +112,18 @@
+ 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)
+ {
+@@ -137,6 +152,20 @@
+       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 */
+--- a/include/linux/gpio.h
++++ b/include/linux/gpio.h
+@@ -79,6 +79,19 @@
+       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 */