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