Commit | Line | Data |
---|---|---|
1394f032 BW |
1 | /* |
2 | * File: arch/blackfin/mach-common/ints-priority-dc.c | |
3 | * Based on: | |
4 | * Author: | |
5 | * | |
6 | * Created: ? | |
d2d50aa9 | 7 | * Description: Set up the interrupt priorities |
1394f032 BW |
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 | ||
1f83b8f1 | 186 | if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { |
1394f032 BW |
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 | ||
1f83b8f1 | 196 | if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { |
1394f032 BW |
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); | |
1f83b8f1 | 225 | if (ret) |
1394f032 BW |
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); | |
1f83b8f1 | 265 | if (ret) |
1394f032 BW |
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 | ||
c04d66bb BW |
374 | bfin_write_SICA_IWR0(IWR_ENABLE_ALL); |
375 | bfin_write_SICA_IWR1(IWR_ENABLE_ALL); | |
1c5d2265 | 376 | |
1394f032 BW |
377 | local_irq_disable(); |
378 | ||
379 | init_exception_buff(); | |
380 | ||
381 | #ifndef CONFIG_KGDB | |
382 | bfin_write_EVT0(evt_emulation); | |
383 | #endif | |
384 | bfin_write_EVT2(evt_evt2); | |
385 | bfin_write_EVT3(trap); | |
386 | bfin_write_EVT5(evt_ivhw); | |
387 | bfin_write_EVT6(evt_timer); | |
388 | bfin_write_EVT7(evt_evt7); | |
389 | bfin_write_EVT8(evt_evt8); | |
390 | bfin_write_EVT9(evt_evt9); | |
391 | bfin_write_EVT10(evt_evt10); | |
392 | bfin_write_EVT11(evt_evt11); | |
393 | bfin_write_EVT12(evt_evt12); | |
394 | bfin_write_EVT13(evt_evt13); | |
395 | bfin_write_EVT14(evt14_softirq); | |
396 | bfin_write_EVT15(evt_system_call); | |
397 | CSYNC(); | |
398 | ||
e3f23000 | 399 | for (irq = 0; irq <= SYS_IRQS; irq++) { |
1394f032 BW |
400 | if (irq <= IRQ_CORETMR) |
401 | set_irq_chip(irq, &bf561_core_irqchip); | |
402 | else | |
403 | set_irq_chip(irq, &bf561_internal_irqchip); | |
404 | #ifdef CONFIG_IRQCHIP_DEMUX_GPIO | |
405 | if ((irq != IRQ_PROG0_INTA) && | |
406 | (irq != IRQ_PROG1_INTA) && (irq != IRQ_PROG2_INTA)) { | |
407 | #endif | |
408 | set_irq_handler(irq, handle_simple_irq); | |
409 | #ifdef CONFIG_IRQCHIP_DEMUX_GPIO | |
410 | } else { | |
411 | set_irq_chained_handler(irq, bf561_demux_gpio_irq); | |
412 | } | |
413 | #endif | |
414 | ||
415 | } | |
416 | ||
417 | #ifdef CONFIG_IRQCHIP_DEMUX_GPIO | |
418 | for (irq = IRQ_PF0; irq <= IRQ_PF47; irq++) { | |
419 | set_irq_chip(irq, &bf561_gpio_irqchip); | |
420 | /* if configured as edge, then will be changed to do_edge_IRQ */ | |
421 | set_irq_handler(irq, handle_level_irq); | |
422 | } | |
423 | #endif | |
424 | bfin_write_IMASK(0); | |
425 | CSYNC(); | |
426 | ilat = bfin_read_ILAT(); | |
427 | CSYNC(); | |
428 | bfin_write_ILAT(ilat); | |
429 | CSYNC(); | |
430 | ||
431 | printk(KERN_INFO "Configuring Blackfin Priority Driven Interrupts\n"); | |
432 | /* IMASK=xxx is equivalent to STI xx or irq_flags=xx, | |
433 | * local_irq_enable() | |
434 | */ | |
435 | program_IAR(); | |
436 | /* Therefore it's better to setup IARs before interrupts enabled */ | |
437 | search_IAR(); | |
438 | ||
439 | /* Enable interrupts IVG7-15 */ | |
440 | irq_flags = irq_flags | IMASK_IVG15 | | |
441 | IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 | | |
442 | IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW; | |
443 | ||
444 | return 0; | |
445 | } | |
446 | ||
447 | #ifdef CONFIG_DO_IRQ_L1 | |
448 | void do_irq(int vec, struct pt_regs *fp)__attribute__((l1_text)); | |
449 | #endif | |
450 | ||
451 | void do_irq(int vec, struct pt_regs *fp) | |
452 | { | |
453 | if (vec == EVT_IVTMR_P) { | |
454 | vec = IRQ_CORETMR; | |
455 | } else { | |
456 | struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst; | |
457 | struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop; | |
458 | unsigned long sic_status0, sic_status1; | |
459 | ||
460 | SSYNC(); | |
461 | sic_status0 = bfin_read_SICA_IMASK0() & bfin_read_SICA_ISR0(); | |
462 | sic_status1 = bfin_read_SICA_IMASK1() & bfin_read_SICA_ISR1(); | |
463 | ||
464 | for (;; ivg++) { | |
465 | if (ivg >= ivg_stop) { | |
466 | atomic_inc(&num_spurious); | |
467 | return; | |
468 | } else if ((sic_status0 & ivg->isrflag0) || | |
469 | (sic_status1 & ivg->isrflag1)) | |
470 | break; | |
471 | } | |
472 | vec = ivg->irqno; | |
473 | } | |
474 | asm_do_IRQ(vec, fp); | |
475 | ||
476 | #ifdef CONFIG_KGDB | |
477 | kgdb_process_breakpoint(); | |
478 | #endif | |
479 | } |