summaryrefslogtreecommitdiffstats
path: root/target/linux/generic-2.6/patches-2.6.25/962-backport_gpiolib_dynamic_gpio_number_allocation.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic-2.6/patches-2.6.25/962-backport_gpiolib_dynamic_gpio_number_allocation.patch')
-rw-r--r--target/linux/generic-2.6/patches-2.6.25/962-backport_gpiolib_dynamic_gpio_number_allocation.patch118
1 files changed, 118 insertions, 0 deletions
diff --git a/target/linux/generic-2.6/patches-2.6.25/962-backport_gpiolib_dynamic_gpio_number_allocation.patch b/target/linux/generic-2.6/patches-2.6.25/962-backport_gpiolib_dynamic_gpio_number_allocation.patch
new file mode 100644
index 000000000..000742322
--- /dev/null
+++ b/target/linux/generic-2.6/patches-2.6.25/962-backport_gpiolib_dynamic_gpio_number_allocation.patch
@@ -0,0 +1,118 @@
+From: Anton Vorontsov <avorontsov@ru.mvista.com>
+Date: Mon, 28 Apr 2008 09:14:46 +0000 (-0700)
+Subject: gpiolib: dynamic gpio number allocation
+X-Git-Tag: v2.6.26-rc1~848
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=8d0aab2f16c4fa170f32e7a74a52cd0122bbafef
+
+gpiolib: dynamic gpio number allocation
+
+If gpio_chip->base is negative during registration, gpiolib performs dynamic
+base allocation. This is useful for devices that aren't always present, such
+as GPIOs on hotplugged devices rather than mainboards. (This behavior was
+previously specified but not implemented.)
+
+To avoid using any numbers that may have been explicitly assigned but not yet
+registered, this dynamic allocation assigns GPIO numbers from the biggest
+number on down, instead of from the smallest on up.
+
+Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+---
+
+diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
+index 623fcd9..2ba6127 100644
+--- a/drivers/gpio/gpiolib.c
++++ b/drivers/gpio/gpiolib.c
+@@ -80,6 +80,33 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
+ return gpio_desc[gpio].chip;
+ }
+
++/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
++static int gpiochip_find_base(int ngpio)
++{
++ int i;
++ int spare = 0;
++ int base = -ENOSPC;
++
++ for (i = ARCH_NR_GPIOS - 1; i >= 0 ; i--) {
++ struct gpio_chip *chip = gpio_desc[i].chip;
++
++ if (!chip) {
++ spare++;
++ if (spare == ngpio) {
++ base = i;
++ break;
++ }
++ } else {
++ spare = 0;
++ i -= chip->ngpio - 1;
++ }
++ }
++
++ if (gpio_is_valid(base))
++ pr_debug("%s: found new base at %d\n", __func__, base);
++ return base;
++}
++
+ /**
+ * gpiochip_add() - register a gpio_chip
+ * @chip: the chip to register, with chip->base initialized
+@@ -88,38 +115,49 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
+ * Returns a negative errno if the chip can't be registered, such as
+ * because the chip->base is invalid or already associated with a
+ * different chip. Otherwise it returns zero as a success code.
++ *
++ * If chip->base is negative, this requests dynamic assignment of
++ * a range of valid GPIOs.
+ */
+ int gpiochip_add(struct gpio_chip *chip)
+ {
+ unsigned long flags;
+ int status = 0;
+ unsigned id;
++ int base = chip->base;
+
+- /* NOTE chip->base negative is reserved to mean a request for
+- * dynamic allocation. We don't currently support that.
+- */
+-
+- if (chip->base < 0 || !gpio_is_valid(chip->base + chip->ngpio)) {
++ if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio))
++ && base >= 0) {
+ status = -EINVAL;
+ goto fail;
+ }
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
++ if (base < 0) {
++ base = gpiochip_find_base(chip->ngpio);
++ if (base < 0) {
++ status = base;
++ goto fail_unlock;
++ }
++ chip->base = base;
++ }
++
+ /* these GPIO numbers must not be managed by another gpio_chip */
+- for (id = chip->base; id < chip->base + chip->ngpio; id++) {
++ for (id = base; id < base + chip->ngpio; id++) {
+ if (gpio_desc[id].chip != NULL) {
+ status = -EBUSY;
+ break;
+ }
+ }
+ if (status == 0) {
+- for (id = chip->base; id < chip->base + chip->ngpio; id++) {
++ for (id = base; id < base + chip->ngpio; id++) {
+ gpio_desc[id].chip = chip;
+ gpio_desc[id].flags = 0;
+ }
+ }
+
++fail_unlock:
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ fail:
+ /* failures here can mean systems won't boot... */