diff options
author | Eric Anholt <eric@anholt.net> | 2015-08-06 19:00:31 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2015-08-20 16:38:41 -0400 |
commit | a493f339a88ddd20693460c1dcf8230aa3732b8b (patch) | |
tree | 1fecfe79a2633e2da69d3fd536ef3e72cd99c2ff | |
parent | de58e52f207e3318cb1e1d43f951454e0c83827f (diff) |
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>
-rw-r--r-- | Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt | 25 | ||||
-rw-r--r-- | drivers/irqchip/irq-bcm2835.c | 43 |
2 files changed, 64 insertions, 4 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt index 7da578d72123..2d6c8bb4d827 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +++ b/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 | |||
5 | controller, or the HW block containing it, is referred to occasionally | 5 | controller, or the HW block containing it, is referred to occasionally |
6 | as "armctrl" in the SoC documentation, hence naming of this binding. | 6 | as "armctrl" in the SoC documentation, hence naming of this binding. |
7 | 7 | ||
8 | The BCM2836 contains the same interrupt controller with the same | ||
9 | interrupts, but the per-CPU interrupt controller is the root, and an | ||
10 | interrupt there indicates that the ARMCTRL has an interrupt to handle. | ||
11 | |||
8 | Required properties: | 12 | Required properties: |
9 | 13 | ||
10 | - compatible : should be "brcm,bcm2835-armctrl-ic" | 14 | - compatible : should be "brcm,bcm2835-armctrl-ic" or |
15 | "brcm,bcm2836-armctrl-ic" | ||
11 | - reg : Specifies base physical address and size of the registers. | 16 | - reg : Specifies base physical address and size of the registers. |
12 | - interrupt-controller : Identifies the node as an interrupt controller | 17 | - interrupt-controller : Identifies the node as an interrupt controller |
13 | - #interrupt-cells : Specifies the number of cells needed to encode an | 18 | - #interrupt-cells : Specifies the number of cells needed to encode an |
@@ -20,6 +25,12 @@ Required properties: | |||
20 | The 2nd cell contains the interrupt number within the bank. Valid values | 25 | The 2nd cell contains the interrupt number within the bank. Valid values |
21 | are 0..7 for bank 0, and 0..31 for bank 1. | 26 | are 0..7 for bank 0, and 0..31 for bank 1. |
22 | 27 | ||
28 | Additional required properties for brcm,bcm2836-armctrl-ic: | ||
29 | - interrupt-parent : Specifies the parent interrupt controller when this | ||
30 | controller is the second level. | ||
31 | - interrupts : Specifies the interrupt on the parent for this interrupt | ||
32 | controller to handle. | ||
33 | |||
23 | The interrupt sources are as follows: | 34 | The interrupt sources are as follows: |
24 | 35 | ||
25 | Bank 0: | 36 | Bank 0: |
@@ -102,9 +113,21 @@ Bank 2: | |||
102 | 113 | ||
103 | Example: | 114 | Example: |
104 | 115 | ||
116 | /* BCM2835, first level */ | ||
105 | intc: interrupt-controller { | 117 | intc: interrupt-controller { |
106 | compatible = "brcm,bcm2835-armctrl-ic"; | 118 | compatible = "brcm,bcm2835-armctrl-ic"; |
107 | reg = <0x7e00b200 0x200>; | 119 | reg = <0x7e00b200 0x200>; |
108 | interrupt-controller; | 120 | interrupt-controller; |
109 | #interrupt-cells = <2>; | 121 | #interrupt-cells = <2>; |
110 | }; | 122 | }; |
123 | |||
124 | /* BCM2836, second level */ | ||
125 | intc: interrupt-controller { | ||
126 | compatible = "brcm,bcm2836-armctrl-ic"; | ||
127 | reg = <0x7e00b200 0x200>; | ||
128 | interrupt-controller; | ||
129 | #interrupt-cells = <2>; | ||
130 | |||
131 | interrupt-parent = <&local_intc>; | ||
132 | interrupts = <8>; | ||
133 | }; | ||
diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c index a40d97268b8a..ed4ca9deca70 100644 --- a/drivers/irqchip/irq-bcm2835.c +++ b/drivers/irqchip/irq-bcm2835.c | |||
@@ -96,6 +96,7 @@ struct armctrl_ic { | |||
96 | static struct armctrl_ic intc __read_mostly; | 96 | static struct armctrl_ic intc __read_mostly; |
97 | static void __exception_irq_entry bcm2835_handle_irq( | 97 | static void __exception_irq_entry bcm2835_handle_irq( |
98 | struct pt_regs *regs); | 98 | struct pt_regs *regs); |
99 | static void bcm2836_chained_handle_irq(unsigned int irq, struct irq_desc *desc); | ||
99 | 100 | ||
100 | static void armctrl_mask_irq(struct irq_data *d) | 101 | static void armctrl_mask_irq(struct irq_data *d) |
101 | { | 102 | { |
@@ -139,7 +140,8 @@ static const struct irq_domain_ops armctrl_ops = { | |||
139 | }; | 140 | }; |
140 | 141 | ||
141 | static int __init armctrl_of_init(struct device_node *node, | 142 | static int __init armctrl_of_init(struct device_node *node, |
142 | struct device_node *parent) | 143 | struct device_node *parent, |
144 | bool is_2836) | ||
143 | { | 145 | { |
144 | void __iomem *base; | 146 | void __iomem *base; |
145 | int irq, b, i; | 147 | int irq, b, i; |
@@ -168,10 +170,34 @@ static int __init armctrl_of_init(struct device_node *node, | |||
168 | } | 170 | } |
169 | } | 171 | } |
170 | 172 | ||
171 | set_handle_irq(bcm2835_handle_irq); | 173 | if (is_2836) { |
174 | int parent_irq = irq_of_parse_and_map(node, 0); | ||
175 | |||
176 | if (!parent_irq) { | ||
177 | panic("%s: unable to get parent interrupt.\n", | ||
178 | node->full_name); | ||
179 | } | ||
180 | irq_set_chained_handler(parent_irq, bcm2836_chained_handle_irq); | ||
181 | } else { | ||
182 | set_handle_irq(bcm2835_handle_irq); | ||
183 | } | ||
184 | |||
172 | return 0; | 185 | return 0; |
173 | } | 186 | } |
174 | 187 | ||
188 | static int __init bcm2835_armctrl_of_init(struct device_node *node, | ||
189 | struct device_node *parent) | ||
190 | { | ||
191 | return armctrl_of_init(node, parent, false); | ||
192 | } | ||
193 | |||
194 | static int __init bcm2836_armctrl_of_init(struct device_node *node, | ||
195 | struct device_node *parent) | ||
196 | { | ||
197 | return armctrl_of_init(node, parent, true); | ||
198 | } | ||
199 | |||
200 | |||
175 | /* | 201 | /* |
176 | * Handle each interrupt across the entire interrupt controller. This reads the | 202 | * Handle each interrupt across the entire interrupt controller. This reads the |
177 | * status register before handling each interrupt, which is necessary given that | 203 | * status register before handling each interrupt, which is necessary given that |
@@ -219,4 +245,15 @@ static void __exception_irq_entry bcm2835_handle_irq( | |||
219 | handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs); | 245 | handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs); |
220 | } | 246 | } |
221 | 247 | ||
222 | IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", armctrl_of_init); | 248 | static void bcm2836_chained_handle_irq(unsigned int irq, struct irq_desc *desc) |
249 | { | ||
250 | u32 hwirq; | ||
251 | |||
252 | while ((hwirq = get_next_armctrl_hwirq()) != ~0) | ||
253 | generic_handle_irq(irq_linear_revmap(intc.domain, hwirq)); | ||
254 | } | ||
255 | |||
256 | IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", | ||
257 | bcm2835_armctrl_of_init); | ||
258 | IRQCHIP_DECLARE(bcm2836_armctrl_ic, "brcm,bcm2836-armctrl-ic", | ||
259 | bcm2836_armctrl_of_init); | ||