]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/contrib/octeon-sdk/cvmx-interrupt.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / contrib / octeon-sdk / cvmx-interrupt.c
1 /***********************license start***************
2  * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
3  * reserved.
4  *
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *   * Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  *
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.
17
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
21  *     permission.
22
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
26  * countries.
27
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**************************************/
39
40
41
42
43
44
45
46 /**
47  * @file
48  *
49  * Interface to the Mips interrupts.
50  *
51  * <hr>$Revision: 52004 $<hr>
52  */
53 #ifndef __U_BOOT__
54 #if __GNUC__ >= 4
55 /* Backtrace is only available with the new toolchain.  */
56 #include <execinfo.h>
57 #endif
58 #endif  /* __U_BOOT__ */
59 #include "cvmx-config.h"
60 #include "cvmx.h"
61 #include "cvmx-interrupt.h"
62 #include "cvmx-sysinfo.h"
63 #include "cvmx-uart.h"
64 #include "cvmx-pow.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"
71
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);
75
76 int cvmx_interrupt_in_isr = 0;
77
78 /**
79  * Internal status the interrupt registration
80  */
81 typedef struct
82 {
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;
87
88 /**
89  * Internal state the interrupt registration
90  */
91 #ifndef __U_BOOT__
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__ */
95
96 #define ULL unsigned long long
97
98 #define HI32(data64)    ((uint32_t)(data64 >> 32))
99 #define LO32(data64)    ((uint32_t)(data64 & 0xFFFFFFFF))
100
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" };
105
106 /**
107  * version of printf that works better in exception context.
108  *
109  * @param format
110  */
111 void cvmx_safe_printf(const char *format, ...)
112 {
113     char buffer[256];
114     char *ptr = buffer;
115     int count;
116     va_list args;
117
118     va_start(args, format);
119 #ifndef __U_BOOT__
120     count = vsnprintf(buffer, sizeof(buffer), format, args);
121 #else
122     count = vsprintf(buffer, format, args);
123 #endif
124     va_end(args);
125
126     while (count-- > 0)
127     {
128         cvmx_uart_lsr_t lsrval;
129
130         /* Spin until there is room */
131         do
132         {
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 */
137 #endif
138         }
139         while (lsrval.s.temt == 0);
140
141         if (*ptr == '\n')
142             cvmx_write_csr(CVMX_MIO_UARTX_THR(0), '\r');
143         cvmx_write_csr(CVMX_MIO_UARTX_THR(0), *ptr++);
144     }
145 }
146
147 /* Textual descriptions of cause codes */
148 static const char cause_names[][128] = {
149         /*  0 */ "Interrupt",
150         /*  1 */ "TLB modification",
151         /*  2 */ "tlb load/fetch",
152         /*  3 */ "tlb store",
153         /*  4 */ "address exc, load/fetch",
154         /*  5 */ "address exc, store",
155         /*  6 */ "bus error, instruction fetch",
156         /*  7 */ "bus error, load/store",
157         /*  8 */ "syscall",
158         /*  9 */ "breakpoint",
159         /* 10 */ "reserved instruction",
160         /* 11 */ "cop unusable",
161         /* 12 */ "arithmetic overflow",
162         /* 13 */ "trap",
163         /* 14 */ "",
164         /* 15 */ "floating point exc",
165         /* 16 */ "",
166         /* 17 */ "",
167         /* 18 */ "cop2 exception",
168         /* 19 */ "",
169         /* 20 */ "",
170         /* 21 */ "",
171         /* 22 */ "mdmx unusable",
172         /* 23 */ "watch",
173         /* 24 */ "machine check",
174         /* 25 */ "",
175         /* 26 */ "",
176         /* 27 */ "",
177         /* 28 */ "",
178         /* 29 */ "",
179         /* 30 */ "cache error",
180         /* 31 */ ""
181 };
182
183 /**
184  * @INTERNAL
185  * print_reg64
186  * @param name   Name of the value to print
187  * @param reg    Value to print
188  */
189 static inline void print_reg64(const char *name, uint64_t reg)
190 {
191     cvmx_safe_printf("%16s: 0x%08x%08x\n", name, (unsigned int)HI32(reg),(unsigned int)LO32(reg));
192 }
193
194 /**
195  * @INTERNAL
196  * Dump all useful registers to the console
197  *
198  * @param registers CPU register to dump
199  */
200 static void __cvmx_interrupt_dump_registers(uint64_t registers[32])
201 {
202     uint64_t r1, r2;
203     int reg;
204     for (reg=0; reg<16; reg++)
205     {
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));
210     }
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);
219 }
220
221 /**
222  * @INTERNAL
223  * Default exception handler. Prints out the exception
224  * cause decode and all relevant registers.
225  *
226  * @param registers Registers at time of the exception
227  */
228 #ifndef __U_BOOT__
229 static
230 #endif  /* __U_BOOT__ */
231 void __cvmx_interrupt_default_exception_handler(uint64_t registers[32])
232 {
233     uint64_t trap_print_cause;
234     const char *str;
235
236 #ifndef __U_BOOT__
237     ebt3000_str_write("Trap");
238     cvmx_spinlock_lock(&cvmx_interrupt_default_lock);
239 #endif
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);
245
246 #ifndef __U_BOOT__
247
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");
253 #endif
254
255     cvmx_spinlock_unlock(&cvmx_interrupt_default_lock);
256
257     if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
258         CVMX_BREAK;
259
260     while (1)
261     {
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));
267         if (lsrval.s.dr)
268         {
269             uint64_t tmp;
270             /* Pulse the MCD0 signal. */
271             asm volatile (
272             ".set push\n"
273             ".set noreorder\n"
274             ".set mips64\n"
275             "dmfc0 %0, $22\n"
276             "ori   %0, %0, 0x10\n"
277             "dmtc0 %0, $22\n"
278             ".set pop\n"
279             : "=r" (tmp));
280         }
281     }
282 #endif /* __U_BOOT__ */
283 }
284
285 #ifndef __U_BOOT__
286 /**
287  * @INTERNAL
288  * Default interrupt handler if the user doesn't register one.
289  *
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
293  */
294 static void __cvmx_interrupt_default(int irq_number, uint64_t registers[32], void *user_arg)
295 {
296     cvmx_safe_printf("cvmx_interrupt_default: Received interrupt %d\n", irq_number);
297     __cvmx_interrupt_dump_registers(registers);
298 }
299
300
301 /**
302  * @INTERNAL
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
306  * occurred.
307  *
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
311  */
312 static void __cvmx_interrupt_ciu(int irq_number, uint64_t registers[32], void *user_arg)
313 {
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));
316     int irq = 8;
317
318     /* Handle EN0 sources */
319     while (irq_mask)
320     {
321         if (irq_mask&1)
322         {
323             cvmx_interrupt_state.handlers[irq](irq, registers, cvmx_interrupt_state.data[irq]);
324             return;
325         }
326         irq_mask = irq_mask >> 1;
327         irq++;
328     }
329
330     /* Handle EN1 sources */
331     irq_mask = cvmx_read_csr(CVMX_CIU_INT_SUM1) & cvmx_read_csr(CVMX_CIU_INTX_EN1(ciu_offset));
332     irq = 8 + 64;
333     while (irq_mask)
334     {
335         if (irq_mask&1)
336         {
337             cvmx_interrupt_state.handlers[irq](irq, registers, cvmx_interrupt_state.data[irq]);
338             return;
339         }
340         irq_mask = irq_mask >> 1;
341         irq++;
342     }
343 }
344
345
346 /**
347  * @INTERNAL
348  * Called for all RML interrupts. This is usually an ECC error
349  *
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
353  */
354 static void __cvmx_interrupt_ecc(int irq_number, uint64_t registers[32], void *user_arg)
355 {
356     cvmx_error_poll();
357 }
358
359
360 /**
361  * Process an interrupt request
362  *
363  * @param registers Registers at time of interrupt / exception
364  * Registers 0-31 are standard MIPS, others specific to this routine
365  * @return
366  */
367 EXTERN_ASM void cvmx_interrupt_do_irq(uint64_t registers[35]);
368 void cvmx_interrupt_do_irq(uint64_t registers[35])
369 {
370     uint64_t        mask;
371     uint64_t        cause;
372     uint64_t        status;
373     uint64_t        cache_err;
374     int             i;
375     uint32_t exc_vec;
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)
382     {
383         asm volatile ("dmtc0 %0, $12, 0" : : "r" (status & ~(0xff02)));
384     }
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 */
389
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))
394     {
395         i = cvmx_get_core_num();
396         CVMX_MF_CACHE_ERR(cache_err);
397
398         /* Use copy of DCACHE_ERR register that early exception stub read */
399         if (registers[34] & 0x1)
400         {
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);
404         }
405         else if (cache_err & 0x1)
406         {
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);
408             cache_err = 0;
409             CVMX_MT_CACHE_ERR(cache_err);
410         }
411         else
412             cvmx_safe_printf("Cache error exception: core %d\n", i);
413     }
414
415     if ((cause & 0x7c) != 0)
416     {
417         cvmx_interrupt_state.exception_handler(registers);
418         goto return_from_interrupt;
419     }
420
421     /* Convert the cause into an active mask */
422     mask = ((cause & status) >> 8) & 0xff;
423     if (mask == 0)
424     {
425         goto return_from_interrupt; /* Spurious interrupt */
426     }
427
428     for (i=0; i<8; i++)
429     {
430         if (mask & (1<<i))
431         {
432             cvmx_interrupt_state.handlers[i](i, registers, cvmx_interrupt_state.data[i]);
433             goto return_from_interrupt;
434         }
435     }
436
437     /* We should never get here */
438     __cvmx_interrupt_default_exception_handler(registers);
439
440 return_from_interrupt:
441     /* Restore Status register before returning from exception. */
442     asm volatile ("dmtc0 %0, $12, 0" : : "r" (status));
443 }
444
445
446 /**
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.
450  */
451 void cvmx_interrupt_initialize(void)
452 {
453     void *low_level_loc;
454     cvmx_sysinfo_t *sys_info_ptr = cvmx_sysinfo_get();
455     int i;
456
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);
462
463     if (cvmx_coremask_first_core(sys_info_ptr->core_mask))
464     {
465         cvmx_interrupt_state.exception_handler = __cvmx_interrupt_default_exception_handler;
466
467         for (i=0; i<256; i++)
468         {
469             cvmx_interrupt_state.handlers[i] = __cvmx_interrupt_default;
470             cvmx_interrupt_state.data[i] = NULL;
471         }
472
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);
478
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);
484         CVMX_SYNC;
485
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);
489
490         /* Add an interrupt handler for ECC failures */
491         cvmx_interrupt_register(CVMX_IRQ_RML, __cvmx_interrupt_ecc, NULL);
492
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);
496     }
497
498     cvmx_interrupt_unmask_irq(CVMX_IRQ_CIU0);
499     cvmx_interrupt_unmask_irq(CVMX_IRQ_CIU1);
500     CVMX_ICACHE_INVALIDATE;
501
502     /* Enable interrupts for each core (bit0 of COP0 Status) */
503     uint32_t mask;
504     asm volatile (
505         "mfc0   %0,$12,0\n"
506         "ori    %0, %0, 1\n"
507         "mtc0   %0,$12,0\n"
508         : "=r" (mask));
509 }
510
511
512 /**
513  * Register an interrupt handler for the specified interrupt number.
514  *
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
519  */
520 void cvmx_interrupt_register(cvmx_irq_t irq_number, cvmx_interrupt_func_t func, void *user_arg)
521 {
522     cvmx_interrupt_state.handlers[irq_number] = func;
523     cvmx_interrupt_state.data[irq_number] = user_arg;
524     CVMX_SYNCWS;
525 }
526
527
528 /**
529  * Set the exception handler for all non interrupt sources.
530  *
531  * @param handler New exception handler
532  * @return Old exception handler
533  */
534 cvmx_interrupt_exception_t cvmx_interrupt_set_exception(cvmx_interrupt_exception_t handler)
535 {
536     cvmx_interrupt_exception_t result = cvmx_interrupt_state.exception_handler;
537     cvmx_interrupt_state.exception_handler = handler;
538     CVMX_SYNCWS;
539     return result;
540 }
541 #endif /* !__U_BOOT__ */
542
543