1 /*
2 * Copyright (c) 2018, Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/math_extras.h>
9 #include <nrfx_wdt.h>
10 #include <zephyr/drivers/watchdog.h>
11
12 #define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
13 #include <zephyr/logging/log.h>
14 #include <zephyr/irq.h>
15 LOG_MODULE_REGISTER(wdt_nrfx);
16
17 #if !CONFIG_WDT_NRFX_NO_IRQ && NRF_WDT_HAS_STOP
18 #define WDT_NRFX_SYNC_STOP 1
19 #endif
20
21 struct wdt_nrfx_data {
22 wdt_callback_t m_callbacks[NRF_WDT_CHANNEL_NUMBER];
23 uint32_t m_timeout;
24 uint8_t m_allocated_channels;
25 bool enabled;
26 #if defined(WDT_NRFX_SYNC_STOP)
27 struct k_sem sync_stop;
28 #endif
29 };
30
31 struct wdt_nrfx_config {
32 nrfx_wdt_t wdt;
33 };
34
wdt_nrf_setup(const struct device * dev,uint8_t options)35 static int wdt_nrf_setup(const struct device *dev, uint8_t options)
36 {
37 const struct wdt_nrfx_config *config = dev->config;
38 struct wdt_nrfx_data *data = dev->data;
39 nrfx_err_t err_code;
40
41 nrfx_wdt_config_t wdt_config = {
42 .reload_value = data->m_timeout
43 };
44
45 #if NRF_WDT_HAS_STOP
46 wdt_config.behaviour |= NRF_WDT_BEHAVIOUR_STOP_ENABLE_MASK;
47 #endif
48
49 if (!(options & WDT_OPT_PAUSE_IN_SLEEP)) {
50 wdt_config.behaviour |= NRF_WDT_BEHAVIOUR_RUN_SLEEP_MASK;
51 }
52
53 if (!(options & WDT_OPT_PAUSE_HALTED_BY_DBG)) {
54 wdt_config.behaviour |= NRF_WDT_BEHAVIOUR_RUN_HALT_MASK;
55 }
56
57 err_code = nrfx_wdt_reconfigure(&config->wdt, &wdt_config);
58
59 if (err_code != NRFX_SUCCESS) {
60 return -EBUSY;
61 }
62
63 nrfx_wdt_enable(&config->wdt);
64
65 data->enabled = true;
66 return 0;
67 }
68
wdt_nrf_disable(const struct device * dev)69 static int wdt_nrf_disable(const struct device *dev)
70 {
71 #if NRFX_WDT_HAS_STOP
72 const struct wdt_nrfx_config *config = dev->config;
73 struct wdt_nrfx_data *data = dev->data;
74 nrfx_err_t err_code;
75 int channel_id;
76
77 err_code = nrfx_wdt_stop(&config->wdt);
78
79 if (err_code != NRFX_SUCCESS) {
80 /* This can only happen if wdt_nrf_setup() is not called first. */
81 return -EFAULT;
82 }
83
84 #if defined(WDT_NRFX_SYNC_STOP)
85 k_sem_take(&data->sync_stop, K_FOREVER);
86 #endif
87
88 nrfx_wdt_channels_free(&config->wdt);
89
90 for (channel_id = 0; channel_id < data->m_allocated_channels; channel_id++) {
91 data->m_callbacks[channel_id] = NULL;
92 }
93 data->m_allocated_channels = 0;
94 data->enabled = false;
95
96 return 0;
97 #else
98 ARG_UNUSED(dev);
99 return -EPERM;
100 #endif
101 }
102
wdt_nrf_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)103 static int wdt_nrf_install_timeout(const struct device *dev,
104 const struct wdt_timeout_cfg *cfg)
105 {
106 const struct wdt_nrfx_config *config = dev->config;
107 struct wdt_nrfx_data *data = dev->data;
108 nrfx_err_t err_code;
109 nrfx_wdt_channel_id channel_id;
110
111 if (data->enabled) {
112 return -EBUSY;
113 }
114
115 if (cfg->flags != WDT_FLAG_RESET_SOC) {
116 return -ENOTSUP;
117 }
118
119 if (cfg->window.min != 0U) {
120 return -EINVAL;
121 }
122
123 if (data->m_allocated_channels == 0U) {
124 /* According to relevant Product Specifications, watchdogs
125 * in all nRF chips can use reload values (determining
126 * the timeout) from range 0xF-0xFFFFFFFF given in 32768 Hz
127 * clock ticks. This makes the allowed range of 0x1-0x07CFFFFF
128 * in milliseconds. Check if the provided value is within
129 * this range.
130 */
131 if ((cfg->window.max == 0U) || (cfg->window.max > 0x07CFFFFF)) {
132 return -EINVAL;
133 }
134
135 /* Save timeout value from first registered watchdog channel. */
136 data->m_timeout = cfg->window.max;
137 } else if (cfg->window.max != data->m_timeout) {
138 return -EINVAL;
139 }
140
141 err_code = nrfx_wdt_channel_alloc(&config->wdt,
142 &channel_id);
143
144 if (err_code == NRFX_ERROR_NO_MEM) {
145 return -ENOMEM;
146 }
147
148 if (cfg->callback != NULL) {
149 data->m_callbacks[channel_id] = cfg->callback;
150 }
151
152 data->m_allocated_channels++;
153 return channel_id;
154 }
155
wdt_nrf_feed(const struct device * dev,int channel_id)156 static int wdt_nrf_feed(const struct device *dev, int channel_id)
157 {
158 const struct wdt_nrfx_config *config = dev->config;
159 struct wdt_nrfx_data *data = dev->data;
160
161 if ((channel_id >= data->m_allocated_channels) || (channel_id < 0)) {
162 return -EINVAL;
163 }
164 if (!data->enabled) {
165 /* Watchdog is not running so does not need to be fed */
166 return -EAGAIN;
167 }
168
169 nrfx_wdt_channel_feed(&config->wdt,
170 (nrfx_wdt_channel_id)channel_id);
171
172 return 0;
173 }
174
175 static DEVICE_API(wdt, wdt_nrfx_driver_api) = {
176 .setup = wdt_nrf_setup,
177 .disable = wdt_nrf_disable,
178 .install_timeout = wdt_nrf_install_timeout,
179 .feed = wdt_nrf_feed,
180 };
181
wdt_event_handler(const struct device * dev,nrf_wdt_event_t event_type,uint32_t requests,void * p_context)182 static void wdt_event_handler(const struct device *dev, nrf_wdt_event_t event_type,
183 uint32_t requests, void *p_context)
184 {
185 struct wdt_nrfx_data *data = dev->data;
186
187 #if defined(WDT_NRFX_SYNC_STOP)
188 if (event_type == NRF_WDT_EVENT_STOPPED) {
189 k_sem_give(&data->sync_stop);
190 }
191 #else
192 (void)event_type;
193 #endif
194 (void)p_context;
195
196 while (requests) {
197 uint8_t i = u32_count_trailing_zeros(requests);
198
199 if (data->m_callbacks[i]) {
200 data->m_callbacks[i](dev, i);
201 }
202 requests &= ~BIT(i);
203 }
204 }
205
206 #define WDT(idx) DT_NODELABEL(wdt##idx)
207
208 #define WDT_NRFX_WDT_IRQ(idx) \
209 COND_CODE_1(CONFIG_WDT_NRFX_NO_IRQ, \
210 (), \
211 (IRQ_CONNECT(DT_IRQN(WDT(idx)), DT_IRQ(WDT(idx), priority), \
212 nrfx_isr, nrfx_wdt_##idx##_irq_handler, 0)))
213
214 #define WDT_NRFX_WDT_DEVICE(idx) \
215 static void wdt_##idx##_event_handler(nrf_wdt_event_t event_type, \
216 uint32_t requests, \
217 void *p_context) \
218 { \
219 wdt_event_handler(DEVICE_DT_GET(WDT(idx)), event_type, \
220 requests, p_context); \
221 } \
222 static int wdt_##idx##_init(const struct device *dev) \
223 { \
224 const struct wdt_nrfx_config *config = dev->config; \
225 nrfx_err_t err_code; \
226 WDT_NRFX_WDT_IRQ(idx); \
227 err_code = nrfx_wdt_init(&config->wdt, \
228 NULL, \
229 IS_ENABLED(CONFIG_WDT_NRFX_NO_IRQ) \
230 ? NULL \
231 : wdt_##idx##_event_handler, \
232 NULL); \
233 if (err_code != NRFX_SUCCESS) { \
234 return -EBUSY; \
235 } \
236 return 0; \
237 } \
238 static struct wdt_nrfx_data wdt_##idx##_data = { \
239 IF_ENABLED(WDT_NRFX_SYNC_STOP, \
240 (.sync_stop = Z_SEM_INITIALIZER( \
241 wdt_##idx##_data.sync_stop, 0, 1),)) \
242 }; \
243 static const struct wdt_nrfx_config wdt_##idx##z_config = { \
244 .wdt = NRFX_WDT_INSTANCE(idx), \
245 }; \
246 DEVICE_DT_DEFINE(WDT(idx), \
247 wdt_##idx##_init, \
248 NULL, \
249 &wdt_##idx##_data, \
250 &wdt_##idx##z_config, \
251 PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
252 &wdt_nrfx_driver_api)
253
254 #ifdef CONFIG_HAS_HW_NRF_WDT0
255 WDT_NRFX_WDT_DEVICE(0);
256 #endif
257
258 #ifdef CONFIG_HAS_HW_NRF_WDT1
259 WDT_NRFX_WDT_DEVICE(1);
260 #endif
261
262 #ifdef CONFIG_HAS_HW_NRF_WDT30
263 WDT_NRFX_WDT_DEVICE(30);
264 #endif
265
266 #ifdef CONFIG_HAS_HW_NRF_WDT31
267 WDT_NRFX_WDT_DEVICE(31);
268 #endif
269
270 #ifdef CONFIG_HAS_HW_NRF_WDT010
271 WDT_NRFX_WDT_DEVICE(010);
272 #endif
273
274 #ifdef CONFIG_HAS_HW_NRF_WDT011
275 WDT_NRFX_WDT_DEVICE(011);
276 #endif
277
278 #ifdef CONFIG_HAS_HW_NRF_WDT130
279 WDT_NRFX_WDT_DEVICE(130);
280 #endif
281
282 #ifdef CONFIG_HAS_HW_NRF_WDT131
283 WDT_NRFX_WDT_DEVICE(131);
284 #endif
285
286 #ifdef CONFIG_HAS_HW_NRF_WDT132
287 WDT_NRFX_WDT_DEVICE(132);
288 #endif
289