From patchwork Thu Sep 27 21:22:02 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roland Stigge X-Patchwork-Id: 1515831 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork2.kernel.org (Postfix) with ESMTP id 3C047DFFAD for ; Thu, 27 Sep 2012 21:25:29 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1THLXR-0003wK-SJ; Thu, 27 Sep 2012 21:22:29 +0000 Received: from mail.work-microwave.de ([62.245.205.51] helo=work-microwave.de) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1THLXJ-0003vt-3i for linux-arm-kernel@lists.infradead.org; Thu, 27 Sep 2012 21:22:24 +0000 Received: from rst-pc1.lan.work-microwave.de ([192.168.11.78]) (authenticated bits=0) by mail.work-microwave.de with ESMTP id q8RLMDRp003814 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Thu, 27 Sep 2012 22:22:14 +0100 Received: by rst-pc1.lan.work-microwave.de (Postfix, from userid 1000) id A7469AE06B; Thu, 27 Sep 2012 23:22:12 +0200 (CEST) From: Roland Stigge To: grant.likely@secretlab.ca, linus.walleij@linaro.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, w.sang@pengutronix.de, jbe@pengutronix.de Subject: [PATCH RFC 1/2] gpio: Add a block GPIO API to gpiolib Date: Thu, 27 Sep 2012 23:22:02 +0200 Message-Id: <1348780923-27428-1-git-send-email-stigge@antcom.de> X-Mailer: git-send-email 1.7.10.4 X-FEAS-SYSTEM-WL: rst@work-microwave.de, 192.168.11.78 X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.7 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.8 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Roland Stigge X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org The recurring task of providing simultaneous access to GPIO lines (especially for bit banging protocols) needs an appropriate API. This patch adds a kernel internal "Block GPIO" API that enables simultaneous access to several GPIOs in the same gpio_chip (bit mapped). Further, it adds a sysfs interface (/sys/class/gpio/gpiochipXX/block). Signed-off-by: Roland Stigge --- NOTE: This is only useful if individual drivers implement the .get_block() and .set_block() functions. I'm providing an example implementation for max730x (see next patch), and can provide further driver patches after API review. Thanks in advance! Documentation/gpio.txt | 52 +++++++++++++++++++ drivers/gpio/gpiolib.c | 121 +++++++++++++++++++++++++++++++++++++++++++++ include/asm-generic/gpio.h | 7 ++ include/linux/gpio.h | 24 ++++++++ 4 files changed, 204 insertions(+) --- linux-2.6.orig/Documentation/gpio.txt +++ linux-2.6/Documentation/gpio.txt @@ -439,6 +439,51 @@ slower clock delays the rising edge of S signaling rate accordingly. +Block GPIO (optional) +--------------------- + +The above described interface concentrates on handling single GPIOs. However, +in applications where it is critical to set several GPIOs at once, this +interface doesn't work well, e.g. bit-banging protocols via GPIO lines. +Consider a GPIO controller that is connected via a slow I2C line. When +switching two or more GPIOs one after another, there can be considerable time +between those events. This is solved by an interface called Block GPIO: + +void gpio_get_block(unsigned int gpio, u8* values, size_t size); +void gpio_set_block(unsigned int gpio, u8* set, u8* clr, size_t size); + +The function gpio_get_block() detects the current state of several GPIOs at +once, practically by doing only one query at the hardware level (e.g. memory +mapped or via bus transfers like I2C). There are some limits to this interface: +A certain gpio_chip (see below) must be specified via the gpio parameter as the +first GPIO in the gpio_chip group. The Block GPIO interface only supports +simultaneous handling of GPIOs in the same gpio_chip group since different +gpio_chips typically map to different GPIO hardware blocks. + +The values and size (in bytes) arguments specify a bit field of consecutive +values for the GPIOs in this gpio_chip group, relative to the specified GPIO. +E.g., when the gpio_chip group contains 16 GPIOs (80-95), size is 2 and values +points to an array of two bytes, the first of which contains the input values +of GPIOs 80-87 and the second one the values of GPIOs 88-95. + +Setting and clearing can be done via gpio_set_block(). Similar to the values +argument of gpio_get_block(), the arrays pointed to by set and clr contain bit +mapped lists of GPIOs to set and clear. This way, it is possible to +simultaneously set e.g. GPIOs 10 and 12 and clear GPIO 3, leaving the others in +the current state. The size argument refers to both set and clr which must be +sized equally. + +Another limit of this interface is that although gpio_get_block() and +gpio_set_block() are valid for all gpio_chips, they only work as expected where +the actual hardware really supports setting and clearing simultaneously. Some +GPIO hardware can only set simultaneously or clear simultaneously, but not set +and clear simultaneously. Further, the respective GPIO driver must implement +the .get_block() and .set_block() functions in their struct gpio_chip +efficiently. If they default to NULL, gpiolib uses .get() and .set() functions +as backup, which effectively leads to non-simultaneous GPIO handling. Please +check the actual GPIO driver you are using. + + What do these conventions omit? =============================== One of the biggest things these conventions omit is pin multiplexing, since @@ -686,6 +731,13 @@ read-only attributes: "ngpio" ... how many GPIOs this manges (N to N + ngpio - 1) + "block" ... get/set Block GPIO: + * reads: space separated list of GPIO inputs of this chip that + are set to 1, e.g. "83 85 87 99" + * write: space separated list of GPIO outputs of this chip + that are to be set or cleared, e.g. "80 -83 -85" (prefix + "-" clears) + 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, --- linux-2.6.orig/drivers/gpio/gpiolib.c +++ linux-2.6/drivers/gpio/gpiolib.c @@ -589,10 +589,114 @@ static ssize_t chip_ngpio_show(struct de } static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL); + +static ssize_t chip_block_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_chip *chip = dev_get_drvdata(dev); + size_t size = (chip->ngpio + 7) / 8; + u8 *bits; + int ret = 0; + int i, chars; + + bits = kzalloc(size, GFP_KERNEL); + + if (chip->get_block) { + chip->get_block(chip, bits, size); + } else { /* emulate as fallback */ + u8 byte = 0; + + for (i = 0; i < chip->ngpio; i++) { + byte |= gpio_get_value_cansleep(chip->base + i) << + (i & 7); + if ((i & 7) == 7 || i == chip->ngpio - 1) { + bits[i >> 3] = byte; + byte = 0; + } + } + } + + for (i = 0; i < chip->ngpio; i++) { + if (bits[i >> 3] & BIT(i & 7)) { + chars = sprintf(buf, "%s%d", ret ? " " : "", + chip->base + i); + buf += chars; + ret += chars; + } + } + + kfree(bits); + + ret += sprintf(buf, "\n"); + + return ret; +} + +static ssize_t chip_block_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct gpio_chip *chip = dev_get_drvdata(dev); + u8 *set_bits; + u8 *clr_bits; + size_t bits_size = (chip->ngpio + 7) / 8; + int count = size; + int gpio; + + mutex_lock(&sysfs_lock); + + set_bits = kzalloc(bits_size, GFP_KERNEL); + clr_bits = kzalloc(bits_size, GFP_KERNEL); + + while (count >= 0) { + bool clear = buf[0] == '-' ? true : false; + if (sscanf(buf, "%d", &gpio) == 1) { + if (clear) + gpio = -gpio; + gpio -= chip->base; + if (gpio >= 0 && gpio < chip->ngpio) { + if (clear) + clr_bits[gpio >> 3] |= BIT(gpio & 7); + else + set_bits[gpio >> 3] |= BIT(gpio & 7); + } + } + + /* Find next token */ + while (count >= 0 && *buf != ' ') { + buf++; + count--; + } + buf++; + count--; + } + + if (chip->set_block) { + chip->set_block(chip, set_bits, clr_bits, bits_size); + } else if (chip->set) { /* emulate as fall back */ + int i; + + for (i = 0; i < chip->ngpio; i++) { + if (set_bits[i >> 3] & BIT(i & 7)) + chip->set(chip, i, 1); + if (clr_bits[i >> 3] & BIT(i & 7)) + chip->set(chip, i, 0); + } + } + kfree(set_bits); + kfree(clr_bits); + mutex_unlock(&sysfs_lock); + + return size; +} + +static DEVICE_ATTR(block, 0644, chip_block_show, chip_block_store); + static const struct attribute *gpiochip_attrs[] = { &dev_attr_base.attr, &dev_attr_label.attr, &dev_attr_ngpio.attr, + &dev_attr_block.attr, NULL, }; @@ -1599,6 +1703,14 @@ int __gpio_get_value(unsigned gpio) } EXPORT_SYMBOL_GPL(__gpio_get_value); +void __gpio_get_block(unsigned gpio, u8 *values, size_t size) +{ + struct gpio_chip *chip = gpio_to_chip(gpio); + + return chip->get_block ? chip->get_block(chip, values, size) : 0; +} +EXPORT_SYMBOL_GPL(__gpio_get_block); + /* * _gpio_set_open_drain_value() - Set the open drain gpio's value. * @gpio: Gpio whose state need to be set. @@ -1676,6 +1788,15 @@ void __gpio_set_value(unsigned gpio, int } EXPORT_SYMBOL_GPL(__gpio_set_value); +void __gpio_set_block(unsigned gpio, u8 *set, u8 *clr, size_t size) +{ + struct gpio_chip *chip = gpio_to_chip(gpio); + + if (chip->set_block) + chip->set_block(chip, set, clr, size); +} +EXPORT_SYMBOL_GPL(__gpio_set_block); + /** * __gpio_cansleep() - report whether gpio value access will sleep * @gpio: gpio in question --- linux-2.6.orig/include/asm-generic/gpio.h +++ linux-2.6/include/asm-generic/gpio.h @@ -105,6 +105,8 @@ struct gpio_chip { unsigned offset); int (*get)(struct gpio_chip *chip, unsigned offset); + void (*get_block)(struct gpio_chip *chip, + u8 *values, size_t size); int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value); int (*set_debounce)(struct gpio_chip *chip, @@ -112,6 +114,8 @@ struct gpio_chip { void (*set)(struct gpio_chip *chip, unsigned offset, int value); + void (*set_block)(struct gpio_chip *chip, u8 *set, + u8 *clr, size_t size); int (*to_irq)(struct gpio_chip *chip, unsigned offset); @@ -171,6 +175,9 @@ extern void gpio_set_value_cansleep(unsi extern int __gpio_get_value(unsigned gpio); extern void __gpio_set_value(unsigned gpio, int value); +extern void __gpio_get_block(unsigned gpio, u8 *values, size_t size); +extern void __gpio_set_block(unsigned gpio, u8 *set, u8 *clr, size_t size); + extern int __gpio_cansleep(unsigned gpio); extern int __gpio_to_irq(unsigned gpio); --- linux-2.6.orig/include/linux/gpio.h +++ linux-2.6/include/linux/gpio.h @@ -57,6 +57,17 @@ static inline void gpio_set_value(unsign __gpio_set_value(gpio, value); } +static inline void gpio_get_block(unsigned int gpio, u8 *values, size_t size) +{ + return __gpio_get_block(gpio, values, size); +} + +static inline +void gpio_set_block(unsigned int gpio, u8 *set, u8 *clr, size_t size) +{ + __gpio_set_block(gpio, set, clr, size); +} + static inline int gpio_cansleep(unsigned int gpio) { return __gpio_cansleep(gpio); @@ -167,6 +178,19 @@ static inline void gpio_set_value(unsign { /* GPIO can never have been requested or set as output */ WARN_ON(1); +} + +static inline void gpio_get_block(unsigned gpio, u8 *values, size_t size) +{ + /* GPIO can never have been requested or set as {in,out}put */ + WARN_ON(1); + return 0; +} + +static inline void gpio_set_block(unsigned gpio, u8 *set, u8 *clr, size_t size) +{ + /* GPIO can never have been requested or set as output */ + WARN_ON(1); } static inline int gpio_cansleep(unsigned gpio)