[RFC 2.6.28 1/3] gpiolib: add batch set/get

From: Jaya Kumar
Date: Sun Jan 25 2009 - 04:55:21 EST


Hi friends,

This is v5 of batch support for gpiolib. Thanks to Uwe Kleine-KÃnig,
Ryan Mallon and others for prior feedback. The changes I've made are:
- split the patches into generic, arch specific and am300epd
- adjusting the API to remove width (note, the actual API call where
width was dropped is in the arch specific code, not here.)
- updating documentation of this API in gpio.txt
- cleanup of the width, mask terms

Please let me know your thoughts and feedback.

Thanks,
jaya

Cc: David Brownell <david-b@xxxxxxxxxxx>
Cc: Eric Miao <eric.miao@xxxxxxxxxxx>
Cc: Paulius Zaleckas <paulius.zaleckas@xxxxxxxxxxxx>
Cc: Geert Uytterhoeven <geert@xxxxxxxxxxxxxx>
Cc: Sam Ravnborg <sam@xxxxxxxxxxxx>
Cc: linux-arm-kernel@xxxxxxxxxxxxxxxxxxxxxx
Cc: linux-fbdev-devel@xxxxxxxxxxxxxxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx
Cc: linux-embedded@xxxxxxxxxxxxxxx
Signed-off-by: Jaya Kumar <jayakumar.lkml@xxxxxxxxx>
---
Documentation/gpio.txt | 72 ++++++++++++
drivers/gpio/Kconfig | 5 +
drivers/gpio/gpiolib.c | 272 ++++++++++++++++++++++++++++++++++++++++++++
include/asm-generic/gpio.h | 17 +++-
4 files changed, 365 insertions(+), 1 deletions(-)

diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt
index b1b9887..d249b5c 100644
--- a/Documentation/gpio.txt
+++ b/Documentation/gpio.txt
@@ -185,6 +185,78 @@ and not to need spinlocks. Such optimized calls can make bitbanging
applications a lot more efficient (in both space and time) than spending
dozens of instructions on subroutine calls.

+[OPTIONAL] Spinlock-Safe GPIO Batch access
+------------------------------------------
+The original GPIO API provides single bit access to GPIO pins. However,
+some devices treat GPIO as a mechanism for bulk data transfer. In this type
+of system, the performance of the single bit API may be inadequate. As such,
+the user can consider enabling the batch access API. The batch access API
+allows access of up-to 32-bits of GPIO at a time. This API is as follows:
+
+ /* BATCH GPIO INPUT */
+int gpio_get_batch(unsigned startpin, u32 mask, u32 *result);
+
+The following examples help explain how this function is to be used.
+ Q: How do I get gpio pins 0 through 7? (8 bits)
+ A: gpio_get_batch(startpin=0, mask=0xFF, &result) result=0xnn
+ Q: How do I get gpio pins 58 through 73? (16 bits)
+ A: gpio_get_batch(startpin=58, mask=0xFFFF, &result) result=0xnnnn
+ Q: How do I get gpio pins 16 through 47? (32 bits)
+ A: gpio_get_batch(startpin=16, mask=0xFFFFFFFF, &result)
+ A: result=0xnnnnnnnn
+ Q: How do I get non-consecutive gpio pins 5 and 9?
+ A: Use the mask to mask out 6, 7 and 8
+ A: So mask in binary is 10001 which is 0x11
+ A: gpio_get_batch(startpin=5, mask=0x11, &result)
+ A: result is in the same form, binary n000n
+
+ /* BATCH GPIO OUTPUT */
+int gpio_set_batch(unsigned startpin, u32 mask, u32 values);
+
+The following examples help explain how this function is to be used.
+ Q: How to set gpio pins 0 through 7 to all 0? (8 bits)
+ A: gpio_set_batch(startpin=0, mask=0xFF, values=0x0);
+ Q: How to set gpio pins 58 through 73 to all 1? (16 bits)
+ A: gpio_set_batch(startpin=58, mask=0xFFFF, values=0xFFFF);
+ Q: How to set gpio pins 16 through 47 to 0xCAFEC001? (32 bits)
+ A: gpio_set_batch(startpin=16, mask=0xFFFFFFFF, values=0xCAFEC001);
+ Q: How do I set non-consecutive gpio pins 5 and 9 to both 1?
+ A: Use the mask to mask out 6, 7 and 8
+ A: So mask in binary is 10001 which is 0x11
+ A: gpio_set_batch(startpin=5, mask=0x11, values=0x11)
+
+The following example shows the use of the batch API and a comparison with
+the original single bit API:
+
+Original input method which loops through a set of pins:
+ for (i = 0; i <= (DB15_GPIO_PIN - DB0_GPIO_PIN) ; i++)
+ res |= (gpio_get_value(DB0_GPIO_PIN + i)) ? (1 << i) : 0;
+
+Batch input method:
+ u32 val;
+ err = gpio_get_batch(DB0_GPIO_PIN, 0xFFFF, &val);
+
+Original output method:
+ for (i = 0; i <= (DB15_GPIO_PIN - DB0_GPIO_PIN) ; i++)
+ gpio_set_value(DB0_GPIO_PIN + i, (data >> i) & 0x01);
+
+Batch output method:
+ int err;
+ err = gpio_set_batch(DB0_GPIO_PIN, 0xFFFF, data);
+
+Using these calls for GPIOs that can't safely be accessed without sleeping
+(see below) is an error.
+
+Platform-specific implementations are encouraged to optimize the two
+calls by checking if the batch get/set requested can be achieved within the
+platform's fast path access to gpio registers. For example, if the starting
+gpio and width of bits to be written is contained within a single register
+then the platform specific implementation may choose to execute it. If the
+request is more complex, then the platform specific implementation can
+choose to call upon the platform independent __gpio_get/set_batch functions
+which are able to cross gpio_chip boundaries. Implementations are also
+encouraged to use the bitops macros. These will also optimize for use cases
+where the masks are constants.

GPIO access that may sleep
--------------------------
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3d25654..474070b 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -37,6 +37,11 @@ menuconfig GPIOLIB

if GPIOLIB

+config GPIOLIB_BATCH
+ bool "Batch GPIO support"
+ help
+ Say Y here to add the capability to batch set/get GPIOs.
+
config DEBUG_GPIO
bool "Debug GPIO calls"
depends on DEBUG_KERNEL
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 35e7aea..12e1e30 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -643,6 +643,272 @@ static inline void gpiochip_unexport(struct gpio_chip *chip)

#endif /* CONFIG_GPIO_SYSFS */

+#ifdef CONFIG_GPIOLIB_BATCH
+/**
+ * __gpio_set_batch_generic() - Set batch of gpio pins in provided gpio_chip.
+ * @chip: gpio_chip containing this set of pins
+ * @startpin: starting gpio pin
+ * @mask: the mask to be applied to values
+ * @width: the width to the last set bit of the mask
+ * @values: values to assign including masked bits
+ * Context: any
+ *
+ * This provides a generic platform independent set_batch capability.
+ * It invokes the associated gpio_chip.set() method to actually set the
+ * value. gpio_chip-s that don't implement their own optimized
+ * set_batch function are assigned this function as a default.
+ *
+ * Returns a negative errno if the caller supplied bad data, such as
+ * startpin or width in excess of this chips gpio. Otherwise, we return zero
+ * as a success code.
+ */
+static int __gpio_set_batch_generic(struct gpio_chip *chip, unsigned startpin,
+ u32 mask, int width, u32 values)
+{
+ int i;
+ u32 pin_mask;
+ int value;
+
+ /*
+ * If the caller attempted to exceed the number of gpios that
+ * are in this chip, then we flag that as an invalid value for
+ * either the startpin or the width supplied.
+ */
+ if (startpin + width > chip->ngpio)
+ return -EINVAL;
+
+ /*
+ * We start the loop and continue till we reach the width
+ * of the mask that is provided to us.
+ */
+ for (i = 0; i < width; i++) {
+ /*
+ * If this bit is enabled by the mask then
+ * we perform the set. If it is disabled we leave
+ * it alone.
+ */
+ pin_mask = 1 << i;
+ if (mask & pin_mask) {
+ value = values & pin_mask;
+ chip->set(chip, startpin + i, value);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * __gpio_get_batch_generic() - Get batch of gpio pins in provided gpio_chip.
+ * @chip: gpio_chip containing this set of pins
+ * @startpin: starting gpio pin
+ * @mask: the mask to be applied to values
+ * @width: the width to the last set bit of the mask
+ * @result: the result to be returned
+ * Context: any
+ *
+ * This provides a generic platform independent get_batch capability.
+ * It invokes the associated gpio_chip.get() method to actually set the
+ * value. gpio_chip-s that don't implement their own optimized
+ * get_batch function are assigned this function as a default.
+ *
+ * Returns a negative errno if the caller supplied bad data, such as
+ * startpin or width in excess of this chips gpio. Otherwise, we return zero
+ * as a success code.
+ * Context: any
+ */
+static int __gpio_get_batch_generic(struct gpio_chip *chip, unsigned startpin,
+ u32 mask, int width, u32 *result)
+{
+ int i;
+ u32 pin_mask;
+ u32 values = 0;
+
+ /*
+ * If the caller attempted to exceed the number of gpios that
+ * are in this chip, then we flag that as an invalid value for
+ * either the startpin or the width supplied.
+ */
+ if (startpin + width > chip->ngpio)
+ return -EINVAL;
+
+ /*
+ * We start the loop and continue till we reach the width
+ * of the mask that is provided to us.
+ */
+ for (i = 0; i < width; i++) {
+ /*
+ * If this bit is enabled by the mask then
+ * we perform the get. If it is disabled we leave
+ * it alone thus leaving it as 0 because we initialized
+ * values.
+ */
+ pin_mask = 1 << i;
+ if (mask & pin_mask) {
+ if (chip->get(chip, startpin + i))
+ values |= pin_mask;
+ }
+ }
+
+ *result = values;
+ return 0;
+}
+
+/**
+ * __gpio_set_batch() - set batch of gpio pins across multiple gpio_chip-s
+ * @startpin: starting gpio pin
+ * @mask: the mask to be applied to values
+ * @width: the width to the last set bit of the mask
+ * @values: values to assign including masked bits
+ * Context: any
+ *
+ * This function is platform independent and uses the starting gpio and
+ * width information to iterate through the set of required gpio_chips
+ * to use their set_batch capability in order to set a batch of gpio pins.
+ * This function handles going across gpio_chip boundaries. It is intended
+ * to be called from arch/mach specific code if they detect that the caller
+ * requires functionality outside the fast-path.
+ *
+ * Returns a negative errno if the caller supplied bad data, such as
+ * startpin or width in excess of the platforms max gpio. Otherwise, we return
+ * zero as a success code.
+ *
+ */
+int __gpio_set_batch(unsigned startpin, u32 mask, int width, u32 values)
+{
+ struct gpio_chip *chip;
+ int i = 0;
+ int subwidth, remwidth;
+ u32 subvalue;
+ u32 submask;
+ int ret;
+
+ /*
+ * If the caller attempted to exceed the number of gpios that
+ * are available here, then we flag that as an invalid value for
+ * either the startpin or the width supplied.
+ */
+ if ((width > 32) || (startpin + width > ARCH_NR_GPIOS))
+ return -EINVAL;
+
+ do {
+ chip = gpio_to_chip(startpin + i);
+ WARN_ON(extra_checks && chip->can_sleep);
+
+ subvalue = values >> i; /* shift off the used data bits */
+
+ /* Work out the remaining width in this chip. */
+ remwidth = ((chip->base + (int) chip->ngpio) -
+ ((int) startpin + i));
+
+ /*
+ * Check if the remaining bits to be handled are less than
+ * the remaining width in this chip. That is the width of
+ * the subset that we are about to handle.
+ */
+ subwidth = min(width, remwidth);
+
+ /* Shift off the used mask bits. */
+ submask = mask >> i;
+
+ /* Now adjust mask by width of this subset. */
+ submask &= ((1 << subwidth) - 1);
+
+ /* If the mask is empty, then we can skip this chip. */
+ if (submask) {
+ ret = chip->set_batch(chip, startpin + i - chip->base,
+ submask, subwidth, subvalue);
+ if (ret)
+ return ret;
+ }
+
+ /* deduct the used bits from our todolist */
+ i += subwidth;
+ width -= subwidth;
+ } while (width);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__gpio_set_batch);
+
+/**
+ * __gpio_get_batch() - get batch of gpio pins across multiple gpio_chip-s
+ * @startpin: starting gpio pin
+ * @mask: the mask to be applied to values
+ * @width: the width to the last set bit of the mask
+ * @result: returned values
+ * Context: any
+ *
+ * This function is platform independent and uses the starting gpio and
+ * width information to iterate through the set of required gpio_chips
+ * to use their get_batch capability in order to get a batch of gpio pins.
+ * This function handles going across gpio_chip boundaries. It is intended
+ * to be called from arch/mach specific code if they detect that the caller
+ * requires functionality outside the fast-path.
+ *
+ * Returns a negative errno if the caller supplied bad data, such as
+ * startpin or width in excess of the platforms max gpio. Otherwise, we return
+ * zero as a success code
+ *
+ */
+int __gpio_get_batch(unsigned startpin, u32 mask, int width, u32 *result)
+{
+ struct gpio_chip *chip;
+ int i = 0;
+ int subwidth, remwidth;
+ u32 submask;
+ u32 values = 0;
+ u32 subvalue;
+ int ret;
+
+ /*
+ * If the caller attempted to exceed the number of gpios that
+ * are available here, then we flag that as an invalid value for
+ * either the startpin or the width supplied.
+ */
+ if ((width > 32) || (startpin + width > ARCH_NR_GPIOS))
+ return -EINVAL;
+
+ do {
+ chip = gpio_to_chip(startpin + i);
+ WARN_ON(extra_checks && chip->can_sleep);
+
+ /* Work out the remaining width in this chip. */
+ remwidth = ((chip->base + (int) chip->ngpio) -
+ ((int) startpin + i));
+
+ /*
+ * Check if the remaining bits to be handled are less than
+ * the remaining width in this chip.
+ */
+ subwidth = min(width, remwidth);
+
+ /* shift off the used mask bits */
+ submask = mask >> i;
+ /* now adjust mask by width of get */
+ submask &= ((1 << width) - 1);
+
+ /* If the mask is empty, then we can skip this chip. */
+ if (submask) {
+ ret = chip->get_batch(chip, startpin + i - chip->base,
+ submask, subwidth, &subvalue);
+ if (ret)
+ return ret;
+
+ /* shift result back into correct position */
+ values |= subvalue << i;
+ }
+
+ /* deduct the used bits from our todolist */
+ i += subwidth;
+ width -= subwidth;
+ } while (width);
+
+ *result = values;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__gpio_get_batch);
+#endif
+
/**
* gpiochip_add() - register a gpio_chip
* @chip: the chip to register, with chip->base initialized
@@ -683,6 +949,12 @@ int gpiochip_add(struct gpio_chip *chip)
}
chip->base = base;
}
+#ifdef CONFIG_GPIOLIB_BATCH
+ if (!chip->set_batch)
+ chip->set_batch = __gpio_set_batch_generic;
+ if (!chip->get_batch)
+ chip->get_batch = __gpio_get_batch_generic;
+#endif

/* these GPIO numbers must not be managed by another gpio_chip */
for (id = base; id < base + chip->ngpio; id++) {
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 81797ec..4e92ccf 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -44,6 +44,10 @@ struct module;
* returns either the value actually sensed, or zero
* @direction_output: configures signal "offset" as output, or returns error
* @set: assigns output value for signal "offset"
+ * @set_batch: batch assigns output values for signals starting at
+ * "startpin" with mask in "mask" all within this gpio_chip
+ * @get_batch: batch fetches values for consecutive signals starting at
+ * "startpin" with mask in "mask" all within this gpio_chip
* @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
* implementation may not sleep
* @dbg_show: optional routine to show contents in debugfs; default code
@@ -84,7 +88,14 @@ struct gpio_chip {
unsigned offset, int value);
void (*set)(struct gpio_chip *chip,
unsigned offset, int value);
-
+#ifdef CONFIG_GPIOLIB_BATCH
+ int (*set_batch)(struct gpio_chip *chip,
+ unsigned startpin, u32 mask,
+ int width, u32 values);
+ int (*get_batch)(struct gpio_chip *chip,
+ unsigned startpin, u32 mask,
+ int width, u32 *result);
+#endif
int (*to_irq)(struct gpio_chip *chip,
unsigned offset);

@@ -124,6 +135,10 @@ extern void gpio_set_value_cansleep(unsigned gpio, int value);
*/
extern int __gpio_get_value(unsigned gpio);
extern void __gpio_set_value(unsigned gpio, int value);
+#ifdef CONFIG_GPIOLIB_BATCH
+extern int __gpio_set_batch(unsigned gpio, u32 mask, int width, u32 values);
+extern int __gpio_get_batch(unsigned gpio, u32 mask, int width, u32 *result);
+#endif

extern int __gpio_cansleep(unsigned gpio);

--
1.5.2.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/