Skip to content

Commit

Permalink
gpiolib: request/free hooks
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David Brownell authored and torvalds committed Oct 16, 2008
1 parent 93a22f8 commit 35e8bb5
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 13 deletions.
4 changes: 4 additions & 0 deletions Documentation/gpio.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
91 changes: 78 additions & 13 deletions drivers/gpio/gpiolib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;

Expand All @@ -760,22 +772,36 @@ 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) {
desc_set_label(desc, 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:
Expand All @@ -791,6 +817,7 @@ void gpio_free(unsigned gpio)
{
unsigned long flags;
struct gpio_desc *desc;
struct gpio_chip *chip;

might_sleep();

Expand All @@ -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);

Expand Down Expand Up @@ -871,17 +906,32 @@ 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 */

spin_unlock_irqrestore(&gpio_lock, flags);

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);
Expand Down Expand Up @@ -909,17 +959,32 @@ 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 */

spin_unlock_irqrestore(&gpio_lock, flags);

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);
Expand Down
9 changes: 9 additions & 0 deletions include/asm-generic/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 35e8bb5

Please sign in to comment.