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