diff options
author | Colin Cross <ccross@android.com> | 2011-11-11 16:22:42 -0800 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2011-11-14 14:16:10 -0800 |
commit | d40caba356780995ae9be60a432a12cee4fe4dae (patch) | |
tree | 7ccff3d523b1a44a75b42beeaae5ab692c74ff81 /drivers/gpio | |
parent | 030f725173448cee5fc96a4a098b01ef10b075d5 (diff) | |
download | kernel_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.c | 44 |
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) |