[PATCH v3 2/3] gpio/omap: modify wake-up register with interrupt enable.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



OMAP4430 TRM chap. 25.4.5.2
To reduce dynamic consumption, an efficient idle scheme is based on the
following:
• An efficient local autoclock gating for each module
• The implementation of control sideband signals between the PRCM module
  and each module
This enhanced idle control allows clocks to be activated and deactivated
safely without requiring complex software management. The idle mode
request, idle acknowledge, and wake-up request are sideband signals
between the PRCM module and the general-purpose interface

OMAP4430 TRM chap. 25.4.6.2
There must be a correlation between the wake-up enable and interrupt
enable register. If a GPIO pin has a wake-up configured on it, it must
also have the corresponding interrupt enabled. Otherwise, it is possible
there is a wake-up event, but after exiting the IDLE state, no interrupt
is generated; the corresponding bit from the interrupt status register is
not cleared, and the module does not acknowledge a future idle request.

Up to now _set_gpio_triggering() is also handling the wake-up enable
register. According the TRM this should be in sync with the interrupt
enable. Wakeup is still enabled by default, since the module would not
wake from idle otherwise.
The enabled_wakeup_gpios was introduced to remember an explicit
_set_gpio_wakeup beyond a mask/unmask cycle. Calling the flag flag
disabled_wakeup_gpios would spare the problem of initializing it, but
feels very unnatural to read.

Wakeup functionality is completely untested, since the AM335x
lacks a IRQWAKEN register.

Signed-off-by: Andreas Fenkart <andreas.fenkart@xxxxxxxxxxxxxxxxxxx>
---
 drivers/gpio/gpio-omap.c |   68 ++++++++++++++++++++++++++++++----------------
 1 file changed, 45 insertions(+), 23 deletions(-)

diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 082919e..44a93be 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -57,6 +57,7 @@ struct gpio_bank {
 	struct irq_domain *domain;
 	u32 non_wakeup_gpios;
 	u32 enabled_non_wakeup_gpios;
+	u32 enabled_wakeup_gpios;
 	struct gpio_regs context;
 	u32 saved_datain;
 	u32 level_mask;
@@ -287,12 +288,6 @@ static inline void set_gpio_trigger(struct gpio_bank *bank, int gpio,
 		_gpio_rmw(base, bank->regs->fallingdetect,
 			  gpio_bit, trigger & IRQ_TYPE_EDGE_FALLING);
 
-	if (likely(!(bank->non_wakeup_gpios & gpio_bit))) {
-		bank->context.wake_en =
-			_gpio_rmw(base, bank->regs->wkup_en, gpio_bit,
-				  trigger != 0);
-	}
-
 	/* This part needs to be executed always for OMAP{34xx, 44xx} */
 	if (!bank->regs->irqctrl) {
 		/* On omap24xx proceed only when valid GPIO bit is set */
@@ -350,12 +345,13 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio,
 							unsigned trigger)
 {
 	void __iomem *reg = bank->base;
-	void __iomem *base = bank->base;
 	u32 l = 0;
 
-	if (bank->regs->leveldetect0 && bank->regs->wkup_en) {
+	if (bank->regs->leveldetect0) {
+		/* edge both flanks simultaneously / plus level */
 		set_gpio_trigger(bank, gpio, trigger);
 	} else if (bank->regs->irqctrl) {
+		/* edge single flank */
 		reg += bank->regs->irqctrl;
 
 		l = __raw_readl(reg);
@@ -370,6 +366,7 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio,
 
 		__raw_writel(l, reg);
 	} else if (bank->regs->edgectrl1) {
+		/* edge both flanks simultaneously */
 		if (gpio & 0x08)
 			reg += bank->regs->edgectrl2;
 		else
@@ -382,11 +379,6 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio,
 			l |= 2 << (gpio << 1);
 		if (trigger & IRQ_TYPE_EDGE_FALLING)
 			l |= 1 << (gpio << 1);
-
-		/* Enable wake-up during idle for dynamic tick */
-		bank->context.wake_en =
-			_gpio_rmw(base, bank->regs->wkup_en, 1 << gpio,
-				  trigger);
 		__raw_writel(l, reg);
 	}
 	return 0;
@@ -485,10 +477,19 @@ static void _disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
 
 static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable)
 {
+	u32 gpio_bit = GPIO_BIT(bank, gpio);
+	void __iomem *base = bank->base;
+
 	if (enable)
-		_enable_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
+		_enable_gpio_irqbank(bank, gpio_bit);
 	else
-		_disable_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
+		_disable_gpio_irqbank(bank, gpio_bit);
+
+	if (bank->enabled_wakeup_gpios & gpio_bit) {
+		/* Enable wake-up during idle for dynamic tick */
+		bank->context.wake_en =
+			_gpio_rmw(base, bank->regs->wkup_en, gpio_bit, enable);
+	}
 }
 
 /*
@@ -511,8 +512,27 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
 	}
 
 	spin_lock_irqsave(&bank->lock, flags);
-	bank->context.wake_en = _gpio_rmw(bank->base, bank->regs->wkup_en,
-					  gpio_bit, enable);
+
+	if (enable)
+		bank->enabled_wakeup_gpios |= gpio_bit;
+	else
+		bank->enabled_wakeup_gpios &= ~gpio_bit;
+
+	/*
+	 * OMAP4430 TRM, 25.4.6.2
+	 * If there is a wake-up event, but after exiting the IDLE
+	 * state, no interrupt is generated; the corresponding bit from
+	 * the interrupt status register is not cleared, and the
+	 * module does not acknowledge a future idle request.
+	 */
+	if (enable && !(bank->context.irqenable1 & gpio_bit))
+		dev_warn(bank->dev, "Wake-up/IRQ enable mismatch for GPIO%d\n",
+			 gpio);
+	else
+		bank->context.wake_en = _gpio_rmw(bank->base,
+						  bank->regs->wkup_en,
+						  gpio_bit, enable);
+
 	spin_unlock_irqrestore(&bank->lock, flags);
 
 	return 0;
@@ -525,6 +545,10 @@ static void _reset_gpio(struct gpio_bank *bank, int gpio)
 	_clear_gpio_irqstatus(bank, gpio);
 	_set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), IRQ_TYPE_NONE);
 	_clear_gpio_debounce(bank, gpio);
+
+	/* wakeup enabled by default */
+	if (!(bank->non_wakeup_gpios & GPIO_BIT(bank, gpio)))
+		bank->enabled_wakeup_gpios |= GPIO_BIT(bank, gpio);
 }
 
 /* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
@@ -554,6 +578,10 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
 	 */
 	_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
 
+	/* wakeup enabled by default */
+	if (!(bank->non_wakeup_gpios & (1 << offset)))
+		bank->enabled_wakeup_gpios |= 1 << offset;
+
 	if (bank->regs->pinctrl) {
 		/* Claim the pin for MPU */
 		_gpio_rmw(bank->base, bank->regs->pinctrl, 1 << offset, 1);
@@ -580,12 +608,6 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
 
 	spin_lock_irqsave(&bank->lock, flags);
 
-	if (bank->regs->wkup_en) {
-		/* Disable wake-up during idle for dynamic tick */
-		bank->context.wake_en =
-			_gpio_rmw(base, bank->regs->wkup_en, 1 << offset, 0);
-	}
-
 	bank->mod_usage &= ~(1 << offset);
 
 	if (bank->regs->ctrl && !bank->mod_usage) {
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux