diff options
author | Gabor Juhos <juhosg@openwrt.org> | 2008-09-04 13:34:36 +0000 |
---|---|---|
committer | Gabor Juhos <juhosg@openwrt.org> | 2008-09-04 13:34:36 +0000 |
commit | c6f0aadc2b41999dedc0b2bfdd1c95bc185cba0a (patch) | |
tree | f1e3a2277d8c5d862020e6e78ac355675237beac /target/linux/generic-2.6/patches-2.6.26 | |
parent | c29140837444cf59eede1a7fe493a53a32b482d6 (diff) | |
download | mtk-20170518-c6f0aadc2b41999dedc0b2bfdd1c95bc185cba0a.zip mtk-20170518-c6f0aadc2b41999dedc0b2bfdd1c95bc185cba0a.tar.gz mtk-20170518-c6f0aadc2b41999dedc0b2bfdd1c95bc185cba0a.tar.bz2 |
2.6.26: backport gpio sysfs support form 2.6.27-rcX
SVN-Revision: 12526
Diffstat (limited to 'target/linux/generic-2.6/patches-2.6.26')
-rw-r--r-- | target/linux/generic-2.6/patches-2.6.26/980-backport_gpio_sysfs_support.patch | 1020 |
1 files changed, 1020 insertions, 0 deletions
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 index 0000000..44d126c --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.26/980-backport_gpio_sysfs_support.patch @@ -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 */ |