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 <stdbool.h>
9 #include <stdio.h>
10 #include "sdkconfig.h"
11 #include "hal/wdt_hal.h"
12 #include "hal/mwdt_ll.h"
13 #include "esp_err.h"
14 #include "esp_attr.h"
15 #include "esp_intr_alloc.h"
16 #include "esp_private/system_internal.h"
17 #include "esp_private/periph_ctrl.h"
18 #include "esp_private/esp_task_wdt_impl.h"
19
20 #define TWDT_INSTANCE WDT_MWDT0
21 #define TWDT_TICKS_PER_US 500
22 #define TWDT_PRESCALER MWDT_LL_DEFAULT_CLK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
23 #define TWDT_PERIPH_MODULE PERIPH_TIMG0_MODULE
24 #define TWDT_INTR_SOURCE ETS_TG0_WDT_LEVEL_INTR_SOURCE
25
26 /**
27 * Context for the software implementation of the Task WatchDog Timer.
28 * This will be passed as a parameter to public functions below. */
29 typedef struct {
30 wdt_hal_context_t hal;
31 intr_handle_t intr_handle;
32 } twdt_ctx_hard_t;
33
34 /**
35 * Declare the initial context as static. It will be passed to the
36 * task_wdt implementation as the implementation context in the
37 * init function. */
38 static twdt_ctx_hard_t init_context;
39
40
41
esp_task_wdt_impl_timer_allocate(const esp_task_wdt_config_t * config,twdt_isr_callback callback,twdt_ctx_t * obj)42 esp_err_t esp_task_wdt_impl_timer_allocate(const esp_task_wdt_config_t *config,
43 twdt_isr_callback callback,
44 twdt_ctx_t *obj)
45 {
46 esp_err_t ret = ESP_OK;
47 twdt_ctx_hard_t *ctx = &init_context;
48
49 if (config == NULL || obj == NULL) {
50 ret = ESP_ERR_INVALID_STATE;
51 }
52
53 if (ret == ESP_OK) {
54 esp_intr_alloc(TWDT_INTR_SOURCE, 0, callback, NULL, &ctx->intr_handle);
55 }
56
57 if (ret == ESP_OK) {
58 periph_module_enable(TWDT_PERIPH_MODULE);
59 wdt_hal_init(&ctx->hal, TWDT_INSTANCE, TWDT_PRESCALER, true);
60
61 wdt_hal_write_protect_disable(&ctx->hal);
62 // Configure 1st stage timeout and behavior
63 wdt_hal_config_stage(&ctx->hal, WDT_STAGE0, config->timeout_ms * (1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
64 // Configure 2nd stage timeout and behavior
65 wdt_hal_config_stage(&ctx->hal, WDT_STAGE1, config->timeout_ms * (2 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
66 // No need to enable to enable the WDT here, it will be enabled with `esp_task_wdt_impl_timer_restart`
67 wdt_hal_write_protect_enable(&ctx->hal);
68
69 /* Return the implementation context to the caller */
70 *obj = (twdt_ctx_t) ctx;
71 }
72
73 return ret;
74 }
75
76
esp_task_wdt_impl_timer_reconfigure(twdt_ctx_t obj,const esp_task_wdt_config_t * config)77 esp_err_t esp_task_wdt_impl_timer_reconfigure(twdt_ctx_t obj, const esp_task_wdt_config_t *config)
78 {
79 esp_err_t ret = ESP_OK;
80
81 twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
82
83 if (config == NULL || ctx == NULL) {
84 ret = ESP_ERR_INVALID_STATE;
85 }
86
87 if (ret == ESP_OK) {
88 wdt_hal_write_protect_disable(&ctx->hal);
89 /* Reconfigure the 1st and 2nd stage timeout */
90 wdt_hal_config_stage(&ctx->hal, WDT_STAGE0, config->timeout_ms * (1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
91 wdt_hal_config_stage(&ctx->hal, WDT_STAGE1, config->timeout_ms * (2 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
92 wdt_hal_write_protect_enable(&ctx->hal);
93 }
94
95 return ret;
96 }
97
98
esp_task_wdt_impl_timer_free(twdt_ctx_t obj)99 void esp_task_wdt_impl_timer_free(twdt_ctx_t obj)
100 {
101 twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
102
103 if (ctx != NULL) {
104 /* Stop hardware timer and the interrupt associated */
105 wdt_hal_deinit(&ctx->hal);
106 ESP_ERROR_CHECK(esp_intr_disable(ctx->intr_handle));
107
108 /* Disable the Timer Group module */
109 periph_module_disable(TWDT_PERIPH_MODULE);
110
111 /* Deregister interrupt */
112 ESP_ERROR_CHECK(esp_intr_free(ctx->intr_handle));
113 }
114 }
115
116
esp_task_wdt_impl_timer_feed(twdt_ctx_t obj)117 esp_err_t esp_task_wdt_impl_timer_feed(twdt_ctx_t obj)
118 {
119 esp_err_t ret = ESP_OK;
120 twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
121
122 if (ctx == NULL) {
123 ret = ESP_ERR_INVALID_STATE;
124 }
125
126 if (ret == ESP_OK) {
127 wdt_hal_write_protect_disable(&ctx->hal);
128 wdt_hal_feed(&ctx->hal);
129 wdt_hal_write_protect_enable(&ctx->hal);
130 }
131
132 return ret;
133 }
134
135
esp_task_wdt_impl_timeout_triggered(twdt_ctx_t obj)136 void esp_task_wdt_impl_timeout_triggered(twdt_ctx_t obj)
137 {
138 twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
139
140 if (ctx != NULL) {
141 /* Reset hardware timer so that 2nd stage timeout is not reached (will trigger system reset) */
142 wdt_hal_write_protect_disable(&ctx->hal);
143 wdt_hal_handle_intr(&ctx->hal); // Feeds WDT and clears acknowledges interrupt
144 wdt_hal_write_protect_enable(&ctx->hal);
145 }
146 }
147
148
esp_task_wdt_impl_timer_stop(twdt_ctx_t obj)149 esp_err_t esp_task_wdt_impl_timer_stop(twdt_ctx_t obj)
150 {
151 esp_err_t ret = ESP_OK;
152 twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
153
154 if (ctx == NULL) {
155 ret = ESP_ERR_INVALID_STATE;
156 }
157
158 if (ret == ESP_OK) {
159 wdt_hal_write_protect_disable(&ctx->hal);
160 wdt_hal_disable(&ctx->hal);
161 wdt_hal_write_protect_enable(&ctx->hal);
162 }
163
164 return ret;
165 }
166
167
esp_task_wdt_impl_timer_restart(twdt_ctx_t obj)168 esp_err_t esp_task_wdt_impl_timer_restart(twdt_ctx_t obj)
169 {
170 esp_err_t ret = ESP_OK;
171 twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
172
173 if (ctx == NULL) {
174 ret = ESP_ERR_INVALID_STATE;
175 }
176
177 if (ret == ESP_OK) {
178 wdt_hal_write_protect_disable(&ctx->hal);
179 wdt_hal_enable(&ctx->hal);
180 wdt_hal_feed(&ctx->hal);
181 wdt_hal_write_protect_enable(&ctx->hal);
182 }
183
184 return ret;
185 }
186