1 /*
2  * Copyright (c) 2018, Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <nrfx_wdt.h>
8 #include <drivers/watchdog.h>
9 
10 #define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
11 #include <logging/log.h>
12 LOG_MODULE_REGISTER(wdt_nrfx);
13 
14 struct wdt_nrfx_data {
15 	wdt_callback_t m_callbacks[NRF_WDT_CHANNEL_NUMBER];
16 	uint32_t m_timeout;
17 	uint8_t m_allocated_channels;
18 };
19 
20 struct wdt_nrfx_config {
21 	nrfx_wdt_t	  wdt;
22 	nrfx_wdt_config_t config;
23 };
24 
get_dev_data(const struct device * dev)25 static inline struct wdt_nrfx_data *get_dev_data(const struct device *dev)
26 {
27 	return dev->data;
28 }
29 
get_dev_config(const struct device * dev)30 static inline const struct wdt_nrfx_config *get_dev_config(const struct device *dev)
31 {
32 	return dev->config;
33 }
34 
35 
wdt_nrf_setup(const struct device * dev,uint8_t options)36 static int wdt_nrf_setup(const struct device *dev, uint8_t options)
37 {
38 	nrf_wdt_behaviour_t behaviour;
39 
40 	/* Activate all available options. Run in all cases. */
41 	behaviour = NRF_WDT_BEHAVIOUR_RUN_SLEEP_HALT;
42 
43 	/* Deactivate running in sleep mode. */
44 	if (options & WDT_OPT_PAUSE_IN_SLEEP) {
45 		behaviour &= ~NRF_WDT_BEHAVIOUR_RUN_SLEEP;
46 	}
47 
48 	/* Deactivate running when debugger is attached. */
49 	if (options & WDT_OPT_PAUSE_HALTED_BY_DBG) {
50 		behaviour &= ~NRF_WDT_BEHAVIOUR_RUN_HALT;
51 	}
52 
53 	nrf_wdt_behaviour_set(get_dev_config(dev)->wdt.p_reg, behaviour);
54 	/* The watchdog timer is driven by the LFCLK clock running at 32768 Hz.
55 	 * The timeout value given in milliseconds needs to be converted here
56 	 * to watchdog ticks.*/
57 	nrf_wdt_reload_value_set(
58 		get_dev_config(dev)->wdt.p_reg,
59 		(uint32_t)(((uint64_t)get_dev_data(dev)->m_timeout * 32768U)
60 			   / 1000));
61 
62 	nrfx_wdt_enable(&get_dev_config(dev)->wdt);
63 
64 	return 0;
65 }
66 
wdt_nrf_disable(const struct device * dev)67 static int wdt_nrf_disable(const struct device *dev)
68 {
69 	/* Started watchdog cannot be stopped on nRF devices. */
70 	ARG_UNUSED(dev);
71 	return -EPERM;
72 }
73 
wdt_nrf_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)74 static int wdt_nrf_install_timeout(const struct device *dev,
75 				   const struct wdt_timeout_cfg *cfg)
76 {
77 	nrfx_err_t err_code;
78 	nrfx_wdt_channel_id channel_id;
79 
80 	if (cfg->flags != WDT_FLAG_RESET_SOC) {
81 		return -ENOTSUP;
82 	}
83 
84 	if (cfg->window.min != 0U) {
85 		return -EINVAL;
86 	}
87 
88 	if (get_dev_data(dev)->m_allocated_channels == 0U) {
89 		/* According to relevant Product Specifications, watchdogs
90 		 * in all nRF chips can use reload values (determining
91 		 * the timeout) from range 0xF-0xFFFFFFFF given in 32768 Hz
92 		 * clock ticks. This makes the allowed range of 0x1-0x07CFFFFF
93 		 * in milliseconds. Check if the provided value is within
94 		 * this range. */
95 		if ((cfg->window.max == 0U) || (cfg->window.max > 0x07CFFFFF)) {
96 			return -EINVAL;
97 		}
98 
99 		/* Save timeout value from first registered watchdog channel. */
100 		get_dev_data(dev)->m_timeout = cfg->window.max;
101 	} else if (cfg->window.max != get_dev_data(dev)->m_timeout) {
102 		return -EINVAL;
103 	}
104 
105 	err_code = nrfx_wdt_channel_alloc(&get_dev_config(dev)->wdt,
106 					  &channel_id);
107 
108 	if (err_code == NRFX_ERROR_NO_MEM) {
109 		return -ENOMEM;
110 	}
111 
112 	if (cfg->callback != NULL) {
113 		get_dev_data(dev)->m_callbacks[channel_id] = cfg->callback;
114 	}
115 
116 	get_dev_data(dev)->m_allocated_channels++;
117 	return channel_id;
118 }
119 
wdt_nrf_feed(const struct device * dev,int channel_id)120 static int wdt_nrf_feed(const struct device *dev, int channel_id)
121 {
122 	if (channel_id > get_dev_data(dev)->m_allocated_channels) {
123 		return -EINVAL;
124 	}
125 
126 	nrfx_wdt_channel_feed(&get_dev_config(dev)->wdt,
127 			      (nrfx_wdt_channel_id)channel_id);
128 
129 	return 0;
130 }
131 
132 static const struct wdt_driver_api wdt_nrfx_driver_api = {
133 	.setup = wdt_nrf_setup,
134 	.disable = wdt_nrf_disable,
135 	.install_timeout = wdt_nrf_install_timeout,
136 	.feed = wdt_nrf_feed,
137 };
138 
wdt_event_handler(const struct device * dev)139 static void wdt_event_handler(const struct device *dev)
140 {
141 	int i;
142 
143 	for (i = 0; i < get_dev_data(dev)->m_allocated_channels; ++i) {
144 		if (nrf_wdt_request_status(get_dev_config(dev)->wdt.p_reg,
145 					   (nrf_wdt_rr_register_t)i)) {
146 			if (get_dev_data(dev)->m_callbacks[i]) {
147 				get_dev_data(dev)->m_callbacks[i](dev, i);
148 			}
149 		}
150 	}
151 }
152 
153 #define WDT(idx) DT_NODELABEL(wdt##idx)
154 
155 #define WDT_NRFX_WDT_DEVICE(idx)					       \
156 	static void wdt_##idx##_event_handler(void)			       \
157 	{								       \
158 		wdt_event_handler(DEVICE_DT_GET(WDT(idx)));		       \
159 	}								       \
160 	static int wdt_##idx##_init(const struct device *dev)		       \
161 	{								       \
162 		nrfx_err_t err_code;					       \
163 		IRQ_CONNECT(DT_IRQN(WDT(idx)), DT_IRQ(WDT(idx), priority),     \
164 			    nrfx_isr, nrfx_wdt_##idx##_irq_handler, 0);	       \
165 		err_code = nrfx_wdt_init(&get_dev_config(dev)->wdt,	       \
166 				 &get_dev_config(dev)->config,		       \
167 				 wdt_##idx##_event_handler);		       \
168 		if (err_code != NRFX_SUCCESS) {				       \
169 			return -EBUSY;					       \
170 		}							       \
171 		return 0;						       \
172 	}								       \
173 	static struct wdt_nrfx_data wdt_##idx##_data = {		       \
174 		.m_timeout = 0,						       \
175 		.m_allocated_channels = 0,				       \
176 	};								       \
177 	static const struct wdt_nrfx_config wdt_##idx##z_config = {	       \
178 		.wdt = NRFX_WDT_INSTANCE(idx),				       \
179 		.config = {						       \
180 			.behaviour   = NRF_WDT_BEHAVIOUR_RUN_SLEEP_HALT,       \
181 			.reload_value  = 2000,				       \
182 		}							       \
183 	};								       \
184 	DEVICE_DT_DEFINE(WDT(idx),					       \
185 			    wdt_##idx##_init,				       \
186 			    NULL,					       \
187 			    &wdt_##idx##_data,				       \
188 			    &wdt_##idx##z_config,			       \
189 			    PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,  \
190 			    &wdt_nrfx_driver_api)
191 
192 #ifdef CONFIG_NRFX_WDT0
193 WDT_NRFX_WDT_DEVICE(0);
194 #endif
195 
196 #ifdef CONFIG_NRFX_WDT1
197 WDT_NRFX_WDT_DEVICE(1);
198 #endif
199