fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / arm / plat-omap / gpio.c
index b4d5b9e..4f2fd55 100644 (file)
@@ -11,7 +11,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/config.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/sched.h>
@@ -95,6 +94,8 @@
 #define OMAP24XX_GPIO_SYSCONFIG                0x0010
 #define OMAP24XX_GPIO_SYSSTATUS                0x0014
 #define OMAP24XX_GPIO_IRQSTATUS1       0x0018
+#define OMAP24XX_GPIO_IRQSTATUS2       0x0028
+#define OMAP24XX_GPIO_IRQENABLE2       0x002c
 #define OMAP24XX_GPIO_IRQENABLE1       0x001c
 #define OMAP24XX_GPIO_CTRL             0x0030
 #define OMAP24XX_GPIO_OE               0x0034
 #define OMAP24XX_GPIO_CLEARDATAOUT     0x0090
 #define OMAP24XX_GPIO_SETDATAOUT       0x0094
 
-#define OMAP_MPUIO_MASK                (~OMAP_MAX_GPIO_LINES & 0xff)
-
 struct gpio_bank {
        void __iomem *base;
        u16 irq;
@@ -174,7 +173,7 @@ static int gpio_bank_count;
 static inline struct gpio_bank *get_gpio_bank(int gpio)
 {
 #ifdef CONFIG_ARCH_OMAP15XX
-       if (cpu_is_omap1510()) {
+       if (cpu_is_omap15xx()) {
                if (OMAP_GPIO_IS_MPUIO(gpio))
                        return &gpio_bank[0];
                return &gpio_bank[1];
@@ -217,13 +216,15 @@ static inline int gpio_valid(int gpio)
 {
        if (gpio < 0)
                return -1;
+#ifndef CONFIG_ARCH_OMAP24XX
        if (OMAP_GPIO_IS_MPUIO(gpio)) {
-               if ((gpio & OMAP_MPUIO_MASK) > 16)
+               if (gpio >= OMAP_MAX_GPIO_LINES + 16)
                        return -1;
                return 0;
        }
+#endif
 #ifdef CONFIG_ARCH_OMAP15XX
-       if (cpu_is_omap1510() && gpio < 16)
+       if (cpu_is_omap15xx() && gpio < 16)
                return 0;
 #endif
 #if defined(CONFIG_ARCH_OMAP16XX)
@@ -402,14 +403,14 @@ static inline void set_24xx_gpio_triggering(void __iomem *base, int gpio, int tr
        u32 gpio_bit = 1 << gpio;
 
        MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT0, gpio_bit,
-               trigger & IRQT_LOW);
+               trigger & __IRQT_LOWLVL);
        MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT1, gpio_bit,
-               trigger & IRQT_HIGH);
+               trigger & __IRQT_HIGHLVL);
        MOD_REG_BIT(OMAP24XX_GPIO_RISINGDETECT, gpio_bit,
-               trigger & IRQT_RISING);
+               trigger & __IRQT_RISEDGE);
        MOD_REG_BIT(OMAP24XX_GPIO_FALLINGDETECT, gpio_bit,
-               trigger & IRQT_FALLING);
-       /* FIXME: Possibly do 'set_irq_handler(j, do_level_IRQ)' if only level
+               trigger & __IRQT_FALEDGE);
+       /* FIXME: Possibly do 'set_irq_handler(j, handle_level_irq)' if only level
         * triggering requested. */
 }
 
@@ -422,9 +423,9 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger)
        case METHOD_MPUIO:
                reg += OMAP_MPUIO_GPIO_INT_EDGE;
                l = __raw_readl(reg);
-               if (trigger == IRQT_RISING)
+               if (trigger & __IRQT_RISEDGE)
                        l |= 1 << gpio;
-               else if (trigger == IRQT_FALLING)
+               else if (trigger & __IRQT_FALEDGE)
                        l &= ~(1 << gpio);
                else
                        goto bad;
@@ -432,9 +433,9 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger)
        case METHOD_GPIO_1510:
                reg += OMAP1510_GPIO_INT_CONTROL;
                l = __raw_readl(reg);
-               if (trigger == IRQT_RISING)
+               if (trigger & __IRQT_RISEDGE)
                        l |= 1 << gpio;
-               else if (trigger == IRQT_FALLING)
+               else if (trigger & __IRQT_FALEDGE)
                        l &= ~(1 << gpio);
                else
                        goto bad;
@@ -446,20 +447,21 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger)
                        reg += OMAP1610_GPIO_EDGE_CTRL1;
                gpio &= 0x07;
                /* We allow only edge triggering, i.e. two lowest bits */
-               if (trigger & ~IRQT_BOTHEDGE)
+               if (trigger & (__IRQT_LOWLVL | __IRQT_HIGHLVL))
                        BUG();
-               /* NOTE: knows __IRQT_{FAL,RIS}EDGE match OMAP hardware */
-               trigger &= 0x03;
                l = __raw_readl(reg);
                l &= ~(3 << (gpio << 1));
-               l |= trigger << (gpio << 1);
+               if (trigger & __IRQT_RISEDGE)
+                       l |= 2 << (gpio << 1);
+               if (trigger & __IRQT_FALEDGE)
+                       l |= 1 << (gpio << 1);
                break;
        case METHOD_GPIO_730:
                reg += OMAP730_GPIO_INT_CONTROL;
                l = __raw_readl(reg);
-               if (trigger == IRQT_RISING)
+               if (trigger & __IRQT_RISEDGE)
                        l |= 1 << gpio;
-               else if (trigger == IRQT_FALLING)
+               else if (trigger & __IRQT_FALEDGE)
                        l &= ~(1 << gpio);
                else
                        goto bad;
@@ -491,7 +493,9 @@ static int gpio_irq_type(unsigned irq, unsigned type)
        if (check_gpio(gpio) < 0)
                return -EINVAL;
 
-       if (type & (__IRQT_LOWLVL|__IRQT_HIGHLVL|IRQT_PROBE))
+       if (type & IRQT_PROBE)
+               return -EINVAL;
+       if (!cpu_is_omap24xx() && (type & (__IRQT_LOWLVL|__IRQT_HIGHLVL)))
                return -EINVAL;
 
        bank = get_gpio_bank(gpio);
@@ -527,6 +531,10 @@ static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
                return;
        }
        __raw_writel(gpio_mask, reg);
+
+       /* Workaround for clearing DSP GPIO interrupts to allow retention */
+       if (cpu_is_omap2420())
+               __raw_writel(gpio_mask, bank->base + OMAP24XX_GPIO_IRQSTATUS2);
 }
 
 static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio)
@@ -534,6 +542,49 @@ static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio)
        _clear_gpio_irqbank(bank, 1 << get_gpio_index(gpio));
 }
 
+static u32 _get_gpio_irqbank_mask(struct gpio_bank *bank)
+{
+       void __iomem *reg = bank->base;
+       int inv = 0;
+       u32 l;
+       u32 mask;
+
+       switch (bank->method) {
+       case METHOD_MPUIO:
+               reg += OMAP_MPUIO_GPIO_MASKIT;
+               mask = 0xffff;
+               inv = 1;
+               break;
+       case METHOD_GPIO_1510:
+               reg += OMAP1510_GPIO_INT_MASK;
+               mask = 0xffff;
+               inv = 1;
+               break;
+       case METHOD_GPIO_1610:
+               reg += OMAP1610_GPIO_IRQENABLE1;
+               mask = 0xffff;
+               break;
+       case METHOD_GPIO_730:
+               reg += OMAP730_GPIO_INT_MASK;
+               mask = 0xffffffff;
+               inv = 1;
+               break;
+       case METHOD_GPIO_24XX:
+               reg += OMAP24XX_GPIO_IRQENABLE1;
+               mask = 0xffffffff;
+               break;
+       default:
+               BUG();
+               return 0;
+       }
+
+       l = __raw_readl(reg);
+       if (inv)
+               l = ~l;
+       l &= mask;
+       return l;
+}
+
 static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enable)
 {
        void __iomem *reg = bank->base;
@@ -617,6 +668,14 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
        }
 }
 
+static void _reset_gpio(struct gpio_bank *bank, int gpio)
+{
+       _set_gpio_direction(bank, get_gpio_index(gpio), 1);
+       _set_gpio_irqenable(bank, gpio, 0);
+       _clear_gpio_irqstatus(bank, gpio);
+       _set_gpio_triggering(bank, get_gpio_index(gpio), IRQT_NOEDGE);
+}
+
 /* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
 static int gpio_wake_enable(unsigned int irq, unsigned int enable)
 {
@@ -627,9 +686,7 @@ static int gpio_wake_enable(unsigned int irq, unsigned int enable)
        if (check_gpio(gpio) < 0)
                return -ENODEV;
        bank = get_gpio_bank(gpio);
-       spin_lock(&bank->lock);
        retval = _set_gpio_wakeup(bank, get_gpio_index(gpio), enable);
-       spin_unlock(&bank->lock);
 
        return retval;
 }
@@ -651,7 +708,9 @@ int omap_request_gpio(int gpio)
        }
        bank->reserved_map |= (1 << get_gpio_index(gpio));
 
-       /* Set trigger to none. You need to enable the trigger after request_irq */
+       /* Set trigger to none. You need to enable the desired trigger with
+        * request_irq() or set_irq_type().
+        */
        _set_gpio_triggering(bank, get_gpio_index(gpio), IRQT_NOEDGE);
 
 #ifdef CONFIG_ARCH_OMAP15XX
@@ -711,9 +770,7 @@ void omap_free_gpio(int gpio)
        }
 #endif
        bank->reserved_map &= ~(1 << get_gpio_index(gpio));
-       _set_gpio_direction(bank, get_gpio_index(gpio), 1);
-       _set_gpio_irqenable(bank, gpio, 0);
-       _clear_gpio_irqstatus(bank, gpio);
+       _reset_gpio(bank, gpio);
        spin_unlock(&bank->lock);
 }
 
@@ -726,17 +783,18 @@ void omap_free_gpio(int gpio)
  * line's interrupt handler has been run, we may miss some nested
  * interrupts.
  */
-static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc,
-                            struct pt_regs *regs)
+static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
 {
        void __iomem *isr_reg = NULL;
        u32 isr;
        unsigned int gpio_irq;
        struct gpio_bank *bank;
+       u32 retrigger = 0;
+       int unmasked = 0;
 
        desc->chip->ack(irq);
 
-       bank = (struct gpio_bank *) desc->data;
+       bank = get_irq_data(irq);
        if (bank->method == METHOD_MPUIO)
                isr_reg = bank->base + OMAP_MPUIO_GPIO_INT;
 #ifdef CONFIG_ARCH_OMAP15XX
@@ -755,26 +813,108 @@ static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc,
        if (bank->method == METHOD_GPIO_24XX)
                isr_reg = bank->base + OMAP24XX_GPIO_IRQSTATUS1;
 #endif
-
        while(1) {
-               isr = __raw_readl(isr_reg);
-               _enable_gpio_irqbank(bank, isr, 0);
-               _clear_gpio_irqbank(bank, isr);
-               _enable_gpio_irqbank(bank, isr, 1);
-               desc->chip->unmask(irq);
+               u32 isr_saved, level_mask = 0;
+               u32 enabled;
+
+               enabled = _get_gpio_irqbank_mask(bank);
+               isr_saved = isr = __raw_readl(isr_reg) & enabled;
+
+               if (cpu_is_omap15xx() && (bank->method == METHOD_MPUIO))
+                       isr &= 0x0000ffff;
+
+               if (cpu_is_omap24xx()) {
+                       level_mask =
+                               __raw_readl(bank->base +
+                                       OMAP24XX_GPIO_LEVELDETECT0) |
+                               __raw_readl(bank->base +
+                                       OMAP24XX_GPIO_LEVELDETECT1);
+                       level_mask &= enabled;
+               }
+
+               /* clear edge sensitive interrupts before handler(s) are
+               called so that we don't miss any interrupt occurred while
+               executing them */
+               _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 0);
+               _clear_gpio_irqbank(bank, isr_saved & ~level_mask);
+               _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 1);
+
+               /* if there is only edge sensitive GPIO pin interrupts
+               configured, we could unmask GPIO bank interrupt immediately */
+               if (!level_mask && !unmasked) {
+                       unmasked = 1;
+                       desc->chip->unmask(irq);
+               }
 
+               isr |= retrigger;
+               retrigger = 0;
                if (!isr)
                        break;
 
                gpio_irq = bank->virtual_irq_start;
                for (; isr != 0; isr >>= 1, gpio_irq++) {
-                       struct irqdesc *d;
+                       struct irq_desc *d;
+                       int irq_mask;
                        if (!(isr & 1))
                                continue;
                        d = irq_desc + gpio_irq;
-                       desc_handle_irq(gpio_irq, d, regs);
+                       /* Don't run the handler if it's already running
+                        * or was disabled lazely.
+                        */
+                       if (unlikely((d->depth ||
+                                     (d->status & IRQ_INPROGRESS)))) {
+                               irq_mask = 1 <<
+                                       (gpio_irq - bank->virtual_irq_start);
+                               /* The unmasking will be done by
+                                * enable_irq in case it is disabled or
+                                * after returning from the handler if
+                                * it's already running.
+                                */
+                               _enable_gpio_irqbank(bank, irq_mask, 0);
+                               if (!d->depth) {
+                                       /* Level triggered interrupts
+                                        * won't ever be reentered
+                                        */
+                                       BUG_ON(level_mask & irq_mask);
+                                       d->status |= IRQ_PENDING;
+                               }
+                               continue;
+                       }
+
+                       desc_handle_irq(gpio_irq, d);
+
+                       if (unlikely((d->status & IRQ_PENDING) && !d->depth)) {
+                               irq_mask = 1 <<
+                                       (gpio_irq - bank->virtual_irq_start);
+                               d->status &= ~IRQ_PENDING;
+                               _enable_gpio_irqbank(bank, irq_mask, 1);
+                               retrigger |= irq_mask;
+                       }
                }
+
+               if (cpu_is_omap24xx()) {
+                       /* clear level sensitive interrupts after handler(s) */
+                       _enable_gpio_irqbank(bank, isr_saved & level_mask, 0);
+                       _clear_gpio_irqbank(bank, isr_saved & level_mask);
+                       _enable_gpio_irqbank(bank, isr_saved & level_mask, 1);
+               }
+
        }
+       /* if bank has any level sensitive GPIO pin interrupt
+       configured, we must unmask the bank interrupt only after
+       handler(s) are executed in order to avoid spurious bank
+       interrupt */
+       if (!unmasked)
+               desc->chip->unmask(irq);
+
+}
+
+static void gpio_irq_shutdown(unsigned int irq)
+{
+       unsigned int gpio = irq - IH_GPIO_BASE;
+       struct gpio_bank *bank = get_gpio_bank(gpio);
+
+       _reset_gpio(bank, gpio);
 }
 
 static void gpio_ack_irq(unsigned int irq)
@@ -823,7 +963,9 @@ static void mpuio_unmask_irq(unsigned int irq)
        _set_gpio_irqenable(bank, gpio, 1);
 }
 
-static struct irqchip gpio_irq_chip = {
+static struct irq_chip gpio_irq_chip = {
+       .name           = "GPIO",
+       .shutdown       = gpio_irq_shutdown,
        .ack            = gpio_ack_irq,
        .mask           = gpio_mask_irq,
        .unmask         = gpio_unmask_irq,
@@ -831,10 +973,11 @@ static struct irqchip gpio_irq_chip = {
        .set_wake       = gpio_wake_enable,
 };
 
-static struct irqchip mpuio_irq_chip = {
+static struct irq_chip mpuio_irq_chip = {
+       .name   = "MPUIO",
        .ack    = mpuio_ack_irq,
        .mask   = mpuio_mask_irq,
-       .unmask = mpuio_unmask_irq
+       .unmask = mpuio_unmask_irq
 };
 
 static int initialized;
@@ -848,7 +991,7 @@ static int __init _omap_gpio_init(void)
 
        initialized = 1;
 
-       if (cpu_is_omap1510()) {
+       if (cpu_is_omap15xx()) {
                gpio_ick = clk_get(NULL, "arm_gpio_ck");
                if (IS_ERR(gpio_ick))
                        printk("Could not get arm_gpio_ck\n");
@@ -862,14 +1005,14 @@ static int __init _omap_gpio_init(void)
                else
                        clk_enable(gpio_ick);
                gpio_fck = clk_get(NULL, "gpios_fck");
-               if (IS_ERR(gpio_ick))
+               if (IS_ERR(gpio_fck))
                        printk("Could not get gpios_fck\n");
                else
                        clk_enable(gpio_fck);
        }
 
 #ifdef CONFIG_ARCH_OMAP15XX
-       if (cpu_is_omap1510()) {
+       if (cpu_is_omap15xx()) {
                printk(KERN_INFO "OMAP1510 GPIO hardware\n");
                gpio_bank_count = 2;
                gpio_bank = gpio_bank_1510;
@@ -949,7 +1092,7 @@ static int __init _omap_gpio_init(void)
                                set_irq_chip(j, &mpuio_irq_chip);
                        else
                                set_irq_chip(j, &gpio_irq_chip);
-                       set_irq_handler(j, do_simple_IRQ);
+                       set_irq_handler(j, handle_simple_irq);
                        set_irq_flags(j, IRQF_VALID);
                }
                set_irq_chained_handler(bank->irq, gpio_irq_handler);
@@ -1021,8 +1164,8 @@ static int omap_gpio_resume(struct sys_device *dev)
                        wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA;
                        break;
                case METHOD_GPIO_24XX:
-                       wake_clear = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA;
-                       wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA;
+                       wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA;
+                       wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA;
                        break;
                default:
                        continue;