aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2011-11-11 16:22:42 -0800
committerColin Cross <ccross@android.com>2011-11-14 14:16:10 -0800
commitd40caba356780995ae9be60a432a12cee4fe4dae (patch)
tree7ccff3d523b1a44a75b42beeaae5ab692c74ff81 /drivers/gpio
parent030f725173448cee5fc96a4a098b01ef10b075d5 (diff)
downloadkernel_samsung_tuna-d40caba356780995ae9be60a432a12cee4fe4dae.zip
kernel_samsung_tuna-d40caba356780995ae9be60a432a12cee4fe4dae.tar.gz
kernel_samsung_tuna-d40caba356780995ae9be60a432a12cee4fe4dae.tar.bz2
ARM: omap4: gpio: abort suspend if a level wake interrupt is pending
If a level wake interrupt arrives after disable_irq has been called but before suspend, the interrupt will be masked. The omap gpio driver erases all triggering information when masking an interrupt. Track the triggering state of gpio interrupts, and abort suspend if a masked wakeup interrupt is active when suspending. Change-Id: I117b592d21c455074796deaea8148edf0947eb35 Signed-off-by: Colin Cross <ccross@android.com>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/gpio-omap.c44
1 files changed, 39 insertions, 5 deletions
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index a5ca770..159b3d9 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -87,6 +87,11 @@ struct gpio_bank {
u32 width;
u16 id;
+ u32 type_leveldetect0;
+ u32 type_leveldetect1;
+ u32 type_risingedge;
+ u32 type_fallingedge;
+
void (*set_dataout)(struct gpio_bank *bank, int gpio, int enable);
struct omap_gpio_reg_offs *regs;
@@ -378,7 +383,23 @@ static int gpio_irq_type(struct irq_data *d, unsigned type)
return -EINVAL;
spin_lock_irqsave(&bank->lock, flags);
+
retval = _set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), type);
+
+ bank->type_leveldetect0 &= ~GPIO_BIT(bank, gpio);
+ bank->type_leveldetect1 &= ~GPIO_BIT(bank, gpio);
+ bank->type_fallingedge &= ~GPIO_BIT(bank, gpio);
+ bank->type_risingedge &= ~GPIO_BIT(bank, gpio);
+
+ if (type & IRQ_TYPE_LEVEL_LOW)
+ bank->type_leveldetect0 |= GPIO_BIT(bank, gpio);
+ if (type & IRQ_TYPE_LEVEL_HIGH)
+ bank->type_leveldetect1 |= GPIO_BIT(bank, gpio);
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ bank->type_fallingedge |= GPIO_BIT(bank, gpio);
+ if (type & IRQ_TYPE_EDGE_RISING)
+ bank->type_risingedge |= GPIO_BIT(bank, gpio);
+
spin_unlock_irqrestore(&bank->lock, flags);
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
@@ -1432,11 +1453,13 @@ static int omap_gpio_pm_runtime_resume(struct device *dev)
}
#ifdef CONFIG_ARCH_OMAP2PLUS
-static int omap2_gpio_set_edge_wakeup(struct gpio_bank *bank)
+static int omap2_gpio_set_edge_wakeup(struct gpio_bank *bank, bool suspend)
{
int ret = 0;
u32 wkup_status = 0;
u32 datain;
+ u32 mask;
+ u32 active;
if (pm_runtime_get_sync(bank->dev) < 0) {
dev_err(bank->dev, "%s: GPIO bank %d pm_runtime_get_sync "
@@ -1477,9 +1500,20 @@ static int omap2_gpio_set_edge_wakeup(struct gpio_bank *bank)
* interrupt mask.
*/
datain = __raw_readl(bank->base + bank->regs->datain);
- if ((datain & bank->context.ew_leveldetect1) ||
- (~datain & bank->context.ew_leveldetect0))
+ if (suspend)
+ mask = bank->suspend_wakeup;
+ else
+ mask = wkup_status;
+
+ active = (datain & bank->type_leveldetect1 & mask) |
+ (~datain & bank->type_leveldetect0 & mask);
+
+ if (active) {
+ if (suspend)
+ pr_info("%s: aborted suspend due to gpio %d\n",
+ __func__, bank->id * bank->width + __ffs(active));
ret = -EBUSY;
+ }
if (pm_runtime_put_sync_suspend(bank->dev) < 0) {
dev_err(bank->dev, "%s: GPIO bank %d pm_runtime_put_sync "
@@ -1514,7 +1548,7 @@ static void omap2_gpio_restore_edge_wakeup(struct gpio_bank *bank)
}
}
-int omap2_gpio_prepare_for_idle(int off_mode)
+int omap2_gpio_prepare_for_idle(int off_mode, bool suspend)
{
int ret = 0;
struct gpio_bank *bank;
@@ -1522,7 +1556,7 @@ int omap2_gpio_prepare_for_idle(int off_mode)
list_for_each_entry(bank, &omap_gpio_list, node) {
omap2_gpio_set_wakeupenables(bank);
- if (omap2_gpio_set_edge_wakeup(bank))
+ if (omap2_gpio_set_edge_wakeup(bank, suspend))
ret = -EBUSY;
if (bank->mod_usage && bank->loses_context && off_mode)