diff options
Diffstat (limited to 'drivers/irqchip/irq-bcm7120-l2.c')
-rw-r--r-- | drivers/irqchip/irq-bcm7120-l2.c | 76 |
1 files changed, 53 insertions, 23 deletions
diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index 3ba5cc780fcb..d3f976913a6f 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c | |||
@@ -26,10 +26,9 @@ | |||
26 | #include <linux/irqdomain.h> | 26 | #include <linux/irqdomain.h> |
27 | #include <linux/reboot.h> | 27 | #include <linux/reboot.h> |
28 | #include <linux/bitops.h> | 28 | #include <linux/bitops.h> |
29 | #include <linux/irqchip.h> | ||
29 | #include <linux/irqchip/chained_irq.h> | 30 | #include <linux/irqchip/chained_irq.h> |
30 | 31 | ||
31 | #include "irqchip.h" | ||
32 | |||
33 | /* Register offset in the L2 interrupt controller */ | 32 | /* Register offset in the L2 interrupt controller */ |
34 | #define IRQEN 0x00 | 33 | #define IRQEN 0x00 |
35 | #define IRQSTAT 0x04 | 34 | #define IRQSTAT 0x04 |
@@ -38,6 +37,11 @@ | |||
38 | #define MAX_MAPPINGS (MAX_WORDS * 2) | 37 | #define MAX_MAPPINGS (MAX_WORDS * 2) |
39 | #define IRQS_PER_WORD 32 | 38 | #define IRQS_PER_WORD 32 |
40 | 39 | ||
40 | struct bcm7120_l1_intc_data { | ||
41 | struct bcm7120_l2_intc_data *b; | ||
42 | u32 irq_map_mask[MAX_WORDS]; | ||
43 | }; | ||
44 | |||
41 | struct bcm7120_l2_intc_data { | 45 | struct bcm7120_l2_intc_data { |
42 | unsigned int n_words; | 46 | unsigned int n_words; |
43 | void __iomem *map_base[MAX_MAPPINGS]; | 47 | void __iomem *map_base[MAX_MAPPINGS]; |
@@ -47,14 +51,15 @@ struct bcm7120_l2_intc_data { | |||
47 | struct irq_domain *domain; | 51 | struct irq_domain *domain; |
48 | bool can_wake; | 52 | bool can_wake; |
49 | u32 irq_fwd_mask[MAX_WORDS]; | 53 | u32 irq_fwd_mask[MAX_WORDS]; |
50 | u32 irq_map_mask[MAX_WORDS]; | 54 | struct bcm7120_l1_intc_data *l1_data; |
51 | int num_parent_irqs; | 55 | int num_parent_irqs; |
52 | const __be32 *map_mask_prop; | 56 | const __be32 *map_mask_prop; |
53 | }; | 57 | }; |
54 | 58 | ||
55 | static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) | 59 | static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) |
56 | { | 60 | { |
57 | struct bcm7120_l2_intc_data *b = irq_desc_get_handler_data(desc); | 61 | struct bcm7120_l1_intc_data *data = irq_desc_get_handler_data(desc); |
62 | struct bcm7120_l2_intc_data *b = data->b; | ||
58 | struct irq_chip *chip = irq_desc_get_chip(desc); | 63 | struct irq_chip *chip = irq_desc_get_chip(desc); |
59 | unsigned int idx; | 64 | unsigned int idx; |
60 | 65 | ||
@@ -69,7 +74,8 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) | |||
69 | 74 | ||
70 | irq_gc_lock(gc); | 75 | irq_gc_lock(gc); |
71 | pending = irq_reg_readl(gc, b->stat_offset[idx]) & | 76 | pending = irq_reg_readl(gc, b->stat_offset[idx]) & |
72 | gc->mask_cache; | 77 | gc->mask_cache & |
78 | data->irq_map_mask[idx]; | ||
73 | irq_gc_unlock(gc); | 79 | irq_gc_unlock(gc); |
74 | 80 | ||
75 | for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { | 81 | for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { |
@@ -81,11 +87,10 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) | |||
81 | chained_irq_exit(chip, desc); | 87 | chained_irq_exit(chip, desc); |
82 | } | 88 | } |
83 | 89 | ||
84 | static void bcm7120_l2_intc_suspend(struct irq_data *d) | 90 | static void bcm7120_l2_intc_suspend(struct irq_chip_generic *gc) |
85 | { | 91 | { |
86 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | ||
87 | struct irq_chip_type *ct = irq_data_get_chip_type(d); | ||
88 | struct bcm7120_l2_intc_data *b = gc->private; | 92 | struct bcm7120_l2_intc_data *b = gc->private; |
93 | struct irq_chip_type *ct = gc->chip_types; | ||
89 | 94 | ||
90 | irq_gc_lock(gc); | 95 | irq_gc_lock(gc); |
91 | if (b->can_wake) | 96 | if (b->can_wake) |
@@ -94,10 +99,9 @@ static void bcm7120_l2_intc_suspend(struct irq_data *d) | |||
94 | irq_gc_unlock(gc); | 99 | irq_gc_unlock(gc); |
95 | } | 100 | } |
96 | 101 | ||
97 | static void bcm7120_l2_intc_resume(struct irq_data *d) | 102 | static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc) |
98 | { | 103 | { |
99 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); | 104 | struct irq_chip_type *ct = gc->chip_types; |
100 | struct irq_chip_type *ct = irq_data_get_chip_type(d); | ||
101 | 105 | ||
102 | /* Restore the saved mask */ | 106 | /* Restore the saved mask */ |
103 | irq_gc_lock(gc); | 107 | irq_gc_lock(gc); |
@@ -107,8 +111,9 @@ static void bcm7120_l2_intc_resume(struct irq_data *d) | |||
107 | 111 | ||
108 | static int bcm7120_l2_intc_init_one(struct device_node *dn, | 112 | static int bcm7120_l2_intc_init_one(struct device_node *dn, |
109 | struct bcm7120_l2_intc_data *data, | 113 | struct bcm7120_l2_intc_data *data, |
110 | int irq) | 114 | int irq, u32 *valid_mask) |
111 | { | 115 | { |
116 | struct bcm7120_l1_intc_data *l1_data = &data->l1_data[irq]; | ||
112 | int parent_irq; | 117 | int parent_irq; |
113 | unsigned int idx; | 118 | unsigned int idx; |
114 | 119 | ||
@@ -120,20 +125,28 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, | |||
120 | 125 | ||
121 | /* For multiple parent IRQs with multiple words, this looks like: | 126 | /* For multiple parent IRQs with multiple words, this looks like: |
122 | * <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...> | 127 | * <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...> |
128 | * | ||
129 | * We need to associate a given parent interrupt with its corresponding | ||
130 | * map_mask in order to mask the status register with it because we | ||
131 | * have the same handler being called for multiple parent interrupts. | ||
132 | * | ||
133 | * This is typically something needed on BCM7xxx (STB chips). | ||
123 | */ | 134 | */ |
124 | for (idx = 0; idx < data->n_words; idx++) { | 135 | for (idx = 0; idx < data->n_words; idx++) { |
125 | if (data->map_mask_prop) { | 136 | if (data->map_mask_prop) { |
126 | data->irq_map_mask[idx] |= | 137 | l1_data->irq_map_mask[idx] |= |
127 | be32_to_cpup(data->map_mask_prop + | 138 | be32_to_cpup(data->map_mask_prop + |
128 | irq * data->n_words + idx); | 139 | irq * data->n_words + idx); |
129 | } else { | 140 | } else { |
130 | data->irq_map_mask[idx] = 0xffffffff; | 141 | l1_data->irq_map_mask[idx] = 0xffffffff; |
131 | } | 142 | } |
143 | valid_mask[idx] |= l1_data->irq_map_mask[idx]; | ||
132 | } | 144 | } |
133 | 145 | ||
134 | irq_set_handler_data(parent_irq, data); | 146 | l1_data->b = data; |
135 | irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle); | ||
136 | 147 | ||
148 | irq_set_chained_handler_and_data(parent_irq, | ||
149 | bcm7120_l2_intc_irq_handle, l1_data); | ||
137 | return 0; | 150 | return 0; |
138 | } | 151 | } |
139 | 152 | ||
@@ -214,6 +227,7 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, | |||
214 | struct irq_chip_type *ct; | 227 | struct irq_chip_type *ct; |
215 | int ret = 0; | 228 | int ret = 0; |
216 | unsigned int idx, irq, flags; | 229 | unsigned int idx, irq, flags; |
230 | u32 valid_mask[MAX_WORDS] = { }; | ||
217 | 231 | ||
218 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 232 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
219 | if (!data) | 233 | if (!data) |
@@ -226,9 +240,16 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, | |||
226 | goto out_unmap; | 240 | goto out_unmap; |
227 | } | 241 | } |
228 | 242 | ||
243 | data->l1_data = kcalloc(data->num_parent_irqs, sizeof(*data->l1_data), | ||
244 | GFP_KERNEL); | ||
245 | if (!data->l1_data) { | ||
246 | ret = -ENOMEM; | ||
247 | goto out_free_l1_data; | ||
248 | } | ||
249 | |||
229 | ret = iomap_regs_fn(dn, data); | 250 | ret = iomap_regs_fn(dn, data); |
230 | if (ret < 0) | 251 | if (ret < 0) |
231 | goto out_unmap; | 252 | goto out_free_l1_data; |
232 | 253 | ||
233 | for (idx = 0; idx < data->n_words; idx++) { | 254 | for (idx = 0; idx < data->n_words; idx++) { |
234 | __raw_writel(data->irq_fwd_mask[idx], | 255 | __raw_writel(data->irq_fwd_mask[idx], |
@@ -237,16 +258,16 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, | |||
237 | } | 258 | } |
238 | 259 | ||
239 | for (irq = 0; irq < data->num_parent_irqs; irq++) { | 260 | for (irq = 0; irq < data->num_parent_irqs; irq++) { |
240 | ret = bcm7120_l2_intc_init_one(dn, data, irq); | 261 | ret = bcm7120_l2_intc_init_one(dn, data, irq, valid_mask); |
241 | if (ret) | 262 | if (ret) |
242 | goto out_unmap; | 263 | goto out_free_l1_data; |
243 | } | 264 | } |
244 | 265 | ||
245 | data->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * data->n_words, | 266 | data->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * data->n_words, |
246 | &irq_generic_chip_ops, NULL); | 267 | &irq_generic_chip_ops, NULL); |
247 | if (!data->domain) { | 268 | if (!data->domain) { |
248 | ret = -ENOMEM; | 269 | ret = -ENOMEM; |
249 | goto out_unmap; | 270 | goto out_free_l1_data; |
250 | } | 271 | } |
251 | 272 | ||
252 | /* MIPS chips strapped for BE will automagically configure the | 273 | /* MIPS chips strapped for BE will automagically configure the |
@@ -270,7 +291,7 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, | |||
270 | irq = idx * IRQS_PER_WORD; | 291 | irq = idx * IRQS_PER_WORD; |
271 | gc = irq_get_domain_generic_chip(data->domain, irq); | 292 | gc = irq_get_domain_generic_chip(data->domain, irq); |
272 | 293 | ||
273 | gc->unused = 0xffffffff & ~data->irq_map_mask[idx]; | 294 | gc->unused = 0xffffffff & ~valid_mask[idx]; |
274 | gc->private = data; | 295 | gc->private = data; |
275 | ct = gc->chip_types; | 296 | ct = gc->chip_types; |
276 | 297 | ||
@@ -280,8 +301,15 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, | |||
280 | ct->chip.irq_mask = irq_gc_mask_clr_bit; | 301 | ct->chip.irq_mask = irq_gc_mask_clr_bit; |
281 | ct->chip.irq_unmask = irq_gc_mask_set_bit; | 302 | ct->chip.irq_unmask = irq_gc_mask_set_bit; |
282 | ct->chip.irq_ack = irq_gc_noop; | 303 | ct->chip.irq_ack = irq_gc_noop; |
283 | ct->chip.irq_suspend = bcm7120_l2_intc_suspend; | 304 | gc->suspend = bcm7120_l2_intc_suspend; |
284 | ct->chip.irq_resume = bcm7120_l2_intc_resume; | 305 | gc->resume = bcm7120_l2_intc_resume; |
306 | |||
307 | /* | ||
308 | * Initialize mask-cache, in case we need it for | ||
309 | * saving/restoring fwd mask even w/o any child interrupts | ||
310 | * installed | ||
311 | */ | ||
312 | gc->mask_cache = irq_reg_readl(gc, ct->regs.mask); | ||
285 | 313 | ||
286 | if (data->can_wake) { | 314 | if (data->can_wake) { |
287 | /* This IRQ chip can wake the system, set all | 315 | /* This IRQ chip can wake the system, set all |
@@ -300,6 +328,8 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, | |||
300 | 328 | ||
301 | out_free_domain: | 329 | out_free_domain: |
302 | irq_domain_remove(data->domain); | 330 | irq_domain_remove(data->domain); |
331 | out_free_l1_data: | ||
332 | kfree(data->l1_data); | ||
303 | out_unmap: | 333 | out_unmap: |
304 | for (idx = 0; idx < MAX_MAPPINGS; idx++) { | 334 | for (idx = 0; idx < MAX_MAPPINGS; idx++) { |
305 | if (data->map_base[idx]) | 335 | if (data->map_base[idx]) |