diff options
Diffstat (limited to 'arch/mips/sibyte/sb1250/irq.c')
-rw-r--r-- | arch/mips/sibyte/sb1250/irq.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/arch/mips/sibyte/sb1250/irq.c b/arch/mips/sibyte/sb1250/irq.c new file mode 100644 index 000000000000..2728abbc94d2 --- /dev/null +++ b/arch/mips/sibyte/sb1250/irq.c | |||
@@ -0,0 +1,431 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version 2 | ||
7 | * of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | */ | ||
18 | #include <linux/config.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/linkage.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/smp.h> | ||
25 | #include <linux/mm.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/kernel_stat.h> | ||
28 | |||
29 | #include <asm/errno.h> | ||
30 | #include <asm/signal.h> | ||
31 | #include <asm/system.h> | ||
32 | #include <asm/ptrace.h> | ||
33 | #include <asm/io.h> | ||
34 | |||
35 | #include <asm/sibyte/sb1250_regs.h> | ||
36 | #include <asm/sibyte/sb1250_int.h> | ||
37 | #include <asm/sibyte/sb1250_uart.h> | ||
38 | #include <asm/sibyte/sb1250_scd.h> | ||
39 | #include <asm/sibyte/sb1250.h> | ||
40 | |||
41 | /* | ||
42 | * These are the routines that handle all the low level interrupt stuff. | ||
43 | * Actions handled here are: initialization of the interrupt map, requesting of | ||
44 | * interrupt lines by handlers, dispatching if interrupts to handlers, probing | ||
45 | * for interrupt lines | ||
46 | */ | ||
47 | |||
48 | |||
49 | #define shutdown_sb1250_irq disable_sb1250_irq | ||
50 | static void end_sb1250_irq(unsigned int irq); | ||
51 | static void enable_sb1250_irq(unsigned int irq); | ||
52 | static void disable_sb1250_irq(unsigned int irq); | ||
53 | static unsigned int startup_sb1250_irq(unsigned int irq); | ||
54 | static void ack_sb1250_irq(unsigned int irq); | ||
55 | #ifdef CONFIG_SMP | ||
56 | static void sb1250_set_affinity(unsigned int irq, unsigned long mask); | ||
57 | #endif | ||
58 | |||
59 | #ifdef CONFIG_SIBYTE_HAS_LDT | ||
60 | extern unsigned long ldt_eoi_space; | ||
61 | #endif | ||
62 | |||
63 | #ifdef CONFIG_KGDB | ||
64 | static int kgdb_irq; | ||
65 | |||
66 | /* Default to UART1 */ | ||
67 | int kgdb_port = 1; | ||
68 | #ifdef CONFIG_SIBYTE_SB1250_DUART | ||
69 | extern char sb1250_duart_present[]; | ||
70 | #endif | ||
71 | #endif | ||
72 | |||
73 | static struct hw_interrupt_type sb1250_irq_type = { | ||
74 | "SB1250-IMR", | ||
75 | startup_sb1250_irq, | ||
76 | shutdown_sb1250_irq, | ||
77 | enable_sb1250_irq, | ||
78 | disable_sb1250_irq, | ||
79 | ack_sb1250_irq, | ||
80 | end_sb1250_irq, | ||
81 | #ifdef CONFIG_SMP | ||
82 | sb1250_set_affinity | ||
83 | #else | ||
84 | NULL | ||
85 | #endif | ||
86 | }; | ||
87 | |||
88 | /* Store the CPU id (not the logical number) */ | ||
89 | int sb1250_irq_owner[SB1250_NR_IRQS]; | ||
90 | |||
91 | DEFINE_SPINLOCK(sb1250_imr_lock); | ||
92 | |||
93 | void sb1250_mask_irq(int cpu, int irq) | ||
94 | { | ||
95 | unsigned long flags; | ||
96 | u64 cur_ints; | ||
97 | |||
98 | spin_lock_irqsave(&sb1250_imr_lock, flags); | ||
99 | cur_ints = __bus_readq(IOADDR(A_IMR_MAPPER(cpu) + | ||
100 | R_IMR_INTERRUPT_MASK)); | ||
101 | cur_ints |= (((u64) 1) << irq); | ||
102 | __bus_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) + | ||
103 | R_IMR_INTERRUPT_MASK)); | ||
104 | spin_unlock_irqrestore(&sb1250_imr_lock, flags); | ||
105 | } | ||
106 | |||
107 | void sb1250_unmask_irq(int cpu, int irq) | ||
108 | { | ||
109 | unsigned long flags; | ||
110 | u64 cur_ints; | ||
111 | |||
112 | spin_lock_irqsave(&sb1250_imr_lock, flags); | ||
113 | cur_ints = __bus_readq(IOADDR(A_IMR_MAPPER(cpu) + | ||
114 | R_IMR_INTERRUPT_MASK)); | ||
115 | cur_ints &= ~(((u64) 1) << irq); | ||
116 | __bus_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) + | ||
117 | R_IMR_INTERRUPT_MASK)); | ||
118 | spin_unlock_irqrestore(&sb1250_imr_lock, flags); | ||
119 | } | ||
120 | |||
121 | #ifdef CONFIG_SMP | ||
122 | static void sb1250_set_affinity(unsigned int irq, unsigned long mask) | ||
123 | { | ||
124 | int i = 0, old_cpu, cpu, int_on; | ||
125 | u64 cur_ints; | ||
126 | irq_desc_t *desc = irq_desc + irq; | ||
127 | unsigned long flags; | ||
128 | |||
129 | while (mask) { | ||
130 | if (mask & 1) { | ||
131 | mask >>= 1; | ||
132 | break; | ||
133 | } | ||
134 | mask >>= 1; | ||
135 | i++; | ||
136 | } | ||
137 | |||
138 | if (mask) { | ||
139 | printk("attempted to set irq affinity for irq %d to multiple CPUs\n", irq); | ||
140 | return; | ||
141 | } | ||
142 | |||
143 | /* Convert logical CPU to physical CPU */ | ||
144 | cpu = cpu_logical_map(i); | ||
145 | |||
146 | /* Protect against other affinity changers and IMR manipulation */ | ||
147 | spin_lock_irqsave(&desc->lock, flags); | ||
148 | spin_lock(&sb1250_imr_lock); | ||
149 | |||
150 | /* Swizzle each CPU's IMR (but leave the IP selection alone) */ | ||
151 | old_cpu = sb1250_irq_owner[irq]; | ||
152 | cur_ints = __bus_readq(IOADDR(A_IMR_MAPPER(old_cpu) + | ||
153 | R_IMR_INTERRUPT_MASK)); | ||
154 | int_on = !(cur_ints & (((u64) 1) << irq)); | ||
155 | if (int_on) { | ||
156 | /* If it was on, mask it */ | ||
157 | cur_ints |= (((u64) 1) << irq); | ||
158 | __bus_writeq(cur_ints, IOADDR(A_IMR_MAPPER(old_cpu) + | ||
159 | R_IMR_INTERRUPT_MASK)); | ||
160 | } | ||
161 | sb1250_irq_owner[irq] = cpu; | ||
162 | if (int_on) { | ||
163 | /* unmask for the new CPU */ | ||
164 | cur_ints = __bus_readq(IOADDR(A_IMR_MAPPER(cpu) + | ||
165 | R_IMR_INTERRUPT_MASK)); | ||
166 | cur_ints &= ~(((u64) 1) << irq); | ||
167 | __bus_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) + | ||
168 | R_IMR_INTERRUPT_MASK)); | ||
169 | } | ||
170 | spin_unlock(&sb1250_imr_lock); | ||
171 | spin_unlock_irqrestore(&desc->lock, flags); | ||
172 | } | ||
173 | #endif | ||
174 | |||
175 | |||
176 | /* Defined in arch/mips/sibyte/sb1250/irq_handler.S */ | ||
177 | extern void sb1250_irq_handler(void); | ||
178 | |||
179 | /*****************************************************************************/ | ||
180 | |||
181 | static unsigned int startup_sb1250_irq(unsigned int irq) | ||
182 | { | ||
183 | sb1250_unmask_irq(sb1250_irq_owner[irq], irq); | ||
184 | |||
185 | return 0; /* never anything pending */ | ||
186 | } | ||
187 | |||
188 | |||
189 | static void disable_sb1250_irq(unsigned int irq) | ||
190 | { | ||
191 | sb1250_mask_irq(sb1250_irq_owner[irq], irq); | ||
192 | } | ||
193 | |||
194 | static void enable_sb1250_irq(unsigned int irq) | ||
195 | { | ||
196 | sb1250_unmask_irq(sb1250_irq_owner[irq], irq); | ||
197 | } | ||
198 | |||
199 | |||
200 | static void ack_sb1250_irq(unsigned int irq) | ||
201 | { | ||
202 | #ifdef CONFIG_SIBYTE_HAS_LDT | ||
203 | u64 pending; | ||
204 | |||
205 | /* | ||
206 | * If the interrupt was an HT interrupt, now is the time to | ||
207 | * clear it. NOTE: we assume the HT bridge was set up to | ||
208 | * deliver the interrupts to all CPUs (which makes affinity | ||
209 | * changing easier for us) | ||
210 | */ | ||
211 | pending = bus_readq(IOADDR(A_IMR_REGISTER(sb1250_irq_owner[irq], | ||
212 | R_IMR_LDT_INTERRUPT))); | ||
213 | pending &= ((u64)1 << (irq)); | ||
214 | if (pending) { | ||
215 | int i; | ||
216 | for (i=0; i<NR_CPUS; i++) { | ||
217 | int cpu; | ||
218 | #ifdef CONFIG_SMP | ||
219 | cpu = cpu_logical_map(i); | ||
220 | #else | ||
221 | cpu = i; | ||
222 | #endif | ||
223 | /* | ||
224 | * Clear for all CPUs so an affinity switch | ||
225 | * doesn't find an old status | ||
226 | */ | ||
227 | bus_writeq(pending, | ||
228 | IOADDR(A_IMR_REGISTER(cpu, | ||
229 | R_IMR_LDT_INTERRUPT_CLR))); | ||
230 | } | ||
231 | |||
232 | /* | ||
233 | * Generate EOI. For Pass 1 parts, EOI is a nop. For | ||
234 | * Pass 2, the LDT world may be edge-triggered, but | ||
235 | * this EOI shouldn't hurt. If they are | ||
236 | * level-sensitive, the EOI is required. | ||
237 | */ | ||
238 | *(uint32_t *)(ldt_eoi_space+(irq<<16)+(7<<2)) = 0; | ||
239 | } | ||
240 | #endif | ||
241 | sb1250_mask_irq(sb1250_irq_owner[irq], irq); | ||
242 | } | ||
243 | |||
244 | |||
245 | static void end_sb1250_irq(unsigned int irq) | ||
246 | { | ||
247 | if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) { | ||
248 | sb1250_unmask_irq(sb1250_irq_owner[irq], irq); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | |||
253 | void __init init_sb1250_irqs(void) | ||
254 | { | ||
255 | int i; | ||
256 | |||
257 | for (i = 0; i < NR_IRQS; i++) { | ||
258 | irq_desc[i].status = IRQ_DISABLED; | ||
259 | irq_desc[i].action = 0; | ||
260 | irq_desc[i].depth = 1; | ||
261 | if (i < SB1250_NR_IRQS) { | ||
262 | irq_desc[i].handler = &sb1250_irq_type; | ||
263 | sb1250_irq_owner[i] = 0; | ||
264 | } else { | ||
265 | irq_desc[i].handler = &no_irq_type; | ||
266 | } | ||
267 | } | ||
268 | } | ||
269 | |||
270 | |||
271 | static irqreturn_t sb1250_dummy_handler(int irq, void *dev_id, | ||
272 | struct pt_regs *regs) | ||
273 | { | ||
274 | return IRQ_NONE; | ||
275 | } | ||
276 | |||
277 | static struct irqaction sb1250_dummy_action = { | ||
278 | .handler = sb1250_dummy_handler, | ||
279 | .flags = 0, | ||
280 | .mask = CPU_MASK_NONE, | ||
281 | .name = "sb1250-private", | ||
282 | .next = NULL, | ||
283 | .dev_id = 0 | ||
284 | }; | ||
285 | |||
286 | int sb1250_steal_irq(int irq) | ||
287 | { | ||
288 | irq_desc_t *desc = irq_desc + irq; | ||
289 | unsigned long flags; | ||
290 | int retval = 0; | ||
291 | |||
292 | if (irq >= SB1250_NR_IRQS) | ||
293 | return -EINVAL; | ||
294 | |||
295 | spin_lock_irqsave(&desc->lock,flags); | ||
296 | /* Don't allow sharing at all for these */ | ||
297 | if (desc->action != NULL) | ||
298 | retval = -EBUSY; | ||
299 | else { | ||
300 | desc->action = &sb1250_dummy_action; | ||
301 | desc->depth = 0; | ||
302 | } | ||
303 | spin_unlock_irqrestore(&desc->lock,flags); | ||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * arch_init_irq is called early in the boot sequence from init/main.c via | ||
309 | * init_IRQ. It is responsible for setting up the interrupt mapper and | ||
310 | * installing the handler that will be responsible for dispatching interrupts | ||
311 | * to the "right" place. | ||
312 | */ | ||
313 | /* | ||
314 | * For now, map all interrupts to IP[2]. We could save | ||
315 | * some cycles by parceling out system interrupts to different | ||
316 | * IP lines, but keep it simple for bringup. We'll also direct | ||
317 | * all interrupts to a single CPU; we should probably route | ||
318 | * PCI and LDT to one cpu and everything else to the other | ||
319 | * to balance the load a bit. | ||
320 | * | ||
321 | * On the second cpu, everything is set to IP5, which is | ||
322 | * ignored, EXCEPT the mailbox interrupt. That one is | ||
323 | * set to IP[2] so it is handled. This is needed so we | ||
324 | * can do cross-cpu function calls, as requred by SMP | ||
325 | */ | ||
326 | |||
327 | #define IMR_IP2_VAL K_INT_MAP_I0 | ||
328 | #define IMR_IP3_VAL K_INT_MAP_I1 | ||
329 | #define IMR_IP4_VAL K_INT_MAP_I2 | ||
330 | #define IMR_IP5_VAL K_INT_MAP_I3 | ||
331 | #define IMR_IP6_VAL K_INT_MAP_I4 | ||
332 | |||
333 | void __init arch_init_irq(void) | ||
334 | { | ||
335 | |||
336 | unsigned int i; | ||
337 | u64 tmp; | ||
338 | unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 | | ||
339 | STATUSF_IP1 | STATUSF_IP0; | ||
340 | |||
341 | /* Default everything to IP2 */ | ||
342 | for (i = 0; i < SB1250_NR_IRQS; i++) { /* was I0 */ | ||
343 | bus_writeq(IMR_IP2_VAL, | ||
344 | IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + | ||
345 | (i << 3))); | ||
346 | bus_writeq(IMR_IP2_VAL, | ||
347 | IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MAP_BASE) + | ||
348 | (i << 3))); | ||
349 | } | ||
350 | |||
351 | init_sb1250_irqs(); | ||
352 | |||
353 | /* | ||
354 | * Map the high 16 bits of the mailbox registers to IP[3], for | ||
355 | * inter-cpu messages | ||
356 | */ | ||
357 | /* Was I1 */ | ||
358 | bus_writeq(IMR_IP3_VAL, | ||
359 | IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + | ||
360 | (K_INT_MBOX_0 << 3))); | ||
361 | bus_writeq(IMR_IP3_VAL, | ||
362 | IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MAP_BASE) + | ||
363 | (K_INT_MBOX_0 << 3))); | ||
364 | |||
365 | /* Clear the mailboxes. The firmware may leave them dirty */ | ||
366 | bus_writeq(0xffffffffffffffffULL, | ||
367 | IOADDR(A_IMR_REGISTER(0, R_IMR_MAILBOX_CLR_CPU))); | ||
368 | bus_writeq(0xffffffffffffffffULL, | ||
369 | IOADDR(A_IMR_REGISTER(1, R_IMR_MAILBOX_CLR_CPU))); | ||
370 | |||
371 | /* Mask everything except the mailbox registers for both cpus */ | ||
372 | tmp = ~((u64) 0) ^ (((u64) 1) << K_INT_MBOX_0); | ||
373 | bus_writeq(tmp, IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MASK))); | ||
374 | bus_writeq(tmp, IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MASK))); | ||
375 | |||
376 | sb1250_steal_irq(K_INT_MBOX_0); | ||
377 | |||
378 | /* | ||
379 | * Note that the timer interrupts are also mapped, but this is | ||
380 | * done in sb1250_time_init(). Also, the profiling driver | ||
381 | * does its own management of IP7. | ||
382 | */ | ||
383 | |||
384 | #ifdef CONFIG_KGDB | ||
385 | imask |= STATUSF_IP6; | ||
386 | #endif | ||
387 | /* Enable necessary IPs, disable the rest */ | ||
388 | change_c0_status(ST0_IM, imask); | ||
389 | set_except_vector(0, sb1250_irq_handler); | ||
390 | |||
391 | #ifdef CONFIG_KGDB | ||
392 | if (kgdb_flag) { | ||
393 | kgdb_irq = K_INT_UART_0 + kgdb_port; | ||
394 | |||
395 | #ifdef CONFIG_SIBYTE_SB1250_DUART | ||
396 | sb1250_duart_present[kgdb_port] = 0; | ||
397 | #endif | ||
398 | /* Setup uart 1 settings, mapper */ | ||
399 | bus_writeq(M_DUART_IMR_BRK, IOADDR(A_DUART_IMRREG(kgdb_port))); | ||
400 | |||
401 | sb1250_steal_irq(kgdb_irq); | ||
402 | bus_writeq(IMR_IP6_VAL, | ||
403 | IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + | ||
404 | (kgdb_irq<<3))); | ||
405 | sb1250_unmask_irq(0, kgdb_irq); | ||
406 | } | ||
407 | #endif | ||
408 | } | ||
409 | |||
410 | #ifdef CONFIG_KGDB | ||
411 | |||
412 | #include <linux/delay.h> | ||
413 | |||
414 | #define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg))) | ||
415 | #define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg))) | ||
416 | |||
417 | void sb1250_kgdb_interrupt(struct pt_regs *regs) | ||
418 | { | ||
419 | /* | ||
420 | * Clear break-change status (allow some time for the remote | ||
421 | * host to stop the break, since we would see another | ||
422 | * interrupt on the end-of-break too) | ||
423 | */ | ||
424 | kstat_this_cpu.irqs[kgdb_irq]++; | ||
425 | mdelay(500); | ||
426 | duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT | | ||
427 | M_DUART_RX_EN | M_DUART_TX_EN); | ||
428 | set_async_breakpoint(®s->cp0_epc); | ||
429 | } | ||
430 | |||
431 | #endif /* CONFIG_KGDB */ | ||