diff options
-rw-r--r-- | arch/arm/mach-omap2/pm44xx.c | 6 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/plat/gpio.h | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-omap.c | 55 |
3 files changed, 48 insertions, 15 deletions
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c index cb40890..ee45244 100644 --- a/arch/arm/mach-omap2/pm44xx.c +++ b/arch/arm/mach-omap2/pm44xx.c @@ -117,6 +117,7 @@ void omap4_enter_sleep(unsigned int cpu, unsigned int power_state, bool suspend) int per_next_state = PWRDM_POWER_ON; int core_next_state = PWRDM_POWER_ON; int mpu_next_state = PWRDM_POWER_ON; + int ret; pwrdm_clear_all_prev_pwrst(cpu0_pwrdm); pwrdm_clear_all_prev_pwrst(mpu_pwrdm); @@ -129,7 +130,9 @@ void omap4_enter_sleep(unsigned int cpu, unsigned int power_state, bool suspend) core_next_state = pwrdm_read_next_pwrst(core_pwrdm); mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); - omap2_gpio_prepare_for_idle(omap4_device_next_state_off()); + ret = omap2_gpio_prepare_for_idle(omap4_device_next_state_off()); + if (ret) + goto abort_gpio; if (mpu_next_state < PWRDM_POWER_INACTIVE) { if (omap_dvfs_is_scaling(mpu_voltdm)) { @@ -236,6 +239,7 @@ abort_device_off: omap2_gpio_resume_after_idle(omap4_device_next_state_off()); +abort_gpio: return; } diff --git a/arch/arm/plat-omap/include/plat/gpio.h b/arch/arm/plat-omap/include/plat/gpio.h index b374a9e..0b1f7c5 100644 --- a/arch/arm/plat-omap/include/plat/gpio.h +++ b/arch/arm/plat-omap/include/plat/gpio.h @@ -213,7 +213,7 @@ struct omap_gpio_platform_data { struct omap_gpio_reg_offs *regs; }; -extern void omap2_gpio_prepare_for_idle(int off_mode); +extern int omap2_gpio_prepare_for_idle(int off_mode); extern void omap2_gpio_resume_after_idle(int off_mode); extern void omap_set_gpio_debounce(int gpio, int enable); extern void omap_set_gpio_debounce_time(int gpio, int enable); diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 26a5a7e..8fff8e1 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1385,21 +1385,21 @@ static int omap_gpio_pm_runtime_resume(struct device *dev) } #ifdef CONFIG_ARCH_OMAP2PLUS -static void omap2_gpio_set_edge_wakeup(struct gpio_bank *bank) +static int omap2_gpio_set_edge_wakeup(struct gpio_bank *bank) { - u32 level_low = 0; - u32 level_high = 0; + int ret = 0; u32 wkup_status = 0; + u32 datain; if (pm_runtime_get_sync(bank->dev) < 0) { dev_err(bank->dev, "%s: GPIO bank %d pm_runtime_get_sync " "failed\n", __func__, bank->id); - return; + return -EINVAL; } - level_low = __raw_readl(bank->base + + bank->context.leveldetect0 = __raw_readl(bank->base + bank->regs->leveldetect0); - level_high = __raw_readl(bank->base + + bank->context.leveldetect1 = __raw_readl(bank->base + bank->regs->leveldetect1); wkup_status = __raw_readl(bank->base + bank->regs->wkup_status); @@ -1413,16 +1413,34 @@ static void omap2_gpio_set_edge_wakeup(struct gpio_bank *bank) * expected to produce wakeup from low power. * even if they are set for level detection only. */ - __raw_writel(bank->context.edge_falling | (level_low & wkup_status), - (bank->base + bank->regs->fallingdetect)); - __raw_writel(bank->context.edge_rising | (level_high & wkup_status), - (bank->base + bank->regs->risingdetect)); + __raw_writel(bank->context.edge_falling | + (bank->context.leveldetect0 & wkup_status), + (bank->base + bank->regs->fallingdetect)); + __raw_writel(bank->context.edge_rising | + (bank->context.leveldetect1 & wkup_status), + (bank->base + bank->regs->risingdetect)); + __raw_writel(0, bank->base + bank->regs->leveldetect0); + __raw_writel(0, bank->base + bank->regs->leveldetect1); + + /* + * If a level interrupt is pending it will be lost since + * we just cleared it's enable bit. Detect and abort, + * the interrupt will be delivered when + * omap2_gpio_restore_edge_wakeup restores the level + * interrupt mask. + */ + datain = __raw_readl(bank->base + bank->regs->datain); + if ((datain & bank->context.leveldetect1) || + (~datain & bank->context.leveldetect0)) + ret = -EBUSY; if (pm_runtime_put_sync_suspend(bank->dev) < 0) { dev_err(bank->dev, "%s: GPIO bank %d pm_runtime_put_sync " "failed\n", __func__, bank->id); - return; + return -EINVAL; } + + return ret; } static void omap2_gpio_restore_edge_wakeup(struct gpio_bank *bank) @@ -1437,6 +1455,10 @@ static void omap2_gpio_restore_edge_wakeup(struct gpio_bank *bank) (bank->base + bank->regs->fallingdetect)); __raw_writel(bank->context.edge_rising, (bank->base + bank->regs->risingdetect)); + __raw_writel(bank->context.leveldetect0, + (bank->base + bank->regs->leveldetect0)); + __raw_writel(bank->context.leveldetect1, + (bank->base + bank->regs->leveldetect1)); if (pm_runtime_put_sync_suspend(bank->dev) < 0) { dev_err(bank->dev, "%s: GPIO bank %d pm_runtime_put_sync " @@ -1445,13 +1467,15 @@ static void omap2_gpio_restore_edge_wakeup(struct gpio_bank *bank) } } -void omap2_gpio_prepare_for_idle(int off_mode) +int omap2_gpio_prepare_for_idle(int off_mode) { + int ret = 0; struct gpio_bank *bank; list_for_each_entry(bank, &omap_gpio_list, node) { if (!bank->mod_usage || !bank->loses_context || !off_mode) { - omap2_gpio_set_edge_wakeup(bank); + if (omap2_gpio_set_edge_wakeup(bank)) + ret = -EBUSY; continue; } @@ -1460,6 +1484,11 @@ void omap2_gpio_prepare_for_idle(int off_mode) "pm_runtime_put_sync failed\n", __func__, bank->id); } + + if (ret) + omap2_gpio_resume_after_idle(off_mode); + + return ret; } void omap2_gpio_resume_after_idle(int off_mode) |