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