int_wdt.c 6.8 KB

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