1210284Sjmallett/***********************license start***************
2232812Sjmallett * Copyright (c) 2003-2010  Cavium Inc. (support@cavium.com). All rights
3215990Sjmallett * reserved.
4210284Sjmallett *
5210284Sjmallett *
6215990Sjmallett * Redistribution and use in source and binary forms, with or without
7215990Sjmallett * modification, are permitted provided that the following conditions are
8215990Sjmallett * met:
9210284Sjmallett *
10215990Sjmallett *   * Redistributions of source code must retain the above copyright
11215990Sjmallett *     notice, this list of conditions and the following disclaimer.
12210284Sjmallett *
13215990Sjmallett *   * Redistributions in binary form must reproduce the above
14215990Sjmallett *     copyright notice, this list of conditions and the following
15215990Sjmallett *     disclaimer in the documentation and/or other materials provided
16215990Sjmallett *     with the distribution.
17215990Sjmallett
18232812Sjmallett *   * Neither the name of Cavium Inc. nor the names of
19215990Sjmallett *     its contributors may be used to endorse or promote products
20215990Sjmallett *     derived from this software without specific prior written
21215990Sjmallett *     permission.
22215990Sjmallett
23215990Sjmallett * This Software, including technical data, may be subject to U.S. export  control
24215990Sjmallett * laws, including the U.S. Export Administration Act and its  associated
25215990Sjmallett * regulations, and may be subject to export or import  regulations in other
26215990Sjmallett * countries.
27215990Sjmallett
28215990Sjmallett * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29232812Sjmallett * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30215990Sjmallett * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31215990Sjmallett * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32215990Sjmallett * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33215990Sjmallett * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34215990Sjmallett * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35215990Sjmallett * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36215990Sjmallett * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37215990Sjmallett * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38210284Sjmallett ***********************license end**************************************/
39210284Sjmallett
40210284Sjmallett/**
41210284Sjmallett * @file
42210284Sjmallett *
43210284Sjmallett * Interface to the Mips interrupts.
44210284Sjmallett *
45232812Sjmallett * <hr>$Revision: 70030 $<hr>
46210284Sjmallett */
47215990Sjmallett#ifndef __U_BOOT__
48210284Sjmallett#if __GNUC__ >= 4
49210284Sjmallett/* Backtrace is only available with the new toolchain.  */
50210284Sjmallett#include <execinfo.h>
51210284Sjmallett#endif
52215990Sjmallett#endif  /* __U_BOOT__ */
53210284Sjmallett#include "cvmx-config.h"
54210284Sjmallett#include "cvmx.h"
55210284Sjmallett#include "cvmx-interrupt.h"
56210284Sjmallett#include "cvmx-sysinfo.h"
57210284Sjmallett#include "cvmx-uart.h"
58210284Sjmallett#include "cvmx-pow.h"
59210284Sjmallett#include "cvmx-ebt3000.h"
60210284Sjmallett#include "cvmx-coremask.h"
61210284Sjmallett#include "cvmx-spinlock.h"
62232812Sjmallett#include "cvmx-atomic.h"
63210284Sjmallett#include "cvmx-app-init.h"
64215990Sjmallett#include "cvmx-error.h"
65232812Sjmallett#include "cvmx-app-hotplug.h"
66232812Sjmallett#include "cvmx-profiler.h"
67232812Sjmallett#ifndef __U_BOOT__
68232812Sjmallett# include <octeon_mem_map.h>
69232812Sjmallett#else
70232812Sjmallett# include <asm/arch/octeon_mem_map.h>
71232812Sjmallett#endif
72210284SjmallettEXTERN_ASM void cvmx_interrupt_stage1(void);
73215990SjmallettEXTERN_ASM void cvmx_debug_handler_stage1(void);
74210284SjmallettEXTERN_ASM void cvmx_interrupt_cache_error(void);
75210284Sjmallett
76215990Sjmallettint cvmx_interrupt_in_isr = 0;
77215990Sjmallett
78232812Sjmallettstruct __cvmx_interrupt_handler {
79232812Sjmallett    cvmx_interrupt_func_t handler;      /**< One function to call per interrupt */
80232812Sjmallett    void *data;                         /**< User data per interrupt */
81232812Sjmallett    int handler_data;                   /**< Used internally */
82232812Sjmallett};
83232812Sjmallett
84210284Sjmallett/**
85210284Sjmallett * Internal status the interrupt registration
86210284Sjmallett */
87210284Sjmalletttypedef struct
88210284Sjmallett{
89232812Sjmallett    struct __cvmx_interrupt_handler handlers[CVMX_IRQ_MAX];
90210284Sjmallett    cvmx_interrupt_exception_t exception_handler;
91210284Sjmallett} cvmx_interrupt_state_t;
92210284Sjmallett
93210284Sjmallett/**
94210284Sjmallett * Internal state the interrupt registration
95210284Sjmallett */
96215990Sjmallett#ifndef __U_BOOT__
97210284Sjmallettstatic CVMX_SHARED cvmx_interrupt_state_t cvmx_interrupt_state;
98210284Sjmallettstatic CVMX_SHARED cvmx_spinlock_t cvmx_interrupt_default_lock;
99232812Sjmallett/* Incremented once first core processing is finished. */
100232812Sjmallettstatic CVMX_SHARED int32_t cvmx_interrupt_initialize_flag;
101215990Sjmallett#endif  /* __U_BOOT__ */
102210284Sjmallett
103210284Sjmallett#define ULL unsigned long long
104210284Sjmallett
105232812Sjmallett#define HI32(data64)    ((uint32_t)(data64 >> 32))
106232812Sjmallett#define LO32(data64)    ((uint32_t)(data64 & 0xFFFFFFFF))
107210284Sjmallett
108215990Sjmallettstatic const char reg_names[][32] = { "r0","at","v0","v1","a0","a1","a2","a3",
109215990Sjmallett                                      "t0","t1","t2","t3","t4","t5","t6","t7",
110215990Sjmallett                                      "s0","s1","s2","s3","s4","s5", "s6","s7",
111215990Sjmallett                                      "t8","t9", "k0","k1","gp","sp","s8","ra" };
112210284Sjmallett
113210284Sjmallett/**
114215990Sjmallett * version of printf that works better in exception context.
115215990Sjmallett *
116215990Sjmallett * @param format
117215990Sjmallett */
118215990Sjmallettvoid cvmx_safe_printf(const char *format, ...)
119215990Sjmallett{
120215990Sjmallett    char buffer[256];
121215990Sjmallett    char *ptr = buffer;
122215990Sjmallett    int count;
123215990Sjmallett    va_list args;
124215990Sjmallett
125215990Sjmallett    va_start(args, format);
126215990Sjmallett#ifndef __U_BOOT__
127215990Sjmallett    count = vsnprintf(buffer, sizeof(buffer), format, args);
128215990Sjmallett#else
129215990Sjmallett    count = vsprintf(buffer, format, args);
130215990Sjmallett#endif
131215990Sjmallett    va_end(args);
132215990Sjmallett
133215990Sjmallett    while (count-- > 0)
134215990Sjmallett    {
135215990Sjmallett        cvmx_uart_lsr_t lsrval;
136215990Sjmallett
137215990Sjmallett        /* Spin until there is room */
138215990Sjmallett        do
139215990Sjmallett        {
140215990Sjmallett            lsrval.u64 = cvmx_read_csr(CVMX_MIO_UARTX_LSR(0));
141215990Sjmallett#if !defined(CONFIG_OCTEON_SIM_SPEED)
142215990Sjmallett            if (lsrval.s.temt == 0)
143215990Sjmallett                cvmx_wait(10000);   /* Just to reduce the load on the system */
144215990Sjmallett#endif
145215990Sjmallett        }
146215990Sjmallett        while (lsrval.s.temt == 0);
147215990Sjmallett
148215990Sjmallett        if (*ptr == '\n')
149215990Sjmallett            cvmx_write_csr(CVMX_MIO_UARTX_THR(0), '\r');
150215990Sjmallett        cvmx_write_csr(CVMX_MIO_UARTX_THR(0), *ptr++);
151215990Sjmallett    }
152215990Sjmallett}
153215990Sjmallett
154215990Sjmallett/* Textual descriptions of cause codes */
155215990Sjmallettstatic const char cause_names[][128] = {
156215990Sjmallett        /*  0 */ "Interrupt",
157215990Sjmallett        /*  1 */ "TLB modification",
158215990Sjmallett        /*  2 */ "tlb load/fetch",
159215990Sjmallett        /*  3 */ "tlb store",
160215990Sjmallett        /*  4 */ "address exc, load/fetch",
161215990Sjmallett        /*  5 */ "address exc, store",
162215990Sjmallett        /*  6 */ "bus error, instruction fetch",
163215990Sjmallett        /*  7 */ "bus error, load/store",
164215990Sjmallett        /*  8 */ "syscall",
165215990Sjmallett        /*  9 */ "breakpoint",
166215990Sjmallett        /* 10 */ "reserved instruction",
167215990Sjmallett        /* 11 */ "cop unusable",
168215990Sjmallett        /* 12 */ "arithmetic overflow",
169215990Sjmallett        /* 13 */ "trap",
170215990Sjmallett        /* 14 */ "",
171215990Sjmallett        /* 15 */ "floating point exc",
172215990Sjmallett        /* 16 */ "",
173215990Sjmallett        /* 17 */ "",
174215990Sjmallett        /* 18 */ "cop2 exception",
175215990Sjmallett        /* 19 */ "",
176215990Sjmallett        /* 20 */ "",
177215990Sjmallett        /* 21 */ "",
178215990Sjmallett        /* 22 */ "mdmx unusable",
179215990Sjmallett        /* 23 */ "watch",
180215990Sjmallett        /* 24 */ "machine check",
181215990Sjmallett        /* 25 */ "",
182215990Sjmallett        /* 26 */ "",
183215990Sjmallett        /* 27 */ "",
184215990Sjmallett        /* 28 */ "",
185215990Sjmallett        /* 29 */ "",
186215990Sjmallett        /* 30 */ "cache error",
187215990Sjmallett        /* 31 */ ""
188215990Sjmallett};
189215990Sjmallett
190215990Sjmallett/**
191210284Sjmallett * @INTERNAL
192215990Sjmallett * print_reg64
193215990Sjmallett * @param name   Name of the value to print
194215990Sjmallett * @param reg    Value to print
195215990Sjmallett */
196215990Sjmallettstatic inline void print_reg64(const char *name, uint64_t reg)
197215990Sjmallett{
198215990Sjmallett    cvmx_safe_printf("%16s: 0x%08x%08x\n", name, (unsigned int)HI32(reg),(unsigned int)LO32(reg));
199215990Sjmallett}
200215990Sjmallett
201215990Sjmallett/**
202215990Sjmallett * @INTERNAL
203210284Sjmallett * Dump all useful registers to the console
204210284Sjmallett *
205210284Sjmallett * @param registers CPU register to dump
206210284Sjmallett */
207232812Sjmallettstatic void __cvmx_interrupt_dump_registers(uint64_t *registers)
208210284Sjmallett{
209215990Sjmallett    uint64_t r1, r2;
210215990Sjmallett    int reg;
211210284Sjmallett    for (reg=0; reg<16; reg++)
212210284Sjmallett    {
213215990Sjmallett        r1 = registers[reg]; r2 = registers[reg+16];
214215990Sjmallett        cvmx_safe_printf("%3s ($%02d): 0x%08x%08x \t %3s ($%02d): 0x%08x%08x\n",
215215990Sjmallett                           reg_names[reg], reg, (unsigned int)HI32(r1), (unsigned int)LO32(r1),
216215990Sjmallett                           reg_names[reg+16], reg+16, (unsigned int)HI32(r2), (unsigned int)LO32(r2));
217210284Sjmallett    }
218215990Sjmallett    CVMX_MF_COP0 (r1, COP0_CAUSE);
219215990Sjmallett    print_reg64 ("COP0_CAUSE", r1);
220215990Sjmallett    CVMX_MF_COP0 (r2, COP0_STATUS);
221215990Sjmallett    print_reg64 ("COP0_STATUS", r2);
222215990Sjmallett    CVMX_MF_COP0 (r1, COP0_BADVADDR);
223215990Sjmallett    print_reg64 ("COP0_BADVADDR", r1);
224215990Sjmallett    CVMX_MF_COP0 (r2, COP0_EPC);
225215990Sjmallett    print_reg64 ("COP0_EPC", r2);
226210284Sjmallett}
227210284Sjmallett
228210284Sjmallett/**
229210284Sjmallett * @INTERNAL
230210284Sjmallett * Default exception handler. Prints out the exception
231210284Sjmallett * cause decode and all relevant registers.
232210284Sjmallett *
233210284Sjmallett * @param registers Registers at time of the exception
234210284Sjmallett */
235215990Sjmallett#ifndef __U_BOOT__
236215990Sjmallettstatic
237215990Sjmallett#endif  /* __U_BOOT__ */
238232812Sjmallettvoid __cvmx_interrupt_default_exception_handler(uint64_t *registers)
239210284Sjmallett{
240210284Sjmallett    uint64_t trap_print_cause;
241215990Sjmallett    const char *str;
242232812Sjmallett#ifndef __U_BOOT__
243232812Sjmallett    int modified_zero_pc = 0;
244210284Sjmallett
245210284Sjmallett    ebt3000_str_write("Trap");
246210284Sjmallett    cvmx_spinlock_lock(&cvmx_interrupt_default_lock);
247215990Sjmallett#endif
248215990Sjmallett    CVMX_MF_COP0 (trap_print_cause, COP0_CAUSE);
249215990Sjmallett    str = cause_names [(trap_print_cause >> 2) & 0x1f];
250215990Sjmallett    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");
251210284Sjmallett    cvmx_safe_printf("******************************************************************\n");
252215990Sjmallett    __cvmx_interrupt_dump_registers(registers);
253210284Sjmallett
254215990Sjmallett#ifndef __U_BOOT__
255210284Sjmallett
256210284Sjmallett    cvmx_safe_printf("******************************************************************\n");
257210284Sjmallett#if __GNUC__ >= 4 && !defined(OCTEON_DISABLE_BACKTRACE)
258210284Sjmallett    cvmx_safe_printf("Backtrace:\n\n");
259232812Sjmallett    if (registers[35] == 0) {
260232812Sjmallett	modified_zero_pc = 1;
261232812Sjmallett	/* If PC is zero we probably did jalr $zero, in which case $31 - 8 is the call site. */
262232812Sjmallett	registers[35] = registers[31] - 8;
263232812Sjmallett    }
264210284Sjmallett    __octeon_print_backtrace_func ((__octeon_backtrace_printf_t)cvmx_safe_printf);
265232812Sjmallett    if (modified_zero_pc)
266232812Sjmallett	registers[35] = 0;
267210284Sjmallett    cvmx_safe_printf("******************************************************************\n");
268210284Sjmallett#endif
269210284Sjmallett
270210284Sjmallett    cvmx_spinlock_unlock(&cvmx_interrupt_default_lock);
271210284Sjmallett
272210284Sjmallett    if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
273232812Sjmallett        CVMX_BREAK;
274210284Sjmallett
275210284Sjmallett    while (1)
276210284Sjmallett    {
277232812Sjmallett        /* Interrupts are suppressed when we are in the exception
278232812Sjmallett           handler (because of SR[EXL]).  Spin and poll the uart
279232812Sjmallett           status and see if the debugger is trying to stop us. */
280232812Sjmallett        cvmx_uart_lsr_t lsrval;
281232812Sjmallett        lsrval.u64 = cvmx_read_csr(CVMX_MIO_UARTX_LSR(cvmx_debug_uart));
282232812Sjmallett        if (lsrval.s.dr)
283232812Sjmallett        {
284232812Sjmallett            uint64_t tmp;
285232812Sjmallett            /* Pulse the MCD0 signal. */
286232812Sjmallett            asm volatile (
287232812Sjmallett            ".set push\n"
288232812Sjmallett            ".set noreorder\n"
289232812Sjmallett            ".set mips64\n"
290232812Sjmallett            "dmfc0 %0, $22\n"
291232812Sjmallett            "ori   %0, %0, 0x10\n"
292232812Sjmallett            "dmtc0 %0, $22\n"
293232812Sjmallett            ".set pop\n"
294232812Sjmallett            : "=r" (tmp));
295232812Sjmallett        }
296210284Sjmallett    }
297215990Sjmallett#endif /* __U_BOOT__ */
298210284Sjmallett}
299210284Sjmallett
300215990Sjmallett#ifndef __U_BOOT__
301210284Sjmallett/**
302210284Sjmallett * @INTERNAL
303210284Sjmallett * Default interrupt handler if the user doesn't register one.
304210284Sjmallett *
305210284Sjmallett * @param irq_number IRQ that caused this interrupt
306210284Sjmallett * @param registers  Register at the time of the interrupt
307210284Sjmallett * @param user_arg   Unused optional user data
308210284Sjmallett */
309232812Sjmallettstatic void __cvmx_interrupt_default(int irq_number, uint64_t *registers, void *user_arg)
310210284Sjmallett{
311210284Sjmallett    cvmx_safe_printf("cvmx_interrupt_default: Received interrupt %d\n", irq_number);
312210284Sjmallett    __cvmx_interrupt_dump_registers(registers);
313210284Sjmallett}
314210284Sjmallett
315232812Sjmallett/**
316232812Sjmallett * Map a ciu bit to an irq number.  0xff for invalid.
317232812Sjmallett * 0-63 for en0.
318232812Sjmallett * 64-127 for en1.
319232812Sjmallett */
320210284Sjmallett
321232812Sjmallettstatic CVMX_SHARED uint8_t cvmx_ciu_to_irq[8][64];
322232812Sjmallett#define cvmx_ciu_en0_to_irq cvmx_ciu_to_irq[0]
323232812Sjmallett#define cvmx_ciu_en1_to_irq cvmx_ciu_to_irq[1]
324232812Sjmallett#define cvmx_ciu2_wrkq_to_irq cvmx_ciu_to_irq[0]
325232812Sjmallett#define cvmx_ciu2_wdog_to_irq cvmx_ciu_to_irq[1]
326232812Sjmallett#define cvmx_ciu2_rml_to_irq cvmx_ciu_to_irq[2]
327232812Sjmallett#define cvmx_ciu2_mio_to_irq cvmx_ciu_to_irq[3]
328232812Sjmallett#define cvmx_ciu2_io_to_irq cvmx_ciu_to_irq[4]
329232812Sjmallett#define cvmx_ciu2_mem_to_irq cvmx_ciu_to_irq[5]
330232812Sjmallett#define cvmx_ciu2_eth_to_irq cvmx_ciu_to_irq[6]
331232812Sjmallett#define cvmx_ciu2_gpio_to_irq cvmx_ciu_to_irq[7]
332232812Sjmallett
333232812Sjmallettstatic CVMX_SHARED uint8_t cvmx_ciu2_mbox_to_irq[64];
334232812Sjmallettstatic CVMX_SHARED uint8_t cvmx_ciu_61xx_timer_to_irq[64];
335232812Sjmallett
336232812Sjmallettstatic void __cvmx_interrupt_set_mapping(int irq, unsigned int en, unsigned int bit)
337232812Sjmallett{
338232812Sjmallett    cvmx_interrupt_state.handlers[irq].handler_data = (en << 6) | bit;
339232812Sjmallett    if (en <= 7)
340232812Sjmallett        cvmx_ciu_to_irq[en][bit] = irq;
341232812Sjmallett    else if (en == 8)
342232812Sjmallett        cvmx_ciu_61xx_timer_to_irq[bit] = irq;
343232812Sjmallett    else
344232812Sjmallett        cvmx_ciu2_mbox_to_irq[bit] = irq;
345232812Sjmallett}
346232812Sjmallett
347232812Sjmallettstatic uint64_t cvmx_interrupt_ciu_en0_mirror;
348232812Sjmallettstatic uint64_t cvmx_interrupt_ciu_en1_mirror;
349232812Sjmallettstatic uint64_t cvmx_interrupt_ciu_61xx_timer_mirror;
350232812Sjmallett
351210284Sjmallett/**
352210284Sjmallett * @INTERNAL
353232812Sjmallett * Called for all Performance Counter interrupts. Handler for
354232812Sjmallett * interrupt line 6
355232812Sjmallett *
356232812Sjmallett * @param irq_number Interrupt number that we're being called for
357232812Sjmallett * @param registers  Registers at the time of the interrupt
358232812Sjmallett * @param user_arg   Unused user argument*
359232812Sjmallett */
360232812Sjmallettstatic void __cvmx_interrupt_perf(int irq_number, uint64_t *registers, void *user_arg)
361232812Sjmallett{
362232812Sjmallett    uint64_t perf_counter;
363232812Sjmallett    CVMX_MF_COP0(perf_counter, COP0_PERFVALUE0);
364232812Sjmallett    if (perf_counter & (1ull << 63))
365232812Sjmallett        cvmx_collect_sample();
366232812Sjmallett}
367232812Sjmallett
368232812Sjmallett/**
369232812Sjmallett * @INTERNAL
370210284Sjmallett * Handler for interrupt lines 2 and 3. These are directly tied
371232812Sjmallett * to the CIU. The handler queries the status of the CIU and
372210284Sjmallett * calls the secondary handler for the CIU interrupt that
373210284Sjmallett * occurred.
374210284Sjmallett *
375210284Sjmallett * @param irq_number Interrupt number that fired (2 or 3)
376210284Sjmallett * @param registers  Registers at the time of the interrupt
377210284Sjmallett * @param user_arg   Unused user argument
378210284Sjmallett */
379232812Sjmallettstatic void __cvmx_interrupt_ciu(int irq_number, uint64_t *registers, void *user_arg)
380210284Sjmallett{
381232812Sjmallett    int ciu_offset;
382232812Sjmallett    uint64_t irq_mask;
383232812Sjmallett    uint64_t irq;
384232812Sjmallett    int bit;
385232812Sjmallett    int core = cvmx_get_core_num();
386210284Sjmallett
387232812Sjmallett    if (irq_number == CVMX_IRQ_MIPS2) {
388232812Sjmallett        /* Handle EN0 sources */
389232812Sjmallett        ciu_offset = core * 2;
390232812Sjmallett        irq_mask = cvmx_read_csr(CVMX_CIU_INTX_SUM0(ciu_offset)) & cvmx_interrupt_ciu_en0_mirror;
391232812Sjmallett        CVMX_DCLZ(bit, irq_mask);
392232812Sjmallett        bit = 63 - bit;
393232812Sjmallett        /* If ciu_int_sum1<sum2> is set, means its a timer interrupt */
394232812Sjmallett        if (bit == 51 && (OCTEON_IS_MODEL(OCTEON_CN61XX) || OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_2))) {
395232812Sjmallett            uint64_t irq_mask;
396232812Sjmallett            int bit;
397232812Sjmallett            irq_mask = cvmx_read_csr(CVMX_CIU_SUM2_PPX_IP2(core)) & cvmx_interrupt_ciu_61xx_timer_mirror;
398232812Sjmallett            CVMX_DCLZ(bit, irq_mask);
399232812Sjmallett            bit = 63 - bit;
400232812Sjmallett            /* Handle TIMER(4..9) interrupts */
401232812Sjmallett            if (bit <= 9 && bit >= 4) {
402232812Sjmallett                uint64_t irq = cvmx_ciu_61xx_timer_to_irq[bit];
403232812Sjmallett                if (cvmx_unlikely(irq == 0xff)) {
404232812Sjmallett                    /* No mapping */
405232812Sjmallett                    cvmx_interrupt_ciu_61xx_timer_mirror &= ~(1ull << bit);
406232812Sjmallett                    cvmx_write_csr(CVMX_CIU_EN2_PPX_IP2(core), cvmx_interrupt_ciu_61xx_timer_mirror);
407232812Sjmallett                    return;
408232812Sjmallett                }
409232812Sjmallett                struct __cvmx_interrupt_handler *h = cvmx_interrupt_state.handlers + irq;
410232812Sjmallett                h->handler(irq, registers, h->data);
411232812Sjmallett                return;
412232812Sjmallett            }
413232812Sjmallett        }
414232812Sjmallett
415232812Sjmallett        if (bit >= 0) {
416232812Sjmallett            irq = cvmx_ciu_en0_to_irq[bit];
417232812Sjmallett            if (cvmx_unlikely(irq == 0xff)) {
418232812Sjmallett                /* No mapping. */
419232812Sjmallett                cvmx_interrupt_ciu_en0_mirror &= ~(1ull << bit);
420232812Sjmallett                cvmx_write_csr(CVMX_CIU_INTX_EN0(ciu_offset), cvmx_interrupt_ciu_en0_mirror);
421232812Sjmallett                return;
422232812Sjmallett            }
423232812Sjmallett            struct __cvmx_interrupt_handler *h = cvmx_interrupt_state.handlers + irq;
424232812Sjmallett            h->handler(irq, registers, h->data);
425210284Sjmallett            return;
426210284Sjmallett        }
427232812Sjmallett    } else {
428232812Sjmallett        /* Handle EN1 sources */
429232812Sjmallett        ciu_offset = cvmx_get_core_num() * 2 + 1;
430232812Sjmallett        irq_mask = cvmx_read_csr(CVMX_CIU_INT_SUM1) & cvmx_interrupt_ciu_en1_mirror;
431232812Sjmallett        CVMX_DCLZ(bit, irq_mask);
432232812Sjmallett        bit = 63 - bit;
433232812Sjmallett        if (bit >= 0) {
434232812Sjmallett            irq = cvmx_ciu_en1_to_irq[bit];
435232812Sjmallett            if (cvmx_unlikely(irq == 0xff)) {
436232812Sjmallett                /* No mapping. */
437232812Sjmallett                cvmx_interrupt_ciu_en1_mirror &= ~(1ull << bit);
438232812Sjmallett                cvmx_write_csr(CVMX_CIU_INTX_EN1(ciu_offset), cvmx_interrupt_ciu_en1_mirror);
439232812Sjmallett                return;
440232812Sjmallett            }
441232812Sjmallett            struct __cvmx_interrupt_handler *h = cvmx_interrupt_state.handlers + irq;
442232812Sjmallett            h->handler(irq, registers, h->data);
443232812Sjmallett            return;
444232812Sjmallett        }
445210284Sjmallett    }
446232812Sjmallett}
447210284Sjmallett
448232812Sjmallett/**
449232812Sjmallett * @INTERNAL
450232812Sjmallett * Handler for interrupt line 3, the DPI_DMA will have different value
451232812Sjmallett * per core, all other fields values are identical for different cores.
452232812Sjmallett *  These are directly tied to the CIU. The handler queries the status of
453232812Sjmallett * the CIU and calls the secondary handler for the CIU interrupt that
454232812Sjmallett * occurred.
455232812Sjmallett *
456232812Sjmallett * @param irq_number Interrupt number that fired (2 or 3)
457232812Sjmallett * @param registers  Registers at the time of the interrupt
458232812Sjmallett * @param user_arg   Unused user argument
459232812Sjmallett */
460232812Sjmallettstatic void __cvmx_interrupt_ciu_cn61xx(int irq_number, uint64_t *registers, void *user_arg)
461232812Sjmallett{
462210284Sjmallett    /* Handle EN1 sources */
463232812Sjmallett    int core = cvmx_get_core_num();
464232812Sjmallett    int ciu_offset;
465232812Sjmallett    uint64_t irq_mask;
466232812Sjmallett    uint64_t irq;
467232812Sjmallett    int bit;
468232812Sjmallett
469232812Sjmallett    ciu_offset = core * 2 + 1;
470232812Sjmallett    irq_mask = cvmx_read_csr(CVMX_CIU_SUM1_PPX_IP3(core)) & cvmx_interrupt_ciu_en1_mirror;
471232812Sjmallett    CVMX_DCLZ(bit, irq_mask);
472232812Sjmallett    bit = 63 - bit;
473232812Sjmallett    if (bit >= 0) {
474232812Sjmallett        irq = cvmx_ciu_en1_to_irq[bit];
475232812Sjmallett        if (cvmx_unlikely(irq == 0xff)) {
476232812Sjmallett            /* No mapping. */
477232812Sjmallett            cvmx_interrupt_ciu_en1_mirror &= ~(1ull << bit);
478232812Sjmallett            cvmx_write_csr(CVMX_CIU_INTX_EN1(ciu_offset), cvmx_interrupt_ciu_en1_mirror);
479210284Sjmallett            return;
480210284Sjmallett        }
481232812Sjmallett        struct __cvmx_interrupt_handler *h = cvmx_interrupt_state.handlers + irq;
482232812Sjmallett        h->handler(irq, registers, h->data);
483232812Sjmallett        return;
484210284Sjmallett    }
485210284Sjmallett}
486210284Sjmallett
487232812Sjmallett/**
488232812Sjmallett * @INTERNAL
489232812Sjmallett * Handler for interrupt line 2 on 68XX. These are directly tied
490232812Sjmallett * to the CIU2. The handler queries the status of the CIU and
491232812Sjmallett * calls the secondary handler for the CIU interrupt that
492232812Sjmallett * occurred.
493232812Sjmallett *
494232812Sjmallett * @param irq_number Interrupt number that fired (2 or 3)
495232812Sjmallett * @param registers  Registers at the time of the interrupt
496232812Sjmallett * @param user_arg   Unused user argument
497232812Sjmallett */
498232812Sjmallettstatic void __cvmx_interrupt_ciu2(int irq_number, uint64_t *registers, void *user_arg)
499232812Sjmallett{
500232812Sjmallett    int sum_bit, src_bit;
501232812Sjmallett    uint64_t irq;
502232812Sjmallett    uint64_t src_reg, src_val;
503232812Sjmallett    struct __cvmx_interrupt_handler *h;
504232812Sjmallett    int core = cvmx_get_core_num();
505232812Sjmallett    uint64_t sum = cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(core));
506210284Sjmallett
507232812Sjmallett    CVMX_DCLZ(sum_bit, sum);
508232812Sjmallett    sum_bit = 63 - sum_bit;
509232812Sjmallett
510232812Sjmallett    if (sum_bit >= 0) {
511232812Sjmallett        switch (sum_bit) {
512232812Sjmallett        case 63:
513232812Sjmallett        case 62:
514232812Sjmallett        case 61:
515232812Sjmallett        case 60:
516232812Sjmallett            irq = cvmx_ciu2_mbox_to_irq[sum_bit - 60];
517232812Sjmallett            if (cvmx_unlikely(irq == 0xff)) {
518232812Sjmallett                /* No mapping. */
519232812Sjmallett                uint64_t mask_reg = CVMX_CIU2_EN_PPX_IP2_MBOX_W1C(core);
520232812Sjmallett                cvmx_write_csr(mask_reg, 1ull << (sum_bit - 60));
521232812Sjmallett                break;
522232812Sjmallett            }
523232812Sjmallett            h = cvmx_interrupt_state.handlers + irq;
524232812Sjmallett            h->handler(irq, registers, h->data);
525232812Sjmallett            break;
526232812Sjmallett
527232812Sjmallett        case 7:
528232812Sjmallett        case 6:
529232812Sjmallett        case 5:
530232812Sjmallett        case 4:
531232812Sjmallett        case 3:
532232812Sjmallett        case 2:
533232812Sjmallett        case 1:
534232812Sjmallett        case 0:
535232812Sjmallett            src_reg = CVMX_CIU2_SRC_PPX_IP2_WRKQ(core) + (0x1000 * sum_bit);
536232812Sjmallett            src_val = cvmx_read_csr(src_reg);
537232812Sjmallett            if (!src_val)
538232812Sjmallett                break;
539232812Sjmallett            CVMX_DCLZ(src_bit, src_val);
540232812Sjmallett            src_bit = 63 - src_bit;
541232812Sjmallett            irq = cvmx_ciu_to_irq[sum_bit][src_bit];
542232812Sjmallett            if (cvmx_unlikely(irq == 0xff)) {
543232812Sjmallett                /* No mapping. */
544232812Sjmallett                uint64_t mask_reg = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(core) + (0x1000 * sum_bit);
545232812Sjmallett                cvmx_write_csr(mask_reg, 1ull << src_bit);
546232812Sjmallett                break;
547232812Sjmallett            }
548232812Sjmallett            h = cvmx_interrupt_state.handlers + irq;
549232812Sjmallett            h->handler(irq, registers, h->data);
550232812Sjmallett            break;
551232812Sjmallett
552232812Sjmallett        default:
553232812Sjmallett            cvmx_safe_printf("Unknown CIU2 bit: %d\n", sum_bit);
554232812Sjmallett            break;
555232812Sjmallett        }
556232812Sjmallett    }
557232812Sjmallett    /* Clear the source to reduce the chance for spurious interrupts.  */
558232812Sjmallett
559232812Sjmallett    /* CN68XX has an CIU-15786 errata that accessing the ACK registers
560232812Sjmallett     * can stop interrupts from propagating
561232812Sjmallett     */
562232812Sjmallett
563232812Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN68XX))
564232812Sjmallett        cvmx_read_csr(CVMX_CIU2_INTR_CIU_READY);
565232812Sjmallett    else
566232812Sjmallett        cvmx_read_csr(CVMX_CIU2_ACK_PPX_IP2(core));
567232812Sjmallett}
568232812Sjmallett
569232812Sjmallett
570210284Sjmallett/**
571210284Sjmallett * @INTERNAL
572210284Sjmallett * Called for all RML interrupts. This is usually an ECC error
573210284Sjmallett *
574210284Sjmallett * @param irq_number Interrupt number that we're being called for
575210284Sjmallett * @param registers  Registers at the time of the interrupt
576210284Sjmallett * @param user_arg   Unused user argument
577210284Sjmallett */
578232812Sjmallettstatic void __cvmx_interrupt_ecc(int irq_number, uint64_t *registers, void *user_arg)
579210284Sjmallett{
580215990Sjmallett    cvmx_error_poll();
581210284Sjmallett}
582210284Sjmallett
583210284Sjmallett
584210284Sjmallett/**
585210284Sjmallett * Process an interrupt request
586210284Sjmallett *
587210284Sjmallett * @param registers Registers at time of interrupt / exception
588210284Sjmallett * Registers 0-31 are standard MIPS, others specific to this routine
589210284Sjmallett * @return
590210284Sjmallett */
591232812Sjmallettvoid cvmx_interrupt_do_irq(uint64_t *registers);
592232812Sjmallettvoid cvmx_interrupt_do_irq(uint64_t *registers)
593210284Sjmallett{
594210284Sjmallett    uint64_t        mask;
595210284Sjmallett    uint64_t        cause;
596210284Sjmallett    uint64_t        status;
597210284Sjmallett    uint64_t        cache_err;
598210284Sjmallett    int             i;
599210284Sjmallett    uint32_t exc_vec;
600210284Sjmallett    /* Determine the cause of the interrupt */
601210284Sjmallett    asm volatile ("dmfc0 %0,$13,0" : "=r" (cause));
602210284Sjmallett    asm volatile ("dmfc0 %0,$12,0" : "=r" (status));
603215990Sjmallett    /* In case of exception, clear all interrupts to avoid recursive interrupts.
604215990Sjmallett       Also clear EXL bit to display the correct PC value. */
605215990Sjmallett    if ((cause & 0x7c) == 0)
606215990Sjmallett    {
607215990Sjmallett        asm volatile ("dmtc0 %0, $12, 0" : : "r" (status & ~(0xff02)));
608215990Sjmallett    }
609210284Sjmallett    /* The assembly stub at each exception vector saves its address in k1 when
610210284Sjmallett    ** it calls the stage 2 handler.  We use this to compute the exception vector
611210284Sjmallett    ** that brought us here */
612210284Sjmallett    exc_vec = (uint32_t)(registers[27] & 0x780);  /* Mask off bits we need to ignore */
613210284Sjmallett
614210284Sjmallett    /* Check for cache errors.  The cache errors go to a separate exception vector,
615210284Sjmallett    ** so we will only check these if we got here from a cache error exception, and
616210284Sjmallett    ** the ERL (error level) bit is set. */
617232812Sjmallett    i = cvmx_get_core_num();
618210284Sjmallett    if (exc_vec == 0x100 && (status & 0x4))
619210284Sjmallett    {
620210284Sjmallett        CVMX_MF_CACHE_ERR(cache_err);
621210284Sjmallett
622210284Sjmallett        /* Use copy of DCACHE_ERR register that early exception stub read */
623232812Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN5XXX))
624210284Sjmallett        {
625232812Sjmallett            if (registers[34] & 0x1)
626232812Sjmallett                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);
627232812Sjmallett            else if (cache_err & 0x1)
628232812Sjmallett                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);
629232812Sjmallett            else
630232812Sjmallett                cvmx_safe_printf("Cache error exception: core %d\n", i);
631210284Sjmallett        }
632232812Sjmallett        else
633210284Sjmallett        {
634232812Sjmallett            if (registers[34] & 0x1)
635232812Sjmallett                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);
636232812Sjmallett            else if (cache_err & 0x1)
637232812Sjmallett                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);
638232812Sjmallett            else
639232812Sjmallett                cvmx_safe_printf("Cache error exception: core %d\n", i);
640210284Sjmallett        }
641232812Sjmallett        CVMX_MT_DCACHE_ERR(1);
642232812Sjmallett        CVMX_MT_CACHE_ERR(0);
643210284Sjmallett    }
644210284Sjmallett
645232812Sjmallett    /* The bus error exceptions can occur due to DID timeout or write buffer,
646232812Sjmallett       check by reading COP0_CACHEERRD */
647232812Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_MODEL(OCTEON_CNF7XXX))
648232812Sjmallett    {
649232812Sjmallett        i = cvmx_get_core_num();
650232812Sjmallett        if (registers[34] & 0x4)
651232812Sjmallett        {
652232812Sjmallett            cvmx_safe_printf("Bus error detected due to DID timeout: core: %d\n", i);
653232812Sjmallett            CVMX_MT_DCACHE_ERR(4);
654232812Sjmallett        }
655232812Sjmallett        else if (registers[34] & 0x2)
656232812Sjmallett        {
657232812Sjmallett            cvmx_safe_printf("Bus error detected due to write buffer parity: core: %d\n", i);
658232812Sjmallett            CVMX_MT_DCACHE_ERR(2);
659232812Sjmallett        }
660232812Sjmallett    }
661232812Sjmallett
662210284Sjmallett    if ((cause & 0x7c) != 0)
663210284Sjmallett    {
664210284Sjmallett        cvmx_interrupt_state.exception_handler(registers);
665215990Sjmallett        goto return_from_interrupt;
666210284Sjmallett    }
667210284Sjmallett
668210284Sjmallett    /* Convert the cause into an active mask */
669210284Sjmallett    mask = ((cause & status) >> 8) & 0xff;
670210284Sjmallett    if (mask == 0)
671215990Sjmallett    {
672215990Sjmallett        goto return_from_interrupt; /* Spurious interrupt */
673215990Sjmallett    }
674210284Sjmallett
675210284Sjmallett    for (i=0; i<8; i++)
676210284Sjmallett    {
677210284Sjmallett        if (mask & (1<<i))
678210284Sjmallett        {
679232812Sjmallett            struct __cvmx_interrupt_handler *h = cvmx_interrupt_state.handlers + i;
680232812Sjmallett            h->handler(i, registers, h->data);
681215990Sjmallett            goto return_from_interrupt;
682210284Sjmallett        }
683210284Sjmallett    }
684210284Sjmallett
685210284Sjmallett    /* We should never get here */
686210284Sjmallett    __cvmx_interrupt_default_exception_handler(registers);
687215990Sjmallett
688215990Sjmallettreturn_from_interrupt:
689215990Sjmallett    /* Restore Status register before returning from exception. */
690215990Sjmallett    asm volatile ("dmtc0 %0, $12, 0" : : "r" (status));
691210284Sjmallett}
692210284Sjmallett
693232812Sjmallettvoid (*cvmx_interrupt_mask_irq)(int irq_number);
694232812Sjmallettvoid (*cvmx_interrupt_unmask_irq)(int irq_number);
695210284Sjmallett
696232812Sjmallett#define CLEAR_OR_MASK(V,M,O) ({\
697232812Sjmallett            if (O)             \
698232812Sjmallett                (V) &= ~(M);   \
699232812Sjmallett            else               \
700232812Sjmallett                (V) |= (M);    \
701232812Sjmallett        })
702232812Sjmallett
703232812Sjmallettstatic void __cvmx_interrupt_ciu2_mask_unmask_irq(int irq_number, int op)
704232812Sjmallett{
705232812Sjmallett
706232812Sjmallett    if (irq_number < 0 || irq_number >= CVMX_IRQ_MAX)
707232812Sjmallett        return;
708232812Sjmallett
709232812Sjmallett    if (irq_number <=  CVMX_IRQ_MIPS7) {
710232812Sjmallett        uint32_t flags, mask;
711232812Sjmallett
712232812Sjmallett        flags = cvmx_interrupt_disable_save();
713232812Sjmallett        asm volatile ("mfc0 %0,$12,0" : "=r" (mask));
714232812Sjmallett        CLEAR_OR_MASK(mask, 1 << (8 + irq_number), op);
715232812Sjmallett        asm volatile ("mtc0 %0,$12,0" : : "r" (mask));
716232812Sjmallett        cvmx_interrupt_restore(flags);
717232812Sjmallett    } else {
718232812Sjmallett        int idx;
719232812Sjmallett        uint64_t reg;
720232812Sjmallett        int core = cvmx_get_core_num();
721232812Sjmallett
722232812Sjmallett        int bit = cvmx_interrupt_state.handlers[irq_number].handler_data;
723232812Sjmallett
724232812Sjmallett        if (bit < 0)
725232812Sjmallett            return;
726232812Sjmallett
727232812Sjmallett        idx = bit >> 6;
728232812Sjmallett        bit &= 0x3f;
729232812Sjmallett        if (idx > 7) {
730232812Sjmallett            /* MBOX */
731232812Sjmallett            if (op)
732232812Sjmallett                reg = CVMX_CIU2_EN_PPX_IP2_MBOX_W1C(core);
733232812Sjmallett            else
734232812Sjmallett                reg = CVMX_CIU2_EN_PPX_IP2_MBOX_W1S(core);
735232812Sjmallett        } else {
736232812Sjmallett            if (op)
737232812Sjmallett                reg = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(core) + (0x1000 * idx);
738232812Sjmallett            else
739232812Sjmallett                reg = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(core) + (0x1000 * idx);
740232812Sjmallett        }
741232812Sjmallett        cvmx_write_csr(reg, 1ull << bit);
742232812Sjmallett    }
743232812Sjmallett}
744232812Sjmallett
745232812Sjmallettstatic void __cvmx_interrupt_ciu2_mask_irq(int irq_number)
746232812Sjmallett{
747232812Sjmallett    __cvmx_interrupt_ciu2_mask_unmask_irq(irq_number, 1);
748232812Sjmallett}
749232812Sjmallett
750232812Sjmallettstatic void __cvmx_interrupt_ciu2_unmask_irq(int irq_number)
751232812Sjmallett{
752232812Sjmallett    __cvmx_interrupt_ciu2_mask_unmask_irq(irq_number, 0);
753232812Sjmallett}
754232812Sjmallett
755232812Sjmallettstatic void __cvmx_interrupt_ciu_mask_unmask_irq(int irq_number, int op)
756232812Sjmallett{
757232812Sjmallett    uint32_t flags;
758232812Sjmallett
759232812Sjmallett    if (irq_number < 0 || irq_number >= CVMX_IRQ_MAX)
760232812Sjmallett        return;
761232812Sjmallett
762232812Sjmallett    flags = cvmx_interrupt_disable_save();
763232812Sjmallett    if (irq_number <=  CVMX_IRQ_MIPS7) {
764232812Sjmallett        uint32_t mask;
765232812Sjmallett        asm volatile ("mfc0 %0,$12,0" : "=r" (mask));
766232812Sjmallett        CLEAR_OR_MASK(mask, 1 << (8 + irq_number), op);
767232812Sjmallett        asm volatile ("mtc0 %0,$12,0" : : "r" (mask));
768232812Sjmallett    } else {
769232812Sjmallett        int ciu_bit, ciu_offset;
770232812Sjmallett        int bit = cvmx_interrupt_state.handlers[irq_number].handler_data;
771232812Sjmallett        int is_timer_intr = bit >> 6;
772232812Sjmallett        int core = cvmx_get_core_num();
773232812Sjmallett
774232812Sjmallett        if (bit < 0)
775232812Sjmallett            goto out;
776232812Sjmallett
777232812Sjmallett        ciu_bit = bit & 0x3f;
778232812Sjmallett        ciu_offset = core * 2;
779232812Sjmallett
780232812Sjmallett        if (is_timer_intr == 8)
781232812Sjmallett        {
782232812Sjmallett            CLEAR_OR_MASK(cvmx_interrupt_ciu_61xx_timer_mirror, 1ull << ciu_bit, op);
783232812Sjmallett            CLEAR_OR_MASK(cvmx_interrupt_ciu_en0_mirror, 1ull << 51, op); // SUM2 bit
784232812Sjmallett            cvmx_write_csr(CVMX_CIU_EN2_PPX_IP2(core), cvmx_interrupt_ciu_61xx_timer_mirror);
785232812Sjmallett        }
786232812Sjmallett        else if (bit & 0x40) {
787232812Sjmallett            /* EN1 */
788232812Sjmallett            ciu_offset += 1;
789232812Sjmallett            CLEAR_OR_MASK(cvmx_interrupt_ciu_en1_mirror, 1ull << ciu_bit, op);
790232812Sjmallett            cvmx_write_csr(CVMX_CIU_INTX_EN1(ciu_offset), cvmx_interrupt_ciu_en1_mirror);
791232812Sjmallett        } else {
792232812Sjmallett            /* EN0 */
793232812Sjmallett            CLEAR_OR_MASK(cvmx_interrupt_ciu_en0_mirror, 1ull << ciu_bit, op);
794232812Sjmallett            cvmx_write_csr(CVMX_CIU_INTX_EN0(ciu_offset), cvmx_interrupt_ciu_en0_mirror);
795232812Sjmallett        }
796232812Sjmallett    }
797232812Sjmallettout:
798232812Sjmallett    cvmx_interrupt_restore(flags);
799232812Sjmallett}
800232812Sjmallett
801232812Sjmallettstatic void __cvmx_interrupt_ciu_mask_irq(int irq_number)
802232812Sjmallett{
803232812Sjmallett    __cvmx_interrupt_ciu_mask_unmask_irq(irq_number, 1);
804232812Sjmallett}
805232812Sjmallett
806232812Sjmallettstatic void __cvmx_interrupt_ciu_unmask_irq(int irq_number)
807232812Sjmallett{
808232812Sjmallett    __cvmx_interrupt_ciu_mask_unmask_irq(irq_number, 0);
809232812Sjmallett}
810232812Sjmallett
811210284Sjmallett/**
812232812Sjmallett * Register an interrupt handler for the specified interrupt number.
813232812Sjmallett *
814232812Sjmallett * @param irq_number Interrupt number to register for See
815232812Sjmallett *                   cvmx-interrupt.h for enumeration and description of sources.
816232812Sjmallett * @param func       Function to call on interrupt.
817232812Sjmallett * @param user_arg   User data to pass to the interrupt handler
818232812Sjmallett */
819232812Sjmallettvoid cvmx_interrupt_register(int irq_number, cvmx_interrupt_func_t func, void *user_arg)
820232812Sjmallett{
821232812Sjmallett    if (irq_number >= CVMX_IRQ_MAX || irq_number < 0) {
822232812Sjmallett        cvmx_warn("cvmx_interrupt_register: Illegal irq_number %d\n", irq_number);
823232812Sjmallett        return;
824232812Sjmallett    }
825232812Sjmallett    cvmx_interrupt_state.handlers[irq_number].handler = func;
826232812Sjmallett    cvmx_interrupt_state.handlers[irq_number].data = user_arg;
827232812Sjmallett    CVMX_SYNCWS;
828232812Sjmallett}
829232812Sjmallett
830232812Sjmallett
831232812Sjmallettstatic void cvmx_interrupt_ciu_initialize(cvmx_sysinfo_t *sys_info_ptr)
832232812Sjmallett{
833232812Sjmallett    int i;
834232812Sjmallett    int core = cvmx_get_core_num();
835232812Sjmallett
836232812Sjmallett    /* Disable all CIU interrupts by default */
837232812Sjmallett    cvmx_interrupt_ciu_en0_mirror = 0;
838232812Sjmallett    cvmx_interrupt_ciu_en1_mirror = 0;
839232812Sjmallett    cvmx_interrupt_ciu_61xx_timer_mirror = 0;
840232812Sjmallett    cvmx_write_csr(CVMX_CIU_INTX_EN0(core * 2), cvmx_interrupt_ciu_en0_mirror);
841232812Sjmallett    cvmx_write_csr(CVMX_CIU_INTX_EN0((core * 2)+1), cvmx_interrupt_ciu_en0_mirror);
842232812Sjmallett    cvmx_write_csr(CVMX_CIU_INTX_EN1(core * 2), cvmx_interrupt_ciu_en1_mirror);
843232812Sjmallett    cvmx_write_csr(CVMX_CIU_INTX_EN1((core * 2)+1), cvmx_interrupt_ciu_en1_mirror);
844232812Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN61XX) || OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_2))
845232812Sjmallett        cvmx_write_csr(CVMX_CIU_EN2_PPX_IP2(cvmx_get_core_num()), cvmx_interrupt_ciu_61xx_timer_mirror);
846232812Sjmallett
847232812Sjmallett    if (!cvmx_coremask_first_core(sys_info_ptr->core_mask)|| is_core_being_hot_plugged())
848232812Sjmallett        return;
849232812Sjmallett
850232812Sjmallett    /* On the first core, set up the maps */
851232812Sjmallett    for (i = 0; i < 64; i++) {
852232812Sjmallett        cvmx_ciu_en0_to_irq[i] = 0xff;
853232812Sjmallett        cvmx_ciu_en1_to_irq[i] = 0xff;
854232812Sjmallett        cvmx_ciu_61xx_timer_to_irq[i] = 0xff;
855232812Sjmallett    }
856232812Sjmallett
857232812Sjmallett    /* WORKQ */
858232812Sjmallett    for (i = 0; i < 16; i++)
859232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_WORKQ0 + i, 0, i);
860232812Sjmallett    /* GPIO */
861232812Sjmallett    for (i = 0; i < 16; i++)
862232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_GPIO0 + i, 0, i + 16);
863232812Sjmallett
864232812Sjmallett    /* MBOX */
865232812Sjmallett    for (i = 0; i < 2; i++)
866232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_MBOX0 + i, 0, i + 32);
867232812Sjmallett
868232812Sjmallett    /* UART */
869232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_UART0 + 0, 0, 34);
870232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_UART0 + 1, 0, 35);
871232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_UART0 + 2, 1, 16);
872232812Sjmallett
873232812Sjmallett    /* PCI */
874232812Sjmallett    for (i = 0; i < 4; i++)
875232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_PCI_INT0 + i, 0, i + 36);
876232812Sjmallett
877232812Sjmallett    /* MSI */
878232812Sjmallett    for (i = 0; i < 4; i++)
879232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_PCI_MSI0 + i, 0, i + 40);
880232812Sjmallett
881232812Sjmallett    /* TWSI */
882232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_TWSI0 + 0, 0, 45);
883232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_TWSI0 + 1, 0, 59);
884232812Sjmallett
885232812Sjmallett    /* other */
886232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_RML, 0, 46);
887232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_TRACE0, 0, 47);
888232812Sjmallett
889232812Sjmallett    /* GMX_DRP */
890232812Sjmallett    for (i = 0; i < 2; i++)
891232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_GMX_DRP0 + i, 0, i + 48);
892232812Sjmallett
893232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_IPD_DRP, 0, 50);
894232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_KEY_ZERO, 0, 51);
895232812Sjmallett
896232812Sjmallett    /* TIMER0 */
897232812Sjmallett    for (i = 0; i < 4; i++)
898232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_TIMER0 + i, 0, i + 52);
899232812Sjmallett
900232812Sjmallett    /* TIMER4..9 */
901232812Sjmallett    for(i = 0; i < 6; i++)
902232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_TIMER4 + i, 8, i + 4);
903232812Sjmallett
904232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_USB0 + 0, 0, 56);
905232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_USB0 + 1, 1, 17);
906232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_PCM, 0, 57);
907232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_MPI, 0, 58);
908232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_POWIQ, 0, 60);
909232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_IPDPPTHR, 0, 61);
910232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_MII0 + 0, 0, 62);
911232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_MII0 + 1, 1, 18);
912232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_BOOTDMA, 0, 63);
913232812Sjmallett
914232812Sjmallett    /* WDOG */
915232812Sjmallett    for (i = 0; i < 16; i++)
916232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_WDOG0 + i, 1, i);
917232812Sjmallett
918232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_NAND, 1, 19);
919232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_MIO, 1, 20);
920232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_IOB, 1, 21);
921232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_FPA, 1, 22);
922232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_POW, 1, 23);
923232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_L2C, 1, 24);
924232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_IPD, 1, 25);
925232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_PIP, 1, 26);
926232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_PKO, 1, 27);
927232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_ZIP, 1, 28);
928232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_TIM, 1, 29);
929232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_RAD, 1, 30);
930232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_KEY, 1, 31);
931232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_DFA, 1, 32);
932232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_USBCTL, 1, 33);
933232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_SLI, 1, 34);
934232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_DPI, 1, 35);
935232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_AGX0, 1, 36);
936232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_AGX0 + 1, 1, 37);
937232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_DPI_DMA, 1, 40);
938232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_AGL, 1, 46);
939232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_PTP, 1, 47);
940232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_PEM0, 1, 48);
941232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_PEM1, 1, 49);
942232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_SRIO0, 1, 50);
943232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_SRIO1, 1, 51);
944232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_LMC0, 1, 52);
945232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_DFM, 1, 56);
946232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_SRIO2, 1, 60);
947232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_RST, 1, 63);
948232812Sjmallett}
949232812Sjmallett
950232812Sjmallettstatic void cvmx_interrupt_ciu2_initialize(cvmx_sysinfo_t *sys_info_ptr)
951232812Sjmallett{
952232812Sjmallett    int i;
953232812Sjmallett
954232812Sjmallett    /* Disable all CIU2 interrupts by default */
955232812Sjmallett
956232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_WRKQ(cvmx_get_core_num()), 0);
957232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_WRKQ(cvmx_get_core_num()), 0);
958232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_WRKQ(cvmx_get_core_num()), 0);
959232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_WDOG(cvmx_get_core_num()), 0);
960232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_WDOG(cvmx_get_core_num()), 0);
961232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_WDOG(cvmx_get_core_num()), 0);
962232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_RML(cvmx_get_core_num()), 0);
963232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_RML(cvmx_get_core_num()), 0);
964232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_RML(cvmx_get_core_num()), 0);
965232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_MIO(cvmx_get_core_num()), 0);
966232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_MIO(cvmx_get_core_num()), 0);
967232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_MIO(cvmx_get_core_num()), 0);
968232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_IO(cvmx_get_core_num()), 0);
969232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_IO(cvmx_get_core_num()), 0);
970232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_IO(cvmx_get_core_num()), 0);
971232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_MEM(cvmx_get_core_num()), 0);
972232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_MEM(cvmx_get_core_num()), 0);
973232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_MEM(cvmx_get_core_num()), 0);
974232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_PKT(cvmx_get_core_num()), 0);
975232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_PKT(cvmx_get_core_num()), 0);
976232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_PKT(cvmx_get_core_num()), 0);
977232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_GPIO(cvmx_get_core_num()), 0);
978232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_GPIO(cvmx_get_core_num()), 0);
979232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_GPIO(cvmx_get_core_num()), 0);
980232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP2_MBOX(cvmx_get_core_num()), 0);
981232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP3_MBOX(cvmx_get_core_num()), 0);
982232812Sjmallett    cvmx_write_csr(CVMX_CIU2_EN_PPX_IP4_MBOX(cvmx_get_core_num()), 0);
983232812Sjmallett
984232812Sjmallett    if (!cvmx_coremask_first_core(sys_info_ptr->core_mask) || is_core_being_hot_plugged())
985232812Sjmallett        return;
986232812Sjmallett
987232812Sjmallett    /* On the first core, set up the maps */
988232812Sjmallett    for (i = 0; i < 64; i++) {
989232812Sjmallett        cvmx_ciu2_wrkq_to_irq[i] = 0xff;
990232812Sjmallett        cvmx_ciu2_wdog_to_irq[i] = 0xff;
991232812Sjmallett        cvmx_ciu2_rml_to_irq[i] = 0xff;
992232812Sjmallett        cvmx_ciu2_mio_to_irq[i] = 0xff;
993232812Sjmallett        cvmx_ciu2_io_to_irq[i] = 0xff;
994232812Sjmallett        cvmx_ciu2_mem_to_irq[i] = 0xff;
995232812Sjmallett        cvmx_ciu2_eth_to_irq[i] = 0xff;
996232812Sjmallett        cvmx_ciu2_gpio_to_irq[i] = 0xff;
997232812Sjmallett        cvmx_ciu2_mbox_to_irq[i] = 0xff;
998232812Sjmallett    }
999232812Sjmallett
1000232812Sjmallett    /* WORKQ */
1001232812Sjmallett    for (i = 0; i < 64; i++)
1002232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_WORKQ0 + i, 0, i);
1003232812Sjmallett
1004232812Sjmallett    /* GPIO */
1005232812Sjmallett    for (i = 0; i < 16; i++)
1006232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_GPIO0 + i, 7, i);
1007232812Sjmallett
1008232812Sjmallett    /* MBOX */
1009232812Sjmallett    for (i = 0; i < 4; i++)
1010232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_MBOX0 + i, 60, i);
1011232812Sjmallett
1012232812Sjmallett    /* UART */
1013232812Sjmallett    for (i = 0; i < 2; i++)
1014232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_UART0 + i, 3, 36 + i);
1015232812Sjmallett
1016232812Sjmallett    /* PCI */
1017232812Sjmallett    for (i = 0; i < 4; i++)
1018232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_PCI_INT0 + i, 4, 16 + i);
1019232812Sjmallett
1020232812Sjmallett    /* MSI */
1021232812Sjmallett    for (i = 0; i < 4; i++)
1022232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_PCI_MSI0 + i, 4, 8 + i);
1023232812Sjmallett
1024232812Sjmallett    /* TWSI */
1025232812Sjmallett    for (i = 0; i < 2; i++)
1026232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_TWSI0 + i, 3, 32 + i);
1027232812Sjmallett
1028232812Sjmallett    /* TRACE */
1029232812Sjmallett    for (i = 0; i < 4; i++)
1030232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_TRACE0 + i, 2, 52 + i);
1031232812Sjmallett
1032232812Sjmallett    /* GMX_DRP */
1033232812Sjmallett    for (i = 0; i < 5; i++)
1034232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_GMX_DRP0 + i, 6, 8 + i);
1035232812Sjmallett
1036232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_IPD_DRP, 3, 2);
1037232812Sjmallett
1038232812Sjmallett    /* TIMER0 */
1039232812Sjmallett    for (i = 0; i < 4; i++)
1040232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_TIMER0 + i, 3, 8 + i);
1041232812Sjmallett
1042232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_USB0, 3, 44);
1043232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_IPDPPTHR, 3, 0);
1044232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_MII0, 6, 40);
1045232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_BOOTDMA, 3, 18);
1046232812Sjmallett
1047232812Sjmallett    /* WDOG */
1048232812Sjmallett    for (i = 0; i < 32; i++)
1049232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_WDOG0 + i, 1, i);
1050232812Sjmallett
1051232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_NAND, 3, 16);
1052232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_MIO, 3, 17);
1053232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_IOB, 2, 0);
1054232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_FPA, 2, 4);
1055232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_POW, 2, 16);
1056232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_L2C, 2, 48);
1057232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_IPD, 2, 5);
1058232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_PIP, 2, 6);
1059232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_PKO, 2, 7);
1060232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_ZIP, 2, 24);
1061232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_TIM, 2, 28);
1062232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_RAD, 2, 29);
1063232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_KEY, 2, 30);
1064232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_DFA, 2, 40);
1065232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_USBCTL, 3, 40);
1066232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_SLI, 2, 32);
1067232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_DPI, 2, 33);
1068232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_DPI_DMA, 2, 36);
1069232812Sjmallett
1070232812Sjmallett    /* AGX */
1071232812Sjmallett    for (i = 0; i < 5; i++)
1072232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_AGX0 + i, 6, i);
1073232812Sjmallett
1074232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_AGL, 6, 32);
1075232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_PTP, 3, 48);
1076232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_PEM0, 4, 32);
1077232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_PEM1, 4, 32);
1078232812Sjmallett
1079232812Sjmallett    /* LMC */
1080232812Sjmallett    for (i = 0; i < 4; i++)
1081232812Sjmallett        __cvmx_interrupt_set_mapping(CVMX_IRQ_LMC0 + i, 5, i);
1082232812Sjmallett
1083232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_RST, 3, 63);
1084232812Sjmallett    __cvmx_interrupt_set_mapping(CVMX_IRQ_ILK, 6, 48);
1085232812Sjmallett}
1086232812Sjmallett
1087232812Sjmallett/**
1088210284Sjmallett * Initialize the interrupt routine and copy the low level
1089210284Sjmallett * stub into the correct interrupt vector. This is called
1090210284Sjmallett * automatically during application startup.
1091210284Sjmallett */
1092210284Sjmallettvoid cvmx_interrupt_initialize(void)
1093210284Sjmallett{
1094210284Sjmallett    void *low_level_loc;
1095210284Sjmallett    cvmx_sysinfo_t *sys_info_ptr = cvmx_sysinfo_get();
1096210284Sjmallett    int i;
1097210284Sjmallett
1098232812Sjmallett    if (cvmx_coremask_first_core(sys_info_ptr->core_mask) && !is_core_being_hot_plugged()) {
1099232812Sjmallett#ifndef CVMX_ENABLE_CSR_ADDRESS_CHECKING
1100232812Sjmallett        /* We assume this relationship between the registers. */
1101232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x1000 == CVMX_CIU2_SRC_PPX_IP2_WDOG(0));
1102232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x2000 == CVMX_CIU2_SRC_PPX_IP2_RML(0));
1103232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x3000 == CVMX_CIU2_SRC_PPX_IP2_MIO(0));
1104232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x4000 == CVMX_CIU2_SRC_PPX_IP2_IO(0));
1105232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x5000 == CVMX_CIU2_SRC_PPX_IP2_MEM(0));
1106232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x6000 == CVMX_CIU2_SRC_PPX_IP2_PKT(0));
1107232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_SRC_PPX_IP2_WRKQ(0) + 0x7000 == CVMX_CIU2_SRC_PPX_IP2_GPIO(0));
1108232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x1000 == CVMX_CIU2_EN_PPX_IP2_WDOG_W1C(0));
1109232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x2000 == CVMX_CIU2_EN_PPX_IP2_RML_W1C(0));
1110232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x3000 == CVMX_CIU2_EN_PPX_IP2_MIO_W1C(0));
1111232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x4000 == CVMX_CIU2_EN_PPX_IP2_IO_W1C(0));
1112232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x5000 == CVMX_CIU2_EN_PPX_IP2_MEM_W1C(0));
1113232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x6000 == CVMX_CIU2_EN_PPX_IP2_PKT_W1C(0));
1114232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(0) + 0x7000 == CVMX_CIU2_EN_PPX_IP2_GPIO_W1C(0));
1115232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x1000 == CVMX_CIU2_EN_PPX_IP2_WDOG_W1S(0));
1116232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x2000 == CVMX_CIU2_EN_PPX_IP2_RML_W1S(0));
1117232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x3000 == CVMX_CIU2_EN_PPX_IP2_MIO_W1S(0));
1118232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x4000 == CVMX_CIU2_EN_PPX_IP2_IO_W1S(0));
1119232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x5000 == CVMX_CIU2_EN_PPX_IP2_MEM_W1S(0));
1120232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x6000 == CVMX_CIU2_EN_PPX_IP2_PKT_W1S(0));
1121232812Sjmallett        CVMX_BUILD_ASSERT(CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(0) + 0x7000 == CVMX_CIU2_EN_PPX_IP2_GPIO_W1S(0));
1122232812Sjmallett#endif /* !CVMX_ENABLE_CSR_ADDRESS_CHECKING */
1123210284Sjmallett
1124232812Sjmallett        for (i = 0; i < CVMX_IRQ_MAX; i++) {
1125232812Sjmallett            cvmx_interrupt_state.handlers[i].handler = __cvmx_interrupt_default;
1126232812Sjmallett            cvmx_interrupt_state.handlers[i].data = NULL;
1127232812Sjmallett            cvmx_interrupt_state.handlers[i].handler_data = -1;
1128232812Sjmallett        }
1129232812Sjmallett    }
1130232812Sjmallett
1131232812Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN68XX))
1132210284Sjmallett    {
1133232812Sjmallett        cvmx_interrupt_mask_irq = __cvmx_interrupt_ciu2_mask_irq;
1134232812Sjmallett        cvmx_interrupt_unmask_irq = __cvmx_interrupt_ciu2_unmask_irq;
1135232812Sjmallett        cvmx_interrupt_ciu2_initialize(sys_info_ptr);
1136232812Sjmallett        /* Add an interrupt handlers for chained CIU interrupt */
1137232812Sjmallett        cvmx_interrupt_register(CVMX_IRQ_MIPS2, __cvmx_interrupt_ciu2, NULL);
1138232812Sjmallett    }
1139232812Sjmallett    else if (OCTEON_IS_MODEL(OCTEON_CN61XX) || OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_2))
1140232812Sjmallett    {
1141232812Sjmallett        cvmx_interrupt_mask_irq = __cvmx_interrupt_ciu_mask_irq;
1142232812Sjmallett        cvmx_interrupt_unmask_irq = __cvmx_interrupt_ciu_unmask_irq;
1143232812Sjmallett        cvmx_interrupt_ciu_initialize(sys_info_ptr);
1144232812Sjmallett
1145232812Sjmallett        /* Add an interrupt handlers for chained CIU interrupts */
1146232812Sjmallett        cvmx_interrupt_register(CVMX_IRQ_MIPS2, __cvmx_interrupt_ciu, NULL);
1147232812Sjmallett        cvmx_interrupt_register(CVMX_IRQ_MIPS3, __cvmx_interrupt_ciu_cn61xx, NULL);
1148232812Sjmallett    }
1149232812Sjmallett    else
1150232812Sjmallett    {
1151232812Sjmallett        cvmx_interrupt_mask_irq = __cvmx_interrupt_ciu_mask_irq;
1152232812Sjmallett        cvmx_interrupt_unmask_irq = __cvmx_interrupt_ciu_unmask_irq;
1153232812Sjmallett        cvmx_interrupt_ciu_initialize(sys_info_ptr);
1154232812Sjmallett
1155232812Sjmallett        /* Add an interrupt handlers for chained CIU interrupts */
1156232812Sjmallett        cvmx_interrupt_register(CVMX_IRQ_MIPS2, __cvmx_interrupt_ciu, NULL);
1157232812Sjmallett        cvmx_interrupt_register(CVMX_IRQ_MIPS3, __cvmx_interrupt_ciu, NULL);
1158232812Sjmallett    }
1159232812Sjmallett
1160232812Sjmallett    /* Move performance counter interrupts to IRQ 6*/
1161232812Sjmallett    cvmx_update_perfcnt_irq();
1162232812Sjmallett
1163232812Sjmallett    /* Add an interrupt handler for Perf counter interrupts */
1164232812Sjmallett    cvmx_interrupt_register(CVMX_IRQ_MIPS6, __cvmx_interrupt_perf, NULL);
1165232812Sjmallett
1166232812Sjmallett    if (cvmx_coremask_first_core(sys_info_ptr->core_mask) && !is_core_being_hot_plugged())
1167232812Sjmallett    {
1168210284Sjmallett        cvmx_interrupt_state.exception_handler = __cvmx_interrupt_default_exception_handler;
1169210284Sjmallett
1170210284Sjmallett        low_level_loc = CASTPTR(void, CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0,sys_info_ptr->exception_base_addr));
1171210284Sjmallett        memcpy(low_level_loc + 0x80, (void*)cvmx_interrupt_stage1, 0x80);
1172210284Sjmallett        memcpy(low_level_loc + 0x100, (void*)cvmx_interrupt_cache_error, 0x80);
1173210284Sjmallett        memcpy(low_level_loc + 0x180, (void*)cvmx_interrupt_stage1, 0x80);
1174210284Sjmallett        memcpy(low_level_loc + 0x200, (void*)cvmx_interrupt_stage1, 0x80);
1175215990Sjmallett
1176210284Sjmallett        /* Make sure the locations used to count Icache and Dcache exceptions
1177210284Sjmallett            starts out as zero */
1178210284Sjmallett        cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 8), 0);
1179210284Sjmallett        cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 16), 0);
1180210284Sjmallett        cvmx_write64_uint64(CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, 24), 0);
1181210284Sjmallett        CVMX_SYNC;
1182210284Sjmallett
1183210284Sjmallett        /* Add an interrupt handler for ECC failures */
1184215990Sjmallett        if (cvmx_error_initialize(0 /* || CVMX_ERROR_FLAGS_ECC_SINGLE_BIT */))
1185215990Sjmallett            cvmx_warn("cvmx_error_initialize() failed\n");
1186232812Sjmallett
1187232812Sjmallett        /* Enable PIP/IPD, POW, PKO, FPA, NAND, KEY, RAD, L2C, LMC, GMX, AGL,
1188232812Sjmallett           DFM, DFA, error handling interrupts. */
1189232812Sjmallett        if (OCTEON_IS_MODEL(OCTEON_CN68XX))
1190232812Sjmallett        {
1191232812Sjmallett            int i;
1192232812Sjmallett
1193232812Sjmallett            for (i = 0; i < 5; i++)
1194232812Sjmallett            {
1195232812Sjmallett                cvmx_interrupt_register(CVMX_IRQ_AGX0+i, __cvmx_interrupt_ecc, NULL);
1196232812Sjmallett                cvmx_interrupt_unmask_irq(CVMX_IRQ_AGX0+i);
1197232812Sjmallett            }
1198232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_NAND, __cvmx_interrupt_ecc, NULL);
1199232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_NAND);
1200232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_MIO, __cvmx_interrupt_ecc, NULL);
1201232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_MIO);
1202232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_FPA, __cvmx_interrupt_ecc, NULL);
1203232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_FPA);
1204232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_IPD, __cvmx_interrupt_ecc, NULL);
1205232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_IPD);
1206232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_PIP, __cvmx_interrupt_ecc, NULL);
1207232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_PIP);
1208232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_POW, __cvmx_interrupt_ecc, NULL);
1209232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_POW);
1210232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_L2C, __cvmx_interrupt_ecc, NULL);
1211232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_L2C);
1212232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_PKO, __cvmx_interrupt_ecc, NULL);
1213232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_PKO);
1214232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_ZIP, __cvmx_interrupt_ecc, NULL);
1215232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_ZIP);
1216232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_RAD, __cvmx_interrupt_ecc, NULL);
1217232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_RAD);
1218232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_KEY, __cvmx_interrupt_ecc, NULL);
1219232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_KEY);
1220232812Sjmallett            /* Before enabling SLI interrupt clear any RML_TO interrupt */
1221232812Sjmallett            if (cvmx_read_csr(CVMX_PEXP_SLI_INT_SUM) & 0x1)
1222232812Sjmallett            {
1223232812Sjmallett                cvmx_safe_printf("clearing pending SLI_INT_SUM[RML_TO] interrupt (ignore)\n");
1224232812Sjmallett                cvmx_write_csr(CVMX_PEXP_SLI_INT_SUM, 1);
1225232812Sjmallett            }
1226232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_SLI, __cvmx_interrupt_ecc, NULL);
1227232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_SLI);
1228232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_DPI, __cvmx_interrupt_ecc, NULL);
1229232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_DPI);
1230232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_DFA, __cvmx_interrupt_ecc, NULL);
1231232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_DFA);
1232232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_AGL, __cvmx_interrupt_ecc, NULL);
1233232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_AGL);
1234232812Sjmallett            for (i = 0; i < 4; i++)
1235232812Sjmallett            {
1236232812Sjmallett                cvmx_interrupt_register(CVMX_IRQ_LMC0+i, __cvmx_interrupt_ecc, NULL);
1237232812Sjmallett                cvmx_interrupt_unmask_irq(CVMX_IRQ_LMC0+i);
1238232812Sjmallett            }
1239232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_DFM, __cvmx_interrupt_ecc, NULL);
1240232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_DFM);
1241232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_RST, __cvmx_interrupt_ecc, NULL);
1242232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_RST);
1243232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_ILK, __cvmx_interrupt_ecc, NULL);
1244232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_ILK);
1245232812Sjmallett        }
1246232812Sjmallett        else
1247232812Sjmallett        {
1248232812Sjmallett            cvmx_interrupt_register(CVMX_IRQ_RML, __cvmx_interrupt_ecc, NULL);
1249232812Sjmallett            cvmx_interrupt_unmask_irq(CVMX_IRQ_RML);
1250232812Sjmallett        }
1251232812Sjmallett
1252232812Sjmallett        cvmx_atomic_set32(&cvmx_interrupt_initialize_flag, 1);
1253210284Sjmallett    }
1254210284Sjmallett
1255232812Sjmallett    while (!cvmx_atomic_get32(&cvmx_interrupt_initialize_flag))
1256232812Sjmallett        ; /* Wait for first core to finish above. */
1257232812Sjmallett
1258232812Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
1259232812Sjmallett        cvmx_interrupt_unmask_irq(CVMX_IRQ_MIPS2);
1260232812Sjmallett    } else {
1261232812Sjmallett        cvmx_interrupt_unmask_irq(CVMX_IRQ_MIPS2);
1262232812Sjmallett        cvmx_interrupt_unmask_irq(CVMX_IRQ_MIPS3);
1263232812Sjmallett    }
1264232812Sjmallett
1265210284Sjmallett    CVMX_ICACHE_INVALIDATE;
1266210284Sjmallett
1267210284Sjmallett    /* Enable interrupts for each core (bit0 of COP0 Status) */
1268232812Sjmallett    cvmx_interrupt_restore(1);
1269210284Sjmallett}
1270210284Sjmallett
1271210284Sjmallett
1272210284Sjmallett
1273210284Sjmallett/**
1274210284Sjmallett * Set the exception handler for all non interrupt sources.
1275210284Sjmallett *
1276210284Sjmallett * @param handler New exception handler
1277210284Sjmallett * @return Old exception handler
1278210284Sjmallett */
1279210284Sjmallettcvmx_interrupt_exception_t cvmx_interrupt_set_exception(cvmx_interrupt_exception_t handler)
1280210284Sjmallett{
1281210284Sjmallett    cvmx_interrupt_exception_t result = cvmx_interrupt_state.exception_handler;
1282210284Sjmallett    cvmx_interrupt_state.exception_handler = handler;
1283210284Sjmallett    CVMX_SYNCWS;
1284210284Sjmallett    return result;
1285210284Sjmallett}
1286215990Sjmallett#endif /* !__U_BOOT__ */
1287210284Sjmallett
1288210284Sjmallett
1289