msm: gpio-v2: Add support for irq_domains

irq_domains are necessary to support interrupt chip drivers with
the Device Tree. In addition, by registering with irq_domains,
other irq chip drivers on the system will respect the virq
allocations made by this driver.

The msm-gpio specification requires two interrupt-cells. The
first specifies the irq chip hwirq, and the second is a default
irq type configuration for the interrupt. It's still possible to
override the default configuration with request_irq() if one
chooses to.

Change-Id: If2d3ce321f4c9ec83b0ee41244533696192e1d42
Signed-off-by: Michael Bohan <mbohan@codeaurora.org>
diff --git a/arch/arm/mach-msm/gpio-v2.c b/arch/arm/mach-msm/gpio-v2.c
index 7ada69a..b716a6c 100644
--- a/arch/arm/mach-msm/gpio-v2.c
+++ b/arch/arm/mach-msm/gpio-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -20,6 +20,8 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
 #include <linux/syscore_ops.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
 
 #include <asm/mach/irq.h>
 
@@ -165,6 +167,7 @@
 	DECLARE_BITMAP(enabled_irqs, NR_MSM_GPIOS);
 	DECLARE_BITMAP(wake_irqs, NR_MSM_GPIOS);
 	DECLARE_BITMAP(dual_edge_irqs, NR_MSM_GPIOS);
+	struct irq_domain domain;
 };
 
 static DEFINE_SPINLOCK(tlmm_lock);
@@ -223,6 +226,21 @@
 	return 0;
 }
 
+#ifdef CONFIG_OF
+static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
+	struct irq_domain *domain = &g_dev->domain;
+	return domain->irq_base + (offset - chip->base);
+}
+
+static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
+{
+	struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
+	struct irq_domain *domain = &g_dev->domain;
+	return irq - domain->irq_base;
+}
+#else
 static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
 {
 	return MSM_GPIO_TO_INT(offset - chip->base);
@@ -232,6 +250,7 @@
 {
 	return irq - MSM_GPIO_TO_INT(chip->base);
 }
+#endif
 
 static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
 {
@@ -673,6 +692,48 @@
 }
 EXPORT_SYMBOL(msm_gpio_install_direct_irq);
 
+#ifdef CONFIG_OF
+static int msm_gpio_domain_dt_translate(struct irq_domain *d,
+					struct device_node *controller,
+					const u32 *intspec,
+					unsigned int intsize,
+					unsigned long *out_hwirq,
+					unsigned int *out_type)
+{
+	if (d->of_node != controller)
+		return -EINVAL;
+	if (intsize != 2)
+		return -EINVAL;
+
+	/* hwirq value */
+	*out_hwirq = intspec[0];
+
+	/* irq flags */
+	*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+	return 0;
+}
+
+static struct irq_domain_ops msm_gpio_irq_domain_ops = {
+	.dt_translate = msm_gpio_domain_dt_translate,
+};
+
+int __init msm_gpio_of_init(struct device_node *node,
+			    struct device_node *parent)
+{
+	struct irq_domain *domain = &msm_gpio.domain;
+
+	domain->irq_base = irq_domain_find_free_range(0, NR_MSM_GPIOS);
+	domain->nr_irq = NR_MSM_GPIOS;
+	domain->of_node = of_node_get(node);
+	domain->priv = &msm_gpio;
+	domain->ops = &msm_gpio_irq_domain_ops;
+	irq_domain_add(domain);
+	pr_debug("%s: irq_base = %u\n", __func__, domain->irq_base);
+
+	return 0;
+}
+#endif
+
 MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
 MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs");
 MODULE_LICENSE("GPL v2");