From patchwork Mon Oct 27 18:30:02 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Soren Brinkmann X-Patchwork-Id: 403730 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id CE549140088 for ; Tue, 28 Oct 2014 05:30:38 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752214AbaJ0SaU (ORCPT ); Mon, 27 Oct 2014 14:30:20 -0400 Received: from mail-pa0-f49.google.com ([209.85.220.49]:57132 "EHLO mail-pa0-f49.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751513AbaJ0SaS (ORCPT ); Mon, 27 Oct 2014 14:30:18 -0400 Received: by mail-pa0-f49.google.com with SMTP id lj1so1861647pab.36 for ; Mon, 27 Oct 2014 11:30:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id; bh=jKaDrRP5ggP0mLqgBtC+toPs7NuWK1ayQ00VTDILsjU=; b=1CmBmoSQ923AoofZQ3yn6h/clmNE3va9UjpxUr21dxhqhonw3tciq3mBYf9EK3AVjq fX6N0lvqxd42bYyeouH40OB8R4Jh9nKcAnI0OHn3MRt/JE3S0sG1J05gVLFmWs+V5Ffm uHfabEI+O08ueulrkcEiEmrWFTERW9MK6JTL3CiV1zoO2fb8wiD7BHYUCTQP4FEODta/ Fy/LFFjLsALn3Jw6b0/fiObgYFXFvfsf1vYwWgQTHO8xkD6kO7fh1OhLl5U5qBqP1Mlc F/O5JxmxEQnGUpDclW9CDnMGGylBofq8pztpBkqyD0nQm6djBSORolrnFqVIp5aw1Byd Yoaw== X-Received: by 10.66.122.2 with SMTP id lo2mr538365pab.9.1414434617405; Mon, 27 Oct 2014 11:30:17 -0700 (PDT) Received: from localhost ([149.199.62.254]) by mx.google.com with ESMTPSA id ah2sm11575767pad.10.2014.10.27.11.30.15 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Mon, 27 Oct 2014 11:30:16 -0700 (PDT) From: Soren Brinkmann To: Linus Walleij , Alexandre Courbot Cc: Jonathan Corbet , linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org, Soren Brinkmann Subject: [PATCH v3] gpio: lib-sysfs: Add 'wakeup' attribute Date: Mon, 27 Oct 2014 11:30:02 -0700 Message-Id: <1414434602-14263-1-git-send-email-soren.brinkmann@xilinx.com> X-Mailer: git-send-email 2.1.2.1.g5e69ed6 Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add an attribute 'wakeup' to the GPIO sysfs interface which allows marking/unmarking a GPIO as wake IRQ. The file 'wakeup' is created in each exported GPIOs directory, if an IRQ is associated with that GPIO and the irqchip implements set_wake(). Writing 'enabled' to that file will enable wake for that GPIO, while writing 'disabled' will disable wake. Reading that file will return either 'disabled' or 'enabled' depening on the currently set flag for the GPIO's IRQ. Signed-off-by: Soren Brinkmann Reviewed-by: Alexandre Courbot --- v3: - add documentation v2: - fix error path to unlock mutex before return As additional reference, these are the email threads of the v2 and v1 submission: https://lkml.org/lkml/2014/10/13/481 https://lkml.org/lkml/2014/9/4/496 --- Documentation/ABI/testing/sysfs-gpio | 1 + Documentation/gpio/sysfs.txt | 8 ++++ drivers/gpio/gpiolib-sysfs.c | 77 +++++++++++++++++++++++++++++++++--- 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-gpio b/Documentation/ABI/testing/sysfs-gpio index 80f4c94c7bef..4cc7f4b3f724 100644 --- a/Documentation/ABI/testing/sysfs-gpio +++ b/Documentation/ABI/testing/sysfs-gpio @@ -20,6 +20,7 @@ Description: /value ... always readable, writes fail for input GPIOs /direction ... r/w as: in, out (default low); write: high, low /edge ... r/w as: none, falling, rising, both + /wakeup ... r/w as: enabled, disabled /gpiochipN ... for each gpiochip; #N is its first GPIO /base ... (r/o) same as N /label ... (r/o) descriptive, not necessarily unique diff --git a/Documentation/gpio/sysfs.txt b/Documentation/gpio/sysfs.txt index c2c3a97f8ff7..f703377d528f 100644 --- a/Documentation/gpio/sysfs.txt +++ b/Documentation/gpio/sysfs.txt @@ -97,6 +97,14 @@ and have the following read/write attributes: for "rising" and "falling" edges will follow this setting. + "wakeup" ... reads as either "enabled" or "disabled". Write these + strings to set/clear the 'wakeup' flag of the IRQ associated + with this GPIO. If the IRQ has the 'wakeup' flag set, it can + wake the system from sleep states. + + This file exists only if the pin can generate interrupts and + the driver implements the required infrastructure. + GPIO controllers have paths like /sys/class/gpio/gpiochip42/ (for the controller implementing GPIOs starting at #42) and have the following read-only attributes: diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 5f2150b619a7..7588b6d5ba94 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -286,6 +286,58 @@ found: static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store); +static ssize_t gpio_wakeup_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t status; + const struct gpio_desc *desc = dev_get_drvdata(dev); + int irq = gpiod_to_irq(desc); + struct irq_desc *irq_desc = irq_to_desc(irq); + + mutex_lock(&sysfs_lock); + + if (irqd_is_wakeup_set(&irq_desc->irq_data)) + status = sprintf(buf, "enabled\n"); + else + status = sprintf(buf, "disabled\n"); + + mutex_unlock(&sysfs_lock); + + return status; +} + +static ssize_t gpio_wakeup_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret; + unsigned int on; + struct gpio_desc *desc = dev_get_drvdata(dev); + int irq = gpiod_to_irq(desc); + + mutex_lock(&sysfs_lock); + + if (sysfs_streq("enabled", buf)) { + on = true; + } else if (sysfs_streq("disabled", buf)) { + on = false; + } else { + mutex_unlock(&sysfs_lock); + return -EINVAL; + } + + ret = irq_set_irq_wake(irq, on); + + mutex_unlock(&sysfs_lock); + + if (ret) + pr_warn("%s: failed to %s wake\n", __func__, + on ? "enable" : "disable"); + + return size; +} + +static DEVICE_ATTR(wakeup, 0644, gpio_wakeup_show, gpio_wakeup_store); + static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev, int value) { @@ -526,7 +578,7 @@ static struct class gpio_class = { int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { unsigned long flags; - int status; + int status, irq; const char *ioname = NULL; struct device *dev; int offset; @@ -582,11 +634,24 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) goto fail_unregister_device; } - if (gpiod_to_irq(desc) >= 0 && (direction_may_change || - !test_bit(FLAG_IS_OUT, &desc->flags))) { - status = device_create_file(dev, &dev_attr_edge); - if (status) - goto fail_unregister_device; + irq = gpiod_to_irq(desc); + if (irq >= 0) { + struct irq_desc *irq_desc = irq_to_desc(irq); + struct irq_chip *irqchip = irq_desc_get_chip(irq_desc); + + if (direction_may_change || + !test_bit(FLAG_IS_OUT, &desc->flags)) { + status = device_create_file(dev, &dev_attr_edge); + if (status) + goto fail_unregister_device; + } + + if (irqchip->flags & IRQCHIP_SKIP_SET_WAKE || + irqchip->irq_set_wake) { + status = device_create_file(dev, &dev_attr_wakeup); + if (status) + goto fail_unregister_device; + } } set_bit(FLAG_EXPORT, &desc->flags);