diff mbox

[v4,4/4] ARM: Exynos: Add device tree support for gpio wakeup interrupt controller

Message ID 1332582075-16204-5-git-send-email-thomas.abraham@linaro.org
State New
Headers show

Commit Message

thomas.abraham@linaro.org March 24, 2012, 9:41 a.m. UTC
Add device tree support for gpio wakeup source interrupt controller for
Exynos platforms.

Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
---
 .../bindings/arm/samsung/wakeup-eint.txt           |  152 ++++++++++++++++++++
 arch/arm/mach-exynos/common.c                      |   55 ++++++--
 2 files changed, 195 insertions(+), 12 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/samsung/wakeup-eint.txt
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/arm/samsung/wakeup-eint.txt b/Documentation/devicetree/bindings/arm/samsung/wakeup-eint.txt
new file mode 100644
index 0000000..04bbf25
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/samsung/wakeup-eint.txt
@@ -0,0 +1,152 @@ 
+* Samsung GPIO Wakeup Interrupt Controller
+
+This document is split into following sections.
+
+[1] Samsung Exynos4 GPIO Wakeup Interrupt Source Controller
+[2] Samsung Exynos5 GPIO Wakeup Interrupt Source Controller
+
+
+[1] Samsung Exynos4 GPIO Wakeup Interrupt Source Controller
+
+Samsung Exynos4 processor supports 32 external wakeup interrupt sources. First
+16 of these interrupts are directly connected to GIC and the rest 16 of the
+interrupts are grouped together to deliver a single interrupt to GIC.
+
+Required properties:
+
+    - compatible: should be "samsung,exynos4210-wakeup-eint".
+
+    - reg: physical base address of the controller and length of memory
+      mapped region.
+
+    - interrupt-controller: Identifies the node as an interrupt controller.
+
+    - interrupt-cells: Specifies the number of cells required to specify the
+      interrupt source number. The value of should be <2>. The first cell
+      represents the wakeup interrupt source number and the second cell
+      should be zero (currently unused).
+
+    - interrupts: List of interrupts generated by the gpio wakeup interrupt
+      controller which are connected to a parent interrupt controller. The
+      format of the interrupt specifier depends on the interrupt parent
+      controller.
+
+Optional properties:
+
+    - interrupt-parent: phandle of the parent interrupt controller, required
+      if not inheriting the interrupt parent from the parent node.
+
+Example:
+
+	The following example is from the Exynos4210 dtsi file.
+
+	wakeup_eint: interrupt-controller-wakeup-eint {
+		compatible = "samsung,exynos4210-wakeup-eint";
+		reg = <0x11000000 0x1000>;
+		#interrupt-cells = <2>;
+		interrupt-controller;
+		interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>,
+				<0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>,
+				<0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
+				<0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>,
+				<0 32 0>;
+	};
+
+
+[2] Samsung Exynos5 GPIO Wakeup Interrupt Source Controller
+
+Samsung Exynos5 processor supports 32 external wakeup interrupt sources. First
+16 of these interrupts are directly connected to GIC and the rest 16 of the
+interrupts are grouped together to deliver a single interrupt to interrupt
+combiner controller.
+
+Since the wakeup interrupts has two interrupt parents, a interrupt nexus
+child node is used that includes a interrupt-map used to translate wakeup
+interrupt specifiers into gic and combiner domain interrupts.
+
+Required properties:
+
+    - compatible: should be "samsung,exynos5210-wakeup-eint".
+
+    - reg: physical base address of the controller and length of memory
+      mapped region.
+
+    - interrupt-controller: Identifies the node as an interrupt controller.
+
+    - interrupt-cells: Specifies the number of cells required to specify the
+      interrupt source number. The value of should be <2>. The first cell
+      represents the wakeup interrupt source number and the second cell
+      should be zero (currently unused).
+
+    - interrupts: List of interrupts generated by the gpio wakeup interrupt
+      controller. Since both gic and combiner controllers are interrupt
+      parents, a interrupt nexus child node is used to translate the interrupt
+      specifiers into respective gic and combiner interrupt domains (see below).
+      The interrupt specifier should be two cells - the first cell should be the
+      interrupt number originating from the wakeup controller and the second
+      cell should be zero (unused).
+
+    - interrupt-parent: The phandle of the interrupt nexus child node.
+
+    - interrupt-nexus child node: This node is used to translate the interrupt
+      specifiers of the wakeup interrupt controller node to the respecitive
+      gic or combiner interrupt domain. The interrupt nexus node should include
+      the following properties.
+
+      - interrupt-cells: Specifies the number of cells required to specify the
+        interrupt source number. The value of should be <2>. The first cell
+        represents the wakeup interrupt source number and the second cell
+        should be zero (currently unused).
+
+      - #address-cells: value of this property should be zero.
+
+      - #size-cells: value of this property should be zero.
+
+      - interrupt-map: The interrpt-map specifies how interrupt specifiers are
+        translated. There should be a interrupt-map entry for each interrupt
+        generated by the wakeup interrupt controller. The format of each entry
+        in the interrupt map should be
+
+              <#intr 0 parent-phandle parent-interrupt-specifier>
+
+	 where #intr is one of the entries in the 'interrupts' property of the
+        parent node.
+
+
+Example:
+
+	wakeup_eint: interrupt-controller@11400000 {
+		compatible = "samsung,exynos5210-wakeup-eint";
+		reg = <0x11400000 0x1000>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		interrupt-parent = <&eint_nexus>;
+		interrupts = <0x0 0>, <0x1 0>, <0x2 0>, <0x3 0>,
+			     <0x4 0>, <0x5 0>, <0x6 0>, <0x7 0>,
+			     <0x8 0>, <0x9 0>, <0xa 0>, <0xb 0>,
+			     <0xc 0>, <0xd 0>, <0xe 0>, <0xf 0>,
+			     <0x10 0>;
+
+                wakeup_map: interrupt-map {
+                        #interrupt-cells = <2>;
+                        #address-cells = <0>;
+                        #size-cells = <0>;
+                        interrupt-map = <0x0 0 &combiner 23 0>,
+                                        <0x1 0 &combiner 24 0>,
+                                        <0x2 0 &combiner 25 0>,
+                                        <0x3 0 &combiner 25 1>,
+                                        <0x4 0 &combiner 26 0>,
+                                        <0x5 0 &combiner 26 1>,
+                                        <0x6 0 &combiner 27 0>,
+                                        <0x7 0 &combiner 27 1>,
+                                        <0x8 0 &combiner 28 0>,
+                                        <0x9 0 &combiner 28 1>,
+                                        <0xa 0 &combiner 29 0>,
+                                        <0xb 0 &combiner 29 1>,
+                                        <0xc 0 &combiner 30 0>,
+                                        <0xd 0 &combiner 30 1>,
+                                        <0xe 0 &combiner 31 0>,
+                                        <0xf 0 &combiner 31 1>,
+                                        <0x10 0 &gic 0 32 0>;
+                };
+        };
diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c
index 7aa1919..d7a321f 100644
--- a/arch/arm/mach-exynos/common.c
+++ b/arch/arm/mach-exynos/common.c
@@ -64,7 +64,8 @@  static void exynos4_init_clocks(int xtal);
 static void exynos5_init_clocks(int xtal);
 static void exynos_init_uarts(struct s3c2410_uartcfg *cfg, int no);
 static int exynos_init(void);
-static int exynos_init_irq_eint(void);
+static int exynos_init_irq_eint(struct device_node *np,
+				struct device_node *parent);
 
 static struct cpu_table cpu_ids[] __initdata = {
 	{
@@ -583,6 +584,8 @@  static const struct of_device_id exynos4_dt_irq_match[] = {
 	{ .compatible = "arm,cortex-a9-gic", .data = gic_of_init, },
 	{ .compatible = "samsung,exynos4210-combiner",
 			.data = combiner_of_init, },
+	{ .compatible = "samsung,exynos4210-wakeup-eint",
+			.data = exynos_init_irq_eint, },
 	{},
 };
 #endif
@@ -600,8 +603,10 @@  void __init exynos4_init_irq(void)
 		of_irq_init(exynos4_dt_irq_match);
 #endif
 
-	if (!of_have_populated_dt())
+	if (!of_have_populated_dt()) {
 		combiner_init(S5P_VA_COMBINER_BASE, NULL);
+		exynos_init_irq_eint(NULL, NULL);
+	}
 
 	/*
 	 * The parameters of s5p_init_irq() are for VIC init.
@@ -609,20 +614,38 @@  void __init exynos4_init_irq(void)
 	 * uses GIC instead of VIC.
 	 */
 	s5p_init_irq(NULL, 0);
-	exynos_init_irq_eint();
 }
 
 void __init exynos5_init_irq(void)
 {
+	struct device_node *np;
+
 	gic_init(0, IRQ_PPI(0), S5P_VA_GIC_DIST, S5P_VA_GIC_CPU);
 
 	/*
+	 * The Exynos5 wakeup interrupt controller has two-interrupt parents,
+	 * gic and combiner. Hence, a interrupt nexus node is used to translate
+	 * interrupt specifers for the interrupts which wakeup interrupt
+	 * controller deliver to the gic and combiner.
+	 *
+	 * When using a interrupt nexus node (which is child node of the wakeup
+	 * controller node), the interrupt parent of the wakeup controller node
+	 * is set as the nexus node and the nexus node does not have a
+	 * 'interrupt-controller' property. Hence, the of_irq_init function
+	 * will not be able to invoke the intializer function for wakeup
+	 * interrupt controller. So call the initiazer explicitly here.
+	 */
+	np = of_find_compatible_node(NULL, NULL,
+					"samsung,exynos5210-wakeup-eint");
+	if (np)
+		exynos_init_irq_eint(np, NULL);
+
+	/*
 	 * The parameters of s5p_init_irq() are for VIC init.
 	 * Theses parameters should be NULL and 0 because EXYNOS4
 	 * uses GIC instead of VIC.
 	 */
 	s5p_init_irq(NULL, 0);
-	exynos_init_irq_eint();
 }
 
 struct bus_type exynos4_subsys = {
@@ -1013,13 +1036,19 @@  static struct irq_domain_ops exynos_eint_irq_domain_ops = {
 	.map = exynos_eint_irq_domain_map,
 };
 
-static int __init exynos_init_irq_eint(void)
+static int __init exynos_init_irq_eint(struct device_node *np,
+					struct device_node *parent)
 {
-	int irq, *src_int, irq_base;
+	int irq, *src_int, irq_base, irq_eint;
 	unsigned int paddr;
 
-	paddr = soc_is_exynos5250() ? EXYNOS5_PA_GPIO1 : EXYNOS4_PA_GPIO2;
-	exynos_eint_base = ioremap(paddr, SZ_4K);
+	if (!np) {
+		paddr = soc_is_exynos5250() ? EXYNOS5_PA_GPIO1 :
+						EXYNOS4_PA_GPIO2;
+		exynos_eint_base = ioremap(paddr, SZ_4K);
+	} else {
+		exynos_eint_base = of_iomap(np, 0);
+	}
 	if (!exynos_eint_base) {
 		pr_err("unable to ioremap for EINT base address\n");
 		return -ENXIO;
@@ -1032,21 +1061,23 @@  static int __init exynos_init_irq_eint(void)
 				"linux irq base\n", __func__, irq_base);
 	}
 
-	irq_domain = irq_domain_add_legacy(NULL, EXYNOS_EINT_NR, irq_base, 0,
+	irq_domain = irq_domain_add_legacy(np, EXYNOS_EINT_NR, irq_base, 0,
 					 &exynos_eint_irq_domain_ops, NULL);
 	if (WARN_ON(!irq_domain)) {
 		pr_warning("%s: irq domain init failed\n", __func__);
 		return 0;
 	}
 
-	irq_set_chained_handler(EXYNOS_IRQ_EINT16_31, exynos_irq_demux_eint16_31);
+	irq_eint = np ? irq_of_parse_and_map(np, 16) : EXYNOS_IRQ_EINT16_31;
+	irq_set_chained_handler(irq_eint, exynos_irq_demux_eint16_31);
 
 	for (irq = 0 ; irq <= 15; irq++) {
 		eint0_15_data[irq] = irq;
 		src_int = soc_is_exynos5250() ? exynos5_eint0_15_src_int :
 						exynos4_eint0_15_src_int;
-		irq_set_handler_data(src_int[irq], &eint0_15_data[irq]);
-		irq_set_chained_handler(src_int[irq], exynos_irq_eint0_15);
+		irq_eint = np ? irq_of_parse_and_map(np, irq) : src_int[irq];
+		irq_set_handler_data(irq_eint, &eint0_15_data[irq]);
+		irq_set_chained_handler(irq_eint, exynos_irq_eint0_15);
 	}
 
 	return 0;