summaryrefslogtreecommitdiff
path: root/cvmx-interrupt.c
diff options
context:
space:
mode:
Diffstat (limited to 'cvmx-interrupt.c')
-rw-r--r--cvmx-interrupt.c983
1 files changed, 864 insertions, 119 deletions
diff --git a/cvmx-interrupt.c b/cvmx-interrupt.c
index 9280e8d7d9a6b..f90412abec354 100644
--- a/cvmx-interrupt.c
+++ b/cvmx-interrupt.c
@@ -1,5 +1,5 @@
/***********************license start***************
- * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights
+ * Copyright (c) 2003-2010 Cavium Inc. (support@cavium.com). All rights
* reserved.
*
*
@@ -15,7 +15,7 @@
* disclaimer in the documentation and/or other materials provided
* with the distribution.
- * * Neither the name of Cavium Networks nor the names of
+ * * 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.
@@ -26,7 +26,7 @@
* countries.
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
- * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
+ * 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
@@ -37,18 +37,12 @@
* PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
***********************license end**************************************/
-
-
-
-
-
-
/**
* @file
*
* Interface to the Mips interrupts.
*
- * <hr>$Revision: 52004 $<hr>
+ * <hr>$Revision: 70030 $<hr>
*/
#ifndef __U_BOOT__
#if __GNUC__ >= 4
@@ -65,23 +59,34 @@
#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 "../../bootloader/u-boot/include/octeon_mem_map.h"
-
+#include "cvmx-app-hotplug.h"
+#include "cvmx-profiler.h"
+#ifndef __U_BOOT__
+# include <octeon_mem_map.h>
+#else
+# include <asm/arch/octeon_mem_map.h>
+#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
{
- cvmx_interrupt_func_t handlers[256]; /**< One function to call per interrupt */
- void * data[256]; /**< User data per interrupt */
+ struct __cvmx_interrupt_handler handlers[CVMX_IRQ_MAX];
cvmx_interrupt_exception_t exception_handler;
} cvmx_interrupt_state_t;
@@ -91,12 +96,14 @@ typedef struct
#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))
+#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",
@@ -197,7 +204,7 @@ static inline void print_reg64(const char *name, uint64_t reg)
*
* @param registers CPU register to dump
*/
-static void __cvmx_interrupt_dump_registers(uint64_t registers[32])
+static void __cvmx_interrupt_dump_registers(uint64_t *registers)
{
uint64_t r1, r2;
int reg;
@@ -228,12 +235,13 @@ static void __cvmx_interrupt_dump_registers(uint64_t registers[32])
#ifndef __U_BOOT__
static
#endif /* __U_BOOT__ */
-void __cvmx_interrupt_default_exception_handler(uint64_t registers[32])
+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
@@ -248,36 +256,43 @@ void __cvmx_interrupt_default_exception_handler(uint64_t registers[32])
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;
+ 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));
- }
+ /* 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__ */
}
@@ -291,17 +306,69 @@ void __cvmx_interrupt_default_exception_handler(uint64_t registers[32])
* @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[32], void *user_arg)
+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 queres the status of the CIU and
+ * to the CIU. The handler queries the status of the CIU and
* calls the secondary handler for the CIU interrupt that
* occurred.
*
@@ -309,37 +376,194 @@ static void __cvmx_interrupt_default(int irq_number, uint64_t registers[32], voi
* @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[32], void *user_arg)
+static void __cvmx_interrupt_ciu(int irq_number, uint64_t *registers, void *user_arg)
{
- int ciu_offset = cvmx_get_core_num() * 2 + irq_number - 2;
- uint64_t irq_mask = cvmx_read_csr(CVMX_CIU_INTX_SUM0(ciu_offset)) & cvmx_read_csr(CVMX_CIU_INTX_EN0(ciu_offset));
- int irq = 8;
+ 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<sum2> 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;
+ }
+ }
- /* Handle EN0 sources */
- while (irq_mask)
- {
- if (irq_mask&1)
- {
- cvmx_interrupt_state.handlers[irq](irq, registers, cvmx_interrupt_state.data[irq]);
+ 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;
}
- irq_mask = irq_mask >> 1;
- irq++;
}
+}
+/**
+ * @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 */
- irq_mask = cvmx_read_csr(CVMX_CIU_INT_SUM1) & cvmx_read_csr(CVMX_CIU_INTX_EN1(ciu_offset));
- irq = 8 + 64;
- while (irq_mask)
- {
- if (irq_mask&1)
- {
- cvmx_interrupt_state.handlers[irq](irq, registers, cvmx_interrupt_state.data[irq]);
+ 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;
}
- irq_mask = irq_mask >> 1;
- irq++;
+ 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));
}
@@ -351,7 +575,7 @@ static void __cvmx_interrupt_ciu(int irq_number, uint64_t registers[32], void *u
* @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[32], void *user_arg)
+static void __cvmx_interrupt_ecc(int irq_number, uint64_t *registers, void *user_arg)
{
cvmx_error_poll();
}
@@ -364,8 +588,8 @@ static void __cvmx_interrupt_ecc(int irq_number, uint64_t registers[32], void *u
* Registers 0-31 are standard MIPS, others specific to this routine
* @return
*/
-EXTERN_ASM void cvmx_interrupt_do_irq(uint64_t registers[35]);
-void cvmx_interrupt_do_irq(uint64_t registers[35])
+void cvmx_interrupt_do_irq(uint64_t *registers);
+void cvmx_interrupt_do_irq(uint64_t *registers)
{
uint64_t mask;
uint64_t cause;
@@ -390,26 +614,49 @@ void cvmx_interrupt_do_irq(uint64_t registers[35])
/* 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))
{
- i = cvmx_get_core_num();
CVMX_MF_CACHE_ERR(cache_err);
/* Use copy of DCACHE_ERR register that early exception stub read */
- if (registers[34] & 0x1)
+ if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN5XXX))
{
- 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);
- uint64_t dcache_err = 0;
- CVMX_MT_DCACHE_ERR(dcache_err);
+ 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 (cache_err & 0x1)
+ else
{
- cvmx_safe_printf("Icache error detected: core: %d, set: %d, way : %d\n", i, (int)(cache_err >> 5) & 0x3f, (int)(cache_err >> 7) & 0x3);
- cache_err = 0;
- CVMX_MT_CACHE_ERR(cache_err);
+ 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);
}
- else
- cvmx_safe_printf("Cache error exception: core %d\n", i);
}
if ((cause & 0x7c) != 0)
@@ -429,7 +676,8 @@ void cvmx_interrupt_do_irq(uint64_t registers[35])
{
if (mask & (1<<i))
{
- cvmx_interrupt_state.handlers[i](i, registers, cvmx_interrupt_state.data[i]);
+ struct __cvmx_interrupt_handler *h = cvmx_interrupt_state.handlers + i;
+ h->handler(i, registers, h->data);
goto return_from_interrupt;
}
}
@@ -442,6 +690,399 @@ return_from_interrupt:
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
@@ -454,21 +1095,77 @@ void cvmx_interrupt_initialize(void)
cvmx_sysinfo_t *sys_info_ptr = cvmx_sysinfo_get();
int i;
- /* Disable all CIU interrupts by default */
- cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), 0);
- cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2+1), 0);
- cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), 0);
- cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2+1), 0);
+ 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 (cvmx_coremask_first_core(sys_info_ptr->core_mask))
+ if (OCTEON_IS_MODEL(OCTEON_CN68XX))
{
- cvmx_interrupt_state.exception_handler = __cvmx_interrupt_default_exception_handler;
+ 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);
- for (i=0; i<256; i++)
- {
- cvmx_interrupt_state.handlers[i] = __cvmx_interrupt_default;
- cvmx_interrupt_state.data[i] = NULL;
- }
+ /* 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);
@@ -483,47 +1180,95 @@ void cvmx_interrupt_initialize(void)
cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 24), 0);
CVMX_SYNC;
- /* Add an interrupt handlers for chained CIU interrupts */
- cvmx_interrupt_register(CVMX_IRQ_CIU0, __cvmx_interrupt_ciu, NULL);
- cvmx_interrupt_register(CVMX_IRQ_CIU1, __cvmx_interrupt_ciu, NULL);
-
/* Add an interrupt handler for ECC failures */
- cvmx_interrupt_register(CVMX_IRQ_RML, __cvmx_interrupt_ecc, NULL);
-
if (cvmx_error_initialize(0 /* || CVMX_ERROR_FLAGS_ECC_SINGLE_BIT */))
cvmx_warn("cvmx_error_initialize() failed\n");
- cvmx_interrupt_unmask_irq(CVMX_IRQ_RML);
+
+ /* 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_interrupt_unmask_irq(CVMX_IRQ_CIU0);
- cvmx_interrupt_unmask_irq(CVMX_IRQ_CIU1);
CVMX_ICACHE_INVALIDATE;
/* Enable interrupts for each core (bit0 of COP0 Status) */
- uint32_t mask;
- asm volatile (
- "mfc0 %0,$12,0\n"
- "ori %0, %0, 1\n"
- "mtc0 %0,$12,0\n"
- : "=r" (mask));
+ cvmx_interrupt_restore(1);
}
-/**
- * Register an interrupt handler for the specified interrupt number.
- *
- * @param irq_number Interrupt number to register for (0-135) 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(cvmx_irq_t irq_number, cvmx_interrupt_func_t func, void *user_arg)
-{
- cvmx_interrupt_state.handlers[irq_number] = func;
- cvmx_interrupt_state.data[irq_number] = user_arg;
- CVMX_SYNCWS;
-}
-
/**
* Set the exception handler for all non interrupt sources.