aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Cernekee <cernekee@gmail.com>2014-11-07 01:44:26 -0500
committerJason Cooper <jason@lakedaemon.net>2014-11-08 23:03:13 -0500
commitc76acf4dffa3232711b5364d7a29746df590f3db (patch)
tree1477f55905d95991574d12a46eb05b17ec2af156
parent05b8ce8260b069b0d59516711e2795758f203556 (diff)
irqchip: bcm7120-l2: Extend driver to support 64+ bit controllers
Most implementations of the bcm7120-l2 controller only have a single 32-bit enable word + 32-bit status word. But some instances have added more enable/status pairs in order to support 64+ IRQs (which are all ORed into one parent IRQ input). Make the following changes to allow the driver to support this: - Extend DT bindings so that multiple words can be specified for the reg property, various masks, etc. - Add loops to the probe/handle functions to deal with each word separately - Allocate 1 generic-chip for every 32 IRQs, so we can still use the clr/set helper functions - Update the documentation This uses one domain per bcm7120-l2 DT node. If the DT node defines multiple enable/status pairs (i.e. >=64 IRQs) then the driver will create a single IRQ domain with 2+ generic chips. Multiple generic chips are required because the generic-chip code can only handle one enable/status register pair per instance. Signed-off-by: Kevin Cernekee <cernekee@gmail.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Link: https://lkml.kernel.org/r/1415342669-30640-12-git-send-email-cernekee@gmail.com Signed-off-by: Jason Cooper <jason@lakedaemon.net>
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt26
-rw-r--r--drivers/irqchip/irq-bcm7120-l2.c144
2 files changed, 113 insertions, 57 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt
index ff812a8a82bc..bae1f2187226 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7120-l2-intc.txt
@@ -13,7 +13,12 @@ Such an interrupt controller has the following hardware design:
13 or if they will output an interrupt signal at this 2nd level interrupt 13 or if they will output an interrupt signal at this 2nd level interrupt
14 controller, in particular for UARTs 14 controller, in particular for UARTs
15 15
16- not all 32-bits within the interrupt controller actually map to an interrupt 16- typically has one 32-bit enable word and one 32-bit status word, but on
17 some hardware may have more than one enable/status pair
18
19- no atomic set/clear operations
20
21- not all bits within the interrupt controller actually map to an interrupt
17 22
18The typical hardware layout for this controller is represented below: 23The typical hardware layout for this controller is represented below:
19 24
@@ -48,7 +53,9 @@ The typical hardware layout for this controller is represented below:
48Required properties: 53Required properties:
49 54
50- compatible: should be "brcm,bcm7120-l2-intc" 55- compatible: should be "brcm,bcm7120-l2-intc"
51- reg: specifies the base physical address and size of the registers 56- reg: specifies the base physical address and size of the registers;
57 multiple pairs may be specified, with the first pair handling IRQ offsets
58 0..31 and the second pair handling 32..63
52- interrupt-controller: identifies the node as an interrupt controller 59- interrupt-controller: identifies the node as an interrupt controller
53- #interrupt-cells: specifies the number of cells needed to encode an interrupt 60- #interrupt-cells: specifies the number of cells needed to encode an interrupt
54 source, should be 1. 61 source, should be 1.
@@ -59,18 +66,21 @@ Required properties:
59- brcm,int-map-mask: 32-bits bit mask describing how many and which interrupts 66- brcm,int-map-mask: 32-bits bit mask describing how many and which interrupts
60 are wired to this 2nd level interrupt controller, and how they match their 67 are wired to this 2nd level interrupt controller, and how they match their
61 respective interrupt parents. Should match exactly the number of interrupts 68 respective interrupt parents. Should match exactly the number of interrupts
62 specified in the 'interrupts' property. 69 specified in the 'interrupts' property, multiplied by the number of
70 enable/status register pairs implemented by this controller. For
71 multiple parent IRQs with multiple enable/status words, this looks like:
72 <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...>
63 73
64Optional properties: 74Optional properties:
65 75
66- brcm,irq-can-wake: if present, this means the L2 controller can be used as a 76- brcm,irq-can-wake: if present, this means the L2 controller can be used as a
67 wakeup source for system suspend/resume. 77 wakeup source for system suspend/resume.
68 78
69- brcm,int-fwd-mask: if present, a 32-bits bit mask to configure for the 79- brcm,int-fwd-mask: if present, a bit mask to configure the interrupts which
70 interrupts which have a mux gate, typically UARTs. Setting these bits will 80 have a mux gate, typically UARTs. Setting these bits will make their
71 make their respective interrupts outputs bypass this 2nd level interrupt 81 respective interrupt outputs bypass this 2nd level interrupt controller
72 controller completely, it completely transparent for the interrupt controller 82 completely; it is completely transparent for the interrupt controller
73 parent 83 parent. This should have one 32-bit word per enable/status pair.
74 84
75Example: 85Example:
76 86
diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c
index 984112112042..ef4d32cf267f 100644
--- a/drivers/irqchip/irq-bcm7120-l2.c
+++ b/drivers/irqchip/irq-bcm7120-l2.c
@@ -23,6 +23,7 @@
23#include <linux/io.h> 23#include <linux/io.h>
24#include <linux/irqdomain.h> 24#include <linux/irqdomain.h>
25#include <linux/reboot.h> 25#include <linux/reboot.h>
26#include <linux/bitops.h>
26#include <linux/irqchip/chained_irq.h> 27#include <linux/irqchip/chained_irq.h>
27 28
28#include "irqchip.h" 29#include "irqchip.h"
@@ -31,27 +32,42 @@
31#define IRQEN 0x00 32#define IRQEN 0x00
32#define IRQSTAT 0x04 33#define IRQSTAT 0x04
33 34
35#define MAX_WORDS 4
36#define IRQS_PER_WORD 32
37
34struct bcm7120_l2_intc_data { 38struct bcm7120_l2_intc_data {
35 void __iomem *base; 39 unsigned int n_words;
40 void __iomem *base[MAX_WORDS];
36 struct irq_domain *domain; 41 struct irq_domain *domain;
37 bool can_wake; 42 bool can_wake;
38 u32 irq_fwd_mask; 43 u32 irq_fwd_mask[MAX_WORDS];
39 u32 irq_map_mask; 44 u32 irq_map_mask[MAX_WORDS];
40}; 45};
41 46
42static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) 47static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
43{ 48{
44 struct bcm7120_l2_intc_data *b = irq_desc_get_handler_data(desc); 49 struct bcm7120_l2_intc_data *b = irq_desc_get_handler_data(desc);
45 struct irq_chip *chip = irq_desc_get_chip(desc); 50 struct irq_chip *chip = irq_desc_get_chip(desc);
46 u32 status; 51 unsigned int idx;
47 52
48 chained_irq_enter(chip, desc); 53 chained_irq_enter(chip, desc);
49 54
50 status = __raw_readl(b->base + IRQSTAT); 55 for (idx = 0; idx < b->n_words; idx++) {
51 while (status) { 56 int base = idx * IRQS_PER_WORD;
52 irq = ffs(status) - 1; 57 struct irq_chip_generic *gc =
53 status &= ~(1 << irq); 58 irq_get_domain_generic_chip(b->domain, base);
54 generic_handle_irq(irq_find_mapping(b->domain, irq)); 59 unsigned long pending;
60 int hwirq;
61
62 irq_gc_lock(gc);
63 pending = __raw_readl(b->base[idx] + IRQSTAT) &
64 gc->mask_cache;
65 irq_gc_unlock(gc);
66
67 for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) {
68 generic_handle_irq(irq_find_mapping(b->domain,
69 base + hwirq));
70 }
55 } 71 }
56 72
57 chained_irq_exit(chip, desc); 73 chained_irq_exit(chip, desc);
@@ -65,7 +81,7 @@ static void bcm7120_l2_intc_suspend(struct irq_data *d)
65 irq_gc_lock(gc); 81 irq_gc_lock(gc);
66 if (b->can_wake) { 82 if (b->can_wake) {
67 __raw_writel(gc->mask_cache | gc->wake_active, 83 __raw_writel(gc->mask_cache | gc->wake_active,
68 b->base + IRQEN); 84 gc->reg_base + IRQEN);
69 } 85 }
70 irq_gc_unlock(gc); 86 irq_gc_unlock(gc);
71} 87}
@@ -76,7 +92,7 @@ static void bcm7120_l2_intc_resume(struct irq_data *d)
76 92
77 /* Restore the saved mask */ 93 /* Restore the saved mask */
78 irq_gc_lock(gc); 94 irq_gc_lock(gc);
79 __raw_writel(gc->mask_cache, b->base + IRQEN); 95 __raw_writel(gc->mask_cache, gc->reg_base + IRQEN);
80 irq_gc_unlock(gc); 96 irq_gc_unlock(gc);
81} 97}
82 98
@@ -85,6 +101,7 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn,
85 int irq, const __be32 *map_mask) 101 int irq, const __be32 *map_mask)
86{ 102{
87 int parent_irq; 103 int parent_irq;
104 unsigned int idx;
88 105
89 parent_irq = irq_of_parse_and_map(dn, irq); 106 parent_irq = irq_of_parse_and_map(dn, irq);
90 if (parent_irq < 0) { 107 if (parent_irq < 0) {
@@ -92,7 +109,12 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn,
92 return parent_irq; 109 return parent_irq;
93 } 110 }
94 111
95 data->irq_map_mask |= be32_to_cpup(map_mask + irq); 112 /* For multiple parent IRQs with multiple words, this looks like:
113 * <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...>
114 */
115 for (idx = 0; idx < data->n_words; idx++)
116 data->irq_map_mask[idx] |=
117 be32_to_cpup(map_mask + irq * data->n_words + idx);
96 118
97 irq_set_handler_data(parent_irq, data); 119 irq_set_handler_data(parent_irq, data);
98 irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle); 120 irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle);
@@ -109,26 +131,41 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn,
109 struct irq_chip_type *ct; 131 struct irq_chip_type *ct;
110 const __be32 *map_mask; 132 const __be32 *map_mask;
111 int num_parent_irqs; 133 int num_parent_irqs;
112 int ret = 0, len, irq; 134 int ret = 0, len;
135 unsigned int idx, irq;
113 136
114 data = kzalloc(sizeof(*data), GFP_KERNEL); 137 data = kzalloc(sizeof(*data), GFP_KERNEL);
115 if (!data) 138 if (!data)
116 return -ENOMEM; 139 return -ENOMEM;
117 140
118 data->base = of_iomap(dn, 0); 141 for (idx = 0; idx < MAX_WORDS; idx++) {
119 if (!data->base) { 142 data->base[idx] = of_iomap(dn, idx);
143 if (!data->base[idx])
144 break;
145 data->n_words = idx + 1;
146 }
147 if (!data->n_words) {
120 pr_err("failed to remap intc L2 registers\n"); 148 pr_err("failed to remap intc L2 registers\n");
121 ret = -ENOMEM; 149 ret = -ENOMEM;
122 goto out_free; 150 goto out_unmap;
123 } 151 }
124 152
125 if (of_property_read_u32(dn, "brcm,int-fwd-mask", &data->irq_fwd_mask)) 153 /* Enable all interrupts specified in the interrupt forward mask;
126 data->irq_fwd_mask = 0; 154 * disable all others. If the property doesn't exist (-EINVAL),
127 155 * assume all zeroes.
128 /* Enable all interrupt specified in the interrupt forward mask and have
129 * the other disabled
130 */ 156 */
131 __raw_writel(data->irq_fwd_mask, data->base + IRQEN); 157 ret = of_property_read_u32_array(dn, "brcm,int-fwd-mask",
158 data->irq_fwd_mask, data->n_words);
159 if (ret == 0 || ret == -EINVAL) {
160 for (idx = 0; idx < data->n_words; idx++)
161 __raw_writel(data->irq_fwd_mask[idx],
162 data->base[idx] + IRQEN);
163 } else {
164 /* property exists but has the wrong number of words */
165 pr_err("invalid int-fwd-mask property\n");
166 ret = -EINVAL;
167 goto out_unmap;
168 }
132 169
133 num_parent_irqs = of_irq_count(dn); 170 num_parent_irqs = of_irq_count(dn);
134 if (num_parent_irqs <= 0) { 171 if (num_parent_irqs <= 0) {
@@ -138,7 +175,8 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn,
138 } 175 }
139 176
140 map_mask = of_get_property(dn, "brcm,int-map-mask", &len); 177 map_mask = of_get_property(dn, "brcm,int-map-mask", &len);
141 if (!map_mask || (len != (sizeof(*map_mask) * num_parent_irqs))) { 178 if (!map_mask ||
179 (len != (sizeof(*map_mask) * num_parent_irqs * data->n_words))) {
142 pr_err("invalid brcm,int-map-mask property\n"); 180 pr_err("invalid brcm,int-map-mask property\n");
143 ret = -EINVAL; 181 ret = -EINVAL;
144 goto out_unmap; 182 goto out_unmap;
@@ -150,14 +188,14 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn,
150 goto out_unmap; 188 goto out_unmap;
151 } 189 }
152 190
153 data->domain = irq_domain_add_linear(dn, 32, 191 data->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * data->n_words,
154 &irq_generic_chip_ops, NULL); 192 &irq_generic_chip_ops, NULL);
155 if (!data->domain) { 193 if (!data->domain) {
156 ret = -ENOMEM; 194 ret = -ENOMEM;
157 goto out_unmap; 195 goto out_unmap;
158 } 196 }
159 197
160 ret = irq_alloc_domain_generic_chips(data->domain, 32, 1, 198 ret = irq_alloc_domain_generic_chips(data->domain, IRQS_PER_WORD, 1,
161 dn->full_name, handle_level_irq, clr, 0, 199 dn->full_name, handle_level_irq, clr, 0,
162 IRQ_GC_INIT_MASK_CACHE); 200 IRQ_GC_INIT_MASK_CACHE);
163 if (ret) { 201 if (ret) {
@@ -165,39 +203,47 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn,
165 goto out_free_domain; 203 goto out_free_domain;
166 } 204 }
167 205
168 gc = irq_get_domain_generic_chip(data->domain, 0); 206 if (of_property_read_bool(dn, "brcm,irq-can-wake"))
169 gc->unused = 0xffffffff & ~data->irq_map_mask;
170 gc->reg_base = data->base;
171 gc->private = data;
172 ct = gc->chip_types;
173
174 ct->regs.mask = IRQEN;
175 ct->chip.irq_mask = irq_gc_mask_clr_bit;
176 ct->chip.irq_unmask = irq_gc_mask_set_bit;
177 ct->chip.irq_ack = irq_gc_noop;
178 ct->chip.irq_suspend = bcm7120_l2_intc_suspend;
179 ct->chip.irq_resume = bcm7120_l2_intc_resume;
180
181 if (of_property_read_bool(dn, "brcm,irq-can-wake")) {
182 data->can_wake = true; 207 data->can_wake = true;
183 /* This IRQ chip can wake the system, set all relevant child 208
184 * interupts in wake_enabled mask 209 for (idx = 0; idx < data->n_words; idx++) {
185 */ 210 irq = idx * IRQS_PER_WORD;
186 gc->wake_enabled = 0xffffffff; 211 gc = irq_get_domain_generic_chip(data->domain, irq);
187 gc->wake_enabled &= ~gc->unused; 212
188 ct->chip.irq_set_wake = irq_gc_set_wake; 213 gc->unused = 0xffffffff & ~data->irq_map_mask[idx];
214 gc->reg_base = data->base[idx];
215 gc->private = data;
216 ct = gc->chip_types;
217
218 ct->regs.mask = IRQEN;
219 ct->chip.irq_mask = irq_gc_mask_clr_bit;
220 ct->chip.irq_unmask = irq_gc_mask_set_bit;
221 ct->chip.irq_ack = irq_gc_noop;
222 ct->chip.irq_suspend = bcm7120_l2_intc_suspend;
223 ct->chip.irq_resume = bcm7120_l2_intc_resume;
224
225 if (data->can_wake) {
226 /* This IRQ chip can wake the system, set all
227 * relevant child interupts in wake_enabled mask
228 */
229 gc->wake_enabled = 0xffffffff;
230 gc->wake_enabled &= ~gc->unused;
231 ct->chip.irq_set_wake = irq_gc_set_wake;
232 }
189 } 233 }
190 234
191 pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n", 235 pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n",
192 data->base, num_parent_irqs); 236 data->base[0], num_parent_irqs);
193 237
194 return 0; 238 return 0;
195 239
196out_free_domain: 240out_free_domain:
197 irq_domain_remove(data->domain); 241 irq_domain_remove(data->domain);
198out_unmap: 242out_unmap:
199 iounmap(data->base); 243 for (idx = 0; idx < MAX_WORDS; idx++) {
200out_free: 244 if (data->base[idx])
245 iounmap(data->base[idx]);
246 }
201 kfree(data); 247 kfree(data);
202 return ret; 248 return ret;
203} 249}