From 6be3809614db2d52724eb4b5193c27d2466142be Mon Sep 17 00:00:00 2001
From: notro <notro@tronnes.org>
Date: Wed, 9 Jul 2014 14:47:48 +0200
Subject: [PATCH 050/114] BCM2708: armctrl: Add IRQ Device Tree support

Add Device Tree IRQ support for BCM2708.
Usage is the same as for irq-bcm2835.
See binding document: brcm,bcm2835-armctrl-ic.txt

A bank 3 is added to handle GPIO interrupts. This is done because
armctrl also handles GPIO interrupts.

Signed-off-by: Noralf Tronnes <notro@tronnes.org>

BCM2708: armctrl: remove irq bank 3

irq bank 3 was needed by the pinctrl-bcm2708 and bcm2708_gpio
combination. It is no longer required.

Signed-off-by: Noralf Tronnes <notro@tronnes.org>
---
 arch/arm/boot/dts/bcm2708.dtsi  |  9 ++++
 arch/arm/mach-bcm2708/armctrl.c | 96 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 105 insertions(+)

--- a/arch/arm/boot/dts/bcm2708.dtsi
+++ b/arch/arm/boot/dts/bcm2708.dtsi
@@ -4,6 +4,8 @@
 	compatible = "brcm,bcm2708";
 	model = "BCM2708";
 
+	interrupt-parent = <&intc>;
+
 	chosen {
 		/*
 		   bootargs must be 1024 characters long because the
@@ -17,6 +19,13 @@
 		#address-cells = <1>;
 		#size-cells = <1>;
 		ranges = <0x7e000000 0x20000000 0x02000000>;
+
+		intc: interrupt-controller {
+			compatible = "brcm,bcm2708-armctrl-ic";
+			reg = <0x7e00b200 0x200>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
 	};
 
 	clocks {
--- a/arch/arm/mach-bcm2708/armctrl.c
+++ b/arch/arm/mach-bcm2708/armctrl.c
@@ -23,6 +23,8 @@
 #include <linux/version.h>
 #include <linux/syscore_ops.h>
 #include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
 
 #include <asm/mach/irq.h>
 #include <mach/hardware.h>
@@ -79,6 +81,99 @@ static void armctrl_unmask_irq(struct ir
 	}
 }
 
+#ifdef CONFIG_OF
+
+#define NR_IRQS_BANK0           21
+#define NR_BANKS                3
+#define IRQS_PER_BANK           32
+
+/* from drivers/irqchip/irq-bcm2835.c */
+static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr,
+        const u32 *intspec, unsigned int intsize,
+        unsigned long *out_hwirq, unsigned int *out_type)
+{
+        if (WARN_ON(intsize != 2))
+                return -EINVAL;
+
+        if (WARN_ON(intspec[0] >= NR_BANKS))
+                return -EINVAL;
+
+        if (WARN_ON(intspec[1] >= IRQS_PER_BANK))
+                return -EINVAL;
+
+        if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0))
+                return -EINVAL;
+
+	if (intspec[0] == 0)
+		*out_hwirq = ARM_IRQ0_BASE + intspec[1];
+	else if (intspec[0] == 1)
+		*out_hwirq = ARM_IRQ1_BASE + intspec[1];
+	else
+		*out_hwirq = ARM_IRQ2_BASE + intspec[1];
+
+	/* reverse remap_irqs[] */
+	switch (*out_hwirq) {
+	case INTERRUPT_VC_JPEG:
+		*out_hwirq = INTERRUPT_JPEG;
+		break;
+	case INTERRUPT_VC_USB:
+		*out_hwirq = INTERRUPT_USB;
+		break;
+	case INTERRUPT_VC_3D:
+		*out_hwirq = INTERRUPT_3D;
+		break;
+	case INTERRUPT_VC_DMA2:
+		*out_hwirq = INTERRUPT_DMA2;
+		break;
+	case INTERRUPT_VC_DMA3:
+		*out_hwirq = INTERRUPT_DMA3;
+		break;
+	case INTERRUPT_VC_I2C:
+		*out_hwirq = INTERRUPT_I2C;
+		break;
+	case INTERRUPT_VC_SPI:
+		*out_hwirq = INTERRUPT_SPI;
+		break;
+	case INTERRUPT_VC_I2SPCM:
+		*out_hwirq = INTERRUPT_I2SPCM;
+		break;
+	case INTERRUPT_VC_SDIO:
+		*out_hwirq = INTERRUPT_SDIO;
+		break;
+	case INTERRUPT_VC_UART:
+		*out_hwirq = INTERRUPT_UART;
+		break;
+	case INTERRUPT_VC_ARASANSDIO:
+		*out_hwirq = INTERRUPT_ARASANSDIO;
+		break;
+	}
+
+        *out_type = IRQ_TYPE_NONE;
+        return 0;
+}
+
+static struct irq_domain_ops armctrl_ops = {
+        .xlate = armctrl_xlate
+};
+
+void __init armctrl_dt_init(void)
+{
+	struct device_node *np;
+	struct irq_domain *domain;
+
+	np = of_find_compatible_node(NULL, NULL, "brcm,bcm2708-armctrl-ic");
+	if (!np)
+		return;
+
+	domain = irq_domain_add_legacy(np, BCM2708_ALLOC_IRQS,
+					IRQ_ARMCTRL_START, 0,
+					&armctrl_ops, NULL);
+        WARN_ON(!domain);
+}
+#else
+void __init armctrl_dt_init(void) { }
+#endif /* CONFIG_OF */
+
 #if defined(CONFIG_PM)
 
 /* for kernels 3.xx use the new syscore_ops apis but for older kernels use the sys dev class */
@@ -215,5 +310,6 @@ int __init armctrl_init(void __iomem * b
 
 	armctrl_pm_register(base, irq_start, resume_sources);
 	init_FIQ(FIQ_START);
+	armctrl_dt_init();
 	return 0;
 }
