diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index b1b9887..998a40f 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -529,6 +529,21 @@ and have the following read/write attributes: is configured as an output, this value may be written; any nonzero value is treated as high. + "notify" ... Selects a method for the detection of pin change + events. This can be written or read as one of "none", + "irq" or a number. If "none", no detection will be + done, if "irq" then the change detection will be done + by registering an interrupt handler on the line given + by gpio_to_irq for that gpio. If a number is written, + the gpio will be polled for a state change every n + milliseconds where n is the number written. The + minimum interval is 10 milliseconds. + + "notify_filter" ... This can be written and read as one of + "rising", "falling" or "both". If "rising", only + rising edges will be reported, similarly for "falling" + and "both". + GPIO controllers have paths like /sys/class/gpio/chipchip42/ (for the controller implementing GPIOs starting at #42) and have the following read-only attributes: @@ -549,6 +564,39 @@ gpiochip nodes (possibly in conjunction with schematics) to determine the correct GPIO number to use for a given signal. +Pin Change Notification +----------------------- +The "value" attribute of a gpio is pollable. For this to work, you +must have set up the notify attribute of that gpio to the type of +detection suitable for the underlying hardware. + +If the hardware supports interrupts on both edges and the gpio to +interrupt line mapping is unique, you should write "irq" to the notify +attribute. + +If the hardware doesn't support this, or you're unsure, you can write +a number to the notify attribute. This number is the delay between +successive polls of the gpio state in milliseconds. You may not poll +more often than 10ms intervals. + +You must use poll mode if communication with the gpio's chip requires +sleeping. This is the case if, for example, the gpio is part of a +I2C or SPI GPIO expander. + +By default, both rising and falling edges are reported. To filter +this, you can write one of "rising" or "falling" to the notify_filter +attribute. To go back to being notified of both edge changes, write +"both" to this attribute. + +Once the notification has been set up, you may use poll(2) and friends +on the "value" attribute. This attribute will register as having new +data ready to be read if and only if there has been a state change +since the last read(2) to the "value" attribute. + +Note that unlike a regular device file, a read on the "value" attribute +will never block whether or not there's new data to be read. + + Exporting from Kernel code -------------------------- Kernel code can explicitly manage exports of GPIOs which have already been diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9112830..f43f231 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -49,13 +50,35 @@ struct gpio_desc { #define FLAG_RESERVED 2 #define FLAG_EXPORT 3 /* protected by sysfs_lock */ #define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */ +#define ASYNC_MODE_IRQ 5 /* using interrupts for async notification */ +#define ASYNC_MODE_POLL 6 /* using polling for async notification */ +#define ASYNC_RISING 7 /* will notify on rising edges */ +#define ASYNC_FALLING 8 /* will notify on falling edges */ #ifdef CONFIG_DEBUG_FS const char *label; #endif + +#ifdef CONFIG_GPIO_SYSFS + struct device *dev; + struct poll_desc *poll; + + /* Poll interval in jiffies, here (rather than in struct poll_desc + * so the user can change the value no matter what their current + * notification mode is */ + long timeout; + + /* Last known value */ + int val; +#endif }; static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; +struct poll_desc { + struct delayed_work work; + unsigned gpio; +}; + static inline void desc_set_label(struct gpio_desc *d, const char *label) { #ifdef CONFIG_DEBUG_FS @@ -184,12 +207,56 @@ static DEFINE_MUTEX(sysfs_lock); * /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". + * /notify + * * read/write as "irq", numeric or "none" + * /notify_filter + * * read/write as "rising", "falling" or "both" */ +struct poll_desc *work_to_poll(struct work_struct *ws) +{ + return container_of((struct delayed_work *)ws, struct poll_desc, work); +} + +void gpio_poll_work(struct work_struct *ws) +{ + struct poll_desc *poll = work_to_poll(ws); + + unsigned gpio = poll->gpio; + struct gpio_desc *desc = &gpio_desc[gpio]; + + int new = gpio_get_value_cansleep(gpio); + int old = desc->val; + + if ((new && !old && test_bit(ASYNC_RISING, &desc->flags)) || + (!new && old && test_bit(ASYNC_FALLING, &desc->flags))) + sysfs_notify(&desc->dev->kobj, NULL, "value"); + + desc->val = new; + schedule_delayed_work(&poll->work, desc->timeout); +} + +static irqreturn_t gpio_irq_handler(int irq, void *dev_id) +{ + struct gpio_desc *desc = dev_id; + int gpio = desc - gpio_desc; + int new, old; + + if (!gpio_is_valid(gpio)) + return IRQ_NONE; + + new = gpio_get_value(gpio); + old = desc->val; + + if ((new && !old && test_bit(ASYNC_RISING, &desc->flags)) || + (!new && old && test_bit(ASYNC_FALLING, &desc->flags))) + sysfs_notify(&desc->dev->kobj, NULL, "value"); + + desc->val = new; + + return IRQ_HANDLED; +} + static ssize_t gpio_direction_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -284,9 +351,162 @@ static ssize_t gpio_value_store(struct device *dev, static /*const*/ DEVICE_ATTR(value, 0644, gpio_value_show, gpio_value_store); +static ssize_t gpio_notify_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&sysfs_lock); + + if (test_bit(ASYNC_MODE_IRQ, &desc->flags)) + ret = sprintf(buf, "irq\n"); + else if (test_bit(ASYNC_MODE_POLL, &desc->flags)) + ret = sprintf(buf, "%ld\n", desc->timeout * 1000 / HZ); + else + ret = sprintf(buf, "none\n"); + + mutex_unlock(&sysfs_lock); + return ret; +} + +static ssize_t gpio_notify_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status = 0; + int new; + long value; + + mutex_lock(&sysfs_lock); + + if (sysfs_streq(buf, "irq")) + new = ASYNC_MODE_IRQ; + else if (sysfs_streq(buf, "none")) + new = -1; + else { + status = strict_strtol(buf, 0, &value); + + if (status || value < 10) { + status = -EINVAL; + goto out; + } + + /* value will be in ms, convert to jiffies. */ + desc->timeout = DIV_ROUND_UP(value * HZ, 1000); + new = ASYNC_MODE_POLL; + } + + if (test_and_clear_bit(ASYNC_MODE_IRQ, &desc->flags)) + free_irq(gpio_to_irq(gpio), desc); + else if (test_and_clear_bit(ASYNC_MODE_POLL, &desc->flags)) { + BUG_ON(!desc->poll); + + cancel_delayed_work(&desc->poll->work); + kfree(desc->poll); + } + + if (new == ASYNC_MODE_IRQ) { + if (desc->chip->can_sleep) { + /* -EINVAL probably isn't appropriate here, + * what's code for "not supported by + * underlying hardware"? */ + status = -EINVAL; + goto out; + } + + desc->val = gpio_get_value(gpio); + + /* REVISIT: We always request both edges then filter in the + * irq handler. How many devices don't support this..?? */ + status = request_irq(gpio_to_irq(gpio), gpio_irq_handler, + IRQF_SHARED | IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "gpiolib", desc); + } else if (new == ASYNC_MODE_POLL) { + + desc->poll = kmalloc(sizeof(struct poll_desc), GFP_KERNEL); + if (!desc->poll) { + status = -ENOMEM; + goto out; + } + + desc->poll->gpio = gpio; + + desc->val = gpio_get_value_cansleep(gpio); + + INIT_DELAYED_WORK(&desc->poll->work, gpio_poll_work); + schedule_delayed_work(&desc->poll->work, desc->timeout); + } + + if (new >= 0 && !status) + set_bit(new, &desc->flags); + +out: + if (status) + dev_dbg(dev, "gpio notification mode set fail, err %d\n", + (int)status); + + mutex_unlock(&sysfs_lock); + return status ? : size; +} + +static const DEVICE_ATTR(notify, 0644, + gpio_notify_show, gpio_notify_store); + +static ssize_t gpio_notify_filter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (test_bit(ASYNC_FALLING, &desc->flags) && + test_bit(ASYNC_RISING, &desc->flags)) + status = sprintf(buf, "both\n"); + else if (test_bit(ASYNC_RISING, &desc->flags)) + status = sprintf(buf, "rising\n"); + else + status = sprintf(buf, "falling\n"); + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_notify_filter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status = 0; + + mutex_lock(&sysfs_lock); + + if (sysfs_streq(buf, "rising")) { + set_bit(ASYNC_RISING, &desc->flags); + clear_bit(ASYNC_FALLING, &desc->flags); + } else if (sysfs_streq(buf, "falling")) { + clear_bit(ASYNC_RISING, &desc->flags); + set_bit(ASYNC_FALLING, &desc->flags); + } else if (sysfs_streq(buf, "both")) { + set_bit(ASYNC_RISING, &desc->flags); + set_bit(ASYNC_FALLING, &desc->flags); + } else + status = -EINVAL; + + mutex_unlock(&sysfs_lock); + return status ? : size; +} + +static const DEVICE_ATTR(notify_filter, 0644, + gpio_notify_filter_show, gpio_notify_filter_store); + static const struct attribute *gpio_attrs[] = { &dev_attr_direction.attr, &dev_attr_value.attr, + &dev_attr_notify.attr, + &dev_attr_notify_filter.attr, NULL, }; @@ -398,6 +618,17 @@ static ssize_t unexport_store(struct class *class, const char *buf, size_t len) status = 0; gpio_free(gpio); } + + if (test_and_clear_bit(ASYNC_MODE_IRQ, &gpio_desc[gpio].flags)) + free_irq(gpio_to_irq(gpio), &gpio_desc[gpio]); + + if (test_and_clear_bit(ASYNC_MODE_POLL, &gpio_desc[gpio].flags)) { + BUG_ON(!gpio_desc[gpio].poll); + + cancel_delayed_work(&gpio_desc[gpio].poll->work); + kfree(gpio_desc[gpio].poll); + } + done: if (status) pr_debug("%s: status %d\n", __func__, status); @@ -479,6 +710,12 @@ int gpio_export(unsigned gpio, bool direction_may_change) status = -ENODEV; if (status == 0) set_bit(FLAG_EXPORT, &desc->flags); + + desc->dev = dev; + /* 100ms default poll interval */ + desc->timeout = HZ / 10; + set_bit(ASYNC_RISING, &desc->flags); + set_bit(ASYNC_FALLING, &desc->flags); } mutex_unlock(&sysfs_lock); @@ -626,7 +863,6 @@ static int __init gpiolib_sysfs_init(void) } spin_unlock_irqrestore(&gpio_lock, flags); - return status; } postcore_initcall(gpiolib_sysfs_init);