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 if ((cfg->window.max == 0U) || (cfg->window.max > 0x07CFFFFF)) {
119 return -EINVAL;
120 }
121
122 /* Save timeout value from first registered watchdog channel. */
123 data->m_timeout = cfg->window.max;
124 } else if (cfg->window.max != data->m_timeout) {
125 return -EINVAL;
126 }
127
128 err_code = nrfx_wdt_channel_alloc(&config->wdt,
129 &channel_id);
130
131 if (err_code == NRFX_ERROR_NO_MEM) {
132 return -ENOMEM;
133 }
134
135 if (cfg->callback != NULL) {
136 data->m_callbacks[channel_id] = cfg->callback;
137 }
138
139 data->m_allocated_channels++;
140 return channel_id;
141 }
142
wdt_nrf_feed(const struct device * dev,int channel_id)143 static int wdt_nrf_feed(const struct device *dev, int channel_id)
144 {
145 const struct wdt_nrfx_config *config = dev->config;
146 struct wdt_nrfx_data *data = dev->data;
147
148 if ((channel_id >= data->m_allocated_channels) || (channel_id < 0)) {
149 return -EINVAL;
150 }
151 if (!data->enabled) {
152 /* Watchdog is not running so does not need to be fed */
153 return -EAGAIN;
154 }
155
156 nrfx_wdt_channel_feed(&config->wdt,
157 (nrfx_wdt_channel_id)channel_id);
158
159 return 0;
160 }
161
162 static const struct wdt_driver_api wdt_nrfx_driver_api = {
163 .setup = wdt_nrf_setup,
164 .disable = wdt_nrf_disable,
165 .install_timeout = wdt_nrf_install_timeout,
166 .feed = wdt_nrf_feed,
167 };
168
wdt_event_handler(const struct device * dev,nrf_wdt_event_t event_type,uint32_t requests,void * p_context)169 static void wdt_event_handler(const struct device *dev, nrf_wdt_event_t event_type,
170 uint32_t requests, void *p_context)
171 {
172 (void)event_type;
173 (void)p_context;
174
175 struct wdt_nrfx_data *data = dev->data;
176
177 while (requests) {
178 uint8_t i = u32_count_trailing_zeros(requests);
179
180 if (data->m_callbacks[i]) {
181 data->m_callbacks[i](dev, i);
182 }
183 requests &= ~BIT(i);
184 }
185 }
186
187 #define WDT(idx) DT_NODELABEL(wdt##idx)
188
189 #define WDT_NRFX_WDT_DEVICE(idx) \
190 static void wdt_##idx##_event_handler(nrf_wdt_event_t event_type, \
191 uint32_t requests, \
192 void *p_context) \
193 { \
194 wdt_event_handler(DEVICE_DT_GET(WDT(idx)), event_type, \
195 requests, p_context); \
196 } \
197 static int wdt_##idx##_init(const struct device *dev) \
198 { \
199 const struct wdt_nrfx_config *config = dev->config; \
200 nrfx_err_t err_code; \
201 IRQ_CONNECT(DT_IRQN(WDT(idx)), DT_IRQ(WDT(idx), priority), \
202 nrfx_isr, nrfx_wdt_##idx##_irq_handler, 0); \
203 err_code = nrfx_wdt_init(&config->wdt, \
204 NULL, \
205 wdt_##idx##_event_handler, \
206 NULL); \
207 if (err_code != NRFX_SUCCESS) { \
208 return -EBUSY; \
209 } \
210 return 0; \
211 } \
212 static struct wdt_nrfx_data wdt_##idx##_data; \
213 static const struct wdt_nrfx_config wdt_##idx##z_config = { \
214 .wdt = NRFX_WDT_INSTANCE(idx), \
215 }; \
216 DEVICE_DT_DEFINE(WDT(idx), \
217 wdt_##idx##_init, \
218 NULL, \
219 &wdt_##idx##_data, \
220 &wdt_##idx##z_config, \
221 PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
222 &wdt_nrfx_driver_api)
223
224 #ifdef CONFIG_HAS_HW_NRF_WDT0
225 WDT_NRFX_WDT_DEVICE(0);
226 #endif
227
228 #ifdef CONFIG_HAS_HW_NRF_WDT1
229 WDT_NRFX_WDT_DEVICE(1);
230 #endif
231
232 #ifdef CONFIG_HAS_HW_NRF_WDT30
233 WDT_NRFX_WDT_DEVICE(30);
234 #endif
235
236 #ifdef CONFIG_HAS_HW_NRF_WDT31
237 WDT_NRFX_WDT_DEVICE(31);
238 #endif
239
240 #ifdef CONFIG_HAS_HW_NRF_WDT010
241 WDT_NRFX_WDT_DEVICE(010);
242 #endif
243
244 #ifdef CONFIG_HAS_HW_NRF_WDT011
245 WDT_NRFX_WDT_DEVICE(011);
246 #endif
247
248 #ifdef CONFIG_HAS_HW_NRF_WDT130
249 WDT_NRFX_WDT_DEVICE(130);
250 #endif
251
252 #ifdef CONFIG_HAS_HW_NRF_WDT131
253 WDT_NRFX_WDT_DEVICE(131);
254 #endif
255
256 #ifdef CONFIG_HAS_HW_NRF_WDT132
257 WDT_NRFX_WDT_DEVICE(132);
258 #endif
259