1 /*
2 * Copyright (c) 2015 - 2025, Nordic Semiconductor ASA
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <nrfx.h>
35
36 #if NRFX_CHECK(NRFX_WDT_ENABLED)
37
38 #include <nrfx_wdt.h>
39
40 #if !NRFX_FEATURE_PRESENT(NRFX_WDT, _ENABLED)
41 #error "No enabled WDT instances. Check <nrfx_config.h>."
42 #endif
43
44 #define NRFX_LOG_MODULE WDT
45 #include <nrfx_log.h>
46
47 // Control block - driver instance local data.
48 typedef struct
49 {
50 nrfx_drv_state_t state;
51 uint8_t alloc_index;
52 #if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
53 nrfx_wdt_event_handler_t wdt_event_handler;
54 void * p_context;
55 #endif
56 #if NRFX_WDT_HAS_STOP
57 bool stoppable;
58 #endif
59 } wdt_control_block_t;
60
61 static wdt_control_block_t m_cb[NRFX_WDT_ENABLED_COUNT];
62
wdt_configure(nrfx_wdt_t const * p_instance,nrfx_wdt_config_t const * p_config)63 static void wdt_configure(nrfx_wdt_t const * p_instance,
64 nrfx_wdt_config_t const * p_config)
65 {
66 uint64_t ticks = (p_config->reload_value * 32768ULL) / 1000;
67 NRFX_ASSERT(ticks <= UINT32_MAX);
68
69 nrfy_wdt_config_t nrfy_conf = {
70 .behaviour = p_config->behaviour,
71 .reload_value = (uint32_t)ticks,
72 };
73
74 nrfy_wdt_periph_configure(p_instance->p_reg, &nrfy_conf);
75
76 wdt_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
77
78 #if NRFX_WDT_HAS_STOP
79 p_cb->stoppable = (bool)(p_config->behaviour & NRF_WDT_BEHAVIOUR_STOP_ENABLE_MASK);
80 #endif
81
82 #if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
83 if (p_cb->wdt_event_handler)
84 {
85 uint32_t mask = NRF_WDT_INT_TIMEOUT_MASK;
86
87 #if NRFX_WDT_HAS_STOP
88 if (p_cb->stoppable)
89 {
90 mask |= NRF_WDT_INT_STOPPED_MASK;
91 }
92 #endif
93
94 nrfy_wdt_int_init(p_instance->p_reg,
95 mask,
96 p_config->interrupt_priority,
97 true);
98 }
99 #endif
100 }
101
wdt_init(nrfx_wdt_t const * p_instance,nrfx_wdt_config_t const * p_config,nrfx_wdt_event_handler_t wdt_event_handler,void * p_context)102 static nrfx_err_t wdt_init(nrfx_wdt_t const * p_instance,
103 nrfx_wdt_config_t const * p_config,
104 nrfx_wdt_event_handler_t wdt_event_handler,
105 void * p_context)
106 {
107 nrfx_err_t err_code;
108
109 wdt_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
110
111 #if NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
112 (void)wdt_event_handler;
113 (void)p_context;
114 #else
115 p_cb->wdt_event_handler = wdt_event_handler;
116 p_cb->p_context = p_context;
117 #endif
118
119 if (p_cb->state == NRFX_DRV_STATE_UNINITIALIZED)
120 {
121 p_cb->state = NRFX_DRV_STATE_INITIALIZED;
122 }
123 else
124 {
125 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
126 err_code = NRFX_ERROR_ALREADY;
127 #else
128 err_code = NRFX_ERROR_INVALID_STATE;
129 #endif
130 NRFX_LOG_WARNING("Function: %s, error code: %s.",
131 __func__,
132 NRFX_LOG_ERROR_STRING_GET(err_code));
133 return err_code;
134 }
135
136 if (p_config)
137 {
138 wdt_configure(p_instance, p_config);
139 }
140
141 err_code = NRFX_SUCCESS;
142 NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
143 return err_code;
144 }
145
146 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
nrfx_wdt_init(nrfx_wdt_t const * p_instance,nrfx_wdt_config_t const * p_config,nrfx_wdt_event_handler_t wdt_event_handler,void * p_context)147 nrfx_err_t nrfx_wdt_init(nrfx_wdt_t const * p_instance,
148 nrfx_wdt_config_t const * p_config,
149 nrfx_wdt_event_handler_t wdt_event_handler,
150 void * p_context)
151 {
152 return wdt_init(p_instance, p_config, wdt_event_handler, p_context);
153 }
154 #else
nrfx_wdt_init(nrfx_wdt_t const * p_instance,nrfx_wdt_config_t const * p_config,nrfx_wdt_event_handler_t wdt_event_handler)155 nrfx_err_t nrfx_wdt_init(nrfx_wdt_t const * p_instance,
156 nrfx_wdt_config_t const * p_config,
157 nrfx_wdt_event_handler_t wdt_event_handler)
158 {
159 return wdt_init(p_instance, p_config, wdt_event_handler, NULL);
160 }
161 #endif
162
nrfx_wdt_reconfigure(nrfx_wdt_t const * p_instance,nrfx_wdt_config_t const * p_config)163 nrfx_err_t nrfx_wdt_reconfigure(nrfx_wdt_t const * p_instance,
164 nrfx_wdt_config_t const * p_config)
165 {
166 NRFX_ASSERT(p_config);
167
168 nrfx_err_t err_code;
169 wdt_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
170
171 if (p_cb->state == NRFX_DRV_STATE_UNINITIALIZED)
172 {
173 err_code = NRFX_ERROR_INVALID_STATE;
174 NRFX_LOG_WARNING("Function: %s, error code: %s.",
175 __func__,
176 NRFX_LOG_ERROR_STRING_GET(err_code));
177 return err_code;
178 }
179 else if (p_cb->state == NRFX_DRV_STATE_POWERED_ON)
180 {
181 err_code = NRFX_ERROR_BUSY;
182 NRFX_LOG_WARNING("Function: %s, error code: %s.",
183 __func__,
184 NRFX_LOG_ERROR_STRING_GET(err_code));
185 return err_code;
186 }
187 wdt_configure(p_instance, p_config);
188 return NRFX_SUCCESS;
189 }
190
nrfx_wdt_uninit(nrfx_wdt_t const * p_instance)191 void nrfx_wdt_uninit(nrfx_wdt_t const * p_instance)
192 {
193 wdt_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
194 NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
195
196 #if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
197 if (p_cb->wdt_event_handler)
198 {
199 nrfy_wdt_int_uninit(p_instance->p_reg);
200 }
201 #endif
202 p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
203 NRFX_LOG_INFO("Uninitialized.");
204 }
205
nrfx_wdt_init_check(nrfx_wdt_t const * p_instance)206 bool nrfx_wdt_init_check(nrfx_wdt_t const * p_instance)
207 {
208 wdt_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
209
210 return (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
211 }
212
nrfx_wdt_enable(nrfx_wdt_t const * p_instance)213 void nrfx_wdt_enable(nrfx_wdt_t const * p_instance)
214 {
215 wdt_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
216 NRFX_ASSERT(p_cb->alloc_index != 0);
217 NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
218 nrfy_wdt_task_trigger(p_instance->p_reg, NRF_WDT_TASK_START);
219 p_cb->state = NRFX_DRV_STATE_POWERED_ON;
220 NRFX_LOG_INFO("Enabled.");
221 }
222
nrfx_wdt_feed(nrfx_wdt_t const * p_instance)223 void nrfx_wdt_feed(nrfx_wdt_t const * p_instance)
224 {
225 wdt_control_block_t const * p_cb = &m_cb[p_instance->drv_inst_idx];
226 NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_POWERED_ON);
227 for (uint8_t i = 0; i < p_cb->alloc_index; i++)
228 {
229 nrfy_wdt_reload_request_set(p_instance->p_reg, (nrf_wdt_rr_register_t)(NRF_WDT_RR0 + i));
230 }
231 }
232
nrfx_wdt_channel_alloc(nrfx_wdt_t const * p_instance,nrfx_wdt_channel_id * p_channel_id)233 nrfx_err_t nrfx_wdt_channel_alloc(nrfx_wdt_t const * p_instance, nrfx_wdt_channel_id * p_channel_id)
234 {
235 nrfx_err_t result;
236 wdt_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
237
238 NRFX_ASSERT(p_channel_id);
239 NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
240
241 NRFX_CRITICAL_SECTION_ENTER();
242 if (p_cb->alloc_index < NRF_WDT_CHANNEL_NUMBER)
243 {
244 *p_channel_id = (nrfx_wdt_channel_id)(NRF_WDT_RR0 + p_cb->alloc_index);
245 p_cb->alloc_index++;
246 nrfy_wdt_reload_request_enable(p_instance->p_reg, *p_channel_id);
247 result = NRFX_SUCCESS;
248 }
249 else
250 {
251 result = NRFX_ERROR_NO_MEM;
252 }
253 NRFX_CRITICAL_SECTION_EXIT();
254 NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(result));
255 return result;
256 }
257
nrfx_wdt_channels_free(nrfx_wdt_t const * p_instance)258 void nrfx_wdt_channels_free(nrfx_wdt_t const * p_instance)
259 {
260 uint8_t index;
261 nrfx_wdt_channel_id channel_id;
262 wdt_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
263 NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
264
265 NRFX_CRITICAL_SECTION_ENTER();
266 for (index = 0; index < p_cb->alloc_index; index++)
267 {
268 channel_id = (nrfx_wdt_channel_id)(NRF_WDT_RR0 + index);
269 nrfy_wdt_reload_request_disable(p_instance->p_reg, channel_id);
270 }
271 p_cb->alloc_index = 0;
272 NRFX_CRITICAL_SECTION_EXIT();
273 }
274
nrfx_wdt_channel_feed(nrfx_wdt_t const * p_instance,nrfx_wdt_channel_id channel_id)275 void nrfx_wdt_channel_feed(nrfx_wdt_t const * p_instance, nrfx_wdt_channel_id channel_id)
276 {
277 NRFX_ASSERT(m_cb[p_instance->drv_inst_idx].state == NRFX_DRV_STATE_POWERED_ON);
278 nrfy_wdt_reload_request_set(p_instance->p_reg, channel_id);
279 }
280
281 #if NRFX_WDT_HAS_STOP
nrfx_wdt_stop(nrfx_wdt_t const * p_instance)282 nrfx_err_t nrfx_wdt_stop(nrfx_wdt_t const * p_instance)
283 {
284 wdt_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
285 NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
286
287 if (!p_cb->stoppable)
288 {
289 return NRFX_ERROR_FORBIDDEN;
290 }
291
292 nrfy_wdt_task_stop_enable_set(p_instance->p_reg, true);
293 nrfy_wdt_task_trigger(p_instance->p_reg, NRF_WDT_TASK_STOP);
294 nrfy_wdt_task_stop_enable_set(p_instance->p_reg, false);
295
296 #if NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
297 while (!nrfy_wdt_events_process(p_instance->p_reg,
298 NRFY_EVENT_TO_INT_BITMASK(NRF_WDT_EVENT_STOPPED)))
299 {}
300 p_cb->state = NRFX_DRV_STATE_INITIALIZED;
301 #endif
302
303 return NRFX_SUCCESS;
304 }
305 #endif
306
307 #if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
irq_handler(NRF_WDT_Type * p_reg,wdt_control_block_t * p_cb)308 static void irq_handler(NRF_WDT_Type * p_reg, wdt_control_block_t * p_cb)
309 {
310 /* Clearing timeout event also causes request status register to be cleared, so read it
311 * before clearing. */
312 uint32_t requests = nrf_wdt_request_status_get(p_reg);
313
314 uint32_t evt_mask = nrfy_wdt_events_process(p_reg,
315 #if NRFX_WDT_HAS_STOP
316 NRFY_EVENT_TO_INT_BITMASK(NRF_WDT_EVENT_STOPPED) |
317 #endif
318 NRFY_EVENT_TO_INT_BITMASK(NRF_WDT_EVENT_TIMEOUT)
319 );
320
321 if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_WDT_EVENT_TIMEOUT))
322 {
323 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
324 p_cb->wdt_event_handler(NRF_WDT_EVENT_TIMEOUT, requests, p_cb->p_context);
325 #else
326 p_cb->wdt_event_handler(requests);
327 #endif
328 }
329
330 #if NRFX_WDT_HAS_STOP
331 if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_WDT_EVENT_STOPPED))
332 {
333 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
334 p_cb->state = NRFX_DRV_STATE_INITIALIZED;
335 p_cb->wdt_event_handler(NRF_WDT_EVENT_STOPPED, 0, p_cb->p_context);
336 #endif
337 }
338 #endif
339 }
340
341 NRFX_INSTANCE_IRQ_HANDLERS(WDT, wdt)
342
343 #endif // !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
344
345 #endif // NRFX_CHECK(NRFX_WDT_ENABLED)
346