ARM: OMAP: /sys/kernel/debug/omap_gpio
authorDavid Brownell <dbrownell@users.sourceforge.net>
Thu, 7 Dec 2006 01:13:53 +0000 (17:13 -0800)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Sat, 5 May 2007 09:54:23 +0000 (10:54 +0100)
Add some GPIO debug support:  /sys/kernel/debug/omap_gpio dumps the state
of all GPIOs that have been claimed, including basic IRQ info if relevant.
Tested on 24xx, 16xx.

Includes minor bugfixes:  recording IRQ trigger mode (this should probably
be a genirq patch), adding missing space to non-wakeup warning

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/plat-omap/gpio.c

index ec0e2f1..0059f19 100644 (file)
@@ -535,6 +535,10 @@ static int gpio_irq_type(unsigned irq, unsigned type)
        bank = get_gpio_bank(gpio);
        spin_lock(&bank->lock);
        retval = _set_gpio_triggering(bank, get_gpio_index(gpio), type);
+       if (retval == 0) {
+               irq_desc[irq].status &= ~IRQ_TYPE_SENSE_MASK;
+               irq_desc[irq].status |= type;
+       }
        spin_unlock(&bank->lock);
        return retval;
 }
@@ -701,7 +705,7 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
                spin_lock(&bank->lock);
                if (enable) {
                        if (bank->non_wakeup_gpios & (1 << gpio)) {
-                               printk(KERN_ERR "Unable to enable wakeup on"
+                               printk(KERN_ERR "Unable to enable wakeup on "
                                                "non-wakeup GPIO%d\n",
                                                (bank - gpio_bank) * 32 + gpio);
                                spin_unlock(&bank->lock);
@@ -1359,3 +1363,127 @@ EXPORT_SYMBOL(omap_set_gpio_dataout);
 EXPORT_SYMBOL(omap_get_gpio_datain);
 
 arch_initcall(omap_gpio_sysinit);
+
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static int gpio_is_input(struct gpio_bank *bank, int mask)
+{
+       void __iomem *reg = bank->base;
+
+       switch (bank->method) {
+       case METHOD_MPUIO:
+               reg += OMAP_MPUIO_IO_CNTL;
+               break;
+       case METHOD_GPIO_1510:
+               reg += OMAP1510_GPIO_DIR_CONTROL;
+               break;
+       case METHOD_GPIO_1610:
+               reg += OMAP1610_GPIO_DIRECTION;
+               break;
+       case METHOD_GPIO_730:
+               reg += OMAP730_GPIO_DIR_CONTROL;
+               break;
+       case METHOD_GPIO_24XX:
+               reg += OMAP24XX_GPIO_OE;
+               break;
+       }
+       return __raw_readl(reg) & mask;
+}
+
+
+static int dbg_gpio_show(struct seq_file *s, void *unused)
+{
+       unsigned        i, j, gpio;
+
+       for (i = 0, gpio = 0; i < gpio_bank_count; i++) {
+               struct gpio_bank        *bank = gpio_bank + i;
+               unsigned                bankwidth = 16;
+               u32                     mask = 1;
+
+               if (!cpu_is_omap24xx() && bank->method == METHOD_MPUIO)
+                       gpio = OMAP_MPUIO(0);
+               else if (cpu_is_omap24xx() || cpu_is_omap730())
+                       bankwidth = 32;
+
+               for (j = 0; j < bankwidth; j++, gpio++, mask <<= 1) {
+                       unsigned        irq, value, is_in, irqstat;
+
+                       if (!(bank->reserved_map & mask))
+                               continue;
+
+                       irq = bank->virtual_irq_start + j;
+                       value = omap_get_gpio_datain(gpio);
+                       is_in = gpio_is_input(bank, mask);
+
+                       if (!cpu_is_omap24xx() && bank->method == METHOD_MPUIO)
+                               seq_printf(s, "MPUIO %2d: ", j);
+                       else
+                               seq_printf(s, "GPIO %3d: ", gpio);
+                       seq_printf(s, "%s %s",
+                                       is_in ? "in " : "out",
+                                       value ? "hi"  : "lo");
+
+                       irqstat = irq_desc[irq].status;
+                       if (is_in && ((bank->suspend_wakeup & mask)
+                                       || irqstat & IRQ_TYPE_SENSE_MASK)) {
+                               char    *trigger = NULL;
+
+                               switch (irqstat & IRQ_TYPE_SENSE_MASK) {
+                               case IRQ_TYPE_EDGE_FALLING:
+                                       trigger = "falling";
+                                       break;
+                               case IRQ_TYPE_EDGE_RISING:
+                                       trigger = "rising";
+                                       break;
+                               case IRQ_TYPE_EDGE_BOTH:
+                                       trigger = "bothedge";
+                                       break;
+                               case IRQ_TYPE_LEVEL_LOW:
+                                       trigger = "low";
+                                       break;
+                               case IRQ_TYPE_LEVEL_HIGH:
+                                       trigger = "high";
+                                       break;
+                               case IRQ_TYPE_NONE:
+                                       trigger = "(unspecified)";
+                                       break;
+                               }
+                               seq_printf(s, ", irq-%d %s%s",
+                                               irq, trigger,
+                                               (bank->suspend_wakeup & mask)
+                                                       ? " wakeup" : "");
+                       }
+                       seq_printf(s, "\n");
+               }
+
+               if (!cpu_is_omap24xx() && bank->method == METHOD_MPUIO) {
+                       seq_printf(s, "\n");
+                       gpio = 0;
+               }
+       }
+       return 0;
+}
+
+static int dbg_gpio_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dbg_gpio_show, inode->u.generic_ip/*i_private*/);
+}
+
+static const struct file_operations debug_fops = {
+       .open           = dbg_gpio_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int __init omap_gpio_debuginit(void)
+{
+       (void) debugfs_create_file("omap_gpio", S_IRUGO, NULL, NULL, &debug_fops);
+       return 0;
+}
+late_initcall(omap_gpio_debuginit);
+#endif