int_wdt.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*
  2. * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdint.h>
  7. #include <stdio.h>
  8. #include <stdbool.h>
  9. #include "sdkconfig.h"
  10. #include "soc/soc_caps.h"
  11. #include "hal/wdt_hal.h"
  12. #include "hal/mwdt_ll.h"
  13. #include "hal/timer_ll.h"
  14. #include "freertos/FreeRTOS.h"
  15. #include "esp_cpu.h"
  16. #include "esp_err.h"
  17. #include "esp_attr.h"
  18. #include "esp_log.h"
  19. #include "esp_intr_alloc.h"
  20. #include "esp_chip_info.h"
  21. #include "esp_freertos_hooks.h"
  22. #include "esp_private/periph_ctrl.h"
  23. #include "esp_private/esp_int_wdt.h"
  24. #if SOC_TIMER_GROUPS > 1
  25. /* If we have two hardware timer groups, use the second one for interrupt watchdog. */
  26. #define WDT_LEVEL_INTR_SOURCE ETS_TG1_WDT_LEVEL_INTR_SOURCE
  27. #define IWDT_PRESCALER MWDT_LL_DEFAULT_CLK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
  28. #define IWDT_TICKS_PER_US 500
  29. #define IWDT_INSTANCE WDT_MWDT1
  30. #define IWDT_INITIAL_TIMEOUT_S 5
  31. #define IWDT_PERIPH PERIPH_TIMG1_MODULE
  32. #define IWDT_TIMER_GROUP 1
  33. #else
  34. #define WDT_LEVEL_INTR_SOURCE ETS_TG0_WDT_LEVEL_INTR_SOURCE
  35. #define IWDT_PRESCALER MWDT_LL_DEFAULT_CLK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
  36. #define IWDT_TICKS_PER_US 500
  37. #define IWDT_INSTANCE WDT_MWDT0
  38. #define IWDT_INITIAL_TIMEOUT_S 5
  39. #define IWDT_PERIPH PERIPH_TIMG0_MODULE
  40. #define IWDT_TIMER_GROUP 0
  41. #endif // SOC_TIMER_GROUPS > 1
  42. #if CONFIG_ESP_INT_WDT
  43. static wdt_hal_context_t iwdt_context;
  44. #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
  45. /*
  46. * This parameter is used to indicate the response time of Interrupt watchdog to
  47. * identify the live lock.
  48. */
  49. #define IWDT_LIVELOCK_TIMEOUT_MS (20)
  50. extern uint32_t _lx_intr_livelock_counter, _lx_intr_livelock_max;
  51. #endif
  52. #if CONFIG_ESP_INT_WDT_CHECK_CPU1
  53. volatile bool int_wdt_cpu1_ticked = false;
  54. #endif
  55. static void IRAM_ATTR tick_hook(void)
  56. {
  57. #if CONFIG_ESP_INT_WDT_CHECK_CPU1
  58. if (esp_cpu_get_core_id() != 0) {
  59. int_wdt_cpu1_ticked = true;
  60. } else {
  61. // Only feed wdt if app cpu also ticked.
  62. if (int_wdt_cpu1_ticked) {
  63. // Todo: Check if there's a way to avoid reconfiguring the stages on each feed.
  64. wdt_hal_write_protect_disable(&iwdt_context);
  65. // Reconfigure stage timeouts
  66. #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
  67. _lx_intr_livelock_counter = 0;
  68. wdt_hal_config_stage(&iwdt_context, WDT_STAGE0,
  69. CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US / (_lx_intr_livelock_max + 1), WDT_STAGE_ACTION_INT); // Set timeout before interrupt
  70. #else
  71. wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT); // Set timeout before interrupt
  72. #endif
  73. wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, 2 * CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); // Set timeout before reset
  74. wdt_hal_feed(&iwdt_context);
  75. wdt_hal_write_protect_enable(&iwdt_context);
  76. int_wdt_cpu1_ticked = false;
  77. }
  78. }
  79. #else // CONFIG_ESP_INT_WDT_CHECK_CPU1
  80. if (esp_cpu_get_core_id() != 0) {
  81. return;
  82. } else {
  83. // Todo: Check if there's a way to avoid reconfiguring the stages on each feed.
  84. wdt_hal_write_protect_disable(&iwdt_context);
  85. // Reconfigure stage timeouts
  86. wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT); // Set timeout before interrupt
  87. wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, 2 * CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); // Set timeout before reset
  88. wdt_hal_feed(&iwdt_context);
  89. wdt_hal_write_protect_enable(&iwdt_context);
  90. }
  91. #endif // CONFIG_ESP_INT_WDT_CHECK_CPU1
  92. }
  93. void esp_int_wdt_init(void)
  94. {
  95. PERIPH_RCC_ACQUIRE_ATOMIC(IWDT_PERIPH, ref_count) {
  96. if (ref_count == 0) {
  97. timer_ll_enable_bus_clock(IWDT_TIMER_GROUP, true);
  98. timer_ll_reset_register(IWDT_TIMER_GROUP);
  99. }
  100. }
  101. /*
  102. * Initialize the WDT timeout stages. Note that the initial timeout is set to 5 seconds as variable startup times of
  103. * each CPU can lead to a timeout. The tick hooks will set the WDT timers to the actual timeout.
  104. * Todo: Fix this
  105. */
  106. wdt_hal_init(&iwdt_context, IWDT_INSTANCE, IWDT_PRESCALER, true);
  107. wdt_hal_write_protect_disable(&iwdt_context);
  108. wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, IWDT_INITIAL_TIMEOUT_S * 1000000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT);
  109. wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, IWDT_INITIAL_TIMEOUT_S * 1000000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM);
  110. wdt_hal_enable(&iwdt_context);
  111. wdt_hal_write_protect_enable(&iwdt_context);
  112. #if (CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI)
  113. #define APB_DCRSET (0x200c)
  114. #define APB_ITCTRL (0x3f00)
  115. #define ERI_ADDR(APB) (0x100000 + (APB))
  116. #define _SYM2STR(x) # x
  117. #define SYM2STR(x) _SYM2STR(x)
  118. uint32_t eriadrs, scratch = 0, immediate = 0;
  119. if (soc_has_cache_lock_bug()) {
  120. if (xPortGetCoreID() != CONFIG_BTDM_CTRL_PINNED_TO_CORE) {
  121. __asm__ __volatile__ (
  122. /* Enable Xtensa Debug Module Integration Mode */
  123. "movi %[ERI], " SYM2STR(ERI_ADDR(APB_ITCTRL)) "\n"
  124. "rer %[REG], %[ERI]\n"
  125. "movi %[IMM], 1\n"
  126. "or %[REG], %[IMM], %[REG]\n"
  127. "wer %[REG], %[ERI]\n"
  128. /* Enable Xtensa Debug Module BreakIn signal */
  129. "movi %[ERI], " SYM2STR(ERI_ADDR(APB_DCRSET)) "\n"
  130. "rer %[REG], %[ERI]\n"
  131. "movi %[IMM], 0x10000\n"
  132. "or %[REG], %[IMM], %[REG]\n"
  133. "wer %[REG], %[ERI]\n"
  134. : [ERI] "=r" (eriadrs), [REG] "+r" (scratch), [IMM] "+r" (immediate)
  135. );
  136. }
  137. }
  138. #endif // (CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI)
  139. }
  140. void esp_int_wdt_cpu_init(void)
  141. {
  142. assert((CONFIG_ESP_INT_WDT_TIMEOUT_MS >= (portTICK_PERIOD_MS << 1)) && "Interrupt watchdog timeout needs to be at least twice the RTOS tick period!");
  143. // Register tick hook for current CPU to feed the INT WDT
  144. esp_register_freertos_tick_hook_for_cpu(tick_hook, esp_cpu_get_core_id());
  145. /*
  146. * Register INT WDT interrupt for current CPU. We do this manually as the timeout interrupt should call an assembly
  147. * panic handler (see riscv/vector.S and xtensa_vectors.S).
  148. */
  149. esp_intr_disable_source(ETS_INT_WDT_INUM);
  150. esp_rom_route_intr_matrix(esp_cpu_get_core_id(), WDT_LEVEL_INTR_SOURCE, ETS_INT_WDT_INUM);
  151. #if SOC_CPU_HAS_FLEXIBLE_INTC
  152. esp_cpu_intr_set_type(ETS_INT_WDT_INUM, INTR_TYPE_LEVEL);
  153. esp_cpu_intr_set_priority(ETS_INT_WDT_INUM, SOC_INTERRUPT_LEVEL_MEDIUM);
  154. #endif
  155. #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
  156. /*
  157. * This is a workaround for issue 3.15 in "ESP32 ECO and workarounds for
  158. * Bugs" document.
  159. */
  160. _lx_intr_livelock_counter = 0;
  161. if (soc_has_cache_lock_bug()) {
  162. assert((portTICK_PERIOD_MS << 1) <= IWDT_LIVELOCK_TIMEOUT_MS);
  163. assert(CONFIG_ESP_INT_WDT_TIMEOUT_MS >= (IWDT_LIVELOCK_TIMEOUT_MS * 3));
  164. _lx_intr_livelock_max = CONFIG_ESP_INT_WDT_TIMEOUT_MS / IWDT_LIVELOCK_TIMEOUT_MS - 1;
  165. }
  166. #endif
  167. esp_intr_enable_source(ETS_INT_WDT_INUM);
  168. }
  169. #endif // CONFIG_ESP_INT_WDT