提交 35e8bb51 编写于 作者: D David Brownell 提交者: Linus Torvalds

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: NDavid Brownell <dbrownell@users.sourceforge.net>
Cc: "Magnus Damm" <magnus.damm@gmail.com>
Signed-off-by: NAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
上级 93a22f8b
...@@ -240,6 +240,10 @@ signal, or (b) something wrongly believes it's safe to remove drivers ...@@ -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 needed to manage a signal that's in active use. That is, requesting a
GPIO can serve as a kind of lock. 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 These two calls are optional because not not all current Linux platforms
offer such functionality in their GPIO support; a valid implementation offer such functionality in their GPIO support; a valid implementation
could return success for all gpio_request() calls. Unlike the other calls, 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) ...@@ -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 * when setting direction, and otherwise illegal. Until board setup code
* and drivers use explicit requests everywhere (which won't happen when * and drivers use explicit requests everywhere (which won't happen when
* those calls have no teeth) we can't avoid autorequesting. This nag * 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) { 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]"); desc_set_label(desc, "[auto]");
if (!try_module_get(desc->chip->owner)) /* caller must chip->request() w/o spinlock */
pr_err("GPIO-%d: module can't be gotten \n", if (chip->request)
(int)(desc - gpio_desc)); return 1;
} }
return 0;
} }
/* caller holds gpio_lock *OR* gpio is marked as requested */ /* caller holds gpio_lock *OR* gpio is marked as requested */
...@@ -752,6 +763,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove); ...@@ -752,6 +763,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove);
int gpio_request(unsigned gpio, const char *label) int gpio_request(unsigned gpio, const char *label)
{ {
struct gpio_desc *desc; struct gpio_desc *desc;
struct gpio_chip *chip;
int status = -EINVAL; int status = -EINVAL;
unsigned long flags; unsigned long flags;
...@@ -760,14 +772,15 @@ int gpio_request(unsigned gpio, const char *label) ...@@ -760,14 +772,15 @@ int gpio_request(unsigned gpio, const char *label)
if (!gpio_is_valid(gpio)) if (!gpio_is_valid(gpio))
goto done; goto done;
desc = &gpio_desc[gpio]; desc = &gpio_desc[gpio];
if (desc->chip == NULL) chip = desc->chip;
if (chip == NULL)
goto done; goto done;
if (!try_module_get(desc->chip->owner)) if (!try_module_get(chip->owner))
goto done; goto done;
/* NOTE: gpio_request() can be called in early boot, /* 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) { if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
...@@ -775,7 +788,20 @@ int gpio_request(unsigned gpio, const char *label) ...@@ -775,7 +788,20 @@ int gpio_request(unsigned gpio, const char *label)
status = 0; status = 0;
} else { } else {
status = -EBUSY; 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: done:
...@@ -791,6 +817,7 @@ void gpio_free(unsigned gpio) ...@@ -791,6 +817,7 @@ void gpio_free(unsigned gpio)
{ {
unsigned long flags; unsigned long flags;
struct gpio_desc *desc; struct gpio_desc *desc;
struct gpio_chip *chip;
might_sleep(); might_sleep();
...@@ -804,9 +831,17 @@ void gpio_free(unsigned gpio) ...@@ -804,9 +831,17 @@ void gpio_free(unsigned gpio)
spin_lock_irqsave(&gpio_lock, flags); spin_lock_irqsave(&gpio_lock, flags);
desc = &gpio_desc[gpio]; 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); desc_set_label(desc, NULL);
module_put(desc->chip->owner); module_put(desc->chip->owner);
clear_bit(FLAG_REQUESTED, &desc->flags);
} else } else
WARN_ON(extra_checks); WARN_ON(extra_checks);
...@@ -871,7 +906,9 @@ int gpio_direction_input(unsigned gpio) ...@@ -871,7 +906,9 @@ int gpio_direction_input(unsigned gpio)
gpio -= chip->base; gpio -= chip->base;
if (gpio >= chip->ngpio) if (gpio >= chip->ngpio)
goto fail; 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 */ /* now we know the gpio is valid and chip won't vanish */
...@@ -879,9 +916,22 @@ int gpio_direction_input(unsigned gpio) ...@@ -879,9 +916,22 @@ int gpio_direction_input(unsigned gpio)
might_sleep_if(extra_checks && chip->can_sleep); 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); status = chip->direction_input(chip, gpio);
if (status == 0) if (status == 0)
clear_bit(FLAG_IS_OUT, &desc->flags); clear_bit(FLAG_IS_OUT, &desc->flags);
lose:
return status; return status;
fail: fail:
spin_unlock_irqrestore(&gpio_lock, flags); spin_unlock_irqrestore(&gpio_lock, flags);
...@@ -909,7 +959,9 @@ int gpio_direction_output(unsigned gpio, int value) ...@@ -909,7 +959,9 @@ int gpio_direction_output(unsigned gpio, int value)
gpio -= chip->base; gpio -= chip->base;
if (gpio >= chip->ngpio) if (gpio >= chip->ngpio)
goto fail; 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 */ /* now we know the gpio is valid and chip won't vanish */
...@@ -917,9 +969,22 @@ int gpio_direction_output(unsigned gpio, int value) ...@@ -917,9 +969,22 @@ int gpio_direction_output(unsigned gpio, int value)
might_sleep_if(extra_checks && chip->can_sleep); 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); status = chip->direction_output(chip, gpio, value);
if (status == 0) if (status == 0)
set_bit(FLAG_IS_OUT, &desc->flags); set_bit(FLAG_IS_OUT, &desc->flags);
lose:
return status; return status;
fail: fail:
spin_unlock_irqrestore(&gpio_lock, flags); spin_unlock_irqrestore(&gpio_lock, flags);
......
...@@ -35,6 +35,10 @@ struct module; ...@@ -35,6 +35,10 @@ struct module;
* @label: for diagnostics * @label: for diagnostics
* @dev: optional device providing the GPIOs * @dev: optional device providing the GPIOs
* @owner: helps prevent removal of modules exporting active 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 * @direction_input: configures signal "offset" as input, or returns error
* @get: returns value for signal "offset"; for output signals this * @get: returns value for signal "offset"; for output signals this
* returns either the value actually sensed, or zero * returns either the value actually sensed, or zero
...@@ -67,6 +71,11 @@ struct gpio_chip { ...@@ -67,6 +71,11 @@ struct gpio_chip {
struct device *dev; struct device *dev;
struct module *owner; 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, int (*direction_input)(struct gpio_chip *chip,
unsigned offset); unsigned offset);
int (*get)(struct gpio_chip *chip, int (*get)(struct gpio_chip *chip,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册