diff options
Diffstat (limited to 'arch/blackfin/mach-common/ints-priority-dc.c')
-rw-r--r-- | arch/blackfin/mach-common/ints-priority-dc.c | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/arch/blackfin/mach-common/ints-priority-dc.c b/arch/blackfin/mach-common/ints-priority-dc.c new file mode 100644 index 000000000000..f3cf07036c2a --- /dev/null +++ b/arch/blackfin/mach-common/ints-priority-dc.c | |||
@@ -0,0 +1,476 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/mach-common/ints-priority-dc.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: ? | ||
7 | * Description: Set up the interupt priorities | ||
8 | * | ||
9 | * Modified: | ||
10 | * 1996 Roman Zippel | ||
11 | * 1999 D. Jeff Dionne <jeff@uclinux.org> | ||
12 | * 2000-2001 Lineo, Inc. D. Jefff Dionne <jeff@lineo.ca> | ||
13 | * 2002 Arcturus Networks Inc. MaTed <mated@sympatico.ca> | ||
14 | * 2003 Metrowerks/Motorola | ||
15 | * 2003 Bas Vermeulen <bas@buyways.nl> | ||
16 | * Copyright 2004-2006 Analog Devices Inc. | ||
17 | * | ||
18 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
19 | * | ||
20 | * This program is free software; you can redistribute it and/or modify | ||
21 | * it under the terms of the GNU General Public License as published by | ||
22 | * the Free Software Foundation; either version 2 of the License, or | ||
23 | * (at your option) any later version. | ||
24 | * | ||
25 | * This program is distributed in the hope that it will be useful, | ||
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
28 | * GNU General Public License for more details. | ||
29 | * | ||
30 | * You should have received a copy of the GNU General Public License | ||
31 | * along with this program; if not, see the file COPYING, or write | ||
32 | * to the Free Software Foundation, Inc., | ||
33 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
34 | */ | ||
35 | |||
36 | #include <linux/module.h> | ||
37 | #include <linux/kernel_stat.h> | ||
38 | #include <linux/seq_file.h> | ||
39 | #include <linux/irq.h> | ||
40 | #ifdef CONFIG_KGDB | ||
41 | #include <linux/kgdb.h> | ||
42 | #endif | ||
43 | #include <asm/traps.h> | ||
44 | #include <asm/blackfin.h> | ||
45 | #include <asm/gpio.h> | ||
46 | #include <asm/irq_handler.h> | ||
47 | |||
48 | /* | ||
49 | * NOTES: | ||
50 | * - we have separated the physical Hardware interrupt from the | ||
51 | * levels that the LINUX kernel sees (see the description in irq.h) | ||
52 | * - | ||
53 | */ | ||
54 | |||
55 | unsigned long irq_flags = 0; | ||
56 | |||
57 | /* The number of spurious interrupts */ | ||
58 | atomic_t num_spurious; | ||
59 | |||
60 | struct ivgx { | ||
61 | /* irq number for request_irq, available in mach-bf561/irq.h */ | ||
62 | int irqno; | ||
63 | /* corresponding bit in the SICA_ISR0 register */ | ||
64 | int isrflag0; | ||
65 | /* corresponding bit in the SICA_ISR1 register */ | ||
66 | int isrflag1; | ||
67 | } ivg_table[NR_PERI_INTS]; | ||
68 | |||
69 | struct ivg_slice { | ||
70 | /* position of first irq in ivg_table for given ivg */ | ||
71 | struct ivgx *ifirst; | ||
72 | struct ivgx *istop; | ||
73 | } ivg7_13[IVG13 - IVG7 + 1]; | ||
74 | |||
75 | static void search_IAR(void); | ||
76 | |||
77 | /* | ||
78 | * Search SIC_IAR and fill tables with the irqvalues | ||
79 | * and their positions in the SIC_ISR register. | ||
80 | */ | ||
81 | static void __init search_IAR(void) | ||
82 | { | ||
83 | unsigned ivg, irq_pos = 0; | ||
84 | for (ivg = 0; ivg <= IVG13 - IVG7; ivg++) { | ||
85 | int irqn; | ||
86 | |||
87 | ivg7_13[ivg].istop = ivg7_13[ivg].ifirst = &ivg_table[irq_pos]; | ||
88 | |||
89 | for (irqn = 0; irqn < NR_PERI_INTS; irqn++) { | ||
90 | int iar_shift = (irqn & 7) * 4; | ||
91 | if (ivg == | ||
92 | (0xf & | ||
93 | bfin_read32((unsigned long *)SICA_IAR0 + | ||
94 | (irqn >> 3)) >> iar_shift)) { | ||
95 | ivg_table[irq_pos].irqno = IVG7 + irqn; | ||
96 | ivg_table[irq_pos].isrflag0 = | ||
97 | (irqn < 32 ? (1 << irqn) : 0); | ||
98 | ivg_table[irq_pos].isrflag1 = | ||
99 | (irqn < 32 ? 0 : (1 << (irqn - 32))); | ||
100 | ivg7_13[ivg].istop++; | ||
101 | irq_pos++; | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * This is for BF561 internal IRQs | ||
109 | */ | ||
110 | |||
111 | static void ack_noop(unsigned int irq) | ||
112 | { | ||
113 | /* Dummy function. */ | ||
114 | } | ||
115 | |||
116 | static void bf561_core_mask_irq(unsigned int irq) | ||
117 | { | ||
118 | irq_flags &= ~(1 << irq); | ||
119 | if (!irqs_disabled()) | ||
120 | local_irq_enable(); | ||
121 | } | ||
122 | |||
123 | static void bf561_core_unmask_irq(unsigned int irq) | ||
124 | { | ||
125 | irq_flags |= 1 << irq; | ||
126 | /* | ||
127 | * If interrupts are enabled, IMASK must contain the same value | ||
128 | * as irq_flags. Make sure that invariant holds. If interrupts | ||
129 | * are currently disabled we need not do anything; one of the | ||
130 | * callers will take care of setting IMASK to the proper value | ||
131 | * when reenabling interrupts. | ||
132 | * local_irq_enable just does "STI irq_flags", so it's exactly | ||
133 | * what we need. | ||
134 | */ | ||
135 | if (!irqs_disabled()) | ||
136 | local_irq_enable(); | ||
137 | return; | ||
138 | } | ||
139 | |||
140 | static void bf561_internal_mask_irq(unsigned int irq) | ||
141 | { | ||
142 | unsigned long irq_mask; | ||
143 | if ((irq - (IRQ_CORETMR + 1)) < 32) { | ||
144 | irq_mask = (1 << (irq - (IRQ_CORETMR + 1))); | ||
145 | bfin_write_SICA_IMASK0(bfin_read_SICA_IMASK0() & ~irq_mask); | ||
146 | } else { | ||
147 | irq_mask = (1 << (irq - (IRQ_CORETMR + 1) - 32)); | ||
148 | bfin_write_SICA_IMASK1(bfin_read_SICA_IMASK1() & ~irq_mask); | ||
149 | } | ||
150 | } | ||
151 | |||
152 | static void bf561_internal_unmask_irq(unsigned int irq) | ||
153 | { | ||
154 | unsigned long irq_mask; | ||
155 | |||
156 | if ((irq - (IRQ_CORETMR + 1)) < 32) { | ||
157 | irq_mask = (1 << (irq - (IRQ_CORETMR + 1))); | ||
158 | bfin_write_SICA_IMASK0(bfin_read_SICA_IMASK0() | irq_mask); | ||
159 | } else { | ||
160 | irq_mask = (1 << (irq - (IRQ_CORETMR + 1) - 32)); | ||
161 | bfin_write_SICA_IMASK1(bfin_read_SICA_IMASK1() | irq_mask); | ||
162 | } | ||
163 | SSYNC(); | ||
164 | } | ||
165 | |||
166 | static struct irq_chip bf561_core_irqchip = { | ||
167 | .ack = ack_noop, | ||
168 | .mask = bf561_core_mask_irq, | ||
169 | .unmask = bf561_core_unmask_irq, | ||
170 | }; | ||
171 | |||
172 | static struct irq_chip bf561_internal_irqchip = { | ||
173 | .ack = ack_noop, | ||
174 | .mask = bf561_internal_mask_irq, | ||
175 | .unmask = bf561_internal_unmask_irq, | ||
176 | }; | ||
177 | |||
178 | #ifdef CONFIG_IRQCHIP_DEMUX_GPIO | ||
179 | static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)]; | ||
180 | static unsigned short gpio_edge_triggered[gpio_bank(MAX_BLACKFIN_GPIOS)]; | ||
181 | |||
182 | static void bf561_gpio_ack_irq(unsigned int irq) | ||
183 | { | ||
184 | u16 gpionr = irq - IRQ_PF0; | ||
185 | |||
186 | if(gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { | ||
187 | set_gpio_data(gpionr, 0); | ||
188 | SSYNC(); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static void bf561_gpio_mask_ack_irq(unsigned int irq) | ||
193 | { | ||
194 | u16 gpionr = irq - IRQ_PF0; | ||
195 | |||
196 | if(gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { | ||
197 | set_gpio_data(gpionr, 0); | ||
198 | SSYNC(); | ||
199 | } | ||
200 | |||
201 | set_gpio_maska(gpionr, 0); | ||
202 | SSYNC(); | ||
203 | } | ||
204 | |||
205 | static void bf561_gpio_mask_irq(unsigned int irq) | ||
206 | { | ||
207 | set_gpio_maska(irq - IRQ_PF0, 0); | ||
208 | SSYNC(); | ||
209 | } | ||
210 | |||
211 | static void bf561_gpio_unmask_irq(unsigned int irq) | ||
212 | { | ||
213 | set_gpio_maska(irq - IRQ_PF0, 1); | ||
214 | SSYNC(); | ||
215 | } | ||
216 | |||
217 | static unsigned int bf561_gpio_irq_startup(unsigned int irq) | ||
218 | { | ||
219 | unsigned int ret; | ||
220 | u16 gpionr = irq - IRQ_PF0; | ||
221 | |||
222 | if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) { | ||
223 | |||
224 | ret = gpio_request(gpionr, NULL); | ||
225 | if(ret) | ||
226 | return ret; | ||
227 | |||
228 | } | ||
229 | |||
230 | gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); | ||
231 | bf561_gpio_unmask_irq(irq); | ||
232 | |||
233 | return ret; | ||
234 | |||
235 | } | ||
236 | |||
237 | static void bf561_gpio_irq_shutdown(unsigned int irq) | ||
238 | { | ||
239 | bf561_gpio_mask_irq(irq); | ||
240 | gpio_free(irq - IRQ_PF0); | ||
241 | gpio_enabled[gpio_bank(irq - IRQ_PF0)] &= ~gpio_bit(irq - IRQ_PF0); | ||
242 | } | ||
243 | |||
244 | static int bf561_gpio_irq_type(unsigned int irq, unsigned int type) | ||
245 | { | ||
246 | |||
247 | unsigned int ret; | ||
248 | u16 gpionr = irq - IRQ_PF0; | ||
249 | |||
250 | |||
251 | if (type == IRQ_TYPE_PROBE) { | ||
252 | /* only probe unenabled GPIO interrupt lines */ | ||
253 | if (gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr)) | ||
254 | return 0; | ||
255 | type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; | ||
256 | |||
257 | } | ||
258 | |||
259 | if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING | | ||
260 | IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { | ||
261 | |||
262 | if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) { | ||
263 | |||
264 | ret = gpio_request(gpionr, NULL); | ||
265 | if(ret) | ||
266 | return ret; | ||
267 | |||
268 | } | ||
269 | |||
270 | gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); | ||
271 | } else { | ||
272 | gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | |||
277 | set_gpio_dir(gpionr, 0); | ||
278 | set_gpio_inen(gpionr, 1); | ||
279 | |||
280 | |||
281 | if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { | ||
282 | gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr); | ||
283 | set_gpio_edge(gpionr, 1); | ||
284 | } else { | ||
285 | set_gpio_edge(gpionr, 0); | ||
286 | gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); | ||
287 | } | ||
288 | |||
289 | if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) | ||
290 | == (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) | ||
291 | set_gpio_both(gpionr, 1); | ||
292 | else | ||
293 | set_gpio_both(gpionr, 0); | ||
294 | |||
295 | if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW))) | ||
296 | set_gpio_polar(gpionr, 1); /* low or falling edge denoted by one */ | ||
297 | else | ||
298 | set_gpio_polar(gpionr, 0); /* high or rising edge denoted by zero */ | ||
299 | |||
300 | SSYNC(); | ||
301 | |||
302 | if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) | ||
303 | set_irq_handler(irq, handle_edge_irq); | ||
304 | else | ||
305 | set_irq_handler(irq, handle_level_irq); | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static struct irq_chip bf561_gpio_irqchip = { | ||
311 | .ack = bf561_gpio_ack_irq, | ||
312 | .mask = bf561_gpio_mask_irq, | ||
313 | .mask_ack = bf561_gpio_mask_ack_irq, | ||
314 | .unmask = bf561_gpio_unmask_irq, | ||
315 | .set_type = bf561_gpio_irq_type, | ||
316 | .startup = bf561_gpio_irq_startup, | ||
317 | .shutdown = bf561_gpio_irq_shutdown | ||
318 | }; | ||
319 | |||
320 | static void bf561_demux_gpio_irq(unsigned int inta_irq, | ||
321 | struct irq_desc *intb_desc) | ||
322 | { | ||
323 | int irq, flag_d, mask; | ||
324 | u16 gpio; | ||
325 | |||
326 | switch (inta_irq) { | ||
327 | case IRQ_PROG0_INTA: | ||
328 | irq = IRQ_PF0; | ||
329 | break; | ||
330 | case IRQ_PROG1_INTA: | ||
331 | irq = IRQ_PF16; | ||
332 | break; | ||
333 | case IRQ_PROG2_INTA: | ||
334 | irq = IRQ_PF32; | ||
335 | break; | ||
336 | default: | ||
337 | dump_stack(); | ||
338 | return; | ||
339 | } | ||
340 | |||
341 | gpio = irq - IRQ_PF0; | ||
342 | |||
343 | flag_d = get_gpiop_data(gpio); | ||
344 | mask = flag_d & (gpio_enabled[gpio_bank(gpio)] & | ||
345 | get_gpiop_maska(gpio)); | ||
346 | |||
347 | do { | ||
348 | if (mask & 1) { | ||
349 | struct irq_desc *desc = irq_desc + irq; | ||
350 | desc->handle_irq(irq, desc); | ||
351 | } | ||
352 | irq++; | ||
353 | mask >>= 1; | ||
354 | } while (mask); | ||
355 | |||
356 | |||
357 | } | ||
358 | |||
359 | #endif /* CONFIG_IRQCHIP_DEMUX_GPIO */ | ||
360 | |||
361 | /* | ||
362 | * This function should be called during kernel startup to initialize | ||
363 | * the BFin IRQ handling routines. | ||
364 | */ | ||
365 | int __init init_arch_irq(void) | ||
366 | { | ||
367 | int irq; | ||
368 | unsigned long ilat = 0; | ||
369 | /* Disable all the peripheral intrs - page 4-29 HW Ref manual */ | ||
370 | bfin_write_SICA_IMASK0(SIC_UNMASK_ALL); | ||
371 | bfin_write_SICA_IMASK1(SIC_UNMASK_ALL); | ||
372 | SSYNC(); | ||
373 | |||
374 | local_irq_disable(); | ||
375 | |||
376 | init_exception_buff(); | ||
377 | |||
378 | #ifndef CONFIG_KGDB | ||
379 | bfin_write_EVT0(evt_emulation); | ||
380 | #endif | ||
381 | bfin_write_EVT2(evt_evt2); | ||
382 | bfin_write_EVT3(trap); | ||
383 | bfin_write_EVT5(evt_ivhw); | ||
384 | bfin_write_EVT6(evt_timer); | ||
385 | bfin_write_EVT7(evt_evt7); | ||
386 | bfin_write_EVT8(evt_evt8); | ||
387 | bfin_write_EVT9(evt_evt9); | ||
388 | bfin_write_EVT10(evt_evt10); | ||
389 | bfin_write_EVT11(evt_evt11); | ||
390 | bfin_write_EVT12(evt_evt12); | ||
391 | bfin_write_EVT13(evt_evt13); | ||
392 | bfin_write_EVT14(evt14_softirq); | ||
393 | bfin_write_EVT15(evt_system_call); | ||
394 | CSYNC(); | ||
395 | |||
396 | for (irq = 0; irq < SYS_IRQS; irq++) { | ||
397 | if (irq <= IRQ_CORETMR) | ||
398 | set_irq_chip(irq, &bf561_core_irqchip); | ||
399 | else | ||
400 | set_irq_chip(irq, &bf561_internal_irqchip); | ||
401 | #ifdef CONFIG_IRQCHIP_DEMUX_GPIO | ||
402 | if ((irq != IRQ_PROG0_INTA) && | ||
403 | (irq != IRQ_PROG1_INTA) && (irq != IRQ_PROG2_INTA)) { | ||
404 | #endif | ||
405 | set_irq_handler(irq, handle_simple_irq); | ||
406 | #ifdef CONFIG_IRQCHIP_DEMUX_GPIO | ||
407 | } else { | ||
408 | set_irq_chained_handler(irq, bf561_demux_gpio_irq); | ||
409 | } | ||
410 | #endif | ||
411 | |||
412 | } | ||
413 | |||
414 | #ifdef CONFIG_IRQCHIP_DEMUX_GPIO | ||
415 | for (irq = IRQ_PF0; irq <= IRQ_PF47; irq++) { | ||
416 | set_irq_chip(irq, &bf561_gpio_irqchip); | ||
417 | /* if configured as edge, then will be changed to do_edge_IRQ */ | ||
418 | set_irq_handler(irq, handle_level_irq); | ||
419 | } | ||
420 | #endif | ||
421 | bfin_write_IMASK(0); | ||
422 | CSYNC(); | ||
423 | ilat = bfin_read_ILAT(); | ||
424 | CSYNC(); | ||
425 | bfin_write_ILAT(ilat); | ||
426 | CSYNC(); | ||
427 | |||
428 | printk(KERN_INFO "Configuring Blackfin Priority Driven Interrupts\n"); | ||
429 | /* IMASK=xxx is equivalent to STI xx or irq_flags=xx, | ||
430 | * local_irq_enable() | ||
431 | */ | ||
432 | program_IAR(); | ||
433 | /* Therefore it's better to setup IARs before interrupts enabled */ | ||
434 | search_IAR(); | ||
435 | |||
436 | /* Enable interrupts IVG7-15 */ | ||
437 | irq_flags = irq_flags | IMASK_IVG15 | | ||
438 | IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 | | ||
439 | IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW; | ||
440 | |||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | #ifdef CONFIG_DO_IRQ_L1 | ||
445 | void do_irq(int vec, struct pt_regs *fp)__attribute__((l1_text)); | ||
446 | #endif | ||
447 | |||
448 | void do_irq(int vec, struct pt_regs *fp) | ||
449 | { | ||
450 | if (vec == EVT_IVTMR_P) { | ||
451 | vec = IRQ_CORETMR; | ||
452 | } else { | ||
453 | struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst; | ||
454 | struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop; | ||
455 | unsigned long sic_status0, sic_status1; | ||
456 | |||
457 | SSYNC(); | ||
458 | sic_status0 = bfin_read_SICA_IMASK0() & bfin_read_SICA_ISR0(); | ||
459 | sic_status1 = bfin_read_SICA_IMASK1() & bfin_read_SICA_ISR1(); | ||
460 | |||
461 | for (;; ivg++) { | ||
462 | if (ivg >= ivg_stop) { | ||
463 | atomic_inc(&num_spurious); | ||
464 | return; | ||
465 | } else if ((sic_status0 & ivg->isrflag0) || | ||
466 | (sic_status1 & ivg->isrflag1)) | ||
467 | break; | ||
468 | } | ||
469 | vec = ivg->irqno; | ||
470 | } | ||
471 | asm_do_IRQ(vec, fp); | ||
472 | |||
473 | #ifdef CONFIG_KGDB | ||
474 | kgdb_process_breakpoint(); | ||
475 | #endif | ||
476 | } | ||