1 /***********************license start***************
2 * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * * Neither the name of Cavium Networks nor the names of
19 * its contributors may be used to endorse or promote products
20 * derived from this software without specific prior written
23 * This Software, including technical data, may be subject to U.S. export control
24 * laws, including the U.S. Export Administration Act and its associated
25 * regulations, and may be subject to export or import regulations in other
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
49 * Interface to the Mips interrupts.
51 * <hr>$Revision: 52004 $<hr>
55 /* Backtrace is only available with the new toolchain. */
58 #endif /* __U_BOOT__ */
59 #include "cvmx-config.h"
61 #include "cvmx-interrupt.h"
62 #include "cvmx-sysinfo.h"
63 #include "cvmx-uart.h"
65 #include "cvmx-ebt3000.h"
66 #include "cvmx-coremask.h"
67 #include "cvmx-spinlock.h"
68 #include "cvmx-app-init.h"
69 #include "cvmx-error.h"
70 #include "../../bootloader/u-boot/include/octeon_mem_map.h"
72 EXTERN_ASM void cvmx_interrupt_stage1(void);
73 EXTERN_ASM void cvmx_debug_handler_stage1(void);
74 EXTERN_ASM void cvmx_interrupt_cache_error(void);
76 int cvmx_interrupt_in_isr = 0;
79 * Internal status the interrupt registration
83 cvmx_interrupt_func_t handlers[256]; /**< One function to call per interrupt */
84 void * data[256]; /**< User data per interrupt */
85 cvmx_interrupt_exception_t exception_handler;
86 } cvmx_interrupt_state_t;
89 * Internal state the interrupt registration
92 static CVMX_SHARED cvmx_interrupt_state_t cvmx_interrupt_state;
93 static CVMX_SHARED cvmx_spinlock_t cvmx_interrupt_default_lock;
94 #endif /* __U_BOOT__ */
96 #define ULL unsigned long long
98 #define HI32(data64) ((uint32_t)(data64 >> 32))
99 #define LO32(data64) ((uint32_t)(data64 & 0xFFFFFFFF))
101 static const char reg_names[][32] = { "r0","at","v0","v1","a0","a1","a2","a3",
102 "t0","t1","t2","t3","t4","t5","t6","t7",
103 "s0","s1","s2","s3","s4","s5", "s6","s7",
104 "t8","t9", "k0","k1","gp","sp","s8","ra" };
107 * version of printf that works better in exception context.
111 void cvmx_safe_printf(const char *format, ...)
118 va_start(args, format);
120 count = vsnprintf(buffer, sizeof(buffer), format, args);
122 count = vsprintf(buffer, format, args);
128 cvmx_uart_lsr_t lsrval;
130 /* Spin until there is room */
133 lsrval.u64 = cvmx_read_csr(CVMX_MIO_UARTX_LSR(0));
134 #if !defined(CONFIG_OCTEON_SIM_SPEED)
135 if (lsrval.s.temt == 0)
136 cvmx_wait(10000); /* Just to reduce the load on the system */
139 while (lsrval.s.temt == 0);
142 cvmx_write_csr(CVMX_MIO_UARTX_THR(0), '\r');
143 cvmx_write_csr(CVMX_MIO_UARTX_THR(0), *ptr++);
147 /* Textual descriptions of cause codes */
148 static const char cause_names[][128] = {
150 /* 1 */ "TLB modification",
151 /* 2 */ "tlb load/fetch",
153 /* 4 */ "address exc, load/fetch",
154 /* 5 */ "address exc, store",
155 /* 6 */ "bus error, instruction fetch",
156 /* 7 */ "bus error, load/store",
158 /* 9 */ "breakpoint",
159 /* 10 */ "reserved instruction",
160 /* 11 */ "cop unusable",
161 /* 12 */ "arithmetic overflow",
164 /* 15 */ "floating point exc",
167 /* 18 */ "cop2 exception",
171 /* 22 */ "mdmx unusable",
173 /* 24 */ "machine check",
179 /* 30 */ "cache error",
186 * @param name Name of the value to print
187 * @param reg Value to print
189 static inline void print_reg64(const char *name, uint64_t reg)
191 cvmx_safe_printf("%16s: 0x%08x%08x\n", name, (unsigned int)HI32(reg),(unsigned int)LO32(reg));
196 * Dump all useful registers to the console
198 * @param registers CPU register to dump
200 static void __cvmx_interrupt_dump_registers(uint64_t registers[32])
204 for (reg=0; reg<16; reg++)
206 r1 = registers[reg]; r2 = registers[reg+16];
207 cvmx_safe_printf("%3s ($%02d): 0x%08x%08x \t %3s ($%02d): 0x%08x%08x\n",
208 reg_names[reg], reg, (unsigned int)HI32(r1), (unsigned int)LO32(r1),
209 reg_names[reg+16], reg+16, (unsigned int)HI32(r2), (unsigned int)LO32(r2));
211 CVMX_MF_COP0 (r1, COP0_CAUSE);
212 print_reg64 ("COP0_CAUSE", r1);
213 CVMX_MF_COP0 (r2, COP0_STATUS);
214 print_reg64 ("COP0_STATUS", r2);
215 CVMX_MF_COP0 (r1, COP0_BADVADDR);
216 print_reg64 ("COP0_BADVADDR", r1);
217 CVMX_MF_COP0 (r2, COP0_EPC);
218 print_reg64 ("COP0_EPC", r2);
223 * Default exception handler. Prints out the exception
224 * cause decode and all relevant registers.
226 * @param registers Registers at time of the exception
230 #endif /* __U_BOOT__ */
231 void __cvmx_interrupt_default_exception_handler(uint64_t registers[32])
233 uint64_t trap_print_cause;
237 ebt3000_str_write("Trap");
238 cvmx_spinlock_lock(&cvmx_interrupt_default_lock);
240 CVMX_MF_COP0 (trap_print_cause, COP0_CAUSE);
241 str = cause_names [(trap_print_cause >> 2) & 0x1f];
242 cvmx_safe_printf("Core %d: Unhandled Exception. Cause register decodes to:\n%s\n", (int)cvmx_get_core_num(), str && *str ? str : "Reserved exception cause");
243 cvmx_safe_printf("******************************************************************\n");
244 __cvmx_interrupt_dump_registers(registers);
248 cvmx_safe_printf("******************************************************************\n");
249 #if __GNUC__ >= 4 && !defined(OCTEON_DISABLE_BACKTRACE)
250 cvmx_safe_printf("Backtrace:\n\n");
251 __octeon_print_backtrace_func ((__octeon_backtrace_printf_t)cvmx_safe_printf);
252 cvmx_safe_printf("******************************************************************\n");
255 cvmx_spinlock_unlock(&cvmx_interrupt_default_lock);
257 if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
262 /* Interrupts are suppressed when we are in the exception
263 handler (because of SR[EXL]). Spin and poll the uart
264 status and see if the debugger is trying to stop us. */
265 cvmx_uart_lsr_t lsrval;
266 lsrval.u64 = cvmx_read_csr(CVMX_MIO_UARTX_LSR(cvmx_debug_uart));
270 /* Pulse the MCD0 signal. */
282 #endif /* __U_BOOT__ */
288 * Default interrupt handler if the user doesn't register one.
290 * @param irq_number IRQ that caused this interrupt
291 * @param registers Register at the time of the interrupt
292 * @param user_arg Unused optional user data
294 static void __cvmx_interrupt_default(int irq_number, uint64_t registers[32], void *user_arg)
296 cvmx_safe_printf("cvmx_interrupt_default: Received interrupt %d\n", irq_number);
297 __cvmx_interrupt_dump_registers(registers);
303 * Handler for interrupt lines 2 and 3. These are directly tied
304 * to the CIU. The handler queres the status of the CIU and
305 * calls the secondary handler for the CIU interrupt that
308 * @param irq_number Interrupt number that fired (2 or 3)
309 * @param registers Registers at the time of the interrupt
310 * @param user_arg Unused user argument
312 static void __cvmx_interrupt_ciu(int irq_number, uint64_t registers[32], void *user_arg)
314 int ciu_offset = cvmx_get_core_num() * 2 + irq_number - 2;
315 uint64_t irq_mask = cvmx_read_csr(CVMX_CIU_INTX_SUM0(ciu_offset)) & cvmx_read_csr(CVMX_CIU_INTX_EN0(ciu_offset));
318 /* Handle EN0 sources */
323 cvmx_interrupt_state.handlers[irq](irq, registers, cvmx_interrupt_state.data[irq]);
326 irq_mask = irq_mask >> 1;
330 /* Handle EN1 sources */
331 irq_mask = cvmx_read_csr(CVMX_CIU_INT_SUM1) & cvmx_read_csr(CVMX_CIU_INTX_EN1(ciu_offset));
337 cvmx_interrupt_state.handlers[irq](irq, registers, cvmx_interrupt_state.data[irq]);
340 irq_mask = irq_mask >> 1;
348 * Called for all RML interrupts. This is usually an ECC error
350 * @param irq_number Interrupt number that we're being called for
351 * @param registers Registers at the time of the interrupt
352 * @param user_arg Unused user argument
354 static void __cvmx_interrupt_ecc(int irq_number, uint64_t registers[32], void *user_arg)
361 * Process an interrupt request
363 * @param registers Registers at time of interrupt / exception
364 * Registers 0-31 are standard MIPS, others specific to this routine
367 EXTERN_ASM void cvmx_interrupt_do_irq(uint64_t registers[35]);
368 void cvmx_interrupt_do_irq(uint64_t registers[35])
376 /* Determine the cause of the interrupt */
377 asm volatile ("dmfc0 %0,$13,0" : "=r" (cause));
378 asm volatile ("dmfc0 %0,$12,0" : "=r" (status));
379 /* In case of exception, clear all interrupts to avoid recursive interrupts.
380 Also clear EXL bit to display the correct PC value. */
381 if ((cause & 0x7c) == 0)
383 asm volatile ("dmtc0 %0, $12, 0" : : "r" (status & ~(0xff02)));
385 /* The assembly stub at each exception vector saves its address in k1 when
386 ** it calls the stage 2 handler. We use this to compute the exception vector
387 ** that brought us here */
388 exc_vec = (uint32_t)(registers[27] & 0x780); /* Mask off bits we need to ignore */
390 /* Check for cache errors. The cache errors go to a separate exception vector,
391 ** so we will only check these if we got here from a cache error exception, and
392 ** the ERL (error level) bit is set. */
393 if (exc_vec == 0x100 && (status & 0x4))
395 i = cvmx_get_core_num();
396 CVMX_MF_CACHE_ERR(cache_err);
398 /* Use copy of DCACHE_ERR register that early exception stub read */
399 if (registers[34] & 0x1)
401 cvmx_safe_printf("Dcache error detected: core: %d, set: %d, va 6:3: 0x%x\n", i, (int)(cache_err >> 3) & 0x3, (int)(cache_err >> 3) & 0xf);
402 uint64_t dcache_err = 0;
403 CVMX_MT_DCACHE_ERR(dcache_err);
405 else if (cache_err & 0x1)
407 cvmx_safe_printf("Icache error detected: core: %d, set: %d, way : %d\n", i, (int)(cache_err >> 5) & 0x3f, (int)(cache_err >> 7) & 0x3);
409 CVMX_MT_CACHE_ERR(cache_err);
412 cvmx_safe_printf("Cache error exception: core %d\n", i);
415 if ((cause & 0x7c) != 0)
417 cvmx_interrupt_state.exception_handler(registers);
418 goto return_from_interrupt;
421 /* Convert the cause into an active mask */
422 mask = ((cause & status) >> 8) & 0xff;
425 goto return_from_interrupt; /* Spurious interrupt */
432 cvmx_interrupt_state.handlers[i](i, registers, cvmx_interrupt_state.data[i]);
433 goto return_from_interrupt;
437 /* We should never get here */
438 __cvmx_interrupt_default_exception_handler(registers);
440 return_from_interrupt:
441 /* Restore Status register before returning from exception. */
442 asm volatile ("dmtc0 %0, $12, 0" : : "r" (status));
447 * Initialize the interrupt routine and copy the low level
448 * stub into the correct interrupt vector. This is called
449 * automatically during application startup.
451 void cvmx_interrupt_initialize(void)
454 cvmx_sysinfo_t *sys_info_ptr = cvmx_sysinfo_get();
457 /* Disable all CIU interrupts by default */
458 cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), 0);
459 cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2+1), 0);
460 cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), 0);
461 cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2+1), 0);
463 if (cvmx_coremask_first_core(sys_info_ptr->core_mask))
465 cvmx_interrupt_state.exception_handler = __cvmx_interrupt_default_exception_handler;
467 for (i=0; i<256; i++)
469 cvmx_interrupt_state.handlers[i] = __cvmx_interrupt_default;
470 cvmx_interrupt_state.data[i] = NULL;
473 low_level_loc = CASTPTR(void, CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0,sys_info_ptr->exception_base_addr));
474 memcpy(low_level_loc + 0x80, (void*)cvmx_interrupt_stage1, 0x80);
475 memcpy(low_level_loc + 0x100, (void*)cvmx_interrupt_cache_error, 0x80);
476 memcpy(low_level_loc + 0x180, (void*)cvmx_interrupt_stage1, 0x80);
477 memcpy(low_level_loc + 0x200, (void*)cvmx_interrupt_stage1, 0x80);
479 /* Make sure the locations used to count Icache and Dcache exceptions
480 starts out as zero */
481 cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 8), 0);
482 cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 16), 0);
483 cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 24), 0);
486 /* Add an interrupt handlers for chained CIU interrupts */
487 cvmx_interrupt_register(CVMX_IRQ_CIU0, __cvmx_interrupt_ciu, NULL);
488 cvmx_interrupt_register(CVMX_IRQ_CIU1, __cvmx_interrupt_ciu, NULL);
490 /* Add an interrupt handler for ECC failures */
491 cvmx_interrupt_register(CVMX_IRQ_RML, __cvmx_interrupt_ecc, NULL);
493 if (cvmx_error_initialize(0 /* || CVMX_ERROR_FLAGS_ECC_SINGLE_BIT */))
494 cvmx_warn("cvmx_error_initialize() failed\n");
495 cvmx_interrupt_unmask_irq(CVMX_IRQ_RML);
498 cvmx_interrupt_unmask_irq(CVMX_IRQ_CIU0);
499 cvmx_interrupt_unmask_irq(CVMX_IRQ_CIU1);
500 CVMX_ICACHE_INVALIDATE;
502 /* Enable interrupts for each core (bit0 of COP0 Status) */
513 * Register an interrupt handler for the specified interrupt number.
515 * @param irq_number Interrupt number to register for (0-135) See
516 * cvmx-interrupt.h for enumeration and description of sources.
517 * @param func Function to call on interrupt.
518 * @param user_arg User data to pass to the interrupt handler
520 void cvmx_interrupt_register(cvmx_irq_t irq_number, cvmx_interrupt_func_t func, void *user_arg)
522 cvmx_interrupt_state.handlers[irq_number] = func;
523 cvmx_interrupt_state.data[irq_number] = user_arg;
529 * Set the exception handler for all non interrupt sources.
531 * @param handler New exception handler
532 * @return Old exception handler
534 cvmx_interrupt_exception_t cvmx_interrupt_set_exception(cvmx_interrupt_exception_t handler)
536 cvmx_interrupt_exception_t result = cvmx_interrupt_state.exception_handler;
537 cvmx_interrupt_state.exception_handler = handler;
541 #endif /* !__U_BOOT__ */