Root/
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 | }; |
344 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9