aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-omap2/pm44xx.c6
-rw-r--r--arch/arm/plat-omap/include/plat/gpio.h2
-rw-r--r--drivers/gpio/gpio-omap.c55
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)