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 <[email protected]>

---
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)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to