diff options
Diffstat (limited to 'arch/mips/mti-malta/malta-int.c')
-rw-r--r-- | arch/mips/mti-malta/malta-int.c | 712 |
1 files changed, 712 insertions, 0 deletions
diff --git a/arch/mips/mti-malta/malta-int.c b/arch/mips/mti-malta/malta-int.c new file mode 100644 index 000000000000..ea176113fea9 --- /dev/null +++ b/arch/mips/mti-malta/malta-int.c | |||
@@ -0,0 +1,712 @@ | |||
1 | /* | ||
2 | * Carsten Langgaard, carstenl@mips.com | ||
3 | * Copyright (C) 2000, 2001, 2004 MIPS Technologies, Inc. | ||
4 | * Copyright (C) 2001 Ralf Baechle | ||
5 | * | ||
6 | * This program is free software; you can distribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License (Version 2) as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
13 | * for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
18 | * | ||
19 | * Routines for generic manipulation of the interrupts found on the MIPS | ||
20 | * Malta board. | ||
21 | * The interrupt controller is located in the South Bridge a PIIX4 device | ||
22 | * with two internal 82C95 interrupt controllers. | ||
23 | */ | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/irq.h> | ||
26 | #include <linux/sched.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/io.h> | ||
30 | #include <linux/kernel_stat.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/random.h> | ||
33 | |||
34 | #include <asm/traps.h> | ||
35 | #include <asm/i8259.h> | ||
36 | #include <asm/irq_cpu.h> | ||
37 | #include <asm/irq_regs.h> | ||
38 | #include <asm/mips-boards/malta.h> | ||
39 | #include <asm/mips-boards/maltaint.h> | ||
40 | #include <asm/mips-boards/piix4.h> | ||
41 | #include <asm/gt64120.h> | ||
42 | #include <asm/mips-boards/generic.h> | ||
43 | #include <asm/mips-boards/msc01_pci.h> | ||
44 | #include <asm/msc01_ic.h> | ||
45 | #include <asm/gic.h> | ||
46 | #include <asm/gcmpregs.h> | ||
47 | |||
48 | int gcmp_present = -1; | ||
49 | int gic_present; | ||
50 | static unsigned long _msc01_biu_base; | ||
51 | static unsigned long _gcmp_base; | ||
52 | static unsigned int ipi_map[NR_CPUS]; | ||
53 | |||
54 | static DEFINE_SPINLOCK(mips_irq_lock); | ||
55 | |||
56 | static inline int mips_pcibios_iack(void) | ||
57 | { | ||
58 | int irq; | ||
59 | u32 dummy; | ||
60 | |||
61 | /* | ||
62 | * Determine highest priority pending interrupt by performing | ||
63 | * a PCI Interrupt Acknowledge cycle. | ||
64 | */ | ||
65 | switch (mips_revision_sconid) { | ||
66 | case MIPS_REVISION_SCON_SOCIT: | ||
67 | case MIPS_REVISION_SCON_ROCIT: | ||
68 | case MIPS_REVISION_SCON_SOCITSC: | ||
69 | case MIPS_REVISION_SCON_SOCITSCP: | ||
70 | MSC_READ(MSC01_PCI_IACK, irq); | ||
71 | irq &= 0xff; | ||
72 | break; | ||
73 | case MIPS_REVISION_SCON_GT64120: | ||
74 | irq = GT_READ(GT_PCI0_IACK_OFS); | ||
75 | irq &= 0xff; | ||
76 | break; | ||
77 | case MIPS_REVISION_SCON_BONITO: | ||
78 | /* The following will generate a PCI IACK cycle on the | ||
79 | * Bonito controller. It's a little bit kludgy, but it | ||
80 | * was the easiest way to implement it in hardware at | ||
81 | * the given time. | ||
82 | */ | ||
83 | BONITO_PCIMAP_CFG = 0x20000; | ||
84 | |||
85 | /* Flush Bonito register block */ | ||
86 | dummy = BONITO_PCIMAP_CFG; | ||
87 | iob(); /* sync */ | ||
88 | |||
89 | irq = readl((u32 *)_pcictrl_bonito_pcicfg); | ||
90 | iob(); /* sync */ | ||
91 | irq &= 0xff; | ||
92 | BONITO_PCIMAP_CFG = 0; | ||
93 | break; | ||
94 | default: | ||
95 | printk(KERN_WARNING "Unknown system controller.\n"); | ||
96 | return -1; | ||
97 | } | ||
98 | return irq; | ||
99 | } | ||
100 | |||
101 | static inline int get_int(void) | ||
102 | { | ||
103 | unsigned long flags; | ||
104 | int irq; | ||
105 | spin_lock_irqsave(&mips_irq_lock, flags); | ||
106 | |||
107 | irq = mips_pcibios_iack(); | ||
108 | |||
109 | /* | ||
110 | * The only way we can decide if an interrupt is spurious | ||
111 | * is by checking the 8259 registers. This needs a spinlock | ||
112 | * on an SMP system, so leave it up to the generic code... | ||
113 | */ | ||
114 | |||
115 | spin_unlock_irqrestore(&mips_irq_lock, flags); | ||
116 | |||
117 | return irq; | ||
118 | } | ||
119 | |||
120 | static void malta_hw0_irqdispatch(void) | ||
121 | { | ||
122 | int irq; | ||
123 | |||
124 | irq = get_int(); | ||
125 | if (irq < 0) { | ||
126 | /* interrupt has already been cleared */ | ||
127 | return; | ||
128 | } | ||
129 | |||
130 | do_IRQ(MALTA_INT_BASE + irq); | ||
131 | } | ||
132 | |||
133 | static void malta_ipi_irqdispatch(void) | ||
134 | { | ||
135 | int irq; | ||
136 | |||
137 | irq = gic_get_int(); | ||
138 | if (irq < 0) | ||
139 | return; /* interrupt has already been cleared */ | ||
140 | |||
141 | do_IRQ(MIPS_GIC_IRQ_BASE + irq); | ||
142 | } | ||
143 | |||
144 | static void corehi_irqdispatch(void) | ||
145 | { | ||
146 | unsigned int intedge, intsteer, pcicmd, pcibadaddr; | ||
147 | unsigned int pcimstat, intisr, inten, intpol; | ||
148 | unsigned int intrcause, datalo, datahi; | ||
149 | struct pt_regs *regs = get_irq_regs(); | ||
150 | |||
151 | printk(KERN_EMERG "CoreHI interrupt, shouldn't happen, we die here!\n"); | ||
152 | printk(KERN_EMERG "epc : %08lx\nStatus: %08lx\n" | ||
153 | "Cause : %08lx\nbadVaddr : %08lx\n", | ||
154 | regs->cp0_epc, regs->cp0_status, | ||
155 | regs->cp0_cause, regs->cp0_badvaddr); | ||
156 | |||
157 | /* Read all the registers and then print them as there is a | ||
158 | problem with interspersed printk's upsetting the Bonito controller. | ||
159 | Do it for the others too. | ||
160 | */ | ||
161 | |||
162 | switch (mips_revision_sconid) { | ||
163 | case MIPS_REVISION_SCON_SOCIT: | ||
164 | case MIPS_REVISION_SCON_ROCIT: | ||
165 | case MIPS_REVISION_SCON_SOCITSC: | ||
166 | case MIPS_REVISION_SCON_SOCITSCP: | ||
167 | ll_msc_irq(); | ||
168 | break; | ||
169 | case MIPS_REVISION_SCON_GT64120: | ||
170 | intrcause = GT_READ(GT_INTRCAUSE_OFS); | ||
171 | datalo = GT_READ(GT_CPUERR_ADDRLO_OFS); | ||
172 | datahi = GT_READ(GT_CPUERR_ADDRHI_OFS); | ||
173 | printk(KERN_EMERG "GT_INTRCAUSE = %08x\n", intrcause); | ||
174 | printk(KERN_EMERG "GT_CPUERR_ADDR = %02x%08x\n", | ||
175 | datahi, datalo); | ||
176 | break; | ||
177 | case MIPS_REVISION_SCON_BONITO: | ||
178 | pcibadaddr = BONITO_PCIBADADDR; | ||
179 | pcimstat = BONITO_PCIMSTAT; | ||
180 | intisr = BONITO_INTISR; | ||
181 | inten = BONITO_INTEN; | ||
182 | intpol = BONITO_INTPOL; | ||
183 | intedge = BONITO_INTEDGE; | ||
184 | intsteer = BONITO_INTSTEER; | ||
185 | pcicmd = BONITO_PCICMD; | ||
186 | printk(KERN_EMERG "BONITO_INTISR = %08x\n", intisr); | ||
187 | printk(KERN_EMERG "BONITO_INTEN = %08x\n", inten); | ||
188 | printk(KERN_EMERG "BONITO_INTPOL = %08x\n", intpol); | ||
189 | printk(KERN_EMERG "BONITO_INTEDGE = %08x\n", intedge); | ||
190 | printk(KERN_EMERG "BONITO_INTSTEER = %08x\n", intsteer); | ||
191 | printk(KERN_EMERG "BONITO_PCICMD = %08x\n", pcicmd); | ||
192 | printk(KERN_EMERG "BONITO_PCIBADADDR = %08x\n", pcibadaddr); | ||
193 | printk(KERN_EMERG "BONITO_PCIMSTAT = %08x\n", pcimstat); | ||
194 | break; | ||
195 | } | ||
196 | |||
197 | die("CoreHi interrupt", regs); | ||
198 | } | ||
199 | |||
200 | static inline int clz(unsigned long x) | ||
201 | { | ||
202 | __asm__( | ||
203 | " .set push \n" | ||
204 | " .set mips32 \n" | ||
205 | " clz %0, %1 \n" | ||
206 | " .set pop \n" | ||
207 | : "=r" (x) | ||
208 | : "r" (x)); | ||
209 | |||
210 | return x; | ||
211 | } | ||
212 | |||
213 | /* | ||
214 | * Version of ffs that only looks at bits 12..15. | ||
215 | */ | ||
216 | static inline unsigned int irq_ffs(unsigned int pending) | ||
217 | { | ||
218 | #if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) | ||
219 | return -clz(pending) + 31 - CAUSEB_IP; | ||
220 | #else | ||
221 | unsigned int a0 = 7; | ||
222 | unsigned int t0; | ||
223 | |||
224 | t0 = pending & 0xf000; | ||
225 | t0 = t0 < 1; | ||
226 | t0 = t0 << 2; | ||
227 | a0 = a0 - t0; | ||
228 | pending = pending << t0; | ||
229 | |||
230 | t0 = pending & 0xc000; | ||
231 | t0 = t0 < 1; | ||
232 | t0 = t0 << 1; | ||
233 | a0 = a0 - t0; | ||
234 | pending = pending << t0; | ||
235 | |||
236 | t0 = pending & 0x8000; | ||
237 | t0 = t0 < 1; | ||
238 | /* t0 = t0 << 2; */ | ||
239 | a0 = a0 - t0; | ||
240 | /* pending = pending << t0; */ | ||
241 | |||
242 | return a0; | ||
243 | #endif | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * IRQs on the Malta board look basically (barring software IRQs which we | ||
248 | * don't use at all and all external interrupt sources are combined together | ||
249 | * on hardware interrupt 0 (MIPS IRQ 2)) like: | ||
250 | * | ||
251 | * MIPS IRQ Source | ||
252 | * -------- ------ | ||
253 | * 0 Software (ignored) | ||
254 | * 1 Software (ignored) | ||
255 | * 2 Combined hardware interrupt (hw0) | ||
256 | * 3 Hardware (ignored) | ||
257 | * 4 Hardware (ignored) | ||
258 | * 5 Hardware (ignored) | ||
259 | * 6 Hardware (ignored) | ||
260 | * 7 R4k timer (what we use) | ||
261 | * | ||
262 | * We handle the IRQ according to _our_ priority which is: | ||
263 | * | ||
264 | * Highest ---- R4k Timer | ||
265 | * Lowest ---- Combined hardware interrupt | ||
266 | * | ||
267 | * then we just return, if multiple IRQs are pending then we will just take | ||
268 | * another exception, big deal. | ||
269 | */ | ||
270 | |||
271 | asmlinkage void plat_irq_dispatch(void) | ||
272 | { | ||
273 | unsigned int pending = read_c0_cause() & read_c0_status() & ST0_IM; | ||
274 | int irq; | ||
275 | |||
276 | irq = irq_ffs(pending); | ||
277 | |||
278 | if (irq == MIPSCPU_INT_I8259A) | ||
279 | malta_hw0_irqdispatch(); | ||
280 | else if (gic_present && ((1 << irq) & ipi_map[smp_processor_id()])) | ||
281 | malta_ipi_irqdispatch(); | ||
282 | else if (irq >= 0) | ||
283 | do_IRQ(MIPS_CPU_IRQ_BASE + irq); | ||
284 | else | ||
285 | spurious_interrupt(); | ||
286 | } | ||
287 | |||
288 | #ifdef CONFIG_MIPS_MT_SMP | ||
289 | |||
290 | |||
291 | #define GIC_MIPS_CPU_IPI_RESCHED_IRQ 3 | ||
292 | #define GIC_MIPS_CPU_IPI_CALL_IRQ 4 | ||
293 | |||
294 | #define MIPS_CPU_IPI_RESCHED_IRQ 0 /* SW int 0 for resched */ | ||
295 | #define C_RESCHED C_SW0 | ||
296 | #define MIPS_CPU_IPI_CALL_IRQ 1 /* SW int 1 for resched */ | ||
297 | #define C_CALL C_SW1 | ||
298 | static int cpu_ipi_resched_irq, cpu_ipi_call_irq; | ||
299 | |||
300 | static void ipi_resched_dispatch(void) | ||
301 | { | ||
302 | do_IRQ(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_RESCHED_IRQ); | ||
303 | } | ||
304 | |||
305 | static void ipi_call_dispatch(void) | ||
306 | { | ||
307 | do_IRQ(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_CALL_IRQ); | ||
308 | } | ||
309 | |||
310 | static irqreturn_t ipi_resched_interrupt(int irq, void *dev_id) | ||
311 | { | ||
312 | return IRQ_HANDLED; | ||
313 | } | ||
314 | |||
315 | static irqreturn_t ipi_call_interrupt(int irq, void *dev_id) | ||
316 | { | ||
317 | smp_call_function_interrupt(); | ||
318 | |||
319 | return IRQ_HANDLED; | ||
320 | } | ||
321 | |||
322 | static struct irqaction irq_resched = { | ||
323 | .handler = ipi_resched_interrupt, | ||
324 | .flags = IRQF_DISABLED|IRQF_PERCPU, | ||
325 | .name = "IPI_resched" | ||
326 | }; | ||
327 | |||
328 | static struct irqaction irq_call = { | ||
329 | .handler = ipi_call_interrupt, | ||
330 | .flags = IRQF_DISABLED|IRQF_PERCPU, | ||
331 | .name = "IPI_call" | ||
332 | }; | ||
333 | #endif /* CONFIG_MIPS_MT_SMP */ | ||
334 | |||
335 | static struct irqaction i8259irq = { | ||
336 | .handler = no_action, | ||
337 | .name = "XT-PIC cascade" | ||
338 | }; | ||
339 | |||
340 | static struct irqaction corehi_irqaction = { | ||
341 | .handler = no_action, | ||
342 | .name = "CoreHi" | ||
343 | }; | ||
344 | |||
345 | static msc_irqmap_t __initdata msc_irqmap[] = { | ||
346 | {MSC01C_INT_TMR, MSC01_IRQ_EDGE, 0}, | ||
347 | {MSC01C_INT_PCI, MSC01_IRQ_LEVEL, 0}, | ||
348 | }; | ||
349 | static int __initdata msc_nr_irqs = ARRAY_SIZE(msc_irqmap); | ||
350 | |||
351 | static msc_irqmap_t __initdata msc_eicirqmap[] = { | ||
352 | {MSC01E_INT_SW0, MSC01_IRQ_LEVEL, 0}, | ||
353 | {MSC01E_INT_SW1, MSC01_IRQ_LEVEL, 0}, | ||
354 | {MSC01E_INT_I8259A, MSC01_IRQ_LEVEL, 0}, | ||
355 | {MSC01E_INT_SMI, MSC01_IRQ_LEVEL, 0}, | ||
356 | {MSC01E_INT_COREHI, MSC01_IRQ_LEVEL, 0}, | ||
357 | {MSC01E_INT_CORELO, MSC01_IRQ_LEVEL, 0}, | ||
358 | {MSC01E_INT_TMR, MSC01_IRQ_EDGE, 0}, | ||
359 | {MSC01E_INT_PCI, MSC01_IRQ_LEVEL, 0}, | ||
360 | {MSC01E_INT_PERFCTR, MSC01_IRQ_LEVEL, 0}, | ||
361 | {MSC01E_INT_CPUCTR, MSC01_IRQ_LEVEL, 0} | ||
362 | }; | ||
363 | |||
364 | static int __initdata msc_nr_eicirqs = ARRAY_SIZE(msc_eicirqmap); | ||
365 | |||
366 | #if defined(CONFIG_MIPS_MT_SMP) | ||
367 | /* | ||
368 | * This GIC specific tabular array defines the association between External | ||
369 | * Interrupts and CPUs/Core Interrupts. The nature of the External | ||
370 | * Interrupts is also defined here - polarity/trigger. | ||
371 | */ | ||
372 | static struct gic_intr_map gic_intr_map[] = { | ||
373 | { GIC_EXT_INTR(0), X, X, X, X, 0 }, | ||
374 | { GIC_EXT_INTR(1), X, X, X, X, 0 }, | ||
375 | { GIC_EXT_INTR(2), X, X, X, X, 0 }, | ||
376 | { GIC_EXT_INTR(3), 0, GIC_CPU_INT0, GIC_POL_POS, GIC_TRIG_LEVEL, 0 }, | ||
377 | { GIC_EXT_INTR(4), 0, GIC_CPU_INT1, GIC_POL_POS, GIC_TRIG_LEVEL, 0 }, | ||
378 | { GIC_EXT_INTR(5), 0, GIC_CPU_INT2, GIC_POL_POS, GIC_TRIG_LEVEL, 0 }, | ||
379 | { GIC_EXT_INTR(6), 0, GIC_CPU_INT3, GIC_POL_POS, GIC_TRIG_LEVEL, 0 }, | ||
380 | { GIC_EXT_INTR(7), 0, GIC_CPU_INT4, GIC_POL_POS, GIC_TRIG_LEVEL, 0 }, | ||
381 | { GIC_EXT_INTR(8), 0, GIC_CPU_INT3, GIC_POL_POS, GIC_TRIG_LEVEL, 0 }, | ||
382 | { GIC_EXT_INTR(9), 0, GIC_CPU_INT3, GIC_POL_POS, GIC_TRIG_LEVEL, 0 }, | ||
383 | { GIC_EXT_INTR(10), X, X, X, X, 0 }, | ||
384 | { GIC_EXT_INTR(11), X, X, X, X, 0 }, | ||
385 | { GIC_EXT_INTR(12), 0, GIC_CPU_INT3, GIC_POL_POS, GIC_TRIG_LEVEL, 0 }, | ||
386 | { GIC_EXT_INTR(13), 0, GIC_MAP_TO_NMI_MSK, GIC_POL_POS, GIC_TRIG_LEVEL, 0 }, | ||
387 | { GIC_EXT_INTR(14), 0, GIC_MAP_TO_NMI_MSK, GIC_POL_POS, GIC_TRIG_LEVEL, 0 }, | ||
388 | { GIC_EXT_INTR(15), X, X, X, X, 0 }, | ||
389 | { GIC_EXT_INTR(16), 0, GIC_CPU_INT1, GIC_POL_POS, GIC_TRIG_EDGE, 1 }, | ||
390 | { GIC_EXT_INTR(17), 0, GIC_CPU_INT2, GIC_POL_POS, GIC_TRIG_EDGE, 1 }, | ||
391 | { GIC_EXT_INTR(18), 1, GIC_CPU_INT1, GIC_POL_POS, GIC_TRIG_EDGE, 1 }, | ||
392 | { GIC_EXT_INTR(19), 1, GIC_CPU_INT2, GIC_POL_POS, GIC_TRIG_EDGE, 1 }, | ||
393 | { GIC_EXT_INTR(20), 2, GIC_CPU_INT1, GIC_POL_POS, GIC_TRIG_EDGE, 1 }, | ||
394 | { GIC_EXT_INTR(21), 2, GIC_CPU_INT2, GIC_POL_POS, GIC_TRIG_EDGE, 1 }, | ||
395 | { GIC_EXT_INTR(22), 3, GIC_CPU_INT1, GIC_POL_POS, GIC_TRIG_EDGE, 1 }, | ||
396 | { GIC_EXT_INTR(23), 3, GIC_CPU_INT2, GIC_POL_POS, GIC_TRIG_EDGE, 1 }, | ||
397 | }; | ||
398 | #endif | ||
399 | |||
400 | /* | ||
401 | * GCMP needs to be detected before any SMP initialisation | ||
402 | */ | ||
403 | static int __init gcmp_probe(unsigned long addr, unsigned long size) | ||
404 | { | ||
405 | if (gcmp_present >= 0) | ||
406 | return gcmp_present; | ||
407 | |||
408 | _gcmp_base = (unsigned long) ioremap_nocache(GCMP_BASE_ADDR, GCMP_ADDRSPACE_SZ); | ||
409 | _msc01_biu_base = (unsigned long) ioremap_nocache(MSC01_BIU_REG_BASE, MSC01_BIU_ADDRSPACE_SZ); | ||
410 | gcmp_present = (GCMPGCB(GCMPB) & GCMP_GCB_GCMPB_GCMPBASE_MSK) == GCMP_BASE_ADDR; | ||
411 | |||
412 | if (gcmp_present) | ||
413 | printk(KERN_DEBUG "GCMP present\n"); | ||
414 | return gcmp_present; | ||
415 | } | ||
416 | |||
417 | #if defined(CONFIG_MIPS_MT_SMP) | ||
418 | static void __init fill_ipi_map(void) | ||
419 | { | ||
420 | int i; | ||
421 | |||
422 | for (i = 0; i < ARRAY_SIZE(gic_intr_map); i++) { | ||
423 | if (gic_intr_map[i].ipiflag && (gic_intr_map[i].cpunum != X)) | ||
424 | ipi_map[gic_intr_map[i].cpunum] |= | ||
425 | (1 << (gic_intr_map[i].pin + 2)); | ||
426 | } | ||
427 | } | ||
428 | #endif | ||
429 | |||
430 | void __init arch_init_irq(void) | ||
431 | { | ||
432 | int gic_present, gcmp_present; | ||
433 | |||
434 | init_i8259_irqs(); | ||
435 | |||
436 | if (!cpu_has_veic) | ||
437 | mips_cpu_irq_init(); | ||
438 | |||
439 | gcmp_present = gcmp_probe(GCMP_BASE_ADDR, GCMP_ADDRSPACE_SZ); | ||
440 | if (gcmp_present) { | ||
441 | GCMPGCB(GICBA) = GIC_BASE_ADDR | GCMP_GCB_GICBA_EN_MSK; | ||
442 | gic_present = 1; | ||
443 | } else { | ||
444 | _msc01_biu_base = (unsigned long) ioremap_nocache(MSC01_BIU_REG_BASE, MSC01_BIU_ADDRSPACE_SZ); | ||
445 | gic_present = (REG(_msc01_biu_base, MSC01_SC_CFG) & | ||
446 | MSC01_SC_CFG_GICPRES_MSK) >> MSC01_SC_CFG_GICPRES_SHF; | ||
447 | } | ||
448 | if (gic_present) | ||
449 | printk(KERN_DEBUG "GIC present\n"); | ||
450 | |||
451 | switch (mips_revision_sconid) { | ||
452 | case MIPS_REVISION_SCON_SOCIT: | ||
453 | case MIPS_REVISION_SCON_ROCIT: | ||
454 | if (cpu_has_veic) | ||
455 | init_msc_irqs(MIPS_MSC01_IC_REG_BASE, | ||
456 | MSC01E_INT_BASE, msc_eicirqmap, | ||
457 | msc_nr_eicirqs); | ||
458 | else | ||
459 | init_msc_irqs(MIPS_MSC01_IC_REG_BASE, | ||
460 | MSC01C_INT_BASE, msc_irqmap, | ||
461 | msc_nr_irqs); | ||
462 | break; | ||
463 | |||
464 | case MIPS_REVISION_SCON_SOCITSC: | ||
465 | case MIPS_REVISION_SCON_SOCITSCP: | ||
466 | if (cpu_has_veic) | ||
467 | init_msc_irqs(MIPS_SOCITSC_IC_REG_BASE, | ||
468 | MSC01E_INT_BASE, msc_eicirqmap, | ||
469 | msc_nr_eicirqs); | ||
470 | else | ||
471 | init_msc_irqs(MIPS_SOCITSC_IC_REG_BASE, | ||
472 | MSC01C_INT_BASE, msc_irqmap, | ||
473 | msc_nr_irqs); | ||
474 | } | ||
475 | |||
476 | if (cpu_has_veic) { | ||
477 | set_vi_handler(MSC01E_INT_I8259A, malta_hw0_irqdispatch); | ||
478 | set_vi_handler(MSC01E_INT_COREHI, corehi_irqdispatch); | ||
479 | setup_irq(MSC01E_INT_BASE+MSC01E_INT_I8259A, &i8259irq); | ||
480 | setup_irq(MSC01E_INT_BASE+MSC01E_INT_COREHI, &corehi_irqaction); | ||
481 | } else if (cpu_has_vint) { | ||
482 | set_vi_handler(MIPSCPU_INT_I8259A, malta_hw0_irqdispatch); | ||
483 | set_vi_handler(MIPSCPU_INT_COREHI, corehi_irqdispatch); | ||
484 | #ifdef CONFIG_MIPS_MT_SMTC | ||
485 | setup_irq_smtc(MIPS_CPU_IRQ_BASE+MIPSCPU_INT_I8259A, &i8259irq, | ||
486 | (0x100 << MIPSCPU_INT_I8259A)); | ||
487 | setup_irq_smtc(MIPS_CPU_IRQ_BASE+MIPSCPU_INT_COREHI, | ||
488 | &corehi_irqaction, (0x100 << MIPSCPU_INT_COREHI)); | ||
489 | /* | ||
490 | * Temporary hack to ensure that the subsidiary device | ||
491 | * interrupts coing in via the i8259A, but associated | ||
492 | * with low IRQ numbers, will restore the Status.IM | ||
493 | * value associated with the i8259A. | ||
494 | */ | ||
495 | { | ||
496 | int i; | ||
497 | |||
498 | for (i = 0; i < 16; i++) | ||
499 | irq_hwmask[i] = (0x100 << MIPSCPU_INT_I8259A); | ||
500 | } | ||
501 | #else /* Not SMTC */ | ||
502 | setup_irq(MIPS_CPU_IRQ_BASE+MIPSCPU_INT_I8259A, &i8259irq); | ||
503 | setup_irq(MIPS_CPU_IRQ_BASE+MIPSCPU_INT_COREHI, | ||
504 | &corehi_irqaction); | ||
505 | #endif /* CONFIG_MIPS_MT_SMTC */ | ||
506 | } else { | ||
507 | setup_irq(MIPS_CPU_IRQ_BASE+MIPSCPU_INT_I8259A, &i8259irq); | ||
508 | setup_irq(MIPS_CPU_IRQ_BASE+MIPSCPU_INT_COREHI, | ||
509 | &corehi_irqaction); | ||
510 | } | ||
511 | |||
512 | #if defined(CONFIG_MIPS_MT_SMP) | ||
513 | if (gic_present) { | ||
514 | /* FIXME */ | ||
515 | int i; | ||
516 | struct { | ||
517 | unsigned int resched; | ||
518 | unsigned int call; | ||
519 | } ipiirq[] = { | ||
520 | { | ||
521 | .resched = GIC_IPI_EXT_INTR_RESCHED_VPE0, | ||
522 | .call = GIC_IPI_EXT_INTR_CALLFNC_VPE0}, | ||
523 | { | ||
524 | .resched = GIC_IPI_EXT_INTR_RESCHED_VPE1, | ||
525 | .call = GIC_IPI_EXT_INTR_CALLFNC_VPE1 | ||
526 | }, { | ||
527 | .resched = GIC_IPI_EXT_INTR_RESCHED_VPE2, | ||
528 | .call = GIC_IPI_EXT_INTR_CALLFNC_VPE2 | ||
529 | }, { | ||
530 | .resched = GIC_IPI_EXT_INTR_RESCHED_VPE3, | ||
531 | .call = GIC_IPI_EXT_INTR_CALLFNC_VPE3 | ||
532 | } | ||
533 | }; | ||
534 | fill_ipi_map(); | ||
535 | gic_init(GIC_BASE_ADDR, GIC_ADDRSPACE_SZ, gic_intr_map, ARRAY_SIZE(gic_intr_map), MIPS_GIC_IRQ_BASE); | ||
536 | if (!gcmp_present) { | ||
537 | /* Enable the GIC */ | ||
538 | i = REG(_msc01_biu_base, MSC01_SC_CFG); | ||
539 | REG(_msc01_biu_base, MSC01_SC_CFG) = | ||
540 | (i | (0x1 << MSC01_SC_CFG_GICENA_SHF)); | ||
541 | pr_debug("GIC Enabled\n"); | ||
542 | } | ||
543 | |||
544 | /* set up ipi interrupts */ | ||
545 | if (cpu_has_vint) { | ||
546 | set_vi_handler(MIPSCPU_INT_IPI0, malta_ipi_irqdispatch); | ||
547 | set_vi_handler(MIPSCPU_INT_IPI1, malta_ipi_irqdispatch); | ||
548 | } | ||
549 | /* Argh.. this really needs sorting out.. */ | ||
550 | printk("CPU%d: status register was %08x\n", smp_processor_id(), read_c0_status()); | ||
551 | write_c0_status(read_c0_status() | STATUSF_IP3 | STATUSF_IP4); | ||
552 | printk("CPU%d: status register now %08x\n", smp_processor_id(), read_c0_status()); | ||
553 | write_c0_status(0x1100dc00); | ||
554 | printk("CPU%d: status register frc %08x\n", smp_processor_id(), read_c0_status()); | ||
555 | for (i = 0; i < ARRAY_SIZE(ipiirq); i++) { | ||
556 | setup_irq(MIPS_GIC_IRQ_BASE + ipiirq[i].resched, &irq_resched); | ||
557 | setup_irq(MIPS_GIC_IRQ_BASE + ipiirq[i].call, &irq_call); | ||
558 | |||
559 | set_irq_handler(MIPS_GIC_IRQ_BASE + ipiirq[i].resched, handle_percpu_irq); | ||
560 | set_irq_handler(MIPS_GIC_IRQ_BASE + ipiirq[i].call, handle_percpu_irq); | ||
561 | } | ||
562 | } else { | ||
563 | /* set up ipi interrupts */ | ||
564 | if (cpu_has_veic) { | ||
565 | set_vi_handler (MSC01E_INT_SW0, ipi_resched_dispatch); | ||
566 | set_vi_handler (MSC01E_INT_SW1, ipi_call_dispatch); | ||
567 | cpu_ipi_resched_irq = MSC01E_INT_SW0; | ||
568 | cpu_ipi_call_irq = MSC01E_INT_SW1; | ||
569 | } else { | ||
570 | if (cpu_has_vint) { | ||
571 | set_vi_handler (MIPS_CPU_IPI_RESCHED_IRQ, ipi_resched_dispatch); | ||
572 | set_vi_handler (MIPS_CPU_IPI_CALL_IRQ, ipi_call_dispatch); | ||
573 | } | ||
574 | cpu_ipi_resched_irq = MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_RESCHED_IRQ; | ||
575 | cpu_ipi_call_irq = MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_CALL_IRQ; | ||
576 | } | ||
577 | |||
578 | setup_irq(cpu_ipi_resched_irq, &irq_resched); | ||
579 | setup_irq(cpu_ipi_call_irq, &irq_call); | ||
580 | |||
581 | set_irq_handler(cpu_ipi_resched_irq, handle_percpu_irq); | ||
582 | set_irq_handler(cpu_ipi_call_irq, handle_percpu_irq); | ||
583 | } | ||
584 | #endif | ||
585 | } | ||
586 | |||
587 | void malta_be_init(void) | ||
588 | { | ||
589 | if (gcmp_present) { | ||
590 | /* Could change CM error mask register */ | ||
591 | } | ||
592 | } | ||
593 | |||
594 | |||
595 | static char *tr[8] = { | ||
596 | "mem", "gcr", "gic", "mmio", | ||
597 | "0x04", "0x05", "0x06", "0x07" | ||
598 | }; | ||
599 | |||
600 | static char *mcmd[32] = { | ||
601 | [0x00] = "0x00", | ||
602 | [0x01] = "Legacy Write", | ||
603 | [0x02] = "Legacy Read", | ||
604 | [0x03] = "0x03", | ||
605 | [0x04] = "0x04", | ||
606 | [0x05] = "0x05", | ||
607 | [0x06] = "0x06", | ||
608 | [0x07] = "0x07", | ||
609 | [0x08] = "Coherent Read Own", | ||
610 | [0x09] = "Coherent Read Share", | ||
611 | [0x0a] = "Coherent Read Discard", | ||
612 | [0x0b] = "Coherent Ready Share Always", | ||
613 | [0x0c] = "Coherent Upgrade", | ||
614 | [0x0d] = "Coherent Writeback", | ||
615 | [0x0e] = "0x0e", | ||
616 | [0x0f] = "0x0f", | ||
617 | [0x10] = "Coherent Copyback", | ||
618 | [0x11] = "Coherent Copyback Invalidate", | ||
619 | [0x12] = "Coherent Invalidate", | ||
620 | [0x13] = "Coherent Write Invalidate", | ||
621 | [0x14] = "Coherent Completion Sync", | ||
622 | [0x15] = "0x15", | ||
623 | [0x16] = "0x16", | ||
624 | [0x17] = "0x17", | ||
625 | [0x18] = "0x18", | ||
626 | [0x19] = "0x19", | ||
627 | [0x1a] = "0x1a", | ||
628 | [0x1b] = "0x1b", | ||
629 | [0x1c] = "0x1c", | ||
630 | [0x1d] = "0x1d", | ||
631 | [0x1e] = "0x1e", | ||
632 | [0x1f] = "0x1f" | ||
633 | }; | ||
634 | |||
635 | static char *core[8] = { | ||
636 | "Invalid/OK", "Invalid/Data", | ||
637 | "Shared/OK", "Shared/Data", | ||
638 | "Modified/OK", "Modified/Data", | ||
639 | "Exclusive/OK", "Exclusive/Data" | ||
640 | }; | ||
641 | |||
642 | static char *causes[32] = { | ||
643 | "None", "GC_WR_ERR", "GC_RD_ERR", "COH_WR_ERR", | ||
644 | "COH_RD_ERR", "MMIO_WR_ERR", "MMIO_RD_ERR", "0x07", | ||
645 | "0x08", "0x09", "0x0a", "0x0b", | ||
646 | "0x0c", "0x0d", "0x0e", "0x0f", | ||
647 | "0x10", "0x11", "0x12", "0x13", | ||
648 | "0x14", "0x15", "0x16", "INTVN_WR_ERR", | ||
649 | "INTVN_RD_ERR", "0x19", "0x1a", "0x1b", | ||
650 | "0x1c", "0x1d", "0x1e", "0x1f" | ||
651 | }; | ||
652 | |||
653 | int malta_be_handler(struct pt_regs *regs, int is_fixup) | ||
654 | { | ||
655 | /* This duplicates the handling in do_be which seems wrong */ | ||
656 | int retval = is_fixup ? MIPS_BE_FIXUP : MIPS_BE_FATAL; | ||
657 | |||
658 | if (gcmp_present) { | ||
659 | unsigned long cm_error = GCMPGCB(GCMEC); | ||
660 | unsigned long cm_addr = GCMPGCB(GCMEA); | ||
661 | unsigned long cm_other = GCMPGCB(GCMEO); | ||
662 | unsigned long cause, ocause; | ||
663 | char buf[256]; | ||
664 | |||
665 | cause = (cm_error & GCMP_GCB_GMEC_ERROR_TYPE_MSK); | ||
666 | if (cause != 0) { | ||
667 | cause >>= GCMP_GCB_GMEC_ERROR_TYPE_SHF; | ||
668 | if (cause < 16) { | ||
669 | unsigned long cca_bits = (cm_error >> 15) & 7; | ||
670 | unsigned long tr_bits = (cm_error >> 12) & 7; | ||
671 | unsigned long mcmd_bits = (cm_error >> 7) & 0x1f; | ||
672 | unsigned long stag_bits = (cm_error >> 3) & 15; | ||
673 | unsigned long sport_bits = (cm_error >> 0) & 7; | ||
674 | |||
675 | snprintf(buf, sizeof(buf), | ||
676 | "CCA=%lu TR=%s MCmd=%s STag=%lu " | ||
677 | "SPort=%lu\n", | ||
678 | cca_bits, tr[tr_bits], mcmd[mcmd_bits], | ||
679 | stag_bits, sport_bits); | ||
680 | } else { | ||
681 | /* glob state & sresp together */ | ||
682 | unsigned long c3_bits = (cm_error >> 18) & 7; | ||
683 | unsigned long c2_bits = (cm_error >> 15) & 7; | ||
684 | unsigned long c1_bits = (cm_error >> 12) & 7; | ||
685 | unsigned long c0_bits = (cm_error >> 9) & 7; | ||
686 | unsigned long sc_bit = (cm_error >> 8) & 1; | ||
687 | unsigned long mcmd_bits = (cm_error >> 3) & 0x1f; | ||
688 | unsigned long sport_bits = (cm_error >> 0) & 7; | ||
689 | snprintf(buf, sizeof(buf), | ||
690 | "C3=%s C2=%s C1=%s C0=%s SC=%s " | ||
691 | "MCmd=%s SPort=%lu\n", | ||
692 | core[c3_bits], core[c2_bits], | ||
693 | core[c1_bits], core[c0_bits], | ||
694 | sc_bit ? "True" : "False", | ||
695 | mcmd[mcmd_bits], sport_bits); | ||
696 | } | ||
697 | |||
698 | ocause = (cm_other & GCMP_GCB_GMEO_ERROR_2ND_MSK) >> | ||
699 | GCMP_GCB_GMEO_ERROR_2ND_SHF; | ||
700 | |||
701 | printk("CM_ERROR=%08lx %s <%s>\n", cm_error, | ||
702 | causes[cause], buf); | ||
703 | printk("CM_ADDR =%08lx\n", cm_addr); | ||
704 | printk("CM_OTHER=%08lx %s\n", cm_other, causes[ocause]); | ||
705 | |||
706 | /* reprime cause register */ | ||
707 | GCMPGCB(GCMEC) = 0; | ||
708 | } | ||
709 | } | ||
710 | |||
711 | return retval; | ||
712 | } | ||