Commit | Line | Data |
---|---|---|
5698c50d JH |
1 | /* |
2 | * Meta External interrupt code. | |
3 | * | |
4 | * Copyright (C) 2005-2012 Imagination Technologies Ltd. | |
5 | * | |
6 | * External interrupts on Meta are configured at two-levels, in the CPU core and | |
7 | * in the external trigger block. Interrupts from SoC peripherals are | |
8 | * multiplexed onto a single Meta CPU "trigger" - traditionally it has always | |
9 | * been trigger 2 (TR2). For info on how de-multiplexing happens check out | |
10 | * meta_intc_irq_demux(). | |
11 | */ | |
12 | ||
13 | #include <linux/interrupt.h> | |
14 | #include <linux/irqchip/metag-ext.h> | |
15 | #include <linux/irqdomain.h> | |
16 | #include <linux/io.h> | |
17 | #include <linux/of.h> | |
18 | #include <linux/slab.h> | |
19 | #include <linux/syscore_ops.h> | |
20 | ||
21 | #include <asm/irq.h> | |
22 | #include <asm/hwthread.h> | |
23 | ||
24 | #define HWSTAT_STRIDE 8 | |
25 | #define HWVEC_BLK_STRIDE 0x1000 | |
26 | ||
27 | /** | |
28 | * struct meta_intc_priv - private meta external interrupt data | |
29 | * @nr_banks: Number of interrupt banks | |
30 | * @domain: IRQ domain for all banks of external IRQs | |
31 | * @unmasked: Record of unmasked IRQs | |
32 | * @levels_altered: Record of altered level bits | |
33 | */ | |
34 | struct meta_intc_priv { | |
35 | unsigned int nr_banks; | |
36 | struct irq_domain *domain; | |
37 | ||
38 | unsigned long unmasked[4]; | |
39 | ||
40 | #ifdef CONFIG_METAG_SUSPEND_MEM | |
41 | unsigned long levels_altered[4]; | |
42 | #endif | |
43 | }; | |
44 | ||
45 | /* Private data for the one and only external interrupt controller */ | |
46 | static struct meta_intc_priv meta_intc_priv; | |
47 | ||
48 | /** | |
49 | * meta_intc_offset() - Get the offset into the bank of a hardware IRQ number | |
50 | * @hw: Hardware IRQ number (within external trigger block) | |
51 | * | |
52 | * Returns: Bit offset into the IRQ's bank registers | |
53 | */ | |
54 | static unsigned int meta_intc_offset(irq_hw_number_t hw) | |
55 | { | |
56 | return hw & 0x1f; | |
57 | } | |
58 | ||
59 | /** | |
60 | * meta_intc_bank() - Get the bank number of a hardware IRQ number | |
61 | * @hw: Hardware IRQ number (within external trigger block) | |
62 | * | |
63 | * Returns: Bank number indicating which register the IRQ's bits are | |
64 | */ | |
65 | static unsigned int meta_intc_bank(irq_hw_number_t hw) | |
66 | { | |
67 | return hw >> 5; | |
68 | } | |
69 | ||
70 | /** | |
71 | * meta_intc_stat_addr() - Get the address of a HWSTATEXT register | |
72 | * @hw: Hardware IRQ number (within external trigger block) | |
73 | * | |
74 | * Returns: Address of a HWSTATEXT register containing the status bit for | |
75 | * the specified hardware IRQ number | |
76 | */ | |
77 | static void __iomem *meta_intc_stat_addr(irq_hw_number_t hw) | |
78 | { | |
79 | return (void __iomem *)(HWSTATEXT + | |
80 | HWSTAT_STRIDE * meta_intc_bank(hw)); | |
81 | } | |
82 | ||
83 | /** | |
84 | * meta_intc_level_addr() - Get the address of a HWLEVELEXT register | |
85 | * @hw: Hardware IRQ number (within external trigger block) | |
86 | * | |
87 | * Returns: Address of a HWLEVELEXT register containing the sense bit for | |
88 | * the specified hardware IRQ number | |
89 | */ | |
90 | static void __iomem *meta_intc_level_addr(irq_hw_number_t hw) | |
91 | { | |
92 | return (void __iomem *)(HWLEVELEXT + | |
93 | HWSTAT_STRIDE * meta_intc_bank(hw)); | |
94 | } | |
95 | ||
96 | /** | |
97 | * meta_intc_mask_addr() - Get the address of a HWMASKEXT register | |
98 | * @hw: Hardware IRQ number (within external trigger block) | |
99 | * | |
100 | * Returns: Address of a HWMASKEXT register containing the mask bit for the | |
101 | * specified hardware IRQ number | |
102 | */ | |
103 | static void __iomem *meta_intc_mask_addr(irq_hw_number_t hw) | |
104 | { | |
105 | return (void __iomem *)(HWMASKEXT + | |
106 | HWSTAT_STRIDE * meta_intc_bank(hw)); | |
107 | } | |
108 | ||
109 | /** | |
110 | * meta_intc_vec_addr() - Get the vector address of a hardware interrupt | |
111 | * @hw: Hardware IRQ number (within external trigger block) | |
112 | * | |
113 | * Returns: Address of a HWVECEXT register controlling the core trigger to | |
114 | * vector the IRQ onto | |
115 | */ | |
116 | static inline void __iomem *meta_intc_vec_addr(irq_hw_number_t hw) | |
117 | { | |
118 | return (void __iomem *)(HWVEC0EXT + | |
119 | HWVEC_BLK_STRIDE * meta_intc_bank(hw) + | |
120 | HWVECnEXT_STRIDE * meta_intc_offset(hw)); | |
121 | } | |
122 | ||
123 | /** | |
124 | * meta_intc_startup_irq() - set up an external irq | |
125 | * @data: data for the external irq to start up | |
126 | * | |
127 | * Multiplex interrupts for irq onto TR2. Clear any pending interrupts and | |
128 | * unmask irq, both using the appropriate callbacks. | |
129 | */ | |
130 | static unsigned int meta_intc_startup_irq(struct irq_data *data) | |
131 | { | |
132 | irq_hw_number_t hw = data->hwirq; | |
133 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | |
134 | int thread = hard_processor_id(); | |
135 | ||
136 | /* Perform any necessary acking. */ | |
137 | if (data->chip->irq_ack) | |
138 | data->chip->irq_ack(data); | |
139 | ||
140 | /* Wire up this interrupt to the core with HWVECxEXT. */ | |
141 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); | |
142 | ||
143 | /* Perform any necessary unmasking. */ | |
144 | data->chip->irq_unmask(data); | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
149 | /** | |
150 | * meta_intc_shutdown_irq() - turn off an external irq | |
151 | * @data: data for the external irq to turn off | |
152 | * | |
153 | * Mask irq using the appropriate callback and stop muxing it onto TR2. | |
154 | */ | |
155 | static void meta_intc_shutdown_irq(struct irq_data *data) | |
156 | { | |
157 | irq_hw_number_t hw = data->hwirq; | |
158 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | |
159 | ||
160 | /* Mask the IRQ */ | |
161 | data->chip->irq_mask(data); | |
162 | ||
163 | /* | |
164 | * Disable the IRQ at the core by removing the interrupt from | |
165 | * the HW vector mapping. | |
166 | */ | |
167 | metag_out32(0, vec_addr); | |
168 | } | |
169 | ||
170 | /** | |
171 | * meta_intc_ack_irq() - acknowledge an external irq | |
172 | * @data: data for the external irq to ack | |
173 | * | |
174 | * Clear down an edge interrupt in the status register. | |
175 | */ | |
176 | static void meta_intc_ack_irq(struct irq_data *data) | |
177 | { | |
178 | irq_hw_number_t hw = data->hwirq; | |
179 | unsigned int bit = 1 << meta_intc_offset(hw); | |
180 | void __iomem *stat_addr = meta_intc_stat_addr(hw); | |
181 | ||
182 | /* Ack the int, if it is still 'on'. | |
183 | * NOTE - this only works for edge triggered interrupts. | |
184 | */ | |
185 | if (metag_in32(stat_addr) & bit) | |
186 | metag_out32(bit, stat_addr); | |
187 | } | |
188 | ||
189 | /** | |
190 | * record_irq_is_masked() - record the IRQ masked so it doesn't get handled | |
191 | * @data: data for the external irq to record | |
192 | * | |
193 | * This should get called whenever an external IRQ is masked (by whichever | |
194 | * callback is used). It records the IRQ masked so that it doesn't get handled | |
195 | * if it still shows up in the status register. | |
196 | */ | |
197 | static void record_irq_is_masked(struct irq_data *data) | |
198 | { | |
199 | struct meta_intc_priv *priv = &meta_intc_priv; | |
200 | irq_hw_number_t hw = data->hwirq; | |
201 | ||
202 | clear_bit(meta_intc_offset(hw), &priv->unmasked[meta_intc_bank(hw)]); | |
203 | } | |
204 | ||
205 | /** | |
206 | * record_irq_is_unmasked() - record the IRQ unmasked so it can be handled | |
207 | * @data: data for the external irq to record | |
208 | * | |
209 | * This should get called whenever an external IRQ is unmasked (by whichever | |
210 | * callback is used). It records the IRQ unmasked so that it gets handled if it | |
211 | * shows up in the status register. | |
212 | */ | |
213 | static void record_irq_is_unmasked(struct irq_data *data) | |
214 | { | |
215 | struct meta_intc_priv *priv = &meta_intc_priv; | |
216 | irq_hw_number_t hw = data->hwirq; | |
217 | ||
218 | set_bit(meta_intc_offset(hw), &priv->unmasked[meta_intc_bank(hw)]); | |
219 | } | |
220 | ||
221 | /* | |
222 | * For use by wrapper IRQ drivers | |
223 | */ | |
224 | ||
225 | /** | |
226 | * meta_intc_mask_irq_simple() - minimal mask used by wrapper IRQ drivers | |
227 | * @data: data for the external irq being masked | |
228 | * | |
229 | * This should be called by any wrapper IRQ driver mask functions. it doesn't do | |
230 | * any masking but records the IRQ as masked so that the core code knows the | |
231 | * mask has taken place. It is the callers responsibility to ensure that the IRQ | |
232 | * won't trigger an interrupt to the core. | |
233 | */ | |
234 | void meta_intc_mask_irq_simple(struct irq_data *data) | |
235 | { | |
236 | record_irq_is_masked(data); | |
237 | } | |
238 | ||
239 | /** | |
240 | * meta_intc_unmask_irq_simple() - minimal unmask used by wrapper IRQ drivers | |
241 | * @data: data for the external irq being unmasked | |
242 | * | |
243 | * This should be called by any wrapper IRQ driver unmask functions. it doesn't | |
244 | * do any unmasking but records the IRQ as unmasked so that the core code knows | |
245 | * the unmask has taken place. It is the callers responsibility to ensure that | |
246 | * the IRQ can now trigger an interrupt to the core. | |
247 | */ | |
248 | void meta_intc_unmask_irq_simple(struct irq_data *data) | |
249 | { | |
250 | record_irq_is_unmasked(data); | |
251 | } | |
252 | ||
253 | ||
254 | /** | |
255 | * meta_intc_mask_irq() - mask an external irq using HWMASKEXT | |
256 | * @data: data for the external irq to mask | |
257 | * | |
258 | * This is a default implementation of a mask function which makes use of the | |
259 | * HWMASKEXT registers available in newer versions. | |
260 | * | |
261 | * Earlier versions without these registers should use SoC level IRQ masking | |
262 | * which call the meta_intc_*_simple() functions above, or if that isn't | |
263 | * available should use the fallback meta_intc_*_nomask() functions below. | |
264 | */ | |
265 | static void meta_intc_mask_irq(struct irq_data *data) | |
266 | { | |
267 | irq_hw_number_t hw = data->hwirq; | |
268 | unsigned int bit = 1 << meta_intc_offset(hw); | |
269 | void __iomem *mask_addr = meta_intc_mask_addr(hw); | |
270 | unsigned long flags; | |
271 | ||
272 | record_irq_is_masked(data); | |
273 | ||
274 | /* update the interrupt mask */ | |
275 | __global_lock2(flags); | |
276 | metag_out32(metag_in32(mask_addr) & ~bit, mask_addr); | |
277 | __global_unlock2(flags); | |
278 | } | |
279 | ||
280 | /** | |
281 | * meta_intc_unmask_irq() - unmask an external irq using HWMASKEXT | |
282 | * @data: data for the external irq to unmask | |
283 | * | |
284 | * This is a default implementation of an unmask function which makes use of the | |
285 | * HWMASKEXT registers available on new versions. It should be paired with | |
286 | * meta_intc_mask_irq() above. | |
287 | */ | |
288 | static void meta_intc_unmask_irq(struct irq_data *data) | |
289 | { | |
290 | irq_hw_number_t hw = data->hwirq; | |
291 | unsigned int bit = 1 << meta_intc_offset(hw); | |
292 | void __iomem *mask_addr = meta_intc_mask_addr(hw); | |
293 | unsigned long flags; | |
294 | ||
295 | record_irq_is_unmasked(data); | |
296 | ||
297 | /* update the interrupt mask */ | |
298 | __global_lock2(flags); | |
299 | metag_out32(metag_in32(mask_addr) | bit, mask_addr); | |
300 | __global_unlock2(flags); | |
301 | } | |
302 | ||
303 | /** | |
304 | * meta_intc_mask_irq_nomask() - mask an external irq by unvectoring | |
305 | * @data: data for the external irq to mask | |
306 | * | |
307 | * This is the version of the mask function for older versions which don't have | |
308 | * HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the IRQ is | |
309 | * unvectored from the core and retriggered if necessary later. | |
310 | */ | |
311 | static void meta_intc_mask_irq_nomask(struct irq_data *data) | |
312 | { | |
313 | irq_hw_number_t hw = data->hwirq; | |
314 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | |
315 | ||
316 | record_irq_is_masked(data); | |
317 | ||
318 | /* there is no interrupt mask, so unvector the interrupt */ | |
319 | metag_out32(0, vec_addr); | |
320 | } | |
321 | ||
322 | /** | |
323 | * meta_intc_unmask_edge_irq_nomask() - unmask an edge irq by revectoring | |
324 | * @data: data for the external irq to unmask | |
325 | * | |
326 | * This is the version of the unmask function for older versions which don't | |
327 | * have HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the | |
328 | * IRQ is revectored back to the core and retriggered if necessary. | |
329 | * | |
330 | * The retriggering done by this function is specific to edge interrupts. | |
331 | */ | |
332 | static void meta_intc_unmask_edge_irq_nomask(struct irq_data *data) | |
333 | { | |
334 | irq_hw_number_t hw = data->hwirq; | |
335 | unsigned int bit = 1 << meta_intc_offset(hw); | |
336 | void __iomem *stat_addr = meta_intc_stat_addr(hw); | |
337 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | |
338 | unsigned int thread = hard_processor_id(); | |
339 | ||
340 | record_irq_is_unmasked(data); | |
341 | ||
342 | /* there is no interrupt mask, so revector the interrupt */ | |
343 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); | |
344 | ||
345 | /* | |
346 | * Re-trigger interrupt | |
347 | * | |
348 | * Writing a 1 toggles, and a 0->1 transition triggers. We only | |
349 | * retrigger if the status bit is already set, which means we | |
350 | * need to clear it first. Retriggering is fundamentally racy | |
351 | * because if the interrupt fires again after we clear it we | |
352 | * could end up clearing it again and the interrupt handler | |
353 | * thinking it hasn't fired. Therefore we need to keep trying to | |
354 | * retrigger until the bit is set. | |
355 | */ | |
356 | if (metag_in32(stat_addr) & bit) { | |
357 | metag_out32(bit, stat_addr); | |
358 | while (!(metag_in32(stat_addr) & bit)) | |
359 | metag_out32(bit, stat_addr); | |
360 | } | |
361 | } | |
362 | ||
363 | /** | |
364 | * meta_intc_unmask_level_irq_nomask() - unmask a level irq by revectoring | |
365 | * @data: data for the external irq to unmask | |
366 | * | |
367 | * This is the version of the unmask function for older versions which don't | |
368 | * have HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the | |
369 | * IRQ is revectored back to the core and retriggered if necessary. | |
370 | * | |
371 | * The retriggering done by this function is specific to level interrupts. | |
372 | */ | |
373 | static void meta_intc_unmask_level_irq_nomask(struct irq_data *data) | |
374 | { | |
375 | irq_hw_number_t hw = data->hwirq; | |
376 | unsigned int bit = 1 << meta_intc_offset(hw); | |
377 | void __iomem *stat_addr = meta_intc_stat_addr(hw); | |
378 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | |
379 | unsigned int thread = hard_processor_id(); | |
380 | ||
381 | record_irq_is_unmasked(data); | |
382 | ||
383 | /* there is no interrupt mask, so revector the interrupt */ | |
384 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); | |
385 | ||
386 | /* Re-trigger interrupt */ | |
387 | /* Writing a 1 triggers interrupt */ | |
388 | if (metag_in32(stat_addr) & bit) | |
389 | metag_out32(bit, stat_addr); | |
390 | } | |
391 | ||
392 | /** | |
393 | * meta_intc_irq_set_type() - set the type of an external irq | |
394 | * @data: data for the external irq to set the type of | |
395 | * @flow_type: new irq flow type | |
396 | * | |
397 | * Set the flow type of an external interrupt. This updates the irq chip and irq | |
398 | * handler depending on whether the irq is edge or level sensitive (the polarity | |
399 | * is ignored), and also sets up the bit in HWLEVELEXT so the hardware knows | |
400 | * when to trigger. | |
401 | */ | |
402 | static int meta_intc_irq_set_type(struct irq_data *data, unsigned int flow_type) | |
403 | { | |
404 | #ifdef CONFIG_METAG_SUSPEND_MEM | |
405 | struct meta_intc_priv *priv = &meta_intc_priv; | |
406 | #endif | |
407 | unsigned int irq = data->irq; | |
408 | irq_hw_number_t hw = data->hwirq; | |
409 | unsigned int bit = 1 << meta_intc_offset(hw); | |
410 | void __iomem *level_addr = meta_intc_level_addr(hw); | |
411 | unsigned long flags; | |
412 | unsigned int level; | |
413 | ||
414 | /* update the chip/handler */ | |
415 | if (flow_type & IRQ_TYPE_LEVEL_MASK) | |
416 | __irq_set_chip_handler_name_locked(irq, &meta_intc_level_chip, | |
417 | handle_level_irq, NULL); | |
418 | else | |
419 | __irq_set_chip_handler_name_locked(irq, &meta_intc_edge_chip, | |
420 | handle_edge_irq, NULL); | |
421 | ||
422 | /* and clear/set the bit in HWLEVELEXT */ | |
423 | __global_lock2(flags); | |
424 | level = metag_in32(level_addr); | |
425 | if (flow_type & IRQ_TYPE_LEVEL_MASK) | |
426 | level |= bit; | |
427 | else | |
428 | level &= ~bit; | |
429 | metag_out32(level, level_addr); | |
430 | #ifdef CONFIG_METAG_SUSPEND_MEM | |
431 | priv->levels_altered[meta_intc_bank(hw)] |= bit; | |
432 | #endif | |
433 | __global_unlock2(flags); | |
434 | ||
435 | return 0; | |
436 | } | |
437 | ||
438 | /** | |
439 | * meta_intc_irq_demux() - external irq de-multiplexer | |
440 | * @irq: the virtual interrupt number | |
441 | * @desc: the interrupt description structure for this irq | |
442 | * | |
443 | * The cpu receives an interrupt on TR2 when a SoC interrupt has occurred. It is | |
444 | * this function's job to demux this irq and figure out exactly which external | |
445 | * irq needs servicing. | |
446 | * | |
447 | * Whilst using TR2 to detect external interrupts is a software convention it is | |
448 | * (hopefully) unlikely to change. | |
449 | */ | |
450 | static void meta_intc_irq_demux(unsigned int irq, struct irq_desc *desc) | |
451 | { | |
452 | struct meta_intc_priv *priv = &meta_intc_priv; | |
453 | irq_hw_number_t hw; | |
454 | unsigned int bank, irq_no, status; | |
455 | void __iomem *stat_addr = meta_intc_stat_addr(0); | |
456 | ||
457 | /* | |
458 | * Locate which interrupt has caused our handler to run. | |
459 | */ | |
460 | for (bank = 0; bank < priv->nr_banks; ++bank) { | |
461 | /* Which interrupts are currently pending in this bank? */ | |
462 | recalculate: | |
463 | status = metag_in32(stat_addr) & priv->unmasked[bank]; | |
464 | ||
465 | for (hw = bank*32; status; status >>= 1, ++hw) { | |
466 | if (status & 0x1) { | |
467 | /* | |
468 | * Map the hardware IRQ number to a virtual | |
469 | * Linux IRQ number. | |
470 | */ | |
471 | irq_no = irq_linear_revmap(priv->domain, hw); | |
472 | ||
473 | /* | |
474 | * Only fire off external interrupts that are | |
475 | * registered to be handled by the kernel. | |
476 | * Other external interrupts are probably being | |
477 | * handled by other Meta hardware threads. | |
478 | */ | |
479 | generic_handle_irq(irq_no); | |
480 | ||
481 | /* | |
482 | * The handler may have re-enabled interrupts | |
483 | * which could have caused a nested invocation | |
484 | * of this code and make the copy of the | |
485 | * status register we are using invalid. | |
486 | */ | |
487 | goto recalculate; | |
488 | } | |
489 | } | |
490 | stat_addr += HWSTAT_STRIDE; | |
491 | } | |
492 | } | |
493 | ||
494 | #ifdef CONFIG_SMP | |
495 | /** | |
496 | * meta_intc_set_affinity() - set the affinity for an interrupt | |
497 | * @data: data for the external irq to set the affinity of | |
498 | * @cpumask: cpu mask representing cpus which can handle the interrupt | |
499 | * @force: whether to force (ignored) | |
500 | * | |
501 | * Revector the specified external irq onto a specific cpu's TR2 trigger, so | |
502 | * that that cpu tends to be the one who handles it. | |
503 | */ | |
504 | static int meta_intc_set_affinity(struct irq_data *data, | |
505 | const struct cpumask *cpumask, bool force) | |
506 | { | |
507 | irq_hw_number_t hw = data->hwirq; | |
508 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | |
509 | unsigned int cpu, thread; | |
510 | ||
511 | /* | |
512 | * Wire up this interrupt from HWVECxEXT to the Meta core. | |
513 | * | |
514 | * Note that we can't wire up HWVECxEXT to interrupt more than | |
515 | * one cpu (the interrupt code doesn't support it), so we just | |
516 | * pick the first cpu we find in 'cpumask'. | |
517 | */ | |
518 | cpu = cpumask_any(cpumask); | |
519 | thread = cpu_2_hwthread_id[cpu]; | |
520 | ||
521 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); | |
522 | ||
523 | return 0; | |
524 | } | |
525 | #else | |
526 | #define meta_intc_set_affinity NULL | |
527 | #endif | |
528 | ||
529 | #ifdef CONFIG_PM_SLEEP | |
530 | #define META_INTC_CHIP_FLAGS (IRQCHIP_MASK_ON_SUSPEND \ | |
531 | | IRQCHIP_SKIP_SET_WAKE) | |
532 | #else | |
533 | #define META_INTC_CHIP_FLAGS 0 | |
534 | #endif | |
535 | ||
536 | /* public edge/level irq chips which SoCs can override */ | |
537 | ||
538 | struct irq_chip meta_intc_edge_chip = { | |
539 | .irq_startup = meta_intc_startup_irq, | |
540 | .irq_shutdown = meta_intc_shutdown_irq, | |
541 | .irq_ack = meta_intc_ack_irq, | |
542 | .irq_mask = meta_intc_mask_irq, | |
543 | .irq_unmask = meta_intc_unmask_irq, | |
544 | .irq_set_type = meta_intc_irq_set_type, | |
545 | .irq_set_affinity = meta_intc_set_affinity, | |
546 | .flags = META_INTC_CHIP_FLAGS, | |
547 | }; | |
548 | ||
549 | struct irq_chip meta_intc_level_chip = { | |
550 | .irq_startup = meta_intc_startup_irq, | |
551 | .irq_shutdown = meta_intc_shutdown_irq, | |
552 | .irq_set_type = meta_intc_irq_set_type, | |
553 | .irq_mask = meta_intc_mask_irq, | |
554 | .irq_unmask = meta_intc_unmask_irq, | |
555 | .irq_set_affinity = meta_intc_set_affinity, | |
556 | .flags = META_INTC_CHIP_FLAGS, | |
557 | }; | |
558 | ||
559 | /** | |
560 | * meta_intc_map() - map an external irq | |
561 | * @d: irq domain of external trigger block | |
562 | * @irq: virtual irq number | |
563 | * @hw: hardware irq number within external trigger block | |
564 | * | |
565 | * This sets up a virtual irq for a specified hardware interrupt. The irq chip | |
566 | * and handler is configured, using the HWLEVELEXT registers to determine | |
567 | * edge/level flow type. These registers will have been set when the irq type is | |
568 | * set (or set to a default at init time). | |
569 | */ | |
570 | static int meta_intc_map(struct irq_domain *d, unsigned int irq, | |
571 | irq_hw_number_t hw) | |
572 | { | |
573 | unsigned int bit = 1 << meta_intc_offset(hw); | |
574 | void __iomem *level_addr = meta_intc_level_addr(hw); | |
575 | ||
576 | /* Go by the current sense in the HWLEVELEXT register */ | |
577 | if (metag_in32(level_addr) & bit) | |
578 | irq_set_chip_and_handler(irq, &meta_intc_level_chip, | |
579 | handle_level_irq); | |
580 | else | |
581 | irq_set_chip_and_handler(irq, &meta_intc_edge_chip, | |
582 | handle_edge_irq); | |
583 | return 0; | |
584 | } | |
585 | ||
586 | static const struct irq_domain_ops meta_intc_domain_ops = { | |
587 | .map = meta_intc_map, | |
588 | .xlate = irq_domain_xlate_twocell, | |
589 | }; | |
590 | ||
591 | #ifdef CONFIG_METAG_SUSPEND_MEM | |
592 | ||
593 | /** | |
594 | * struct meta_intc_context - suspend context | |
595 | * @levels: State of HWLEVELEXT registers | |
596 | * @masks: State of HWMASKEXT registers | |
597 | * @vectors: State of HWVECEXT registers | |
598 | * @txvecint: State of TxVECINT registers | |
599 | * | |
600 | * This structure stores the IRQ state across suspend. | |
601 | */ | |
602 | struct meta_intc_context { | |
603 | u32 levels[4]; | |
604 | u32 masks[4]; | |
605 | u8 vectors[4*32]; | |
606 | ||
607 | u8 txvecint[4][4]; | |
608 | }; | |
609 | ||
610 | /* suspend context */ | |
611 | static struct meta_intc_context *meta_intc_context; | |
612 | ||
613 | /** | |
614 | * meta_intc_suspend() - store irq state | |
615 | * | |
616 | * To avoid interfering with other threads we only save the IRQ state of IRQs in | |
617 | * use by Linux. | |
618 | */ | |
619 | static int meta_intc_suspend(void) | |
620 | { | |
621 | struct meta_intc_priv *priv = &meta_intc_priv; | |
622 | int i, j; | |
623 | irq_hw_number_t hw; | |
624 | unsigned int bank; | |
625 | unsigned long flags; | |
626 | struct meta_intc_context *context; | |
627 | void __iomem *level_addr, *mask_addr, *vec_addr; | |
628 | u32 mask, bit; | |
629 | ||
630 | context = kzalloc(sizeof(*context), GFP_ATOMIC); | |
631 | if (!context) | |
632 | return -ENOMEM; | |
633 | ||
634 | hw = 0; | |
635 | level_addr = meta_intc_level_addr(0); | |
636 | mask_addr = meta_intc_mask_addr(0); | |
637 | for (bank = 0; bank < priv->nr_banks; ++bank) { | |
638 | vec_addr = meta_intc_vec_addr(hw); | |
639 | ||
640 | /* create mask of interrupts in use */ | |
641 | mask = 0; | |
642 | for (bit = 1; bit; bit <<= 1) { | |
643 | i = irq_linear_revmap(priv->domain, hw); | |
644 | /* save mapped irqs which are enabled or have actions */ | |
645 | if (i && (!irqd_irq_disabled(irq_get_irq_data(i)) || | |
646 | irq_has_action(i))) { | |
647 | mask |= bit; | |
648 | ||
649 | /* save trigger vector */ | |
650 | context->vectors[hw] = metag_in32(vec_addr); | |
651 | } | |
652 | ||
653 | ++hw; | |
654 | vec_addr += HWVECnEXT_STRIDE; | |
655 | } | |
656 | ||
657 | /* save level state if any IRQ levels altered */ | |
658 | if (priv->levels_altered[bank]) | |
659 | context->levels[bank] = metag_in32(level_addr); | |
660 | /* save mask state if any IRQs in use */ | |
661 | if (mask) | |
662 | context->masks[bank] = metag_in32(mask_addr); | |
663 | ||
664 | level_addr += HWSTAT_STRIDE; | |
665 | mask_addr += HWSTAT_STRIDE; | |
666 | } | |
667 | ||
668 | /* save trigger matrixing */ | |
669 | __global_lock2(flags); | |
670 | for (i = 0; i < 4; ++i) | |
671 | for (j = 0; j < 4; ++j) | |
672 | context->txvecint[i][j] = metag_in32(T0VECINT_BHALT + | |
673 | TnVECINT_STRIDE*i + | |
674 | 8*j); | |
675 | __global_unlock2(flags); | |
676 | ||
677 | meta_intc_context = context; | |
678 | return 0; | |
679 | } | |
680 | ||
681 | /** | |
682 | * meta_intc_resume() - restore saved irq state | |
683 | * | |
684 | * Restore the saved IRQ state and drop it. | |
685 | */ | |
686 | static void meta_intc_resume(void) | |
687 | { | |
688 | struct meta_intc_priv *priv = &meta_intc_priv; | |
689 | int i, j; | |
690 | irq_hw_number_t hw; | |
691 | unsigned int bank; | |
692 | unsigned long flags; | |
693 | struct meta_intc_context *context = meta_intc_context; | |
694 | void __iomem *level_addr, *mask_addr, *vec_addr; | |
695 | u32 mask, bit, tmp; | |
696 | ||
697 | meta_intc_context = NULL; | |
698 | ||
699 | hw = 0; | |
700 | level_addr = meta_intc_level_addr(0); | |
701 | mask_addr = meta_intc_mask_addr(0); | |
702 | for (bank = 0; bank < priv->nr_banks; ++bank) { | |
703 | vec_addr = meta_intc_vec_addr(hw); | |
704 | ||
705 | /* create mask of interrupts in use */ | |
706 | mask = 0; | |
707 | for (bit = 1; bit; bit <<= 1) { | |
708 | i = irq_linear_revmap(priv->domain, hw); | |
709 | /* restore mapped irqs, enabled or with actions */ | |
710 | if (i && (!irqd_irq_disabled(irq_get_irq_data(i)) || | |
711 | irq_has_action(i))) { | |
712 | mask |= bit; | |
713 | ||
714 | /* restore trigger vector */ | |
715 | metag_out32(context->vectors[hw], vec_addr); | |
716 | } | |
717 | ||
718 | ++hw; | |
719 | vec_addr += HWVECnEXT_STRIDE; | |
720 | } | |
721 | ||
722 | if (mask) { | |
723 | /* restore mask state */ | |
724 | __global_lock2(flags); | |
725 | tmp = metag_in32(mask_addr); | |
726 | tmp = (tmp & ~mask) | (context->masks[bank] & mask); | |
727 | metag_out32(tmp, mask_addr); | |
728 | __global_unlock2(flags); | |
729 | } | |
730 | ||
731 | mask = priv->levels_altered[bank]; | |
732 | if (mask) { | |
733 | /* restore level state */ | |
734 | __global_lock2(flags); | |
735 | tmp = metag_in32(level_addr); | |
736 | tmp = (tmp & ~mask) | (context->levels[bank] & mask); | |
737 | metag_out32(tmp, level_addr); | |
738 | __global_unlock2(flags); | |
739 | } | |
740 | ||
741 | level_addr += HWSTAT_STRIDE; | |
742 | mask_addr += HWSTAT_STRIDE; | |
743 | } | |
744 | ||
745 | /* restore trigger matrixing */ | |
746 | __global_lock2(flags); | |
747 | for (i = 0; i < 4; ++i) { | |
748 | for (j = 0; j < 4; ++j) { | |
749 | metag_out32(context->txvecint[i][j], | |
750 | T0VECINT_BHALT + | |
751 | TnVECINT_STRIDE*i + | |
752 | 8*j); | |
753 | } | |
754 | } | |
755 | __global_unlock2(flags); | |
756 | ||
757 | kfree(context); | |
758 | } | |
759 | ||
760 | static struct syscore_ops meta_intc_syscore_ops = { | |
761 | .suspend = meta_intc_suspend, | |
762 | .resume = meta_intc_resume, | |
763 | }; | |
764 | ||
765 | static void __init meta_intc_init_syscore_ops(struct meta_intc_priv *priv) | |
766 | { | |
767 | register_syscore_ops(&meta_intc_syscore_ops); | |
768 | } | |
769 | #else | |
770 | #define meta_intc_init_syscore_ops(priv) do {} while (0) | |
771 | #endif | |
772 | ||
773 | /** | |
774 | * meta_intc_init_cpu() - register with a Meta cpu | |
775 | * @priv: private interrupt controller data | |
776 | * @cpu: the CPU to register on | |
777 | * | |
778 | * Configure @cpu's TR2 irq so that we can demux external irqs. | |
779 | */ | |
780 | static void __init meta_intc_init_cpu(struct meta_intc_priv *priv, int cpu) | |
781 | { | |
782 | unsigned int thread = cpu_2_hwthread_id[cpu]; | |
783 | unsigned int signum = TBID_SIGNUM_TR2(thread); | |
784 | int irq = tbisig_map(signum); | |
785 | ||
786 | /* Register the multiplexed IRQ handler */ | |
787 | irq_set_chained_handler(irq, meta_intc_irq_demux); | |
788 | irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); | |
789 | } | |
790 | ||
791 | /** | |
792 | * meta_intc_no_mask() - indicate lack of HWMASKEXT registers | |
793 | * | |
794 | * Called from SoC code (or init code below) to dynamically indicate the lack of | |
795 | * HWMASKEXT registers (for example depending on some SoC revision register). | |
796 | * This alters the irq mask and unmask callbacks to use the fallback | |
797 | * unvectoring/retriggering technique instead of using HWMASKEXT registers. | |
798 | */ | |
799 | void __init meta_intc_no_mask(void) | |
800 | { | |
801 | meta_intc_edge_chip.irq_mask = meta_intc_mask_irq_nomask; | |
802 | meta_intc_edge_chip.irq_unmask = meta_intc_unmask_edge_irq_nomask; | |
803 | meta_intc_level_chip.irq_mask = meta_intc_mask_irq_nomask; | |
804 | meta_intc_level_chip.irq_unmask = meta_intc_unmask_level_irq_nomask; | |
805 | } | |
806 | ||
807 | /** | |
808 | * init_external_IRQ() - initialise the external irq controller | |
809 | * | |
810 | * Set up the external irq controller using device tree properties. This is | |
811 | * called from init_IRQ(). | |
812 | */ | |
813 | int __init init_external_IRQ(void) | |
814 | { | |
815 | struct meta_intc_priv *priv = &meta_intc_priv; | |
816 | struct device_node *node; | |
817 | int ret, cpu; | |
818 | u32 val; | |
819 | bool no_masks = false; | |
820 | ||
821 | node = of_find_compatible_node(NULL, NULL, "img,meta-intc"); | |
822 | if (!node) | |
823 | return -ENOENT; | |
824 | ||
825 | /* Get number of banks */ | |
826 | ret = of_property_read_u32(node, "num-banks", &val); | |
827 | if (ret) { | |
828 | pr_err("meta-intc: No num-banks property found\n"); | |
829 | return ret; | |
830 | } | |
831 | if (val < 1 || val > 4) { | |
832 | pr_err("meta-intc: num-banks (%u) out of range\n", val); | |
833 | return -EINVAL; | |
834 | } | |
835 | priv->nr_banks = val; | |
836 | ||
837 | /* Are any mask registers present? */ | |
838 | if (of_get_property(node, "no-mask", NULL)) | |
839 | no_masks = true; | |
840 | ||
841 | /* No HWMASKEXT registers present? */ | |
842 | if (no_masks) | |
843 | meta_intc_no_mask(); | |
844 | ||
845 | /* Set up an IRQ domain */ | |
846 | /* | |
847 | * This is a legacy IRQ domain for now until all the platform setup code | |
848 | * has been converted to devicetree. | |
849 | */ | |
850 | priv->domain = irq_domain_add_linear(node, priv->nr_banks*32, | |
851 | &meta_intc_domain_ops, priv); | |
852 | if (unlikely(!priv->domain)) { | |
853 | pr_err("meta-intc: cannot add IRQ domain\n"); | |
854 | return -ENOMEM; | |
855 | } | |
856 | ||
857 | /* Setup TR2 for all cpus. */ | |
858 | for_each_possible_cpu(cpu) | |
859 | meta_intc_init_cpu(priv, cpu); | |
860 | ||
861 | /* Set up system suspend/resume callbacks */ | |
862 | meta_intc_init_syscore_ops(priv); | |
863 | ||
864 | pr_info("meta-intc: External IRQ controller initialised (%u IRQs)\n", | |
865 | priv->nr_banks*32); | |
866 | ||
867 | return 0; | |
868 | } |