aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpio-omap.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-omap.c')
-rw-r--r--drivers/gpio/gpio-omap.c192
1 files changed, 130 insertions, 62 deletions
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index d91a5aa..5b45bc1 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -21,6 +21,7 @@
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/bitops.h>
#include <mach/hardware.h>
#include <asm/irq.h>
@@ -29,6 +30,8 @@
#include <asm/mach/irq.h>
#include <plat/omap-pm.h>
+#include "../mux.h"
+
static LIST_HEAD(omap_gpio_list);
struct gpio_regs {
@@ -46,6 +49,7 @@ struct gpio_regs {
u32 debounce_en;
u32 edge_falling;
u32 edge_rising;
+ u32 pad_set_wakeupenable;
};
struct gpio_bank {
@@ -81,6 +85,8 @@ struct gpio_bank {
void (*set_dataout)(struct gpio_bank *bank, int gpio, int enable);
struct omap_gpio_reg_offs *regs;
+
+ struct omap_mux *mux[32];
};
static void omap_gpio_mod_init(struct gpio_bank *bank);
@@ -761,6 +767,7 @@ static struct irq_chip gpio_irq_chip = {
.irq_unmask = gpio_unmask_irq,
.irq_set_type = gpio_irq_type,
.irq_set_wake = gpio_wake_enable,
+ .flags = IRQCHIP_MASK_ON_SUSPEND,
};
/*---------------------------------------------------------------------*/
@@ -941,6 +948,8 @@ static struct lock_class_key gpio_lock_class;
static void omap_gpio_mod_init(struct gpio_bank *bank)
{
+ int i;
+
if (bank->width == 32) {
u32 clr_all = 0; /* clear all the bits */
u32 set_all = 0xFFFFFFFF; /* set all the bits */
@@ -1036,6 +1045,11 @@ static void omap_gpio_mod_init(struct gpio_bank *bank)
ULPD_CAM_CLK_CTRL);
}
}
+
+ for (i = 0; i < bank->width; i++) {
+ int gpio = irq_to_gpio(bank->virtual_irq_start + i);
+ bank->mux[i] = omap_mux_get_gpio(gpio);
+ }
}
static __init void
@@ -1271,6 +1285,35 @@ static int omap_gpio_resume(struct device *dev)
#ifdef CONFIG_ARCH_OMAP2PLUS
static void omap_gpio_save_context(struct gpio_bank *bank);
static void omap_gpio_restore_context(struct gpio_bank *bank);
+
+static void omap2_gpio_set_wakeupenables(struct gpio_bank *bank)
+{
+ unsigned long pad_wakeup;
+ int i;
+
+ bank->context.pad_set_wakeupenable = 0;
+
+ pad_wakeup = __raw_readl(bank->base + bank->regs->irqenable);
+
+ for_each_set_bit(i, &pad_wakeup, bank->width) {
+ if (!omap_mux_get_wakeupenable(bank->mux[i])) {
+ bank->context.pad_set_wakeupenable |= BIT(i);
+ omap_mux_set_wakeupenable(bank->mux[i]);
+ }
+ }
+}
+
+static void omap2_gpio_clear_wakeupenables(struct gpio_bank *bank)
+{
+ unsigned long pad_wakeup;
+ int i;
+
+ pad_wakeup = bank->context.pad_set_wakeupenable;
+
+ for_each_set_bit(i, &pad_wakeup, bank->width)
+ omap_mux_clear_wakeupenable(bank->mux[i]);
+}
+
#endif
static int omap_gpio_pm_runtime_suspend(struct device *dev)
@@ -1385,100 +1428,125 @@ static int omap_gpio_pm_runtime_resume(struct device *dev)
}
#ifdef CONFIG_ARCH_OMAP2PLUS
-void omap2_gpio_set_edge_wakeup(void)
+static int omap2_gpio_set_edge_wakeup(struct gpio_bank *bank)
{
- struct gpio_bank *bank;
+ int ret = 0;
+ u32 wkup_status = 0;
+ u32 datain;
- list_for_each_entry(bank, &omap_gpio_list, node) {
- u32 level_low = 0;
- u32 level_high = 0;
- u32 wkup_status = 0;
+ 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 -EINVAL;
+ }
- 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;
- }
+ bank->context.leveldetect0 = __raw_readl(bank->base +
+ bank->regs->leveldetect0);
+ bank->context.leveldetect1 = __raw_readl(bank->base +
+ bank->regs->leveldetect1);
+ wkup_status = __raw_readl(bank->base +
+ bank->regs->wkup_status);
+ bank->context.edge_falling = __raw_readl(bank->base +
+ bank->regs->fallingdetect);
+ bank->context.edge_rising = __raw_readl(bank->base +
+ bank->regs->risingdetect);
- level_low = __raw_readl(bank->base +
- bank->regs->leveldetect0);
- level_high = __raw_readl(bank->base +
- bank->regs->leveldetect1);
- wkup_status = __raw_readl(bank->base +
- bank->regs->wkup_status);
- bank->context.edge_falling = __raw_readl(bank->base +
- bank->regs->fallingdetect);
- bank->context.edge_rising = __raw_readl(bank->base +
- bank->regs->risingdetect);
+ /*
+ * Set edge trigger for all gpio's that are
+ * expected to produce wakeup from low power.
+ * even if they are set for level detection only.
+ */
+ __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);
- /*
- * Set edge trigger for all gpio's that are
- * 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));
-
- 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;
- }
+ /*
+ * 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 -EINVAL;
}
+
+ return ret;
}
-void omap2_gpio_restore_edge_wakeup(void)
+static void omap2_gpio_restore_edge_wakeup(struct gpio_bank *bank)
{
- struct gpio_bank *bank;
-
- list_for_each_entry(bank, &omap_gpio_list, node) {
- /* restore edge setting */
- 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;
- }
+ 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;
+ }
- __raw_writel(bank->context.edge_falling,
- (bank->base + bank->regs->fallingdetect));
- __raw_writel(bank->context.edge_rising,
- (bank->base + bank->regs->risingdetect));
+ __raw_writel(bank->context.edge_falling,
+ (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 "
- "failed\n", __func__, bank->id);
- return;
- }
+ 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;
}
}
-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;
- if (!off_mode)
- return;
-
list_for_each_entry(bank, &omap_gpio_list, node) {
- if (!bank->mod_usage || !bank->loses_context)
+ omap2_gpio_set_wakeupenables(bank);
+
+ if (!bank->mod_usage || !bank->loses_context || !off_mode) {
+ if (omap2_gpio_set_edge_wakeup(bank))
+ ret = -EBUSY;
continue;
+ }
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);
}
+
+ if (ret)
+ omap2_gpio_resume_after_idle(off_mode);
+
+ return ret;
}
-void omap2_gpio_resume_after_idle(void)
+void omap2_gpio_resume_after_idle(int off_mode)
{
struct gpio_bank *bank;
list_for_each_entry(bank, &omap_gpio_list, node) {
- if (!bank->mod_usage || !bank->loses_context)
+ omap2_gpio_clear_wakeupenables(bank);
+
+ if (!bank->mod_usage || !bank->loses_context || !off_mode) {
+ omap2_gpio_restore_edge_wakeup(bank);
continue;
+ }
if (pm_runtime_get_sync(bank->dev) < 0)
dev_err(bank->dev, "%s: GPIO bank %d "