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