[ARM] tegra: gpio: Add suspend and wake support
authorColin Cross <ccross@android.com>
Wed, 7 Apr 2010 19:59:42 +0000 (12:59 -0700)
committerColin Cross <ccross@android.com>
Fri, 22 Oct 2010 01:12:01 +0000 (18:12 -0700)
Includes checkpatch fixes and TEGRA_NR_GPIOS changes from
Mike Rapoport <mike@compulab.co.il>

Signed-off-by: Colin Cross <ccross@android.com>
arch/arm/mach-tegra/gpio.c
arch/arm/mach-tegra/include/mach/gpio.h

index fe78fba..0775265 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <linux/init.h>
 #include <linux/irq.h>
+#include <linux/interrupt.h>
 
 #include <linux/io.h>
 #include <linux/gpio.h>
@@ -60,6 +61,13 @@ struct tegra_gpio_bank {
        int bank;
        int irq;
        spinlock_t lvl_lock[4];
+#ifdef CONFIG_PM
+       u32 cnf[4];
+       u32 out[4];
+       u32 oe[4];
+       u32 int_enb[4];
+       u32 int_lvl[4];
+#endif
 };
 
 
@@ -131,7 +139,7 @@ static struct gpio_chip tegra_gpio_chip = {
        .direction_output       = tegra_gpio_direction_output,
        .set                    = tegra_gpio_set,
        .base                   = 0,
-       .ngpio                  = ARCH_NR_GPIOS,
+       .ngpio                  = TEGRA_NR_GPIOS,
 };
 
 static void tegra_gpio_irq_ack(unsigned int irq)
@@ -244,6 +252,76 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
 
 }
 
+#ifdef CONFIG_PM
+void tegra_gpio_resume(void)
+{
+       unsigned long flags;
+       int b, p, i;
+
+       local_irq_save(flags);
+
+       for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) {
+               struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
+
+               for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
+                       unsigned int gpio = (b<<5) | (p<<3);
+                       __raw_writel(bank->cnf[p], GPIO_CNF(gpio));
+                       __raw_writel(bank->out[p], GPIO_OUT(gpio));
+                       __raw_writel(bank->oe[p], GPIO_OE(gpio));
+                       __raw_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio));
+                       __raw_writel(bank->int_enb[p], GPIO_INT_ENB(gpio));
+               }
+       }
+
+       local_irq_restore(flags);
+
+       for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) {
+               struct irq_desc *desc = irq_to_desc(i);
+               if (!desc || (desc->status & IRQ_WAKEUP))
+                       continue;
+               enable_irq(i);
+       }
+}
+
+void tegra_gpio_suspend(void)
+{
+       unsigned long flags;
+       int b, p, i;
+
+       for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) {
+               struct irq_desc *desc = irq_to_desc(i);
+               if (!desc)
+                       continue;
+               if (desc->status & IRQ_WAKEUP) {
+                       int gpio = i - INT_GPIO_BASE;
+                       pr_debug("gpio %d.%d is wakeup\n", gpio/8, gpio&7);
+                       continue;
+               }
+               disable_irq(i);
+       }
+
+       local_irq_save(flags);
+       for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) {
+               struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
+
+               for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
+                       unsigned int gpio = (b<<5) | (p<<3);
+                       bank->cnf[p] = __raw_readl(GPIO_CNF(gpio));
+                       bank->out[p] = __raw_readl(GPIO_OUT(gpio));
+                       bank->oe[p] = __raw_readl(GPIO_OE(gpio));
+                       bank->int_enb[p] = __raw_readl(GPIO_INT_ENB(gpio));
+                       bank->int_lvl[p] = __raw_readl(GPIO_INT_LVL(gpio));
+               }
+       }
+       local_irq_restore(flags);
+}
+
+static int tegra_gpio_wake_enable(unsigned int irq, unsigned int enable)
+{
+       struct tegra_gpio_bank *bank = get_irq_chip_data(irq);
+       return set_irq_wake(bank->irq, enable);
+}
+#endif
 
 static struct irq_chip tegra_gpio_irq_chip = {
        .name           = "GPIO",
@@ -251,6 +329,9 @@ static struct irq_chip tegra_gpio_irq_chip = {
        .mask           = tegra_gpio_irq_mask,
        .unmask         = tegra_gpio_irq_unmask,
        .set_type       = tegra_gpio_irq_set_type,
+#ifdef CONFIG_PM
+       .set_wake       = tegra_gpio_wake_enable,
+#endif
 };
 
 
@@ -274,7 +355,7 @@ static int __init tegra_gpio_init(void)
 
        gpiochip_add(&tegra_gpio_chip);
 
-       for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + ARCH_NR_GPIOS); i++) {
+       for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) {
                bank = &tegra_gpio_banks[GPIO_BANK(irq_to_gpio(i))];
 
                lockdep_set_class(&irq_desc[i].lock, &gpio_lock_class);
@@ -312,15 +393,16 @@ static int dbg_gpio_show(struct seq_file *s, void *unused)
        for (i = 0; i < 7; i++) {
                for (j = 0; j < 4; j++) {
                        int gpio = tegra_gpio_compose(i, j, 0);
-                       seq_printf(s, "%d:%d %02x %02x %02x %02x %02x %02x %06x\n",
-                              i, j,
-                              __raw_readl(GPIO_CNF(gpio)),
-                              __raw_readl(GPIO_OE(gpio)),
-                              __raw_readl(GPIO_OUT(gpio)),
-                              __raw_readl(GPIO_IN(gpio)),
-                              __raw_readl(GPIO_INT_STA(gpio)),
-                              __raw_readl(GPIO_INT_ENB(gpio)),
-                              __raw_readl(GPIO_INT_LVL(gpio)));
+                       seq_printf(s,
+                               "%d:%d %02x %02x %02x %02x %02x %02x %06x\n",
+                               i, j,
+                               __raw_readl(GPIO_CNF(gpio)),
+                               __raw_readl(GPIO_OE(gpio)),
+                               __raw_readl(GPIO_OUT(gpio)),
+                               __raw_readl(GPIO_IN(gpio)),
+                               __raw_readl(GPIO_INT_STA(gpio)),
+                               __raw_readl(GPIO_INT_ENB(gpio)),
+                               __raw_readl(GPIO_INT_LVL(gpio)));
                }
        }
        return 0;
index 540e822..e31f486 100644 (file)
@@ -22,7 +22,7 @@
 
 #include <mach/irqs.h>
 
-#define ARCH_NR_GPIOS          INT_GPIO_NR
+#define TEGRA_NR_GPIOS         INT_GPIO_NR
 
 #include <asm-generic/gpio.h>
 
@@ -35,7 +35,7 @@
 
 static inline int gpio_to_irq(unsigned int gpio)
 {
-       if (gpio < ARCH_NR_GPIOS)
+       if (gpio < TEGRA_NR_GPIOS)
                return INT_GPIO_BASE + gpio;
        return -EINVAL;
 }