/* * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include "xtensa_context.h" #include "freertos/xtensa_rtos.h" #include "esp_private/panic_reason.h" #include "sdkconfig.h" #include "soc/soc.h" #include "soc/soc_caps.h" #include "soc/dport_reg.h" #include "soc/timer_group_reg.h" /* Interrupt , a high-priority interrupt, is used for several things: - IPC_ISR handler - Cache error panic handler - Interrupt watchdog panic handler */ #if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 #define LX_INTR_STACK_SIZE 12 #define LX_INTR_A2_OFFSET 0 #define LX_INTR_A3_OFFSET 4 #define LX_INTR_A4_OFFSET 8 #define EPC_X EPC_5 #define EXCSAVE_X EXCSAVE_5 #define RFI_X 5 #define xt_highintx xt_highint5 #elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4 #define LX_INTR_STACK_SIZE 12 #define LX_INTR_A2_OFFSET 0 #define LX_INTR_A3_OFFSET 4 #define LX_INTR_A4_OFFSET 8 #define EPC_X EPC_4 #define EXCSAVE_X EXCSAVE_4 #define RFI_X 4 #define xt_highintx xt_highint4 #endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */ /* -------------------------------------------------------------------------------- Macro wdt_clr_intr_status - Clear the WDT interrupt status. Macro wdt_feed - Feed the WDT. Input : "dev" - Beginning address of the peripheral registers Macro get_int_status_tg1wdt - Get the ETS_TG1_WDT_LEVEL_INTR_SOURCE bit in interrupt status output : "reg" - Store the result into the reg -------------------------------------------------------------------------------- */ #define TIMG1_REG_OFFSET(reg) ((reg) - REG_TIMG_BASE(1)) #define TIMG1_WDTWPROTECT_OFFSET TIMG1_REG_OFFSET(TIMG_WDTWPROTECT_REG(1)) #define TIMG1_INT_CLR_OFFSET TIMG1_REG_OFFSET(TIMG_INT_CLR_TIMERS_REG(1)) #define TIMG1_WDT_STG0_HOLD_OFFSET TIMG1_REG_OFFSET(TIMG_WDTCONFIG2_REG(1)) #define TIMG1_WDT_STG1_HOLD_OFFSET TIMG1_REG_OFFSET(TIMG_WDTCONFIG3_REG(1)) #define TIMG1_WDT_FEED_OFFSET TIMG1_REG_OFFSET(TIMG_WDTFEED_REG(1)) #define UART0_DATA_REG (0x3FF40078) #define TIMG_WDT_WKEY_VALUE 0x50D83AA1 #define ETS_TG1_WDT_LEVEL_INTR_SOURCE 20 .macro wdt_clr_intr_status dev movi a2, \dev movi a3, TIMG_WDT_WKEY_VALUE s32i a3, a2, TIMG1_WDTWPROTECT_OFFSET /* disable write protect */ memw l32i a4, a2, TIMG1_INT_CLR_OFFSET memw movi a3, 4 or a3, a4, a3 s32i a3, a2, TIMG1_INT_CLR_OFFSET /* clear 1st stage timeout interrupt */ memw movi a3, 0 s32i a3, a2, TIMG1_WDTWPROTECT_OFFSET /* enable write protect */ memw .endm .macro wdt_feed dev movi a2, \dev movi a3, TIMG_WDT_WKEY_VALUE s32i a3, a2, TIMG1_WDTWPROTECT_OFFSET /* disable write protect */ memw movi a4, _lx_intr_livelock_max l32i a4, a4, 0 memw addi a4, a4, 1 movi a3, (CONFIG_ESP_INT_WDT_TIMEOUT_MS<<1) quou a3, a3, a4 s32i a3, a2, TIMG1_WDT_STG0_HOLD_OFFSET /* set timeout before interrupt */ memw movi a3, (CONFIG_ESP_INT_WDT_TIMEOUT_MS<<2) s32i a3, a2, TIMG1_WDT_STG1_HOLD_OFFSET /* set timeout before system reset */ memw movi a3, 1 s32i a3, a2, TIMG1_WDT_FEED_OFFSET /* feed wdt */ memw movi a3, 0 s32i a3, a2, TIMG1_WDTWPROTECT_OFFSET /* enable write protect */ memw .endm .macro get_int_status_tg1wdt reg rsr \reg, INTERRUPT extui \reg, \reg, ETS_T1_WDT_CACHEERR_INUM, 1 beqz \reg, 99f /* not ETS_T1_WDT_INUM or ETS_CACHEERR_INUM */ getcoreid \reg bnez \reg, 98f /* core 0 */ movi \reg, UART0_DATA_REG l32i \reg, \reg, 0 /* Workaround for DPORT read error, for silicon revision 0~2 (ECO V0 ~ ECO V2). */ movi \reg, DPORT_PRO_INTR_STATUS_0_REG l32i \reg, \reg, 0 extui \reg, \reg, ETS_TG1_WDT_LEVEL_INTR_SOURCE, 1 j 99f 98: /* core 1 */ movi \reg, UART0_DATA_REG l32i \reg, \reg, 0 /* Workaround for DPORT read error, for silicon revision 0~2 (ECO V0 ~ ECO V2). */ movi \reg, DPORT_APP_INTR_STATUS_0_REG l32i \reg, \reg, 0 extui \reg, \reg, ETS_TG1_WDT_LEVEL_INTR_SOURCE, 1 99: .endm .data _lx_intr_stack: .space LX_INTR_STACK_SIZE*CONFIG_FREERTOS_NUMBER_OF_CORES /* This allocates stacks for each individual CPU. */ #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT .global _lx_intr_livelock_counter .global _lx_intr_livelock_max .align 16 _lx_intr_livelock_counter: .word 0 _lx_intr_livelock_max: .word 0 _lx_intr_livelock_sync: .word 0, 0 _lx_intr_livelock_app: .word 0 _lx_intr_livelock_pro: .word 0 #endif .section .iram1,"ax" .global xt_highintx .type xt_highintx,@function .align 4 xt_highintx: #ifndef CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE /* See if we're here for the IPC_ISR interrupt */ rsr a0, INTERRUPT extui a0, a0, ETS_IPC_ISR_INUM, 1 beqz a0, 1f /* Jump to `esp_ipc_isr_handler` which is non-returning. We need to use `jx` * because on Xtensa, `j` instruction can only refer to a label which * is in the range [-131068;+131075]. If the destination is out of scope, * linking will fail. So, to make sure we will always be able to jump to * that subroutine, retrieve its address and store it in a register. */ movi a0, esp_ipc_isr_handler jx a0 1: #endif /* not CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE */ #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT #if CONFIG_BTDM_CTRL_HLI /* Timer 2 interrupt */ rsr a0, INTENABLE extui a0, a0, 16, 1 beqz a0, 1f rsr a0, INTERRUPT extui a0, a0, 16, 1 bnez a0, .handle_multicore_debug_int 1: #endif /* CONFIG_BTDM_CTRL_HLI */ /* ETS_T1_WDT_INUM */ #if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 get_int_status_tg1wdt a0 #elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4 /* See if we're here for the tg1 watchdog interrupt */ rsr a0, INTERRUPT extui a0, a0, ETS_T1_WDT_INUM, 1 #endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */ beqz a0, 1f wsr a5, depc /* use DEPC as temp storage */ movi a0, _lx_intr_livelock_counter l32i a0, a0, 0 movi a5, _lx_intr_livelock_max l32i a5, a5, 0 bltu a0, a5, .handle_livelock_int /* _lx_intr_livelock_counter < _lx_intr_livelock_max */ rsr a5, depc /* restore a5 */ #endif 1: /* ETS_CACHEERR_INUM or ETS_T1_WDT_INUM */ /* Allocate exception frame and save minimal context. */ mov a0, sp addi sp, sp, -XT_STK_FRMSZ s32i a0, sp, XT_STK_A1 #if XCHAL_HAVE_WINDOWED s32e a0, sp, -12 /* for debug backtrace */ #endif rsr a0, PS /* save interruptee's PS */ s32i a0, sp, XT_STK_PS rsr a0, EPC_X /* save interruptee's PC */ s32i a0, sp, XT_STK_PC rsr a0, EXCSAVE_X /* save interruptee's a0 */ s32i a0, sp, XT_STK_A0 #if XCHAL_HAVE_WINDOWED s32e a0, sp, -16 /* for debug backtrace */ #endif s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ call0 _xt_context_save /* Save vaddr into exception frame */ rsr a0, EXCVADDR s32i a0, sp, XT_STK_EXCVADDR /* Figure out reason, save into EXCCAUSE reg */ #if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 get_int_status_tg1wdt a0 bnez a0, 1f /* TODO: Clear the MEMACCESS_ERR interrupt status. */ #elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4 rsr a0, INTERRUPT extui a0, a0, ETS_MEMACCESS_ERR_INUM, 1 /* get cacheerr int bit */ beqz a0, 1f #endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */ /* Kill this interrupt; we cannot reset it. */ rsr a0, INTENABLE movi a4, ~(1<