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