forked from Minki/linux
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>
This commit is contained in:
parent
8b6dd98682
commit
d8f388d8dc
@ -347,15 +347,12 @@ necessarily be nonportable.
|
|||||||
Dynamic definition of GPIOs is not currently standard; for example, as
|
Dynamic definition of GPIOs is not currently standard; for example, as
|
||||||
a side effect of configuring an add-on board with some GPIO expanders.
|
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)
|
GPIO implementor's framework (OPTIONAL)
|
||||||
=======================================
|
=======================================
|
||||||
As noted earlier, there is an optional implementation framework making it
|
As noted earlier, there is an optional implementation framework making it
|
||||||
easier for platforms to support different kinds of GPIO controller using
|
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
|
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
|
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
|
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
|
such gpio_chip controllers to provide setup() and teardown() callbacks to
|
||||||
board specific code; those board specific callbacks would register devices
|
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.
|
||||||
|
@ -1488,6 +1488,9 @@ static int __init _omap_gpio_init(void)
|
|||||||
bank->chip.set = gpio_set;
|
bank->chip.set = gpio_set;
|
||||||
if (bank_is_mpuio(bank)) {
|
if (bank_is_mpuio(bank)) {
|
||||||
bank->chip.label = "mpuio";
|
bank->chip.label = "mpuio";
|
||||||
|
#ifdef CONFIG_ARCH_OMAP1
|
||||||
|
bank->chip.dev = &omap_mpuio_device.dev;
|
||||||
|
#endif
|
||||||
bank->chip.base = OMAP_MPUIO(0);
|
bank->chip.base = OMAP_MPUIO(0);
|
||||||
} else {
|
} else {
|
||||||
bank->chip.label = "gpio";
|
bank->chip.label = "gpio";
|
||||||
|
@ -360,6 +360,8 @@ static int __init pio_probe(struct platform_device *pdev)
|
|||||||
pio->chip.label = pio->name;
|
pio->chip.label = pio->name;
|
||||||
pio->chip.base = pdev->id * 32;
|
pio->chip.base = pdev->id * 32;
|
||||||
pio->chip.ngpio = 32;
|
pio->chip.ngpio = 32;
|
||||||
|
pio->chip.dev = &pdev->dev;
|
||||||
|
pio->chip.owner = THIS_MODULE;
|
||||||
|
|
||||||
pio->chip.direction_input = direction_input;
|
pio->chip.direction_input = direction_input;
|
||||||
pio->chip.get = gpio_get;
|
pio->chip.get = gpio_get;
|
||||||
|
@ -23,6 +23,21 @@ config DEBUG_GPIO
|
|||||||
slower. The diagnostics help catch the type of setup errors
|
slower. The diagnostics help catch the type of setup errors
|
||||||
that are most common when setting up new platforms or boards.
|
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
|
# put expanders in the right section, in alphabetical order
|
||||||
|
|
||||||
comment "I2C GPIO expanders:"
|
comment "I2C GPIO expanders:"
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/device.h>
|
||||||
#include <asm/gpio.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_REQUESTED 0
|
||||||
#define FLAG_IS_OUT 1
|
#define FLAG_IS_OUT 1
|
||||||
#define FLAG_RESERVED 2
|
#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
|
#ifdef CONFIG_DEBUG_FS
|
||||||
const char *label;
|
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
|
* gpiochip_add() - register a gpio_chip
|
||||||
* @chip: the chip to register, with chip->base initialized
|
* @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
|
* because the chip->base is invalid or already associated with a
|
||||||
* different chip. Otherwise it returns zero as a success code.
|
* 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
|
* If chip->base is negative, this requests dynamic assignment of
|
||||||
* a range of valid GPIOs.
|
* a range of valid GPIOs.
|
||||||
*/
|
*/
|
||||||
@ -182,7 +668,7 @@ int gpiochip_add(struct gpio_chip *chip)
|
|||||||
base = gpiochip_find_base(chip->ngpio);
|
base = gpiochip_find_base(chip->ngpio);
|
||||||
if (base < 0) {
|
if (base < 0) {
|
||||||
status = base;
|
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) {
|
if (status == 0) {
|
||||||
for (id = base; id < base + chip->ngpio; id++) {
|
for (id = base; id < base + chip->ngpio; id++) {
|
||||||
gpio_desc[id].chip = chip;
|
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:
|
fail:
|
||||||
/* failures here can mean systems won't boot... */
|
/* failures here can mean systems won't boot... */
|
||||||
if (status)
|
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;
|
return status;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gpiochip_remove);
|
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);
|
spin_lock_irqsave(&gpio_lock, flags);
|
||||||
|
|
||||||
desc = &gpio_desc[gpio];
|
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)
|
static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
||||||
{
|
{
|
||||||
unsigned i;
|
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 ... */
|
/* REVISIT this isn't locked against gpio_chip removal ... */
|
||||||
|
|
||||||
for (gpio = 0; gpio_is_valid(gpio); gpio++) {
|
for (gpio = 0; gpio_is_valid(gpio); gpio++) {
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
if (chip == gpio_desc[gpio].chip)
|
if (chip == gpio_desc[gpio].chip)
|
||||||
continue;
|
continue;
|
||||||
chip = gpio_desc[gpio].chip;
|
chip = gpio_desc[gpio].chip;
|
||||||
if (!chip)
|
if (!chip)
|
||||||
continue;
|
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->base, chip->base + chip->ngpio - 1);
|
||||||
chip->label ? : "generic",
|
dev = chip->dev;
|
||||||
chip->can_sleep ? ", can sleep" : "");
|
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;
|
started = 1;
|
||||||
if (chip->dbg_show)
|
if (chip->dbg_show)
|
||||||
chip->dbg_show(s, chip);
|
chip->dbg_show(s, chip);
|
||||||
|
@ -239,6 +239,7 @@ static int mcp23s08_probe(struct spi_device *spi)
|
|||||||
mcp->chip.base = pdata->base;
|
mcp->chip.base = pdata->base;
|
||||||
mcp->chip.ngpio = 8;
|
mcp->chip.ngpio = 8;
|
||||||
mcp->chip.can_sleep = 1;
|
mcp->chip.can_sleep = 1;
|
||||||
|
mcp->chip.dev = &spi->dev;
|
||||||
mcp->chip.owner = THIS_MODULE;
|
mcp->chip.owner = THIS_MODULE;
|
||||||
|
|
||||||
spi_set_drvdata(spi, mcp);
|
spi_set_drvdata(spi, mcp);
|
||||||
|
@ -188,6 +188,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
|
|||||||
gc->base = chip->gpio_start;
|
gc->base = chip->gpio_start;
|
||||||
gc->ngpio = gpios;
|
gc->ngpio = gpios;
|
||||||
gc->label = chip->client->name;
|
gc->label = chip->client->name;
|
||||||
|
gc->dev = &chip->client->dev;
|
||||||
gc->owner = THIS_MODULE;
|
gc->owner = THIS_MODULE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +200,7 @@ static int pcf857x_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
gpio->chip.base = pdata->gpio_base;
|
gpio->chip.base = pdata->gpio_base;
|
||||||
gpio->chip.can_sleep = 1;
|
gpio->chip.can_sleep = 1;
|
||||||
|
gpio->chip.dev = &client->dev;
|
||||||
gpio->chip.owner = THIS_MODULE;
|
gpio->chip.owner = THIS_MODULE;
|
||||||
|
|
||||||
/* NOTE: the OnSemi jlc1562b is also largely compatible with
|
/* NOTE: the OnSemi jlc1562b is also largely compatible with
|
||||||
|
@ -636,6 +636,8 @@ static int tps65010_probe(struct i2c_client *client,
|
|||||||
tps->outmask = board->outmask;
|
tps->outmask = board->outmask;
|
||||||
|
|
||||||
tps->chip.label = client->name;
|
tps->chip.label = client->name;
|
||||||
|
tps->chip.dev = &client->dev;
|
||||||
|
tps->chip.owner = THIS_MODULE;
|
||||||
|
|
||||||
tps->chip.set = tps65010_gpio_set;
|
tps->chip.set = tps65010_gpio_set;
|
||||||
tps->chip.direction_output = tps65010_output;
|
tps->chip.direction_output = tps65010_output;
|
||||||
|
@ -318,6 +318,8 @@ static int __init egpio_probe(struct platform_device *pdev)
|
|||||||
ei->chip[i].dev = &(pdev->dev);
|
ei->chip[i].dev = &(pdev->dev);
|
||||||
chip = &(ei->chip[i].chip);
|
chip = &(ei->chip[i].chip);
|
||||||
chip->label = "htc-egpio";
|
chip->label = "htc-egpio";
|
||||||
|
chip->dev = &pdev->dev;
|
||||||
|
chip->owner = THIS_MODULE;
|
||||||
chip->get = egpio_get;
|
chip->get = egpio_get;
|
||||||
chip->set = egpio_set;
|
chip->set = egpio_set;
|
||||||
chip->direction_input = egpio_direction_input;
|
chip->direction_input = egpio_direction_input;
|
||||||
|
@ -32,6 +32,8 @@ struct module;
|
|||||||
/**
|
/**
|
||||||
* struct gpio_chip - abstract a GPIO controller
|
* struct gpio_chip - abstract a GPIO controller
|
||||||
* @label: for diagnostics
|
* @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
|
* @direction_input: configures signal "offset" as input, or returns error
|
||||||
* @get: returns value for signal "offset"; for output signals this
|
* @get: returns value for signal "offset"; for output signals this
|
||||||
* returns either the value actually sensed, or zero
|
* returns either the value actually sensed, or zero
|
||||||
@ -59,6 +61,7 @@ struct module;
|
|||||||
*/
|
*/
|
||||||
struct gpio_chip {
|
struct gpio_chip {
|
||||||
char *label;
|
char *label;
|
||||||
|
struct device *dev;
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
|
|
||||||
int (*direction_input)(struct gpio_chip *chip,
|
int (*direction_input)(struct gpio_chip *chip,
|
||||||
@ -74,6 +77,7 @@ struct gpio_chip {
|
|||||||
int base;
|
int base;
|
||||||
u16 ngpio;
|
u16 ngpio;
|
||||||
unsigned can_sleep:1;
|
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 */
|
||||||
|
@ -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)
|
static inline int gpio_to_irq(unsigned gpio)
|
||||||
{
|
{
|
||||||
/* GPIO can never have been requested or set as input */
|
/* GPIO can never have been requested or set as input */
|
||||||
|
Loading…
Reference in New Issue
Block a user