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