Эх сурвалжийг харах

irqchip/bcm2835: Add support for being used as a second level controller

The BCM2836 (Raspberry Pi 2) uses two levels of interrupt handling
with the CPU-local interrupts being the root, so we need to register
ours as chained off of the CPU's local interrupt.

Signed-off-by: Eric Anholt <eric@anholt.net>
Acked-by: Stephen Warren <swarren@wwwdotorg.org>
Cc: linux-rpi-kernel@lists.infradead.org
Cc: Lee Jones <lee@kernel.org>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lkml.kernel.org/r/1438902033-31477-3-git-send-email-eric@anholt.net
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Eric Anholt 10 жил өмнө
parent
commit
a493f339a8

+ 24 - 1
Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt

@@ -5,9 +5,14 @@ The BCM2835 contains a custom top-level interrupt controller, which supports
 controller, or the HW block containing it, is referred to occasionally
 controller, or the HW block containing it, is referred to occasionally
 as "armctrl" in the SoC documentation, hence naming of this binding.
 as "armctrl" in the SoC documentation, hence naming of this binding.
 
 
+The BCM2836 contains the same interrupt controller with the same
+interrupts, but the per-CPU interrupt controller is the root, and an
+interrupt there indicates that the ARMCTRL has an interrupt to handle.
+
 Required properties:
 Required properties:
 
 
-- compatible : should be "brcm,bcm2835-armctrl-ic"
+- compatible : should be "brcm,bcm2835-armctrl-ic" or
+                 "brcm,bcm2836-armctrl-ic"
 - reg : Specifies base physical address and size of the registers.
 - reg : Specifies base physical address and size of the registers.
 - interrupt-controller : Identifies the node as an interrupt controller
 - interrupt-controller : Identifies the node as an interrupt controller
 - #interrupt-cells : Specifies the number of cells needed to encode an
 - #interrupt-cells : Specifies the number of cells needed to encode an
@@ -20,6 +25,12 @@ Required properties:
   The 2nd cell contains the interrupt number within the bank. Valid values
   The 2nd cell contains the interrupt number within the bank. Valid values
   are 0..7 for bank 0, and 0..31 for bank 1.
   are 0..7 for bank 0, and 0..31 for bank 1.
 
 
+Additional required properties for brcm,bcm2836-armctrl-ic:
+- interrupt-parent : Specifies the parent interrupt controller when this
+  controller is the second level.
+- interrupts : Specifies the interrupt on the parent for this interrupt
+  controller to handle.
+
 The interrupt sources are as follows:
 The interrupt sources are as follows:
 
 
 Bank 0:
 Bank 0:
@@ -102,9 +113,21 @@ Bank 2:
 
 
 Example:
 Example:
 
 
+/* BCM2835, first level */
 intc: interrupt-controller {
 intc: interrupt-controller {
 	compatible = "brcm,bcm2835-armctrl-ic";
 	compatible = "brcm,bcm2835-armctrl-ic";
 	reg = <0x7e00b200 0x200>;
 	reg = <0x7e00b200 0x200>;
 	interrupt-controller;
 	interrupt-controller;
 	#interrupt-cells = <2>;
 	#interrupt-cells = <2>;
 };
 };
+
+/* BCM2836, second level */
+intc: interrupt-controller {
+	compatible = "brcm,bcm2836-armctrl-ic";
+	reg = <0x7e00b200 0x200>;
+	interrupt-controller;
+	#interrupt-cells = <2>;
+
+	interrupt-parent = <&local_intc>;
+	interrupts = <8>;
+};

+ 40 - 3
drivers/irqchip/irq-bcm2835.c

@@ -96,6 +96,7 @@ struct armctrl_ic {
 static struct armctrl_ic intc __read_mostly;
 static struct armctrl_ic intc __read_mostly;
 static void __exception_irq_entry bcm2835_handle_irq(
 static void __exception_irq_entry bcm2835_handle_irq(
 	struct pt_regs *regs);
 	struct pt_regs *regs);
+static void bcm2836_chained_handle_irq(unsigned int irq, struct irq_desc *desc);
 
 
 static void armctrl_mask_irq(struct irq_data *d)
 static void armctrl_mask_irq(struct irq_data *d)
 {
 {
@@ -139,7 +140,8 @@ static const struct irq_domain_ops armctrl_ops = {
 };
 };
 
 
 static int __init armctrl_of_init(struct device_node *node,
 static int __init armctrl_of_init(struct device_node *node,
-	struct device_node *parent)
+				  struct device_node *parent,
+				  bool is_2836)
 {
 {
 	void __iomem *base;
 	void __iomem *base;
 	int irq, b, i;
 	int irq, b, i;
@@ -168,10 +170,34 @@ static int __init armctrl_of_init(struct device_node *node,
 		}
 		}
 	}
 	}
 
 
-	set_handle_irq(bcm2835_handle_irq);
+	if (is_2836) {
+		int parent_irq = irq_of_parse_and_map(node, 0);
+
+		if (!parent_irq) {
+			panic("%s: unable to get parent interrupt.\n",
+			      node->full_name);
+		}
+		irq_set_chained_handler(parent_irq, bcm2836_chained_handle_irq);
+	} else {
+		set_handle_irq(bcm2835_handle_irq);
+	}
+
 	return 0;
 	return 0;
 }
 }
 
 
+static int __init bcm2835_armctrl_of_init(struct device_node *node,
+					  struct device_node *parent)
+{
+	return armctrl_of_init(node, parent, false);
+}
+
+static int __init bcm2836_armctrl_of_init(struct device_node *node,
+					  struct device_node *parent)
+{
+	return armctrl_of_init(node, parent, true);
+}
+
+
 /*
 /*
  * Handle each interrupt across the entire interrupt controller.  This reads the
  * Handle each interrupt across the entire interrupt controller.  This reads the
  * status register before handling each interrupt, which is necessary given that
  * status register before handling each interrupt, which is necessary given that
@@ -219,4 +245,15 @@ static void __exception_irq_entry bcm2835_handle_irq(
 		handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
 		handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
 }
 }
 
 
-IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", armctrl_of_init);
+static void bcm2836_chained_handle_irq(unsigned int irq, struct irq_desc *desc)
+{
+	u32 hwirq;
+
+	while ((hwirq = get_next_armctrl_hwirq()) != ~0)
+		generic_handle_irq(irq_linear_revmap(intc.domain, hwirq));
+}
+
+IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic",
+		bcm2835_armctrl_of_init);
+IRQCHIP_DECLARE(bcm2836_armctrl_ic, "brcm,bcm2836-armctrl-ic",
+		bcm2836_armctrl_of_init);