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