1 /***********************license start***************
2 * Copyright (c) 2003-2008 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 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
24 * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
25 * OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
26 * RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
27 * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
28 * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
29 * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
30 * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
31 * POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
32 * OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
35 * For any questions regarding licensing please contact marketing@caviumnetworks.com
37 ***********************license end**************************************/
47 * Interface to the Mips interrupts.
49 * <hr>$Revision: 42264 $<hr>
52 /* Backtrace is only available with the new toolchain. */
55 #include "cvmx-config.h"
57 #include "cvmx-interrupt.h"
58 #include "cvmx-sysinfo.h"
59 #include "cvmx-uart.h"
61 #include "cvmx-ebt3000.h"
62 #include "cvmx-coremask.h"
63 #include "cvmx-spinlock.h"
64 #include "cvmx-app-init.h"
66 EXTERN_ASM void cvmx_interrupt_stage1(void);
67 EXTERN_ASM void cvmx_interrupt_cache_error(void);
70 * Internal status the interrupt registration
74 cvmx_interrupt_func_t handlers[256]; /**< One function to call per interrupt */
75 void * data[256]; /**< User data per interrupt */
76 cvmx_interrupt_exception_t exception_handler;
77 } cvmx_interrupt_state_t;
80 * Internal state the interrupt registration
82 static CVMX_SHARED cvmx_interrupt_state_t cvmx_interrupt_state;
83 static CVMX_SHARED cvmx_spinlock_t cvmx_interrupt_default_lock;
85 #define COP0_CAUSE "$13,0"
86 #define COP0_STATUS "$12,0"
87 #define COP0_BADVADDR "$8,0"
88 #define COP0_EPC "$14,0"
89 #define READ_COP0(dest, R) asm volatile ("dmfc0 %[rt]," R : [rt] "=r" (dest))
90 #define ULL unsigned long long
96 * Dump all useful registers to the console
98 * @param registers CPU register to dump
100 static void __cvmx_interrupt_dump_registers(uint64_t registers[32])
102 static const char *name[32] = {"r0","at","v0","v1","a0","a1","a2","a3",
103 "t0","t1","t2","t3","t4","t5","t6","t7","s0","s1","s2","s3","s4","s5",
104 "s6","s7", "t8","t9", "k0","k1","gp","sp","s8","ra"};
106 for (reg=0; reg<16; reg++)
108 cvmx_safe_printf("%3s ($%02d): 0x%016llx \t %3s ($%02d): 0x%016llx\n",
109 name[reg], (int)reg, (ULL)registers[reg], name[reg+16], (int)reg+16, (ULL)registers[reg+16]);
111 READ_COP0(reg, COP0_CAUSE);
112 cvmx_safe_printf("%16s: 0x%016llx\n", "COP0_CAUSE", (ULL)reg);
113 READ_COP0(reg, COP0_STATUS);
114 cvmx_safe_printf("%16s: 0x%016llx\n", "COP0_STATUS", (ULL)reg);
115 READ_COP0(reg, COP0_BADVADDR);
116 cvmx_safe_printf("%16s: 0x%016llx\n", "COP0_BADVADDR", (ULL)reg);
117 READ_COP0(reg, COP0_EPC);
118 cvmx_safe_printf("%16s: 0x%016llx\n", "COP0_EPC", (ULL)reg);
124 * Default exception handler. Prints out the exception
125 * cause decode and all relevant registers.
127 * @param registers Registers at time of the exception
129 static void __cvmx_interrupt_default_exception_handler(uint64_t registers[32])
131 uint64_t trap_print_cause;
133 ebt3000_str_write("Trap");
134 cvmx_spinlock_lock(&cvmx_interrupt_default_lock);
135 cvmx_safe_printf("******************************************************************\n");
136 cvmx_safe_printf("Core %d: Unhandled Exception. Cause register decodes to:\n", (int)cvmx_get_core_num());
137 READ_COP0(trap_print_cause, COP0_CAUSE);
138 switch ((trap_print_cause >> 2) & 0x1f)
141 cvmx_safe_printf("Interrupt\n");
144 cvmx_safe_printf("TLB Mod\n");
147 cvmx_safe_printf("tlb load/fetch\n");
150 cvmx_safe_printf("tlb store\n");
153 cvmx_safe_printf("address exc, load/fetch\n");
156 cvmx_safe_printf("address exc, store\n");
159 cvmx_safe_printf("bus error, inst. fetch\n");
162 cvmx_safe_printf("bus error, load/store\n");
165 cvmx_safe_printf("syscall\n");
168 cvmx_safe_printf("breakpoint \n");
171 cvmx_safe_printf("reserved instruction\n");
174 cvmx_safe_printf("cop unusable\n");
177 cvmx_safe_printf("arithmetic overflow\n");
180 cvmx_safe_printf("trap\n");
183 cvmx_safe_printf("floating point exc\n");
186 cvmx_safe_printf("cop2 exception\n");
189 cvmx_safe_printf("mdmx unusable\n");
192 cvmx_safe_printf("watch\n");
195 cvmx_safe_printf("machine check\n");
198 cvmx_safe_printf("cache error\n");
201 cvmx_safe_printf("Reserved exception cause.\n");
206 cvmx_safe_printf("******************************************************************\n");
207 __cvmx_interrupt_dump_registers(registers);
208 cvmx_safe_printf("******************************************************************\n");
210 #if __GNUC__ >= 4 && !defined(OCTEON_DISABLE_BACKTRACE)
211 cvmx_safe_printf("Backtrace:\n\n");
212 __octeon_print_backtrace_func ((__octeon_backtrace_printf_t)cvmx_safe_printf);
213 cvmx_safe_printf("******************************************************************\n");
216 cvmx_spinlock_unlock(&cvmx_interrupt_default_lock);
218 if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
223 /* Interrupts are suppressed when we are in the exception
224 handler (because of SR[EXL]). Spin and poll the uart
225 status and see if the debugger is trying to stop us. */
226 cvmx_uart_lsr_t lsrval;
227 lsrval.u64 = cvmx_read_csr(CVMX_MIO_UARTX_LSR(cvmx_debug_uart));
231 /* Pulse the MCD0 signal. */
248 * Default interrupt handler if the user doesn't register one.
250 * @param irq_number IRQ that caused this interrupt
251 * @param registers Register at the time of the interrupt
252 * @param user_arg Unused optional user data
254 static void __cvmx_interrupt_default(int irq_number, uint64_t registers[32], void *user_arg)
256 cvmx_safe_printf("cvmx_interrupt_default: Received interrupt %d\n", irq_number);
257 __cvmx_interrupt_dump_registers(registers);
263 * Handler for interrupt lines 2 and 3. These are directly tied
264 * to the CIU. The handler queres the status of the CIU and
265 * calls the secondary handler for the CIU interrupt that
268 * @param irq_number Interrupt number that fired (2 or 3)
269 * @param registers Registers at the time of the interrupt
270 * @param user_arg Unused user argument
272 static void __cvmx_interrupt_ciu(int irq_number, uint64_t registers[32], void *user_arg)
274 int ciu_offset = cvmx_get_core_num() * 2 + irq_number - 2;
275 uint64_t irq_mask = cvmx_read_csr(CVMX_CIU_INTX_SUM0(ciu_offset)) & cvmx_read_csr(CVMX_CIU_INTX_EN0(ciu_offset));
278 /* Handle EN0 sources */
283 cvmx_interrupt_state.handlers[irq](irq, registers, cvmx_interrupt_state.data[irq]);
286 irq_mask = irq_mask >> 1;
290 /* Handle EN1 sources */
291 irq_mask = cvmx_read_csr(CVMX_CIU_INT_SUM1) & cvmx_read_csr(CVMX_CIU_INTX_EN1(ciu_offset));
297 cvmx_interrupt_state.handlers[irq](irq, registers, cvmx_interrupt_state.data[irq]);
300 irq_mask = irq_mask >> 1;
308 * Called for all RML interrupts. This is usually an ECC error
310 * @param irq_number Interrupt number that we're being called for
311 * @param registers Registers at the time of the interrupt
312 * @param user_arg Unused user argument
314 static void __cvmx_interrupt_ecc(int irq_number, uint64_t registers[32], void *user_arg)
316 cvmx_interrupt_rsl_decode();
321 * Process an interrupt request
323 * @param registers Registers at time of interrupt / exception
324 * Registers 0-31 are standard MIPS, others specific to this routine
327 EXTERN_ASM void cvmx_interrupt_do_irq(uint64_t registers[35]);
328 void cvmx_interrupt_do_irq(uint64_t registers[35])
337 /* Determine the cause of the interrupt */
338 asm volatile ("dmfc0 %0,$13,0" : "=r" (cause));
339 asm volatile ("dmfc0 %0,$12,0" : "=r" (status));
341 /* The assembly stub at each exception vector saves its address in k1 when
342 ** it calls the stage 2 handler. We use this to compute the exception vector
343 ** that brought us here */
344 exc_vec = (uint32_t)(registers[27] & 0x780); /* Mask off bits we need to ignore */
346 /* Check for cache errors. The cache errors go to a separate exception vector,
347 ** so we will only check these if we got here from a cache error exception, and
348 ** the ERL (error level) bit is set. */
349 if (exc_vec == 0x100 && (status & 0x4))
351 i = cvmx_get_core_num();
352 CVMX_MF_CACHE_ERR(cache_err);
354 /* Use copy of DCACHE_ERR register that early exception stub read */
355 if (registers[34] & 0x1)
357 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);
358 uint64_t dcache_err = 0;
359 CVMX_MT_DCACHE_ERR(dcache_err);
361 else if (cache_err & 0x1)
363 cvmx_safe_printf("Icache error detected: core: %d, set: %d, way : %d\n", i, (int)(cache_err >> 5) & 0x3f, (int)(cache_err >> 7) & 0x3);
365 CVMX_MT_CACHE_ERR(cache_err);
368 cvmx_safe_printf("Cache error exception: core %d\n", i);
371 if ((cause & 0x7c) != 0)
373 cvmx_interrupt_state.exception_handler(registers);
377 /* Convert the cause into an active mask */
378 mask = ((cause & status) >> 8) & 0xff;
380 return; /* Spurious interrupt */
386 cvmx_interrupt_state.handlers[i](i, registers, cvmx_interrupt_state.data[i]);
391 /* We should never get here */
392 __cvmx_interrupt_default_exception_handler(registers);
397 * Initialize the interrupt routine and copy the low level
398 * stub into the correct interrupt vector. This is called
399 * automatically during application startup.
401 void cvmx_interrupt_initialize(void)
404 cvmx_sysinfo_t *sys_info_ptr = cvmx_sysinfo_get();
407 /* Disable all CIU interrupts by default */
408 cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), 0);
409 cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2+1), 0);
410 cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), 0);
411 cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2+1), 0);
413 if (cvmx_coremask_first_core(sys_info_ptr->core_mask))
415 cvmx_interrupt_state.exception_handler = __cvmx_interrupt_default_exception_handler;
417 for (i=0; i<256; i++)
419 cvmx_interrupt_state.handlers[i] = __cvmx_interrupt_default;
420 cvmx_interrupt_state.data[i] = NULL;
423 low_level_loc = CASTPTR(void, CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0,sys_info_ptr->exception_base_addr));
424 memcpy(low_level_loc + 0x80, (void*)cvmx_interrupt_stage1, 0x80);
425 memcpy(low_level_loc + 0x100, (void*)cvmx_interrupt_cache_error, 0x80);
426 memcpy(low_level_loc + 0x180, (void*)cvmx_interrupt_stage1, 0x80);
427 memcpy(low_level_loc + 0x200, (void*)cvmx_interrupt_stage1, 0x80);
428 /* Make sure the locations used to count Icache and Dcache exceptions
429 starts out as zero */
430 cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 8), 0);
431 cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 16), 0);
432 cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 24), 0);
435 /* Add an interrupt handlers for chained CIU interrupts */
436 cvmx_interrupt_register(CVMX_IRQ_CIU0, __cvmx_interrupt_ciu, NULL);
437 cvmx_interrupt_register(CVMX_IRQ_CIU1, __cvmx_interrupt_ciu, NULL);
439 /* Add an interrupt handler for ECC failures */
440 cvmx_interrupt_register(CVMX_IRQ_RML, __cvmx_interrupt_ecc, NULL);
442 cvmx_interrupt_rsl_enable();
443 cvmx_interrupt_unmask_irq(CVMX_IRQ_RML);
446 cvmx_interrupt_unmask_irq(CVMX_IRQ_CIU0);
447 cvmx_interrupt_unmask_irq(CVMX_IRQ_CIU1);
448 CVMX_ICACHE_INVALIDATE;
450 /* Enable interrupts for each core (bit0 of COP0 Status) */
461 * Register an interrupt handler for the specified interrupt number.
463 * @param irq_number Interrupt number to register for (0-135) See
464 * cvmx-interrupt.h for enumeration and description of sources.
465 * @param func Function to call on interrupt.
466 * @param user_arg User data to pass to the interrupt handler
468 void cvmx_interrupt_register(cvmx_irq_t irq_number, cvmx_interrupt_func_t func, void *user_arg)
470 cvmx_interrupt_state.handlers[irq_number] = func;
471 cvmx_interrupt_state.data[irq_number] = user_arg;
477 * Set the exception handler for all non interrupt sources.
479 * @param handler New exception handler
480 * @return Old exception handler
482 cvmx_interrupt_exception_t cvmx_interrupt_set_exception(cvmx_interrupt_exception_t handler)
484 cvmx_interrupt_exception_t result = cvmx_interrupt_state.exception_handler;
485 cvmx_interrupt_state.exception_handler = handler;
492 * version of printf that works better in exception context.
496 void cvmx_safe_printf(const char *format, ...)
498 static char buffer[256];
500 va_start(args, format);
501 int count = vsnprintf(buffer, sizeof(buffer), format, args);
507 cvmx_uart_lsr_t lsrval;
509 /* Spin until there is room */
512 lsrval.u64 = cvmx_read_csr(CVMX_MIO_UARTX_LSR(0));
513 if (lsrval.s.temt == 0)
514 cvmx_wait(10000); /* Just to reduce the load on the system */
516 while (lsrval.s.temt == 0);
519 cvmx_write_csr(CVMX_MIO_UARTX_THR(0), '\r');
520 cvmx_write_csr(CVMX_MIO_UARTX_THR(0), *ptr++);