/***********************license start*************** * Copyright (c) 2003-2010 Cavium Inc. (support@cavium.com). All rights * reserved. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of Cavium Inc. nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * This Software, including technical data, may be subject to U.S. export control * laws, including the U.S. Export Administration Act and its associated * regulations, and may be subject to export or import regulations in other * countries. * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. ***********************license end**************************************/ /** * @file * * Interface to the Mips interrupts. * *
$Revision: 70030 $
*/ #ifndef __U_BOOT__ #if __GNUC__ >= 4 /* Backtrace is only available with the new toolchain. */ #include #endif #endif /* __U_BOOT__ */ #include "cvmx-config.h" #include "cvmx.h" #include "cvmx-interrupt.h" #include "cvmx-sysinfo.h" #include "cvmx-uart.h" #include "cvmx-pow.h" #include "cvmx-ebt3000.h" #include "cvmx-coremask.h" #include "cvmx-spinlock.h" #include "cvmx-atomic.h" #include "cvmx-app-init.h" #include "cvmx-error.h" #include "cvmx-app-hotplug.h" #include "cvmx-profiler.h" #ifndef __U_BOOT__ # include #else # include #endif EXTERN_ASM void cvmx_interrupt_stage1(void); EXTERN_ASM void cvmx_debug_handler_stage1(void); EXTERN_ASM void cvmx_interrupt_cache_error(void); int cvmx_interrupt_in_isr = 0; struct __cvmx_interrupt_handler { cvmx_interrupt_func_t handler; /**< One function to call per interrupt */ void *data; /**< User data per interrupt */ int handler_data; /**< Used internally */ }; /** * Internal status the interrupt registration */ typedef struct { struct __cvmx_interrupt_handler handlers[CVMX_IRQ_MAX]; cvmx_interrupt_exception_t exception_handler; } cvmx_interrupt_state_t; /** * Internal state the interrupt registration */ #ifndef __U_BOOT__ static CVMX_SHARED cvmx_interrupt_state_t cvmx_interrupt_state; static CVMX_SHARED cvmx_spinlock_t cvmx_interrupt_default_lock; /* Incremented once first core processing is finished. */ static CVMX_SHARED int32_t cvmx_interrupt_initialize_flag; #endif /* __U_BOOT__ */ #define ULL unsigned long long #define HI32(data64) ((uint32_t)(data64 >> 32)) #define LO32(data64) ((uint32_t)(data64 & 0xFFFFFFFF)) static const char reg_names[][32] = { "r0","at","v0","v1","a0","a1","a2","a3", "t0","t1","t2","t3","t4","t5","t6","t7", "s0","s1","s2","s3","s4","s5", "s6","s7", "t8","t9", "k0","k1","gp","sp","s8","ra" }; /** * version of printf that works better in exception context. * * @param format */ void cvmx_safe_printf(const char *format, ...) { char buffer[256]; char *ptr = buffer; int count; va_list args; va_start(args, format); #ifndef __U_BOOT__ count = vsnprintf(buffer, sizeof(buffer), format, args); #else count = vsprintf(buffer, format, args); #endif va_end(args); while (count-- > 0) { cvmx_uart_lsr_t lsrval; /* Spin until there is room */ do { lsrval.u64 = cvmx_read_csr(CVMX_MIO_UARTX_LSR(0)); #if !defined(CONFIG_OCTEON_SIM_SPEED) if (lsrval.s.temt == 0) cvmx_wait(10000); /* Just to reduce the load on the system */ #endif } while (lsrval.s.temt == 0); if (*ptr == '\n') cvmx_write_csr(CVMX_MIO_UARTX_THR(0), '\r'); cvmx_write_csr(CVMX_MIO_UARTX_THR(0), *ptr++); } } /* Textual descriptions of cause codes */ static const char cause_names[][128] = { /* 0 */ "Interrupt", /* 1 */ "TLB modification", /* 2 */ "tlb load/fetch", /* 3 */ "tlb store", /* 4 */ "address exc, load/fetch", /* 5 */ "address exc, store", /* 6 */ "bus error, instruction fetch", /* 7 */ "bus error, load/store", /* 8 */ "syscall", /* 9 */ "breakpoint", /* 10 */ "reserved instruction", /* 11 */ "cop unusable", /* 12 */ "arithmetic overflow", /* 13 */ "trap", /* 14 */ "", /* 15 */ "floating point exc", /* 16 */ "", /* 17 */ "", /* 18 */ "cop2 exception", /* 19 */ "", /* 20 */ "", /* 21 */ "", /* 22 */ "mdmx unusable", /* 23 */ "watch", /* 24 */ "machine check", /* 25 */ "", /* 26 */ "", /* 27 */ "", /* 28 */ "", /* 29 */ "", /* 30 */ "cache error", /* 31 */ "" }; /** * @INTERNAL * print_reg64 * @param name Name of the value to print * @param reg Value to print */ static inline void print_reg64(const char *name, uint64_t reg) { cvmx_safe_printf("%16s: 0x%08x%08x\n", name, (unsigned int)HI32(reg),(unsigned int)LO32(reg)); } /** * @INTERNAL * Dump all useful registers to the console * * @param registers CPU register to dump */ static void __cvmx_interrupt_dump_registers(uint64_t *registers) { uint64_t r1, r2; int reg; for (reg=0; reg<16; reg++) { r1 = registers[reg]; r2 = registers[reg+16]; cvmx_safe_printf("%3s ($%02d): 0x%08x%08x \t %3s ($%02d): 0x%08x%08x\n", reg_names[reg], reg, (unsigned int)HI32(r1), (unsigned int)LO32(r1), reg_names[reg+16], reg+16, (unsigned int)HI32(r2), (unsigned int)LO32(r2)); } CVMX_MF_COP0 (r1, COP0_CAUSE); print_reg64 ("COP0_CAUSE", r1); CVMX_MF_COP0 (r2, COP0_STATUS); print_reg64 ("COP0_STATUS", r2); CVMX_MF_COP0 (r1, COP0_BADVADDR); print_reg64 ("COP0_BADVADDR", r1); CVMX_MF_COP0 (r2, COP0_EPC); print_reg64 ("COP0_EPC", r2); } /** * @INTERNAL * Default exception handler. Prints out the exception * cause decode and all relevant registers. * * @param registers Registers at time of the exception */ #ifndef __U_BOOT__ static #endif /* __U_BOOT__ */ void __cvmx_interrupt_default_exception_handler(uint64_t *registers) { uint64_t trap_print_cause; const char *str; #ifndef __U_BOOT__ int modified_zero_pc = 0; ebt3000_str_write("Trap"); cvmx_spinlock_lock(&cvmx_interrupt_default_lock); #endif CVMX_MF_COP0 (trap_print_cause, COP0_CAUSE); str = cause_names [(trap_print_cause >> 2) & 0x1f]; 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"); cvmx_safe_printf("******************************************************************\n"); __cvmx_interrupt_dump_registers(registers); #ifndef __U_BOOT__ cvmx_safe_printf("******************************************************************\n"); #if __GNUC__ >= 4 && !defined(OCTEON_DISABLE_BACKTRACE) cvmx_safe_printf("Backtrace:\n\n"); if (registers[35] == 0) { modified_zero_pc = 1; /* If PC is zero we probably did jalr $zero, in which case $31 - 8 is the call site. */ registers[35] = registers[31] - 8; } __octeon_print_backtrace_func ((__octeon_backtrace_printf_t)cvmx_safe_printf); if (modified_zero_pc) registers[35] = 0; cvmx_safe_printf("******************************************************************\n"); #endif cvmx_spinlock_unlock(&cvmx_interrupt_default_lock); if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) CVMX_BREAK; while (1) { /* Interrupts are suppressed when we are in the exception handler (because of SR[EXL]). Spin and poll the uart status and see if the debugger is trying to stop us. */ cvmx_uart_lsr_t lsrval; lsrval.u64 = cvmx_read_csr(CVMX_MIO_UARTX_LSR(cvmx_debug_uart)); if (lsrval.s.dr) { uint64_t tmp; /* Pulse the MCD0 signal. */ asm volatile ( ".set push\n" ".set noreorder\n" ".set mips64\n" "dmfc0 %0, $22\n" "ori %0, %0, 0x10\n" "dmtc0 %0, $22\n" ".set pop\n" : "=r" (tmp)); } } #endif /* __U_BOOT__ */ } #ifndef __U_BOOT__ /** * @INTERNAL * Default interrupt handler if the user doesn't register one. * * @param irq_number IRQ that caused this interrupt * @param registers Register at the time of the interrupt * @param user_arg Unused optional user data */ static void __cvmx_interrupt_default(int irq_number, uint64_t *registers, void *user_arg) { cvmx_safe_printf("cvmx_interrupt_default: Received interrupt %d\n", irq_number); __cvmx_interrupt_dump_registers(registers); } /** * Map a ciu bit to an irq number. 0xff for invalid. * 0-63 for en0. * 64-127 for en1. */ static CVMX_SHARED uint8_t cvmx_ciu_to_irq[8][64]; #define cvmx_ciu_en0_to_irq cvmx_ciu_to_irq[0] #define cvmx_ciu_en1_to_irq cvmx_ciu_to_irq[1] #define cvmx_ciu2_wrkq_to_irq cvmx_ciu_to_irq[0] #define cvmx_ciu2_wdog_to_irq cvmx_ciu_to_irq[1] #define cvmx_ciu2_rml_to_irq cvmx_ciu_to_irq[2] #define cvmx_ciu2_mio_to_irq cvmx_ciu_to_irq[3] #define cvmx_ciu2_io_to_irq cvmx_ciu_to_irq[4] #define cvmx_ciu2_mem_to_irq cvmx_ciu_to_irq[5] #define cvmx_ciu2_eth_to_irq cvmx_ciu_to_irq[6] #define cvmx_ciu2_gpio_to_irq cvmx_ciu_to_irq[7] static CVMX_SHARED uint8_t cvmx_ciu2_mbox_to_irq[64]; static CVMX_SHARED uint8_t cvmx_ciu_61xx_timer_to_irq[64]; static void __cvmx_interrupt_set_mapping(int irq, unsigned int en, unsigned int bit) { cvmx_interrupt_state.handlers[irq].handler_data = (en << 6) | bit; if (en <= 7) cvmx_ciu_to_irq[en][bit] = irq; else if (en == 8) cvmx_ciu_61xx_timer_to_irq[bit] = irq; else cvmx_ciu2_mbox_to_irq[bit] = irq; } static uint64_t cvmx_interrupt_ciu_en0_mirror; static uint64_t cvmx_interrupt_ciu_en1_mirror; static uint64_t cvmx_interrupt_ciu_61xx_timer_mirror; /** * @INTERNAL * Called for all Performance Counter interrupts. Handler for * interrupt line 6 * * @param irq_number Interrupt number that we're being called for * @param registers Registers at the time of the interrupt * @param user_arg Unused user argument* */ static void __cvmx_interrupt_perf(int irq_number, uint64_t *registers, void *user_arg) { uint64_t perf_counter; CVMX_MF_COP0(perf_counter, COP0_PERFVALUE0); if (perf_counter & (1ull << 63)) cvmx_collect_sample(); } /** * @INTERNAL * Handler for interrupt lines 2 and 3. These are directly tied * to the CIU. The handler queries the status of the CIU and * calls the secondary handler for the CIU interrupt that * occurred. * * @param irq_number Interrupt number that fired (2 or 3) * @param registers Registers at the time of the interrupt * @param user_arg Unused user argument */ static void __cvmx_interrupt_ciu(int irq_number, uint64_t *registers, void *user_arg) { int ciu_offset; uint64_t irq_mask; uint64_t irq; int bit; int core = cvmx_get_core_num(); if (irq_number == CVMX_IRQ_MIPS2) { /* Handle EN0 sources */ ciu_offset = core * 2; irq_mask = cvmx_read_csr(CVMX_CIU_INTX_SUM0(ciu_offset)) & cvmx_interrupt_ciu_en0_mirror; CVMX_DCLZ(bit, irq_mask); bit = 63 - bit; /* If ciu_int_sum1 is set, means its a timer interrupt */ if (bit == 51 && (OCTEON_IS_MODEL(OCTEON_CN61XX) || OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_2))) { uint64_t irq_mask; int bit; irq_mask = cvmx_read_csr(CVMX_CIU_SUM2_PPX_IP2(core)) & cvmx_interrupt_ciu_61xx_timer_mirror; CVMX_DCLZ(bit, irq_mask); bit = 63 - bit; /* Handle TIMER(4..9) interrupts */ if (bit <= 9 && bit >= 4) { uint64_t irq = cvmx_ciu_61xx_timer_to_irq[bit]; if (cvmx_unlikely(irq == 0xff)) { /* No mapping */ cvmx_interrupt_ciu_61xx_timer_mirror &= ~(1ull << bit); cvmx_write_csr(CVMX_CIU_EN2_PPX_IP2(core), cvmx_interrupt_ciu_61xx_timer_mirror); return; } struct __cvmx_interrupt_handler *h = cvmx_interrupt_state.handlers + irq; h->handler(irq, registers, h->data); return; } } if (bit >= 0) { irq = cvmx_ciu_en0_to_irq[bit]; if (cvmx_unlikely(irq == 0xff)) { /* No mapping. */ cvmx_interrupt_ciu_en0_mirror &= ~(1ull << bit); cvmx_write_csr(CVMX_CIU_INTX_EN0(ciu_offset), cvmx_interrupt_ciu_en0_mirror); return; } struct __cvmx_interrupt_handler *h = cvmx_interrupt_state.handlers + irq; h->handler(irq, registers, h->data); return; } } else { /* Handle EN1 sources */ ciu_offset = cvmx_get_core_num() * 2 + 1; irq_mask = cvmx_read_csr(CVMX_CIU_INT_SUM1) & cvmx_interrupt_ciu_en1_mirror; CVMX_DCLZ(bit, irq_mask); bit = 63 - bit; if (bit >= 0) { irq = cvmx_ciu_en1_to_irq[bit]; if (cvmx_unlikely(irq == 0xff)) { /* No mapping. */ cvmx_interrupt_ciu_en1_mirror &= ~(1ull << bit); cvmx_write_csr(CVMX_CIU_INTX_EN1(ciu_offset), cvmx_interrupt_ciu_en1_mirror); return; } struct __cvmx_interrupt_handler *h = cvmx_interrupt_state.handlers + irq; h->handler(irq, registers, h->data); return; } } } /** * @INTERNAL * Handler for interrupt line 3, the DPI_DMA will have different value * per core, all other fields values are identical for different cores. * These are directly tied to the CIU. The handler queries the status of * the CIU and calls the secondary handler for the CIU interrupt that * occurred. * * @param irq_number Interrupt number that fired (2 or 3) * @param registers Registers at the time of the interrupt * @param user_arg Unused user argument */ static void __cvmx_interrupt_ciu_cn61xx(int irq_number, uint64_t *registers, void *user_arg) { /* Handle EN1 sources */ int core = cvmx_get_core_num(); int ciu_offset; uint64_t irq_mask; uint64_t irq; int bit; ciu_offset = core * 2 + 1; irq_mask = cvmx_read_csr(CVMX_CIU_SUM1_PPX_IP3(core)) & cvmx_interrupt_ciu_en1_mirror; CVMX_DCLZ(bit, irq_mask); bit = 63 - bit; if (bit >= 0) { irq = cvmx_ciu_en1_to_irq[bit]; if (cvmx_unlikely(irq == 0xff)) { /* No mapping. */ cvmx_interrupt_ciu_en1_mirror &= ~(1ull << bit); cvmx_write_csr(CVMX_CIU_INTX_EN1(ciu_offset), cvmx_interrupt_ciu_en1_mirror); return; } struct __cvmx_interrupt_handler *h = cvmx_interrupt_state.handlers + irq; h->handler(irq, registers, h->data); return; } } /** * @INTERNAL * Handler for interrupt line 2 on 68XX. These are directly tied * to the CIU2. The handler queries the status of the CIU and * calls the secondary handler for the CIU interrupt that * occurred. * * @param irq_number Interrupt number that fired (2 or 3) * @param registers Registers at the time of the interrupt * @param user_arg Unused user argument */ static void __cvmx_interrupt_ciu2(int irq_number, uint64_t *registers, void *user_arg) { int sum_bit, src_bit; uint64_t irq; uint64_t src_reg, src_val; struct __cvmx_interrupt_handler *h; int core = cvmx_get_core_num(); uint64_t sum = cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(core)); CVMX_DCLZ(sum_bit, sum); sum_bit = 63 - sum_bit; if (sum_bit >= 0) { switch (sum_bit) { case 63: case 62: case 61: case 60: irq = cvmx_ciu2_mbox_to_irq[sum_bit - 60]; if (cvmx_unlikely(irq == 0xff)) { /* No mapping. */ uint64_t mask_reg = CVMX_CIU2_EN_PPX_IP2_MBOX_W1C(core); cvmx_write_csr(mask_reg, 1ull << (sum_bit - 60)); break; } h = cvmx_interrupt_state.handlers + irq; h->handler(irq, registers, h->data); break; case 7: case 6: case 5: case 4: case 3: case 2: case 1: case 0: src_reg = CVMX_CIU2_SRC_PPX_IP2_WRKQ(core) + (0x1000 * sum_bit); src_val = cvmx_read_csr(src_reg); if (!src_val) break; CVMX_DCLZ(src_bit, src_val); src_bit = 63 - src_bit; irq = cvmx_ciu_to_irq[sum_bit][src_bit]; if (cvmx_unlikely(irq == 0xff)) { /* No mapping. */ uint64_t mask_reg = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(core) + (0x1000 * sum_bit); cvmx_write_csr(mask_reg, 1ull << src_bit); break; } h = cvmx_interrupt_state.handlers + irq; h->handler(irq, registers, h->data); break; default: cvmx_safe_printf("Unknown CIU2 bit: %d\n", sum_bit); break; } } /* Clear the source to reduce the chance for spurious interrupts. */ /* CN68XX has an CIU-15786 errata that accessing the ACK registers * can stop interrupts from propagating */ if (OCTEON_IS_MODEL(OCTEON_CN68XX)) cvmx_read_csr(CVMX_CIU2_INTR_CIU_READY); else cvmx_read_csr(CVMX_CIU2_ACK_PPX_IP2(core)); } /** * @INTERNAL * Called for all RML interrupts. This is usually an ECC error * * @param irq_number Interrupt number that we're being called for * @param registers Registers at the time of the interrupt * @param user_arg Unused user argument */ static void __cvmx_interrupt_ecc(int irq_number, uint64_t *registers, void *user_arg) { cvmx_error_poll(); } /** * Process an interrupt request * * @param registers Registers at time of interrupt / exception * Registers 0-31 are standard MIPS, others specific to this routine * @return */ void cvmx_interrupt_do_irq(uint64_t *registers); void cvmx_interrupt_do_irq(uint64_t *registers) { uint64_t mask; uint64_t cause; uint64_t status; uint64_t cache_err; int i; uint32_t exc_vec; /* Determine the cause of the interrupt */ asm volatile ("dmfc0 %0,$13,0" : "=r" (cause)); asm volatile ("dmfc0 %0,$12,0" : "=r" (status)); /* In case of exception, clear all interrupts to avoid recursive interrupts. Also clear EXL bit to display the correct PC value. */ if ((cause & 0x7c) == 0) { asm volatile ("dmtc0 %0, $12, 0" : : "r" (status & ~(0xff02))); } /* The assembly stub at each exception vector saves its address in k1 when ** it calls the stage 2 handler. We use this to compute the exception vector ** that brought us here */ exc_vec = (uint32_t)(registers[27] & 0x780); /* Mask off bits we need to ignore */ /* Check for cache errors. The cache errors go to a separate exception vector, ** so we will only check these if we got here from a cache error exception, and ** the ERL (error level) bit is set. */ i = cvmx_get_core_num(); if (exc_vec == 0x100 && (status & 0x4)) { CVMX_MF_CACHE_ERR(cache_err); /* Use copy of DCACHE_ERR register that early exception stub read */ if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN5XXX)) { if (registers[34] & 0x1) cvmx_safe_printf("Dcache error detected: core: %d, way: %d, va 7:3: 0x%x\n", i, (int)(registers[34] >> 8) & 0x3f, (int)(registers[34] >> 3) & 0x1f); else if (cache_err & 0x1) cvmx_safe_printf("Icache error detected: core: %d, set: %d, way : %d, va 6:3 = 0x%x\n", i, (int)(cache_err >> 5) & 0x3f, (int)(cache_err >> 3) & 0x3, (int)(cache_err >> 11) & 0xf); else cvmx_safe_printf("Cache error exception: core %d\n", i); } else { if (registers[34] & 0x1) cvmx_safe_printf("Dcache error detected: core: %d, way: %d, va 9:7: 0x%x\n", i, (int)(registers[34] >> 10) & 0x1f, (int)(registers[34] >> 7) & 0x3); else if (cache_err & 0x1) cvmx_safe_printf("Icache error detected: core: %d, way : %d, va 9:3 = 0x%x\n", i, (int)(cache_err >> 10) & 0x3f, (int)(cache_err >> 3) & 0x7f); else cvmx_safe_printf("Cache error exception: core %d\n", i); } CVMX_MT_DCACHE_ERR(1); CVMX_MT_CACHE_ERR(0); } /* The bus error exceptions can occur due to DID timeout or write buffer, check by reading COP0_CACHEERRD */ if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_MODEL(OCTEON_CNF7XXX)) { i = cvmx_get_core_num(); if (registers[34] & 0x4) { cvmx_safe_printf("Bus error detected due to DID timeout: core: %d\n", i); CVMX_MT_DCACHE_ERR(4); } else if (registers[34] & 0x2) { cvmx_safe_printf("Bus error detected due to write buffer parity: core: %d\n", i); CVMX_MT_DCACHE_ERR(2); } } if ((cause & 0x7c) != 0) { cvmx_interrupt_state.exception_handler(registers); goto return_from_interrupt; } /* Convert the cause into an active mask */ mask = ((cause & status) >> 8) & 0xff; if (mask == 0) { goto return_from_interrupt; /* Spurious interrupt */ } for (i=0; i<8; i++) { if (mask & (1<handler(i, registers, h->data); goto return_from_interrupt; } } /* We should never get here */ __cvmx_interrupt_default_exception_handler(registers); return_from_interrupt: /* Restore Status register before returning from exception. */ asm volatile ("dmtc0 %0, $12, 0" : : "r" (status)); } void (*cvmx_interrupt_mask_irq)(int irq_number); void (*cvmx_interrupt_unmask_irq)(int irq_number); #define CLEAR_OR_MASK(V,M,O) ({\ if (O) \ (V) &= ~(M); \ else \ (V) |= (M); \ }) static void __cvmx_interrupt_ciu2_mask_unmask_irq(int irq_number, int op) { if (irq_number < 0 || irq_number >= CVMX_IRQ_MAX) return; if (irq_number <= CVMX_IRQ_MIPS7) { uint32_t flags, mask; flags = cvmx_interrupt_disable_save(); asm volatile ("mfc0 %0,$12,0" : "=r" (mask)); CLEAR_OR_MASK(mask, 1 << (8 + irq_number), op); asm volatile ("mtc0 %0,$12,0" : : "r" (mask)); cvmx_interrupt_restore(flags); } else { int idx; uint64_t reg; int core = cvmx_get_core_num(); int bit = cvmx_interrupt_state.handlers[irq_number].handler_data; if (bit < 0) return; idx = bit >> 6; bit &= 0x3f; if (idx > 7) { /* MBOX */ if (op) reg = CVMX_CIU2_EN_PPX_IP2_MBOX_W1C(core); else reg = CVMX_CIU2_EN_PPX_IP2_MBOX_W1S(core); } else { if (op) reg = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(core) + (0x1000 * idx); else reg = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(core) + (0x1000 * idx); } cvmx_write_csr(reg, 1ull << bit); } } static void __cvmx_interrupt_ciu2_mask_irq(int irq_number) { __cvmx_interrupt_ciu2_mask_unmask_irq(irq_number, 1); } static void __cvmx_interrupt_ciu2_unmask_irq(int irq_number) { __cvmx_interrupt_ciu2_mask_unmask_irq(irq_number, 0); } static void __cvmx_interrupt_ciu_mask_unmask_irq(int irq_number, int op) { uint32_t flags; if (irq_number < 0 || irq_number >= CVMX_IRQ_MAX) return; flags = cvmx_interrupt_disable_save(); if (irq_number <= CVMX_IRQ_MIPS7) { uint32_t mask; asm volatile ("mfc0 %0,$12,0" : "=r" (mask)); CLEAR_OR_MASK(mask, 1 << (8 + irq_number), op); asm volatile ("mtc0 %0,$12,0" : : "r" (mask)); } else { int ciu_bit, ciu_offset; int bit = cvmx_interrupt_state.handlers[irq_number].handler_data; int is_timer_intr = bit >> 6; int core = cvmx_get_core_num(); if (bit < 0) goto out; ciu_bit = bit & 0x3f; ciu_offset = core * 2; if (is_timer_intr == 8) { CLEAR_OR_MASK(cvmx_interrupt_ciu_61xx_timer_mirror, 1ull << ciu_bit, op); CLEAR_OR_MASK(cvmx_interrupt_ciu_en0_mirror, 1ull << 51, op); // SUM2 bit cvmx_write_csr(CVMX_CIU_EN2_PPX_IP2(core), cvmx_interrupt_ciu_61xx_timer_mirror); } else if (bit & 0x40) { /* EN1 */ ciu_offset += 1; CLEAR_OR_MASK(cvmx_interrupt_ciu_en1_mirror, 1ull << ciu_bit, op); cvmx_write_csr(CVMX_CIU_INTX_EN1(ciu_offset), cvmx_interrupt_ciu_en1_mirror); } else { /* EN0 */ CLEAR_OR_MASK(cvmx_interrupt_ciu_en0_mirror, 1ull << ciu_bit, op); cvmx_write_csr(CVMX_CIU_INTX_EN0(ciu_offset), cvmx_interrupt_ciu_en0_mirror); } } out: cvmx_interrupt_restore(flags); } static void __cvmx_interrupt_ciu_mask_irq(int irq_number) { __cvmx_interrupt_ciu_mask_unmask_irq(irq_number, 1); } static void __cvmx_interrupt_ciu_unmask_irq(int irq_number) { __cvmx_interrupt_ciu_mask_unmask_irq(irq_number, 0); } /** * Register an interrupt handler for the specified interrupt number. * * @param irq_number Interrupt number to register for See * cvmx-interrupt.h for enumeration and description of sources. * @param func Function to call on interrupt. * @param user_arg User data to pass to the interrupt handler */ void cvmx_interrupt_register(int irq_number, cvmx_interrupt_func_t func, void *user_arg) { if (irq_number >= CVMX_IRQ_MAX || irq_number < 0) { cvmx_warn("cvmx_interrupt_register: Illegal irq_number %d\n", irq_number); return; } cvmx_interrupt_state.handlers[irq_number].handler = func; cvmx_interrupt_state.handlers[irq_number].data = user_arg; CVMX_SYNCWS; } static void cvmx_interrupt_ciu_initialize(cvmx_sysinfo_t *sys_info_ptr) { int i; int core = cvmx_get_core_num(); /* Disable all CIU interrupts by default */ cvmx_interrupt_ciu_en0_mirror = 0; cvmx_interrupt_ciu_en1_mirror = 0; cvmx_interrupt_ciu_61xx_timer_mirror = 0; cvmx_write_csr(CVMX_CIU_INTX_EN0(core * 2), cvmx_interrupt_ciu_en0_mirror); cvmx_write_csr(CVMX_CIU_INTX_EN0((core * 2)+1), cvmx_interrupt_ciu_en0_mirror); cvmx_write_csr(CVMX_CIU_INTX_EN1(core * 2), cvmx_interrupt_ciu_en1_mirror); cvmx_write_csr(CVMX_CIU_INTX_EN1((core * 2)+1), cvmx_interrupt_ciu_en1_mirror); if (OCTEON_IS_MODEL(OCTEON_CN61XX) || OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_2)) cvmx_write_csr(CVMX_CIU_EN2_PPX_IP2(cvmx_get_core_num()), cvmx_interrupt_ciu_61xx_timer_mirror); if (!cvmx_coremask_first_core(sys_info_ptr->core_mask)|| is_core_being_hot_plugged()) return; /* On the first core, set up the maps */ for (i = 0; i < 64; i++) { cvmx_ciu_en0_to_irq[i] = 0xff; cvmx_ciu_en1_to_irq[i] = 0xff; cvmx_ciu_61xx_timer_to_irq[i] = 0xff; } /* WORKQ */ for (i = 0; i < 16; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_WORKQ0 + i, 0, i); /* GPIO */ for (i = 0; i < 16; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_GPIO0 + i, 0, i + 16); /* MBOX */ for (i = 0; i < 2; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_MBOX0 + i, 0, i + 32); /* UART */ __cvmx_interrupt_set_mapping(CVMX_IRQ_UART0 + 0, 0, 34); __cvmx_interrupt_set_mapping(CVMX_IRQ_UART0 + 1, 0, 35); __cvmx_interrupt_set_mapping(CVMX_IRQ_UART0 + 2, 1, 16); /* PCI */ for (i = 0; i < 4; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_PCI_INT0 + i, 0, i + 36); /* MSI */ for (i = 0; i < 4; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_PCI_MSI0 + i, 0, i + 40); /* TWSI */ __cvmx_interrupt_set_mapping(CVMX_IRQ_TWSI0 + 0, 0, 45); __cvmx_interrupt_set_mapping(CVMX_IRQ_TWSI0 + 1, 0, 59); /* other */ __cvmx_interrupt_set_mapping(CVMX_IRQ_RML, 0, 46); __cvmx_interrupt_set_mapping(CVMX_IRQ_TRACE0, 0, 47); /* GMX_DRP */ for (i = 0; i < 2; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_GMX_DRP0 + i, 0, i + 48); __cvmx_interrupt_set_mapping(CVMX_IRQ_IPD_DRP, 0, 50); __cvmx_interrupt_set_mapping(CVMX_IRQ_KEY_ZERO, 0, 51); /* TIMER0 */ for (i = 0; i < 4; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_TIMER0 + i, 0, i + 52); /* TIMER4..9 */ for(i = 0; i < 6; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_TIMER4 + i, 8, i + 4); __cvmx_interrupt_set_mapping(CVMX_IRQ_USB0 + 0, 0, 56); __cvmx_interrupt_set_mapping(CVMX_IRQ_USB0 + 1, 1, 17); __cvmx_interrupt_set_mapping(CVMX_IRQ_PCM, 0, 57); __cvmx_interrupt_set_mapping(CVMX_IRQ_MPI, 0, 58); __cvmx_interrupt_set_mapping(CVMX_IRQ_POWIQ, 0, 60); __cvmx_interrupt_set_mapping(CVMX_IRQ_IPDPPTHR, 0, 61); __cvmx_interrupt_set_mapping(CVMX_IRQ_MII0 + 0, 0, 62); __cvmx_interrupt_set_mapping(CVMX_IRQ_MII0 + 1, 1, 18); __cvmx_interrupt_set_mapping(CVMX_IRQ_BOOTDMA, 0, 63); /* WDOG */ for (i = 0; i < 16; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_WDOG0 + i, 1, i); __cvmx_interrupt_set_mapping(CVMX_IRQ_NAND, 1, 19); __cvmx_interrupt_set_mapping(CVMX_IRQ_MIO, 1, 20); __cvmx_interrupt_set_mapping(CVMX_IRQ_IOB, 1, 21); __cvmx_interrupt_set_mapping(CVMX_IRQ_FPA, 1, 22); __cvmx_interrupt_set_mapping(CVMX_IRQ_POW, 1, 23); __cvmx_interrupt_set_mapping(CVMX_IRQ_L2C, 1, 24); __cvmx_interrupt_set_mapping(CVMX_IRQ_IPD, 1, 25); __cvmx_interrupt_set_mapping(CVMX_IRQ_PIP, 1, 26); __cvmx_interrupt_set_mapping(CVMX_IRQ_PKO, 1, 27); __cvmx_interrupt_set_mapping(CVMX_IRQ_ZIP, 1, 28); __cvmx_interrupt_set_mapping(CVMX_IRQ_TIM, 1, 29); __cvmx_interrupt_set_mapping(CVMX_IRQ_RAD, 1, 30); __cvmx_interrupt_set_mapping(CVMX_IRQ_KEY, 1, 31); __cvmx_interrupt_set_mapping(CVMX_IRQ_DFA, 1, 32); __cvmx_interrupt_set_mapping(CVMX_IRQ_USBCTL, 1, 33); __cvmx_interrupt_set_mapping(CVMX_IRQ_SLI, 1, 34); __cvmx_interrupt_set_mapping(CVMX_IRQ_DPI, 1, 35); __cvmx_interrupt_set_mapping(CVMX_IRQ_AGX0, 1, 36); __cvmx_interrupt_set_mapping(CVMX_IRQ_AGX0 + 1, 1, 37); __cvmx_interrupt_set_mapping(CVMX_IRQ_DPI_DMA, 1, 40); __cvmx_interrupt_set_mapping(CVMX_IRQ_AGL, 1, 46); __cvmx_interrupt_set_mapping(CVMX_IRQ_PTP, 1, 47); __cvmx_interrupt_set_mapping(CVMX_IRQ_PEM0, 1, 48); __cvmx_interrupt_set_mapping(CVMX_IRQ_PEM1, 1, 49); __cvmx_interrupt_set_mapping(CVMX_IRQ_SRIO0, 1, 50); __cvmx_interrupt_set_mapping(CVMX_IRQ_SRIO1, 1, 51); __cvmx_interrupt_set_mapping(CVMX_IRQ_LMC0, 1, 52); __cvmx_interrupt_set_mapping(CVMX_IRQ_DFM, 1, 56); __cvmx_interrupt_set_mapping(CVMX_IRQ_SRIO2, 1, 60); __cvmx_interrupt_set_mapping(CVMX_IRQ_RST, 1, 63); } static void cvmx_interrupt_ciu2_initialize(cvmx_sysinfo_t *sys_info_ptr) { int i; /* Disable all CIU2 interrupts by default */ cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_WRKQ(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_WRKQ(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_WRKQ(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_WDOG(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_WDOG(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_WDOG(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_RML(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_RML(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_RML(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_MIO(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_MIO(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_MIO(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_IO(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_IO(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_IO(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_MEM(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_MEM(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_MEM(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_PKT(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_PKT(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_PKT(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_GPIO(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_GPIO(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_GPIO(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_MBOX(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_MBOX(cvmx_get_core_num()), 0); cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_MBOX(cvmx_get_core_num()), 0); if (!cvmx_coremask_first_core(sys_info_ptr->core_mask) || is_core_being_hot_plugged()) return; /* On the first core, set up the maps */ for (i = 0; i < 64; i++) { cvmx_ciu2_wrkq_to_irq[i] = 0xff; cvmx_ciu2_wdog_to_irq[i] = 0xff; cvmx_ciu2_rml_to_irq[i] = 0xff; cvmx_ciu2_mio_to_irq[i] = 0xff; cvmx_ciu2_io_to_irq[i] = 0xff; cvmx_ciu2_mem_to_irq[i] = 0xff; cvmx_ciu2_eth_to_irq[i] = 0xff; cvmx_ciu2_gpio_to_irq[i] = 0xff; cvmx_ciu2_mbox_to_irq[i] = 0xff; } /* WORKQ */ for (i = 0; i < 64; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_WORKQ0 + i, 0, i); /* GPIO */ for (i = 0; i < 16; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_GPIO0 + i, 7, i); /* MBOX */ for (i = 0; i < 4; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_MBOX0 + i, 60, i); /* UART */ for (i = 0; i < 2; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_UART0 + i, 3, 36 + i); /* PCI */ for (i = 0; i < 4; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_PCI_INT0 + i, 4, 16 + i); /* MSI */ for (i = 0; i < 4; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_PCI_MSI0 + i, 4, 8 + i); /* TWSI */ for (i = 0; i < 2; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_TWSI0 + i, 3, 32 + i); /* TRACE */ for (i = 0; i < 4; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_TRACE0 + i, 2, 52 + i); /* GMX_DRP */ for (i = 0; i < 5; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_GMX_DRP0 + i, 6, 8 + i); __cvmx_interrupt_set_mapping(CVMX_IRQ_IPD_DRP, 3, 2); /* TIMER0 */ for (i = 0; i < 4; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_TIMER0 + i, 3, 8 + i); __cvmx_interrupt_set_mapping(CVMX_IRQ_USB0, 3, 44); __cvmx_interrupt_set_mapping(CVMX_IRQ_IPDPPTHR, 3, 0); __cvmx_interrupt_set_mapping(CVMX_IRQ_MII0, 6, 40); __cvmx_interrupt_set_mapping(CVMX_IRQ_BOOTDMA, 3, 18); /* WDOG */ for (i = 0; i < 32; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_WDOG0 + i, 1, i); __cvmx_interrupt_set_mapping(CVMX_IRQ_NAND, 3, 16); __cvmx_interrupt_set_mapping(CVMX_IRQ_MIO, 3, 17); __cvmx_interrupt_set_mapping(CVMX_IRQ_IOB, 2, 0); __cvmx_interrupt_set_mapping(CVMX_IRQ_FPA, 2, 4); __cvmx_interrupt_set_mapping(CVMX_IRQ_POW, 2, 16); __cvmx_interrupt_set_mapping(CVMX_IRQ_L2C, 2, 48); __cvmx_interrupt_set_mapping(CVMX_IRQ_IPD, 2, 5); __cvmx_interrupt_set_mapping(CVMX_IRQ_PIP, 2, 6); __cvmx_interrupt_set_mapping(CVMX_IRQ_PKO, 2, 7); __cvmx_interrupt_set_mapping(CVMX_IRQ_ZIP, 2, 24); __cvmx_interrupt_set_mapping(CVMX_IRQ_TIM, 2, 28); __cvmx_interrupt_set_mapping(CVMX_IRQ_RAD, 2, 29); __cvmx_interrupt_set_mapping(CVMX_IRQ_KEY, 2, 30); __cvmx_interrupt_set_mapping(CVMX_IRQ_DFA, 2, 40); __cvmx_interrupt_set_mapping(CVMX_IRQ_USBCTL, 3, 40); __cvmx_interrupt_set_mapping(CVMX_IRQ_SLI, 2, 32); __cvmx_interrupt_set_mapping(CVMX_IRQ_DPI, 2, 33); __cvmx_interrupt_set_mapping(CVMX_IRQ_DPI_DMA, 2, 36); /* AGX */ for (i = 0; i < 5; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_AGX0 + i, 6, i); __cvmx_interrupt_set_mapping(CVMX_IRQ_AGL, 6, 32); __cvmx_interrupt_set_mapping(CVMX_IRQ_PTP, 3, 48); __cvmx_interrupt_set_mapping(CVMX_IRQ_PEM0, 4, 32); __cvmx_interrupt_set_mapping(CVMX_IRQ_PEM1, 4, 32); /* LMC */ for (i = 0; i < 4; i++) __cvmx_interrupt_set_mapping(CVMX_IRQ_LMC0 + i, 5, i); __cvmx_interrupt_set_mapping(CVMX_IRQ_RST, 3, 63); __cvmx_interrupt_set_mapping(CVMX_IRQ_ILK, 6, 48); } /** * Initialize the interrupt routine and copy the low level * stub into the correct interrupt vector. This is called * automatically during application startup. */ void cvmx_interrupt_initialize(void) { void *low_level_loc; cvmx_sysinfo_t *sys_info_ptr = cvmx_sysinfo_get(); int i; if (cvmx_coremask_first_core(sys_info_ptr->core_mask) && !is_core_being_hot_plugged()) { #ifndef CVMX_ENABLE_CSR_ADDRESS_CHECKING /* We assume this relationship between the registers. */ CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x1000 == CVMX_CIU2_SRC_PPX_IP2_WDOG(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x2000 == CVMX_CIU2_SRC_PPX_IP2_RML(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x3000 == CVMX_CIU2_SRC_PPX_IP2_MIO(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x4000 == CVMX_CIU2_SRC_PPX_IP2_IO(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x5000 == CVMX_CIU2_SRC_PPX_IP2_MEM(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x6000 == CVMX_CIU2_SRC_PPX_IP2_PKT(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x7000 == CVMX_CIU2_SRC_PPX_IP2_GPIO(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x1000 == CVMX_CIU2_EN_PPX_IP2_WDOG_W1C(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x2000 == CVMX_CIU2_EN_PPX_IP2_RML_W1C(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x3000 == CVMX_CIU2_EN_PPX_IP2_MIO_W1C(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x4000 == CVMX_CIU2_EN_PPX_IP2_IO_W1C(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x5000 == CVMX_CIU2_EN_PPX_IP2_MEM_W1C(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x6000 == CVMX_CIU2_EN_PPX_IP2_PKT_W1C(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x7000 == CVMX_CIU2_EN_PPX_IP2_GPIO_W1C(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x1000 == CVMX_CIU2_EN_PPX_IP2_WDOG_W1S(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x2000 == CVMX_CIU2_EN_PPX_IP2_RML_W1S(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x3000 == CVMX_CIU2_EN_PPX_IP2_MIO_W1S(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x4000 == CVMX_CIU2_EN_PPX_IP2_IO_W1S(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x5000 == CVMX_CIU2_EN_PPX_IP2_MEM_W1S(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x6000 == CVMX_CIU2_EN_PPX_IP2_PKT_W1S(0)); CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x7000 == CVMX_CIU2_EN_PPX_IP2_GPIO_W1S(0)); #endif /* !CVMX_ENABLE_CSR_ADDRESS_CHECKING */ for (i = 0; i < CVMX_IRQ_MAX; i++) { cvmx_interrupt_state.handlers[i].handler = __cvmx_interrupt_default; cvmx_interrupt_state.handlers[i].data = NULL; cvmx_interrupt_state.handlers[i].handler_data = -1; } } if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { cvmx_interrupt_mask_irq = __cvmx_interrupt_ciu2_mask_irq; cvmx_interrupt_unmask_irq = __cvmx_interrupt_ciu2_unmask_irq; cvmx_interrupt_ciu2_initialize(sys_info_ptr); /* Add an interrupt handlers for chained CIU interrupt */ cvmx_interrupt_register(CVMX_IRQ_MIPS2, __cvmx_interrupt_ciu2, NULL); } else if (OCTEON_IS_MODEL(OCTEON_CN61XX) || OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_2)) { cvmx_interrupt_mask_irq = __cvmx_interrupt_ciu_mask_irq; cvmx_interrupt_unmask_irq = __cvmx_interrupt_ciu_unmask_irq; cvmx_interrupt_ciu_initialize(sys_info_ptr); /* Add an interrupt handlers for chained CIU interrupts */ cvmx_interrupt_register(CVMX_IRQ_MIPS2, __cvmx_interrupt_ciu, NULL); cvmx_interrupt_register(CVMX_IRQ_MIPS3, __cvmx_interrupt_ciu_cn61xx, NULL); } else { cvmx_interrupt_mask_irq = __cvmx_interrupt_ciu_mask_irq; cvmx_interrupt_unmask_irq = __cvmx_interrupt_ciu_unmask_irq; cvmx_interrupt_ciu_initialize(sys_info_ptr); /* Add an interrupt handlers for chained CIU interrupts */ cvmx_interrupt_register(CVMX_IRQ_MIPS2, __cvmx_interrupt_ciu, NULL); cvmx_interrupt_register(CVMX_IRQ_MIPS3, __cvmx_interrupt_ciu, NULL); } /* Move performance counter interrupts to IRQ 6*/ cvmx_update_perfcnt_irq(); /* Add an interrupt handler for Perf counter interrupts */ cvmx_interrupt_register(CVMX_IRQ_MIPS6, __cvmx_interrupt_perf, NULL); if (cvmx_coremask_first_core(sys_info_ptr->core_mask) && !is_core_being_hot_plugged()) { cvmx_interrupt_state.exception_handler = __cvmx_interrupt_default_exception_handler; low_level_loc = CASTPTR(void, CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0,sys_info_ptr->exception_base_addr)); memcpy(low_level_loc + 0x80, (void*)cvmx_interrupt_stage1, 0x80); memcpy(low_level_loc + 0x100, (void*)cvmx_interrupt_cache_error, 0x80); memcpy(low_level_loc + 0x180, (void*)cvmx_interrupt_stage1, 0x80); memcpy(low_level_loc + 0x200, (void*)cvmx_interrupt_stage1, 0x80); /* Make sure the locations used to count Icache and Dcache exceptions starts out as zero */ cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 8), 0); cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 16), 0); cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 24), 0); CVMX_SYNC; /* Add an interrupt handler for ECC failures */ if (cvmx_error_initialize(0 /* || CVMX_ERROR_FLAGS_ECC_SINGLE_BIT */)) cvmx_warn("cvmx_error_initialize() failed\n"); /* Enable PIP/IPD, POW, PKO, FPA, NAND, KEY, RAD, L2C, LMC, GMX, AGL, DFM, DFA, error handling interrupts. */ if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { int i; for (i = 0; i < 5; i++) { cvmx_interrupt_register(CVMX_IRQ_AGX0+i, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_AGX0+i); } cvmx_interrupt_register(CVMX_IRQ_NAND, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_NAND); cvmx_interrupt_register(CVMX_IRQ_MIO, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_MIO); cvmx_interrupt_register(CVMX_IRQ_FPA, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_FPA); cvmx_interrupt_register(CVMX_IRQ_IPD, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_IPD); cvmx_interrupt_register(CVMX_IRQ_PIP, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_PIP); cvmx_interrupt_register(CVMX_IRQ_POW, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_POW); cvmx_interrupt_register(CVMX_IRQ_L2C, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_L2C); cvmx_interrupt_register(CVMX_IRQ_PKO, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_PKO); cvmx_interrupt_register(CVMX_IRQ_ZIP, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_ZIP); cvmx_interrupt_register(CVMX_IRQ_RAD, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_RAD); cvmx_interrupt_register(CVMX_IRQ_KEY, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_KEY); /* Before enabling SLI interrupt clear any RML_TO interrupt */ if (cvmx_read_csr(CVMX_PEXP_SLI_INT_SUM) & 0x1) { cvmx_safe_printf("clearing pending SLI_INT_SUM[RML_TO] interrupt (ignore)\n"); cvmx_write_csr(CVMX_PEXP_SLI_INT_SUM, 1); } cvmx_interrupt_register(CVMX_IRQ_SLI, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_SLI); cvmx_interrupt_register(CVMX_IRQ_DPI, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_DPI); cvmx_interrupt_register(CVMX_IRQ_DFA, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_DFA); cvmx_interrupt_register(CVMX_IRQ_AGL, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_AGL); for (i = 0; i < 4; i++) { cvmx_interrupt_register(CVMX_IRQ_LMC0+i, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_LMC0+i); } cvmx_interrupt_register(CVMX_IRQ_DFM, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_DFM); cvmx_interrupt_register(CVMX_IRQ_RST, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_RST); cvmx_interrupt_register(CVMX_IRQ_ILK, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_ILK); } else { cvmx_interrupt_register(CVMX_IRQ_RML, __cvmx_interrupt_ecc, NULL); cvmx_interrupt_unmask_irq(CVMX_IRQ_RML); } cvmx_atomic_set32(&cvmx_interrupt_initialize_flag, 1); } while (!cvmx_atomic_get32(&cvmx_interrupt_initialize_flag)) ; /* Wait for first core to finish above. */ if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { cvmx_interrupt_unmask_irq(CVMX_IRQ_MIPS2); } else { cvmx_interrupt_unmask_irq(CVMX_IRQ_MIPS2); cvmx_interrupt_unmask_irq(CVMX_IRQ_MIPS3); } CVMX_ICACHE_INVALIDATE; /* Enable interrupts for each core (bit0 of COP0 Status) */ cvmx_interrupt_restore(1); } /** * Set the exception handler for all non interrupt sources. * * @param handler New exception handler * @return Old exception handler */ cvmx_interrupt_exception_t cvmx_interrupt_set_exception(cvmx_interrupt_exception_t handler) { cvmx_interrupt_exception_t result = cvmx_interrupt_state.exception_handler; cvmx_interrupt_state.exception_handler = handler; CVMX_SYNCWS; return result; } #endif /* !__U_BOOT__ */