Merge branch 'master' of /home/cbou/linux-2.6
[pandora-kernel.git] / arch / arm / plat-omap / gpio.c
index 8c78e4e..63e0943 100644 (file)
@@ -136,7 +136,6 @@ struct gpio_bank {
        u16 irq;
        u16 virtual_irq_start;
        int method;
-       u32 reserved_map;
 #if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
        u32 suspend_wakeup;
        u32 saved_wakeup;
@@ -149,7 +148,9 @@ struct gpio_bank {
        u32 saved_fallingdetect;
        u32 saved_risingdetect;
 #endif
+       u32 level_mask;
        spinlock_t lock;
+       struct gpio_chip chip;
 };
 
 #define METHOD_MPUIO           0
@@ -516,13 +517,13 @@ static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio,
        u32 gpio_bit = 1 << gpio;
 
        MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT0, gpio_bit,
-               trigger & __IRQT_LOWLVL);
+               trigger & IRQ_TYPE_LEVEL_LOW);
        MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT1, gpio_bit,
-               trigger & __IRQT_HIGHLVL);
+               trigger & IRQ_TYPE_LEVEL_HIGH);
        MOD_REG_BIT(OMAP24XX_GPIO_RISINGDETECT, gpio_bit,
-               trigger & __IRQT_RISEDGE);
+               trigger & IRQ_TYPE_EDGE_RISING);
        MOD_REG_BIT(OMAP24XX_GPIO_FALLINGDETECT, gpio_bit,
-               trigger & __IRQT_FALEDGE);
+               trigger & IRQ_TYPE_EDGE_FALLING);
 
        if (likely(!(bank->non_wakeup_gpios & gpio_bit))) {
                if (trigger != 0)
@@ -538,10 +539,9 @@ static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio,
                        bank->enabled_non_wakeup_gpios &= ~gpio_bit;
        }
 
-       /*
-        * FIXME: Possibly do 'set_irq_handler(j, handle_level_irq)' if only
-        * level triggering requested.
-        */
+       bank->level_mask =
+               __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0) |
+               __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1);
 }
 #endif
 
@@ -555,9 +555,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_RISEDGE)
+               if (trigger & IRQ_TYPE_EDGE_RISING)
                        l |= 1 << gpio;
-               else if (trigger & __IRQT_FALEDGE)
+               else if (trigger & IRQ_TYPE_EDGE_FALLING)
                        l &= ~(1 << gpio);
                else
                        goto bad;
@@ -567,9 +567,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_RISEDGE)
+               if (trigger & IRQ_TYPE_EDGE_RISING)
                        l |= 1 << gpio;
-               else if (trigger & __IRQT_FALEDGE)
+               else if (trigger & IRQ_TYPE_EDGE_FALLING)
                        l &= ~(1 << gpio);
                else
                        goto bad;
@@ -584,9 +584,9 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger)
                gpio &= 0x07;
                l = __raw_readl(reg);
                l &= ~(3 << (gpio << 1));
-               if (trigger & __IRQT_RISEDGE)
+               if (trigger & IRQ_TYPE_EDGE_RISING)
                        l |= 2 << (gpio << 1);
-               if (trigger & __IRQT_FALEDGE)
+               if (trigger & IRQ_TYPE_EDGE_FALLING)
                        l |= 1 << (gpio << 1);
                if (trigger)
                        /* Enable wake-up during idle for dynamic tick */
@@ -599,9 +599,9 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger)
        case METHOD_GPIO_730:
                reg += OMAP730_GPIO_INT_CONTROL;
                l = __raw_readl(reg);
-               if (trigger & __IRQT_RISEDGE)
+               if (trigger & IRQ_TYPE_EDGE_RISING)
                        l |= 1 << gpio;
-               else if (trigger & __IRQT_FALEDGE)
+               else if (trigger & IRQ_TYPE_EDGE_FALLING)
                        l &= ~(1 << gpio);
                else
                        goto bad;
@@ -652,6 +652,12 @@ static int gpio_irq_type(unsigned irq, unsigned type)
                irq_desc[irq].status |= type;
        }
        spin_unlock_irqrestore(&bank->lock, flags);
+
+       if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+               __set_irq_handler_unlocked(irq, handle_level_irq);
+       else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+               __set_irq_handler_unlocked(irq, handle_edge_irq);
+
        return retval;
 }
 
@@ -881,7 +887,7 @@ 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);
+       _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE);
 }
 
 /* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
@@ -903,24 +909,22 @@ int omap_request_gpio(int gpio)
 {
        struct gpio_bank *bank;
        unsigned long flags;
+       int status;
 
        if (check_gpio(gpio) < 0)
                return -EINVAL;
 
+       status = gpio_request(gpio, NULL);
+       if (status < 0)
+               return status;
+
        bank = get_gpio_bank(gpio);
        spin_lock_irqsave(&bank->lock, flags);
-       if (unlikely(bank->reserved_map & (1 << get_gpio_index(gpio)))) {
-               printk(KERN_ERR "omap-gpio: GPIO %d is already reserved!\n", gpio);
-               dump_stack();
-               spin_unlock_irqrestore(&bank->lock, flags);
-               return -1;
-       }
-       bank->reserved_map |= (1 << get_gpio_index(gpio));
 
        /* 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);
+       _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE);
 
 #ifdef CONFIG_ARCH_OMAP15XX
        if (bank->method == METHOD_GPIO_1510) {
@@ -945,10 +949,11 @@ void omap_free_gpio(int gpio)
                return;
        bank = get_gpio_bank(gpio);
        spin_lock_irqsave(&bank->lock, flags);
-       if (unlikely(!(bank->reserved_map & (1 << get_gpio_index(gpio))))) {
+       if (unlikely(!gpiochip_is_requested(&bank->chip,
+                               get_gpio_index(gpio)))) {
+               spin_unlock_irqrestore(&bank->lock, flags);
                printk(KERN_ERR "omap-gpio: GPIO %d wasn't reserved!\n", gpio);
                dump_stack();
-               spin_unlock_irqrestore(&bank->lock, flags);
                return;
        }
 #ifdef CONFIG_ARCH_OMAP16XX
@@ -965,9 +970,9 @@ void omap_free_gpio(int gpio)
                __raw_writel(1 << get_gpio_index(gpio), reg);
        }
 #endif
-       bank->reserved_map &= ~(1 << get_gpio_index(gpio));
        _reset_gpio(bank, gpio);
        spin_unlock_irqrestore(&bank->lock, flags);
+       gpio_free(gpio);
 }
 
 /*
@@ -1022,12 +1027,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
                        isr &= 0x0000ffff;
 
                if (cpu_class_is_omap2()) {
-                       level_mask =
-                               __raw_readl(bank->base +
-                                       OMAP24XX_GPIO_LEVELDETECT0) |
-                               __raw_readl(bank->base +
-                                       OMAP24XX_GPIO_LEVELDETECT1);
-                       level_mask &= enabled;
+                       level_mask = bank->level_mask & enabled;
                }
 
                /* clear edge sensitive interrupts before handler(s) are
@@ -1052,51 +1052,13 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
                gpio_irq = bank->virtual_irq_start;
                for (; isr != 0; isr >>= 1, gpio_irq++) {
                        struct irq_desc *d;
-                       int irq_mask;
+
                        if (!(isr & 1))
                                continue;
                        d = irq_desc + gpio_irq;
-                       /* 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_class_is_omap2()) {
-                       /* 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
@@ -1135,6 +1097,14 @@ static void gpio_unmask_irq(unsigned int irq)
 {
        unsigned int gpio = irq - IH_GPIO_BASE;
        struct gpio_bank *bank = get_irq_chip_data(irq);
+       unsigned int irq_mask = 1 << get_gpio_index(gpio);
+
+       /* For level-triggered GPIOs, the clearing must be done after
+        * the HW source is cleared, thus after the handler has run */
+       if (bank->level_mask & irq_mask) {
+               _set_gpio_irqenable(bank, gpio, 0);
+               _clear_gpio_irqstatus(bank, gpio);
+       }
 
        _set_gpio_irqenable(bank, gpio, 1);
 }
@@ -1266,6 +1236,53 @@ static inline void mpuio_init(void) {}
 
 /*---------------------------------------------------------------------*/
 
+/* REVISIT these are stupid implementations!  replace by ones that
+ * don't switch on METHOD_* and which mostly avoid spinlocks
+ */
+
+static int gpio_input(struct gpio_chip *chip, unsigned offset)
+{
+       struct gpio_bank *bank;
+       unsigned long flags;
+
+       bank = container_of(chip, struct gpio_bank, chip);
+       spin_lock_irqsave(&bank->lock, flags);
+       _set_gpio_direction(bank, offset, 1);
+       spin_unlock_irqrestore(&bank->lock, flags);
+       return 0;
+}
+
+static int gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       return omap_get_gpio_datain(chip->base + offset);
+}
+
+static int gpio_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+       struct gpio_bank *bank;
+       unsigned long flags;
+
+       bank = container_of(chip, struct gpio_bank, chip);
+       spin_lock_irqsave(&bank->lock, flags);
+       _set_gpio_dataout(bank, offset, value);
+       _set_gpio_direction(bank, offset, 0);
+       spin_unlock_irqrestore(&bank->lock, flags);
+       return 0;
+}
+
+static void gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       struct gpio_bank *bank;
+       unsigned long flags;
+
+       bank = container_of(chip, struct gpio_bank, chip);
+       spin_lock_irqsave(&bank->lock, flags);
+       _set_gpio_dataout(bank, offset, value);
+       spin_unlock_irqrestore(&bank->lock, flags);
+}
+
+/*---------------------------------------------------------------------*/
+
 static int initialized;
 #if !defined(CONFIG_ARCH_OMAP3)
 static struct clk * gpio_ick;
@@ -1293,6 +1310,7 @@ static struct lock_class_key gpio_lock_class;
 static int __init _omap_gpio_init(void)
 {
        int i;
+       int gpio = 0;
        struct gpio_bank *bank;
 #if defined(CONFIG_ARCH_OMAP3)
        char clk_name[11];
@@ -1423,7 +1441,6 @@ static int __init _omap_gpio_init(void)
                int j, gpio_count = 16;
 
                bank = &gpio_bank[i];
-               bank->reserved_map = 0;
                bank->base = IO_ADDRESS(bank->base);
                spin_lock_init(&bank->lock);
                if (bank_is_mpuio(bank))
@@ -1461,6 +1478,29 @@ static int __init _omap_gpio_init(void)
                        gpio_count = 32;
                }
 #endif
+
+               /* REVISIT eventually switch from OMAP-specific gpio structs
+                * over to the generic ones
+                */
+               bank->chip.direction_input = gpio_input;
+               bank->chip.get = gpio_get;
+               bank->chip.direction_output = gpio_output;
+               bank->chip.set = gpio_set;
+               if (bank_is_mpuio(bank)) {
+                       bank->chip.label = "mpuio";
+#ifdef CONFIG_ARCH_OMAP1
+                       bank->chip.dev = &omap_mpuio_device.dev;
+#endif
+                       bank->chip.base = OMAP_MPUIO(0);
+               } else {
+                       bank->chip.label = "gpio";
+                       bank->chip.base = gpio;
+                       gpio += gpio_count;
+               }
+               bank->chip.ngpio = gpio_count;
+
+               gpiochip_add(&bank->chip);
+
                for (j = bank->virtual_irq_start;
                     j < bank->virtual_irq_start + gpio_count; j++) {
                        lockdep_set_class(&irq_desc[j].lock, &gpio_lock_class);
@@ -1757,8 +1797,10 @@ static int dbg_gpio_show(struct seq_file *s, void *unused)
 
                for (j = 0; j < bankwidth; j++, gpio++, mask <<= 1) {
                        unsigned        irq, value, is_in, irqstat;
+                       const char      *label;
 
-                       if (!(bank->reserved_map & mask))
+                       label = gpiochip_is_requested(&bank->chip, j);
+                       if (!label)
                                continue;
 
                        irq = bank->virtual_irq_start + j;
@@ -1766,13 +1808,16 @@ static int dbg_gpio_show(struct seq_file *s, void *unused)
                        is_in = gpio_is_input(bank, mask);
 
                        if (bank_is_mpuio(bank))
-                               seq_printf(s, "MPUIO %2d: ", j);
+                               seq_printf(s, "MPUIO %2d ", j);
                        else
-                               seq_printf(s, "GPIO %3d: ", gpio);
-                       seq_printf(s, "%s %s",
+                               seq_printf(s, "GPIO %3d ", gpio);
+                       seq_printf(s, "(%10s): %s %s",
+                                       label,
                                        is_in ? "in " : "out",
                                        value ? "hi"  : "lo");
 
+/* FIXME for at least omap2, show pullup/pulldown state */
+
                        irqstat = irq_desc[irq].status;
                        if (is_in && ((bank->suspend_wakeup & mask)
                                        || irqstat & IRQ_TYPE_SENSE_MASK)) {
@@ -1795,10 +1840,10 @@ static int dbg_gpio_show(struct seq_file *s, void *unused)
                                        trigger = "high";
                                        break;
                                case IRQ_TYPE_NONE:
-                                       trigger = "(unspecified)";
+                                       trigger = "(?)";
                                        break;
                                }
-                               seq_printf(s, ", irq-%d %s%s",
+                               seq_printf(s, ", irq-%d %-8s%s",
                                                irq, trigger,
                                                (bank->suspend_wakeup & mask)
                                                        ? " wakeup" : "");