Subject: [PATCH v2 1/4] irq: Get rid of irq_domain_for_each_hwirq().

From: David Daney <[email-masked]>
Date: Wed, 14 Dec 2011 18:32:07 -0800

From: David Daney <[email-masked]>

It is not used anywhere in the kernel.

Signed-off-by: David Daney <[email-masked]>
---
 include/linux/irqdomain.h |    3 ---
 1 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 99834e58..0914a54 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -81,9 +81,6 @@ static inline unsigned int irq_domain_to_irq(struct irq_domain *d,
 	return d->irq_base + hwirq - d->hwirq_base;
 }
 
-#define irq_domain_for_each_hwirq(d, hw) \
-	for (hw = d->hwirq_base; hw < d->hwirq_base + d->nr_irq; hw++)
-
 #define irq_domain_for_each_irq(d, hw, irq) \
 	for (hw = d->hwirq_base, irq = irq_domain_to_irq(d, hw); \
 	     hw < d->hwirq_base + d->nr_irq; \
-- 
1.7.2.3

---

From: David Daney <[email-masked]>
Date: Wed, 14 Dec 2011 18:32:06 -0800

Back in early Nov. I send the first version of this patch set.  Now
things are heating up again in the world of irq_domain, so I wanted to
try to get some closure on the issues I had.  The Octeon patch is
included here to show how I am using irq_domain, but is part of a much
larger effort to merge Octeon device tree support.

The basic problem I am attempting to solve is using irq domains when
there is a 'non-linear' mapping of hwirq <--> irq within a domain.
Octeon has a single set of irq numbers that is used across two
different implementations of the interrupt controller as well as more
than 10 different SOCs all which use different subsets of the irq
number space.  The result is that the hwirq to irq mapping function
contains many gaps and discontinuities, it is really quite random.

The existing irq domain infrastructure assumes a continuous linear
mapping of hwirq to irq that can be encapsulated by the irq_base,
hwirq_base and nr_irq elements of struct irq_domain.  This is not
suitable for the Octeon implementation.

The gist of my change is to add an optional iterator function to
irq_domain_ops which knows how to iterate over the irq numbers in a
given domain.  For simple linear domains (those currently supported),
we iterate using the current method based on irq_base, hwirq_base and
nr_irq.

Summary of the patches:

1) Get rid of some unused code to make subsequent changes simpler.

2) Cleanup the data type used by various hwirq functions and users.

3) Add the irq iterator, and fix up the ARM GIC code to use it instead
of the current irq_domain_for_each_irq().

4) Add the Octeon users of the interface.

In an earlier exchange, Rob Herring had said:

   ... Handling sparse irqs is a potentially common problem, so we
   should address that in the core irqdomain code.

Which is what this patch set is doing.

There was a suggestion that perhaps having .to_irq() return a magic
value if there was no mapping would also work.  However I prefer this
approach as it separates the concepts of iteration and mapping of irq
numbers.

Please comment.

David Daney (4):
  irq: Get rid of irq_domain_for_each_hwirq().
  irq/of/ARM: Make irq_domain hwirq type consistent throughout the
    kernel.
  irq/of/ARM: Enhance irq iteration capability of irq_domain code.
  MIPS: Octeon: Add irq_create_of_mapping() and GPIO interrupts.

 arch/arm/common/gic.c                |   34 +++--
 arch/mips/Kconfig                    |    1 +
 arch/mips/cavium-octeon/octeon-irq.c |  259 +++++++++++++++++++++++++++++++++-
 include/linux/irqdomain.h            |   23 ++--
 kernel/irq/irqdomain.c               |   88 ++++++++----
 5 files changed, 354 insertions(+), 51 deletions(-)

-- 
1.7.2.3

---

From: David Daney <[email-masked]>
Date: Wed, 14 Dec 2011 18:32:08 -0800

There is a mixture of unsigned long and unsigned int being used with
hwirq and hwirq_base.  Change it so we use unsigned int everywhere.

Signed-off-by: David Daney <[email-masked]>
---
 arch/arm/common/gic.c     |    2 +-
 include/linux/irqdomain.h |    6 +++---
 kernel/irq/irqdomain.c    |    8 ++++----
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index b2dc2dd..171061f 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -625,7 +625,7 @@ static void __init gic_pm_init(struct gic_chip_data *gic)
 static int gic_irq_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)
+				       unsigned int *out_hwirq, unsigned int *out_type)
 {
 	if (d->of_node != controller)
 		return -EINVAL;
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 0914a54..16ba5a9 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -31,12 +31,12 @@ struct irq_domain;
  *                the hardware irq number and linux irq type value.
  */
 struct irq_domain_ops {
-	unsigned int (*to_irq)(struct irq_domain *d, unsigned long hwirq);
+	unsigned int (*to_irq)(struct irq_domain *d, unsigned int hwirq);
 
 #ifdef CONFIG_OF
 	int (*dt_translate)(struct irq_domain *d, struct device_node *node,
 			    const u32 *intspec, unsigned int intsize,
-			    unsigned long *out_hwirq, unsigned int *out_type);
+			    unsigned int *out_hwirq, unsigned int *out_type);
 #endif /* CONFIG_OF */
 };
 
@@ -72,7 +72,7 @@ struct irq_domain {
  * be overridden if the irq_domain implements a .to_irq() hook.
  */
 static inline unsigned int irq_domain_to_irq(struct irq_domain *d,
-					     unsigned long hwirq)
+					     unsigned int hwirq)
 {
 	if (d->ops->to_irq)
 		return d->ops->to_irq(d, hwirq);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 200ce83..7bae422 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -20,7 +20,7 @@ static DEFINE_MUTEX(irq_domain_mutex);
 void irq_domain_add(struct irq_domain *domain)
 {
 	struct irq_data *d;
-	int hwirq, irq;
+	unsigned int hwirq, irq;
 
 	/*
 	 * This assumes that the irq_domain owner has already allocated
@@ -54,7 +54,7 @@ void irq_domain_add(struct irq_domain *domain)
 void irq_domain_del(struct irq_domain *domain)
 {
 	struct irq_data *d;
-	int hwirq, irq;
+	unsigned int hwirq, irq;
 
 	mutex_lock(&irq_domain_mutex);
 	list_del(&domain->list);
@@ -82,7 +82,7 @@ unsigned int irq_create_of_mapping(struct device_node *controller,
 				   const u32 *intspec, unsigned int intsize)
 {
 	struct irq_domain *domain;
-	unsigned long hwirq;
+	unsigned int hwirq;
 	unsigned int irq, type;
 	int rc = -EINVAL;
 
@@ -129,7 +129,7 @@ EXPORT_SYMBOL_GPL(irq_dispose_mapping);
 int irq_domain_simple_dt_translate(struct irq_domain *d,
 			    struct device_node *controller,
 			    const u32 *intspec, unsigned int intsize,
-			    unsigned long *out_hwirq, unsigned int *out_type)
+			    unsigned int *out_hwirq, unsigned int *out_type)
 {
 	if (d->of_node != controller)
 		return -EINVAL;
-- 
1.7.2.3

---

From: David Daney <[email-masked]>
Date: Wed, 14 Dec 2011 18:32:09 -0800

Not all irq_domain have linear sequences of irq numbers, so add hooks
for domain specific iteration of the irqs in the domain.

There is an optional function in irq_domain_ops (each_irq) that iterates over the
irqs in the domain calling a callback function on each one.  If it is
not present, the new helper functions irq_domain_each_irq() and
irq_domain_each_hwirq() iterate using the existing semantics.

arch/arm/common/gic.c had to be modified to use the new iteration
method as part of the change.

Signed-off-by: David Daney <[email-masked]>
---
 arch/arm/common/gic.c     |   32 ++++++++++-------
 include/linux/irqdomain.h |   14 +++++---
 kernel/irq/irqdomain.c    |   84 ++++++++++++++++++++++++++++++++------------
 3 files changed, 89 insertions(+), 41 deletions(-)

diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 171061f..2e091b7 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -345,6 +345,24 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
 	irq_set_chained_handler(irq, gic_handle_cascade_irq);
 }
 
+static int __init gic_dist_init_domain_cb(struct irq_domain *domain,
+					  unsigned int irq,
+					  unsigned int hwirq)
+{
+	if (hwirq < 32) {
+		irq_set_percpu_devid(irq);
+		irq_set_chip_and_handler(irq, &gic_chip,
+					 handle_percpu_devid_irq);
+		set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
+	} else {
+		irq_set_chip_and_handler(irq, &gic_chip,
+					 handle_fasteoi_irq);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+	irq_set_chip_data(irq, gic);
+	return 0;
+}
+
 static void __init gic_dist_init(struct gic_chip_data *gic)
 {
 	unsigned int i, irq;
@@ -392,19 +410,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
 	/*
 	 * Setup the Linux IRQ subsystem.
 	 */
-	irq_domain_for_each_irq(domain, i, irq) {
-		if (i < 32) {
-			irq_set_percpu_devid(irq);
-			irq_set_chip_and_handler(irq, &gic_chip,
-						 handle_percpu_devid_irq);
-			set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
-		} else {
-			irq_set_chip_and_handler(irq, &gic_chip,
-						 handle_fasteoi_irq);
-			set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
-		}
-		irq_set_chip_data(irq, gic);
-	}
+	irq_domain_each_irq(domain, gic_dist_init_domain_cb);
 
 	writel_relaxed(1, base + GIC_DIST_CTRL);
 }
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 16ba5a9..68d031b 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -27,12 +27,16 @@ struct irq_domain;
  * @to_irq: (optional) given a local hardware irq number, return the linux
  *          irq number.  If to_irq is not implemented, then the irq_domain
  *          will use this translation: irq = (domain->irq_base + hwirq)
+ * @each_irq: (optional) call the cb function for each irq that is a
+ *            member of the domain.
  * @dt_translate: Given a device tree node and interrupt specifier, decode
  *                the hardware irq number and linux irq type value.
  */
 struct irq_domain_ops {
 	unsigned int (*to_irq)(struct irq_domain *d, unsigned int hwirq);
-
+	int (*each_irq)(struct irq_domain *d,
+			int (*cb)(struct irq_domain *d,
+				  unsigned int irq, unsigned int hwirq));
 #ifdef CONFIG_OF
 	int (*dt_translate)(struct irq_domain *d, struct device_node *node,
 			    const u32 *intspec, unsigned int intsize,
@@ -81,10 +85,10 @@ static inline unsigned int irq_domain_to_irq(struct irq_domain *d,
 	return d->irq_base + hwirq - d->hwirq_base;
 }
 
-#define irq_domain_for_each_irq(d, hw, irq) \
-	for (hw = d->hwirq_base, irq = irq_domain_to_irq(d, hw); \
-	     hw < d->hwirq_base + d->nr_irq; \
-	     hw++, irq = irq_domain_to_irq(d, hw))
+extern int irq_domain_each_irq(struct irq_domain *d,
+			       int (*cb)(struct irq_domain *d,
+					 unsigned int irq,
+					 unsigned int hwirq));
 
 extern void irq_domain_add(struct irq_domain *domain);
 extern void irq_domain_del(struct irq_domain *domain);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 7bae422..c9cec12 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -9,6 +9,25 @@
 static LIST_HEAD(irq_domain_list);
 static DEFINE_MUTEX(irq_domain_mutex);
 
+static int irq_domain_add_per_irq(struct irq_domain *domain, unsigned int irq,
+				  unsigned int hwirq)
+{
+	struct irq_data *d = irq_get_irq_data(irq);
+
+	if (!d) {
+		WARN(1, "error: assigning domain to non existant irq_desc");
+		return 1;
+	}
+	if (d->domain) {
+		/* things are broken; just report, don't clean up */
+		WARN(1, "error: irq_desc already assigned to a domain");
+		return 1;
+	}
+	d->domain = domain;
+	d->hwirq = hwirq;
+	return 0;
+}
+
 /**
  * irq_domain_add() - Register an irq_domain
  * @domain: ptr to initialized irq_domain structure
@@ -19,52 +38,71 @@ static DEFINE_MUTEX(irq_domain_mutex);
  */
 void irq_domain_add(struct irq_domain *domain)
 {
-	struct irq_data *d;
-	unsigned int hwirq, irq;
-
 	/*
 	 * This assumes that the irq_domain owner has already allocated
 	 * the irq_descs.  This block will be removed when support for dynamic
 	 * allocation of irq_descs is added to irq_domain.
 	 */
-	irq_domain_for_each_irq(domain, hwirq, irq) {
-		d = irq_get_irq_data(irq);
-		if (!d) {
-			WARN(1, "error: assigning domain to non existant irq_desc");
-			return;
-		}
-		if (d->domain) {
-			/* things are broken; just report, don't clean up */
-			WARN(1, "error: irq_desc already assigned to a domain");
-			return;
-		}
-		d->domain = domain;
-		d->hwirq = hwirq;
-	}
+	irq_domain_each_irq(domain, irq_domain_add_per_irq);
 
 	mutex_lock(&irq_domain_mutex);
 	list_add(&domain->list, &irq_domain_list);
 	mutex_unlock(&irq_domain_mutex);
 }
 
+static int irq_domain_del_per_irq(struct irq_domain *domain, unsigned int irq,
+				  unsigned int hwirq)
+{
+	struct irq_data *d = irq_get_irq_data(irq);
+
+	d->domain = NULL;
+	return 0;
+}
+
 /**
  * irq_domain_del() - Unregister an irq_domain
  * @domain: ptr to registered irq_domain.
  */
 void irq_domain_del(struct irq_domain *domain)
 {
-	struct irq_data *d;
-	unsigned int hwirq, irq;
-
 	mutex_lock(&irq_domain_mutex);
 	list_del(&domain->list);
 	mutex_unlock(&irq_domain_mutex);
 
 	/* Clear the irq_domain assignments */
-	irq_domain_for_each_irq(domain, hwirq, irq) {
-		d = irq_get_irq_data(irq);
-		d->domain = NULL;
+	irq_domain_each_irq(domain, irq_domain_del_per_irq);
+}
+
+/**
+ * irq_domain_each_irq() - Iterate over each irq in an irq_domain
+ * @d: Pointer to the irq_domain to iterate over.
+ * @cb: Pointer to a function to be called for each irq in the
+ *      irq_domain.
+ *
+ * The cb() function is called for each irq in the irq_domain.  If it
+ * returns non-zero, the iteration is stopped.
+ *
+ * Returns: The value returned by the last invocation of cb().
+ */
+int irq_domain_each_irq(struct irq_domain *d,
+			int (*cb)(struct irq_domain *d,	unsigned int irq,
+				  unsigned int hwirq))
+{
+	unsigned int hw, irq;
+	int ret = 0;
+
+	if (d->ops->each_irq)
+		return d->ops->each_irq(d, cb);
+
+	for (hw = d->hwirq_base, irq = irq_domain_to_irq(d, hw);
+	     hw < d->hwirq_base + d->nr_irq;
+	     hw++, irq = irq_domain_to_irq(d, hw)) {
+		ret = cb(d, irq, hw);
+		if (ret)
+			break;
 	}
+
+	return ret;
 }
 
 #if defined(CONFIG_OF_IRQ)
-- 
1.7.2.3

---

From: David Daney <[email-masked]>
Date: Wed, 14 Dec 2011 18:32:10 -0800

This is needed for Octeon to use the Device Tree.

The GPIO interrupts are configured based on Device Tree properties

Signed-off-by: David Daney <[email-masked]>
---
 arch/mips/Kconfig                    |    1 +
 arch/mips/cavium-octeon/octeon-irq.c |  259 +++++++++++++++++++++++++++++++++-
 2 files changed, 259 insertions(+), 1 deletions(-)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 825ded9..6685497 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1432,6 +1432,7 @@ config CPU_CAVIUM_OCTEON
 	select WEAK_ORDERING
 	select CPU_SUPPORTS_HIGHMEM
 	select CPU_SUPPORTS_HUGEPAGES
+	select IRQ_DOMAIN
 	help
 	  The Cavium Octeon processor is a highly integrated chip containing
 	  many ethernet hardware widgets for networking tasks. The processor
diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c
index ffd4ae6..9a9d4a7 100644
--- a/arch/mips/cavium-octeon/octeon-irq.c
+++ b/arch/mips/cavium-octeon/octeon-irq.c
@@ -7,12 +7,16 @@
  */
 
 #include <linux/interrupt.h>
+#include <linux/irqdomain.h>
 #include <linux/bitops.h>
+#include <linux/module.h>
 #include <linux/percpu.h>
+#include <linux/of_irq.h>
 #include <linux/irq.h>
 #include <linux/smp.h>
 
 #include <asm/octeon/octeon.h>
+#include <asm/octeon/cvmx-gpio-defs.h>
 
 static DEFINE_RAW_SPINLOCK(octeon_irq_ciu0_lock);
 static DEFINE_RAW_SPINLOCK(octeon_irq_ciu1_lock);
@@ -58,6 +62,158 @@ static void __init octeon_irq_set_ciu_mapping(int irq, int line, int bit,
 	octeon_irq_ciu_to_irq[line][bit] = irq;
 }
 
+static int octeon_irq_gpio_dt_translate(struct irq_domain *d,
+				       struct device_node *node,
+				       const u32 *intspec,
+				       unsigned int intsize,
+				       unsigned int *out_hwirq,
+				       unsigned int *out_type)
+{
+	unsigned int irq;
+	unsigned int type;
+	unsigned int ciu = 0, bit = 0;
+	unsigned int pin;
+	unsigned int trigger;
+	bool set_edge_handler = false;
+
+	if (d->of_node != node)
+		return -EINVAL;
+
+	if (intsize < 2)
+		return -EINVAL;
+
+	pin = be32_to_cpup(intspec);
+	if (pin >= 16)
+		return -EINVAL;
+
+	trigger = be32_to_cpup(intspec + 1);
+
+	switch (trigger) {
+	case 1:
+		type = IRQ_TYPE_EDGE_RISING;
+		set_edge_handler = true;
+		break;
+	case 2:
+		type = IRQ_TYPE_EDGE_FALLING;
+		set_edge_handler = true;
+		break;
+	case 4:
+		type = IRQ_TYPE_LEVEL_HIGH;
+		break;
+	case 8:
+		type = IRQ_TYPE_LEVEL_LOW;
+		break;
+	default:
+		pr_err("Error: (%s) Invalid irq trigger specification: %x\n",
+		       node->name,
+		       trigger);
+		type = IRQ_TYPE_LEVEL_LOW;
+		break;
+	}
+	*out_type = type;
+	*out_hwirq = d->hwirq_base + pin;
+
+	ciu = *out_hwirq >> 6;
+	bit = *out_hwirq & 0x3f;
+
+	irq = octeon_irq_ciu_to_irq[ciu][bit];
+
+	if (set_edge_handler)
+		__irq_set_handler(irq, handle_edge_irq, 0, NULL);
+
+
+	return 0;
+}
+
+/*
+ * octeon_irq_ciu_dt_translate - Hook to resolve OF irq specifier into a Linux irq#
+ *
+ * Octeon irq maps are a pair of indexes.  The first selects either
+ * ciu0 or ciu1, the second is the bit within the ciu register.
+ */
+static int octeon_irq_ciu_dt_translate(struct irq_domain *d,
+				       struct device_node *node,
+				       const u32 *intspec,
+				       unsigned int intsize,
+				       unsigned int *out_hwirq,
+				       unsigned int *out_type)
+{
+	unsigned int ciu, bit;
+
+	ciu = be32_to_cpup(intspec);
+	bit = be32_to_cpup(intspec + 1);
+
+	if (ciu > 1 || bit > 63)
+		return -EINVAL;
+
+	if (octeon_irq_ciu_to_irq[ciu][bit] == 0)
+		return -EINVAL;
+
+	*out_hwirq = (ciu << 6) | bit;
+	*out_type = 0;
+
+	return 0;
+}
+
+static unsigned int octeon_irq_ciu_to_irqf(struct irq_domain *d,
+					   unsigned int hwirq)
+{
+	unsigned int ciu, bit;
+
+	ciu = (hwirq >> 6) & 1;
+	bit = hwirq & 0x3f;
+	return octeon_irq_ciu_to_irq[ciu][bit];
+}
+
+static bool octeon_irq_ciu_in_domain(unsigned int irq)
+{
+	return (irq != 0) &&
+		!(irq >= OCTEON_IRQ_GPIO0 && irq < OCTEON_IRQ_GPIO0 + 16);
+}
+
+static int octeon_irq_ciu_each_irq(struct irq_domain *d,
+				   int (*cb)(struct irq_domain *d,
+					     unsigned int irq,
+					     unsigned int hwirq))
+{
+	int ciu, bit;
+	int ret = 0;
+	unsigned int irq, hwirq;
+
+	for (ciu = 0; ciu <= 1; ciu++)
+		for (bit = 0; bit <= 63; bit++) {
+			irq = octeon_irq_ciu_to_irq[ciu][bit];
+			hwirq = (ciu << 6) | bit;
+			if (octeon_irq_ciu_in_domain(irq)) {
+				ret = cb(d, irq, hwirq);
+				if (ret)
+					return ret;
+			}
+		}
+	return 0;
+}
+
+static const struct irq_domain_ops octeon_irq_ciu_domain_ops = {
+	.to_irq = octeon_irq_ciu_to_irqf,
+	.each_irq = octeon_irq_ciu_each_irq,
+	.dt_translate = octeon_irq_ciu_dt_translate,
+};
+
+static const struct irq_domain_ops octeon_irq_gpio_domain_ops = {
+	.to_irq = octeon_irq_ciu_to_irqf,
+	.dt_translate = octeon_irq_gpio_dt_translate,
+};
+
+static struct irq_domain octeon_irq_ciu_domain  = {
+	.ops = &octeon_irq_ciu_domain_ops
+};
+
+static struct irq_domain octeon_irq_gpio_domain  = {
+	.irq_base = OCTEON_IRQ_GPIO0,
+	.nr_irq = 16,
+	.ops = &octeon_irq_gpio_domain_ops
+};
+
 static int octeon_coreid_for_cpu(int cpu)
 {
 #ifdef CONFIG_SMP
@@ -505,6 +661,72 @@ static void octeon_irq_ciu_enable_all_v2(struct irq_data *data)
 	}
 }
 
+static void octeon_irq_gpio_setup(struct irq_data *data)
+{
+	union cvmx_gpio_bit_cfgx cfg;
+	int bit = data->irq - OCTEON_IRQ_GPIO0;
+	u32 t = irqd_get_trigger_type(data);
+
+	cfg.u64 = 0;
+	cfg.s.int_en = 1;
+	cfg.s.int_type = (t & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) != 0;
+	cfg.s.rx_xor = (t & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) != 0;
+
+	/* 1 uS glitch filter*/
+	cfg.s.fil_cnt = 7;
+	cfg.s.fil_sel = 3;
+
+	cvmx_write_csr(CVMX_GPIO_BIT_CFGX(bit), cfg.u64);
+}
+
+static void octeon_irq_ciu_enable_gpio_v2(struct irq_data *data)
+{
+	octeon_irq_gpio_setup(data);
+	octeon_irq_ciu_enable_v2(data);
+}
+
+static void octeon_irq_ciu_enable_gpio(struct irq_data *data)
+{
+	octeon_irq_gpio_setup(data);
+	octeon_irq_ciu_enable(data);
+}
+
+static int octeon_irq_ciu_gpio_set_type(struct irq_data *data, unsigned int t)
+{
+	u32 current_type = irqd_get_trigger_type(data);
+
+	/* If the type has been set, don't change it */
+	if (current_type && current_type != t)
+		return -EINVAL;
+
+	irqd_set_trigger_type(data, t);
+	return IRQ_SET_MASK_OK;
+}
+
+static void octeon_irq_ciu_disable_gpio_v2(struct irq_data *data)
+{
+	int bit = data->irq - OCTEON_IRQ_GPIO0;
+	cvmx_write_csr(CVMX_GPIO_BIT_CFGX(bit), 0);
+
+	octeon_irq_ciu_disable_all_v2(data);
+}
+
+static void octeon_irq_ciu_disable_gpio(struct irq_data *data)
+{
+	int bit = data->irq - OCTEON_IRQ_GPIO0;
+	cvmx_write_csr(CVMX_GPIO_BIT_CFGX(bit), 0);
+
+	octeon_irq_ciu_disable_all(data);
+}
+
+static void octeon_irq_ciu_gpio_ack(struct irq_data *data)
+{
+	int bit = data->irq - OCTEON_IRQ_GPIO0;
+	u64 mask = 1ull << bit;
+
+	cvmx_write_csr(CVMX_GPIO_INT_CLR, mask);
+}
+
 #ifdef CONFIG_SMP
 
 static void octeon_irq_cpu_offline_ciu(struct irq_data *data)
@@ -717,6 +939,31 @@ static struct irq_chip octeon_irq_chip_ciu_mbox = {
 	.flags = IRQCHIP_ONOFFLINE_ENABLED,
 };
 
+static struct irq_chip octeon_irq_chip_ciu_gpio_v2 = {
+	.name = "CIU-GPIO",
+	.irq_enable = octeon_irq_ciu_enable_gpio_v2,
+	.irq_disable = octeon_irq_ciu_disable_gpio_v2,
+	.irq_ack = octeon_irq_ciu_gpio_ack,
+	.irq_mask = octeon_irq_ciu_disable_local_v2,
+	.irq_unmask = octeon_irq_ciu_enable_v2,
+	.irq_set_type = octeon_irq_ciu_gpio_set_type,
+#ifdef CONFIG_SMP
+	.irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
+#endif
+};
+
+static struct irq_chip octeon_irq_chip_ciu_gpio = {
+	.name = "CIU-GPIO",
+	.irq_enable = octeon_irq_ciu_enable_gpio,
+	.irq_disable = octeon_irq_ciu_disable_gpio,
+	.irq_mask = octeon_irq_dummy_mask,
+	.irq_ack = octeon_irq_ciu_gpio_ack,
+	.irq_set_type = octeon_irq_ciu_gpio_set_type,
+#ifdef CONFIG_SMP
+	.irq_set_affinity = octeon_irq_ciu_set_affinity,
+#endif
+};
+
 /*
  * Watchdog interrupts are special.  They are associated with a single
  * core, so we hardwire the affinity to that core.
@@ -890,6 +1137,7 @@ static void __init octeon_irq_init_ciu(void)
 	struct irq_chip *chip_edge;
 	struct irq_chip *chip_mbox;
 	struct irq_chip *chip_wd;
+	struct irq_chip *chip_gpio;
 
 	octeon_irq_init_ciu_percpu();
 	octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu;
@@ -904,6 +1152,7 @@ static void __init octeon_irq_init_ciu(void)
 		chip_edge = &octeon_irq_chip_ciu_edge_v2;
 		chip_mbox = &octeon_irq_chip_ciu_mbox_v2;
 		chip_wd = &octeon_irq_chip_ciu_wd_v2;
+		chip_gpio = &octeon_irq_chip_ciu_gpio_v2;
 	} else {
 		octeon_irq_ip2 = octeon_irq_ip2_v1;
 		octeon_irq_ip3 = octeon_irq_ip3_v1;
@@ -911,6 +1160,7 @@ static void __init octeon_irq_init_ciu(void)
 		chip_edge = &octeon_irq_chip_ciu_edge;
 		chip_mbox = &octeon_irq_chip_ciu_mbox;
 		chip_wd = &octeon_irq_chip_ciu_wd;
+		chip_gpio = &octeon_irq_chip_ciu_gpio;
 	}
 	octeon_irq_ip4 = octeon_irq_ip4_mask;
 
@@ -921,7 +1171,7 @@ static void __init octeon_irq_init_ciu(void)
 	for (i = 0; i < 16; i++)
 		octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WORKQ0, 0, i + 0, chip, handle_level_irq);
 	for (i = 0; i < 16; i++)
-		octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GPIO0, 0, i + 16, chip, handle_level_irq);
+		octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GPIO0, 0, i + 16, chip_gpio, handle_level_irq);
 
 	octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq);
 	octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq);
@@ -995,6 +1245,13 @@ static void __init octeon_irq_init_ciu(void)
 	octeon_irq_set_ciu_mapping(OCTEON_IRQ_DFM, 1, 56, chip, handle_level_irq);
 	octeon_irq_set_ciu_mapping(OCTEON_IRQ_RST, 1, 63, chip, handle_level_irq);
 
+	octeon_irq_ciu_domain.of_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-ciu");
+	irq_domain_add(&octeon_irq_ciu_domain);
+
+	octeon_irq_gpio_domain.of_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio");
+	octeon_irq_gpio_domain.hwirq_base = ((0 << 6) | 16);
+	irq_domain_add(&octeon_irq_gpio_domain);
+
 	/* Enable the CIU lines */
 	set_c0_status(STATUSF_IP3 | STATUSF_IP2);
 	clear_c0_status(STATUSF_IP4);
-- 
1.7.2.3

---

From: Rob Herring <[email-masked]>
Date: Fri, 30 Dec 2011 09:30:45 -0600

David,

> approach as it separates the concepts of iteration and mapping of irq

Can we first have a patch that just allows irq domains to be enabled on
MIPS. It collides because of multiple versions of irq_create_of_mapping.

Rob

---

From: Grant Likely <[email-masked]>
Date: Fri, 30 Dec 2011 11:19:35 -0700

> MIPS. It collides because of multiple versions of irq_create_of_mapping.

I'm working on this.  I made some poor decisions when first
implementing irq_domain which have made it difficult to bring into
sync with powerpc.  Right now I'm working on a series to replace the
new irq_domain with the existing (working-for-years) powerpc code and
converting over all the users.  Basically what I should have done in
the first place.

g.

-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.