aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpio-omap.c
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2011-11-01 18:47:18 -0700
committerColin Cross <ccross@android.com>2011-11-03 14:13:45 -0700
commit8ab5eec38348bf7521c09bc33b05aec5eea66c62 (patch)
tree8350e9c93b2ab91d5782f670dd7734a0d62f1f34 /drivers/gpio/gpio-omap.c
parent203f3a9979dfa0983c5462086e17dc5c30a5625e (diff)
downloadkernel_samsung_tuna-8ab5eec38348bf7521c09bc33b05aec5eea66c62.zip
kernel_samsung_tuna-8ab5eec38348bf7521c09bc33b05aec5eea66c62.tar.gz
kernel_samsung_tuna-8ab5eec38348bf7521c09bc33b05aec5eea66c62.tar.bz2
ARM: omap: gpio: set wakeupenable bits when entering idle modes
THe pad wakeupenable bits are required to wake through a gpio when in OFF or OSWR. If the wakeupenable bits are set when the controller is not in idle, a PRCM and a GPIO interrupt will occur for every interrupt. Set the wakeupenable bits for every active gpio interrupt when entering any gpio idle mode. Also sets the irq chip flag IRQCHIP_MASK_ON_SUSPEND to cause the irq pm code to mask all non-wake gpios in suspend, which will ensure the wakeupenable bit is not set on non-wake gpios. Change-Id: I03b8d954a65f8cc47388319e983decf03308448f Signed-off-by: Colin Cross <ccross@android.com>
Diffstat (limited to 'drivers/gpio/gpio-omap.c')
-rw-r--r--drivers/gpio/gpio-omap.c47
1 files changed, 47 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 8fff8e1..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)
@@ -1473,6 +1516,8 @@ int omap2_gpio_prepare_for_idle(int off_mode)
struct gpio_bank *bank;
list_for_each_entry(bank, &omap_gpio_list, node) {
+ omap2_gpio_set_wakeupenables(bank);
+
if (!bank->mod_usage || !bank->loses_context || !off_mode) {
if (omap2_gpio_set_edge_wakeup(bank))
ret = -EBUSY;
@@ -1496,6 +1541,8 @@ void omap2_gpio_resume_after_idle(int off_mode)
struct gpio_bank *bank;
list_for_each_entry(bank, &omap_gpio_list, node) {
+ omap2_gpio_clear_wakeupenables(bank);
+
if (!bank->mod_usage || !bank->loses_context || !off_mode) {
omap2_gpio_restore_edge_wakeup(bank);
continue;