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