]>
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 | |
5698c50d JH |
407 | irq_hw_number_t hw = data->hwirq; |
408 | unsigned int bit = 1 << meta_intc_offset(hw); | |
409 | void __iomem *level_addr = meta_intc_level_addr(hw); | |
410 | unsigned long flags; | |
411 | unsigned int level; | |
412 | ||
413 | /* update the chip/handler */ | |
414 | if (flow_type & IRQ_TYPE_LEVEL_MASK) | |
0a2b6497 TG |
415 | irq_set_chip_handler_name_locked(data, &meta_intc_level_chip, |
416 | handle_level_irq, NULL); | |
5698c50d | 417 | else |
0a2b6497 TG |
418 | irq_set_chip_handler_name_locked(data, &meta_intc_edge_chip, |
419 | handle_edge_irq, NULL); | |
5698c50d JH |
420 | |
421 | /* and clear/set the bit in HWLEVELEXT */ | |
422 | __global_lock2(flags); | |
423 | level = metag_in32(level_addr); | |
424 | if (flow_type & IRQ_TYPE_LEVEL_MASK) | |
425 | level |= bit; | |
426 | else | |
427 | level &= ~bit; | |
428 | metag_out32(level, level_addr); | |
429 | #ifdef CONFIG_METAG_SUSPEND_MEM | |
430 | priv->levels_altered[meta_intc_bank(hw)] |= bit; | |
431 | #endif | |
432 | __global_unlock2(flags); | |
433 | ||
434 | return 0; | |
435 | } | |
436 | ||
437 | /** | |
438 | * meta_intc_irq_demux() - external irq de-multiplexer | |
5698c50d JH |
439 | * @desc: the interrupt description structure for this irq |
440 | * | |
441 | * The cpu receives an interrupt on TR2 when a SoC interrupt has occurred. It is | |
442 | * this function's job to demux this irq and figure out exactly which external | |
443 | * irq needs servicing. | |
444 | * | |
445 | * Whilst using TR2 to detect external interrupts is a software convention it is | |
446 | * (hopefully) unlikely to change. | |
447 | */ | |
bd0b9ac4 | 448 | static void meta_intc_irq_demux(struct irq_desc *desc) |
5698c50d JH |
449 | { |
450 | struct meta_intc_priv *priv = &meta_intc_priv; | |
451 | irq_hw_number_t hw; | |
452 | unsigned int bank, irq_no, status; | |
453 | void __iomem *stat_addr = meta_intc_stat_addr(0); | |
454 | ||
455 | /* | |
456 | * Locate which interrupt has caused our handler to run. | |
457 | */ | |
458 | for (bank = 0; bank < priv->nr_banks; ++bank) { | |
459 | /* Which interrupts are currently pending in this bank? */ | |
460 | recalculate: | |
461 | status = metag_in32(stat_addr) & priv->unmasked[bank]; | |
462 | ||
463 | for (hw = bank*32; status; status >>= 1, ++hw) { | |
464 | if (status & 0x1) { | |
465 | /* | |
466 | * Map the hardware IRQ number to a virtual | |
467 | * Linux IRQ number. | |
468 | */ | |
469 | irq_no = irq_linear_revmap(priv->domain, hw); | |
470 | ||
471 | /* | |
472 | * Only fire off external interrupts that are | |
473 | * registered to be handled by the kernel. | |
474 | * Other external interrupts are probably being | |
475 | * handled by other Meta hardware threads. | |
476 | */ | |
477 | generic_handle_irq(irq_no); | |
478 | ||
479 | /* | |
480 | * The handler may have re-enabled interrupts | |
481 | * which could have caused a nested invocation | |
482 | * of this code and make the copy of the | |
483 | * status register we are using invalid. | |
484 | */ | |
485 | goto recalculate; | |
486 | } | |
487 | } | |
488 | stat_addr += HWSTAT_STRIDE; | |
489 | } | |
490 | } | |
491 | ||
492 | #ifdef CONFIG_SMP | |
493 | /** | |
494 | * meta_intc_set_affinity() - set the affinity for an interrupt | |
495 | * @data: data for the external irq to set the affinity of | |
496 | * @cpumask: cpu mask representing cpus which can handle the interrupt | |
497 | * @force: whether to force (ignored) | |
498 | * | |
499 | * Revector the specified external irq onto a specific cpu's TR2 trigger, so | |
500 | * that that cpu tends to be the one who handles it. | |
501 | */ | |
502 | static int meta_intc_set_affinity(struct irq_data *data, | |
503 | const struct cpumask *cpumask, bool force) | |
504 | { | |
505 | irq_hw_number_t hw = data->hwirq; | |
506 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | |
507 | unsigned int cpu, thread; | |
508 | ||
509 | /* | |
510 | * Wire up this interrupt from HWVECxEXT to the Meta core. | |
511 | * | |
512 | * Note that we can't wire up HWVECxEXT to interrupt more than | |
513 | * one cpu (the interrupt code doesn't support it), so we just | |
514 | * pick the first cpu we find in 'cpumask'. | |
515 | */ | |
f229006e | 516 | cpu = cpumask_any_and(cpumask, cpu_online_mask); |
5698c50d JH |
517 | thread = cpu_2_hwthread_id[cpu]; |
518 | ||
519 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); | |
520 | ||
521 | return 0; | |
522 | } | |
523 | #else | |
524 | #define meta_intc_set_affinity NULL | |
525 | #endif | |
526 | ||
527 | #ifdef CONFIG_PM_SLEEP | |
528 | #define META_INTC_CHIP_FLAGS (IRQCHIP_MASK_ON_SUSPEND \ | |
529 | | IRQCHIP_SKIP_SET_WAKE) | |
530 | #else | |
531 | #define META_INTC_CHIP_FLAGS 0 | |
532 | #endif | |
533 | ||
534 | /* public edge/level irq chips which SoCs can override */ | |
535 | ||
536 | struct irq_chip meta_intc_edge_chip = { | |
537 | .irq_startup = meta_intc_startup_irq, | |
538 | .irq_shutdown = meta_intc_shutdown_irq, | |
539 | .irq_ack = meta_intc_ack_irq, | |
540 | .irq_mask = meta_intc_mask_irq, | |
541 | .irq_unmask = meta_intc_unmask_irq, | |
542 | .irq_set_type = meta_intc_irq_set_type, | |
543 | .irq_set_affinity = meta_intc_set_affinity, | |
544 | .flags = META_INTC_CHIP_FLAGS, | |
545 | }; | |
546 | ||
547 | struct irq_chip meta_intc_level_chip = { | |
548 | .irq_startup = meta_intc_startup_irq, | |
549 | .irq_shutdown = meta_intc_shutdown_irq, | |
550 | .irq_set_type = meta_intc_irq_set_type, | |
551 | .irq_mask = meta_intc_mask_irq, | |
552 | .irq_unmask = meta_intc_unmask_irq, | |
553 | .irq_set_affinity = meta_intc_set_affinity, | |
554 | .flags = META_INTC_CHIP_FLAGS, | |
555 | }; | |
556 | ||
557 | /** | |
558 | * meta_intc_map() - map an external irq | |
559 | * @d: irq domain of external trigger block | |
560 | * @irq: virtual irq number | |
561 | * @hw: hardware irq number within external trigger block | |
562 | * | |
563 | * This sets up a virtual irq for a specified hardware interrupt. The irq chip | |
564 | * and handler is configured, using the HWLEVELEXT registers to determine | |
565 | * edge/level flow type. These registers will have been set when the irq type is | |
566 | * set (or set to a default at init time). | |
567 | */ | |
568 | static int meta_intc_map(struct irq_domain *d, unsigned int irq, | |
569 | irq_hw_number_t hw) | |
570 | { | |
571 | unsigned int bit = 1 << meta_intc_offset(hw); | |
572 | void __iomem *level_addr = meta_intc_level_addr(hw); | |
573 | ||
574 | /* Go by the current sense in the HWLEVELEXT register */ | |
575 | if (metag_in32(level_addr) & bit) | |
576 | irq_set_chip_and_handler(irq, &meta_intc_level_chip, | |
577 | handle_level_irq); | |
578 | else | |
579 | irq_set_chip_and_handler(irq, &meta_intc_edge_chip, | |
580 | handle_edge_irq); | |
581 | return 0; | |
582 | } | |
583 | ||
584 | static const struct irq_domain_ops meta_intc_domain_ops = { | |
585 | .map = meta_intc_map, | |
586 | .xlate = irq_domain_xlate_twocell, | |
587 | }; | |
588 | ||
589 | #ifdef CONFIG_METAG_SUSPEND_MEM | |
590 | ||
591 | /** | |
592 | * struct meta_intc_context - suspend context | |
593 | * @levels: State of HWLEVELEXT registers | |
594 | * @masks: State of HWMASKEXT registers | |
595 | * @vectors: State of HWVECEXT registers | |
596 | * @txvecint: State of TxVECINT registers | |
597 | * | |
598 | * This structure stores the IRQ state across suspend. | |
599 | */ | |
600 | struct meta_intc_context { | |
601 | u32 levels[4]; | |
602 | u32 masks[4]; | |
603 | u8 vectors[4*32]; | |
604 | ||
605 | u8 txvecint[4][4]; | |
606 | }; | |
607 | ||
608 | /* suspend context */ | |
609 | static struct meta_intc_context *meta_intc_context; | |
610 | ||
611 | /** | |
612 | * meta_intc_suspend() - store irq state | |
613 | * | |
614 | * To avoid interfering with other threads we only save the IRQ state of IRQs in | |
615 | * use by Linux. | |
616 | */ | |
617 | static int meta_intc_suspend(void) | |
618 | { | |
619 | struct meta_intc_priv *priv = &meta_intc_priv; | |
620 | int i, j; | |
621 | irq_hw_number_t hw; | |
622 | unsigned int bank; | |
623 | unsigned long flags; | |
624 | struct meta_intc_context *context; | |
625 | void __iomem *level_addr, *mask_addr, *vec_addr; | |
626 | u32 mask, bit; | |
627 | ||
628 | context = kzalloc(sizeof(*context), GFP_ATOMIC); | |
629 | if (!context) | |
630 | return -ENOMEM; | |
631 | ||
632 | hw = 0; | |
633 | level_addr = meta_intc_level_addr(0); | |
634 | mask_addr = meta_intc_mask_addr(0); | |
635 | for (bank = 0; bank < priv->nr_banks; ++bank) { | |
636 | vec_addr = meta_intc_vec_addr(hw); | |
637 | ||
638 | /* create mask of interrupts in use */ | |
639 | mask = 0; | |
640 | for (bit = 1; bit; bit <<= 1) { | |
641 | i = irq_linear_revmap(priv->domain, hw); | |
642 | /* save mapped irqs which are enabled or have actions */ | |
643 | if (i && (!irqd_irq_disabled(irq_get_irq_data(i)) || | |
644 | irq_has_action(i))) { | |
645 | mask |= bit; | |
646 | ||
647 | /* save trigger vector */ | |
648 | context->vectors[hw] = metag_in32(vec_addr); | |
649 | } | |
650 | ||
651 | ++hw; | |
652 | vec_addr += HWVECnEXT_STRIDE; | |
653 | } | |
654 | ||
655 | /* save level state if any IRQ levels altered */ | |
656 | if (priv->levels_altered[bank]) | |
657 | context->levels[bank] = metag_in32(level_addr); | |
658 | /* save mask state if any IRQs in use */ | |
659 | if (mask) | |
660 | context->masks[bank] = metag_in32(mask_addr); | |
661 | ||
662 | level_addr += HWSTAT_STRIDE; | |
663 | mask_addr += HWSTAT_STRIDE; | |
664 | } | |
665 | ||
666 | /* save trigger matrixing */ | |
667 | __global_lock2(flags); | |
668 | for (i = 0; i < 4; ++i) | |
669 | for (j = 0; j < 4; ++j) | |
670 | context->txvecint[i][j] = metag_in32(T0VECINT_BHALT + | |
671 | TnVECINT_STRIDE*i + | |
672 | 8*j); | |
673 | __global_unlock2(flags); | |
674 | ||
675 | meta_intc_context = context; | |
676 | return 0; | |
677 | } | |
678 | ||
679 | /** | |
680 | * meta_intc_resume() - restore saved irq state | |
681 | * | |
682 | * Restore the saved IRQ state and drop it. | |
683 | */ | |
684 | static void meta_intc_resume(void) | |
685 | { | |
686 | struct meta_intc_priv *priv = &meta_intc_priv; | |
687 | int i, j; | |
688 | irq_hw_number_t hw; | |
689 | unsigned int bank; | |
690 | unsigned long flags; | |
691 | struct meta_intc_context *context = meta_intc_context; | |
692 | void __iomem *level_addr, *mask_addr, *vec_addr; | |
693 | u32 mask, bit, tmp; | |
694 | ||
695 | meta_intc_context = NULL; | |
696 | ||
697 | hw = 0; | |
698 | level_addr = meta_intc_level_addr(0); | |
699 | mask_addr = meta_intc_mask_addr(0); | |
700 | for (bank = 0; bank < priv->nr_banks; ++bank) { | |
701 | vec_addr = meta_intc_vec_addr(hw); | |
702 | ||
703 | /* create mask of interrupts in use */ | |
704 | mask = 0; | |
705 | for (bit = 1; bit; bit <<= 1) { | |
706 | i = irq_linear_revmap(priv->domain, hw); | |
707 | /* restore mapped irqs, enabled or with actions */ | |
708 | if (i && (!irqd_irq_disabled(irq_get_irq_data(i)) || | |
709 | irq_has_action(i))) { | |
710 | mask |= bit; | |
711 | ||
712 | /* restore trigger vector */ | |
713 | metag_out32(context->vectors[hw], vec_addr); | |
714 | } | |
715 | ||
716 | ++hw; | |
717 | vec_addr += HWVECnEXT_STRIDE; | |
718 | } | |
719 | ||
720 | if (mask) { | |
721 | /* restore mask state */ | |
722 | __global_lock2(flags); | |
723 | tmp = metag_in32(mask_addr); | |
724 | tmp = (tmp & ~mask) | (context->masks[bank] & mask); | |
725 | metag_out32(tmp, mask_addr); | |
726 | __global_unlock2(flags); | |
727 | } | |
728 | ||
729 | mask = priv->levels_altered[bank]; | |
730 | if (mask) { | |
731 | /* restore level state */ | |
732 | __global_lock2(flags); | |
733 | tmp = metag_in32(level_addr); | |
734 | tmp = (tmp & ~mask) | (context->levels[bank] & mask); | |
735 | metag_out32(tmp, level_addr); | |
736 | __global_unlock2(flags); | |
737 | } | |
738 | ||
739 | level_addr += HWSTAT_STRIDE; | |
740 | mask_addr += HWSTAT_STRIDE; | |
741 | } | |
742 | ||
743 | /* restore trigger matrixing */ | |
744 | __global_lock2(flags); | |
745 | for (i = 0; i < 4; ++i) { | |
746 | for (j = 0; j < 4; ++j) { | |
747 | metag_out32(context->txvecint[i][j], | |
748 | T0VECINT_BHALT + | |
749 | TnVECINT_STRIDE*i + | |
750 | 8*j); | |
751 | } | |
752 | } | |
753 | __global_unlock2(flags); | |
754 | ||
755 | kfree(context); | |
756 | } | |
757 | ||
758 | static struct syscore_ops meta_intc_syscore_ops = { | |
759 | .suspend = meta_intc_suspend, | |
760 | .resume = meta_intc_resume, | |
761 | }; | |
762 | ||
763 | static void __init meta_intc_init_syscore_ops(struct meta_intc_priv *priv) | |
764 | { | |
765 | register_syscore_ops(&meta_intc_syscore_ops); | |
766 | } | |
767 | #else | |
768 | #define meta_intc_init_syscore_ops(priv) do {} while (0) | |
769 | #endif | |
770 | ||
771 | /** | |
772 | * meta_intc_init_cpu() - register with a Meta cpu | |
773 | * @priv: private interrupt controller data | |
774 | * @cpu: the CPU to register on | |
775 | * | |
776 | * Configure @cpu's TR2 irq so that we can demux external irqs. | |
777 | */ | |
778 | static void __init meta_intc_init_cpu(struct meta_intc_priv *priv, int cpu) | |
779 | { | |
780 | unsigned int thread = cpu_2_hwthread_id[cpu]; | |
781 | unsigned int signum = TBID_SIGNUM_TR2(thread); | |
782 | int irq = tbisig_map(signum); | |
783 | ||
784 | /* Register the multiplexed IRQ handler */ | |
785 | irq_set_chained_handler(irq, meta_intc_irq_demux); | |
786 | irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); | |
787 | } | |
788 | ||
789 | /** | |
790 | * meta_intc_no_mask() - indicate lack of HWMASKEXT registers | |
791 | * | |
792 | * Called from SoC code (or init code below) to dynamically indicate the lack of | |
793 | * HWMASKEXT registers (for example depending on some SoC revision register). | |
794 | * This alters the irq mask and unmask callbacks to use the fallback | |
795 | * unvectoring/retriggering technique instead of using HWMASKEXT registers. | |
796 | */ | |
797 | void __init meta_intc_no_mask(void) | |
798 | { | |
799 | meta_intc_edge_chip.irq_mask = meta_intc_mask_irq_nomask; | |
800 | meta_intc_edge_chip.irq_unmask = meta_intc_unmask_edge_irq_nomask; | |
801 | meta_intc_level_chip.irq_mask = meta_intc_mask_irq_nomask; | |
802 | meta_intc_level_chip.irq_unmask = meta_intc_unmask_level_irq_nomask; | |
803 | } | |
804 | ||
805 | /** | |
806 | * init_external_IRQ() - initialise the external irq controller | |
807 | * | |
808 | * Set up the external irq controller using device tree properties. This is | |
809 | * called from init_IRQ(). | |
810 | */ | |
811 | int __init init_external_IRQ(void) | |
812 | { | |
813 | struct meta_intc_priv *priv = &meta_intc_priv; | |
814 | struct device_node *node; | |
815 | int ret, cpu; | |
816 | u32 val; | |
817 | bool no_masks = false; | |
818 | ||
819 | node = of_find_compatible_node(NULL, NULL, "img,meta-intc"); | |
820 | if (!node) | |
821 | return -ENOENT; | |
822 | ||
823 | /* Get number of banks */ | |
824 | ret = of_property_read_u32(node, "num-banks", &val); | |
825 | if (ret) { | |
826 | pr_err("meta-intc: No num-banks property found\n"); | |
827 | return ret; | |
828 | } | |
829 | if (val < 1 || val > 4) { | |
830 | pr_err("meta-intc: num-banks (%u) out of range\n", val); | |
831 | return -EINVAL; | |
832 | } | |
833 | priv->nr_banks = val; | |
834 | ||
835 | /* Are any mask registers present? */ | |
836 | if (of_get_property(node, "no-mask", NULL)) | |
837 | no_masks = true; | |
838 | ||
839 | /* No HWMASKEXT registers present? */ | |
840 | if (no_masks) | |
841 | meta_intc_no_mask(); | |
842 | ||
843 | /* Set up an IRQ domain */ | |
844 | /* | |
845 | * This is a legacy IRQ domain for now until all the platform setup code | |
846 | * has been converted to devicetree. | |
847 | */ | |
848 | priv->domain = irq_domain_add_linear(node, priv->nr_banks*32, | |
849 | &meta_intc_domain_ops, priv); | |
850 | if (unlikely(!priv->domain)) { | |
851 | pr_err("meta-intc: cannot add IRQ domain\n"); | |
852 | return -ENOMEM; | |
853 | } | |
854 | ||
855 | /* Setup TR2 for all cpus. */ | |
856 | for_each_possible_cpu(cpu) | |
857 | meta_intc_init_cpu(priv, cpu); | |
858 | ||
859 | /* Set up system suspend/resume callbacks */ | |
860 | meta_intc_init_syscore_ops(priv); | |
861 | ||
862 | pr_info("meta-intc: External IRQ controller initialised (%u IRQs)\n", | |
863 | priv->nr_banks*32); | |
864 | ||
865 | return 0; | |
866 | } |