summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Fainelli <f.fainelli@gmail.com>2015-07-23 18:52:21 -0400
committerThomas Gleixner <tglx@linutronix.de>2015-07-27 02:09:38 -0400
commit0aef3997e12a10d4dfb6e01133e2fe478b9aa5eb (patch)
tree9f590ae0085e4e9945b99952b1761ade0c5f4e3a
parentfd537766715e9b6bf7ff07abb22f4817201433db (diff)
irqchip/bcm7120-l2: Fix interrupt status for multiple parent IRQs
Our irq-bcm7120-l2 interrupt controller driver utilizes the same handler function for the different parent interrupts it services: UPG_MAIN, UPG_BSC for instance. The problem is that function reads the IRQSTAT register which can combine interrupt causes for different parent interrupts, such that we can end-up in the following situation: - CPU takes an interrupt - bcm7120_l2_intc_irq_handle() reads IRQSTAT - generic_handle_irq() is invoked - there are still pending interrupts flagged in IRQSTAT from a different parent - handle_bad_irq() is invoked for these since they come from a different irq_desc/irq In order to fix this, make sure that we always mask IRQSTAT with the appropriate bits that correspond go the parent interrupt source this is coming from. To simplify things, associate an unique structure per parent interrupt handler to avoid multiplying the number of lookups. Fixes: a5042de2688d ("irqchip: bcm7120-l2: Add Broadcom BCM7120-style Level 2 interrupt controller") Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Cc: linux-mips@linux-mips.org Cc: cernekee@gmail.com Cc: jason@lakedaemon.net Cc: bcm-kernel-feedback-list@broadcom.com Cc: gregory.0xf0@gmail.com Cc: computersforpeace@gmail.com Link: http://lkml.kernel.org/r/1437691941-3100-1-git-send-email-f.fainelli@gmail.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--drivers/irqchip/irq-bcm7120-l2.c52
1 files changed, 39 insertions, 13 deletions
diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c
index c885a5c4632a..d3f976913a6f 100644
--- a/drivers/irqchip/irq-bcm7120-l2.c
+++ b/drivers/irqchip/irq-bcm7120-l2.c
@@ -37,6 +37,11 @@
37#define MAX_MAPPINGS (MAX_WORDS * 2) 37#define MAX_MAPPINGS (MAX_WORDS * 2)
38#define IRQS_PER_WORD 32 38#define IRQS_PER_WORD 32
39 39
40struct bcm7120_l1_intc_data {
41 struct bcm7120_l2_intc_data *b;
42 u32 irq_map_mask[MAX_WORDS];
43};
44
40struct bcm7120_l2_intc_data { 45struct bcm7120_l2_intc_data {
41 unsigned int n_words; 46 unsigned int n_words;
42 void __iomem *map_base[MAX_MAPPINGS]; 47 void __iomem *map_base[MAX_MAPPINGS];
@@ -46,14 +51,15 @@ struct bcm7120_l2_intc_data {
46 struct irq_domain *domain; 51 struct irq_domain *domain;
47 bool can_wake; 52 bool can_wake;
48 u32 irq_fwd_mask[MAX_WORDS]; 53 u32 irq_fwd_mask[MAX_WORDS];
49 u32 irq_map_mask[MAX_WORDS]; 54 struct bcm7120_l1_intc_data *l1_data;
50 int num_parent_irqs; 55 int num_parent_irqs;
51 const __be32 *map_mask_prop; 56 const __be32 *map_mask_prop;
52}; 57};
53 58
54static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) 59static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
55{ 60{
56 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;
57 struct irq_chip *chip = irq_desc_get_chip(desc); 63 struct irq_chip *chip = irq_desc_get_chip(desc);
58 unsigned int idx; 64 unsigned int idx;
59 65
@@ -68,7 +74,8 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
68 74
69 irq_gc_lock(gc); 75 irq_gc_lock(gc);
70 pending = irq_reg_readl(gc, b->stat_offset[idx]) & 76 pending = irq_reg_readl(gc, b->stat_offset[idx]) &
71 gc->mask_cache; 77 gc->mask_cache &
78 data->irq_map_mask[idx];
72 irq_gc_unlock(gc); 79 irq_gc_unlock(gc);
73 80
74 for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { 81 for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) {
@@ -104,8 +111,9 @@ static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc)
104 111
105static int bcm7120_l2_intc_init_one(struct device_node *dn, 112static int bcm7120_l2_intc_init_one(struct device_node *dn,
106 struct bcm7120_l2_intc_data *data, 113 struct bcm7120_l2_intc_data *data,
107 int irq) 114 int irq, u32 *valid_mask)
108{ 115{
116 struct bcm7120_l1_intc_data *l1_data = &data->l1_data[irq];
109 int parent_irq; 117 int parent_irq;
110 unsigned int idx; 118 unsigned int idx;
111 119
@@ -117,20 +125,28 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn,
117 125
118 /* For multiple parent IRQs with multiple words, this looks like: 126 /* For multiple parent IRQs with multiple words, this looks like:
119 * <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).
120 */ 134 */
121 for (idx = 0; idx < data->n_words; idx++) { 135 for (idx = 0; idx < data->n_words; idx++) {
122 if (data->map_mask_prop) { 136 if (data->map_mask_prop) {
123 data->irq_map_mask[idx] |= 137 l1_data->irq_map_mask[idx] |=
124 be32_to_cpup(data->map_mask_prop + 138 be32_to_cpup(data->map_mask_prop +
125 irq * data->n_words + idx); 139 irq * data->n_words + idx);
126 } else { 140 } else {
127 data->irq_map_mask[idx] = 0xffffffff; 141 l1_data->irq_map_mask[idx] = 0xffffffff;
128 } 142 }
143 valid_mask[idx] |= l1_data->irq_map_mask[idx];
129 } 144 }
130 145
131 irq_set_chained_handler_and_data(parent_irq, 146 l1_data->b = data;
132 bcm7120_l2_intc_irq_handle, data);
133 147
148 irq_set_chained_handler_and_data(parent_irq,
149 bcm7120_l2_intc_irq_handle, l1_data);
134 return 0; 150 return 0;
135} 151}
136 152
@@ -211,6 +227,7 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn,
211 struct irq_chip_type *ct; 227 struct irq_chip_type *ct;
212 int ret = 0; 228 int ret = 0;
213 unsigned int idx, irq, flags; 229 unsigned int idx, irq, flags;
230 u32 valid_mask[MAX_WORDS] = { };
214 231
215 data = kzalloc(sizeof(*data), GFP_KERNEL); 232 data = kzalloc(sizeof(*data), GFP_KERNEL);
216 if (!data) 233 if (!data)
@@ -223,9 +240,16 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn,
223 goto out_unmap; 240 goto out_unmap;
224 } 241 }
225 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
226 ret = iomap_regs_fn(dn, data); 250 ret = iomap_regs_fn(dn, data);
227 if (ret < 0) 251 if (ret < 0)
228 goto out_unmap; 252 goto out_free_l1_data;
229 253
230 for (idx = 0; idx < data->n_words; idx++) { 254 for (idx = 0; idx < data->n_words; idx++) {
231 __raw_writel(data->irq_fwd_mask[idx], 255 __raw_writel(data->irq_fwd_mask[idx],
@@ -234,16 +258,16 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn,
234 } 258 }
235 259
236 for (irq = 0; irq < data->num_parent_irqs; irq++) { 260 for (irq = 0; irq < data->num_parent_irqs; irq++) {
237 ret = bcm7120_l2_intc_init_one(dn, data, irq); 261 ret = bcm7120_l2_intc_init_one(dn, data, irq, valid_mask);
238 if (ret) 262 if (ret)
239 goto out_unmap; 263 goto out_free_l1_data;
240 } 264 }
241 265
242 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,
243 &irq_generic_chip_ops, NULL); 267 &irq_generic_chip_ops, NULL);
244 if (!data->domain) { 268 if (!data->domain) {
245 ret = -ENOMEM; 269 ret = -ENOMEM;
246 goto out_unmap; 270 goto out_free_l1_data;
247 } 271 }
248 272
249 /* MIPS chips strapped for BE will automagically configure the 273 /* MIPS chips strapped for BE will automagically configure the
@@ -267,7 +291,7 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn,
267 irq = idx * IRQS_PER_WORD; 291 irq = idx * IRQS_PER_WORD;
268 gc = irq_get_domain_generic_chip(data->domain, irq); 292 gc = irq_get_domain_generic_chip(data->domain, irq);
269 293
270 gc->unused = 0xffffffff & ~data->irq_map_mask[idx]; 294 gc->unused = 0xffffffff & ~valid_mask[idx];
271 gc->private = data; 295 gc->private = data;
272 ct = gc->chip_types; 296 ct = gc->chip_types;
273 297
@@ -304,6 +328,8 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn,
304 328
305out_free_domain: 329out_free_domain:
306 irq_domain_remove(data->domain); 330 irq_domain_remove(data->domain);
331out_free_l1_data:
332 kfree(data->l1_data);
307out_unmap: 333out_unmap:
308 for (idx = 0; idx < MAX_MAPPINGS; idx++) { 334 for (idx = 0; idx < MAX_MAPPINGS; idx++) {
309 if (data->map_base[idx]) 335 if (data->map_base[idx])