All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RESEND 0/6 v13] gpio: Add block GPIO
@ 2013-01-15 11:51 ` Roland Stigge
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: gregkh, grant.likely, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, highguy, broonie,
	daniel-gl, rmallon, sr, wg, tru, mark.rutland
  Cc: Roland Stigge

This set of patches adds:

* Block GPIO API to gpiolib
* Sysfs support for GPIO API, to provide userland access
* Device interface for userland access (alternative to sysfs)
* Devicetree support to instantiate GPIO blocks via DT
* Example implementations in several gpio drivers since they need
  special accessor functions for block wise GPIO access (only for some
  selected drivers since the feature is optional and not suitable for
  all GPIO hardware)
* Fix for race condition in gpiolib on device creation

Signed-off-by: Roland Stigge <stigge@antcom.de>
Tested by: Wolfgang Grandegger <wg@grandegger.com>
--

Testing branch available at: git://git.antcom.de/linux-2.6.git blockgpio

Changes since v12:
* Added support for gpio drivers ge, ks8695, mc33880, ml-ioh, mpc5200 and
  mpc8xxx
* Added support for pinctrl driver at91

Changes since v11:
* Removed support for at91, since this one is currently in the migration
  process to pinctrl implementation
* Fixed mask setting via sysfs
* Device interface: declare gpio_block_fop_poll() static

Changes since v10:
* gpioblock-of.c: Catch <1 specified GPIOs
* gpioblock-of.c: Fixed memory leak in probe()'s error path
* gpioblock-of.c: Fixed block node's refcount via of_node_put()
* Added driver support for samsung, mm-lantiq and sa1100
* Added driver support for at91 (by Wolfgang Grandegger)
* Added pinctrl driver support for pinctl-nomadik

Changed since v9:
* Changed IRQ flags for device interface's poll() from IRQF_TRIGGER_FALLING
  to IRQF_SHARED
* Use block name on gpio_request
* Added spinlock for ISR manipulated data (dev interface / poll())

Changed since v8:
* Added poll() function to dev interface for IRQ handling
* Fixed gpio_block_register() in case of missing CONFIG_GPIO_SYSFS
* Fixed mask handling in sysfs interface
* Fixed get_user/put_user pointer (cast)

Changes since v7:
* Fixed error in API documentation (name arg missing in prototype)
* Fixed compile error if sysfs is missing: Moved gpio_block_is_output()
  out of sysfs-ifdef
* Added error handling to gpio_block_register()
* Added mask attribute to sysfs (although dev interface exists now)

Changes since v6:
* Changed realloc to list handling
* Added mask to kernel API
* Added device interface as userspace API
* Changed sizeof() to BITS_PER_LONG
* Fixed multiline comment style
* Documented direction setting by the user
* Documented order of handled chips in case of multiple chips in one block

Changes since v5:
* Documented sysfs: elaborated on "exported" and "values" attributes
* Documented sysfs: gpiochip is a separate class now
* Aggregated driver support patches for block gpio into one single patch
* Added gpio block driver support for twl6040 and pch

Changes since v4:
* Documented word width
* Bugfix: export/unexport on register/unregister
* Using default dev_attrs for gpio_block_class
* Fix gpiolib: race condition on device creation
* Added driver support for ucb14500, vt8500, xilinx

Changes since v3:
* Added driver support for pca953x, em, pl061, max732x, pcf857x
* Coding style improvements
* Fixed krealloc memory leak in error case
* sysfs: values in hex
* Register blocks in a list
* Narrowing lock scope
* Use S_IWUSR and S_IRUGO instead of direct octal values
* Use for_each_set_bit()
* Change from unsigned to unsigned long for masks and values

Changes since v2:
* Added sysfs support
* Added devicetree support
* Added support for lpc32xx, generic
* Added functions for GPIO block registration
* Added more error checking
* Bit remapping bugfix

Changes since v1:
* API change to 32/64 bit word, bit masks

Thanks to Ryan Mallon, Linus Walleij, Stijn Devriendt, Jean-Christophe
Plagniol-Villard, Mark Brown, Greg Kroah-Hartman, Grant Likely, Stefan
Roese, Wolfgang Grandegger, Tobias Rutz, Fengguang Wu and Mark Rutland
for reviewing and testing!

Roland Stigge (6):
 gpio: Add a block GPIO API to gpiolib
 gpio: Add sysfs support to block GPIO API
 gpio: Add userland device interface to block GPIO
 gpiolib: Fix default attributes for class
 gpio: Add device tree support to block GPIO API
 gpio: Add block gpio to several gpio drivers

 Documentation/ABI/testing/dev-gpioblock               |   34 
 Documentation/ABI/testing/sysfs-gpio                  |   31 
 Documentation/devicetree/bindings/gpio/gpio-block.txt |   36 
 Documentation/gpio.txt                                |   58 +
 drivers/gpio/Kconfig                                  |    2 
 drivers/gpio/Makefile                                 |    1 
 drivers/gpio/gpio-em.c                                |   23 
 drivers/gpio/gpio-ge.c                                |   29 
 drivers/gpio/gpio-generic.c                           |   56 +
 drivers/gpio/gpio-ks8695.c                            |   34 
 drivers/gpio/gpio-lpc32xx.c                           |   82 +
 drivers/gpio/gpio-max730x.c                           |   61 +
 drivers/gpio/gpio-max732x.c                           |   59 +
 drivers/gpio/gpio-mc33880.c                           |   16 
 drivers/gpio/gpio-ml-ioh.c                            |   27 
 drivers/gpio/gpio-mm-lantiq.c                         |   22 
 drivers/gpio/gpio-mpc5200.c                           |   64 +
 drivers/gpio/gpio-mpc8xxx.c                           |   41 
 drivers/gpio/gpio-pca953x.c                           |   64 +
 drivers/gpio/gpio-pcf857x.c                           |   24 
 drivers/gpio/gpio-pch.c                               |   27 
 drivers/gpio/gpio-pl061.c                             |   17 
 drivers/gpio/gpio-sa1100.c                            |   20 
 drivers/gpio/gpio-samsung.c                           |   31 
 drivers/gpio/gpio-twl6040.c                           |   32 
 drivers/gpio/gpio-ucb1400.c                           |   23 
 drivers/gpio/gpio-vt8500.c                            |   24 
 drivers/gpio/gpio-xilinx.c                            |   44 +
 drivers/gpio/gpioblock-of.c                           |  100 ++
 drivers/gpio/gpiolib.c                                |  782 ++++++++++++++++--
 drivers/pinctrl/pinctrl-at91.c                        |   29 
 drivers/pinctrl/pinctrl-nomadik.c                     |   36 
 include/asm-generic/gpio.h                            |   28 
 include/linux/gpio.h                                  |  125 ++
 34 files changed, 2035 insertions(+), 47 deletions(-)

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 0/6 v13] gpio: Add block GPIO
@ 2013-01-15 11:51 ` Roland Stigge
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel

This set of patches adds:

* Block GPIO API to gpiolib
* Sysfs support for GPIO API, to provide userland access
* Device interface for userland access (alternative to sysfs)
* Devicetree support to instantiate GPIO blocks via DT
* Example implementations in several gpio drivers since they need
  special accessor functions for block wise GPIO access (only for some
  selected drivers since the feature is optional and not suitable for
  all GPIO hardware)
* Fix for race condition in gpiolib on device creation

Signed-off-by: Roland Stigge <stigge@antcom.de>
Tested by: Wolfgang Grandegger <wg@grandegger.com>
--

Testing branch available at: git://git.antcom.de/linux-2.6.git blockgpio

Changes since v12:
* Added support for gpio drivers ge, ks8695, mc33880, ml-ioh, mpc5200 and
  mpc8xxx
* Added support for pinctrl driver at91

Changes since v11:
* Removed support for at91, since this one is currently in the migration
  process to pinctrl implementation
* Fixed mask setting via sysfs
* Device interface: declare gpio_block_fop_poll() static

Changes since v10:
* gpioblock-of.c: Catch <1 specified GPIOs
* gpioblock-of.c: Fixed memory leak in probe()'s error path
* gpioblock-of.c: Fixed block node's refcount via of_node_put()
* Added driver support for samsung, mm-lantiq and sa1100
* Added driver support for at91 (by Wolfgang Grandegger)
* Added pinctrl driver support for pinctl-nomadik

Changed since v9:
* Changed IRQ flags for device interface's poll() from IRQF_TRIGGER_FALLING
  to IRQF_SHARED
* Use block name on gpio_request
* Added spinlock for ISR manipulated data (dev interface / poll())

Changed since v8:
* Added poll() function to dev interface for IRQ handling
* Fixed gpio_block_register() in case of missing CONFIG_GPIO_SYSFS
* Fixed mask handling in sysfs interface
* Fixed get_user/put_user pointer (cast)

Changes since v7:
* Fixed error in API documentation (name arg missing in prototype)
* Fixed compile error if sysfs is missing: Moved gpio_block_is_output()
  out of sysfs-ifdef
* Added error handling to gpio_block_register()
* Added mask attribute to sysfs (although dev interface exists now)

Changes since v6:
* Changed realloc to list handling
* Added mask to kernel API
* Added device interface as userspace API
* Changed sizeof() to BITS_PER_LONG
* Fixed multiline comment style
* Documented direction setting by the user
* Documented order of handled chips in case of multiple chips in one block

Changes since v5:
* Documented sysfs: elaborated on "exported" and "values" attributes
* Documented sysfs: gpiochip is a separate class now
* Aggregated driver support patches for block gpio into one single patch
* Added gpio block driver support for twl6040 and pch

Changes since v4:
* Documented word width
* Bugfix: export/unexport on register/unregister
* Using default dev_attrs for gpio_block_class
* Fix gpiolib: race condition on device creation
* Added driver support for ucb14500, vt8500, xilinx

Changes since v3:
* Added driver support for pca953x, em, pl061, max732x, pcf857x
* Coding style improvements
* Fixed krealloc memory leak in error case
* sysfs: values in hex
* Register blocks in a list
* Narrowing lock scope
* Use S_IWUSR and S_IRUGO instead of direct octal values
* Use for_each_set_bit()
* Change from unsigned to unsigned long for masks and values

Changes since v2:
* Added sysfs support
* Added devicetree support
* Added support for lpc32xx, generic
* Added functions for GPIO block registration
* Added more error checking
* Bit remapping bugfix

Changes since v1:
* API change to 32/64 bit word, bit masks

Thanks to Ryan Mallon, Linus Walleij, Stijn Devriendt, Jean-Christophe
Plagniol-Villard, Mark Brown, Greg Kroah-Hartman, Grant Likely, Stefan
Roese, Wolfgang Grandegger, Tobias Rutz, Fengguang Wu and Mark Rutland
for reviewing and testing!

Roland Stigge (6):
 gpio: Add a block GPIO API to gpiolib
 gpio: Add sysfs support to block GPIO API
 gpio: Add userland device interface to block GPIO
 gpiolib: Fix default attributes for class
 gpio: Add device tree support to block GPIO API
 gpio: Add block gpio to several gpio drivers

 Documentation/ABI/testing/dev-gpioblock               |   34 
 Documentation/ABI/testing/sysfs-gpio                  |   31 
 Documentation/devicetree/bindings/gpio/gpio-block.txt |   36 
 Documentation/gpio.txt                                |   58 +
 drivers/gpio/Kconfig                                  |    2 
 drivers/gpio/Makefile                                 |    1 
 drivers/gpio/gpio-em.c                                |   23 
 drivers/gpio/gpio-ge.c                                |   29 
 drivers/gpio/gpio-generic.c                           |   56 +
 drivers/gpio/gpio-ks8695.c                            |   34 
 drivers/gpio/gpio-lpc32xx.c                           |   82 +
 drivers/gpio/gpio-max730x.c                           |   61 +
 drivers/gpio/gpio-max732x.c                           |   59 +
 drivers/gpio/gpio-mc33880.c                           |   16 
 drivers/gpio/gpio-ml-ioh.c                            |   27 
 drivers/gpio/gpio-mm-lantiq.c                         |   22 
 drivers/gpio/gpio-mpc5200.c                           |   64 +
 drivers/gpio/gpio-mpc8xxx.c                           |   41 
 drivers/gpio/gpio-pca953x.c                           |   64 +
 drivers/gpio/gpio-pcf857x.c                           |   24 
 drivers/gpio/gpio-pch.c                               |   27 
 drivers/gpio/gpio-pl061.c                             |   17 
 drivers/gpio/gpio-sa1100.c                            |   20 
 drivers/gpio/gpio-samsung.c                           |   31 
 drivers/gpio/gpio-twl6040.c                           |   32 
 drivers/gpio/gpio-ucb1400.c                           |   23 
 drivers/gpio/gpio-vt8500.c                            |   24 
 drivers/gpio/gpio-xilinx.c                            |   44 +
 drivers/gpio/gpioblock-of.c                           |  100 ++
 drivers/gpio/gpiolib.c                                |  782 ++++++++++++++++--
 drivers/pinctrl/pinctrl-at91.c                        |   29 
 drivers/pinctrl/pinctrl-nomadik.c                     |   36 
 include/asm-generic/gpio.h                            |   28 
 include/linux/gpio.h                                  |  125 ++
 34 files changed, 2035 insertions(+), 47 deletions(-)

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
  2013-01-15 11:51 ` Roland Stigge
@ 2013-01-15 11:51   ` Roland Stigge
  -1 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: gregkh, grant.likely, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, highguy, broonie,
	daniel-gl, rmallon, sr, wg, tru, mark.rutland
  Cc: Roland Stigge

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. This is done by abstracting GPIOs to an n-bit word:
Once requested, it provides access to a group of GPIOs which can range over
multiple GPIO chips.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---

 Documentation/gpio.txt     |   58 +++++++++++
 drivers/gpio/gpiolib.c     |  227 +++++++++++++++++++++++++++++++++++++++++++++
 include/asm-generic/gpio.h |   17 +++
 include/linux/gpio.h       |   97 +++++++++++++++++++
 4 files changed, 399 insertions(+)

--- linux-2.6.orig/Documentation/gpio.txt
+++ linux-2.6/Documentation/gpio.txt
@@ -481,6 +481,64 @@ exact name string of pinctrl device has
 argument to this routine.
 
 
+Block GPIO
+----------
+
+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 grouped 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:
+
+struct gpio_block *gpio_block_create(unsigned int *gpios, size_t size,
+				     const char *name);
+
+This creates a new block of GPIOs as a list of GPIO numbers with the specified
+size which are accessible via the returned struct gpio_block and the accessor
+functions described below. Please note that you need to request the GPIOs
+separately via gpio_request(). Similarly, the direction of the used GPIOs need
+to be set by the user before using the block. An arbitrary list of globally
+valid GPIOs can be specified, even ranging over several gpio_chips. Actual
+handling of I/O operations will be done on a best effort base, i.e.
+simultaneous I/O only where possible by hardware and implemented in the
+respective GPIO driver. The number of GPIOs in one block is limited to the
+number of bits in an unsigned long, or BITS_PER_LONG, of the respective
+platform, i.e. typically at least 32 on a 32 bit system, and at least 64 on a
+64 bit system. However, several blocks can be defined at once.
+
+unsigned long gpio_block_get(struct gpio_block *block, unsigned long mask);
+void gpio_block_set(struct gpio_block *block, unsigned long mask,
+		    unsigned long values);
+
+With those accessor functions, setting and getting the GPIO values is possible,
+analogous to gpio_get_value() and gpio_set_value(). Each bit in the return
+value of gpio_block_get() and in the value argument of gpio_block_set()
+corresponds to a bit specified on gpio_block_create(). The mask parameters
+specify which bits in the block are acted upon. This is useful to let some bits
+untouched when doing a set operation on the block.
+
+Block operations in hardware are only possible where the respective GPIO driver
+implements it, falling back to using single GPIO operations where the driver
+only implements single GPIO access.
+
+If a GPIO block includes GPIOs from several chips, the chips are handled one
+after another in the order of first specification in the list of GPIOs defined
+in the GPIO block, starting with bit 0. Note that in this case, you typically
+can't assume simultaneous access.
+
+void gpio_block_free(struct gpio_block *block);
+
+After the GPIO block isn't used anymore, it should be free'd via
+gpio_block_free().
+
+int gpio_block_register(struct gpio_block *block);
+void gpio_block_unregister(struct gpio_block *block);
+
+These functions can be used to register a GPIO block. Blocks registered this
+way will be available via userspace API.
+
+
 What do these conventions omit?
 ===============================
 One of the biggest things these conventions omit is pin multiplexing, since
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -83,6 +83,8 @@ static inline void desc_set_label(struct
 #endif
 }
 
+static LIST_HEAD(gpio_block_list);
+
 /* Warn when drivers omit gpio_request() calls -- legal but ill-advised
  * when setting direction, and otherwise illegal.  Until board setup code
  * and drivers use explicit requests everywhere (which won't happen when
@@ -217,6 +219,16 @@ static int gpio_get_direction(unsigned g
 	return status;
 }
 
+static bool gpio_block_is_output(struct gpio_block *block)
+{
+	int i;
+
+	for (i = 0; i < block->ngpio; i++)
+		if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
+			return false;
+	return true;
+}
+
 #ifdef CONFIG_GPIO_SYSFS
 
 /* lock protects against unexport_gpio() being called while
@@ -1799,6 +1811,221 @@ void __gpio_set_value(unsigned gpio, int
 }
 EXPORT_SYMBOL_GPL(__gpio_set_value);
 
+static struct gpio_block_chip *gpio_block_chip_get(struct gpio_block *block,
+						   struct gpio_chip *gc)
+{
+	struct gpio_block_chip *i;
+
+	list_for_each_entry(i, &block->gbc_list, list)
+		if (i->gc == gc)
+			return i;
+	return NULL;
+}
+
+struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
+				     const char *name)
+{
+	struct gpio_block *block;
+	struct gpio_block_chip *gbc;
+	struct gpio_remap *remap;
+	int i;
+
+	if (size < 1 || size > BITS_PER_LONG)
+		return ERR_PTR(-EINVAL);
+
+	for (i = 0; i < size; i++)
+		if (!gpio_is_valid(gpios[i]))
+			return ERR_PTR(-EINVAL);
+
+	block = kzalloc(sizeof(struct gpio_block), GFP_KERNEL);
+	if (!block)
+		return ERR_PTR(-ENOMEM);
+
+	block->name = name;
+	block->ngpio = size;
+	INIT_LIST_HEAD(&block->gbc_list);
+	block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
+	if (!block->gpio)
+		goto err;
+
+	memcpy(block->gpio, gpios, sizeof(*block->gpio) * size);
+
+	for (i = 0; i < size; i++) {
+		struct gpio_chip *gc = gpio_to_chip(gpios[i]);
+		int bit = gpios[i] - gc->base;
+		gbc = gpio_block_chip_get(block, gc);
+
+		if (!gbc) {
+			gbc = kzalloc(sizeof(struct gpio_block_chip),
+				      GFP_KERNEL);
+			if (!gbc)
+				goto err;
+			list_add_tail(&gbc->list, &block->gbc_list);
+			gbc->gc = gc;
+			INIT_LIST_HEAD(&gbc->remap_list);
+		}
+
+		/*
+		 * represents the mask necessary on calls to the driver's
+		 * .get_block() and .set_block()
+		 */
+		gbc->mask |= BIT(bit);
+
+		/*
+		 * collect gpios that are specified together, represented by
+		 * neighboring bits
+		 *
+		 * get last element and create new element if list empty or
+		 * new element necessary
+		 */
+		remap = list_entry(gbc->remap_list.prev, struct gpio_remap,
+				   list);
+		if (list_empty(&gbc->remap_list) ||
+		    (bit - i != remap->offset)) {
+			remap = kzalloc(sizeof(struct gpio_remap), GFP_KERNEL);
+			if (!remap)
+				goto err;
+			list_add_tail(&remap->list, &gbc->remap_list);
+			remap->offset = bit - i;
+		}
+
+		/*
+		 * represents the mask necessary for bit reordering between
+		 * gpio_block (i.e. as specified on gpio_block_get() and
+		 * gpio_block_set()) and gpio_chip domain (i.e. as specified on
+		 * the driver's .set_block() and .get_block())
+		 */
+		remap->mask |= BIT(i);
+	}
+
+	return block;
+err:
+	gpio_block_free(block);
+	return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL_GPL(gpio_block_create);
+
+void gpio_block_free(struct gpio_block *block)
+{
+	struct list_head *i, *itmp, *j, *jtmp;
+	struct gpio_block_chip *gbc;
+	struct gpio_remap *remap;
+
+	list_for_each_safe(i, itmp, &block->gbc_list) {
+		gbc = list_entry(i, struct gpio_block_chip, list);
+		list_del(i);
+		list_for_each_safe(j, jtmp, &gbc->remap_list) {
+		       remap = list_entry(j, struct gpio_remap, list);
+		       list_del(j);
+		       kfree(remap);
+		}
+		kfree(gbc);
+	}
+
+	kfree(block->gpio);
+	kfree(block);
+}
+EXPORT_SYMBOL_GPL(gpio_block_free);
+
+unsigned long gpio_block_get(const struct gpio_block *block, unsigned long mask)
+{
+	struct gpio_block_chip *gbc;
+	unsigned long values = 0;
+
+	list_for_each_entry(gbc, &block->gbc_list, list) {
+		struct gpio_remap *remap;
+		int i;
+		unsigned long remapped = 0;
+		unsigned long remapped_mask = 0;
+
+		list_for_each_entry(remap, &gbc->remap_list, list)
+			remapped_mask |= (mask & remap->mask) << remap->offset;
+		remapped_mask &= gbc->mask;
+
+		if (!remapped_mask)
+			continue;
+
+		if (gbc->gc->get_block)
+			remapped = gbc->gc->get_block(gbc->gc, remapped_mask);
+		else
+			/* emulate */
+			for_each_set_bit(i, &remapped_mask, BITS_PER_LONG)
+				remapped |= gbc->gc->get(gbc->gc,
+						gbc->gc->base + i) << i;
+
+		list_for_each_entry(remap, &gbc->remap_list, list)
+			values |= (remapped >> remap->offset) & remap->mask;
+	}
+
+	return values;
+}
+EXPORT_SYMBOL_GPL(gpio_block_get);
+
+void gpio_block_set(struct gpio_block *block, unsigned long mask,
+		    unsigned long values)
+{
+	struct gpio_block_chip *gbc;
+
+	list_for_each_entry(gbc, &block->gbc_list, list) {
+		struct gpio_remap *remap;
+		int i;
+		unsigned long remapped = 0;
+		unsigned long remapped_mask = 0;
+
+		list_for_each_entry(remap, &gbc->remap_list, list) {
+			remapped |= (values & remap->mask) << remap->offset;
+			remapped_mask |= (mask & remap->mask) << remap->offset;
+		}
+		remapped_mask &= gbc->mask;
+
+		if (!remapped_mask)
+			continue;
+
+		if (gbc->gc->set_block)
+			gbc->gc->set_block(gbc->gc, remapped_mask, remapped);
+		else
+			/* emulate */
+			for_each_set_bit(i, &remapped_mask, BITS_PER_LONG)
+				gbc->gc->set(gbc->gc, gbc->gc->base + i,
+					     (remapped >> i) & 1);
+	}
+}
+EXPORT_SYMBOL_GPL(gpio_block_set);
+
+struct gpio_block *gpio_block_find_by_name(const char *name)
+{
+	struct gpio_block *i;
+
+	list_for_each_entry(i, &gpio_block_list, list)
+		if (!strcmp(i->name, name))
+			return i;
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(gpio_block_find_by_name);
+
+int gpio_block_register(struct gpio_block *block)
+{
+	if (gpio_block_find_by_name(block->name))
+		return -EBUSY;
+
+	list_add(&block->list, &gpio_block_list);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_block_register);
+
+void gpio_block_unregister(struct gpio_block *block)
+{
+	struct gpio_block *i;
+
+	list_for_each_entry(i, &gpio_block_list, list)
+		if (i == block) {
+			list_del(&i->list);
+			break;
+		}
+}
+EXPORT_SYMBOL_GPL(gpio_block_unregister);
+
 /**
  * __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
@@ -44,6 +44,7 @@ static inline bool gpio_is_valid(int num
 
 struct device;
 struct gpio;
+struct gpio_block;
 struct seq_file;
 struct module;
 struct device_node;
@@ -109,6 +110,8 @@ struct gpio_chip {
 						unsigned offset);
 	int			(*get)(struct gpio_chip *chip,
 						unsigned offset);
+	unsigned long		(*get_block)(struct gpio_chip *chip,
+					     unsigned long mask);
 	int			(*direction_output)(struct gpio_chip *chip,
 						unsigned offset, int value);
 	int			(*set_debounce)(struct gpio_chip *chip,
@@ -116,6 +119,9 @@ struct gpio_chip {
 
 	void			(*set)(struct gpio_chip *chip,
 						unsigned offset, int value);
+	void			(*set_block)(struct gpio_chip *chip,
+					     unsigned long mask,
+					     unsigned long values);
 
 	int			(*to_irq)(struct gpio_chip *chip,
 						unsigned offset);
@@ -184,6 +190,17 @@ extern void gpio_set_value_cansleep(unsi
 extern int __gpio_get_value(unsigned gpio);
 extern void __gpio_set_value(unsigned gpio, int value);
 
+extern struct gpio_block *gpio_block_create(unsigned *gpio, size_t size,
+					    const char *name);
+extern void gpio_block_free(struct gpio_block *block);
+extern unsigned long gpio_block_get(const struct gpio_block *block,
+				    unsigned long mask);
+extern void gpio_block_set(struct gpio_block *block, unsigned long mask,
+			   unsigned long values);
+extern struct gpio_block *gpio_block_find_by_name(const char *name);
+extern int gpio_block_register(struct gpio_block *block);
+extern void gpio_block_unregister(struct gpio_block *block);
+
 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
@@ -2,6 +2,8 @@
 #define __LINUX_GPIO_H
 
 #include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/list.h>
 
 /* see Documentation/gpio.txt */
 
@@ -39,6 +41,64 @@ struct gpio {
 	const char	*label;
 };
 
+/**
+ * struct gpio_remap - a structure for describing a bit mapping
+ * @mask:	a bit mask, relevant for a (partial) mapping
+ * @offset:	how many bits to shift to the left (negative: to the right)
+ * @list:	several remappings are internally collected in a list
+ *
+ * When we are mapping bit values from one word to another (here: from GPIO
+ * block domain to GPIO driver domain) we first mask them out with mask and
+ * shift them as specified with offset. More complicated mappings are done by
+ * grouping several of those structs and adding the results together.
+ */
+struct gpio_remap {
+	unsigned long		mask;
+	int			offset;
+
+	struct list_head	list;
+};
+
+/**
+ * struct gpio_block_chip - a structure representing chip specific data in a
+ *                          gpio block
+ * @gc:          the chip
+ * @remap_list:  list of remappings, there are several necessary if the bits
+ *               are not consecutive
+ * @mask:        chip specific mask, used for propagating to the driver's
+ *               get_block() and set_block() functions
+ * @list:        list collecting potentially multiple chips in one block
+ *
+ * This structure holds information about remapping and masking of gpios within
+ * one chip. There can be several of those in one block.
+ */
+struct gpio_block_chip {
+	struct gpio_chip	*gc;
+	struct list_head	remap_list;
+	unsigned long		mask;
+
+	struct list_head	list;
+};
+
+/**
+ * struct gpio_block - a structure describing a list of GPIOs for simultaneous
+ *                     operations
+ * @gbc_list:   list of chips in this block, typically just one
+ * @name:       the name of this block
+ * @ngpio:      number of gpios in this block
+ * @gpio:       list of gpios in this block
+ * @list:       global list of blocks, maintained by gpiolib
+ */
+struct gpio_block {
+	struct list_head	gbc_list;
+	const char		*name;
+
+	int			ngpio;
+	unsigned		*gpio;
+
+	struct list_head	list;
+};
+
 #ifdef CONFIG_GENERIC_GPIO
 
 #ifdef CONFIG_ARCH_HAVE_CUSTOM_GPIO_H
@@ -169,6 +229,43 @@ static inline void gpio_set_value(unsign
 	WARN_ON(1);
 }
 
+static inline
+struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
+				     const char *name)
+{
+	WARN_ON(1);
+	return NULL;
+}
+
+static inline void gpio_block_free(struct gpio_block *block)
+{
+	WARN_ON(1);
+}
+
+static inline unsigned long gpio_block_get(const struct gpio_block *block,
+					   unsigned long mask)
+{
+	WARN_ON(1);
+	return 0;
+}
+
+static inline void gpio_block_set(struct gpio_block *block, unsigned long mask,
+				  unsigned long values)
+{
+	WARN_ON(1);
+}
+
+static inline int gpio_block_register(struct gpio_block *block)
+{
+	WARN_ON(1);
+	return 0;
+}
+
+static inline void gpio_block_unregister(struct gpio_block *block)
+{
+	WARN_ON(1);
+}
+
 static inline int gpio_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
@ 2013-01-15 11:51   ` Roland Stigge
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel

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. This is done by abstracting GPIOs to an n-bit word:
Once requested, it provides access to a group of GPIOs which can range over
multiple GPIO chips.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---

 Documentation/gpio.txt     |   58 +++++++++++
 drivers/gpio/gpiolib.c     |  227 +++++++++++++++++++++++++++++++++++++++++++++
 include/asm-generic/gpio.h |   17 +++
 include/linux/gpio.h       |   97 +++++++++++++++++++
 4 files changed, 399 insertions(+)

--- linux-2.6.orig/Documentation/gpio.txt
+++ linux-2.6/Documentation/gpio.txt
@@ -481,6 +481,64 @@ exact name string of pinctrl device has
 argument to this routine.
 
 
+Block GPIO
+----------
+
+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 grouped 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:
+
+struct gpio_block *gpio_block_create(unsigned int *gpios, size_t size,
+				     const char *name);
+
+This creates a new block of GPIOs as a list of GPIO numbers with the specified
+size which are accessible via the returned struct gpio_block and the accessor
+functions described below. Please note that you need to request the GPIOs
+separately via gpio_request(). Similarly, the direction of the used GPIOs need
+to be set by the user before using the block. An arbitrary list of globally
+valid GPIOs can be specified, even ranging over several gpio_chips. Actual
+handling of I/O operations will be done on a best effort base, i.e.
+simultaneous I/O only where possible by hardware and implemented in the
+respective GPIO driver. The number of GPIOs in one block is limited to the
+number of bits in an unsigned long, or BITS_PER_LONG, of the respective
+platform, i.e. typically at least 32 on a 32 bit system, and at least 64 on a
+64 bit system. However, several blocks can be defined at once.
+
+unsigned long gpio_block_get(struct gpio_block *block, unsigned long mask);
+void gpio_block_set(struct gpio_block *block, unsigned long mask,
+		    unsigned long values);
+
+With those accessor functions, setting and getting the GPIO values is possible,
+analogous to gpio_get_value() and gpio_set_value(). Each bit in the return
+value of gpio_block_get() and in the value argument of gpio_block_set()
+corresponds to a bit specified on gpio_block_create(). The mask parameters
+specify which bits in the block are acted upon. This is useful to let some bits
+untouched when doing a set operation on the block.
+
+Block operations in hardware are only possible where the respective GPIO driver
+implements it, falling back to using single GPIO operations where the driver
+only implements single GPIO access.
+
+If a GPIO block includes GPIOs from several chips, the chips are handled one
+after another in the order of first specification in the list of GPIOs defined
+in the GPIO block, starting with bit 0. Note that in this case, you typically
+can't assume simultaneous access.
+
+void gpio_block_free(struct gpio_block *block);
+
+After the GPIO block isn't used anymore, it should be free'd via
+gpio_block_free().
+
+int gpio_block_register(struct gpio_block *block);
+void gpio_block_unregister(struct gpio_block *block);
+
+These functions can be used to register a GPIO block. Blocks registered this
+way will be available via userspace API.
+
+
 What do these conventions omit?
 ===============================
 One of the biggest things these conventions omit is pin multiplexing, since
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -83,6 +83,8 @@ static inline void desc_set_label(struct
 #endif
 }
 
+static LIST_HEAD(gpio_block_list);
+
 /* Warn when drivers omit gpio_request() calls -- legal but ill-advised
  * when setting direction, and otherwise illegal.  Until board setup code
  * and drivers use explicit requests everywhere (which won't happen when
@@ -217,6 +219,16 @@ static int gpio_get_direction(unsigned g
 	return status;
 }
 
+static bool gpio_block_is_output(struct gpio_block *block)
+{
+	int i;
+
+	for (i = 0; i < block->ngpio; i++)
+		if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
+			return false;
+	return true;
+}
+
 #ifdef CONFIG_GPIO_SYSFS
 
 /* lock protects against unexport_gpio() being called while
@@ -1799,6 +1811,221 @@ void __gpio_set_value(unsigned gpio, int
 }
 EXPORT_SYMBOL_GPL(__gpio_set_value);
 
+static struct gpio_block_chip *gpio_block_chip_get(struct gpio_block *block,
+						   struct gpio_chip *gc)
+{
+	struct gpio_block_chip *i;
+
+	list_for_each_entry(i, &block->gbc_list, list)
+		if (i->gc == gc)
+			return i;
+	return NULL;
+}
+
+struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
+				     const char *name)
+{
+	struct gpio_block *block;
+	struct gpio_block_chip *gbc;
+	struct gpio_remap *remap;
+	int i;
+
+	if (size < 1 || size > BITS_PER_LONG)
+		return ERR_PTR(-EINVAL);
+
+	for (i = 0; i < size; i++)
+		if (!gpio_is_valid(gpios[i]))
+			return ERR_PTR(-EINVAL);
+
+	block = kzalloc(sizeof(struct gpio_block), GFP_KERNEL);
+	if (!block)
+		return ERR_PTR(-ENOMEM);
+
+	block->name = name;
+	block->ngpio = size;
+	INIT_LIST_HEAD(&block->gbc_list);
+	block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
+	if (!block->gpio)
+		goto err;
+
+	memcpy(block->gpio, gpios, sizeof(*block->gpio) * size);
+
+	for (i = 0; i < size; i++) {
+		struct gpio_chip *gc = gpio_to_chip(gpios[i]);
+		int bit = gpios[i] - gc->base;
+		gbc = gpio_block_chip_get(block, gc);
+
+		if (!gbc) {
+			gbc = kzalloc(sizeof(struct gpio_block_chip),
+				      GFP_KERNEL);
+			if (!gbc)
+				goto err;
+			list_add_tail(&gbc->list, &block->gbc_list);
+			gbc->gc = gc;
+			INIT_LIST_HEAD(&gbc->remap_list);
+		}
+
+		/*
+		 * represents the mask necessary on calls to the driver's
+		 * .get_block() and .set_block()
+		 */
+		gbc->mask |= BIT(bit);
+
+		/*
+		 * collect gpios that are specified together, represented by
+		 * neighboring bits
+		 *
+		 * get last element and create new element if list empty or
+		 * new element necessary
+		 */
+		remap = list_entry(gbc->remap_list.prev, struct gpio_remap,
+				   list);
+		if (list_empty(&gbc->remap_list) ||
+		    (bit - i != remap->offset)) {
+			remap = kzalloc(sizeof(struct gpio_remap), GFP_KERNEL);
+			if (!remap)
+				goto err;
+			list_add_tail(&remap->list, &gbc->remap_list);
+			remap->offset = bit - i;
+		}
+
+		/*
+		 * represents the mask necessary for bit reordering between
+		 * gpio_block (i.e. as specified on gpio_block_get() and
+		 * gpio_block_set()) and gpio_chip domain (i.e. as specified on
+		 * the driver's .set_block() and .get_block())
+		 */
+		remap->mask |= BIT(i);
+	}
+
+	return block;
+err:
+	gpio_block_free(block);
+	return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL_GPL(gpio_block_create);
+
+void gpio_block_free(struct gpio_block *block)
+{
+	struct list_head *i, *itmp, *j, *jtmp;
+	struct gpio_block_chip *gbc;
+	struct gpio_remap *remap;
+
+	list_for_each_safe(i, itmp, &block->gbc_list) {
+		gbc = list_entry(i, struct gpio_block_chip, list);
+		list_del(i);
+		list_for_each_safe(j, jtmp, &gbc->remap_list) {
+		       remap = list_entry(j, struct gpio_remap, list);
+		       list_del(j);
+		       kfree(remap);
+		}
+		kfree(gbc);
+	}
+
+	kfree(block->gpio);
+	kfree(block);
+}
+EXPORT_SYMBOL_GPL(gpio_block_free);
+
+unsigned long gpio_block_get(const struct gpio_block *block, unsigned long mask)
+{
+	struct gpio_block_chip *gbc;
+	unsigned long values = 0;
+
+	list_for_each_entry(gbc, &block->gbc_list, list) {
+		struct gpio_remap *remap;
+		int i;
+		unsigned long remapped = 0;
+		unsigned long remapped_mask = 0;
+
+		list_for_each_entry(remap, &gbc->remap_list, list)
+			remapped_mask |= (mask & remap->mask) << remap->offset;
+		remapped_mask &= gbc->mask;
+
+		if (!remapped_mask)
+			continue;
+
+		if (gbc->gc->get_block)
+			remapped = gbc->gc->get_block(gbc->gc, remapped_mask);
+		else
+			/* emulate */
+			for_each_set_bit(i, &remapped_mask, BITS_PER_LONG)
+				remapped |= gbc->gc->get(gbc->gc,
+						gbc->gc->base + i) << i;
+
+		list_for_each_entry(remap, &gbc->remap_list, list)
+			values |= (remapped >> remap->offset) & remap->mask;
+	}
+
+	return values;
+}
+EXPORT_SYMBOL_GPL(gpio_block_get);
+
+void gpio_block_set(struct gpio_block *block, unsigned long mask,
+		    unsigned long values)
+{
+	struct gpio_block_chip *gbc;
+
+	list_for_each_entry(gbc, &block->gbc_list, list) {
+		struct gpio_remap *remap;
+		int i;
+		unsigned long remapped = 0;
+		unsigned long remapped_mask = 0;
+
+		list_for_each_entry(remap, &gbc->remap_list, list) {
+			remapped |= (values & remap->mask) << remap->offset;
+			remapped_mask |= (mask & remap->mask) << remap->offset;
+		}
+		remapped_mask &= gbc->mask;
+
+		if (!remapped_mask)
+			continue;
+
+		if (gbc->gc->set_block)
+			gbc->gc->set_block(gbc->gc, remapped_mask, remapped);
+		else
+			/* emulate */
+			for_each_set_bit(i, &remapped_mask, BITS_PER_LONG)
+				gbc->gc->set(gbc->gc, gbc->gc->base + i,
+					     (remapped >> i) & 1);
+	}
+}
+EXPORT_SYMBOL_GPL(gpio_block_set);
+
+struct gpio_block *gpio_block_find_by_name(const char *name)
+{
+	struct gpio_block *i;
+
+	list_for_each_entry(i, &gpio_block_list, list)
+		if (!strcmp(i->name, name))
+			return i;
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(gpio_block_find_by_name);
+
+int gpio_block_register(struct gpio_block *block)
+{
+	if (gpio_block_find_by_name(block->name))
+		return -EBUSY;
+
+	list_add(&block->list, &gpio_block_list);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_block_register);
+
+void gpio_block_unregister(struct gpio_block *block)
+{
+	struct gpio_block *i;
+
+	list_for_each_entry(i, &gpio_block_list, list)
+		if (i == block) {
+			list_del(&i->list);
+			break;
+		}
+}
+EXPORT_SYMBOL_GPL(gpio_block_unregister);
+
 /**
  * __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
@@ -44,6 +44,7 @@ static inline bool gpio_is_valid(int num
 
 struct device;
 struct gpio;
+struct gpio_block;
 struct seq_file;
 struct module;
 struct device_node;
@@ -109,6 +110,8 @@ struct gpio_chip {
 						unsigned offset);
 	int			(*get)(struct gpio_chip *chip,
 						unsigned offset);
+	unsigned long		(*get_block)(struct gpio_chip *chip,
+					     unsigned long mask);
 	int			(*direction_output)(struct gpio_chip *chip,
 						unsigned offset, int value);
 	int			(*set_debounce)(struct gpio_chip *chip,
@@ -116,6 +119,9 @@ struct gpio_chip {
 
 	void			(*set)(struct gpio_chip *chip,
 						unsigned offset, int value);
+	void			(*set_block)(struct gpio_chip *chip,
+					     unsigned long mask,
+					     unsigned long values);
 
 	int			(*to_irq)(struct gpio_chip *chip,
 						unsigned offset);
@@ -184,6 +190,17 @@ extern void gpio_set_value_cansleep(unsi
 extern int __gpio_get_value(unsigned gpio);
 extern void __gpio_set_value(unsigned gpio, int value);
 
+extern struct gpio_block *gpio_block_create(unsigned *gpio, size_t size,
+					    const char *name);
+extern void gpio_block_free(struct gpio_block *block);
+extern unsigned long gpio_block_get(const struct gpio_block *block,
+				    unsigned long mask);
+extern void gpio_block_set(struct gpio_block *block, unsigned long mask,
+			   unsigned long values);
+extern struct gpio_block *gpio_block_find_by_name(const char *name);
+extern int gpio_block_register(struct gpio_block *block);
+extern void gpio_block_unregister(struct gpio_block *block);
+
 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
@@ -2,6 +2,8 @@
 #define __LINUX_GPIO_H
 
 #include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/list.h>
 
 /* see Documentation/gpio.txt */
 
@@ -39,6 +41,64 @@ struct gpio {
 	const char	*label;
 };
 
+/**
+ * struct gpio_remap - a structure for describing a bit mapping
+ * @mask:	a bit mask, relevant for a (partial) mapping
+ * @offset:	how many bits to shift to the left (negative: to the right)
+ * @list:	several remappings are internally collected in a list
+ *
+ * When we are mapping bit values from one word to another (here: from GPIO
+ * block domain to GPIO driver domain) we first mask them out with mask and
+ * shift them as specified with offset. More complicated mappings are done by
+ * grouping several of those structs and adding the results together.
+ */
+struct gpio_remap {
+	unsigned long		mask;
+	int			offset;
+
+	struct list_head	list;
+};
+
+/**
+ * struct gpio_block_chip - a structure representing chip specific data in a
+ *                          gpio block
+ * @gc:          the chip
+ * @remap_list:  list of remappings, there are several necessary if the bits
+ *               are not consecutive
+ * @mask:        chip specific mask, used for propagating to the driver's
+ *               get_block() and set_block() functions
+ * @list:        list collecting potentially multiple chips in one block
+ *
+ * This structure holds information about remapping and masking of gpios within
+ * one chip. There can be several of those in one block.
+ */
+struct gpio_block_chip {
+	struct gpio_chip	*gc;
+	struct list_head	remap_list;
+	unsigned long		mask;
+
+	struct list_head	list;
+};
+
+/**
+ * struct gpio_block - a structure describing a list of GPIOs for simultaneous
+ *                     operations
+ * @gbc_list:   list of chips in this block, typically just one
+ * @name:       the name of this block
+ * @ngpio:      number of gpios in this block
+ * @gpio:       list of gpios in this block
+ * @list:       global list of blocks, maintained by gpiolib
+ */
+struct gpio_block {
+	struct list_head	gbc_list;
+	const char		*name;
+
+	int			ngpio;
+	unsigned		*gpio;
+
+	struct list_head	list;
+};
+
 #ifdef CONFIG_GENERIC_GPIO
 
 #ifdef CONFIG_ARCH_HAVE_CUSTOM_GPIO_H
@@ -169,6 +229,43 @@ static inline void gpio_set_value(unsign
 	WARN_ON(1);
 }
 
+static inline
+struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
+				     const char *name)
+{
+	WARN_ON(1);
+	return NULL;
+}
+
+static inline void gpio_block_free(struct gpio_block *block)
+{
+	WARN_ON(1);
+}
+
+static inline unsigned long gpio_block_get(const struct gpio_block *block,
+					   unsigned long mask)
+{
+	WARN_ON(1);
+	return 0;
+}
+
+static inline void gpio_block_set(struct gpio_block *block, unsigned long mask,
+				  unsigned long values)
+{
+	WARN_ON(1);
+}
+
+static inline int gpio_block_register(struct gpio_block *block)
+{
+	WARN_ON(1);
+	return 0;
+}
+
+static inline void gpio_block_unregister(struct gpio_block *block)
+{
+	WARN_ON(1);
+}
+
 static inline int gpio_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 2/6 v13] gpio: Add sysfs support to block GPIO API
  2013-01-15 11:51 ` Roland Stigge
@ 2013-01-15 11:51   ` Roland Stigge
  -1 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: gregkh, grant.likely, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, highguy, broonie,
	daniel-gl, rmallon, sr, wg, tru, mark.rutland
  Cc: Roland Stigge

This patch adds sysfs support to the block GPIO API.

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 Documentation/ABI/testing/sysfs-gpio |   20 ++
 drivers/gpio/gpiolib.c               |  252 ++++++++++++++++++++++++++++++++++-
 include/asm-generic/gpio.h           |   11 +
 include/linux/gpio.h                 |   15 ++
 4 files changed, 297 insertions(+), 1 deletion(-)

--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -25,3 +25,23 @@ Description:
 	    /label ... (r/o) descriptive, not necessarily unique
 	    /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
 
+What:		/sys/class/gpioblock/
+Date:		October 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:
+
+  Block GPIO devices are visible in sysfs as soon as they are registered
+  (e.g. via devicetree definition). For actual I/O use, their "exported"
+  boolean attribute must be set to "1". Then, the attribute "values" is
+  created and at the same time, the GPIOs in the block are requested for
+  exclusive use by sysfs.
+
+    /sys/class/gpioblock
+	/BLOCKNAME ... for each GPIO block name
+	    /ngpio ... (r/o) number of GPIOs in this group
+	    /exported ... sysfs export state of this group (0, 1)
+	    /value ... current value as 32 or 64 bit integer in hex
+                       (only available if /exported is 1)
+	    /mask ... current mask as 32 or 64 bit integer in hex
+                       (only available if /exported is 1)
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -224,7 +224,8 @@ static bool gpio_block_is_output(struct
 	int i;
 
 	for (i = 0; i < block->ngpio; i++)
-		if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
+		if ((block->cur_mask & BIT(i)) &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
 			return false;
 	return true;
 }
@@ -1020,6 +1021,240 @@ static void gpiochip_unexport(struct gpi
 				chip->label, status);
 }
 
+static ssize_t gpio_block_ngpio_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block	*block = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", block->ngpio);
+}
+
+static ssize_t gpio_block_value_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block *block = dev_get_drvdata(dev);
+
+	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
+		       "0x%016lx\n", gpio_block_get(block, block->cur_mask));
+}
+
+static ssize_t gpio_block_value_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	ssize_t			status;
+	struct gpio_block	*block = dev_get_drvdata(dev);
+	unsigned long		value;
+
+	status = kstrtoul(buf, 0, &value);
+	if (status == 0) {
+		mutex_lock(&sysfs_lock);
+		if (gpio_block_is_output(block)) {
+			gpio_block_set(block, block->cur_mask, value);
+			status = size;
+		} else {
+			status = -EPERM;
+		}
+		mutex_unlock(&sysfs_lock);
+	}
+	return status;
+}
+
+static struct device_attribute
+dev_attr_block_value = __ATTR(value, S_IWUSR | S_IRUGO, gpio_block_value_show,
+			      gpio_block_value_store);
+
+static ssize_t gpio_block_mask_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block *block = dev_get_drvdata(dev);
+
+	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
+		       "0x%016lx\n", block->cur_mask);
+}
+
+static ssize_t gpio_block_mask_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t size)
+{
+	ssize_t			status;
+	struct gpio_block	*block = dev_get_drvdata(dev);
+	unsigned long		mask;
+
+	status = kstrtoul(buf, 0, &mask);
+	if (status == 0) {
+		block->cur_mask = mask;
+		status = size;
+	}
+	return status;
+}
+
+static struct device_attribute
+dev_attr_block_mask = __ATTR(mask, S_IWUSR | S_IRUGO, gpio_block_mask_show,
+			     gpio_block_mask_store);
+
+static struct class gpio_block_class;
+
+static int gpio_block_value_is_exported(struct gpio_block *block)
+{
+	struct device		*dev;
+	struct sysfs_dirent	*sd = NULL;
+
+	mutex_lock(&sysfs_lock);
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev)
+		goto out;
+
+	sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return !!sd;
+}
+
+static ssize_t gpio_block_exported_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct gpio_block	*block = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", gpio_block_value_is_exported(block));
+}
+
+static int gpio_block_value_export(struct gpio_block *block)
+{
+	struct device	*dev;
+	int		status;
+	int		i;
+
+	mutex_lock(&sysfs_lock);
+
+	for (i = 0; i < block->ngpio; i++) {
+		status = gpio_request(block->gpio[i], "sysfs");
+		if (status)
+			goto out1;
+	}
+
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev) {
+		status = -ENODEV;
+		goto out1;
+	}
+
+	status = device_create_file(dev, &dev_attr_block_value);
+	if (status)
+		goto out1;
+
+	status = device_create_file(dev, &dev_attr_block_mask);
+	if (status)
+		goto out2;
+
+	mutex_unlock(&sysfs_lock);
+	return 0;
+
+out2:
+	device_remove_file(dev, &dev_attr_block_value);
+out1:
+	while (--i >= 0)
+		gpio_free(block->gpio[i]);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static int gpio_block_value_unexport(struct gpio_block *block)
+{
+	struct device	*dev;
+	int		i;
+
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev)
+		return -ENODEV;
+
+	for (i = 0; i < block->ngpio; i++)
+		gpio_free(block->gpio[i]);
+
+	device_remove_file(dev, &dev_attr_block_value);
+	device_remove_file(dev, &dev_attr_block_mask);
+
+	return 0;
+}
+
+static ssize_t gpio_block_exported_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t size)
+{
+	long	value;
+	int	status;
+	struct	gpio_block *block = dev_get_drvdata(dev);
+	int	exported = gpio_block_value_is_exported(block);
+
+	status = kstrtoul(buf, 0, &value);
+	if (status < 0)
+		goto err;
+
+	if (value != exported) {
+		if (value)
+			status = gpio_block_value_export(block);
+		else
+			status = gpio_block_value_unexport(block);
+		if (!status)
+			status = size;
+	} else {
+		status = size;
+	}
+err:
+	return status;
+}
+
+static struct device_attribute gpio_block_attrs[] = {
+	__ATTR(exported, S_IWUSR | S_IRUGO, gpio_block_exported_show,
+	       gpio_block_exported_store),
+	__ATTR(ngpio, S_IRUGO, gpio_block_ngpio_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct class gpio_block_class = {
+	.name =         "gpioblock",
+	.owner =        THIS_MODULE,
+
+	.dev_attrs =  gpio_block_attrs,
+};
+
+int gpio_block_export(struct gpio_block *block)
+{
+	int		status = 0;
+	struct device	*dev;
+
+	/* can't export until sysfs is available ... */
+	if (!gpio_block_class.p) {
+		pr_debug("%s: called too early!\n", __func__);
+		return -ENOENT;
+	}
+
+	mutex_lock(&sysfs_lock);
+	dev = device_create(&gpio_block_class, NULL, MKDEV(0, 0), block,
+			    block->name);
+	if (IS_ERR(dev))
+		status = PTR_ERR(dev);
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_block_export);
+
+void gpio_block_unexport(struct gpio_block *block)
+{
+	struct device *dev;
+
+	mutex_lock(&sysfs_lock);
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (dev)
+		device_unregister(dev);
+	mutex_unlock(&sysfs_lock);
+}
+EXPORT_SYMBOL_GPL(gpio_block_unexport);
+
 static int __init gpiolib_sysfs_init(void)
 {
 	int		status;
@@ -1030,6 +1265,10 @@ static int __init gpiolib_sysfs_init(voi
 	if (status < 0)
 		return status;
 
+	status = class_register(&gpio_block_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).
 	 *
@@ -1843,6 +2082,7 @@ struct gpio_block *gpio_block_create(uns
 
 	block->name = name;
 	block->ngpio = size;
+	block->cur_mask = ~0;
 	INIT_LIST_HEAD(&block->gbc_list);
 	block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
 	if (!block->gpio)
@@ -2005,12 +2245,21 @@ EXPORT_SYMBOL_GPL(gpio_block_find_by_nam
 
 int gpio_block_register(struct gpio_block *block)
 {
+	int ret;
+
 	if (gpio_block_find_by_name(block->name))
 		return -EBUSY;
 
 	list_add(&block->list, &gpio_block_list);
 
+	ret = gpio_block_export(block);
+	if (ret)
+		goto err1;
+
 	return 0;
+err1:
+	list_del(&block->list);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(gpio_block_register);
 
@@ -2021,6 +2270,7 @@ void gpio_block_unregister(struct gpio_b
 	list_for_each_entry(i, &gpio_block_list, list)
 		if (i == block) {
 			list_del(&i->list);
+			gpio_block_unexport(block);
 			break;
 		}
 }
--- linux-2.6.orig/include/asm-generic/gpio.h
+++ linux-2.6/include/asm-generic/gpio.h
@@ -226,6 +226,8 @@ extern int gpio_export_link(struct devic
 			unsigned gpio);
 extern int gpio_sysfs_set_active_low(unsigned gpio, int value);
 extern void gpio_unexport(unsigned gpio);
+extern int gpio_block_export(struct gpio_block *block);
+extern void gpio_block_unexport(struct gpio_block *block);
 
 #endif	/* CONFIG_GPIO_SYSFS */
 
@@ -285,6 +287,15 @@ static inline int gpio_sysfs_set_active_
 static inline void gpio_unexport(unsigned gpio)
 {
 }
+
+static inline int gpio_block_export(struct gpio_block *block)
+{
+	return -ENOSYS;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+}
 #endif	/* CONFIG_GPIO_SYSFS */
 
 #ifdef CONFIG_PINCTRL
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -88,6 +88,7 @@ struct gpio_block_chip {
  * @ngpio:      number of gpios in this block
  * @gpio:       list of gpios in this block
  * @list:       global list of blocks, maintained by gpiolib
+ * @cur_mask:   currently used gpio mask used by userspace API
  */
 struct gpio_block {
 	struct list_head	gbc_list;
@@ -97,6 +98,7 @@ struct gpio_block {
 	unsigned		*gpio;
 
 	struct list_head	list;
+	unsigned long		cur_mask;
 };
 
 #ifdef CONFIG_GENERIC_GPIO
@@ -314,6 +316,19 @@ static inline void gpio_unexport(unsigne
 	WARN_ON(1);
 }
 
+static inline int gpio_block_export(struct gpio_block *block)
+{
+	/* GPIO block can never have been requested or set as {in,out}put */
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+	/* GPIO block can never have been exported */
+	WARN_ON(1);
+}
+
 static inline int gpio_to_irq(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as input */

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 2/6 v13] gpio: Add sysfs support to block GPIO API
@ 2013-01-15 11:51   ` Roland Stigge
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds sysfs support to the block GPIO API.

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 Documentation/ABI/testing/sysfs-gpio |   20 ++
 drivers/gpio/gpiolib.c               |  252 ++++++++++++++++++++++++++++++++++-
 include/asm-generic/gpio.h           |   11 +
 include/linux/gpio.h                 |   15 ++
 4 files changed, 297 insertions(+), 1 deletion(-)

--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -25,3 +25,23 @@ Description:
 	    /label ... (r/o) descriptive, not necessarily unique
 	    /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
 
+What:		/sys/class/gpioblock/
+Date:		October 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:
+
+  Block GPIO devices are visible in sysfs as soon as they are registered
+  (e.g. via devicetree definition). For actual I/O use, their "exported"
+  boolean attribute must be set to "1". Then, the attribute "values" is
+  created and at the same time, the GPIOs in the block are requested for
+  exclusive use by sysfs.
+
+    /sys/class/gpioblock
+	/BLOCKNAME ... for each GPIO block name
+	    /ngpio ... (r/o) number of GPIOs in this group
+	    /exported ... sysfs export state of this group (0, 1)
+	    /value ... current value as 32 or 64 bit integer in hex
+                       (only available if /exported is 1)
+	    /mask ... current mask as 32 or 64 bit integer in hex
+                       (only available if /exported is 1)
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -224,7 +224,8 @@ static bool gpio_block_is_output(struct
 	int i;
 
 	for (i = 0; i < block->ngpio; i++)
-		if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
+		if ((block->cur_mask & BIT(i)) &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
 			return false;
 	return true;
 }
@@ -1020,6 +1021,240 @@ static void gpiochip_unexport(struct gpi
 				chip->label, status);
 }
 
+static ssize_t gpio_block_ngpio_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block	*block = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", block->ngpio);
+}
+
+static ssize_t gpio_block_value_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block *block = dev_get_drvdata(dev);
+
+	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
+		       "0x%016lx\n", gpio_block_get(block, block->cur_mask));
+}
+
+static ssize_t gpio_block_value_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	ssize_t			status;
+	struct gpio_block	*block = dev_get_drvdata(dev);
+	unsigned long		value;
+
+	status = kstrtoul(buf, 0, &value);
+	if (status == 0) {
+		mutex_lock(&sysfs_lock);
+		if (gpio_block_is_output(block)) {
+			gpio_block_set(block, block->cur_mask, value);
+			status = size;
+		} else {
+			status = -EPERM;
+		}
+		mutex_unlock(&sysfs_lock);
+	}
+	return status;
+}
+
+static struct device_attribute
+dev_attr_block_value = __ATTR(value, S_IWUSR | S_IRUGO, gpio_block_value_show,
+			      gpio_block_value_store);
+
+static ssize_t gpio_block_mask_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block *block = dev_get_drvdata(dev);
+
+	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
+		       "0x%016lx\n", block->cur_mask);
+}
+
+static ssize_t gpio_block_mask_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t size)
+{
+	ssize_t			status;
+	struct gpio_block	*block = dev_get_drvdata(dev);
+	unsigned long		mask;
+
+	status = kstrtoul(buf, 0, &mask);
+	if (status == 0) {
+		block->cur_mask = mask;
+		status = size;
+	}
+	return status;
+}
+
+static struct device_attribute
+dev_attr_block_mask = __ATTR(mask, S_IWUSR | S_IRUGO, gpio_block_mask_show,
+			     gpio_block_mask_store);
+
+static struct class gpio_block_class;
+
+static int gpio_block_value_is_exported(struct gpio_block *block)
+{
+	struct device		*dev;
+	struct sysfs_dirent	*sd = NULL;
+
+	mutex_lock(&sysfs_lock);
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev)
+		goto out;
+
+	sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return !!sd;
+}
+
+static ssize_t gpio_block_exported_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct gpio_block	*block = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", gpio_block_value_is_exported(block));
+}
+
+static int gpio_block_value_export(struct gpio_block *block)
+{
+	struct device	*dev;
+	int		status;
+	int		i;
+
+	mutex_lock(&sysfs_lock);
+
+	for (i = 0; i < block->ngpio; i++) {
+		status = gpio_request(block->gpio[i], "sysfs");
+		if (status)
+			goto out1;
+	}
+
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev) {
+		status = -ENODEV;
+		goto out1;
+	}
+
+	status = device_create_file(dev, &dev_attr_block_value);
+	if (status)
+		goto out1;
+
+	status = device_create_file(dev, &dev_attr_block_mask);
+	if (status)
+		goto out2;
+
+	mutex_unlock(&sysfs_lock);
+	return 0;
+
+out2:
+	device_remove_file(dev, &dev_attr_block_value);
+out1:
+	while (--i >= 0)
+		gpio_free(block->gpio[i]);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static int gpio_block_value_unexport(struct gpio_block *block)
+{
+	struct device	*dev;
+	int		i;
+
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev)
+		return -ENODEV;
+
+	for (i = 0; i < block->ngpio; i++)
+		gpio_free(block->gpio[i]);
+
+	device_remove_file(dev, &dev_attr_block_value);
+	device_remove_file(dev, &dev_attr_block_mask);
+
+	return 0;
+}
+
+static ssize_t gpio_block_exported_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t size)
+{
+	long	value;
+	int	status;
+	struct	gpio_block *block = dev_get_drvdata(dev);
+	int	exported = gpio_block_value_is_exported(block);
+
+	status = kstrtoul(buf, 0, &value);
+	if (status < 0)
+		goto err;
+
+	if (value != exported) {
+		if (value)
+			status = gpio_block_value_export(block);
+		else
+			status = gpio_block_value_unexport(block);
+		if (!status)
+			status = size;
+	} else {
+		status = size;
+	}
+err:
+	return status;
+}
+
+static struct device_attribute gpio_block_attrs[] = {
+	__ATTR(exported, S_IWUSR | S_IRUGO, gpio_block_exported_show,
+	       gpio_block_exported_store),
+	__ATTR(ngpio, S_IRUGO, gpio_block_ngpio_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct class gpio_block_class = {
+	.name =         "gpioblock",
+	.owner =        THIS_MODULE,
+
+	.dev_attrs =  gpio_block_attrs,
+};
+
+int gpio_block_export(struct gpio_block *block)
+{
+	int		status = 0;
+	struct device	*dev;
+
+	/* can't export until sysfs is available ... */
+	if (!gpio_block_class.p) {
+		pr_debug("%s: called too early!\n", __func__);
+		return -ENOENT;
+	}
+
+	mutex_lock(&sysfs_lock);
+	dev = device_create(&gpio_block_class, NULL, MKDEV(0, 0), block,
+			    block->name);
+	if (IS_ERR(dev))
+		status = PTR_ERR(dev);
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_block_export);
+
+void gpio_block_unexport(struct gpio_block *block)
+{
+	struct device *dev;
+
+	mutex_lock(&sysfs_lock);
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (dev)
+		device_unregister(dev);
+	mutex_unlock(&sysfs_lock);
+}
+EXPORT_SYMBOL_GPL(gpio_block_unexport);
+
 static int __init gpiolib_sysfs_init(void)
 {
 	int		status;
@@ -1030,6 +1265,10 @@ static int __init gpiolib_sysfs_init(voi
 	if (status < 0)
 		return status;
 
+	status = class_register(&gpio_block_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).
 	 *
@@ -1843,6 +2082,7 @@ struct gpio_block *gpio_block_create(uns
 
 	block->name = name;
 	block->ngpio = size;
+	block->cur_mask = ~0;
 	INIT_LIST_HEAD(&block->gbc_list);
 	block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
 	if (!block->gpio)
@@ -2005,12 +2245,21 @@ EXPORT_SYMBOL_GPL(gpio_block_find_by_nam
 
 int gpio_block_register(struct gpio_block *block)
 {
+	int ret;
+
 	if (gpio_block_find_by_name(block->name))
 		return -EBUSY;
 
 	list_add(&block->list, &gpio_block_list);
 
+	ret = gpio_block_export(block);
+	if (ret)
+		goto err1;
+
 	return 0;
+err1:
+	list_del(&block->list);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(gpio_block_register);
 
@@ -2021,6 +2270,7 @@ void gpio_block_unregister(struct gpio_b
 	list_for_each_entry(i, &gpio_block_list, list)
 		if (i == block) {
 			list_del(&i->list);
+			gpio_block_unexport(block);
 			break;
 		}
 }
--- linux-2.6.orig/include/asm-generic/gpio.h
+++ linux-2.6/include/asm-generic/gpio.h
@@ -226,6 +226,8 @@ extern int gpio_export_link(struct devic
 			unsigned gpio);
 extern int gpio_sysfs_set_active_low(unsigned gpio, int value);
 extern void gpio_unexport(unsigned gpio);
+extern int gpio_block_export(struct gpio_block *block);
+extern void gpio_block_unexport(struct gpio_block *block);
 
 #endif	/* CONFIG_GPIO_SYSFS */
 
@@ -285,6 +287,15 @@ static inline int gpio_sysfs_set_active_
 static inline void gpio_unexport(unsigned gpio)
 {
 }
+
+static inline int gpio_block_export(struct gpio_block *block)
+{
+	return -ENOSYS;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+}
 #endif	/* CONFIG_GPIO_SYSFS */
 
 #ifdef CONFIG_PINCTRL
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -88,6 +88,7 @@ struct gpio_block_chip {
  * @ngpio:      number of gpios in this block
  * @gpio:       list of gpios in this block
  * @list:       global list of blocks, maintained by gpiolib
+ * @cur_mask:   currently used gpio mask used by userspace API
  */
 struct gpio_block {
 	struct list_head	gbc_list;
@@ -97,6 +98,7 @@ struct gpio_block {
 	unsigned		*gpio;
 
 	struct list_head	list;
+	unsigned long		cur_mask;
 };
 
 #ifdef CONFIG_GENERIC_GPIO
@@ -314,6 +316,19 @@ static inline void gpio_unexport(unsigne
 	WARN_ON(1);
 }
 
+static inline int gpio_block_export(struct gpio_block *block)
+{
+	/* GPIO block can never have been requested or set as {in,out}put */
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+	/* GPIO block can never have been exported */
+	WARN_ON(1);
+}
+
 static inline int gpio_to_irq(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as input */

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 3/6 v13] gpio: Add userland device interface to block GPIO
  2013-01-15 11:51 ` Roland Stigge
@ 2013-01-15 11:51   ` Roland Stigge
  -1 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: gregkh, grant.likely, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, highguy, broonie,
	daniel-gl, rmallon, sr, wg, tru, mark.rutland
  Cc: Roland Stigge

This patch adds a character device interface to the block GPIO system.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---
 Documentation/ABI/testing/dev-gpioblock |   34 ++++
 drivers/gpio/gpiolib.c                  |  225 +++++++++++++++++++++++++++++++-
 include/linux/gpio.h                    |   13 +
 3 files changed, 271 insertions(+), 1 deletion(-)

--- /dev/null
+++ linux-2.6/Documentation/ABI/testing/dev-gpioblock
@@ -0,0 +1,34 @@
+What:		/dev/<gpioblock>
+Date:		Nov 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:	The /dev/<gpioblock> character device node provides userspace
+		access to GPIO blocks, named exactly as the block, e.g.
+		/dev/block0.
+
+		Reading:
+		When reading sizeof(unsigned long) bytes from the device, the
+		current state of the block, masked by the current mask (see
+		below) can be obtained as a word. When the device is opened
+		with O_NONBLOCK, read() always returns with data immediately,
+		otherwise it blocks until data is available, see IRQ handling
+		below.
+
+		Writing:
+		By writing sizeof(unsigned long) bytes to the device, the
+		current state of the block can be set. This operation is
+		masked by the current mask (see below).
+
+		IRQ handling:
+		When one or more IRQs in the block are IRQ capable, you can
+		poll() on the device to check/wait for this IRQ. If no IRQ
+		is available, poll() returns -ENOSYS and userspace needs to
+		(busy) poll itself if necessary.
+
+		Setting the mask (default: all bits set):
+		By doing an ioctl(fd, 0, &mask) with an unsigned long mask, the
+		current mask for read and write operations on this gpio block
+		can be set.
+
+		See also Documentation/gpio.txt for an explanation of block
+		GPIO.
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -11,6 +11,8 @@
 #include <linux/of_gpio.h>
 #include <linux/idr.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/gpio.h>
@@ -2243,6 +2245,207 @@ struct gpio_block *gpio_block_find_by_na
 }
 EXPORT_SYMBOL_GPL(gpio_block_find_by_name);
 
+static struct gpio_block *gpio_block_find_by_minor(int minor)
+{
+	struct gpio_block *i;
+
+	list_for_each_entry(i, &gpio_block_list, list)
+		if (i->miscdev.minor == minor)
+			return i;
+	return NULL;
+}
+
+static bool gpio_block_is_irq_duplicate(struct gpio_block *block, int index)
+{
+	int irq = gpio_to_irq(block->gpio[index]);
+	int i;
+
+	for (i = 0; i < index; i++)
+		if (gpio_to_irq(block->gpio[i]) == irq)
+			return true;
+	return false;
+}
+
+static irqreturn_t gpio_block_irq_handler(int irq, void *dev)
+{
+	struct gpio_block *block = dev;
+
+	wake_up_interruptible(&block->wait_queue);
+	block->got_int = true;
+
+	return IRQ_HANDLED;
+}
+
+static int gpio_block_fop_open(struct inode *in, struct file *f)
+{
+	int i;
+	struct gpio_block *block = gpio_block_find_by_minor(MINOR(in->i_rdev));
+	int status;
+	int irq;
+
+	if (!block)
+		return -ENOENT;
+
+	block->irq_controlled = false;
+	block->got_int = false;
+	spin_lock_init(&block->lock);
+	init_waitqueue_head(&block->wait_queue);
+	f->private_data = block;
+
+	for (i = 0; i < block->ngpio; i++) {
+		status = gpio_request(block->gpio[i], block->name);
+		if (status)
+			goto err1;
+
+		irq = gpio_to_irq(block->gpio[i]);
+		if (irq >= 0 &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
+		    !gpio_block_is_irq_duplicate(block, i)) {
+			status = request_irq(irq, gpio_block_irq_handler,
+					     IRQF_SHARED,
+					     block->name, block);
+			if (status)
+				goto err2;
+
+			block->irq_controlled = true;
+		}
+	}
+
+	return 0;
+
+err1:
+	while (i > 0) {
+		i--;
+
+		irq = gpio_to_irq(block->gpio[i]);
+		if (irq >= 0 &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
+		    !gpio_block_is_irq_duplicate(block, i))
+			free_irq(irq, block);
+err2:
+		gpio_free(block->gpio[i]);
+	}
+	return status;
+}
+
+static int gpio_block_fop_release(struct inode *in, struct file *f)
+{
+	int i;
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+
+	for (i = 0; i < block->ngpio; i++) {
+		int irq = gpio_to_irq(block->gpio[i]);
+
+		if (irq >= 0 &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
+		    !gpio_block_is_irq_duplicate(block, i))
+			free_irq(irq, block);
+
+		gpio_free(block->gpio[i]);
+	}
+
+	return 0;
+}
+
+static int got_int(struct gpio_block *block)
+{
+	unsigned long flags;
+	int result;
+
+	spin_lock_irqsave(&block->lock, flags);
+	result = block->got_int;
+	spin_unlock_irqrestore(&block->lock, flags);
+
+	return result;
+}
+
+static ssize_t gpio_block_fop_read(struct file *f, char __user *buf, size_t n,
+				   loff_t *offset)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+	int err;
+	unsigned long flags;
+
+	if (block->irq_controlled) {
+		if (!(f->f_flags & O_NONBLOCK))
+			wait_event_interruptible(block->wait_queue,
+						 got_int(block));
+		spin_lock_irqsave(&block->lock, flags);
+		block->got_int = 0;
+		spin_unlock_irqrestore(&block->lock, flags);
+	}
+
+	if (n >= sizeof(unsigned long)) {
+		unsigned long values = gpio_block_get(block, block->cur_mask);
+
+		err = put_user(values, (unsigned long __user *)buf);
+		if (err)
+			return err;
+
+		return sizeof(unsigned long);
+	}
+	return 0;
+}
+
+static ssize_t gpio_block_fop_write(struct file *f, const char __user *buf,
+				    size_t n, loff_t *offset)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+	int err;
+
+	if (n >= sizeof(unsigned long)) {
+		unsigned long values;
+
+		err = get_user(values, (unsigned long __user *)buf);
+		if (err)
+			return err;
+		if (gpio_block_is_output(block))
+			gpio_block_set(block, block->cur_mask, values);
+		else
+			return -EPERM;
+		return sizeof(unsigned long);
+	}
+	return 0;
+}
+
+static long gpio_block_fop_ioctl(struct file *f, unsigned int cmd,
+				 unsigned long arg)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+	unsigned long __user *x = (unsigned long __user *)arg;
+
+	if (cmd == 0)
+		return get_user(block->cur_mask, x);
+
+	return -EINVAL;
+}
+
+static unsigned int gpio_block_fop_poll(struct file *f,
+					struct poll_table_struct *pt)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+
+	if (!block->irq_controlled)
+		return -ENOSYS;
+
+	if (!got_int(block))
+		poll_wait(f, &block->wait_queue, pt);
+
+	if (got_int(block))
+		return POLLIN;
+
+	return 0;
+}
+
+static const struct file_operations gpio_block_fops = {
+	.open = gpio_block_fop_open,
+	.release = gpio_block_fop_release,
+	.read = gpio_block_fop_read,
+	.write = gpio_block_fop_write,
+	.unlocked_ioctl = gpio_block_fop_ioctl,
+	.poll = gpio_block_fop_poll,
+};
+
 int gpio_block_register(struct gpio_block *block)
 {
 	int ret;
@@ -2253,12 +2456,31 @@ int gpio_block_register(struct gpio_bloc
 	list_add(&block->list, &gpio_block_list);
 
 	ret = gpio_block_export(block);
-	if (ret)
+
+	/*
+	 * ret == ENOSYS is the case where GPIO_SYSFS is deactivated. In this
+	 * case, we can continue safely anyway, since we can provide the device
+	 * interface.
+	 */
+	if (ret && ret != -ENOSYS)
 		goto err1;
 
+	block->miscdev.name = block->name;
+	block->miscdev.nodename = block->name;
+	block->miscdev.minor = MISC_DYNAMIC_MINOR;
+	block->miscdev.fops = &gpio_block_fops;
+	block->miscdev.mode = S_IWUSR | S_IRUGO;
+
+	ret = misc_register(&block->miscdev);
+	if (ret)
+		goto err2;
+
 	return 0;
+err2:
+	gpio_block_unexport(block);
 err1:
 	list_del(&block->list);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(gpio_block_register);
@@ -2271,6 +2493,7 @@ void gpio_block_unregister(struct gpio_b
 		if (i == block) {
 			list_del(&i->list);
 			gpio_block_unexport(block);
+			misc_deregister(&block->miscdev);
 			break;
 		}
 }
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -4,6 +4,9 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
 
 /* see Documentation/gpio.txt */
 
@@ -89,6 +92,11 @@ struct gpio_block_chip {
  * @gpio:       list of gpios in this block
  * @list:       global list of blocks, maintained by gpiolib
  * @cur_mask:   currently used gpio mask used by userspace API
+ * @miscdev:    userspace API: device
+ * @wait_queue: userspace API: wait queue waiting for IRQ
+ * @irq_controlled: userspace API: flag: using IRQ or not
+ * @got_int:    userspace API: change detection via IRQ
+ * @lock:	userspace API: spinlock for IRQ manipulated data
  */
 struct gpio_block {
 	struct list_head	gbc_list;
@@ -99,6 +107,11 @@ struct gpio_block {
 
 	struct list_head	list;
 	unsigned long		cur_mask;
+	struct miscdevice	miscdev;
+	wait_queue_head_t	wait_queue;
+	bool			irq_controlled;
+	bool			got_int;
+	spinlock_t		lock;
 };
 
 #ifdef CONFIG_GENERIC_GPIO

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 3/6 v13] gpio: Add userland device interface to block GPIO
@ 2013-01-15 11:51   ` Roland Stigge
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds a character device interface to the block GPIO system.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---
 Documentation/ABI/testing/dev-gpioblock |   34 ++++
 drivers/gpio/gpiolib.c                  |  225 +++++++++++++++++++++++++++++++-
 include/linux/gpio.h                    |   13 +
 3 files changed, 271 insertions(+), 1 deletion(-)

--- /dev/null
+++ linux-2.6/Documentation/ABI/testing/dev-gpioblock
@@ -0,0 +1,34 @@
+What:		/dev/<gpioblock>
+Date:		Nov 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:	The /dev/<gpioblock> character device node provides userspace
+		access to GPIO blocks, named exactly as the block, e.g.
+		/dev/block0.
+
+		Reading:
+		When reading sizeof(unsigned long) bytes from the device, the
+		current state of the block, masked by the current mask (see
+		below) can be obtained as a word. When the device is opened
+		with O_NONBLOCK, read() always returns with data immediately,
+		otherwise it blocks until data is available, see IRQ handling
+		below.
+
+		Writing:
+		By writing sizeof(unsigned long) bytes to the device, the
+		current state of the block can be set. This operation is
+		masked by the current mask (see below).
+
+		IRQ handling:
+		When one or more IRQs in the block are IRQ capable, you can
+		poll() on the device to check/wait for this IRQ. If no IRQ
+		is available, poll() returns -ENOSYS and userspace needs to
+		(busy) poll itself if necessary.
+
+		Setting the mask (default: all bits set):
+		By doing an ioctl(fd, 0, &mask) with an unsigned long mask, the
+		current mask for read and write operations on this gpio block
+		can be set.
+
+		See also Documentation/gpio.txt for an explanation of block
+		GPIO.
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -11,6 +11,8 @@
 #include <linux/of_gpio.h>
 #include <linux/idr.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/gpio.h>
@@ -2243,6 +2245,207 @@ struct gpio_block *gpio_block_find_by_na
 }
 EXPORT_SYMBOL_GPL(gpio_block_find_by_name);
 
+static struct gpio_block *gpio_block_find_by_minor(int minor)
+{
+	struct gpio_block *i;
+
+	list_for_each_entry(i, &gpio_block_list, list)
+		if (i->miscdev.minor == minor)
+			return i;
+	return NULL;
+}
+
+static bool gpio_block_is_irq_duplicate(struct gpio_block *block, int index)
+{
+	int irq = gpio_to_irq(block->gpio[index]);
+	int i;
+
+	for (i = 0; i < index; i++)
+		if (gpio_to_irq(block->gpio[i]) == irq)
+			return true;
+	return false;
+}
+
+static irqreturn_t gpio_block_irq_handler(int irq, void *dev)
+{
+	struct gpio_block *block = dev;
+
+	wake_up_interruptible(&block->wait_queue);
+	block->got_int = true;
+
+	return IRQ_HANDLED;
+}
+
+static int gpio_block_fop_open(struct inode *in, struct file *f)
+{
+	int i;
+	struct gpio_block *block = gpio_block_find_by_minor(MINOR(in->i_rdev));
+	int status;
+	int irq;
+
+	if (!block)
+		return -ENOENT;
+
+	block->irq_controlled = false;
+	block->got_int = false;
+	spin_lock_init(&block->lock);
+	init_waitqueue_head(&block->wait_queue);
+	f->private_data = block;
+
+	for (i = 0; i < block->ngpio; i++) {
+		status = gpio_request(block->gpio[i], block->name);
+		if (status)
+			goto err1;
+
+		irq = gpio_to_irq(block->gpio[i]);
+		if (irq >= 0 &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
+		    !gpio_block_is_irq_duplicate(block, i)) {
+			status = request_irq(irq, gpio_block_irq_handler,
+					     IRQF_SHARED,
+					     block->name, block);
+			if (status)
+				goto err2;
+
+			block->irq_controlled = true;
+		}
+	}
+
+	return 0;
+
+err1:
+	while (i > 0) {
+		i--;
+
+		irq = gpio_to_irq(block->gpio[i]);
+		if (irq >= 0 &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
+		    !gpio_block_is_irq_duplicate(block, i))
+			free_irq(irq, block);
+err2:
+		gpio_free(block->gpio[i]);
+	}
+	return status;
+}
+
+static int gpio_block_fop_release(struct inode *in, struct file *f)
+{
+	int i;
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+
+	for (i = 0; i < block->ngpio; i++) {
+		int irq = gpio_to_irq(block->gpio[i]);
+
+		if (irq >= 0 &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
+		    !gpio_block_is_irq_duplicate(block, i))
+			free_irq(irq, block);
+
+		gpio_free(block->gpio[i]);
+	}
+
+	return 0;
+}
+
+static int got_int(struct gpio_block *block)
+{
+	unsigned long flags;
+	int result;
+
+	spin_lock_irqsave(&block->lock, flags);
+	result = block->got_int;
+	spin_unlock_irqrestore(&block->lock, flags);
+
+	return result;
+}
+
+static ssize_t gpio_block_fop_read(struct file *f, char __user *buf, size_t n,
+				   loff_t *offset)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+	int err;
+	unsigned long flags;
+
+	if (block->irq_controlled) {
+		if (!(f->f_flags & O_NONBLOCK))
+			wait_event_interruptible(block->wait_queue,
+						 got_int(block));
+		spin_lock_irqsave(&block->lock, flags);
+		block->got_int = 0;
+		spin_unlock_irqrestore(&block->lock, flags);
+	}
+
+	if (n >= sizeof(unsigned long)) {
+		unsigned long values = gpio_block_get(block, block->cur_mask);
+
+		err = put_user(values, (unsigned long __user *)buf);
+		if (err)
+			return err;
+
+		return sizeof(unsigned long);
+	}
+	return 0;
+}
+
+static ssize_t gpio_block_fop_write(struct file *f, const char __user *buf,
+				    size_t n, loff_t *offset)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+	int err;
+
+	if (n >= sizeof(unsigned long)) {
+		unsigned long values;
+
+		err = get_user(values, (unsigned long __user *)buf);
+		if (err)
+			return err;
+		if (gpio_block_is_output(block))
+			gpio_block_set(block, block->cur_mask, values);
+		else
+			return -EPERM;
+		return sizeof(unsigned long);
+	}
+	return 0;
+}
+
+static long gpio_block_fop_ioctl(struct file *f, unsigned int cmd,
+				 unsigned long arg)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+	unsigned long __user *x = (unsigned long __user *)arg;
+
+	if (cmd == 0)
+		return get_user(block->cur_mask, x);
+
+	return -EINVAL;
+}
+
+static unsigned int gpio_block_fop_poll(struct file *f,
+					struct poll_table_struct *pt)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+
+	if (!block->irq_controlled)
+		return -ENOSYS;
+
+	if (!got_int(block))
+		poll_wait(f, &block->wait_queue, pt);
+
+	if (got_int(block))
+		return POLLIN;
+
+	return 0;
+}
+
+static const struct file_operations gpio_block_fops = {
+	.open = gpio_block_fop_open,
+	.release = gpio_block_fop_release,
+	.read = gpio_block_fop_read,
+	.write = gpio_block_fop_write,
+	.unlocked_ioctl = gpio_block_fop_ioctl,
+	.poll = gpio_block_fop_poll,
+};
+
 int gpio_block_register(struct gpio_block *block)
 {
 	int ret;
@@ -2253,12 +2456,31 @@ int gpio_block_register(struct gpio_bloc
 	list_add(&block->list, &gpio_block_list);
 
 	ret = gpio_block_export(block);
-	if (ret)
+
+	/*
+	 * ret == ENOSYS is the case where GPIO_SYSFS is deactivated. In this
+	 * case, we can continue safely anyway, since we can provide the device
+	 * interface.
+	 */
+	if (ret && ret != -ENOSYS)
 		goto err1;
 
+	block->miscdev.name = block->name;
+	block->miscdev.nodename = block->name;
+	block->miscdev.minor = MISC_DYNAMIC_MINOR;
+	block->miscdev.fops = &gpio_block_fops;
+	block->miscdev.mode = S_IWUSR | S_IRUGO;
+
+	ret = misc_register(&block->miscdev);
+	if (ret)
+		goto err2;
+
 	return 0;
+err2:
+	gpio_block_unexport(block);
 err1:
 	list_del(&block->list);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(gpio_block_register);
@@ -2271,6 +2493,7 @@ void gpio_block_unregister(struct gpio_b
 		if (i == block) {
 			list_del(&i->list);
 			gpio_block_unexport(block);
+			misc_deregister(&block->miscdev);
 			break;
 		}
 }
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -4,6 +4,9 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
 
 /* see Documentation/gpio.txt */
 
@@ -89,6 +92,11 @@ struct gpio_block_chip {
  * @gpio:       list of gpios in this block
  * @list:       global list of blocks, maintained by gpiolib
  * @cur_mask:   currently used gpio mask used by userspace API
+ * @miscdev:    userspace API: device
+ * @wait_queue: userspace API: wait queue waiting for IRQ
+ * @irq_controlled: userspace API: flag: using IRQ or not
+ * @got_int:    userspace API: change detection via IRQ
+ * @lock:	userspace API: spinlock for IRQ manipulated data
  */
 struct gpio_block {
 	struct list_head	gbc_list;
@@ -99,6 +107,11 @@ struct gpio_block {
 
 	struct list_head	list;
 	unsigned long		cur_mask;
+	struct miscdevice	miscdev;
+	wait_queue_head_t	wait_queue;
+	bool			irq_controlled;
+	bool			got_int;
+	spinlock_t		lock;
 };
 
 #ifdef CONFIG_GENERIC_GPIO

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 4/6 v13] gpiolib: Fix default attributes for class
  2013-01-15 11:51 ` Roland Stigge
@ 2013-01-15 11:51   ` Roland Stigge
  -1 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: gregkh, grant.likely, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, highguy, broonie,
	daniel-gl, rmallon, sr, wg, tru, mark.rutland
  Cc: Roland Stigge

There is a race condition between creating a gpio or gpiochip device and adding
default attributes. This patch fixes this by defining the default attributes as
dev_attrs of the class. For this, it was necessary to create a separate
gpiochip_class besides gpio_class.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---
 Documentation/ABI/testing/sysfs-gpio |   11 ++++
 drivers/gpio/gpiolib.c               |   78 ++++++++++++++---------------------
 2 files changed, 44 insertions(+), 45 deletions(-)

--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -20,6 +20,17 @@ 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
+
+What:		/sys/class/gpiochip/
+Date:		October 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:
+
+  Each gpiochip is represented by a separate device having the following
+  attributes:
+
+    /sys/class/gpiochip
 	/gpiochipN ... for each gpiochip; #N is its first GPIO
 	    /base ... (r/o) same as N
 	    /label ... (r/o) descriptive, not necessarily unique
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -362,9 +362,6 @@ static ssize_t gpio_value_store(struct d
 	return status;
 }
 
-static const DEVICE_ATTR(value, 0644,
-		gpio_value_show, gpio_value_store);
-
 static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
 {
 	struct sysfs_dirent	*value_sd = priv;
@@ -585,19 +582,6 @@ static ssize_t gpio_active_low_store(str
 	return status ? : size;
 }
 
-static const DEVICE_ATTR(active_low, 0644,
-		gpio_active_low_show, gpio_active_low_store);
-
-static const struct attribute *gpio_attrs[] = {
-	&dev_attr_value.attr,
-	&dev_attr_active_low.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)
@@ -612,7 +596,6 @@ static ssize_t chip_base_show(struct 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)
@@ -621,7 +604,6 @@ static ssize_t chip_label_show(struct de
 
 	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)
@@ -630,18 +612,6 @@ static ssize_t chip_ngpio_show(struct de
 
 	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
@@ -720,11 +690,32 @@ static struct class_attribute gpio_class
 	__ATTR_NULL,
 };
 
+static struct device_attribute gpio_attrs[] = {
+	__ATTR(active_low, 0644, gpio_active_low_show, gpio_active_low_store),
+	__ATTR(value, 0644, gpio_value_show, gpio_value_store),
+	__ATTR_NULL,
+};
+
 static struct class gpio_class = {
 	.name =		"gpio",
 	.owner =	THIS_MODULE,
 
-	.class_attrs =	gpio_class_attrs,
+	.class_attrs = gpio_class_attrs,
+	.dev_attrs = gpio_attrs,
+};
+
+static struct device_attribute gpiochip_attrs[] = {
+	__ATTR(label, 0444, chip_label_show, NULL),
+	__ATTR(base, 0444, chip_base_show, NULL),
+	__ATTR(ngpio, 0444, chip_ngpio_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct class gpiochip_class = {
+	.name =		"gpiochip",
+	.owner =	THIS_MODULE,
+
+	.dev_attrs =	gpiochip_attrs,
 };
 
 
@@ -791,10 +782,6 @@ int gpio_export(unsigned gpio, bool dire
 		goto fail_unlock;
 	}
 
-	status = sysfs_create_group(&dev->kobj, &gpio_attr_group);
-	if (status)
-		goto fail_unregister_device;
-
 	if (direction_may_change) {
 		status = device_create_file(dev, &dev_attr_direction);
 		if (status)
@@ -962,25 +949,22 @@ EXPORT_SYMBOL_GPL(gpio_unexport);
 
 static int gpiochip_export(struct gpio_chip *chip)
 {
-	int		status;
+	int		status = 0;
 	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.
+	 * verify that _some_ field of gpiochip_class got initialized.
 	 */
-	if (!gpio_class.p)
+	if (!gpiochip_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 (!IS_ERR(dev)) {
-		status = sysfs_create_group(&dev->kobj,
-				&gpiochip_attr_group);
-	} else
+	dev = device_create(&gpiochip_class, chip->dev, MKDEV(0, 0), chip,
+			    "gpiochip%d", chip->base);
+	if (IS_ERR(dev))
 		status = PTR_ERR(dev);
 	chip->exported = (status == 0);
 	mutex_unlock(&sysfs_lock);
@@ -1008,7 +992,7 @@ static void gpiochip_unexport(struct gpi
 	struct device		*dev;
 
 	mutex_lock(&sysfs_lock);
-	dev = class_find_device(&gpio_class, NULL, chip, match_export);
+	dev = class_find_device(&gpiochip_class, NULL, chip, match_export);
 	if (dev) {
 		put_device(dev);
 		device_unregister(dev);
@@ -1267,6 +1251,10 @@ static int __init gpiolib_sysfs_init(voi
 	if (status < 0)
 		return status;
 
+	status = class_register(&gpiochip_class);
+	if (status < 0)
+		return status;
+
 	status = class_register(&gpio_block_class);
 	if (status < 0)
 		return status;

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 4/6 v13] gpiolib: Fix default attributes for class
@ 2013-01-15 11:51   ` Roland Stigge
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel

There is a race condition between creating a gpio or gpiochip device and adding
default attributes. This patch fixes this by defining the default attributes as
dev_attrs of the class. For this, it was necessary to create a separate
gpiochip_class besides gpio_class.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---
 Documentation/ABI/testing/sysfs-gpio |   11 ++++
 drivers/gpio/gpiolib.c               |   78 ++++++++++++++---------------------
 2 files changed, 44 insertions(+), 45 deletions(-)

--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -20,6 +20,17 @@ 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
+
+What:		/sys/class/gpiochip/
+Date:		October 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:
+
+  Each gpiochip is represented by a separate device having the following
+  attributes:
+
+    /sys/class/gpiochip
 	/gpiochipN ... for each gpiochip; #N is its first GPIO
 	    /base ... (r/o) same as N
 	    /label ... (r/o) descriptive, not necessarily unique
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -362,9 +362,6 @@ static ssize_t gpio_value_store(struct d
 	return status;
 }
 
-static const DEVICE_ATTR(value, 0644,
-		gpio_value_show, gpio_value_store);
-
 static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
 {
 	struct sysfs_dirent	*value_sd = priv;
@@ -585,19 +582,6 @@ static ssize_t gpio_active_low_store(str
 	return status ? : size;
 }
 
-static const DEVICE_ATTR(active_low, 0644,
-		gpio_active_low_show, gpio_active_low_store);
-
-static const struct attribute *gpio_attrs[] = {
-	&dev_attr_value.attr,
-	&dev_attr_active_low.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)
@@ -612,7 +596,6 @@ static ssize_t chip_base_show(struct 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)
@@ -621,7 +604,6 @@ static ssize_t chip_label_show(struct de
 
 	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)
@@ -630,18 +612,6 @@ static ssize_t chip_ngpio_show(struct de
 
 	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
@@ -720,11 +690,32 @@ static struct class_attribute gpio_class
 	__ATTR_NULL,
 };
 
+static struct device_attribute gpio_attrs[] = {
+	__ATTR(active_low, 0644, gpio_active_low_show, gpio_active_low_store),
+	__ATTR(value, 0644, gpio_value_show, gpio_value_store),
+	__ATTR_NULL,
+};
+
 static struct class gpio_class = {
 	.name =		"gpio",
 	.owner =	THIS_MODULE,
 
-	.class_attrs =	gpio_class_attrs,
+	.class_attrs = gpio_class_attrs,
+	.dev_attrs = gpio_attrs,
+};
+
+static struct device_attribute gpiochip_attrs[] = {
+	__ATTR(label, 0444, chip_label_show, NULL),
+	__ATTR(base, 0444, chip_base_show, NULL),
+	__ATTR(ngpio, 0444, chip_ngpio_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct class gpiochip_class = {
+	.name =		"gpiochip",
+	.owner =	THIS_MODULE,
+
+	.dev_attrs =	gpiochip_attrs,
 };
 
 
@@ -791,10 +782,6 @@ int gpio_export(unsigned gpio, bool dire
 		goto fail_unlock;
 	}
 
-	status = sysfs_create_group(&dev->kobj, &gpio_attr_group);
-	if (status)
-		goto fail_unregister_device;
-
 	if (direction_may_change) {
 		status = device_create_file(dev, &dev_attr_direction);
 		if (status)
@@ -962,25 +949,22 @@ EXPORT_SYMBOL_GPL(gpio_unexport);
 
 static int gpiochip_export(struct gpio_chip *chip)
 {
-	int		status;
+	int		status = 0;
 	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.
+	 * verify that _some_ field of gpiochip_class got initialized.
 	 */
-	if (!gpio_class.p)
+	if (!gpiochip_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 (!IS_ERR(dev)) {
-		status = sysfs_create_group(&dev->kobj,
-				&gpiochip_attr_group);
-	} else
+	dev = device_create(&gpiochip_class, chip->dev, MKDEV(0, 0), chip,
+			    "gpiochip%d", chip->base);
+	if (IS_ERR(dev))
 		status = PTR_ERR(dev);
 	chip->exported = (status == 0);
 	mutex_unlock(&sysfs_lock);
@@ -1008,7 +992,7 @@ static void gpiochip_unexport(struct gpi
 	struct device		*dev;
 
 	mutex_lock(&sysfs_lock);
-	dev = class_find_device(&gpio_class, NULL, chip, match_export);
+	dev = class_find_device(&gpiochip_class, NULL, chip, match_export);
 	if (dev) {
 		put_device(dev);
 		device_unregister(dev);
@@ -1267,6 +1251,10 @@ static int __init gpiolib_sysfs_init(voi
 	if (status < 0)
 		return status;
 
+	status = class_register(&gpiochip_class);
+	if (status < 0)
+		return status;
+
 	status = class_register(&gpio_block_class);
 	if (status < 0)
 		return status;

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 5/6 v13] gpio: Add device tree support to block GPIO API
  2013-01-15 11:51 ` Roland Stigge
@ 2013-01-15 11:51   ` Roland Stigge
  -1 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: gregkh, grant.likely, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, highguy, broonie,
	daniel-gl, rmallon, sr, wg, tru, mark.rutland
  Cc: Roland Stigge

This patch adds device tree support to the block GPIO API.

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 Documentation/devicetree/bindings/gpio/gpio-block.txt |   36 ++++++
 drivers/gpio/Makefile                                 |    1 
 drivers/gpio/gpioblock-of.c                           |  100 ++++++++++++++++++
 3 files changed, 137 insertions(+)

--- /dev/null
+++ linux-2.6/Documentation/devicetree/bindings/gpio/gpio-block.txt
@@ -0,0 +1,36 @@
+Block GPIO definition
+=====================
+
+This binding specifies arbitrary blocks of gpios, combining gpios from one or
+more GPIO controllers together, to form a word for r/w access.
+
+Required property:
+- compatible: must be "linux,gpio-block"
+
+Required subnodes:
+- the name will be the registered name of the block
+- property "gpios" is a list of gpios for the respective block
+
+Example:
+
+        blockgpio {
+                compatible = "linux,gpio-block";
+
+                block0 {
+                        gpios = <&gpio 3 0 0>,
+                                <&gpio 3 1 0>;
+                };
+                block1 {
+                        gpios = <&gpio 4 1 0>,
+                                <&gpio 4 3 0>,
+                                <&gpio 4 2 0>,
+                                <&gpio 4 4 0>,
+                                <&gpio 4 5 0>,
+                                <&gpio 4 6 0>,
+                                <&gpio 4 7 0>,
+                                <&gpio 4 8 0>,
+                                <&gpio 4 9 0>,
+                                <&gpio 4 10 0>,
+                                <&gpio 4 19 0>;
+                };
+        };
--- linux-2.6.orig/drivers/gpio/Makefile
+++ linux-2.6/drivers/gpio/Makefile
@@ -5,6 +5,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG
 obj-$(CONFIG_GPIOLIB)		+= gpiolib.o devres.o
 obj-$(CONFIG_OF_GPIO)		+= gpiolib-of.o
 obj-$(CONFIG_GPIO_ACPI)		+= gpiolib-acpi.o
+obj-$(CONFIG_OF_GPIO)		+= gpioblock-of.o
 
 # Device drivers. Generally keep list sorted alphabetically
 obj-$(CONFIG_GPIO_GENERIC)	+= gpio-generic.o
--- /dev/null
+++ linux-2.6/drivers/gpio/gpioblock-of.c
@@ -0,0 +1,100 @@
+/*
+ * OF implementation for Block GPIO
+ *
+ * Copyright (C) 2012 Roland Stigge <stigge@antcom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+static int __devinit gpioblock_of_probe(struct platform_device *pdev)
+{
+	struct device_node *block;
+	unsigned *gpios;
+	int ngpio;
+	int ret;
+	struct gpio_block *gb;
+
+	for_each_available_child_of_node(pdev->dev.of_node, block) {
+		int i;
+
+		ngpio = of_gpio_count(block);
+		if (ngpio < 1) {
+			ret = -ENODEV;
+			goto err1;
+		}
+
+		gpios = kzalloc(ngpio * sizeof(*gpios), GFP_KERNEL);
+		if (!gpios) {
+			ret = -ENOMEM;
+			goto err1;
+		}
+
+		for (i = 0; i < ngpio; i++) {
+			ret = of_get_gpio(block, i);
+			if (ret < 0)
+				goto err2; /* expect -EPROBE_DEFER */
+			gpios[i] = ret;
+		}
+		gb = gpio_block_create(gpios, ngpio, block->name);
+		if (IS_ERR(gb)) {
+			dev_err(&pdev->dev,
+				"Error creating GPIO block from device tree\n");
+			ret = PTR_ERR(gb);
+			goto err2;
+		}
+		ret = gpio_block_register(gb);
+		if (ret < 0)
+			goto err3;
+
+		kfree(gpios);
+		dev_info(&pdev->dev, "Registered gpio block %s: %d gpios\n",
+			 block->name, ngpio);
+	}
+	return 0;
+
+err3:
+	gpio_block_free(gb);
+err2:
+	kfree(gpios);
+err1:
+	of_node_put(block);
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id gpioblock_of_match[] __devinitdata = {
+	{ .compatible = "linux,gpio-block", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpioblock_of_match);
+#endif
+
+static struct platform_driver gpioblock_of_driver = {
+	.driver	= {
+		.name = "gpio-block",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(gpioblock_of_match),
+
+	},
+	.probe	= gpioblock_of_probe,
+};
+
+module_platform_driver(gpioblock_of_driver);
+
+MODULE_DESCRIPTION("GPIO Block driver");
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpioblock-of");

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 5/6 v13] gpio: Add device tree support to block GPIO API
@ 2013-01-15 11:51   ` Roland Stigge
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds device tree support to the block GPIO API.

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 Documentation/devicetree/bindings/gpio/gpio-block.txt |   36 ++++++
 drivers/gpio/Makefile                                 |    1 
 drivers/gpio/gpioblock-of.c                           |  100 ++++++++++++++++++
 3 files changed, 137 insertions(+)

--- /dev/null
+++ linux-2.6/Documentation/devicetree/bindings/gpio/gpio-block.txt
@@ -0,0 +1,36 @@
+Block GPIO definition
+=====================
+
+This binding specifies arbitrary blocks of gpios, combining gpios from one or
+more GPIO controllers together, to form a word for r/w access.
+
+Required property:
+- compatible: must be "linux,gpio-block"
+
+Required subnodes:
+- the name will be the registered name of the block
+- property "gpios" is a list of gpios for the respective block
+
+Example:
+
+        blockgpio {
+                compatible = "linux,gpio-block";
+
+                block0 {
+                        gpios = <&gpio 3 0 0>,
+                                <&gpio 3 1 0>;
+                };
+                block1 {
+                        gpios = <&gpio 4 1 0>,
+                                <&gpio 4 3 0>,
+                                <&gpio 4 2 0>,
+                                <&gpio 4 4 0>,
+                                <&gpio 4 5 0>,
+                                <&gpio 4 6 0>,
+                                <&gpio 4 7 0>,
+                                <&gpio 4 8 0>,
+                                <&gpio 4 9 0>,
+                                <&gpio 4 10 0>,
+                                <&gpio 4 19 0>;
+                };
+        };
--- linux-2.6.orig/drivers/gpio/Makefile
+++ linux-2.6/drivers/gpio/Makefile
@@ -5,6 +5,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG
 obj-$(CONFIG_GPIOLIB)		+= gpiolib.o devres.o
 obj-$(CONFIG_OF_GPIO)		+= gpiolib-of.o
 obj-$(CONFIG_GPIO_ACPI)		+= gpiolib-acpi.o
+obj-$(CONFIG_OF_GPIO)		+= gpioblock-of.o
 
 # Device drivers. Generally keep list sorted alphabetically
 obj-$(CONFIG_GPIO_GENERIC)	+= gpio-generic.o
--- /dev/null
+++ linux-2.6/drivers/gpio/gpioblock-of.c
@@ -0,0 +1,100 @@
+/*
+ * OF implementation for Block GPIO
+ *
+ * Copyright (C) 2012 Roland Stigge <stigge@antcom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+static int __devinit gpioblock_of_probe(struct platform_device *pdev)
+{
+	struct device_node *block;
+	unsigned *gpios;
+	int ngpio;
+	int ret;
+	struct gpio_block *gb;
+
+	for_each_available_child_of_node(pdev->dev.of_node, block) {
+		int i;
+
+		ngpio = of_gpio_count(block);
+		if (ngpio < 1) {
+			ret = -ENODEV;
+			goto err1;
+		}
+
+		gpios = kzalloc(ngpio * sizeof(*gpios), GFP_KERNEL);
+		if (!gpios) {
+			ret = -ENOMEM;
+			goto err1;
+		}
+
+		for (i = 0; i < ngpio; i++) {
+			ret = of_get_gpio(block, i);
+			if (ret < 0)
+				goto err2; /* expect -EPROBE_DEFER */
+			gpios[i] = ret;
+		}
+		gb = gpio_block_create(gpios, ngpio, block->name);
+		if (IS_ERR(gb)) {
+			dev_err(&pdev->dev,
+				"Error creating GPIO block from device tree\n");
+			ret = PTR_ERR(gb);
+			goto err2;
+		}
+		ret = gpio_block_register(gb);
+		if (ret < 0)
+			goto err3;
+
+		kfree(gpios);
+		dev_info(&pdev->dev, "Registered gpio block %s: %d gpios\n",
+			 block->name, ngpio);
+	}
+	return 0;
+
+err3:
+	gpio_block_free(gb);
+err2:
+	kfree(gpios);
+err1:
+	of_node_put(block);
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id gpioblock_of_match[] __devinitdata = {
+	{ .compatible = "linux,gpio-block", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpioblock_of_match);
+#endif
+
+static struct platform_driver gpioblock_of_driver = {
+	.driver	= {
+		.name = "gpio-block",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(gpioblock_of_match),
+
+	},
+	.probe	= gpioblock_of_probe,
+};
+
+module_platform_driver(gpioblock_of_driver);
+
+MODULE_DESCRIPTION("GPIO Block driver");
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpioblock-of");

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 6/6 v13] gpio: Add block gpio to several gpio drivers
  2013-01-15 11:51 ` Roland Stigge
@ 2013-01-15 11:51   ` Roland Stigge
  -1 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: gregkh, grant.likely, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, highguy, broonie,
	daniel-gl, rmallon, sr, wg, tru, mark.rutland
  Cc: Roland Stigge

This patch adds block GPIO support to several gpio drivers.

This implements block GPIO only for some selected drivers since block GPIO is
an optional feature which may not be suitable for every GPIO hardware. (With
automatic fallback to the single GPIO functions if not available in a driver.)

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 drivers/gpio/Kconfig              |    2 
 drivers/gpio/gpio-em.c            |   23 ++++++++++
 drivers/gpio/gpio-ge.c            |   29 +++++++++++++
 drivers/gpio/gpio-generic.c       |   56 +++++++++++++++++++++++++
 drivers/gpio/gpio-ks8695.c        |   34 +++++++++++++++
 drivers/gpio/gpio-lpc32xx.c       |   82 ++++++++++++++++++++++++++++++++++++++
 drivers/gpio/gpio-max730x.c       |   61 ++++++++++++++++++++++++++++
 drivers/gpio/gpio-max732x.c       |   59 +++++++++++++++++++++++++++
 drivers/gpio/gpio-mc33880.c       |   16 +++++++
 drivers/gpio/gpio-ml-ioh.c        |   27 ++++++++++++
 drivers/gpio/gpio-mm-lantiq.c     |   22 ++++++++++
 drivers/gpio/gpio-mpc5200.c       |   64 +++++++++++++++++++++++++++++
 drivers/gpio/gpio-mpc8xxx.c       |   41 +++++++++++++++++++
 drivers/gpio/gpio-pca953x.c       |   64 +++++++++++++++++++++++++++++
 drivers/gpio/gpio-pcf857x.c       |   24 +++++++++++
 drivers/gpio/gpio-pch.c           |   27 ++++++++++++
 drivers/gpio/gpio-pl061.c         |   17 +++++++
 drivers/gpio/gpio-sa1100.c        |   20 +++++++++
 drivers/gpio/gpio-samsung.c       |   31 ++++++++++++++
 drivers/gpio/gpio-twl6040.c       |   32 ++++++++++++++
 drivers/gpio/gpio-ucb1400.c       |   23 ++++++++++
 drivers/gpio/gpio-vt8500.c        |   24 +++++++++++
 drivers/gpio/gpio-xilinx.c        |   44 ++++++++++++++++++++
 drivers/pinctrl/pinctrl-at91.c    |   29 +++++++++++++
 drivers/pinctrl/pinctrl-nomadik.c |   36 ++++++++++++++++
 25 files changed, 887 insertions(+)

--- linux-2.6.orig/drivers/gpio/Kconfig
+++ linux-2.6/drivers/gpio/Kconfig
@@ -144,11 +144,13 @@ config GPIO_MM_LANTIQ
 config GPIO_MPC5200
 	def_bool y
 	depends on PPC_MPC52xx
+	select BITREVERSE
 
 config GPIO_MPC8XXX
 	bool "MPC512x/MPC8xxx GPIO support"
 	depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \
 		   FSL_SOC_BOOKE || PPC_86xx
+	select BITREVERSE
 	help
 	  Say Y here if you're going to use hardware that connects to the
 	  MPC512x/831x/834x/837x/8572/8610 GPIOs.
--- linux-2.6.orig/drivers/gpio/gpio-em.c
+++ linux-2.6/drivers/gpio/gpio-em.c
@@ -202,6 +202,27 @@ static void em_gio_set(struct gpio_chip
 		__em_gio_set(chip, GIO_OH, offset - 16, value);
 }
 
+static unsigned long em_gio_get_block(struct gpio_chip *chip,
+				      unsigned long mask)
+{
+	return (int)(em_gio_read(gpio_to_priv(chip), GIO_I) & mask);
+}
+
+static void em_gio_set_block(struct gpio_chip *chip, unsigned long mask,
+			     unsigned long values)
+{
+	unsigned long mask_ol = mask & 0xFFFF;
+	unsigned long mask_oh = mask >> 16;
+
+	unsigned long values_ol = values & mask_ol;
+	unsigned long values_oh = (values >> 16) & mask_oh;
+
+	em_gio_write(gpio_to_priv(chip), GIO_OL,
+		     mask_ol << 16 | values_ol);
+	em_gio_write(gpio_to_priv(chip), GIO_OH,
+		     mask_oh << 16 | values_oh);
+}
+
 static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset,
 				   int value)
 {
@@ -282,8 +303,10 @@ static int em_gio_probe(struct platform_
 	gpio_chip = &p->gpio_chip;
 	gpio_chip->direction_input = em_gio_direction_input;
 	gpio_chip->get = em_gio_get;
+	gpio_chip->get_block = em_gio_get_block;
 	gpio_chip->direction_output = em_gio_direction_output;
 	gpio_chip->set = em_gio_set;
+	gpio_chip->set_block = em_gio_set_block;
 	gpio_chip->to_irq = em_gio_to_irq;
 	gpio_chip->label = name;
 	gpio_chip->owner = THIS_MODULE;
--- linux-2.6.orig/drivers/gpio/gpio-ge.c
+++ linux-2.6/drivers/gpio/gpio-ge.c
@@ -100,6 +100,29 @@ static void gef_gpio_set(struct gpio_chi
 	_gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value);
 }
 
+static unsigned long gef_gpio_get_block(struct gpio_chip *chip,
+					unsigned long mask)
+{
+	struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip);
+
+	return ioread32be(mmchip->regs + GEF_GPIO_IN) & mask;
+}
+
+static void gef_gpio_set_block(struct gpio_chip *chip,
+			       unsigned long mask, unsigned long values)
+{
+	struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip);
+	void __iomem *reg = mmchip->regs + GEF_GPIO_OUT;
+	unsigned int data;
+
+	data = ioread32be(reg);
+
+	data &= ~mask;
+	data |= values & mask;
+
+	iowrite32be(data, reg);
+}
+
 static int __init gef_gpio_init(void)
 {
 	struct device_node *np;
@@ -125,6 +148,8 @@ static int __init gef_gpio_init(void)
 		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out;
 		gef_gpio_chip->gc.get = gef_gpio_get;
 		gef_gpio_chip->gc.set = gef_gpio_set;
+		gef_gpio_chip->gc.get_block = gef_gpio_get_block;
+		gef_gpio_chip->gc.set_block = gef_gpio_set_block;
 
 		/* This function adds a memory mapped GPIO chip */
 		retval = of_mm_gpiochip_add(np, gef_gpio_chip);
@@ -153,6 +178,8 @@ static int __init gef_gpio_init(void)
 		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out;
 		gef_gpio_chip->gc.get = gef_gpio_get;
 		gef_gpio_chip->gc.set = gef_gpio_set;
+		gef_gpio_chip->gc.get_block = gef_gpio_get_block;
+		gef_gpio_chip->gc.set_block = gef_gpio_set_block;
 
 		/* This function adds a memory mapped GPIO chip */
 		retval = of_mm_gpiochip_add(np, gef_gpio_chip);
@@ -181,6 +208,8 @@ static int __init gef_gpio_init(void)
 		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out;
 		gef_gpio_chip->gc.get = gef_gpio_get;
 		gef_gpio_chip->gc.set = gef_gpio_set;
+		gef_gpio_chip->gc.get_block = gef_gpio_get_block;
+		gef_gpio_chip->gc.set_block = gef_gpio_set_block;
 
 		/* This function adds a memory mapped GPIO chip */
 		retval = of_mm_gpiochip_add(np, gef_gpio_chip);
--- linux-2.6.orig/drivers/gpio/gpio-generic.c
+++ linux-2.6/drivers/gpio/gpio-generic.c
@@ -122,6 +122,13 @@ static int bgpio_get(struct gpio_chip *g
 	return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio);
 }
 
+static unsigned long bgpio_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+	return bgc->read_reg(bgc->reg_dat) & mask;
+}
+
 static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
 {
 	struct bgpio_chip *bgc = to_bgpio_chip(gc);
@@ -170,6 +177,51 @@ static void bgpio_set_set(struct gpio_ch
 	spin_unlock_irqrestore(&bgc->lock, flags);
 }
 
+static void bgpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bgc->lock, flags);
+
+	bgc->data &= ~mask;
+	bgc->data |= values & mask;
+
+	bgc->write_reg(bgc->reg_dat, bgc->data);
+
+	spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static void bgpio_set_with_clear_block(struct gpio_chip *gc, unsigned long mask,
+				       unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long set_bits = values & mask;
+	unsigned long clr_bits = ~values & mask;
+
+	if (set_bits)
+		bgc->write_reg(bgc->reg_set, set_bits);
+	if (clr_bits)
+		bgc->write_reg(bgc->reg_set, clr_bits);
+}
+
+static void bgpio_set_set_block(struct gpio_chip *gc, unsigned long mask,
+				unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bgc->lock, flags);
+
+	bgc->data &= ~mask;
+	bgc->data |= values & mask;
+
+	bgc->write_reg(bgc->reg_set, bgc->data);
+
+	spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
 static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	return 0;
@@ -317,14 +369,18 @@ static int bgpio_setup_io(struct bgpio_c
 		bgc->reg_set = set;
 		bgc->reg_clr = clr;
 		bgc->gc.set = bgpio_set_with_clear;
+		bgc->gc.set_block = bgpio_set_with_clear_block;
 	} else if (set && !clr) {
 		bgc->reg_set = set;
 		bgc->gc.set = bgpio_set_set;
+		bgc->gc.set_block = bgpio_set_set_block;
 	} else {
 		bgc->gc.set = bgpio_set;
+		bgc->gc.set_block = bgpio_set_block;
 	}
 
 	bgc->gc.get = bgpio_get;
+	bgc->gc.get_block = bgpio_get_block;
 
 	return 0;
 }
--- linux-2.6.orig/drivers/gpio/gpio-ks8695.c
+++ linux-2.6/drivers/gpio/gpio-ks8695.c
@@ -195,6 +195,38 @@ static int ks8695_gpio_get_value(struct
 
 
 /*
+ * Set the state of GPIO lines simultaneously.
+ */
+static void ks8695_gpio_set_block(struct gpio_chip *gc,
+				  unsigned long mask, unsigned long values)
+{
+	unsigned long x, flags;
+
+	local_irq_save(flags);
+
+	/* set output line state */
+	x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
+
+	x &= ~mask;
+	x |= values & mask;
+
+	__raw_writel(x, KS8695_GPIO_VA + KS8695_IOPD);
+
+	local_irq_restore(flags);
+}
+
+
+/*
+ * Read the state of GPIO lines simultaneously.
+ */
+static unsigned long ks8695_gpio_get_block(struct gpio_chip *gc,
+					   unsigned long mask)
+{
+	return __raw_readl(KS8695_GPIO_VA + KS8695_IOPD) & mask;
+}
+
+
+/*
  * Map GPIO line to IRQ number.
  */
 static int ks8695_gpio_to_irq(struct gpio_chip *gc, unsigned int pin)
@@ -225,6 +257,8 @@ static struct gpio_chip ks8695_gpio_chip
 	.direction_output	= ks8695_gpio_direction_output,
 	.get			= ks8695_gpio_get_value,
 	.set			= ks8695_gpio_set_value,
+	.get_block		= ks8695_gpio_get_block,
+	.set_block		= ks8695_gpio_set_block,
 	.to_irq			= ks8695_gpio_to_irq,
 	.base			= 0,
 	.ngpio			= 16,
--- linux-2.6.orig/drivers/gpio/gpio-lpc32xx.c
+++ linux-2.6/drivers/gpio/gpio-lpc32xx.c
@@ -297,6 +297,22 @@ static int lpc32xx_gpio_get_value_p3(str
 	return __get_gpio_state_p3(group, pin);
 }
 
+static unsigned long lpc32xx_gpio_get_block_p3(struct gpio_chip *chip,
+					       unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->inp_state);
+
+	/* In P3_INP_STATE, the 6 GPIOs are scattered over the register,
+	 * rearranging to bits 0-5
+	 */
+	bits >>= 10;
+	bits &= 0x401F;
+	bits |= (bits & 0x4000) >> 9;
+
+	return bits & mask & 0x3F;
+}
+
 static int lpc32xx_gpi_get_value(struct gpio_chip *chip, unsigned pin)
 {
 	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
@@ -304,6 +320,15 @@ static int lpc32xx_gpi_get_value(struct
 	return __get_gpi_state_p3(group, pin);
 }
 
+static unsigned long lpc32xx_gpi_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->inp_state);
+
+	return bits & mask;
+}
+
 static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
 	int value)
 {
@@ -351,6 +376,27 @@ static void lpc32xx_gpio_set_value_p3(st
 	__set_gpio_level_p3(group, pin, value);
 }
 
+static void lpc32xx_gpio_set_block_p3(struct gpio_chip *chip,
+				      unsigned long mask,
+				      unsigned long values)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 set_bits = values & mask;
+	u32 clr_bits = ~values & mask;
+
+	/* States of GPIO 0-5 start at bit 25 */
+	set_bits <<= 25;
+	clr_bits <<= 25;
+
+	/* Note: On LPC32xx, GPOs can only be set at once or cleared at once,
+	 *       but not set and cleared at once
+	 */
+	if (set_bits)
+		__raw_writel(set_bits, group->gpio_grp->outp_set);
+	if (clr_bits)
+		__raw_writel(clr_bits, group->gpio_grp->outp_clr);
+}
+
 static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin,
 	int value)
 {
@@ -366,6 +412,31 @@ static int lpc32xx_gpo_get_value(struct
 	return __get_gpo_state_p3(group, pin);
 }
 
+static void lpc32xx_gpo_set_block(struct gpio_chip *chip, unsigned long mask,
+				  unsigned long values)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 set_bits = values & mask;
+	u32 clr_bits = ~values & mask;
+
+	/* Note: On LPC32xx, GPOs can only be set at once or cleared at once,
+	 *       but not set and cleared at once
+	 */
+	if (set_bits)
+		__raw_writel(set_bits, group->gpio_grp->outp_set);
+	if (clr_bits)
+		__raw_writel(clr_bits, group->gpio_grp->outp_clr);
+}
+
+static unsigned long lpc32xx_gpo_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->outp_state);
+
+	return bits & mask;
+}
+
 static int lpc32xx_gpio_request(struct gpio_chip *chip, unsigned pin)
 {
 	if (pin < chip->ngpio)
@@ -440,8 +511,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p0",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_p01,
 			.base			= LPC32XX_GPIO_P0_GRP,
@@ -456,8 +529,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p1",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_p01,
 			.base			= LPC32XX_GPIO_P1_GRP,
@@ -472,8 +547,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p2",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.base			= LPC32XX_GPIO_P2_GRP,
 			.ngpio			= LPC32XX_GPIO_P2_MAX,
@@ -487,8 +564,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p3",
 			.direction_input	= lpc32xx_gpio_dir_input_p3,
 			.get			= lpc32xx_gpio_get_value_p3,
+			.get_block		= lpc32xx_gpio_get_block_p3,
 			.direction_output	= lpc32xx_gpio_dir_output_p3,
 			.set			= lpc32xx_gpio_set_value_p3,
+			.set_block		= lpc32xx_gpio_set_block_p3,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_gpio_p3,
 			.base			= LPC32XX_GPIO_P3_GRP,
@@ -503,6 +582,7 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpi_p3",
 			.direction_input	= lpc32xx_gpio_dir_in_always,
 			.get			= lpc32xx_gpi_get_value,
+			.get_block		= lpc32xx_gpi_get_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_gpi_p3,
 			.base			= LPC32XX_GPI_P3_GRP,
@@ -517,7 +597,9 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpo_p3",
 			.direction_output	= lpc32xx_gpio_dir_out_always,
 			.set			= lpc32xx_gpo_set_value,
+			.set_block		= lpc32xx_gpo_set_block,
 			.get			= lpc32xx_gpo_get_value,
+			.get_block		= lpc32xx_gpo_get_block,
 			.request		= lpc32xx_gpio_request,
 			.base			= LPC32XX_GPO_P3_GRP,
 			.ngpio			= LPC32XX_GPO_P3_MAX,
--- linux-2.6.orig/drivers/gpio/gpio-max730x.c
+++ linux-2.6/drivers/gpio/gpio-max730x.c
@@ -146,6 +146,44 @@ static int max7301_get(struct gpio_chip
 	return level;
 }
 
+static unsigned long max7301_get_block(struct gpio_chip *chip,
+				       unsigned long mask)
+{
+	struct max7301 *ts = container_of(chip, struct max7301, chip);
+	int i, j;
+	unsigned long result = 0;
+
+	for (i = 0; i < 4; i++) {
+		if ((mask >> (i * 8)) & 0xFF) { /* i/o only if necessary */
+			u8 in_level = ts->read(ts->dev, 0x44 + i * 8);
+			u8 in_mask = 0;
+			u8 out_level = (ts->out_level >> (i * 8 + 4)) & 0xFF;
+			u8 out_mask = 0;
+
+			for (j = 0; j < 8; j++) {
+				int offset = 4 + i * 8 + j;
+				int config = (ts->port_config[offset >> 2] >>
+					      ((offset & 3) << 1)) &
+					PIN_CONFIG_MASK;
+
+				switch (config) {
+				case PIN_CONFIG_OUT:
+					out_mask |= BIT(j);
+					break;
+				case PIN_CONFIG_IN_WO_PULLUP:
+				case PIN_CONFIG_IN_PULLUP:
+					in_mask |= BIT(j);
+				}
+			}
+
+			result |= ((unsigned long)(in_level & in_mask) |
+				   (out_level & out_mask)) << (i * 8);
+		}
+	}
+
+	return result & mask;
+}
+
 static void max7301_set(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct max7301 *ts = container_of(chip, struct max7301, chip);
@@ -160,6 +198,27 @@ static void max7301_set(struct gpio_chip
 	mutex_unlock(&ts->lock);
 }
 
+static void max7301_set_block(struct gpio_chip *chip, unsigned long mask,
+			      unsigned long values)
+{
+	struct max7301 *ts = container_of(chip, struct max7301, chip);
+	unsigned long changes;
+	int i;
+
+	mutex_lock(&ts->lock);
+
+	changes = (ts->out_level ^ (values << 4)) & (mask << 4);
+	ts->out_level ^= changes;
+
+	for (i = 0; i < 4; i++) {
+		if ((changes >> (i * 8 + 4)) & 0xFF) /* i/o only on change */
+			ts->write(ts->dev, 0x44 + i * 8,
+				  (ts->out_level >> (i * 8 + 4)) & 0xFF);
+	}
+
+	mutex_unlock(&ts->lock);
+}
+
 int __max730x_probe(struct max7301 *ts)
 {
 	struct device *dev = ts->dev;
@@ -184,8 +243,10 @@ int __max730x_probe(struct max7301 *ts)
 
 	ts->chip.direction_input = max7301_direction_input;
 	ts->chip.get = max7301_get;
+	ts->chip.get_block = max7301_get_block;
 	ts->chip.direction_output = max7301_direction_output;
 	ts->chip.set = max7301_set;
+	ts->chip.set_block = max7301_set_block;
 
 	ts->chip.ngpio = PIN_NUMBER;
 	ts->chip.can_sleep = 1;
--- linux-2.6.orig/drivers/gpio/gpio-max732x.c
+++ linux-2.6/drivers/gpio/gpio-max732x.c
@@ -219,6 +219,63 @@ out:
 	mutex_unlock(&chip->lock);
 }
 
+static unsigned long max732x_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct max732x_chip *chip;
+	uint8_t lo = 0;
+	uint8_t hi = 0;
+	int ret;
+
+	chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+	if (mask & 0xFF) {
+		ret = max732x_readb(chip, is_group_a(chip, 0), &lo);
+		if (ret < 0)
+			return 0;
+	}
+	if (mask & 0xFF00) {
+		ret = max732x_readb(chip, is_group_a(chip, 8), &hi);
+		if (ret < 0)
+			return 0;
+	}
+
+	return (((unsigned long)hi << 8) | lo) & mask;
+}
+
+static void max732x_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct max732x_chip *chip;
+	uint8_t reg_out;
+	uint8_t lo_mask = mask & 0xFF;
+	uint8_t hi_mask = (mask >> 8) & 0xFF;
+	int ret;
+
+	chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+	mutex_lock(&chip->lock);
+
+	if (lo_mask) {
+		reg_out = (chip->reg_out[0] & ~lo_mask) | (values & lo_mask);
+		ret = max732x_writeb(chip, is_group_a(chip, 0), reg_out);
+		if (ret < 0)
+			goto out;
+		chip->reg_out[0] = reg_out;
+	}
+	if (hi_mask) {
+		reg_out = (chip->reg_out[1] & ~hi_mask) |
+			((values >> 8) & hi_mask);
+		ret = max732x_writeb(chip, is_group_a(chip, 8), reg_out);
+		if (ret < 0)
+			goto out;
+		chip->reg_out[1] = reg_out;
+	}
+
+out:
+	mutex_unlock(&chip->lock);
+}
+
 static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
 {
 	struct max732x_chip *chip;
@@ -562,8 +619,10 @@ static int max732x_setup_gpio(struct max
 	if (chip->dir_output) {
 		gc->direction_output = max732x_gpio_direction_output;
 		gc->set = max732x_gpio_set_value;
+		gc->set_block = max732x_gpio_set_block;
 	}
 	gc->get = max732x_gpio_get_value;
+	gc->get_block = max732x_gpio_get_block;
 	gc->can_sleep = 1;
 
 	gc->base = gpio_start;
--- linux-2.6.orig/drivers/gpio/gpio-mc33880.c
+++ linux-2.6/drivers/gpio/gpio-mc33880.c
@@ -80,6 +80,21 @@ static void mc33880_set(struct gpio_chip
 	mutex_unlock(&mc->lock);
 }
 
+static void mc33880_set_block(struct gpio_chip *chip, unsigned long mask,
+			      unsigned long values)
+{
+	struct mc33880 *mc = container_of(chip, struct mc33880, chip);
+
+	mutex_lock(&mc->lock);
+
+	mc->port_config &= ~mask;
+	mc->port_config |= values & mask;
+
+	mc33880_write_config(mc);
+
+	mutex_unlock(&mc->lock);
+}
+
 static int mc33880_probe(struct spi_device *spi)
 {
 	struct mc33880 *mc;
@@ -113,6 +128,7 @@ static int mc33880_probe(struct spi_devi
 
 	mc->chip.label = DRIVER_NAME,
 	mc->chip.set = mc33880_set;
+	mc->chip.set_block = mc33880_set_block;
 	mc->chip.base = pdata->base;
 	mc->chip.ngpio = PIN_NUMBER;
 	mc->chip.can_sleep = 1;
--- linux-2.6.orig/drivers/gpio/gpio-ml-ioh.c
+++ linux-2.6/drivers/gpio/gpio-ml-ioh.c
@@ -127,6 +127,31 @@ static int ioh_gpio_get(struct gpio_chip
 	return ioread32(&chip->reg->regs[chip->ch].pi) & (1 << nr);
 }
 
+static void ioh_gpio_set_block(struct gpio_chip *gpio, unsigned long mask,
+			       unsigned long values)
+{
+	u32 reg_val;
+	struct ioh_gpio *chip =	container_of(gpio, struct ioh_gpio, gpio);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->spinlock, flags);
+	reg_val = ioread32(&chip->reg->regs[chip->ch].po);
+
+	reg_val &= ~mask;
+	reg_val |= values & mask;
+
+	iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
+	spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
+static unsigned long ioh_gpio_get_block(struct gpio_chip *gpio,
+					unsigned long mask)
+{
+	struct ioh_gpio *chip =	container_of(gpio, struct ioh_gpio, gpio);
+
+	return ioread32(&chip->reg->regs[chip->ch].pi) & mask;
+}
+
 static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
 				     int val)
 {
@@ -237,8 +262,10 @@ static void ioh_gpio_setup(struct ioh_gp
 	gpio->owner = THIS_MODULE;
 	gpio->direction_input = ioh_gpio_direction_input;
 	gpio->get = ioh_gpio_get;
+	gpio->get_block = ioh_gpio_get_block;
 	gpio->direction_output = ioh_gpio_direction_output;
 	gpio->set = ioh_gpio_set;
+	gpio->set_block = ioh_gpio_set_block;
 	gpio->dbg_show = NULL;
 	gpio->base = -1;
 	gpio->ngpio = num_port;
--- linux-2.6.orig/drivers/gpio/gpio-mm-lantiq.c
+++ linux-2.6/drivers/gpio/gpio-mm-lantiq.c
@@ -73,6 +73,27 @@ static void ltq_mm_set(struct gpio_chip
 }
 
 /**
+ * ltq_mm_set_block() - gpio_chip->set_block - set gpios simultaneously.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @mask:   Bit map of masked GPIOs in this gpio_chip.
+ * @values: Values to be written to the specified signals.
+ *
+ * Set the shadow values and call ltq_mm_apply.
+ */
+static void ltq_mm_set_block(struct gpio_chip *gc, unsigned long mask,
+			     unsigned long values)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct ltq_mm *chip =
+		container_of(mm_gc, struct ltq_mm, mmchip);
+
+	chip->shadow &= ~mask;
+	chip->shadow |= values & mask;
+
+	ltq_mm_apply(chip);
+}
+
+/**
  * ltq_mm_dir_out() - gpio_chip->dir_out - set gpio direction.
  * @gc:     Pointer to gpio_chip device structure.
  * @gpio:   GPIO signal number.
@@ -122,6 +143,7 @@ static int ltq_mm_probe(struct platform_
 	chip->mmchip.gc.label = "gpio-mm-ltq";
 	chip->mmchip.gc.direction_output = ltq_mm_dir_out;
 	chip->mmchip.gc.set = ltq_mm_set;
+	chip->mmchip.gc.set_block = ltq_mm_set_block;
 	chip->mmchip.save_regs = ltq_mm_save_regs;
 
 	/* store the shadow value if one was passed by the devicetree */
--- linux-2.6.orig/drivers/gpio/gpio-mpc5200.c
+++ linux-2.6/drivers/gpio/gpio-mpc5200.c
@@ -24,6 +24,7 @@
 #include <linux/io.h>
 #include <linux/of_platform.h>
 #include <linux/module.h>
+#include <linux/bitrev.h>
 
 #include <asm/gpio.h>
 #include <asm/mpc52xx.h>
@@ -97,6 +98,35 @@ mpc52xx_wkup_gpio_set(struct gpio_chip *
 	pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
 }
 
+static unsigned long mpc52xx_wkup_gpio_get_block(struct gpio_chip *gc,
+						 unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+
+	return bitrev8(in_8(&regs->wkup_ival)) & mask;
+}
+
+static void
+mpc52xx_wkup_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+			struct mpc52xx_gpiochip, mmchip);
+	struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip->shadow_dvo &= ~bitrev8(mask);
+	chip->shadow_dvo |= bitrev8(values & mask);
+
+	out_8(&regs->wkup_dvo, chip->shadow_dvo);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
 static int mpc52xx_wkup_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
@@ -166,6 +196,8 @@ static int mpc52xx_wkup_gpiochip_probe(s
 	gc->direction_output = mpc52xx_wkup_gpio_dir_out;
 	gc->get              = mpc52xx_wkup_gpio_get;
 	gc->set              = mpc52xx_wkup_gpio_set;
+	gc->get_block        = mpc52xx_wkup_gpio_get_block;
+	gc->set_block        = mpc52xx_wkup_gpio_set_block;
 
 	ret = of_mm_gpiochip_add(ofdev->dev.of_node, &chip->mmchip);
 	if (ret)
@@ -256,6 +288,36 @@ mpc52xx_simple_gpio_set(struct gpio_chip
 	pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
 }
 
+static unsigned long mpc52xx_simple_gpio_get_block(struct gpio_chip *gc,
+						   unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+
+	return bitrev32(in_be32(&regs->simple_ival)) & mask;
+}
+
+static void
+mpc52xx_simple_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			      unsigned long values)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+						     struct mpc52xx_gpiochip,
+						     mmchip);
+	struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip->shadow_dvo &= ~bitrev32(mask);
+	chip->shadow_dvo |= bitrev32(values & mask);
+
+	out_be32(&regs->simple_dvo, chip->shadow_dvo);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
 static int mpc52xx_simple_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
@@ -326,6 +388,8 @@ static int mpc52xx_simple_gpiochip_probe
 	gc->direction_output = mpc52xx_simple_gpio_dir_out;
 	gc->get              = mpc52xx_simple_gpio_get;
 	gc->set              = mpc52xx_simple_gpio_set;
+	gc->get_block        = mpc52xx_simple_gpio_get_block;
+	gc->set_block        = mpc52xx_simple_gpio_set_block;
 
 	ret = of_mm_gpiochip_add(ofdev->dev.of_node, &chip->mmchip);
 	if (ret)
--- linux-2.6.orig/drivers/gpio/gpio-mpc8xxx.c
+++ linux-2.6/drivers/gpio/gpio-mpc8xxx.c
@@ -17,6 +17,7 @@
 #include <linux/gpio.h>
 #include <linux/slab.h>
 #include <linux/irq.h>
+#include <linux/bitrev.h>
 
 #define MPC8XXX_GPIO_PINS	32
 
@@ -100,6 +101,43 @@ static void mpc8xxx_gpio_set(struct gpio
 	spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
 }
 
+static unsigned long mpc8572_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	u32 val;
+	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+	struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+
+	val = in_be32(mm->regs + GPIO_DAT) & ~in_be32(mm->regs + GPIO_DIR);
+
+	return bitrev32(val | mpc8xxx_gc->data) & mask;
+}
+
+static unsigned long mpc8xxx_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+
+	return bitrev32(in_be32(mm->regs + GPIO_DAT)) & mask;
+}
+
+static void mpc8xxx_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+	struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+	unsigned long flags;
+
+	spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+	mpc8xxx_gc->data &= ~bitrev32(mask);
+	mpc8xxx_gc->data |= bitrev32(values | mask);
+
+	out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data);
+
+	spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+}
+
 static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
@@ -340,6 +378,9 @@ static void __init mpc8xxx_add_controlle
 	gc->get = of_device_is_compatible(np, "fsl,mpc8572-gpio") ?
 		mpc8572_gpio_get : mpc8xxx_gpio_get;
 	gc->set = mpc8xxx_gpio_set;
+	gc->get_block = of_device_is_compatible(np, "fsl,mpc8572-gpio") ?
+		mpc8572_gpio_get_block : mpc8xxx_gpio_get_block;
+	gc->set_block = mpc8xxx_gpio_set_block;
 	gc->to_irq = mpc8xxx_gpio_to_irq;
 
 	ret = of_mm_gpiochip_add(np, mm_gc);
--- linux-2.6.orig/drivers/gpio/gpio-pca953x.c
+++ linux-2.6/drivers/gpio/gpio-pca953x.c
@@ -302,6 +302,68 @@ exit:
 	mutex_unlock(&chip->i2c_lock);
 }
 
+static unsigned long pca953x_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct pca953x_chip *chip;
+	u32 reg_val;
+	int ret, offset = 0;
+
+	chip = container_of(gc, struct pca953x_chip, gpio_chip);
+
+	mutex_lock(&chip->i2c_lock);
+	switch (chip->chip_type) {
+	case PCA953X_TYPE:
+		offset = PCA953X_INPUT;
+		break;
+	case PCA957X_TYPE:
+		offset = PCA957X_IN;
+		break;
+	}
+	ret = pca953x_read_reg(chip, offset, &reg_val);
+	mutex_unlock(&chip->i2c_lock);
+	if (ret < 0) {
+		/* NOTE:  diagnostic already emitted; that's all we should
+		 * do unless gpio_*_value_cansleep() calls become different
+		 * from their nonsleeping siblings (and report faults).
+		 */
+		return 0;
+	}
+
+	return reg_val & mask;
+}
+
+static void pca953x_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct pca953x_chip *chip;
+	u32 reg_val;
+	int ret, offset = 0;
+
+	chip = container_of(gc, struct pca953x_chip, gpio_chip);
+
+	mutex_lock(&chip->i2c_lock);
+
+	reg_val = chip->reg_output & ~mask;
+	reg_val |= values & mask;
+
+	switch (chip->chip_type) {
+	case PCA953X_TYPE:
+		offset = PCA953X_OUTPUT;
+		break;
+	case PCA957X_TYPE:
+		offset = PCA957X_OUT;
+		break;
+	}
+	ret = pca953x_write_reg(chip, offset, reg_val);
+	if (ret)
+		goto exit;
+
+	chip->reg_output = reg_val;
+exit:
+	mutex_unlock(&chip->i2c_lock);
+}
+
 static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
 {
 	struct gpio_chip *gc;
@@ -312,6 +374,8 @@ static void pca953x_setup_gpio(struct pc
 	gc->direction_output = pca953x_gpio_direction_output;
 	gc->get = pca953x_gpio_get_value;
 	gc->set = pca953x_gpio_set_value;
+	gc->get_block = pca953x_gpio_get_block;
+	gc->set_block = pca953x_gpio_set_block;
 	gc->can_sleep = 1;
 
 	gc->base = chip->gpio_start;
--- linux-2.6.orig/drivers/gpio/gpio-pcf857x.c
+++ linux-2.6/drivers/gpio/gpio-pcf857x.c
@@ -158,6 +158,28 @@ static void pcf857x_set(struct gpio_chip
 	pcf857x_output(chip, offset, value);
 }
 
+static unsigned long pcf857x_get_block(struct gpio_chip *chip,
+				       unsigned long mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		value;
+
+	value = gpio->read(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
+static void pcf857x_set_block(struct gpio_chip *chip, unsigned long mask,
+			      unsigned long values)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= values & mask;
+	gpio->write(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+}
+
 /*-------------------------------------------------------------------------*/
 
 static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -280,6 +302,8 @@ static int pcf857x_probe(struct i2c_clie
 	gpio->chip.owner		= THIS_MODULE;
 	gpio->chip.get			= pcf857x_get;
 	gpio->chip.set			= pcf857x_set;
+	gpio->chip.get_block		= pcf857x_get_block;
+	gpio->chip.set_block		= pcf857x_set_block;
 	gpio->chip.direction_input	= pcf857x_input;
 	gpio->chip.direction_output	= pcf857x_output;
 	gpio->chip.ngpio		= id->driver_data;
--- linux-2.6.orig/drivers/gpio/gpio-pch.c
+++ linux-2.6/drivers/gpio/gpio-pch.c
@@ -122,6 +122,23 @@ static void pch_gpio_set(struct gpio_chi
 	spin_unlock_irqrestore(&chip->spinlock, flags);
 }
 
+static void pch_gpio_set_block(struct gpio_chip *gpio, unsigned long mask,
+			       unsigned long values)
+{
+	u32 reg_val;
+	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->spinlock, flags);
+	reg_val = ioread32(&chip->reg->po);
+
+	reg_val &= ~mask;
+	reg_val |= values & mask;
+
+	iowrite32(reg_val, &chip->reg->po);
+	spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
 static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr)
 {
 	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
@@ -129,6 +146,14 @@ static int pch_gpio_get(struct gpio_chip
 	return ioread32(&chip->reg->pi) & (1 << nr);
 }
 
+static unsigned long pch_gpio_get_block(struct gpio_chip *gpio,
+					unsigned long mask)
+{
+	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
+
+	return ioread32(&chip->reg->pi) & mask;
+}
+
 static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
 				     int val)
 {
@@ -219,8 +244,10 @@ static void pch_gpio_setup(struct pch_gp
 	gpio->owner = THIS_MODULE;
 	gpio->direction_input = pch_gpio_direction_input;
 	gpio->get = pch_gpio_get;
+	gpio->get_block = pch_gpio_get_block;
 	gpio->direction_output = pch_gpio_direction_output;
 	gpio->set = pch_gpio_set;
+	gpio->set_block = pch_gpio_set_block;
 	gpio->dbg_show = NULL;
 	gpio->base = -1;
 	gpio->ngpio = gpio_pins[chip->ioh];
--- linux-2.6.orig/drivers/gpio/gpio-pl061.c
+++ linux-2.6/drivers/gpio/gpio-pl061.c
@@ -118,6 +118,21 @@ static void pl061_set_value(struct gpio_
 	writeb(!!value << offset, chip->base + (1 << (offset + 2)));
 }
 
+static unsigned long pl061_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+	return !!readb(chip->base + (mask << 2));
+}
+
+static void pl061_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+	writeb(values & mask, chip->base + (mask << 2));
+}
+
 static int pl061_to_irq(struct gpio_chip *gc, unsigned offset)
 {
 	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
@@ -246,6 +261,8 @@ static int pl061_probe(struct amba_devic
 	chip->gc.direction_output = pl061_direction_output;
 	chip->gc.get = pl061_get_value;
 	chip->gc.set = pl061_set_value;
+	chip->gc.get_block = pl061_get_block;
+	chip->gc.set_block = pl061_set_block;
 	chip->gc.to_irq = pl061_to_irq;
 	chip->gc.ngpio = PL061_GPIO_NR;
 	chip->gc.label = dev_name(dev);
--- linux-2.6.orig/drivers/gpio/gpio-sa1100.c
+++ linux-2.6/drivers/gpio/gpio-sa1100.c
@@ -27,6 +27,24 @@ static void sa1100_gpio_set(struct gpio_
 		GPCR = GPIO_GPIO(offset);
 }
 
+static unsigned long sa1100_gpio_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	return GPLR & mask;
+}
+
+static void sa1100_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+				  unsigned long values)
+{
+	unsigned long set_bits = values & mask;
+	unsigned long clr_bits = ~values & mask;
+
+	if (set_bits)
+		GPSR = set_bits;
+	if (clr_bits)
+		GPCR = clr_bits;
+}
+
 static int sa1100_direction_input(struct gpio_chip *chip, unsigned offset)
 {
 	unsigned long flags;
@@ -59,6 +77,8 @@ static struct gpio_chip sa1100_gpio_chip
 	.direction_output	= sa1100_direction_output,
 	.set			= sa1100_gpio_set,
 	.get			= sa1100_gpio_get,
+	.set_block		= sa1100_gpio_set_block,
+	.get_block		= sa1100_gpio_get_block,
 	.to_irq			= sa1100_to_irq,
 	.base			= 0,
 	.ngpio			= GPIO_MAX + 1,
--- linux-2.6.orig/drivers/gpio/gpio-samsung.c
+++ linux-2.6/drivers/gpio/gpio-samsung.c
@@ -862,6 +862,33 @@ static int samsung_gpiolib_get(struct gp
 	return val;
 }
 
+static void samsung_gpiolib_set_block(struct gpio_chip *chip,
+				      unsigned long mask,
+				      unsigned long values)
+{
+	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+	void __iomem *base = ourchip->base;
+	unsigned long flags;
+	unsigned long dat;
+
+	samsung_gpio_lock(ourchip, flags);
+
+	dat = __raw_readl(base + 0x04);
+	dat &= ~mask;
+	dat |= values & mask;
+	__raw_writel(dat, base + 0x04);
+
+	samsung_gpio_unlock(ourchip, flags);
+}
+
+static unsigned long samsung_gpiolib_get_block(struct gpio_chip *chip,
+					       unsigned long mask)
+{
+	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+
+	return __raw_readl(ourchip->base + 0x04) & mask;
+}
+
 /*
  * CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios
  * for use with the configuration calls, and other parts of the s3c gpiolib
@@ -919,6 +946,10 @@ static void __init samsung_gpiolib_add(s
 		gc->set = samsung_gpiolib_set;
 	if (!gc->get)
 		gc->get = samsung_gpiolib_get;
+	if (!gc->set_block)
+		gc->set_block = samsung_gpiolib_set_block;
+	if (!gc->get_block)
+		gc->get_block = samsung_gpiolib_get_block;
 
 #ifdef CONFIG_PM
 	if (chip->pm != NULL) {
--- linux-2.6.orig/drivers/gpio/gpio-twl6040.c
+++ linux-2.6/drivers/gpio/gpio-twl6040.c
@@ -46,6 +46,19 @@ static int twl6040gpo_get(struct gpio_ch
 	return (ret >> offset) & 1;
 }
 
+static unsigned long twl6040gpo_get_block(struct gpio_chip *chip,
+					  unsigned long mask)
+{
+	struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+	int ret = 0;
+
+	ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+	if (ret < 0)
+		return 0;
+
+	return ret & mask;
+}
+
 static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset,
 				    int value)
 {
@@ -71,12 +84,31 @@ static void twl6040gpo_set(struct gpio_c
 	twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
 }
 
+static void twl6040gpo_set_block(struct gpio_chip *chip, unsigned long mask,
+				 unsigned long values)
+{
+	struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+	int ret;
+	u8 gpoctl;
+
+	ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+	if (ret < 0)
+		return;
+
+	gpoctl = ret & ~mask;
+	gpoctl |= values & mask;
+
+	twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
+}
+
 static struct gpio_chip twl6040gpo_chip = {
 	.label			= "twl6040",
 	.owner			= THIS_MODULE,
 	.get			= twl6040gpo_get,
+	.get_block		= twl6040gpo_get_block,
 	.direction_output	= twl6040gpo_direction_out,
 	.set			= twl6040gpo_set,
+	.set_block		= twl6040gpo_set_block,
 	.can_sleep		= 1,
 };
 
--- linux-2.6.orig/drivers/gpio/gpio-ucb1400.c
+++ linux-2.6/drivers/gpio/gpio-ucb1400.c
@@ -45,6 +45,27 @@ static void ucb1400_gpio_set(struct gpio
 	ucb1400_gpio_set_value(gpio->ac97, off, val);
 }
 
+static unsigned long ucb1400_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct ucb1400_gpio *gpio;
+	gpio = container_of(gc, struct ucb1400_gpio, gc);
+	return ucb1400_reg_read(gpio->ac97, UCB_IO_DATA) & mask;
+}
+
+static void ucb1400_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct ucb1400_gpio *gpio;
+	u16 tmp;
+	gpio = container_of(gc, struct ucb1400_gpio, gc);
+
+	tmp = ucb1400_reg_read(gpio->ac97, UCB_IO_DATA) & ~mask;
+	tmp |= values & mask;
+
+	ucb1400_reg_write(gpio->ac97, UCB_IO_DATA, tmp);
+}
+
 static int ucb1400_gpio_probe(struct platform_device *dev)
 {
 	struct ucb1400_gpio *ucb = dev->dev.platform_data;
@@ -66,6 +87,8 @@ static int ucb1400_gpio_probe(struct pla
 	ucb->gc.direction_output = ucb1400_gpio_dir_out;
 	ucb->gc.get = ucb1400_gpio_get;
 	ucb->gc.set = ucb1400_gpio_set;
+	ucb->gc.get_block = ucb1400_gpio_get_block;
+	ucb->gc.set_block = ucb1400_gpio_set_block;
 	ucb->gc.can_sleep = 1;
 
 	err = gpiochip_add(&ucb->gc);
--- linux-2.6.orig/drivers/gpio/gpio-vt8500.c
+++ linux-2.6/drivers/gpio/gpio-vt8500.c
@@ -211,6 +211,28 @@ static void vt8500_gpio_set_value(struct
 	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
 }
 
+static unsigned long vt8500_gpio_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) &
+								mask;
+}
+
+static void vt8500_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+				  unsigned long  values)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	u32 val = readl_relaxed(vt8500_chip->base +
+				vt8500_chip->regs->data_out);
+	val &= ~mask;
+	val |= values & mask;
+
+	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
+}
+
 static int vt8500_of_xlate(struct gpio_chip *gc,
 			    const struct of_phandle_args *gpiospec, u32 *flags)
 {
@@ -253,6 +275,8 @@ static int vt8500_add_chips(struct platf
 		chip->direction_output = vt8500_gpio_direction_output;
 		chip->get = vt8500_gpio_get_value;
 		chip->set = vt8500_gpio_set_value;
+		chip->get_block = vt8500_gpio_get_block;
+		chip->set_block = vt8500_gpio_set_block;
 		chip->can_sleep = 0;
 		chip->base = pin_cnt;
 		chip->ngpio = data->banks[i].ngpio;
--- linux-2.6.orig/drivers/gpio/gpio-xilinx.c
+++ linux-2.6/drivers/gpio/gpio-xilinx.c
@@ -49,6 +49,21 @@ static int xgpio_get(struct gpio_chip *g
 }
 
 /**
+ * xgpio_get_block - Read a block of specified signals of the GPIO device.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @mask:   Bit mask (bit 0 == 0th GPIO) for GPIOs to get
+ *
+ * This function reads a block of specified signal of the GPIO device, returned
+ * as a bit mask, each bit representing a GPIO
+ */
+static unsigned long xgpio_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+
+	return in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) & mask;
+}
+
+/**
  * xgpio_set - Write the specified signal of the GPIO device.
  * @gc:     Pointer to gpio_chip device structure.
  * @gpio:   GPIO signal number.
@@ -77,6 +92,33 @@ static void xgpio_set(struct gpio_chip *
 }
 
 /**
+ * xgpio_set_block - Write a block of specified signals of the GPIO device.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @mask:   Bit mask (bit 0 == 0th GPIO) for GPIOs to set
+ * @values: Bit mapped values
+ *
+ * This function writes the specified values in to the specified signals of the
+ * GPIO device.
+ */
+static void xgpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	unsigned long flags;
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct xgpio_instance *chip =
+	    container_of(mm_gc, struct xgpio_instance, mmchip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	chip->gpio_state &= ~mask;
+	chip->gpio_state |= mask & values;
+
+	out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/**
  * xgpio_dir_in - Set the direction of the specified GPIO signal as input.
  * @gc:     Pointer to gpio_chip device structure.
  * @gpio:   GPIO signal number.
@@ -195,6 +237,8 @@ static int xgpio_of_probe(struct device_
 	chip->mmchip.gc.direction_output = xgpio_dir_out;
 	chip->mmchip.gc.get = xgpio_get;
 	chip->mmchip.gc.set = xgpio_set;
+	chip->mmchip.gc.get_block = xgpio_get_block;
+	chip->mmchip.gc.set_block = xgpio_set_block;
 
 	chip->mmchip.save_regs = xgpio_save_regs;
 
--- linux-2.6.orig/drivers/pinctrl/pinctrl-at91.c
+++ linux-2.6/drivers/pinctrl/pinctrl-at91.c
@@ -49,6 +49,7 @@ struct at91_gpio_chip {
 	struct clk		*clock;		/* associated clock */
 	struct irq_domain	*domain;	/* associated irq domain */
 	struct at91_pinctrl_mux_ops *ops;	/* ops */
+	unsigned long		mask_cache;	/* cached mask for block gpio */
 };
 
 #define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip)
@@ -1125,6 +1126,32 @@ static void at91_gpio_set(struct gpio_ch
 	writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR));
 }
 
+static unsigned long at91_gpio_get_block(struct gpio_chip *chip,
+					 unsigned long mask)
+{
+	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
+	void __iomem *pio = at91_gpio->regbase;
+	u32 pdsr;
+
+	pdsr = __raw_readl(pio + PIO_PDSR);
+	return pdsr & mask;
+}
+
+static void at91_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+				unsigned long val)
+{
+	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
+	void __iomem *pio = at91_gpio->regbase;
+
+	/* Do synchronous data output with a single write access */
+	if (mask != at91_gpio->mask_cache) {
+		at91_gpio->mask_cache = mask;
+		__raw_writel(~mask, pio + PIO_OWDR);
+		__raw_writel(mask, pio + PIO_OWER);
+	}
+	__raw_writel(val, pio + PIO_ODSR);
+}
+
 static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
 				int val)
 {
@@ -1435,8 +1462,10 @@ static struct gpio_chip at91_gpio_templa
 	.free			= at91_gpio_free,
 	.direction_input	= at91_gpio_direction_input,
 	.get			= at91_gpio_get,
+	.get_block		= at91_gpio_get_block,
 	.direction_output	= at91_gpio_direction_output,
 	.set			= at91_gpio_set,
+	.set_block		= at91_gpio_set_block,
 	.to_irq			= at91_gpio_to_irq,
 	.dbg_show		= at91_gpio_dbg_show,
 	.can_sleep		= 0,
--- linux-2.6.orig/drivers/pinctrl/pinctrl-nomadik.c
+++ linux-2.6/drivers/pinctrl/pinctrl-nomadik.c
@@ -1063,6 +1063,40 @@ static void nmk_gpio_set_output(struct g
 	clk_disable(nmk_chip->clk);
 }
 
+static unsigned long nmk_gpio_get_block(struct gpio_chip *chip,
+					unsigned long mask)
+{
+	struct nmk_gpio_chip *nmk_chip =
+		container_of(chip, struct nmk_gpio_chip, chip);
+	unsigned long values;
+
+	clk_enable(nmk_chip->clk);
+
+	values = readl(nmk_chip->addr + NMK_GPIO_DAT);
+
+	clk_disable(nmk_chip->clk);
+
+	return values & mask;
+}
+
+static void nmk_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+			       unsigned long values)
+{
+	struct nmk_gpio_chip *nmk_chip =
+		container_of(chip, struct nmk_gpio_chip, chip);
+	unsigned long set_bits = values & mask;
+	unsigned long clr_bits = ~values & mask;
+
+	clk_enable(nmk_chip->clk);
+
+	if (set_bits)
+		writel(set_bits, nmk_chip->addr + NMK_GPIO_DATS);
+	if (clr_bits)
+		writel(clr_bits, nmk_chip->addr + NMK_GPIO_DATC);
+
+	clk_disable(nmk_chip->clk);
+}
+
 static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset,
 				int val)
 {
@@ -1182,8 +1216,10 @@ static struct gpio_chip nmk_gpio_templat
 	.free			= nmk_gpio_free,
 	.direction_input	= nmk_gpio_make_input,
 	.get			= nmk_gpio_get_input,
+	.get_block		= nmk_gpio_get_block,
 	.direction_output	= nmk_gpio_make_output,
 	.set			= nmk_gpio_set_output,
+	.set_block		= nmk_gpio_set_block,
 	.to_irq			= nmk_gpio_to_irq,
 	.dbg_show		= nmk_gpio_dbg_show,
 	.can_sleep		= 0,

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 6/6 v13] gpio: Add block gpio to several gpio drivers
@ 2013-01-15 11:51   ` Roland Stigge
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds block GPIO support to several gpio drivers.

This implements block GPIO only for some selected drivers since block GPIO is
an optional feature which may not be suitable for every GPIO hardware. (With
automatic fallback to the single GPIO functions if not available in a driver.)

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 drivers/gpio/Kconfig              |    2 
 drivers/gpio/gpio-em.c            |   23 ++++++++++
 drivers/gpio/gpio-ge.c            |   29 +++++++++++++
 drivers/gpio/gpio-generic.c       |   56 +++++++++++++++++++++++++
 drivers/gpio/gpio-ks8695.c        |   34 +++++++++++++++
 drivers/gpio/gpio-lpc32xx.c       |   82 ++++++++++++++++++++++++++++++++++++++
 drivers/gpio/gpio-max730x.c       |   61 ++++++++++++++++++++++++++++
 drivers/gpio/gpio-max732x.c       |   59 +++++++++++++++++++++++++++
 drivers/gpio/gpio-mc33880.c       |   16 +++++++
 drivers/gpio/gpio-ml-ioh.c        |   27 ++++++++++++
 drivers/gpio/gpio-mm-lantiq.c     |   22 ++++++++++
 drivers/gpio/gpio-mpc5200.c       |   64 +++++++++++++++++++++++++++++
 drivers/gpio/gpio-mpc8xxx.c       |   41 +++++++++++++++++++
 drivers/gpio/gpio-pca953x.c       |   64 +++++++++++++++++++++++++++++
 drivers/gpio/gpio-pcf857x.c       |   24 +++++++++++
 drivers/gpio/gpio-pch.c           |   27 ++++++++++++
 drivers/gpio/gpio-pl061.c         |   17 +++++++
 drivers/gpio/gpio-sa1100.c        |   20 +++++++++
 drivers/gpio/gpio-samsung.c       |   31 ++++++++++++++
 drivers/gpio/gpio-twl6040.c       |   32 ++++++++++++++
 drivers/gpio/gpio-ucb1400.c       |   23 ++++++++++
 drivers/gpio/gpio-vt8500.c        |   24 +++++++++++
 drivers/gpio/gpio-xilinx.c        |   44 ++++++++++++++++++++
 drivers/pinctrl/pinctrl-at91.c    |   29 +++++++++++++
 drivers/pinctrl/pinctrl-nomadik.c |   36 ++++++++++++++++
 25 files changed, 887 insertions(+)

--- linux-2.6.orig/drivers/gpio/Kconfig
+++ linux-2.6/drivers/gpio/Kconfig
@@ -144,11 +144,13 @@ config GPIO_MM_LANTIQ
 config GPIO_MPC5200
 	def_bool y
 	depends on PPC_MPC52xx
+	select BITREVERSE
 
 config GPIO_MPC8XXX
 	bool "MPC512x/MPC8xxx GPIO support"
 	depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \
 		   FSL_SOC_BOOKE || PPC_86xx
+	select BITREVERSE
 	help
 	  Say Y here if you're going to use hardware that connects to the
 	  MPC512x/831x/834x/837x/8572/8610 GPIOs.
--- linux-2.6.orig/drivers/gpio/gpio-em.c
+++ linux-2.6/drivers/gpio/gpio-em.c
@@ -202,6 +202,27 @@ static void em_gio_set(struct gpio_chip
 		__em_gio_set(chip, GIO_OH, offset - 16, value);
 }
 
+static unsigned long em_gio_get_block(struct gpio_chip *chip,
+				      unsigned long mask)
+{
+	return (int)(em_gio_read(gpio_to_priv(chip), GIO_I) & mask);
+}
+
+static void em_gio_set_block(struct gpio_chip *chip, unsigned long mask,
+			     unsigned long values)
+{
+	unsigned long mask_ol = mask & 0xFFFF;
+	unsigned long mask_oh = mask >> 16;
+
+	unsigned long values_ol = values & mask_ol;
+	unsigned long values_oh = (values >> 16) & mask_oh;
+
+	em_gio_write(gpio_to_priv(chip), GIO_OL,
+		     mask_ol << 16 | values_ol);
+	em_gio_write(gpio_to_priv(chip), GIO_OH,
+		     mask_oh << 16 | values_oh);
+}
+
 static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset,
 				   int value)
 {
@@ -282,8 +303,10 @@ static int em_gio_probe(struct platform_
 	gpio_chip = &p->gpio_chip;
 	gpio_chip->direction_input = em_gio_direction_input;
 	gpio_chip->get = em_gio_get;
+	gpio_chip->get_block = em_gio_get_block;
 	gpio_chip->direction_output = em_gio_direction_output;
 	gpio_chip->set = em_gio_set;
+	gpio_chip->set_block = em_gio_set_block;
 	gpio_chip->to_irq = em_gio_to_irq;
 	gpio_chip->label = name;
 	gpio_chip->owner = THIS_MODULE;
--- linux-2.6.orig/drivers/gpio/gpio-ge.c
+++ linux-2.6/drivers/gpio/gpio-ge.c
@@ -100,6 +100,29 @@ static void gef_gpio_set(struct gpio_chi
 	_gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value);
 }
 
+static unsigned long gef_gpio_get_block(struct gpio_chip *chip,
+					unsigned long mask)
+{
+	struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip);
+
+	return ioread32be(mmchip->regs + GEF_GPIO_IN) & mask;
+}
+
+static void gef_gpio_set_block(struct gpio_chip *chip,
+			       unsigned long mask, unsigned long values)
+{
+	struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip);
+	void __iomem *reg = mmchip->regs + GEF_GPIO_OUT;
+	unsigned int data;
+
+	data = ioread32be(reg);
+
+	data &= ~mask;
+	data |= values & mask;
+
+	iowrite32be(data, reg);
+}
+
 static int __init gef_gpio_init(void)
 {
 	struct device_node *np;
@@ -125,6 +148,8 @@ static int __init gef_gpio_init(void)
 		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out;
 		gef_gpio_chip->gc.get = gef_gpio_get;
 		gef_gpio_chip->gc.set = gef_gpio_set;
+		gef_gpio_chip->gc.get_block = gef_gpio_get_block;
+		gef_gpio_chip->gc.set_block = gef_gpio_set_block;
 
 		/* This function adds a memory mapped GPIO chip */
 		retval = of_mm_gpiochip_add(np, gef_gpio_chip);
@@ -153,6 +178,8 @@ static int __init gef_gpio_init(void)
 		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out;
 		gef_gpio_chip->gc.get = gef_gpio_get;
 		gef_gpio_chip->gc.set = gef_gpio_set;
+		gef_gpio_chip->gc.get_block = gef_gpio_get_block;
+		gef_gpio_chip->gc.set_block = gef_gpio_set_block;
 
 		/* This function adds a memory mapped GPIO chip */
 		retval = of_mm_gpiochip_add(np, gef_gpio_chip);
@@ -181,6 +208,8 @@ static int __init gef_gpio_init(void)
 		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out;
 		gef_gpio_chip->gc.get = gef_gpio_get;
 		gef_gpio_chip->gc.set = gef_gpio_set;
+		gef_gpio_chip->gc.get_block = gef_gpio_get_block;
+		gef_gpio_chip->gc.set_block = gef_gpio_set_block;
 
 		/* This function adds a memory mapped GPIO chip */
 		retval = of_mm_gpiochip_add(np, gef_gpio_chip);
--- linux-2.6.orig/drivers/gpio/gpio-generic.c
+++ linux-2.6/drivers/gpio/gpio-generic.c
@@ -122,6 +122,13 @@ static int bgpio_get(struct gpio_chip *g
 	return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio);
 }
 
+static unsigned long bgpio_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+	return bgc->read_reg(bgc->reg_dat) & mask;
+}
+
 static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
 {
 	struct bgpio_chip *bgc = to_bgpio_chip(gc);
@@ -170,6 +177,51 @@ static void bgpio_set_set(struct gpio_ch
 	spin_unlock_irqrestore(&bgc->lock, flags);
 }
 
+static void bgpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bgc->lock, flags);
+
+	bgc->data &= ~mask;
+	bgc->data |= values & mask;
+
+	bgc->write_reg(bgc->reg_dat, bgc->data);
+
+	spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static void bgpio_set_with_clear_block(struct gpio_chip *gc, unsigned long mask,
+				       unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long set_bits = values & mask;
+	unsigned long clr_bits = ~values & mask;
+
+	if (set_bits)
+		bgc->write_reg(bgc->reg_set, set_bits);
+	if (clr_bits)
+		bgc->write_reg(bgc->reg_set, clr_bits);
+}
+
+static void bgpio_set_set_block(struct gpio_chip *gc, unsigned long mask,
+				unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bgc->lock, flags);
+
+	bgc->data &= ~mask;
+	bgc->data |= values & mask;
+
+	bgc->write_reg(bgc->reg_set, bgc->data);
+
+	spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
 static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	return 0;
@@ -317,14 +369,18 @@ static int bgpio_setup_io(struct bgpio_c
 		bgc->reg_set = set;
 		bgc->reg_clr = clr;
 		bgc->gc.set = bgpio_set_with_clear;
+		bgc->gc.set_block = bgpio_set_with_clear_block;
 	} else if (set && !clr) {
 		bgc->reg_set = set;
 		bgc->gc.set = bgpio_set_set;
+		bgc->gc.set_block = bgpio_set_set_block;
 	} else {
 		bgc->gc.set = bgpio_set;
+		bgc->gc.set_block = bgpio_set_block;
 	}
 
 	bgc->gc.get = bgpio_get;
+	bgc->gc.get_block = bgpio_get_block;
 
 	return 0;
 }
--- linux-2.6.orig/drivers/gpio/gpio-ks8695.c
+++ linux-2.6/drivers/gpio/gpio-ks8695.c
@@ -195,6 +195,38 @@ static int ks8695_gpio_get_value(struct
 
 
 /*
+ * Set the state of GPIO lines simultaneously.
+ */
+static void ks8695_gpio_set_block(struct gpio_chip *gc,
+				  unsigned long mask, unsigned long values)
+{
+	unsigned long x, flags;
+
+	local_irq_save(flags);
+
+	/* set output line state */
+	x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
+
+	x &= ~mask;
+	x |= values & mask;
+
+	__raw_writel(x, KS8695_GPIO_VA + KS8695_IOPD);
+
+	local_irq_restore(flags);
+}
+
+
+/*
+ * Read the state of GPIO lines simultaneously.
+ */
+static unsigned long ks8695_gpio_get_block(struct gpio_chip *gc,
+					   unsigned long mask)
+{
+	return __raw_readl(KS8695_GPIO_VA + KS8695_IOPD) & mask;
+}
+
+
+/*
  * Map GPIO line to IRQ number.
  */
 static int ks8695_gpio_to_irq(struct gpio_chip *gc, unsigned int pin)
@@ -225,6 +257,8 @@ static struct gpio_chip ks8695_gpio_chip
 	.direction_output	= ks8695_gpio_direction_output,
 	.get			= ks8695_gpio_get_value,
 	.set			= ks8695_gpio_set_value,
+	.get_block		= ks8695_gpio_get_block,
+	.set_block		= ks8695_gpio_set_block,
 	.to_irq			= ks8695_gpio_to_irq,
 	.base			= 0,
 	.ngpio			= 16,
--- linux-2.6.orig/drivers/gpio/gpio-lpc32xx.c
+++ linux-2.6/drivers/gpio/gpio-lpc32xx.c
@@ -297,6 +297,22 @@ static int lpc32xx_gpio_get_value_p3(str
 	return __get_gpio_state_p3(group, pin);
 }
 
+static unsigned long lpc32xx_gpio_get_block_p3(struct gpio_chip *chip,
+					       unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->inp_state);
+
+	/* In P3_INP_STATE, the 6 GPIOs are scattered over the register,
+	 * rearranging to bits 0-5
+	 */
+	bits >>= 10;
+	bits &= 0x401F;
+	bits |= (bits & 0x4000) >> 9;
+
+	return bits & mask & 0x3F;
+}
+
 static int lpc32xx_gpi_get_value(struct gpio_chip *chip, unsigned pin)
 {
 	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
@@ -304,6 +320,15 @@ static int lpc32xx_gpi_get_value(struct
 	return __get_gpi_state_p3(group, pin);
 }
 
+static unsigned long lpc32xx_gpi_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->inp_state);
+
+	return bits & mask;
+}
+
 static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
 	int value)
 {
@@ -351,6 +376,27 @@ static void lpc32xx_gpio_set_value_p3(st
 	__set_gpio_level_p3(group, pin, value);
 }
 
+static void lpc32xx_gpio_set_block_p3(struct gpio_chip *chip,
+				      unsigned long mask,
+				      unsigned long values)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 set_bits = values & mask;
+	u32 clr_bits = ~values & mask;
+
+	/* States of GPIO 0-5 start at bit 25 */
+	set_bits <<= 25;
+	clr_bits <<= 25;
+
+	/* Note: On LPC32xx, GPOs can only be set at once or cleared at once,
+	 *       but not set and cleared@once
+	 */
+	if (set_bits)
+		__raw_writel(set_bits, group->gpio_grp->outp_set);
+	if (clr_bits)
+		__raw_writel(clr_bits, group->gpio_grp->outp_clr);
+}
+
 static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin,
 	int value)
 {
@@ -366,6 +412,31 @@ static int lpc32xx_gpo_get_value(struct
 	return __get_gpo_state_p3(group, pin);
 }
 
+static void lpc32xx_gpo_set_block(struct gpio_chip *chip, unsigned long mask,
+				  unsigned long values)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 set_bits = values & mask;
+	u32 clr_bits = ~values & mask;
+
+	/* Note: On LPC32xx, GPOs can only be set at once or cleared at once,
+	 *       but not set and cleared at once
+	 */
+	if (set_bits)
+		__raw_writel(set_bits, group->gpio_grp->outp_set);
+	if (clr_bits)
+		__raw_writel(clr_bits, group->gpio_grp->outp_clr);
+}
+
+static unsigned long lpc32xx_gpo_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->outp_state);
+
+	return bits & mask;
+}
+
 static int lpc32xx_gpio_request(struct gpio_chip *chip, unsigned pin)
 {
 	if (pin < chip->ngpio)
@@ -440,8 +511,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p0",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_p01,
 			.base			= LPC32XX_GPIO_P0_GRP,
@@ -456,8 +529,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p1",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_p01,
 			.base			= LPC32XX_GPIO_P1_GRP,
@@ -472,8 +547,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p2",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.base			= LPC32XX_GPIO_P2_GRP,
 			.ngpio			= LPC32XX_GPIO_P2_MAX,
@@ -487,8 +564,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p3",
 			.direction_input	= lpc32xx_gpio_dir_input_p3,
 			.get			= lpc32xx_gpio_get_value_p3,
+			.get_block		= lpc32xx_gpio_get_block_p3,
 			.direction_output	= lpc32xx_gpio_dir_output_p3,
 			.set			= lpc32xx_gpio_set_value_p3,
+			.set_block		= lpc32xx_gpio_set_block_p3,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_gpio_p3,
 			.base			= LPC32XX_GPIO_P3_GRP,
@@ -503,6 +582,7 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpi_p3",
 			.direction_input	= lpc32xx_gpio_dir_in_always,
 			.get			= lpc32xx_gpi_get_value,
+			.get_block		= lpc32xx_gpi_get_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_gpi_p3,
 			.base			= LPC32XX_GPI_P3_GRP,
@@ -517,7 +597,9 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpo_p3",
 			.direction_output	= lpc32xx_gpio_dir_out_always,
 			.set			= lpc32xx_gpo_set_value,
+			.set_block		= lpc32xx_gpo_set_block,
 			.get			= lpc32xx_gpo_get_value,
+			.get_block		= lpc32xx_gpo_get_block,
 			.request		= lpc32xx_gpio_request,
 			.base			= LPC32XX_GPO_P3_GRP,
 			.ngpio			= LPC32XX_GPO_P3_MAX,
--- linux-2.6.orig/drivers/gpio/gpio-max730x.c
+++ linux-2.6/drivers/gpio/gpio-max730x.c
@@ -146,6 +146,44 @@ static int max7301_get(struct gpio_chip
 	return level;
 }
 
+static unsigned long max7301_get_block(struct gpio_chip *chip,
+				       unsigned long mask)
+{
+	struct max7301 *ts = container_of(chip, struct max7301, chip);
+	int i, j;
+	unsigned long result = 0;
+
+	for (i = 0; i < 4; i++) {
+		if ((mask >> (i * 8)) & 0xFF) { /* i/o only if necessary */
+			u8 in_level = ts->read(ts->dev, 0x44 + i * 8);
+			u8 in_mask = 0;
+			u8 out_level = (ts->out_level >> (i * 8 + 4)) & 0xFF;
+			u8 out_mask = 0;
+
+			for (j = 0; j < 8; j++) {
+				int offset = 4 + i * 8 + j;
+				int config = (ts->port_config[offset >> 2] >>
+					      ((offset & 3) << 1)) &
+					PIN_CONFIG_MASK;
+
+				switch (config) {
+				case PIN_CONFIG_OUT:
+					out_mask |= BIT(j);
+					break;
+				case PIN_CONFIG_IN_WO_PULLUP:
+				case PIN_CONFIG_IN_PULLUP:
+					in_mask |= BIT(j);
+				}
+			}
+
+			result |= ((unsigned long)(in_level & in_mask) |
+				   (out_level & out_mask)) << (i * 8);
+		}
+	}
+
+	return result & mask;
+}
+
 static void max7301_set(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct max7301 *ts = container_of(chip, struct max7301, chip);
@@ -160,6 +198,27 @@ static void max7301_set(struct gpio_chip
 	mutex_unlock(&ts->lock);
 }
 
+static void max7301_set_block(struct gpio_chip *chip, unsigned long mask,
+			      unsigned long values)
+{
+	struct max7301 *ts = container_of(chip, struct max7301, chip);
+	unsigned long changes;
+	int i;
+
+	mutex_lock(&ts->lock);
+
+	changes = (ts->out_level ^ (values << 4)) & (mask << 4);
+	ts->out_level ^= changes;
+
+	for (i = 0; i < 4; i++) {
+		if ((changes >> (i * 8 + 4)) & 0xFF) /* i/o only on change */
+			ts->write(ts->dev, 0x44 + i * 8,
+				  (ts->out_level >> (i * 8 + 4)) & 0xFF);
+	}
+
+	mutex_unlock(&ts->lock);
+}
+
 int __max730x_probe(struct max7301 *ts)
 {
 	struct device *dev = ts->dev;
@@ -184,8 +243,10 @@ int __max730x_probe(struct max7301 *ts)
 
 	ts->chip.direction_input = max7301_direction_input;
 	ts->chip.get = max7301_get;
+	ts->chip.get_block = max7301_get_block;
 	ts->chip.direction_output = max7301_direction_output;
 	ts->chip.set = max7301_set;
+	ts->chip.set_block = max7301_set_block;
 
 	ts->chip.ngpio = PIN_NUMBER;
 	ts->chip.can_sleep = 1;
--- linux-2.6.orig/drivers/gpio/gpio-max732x.c
+++ linux-2.6/drivers/gpio/gpio-max732x.c
@@ -219,6 +219,63 @@ out:
 	mutex_unlock(&chip->lock);
 }
 
+static unsigned long max732x_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct max732x_chip *chip;
+	uint8_t lo = 0;
+	uint8_t hi = 0;
+	int ret;
+
+	chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+	if (mask & 0xFF) {
+		ret = max732x_readb(chip, is_group_a(chip, 0), &lo);
+		if (ret < 0)
+			return 0;
+	}
+	if (mask & 0xFF00) {
+		ret = max732x_readb(chip, is_group_a(chip, 8), &hi);
+		if (ret < 0)
+			return 0;
+	}
+
+	return (((unsigned long)hi << 8) | lo) & mask;
+}
+
+static void max732x_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct max732x_chip *chip;
+	uint8_t reg_out;
+	uint8_t lo_mask = mask & 0xFF;
+	uint8_t hi_mask = (mask >> 8) & 0xFF;
+	int ret;
+
+	chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+	mutex_lock(&chip->lock);
+
+	if (lo_mask) {
+		reg_out = (chip->reg_out[0] & ~lo_mask) | (values & lo_mask);
+		ret = max732x_writeb(chip, is_group_a(chip, 0), reg_out);
+		if (ret < 0)
+			goto out;
+		chip->reg_out[0] = reg_out;
+	}
+	if (hi_mask) {
+		reg_out = (chip->reg_out[1] & ~hi_mask) |
+			((values >> 8) & hi_mask);
+		ret = max732x_writeb(chip, is_group_a(chip, 8), reg_out);
+		if (ret < 0)
+			goto out;
+		chip->reg_out[1] = reg_out;
+	}
+
+out:
+	mutex_unlock(&chip->lock);
+}
+
 static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
 {
 	struct max732x_chip *chip;
@@ -562,8 +619,10 @@ static int max732x_setup_gpio(struct max
 	if (chip->dir_output) {
 		gc->direction_output = max732x_gpio_direction_output;
 		gc->set = max732x_gpio_set_value;
+		gc->set_block = max732x_gpio_set_block;
 	}
 	gc->get = max732x_gpio_get_value;
+	gc->get_block = max732x_gpio_get_block;
 	gc->can_sleep = 1;
 
 	gc->base = gpio_start;
--- linux-2.6.orig/drivers/gpio/gpio-mc33880.c
+++ linux-2.6/drivers/gpio/gpio-mc33880.c
@@ -80,6 +80,21 @@ static void mc33880_set(struct gpio_chip
 	mutex_unlock(&mc->lock);
 }
 
+static void mc33880_set_block(struct gpio_chip *chip, unsigned long mask,
+			      unsigned long values)
+{
+	struct mc33880 *mc = container_of(chip, struct mc33880, chip);
+
+	mutex_lock(&mc->lock);
+
+	mc->port_config &= ~mask;
+	mc->port_config |= values & mask;
+
+	mc33880_write_config(mc);
+
+	mutex_unlock(&mc->lock);
+}
+
 static int mc33880_probe(struct spi_device *spi)
 {
 	struct mc33880 *mc;
@@ -113,6 +128,7 @@ static int mc33880_probe(struct spi_devi
 
 	mc->chip.label = DRIVER_NAME,
 	mc->chip.set = mc33880_set;
+	mc->chip.set_block = mc33880_set_block;
 	mc->chip.base = pdata->base;
 	mc->chip.ngpio = PIN_NUMBER;
 	mc->chip.can_sleep = 1;
--- linux-2.6.orig/drivers/gpio/gpio-ml-ioh.c
+++ linux-2.6/drivers/gpio/gpio-ml-ioh.c
@@ -127,6 +127,31 @@ static int ioh_gpio_get(struct gpio_chip
 	return ioread32(&chip->reg->regs[chip->ch].pi) & (1 << nr);
 }
 
+static void ioh_gpio_set_block(struct gpio_chip *gpio, unsigned long mask,
+			       unsigned long values)
+{
+	u32 reg_val;
+	struct ioh_gpio *chip =	container_of(gpio, struct ioh_gpio, gpio);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->spinlock, flags);
+	reg_val = ioread32(&chip->reg->regs[chip->ch].po);
+
+	reg_val &= ~mask;
+	reg_val |= values & mask;
+
+	iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
+	spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
+static unsigned long ioh_gpio_get_block(struct gpio_chip *gpio,
+					unsigned long mask)
+{
+	struct ioh_gpio *chip =	container_of(gpio, struct ioh_gpio, gpio);
+
+	return ioread32(&chip->reg->regs[chip->ch].pi) & mask;
+}
+
 static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
 				     int val)
 {
@@ -237,8 +262,10 @@ static void ioh_gpio_setup(struct ioh_gp
 	gpio->owner = THIS_MODULE;
 	gpio->direction_input = ioh_gpio_direction_input;
 	gpio->get = ioh_gpio_get;
+	gpio->get_block = ioh_gpio_get_block;
 	gpio->direction_output = ioh_gpio_direction_output;
 	gpio->set = ioh_gpio_set;
+	gpio->set_block = ioh_gpio_set_block;
 	gpio->dbg_show = NULL;
 	gpio->base = -1;
 	gpio->ngpio = num_port;
--- linux-2.6.orig/drivers/gpio/gpio-mm-lantiq.c
+++ linux-2.6/drivers/gpio/gpio-mm-lantiq.c
@@ -73,6 +73,27 @@ static void ltq_mm_set(struct gpio_chip
 }
 
 /**
+ * ltq_mm_set_block() - gpio_chip->set_block - set gpios simultaneously.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @mask:   Bit map of masked GPIOs in this gpio_chip.
+ * @values: Values to be written to the specified signals.
+ *
+ * Set the shadow values and call ltq_mm_apply.
+ */
+static void ltq_mm_set_block(struct gpio_chip *gc, unsigned long mask,
+			     unsigned long values)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct ltq_mm *chip =
+		container_of(mm_gc, struct ltq_mm, mmchip);
+
+	chip->shadow &= ~mask;
+	chip->shadow |= values & mask;
+
+	ltq_mm_apply(chip);
+}
+
+/**
  * ltq_mm_dir_out() - gpio_chip->dir_out - set gpio direction.
  * @gc:     Pointer to gpio_chip device structure.
  * @gpio:   GPIO signal number.
@@ -122,6 +143,7 @@ static int ltq_mm_probe(struct platform_
 	chip->mmchip.gc.label = "gpio-mm-ltq";
 	chip->mmchip.gc.direction_output = ltq_mm_dir_out;
 	chip->mmchip.gc.set = ltq_mm_set;
+	chip->mmchip.gc.set_block = ltq_mm_set_block;
 	chip->mmchip.save_regs = ltq_mm_save_regs;
 
 	/* store the shadow value if one was passed by the devicetree */
--- linux-2.6.orig/drivers/gpio/gpio-mpc5200.c
+++ linux-2.6/drivers/gpio/gpio-mpc5200.c
@@ -24,6 +24,7 @@
 #include <linux/io.h>
 #include <linux/of_platform.h>
 #include <linux/module.h>
+#include <linux/bitrev.h>
 
 #include <asm/gpio.h>
 #include <asm/mpc52xx.h>
@@ -97,6 +98,35 @@ mpc52xx_wkup_gpio_set(struct gpio_chip *
 	pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
 }
 
+static unsigned long mpc52xx_wkup_gpio_get_block(struct gpio_chip *gc,
+						 unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+
+	return bitrev8(in_8(&regs->wkup_ival)) & mask;
+}
+
+static void
+mpc52xx_wkup_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+			struct mpc52xx_gpiochip, mmchip);
+	struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip->shadow_dvo &= ~bitrev8(mask);
+	chip->shadow_dvo |= bitrev8(values & mask);
+
+	out_8(&regs->wkup_dvo, chip->shadow_dvo);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
 static int mpc52xx_wkup_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
@@ -166,6 +196,8 @@ static int mpc52xx_wkup_gpiochip_probe(s
 	gc->direction_output = mpc52xx_wkup_gpio_dir_out;
 	gc->get              = mpc52xx_wkup_gpio_get;
 	gc->set              = mpc52xx_wkup_gpio_set;
+	gc->get_block        = mpc52xx_wkup_gpio_get_block;
+	gc->set_block        = mpc52xx_wkup_gpio_set_block;
 
 	ret = of_mm_gpiochip_add(ofdev->dev.of_node, &chip->mmchip);
 	if (ret)
@@ -256,6 +288,36 @@ mpc52xx_simple_gpio_set(struct gpio_chip
 	pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
 }
 
+static unsigned long mpc52xx_simple_gpio_get_block(struct gpio_chip *gc,
+						   unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+
+	return bitrev32(in_be32(&regs->simple_ival)) & mask;
+}
+
+static void
+mpc52xx_simple_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			      unsigned long values)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+						     struct mpc52xx_gpiochip,
+						     mmchip);
+	struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip->shadow_dvo &= ~bitrev32(mask);
+	chip->shadow_dvo |= bitrev32(values & mask);
+
+	out_be32(&regs->simple_dvo, chip->shadow_dvo);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
 static int mpc52xx_simple_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
@@ -326,6 +388,8 @@ static int mpc52xx_simple_gpiochip_probe
 	gc->direction_output = mpc52xx_simple_gpio_dir_out;
 	gc->get              = mpc52xx_simple_gpio_get;
 	gc->set              = mpc52xx_simple_gpio_set;
+	gc->get_block        = mpc52xx_simple_gpio_get_block;
+	gc->set_block        = mpc52xx_simple_gpio_set_block;
 
 	ret = of_mm_gpiochip_add(ofdev->dev.of_node, &chip->mmchip);
 	if (ret)
--- linux-2.6.orig/drivers/gpio/gpio-mpc8xxx.c
+++ linux-2.6/drivers/gpio/gpio-mpc8xxx.c
@@ -17,6 +17,7 @@
 #include <linux/gpio.h>
 #include <linux/slab.h>
 #include <linux/irq.h>
+#include <linux/bitrev.h>
 
 #define MPC8XXX_GPIO_PINS	32
 
@@ -100,6 +101,43 @@ static void mpc8xxx_gpio_set(struct gpio
 	spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
 }
 
+static unsigned long mpc8572_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	u32 val;
+	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+	struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+
+	val = in_be32(mm->regs + GPIO_DAT) & ~in_be32(mm->regs + GPIO_DIR);
+
+	return bitrev32(val | mpc8xxx_gc->data) & mask;
+}
+
+static unsigned long mpc8xxx_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+
+	return bitrev32(in_be32(mm->regs + GPIO_DAT)) & mask;
+}
+
+static void mpc8xxx_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+	struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+	unsigned long flags;
+
+	spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+	mpc8xxx_gc->data &= ~bitrev32(mask);
+	mpc8xxx_gc->data |= bitrev32(values | mask);
+
+	out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data);
+
+	spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+}
+
 static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
@@ -340,6 +378,9 @@ static void __init mpc8xxx_add_controlle
 	gc->get = of_device_is_compatible(np, "fsl,mpc8572-gpio") ?
 		mpc8572_gpio_get : mpc8xxx_gpio_get;
 	gc->set = mpc8xxx_gpio_set;
+	gc->get_block = of_device_is_compatible(np, "fsl,mpc8572-gpio") ?
+		mpc8572_gpio_get_block : mpc8xxx_gpio_get_block;
+	gc->set_block = mpc8xxx_gpio_set_block;
 	gc->to_irq = mpc8xxx_gpio_to_irq;
 
 	ret = of_mm_gpiochip_add(np, mm_gc);
--- linux-2.6.orig/drivers/gpio/gpio-pca953x.c
+++ linux-2.6/drivers/gpio/gpio-pca953x.c
@@ -302,6 +302,68 @@ exit:
 	mutex_unlock(&chip->i2c_lock);
 }
 
+static unsigned long pca953x_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct pca953x_chip *chip;
+	u32 reg_val;
+	int ret, offset = 0;
+
+	chip = container_of(gc, struct pca953x_chip, gpio_chip);
+
+	mutex_lock(&chip->i2c_lock);
+	switch (chip->chip_type) {
+	case PCA953X_TYPE:
+		offset = PCA953X_INPUT;
+		break;
+	case PCA957X_TYPE:
+		offset = PCA957X_IN;
+		break;
+	}
+	ret = pca953x_read_reg(chip, offset, &reg_val);
+	mutex_unlock(&chip->i2c_lock);
+	if (ret < 0) {
+		/* NOTE:  diagnostic already emitted; that's all we should
+		 * do unless gpio_*_value_cansleep() calls become different
+		 * from their nonsleeping siblings (and report faults).
+		 */
+		return 0;
+	}
+
+	return reg_val & mask;
+}
+
+static void pca953x_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct pca953x_chip *chip;
+	u32 reg_val;
+	int ret, offset = 0;
+
+	chip = container_of(gc, struct pca953x_chip, gpio_chip);
+
+	mutex_lock(&chip->i2c_lock);
+
+	reg_val = chip->reg_output & ~mask;
+	reg_val |= values & mask;
+
+	switch (chip->chip_type) {
+	case PCA953X_TYPE:
+		offset = PCA953X_OUTPUT;
+		break;
+	case PCA957X_TYPE:
+		offset = PCA957X_OUT;
+		break;
+	}
+	ret = pca953x_write_reg(chip, offset, reg_val);
+	if (ret)
+		goto exit;
+
+	chip->reg_output = reg_val;
+exit:
+	mutex_unlock(&chip->i2c_lock);
+}
+
 static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
 {
 	struct gpio_chip *gc;
@@ -312,6 +374,8 @@ static void pca953x_setup_gpio(struct pc
 	gc->direction_output = pca953x_gpio_direction_output;
 	gc->get = pca953x_gpio_get_value;
 	gc->set = pca953x_gpio_set_value;
+	gc->get_block = pca953x_gpio_get_block;
+	gc->set_block = pca953x_gpio_set_block;
 	gc->can_sleep = 1;
 
 	gc->base = chip->gpio_start;
--- linux-2.6.orig/drivers/gpio/gpio-pcf857x.c
+++ linux-2.6/drivers/gpio/gpio-pcf857x.c
@@ -158,6 +158,28 @@ static void pcf857x_set(struct gpio_chip
 	pcf857x_output(chip, offset, value);
 }
 
+static unsigned long pcf857x_get_block(struct gpio_chip *chip,
+				       unsigned long mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		value;
+
+	value = gpio->read(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
+static void pcf857x_set_block(struct gpio_chip *chip, unsigned long mask,
+			      unsigned long values)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= values & mask;
+	gpio->write(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+}
+
 /*-------------------------------------------------------------------------*/
 
 static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -280,6 +302,8 @@ static int pcf857x_probe(struct i2c_clie
 	gpio->chip.owner		= THIS_MODULE;
 	gpio->chip.get			= pcf857x_get;
 	gpio->chip.set			= pcf857x_set;
+	gpio->chip.get_block		= pcf857x_get_block;
+	gpio->chip.set_block		= pcf857x_set_block;
 	gpio->chip.direction_input	= pcf857x_input;
 	gpio->chip.direction_output	= pcf857x_output;
 	gpio->chip.ngpio		= id->driver_data;
--- linux-2.6.orig/drivers/gpio/gpio-pch.c
+++ linux-2.6/drivers/gpio/gpio-pch.c
@@ -122,6 +122,23 @@ static void pch_gpio_set(struct gpio_chi
 	spin_unlock_irqrestore(&chip->spinlock, flags);
 }
 
+static void pch_gpio_set_block(struct gpio_chip *gpio, unsigned long mask,
+			       unsigned long values)
+{
+	u32 reg_val;
+	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->spinlock, flags);
+	reg_val = ioread32(&chip->reg->po);
+
+	reg_val &= ~mask;
+	reg_val |= values & mask;
+
+	iowrite32(reg_val, &chip->reg->po);
+	spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
 static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr)
 {
 	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
@@ -129,6 +146,14 @@ static int pch_gpio_get(struct gpio_chip
 	return ioread32(&chip->reg->pi) & (1 << nr);
 }
 
+static unsigned long pch_gpio_get_block(struct gpio_chip *gpio,
+					unsigned long mask)
+{
+	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
+
+	return ioread32(&chip->reg->pi) & mask;
+}
+
 static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
 				     int val)
 {
@@ -219,8 +244,10 @@ static void pch_gpio_setup(struct pch_gp
 	gpio->owner = THIS_MODULE;
 	gpio->direction_input = pch_gpio_direction_input;
 	gpio->get = pch_gpio_get;
+	gpio->get_block = pch_gpio_get_block;
 	gpio->direction_output = pch_gpio_direction_output;
 	gpio->set = pch_gpio_set;
+	gpio->set_block = pch_gpio_set_block;
 	gpio->dbg_show = NULL;
 	gpio->base = -1;
 	gpio->ngpio = gpio_pins[chip->ioh];
--- linux-2.6.orig/drivers/gpio/gpio-pl061.c
+++ linux-2.6/drivers/gpio/gpio-pl061.c
@@ -118,6 +118,21 @@ static void pl061_set_value(struct gpio_
 	writeb(!!value << offset, chip->base + (1 << (offset + 2)));
 }
 
+static unsigned long pl061_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+	return !!readb(chip->base + (mask << 2));
+}
+
+static void pl061_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+	writeb(values & mask, chip->base + (mask << 2));
+}
+
 static int pl061_to_irq(struct gpio_chip *gc, unsigned offset)
 {
 	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
@@ -246,6 +261,8 @@ static int pl061_probe(struct amba_devic
 	chip->gc.direction_output = pl061_direction_output;
 	chip->gc.get = pl061_get_value;
 	chip->gc.set = pl061_set_value;
+	chip->gc.get_block = pl061_get_block;
+	chip->gc.set_block = pl061_set_block;
 	chip->gc.to_irq = pl061_to_irq;
 	chip->gc.ngpio = PL061_GPIO_NR;
 	chip->gc.label = dev_name(dev);
--- linux-2.6.orig/drivers/gpio/gpio-sa1100.c
+++ linux-2.6/drivers/gpio/gpio-sa1100.c
@@ -27,6 +27,24 @@ static void sa1100_gpio_set(struct gpio_
 		GPCR = GPIO_GPIO(offset);
 }
 
+static unsigned long sa1100_gpio_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	return GPLR & mask;
+}
+
+static void sa1100_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+				  unsigned long values)
+{
+	unsigned long set_bits = values & mask;
+	unsigned long clr_bits = ~values & mask;
+
+	if (set_bits)
+		GPSR = set_bits;
+	if (clr_bits)
+		GPCR = clr_bits;
+}
+
 static int sa1100_direction_input(struct gpio_chip *chip, unsigned offset)
 {
 	unsigned long flags;
@@ -59,6 +77,8 @@ static struct gpio_chip sa1100_gpio_chip
 	.direction_output	= sa1100_direction_output,
 	.set			= sa1100_gpio_set,
 	.get			= sa1100_gpio_get,
+	.set_block		= sa1100_gpio_set_block,
+	.get_block		= sa1100_gpio_get_block,
 	.to_irq			= sa1100_to_irq,
 	.base			= 0,
 	.ngpio			= GPIO_MAX + 1,
--- linux-2.6.orig/drivers/gpio/gpio-samsung.c
+++ linux-2.6/drivers/gpio/gpio-samsung.c
@@ -862,6 +862,33 @@ static int samsung_gpiolib_get(struct gp
 	return val;
 }
 
+static void samsung_gpiolib_set_block(struct gpio_chip *chip,
+				      unsigned long mask,
+				      unsigned long values)
+{
+	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+	void __iomem *base = ourchip->base;
+	unsigned long flags;
+	unsigned long dat;
+
+	samsung_gpio_lock(ourchip, flags);
+
+	dat = __raw_readl(base + 0x04);
+	dat &= ~mask;
+	dat |= values & mask;
+	__raw_writel(dat, base + 0x04);
+
+	samsung_gpio_unlock(ourchip, flags);
+}
+
+static unsigned long samsung_gpiolib_get_block(struct gpio_chip *chip,
+					       unsigned long mask)
+{
+	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+
+	return __raw_readl(ourchip->base + 0x04) & mask;
+}
+
 /*
  * CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios
  * for use with the configuration calls, and other parts of the s3c gpiolib
@@ -919,6 +946,10 @@ static void __init samsung_gpiolib_add(s
 		gc->set = samsung_gpiolib_set;
 	if (!gc->get)
 		gc->get = samsung_gpiolib_get;
+	if (!gc->set_block)
+		gc->set_block = samsung_gpiolib_set_block;
+	if (!gc->get_block)
+		gc->get_block = samsung_gpiolib_get_block;
 
 #ifdef CONFIG_PM
 	if (chip->pm != NULL) {
--- linux-2.6.orig/drivers/gpio/gpio-twl6040.c
+++ linux-2.6/drivers/gpio/gpio-twl6040.c
@@ -46,6 +46,19 @@ static int twl6040gpo_get(struct gpio_ch
 	return (ret >> offset) & 1;
 }
 
+static unsigned long twl6040gpo_get_block(struct gpio_chip *chip,
+					  unsigned long mask)
+{
+	struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+	int ret = 0;
+
+	ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+	if (ret < 0)
+		return 0;
+
+	return ret & mask;
+}
+
 static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset,
 				    int value)
 {
@@ -71,12 +84,31 @@ static void twl6040gpo_set(struct gpio_c
 	twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
 }
 
+static void twl6040gpo_set_block(struct gpio_chip *chip, unsigned long mask,
+				 unsigned long values)
+{
+	struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+	int ret;
+	u8 gpoctl;
+
+	ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+	if (ret < 0)
+		return;
+
+	gpoctl = ret & ~mask;
+	gpoctl |= values & mask;
+
+	twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
+}
+
 static struct gpio_chip twl6040gpo_chip = {
 	.label			= "twl6040",
 	.owner			= THIS_MODULE,
 	.get			= twl6040gpo_get,
+	.get_block		= twl6040gpo_get_block,
 	.direction_output	= twl6040gpo_direction_out,
 	.set			= twl6040gpo_set,
+	.set_block		= twl6040gpo_set_block,
 	.can_sleep		= 1,
 };
 
--- linux-2.6.orig/drivers/gpio/gpio-ucb1400.c
+++ linux-2.6/drivers/gpio/gpio-ucb1400.c
@@ -45,6 +45,27 @@ static void ucb1400_gpio_set(struct gpio
 	ucb1400_gpio_set_value(gpio->ac97, off, val);
 }
 
+static unsigned long ucb1400_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct ucb1400_gpio *gpio;
+	gpio = container_of(gc, struct ucb1400_gpio, gc);
+	return ucb1400_reg_read(gpio->ac97, UCB_IO_DATA) & mask;
+}
+
+static void ucb1400_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct ucb1400_gpio *gpio;
+	u16 tmp;
+	gpio = container_of(gc, struct ucb1400_gpio, gc);
+
+	tmp = ucb1400_reg_read(gpio->ac97, UCB_IO_DATA) & ~mask;
+	tmp |= values & mask;
+
+	ucb1400_reg_write(gpio->ac97, UCB_IO_DATA, tmp);
+}
+
 static int ucb1400_gpio_probe(struct platform_device *dev)
 {
 	struct ucb1400_gpio *ucb = dev->dev.platform_data;
@@ -66,6 +87,8 @@ static int ucb1400_gpio_probe(struct pla
 	ucb->gc.direction_output = ucb1400_gpio_dir_out;
 	ucb->gc.get = ucb1400_gpio_get;
 	ucb->gc.set = ucb1400_gpio_set;
+	ucb->gc.get_block = ucb1400_gpio_get_block;
+	ucb->gc.set_block = ucb1400_gpio_set_block;
 	ucb->gc.can_sleep = 1;
 
 	err = gpiochip_add(&ucb->gc);
--- linux-2.6.orig/drivers/gpio/gpio-vt8500.c
+++ linux-2.6/drivers/gpio/gpio-vt8500.c
@@ -211,6 +211,28 @@ static void vt8500_gpio_set_value(struct
 	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
 }
 
+static unsigned long vt8500_gpio_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) &
+								mask;
+}
+
+static void vt8500_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+				  unsigned long  values)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	u32 val = readl_relaxed(vt8500_chip->base +
+				vt8500_chip->regs->data_out);
+	val &= ~mask;
+	val |= values & mask;
+
+	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
+}
+
 static int vt8500_of_xlate(struct gpio_chip *gc,
 			    const struct of_phandle_args *gpiospec, u32 *flags)
 {
@@ -253,6 +275,8 @@ static int vt8500_add_chips(struct platf
 		chip->direction_output = vt8500_gpio_direction_output;
 		chip->get = vt8500_gpio_get_value;
 		chip->set = vt8500_gpio_set_value;
+		chip->get_block = vt8500_gpio_get_block;
+		chip->set_block = vt8500_gpio_set_block;
 		chip->can_sleep = 0;
 		chip->base = pin_cnt;
 		chip->ngpio = data->banks[i].ngpio;
--- linux-2.6.orig/drivers/gpio/gpio-xilinx.c
+++ linux-2.6/drivers/gpio/gpio-xilinx.c
@@ -49,6 +49,21 @@ static int xgpio_get(struct gpio_chip *g
 }
 
 /**
+ * xgpio_get_block - Read a block of specified signals of the GPIO device.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @mask:   Bit mask (bit 0 == 0th GPIO) for GPIOs to get
+ *
+ * This function reads a block of specified signal of the GPIO device, returned
+ * as a bit mask, each bit representing a GPIO
+ */
+static unsigned long xgpio_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+
+	return in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) & mask;
+}
+
+/**
  * xgpio_set - Write the specified signal of the GPIO device.
  * @gc:     Pointer to gpio_chip device structure.
  * @gpio:   GPIO signal number.
@@ -77,6 +92,33 @@ static void xgpio_set(struct gpio_chip *
 }
 
 /**
+ * xgpio_set_block - Write a block of specified signals of the GPIO device.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @mask:   Bit mask (bit 0 == 0th GPIO) for GPIOs to set
+ * @values: Bit mapped values
+ *
+ * This function writes the specified values in to the specified signals of the
+ * GPIO device.
+ */
+static void xgpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	unsigned long flags;
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct xgpio_instance *chip =
+	    container_of(mm_gc, struct xgpio_instance, mmchip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	chip->gpio_state &= ~mask;
+	chip->gpio_state |= mask & values;
+
+	out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/**
  * xgpio_dir_in - Set the direction of the specified GPIO signal as input.
  * @gc:     Pointer to gpio_chip device structure.
  * @gpio:   GPIO signal number.
@@ -195,6 +237,8 @@ static int xgpio_of_probe(struct device_
 	chip->mmchip.gc.direction_output = xgpio_dir_out;
 	chip->mmchip.gc.get = xgpio_get;
 	chip->mmchip.gc.set = xgpio_set;
+	chip->mmchip.gc.get_block = xgpio_get_block;
+	chip->mmchip.gc.set_block = xgpio_set_block;
 
 	chip->mmchip.save_regs = xgpio_save_regs;
 
--- linux-2.6.orig/drivers/pinctrl/pinctrl-at91.c
+++ linux-2.6/drivers/pinctrl/pinctrl-at91.c
@@ -49,6 +49,7 @@ struct at91_gpio_chip {
 	struct clk		*clock;		/* associated clock */
 	struct irq_domain	*domain;	/* associated irq domain */
 	struct at91_pinctrl_mux_ops *ops;	/* ops */
+	unsigned long		mask_cache;	/* cached mask for block gpio */
 };
 
 #define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip)
@@ -1125,6 +1126,32 @@ static void at91_gpio_set(struct gpio_ch
 	writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR));
 }
 
+static unsigned long at91_gpio_get_block(struct gpio_chip *chip,
+					 unsigned long mask)
+{
+	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
+	void __iomem *pio = at91_gpio->regbase;
+	u32 pdsr;
+
+	pdsr = __raw_readl(pio + PIO_PDSR);
+	return pdsr & mask;
+}
+
+static void at91_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+				unsigned long val)
+{
+	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
+	void __iomem *pio = at91_gpio->regbase;
+
+	/* Do synchronous data output with a single write access */
+	if (mask != at91_gpio->mask_cache) {
+		at91_gpio->mask_cache = mask;
+		__raw_writel(~mask, pio + PIO_OWDR);
+		__raw_writel(mask, pio + PIO_OWER);
+	}
+	__raw_writel(val, pio + PIO_ODSR);
+}
+
 static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
 				int val)
 {
@@ -1435,8 +1462,10 @@ static struct gpio_chip at91_gpio_templa
 	.free			= at91_gpio_free,
 	.direction_input	= at91_gpio_direction_input,
 	.get			= at91_gpio_get,
+	.get_block		= at91_gpio_get_block,
 	.direction_output	= at91_gpio_direction_output,
 	.set			= at91_gpio_set,
+	.set_block		= at91_gpio_set_block,
 	.to_irq			= at91_gpio_to_irq,
 	.dbg_show		= at91_gpio_dbg_show,
 	.can_sleep		= 0,
--- linux-2.6.orig/drivers/pinctrl/pinctrl-nomadik.c
+++ linux-2.6/drivers/pinctrl/pinctrl-nomadik.c
@@ -1063,6 +1063,40 @@ static void nmk_gpio_set_output(struct g
 	clk_disable(nmk_chip->clk);
 }
 
+static unsigned long nmk_gpio_get_block(struct gpio_chip *chip,
+					unsigned long mask)
+{
+	struct nmk_gpio_chip *nmk_chip =
+		container_of(chip, struct nmk_gpio_chip, chip);
+	unsigned long values;
+
+	clk_enable(nmk_chip->clk);
+
+	values = readl(nmk_chip->addr + NMK_GPIO_DAT);
+
+	clk_disable(nmk_chip->clk);
+
+	return values & mask;
+}
+
+static void nmk_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+			       unsigned long values)
+{
+	struct nmk_gpio_chip *nmk_chip =
+		container_of(chip, struct nmk_gpio_chip, chip);
+	unsigned long set_bits = values & mask;
+	unsigned long clr_bits = ~values & mask;
+
+	clk_enable(nmk_chip->clk);
+
+	if (set_bits)
+		writel(set_bits, nmk_chip->addr + NMK_GPIO_DATS);
+	if (clr_bits)
+		writel(clr_bits, nmk_chip->addr + NMK_GPIO_DATC);
+
+	clk_disable(nmk_chip->clk);
+}
+
 static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset,
 				int val)
 {
@@ -1182,8 +1216,10 @@ static struct gpio_chip nmk_gpio_templat
 	.free			= nmk_gpio_free,
 	.direction_input	= nmk_gpio_make_input,
 	.get			= nmk_gpio_get_input,
+	.get_block		= nmk_gpio_get_block,
 	.direction_output	= nmk_gpio_make_output,
 	.set			= nmk_gpio_set_output,
+	.set_block		= nmk_gpio_set_block,
 	.to_irq			= nmk_gpio_to_irq,
 	.dbg_show		= nmk_gpio_dbg_show,
 	.can_sleep		= 0,

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH RESEND 6/6 v13] gpio: Add block gpio to several gpio drivers
  2013-01-15 11:51   ` Roland Stigge
@ 2013-01-15 13:18     ` Nicolas Ferre
  -1 siblings, 0 replies; 32+ messages in thread
From: Nicolas Ferre @ 2013-01-15 13:18 UTC (permalink / raw)
  To: Roland Stigge, plagnioj
  Cc: gregkh, grant.likely, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, highguy, broonie, daniel-gl,
	rmallon, sr, wg, tru, mark.rutland

Hi,

Le 15/01/2013 12:51, Roland Stigge a écrit :
> This patch adds block GPIO support to several gpio drivers.
>
> This implements block GPIO only for some selected drivers since block GPIO is
> an optional feature which may not be suitable for every GPIO hardware. (With
> automatic fallback to the single GPIO functions if not available in a driver.)
>
> Signed-off-by: Roland Stigge <stigge@antcom.de>
>
> ---
>   drivers/gpio/Kconfig              |    2
>   drivers/gpio/gpio-em.c            |   23 ++++++++++
>   drivers/gpio/gpio-ge.c            |   29 +++++++++++++
>   drivers/gpio/gpio-generic.c       |   56 +++++++++++++++++++++++++
>   drivers/gpio/gpio-ks8695.c        |   34 +++++++++++++++
>   drivers/gpio/gpio-lpc32xx.c       |   82 ++++++++++++++++++++++++++++++++++++++
>   drivers/gpio/gpio-max730x.c       |   61 ++++++++++++++++++++++++++++
>   drivers/gpio/gpio-max732x.c       |   59 +++++++++++++++++++++++++++
>   drivers/gpio/gpio-mc33880.c       |   16 +++++++
>   drivers/gpio/gpio-ml-ioh.c        |   27 ++++++++++++
>   drivers/gpio/gpio-mm-lantiq.c     |   22 ++++++++++
>   drivers/gpio/gpio-mpc5200.c       |   64 +++++++++++++++++++++++++++++
>   drivers/gpio/gpio-mpc8xxx.c       |   41 +++++++++++++++++++
>   drivers/gpio/gpio-pca953x.c       |   64 +++++++++++++++++++++++++++++
>   drivers/gpio/gpio-pcf857x.c       |   24 +++++++++++
>   drivers/gpio/gpio-pch.c           |   27 ++++++++++++
>   drivers/gpio/gpio-pl061.c         |   17 +++++++
>   drivers/gpio/gpio-sa1100.c        |   20 +++++++++
>   drivers/gpio/gpio-samsung.c       |   31 ++++++++++++++
>   drivers/gpio/gpio-twl6040.c       |   32 ++++++++++++++
>   drivers/gpio/gpio-ucb1400.c       |   23 ++++++++++
>   drivers/gpio/gpio-vt8500.c        |   24 +++++++++++
>   drivers/gpio/gpio-xilinx.c        |   44 ++++++++++++++++++++
>   drivers/pinctrl/pinctrl-at91.c    |   29 +++++++++++++

I do not want to delay the process of inclusion for this patch series. 
But I have a little question on AT91 driver modification...

>   drivers/pinctrl/pinctrl-nomadik.c |   36 ++++++++++++++++
>   25 files changed, 887 insertions(+)

[..]

> --- linux-2.6.orig/drivers/pinctrl/pinctrl-at91.c
> +++ linux-2.6/drivers/pinctrl/pinctrl-at91.c
> @@ -49,6 +49,7 @@ struct at91_gpio_chip {
>   	struct clk		*clock;		/* associated clock */
>   	struct irq_domain	*domain;	/* associated irq domain */
>   	struct at91_pinctrl_mux_ops *ops;	/* ops */
> +	unsigned long		mask_cache;	/* cached mask for block gpio */
>   };
>
>   #define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip)
> @@ -1125,6 +1126,32 @@ static void at91_gpio_set(struct gpio_ch
>   	writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR));
>   }
>
> +static unsigned long at91_gpio_get_block(struct gpio_chip *chip,
> +					 unsigned long mask)
> +{
> +	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
> +	void __iomem *pio = at91_gpio->regbase;
> +	u32 pdsr;
> +
> +	pdsr = __raw_readl(pio + PIO_PDSR);

Maybe you should use readl_relaxed() here as it is used in the 
at91_gpio_[get|set]() functions.


> +	return pdsr & mask;
> +}
> +
> +static void at91_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
> +				unsigned long val)
> +{
> +	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
> +	void __iomem *pio = at91_gpio->regbase;
> +
> +	/* Do synchronous data output with a single write access */
> +	if (mask != at91_gpio->mask_cache) {
> +		at91_gpio->mask_cache = mask;
> +		__raw_writel(~mask, pio + PIO_OWDR);
> +		__raw_writel(mask, pio + PIO_OWER);
> +	}
> +	__raw_writel(val, pio + PIO_ODSR);

Ditto.

> +}
> +
>   static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
>   				int val)
>   {
> @@ -1435,8 +1462,10 @@ static struct gpio_chip at91_gpio_templa
>   	.free			= at91_gpio_free,
>   	.direction_input	= at91_gpio_direction_input,
>   	.get			= at91_gpio_get,
> +	.get_block		= at91_gpio_get_block,
>   	.direction_output	= at91_gpio_direction_output,
>   	.set			= at91_gpio_set,
> +	.set_block		= at91_gpio_set_block,
>   	.to_irq			= at91_gpio_to_irq,
>   	.dbg_show		= at91_gpio_dbg_show,
>   	.can_sleep		= 0,
> --- linux-2.6.orig/drivers/pinctrl/pinctrl-nomadik.c
> +++ linux-2.6/drivers/pinctrl/pinctrl-nomadik.c

[..]

Otherwise, seems ok to me ; for AT91 part:

Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>

Best regards,
-- 
Nicolas Ferre

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 6/6 v13] gpio: Add block gpio to several gpio drivers
@ 2013-01-15 13:18     ` Nicolas Ferre
  0 siblings, 0 replies; 32+ messages in thread
From: Nicolas Ferre @ 2013-01-15 13:18 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Le 15/01/2013 12:51, Roland Stigge a ?crit :
> This patch adds block GPIO support to several gpio drivers.
>
> This implements block GPIO only for some selected drivers since block GPIO is
> an optional feature which may not be suitable for every GPIO hardware. (With
> automatic fallback to the single GPIO functions if not available in a driver.)
>
> Signed-off-by: Roland Stigge <stigge@antcom.de>
>
> ---
>   drivers/gpio/Kconfig              |    2
>   drivers/gpio/gpio-em.c            |   23 ++++++++++
>   drivers/gpio/gpio-ge.c            |   29 +++++++++++++
>   drivers/gpio/gpio-generic.c       |   56 +++++++++++++++++++++++++
>   drivers/gpio/gpio-ks8695.c        |   34 +++++++++++++++
>   drivers/gpio/gpio-lpc32xx.c       |   82 ++++++++++++++++++++++++++++++++++++++
>   drivers/gpio/gpio-max730x.c       |   61 ++++++++++++++++++++++++++++
>   drivers/gpio/gpio-max732x.c       |   59 +++++++++++++++++++++++++++
>   drivers/gpio/gpio-mc33880.c       |   16 +++++++
>   drivers/gpio/gpio-ml-ioh.c        |   27 ++++++++++++
>   drivers/gpio/gpio-mm-lantiq.c     |   22 ++++++++++
>   drivers/gpio/gpio-mpc5200.c       |   64 +++++++++++++++++++++++++++++
>   drivers/gpio/gpio-mpc8xxx.c       |   41 +++++++++++++++++++
>   drivers/gpio/gpio-pca953x.c       |   64 +++++++++++++++++++++++++++++
>   drivers/gpio/gpio-pcf857x.c       |   24 +++++++++++
>   drivers/gpio/gpio-pch.c           |   27 ++++++++++++
>   drivers/gpio/gpio-pl061.c         |   17 +++++++
>   drivers/gpio/gpio-sa1100.c        |   20 +++++++++
>   drivers/gpio/gpio-samsung.c       |   31 ++++++++++++++
>   drivers/gpio/gpio-twl6040.c       |   32 ++++++++++++++
>   drivers/gpio/gpio-ucb1400.c       |   23 ++++++++++
>   drivers/gpio/gpio-vt8500.c        |   24 +++++++++++
>   drivers/gpio/gpio-xilinx.c        |   44 ++++++++++++++++++++
>   drivers/pinctrl/pinctrl-at91.c    |   29 +++++++++++++

I do not want to delay the process of inclusion for this patch series. 
But I have a little question on AT91 driver modification...

>   drivers/pinctrl/pinctrl-nomadik.c |   36 ++++++++++++++++
>   25 files changed, 887 insertions(+)

[..]

> --- linux-2.6.orig/drivers/pinctrl/pinctrl-at91.c
> +++ linux-2.6/drivers/pinctrl/pinctrl-at91.c
> @@ -49,6 +49,7 @@ struct at91_gpio_chip {
>   	struct clk		*clock;		/* associated clock */
>   	struct irq_domain	*domain;	/* associated irq domain */
>   	struct at91_pinctrl_mux_ops *ops;	/* ops */
> +	unsigned long		mask_cache;	/* cached mask for block gpio */
>   };
>
>   #define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip)
> @@ -1125,6 +1126,32 @@ static void at91_gpio_set(struct gpio_ch
>   	writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR));
>   }
>
> +static unsigned long at91_gpio_get_block(struct gpio_chip *chip,
> +					 unsigned long mask)
> +{
> +	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
> +	void __iomem *pio = at91_gpio->regbase;
> +	u32 pdsr;
> +
> +	pdsr = __raw_readl(pio + PIO_PDSR);

Maybe you should use readl_relaxed() here as it is used in the 
at91_gpio_[get|set]() functions.


> +	return pdsr & mask;
> +}
> +
> +static void at91_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
> +				unsigned long val)
> +{
> +	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
> +	void __iomem *pio = at91_gpio->regbase;
> +
> +	/* Do synchronous data output with a single write access */
> +	if (mask != at91_gpio->mask_cache) {
> +		at91_gpio->mask_cache = mask;
> +		__raw_writel(~mask, pio + PIO_OWDR);
> +		__raw_writel(mask, pio + PIO_OWER);
> +	}
> +	__raw_writel(val, pio + PIO_ODSR);

Ditto.

> +}
> +
>   static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
>   				int val)
>   {
> @@ -1435,8 +1462,10 @@ static struct gpio_chip at91_gpio_templa
>   	.free			= at91_gpio_free,
>   	.direction_input	= at91_gpio_direction_input,
>   	.get			= at91_gpio_get,
> +	.get_block		= at91_gpio_get_block,
>   	.direction_output	= at91_gpio_direction_output,
>   	.set			= at91_gpio_set,
> +	.set_block		= at91_gpio_set_block,
>   	.to_irq			= at91_gpio_to_irq,
>   	.dbg_show		= at91_gpio_dbg_show,
>   	.can_sleep		= 0,
> --- linux-2.6.orig/drivers/pinctrl/pinctrl-nomadik.c
> +++ linux-2.6/drivers/pinctrl/pinctrl-nomadik.c

[..]

Otherwise, seems ok to me ; for AT91 part:

Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>

Best regards,
-- 
Nicolas Ferre

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH RESEND 6/6 v13] gpio: Add block gpio to several gpio drivers
  2013-01-15 13:18     ` Nicolas Ferre
@ 2013-01-15 13:30       ` Roland Stigge
  -1 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 13:30 UTC (permalink / raw)
  To: Nicolas Ferre
  Cc: plagnioj, gregkh, grant.likely, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, highguy, broonie, daniel-gl,
	rmallon, sr, wg, tru, mark.rutland

On 01/15/2013 02:18 PM, Nicolas Ferre wrote:
>> --- linux-2.6.orig/drivers/pinctrl/pinctrl-at91.c
>> +++ linux-2.6/drivers/pinctrl/pinctrl-at91.c
>> @@ -49,6 +49,7 @@ struct at91_gpio_chip {
>>       struct clk        *clock;        /* associated clock */
>>       struct irq_domain    *domain;    /* associated irq domain */
>>       struct at91_pinctrl_mux_ops *ops;    /* ops */
>> +    unsigned long        mask_cache;    /* cached mask for block gpio */
>>   };
>>
>>   #define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip,
>> chip)
>> @@ -1125,6 +1126,32 @@ static void at91_gpio_set(struct gpio_ch
>>       writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR));
>>   }
>>
>> +static unsigned long at91_gpio_get_block(struct gpio_chip *chip,
>> +                     unsigned long mask)
>> +{
>> +    struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
>> +    void __iomem *pio = at91_gpio->regbase;
>> +    u32 pdsr;
>> +
>> +    pdsr = __raw_readl(pio + PIO_PDSR);
> 
> Maybe you should use readl_relaxed() here as it is used in the
> at91_gpio_[get|set]() functions.

Thanks for the note! Seem to have missed this when forward porting in
pinctrl-at91.c was due.

Will include the respective change in a subsequent update.

Roland

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 6/6 v13] gpio: Add block gpio to several gpio drivers
@ 2013-01-15 13:30       ` Roland Stigge
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-15 13:30 UTC (permalink / raw)
  To: linux-arm-kernel

On 01/15/2013 02:18 PM, Nicolas Ferre wrote:
>> --- linux-2.6.orig/drivers/pinctrl/pinctrl-at91.c
>> +++ linux-2.6/drivers/pinctrl/pinctrl-at91.c
>> @@ -49,6 +49,7 @@ struct at91_gpio_chip {
>>       struct clk        *clock;        /* associated clock */
>>       struct irq_domain    *domain;    /* associated irq domain */
>>       struct at91_pinctrl_mux_ops *ops;    /* ops */
>> +    unsigned long        mask_cache;    /* cached mask for block gpio */
>>   };
>>
>>   #define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip,
>> chip)
>> @@ -1125,6 +1126,32 @@ static void at91_gpio_set(struct gpio_ch
>>       writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR));
>>   }
>>
>> +static unsigned long at91_gpio_get_block(struct gpio_chip *chip,
>> +                     unsigned long mask)
>> +{
>> +    struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
>> +    void __iomem *pio = at91_gpio->regbase;
>> +    u32 pdsr;
>> +
>> +    pdsr = __raw_readl(pio + PIO_PDSR);
> 
> Maybe you should use readl_relaxed() here as it is used in the
> at91_gpio_[get|set]() functions.

Thanks for the note! Seem to have missed this when forward porting in
pinctrl-at91.c was due.

Will include the respective change in a subsequent update.

Roland

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
  2013-01-15 11:51   ` Roland Stigge
@ 2013-01-18 12:13     ` Stijn Devriendt
  -1 siblings, 0 replies; 32+ messages in thread
From: Stijn Devriendt @ 2013-01-18 12:13 UTC (permalink / raw)
  To: Roland Stigge
  Cc: gregkh, grant.likely, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, broonie, daniel-gl,
	rmallon, sr, wg, tru, mark.rutland

Hi Roland,

This mail has been long overdue due to issues with some internal
permission-tool.
Just to be clear, this is not a competing implementation, it's what we
currently use as-is. I'm just posting this as a reference to see if
perhaps more concepts could be reused. It's based on a 2.6.32 kernel.

It includes:
- labels in sysfs (to provide useful names to userspace)
- gpio group support
- exporting individual/groups of gpios dictated by platform-data or device-tree
- open-drain support (different from mainline)
- examplary support for multi-gpio to pcf8575 driver
- gpio_direction_output_keep() function that prevents toggling when
changing direction

Provided-as-is-by: Stijn Devriendt <sdevrien@cisco.com>
---
 drivers/gpio/Kconfig        |    6 +
 drivers/gpio/Makefile       |    1 +
 drivers/gpio/gpio-export.c  |  327 ++++++++++++++
 drivers/gpio/gpiolib.c      | 1021 ++++++++++++++++++++++++++++++++++++++++---
 drivers/gpio/pcf857x.c      |  102 ++++-
 include/asm-generic/gpio.h  |   67 +++
 include/linux/gpio-export.h |   64 +++
 include/linux/gpio.h        |   83 ++++
 8 files changed, 1602 insertions(+), 69 deletions(-)
 create mode 100644 drivers/gpio/gpio-export.c
 create mode 100644 include/linux/gpio-export.h

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 2ad0128..7daf6df 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -48,6 +48,12 @@ config DEBUG_GPIO
 	  slower.  The diagnostics help catch the type of setup errors
 	  that are most common when setting up new platforms or boards.

+config GPIO_EXPORT
+	bool "GPIO export driver"
+	depends on GPIO_SYSFS && GPIOLIB
+	help
+	  Say Y here to include the GPIO export driver.
+
 config GPIO_SYSFS
 	bool "/sys/class/gpio/... (sysfs interface)"
 	depends on SYSFS && EXPERIMENTAL
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 00a532c..40b96d7 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG

 obj-$(CONFIG_GPIOLIB)		+= gpiolib.o

+obj-$(CONFIG_GPIO_EXPORT)	+= gpio-export.o
 obj-$(CONFIG_GPIO_ADP5520)	+= adp5520-gpio.o
 obj-$(CONFIG_GPIO_LANGWELL)	+= langwell_gpio.o
 obj-$(CONFIG_GPIO_MAX7301)	+= max7301.o
diff --git a/drivers/gpio/gpio-export.c b/drivers/gpio/gpio-export.c
new file mode 100644
index 0000000..4ee4fe5
--- /dev/null
+++ b/drivers/gpio/gpio-export.c
@@ -0,0 +1,327 @@
+/* drivers/gpio/gpio-export.c
+ *
+ * Copyright (C) 2011 Stijn Devriendt, Cisco Systems Inc.
+ * Copyright (C) 2011 Eli Steenput, Cisco Systems Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/gpio-export.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_OF
+
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
+#endif
+
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+struct gpio_export_priv
+{
+	int count;
+	int gpio_num;
+	struct gpio_group *group;
+	char desc[MAX_GPIO_LABEL_SIZE];
+};
+
+static __devinit int common_gpio_probe(struct mp_gpio_platform_data
*pdata, struct device *dev)
+{
+	int gpio_count = pdata->gpio_count;
+	struct mp_gpio_line *gpio_line;
+	int i;
+	int err = 0;
+	struct gpio_export_priv *priv;
+
+	if (gpio_count <= 0 || gpio_count > 32)
+		return -ENODEV;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->count = gpio_count;
+	strncpy(priv->desc, pdata->desc, MAX_GPIO_LABEL_SIZE);
+	if (gpio_count > 1)
+	{
+		u32 gpio[32];
+		u32 value = pdata->initialvalue;
+		u32 opendrain = 0;
+		for (i = 0; i < gpio_count; ++i)
+		{
+			gpio_line = &pdata->gpio_data[i];
+			gpio[i] = gpio_line->gpio_num;
+			if (gpio_line->active_low)
+				value ^= (1 << i);
+			if (gpio_line->open_drain)
+				opendrain |= (1 << i);
+		}
+
+		priv->group = gpio_group_request(gpio, gpio_count, priv->desc);
+		if (IS_ERR(priv->group))
+		{
+			dev_err(dev, "Could not request gpio-group: %ld\n", PTR_ERR(priv->group));
+			err = PTR_ERR(priv->group);
+			goto out_mem;
+		}
+
+		if (opendrain)
+		{
+			err = gpio_group_set_opendrain(priv->group,
gpio_group_value_to_raw(priv->group, opendrain));
+			if (err)
+			{
+				dev_err(dev, "Could not set gpio-group open-drain: %d\n", err);
+				goto out_free_group;
+			}
+		}
+
+		switch (pdata->direction)
+		{
+			case GPIO_INPUT:
+				err = gpio_group_direction_input(priv->group);
+				break;
+			case GPIO_OUTPUT:
+				err = gpio_group_direction_output(priv->group,
gpio_group_value_to_raw(priv->group, value));
+				break;
+			case GPIO_OUTPUT_KEEP:
+				err = gpio_group_direction_output_keep(priv->group);
+				break;
+			default:
+        break;
+		}
+
+		if (err)
+		{
+			dev_err(dev, "Could not set gpio-group direction: %d\n", err);
+			goto out_free_group;
+		}
+
+		err = gpio_group_export(priv->group, pdata->direction == GPIO_CHANGE);
+		if (err)
+		{
+			dev_err(dev, "Could not export gpio-group: %d\n", err);
+			goto out_free_group;
+		}
+	}
+	else
+	{
+		gpio_line = &pdata->gpio_data[0];
+		err = gpio_request(gpio_line->gpio_num, priv->desc);
+		if (err)
+		{
+			dev_err(dev, "Could not request gpio %d\n", gpio_line->gpio_num);
+			goto out_free;
+		}
+		if (gpio_line->open_drain)
+		{
+			err = gpio_set_opendrain(gpio_line->gpio_num, 1);
+			if (err)
+			{
+				dev_warn(dev, "Could not set open-drain on gpio %d\n",
gpio_line->gpio_num);
+				goto out_free;
+			}
+		}
+
+		if (pdata->direction == GPIO_INPUT)
+		{
+			err = gpio_direction_input(gpio_line->gpio_num);
+		}
+		else if (pdata->direction == GPIO_OUTPUT)
+		{
+			int value = 0;
+			value = pdata->initialvalue;
+			if (gpio_line->active_low)
+				value = !value;
+			dev_dbg(dev, "Setting output on gpio %d with value %d\n",
gpio_line->gpio_num, value);
+			err = gpio_direction_output(gpio_line->gpio_num, value);
+		}
+		else if (pdata->direction == GPIO_OUTPUT_KEEP)
+		{
+			err = gpio_direction_output_keep(gpio_line->gpio_num);
+		}
+
+		if (err)
+		{
+			dev_err(dev, "Could not set direction: %d\n", err);
+			goto out_free;
+		}
+
+		err = gpio_export(gpio_line->gpio_num, pdata->direction == GPIO_CHANGE);
+		if (err)
+		{
+			dev_warn(dev, "Could not export gpio %d\n", gpio_line->gpio_num);
+			goto out_free;
+		}
+		priv->gpio_num = gpio_line->gpio_num;
+	}
+	dev_set_drvdata(dev, priv);
+	dev_info(dev, "%s: Exported %d GPIO pins\n", pdata->desc, pdata->gpio_count);
+out:
+	return err;
+out_free:
+	gpio_free(gpio_line->gpio_num);
+	kfree(priv);
+	goto out;
+out_free_group:
+	gpio_group_free(priv->group);
+	kfree(priv);
+	goto out;
+out_mem:
+	kfree(priv);
+	goto out;
+}
+
+int common_gpio_remove(struct device *dev)
+{
+	struct gpio_export_priv *priv = dev_get_drvdata(dev);
+	BUG_ON(!priv);
+
+	if (priv->count == 1)
+		gpio_free(priv->gpio_num);
+	else
+		gpio_group_free(priv->group);
+
+	dev_set_drvdata(dev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+
+static __devinit int of_gpio_probe(struct platform_device *of_dev,
+		const struct of_device_id *match)
+{
+	struct device_node *np = of_dev->dev.of_node;
+	int gpio_count = of_gpio_count(np);
+	const char* linuxname = of_get_property(np, "desc", NULL);
+
+	struct mp_gpio_platform_data pdata;
+	struct mp_gpio_line gpio_line[32];
+	int i;
+
+	pdata.gpio_data = gpio_line;
+	pdata.gpio_count = gpio_count;
+	strncpy(pdata.desc, linuxname, MAX_GPIO_LABEL_SIZE);
+	pdata.initialvalue = 0;
+
+	if (of_device_is_compatible(np, "gpio-input"))
+		pdata.direction = GPIO_INPUT;
+	else if (of_device_is_compatible(np, "gpio-output"))
+	{
+		const __be32 *value = of_get_property_u32(np, "initial");
+		if (!value)
+			pdata.direction = GPIO_OUTPUT_KEEP;
+		else
+		{
+			pdata.direction = GPIO_OUTPUT;
+			pdata.initialvalue = be32_to_cpup(value);
+			dev_dbg(&of_dev->dev, "initialvalue=%u, keep=%d\n",
pdata.initialvalue, pdata.direction == GPIO_OUTPUT_KEEP);
+		}
+	}
+	else
+	{
+		pdata.direction = GPIO_CHANGE;
+	}
+
+	for (i=0; i < gpio_count; ++i)
+	{
+		u32 flags;
+		gpio_line[i].gpio_num = of_get_gpio_flags(np, i, &flags);
+		gpio_line[i].active_low = flags & OF_GPIO_ACTIVE_LOW;
+		gpio_line[i].open_drain = flags & OF_GPIO_OPEN_DRAIN;
+	}
+	return common_gpio_probe(&pdata, &of_dev->dev);
+}
+
+static __devexit int of_gpio_remove(struct platform_device *of_dev)
+{
+	return common_gpio_remove(&of_dev->dev);
+}
+
+static const struct of_device_id __devinitconst of_gpio_match[] = {
+	{ .compatible = "gpio-input", },
+	{ .compatible = "gpio-output", },
+	{ .compatible = "gpio-user", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, of_gpio_match);
+
+static struct of_platform_driver of_gpio_driver = {
+	.probe = of_gpio_probe,
+	.remove = __devexit_p(of_gpio_remove),
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "of-gpio",
+		.of_match_table = of_gpio_match,
+	},
+};
+
+static int __init of_gpio_init(void)
+{
+	// Use i2c_bus_type to support I/O expanders?
+	return of_register_platform_driver(&of_gpio_driver);
+}
+
+static void __exit of_gpio_exit(void)
+{
+	of_unregister_driver(&of_gpio_driver);
+}
+
+late_initcall(of_gpio_init);
+module_exit(of_gpio_exit);
+
+#else
+
+static __devinit int mp_probe(struct platform_device *p_device)
+{
+	return common_gpio_probe(dev_get_platdata(&p_device->dev), &p_device->dev);
+}
+
+static __devexit int mp_remove(struct platform_device *p_device)
+{
+	return common_gpio_remove(&p_device->dev);
+}
+
+static struct platform_device_id mp_id_table[] = {
+	{
+		.name		= "gpio-export",
+	},
+};
+
+static struct platform_driver mp_gpio_driver = {
+	.probe = mp_probe,
+	.remove = mp_remove,
+	.id_table = mp_id_table,
+	.driver.name = "gpio-export",
+	.driver.bus = &platform_bus_type,
+	.driver.owner = THIS_MODULE,
+};
+
+static int __init mp_gpio_init(void)
+{
+	return platform_driver_register(&mp_gpio_driver);
+}
+
+static void __exit mp_gpio_exit(void)
+{
+	platform_driver_unregister(&mp_gpio_driver);
+}
+
+late_initcall(mp_gpio_init);
+module_exit(mp_gpio_exit);
+
+#endif
+
+MODULE_AUTHOR("Stijn Devriendt, Eli Steenput");
+MODULE_DESCRIPTION("Multi Purpose GPIO export driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 50de0f5..1c9c426 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -8,7 +8,9 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/idr.h>
+#include <linux/ctype.h>


 /* Optional implementation infrastructure for GPIO interfaces.
@@ -53,15 +55,15 @@ struct gpio_desc {
 #define FLAG_SYSFS	4	/* exported via /sys/class/gpio/control */
 #define FLAG_TRIG_FALL	5	/* trigger on falling edge */
 #define FLAG_TRIG_RISE	6	/* trigger on rising edge */
+#define FLAG_OPEN_DRAIN 7       /* gpio is open drain */

 #define PDESC_ID_SHIFT	16	/* add new flags before this one */

 #define GPIO_FLAGS_MASK		((1 << PDESC_ID_SHIFT) - 1)
 #define GPIO_TRIGGER_MASK	(BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))

-#ifdef CONFIG_DEBUG_FS
 	const char		*label;
-#endif
+	struct gpio_group *group;
 };
 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

@@ -76,9 +78,7 @@ static struct idr pdesc_idr;

 static inline void desc_set_label(struct gpio_desc *d, const char *label)
 {
-#ifdef CONFIG_DEBUG_FS
 	d->label = label;
-#endif
 }

 /* Warn when drivers omit gpio_request() calls -- legal but ill-advised
@@ -119,6 +119,11 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
 	return gpio_desc[gpio].chip;
 }

+static inline struct gpio_group *gpio_to_group(unsigned gpio)
+{
+	return gpio_desc[gpio].group;
+}
+
 /* dynamic allocation of GPIOs, e.g. on a hotplugged device */
 static int gpiochip_find_base(int ngpio)
 {
@@ -189,6 +194,110 @@ err:
 	return ret;
 }

+/* gpio_group_raw_to_value() - reorder bits according to the gpio group
+ * request
+ *
+ * @group: gpio_group
+ * @raw: raw value
+ *
+ * Returns a compact value representing the gpio_group value.
+ * e.g. consider gpio pins [1,0,2,3] have been requested and their
+ * value is [1,0,0,1] respectively. The raw readout will be 0xA [1,0,1,0]
+ * while the return value of this function will be 0x9, considering
+ * the order of the GPIOs in the group.
+ */
+u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw)
+{
+	int i = 0;
+	u32 ret = 0;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+	{
+		unsigned offset = group->gpios[i] - base;
+		u32 rawbit = (1 << offset);
+
+		// if raw[offset] is set
+		// then set ret[i]
+		if (raw & rawbit)
+			ret |= (1 << i);
+
+		++i;
+	}
+	return ret;
+}
+
+/* gpio_group_value_to_raw() - Inverse of gpio_group_raw_to_value
+ *
+ * @group: gpio_group
+ * @raw: compact value
+ *
+ * Returns the raw value.
+ */
+u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value)
+{
+	int i = 0;
+	u32 raw = 0;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+	{
+		unsigned offset = group->gpios[i] - base;
+		u32 rawbit = (1 << offset);
+
+		// if value[i] is set
+		// then set ret[offset]
+		if (value & (1 << i))
+			raw |= rawbit;
+
+		++i;
+	}
+	return raw;
+}
+
+int gpio_group_test_bit(unsigned long flag, const struct gpio_group *group)
+{
+	u32 set = 0;
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i) && test_bit(flag, &gpio_desc[base + i].flags))
+			set |= (1 << i);
+	}
+	if (set == 0)
+		return 0;
+	else if (set == group->mask)
+		return 1;
+	else
+		return -EIO;
+}
+
+void gpio_group_set_bit(unsigned long flag, const struct gpio_group *group)
+{
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i))
+			set_bit(flag, &gpio_desc[base + i].flags);
+	}
+}
+
+void gpio_group_clear_bit(unsigned long flag, const struct gpio_group *group)
+{
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i))
+			clear_bit(flag, &gpio_desc[base + i].flags);
+	}
+}
+
+
 #ifdef CONFIG_GPIO_SYSFS

 /* lock protects against unexport_gpio() being called while
@@ -231,6 +340,30 @@ static ssize_t gpio_direction_show(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_direction_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+
+	status = gpio_group_test_bit(FLAG_IS_OUT, group);
+	if (status >= 0)
+		status = sprintf(buf, "%s\n", status == 0 ? "in" : "out");
+
+out:
+	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)
 {
@@ -255,9 +388,49 @@ static ssize_t gpio_direction_store(struct device *dev,
 	return status ? : size;
 }

-static const DEVICE_ATTR(direction, 0644,
+static ssize_t gpio_group_direction_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+  const struct gpio_group *group = dev_get_drvdata(dev);
+  ssize_t status;
+  unsigned long input;
+
+  mutex_lock(&sysfs_lock);
+
+  if (gpio_desc[group->gpios[0]].group != group
+   || gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+  {
+    status = -EIO;
+    goto out;
+  }
+
+  if (sysfs_streq(buf, "in"))
+    status = gpio_group_direction_input(group);
+  else if (sysfs_streq(buf, "out"))
+    status = gpio_group_direction_output(group, 0);
+  else
+  {
+    status = strict_strtoul(buf, 0, &input);
+    if (!status)
+    {
+      if ((input & group->mask) == input)
+        status = gpio_group_direction_output(group, input);
+      else
+        status = -EINVAL;
+    }
+  }
+
+out:
+  mutex_unlock(&sysfs_lock);
+  return status ? : size;
+}
+
+static DEVICE_ATTR(direction, 0644,
 		gpio_direction_show, gpio_direction_store);

+struct device_attribute dev_attr_direction_group =
+	__ATTR(direction, 0644, gpio_group_direction_show,
gpio_group_direction_store);
+
 static ssize_t gpio_value_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
@@ -276,6 +449,50 @@ static ssize_t gpio_value_show(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_value_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", gpio_group_raw_to_value(group,
+				gpio_group_get_raw_cansleep(group)));
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+
+static ssize_t gpio_group_raw_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", gpio_group_get_raw_cansleep(group));
+
+out:
+	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)
 {
@@ -303,9 +520,96 @@ static ssize_t gpio_value_store(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_value_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+	unsigned long value;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
+			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = strict_strtoul(buf, 0, &value);
+	if (status == 0)
+	{
+		gpio_group_set_raw_cansleep(group,
+				gpio_group_value_to_raw(group, value));
+		status = size;
+	}
+
+out:
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+
+static ssize_t gpio_group_raw_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+	unsigned long value;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
+			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = strict_strtoul(buf, 0, &value);
+	if (status == 0)
+	{
+		gpio_group_set_raw_cansleep(group, value);
+		status = size;
+	}
+
+out:
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+
+static ssize_t gpio_group_mask_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", group->mask);
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
 static /*const*/ DEVICE_ATTR(value, 0644,
 		gpio_value_show, gpio_value_store);

+struct device_attribute dev_attr_value_group =
+	__ATTR(value, 0644, gpio_group_value_show, gpio_group_value_store);
+
+DEVICE_ATTR(raw, 0644, gpio_group_raw_show, gpio_group_raw_store);
+
+DEVICE_ATTR(mask, 0444, gpio_group_mask_show, NULL);
+
 static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
 {
 	struct work_struct	*work = priv;
@@ -381,7 +685,7 @@ static int gpio_setup_irq(struct gpio_desc *desc,
struct device *dev,
 			goto free_id;
 		}

-		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value");
+		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
 		if (!pdesc->value_sd) {
 			ret = -ENODEV;
 			goto free_id;
@@ -475,9 +779,55 @@ found:

 static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);

+static ssize_t gpio_label_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	const struct gpio_desc	*desc = dev_get_drvdata(dev);
+	ssize_t			status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (!test_bit(FLAG_EXPORT, &desc->flags))
+		status = -EIO;
+	else if (desc->label)
+		status = sprintf(buf, "%s\n", desc->label);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static ssize_t gpio_group_label_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	const struct gpio_desc	*desc = &gpio_desc[group->gpios[0]];
+	ssize_t			status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (!test_bit(FLAG_EXPORT, &desc->flags))
+		status = -EIO;
+	else if (desc->label)
+		status = sprintf(buf, "%s\n", desc->label);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static struct device_attribute dev_attr_gpio_label = __ATTR(label,
0444, gpio_label_show, NULL);
+static struct device_attribute dev_attr_gpio_group_label =
__ATTR(label, 0444, gpio_group_label_show, NULL);
+
 static const struct attribute *gpio_attrs[] = {
-	&dev_attr_direction.attr,
 	&dev_attr_value.attr,
+	&dev_attr_gpio_label.attr,
+	NULL,
+};
+
+static const struct attribute *gpio_group_attrs[] = {
+	&dev_attr_value_group.attr,
+	&dev_attr_raw.attr,
+	&dev_attr_mask.attr,
+	&dev_attr_gpio_group_label.attr,
 	NULL,
 };

@@ -485,6 +835,10 @@ static const struct attribute_group gpio_attr_group = {
 	.attrs = (struct attribute **) gpio_attrs,
 };

+static const struct attribute_group gpio_group_attr_group = {
+	.attrs = (struct attribute **) gpio_group_attrs,
+};
+
 /*
  * /sys/class/gpio/gpiochipN/
  *   /base ... matching gpio_chip.base (N)
@@ -530,6 +884,36 @@ static const struct attribute_group gpiochip_attr_group = {
 	.attrs = (struct attribute **) gpiochip_attrs,
 };

+int read_gpios(const char *buf, size_t len, unsigned *gpios)
+{
+	const char *startp = buf;
+	char *endp = (char*)buf;
+	int i;
+	for (i = 0; i < 32; ++i)
+	{
+		unsigned gpio;
+
+		gpio = simple_strtoul(startp, &endp, 0);
+
+		if (endp == startp)
+			return -EINVAL; // not a number, bail out
+
+		gpios[i] = gpio;
+
+		while (endp - buf < len && isspace(*endp)) // eat whitespace
+			endp++;
+
+
+		if (endp - buf > len) // buffer overrun, should never happen
+			return -EINVAL;
+		else if (endp - buf == len) // end of buffer, return number of read gpios
+			return i+1;
+
+		startp = endp;
+	}
+	return -EINVAL;
+}
+
 /*
  * /sys/class/gpio/export ... write-only
  *	integer N ... number of GPIO to export (full access)
@@ -538,10 +922,10 @@ static const struct attribute_group
gpiochip_attr_group = {
  */
 static ssize_t export_store(struct class *class, const char *buf, size_t len)
 {
-	long	gpio;
+	unsigned	gpio[32];
 	int	status;

-	status = strict_strtol(buf, 0, &gpio);
+	status = read_gpios(buf, len, gpio);
 	if (status < 0)
 		goto done;

@@ -550,16 +934,41 @@ static ssize_t export_store(struct class *class,
const char *buf, size_t len)
 	 * they may be undone on its behalf too.
 	 */

-	status = gpio_request(gpio, "sysfs");
-	if (status < 0)
-		goto done;
+	if (status == 1)
+	{
+		status = gpio_request(gpio[0], "sysfs");
+		if (status < 0)
+			goto done;

-	status = gpio_export(gpio, true);
-	if (status < 0)
-		gpio_free(gpio);
+		status = gpio_export(gpio[0], true);
+		if (status < 0)
+			gpio_free(gpio[0]);
+		else
+			set_bit(FLAG_SYSFS, &gpio_desc[gpio[0]].flags);
+	}
 	else
-		set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags);
+	{
+		struct gpio_group *group = gpio_group_request(gpio, status, "sysfs");
+		if (IS_ERR(group))
+		{
+			status = PTR_ERR(group);
+			goto done;
+		}

+		status = gpio_group_export(group, true);
+		if (status < 0)
+			gpio_group_free(group);
+		else
+    {
+      // Lock required to protect against unexport being called
+      // against when only parts of the group have the flag set.
+      // The other cases: all have the flag or none have the flag
+      // are handled correctly.
+      mutex_lock(&sysfs_lock);
+			gpio_group_set_bit(FLAG_SYSFS, group);
+      mutex_unlock(&sysfs_lock);
+    }
+	}
 done:
 	if (status)
 		pr_debug("%s: status %d\n", __func__, status);
@@ -570,6 +979,7 @@ static ssize_t unexport_store(struct class *class,
const char *buf, size_t len)
 {
 	long	gpio;
 	int	status;
+	struct gpio_group *group;

 	status = strict_strtol(buf, 0, &gpio);
 	if (status < 0)
@@ -581,13 +991,29 @@ static ssize_t unexport_store(struct class
*class, const char *buf, size_t len)
 	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);
+	group = gpio_to_group(gpio);
+	if (group)
+	{
+		mutex_lock(&sysfs_lock);
+		if (gpio_group_test_bit(FLAG_SYSFS, group))
+		{
+			status = 0;
+			gpio_group_clear_bit(FLAG_SYSFS, group);
+		}
+		mutex_unlock(&sysfs_lock);
+		if (!status)
+			gpio_group_free(group);
+	}
+	else
+	{
+		/* 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)
@@ -662,12 +1088,11 @@ int gpio_export(unsigned gpio, bool direction_may_change)
 		dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
 				desc, ioname ? ioname : "gpio%d", gpio);
 		if (!IS_ERR(dev)) {
-			if (direction_may_change)
-				status = sysfs_create_group(&dev->kobj,
-						&gpio_attr_group);
-			else
-				status = device_create_file(dev,
-						&dev_attr_value);
+  		status = sysfs_create_group(&dev->kobj,
+  						&gpio_attr_group);
+
+			if (!status && direction_may_change)
+				status = device_create_file(dev, &dev_attr_direction);

 			if (!status && gpio_to_irq(gpio) >= 0
 					&& (direction_may_change
@@ -694,6 +1119,72 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_export);

+int gpio_group_export(struct gpio_group *group, bool direction_may_change)
+{
+	unsigned long flags;
+	int status = -EINVAL;
+	struct gpio_chip *chip;
+	unsigned lowest = group->gpios[0];
+	int i;
+
+	/* can't export until sysfs is available ... */
+	if (!gpio_class.p) {
+		pr_debug("%s: called too early!\n", __func__);
+		return -ENOENT;
+	}
+
+	mutex_lock(&sysfs_lock);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	chip = gpio_to_chip(group->gpios[0]);
+	if (gpio_group_test_bit(FLAG_REQUESTED, group) > 0
+			&& gpio_group_test_bit(FLAG_EXPORT, group) == 0)
+	{
+		status = 0;
+		if (!chip->direction_input_multi || !chip->direction_output_multi)
+		{
+			direction_may_change = false;
+		}
+	}
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	if (status == 0)
+	{
+		struct device *dev;
+
+		i=1;
+		while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+		{
+			if (group->gpios[i] < lowest)
+				lowest = group->gpios[i];
+			++i;
+		}
+
+		dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0),
+				group, "group%d", lowest);
+		if (!IS_ERR(dev)) {
+			status = sysfs_create_group(&dev->kobj, &gpio_group_attr_group);
+
+			if (status == 0 && direction_may_change)
+				status = device_create_file(dev, &dev_attr_direction_group);
+
+			if (status != 0)
+				device_unregister(dev);
+		}
+		else
+			status = PTR_ERR(dev);
+		if (status == 0)
+			gpio_group_set_bit(FLAG_EXPORT, group);
+	}
+	mutex_unlock(&sysfs_lock);
+
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_group_export);
+
+
 static int match_export(struct device *dev, void *data)
 {
 	return dev_get_drvdata(dev) == data;
@@ -744,6 +1235,30 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_export_link);

+int gpio_group_export_link(struct device *dev, const char *name,
struct gpio_group *group)
+{
+	int status = -EINVAL;
+	mutex_lock(&sysfs_lock);
+	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
+	{
+		struct device *tdev;
+
+		tdev = class_find_device(&gpio_class, NULL, group, match_export);
+		if (tdev != NULL) {
+			status = sysfs_create_link(&dev->kobj, &tdev->kobj, name);
+		}
+		else
+		{
+			status = -ENODEV;
+		}
+	}
+	mutex_unlock(&sysfs_lock);
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_group_export_link);
+
 /**
  * gpio_unexport - reverse effect of gpio_export()
  * @gpio: gpio to make unavailable
@@ -783,6 +1298,31 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_unexport);

+void gpio_group_unexport(struct gpio_group *group)
+{
+	int status = -EINVAL;
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
+	{
+		struct device *dev = NULL;
+		dev = class_find_device(&gpio_class, NULL, group, match_export);
+		if (dev)
+		{
+			gpio_group_clear_bit(FLAG_EXPORT, group);
+			put_device(dev);
+			device_unregister(dev);
+			status = 0;
+		}
+		else
+			status = -ENODEV;
+	}
+	mutex_unlock(&sysfs_lock);
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+}
+EXPORT_SYMBOL_GPL(gpio_group_unexport);
+
 static int gpiochip_export(struct gpio_chip *chip)
 {
 	int		status;
@@ -959,6 +1499,8 @@ int gpiochip_add(struct gpio_chip *chip)
 		}
 	}

+	of_gpiochip_add(chip);
+
 unlock:
 	spin_unlock_irqrestore(&gpio_lock, flags);
 	if (status == 0)
@@ -987,6 +1529,8 @@ int gpiochip_remove(struct gpio_chip *chip)

 	spin_lock_irqsave(&gpio_lock, flags);

+	of_gpiochip_remove(chip);
+
 	for (id = chip->base; id < chip->base + chip->ngpio; id++) {
 		if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) {
 			status = -EBUSY;
@@ -1007,6 +1551,38 @@ int gpiochip_remove(struct gpio_chip *chip)
 }
 EXPORT_SYMBOL_GPL(gpiochip_remove);

+/**
+ * gpiochip_find() - iterator for locating a specific gpio_chip
+ * @data: data to pass to match function
+ * @callback: Callback function to check gpio_chip
+ *
+ * Similar to bus_find_device.  It returns a reference to a gpio_chip as
+ * determined by a user supplied @match callback.  The callback should return
+ * 0 if the device doesn't match and non-zero if it does.  If the callback is
+ * non-zero, this function will return to the caller and not iterate over any
+ * more gpio_chips.
+ */
+struct gpio_chip *gpiochip_find(void *data,
+				int (*match)(struct gpio_chip *chip, void *data))
+{
+	struct gpio_chip *chip = NULL;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	for (i = 0; i < ARCH_NR_GPIOS; i++) {
+		if (!gpio_desc[i].chip)
+			continue;
+
+		if (match(gpio_desc[i].chip, data)) {
+			chip = gpio_desc[i].chip;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	return chip;
+}

 /* These "optional" allocation calls help prevent drivers from stomping
  * on each other, and help provide better diagnostics in debugfs.
@@ -1066,6 +1642,111 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_request);

+struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
const char* label)
+{
+	int i;
+	unsigned long flags;
+	u32 mask = 0;
+	struct gpio_chip *chip;
+	int rc = 0;
+	struct gpio_group *group;
+
+	if (ngpios <= 0)
+		return ERR_PTR(-EINVAL);
+
+	group = kzalloc(sizeof(struct gpio_group), GFP_KERNEL);
+	if (!group)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	if (!gpio_is_valid(gpio[0]))
+	{
+		rc = -EINVAL;
+		goto out_free;
+	}
+	chip = gpio_to_chip(gpio[0]);
+	// if not (multi-input or multi-output capable)
+	if (!chip ||
+			!(   (chip->get_multi && chip->direction_input_multi)
+				|| (chip->set_multi && chip->direction_output_multi))
+	   )
+	{
+		rc = -ENODEV;
+		goto out_free;
+	}
+
+	mask |= (1 << (gpio[0] - chip->base));
+	for (i = 1; i < ngpios; ++i)
+	{
+		if (gpio[i] < chip->base || gpio[i] > chip->base + chip->ngpio)
+		{
+			rc = -EINVAL;
+			goto out_free;
+		}
+		mask |= (1 << (gpio[i] - chip->base));
+	}
+
+	if (!try_module_get(chip->owner))
+	{
+		rc = -ENOSYS;
+		goto out_free;
+	}
+
+	group->mask = mask;
+	for (i = 0; i < 32; ++i)
+	{
+		if (i < ngpios)
+			group->gpios[i] = gpio[i];
+		else
+			group->gpios[i] = ARCH_NR_GPIOS;
+	}
+
+	if (gpio_group_test_bit(FLAG_REQUESTED, group) != 0)
+	{
+		rc = -EBUSY;
+		goto out_put;
+	}
+
+	gpio_group_set_bit(FLAG_REQUESTED, group);
+	for (i = 0; i < ngpios; ++i)
+	{
+		gpio_desc[gpio[i]].group = group;
+		desc_set_label(&gpio_desc[gpio[i]], label ? : "?");
+	}
+
+	if (chip->request_multi) {
+		spin_unlock_irqrestore(&gpio_lock, flags);
+		rc = chip->request_multi(chip, mask);
+		spin_lock_irqsave(&gpio_lock, flags);
+		if (rc)
+			goto out_label;
+	}
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	return group;
+out_label:
+	for (i = 0; i < ngpios; ++i)
+	{
+		desc_set_label(&gpio_desc[gpio[i]], NULL);
+		gpio_desc[gpio[i]].group = NULL;
+	}
+out_put:
+	module_put(chip->owner);
+out_free:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	kfree(group);
+	pr_debug("%s: group%d (%s) status %d\n", __func__, *gpio, label ? : "?", rc);
+	return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(gpio_group_request);
+
+struct gpio_group * gpio_get_group(unsigned gpio)
+{
+	if (!gpio_is_valid(gpio))
+		return NULL;
+	return gpio_desc[gpio].group;
+}
+
 void gpio_free(unsigned gpio)
 {
 	unsigned long		flags;
@@ -1102,6 +1783,45 @@ void gpio_free(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(gpio_free);

+void gpio_group_free(struct gpio_group *group)
+{
+	unsigned long flags;
+	struct gpio_chip *chip;
+
+	might_sleep();
+
+	gpio_group_unexport(group);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip = gpio_to_chip(group->gpios[0]);
+	if (chip && gpio_group_test_bit(FLAG_REQUESTED, group))
+	{
+		int i;
+		if (chip->free_multi) {
+			spin_unlock_irqrestore(&gpio_lock, flags);
+			might_sleep_if(extra_checks && chip->can_sleep);
+			chip->free_multi(chip, group->mask);
+			spin_lock_irqsave(&gpio_lock, flags);
+		}
+		for (i = 0; i < 32; ++i)
+		{
+			if (group->mask & (1 << i))
+			{
+				desc_set_label(&gpio_desc[chip->base + i], NULL);
+				gpio_desc[chip->base +i].group = NULL;
+			}
+			module_put(chip->owner);
+			gpio_group_clear_bit(FLAG_REQUESTED, group);
+		}
+	}
+	else
+		WARN_ON(extra_checks);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	kfree(group);
+}
+EXPORT_SYMBOL_GPL(gpio_group_free);

 /**
  * gpiochip_is_requested - return string iff signal was requested
@@ -1124,15 +1844,10 @@ const char *gpiochip_is_requested(struct
gpio_chip *chip, unsigned offset)
 		return NULL;
 	if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0)
 		return NULL;
-#ifdef CONFIG_DEBUG_FS
 	return gpio_desc[gpio].label;
-#else
-	return "?";
-#endif
 }
 EXPORT_SYMBOL_GPL(gpiochip_is_requested);

-
 /* Drivers MUST set GPIO direction before making get/set calls.  In
  * some cases this is done in early boot, before IRQs are enabled.
  *
@@ -1195,6 +1910,25 @@ fail:
 }
 EXPORT_SYMBOL_GPL(gpio_direction_input);

+
+int gpio_group_direction_input(const struct gpio_group *group)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int rc;
+
+	if (!chip->get_multi || !chip->direction_input_multi)
+		return -EINVAL;
+
+	rc = chip->direction_input_multi(chip, group->mask);
+	if (rc == 0)
+	{
+		gpio_group_clear_bit(FLAG_IS_OUT, group);
+	}
+	else
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
+	return rc;
+}
+
 int gpio_direction_output(unsigned gpio, int value)
 {
 	unsigned long		flags;
@@ -1248,6 +1982,124 @@ fail:
 }
 EXPORT_SYMBOL_GPL(gpio_direction_output);

+int gpio_group_direction_output(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int rc;
+
+	if (!chip->set_multi || !chip->direction_output_multi)
+		return -EINVAL;
+
+	rc = chip->direction_output_multi(chip, group->mask, value & group->mask);
+	if (rc == 0)
+	{
+		gpio_group_set_bit(FLAG_IS_OUT, group);
+	}
+	else
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
+	return rc;
+}
+
+int gpio_set_opendrain(unsigned gpio, int value)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+	struct gpio_desc	*desc = &gpio_desc[gpio];
+	int			status = -EINVAL;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	if (!gpio_is_valid(gpio))
+		goto fail;
+	chip = desc->chip;
+	if (!chip || !chip->set || !chip->direction_output)
+		goto fail;
+	gpio -= chip->base;
+	if (gpio >= chip->ngpio)
+		goto fail;
+	status = gpio_ensure_requested(desc, gpio);
+	if (status < 0)
+		goto fail;
+
+	/* now we know the gpio is valid and chip won't vanish */
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	might_sleep_if(extra_checks && chip->can_sleep);
+
+	if (!chip->set_opendrain)
+		return -ENOSYS;
+
+	if (status) {
+		status = chip->request(chip, gpio);
+		if (status < 0) {
+			pr_debug("GPIO-%d: chip request fail, %d\n",
+				chip->base + gpio, status);
+			/* and it's not available to anyone else ...
+			 * gpio_request() is the fully clean solution.
+			 */
+			goto lose;
+		}
+	}
+
+	status = chip->set_opendrain(chip, gpio, value);
+	if (status == 0)
+	{
+		if (value)
+			set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+		else
+			clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
+	}
+lose:
+	return status;
+fail:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	if (status)
+		pr_debug("%s: gpio-%d status %d\n",
+			__func__, gpio, status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_set_opendrain);
+
+int gpio_group_set_opendrain(struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int err;
+	if (!chip)
+		return -EINVAL;
+	if (!chip->set_multi || !chip->direction_output_multi)
+		return -EINVAL;
+	if (!chip->set_opendrain_multi)
+		return -ENOSYS;
+
+	might_sleep_if(extra_checks && chip->can_sleep);
+
+	err = chip->set_opendrain_multi(chip, group->mask, value & group->mask);
+	if (err == 0)
+	{
+		int i;
+		unsigned base = chip->base;
+		for (i = 0; i < 32; ++i)
+		{
+			if (group->mask & (1 << i))
+			{
+				if (value & (1 << i))
+				{
+					set_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
+				}
+				else
+				{
+					clear_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
+				}
+			}
+		}
+	}
+	if (err)
+		pr_debug("%s: group%d status %d\n",
+				__func__, group->gpios[0], err);
+	return err;
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_opendrain);

 /* I/O calls are only valid after configuration completed; the relevant
  * "is this a valid GPIO" error checks should already have been done.
@@ -1290,6 +2142,14 @@ int __gpio_get_value(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(__gpio_get_value);

+u32 gpio_group_get_raw(const struct gpio_group* group)
+{
+	struct gpio_chip *chip;
+	chip = gpio_to_chip(group->gpios[0]);
+	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
+}
+EXPORT_SYMBOL_GPL(gpio_group_get_raw);
+
 /**
  * __gpio_set_value() - assign a gpio's value
  * @gpio: gpio whose value will be assigned
@@ -1309,6 +2169,13 @@ void __gpio_set_value(unsigned gpio, int value)
 }
 EXPORT_SYMBOL_GPL(__gpio_set_value);

+void gpio_group_set_raw(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip;
+	chip = gpio_to_chip(group->gpios[0]);
+	chip->set_multi(chip, group->mask, value | group->mask);
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_raw);
 /**
  * __gpio_cansleep() - report whether gpio value access will sleep
  * @gpio: gpio in question
@@ -1328,6 +2195,11 @@ int __gpio_cansleep(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(__gpio_cansleep);

+int gpio_group_cansleep(const struct gpio_group *group)
+{
+	return __gpio_cansleep(group->gpios[0]);
+}
+
 /**
  * __gpio_to_irq() - return the IRQ corresponding to a GPIO
  * @gpio: gpio whose IRQ will be returned (already requested)
@@ -1362,6 +2234,15 @@ int gpio_get_value_cansleep(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);

+u32 gpio_group_get_raw_cansleep(const struct gpio_group *group)
+{
+	struct gpio_chip *chip;
+	might_sleep_if(extra_checks);
+	chip = gpio_to_chip(group->gpios[0]);
+	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
+}
+EXPORT_SYMBOL_GPL(gpio_group_get_raw_cansleep);
+
 void gpio_set_value_cansleep(unsigned gpio, int value)
 {
 	struct gpio_chip	*chip;
@@ -1372,6 +2253,14 @@ void gpio_set_value_cansleep(unsigned gpio, int value)
 }
 EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);

+void gpio_group_set_raw_cansleep(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip;
+	might_sleep_if(extra_checks);
+	chip = gpio_to_chip(group->gpios[0]);
+	chip->set_multi(chip, group->mask, value & group->mask);
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_raw_cansleep);

 #ifdef CONFIG_DEBUG_FS

@@ -1381,18 +2270,24 @@ static void gpiolib_dbg_show(struct seq_file
*s, struct gpio_chip *chip)
 	unsigned		gpio = chip->base;
 	struct gpio_desc	*gdesc = &gpio_desc[gpio];
 	int			is_out;
+	int			is_open;
+	int     is_group;

 	for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
 		if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
 			continue;

 		is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
-		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s",
-			gpio, gdesc->label,
-			is_out ? "out" : "in ",
-			chip->get
+		is_open = test_bit(FLAG_OPEN_DRAIN, &gdesc->flags);
+		is_group = (gdesc->group != NULL);
+		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s",
+				gpio, gdesc->label,
+				is_out ? "out" : "in ",
+				chip->get
 				? (chip->get(chip, i) ? "hi" : "lo")
-				: "?  ");
+				: "?  ",
+				is_open ? "open" : "act ",
+				is_group ? "grp" : "pin");

 		if (!is_out) {
 			int		irq = gpio_to_irq(gpio);
@@ -1408,32 +2303,32 @@ static void gpiolib_dbg_show(struct seq_file
*s, struct gpio_chip *chip)
 				char *trigger;

 				switch (desc->status & IRQ_TYPE_SENSE_MASK) {
-				case IRQ_TYPE_NONE:
-					trigger = "(default)";
-					break;
-				case IRQ_TYPE_EDGE_FALLING:
-					trigger = "edge-falling";
-					break;
-				case IRQ_TYPE_EDGE_RISING:
-					trigger = "edge-rising";
-					break;
-				case IRQ_TYPE_EDGE_BOTH:
-					trigger = "edge-both";
-					break;
-				case IRQ_TYPE_LEVEL_HIGH:
-					trigger = "level-high";
-					break;
-				case IRQ_TYPE_LEVEL_LOW:
-					trigger = "level-low";
-					break;
-				default:
-					trigger = "?trigger?";
-					break;
+					case IRQ_TYPE_NONE:
+						trigger = "(default)";
+						break;
+					case IRQ_TYPE_EDGE_FALLING:
+						trigger = "edge-falling";
+						break;
+					case IRQ_TYPE_EDGE_RISING:
+						trigger = "edge-rising";
+						break;
+					case IRQ_TYPE_EDGE_BOTH:
+						trigger = "edge-both";
+						break;
+					case IRQ_TYPE_LEVEL_HIGH:
+						trigger = "level-high";
+						break;
+					case IRQ_TYPE_LEVEL_LOW:
+						trigger = "level-low";
+						break;
+					default:
+						trigger = "?trigger?";
+						break;
 				}

 				seq_printf(s, " irq-%d %s%s",
-					irq, trigger,
-					(desc->status & IRQ_WAKEUP)
+						irq, trigger,
+						(desc->status & IRQ_WAKEUP)
 						? " wakeup" : "");
 			}
 		}
diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c
index 29f19ce..a51d9ad 100644
--- a/drivers/gpio/pcf857x.c
+++ b/drivers/gpio/pcf857x.c
@@ -79,6 +79,20 @@ static int pcf857x_input8(struct gpio_chip *chip,
unsigned offset)
 	return status;
 }

+static int pcf857x_input8_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out |= (u16)mask;
+	status = i2c_smbus_write_byte(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
+
 static int pcf857x_get8(struct gpio_chip *chip, unsigned offset)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -88,6 +102,15 @@ static int pcf857x_get8(struct gpio_chip *chip,
unsigned offset)
 	return (value < 0) ? 0 : (value & (1 << offset));
 }

+static u32 pcf857x_get8_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	u32		value;
+
+	value = i2c_smbus_read_byte(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
 static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -105,6 +128,25 @@ static int pcf857x_output8(struct gpio_chip
*chip, unsigned offset, int value)
 	return status;
 }

+static int pcf857x_output8_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= value;
+	status = i2c_smbus_write_byte(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
+static void pcf857x_set8_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	pcf857x_output8_multi(chip, mask, value);
+}
+
 static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
 {
 	pcf857x_output8(chip, offset, value);
@@ -147,6 +189,19 @@ static int pcf857x_input16(struct gpio_chip
*chip, unsigned offset)
 	return status;
 }

+static int pcf857x_input16_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out |= mask;
+	status = i2c_write_le16(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
 static int pcf857x_get16(struct gpio_chip *chip, unsigned offset)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -156,6 +211,15 @@ static int pcf857x_get16(struct gpio_chip *chip,
unsigned offset)
 	return (value < 0) ? 0 : (value & (1 << offset));
 }

+static u32 pcf857x_get16_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	u32		value;
+
+	value = i2c_read_le16(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
 static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -173,11 +237,30 @@ static int pcf857x_output16(struct gpio_chip
*chip, unsigned offset, int value)
 	return status;
 }

+static int pcf857x_output16_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= value;
+	status = i2c_write_le16(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
 static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value)
 {
 	pcf857x_output16(chip, offset, value);
 }

+static void pcf857x_set16_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	pcf857x_output16_multi(chip, mask, value);
+}
+
 /*-------------------------------------------------------------------------*/

 static int pcf857x_probe(struct i2c_client *client,
@@ -190,7 +273,6 @@ static int pcf857x_probe(struct i2c_client *client,
 	pdata = client->dev.platform_data;
 	if (!pdata) {
 		dev_dbg(&client->dev, "no platform data\n");
-		return -EINVAL;
 	}

 	/* Allocate, initialize, and register this gpio_chip. */
@@ -200,7 +282,7 @@ static int pcf857x_probe(struct i2c_client *client,

 	mutex_init(&gpio->lock);

-	gpio->chip.base = pdata->gpio_base;
+	gpio->chip.base = pdata ? pdata->gpio_base : -1;
 	gpio->chip.can_sleep = 1;
 	gpio->chip.dev = &client->dev;
 	gpio->chip.owner = THIS_MODULE;
@@ -218,10 +300,14 @@ static int pcf857x_probe(struct i2c_client *client,
 	 */
 	gpio->chip.ngpio = id->driver_data;
 	if (gpio->chip.ngpio == 8) {
+		gpio->chip.direction_input_multi = pcf857x_input8_multi;
 		gpio->chip.direction_input = pcf857x_input8;
 		gpio->chip.get = pcf857x_get8;
+		gpio->chip.get_multi = pcf857x_get8_multi;
 		gpio->chip.direction_output = pcf857x_output8;
+		gpio->chip.direction_output_multi = pcf857x_output8_multi;
 		gpio->chip.set = pcf857x_set8;
+		gpio->chip.set_multi = pcf857x_set8_multi;

 		if (!i2c_check_functionality(client->adapter,
 				I2C_FUNC_SMBUS_BYTE))
@@ -239,9 +325,13 @@ static int pcf857x_probe(struct i2c_client *client,
 	 */
 	} else if (gpio->chip.ngpio == 16) {
 		gpio->chip.direction_input = pcf857x_input16;
+		gpio->chip.direction_input_multi = pcf857x_input16_multi;
 		gpio->chip.get = pcf857x_get16;
+		gpio->chip.get_multi = pcf857x_get16_multi;
 		gpio->chip.direction_output = pcf857x_output16;
+		gpio->chip.direction_output_multi = pcf857x_output16_multi;
 		gpio->chip.set = pcf857x_set16;
+		gpio->chip.set_multi = pcf857x_set16_multi;

 		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 			status = -EIO;
@@ -278,7 +368,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	 * to zero, our software copy of the "latch" then matches the chip's
 	 * all-ones reset state.  Otherwise it flags pins to be driven low.
 	 */
-	gpio->out = ~pdata->n_latch;
+	gpio->out = pdata ? ~pdata->n_latch : ~0;

 	status = gpiochip_add(&gpio->chip);
 	if (status < 0)
@@ -299,7 +389,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	/* Let platform code set up the GPIOs and their users.
 	 * Now is the first time anyone could use them.
 	 */
-	if (pdata->setup) {
+	if (pdata && pdata->setup) {
 		status = pdata->setup(client,
 				gpio->chip.base, gpio->chip.ngpio,
 				pdata->context);
@@ -310,7 +400,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	return 0;

 fail:
-	dev_dbg(&client->dev, "probe error %d for '%s'\n",
+	dev_err(&client->dev, "probe error %d for '%s'\n",
 			status, client->name);
 	kfree(gpio);
 	return status;
@@ -322,7 +412,7 @@ static int pcf857x_remove(struct i2c_client *client)
 	struct pcf857x			*gpio = i2c_get_clientdata(client);
 	int				status = 0;

-	if (pdata->teardown) {
+	if (pdata && pdata->teardown) {
 		status = pdata->teardown(client,
 				gpio->chip.base, gpio->chip.ngpio,
 				pdata->context);
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 66d6106..ed2407f 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -30,6 +30,7 @@ static inline int gpio_is_valid(int number)

 struct seq_file;
 struct module;
+struct device_node;

 /**
  * struct gpio_chip - abstract a GPIO controller
@@ -89,6 +90,22 @@ struct gpio_chip {
 						unsigned offset, int value);
 	void			(*set)(struct gpio_chip *chip,
 						unsigned offset, int value);
+	int			(*set_opendrain)(struct gpio_chip *chip,
+						unsigned offset, int value);
+	int			(*set_opendrain_multi)(struct gpio_chip *chip,
+						u32 mask, u32 value);
+
+	int			(*request_multi)(struct gpio_chip *chip,
+						u32 mask);
+	void			(*free_multi)(struct gpio_chip *chip,
+						u32 mask);
+
+	int			(*direction_input_multi)(struct gpio_chip *chip,
+						u32 mask);
+	int			(*direction_output_multi)(struct gpio_chip *chip,
+						u32 mask, u32 value);
+	void			(*set_multi)(struct gpio_chip *chip, u32 mask, u32 value);
+	u32			(*get_multi)(struct gpio_chip *chip, u32 mask);

 	int			(*to_irq)(struct gpio_chip *chip,
 						unsigned offset);
@@ -100,6 +117,23 @@ struct gpio_chip {
 	char			**names;
 	unsigned		can_sleep:1;
 	unsigned		exported:1;
+
+#if defined(CONFIG_OF_GPIO)
+	/*
+	 * If CONFIG_OF is enabled, then all GPIO controllers described in the
+	 * device tree automatically may have an OF translation
+	 */
+	struct device_node *of_node;
+	int of_gpio_n_cells;
+	int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
+		        const void *gpio_spec, u32 *flags);
+#endif
+};
+
+struct gpio_group
+{
+	unsigned gpios[32];
+	u32 mask;
 };

 extern const char *gpiochip_is_requested(struct gpio_chip *chip,
@@ -109,6 +143,9 @@ extern int __must_check gpiochip_reserve(int
start, int ngpio);
 /* add/remove chips */
 extern int gpiochip_add(struct gpio_chip *chip);
 extern int __must_check gpiochip_remove(struct gpio_chip *chip);
+extern struct gpio_chip *gpiochip_find(void *data,
+					int (*match)(struct gpio_chip *chip,
+						     void *data));


 /* Always use the library code for GPIO management calls,
@@ -120,9 +157,28 @@ extern void gpio_free(unsigned gpio);
 extern int gpio_direction_input(unsigned gpio);
 extern int gpio_direction_output(unsigned gpio, int value);

+extern int gpio_set_opendrain(unsigned gpio, int value);
+
 extern int gpio_get_value_cansleep(unsigned gpio);
 extern void gpio_set_value_cansleep(unsigned gpio, int value);

+/*
+ * Handling of gpio groups
+ */
+extern struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
+    const char *label);
+extern void gpio_group_free(struct gpio_group* group);
+
+extern int gpio_group_direction_input(const struct gpio_group *group);
+extern int gpio_group_direction_output(const struct gpio_group
*group, u32 value);
+
+extern u32 gpio_group_get_raw_cansleep(const struct gpio_group *group);
+extern void gpio_group_set_raw_cansleep(const struct gpio_group
*group, u32 value);
+
+extern int gpio_group_set_opendrain(struct gpio_group* group, u32 value);
+
+u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw);
+u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value);

 /* A platform's <asm/gpio.h> code may want to inline the I/O calls when
  * the GPIO is constant and refers to some always-present controller,
@@ -135,6 +191,11 @@ extern int __gpio_cansleep(unsigned gpio);

 extern int __gpio_to_irq(unsigned gpio);

+extern u32 gpio_group_get_raw(const struct gpio_group *group);
+extern void gpio_group_set_raw(const struct gpio_group *group, u32 value);
+
+extern int gpio_group_cansleep(const struct gpio_group *group);
+
 #ifdef CONFIG_GPIO_SYSFS

 /*
@@ -146,6 +207,12 @@ extern int gpio_export_link(struct device *dev,
const char *name,
 			unsigned gpio);
 extern void gpio_unexport(unsigned gpio);

+extern int gpio_group_export(struct gpio_group *group,
+    bool direction_may_change);
+extern int gpio_group_export_link(struct device *dev, const char *name,
+    struct gpio_group *group);
+extern void gpio_group_unexport(struct gpio_group *group);
+
 #endif	/* CONFIG_GPIO_SYSFS */

 #else	/* !CONFIG_HAVE_GPIO_LIB */
diff --git a/include/linux/gpio-export.h b/include/linux/gpio-export.h
new file mode 100644
index 0000000..712e9ff
--- /dev/null
+++ b/include/linux/gpio-export.h
@@ -0,0 +1,64 @@
+/*    Structures for passing gpio settings to drivers/gpio/gpio-export.c
+ *    201103 steene99
+ */
+#ifndef __LINUX_GPIO_EXPORT_H
+#define __LINUX_GPIO_EXPORT_H
+
+enum gpio_direction
+{
+	GPIO_INPUT,
+	GPIO_OUTPUT,
+	GPIO_CHANGE,
+	GPIO_OUTPUT_KEEP,
+};
+
+struct mp_gpio_line
+{
+	int gpio_num;
+	int active_low;
+	int open_drain;
+};
+
+#define GPIO_PIN(_nr, _active_low, _open_drain) \
+{ \
+	.gpio_num = _nr, \
+	.active_low = _active_low, \
+	.open_drain = _open_drain, \
+}
+
+#define SIMPLE_GPIO_PIN(_name, _nr) \
+static struct mp_gpio_line _name[] = \
+{ \
+	GPIO_PIN(_nr, 0, 0), \
+}
+
+#define MAX_GPIO_LABEL_SIZE 32
+
+struct mp_gpio_platform_data
+{
+	int gpio_count;
+	enum gpio_direction direction;
+	u32 initialvalue;  // value
+	struct mp_gpio_line *gpio_data;
+	char desc[MAX_GPIO_LABEL_SIZE];
+};
+
+#define DEFINE_GPIO_GROUP(_name, _count, _direction, _initial, _pin, _desc) \
+	static struct mp_gpio_platform_data _name = { \
+		.gpio_count = _count, \
+		.direction = _direction, \
+		.initialvalue = _initial, \
+		.gpio_data = _pin, \
+		.desc=_desc, \
+	}
+
+#define GPIO_PDEV(_id, _pdata) \
+{ \
+	.name = "gpio-export", \
+	.id = _id, \
+	.dev = { \
+		.platform_data = &_pdata, \
+	}, \
+}
+
+#endif //__LINUX_GPIO_EXPORT_H
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 059bd18..f82edad 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -13,6 +13,7 @@
 #include <linux/errno.h>

 struct device;
+struct gpio_chip;

 /*
  * Some platforms don't support the GPIO programming interface.
@@ -33,6 +34,11 @@ static inline int gpio_request(unsigned gpio, const
char *label)
 	return -ENOSYS;
 }

+static inline int gpio_group_request(unsigned *gpios, int ngpios,
const char *label)
+{
+	return ERR_PTR(-ENOSYS);
+}
+
 static inline void gpio_free(unsigned gpio)
 {
 	might_sleep();
@@ -41,16 +47,32 @@ static inline void gpio_free(unsigned gpio)
 	WARN_ON(1);
 }

+static inline void gpio_group_free(struct gpio_group *group)
+{
+	might_sleep();
+	WARN_ON(1);
+}
+
 static inline int gpio_direction_input(unsigned gpio)
 {
 	return -ENOSYS;
 }

+static inline int gpio_group_direction_input(const struct gpio_group *group)
+{
+	return -ENOSYS;
+}
+
 static inline int gpio_direction_output(unsigned gpio, int value)
 {
 	return -ENOSYS;
 }

+static inline int gpio_group_direction_output(const struct gpio_group
*group, u32 value)
+{
+	return -ENOSYS;
+}
+
 static inline int gpio_get_value(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -58,12 +80,29 @@ static inline int gpio_get_value(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_get_raw(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline void gpio_set_value(unsigned gpio, int value)
 {
 	/* GPIO can never have been requested or set as output */
 	WARN_ON(1);
 }

+static inline void gpio_group_set_raw(const struct gpio_group *group,
u32 value)
+{
+	WARN_ON(1);
+}
+
+static inline int gpio_set_opendrain(unsigned gpio, int value)
+{
+	WARN_ON(1);
+	return -ENOSYS;
+}
+
 static inline int gpio_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -71,6 +110,12 @@ static inline int gpio_cansleep(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_cansleep(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline int gpio_get_value_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -78,12 +123,23 @@ static inline int gpio_get_value_cansleep(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_get_raw_cansleep(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline void gpio_set_value_cansleep(unsigned gpio, int value)
 {
 	/* GPIO can never have been requested or set as output */
 	WARN_ON(1);
 }

+static inline void gpio_group_set_raw_cansleep(const struct
gpio_group *group, u32 value)
+{
+	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 */
@@ -91,6 +147,12 @@ static inline int gpio_export(unsigned gpio, bool
direction_may_change)
 	return -EINVAL;
 }

+static inline int gpio_group_export(struct gpio_group *group, bool
direction_may_change)
+{
+	WARN_ON(1);
+	return -EINVAL;
+}
+
 static inline int gpio_export_link(struct device *dev, const char *name,
 				unsigned gpio)
 {
@@ -99,6 +161,12 @@ static inline int gpio_export_link(struct device
*dev, const char *name,
 	return -EINVAL;
 }

+static inline int gpio_group_export_link(struct device *dev, const char *name,
+    struct gpio_group *group)
+{
+	WARN_ON(1);
+	return -EINVAL;
+}

 static inline void gpio_unexport(unsigned gpio)
 {
@@ -106,6 +174,11 @@ static inline void gpio_unexport(unsigned gpio)
 	WARN_ON(1);
 }

+static inline void gpio_group_unexport(struct gpio_group *group)
+{
+	WARN_ON(1);
+}
+
 static inline int gpio_to_irq(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as input */
@@ -122,4 +195,14 @@ static inline int irq_to_gpio(unsigned irq)

 #endif

+static inline int gpio_direction_output_keep(int gpio)
+{
+	return gpio_direction_output(gpio, gpio_get_value_cansleep(gpio));
+}
+
+static inline int gpio_group_direction_output_keep(const struct
gpio_group *group)
+{
+	return gpio_group_direction_output(group, gpio_group_get_raw_cansleep(group));
+}
+
 #endif /* __LINUX_GPIO_H */
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
@ 2013-01-18 12:13     ` Stijn Devriendt
  0 siblings, 0 replies; 32+ messages in thread
From: Stijn Devriendt @ 2013-01-18 12:13 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Roland,

This mail has been long overdue due to issues with some internal
permission-tool.
Just to be clear, this is not a competing implementation, it's what we
currently use as-is. I'm just posting this as a reference to see if
perhaps more concepts could be reused. It's based on a 2.6.32 kernel.

It includes:
- labels in sysfs (to provide useful names to userspace)
- gpio group support
- exporting individual/groups of gpios dictated by platform-data or device-tree
- open-drain support (different from mainline)
- examplary support for multi-gpio to pcf8575 driver
- gpio_direction_output_keep() function that prevents toggling when
changing direction

Provided-as-is-by: Stijn Devriendt <sdevrien@cisco.com>
---
 drivers/gpio/Kconfig        |    6 +
 drivers/gpio/Makefile       |    1 +
 drivers/gpio/gpio-export.c  |  327 ++++++++++++++
 drivers/gpio/gpiolib.c      | 1021 ++++++++++++++++++++++++++++++++++++++++---
 drivers/gpio/pcf857x.c      |  102 ++++-
 include/asm-generic/gpio.h  |   67 +++
 include/linux/gpio-export.h |   64 +++
 include/linux/gpio.h        |   83 ++++
 8 files changed, 1602 insertions(+), 69 deletions(-)
 create mode 100644 drivers/gpio/gpio-export.c
 create mode 100644 include/linux/gpio-export.h

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 2ad0128..7daf6df 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -48,6 +48,12 @@ config DEBUG_GPIO
 	  slower.  The diagnostics help catch the type of setup errors
 	  that are most common when setting up new platforms or boards.

+config GPIO_EXPORT
+	bool "GPIO export driver"
+	depends on GPIO_SYSFS && GPIOLIB
+	help
+	  Say Y here to include the GPIO export driver.
+
 config GPIO_SYSFS
 	bool "/sys/class/gpio/... (sysfs interface)"
 	depends on SYSFS && EXPERIMENTAL
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 00a532c..40b96d7 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG

 obj-$(CONFIG_GPIOLIB)		+= gpiolib.o

+obj-$(CONFIG_GPIO_EXPORT)	+= gpio-export.o
 obj-$(CONFIG_GPIO_ADP5520)	+= adp5520-gpio.o
 obj-$(CONFIG_GPIO_LANGWELL)	+= langwell_gpio.o
 obj-$(CONFIG_GPIO_MAX7301)	+= max7301.o
diff --git a/drivers/gpio/gpio-export.c b/drivers/gpio/gpio-export.c
new file mode 100644
index 0000000..4ee4fe5
--- /dev/null
+++ b/drivers/gpio/gpio-export.c
@@ -0,0 +1,327 @@
+/* drivers/gpio/gpio-export.c
+ *
+ * Copyright (C) 2011 Stijn Devriendt, Cisco Systems Inc.
+ * Copyright (C) 2011 Eli Steenput, Cisco Systems Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/gpio-export.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_OF
+
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
+#endif
+
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+struct gpio_export_priv
+{
+	int count;
+	int gpio_num;
+	struct gpio_group *group;
+	char desc[MAX_GPIO_LABEL_SIZE];
+};
+
+static __devinit int common_gpio_probe(struct mp_gpio_platform_data
*pdata, struct device *dev)
+{
+	int gpio_count = pdata->gpio_count;
+	struct mp_gpio_line *gpio_line;
+	int i;
+	int err = 0;
+	struct gpio_export_priv *priv;
+
+	if (gpio_count <= 0 || gpio_count > 32)
+		return -ENODEV;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->count = gpio_count;
+	strncpy(priv->desc, pdata->desc, MAX_GPIO_LABEL_SIZE);
+	if (gpio_count > 1)
+	{
+		u32 gpio[32];
+		u32 value = pdata->initialvalue;
+		u32 opendrain = 0;
+		for (i = 0; i < gpio_count; ++i)
+		{
+			gpio_line = &pdata->gpio_data[i];
+			gpio[i] = gpio_line->gpio_num;
+			if (gpio_line->active_low)
+				value ^= (1 << i);
+			if (gpio_line->open_drain)
+				opendrain |= (1 << i);
+		}
+
+		priv->group = gpio_group_request(gpio, gpio_count, priv->desc);
+		if (IS_ERR(priv->group))
+		{
+			dev_err(dev, "Could not request gpio-group: %ld\n", PTR_ERR(priv->group));
+			err = PTR_ERR(priv->group);
+			goto out_mem;
+		}
+
+		if (opendrain)
+		{
+			err = gpio_group_set_opendrain(priv->group,
gpio_group_value_to_raw(priv->group, opendrain));
+			if (err)
+			{
+				dev_err(dev, "Could not set gpio-group open-drain: %d\n", err);
+				goto out_free_group;
+			}
+		}
+
+		switch (pdata->direction)
+		{
+			case GPIO_INPUT:
+				err = gpio_group_direction_input(priv->group);
+				break;
+			case GPIO_OUTPUT:
+				err = gpio_group_direction_output(priv->group,
gpio_group_value_to_raw(priv->group, value));
+				break;
+			case GPIO_OUTPUT_KEEP:
+				err = gpio_group_direction_output_keep(priv->group);
+				break;
+			default:
+        break;
+		}
+
+		if (err)
+		{
+			dev_err(dev, "Could not set gpio-group direction: %d\n", err);
+			goto out_free_group;
+		}
+
+		err = gpio_group_export(priv->group, pdata->direction == GPIO_CHANGE);
+		if (err)
+		{
+			dev_err(dev, "Could not export gpio-group: %d\n", err);
+			goto out_free_group;
+		}
+	}
+	else
+	{
+		gpio_line = &pdata->gpio_data[0];
+		err = gpio_request(gpio_line->gpio_num, priv->desc);
+		if (err)
+		{
+			dev_err(dev, "Could not request gpio %d\n", gpio_line->gpio_num);
+			goto out_free;
+		}
+		if (gpio_line->open_drain)
+		{
+			err = gpio_set_opendrain(gpio_line->gpio_num, 1);
+			if (err)
+			{
+				dev_warn(dev, "Could not set open-drain on gpio %d\n",
gpio_line->gpio_num);
+				goto out_free;
+			}
+		}
+
+		if (pdata->direction == GPIO_INPUT)
+		{
+			err = gpio_direction_input(gpio_line->gpio_num);
+		}
+		else if (pdata->direction == GPIO_OUTPUT)
+		{
+			int value = 0;
+			value = pdata->initialvalue;
+			if (gpio_line->active_low)
+				value = !value;
+			dev_dbg(dev, "Setting output on gpio %d with value %d\n",
gpio_line->gpio_num, value);
+			err = gpio_direction_output(gpio_line->gpio_num, value);
+		}
+		else if (pdata->direction == GPIO_OUTPUT_KEEP)
+		{
+			err = gpio_direction_output_keep(gpio_line->gpio_num);
+		}
+
+		if (err)
+		{
+			dev_err(dev, "Could not set direction: %d\n", err);
+			goto out_free;
+		}
+
+		err = gpio_export(gpio_line->gpio_num, pdata->direction == GPIO_CHANGE);
+		if (err)
+		{
+			dev_warn(dev, "Could not export gpio %d\n", gpio_line->gpio_num);
+			goto out_free;
+		}
+		priv->gpio_num = gpio_line->gpio_num;
+	}
+	dev_set_drvdata(dev, priv);
+	dev_info(dev, "%s: Exported %d GPIO pins\n", pdata->desc, pdata->gpio_count);
+out:
+	return err;
+out_free:
+	gpio_free(gpio_line->gpio_num);
+	kfree(priv);
+	goto out;
+out_free_group:
+	gpio_group_free(priv->group);
+	kfree(priv);
+	goto out;
+out_mem:
+	kfree(priv);
+	goto out;
+}
+
+int common_gpio_remove(struct device *dev)
+{
+	struct gpio_export_priv *priv = dev_get_drvdata(dev);
+	BUG_ON(!priv);
+
+	if (priv->count == 1)
+		gpio_free(priv->gpio_num);
+	else
+		gpio_group_free(priv->group);
+
+	dev_set_drvdata(dev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+
+static __devinit int of_gpio_probe(struct platform_device *of_dev,
+		const struct of_device_id *match)
+{
+	struct device_node *np = of_dev->dev.of_node;
+	int gpio_count = of_gpio_count(np);
+	const char* linuxname = of_get_property(np, "desc", NULL);
+
+	struct mp_gpio_platform_data pdata;
+	struct mp_gpio_line gpio_line[32];
+	int i;
+
+	pdata.gpio_data = gpio_line;
+	pdata.gpio_count = gpio_count;
+	strncpy(pdata.desc, linuxname, MAX_GPIO_LABEL_SIZE);
+	pdata.initialvalue = 0;
+
+	if (of_device_is_compatible(np, "gpio-input"))
+		pdata.direction = GPIO_INPUT;
+	else if (of_device_is_compatible(np, "gpio-output"))
+	{
+		const __be32 *value = of_get_property_u32(np, "initial");
+		if (!value)
+			pdata.direction = GPIO_OUTPUT_KEEP;
+		else
+		{
+			pdata.direction = GPIO_OUTPUT;
+			pdata.initialvalue = be32_to_cpup(value);
+			dev_dbg(&of_dev->dev, "initialvalue=%u, keep=%d\n",
pdata.initialvalue, pdata.direction == GPIO_OUTPUT_KEEP);
+		}
+	}
+	else
+	{
+		pdata.direction = GPIO_CHANGE;
+	}
+
+	for (i=0; i < gpio_count; ++i)
+	{
+		u32 flags;
+		gpio_line[i].gpio_num = of_get_gpio_flags(np, i, &flags);
+		gpio_line[i].active_low = flags & OF_GPIO_ACTIVE_LOW;
+		gpio_line[i].open_drain = flags & OF_GPIO_OPEN_DRAIN;
+	}
+	return common_gpio_probe(&pdata, &of_dev->dev);
+}
+
+static __devexit int of_gpio_remove(struct platform_device *of_dev)
+{
+	return common_gpio_remove(&of_dev->dev);
+}
+
+static const struct of_device_id __devinitconst of_gpio_match[] = {
+	{ .compatible = "gpio-input", },
+	{ .compatible = "gpio-output", },
+	{ .compatible = "gpio-user", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, of_gpio_match);
+
+static struct of_platform_driver of_gpio_driver = {
+	.probe = of_gpio_probe,
+	.remove = __devexit_p(of_gpio_remove),
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "of-gpio",
+		.of_match_table = of_gpio_match,
+	},
+};
+
+static int __init of_gpio_init(void)
+{
+	// Use i2c_bus_type to support I/O expanders?
+	return of_register_platform_driver(&of_gpio_driver);
+}
+
+static void __exit of_gpio_exit(void)
+{
+	of_unregister_driver(&of_gpio_driver);
+}
+
+late_initcall(of_gpio_init);
+module_exit(of_gpio_exit);
+
+#else
+
+static __devinit int mp_probe(struct platform_device *p_device)
+{
+	return common_gpio_probe(dev_get_platdata(&p_device->dev), &p_device->dev);
+}
+
+static __devexit int mp_remove(struct platform_device *p_device)
+{
+	return common_gpio_remove(&p_device->dev);
+}
+
+static struct platform_device_id mp_id_table[] = {
+	{
+		.name		= "gpio-export",
+	},
+};
+
+static struct platform_driver mp_gpio_driver = {
+	.probe = mp_probe,
+	.remove = mp_remove,
+	.id_table = mp_id_table,
+	.driver.name = "gpio-export",
+	.driver.bus = &platform_bus_type,
+	.driver.owner = THIS_MODULE,
+};
+
+static int __init mp_gpio_init(void)
+{
+	return platform_driver_register(&mp_gpio_driver);
+}
+
+static void __exit mp_gpio_exit(void)
+{
+	platform_driver_unregister(&mp_gpio_driver);
+}
+
+late_initcall(mp_gpio_init);
+module_exit(mp_gpio_exit);
+
+#endif
+
+MODULE_AUTHOR("Stijn Devriendt, Eli Steenput");
+MODULE_DESCRIPTION("Multi Purpose GPIO export driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 50de0f5..1c9c426 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -8,7 +8,9 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/idr.h>
+#include <linux/ctype.h>


 /* Optional implementation infrastructure for GPIO interfaces.
@@ -53,15 +55,15 @@ struct gpio_desc {
 #define FLAG_SYSFS	4	/* exported via /sys/class/gpio/control */
 #define FLAG_TRIG_FALL	5	/* trigger on falling edge */
 #define FLAG_TRIG_RISE	6	/* trigger on rising edge */
+#define FLAG_OPEN_DRAIN 7       /* gpio is open drain */

 #define PDESC_ID_SHIFT	16	/* add new flags before this one */

 #define GPIO_FLAGS_MASK		((1 << PDESC_ID_SHIFT) - 1)
 #define GPIO_TRIGGER_MASK	(BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))

-#ifdef CONFIG_DEBUG_FS
 	const char		*label;
-#endif
+	struct gpio_group *group;
 };
 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

@@ -76,9 +78,7 @@ static struct idr pdesc_idr;

 static inline void desc_set_label(struct gpio_desc *d, const char *label)
 {
-#ifdef CONFIG_DEBUG_FS
 	d->label = label;
-#endif
 }

 /* Warn when drivers omit gpio_request() calls -- legal but ill-advised
@@ -119,6 +119,11 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
 	return gpio_desc[gpio].chip;
 }

+static inline struct gpio_group *gpio_to_group(unsigned gpio)
+{
+	return gpio_desc[gpio].group;
+}
+
 /* dynamic allocation of GPIOs, e.g. on a hotplugged device */
 static int gpiochip_find_base(int ngpio)
 {
@@ -189,6 +194,110 @@ err:
 	return ret;
 }

+/* gpio_group_raw_to_value() - reorder bits according to the gpio group
+ * request
+ *
+ * @group: gpio_group
+ * @raw: raw value
+ *
+ * Returns a compact value representing the gpio_group value.
+ * e.g. consider gpio pins [1,0,2,3] have been requested and their
+ * value is [1,0,0,1] respectively. The raw readout will be 0xA [1,0,1,0]
+ * while the return value of this function will be 0x9, considering
+ * the order of the GPIOs in the group.
+ */
+u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw)
+{
+	int i = 0;
+	u32 ret = 0;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+	{
+		unsigned offset = group->gpios[i] - base;
+		u32 rawbit = (1 << offset);
+
+		// if raw[offset] is set
+		// then set ret[i]
+		if (raw & rawbit)
+			ret |= (1 << i);
+
+		++i;
+	}
+	return ret;
+}
+
+/* gpio_group_value_to_raw() - Inverse of gpio_group_raw_to_value
+ *
+ * @group: gpio_group
+ * @raw: compact value
+ *
+ * Returns the raw value.
+ */
+u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value)
+{
+	int i = 0;
+	u32 raw = 0;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+	{
+		unsigned offset = group->gpios[i] - base;
+		u32 rawbit = (1 << offset);
+
+		// if value[i] is set
+		// then set ret[offset]
+		if (value & (1 << i))
+			raw |= rawbit;
+
+		++i;
+	}
+	return raw;
+}
+
+int gpio_group_test_bit(unsigned long flag, const struct gpio_group *group)
+{
+	u32 set = 0;
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i) && test_bit(flag, &gpio_desc[base + i].flags))
+			set |= (1 << i);
+	}
+	if (set == 0)
+		return 0;
+	else if (set == group->mask)
+		return 1;
+	else
+		return -EIO;
+}
+
+void gpio_group_set_bit(unsigned long flag, const struct gpio_group *group)
+{
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i))
+			set_bit(flag, &gpio_desc[base + i].flags);
+	}
+}
+
+void gpio_group_clear_bit(unsigned long flag, const struct gpio_group *group)
+{
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i))
+			clear_bit(flag, &gpio_desc[base + i].flags);
+	}
+}
+
+
 #ifdef CONFIG_GPIO_SYSFS

 /* lock protects against unexport_gpio() being called while
@@ -231,6 +340,30 @@ static ssize_t gpio_direction_show(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_direction_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+
+	status = gpio_group_test_bit(FLAG_IS_OUT, group);
+	if (status >= 0)
+		status = sprintf(buf, "%s\n", status == 0 ? "in" : "out");
+
+out:
+	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)
 {
@@ -255,9 +388,49 @@ static ssize_t gpio_direction_store(struct device *dev,
 	return status ? : size;
 }

-static const DEVICE_ATTR(direction, 0644,
+static ssize_t gpio_group_direction_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+  const struct gpio_group *group = dev_get_drvdata(dev);
+  ssize_t status;
+  unsigned long input;
+
+  mutex_lock(&sysfs_lock);
+
+  if (gpio_desc[group->gpios[0]].group != group
+   || gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+  {
+    status = -EIO;
+    goto out;
+  }
+
+  if (sysfs_streq(buf, "in"))
+    status = gpio_group_direction_input(group);
+  else if (sysfs_streq(buf, "out"))
+    status = gpio_group_direction_output(group, 0);
+  else
+  {
+    status = strict_strtoul(buf, 0, &input);
+    if (!status)
+    {
+      if ((input & group->mask) == input)
+        status = gpio_group_direction_output(group, input);
+      else
+        status = -EINVAL;
+    }
+  }
+
+out:
+  mutex_unlock(&sysfs_lock);
+  return status ? : size;
+}
+
+static DEVICE_ATTR(direction, 0644,
 		gpio_direction_show, gpio_direction_store);

+struct device_attribute dev_attr_direction_group =
+	__ATTR(direction, 0644, gpio_group_direction_show,
gpio_group_direction_store);
+
 static ssize_t gpio_value_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
@@ -276,6 +449,50 @@ static ssize_t gpio_value_show(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_value_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", gpio_group_raw_to_value(group,
+				gpio_group_get_raw_cansleep(group)));
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+
+static ssize_t gpio_group_raw_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", gpio_group_get_raw_cansleep(group));
+
+out:
+	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)
 {
@@ -303,9 +520,96 @@ static ssize_t gpio_value_store(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_value_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+	unsigned long value;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
+			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = strict_strtoul(buf, 0, &value);
+	if (status == 0)
+	{
+		gpio_group_set_raw_cansleep(group,
+				gpio_group_value_to_raw(group, value));
+		status = size;
+	}
+
+out:
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+
+static ssize_t gpio_group_raw_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+	unsigned long value;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
+			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = strict_strtoul(buf, 0, &value);
+	if (status == 0)
+	{
+		gpio_group_set_raw_cansleep(group, value);
+		status = size;
+	}
+
+out:
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+
+static ssize_t gpio_group_mask_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", group->mask);
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
 static /*const*/ DEVICE_ATTR(value, 0644,
 		gpio_value_show, gpio_value_store);

+struct device_attribute dev_attr_value_group =
+	__ATTR(value, 0644, gpio_group_value_show, gpio_group_value_store);
+
+DEVICE_ATTR(raw, 0644, gpio_group_raw_show, gpio_group_raw_store);
+
+DEVICE_ATTR(mask, 0444, gpio_group_mask_show, NULL);
+
 static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
 {
 	struct work_struct	*work = priv;
@@ -381,7 +685,7 @@ static int gpio_setup_irq(struct gpio_desc *desc,
struct device *dev,
 			goto free_id;
 		}

-		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value");
+		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
 		if (!pdesc->value_sd) {
 			ret = -ENODEV;
 			goto free_id;
@@ -475,9 +779,55 @@ found:

 static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);

+static ssize_t gpio_label_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	const struct gpio_desc	*desc = dev_get_drvdata(dev);
+	ssize_t			status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (!test_bit(FLAG_EXPORT, &desc->flags))
+		status = -EIO;
+	else if (desc->label)
+		status = sprintf(buf, "%s\n", desc->label);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static ssize_t gpio_group_label_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	const struct gpio_desc	*desc = &gpio_desc[group->gpios[0]];
+	ssize_t			status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (!test_bit(FLAG_EXPORT, &desc->flags))
+		status = -EIO;
+	else if (desc->label)
+		status = sprintf(buf, "%s\n", desc->label);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static struct device_attribute dev_attr_gpio_label = __ATTR(label,
0444, gpio_label_show, NULL);
+static struct device_attribute dev_attr_gpio_group_label =
__ATTR(label, 0444, gpio_group_label_show, NULL);
+
 static const struct attribute *gpio_attrs[] = {
-	&dev_attr_direction.attr,
 	&dev_attr_value.attr,
+	&dev_attr_gpio_label.attr,
+	NULL,
+};
+
+static const struct attribute *gpio_group_attrs[] = {
+	&dev_attr_value_group.attr,
+	&dev_attr_raw.attr,
+	&dev_attr_mask.attr,
+	&dev_attr_gpio_group_label.attr,
 	NULL,
 };

@@ -485,6 +835,10 @@ static const struct attribute_group gpio_attr_group = {
 	.attrs = (struct attribute **) gpio_attrs,
 };

+static const struct attribute_group gpio_group_attr_group = {
+	.attrs = (struct attribute **) gpio_group_attrs,
+};
+
 /*
  * /sys/class/gpio/gpiochipN/
  *   /base ... matching gpio_chip.base (N)
@@ -530,6 +884,36 @@ static const struct attribute_group gpiochip_attr_group = {
 	.attrs = (struct attribute **) gpiochip_attrs,
 };

+int read_gpios(const char *buf, size_t len, unsigned *gpios)
+{
+	const char *startp = buf;
+	char *endp = (char*)buf;
+	int i;
+	for (i = 0; i < 32; ++i)
+	{
+		unsigned gpio;
+
+		gpio = simple_strtoul(startp, &endp, 0);
+
+		if (endp == startp)
+			return -EINVAL; // not a number, bail out
+
+		gpios[i] = gpio;
+
+		while (endp - buf < len && isspace(*endp)) // eat whitespace
+			endp++;
+
+
+		if (endp - buf > len) // buffer overrun, should never happen
+			return -EINVAL;
+		else if (endp - buf == len) // end of buffer, return number of read gpios
+			return i+1;
+
+		startp = endp;
+	}
+	return -EINVAL;
+}
+
 /*
  * /sys/class/gpio/export ... write-only
  *	integer N ... number of GPIO to export (full access)
@@ -538,10 +922,10 @@ static const struct attribute_group
gpiochip_attr_group = {
  */
 static ssize_t export_store(struct class *class, const char *buf, size_t len)
 {
-	long	gpio;
+	unsigned	gpio[32];
 	int	status;

-	status = strict_strtol(buf, 0, &gpio);
+	status = read_gpios(buf, len, gpio);
 	if (status < 0)
 		goto done;

@@ -550,16 +934,41 @@ static ssize_t export_store(struct class *class,
const char *buf, size_t len)
 	 * they may be undone on its behalf too.
 	 */

-	status = gpio_request(gpio, "sysfs");
-	if (status < 0)
-		goto done;
+	if (status == 1)
+	{
+		status = gpio_request(gpio[0], "sysfs");
+		if (status < 0)
+			goto done;

-	status = gpio_export(gpio, true);
-	if (status < 0)
-		gpio_free(gpio);
+		status = gpio_export(gpio[0], true);
+		if (status < 0)
+			gpio_free(gpio[0]);
+		else
+			set_bit(FLAG_SYSFS, &gpio_desc[gpio[0]].flags);
+	}
 	else
-		set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags);
+	{
+		struct gpio_group *group = gpio_group_request(gpio, status, "sysfs");
+		if (IS_ERR(group))
+		{
+			status = PTR_ERR(group);
+			goto done;
+		}

+		status = gpio_group_export(group, true);
+		if (status < 0)
+			gpio_group_free(group);
+		else
+    {
+      // Lock required to protect against unexport being called
+      // against when only parts of the group have the flag set.
+      // The other cases: all have the flag or none have the flag
+      // are handled correctly.
+      mutex_lock(&sysfs_lock);
+			gpio_group_set_bit(FLAG_SYSFS, group);
+      mutex_unlock(&sysfs_lock);
+    }
+	}
 done:
 	if (status)
 		pr_debug("%s: status %d\n", __func__, status);
@@ -570,6 +979,7 @@ static ssize_t unexport_store(struct class *class,
const char *buf, size_t len)
 {
 	long	gpio;
 	int	status;
+	struct gpio_group *group;

 	status = strict_strtol(buf, 0, &gpio);
 	if (status < 0)
@@ -581,13 +991,29 @@ static ssize_t unexport_store(struct class
*class, const char *buf, size_t len)
 	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);
+	group = gpio_to_group(gpio);
+	if (group)
+	{
+		mutex_lock(&sysfs_lock);
+		if (gpio_group_test_bit(FLAG_SYSFS, group))
+		{
+			status = 0;
+			gpio_group_clear_bit(FLAG_SYSFS, group);
+		}
+		mutex_unlock(&sysfs_lock);
+		if (!status)
+			gpio_group_free(group);
+	}
+	else
+	{
+		/* 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)
@@ -662,12 +1088,11 @@ int gpio_export(unsigned gpio, bool direction_may_change)
 		dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
 				desc, ioname ? ioname : "gpio%d", gpio);
 		if (!IS_ERR(dev)) {
-			if (direction_may_change)
-				status = sysfs_create_group(&dev->kobj,
-						&gpio_attr_group);
-			else
-				status = device_create_file(dev,
-						&dev_attr_value);
+  		status = sysfs_create_group(&dev->kobj,
+  						&gpio_attr_group);
+
+			if (!status && direction_may_change)
+				status = device_create_file(dev, &dev_attr_direction);

 			if (!status && gpio_to_irq(gpio) >= 0
 					&& (direction_may_change
@@ -694,6 +1119,72 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_export);

+int gpio_group_export(struct gpio_group *group, bool direction_may_change)
+{
+	unsigned long flags;
+	int status = -EINVAL;
+	struct gpio_chip *chip;
+	unsigned lowest = group->gpios[0];
+	int i;
+
+	/* can't export until sysfs is available ... */
+	if (!gpio_class.p) {
+		pr_debug("%s: called too early!\n", __func__);
+		return -ENOENT;
+	}
+
+	mutex_lock(&sysfs_lock);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	chip = gpio_to_chip(group->gpios[0]);
+	if (gpio_group_test_bit(FLAG_REQUESTED, group) > 0
+			&& gpio_group_test_bit(FLAG_EXPORT, group) == 0)
+	{
+		status = 0;
+		if (!chip->direction_input_multi || !chip->direction_output_multi)
+		{
+			direction_may_change = false;
+		}
+	}
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	if (status == 0)
+	{
+		struct device *dev;
+
+		i=1;
+		while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+		{
+			if (group->gpios[i] < lowest)
+				lowest = group->gpios[i];
+			++i;
+		}
+
+		dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0),
+				group, "group%d", lowest);
+		if (!IS_ERR(dev)) {
+			status = sysfs_create_group(&dev->kobj, &gpio_group_attr_group);
+
+			if (status == 0 && direction_may_change)
+				status = device_create_file(dev, &dev_attr_direction_group);
+
+			if (status != 0)
+				device_unregister(dev);
+		}
+		else
+			status = PTR_ERR(dev);
+		if (status == 0)
+			gpio_group_set_bit(FLAG_EXPORT, group);
+	}
+	mutex_unlock(&sysfs_lock);
+
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_group_export);
+
+
 static int match_export(struct device *dev, void *data)
 {
 	return dev_get_drvdata(dev) == data;
@@ -744,6 +1235,30 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_export_link);

+int gpio_group_export_link(struct device *dev, const char *name,
struct gpio_group *group)
+{
+	int status = -EINVAL;
+	mutex_lock(&sysfs_lock);
+	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
+	{
+		struct device *tdev;
+
+		tdev = class_find_device(&gpio_class, NULL, group, match_export);
+		if (tdev != NULL) {
+			status = sysfs_create_link(&dev->kobj, &tdev->kobj, name);
+		}
+		else
+		{
+			status = -ENODEV;
+		}
+	}
+	mutex_unlock(&sysfs_lock);
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_group_export_link);
+
 /**
  * gpio_unexport - reverse effect of gpio_export()
  * @gpio: gpio to make unavailable
@@ -783,6 +1298,31 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_unexport);

+void gpio_group_unexport(struct gpio_group *group)
+{
+	int status = -EINVAL;
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
+	{
+		struct device *dev = NULL;
+		dev = class_find_device(&gpio_class, NULL, group, match_export);
+		if (dev)
+		{
+			gpio_group_clear_bit(FLAG_EXPORT, group);
+			put_device(dev);
+			device_unregister(dev);
+			status = 0;
+		}
+		else
+			status = -ENODEV;
+	}
+	mutex_unlock(&sysfs_lock);
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+}
+EXPORT_SYMBOL_GPL(gpio_group_unexport);
+
 static int gpiochip_export(struct gpio_chip *chip)
 {
 	int		status;
@@ -959,6 +1499,8 @@ int gpiochip_add(struct gpio_chip *chip)
 		}
 	}

+	of_gpiochip_add(chip);
+
 unlock:
 	spin_unlock_irqrestore(&gpio_lock, flags);
 	if (status == 0)
@@ -987,6 +1529,8 @@ int gpiochip_remove(struct gpio_chip *chip)

 	spin_lock_irqsave(&gpio_lock, flags);

+	of_gpiochip_remove(chip);
+
 	for (id = chip->base; id < chip->base + chip->ngpio; id++) {
 		if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) {
 			status = -EBUSY;
@@ -1007,6 +1551,38 @@ int gpiochip_remove(struct gpio_chip *chip)
 }
 EXPORT_SYMBOL_GPL(gpiochip_remove);

+/**
+ * gpiochip_find() - iterator for locating a specific gpio_chip
+ * @data: data to pass to match function
+ * @callback: Callback function to check gpio_chip
+ *
+ * Similar to bus_find_device.  It returns a reference to a gpio_chip as
+ * determined by a user supplied @match callback.  The callback should return
+ * 0 if the device doesn't match and non-zero if it does.  If the callback is
+ * non-zero, this function will return to the caller and not iterate over any
+ * more gpio_chips.
+ */
+struct gpio_chip *gpiochip_find(void *data,
+				int (*match)(struct gpio_chip *chip, void *data))
+{
+	struct gpio_chip *chip = NULL;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	for (i = 0; i < ARCH_NR_GPIOS; i++) {
+		if (!gpio_desc[i].chip)
+			continue;
+
+		if (match(gpio_desc[i].chip, data)) {
+			chip = gpio_desc[i].chip;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	return chip;
+}

 /* These "optional" allocation calls help prevent drivers from stomping
  * on each other, and help provide better diagnostics in debugfs.
@@ -1066,6 +1642,111 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_request);

+struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
const char* label)
+{
+	int i;
+	unsigned long flags;
+	u32 mask = 0;
+	struct gpio_chip *chip;
+	int rc = 0;
+	struct gpio_group *group;
+
+	if (ngpios <= 0)
+		return ERR_PTR(-EINVAL);
+
+	group = kzalloc(sizeof(struct gpio_group), GFP_KERNEL);
+	if (!group)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	if (!gpio_is_valid(gpio[0]))
+	{
+		rc = -EINVAL;
+		goto out_free;
+	}
+	chip = gpio_to_chip(gpio[0]);
+	// if not (multi-input or multi-output capable)
+	if (!chip ||
+			!(   (chip->get_multi && chip->direction_input_multi)
+				|| (chip->set_multi && chip->direction_output_multi))
+	   )
+	{
+		rc = -ENODEV;
+		goto out_free;
+	}
+
+	mask |= (1 << (gpio[0] - chip->base));
+	for (i = 1; i < ngpios; ++i)
+	{
+		if (gpio[i] < chip->base || gpio[i] > chip->base + chip->ngpio)
+		{
+			rc = -EINVAL;
+			goto out_free;
+		}
+		mask |= (1 << (gpio[i] - chip->base));
+	}
+
+	if (!try_module_get(chip->owner))
+	{
+		rc = -ENOSYS;
+		goto out_free;
+	}
+
+	group->mask = mask;
+	for (i = 0; i < 32; ++i)
+	{
+		if (i < ngpios)
+			group->gpios[i] = gpio[i];
+		else
+			group->gpios[i] = ARCH_NR_GPIOS;
+	}
+
+	if (gpio_group_test_bit(FLAG_REQUESTED, group) != 0)
+	{
+		rc = -EBUSY;
+		goto out_put;
+	}
+
+	gpio_group_set_bit(FLAG_REQUESTED, group);
+	for (i = 0; i < ngpios; ++i)
+	{
+		gpio_desc[gpio[i]].group = group;
+		desc_set_label(&gpio_desc[gpio[i]], label ? : "?");
+	}
+
+	if (chip->request_multi) {
+		spin_unlock_irqrestore(&gpio_lock, flags);
+		rc = chip->request_multi(chip, mask);
+		spin_lock_irqsave(&gpio_lock, flags);
+		if (rc)
+			goto out_label;
+	}
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	return group;
+out_label:
+	for (i = 0; i < ngpios; ++i)
+	{
+		desc_set_label(&gpio_desc[gpio[i]], NULL);
+		gpio_desc[gpio[i]].group = NULL;
+	}
+out_put:
+	module_put(chip->owner);
+out_free:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	kfree(group);
+	pr_debug("%s: group%d (%s) status %d\n", __func__, *gpio, label ? : "?", rc);
+	return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(gpio_group_request);
+
+struct gpio_group * gpio_get_group(unsigned gpio)
+{
+	if (!gpio_is_valid(gpio))
+		return NULL;
+	return gpio_desc[gpio].group;
+}
+
 void gpio_free(unsigned gpio)
 {
 	unsigned long		flags;
@@ -1102,6 +1783,45 @@ void gpio_free(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(gpio_free);

+void gpio_group_free(struct gpio_group *group)
+{
+	unsigned long flags;
+	struct gpio_chip *chip;
+
+	might_sleep();
+
+	gpio_group_unexport(group);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip = gpio_to_chip(group->gpios[0]);
+	if (chip && gpio_group_test_bit(FLAG_REQUESTED, group))
+	{
+		int i;
+		if (chip->free_multi) {
+			spin_unlock_irqrestore(&gpio_lock, flags);
+			might_sleep_if(extra_checks && chip->can_sleep);
+			chip->free_multi(chip, group->mask);
+			spin_lock_irqsave(&gpio_lock, flags);
+		}
+		for (i = 0; i < 32; ++i)
+		{
+			if (group->mask & (1 << i))
+			{
+				desc_set_label(&gpio_desc[chip->base + i], NULL);
+				gpio_desc[chip->base +i].group = NULL;
+			}
+			module_put(chip->owner);
+			gpio_group_clear_bit(FLAG_REQUESTED, group);
+		}
+	}
+	else
+		WARN_ON(extra_checks);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	kfree(group);
+}
+EXPORT_SYMBOL_GPL(gpio_group_free);

 /**
  * gpiochip_is_requested - return string iff signal was requested
@@ -1124,15 +1844,10 @@ const char *gpiochip_is_requested(struct
gpio_chip *chip, unsigned offset)
 		return NULL;
 	if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0)
 		return NULL;
-#ifdef CONFIG_DEBUG_FS
 	return gpio_desc[gpio].label;
-#else
-	return "?";
-#endif
 }
 EXPORT_SYMBOL_GPL(gpiochip_is_requested);

-
 /* Drivers MUST set GPIO direction before making get/set calls.  In
  * some cases this is done in early boot, before IRQs are enabled.
  *
@@ -1195,6 +1910,25 @@ fail:
 }
 EXPORT_SYMBOL_GPL(gpio_direction_input);

+
+int gpio_group_direction_input(const struct gpio_group *group)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int rc;
+
+	if (!chip->get_multi || !chip->direction_input_multi)
+		return -EINVAL;
+
+	rc = chip->direction_input_multi(chip, group->mask);
+	if (rc == 0)
+	{
+		gpio_group_clear_bit(FLAG_IS_OUT, group);
+	}
+	else
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
+	return rc;
+}
+
 int gpio_direction_output(unsigned gpio, int value)
 {
 	unsigned long		flags;
@@ -1248,6 +1982,124 @@ fail:
 }
 EXPORT_SYMBOL_GPL(gpio_direction_output);

+int gpio_group_direction_output(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int rc;
+
+	if (!chip->set_multi || !chip->direction_output_multi)
+		return -EINVAL;
+
+	rc = chip->direction_output_multi(chip, group->mask, value & group->mask);
+	if (rc == 0)
+	{
+		gpio_group_set_bit(FLAG_IS_OUT, group);
+	}
+	else
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
+	return rc;
+}
+
+int gpio_set_opendrain(unsigned gpio, int value)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+	struct gpio_desc	*desc = &gpio_desc[gpio];
+	int			status = -EINVAL;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	if (!gpio_is_valid(gpio))
+		goto fail;
+	chip = desc->chip;
+	if (!chip || !chip->set || !chip->direction_output)
+		goto fail;
+	gpio -= chip->base;
+	if (gpio >= chip->ngpio)
+		goto fail;
+	status = gpio_ensure_requested(desc, gpio);
+	if (status < 0)
+		goto fail;
+
+	/* now we know the gpio is valid and chip won't vanish */
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	might_sleep_if(extra_checks && chip->can_sleep);
+
+	if (!chip->set_opendrain)
+		return -ENOSYS;
+
+	if (status) {
+		status = chip->request(chip, gpio);
+		if (status < 0) {
+			pr_debug("GPIO-%d: chip request fail, %d\n",
+				chip->base + gpio, status);
+			/* and it's not available to anyone else ...
+			 * gpio_request() is the fully clean solution.
+			 */
+			goto lose;
+		}
+	}
+
+	status = chip->set_opendrain(chip, gpio, value);
+	if (status == 0)
+	{
+		if (value)
+			set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+		else
+			clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
+	}
+lose:
+	return status;
+fail:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	if (status)
+		pr_debug("%s: gpio-%d status %d\n",
+			__func__, gpio, status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_set_opendrain);
+
+int gpio_group_set_opendrain(struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int err;
+	if (!chip)
+		return -EINVAL;
+	if (!chip->set_multi || !chip->direction_output_multi)
+		return -EINVAL;
+	if (!chip->set_opendrain_multi)
+		return -ENOSYS;
+
+	might_sleep_if(extra_checks && chip->can_sleep);
+
+	err = chip->set_opendrain_multi(chip, group->mask, value & group->mask);
+	if (err == 0)
+	{
+		int i;
+		unsigned base = chip->base;
+		for (i = 0; i < 32; ++i)
+		{
+			if (group->mask & (1 << i))
+			{
+				if (value & (1 << i))
+				{
+					set_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
+				}
+				else
+				{
+					clear_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
+				}
+			}
+		}
+	}
+	if (err)
+		pr_debug("%s: group%d status %d\n",
+				__func__, group->gpios[0], err);
+	return err;
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_opendrain);

 /* I/O calls are only valid after configuration completed; the relevant
  * "is this a valid GPIO" error checks should already have been done.
@@ -1290,6 +2142,14 @@ int __gpio_get_value(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(__gpio_get_value);

+u32 gpio_group_get_raw(const struct gpio_group* group)
+{
+	struct gpio_chip *chip;
+	chip = gpio_to_chip(group->gpios[0]);
+	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
+}
+EXPORT_SYMBOL_GPL(gpio_group_get_raw);
+
 /**
  * __gpio_set_value() - assign a gpio's value
  * @gpio: gpio whose value will be assigned
@@ -1309,6 +2169,13 @@ void __gpio_set_value(unsigned gpio, int value)
 }
 EXPORT_SYMBOL_GPL(__gpio_set_value);

+void gpio_group_set_raw(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip;
+	chip = gpio_to_chip(group->gpios[0]);
+	chip->set_multi(chip, group->mask, value | group->mask);
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_raw);
 /**
  * __gpio_cansleep() - report whether gpio value access will sleep
  * @gpio: gpio in question
@@ -1328,6 +2195,11 @@ int __gpio_cansleep(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(__gpio_cansleep);

+int gpio_group_cansleep(const struct gpio_group *group)
+{
+	return __gpio_cansleep(group->gpios[0]);
+}
+
 /**
  * __gpio_to_irq() - return the IRQ corresponding to a GPIO
  * @gpio: gpio whose IRQ will be returned (already requested)
@@ -1362,6 +2234,15 @@ int gpio_get_value_cansleep(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);

+u32 gpio_group_get_raw_cansleep(const struct gpio_group *group)
+{
+	struct gpio_chip *chip;
+	might_sleep_if(extra_checks);
+	chip = gpio_to_chip(group->gpios[0]);
+	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
+}
+EXPORT_SYMBOL_GPL(gpio_group_get_raw_cansleep);
+
 void gpio_set_value_cansleep(unsigned gpio, int value)
 {
 	struct gpio_chip	*chip;
@@ -1372,6 +2253,14 @@ void gpio_set_value_cansleep(unsigned gpio, int value)
 }
 EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);

+void gpio_group_set_raw_cansleep(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip;
+	might_sleep_if(extra_checks);
+	chip = gpio_to_chip(group->gpios[0]);
+	chip->set_multi(chip, group->mask, value & group->mask);
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_raw_cansleep);

 #ifdef CONFIG_DEBUG_FS

@@ -1381,18 +2270,24 @@ static void gpiolib_dbg_show(struct seq_file
*s, struct gpio_chip *chip)
 	unsigned		gpio = chip->base;
 	struct gpio_desc	*gdesc = &gpio_desc[gpio];
 	int			is_out;
+	int			is_open;
+	int     is_group;

 	for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
 		if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
 			continue;

 		is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
-		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s",
-			gpio, gdesc->label,
-			is_out ? "out" : "in ",
-			chip->get
+		is_open = test_bit(FLAG_OPEN_DRAIN, &gdesc->flags);
+		is_group = (gdesc->group != NULL);
+		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s",
+				gpio, gdesc->label,
+				is_out ? "out" : "in ",
+				chip->get
 				? (chip->get(chip, i) ? "hi" : "lo")
-				: "?  ");
+				: "?  ",
+				is_open ? "open" : "act ",
+				is_group ? "grp" : "pin");

 		if (!is_out) {
 			int		irq = gpio_to_irq(gpio);
@@ -1408,32 +2303,32 @@ static void gpiolib_dbg_show(struct seq_file
*s, struct gpio_chip *chip)
 				char *trigger;

 				switch (desc->status & IRQ_TYPE_SENSE_MASK) {
-				case IRQ_TYPE_NONE:
-					trigger = "(default)";
-					break;
-				case IRQ_TYPE_EDGE_FALLING:
-					trigger = "edge-falling";
-					break;
-				case IRQ_TYPE_EDGE_RISING:
-					trigger = "edge-rising";
-					break;
-				case IRQ_TYPE_EDGE_BOTH:
-					trigger = "edge-both";
-					break;
-				case IRQ_TYPE_LEVEL_HIGH:
-					trigger = "level-high";
-					break;
-				case IRQ_TYPE_LEVEL_LOW:
-					trigger = "level-low";
-					break;
-				default:
-					trigger = "?trigger?";
-					break;
+					case IRQ_TYPE_NONE:
+						trigger = "(default)";
+						break;
+					case IRQ_TYPE_EDGE_FALLING:
+						trigger = "edge-falling";
+						break;
+					case IRQ_TYPE_EDGE_RISING:
+						trigger = "edge-rising";
+						break;
+					case IRQ_TYPE_EDGE_BOTH:
+						trigger = "edge-both";
+						break;
+					case IRQ_TYPE_LEVEL_HIGH:
+						trigger = "level-high";
+						break;
+					case IRQ_TYPE_LEVEL_LOW:
+						trigger = "level-low";
+						break;
+					default:
+						trigger = "?trigger?";
+						break;
 				}

 				seq_printf(s, " irq-%d %s%s",
-					irq, trigger,
-					(desc->status & IRQ_WAKEUP)
+						irq, trigger,
+						(desc->status & IRQ_WAKEUP)
 						? " wakeup" : "");
 			}
 		}
diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c
index 29f19ce..a51d9ad 100644
--- a/drivers/gpio/pcf857x.c
+++ b/drivers/gpio/pcf857x.c
@@ -79,6 +79,20 @@ static int pcf857x_input8(struct gpio_chip *chip,
unsigned offset)
 	return status;
 }

+static int pcf857x_input8_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out |= (u16)mask;
+	status = i2c_smbus_write_byte(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
+
 static int pcf857x_get8(struct gpio_chip *chip, unsigned offset)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -88,6 +102,15 @@ static int pcf857x_get8(struct gpio_chip *chip,
unsigned offset)
 	return (value < 0) ? 0 : (value & (1 << offset));
 }

+static u32 pcf857x_get8_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	u32		value;
+
+	value = i2c_smbus_read_byte(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
 static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -105,6 +128,25 @@ static int pcf857x_output8(struct gpio_chip
*chip, unsigned offset, int value)
 	return status;
 }

+static int pcf857x_output8_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= value;
+	status = i2c_smbus_write_byte(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
+static void pcf857x_set8_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	pcf857x_output8_multi(chip, mask, value);
+}
+
 static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
 {
 	pcf857x_output8(chip, offset, value);
@@ -147,6 +189,19 @@ static int pcf857x_input16(struct gpio_chip
*chip, unsigned offset)
 	return status;
 }

+static int pcf857x_input16_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out |= mask;
+	status = i2c_write_le16(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
 static int pcf857x_get16(struct gpio_chip *chip, unsigned offset)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -156,6 +211,15 @@ static int pcf857x_get16(struct gpio_chip *chip,
unsigned offset)
 	return (value < 0) ? 0 : (value & (1 << offset));
 }

+static u32 pcf857x_get16_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	u32		value;
+
+	value = i2c_read_le16(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
 static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -173,11 +237,30 @@ static int pcf857x_output16(struct gpio_chip
*chip, unsigned offset, int value)
 	return status;
 }

+static int pcf857x_output16_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= value;
+	status = i2c_write_le16(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
 static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value)
 {
 	pcf857x_output16(chip, offset, value);
 }

+static void pcf857x_set16_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	pcf857x_output16_multi(chip, mask, value);
+}
+
 /*-------------------------------------------------------------------------*/

 static int pcf857x_probe(struct i2c_client *client,
@@ -190,7 +273,6 @@ static int pcf857x_probe(struct i2c_client *client,
 	pdata = client->dev.platform_data;
 	if (!pdata) {
 		dev_dbg(&client->dev, "no platform data\n");
-		return -EINVAL;
 	}

 	/* Allocate, initialize, and register this gpio_chip. */
@@ -200,7 +282,7 @@ static int pcf857x_probe(struct i2c_client *client,

 	mutex_init(&gpio->lock);

-	gpio->chip.base = pdata->gpio_base;
+	gpio->chip.base = pdata ? pdata->gpio_base : -1;
 	gpio->chip.can_sleep = 1;
 	gpio->chip.dev = &client->dev;
 	gpio->chip.owner = THIS_MODULE;
@@ -218,10 +300,14 @@ static int pcf857x_probe(struct i2c_client *client,
 	 */
 	gpio->chip.ngpio = id->driver_data;
 	if (gpio->chip.ngpio == 8) {
+		gpio->chip.direction_input_multi = pcf857x_input8_multi;
 		gpio->chip.direction_input = pcf857x_input8;
 		gpio->chip.get = pcf857x_get8;
+		gpio->chip.get_multi = pcf857x_get8_multi;
 		gpio->chip.direction_output = pcf857x_output8;
+		gpio->chip.direction_output_multi = pcf857x_output8_multi;
 		gpio->chip.set = pcf857x_set8;
+		gpio->chip.set_multi = pcf857x_set8_multi;

 		if (!i2c_check_functionality(client->adapter,
 				I2C_FUNC_SMBUS_BYTE))
@@ -239,9 +325,13 @@ static int pcf857x_probe(struct i2c_client *client,
 	 */
 	} else if (gpio->chip.ngpio == 16) {
 		gpio->chip.direction_input = pcf857x_input16;
+		gpio->chip.direction_input_multi = pcf857x_input16_multi;
 		gpio->chip.get = pcf857x_get16;
+		gpio->chip.get_multi = pcf857x_get16_multi;
 		gpio->chip.direction_output = pcf857x_output16;
+		gpio->chip.direction_output_multi = pcf857x_output16_multi;
 		gpio->chip.set = pcf857x_set16;
+		gpio->chip.set_multi = pcf857x_set16_multi;

 		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 			status = -EIO;
@@ -278,7 +368,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	 * to zero, our software copy of the "latch" then matches the chip's
 	 * all-ones reset state.  Otherwise it flags pins to be driven low.
 	 */
-	gpio->out = ~pdata->n_latch;
+	gpio->out = pdata ? ~pdata->n_latch : ~0;

 	status = gpiochip_add(&gpio->chip);
 	if (status < 0)
@@ -299,7 +389,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	/* Let platform code set up the GPIOs and their users.
 	 * Now is the first time anyone could use them.
 	 */
-	if (pdata->setup) {
+	if (pdata && pdata->setup) {
 		status = pdata->setup(client,
 				gpio->chip.base, gpio->chip.ngpio,
 				pdata->context);
@@ -310,7 +400,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	return 0;

 fail:
-	dev_dbg(&client->dev, "probe error %d for '%s'\n",
+	dev_err(&client->dev, "probe error %d for '%s'\n",
 			status, client->name);
 	kfree(gpio);
 	return status;
@@ -322,7 +412,7 @@ static int pcf857x_remove(struct i2c_client *client)
 	struct pcf857x			*gpio = i2c_get_clientdata(client);
 	int				status = 0;

-	if (pdata->teardown) {
+	if (pdata && pdata->teardown) {
 		status = pdata->teardown(client,
 				gpio->chip.base, gpio->chip.ngpio,
 				pdata->context);
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 66d6106..ed2407f 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -30,6 +30,7 @@ static inline int gpio_is_valid(int number)

 struct seq_file;
 struct module;
+struct device_node;

 /**
  * struct gpio_chip - abstract a GPIO controller
@@ -89,6 +90,22 @@ struct gpio_chip {
 						unsigned offset, int value);
 	void			(*set)(struct gpio_chip *chip,
 						unsigned offset, int value);
+	int			(*set_opendrain)(struct gpio_chip *chip,
+						unsigned offset, int value);
+	int			(*set_opendrain_multi)(struct gpio_chip *chip,
+						u32 mask, u32 value);
+
+	int			(*request_multi)(struct gpio_chip *chip,
+						u32 mask);
+	void			(*free_multi)(struct gpio_chip *chip,
+						u32 mask);
+
+	int			(*direction_input_multi)(struct gpio_chip *chip,
+						u32 mask);
+	int			(*direction_output_multi)(struct gpio_chip *chip,
+						u32 mask, u32 value);
+	void			(*set_multi)(struct gpio_chip *chip, u32 mask, u32 value);
+	u32			(*get_multi)(struct gpio_chip *chip, u32 mask);

 	int			(*to_irq)(struct gpio_chip *chip,
 						unsigned offset);
@@ -100,6 +117,23 @@ struct gpio_chip {
 	char			**names;
 	unsigned		can_sleep:1;
 	unsigned		exported:1;
+
+#if defined(CONFIG_OF_GPIO)
+	/*
+	 * If CONFIG_OF is enabled, then all GPIO controllers described in the
+	 * device tree automatically may have an OF translation
+	 */
+	struct device_node *of_node;
+	int of_gpio_n_cells;
+	int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
+		        const void *gpio_spec, u32 *flags);
+#endif
+};
+
+struct gpio_group
+{
+	unsigned gpios[32];
+	u32 mask;
 };

 extern const char *gpiochip_is_requested(struct gpio_chip *chip,
@@ -109,6 +143,9 @@ extern int __must_check gpiochip_reserve(int
start, int ngpio);
 /* add/remove chips */
 extern int gpiochip_add(struct gpio_chip *chip);
 extern int __must_check gpiochip_remove(struct gpio_chip *chip);
+extern struct gpio_chip *gpiochip_find(void *data,
+					int (*match)(struct gpio_chip *chip,
+						     void *data));


 /* Always use the library code for GPIO management calls,
@@ -120,9 +157,28 @@ extern void gpio_free(unsigned gpio);
 extern int gpio_direction_input(unsigned gpio);
 extern int gpio_direction_output(unsigned gpio, int value);

+extern int gpio_set_opendrain(unsigned gpio, int value);
+
 extern int gpio_get_value_cansleep(unsigned gpio);
 extern void gpio_set_value_cansleep(unsigned gpio, int value);

+/*
+ * Handling of gpio groups
+ */
+extern struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
+    const char *label);
+extern void gpio_group_free(struct gpio_group* group);
+
+extern int gpio_group_direction_input(const struct gpio_group *group);
+extern int gpio_group_direction_output(const struct gpio_group
*group, u32 value);
+
+extern u32 gpio_group_get_raw_cansleep(const struct gpio_group *group);
+extern void gpio_group_set_raw_cansleep(const struct gpio_group
*group, u32 value);
+
+extern int gpio_group_set_opendrain(struct gpio_group* group, u32 value);
+
+u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw);
+u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value);

 /* A platform's <asm/gpio.h> code may want to inline the I/O calls when
  * the GPIO is constant and refers to some always-present controller,
@@ -135,6 +191,11 @@ extern int __gpio_cansleep(unsigned gpio);

 extern int __gpio_to_irq(unsigned gpio);

+extern u32 gpio_group_get_raw(const struct gpio_group *group);
+extern void gpio_group_set_raw(const struct gpio_group *group, u32 value);
+
+extern int gpio_group_cansleep(const struct gpio_group *group);
+
 #ifdef CONFIG_GPIO_SYSFS

 /*
@@ -146,6 +207,12 @@ extern int gpio_export_link(struct device *dev,
const char *name,
 			unsigned gpio);
 extern void gpio_unexport(unsigned gpio);

+extern int gpio_group_export(struct gpio_group *group,
+    bool direction_may_change);
+extern int gpio_group_export_link(struct device *dev, const char *name,
+    struct gpio_group *group);
+extern void gpio_group_unexport(struct gpio_group *group);
+
 #endif	/* CONFIG_GPIO_SYSFS */

 #else	/* !CONFIG_HAVE_GPIO_LIB */
diff --git a/include/linux/gpio-export.h b/include/linux/gpio-export.h
new file mode 100644
index 0000000..712e9ff
--- /dev/null
+++ b/include/linux/gpio-export.h
@@ -0,0 +1,64 @@
+/*    Structures for passing gpio settings to drivers/gpio/gpio-export.c
+ *    201103 steene99
+ */
+#ifndef __LINUX_GPIO_EXPORT_H
+#define __LINUX_GPIO_EXPORT_H
+
+enum gpio_direction
+{
+	GPIO_INPUT,
+	GPIO_OUTPUT,
+	GPIO_CHANGE,
+	GPIO_OUTPUT_KEEP,
+};
+
+struct mp_gpio_line
+{
+	int gpio_num;
+	int active_low;
+	int open_drain;
+};
+
+#define GPIO_PIN(_nr, _active_low, _open_drain) \
+{ \
+	.gpio_num = _nr, \
+	.active_low = _active_low, \
+	.open_drain = _open_drain, \
+}
+
+#define SIMPLE_GPIO_PIN(_name, _nr) \
+static struct mp_gpio_line _name[] = \
+{ \
+	GPIO_PIN(_nr, 0, 0), \
+}
+
+#define MAX_GPIO_LABEL_SIZE 32
+
+struct mp_gpio_platform_data
+{
+	int gpio_count;
+	enum gpio_direction direction;
+	u32 initialvalue;  // value
+	struct mp_gpio_line *gpio_data;
+	char desc[MAX_GPIO_LABEL_SIZE];
+};
+
+#define DEFINE_GPIO_GROUP(_name, _count, _direction, _initial, _pin, _desc) \
+	static struct mp_gpio_platform_data _name = { \
+		.gpio_count = _count, \
+		.direction = _direction, \
+		.initialvalue = _initial, \
+		.gpio_data = _pin, \
+		.desc=_desc, \
+	}
+
+#define GPIO_PDEV(_id, _pdata) \
+{ \
+	.name = "gpio-export", \
+	.id = _id, \
+	.dev = { \
+		.platform_data = &_pdata, \
+	}, \
+}
+
+#endif //__LINUX_GPIO_EXPORT_H
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 059bd18..f82edad 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -13,6 +13,7 @@
 #include <linux/errno.h>

 struct device;
+struct gpio_chip;

 /*
  * Some platforms don't support the GPIO programming interface.
@@ -33,6 +34,11 @@ static inline int gpio_request(unsigned gpio, const
char *label)
 	return -ENOSYS;
 }

+static inline int gpio_group_request(unsigned *gpios, int ngpios,
const char *label)
+{
+	return ERR_PTR(-ENOSYS);
+}
+
 static inline void gpio_free(unsigned gpio)
 {
 	might_sleep();
@@ -41,16 +47,32 @@ static inline void gpio_free(unsigned gpio)
 	WARN_ON(1);
 }

+static inline void gpio_group_free(struct gpio_group *group)
+{
+	might_sleep();
+	WARN_ON(1);
+}
+
 static inline int gpio_direction_input(unsigned gpio)
 {
 	return -ENOSYS;
 }

+static inline int gpio_group_direction_input(const struct gpio_group *group)
+{
+	return -ENOSYS;
+}
+
 static inline int gpio_direction_output(unsigned gpio, int value)
 {
 	return -ENOSYS;
 }

+static inline int gpio_group_direction_output(const struct gpio_group
*group, u32 value)
+{
+	return -ENOSYS;
+}
+
 static inline int gpio_get_value(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -58,12 +80,29 @@ static inline int gpio_get_value(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_get_raw(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline void gpio_set_value(unsigned gpio, int value)
 {
 	/* GPIO can never have been requested or set as output */
 	WARN_ON(1);
 }

+static inline void gpio_group_set_raw(const struct gpio_group *group,
u32 value)
+{
+	WARN_ON(1);
+}
+
+static inline int gpio_set_opendrain(unsigned gpio, int value)
+{
+	WARN_ON(1);
+	return -ENOSYS;
+}
+
 static inline int gpio_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -71,6 +110,12 @@ static inline int gpio_cansleep(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_cansleep(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline int gpio_get_value_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -78,12 +123,23 @@ static inline int gpio_get_value_cansleep(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_get_raw_cansleep(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline void gpio_set_value_cansleep(unsigned gpio, int value)
 {
 	/* GPIO can never have been requested or set as output */
 	WARN_ON(1);
 }

+static inline void gpio_group_set_raw_cansleep(const struct
gpio_group *group, u32 value)
+{
+	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 */
@@ -91,6 +147,12 @@ static inline int gpio_export(unsigned gpio, bool
direction_may_change)
 	return -EINVAL;
 }

+static inline int gpio_group_export(struct gpio_group *group, bool
direction_may_change)
+{
+	WARN_ON(1);
+	return -EINVAL;
+}
+
 static inline int gpio_export_link(struct device *dev, const char *name,
 				unsigned gpio)
 {
@@ -99,6 +161,12 @@ static inline int gpio_export_link(struct device
*dev, const char *name,
 	return -EINVAL;
 }

+static inline int gpio_group_export_link(struct device *dev, const char *name,
+    struct gpio_group *group)
+{
+	WARN_ON(1);
+	return -EINVAL;
+}

 static inline void gpio_unexport(unsigned gpio)
 {
@@ -106,6 +174,11 @@ static inline void gpio_unexport(unsigned gpio)
 	WARN_ON(1);
 }

+static inline void gpio_group_unexport(struct gpio_group *group)
+{
+	WARN_ON(1);
+}
+
 static inline int gpio_to_irq(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as input */
@@ -122,4 +195,14 @@ static inline int irq_to_gpio(unsigned irq)

 #endif

+static inline int gpio_direction_output_keep(int gpio)
+{
+	return gpio_direction_output(gpio, gpio_get_value_cansleep(gpio));
+}
+
+static inline int gpio_group_direction_output_keep(const struct
gpio_group *group)
+{
+	return gpio_group_direction_output(group, gpio_group_get_raw_cansleep(group));
+}
+
 #endif /* __LINUX_GPIO_H */
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
  2013-01-18 12:13     ` Stijn Devriendt
@ 2013-01-18 13:18       ` Roland Stigge
  -1 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-18 13:18 UTC (permalink / raw)
  To: Stijn Devriendt
  Cc: gregkh, grant.likely, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, broonie, daniel-gl,
	rmallon, sr, wg, tru, mark.rutland

Hi Stijn,

On 01/18/2013 01:13 PM, Stijn Devriendt wrote:
> Hi Roland,
> 
> This mail has been long overdue due to issues with some internal
> permission-tool.
> Just to be clear, this is not a competing implementation, it's what we
> currently use as-is. I'm just posting this as a reference to see if
> perhaps more concepts could be reused. It's based on a 2.6.32 kernel.
> 
> It includes:
> - labels in sysfs (to provide useful names to userspace)
> - gpio group support
> - exporting individual/groups of gpios dictated by platform-data or device-tree
> - open-drain support (different from mainline)
> - examplary support for multi-gpio to pcf8575 driver
> - gpio_direction_output_keep() function that prevents toggling when
> changing direction
> 
> Provided-as-is-by: Stijn Devriendt <sdevrien@cisco.com>

Thanks for posting.

Except for open-drain support, everything in the above list is supported
in the last posted version of block GPIO.

So please tell if there are certain details you would like to discuss
about or need an alternative implementation.

Thanks in advance,

Roland

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
@ 2013-01-18 13:18       ` Roland Stigge
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Stigge @ 2013-01-18 13:18 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Stijn,

On 01/18/2013 01:13 PM, Stijn Devriendt wrote:
> Hi Roland,
> 
> This mail has been long overdue due to issues with some internal
> permission-tool.
> Just to be clear, this is not a competing implementation, it's what we
> currently use as-is. I'm just posting this as a reference to see if
> perhaps more concepts could be reused. It's based on a 2.6.32 kernel.
> 
> It includes:
> - labels in sysfs (to provide useful names to userspace)
> - gpio group support
> - exporting individual/groups of gpios dictated by platform-data or device-tree
> - open-drain support (different from mainline)
> - examplary support for multi-gpio to pcf8575 driver
> - gpio_direction_output_keep() function that prevents toggling when
> changing direction
> 
> Provided-as-is-by: Stijn Devriendt <sdevrien@cisco.com>

Thanks for posting.

Except for open-drain support, everything in the above list is supported
in the last posted version of block GPIO.

So please tell if there are certain details you would like to discuss
about or need an alternative implementation.

Thanks in advance,

Roland

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
  2013-01-18 12:13     ` Stijn Devriendt
@ 2013-02-15 13:16       ` Grant Likely
  -1 siblings, 0 replies; 32+ messages in thread
From: Grant Likely @ 2013-02-15 13:16 UTC (permalink / raw)
  To: Stijn Devriendt, Roland Stigge
  Cc: gregkh, linus.walleij, linux-kernel, linux-arm-kernel, w.sang,
	jbe, plagnioj, broonie, daniel-gl, rmallon, sr, wg, tru,
	mark.rutland

On Fri, 18 Jan 2013 13:13:01 +0100, Stijn Devriendt <highguy@gmail.com> wrote:
> Hi Roland,
> 
> This mail has been long overdue due to issues with some internal
> permission-tool.
> Just to be clear, this is not a competing implementation, it's what we
> currently use as-is. I'm just posting this as a reference to see if
> perhaps more concepts could be reused. It's based on a 2.6.32 kernel.

Hi Stijn,

Thanks for posting this. As you say it isn't in a mergable state and
there are a lot if individual features lumped into a single patch where
some of them could be split out as seperate patches. Open-drain support
for instance is something that should be investigated.

However, the one thing I do want to raise (and I have this concern when
reading Roland's series too) is that I'm going to be very cautious about
extending the sysfs interface. Aside from the fact that the current
interface is buggy, it also potientially exposes far too much control
over hardware into userspace. I do see the advantages of a group
interface to a block of GPIO pins, but the details about how it is
accomplished are really key.

I'm in the process of reviewing Roland's patches now, and I'll respond
to his series with specifics.

g.

> 
> It includes:
> - labels in sysfs (to provide useful names to userspace)
> - gpio group support
> - exporting individual/groups of gpios dictated by platform-data or device-tree
> - open-drain support (different from mainline)
> - examplary support for multi-gpio to pcf8575 driver
> - gpio_direction_output_keep() function that prevents toggling when
> changing direction
> 
> Provided-as-is-by: Stijn Devriendt <sdevrien@cisco.com>
> ---
>  drivers/gpio/Kconfig        |    6 +
>  drivers/gpio/Makefile       |    1 +
>  drivers/gpio/gpio-export.c  |  327 ++++++++++++++
>  drivers/gpio/gpiolib.c      | 1021 ++++++++++++++++++++++++++++++++++++++++---
>  drivers/gpio/pcf857x.c      |  102 ++++-
>  include/asm-generic/gpio.h  |   67 +++
>  include/linux/gpio-export.h |   64 +++
>  include/linux/gpio.h        |   83 ++++
>  8 files changed, 1602 insertions(+), 69 deletions(-)
>  create mode 100644 drivers/gpio/gpio-export.c
>  create mode 100644 include/linux/gpio-export.h
> 
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 2ad0128..7daf6df 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -48,6 +48,12 @@ config DEBUG_GPIO
>  	  slower.  The diagnostics help catch the type of setup errors
>  	  that are most common when setting up new platforms or boards.
> 
> +config GPIO_EXPORT
> +	bool "GPIO export driver"
> +	depends on GPIO_SYSFS && GPIOLIB
> +	help
> +	  Say Y here to include the GPIO export driver.
> +
>  config GPIO_SYSFS
>  	bool "/sys/class/gpio/... (sysfs interface)"
>  	depends on SYSFS && EXPERIMENTAL
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 00a532c..40b96d7 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG
> 
>  obj-$(CONFIG_GPIOLIB)		+= gpiolib.o
> 
> +obj-$(CONFIG_GPIO_EXPORT)	+= gpio-export.o
>  obj-$(CONFIG_GPIO_ADP5520)	+= adp5520-gpio.o
>  obj-$(CONFIG_GPIO_LANGWELL)	+= langwell_gpio.o
>  obj-$(CONFIG_GPIO_MAX7301)	+= max7301.o
> diff --git a/drivers/gpio/gpio-export.c b/drivers/gpio/gpio-export.c
> new file mode 100644
> index 0000000..4ee4fe5
> --- /dev/null
> +++ b/drivers/gpio/gpio-export.c
> @@ -0,0 +1,327 @@
> +/* drivers/gpio/gpio-export.c
> + *
> + * Copyright (C) 2011 Stijn Devriendt, Cisco Systems Inc.
> + * Copyright (C) 2011 Eli Steenput, Cisco Systems Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/gpio.h>
> +#include <linux/device.h>
> +#include <linux/string.h>
> +#include <linux/gpio-export.h>
> +#include <linux/err.h>
> +
> +#ifdef CONFIG_OF
> +
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_gpio.h>
> +
> +#endif
> +
> +#include <linux/platform_device.h>
> +#include <linux/i2c.h>
> +
> +struct gpio_export_priv
> +{
> +	int count;
> +	int gpio_num;
> +	struct gpio_group *group;
> +	char desc[MAX_GPIO_LABEL_SIZE];
> +};
> +
> +static __devinit int common_gpio_probe(struct mp_gpio_platform_data
> *pdata, struct device *dev)
> +{
> +	int gpio_count = pdata->gpio_count;
> +	struct mp_gpio_line *gpio_line;
> +	int i;
> +	int err = 0;
> +	struct gpio_export_priv *priv;
> +
> +	if (gpio_count <= 0 || gpio_count > 32)
> +		return -ENODEV;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->count = gpio_count;
> +	strncpy(priv->desc, pdata->desc, MAX_GPIO_LABEL_SIZE);
> +	if (gpio_count > 1)
> +	{
> +		u32 gpio[32];
> +		u32 value = pdata->initialvalue;
> +		u32 opendrain = 0;
> +		for (i = 0; i < gpio_count; ++i)
> +		{
> +			gpio_line = &pdata->gpio_data[i];
> +			gpio[i] = gpio_line->gpio_num;
> +			if (gpio_line->active_low)
> +				value ^= (1 << i);
> +			if (gpio_line->open_drain)
> +				opendrain |= (1 << i);
> +		}
> +
> +		priv->group = gpio_group_request(gpio, gpio_count, priv->desc);
> +		if (IS_ERR(priv->group))
> +		{
> +			dev_err(dev, "Could not request gpio-group: %ld\n", PTR_ERR(priv->group));
> +			err = PTR_ERR(priv->group);
> +			goto out_mem;
> +		}
> +
> +		if (opendrain)
> +		{
> +			err = gpio_group_set_opendrain(priv->group,
> gpio_group_value_to_raw(priv->group, opendrain));
> +			if (err)
> +			{
> +				dev_err(dev, "Could not set gpio-group open-drain: %d\n", err);
> +				goto out_free_group;
> +			}
> +		}
> +
> +		switch (pdata->direction)
> +		{
> +			case GPIO_INPUT:
> +				err = gpio_group_direction_input(priv->group);
> +				break;
> +			case GPIO_OUTPUT:
> +				err = gpio_group_direction_output(priv->group,
> gpio_group_value_to_raw(priv->group, value));
> +				break;
> +			case GPIO_OUTPUT_KEEP:
> +				err = gpio_group_direction_output_keep(priv->group);
> +				break;
> +			default:
> +        break;
> +		}
> +
> +		if (err)
> +		{
> +			dev_err(dev, "Could not set gpio-group direction: %d\n", err);
> +			goto out_free_group;
> +		}
> +
> +		err = gpio_group_export(priv->group, pdata->direction == GPIO_CHANGE);
> +		if (err)
> +		{
> +			dev_err(dev, "Could not export gpio-group: %d\n", err);
> +			goto out_free_group;
> +		}
> +	}
> +	else
> +	{
> +		gpio_line = &pdata->gpio_data[0];
> +		err = gpio_request(gpio_line->gpio_num, priv->desc);
> +		if (err)
> +		{
> +			dev_err(dev, "Could not request gpio %d\n", gpio_line->gpio_num);
> +			goto out_free;
> +		}
> +		if (gpio_line->open_drain)
> +		{
> +			err = gpio_set_opendrain(gpio_line->gpio_num, 1);
> +			if (err)
> +			{
> +				dev_warn(dev, "Could not set open-drain on gpio %d\n",
> gpio_line->gpio_num);
> +				goto out_free;
> +			}
> +		}
> +
> +		if (pdata->direction == GPIO_INPUT)
> +		{
> +			err = gpio_direction_input(gpio_line->gpio_num);
> +		}
> +		else if (pdata->direction == GPIO_OUTPUT)
> +		{
> +			int value = 0;
> +			value = pdata->initialvalue;
> +			if (gpio_line->active_low)
> +				value = !value;
> +			dev_dbg(dev, "Setting output on gpio %d with value %d\n",
> gpio_line->gpio_num, value);
> +			err = gpio_direction_output(gpio_line->gpio_num, value);
> +		}
> +		else if (pdata->direction == GPIO_OUTPUT_KEEP)
> +		{
> +			err = gpio_direction_output_keep(gpio_line->gpio_num);
> +		}
> +
> +		if (err)
> +		{
> +			dev_err(dev, "Could not set direction: %d\n", err);
> +			goto out_free;
> +		}
> +
> +		err = gpio_export(gpio_line->gpio_num, pdata->direction == GPIO_CHANGE);
> +		if (err)
> +		{
> +			dev_warn(dev, "Could not export gpio %d\n", gpio_line->gpio_num);
> +			goto out_free;
> +		}
> +		priv->gpio_num = gpio_line->gpio_num;
> +	}
> +	dev_set_drvdata(dev, priv);
> +	dev_info(dev, "%s: Exported %d GPIO pins\n", pdata->desc, pdata->gpio_count);
> +out:
> +	return err;
> +out_free:
> +	gpio_free(gpio_line->gpio_num);
> +	kfree(priv);
> +	goto out;
> +out_free_group:
> +	gpio_group_free(priv->group);
> +	kfree(priv);
> +	goto out;
> +out_mem:
> +	kfree(priv);
> +	goto out;
> +}
> +
> +int common_gpio_remove(struct device *dev)
> +{
> +	struct gpio_export_priv *priv = dev_get_drvdata(dev);
> +	BUG_ON(!priv);
> +
> +	if (priv->count == 1)
> +		gpio_free(priv->gpio_num);
> +	else
> +		gpio_group_free(priv->group);
> +
> +	dev_set_drvdata(dev, NULL);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +
> +static __devinit int of_gpio_probe(struct platform_device *of_dev,
> +		const struct of_device_id *match)
> +{
> +	struct device_node *np = of_dev->dev.of_node;
> +	int gpio_count = of_gpio_count(np);
> +	const char* linuxname = of_get_property(np, "desc", NULL);
> +
> +	struct mp_gpio_platform_data pdata;
> +	struct mp_gpio_line gpio_line[32];
> +	int i;
> +
> +	pdata.gpio_data = gpio_line;
> +	pdata.gpio_count = gpio_count;
> +	strncpy(pdata.desc, linuxname, MAX_GPIO_LABEL_SIZE);
> +	pdata.initialvalue = 0;
> +
> +	if (of_device_is_compatible(np, "gpio-input"))
> +		pdata.direction = GPIO_INPUT;
> +	else if (of_device_is_compatible(np, "gpio-output"))
> +	{
> +		const __be32 *value = of_get_property_u32(np, "initial");
> +		if (!value)
> +			pdata.direction = GPIO_OUTPUT_KEEP;
> +		else
> +		{
> +			pdata.direction = GPIO_OUTPUT;
> +			pdata.initialvalue = be32_to_cpup(value);
> +			dev_dbg(&of_dev->dev, "initialvalue=%u, keep=%d\n",
> pdata.initialvalue, pdata.direction == GPIO_OUTPUT_KEEP);
> +		}
> +	}
> +	else
> +	{
> +		pdata.direction = GPIO_CHANGE;
> +	}
> +
> +	for (i=0; i < gpio_count; ++i)
> +	{
> +		u32 flags;
> +		gpio_line[i].gpio_num = of_get_gpio_flags(np, i, &flags);
> +		gpio_line[i].active_low = flags & OF_GPIO_ACTIVE_LOW;
> +		gpio_line[i].open_drain = flags & OF_GPIO_OPEN_DRAIN;
> +	}
> +	return common_gpio_probe(&pdata, &of_dev->dev);
> +}
> +
> +static __devexit int of_gpio_remove(struct platform_device *of_dev)
> +{
> +	return common_gpio_remove(&of_dev->dev);
> +}
> +
> +static const struct of_device_id __devinitconst of_gpio_match[] = {
> +	{ .compatible = "gpio-input", },
> +	{ .compatible = "gpio-output", },
> +	{ .compatible = "gpio-user", },
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(of, of_gpio_match);
> +
> +static struct of_platform_driver of_gpio_driver = {
> +	.probe = of_gpio_probe,
> +	.remove = __devexit_p(of_gpio_remove),
> +	.driver = {
> +		.owner = THIS_MODULE,
> +		.name = "of-gpio",
> +		.of_match_table = of_gpio_match,
> +	},
> +};
> +
> +static int __init of_gpio_init(void)
> +{
> +	// Use i2c_bus_type to support I/O expanders?
> +	return of_register_platform_driver(&of_gpio_driver);
> +}
> +
> +static void __exit of_gpio_exit(void)
> +{
> +	of_unregister_driver(&of_gpio_driver);
> +}
> +
> +late_initcall(of_gpio_init);
> +module_exit(of_gpio_exit);
> +
> +#else
> +
> +static __devinit int mp_probe(struct platform_device *p_device)
> +{
> +	return common_gpio_probe(dev_get_platdata(&p_device->dev), &p_device->dev);
> +}
> +
> +static __devexit int mp_remove(struct platform_device *p_device)
> +{
> +	return common_gpio_remove(&p_device->dev);
> +}
> +
> +static struct platform_device_id mp_id_table[] = {
> +	{
> +		.name		= "gpio-export",
> +	},
> +};
> +
> +static struct platform_driver mp_gpio_driver = {
> +	.probe = mp_probe,
> +	.remove = mp_remove,
> +	.id_table = mp_id_table,
> +	.driver.name = "gpio-export",
> +	.driver.bus = &platform_bus_type,
> +	.driver.owner = THIS_MODULE,
> +};
> +
> +static int __init mp_gpio_init(void)
> +{
> +	return platform_driver_register(&mp_gpio_driver);
> +}
> +
> +static void __exit mp_gpio_exit(void)
> +{
> +	platform_driver_unregister(&mp_gpio_driver);
> +}
> +
> +late_initcall(mp_gpio_init);
> +module_exit(mp_gpio_exit);
> +
> +#endif
> +
> +MODULE_AUTHOR("Stijn Devriendt, Eli Steenput");
> +MODULE_DESCRIPTION("Multi Purpose GPIO export driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index 50de0f5..1c9c426 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -8,7 +8,9 @@
>  #include <linux/debugfs.h>
>  #include <linux/seq_file.h>
>  #include <linux/gpio.h>
> +#include <linux/of_gpio.h>
>  #include <linux/idr.h>
> +#include <linux/ctype.h>
> 
> 
>  /* Optional implementation infrastructure for GPIO interfaces.
> @@ -53,15 +55,15 @@ struct gpio_desc {
>  #define FLAG_SYSFS	4	/* exported via /sys/class/gpio/control */
>  #define FLAG_TRIG_FALL	5	/* trigger on falling edge */
>  #define FLAG_TRIG_RISE	6	/* trigger on rising edge */
> +#define FLAG_OPEN_DRAIN 7       /* gpio is open drain */
> 
>  #define PDESC_ID_SHIFT	16	/* add new flags before this one */
> 
>  #define GPIO_FLAGS_MASK		((1 << PDESC_ID_SHIFT) - 1)
>  #define GPIO_TRIGGER_MASK	(BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
> 
> -#ifdef CONFIG_DEBUG_FS
>  	const char		*label;
> -#endif
> +	struct gpio_group *group;
>  };
>  static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
> 
> @@ -76,9 +78,7 @@ static struct idr pdesc_idr;
> 
>  static inline void desc_set_label(struct gpio_desc *d, const char *label)
>  {
> -#ifdef CONFIG_DEBUG_FS
>  	d->label = label;
> -#endif
>  }
> 
>  /* Warn when drivers omit gpio_request() calls -- legal but ill-advised
> @@ -119,6 +119,11 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
>  	return gpio_desc[gpio].chip;
>  }
> 
> +static inline struct gpio_group *gpio_to_group(unsigned gpio)
> +{
> +	return gpio_desc[gpio].group;
> +}
> +
>  /* dynamic allocation of GPIOs, e.g. on a hotplugged device */
>  static int gpiochip_find_base(int ngpio)
>  {
> @@ -189,6 +194,110 @@ err:
>  	return ret;
>  }
> 
> +/* gpio_group_raw_to_value() - reorder bits according to the gpio group
> + * request
> + *
> + * @group: gpio_group
> + * @raw: raw value
> + *
> + * Returns a compact value representing the gpio_group value.
> + * e.g. consider gpio pins [1,0,2,3] have been requested and their
> + * value is [1,0,0,1] respectively. The raw readout will be 0xA [1,0,1,0]
> + * while the return value of this function will be 0x9, considering
> + * the order of the GPIOs in the group.
> + */
> +u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw)
> +{
> +	int i = 0;
> +	u32 ret = 0;
> +	unsigned base = gpio_to_chip(group->gpios[0])->base;
> +
> +	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
> +	{
> +		unsigned offset = group->gpios[i] - base;
> +		u32 rawbit = (1 << offset);
> +
> +		// if raw[offset] is set
> +		// then set ret[i]
> +		if (raw & rawbit)
> +			ret |= (1 << i);
> +
> +		++i;
> +	}
> +	return ret;
> +}
> +
> +/* gpio_group_value_to_raw() - Inverse of gpio_group_raw_to_value
> + *
> + * @group: gpio_group
> + * @raw: compact value
> + *
> + * Returns the raw value.
> + */
> +u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value)
> +{
> +	int i = 0;
> +	u32 raw = 0;
> +	unsigned base = gpio_to_chip(group->gpios[0])->base;
> +
> +	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
> +	{
> +		unsigned offset = group->gpios[i] - base;
> +		u32 rawbit = (1 << offset);
> +
> +		// if value[i] is set
> +		// then set ret[offset]
> +		if (value & (1 << i))
> +			raw |= rawbit;
> +
> +		++i;
> +	}
> +	return raw;
> +}
> +
> +int gpio_group_test_bit(unsigned long flag, const struct gpio_group *group)
> +{
> +	u32 set = 0;
> +	int i;
> +	unsigned base = gpio_to_chip(group->gpios[0])->base;
> +	for (i = 0; i < 32; ++i)
> +	{
> +		if (group->mask & (1 << i) && test_bit(flag, &gpio_desc[base + i].flags))
> +			set |= (1 << i);
> +	}
> +	if (set == 0)
> +		return 0;
> +	else if (set == group->mask)
> +		return 1;
> +	else
> +		return -EIO;
> +}
> +
> +void gpio_group_set_bit(unsigned long flag, const struct gpio_group *group)
> +{
> +	int i;
> +	unsigned base = gpio_to_chip(group->gpios[0])->base;
> +
> +	for (i = 0; i < 32; ++i)
> +	{
> +		if (group->mask & (1 << i))
> +			set_bit(flag, &gpio_desc[base + i].flags);
> +	}
> +}
> +
> +void gpio_group_clear_bit(unsigned long flag, const struct gpio_group *group)
> +{
> +	int i;
> +	unsigned base = gpio_to_chip(group->gpios[0])->base;
> +
> +	for (i = 0; i < 32; ++i)
> +	{
> +		if (group->mask & (1 << i))
> +			clear_bit(flag, &gpio_desc[base + i].flags);
> +	}
> +}
> +
> +
>  #ifdef CONFIG_GPIO_SYSFS
> 
>  /* lock protects against unexport_gpio() being called while
> @@ -231,6 +340,30 @@ static ssize_t gpio_direction_show(struct device *dev,
>  	return status;
>  }
> 
> +static ssize_t gpio_group_direction_show(struct device *dev,
> +    struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status = 0;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +
> +	status = gpio_group_test_bit(FLAG_IS_OUT, group);
> +	if (status >= 0)
> +		status = sprintf(buf, "%s\n", status == 0 ? "in" : "out");
> +
> +out:
> +	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)
>  {
> @@ -255,9 +388,49 @@ static ssize_t gpio_direction_store(struct device *dev,
>  	return status ? : size;
>  }
> 
> -static const DEVICE_ATTR(direction, 0644,
> +static ssize_t gpio_group_direction_store(struct device *dev,
> +    struct device_attribute *attr, const char* buf, size_t size)
> +{
> +  const struct gpio_group *group = dev_get_drvdata(dev);
> +  ssize_t status;
> +  unsigned long input;
> +
> +  mutex_lock(&sysfs_lock);
> +
> +  if (gpio_desc[group->gpios[0]].group != group
> +   || gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
> +  {
> +    status = -EIO;
> +    goto out;
> +  }
> +
> +  if (sysfs_streq(buf, "in"))
> +    status = gpio_group_direction_input(group);
> +  else if (sysfs_streq(buf, "out"))
> +    status = gpio_group_direction_output(group, 0);
> +  else
> +  {
> +    status = strict_strtoul(buf, 0, &input);
> +    if (!status)
> +    {
> +      if ((input & group->mask) == input)
> +        status = gpio_group_direction_output(group, input);
> +      else
> +        status = -EINVAL;
> +    }
> +  }
> +
> +out:
> +  mutex_unlock(&sysfs_lock);
> +  return status ? : size;
> +}
> +
> +static DEVICE_ATTR(direction, 0644,
>  		gpio_direction_show, gpio_direction_store);
> 
> +struct device_attribute dev_attr_direction_group =
> +	__ATTR(direction, 0644, gpio_group_direction_show,
> gpio_group_direction_store);
> +
>  static ssize_t gpio_value_show(struct device *dev,
>  		struct device_attribute *attr, char *buf)
>  {
> @@ -276,6 +449,50 @@ static ssize_t gpio_value_show(struct device *dev,
>  	return status;
>  }
> 
> +static ssize_t gpio_group_value_show(struct device *dev,
> +    struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +	status = sprintf(buf, "%u\n", gpio_group_raw_to_value(group,
> +				gpio_group_get_raw_cansleep(group)));
> +
> +out:
> +	mutex_unlock(&sysfs_lock);
> +	return status;
> +}
> +
> +
> +static ssize_t gpio_group_raw_show(struct device *dev,
> +    struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +	status = sprintf(buf, "%u\n", gpio_group_get_raw_cansleep(group));
> +
> +out:
> +	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)
>  {
> @@ -303,9 +520,96 @@ static ssize_t gpio_value_store(struct device *dev,
>  	return status;
>  }
> 
> +static ssize_t gpio_group_value_store(struct device *dev,
> +    struct device_attribute *attr, const char* buf, size_t size)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status;
> +	unsigned long value;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
> +			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +	status = strict_strtoul(buf, 0, &value);
> +	if (status == 0)
> +	{
> +		gpio_group_set_raw_cansleep(group,
> +				gpio_group_value_to_raw(group, value));
> +		status = size;
> +	}
> +
> +out:
> +	mutex_unlock(&sysfs_lock);
> +
> +	return status;
> +}
> +
> +static ssize_t gpio_group_raw_store(struct device *dev,
> +    struct device_attribute *attr, const char* buf, size_t size)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status;
> +	unsigned long value;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
> +			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +	status = strict_strtoul(buf, 0, &value);
> +	if (status == 0)
> +	{
> +		gpio_group_set_raw_cansleep(group, value);
> +		status = size;
> +	}
> +
> +out:
> +	mutex_unlock(&sysfs_lock);
> +
> +	return status;
> +}
> +
> +static ssize_t gpio_group_mask_show(struct device *dev,
> +    struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +	status = sprintf(buf, "%u\n", group->mask);
> +
> +out:
> +	mutex_unlock(&sysfs_lock);
> +	return status;
> +}
> +
>  static /*const*/ DEVICE_ATTR(value, 0644,
>  		gpio_value_show, gpio_value_store);
> 
> +struct device_attribute dev_attr_value_group =
> +	__ATTR(value, 0644, gpio_group_value_show, gpio_group_value_store);
> +
> +DEVICE_ATTR(raw, 0644, gpio_group_raw_show, gpio_group_raw_store);
> +
> +DEVICE_ATTR(mask, 0444, gpio_group_mask_show, NULL);
> +
>  static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
>  {
>  	struct work_struct	*work = priv;
> @@ -381,7 +685,7 @@ static int gpio_setup_irq(struct gpio_desc *desc,
> struct device *dev,
>  			goto free_id;
>  		}
> 
> -		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value");
> +		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
>  		if (!pdesc->value_sd) {
>  			ret = -ENODEV;
>  			goto free_id;
> @@ -475,9 +779,55 @@ found:
> 
>  static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);
> 
> +static ssize_t gpio_label_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_desc	*desc = dev_get_drvdata(dev);
> +	ssize_t			status = 0;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (!test_bit(FLAG_EXPORT, &desc->flags))
> +		status = -EIO;
> +	else if (desc->label)
> +		status = sprintf(buf, "%s\n", desc->label);
> +
> +	mutex_unlock(&sysfs_lock);
> +	return status;
> +}
> +
> +static ssize_t gpio_group_label_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	const struct gpio_desc	*desc = &gpio_desc[group->gpios[0]];
> +	ssize_t			status = 0;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (!test_bit(FLAG_EXPORT, &desc->flags))
> +		status = -EIO;
> +	else if (desc->label)
> +		status = sprintf(buf, "%s\n", desc->label);
> +
> +	mutex_unlock(&sysfs_lock);
> +	return status;
> +}
> +
> +static struct device_attribute dev_attr_gpio_label = __ATTR(label,
> 0444, gpio_label_show, NULL);
> +static struct device_attribute dev_attr_gpio_group_label =
> __ATTR(label, 0444, gpio_group_label_show, NULL);
> +
>  static const struct attribute *gpio_attrs[] = {
> -	&dev_attr_direction.attr,
>  	&dev_attr_value.attr,
> +	&dev_attr_gpio_label.attr,
> +	NULL,
> +};
> +
> +static const struct attribute *gpio_group_attrs[] = {
> +	&dev_attr_value_group.attr,
> +	&dev_attr_raw.attr,
> +	&dev_attr_mask.attr,
> +	&dev_attr_gpio_group_label.attr,
>  	NULL,
>  };
> 
> @@ -485,6 +835,10 @@ static const struct attribute_group gpio_attr_group = {
>  	.attrs = (struct attribute **) gpio_attrs,
>  };
> 
> +static const struct attribute_group gpio_group_attr_group = {
> +	.attrs = (struct attribute **) gpio_group_attrs,
> +};
> +
>  /*
>   * /sys/class/gpio/gpiochipN/
>   *   /base ... matching gpio_chip.base (N)
> @@ -530,6 +884,36 @@ static const struct attribute_group gpiochip_attr_group = {
>  	.attrs = (struct attribute **) gpiochip_attrs,
>  };
> 
> +int read_gpios(const char *buf, size_t len, unsigned *gpios)
> +{
> +	const char *startp = buf;
> +	char *endp = (char*)buf;
> +	int i;
> +	for (i = 0; i < 32; ++i)
> +	{
> +		unsigned gpio;
> +
> +		gpio = simple_strtoul(startp, &endp, 0);
> +
> +		if (endp == startp)
> +			return -EINVAL; // not a number, bail out
> +
> +		gpios[i] = gpio;
> +
> +		while (endp - buf < len && isspace(*endp)) // eat whitespace
> +			endp++;
> +
> +
> +		if (endp - buf > len) // buffer overrun, should never happen
> +			return -EINVAL;
> +		else if (endp - buf == len) // end of buffer, return number of read gpios
> +			return i+1;
> +
> +		startp = endp;
> +	}
> +	return -EINVAL;
> +}
> +
>  /*
>   * /sys/class/gpio/export ... write-only
>   *	integer N ... number of GPIO to export (full access)
> @@ -538,10 +922,10 @@ static const struct attribute_group
> gpiochip_attr_group = {
>   */
>  static ssize_t export_store(struct class *class, const char *buf, size_t len)
>  {
> -	long	gpio;
> +	unsigned	gpio[32];
>  	int	status;
> 
> -	status = strict_strtol(buf, 0, &gpio);
> +	status = read_gpios(buf, len, gpio);
>  	if (status < 0)
>  		goto done;
> 
> @@ -550,16 +934,41 @@ static ssize_t export_store(struct class *class,
> const char *buf, size_t len)
>  	 * they may be undone on its behalf too.
>  	 */
> 
> -	status = gpio_request(gpio, "sysfs");
> -	if (status < 0)
> -		goto done;
> +	if (status == 1)
> +	{
> +		status = gpio_request(gpio[0], "sysfs");
> +		if (status < 0)
> +			goto done;
> 
> -	status = gpio_export(gpio, true);
> -	if (status < 0)
> -		gpio_free(gpio);
> +		status = gpio_export(gpio[0], true);
> +		if (status < 0)
> +			gpio_free(gpio[0]);
> +		else
> +			set_bit(FLAG_SYSFS, &gpio_desc[gpio[0]].flags);
> +	}
>  	else
> -		set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags);
> +	{
> +		struct gpio_group *group = gpio_group_request(gpio, status, "sysfs");
> +		if (IS_ERR(group))
> +		{
> +			status = PTR_ERR(group);
> +			goto done;
> +		}
> 
> +		status = gpio_group_export(group, true);
> +		if (status < 0)
> +			gpio_group_free(group);
> +		else
> +    {
> +      // Lock required to protect against unexport being called
> +      // against when only parts of the group have the flag set.
> +      // The other cases: all have the flag or none have the flag
> +      // are handled correctly.
> +      mutex_lock(&sysfs_lock);
> +			gpio_group_set_bit(FLAG_SYSFS, group);
> +      mutex_unlock(&sysfs_lock);
> +    }
> +	}
>  done:
>  	if (status)
>  		pr_debug("%s: status %d\n", __func__, status);
> @@ -570,6 +979,7 @@ static ssize_t unexport_store(struct class *class,
> const char *buf, size_t len)
>  {
>  	long	gpio;
>  	int	status;
> +	struct gpio_group *group;
> 
>  	status = strict_strtol(buf, 0, &gpio);
>  	if (status < 0)
> @@ -581,13 +991,29 @@ static ssize_t unexport_store(struct class
> *class, const char *buf, size_t len)
>  	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);
> +	group = gpio_to_group(gpio);
> +	if (group)
> +	{
> +		mutex_lock(&sysfs_lock);
> +		if (gpio_group_test_bit(FLAG_SYSFS, group))
> +		{
> +			status = 0;
> +			gpio_group_clear_bit(FLAG_SYSFS, group);
> +		}
> +		mutex_unlock(&sysfs_lock);
> +		if (!status)
> +			gpio_group_free(group);
> +	}
> +	else
> +	{
> +		/* 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)
> @@ -662,12 +1088,11 @@ int gpio_export(unsigned gpio, bool direction_may_change)
>  		dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
>  				desc, ioname ? ioname : "gpio%d", gpio);
>  		if (!IS_ERR(dev)) {
> -			if (direction_may_change)
> -				status = sysfs_create_group(&dev->kobj,
> -						&gpio_attr_group);
> -			else
> -				status = device_create_file(dev,
> -						&dev_attr_value);
> +  		status = sysfs_create_group(&dev->kobj,
> +  						&gpio_attr_group);
> +
> +			if (!status && direction_may_change)
> +				status = device_create_file(dev, &dev_attr_direction);
> 
>  			if (!status && gpio_to_irq(gpio) >= 0
>  					&& (direction_may_change
> @@ -694,6 +1119,72 @@ done:
>  }
>  EXPORT_SYMBOL_GPL(gpio_export);
> 
> +int gpio_group_export(struct gpio_group *group, bool direction_may_change)
> +{
> +	unsigned long flags;
> +	int status = -EINVAL;
> +	struct gpio_chip *chip;
> +	unsigned lowest = group->gpios[0];
> +	int i;
> +
> +	/* can't export until sysfs is available ... */
> +	if (!gpio_class.p) {
> +		pr_debug("%s: called too early!\n", __func__);
> +		return -ENOENT;
> +	}
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	spin_lock_irqsave(&gpio_lock, flags);
> +	chip = gpio_to_chip(group->gpios[0]);
> +	if (gpio_group_test_bit(FLAG_REQUESTED, group) > 0
> +			&& gpio_group_test_bit(FLAG_EXPORT, group) == 0)
> +	{
> +		status = 0;
> +		if (!chip->direction_input_multi || !chip->direction_output_multi)
> +		{
> +			direction_may_change = false;
> +		}
> +	}
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +
> +	if (status == 0)
> +	{
> +		struct device *dev;
> +
> +		i=1;
> +		while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
> +		{
> +			if (group->gpios[i] < lowest)
> +				lowest = group->gpios[i];
> +			++i;
> +		}
> +
> +		dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0),
> +				group, "group%d", lowest);
> +		if (!IS_ERR(dev)) {
> +			status = sysfs_create_group(&dev->kobj, &gpio_group_attr_group);
> +
> +			if (status == 0 && direction_may_change)
> +				status = device_create_file(dev, &dev_attr_direction_group);
> +
> +			if (status != 0)
> +				device_unregister(dev);
> +		}
> +		else
> +			status = PTR_ERR(dev);
> +		if (status == 0)
> +			gpio_group_set_bit(FLAG_EXPORT, group);
> +	}
> +	mutex_unlock(&sysfs_lock);
> +
> +	if (status)
> +		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
> +	return status;
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_export);
> +
> +
>  static int match_export(struct device *dev, void *data)
>  {
>  	return dev_get_drvdata(dev) == data;
> @@ -744,6 +1235,30 @@ done:
>  }
>  EXPORT_SYMBOL_GPL(gpio_export_link);
> 
> +int gpio_group_export_link(struct device *dev, const char *name,
> struct gpio_group *group)
> +{
> +	int status = -EINVAL;
> +	mutex_lock(&sysfs_lock);
> +	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
> +	{
> +		struct device *tdev;
> +
> +		tdev = class_find_device(&gpio_class, NULL, group, match_export);
> +		if (tdev != NULL) {
> +			status = sysfs_create_link(&dev->kobj, &tdev->kobj, name);
> +		}
> +		else
> +		{
> +			status = -ENODEV;
> +		}
> +	}
> +	mutex_unlock(&sysfs_lock);
> +	if (status)
> +		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
> +	return status;
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_export_link);
> +
>  /**
>   * gpio_unexport - reverse effect of gpio_export()
>   * @gpio: gpio to make unavailable
> @@ -783,6 +1298,31 @@ done:
>  }
>  EXPORT_SYMBOL_GPL(gpio_unexport);
> 
> +void gpio_group_unexport(struct gpio_group *group)
> +{
> +	int status = -EINVAL;
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
> +	{
> +		struct device *dev = NULL;
> +		dev = class_find_device(&gpio_class, NULL, group, match_export);
> +		if (dev)
> +		{
> +			gpio_group_clear_bit(FLAG_EXPORT, group);
> +			put_device(dev);
> +			device_unregister(dev);
> +			status = 0;
> +		}
> +		else
> +			status = -ENODEV;
> +	}
> +	mutex_unlock(&sysfs_lock);
> +	if (status)
> +		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_unexport);
> +
>  static int gpiochip_export(struct gpio_chip *chip)
>  {
>  	int		status;
> @@ -959,6 +1499,8 @@ int gpiochip_add(struct gpio_chip *chip)
>  		}
>  	}
> 
> +	of_gpiochip_add(chip);
> +
>  unlock:
>  	spin_unlock_irqrestore(&gpio_lock, flags);
>  	if (status == 0)
> @@ -987,6 +1529,8 @@ int gpiochip_remove(struct gpio_chip *chip)
> 
>  	spin_lock_irqsave(&gpio_lock, flags);
> 
> +	of_gpiochip_remove(chip);
> +
>  	for (id = chip->base; id < chip->base + chip->ngpio; id++) {
>  		if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) {
>  			status = -EBUSY;
> @@ -1007,6 +1551,38 @@ int gpiochip_remove(struct gpio_chip *chip)
>  }
>  EXPORT_SYMBOL_GPL(gpiochip_remove);
> 
> +/**
> + * gpiochip_find() - iterator for locating a specific gpio_chip
> + * @data: data to pass to match function
> + * @callback: Callback function to check gpio_chip
> + *
> + * Similar to bus_find_device.  It returns a reference to a gpio_chip as
> + * determined by a user supplied @match callback.  The callback should return
> + * 0 if the device doesn't match and non-zero if it does.  If the callback is
> + * non-zero, this function will return to the caller and not iterate over any
> + * more gpio_chips.
> + */
> +struct gpio_chip *gpiochip_find(void *data,
> +				int (*match)(struct gpio_chip *chip, void *data))
> +{
> +	struct gpio_chip *chip = NULL;
> +	unsigned long flags;
> +	int i;
> +
> +	spin_lock_irqsave(&gpio_lock, flags);
> +	for (i = 0; i < ARCH_NR_GPIOS; i++) {
> +		if (!gpio_desc[i].chip)
> +			continue;
> +
> +		if (match(gpio_desc[i].chip, data)) {
> +			chip = gpio_desc[i].chip;
> +			break;
> +		}
> +	}
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +
> +	return chip;
> +}
> 
>  /* These "optional" allocation calls help prevent drivers from stomping
>   * on each other, and help provide better diagnostics in debugfs.
> @@ -1066,6 +1642,111 @@ done:
>  }
>  EXPORT_SYMBOL_GPL(gpio_request);
> 
> +struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
> const char* label)
> +{
> +	int i;
> +	unsigned long flags;
> +	u32 mask = 0;
> +	struct gpio_chip *chip;
> +	int rc = 0;
> +	struct gpio_group *group;
> +
> +	if (ngpios <= 0)
> +		return ERR_PTR(-EINVAL);
> +
> +	group = kzalloc(sizeof(struct gpio_group), GFP_KERNEL);
> +	if (!group)
> +		return ERR_PTR(-ENOMEM);
> +
> +	spin_lock_irqsave(&gpio_lock, flags);
> +	if (!gpio_is_valid(gpio[0]))
> +	{
> +		rc = -EINVAL;
> +		goto out_free;
> +	}
> +	chip = gpio_to_chip(gpio[0]);
> +	// if not (multi-input or multi-output capable)
> +	if (!chip ||
> +			!(   (chip->get_multi && chip->direction_input_multi)
> +				|| (chip->set_multi && chip->direction_output_multi))
> +	   )
> +	{
> +		rc = -ENODEV;
> +		goto out_free;
> +	}
> +
> +	mask |= (1 << (gpio[0] - chip->base));
> +	for (i = 1; i < ngpios; ++i)
> +	{
> +		if (gpio[i] < chip->base || gpio[i] > chip->base + chip->ngpio)
> +		{
> +			rc = -EINVAL;
> +			goto out_free;
> +		}
> +		mask |= (1 << (gpio[i] - chip->base));
> +	}
> +
> +	if (!try_module_get(chip->owner))
> +	{
> +		rc = -ENOSYS;
> +		goto out_free;
> +	}
> +
> +	group->mask = mask;
> +	for (i = 0; i < 32; ++i)
> +	{
> +		if (i < ngpios)
> +			group->gpios[i] = gpio[i];
> +		else
> +			group->gpios[i] = ARCH_NR_GPIOS;
> +	}
> +
> +	if (gpio_group_test_bit(FLAG_REQUESTED, group) != 0)
> +	{
> +		rc = -EBUSY;
> +		goto out_put;
> +	}
> +
> +	gpio_group_set_bit(FLAG_REQUESTED, group);
> +	for (i = 0; i < ngpios; ++i)
> +	{
> +		gpio_desc[gpio[i]].group = group;
> +		desc_set_label(&gpio_desc[gpio[i]], label ? : "?");
> +	}
> +
> +	if (chip->request_multi) {
> +		spin_unlock_irqrestore(&gpio_lock, flags);
> +		rc = chip->request_multi(chip, mask);
> +		spin_lock_irqsave(&gpio_lock, flags);
> +		if (rc)
> +			goto out_label;
> +	}
> +
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +	return group;
> +out_label:
> +	for (i = 0; i < ngpios; ++i)
> +	{
> +		desc_set_label(&gpio_desc[gpio[i]], NULL);
> +		gpio_desc[gpio[i]].group = NULL;
> +	}
> +out_put:
> +	module_put(chip->owner);
> +out_free:
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +	kfree(group);
> +	pr_debug("%s: group%d (%s) status %d\n", __func__, *gpio, label ? : "?", rc);
> +	return ERR_PTR(rc);
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_request);
> +
> +struct gpio_group * gpio_get_group(unsigned gpio)
> +{
> +	if (!gpio_is_valid(gpio))
> +		return NULL;
> +	return gpio_desc[gpio].group;
> +}
> +
>  void gpio_free(unsigned gpio)
>  {
>  	unsigned long		flags;
> @@ -1102,6 +1783,45 @@ void gpio_free(unsigned gpio)
>  }
>  EXPORT_SYMBOL_GPL(gpio_free);
> 
> +void gpio_group_free(struct gpio_group *group)
> +{
> +	unsigned long flags;
> +	struct gpio_chip *chip;
> +
> +	might_sleep();
> +
> +	gpio_group_unexport(group);
> +
> +	spin_lock_irqsave(&gpio_lock, flags);
> +
> +	chip = gpio_to_chip(group->gpios[0]);
> +	if (chip && gpio_group_test_bit(FLAG_REQUESTED, group))
> +	{
> +		int i;
> +		if (chip->free_multi) {
> +			spin_unlock_irqrestore(&gpio_lock, flags);
> +			might_sleep_if(extra_checks && chip->can_sleep);
> +			chip->free_multi(chip, group->mask);
> +			spin_lock_irqsave(&gpio_lock, flags);
> +		}
> +		for (i = 0; i < 32; ++i)
> +		{
> +			if (group->mask & (1 << i))
> +			{
> +				desc_set_label(&gpio_desc[chip->base + i], NULL);
> +				gpio_desc[chip->base +i].group = NULL;
> +			}
> +			module_put(chip->owner);
> +			gpio_group_clear_bit(FLAG_REQUESTED, group);
> +		}
> +	}
> +	else
> +		WARN_ON(extra_checks);
> +
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +	kfree(group);
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_free);
> 
>  /**
>   * gpiochip_is_requested - return string iff signal was requested
> @@ -1124,15 +1844,10 @@ const char *gpiochip_is_requested(struct
> gpio_chip *chip, unsigned offset)
>  		return NULL;
>  	if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0)
>  		return NULL;
> -#ifdef CONFIG_DEBUG_FS
>  	return gpio_desc[gpio].label;
> -#else
> -	return "?";
> -#endif
>  }
>  EXPORT_SYMBOL_GPL(gpiochip_is_requested);
> 
> -
>  /* Drivers MUST set GPIO direction before making get/set calls.  In
>   * some cases this is done in early boot, before IRQs are enabled.
>   *
> @@ -1195,6 +1910,25 @@ fail:
>  }
>  EXPORT_SYMBOL_GPL(gpio_direction_input);
> 
> +
> +int gpio_group_direction_input(const struct gpio_group *group)
> +{
> +	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
> +	int rc;
> +
> +	if (!chip->get_multi || !chip->direction_input_multi)
> +		return -EINVAL;
> +
> +	rc = chip->direction_input_multi(chip, group->mask);
> +	if (rc == 0)
> +	{
> +		gpio_group_clear_bit(FLAG_IS_OUT, group);
> +	}
> +	else
> +		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
> +	return rc;
> +}
> +
>  int gpio_direction_output(unsigned gpio, int value)
>  {
>  	unsigned long		flags;
> @@ -1248,6 +1982,124 @@ fail:
>  }
>  EXPORT_SYMBOL_GPL(gpio_direction_output);
> 
> +int gpio_group_direction_output(const struct gpio_group *group, u32 value)
> +{
> +	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
> +	int rc;
> +
> +	if (!chip->set_multi || !chip->direction_output_multi)
> +		return -EINVAL;
> +
> +	rc = chip->direction_output_multi(chip, group->mask, value & group->mask);
> +	if (rc == 0)
> +	{
> +		gpio_group_set_bit(FLAG_IS_OUT, group);
> +	}
> +	else
> +		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
> +	return rc;
> +}
> +
> +int gpio_set_opendrain(unsigned gpio, int value)
> +{
> +	unsigned long		flags;
> +	struct gpio_chip	*chip;
> +	struct gpio_desc	*desc = &gpio_desc[gpio];
> +	int			status = -EINVAL;
> +
> +	spin_lock_irqsave(&gpio_lock, flags);
> +
> +	if (!gpio_is_valid(gpio))
> +		goto fail;
> +	chip = desc->chip;
> +	if (!chip || !chip->set || !chip->direction_output)
> +		goto fail;
> +	gpio -= chip->base;
> +	if (gpio >= chip->ngpio)
> +		goto fail;
> +	status = gpio_ensure_requested(desc, gpio);
> +	if (status < 0)
> +		goto fail;
> +
> +	/* now we know the gpio is valid and chip won't vanish */
> +
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +
> +	might_sleep_if(extra_checks && chip->can_sleep);
> +
> +	if (!chip->set_opendrain)
> +		return -ENOSYS;
> +
> +	if (status) {
> +		status = chip->request(chip, gpio);
> +		if (status < 0) {
> +			pr_debug("GPIO-%d: chip request fail, %d\n",
> +				chip->base + gpio, status);
> +			/* and it's not available to anyone else ...
> +			 * gpio_request() is the fully clean solution.
> +			 */
> +			goto lose;
> +		}
> +	}
> +
> +	status = chip->set_opendrain(chip, gpio, value);
> +	if (status == 0)
> +	{
> +		if (value)
> +			set_bit(FLAG_OPEN_DRAIN, &desc->flags);
> +		else
> +			clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
> +	}
> +lose:
> +	return status;
> +fail:
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +	if (status)
> +		pr_debug("%s: gpio-%d status %d\n",
> +			__func__, gpio, status);
> +	return status;
> +}
> +EXPORT_SYMBOL_GPL(gpio_set_opendrain);
> +
> +int gpio_group_set_opendrain(struct gpio_group *group, u32 value)
> +{
> +	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
> +	int err;
> +	if (!chip)
> +		return -EINVAL;
> +	if (!chip->set_multi || !chip->direction_output_multi)
> +		return -EINVAL;
> +	if (!chip->set_opendrain_multi)
> +		return -ENOSYS;
> +
> +	might_sleep_if(extra_checks && chip->can_sleep);
> +
> +	err = chip->set_opendrain_multi(chip, group->mask, value & group->mask);
> +	if (err == 0)
> +	{
> +		int i;
> +		unsigned base = chip->base;
> +		for (i = 0; i < 32; ++i)
> +		{
> +			if (group->mask & (1 << i))
> +			{
> +				if (value & (1 << i))
> +				{
> +					set_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
> +				}
> +				else
> +				{
> +					clear_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
> +				}
> +			}
> +		}
> +	}
> +	if (err)
> +		pr_debug("%s: group%d status %d\n",
> +				__func__, group->gpios[0], err);
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_set_opendrain);
> 
>  /* I/O calls are only valid after configuration completed; the relevant
>   * "is this a valid GPIO" error checks should already have been done.
> @@ -1290,6 +2142,14 @@ int __gpio_get_value(unsigned gpio)
>  }
>  EXPORT_SYMBOL_GPL(__gpio_get_value);
> 
> +u32 gpio_group_get_raw(const struct gpio_group* group)
> +{
> +	struct gpio_chip *chip;
> +	chip = gpio_to_chip(group->gpios[0]);
> +	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_get_raw);
> +
>  /**
>   * __gpio_set_value() - assign a gpio's value
>   * @gpio: gpio whose value will be assigned
> @@ -1309,6 +2169,13 @@ void __gpio_set_value(unsigned gpio, int value)
>  }
>  EXPORT_SYMBOL_GPL(__gpio_set_value);
> 
> +void gpio_group_set_raw(const struct gpio_group *group, u32 value)
> +{
> +	struct gpio_chip *chip;
> +	chip = gpio_to_chip(group->gpios[0]);
> +	chip->set_multi(chip, group->mask, value | group->mask);
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_set_raw);
>  /**
>   * __gpio_cansleep() - report whether gpio value access will sleep
>   * @gpio: gpio in question
> @@ -1328,6 +2195,11 @@ int __gpio_cansleep(unsigned gpio)
>  }
>  EXPORT_SYMBOL_GPL(__gpio_cansleep);
> 
> +int gpio_group_cansleep(const struct gpio_group *group)
> +{
> +	return __gpio_cansleep(group->gpios[0]);
> +}
> +
>  /**
>   * __gpio_to_irq() - return the IRQ corresponding to a GPIO
>   * @gpio: gpio whose IRQ will be returned (already requested)
> @@ -1362,6 +2234,15 @@ int gpio_get_value_cansleep(unsigned gpio)
>  }
>  EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);
> 
> +u32 gpio_group_get_raw_cansleep(const struct gpio_group *group)
> +{
> +	struct gpio_chip *chip;
> +	might_sleep_if(extra_checks);
> +	chip = gpio_to_chip(group->gpios[0]);
> +	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_get_raw_cansleep);
> +
>  void gpio_set_value_cansleep(unsigned gpio, int value)
>  {
>  	struct gpio_chip	*chip;
> @@ -1372,6 +2253,14 @@ void gpio_set_value_cansleep(unsigned gpio, int value)
>  }
>  EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
> 
> +void gpio_group_set_raw_cansleep(const struct gpio_group *group, u32 value)
> +{
> +	struct gpio_chip *chip;
> +	might_sleep_if(extra_checks);
> +	chip = gpio_to_chip(group->gpios[0]);
> +	chip->set_multi(chip, group->mask, value & group->mask);
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_set_raw_cansleep);
> 
>  #ifdef CONFIG_DEBUG_FS
> 
> @@ -1381,18 +2270,24 @@ static void gpiolib_dbg_show(struct seq_file
> *s, struct gpio_chip *chip)
>  	unsigned		gpio = chip->base;
>  	struct gpio_desc	*gdesc = &gpio_desc[gpio];
>  	int			is_out;
> +	int			is_open;
> +	int     is_group;
> 
>  	for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
>  		if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
>  			continue;
> 
>  		is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
> -		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s",
> -			gpio, gdesc->label,
> -			is_out ? "out" : "in ",
> -			chip->get
> +		is_open = test_bit(FLAG_OPEN_DRAIN, &gdesc->flags);
> +		is_group = (gdesc->group != NULL);
> +		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s",
> +				gpio, gdesc->label,
> +				is_out ? "out" : "in ",
> +				chip->get
>  				? (chip->get(chip, i) ? "hi" : "lo")
> -				: "?  ");
> +				: "?  ",
> +				is_open ? "open" : "act ",
> +				is_group ? "grp" : "pin");
> 
>  		if (!is_out) {
>  			int		irq = gpio_to_irq(gpio);
> @@ -1408,32 +2303,32 @@ static void gpiolib_dbg_show(struct seq_file
> *s, struct gpio_chip *chip)
>  				char *trigger;
> 
>  				switch (desc->status & IRQ_TYPE_SENSE_MASK) {
> -				case IRQ_TYPE_NONE:
> -					trigger = "(default)";
> -					break;
> -				case IRQ_TYPE_EDGE_FALLING:
> -					trigger = "edge-falling";
> -					break;
> -				case IRQ_TYPE_EDGE_RISING:
> -					trigger = "edge-rising";
> -					break;
> -				case IRQ_TYPE_EDGE_BOTH:
> -					trigger = "edge-both";
> -					break;
> -				case IRQ_TYPE_LEVEL_HIGH:
> -					trigger = "level-high";
> -					break;
> -				case IRQ_TYPE_LEVEL_LOW:
> -					trigger = "level-low";
> -					break;
> -				default:
> -					trigger = "?trigger?";
> -					break;
> +					case IRQ_TYPE_NONE:
> +						trigger = "(default)";
> +						break;
> +					case IRQ_TYPE_EDGE_FALLING:
> +						trigger = "edge-falling";
> +						break;
> +					case IRQ_TYPE_EDGE_RISING:
> +						trigger = "edge-rising";
> +						break;
> +					case IRQ_TYPE_EDGE_BOTH:
> +						trigger = "edge-both";
> +						break;
> +					case IRQ_TYPE_LEVEL_HIGH:
> +						trigger = "level-high";
> +						break;
> +					case IRQ_TYPE_LEVEL_LOW:
> +						trigger = "level-low";
> +						break;
> +					default:
> +						trigger = "?trigger?";
> +						break;
>  				}
> 
>  				seq_printf(s, " irq-%d %s%s",
> -					irq, trigger,
> -					(desc->status & IRQ_WAKEUP)
> +						irq, trigger,
> +						(desc->status & IRQ_WAKEUP)
>  						? " wakeup" : "");
>  			}
>  		}
> diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c
> index 29f19ce..a51d9ad 100644
> --- a/drivers/gpio/pcf857x.c
> +++ b/drivers/gpio/pcf857x.c
> @@ -79,6 +79,20 @@ static int pcf857x_input8(struct gpio_chip *chip,
> unsigned offset)
>  	return status;
>  }
> 
> +static int pcf857x_input8_multi(struct gpio_chip *chip, u32 mask)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	int		status;
> +
> +	mutex_lock(&gpio->lock);
> +	gpio->out |= (u16)mask;
> +	status = i2c_smbus_write_byte(gpio->client, gpio->out);
> +	mutex_unlock(&gpio->lock);
> +
> +	return status;
> +}
> +
> +
>  static int pcf857x_get8(struct gpio_chip *chip, unsigned offset)
>  {
>  	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> @@ -88,6 +102,15 @@ static int pcf857x_get8(struct gpio_chip *chip,
> unsigned offset)
>  	return (value < 0) ? 0 : (value & (1 << offset));
>  }
> 
> +static u32 pcf857x_get8_multi(struct gpio_chip *chip, u32 mask)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	u32		value;
> +
> +	value = i2c_smbus_read_byte(gpio->client);
> +	return (value < 0) ? 0 : (value & mask);
> +}
> +
>  static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value)
>  {
>  	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> @@ -105,6 +128,25 @@ static int pcf857x_output8(struct gpio_chip
> *chip, unsigned offset, int value)
>  	return status;
>  }
> 
> +static int pcf857x_output8_multi(struct gpio_chip *chip, u32 mask, u32 value)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	int		status;
> +
> +	mutex_lock(&gpio->lock);
> +	gpio->out &= ~mask;
> +	gpio->out |= value;
> +	status = i2c_smbus_write_byte(gpio->client, gpio->out);
> +	mutex_unlock(&gpio->lock);
> +
> +	return status;
> +}
> +
> +static void pcf857x_set8_multi(struct gpio_chip *chip, u32 mask, u32 value)
> +{
> +	pcf857x_output8_multi(chip, mask, value);
> +}
> +
>  static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
>  {
>  	pcf857x_output8(chip, offset, value);
> @@ -147,6 +189,19 @@ static int pcf857x_input16(struct gpio_chip
> *chip, unsigned offset)
>  	return status;
>  }
> 
> +static int pcf857x_input16_multi(struct gpio_chip *chip, u32 mask)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	int		status;
> +
> +	mutex_lock(&gpio->lock);
> +	gpio->out |= mask;
> +	status = i2c_write_le16(gpio->client, gpio->out);
> +	mutex_unlock(&gpio->lock);
> +
> +	return status;
> +}
> +
>  static int pcf857x_get16(struct gpio_chip *chip, unsigned offset)
>  {
>  	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> @@ -156,6 +211,15 @@ static int pcf857x_get16(struct gpio_chip *chip,
> unsigned offset)
>  	return (value < 0) ? 0 : (value & (1 << offset));
>  }
> 
> +static u32 pcf857x_get16_multi(struct gpio_chip *chip, u32 mask)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	u32		value;
> +
> +	value = i2c_read_le16(gpio->client);
> +	return (value < 0) ? 0 : (value & mask);
> +}
> +
>  static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value)
>  {
>  	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> @@ -173,11 +237,30 @@ static int pcf857x_output16(struct gpio_chip
> *chip, unsigned offset, int value)
>  	return status;
>  }
> 
> +static int pcf857x_output16_multi(struct gpio_chip *chip, u32 mask, u32 value)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	int		status;
> +
> +	mutex_lock(&gpio->lock);
> +	gpio->out &= ~mask;
> +	gpio->out |= value;
> +	status = i2c_write_le16(gpio->client, gpio->out);
> +	mutex_unlock(&gpio->lock);
> +
> +	return status;
> +}
> +
>  static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value)
>  {
>  	pcf857x_output16(chip, offset, value);
>  }
> 
> +static void pcf857x_set16_multi(struct gpio_chip *chip, u32 mask, u32 value)
> +{
> +	pcf857x_output16_multi(chip, mask, value);
> +}
> +
>  /*-------------------------------------------------------------------------*/
> 
>  static int pcf857x_probe(struct i2c_client *client,
> @@ -190,7 +273,6 @@ static int pcf857x_probe(struct i2c_client *client,
>  	pdata = client->dev.platform_data;
>  	if (!pdata) {
>  		dev_dbg(&client->dev, "no platform data\n");
> -		return -EINVAL;
>  	}
> 
>  	/* Allocate, initialize, and register this gpio_chip. */
> @@ -200,7 +282,7 @@ static int pcf857x_probe(struct i2c_client *client,
> 
>  	mutex_init(&gpio->lock);
> 
> -	gpio->chip.base = pdata->gpio_base;
> +	gpio->chip.base = pdata ? pdata->gpio_base : -1;
>  	gpio->chip.can_sleep = 1;
>  	gpio->chip.dev = &client->dev;
>  	gpio->chip.owner = THIS_MODULE;
> @@ -218,10 +300,14 @@ static int pcf857x_probe(struct i2c_client *client,
>  	 */
>  	gpio->chip.ngpio = id->driver_data;
>  	if (gpio->chip.ngpio == 8) {
> +		gpio->chip.direction_input_multi = pcf857x_input8_multi;
>  		gpio->chip.direction_input = pcf857x_input8;
>  		gpio->chip.get = pcf857x_get8;
> +		gpio->chip.get_multi = pcf857x_get8_multi;
>  		gpio->chip.direction_output = pcf857x_output8;
> +		gpio->chip.direction_output_multi = pcf857x_output8_multi;
>  		gpio->chip.set = pcf857x_set8;
> +		gpio->chip.set_multi = pcf857x_set8_multi;
> 
>  		if (!i2c_check_functionality(client->adapter,
>  				I2C_FUNC_SMBUS_BYTE))
> @@ -239,9 +325,13 @@ static int pcf857x_probe(struct i2c_client *client,
>  	 */
>  	} else if (gpio->chip.ngpio == 16) {
>  		gpio->chip.direction_input = pcf857x_input16;
> +		gpio->chip.direction_input_multi = pcf857x_input16_multi;
>  		gpio->chip.get = pcf857x_get16;
> +		gpio->chip.get_multi = pcf857x_get16_multi;
>  		gpio->chip.direction_output = pcf857x_output16;
> +		gpio->chip.direction_output_multi = pcf857x_output16_multi;
>  		gpio->chip.set = pcf857x_set16;
> +		gpio->chip.set_multi = pcf857x_set16_multi;
> 
>  		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
>  			status = -EIO;
> @@ -278,7 +368,7 @@ static int pcf857x_probe(struct i2c_client *client,
>  	 * to zero, our software copy of the "latch" then matches the chip's
>  	 * all-ones reset state.  Otherwise it flags pins to be driven low.
>  	 */
> -	gpio->out = ~pdata->n_latch;
> +	gpio->out = pdata ? ~pdata->n_latch : ~0;
> 
>  	status = gpiochip_add(&gpio->chip);
>  	if (status < 0)
> @@ -299,7 +389,7 @@ static int pcf857x_probe(struct i2c_client *client,
>  	/* Let platform code set up the GPIOs and their users.
>  	 * Now is the first time anyone could use them.
>  	 */
> -	if (pdata->setup) {
> +	if (pdata && pdata->setup) {
>  		status = pdata->setup(client,
>  				gpio->chip.base, gpio->chip.ngpio,
>  				pdata->context);
> @@ -310,7 +400,7 @@ static int pcf857x_probe(struct i2c_client *client,
>  	return 0;
> 
>  fail:
> -	dev_dbg(&client->dev, "probe error %d for '%s'\n",
> +	dev_err(&client->dev, "probe error %d for '%s'\n",
>  			status, client->name);
>  	kfree(gpio);
>  	return status;
> @@ -322,7 +412,7 @@ static int pcf857x_remove(struct i2c_client *client)
>  	struct pcf857x			*gpio = i2c_get_clientdata(client);
>  	int				status = 0;
> 
> -	if (pdata->teardown) {
> +	if (pdata && pdata->teardown) {
>  		status = pdata->teardown(client,
>  				gpio->chip.base, gpio->chip.ngpio,
>  				pdata->context);
> diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
> index 66d6106..ed2407f 100644
> --- a/include/asm-generic/gpio.h
> +++ b/include/asm-generic/gpio.h
> @@ -30,6 +30,7 @@ static inline int gpio_is_valid(int number)
> 
>  struct seq_file;
>  struct module;
> +struct device_node;
> 
>  /**
>   * struct gpio_chip - abstract a GPIO controller
> @@ -89,6 +90,22 @@ struct gpio_chip {
>  						unsigned offset, int value);
>  	void			(*set)(struct gpio_chip *chip,
>  						unsigned offset, int value);
> +	int			(*set_opendrain)(struct gpio_chip *chip,
> +						unsigned offset, int value);
> +	int			(*set_opendrain_multi)(struct gpio_chip *chip,
> +						u32 mask, u32 value);
> +
> +	int			(*request_multi)(struct gpio_chip *chip,
> +						u32 mask);
> +	void			(*free_multi)(struct gpio_chip *chip,
> +						u32 mask);
> +
> +	int			(*direction_input_multi)(struct gpio_chip *chip,
> +						u32 mask);
> +	int			(*direction_output_multi)(struct gpio_chip *chip,
> +						u32 mask, u32 value);
> +	void			(*set_multi)(struct gpio_chip *chip, u32 mask, u32 value);
> +	u32			(*get_multi)(struct gpio_chip *chip, u32 mask);
> 
>  	int			(*to_irq)(struct gpio_chip *chip,
>  						unsigned offset);
> @@ -100,6 +117,23 @@ struct gpio_chip {
>  	char			**names;
>  	unsigned		can_sleep:1;
>  	unsigned		exported:1;
> +
> +#if defined(CONFIG_OF_GPIO)
> +	/*
> +	 * If CONFIG_OF is enabled, then all GPIO controllers described in the
> +	 * device tree automatically may have an OF translation
> +	 */
> +	struct device_node *of_node;
> +	int of_gpio_n_cells;
> +	int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
> +		        const void *gpio_spec, u32 *flags);
> +#endif
> +};
> +
> +struct gpio_group
> +{
> +	unsigned gpios[32];
> +	u32 mask;
>  };
> 
>  extern const char *gpiochip_is_requested(struct gpio_chip *chip,
> @@ -109,6 +143,9 @@ extern int __must_check gpiochip_reserve(int
> start, int ngpio);
>  /* add/remove chips */
>  extern int gpiochip_add(struct gpio_chip *chip);
>  extern int __must_check gpiochip_remove(struct gpio_chip *chip);
> +extern struct gpio_chip *gpiochip_find(void *data,
> +					int (*match)(struct gpio_chip *chip,
> +						     void *data));
> 
> 
>  /* Always use the library code for GPIO management calls,
> @@ -120,9 +157,28 @@ extern void gpio_free(unsigned gpio);
>  extern int gpio_direction_input(unsigned gpio);
>  extern int gpio_direction_output(unsigned gpio, int value);
> 
> +extern int gpio_set_opendrain(unsigned gpio, int value);
> +
>  extern int gpio_get_value_cansleep(unsigned gpio);
>  extern void gpio_set_value_cansleep(unsigned gpio, int value);
> 
> +/*
> + * Handling of gpio groups
> + */
> +extern struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
> +    const char *label);
> +extern void gpio_group_free(struct gpio_group* group);
> +
> +extern int gpio_group_direction_input(const struct gpio_group *group);
> +extern int gpio_group_direction_output(const struct gpio_group
> *group, u32 value);
> +
> +extern u32 gpio_group_get_raw_cansleep(const struct gpio_group *group);
> +extern void gpio_group_set_raw_cansleep(const struct gpio_group
> *group, u32 value);
> +
> +extern int gpio_group_set_opendrain(struct gpio_group* group, u32 value);
> +
> +u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw);
> +u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value);
> 
>  /* A platform's <asm/gpio.h> code may want to inline the I/O calls when
>   * the GPIO is constant and refers to some always-present controller,
> @@ -135,6 +191,11 @@ extern int __gpio_cansleep(unsigned gpio);
> 
>  extern int __gpio_to_irq(unsigned gpio);
> 
> +extern u32 gpio_group_get_raw(const struct gpio_group *group);
> +extern void gpio_group_set_raw(const struct gpio_group *group, u32 value);
> +
> +extern int gpio_group_cansleep(const struct gpio_group *group);
> +
>  #ifdef CONFIG_GPIO_SYSFS
> 
>  /*
> @@ -146,6 +207,12 @@ extern int gpio_export_link(struct device *dev,
> const char *name,
>  			unsigned gpio);
>  extern void gpio_unexport(unsigned gpio);
> 
> +extern int gpio_group_export(struct gpio_group *group,
> +    bool direction_may_change);
> +extern int gpio_group_export_link(struct device *dev, const char *name,
> +    struct gpio_group *group);
> +extern void gpio_group_unexport(struct gpio_group *group);
> +
>  #endif	/* CONFIG_GPIO_SYSFS */
> 
>  #else	/* !CONFIG_HAVE_GPIO_LIB */
> diff --git a/include/linux/gpio-export.h b/include/linux/gpio-export.h
> new file mode 100644
> index 0000000..712e9ff
> --- /dev/null
> +++ b/include/linux/gpio-export.h
> @@ -0,0 +1,64 @@
> +/*    Structures for passing gpio settings to drivers/gpio/gpio-export.c
> + *    201103 steene99
> + */
> +#ifndef __LINUX_GPIO_EXPORT_H
> +#define __LINUX_GPIO_EXPORT_H
> +
> +enum gpio_direction
> +{
> +	GPIO_INPUT,
> +	GPIO_OUTPUT,
> +	GPIO_CHANGE,
> +	GPIO_OUTPUT_KEEP,
> +};
> +
> +struct mp_gpio_line
> +{
> +	int gpio_num;
> +	int active_low;
> +	int open_drain;
> +};
> +
> +#define GPIO_PIN(_nr, _active_low, _open_drain) \
> +{ \
> +	.gpio_num = _nr, \
> +	.active_low = _active_low, \
> +	.open_drain = _open_drain, \
> +}
> +
> +#define SIMPLE_GPIO_PIN(_name, _nr) \
> +static struct mp_gpio_line _name[] = \
> +{ \
> +	GPIO_PIN(_nr, 0, 0), \
> +}
> +
> +#define MAX_GPIO_LABEL_SIZE 32
> +
> +struct mp_gpio_platform_data
> +{
> +	int gpio_count;
> +	enum gpio_direction direction;
> +	u32 initialvalue;  // value
> +	struct mp_gpio_line *gpio_data;
> +	char desc[MAX_GPIO_LABEL_SIZE];
> +};
> +
> +#define DEFINE_GPIO_GROUP(_name, _count, _direction, _initial, _pin, _desc) \
> +	static struct mp_gpio_platform_data _name = { \
> +		.gpio_count = _count, \
> +		.direction = _direction, \
> +		.initialvalue = _initial, \
> +		.gpio_data = _pin, \
> +		.desc=_desc, \
> +	}
> +
> +#define GPIO_PDEV(_id, _pdata) \
> +{ \
> +	.name = "gpio-export", \
> +	.id = _id, \
> +	.dev = { \
> +		.platform_data = &_pdata, \
> +	}, \
> +}
> +
> +#endif //__LINUX_GPIO_EXPORT_H
> diff --git a/include/linux/gpio.h b/include/linux/gpio.h
> index 059bd18..f82edad 100644
> --- a/include/linux/gpio.h
> +++ b/include/linux/gpio.h
> @@ -13,6 +13,7 @@
>  #include <linux/errno.h>
> 
>  struct device;
> +struct gpio_chip;
> 
>  /*
>   * Some platforms don't support the GPIO programming interface.
> @@ -33,6 +34,11 @@ static inline int gpio_request(unsigned gpio, const
> char *label)
>  	return -ENOSYS;
>  }
> 
> +static inline int gpio_group_request(unsigned *gpios, int ngpios,
> const char *label)
> +{
> +	return ERR_PTR(-ENOSYS);
> +}
> +
>  static inline void gpio_free(unsigned gpio)
>  {
>  	might_sleep();
> @@ -41,16 +47,32 @@ static inline void gpio_free(unsigned gpio)
>  	WARN_ON(1);
>  }
> 
> +static inline void gpio_group_free(struct gpio_group *group)
> +{
> +	might_sleep();
> +	WARN_ON(1);
> +}
> +
>  static inline int gpio_direction_input(unsigned gpio)
>  {
>  	return -ENOSYS;
>  }
> 
> +static inline int gpio_group_direction_input(const struct gpio_group *group)
> +{
> +	return -ENOSYS;
> +}
> +
>  static inline int gpio_direction_output(unsigned gpio, int value)
>  {
>  	return -ENOSYS;
>  }
> 
> +static inline int gpio_group_direction_output(const struct gpio_group
> *group, u32 value)
> +{
> +	return -ENOSYS;
> +}
> +
>  static inline int gpio_get_value(unsigned gpio)
>  {
>  	/* GPIO can never have been requested or set as {in,out}put */
> @@ -58,12 +80,29 @@ static inline int gpio_get_value(unsigned gpio)
>  	return 0;
>  }
> 
> +static inline int gpio_group_get_raw(const struct gpio_group *group)
> +{
> +	WARN_ON(1);
> +	return 0;
> +}
> +
>  static inline void gpio_set_value(unsigned gpio, int value)
>  {
>  	/* GPIO can never have been requested or set as output */
>  	WARN_ON(1);
>  }
> 
> +static inline void gpio_group_set_raw(const struct gpio_group *group,
> u32 value)
> +{
> +	WARN_ON(1);
> +}
> +
> +static inline int gpio_set_opendrain(unsigned gpio, int value)
> +{
> +	WARN_ON(1);
> +	return -ENOSYS;
> +}
> +
>  static inline int gpio_cansleep(unsigned gpio)
>  {
>  	/* GPIO can never have been requested or set as {in,out}put */
> @@ -71,6 +110,12 @@ static inline int gpio_cansleep(unsigned gpio)
>  	return 0;
>  }
> 
> +static inline int gpio_group_cansleep(const struct gpio_group *group)
> +{
> +	WARN_ON(1);
> +	return 0;
> +}
> +
>  static inline int gpio_get_value_cansleep(unsigned gpio)
>  {
>  	/* GPIO can never have been requested or set as {in,out}put */
> @@ -78,12 +123,23 @@ static inline int gpio_get_value_cansleep(unsigned gpio)
>  	return 0;
>  }
> 
> +static inline int gpio_group_get_raw_cansleep(const struct gpio_group *group)
> +{
> +	WARN_ON(1);
> +	return 0;
> +}
> +
>  static inline void gpio_set_value_cansleep(unsigned gpio, int value)
>  {
>  	/* GPIO can never have been requested or set as output */
>  	WARN_ON(1);
>  }
> 
> +static inline void gpio_group_set_raw_cansleep(const struct
> gpio_group *group, u32 value)
> +{
> +	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 */
> @@ -91,6 +147,12 @@ static inline int gpio_export(unsigned gpio, bool
> direction_may_change)
>  	return -EINVAL;
>  }
> 
> +static inline int gpio_group_export(struct gpio_group *group, bool
> direction_may_change)
> +{
> +	WARN_ON(1);
> +	return -EINVAL;
> +}
> +
>  static inline int gpio_export_link(struct device *dev, const char *name,
>  				unsigned gpio)
>  {
> @@ -99,6 +161,12 @@ static inline int gpio_export_link(struct device
> *dev, const char *name,
>  	return -EINVAL;
>  }
> 
> +static inline int gpio_group_export_link(struct device *dev, const char *name,
> +    struct gpio_group *group)
> +{
> +	WARN_ON(1);
> +	return -EINVAL;
> +}
> 
>  static inline void gpio_unexport(unsigned gpio)
>  {
> @@ -106,6 +174,11 @@ static inline void gpio_unexport(unsigned gpio)
>  	WARN_ON(1);
>  }
> 
> +static inline void gpio_group_unexport(struct gpio_group *group)
> +{
> +	WARN_ON(1);
> +}
> +
>  static inline int gpio_to_irq(unsigned gpio)
>  {
>  	/* GPIO can never have been requested or set as input */
> @@ -122,4 +195,14 @@ static inline int irq_to_gpio(unsigned irq)
> 
>  #endif
> 
> +static inline int gpio_direction_output_keep(int gpio)
> +{
> +	return gpio_direction_output(gpio, gpio_get_value_cansleep(gpio));
> +}
> +
> +static inline int gpio_group_direction_output_keep(const struct
> gpio_group *group)
> +{
> +	return gpio_group_direction_output(group, gpio_group_get_raw_cansleep(group));
> +}
> +
>  #endif /* __LINUX_GPIO_H */
> -- 
> 1.7.5.4

-- 
Grant Likely, B.Sc, P.Eng.
Secret Lab Technologies, Ltd.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
@ 2013-02-15 13:16       ` Grant Likely
  0 siblings, 0 replies; 32+ messages in thread
From: Grant Likely @ 2013-02-15 13:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 18 Jan 2013 13:13:01 +0100, Stijn Devriendt <highguy@gmail.com> wrote:
> Hi Roland,
> 
> This mail has been long overdue due to issues with some internal
> permission-tool.
> Just to be clear, this is not a competing implementation, it's what we
> currently use as-is. I'm just posting this as a reference to see if
> perhaps more concepts could be reused. It's based on a 2.6.32 kernel.

Hi Stijn,

Thanks for posting this. As you say it isn't in a mergable state and
there are a lot if individual features lumped into a single patch where
some of them could be split out as seperate patches. Open-drain support
for instance is something that should be investigated.

However, the one thing I do want to raise (and I have this concern when
reading Roland's series too) is that I'm going to be very cautious about
extending the sysfs interface. Aside from the fact that the current
interface is buggy, it also potientially exposes far too much control
over hardware into userspace. I do see the advantages of a group
interface to a block of GPIO pins, but the details about how it is
accomplished are really key.

I'm in the process of reviewing Roland's patches now, and I'll respond
to his series with specifics.

g.

> 
> It includes:
> - labels in sysfs (to provide useful names to userspace)
> - gpio group support
> - exporting individual/groups of gpios dictated by platform-data or device-tree
> - open-drain support (different from mainline)
> - examplary support for multi-gpio to pcf8575 driver
> - gpio_direction_output_keep() function that prevents toggling when
> changing direction
> 
> Provided-as-is-by: Stijn Devriendt <sdevrien@cisco.com>
> ---
>  drivers/gpio/Kconfig        |    6 +
>  drivers/gpio/Makefile       |    1 +
>  drivers/gpio/gpio-export.c  |  327 ++++++++++++++
>  drivers/gpio/gpiolib.c      | 1021 ++++++++++++++++++++++++++++++++++++++++---
>  drivers/gpio/pcf857x.c      |  102 ++++-
>  include/asm-generic/gpio.h  |   67 +++
>  include/linux/gpio-export.h |   64 +++
>  include/linux/gpio.h        |   83 ++++
>  8 files changed, 1602 insertions(+), 69 deletions(-)
>  create mode 100644 drivers/gpio/gpio-export.c
>  create mode 100644 include/linux/gpio-export.h
> 
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 2ad0128..7daf6df 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -48,6 +48,12 @@ config DEBUG_GPIO
>  	  slower.  The diagnostics help catch the type of setup errors
>  	  that are most common when setting up new platforms or boards.
> 
> +config GPIO_EXPORT
> +	bool "GPIO export driver"
> +	depends on GPIO_SYSFS && GPIOLIB
> +	help
> +	  Say Y here to include the GPIO export driver.
> +
>  config GPIO_SYSFS
>  	bool "/sys/class/gpio/... (sysfs interface)"
>  	depends on SYSFS && EXPERIMENTAL
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 00a532c..40b96d7 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG
> 
>  obj-$(CONFIG_GPIOLIB)		+= gpiolib.o
> 
> +obj-$(CONFIG_GPIO_EXPORT)	+= gpio-export.o
>  obj-$(CONFIG_GPIO_ADP5520)	+= adp5520-gpio.o
>  obj-$(CONFIG_GPIO_LANGWELL)	+= langwell_gpio.o
>  obj-$(CONFIG_GPIO_MAX7301)	+= max7301.o
> diff --git a/drivers/gpio/gpio-export.c b/drivers/gpio/gpio-export.c
> new file mode 100644
> index 0000000..4ee4fe5
> --- /dev/null
> +++ b/drivers/gpio/gpio-export.c
> @@ -0,0 +1,327 @@
> +/* drivers/gpio/gpio-export.c
> + *
> + * Copyright (C) 2011 Stijn Devriendt, Cisco Systems Inc.
> + * Copyright (C) 2011 Eli Steenput, Cisco Systems Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/gpio.h>
> +#include <linux/device.h>
> +#include <linux/string.h>
> +#include <linux/gpio-export.h>
> +#include <linux/err.h>
> +
> +#ifdef CONFIG_OF
> +
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_gpio.h>
> +
> +#endif
> +
> +#include <linux/platform_device.h>
> +#include <linux/i2c.h>
> +
> +struct gpio_export_priv
> +{
> +	int count;
> +	int gpio_num;
> +	struct gpio_group *group;
> +	char desc[MAX_GPIO_LABEL_SIZE];
> +};
> +
> +static __devinit int common_gpio_probe(struct mp_gpio_platform_data
> *pdata, struct device *dev)
> +{
> +	int gpio_count = pdata->gpio_count;
> +	struct mp_gpio_line *gpio_line;
> +	int i;
> +	int err = 0;
> +	struct gpio_export_priv *priv;
> +
> +	if (gpio_count <= 0 || gpio_count > 32)
> +		return -ENODEV;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->count = gpio_count;
> +	strncpy(priv->desc, pdata->desc, MAX_GPIO_LABEL_SIZE);
> +	if (gpio_count > 1)
> +	{
> +		u32 gpio[32];
> +		u32 value = pdata->initialvalue;
> +		u32 opendrain = 0;
> +		for (i = 0; i < gpio_count; ++i)
> +		{
> +			gpio_line = &pdata->gpio_data[i];
> +			gpio[i] = gpio_line->gpio_num;
> +			if (gpio_line->active_low)
> +				value ^= (1 << i);
> +			if (gpio_line->open_drain)
> +				opendrain |= (1 << i);
> +		}
> +
> +		priv->group = gpio_group_request(gpio, gpio_count, priv->desc);
> +		if (IS_ERR(priv->group))
> +		{
> +			dev_err(dev, "Could not request gpio-group: %ld\n", PTR_ERR(priv->group));
> +			err = PTR_ERR(priv->group);
> +			goto out_mem;
> +		}
> +
> +		if (opendrain)
> +		{
> +			err = gpio_group_set_opendrain(priv->group,
> gpio_group_value_to_raw(priv->group, opendrain));
> +			if (err)
> +			{
> +				dev_err(dev, "Could not set gpio-group open-drain: %d\n", err);
> +				goto out_free_group;
> +			}
> +		}
> +
> +		switch (pdata->direction)
> +		{
> +			case GPIO_INPUT:
> +				err = gpio_group_direction_input(priv->group);
> +				break;
> +			case GPIO_OUTPUT:
> +				err = gpio_group_direction_output(priv->group,
> gpio_group_value_to_raw(priv->group, value));
> +				break;
> +			case GPIO_OUTPUT_KEEP:
> +				err = gpio_group_direction_output_keep(priv->group);
> +				break;
> +			default:
> +        break;
> +		}
> +
> +		if (err)
> +		{
> +			dev_err(dev, "Could not set gpio-group direction: %d\n", err);
> +			goto out_free_group;
> +		}
> +
> +		err = gpio_group_export(priv->group, pdata->direction == GPIO_CHANGE);
> +		if (err)
> +		{
> +			dev_err(dev, "Could not export gpio-group: %d\n", err);
> +			goto out_free_group;
> +		}
> +	}
> +	else
> +	{
> +		gpio_line = &pdata->gpio_data[0];
> +		err = gpio_request(gpio_line->gpio_num, priv->desc);
> +		if (err)
> +		{
> +			dev_err(dev, "Could not request gpio %d\n", gpio_line->gpio_num);
> +			goto out_free;
> +		}
> +		if (gpio_line->open_drain)
> +		{
> +			err = gpio_set_opendrain(gpio_line->gpio_num, 1);
> +			if (err)
> +			{
> +				dev_warn(dev, "Could not set open-drain on gpio %d\n",
> gpio_line->gpio_num);
> +				goto out_free;
> +			}
> +		}
> +
> +		if (pdata->direction == GPIO_INPUT)
> +		{
> +			err = gpio_direction_input(gpio_line->gpio_num);
> +		}
> +		else if (pdata->direction == GPIO_OUTPUT)
> +		{
> +			int value = 0;
> +			value = pdata->initialvalue;
> +			if (gpio_line->active_low)
> +				value = !value;
> +			dev_dbg(dev, "Setting output on gpio %d with value %d\n",
> gpio_line->gpio_num, value);
> +			err = gpio_direction_output(gpio_line->gpio_num, value);
> +		}
> +		else if (pdata->direction == GPIO_OUTPUT_KEEP)
> +		{
> +			err = gpio_direction_output_keep(gpio_line->gpio_num);
> +		}
> +
> +		if (err)
> +		{
> +			dev_err(dev, "Could not set direction: %d\n", err);
> +			goto out_free;
> +		}
> +
> +		err = gpio_export(gpio_line->gpio_num, pdata->direction == GPIO_CHANGE);
> +		if (err)
> +		{
> +			dev_warn(dev, "Could not export gpio %d\n", gpio_line->gpio_num);
> +			goto out_free;
> +		}
> +		priv->gpio_num = gpio_line->gpio_num;
> +	}
> +	dev_set_drvdata(dev, priv);
> +	dev_info(dev, "%s: Exported %d GPIO pins\n", pdata->desc, pdata->gpio_count);
> +out:
> +	return err;
> +out_free:
> +	gpio_free(gpio_line->gpio_num);
> +	kfree(priv);
> +	goto out;
> +out_free_group:
> +	gpio_group_free(priv->group);
> +	kfree(priv);
> +	goto out;
> +out_mem:
> +	kfree(priv);
> +	goto out;
> +}
> +
> +int common_gpio_remove(struct device *dev)
> +{
> +	struct gpio_export_priv *priv = dev_get_drvdata(dev);
> +	BUG_ON(!priv);
> +
> +	if (priv->count == 1)
> +		gpio_free(priv->gpio_num);
> +	else
> +		gpio_group_free(priv->group);
> +
> +	dev_set_drvdata(dev, NULL);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +
> +static __devinit int of_gpio_probe(struct platform_device *of_dev,
> +		const struct of_device_id *match)
> +{
> +	struct device_node *np = of_dev->dev.of_node;
> +	int gpio_count = of_gpio_count(np);
> +	const char* linuxname = of_get_property(np, "desc", NULL);
> +
> +	struct mp_gpio_platform_data pdata;
> +	struct mp_gpio_line gpio_line[32];
> +	int i;
> +
> +	pdata.gpio_data = gpio_line;
> +	pdata.gpio_count = gpio_count;
> +	strncpy(pdata.desc, linuxname, MAX_GPIO_LABEL_SIZE);
> +	pdata.initialvalue = 0;
> +
> +	if (of_device_is_compatible(np, "gpio-input"))
> +		pdata.direction = GPIO_INPUT;
> +	else if (of_device_is_compatible(np, "gpio-output"))
> +	{
> +		const __be32 *value = of_get_property_u32(np, "initial");
> +		if (!value)
> +			pdata.direction = GPIO_OUTPUT_KEEP;
> +		else
> +		{
> +			pdata.direction = GPIO_OUTPUT;
> +			pdata.initialvalue = be32_to_cpup(value);
> +			dev_dbg(&of_dev->dev, "initialvalue=%u, keep=%d\n",
> pdata.initialvalue, pdata.direction == GPIO_OUTPUT_KEEP);
> +		}
> +	}
> +	else
> +	{
> +		pdata.direction = GPIO_CHANGE;
> +	}
> +
> +	for (i=0; i < gpio_count; ++i)
> +	{
> +		u32 flags;
> +		gpio_line[i].gpio_num = of_get_gpio_flags(np, i, &flags);
> +		gpio_line[i].active_low = flags & OF_GPIO_ACTIVE_LOW;
> +		gpio_line[i].open_drain = flags & OF_GPIO_OPEN_DRAIN;
> +	}
> +	return common_gpio_probe(&pdata, &of_dev->dev);
> +}
> +
> +static __devexit int of_gpio_remove(struct platform_device *of_dev)
> +{
> +	return common_gpio_remove(&of_dev->dev);
> +}
> +
> +static const struct of_device_id __devinitconst of_gpio_match[] = {
> +	{ .compatible = "gpio-input", },
> +	{ .compatible = "gpio-output", },
> +	{ .compatible = "gpio-user", },
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(of, of_gpio_match);
> +
> +static struct of_platform_driver of_gpio_driver = {
> +	.probe = of_gpio_probe,
> +	.remove = __devexit_p(of_gpio_remove),
> +	.driver = {
> +		.owner = THIS_MODULE,
> +		.name = "of-gpio",
> +		.of_match_table = of_gpio_match,
> +	},
> +};
> +
> +static int __init of_gpio_init(void)
> +{
> +	// Use i2c_bus_type to support I/O expanders?
> +	return of_register_platform_driver(&of_gpio_driver);
> +}
> +
> +static void __exit of_gpio_exit(void)
> +{
> +	of_unregister_driver(&of_gpio_driver);
> +}
> +
> +late_initcall(of_gpio_init);
> +module_exit(of_gpio_exit);
> +
> +#else
> +
> +static __devinit int mp_probe(struct platform_device *p_device)
> +{
> +	return common_gpio_probe(dev_get_platdata(&p_device->dev), &p_device->dev);
> +}
> +
> +static __devexit int mp_remove(struct platform_device *p_device)
> +{
> +	return common_gpio_remove(&p_device->dev);
> +}
> +
> +static struct platform_device_id mp_id_table[] = {
> +	{
> +		.name		= "gpio-export",
> +	},
> +};
> +
> +static struct platform_driver mp_gpio_driver = {
> +	.probe = mp_probe,
> +	.remove = mp_remove,
> +	.id_table = mp_id_table,
> +	.driver.name = "gpio-export",
> +	.driver.bus = &platform_bus_type,
> +	.driver.owner = THIS_MODULE,
> +};
> +
> +static int __init mp_gpio_init(void)
> +{
> +	return platform_driver_register(&mp_gpio_driver);
> +}
> +
> +static void __exit mp_gpio_exit(void)
> +{
> +	platform_driver_unregister(&mp_gpio_driver);
> +}
> +
> +late_initcall(mp_gpio_init);
> +module_exit(mp_gpio_exit);
> +
> +#endif
> +
> +MODULE_AUTHOR("Stijn Devriendt, Eli Steenput");
> +MODULE_DESCRIPTION("Multi Purpose GPIO export driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index 50de0f5..1c9c426 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -8,7 +8,9 @@
>  #include <linux/debugfs.h>
>  #include <linux/seq_file.h>
>  #include <linux/gpio.h>
> +#include <linux/of_gpio.h>
>  #include <linux/idr.h>
> +#include <linux/ctype.h>
> 
> 
>  /* Optional implementation infrastructure for GPIO interfaces.
> @@ -53,15 +55,15 @@ struct gpio_desc {
>  #define FLAG_SYSFS	4	/* exported via /sys/class/gpio/control */
>  #define FLAG_TRIG_FALL	5	/* trigger on falling edge */
>  #define FLAG_TRIG_RISE	6	/* trigger on rising edge */
> +#define FLAG_OPEN_DRAIN 7       /* gpio is open drain */
> 
>  #define PDESC_ID_SHIFT	16	/* add new flags before this one */
> 
>  #define GPIO_FLAGS_MASK		((1 << PDESC_ID_SHIFT) - 1)
>  #define GPIO_TRIGGER_MASK	(BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
> 
> -#ifdef CONFIG_DEBUG_FS
>  	const char		*label;
> -#endif
> +	struct gpio_group *group;
>  };
>  static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
> 
> @@ -76,9 +78,7 @@ static struct idr pdesc_idr;
> 
>  static inline void desc_set_label(struct gpio_desc *d, const char *label)
>  {
> -#ifdef CONFIG_DEBUG_FS
>  	d->label = label;
> -#endif
>  }
> 
>  /* Warn when drivers omit gpio_request() calls -- legal but ill-advised
> @@ -119,6 +119,11 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
>  	return gpio_desc[gpio].chip;
>  }
> 
> +static inline struct gpio_group *gpio_to_group(unsigned gpio)
> +{
> +	return gpio_desc[gpio].group;
> +}
> +
>  /* dynamic allocation of GPIOs, e.g. on a hotplugged device */
>  static int gpiochip_find_base(int ngpio)
>  {
> @@ -189,6 +194,110 @@ err:
>  	return ret;
>  }
> 
> +/* gpio_group_raw_to_value() - reorder bits according to the gpio group
> + * request
> + *
> + * @group: gpio_group
> + * @raw: raw value
> + *
> + * Returns a compact value representing the gpio_group value.
> + * e.g. consider gpio pins [1,0,2,3] have been requested and their
> + * value is [1,0,0,1] respectively. The raw readout will be 0xA [1,0,1,0]
> + * while the return value of this function will be 0x9, considering
> + * the order of the GPIOs in the group.
> + */
> +u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw)
> +{
> +	int i = 0;
> +	u32 ret = 0;
> +	unsigned base = gpio_to_chip(group->gpios[0])->base;
> +
> +	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
> +	{
> +		unsigned offset = group->gpios[i] - base;
> +		u32 rawbit = (1 << offset);
> +
> +		// if raw[offset] is set
> +		// then set ret[i]
> +		if (raw & rawbit)
> +			ret |= (1 << i);
> +
> +		++i;
> +	}
> +	return ret;
> +}
> +
> +/* gpio_group_value_to_raw() - Inverse of gpio_group_raw_to_value
> + *
> + * @group: gpio_group
> + * @raw: compact value
> + *
> + * Returns the raw value.
> + */
> +u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value)
> +{
> +	int i = 0;
> +	u32 raw = 0;
> +	unsigned base = gpio_to_chip(group->gpios[0])->base;
> +
> +	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
> +	{
> +		unsigned offset = group->gpios[i] - base;
> +		u32 rawbit = (1 << offset);
> +
> +		// if value[i] is set
> +		// then set ret[offset]
> +		if (value & (1 << i))
> +			raw |= rawbit;
> +
> +		++i;
> +	}
> +	return raw;
> +}
> +
> +int gpio_group_test_bit(unsigned long flag, const struct gpio_group *group)
> +{
> +	u32 set = 0;
> +	int i;
> +	unsigned base = gpio_to_chip(group->gpios[0])->base;
> +	for (i = 0; i < 32; ++i)
> +	{
> +		if (group->mask & (1 << i) && test_bit(flag, &gpio_desc[base + i].flags))
> +			set |= (1 << i);
> +	}
> +	if (set == 0)
> +		return 0;
> +	else if (set == group->mask)
> +		return 1;
> +	else
> +		return -EIO;
> +}
> +
> +void gpio_group_set_bit(unsigned long flag, const struct gpio_group *group)
> +{
> +	int i;
> +	unsigned base = gpio_to_chip(group->gpios[0])->base;
> +
> +	for (i = 0; i < 32; ++i)
> +	{
> +		if (group->mask & (1 << i))
> +			set_bit(flag, &gpio_desc[base + i].flags);
> +	}
> +}
> +
> +void gpio_group_clear_bit(unsigned long flag, const struct gpio_group *group)
> +{
> +	int i;
> +	unsigned base = gpio_to_chip(group->gpios[0])->base;
> +
> +	for (i = 0; i < 32; ++i)
> +	{
> +		if (group->mask & (1 << i))
> +			clear_bit(flag, &gpio_desc[base + i].flags);
> +	}
> +}
> +
> +
>  #ifdef CONFIG_GPIO_SYSFS
> 
>  /* lock protects against unexport_gpio() being called while
> @@ -231,6 +340,30 @@ static ssize_t gpio_direction_show(struct device *dev,
>  	return status;
>  }
> 
> +static ssize_t gpio_group_direction_show(struct device *dev,
> +    struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status = 0;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +
> +	status = gpio_group_test_bit(FLAG_IS_OUT, group);
> +	if (status >= 0)
> +		status = sprintf(buf, "%s\n", status == 0 ? "in" : "out");
> +
> +out:
> +	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)
>  {
> @@ -255,9 +388,49 @@ static ssize_t gpio_direction_store(struct device *dev,
>  	return status ? : size;
>  }
> 
> -static const DEVICE_ATTR(direction, 0644,
> +static ssize_t gpio_group_direction_store(struct device *dev,
> +    struct device_attribute *attr, const char* buf, size_t size)
> +{
> +  const struct gpio_group *group = dev_get_drvdata(dev);
> +  ssize_t status;
> +  unsigned long input;
> +
> +  mutex_lock(&sysfs_lock);
> +
> +  if (gpio_desc[group->gpios[0]].group != group
> +   || gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
> +  {
> +    status = -EIO;
> +    goto out;
> +  }
> +
> +  if (sysfs_streq(buf, "in"))
> +    status = gpio_group_direction_input(group);
> +  else if (sysfs_streq(buf, "out"))
> +    status = gpio_group_direction_output(group, 0);
> +  else
> +  {
> +    status = strict_strtoul(buf, 0, &input);
> +    if (!status)
> +    {
> +      if ((input & group->mask) == input)
> +        status = gpio_group_direction_output(group, input);
> +      else
> +        status = -EINVAL;
> +    }
> +  }
> +
> +out:
> +  mutex_unlock(&sysfs_lock);
> +  return status ? : size;
> +}
> +
> +static DEVICE_ATTR(direction, 0644,
>  		gpio_direction_show, gpio_direction_store);
> 
> +struct device_attribute dev_attr_direction_group =
> +	__ATTR(direction, 0644, gpio_group_direction_show,
> gpio_group_direction_store);
> +
>  static ssize_t gpio_value_show(struct device *dev,
>  		struct device_attribute *attr, char *buf)
>  {
> @@ -276,6 +449,50 @@ static ssize_t gpio_value_show(struct device *dev,
>  	return status;
>  }
> 
> +static ssize_t gpio_group_value_show(struct device *dev,
> +    struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +	status = sprintf(buf, "%u\n", gpio_group_raw_to_value(group,
> +				gpio_group_get_raw_cansleep(group)));
> +
> +out:
> +	mutex_unlock(&sysfs_lock);
> +	return status;
> +}
> +
> +
> +static ssize_t gpio_group_raw_show(struct device *dev,
> +    struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +	status = sprintf(buf, "%u\n", gpio_group_get_raw_cansleep(group));
> +
> +out:
> +	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)
>  {
> @@ -303,9 +520,96 @@ static ssize_t gpio_value_store(struct device *dev,
>  	return status;
>  }
> 
> +static ssize_t gpio_group_value_store(struct device *dev,
> +    struct device_attribute *attr, const char* buf, size_t size)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status;
> +	unsigned long value;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
> +			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +	status = strict_strtoul(buf, 0, &value);
> +	if (status == 0)
> +	{
> +		gpio_group_set_raw_cansleep(group,
> +				gpio_group_value_to_raw(group, value));
> +		status = size;
> +	}
> +
> +out:
> +	mutex_unlock(&sysfs_lock);
> +
> +	return status;
> +}
> +
> +static ssize_t gpio_group_raw_store(struct device *dev,
> +    struct device_attribute *attr, const char* buf, size_t size)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status;
> +	unsigned long value;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
> +			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +	status = strict_strtoul(buf, 0, &value);
> +	if (status == 0)
> +	{
> +		gpio_group_set_raw_cansleep(group, value);
> +		status = size;
> +	}
> +
> +out:
> +	mutex_unlock(&sysfs_lock);
> +
> +	return status;
> +}
> +
> +static ssize_t gpio_group_mask_show(struct device *dev,
> +    struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	ssize_t status;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_desc[group->gpios[0]].group != group
> +			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
> +	{
> +		status = -EIO;
> +		goto out;
> +	}
> +	status = sprintf(buf, "%u\n", group->mask);
> +
> +out:
> +	mutex_unlock(&sysfs_lock);
> +	return status;
> +}
> +
>  static /*const*/ DEVICE_ATTR(value, 0644,
>  		gpio_value_show, gpio_value_store);
> 
> +struct device_attribute dev_attr_value_group =
> +	__ATTR(value, 0644, gpio_group_value_show, gpio_group_value_store);
> +
> +DEVICE_ATTR(raw, 0644, gpio_group_raw_show, gpio_group_raw_store);
> +
> +DEVICE_ATTR(mask, 0444, gpio_group_mask_show, NULL);
> +
>  static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
>  {
>  	struct work_struct	*work = priv;
> @@ -381,7 +685,7 @@ static int gpio_setup_irq(struct gpio_desc *desc,
> struct device *dev,
>  			goto free_id;
>  		}
> 
> -		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value");
> +		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
>  		if (!pdesc->value_sd) {
>  			ret = -ENODEV;
>  			goto free_id;
> @@ -475,9 +779,55 @@ found:
> 
>  static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);
> 
> +static ssize_t gpio_label_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_desc	*desc = dev_get_drvdata(dev);
> +	ssize_t			status = 0;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (!test_bit(FLAG_EXPORT, &desc->flags))
> +		status = -EIO;
> +	else if (desc->label)
> +		status = sprintf(buf, "%s\n", desc->label);
> +
> +	mutex_unlock(&sysfs_lock);
> +	return status;
> +}
> +
> +static ssize_t gpio_group_label_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_group *group = dev_get_drvdata(dev);
> +	const struct gpio_desc	*desc = &gpio_desc[group->gpios[0]];
> +	ssize_t			status = 0;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	if (!test_bit(FLAG_EXPORT, &desc->flags))
> +		status = -EIO;
> +	else if (desc->label)
> +		status = sprintf(buf, "%s\n", desc->label);
> +
> +	mutex_unlock(&sysfs_lock);
> +	return status;
> +}
> +
> +static struct device_attribute dev_attr_gpio_label = __ATTR(label,
> 0444, gpio_label_show, NULL);
> +static struct device_attribute dev_attr_gpio_group_label =
> __ATTR(label, 0444, gpio_group_label_show, NULL);
> +
>  static const struct attribute *gpio_attrs[] = {
> -	&dev_attr_direction.attr,
>  	&dev_attr_value.attr,
> +	&dev_attr_gpio_label.attr,
> +	NULL,
> +};
> +
> +static const struct attribute *gpio_group_attrs[] = {
> +	&dev_attr_value_group.attr,
> +	&dev_attr_raw.attr,
> +	&dev_attr_mask.attr,
> +	&dev_attr_gpio_group_label.attr,
>  	NULL,
>  };
> 
> @@ -485,6 +835,10 @@ static const struct attribute_group gpio_attr_group = {
>  	.attrs = (struct attribute **) gpio_attrs,
>  };
> 
> +static const struct attribute_group gpio_group_attr_group = {
> +	.attrs = (struct attribute **) gpio_group_attrs,
> +};
> +
>  /*
>   * /sys/class/gpio/gpiochipN/
>   *   /base ... matching gpio_chip.base (N)
> @@ -530,6 +884,36 @@ static const struct attribute_group gpiochip_attr_group = {
>  	.attrs = (struct attribute **) gpiochip_attrs,
>  };
> 
> +int read_gpios(const char *buf, size_t len, unsigned *gpios)
> +{
> +	const char *startp = buf;
> +	char *endp = (char*)buf;
> +	int i;
> +	for (i = 0; i < 32; ++i)
> +	{
> +		unsigned gpio;
> +
> +		gpio = simple_strtoul(startp, &endp, 0);
> +
> +		if (endp == startp)
> +			return -EINVAL; // not a number, bail out
> +
> +		gpios[i] = gpio;
> +
> +		while (endp - buf < len && isspace(*endp)) // eat whitespace
> +			endp++;
> +
> +
> +		if (endp - buf > len) // buffer overrun, should never happen
> +			return -EINVAL;
> +		else if (endp - buf == len) // end of buffer, return number of read gpios
> +			return i+1;
> +
> +		startp = endp;
> +	}
> +	return -EINVAL;
> +}
> +
>  /*
>   * /sys/class/gpio/export ... write-only
>   *	integer N ... number of GPIO to export (full access)
> @@ -538,10 +922,10 @@ static const struct attribute_group
> gpiochip_attr_group = {
>   */
>  static ssize_t export_store(struct class *class, const char *buf, size_t len)
>  {
> -	long	gpio;
> +	unsigned	gpio[32];
>  	int	status;
> 
> -	status = strict_strtol(buf, 0, &gpio);
> +	status = read_gpios(buf, len, gpio);
>  	if (status < 0)
>  		goto done;
> 
> @@ -550,16 +934,41 @@ static ssize_t export_store(struct class *class,
> const char *buf, size_t len)
>  	 * they may be undone on its behalf too.
>  	 */
> 
> -	status = gpio_request(gpio, "sysfs");
> -	if (status < 0)
> -		goto done;
> +	if (status == 1)
> +	{
> +		status = gpio_request(gpio[0], "sysfs");
> +		if (status < 0)
> +			goto done;
> 
> -	status = gpio_export(gpio, true);
> -	if (status < 0)
> -		gpio_free(gpio);
> +		status = gpio_export(gpio[0], true);
> +		if (status < 0)
> +			gpio_free(gpio[0]);
> +		else
> +			set_bit(FLAG_SYSFS, &gpio_desc[gpio[0]].flags);
> +	}
>  	else
> -		set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags);
> +	{
> +		struct gpio_group *group = gpio_group_request(gpio, status, "sysfs");
> +		if (IS_ERR(group))
> +		{
> +			status = PTR_ERR(group);
> +			goto done;
> +		}
> 
> +		status = gpio_group_export(group, true);
> +		if (status < 0)
> +			gpio_group_free(group);
> +		else
> +    {
> +      // Lock required to protect against unexport being called
> +      // against when only parts of the group have the flag set.
> +      // The other cases: all have the flag or none have the flag
> +      // are handled correctly.
> +      mutex_lock(&sysfs_lock);
> +			gpio_group_set_bit(FLAG_SYSFS, group);
> +      mutex_unlock(&sysfs_lock);
> +    }
> +	}
>  done:
>  	if (status)
>  		pr_debug("%s: status %d\n", __func__, status);
> @@ -570,6 +979,7 @@ static ssize_t unexport_store(struct class *class,
> const char *buf, size_t len)
>  {
>  	long	gpio;
>  	int	status;
> +	struct gpio_group *group;
> 
>  	status = strict_strtol(buf, 0, &gpio);
>  	if (status < 0)
> @@ -581,13 +991,29 @@ static ssize_t unexport_store(struct class
> *class, const char *buf, size_t len)
>  	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);
> +	group = gpio_to_group(gpio);
> +	if (group)
> +	{
> +		mutex_lock(&sysfs_lock);
> +		if (gpio_group_test_bit(FLAG_SYSFS, group))
> +		{
> +			status = 0;
> +			gpio_group_clear_bit(FLAG_SYSFS, group);
> +		}
> +		mutex_unlock(&sysfs_lock);
> +		if (!status)
> +			gpio_group_free(group);
> +	}
> +	else
> +	{
> +		/* 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)
> @@ -662,12 +1088,11 @@ int gpio_export(unsigned gpio, bool direction_may_change)
>  		dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
>  				desc, ioname ? ioname : "gpio%d", gpio);
>  		if (!IS_ERR(dev)) {
> -			if (direction_may_change)
> -				status = sysfs_create_group(&dev->kobj,
> -						&gpio_attr_group);
> -			else
> -				status = device_create_file(dev,
> -						&dev_attr_value);
> +  		status = sysfs_create_group(&dev->kobj,
> +  						&gpio_attr_group);
> +
> +			if (!status && direction_may_change)
> +				status = device_create_file(dev, &dev_attr_direction);
> 
>  			if (!status && gpio_to_irq(gpio) >= 0
>  					&& (direction_may_change
> @@ -694,6 +1119,72 @@ done:
>  }
>  EXPORT_SYMBOL_GPL(gpio_export);
> 
> +int gpio_group_export(struct gpio_group *group, bool direction_may_change)
> +{
> +	unsigned long flags;
> +	int status = -EINVAL;
> +	struct gpio_chip *chip;
> +	unsigned lowest = group->gpios[0];
> +	int i;
> +
> +	/* can't export until sysfs is available ... */
> +	if (!gpio_class.p) {
> +		pr_debug("%s: called too early!\n", __func__);
> +		return -ENOENT;
> +	}
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	spin_lock_irqsave(&gpio_lock, flags);
> +	chip = gpio_to_chip(group->gpios[0]);
> +	if (gpio_group_test_bit(FLAG_REQUESTED, group) > 0
> +			&& gpio_group_test_bit(FLAG_EXPORT, group) == 0)
> +	{
> +		status = 0;
> +		if (!chip->direction_input_multi || !chip->direction_output_multi)
> +		{
> +			direction_may_change = false;
> +		}
> +	}
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +
> +	if (status == 0)
> +	{
> +		struct device *dev;
> +
> +		i=1;
> +		while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
> +		{
> +			if (group->gpios[i] < lowest)
> +				lowest = group->gpios[i];
> +			++i;
> +		}
> +
> +		dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0),
> +				group, "group%d", lowest);
> +		if (!IS_ERR(dev)) {
> +			status = sysfs_create_group(&dev->kobj, &gpio_group_attr_group);
> +
> +			if (status == 0 && direction_may_change)
> +				status = device_create_file(dev, &dev_attr_direction_group);
> +
> +			if (status != 0)
> +				device_unregister(dev);
> +		}
> +		else
> +			status = PTR_ERR(dev);
> +		if (status == 0)
> +			gpio_group_set_bit(FLAG_EXPORT, group);
> +	}
> +	mutex_unlock(&sysfs_lock);
> +
> +	if (status)
> +		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
> +	return status;
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_export);
> +
> +
>  static int match_export(struct device *dev, void *data)
>  {
>  	return dev_get_drvdata(dev) == data;
> @@ -744,6 +1235,30 @@ done:
>  }
>  EXPORT_SYMBOL_GPL(gpio_export_link);
> 
> +int gpio_group_export_link(struct device *dev, const char *name,
> struct gpio_group *group)
> +{
> +	int status = -EINVAL;
> +	mutex_lock(&sysfs_lock);
> +	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
> +	{
> +		struct device *tdev;
> +
> +		tdev = class_find_device(&gpio_class, NULL, group, match_export);
> +		if (tdev != NULL) {
> +			status = sysfs_create_link(&dev->kobj, &tdev->kobj, name);
> +		}
> +		else
> +		{
> +			status = -ENODEV;
> +		}
> +	}
> +	mutex_unlock(&sysfs_lock);
> +	if (status)
> +		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
> +	return status;
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_export_link);
> +
>  /**
>   * gpio_unexport - reverse effect of gpio_export()
>   * @gpio: gpio to make unavailable
> @@ -783,6 +1298,31 @@ done:
>  }
>  EXPORT_SYMBOL_GPL(gpio_unexport);
> 
> +void gpio_group_unexport(struct gpio_group *group)
> +{
> +	int status = -EINVAL;
> +	mutex_lock(&sysfs_lock);
> +
> +	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
> +	{
> +		struct device *dev = NULL;
> +		dev = class_find_device(&gpio_class, NULL, group, match_export);
> +		if (dev)
> +		{
> +			gpio_group_clear_bit(FLAG_EXPORT, group);
> +			put_device(dev);
> +			device_unregister(dev);
> +			status = 0;
> +		}
> +		else
> +			status = -ENODEV;
> +	}
> +	mutex_unlock(&sysfs_lock);
> +	if (status)
> +		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_unexport);
> +
>  static int gpiochip_export(struct gpio_chip *chip)
>  {
>  	int		status;
> @@ -959,6 +1499,8 @@ int gpiochip_add(struct gpio_chip *chip)
>  		}
>  	}
> 
> +	of_gpiochip_add(chip);
> +
>  unlock:
>  	spin_unlock_irqrestore(&gpio_lock, flags);
>  	if (status == 0)
> @@ -987,6 +1529,8 @@ int gpiochip_remove(struct gpio_chip *chip)
> 
>  	spin_lock_irqsave(&gpio_lock, flags);
> 
> +	of_gpiochip_remove(chip);
> +
>  	for (id = chip->base; id < chip->base + chip->ngpio; id++) {
>  		if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) {
>  			status = -EBUSY;
> @@ -1007,6 +1551,38 @@ int gpiochip_remove(struct gpio_chip *chip)
>  }
>  EXPORT_SYMBOL_GPL(gpiochip_remove);
> 
> +/**
> + * gpiochip_find() - iterator for locating a specific gpio_chip
> + * @data: data to pass to match function
> + * @callback: Callback function to check gpio_chip
> + *
> + * Similar to bus_find_device.  It returns a reference to a gpio_chip as
> + * determined by a user supplied @match callback.  The callback should return
> + * 0 if the device doesn't match and non-zero if it does.  If the callback is
> + * non-zero, this function will return to the caller and not iterate over any
> + * more gpio_chips.
> + */
> +struct gpio_chip *gpiochip_find(void *data,
> +				int (*match)(struct gpio_chip *chip, void *data))
> +{
> +	struct gpio_chip *chip = NULL;
> +	unsigned long flags;
> +	int i;
> +
> +	spin_lock_irqsave(&gpio_lock, flags);
> +	for (i = 0; i < ARCH_NR_GPIOS; i++) {
> +		if (!gpio_desc[i].chip)
> +			continue;
> +
> +		if (match(gpio_desc[i].chip, data)) {
> +			chip = gpio_desc[i].chip;
> +			break;
> +		}
> +	}
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +
> +	return chip;
> +}
> 
>  /* These "optional" allocation calls help prevent drivers from stomping
>   * on each other, and help provide better diagnostics in debugfs.
> @@ -1066,6 +1642,111 @@ done:
>  }
>  EXPORT_SYMBOL_GPL(gpio_request);
> 
> +struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
> const char* label)
> +{
> +	int i;
> +	unsigned long flags;
> +	u32 mask = 0;
> +	struct gpio_chip *chip;
> +	int rc = 0;
> +	struct gpio_group *group;
> +
> +	if (ngpios <= 0)
> +		return ERR_PTR(-EINVAL);
> +
> +	group = kzalloc(sizeof(struct gpio_group), GFP_KERNEL);
> +	if (!group)
> +		return ERR_PTR(-ENOMEM);
> +
> +	spin_lock_irqsave(&gpio_lock, flags);
> +	if (!gpio_is_valid(gpio[0]))
> +	{
> +		rc = -EINVAL;
> +		goto out_free;
> +	}
> +	chip = gpio_to_chip(gpio[0]);
> +	// if not (multi-input or multi-output capable)
> +	if (!chip ||
> +			!(   (chip->get_multi && chip->direction_input_multi)
> +				|| (chip->set_multi && chip->direction_output_multi))
> +	   )
> +	{
> +		rc = -ENODEV;
> +		goto out_free;
> +	}
> +
> +	mask |= (1 << (gpio[0] - chip->base));
> +	for (i = 1; i < ngpios; ++i)
> +	{
> +		if (gpio[i] < chip->base || gpio[i] > chip->base + chip->ngpio)
> +		{
> +			rc = -EINVAL;
> +			goto out_free;
> +		}
> +		mask |= (1 << (gpio[i] - chip->base));
> +	}
> +
> +	if (!try_module_get(chip->owner))
> +	{
> +		rc = -ENOSYS;
> +		goto out_free;
> +	}
> +
> +	group->mask = mask;
> +	for (i = 0; i < 32; ++i)
> +	{
> +		if (i < ngpios)
> +			group->gpios[i] = gpio[i];
> +		else
> +			group->gpios[i] = ARCH_NR_GPIOS;
> +	}
> +
> +	if (gpio_group_test_bit(FLAG_REQUESTED, group) != 0)
> +	{
> +		rc = -EBUSY;
> +		goto out_put;
> +	}
> +
> +	gpio_group_set_bit(FLAG_REQUESTED, group);
> +	for (i = 0; i < ngpios; ++i)
> +	{
> +		gpio_desc[gpio[i]].group = group;
> +		desc_set_label(&gpio_desc[gpio[i]], label ? : "?");
> +	}
> +
> +	if (chip->request_multi) {
> +		spin_unlock_irqrestore(&gpio_lock, flags);
> +		rc = chip->request_multi(chip, mask);
> +		spin_lock_irqsave(&gpio_lock, flags);
> +		if (rc)
> +			goto out_label;
> +	}
> +
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +	return group;
> +out_label:
> +	for (i = 0; i < ngpios; ++i)
> +	{
> +		desc_set_label(&gpio_desc[gpio[i]], NULL);
> +		gpio_desc[gpio[i]].group = NULL;
> +	}
> +out_put:
> +	module_put(chip->owner);
> +out_free:
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +	kfree(group);
> +	pr_debug("%s: group%d (%s) status %d\n", __func__, *gpio, label ? : "?", rc);
> +	return ERR_PTR(rc);
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_request);
> +
> +struct gpio_group * gpio_get_group(unsigned gpio)
> +{
> +	if (!gpio_is_valid(gpio))
> +		return NULL;
> +	return gpio_desc[gpio].group;
> +}
> +
>  void gpio_free(unsigned gpio)
>  {
>  	unsigned long		flags;
> @@ -1102,6 +1783,45 @@ void gpio_free(unsigned gpio)
>  }
>  EXPORT_SYMBOL_GPL(gpio_free);
> 
> +void gpio_group_free(struct gpio_group *group)
> +{
> +	unsigned long flags;
> +	struct gpio_chip *chip;
> +
> +	might_sleep();
> +
> +	gpio_group_unexport(group);
> +
> +	spin_lock_irqsave(&gpio_lock, flags);
> +
> +	chip = gpio_to_chip(group->gpios[0]);
> +	if (chip && gpio_group_test_bit(FLAG_REQUESTED, group))
> +	{
> +		int i;
> +		if (chip->free_multi) {
> +			spin_unlock_irqrestore(&gpio_lock, flags);
> +			might_sleep_if(extra_checks && chip->can_sleep);
> +			chip->free_multi(chip, group->mask);
> +			spin_lock_irqsave(&gpio_lock, flags);
> +		}
> +		for (i = 0; i < 32; ++i)
> +		{
> +			if (group->mask & (1 << i))
> +			{
> +				desc_set_label(&gpio_desc[chip->base + i], NULL);
> +				gpio_desc[chip->base +i].group = NULL;
> +			}
> +			module_put(chip->owner);
> +			gpio_group_clear_bit(FLAG_REQUESTED, group);
> +		}
> +	}
> +	else
> +		WARN_ON(extra_checks);
> +
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +	kfree(group);
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_free);
> 
>  /**
>   * gpiochip_is_requested - return string iff signal was requested
> @@ -1124,15 +1844,10 @@ const char *gpiochip_is_requested(struct
> gpio_chip *chip, unsigned offset)
>  		return NULL;
>  	if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0)
>  		return NULL;
> -#ifdef CONFIG_DEBUG_FS
>  	return gpio_desc[gpio].label;
> -#else
> -	return "?";
> -#endif
>  }
>  EXPORT_SYMBOL_GPL(gpiochip_is_requested);
> 
> -
>  /* Drivers MUST set GPIO direction before making get/set calls.  In
>   * some cases this is done in early boot, before IRQs are enabled.
>   *
> @@ -1195,6 +1910,25 @@ fail:
>  }
>  EXPORT_SYMBOL_GPL(gpio_direction_input);
> 
> +
> +int gpio_group_direction_input(const struct gpio_group *group)
> +{
> +	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
> +	int rc;
> +
> +	if (!chip->get_multi || !chip->direction_input_multi)
> +		return -EINVAL;
> +
> +	rc = chip->direction_input_multi(chip, group->mask);
> +	if (rc == 0)
> +	{
> +		gpio_group_clear_bit(FLAG_IS_OUT, group);
> +	}
> +	else
> +		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
> +	return rc;
> +}
> +
>  int gpio_direction_output(unsigned gpio, int value)
>  {
>  	unsigned long		flags;
> @@ -1248,6 +1982,124 @@ fail:
>  }
>  EXPORT_SYMBOL_GPL(gpio_direction_output);
> 
> +int gpio_group_direction_output(const struct gpio_group *group, u32 value)
> +{
> +	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
> +	int rc;
> +
> +	if (!chip->set_multi || !chip->direction_output_multi)
> +		return -EINVAL;
> +
> +	rc = chip->direction_output_multi(chip, group->mask, value & group->mask);
> +	if (rc == 0)
> +	{
> +		gpio_group_set_bit(FLAG_IS_OUT, group);
> +	}
> +	else
> +		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
> +	return rc;
> +}
> +
> +int gpio_set_opendrain(unsigned gpio, int value)
> +{
> +	unsigned long		flags;
> +	struct gpio_chip	*chip;
> +	struct gpio_desc	*desc = &gpio_desc[gpio];
> +	int			status = -EINVAL;
> +
> +	spin_lock_irqsave(&gpio_lock, flags);
> +
> +	if (!gpio_is_valid(gpio))
> +		goto fail;
> +	chip = desc->chip;
> +	if (!chip || !chip->set || !chip->direction_output)
> +		goto fail;
> +	gpio -= chip->base;
> +	if (gpio >= chip->ngpio)
> +		goto fail;
> +	status = gpio_ensure_requested(desc, gpio);
> +	if (status < 0)
> +		goto fail;
> +
> +	/* now we know the gpio is valid and chip won't vanish */
> +
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +
> +	might_sleep_if(extra_checks && chip->can_sleep);
> +
> +	if (!chip->set_opendrain)
> +		return -ENOSYS;
> +
> +	if (status) {
> +		status = chip->request(chip, gpio);
> +		if (status < 0) {
> +			pr_debug("GPIO-%d: chip request fail, %d\n",
> +				chip->base + gpio, status);
> +			/* and it's not available to anyone else ...
> +			 * gpio_request() is the fully clean solution.
> +			 */
> +			goto lose;
> +		}
> +	}
> +
> +	status = chip->set_opendrain(chip, gpio, value);
> +	if (status == 0)
> +	{
> +		if (value)
> +			set_bit(FLAG_OPEN_DRAIN, &desc->flags);
> +		else
> +			clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
> +	}
> +lose:
> +	return status;
> +fail:
> +	spin_unlock_irqrestore(&gpio_lock, flags);
> +	if (status)
> +		pr_debug("%s: gpio-%d status %d\n",
> +			__func__, gpio, status);
> +	return status;
> +}
> +EXPORT_SYMBOL_GPL(gpio_set_opendrain);
> +
> +int gpio_group_set_opendrain(struct gpio_group *group, u32 value)
> +{
> +	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
> +	int err;
> +	if (!chip)
> +		return -EINVAL;
> +	if (!chip->set_multi || !chip->direction_output_multi)
> +		return -EINVAL;
> +	if (!chip->set_opendrain_multi)
> +		return -ENOSYS;
> +
> +	might_sleep_if(extra_checks && chip->can_sleep);
> +
> +	err = chip->set_opendrain_multi(chip, group->mask, value & group->mask);
> +	if (err == 0)
> +	{
> +		int i;
> +		unsigned base = chip->base;
> +		for (i = 0; i < 32; ++i)
> +		{
> +			if (group->mask & (1 << i))
> +			{
> +				if (value & (1 << i))
> +				{
> +					set_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
> +				}
> +				else
> +				{
> +					clear_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
> +				}
> +			}
> +		}
> +	}
> +	if (err)
> +		pr_debug("%s: group%d status %d\n",
> +				__func__, group->gpios[0], err);
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_set_opendrain);
> 
>  /* I/O calls are only valid after configuration completed; the relevant
>   * "is this a valid GPIO" error checks should already have been done.
> @@ -1290,6 +2142,14 @@ int __gpio_get_value(unsigned gpio)
>  }
>  EXPORT_SYMBOL_GPL(__gpio_get_value);
> 
> +u32 gpio_group_get_raw(const struct gpio_group* group)
> +{
> +	struct gpio_chip *chip;
> +	chip = gpio_to_chip(group->gpios[0]);
> +	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_get_raw);
> +
>  /**
>   * __gpio_set_value() - assign a gpio's value
>   * @gpio: gpio whose value will be assigned
> @@ -1309,6 +2169,13 @@ void __gpio_set_value(unsigned gpio, int value)
>  }
>  EXPORT_SYMBOL_GPL(__gpio_set_value);
> 
> +void gpio_group_set_raw(const struct gpio_group *group, u32 value)
> +{
> +	struct gpio_chip *chip;
> +	chip = gpio_to_chip(group->gpios[0]);
> +	chip->set_multi(chip, group->mask, value | group->mask);
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_set_raw);
>  /**
>   * __gpio_cansleep() - report whether gpio value access will sleep
>   * @gpio: gpio in question
> @@ -1328,6 +2195,11 @@ int __gpio_cansleep(unsigned gpio)
>  }
>  EXPORT_SYMBOL_GPL(__gpio_cansleep);
> 
> +int gpio_group_cansleep(const struct gpio_group *group)
> +{
> +	return __gpio_cansleep(group->gpios[0]);
> +}
> +
>  /**
>   * __gpio_to_irq() - return the IRQ corresponding to a GPIO
>   * @gpio: gpio whose IRQ will be returned (already requested)
> @@ -1362,6 +2234,15 @@ int gpio_get_value_cansleep(unsigned gpio)
>  }
>  EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);
> 
> +u32 gpio_group_get_raw_cansleep(const struct gpio_group *group)
> +{
> +	struct gpio_chip *chip;
> +	might_sleep_if(extra_checks);
> +	chip = gpio_to_chip(group->gpios[0]);
> +	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_get_raw_cansleep);
> +
>  void gpio_set_value_cansleep(unsigned gpio, int value)
>  {
>  	struct gpio_chip	*chip;
> @@ -1372,6 +2253,14 @@ void gpio_set_value_cansleep(unsigned gpio, int value)
>  }
>  EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
> 
> +void gpio_group_set_raw_cansleep(const struct gpio_group *group, u32 value)
> +{
> +	struct gpio_chip *chip;
> +	might_sleep_if(extra_checks);
> +	chip = gpio_to_chip(group->gpios[0]);
> +	chip->set_multi(chip, group->mask, value & group->mask);
> +}
> +EXPORT_SYMBOL_GPL(gpio_group_set_raw_cansleep);
> 
>  #ifdef CONFIG_DEBUG_FS
> 
> @@ -1381,18 +2270,24 @@ static void gpiolib_dbg_show(struct seq_file
> *s, struct gpio_chip *chip)
>  	unsigned		gpio = chip->base;
>  	struct gpio_desc	*gdesc = &gpio_desc[gpio];
>  	int			is_out;
> +	int			is_open;
> +	int     is_group;
> 
>  	for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
>  		if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
>  			continue;
> 
>  		is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
> -		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s",
> -			gpio, gdesc->label,
> -			is_out ? "out" : "in ",
> -			chip->get
> +		is_open = test_bit(FLAG_OPEN_DRAIN, &gdesc->flags);
> +		is_group = (gdesc->group != NULL);
> +		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s",
> +				gpio, gdesc->label,
> +				is_out ? "out" : "in ",
> +				chip->get
>  				? (chip->get(chip, i) ? "hi" : "lo")
> -				: "?  ");
> +				: "?  ",
> +				is_open ? "open" : "act ",
> +				is_group ? "grp" : "pin");
> 
>  		if (!is_out) {
>  			int		irq = gpio_to_irq(gpio);
> @@ -1408,32 +2303,32 @@ static void gpiolib_dbg_show(struct seq_file
> *s, struct gpio_chip *chip)
>  				char *trigger;
> 
>  				switch (desc->status & IRQ_TYPE_SENSE_MASK) {
> -				case IRQ_TYPE_NONE:
> -					trigger = "(default)";
> -					break;
> -				case IRQ_TYPE_EDGE_FALLING:
> -					trigger = "edge-falling";
> -					break;
> -				case IRQ_TYPE_EDGE_RISING:
> -					trigger = "edge-rising";
> -					break;
> -				case IRQ_TYPE_EDGE_BOTH:
> -					trigger = "edge-both";
> -					break;
> -				case IRQ_TYPE_LEVEL_HIGH:
> -					trigger = "level-high";
> -					break;
> -				case IRQ_TYPE_LEVEL_LOW:
> -					trigger = "level-low";
> -					break;
> -				default:
> -					trigger = "?trigger?";
> -					break;
> +					case IRQ_TYPE_NONE:
> +						trigger = "(default)";
> +						break;
> +					case IRQ_TYPE_EDGE_FALLING:
> +						trigger = "edge-falling";
> +						break;
> +					case IRQ_TYPE_EDGE_RISING:
> +						trigger = "edge-rising";
> +						break;
> +					case IRQ_TYPE_EDGE_BOTH:
> +						trigger = "edge-both";
> +						break;
> +					case IRQ_TYPE_LEVEL_HIGH:
> +						trigger = "level-high";
> +						break;
> +					case IRQ_TYPE_LEVEL_LOW:
> +						trigger = "level-low";
> +						break;
> +					default:
> +						trigger = "?trigger?";
> +						break;
>  				}
> 
>  				seq_printf(s, " irq-%d %s%s",
> -					irq, trigger,
> -					(desc->status & IRQ_WAKEUP)
> +						irq, trigger,
> +						(desc->status & IRQ_WAKEUP)
>  						? " wakeup" : "");
>  			}
>  		}
> diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c
> index 29f19ce..a51d9ad 100644
> --- a/drivers/gpio/pcf857x.c
> +++ b/drivers/gpio/pcf857x.c
> @@ -79,6 +79,20 @@ static int pcf857x_input8(struct gpio_chip *chip,
> unsigned offset)
>  	return status;
>  }
> 
> +static int pcf857x_input8_multi(struct gpio_chip *chip, u32 mask)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	int		status;
> +
> +	mutex_lock(&gpio->lock);
> +	gpio->out |= (u16)mask;
> +	status = i2c_smbus_write_byte(gpio->client, gpio->out);
> +	mutex_unlock(&gpio->lock);
> +
> +	return status;
> +}
> +
> +
>  static int pcf857x_get8(struct gpio_chip *chip, unsigned offset)
>  {
>  	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> @@ -88,6 +102,15 @@ static int pcf857x_get8(struct gpio_chip *chip,
> unsigned offset)
>  	return (value < 0) ? 0 : (value & (1 << offset));
>  }
> 
> +static u32 pcf857x_get8_multi(struct gpio_chip *chip, u32 mask)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	u32		value;
> +
> +	value = i2c_smbus_read_byte(gpio->client);
> +	return (value < 0) ? 0 : (value & mask);
> +}
> +
>  static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value)
>  {
>  	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> @@ -105,6 +128,25 @@ static int pcf857x_output8(struct gpio_chip
> *chip, unsigned offset, int value)
>  	return status;
>  }
> 
> +static int pcf857x_output8_multi(struct gpio_chip *chip, u32 mask, u32 value)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	int		status;
> +
> +	mutex_lock(&gpio->lock);
> +	gpio->out &= ~mask;
> +	gpio->out |= value;
> +	status = i2c_smbus_write_byte(gpio->client, gpio->out);
> +	mutex_unlock(&gpio->lock);
> +
> +	return status;
> +}
> +
> +static void pcf857x_set8_multi(struct gpio_chip *chip, u32 mask, u32 value)
> +{
> +	pcf857x_output8_multi(chip, mask, value);
> +}
> +
>  static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
>  {
>  	pcf857x_output8(chip, offset, value);
> @@ -147,6 +189,19 @@ static int pcf857x_input16(struct gpio_chip
> *chip, unsigned offset)
>  	return status;
>  }
> 
> +static int pcf857x_input16_multi(struct gpio_chip *chip, u32 mask)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	int		status;
> +
> +	mutex_lock(&gpio->lock);
> +	gpio->out |= mask;
> +	status = i2c_write_le16(gpio->client, gpio->out);
> +	mutex_unlock(&gpio->lock);
> +
> +	return status;
> +}
> +
>  static int pcf857x_get16(struct gpio_chip *chip, unsigned offset)
>  {
>  	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> @@ -156,6 +211,15 @@ static int pcf857x_get16(struct gpio_chip *chip,
> unsigned offset)
>  	return (value < 0) ? 0 : (value & (1 << offset));
>  }
> 
> +static u32 pcf857x_get16_multi(struct gpio_chip *chip, u32 mask)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	u32		value;
> +
> +	value = i2c_read_le16(gpio->client);
> +	return (value < 0) ? 0 : (value & mask);
> +}
> +
>  static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value)
>  {
>  	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> @@ -173,11 +237,30 @@ static int pcf857x_output16(struct gpio_chip
> *chip, unsigned offset, int value)
>  	return status;
>  }
> 
> +static int pcf857x_output16_multi(struct gpio_chip *chip, u32 mask, u32 value)
> +{
> +	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
> +	int		status;
> +
> +	mutex_lock(&gpio->lock);
> +	gpio->out &= ~mask;
> +	gpio->out |= value;
> +	status = i2c_write_le16(gpio->client, gpio->out);
> +	mutex_unlock(&gpio->lock);
> +
> +	return status;
> +}
> +
>  static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value)
>  {
>  	pcf857x_output16(chip, offset, value);
>  }
> 
> +static void pcf857x_set16_multi(struct gpio_chip *chip, u32 mask, u32 value)
> +{
> +	pcf857x_output16_multi(chip, mask, value);
> +}
> +
>  /*-------------------------------------------------------------------------*/
> 
>  static int pcf857x_probe(struct i2c_client *client,
> @@ -190,7 +273,6 @@ static int pcf857x_probe(struct i2c_client *client,
>  	pdata = client->dev.platform_data;
>  	if (!pdata) {
>  		dev_dbg(&client->dev, "no platform data\n");
> -		return -EINVAL;
>  	}
> 
>  	/* Allocate, initialize, and register this gpio_chip. */
> @@ -200,7 +282,7 @@ static int pcf857x_probe(struct i2c_client *client,
> 
>  	mutex_init(&gpio->lock);
> 
> -	gpio->chip.base = pdata->gpio_base;
> +	gpio->chip.base = pdata ? pdata->gpio_base : -1;
>  	gpio->chip.can_sleep = 1;
>  	gpio->chip.dev = &client->dev;
>  	gpio->chip.owner = THIS_MODULE;
> @@ -218,10 +300,14 @@ static int pcf857x_probe(struct i2c_client *client,
>  	 */
>  	gpio->chip.ngpio = id->driver_data;
>  	if (gpio->chip.ngpio == 8) {
> +		gpio->chip.direction_input_multi = pcf857x_input8_multi;
>  		gpio->chip.direction_input = pcf857x_input8;
>  		gpio->chip.get = pcf857x_get8;
> +		gpio->chip.get_multi = pcf857x_get8_multi;
>  		gpio->chip.direction_output = pcf857x_output8;
> +		gpio->chip.direction_output_multi = pcf857x_output8_multi;
>  		gpio->chip.set = pcf857x_set8;
> +		gpio->chip.set_multi = pcf857x_set8_multi;
> 
>  		if (!i2c_check_functionality(client->adapter,
>  				I2C_FUNC_SMBUS_BYTE))
> @@ -239,9 +325,13 @@ static int pcf857x_probe(struct i2c_client *client,
>  	 */
>  	} else if (gpio->chip.ngpio == 16) {
>  		gpio->chip.direction_input = pcf857x_input16;
> +		gpio->chip.direction_input_multi = pcf857x_input16_multi;
>  		gpio->chip.get = pcf857x_get16;
> +		gpio->chip.get_multi = pcf857x_get16_multi;
>  		gpio->chip.direction_output = pcf857x_output16;
> +		gpio->chip.direction_output_multi = pcf857x_output16_multi;
>  		gpio->chip.set = pcf857x_set16;
> +		gpio->chip.set_multi = pcf857x_set16_multi;
> 
>  		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
>  			status = -EIO;
> @@ -278,7 +368,7 @@ static int pcf857x_probe(struct i2c_client *client,
>  	 * to zero, our software copy of the "latch" then matches the chip's
>  	 * all-ones reset state.  Otherwise it flags pins to be driven low.
>  	 */
> -	gpio->out = ~pdata->n_latch;
> +	gpio->out = pdata ? ~pdata->n_latch : ~0;
> 
>  	status = gpiochip_add(&gpio->chip);
>  	if (status < 0)
> @@ -299,7 +389,7 @@ static int pcf857x_probe(struct i2c_client *client,
>  	/* Let platform code set up the GPIOs and their users.
>  	 * Now is the first time anyone could use them.
>  	 */
> -	if (pdata->setup) {
> +	if (pdata && pdata->setup) {
>  		status = pdata->setup(client,
>  				gpio->chip.base, gpio->chip.ngpio,
>  				pdata->context);
> @@ -310,7 +400,7 @@ static int pcf857x_probe(struct i2c_client *client,
>  	return 0;
> 
>  fail:
> -	dev_dbg(&client->dev, "probe error %d for '%s'\n",
> +	dev_err(&client->dev, "probe error %d for '%s'\n",
>  			status, client->name);
>  	kfree(gpio);
>  	return status;
> @@ -322,7 +412,7 @@ static int pcf857x_remove(struct i2c_client *client)
>  	struct pcf857x			*gpio = i2c_get_clientdata(client);
>  	int				status = 0;
> 
> -	if (pdata->teardown) {
> +	if (pdata && pdata->teardown) {
>  		status = pdata->teardown(client,
>  				gpio->chip.base, gpio->chip.ngpio,
>  				pdata->context);
> diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
> index 66d6106..ed2407f 100644
> --- a/include/asm-generic/gpio.h
> +++ b/include/asm-generic/gpio.h
> @@ -30,6 +30,7 @@ static inline int gpio_is_valid(int number)
> 
>  struct seq_file;
>  struct module;
> +struct device_node;
> 
>  /**
>   * struct gpio_chip - abstract a GPIO controller
> @@ -89,6 +90,22 @@ struct gpio_chip {
>  						unsigned offset, int value);
>  	void			(*set)(struct gpio_chip *chip,
>  						unsigned offset, int value);
> +	int			(*set_opendrain)(struct gpio_chip *chip,
> +						unsigned offset, int value);
> +	int			(*set_opendrain_multi)(struct gpio_chip *chip,
> +						u32 mask, u32 value);
> +
> +	int			(*request_multi)(struct gpio_chip *chip,
> +						u32 mask);
> +	void			(*free_multi)(struct gpio_chip *chip,
> +						u32 mask);
> +
> +	int			(*direction_input_multi)(struct gpio_chip *chip,
> +						u32 mask);
> +	int			(*direction_output_multi)(struct gpio_chip *chip,
> +						u32 mask, u32 value);
> +	void			(*set_multi)(struct gpio_chip *chip, u32 mask, u32 value);
> +	u32			(*get_multi)(struct gpio_chip *chip, u32 mask);
> 
>  	int			(*to_irq)(struct gpio_chip *chip,
>  						unsigned offset);
> @@ -100,6 +117,23 @@ struct gpio_chip {
>  	char			**names;
>  	unsigned		can_sleep:1;
>  	unsigned		exported:1;
> +
> +#if defined(CONFIG_OF_GPIO)
> +	/*
> +	 * If CONFIG_OF is enabled, then all GPIO controllers described in the
> +	 * device tree automatically may have an OF translation
> +	 */
> +	struct device_node *of_node;
> +	int of_gpio_n_cells;
> +	int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
> +		        const void *gpio_spec, u32 *flags);
> +#endif
> +};
> +
> +struct gpio_group
> +{
> +	unsigned gpios[32];
> +	u32 mask;
>  };
> 
>  extern const char *gpiochip_is_requested(struct gpio_chip *chip,
> @@ -109,6 +143,9 @@ extern int __must_check gpiochip_reserve(int
> start, int ngpio);
>  /* add/remove chips */
>  extern int gpiochip_add(struct gpio_chip *chip);
>  extern int __must_check gpiochip_remove(struct gpio_chip *chip);
> +extern struct gpio_chip *gpiochip_find(void *data,
> +					int (*match)(struct gpio_chip *chip,
> +						     void *data));
> 
> 
>  /* Always use the library code for GPIO management calls,
> @@ -120,9 +157,28 @@ extern void gpio_free(unsigned gpio);
>  extern int gpio_direction_input(unsigned gpio);
>  extern int gpio_direction_output(unsigned gpio, int value);
> 
> +extern int gpio_set_opendrain(unsigned gpio, int value);
> +
>  extern int gpio_get_value_cansleep(unsigned gpio);
>  extern void gpio_set_value_cansleep(unsigned gpio, int value);
> 
> +/*
> + * Handling of gpio groups
> + */
> +extern struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
> +    const char *label);
> +extern void gpio_group_free(struct gpio_group* group);
> +
> +extern int gpio_group_direction_input(const struct gpio_group *group);
> +extern int gpio_group_direction_output(const struct gpio_group
> *group, u32 value);
> +
> +extern u32 gpio_group_get_raw_cansleep(const struct gpio_group *group);
> +extern void gpio_group_set_raw_cansleep(const struct gpio_group
> *group, u32 value);
> +
> +extern int gpio_group_set_opendrain(struct gpio_group* group, u32 value);
> +
> +u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw);
> +u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value);
> 
>  /* A platform's <asm/gpio.h> code may want to inline the I/O calls when
>   * the GPIO is constant and refers to some always-present controller,
> @@ -135,6 +191,11 @@ extern int __gpio_cansleep(unsigned gpio);
> 
>  extern int __gpio_to_irq(unsigned gpio);
> 
> +extern u32 gpio_group_get_raw(const struct gpio_group *group);
> +extern void gpio_group_set_raw(const struct gpio_group *group, u32 value);
> +
> +extern int gpio_group_cansleep(const struct gpio_group *group);
> +
>  #ifdef CONFIG_GPIO_SYSFS
> 
>  /*
> @@ -146,6 +207,12 @@ extern int gpio_export_link(struct device *dev,
> const char *name,
>  			unsigned gpio);
>  extern void gpio_unexport(unsigned gpio);
> 
> +extern int gpio_group_export(struct gpio_group *group,
> +    bool direction_may_change);
> +extern int gpio_group_export_link(struct device *dev, const char *name,
> +    struct gpio_group *group);
> +extern void gpio_group_unexport(struct gpio_group *group);
> +
>  #endif	/* CONFIG_GPIO_SYSFS */
> 
>  #else	/* !CONFIG_HAVE_GPIO_LIB */
> diff --git a/include/linux/gpio-export.h b/include/linux/gpio-export.h
> new file mode 100644
> index 0000000..712e9ff
> --- /dev/null
> +++ b/include/linux/gpio-export.h
> @@ -0,0 +1,64 @@
> +/*    Structures for passing gpio settings to drivers/gpio/gpio-export.c
> + *    201103 steene99
> + */
> +#ifndef __LINUX_GPIO_EXPORT_H
> +#define __LINUX_GPIO_EXPORT_H
> +
> +enum gpio_direction
> +{
> +	GPIO_INPUT,
> +	GPIO_OUTPUT,
> +	GPIO_CHANGE,
> +	GPIO_OUTPUT_KEEP,
> +};
> +
> +struct mp_gpio_line
> +{
> +	int gpio_num;
> +	int active_low;
> +	int open_drain;
> +};
> +
> +#define GPIO_PIN(_nr, _active_low, _open_drain) \
> +{ \
> +	.gpio_num = _nr, \
> +	.active_low = _active_low, \
> +	.open_drain = _open_drain, \
> +}
> +
> +#define SIMPLE_GPIO_PIN(_name, _nr) \
> +static struct mp_gpio_line _name[] = \
> +{ \
> +	GPIO_PIN(_nr, 0, 0), \
> +}
> +
> +#define MAX_GPIO_LABEL_SIZE 32
> +
> +struct mp_gpio_platform_data
> +{
> +	int gpio_count;
> +	enum gpio_direction direction;
> +	u32 initialvalue;  // value
> +	struct mp_gpio_line *gpio_data;
> +	char desc[MAX_GPIO_LABEL_SIZE];
> +};
> +
> +#define DEFINE_GPIO_GROUP(_name, _count, _direction, _initial, _pin, _desc) \
> +	static struct mp_gpio_platform_data _name = { \
> +		.gpio_count = _count, \
> +		.direction = _direction, \
> +		.initialvalue = _initial, \
> +		.gpio_data = _pin, \
> +		.desc=_desc, \
> +	}
> +
> +#define GPIO_PDEV(_id, _pdata) \
> +{ \
> +	.name = "gpio-export", \
> +	.id = _id, \
> +	.dev = { \
> +		.platform_data = &_pdata, \
> +	}, \
> +}
> +
> +#endif //__LINUX_GPIO_EXPORT_H
> diff --git a/include/linux/gpio.h b/include/linux/gpio.h
> index 059bd18..f82edad 100644
> --- a/include/linux/gpio.h
> +++ b/include/linux/gpio.h
> @@ -13,6 +13,7 @@
>  #include <linux/errno.h>
> 
>  struct device;
> +struct gpio_chip;
> 
>  /*
>   * Some platforms don't support the GPIO programming interface.
> @@ -33,6 +34,11 @@ static inline int gpio_request(unsigned gpio, const
> char *label)
>  	return -ENOSYS;
>  }
> 
> +static inline int gpio_group_request(unsigned *gpios, int ngpios,
> const char *label)
> +{
> +	return ERR_PTR(-ENOSYS);
> +}
> +
>  static inline void gpio_free(unsigned gpio)
>  {
>  	might_sleep();
> @@ -41,16 +47,32 @@ static inline void gpio_free(unsigned gpio)
>  	WARN_ON(1);
>  }
> 
> +static inline void gpio_group_free(struct gpio_group *group)
> +{
> +	might_sleep();
> +	WARN_ON(1);
> +}
> +
>  static inline int gpio_direction_input(unsigned gpio)
>  {
>  	return -ENOSYS;
>  }
> 
> +static inline int gpio_group_direction_input(const struct gpio_group *group)
> +{
> +	return -ENOSYS;
> +}
> +
>  static inline int gpio_direction_output(unsigned gpio, int value)
>  {
>  	return -ENOSYS;
>  }
> 
> +static inline int gpio_group_direction_output(const struct gpio_group
> *group, u32 value)
> +{
> +	return -ENOSYS;
> +}
> +
>  static inline int gpio_get_value(unsigned gpio)
>  {
>  	/* GPIO can never have been requested or set as {in,out}put */
> @@ -58,12 +80,29 @@ static inline int gpio_get_value(unsigned gpio)
>  	return 0;
>  }
> 
> +static inline int gpio_group_get_raw(const struct gpio_group *group)
> +{
> +	WARN_ON(1);
> +	return 0;
> +}
> +
>  static inline void gpio_set_value(unsigned gpio, int value)
>  {
>  	/* GPIO can never have been requested or set as output */
>  	WARN_ON(1);
>  }
> 
> +static inline void gpio_group_set_raw(const struct gpio_group *group,
> u32 value)
> +{
> +	WARN_ON(1);
> +}
> +
> +static inline int gpio_set_opendrain(unsigned gpio, int value)
> +{
> +	WARN_ON(1);
> +	return -ENOSYS;
> +}
> +
>  static inline int gpio_cansleep(unsigned gpio)
>  {
>  	/* GPIO can never have been requested or set as {in,out}put */
> @@ -71,6 +110,12 @@ static inline int gpio_cansleep(unsigned gpio)
>  	return 0;
>  }
> 
> +static inline int gpio_group_cansleep(const struct gpio_group *group)
> +{
> +	WARN_ON(1);
> +	return 0;
> +}
> +
>  static inline int gpio_get_value_cansleep(unsigned gpio)
>  {
>  	/* GPIO can never have been requested or set as {in,out}put */
> @@ -78,12 +123,23 @@ static inline int gpio_get_value_cansleep(unsigned gpio)
>  	return 0;
>  }
> 
> +static inline int gpio_group_get_raw_cansleep(const struct gpio_group *group)
> +{
> +	WARN_ON(1);
> +	return 0;
> +}
> +
>  static inline void gpio_set_value_cansleep(unsigned gpio, int value)
>  {
>  	/* GPIO can never have been requested or set as output */
>  	WARN_ON(1);
>  }
> 
> +static inline void gpio_group_set_raw_cansleep(const struct
> gpio_group *group, u32 value)
> +{
> +	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 */
> @@ -91,6 +147,12 @@ static inline int gpio_export(unsigned gpio, bool
> direction_may_change)
>  	return -EINVAL;
>  }
> 
> +static inline int gpio_group_export(struct gpio_group *group, bool
> direction_may_change)
> +{
> +	WARN_ON(1);
> +	return -EINVAL;
> +}
> +
>  static inline int gpio_export_link(struct device *dev, const char *name,
>  				unsigned gpio)
>  {
> @@ -99,6 +161,12 @@ static inline int gpio_export_link(struct device
> *dev, const char *name,
>  	return -EINVAL;
>  }
> 
> +static inline int gpio_group_export_link(struct device *dev, const char *name,
> +    struct gpio_group *group)
> +{
> +	WARN_ON(1);
> +	return -EINVAL;
> +}
> 
>  static inline void gpio_unexport(unsigned gpio)
>  {
> @@ -106,6 +174,11 @@ static inline void gpio_unexport(unsigned gpio)
>  	WARN_ON(1);
>  }
> 
> +static inline void gpio_group_unexport(struct gpio_group *group)
> +{
> +	WARN_ON(1);
> +}
> +
>  static inline int gpio_to_irq(unsigned gpio)
>  {
>  	/* GPIO can never have been requested or set as input */
> @@ -122,4 +195,14 @@ static inline int irq_to_gpio(unsigned irq)
> 
>  #endif
> 
> +static inline int gpio_direction_output_keep(int gpio)
> +{
> +	return gpio_direction_output(gpio, gpio_get_value_cansleep(gpio));
> +}
> +
> +static inline int gpio_group_direction_output_keep(const struct
> gpio_group *group)
> +{
> +	return gpio_group_direction_output(group, gpio_group_get_raw_cansleep(group));
> +}
> +
>  #endif /* __LINUX_GPIO_H */
> -- 
> 1.7.5.4

-- 
Grant Likely, B.Sc, P.Eng.
Secret Lab Technologies, Ltd.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
  2013-01-15 11:51   ` Roland Stigge
@ 2013-02-15 14:47     ` Grant Likely
  -1 siblings, 0 replies; 32+ messages in thread
From: Grant Likely @ 2013-02-15 14:47 UTC (permalink / raw)
  To: Roland Stigge, gregkh, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, highguy, broonie,
	daniel-gl, rmallon, sr, wg, tru, mark.rutland
  Cc: Roland Stigge

On Tue, 15 Jan 2013 12:51:51 +0100, Roland Stigge <stigge@antcom.de> wrote:
> 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. This is done by abstracting GPIOs to an n-bit word:
> Once requested, it provides access to a group of GPIOs which can range over
> multiple GPIO chips.
> 
> Signed-off-by: Roland Stigge <stigge@antcom.de>
> ---
> 
>  Documentation/gpio.txt     |   58 +++++++++++
>  drivers/gpio/gpiolib.c     |  227 +++++++++++++++++++++++++++++++++++++++++++++
>  include/asm-generic/gpio.h |   17 +++
>  include/linux/gpio.h       |   97 +++++++++++++++++++
>  4 files changed, 399 insertions(+)
> 
> --- linux-2.6.orig/Documentation/gpio.txt
> +++ linux-2.6/Documentation/gpio.txt
> @@ -481,6 +481,64 @@ exact name string of pinctrl device has
>  argument to this routine.

Hi Roland,

The first thing that jumps out at me on this is that it really is two
separate concepts implemented in one patch.
1) allow individual chips to expose a block API for that single controller.
2) creating a global block gpio interface for multiple arbitrary
groupings of chips

The first is relatively noncontroversial. It is easy to implement and
there are there are real performance issues that it addresses. If you
split that out as a separate patch it is something I think I can merge.
I do have some minor comments on this feature, but I'll put the details
below.

The second I'm not that thrilled with, or at least I think the
implementation is more complex than it needs to be. The big problem is
that it tries to abstract the fact that GPIOs may or may not be on the
same controller, and in doing so it has to do a bunch of housekeeping to
map 'virtual' gpio numbers to real gpios. I recognized that the feature
is needed to take advantage of gpiochip block access, but I'd like to
suggest a different implementation.

Instead of keeping track of separate block_gpio chip references, how
about an API that consolidates GPIO operations. For example (rough
sketch, and using the new gpiodesc infrastructure):

struct gpiocmd {
	struct gpio_desc *gpio;
	int data;
}

int gpio_set_sequence(struct gpiocmd *gpiocmd, int count)
{
	struct gpio_chip *gc = GPIO_DESC_TO_GPIOCHIP(gpiocmd->gpio);
	int bit = GPIO_DESC_TO_BIT(gpiocmd->gpio);
	unsigned long data, mask;

	/*
	 * Consolidate and execute the gpio commands. A little naive,
	 * but you get the idea.
	 *
	 * GPIO_DESC_TO_GPIOCHIP() and GPIO_DESC_TO_BIT() are fictions
	 * at the moment; something will need to be implemented here.
	 */
	mask = 1 << bit;
	data = gpiocmd->data << bit;
	for (i = 1; i < count; i++, gpiocmd++) {
		struct gpio_chip *nextgc = GPIO_DESC_TO_GPIOCHIP(gpiocmd->gpio):
		bit = GPIO_DESC_TO_BIT(gpiocmd->gpio);

		/* Consolidate if same gpio_chip and go to next iteration */
		if (gc == nextgc) {
			mask &= 1 << bit
			data &= gpiocmd->data << bit
			continue;
		}

		gc->set_block(gc, mask, data);

		gc = nextgc;
		mask = 1 << bit;
		data = gpiocmd->data << bit;
	}

	/* Last one because the loop alwasy exits with at least one more
	 * thing to do */
	gc->set_block(gc, mask, data);
}

And that's it. It maintains the abstraction that GPIOs are separate and
that assumptions cannot be made about how there are wired together, but
still allows any driver to take advantage of speedups with consolidating
operations. Drivers also get to use the same handles they are currently
using instead of a separate gpio_block namespace which means it
interworks nicely with the existing API.

It also should be lighter weight when it comes to speed of processing.
Normally I'm not too worried about that, but when it comes to GPIOs and
bitbanging it can end up having a really big cost.

Naturally the thing I hacked together above is just a draft. It can
certainly be refined. It might make sense for it to be a bitbang
statemachine that can handle both set, get and barrier/delay operations.
That might actually be simpler and better than having separate set & get
sequences with callers handing the bitbanging.

For performance, it might make sense to separate the consolidation pass
from the execution pass.

g.


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
@ 2013-02-15 14:47     ` Grant Likely
  0 siblings, 0 replies; 32+ messages in thread
From: Grant Likely @ 2013-02-15 14:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 15 Jan 2013 12:51:51 +0100, Roland Stigge <stigge@antcom.de> wrote:
> 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. This is done by abstracting GPIOs to an n-bit word:
> Once requested, it provides access to a group of GPIOs which can range over
> multiple GPIO chips.
> 
> Signed-off-by: Roland Stigge <stigge@antcom.de>
> ---
> 
>  Documentation/gpio.txt     |   58 +++++++++++
>  drivers/gpio/gpiolib.c     |  227 +++++++++++++++++++++++++++++++++++++++++++++
>  include/asm-generic/gpio.h |   17 +++
>  include/linux/gpio.h       |   97 +++++++++++++++++++
>  4 files changed, 399 insertions(+)
> 
> --- linux-2.6.orig/Documentation/gpio.txt
> +++ linux-2.6/Documentation/gpio.txt
> @@ -481,6 +481,64 @@ exact name string of pinctrl device has
>  argument to this routine.

Hi Roland,

The first thing that jumps out at me on this is that it really is two
separate concepts implemented in one patch.
1) allow individual chips to expose a block API for that single controller.
2) creating a global block gpio interface for multiple arbitrary
groupings of chips

The first is relatively noncontroversial. It is easy to implement and
there are there are real performance issues that it addresses. If you
split that out as a separate patch it is something I think I can merge.
I do have some minor comments on this feature, but I'll put the details
below.

The second I'm not that thrilled with, or at least I think the
implementation is more complex than it needs to be. The big problem is
that it tries to abstract the fact that GPIOs may or may not be on the
same controller, and in doing so it has to do a bunch of housekeeping to
map 'virtual' gpio numbers to real gpios. I recognized that the feature
is needed to take advantage of gpiochip block access, but I'd like to
suggest a different implementation.

Instead of keeping track of separate block_gpio chip references, how
about an API that consolidates GPIO operations. For example (rough
sketch, and using the new gpiodesc infrastructure):

struct gpiocmd {
	struct gpio_desc *gpio;
	int data;
}

int gpio_set_sequence(struct gpiocmd *gpiocmd, int count)
{
	struct gpio_chip *gc = GPIO_DESC_TO_GPIOCHIP(gpiocmd->gpio);
	int bit = GPIO_DESC_TO_BIT(gpiocmd->gpio);
	unsigned long data, mask;

	/*
	 * Consolidate and execute the gpio commands. A little naive,
	 * but you get the idea.
	 *
	 * GPIO_DESC_TO_GPIOCHIP() and GPIO_DESC_TO_BIT() are fictions
	 * at the moment; something will need to be implemented here.
	 */
	mask = 1 << bit;
	data = gpiocmd->data << bit;
	for (i = 1; i < count; i++, gpiocmd++) {
		struct gpio_chip *nextgc = GPIO_DESC_TO_GPIOCHIP(gpiocmd->gpio):
		bit = GPIO_DESC_TO_BIT(gpiocmd->gpio);

		/* Consolidate if same gpio_chip and go to next iteration */
		if (gc == nextgc) {
			mask &= 1 << bit
			data &= gpiocmd->data << bit
			continue;
		}

		gc->set_block(gc, mask, data);

		gc = nextgc;
		mask = 1 << bit;
		data = gpiocmd->data << bit;
	}

	/* Last one because the loop alwasy exits with@least one more
	 * thing to do */
	gc->set_block(gc, mask, data);
}

And that's it. It maintains the abstraction that GPIOs are separate and
that assumptions cannot be made about how there are wired together, but
still allows any driver to take advantage of speedups with consolidating
operations. Drivers also get to use the same handles they are currently
using instead of a separate gpio_block namespace which means it
interworks nicely with the existing API.

It also should be lighter weight when it comes to speed of processing.
Normally I'm not too worried about that, but when it comes to GPIOs and
bitbanging it can end up having a really big cost.

Naturally the thing I hacked together above is just a draft. It can
certainly be refined. It might make sense for it to be a bitbang
statemachine that can handle both set, get and barrier/delay operations.
That might actually be simpler and better than having separate set & get
sequences with callers handing the bitbanging.

For performance, it might make sense to separate the consolidation pass
from the execution pass.

g.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
  2013-01-15 11:51   ` Roland Stigge
@ 2013-02-15 16:34     ` Grant Likely
  -1 siblings, 0 replies; 32+ messages in thread
From: Grant Likely @ 2013-02-15 16:34 UTC (permalink / raw)
  To: Roland Stigge, gregkh, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, highguy, broonie,
	daniel-gl, rmallon, sr, wg, tru, mark.rutland
  Cc: Roland Stigge

On Tue, 15 Jan 2013 12:51:51 +0100, Roland Stigge <stigge@antcom.de> wrote:
> 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. This is done by abstracting GPIOs to an n-bit word:
> Once requested, it provides access to a group of GPIOs which can range over
> multiple GPIO chips.
> 
> Signed-off-by: Roland Stigge <stigge@antcom.de>

Oops, I forgot to comment on the block API bits. Comments below...

> --- linux-2.6.orig/drivers/gpio/gpiolib.c
> +++ linux-2.6/drivers/gpio/gpiolib.c
> --- linux-2.6.orig/include/asm-generic/gpio.h
> +++ linux-2.6/include/asm-generic/gpio.h
> @@ -44,6 +44,7 @@ static inline bool gpio_is_valid(int num
>  
>  struct device;
>  struct gpio;
> +struct gpio_block;
>  struct seq_file;
>  struct module;
>  struct device_node;
> @@ -109,6 +110,8 @@ struct gpio_chip {
>  						unsigned offset);
>  	int			(*get)(struct gpio_chip *chip,
>  						unsigned offset);
> +	unsigned long		(*get_block)(struct gpio_chip *chip,
> +					     unsigned long mask);
>  	int			(*direction_output)(struct gpio_chip *chip,
>  						unsigned offset, int value);
>  	int			(*set_debounce)(struct gpio_chip *chip,
> @@ -116,6 +119,9 @@ struct gpio_chip {
>  
>  	void			(*set)(struct gpio_chip *chip,
>  						unsigned offset, int value);
> +	void			(*set_block)(struct gpio_chip *chip,
> +					     unsigned long mask,
> +					     unsigned long values);
>  
>  	int			(*to_irq)(struct gpio_chip *chip,
>  						unsigned offset);

This is the core of the impact on gpio_chip drivers, so I'll restrict my
comments to this bit.

First to set the stage. The gpio_chip api is used by all types of GPIO
interfaces from on-chip MMIO (fast & atomic) to off-chip I2c/SPI/USB/etc
attached (slow and will sleep). For on-chip GPIO we want the API to get
out of the way as much as possible. Some systems choose fast on-chip
gpios to get as much speed as possible between bit flips. If there is
too much overhead in gpiolib, then they have to dump gpiolib entirely
and open-code it. There will always be a segment of uses that need to do
this, but I don't want to completely ignore highspeed gpiolib users.

For the slow chips, gpiolib overhead is far dwarfed by the serial bus
latency, so any work that can consolidate transfers has measurable
payoff.

The reason I bring this up even though consolidation calculation isn't
going to be done by the chip itself is that the format of the API is
going to affect how much work the chip needs to do[1]. If set and
direction are different operations, then some chips may not be able to
consolidate them into a single transfer. On the other end, some fast
gpio chips have separate set and clear registers that don't require
read/modify/write cycles, but we don't get the advantage of those if the
API is based on mask & value..... I am thinking/rambling out loud here.
Not everything *has* to be supported, but this is the thought process
I'm going through when looking at the API.

I can think of the following operations that the block API needs to
support for any given pin:

1) set to output and level high
2) set to output and level low
3) set to input (rely on open-collector for output)

Right there, a simple mask/value pair isn't going to do the trick
because using an open-collector state for output is quite common (see
the i2c gpio code for example). Adding a direction parameter would solve
that problem:

	void			(*set_block)(struct gpio_chip *chip,
					     unsigned long mask,
					     unsigned long direction,
					     unsigned long data);

Alternately, if open-collector support was moved inside gpiolib then
that problem goes away.

ugh... I've run out of time. I need to shut down for the day. Anyway,
there are my thoughts. I think the API you have would work fine, but
only if you also add open-collector support into the core. I'll let you
know if I think of anything else.

g.

---

[1] However, I've just had another thought. Maybe we're approaching this
problem from entirely the wrong way around. Instead of baking in a block
GPIO API to all of the chip drivers we could instead allow gpio_chips to
do lazy set/get. For fast chips this wouldn't matter, but a driver could
do multiple lazy set operations, and then one flush that pushes all the
updates out. Similarly, a driver could do an "update" on all the input
gpios that it has followed by lazy gets which just use the last cached
value. I kind of like the elegance of it and it doesn't have the
inherent (unsigned long) limitation of 32 or 64 bits that the block gpio
api imposes.

In this scenario, lazy get/set would just by the real get/set for
existing gpio_chip drivers, but enhanced drivers could replace the
immediate versions with lazy variants and the gpiolib core could take
care of setting and flushing when the non-lazy API gets called.

g.


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
@ 2013-02-15 16:34     ` Grant Likely
  0 siblings, 0 replies; 32+ messages in thread
From: Grant Likely @ 2013-02-15 16:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 15 Jan 2013 12:51:51 +0100, Roland Stigge <stigge@antcom.de> wrote:
> 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. This is done by abstracting GPIOs to an n-bit word:
> Once requested, it provides access to a group of GPIOs which can range over
> multiple GPIO chips.
> 
> Signed-off-by: Roland Stigge <stigge@antcom.de>

Oops, I forgot to comment on the block API bits. Comments below...

> --- linux-2.6.orig/drivers/gpio/gpiolib.c
> +++ linux-2.6/drivers/gpio/gpiolib.c
> --- linux-2.6.orig/include/asm-generic/gpio.h
> +++ linux-2.6/include/asm-generic/gpio.h
> @@ -44,6 +44,7 @@ static inline bool gpio_is_valid(int num
>  
>  struct device;
>  struct gpio;
> +struct gpio_block;
>  struct seq_file;
>  struct module;
>  struct device_node;
> @@ -109,6 +110,8 @@ struct gpio_chip {
>  						unsigned offset);
>  	int			(*get)(struct gpio_chip *chip,
>  						unsigned offset);
> +	unsigned long		(*get_block)(struct gpio_chip *chip,
> +					     unsigned long mask);
>  	int			(*direction_output)(struct gpio_chip *chip,
>  						unsigned offset, int value);
>  	int			(*set_debounce)(struct gpio_chip *chip,
> @@ -116,6 +119,9 @@ struct gpio_chip {
>  
>  	void			(*set)(struct gpio_chip *chip,
>  						unsigned offset, int value);
> +	void			(*set_block)(struct gpio_chip *chip,
> +					     unsigned long mask,
> +					     unsigned long values);
>  
>  	int			(*to_irq)(struct gpio_chip *chip,
>  						unsigned offset);

This is the core of the impact on gpio_chip drivers, so I'll restrict my
comments to this bit.

First to set the stage. The gpio_chip api is used by all types of GPIO
interfaces from on-chip MMIO (fast & atomic) to off-chip I2c/SPI/USB/etc
attached (slow and will sleep). For on-chip GPIO we want the API to get
out of the way as much as possible. Some systems choose fast on-chip
gpios to get as much speed as possible between bit flips. If there is
too much overhead in gpiolib, then they have to dump gpiolib entirely
and open-code it. There will always be a segment of uses that need to do
this, but I don't want to completely ignore highspeed gpiolib users.

For the slow chips, gpiolib overhead is far dwarfed by the serial bus
latency, so any work that can consolidate transfers has measurable
payoff.

The reason I bring this up even though consolidation calculation isn't
going to be done by the chip itself is that the format of the API is
going to affect how much work the chip needs to do[1]. If set and
direction are different operations, then some chips may not be able to
consolidate them into a single transfer. On the other end, some fast
gpio chips have separate set and clear registers that don't require
read/modify/write cycles, but we don't get the advantage of those if the
API is based on mask & value..... I am thinking/rambling out loud here.
Not everything *has* to be supported, but this is the thought process
I'm going through when looking at the API.

I can think of the following operations that the block API needs to
support for any given pin:

1) set to output and level high
2) set to output and level low
3) set to input (rely on open-collector for output)

Right there, a simple mask/value pair isn't going to do the trick
because using an open-collector state for output is quite common (see
the i2c gpio code for example). Adding a direction parameter would solve
that problem:

	void			(*set_block)(struct gpio_chip *chip,
					     unsigned long mask,
					     unsigned long direction,
					     unsigned long data);

Alternately, if open-collector support was moved inside gpiolib then
that problem goes away.

ugh... I've run out of time. I need to shut down for the day. Anyway,
there are my thoughts. I think the API you have would work fine, but
only if you also add open-collector support into the core. I'll let you
know if I think of anything else.

g.

---

[1] However, I've just had another thought. Maybe we're approaching this
problem from entirely the wrong way around. Instead of baking in a block
GPIO API to all of the chip drivers we could instead allow gpio_chips to
do lazy set/get. For fast chips this wouldn't matter, but a driver could
do multiple lazy set operations, and then one flush that pushes all the
updates out. Similarly, a driver could do an "update" on all the input
gpios that it has followed by lazy gets which just use the last cached
value. I kind of like the elegance of it and it doesn't have the
inherent (unsigned long) limitation of 32 or 64 bits that the block gpio
api imposes.

In this scenario, lazy get/set would just by the real get/set for
existing gpio_chip drivers, but enhanced drivers could replace the
immediate versions with lazy variants and the gpiolib core could take
care of setting and flushing when the non-lazy API gets called.

g.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH RESEND 2/6 v13] gpio: Add sysfs support to block GPIO API
  2013-01-15 11:51   ` Roland Stigge
@ 2013-02-15 21:50     ` Grant Likely
  -1 siblings, 0 replies; 32+ messages in thread
From: Grant Likely @ 2013-02-15 21:50 UTC (permalink / raw)
  To: Roland Stigge, gregkh, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, highguy, broonie,
	daniel-gl, rmallon, sr, wg, tru, mark.rutland
  Cc: Roland Stigge

On Tue, 15 Jan 2013 12:51:52 +0100, Roland Stigge <stigge@antcom.de> wrote:
> This patch adds sysfs support to the block GPIO API.
> 
> Signed-off-by: Roland Stigge <stigge@antcom.de>

Hi Roland.

I'm going to ignore this patch for the time being because I think my
comments on the first patch basically obsolete what is being done here.
If the a gpio sequence parser is used instead of a global block gpio api
is used, then a sysfs interface for blocks of GPIOs could be implemented
merely as a separate device driver that consumes a bunch of standard
GPIO references.

I would also suggest that an API that accepts a stream of commands
instead of a fixed u32 would be better for the userspace interface. When
I originally suggested doing a char device interface, I was expecting
the sysfs interface to be dropped entirely.

g.

> 
> ---
>  Documentation/ABI/testing/sysfs-gpio |   20 ++
>  drivers/gpio/gpiolib.c               |  252 ++++++++++++++++++++++++++++++++++-
>  include/asm-generic/gpio.h           |   11 +
>  include/linux/gpio.h                 |   15 ++
>  4 files changed, 297 insertions(+), 1 deletion(-)
> 
> --- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
> +++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
> @@ -25,3 +25,23 @@ Description:
>  	    /label ... (r/o) descriptive, not necessarily unique
>  	    /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
>  
> +What:		/sys/class/gpioblock/
> +Date:		October 2012
> +KernelVersion:	3.7
> +Contact:	Roland Stigge <stigge@antcom.de>
> +Description:
> +
> +  Block GPIO devices are visible in sysfs as soon as they are registered
> +  (e.g. via devicetree definition). For actual I/O use, their "exported"
> +  boolean attribute must be set to "1". Then, the attribute "values" is
> +  created and at the same time, the GPIOs in the block are requested for
> +  exclusive use by sysfs.
> +
> +    /sys/class/gpioblock
> +	/BLOCKNAME ... for each GPIO block name
> +	    /ngpio ... (r/o) number of GPIOs in this group
> +	    /exported ... sysfs export state of this group (0, 1)
> +	    /value ... current value as 32 or 64 bit integer in hex
> +                       (only available if /exported is 1)
> +	    /mask ... current mask as 32 or 64 bit integer in hex
> +                       (only available if /exported is 1)
> --- linux-2.6.orig/drivers/gpio/gpiolib.c
> +++ linux-2.6/drivers/gpio/gpiolib.c
> @@ -224,7 +224,8 @@ static bool gpio_block_is_output(struct
>  	int i;
>  
>  	for (i = 0; i < block->ngpio; i++)
> -		if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
> +		if ((block->cur_mask & BIT(i)) &&
> +		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
>  			return false;
>  	return true;
>  }
> @@ -1020,6 +1021,240 @@ static void gpiochip_unexport(struct gpi
>  				chip->label, status);
>  }
>  
> +static ssize_t gpio_block_ngpio_show(struct device *dev,
> +				     struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_block	*block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%u\n", block->ngpio);
> +}
> +
> +static ssize_t gpio_block_value_show(struct device *dev,
> +				     struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_block *block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
> +		       "0x%016lx\n", gpio_block_get(block, block->cur_mask));
> +}
> +
> +static ssize_t gpio_block_value_store(struct device *dev,
> +				      struct device_attribute *attr,
> +				      const char *buf, size_t size)
> +{
> +	ssize_t			status;
> +	struct gpio_block	*block = dev_get_drvdata(dev);
> +	unsigned long		value;
> +
> +	status = kstrtoul(buf, 0, &value);
> +	if (status == 0) {
> +		mutex_lock(&sysfs_lock);
> +		if (gpio_block_is_output(block)) {
> +			gpio_block_set(block, block->cur_mask, value);
> +			status = size;
> +		} else {
> +			status = -EPERM;
> +		}
> +		mutex_unlock(&sysfs_lock);
> +	}
> +	return status;
> +}
> +
> +static struct device_attribute
> +dev_attr_block_value = __ATTR(value, S_IWUSR | S_IRUGO, gpio_block_value_show,
> +			      gpio_block_value_store);
> +
> +static ssize_t gpio_block_mask_show(struct device *dev,
> +				    struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_block *block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
> +		       "0x%016lx\n", block->cur_mask);
> +}
> +
> +static ssize_t gpio_block_mask_store(struct device *dev,
> +				     struct device_attribute *attr,
> +				     const char *buf, size_t size)
> +{
> +	ssize_t			status;
> +	struct gpio_block	*block = dev_get_drvdata(dev);
> +	unsigned long		mask;
> +
> +	status = kstrtoul(buf, 0, &mask);
> +	if (status == 0) {
> +		block->cur_mask = mask;
> +		status = size;
> +	}
> +	return status;
> +}
> +
> +static struct device_attribute
> +dev_attr_block_mask = __ATTR(mask, S_IWUSR | S_IRUGO, gpio_block_mask_show,
> +			     gpio_block_mask_store);
> +
> +static struct class gpio_block_class;
> +
> +static int gpio_block_value_is_exported(struct gpio_block *block)
> +{
> +	struct device		*dev;
> +	struct sysfs_dirent	*sd = NULL;
> +
> +	mutex_lock(&sysfs_lock);
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (!dev)
> +		goto out;
> +
> +	sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
> +
> +out:
> +	mutex_unlock(&sysfs_lock);
> +	return !!sd;
> +}
> +
> +static ssize_t gpio_block_exported_show(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct gpio_block	*block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%u\n", gpio_block_value_is_exported(block));
> +}
> +
> +static int gpio_block_value_export(struct gpio_block *block)
> +{
> +	struct device	*dev;
> +	int		status;
> +	int		i;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	for (i = 0; i < block->ngpio; i++) {
> +		status = gpio_request(block->gpio[i], "sysfs");
> +		if (status)
> +			goto out1;
> +	}
> +
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (!dev) {
> +		status = -ENODEV;
> +		goto out1;
> +	}
> +
> +	status = device_create_file(dev, &dev_attr_block_value);
> +	if (status)
> +		goto out1;
> +
> +	status = device_create_file(dev, &dev_attr_block_mask);
> +	if (status)
> +		goto out2;
> +
> +	mutex_unlock(&sysfs_lock);
> +	return 0;
> +
> +out2:
> +	device_remove_file(dev, &dev_attr_block_value);
> +out1:
> +	while (--i >= 0)
> +		gpio_free(block->gpio[i]);
> +
> +	mutex_unlock(&sysfs_lock);
> +	return status;
> +}
> +
> +static int gpio_block_value_unexport(struct gpio_block *block)
> +{
> +	struct device	*dev;
> +	int		i;
> +
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (!dev)
> +		return -ENODEV;
> +
> +	for (i = 0; i < block->ngpio; i++)
> +		gpio_free(block->gpio[i]);
> +
> +	device_remove_file(dev, &dev_attr_block_value);
> +	device_remove_file(dev, &dev_attr_block_mask);
> +
> +	return 0;
> +}
> +
> +static ssize_t gpio_block_exported_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t size)
> +{
> +	long	value;
> +	int	status;
> +	struct	gpio_block *block = dev_get_drvdata(dev);
> +	int	exported = gpio_block_value_is_exported(block);
> +
> +	status = kstrtoul(buf, 0, &value);
> +	if (status < 0)
> +		goto err;
> +
> +	if (value != exported) {
> +		if (value)
> +			status = gpio_block_value_export(block);
> +		else
> +			status = gpio_block_value_unexport(block);
> +		if (!status)
> +			status = size;
> +	} else {
> +		status = size;
> +	}
> +err:
> +	return status;
> +}
> +
> +static struct device_attribute gpio_block_attrs[] = {
> +	__ATTR(exported, S_IWUSR | S_IRUGO, gpio_block_exported_show,
> +	       gpio_block_exported_store),
> +	__ATTR(ngpio, S_IRUGO, gpio_block_ngpio_show, NULL),
> +	__ATTR_NULL,
> +};
> +
> +static struct class gpio_block_class = {
> +	.name =         "gpioblock",
> +	.owner =        THIS_MODULE,
> +
> +	.dev_attrs =  gpio_block_attrs,
> +};
> +
> +int gpio_block_export(struct gpio_block *block)
> +{
> +	int		status = 0;
> +	struct device	*dev;
> +
> +	/* can't export until sysfs is available ... */
> +	if (!gpio_block_class.p) {
> +		pr_debug("%s: called too early!\n", __func__);
> +		return -ENOENT;
> +	}
> +
> +	mutex_lock(&sysfs_lock);
> +	dev = device_create(&gpio_block_class, NULL, MKDEV(0, 0), block,
> +			    block->name);
> +	if (IS_ERR(dev))
> +		status = PTR_ERR(dev);
> +	mutex_unlock(&sysfs_lock);
> +
> +	return status;
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_export);
> +
> +void gpio_block_unexport(struct gpio_block *block)
> +{
> +	struct device *dev;
> +
> +	mutex_lock(&sysfs_lock);
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (dev)
> +		device_unregister(dev);
> +	mutex_unlock(&sysfs_lock);
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_unexport);
> +
>  static int __init gpiolib_sysfs_init(void)
>  {
>  	int		status;
> @@ -1030,6 +1265,10 @@ static int __init gpiolib_sysfs_init(voi
>  	if (status < 0)
>  		return status;
>  
> +	status = class_register(&gpio_block_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).
>  	 *
> @@ -1843,6 +2082,7 @@ struct gpio_block *gpio_block_create(uns
>  
>  	block->name = name;
>  	block->ngpio = size;
> +	block->cur_mask = ~0;
>  	INIT_LIST_HEAD(&block->gbc_list);
>  	block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
>  	if (!block->gpio)
> @@ -2005,12 +2245,21 @@ EXPORT_SYMBOL_GPL(gpio_block_find_by_nam
>  
>  int gpio_block_register(struct gpio_block *block)
>  {
> +	int ret;
> +
>  	if (gpio_block_find_by_name(block->name))
>  		return -EBUSY;
>  
>  	list_add(&block->list, &gpio_block_list);
>  
> +	ret = gpio_block_export(block);
> +	if (ret)
> +		goto err1;
> +
>  	return 0;
> +err1:
> +	list_del(&block->list);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(gpio_block_register);
>  
> @@ -2021,6 +2270,7 @@ void gpio_block_unregister(struct gpio_b
>  	list_for_each_entry(i, &gpio_block_list, list)
>  		if (i == block) {
>  			list_del(&i->list);
> +			gpio_block_unexport(block);
>  			break;
>  		}
>  }
> --- linux-2.6.orig/include/asm-generic/gpio.h
> +++ linux-2.6/include/asm-generic/gpio.h
> @@ -226,6 +226,8 @@ extern int gpio_export_link(struct devic
>  			unsigned gpio);
>  extern int gpio_sysfs_set_active_low(unsigned gpio, int value);
>  extern void gpio_unexport(unsigned gpio);
> +extern int gpio_block_export(struct gpio_block *block);
> +extern void gpio_block_unexport(struct gpio_block *block);
>  
>  #endif	/* CONFIG_GPIO_SYSFS */
>  
> @@ -285,6 +287,15 @@ static inline int gpio_sysfs_set_active_
>  static inline void gpio_unexport(unsigned gpio)
>  {
>  }
> +
> +static inline int gpio_block_export(struct gpio_block *block)
> +{
> +	return -ENOSYS;
> +}
> +
> +static inline void gpio_block_unexport(struct gpio_block *block)
> +{
> +}
>  #endif	/* CONFIG_GPIO_SYSFS */
>  
>  #ifdef CONFIG_PINCTRL
> --- linux-2.6.orig/include/linux/gpio.h
> +++ linux-2.6/include/linux/gpio.h
> @@ -88,6 +88,7 @@ struct gpio_block_chip {
>   * @ngpio:      number of gpios in this block
>   * @gpio:       list of gpios in this block
>   * @list:       global list of blocks, maintained by gpiolib
> + * @cur_mask:   currently used gpio mask used by userspace API
>   */
>  struct gpio_block {
>  	struct list_head	gbc_list;
> @@ -97,6 +98,7 @@ struct gpio_block {
>  	unsigned		*gpio;
>  
>  	struct list_head	list;
> +	unsigned long		cur_mask;
>  };
>  
>  #ifdef CONFIG_GENERIC_GPIO
> @@ -314,6 +316,19 @@ static inline void gpio_unexport(unsigne
>  	WARN_ON(1);
>  }
>  
> +static inline int gpio_block_export(struct gpio_block *block)
> +{
> +	/* GPIO block can never have been requested or set as {in,out}put */
> +	WARN_ON(1);
> +	return -EINVAL;
> +}
> +
> +static inline void gpio_block_unexport(struct gpio_block *block)
> +{
> +	/* GPIO block can never have been exported */
> +	WARN_ON(1);
> +}
> +
>  static inline int gpio_to_irq(unsigned gpio)
>  {
>  	/* GPIO can never have been requested or set as input */

-- 
Grant Likely, B.Sc, P.Eng.
Secret Lab Technologies, Ltd.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 2/6 v13] gpio: Add sysfs support to block GPIO API
@ 2013-02-15 21:50     ` Grant Likely
  0 siblings, 0 replies; 32+ messages in thread
From: Grant Likely @ 2013-02-15 21:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 15 Jan 2013 12:51:52 +0100, Roland Stigge <stigge@antcom.de> wrote:
> This patch adds sysfs support to the block GPIO API.
> 
> Signed-off-by: Roland Stigge <stigge@antcom.de>

Hi Roland.

I'm going to ignore this patch for the time being because I think my
comments on the first patch basically obsolete what is being done here.
If the a gpio sequence parser is used instead of a global block gpio api
is used, then a sysfs interface for blocks of GPIOs could be implemented
merely as a separate device driver that consumes a bunch of standard
GPIO references.

I would also suggest that an API that accepts a stream of commands
instead of a fixed u32 would be better for the userspace interface. When
I originally suggested doing a char device interface, I was expecting
the sysfs interface to be dropped entirely.

g.

> 
> ---
>  Documentation/ABI/testing/sysfs-gpio |   20 ++
>  drivers/gpio/gpiolib.c               |  252 ++++++++++++++++++++++++++++++++++-
>  include/asm-generic/gpio.h           |   11 +
>  include/linux/gpio.h                 |   15 ++
>  4 files changed, 297 insertions(+), 1 deletion(-)
> 
> --- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
> +++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
> @@ -25,3 +25,23 @@ Description:
>  	    /label ... (r/o) descriptive, not necessarily unique
>  	    /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
>  
> +What:		/sys/class/gpioblock/
> +Date:		October 2012
> +KernelVersion:	3.7
> +Contact:	Roland Stigge <stigge@antcom.de>
> +Description:
> +
> +  Block GPIO devices are visible in sysfs as soon as they are registered
> +  (e.g. via devicetree definition). For actual I/O use, their "exported"
> +  boolean attribute must be set to "1". Then, the attribute "values" is
> +  created and at the same time, the GPIOs in the block are requested for
> +  exclusive use by sysfs.
> +
> +    /sys/class/gpioblock
> +	/BLOCKNAME ... for each GPIO block name
> +	    /ngpio ... (r/o) number of GPIOs in this group
> +	    /exported ... sysfs export state of this group (0, 1)
> +	    /value ... current value as 32 or 64 bit integer in hex
> +                       (only available if /exported is 1)
> +	    /mask ... current mask as 32 or 64 bit integer in hex
> +                       (only available if /exported is 1)
> --- linux-2.6.orig/drivers/gpio/gpiolib.c
> +++ linux-2.6/drivers/gpio/gpiolib.c
> @@ -224,7 +224,8 @@ static bool gpio_block_is_output(struct
>  	int i;
>  
>  	for (i = 0; i < block->ngpio; i++)
> -		if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
> +		if ((block->cur_mask & BIT(i)) &&
> +		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
>  			return false;
>  	return true;
>  }
> @@ -1020,6 +1021,240 @@ static void gpiochip_unexport(struct gpi
>  				chip->label, status);
>  }
>  
> +static ssize_t gpio_block_ngpio_show(struct device *dev,
> +				     struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_block	*block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%u\n", block->ngpio);
> +}
> +
> +static ssize_t gpio_block_value_show(struct device *dev,
> +				     struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_block *block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
> +		       "0x%016lx\n", gpio_block_get(block, block->cur_mask));
> +}
> +
> +static ssize_t gpio_block_value_store(struct device *dev,
> +				      struct device_attribute *attr,
> +				      const char *buf, size_t size)
> +{
> +	ssize_t			status;
> +	struct gpio_block	*block = dev_get_drvdata(dev);
> +	unsigned long		value;
> +
> +	status = kstrtoul(buf, 0, &value);
> +	if (status == 0) {
> +		mutex_lock(&sysfs_lock);
> +		if (gpio_block_is_output(block)) {
> +			gpio_block_set(block, block->cur_mask, value);
> +			status = size;
> +		} else {
> +			status = -EPERM;
> +		}
> +		mutex_unlock(&sysfs_lock);
> +	}
> +	return status;
> +}
> +
> +static struct device_attribute
> +dev_attr_block_value = __ATTR(value, S_IWUSR | S_IRUGO, gpio_block_value_show,
> +			      gpio_block_value_store);
> +
> +static ssize_t gpio_block_mask_show(struct device *dev,
> +				    struct device_attribute *attr, char *buf)
> +{
> +	const struct gpio_block *block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
> +		       "0x%016lx\n", block->cur_mask);
> +}
> +
> +static ssize_t gpio_block_mask_store(struct device *dev,
> +				     struct device_attribute *attr,
> +				     const char *buf, size_t size)
> +{
> +	ssize_t			status;
> +	struct gpio_block	*block = dev_get_drvdata(dev);
> +	unsigned long		mask;
> +
> +	status = kstrtoul(buf, 0, &mask);
> +	if (status == 0) {
> +		block->cur_mask = mask;
> +		status = size;
> +	}
> +	return status;
> +}
> +
> +static struct device_attribute
> +dev_attr_block_mask = __ATTR(mask, S_IWUSR | S_IRUGO, gpio_block_mask_show,
> +			     gpio_block_mask_store);
> +
> +static struct class gpio_block_class;
> +
> +static int gpio_block_value_is_exported(struct gpio_block *block)
> +{
> +	struct device		*dev;
> +	struct sysfs_dirent	*sd = NULL;
> +
> +	mutex_lock(&sysfs_lock);
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (!dev)
> +		goto out;
> +
> +	sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
> +
> +out:
> +	mutex_unlock(&sysfs_lock);
> +	return !!sd;
> +}
> +
> +static ssize_t gpio_block_exported_show(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct gpio_block	*block = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%u\n", gpio_block_value_is_exported(block));
> +}
> +
> +static int gpio_block_value_export(struct gpio_block *block)
> +{
> +	struct device	*dev;
> +	int		status;
> +	int		i;
> +
> +	mutex_lock(&sysfs_lock);
> +
> +	for (i = 0; i < block->ngpio; i++) {
> +		status = gpio_request(block->gpio[i], "sysfs");
> +		if (status)
> +			goto out1;
> +	}
> +
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (!dev) {
> +		status = -ENODEV;
> +		goto out1;
> +	}
> +
> +	status = device_create_file(dev, &dev_attr_block_value);
> +	if (status)
> +		goto out1;
> +
> +	status = device_create_file(dev, &dev_attr_block_mask);
> +	if (status)
> +		goto out2;
> +
> +	mutex_unlock(&sysfs_lock);
> +	return 0;
> +
> +out2:
> +	device_remove_file(dev, &dev_attr_block_value);
> +out1:
> +	while (--i >= 0)
> +		gpio_free(block->gpio[i]);
> +
> +	mutex_unlock(&sysfs_lock);
> +	return status;
> +}
> +
> +static int gpio_block_value_unexport(struct gpio_block *block)
> +{
> +	struct device	*dev;
> +	int		i;
> +
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (!dev)
> +		return -ENODEV;
> +
> +	for (i = 0; i < block->ngpio; i++)
> +		gpio_free(block->gpio[i]);
> +
> +	device_remove_file(dev, &dev_attr_block_value);
> +	device_remove_file(dev, &dev_attr_block_mask);
> +
> +	return 0;
> +}
> +
> +static ssize_t gpio_block_exported_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf, size_t size)
> +{
> +	long	value;
> +	int	status;
> +	struct	gpio_block *block = dev_get_drvdata(dev);
> +	int	exported = gpio_block_value_is_exported(block);
> +
> +	status = kstrtoul(buf, 0, &value);
> +	if (status < 0)
> +		goto err;
> +
> +	if (value != exported) {
> +		if (value)
> +			status = gpio_block_value_export(block);
> +		else
> +			status = gpio_block_value_unexport(block);
> +		if (!status)
> +			status = size;
> +	} else {
> +		status = size;
> +	}
> +err:
> +	return status;
> +}
> +
> +static struct device_attribute gpio_block_attrs[] = {
> +	__ATTR(exported, S_IWUSR | S_IRUGO, gpio_block_exported_show,
> +	       gpio_block_exported_store),
> +	__ATTR(ngpio, S_IRUGO, gpio_block_ngpio_show, NULL),
> +	__ATTR_NULL,
> +};
> +
> +static struct class gpio_block_class = {
> +	.name =         "gpioblock",
> +	.owner =        THIS_MODULE,
> +
> +	.dev_attrs =  gpio_block_attrs,
> +};
> +
> +int gpio_block_export(struct gpio_block *block)
> +{
> +	int		status = 0;
> +	struct device	*dev;
> +
> +	/* can't export until sysfs is available ... */
> +	if (!gpio_block_class.p) {
> +		pr_debug("%s: called too early!\n", __func__);
> +		return -ENOENT;
> +	}
> +
> +	mutex_lock(&sysfs_lock);
> +	dev = device_create(&gpio_block_class, NULL, MKDEV(0, 0), block,
> +			    block->name);
> +	if (IS_ERR(dev))
> +		status = PTR_ERR(dev);
> +	mutex_unlock(&sysfs_lock);
> +
> +	return status;
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_export);
> +
> +void gpio_block_unexport(struct gpio_block *block)
> +{
> +	struct device *dev;
> +
> +	mutex_lock(&sysfs_lock);
> +	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
> +	if (dev)
> +		device_unregister(dev);
> +	mutex_unlock(&sysfs_lock);
> +}
> +EXPORT_SYMBOL_GPL(gpio_block_unexport);
> +
>  static int __init gpiolib_sysfs_init(void)
>  {
>  	int		status;
> @@ -1030,6 +1265,10 @@ static int __init gpiolib_sysfs_init(voi
>  	if (status < 0)
>  		return status;
>  
> +	status = class_register(&gpio_block_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).
>  	 *
> @@ -1843,6 +2082,7 @@ struct gpio_block *gpio_block_create(uns
>  
>  	block->name = name;
>  	block->ngpio = size;
> +	block->cur_mask = ~0;
>  	INIT_LIST_HEAD(&block->gbc_list);
>  	block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
>  	if (!block->gpio)
> @@ -2005,12 +2245,21 @@ EXPORT_SYMBOL_GPL(gpio_block_find_by_nam
>  
>  int gpio_block_register(struct gpio_block *block)
>  {
> +	int ret;
> +
>  	if (gpio_block_find_by_name(block->name))
>  		return -EBUSY;
>  
>  	list_add(&block->list, &gpio_block_list);
>  
> +	ret = gpio_block_export(block);
> +	if (ret)
> +		goto err1;
> +
>  	return 0;
> +err1:
> +	list_del(&block->list);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(gpio_block_register);
>  
> @@ -2021,6 +2270,7 @@ void gpio_block_unregister(struct gpio_b
>  	list_for_each_entry(i, &gpio_block_list, list)
>  		if (i == block) {
>  			list_del(&i->list);
> +			gpio_block_unexport(block);
>  			break;
>  		}
>  }
> --- linux-2.6.orig/include/asm-generic/gpio.h
> +++ linux-2.6/include/asm-generic/gpio.h
> @@ -226,6 +226,8 @@ extern int gpio_export_link(struct devic
>  			unsigned gpio);
>  extern int gpio_sysfs_set_active_low(unsigned gpio, int value);
>  extern void gpio_unexport(unsigned gpio);
> +extern int gpio_block_export(struct gpio_block *block);
> +extern void gpio_block_unexport(struct gpio_block *block);
>  
>  #endif	/* CONFIG_GPIO_SYSFS */
>  
> @@ -285,6 +287,15 @@ static inline int gpio_sysfs_set_active_
>  static inline void gpio_unexport(unsigned gpio)
>  {
>  }
> +
> +static inline int gpio_block_export(struct gpio_block *block)
> +{
> +	return -ENOSYS;
> +}
> +
> +static inline void gpio_block_unexport(struct gpio_block *block)
> +{
> +}
>  #endif	/* CONFIG_GPIO_SYSFS */
>  
>  #ifdef CONFIG_PINCTRL
> --- linux-2.6.orig/include/linux/gpio.h
> +++ linux-2.6/include/linux/gpio.h
> @@ -88,6 +88,7 @@ struct gpio_block_chip {
>   * @ngpio:      number of gpios in this block
>   * @gpio:       list of gpios in this block
>   * @list:       global list of blocks, maintained by gpiolib
> + * @cur_mask:   currently used gpio mask used by userspace API
>   */
>  struct gpio_block {
>  	struct list_head	gbc_list;
> @@ -97,6 +98,7 @@ struct gpio_block {
>  	unsigned		*gpio;
>  
>  	struct list_head	list;
> +	unsigned long		cur_mask;
>  };
>  
>  #ifdef CONFIG_GENERIC_GPIO
> @@ -314,6 +316,19 @@ static inline void gpio_unexport(unsigne
>  	WARN_ON(1);
>  }
>  
> +static inline int gpio_block_export(struct gpio_block *block)
> +{
> +	/* GPIO block can never have been requested or set as {in,out}put */
> +	WARN_ON(1);
> +	return -EINVAL;
> +}
> +
> +static inline void gpio_block_unexport(struct gpio_block *block)
> +{
> +	/* GPIO block can never have been exported */
> +	WARN_ON(1);
> +}
> +
>  static inline int gpio_to_irq(unsigned gpio)
>  {
>  	/* GPIO can never have been requested or set as input */

-- 
Grant Likely, B.Sc, P.Eng.
Secret Lab Technologies, Ltd.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH RESEND 4/6 v13] gpiolib: Fix default attributes for class
  2013-01-15 11:51   ` Roland Stigge
@ 2013-02-15 22:55     ` Grant Likely
  -1 siblings, 0 replies; 32+ messages in thread
From: Grant Likely @ 2013-02-15 22:55 UTC (permalink / raw)
  To: Roland Stigge, gregkh, linus.walleij, linux-kernel,
	linux-arm-kernel, w.sang, jbe, plagnioj, highguy, broonie,
	daniel-gl, rmallon, sr, wg, tru, mark.rutland
  Cc: Roland Stigge

On Tue, 15 Jan 2013 12:51:54 +0100, Roland Stigge <stigge@antcom.de> wrote:
> There is a race condition between creating a gpio or gpiochip device and adding
> default attributes. This patch fixes this by defining the default attributes as
> dev_attrs of the class. For this, it was necessary to create a separate
> gpiochip_class besides gpio_class.
> 
> Signed-off-by: Roland Stigge <stigge@antcom.de>

Hi Roland,

Yup, that's a bug. Thanks for looking at this. Unfortunately creating a
new class creates a new problem because it changes the userspace ABI
(the gpiochip simlinks move to a different class directory).

Instead, I think the correct solution here is to use (struct device *)->groups
to add attributes to a device. Unfortunately that means that
create_device() won't work anymore. It does some useful setup work
though that I don't want to have to reproduce. You could create a
variant that also allows groups to be set, or you could factor out the
allocation/setup stuff so we can do an allocate/fixup/register sequence.

g.


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH RESEND 4/6 v13] gpiolib: Fix default attributes for class
@ 2013-02-15 22:55     ` Grant Likely
  0 siblings, 0 replies; 32+ messages in thread
From: Grant Likely @ 2013-02-15 22:55 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 15 Jan 2013 12:51:54 +0100, Roland Stigge <stigge@antcom.de> wrote:
> There is a race condition between creating a gpio or gpiochip device and adding
> default attributes. This patch fixes this by defining the default attributes as
> dev_attrs of the class. For this, it was necessary to create a separate
> gpiochip_class besides gpio_class.
> 
> Signed-off-by: Roland Stigge <stigge@antcom.de>

Hi Roland,

Yup, that's a bug. Thanks for looking at this. Unfortunately creating a
new class creates a new problem because it changes the userspace ABI
(the gpiochip simlinks move to a different class directory).

Instead, I think the correct solution here is to use (struct device *)->groups
to add attributes to a device. Unfortunately that means that
create_device() won't work anymore. It does some useful setup work
though that I don't want to have to reproduce. You could create a
variant that also allows groups to be set, or you could factor out the
allocation/setup stuff so we can do an allocate/fixup/register sequence.

g.

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2013-02-15 22:55 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-15 11:51 [PATCH RESEND 0/6 v13] gpio: Add block GPIO Roland Stigge
2013-01-15 11:51 ` Roland Stigge
2013-01-15 11:51 ` [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib Roland Stigge
2013-01-15 11:51   ` Roland Stigge
2013-01-18 12:13   ` Stijn Devriendt
2013-01-18 12:13     ` Stijn Devriendt
2013-01-18 13:18     ` Roland Stigge
2013-01-18 13:18       ` Roland Stigge
2013-02-15 13:16     ` Grant Likely
2013-02-15 13:16       ` Grant Likely
2013-02-15 14:47   ` Grant Likely
2013-02-15 14:47     ` Grant Likely
2013-02-15 16:34   ` Grant Likely
2013-02-15 16:34     ` Grant Likely
2013-01-15 11:51 ` [PATCH RESEND 2/6 v13] gpio: Add sysfs support to block GPIO API Roland Stigge
2013-01-15 11:51   ` Roland Stigge
2013-02-15 21:50   ` Grant Likely
2013-02-15 21:50     ` Grant Likely
2013-01-15 11:51 ` [PATCH RESEND 3/6 v13] gpio: Add userland device interface to block GPIO Roland Stigge
2013-01-15 11:51   ` Roland Stigge
2013-01-15 11:51 ` [PATCH RESEND 4/6 v13] gpiolib: Fix default attributes for class Roland Stigge
2013-01-15 11:51   ` Roland Stigge
2013-02-15 22:55   ` Grant Likely
2013-02-15 22:55     ` Grant Likely
2013-01-15 11:51 ` [PATCH RESEND 5/6 v13] gpio: Add device tree support to block GPIO API Roland Stigge
2013-01-15 11:51   ` Roland Stigge
2013-01-15 11:51 ` [PATCH RESEND 6/6 v13] gpio: Add block gpio to several gpio drivers Roland Stigge
2013-01-15 11:51   ` Roland Stigge
2013-01-15 13:18   ` Nicolas Ferre
2013-01-15 13:18     ` Nicolas Ferre
2013-01-15 13:30     ` Roland Stigge
2013-01-15 13:30       ` Roland Stigge

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.