]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
gpiolib: request/free hooks
authorDavid Brownell <dbrownell@users.sourceforge.net>
Thu, 16 Oct 2008 05:03:16 +0000 (22:03 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 16 Oct 2008 18:21:40 +0000 (11:21 -0700)
Add a new internal mechanism to gpiolib to support low power
operations by letting gpio_chip instances see when their GPIOs
are in use.  When no GPIOs are active, chips may be able to
enter lower powered runtime states by disabling clocks and/or
power domains.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Cc: "Magnus Damm" <magnus.damm@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Documentation/gpio.txt
drivers/gpio/gpiolib.c
include/asm-generic/gpio.h

index 8a7c45956d2412d69a26f8de991db19ed25a838f..b1b9887012478f9b654edaa90bcdd13e72a9f20e 100644 (file)
@@ -240,6 +240,10 @@ signal, or (b) something wrongly believes it's safe to remove drivers
 needed to manage a signal that's in active use.  That is, requesting a
 GPIO can serve as a kind of lock.
 
+Some platforms may also use knowledge about what GPIOs are active for
+power management, such as by powering down unused chip sectors and, more
+easily, gating off unused clocks.
+
 These two calls are optional because not not all current Linux platforms
 offer such functionality in their GPIO support; a valid implementation
 could return success for all gpio_request() calls.  Unlike the other calls,
index 29a7e6b1be5d8db915909a17164aec251ac91759..9112830107a53d4c208b5a8cbf1934026549bac1 100644 (file)
@@ -67,17 +67,28 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
  * when setting direction, and otherwise illegal.  Until board setup code
  * and drivers use explicit requests everywhere (which won't happen when
  * those calls have no teeth) we can't avoid autorequesting.  This nag
- * message should motivate switching to explicit requests...
+ * message should motivate switching to explicit requests... so should
+ * the weaker cleanup after faults, compared to gpio_request().
  */
-static void gpio_ensure_requested(struct gpio_desc *desc)
+static int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset)
 {
        if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
-               pr_warning("GPIO-%d autorequested\n", (int)(desc - gpio_desc));
+               struct gpio_chip *chip = desc->chip;
+               int gpio = chip->base + offset;
+
+               if (!try_module_get(chip->owner)) {
+                       pr_err("GPIO-%d: module can't be gotten \n", gpio);
+                       clear_bit(FLAG_REQUESTED, &desc->flags);
+                       /* lose */
+                       return -EIO;
+               }
+               pr_warning("GPIO-%d autorequested\n", gpio);
                desc_set_label(desc, "[auto]");
-               if (!try_module_get(desc->chip->owner))
-                       pr_err("GPIO-%d: module can't be gotten \n",
-                                       (int)(desc - gpio_desc));
+               /* caller must chip->request() w/o spinlock */
+               if (chip->request)
+                       return 1;
        }
+       return 0;
 }
 
 /* caller holds gpio_lock *OR* gpio is marked as requested */
@@ -752,6 +763,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove);
 int gpio_request(unsigned gpio, const char *label)
 {
        struct gpio_desc        *desc;
+       struct gpio_chip        *chip;
        int                     status = -EINVAL;
        unsigned long           flags;
 
@@ -760,14 +772,15 @@ int gpio_request(unsigned gpio, const char *label)
        if (!gpio_is_valid(gpio))
                goto done;
        desc = &gpio_desc[gpio];
-       if (desc->chip == NULL)
+       chip = desc->chip;
+       if (chip == NULL)
                goto done;
 
-       if (!try_module_get(desc->chip->owner))
+       if (!try_module_get(chip->owner))
                goto done;
 
        /* NOTE:  gpio_request() can be called in early boot,
-        * before IRQs are enabled.
+        * before IRQs are enabled, for non-sleeping (SOC) GPIOs.
         */
 
        if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
@@ -775,7 +788,20 @@ int gpio_request(unsigned gpio, const char *label)
                status = 0;
        } else {
                status = -EBUSY;
-               module_put(desc->chip->owner);
+               module_put(chip->owner);
+       }
+
+       if (chip->request) {
+               /* chip->request may sleep */
+               spin_unlock_irqrestore(&gpio_lock, flags);
+               status = chip->request(chip, gpio - chip->base);
+               spin_lock_irqsave(&gpio_lock, flags);
+
+               if (status < 0) {
+                       desc_set_label(desc, NULL);
+                       module_put(chip->owner);
+                       clear_bit(FLAG_REQUESTED, &desc->flags);
+               }
        }
 
 done:
@@ -791,6 +817,7 @@ void gpio_free(unsigned gpio)
 {
        unsigned long           flags;
        struct gpio_desc        *desc;
+       struct gpio_chip        *chip;
 
        might_sleep();
 
@@ -804,9 +831,17 @@ void gpio_free(unsigned gpio)
        spin_lock_irqsave(&gpio_lock, flags);
 
        desc = &gpio_desc[gpio];
-       if (desc->chip && test_and_clear_bit(FLAG_REQUESTED, &desc->flags)) {
+       chip = desc->chip;
+       if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
+               if (chip->free) {
+                       spin_unlock_irqrestore(&gpio_lock, flags);
+                       might_sleep_if(extra_checks && chip->can_sleep);
+                       chip->free(chip, gpio - chip->base);
+                       spin_lock_irqsave(&gpio_lock, flags);
+               }
                desc_set_label(desc, NULL);
                module_put(desc->chip->owner);
+               clear_bit(FLAG_REQUESTED, &desc->flags);
        } else
                WARN_ON(extra_checks);
 
@@ -871,7 +906,9 @@ int gpio_direction_input(unsigned gpio)
        gpio -= chip->base;
        if (gpio >= chip->ngpio)
                goto fail;
-       gpio_ensure_requested(desc);
+       status = gpio_ensure_requested(desc, gpio);
+       if (status < 0)
+               goto fail;
 
        /* now we know the gpio is valid and chip won't vanish */
 
@@ -879,9 +916,22 @@ int gpio_direction_input(unsigned gpio)
 
        might_sleep_if(extra_checks && chip->can_sleep);
 
+       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->direction_input(chip, gpio);
        if (status == 0)
                clear_bit(FLAG_IS_OUT, &desc->flags);
+lose:
        return status;
 fail:
        spin_unlock_irqrestore(&gpio_lock, flags);
@@ -909,7 +959,9 @@ int gpio_direction_output(unsigned gpio, int value)
        gpio -= chip->base;
        if (gpio >= chip->ngpio)
                goto fail;
-       gpio_ensure_requested(desc);
+       status = gpio_ensure_requested(desc, gpio);
+       if (status < 0)
+               goto fail;
 
        /* now we know the gpio is valid and chip won't vanish */
 
@@ -917,9 +969,22 @@ int gpio_direction_output(unsigned gpio, int value)
 
        might_sleep_if(extra_checks && chip->can_sleep);
 
+       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->direction_output(chip, gpio, value);
        if (status == 0)
                set_bit(FLAG_IS_OUT, &desc->flags);
+lose:
        return status;
 fail:
        spin_unlock_irqrestore(&gpio_lock, flags);
index 04cb1d175df1782274bda4d9098d43937ae6f60c..81797ec9ab2917413ba28d166adb267726c8f177 100644 (file)
@@ -35,6 +35,10 @@ struct module;
  * @label: for diagnostics
  * @dev: optional device providing the GPIOs
  * @owner: helps prevent removal of modules exporting active GPIOs
+ * @request: optional hook for chip-specific activation, such as
+ *     enabling module power and clock; may sleep
+ * @free: optional hook for chip-specific deactivation, such as
+ *     disabling module power and clock; may sleep
  * @direction_input: configures signal "offset" as input, or returns error
  * @get: returns value for signal "offset"; for output signals this
  *     returns either the value actually sensed, or zero
@@ -67,6 +71,11 @@ struct gpio_chip {
        struct device           *dev;
        struct module           *owner;
 
+       int                     (*request)(struct gpio_chip *chip,
+                                               unsigned offset);
+       void                    (*free)(struct gpio_chip *chip,
+                                               unsigned offset);
+
        int                     (*direction_input)(struct gpio_chip *chip,
                                                unsigned offset);
        int                     (*get)(struct gpio_chip *chip,