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