gpiolib: request/free hooks

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>
This commit is contained in:
David Brownell
2008-10-15 22:03:16 -07:00
committed by Linus Torvalds
parent 93a22f8b95
commit 35e8bb5175
3 changed files with 91 additions and 13 deletions

View 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);