diff options
Diffstat (limited to 'arch/cris/arch-v32/kernel/irq.c')
-rw-r--r-- | arch/cris/arch-v32/kernel/irq.c | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/arch/cris/arch-v32/kernel/irq.c b/arch/cris/arch-v32/kernel/irq.c new file mode 100644 index 000000000000..c78cc2685133 --- /dev/null +++ b/arch/cris/arch-v32/kernel/irq.c | |||
@@ -0,0 +1,413 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2003, Axis Communications AB. | ||
3 | */ | ||
4 | |||
5 | #include <asm/irq.h> | ||
6 | #include <linux/irq.h> | ||
7 | #include <linux/interrupt.h> | ||
8 | #include <linux/smp.h> | ||
9 | #include <linux/config.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/profile.h> | ||
14 | #include <linux/proc_fs.h> | ||
15 | #include <linux/seq_file.h> | ||
16 | #include <linux/threads.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/kernel_stat.h> | ||
19 | #include <asm/arch/hwregs/reg_map.h> | ||
20 | #include <asm/arch/hwregs/reg_rdwr.h> | ||
21 | #include <asm/arch/hwregs/intr_vect.h> | ||
22 | #include <asm/arch/hwregs/intr_vect_defs.h> | ||
23 | |||
24 | #define CPU_FIXED -1 | ||
25 | |||
26 | /* IRQ masks (refer to comment for crisv32_do_multiple) */ | ||
27 | #define TIMER_MASK (1 << (TIMER_INTR_VECT - FIRST_IRQ)) | ||
28 | #ifdef CONFIG_ETRAX_KGDB | ||
29 | #if defined(CONFIG_ETRAX_KGDB_PORT0) | ||
30 | #define IGNOREMASK (1 << (SER0_INTR_VECT - FIRST_IRQ)) | ||
31 | #elif defined(CONFIG_ETRAX_KGDB_PORT1) | ||
32 | #define IGNOREMASK (1 << (SER1_INTR_VECT - FIRST_IRQ)) | ||
33 | #elif defined(CONFIG_ETRAX_KGB_PORT2) | ||
34 | #define IGNOREMASK (1 << (SER2_INTR_VECT - FIRST_IRQ)) | ||
35 | #elif defined(CONFIG_ETRAX_KGDB_PORT3) | ||
36 | #define IGNOREMASK (1 << (SER3_INTR_VECT - FIRST_IRQ)) | ||
37 | #endif | ||
38 | #endif | ||
39 | |||
40 | DEFINE_SPINLOCK(irq_lock); | ||
41 | |||
42 | struct cris_irq_allocation | ||
43 | { | ||
44 | int cpu; /* The CPU to which the IRQ is currently allocated. */ | ||
45 | cpumask_t mask; /* The CPUs to which the IRQ may be allocated. */ | ||
46 | }; | ||
47 | |||
48 | struct cris_irq_allocation irq_allocations[NR_IRQS] = | ||
49 | {[0 ... NR_IRQS - 1] = {0, CPU_MASK_ALL}}; | ||
50 | |||
51 | static unsigned long irq_regs[NR_CPUS] = | ||
52 | { | ||
53 | regi_irq, | ||
54 | #ifdef CONFIG_SMP | ||
55 | regi_irq2, | ||
56 | #endif | ||
57 | }; | ||
58 | |||
59 | unsigned long cpu_irq_counters[NR_CPUS]; | ||
60 | unsigned long irq_counters[NR_REAL_IRQS]; | ||
61 | |||
62 | /* From irq.c. */ | ||
63 | extern void weird_irq(void); | ||
64 | |||
65 | /* From entry.S. */ | ||
66 | extern void system_call(void); | ||
67 | extern void nmi_interrupt(void); | ||
68 | extern void multiple_interrupt(void); | ||
69 | extern void gdb_handle_exception(void); | ||
70 | extern void i_mmu_refill(void); | ||
71 | extern void i_mmu_invalid(void); | ||
72 | extern void i_mmu_access(void); | ||
73 | extern void i_mmu_execute(void); | ||
74 | extern void d_mmu_refill(void); | ||
75 | extern void d_mmu_invalid(void); | ||
76 | extern void d_mmu_access(void); | ||
77 | extern void d_mmu_write(void); | ||
78 | |||
79 | /* From kgdb.c. */ | ||
80 | extern void kgdb_init(void); | ||
81 | extern void breakpoint(void); | ||
82 | |||
83 | /* | ||
84 | * Build the IRQ handler stubs using macros from irq.h. First argument is the | ||
85 | * IRQ number, the second argument is the corresponding bit in | ||
86 | * intr_rw_vect_mask found in asm/arch/hwregs/intr_vect_defs.h. | ||
87 | */ | ||
88 | BUILD_IRQ(0x31, (1 << 0)) /* memarb */ | ||
89 | BUILD_IRQ(0x32, (1 << 1)) /* gen_io */ | ||
90 | BUILD_IRQ(0x33, (1 << 2)) /* iop0 */ | ||
91 | BUILD_IRQ(0x34, (1 << 3)) /* iop1 */ | ||
92 | BUILD_IRQ(0x35, (1 << 4)) /* iop2 */ | ||
93 | BUILD_IRQ(0x36, (1 << 5)) /* iop3 */ | ||
94 | BUILD_IRQ(0x37, (1 << 6)) /* dma0 */ | ||
95 | BUILD_IRQ(0x38, (1 << 7)) /* dma1 */ | ||
96 | BUILD_IRQ(0x39, (1 << 8)) /* dma2 */ | ||
97 | BUILD_IRQ(0x3a, (1 << 9)) /* dma3 */ | ||
98 | BUILD_IRQ(0x3b, (1 << 10)) /* dma4 */ | ||
99 | BUILD_IRQ(0x3c, (1 << 11)) /* dma5 */ | ||
100 | BUILD_IRQ(0x3d, (1 << 12)) /* dma6 */ | ||
101 | BUILD_IRQ(0x3e, (1 << 13)) /* dma7 */ | ||
102 | BUILD_IRQ(0x3f, (1 << 14)) /* dma8 */ | ||
103 | BUILD_IRQ(0x40, (1 << 15)) /* dma9 */ | ||
104 | BUILD_IRQ(0x41, (1 << 16)) /* ata */ | ||
105 | BUILD_IRQ(0x42, (1 << 17)) /* sser0 */ | ||
106 | BUILD_IRQ(0x43, (1 << 18)) /* sser1 */ | ||
107 | BUILD_IRQ(0x44, (1 << 19)) /* ser0 */ | ||
108 | BUILD_IRQ(0x45, (1 << 20)) /* ser1 */ | ||
109 | BUILD_IRQ(0x46, (1 << 21)) /* ser2 */ | ||
110 | BUILD_IRQ(0x47, (1 << 22)) /* ser3 */ | ||
111 | BUILD_IRQ(0x48, (1 << 23)) | ||
112 | BUILD_IRQ(0x49, (1 << 24)) /* eth0 */ | ||
113 | BUILD_IRQ(0x4a, (1 << 25)) /* eth1 */ | ||
114 | BUILD_TIMER_IRQ(0x4b, (1 << 26))/* timer */ | ||
115 | BUILD_IRQ(0x4c, (1 << 27)) /* bif_arb */ | ||
116 | BUILD_IRQ(0x4d, (1 << 28)) /* bif_dma */ | ||
117 | BUILD_IRQ(0x4e, (1 << 29)) /* ext */ | ||
118 | BUILD_IRQ(0x4f, (1 << 29)) /* ipi */ | ||
119 | |||
120 | /* Pointers to the low-level handlers. */ | ||
121 | static void (*interrupt[NR_IRQS])(void) = { | ||
122 | IRQ0x31_interrupt, IRQ0x32_interrupt, IRQ0x33_interrupt, | ||
123 | IRQ0x34_interrupt, IRQ0x35_interrupt, IRQ0x36_interrupt, | ||
124 | IRQ0x37_interrupt, IRQ0x38_interrupt, IRQ0x39_interrupt, | ||
125 | IRQ0x3a_interrupt, IRQ0x3b_interrupt, IRQ0x3c_interrupt, | ||
126 | IRQ0x3d_interrupt, IRQ0x3e_interrupt, IRQ0x3f_interrupt, | ||
127 | IRQ0x40_interrupt, IRQ0x41_interrupt, IRQ0x42_interrupt, | ||
128 | IRQ0x43_interrupt, IRQ0x44_interrupt, IRQ0x45_interrupt, | ||
129 | IRQ0x46_interrupt, IRQ0x47_interrupt, IRQ0x48_interrupt, | ||
130 | IRQ0x49_interrupt, IRQ0x4a_interrupt, IRQ0x4b_interrupt, | ||
131 | IRQ0x4c_interrupt, IRQ0x4d_interrupt, IRQ0x4e_interrupt, | ||
132 | IRQ0x4f_interrupt | ||
133 | }; | ||
134 | |||
135 | void | ||
136 | block_irq(int irq, int cpu) | ||
137 | { | ||
138 | int intr_mask; | ||
139 | unsigned long flags; | ||
140 | |||
141 | spin_lock_irqsave(&irq_lock, flags); | ||
142 | intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); | ||
143 | |||
144 | /* Remember; 1 let thru, 0 block. */ | ||
145 | intr_mask &= ~(1 << (irq - FIRST_IRQ)); | ||
146 | |||
147 | REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask); | ||
148 | spin_unlock_irqrestore(&irq_lock, flags); | ||
149 | } | ||
150 | |||
151 | void | ||
152 | unblock_irq(int irq, int cpu) | ||
153 | { | ||
154 | int intr_mask; | ||
155 | unsigned long flags; | ||
156 | |||
157 | spin_lock_irqsave(&irq_lock, flags); | ||
158 | intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); | ||
159 | |||
160 | /* Remember; 1 let thru, 0 block. */ | ||
161 | intr_mask |= (1 << (irq - FIRST_IRQ)); | ||
162 | |||
163 | REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask); | ||
164 | spin_unlock_irqrestore(&irq_lock, flags); | ||
165 | } | ||
166 | |||
167 | /* Find out which CPU the irq should be allocated to. */ | ||
168 | static int irq_cpu(int irq) | ||
169 | { | ||
170 | int cpu; | ||
171 | unsigned long flags; | ||
172 | |||
173 | spin_lock_irqsave(&irq_lock, flags); | ||
174 | cpu = irq_allocations[irq - FIRST_IRQ].cpu; | ||
175 | |||
176 | /* Fixed interrupts stay on the local CPU. */ | ||
177 | if (cpu == CPU_FIXED) | ||
178 | { | ||
179 | spin_unlock_irqrestore(&irq_lock, flags); | ||
180 | return smp_processor_id(); | ||
181 | } | ||
182 | |||
183 | |||
184 | /* Let the interrupt stay if possible */ | ||
185 | if (cpu_isset(cpu, irq_allocations[irq - FIRST_IRQ].mask)) | ||
186 | goto out; | ||
187 | |||
188 | /* IRQ must be moved to another CPU. */ | ||
189 | cpu = first_cpu(irq_allocations[irq - FIRST_IRQ].mask); | ||
190 | irq_allocations[irq - FIRST_IRQ].cpu = cpu; | ||
191 | out: | ||
192 | spin_unlock_irqrestore(&irq_lock, flags); | ||
193 | return cpu; | ||
194 | } | ||
195 | |||
196 | void | ||
197 | mask_irq(int irq) | ||
198 | { | ||
199 | int cpu; | ||
200 | |||
201 | for (cpu = 0; cpu < NR_CPUS; cpu++) | ||
202 | block_irq(irq, cpu); | ||
203 | } | ||
204 | |||
205 | void | ||
206 | unmask_irq(int irq) | ||
207 | { | ||
208 | unblock_irq(irq, irq_cpu(irq)); | ||
209 | } | ||
210 | |||
211 | |||
212 | static unsigned int startup_crisv32_irq(unsigned int irq) | ||
213 | { | ||
214 | unmask_irq(irq); | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static void shutdown_crisv32_irq(unsigned int irq) | ||
219 | { | ||
220 | mask_irq(irq); | ||
221 | } | ||
222 | |||
223 | static void enable_crisv32_irq(unsigned int irq) | ||
224 | { | ||
225 | unmask_irq(irq); | ||
226 | } | ||
227 | |||
228 | static void disable_crisv32_irq(unsigned int irq) | ||
229 | { | ||
230 | mask_irq(irq); | ||
231 | } | ||
232 | |||
233 | static void ack_crisv32_irq(unsigned int irq) | ||
234 | { | ||
235 | } | ||
236 | |||
237 | static void end_crisv32_irq(unsigned int irq) | ||
238 | { | ||
239 | } | ||
240 | |||
241 | void set_affinity_crisv32_irq(unsigned int irq, cpumask_t dest) | ||
242 | { | ||
243 | unsigned long flags; | ||
244 | spin_lock_irqsave(&irq_lock, flags); | ||
245 | irq_allocations[irq - FIRST_IRQ].mask = dest; | ||
246 | spin_unlock_irqrestore(&irq_lock, flags); | ||
247 | } | ||
248 | |||
249 | static struct hw_interrupt_type crisv32_irq_type = { | ||
250 | .typename = "CRISv32", | ||
251 | .startup = startup_crisv32_irq, | ||
252 | .shutdown = shutdown_crisv32_irq, | ||
253 | .enable = enable_crisv32_irq, | ||
254 | .disable = disable_crisv32_irq, | ||
255 | .ack = ack_crisv32_irq, | ||
256 | .end = end_crisv32_irq, | ||
257 | .set_affinity = set_affinity_crisv32_irq | ||
258 | }; | ||
259 | |||
260 | void | ||
261 | set_exception_vector(int n, irqvectptr addr) | ||
262 | { | ||
263 | etrax_irv->v[n] = (irqvectptr) addr; | ||
264 | } | ||
265 | |||
266 | extern void do_IRQ(int irq, struct pt_regs * regs); | ||
267 | |||
268 | void | ||
269 | crisv32_do_IRQ(int irq, int block, struct pt_regs* regs) | ||
270 | { | ||
271 | /* Interrupts that may not be moved to another CPU and | ||
272 | * are SA_INTERRUPT may skip blocking. This is currently | ||
273 | * only valid for the timer IRQ and the IPI and is used | ||
274 | * for the timer interrupt to avoid watchdog starvation. | ||
275 | */ | ||
276 | if (!block) { | ||
277 | do_IRQ(irq, regs); | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | block_irq(irq, smp_processor_id()); | ||
282 | do_IRQ(irq, regs); | ||
283 | |||
284 | unblock_irq(irq, irq_cpu(irq)); | ||
285 | } | ||
286 | |||
287 | /* If multiple interrupts occur simultaneously we get a multiple | ||
288 | * interrupt from the CPU and software has to sort out which | ||
289 | * interrupts that happened. There are two special cases here: | ||
290 | * | ||
291 | * 1. Timer interrupts may never be blocked because of the | ||
292 | * watchdog (refer to comment in include/asr/arch/irq.h) | ||
293 | * 2. GDB serial port IRQs are unhandled here and will be handled | ||
294 | * as a single IRQ when it strikes again because the GDB | ||
295 | * stubb wants to save the registers in its own fashion. | ||
296 | */ | ||
297 | void | ||
298 | crisv32_do_multiple(struct pt_regs* regs) | ||
299 | { | ||
300 | int cpu; | ||
301 | int mask; | ||
302 | int masked; | ||
303 | int bit; | ||
304 | |||
305 | cpu = smp_processor_id(); | ||
306 | |||
307 | /* An extra irq_enter here to prevent softIRQs to run after | ||
308 | * each do_IRQ. This will decrease the interrupt latency. | ||
309 | */ | ||
310 | irq_enter(); | ||
311 | |||
312 | /* Get which IRQs that happend. */ | ||
313 | masked = REG_RD_INT(intr_vect, irq_regs[cpu], r_masked_vect); | ||
314 | |||
315 | /* Calculate new IRQ mask with these IRQs disabled. */ | ||
316 | mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); | ||
317 | mask &= ~masked; | ||
318 | |||
319 | /* Timer IRQ is never masked */ | ||
320 | if (masked & TIMER_MASK) | ||
321 | mask |= TIMER_MASK; | ||
322 | |||
323 | /* Block all the IRQs */ | ||
324 | REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask); | ||
325 | |||
326 | /* Check for timer IRQ and handle it special. */ | ||
327 | if (masked & TIMER_MASK) { | ||
328 | masked &= ~TIMER_MASK; | ||
329 | do_IRQ(TIMER_INTR_VECT, regs); | ||
330 | } | ||
331 | |||
332 | #ifdef IGNORE_MASK | ||
333 | /* Remove IRQs that can't be handled as multiple. */ | ||
334 | masked &= ~IGNORE_MASK; | ||
335 | #endif | ||
336 | |||
337 | /* Handle the rest of the IRQs. */ | ||
338 | for (bit = 0; bit < 32; bit++) | ||
339 | { | ||
340 | if (masked & (1 << bit)) | ||
341 | do_IRQ(bit + FIRST_IRQ, regs); | ||
342 | } | ||
343 | |||
344 | /* Unblock all the IRQs. */ | ||
345 | mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); | ||
346 | mask |= masked; | ||
347 | REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask); | ||
348 | |||
349 | /* This irq_exit() will trigger the soft IRQs. */ | ||
350 | irq_exit(); | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | * This is called by start_kernel. It fixes the IRQ masks and setup the | ||
355 | * interrupt vector table to point to bad_interrupt pointers. | ||
356 | */ | ||
357 | void __init | ||
358 | init_IRQ(void) | ||
359 | { | ||
360 | int i; | ||
361 | int j; | ||
362 | reg_intr_vect_rw_mask vect_mask = {0}; | ||
363 | |||
364 | /* Clear all interrupts masks. */ | ||
365 | REG_WR(intr_vect, regi_irq, rw_mask, vect_mask); | ||
366 | |||
367 | for (i = 0; i < 256; i++) | ||
368 | etrax_irv->v[i] = weird_irq; | ||
369 | |||
370 | /* Point all IRQ's to bad handlers. */ | ||
371 | for (i = FIRST_IRQ, j = 0; j < NR_IRQS; i++, j++) { | ||
372 | irq_desc[j].handler = &crisv32_irq_type; | ||
373 | set_exception_vector(i, interrupt[j]); | ||
374 | } | ||
375 | |||
376 | /* Mark Timer and IPI IRQs as CPU local */ | ||
377 | irq_allocations[TIMER_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; | ||
378 | irq_desc[TIMER_INTR_VECT].status |= IRQ_PER_CPU; | ||
379 | irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; | ||
380 | irq_desc[IPI_INTR_VECT].status |= IRQ_PER_CPU; | ||
381 | |||
382 | set_exception_vector(0x00, nmi_interrupt); | ||
383 | set_exception_vector(0x30, multiple_interrupt); | ||
384 | |||
385 | /* Set up handler for various MMU bus faults. */ | ||
386 | set_exception_vector(0x04, i_mmu_refill); | ||
387 | set_exception_vector(0x05, i_mmu_invalid); | ||
388 | set_exception_vector(0x06, i_mmu_access); | ||
389 | set_exception_vector(0x07, i_mmu_execute); | ||
390 | set_exception_vector(0x08, d_mmu_refill); | ||
391 | set_exception_vector(0x09, d_mmu_invalid); | ||
392 | set_exception_vector(0x0a, d_mmu_access); | ||
393 | set_exception_vector(0x0b, d_mmu_write); | ||
394 | |||
395 | /* The system-call trap is reached by "break 13". */ | ||
396 | set_exception_vector(0x1d, system_call); | ||
397 | |||
398 | /* Exception handlers for debugging, both user-mode and kernel-mode. */ | ||
399 | |||
400 | /* Break 8. */ | ||
401 | set_exception_vector(0x18, gdb_handle_exception); | ||
402 | /* Hardware single step. */ | ||
403 | set_exception_vector(0x3, gdb_handle_exception); | ||
404 | /* Hardware breakpoint. */ | ||
405 | set_exception_vector(0xc, gdb_handle_exception); | ||
406 | |||
407 | #ifdef CONFIG_ETRAX_KGDB | ||
408 | kgdb_init(); | ||
409 | /* Everything is set up; now trap the kernel. */ | ||
410 | breakpoint(); | ||
411 | #endif | ||
412 | } | ||
413 | |||