瀏覽代碼

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>
David Brownell 16 年之前
父節點
當前提交
35e8bb5175
共有 3 個文件被更改,包括 91 次插入13 次删除
  1. 4 0
      Documentation/gpio.txt
  2. 78 13
      drivers/gpio/gpiolib.c
  3. 9 0
      include/asm-generic/gpio.h

+ 4 - 0
Documentation/gpio.txt

@@ -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,

+ 78 - 13
drivers/gpio/gpiolib.c

@@ -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))
-			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 */
 /* 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)
 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)
 	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)
 		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)
 {
 {
 	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)
 	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)
 	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)
 
 
 	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)
 	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)
 
 
 	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);

+ 9 - 0
include/asm-generic/gpio.h

@@ -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 {
 	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,