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