1 /*
2 * Copyright (c) 2015 - 2023, 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_channel_feed(nrfx_wdt_t const * p_instance,nrfx_wdt_channel_id channel_id)258 void nrfx_wdt_channel_feed(nrfx_wdt_t const * p_instance, nrfx_wdt_channel_id channel_id)
259 {
260 NRFX_ASSERT(m_cb[p_instance->drv_inst_idx].state == NRFX_DRV_STATE_POWERED_ON);
261 nrfy_wdt_reload_request_set(p_instance->p_reg, channel_id);
262 }
263
264 #if NRFX_WDT_HAS_STOP
nrfx_wdt_stop(nrfx_wdt_t const * p_instance)265 nrfx_err_t nrfx_wdt_stop(nrfx_wdt_t const * p_instance)
266 {
267 wdt_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
268 NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
269
270 if (!p_cb->stoppable)
271 {
272 return NRFX_ERROR_FORBIDDEN;
273 }
274
275 nrfy_wdt_task_stop_enable_set(p_instance->p_reg, true);
276 nrfy_wdt_task_trigger(p_instance->p_reg, NRF_WDT_TASK_STOP);
277 nrfy_wdt_task_stop_enable_set(p_instance->p_reg, false);
278
279 #if NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
280 while (!nrfy_wdt_events_process(p_instance->p_reg,
281 NRFY_EVENT_TO_INT_BITMASK(NRF_WDT_EVENT_STOPPED)))
282 {}
283 p_cb->state = NRFX_DRV_STATE_INITIALIZED;
284 #endif
285
286 return NRFX_SUCCESS;
287 }
288 #endif
289
290 #if !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
irq_handler(NRF_WDT_Type * p_reg,wdt_control_block_t * p_cb)291 static void irq_handler(NRF_WDT_Type * p_reg, wdt_control_block_t * p_cb)
292 {
293 /* Clearing timeout event also causes request status register to be cleared, so read it
294 * before clearing. */
295 uint32_t requests = nrf_wdt_request_status_get(p_reg);
296
297 uint32_t evt_mask = nrfy_wdt_events_process(p_reg,
298 #if NRFX_WDT_HAS_STOP
299 NRFY_EVENT_TO_INT_BITMASK(NRF_WDT_EVENT_STOPPED) |
300 #endif
301 NRFY_EVENT_TO_INT_BITMASK(NRF_WDT_EVENT_TIMEOUT)
302 );
303
304 if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_WDT_EVENT_TIMEOUT))
305 {
306 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
307 p_cb->wdt_event_handler(NRF_WDT_EVENT_TIMEOUT, requests, p_cb->p_context);
308 #else
309 p_cb->wdt_event_handler(requests);
310 #endif
311 }
312
313 #if NRFX_WDT_HAS_STOP
314 if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_WDT_EVENT_STOPPED))
315 {
316 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
317 p_cb->state = NRFX_DRV_STATE_INITIALIZED;
318 p_cb->wdt_event_handler(NRF_WDT_EVENT_STOPPED, 0, p_cb->p_context);
319 #endif
320 }
321 #endif
322 }
323
324 NRFX_INSTANCE_IRQ_HANDLERS(WDT, wdt)
325
326 #endif // !NRFX_CHECK(NRFX_WDT_CONFIG_NO_IRQ)
327
328 #endif // NRFX_CHECK(NRFX_WDT_ENABLED)
329