diff options
author | James Hogan <james.hogan@imgtec.com> | 2012-10-09 05:54:47 -0400 |
---|---|---|
committer | James Hogan <james.hogan@imgtec.com> | 2013-03-02 15:09:48 -0500 |
commit | 5698c50d9da4ab2f57d98c64ea97675dcaf2a608 (patch) | |
tree | 5b82a24daa8bbb85703489340cbfdcb00a2f20a4 /drivers/irqchip | |
parent | 63047ea36070d11f902ab7d09a5a18aea037c0f7 (diff) |
metag: Internal and external irqchips
Meta core internal interrupts (from HWSTATMETA and friends) are vectored
onto the TR1 core trigger for the current thread. This is demultiplexed
in irq-metag.c to individual Linux IRQs for each internal interrupt.
External SoC interrupts (from HWSTATEXT and friends) are vectored onto
the TR2 core trigger for the current thread. This is demultiplexed in
irq-metag-ext.c to individual Linux IRQs for each external SoC interrupt.
The external irqchip has devicetree bindings for configuring the number
of irq banks and the type of masking available.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: Dom Cobley <popcornmix@gmail.com>
Cc: Simon Arlott <simon@fire.lp0.eu>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: devicetree-discuss@lists.ozlabs.org
Cc: linux-doc@vger.kernel.org
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Makefile | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-metag-ext.c | 868 | ||||
-rw-r--r-- | drivers/irqchip/irq-metag.c | 343 |
3 files changed, 1213 insertions, 0 deletions
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index bf4609a5bd9d..ff02f6b98863 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -1,4 +1,6 @@ | |||
1 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o | 1 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o |
2 | obj-$(CONFIG_METAG) += irq-metag-ext.o | ||
3 | obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o | ||
2 | obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o | 4 | obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o |
3 | obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o | 5 | obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o |
4 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o | 6 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o |
diff --git a/drivers/irqchip/irq-metag-ext.c b/drivers/irqchip/irq-metag-ext.c new file mode 100644 index 000000000000..92c41ab4dbfd --- /dev/null +++ b/drivers/irqchip/irq-metag-ext.c | |||
@@ -0,0 +1,868 @@ | |||
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 | } | ||
diff --git a/drivers/irqchip/irq-metag.c b/drivers/irqchip/irq-metag.c new file mode 100644 index 000000000000..8e94d7a3b20d --- /dev/null +++ b/drivers/irqchip/irq-metag.c | |||
@@ -0,0 +1,343 @@ | |||
1 | /* | ||
2 | * Meta internal (HWSTATMETA) interrupt code. | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 Imagination Technologies Ltd. | ||
5 | * | ||
6 | * This code is based on the code in SoC/common/irq.c and SoC/comet/irq.c | ||
7 | * The code base could be generalised/merged as a lot of the functionality is | ||
8 | * similar. Until this is done, we try to keep the code simple here. | ||
9 | */ | ||
10 | |||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/io.h> | ||
13 | #include <linux/irqdomain.h> | ||
14 | |||
15 | #include <asm/irq.h> | ||
16 | #include <asm/hwthread.h> | ||
17 | |||
18 | #define PERF0VECINT 0x04820580 | ||
19 | #define PERF1VECINT 0x04820588 | ||
20 | #define PERF0TRIG_OFFSET 16 | ||
21 | #define PERF1TRIG_OFFSET 17 | ||
22 | |||
23 | /** | ||
24 | * struct metag_internal_irq_priv - private meta internal interrupt data | ||
25 | * @domain: IRQ domain for all internal Meta IRQs (HWSTATMETA) | ||
26 | * @unmasked: Record of unmasked IRQs | ||
27 | */ | ||
28 | struct metag_internal_irq_priv { | ||
29 | struct irq_domain *domain; | ||
30 | |||
31 | unsigned long unmasked; | ||
32 | }; | ||
33 | |||
34 | /* Private data for the one and only internal interrupt controller */ | ||
35 | static struct metag_internal_irq_priv metag_internal_irq_priv; | ||
36 | |||
37 | static unsigned int metag_internal_irq_startup(struct irq_data *data); | ||
38 | static void metag_internal_irq_shutdown(struct irq_data *data); | ||
39 | static void metag_internal_irq_ack(struct irq_data *data); | ||
40 | static void metag_internal_irq_mask(struct irq_data *data); | ||
41 | static void metag_internal_irq_unmask(struct irq_data *data); | ||
42 | #ifdef CONFIG_SMP | ||
43 | static int metag_internal_irq_set_affinity(struct irq_data *data, | ||
44 | const struct cpumask *cpumask, bool force); | ||
45 | #endif | ||
46 | |||
47 | static struct irq_chip internal_irq_edge_chip = { | ||
48 | .name = "HWSTATMETA-IRQ", | ||
49 | .irq_startup = metag_internal_irq_startup, | ||
50 | .irq_shutdown = metag_internal_irq_shutdown, | ||
51 | .irq_ack = metag_internal_irq_ack, | ||
52 | .irq_mask = metag_internal_irq_mask, | ||
53 | .irq_unmask = metag_internal_irq_unmask, | ||
54 | #ifdef CONFIG_SMP | ||
55 | .irq_set_affinity = metag_internal_irq_set_affinity, | ||
56 | #endif | ||
57 | }; | ||
58 | |||
59 | /* | ||
60 | * metag_hwvec_addr - get the address of *VECINT regs of irq | ||
61 | * | ||
62 | * This function is a table of supported triggers on HWSTATMETA | ||
63 | * Could do with a structure, but better keep it simple. Changes | ||
64 | * in this code should be rare. | ||
65 | */ | ||
66 | static inline void __iomem *metag_hwvec_addr(irq_hw_number_t hw) | ||
67 | { | ||
68 | void __iomem *addr; | ||
69 | |||
70 | switch (hw) { | ||
71 | case PERF0TRIG_OFFSET: | ||
72 | addr = (void __iomem *)PERF0VECINT; | ||
73 | break; | ||
74 | case PERF1TRIG_OFFSET: | ||
75 | addr = (void __iomem *)PERF1VECINT; | ||
76 | break; | ||
77 | default: | ||
78 | addr = NULL; | ||
79 | break; | ||
80 | } | ||
81 | return addr; | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * metag_internal_startup - setup an internal irq | ||
86 | * @irq: the irq to startup | ||
87 | * | ||
88 | * Multiplex interrupts for @irq onto TR1. Clear any pending | ||
89 | * interrupts. | ||
90 | */ | ||
91 | static unsigned int metag_internal_irq_startup(struct irq_data *data) | ||
92 | { | ||
93 | /* Clear (toggle) the bit in HWSTATMETA for our interrupt. */ | ||
94 | metag_internal_irq_ack(data); | ||
95 | |||
96 | /* Enable the interrupt by unmasking it */ | ||
97 | metag_internal_irq_unmask(data); | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * metag_internal_irq_shutdown - turn off the irq | ||
104 | * @irq: the irq number to turn off | ||
105 | * | ||
106 | * Mask @irq and clear any pending interrupts. | ||
107 | * Stop muxing @irq onto TR1. | ||
108 | */ | ||
109 | static void metag_internal_irq_shutdown(struct irq_data *data) | ||
110 | { | ||
111 | /* Disable the IRQ at the core by masking it. */ | ||
112 | metag_internal_irq_mask(data); | ||
113 | |||
114 | /* Clear (toggle) the bit in HWSTATMETA for our interrupt. */ | ||
115 | metag_internal_irq_ack(data); | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * metag_internal_irq_ack - acknowledge irq | ||
120 | * @irq: the irq to ack | ||
121 | */ | ||
122 | static void metag_internal_irq_ack(struct irq_data *data) | ||
123 | { | ||
124 | irq_hw_number_t hw = data->hwirq; | ||
125 | unsigned int bit = 1 << hw; | ||
126 | |||
127 | if (metag_in32(HWSTATMETA) & bit) | ||
128 | metag_out32(bit, HWSTATMETA); | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * metag_internal_irq_mask() - mask an internal irq by unvectoring | ||
133 | * @data: data for the internal irq to mask | ||
134 | * | ||
135 | * HWSTATMETA has no mask register. Instead the IRQ is unvectored from the core | ||
136 | * and retriggered if necessary later. | ||
137 | */ | ||
138 | static void metag_internal_irq_mask(struct irq_data *data) | ||
139 | { | ||
140 | struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; | ||
141 | irq_hw_number_t hw = data->hwirq; | ||
142 | void __iomem *vec_addr = metag_hwvec_addr(hw); | ||
143 | |||
144 | clear_bit(hw, &priv->unmasked); | ||
145 | |||
146 | /* there is no interrupt mask, so unvector the interrupt */ | ||
147 | metag_out32(0, vec_addr); | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * meta_intc_unmask_edge_irq_nomask() - unmask an edge irq by revectoring | ||
152 | * @data: data for the internal irq to unmask | ||
153 | * | ||
154 | * HWSTATMETA has no mask register. Instead the IRQ is revectored back to the | ||
155 | * core and retriggered if necessary. | ||
156 | */ | ||
157 | static void metag_internal_irq_unmask(struct irq_data *data) | ||
158 | { | ||
159 | struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; | ||
160 | irq_hw_number_t hw = data->hwirq; | ||
161 | unsigned int bit = 1 << hw; | ||
162 | void __iomem *vec_addr = metag_hwvec_addr(hw); | ||
163 | unsigned int thread = hard_processor_id(); | ||
164 | |||
165 | set_bit(hw, &priv->unmasked); | ||
166 | |||
167 | /* there is no interrupt mask, so revector the interrupt */ | ||
168 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), vec_addr); | ||
169 | |||
170 | /* | ||
171 | * Re-trigger interrupt | ||
172 | * | ||
173 | * Writing a 1 toggles, and a 0->1 transition triggers. We only | ||
174 | * retrigger if the status bit is already set, which means we | ||
175 | * need to clear it first. Retriggering is fundamentally racy | ||
176 | * because if the interrupt fires again after we clear it we | ||
177 | * could end up clearing it again and the interrupt handler | ||
178 | * thinking it hasn't fired. Therefore we need to keep trying to | ||
179 | * retrigger until the bit is set. | ||
180 | */ | ||
181 | if (metag_in32(HWSTATMETA) & bit) { | ||
182 | metag_out32(bit, HWSTATMETA); | ||
183 | while (!(metag_in32(HWSTATMETA) & bit)) | ||
184 | metag_out32(bit, HWSTATMETA); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | #ifdef CONFIG_SMP | ||
189 | /* | ||
190 | * metag_internal_irq_set_affinity - set the affinity for an interrupt | ||
191 | */ | ||
192 | static int metag_internal_irq_set_affinity(struct irq_data *data, | ||
193 | const struct cpumask *cpumask, bool force) | ||
194 | { | ||
195 | unsigned int cpu, thread; | ||
196 | irq_hw_number_t hw = data->hwirq; | ||
197 | /* | ||
198 | * Wire up this interrupt from *VECINT to the Meta core. | ||
199 | * | ||
200 | * Note that we can't wire up *VECINT to interrupt more than | ||
201 | * one cpu (the interrupt code doesn't support it), so we just | ||
202 | * pick the first cpu we find in 'cpumask'. | ||
203 | */ | ||
204 | cpu = cpumask_any(cpumask); | ||
205 | thread = cpu_2_hwthread_id[cpu]; | ||
206 | |||
207 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), | ||
208 | metag_hwvec_addr(hw)); | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | #endif | ||
213 | |||
214 | /* | ||
215 | * metag_internal_irq_demux - irq de-multiplexer | ||
216 | * @irq: the interrupt number | ||
217 | * @desc: the interrupt description structure for this irq | ||
218 | * | ||
219 | * The cpu receives an interrupt on TR1 when an interrupt has | ||
220 | * occurred. It is this function's job to demux this irq and | ||
221 | * figure out exactly which trigger needs servicing. | ||
222 | */ | ||
223 | static void metag_internal_irq_demux(unsigned int irq, struct irq_desc *desc) | ||
224 | { | ||
225 | struct metag_internal_irq_priv *priv = irq_desc_get_handler_data(desc); | ||
226 | irq_hw_number_t hw; | ||
227 | unsigned int irq_no; | ||
228 | u32 status; | ||
229 | |||
230 | recalculate: | ||
231 | status = metag_in32(HWSTATMETA) & priv->unmasked; | ||
232 | |||
233 | for (hw = 0; status != 0; status >>= 1, ++hw) { | ||
234 | if (status & 0x1) { | ||
235 | /* | ||
236 | * Map the hardware IRQ number to a virtual Linux IRQ | ||
237 | * number. | ||
238 | */ | ||
239 | irq_no = irq_linear_revmap(priv->domain, hw); | ||
240 | |||
241 | /* | ||
242 | * Only fire off interrupts that are | ||
243 | * registered to be handled by the kernel. | ||
244 | * Other interrupts are probably being | ||
245 | * handled by other Meta hardware threads. | ||
246 | */ | ||
247 | generic_handle_irq(irq_no); | ||
248 | |||
249 | /* | ||
250 | * The handler may have re-enabled interrupts | ||
251 | * which could have caused a nested invocation | ||
252 | * of this code and make the copy of the | ||
253 | * status register we are using invalid. | ||
254 | */ | ||
255 | goto recalculate; | ||
256 | } | ||
257 | } | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * internal_irq_map() - Map an internal meta IRQ to a virtual IRQ number. | ||
262 | * @hw: Number of the internal IRQ. Must be in range. | ||
263 | * | ||
264 | * Returns: The virtual IRQ number of the Meta internal IRQ specified by | ||
265 | * @hw. | ||
266 | */ | ||
267 | int internal_irq_map(unsigned int hw) | ||
268 | { | ||
269 | struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; | ||
270 | if (!priv->domain) | ||
271 | return -ENODEV; | ||
272 | return irq_create_mapping(priv->domain, hw); | ||
273 | } | ||
274 | |||
275 | /** | ||
276 | * metag_internal_irq_init_cpu - regsister with the Meta cpu | ||
277 | * @cpu: the CPU to register on | ||
278 | * | ||
279 | * Configure @cpu's TR1 irq so that we can demux irqs. | ||
280 | */ | ||
281 | static void metag_internal_irq_init_cpu(struct metag_internal_irq_priv *priv, | ||
282 | int cpu) | ||
283 | { | ||
284 | unsigned int thread = cpu_2_hwthread_id[cpu]; | ||
285 | unsigned int signum = TBID_SIGNUM_TR1(thread); | ||
286 | int irq = tbisig_map(signum); | ||
287 | |||
288 | /* Register the multiplexed IRQ handler */ | ||
289 | irq_set_handler_data(irq, priv); | ||
290 | irq_set_chained_handler(irq, metag_internal_irq_demux); | ||
291 | irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); | ||
292 | } | ||
293 | |||
294 | /** | ||
295 | * metag_internal_intc_map() - map an internal irq | ||
296 | * @d: irq domain of internal trigger block | ||
297 | * @irq: virtual irq number | ||
298 | * @hw: hardware irq number within internal trigger block | ||
299 | * | ||
300 | * This sets up a virtual irq for a specified hardware interrupt. The irq chip | ||
301 | * and handler is configured. | ||
302 | */ | ||
303 | static int metag_internal_intc_map(struct irq_domain *d, unsigned int irq, | ||
304 | irq_hw_number_t hw) | ||
305 | { | ||
306 | /* only register interrupt if it is mapped */ | ||
307 | if (!metag_hwvec_addr(hw)) | ||
308 | return -EINVAL; | ||
309 | |||
310 | irq_set_chip_and_handler(irq, &internal_irq_edge_chip, | ||
311 | handle_edge_irq); | ||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | static const struct irq_domain_ops metag_internal_intc_domain_ops = { | ||
316 | .map = metag_internal_intc_map, | ||
317 | }; | ||
318 | |||
319 | /** | ||
320 | * metag_internal_irq_register - register internal IRQs | ||
321 | * | ||
322 | * Register the irq chip and handler function for all internal IRQs | ||
323 | */ | ||
324 | int __init init_internal_IRQ(void) | ||
325 | { | ||
326 | struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; | ||
327 | unsigned int cpu; | ||
328 | |||
329 | /* Set up an IRQ domain */ | ||
330 | priv->domain = irq_domain_add_linear(NULL, 32, | ||
331 | &metag_internal_intc_domain_ops, | ||
332 | priv); | ||
333 | if (unlikely(!priv->domain)) { | ||
334 | pr_err("meta-internal-intc: cannot add IRQ domain\n"); | ||
335 | return -ENOMEM; | ||
336 | } | ||
337 | |||
338 | /* Setup TR1 for all cpus. */ | ||
339 | for_each_possible_cpu(cpu) | ||
340 | metag_internal_irq_init_cpu(priv, cpu); | ||
341 | |||
342 | return 0; | ||
343 | }; | ||