diff mbox

[RESEND,1/6,v13] gpio: Add a block GPIO API to gpiolib

Message ID 1358250716-21986-2-git-send-email-stigge@antcom.de (mailing list archive)
State New, archived
Headers show

Commit Message

Roland Stigge Jan. 15, 2013, 11:51 a.m. UTC
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(+)

Comments

Grant Likely Feb. 15, 2013, 2:47 p.m. UTC | #1
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.
Grant Likely Feb. 15, 2013, 4:34 p.m. UTC | #2
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.
diff mbox

Patch

--- 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 */