1/*
2 * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7
8#include <xtensa/coreasm.h>
9#include <xtensa/corebits.h>
10#include <xtensa/config/system.h>
11#include "freertos/xtensa_context.h"
12#include "freertos/xtensa_rtos.h"
13#include "esp_private/panic_reason.h"
14#include "sdkconfig.h"
15#include "soc/soc.h"
16#include "soc/soc_caps.h"
17#include "soc/dport_reg.h"
18#include "soc/timer_group_reg.h"
19
20/*
21
22Interrupt , a high-priority interrupt, is used for several things:
23- IPC_ISR handler
24- Cache error panic handler
25- Interrupt watchdog panic handler
26
27*/
28
29#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
30
31#define LX_INTR_STACK_SIZE  12
32#define LX_INTR_A2_OFFSET   0
33#define LX_INTR_A3_OFFSET   4
34#define LX_INTR_A4_OFFSET   8
35#define EPC_X               EPC_5
36#define EXCSAVE_X           EXCSAVE_5
37#define RFI_X               5
38#define xt_highintx         xt_highint5
39
40#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
41
42#define LX_INTR_STACK_SIZE  12
43#define LX_INTR_A2_OFFSET   0
44#define LX_INTR_A3_OFFSET   4
45#define LX_INTR_A4_OFFSET   8
46#define EPC_X               EPC_4
47#define EXCSAVE_X           EXCSAVE_4
48#define RFI_X               4
49#define xt_highintx         xt_highint4
50
51#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
52
53/*
54--------------------------------------------------------------------------------
55  Macro wdt_clr_intr_status - Clear the WDT interrupt status.
56  Macro wdt_feed            - Feed the WDT.
57
58  Input  : "dev" - Beginning address of the peripheral registers
59
60  Macro get_int_status_tg1wdt - Get the ETS_TG1_WDT_LEVEL_INTR_SOURCE bit in interrupt status
61
62  output  : "reg" - Store the result into the reg
63--------------------------------------------------------------------------------
64*/
65
66#define TIMG1_REG_OFFSET(reg)               ((reg) - REG_TIMG_BASE(1))
67#define TIMG1_WDTWPROTECT_OFFSET            TIMG1_REG_OFFSET(TIMG_WDTWPROTECT_REG(1))
68#define TIMG1_INT_CLR_OFFSET                TIMG1_REG_OFFSET(TIMG_INT_CLR_TIMERS_REG(1))
69#define TIMG1_WDT_STG0_HOLD_OFFSET          TIMG1_REG_OFFSET(TIMG_WDTCONFIG2_REG(1))
70#define TIMG1_WDT_STG1_HOLD_OFFSET          TIMG1_REG_OFFSET(TIMG_WDTCONFIG3_REG(1))
71#define TIMG1_WDT_FEED_OFFSET               TIMG1_REG_OFFSET(TIMG_WDTFEED_REG(1))
72#define UART0_DATA_REG                      (0x3FF40078)
73
74    .macro wdt_clr_intr_status  dev
75    movi    a2, \dev
76    movi    a3, TIMG_WDT_WKEY_VALUE
77    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* disable write protect */
78    memw
79    l32i    a4, a2, TIMG1_INT_CLR_OFFSET
80    memw
81    movi    a3, 4
82    or      a3, a4, a3
83    s32i    a3, a2, TIMG1_INT_CLR_OFFSET        /* clear 1st stage timeout interrupt */
84    memw
85    movi    a3, 0
86    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* enable write protect */
87    memw
88    .endm
89
90    .macro wdt_feed dev
91    movi    a2, \dev
92    movi    a3, TIMG_WDT_WKEY_VALUE
93    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* disable write protect */
94    memw
95    movi    a4, _lx_intr_livelock_max
96    l32i    a4, a4, 0
97    memw
98    addi    a4, a4, 1
99    movi    a3, (CONFIG_ESP_INT_WDT_TIMEOUT_MS<<1)
100    quou    a3, a3, a4
101    s32i    a3, a2, TIMG1_WDT_STG0_HOLD_OFFSET  /* set timeout before interrupt */
102    memw
103    movi    a3, (CONFIG_ESP_INT_WDT_TIMEOUT_MS<<2)
104    s32i    a3, a2, TIMG1_WDT_STG1_HOLD_OFFSET  /* set timeout before system reset */
105    memw
106    movi    a3, 1
107    s32i    a3, a2, TIMG1_WDT_FEED_OFFSET       /* feed wdt */
108    memw
109    movi    a3, 0
110    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* enable write protect */
111    memw
112    .endm
113
114    .macro get_int_status_tg1wdt reg
115    rsr     \reg, INTERRUPT
116    extui   \reg, \reg, ETS_T1_WDT_CACHEERR_INUM, 1
117    beqz    \reg, 99f        /* not ETS_T1_WDT_INUM or ETS_CACHEERR_INUM */
118
119    getcoreid   \reg
120    bnez    \reg, 98f
121    /* core 0 */
122    movi    \reg, UART0_DATA_REG
123    l32i    \reg, \reg, 0     /* Workaround for DPORT read error, for silicon revision 0~2 (ECO V0 ~ ECO V2). */
124    movi    \reg, DPORT_PRO_INTR_STATUS_0_REG
125    l32i    \reg, \reg, 0
126    extui   \reg, \reg, ETS_TG1_WDT_LEVEL_INTR_SOURCE, 1
127    j       99f
128
12998: /* core 1 */
130    movi    \reg, UART0_DATA_REG
131    l32i    \reg, \reg, 0     /* Workaround for DPORT read error, for silicon revision 0~2 (ECO V0 ~ ECO V2). */
132    movi    \reg, DPORT_APP_INTR_STATUS_0_REG
133    l32i    \reg, \reg, 0
134    extui   \reg, \reg, ETS_TG1_WDT_LEVEL_INTR_SOURCE, 1
13599:
136    .endm
137
138    .data
139_lx_intr_stack:
140    .space      LX_INTR_STACK_SIZE*portNUM_PROCESSORS /* This allocates stacks for each individual CPU. */
141
142#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT
143    .global _lx_intr_livelock_counter
144    .global _lx_intr_livelock_max
145    .align  16
146_lx_intr_livelock_counter:
147    .word   0
148_lx_intr_livelock_max:
149    .word   0
150_lx_intr_livelock_sync:
151    .word   0, 0
152_lx_intr_livelock_app:
153    .word   0
154_lx_intr_livelock_pro:
155    .word   0
156#endif
157
158    .section .iram1,"ax"
159    .global     xt_highintx
160    .type       xt_highintx,@function
161    .align      4
162xt_highintx:
163
164#ifndef CONFIG_FREERTOS_UNICORE
165    /* See if we're here for the IPC_ISR interrupt */
166    rsr     a0, INTERRUPT
167    extui   a0, a0, ETS_IPC_ISR_INUM, 1
168    beqz    a0, 1f
169    /* Jump to `esp_ipc_isr_handler` which is non-returning. We need to use `jx`
170     * because on Xtensa, `j` instruction can only refer to a label which
171     * is in the range [-131068;+131075]. If the destination is out of scope,
172     * linking will fail. So, to make sure we will always be able to jump to
173     * that subroutine, retrieve its address and store it in a register. */
174    movi    a0, esp_ipc_isr_handler
175    jx      a0
1761:
177#endif /* not CONFIG_FREERTOS_UNICORE */
178
179#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT
180
181#if CONFIG_BTDM_CTRL_HLI
182    /* Timer 2 interrupt */
183    rsr     a0, INTENABLE
184    extui   a0, a0, 16, 1
185    beqz    a0, 1f
186    rsr     a0, INTERRUPT
187    extui   a0, a0, 16, 1
188    bnez    a0, .handle_multicore_debug_int
1891:
190#endif /* CONFIG_BTDM_CTRL_HLI */
191
192    /* ETS_T1_WDT_INUM */
193#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
194    get_int_status_tg1wdt a0
195#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
196    /* See if we're here for the tg1 watchdog interrupt */
197    rsr     a0, INTERRUPT
198    extui   a0, a0, ETS_T1_WDT_INUM, 1
199#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
200    beqz    a0, 1f
201
202    wsr     a5, depc                        /* use DEPC as temp storage */
203    movi    a0, _lx_intr_livelock_counter
204    l32i    a0, a0, 0
205    movi    a5, _lx_intr_livelock_max
206    l32i    a5, a5, 0
207    bltu    a0, a5, .handle_livelock_int    /* _lx_intr_livelock_counter < _lx_intr_livelock_max */
208
209    rsr     a5, depc                        /* restore a5 */
210#endif
211
2121:  /* ETS_CACHEERR_INUM or ETS_T1_WDT_INUM */
213    /* Allocate exception frame and save minimal context. */
214    mov     a0, sp
215    addi    sp, sp, -XT_STK_FRMSZ
216    s32i    a0, sp, XT_STK_A1
217    #if XCHAL_HAVE_WINDOWED
218    s32e    a0, sp, -12                     /* for debug backtrace */
219    #endif
220    rsr     a0, PS                          /* save interruptee's PS */
221    s32i    a0, sp, XT_STK_PS
222    rsr     a0, EPC_X                       /* save interruptee's PC */
223    s32i    a0, sp, XT_STK_PC
224    rsr     a0, EXCSAVE_X                   /* save interruptee's a0 */
225    s32i    a0, sp, XT_STK_A0
226    #if XCHAL_HAVE_WINDOWED
227    s32e    a0, sp, -16                     /* for debug backtrace */
228    #endif
229    s32i    a12, sp, XT_STK_A12             /* _xt_context_save requires A12- */
230    s32i    a13, sp, XT_STK_A13             /* A13 to have already been saved */
231    call0   _xt_context_save
232
233    /* Save vaddr into exception frame */
234    rsr     a0, EXCVADDR
235    s32i    a0, sp, XT_STK_EXCVADDR
236
237    /* Figure out reason, save into EXCCAUSE reg */
238#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
239    get_int_status_tg1wdt a0
240    bnez    a0, 1f
241
242    /* TODO: Clear the MEMACCESS_ERR interrupt status. */
243#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
244    rsr     a0, INTERRUPT
245    extui   a0, a0, ETS_MEMACCESS_ERR_INUM, 1 /* get cacheerr int bit */
246    beqz    a0, 1f
247#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
248
249    /* Kill this interrupt; we cannot reset it. */
250    rsr     a0, INTENABLE
251    movi    a4, ~(1<<ETS_MEMACCESS_ERR_INUM)
252    and     a0, a4, a0
253    wsr     a0, INTENABLE
254    movi    a0, PANIC_RSN_CACHEERR
255    j 9f
2561:
257#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
258   /* Clear the WDT interrupt status. */
259    wdt_clr_intr_status TIMERG1
260#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
261#if CONFIG_ESP_INT_WDT_CHECK_CPU1
262    /* Check if the cause is the app cpu failing to tick.*/
263    movi    a0, int_wdt_cpu1_ticked
264    l32i    a0, a0, 0
265    bnez    a0, 2f
266    /* It is. Modify cause. */
267    movi    a0,PANIC_RSN_INTWDT_CPU1
268    j 9f
2692:
270#endif
271    /* Set EXCCAUSE to reflect cause of the wdt int trigger */
272    movi    a0,PANIC_RSN_INTWDT_CPU0
2739:
274    /* Found the reason, now save it. */
275    s32i    a0, sp, XT_STK_EXCCAUSE
276
277    /* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
278    movi    a0, PS_INTLEVEL(5) | PS_UM | PS_WOE
279    wsr     a0, PS
280
281    /* Call panic handler */
282    mov     a6,sp
283    call4   panicHandler
284
285    call0   _xt_context_restore
286    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
287    wsr     a0, PS
288    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
289    wsr     a0, EPC_X
290    l32i    a0, sp, XT_STK_A0               /* retrieve interruptee's A0 */
291    l32i    sp, sp, XT_STK_A1               /* remove exception frame */
292    rsync                                   /* ensure PS and EPC written */
293
294    rsr     a0, EXCSAVE_X                   /* restore a0 */
295    rfi     RFI_X
296
297
298#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT
299#if CONFIG_BTDM_CTRL_HLI
300#define APB_ITCTRL              (0x3f00)
301#define APB_DCRSET              (0x200c)
302
303#define ERI_ADDR(APB)           (0x100000 + (APB))
304
305    .align      4
306.handle_multicore_debug_int:
307
308    wsr     a2, depc                        /* temp storage */
309
310    rsr.ccount  a2
311    addmi   a2, a2, (CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ*50)
312    wsr     a2, CCOMPARE2
313
314    /* Enable Integration Mode */
315    movi    a2, ERI_ADDR(APB_ITCTRL)
316    rer     a0, a2
317    addi    a0, a0, 1
318    wer     a0, a2
319
320    /* Enable and emit BreakOut signal */
321    movi    a2, ERI_ADDR(APB_DCRSET)
322    rer     a0, a2
323    movi    a2, 0x1020000
324    or      a0, a2, a0
325    movi    a2, ERI_ADDR(APB_DCRSET)
326    wer     a0, a2
327
328    .rept   4
329    nop
330    .endr
331
332    /* Enable Normal Mode */
333    movi    a2, ERI_ADDR(APB_ITCTRL)
334    rer     a0, a2
335    movi    a2, ~0x1
336    and     a0, a2, a0
337    movi    a2, ERI_ADDR(APB_ITCTRL)
338    wer     a0, a2
339
340    rsr     a2, depc
341
342    rsr     a0, EXCSAVE_5                   /* restore a0 */
343    rfi     5
344#endif /* CONFIG_BTDM_CTRL_HLI */
345
346/*
347--------------------------------------------------------------------------------
348  Macro intr_matrix_map - Attach an CPU interrupt to a hardware source.
349
350  Input  : "addr" - Interrupt map configuration base address
351  Input  : "src"  - Interrupt source.
352  Input  : "inum" - Interrupt number.
353--------------------------------------------------------------------------------
354*/
355    .macro intr_matrix_map  addr src inum
356    movi    a2, \src
357    slli    a2, a2, 2
358    movi    a3, \addr
359    add     a3, a3, a2
360    movi    a2, \inum
361    s32i    a2, a3, 0
362    memw
363    .endm
364
365
366
367    .align      4
368.handle_livelock_int:
369
370    getcoreid   a5
371
372    /* Save A2, A3, A4 so we can use those registers */
373    movi    a0, LX_INTR_STACK_SIZE
374    mull    a5, a5, a0
375    movi    a0, _lx_intr_stack
376    add     a0, a0, a5
377    s32i    a2, a0, LX_INTR_A2_OFFSET
378    s32i    a3, a0, LX_INTR_A3_OFFSET
379    s32i    a4, a0, LX_INTR_A4_OFFSET
380
381    /* Here, we can use a0, a2, a3, a4, a5 registers */
382    getcoreid   a5
383
384    rsil    a0, SOC_DPORT_WORKAROUND_DIS_INTERRUPT_LVL /* disable nested interrupt */
385
386    beqz    a5, 1f
387    movi    a2, _lx_intr_livelock_app
388    l32i    a3, a2, 0
389    addi    a3, a3, 1
390    s32i    a3, a2, 0
391
392    /* Dual core synchronization, ensuring that both cores enter interrupts */
3931:  movi    a4, 0x1
394    movi    a2, _lx_intr_livelock_sync
395    addx4   a3, a5, a2
396    s32i    a4, a3, 0
397
3981:  movi    a2, _lx_intr_livelock_sync
399    movi    a3, 1
400    addx4   a3, a3, a2
401    l32i    a2, a2, 0
402    l32i    a3, a3, 0
403    and     a2, a2, a3
404    beqz    a2, 1b
405
406    beqz    a5, 1f                          /* Pro cpu (Core 0) jump bypass */
407
408    movi    a2, _lx_intr_livelock_app
409    l32i    a2, a2, 0
410    bnei    a2, 2, 1f
411    movi    a2, _lx_intr_livelock_counter   /* _lx_intr_livelock_counter++ */
412    l32i    a3, a2, 0
413    addi    a3, a3, 1
414    s32i    a3, a2, 0
415
416    /*
417    The delay time can be calculated by the following formula:
418      T = ceil(0.25 + max(t1, t2)) us
419
420      t1 = 80 / f1, t2 = (1 + 14/N) * 20 / f2
421
422      f1: PSRAM access frequency, unit: MHz.
423      f2: Flash access frequency, unit: MHz.
424
425      When flash is slow/fast read, N = 1.
426      When flash is DOUT/DIO read, N = 2.
427      When flash is QOUT/QIO read, N = 4.
428    */
4291:  rsr.ccount  a2
430#if defined(CONFIG_ESPTOOLPY_FLASHMODE_QIO) || defined(CONFIG_ESPTOOLPY_FLASHMODE_QOUT)
431# if defined(CONFIG_ESPTOOLPY_FLASHFREQ_80M) && defined(CONFIG_SPIRAM_SPEED_80M)
432    movi    a3, 480
433# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_80M) && defined(CONFIG_SPIRAM_SPEED_40M)
434    movi    a3, 720
435# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_40M) && defined(CONFIG_SPIRAM_SPEED_40M)
436    movi    a3, 720
437# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_26M) && defined(CONFIG_SPIRAM_SPEED_40M)
438    movi    a3, 960
439# else
440    movi    a3, 1200
441# endif
442#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_DIO) || defined(CONFIG_ESPTOOLPY_FLASHMODE_DOUT)
443# if defined(CONFIG_ESPTOOLPY_FLASHFREQ_80M) && defined(CONFIG_SPIRAM_SPEED_80M)
444    movi    a3, 720
445# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_80M) && defined(CONFIG_SPIRAM_SPEED_40M)
446    movi    a3, 720
447# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_40M) && defined(CONFIG_SPIRAM_SPEED_40M)
448    movi    a3, 1200
449# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_26M) && defined(CONFIG_SPIRAM_SPEED_40M)
450    movi    a3, 1680
451# else
452    movi    a3, 2160
453# endif
454#endif
4552:  rsr.ccount  a4                          /* delay_us(N) */
456    sub     a4, a4, a2
457    bltu    a4, a3, 2b
458
459    beqz    a5, 2f
460    movi    a2, _lx_intr_livelock_app
461    l32i    a2, a2, 0
462    beqi    a2, 2, 8f
463    j       3f
464
4652:  movi    a2, _lx_intr_livelock_pro
466    l32i    a4, a2, 0
467    addi    a4, a4, 1
468    s32i    a4, a2, 0
469
470    movi    a2, _lx_intr_livelock_sync
471    movi    a3, 1
472    addx4   a3, a3, a2
473    l32i    a2, a2, 0
474    l32i    a3, a3, 0
475    and     a2, a2, a3
476    beqz    a2, 5f
477    j       1b
4785:  bgei    a4, 2, 4f
479    j       1b
480
481    /*
482    Pro cpu (Core 0) jump bypass, continue waiting, App cpu (Core 1)
483    can execute to here, unmap itself tg1 1st stage timeout interrupt
484    then restore registers and exit highint5/4.
485    */
4863:  intr_matrix_map DPORT_APP_MAC_INTR_MAP_REG, ETS_TG1_WDT_LEVEL_INTR_SOURCE, 16
487    j       9f
488
489    /*
490    Here, App cpu (Core 1) has exited isr, Pro cpu (Core 0) help the
491    App cpu map tg1 1st stage timeout interrupt clear tg1 interrupt.
492    */
4934:  intr_matrix_map DPORT_APP_MAC_INTR_MAP_REG, ETS_TG1_WDT_LEVEL_INTR_SOURCE, ETS_T1_WDT_INUM
494
4951:  movi    a2, _lx_intr_livelock_sync
496    movi    a4, 1
497    addx4   a3, a4, a2
498    l32i    a2, a2, 0
499    l32i    a3, a3, 0
500    and     a2, a2, a3
501    beqz    a2, 1b                          /* Wait for App cpu to enter highint5/4 again */
502
503    wdt_clr_intr_status TIMERG1
504    j       9f
505
506    /* Feed watchdog */
5078:  wdt_feed    TIMERG1
508
5099:  wsr     a0, PS                          /* restore interrupt level */
510
511    movi    a0, 0
512    beqz    a5, 1f
513    movi    a2, _lx_intr_livelock_app
514    l32i    a3, a2, 0
515    bnei    a3, 2, 1f
516    s32i    a0, a2, 0
517
5181:  bnez    a5, 2f
519    movi    a2, _lx_intr_livelock_pro
520    s32i    a0, a2, 0
5212:  movi    a2, _lx_intr_livelock_sync
522    addx4   a2, a5, a2
523    s32i    a0, a2, 0
524
525    /* Done. Restore registers and return. */
526    movi    a0, LX_INTR_STACK_SIZE
527    mull    a5, a5, a0
528    movi    a0, _lx_intr_stack
529    add     a0, a0, a5
530    l32i    a2, a0, LX_INTR_A2_OFFSET
531    l32i    a3, a0, LX_INTR_A3_OFFSET
532    l32i    a4, a0, LX_INTR_A4_OFFSET
533    rsync                                   /* ensure register restored */
534
535    rsr     a5, depc
536
537    rsr     a0, EXCSAVE_X                   /* restore a0 */
538    rfi     RFI_X
539
540#endif
541
542/* The linker has no reason to link in this file; all symbols it exports are already defined
543   (weakly!) in the default int handler. Define a symbol here so we can use it to have the
544   linker inspect this anyway. */
545
546    .global ld_include_highint_hdl
547ld_include_highint_hdl:
548