mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 13:22:23 +00:00
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:
parent
93a22f8b95
commit
35e8bb5175
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user