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